diff --git a/api/src/main/java/org/apache/cloudstack/storage/DiskControllerMapping.java b/api/src/main/java/org/apache/cloudstack/storage/DiskControllerMapping.java new file mode 100644 index 000000000000..0c1ab70ebd5d --- /dev/null +++ b/api/src/main/java/org/apache/cloudstack/storage/DiskControllerMapping.java @@ -0,0 +1,45 @@ +// 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 org.apache.cloudstack.storage; + +import com.cloud.hypervisor.Hypervisor.HypervisorType; +import org.apache.cloudstack.api.Identity; +import org.apache.cloudstack.api.InternalIdentity; + +import java.util.Date; + +public interface DiskControllerMapping extends InternalIdentity, Identity { + String getName(); + + String getControllerReference(); + + String getBusName(); + + HypervisorType getHypervisor(); + + Integer getMaxDeviceCount(); + + Integer getMaxControllerCount(); + + String getVmdkAdapterType(); + + String getMinHardwareVersion(); + + Date getRemoved(); + + Date getCreated(); +} diff --git a/core/src/main/java/com/cloud/agent/api/SecStorageVMSetupCommand.java b/core/src/main/java/com/cloud/agent/api/SecStorageVMSetupCommand.java index 400fc5aacdda..449cc7dbed62 100644 --- a/core/src/main/java/com/cloud/agent/api/SecStorageVMSetupCommand.java +++ b/core/src/main/java/com/cloud/agent/api/SecStorageVMSetupCommand.java @@ -20,6 +20,9 @@ package com.cloud.agent.api; import com.cloud.agent.api.LogLevel.Log4jLevel; +import org.apache.cloudstack.storage.DiskControllerMappingVO; + +import java.util.List; public class SecStorageVMSetupCommand extends Command { String[] allowedInternalSites = new String[0]; @@ -27,6 +30,8 @@ public class SecStorageVMSetupCommand extends Command { @LogLevel(Log4jLevel.Off) String copyPassword; + private List supportedDiskControllers; + public SecStorageVMSetupCommand() { super(); } @@ -60,4 +65,11 @@ public void setCopyPassword(String copyPassword) { this.copyPassword = copyPassword; } + public List getSupportedDiskControllers() { + return supportedDiskControllers; + } + + public void setSupportedDiskControllers(List supportedDiskControllers) { + this.supportedDiskControllers = supportedDiskControllers; + } } diff --git a/engine/schema/src/main/java/org/apache/cloudstack/storage/DiskControllerMappingVO.java b/engine/schema/src/main/java/org/apache/cloudstack/storage/DiskControllerMappingVO.java new file mode 100644 index 000000000000..3fc7c4c75575 --- /dev/null +++ b/engine/schema/src/main/java/org/apache/cloudstack/storage/DiskControllerMappingVO.java @@ -0,0 +1,205 @@ +// 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 org.apache.cloudstack.storage; + +import com.cloud.hypervisor.Hypervisor.HypervisorType; +import com.cloud.utils.db.GenericDao; +import org.apache.cloudstack.util.HypervisorTypeConverter; +import org.apache.cloudstack.utils.reflectiontostringbuilderutils.ReflectionToStringBuilderUtils; + +import javax.persistence.Column; +import javax.persistence.Convert; +import javax.persistence.Entity; +import javax.persistence.GeneratedValue; +import javax.persistence.GenerationType; +import javax.persistence.Id; +import javax.persistence.Table; +import javax.persistence.Temporal; +import javax.persistence.TemporalType; +import java.util.Date; +import java.util.UUID; + +@Entity +@Table(name = "disk_controller_mapping") +public class DiskControllerMappingVO implements DiskControllerMapping { + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + @Column(name = "id", nullable = false) + private Long id; + + @Column(name = "uuid", nullable = false) + private String uuid = UUID.randomUUID().toString(); + + @Column(name = "name", nullable = false) + private String name; + + @Column(name = "controller_reference", nullable = false) + private String controllerReference; + + @Column(name = "bus_name", nullable = false) + private String busName; + + @Column(name = "hypervisor", nullable = false) + @Convert(converter = HypervisorTypeConverter.class) + private HypervisorType hypervisor; + + @Column(name = "max_device_count") + private Integer maxDeviceCount = null; + + @Column(name = "max_controller_count") + private Integer maxControllerCount = null; + + @Column(name = "vmdk_adapter_type") + private String vmdkAdapterType = null; + + @Column(name = "min_hardware_version") + private String minHardwareVersion = null; + + @Column(name = GenericDao.CREATED_COLUMN, nullable = false) + @Temporal(value = TemporalType.TIMESTAMP) + private Date created; + + @Column(name = GenericDao.REMOVED_COLUMN) + @Temporal(value = TemporalType.TIMESTAMP) + private Date removed = null; + + public DiskControllerMappingVO() { + } + + @Override + public String getName() { + return name; + } + + @Override + public String getControllerReference() { + return controllerReference; + } + + @Override + public String getBusName() { + return busName; + } + + @Override + public HypervisorType getHypervisor() { + return hypervisor; + } + + @Override + public Integer getMaxDeviceCount() { + return maxDeviceCount; + } + + @Override + public Integer getMaxControllerCount() { + return maxControllerCount; + } + + @Override + public String getVmdkAdapterType() { + return vmdkAdapterType; + } + + @Override + public String getMinHardwareVersion() { + return minHardwareVersion; + } + + @Override + public Date getRemoved() { + return removed; + } + + @Override + public Date getCreated() { + return created; + } + + @Override + public String getUuid() { + return uuid; + } + + @Override + public long getId() { + return id; + } + + public void setId(Long id) { + this.id = id; + } + + public void setUuid(String uuid) { + this.uuid = uuid; + } + + public void setName(String name) { + this.name = name; + } + + public void setControllerReference(String controllerReference) { + this.controllerReference = controllerReference; + } + + public void setBusName(String busName) { + this.busName = busName; + } + + public void setHypervisor(HypervisorType hypervisor) { + this.hypervisor = hypervisor; + } + + public void setMaxDeviceCount(Integer maxDeviceCount) { + this.maxDeviceCount = maxDeviceCount; + } + + public void setMaxControllerCount(Integer maxControllerCount) { + this.maxControllerCount = maxControllerCount; + } + + public void setVmdkAdapterType(String vmdkAdapterType) { + this.vmdkAdapterType = vmdkAdapterType; + } + + public void setMinHardwareVersion(String minHardwareVersion) { + this.minHardwareVersion = minHardwareVersion; + } + + public void setCreated(Date created) { + this.created = created; + } + + public void setRemoved(Date removed) { + this.removed = removed; + } + + @Override + public boolean equals(Object obj) { + if (!(obj instanceof DiskControllerMappingVO)) { + return false; + } + DiskControllerMappingVO that = (DiskControllerMappingVO) obj; + return controllerReference.equals(that.getControllerReference()); + } + + @Override + public String toString() { + return ReflectionToStringBuilderUtils.reflectOnlySelectedFields(this, "name", "controllerReference", + "busName", "hypervisor", "maxDeviceCount", "maxControllerCount", "vmdkAdapterType", "minHardwareVersion"); + } +} diff --git a/engine/schema/src/main/java/org/apache/cloudstack/storage/dao/DiskControllerMappingDao.java b/engine/schema/src/main/java/org/apache/cloudstack/storage/dao/DiskControllerMappingDao.java new file mode 100644 index 000000000000..b76b6b56900d --- /dev/null +++ b/engine/schema/src/main/java/org/apache/cloudstack/storage/dao/DiskControllerMappingDao.java @@ -0,0 +1,29 @@ +// 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 org.apache.cloudstack.storage.dao; + +import com.cloud.hypervisor.Hypervisor.HypervisorType; +import org.apache.cloudstack.storage.DiskControllerMappingVO; +import com.cloud.utils.db.GenericDao; + +import java.util.List; + +public interface DiskControllerMappingDao extends GenericDao { + DiskControllerMappingVO findDiskControllerMapping(String name, String classReference, HypervisorType hypervisor); + + List listForHypervisor(HypervisorType hypervisor); +} diff --git a/engine/schema/src/main/java/org/apache/cloudstack/storage/dao/DiskControllerMappingDaoImpl.java b/engine/schema/src/main/java/org/apache/cloudstack/storage/dao/DiskControllerMappingDaoImpl.java new file mode 100644 index 000000000000..d291b169745b --- /dev/null +++ b/engine/schema/src/main/java/org/apache/cloudstack/storage/dao/DiskControllerMappingDaoImpl.java @@ -0,0 +1,57 @@ +// 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 org.apache.cloudstack.storage.dao; + +import com.cloud.hypervisor.Hypervisor.HypervisorType; +import com.cloud.utils.db.GenericDaoBase; +import com.cloud.utils.db.SearchBuilder; +import com.cloud.utils.db.SearchCriteria; +import org.apache.cloudstack.storage.DiskControllerMappingVO; +import org.springframework.stereotype.Component; + +import javax.annotation.PostConstruct; +import java.util.List; + +@Component +public class DiskControllerMappingDaoImpl extends GenericDaoBase implements DiskControllerMappingDao { + private SearchBuilder diskControllerMappingSearch; + + @PostConstruct + public void init() { + diskControllerMappingSearch = createSearchBuilder(); + diskControllerMappingSearch.and("name", diskControllerMappingSearch.entity().getName(), SearchCriteria.Op.EQ); + diskControllerMappingSearch.and("controllerReference", diskControllerMappingSearch.entity().getControllerReference(), SearchCriteria.Op.EQ); + diskControllerMappingSearch.and("hypervisor", diskControllerMappingSearch.entity().getHypervisor(), SearchCriteria.Op.EQ); + diskControllerMappingSearch.done(); + } + + @Override + public DiskControllerMappingVO findDiskControllerMapping(String name, String controllerReference, HypervisorType hypervisor) { + SearchCriteria sc = diskControllerMappingSearch.create(); + sc.setParametersIfNotNull("name", name); + sc.setParametersIfNotNull("controllerReference", controllerReference); + sc.setParameters("hypervisor", hypervisor); + return findOneBy(sc); + } + + @Override + public List listForHypervisor(HypervisorType hypervisor) { + SearchCriteria sc = diskControllerMappingSearch.create(); + sc.setParameters("hypervisor", hypervisor); + return listBy(sc); + } +} diff --git a/engine/schema/src/main/resources/META-INF/cloudstack/core/spring-engine-schema-core-daos-context.xml b/engine/schema/src/main/resources/META-INF/cloudstack/core/spring-engine-schema-core-daos-context.xml index 4f22234d7bf4..838143883500 100644 --- a/engine/schema/src/main/resources/META-INF/cloudstack/core/spring-engine-schema-core-daos-context.xml +++ b/engine/schema/src/main/resources/META-INF/cloudstack/core/spring-engine-schema-core-daos-context.xml @@ -67,6 +67,7 @@ + diff --git a/engine/schema/src/main/resources/META-INF/db/procedures/cloud.add_disk_controller_mapping.sql b/engine/schema/src/main/resources/META-INF/db/procedures/cloud.add_disk_controller_mapping.sql new file mode 100644 index 000000000000..cdc617aa9467 --- /dev/null +++ b/engine/schema/src/main/resources/META-INF/db/procedures/cloud.add_disk_controller_mapping.sql @@ -0,0 +1,39 @@ +-- 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. + +DROP PROCEDURE IF EXISTS `cloud`.`ADD_DISK_CONTROLLER_MAPPING`; + +CREATE PROCEDURE `cloud`.`ADD_DISK_CONTROLLER_MAPPING` ( + IN disk_controller_name varchar(255), + IN disk_controller_reference varchar(255), + IN disk_controller_bus_name varchar(255), + IN disk_controller_hypervisor varchar(40), + IN disk_controller_max_device_count bigint unsigned, + IN disk_controller_max_controller_count bigint unsigned, + IN disk_controller_vmdk_adapter_type varchar(255), + IN disk_controller_min_hardware_version varchar(20) +) +BEGIN +INSERT INTO cloud.disk_controller_mapping (uuid, name, controller_reference, bus_name, hypervisor, max_device_count, + max_controller_count, vmdk_adapter_type, min_hardware_version, created) +SELECT UUID(), disk_controller_name, disk_controller_reference, disk_controller_bus_name, disk_controller_hypervisor, + disk_controller_max_device_count, disk_controller_max_controller_count, disk_controller_vmdk_adapter_type, + disk_controller_min_hardware_version, now() +FROM DUAL +WHERE NOT EXISTS (SELECT 1 FROM cloud.disk_controller_mapping WHERE cloud.disk_controller_mapping.name = disk_controller_name + AND cloud.disk_controller_mapping.hypervisor = disk_controller_hypervisor) +;END; diff --git a/engine/schema/src/main/resources/META-INF/db/schema-42010to42100.sql b/engine/schema/src/main/resources/META-INF/db/schema-42010to42100.sql index b01243ad989d..7fb45de73994 100644 --- a/engine/schema/src/main/resources/META-INF/db/schema-42010to42100.sql +++ b/engine/schema/src/main/resources/META-INF/db/schema-42010to42100.sql @@ -37,3 +37,32 @@ WHERE rp.rule = 'quotaStatement' AND NOT EXISTS(SELECT 1 FROM cloud.role_permissions rp_ WHERE rp.role_id = rp_.role_id AND rp_.rule = 'quotaCreditsList'); CALL `cloud`.`IDEMPOTENT_ADD_COLUMN`('cloud.host', 'last_mgmt_server_id', 'bigint unsigned DEFAULT NULL COMMENT "last management server this host is connected to" AFTER `mgmt_server_id`'); + +-- Disk controller mappings +CREATE TABLE IF NOT EXISTS `cloud`.`disk_controller_mapping` ( + `id` bigint(20) unsigned NOT NULL auto_increment, + `uuid` varchar(255) UNIQUE NOT NULL, + `name` varchar(255) NOT NULL, + `controller_reference` varchar(255) NOT NULL, + `bus_name` varchar(255) NOT NULL, + `hypervisor` varchar(40) NOT NULL, + `max_device_count` bigint unsigned DEFAULT NULL, + `max_controller_count` bigint unsigned DEFAULT NULL, + `vmdk_adapter_type` varchar(255) DEFAULT NULL, + `min_hardware_version` varchar(20) DEFAULT NULL, + `created` datetime NOT NULL, + `removed` datetime DEFAULT NULL, + PRIMARY KEY (`id`) + ); + +-- Add VMware's default disk controller mappings +CALL `cloud`.`ADD_DISK_CONTROLLER_MAPPING` ('osdefault', 'unused', 'unused', 'VMware', NULL, NULL, NULL, NULL); +CALL `cloud`.`ADD_DISK_CONTROLLER_MAPPING` ('ide', 'com.vmware.vim25.VirtualIDEController', 'ide', 'VMware', 2, 2, 'ide', NULL); +CALL `cloud`.`ADD_DISK_CONTROLLER_MAPPING` ('scsi', 'com.vmware.vim25.VirtualLsiLogicController', 'scsi', 'VMware', 16, 4, 'lsilogic', NULL); +CALL `cloud`.`ADD_DISK_CONTROLLER_MAPPING` ('buslogic', 'com.vmware.vim25.VirtualBusLogicController', 'scsi', 'VMware', 16, 4, 'buslogic', NULL); +CALL `cloud`.`ADD_DISK_CONTROLLER_MAPPING` ('lsilogic', 'com.vmware.vim25.VirtualLsiLogicController', 'scsi', 'VMware', 16, 4, 'lsilogic', NULL); +CALL `cloud`.`ADD_DISK_CONTROLLER_MAPPING` ('lsisas1068', 'com.vmware.vim25.VirtualLsiLogicSASController', 'scsi', 'VMware', 16, 4, 'lsilogic', NULL); +CALL `cloud`.`ADD_DISK_CONTROLLER_MAPPING` ('pvscsi', 'com.vmware.vim25.ParaVirtualSCSIController', 'scsi', 'VMware', 16, 4, 'lsilogic', '7'); +CALL `cloud`.`ADD_DISK_CONTROLLER_MAPPING` ('sata', 'com.vmware.vim25.VirtualAHCIController', 'sata', 'VMware', 30, 4, 'ide', '10'); +CALL `cloud`.`ADD_DISK_CONTROLLER_MAPPING` ('ahci', 'com.vmware.vim25.VirtualAHCIController', 'sata', 'VMware', 30, 4, 'ide', '10'); +CALL `cloud`.`ADD_DISK_CONTROLLER_MAPPING` ('nvme', 'com.vmware.vim25.VirtualNVMEController', 'nvme', 'VMware', 15, 4, 'ide', '13'); diff --git a/plugins/hypervisors/vmware/src/main/java/com/cloud/hypervisor/guru/VmwareVmImplementer.java b/plugins/hypervisors/vmware/src/main/java/com/cloud/hypervisor/guru/VmwareVmImplementer.java index 3885e06e7408..7c652f68aed0 100644 --- a/plugins/hypervisors/vmware/src/main/java/com/cloud/hypervisor/guru/VmwareVmImplementer.java +++ b/plugins/hypervisors/vmware/src/main/java/com/cloud/hypervisor/guru/VmwareVmImplementer.java @@ -322,7 +322,7 @@ private void setDiskControllers(VirtualMachineProfile vm, Map de } if (vm.getType() == VirtualMachine.Type.NetScalerVm) { - details.put(VmDetailConstants.ROOT_DISK_CONTROLLER, "scsi"); + details.put(VmDetailConstants.ROOT_DISK_CONTROLLER, DiskControllerType.scsi.toString()); } } diff --git a/plugins/hypervisors/vmware/src/main/java/com/cloud/hypervisor/vmware/manager/VmwareManagerImpl.java b/plugins/hypervisors/vmware/src/main/java/com/cloud/hypervisor/vmware/manager/VmwareManagerImpl.java index b64422482aa6..73e7d6722477 100644 --- a/plugins/hypervisors/vmware/src/main/java/com/cloud/hypervisor/vmware/manager/VmwareManagerImpl.java +++ b/plugins/hypervisors/vmware/src/main/java/com/cloud/hypervisor/vmware/manager/VmwareManagerImpl.java @@ -62,7 +62,9 @@ import org.apache.cloudstack.framework.config.dao.ConfigurationDao; import org.apache.cloudstack.framework.jobs.impl.AsyncJobManagerImpl; import org.apache.cloudstack.management.ManagementServerHost; +import org.apache.cloudstack.storage.DiskControllerMappingVO; import org.apache.cloudstack.storage.command.CheckDataStoreStoragePolicyComplainceCommand; +import org.apache.cloudstack.storage.dao.DiskControllerMappingDao; import org.apache.cloudstack.storage.datastore.db.PrimaryDataStoreDao; import org.apache.cloudstack.storage.datastore.db.StoragePoolVO; import org.apache.cloudstack.utils.identity.ManagementServerNode; @@ -243,6 +245,7 @@ public class VmwareManagerImpl extends ManagerBase implements VmwareManager, Vmw @Inject private VMInstanceDao vmInstanceDao; @Inject + private DiskControllerMappingDao diskControllerMappingDao; private UserVmCloneSettingDao cloneSettingDao; @Inject private TemplateManager templateManager; @@ -400,6 +403,9 @@ public boolean configure(String name, Map params) throws Configu _agentMgr.registerForHostEvents(this, true, true, true); + List mappingsInDatabase = diskControllerMappingDao.listForHypervisor(Hypervisor.HypervisorType.VMware); + VmwareHelper.configureDiskControllerMappingsInVmwareBaseModule(mappingsInDatabase); + logger.info("VmwareManagerImpl has been successfully configured"); return true; } diff --git a/plugins/hypervisors/vmware/src/main/java/com/cloud/hypervisor/vmware/resource/VmwareResource.java b/plugins/hypervisors/vmware/src/main/java/com/cloud/hypervisor/vmware/resource/VmwareResource.java index a8ba033f3ccd..7c579fa3f9de 100644 --- a/plugins/hypervisors/vmware/src/main/java/com/cloud/hypervisor/vmware/resource/VmwareResource.java +++ b/plugins/hypervisors/vmware/src/main/java/com/cloud/hypervisor/vmware/resource/VmwareResource.java @@ -56,10 +56,14 @@ import com.vmware.vim25.FolderFileInfo; import com.vmware.vim25.HostDatastoreBrowserSearchResults; import com.vmware.vim25.HostDatastoreBrowserSearchSpec; +import com.vmware.vim25.VirtualCdrom; import com.vmware.vim25.VirtualCdromIsoBackingInfo; +import com.vmware.vim25.VirtualController; +import com.vmware.vim25.VirtualIDEController; import com.vmware.vim25.VirtualMachineConfigSummary; import org.apache.cloudstack.api.ApiConstants; import org.apache.cloudstack.backup.PrepareForBackupRestorationCommand; +import org.apache.cloudstack.storage.DiskControllerMappingVO; import org.apache.cloudstack.storage.command.CopyCommand; import org.apache.cloudstack.storage.command.StorageSubSystemCommand; import org.apache.cloudstack.storage.command.browser.ListDataStoreObjectsAnswer; @@ -245,7 +249,6 @@ 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.FeatureKeyConstants; import com.cloud.hypervisor.vmware.mo.HostDatastoreSystemMO; import com.cloud.hypervisor.vmware.mo.HostMO; @@ -371,7 +374,6 @@ import com.vmware.vim25.VirtualMachineRuntimeInfo; import com.vmware.vim25.VirtualMachineToolsStatus; import com.vmware.vim25.VirtualMachineVideoCard; -import com.vmware.vim25.VirtualSCSIController; import com.vmware.vim25.VirtualUSBController; import com.vmware.vim25.VmConfigInfo; import com.vmware.vim25.VmConfigSpec; @@ -415,7 +417,6 @@ public class VmwareResource extends ServerResourceBase implements StoragePoolRes protected boolean _instanceNameFlag = false; protected boolean _recycleHungWorker = false; - protected DiskControllerType _rootDiskController = DiskControllerType.ide; protected ManagedObjectReference _morHyperHost; protected final static ThreadLocal s_serviceContext = new ThreadLocal(); @@ -1971,61 +1972,157 @@ protected ScaleVmAnswer execute(ScaleVmCommand cmd) { return new ScaleVmAnswer(cmd, true, null); } - protected void ensureDiskControllers(VirtualMachineMO vmMo, Pair controllerInfo) throws Exception { - if (vmMo == null) { + /** + * Validates whether the instance has the required disk controllers, and creates them if it does not. + * @param controllerInfo pair containing the root disk and data disk controllers, respectively. + * @throws CloudRuntimeException if the instance does not have the required controllers, but this method is unable to create them. + */ + protected void ensureDiskControllers(VirtualMachineMO vmMo, Pair controllerInfo, + boolean isSystemVm) throws Exception { + Pair chosenDiskControllers = VmwareHelper.chooseDiskControllers(controllerInfo, vmMo, null, null); + Set requiredDiskControllers = VmwareHelper.getRequiredDiskControllers(chosenDiskControllers, isSystemVm); + + if (vmHasRequiredControllers(vmMo, requiredDiskControllers)) { return; } - Pair chosenDiskControllers = VmwareHelper.chooseRequiredDiskControllers(controllerInfo, vmMo, null, null); - String scsiDiskController = HypervisorHostHelper.getScsiController(chosenDiskControllers); - if (scsiDiskController == null) { - return; + validateRequiredVirtualHardwareVersionForNewDiskControllers(vmMo, requiredDiskControllers); + validateNewDiskControllersSupportExistingDisks(vmMo, chosenDiskControllers); + teardownAllDiskControllers(vmMo); + + VirtualMachineConfigSpec vmConfigSpec = new VirtualMachineConfigSpec(); + VmwareHelper.addDiskControllersToVmConfigSpec(vmConfigSpec, requiredDiskControllers, isSystemVm); + if (!vmMo.configureVm(vmConfigSpec)) { + throw new CloudRuntimeException("Unable to configure virtual machine's disk controllers."); } + logger.info("Successfully added virtual machine [{}]'s required disk controllers [{}].", vmMo, requiredDiskControllers); + } - 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 + protected boolean vmHasRequiredControllers(VirtualMachineMO vmMo, Set requiredDiskControllers) throws Exception { + Set requiredDiskControllerClasspaths = requiredDiskControllers.stream() + .map(DiskControllerMappingVO::getControllerReference) + .collect(Collectors.toSet()); - if (DiskControllerType.getType(scsiDiskController) != scsiControllerInfo.third()) { - logger.debug(String.format("Change controller type from: %s to: %s", scsiControllerInfo.third().toString(), - scsiDiskController)); - vmMo.tearDownDevices(new Class[]{VirtualSCSIController.class}); - vmMo.addScsiDeviceControllers(DiskControllerType.getType(scsiDiskController)); - return; + List existingControllers = vmMo.getControllers(); + for (VirtualController controller : existingControllers) { + String classpath = controller.getClass().getName(); + requiredDiskControllerClasspaths.remove(classpath); } - if (requiredNumScsiControllers == 0) { - return; + if (requiredDiskControllerClasspaths.isEmpty()) { + logger.debug("Virtual machine [{}] has all the required controllers [{}].", vmMo, requiredDiskControllers); + return true; + } + logger.debug("Virtual machine [{}] does not have the following required controllers: [{}].", vmMo, requiredDiskControllerClasspaths); + return false; + } + + /*** + * Validates if the provided VirtualMachineMO's virtual hardware version supports the required disk controllers. + * @throws CloudRuntimeException if one of the disk controllers requires a version greater than the instance's virtual + * hardware version. + */ + protected void validateRequiredVirtualHardwareVersionForNewDiskControllers(VirtualMachineMO vmMo, Set requiredDiskControllers) throws Exception { + int hardwareVersion = vmMo.getVirtualHardwareVersion(); + List unsupportedDiskControllers = new ArrayList<>(); + + for (DiskControllerMappingVO diskController : requiredDiskControllers) { + if (diskController.getMinHardwareVersion() == null) { + continue; + } + Integer requiredVersion = Integer.parseInt(diskController.getMinHardwareVersion()); + if (hardwareVersion < requiredVersion) { + unsupportedDiskControllers.add(diskController); + } + } + + if (!unsupportedDiskControllers.isEmpty()) { + String names = unsupportedDiskControllers.stream().map(DiskControllerMappingVO::getName).collect(Collectors.joining(", ")); + String requiredVersions = unsupportedDiskControllers.stream().map(DiskControllerMappingVO::getMinHardwareVersion).collect(Collectors.joining(", ")); + logger.debug("Virtual machine [{}] does not support disk controllers [{}], as its virtual hardware version is [{}] but the controllers require, respectfully, versions [{}].", + vmMo, names, hardwareVersion, requiredVersions); + throw new CloudRuntimeException(String.format("Disk controllers [%s] are not supported.", names)); } - 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. + } + + /*** + * Validates if the provided disk controllers have enough device nodes to support all the existing disks. + * @param controllerInfo pair containing the root disk and data disk controllers, respectively. + * @throws CloudRuntimeException if the instance has more disks than the amount supported by the controllers. + */ + protected void validateNewDiskControllersSupportExistingDisks(VirtualMachineMO vmMo, Pair controllerInfo) throws Exception { + DiskControllerMappingVO rootDiskController = controllerInfo.first(); + DiskControllerMappingVO dataDiskController = controllerInfo.second(); + + List disks = vmMo.getVirtualDisks(); + VirtualCdrom cdrom = (VirtualCdrom) vmMo.getIsoDevice(); + + if (rootDiskController == dataDiskController) { + int maxDevicesInBus = getMaxSupportedDevicesInBus(rootDiskController); + int devicesInBus = disks.size(); + + if (cdrom != null && Class.forName(rootDiskController.getControllerReference()) == VirtualIDEController.class) { + devicesInBus++; + } + + if (maxDevicesInBus < devicesInBus) { + throw new CloudRuntimeException(String.format("Virtual machine [%s] has [%s] virtual disks (including cdrom), but the new disk controllers only support [%s] devices.", + vmMo.getName(), devicesInBus, maxDevicesInBus)); + } return; } - ensureScsiDiskControllers(vmMo, scsiDiskController, requiredNumScsiControllers, availableBusNum); + + int maxDevicesInRootDiskBus = getMaxSupportedDevicesInBus(rootDiskController); + int maxDevicesInDataDiskBus = getMaxSupportedDevicesInBus(dataDiskController); + int devicesInRootDiskBus = 1; + int devicesInDataDiskBus = disks.size() - 1; + + if (cdrom != null) { + if (Class.forName(rootDiskController.getControllerReference()) == VirtualIDEController.class) { + devicesInRootDiskBus++; + } else if (Class.forName(dataDiskController.getControllerReference()) == VirtualIDEController.class) { + devicesInDataDiskBus++; + } + } + + if (maxDevicesInRootDiskBus < devicesInRootDiskBus || maxDevicesInDataDiskBus < devicesInDataDiskBus) { + throw new CloudRuntimeException(String.format("Virtual machine [%s] has [%s] devices in the root disk's bus and [%s] in the data disks' bus, " + + "but these buses only support [%s] and [%s] devices.", vmMo.getName(), devicesInRootDiskBus, devicesInDataDiskBus, + maxDevicesInRootDiskBus, maxDevicesInDataDiskBus)); + } } - 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); + /** + * Returns the total amount of disks that the instance can have using the provided disk controller type, considering + * that the instance has the maximum amount of that disk controller type created. + */ + protected int getMaxSupportedDevicesInBus(DiskControllerMappingVO mapping) { + int devicesPerController = mapping.getMaxDeviceCount(); + if (VmwareHelper.isControllerScsi(mapping)) { + devicesPerController--; + } + return devicesPerController * mapping.getMaxControllerCount(); + } + + /** + * Removes all disk controllers from the virtual machine. + */ + protected void teardownAllDiskControllers(VirtualMachineMO vmMo) throws Exception { + List allMappings = VmwareHelper.getAllSupportedDiskControllerMappingsExceptOsDefault(); + Set> classesToTeardown = new HashSet<>(); + + for (DiskControllerMappingVO mapping : allMappings) { + try { + Class diskControllerClass = Class.forName(mapping.getControllerReference()); + if (diskControllerClass != VirtualIDEController.class) { + classesToTeardown.add(diskControllerClass); + } + } catch (ClassNotFoundException e) { + logger.debug("Could not find class for controller reference [{}]; ignoring it on teardown.", mapping.getControllerReference()); } - 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.ensureLsiLogicDeviceControllers(requiredNumScsiControllers, availableBusNum); } + + vmMo.tearDownDevices(classesToTeardown.toArray(new Class[0])); } protected StartAnswer execute(StartCommand cmd) { @@ -2050,7 +2147,7 @@ protected StartAnswer execute(StartCommand cmd) { String vmNameOnVcenter = names.second(); DiskTO rootDiskTO = null; String bootMode = getBootModeFromVmSpec(vmSpec, deployAsIs); - Pair controllerInfo = getControllerInfoFromVmSpec(vmSpec); + Pair controllerInfo = getControllerInfoFromVmSpec(vmSpec); Boolean systemVm = vmSpec.getType().isUsedBySystem(); // Thus, vmInternalCSName always holds i-x-y, the cloudstack generated internal VM name. @@ -2087,9 +2184,6 @@ protected StartAnswer execute(StartCommand cmd) { VirtualMachineDiskInfoBuilder diskInfoBuilder = null; VirtualDevice[] nicDevices = null; VirtualMachineMO vmMo = hyperHost.findVmOnHyperHost(vmInternalCSName); - DiskControllerType systemVmScsiControllerType = DiskControllerType.lsilogic; - int firstScsiControllerBusNum = 0; - int numScsiControllerForSystemVm = 1; boolean hasSnapshot = false; List> diskDatastores = null; @@ -2105,8 +2199,7 @@ protected StartAnswer execute(StartCommand cmd) { nicDevices = vmMo.getNicDevices(); tearDownVmDevices(vmMo, hasSnapshot, deployAsIs); - ensureDiskControllersInternal(vmMo, systemVm, controllerInfo, systemVmScsiControllerType, - numScsiControllerForSystemVm, firstScsiControllerBusNum, deployAsIs); + ensureDiskControllersInternal(vmMo, systemVm, controllerInfo, deployAsIs); } else { ManagedObjectReference morDc = hyperHost.getHyperHostDatacenter(); assert (morDc != null); @@ -2127,8 +2220,7 @@ protected StartAnswer execute(StartCommand cmd) { diskDatastores = vmMo.getAllDiskDatastores(); tearDownVmDevices(vmMo, hasSnapshot, deployAsIs); - ensureDiskControllersInternal(vmMo, systemVm, controllerInfo, systemVmScsiControllerType, - numScsiControllerForSystemVm, firstScsiControllerBusNum, deployAsIs); + ensureDiskControllersInternal(vmMo, systemVm, controllerInfo, deployAsIs); } 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); @@ -2223,10 +2315,6 @@ protected StartAnswer execute(StartCommand cmd) { VirtualMachineConfigSpec vmConfigSpec = new VirtualMachineConfigSpec(); int i = 0; - int ideUnitNumber = !deployAsIs ? 0 : vmMo.getNextIDEDeviceNumber(); - int scsiUnitNumber = !deployAsIs ? 0 : vmMo.getNextScsiDiskDeviceNumber(); - int ideControllerKey = vmMo.getIDEDeviceControllerKey(); - int scsiControllerKey = vmMo.getScsiDeviceControllerKeyNoException(); VirtualDeviceConfigSpec[] deviceConfigSpecArray = new VirtualDeviceConfigSpec[totalChangeDevices]; DiskTO[] sortedDisks = sortVolumesByDeviceId(disks); VmwareHelper.setBasicVmConfig(vmConfigSpec, vmSpec.getCpus(), vmSpec.getMaxSpeed(), getReservedCpuMHZ(vmSpec), (int) (vmSpec.getMaxRam() / (1024 * 1024)), @@ -2268,8 +2356,12 @@ protected StartAnswer execute(StartCommand cmd) { // // Setup ISO device // + Pair chosenDiskControllers = VmwareHelper.chooseDiskControllers(controllerInfo, vmMo, null, null); + Map diskControllerCurrentUnitNumbers = createDiskControllerUnitNumberMap(vmMo, chosenDiskControllers, deployAsIs); // prepare systemvm patch ISO + String ideClasspath = VirtualIDEController.class.getName(); + int ideUnitNumber = diskControllerCurrentUnitNumbers.get(ideClasspath); if (vmSpec.getType() != VirtualMachine.Type.User) { // attach ISO (for patching of system VM) Pair secStoreUrlAndId = mgr.getSecondaryStorageStoreUrlAndId(Long.parseLong(_dcId)); @@ -2288,8 +2380,9 @@ protected StartAnswer execute(StartCommand cmd) { DatastoreMO secDsMo = new DatastoreMO(hyperHost.getContext(), morSecDs); deviceConfigSpecArray[i] = new VirtualDeviceConfigSpec(); - Pair isoInfo = VmwareHelper.prepareIsoDevice(vmMo, - null, secDsMo.getMor(), true, true, ideUnitNumber++, i + 1); + Pair isoInfo = VmwareHelper.prepareIsoDevice(vmMo, null, secDsMo.getMor(), + true, true, ideUnitNumber++, i + 1); + diskControllerCurrentUnitNumbers.replace(ideClasspath, ideUnitNumber); deviceConfigSpecArray[i].setDevice(isoInfo.first()); if (isoInfo.second()) { if (logger.isDebugEnabled()) @@ -2307,12 +2400,14 @@ protected StartAnswer execute(StartCommand cmd) { for (DiskTO vol : disks) { if (vol.getType() == Volume.Type.ISO) { configureIso(hyperHost, vmMo, vol, deviceConfigSpecArray, ideUnitNumber++, i); + diskControllerCurrentUnitNumbers.replace(ideClasspath, ideUnitNumber); i++; } } } else { deviceConfigSpecArray[i] = new VirtualDeviceConfigSpec(); Pair isoInfo = VmwareHelper.prepareIsoDevice(vmMo, null, null, true, true, ideUnitNumber++, i + 1); + diskControllerCurrentUnitNumbers.replace(ideClasspath, ideUnitNumber); deviceConfigSpecArray[i].setDevice(isoInfo.first()); if (isoInfo.second()) { if (logger.isDebugEnabled()) @@ -2329,9 +2424,6 @@ protected StartAnswer execute(StartCommand cmd) { } } - int controllerKey; - Pair chosenDiskControllers = VmwareHelper.chooseRequiredDiskControllers(controllerInfo,vmMo, null, null); - // // Setup ROOT/DATA disk devices // @@ -2343,6 +2435,7 @@ protected StartAnswer execute(StartCommand cmd) { if (vol.getType() == Volume.Type.ISO) { if (deployAsIs) { configureIso(hyperHost, vmMo, vol, deviceConfigSpecArray, ideUnitNumber++, i); + diskControllerCurrentUnitNumbers.replace(ideClasspath, ideUnitNumber); i++; } continue; @@ -2355,32 +2448,15 @@ protected StartAnswer execute(StartCommand cmd) { } VirtualMachineDiskInfo matchingExistingDisk = getMatchingExistingDisk(diskInfoBuilder, vol, hyperHost, context); - String diskController = getDiskController(vmMo, matchingExistingDisk, vol, chosenDiskControllers, deployAsIs); - 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 { - if (VmwareHelper.isReservedScsiDeviceNumber(scsiUnitNumber)) { - scsiUnitNumber++; - } + DiskControllerMappingVO diskController = getControllerForDisk(vmMo, matchingExistingDisk, vol, chosenDiskControllers, deployAsIs); + int unitNumber = diskControllerCurrentUnitNumbers.get(diskController.getControllerReference()); - controllerKey = vmMo.getScsiDiskControllerKeyNoException(diskController, scsiUnitNumber); - 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(), scsiUnitNumber); - } - } if (!hasSnapshot) { + int diskControllerNumber = unitNumber / diskController.getMaxDeviceCount(); + VirtualController diskControllerDevice = (VirtualController) vmMo.getNthDevice(diskController.getControllerReference(), diskControllerNumber); + vmMo.validateDiskControllerIsAvailable(diskControllerDevice, diskController); // This may be unnecessary, as a validation of whether the controllers support all disks was already performed. + int deviceNumber = unitNumber % diskController.getMaxDeviceCount(); + deviceConfigSpecArray[i] = new VirtualDeviceConfigSpec(); VolumeObjectTO volumeTO = (VolumeObjectTO) vol.getData(); @@ -2430,17 +2506,9 @@ protected StartAnswer execute(StartCommand cmd) { String[] diskChain = syncDiskChain(dcMo, vmMo, vol, matchingExistingDisk, volumeDsDetails.second()); - int deviceNumber = -1; - if (controllerKey == vmMo.getIDEControllerKey(ideUnitNumber)) { - deviceNumber = ideUnitNumber % VmwareHelper.MAX_ALLOWED_DEVICES_IDE_CONTROLLER; - ideUnitNumber++; - } else { - deviceNumber = scsiUnitNumber % VmwareHelper.MAX_ALLOWED_DEVICES_SCSI_CONTROLLER; - scsiUnitNumber++; - } - Long maxIops = volumeTO.getIopsWriteRate() + volumeTO.getIopsReadRate(); - VirtualDevice device = VmwareHelper.prepareDiskDevice(vmMo, null, controllerKey, diskChain, volumeDsDetails.first(), deviceNumber, i + 1, maxIops); + VirtualDevice device = VmwareHelper.prepareDiskDevice(vmMo, null, diskControllerDevice.getKey(), diskChain, + volumeDsDetails.first(), deviceNumber, i + 1, maxIops); logger.debug(LogUtils.logGsonWithoutException("The following definitions will be used to start the VM: virtual device [%s], volume [%s].", device, volumeTO)); diskStoragePolicyId = volumeTO.getvSphereStoragePolicyId(); @@ -2464,11 +2532,14 @@ protected StartAnswer execute(StartCommand cmd) { logger.debug("Prepare volume at new device " + _gson.toJson(device)); i++; - } else { - if (controllerKey == vmMo.getIDEControllerKey(ideUnitNumber)) - ideUnitNumber++; - else - scsiUnitNumber++; + } + + diskControllerCurrentUnitNumbers.replace(diskController.getControllerReference(), unitNumber + 1); + if (VmwareHelper.isControllerScsi(diskController)) { + int scsiUnitNumber = diskControllerCurrentUnitNumbers.get(diskController.getControllerReference()); + if (VmwareHelper.isReservedScsiDeviceNumber(scsiUnitNumber)) { + diskControllerCurrentUnitNumbers.replace(diskController.getControllerReference(), scsiUnitNumber + 1); + } } } @@ -2629,7 +2700,7 @@ protected StartAnswer execute(StartCommand cmd) { Map> iqnToData = new HashMap<>(); - postDiskConfigBeforeStart(vmMo, vmSpec, sortedDisks, ideControllerKey, scsiControllerKey, iqnToData, hyperHost, context); + postDiskConfigBeforeStart(vmMo, vmSpec, sortedDisks, iqnToData, hyperHost, context); // // Power-on VM @@ -2708,6 +2779,38 @@ protected StartAnswer execute(StartCommand cmd) { } } + /** + * Returns a hash-map with keys being the classpath of existing disk controllers, and values representing the next available + * unit number for the disk controller type. This hash-map is used in the virtual machine start workflow to associate + * the disks with their respective controller. + * @param controllerInfo pair containing the root disk and data disk controllers, respectively. + * @param deployAsIs if false, all values will be initialized as 0; otherwise, they will be initialized to the next available unit number for each of the controller types. + * @throws CloudRuntimeException if the template is deploy-as-is and no disk controller for one of the required controller types is found. + */ + protected Map createDiskControllerUnitNumberMap(VirtualMachineMO vmMo, Pair controllerInfo, + boolean deployAsIs) throws Exception { + Set existingDiskControllers = new HashSet<>(); + existingDiskControllers.add(controllerInfo.first()); + existingDiskControllers.add(controllerInfo.second()); + existingDiskControllers.add(VmwareHelper.getDiskControllerMapping(null, VirtualIDEController.class.getName())); + + Map currentUnitNumberMap = new HashMap<>(); + for (DiskControllerMappingVO diskController : existingDiskControllers) { + String classpath = diskController.getControllerReference(); + if (!deployAsIs) { + currentUnitNumberMap.put(classpath, 0); + continue; + } + Pair nextAvailableControllerKeyAndUnitNumber = vmMo.getNextAvailableControllerKeyAndDeviceNumberForType(diskController); + if (nextAvailableControllerKeyAndUnitNumber == null) { + throw new CloudRuntimeException(String.format("Unable to find an available disk controller of the required type: [%s].", + diskController.getName())); + } + currentUnitNumberMap.put(diskController.getControllerReference(), nextAvailableControllerKeyAndUnitNumber.second()); + } + return currentUnitNumberMap; + } + private boolean powerOnVM(final VirtualMachineMO vmMo, final String vmInternalCSName, final String vmNameOnVcenter) throws Exception { int retry = 20; while (retry-- > 0) { @@ -2806,15 +2909,10 @@ private void configureVNC(VirtualMachineTO vmSpec, ArrayList extraO } - private void ensureDiskControllersInternal(VirtualMachineMO vmMo, Boolean systemVm, - Pair controllerInfo, - DiskControllerType systemVmScsiControllerType, - int numScsiControllerForSystemVm, - int firstScsiControllerBusNum, boolean deployAsIs) throws Exception { - if (systemVm) { - ensureScsiDiskControllers(vmMo, systemVmScsiControllerType.toString(), numScsiControllerForSystemVm, firstScsiControllerBusNum); - } else if (!deployAsIs) { - ensureDiskControllers(vmMo, controllerInfo); + protected void ensureDiskControllersInternal(VirtualMachineMO vmMo, Boolean systemVm, Pair controllerInfo, + boolean deployAsIs) throws Exception { + if (systemVm || !deployAsIs) { + ensureDiskControllers(vmMo, controllerInfo, systemVm); } } @@ -2838,11 +2936,11 @@ private String getGuestOsIdFromVmSpec(VirtualMachineTO vmSpec, boolean deployAsI return translateGuestOsIdentifier(vmSpec.getArch(), vmSpec.getOs(), vmSpec.getPlatformEmulator()).value(); } - private Pair getControllerInfoFromVmSpec(VirtualMachineTO vmSpec) throws CloudRuntimeException { + protected Pair getControllerInfoFromVmSpec(VirtualMachineTO vmSpec) throws CloudRuntimeException { + boolean isSystemVm = vmSpec.getType().isUsedBySystem(); String rootDiskControllerDetail = vmSpec.getDetails().get(VmDetailConstants.ROOT_DISK_CONTROLLER); String dataDiskControllerDetail = vmSpec.getDetails().get(VmDetailConstants.DATA_DISK_CONTROLLER); - VmwareHelper.validateDiskControllerDetails(rootDiskControllerDetail, dataDiskControllerDetail); - return new Pair<>(rootDiskControllerDetail, dataDiskControllerDetail); + return VmwareHelper.getDiskControllersFromVmSettings(rootDiskControllerDetail, dataDiskControllerDetail, isSystemVm); } private String getBootModeFromVmSpec(VirtualMachineTO vmSpec, boolean deployAsIs) { @@ -3571,30 +3669,36 @@ private VirtualMachineDiskInfo getMatchingExistingDisk(VirtualMachineDiskInfoBui } } - private String getDiskController(VirtualMachineMO vmMo, VirtualMachineDiskInfo matchingExistingDisk, DiskTO vol, Pair controllerInfo, boolean deployAsIs) throws Exception { - DiskControllerType controllerType = DiskControllerType.none; + /** + * Returns the disk controller mapping that should be used for the disk. If the instance uses a deploy-as-is template + * and the disk already exists, tries to choose based on the current bus name first, but chooses any available disk controller + * if unable to choose a type based on the bus name; if the instance does not use a deploy-as-is template or the disk + * does not exist, chooses based on controllerInfo. + * @param controllerInfo pair containing the root disk and data disk controllers, respectively. Ignored if the instance uses a deploy-as-is template and the disk already exists. + * @throws CloudRuntimeException if the instance uses a deploy-as-is template, but no disk controller is available. + */ + protected DiskControllerMappingVO getControllerForDisk(VirtualMachineMO vmMo, VirtualMachineDiskInfo matchingExistingDisk, + DiskTO vol, Pair controllerInfo, + boolean deployAsIs) throws Exception { if (deployAsIs && matchingExistingDisk != null) { String currentBusName = matchingExistingDisk.getDiskDeviceBusName(); if (currentBusName != null) { - 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; + Set mappingsForExistingDiskControllers = vmMo.getMappingsForExistingDiskControllers(); + for (DiskControllerMappingVO mapping : mappingsForExistingDiskControllers) { + if (currentBusName.startsWith(mapping.getBusName())) { + logger.debug("Choosing disk controller [{}] for virtual machine [{}] based on current bus name [{}].", mapping.getName(), vmMo, currentBusName); + return mapping; + } } } - if (controllerType == DiskControllerType.scsi || controllerType == DiskControllerType.none) { - Ternary vmScsiControllerInfo = vmMo.getScsiControllerInfo(); - controllerType = vmScsiControllerInfo.third(); - } - return controllerType.toString(); + return vmMo.getAnyExistingAvailableDiskController(); } return VmwareHelper.getControllerBasedOnDiskType(controllerInfo, vol); } - private void postDiskConfigBeforeStart(VirtualMachineMO vmMo, VirtualMachineTO vmSpec, DiskTO[] sortedDisks, int ideControllerKey, - int scsiControllerKey, Map> iqnToData, VmwareHypervisorHost hyperHost, VmwareContext context) throws Exception { + private void postDiskConfigBeforeStart(VirtualMachineMO vmMo, VirtualMachineTO vmSpec, DiskTO[] sortedDisks, Map> iqnToData, + VmwareHypervisorHost hyperHost, VmwareContext context) throws Exception { VirtualMachineDiskInfoBuilder diskInfoBuilder = vmMo.getDiskInfoBuilder(); for (DiskTO vol : sortedDisks) { @@ -6884,14 +6988,6 @@ public boolean configure(String name, Map params) throws Configu if (value != null && value.equalsIgnoreCase("true")) _recycleHungWorker = true; - value = (String) params.get("vmware.root.disk.controller"); - if (value != null && value.equalsIgnoreCase("scsi")) - _rootDiskController = DiskControllerType.scsi; - 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) _portsPerDvPortGroup = intObj.intValue(); diff --git a/plugins/hypervisors/vmware/src/main/java/com/cloud/storage/resource/VmwareSecondaryStorageResourceHandler.java b/plugins/hypervisors/vmware/src/main/java/com/cloud/storage/resource/VmwareSecondaryStorageResourceHandler.java index ece6176547c0..f53a3f7b2260 100644 --- a/plugins/hypervisors/vmware/src/main/java/com/cloud/storage/resource/VmwareSecondaryStorageResourceHandler.java +++ b/plugins/hypervisors/vmware/src/main/java/com/cloud/storage/resource/VmwareSecondaryStorageResourceHandler.java @@ -18,6 +18,7 @@ import java.util.List; +import com.cloud.agent.api.SecStorageVMSetupCommand; import org.apache.cloudstack.storage.command.StorageSubSystemCommand; import org.apache.cloudstack.storage.resource.SecondaryStorageResourceHandler; import org.apache.logging.log4j.Logger; @@ -114,6 +115,8 @@ public Answer executeRequest(Command cmd) { answer = storageSubsystemHandler.handleStorageCommands((StorageSubSystemCommand)cmd); } else if (cmd instanceof CreateEntityDownloadURLCommand) { answer = execute((CreateEntityDownloadURLCommand)cmd); + } else if (cmd instanceof SecStorageVMSetupCommand) { + answer = execute((SecStorageVMSetupCommand) cmd); } else { answer = _resource.defaultAction(cmd); } @@ -183,6 +186,11 @@ private Answer execute(CreateVolumeFromSnapshotCommand cmd) { return _storageMgr.execute(this, cmd); } + private Answer execute(SecStorageVMSetupCommand cmd) { + VmwareHelper.configureDiskControllerMappingsInVmwareBaseModule(cmd.getSupportedDiskControllers()); + return _resource.defaultAction(cmd); + } + @Override public VmwareContext getServiceContext(Command cmd) { String guid = cmd.getContextParam("guid"); diff --git a/plugins/hypervisors/vmware/src/main/java/com/cloud/storage/resource/VmwareStorageProcessor.java b/plugins/hypervisors/vmware/src/main/java/com/cloud/storage/resource/VmwareStorageProcessor.java index c99d7d4d7075..900a9e214da7 100644 --- a/plugins/hypervisors/vmware/src/main/java/com/cloud/storage/resource/VmwareStorageProcessor.java +++ b/plugins/hypervisors/vmware/src/main/java/com/cloud/storage/resource/VmwareStorageProcessor.java @@ -35,6 +35,7 @@ import java.util.concurrent.TimeUnit; import org.apache.cloudstack.agent.directdownload.DirectDownloadCommand; +import org.apache.cloudstack.storage.DiskControllerMappingVO; import org.apache.cloudstack.storage.command.AttachAnswer; import org.apache.cloudstack.storage.command.AttachCommand; import org.apache.cloudstack.storage.command.CheckDataStoreStoragePolicyComplainceCommand; @@ -76,7 +77,6 @@ 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; @@ -2101,18 +2101,11 @@ private Answer attachVolume(Command cmd, DiskTO disk, boolean isAttach, boolean AttachAnswer answer = new AttachAnswer(disk); if (isAttach) { - String rootDiskControllerDetail = DiskControllerType.ide.toString(); - if (controllerInfo != null && StringUtils.isNotEmpty(controllerInfo.get(VmDetailConstants.ROOT_DISK_CONTROLLER))) { - rootDiskControllerDetail = controllerInfo.get(VmDetailConstants.ROOT_DISK_CONTROLLER); - } - String dataDiskControllerDetail = getLegacyVmDataDiskController(); - if (controllerInfo != null && StringUtils.isNotEmpty(controllerInfo.get(VmDetailConstants.DATA_DISK_CONTROLLER))) { - dataDiskControllerDetail = controllerInfo.get(VmDetailConstants.DATA_DISK_CONTROLLER); - } - - VmwareHelper.validateDiskControllerDetails(rootDiskControllerDetail, dataDiskControllerDetail); - Pair chosenDiskControllers = VmwareHelper.chooseRequiredDiskControllers(new Pair<>(rootDiskControllerDetail, dataDiskControllerDetail), vmMo, null, null); - String diskController = VmwareHelper.getControllerBasedOnDiskType(chosenDiskControllers, disk); + String rootDiskControllerDetail = controllerInfo.get(VmDetailConstants.ROOT_DISK_CONTROLLER); + String dataDiskControllerDetail = controllerInfo.get(VmDetailConstants.DATA_DISK_CONTROLLER); + Pair specifiedDiskControllers = VmwareHelper.getDiskControllersFromVmSettings(rootDiskControllerDetail, dataDiskControllerDetail, false); + Pair chosenDiskControllers = VmwareHelper.chooseDiskControllers(specifiedDiskControllers, vmMo, null, null); + DiskControllerMappingVO diskController = VmwareHelper.getControllerBasedOnDiskType(chosenDiskControllers, disk); vmMo.attachDisk(new String[] { datastoreVolumePath }, morDs, diskController, storagePolicyId, volumeTO.getIopsReadRate() + volumeTO.getIopsWriteRate()); VirtualMachineDiskInfoBuilder diskInfoBuilder = vmMo.getDiskInfoBuilder(); @@ -3750,10 +3743,6 @@ private String deriveTemplateUuidOnHost(VmwareHypervisorHost hyperHost, String s return templateUuid; } - private String getLegacyVmDataDiskController() throws Exception { - return DiskControllerType.lsilogic.toString(); - } - void setNfsVersion(String nfsVersion){ this._nfsVersion = nfsVersion; logger.debug("VmwareProcessor instance now using NFS version: " + nfsVersion); diff --git a/plugins/hypervisors/vmware/src/test/java/com/cloud/hypervisor/vmware/VmwareDatacenterApiUnitTest.java b/plugins/hypervisors/vmware/src/test/java/com/cloud/hypervisor/vmware/VmwareDatacenterApiUnitTest.java index 940bfcac5473..7260f22f2840 100644 --- a/plugins/hypervisors/vmware/src/test/java/com/cloud/hypervisor/vmware/VmwareDatacenterApiUnitTest.java +++ b/plugins/hypervisors/vmware/src/test/java/com/cloud/hypervisor/vmware/VmwareDatacenterApiUnitTest.java @@ -73,6 +73,7 @@ import org.apache.cloudstack.context.CallContext; import org.apache.cloudstack.engine.subsystem.api.storage.DataStoreManager; import org.apache.cloudstack.framework.config.dao.ConfigurationDao; +import org.apache.cloudstack.storage.dao.DiskControllerMappingDao; import org.apache.cloudstack.storage.datastore.db.ImageStoreDao; import org.apache.cloudstack.storage.datastore.db.ImageStoreDetailsDao; import org.apache.cloudstack.storage.datastore.db.PrimaryDataStoreDao; @@ -495,6 +496,11 @@ public VsphereStoragePolicyDao vsphereStoragePolicyDao() { return Mockito.mock(VsphereStoragePolicyDao.class); } + @Bean + public DiskControllerMappingDao diskControllerMappingDao() { + return Mockito.mock(DiskControllerMappingDao.class); + } + @Bean public StorageManager storageManager() { return Mockito.mock(StorageManager.class); diff --git a/plugins/hypervisors/vmware/src/test/java/com/cloud/hypervisor/vmware/manager/VmwareManagerImplTest.java b/plugins/hypervisors/vmware/src/test/java/com/cloud/hypervisor/vmware/manager/VmwareManagerImplTest.java index 68e2ae4cc660..ed41d2463d16 100644 --- a/plugins/hypervisors/vmware/src/test/java/com/cloud/hypervisor/vmware/manager/VmwareManagerImplTest.java +++ b/plugins/hypervisors/vmware/src/test/java/com/cloud/hypervisor/vmware/manager/VmwareManagerImplTest.java @@ -21,6 +21,7 @@ import java.util.Map; import org.apache.cloudstack.api.command.admin.zone.UpdateVmwareDcCmd; +import org.apache.cloudstack.storage.dao.DiskControllerMappingDao; import org.junit.Assert; import org.junit.Before; import org.junit.Test; @@ -69,6 +70,8 @@ public class VmwareManagerImplTest { private Map clusterDetails; @Mock private Map hostDetails; + @Mock + private DiskControllerMappingDao diskControllerMappingDaoMock; @Before public void beforeTest() { diff --git a/plugins/hypervisors/vmware/src/test/java/com/cloud/hypervisor/vmware/resource/VmwareResourceTest.java b/plugins/hypervisors/vmware/src/test/java/com/cloud/hypervisor/vmware/resource/VmwareResourceTest.java index 58d8e5ef254a..962cfa3e193c 100644 --- a/plugins/hypervisors/vmware/src/test/java/com/cloud/hypervisor/vmware/resource/VmwareResourceTest.java +++ b/plugins/hypervisors/vmware/src/test/java/com/cloud/hypervisor/vmware/resource/VmwareResourceTest.java @@ -38,21 +38,36 @@ import java.util.Date; import java.util.EnumMap; import java.util.HashMap; +import java.util.HashSet; import java.util.List; import java.util.Map; +import java.util.Set; +import com.cloud.agent.api.to.DiskTO; import com.cloud.hypervisor.vmware.mo.DatastoreMO; import com.cloud.hypervisor.vmware.mo.HostDatastoreBrowserMO; import com.cloud.hypervisor.vmware.mo.HypervisorHostHelper; import com.cloud.hypervisor.vmware.util.VmwareHelper; +import com.cloud.utils.Pair; +import com.cloud.vm.VirtualMachine; import com.vmware.vim25.FileInfo; import com.vmware.vim25.HostDatastoreBrowserSearchResults; import com.vmware.vim25.HostDatastoreBrowserSearchSpec; +import com.vmware.vim25.ParaVirtualSCSIController; +import com.vmware.vim25.VirtualAHCIController; +import com.vmware.vim25.VirtualCdrom; +import com.vmware.vim25.VirtualDisk; +import com.vmware.vim25.VirtualIDEController; +import com.vmware.vim25.VirtualLsiLogicController; +import com.vmware.vim25.VirtualNVMEController; +import org.apache.cloudstack.storage.DiskControllerMappingVO; import org.apache.cloudstack.storage.command.CopyCommand; import org.apache.cloudstack.storage.command.browser.ListDataStoreObjectsAnswer; import org.apache.cloudstack.storage.command.browser.ListDataStoreObjectsCommand; import org.apache.cloudstack.storage.to.PrimaryDataStoreTO; +import org.apache.cloudstack.utils.volume.VirtualMachineDiskInfo; import org.junit.After; +import org.junit.Assert; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; @@ -144,6 +159,8 @@ public VmwareHypervisorHost getHyperHost(VmwareContext context, Command cmd) { @Mock VirtualMachineTO vmSpec; @Mock + DiskTO diskTo; + @Mock VmwareHypervisorHost hyperHost; @Mock VirtualMachineMO vmMo; @@ -185,6 +202,8 @@ public VmwareHypervisorHost getHyperHost(VmwareContext context, Command cmd) { VimPortType vimService; @Mock HostCapability hostCapability; + @Mock + VirtualMachineDiskInfo virtualMachineDiskInfo; CopyCommand storageCmd; EnumMap params = new EnumMap(VmwareStorageProcessorConfigurableFields.class); @@ -232,6 +251,8 @@ public void setup() throws Exception { when(context.getService()).thenReturn(vimService); when(vimService.queryTargetCapabilities(envRef, hostRef)).thenReturn(hostCapability); when(hostCapability.isNestedHVSupported()).thenReturn(true); + + configureSupportedDiskControllersForTests(); } @After @@ -842,4 +863,359 @@ public void testExecuteWithEmptyPath() throws Exception { assertEquals(Collections.singletonList(1L), answer.getSizes()); assertEquals(Collections.singletonList(date.getTime()), answer.getLastModified()); } + + private void configureSupportedDiskControllersForTests() { + DiskControllerMappingVO osdefaultMapping = new DiskControllerMappingVO(); + osdefaultMapping.setName("osdefault"); + osdefaultMapping.setControllerReference("osdefault"); + + DiskControllerMappingVO ideMapping = new DiskControllerMappingVO(); + ideMapping.setName("ide"); + ideMapping.setControllerReference(VirtualIDEController.class.getName()); + ideMapping.setBusName("ide"); + ideMapping.setMaxDeviceCount(2); + ideMapping.setMaxControllerCount(2); + + DiskControllerMappingVO lsilogicMapping = new DiskControllerMappingVO(); + lsilogicMapping.setName("lsilogic"); + lsilogicMapping.setControllerReference(VirtualLsiLogicController.class.getName()); + lsilogicMapping.setBusName("scsi"); + lsilogicMapping.setMaxDeviceCount(16); + lsilogicMapping.setMaxControllerCount(4); + + DiskControllerMappingVO pvscsiMapping = new DiskControllerMappingVO(); + pvscsiMapping.setName("pvscsi"); + pvscsiMapping.setControllerReference(ParaVirtualSCSIController.class.getName()); + pvscsiMapping.setBusName("scsi"); + pvscsiMapping.setMaxDeviceCount(16); + pvscsiMapping.setMaxControllerCount(4); + pvscsiMapping.setMinHardwareVersion("7"); + + DiskControllerMappingVO sataMapping = new DiskControllerMappingVO(); + sataMapping.setName("sata"); + sataMapping.setControllerReference(VirtualAHCIController.class.getName()); + sataMapping.setBusName("sata"); + sataMapping.setMaxDeviceCount(30); + sataMapping.setMaxControllerCount(4); + sataMapping.setMinHardwareVersion("10"); + + DiskControllerMappingVO nvmeMapping = new DiskControllerMappingVO(); + nvmeMapping.setName("nvme"); + nvmeMapping.setControllerReference(VirtualNVMEController.class.getName()); + nvmeMapping.setBusName("nvme"); + nvmeMapping.setMaxDeviceCount(15); + nvmeMapping.setMaxControllerCount(4); + nvmeMapping.setMinHardwareVersion("13"); + + VmwareHelper.setSupportedDiskControllers(List.of(osdefaultMapping, ideMapping, lsilogicMapping, pvscsiMapping, sataMapping, nvmeMapping)); + } + + private Set getRequiredDiskControllersForValidateRequiredVirtualHardwareVersionForNewDiskControllersTests() { + DiskControllerMappingVO lsilogicMapping = VmwareHelper.getDiskControllerMapping("lsilogic", null); + DiskControllerMappingVO nvmeMapping = VmwareHelper.getDiskControllerMapping("nvme", null); + Set requiredDiskControllers = new HashSet<>(); + requiredDiskControllers.add(lsilogicMapping); + requiredDiskControllers.add(nvmeMapping); + return requiredDiskControllers; + } + + @Test + public void validateRequiredVirtualHardwareVersionForNewDiskControllersTestDoesNothingWhenControllerWithSupportedVirtualHardwareVersionIsRequired() throws Exception { + Set requiredDiskControllers = getRequiredDiskControllersForValidateRequiredVirtualHardwareVersionForNewDiskControllersTests(); + Mockito.when(vmMo.getVirtualHardwareVersion()).thenReturn(13); + + vmwareResource.validateRequiredVirtualHardwareVersionForNewDiskControllers(vmMo, requiredDiskControllers); + } + + @Test(expected = CloudRuntimeException.class) + public void validateRequiredVirtualHardwareVersionForNewDiskControllersTestThrowsExceptionWhenControllerWithGreaterVirtualHardwareVersionIsRequired() throws Exception { + Set requiredDiskControllers = getRequiredDiskControllersForValidateRequiredVirtualHardwareVersionForNewDiskControllersTests(); + Mockito.when(vmMo.getVirtualHardwareVersion()).thenReturn(8); + + vmwareResource.validateRequiredVirtualHardwareVersionForNewDiskControllers(vmMo, requiredDiskControllers); + } + + private void createDisksForValidateNewDiskControllersSupportExistingDisksTests(int numberOfDisks) throws Exception { + List virtualDisks = new ArrayList<>(); + for (int i = 0; i < numberOfDisks; i++) { + virtualDisks.add(new VirtualDisk()); + } + Mockito.when(vmMo.getVirtualDisks()).thenReturn(virtualDisks); + } + + @Test + public void validateNewDiskControllersSupportExistingDisksTestDoesNothingWhenBothControllersAreTheSameAndMaximumDevicesAreOnTheBus() throws Exception { + DiskControllerMappingVO ideMapping = VmwareHelper.getDiskControllerMapping("ide", null); + Pair controllerInfo = new Pair<>(ideMapping, ideMapping); + createDisksForValidateNewDiskControllersSupportExistingDisksTests(4); + + vmwareResource.validateNewDiskControllersSupportExistingDisks(vmMo, controllerInfo); + } + + @Test(expected = CloudRuntimeException.class) + public void validateNewDiskControllersSupportExistingDisksTestThrowsExceptionWhenBothControllersAreTheSameAndMoreThanMaximumDevicesAreOnTheBus() throws Exception { + DiskControllerMappingVO ideMapping = VmwareHelper.getDiskControllerMapping("ide", null); + Pair controllerInfo = new Pair<>(ideMapping, ideMapping); + Mockito.when(vmMo.getIsoDevice()).thenReturn(new VirtualCdrom()); + createDisksForValidateNewDiskControllersSupportExistingDisksTests(4); + + vmwareResource.validateNewDiskControllersSupportExistingDisks(vmMo, controllerInfo); + } + + @Test + public void validateNewDiskControllersSupportExistingDisksTestDoesNothingWhenControllersAreNotTheSameAndMaximumDevicesAreOnDataDiskBus() throws Exception { + DiskControllerMappingVO nvmeMapping = VmwareHelper.getDiskControllerMapping("nvme", null); + DiskControllerMappingVO ideMapping = VmwareHelper.getDiskControllerMapping("ide", null); + Pair controllerInfo = new Pair<>(nvmeMapping, ideMapping); + createDisksForValidateNewDiskControllersSupportExistingDisksTests(5); + + vmwareResource.validateNewDiskControllersSupportExistingDisks(vmMo, controllerInfo); + } + + @Test(expected = CloudRuntimeException.class) + public void validateNewDiskControllersSupportExistingDisksTestThrowsExceptionWhenBothControllersAreNotTheSameAndMoreThanMaximumDevicesAreOnDataDiskBus() throws Exception { + DiskControllerMappingVO nvmeMapping = VmwareHelper.getDiskControllerMapping("nvme", null); + DiskControllerMappingVO ideMapping = VmwareHelper.getDiskControllerMapping("ide", null); + Pair controllerInfo = new Pair<>(nvmeMapping, ideMapping); + Mockito.when(vmMo.getIsoDevice()).thenReturn(new VirtualCdrom()); + createDisksForValidateNewDiskControllersSupportExistingDisksTests(5); + + vmwareResource.validateNewDiskControllersSupportExistingDisks(vmMo, controllerInfo); + } + + @Test + public void getMaxSupportedDevicesInBusTestExpectedValueWhenBusIsNotScsi() { + DiskControllerMappingVO nvmeMapping = VmwareHelper.getDiskControllerMapping("nvme", null); + + int result = vmwareResource.getMaxSupportedDevicesInBus(nvmeMapping); + + Assert.assertEquals(nvmeMapping.getMaxControllerCount() * nvmeMapping.getMaxDeviceCount(), result); + } + + @Test + public void getMaxSupportedDevicesInBusTestExpectedValueWhenBusIsScsi() { + DiskControllerMappingVO lsilogicMapping = VmwareHelper.getDiskControllerMapping("lsilogic", null); + + int result = vmwareResource.getMaxSupportedDevicesInBus(lsilogicMapping); + + Assert.assertEquals(lsilogicMapping.getMaxControllerCount() * (lsilogicMapping.getMaxDeviceCount() - 1), result); + } + + @Test + public void teardownAllDiskControllersTestTearsdownAllControllersWithValidMappings() throws Exception { + vmwareResource.teardownAllDiskControllers(vmMo); + + int numberOfConfiguredDiskControllersToTeardown = VmwareHelper.getAllSupportedDiskControllerMappingsExceptOsDefault().size() - 1; + Mockito.verify(vmMo, Mockito.times(1)).tearDownDevices(Mockito.argThat((Class[] c) -> c.length == numberOfConfiguredDiskControllersToTeardown)); + } + + @Test + public void createDiskControllerUnitNumberMapTestInitializesValuesAsZeroWhenTemplateIsNotDeployAsIs() throws Exception { + DiskControllerMappingVO ideMapping = VmwareHelper.getDiskControllerMapping("ide", null); + DiskControllerMappingVO nvmeMapping = VmwareHelper.getDiskControllerMapping("nvme", null); + DiskControllerMappingVO lsilogicMapping = VmwareHelper.getDiskControllerMapping("lsilogic", null); + Pair controllerInfo = new Pair<>(nvmeMapping, lsilogicMapping); + + Map result = vmwareResource.createDiskControllerUnitNumberMap(vmMo, controllerInfo, false); + + Assert.assertEquals(0, (int) result.get(ideMapping.getControllerReference())); + Assert.assertEquals(0, (int) result.get(nvmeMapping.getControllerReference())); + Assert.assertEquals(0, (int) result.get(lsilogicMapping.getControllerReference())); + } + + @Test + public void createDiskControllerUnitNumberMapTestInitializesValuesAtNextAvailableUnitNumberWhenTemplateIsNotDeployAsIs() throws Exception { + DiskControllerMappingVO ideMapping = VmwareHelper.getDiskControllerMapping("ide", null); + DiskControllerMappingVO nvmeMapping = VmwareHelper.getDiskControllerMapping("nvme", null); + DiskControllerMappingVO lsilogicMapping = VmwareHelper.getDiskControllerMapping("lsilogic", null); + Mockito.doReturn(new Pair<>(100, 10)).when(vmMo).getNextAvailableControllerKeyAndDeviceNumberForType(ideMapping); + Mockito.doReturn(new Pair<>(200, 11)).when(vmMo).getNextAvailableControllerKeyAndDeviceNumberForType(nvmeMapping); + Mockito.doReturn(new Pair<>(300, 12)).when(vmMo).getNextAvailableControllerKeyAndDeviceNumberForType(lsilogicMapping); + Pair controllerInfo = new Pair<>(nvmeMapping, lsilogicMapping); + + Map result = vmwareResource.createDiskControllerUnitNumberMap(vmMo, controllerInfo, true); + + Assert.assertEquals(10, (int) result.get(ideMapping.getControllerReference())); + Assert.assertEquals(11, (int) result.get(nvmeMapping.getControllerReference())); + Assert.assertEquals(12, (int) result.get(lsilogicMapping.getControllerReference())); + } + + @Test + public void ensureDiskControllersInternalTestCallsEnsureDiskControllersWhenInstanceIsSystemVm() throws Exception { + Pair controllerInfo = new Pair<>(new DiskControllerMappingVO(), new DiskControllerMappingVO()); + Mockito.doNothing().when(vmwareResource).ensureDiskControllers(Mockito.any(), Mockito.any(), Mockito.anyBoolean()); + + vmwareResource.ensureDiskControllersInternal(vmMo, true, controllerInfo, true); + + Mockito.verify(vmwareResource, Mockito.times(1)).ensureDiskControllers(vmMo, controllerInfo, true); + } + + @Test + public void ensureDiskControllersInternalTestCallsEnsureDiskControllersWhenTemplateIsNotDeployAsIs() throws Exception { + Pair controllerInfo = new Pair<>(new DiskControllerMappingVO(), new DiskControllerMappingVO()); + Mockito.doNothing().when(vmwareResource).ensureDiskControllers(Mockito.any(), Mockito.any(), Mockito.anyBoolean()); + + vmwareResource.ensureDiskControllersInternal(vmMo, false, controllerInfo, false); + + Mockito.verify(vmwareResource, Mockito.times(1)).ensureDiskControllers(vmMo, controllerInfo, false); + } + + @Test + public void ensureDiskControllersInternalTestDoesNotCallEnsureDiskControllersWhenInstanceIsNotSystemVmAndTemplateIsDeployAsIs() throws Exception { + vmwareResource.ensureDiskControllersInternal(vmMo, false, new Pair<>(new DiskControllerMappingVO(), new DiskControllerMappingVO()), true); + + Mockito.verify(vmwareResource, Mockito.never()).ensureDiskControllers(Mockito.any(), Mockito.any(), Mockito.anyBoolean()); + } + + @Test + public void ensureDiskControllersTestVmIsConfiguredWhenRequiredDiskControllersDoNotExist() throws Exception { + boolean isSystemVm = false; + DiskControllerMappingVO osdefaultMapping = VmwareHelper.getDiskControllerMapping("osdefault", null); + Pair controllerInfo = new Pair<>(osdefaultMapping, osdefaultMapping); + + try (MockedStatic vmwareHelperMock = Mockito.mockStatic(VmwareHelper.class)) { + DiskControllerMappingVO nvmeMapping = VmwareHelper.getDiskControllerMapping("nvme", null); + Pair chosenDiskControllers = new Pair<>(nvmeMapping, nvmeMapping); + vmwareHelperMock.when(() -> VmwareHelper.chooseDiskControllers(controllerInfo, vmMo, null, null)).thenReturn(chosenDiskControllers); + Set requiredDiskControllers = new HashSet<>(); + requiredDiskControllers.add(nvmeMapping); + vmwareHelperMock.when(() -> VmwareHelper.getRequiredDiskControllers(chosenDiskControllers, isSystemVm)).thenReturn(requiredDiskControllers); + Mockito.doReturn(false).when(vmwareResource).vmHasRequiredControllers(vmMo, requiredDiskControllers); + Mockito.doNothing().when(vmwareResource).validateRequiredVirtualHardwareVersionForNewDiskControllers(vmMo, requiredDiskControllers); + Mockito.doNothing().when(vmwareResource).validateNewDiskControllersSupportExistingDisks(vmMo, chosenDiskControllers); + Mockito.doNothing().when(vmwareResource).teardownAllDiskControllers(vmMo); + Mockito.doReturn(true).when(vmMo).configureVm(Mockito.any(VirtualMachineConfigSpec.class)); + + vmwareResource.ensureDiskControllers(vmMo, controllerInfo, isSystemVm); + + Mockito.verify(vmwareResource, Mockito.times(1)).vmHasRequiredControllers(vmMo, requiredDiskControllers); + Mockito.verify(vmwareResource, Mockito.times(1)).validateRequiredVirtualHardwareVersionForNewDiskControllers(vmMo, requiredDiskControllers); + Mockito.verify(vmwareResource, Mockito.times(1)).validateNewDiskControllersSupportExistingDisks(vmMo, chosenDiskControllers); + Mockito.verify(vmwareResource, Mockito.times(1)).teardownAllDiskControllers(vmMo); + vmwareHelperMock.verify(() -> VmwareHelper.addDiskControllersToVmConfigSpec(Mockito.any(), Mockito.any(), Mockito.anyBoolean()), Mockito.times(1)); + Mockito.verify(vmMo, Mockito.times(1)).configureVm(Mockito.any(VirtualMachineConfigSpec.class)); + } + } + + @Test + public void ensureDiskControllersTestVmIsNotConfiguredWhenRequiredDiskControllersExist() throws Exception { + boolean isSystemVm = false; + DiskControllerMappingVO osdefaultMapping = VmwareHelper.getDiskControllerMapping("osdefault", null); + Pair controllerInfo = new Pair<>(osdefaultMapping, osdefaultMapping); + + try (MockedStatic vmwareHelperMock = Mockito.mockStatic(VmwareHelper.class)) { + DiskControllerMappingVO nvmeMapping = VmwareHelper.getDiskControllerMapping("nvme", null); + Pair chosenDiskControllers = new Pair<>(nvmeMapping, nvmeMapping); + vmwareHelperMock.when(() -> VmwareHelper.chooseDiskControllers(controllerInfo, vmMo, null, null)).thenReturn(chosenDiskControllers); + Set requiredDiskControllers = new HashSet<>(); + requiredDiskControllers.add(nvmeMapping); + vmwareHelperMock.when(() -> VmwareHelper.getRequiredDiskControllers(chosenDiskControllers, isSystemVm)).thenReturn(requiredDiskControllers); + Mockito.doReturn(true).when(vmwareResource).vmHasRequiredControllers(vmMo, requiredDiskControllers); + + vmwareResource.ensureDiskControllers(vmMo, controllerInfo, isSystemVm); + + Mockito.verify(vmwareResource, Mockito.times(1)).vmHasRequiredControllers(vmMo, requiredDiskControllers); + Mockito.verify(vmwareResource, Mockito.never()).validateRequiredVirtualHardwareVersionForNewDiskControllers(vmMo, requiredDiskControllers); + Mockito.verify(vmwareResource, Mockito.never()).validateNewDiskControllersSupportExistingDisks(vmMo, chosenDiskControllers); + Mockito.verify(vmwareResource, Mockito.never()).teardownAllDiskControllers(vmMo); + vmwareHelperMock.verify(() -> VmwareHelper.addDiskControllersToVmConfigSpec(Mockito.any(), Mockito.any(), Mockito.anyBoolean()), Mockito.never()); + Mockito.verify(vmMo, Mockito.never()).configureVm(Mockito.any(VirtualMachineConfigSpec.class)); + } + } + + @Test + public void vmHasRequiredControllersTestReturnsTrueWhenInstanceHasAllTheRequiredControllers() throws Exception { + DiskControllerMappingVO nvmeMapping = VmwareHelper.getDiskControllerMapping("nvme", null); + DiskControllerMappingVO lsilogicMapping = VmwareHelper.getDiskControllerMapping("lsilogic", null); + Set requiredControllers = new HashSet<>(); + requiredControllers.add(nvmeMapping); + requiredControllers.add(lsilogicMapping); + VirtualNVMEController firstExistingController = new VirtualNVMEController(); + VirtualLsiLogicController secondExistingController = new VirtualLsiLogicController(); + Mockito.when(vmMo.getControllers()).thenReturn(List.of(firstExistingController, secondExistingController)); + + boolean result = vmwareResource.vmHasRequiredControllers(vmMo, requiredControllers); + + Assert.assertTrue(result); + } + + @Test + public void vmHasRequiredControllersTestReturnsFalseWhenInstanceDoesNotHaveAllTheRequiredControllers() throws Exception { + DiskControllerMappingVO sataMapping = VmwareHelper.getDiskControllerMapping("sata", null); + DiskControllerMappingVO pvscsiMapping = VmwareHelper.getDiskControllerMapping("pvscsi", null); + Set requiredControllers = new HashSet<>(); + requiredControllers.add(sataMapping); + requiredControllers.add(pvscsiMapping); + VirtualAHCIController firstExistingController = new VirtualAHCIController(); + VirtualLsiLogicController secondExistingController = new VirtualLsiLogicController(); + Mockito.when(vmMo.getControllers()).thenReturn(List.of(firstExistingController, secondExistingController)); + + boolean result = vmwareResource.vmHasRequiredControllers(vmMo, requiredControllers); + + Assert.assertFalse(result); + } + + @Test + public void getControllerInfoFromVmSpecTestExpectedValues() { + String rootDiskControllerDetail = "nvme"; + String dataDiskControllerDetail = "sata"; + Map details = new HashMap<>(); + details.put(VmDetailConstants.ROOT_DISK_CONTROLLER, rootDiskControllerDetail); + details.put(VmDetailConstants.DATA_DISK_CONTROLLER, dataDiskControllerDetail); + Mockito.when(vmSpec.getDetails()).thenReturn(details); + Mockito.when(vmSpec.getType()).thenReturn(VirtualMachine.Type.SecondaryStorageVm); + + DiskControllerMappingVO expectedMapping = new DiskControllerMappingVO(); + expectedMapping.setControllerReference("expected"); + Pair expectedResult = new Pair<>(expectedMapping, expectedMapping); + + try (MockedStatic vmwareHelperMock = Mockito.mockStatic(VmwareHelper.class)) { + vmwareHelperMock.when(() -> VmwareHelper.getDiskControllersFromVmSettings(rootDiskControllerDetail, dataDiskControllerDetail, true)).thenReturn(expectedResult); + + Pair result = vmwareResource.getControllerInfoFromVmSpec(vmSpec); + + Assert.assertEquals(expectedResult, result); + } + } + + @Test + public void getControllerForDiskTestChoosesControllerFromControllerInfoWhenTemplateIsNotDeployAsIs() throws Exception { + Pair controllerInfo = new Pair<>(new DiskControllerMappingVO(), new DiskControllerMappingVO()); + + try (MockedStatic vmwareHelperMock = Mockito.mockStatic(VmwareHelper.class)) { + DiskControllerMappingVO expectedResult = new DiskControllerMappingVO(); + expectedResult.setControllerReference("expected"); + vmwareHelperMock.when(() -> VmwareHelper.getControllerBasedOnDiskType(controllerInfo, diskTo)).thenReturn(expectedResult); + + DiskControllerMappingVO result = vmwareResource.getControllerForDisk(vmMo, null, diskTo, controllerInfo, false); + + Assert.assertEquals(expectedResult, result); + } + } + + @Test + public void getControllerForDiskTestChoosesControllerFromCurrentBusNameWhenTemplateIsDeployAsIsAndCurrentBusNameIsValid() throws Exception { + Mockito.when(virtualMachineDiskInfo.getDiskDeviceBusName()).thenReturn("nvme1:3"); + Mockito.when(vmMo.getMappingsForExistingDiskControllers()).thenReturn(new HashSet<>(VmwareHelper.getAllSupportedDiskControllerMappingsExceptOsDefault())); + Pair controllerInfo = new Pair<>(new DiskControllerMappingVO(), new DiskControllerMappingVO()); + + DiskControllerMappingVO result = vmwareResource.getControllerForDisk(vmMo, virtualMachineDiskInfo, diskTo, controllerInfo, true); + + DiskControllerMappingVO nvmeMapping = VmwareHelper.getDiskControllerMapping("nvme", null); + Assert.assertEquals(nvmeMapping, result); + } + + @Test + public void getControllerForDiskTestChoosesAnyControllerWhenTemplateIsDeployAsIsAndBusNameIsInvalid() throws Exception { + Mockito.when(virtualMachineDiskInfo.getDiskDeviceBusName()).thenReturn("unmapped1:3"); + Mockito.when(vmMo.getMappingsForExistingDiskControllers()).thenReturn(new HashSet<>(VmwareHelper.getAllSupportedDiskControllerMappingsExceptOsDefault())); + Pair controllerInfo = new Pair<>(new DiskControllerMappingVO(), new DiskControllerMappingVO()); + DiskControllerMappingVO expectedResult = new DiskControllerMappingVO(); + expectedResult.setControllerReference("expected"); + Mockito.when(vmMo.getAnyExistingAvailableDiskController()).thenReturn(expectedResult); + + DiskControllerMappingVO result = vmwareResource.getControllerForDisk(vmMo, virtualMachineDiskInfo, diskTo, controllerInfo, true); + + Assert.assertEquals(expectedResult, result); + } } diff --git a/server/src/main/java/com/cloud/api/query/QueryManagerImpl.java b/server/src/main/java/com/cloud/api/query/QueryManagerImpl.java index 846fc1921992..93b448d69818 100644 --- a/server/src/main/java/com/cloud/api/query/QueryManagerImpl.java +++ b/server/src/main/java/com/cloud/api/query/QueryManagerImpl.java @@ -153,6 +153,8 @@ import org.apache.cloudstack.secstorage.HeuristicVO; import org.apache.cloudstack.secstorage.dao.SecondaryStorageHeuristicDao; import org.apache.cloudstack.secstorage.heuristics.Heuristic; +import org.apache.cloudstack.storage.DiskControllerMappingVO; +import org.apache.cloudstack.storage.dao.DiskControllerMappingDao; import org.apache.cloudstack.storage.datastore.db.ObjectStoreDao; import org.apache.cloudstack.storage.datastore.db.ObjectStoreVO; import org.apache.cloudstack.storage.datastore.db.PrimaryDataStoreDao; @@ -614,6 +616,9 @@ public class QueryManagerImpl extends MutualExclusiveIdsManagerBase implements Q @Inject ManagementServerHostPeerJoinDao mshostPeerJoinDao; + @Inject + private DiskControllerMappingDao diskControllerMappingDao; + @Inject private AsyncJobManager jobManager; @@ -5084,8 +5089,9 @@ private void fillVMOrTemplateDetailOptions(final Map> optio if (HypervisorType.VMware.equals(hypervisorType)) { options.put(VmDetailConstants.NIC_ADAPTER, Arrays.asList("E1000", "PCNet32", "Vmxnet2", "Vmxnet3")); - options.put(VmDetailConstants.ROOT_DISK_CONTROLLER, Arrays.asList("osdefault", "ide", "scsi", "lsilogic", "lsisas1068", "buslogic", "pvscsi")); - options.put(VmDetailConstants.DATA_DISK_CONTROLLER, Arrays.asList("osdefault", "ide", "scsi", "lsilogic", "lsisas1068", "buslogic", "pvscsi")); + List availableDiskControllers = diskControllerMappingDao.listForHypervisor(HypervisorType.VMware).stream().map(DiskControllerMappingVO::getName).collect(Collectors.toList()); + options.put(VmDetailConstants.ROOT_DISK_CONTROLLER, availableDiskControllers); + options.put(VmDetailConstants.DATA_DISK_CONTROLLER, availableDiskControllers); options.put(VmDetailConstants.NESTED_VIRTUALIZATION_FLAG, Arrays.asList("true", "false")); options.put(VmDetailConstants.SVGA_VRAM_SIZE, Collections.emptyList()); options.put(VmDetailConstants.RAM_RESERVATION, Collections.emptyList()); diff --git a/services/secondary-storage/controller/src/main/java/org/apache/cloudstack/secondarystorage/SecondaryStorageManagerImpl.java b/services/secondary-storage/controller/src/main/java/org/apache/cloudstack/secondarystorage/SecondaryStorageManagerImpl.java index d8e44203187e..d1fcc6b55b3d 100644 --- a/services/secondary-storage/controller/src/main/java/org/apache/cloudstack/secondarystorage/SecondaryStorageManagerImpl.java +++ b/services/secondary-storage/controller/src/main/java/org/apache/cloudstack/secondarystorage/SecondaryStorageManagerImpl.java @@ -45,6 +45,8 @@ import org.apache.cloudstack.framework.config.Configurable; import org.apache.cloudstack.framework.config.dao.ConfigurationDao; import org.apache.cloudstack.framework.security.keystore.KeystoreManager; +import org.apache.cloudstack.storage.DiskControllerMappingVO; +import org.apache.cloudstack.storage.dao.DiskControllerMappingDao; import org.apache.cloudstack.storage.datastore.db.ImageStoreDao; import org.apache.cloudstack.storage.datastore.db.ImageStoreVO; import org.apache.cloudstack.storage.datastore.db.TemplateDataStoreDao; @@ -254,6 +256,8 @@ public class SecondaryStorageManagerImpl extends ManagerBase implements Secondar private IndirectAgentLB indirectAgentLB; @Inject private CAManager caManager; + @Inject + private DiskControllerMappingDao diskControllerMappingDao; private int _secStorageVmMtuSize; private String _instance; @@ -379,6 +383,12 @@ public boolean generateVMSetupCommand(Long ssAHostId) { setupCmd.setCopyPassword(copyPasswd); setupCmd.setCopyUserName(TemplateConstants.DEFAULT_HTTP_AUTH_USER); + List mappingsInDatabase = diskControllerMappingDao.listForHypervisor(HypervisorType.VMware); + if (!CollectionUtils.isEmpty(mappingsInDatabase)) { + setupCmd.setSupportedDiskControllers(mappingsInDatabase); + setupCmd.setContextParam("hypervisor", HypervisorType.VMware.toString()); + } + Answer answer = _agentMgr.easySend(ssAHostId, setupCmd); if (answer != null && answer.getResult()) { if (logger.isDebugEnabled()) { diff --git a/vmware-base/pom.xml b/vmware-base/pom.xml index 0cf3dbf78dcb..fda4d25b6bf0 100644 --- a/vmware-base/pom.xml +++ b/vmware-base/pom.xml @@ -47,6 +47,11 @@ cloud-engine-api ${project.version} + + org.apache.cloudstack + cloud-engine-schema + ${project.version} + com.google.code.gson gson diff --git a/vmware-base/src/main/java/com/cloud/hypervisor/vmware/mo/BaseMO.java b/vmware-base/src/main/java/com/cloud/hypervisor/vmware/mo/BaseMO.java index c0e7f29375fc..047f426546e1 100644 --- a/vmware-base/src/main/java/com/cloud/hypervisor/vmware/mo/BaseMO.java +++ b/vmware-base/src/main/java/com/cloud/hypervisor/vmware/mo/BaseMO.java @@ -45,6 +45,9 @@ public class BaseMO { private String _name; + protected BaseMO() { + } + public BaseMO(VmwareContext context, ManagedObjectReference mor) { assert (context != null); diff --git a/vmware-base/src/main/java/com/cloud/hypervisor/vmware/mo/ClusterMO.java b/vmware-base/src/main/java/com/cloud/hypervisor/vmware/mo/ClusterMO.java index 9198f310b656..2b742a23dbed 100644 --- a/vmware-base/src/main/java/com/cloud/hypervisor/vmware/mo/ClusterMO.java +++ b/vmware-base/src/main/java/com/cloud/hypervisor/vmware/mo/ClusterMO.java @@ -22,6 +22,7 @@ import java.util.HashMap; import java.util.List; +import org.apache.cloudstack.storage.DiskControllerMappingVO; import org.apache.commons.collections.CollectionUtils; import org.apache.commons.lang3.StringUtils; @@ -403,7 +404,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, Pair controllerInfo, Boolean systemVm) throws Exception { + int memoryReserveMB, String guestOsIdentifier, ManagedObjectReference morDs, boolean snapshotDirToParent, Pair controllerInfo, Boolean systemVm) throws Exception { if (logger.isTraceEnabled()) logger.trace("vCenter API trace - createBlankVm(). target MOR: " + _mor.getValue() + ", vmName: " + vmName + ", cpuCount: " + cpuCount + ", cpuSpeedMhz: " + diff --git a/vmware-base/src/main/java/com/cloud/hypervisor/vmware/mo/DiskControllerType.java b/vmware-base/src/main/java/com/cloud/hypervisor/vmware/mo/DiskControllerType.java index 2ee1e48aaebb..11653849a527 100644 --- a/vmware-base/src/main/java/com/cloud/hypervisor/vmware/mo/DiskControllerType.java +++ b/vmware-base/src/main/java/com/cloud/hypervisor/vmware/mo/DiskControllerType.java @@ -17,36 +17,12 @@ package com.cloud.hypervisor.vmware.mo; public enum DiskControllerType { + // These enums are used only for default values and validations. + // To add support for a new disk controller, create a new disk controller mapping instead. + osdefault, 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_1068) - || 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) - || diskController.equalsIgnoreCase(ScsiDiskControllerType.VIRTUAL_SCSI)) { - 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/main/java/com/cloud/hypervisor/vmware/mo/HostMO.java b/vmware-base/src/main/java/com/cloud/hypervisor/vmware/mo/HostMO.java index e710adc3b769..0870691523a0 100644 --- a/vmware-base/src/main/java/com/cloud/hypervisor/vmware/mo/HostMO.java +++ b/vmware-base/src/main/java/com/cloud/hypervisor/vmware/mo/HostMO.java @@ -25,15 +25,14 @@ import java.util.Map; import java.util.regex.Pattern; -import org.apache.cloudstack.vm.UnmanagedInstanceTO; - import com.cloud.hypervisor.vmware.util.VmwareClientException; import com.cloud.hypervisor.vmware.util.VmwareContext; import com.cloud.hypervisor.vmware.util.VmwareHelper; import com.cloud.utils.LogUtils; import com.cloud.utils.Pair; import com.cloud.utils.StringUtils; - +import org.apache.cloudstack.storage.DiskControllerMappingVO; +import org.apache.cloudstack.vm.UnmanagedInstanceTO; import org.apache.commons.collections.CollectionUtils; import com.google.gson.Gson; @@ -807,7 +806,7 @@ public List> getLocalDatastoreOnHost() thro ObjectContent[] ocs = getDatastorePropertiesOnHyperHost(new String[] {"name", "summary"}); if (ocs != null) { for (ObjectContent oc : ocs) { - DatastoreSummary dsSummary = (DatastoreSummary)VmwareHelper.getPropValue(oc, "summary"); + DatastoreSummary dsSummary = (DatastoreSummary) VmwareHelper.getPropValue(oc, "summary"); if (dsSummary.isMultipleHostAccess() == false && dsSummary.isAccessible() && dsSummary.getType().equalsIgnoreCase("vmfs")) { ManagedObjectReference morDs = oc.getObj(); String name = (String)VmwareHelper.getPropValue(oc, "name"); @@ -847,7 +846,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, Pair controllerInfo, Boolean systemVm) throws Exception { + int memoryReserveMB, String guestOsIdentifier, ManagedObjectReference morDs, boolean snapshotDirToParent, Pair controllerInfo, Boolean systemVm) throws Exception { if (logger.isTraceEnabled()) logger.trace("vCenter API trace - createBlankVm(). target MOR: " + _mor.getValue() + ", vmName: " + vmName + ", cpuCount: " + cpuCount + ", cpuSpeedMhz: " + diff --git a/vmware-base/src/main/java/com/cloud/hypervisor/vmware/mo/HypervisorHostHelper.java b/vmware-base/src/main/java/com/cloud/hypervisor/vmware/mo/HypervisorHostHelper.java index fb5857c58a8b..6ca1c4159f19 100644 --- a/vmware-base/src/main/java/com/cloud/hypervisor/vmware/mo/HypervisorHostHelper.java +++ b/vmware-base/src/main/java/com/cloud/hypervisor/vmware/mo/HypervisorHostHelper.java @@ -28,6 +28,7 @@ import java.util.HashMap; import java.util.List; import java.util.Map; +import java.util.Set; import java.util.Objects; import java.util.UUID; @@ -40,6 +41,7 @@ import javax.xml.transform.stream.StreamResult; import org.apache.cloudstack.engine.orchestration.service.NetworkOrchestrationService; +import org.apache.cloudstack.storage.DiskControllerMappingVO; import org.apache.cloudstack.utils.security.ParserUtils; import org.apache.commons.collections.MapUtils; import org.apache.commons.lang3.StringUtils; @@ -118,7 +120,6 @@ import com.vmware.vim25.OvfCreateImportSpecResult; import com.vmware.vim25.OvfFile; import com.vmware.vim25.OvfFileItem; -import com.vmware.vim25.ParaVirtualSCSIController; import com.vmware.vim25.RuntimeFaultFaultMsg; import com.vmware.vim25.TaskInProgressFaultMsg; import com.vmware.vim25.VMwareDVSConfigSpec; @@ -126,15 +127,12 @@ 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.VirtualDevice; import com.vmware.vim25.VirtualDeviceConfigSpec; import com.vmware.vim25.VirtualDeviceConfigSpecOperation; import com.vmware.vim25.VirtualDisk; 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; @@ -1560,12 +1558,11 @@ 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, - Pair controllerInfo, Boolean systemVm) throws Exception { + Pair controllerInfo, Boolean systemVm) throws Exception { if (LOGGER.isInfoEnabled()) 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); @@ -1574,23 +1571,9 @@ public static boolean createBlankVm(VmwareHypervisorHost host, String vmName, St VmwareHelper.setBasicVmConfig(vmConfig, cpuCount, cpuSpeedMHz, cpuReservedMHz, memoryMB, memoryReserveMB, guestOsIdentifier, limitCpuUse, false); - Pair chosenDiskControllers = VmwareHelper.chooseRequiredDiskControllers(controllerInfo, null, host, guestOsIdentifier); - String scsiDiskController = HypervisorHostHelper.getScsiController(chosenDiskControllers); - // 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++; - } - } + Pair chosenDiskControllers = VmwareHelper.chooseDiskControllers(controllerInfo, null, host, guestOsIdentifier); + Set requiredDiskControllers = VmwareHelper.getRequiredDiskControllers(chosenDiskControllers, systemVm); + VmwareHelper.addDiskControllersToVmConfigSpec(vmConfig, requiredDiskControllers, systemVm); if (guestOsIdentifier.startsWith("darwin")) { //Mac OS LOGGER.debug("Add USB Controller device for blank Mac OS VM " + vmName); @@ -1689,34 +1672,6 @@ public static String getNewVMHardwareVersion(ClusterMO clusterMO, DatacenterMO d return version; } - 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, String vmxFormattedHardwareVersion) throws Exception { // Allow worker VM to float within cluster so that we will have better chance to @@ -2249,35 +2204,6 @@ public static ManagedObjectReference getHypervisorHostMorFromGuid(String guid) { return morHyperHost; } - public static String getScsiController(Pair controllerInfo) { - String rootDiskController = controllerInfo.first(); - String dataDiskController = controllerInfo.second(); - - String scsiDiskController; //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; - } - public static void createBaseFolder(DatastoreMO dsMo, VmwareHypervisorHost hyperHost, StoragePoolType poolType) throws Exception { if (poolType != null && poolType == StoragePoolType.DatastoreCluster) { StoragepodMO storagepodMO = new StoragepodMO(hyperHost.getContext(), dsMo.getMor()); diff --git a/vmware-base/src/main/java/com/cloud/hypervisor/vmware/mo/VirtualMachineMO.java b/vmware-base/src/main/java/com/cloud/hypervisor/vmware/mo/VirtualMachineMO.java index 169959fdb91d..b87ca4cb7bee 100644 --- a/vmware-base/src/main/java/com/cloud/hypervisor/vmware/mo/VirtualMachineMO.java +++ b/vmware-base/src/main/java/com/cloud/hypervisor/vmware/mo/VirtualMachineMO.java @@ -32,7 +32,9 @@ import java.util.Arrays; import java.util.Collections; import java.util.Comparator; +import java.util.HashSet; import java.util.List; +import java.util.Set; import java.util.concurrent.CompletableFuture; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; @@ -42,6 +44,8 @@ import com.cloud.storage.Storage; import com.cloud.utils.exception.CloudRuntimeException; +import com.vmware.vim25.VirtualController; +import org.apache.cloudstack.storage.DiskControllerMappingVO; import org.apache.cloudstack.utils.reflectiontostringbuilderutils.ReflectionToStringBuilderUtils; import org.apache.commons.collections.CollectionUtils; import org.apache.commons.lang3.StringUtils; @@ -77,14 +81,12 @@ 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.RuntimeFaultFaultMsg; import com.vmware.vim25.TaskInfo; import com.vmware.vim25.TaskInfoState; 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; @@ -107,7 +109,6 @@ 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; @@ -148,6 +149,10 @@ public void setInternalCSName(String internalVMName) { this.internalCSName = internalVMName; } + protected VirtualMachineMO() { + super(); + } + public VirtualMachineMO(VmwareContext context, ManagedObjectReference morVm) { super(context, morVm); } @@ -1187,13 +1192,11 @@ public void createDisk(String vmdkDatastorePath, VirtualDiskType diskType, Virtu _context.waitForTaskProgressDone(morTask); } - public void updateVmdkAdapter(String vmdkFileName, String diskController) throws Exception { - - DiskControllerType diskControllerType = DiskControllerType.getType(diskController); - VmdkAdapterType vmdkAdapterType = VmdkAdapterType.getAdapterType(diskControllerType); + public void updateVmdkAdapter(String vmdkFileName, DiskControllerMappingVO diskController) throws Exception { + VmdkAdapterType vmdkAdapterType = VmdkAdapterType.getType(diskController.getVmdkAdapterType()); if (vmdkAdapterType == VmdkAdapterType.none) { String message = "Failed to attach disk due to invalid vmdk adapter type for vmdk file [" + - vmdkFileName + "] with controller : " + diskControllerType; + vmdkFileName + "] with controller : " + diskController.getName(); logger.debug(message); throw new Exception(message); } @@ -1247,45 +1250,40 @@ public void attachDisk(String[] vmdkDatastorePathChain, ManagedObjectReference m attachDisk(vmdkDatastorePathChain, morDs, null, null); } - public void attachDisk(String[] vmdkDatastorePathChain, ManagedObjectReference morDs, String diskController, String vSphereStoragePolicyId) throws Exception { + public void attachDisk(String[] vmdkDatastorePathChain, ManagedObjectReference morDs, DiskControllerMappingVO diskController, String vSphereStoragePolicyId) throws Exception { attachDisk(vmdkDatastorePathChain, morDs, diskController, vSphereStoragePolicyId, null); } - public void attachDisk(String[] vmdkDatastorePathChain, ManagedObjectReference morDs, String diskController, String vSphereStoragePolicyId, Long maxIops) throws Exception { - if(logger.isTraceEnabled()) - logger.trace("vCenter API trace - attachDisk(). target MOR: " + _mor.getValue() + ", vmdkDatastorePath: " - + GSON.toJson(vmdkDatastorePathChain) + ", datastore: " + morDs.getValue()); - int controllerKey; - int unitNumber; + public void attachDisk(String[] vmdkDatastorePathChain, ManagedObjectReference morDs, DiskControllerMappingVO diskController, String vSphereStoragePolicyId, Long maxIops) throws Exception { + logger.trace("vCenter API trace - attachDisk(). target MOR: {}, vmdkDatastorePath: {}, datastore: {}", _mor.getValue(), + GSON.toJson(vmdkDatastorePathChain), morDs.getValue()); + + if (diskController == null) { + logger.debug("Provided disk controller is null; therefore, we will choose any existing disk controller to use."); + diskController = getAnyExistingAvailableDiskController(); + } - if (DiskControllerType.getType(diskController) == DiskControllerType.ide) { + if (VirtualIDEController.class.getName().equals(diskController.getControllerReference())) { // 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 { - if (StringUtils.isNotBlank(diskController)) { - controllerKey = getScsiDiskControllerKey(diskController); - } else { - controllerKey = getScsiDeviceControllerKey(); - } - unitNumber = -1; + } + + Pair controllerKeyAndUnitNumber = getNextAvailableControllerKeyAndDeviceNumberForType(diskController); + if (controllerKeyAndUnitNumber == null) { + throw new CloudRuntimeException(String.format("Unable to find an available disk controller of the required type: [%s]. " + + "If the disk controller being used has been changed, please restart your virtual machine so that CloudStack creates the required controllers. " + + "Otherwise, the maximum number of disks for the bus has been reached.", + diskController.getName())); } synchronized (_mor.getValue().intern()) { - VirtualDevice newDisk = VmwareHelper.prepareDiskDevice(this, null, controllerKey, vmdkDatastorePathChain, morDs, unitNumber, 1, maxIops); - if (StringUtils.isNotBlank(diskController)) { - String vmdkFileName = vmdkDatastorePathChain[0]; - updateVmdkAdapter(vmdkFileName, diskController); - } + VirtualDevice newDisk = VmwareHelper.prepareDiskDevice(this, null, controllerKeyAndUnitNumber.first(), + vmdkDatastorePathChain, morDs, controllerKeyAndUnitNumber.second(), 1, maxIops); + String vmdkFileName = vmdkDatastorePathChain[0]; + updateVmdkAdapter(vmdkFileName, diskController); + VirtualMachineConfigSpec reConfigSpec = new VirtualMachineConfigSpec(); VirtualDeviceConfigSpec deviceConfigSpec = new VirtualDeviceConfigSpec(); @@ -2053,66 +2051,6 @@ public void moveAllVmDiskFiles(DatastoreMO destDsMo, String destDsDir, boolean f } } - protected VirtualSCSIController getScsiController(DiskControllerType type) { - switch (type) { - case pvscsi: - return new ParaVirtualSCSIController(); - case lsisas1068: - return new VirtualLsiLogicSASController(); - case buslogic: - return new VirtualBusLogicController(); - default: - return new VirtualLsiLogicController(); - } - } - - public void addScsiDeviceControllers(DiskControllerType type) throws Exception { - VirtualMachineConfigSpec vmConfig = new VirtualMachineConfigSpec(); - int busNum = 0; - while (busNum < VmwareHelper.MAX_SCSI_CONTROLLER_COUNT) { - VirtualSCSIController scsiController = getScsiController(type); - 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)) { - logger.info("Successfully added SCSI controllers."); - } else { - throw new Exception("Unable to add Scsi controllers to the VM " + getName()); - } - } - - 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 { - logger.info("Successfully added " + requiredNumScsiControllers + " SCSI controllers."); - } - } - public String getRecommendedDiskController(String guestOsId) throws Exception { String recommendedController; GuestOsDescriptor guestOsDescriptor = getGuestOsDescriptor(guestOsId); @@ -2120,98 +2058,6 @@ public String getRecommendedDiskController(String guestOsId) throws Exception { 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) { - 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 = _context.getVimClient().getDynamicProperty(_mor, "config.hardware.device"); - - if (CollectionUtils.isNotEmpty(devices)) { - DiskControllerType diskControllerType = DiskControllerType.getType(diskController); - for (VirtualDevice device : devices) { - if ((diskControllerType == DiskControllerType.lsilogic || diskControllerType == DiskControllerType.scsi) - && device instanceof VirtualLsiLogicController && isValidScsiDiskController((VirtualLsiLogicController)device)) { - return device.getKey(); - } else if ((diskControllerType == DiskControllerType.lsisas1068 || diskControllerType == DiskControllerType.scsi) - && device instanceof VirtualLsiLogicSASController && isValidScsiDiskController((VirtualLsiLogicSASController)device)) { - return device.getKey(); - } else if ((diskControllerType == DiskControllerType.pvscsi || diskControllerType == DiskControllerType.scsi) - && device instanceof ParaVirtualSCSIController && isValidScsiDiskController((ParaVirtualSCSIController)device)) { - return device.getKey(); - } else if ((diskControllerType == DiskControllerType.buslogic || diskControllerType == DiskControllerType.scsi) - && device instanceof VirtualBusLogicController && isValidScsiDiskController((VirtualBusLogicController)device)) { - return device.getKey(); - } - } - } - - assert (false); - throw new IllegalStateException("Scsi disk controller of type " + diskController + " not found among configured devices."); - } - - public int getScsiDiskControllerKeyNoException(String diskController, int scsiUnitNumber) throws Exception { - List devices = _context.getVimClient().getDynamicProperty(_mor, "config.hardware.device"); - - if (CollectionUtils.isNotEmpty(devices) && scsiUnitNumber >= 0) { - int requiredScsiController = scsiUnitNumber / VmwareHelper.MAX_ALLOWED_DEVICES_SCSI_CONTROLLER; - int scsiControllerDeviceCount = 0; - DiskControllerType diskControllerType = DiskControllerType.getType(diskController); - for (VirtualDevice device : devices) { - if ((diskControllerType == DiskControllerType.lsilogic || diskControllerType == DiskControllerType.scsi) && device instanceof VirtualLsiLogicController) { - if (scsiControllerDeviceCount == requiredScsiController) { - if (isValidScsiDiskController((VirtualLsiLogicController)device)) { - return device.getKey(); - } - break; - } - scsiControllerDeviceCount++; - } else if ((diskControllerType == DiskControllerType.lsisas1068 || diskControllerType == DiskControllerType.scsi) && device instanceof VirtualLsiLogicSASController) { - if (scsiControllerDeviceCount == requiredScsiController) { - if (isValidScsiDiskController((VirtualLsiLogicSASController)device)) { - return device.getKey(); - } - break; - } - scsiControllerDeviceCount++; - } else if ((diskControllerType == DiskControllerType.pvscsi || diskControllerType == DiskControllerType.scsi) && device instanceof ParaVirtualSCSIController) { - if (scsiControllerDeviceCount == requiredScsiController) { - if (isValidScsiDiskController((ParaVirtualSCSIController)device)) { - return device.getKey(); - } - break; - } - scsiControllerDeviceCount++; - } else if ((diskControllerType == DiskControllerType.buslogic || diskControllerType == DiskControllerType.scsi) && device instanceof VirtualBusLogicController) { - if (scsiControllerDeviceCount == requiredScsiController) { - if (isValidScsiDiskController((VirtualBusLogicController)device)) { - return device.getKey(); - } - break; - } - scsiControllerDeviceCount++; - } - } - } - return -1; - } - - public int getNextScsiDiskDeviceNumber() throws Exception { - int scsiControllerKey = getScsiDeviceControllerKey(); - return getNextDeviceNumber(scsiControllerKey); - } - public int getScsiDeviceControllerKey() throws Exception { List devices = _context.getVimClient().getDynamicProperty(_mor, "config.hardware.device"); @@ -2241,47 +2087,6 @@ public int getScsiDeviceControllerKeyNoException() throws Exception { return -1; } - public void ensureLsiLogicDeviceControllers(int count, int availableBusNum) throws Exception { - int scsiControllerKey = getLsiLogicDeviceControllerKeyNoException(); - 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 Lsi Logic controllers to the VM " + getName()); - } else { - logger.info("Successfully added " + count + " LsiLogic Parallel SCSI controllers."); - } - } - } - - private int getLsiLogicDeviceControllerKeyNoException() throws Exception { - List devices = _context.getVimClient(). - getDynamicProperty(_mor, "config.hardware.device"); - - if (CollectionUtils.isNotEmpty(devices)) { - for (VirtualDevice device : devices) { - if (device instanceof VirtualLsiLogicController) { - return device.getKey(); - } - } - } - - return -1; - } - public void ensureScsiDeviceController() throws Exception { int scsiControllerKey = getScsiDeviceControllerKeyNoException(); if (scsiControllerKey < 0) { @@ -2596,19 +2401,24 @@ public List> getDiskDatastorePathChain(Virt return pathList; } + /** + * Returns a virtual disk's numbering. For example, if the disk uses the second SCSI controller's third device node, + * then "scsi1:2" will be returned. + * @param allDevices a list containing all the instance's devices. + * @param theDevice the virtual disk. + * @throws Exception if the disk's controller is not found in allDevices or its type does not have an + * equivalent mapping. + */ public String getDeviceBusName(List allDevices, VirtualDevice theDevice) throws Exception { for (VirtualDevice device : allDevices) { - if (device.getKey() == theDevice.getControllerKey().intValue()) { - if (device instanceof VirtualIDEController) { - return String.format("ide%d:%d", ((VirtualIDEController)device).getBusNumber(), theDevice.getUnitNumber()); - } else if (device instanceof VirtualSCSIController) { - return String.format("scsi%d:%d", ((VirtualSCSIController)device).getBusNumber(), theDevice.getUnitNumber()); - } else { - throw new Exception("Device controller is not supported yet"); - } + if (device.getKey() != theDevice.getControllerKey()) { + continue; } + DiskControllerMappingVO mapping = VmwareHelper.getDiskControllerMapping(null, device.getClass().getName()); + return String.format("%s%d:%d", mapping.getBusName(), ((VirtualController) device).getBusNumber(), + theDevice.getUnitNumber()); } - throw new Exception("Unable to find device controller"); + throw new Exception("Unable to find device controller."); } public List getVirtualDisks() throws Exception { @@ -2699,19 +2509,6 @@ public VirtualDisk[] getAllDiskDevice() throws Exception { return deviceList.toArray(new VirtualDisk[0]); } - public VirtualDisk getDiskDeviceByBusName(List allDevices, String busName) throws Exception { - for (VirtualDevice device : allDevices) { - if (device instanceof VirtualDisk) { - VirtualDisk disk = (VirtualDisk)device; - String diskBusName = getDeviceBusName(allDevices, disk); - if (busName.equalsIgnoreCase(diskBusName)) - return disk; - } - } - - return null; - } - public VirtualDisk[] getAllIndependentDiskDevice() throws Exception { List independentDisks = new ArrayList<>(); VirtualDisk[] allDisks = getAllDiskDevice(); @@ -2766,70 +2563,6 @@ public int getIDEDeviceControllerKey() throws Exception { throw new Exception("IDE Controller Not Found"); } - public int getIDEControllerKey(int ideUnitNumber) throws Exception { - List devices = _context.getVimClient(). - getDynamicProperty(_mor, "config.hardware.device"); - - int requiredIdeController = ideUnitNumber / VmwareHelper.MAX_IDE_CONTROLLER_COUNT; - - int ideControllerCount = 0; - if(devices != null && !devices.isEmpty()) { - for(VirtualDevice device : devices) { - if(device instanceof VirtualIDEController) { - if (ideControllerCount == requiredIdeController) { - return device.getKey(); - } - ideControllerCount++; - } - } - } - - assert(false); - throw new Exception("IDE Controller Not Found"); - } - - public int getNumberOfIDEDevices() throws Exception { - int ideDeviceCount = 0; - List devices = _context.getVimClient(). - getDynamicProperty(_mor, "config.hardware.device"); - - if (devices != null && !devices.isEmpty()) { - 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 = _context.getVimClient(). - getDynamicProperty(_mor, "config.hardware.device"); - - int deviceCount = 0; - int ideDeviceUnitNumber = -1; - if (devices != null) { - for (VirtualDevice device : devices) { - if (device.getControllerKey() == null || device.getControllerKey() != controllerKey) { - continue; - } - if (device instanceof VirtualDisk || device instanceof VirtualCdrom) { - 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); @@ -3239,135 +2972,6 @@ 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 = _context.getVimClient(). - getDynamicProperty(_mor, "config.hardware.device"); - - if (devices != null && !devices.isEmpty()) { - 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 { - logger.info("Successfully added " + count + " SCSI BusLogic controllers."); - } - } - } - - private int getBusLogicDeviceControllerKeyNoException() throws Exception { - List devices = _context.getVimClient(). - getDynamicProperty(_mor, "config.hardware.device"); - - if (devices != null && !devices.isEmpty()) { - for (VirtualDevice device : devices) { - if (device instanceof VirtualBusLogicController) { - return device.getKey(); - } - } - } - - return -1; - } - - public Ternary getScsiControllerInfo() throws Exception { - List devices = _context.getVimClient(). - getDynamicProperty(_mor, "config.hardware.device"); - - int scsiControllerCount = 0; - int busNum = -1; - DiskControllerType controllerType = DiskControllerType.lsilogic; - if (devices != null && !devices.isEmpty()) { - 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 = _context.getVimClient().getDynamicProperty(_mor, "config.hardware.device"); - - logger.info("Counting disk devices attached to VM " + getVmName()); - int count = 0; - - if (devices != null && !devices.isEmpty()) { - for (VirtualDevice device : devices) { - if (device instanceof VirtualDisk) { - count++; - } - } - } - return count; - } public String getExternalDiskUUID(String datastoreVolumePath) throws Exception{ List devices = _context.getVimClient().getDynamicProperty(_mor, "config.hardware.device"); @@ -3492,4 +3096,169 @@ public void removeChangeTrackPathFromVmdkForDisks() throws Exception { public String toString() { return ReflectionToStringBuilderUtils.reflectOnlySelectedFields(this, "internalCSName"); } + + public List getControllers() throws Exception { + List controllers = new ArrayList<>(); + + List devices = _context.getVimClient().getDynamicProperty(_mor, "config.hardware.device"); + for (VirtualDevice device : devices) { + if (device instanceof VirtualController) { + controllers.add((VirtualController) device); + } + } + + return controllers; + } + + public Set getMappingsForExistingDiskControllers() throws Exception { + Set mappingsForExistingControllers = new HashSet<>(); + + List devices = _context.getVimClient().getDynamicProperty(_mor, "config.hardware.device"); + for (VirtualDevice device : devices) { + if (!(device instanceof VirtualController)) { + continue; + } + String classpath = device.getClass().getName(); + try { + DiskControllerMappingVO mapping = VmwareHelper.getDiskControllerMapping(null, classpath); + mappingsForExistingControllers.add(mapping); + } catch (CloudRuntimeException e) { + logger.debug("No disk controller mapping found for existing controller [{}]; ignoring it.", classpath); + } + } + + return mappingsForExistingControllers; + } + + /** + * Searches for a disk controller that has available device nodes, and returns its type's equivalent mapping.
+ * This function prioritizes choosing disk controllers different than VirtualIDEController. If no other + * disk controller type is available, but a VirtualIDEController is, then the IDE will be chosen. + * @throws CloudRuntimeException if no disk controller is available. + */ + public DiskControllerMappingVO getAnyExistingAvailableDiskController() throws Exception { + Set validDiskControllerClasspaths = VmwareHelper.getAllSupportedDiskControllerMappingsExceptOsDefault().stream() + .map(DiskControllerMappingVO::getControllerReference) + .collect(Collectors.toSet()); + Set unavailableControllerClasspaths = new HashSet<>(); + + List devices = _context.getVimClient().getDynamicProperty(_mor, "config.hardware.device"); + for (VirtualDevice device : devices) { + String deviceClasspath = device.getClass().getName(); + if (VirtualIDEController.class.getName().equals(deviceClasspath)) { + // Prioritize choosing controllers different from IDE + continue; + } + if (!validDiskControllerClasspaths.contains(deviceClasspath) || unavailableControllerClasspaths.contains(deviceClasspath)) { + continue; + } + DiskControllerMappingVO diskController = VmwareHelper.getDiskControllerMapping(null, deviceClasspath); + Pair nextAvailableControllerKeyAndUnitNumber = getNextAvailableControllerKeyAndDeviceNumberForType(diskController); + if (nextAvailableControllerKeyAndUnitNumber != null) { + return diskController; + } + unavailableControllerClasspaths.add(deviceClasspath); + } + + DiskControllerMappingVO ideMapping = VmwareHelper.getDiskControllerMapping(null, VirtualIDEController.class.getName()); + Pair nextAvailableControllerKeyAndUnitNumber = getNextAvailableControllerKeyAndDeviceNumberForType(ideMapping); + if (nextAvailableControllerKeyAndUnitNumber != null) { + return ideMapping; + } + throw new CloudRuntimeException("Unable to find an available disk controller in the virtual machine."); + } + + /** + * Searches for disk controllers matching the provided mapping, and returns a pair containing, respectively, the first + * found available controller's key and its next available device node number. If no disk controller matching the mapping + * is found, returns null. + */ + public Pair getNextAvailableControllerKeyAndDeviceNumberForType(DiskControllerMappingVO mapping) throws Exception { + List devices = _context.getVimClient().getDynamicProperty(_mor, "config.hardware.device"); + for (VirtualDevice device : devices) { + if (!mapping.getControllerReference().equals(device.getClass().getName())) { + continue; + } + VirtualController controller = (VirtualController) device; + if (controller.getBusNumber() >= mapping.getMaxControllerCount()) { + continue; + } + int nextAvailableDeviceNumber = getNextAvailableDeviceNumberForController(controller, mapping); + if (nextAvailableDeviceNumber >= 0) { + return new Pair<>(controller.getKey(), nextAvailableDeviceNumber); + } + } + logger.debug("No available disk controller of class [{}] found.", mapping.getControllerReference()); + return null; + } + + /** + * Returns the next available device node number for the provided disk controller. If no node is available, returns -1. + * @param mapping mapping that represents the provided disk controller's type. + */ + protected int getNextAvailableDeviceNumberForController(VirtualController diskController, DiskControllerMappingVO mapping) throws Exception { + List devices = _context.getVimClient().getDynamicProperty(_mor, "config.hardware.device"); + + Set usedUnitNumbers = new HashSet<>(); + for (VirtualDevice device : devices) { + if (device.getControllerKey() == null || device.getControllerKey() != diskController.getKey()) { + continue; + } + if (device instanceof VirtualDisk || device instanceof VirtualCdrom) { + usedUnitNumbers.add(device.getUnitNumber()); + } + } + if (VmwareHelper.isControllerScsi(mapping)) { + usedUnitNumbers.add(7); + } + + int unitNumber = 0; + while (unitNumber < mapping.getMaxDeviceCount()) { + if (!usedUnitNumbers.contains(unitNumber)) { + return unitNumber; + } + unitNumber++; + } + logger.debug("Disk controller [{}] does not have an available device number.", diskController.getKey()); + return -1; + } + + /** + * Searches all the instance's devices for devices with a class represented by classpath, and returns the + * nth (specified by number) device found. + * @throws CloudRuntimeException if the device is not found. + */ + public VirtualDevice getNthDevice(String classpath, int number) throws Exception { + int currentNumber = 0; + + List devices = _context.getVimClient().getDynamicProperty(_mor, "config.hardware.device"); + for (VirtualDevice device : devices) { + if (classpath.equals(device.getClass().getName())) { + if (currentNumber == number) { + return device; + } + currentNumber++; + } + } + + logger.debug("Unable to find a device with classpath [{}] and number [{}] in the virtual machine.", classpath, number); + throw new CloudRuntimeException("Unable to find the required device in the virtual machine."); + } + + /** + * Validates (i) if the provided disk controller has a valid bus number and (ii) if it has an available device node number. + * @param mapping mapping that represents the provided disk controller's type. + * @throws CloudRuntimeException if it does not. + */ + public void validateDiskControllerIsAvailable(VirtualController diskController, DiskControllerMappingVO mapping) throws Exception { + if (diskController.getBusNumber() >= mapping.getMaxControllerCount()) { + logger.debug("Disk controller [{}] has an invalid bus number [{}].", diskController.getKey(), diskController.getBusNumber()); + throw new CloudRuntimeException("Virtual machine does not have an available disk controller device."); + } + + int nextUnconfiguredDeviceNumber = getNextAvailableDeviceNumberForController(diskController, mapping); + if (nextUnconfiguredDeviceNumber == -1) { + throw new CloudRuntimeException("Virtual machine does not have an available disk controller device."); + } + } } diff --git a/vmware-base/src/main/java/com/cloud/hypervisor/vmware/mo/VmdkAdapterType.java b/vmware-base/src/main/java/com/cloud/hypervisor/vmware/mo/VmdkAdapterType.java index ff0a7d30672d..7ac499765567 100644 --- a/vmware-base/src/main/java/com/cloud/hypervisor/vmware/mo/VmdkAdapterType.java +++ b/vmware-base/src/main/java/com/cloud/hypervisor/vmware/mo/VmdkAdapterType.java @@ -22,18 +22,6 @@ public enum VmdkAdapterType { 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 == null) return VmdkAdapterType.none; diff --git a/vmware-base/src/main/java/com/cloud/hypervisor/vmware/mo/VmwareHypervisorHost.java b/vmware-base/src/main/java/com/cloud/hypervisor/vmware/mo/VmwareHypervisorHost.java index 2177b062b003..b4fc8fc707bd 100644 --- a/vmware-base/src/main/java/com/cloud/hypervisor/vmware/mo/VmwareHypervisorHost.java +++ b/vmware-base/src/main/java/com/cloud/hypervisor/vmware/mo/VmwareHypervisorHost.java @@ -27,6 +27,7 @@ import com.cloud.hypervisor.vmware.util.VmwareContext; import com.cloud.utils.Pair; +import org.apache.cloudstack.storage.DiskControllerMappingVO; /** * Interface to consolidate ESX(i) hosts and HA/FT clusters into a common interface used by CloudStack Hypervisor resources @@ -64,7 +65,7 @@ public interface VmwareHypervisorHost { boolean createBlankVm(String vmName, String vmInternalCSName, int cpuCount, int cpuSpeedMHz, int cpuReservedMHz, boolean limitCpuUse, int memoryMB, int memoryReserveMB, String guestOsIdentifier, ManagedObjectReference morDs, boolean snapshotDirToParent, - Pair controllerInfo, Boolean systemVm) throws Exception; + Pair controllerInfo, Boolean systemVm) throws Exception; void importVmFromOVF(String ovfFilePath, String vmName, DatastoreMO dsMo, String diskOption, String configurationId) throws Exception; diff --git a/vmware-base/src/main/java/com/cloud/hypervisor/vmware/util/VmwareHelper.java b/vmware-base/src/main/java/com/cloud/hypervisor/vmware/util/VmwareHelper.java index 62d283e0e0d6..609e0f8c8876 100644 --- a/vmware-base/src/main/java/com/cloud/hypervisor/vmware/util/VmwareHelper.java +++ b/vmware-base/src/main/java/com/cloud/hypervisor/vmware/util/VmwareHelper.java @@ -31,9 +31,12 @@ import java.util.Date; import java.util.GregorianCalendar; import java.util.HashMap; +import java.util.HashSet; import java.util.List; import java.util.Random; +import java.util.Set; import java.util.UUID; +import java.util.stream.Collectors; import javax.annotation.Nonnull; import javax.xml.datatype.DatatypeConfigurationException; @@ -56,18 +59,26 @@ import com.vmware.vim25.GuestNicInfo; import com.vmware.vim25.HostPortGroupSpec; import com.vmware.vim25.NasDatastoreInfo; +import com.vmware.vim25.ParaVirtualSCSIController; import com.vmware.vim25.VMwareDVSPortSetting; +import com.vmware.vim25.VirtualController; +import com.vmware.vim25.VirtualDeviceConfigSpec; +import com.vmware.vim25.VirtualDeviceConfigSpecOperation; import com.vmware.vim25.VirtualDeviceFileBackingInfo; import com.vmware.vim25.VirtualIDEController; +import com.vmware.vim25.VirtualLsiLogicController; import com.vmware.vim25.VirtualMachineConfigSummary; import com.vmware.vim25.VirtualMachineGuestOsIdentifier; import com.vmware.vim25.VirtualMachineToolsStatus; import com.vmware.vim25.VirtualSCSIController; +import com.vmware.vim25.VirtualSCSISharing; import com.vmware.vim25.VmwareDistributedVirtualSwitchPvlanSpec; import com.vmware.vim25.VmwareDistributedVirtualSwitchVlanIdSpec; +import org.apache.cloudstack.storage.DiskControllerMappingVO; import org.apache.cloudstack.utils.volume.VirtualMachineDiskInfo; import org.apache.cloudstack.vm.UnmanagedInstanceTO; import org.apache.commons.collections.CollectionUtils; +import org.apache.commons.lang3.ObjectUtils; import org.apache.commons.lang3.StringUtils; import org.apache.logging.log4j.Logger; import org.apache.logging.log4j.LogManager; @@ -124,17 +135,20 @@ public class VmwareHelper { protected static Logger LOGGER = LogManager.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 = 16; public static final int MAX_SUPPORTED_DEVICES_SCSI_CONTROLLER = MAX_ALLOWED_DEVICES_SCSI_CONTROLLER - 1; // One device node is unavailable for hard disks or SCSI devices - public static final int MAX_USABLE_SCSI_CONTROLLERS = 2; public static final String MIN_VERSION_UEFI_LEGACY = "5.5"; public static final String MIN_VERSION_VMFS6 = "6.5"; + private static List supportedDiskControllers; + + public static void setSupportedDiskControllers(List controllers) { + supportedDiskControllers = controllers; + } + public static boolean isReservedScsiDeviceNumber(int deviceNumber) { // The SCSI controller is assigned to virtual device node (z:7), so that device node is unavailable for hard disks or SCSI devices. - return (deviceNumber % VmwareHelper.MAX_ALLOWED_DEVICES_SCSI_CONTROLLER) == 7; + return (deviceNumber % MAX_ALLOWED_DEVICES_SCSI_CONTROLLER) == 7; } @Nonnull @@ -746,13 +760,11 @@ public static String getVCenterSafeUuid(DatastoreMO dsMo) throws Exception{ } public static String getRecommendedDiskControllerFromDescriptor(GuestOsDescriptor guestOsDescriptor) throws Exception { - String recommendedController; - - recommendedController = guestOsDescriptor.getRecommendedDiskController(); + String recommendedController = guestOsDescriptor.getRecommendedDiskController(); // By-pass auto detected PVSCSI controller to use LsiLogic Parallel instead - if (DiskControllerType.getType(recommendedController) == DiskControllerType.pvscsi) { - recommendedController = DiskControllerType.lsilogic.toString(); + if (ParaVirtualSCSIController.class.getSimpleName().equals(recommendedController)) { + return VirtualLsiLogicController.class.getSimpleName(); } return recommendedController; @@ -769,8 +781,18 @@ public static String trimSnapshotDeltaPostfix(String name) { return name; } - public static boolean isControllerOsRecommended(String dataDiskController) { - return DiskControllerType.getType(dataDiskController) == DiskControllerType.osdefault; + /** + * Returns true if the provided mapping's name is "osdefault". + */ + public static boolean isControllerOsRecommended(DiskControllerMappingVO mapping) { + return DiskControllerType.osdefault.toString().equals(mapping.getName()); + } + + /** + * Returns true if the provided mapping's bus name is "scsi". + */ + public static boolean isControllerScsi(DiskControllerMappingVO mapping) { + return DiskControllerType.scsi.toString().equals(mapping.getBusName()); } public static XMLGregorianCalendar getXMLGregorianCalendar(final Date date, final int offsetSeconds) throws DatatypeConfigurationException { @@ -879,18 +901,18 @@ protected static List getUnmanageInstanceDisks(Virtual instanceDisk.setChainInfo(GsonHelper.getGsonLogger().toJson(diskInfo)); } for (VirtualDevice device : vmMo.getAllDeviceList()) { - if (diskDevice.getControllerKey() == device.getKey()) { - if (device instanceof VirtualIDEController) { - instanceDisk.setController(DiskControllerType.getType(device.getClass().getSimpleName()).toString()); - instanceDisk.setControllerUnit(((VirtualIDEController) device).getBusNumber()); - } else if (device instanceof VirtualSCSIController) { - instanceDisk.setController(DiskControllerType.getType(device.getClass().getSimpleName()).toString()); - instanceDisk.setControllerUnit(((VirtualSCSIController) device).getBusNumber()); - } else { - instanceDisk.setController(DiskControllerType.none.toString()); - } - break; + if (diskDevice.getControllerKey() != device.getKey()) { + continue; + } + try { + DiskControllerMappingVO diskController = VmwareHelper.getDiskControllerMapping(null, device.getClass().getName()); + instanceDisk.setController(diskController.getName()); + instanceDisk.setControllerUnit(((VirtualController) device).getBusNumber()); + } catch (CloudRuntimeException e) { + LOGGER.debug("Unable to find a disk controller mapping for class [{}]; defaulting disk controller to 'none'.", device.getClass().getName()); + instanceDisk.setController(DiskControllerType.none.toString()); } + break; } if (disk.getBacking() instanceof VirtualDeviceFileBackingInfo) { VirtualDeviceFileBackingInfo diskBacking = (VirtualDeviceFileBackingInfo) disk.getBacking(); @@ -1070,45 +1092,127 @@ public static String getAbsoluteVmdkFile(VirtualDisk disk) { } /** - * Validates an instance's rootDiskController and dataDiskController details. Throws a - * CloudRuntimeException if they are invalid. + * Returns a disk controller mapping that has the provided name or the provided controller reference. + * @throws CloudRuntimeException if there is no configured disk controller matching the provided params. */ - public static void validateDiskControllerDetails(String rootDiskControllerDetail, String dataDiskControllerDetail) { - rootDiskControllerDetail = DiskControllerType.getType(rootDiskControllerDetail).toString(); - if (DiskControllerType.getType(rootDiskControllerDetail) == DiskControllerType.none) { - throw new CloudRuntimeException(String.format("[%s] is not a valid root disk controller", rootDiskControllerDetail)); + public static DiskControllerMappingVO getDiskControllerMapping(String name, String controllerReference) { + for (DiskControllerMappingVO mapping : getAllSupportedDiskControllerMappings()) { + if (mapping.getName().equals(name) || mapping.getControllerReference().equals(controllerReference)) { + return mapping; + } } - dataDiskControllerDetail = DiskControllerType.getType(dataDiskControllerDetail).toString(); - if (DiskControllerType.getType(dataDiskControllerDetail) == DiskControllerType.none) { - throw new CloudRuntimeException(String.format("[%s] is not a valid data disk controller", dataDiskControllerDetail)); + LOGGER.debug("No corresponding disk controller mapping found for name [{}] and controller reference [{}].", name, controllerReference); + throw new CloudRuntimeException("Specified disk controller is invalid."); + } + + public static List getAllSupportedDiskControllerMappings() { + return supportedDiskControllers; + } + + public static List getAllSupportedDiskControllerMappingsExceptOsDefault() { + return getAllSupportedDiskControllerMappings().stream() + .filter(c -> !isControllerOsRecommended(c)) + .collect(Collectors.toList()); + } + + /** + * Returns a set containing the disk controllers an instance should have based on the provided params. + * @param controllerInfo pair containing the root disk and data disk controllers, respectively. + * @param isSystemVm if true, only the root disk controller is required; otherwise, both controllers are required. + */ + public static Set getRequiredDiskControllers(Pair controllerInfo, + boolean isSystemVm) { + Set requiredDiskControllers = new HashSet<>(); + requiredDiskControllers.add(controllerInfo.first()); + if (!isSystemVm) { + requiredDiskControllers.add(controllerInfo.second()); } + return requiredDiskControllers; + } + + /** + * Instructs the provided VirtualMachineConfigSpec to create the disk controllers contained in requiredDiskControllers. + * @param isSystemVm if true, only one disk controller of each type will be created; otherwise, the maximum amount of each controller will be created. + * @throws Exception if a disk controller mapping has an invalid controller reference. + */ + public static void addDiskControllersToVmConfigSpec(VirtualMachineConfigSpec vmConfigSpec, Set requiredDiskControllers, + boolean isSystemVm) throws Exception { + int currentKey = -1; + + for (DiskControllerMappingVO diskController : requiredDiskControllers) { + Class controllerClass = Class.forName(diskController.getControllerReference()); + if (controllerClass == VirtualIDEController.class) { + continue; + } + for (int bus = 0; bus < diskController.getMaxControllerCount(); bus++) { + VirtualController controller = (VirtualController) controllerClass.newInstance(); + controller.setBusNumber(bus); + controller.setKey(currentKey); + currentKey--; + + if (controller instanceof VirtualSCSIController) { + ((VirtualSCSIController) controller).setSharedBus(VirtualSCSISharing.NO_SHARING); + } + + VirtualDeviceConfigSpec controllerConfigSpec = new VirtualDeviceConfigSpec(); + controllerConfigSpec.setDevice(controller); + controllerConfigSpec.setOperation(VirtualDeviceConfigSpecOperation.ADD); + vmConfigSpec.getDeviceChange().add(controllerConfigSpec); + + if (isSystemVm) { + break; + } + } + } + } + + /** + * Based on an instance's rootDiskController and dataDiskController details, returns a pair + * containing the disk controller mappings that should be used for root disk and the data disks, respectively. + * @param isSystemVm if true, the root disk controller detail will be ignored, and the chosen root disk controller will be lsilogic. + */ + public static Pair getDiskControllersFromVmSettings(String rootDiskControllerDetail, + String dataDiskControllerDetail, + boolean isSystemVm) { + String updatedRootDiskControllerDetail = isSystemVm ? DiskControllerType.lsilogic.toString() : ObjectUtils.defaultIfNull(rootDiskControllerDetail, DiskControllerType.osdefault.toString()); + String updatedDataDiskControllerDetail = ObjectUtils.defaultIfNull(dataDiskControllerDetail, updatedRootDiskControllerDetail); + + DiskControllerMappingVO specifiedRootDiskController = getDiskControllerMapping(updatedRootDiskControllerDetail, null); + DiskControllerMappingVO specifiedDataDiskController = getDiskControllerMapping(updatedDataDiskControllerDetail, null); + + return new Pair<>(specifiedRootDiskController, specifiedDataDiskController); } /** * Based on an instance's rootDiskController and dataDiskController details, returns a pair * containing the disk controllers that should be used for root disk and the data disks, respectively. - * - * @param controllerInfo pair containing the root disk and data disk controllers, respectively. - * @param vmMo virtual machine to derive the recommended disk controllers from. If not null, host and guestOsIdentifier will be ignored. - * @param host host to derive the recommended disk controllers from. Must be provided with guestOsIdentifier. + * @param controllerInfo pair containing the root disk and data disk controllers, respectively. + * @param vmMo virtual machine to derive the recommended disk controllers from. If not null, host and guestOsIdentifier will be ignored. + * @param host host to derive the recommended disk controllers from. Must be provided with guestOsIdentifier. * @param guestOsIdentifier used to derive the recommended disk controllers from the host. + * @throws Exception if no disk controller mapping matching the recommended disk controller exists. */ - public static Pair chooseRequiredDiskControllers(Pair controllerInfo, VirtualMachineMO vmMo, - VmwareHypervisorHost host, String guestOsIdentifier) throws Exception { + public static Pair chooseDiskControllers(Pair controllerInfo, + VirtualMachineMO vmMo, + VmwareHypervisorHost host, + String guestOsIdentifier) throws Exception { String recommendedDiskControllerClassName = vmMo != null ? vmMo.getRecommendedDiskController(null) : host.getRecommendedDiskController(guestOsIdentifier); - String recommendedDiskController = DiskControllerType.getType(recommendedDiskControllerClassName).toString(); + DiskControllerMappingVO recommendedDiskController = getAllSupportedDiskControllerMappingsExceptOsDefault().stream() + .filter(c -> c.getControllerReference().contains(recommendedDiskControllerClassName)) + .findFirst() + .orElseThrow(() -> new CloudRuntimeException("Recommended disk controller is not mapped.")); - String convertedRootDiskController = controllerInfo.first(); + DiskControllerMappingVO convertedRootDiskController = controllerInfo.first(); if (isControllerOsRecommended(convertedRootDiskController)) { convertedRootDiskController = recommendedDiskController; } - String convertedDataDiskController = controllerInfo.second(); + DiskControllerMappingVO convertedDataDiskController = controllerInfo.second(); if (isControllerOsRecommended(convertedDataDiskController)) { convertedDataDiskController = recommendedDiskController; } - if (diskControllersShareTheSameBusType(convertedRootDiskController, convertedDataDiskController)) { + if (convertedRootDiskController.getBusName().equals(convertedDataDiskController.getBusName())) { LOGGER.debug("Root and data disk controllers share the same bus type; therefore, we will only use the controllers specified for the root disk."); return new Pair<>(convertedRootDiskController, convertedRootDiskController); } @@ -1116,28 +1220,40 @@ public static Pair chooseRequiredDiskControllers(Pair(convertedRootDiskController, convertedDataDiskController); } - protected static boolean diskControllersShareTheSameBusType(String rootDiskController, String dataDiskController) { - DiskControllerType rootDiskControllerType = DiskControllerType.getType(rootDiskController); - DiskControllerType dataDiskControllerType = DiskControllerType.getType(dataDiskController); - if (rootDiskControllerType.equals(dataDiskControllerType)) { - return true; - } - List scsiDiskControllers = List.of(DiskControllerType.scsi, DiskControllerType.lsilogic, DiskControllerType.lsisas1068, - DiskControllerType.buslogic ,DiskControllerType.pvscsi); - return scsiDiskControllers.contains(rootDiskControllerType) && scsiDiskControllers.contains(dataDiskControllerType); - } - /** * Identifies whether the disk is a root or data disk, and returns the controller from the provided pair that should * be used for the disk. * @param controllerInfo pair containing the root disk and data disk controllers, respectively. */ - public static String getControllerBasedOnDiskType(Pair controllerInfo, DiskTO disk) { + public static DiskControllerMappingVO getControllerBasedOnDiskType(Pair controllerInfo, + DiskTO disk) { if (disk.getType() == Volume.Type.ROOT || disk.getDiskSeq() == 0) { - LOGGER.debug(String.format("Choosing disk controller [%s] for the root disk.", controllerInfo.first())); + LOGGER.debug("Choosing disk controller [{}] for the root disk.", controllerInfo.first()); return controllerInfo.first(); } - LOGGER.debug(String.format("Choosing disk controller [%s] for the data disks.", controllerInfo.second())); + LOGGER.debug("Choosing disk controller [{}] for the data disks.", controllerInfo.second()); return controllerInfo.second(); } + + public static void configureDiskControllerMappingsInVmwareBaseModule(List mappings) { + List validMappings = new ArrayList<>(); + + for (DiskControllerMappingVO mapping : mappings) { + try { + if (!DiskControllerType.osdefault.toString().equals(mapping.getName())) { + Class.forName(mapping.getControllerReference()); + } + LOGGER.debug("Adding disk controller mapping with name [{}] and controller reference [{}] to the list of available disk controllers.", + mapping.getName(), mapping.getControllerReference()); + validMappings.add(mapping); + } catch (ClassNotFoundException e) { + LOGGER.debug("Disk controller mapping with name [{}] and controller reference [{}] is invalid; ignoring it.", + mapping.getName(), mapping.getControllerReference()); + } + } + + setSupportedDiskControllers(validMappings); + LOGGER.info("Configured the available disk controller mappings on the 'vmware-base' module. To add support for a new controller, " + + "it is necessary to restart the management servers."); + } } diff --git a/vmware-base/src/test/java/com/cloud/hypervisor/vmware/mo/VirtualMachineMOTest.java b/vmware-base/src/test/java/com/cloud/hypervisor/vmware/mo/VirtualMachineMOTest.java index 570f2a7b4a16..fd091fa9aa1a 100644 --- a/vmware-base/src/test/java/com/cloud/hypervisor/vmware/mo/VirtualMachineMOTest.java +++ b/vmware-base/src/test/java/com/cloud/hypervisor/vmware/mo/VirtualMachineMOTest.java @@ -19,10 +19,19 @@ import com.cloud.hypervisor.vmware.util.VmwareClient; import com.cloud.hypervisor.vmware.util.VmwareContext; +import com.cloud.hypervisor.vmware.util.VmwareHelper; +import com.vmware.vim25.VirtualCdrom; +import com.vmware.vim25.VirtualDisk; +import com.vmware.vim25.VirtualE1000; +import com.vmware.vim25.VirtualIDEController; +import com.vmware.vim25.VirtualLsiLogicController; +import com.vmware.vim25.VirtualNVMEController; +import org.apache.cloudstack.storage.DiskControllerMappingVO; +import com.cloud.utils.Pair; import com.cloud.utils.exception.CloudRuntimeException; import com.vmware.vim25.ManagedObjectReference; +import com.vmware.vim25.VirtualController; import com.vmware.vim25.VirtualDevice; -import com.vmware.vim25.VirtualLsiLogicController; import com.vmware.vim25.VirtualLsiLogicSASController; import com.vmware.vim25.VirtualSCSIController; import com.vmware.vim25.VirtualSCSISharing; @@ -34,11 +43,14 @@ import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.Mock; +import org.mockito.Mockito; import org.mockito.MockitoAnnotations; +import org.mockito.Spy; import org.mockito.junit.MockitoJUnitRunner; import java.util.ArrayList; import java.util.List; +import java.util.Set; import static org.junit.Assert.fail; import static org.mockito.ArgumentMatchers.any; @@ -53,6 +65,10 @@ public class VirtualMachineMOTest { VmwareClient client; @Mock ManagedObjectReference mor; + @Mock + VirtualController virtualControllerMock; + @Spy + VirtualMachineMO vmMoSpy; VirtualMachineMO vmMo; @@ -81,6 +97,40 @@ public void setup() { closeable = MockitoAnnotations.openMocks(this); vmMo = new VirtualMachineMO(context, mor); when(context.getVimClient()).thenReturn(client); + + configureSupportedDiskControllersForTests(); + vmMoSpy._context = context; + vmMoSpy._mor = mor; + } + + private void configureSupportedDiskControllersForTests() { + DiskControllerMappingVO osdefaultMapping = new DiskControllerMappingVO(); + osdefaultMapping.setName("osdefault"); + osdefaultMapping.setControllerReference("osdefault"); + + DiskControllerMappingVO ideMapping = new DiskControllerMappingVO(); + ideMapping.setName("ide"); + ideMapping.setControllerReference(VirtualIDEController.class.getName()); + ideMapping.setBusName("ide"); + ideMapping.setMaxDeviceCount(2); + ideMapping.setMaxControllerCount(2); + + DiskControllerMappingVO lsilogicMapping = new DiskControllerMappingVO(); + lsilogicMapping.setName("lsilogic"); + lsilogicMapping.setControllerReference(VirtualLsiLogicController.class.getName()); + lsilogicMapping.setBusName("scsi"); + lsilogicMapping.setMaxDeviceCount(16); + lsilogicMapping.setMaxControllerCount(4); + + DiskControllerMappingVO nvmeMapping = new DiskControllerMappingVO(); + nvmeMapping.setName("nvme"); + nvmeMapping.setControllerReference(VirtualNVMEController.class.getName()); + nvmeMapping.setBusName("nvme"); + nvmeMapping.setMaxDeviceCount(15); + nvmeMapping.setMaxControllerCount(4); + nvmeMapping.setMinHardwareVersion("13"); + + VmwareHelper.setSupportedDiskControllers(List.of(osdefaultMapping, ideMapping, lsilogicMapping, nvmeMapping)); } @BeforeClass @@ -107,18 +157,6 @@ public void testEnsureScsiDeviceController() { } } - @Test - public void TestEnsureLsiLogicDeviceControllers() { - try { - when(client.getDynamicProperty(any(ManagedObjectReference.class), any(String.class))).thenReturn(getVirtualScSiDeviceList(VirtualLsiLogicController.class)); - vmMo.ensureLsiLogicDeviceControllers(1, 0); - } - catch (Exception e) { - fail("Received exception when success expected: " + e.getMessage()); - } - - } - @Test public void testGetVmxFormattedVirtualHardwareVersionOneDigit() { String vmxHwVersion = VirtualMachineMO.getVmxFormattedVirtualHardwareVersion(8); @@ -135,4 +173,216 @@ public void testGetVmxFormattedVirtualHardwareVersionTwoDigits() { public void testGetVmxFormattedVirtualHardwareVersionInvalid() { VirtualMachineMO.getVmxFormattedVirtualHardwareVersion(-1); } + + @Test + public void getDeviceBusNameTestReturnsValue() throws Exception { + List virtualDevices = new ArrayList<>(); + for (int i = 0; i < 4; i++) { + VirtualLsiLogicController controller = new VirtualLsiLogicController(); + controller.setKey(100 + i); + controller.setBusNumber(i); + virtualDevices.add(controller); + } + VirtualDisk disk = new VirtualDisk(); + disk.setControllerKey(101); + disk.setUnitNumber(3); + + String result = vmMoSpy.getDeviceBusName(virtualDevices, disk); + + Assert.assertEquals("scsi1:3", result); + } + + private List configureDevicesForTests() throws Exception { + List devices = new ArrayList<>(); + devices.add(new VirtualE1000()); + devices.add(new VirtualCdrom()); + devices.add(new VirtualDisk()); + devices.add(new VirtualIDEController()); + devices.add(new VirtualIDEController()); + VirtualNVMEController nvmeController = new VirtualNVMEController(); + nvmeController.setKey(111); + nvmeController.setBusNumber(3); + devices.add(nvmeController); + Mockito.doReturn(devices).when(client).getDynamicProperty(mor, "config.hardware.device"); + return devices; + } + + @Test + public void getControllersTestReturnsValue() throws Exception { + List devices = configureDevicesForTests(); + + List result = vmMoSpy.getControllers(); + + for (VirtualDevice device : devices) { + if (!(device instanceof VirtualController)) { + continue; + } + Assert.assertTrue(result.contains(device)); + } + } + + @Test + public void getMappingsForExistingDiskControllersTestReturnsValue() throws Exception { + configureDevicesForTests(); + + Set result = vmMoSpy.getMappingsForExistingDiskControllers(); + + DiskControllerMappingVO ideMapping = VmwareHelper.getDiskControllerMapping("ide", null); + DiskControllerMappingVO nvmeMapping = VmwareHelper.getDiskControllerMapping("nvme", null); + Assert.assertTrue(result.contains(ideMapping)); + Assert.assertTrue(result.contains(nvmeMapping)); + } + + @Test + public void getAnyExistingAvailableDiskControllerTestReturnsNonIdeControllerWhenNonIdeControllerIsAvailable() throws Exception { + DiskControllerMappingVO nvmeMapping = VmwareHelper.getDiskControllerMapping("nvme", null); + DiskControllerMappingVO ideMapping = VmwareHelper.getDiskControllerMapping("ide", null); + Mockito.doReturn(new Pair<>(1, 2)).when(vmMoSpy).getNextAvailableControllerKeyAndDeviceNumberForType(nvmeMapping); + Mockito.lenient().doReturn(new Pair<>(3, 4)).when(vmMoSpy).getNextAvailableControllerKeyAndDeviceNumberForType(ideMapping); + configureDevicesForTests(); + + DiskControllerMappingVO result = vmMoSpy.getAnyExistingAvailableDiskController(); + + + Assert.assertEquals(nvmeMapping, result); + } + + @Test + public void getAnyExistingAvailableDiskControllerTestReturnsIdeControllerWhenNonIdeControllerIsNotAvailable() throws Exception { + DiskControllerMappingVO nvmeMapping = VmwareHelper.getDiskControllerMapping("nvme", null); + DiskControllerMappingVO ideMapping = VmwareHelper.getDiskControllerMapping("ide", null); + Mockito.doReturn(null).when(vmMoSpy).getNextAvailableControllerKeyAndDeviceNumberForType(nvmeMapping); + Mockito.doReturn(new Pair<>(1, 2)).when(vmMoSpy).getNextAvailableControllerKeyAndDeviceNumberForType(ideMapping); + configureDevicesForTests(); + + DiskControllerMappingVO result = vmMoSpy.getAnyExistingAvailableDiskController(); + + Assert.assertEquals(ideMapping, result); + } + + @Test(expected = CloudRuntimeException.class) + public void getAnyExistingAvailableDiskControllerTestThrowsExceptionWhenNoDiskControllerIsAvailable() throws Exception { + DiskControllerMappingVO nvmeMapping = VmwareHelper.getDiskControllerMapping("nvme", null); + DiskControllerMappingVO ideMapping = VmwareHelper.getDiskControllerMapping("ide", null); + Mockito.doReturn(null).when(vmMoSpy).getNextAvailableControllerKeyAndDeviceNumberForType(nvmeMapping); + Mockito.doReturn(null).when(vmMoSpy).getNextAvailableControllerKeyAndDeviceNumberForType(ideMapping); + configureDevicesForTests(); + + vmMoSpy.getAnyExistingAvailableDiskController(); + } + + @Test + public void getNextAvailableControllerKeyAndDeviceNumberForTypeTestReturnsValueWhenControllerIsAvailable() throws Exception { + Mockito.doReturn(0).when(vmMoSpy).getNextAvailableDeviceNumberForController(Mockito.any(VirtualNVMEController.class), Mockito.any()); + configureDevicesForTests(); + + DiskControllerMappingVO nvmeMapping = VmwareHelper.getDiskControllerMapping("nvme", null); + Pair result = vmMoSpy.getNextAvailableControllerKeyAndDeviceNumberForType(nvmeMapping); + + Assert.assertEquals(111, (int) result.first()); + Assert.assertEquals(0, (int) result.second()); + } + + @Test + public void getNextAvailableControllerKeyAndDeviceNumberForTypeTestReturnsNullWhenNoControllerIsAvailable() throws Exception { + Mockito.doReturn(-1).when(vmMoSpy).getNextAvailableDeviceNumberForController(Mockito.any(), Mockito.any()); + configureDevicesForTests(); + + DiskControllerMappingVO nvmeMapping = VmwareHelper.getDiskControllerMapping("nvme", null); + Pair result = vmMoSpy.getNextAvailableControllerKeyAndDeviceNumberForType(nvmeMapping); + + Assert.assertNull(result); + } + + private Pair configureScsiControllersAndDisksForGetNextAvailableDeviceNumberForControllerTests(int numberOfDevices) throws Exception { + List devices = new ArrayList<>(); + + devices.add(new VirtualIDEController()); + devices.add(new VirtualIDEController()); + + int firstScsiKey = 1; + VirtualLsiLogicController firstScsiController = new VirtualLsiLogicController(); + firstScsiController.setKey(firstScsiKey); + firstScsiController.setBusNumber(0); + VirtualLsiLogicController secondScsiController = new VirtualLsiLogicController(); + secondScsiController.setKey(firstScsiKey + 1); + secondScsiController.setBusNumber(1); + + int currentDeviceNumber = 0; + for (int i = 0; i < numberOfDevices; i++) { + VirtualDevice device = i == 0 ? new VirtualCdrom() : new VirtualDisk(); + device.setControllerKey(firstScsiKey); + device.setUnitNumber(currentDeviceNumber); + devices.add(device); + + currentDeviceNumber++; + if (VmwareHelper.isReservedScsiDeviceNumber(currentDeviceNumber)) { + currentDeviceNumber++; + } + } + + Mockito.doReturn(devices).when(client).getDynamicProperty(mor, "config.hardware.device"); + + DiskControllerMappingVO lsilogicMapping = VmwareHelper.getDiskControllerMapping("lsilogic", null); + return new Pair<>(firstScsiController, lsilogicMapping); + } + + @Test + public void getNextAvailableDeviceNumberForControllerTestValueWhenDeviceNumberIsAvailable() throws Exception { + int numberOfDevices = 14; + Pair config = configureScsiControllersAndDisksForGetNextAvailableDeviceNumberForControllerTests(numberOfDevices); + + int result = vmMoSpy.getNextAvailableDeviceNumberForController(config.first(), config.second()); + + Assert.assertEquals(numberOfDevices + 1, result); + } + + @Test + public void getNextAvailableDeviceNumberForControllerTestValueWhenNoDeviceNumberIsAvailable() throws Exception { + Pair config = configureScsiControllersAndDisksForGetNextAvailableDeviceNumberForControllerTests(15); + + int result = vmMoSpy.getNextAvailableDeviceNumberForController(config.first(), config.second()); + + Assert.assertEquals(-1, result); + } + + @Test + public void getNthDeviceTestReturnsDeviceWhenDeviceExists() throws Exception { + configureDevicesForTests(); + + vmMoSpy.getNthDevice(VirtualIDEController.class.getName(), 1); + } + + @Test(expected = CloudRuntimeException.class) + public void getNthDeviceTestThrowsExceptionWhenDeviceDoesNotExist() throws Exception { + configureDevicesForTests(); + + vmMoSpy.getNthDevice(VirtualIDEController.class.getName(), 2); + } + + @Test(expected = CloudRuntimeException.class) + public void validateDiskControllerIsAvailableTestThrowsExceptionWhenBusNumberIsInvalid() throws Exception { + DiskControllerMappingVO nvmeMapping = VmwareHelper.getDiskControllerMapping("nvme", null); + Mockito.when(virtualControllerMock.getBusNumber()).thenReturn(4); + + vmMo.validateDiskControllerIsAvailable(virtualControllerMock, nvmeMapping); + } + + @Test(expected = CloudRuntimeException.class) + public void validateDiskControllerIsAvailableTestThrowsExceptionWhenNoAvailableDeviceNumberExists() throws Exception { + DiskControllerMappingVO nvmeMapping = VmwareHelper.getDiskControllerMapping("nvme", null); + Mockito.when(virtualControllerMock.getBusNumber()).thenReturn(3); + Mockito.doReturn(-1).when(vmMoSpy).getNextAvailableDeviceNumberForController(virtualControllerMock, nvmeMapping); + + vmMoSpy.validateDiskControllerIsAvailable(virtualControllerMock, nvmeMapping); + } + + @Test + public void validateDiskControllerIsAvailableTestDoesNothingWhenBusNumberIsValidAndAvailableDeviceNumberExists() throws Exception { + DiskControllerMappingVO nvmeMapping = VmwareHelper.getDiskControllerMapping("nvme", null); + Mockito.when(virtualControllerMock.getBusNumber()).thenReturn(3); + Mockito.doReturn(14).when(vmMoSpy).getNextAvailableDeviceNumberForController(virtualControllerMock, nvmeMapping); + + vmMoSpy.validateDiskControllerIsAvailable(virtualControllerMock, nvmeMapping); + } } diff --git a/vmware-base/src/test/java/com/cloud/hypervisor/vmware/util/VmwareHelperTest.java b/vmware-base/src/test/java/com/cloud/hypervisor/vmware/util/VmwareHelperTest.java index 3908f8b0be34..4db5ec055c68 100644 --- a/vmware-base/src/test/java/com/cloud/hypervisor/vmware/util/VmwareHelperTest.java +++ b/vmware-base/src/test/java/com/cloud/hypervisor/vmware/util/VmwareHelperTest.java @@ -20,10 +20,27 @@ import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNull; +import com.cloud.agent.api.to.DiskTO; +import com.cloud.hypervisor.vmware.mo.VmwareHypervisorHost; +import com.cloud.storage.Volume; +import com.cloud.utils.Pair; +import com.cloud.utils.exception.CloudRuntimeException; import com.vmware.vim25.DatastoreInfo; import com.vmware.vim25.Description; import com.vmware.vim25.ManagedObjectReference; +import com.vmware.vim25.ParaVirtualSCSIController; +import com.vmware.vim25.VirtualAHCIController; +import com.vmware.vim25.VirtualController; +import com.vmware.vim25.VirtualDeviceConfigSpec; +import com.vmware.vim25.VirtualDeviceConfigSpecOperation; import com.vmware.vim25.VirtualDiskFlatVer2BackingInfo; +import com.vmware.vim25.VirtualIDEController; +import com.vmware.vim25.VirtualLsiLogicController; +import com.vmware.vim25.VirtualMachineConfigSpec; +import com.vmware.vim25.VirtualNVMEController; +import com.vmware.vim25.VirtualSCSIController; +import com.vmware.vim25.VirtualSCSISharing; +import org.apache.cloudstack.storage.DiskControllerMappingVO; import org.apache.cloudstack.vm.UnmanagedInstanceTO; import org.junit.Assert; import org.junit.Before; @@ -36,12 +53,21 @@ import com.cloud.hypervisor.vmware.mo.VirtualMachineMO; import com.vmware.vim25.VirtualDisk; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.HashSet; import java.util.List; +import java.util.Map; +import java.util.Set; @RunWith(MockitoJUnitRunner.class) public class VmwareHelperTest { @Mock private VirtualMachineMO virtualMachineMO; + @Mock + private VmwareHypervisorHost vmwareHypervisorHostMock; + @Mock + private VirtualMachineConfigSpec virtualMachineConfigSpecMock; private static final String diskLabel = "disk1"; private static final String diskFileBaseName = "xyz.vmdk"; @@ -73,6 +99,43 @@ public void setUp() throws Exception { Mockito.when(context.getVimClient()).thenReturn(client); Mockito.when(virtualMachineMO.getContext()).thenReturn(context); Mockito.when(virtualMachineMO.getName()).thenReturn(vmName); + + DiskControllerMappingVO osdefaultMapping = new DiskControllerMappingVO(); + osdefaultMapping.setName("osdefault"); + osdefaultMapping.setControllerReference("osdefault"); + DiskControllerMappingVO ideMapping = new DiskControllerMappingVO(); + ideMapping.setName("ide"); + ideMapping.setControllerReference(VirtualIDEController.class.getName()); + ideMapping.setBusName("ide"); + ideMapping.setMaxDeviceCount(2); + ideMapping.setMaxControllerCount(2); + DiskControllerMappingVO lsilogicMapping = new DiskControllerMappingVO(); + lsilogicMapping.setName("lsilogic"); + lsilogicMapping.setControllerReference(VirtualLsiLogicController.class.getName()); + lsilogicMapping.setBusName("scsi"); + lsilogicMapping.setMaxDeviceCount(16); + lsilogicMapping.setMaxControllerCount(4); + DiskControllerMappingVO pvscsiMapping = new DiskControllerMappingVO(); + pvscsiMapping.setName("pvscsi"); + pvscsiMapping.setControllerReference(ParaVirtualSCSIController.class.getName()); + pvscsiMapping.setBusName("scsi"); + pvscsiMapping.setMaxDeviceCount(16); + pvscsiMapping.setMaxControllerCount(4); + DiskControllerMappingVO sataMapping = new DiskControllerMappingVO(); + sataMapping.setName("sata"); + sataMapping.setControllerReference(VirtualAHCIController.class.getName()); + sataMapping.setBusName("sata"); + sataMapping.setMaxDeviceCount(30); + sataMapping.setMaxControllerCount(4); + DiskControllerMappingVO nvmeMapping = new DiskControllerMappingVO(); + nvmeMapping.setName("nvme"); + nvmeMapping.setControllerReference(VirtualNVMEController.class.getName()); + nvmeMapping.setBusName("nvme"); + nvmeMapping.setMaxDeviceCount(15); + nvmeMapping.setMaxControllerCount(4); + VmwareHelper.setSupportedDiskControllers(List.of(osdefaultMapping, ideMapping, lsilogicMapping, pvscsiMapping, sataMapping, nvmeMapping)); + + Mockito.when(virtualMachineConfigSpecMock.getDeviceChange()).thenReturn(new ArrayList<>()); } @Test @@ -105,4 +168,304 @@ public void testGetUnmanageInstanceDisks() { Assert.assertEquals(diskFileBaseName, disk.getFileBaseName()); Assert.assertEquals(dataStoreName, disk.getDatastoreName()); } + + + + @Test + public void isControllerOsRecommendedTestControllerIsOsRecommendedReturnsTrue() { + DiskControllerMappingVO mapping = new DiskControllerMappingVO(); + mapping.setName("osdefault"); + + boolean result = VmwareHelper.isControllerOsRecommended(mapping); + + Assert.assertTrue(result); + } + + @Test + public void isControllerOsRecommendedTestControllerIsNotOsRecommendedReturnsFalse() { + DiskControllerMappingVO mapping = new DiskControllerMappingVO(); + mapping.setName("lsilogic"); + + boolean result = VmwareHelper.isControllerOsRecommended(mapping); + + Assert.assertFalse(result); + } + + @Test + public void isControllerScsiTestControllerIsScsiReturnsTrue() { + DiskControllerMappingVO mapping = new DiskControllerMappingVO(); + mapping.setBusName("scsi"); + + boolean result = VmwareHelper.isControllerScsi(mapping); + + Assert.assertTrue(result); + } + + @Test + public void isControllerScsiTestControllerIsNotScsiReturnsFalse() { + DiskControllerMappingVO mapping = new DiskControllerMappingVO(); + mapping.setBusName("nvme"); + + boolean result = VmwareHelper.isControllerScsi(mapping); + + Assert.assertFalse(result); + } + + @Test + public void getDiskControllerMappingTestSearchByExistingNameReturnsObject() { + String name = "lsilogic"; + + DiskControllerMappingVO mapping = VmwareHelper.getDiskControllerMapping(name, null); + + Assert.assertEquals(name, mapping.getName()); + } + + @Test + public void getDiskControllerMappingTestSearchByExistingControllerReferenceReturnsObject() { + String classpath = VirtualLsiLogicController.class.getName(); + + DiskControllerMappingVO mapping = VmwareHelper.getDiskControllerMapping(null, classpath); + + Assert.assertEquals(classpath, mapping.getControllerReference()); + } + + @Test(expected = CloudRuntimeException.class) + public void getDiskControllerMappingTestThrowsExceptionWhenNoMatches() { + VmwareHelper.getDiskControllerMapping("invalid", "invalid"); + } + + @Test + public void getAllDiskControllerMappingsExceptOsDefaultTestReturnDoesNotContainOsDefaultMapping() { + List result = VmwareHelper.getAllSupportedDiskControllerMappingsExceptOsDefault(); + + DiskControllerMappingVO osdefaultMapping = VmwareHelper.getDiskControllerMapping("osdefault", null); + Assert.assertFalse(result.contains(osdefaultMapping)); + Assert.assertFalse(result.isEmpty()); + } + + @Test + public void getRequiredDiskControllersTestRequiresAllControllersWhenInstanceIsNotSystemVm() { + Pair controllerInfo = new Pair<>(new DiskControllerMappingVO(), new DiskControllerMappingVO()); + + Set result = VmwareHelper.getRequiredDiskControllers(controllerInfo, false); + + Assert.assertTrue(result.contains(controllerInfo.first())); + Assert.assertTrue(result.contains(controllerInfo.second())); + } + + @Test + public void getRequiredDiskControllersTestRequiresOnlyRootDiskControllerWhenInstanceIsSystemVm() { + Pair controllerInfo = new Pair<>(new DiskControllerMappingVO(), new DiskControllerMappingVO()); + + Set result = VmwareHelper.getRequiredDiskControllers(controllerInfo, true); + + Assert.assertTrue(result.contains(controllerInfo.first())); + Assert.assertFalse(result.contains(controllerInfo.second())); + } + + @Test + public void chooseDiskControllersDiskControllersTestControllersAreNotOsRecommendedReturnsProvidedControllers() throws Exception { + DiskControllerMappingVO nvmeMapping = VmwareHelper.getDiskControllerMapping("nvme", null); + DiskControllerMappingVO sataMapping = VmwareHelper.getDiskControllerMapping("sata", null); + Pair controllerInfo = new Pair<>(nvmeMapping, sataMapping); + Mockito.doReturn("VirtualLsiLogicController").when(virtualMachineMO).getRecommendedDiskController(null); + + Pair result = VmwareHelper.chooseDiskControllers(controllerInfo, virtualMachineMO, null, null); + + Assert.assertEquals(nvmeMapping, result.first()); + Assert.assertEquals(sataMapping, result.second()); + } + + @Test + public void chooseDiskControllersTestControllersAreOsRecommendedAndVmMoIsProvidedReturnsConvertedControllersBasedOnVmMo() throws Exception { + DiskControllerMappingVO osdefaultMapping = VmwareHelper.getDiskControllerMapping("osdefault", null); + Pair controllerInfo = new Pair<>(osdefaultMapping, osdefaultMapping); + Mockito.doReturn("VirtualLsiLogicController").when(virtualMachineMO).getRecommendedDiskController(null); + + Pair result = VmwareHelper.chooseDiskControllers(controllerInfo, virtualMachineMO, null, null); + + DiskControllerMappingVO lsilogicMapping = VmwareHelper.getDiskControllerMapping("lsilogic", null); + Assert.assertEquals(lsilogicMapping, result.first()); + Assert.assertEquals(lsilogicMapping, result.second()); + } + + @Test + public void chooseDiskControllersTestControllersAreOsRecommendedAndHostIsProvidedReturnsConvertedControllersBasedOnHost() throws Exception { + DiskControllerMappingVO osdefaultMapping = VmwareHelper.getDiskControllerMapping("osdefault", null); + Pair controllerInfo = new Pair<>(osdefaultMapping, osdefaultMapping); + String guestOsId = "guestOsId"; + Mockito.doReturn("VirtualLsiLogicController").when(vmwareHypervisorHostMock).getRecommendedDiskController(guestOsId); + + Pair result = VmwareHelper.chooseDiskControllers(controllerInfo, null, vmwareHypervisorHostMock, guestOsId); + + DiskControllerMappingVO lsilogicMapping = VmwareHelper.getDiskControllerMapping("lsilogic", null); + Assert.assertEquals(lsilogicMapping, result.first()); + Assert.assertEquals(lsilogicMapping, result.second()); + } + + @Test + public void chooseDiskControllersTestControllersShareTheSameBusTypeReturnsRootDiskController() throws Exception { + DiskControllerMappingVO osdefaultMapping = VmwareHelper.getDiskControllerMapping("osdefault", null); + DiskControllerMappingVO pvscsiMapping = VmwareHelper.getDiskControllerMapping("pvscsi", null); + Pair controllerInfo = new Pair<>(osdefaultMapping, pvscsiMapping); + Mockito.doReturn("VirtualLsiLogicController").when(virtualMachineMO).getRecommendedDiskController(null); + + Pair result = VmwareHelper.chooseDiskControllers(controllerInfo, virtualMachineMO, null, null); + + DiskControllerMappingVO lsilogicMapping = VmwareHelper.getDiskControllerMapping("lsilogic", null); + Assert.assertEquals(lsilogicMapping, result.first()); + Assert.assertEquals(lsilogicMapping, result.second()); + } + + @Test + public void addDiskControllersToVmConfigSpecTestDoesNotAddIdeControllers() throws Exception { + DiskControllerMappingVO ideMapping = VmwareHelper.getDiskControllerMapping("ide", null); + Set requiredControllers = new HashSet<>(); + requiredControllers.add(ideMapping); + + VmwareHelper.addDiskControllersToVmConfigSpec(virtualMachineConfigSpecMock, requiredControllers, false); + + Assert.assertEquals(0, virtualMachineConfigSpecMock.getDeviceChange().size()); + } + + @Test + public void addDiskControllersToVmConfigSpecTestMaximumAmmountOfControllersIsAdded() throws Exception { + DiskControllerMappingVO nvmeMapping = VmwareHelper.getDiskControllerMapping("nvme", null); + DiskControllerMappingVO sataMapping = VmwareHelper.getDiskControllerMapping("sata", null); + Set requiredControllers = new HashSet<>(); + requiredControllers.add(nvmeMapping); + requiredControllers.add(sataMapping); + + VmwareHelper.addDiskControllersToVmConfigSpec(virtualMachineConfigSpecMock, requiredControllers, false); + + int expectedControllerAmmount = nvmeMapping.getMaxControllerCount() + sataMapping.getMaxControllerCount(); + Assert.assertEquals(expectedControllerAmmount, virtualMachineConfigSpecMock.getDeviceChange().size()); + + Set usedKeys = new HashSet<>(); + Map> usedBusNumbers = new HashMap<>(); + usedBusNumbers.put(nvmeMapping.getControllerReference(), new HashSet<>()); + usedBusNumbers.put(sataMapping.getControllerReference(), new HashSet<>()); + for (VirtualDeviceConfigSpec virtualDeviceConfigSpec : virtualMachineConfigSpecMock.getDeviceChange()) { + Assert.assertEquals(VirtualDeviceConfigSpecOperation.ADD, virtualDeviceConfigSpec.getOperation()); + VirtualController controller = (VirtualController) virtualDeviceConfigSpec.getDevice(); + usedKeys.add(controller.getKey()); + usedBusNumbers.get(controller.getClass().getName()).add(controller.getBusNumber()); + } + Assert.assertEquals(expectedControllerAmmount, usedKeys.size()); + Assert.assertEquals((int) nvmeMapping.getMaxControllerCount(), usedBusNumbers.get(nvmeMapping.getControllerReference()).size()); + Assert.assertEquals((int) sataMapping.getMaxControllerCount(), usedBusNumbers.get(sataMapping.getControllerReference()).size()); + } + + @Test + public void addDiskControllersToVmConfigSpecTestAddedScsiControllersDoNotShareBus() throws Exception { + DiskControllerMappingVO lsilogicMapping = VmwareHelper.getDiskControllerMapping("lsilogic", null); + Set requiredControllers = new HashSet<>(); + requiredControllers.add(lsilogicMapping); + + VmwareHelper.addDiskControllersToVmConfigSpec(virtualMachineConfigSpecMock, requiredControllers, false); + + for (VirtualDeviceConfigSpec virtualDeviceConfigSpec : virtualMachineConfigSpecMock.getDeviceChange()) { + Assert.assertEquals(VirtualSCSISharing.NO_SHARING, ((VirtualSCSIController) virtualDeviceConfigSpec.getDevice()).getSharedBus()); + } + } + + @Test + public void addDiskControllersToVmConfigSpecTestInstanceIsSystemVmAddsOneController() throws Exception { + DiskControllerMappingVO lsilogicMapping = VmwareHelper.getDiskControllerMapping("lsilogic", null); + Set requiredControllers = new HashSet<>(); + requiredControllers.add(lsilogicMapping); + + VmwareHelper.addDiskControllersToVmConfigSpec(virtualMachineConfigSpecMock, requiredControllers, true); + + Assert.assertEquals(1, virtualMachineConfigSpecMock.getDeviceChange().size()); + } + + @Test + public void getDiskControllersFromVmSettingsTestReturnsSpecifiedControllersWhenInstanceIsNotSystemVm() { + Pair controllerInfo = VmwareHelper.getDiskControllersFromVmSettings("nvme", "sata", false); + + Assert.assertEquals("nvme", controllerInfo.first().getName()); + Assert.assertEquals("sata", controllerInfo.second().getName()); + } + + @Test + public void getDiskControllersFromVmSettingsTestReturnsLsiLogicForRootDiskWhenInstanceIsSystemVm() { + Pair controllerInfo = VmwareHelper.getDiskControllersFromVmSettings("nvme", "sata", true); + + Assert.assertEquals("lsilogic", controllerInfo.first().getName()); + Assert.assertEquals("sata", controllerInfo.second().getName()); + } + + @Test + public void getControllerBasedOnDiskTypeTestReturnsRootDiskControllerWhenVolumeTypeIsRoot() { + DiskControllerMappingVO rootDiskController = new DiskControllerMappingVO(); + rootDiskController.setControllerReference("root"); + Pair controllerInfo = new Pair<>(rootDiskController, new DiskControllerMappingVO()); + DiskTO disk = new DiskTO(); + disk.setType(Volume.Type.ROOT); + + DiskControllerMappingVO result = VmwareHelper.getControllerBasedOnDiskType(controllerInfo, disk); + + Assert.assertEquals(rootDiskController, result); + } + + @Test + public void getControllerBasedOnDiskTypeTestReturnsRootDiskControllerWhenVolumeDiskSeqIsZero() { + DiskControllerMappingVO rootDiskController = new DiskControllerMappingVO(); + rootDiskController.setControllerReference("root"); + Pair controllerInfo = new Pair<>(rootDiskController, new DiskControllerMappingVO()); + DiskTO disk = new DiskTO(); + disk.setDiskSeq(0L); + + DiskControllerMappingVO result = VmwareHelper.getControllerBasedOnDiskType(controllerInfo, disk); + + Assert.assertEquals(rootDiskController, result); + } + + @Test + public void getControllerBasedOnDiskTypeTestReturnsDataDiskControllerWhenVolumeTypeIsNotRootAndDiskSeqIsNotZero() { + DiskControllerMappingVO dataDiskController = new DiskControllerMappingVO(); + dataDiskController.setControllerReference("data"); + Pair controllerInfo = new Pair<>(new DiskControllerMappingVO(), dataDiskController); + DiskTO disk = new DiskTO(); + disk.setType(Volume.Type.DATADISK); + disk.setDiskSeq(1L); + + DiskControllerMappingVO result = VmwareHelper.getControllerBasedOnDiskType(controllerInfo, disk); + + Assert.assertEquals(dataDiskController, result); + } + + @Test + public void configureDiskControllerMappingsInVmwareBaseModuleTestOsdefaultIsConfigured() { + DiskControllerMappingVO osdefaultMappingVo = new DiskControllerMappingVO(); + osdefaultMappingVo.setName("osdefault"); + osdefaultMappingVo.setControllerReference("osdefault"); + + VmwareHelper.configureDiskControllerMappingsInVmwareBaseModule(List.of(osdefaultMappingVo)); + + Assert.assertEquals(List.of(osdefaultMappingVo), VmwareHelper.getAllSupportedDiskControllerMappings()); + } + + @Test + public void configureDiskControllerMappingsInVmwareBaseModuleTestLsiLogicIsConfigured() { + DiskControllerMappingVO lsilogicMappingVo = new DiskControllerMappingVO(); + lsilogicMappingVo.setName("lsilogic"); + lsilogicMappingVo.setControllerReference(VirtualLsiLogicController.class.getName()); + + VmwareHelper.configureDiskControllerMappingsInVmwareBaseModule(List.of(lsilogicMappingVo)); + + Assert.assertEquals(List.of(lsilogicMappingVo), VmwareHelper.getAllSupportedDiskControllerMappings()); + } + + @Test + public void configureDiskControllerMappingsInVmwareBaseModuleTestInvalidMappingIsNotConfigured() { + DiskControllerMappingVO invalidMappingVo = new DiskControllerMappingVO(); + invalidMappingVo.setName("invalid"); + invalidMappingVo.setControllerReference("invalid"); + + VmwareHelper.configureDiskControllerMappingsInVmwareBaseModule(List.of(invalidMappingVo)); + + Assert.assertEquals(0, VmwareHelper.getAllSupportedDiskControllerMappings().size()); + } }