blob: 461e141fa3db4d1f3a959abfe6e43a950d8d93f9 [file] [log] [blame]
// 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;
}
}