diff --git a/vmware-base/src/main/java/com/cloud/hypervisor/vmware/mo/VirtualMachineMO.java b/vmware-base/src/main/java/com/cloud/hypervisor/vmware/mo/VirtualMachineMO.java index 950ec9010cbd..fd6ad8e6aa57 100644 --- a/vmware-base/src/main/java/com/cloud/hypervisor/vmware/mo/VirtualMachineMO.java +++ b/vmware-base/src/main/java/com/cloud/hypervisor/vmware/mo/VirtualMachineMO.java @@ -24,6 +24,7 @@ import java.io.ByteArrayOutputStream; import java.io.File; import java.io.FileOutputStream; +import java.io.IOException; import java.io.InputStreamReader; import java.io.OutputStreamWriter; import java.net.URLEncoder; @@ -1466,7 +1467,11 @@ public void attachDisk(String[] vmdkDatastorePathChain, ManagedObjectReference m VirtualDevice newDisk = VmwareHelper.prepareDiskDevice(this, null, controllerKey, vmdkDatastorePathChain, morDs, unitNumber, 1, maxIops); if (StringUtils.isNotBlank(diskController)) { String vmdkFileName = vmdkDatastorePathChain[0]; - updateVmdkAdapter(vmdkFileName, diskController); + try { + updateVmdkAdapter(vmdkFileName, diskController); + } catch (IOException e) { + logger.warn("Unable to verify/update adapter type for VMDK file " + vmdkFileName + " due to a datastore browser I/O failure, proceeding with disk attach: " + e.getMessage(), e); + } } VirtualMachineConfigSpec reConfigSpec = new VirtualMachineConfigSpec(); VirtualDeviceConfigSpec deviceConfigSpec = new VirtualDeviceConfigSpec(); diff --git a/vmware-base/src/test/java/com/cloud/hypervisor/vmware/mo/VirtualMachineMOTest.java b/vmware-base/src/test/java/com/cloud/hypervisor/vmware/mo/VirtualMachineMOTest.java index 570f2a7b4a16..b1ec1ef90b8f 100644 --- a/vmware-base/src/test/java/com/cloud/hypervisor/vmware/mo/VirtualMachineMOTest.java +++ b/vmware-base/src/test/java/com/cloud/hypervisor/vmware/mo/VirtualMachineMOTest.java @@ -21,6 +21,7 @@ import com.cloud.hypervisor.vmware.util.VmwareContext; import com.cloud.utils.exception.CloudRuntimeException; import com.vmware.vim25.ManagedObjectReference; +import com.vmware.vim25.VimPortType; import com.vmware.vim25.VirtualDevice; import com.vmware.vim25.VirtualLsiLogicController; import com.vmware.vim25.VirtualLsiLogicSASController; @@ -37,11 +38,21 @@ import org.mockito.MockitoAnnotations; import org.mockito.junit.MockitoJUnitRunner; +import java.io.IOException; import java.util.ArrayList; import java.util.List; import static org.junit.Assert.fail; import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyInt; +import static org.mockito.ArgumentMatchers.anyString; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.doReturn; +import static org.mockito.Mockito.doThrow; +import static org.mockito.Mockito.lenient; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.spy; +import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; @RunWith(MockitoJUnitRunner.class) @@ -135,4 +146,50 @@ public void testGetVmxFormattedVirtualHardwareVersionTwoDigits() { public void testGetVmxFormattedVirtualHardwareVersionInvalid() { VirtualMachineMO.getVmxFormattedVirtualHardwareVersion(-1); } + + @Test + public void testAttachDiskSucceedsWhenAdapterTypeUpdateFails() throws Exception { + VirtualMachineMO spyVmMo = spy(vmMo); + ManagedObjectReference morDs = mock(ManagedObjectReference.class); + ManagedObjectReference morTask = mock(ManagedObjectReference.class); + VimPortType service = mock(VimPortType.class); + + doReturn(1).when(spyVmMo).getScsiDiskControllerKey(anyString()); + doReturn(200).when(spyVmMo).getIDEDeviceControllerKey(); + doReturn(0).when(spyVmMo).getNextDeviceNumber(anyInt()); + doThrow(new IOException("HTTP 500 from vCenter datastore browser")).when(spyVmMo).updateVmdkAdapter(anyString(), anyString()); + + when(mor.getValue()).thenReturn("vm-1"); + when(context.getService()).thenReturn(service); + when(service.reconfigVMTask(eq(mor), any())).thenReturn(morTask); + when(client.waitForTask(morTask)).thenReturn(true); + + spyVmMo.attachDisk(new String[]{"[ds] i-2-3-VM/data.vmdk"}, morDs, "pvscsi", null, null); + + verify(spyVmMo).updateVmdkAdapter(eq("[ds] i-2-3-VM/data.vmdk"), eq("pvscsi")); + verify(service).reconfigVMTask(eq(mor), any()); + verify(client).waitForTask(morTask); + } + + @Test(expected = Exception.class) + public void testAttachDiskFailsWhenAdapterTypeIsInvalid() throws Exception { + VirtualMachineMO spyVmMo = spy(vmMo); + ManagedObjectReference morDs = mock(ManagedObjectReference.class); + ManagedObjectReference morTask = mock(ManagedObjectReference.class); + VimPortType service = mock(VimPortType.class); + + doReturn(1).when(spyVmMo).getScsiDiskControllerKey(anyString()); + doReturn(200).when(spyVmMo).getIDEDeviceControllerKey(); + doReturn(0).when(spyVmMo).getNextDeviceNumber(anyInt()); + doThrow(new Exception("Failed to attach disk due to invalid vmdk adapter type")).when(spyVmMo).updateVmdkAdapter(anyString(), anyString()); + + when(mor.getValue()).thenReturn("vm-1"); + // Lenient: these mirror the success path and are only reached if the + // invalid-adapter-type exception is (incorrectly) swallowed by attachDisk. + lenient().when(context.getService()).thenReturn(service); + lenient().when(service.reconfigVMTask(eq(mor), any())).thenReturn(morTask); + lenient().when(client.waitForTask(morTask)).thenReturn(true); + + spyVmMo.attachDisk(new String[]{"[ds] i-2-3-VM/data.vmdk"}, morDs, "pvscsi", null, null); + } }