diff --git a/api/src/com/cloud/vm/VmDetailConstants.java b/api/src/com/cloud/vm/VmDetailConstants.java index bd6f0e2f646..d06ad67197e 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 34aaac92778..d15a4e42da3 100644 --- a/core/src/org/apache/cloudstack/storage/command/AttachCommand.java +++ b/core/src/org/apache/cloudstack/storage/command/AttachCommand.java @@ -19,18 +19,34 @@ package org.apache.cloudstack.storage.command; +import java.util.Map; + import com.cloud.agent.api.to.DiskTO; public final class AttachCommand extends StorageSubSystemCommand { private DiskTO disk; private String vmName; - private boolean inSeq; + private boolean inSeq = false; + private Map controllerInfo; public AttachCommand(final DiskTO disk, final 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 cc3880b2c0e..fd96bc8357c 100644 --- a/plugins/hypervisors/vmware/src/com/cloud/hypervisor/guru/VMwareGuru.java +++ b/plugins/hypervisors/vmware/src/com/cloud/hypervisor/guru/VMwareGuru.java @@ -67,6 +67,7 @@ import com.cloud.hypervisor.Hypervisor.HypervisorType; 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; @@ -205,6 +206,12 @@ public class VMwareGuru extends HypervisorGuruBase implements HypervisorGuru, Co 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 35e275e004e..72ee2184e39 100644 --- 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 b65916b63eb..575801fa603 100644 --- a/plugins/hypervisors/vmware/src/com/cloud/hypervisor/vmware/manager/VmwareManagerImpl.java +++ b/plugins/hypervisors/vmware/src/com/cloud/hypervisor/vmware/manager/VmwareManagerImpl.java @@ -186,6 +186,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()); @@ -484,6 +486,7 @@ public class VmwareManagerImpl extends ManagerBase implements VmwareManager, Vmw 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); } @@ -936,6 +939,11 @@ public class VmwareManagerImpl extends ManagerBase implements VmwareManager, Vmw 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 c5f7ceec3ec..fdbc244997d 100644 --- 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.VirtualDeviceBackingInfo; 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; @@ -596,7 +597,18 @@ public class VmwareResource implements StoragePoolResource, ServerResource, Vmwa 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(); @@ -1327,6 +1339,63 @@ public class VmwareResource implements StoragePoolResource, ServerResource, Vmwa 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)); @@ -1342,7 +1411,29 @@ public class VmwareResource implements StoragePoolResource, ServerResource, Vmwa 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; @@ -1381,6 +1472,9 @@ public class VmwareResource implements StoragePoolResource, ServerResource, Vmwa 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"); @@ -1394,7 +1488,11 @@ public class VmwareResource implements StoragePoolResource, ServerResource, Vmwa 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); @@ -1416,7 +1514,13 @@ public class VmwareResource implements StoragePoolResource, ServerResource, Vmwa 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); @@ -1460,7 +1564,7 @@ public class VmwareResource implements StoragePoolResource, ServerResource, Vmwa 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); } } @@ -1522,7 +1626,7 @@ public class VmwareResource implements StoragePoolResource, ServerResource, Vmwa int scsiUnitNumber = 0; int nicUnitNumber = 0; int ideControllerKey = vmMo.getIDEDeviceControllerKey(); - int scsiControllerKey = vmMo.getScsiDeviceControllerKey(); + int scsiControllerKey = vmMo.getGenericScsiDeviceControllerKeyNoException(); int controllerKey; // @@ -1621,7 +1725,31 @@ public class VmwareResource implements StoragePoolResource, ServerResource, Vmwa 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(); @@ -1650,7 +1778,7 @@ public class VmwareResource implements StoragePoolResource, ServerResource, Vmwa 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); @@ -1662,7 +1790,7 @@ public class VmwareResource implements StoragePoolResource, ServerResource, Vmwa } else { if (controllerKey == scsiControllerKey && VmwareHelper.isReservedScsiDeviceNumber(scsiUnitNumber)) scsiUnitNumber++; - if (controllerKey == ideControllerKey) + if (controllerKey == vmMo.getIDEControllerKey(ideUnitNumber)) ideUnitNumber++; else scsiUnitNumber++; @@ -2218,6 +2346,36 @@ public class VmwareResource implements StoragePoolResource, ServerResource, Vmwa 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(); @@ -3158,8 +3316,13 @@ public class VmwareResource implements StoragePoolResource, ServerResource, Vmwa 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); @@ -3326,7 +3489,13 @@ public class VmwareResource implements StoragePoolResource, ServerResource, Vmwa 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); @@ -3385,12 +3554,12 @@ public class VmwareResource implements StoragePoolResource, ServerResource, Vmwa } } - private int getVirtualDiskInfo(VirtualMachineMO vmMo, String srcDiskName) throws Exception { + private Pair getVirtualDiskInfo(VirtualMachineMO vmMo, String srcDiskName) throws Exception { Pair deviceInfo = vmMo.getDiskDevice(srcDiskName); 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 { @@ -4993,8 +5162,10 @@ public class VmwareResource implements StoragePoolResource, ServerResource, Vmwa 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) @@ -5218,4 +5389,14 @@ public class VmwareResource implements StoragePoolResource, ServerResource, Vmwa 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 cc7071523bf..fa2f369afaf 100644 --- a/plugins/hypervisors/vmware/src/com/cloud/storage/resource/VmwareStorageProcessor.java +++ b/plugins/hypervisors/vmware/src/com/cloud/storage/resource/VmwareStorageProcessor.java @@ -75,12 +75,14 @@ import com.cloud.agent.api.to.DataTO; 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; @@ -104,6 +106,7 @@ import com.cloud.utils.Ternary; 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); @@ -1292,10 +1295,10 @@ public class VmwareStorageProcessor implements StorageProcessor { 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 { @@ -1360,7 +1363,24 @@ public class VmwareStorageProcessor implements StorageProcessor { 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); @@ -1504,7 +1524,7 @@ public class VmwareStorageProcessor implements StorageProcessor { @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 @@ -2277,4 +2297,28 @@ public class VmwareStorageProcessor implements StorageProcessor { 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/configuration/Config.java b/server/src/com/cloud/configuration/Config.java index bf839da7e4d..600ecc4ac53 100644 --- a/server/src/com/cloud/configuration/Config.java +++ b/server/src/com/cloud/configuration/Config.java @@ -1202,7 +1202,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/storage/VolumeApiServiceImpl.java b/server/src/com/cloud/storage/VolumeApiServiceImpl.java index ba2b7bc1052..afa5cc40089 100644 --- a/server/src/com/cloud/storage/VolumeApiServiceImpl.java +++ b/server/src/com/cloud/storage/VolumeApiServiceImpl.java @@ -150,6 +150,7 @@ import com.cloud.vm.UserVmVO; 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; @@ -2453,6 +2454,11 @@ public class VolumeApiServiceImpl extends ManagerBase implements VolumeApiServic 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 6fd10ca8b28..c586e14a819 100644 --- a/server/src/com/cloud/vm/UserVmManagerImpl.java +++ b/server/src/com/cloud/vm/UserVmManagerImpl.java @@ -3510,8 +3510,22 @@ public class UserVmManagerImpl extends ManagerBase implements UserVmManager, Vir 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"); + // 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 ac739c631e4..c8c5b89df25 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 }); diff --git a/ui/scripts/vpc.js b/ui/scripts/vpc.js index 41d10edc73a..4b10d8baeaf 100644 --- a/ui/scripts/vpc.js +++ b/ui/scripts/vpc.js @@ -638,7 +638,7 @@ args.response.success({ _custom: { jobId: jobID, - getUpdateIdtem: function() { + getUpdatedItem: function() { $(window).trigger('cloudStack.fullRefresh'); } }, @@ -1225,12 +1225,19 @@ url: createURL('createNetworkACLList&vpcid=' + args.context.vpc[0].id), data: data, success: function(json) { - var items = json.createnetworkacllistresponse; args.response.success({ - data: items + _custom: { + jobId: json.createnetworkacllistresponse.jobid, + getUpdatedItem: function(json) { + return json.queryasyncjobresultresponse.jobresult.networkacllist; + } + } }); } }); + }, + notification: { + poll: pollAsyncJobResult } } }, @@ -1255,7 +1262,10 @@ var jid = json.deletenetworkacllistresponse.jobid; args.response.success({ _custom: { - jobId: jid + jobId: jid, + getUpdatedItem: function() { + $(window).trigger('cloudStack.fullRefresh'); + } } }); }, @@ -1292,9 +1302,8 @@ data: items, actionFilter: function(args) { var allowedActions = []; - if (isAdmin() && items.vpcid) { + if (items.vpcid != null) { allowedActions.push("remove"); - } return allowedActions; } 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 1b72b73bd88..b5f0500d391 100644 --- 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.ComputeResourceSummary; 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.OptionValue; 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 class ClusterMO extends BaseMO implements VmwareHypervisorHost { @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 class ClusterMO extends BaseMO implements VmwareHypervisorHost { 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 class ClusterMO extends BaseMO implements VmwareHypervisorHost { // 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 def584df1fd..3e6b8bb53e0 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 1cd9d729133..d8fa7f355db 100644 --- a/vmware-base/src/com/cloud/hypervisor/vmware/mo/HostMO.java +++ b/vmware-base/src/com/cloud/hypervisor/vmware/mo/HostMO.java @@ -751,16 +751,17 @@ public class HostMO extends BaseMO implements VmwareHypervisorHost { @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"); @@ -1069,6 +1070,16 @@ public class HostMO extends BaseMO implements VmwareHypervisorHost { } } + @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 4b11593ea4c..fc27d1f8e43 100644 --- a/vmware-base/src/com/cloud/hypervisor/vmware/mo/HypervisorHostHelper.java +++ b/vmware-base/src/com/cloud/hypervisor/vmware/mo/HypervisorHostHelper.java @@ -69,18 +69,24 @@ import com.vmware.vim25.ObjectContent; 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; @@ -1242,11 +1248,13 @@ public class HypervisorHostHelper { } 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); @@ -1255,14 +1263,33 @@ public class HypervisorHostHelper { 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); @@ -1277,7 +1304,6 @@ public class HypervisorHostHelper { 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 @@ -1307,6 +1333,34 @@ public class HypervisorHostHelper { 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 @@ -1516,4 +1570,42 @@ public class HypervisorHostHelper { } } } + + 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 00000000000..8b9a949bbef --- /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 c3a04b688da..eeb0b4da278 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.OptionValue; 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.VirtualEthernetCardDistributedVirtualPortBackingInfo; 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.VirtualMachineRelocateSpecDiskLocator; 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; @@ -1091,6 +1094,130 @@ public class VirtualMachineMO extends BaseMO { 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()) @@ -1760,20 +1887,17 @@ public class VirtualMachineMO extends BaseMO { } } - 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; } @@ -1912,6 +2036,134 @@ public class VirtualMachineMO extends BaseMO { } } + 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); @@ -1924,7 +2176,7 @@ public class VirtualMachineMO extends BaseMO { if (devices != null && devices.size() > 0) { for (VirtualDevice device : devices) { - if (device instanceof VirtualLsiLogicController) { + if (device instanceof VirtualSCSIController) { return device.getKey(); } } @@ -1949,11 +2201,12 @@ public class VirtualMachineMO extends BaseMO { } public int getScsiDeviceControllerKeyNoException() throws Exception { - List devices = _context.getVimClient().getDynamicProperty(_mor, "config.hardware.device"); + List devices = (List)_context.getVimClient(). + getDynamicProperty(_mor, "config.hardware.device"); - if (devices != null && devices.size() > 0) { - for (VirtualDevice device : devices) { - if (device instanceof VirtualLsiLogicController) { + if(devices != null && devices.size() > 0) { + for(VirtualDevice device : devices) { + if(device instanceof VirtualLsiLogicController) { return device.getKey(); } } @@ -1983,6 +2236,32 @@ public class VirtualMachineMO extends BaseMO { } } + 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) throws Exception { List devices = _context.getVimClient().getDynamicProperty(_mor, "config.hardware.device"); @@ -2361,6 +2640,67 @@ public class VirtualMachineMO extends BaseMO { throw new Exception("IDE Controller Not Found"); } + 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 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 VirtualIDEController) { + ideDeviceCount += ((VirtualIDEController)device).getDevice().size(); + } + } + } + return ideDeviceCount; + } + + 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 VirtualDisk && (controllerKey == device.getControllerKey())) { + deviceCount++; + ideDeviceUnitNumber = device.getUnitNumber(); + } + } + } + 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 getNextIDEDeviceNumber() throws Exception { int controllerKey = getIDEDeviceControllerKey(); return getNextDeviceNumber(controllerKey); @@ -2378,26 +2718,6 @@ public class VirtualMachineMO extends BaseMO { return null; } - public int getPCIDeviceControllerKey() throws Exception { - List devices = _context.getVimClient().getDynamicProperty(_mor, "config.hardware.device"); - - if (devices != null && devices.size() > 0) { - for (VirtualDevice device : devices) { - if (device instanceof VirtualPCIController) { - return ((VirtualPCIController)device).getKey(); - } - } - } - - assert (false); - throw new Exception("PCI Controller Not Found"); - } - - public int getNextPCIDeviceNumber() throws Exception { - int controllerKey = getPCIDeviceControllerKey(); - return getNextDeviceNumber(controllerKey); - } - public int getNextDeviceNumber(int controllerKey) throws Exception { List devices = _context.getVimClient().getDynamicProperty(_mor, "config.hardware.device"); @@ -2653,6 +2973,9 @@ public class VirtualMachineMO extends BaseMO { 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. @@ -2729,6 +3052,135 @@ public class VirtualMachineMO extends BaseMO { } 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 00000000000..f602c46d795 --- /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 fdce93c8122..556efd7e7fa 100644 --- 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 @@ import org.apache.log4j.Logger; 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 class VmdkFileDescriptor { 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 9727e173316..fdfc254278b 100644 --- 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.ObjectContent; 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 @@ public interface VmwareHypervisorHost { 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 3d62fdcd16b..84e9dad0538 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 org.apache.log4j.Logger; 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,8 @@ import com.vmware.vim25.VirtualMachineSnapshotTree; import com.vmware.vim25.VirtualPCNet32; 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 +78,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; } @@ -751,6 +759,19 @@ public class VmwareHelper { 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,}")) { @@ -762,4 +783,8 @@ public class VmwareHelper { return name; } + public static boolean isControllerOsRecommended(String dataDiskController) { + return DiskControllerType.getType(dataDiskController) == DiskControllerType.osdefault; + } + }