| // Licensed to the Apache Software Foundation (ASF) under one |
| // or more contributor license agreements. See the NOTICE file |
| // distributed with this work for additional information |
| // regarding copyright ownership. The ASF licenses this file |
| // to you under the Apache License, Version 2.0 (the |
| // "License"); you may not use this file except in compliance |
| // with the License. You may obtain a copy of the License at |
| // |
| // http://www.apache.org/licenses/LICENSE-2.0 |
| // |
| // Unless required by applicable law or agreed to in writing, |
| // software distributed under the License is distributed on an |
| // "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY |
| // KIND, either express or implied. See the License for the |
| // specific language governing permissions and limitations |
| // under the License. |
| package com.cloud.hypervisor.guru; |
| |
| import com.cloud.agent.api.to.DeployAsIsInfoTO; |
| import com.cloud.agent.api.to.NicTO; |
| import com.cloud.agent.api.to.VirtualMachineTO; |
| import com.cloud.exception.InsufficientAddressCapacityException; |
| import com.cloud.host.HostVO; |
| import com.cloud.host.dao.HostDao; |
| import com.cloud.hypervisor.Hypervisor; |
| import com.cloud.hypervisor.vmware.manager.VmwareManager; |
| import com.cloud.hypervisor.vmware.mo.DiskControllerType; |
| import com.cloud.hypervisor.vmware.mo.VirtualEthernetCardType; |
| import com.cloud.network.Network; |
| import com.cloud.network.NetworkModel; |
| import com.cloud.network.Networks; |
| import com.cloud.network.dao.NetworkDao; |
| import com.cloud.network.dao.NetworkVO; |
| import com.cloud.storage.GuestOSHypervisorVO; |
| import com.cloud.storage.GuestOSVO; |
| import com.cloud.storage.dao.GuestOSDao; |
| import com.cloud.storage.dao.GuestOSHypervisorDao; |
| import com.cloud.storage.dao.VMTemplatePoolDao; |
| import com.cloud.template.VirtualMachineTemplate; |
| import com.cloud.utils.exception.CloudRuntimeException; |
| import com.cloud.vm.DomainRouterVO; |
| import com.cloud.vm.NicProfile; |
| import com.cloud.vm.VirtualMachine; |
| import com.cloud.vm.VirtualMachineProfile; |
| import com.cloud.vm.VmDetailConstants; |
| import com.cloud.vm.dao.DomainRouterDao; |
| import com.cloud.vm.dao.NicDao; |
| import org.apache.cloudstack.storage.datastore.db.PrimaryDataStoreDao; |
| import org.apache.cloudstack.storage.image.deployasis.DeployAsIsHelper; |
| import org.apache.cloudstack.utils.CloudStackVersion; |
| import org.apache.commons.lang.BooleanUtils; |
| import org.apache.logging.log4j.Logger; |
| import org.apache.logging.log4j.LogManager; |
| |
| import javax.inject.Inject; |
| import java.util.ArrayList; |
| import java.util.Collections; |
| import java.util.Comparator; |
| import java.util.HashMap; |
| import java.util.List; |
| import java.util.Map; |
| |
| class VmwareVmImplementer { |
| protected Logger logger = LogManager.getLogger(getClass()); |
| |
| @Inject |
| DomainRouterDao domainRouterDao; |
| @Inject |
| GuestOSDao guestOsDao; |
| @Inject |
| GuestOSHypervisorDao guestOsHypervisorDao; |
| @Inject |
| HostDao hostDao; |
| @Inject |
| NetworkDao networkDao; |
| @Inject |
| NetworkModel networkModel; |
| @Inject |
| NicDao nicDao; |
| @Inject |
| PrimaryDataStoreDao storagePoolDao; |
| @Inject |
| VMTemplatePoolDao templateStoragePoolDao; |
| @Inject |
| VmwareManager vmwareMgr; |
| @Inject |
| DeployAsIsHelper deployAsIsHelper; |
| |
| private Boolean globalNestedVirtualisationEnabled; |
| private Boolean globalNestedVPerVMEnabled; |
| |
| Boolean getGlobalNestedVirtualisationEnabled() { |
| return globalNestedVirtualisationEnabled != null ? globalNestedVirtualisationEnabled : false; |
| } |
| |
| void setGlobalNestedVirtualisationEnabled(Boolean globalNestedVirtualisationEnabled) { |
| this.globalNestedVirtualisationEnabled = globalNestedVirtualisationEnabled; |
| } |
| |
| Boolean getGlobalNestedVPerVMEnabled() { |
| return globalNestedVPerVMEnabled != null ? globalNestedVPerVMEnabled : false; |
| } |
| |
| void setGlobalNestedVPerVMEnabled(Boolean globalNestedVPerVMEnabled) { |
| this.globalNestedVPerVMEnabled = globalNestedVPerVMEnabled; |
| } |
| |
| VirtualMachineTO implement(VirtualMachineProfile vm, VirtualMachineTO to, long clusterId) { |
| to.setBootloader(VirtualMachineTemplate.BootloaderType.HVM); |
| boolean deployAsIs = vm.getTemplate().isDeployAsIs(); |
| HostVO host = hostDao.findById(vm.getVirtualMachine().getHostId()); |
| Map<String, String> details = to.getDetails(); |
| if (details == null) |
| details = new HashMap<>(); |
| |
| VirtualMachine.Type vmType = vm.getType(); |
| boolean userVm = !(vmType.equals(VirtualMachine.Type.DomainRouter) || vmType.equals(VirtualMachine.Type.ConsoleProxy) || vmType.equals(VirtualMachine.Type.SecondaryStorageVm)); |
| |
| String nicDeviceType = details.get(VmDetailConstants.NIC_ADAPTER); |
| if (!userVm) { |
| |
| if (nicDeviceType == null) { |
| details.put(VmDetailConstants.NIC_ADAPTER, vmwareMgr.getSystemVMDefaultNicAdapterType()); |
| } else { |
| try { |
| VirtualEthernetCardType.valueOf(nicDeviceType); |
| } catch (Exception e) { |
| logger.warn("Invalid NIC device type " + nicDeviceType + " is specified in VM details, switch to default E1000"); |
| details.put(VmDetailConstants.NIC_ADAPTER, VirtualEthernetCardType.E1000.toString()); |
| } |
| } |
| } else { |
| if (nicDeviceType == null) { |
| details.put(VmDetailConstants.NIC_ADAPTER, vmwareMgr.VmwareUserVmNicDeviceType.value()); |
| } else { |
| try { |
| VirtualEthernetCardType.valueOf(nicDeviceType); |
| } catch (Exception e) { |
| logger.warn(String.format("Invalid NIC device type [%s] specified in VM details, switching to value [%s] of configuration [%s].", |
| nicDeviceType, vmwareMgr.VmwareUserVmNicDeviceType.value(), vmwareMgr.VmwareUserVmNicDeviceType.toString())); |
| details.put(VmDetailConstants.NIC_ADAPTER, vmwareMgr.VmwareUserVmNicDeviceType.value()); |
| } |
| } |
| } |
| |
| setBootParameters(vm, to, details); |
| |
| setDiskControllers(vm, details, userVm); |
| |
| List<NicProfile> nicProfiles = getNicProfiles(vm, details); |
| |
| addReservationDetails(clusterId, details); |
| |
| if (vmType.equals(VirtualMachine.Type.DomainRouter)) { |
| configureDomainRouterNicsAndDetails(vm, to, details, nicProfiles); |
| } |
| |
| // Don't do this if the virtual machine is one of the special types |
| // Should only be done on user machines |
| if (userVm) { |
| configureNestedVirtualization(details, to); |
| } |
| // Determine the VM's OS description |
| GuestOSVO guestOS = guestOsDao.findByIdIncludingRemoved(vm.getVirtualMachine().getGuestOSId()); |
| to.setOs(guestOS.getDisplayName()); |
| to.setHostName(vm.getHostName()); |
| |
| GuestOSHypervisorVO guestOsMapping = null; |
| if (host != null) { |
| guestOsMapping = getGuestOsMapping(guestOS, host.getHypervisorVersion()); |
| } |
| if (guestOsMapping == null || host == null) { |
| to.setPlatformEmulator(null); |
| } else { |
| to.setPlatformEmulator(guestOsMapping.getGuestOsName()); |
| } |
| |
| if (deployAsIs) { |
| setDeployAsIsInfoTO(vm, to, details); |
| } |
| |
| setDetails(to, details); |
| |
| return to; |
| } |
| |
| /** |
| * Set the information relevant for deploy-as-is VMs on the VM TO |
| */ |
| private void setDeployAsIsInfoTO(VirtualMachineProfile vm, VirtualMachineTO to, Map<String, String> details) { |
| Map<String, String> properties = deployAsIsHelper.getVirtualMachineDeployAsIsProperties(vm); |
| Map<Integer, String> nicsAdapterMapping = deployAsIsHelper.getAllocatedVirtualMachineNicsAdapterMapping(vm, to.getNics()); |
| DeployAsIsInfoTO info = new DeployAsIsInfoTO(properties, nicsAdapterMapping); |
| to.setDeployAsIsInfo(info); |
| } |
| |
| private void setDetails(VirtualMachineTO to, Map<String, String> details) { |
| if (logger.isTraceEnabled()) { |
| for (String key : details.keySet()) { |
| logger.trace(String.format("Detail for VM %s: %s => %s", to.getName(), key, details.get(key))); |
| } |
| } |
| to.setDetails(details); |
| } |
| |
| private void configureDomainRouterNicsAndDetails(VirtualMachineProfile vm, VirtualMachineTO to, Map<String, String> details, List<NicProfile> nicProfiles) { |
| NicProfile publicNicProfile = null; |
| for (NicProfile nicProfile : nicProfiles) { |
| if (nicProfile.getTrafficType() == Networks.TrafficType.Public) { |
| publicNicProfile = nicProfile; |
| break; |
| } |
| } |
| |
| if (publicNicProfile != null) { |
| NicTO[] nics = to.getNics(); |
| |
| // reserve extra NICs |
| NicTO[] expandedNics = new NicTO[nics.length]; |
| int i = 0; |
| int deviceId = -1; |
| for (i = 0; i < nics.length; i++) { |
| expandedNics[i] = nics[i]; |
| if (nics[i].getDeviceId() > deviceId) |
| deviceId = nics[i].getDeviceId(); |
| } |
| deviceId++; |
| |
| long networkId = publicNicProfile.getNetworkId(); |
| NetworkVO network = networkDao.findById(networkId); |
| |
| for (; i < nics.length; i++) { |
| NicTO nicTo = new NicTO(); |
| |
| nicTo.setDeviceId(deviceId++); |
| nicTo.setBroadcastType(publicNicProfile.getBroadcastType()); |
| nicTo.setType(publicNicProfile.getTrafficType()); |
| nicTo.setIp("0.0.0.0"); |
| nicTo.setNetmask("255.255.255.255"); |
| |
| try { |
| String mac = networkModel.getNextAvailableMacAddressInNetwork(networkId); |
| nicTo.setMac(mac); |
| } catch (InsufficientAddressCapacityException e) { |
| throw new CloudRuntimeException("unable to allocate mac address on network: " + networkId); |
| } |
| nicTo.setDns1(publicNicProfile.getIPv4Dns1()); |
| nicTo.setDns2(publicNicProfile.getIPv4Dns2()); |
| if (publicNicProfile.getIPv4Gateway() != null) { |
| nicTo.setGateway(publicNicProfile.getIPv4Gateway()); |
| } else { |
| nicTo.setGateway(network.getGateway()); |
| } |
| nicTo.setDefaultNic(false); |
| nicTo.setBroadcastUri(publicNicProfile.getBroadCastUri()); |
| nicTo.setIsolationuri(publicNicProfile.getIsolationUri()); |
| |
| Integer networkRate = networkModel.getNetworkRate(network.getId(), null); |
| nicTo.setNetworkRateMbps(networkRate); |
| |
| expandedNics[i] = nicTo; |
| } |
| |
| to.setNics(expandedNics); |
| |
| VirtualMachine router = vm.getVirtualMachine(); |
| DomainRouterVO routerVO = domainRouterDao.findById(router.getId()); |
| if (routerVO != null && routerVO.getIsRedundantRouter()) { |
| Long peerRouterId = nicDao.getPeerRouterId(publicNicProfile.getMacAddress(), router.getId()); |
| DomainRouterVO peerRouterVO = null; |
| if (peerRouterId != null) { |
| peerRouterVO = domainRouterDao.findById(peerRouterId); |
| if (peerRouterVO != null) { |
| details.put("PeerRouterInstanceName", peerRouterVO.getInstanceName()); |
| } |
| } |
| } |
| } |
| |
| StringBuffer sbMacSequence = new StringBuffer(); |
| for (NicTO nicTo : sortNicsByDeviceId(to.getNics())) { |
| sbMacSequence.append(nicTo.getMac()).append("|"); |
| } |
| if (!sbMacSequence.toString().isEmpty()) { |
| sbMacSequence.deleteCharAt(sbMacSequence.length() - 1); |
| String bootArgs = to.getBootArgs(); |
| to.setBootArgs(bootArgs + " nic_macs=" + sbMacSequence.toString()); |
| } |
| } |
| |
| private void addReservationDetails(long clusterId, Map<String, String> details) { |
| details.put(VMwareGuru.VmwareReserveCpu.key(), VMwareGuru.VmwareReserveCpu.valueIn(clusterId).toString()); |
| details.put(VMwareGuru.VmwareReserveMemory.key(), VMwareGuru.VmwareReserveMemory.valueIn(clusterId).toString()); |
| } |
| |
| private List<NicProfile> getNicProfiles(VirtualMachineProfile vm, Map<String, String> details) { |
| List<NicProfile> nicProfiles = vm.getNics(); |
| |
| for (NicProfile nicProfile : nicProfiles) { |
| if (nicProfile.getTrafficType() == Networks.TrafficType.Guest) { |
| if (networkModel.isProviderSupportServiceInNetwork(nicProfile.getNetworkId(), Network.Service.Firewall, Network.Provider.CiscoVnmc)) { |
| details.put("ConfigureVServiceInNexus", Boolean.TRUE.toString()); |
| } |
| break; |
| } |
| } |
| return nicProfiles; |
| } |
| |
| private void setDiskControllers(VirtualMachineProfile vm, Map<String, String> details, boolean userVm) { |
| String diskDeviceType = details.get(VmDetailConstants.ROOT_DISK_CONTROLLER); |
| if (userVm) { |
| if (diskDeviceType == null) { |
| details.put(VmDetailConstants.ROOT_DISK_CONTROLLER,vmwareMgr.getRootDiskController()); |
| } |
| } |
| String diskController = details.get(VmDetailConstants.DATA_DISK_CONTROLLER); |
| if (userVm) { |
| if (diskController == null) { |
| details.put(VmDetailConstants.DATA_DISK_CONTROLLER, DiskControllerType.lsilogic.toString()); |
| } |
| } |
| |
| if (vm.getType() == VirtualMachine.Type.NetScalerVm) { |
| details.put(VmDetailConstants.ROOT_DISK_CONTROLLER, "scsi"); |
| } |
| } |
| |
| private void setBootParameters(VirtualMachineProfile vm, VirtualMachineTO to, Map<String, String> details) { |
| details.put(VmDetailConstants.BOOT_MODE, to.getBootMode()); |
| if (vm.getParameter(VirtualMachineProfile.Param.BootIntoSetup) != null && (Boolean)vm.getParameter(VirtualMachineProfile.Param.BootIntoSetup) == true) { |
| to.setEnterHardwareSetup(true); |
| } |
| // there should also be |
| // details.put(VmDetailConstants.BOOT_TYPE, to.getBootType()); |
| } |
| |
| /** |
| * Adds {@code 'nestedVirtualizationFlag'} value to {@code details} due to if it should be enabled or not |
| * @param details vm details should not be null |
| * @param to vm to |
| */ |
| protected void configureNestedVirtualization(Map<String, String> details, VirtualMachineTO to) { |
| String localNestedV = details.get(VmDetailConstants.NESTED_VIRTUALIZATION_FLAG); |
| |
| Boolean globalNestedVirtualisationEnabled = getGlobalNestedVirtualisationEnabled(); |
| Boolean globalNestedVPerVMEnabled = getGlobalNestedVPerVMEnabled(); |
| |
| Boolean shouldEnableNestedVirtualization = shouldEnableNestedVirtualization(globalNestedVirtualisationEnabled, globalNestedVPerVMEnabled, localNestedV); |
| if(logger.isDebugEnabled()) { |
| logger.debug(String.format( |
| "Due to '%B'(globalNestedVirtualisationEnabled) and '%B'(globalNestedVPerVMEnabled) I'm adding a flag with value %B to the vm configuration for Nested Virtualisation.", |
| globalNestedVirtualisationEnabled, |
| globalNestedVPerVMEnabled, |
| shouldEnableNestedVirtualization) |
| ); |
| } |
| details.put(VmDetailConstants.NESTED_VIRTUALIZATION_FLAG, Boolean.toString(shouldEnableNestedVirtualization)); |
| } |
| |
| /** |
| * Decide in which cases nested virtualization should be enabled based on (1){@code globalNestedV}, (2){@code globalNestedVPerVM}, (3){@code localNestedV}<br/> |
| * Nested virtualization should be enabled when one of this cases: |
| * <ul> |
| * <li>(1)=TRUE, (2)=TRUE, (3) is NULL (missing)</li> |
| * <li>(1)=TRUE, (2)=TRUE, (3)=TRUE</li> |
| * <li>(1)=TRUE, (2)=FALSE</li> |
| * <li>(1)=FALSE, (2)=TRUE, (3)=TRUE</li> |
| * </ul> |
| * In any other case, it shouldn't be enabled |
| * @param globalNestedV value of {@code 'vmware.nested.virtualization'} global config |
| * @param globalNestedVPerVM value of {@code 'vmware.nested.virtualization.perVM'} global config |
| * @param localNestedV value of {@code 'nestedVirtualizationFlag'} key in vm details if present, null if not present |
| * @return "true" for cases in which nested virtualization is enabled, "false" if not |
| */ |
| Boolean shouldEnableNestedVirtualization(Boolean globalNestedV, Boolean globalNestedVPerVM, String localNestedV) { |
| if (globalNestedV == null || globalNestedVPerVM == null) { |
| return false; |
| } |
| boolean globalNV = globalNestedV.booleanValue(); |
| boolean globalNVPVM = globalNestedVPerVM.booleanValue(); |
| |
| if (globalNVPVM) { |
| return (localNestedV == null && globalNV) || BooleanUtils.toBoolean(localNestedV); |
| } |
| return globalNV; |
| } |
| |
| private NicTO[] sortNicsByDeviceId(NicTO[] nics) { |
| |
| List<NicTO> listForSort = new ArrayList<NicTO>(); |
| for (NicTO nic : nics) { |
| listForSort.add(nic); |
| } |
| Collections.sort(listForSort, new Comparator<NicTO>() { |
| |
| @Override public int compare(NicTO arg0, NicTO arg1) { |
| if (arg0.getDeviceId() < arg1.getDeviceId()) { |
| return -1; |
| } else if (arg0.getDeviceId() == arg1.getDeviceId()) { |
| return 0; |
| } |
| |
| return 1; |
| } |
| }); |
| |
| return listForSort.toArray(new NicTO[0]); |
| } |
| |
| protected GuestOSHypervisorVO getGuestOsMapping(GuestOSVO guestOS , String hypervisorVersion) { |
| GuestOSHypervisorVO guestOsMapping = guestOsHypervisorDao.findByOsIdAndHypervisor(guestOS.getId(), Hypervisor.HypervisorType.VMware.toString(), hypervisorVersion); |
| if (guestOsMapping == null) { |
| logger.debug(String.format("Cannot find guest os mappings for guest os \"%s\" on VMware %s", guestOS.getDisplayName(), hypervisorVersion)); |
| String parentVersion = CloudStackVersion.getVMwareParentVersion(hypervisorVersion); |
| if (parentVersion != null) { |
| guestOsMapping = guestOsHypervisorDao.findByOsIdAndHypervisor(guestOS.getId(), Hypervisor.HypervisorType.VMware.toString(), parentVersion); |
| logger.debug(String.format("Found guest os mappings for guest os \"%s\" on VMware %s: %s", guestOS.getDisplayName(), parentVersion, guestOsMapping)); |
| } |
| } |
| return guestOsMapping; |
| } |
| } |