From 4a48427ea49cdd3154ce7b3b574cad502b6b0062 Mon Sep 17 00:00:00 2001 From: Sateesh Chodapuneedi Date: Wed, 14 Jan 2015 08:13:10 +0530 Subject: [PATCH 1/2] CLOUDSTACK-4787 Allow selection of scsi controller type in vSphere commit #7 So far only 1 controller (scsi or ide) is supported in Cloudstack for ide or scsi, this is existing limitation. Added support for 2nd IDE controller. Support adding IDE virtual disk to VM. Also added check if VM is running as IDE virtual disk cannot be attached to VM if VM is runnning.If user detaches a virtual disk on lower unit number of controller, then subsequent attach operation should find free unit number on the controller and attach the virtual disk there. commit #6 Let the controllers of existing VMs continue without flip, current busInfo retrieved from chain_info field of volume record from database would be preferred over controller settings from all configuration settings. commit #5 Editing global configuration param vmware.root.disk.controller osdefault value results in loss of previous root disk controller type. Hence root disk's controller type for legacy VMs is unknow post that modificaiton by user. If VM is stop/start then we could get this infromation from bus info of existing volume. But if user resets VM and then try to start VM. The existing bus info would be lost. Hence existing disk info is not available to depend on. Using lsilogic or generic scsi controller for ROOT disk of legacy VMs if reset. commit #4 Avoid adding additional (>1) scsi controllers to system vms. While attaching volume to legacy VM don't use osdefault optoin which applicable only for VM created with the option enabled, use legacy data disk controller type (lsilogic) commit #3 If root disk's controller type is scsi and data disk controller type condenses to any of scsi sub-types then data disk controller type would fall back to root disk controller itself. This ensures data volumes would be accessible in all cases as controller of root volume would be reliable and it means VM has the supported controller. It also avoids mix of scsi controller sub-types in a user instance. Also translating disk controller type scsi to lsilogic. commit #2 Support auto detection of recommended virtual disk controller type for specific guest OS. commit #1 Support granual controller types. Add support for controller types in template registration as well. Fix white spaces. Removed stale HEAD merge lines Removed tail of merge lines Fixed VmwareResource, removing storage commands that moved to VmwareStorageProcessor. removed stale code of controller that is present in processor Fixed check style errors. Fixed injection. Tested with Linux and windows templates. Unable to run iso based tests due to few bugs in register iso area. Signed-off-by: Sateesh Chodapuneedi (cherry picked from commit a4cc987a6f66f20c434942956fffe5951df09e43) Signed-off-by: Rohit Yadav --- api/src/com/cloud/vm/VmDetailConstants.java | 1 + .../storage/command/AttachCommand.java | 16 + .../com/cloud/hypervisor/guru/VMwareGuru.java | 7 + .../vmware/manager/VmwareManager.java | 1 + .../vmware/manager/VmwareManagerImpl.java | 8 + .../vmware/resource/VmwareResource.java | 209 ++++++- .../resource/VmwareStorageProcessor.java | 62 ++- .../cloud/storage/VolumeApiServiceImpl.java | 6 + .../src/com/cloud/vm/UserVmManagerImpl.java | 9 + .../cloud/hypervisor/vmware/mo/ClusterMO.java | 35 +- .../vmware/mo/DiskControllerType.java | 31 +- .../cloud/hypervisor/vmware/mo/HostMO.java | 17 +- .../vmware/mo/HypervisorHostHelper.java | 112 +++- .../vmware/mo/ScsiDiskControllerType.java | 24 + .../vmware/mo/VirtualMachineMO.java | 524 +++++++++++++++++- .../hypervisor/vmware/mo/VmdkAdapterType.java | 48 ++ .../vmware/mo/VmdkFileDescriptor.java | 70 +++ .../vmware/mo/VmwareHypervisorHost.java | 5 +- .../hypervisor/vmware/util/VmwareHelper.java | 24 + 19 files changed, 1140 insertions(+), 69 deletions(-) create mode 100644 vmware-base/src/com/cloud/hypervisor/vmware/mo/ScsiDiskControllerType.java create mode 100644 vmware-base/src/com/cloud/hypervisor/vmware/mo/VmdkAdapterType.java diff --git a/api/src/com/cloud/vm/VmDetailConstants.java b/api/src/com/cloud/vm/VmDetailConstants.java index bd6f0e2f6465..d06ad67197eb 100644 --- a/api/src/com/cloud/vm/VmDetailConstants.java +++ b/api/src/com/cloud/vm/VmDetailConstants.java @@ -22,4 +22,5 @@ public interface VmDetailConstants { public static final String ROOK_DISK_CONTROLLER = "rootDiskController"; public static final String NESTED_VIRTUALIZATION_FLAG = "nestedVirtualizationFlag"; public static final String HYPERVISOR_TOOLS_VERSION = "hypervisortoolsversion"; + public static final String DATA_DISK_CONTROLLER = "dataDiskController"; } diff --git a/core/src/org/apache/cloudstack/storage/command/AttachCommand.java b/core/src/org/apache/cloudstack/storage/command/AttachCommand.java index 795deb2c0649..0109d275af38 100644 --- a/core/src/org/apache/cloudstack/storage/command/AttachCommand.java +++ b/core/src/org/apache/cloudstack/storage/command/AttachCommand.java @@ -19,6 +19,8 @@ package org.apache.cloudstack.storage.command; +import java.util.Map; + import com.cloud.agent.api.Command; import com.cloud.agent.api.to.DiskTO; @@ -26,12 +28,26 @@ public final class AttachCommand extends Command implements StorageSubSystemComm private DiskTO disk; private String vmName; private boolean inSeq = false; + private Map controllerInfo; public AttachCommand(DiskTO disk, String vmName) { super(); this.disk = disk; this.vmName = vmName; } + public AttachCommand(DiskTO disk, String vmName, Map controllerInfo) { + super(); + this.disk = disk; + this.vmName = vmName; + this.controllerInfo = controllerInfo; + } + + public Map getControllerInfo() { + return controllerInfo; + } + public void setControllerInfo(Map controllerInfo) { + this.controllerInfo = controllerInfo; + } @Override public boolean executeInSequence() { diff --git a/plugins/hypervisors/vmware/src/com/cloud/hypervisor/guru/VMwareGuru.java b/plugins/hypervisors/vmware/src/com/cloud/hypervisor/guru/VMwareGuru.java index 9ac32d4757eb..88ed576283eb 100644 --- a/plugins/hypervisors/vmware/src/com/cloud/hypervisor/guru/VMwareGuru.java +++ b/plugins/hypervisors/vmware/src/com/cloud/hypervisor/guru/VMwareGuru.java @@ -69,6 +69,7 @@ import com.cloud.hypervisor.HypervisorGuru; import com.cloud.hypervisor.HypervisorGuruBase; import com.cloud.hypervisor.vmware.manager.VmwareManager; +import com.cloud.hypervisor.vmware.mo.DiskControllerType; import com.cloud.hypervisor.vmware.mo.VirtualEthernetCardType; import com.cloud.network.Network.Provider; import com.cloud.network.Network.Service; @@ -208,6 +209,12 @@ public VirtualMachineTO implement(VirtualMachineProfile vm) { details.put(VmDetailConstants.ROOK_DISK_CONTROLLER, _vmwareMgr.getRootDiskController()); } } + String diskController = details.get(VmDetailConstants.DATA_DISK_CONTROLLER); + if (userVm) { + if (diskController == null) { + details.put(VmDetailConstants.DATA_DISK_CONTROLLER, DiskControllerType.lsilogic.toString()); + } + } List nicProfiles = vm.getNics(); diff --git a/plugins/hypervisors/vmware/src/com/cloud/hypervisor/vmware/manager/VmwareManager.java b/plugins/hypervisors/vmware/src/com/cloud/hypervisor/vmware/manager/VmwareManager.java index 35e275e004e7..72ee2184e399 100755 --- a/plugins/hypervisors/vmware/src/com/cloud/hypervisor/vmware/manager/VmwareManager.java +++ b/plugins/hypervisors/vmware/src/com/cloud/hypervisor/vmware/manager/VmwareManager.java @@ -78,5 +78,6 @@ public interface VmwareManager { boolean isLegacyZone(long dcId); + public String getDataDiskController(); boolean hasNexusVSM(Long clusterId); } diff --git a/plugins/hypervisors/vmware/src/com/cloud/hypervisor/vmware/manager/VmwareManagerImpl.java b/plugins/hypervisors/vmware/src/com/cloud/hypervisor/vmware/manager/VmwareManagerImpl.java index e852948b499f..2bac9be134b8 100755 --- a/plugins/hypervisors/vmware/src/com/cloud/hypervisor/vmware/manager/VmwareManagerImpl.java +++ b/plugins/hypervisors/vmware/src/com/cloud/hypervisor/vmware/manager/VmwareManagerImpl.java @@ -185,6 +185,8 @@ public class VmwareManagerImpl extends ManagerBase implements VmwareManager, Vmw private String _rootDiskController = DiskControllerType.ide.toString(); + private String _dataDiskController = DiskControllerType.osdefault.toString(); + private final Map _storageMounts = new HashMap(); private final Random _rand = new Random(System.currentTimeMillis()); @@ -478,6 +480,7 @@ public void setupResourceStartupParams(Map params) { params.put("service.console.name", _serviceConsoleName); params.put("management.portgroup.name", _managemetPortGroupName); params.put("vmware.root.disk.controller", _rootDiskController); + params.put("vmware.data.disk.controller", _dataDiskController); params.put("vmware.recycle.hung.wokervm", _recycleHungWorker); params.put("ports.per.dvportgroup", _portsPerDvPortGroup); } @@ -930,6 +933,11 @@ public String getRootDiskController() { return _rootDiskController; } + @Override + public String getDataDiskController() { + return _dataDiskController; + } + @Override public int getVcenterSessionTimeout() { return _vCenterSessionTimeout; diff --git a/plugins/hypervisors/vmware/src/com/cloud/hypervisor/vmware/resource/VmwareResource.java b/plugins/hypervisors/vmware/src/com/cloud/hypervisor/vmware/resource/VmwareResource.java index 9e7db37e230c..008f3a39a64b 100755 --- a/plugins/hypervisors/vmware/src/com/cloud/hypervisor/vmware/resource/VmwareResource.java +++ b/plugins/hypervisors/vmware/src/com/cloud/hypervisor/vmware/resource/VmwareResource.java @@ -80,6 +80,7 @@ import com.vmware.vim25.VirtualDeviceConfigSpec; import com.vmware.vim25.VirtualDeviceConfigSpecOperation; import com.vmware.vim25.VirtualDisk; +import com.vmware.vim25.VirtualDiskFlatVer2BackingInfo; import com.vmware.vim25.VirtualEthernetCard; import com.vmware.vim25.VirtualEthernetCardDistributedVirtualPortBackingInfo; import com.vmware.vim25.VirtualEthernetCardNetworkBackingInfo; @@ -215,8 +216,8 @@ import com.cloud.exception.CloudException; import com.cloud.exception.InternalErrorException; import com.cloud.host.Host.Type; -import com.cloud.hypervisor.guru.VMwareGuru; import com.cloud.hypervisor.Hypervisor.HypervisorType; +import com.cloud.hypervisor.guru.VMwareGuru; import com.cloud.hypervisor.vmware.manager.VmwareHostService; import com.cloud.hypervisor.vmware.manager.VmwareManager; import com.cloud.hypervisor.vmware.manager.VmwareStorageMount; @@ -566,7 +567,7 @@ private Answer execute(ResizeVolumeCommand cmd) { // we need to spawn a worker VM to attach the volume to and // resize the volume. useWorkerVm = true; - vmName = this.getWorkerName(getServiceContext(), cmd, 0); + vmName = getWorkerName(getServiceContext(), cmd, 0); morDS = HypervisorHostHelper.findDatastoreWithBackwardsCompatibility(hyperHost, poolId); dsMo = new DatastoreMO(hyperHost.getContext(), morDS); @@ -595,7 +596,18 @@ private Answer execute(ResizeVolumeCommand cmd) { s_logger.trace("resize volume done (failed)"); throw new Exception("No such disk device: " + path); } + // IDE virtual disk cannot be re-sized if VM is running + if (vdisk.second() != null && vdisk.second().contains("ide")) { + throw new Exception("Re-sizing a virtual disk over IDE controller is not supported in VMware hypervisor. " + + "Please re-try when virtual disk is attached to a VM using SCSI controller."); + } + VirtualDisk disk = vdisk.first(); + String vmdkAbsFile = getAbsoluteVmdkFile(disk); + if (vmdkAbsFile != null && !vmdkAbsFile.isEmpty()) { + vmMo.updateAdapterTypeIfRequired(vmdkAbsFile); + } + disk.setCapacityInKB(newSize); VirtualMachineConfigSpec vmConfigSpec = new VirtualMachineConfigSpec(); @@ -1325,6 +1337,63 @@ protected ScaleVmAnswer execute(ScaleVmCommand cmd) { return new ScaleVmAnswer(cmd, true, null); } + protected void ensureDiskControllers(VirtualMachineMO vmMo, Pair controllerInfo) throws Exception { + if (vmMo == null) { + return; + } + + String msg; + String rootDiskController = controllerInfo.first(); + String dataDiskController = controllerInfo.second(); + String scsiDiskController; + String recommendedDiskController = null; + + if (VmwareHelper.isControllerOsRecommended(dataDiskController) || VmwareHelper.isControllerOsRecommended(rootDiskController)) { + recommendedDiskController = vmMo.getRecommendedDiskController(null); + } + scsiDiskController = HypervisorHostHelper.getScsiController(new Pair(rootDiskController, dataDiskController), recommendedDiskController); + if (scsiDiskController == null) { + return; + } + + vmMo.getScsiDeviceControllerKeyNoException(); + // This VM needs SCSI controllers. + // Get count of existing scsi controllers. Helps not to attempt to create more than the maximum allowed 4 + // Get maximum among the bus numbers in use by scsi controllers. Safe to pick maximum, because we always go sequential allocating bus numbers. + Ternary scsiControllerInfo = vmMo.getScsiControllerInfo(); + int requiredNumScsiControllers = VmwareHelper.MAX_SCSI_CONTROLLER_COUNT - scsiControllerInfo.first(); + int availableBusNum = scsiControllerInfo.second() + 1; // method returned current max. bus number + + if (requiredNumScsiControllers == 0) { + return; + } + if (scsiControllerInfo.first() > 0) { + // For VMs which already have a SCSI controller, do NOT attempt to add any more SCSI controllers & return the sub type. + // For Legacy VMs would have only 1 LsiLogic Parallel SCSI controller, and doesn't require more. + // For VMs created post device ordering support, 4 SCSI subtype controllers are ensured during deployment itself. No need to add more. + // For fresh VM deployment only, all required controllers should be ensured. + return; + } + ensureScsiDiskControllers(vmMo, scsiDiskController, requiredNumScsiControllers, availableBusNum); + } + + private void ensureScsiDiskControllers(VirtualMachineMO vmMo, String scsiDiskController, int requiredNumScsiControllers, int availableBusNum) throws Exception { + // Pick the sub type of scsi + if (DiskControllerType.getType(scsiDiskController) == DiskControllerType.pvscsi) { + if (!vmMo.isPvScsiSupported()) { + String msg = "This VM doesn't support Vmware Paravirtual SCSI controller for virtual disks, because the virtual hardware version is less than 7."; + throw new Exception(msg); + } + vmMo.ensurePvScsiDeviceController(requiredNumScsiControllers, availableBusNum); + } else if (DiskControllerType.getType(scsiDiskController) == DiskControllerType.lsisas1068) { + vmMo.ensureLsiLogicSasDeviceControllers(requiredNumScsiControllers, availableBusNum); + } else if (DiskControllerType.getType(scsiDiskController) == DiskControllerType.buslogic) { + vmMo.ensureBusLogicDeviceControllers(requiredNumScsiControllers, availableBusNum); + } else if (DiskControllerType.getType(scsiDiskController) == DiskControllerType.lsilogic) { + vmMo.ensureScsiDeviceControllers(requiredNumScsiControllers, availableBusNum); + } + } + protected StartAnswer execute(StartCommand cmd) { if (s_logger.isInfoEnabled()) { s_logger.info("Executing resource StartCommand: " + _gson.toJson(cmd)); @@ -1340,7 +1409,29 @@ protected StartAnswer execute(StartCommand cmd) { Pair names = composeVmNames(vmSpec); String vmInternalCSName = names.first(); String vmNameOnVcenter = names.second(); + String dataDiskController = vmSpec.getDetails().get(VmDetailConstants.DATA_DISK_CONTROLLER); + String rootDiskController = vmSpec.getDetails().get(VmDetailConstants.ROOK_DISK_CONTROLLER); + // If root disk controller is scsi, then data disk controller would also be scsi instead of using 'osdefault' + // This helps avoid mix of different scsi subtype controllers in instance. + if (DiskControllerType.lsilogic == DiskControllerType.getType(rootDiskController)) { + dataDiskController = DiskControllerType.scsi.toString(); + } + + // Validate the controller types + dataDiskController = DiskControllerType.getType(dataDiskController).toString(); + rootDiskController = DiskControllerType.getType(rootDiskController).toString(); + + if (DiskControllerType.getType(rootDiskController) == DiskControllerType.none) { + throw new CloudRuntimeException("Invalid root disk controller detected : " + rootDiskController); + } + if (DiskControllerType.getType(dataDiskController) == DiskControllerType.none) { + throw new CloudRuntimeException("Invalid data disk controller detected : " + dataDiskController); + } + + Pair controllerInfo = new Pair(rootDiskController, dataDiskController); + + Boolean systemVm = vmSpec.getType().isUsedBySystem(); // Thus, vmInternalCSName always holds i-x-y, the cloudstack generated internal VM name. VmwareContext context = getServiceContext(); DatacenterMO dcMo = null; @@ -1379,6 +1470,9 @@ protected StartAnswer execute(StartCommand cmd) { VirtualMachineDiskInfoBuilder diskInfoBuilder = null; VirtualMachineMO vmMo = hyperHost.findVmOnHyperHost(vmInternalCSName); + DiskControllerType systemVmScsiControllerType = DiskControllerType.lsilogic; + int firstScsiControllerBusNum = 0; + int numScsiControllerForSystemVm = 1; boolean hasSnapshot = false; if (vmMo != null) { s_logger.info("VM " + vmInternalCSName + " already exists, tear down devices for reconfiguration"); @@ -1392,7 +1486,11 @@ protected StartAnswer execute(StartCommand cmd) { vmMo.tearDownDevices(new Class[] {VirtualDisk.class, VirtualEthernetCard.class}); else vmMo.tearDownDevices(new Class[] {VirtualEthernetCard.class}); - vmMo.ensureScsiDeviceController(); + if (systemVm) { + ensureScsiDiskControllers(vmMo, systemVmScsiControllerType.toString(), numScsiControllerForSystemVm, firstScsiControllerBusNum); + } else { + ensureDiskControllers(vmMo, controllerInfo); + } } else { ManagedObjectReference morDc = hyperHost.getHyperHostDatacenter(); assert (morDc != null); @@ -1414,7 +1512,13 @@ protected StartAnswer execute(StartCommand cmd) { vmMo.tearDownDevices(new Class[] {VirtualDisk.class, VirtualEthernetCard.class}); else vmMo.tearDownDevices(new Class[] {VirtualEthernetCard.class}); - vmMo.ensureScsiDeviceController(); + + if (systemVm) { + // System volumes doesn't require more than 1 SCSI controller as there is no requirement for data volumes. + ensureScsiDiskControllers(vmMo, systemVmScsiControllerType.toString(), numScsiControllerForSystemVm, firstScsiControllerBusNum); + } else { + ensureDiskControllers(vmMo, controllerInfo); + } } else { // If a VM with the same name is found in a different cluster in the DC, unregister the old VM and configure a new VM (cold-migration). VirtualMachineMO existingVmInDc = dcMo.findVm(vmInternalCSName); @@ -1458,7 +1562,7 @@ protected StartAnswer execute(StartCommand cmd) { tearDownVm(vmMo); }else if (!hyperHost.createBlankVm(vmNameOnVcenter, vmInternalCSName, vmSpec.getCpus(), vmSpec.getMaxSpeed().intValue(), getReservedCpuMHZ(vmSpec), vmSpec.getLimitCpuUse(), (int)(vmSpec.getMaxRam() / (1024 * 1024)), getReservedMemoryMb(vmSpec), - translateGuestOsIdentifier(vmSpec.getArch(), vmSpec.getOs(), vmSpec.getPlatformEmulator()).value(), rootDiskDataStoreDetails.first(), false)) { + translateGuestOsIdentifier(vmSpec.getArch(), vmSpec.getOs(), vmSpec.getPlatformEmulator()).value(), rootDiskDataStoreDetails.first(), false, controllerInfo, systemVm)) { throw new Exception("Failed to create VM. vmName: " + vmInternalCSName); } } @@ -1520,7 +1624,7 @@ protected StartAnswer execute(StartCommand cmd) { int scsiUnitNumber = 0; int nicUnitNumber = 0; int ideControllerKey = vmMo.getIDEDeviceControllerKey(); - int scsiControllerKey = vmMo.getScsiDeviceControllerKey(); + int scsiControllerKey = vmMo.getGenericScsiDeviceControllerKeyNoException(); int controllerKey; // @@ -1619,7 +1723,31 @@ protected StartAnswer execute(StartCommand cmd) { VirtualMachineDiskInfo matchingExistingDisk = getMatchingExistingDisk(diskInfoBuilder, vol, hyperHost, context); controllerKey = getDiskController(matchingExistingDisk, vol, vmSpec, ideControllerKey, scsiControllerKey); + String diskController = getDiskController(vmMo, matchingExistingDisk, vol, new Pair(rootDiskController, dataDiskController)); + if (DiskControllerType.getType(diskController) == DiskControllerType.osdefault) { + diskController = vmMo.getRecommendedDiskController(null); + } + if (DiskControllerType.getType(diskController) == DiskControllerType.ide) { + controllerKey = vmMo.getIDEControllerKey(ideUnitNumber); + if (vol.getType() == Volume.Type.DATADISK) { + // Could be result of flip due to user configured setting or "osdefault" for data disks + // Ensure maximum of 2 data volumes over IDE controller, 3 includeing root volume + if (vmMo.getNumberOfVirtualDisks() > 3) { + throw new CloudRuntimeException("Found more than 3 virtual disks attached to this VM [" + vmMo.getVmName() + "]. Unable to implement the disks over " + + diskController + " controller, as maximum number of devices supported over IDE controller is 4 includeing CDROM device."); + } + } + } else { + controllerKey = vmMo.getScsiDiskControllerKeyNoException(diskController); + if (controllerKey == -1) { + // This may happen for ROOT legacy VMs which doesn't have recommended disk controller when global configuration parameter 'vmware.root.disk.controller' is set to "osdefault" + // Retrieve existing controller and use. + Ternary vmScsiControllerInfo = vmMo.getScsiControllerInfo(); + DiskControllerType existingControllerType = vmScsiControllerInfo.third(); + controllerKey = vmMo.getScsiDiskControllerKeyNoException(existingControllerType.toString()); + } + } if (!hasSnapshot) { deviceConfigSpecArray[i] = new VirtualDeviceConfigSpec(); @@ -1648,7 +1776,7 @@ protected StartAnswer execute(StartCommand cmd) { VirtualDevice device = VmwareHelper.prepareDiskDevice(vmMo, null, controllerKey, diskChain, volumeDsDetails.first(), - (controllerKey == ideControllerKey) ? ideUnitNumber++ : scsiUnitNumber++, i + 1); + (controllerKey == vmMo.getIDEControllerKey(ideUnitNumber)) ? ((ideUnitNumber++) % VmwareHelper.MAX_IDE_CONTROLLER_COUNT) : scsiUnitNumber++, i + 1); deviceConfigSpecArray[i].setDevice(device); deviceConfigSpecArray[i].setOperation(VirtualDeviceConfigSpecOperation.ADD); @@ -1660,7 +1788,7 @@ protected StartAnswer execute(StartCommand cmd) { } else { if (controllerKey == scsiControllerKey && VmwareHelper.isReservedScsiDeviceNumber(scsiUnitNumber)) scsiUnitNumber++; - if (controllerKey == ideControllerKey) + if (controllerKey == vmMo.getIDEControllerKey(ideUnitNumber)) ideUnitNumber++; else scsiUnitNumber++; @@ -2187,6 +2315,36 @@ private int getDiskController(VirtualMachineDiskInfo matchingExistingDisk, DiskT return controllerKey; } + private String getDiskController(VirtualMachineMO vmMo, VirtualMachineDiskInfo matchingExistingDisk, DiskTO vol, Pair controllerInfo) throws Exception { + int controllerKey; + DiskControllerType controllerType = DiskControllerType.none; + if (matchingExistingDisk != null) { + String currentBusName = matchingExistingDisk.getDiskDeviceBusName(); + if (currentBusName != null) { + s_logger.info("Chose disk controller based on existing information: " + currentBusName); + if (currentBusName.startsWith("ide")) { + controllerType = DiskControllerType.ide; + } else if (currentBusName.startsWith("scsi")) { + controllerType = DiskControllerType.scsi; + } + } + if (controllerType == DiskControllerType.scsi || controllerType == DiskControllerType.none) { + Ternary vmScsiControllerInfo = vmMo.getScsiControllerInfo(); + controllerType = vmScsiControllerInfo.third(); + } + return controllerType.toString(); + } + + if (vol.getType() == Volume.Type.ROOT) { + s_logger.info("Chose disk controller for vol " + vol.getType() + " -> " + controllerInfo.first() + + ", based on root disk controller settings at global configuration setting."); + return controllerInfo.first(); + } else { + s_logger.info("Chose disk controller for vol " + vol.getType() + " -> " + controllerInfo.second() + + ", based on default data disk controller setting i.e. Operating system recommended."); // Need to bring in global configuration setting & template level setting. + return controllerInfo.second(); + } + } private void postDiskConfigBeforeStart(VirtualMachineMO vmMo, VirtualMachineTO vmSpec, DiskTO[] sortedDisks, int ideControllerKey, int scsiControllerKey, Map iqnToPath, VmwareHypervisorHost hyperHost, VmwareContext context) throws Exception { VirtualMachineDiskInfoBuilder diskInfoBuilder = vmMo.getDiskInfoBuilder(); @@ -3118,8 +3276,13 @@ protected Answer execute(MigrateWithStorageCommand cmd) { relocateSpec.setDatastore(morTgtDatastore); } diskLocator = new VirtualMachineRelocateSpecDiskLocator(); - diskLocator.setDatastore(morTgtDatastore); - int diskId = getVirtualDiskInfo(vmMo, volume.getPath() + ".vmdk"); + diskLocator.setDatastore(morDsAtSource); + Pair diskInfo = getVirtualDiskInfo(vmMo, volume.getPath() + ".vmdk"); + String vmdkAbsFile = getAbsoluteVmdkFile(diskInfo.first()); + if (vmdkAbsFile != null && !vmdkAbsFile.isEmpty()) { + vmMo.updateAdapterTypeIfRequired(vmdkAbsFile); + } + int diskId = diskInfo.first().getKey(); diskLocator.setDiskId(diskId); diskLocators.add(diskLocator); @@ -3281,7 +3444,13 @@ private Answer execute(MigrateVolumeCommand cmd) { DatastoreMO targetDsMo = new DatastoreMO(srcHyperHost.getContext(), morDs); String fullVolumePath = VmwareStorageLayoutHelper.getVmwareDatastorePathFromVmdkFileName(targetDsMo, vmName, volumePath + ".vmdk"); - int diskId = getVirtualDiskInfo(vmMo, volumePath + ".vmdk"); + Pair diskInfo = getVirtualDiskInfo(vmMo, volumePath + ".vmdk"); + String vmdkAbsFile = getAbsoluteVmdkFile(diskInfo.first()); + if (vmdkAbsFile != null && !vmdkAbsFile.isEmpty()) { + vmMo.updateAdapterTypeIfRequired(vmdkAbsFile); + } + int diskId = diskInfo.first().getKey(); + diskLocator = new VirtualMachineRelocateSpecDiskLocator(); diskLocator.setDatastore(morDs); diskLocator.setDiskId(diskId); @@ -3337,12 +3506,12 @@ private Answer execute(MigrateVolumeCommand cmd) { } } - private int getVirtualDiskInfo(VirtualMachineMO vmMo, String srcDiskName) throws Exception { + private Pair getVirtualDiskInfo(VirtualMachineMO vmMo, String srcDiskName) throws Exception { Pair deviceInfo = vmMo.getDiskDevice(srcDiskName, false); if (deviceInfo == null) { throw new Exception("No such disk device: " + srcDiskName); } - return deviceInfo.first().getKey(); + return deviceInfo; } private VmwareHypervisorHost getTargetHyperHost(DatacenterMO dcMo, String destIp) throws Exception { @@ -4943,8 +5112,10 @@ public boolean configure(String name, Map params) throws Configu value = (String)params.get("vmware.root.disk.controller"); if (value != null && value.equalsIgnoreCase("scsi")) _rootDiskController = DiskControllerType.scsi; - else + else if (value != null && value.equalsIgnoreCase("ide")) _rootDiskController = DiskControllerType.ide; + else + _rootDiskController = DiskControllerType.osdefault; Integer intObj = (Integer)params.get("ports.per.dvportgroup"); if (intObj != null) @@ -5168,4 +5339,14 @@ public Answer execute(DestroyCommand cmd) { return new Answer(cmd, false, msg); } } + + private String getAbsoluteVmdkFile(VirtualDisk disk) { + String vmdkAbsFile = null; + VirtualDeviceBackingInfo backingInfo = disk.getBacking(); + if (backingInfo instanceof VirtualDiskFlatVer2BackingInfo) { + VirtualDiskFlatVer2BackingInfo diskBackingInfo = (VirtualDiskFlatVer2BackingInfo)backingInfo; + vmdkAbsFile = diskBackingInfo.getFileName(); + } + return vmdkAbsFile; + } } diff --git a/plugins/hypervisors/vmware/src/com/cloud/storage/resource/VmwareStorageProcessor.java b/plugins/hypervisors/vmware/src/com/cloud/storage/resource/VmwareStorageProcessor.java index 0ab97c0920f1..bc454e7425d3 100644 --- a/plugins/hypervisors/vmware/src/com/cloud/storage/resource/VmwareStorageProcessor.java +++ b/plugins/hypervisors/vmware/src/com/cloud/storage/resource/VmwareStorageProcessor.java @@ -71,12 +71,14 @@ import com.cloud.agent.api.to.DiskTO; import com.cloud.agent.api.to.NfsTO; import com.cloud.hypervisor.vmware.manager.VmwareHostService; +import com.cloud.hypervisor.vmware.manager.VmwareManager; import com.cloud.hypervisor.vmware.manager.VmwareStorageMount; import com.cloud.hypervisor.vmware.mo.ClusterMO; import com.cloud.hypervisor.vmware.mo.CustomFieldConstants; import com.cloud.hypervisor.vmware.mo.DatacenterMO; import com.cloud.hypervisor.vmware.mo.DatastoreFile; import com.cloud.hypervisor.vmware.mo.DatastoreMO; +import com.cloud.hypervisor.vmware.mo.DiskControllerType; import com.cloud.hypervisor.vmware.mo.HostDatastoreSystemMO; import com.cloud.hypervisor.vmware.mo.HostMO; import com.cloud.hypervisor.vmware.mo.HostStorageSystemMO; @@ -100,15 +102,16 @@ import com.cloud.utils.exception.CloudRuntimeException; import com.cloud.utils.script.Script; import com.cloud.vm.VirtualMachine.PowerState; +import com.cloud.vm.VmDetailConstants; public class VmwareStorageProcessor implements StorageProcessor { private static final Logger s_logger = Logger.getLogger(VmwareStorageProcessor.class); - private VmwareHostService hostService; - private boolean _fullCloneFlag; - private VmwareStorageMount mountService; - private VmwareResource resource; - private Integer _timeout; + private final VmwareHostService hostService; + private final boolean _fullCloneFlag; + private final VmwareStorageMount mountService; + private final VmwareResource resource; + private final Integer _timeout; protected Integer _shutdownWaitMs; private final Gson _gson; private final StorageLayer _storage = new JavaStorageLayer(); @@ -1279,10 +1282,10 @@ public Answer attachVolume(AttachCommand cmd) { String storageHost = details.get(DiskTO.STORAGE_HOST); int storagePort = Integer.parseInt(details.get(DiskTO.STORAGE_PORT)); - return this.attachVolume(cmd, cmd.getDisk(), true, isManaged, cmd.getVmName(), iScsiName, storageHost, storagePort); + return this.attachVolume(cmd, cmd.getDisk(), true, isManaged, cmd.getVmName(), iScsiName, storageHost, storagePort, cmd.getControllerInfo()); } - private Answer attachVolume(Command cmd, DiskTO disk, boolean isAttach, boolean isManaged, String vmName, String iScsiName, String storageHost, int storagePort) { + private Answer attachVolume(Command cmd, DiskTO disk, boolean isAttach, boolean isManaged, String vmName, String iScsiName, String storageHost, int storagePort, Map controllerInfo) { VolumeObjectTO volumeTO = (VolumeObjectTO)disk.getData(); DataStoreTO primaryStore = volumeTO.getDataStore(); try { @@ -1342,7 +1345,24 @@ private Answer attachVolume(Command cmd, DiskTO disk, boolean isAttach, boolean AttachAnswer answer = new AttachAnswer(disk); if (isAttach) { - vmMo.attachDisk(new String[] { datastoreVolumePath }, morDs); + String dataDiskController = controllerInfo.get(VmDetailConstants.DATA_DISK_CONTROLLER); + String rootDiskController = controllerInfo.get(VmDetailConstants.ROOK_DISK_CONTROLLER); + DiskControllerType rootDiskControllerType = DiskControllerType.getType(rootDiskController); + + if (dataDiskController == null) { + dataDiskController = getLegacyVmDataDiskController(); + } else if ((rootDiskControllerType == DiskControllerType.lsilogic) || + (rootDiskControllerType == DiskControllerType.lsisas1068) || + (rootDiskControllerType == DiskControllerType.pvscsi) || + (rootDiskControllerType == DiskControllerType.buslogic)) { + //TODO: Support mix of SCSI controller types for single VM. If root disk is already over + //a SCSI controller then use the same for data volume as well. This limitation will go once mix + //of SCSI controller types for single VM. + dataDiskController = rootDiskController; + } else if (DiskControllerType.getType(dataDiskController) == DiskControllerType.osdefault) { + dataDiskController = vmMo.getRecommendedDiskController(null); + } + vmMo.attachDisk(new String[] {datastoreVolumePath}, morDs, dataDiskController); } else { vmMo.removeAllSnapshots(); vmMo.detachDisk(datastoreVolumePath, false); @@ -1480,7 +1500,7 @@ public Answer dettachIso(DettachCommand cmd) { @Override public Answer dettachVolume(DettachCommand cmd) { - return this.attachVolume(cmd, cmd.getDisk(), false, cmd.isManaged(), cmd.getVmName(), cmd.get_iScsiName(), cmd.getStorageHost(), cmd.getStoragePort()); + return this.attachVolume(cmd, cmd.getDisk(), false, cmd.isManaged(), cmd.getVmName(), cmd.get_iScsiName(), cmd.getStorageHost(), cmd.getStoragePort(), null); } @Override @@ -2225,4 +2245,28 @@ private static String deriveTemplateUuidOnHost(VmwareHypervisorHost hyperHost, S templateUuid = templateUuid.replaceAll("-", ""); return templateUuid; } + + private String getControllerFromConfigurationSetting() throws Exception { + String diskController = null; + VmwareContext context = null; + try { + context = hostService.getServiceContext(null); + VmwareManager mgr = context.getStockObject(VmwareManager.CONTEXT_STOCK_NAME); + diskController = mgr.getDataDiskController(); + } catch (Throwable e) { + if (e instanceof RemoteException) { + s_logger.warn("Encounter remote exception to vCenter, invalidate VMware session context"); + hostService.invalidateServiceContext(context); + } + + String details = "Failed to connect to vCenter due to " + VmwareHelper.getExceptionMessage(e); + s_logger.error(details, e); + } + + return diskController; + } + + private String getLegacyVmDataDiskController() throws Exception { + return DiskControllerType.lsilogic.toString(); + } } diff --git a/server/src/com/cloud/storage/VolumeApiServiceImpl.java b/server/src/com/cloud/storage/VolumeApiServiceImpl.java index b45b8aa872a1..fa2888ab3f54 100644 --- a/server/src/com/cloud/storage/VolumeApiServiceImpl.java +++ b/server/src/com/cloud/storage/VolumeApiServiceImpl.java @@ -138,6 +138,7 @@ import com.cloud.vm.VMInstanceVO; import com.cloud.vm.VirtualMachine; import com.cloud.vm.VirtualMachine.State; +import com.cloud.vm.VmDetailConstants; import com.cloud.vm.VmWork; import com.cloud.vm.VmWorkAttachVolume; import com.cloud.vm.VmWorkConstants; @@ -2319,6 +2320,11 @@ private VolumeVO sendAttachVolumeCommand(UserVmVO vm, VolumeVO volumeToAttach, L details.put(DiskTO.CHAP_TARGET_USERNAME, chapInfo.getTargetUsername()); details.put(DiskTO.CHAP_TARGET_SECRET, chapInfo.getTargetSecret()); } + _userVmDao.loadDetails(vm); + Map controllerInfo = new HashMap(); + controllerInfo.put(VmDetailConstants.ROOK_DISK_CONTROLLER, vm.getDetail(VmDetailConstants.ROOK_DISK_CONTROLLER)); + controllerInfo.put(VmDetailConstants.DATA_DISK_CONTROLLER, vm.getDetail(VmDetailConstants.DATA_DISK_CONTROLLER)); + cmd.setControllerInfo(controllerInfo); try { answer = (AttachAnswer)_agentMgr.send(hostId, cmd); diff --git a/server/src/com/cloud/vm/UserVmManagerImpl.java b/server/src/com/cloud/vm/UserVmManagerImpl.java index 66f7fc1d8d6d..6f49bb2b5c53 100755 --- a/server/src/com/cloud/vm/UserVmManagerImpl.java +++ b/server/src/com/cloud/vm/UserVmManagerImpl.java @@ -3112,8 +3112,17 @@ public UserVmVO doInTransaction(TransactionStatus status) throws InsufficientCap if (guestOS.getDisplayName().toLowerCase().contains("apple mac os")) { vm.setDetail("smc.present", "TRUE"); vm.setDetail(VmDetailConstants.ROOK_DISK_CONTROLLER, "scsi"); + vm.setDetail(VmDetailConstants.DATA_DISK_CONTROLLER, "scsi"); vm.setDetail("firmware", "efi"); s_logger.info("guestOS is OSX : overwrite root disk controller to scsi, use smc and efi"); + } else { + String controllerSetting = _configDao.getValue("vmware.root.disk.controller"); + vm.setDetail(VmDetailConstants.ROOK_DISK_CONTROLLER, controllerSetting); + if (controllerSetting.equalsIgnoreCase("scsi")) { + vm.setDetail(VmDetailConstants.DATA_DISK_CONTROLLER, "scsi"); + } else { + vm.setDetail(VmDetailConstants.DATA_DISK_CONTROLLER, "osdefault"); + } } } diff --git a/vmware-base/src/com/cloud/hypervisor/vmware/mo/ClusterMO.java b/vmware-base/src/com/cloud/hypervisor/vmware/mo/ClusterMO.java index 1b72b73bd887..b5f0500d3915 100755 --- a/vmware-base/src/com/cloud/hypervisor/vmware/mo/ClusterMO.java +++ b/vmware-base/src/com/cloud/hypervisor/vmware/mo/ClusterMO.java @@ -34,6 +34,7 @@ import com.vmware.vim25.CustomFieldStringValue; import com.vmware.vim25.DatastoreInfo; import com.vmware.vim25.DynamicProperty; +import com.vmware.vim25.GuestOsDescriptor; import com.vmware.vim25.HostHardwareSummary; import com.vmware.vim25.HostIpRouteEntry; import com.vmware.vim25.HostRuntimeInfo; @@ -46,9 +47,11 @@ import com.vmware.vim25.PropertyFilterSpec; import com.vmware.vim25.PropertySpec; import com.vmware.vim25.TraversalSpec; +import com.vmware.vim25.VirtualMachineConfigOption; import com.vmware.vim25.VirtualMachineConfigSpec; import com.cloud.hypervisor.vmware.util.VmwareContext; +import com.cloud.hypervisor.vmware.util.VmwareHelper; import com.cloud.utils.Pair; import com.cloud.utils.exception.CloudRuntimeException; @@ -271,7 +274,7 @@ public void importVmFromOVF(String ovfFilePath, String vmName, DatastoreMO dsMo, @Override public boolean createBlankVm(String vmName, String vmInternalCSName, int cpuCount, int cpuSpeedMHz, int cpuReservedMHz, boolean limitCpuUse, int memoryMB, - int memoryReserveMB, String guestOsIdentifier, ManagedObjectReference morDs, boolean snapshotDirToParent) throws Exception { + int memoryReserveMB, String guestOsIdentifier, ManagedObjectReference morDs, boolean snapshotDirToParent, Pair controllerInfo, Boolean systemVm) throws Exception { if (s_logger.isTraceEnabled()) s_logger.trace("vCenter API trace - createBlankVm(). target MOR: " + _mor.getValue() + ", vmName: " + vmName + ", cpuCount: " + cpuCount + ", cpuSpeedMhz: " + @@ -280,7 +283,7 @@ public boolean createBlankVm(String vmName, String vmInternalCSName, int cpuCoun boolean result = HypervisorHostHelper.createBlankVm(this, vmName, vmInternalCSName, cpuCount, cpuSpeedMHz, cpuReservedMHz, limitCpuUse, memoryMB, memoryReserveMB, - guestOsIdentifier, morDs, snapshotDirToParent); + guestOsIdentifier, morDs, snapshotDirToParent, controllerInfo, systemVm); if (s_logger.isTraceEnabled()) s_logger.trace("vCenter API trace - createBlankVm() done"); @@ -582,4 +585,32 @@ public LicenseAssignmentManagerMO getLicenseAssignmentManager() throws Exception // LicenseAssignmentManager deals with only host/vcenter licenses only. Has nothing todo with cluster throw new CloudRuntimeException("Unable to get LicenseAssignmentManager at cluster level"); } + private ManagedObjectReference getEnvironmentBrowser() throws Exception { + if (_environmentBrowser == null) { + _environmentBrowser = _context.getVimClient().getMoRefProp(_mor, "environmentBrowser"); + } + return _environmentBrowser; + } + @Override + public String getRecommendedDiskController(String guestOsId) throws Exception { + VirtualMachineConfigOption vmConfigOption = _context.getService().queryConfigOption(getEnvironmentBrowser(), null, null); + GuestOsDescriptor guestOsDescriptor = null; + String diskController = null; + List guestDescriptors = vmConfigOption.getGuestOSDescriptor(); + for (GuestOsDescriptor descriptor : guestDescriptors) { + if (guestOsId != null && guestOsId.equalsIgnoreCase(descriptor.getId())) { + guestOsDescriptor = descriptor; + break; + } + } + if (guestOsDescriptor != null) { + diskController = VmwareHelper.getRecommendedDiskControllerFromDescriptor(guestOsDescriptor); + s_logger.debug("Retrieved recommended disk controller for guest OS : " + guestOsId + " in cluster " + getHyperHostName() + " : " + diskController); + return diskController; + } else { + String msg = "Unable to retrieve recommended disk controller for guest OS : " + guestOsId + " in cluster " + getHyperHostName(); + s_logger.error(msg); + throw new CloudRuntimeException(msg); + } + } } diff --git a/vmware-base/src/com/cloud/hypervisor/vmware/mo/DiskControllerType.java b/vmware-base/src/com/cloud/hypervisor/vmware/mo/DiskControllerType.java index def584df1fd1..3e6b8bb53e04 100644 --- a/vmware-base/src/com/cloud/hypervisor/vmware/mo/DiskControllerType.java +++ b/vmware-base/src/com/cloud/hypervisor/vmware/mo/DiskControllerType.java @@ -17,5 +17,34 @@ package com.cloud.hypervisor.vmware.mo; public enum DiskControllerType { - ide, scsi + ide, + scsi, + osdefault, + lsilogic, + lsisas1068, + buslogic, + pvscsi, + none; + public static DiskControllerType getType(String diskController) { + if (diskController == null || diskController.equalsIgnoreCase("osdefault")) { + return DiskControllerType.osdefault; + } else if (diskController.equalsIgnoreCase("vim.vm.device.VirtualLsiLogicSASController") || diskController.equalsIgnoreCase("VirtualLsiLogicSASController") + || diskController.equalsIgnoreCase(ScsiDiskControllerType.LSILOGIC_SAS)) { + return DiskControllerType.lsisas1068; + } else if (diskController.equalsIgnoreCase("vim.vm.device.VirtualLsiLogicController") || diskController.equalsIgnoreCase("VirtualLsiLogicController") + || diskController.equalsIgnoreCase(ScsiDiskControllerType.LSILOGIC_PARALLEL) || diskController.equalsIgnoreCase("scsi")) { + return DiskControllerType.lsilogic; + } else if (diskController.equalsIgnoreCase("vim.vm.device.VirtualIDEController") || diskController.equalsIgnoreCase("VirtualIDEController") + || diskController.equalsIgnoreCase("ide")) { + return DiskControllerType.ide; + } else if (diskController.equalsIgnoreCase("vim.vm.device.ParaVirtualSCSIController") || diskController.equalsIgnoreCase("ParaVirtualSCSIController") + || diskController.equalsIgnoreCase(ScsiDiskControllerType.VMWARE_PARAVIRTUAL)) { + return DiskControllerType.pvscsi; + } else if (diskController.equalsIgnoreCase("vim.vm.device.VirtualBusLogicController") || diskController.equalsIgnoreCase("VirtualBusLogicController") + || diskController.equalsIgnoreCase(ScsiDiskControllerType.BUSLOGIC)) { + return DiskControllerType.buslogic; + } else { + return DiskControllerType.none; + } + } } diff --git a/vmware-base/src/com/cloud/hypervisor/vmware/mo/HostMO.java b/vmware-base/src/com/cloud/hypervisor/vmware/mo/HostMO.java index b5844e9a98bd..a635712ffcbe 100755 --- a/vmware-base/src/com/cloud/hypervisor/vmware/mo/HostMO.java +++ b/vmware-base/src/com/cloud/hypervisor/vmware/mo/HostMO.java @@ -734,16 +734,17 @@ public void importVmFromOVF(String ovfFilePath, String vmName, DatastoreMO dsMo, @Override public boolean createBlankVm(String vmName, String vmInternalCSName, int cpuCount, int cpuSpeedMHz, int cpuReservedMHz, boolean limitCpuUse, int memoryMB, - int memoryReserveMB, String guestOsIdentifier, ManagedObjectReference morDs, boolean snapshotDirToParent) throws Exception { + int memoryReserveMB, String guestOsIdentifier, ManagedObjectReference morDs, boolean snapshotDirToParent, Pair controllerInfo, Boolean systemVm) throws Exception { if (s_logger.isTraceEnabled()) s_logger.trace("vCenter API trace - createBlankVm(). target MOR: " + _mor.getValue() + ", vmName: " + vmName + ", cpuCount: " + cpuCount + ", cpuSpeedMhz: " + cpuSpeedMHz + ", cpuReservedMHz: " + cpuReservedMHz + ", limitCpu: " + limitCpuUse + ", memoryMB: " + memoryMB + ", guestOS: " + guestOsIdentifier + - ", datastore: " + morDs.getValue() + ", snapshotDirToParent: " + snapshotDirToParent); + ", datastore: " + morDs.getValue() + ", snapshotDirToParent: " + snapshotDirToParent + + ", controllerInfo:[" + controllerInfo.first() + "," + controllerInfo.second() + "], systemvm: " + systemVm); boolean result = HypervisorHostHelper.createBlankVm(this, vmName, vmInternalCSName, cpuCount, cpuSpeedMHz, cpuReservedMHz, limitCpuUse, memoryMB, memoryReserveMB, - guestOsIdentifier, morDs, snapshotDirToParent); + guestOsIdentifier, morDs, snapshotDirToParent, controllerInfo, systemVm); if (s_logger.isTraceEnabled()) s_logger.trace("vCenter API trace - createBlankVm() done"); @@ -1045,6 +1046,16 @@ public void enableVncOnHostFirewall() throws Exception { } } + @Override + public String getRecommendedDiskController(String guestOsId) throws Exception { + ManagedObjectReference morParent = getParentMor(); + if (morParent.getType().equals("ClusterComputeResource")) { + ClusterMO clusterMo = new ClusterMO(_context, morParent); + return clusterMo.getRecommendedDiskController(guestOsId); + } + return null; + } + public String getHostManagementIp(String managementPortGroup) throws Exception { HostNetworkInfo netInfo = getHostNetworkInfo(); diff --git a/vmware-base/src/com/cloud/hypervisor/vmware/mo/HypervisorHostHelper.java b/vmware-base/src/com/cloud/hypervisor/vmware/mo/HypervisorHostHelper.java index a3a9d89af988..f012d4d541e6 100755 --- a/vmware-base/src/com/cloud/hypervisor/vmware/mo/HypervisorHostHelper.java +++ b/vmware-base/src/com/cloud/hypervisor/vmware/mo/HypervisorHostHelper.java @@ -68,18 +68,24 @@ import com.vmware.vim25.OvfCreateImportSpecParams; import com.vmware.vim25.OvfCreateImportSpecResult; import com.vmware.vim25.OvfFileItem; +import com.vmware.vim25.ParaVirtualSCSIController; import com.vmware.vim25.VMwareDVSConfigSpec; import com.vmware.vim25.VMwareDVSPortSetting; import com.vmware.vim25.VMwareDVSPortgroupPolicy; import com.vmware.vim25.VMwareDVSPvlanConfigSpec; import com.vmware.vim25.VMwareDVSPvlanMapEntry; +import com.vmware.vim25.VirtualBusLogicController; +import com.vmware.vim25.VirtualController; import com.vmware.vim25.VirtualDeviceConfigSpec; import com.vmware.vim25.VirtualDeviceConfigSpecOperation; +import com.vmware.vim25.VirtualIDEController; import com.vmware.vim25.VirtualLsiLogicController; +import com.vmware.vim25.VirtualLsiLogicSASController; import com.vmware.vim25.VirtualMachineConfigSpec; import com.vmware.vim25.VirtualMachineFileInfo; import com.vmware.vim25.VirtualMachineGuestOsIdentifier; import com.vmware.vim25.VirtualMachineVideoCard; +import com.vmware.vim25.VirtualSCSIController; import com.vmware.vim25.VirtualSCSISharing; import com.vmware.vim25.VmwareDistributedVirtualSwitchPvlanSpec; import com.vmware.vim25.VmwareDistributedVirtualSwitchVlanIdSpec; @@ -1125,11 +1131,13 @@ public static ManagedObjectReference waitForNetworkReady(HostMO hostMo, String n } public static boolean createBlankVm(VmwareHypervisorHost host, String vmName, String vmInternalCSName, int cpuCount, int cpuSpeedMHz, int cpuReservedMHz, - boolean limitCpuUse, int memoryMB, int memoryReserveMB, String guestOsIdentifier, ManagedObjectReference morDs, boolean snapshotDirToParent) throws Exception { + boolean limitCpuUse, int memoryMB, int memoryReserveMB, String guestOsIdentifier, ManagedObjectReference morDs, boolean snapshotDirToParent, + Pair controllerInfo, Boolean systemVm) throws Exception { if (s_logger.isInfoEnabled()) s_logger.info("Create blank VM. cpuCount: " + cpuCount + ", cpuSpeed(MHz): " + cpuSpeedMHz + ", mem(Mb): " + memoryMB); + VirtualDeviceConfigSpec controllerSpec = null; // VM config basics VirtualMachineConfigSpec vmConfig = new VirtualMachineConfigSpec(); vmConfig.setName(vmName); @@ -1138,14 +1146,33 @@ public static boolean createBlankVm(VmwareHypervisorHost host, String vmName, St VmwareHelper.setBasicVmConfig(vmConfig, cpuCount, cpuSpeedMHz, cpuReservedMHz, memoryMB, memoryReserveMB, guestOsIdentifier, limitCpuUse); - // Scsi controller - VirtualLsiLogicController scsiController = new VirtualLsiLogicController(); - scsiController.setSharedBus(VirtualSCSISharing.NO_SHARING); - scsiController.setBusNumber(0); - scsiController.setKey(1); - VirtualDeviceConfigSpec scsiControllerSpec = new VirtualDeviceConfigSpec(); - scsiControllerSpec.setDevice(scsiController); - scsiControllerSpec.setOperation(VirtualDeviceConfigSpecOperation.ADD); + String recommendedController = host.getRecommendedDiskController(guestOsIdentifier); + String newRootDiskController = controllerInfo.first(); + String newDataDiskController = controllerInfo.second(); + if (DiskControllerType.getType(controllerInfo.first()) == DiskControllerType.osdefault) { + newRootDiskController = recommendedController; + } + if (DiskControllerType.getType(controllerInfo.second()) == DiskControllerType.osdefault) { + newDataDiskController = recommendedController; + } + + Pair updatedControllerInfo = new Pair(newRootDiskController, newDataDiskController); + String scsiDiskController = HypervisorHostHelper.getScsiController(updatedControllerInfo, recommendedController); + // If there is requirement for a SCSI controller, ensure to create those. + if (scsiDiskController != null) { + int busNum = 0; + int maxControllerCount = VmwareHelper.MAX_SCSI_CONTROLLER_COUNT; + if (systemVm) { + maxControllerCount = 1; + } + while (busNum < maxControllerCount) { + VirtualDeviceConfigSpec scsiControllerSpec = new VirtualDeviceConfigSpec(); + scsiControllerSpec = getControllerSpec(DiskControllerType.getType(scsiDiskController).toString(), busNum); + + vmConfig.getDeviceChange().add(scsiControllerSpec); + busNum++; + } + } VirtualMachineFileInfo fileInfo = new VirtualMachineFileInfo(); DatastoreMO dsMo = new DatastoreMO(host.getContext(), morDs); @@ -1160,7 +1187,6 @@ public static boolean createBlankVm(VmwareHypervisorHost host, String vmName, St videoDeviceSpec.setDevice(videoCard); videoDeviceSpec.setOperation(VirtualDeviceConfigSpecOperation.ADD); - vmConfig.getDeviceChange().add(scsiControllerSpec); vmConfig.getDeviceChange().add(videoDeviceSpec); if (host.createVm(vmConfig)) { // Here, when attempting to find the VM, we need to use the name @@ -1190,6 +1216,34 @@ public static boolean createBlankVm(VmwareHypervisorHost host, String vmName, St return false; } + private static VirtualDeviceConfigSpec getControllerSpec(String diskController, int busNum) { + VirtualDeviceConfigSpec controllerSpec = new VirtualDeviceConfigSpec(); + VirtualController controller = null; + + if (diskController.equalsIgnoreCase(DiskControllerType.ide.toString())) { + controller = new VirtualIDEController(); + } else if (DiskControllerType.pvscsi == DiskControllerType.getType(diskController)) { + controller = new ParaVirtualSCSIController(); + } else if (DiskControllerType.lsisas1068 == DiskControllerType.getType(diskController)) { + controller = new VirtualLsiLogicSASController(); + } else if (DiskControllerType.buslogic == DiskControllerType.getType(diskController)) { + controller = new VirtualBusLogicController(); + } else if (DiskControllerType.lsilogic == DiskControllerType.getType(diskController)) { + controller = new VirtualLsiLogicController(); + } + + if (!diskController.equalsIgnoreCase(DiskControllerType.ide.toString())) { + ((VirtualSCSIController)controller).setSharedBus(VirtualSCSISharing.NO_SHARING); + } + + controller.setBusNumber(busNum); + controller.setKey(busNum - VmwareHelper.MAX_SCSI_CONTROLLER_COUNT); + + controllerSpec.setDevice(controller); + controllerSpec.setOperation(VirtualDeviceConfigSpecOperation.ADD); + + return controllerSpec; + } public static VirtualMachineMO createWorkerVM(VmwareHypervisorHost hyperHost, DatastoreMO dsMo, String vmName) throws Exception { // Allow worker VM to float within cluster so that we will have better chance to @@ -1398,4 +1452,42 @@ public void action(Long param) { } } } + + public static String getScsiController(Pair controllerInfo, String recommendedController) { + String rootDiskController = controllerInfo.first(); + String dataDiskController = controllerInfo.second(); + + // If "osdefault" is specified as controller type, then translate to actual recommended controller. + if (VmwareHelper.isControllerOsRecommended(rootDiskController)) { + rootDiskController = recommendedController; + } + if (VmwareHelper.isControllerOsRecommended(dataDiskController)) { + dataDiskController = recommendedController; + } + + String scsiDiskController = null; //If any of the controller provided is SCSI then return it's sub-type. + if (isIdeController(rootDiskController) && isIdeController(dataDiskController)) { + //Default controllers would exist + return null; + } else if (isIdeController(rootDiskController) || isIdeController(dataDiskController)) { + // Only one of the controller types is IDE. Pick the other controller type to create controller. + if (isIdeController(rootDiskController)) { + scsiDiskController = dataDiskController; + } else { + scsiDiskController = rootDiskController; + } + } else if (DiskControllerType.getType(rootDiskController) != DiskControllerType.getType(dataDiskController)) { + // Both ROOT and DATA controllers are SCSI controllers but different sub-types, then prefer ROOT controller + scsiDiskController = rootDiskController; + } else { + // Both are SCSI controllers. + scsiDiskController = rootDiskController; + } + return scsiDiskController; + } + + public static boolean isIdeController(String controller) { + return DiskControllerType.getType(controller) == DiskControllerType.ide; + } + } diff --git a/vmware-base/src/com/cloud/hypervisor/vmware/mo/ScsiDiskControllerType.java b/vmware-base/src/com/cloud/hypervisor/vmware/mo/ScsiDiskControllerType.java new file mode 100644 index 000000000000..8b9a949bbefe --- /dev/null +++ b/vmware-base/src/com/cloud/hypervisor/vmware/mo/ScsiDiskControllerType.java @@ -0,0 +1,24 @@ +//Licensed to the Apache Software Foundation (ASF) under one +//or more contributor license agreements. See the NOTICE file +//distributed with this work for additional information +//regarding copyright ownership. The ASF licenses this file +//to you under the Apache License, Version 2.0 (the +//"License"); you may not use this file except in compliance +//with the License. You may obtain a copy of the License at +// +//http://www.apache.org/licenses/LICENSE-2.0 +// +//Unless required by applicable law or agreed to in writing, +//software distributed under the License is distributed on an +//"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +//KIND, either express or implied. See the License for the +//specific language governing permissions and limitations +//under the License. +package com.cloud.hypervisor.vmware.mo; + +public interface ScsiDiskControllerType { + public final static String LSILOGIC_PARALLEL = "lsilogic"; + public final static String LSILOGIC_SAS = "lsisas1068"; + public final static String BUSLOGIC = "buslogic"; + public final static String VMWARE_PARAVIRTUAL = "pvscsi"; +} diff --git a/vmware-base/src/com/cloud/hypervisor/vmware/mo/VirtualMachineMO.java b/vmware-base/src/com/cloud/hypervisor/vmware/mo/VirtualMachineMO.java index ac5b41411237..d61cb2ea15b0 100644 --- a/vmware-base/src/com/cloud/hypervisor/vmware/mo/VirtualMachineMO.java +++ b/vmware-base/src/com/cloud/hypervisor/vmware/mo/VirtualMachineMO.java @@ -55,12 +55,15 @@ import com.vmware.vim25.OvfCreateDescriptorParams; import com.vmware.vim25.OvfCreateDescriptorResult; import com.vmware.vim25.OvfFile; +import com.vmware.vim25.ParaVirtualSCSIController; import com.vmware.vim25.PropertyFilterSpec; import com.vmware.vim25.PropertySpec; import com.vmware.vim25.TraversalSpec; +import com.vmware.vim25.VirtualBusLogicController; import com.vmware.vim25.VirtualCdrom; import com.vmware.vim25.VirtualCdromIsoBackingInfo; import com.vmware.vim25.VirtualCdromRemotePassthroughBackingInfo; +import com.vmware.vim25.VirtualController; import com.vmware.vim25.VirtualDevice; import com.vmware.vim25.VirtualDeviceBackingInfo; import com.vmware.vim25.VirtualDeviceConfigSpec; @@ -80,6 +83,7 @@ import com.vmware.vim25.VirtualHardwareOption; import com.vmware.vim25.VirtualIDEController; import com.vmware.vim25.VirtualLsiLogicController; +import com.vmware.vim25.VirtualLsiLogicSASController; import com.vmware.vim25.VirtualMachineCloneSpec; import com.vmware.vim25.VirtualMachineConfigInfo; import com.vmware.vim25.VirtualMachineConfigOption; @@ -97,7 +101,6 @@ import com.vmware.vim25.VirtualMachineRuntimeInfo; import com.vmware.vim25.VirtualMachineSnapshotInfo; import com.vmware.vim25.VirtualMachineSnapshotTree; -import com.vmware.vim25.VirtualPCIController; import com.vmware.vim25.VirtualSCSIController; import com.vmware.vim25.VirtualSCSISharing; @@ -1086,6 +1089,130 @@ public void createDisk(String vmdkDatastorePath, VirtualDiskType diskType, Virtu s_logger.trace("vCenter API trace - createDisk() done(successfully)"); } + public void updateVmdkAdapter(String vmdkFileName, String newAdapterType) throws Exception { + Pair vmdkInfo = getVmdkFileInfo(vmdkFileName); + VmdkFileDescriptor vmdkFileDescriptor = vmdkInfo.first(); + boolean isVmfsSparseFile = vmdkFileDescriptor.isVmfsSparseFile(); + if (!isVmfsSparseFile) { + String currentAdapterType = vmdkFileDescriptor.getAdapterType(); + if (!currentAdapterType.equalsIgnoreCase(newAdapterType)) { + s_logger.info("Updating adapter type to " + newAdapterType + " for VMDK file " + vmdkFileName); + Pair dcInfo = getOwnerDatacenter(); + byte[] newVmdkContent = vmdkFileDescriptor.changeVmdkAdapterType(vmdkInfo.second(), newAdapterType); + String vmdkUploadUrl = getContext().composeDatastoreBrowseUrl(dcInfo.first().getName(), vmdkFileName); + getContext().uploadResourceContent(vmdkUploadUrl, newVmdkContent); + s_logger.info("Updated VMDK file " + vmdkFileName); + } + } + } + + public void updateAdapterTypeIfRequired(String vmdkFileName) throws Exception { + // Validate existing adapter type of VMDK file. Update it with a supported adapter type if validation fails. + Pair vmdkInfo = getVmdkFileInfo(vmdkFileName); + VmdkFileDescriptor vmdkFileDescriptor = vmdkInfo.first(); + + boolean isVmfsSparseFile = vmdkFileDescriptor.isVmfsSparseFile(); + if (!isVmfsSparseFile) { + String currentAdapterTypeStr = vmdkFileDescriptor.getAdapterType(); + if (s_logger.isTraceEnabled()) { + s_logger.trace("Detected adapter type " + currentAdapterTypeStr + " for VMDK file " + vmdkFileName); + } + VmdkAdapterType currentAdapterType = VmdkAdapterType.getType(currentAdapterTypeStr); + if (currentAdapterType == VmdkAdapterType.none) { + // Value of currentAdapterType can be VmdkAdapterType.none only if adapter type of vmdk is set to either + // lsisas1068 (SAS controller) or pvscsi (Vmware Paravirtual) only. Valid adapter type for those controllers is lsilogic. + // Hence use adapter type lsilogic. Other adapter types ide, lsilogic, buslogic are valid and does not need to be modified. + VmdkAdapterType newAdapterType = VmdkAdapterType.lsilogic; + s_logger.debug("Updating adapter type to " + newAdapterType + " from " + currentAdapterTypeStr + " for VMDK file " + vmdkFileName); + Pair dcInfo = getOwnerDatacenter(); + byte[] newVmdkContent = vmdkFileDescriptor.changeVmdkAdapterType(vmdkInfo.second(), newAdapterType.toString()); + String vmdkUploadUrl = getContext().composeDatastoreBrowseUrl(dcInfo.first().getName(), vmdkFileName); + + getContext().uploadResourceContent(vmdkUploadUrl, newVmdkContent); + s_logger.debug("Updated VMDK file " + vmdkFileName); + } + } + } + + public void attachDisk(String[] vmdkDatastorePathChain, ManagedObjectReference morDs, String diskController) throws Exception { + + if(s_logger.isTraceEnabled()) + s_logger.trace("vCenter API trace - attachDisk(). target MOR: " + _mor.getValue() + ", vmdkDatastorePath: " + + new Gson().toJson(vmdkDatastorePathChain) + ", datastore: " + morDs.getValue()); + int controllerKey = 0; + int unitNumber = 0; + + if (DiskControllerType.getType(diskController) == DiskControllerType.ide) { + // IDE virtual disk cannot be added if VM is running + if (getPowerState() == VirtualMachinePowerState.POWERED_ON) { + throw new Exception("Adding a virtual disk over IDE controller is not supported while VM is running in VMware hypervisor. Please re-try when VM is not running."); + } + // Get next available unit number and controller key + int ideDeviceCount = getNumberOfIDEDevices(); + if (ideDeviceCount >= VmwareHelper.MAX_IDE_CONTROLLER_COUNT * VmwareHelper.MAX_ALLOWED_DEVICES_IDE_CONTROLLER) { + throw new Exception("Maximum limit of devices supported on IDE controllers [" + VmwareHelper.MAX_IDE_CONTROLLER_COUNT + * VmwareHelper.MAX_ALLOWED_DEVICES_IDE_CONTROLLER + "] is reached."); + } + controllerKey = getIDEControllerKey(ideDeviceCount); + unitNumber = getFreeUnitNumberOnIDEController(controllerKey); + } else { + controllerKey = getScsiDiskControllerKey(diskController); + unitNumber = -1; + } + synchronized (_mor.getValue().intern()) { + VirtualDevice newDisk = VmwareHelper.prepareDiskDevice(this, null, controllerKey, vmdkDatastorePathChain, morDs, unitNumber, 1); + controllerKey = newDisk.getControllerKey(); + unitNumber = newDisk.getUnitNumber(); + VirtualDiskFlatVer2BackingInfo backingInfo = (VirtualDiskFlatVer2BackingInfo)newDisk.getBacking(); + String vmdkFileName = backingInfo.getFileName(); + DiskControllerType diskControllerType = DiskControllerType.getType(diskController); + VmdkAdapterType vmdkAdapterType = VmdkAdapterType.getAdapterType(diskControllerType); + if (vmdkAdapterType == VmdkAdapterType.none) { + String message = "Failed to attach disk due to invalid vmdk adapter type for vmdk file [" + + vmdkFileName + "] with controller : " + diskControllerType; + s_logger.debug(message); + throw new Exception(message); + } + updateVmdkAdapter(vmdkFileName, vmdkAdapterType.toString()); + VirtualMachineConfigSpec reConfigSpec = new VirtualMachineConfigSpec(); + VirtualDeviceConfigSpec deviceConfigSpec = new VirtualDeviceConfigSpec(); + + deviceConfigSpec.setDevice(newDisk); + deviceConfigSpec.setOperation(VirtualDeviceConfigSpecOperation.ADD); + + reConfigSpec.getDeviceChange().add(deviceConfigSpec); + + ManagedObjectReference morTask = _context.getService().reconfigVMTask(_mor, reConfigSpec); + boolean result = _context.getVimClient().waitForTask(morTask); + + if (!result) { + if (s_logger.isTraceEnabled()) + s_logger.trace("vCenter API trace - attachDisk() done(failed)"); + throw new Exception("Failed to attach disk due to " + TaskMO.getTaskFailureInfo(_context, morTask)); + } + + _context.waitForTaskProgressDone(morTask); + } + + if(s_logger.isTraceEnabled()) + s_logger.trace("vCenter API trace - attachDisk() done(successfully)"); + } + + private int getControllerBusNumber(int controllerKey) throws Exception { + List devices = (List)_context.getVimClient(). + getDynamicProperty(_mor, "config.hardware.device"); + + if (devices != null && devices.size() > 0) { + for (VirtualDevice device : devices) { + if (device instanceof VirtualController && device.getKey() == controllerKey) { + return ((VirtualController)device).getBusNumber(); + } + } + } + throw new Exception("SCSI Controller with key " + controllerKey + " is Not Found"); + + } + public void attachDisk(String[] vmdkDatastorePathChain, ManagedObjectReference morDs) throws Exception { if (s_logger.isTraceEnabled()) @@ -1754,20 +1881,17 @@ public VirtualMachineMO cloneFromDiskChain(String clonedVmName, int cpuSpeedMHz, } } - public String getGuestId() throws Exception { - return (String)_context.getVimClient().getDynamicProperty(_mor, "config.guestId"); - } - public GuestOsDescriptor getGuestOsDescriptor(String guestOsId) throws Exception { GuestOsDescriptor guestOsDescriptor = null; - if (guestOsId == null) { - guestOsId = getGuestId(); + String guestId = guestOsId; + if (guestId == null) { + guestId = getGuestId(); } ManagedObjectReference vmEnvironmentBrowser = _context.getVimClient().getMoRefProp(_mor, "environmentBrowser"); VirtualMachineConfigOption vmConfigOption = _context.getService().queryConfigOption(vmEnvironmentBrowser, null, null); List guestDescriptors = vmConfigOption.getGuestOSDescriptor(); for (GuestOsDescriptor descriptor : guestDescriptors) { - if (guestOsId != null && guestOsId.equalsIgnoreCase(descriptor.getId())) { + if (guestId != null && guestId.equalsIgnoreCase(descriptor.getId())) { guestOsDescriptor = descriptor; break; } @@ -1906,6 +2030,134 @@ public void moveAllVmDiskFiles(DatastoreMO destDsMo, String destDsDir, boolean f } } + public int getPvScsiDeviceControllerKeyNoException() throws Exception { + List devices = (List)_context.getVimClient(). + getDynamicProperty(_mor, "config.hardware.device"); + + if (devices != null && devices.size() > 0) { + for (VirtualDevice device : devices) { + if (device instanceof ParaVirtualSCSIController) { + return device.getKey(); + } + } + } + + return -1; + } + + public int getPvScsiDeviceControllerKey() throws Exception { + List devices = (List)_context.getVimClient(). + getDynamicProperty(_mor, "config.hardware.device"); + + if (devices != null && devices.size() > 0) { + for (VirtualDevice device : devices) { + if (device instanceof ParaVirtualSCSIController) { + return device.getKey(); + } + } + } + + assert (false); + throw new Exception("VMware Paravirtual SCSI Controller Not Found"); + } + + public void ensurePvScsiDeviceController(int requiredNumScsiControllers, int availableBusNum) throws Exception { + VirtualMachineConfigSpec vmConfig = new VirtualMachineConfigSpec(); + + int busNum = availableBusNum; + while (busNum < requiredNumScsiControllers) { + ParaVirtualSCSIController scsiController = new ParaVirtualSCSIController(); + + scsiController.setSharedBus(VirtualSCSISharing.NO_SHARING); + scsiController.setBusNumber(busNum); + scsiController.setKey(busNum - VmwareHelper.MAX_SCSI_CONTROLLER_COUNT); + VirtualDeviceConfigSpec scsiControllerSpec = new VirtualDeviceConfigSpec(); + scsiControllerSpec.setDevice(scsiController); + scsiControllerSpec.setOperation(VirtualDeviceConfigSpecOperation.ADD); + + vmConfig.getDeviceChange().add(scsiControllerSpec); + busNum++; + } + + if (configureVm(vmConfig)) { + throw new Exception("Unable to add Scsi controllers to the VM " + getName()); + } else { + s_logger.info("Successfully added " + requiredNumScsiControllers + " SCSI controllers."); + } + } + + public String getRecommendedDiskController(String guestOsId) throws Exception { + String recommendedController; + GuestOsDescriptor guestOsDescriptor = getGuestOsDescriptor(guestOsId); + recommendedController = VmwareHelper.getRecommendedDiskControllerFromDescriptor(guestOsDescriptor); + return recommendedController; + } + + public boolean isPvScsiSupported() throws Exception { + int virtualHardwareVersion; + + virtualHardwareVersion = getVirtualHardwareVersion(); + + // Check if virtual machine is using hardware version 7 or later. + if (virtualHardwareVersion < 7) { + s_logger.error("The virtual hardware version of the VM is " + virtualHardwareVersion + + ", which doesn't support PV SCSI controller type for virtual harddisks. Please upgrade this VM's virtual hardware version to 7 or later."); + return false; + } + return true; + } + + // Would be useful if there exists multiple sub types of SCSI controllers per VM are supported in CloudStack f + public int getScsiDiskControllerKey(String diskController) throws Exception { + List devices = (List)_context.getVimClient(). + getDynamicProperty(_mor, "config.hardware.device"); + + if (devices != null && devices.size() > 0) { + for (VirtualDevice device : devices) { + if ((DiskControllerType.getType(diskController) == DiskControllerType.lsilogic || DiskControllerType.getType(diskController) == DiskControllerType.scsi) + && device instanceof VirtualLsiLogicController) { + return ((VirtualLsiLogicController)device).getKey(); + } else if ((DiskControllerType.getType(diskController) == DiskControllerType.lsisas1068 || DiskControllerType.getType(diskController) == DiskControllerType.scsi) + && device instanceof VirtualLsiLogicSASController) { + return ((VirtualLsiLogicSASController)device).getKey(); + } else if ((DiskControllerType.getType(diskController) == DiskControllerType.pvscsi || DiskControllerType.getType(diskController) == DiskControllerType.scsi) + && device instanceof ParaVirtualSCSIController) { + return ((ParaVirtualSCSIController)device).getKey(); + } else if ((DiskControllerType.getType(diskController) == DiskControllerType.buslogic || DiskControllerType.getType(diskController) == DiskControllerType.scsi) + && device instanceof VirtualBusLogicController) { + return ((VirtualBusLogicController)device).getKey(); + } + } + } + + assert (false); + throw new Exception(diskController + " Controller Not Found"); + } + + public int getScsiDiskControllerKeyNoException(String diskController) throws Exception { + List devices = (List)_context.getVimClient(). + getDynamicProperty(_mor, "config.hardware.device"); + + if (devices != null && devices.size() > 0) { + for (VirtualDevice device : devices) { + if ((DiskControllerType.getType(diskController) == DiskControllerType.lsilogic || DiskControllerType.getType(diskController) == DiskControllerType.scsi) + && device instanceof VirtualLsiLogicController) { + return ((VirtualLsiLogicController)device).getKey(); + } else if ((DiskControllerType.getType(diskController) == DiskControllerType.lsisas1068 || DiskControllerType.getType(diskController) == DiskControllerType.scsi) + && device instanceof VirtualLsiLogicSASController) { + return ((VirtualLsiLogicSASController)device).getKey(); + } else if ((DiskControllerType.getType(diskController) == DiskControllerType.pvscsi || DiskControllerType.getType(diskController) == DiskControllerType.scsi) + && device instanceof ParaVirtualSCSIController) { + return ((ParaVirtualSCSIController)device).getKey(); + } else if ((DiskControllerType.getType(diskController) == DiskControllerType.buslogic || DiskControllerType.getType(diskController) == DiskControllerType.scsi) + && device instanceof VirtualBusLogicController) { + return ((VirtualBusLogicController)device).getKey(); + } + } + } + return -1; + } + public int getNextScsiDiskDeviceNumber() throws Exception { int scsiControllerKey = getScsiDeviceControllerKey(); int deviceNumber = getNextDeviceNumber(scsiControllerKey); @@ -1918,7 +2170,7 @@ public int getScsiDeviceControllerKey() throws Exception { if (devices != null && devices.size() > 0) { for (VirtualDevice device : devices) { - if (device instanceof VirtualLsiLogicController) { + if (device instanceof VirtualSCSIController) { return device.getKey(); } } @@ -1928,12 +2180,27 @@ public int getScsiDeviceControllerKey() throws Exception { throw new Exception("SCSI Controller Not Found"); } - public int getScsiDeviceControllerKeyNoException() throws Exception { + public int getGenericScsiDeviceControllerKeyNoException() throws Exception { List devices = _context.getVimClient().getDynamicProperty(_mor, "config.hardware.device"); if (devices != null && devices.size() > 0) { for (VirtualDevice device : devices) { - if (device instanceof VirtualLsiLogicController) { + if (device instanceof VirtualSCSIController) { + return device.getKey(); + } + } + } + + return -1; + } + + public int getScsiDeviceControllerKeyNoException() throws Exception { + List devices = (List)_context.getVimClient(). + getDynamicProperty(_mor, "config.hardware.device"); + + if(devices != null && devices.size() > 0) { + for(VirtualDevice device : devices) { + if(device instanceof VirtualLsiLogicController) { return device.getKey(); } } @@ -1963,6 +2230,32 @@ public void ensureScsiDeviceController() throws Exception { } } + public void ensureScsiDeviceControllers(int count, int availableBusNum) throws Exception { + int scsiControllerKey = getScsiDeviceControllerKeyNoException(); + if (scsiControllerKey < 0) { + VirtualMachineConfigSpec vmConfig = new VirtualMachineConfigSpec(); + + int busNum = availableBusNum; + while (busNum < count) { + VirtualLsiLogicController scsiController = new VirtualLsiLogicController(); + scsiController.setSharedBus(VirtualSCSISharing.NO_SHARING); + scsiController.setBusNumber(busNum); + scsiController.setKey(busNum - VmwareHelper.MAX_SCSI_CONTROLLER_COUNT); + VirtualDeviceConfigSpec scsiControllerSpec = new VirtualDeviceConfigSpec(); + scsiControllerSpec.setDevice(scsiController); + scsiControllerSpec.setOperation(VirtualDeviceConfigSpecOperation.ADD); + + vmConfig.getDeviceChange().add(scsiControllerSpec); + busNum++; + } + if (configureVm(vmConfig)) { + throw new Exception("Unable to add Scsi controllers to the VM " + getName()); + } else { + s_logger.info("Successfully added " + count + " SCSI controllers."); + } + } + } + // return pair of VirtualDisk and disk device bus name(ide0:0, etc) public Pair getDiskDevice(String vmdkDatastorePath, boolean matchExactly) throws Exception { List devices = _context.getVimClient().getDynamicProperty(_mor, "config.hardware.device"); @@ -2335,43 +2628,84 @@ public int getIDEDeviceControllerKey() throws Exception { throw new Exception("IDE Controller Not Found"); } - public int getNextIDEDeviceNumber() throws Exception { - int controllerKey = getIDEDeviceControllerKey(); - return getNextDeviceNumber(controllerKey); + public int getIDEControllerKey(int ideUnitNumber) throws Exception { + List devices = (List)_context.getVimClient(). + getDynamicProperty(_mor, "config.hardware.device"); + + int requiredIdeController = ideUnitNumber / VmwareHelper.MAX_IDE_CONTROLLER_COUNT; + + int ideControllerCount = 0; + if(devices != null && devices.size() > 0) { + for(VirtualDevice device : devices) { + if(device instanceof VirtualIDEController) { + if (ideControllerCount == requiredIdeController) { + return ((VirtualIDEController)device).getKey(); + } + ideControllerCount++; + } + } + } + + assert(false); + throw new Exception("IDE Controller Not Found"); } - public VirtualDevice getIsoDevice() throws Exception { - List devices = _context.getVimClient().getDynamicProperty(_mor, "config.hardware.device"); + public int getNumberOfIDEDevices() throws Exception { + int ideDeviceCount = 0; + List devices = (List)_context.getVimClient(). + getDynamicProperty(_mor, "config.hardware.device"); + if (devices != null && devices.size() > 0) { for (VirtualDevice device : devices) { - if (device instanceof VirtualCdrom) { - return device; + if (device instanceof VirtualIDEController) { + ideDeviceCount += ((VirtualIDEController)device).getDevice().size(); } } } - return null; + return ideDeviceCount; } - public int getPCIDeviceControllerKey() throws Exception { - List devices = _context.getVimClient().getDynamicProperty(_mor, "config.hardware.device"); + public int getFreeUnitNumberOnIDEController(int controllerKey) throws Exception { + int freeUnitNumber = 0; + List devices = (List)_context.getVimClient(). + getDynamicProperty(_mor, "config.hardware.device"); + int deviceCount = 0; + int ideDeviceUnitNumber = -1; if (devices != null && devices.size() > 0) { for (VirtualDevice device : devices) { - if (device instanceof VirtualPCIController) { - return ((VirtualPCIController)device).getKey(); + if (device instanceof VirtualDisk && (controllerKey == device.getControllerKey())) { + deviceCount++; + ideDeviceUnitNumber = device.getUnitNumber(); } } } - - assert (false); - throw new Exception("PCI Controller Not Found"); + if (deviceCount == 1) { + if (ideDeviceUnitNumber == 0) { + freeUnitNumber = 1; + } // else freeUnitNumber is already initialized to 0 + } else if (deviceCount == 2) { + throw new Exception("IDE controller with key [" + controllerKey + "] already has 2 device attached. Cannot attach more than the limit of 2."); + } + return freeUnitNumber; } - - public int getNextPCIDeviceNumber() throws Exception { - int controllerKey = getPCIDeviceControllerKey(); + public int getNextIDEDeviceNumber() throws Exception { + int controllerKey = getIDEDeviceControllerKey(); return getNextDeviceNumber(controllerKey); } + public VirtualDevice getIsoDevice() throws Exception { + List devices = _context.getVimClient().getDynamicProperty(_mor, "config.hardware.device"); + if (devices != null && devices.size() > 0) { + for (VirtualDevice device : devices) { + if (device instanceof VirtualCdrom) { + return device; + } + } + } + return null; + } + public int getNextDeviceNumber(int controllerKey) throws Exception { List devices = _context.getVimClient().getDynamicProperty(_mor, "config.hardware.device"); @@ -2626,6 +2960,9 @@ public long getHotAddMemoryIncrementSizeInMb() throws Exception { public long getHotAddMemoryLimitInMb() throws Exception { return (Long)_context.getVimClient().getDynamicProperty(_mor, "config.hotPlugMemoryLimit"); } + public String getGuestId() throws Exception { + return (String)_context.getVimClient().getDynamicProperty(_mor, "config.guestId"); + } public int getCoresPerSocket() throws Exception { // number of cores per socket is 1 in case of ESXi. It's not defined explicitly and the property is support since vSphere API 5.0. @@ -2702,6 +3039,135 @@ public boolean isMemoryHotAddSupported(String guestOsId) throws Exception { } return guestOsSupportsMemoryHotAdd && virtualHardwareSupportsMemoryHotAdd; } + public void ensureLsiLogicSasDeviceControllers(int count, int availableBusNum) throws Exception { + int scsiControllerKey = getLsiLogicSasDeviceControllerKeyNoException(); + if (scsiControllerKey < 0) { + VirtualMachineConfigSpec vmConfig = new VirtualMachineConfigSpec(); + + int busNum = availableBusNum; + while (busNum < count) { + VirtualLsiLogicSASController scsiController = new VirtualLsiLogicSASController(); + scsiController.setSharedBus(VirtualSCSISharing.NO_SHARING); + scsiController.setBusNumber(busNum); + scsiController.setKey(busNum - VmwareHelper.MAX_SCSI_CONTROLLER_COUNT); + VirtualDeviceConfigSpec scsiControllerSpec = new VirtualDeviceConfigSpec(); + scsiControllerSpec.setDevice(scsiController); + scsiControllerSpec.setOperation(VirtualDeviceConfigSpecOperation.ADD); + + vmConfig.getDeviceChange().add(scsiControllerSpec); + busNum++; + } + if (configureVm(vmConfig)) { + throw new Exception("Unable to add Scsi controller of type LsiLogic SAS."); + } + } + + } + + private int getLsiLogicSasDeviceControllerKeyNoException() throws Exception { + List devices = (List)_context.getVimClient(). + getDynamicProperty(_mor, "config.hardware.device"); + + if (devices != null && devices.size() > 0) { + for (VirtualDevice device : devices) { + if (device instanceof VirtualLsiLogicSASController) { + return device.getKey(); + } + } + } + + return -1; + } + + public void ensureBusLogicDeviceControllers(int count, int availableBusNum) throws Exception { + int scsiControllerKey = getBusLogicDeviceControllerKeyNoException(); + if (scsiControllerKey < 0) { + VirtualMachineConfigSpec vmConfig = new VirtualMachineConfigSpec(); + + int busNum = availableBusNum; + while (busNum < count) { + VirtualBusLogicController scsiController = new VirtualBusLogicController(); + + scsiController.setSharedBus(VirtualSCSISharing.NO_SHARING); + scsiController.setBusNumber(busNum); + scsiController.setKey(busNum - VmwareHelper.MAX_SCSI_CONTROLLER_COUNT); + VirtualDeviceConfigSpec scsiControllerSpec = new VirtualDeviceConfigSpec(); + scsiControllerSpec.setDevice(scsiController); + scsiControllerSpec.setOperation(VirtualDeviceConfigSpecOperation.ADD); + + vmConfig.getDeviceChange().add(scsiControllerSpec); + busNum++; + } + + if (configureVm(vmConfig)) { + throw new Exception("Unable to add Scsi BusLogic controllers to the VM " + getName()); + } else { + s_logger.info("Successfully added " + count + " SCSI BusLogic controllers."); + } + } + } + + private int getBusLogicDeviceControllerKeyNoException() throws Exception { + List devices = (List)_context.getVimClient(). + getDynamicProperty(_mor, "config.hardware.device"); + + if (devices != null && devices.size() > 0) { + for (VirtualDevice device : devices) { + if (device instanceof VirtualBusLogicController) { + return device.getKey(); + } + } + } + + return -1; + } + + public Ternary getScsiControllerInfo() throws Exception { + List devices = (List)_context.getVimClient(). + getDynamicProperty(_mor, "config.hardware.device"); + + int scsiControllerCount = 0; + int busNum = -1; + DiskControllerType controllerType = DiskControllerType.lsilogic; + if (devices != null && devices.size() > 0) { + for (VirtualDevice device : devices) { + if (device instanceof VirtualSCSIController) { + scsiControllerCount++; + int deviceBus = ((VirtualSCSIController)device).getBusNumber(); + if (busNum < deviceBus) { + busNum = deviceBus; + } + if (device instanceof VirtualLsiLogicController) { + controllerType = DiskControllerType.lsilogic; + } else if (device instanceof VirtualLsiLogicSASController) { + controllerType = DiskControllerType.lsisas1068; + } else if (device instanceof VirtualBusLogicController) { + controllerType = DiskControllerType.buslogic; + } else if (device instanceof ParaVirtualSCSIController) { + controllerType = DiskControllerType.pvscsi; + } + } + } + } + + return new Ternary(scsiControllerCount, busNum, controllerType); + } + + public int getNumberOfVirtualDisks() throws Exception { + List devices = (List)_context.getVimClient().getDynamicProperty(_mor, "config.hardware.device"); + + s_logger.info("Counting disk devices attached to VM " + getVmName()); + int count = 0; + + if (devices != null && devices.size() > 0) { + for (VirtualDevice device : devices) { + if (device instanceof VirtualDisk) { + count++; + } + } + } + return count; + } public boolean consolidateVmDisks() throws Exception { ManagedObjectReference morTask = _context.getService().consolidateVMDisksTask(_mor); diff --git a/vmware-base/src/com/cloud/hypervisor/vmware/mo/VmdkAdapterType.java b/vmware-base/src/com/cloud/hypervisor/vmware/mo/VmdkAdapterType.java new file mode 100644 index 000000000000..f602c46d7955 --- /dev/null +++ b/vmware-base/src/com/cloud/hypervisor/vmware/mo/VmdkAdapterType.java @@ -0,0 +1,48 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. +package com.cloud.hypervisor.vmware.mo; + +public enum VmdkAdapterType { + ide, + lsilogic, + buslogic, + none; + + public static VmdkAdapterType getAdapterType(DiskControllerType diskControllerType) { + if (diskControllerType == DiskControllerType.ide) { + return VmdkAdapterType.ide; + } else if (diskControllerType == DiskControllerType.buslogic) { + return VmdkAdapterType.buslogic; + } else if (diskControllerType == DiskControllerType.lsilogic || diskControllerType == DiskControllerType.pvscsi || diskControllerType == DiskControllerType.lsisas1068) { + return VmdkAdapterType.lsilogic; + } else { + return VmdkAdapterType.none; + } + } + + public static VmdkAdapterType getType(String vmdkAdapterType) { + if (vmdkAdapterType.equalsIgnoreCase("ide")) { + return VmdkAdapterType.ide; + } else if (vmdkAdapterType.equalsIgnoreCase("lsilogic")) { + return VmdkAdapterType.lsilogic; + } else if (vmdkAdapterType.equalsIgnoreCase("buslogic")) { + return VmdkAdapterType.buslogic; + } else { + return VmdkAdapterType.none; + } + } +} diff --git a/vmware-base/src/com/cloud/hypervisor/vmware/mo/VmdkFileDescriptor.java b/vmware-base/src/com/cloud/hypervisor/vmware/mo/VmdkFileDescriptor.java index 8aecfdd01beb..932c251b0464 100755 --- a/vmware-base/src/com/cloud/hypervisor/vmware/mo/VmdkFileDescriptor.java +++ b/vmware-base/src/com/cloud/hypervisor/vmware/mo/VmdkFileDescriptor.java @@ -29,6 +29,9 @@ public class VmdkFileDescriptor { private static final Logger s_logger = Logger.getLogger(VmdkFileDescriptor.class); + private static final String VMDK_PROPERTY_CREATE_TYPE = "createType"; + private static final String VMDK_CREATE_TYPE_VMFSSPARSE = "vmfsSparse"; + private static final String VMDK_PROPERTY_ADAPTER_TYPE = "ddb.adapterType"; private Properties _properties = new Properties(); private String _baseFileName; @@ -84,6 +87,73 @@ public String getParentFileName() { return _properties.getProperty("parentFileNameHint"); } + public boolean isVmfsSparseFile() { + String vmdkCreateType = _properties.getProperty(VMDK_PROPERTY_CREATE_TYPE); + if (vmdkCreateType.equalsIgnoreCase(VMDK_CREATE_TYPE_VMFSSPARSE)) { + return true; + } + return false; + } + + public String getAdapterType() { + return _properties.getProperty(VMDK_PROPERTY_ADAPTER_TYPE); + } + + + public static byte[] changeVmdkAdapterType(byte[] vmdkContent, String newAdapterType) throws IOException { + assert (vmdkContent != null); + + BufferedReader in = null; + BufferedWriter out = null; + ByteArrayOutputStream bos = new ByteArrayOutputStream(); + + try { + in = new BufferedReader(new InputStreamReader(new ByteArrayInputStream(vmdkContent))); + out = new BufferedWriter(new OutputStreamWriter(bos)); + String line; + while ((line = in.readLine()) != null) { + // ignore empty and comment lines + line = line.trim(); + if (line.isEmpty()) { + out.newLine(); + continue; + } + if (line.charAt(0) == '#') { + out.write(line); + out.newLine(); + continue; + } + + String[] tokens = line.split("="); + if (tokens.length == 2) { + String name = tokens[0].trim(); + String value = tokens[1].trim(); + if (value.charAt(0) == '\"') + value = value.substring(1, value.length() - 1); + + if (newAdapterType != null && name.equals(VMDK_PROPERTY_ADAPTER_TYPE)) { + out.write(name + "=\"" + newAdapterType + "\""); + out.newLine(); + } else { + out.write(line); + out.newLine(); + } + } else { + out.write(line); + out.newLine(); + } + } + } finally { + if (in != null) + in.close(); + if (out != null) + out.close(); + } + + return bos.toByteArray(); + + } + public static byte[] changeVmdkContentBaseInfo(byte[] vmdkContent, String baseFileName, String parentFileName) throws IOException { assert (vmdkContent != null); diff --git a/vmware-base/src/com/cloud/hypervisor/vmware/mo/VmwareHypervisorHost.java b/vmware-base/src/com/cloud/hypervisor/vmware/mo/VmwareHypervisorHost.java index 9727e1733165..fdfc254278b2 100755 --- a/vmware-base/src/com/cloud/hypervisor/vmware/mo/VmwareHypervisorHost.java +++ b/vmware-base/src/com/cloud/hypervisor/vmware/mo/VmwareHypervisorHost.java @@ -23,6 +23,7 @@ import com.vmware.vim25.VirtualMachineConfigSpec; import com.cloud.hypervisor.vmware.util.VmwareContext; +import com.cloud.utils.Pair; /** * Interface to consolidate ESX(i) hosts and HA/FT clusters into a common interface used by CloudStack Hypervisor resources @@ -53,7 +54,8 @@ public interface VmwareHypervisorHost { boolean createVm(VirtualMachineConfigSpec vmSpec) throws Exception; boolean createBlankVm(String vmName, String vmInternalCSName, int cpuCount, int cpuSpeedMHz, int cpuReservedMHz, boolean limitCpuUse, int memoryMB, - int memoryReserveMB, String guestOsIdentifier, ManagedObjectReference morDs, boolean snapshotDirToParent) throws Exception; + int memoryReserveMB, String guestOsIdentifier, ManagedObjectReference morDs, boolean snapshotDirToParent, + Pair controllerInfo, Boolean systemVm) throws Exception; void importVmFromOVF(String ovfFilePath, String vmName, DatastoreMO dsMo, String diskOption) throws Exception; @@ -81,4 +83,5 @@ boolean createBlankVm(String vmName, String vmInternalCSName, int cpuCount, int ComputeResourceSummary getHyperHostHardwareSummary() throws Exception; LicenseAssignmentManagerMO getLicenseAssignmentManager() throws Exception; + String getRecommendedDiskController(String guestOsId) throws Exception; } diff --git a/vmware-base/src/com/cloud/hypervisor/vmware/util/VmwareHelper.java b/vmware-base/src/com/cloud/hypervisor/vmware/util/VmwareHelper.java index b2b10907d687..837981393e10 100644 --- a/vmware-base/src/com/cloud/hypervisor/vmware/util/VmwareHelper.java +++ b/vmware-base/src/com/cloud/hypervisor/vmware/util/VmwareHelper.java @@ -34,6 +34,7 @@ import com.vmware.vim25.DistributedVirtualSwitchPortConnection; import com.vmware.vim25.DynamicProperty; +import com.vmware.vim25.GuestOsDescriptor; import com.vmware.vim25.ManagedObjectReference; import com.vmware.vim25.MethodFault; import com.vmware.vim25.ObjectContent; @@ -62,6 +63,7 @@ import com.vmware.vim25.VirtualVmxnet2; import com.vmware.vim25.VirtualVmxnet3; +import com.cloud.hypervisor.vmware.mo.DiskControllerType; import com.cloud.hypervisor.vmware.mo.HostMO; import com.cloud.hypervisor.vmware.mo.LicenseAssignmentManagerMO; import com.cloud.hypervisor.vmware.mo.VirtualEthernetCardType; @@ -75,6 +77,11 @@ public class VmwareHelper { @SuppressWarnings("unused") private static final Logger s_logger = Logger.getLogger(VmwareHelper.class); + public static final int MAX_SCSI_CONTROLLER_COUNT = 4; + public static final int MAX_IDE_CONTROLLER_COUNT = 2; + public static final int MAX_ALLOWED_DEVICES_IDE_CONTROLLER = 2; + public static final int MAX_ALLOWED_DEVICES_SCSI_CONTROLLER = 15; + public static boolean isReservedScsiDeviceNumber(int deviceNumber) { return deviceNumber == 7; } @@ -705,6 +712,19 @@ public static String getVCenterSafeUuid() { return UUID.randomUUID().toString().replaceAll("-", ""); } + public static String getRecommendedDiskControllerFromDescriptor(GuestOsDescriptor guestOsDescriptor) throws Exception { + String recommendedController; + + recommendedController = guestOsDescriptor.getRecommendedDiskController(); + + // By-pass auto detected PVSCSI controller to use LsiLogic Parallel instead + if (DiskControllerType.getType(recommendedController) == DiskControllerType.pvscsi) { + recommendedController = DiskControllerType.lsilogic.toString(); + } + + return recommendedController; + } + public static String trimSnapshotDeltaPostfix(String name) { String[] tokens = name.split("-"); if (tokens.length > 1 && tokens[tokens.length - 1].matches("[0-9]{6,}")) { @@ -716,4 +736,8 @@ public static String trimSnapshotDeltaPostfix(String name) { return name; } + public static boolean isControllerOsRecommended(String dataDiskController) { + return DiskControllerType.getType(dataDiskController) == DiskControllerType.osdefault; + } + } From c7d67628b3ff86802a1ac3b58e0bc05d54afd543 Mon Sep 17 00:00:00 2001 From: Rohit Yadav Date: Fri, 27 Nov 2015 18:30:00 +0530 Subject: [PATCH 2/2] CLOUDSTACK-4787: Allow users to select disk controller for VM/template - Adds new controller types in the UI, for selecting root disk controller while registering templates - Fixes bug to not override disk controller type if provided in the details (either vm details or from template details) Signed-off-by: Rohit Yadav --- .../src/com/cloud/configuration/Config.java | 2 +- .../src/com/cloud/vm/UserVmManagerImpl.java | 15 +++++++++----- ui/scripts/templates.js | 20 +++++++++++++++++++ 3 files changed, 31 insertions(+), 6 deletions(-) diff --git a/server/src/com/cloud/configuration/Config.java b/server/src/com/cloud/configuration/Config.java index 41a187237c1c..3a24698f5287 100755 --- a/server/src/com/cloud/configuration/Config.java +++ b/server/src/com/cloud/configuration/Config.java @@ -1195,7 +1195,7 @@ public enum Config { String.class, "vmware.root.disk.controller", "ide", - "Specify the default disk controller for root volumes, valid values are scsi, ide", + "Specify the default disk controller for root volumes, valid values are scsi, ide, osdefault. Please check documentation for more details on each of these values.", null), VmwareSystemVmNicDeviceType( "Advanced", diff --git a/server/src/com/cloud/vm/UserVmManagerImpl.java b/server/src/com/cloud/vm/UserVmManagerImpl.java index 6f49bb2b5c53..35aafb8164a4 100755 --- a/server/src/com/cloud/vm/UserVmManagerImpl.java +++ b/server/src/com/cloud/vm/UserVmManagerImpl.java @@ -3117,11 +3117,16 @@ public UserVmVO doInTransaction(TransactionStatus status) throws InsufficientCap s_logger.info("guestOS is OSX : overwrite root disk controller to scsi, use smc and efi"); } else { String controllerSetting = _configDao.getValue("vmware.root.disk.controller"); - vm.setDetail(VmDetailConstants.ROOK_DISK_CONTROLLER, controllerSetting); - if (controllerSetting.equalsIgnoreCase("scsi")) { - vm.setDetail(VmDetailConstants.DATA_DISK_CONTROLLER, "scsi"); - } else { - vm.setDetail(VmDetailConstants.DATA_DISK_CONTROLLER, "osdefault"); + // Don't override if VM already has root/data disk controller detail + if (vm.getDetail(VmDetailConstants.ROOK_DISK_CONTROLLER) == null) { + vm.setDetail(VmDetailConstants.ROOK_DISK_CONTROLLER, controllerSetting); + } + if (vm.getDetail(VmDetailConstants.DATA_DISK_CONTROLLER) == null) { + if (controllerSetting.equalsIgnoreCase("scsi")) { + vm.setDetail(VmDetailConstants.DATA_DISK_CONTROLLER, "scsi"); + } else { + vm.setDetail(VmDetailConstants.DATA_DISK_CONTROLLER, "osdefault"); + } } } } diff --git a/ui/scripts/templates.js b/ui/scripts/templates.js index 6d09b5104274..bff971c56c8f 100644 --- a/ui/scripts/templates.js +++ b/ui/scripts/templates.js @@ -281,6 +281,26 @@ id: "ide", description: "ide" }); + items.push({ + id: "osdefault", + description: "osdefault" + }); + items.push({ + id: "pvscsi", + description: "pvscsi" + }); + items.push({ + id: "lsilogic", + description: "lsilogic" + }); + items.push({ + id: "lsisas1068", + description: "lsilogicsas" + }); + items.push({ + id: "buslogic", + description: "buslogic" + }); args.response.success({ data: items });