This commit is contained in:
Fabricio Duarte 2026-03-09 14:14:28 +01:00 committed by GitHub
commit 4906801a8f
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
29 changed files with 2124 additions and 855 deletions

View File

@ -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();
}

View File

@ -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<DiskControllerMappingVO> supportedDiskControllers;
public SecStorageVMSetupCommand() {
super();
}
@ -60,4 +65,11 @@ public class SecStorageVMSetupCommand extends Command {
this.copyPassword = copyPassword;
}
public List<DiskControllerMappingVO> getSupportedDiskControllers() {
return supportedDiskControllers;
}
public void setSupportedDiskControllers(List<DiskControllerMappingVO> supportedDiskControllers) {
this.supportedDiskControllers = supportedDiskControllers;
}
}

View File

@ -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");
}
}

View File

@ -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, Long> {
DiskControllerMappingVO findDiskControllerMapping(String name, String classReference, HypervisorType hypervisor);
List<DiskControllerMappingVO> listForHypervisor(HypervisorType hypervisor);
}

View File

@ -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<DiskControllerMappingVO, Long> implements DiskControllerMappingDao {
private SearchBuilder<DiskControllerMappingVO> 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<DiskControllerMappingVO> sc = diskControllerMappingSearch.create();
sc.setParametersIfNotNull("name", name);
sc.setParametersIfNotNull("controllerReference", controllerReference);
sc.setParameters("hypervisor", hypervisor);
return findOneBy(sc);
}
@Override
public List<DiskControllerMappingVO> listForHypervisor(HypervisorType hypervisor) {
SearchCriteria<DiskControllerMappingVO> sc = diskControllerMappingSearch.create();
sc.setParameters("hypervisor", hypervisor);
return listBy(sc);
}
}

View File

@ -67,6 +67,7 @@
<bean id="dataCenterJoinDaoImpl" class="com.cloud.api.query.dao.DataCenterJoinDaoImpl" />
<bean id="domainVlanMapDaoImpl" class="com.cloud.dc.dao.DomainVlanMapDaoImpl" />
<bean id="engineDcDetailsDaoImpl" class="org.apache.cloudstack.engine.datacenter.entity.api.db.dao.DcDetailsDaoImpl" />
<bean id="diskControllerMappingDaoImpl" class="org.apache.cloudstack.storage.dao.DiskControllerMappingDaoImpl" />
<bean id="diskOfferingJoinDaoImpl" class="com.cloud.api.query.dao.DiskOfferingJoinDaoImpl" />
<bean id="domainDaoImpl" class="com.cloud.domain.dao.DomainDaoImpl" />
<bean id="domainDetailsDaoImpl" class="com.cloud.domain.dao.DomainDetailsDaoImpl" />

View File

@ -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;

View File

@ -49,3 +49,32 @@ CREATE TABLE IF NOT EXISTS `cloud`.`webhook_filter` (
INDEX `i_webhook_filter__webhook_id`(`webhook_id`),
CONSTRAINT `fk_webhook_filter__webhook_id` FOREIGN KEY(`webhook_id`) REFERENCES `webhook`(`id`) ON DELETE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
-- 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');

View File

@ -322,7 +322,7 @@ class VmwareVmImplementer {
}
if (vm.getType() == VirtualMachine.Type.NetScalerVm) {
details.put(VmDetailConstants.ROOT_DISK_CONTROLLER, "scsi");
details.put(VmDetailConstants.ROOT_DISK_CONTROLLER, DiskControllerType.scsi.toString());
}
}

View File

@ -60,7 +60,9 @@ import org.apache.cloudstack.framework.config.Configurable;
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.CheckDataStoreStoragePolicyComplianceCommand;
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;
@ -235,6 +237,7 @@ public class VmwareManagerImpl extends ManagerBase implements VmwareManager, Vmw
@Inject
private VMInstanceDao vmInstanceDao;
@Inject
private DiskControllerMappingDao diskControllerMappingDao;
private UserVmCloneSettingDao cloneSettingDao;
@Inject
private TemplateManager templateManager;
@ -392,6 +395,9 @@ public class VmwareManagerImpl extends ManagerBase implements VmwareManager, Vmw
_agentMgr.registerForHostEvents(this, true, true, true);
List<DiskControllerMappingVO> mappingsInDatabase = diskControllerMappingDao.listForHypervisor(Hypervisor.HypervisorType.VMware);
VmwareHelper.configureDiskControllerMappingsInVmwareBaseModule(mappingsInDatabase);
logger.info("VmwareManagerImpl has been successfully configured");
return true;
}

View File

@ -57,11 +57,15 @@ import com.vmware.vim25.FileQueryFlags;
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 com.vmware.vim25.VirtualTPM;
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;
@ -246,7 +250,6 @@ import com.cloud.hypervisor.vmware.mo.CustomFieldsManagerMO;
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;
@ -374,7 +377,6 @@ import com.vmware.vim25.VirtualMachineRelocateSpecDiskLocator;
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;
@ -417,7 +419,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<VmwareContext> s_serviceContext = new ThreadLocal<>();
@ -1973,61 +1974,157 @@ public class VmwareResource extends ServerResourceBase implements StoragePoolRes
return new ScaleVmAnswer(cmd, true, null);
}
protected void ensureDiskControllers(VirtualMachineMO vmMo, Pair<String, String> 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<DiskControllerMappingVO, DiskControllerMappingVO> controllerInfo,
boolean isSystemVm) throws Exception {
Pair<DiskControllerMappingVO, DiskControllerMappingVO> chosenDiskControllers = VmwareHelper.chooseDiskControllers(controllerInfo, vmMo, null, null);
Set<DiskControllerMappingVO> requiredDiskControllers = VmwareHelper.getRequiredDiskControllers(chosenDiskControllers, isSystemVm);
if (vmHasRequiredControllers(vmMo, requiredDiskControllers)) {
return;
}
Pair<String, String> chosenDiskControllers = VmwareHelper.chooseRequiredDiskControllers(controllerInfo, vmMo, null, null);
String scsiDiskController = HypervisorHostHelper.getScsiController(chosenDiskControllers);
if (scsiDiskController == null) {
return;
}
validateRequiredVirtualHardwareVersionForNewDiskControllers(vmMo, requiredDiskControllers);
validateNewDiskControllersSupportExistingDisks(vmMo, chosenDiskControllers);
teardownAllDiskControllers(vmMo);
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<Integer, Integer, DiskControllerType> scsiControllerInfo = vmMo.getScsiControllerInfo();
int requiredNumScsiControllers = VmwareHelper.MAX_SCSI_CONTROLLER_COUNT - scsiControllerInfo.first();
int availableBusNum = scsiControllerInfo.second() + 1; // method returned current max. bus number
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;
VirtualMachineConfigSpec vmConfigSpec = new VirtualMachineConfigSpec();
VmwareHelper.addDiskControllersToVmConfigSpec(vmConfigSpec, requiredDiskControllers, isSystemVm);
if (!vmMo.configureVm(vmConfigSpec)) {
throw new CloudRuntimeException("Unable to configure virtual machine's disk controllers.");
}
if (requiredNumScsiControllers == 0) {
return;
}
if (scsiControllerInfo.first() > 0) {
// For VMs which already have a SCSI controller, do NOT attempt to add any more SCSI controllers & return the sub type.
// For Legacy VMs would have only 1 LsiLogic Parallel SCSI controller, and doesn't require more.
// For VMs created post device ordering support, 4 SCSI subtype controllers are ensured during deployment itself. No need to add more.
// For fresh VM deployment only, all required controllers should be ensured.
return;
}
ensureScsiDiskControllers(vmMo, scsiDiskController, requiredNumScsiControllers, availableBusNum);
logger.info("Successfully added virtual machine [{}]'s required disk controllers [{}].", vmMo, requiredDiskControllers);
}
private void ensureScsiDiskControllers(VirtualMachineMO vmMo, String scsiDiskController, int requiredNumScsiControllers, int availableBusNum) throws Exception {
// Pick the sub type of scsi
if (DiskControllerType.getType(scsiDiskController) == DiskControllerType.pvscsi) {
if (!vmMo.isPvScsiSupported()) {
String msg = "This VM doesn't support Vmware Paravirtual SCSI controller for virtual disks, because the virtual hardware version is less than 7.";
throw new Exception(msg);
}
vmMo.ensurePvScsiDeviceController(requiredNumScsiControllers, availableBusNum);
} else if (DiskControllerType.getType(scsiDiskController) == DiskControllerType.lsisas1068) {
vmMo.ensureLsiLogicSasDeviceControllers(requiredNumScsiControllers, availableBusNum);
} else if (DiskControllerType.getType(scsiDiskController) == DiskControllerType.buslogic) {
vmMo.ensureBusLogicDeviceControllers(requiredNumScsiControllers, availableBusNum);
} else if (DiskControllerType.getType(scsiDiskController) == DiskControllerType.lsilogic) {
vmMo.ensureLsiLogicDeviceControllers(requiredNumScsiControllers, availableBusNum);
protected boolean vmHasRequiredControllers(VirtualMachineMO vmMo, Set<DiskControllerMappingVO> requiredDiskControllers) throws Exception {
Set<String> requiredDiskControllerClasspaths = requiredDiskControllers.stream()
.map(DiskControllerMappingVO::getControllerReference)
.collect(Collectors.toSet());
List<VirtualController> existingControllers = vmMo.getControllers();
for (VirtualController controller : existingControllers) {
String classpath = controller.getClass().getName();
requiredDiskControllerClasspaths.remove(classpath);
}
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 <code>VirtualMachineMO</code>'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<DiskControllerMappingVO> requiredDiskControllers) throws Exception {
int hardwareVersion = vmMo.getVirtualHardwareVersion();
List<DiskControllerMappingVO> 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));
}
}
/***
* 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<DiskControllerMappingVO, DiskControllerMappingVO> controllerInfo) throws Exception {
DiskControllerMappingVO rootDiskController = controllerInfo.first();
DiskControllerMappingVO dataDiskController = controllerInfo.second();
List<VirtualDisk> 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;
}
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));
}
}
/**
* 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<DiskControllerMappingVO> allMappings = VmwareHelper.getAllSupportedDiskControllerMappingsExceptOsDefault();
Set<Class<?>> 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.tearDownDevices(classesToTeardown.toArray(new Class<?>[0]));
}
protected StartAnswer execute(StartCommand cmd) {
@ -2051,7 +2148,7 @@ public class VmwareResource extends ServerResourceBase implements StoragePoolRes
String vmNameOnVcenter = names.second();
DiskTO rootDiskTO = null;
String bootMode = getBootModeFromVmSpec(vmSpec, deployAsIs);
Pair<String, String> controllerInfo = getControllerInfoFromVmSpec(vmSpec);
Pair<DiskControllerMappingVO, DiskControllerMappingVO> 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 @@ public class VmwareResource extends ServerResourceBase implements StoragePoolRes
VirtualMachineDiskInfoBuilder diskInfoBuilder = null;
VirtualMachineMO vmMo = hyperHost.findVmOnHyperHost(vmInternalCSName);
DiskControllerType systemVmScsiControllerType = DiskControllerType.lsilogic;
int firstScsiControllerBusNum = 0;
int numScsiControllerForSystemVm = 1;
boolean hasSnapshot = false;
List<Pair<Integer, ManagedObjectReference>> diskDatastores = null;
@ -2104,8 +2198,7 @@ public class VmwareResource extends ServerResourceBase implements StoragePoolRes
hasSnapshot = vmMo.hasSnapshot();
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);
@ -2128,8 +2221,7 @@ public class VmwareResource extends ServerResourceBase implements StoragePoolRes
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).
@ -2225,10 +2317,6 @@ public class VmwareResource extends ServerResourceBase implements StoragePoolRes
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)),
@ -2270,8 +2358,12 @@ public class VmwareResource extends ServerResourceBase implements StoragePoolRes
//
// Setup ISO device
//
Pair<DiskControllerMappingVO, DiskControllerMappingVO> chosenDiskControllers = VmwareHelper.chooseDiskControllers(controllerInfo, vmMo, null, null);
Map<String, Integer> 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<String, Long> secStoreUrlAndId = mgr.getSecondaryStorageStoreUrlAndId(Long.parseLong(_dcId));
@ -2290,8 +2382,9 @@ public class VmwareResource extends ServerResourceBase implements StoragePoolRes
DatastoreMO secDsMo = new DatastoreMO(hyperHost.getContext(), morSecDs);
deviceConfigSpecArray[i] = new VirtualDeviceConfigSpec();
Pair<VirtualDevice, Boolean> isoInfo = VmwareHelper.prepareIsoDevice(vmMo,
null, secDsMo.getMor(), true, true, ideUnitNumber++, i + 1);
Pair<VirtualDevice, Boolean> 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())
@ -2309,12 +2402,14 @@ public class VmwareResource extends ServerResourceBase implements StoragePoolRes
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<VirtualDevice, Boolean> 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())
@ -2331,9 +2426,6 @@ public class VmwareResource extends ServerResourceBase implements StoragePoolRes
}
}
int controllerKey;
Pair<String, String> chosenDiskControllers = VmwareHelper.chooseRequiredDiskControllers(controllerInfo,vmMo, null, null);
//
// Setup ROOT/DATA disk devices
//
@ -2345,6 +2437,7 @@ public class VmwareResource extends ServerResourceBase implements StoragePoolRes
if (vol.getType() == Volume.Type.ISO) {
if (deployAsIs) {
configureIso(hyperHost, vmMo, vol, deviceConfigSpecArray, ideUnitNumber++, i);
diskControllerCurrentUnitNumbers.replace(ideClasspath, ideUnitNumber);
i++;
}
continue;
@ -2360,48 +2453,23 @@ public class VmwareResource extends ServerResourceBase implements StoragePoolRes
continue;
}
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 including 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<Integer, Integer, DiskControllerType> 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();
String[] diskChain = syncDiskChain(dcMo, vmMo, vol, matchingExistingDisk, volumeDsDetails.second());
int deviceNumber;
if (controllerKey == vmMo.getIDEControllerKey(ideUnitNumber)) {
deviceNumber = ideUnitNumber % VmwareHelper.MAX_ALLOWED_DEVICES_IDE_CONTROLLER;
ideUnitNumber++;
} else {
deviceNumber = scsiUnitNumber % VmwareHelper.MAX_ALLOWED_DEVICES_SCSI_CONTROLLER;
scsiUnitNumber++;
}
VolumeObjectTO volumeTO = (VolumeObjectTO) vol.getData();
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();
@ -2425,11 +2493,14 @@ public class VmwareResource extends ServerResourceBase implements StoragePoolRes
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);
}
}
}
@ -2673,6 +2744,38 @@ public class VmwareResource extends ServerResourceBase implements StoragePoolRes
}
}
/**
* 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<String, Integer> createDiskControllerUnitNumberMap(VirtualMachineMO vmMo, Pair<DiskControllerMappingVO, DiskControllerMappingVO> controllerInfo,
boolean deployAsIs) throws Exception {
Set<DiskControllerMappingVO> existingDiskControllers = new HashSet<>();
existingDiskControllers.add(controllerInfo.first());
existingDiskControllers.add(controllerInfo.second());
existingDiskControllers.add(VmwareHelper.getDiskControllerMapping(null, VirtualIDEController.class.getName()));
Map<String, Integer> currentUnitNumberMap = new HashMap<>();
for (DiskControllerMappingVO diskController : existingDiskControllers) {
String classpath = diskController.getControllerReference();
if (!deployAsIs) {
currentUnitNumberMap.put(classpath, 0);
continue;
}
Pair<Integer, Integer> 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 Pair<ManagedObjectReference, DatastoreMO> getVolumeDatastoreDetails(DiskTO vol, HashMap<String, Pair<ManagedObjectReference, DatastoreMO>> dataStoresDetails) throws Exception {
boolean managed = false;
String iScsiName = null;
@ -2839,15 +2942,10 @@ public class VmwareResource extends ServerResourceBase implements StoragePoolRes
}
private void ensureDiskControllersInternal(VirtualMachineMO vmMo, Boolean systemVm,
Pair<String, String> 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<DiskControllerMappingVO, DiskControllerMappingVO> controllerInfo,
boolean deployAsIs) throws Exception {
if (systemVm || !deployAsIs) {
ensureDiskControllers(vmMo, controllerInfo, systemVm);
}
}
@ -2871,11 +2969,11 @@ public class VmwareResource extends ServerResourceBase implements StoragePoolRes
return translateGuestOsIdentifier(vmSpec.getArch(), vmSpec.getOs(), vmSpec.getPlatformEmulator()).value();
}
private Pair<String, String> getControllerInfoFromVmSpec(VirtualMachineTO vmSpec) throws CloudRuntimeException {
protected Pair<DiskControllerMappingVO, DiskControllerMappingVO> 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) {
@ -3655,23 +3753,32 @@ public class VmwareResource extends ServerResourceBase implements StoragePoolRes
return getMatchingExistingDiskWithVolumeDetails(diskInfoBuilder, volume.getPath(), chainInfo, isManaged, iScsiName, datastoreUUID, hyperHost, context);
}
private String getDiskController(VirtualMachineMO vmMo, VirtualMachineDiskInfo matchingExistingDisk, DiskTO vol, Pair<String, String> 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 <code>controllerInfo</code>.
* @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<DiskControllerMappingVO, DiskControllerMappingVO> 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<DiskControllerMappingVO> 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<Integer, Integer, DiskControllerType> vmScsiControllerInfo = vmMo.getScsiControllerInfo();
controllerType = vmScsiControllerInfo.third();
}
return controllerType.toString();
DiskControllerMappingVO existingAvailableDiskController = vmMo.getAnyExistingAvailableDiskController();
logger.debug("Falling back to existing disk controller [{}] for virtual machine [{}] as no matching controller was found based on the current bus name [{}].",
existingAvailableDiskController, vmMo, currentBusName);
return existingAvailableDiskController;
}
return VmwareHelper.getControllerBasedOnDiskType(controllerInfo, vol);
@ -6979,14 +7086,6 @@ public class VmwareResource extends ServerResourceBase implements StoragePoolRes
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;

View File

@ -18,6 +18,7 @@ package com.cloud.storage.resource;
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 class VmwareSecondaryStorageResourceHandler implements SecondaryStorageRe
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 @@ public class VmwareSecondaryStorageResourceHandler implements SecondaryStorageRe
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");

View File

@ -35,6 +35,7 @@ import java.util.concurrent.Executors;
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.CheckDataStoreStoragePolicyComplianceCommand;
@ -76,7 +77,6 @@ import com.cloud.hypervisor.vmware.mo.CustomFieldConstants;
import com.cloud.hypervisor.vmware.mo.DatacenterMO;
import com.cloud.hypervisor.vmware.mo.DatastoreFile;
import com.cloud.hypervisor.vmware.mo.DatastoreMO;
import com.cloud.hypervisor.vmware.mo.DiskControllerType;
import com.cloud.hypervisor.vmware.mo.HostDatastoreSystemMO;
import com.cloud.hypervisor.vmware.mo.HostMO;
import com.cloud.hypervisor.vmware.mo.HostStorageSystemMO;
@ -2102,18 +2102,19 @@ public class VmwareStorageProcessor implements StorageProcessor {
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<String, String> chosenDiskControllers = VmwareHelper.chooseRequiredDiskControllers(new Pair<>(rootDiskControllerDetail, dataDiskControllerDetail), vmMo, null, null);
String diskController = VmwareHelper.getControllerBasedOnDiskType(chosenDiskControllers, disk);
// Let's first find which disk controller should be used for the volume being attached.
//
// `controllerInfo` can not be null here. It is always defined when creating the `AttachComand` in
// `com.cloud.storage.VolumeApiServiceImpl#sendAttachVolumeCommand`.
//
// If `VmDetailConstants.ROOT_DISK_CONTROLLER` or `VmDetailConstants.DATA_DISK_CONTROLLER` are not present
// in `controllerInfo`, `com.cloud.hypervisor.vmware.util.VmwareHelper#getDiskControllersFromVmSettings`
// will return default values.
String rootDiskControllerDetail = controllerInfo.get(VmDetailConstants.ROOT_DISK_CONTROLLER);
String dataDiskControllerDetail = controllerInfo.get(VmDetailConstants.DATA_DISK_CONTROLLER);
Pair<DiskControllerMappingVO, DiskControllerMappingVO> specifiedDiskControllers = VmwareHelper.getDiskControllersFromVmSettings(rootDiskControllerDetail, dataDiskControllerDetail, false);
Pair<DiskControllerMappingVO, DiskControllerMappingVO> 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();
@ -3751,10 +3752,6 @@ public class VmwareStorageProcessor implements StorageProcessor {
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);

View File

@ -73,6 +73,7 @@ import org.apache.cloudstack.api.command.admin.zone.RemoveVmwareDcCmd;
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 class VmwareDatacenterApiUnitTest {
return Mockito.mock(VsphereStoragePolicyDao.class);
}
@Bean
public DiskControllerMappingDao diskControllerMappingDao() {
return Mockito.mock(DiskControllerMappingDao.class);
}
@Bean
public StorageManager storageManager() {
return Mockito.mock(StorageManager.class);

View File

@ -38,24 +38,40 @@ import java.util.Collections;
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.Command;
import com.cloud.agent.api.ScaleVmAnswer;
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 com.vmware.vim25.VirtualTPM;
import org.apache.cloudstack.api.ApiConstants;
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.Assert;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
@ -144,6 +160,8 @@ public class VmwareResourceTest {
@Mock
VirtualMachineTO vmSpec;
@Mock
DiskTO diskTo;
@Mock
VmwareHypervisorHost hyperHost;
@Mock
VirtualMachineMO vmMo;
@ -186,8 +204,11 @@ public class VmwareResourceTest {
@Mock
HostCapability hostCapability;
@Mock
VirtualMachineDiskInfo virtualMachineDiskInfo;
@Mock
ManagedObjectReference _morHyperHost;
CopyCommand storageCmd;
EnumMap<VmwareStorageProcessorConfigurableFields, Object> params = new EnumMap<VmwareStorageProcessorConfigurableFields,Object>(VmwareStorageProcessorConfigurableFields.class);
@ -232,6 +253,8 @@ public class VmwareResourceTest {
when(context.getService()).thenReturn(vimService);
when(vimService.queryTargetCapabilities(envRef, hostRef)).thenReturn(hostCapability);
when(hostCapability.isNestedHVSupported()).thenReturn(true);
configureSupportedDiskControllersForTests();
}
//Test successful scaling up the vm
@ -838,6 +861,361 @@ public class VmwareResourceTest {
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<DiskControllerMappingVO> getRequiredDiskControllersForValidateRequiredVirtualHardwareVersionForNewDiskControllersTests() {
DiskControllerMappingVO lsilogicMapping = VmwareHelper.getDiskControllerMapping("lsilogic", null);
DiskControllerMappingVO nvmeMapping = VmwareHelper.getDiskControllerMapping("nvme", null);
Set<DiskControllerMappingVO> requiredDiskControllers = new HashSet<>();
requiredDiskControllers.add(lsilogicMapping);
requiredDiskControllers.add(nvmeMapping);
return requiredDiskControllers;
}
@Test
public void validateRequiredVirtualHardwareVersionForNewDiskControllersTestDoesNothingWhenControllerWithSupportedVirtualHardwareVersionIsRequired() throws Exception {
Set<DiskControllerMappingVO> requiredDiskControllers = getRequiredDiskControllersForValidateRequiredVirtualHardwareVersionForNewDiskControllersTests();
Mockito.when(vmMo.getVirtualHardwareVersion()).thenReturn(13);
vmwareResource.validateRequiredVirtualHardwareVersionForNewDiskControllers(vmMo, requiredDiskControllers);
}
@Test(expected = CloudRuntimeException.class)
public void validateRequiredVirtualHardwareVersionForNewDiskControllersTestThrowsExceptionWhenControllerWithGreaterVirtualHardwareVersionIsRequired() throws Exception {
Set<DiskControllerMappingVO> requiredDiskControllers = getRequiredDiskControllersForValidateRequiredVirtualHardwareVersionForNewDiskControllersTests();
Mockito.when(vmMo.getVirtualHardwareVersion()).thenReturn(8);
vmwareResource.validateRequiredVirtualHardwareVersionForNewDiskControllers(vmMo, requiredDiskControllers);
}
private void createDisksForValidateNewDiskControllersSupportExistingDisksTests(int numberOfDisks) throws Exception {
List<VirtualDisk> 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<DiskControllerMappingVO, DiskControllerMappingVO> 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<DiskControllerMappingVO, DiskControllerMappingVO> 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<DiskControllerMappingVO, DiskControllerMappingVO> 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<DiskControllerMappingVO, DiskControllerMappingVO> 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<DiskControllerMappingVO, DiskControllerMappingVO> controllerInfo = new Pair<>(nvmeMapping, lsilogicMapping);
Map<String, Integer> 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<DiskControllerMappingVO, DiskControllerMappingVO> controllerInfo = new Pair<>(nvmeMapping, lsilogicMapping);
Map<String, Integer> 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<DiskControllerMappingVO, DiskControllerMappingVO> 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<DiskControllerMappingVO, DiskControllerMappingVO> 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<DiskControllerMappingVO, DiskControllerMappingVO> controllerInfo = new Pair<>(osdefaultMapping, osdefaultMapping);
try (MockedStatic<VmwareHelper> vmwareHelperMock = Mockito.mockStatic(VmwareHelper.class)) {
DiskControllerMappingVO nvmeMapping = VmwareHelper.getDiskControllerMapping("nvme", null);
Pair<DiskControllerMappingVO, DiskControllerMappingVO> chosenDiskControllers = new Pair<>(nvmeMapping, nvmeMapping);
vmwareHelperMock.when(() -> VmwareHelper.chooseDiskControllers(controllerInfo, vmMo, null, null)).thenReturn(chosenDiskControllers);
Set<DiskControllerMappingVO> 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<DiskControllerMappingVO, DiskControllerMappingVO> controllerInfo = new Pair<>(osdefaultMapping, osdefaultMapping);
try (MockedStatic<VmwareHelper> vmwareHelperMock = Mockito.mockStatic(VmwareHelper.class)) {
DiskControllerMappingVO nvmeMapping = VmwareHelper.getDiskControllerMapping("nvme", null);
Pair<DiskControllerMappingVO, DiskControllerMappingVO> chosenDiskControllers = new Pair<>(nvmeMapping, nvmeMapping);
vmwareHelperMock.when(() -> VmwareHelper.chooseDiskControllers(controllerInfo, vmMo, null, null)).thenReturn(chosenDiskControllers);
Set<DiskControllerMappingVO> 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<DiskControllerMappingVO> 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<DiskControllerMappingVO> 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<String, String> 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<DiskControllerMappingVO, DiskControllerMappingVO> expectedResult = new Pair<>(expectedMapping, expectedMapping);
try (MockedStatic<VmwareHelper> vmwareHelperMock = Mockito.mockStatic(VmwareHelper.class)) {
vmwareHelperMock.when(() -> VmwareHelper.getDiskControllersFromVmSettings(rootDiskControllerDetail, dataDiskControllerDetail, true)).thenReturn(expectedResult);
Pair<DiskControllerMappingVO, DiskControllerMappingVO> result = vmwareResource.getControllerInfoFromVmSpec(vmSpec);
Assert.assertEquals(expectedResult, result);
}
}
@Test
public void getControllerForDiskTestChoosesControllerFromControllerInfoWhenTemplateIsNotDeployAsIs() throws Exception {
Pair<DiskControllerMappingVO, DiskControllerMappingVO> controllerInfo = new Pair<>(new DiskControllerMappingVO(), new DiskControllerMappingVO());
try (MockedStatic<VmwareHelper> 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<DiskControllerMappingVO, DiskControllerMappingVO> 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<DiskControllerMappingVO, DiskControllerMappingVO> 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);
}
@Test
public void testAddVirtualTPMDevice() throws Exception {
VirtualMachineMO vmMo = Mockito.mock(VirtualMachineMO.class);

View File

@ -170,6 +170,8 @@ import org.apache.cloudstack.resourcedetail.dao.DiskOfferingDetailsDao;
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;
@ -625,6 +627,9 @@ public class QueryManagerImpl extends MutualExclusiveIdsManagerBase implements Q
@Inject
ManagementServerHostPeerJoinDao mshostPeerJoinDao;
@Inject
private DiskControllerMappingDao diskControllerMappingDao;
@Inject
private AsyncJobManager jobManager;
@ -5433,8 +5438,12 @@ public class QueryManagerImpl extends MutualExclusiveIdsManagerBase implements Q
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<String> availableDiskControllers = diskControllerMappingDao.listForHypervisor(HypervisorType.VMware).stream()
.map(DiskControllerMappingVO::getName)
.sorted()
.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());

View File

@ -47,6 +47,8 @@ import org.apache.cloudstack.framework.config.ConfigKey;
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;
@ -252,6 +254,8 @@ public class SecondaryStorageManagerImpl extends ManagerBase implements Secondar
@Inject
VolumeDataStoreDao _volumeStoreDao;
@Inject
DiskControllerMappingDao diskControllerMappingDao;
@Inject
private ImageStoreDetailsUtil imageStoreDetailsUtil;
@Inject
private IndirectAgentLB indirectAgentLB;
@ -382,6 +386,12 @@ public class SecondaryStorageManagerImpl extends ManagerBase implements Secondar
setupCmd.setCopyPassword(copyPasswd);
setupCmd.setCopyUserName(TemplateConstants.DEFAULT_HTTP_AUTH_USER);
List<DiskControllerMappingVO> 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()) {

View File

@ -47,6 +47,11 @@
<artifactId>cloud-engine-api</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>org.apache.cloudstack</groupId>
<artifactId>cloud-engine-schema</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>com.google.code.gson</groupId>
<artifactId>gson</artifactId>

View File

@ -51,6 +51,9 @@ public class BaseMO {
private String _name;
protected BaseMO() {
}
public BaseMO(VmwareContext context, ManagedObjectReference mor) {
assert (context != null);

View File

@ -22,6 +22,7 @@ import java.util.Arrays;
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 class ClusterMO extends BaseMO implements VmwareHypervisorHost {
@Override
public boolean createBlankVm(String vmName, String vmInternalCSName, int cpuCount, int cpuSpeedMHz, int cpuReservedMHz, boolean limitCpuUse, int memoryMB,
int memoryReserveMB, String guestOsIdentifier, ManagedObjectReference morDs, boolean snapshotDirToParent, Pair<String, String> controllerInfo, Boolean systemVm) throws Exception {
int memoryReserveMB, String guestOsIdentifier, ManagedObjectReference morDs, boolean snapshotDirToParent, Pair<DiskControllerMappingVO, DiskControllerMappingVO> controllerInfo, Boolean systemVm) throws Exception {
if (logger.isTraceEnabled())
logger.trace("vCenter API trace - createBlankVm(). target MOR: " + _mor.getValue() + ", vmName: " + vmName + ", cpuCount: " + cpuCount + ", cpuSpeedMhz: " +

View File

@ -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;
}
}
}

View File

@ -24,6 +24,7 @@ import java.util.List;
import java.util.Map;
import java.util.regex.Pattern;
import org.apache.cloudstack.storage.DiskControllerMappingVO;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
@ -672,7 +673,7 @@ public class HostMO extends BaseMO implements VmwareHypervisorHost {
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");
@ -697,7 +698,7 @@ public class HostMO extends BaseMO implements VmwareHypervisorHost {
@Override
public boolean createBlankVm(String vmName, String vmInternalCSName, int cpuCount, int cpuSpeedMHz, int cpuReservedMHz, boolean limitCpuUse, int memoryMB,
int memoryReserveMB, String guestOsIdentifier, ManagedObjectReference morDs, boolean snapshotDirToParent, Pair<String, String> controllerInfo, Boolean systemVm) throws Exception {
int memoryReserveMB, String guestOsIdentifier, ManagedObjectReference morDs, boolean snapshotDirToParent, Pair<DiskControllerMappingVO, DiskControllerMappingVO> controllerInfo, Boolean systemVm) throws Exception {
if (logger.isTraceEnabled())
logger.trace("vCenter API trace - createBlankVm(). target MOR: " + _mor.getValue() + ", vmName: " + vmName + ", cpuCount: " + cpuCount + ", cpuSpeedMhz: " +

View File

@ -28,6 +28,7 @@ import java.util.Arrays;
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.dom.DOMSource;
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;
@ -119,7 +121,6 @@ import com.vmware.vim25.OvfCreateImportSpecParams;
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;
@ -127,15 +128,12 @@ import com.vmware.vim25.VMwareDVSPortSetting;
import com.vmware.vim25.VMwareDVSPortgroupPolicy;
import com.vmware.vim25.VMwareDVSPvlanConfigSpec;
import com.vmware.vim25.VMwareDVSPvlanMapEntry;
import com.vmware.vim25.VirtualBusLogicController;
import com.vmware.vim25.VirtualController;
import com.vmware.vim25.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;
@ -1569,12 +1567,11 @@ public class HypervisorHostHelper {
public static boolean createBlankVm(VmwareHypervisorHost host, String vmName, String vmInternalCSName, int cpuCount, int cpuSpeedMHz, int cpuReservedMHz,
boolean limitCpuUse, int memoryMB, int memoryReserveMB, String guestOsIdentifier, ManagedObjectReference morDs, boolean snapshotDirToParent,
Pair<String, String> controllerInfo, Boolean systemVm) throws Exception {
Pair<DiskControllerMappingVO, DiskControllerMappingVO> 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);
@ -1583,23 +1580,9 @@ public class HypervisorHostHelper {
VmwareHelper.setBasicVmConfig(vmConfig, cpuCount, cpuSpeedMHz, cpuReservedMHz, memoryMB, memoryReserveMB, guestOsIdentifier, limitCpuUse, false);
Pair<String, String> 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<DiskControllerMappingVO, DiskControllerMappingVO> chosenDiskControllers = VmwareHelper.chooseDiskControllers(controllerInfo, null, host, guestOsIdentifier);
Set<DiskControllerMappingVO> 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);
@ -1698,34 +1681,6 @@ public class HypervisorHostHelper {
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
@ -2258,35 +2213,6 @@ public class HypervisorHostHelper {
return morHyperHost;
}
public static String getScsiController(Pair<String, String> 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());

View File

@ -31,7 +31,9 @@ import java.util.ArrayList;
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;
@ -46,6 +48,7 @@ import com.vmware.vim25.RuntimeFaultFaultMsg;
import com.vmware.vim25.TaskInfo;
import com.vmware.vim25.TaskInfoState;
import com.vmware.vim25.VirtualMachineTicket;
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;
@ -78,11 +81,9 @@ import com.vmware.vim25.OptionValue;
import com.vmware.vim25.OvfCreateDescriptorParams;
import com.vmware.vim25.OvfCreateDescriptorResult;
import com.vmware.vim25.OvfFile;
import com.vmware.vim25.ParaVirtualSCSIController;
import com.vmware.vim25.PropertyFilterSpec;
import com.vmware.vim25.PropertySpec;
import com.vmware.vim25.TraversalSpec;
import com.vmware.vim25.VirtualBusLogicController;
import com.vmware.vim25.VirtualCdrom;
import com.vmware.vim25.VirtualCdromIsoBackingInfo;
import com.vmware.vim25.VirtualCdromRemotePassthroughBackingInfo;
@ -106,7 +107,6 @@ import com.vmware.vim25.VirtualEthernetCardDistributedVirtualPortBackingInfo;
import com.vmware.vim25.VirtualHardwareOption;
import com.vmware.vim25.VirtualIDEController;
import com.vmware.vim25.VirtualLsiLogicController;
import com.vmware.vim25.VirtualLsiLogicSASController;
import com.vmware.vim25.VirtualMachineCloneSpec;
import com.vmware.vim25.VirtualMachineConfigInfo;
import com.vmware.vim25.VirtualMachineConfigOption;
@ -146,6 +146,10 @@ public class VirtualMachineMO extends BaseMO {
this.internalCSName = internalVMName;
}
protected VirtualMachineMO() {
super();
}
public VirtualMachineMO(VmwareContext context, ManagedObjectReference morVm) {
super(context, morVm);
}
@ -1370,13 +1374,11 @@ public class VirtualMachineMO extends BaseMO {
_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);
}
@ -1430,45 +1432,40 @@ public class VirtualMachineMO extends BaseMO {
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 = 0;
int unitNumber = 0;
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 (DiskControllerType.getType(diskController) == DiskControllerType.ide) {
if (diskController == null) {
logger.debug("Provided disk controller is null; therefore, we will choose any existing disk controller to use.");
diskController = getAnyExistingAvailableDiskController();
}
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 instance is running in VMware hypervisor. Please re-try when instance 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<Integer, Integer> 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();
@ -2381,97 +2378,6 @@ public class VirtualMachineMO extends BaseMO {
return false;
}
public int getPvScsiDeviceControllerKeyNoException() throws Exception {
List<VirtualDevice> devices = (List<VirtualDevice>)_context.getVimClient().
getDynamicProperty(_mor, "config.hardware.device");
if (devices != null && devices.size() > 0) {
for (VirtualDevice device : devices) {
if (device instanceof ParaVirtualSCSIController) {
return device.getKey();
}
}
}
return -1;
}
public int getPvScsiDeviceControllerKey() throws Exception {
List<VirtualDevice> devices = (List<VirtualDevice>)_context.getVimClient().
getDynamicProperty(_mor, "config.hardware.device");
if (devices != null && devices.size() > 0) {
for (VirtualDevice device : devices) {
if (device instanceof ParaVirtualSCSIController) {
return device.getKey();
}
}
}
assert (false);
throw new Exception("VMware Paravirtual SCSI Controller Not Found");
}
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 instance " + 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 instance " + getName());
} else {
logger.info("Successfully added " + requiredNumScsiControllers + " SCSI controllers.");
}
}
public String getRecommendedDiskController(String guestOsId) throws Exception {
String recommendedController;
GuestOsDescriptor guestOsDescriptor = getGuestOsDescriptor(guestOsId);
@ -2479,100 +2385,6 @@ public class VirtualMachineMO extends BaseMO {
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 Instance is " + virtualHardwareVersion
+ ", which doesn't support PV SCSI controller type for virtual harddisks. Please upgrade this Instance'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<VirtualDevice> devices = (List<VirtualDevice>)_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 ((VirtualLsiLogicController)device).getKey();
} else if ((diskControllerType == DiskControllerType.lsisas1068 || diskControllerType == DiskControllerType.scsi)
&& device instanceof VirtualLsiLogicSASController && isValidScsiDiskController((VirtualLsiLogicSASController)device)) {
return ((VirtualLsiLogicSASController)device).getKey();
} else if ((diskControllerType == DiskControllerType.pvscsi || diskControllerType == DiskControllerType.scsi)
&& device instanceof ParaVirtualSCSIController && isValidScsiDiskController((ParaVirtualSCSIController)device)) {
return ((ParaVirtualSCSIController)device).getKey();
} else if ((diskControllerType == DiskControllerType.buslogic || diskControllerType == DiskControllerType.scsi)
&& device instanceof VirtualBusLogicController && isValidScsiDiskController((VirtualBusLogicController)device)) {
return ((VirtualBusLogicController)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<VirtualDevice> devices = (List<VirtualDevice>)_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 ((VirtualLsiLogicController)device).getKey();
}
break;
}
scsiControllerDeviceCount++;
} else if ((diskControllerType == DiskControllerType.lsisas1068 || diskControllerType == DiskControllerType.scsi) && device instanceof VirtualLsiLogicSASController) {
if (scsiControllerDeviceCount == requiredScsiController) {
if (isValidScsiDiskController((VirtualLsiLogicSASController)device)) {
return ((VirtualLsiLogicSASController)device).getKey();
}
break;
}
scsiControllerDeviceCount++;
} else if ((diskControllerType == DiskControllerType.pvscsi || diskControllerType == DiskControllerType.scsi) && device instanceof ParaVirtualSCSIController) {
if (scsiControllerDeviceCount == requiredScsiController) {
if (isValidScsiDiskController((ParaVirtualSCSIController)device)) {
return ((ParaVirtualSCSIController)device).getKey();
}
break;
}
scsiControllerDeviceCount++;
} else if ((diskControllerType == DiskControllerType.buslogic || diskControllerType == DiskControllerType.scsi) && device instanceof VirtualBusLogicController) {
if (scsiControllerDeviceCount == requiredScsiController) {
if (isValidScsiDiskController((VirtualBusLogicController)device)) {
return ((VirtualBusLogicController)device).getKey();
}
break;
}
scsiControllerDeviceCount++;
}
}
}
return -1;
}
public int getNextScsiDiskDeviceNumber() throws Exception {
int scsiControllerKey = getScsiDeviceControllerKey();
int deviceNumber = getNextDeviceNumber(scsiControllerKey);
return deviceNumber;
}
public int getScsiDeviceControllerKey() throws Exception {
List<VirtualDevice> devices = _context.getVimClient().getDynamicProperty(_mor, "config.hardware.device");
@ -2602,47 +2414,6 @@ public class VirtualMachineMO extends BaseMO {
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 instance " + getName());
} else {
logger.info("Successfully added " + count + " LsiLogic Parallel SCSI controllers.");
}
}
}
private int getLsiLogicDeviceControllerKeyNoException() throws Exception {
List<VirtualDevice> devices = (List<VirtualDevice>)_context.getVimClient().
getDynamicProperty(_mor, "config.hardware.device");
if (devices != null && devices.size() > 0) {
for (VirtualDevice device : devices) {
if (device instanceof VirtualLsiLogicController) {
return device.getKey();
}
}
}
return -1;
}
public void ensureScsiDeviceController() throws Exception {
int scsiControllerKey = getScsiDeviceControllerKeyNoException();
if (scsiControllerKey < 0) {
@ -2664,32 +2435,6 @@ public class VirtualMachineMO extends BaseMO {
}
}
public void ensureScsiDeviceControllers(int count, int availableBusNum) throws Exception {
int scsiControllerKey = getScsiDeviceControllerKeyNoException();
if (scsiControllerKey < 0) {
VirtualMachineConfigSpec vmConfig = new VirtualMachineConfigSpec();
int busNum = availableBusNum;
while (busNum < count) {
VirtualLsiLogicController scsiController = new VirtualLsiLogicController();
scsiController.setSharedBus(VirtualSCSISharing.NO_SHARING);
scsiController.setBusNumber(busNum);
scsiController.setKey(busNum - VmwareHelper.MAX_SCSI_CONTROLLER_COUNT);
VirtualDeviceConfigSpec scsiControllerSpec = new VirtualDeviceConfigSpec();
scsiControllerSpec.setDevice(scsiController);
scsiControllerSpec.setOperation(VirtualDeviceConfigSpecOperation.ADD);
vmConfig.getDeviceChange().add(scsiControllerSpec);
busNum++;
}
if (configureVm(vmConfig)) {
throw new Exception("Unable to add Scsi controllers to the instance " + getName());
} else {
logger.info("Successfully added " + count + " SCSI controllers.");
}
}
}
private boolean isValidScsiDiskController(VirtualSCSIController scsiDiskController) {
if (scsiDiskController == null) {
return false;
@ -3003,19 +2748,24 @@ public class VirtualMachineMO extends BaseMO {
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 <code>allDevices</code> or its type does not have an
* equivalent mapping.
*/
public String getDeviceBusName(List<VirtualDevice> 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<VirtualDisk> getVirtualDisks() throws Exception {
@ -3106,19 +2856,6 @@ public class VirtualMachineMO extends BaseMO {
return deviceList.toArray(new VirtualDisk[0]);
}
public VirtualDisk getDiskDeviceByBusName(List<VirtualDevice> 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<VirtualDisk> independentDisks = new ArrayList<>();
VirtualDisk[] allDisks = getAllDiskDevice();
@ -3175,70 +2912,6 @@ public class VirtualMachineMO extends BaseMO {
throw new Exception("IDE Controller Not Found");
}
public int getIDEControllerKey(int ideUnitNumber) throws Exception {
List<VirtualDevice> devices = (List<VirtualDevice>)_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 ((VirtualIDEController)device).getKey();
}
ideControllerCount++;
}
}
}
assert(false);
throw new Exception("IDE Controller Not Found");
}
public int getNumberOfIDEDevices() throws Exception {
int ideDeviceCount = 0;
List<VirtualDevice> devices = (List<VirtualDevice>)_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<VirtualDevice> devices = (List<VirtualDevice>)_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);
@ -3648,135 +3321,6 @@ public class VirtualMachineMO extends BaseMO {
}
return guestOsSupportsMemoryHotAdd && virtualHardwareSupportsMemoryHotAdd;
}
public void ensureLsiLogicSasDeviceControllers(int count, int availableBusNum) throws Exception {
int scsiControllerKey = getLsiLogicSasDeviceControllerKeyNoException();
if (scsiControllerKey < 0) {
VirtualMachineConfigSpec vmConfig = new VirtualMachineConfigSpec();
int busNum = availableBusNum;
while (busNum < count) {
VirtualLsiLogicSASController scsiController = new VirtualLsiLogicSASController();
scsiController.setSharedBus(VirtualSCSISharing.NO_SHARING);
scsiController.setBusNumber(busNum);
scsiController.setKey(busNum - VmwareHelper.MAX_SCSI_CONTROLLER_COUNT);
VirtualDeviceConfigSpec scsiControllerSpec = new VirtualDeviceConfigSpec();
scsiControllerSpec.setDevice(scsiController);
scsiControllerSpec.setOperation(VirtualDeviceConfigSpecOperation.ADD);
vmConfig.getDeviceChange().add(scsiControllerSpec);
busNum++;
}
if (configureVm(vmConfig)) {
throw new Exception("Unable to add Scsi controller of type LsiLogic SAS.");
}
}
}
private int getLsiLogicSasDeviceControllerKeyNoException() throws Exception {
List<VirtualDevice> devices = (List<VirtualDevice>)_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 instance " + getName());
} else {
logger.info("Successfully added " + count + " SCSI BusLogic controllers.");
}
}
}
private int getBusLogicDeviceControllerKeyNoException() throws Exception {
List<VirtualDevice> devices = (List<VirtualDevice>)_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<Integer, Integer, DiskControllerType> getScsiControllerInfo() throws Exception {
List<VirtualDevice> devices = (List<VirtualDevice>)_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<VirtualDevice> devices = (List<VirtualDevice>)_context.getVimClient().getDynamicProperty(_mor, "config.hardware.device");
logger.info("Counting disk devices attached to Instance " + 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<VirtualDevice> devices = (List<VirtualDevice>)_context.getVimClient().getDynamicProperty(_mor, "config.hardware.device");
@ -3901,4 +3445,169 @@ public class VirtualMachineMO extends BaseMO {
public String toString() {
return ReflectionToStringBuilderUtils.reflectOnlySelectedFields(this, "internalCSName");
}
public List<VirtualController> getControllers() throws Exception {
List<VirtualController> controllers = new ArrayList<>();
List<VirtualDevice> devices = _context.getVimClient().getDynamicProperty(_mor, "config.hardware.device");
for (VirtualDevice device : devices) {
if (device instanceof VirtualController) {
controllers.add((VirtualController) device);
}
}
return controllers;
}
public Set<DiskControllerMappingVO> getMappingsForExistingDiskControllers() throws Exception {
Set<DiskControllerMappingVO> mappingsForExistingControllers = new HashSet<>();
List<VirtualDevice> 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. <br>
* This function prioritizes choosing disk controllers different than <code>VirtualIDEController</code>. If no other
* disk controller type is available, but a <code>VirtualIDEController</code> is, then the IDE will be chosen.
* @throws CloudRuntimeException if no disk controller is available.
*/
public DiskControllerMappingVO getAnyExistingAvailableDiskController() throws Exception {
Set<String> validDiskControllerClasspaths = VmwareHelper.getAllSupportedDiskControllerMappingsExceptOsDefault().stream()
.map(DiskControllerMappingVO::getControllerReference)
.collect(Collectors.toSet());
Set<String> unavailableControllerClasspaths = new HashSet<>();
List<VirtualDevice> 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<Integer, Integer> nextAvailableControllerKeyAndUnitNumber = getNextAvailableControllerKeyAndDeviceNumberForType(diskController);
if (nextAvailableControllerKeyAndUnitNumber != null) {
return diskController;
}
unavailableControllerClasspaths.add(deviceClasspath);
}
DiskControllerMappingVO ideMapping = VmwareHelper.getDiskControllerMapping(null, VirtualIDEController.class.getName());
Pair<Integer, Integer> 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 <code>null</code>.
*/
public Pair<Integer, Integer> getNextAvailableControllerKeyAndDeviceNumberForType(DiskControllerMappingVO mapping) throws Exception {
List<VirtualDevice> 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<VirtualDevice> devices = _context.getVimClient().getDynamicProperty(_mor, "config.hardware.device");
Set<Integer> 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 <code>classpath</code>, and returns the
* nth (specified by <code>number</code>) device found.
* @throws CloudRuntimeException if the device is not found.
*/
public VirtualDevice getNthDevice(String classpath, int number) throws Exception {
int currentNumber = 0;
List<VirtualDevice> 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.");
}
}
}

View File

@ -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;

View File

@ -27,6 +27,7 @@ import com.vmware.vim25.VirtualMachineConfigSpec;
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<String, String> controllerInfo, Boolean systemVm) throws Exception;
Pair<DiskControllerMappingVO, DiskControllerMappingVO> controllerInfo, Boolean systemVm) throws Exception;
void importVmFromOVF(String ovfFilePath, String vmName, DatastoreMO dsMo, String diskOption, String configurationId) throws Exception;

View File

@ -31,9 +31,12 @@ import java.util.Comparator;
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,20 +59,28 @@ import com.vmware.vim25.GuestInfo;
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.VirtualMachineBootOptions;
import com.vmware.vim25.VirtualMachineConfigInfo;
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;
@ -126,17 +137,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<DiskControllerMappingVO> supportedDiskControllers;
public static void setSupportedDiskControllers(List<DiskControllerMappingVO> 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
@ -748,13 +762,11 @@ public class VmwareHelper {
}
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;
@ -771,8 +783,18 @@ public class VmwareHelper {
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 {
@ -892,18 +914,18 @@ public class VmwareHelper {
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();
@ -1083,45 +1105,127 @@ public class VmwareHelper {
}
/**
* Validates an instance's <code>rootDiskController</code> and <code>dataDiskController</code> details. Throws a
* <code>CloudRuntimeException</code> 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<DiskControllerMappingVO> getAllSupportedDiskControllerMappings() {
return supportedDiskControllers;
}
public static List<DiskControllerMappingVO> 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<DiskControllerMappingVO> getRequiredDiskControllers(Pair<DiskControllerMappingVO, DiskControllerMappingVO> controllerInfo,
boolean isSystemVm) {
Set<DiskControllerMappingVO> requiredDiskControllers = new HashSet<>();
requiredDiskControllers.add(controllerInfo.first());
if (!isSystemVm) {
requiredDiskControllers.add(controllerInfo.second());
}
return requiredDiskControllers;
}
/**
* Instructs the provided <code>VirtualMachineConfigSpec</code> to create the disk controllers contained in <code>requiredDiskControllers</code>.
* @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<DiskControllerMappingVO> 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 <code>rootDiskController</code> and <code>dataDiskController</code> 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, <code>host</code> and <code>guestOsIdentifier</code> will be ignored.
* @param host host to derive the recommended disk controllers from. Must be provided with <code>guestOsIdentifier</code>.
* @param guestOsIdentifier used to derive the recommended disk controllers from the host.
* 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 <code>lsilogic</code>.
*/
public static Pair<String, String> chooseRequiredDiskControllers(Pair<String, String> 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();
public static Pair<DiskControllerMappingVO, DiskControllerMappingVO> 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);
String convertedRootDiskController = controllerInfo.first();
DiskControllerMappingVO specifiedRootDiskController = getDiskControllerMapping(updatedRootDiskControllerDetail, null);
DiskControllerMappingVO specifiedDataDiskController = getDiskControllerMapping(updatedDataDiskControllerDetail, null);
return new Pair<>(specifiedRootDiskController, specifiedDataDiskController);
}
/**
* Based on an instance's <code>rootDiskController</code> and <code>dataDiskController</code> 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, <code>host</code> and <code>guestOsIdentifier</code> will be ignored.
* @param host host to derive the recommended disk controllers from. Must be provided with <code>guestOsIdentifier</code>.
* @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<DiskControllerMappingVO, DiskControllerMappingVO> chooseDiskControllers(Pair<DiskControllerMappingVO, DiskControllerMappingVO> controllerInfo,
VirtualMachineMO vmMo,
VmwareHypervisorHost host,
String guestOsIdentifier) throws Exception {
String recommendedDiskControllerClassName = vmMo != null ? vmMo.getRecommendedDiskController(null) : host.getRecommendedDiskController(guestOsIdentifier);
DiskControllerMappingVO recommendedDiskController = getAllSupportedDiskControllerMappingsExceptOsDefault().stream()
.filter(c -> c.getControllerReference().contains(recommendedDiskControllerClassName))
.findFirst()
.orElseThrow(() -> new CloudRuntimeException("Recommended disk controller is not mapped."));
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);
}
@ -1129,28 +1233,40 @@ public class VmwareHelper {
return new 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<DiskControllerType> 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<String, String> controllerInfo, DiskTO disk) {
public static DiskControllerMappingVO getControllerBasedOnDiskType(Pair<DiskControllerMappingVO, DiskControllerMappingVO> 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<DiskControllerMappingVO> mappings) {
List<DiskControllerMappingVO> 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.");
}
}

View File

@ -19,10 +19,19 @@ package com.cloud.hypervisor.vmware.mo;
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.BeforeClass;
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 class VirtualMachineMOTest {
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 class VirtualMachineMOTest {
}
}
@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 class VirtualMachineMOTest {
public void testGetVmxFormattedVirtualHardwareVersionInvalid() {
VirtualMachineMO.getVmxFormattedVirtualHardwareVersion(-1);
}
@Test
public void getDeviceBusNameTestReturnsValue() throws Exception {
List<VirtualDevice> 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<VirtualDevice> configureDevicesForTests() throws Exception {
List<VirtualDevice> 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<VirtualDevice> devices = configureDevicesForTests();
List<VirtualController> 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<DiskControllerMappingVO> 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<Integer, Integer> 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<Integer, Integer> result = vmMoSpy.getNextAvailableControllerKeyAndDeviceNumberForType(nvmeMapping);
Assert.assertNull(result);
}
private Pair<VirtualController, DiskControllerMappingVO> configureScsiControllersAndDisksForGetNextAvailableDeviceNumberForControllerTests(int numberOfDevices) throws Exception {
List<VirtualDevice> 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<VirtualController, DiskControllerMappingVO> config = configureScsiControllersAndDisksForGetNextAvailableDeviceNumberForControllerTests(numberOfDevices);
int result = vmMoSpy.getNextAvailableDeviceNumberForController(config.first(), config.second());
Assert.assertEquals(numberOfDevices + 1, result);
}
@Test
public void getNextAvailableDeviceNumberForControllerTestValueWhenNoDeviceNumberIsAvailable() throws Exception {
Pair<VirtualController, DiskControllerMappingVO> 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);
}
}

View File

@ -20,10 +20,27 @@ package com.cloud.hypervisor.vmware.util;
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 org.mockito.junit.MockitoJUnitRunner;
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 class VmwareHelperTest {
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 class VmwareHelperTest {
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<DiskControllerMappingVO> result = VmwareHelper.getAllSupportedDiskControllerMappingsExceptOsDefault();
DiskControllerMappingVO osdefaultMapping = VmwareHelper.getDiskControllerMapping("osdefault", null);
Assert.assertFalse(result.contains(osdefaultMapping));
Assert.assertFalse(result.isEmpty());
}
@Test
public void getRequiredDiskControllersTestRequiresAllControllersWhenInstanceIsNotSystemVm() {
Pair<DiskControllerMappingVO, DiskControllerMappingVO> controllerInfo = new Pair<>(new DiskControllerMappingVO(), new DiskControllerMappingVO());
Set<DiskControllerMappingVO> result = VmwareHelper.getRequiredDiskControllers(controllerInfo, false);
Assert.assertTrue(result.contains(controllerInfo.first()));
Assert.assertTrue(result.contains(controllerInfo.second()));
}
@Test
public void getRequiredDiskControllersTestRequiresOnlyRootDiskControllerWhenInstanceIsSystemVm() {
Pair<DiskControllerMappingVO, DiskControllerMappingVO> controllerInfo = new Pair<>(new DiskControllerMappingVO(), new DiskControllerMappingVO());
Set<DiskControllerMappingVO> 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<DiskControllerMappingVO, DiskControllerMappingVO> controllerInfo = new Pair<>(nvmeMapping, sataMapping);
Mockito.doReturn("VirtualLsiLogicController").when(virtualMachineMO).getRecommendedDiskController(null);
Pair<DiskControllerMappingVO, DiskControllerMappingVO> 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<DiskControllerMappingVO, DiskControllerMappingVO> controllerInfo = new Pair<>(osdefaultMapping, osdefaultMapping);
Mockito.doReturn("VirtualLsiLogicController").when(virtualMachineMO).getRecommendedDiskController(null);
Pair<DiskControllerMappingVO, DiskControllerMappingVO> 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<DiskControllerMappingVO, DiskControllerMappingVO> controllerInfo = new Pair<>(osdefaultMapping, osdefaultMapping);
String guestOsId = "guestOsId";
Mockito.doReturn("VirtualLsiLogicController").when(vmwareHypervisorHostMock).getRecommendedDiskController(guestOsId);
Pair<DiskControllerMappingVO, DiskControllerMappingVO> 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<DiskControllerMappingVO, DiskControllerMappingVO> controllerInfo = new Pair<>(osdefaultMapping, pvscsiMapping);
Mockito.doReturn("VirtualLsiLogicController").when(virtualMachineMO).getRecommendedDiskController(null);
Pair<DiskControllerMappingVO, DiskControllerMappingVO> 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<DiskControllerMappingVO> 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<DiskControllerMappingVO> 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<Integer> usedKeys = new HashSet<>();
Map<String, Set<Integer>> 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<DiskControllerMappingVO> 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<DiskControllerMappingVO> requiredControllers = new HashSet<>();
requiredControllers.add(lsilogicMapping);
VmwareHelper.addDiskControllersToVmConfigSpec(virtualMachineConfigSpecMock, requiredControllers, true);
Assert.assertEquals(1, virtualMachineConfigSpecMock.getDeviceChange().size());
}
@Test
public void getDiskControllersFromVmSettingsTestReturnsSpecifiedControllersWhenInstanceIsNotSystemVm() {
Pair<DiskControllerMappingVO, DiskControllerMappingVO> controllerInfo = VmwareHelper.getDiskControllersFromVmSettings("nvme", "sata", false);
Assert.assertEquals("nvme", controllerInfo.first().getName());
Assert.assertEquals("sata", controllerInfo.second().getName());
}
@Test
public void getDiskControllersFromVmSettingsTestReturnsLsiLogicForRootDiskWhenInstanceIsSystemVm() {
Pair<DiskControllerMappingVO, DiskControllerMappingVO> 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<DiskControllerMappingVO, DiskControllerMappingVO> 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<DiskControllerMappingVO, DiskControllerMappingVO> 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<DiskControllerMappingVO, DiskControllerMappingVO> 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());
}
}