| // Licensed to the Apache Software Foundation (ASF) under one |
| // or more contributor license agreements. See the NOTICE file |
| // distributed with this work for additional information |
| // regarding copyright ownership. The ASF licenses this file |
| // to you under the Apache License, Version 2.0 (the |
| // "License"); you may not use this file except in compliance |
| // with the License. You may obtain a copy of the License at |
| // |
| // http://www.apache.org/licenses/LICENSE-2.0 |
| // |
| // Unless required by applicable law or agreed to in writing, |
| // software distributed under the License is distributed on an |
| // "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY |
| // KIND, either express or implied. See the License for the |
| // specific language governing permissions and limitations |
| // under the License. |
| package com.cloud.hypervisor.vmware.mo; |
| |
| import java.io.ByteArrayInputStream; |
| import java.io.File; |
| import java.io.FileWriter; |
| import java.io.IOException; |
| import java.io.StringWriter; |
| import java.net.URI; |
| import java.net.URISyntaxException; |
| import java.security.InvalidParameterException; |
| import java.util.ArrayList; |
| import java.util.Arrays; |
| import java.util.HashMap; |
| import java.util.List; |
| import java.util.Map; |
| import java.util.Objects; |
| import java.util.UUID; |
| |
| import javax.xml.parsers.DocumentBuilderFactory; |
| import javax.xml.parsers.ParserConfigurationException; |
| import javax.xml.transform.Transformer; |
| import javax.xml.transform.TransformerException; |
| import javax.xml.transform.TransformerFactory; |
| import javax.xml.transform.dom.DOMSource; |
| import javax.xml.transform.stream.StreamResult; |
| |
| import org.apache.cloudstack.engine.orchestration.service.NetworkOrchestrationService; |
| import org.apache.cloudstack.utils.security.ParserUtils; |
| import org.apache.commons.collections.MapUtils; |
| import org.apache.commons.lang3.StringUtils; |
| import org.apache.logging.log4j.Logger; |
| import org.apache.logging.log4j.LogManager; |
| import org.apache.maven.artifact.versioning.ComparableVersion; |
| import org.w3c.dom.Document; |
| import org.w3c.dom.Element; |
| import org.w3c.dom.Node; |
| import org.w3c.dom.traversal.DocumentTraversal; |
| import org.w3c.dom.traversal.NodeFilter; |
| import org.w3c.dom.traversal.NodeIterator; |
| import org.xml.sax.SAXException; |
| |
| import com.cloud.exception.CloudException; |
| import com.cloud.hypervisor.vmware.util.VmwareContext; |
| import com.cloud.hypervisor.vmware.util.VmwareHelper; |
| import com.cloud.network.Networks.BroadcastDomainType; |
| import com.cloud.offering.NetworkOffering; |
| import com.cloud.storage.Storage.StoragePoolType; |
| import com.cloud.utils.ActionDelegate; |
| import com.cloud.utils.LogUtils; |
| import com.cloud.utils.NumbersUtil; |
| import com.cloud.utils.Pair; |
| import com.cloud.utils.cisco.n1kv.vsm.NetconfHelper; |
| import com.cloud.utils.cisco.n1kv.vsm.PolicyMap; |
| import com.cloud.utils.cisco.n1kv.vsm.PortProfile; |
| import com.cloud.utils.cisco.n1kv.vsm.VsmCommand.BindingType; |
| import com.cloud.utils.cisco.n1kv.vsm.VsmCommand.OperationType; |
| import com.cloud.utils.cisco.n1kv.vsm.VsmCommand.PortProfileType; |
| import com.cloud.utils.cisco.n1kv.vsm.VsmCommand.SwitchPortMode; |
| import com.cloud.utils.db.GlobalLock; |
| import com.cloud.utils.exception.CloudRuntimeException; |
| import com.cloud.utils.net.NetUtils; |
| import com.cloud.utils.nicira.nvp.plugin.NiciraNvpApiVersion; |
| import com.vmware.vim25.AlreadyExistsFaultMsg; |
| import com.vmware.vim25.BoolPolicy; |
| import com.vmware.vim25.ClusterConfigInfoEx; |
| import com.vmware.vim25.ConcurrentAccessFaultMsg; |
| import com.vmware.vim25.CustomFieldStringValue; |
| import com.vmware.vim25.DVPortSetting; |
| import com.vmware.vim25.DVPortgroupConfigInfo; |
| import com.vmware.vim25.DVPortgroupConfigSpec; |
| import com.vmware.vim25.DVSMacLearningPolicy; |
| import com.vmware.vim25.DVSMacManagementPolicy; |
| import com.vmware.vim25.DVSSecurityPolicy; |
| import com.vmware.vim25.DVSTrafficShapingPolicy; |
| import com.vmware.vim25.DatacenterConfigInfo; |
| import com.vmware.vim25.DuplicateNameFaultMsg; |
| import com.vmware.vim25.DynamicProperty; |
| import com.vmware.vim25.FileFaultFaultMsg; |
| import com.vmware.vim25.HostNetworkSecurityPolicy; |
| import com.vmware.vim25.HostNetworkTrafficShapingPolicy; |
| import com.vmware.vim25.HostPortGroup; |
| import com.vmware.vim25.HostPortGroupSpec; |
| import com.vmware.vim25.HostVirtualSwitch; |
| import com.vmware.vim25.HttpNfcLeaseDeviceUrl; |
| import com.vmware.vim25.HttpNfcLeaseInfo; |
| import com.vmware.vim25.HttpNfcLeaseState; |
| import com.vmware.vim25.InsufficientResourcesFaultFaultMsg; |
| import com.vmware.vim25.InvalidDatastoreFaultMsg; |
| import com.vmware.vim25.InvalidNameFaultMsg; |
| import com.vmware.vim25.InvalidStateFaultMsg; |
| import com.vmware.vim25.LocalizedMethodFault; |
| import com.vmware.vim25.LongPolicy; |
| import com.vmware.vim25.ManagedObjectReference; |
| import com.vmware.vim25.MethodFault; |
| import com.vmware.vim25.NumericRange; |
| import com.vmware.vim25.ObjectContent; |
| import com.vmware.vim25.OptionValue; |
| import com.vmware.vim25.OutOfBoundsFaultMsg; |
| import com.vmware.vim25.OvfCreateDescriptorParams; |
| import com.vmware.vim25.OvfCreateDescriptorResult; |
| 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; |
| 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; |
| import com.vmware.vim25.VirtualMachineImportSpec; |
| import com.vmware.vim25.VirtualMachineVideoCard; |
| import com.vmware.vim25.VirtualSCSIController; |
| import com.vmware.vim25.VirtualSCSISharing; |
| import com.vmware.vim25.VmConfigFaultFaultMsg; |
| import com.vmware.vim25.VmwareDistributedVirtualSwitchPvlanSpec; |
| import com.vmware.vim25.VmwareDistributedVirtualSwitchTrunkVlanSpec; |
| import com.vmware.vim25.VmwareDistributedVirtualSwitchVlanIdSpec; |
| import com.vmware.vim25.VmwareDistributedVirtualSwitchVlanSpec; |
| |
| public class HypervisorHostHelper { |
| protected static Logger LOGGER = LogManager.getLogger(HypervisorHostHelper.class); |
| private static final int DEFAULT_LOCK_TIMEOUT_SECONDS = 600; |
| private static final String s_policyNamePrefix = "cloud.policy."; |
| |
| // make vmware-base loosely coupled with cloud-specific stuff, duplicate VLAN.UNTAGGED constant here |
| private static final String UNTAGGED_VLAN_NAME = "untagged"; |
| private static final String VMDK_PACK_DIR = "ova"; |
| private static final String OVA_OPTION_KEY_BOOTDISK = "cloud.ova.bootdisk"; |
| public static final String VSPHERE_DATASTORE_BASE_FOLDER = "fcd"; |
| public static final String VSPHERE_DATASTORE_HIDDEN_FOLDER = ".hidden"; |
| |
| protected final static Map<String, Integer> apiVersionHardwareVersionMap; |
| |
| static { |
| apiVersionHardwareVersionMap = new HashMap<String, Integer>(); |
| apiVersionHardwareVersionMap.put("3.5", 4); |
| apiVersionHardwareVersionMap.put("3.6", 4); |
| apiVersionHardwareVersionMap.put("3.7", 4); |
| apiVersionHardwareVersionMap.put("3.8", 4); |
| apiVersionHardwareVersionMap.put("3.9", 4); |
| apiVersionHardwareVersionMap.put("4.0", 7); |
| apiVersionHardwareVersionMap.put("4.1", 7); |
| apiVersionHardwareVersionMap.put("4.2", 7); |
| apiVersionHardwareVersionMap.put("4.3", 7); |
| apiVersionHardwareVersionMap.put("4.4", 7); |
| apiVersionHardwareVersionMap.put("4.5", 7); |
| apiVersionHardwareVersionMap.put("4.6", 7); |
| apiVersionHardwareVersionMap.put("4.7", 7); |
| apiVersionHardwareVersionMap.put("4.8", 7); |
| apiVersionHardwareVersionMap.put("4.9", 7); |
| apiVersionHardwareVersionMap.put("5.0", 8); |
| apiVersionHardwareVersionMap.put("5.1", 9); |
| apiVersionHardwareVersionMap.put("5.2", 9); |
| apiVersionHardwareVersionMap.put("5.3", 9); |
| apiVersionHardwareVersionMap.put("5.4", 9); |
| apiVersionHardwareVersionMap.put("5.5", 10); |
| apiVersionHardwareVersionMap.put("5.6", 10); |
| apiVersionHardwareVersionMap.put("5.7", 10); |
| apiVersionHardwareVersionMap.put("5.8", 10); |
| apiVersionHardwareVersionMap.put("5.9", 10); |
| apiVersionHardwareVersionMap.put("6.0", 11); |
| apiVersionHardwareVersionMap.put("6.1", 11); |
| apiVersionHardwareVersionMap.put("6.2", 11); |
| apiVersionHardwareVersionMap.put("6.3", 11); |
| apiVersionHardwareVersionMap.put("6.4", 11); |
| apiVersionHardwareVersionMap.put("6.5", 13); |
| apiVersionHardwareVersionMap.put("6.6", 13); |
| apiVersionHardwareVersionMap.put("6.7", 14); |
| apiVersionHardwareVersionMap.put("6.8", 14); |
| apiVersionHardwareVersionMap.put("6.9", 14); |
| apiVersionHardwareVersionMap.put("7.0", 17); |
| } |
| private static final String MINIMUM_VCENTER_API_VERSION_WITH_DVS_NEW_POLICIES_SUPPORT = "6.7"; |
| private static final String MINIMUM_DVS_VERSION_WITH_NEW_POLICIES_SUPPORT = "6.6.0"; |
| |
| private static boolean isVersionEqualOrHigher(String check, String base) { |
| if (check == null || base == null) { |
| return false; |
| } |
| ComparableVersion baseVersion = new ComparableVersion(base); |
| ComparableVersion checkVersion = new ComparableVersion(check); |
| return checkVersion.compareTo(baseVersion) >= 0; |
| } |
| |
| public static VirtualMachineMO findVmFromObjectContent(VmwareContext context, ObjectContent[] ocs, String name, String instanceNameCustomField) { |
| |
| if (ocs != null && ocs.length > 0) { |
| for (ObjectContent oc : ocs) { |
| String vmNameInvCenter = null; |
| String vmInternalCSName = null; |
| List<DynamicProperty> objProps = oc.getPropSet(); |
| if (objProps != null) { |
| for (DynamicProperty objProp : objProps) { |
| if (objProp.getName().equals("name")) { |
| vmNameInvCenter = (String)objProp.getVal(); |
| } else if (objProp.getName().contains(instanceNameCustomField)) { |
| if (objProp.getVal() != null) |
| vmInternalCSName = ((CustomFieldStringValue)objProp.getVal()).getValue(); |
| } |
| |
| if ((vmNameInvCenter != null && name.equalsIgnoreCase(vmNameInvCenter)) || (vmInternalCSName != null && name.equalsIgnoreCase(vmInternalCSName))) { |
| VirtualMachineMO vmMo = new VirtualMachineMO(context, oc.getObj()); |
| return vmMo; |
| } |
| } |
| } |
| } |
| } |
| return null; |
| } |
| |
| public static ManagedObjectReference findDatastoreWithBackwardsCompatibility(VmwareHypervisorHost hyperHost, String uuidName) throws Exception { |
| ManagedObjectReference morDs = hyperHost.findDatastore(uuidName.replace("-", "")); |
| if (morDs == null) |
| morDs = hyperHost.findDatastore(uuidName); |
| |
| return morDs; |
| } |
| |
| public static String getSecondaryDatastoreUUID(String storeUrl) { |
| return UUID.nameUUIDFromBytes(storeUrl.getBytes()).toString(); |
| } |
| |
| public static DatastoreMO getHyperHostDatastoreMO(VmwareHypervisorHost hyperHost, String datastoreName) throws Exception { |
| ObjectContent[] ocs = hyperHost.getDatastorePropertiesOnHyperHost(new String[] {"name"}); |
| if (ocs != null && ocs.length > 0) { |
| for (ObjectContent oc : ocs) { |
| List<DynamicProperty> objProps = oc.getPropSet(); |
| if (objProps != null) { |
| for (DynamicProperty objProp : objProps) { |
| if (objProp.getVal().toString().equals(datastoreName)) |
| return new DatastoreMO(hyperHost.getContext(), oc.getObj()); |
| } |
| } |
| } |
| } |
| return null; |
| } |
| |
| public static String getPublicNetworkNamePrefix(String vlanId) { |
| if (UNTAGGED_VLAN_NAME.equalsIgnoreCase(vlanId)) { |
| return "cloud.public.untagged"; |
| } else { |
| return "cloud.public." + vlanId + "."; |
| } |
| } |
| |
| public static String composeCloudNetworkName(String prefix, String vlanId, String svlanId, Integer networkRateMbps, String vSwitchName) { |
| StringBuffer sb = new StringBuffer(prefix); |
| if (vlanId == null || UNTAGGED_VLAN_NAME.equalsIgnoreCase(vlanId)) { |
| sb.append(".untagged"); |
| } else { |
| sb.append(".").append(vlanId); |
| if (svlanId != null) { |
| sb.append(".").append("s" + svlanId); |
| } |
| |
| } |
| |
| if (networkRateMbps != null && networkRateMbps.intValue() > 0) |
| sb.append(".").append(String.valueOf(networkRateMbps)); |
| else |
| sb.append(".0"); |
| sb.append(".").append(VersioningContants.PORTGROUP_NAMING_VERSION); |
| sb.append("-").append(vSwitchName); |
| |
| return sb.toString(); |
| } |
| |
| public static Map<String, String> getValidatedVsmCredentials(VmwareContext context) throws Exception { |
| Map<String, String> vsmCredentials = context.getStockObject("vsmcredentials"); |
| String msg; |
| if (vsmCredentials == null || vsmCredentials.size() != 3) { |
| msg = "Failed to retrieve required credentials of Nexus VSM from database."; |
| LOGGER.error(msg); |
| throw new Exception(msg); |
| } |
| |
| String vsmIp = vsmCredentials.containsKey("vsmip") ? vsmCredentials.get("vsmip") : null; |
| String vsmUserName = vsmCredentials.containsKey("vsmusername") ? vsmCredentials.get("vsmusername") : null; |
| String vsmPassword = vsmCredentials.containsKey("vsmpassword") ? vsmCredentials.get("vsmpassword") : null; |
| if (vsmIp == null || vsmIp.isEmpty() || vsmUserName == null || vsmUserName.isEmpty() || vsmPassword == null || vsmPassword.isEmpty()) { |
| msg = "Detected invalid credentials for Nexus 1000v."; |
| LOGGER.error(msg); |
| throw new Exception(msg); |
| } |
| return vsmCredentials; |
| } |
| |
| public static void createPortProfile(VmwareContext context, String ethPortProfileName, String networkName, Integer vlanId, Integer networkRateMbps, |
| long peakBandwidth, long burstSize, String gateway, boolean configureVServiceInNexus) throws Exception { |
| Map<String, String> vsmCredentials = getValidatedVsmCredentials(context); |
| String vsmIp = vsmCredentials.get("vsmip"); |
| String vsmUserName = vsmCredentials.get("vsmusername"); |
| String vsmPassword = vsmCredentials.get("vsmpassword"); |
| String msg; |
| |
| NetconfHelper netconfClient; |
| try { |
| LOGGER.info("Connecting to Nexus 1000v: " + vsmIp); |
| netconfClient = new NetconfHelper(vsmIp, vsmUserName, vsmPassword); |
| LOGGER.info("Successfully connected to Nexus 1000v : " + vsmIp); |
| } catch (CloudRuntimeException e) { |
| msg = "Failed to connect to Nexus 1000v " + vsmIp + " with credentials of user " + vsmUserName + ". Exception: " + e.toString(); |
| LOGGER.error(msg); |
| throw new CloudRuntimeException(msg); |
| } |
| |
| String policyName = s_policyNamePrefix; |
| int averageBandwidth = 0; |
| if (networkRateMbps != null) { |
| averageBandwidth = networkRateMbps.intValue(); |
| policyName += averageBandwidth; |
| } |
| |
| try { |
| // TODO(sateesh): Change the type of peakBandwidth & burstRate in |
| // PolicyMap to long. |
| if (averageBandwidth > 0) { |
| LOGGER.debug("Adding policy map " + policyName); |
| netconfClient.addPolicyMap(policyName, averageBandwidth, (int)peakBandwidth, (int)burstSize); |
| } |
| } catch (CloudRuntimeException e) { |
| msg = |
| "Failed to add policy map of " + policyName + " with parameters " + "committed rate = " + averageBandwidth + "peak bandwidth = " + peakBandwidth + |
| "burst size = " + burstSize + ". Exception: " + e.toString(); |
| LOGGER.error(msg); |
| if (netconfClient != null) { |
| netconfClient.disconnect(); |
| LOGGER.debug("Disconnected Nexus 1000v session."); |
| } |
| throw new CloudRuntimeException(msg); |
| } |
| |
| List<Pair<OperationType, String>> params = new ArrayList<Pair<OperationType, String>>(); |
| if (vlanId != null) { |
| // No need to update ethernet port profile for untagged vlans |
| params.add(new Pair<OperationType, String>(OperationType.addvlanid, vlanId.toString())); |
| try { |
| LOGGER.info("Updating Ethernet port profile " + ethPortProfileName + " with VLAN " + vlanId); |
| netconfClient.updatePortProfile(ethPortProfileName, SwitchPortMode.trunk, params); |
| LOGGER.info("Added " + vlanId + " to Ethernet port profile " + ethPortProfileName); |
| } catch (CloudRuntimeException e) { |
| msg = "Failed to update Ethernet port profile " + ethPortProfileName + " with VLAN " + vlanId + ". Exception: " + e.toString(); |
| LOGGER.error(msg); |
| if (netconfClient != null) { |
| netconfClient.disconnect(); |
| LOGGER.debug("Disconnected Nexus 1000v session."); |
| } |
| throw new CloudRuntimeException(msg); |
| } |
| } |
| |
| try { |
| if (vlanId == null) { |
| LOGGER.info("Adding port profile configured over untagged VLAN."); |
| netconfClient.addPortProfile(networkName, PortProfileType.vethernet, BindingType.portbindingstatic, SwitchPortMode.access, 0); |
| } else { |
| if (!configureVServiceInNexus) { |
| LOGGER.info("Adding port profile configured over VLAN : " + vlanId.toString()); |
| netconfClient.addPortProfile(networkName, PortProfileType.vethernet, BindingType.portbindingstatic, SwitchPortMode.access, vlanId.intValue()); |
| } else { |
| String tenant = "vlan-" + vlanId.intValue(); |
| String vdc = "root/" + tenant + "/VDC-" + tenant; |
| String esp = "ESP-" + tenant; |
| LOGGER.info("Adding vservice node in Nexus VSM for VLAN : " + vlanId.toString()); |
| netconfClient.addVServiceNode(vlanId.toString(), gateway); |
| LOGGER.info("Adding port profile with vservice details configured over VLAN : " + vlanId.toString()); |
| netconfClient.addPortProfile(networkName, PortProfileType.vethernet, BindingType.portbindingstatic, SwitchPortMode.access, vlanId.intValue(), vdc, |
| esp); |
| } |
| } |
| } catch (CloudRuntimeException e) { |
| msg = "Failed to add vEthernet port profile " + networkName + "." + ". Exception: " + e.toString(); |
| LOGGER.error(msg); |
| if (netconfClient != null) { |
| netconfClient.disconnect(); |
| LOGGER.debug("Disconnected Nexus 1000v session."); |
| } |
| throw new CloudRuntimeException(msg); |
| } |
| |
| try { |
| if (averageBandwidth > 0) { |
| LOGGER.info("Associating policy map " + policyName + " with port profile " + networkName + "."); |
| netconfClient.attachServicePolicy(policyName, networkName); |
| } |
| } catch (CloudRuntimeException e) { |
| msg = "Failed to associate policy map " + policyName + " with port profile " + networkName + ". Exception: " + e.toString(); |
| LOGGER.error(msg); |
| throw new CloudRuntimeException(msg); |
| } finally { |
| if (netconfClient != null) { |
| netconfClient.disconnect(); |
| LOGGER.debug("Disconnected Nexus 1000v session."); |
| } |
| } |
| } |
| |
| public static void updatePortProfile(VmwareContext context, String ethPortProfileName, String vethPortProfileName, Integer vlanId, Integer networkRateMbps, |
| long peakBandwidth, long burstRate) throws Exception { |
| NetconfHelper netconfClient = null; |
| Map<String, String> vsmCredentials = getValidatedVsmCredentials(context); |
| String vsmIp = vsmCredentials.get("vsmip"); |
| String vsmUserName = vsmCredentials.get("vsmusername"); |
| String vsmPassword = vsmCredentials.get("vsmpassword"); |
| |
| String msg; |
| try { |
| netconfClient = new NetconfHelper(vsmIp, vsmUserName, vsmPassword); |
| } catch (CloudRuntimeException e) { |
| msg = "Failed to connect to Nexus 1000v " + vsmIp + " with credentials of user " + vsmUserName + ". Exception: " + e.toString(); |
| LOGGER.error(msg); |
| throw new CloudRuntimeException(msg); |
| } |
| |
| PortProfile portProfile = netconfClient.getPortProfileByName(vethPortProfileName); |
| int averageBandwidth = 0; |
| String policyName = s_policyNamePrefix; |
| if (networkRateMbps != null) { |
| averageBandwidth = networkRateMbps.intValue(); |
| policyName += averageBandwidth; |
| } |
| |
| if (averageBandwidth > 0) { |
| PolicyMap policyMap = netconfClient.getPolicyMapByName(portProfile.inputPolicyMap); |
| if (policyMap.committedRate == averageBandwidth && policyMap.peakRate == peakBandwidth && policyMap.burstRate == burstRate) { |
| LOGGER.debug("Detected that policy map is already applied to port profile " + vethPortProfileName); |
| if (netconfClient != null) { |
| netconfClient.disconnect(); |
| LOGGER.debug("Disconnected Nexus 1000v session."); |
| } |
| return; |
| } else { |
| try { |
| // TODO(sateesh): Change the type of peakBandwidth & |
| // burstRate in PolicyMap to long. |
| LOGGER.info("Adding policy map " + policyName); |
| netconfClient.addPolicyMap(policyName, averageBandwidth, (int)peakBandwidth, (int)burstRate); |
| } catch (CloudRuntimeException e) { |
| msg = |
| "Failed to add policy map of " + policyName + " with parameters " + "committed rate = " + averageBandwidth + "peak bandwidth = " + peakBandwidth + |
| "burst size = " + burstRate + ". Exception: " + e.toString(); |
| LOGGER.error(msg); |
| if (netconfClient != null) { |
| netconfClient.disconnect(); |
| LOGGER.debug("Disconnected Nexus 1000v session."); |
| } |
| throw new CloudRuntimeException(msg); |
| } |
| |
| try { |
| LOGGER.info("Associating policy map " + policyName + " with port profile " + vethPortProfileName + "."); |
| netconfClient.attachServicePolicy(policyName, vethPortProfileName); |
| } catch (CloudRuntimeException e) { |
| msg = "Failed to associate policy map " + policyName + " with port profile " + vethPortProfileName + ". Exception: " + e.toString(); |
| LOGGER.error(msg); |
| if (netconfClient != null) { |
| netconfClient.disconnect(); |
| LOGGER.debug("Disconnected Nexus 1000v session."); |
| } |
| throw new CloudRuntimeException(msg); |
| } |
| } |
| } |
| |
| if (vlanId == null) { |
| LOGGER.info("Skipping update operation over ethernet port profile " + ethPortProfileName + " for untagged VLAN."); |
| if (netconfClient != null) { |
| netconfClient.disconnect(); |
| LOGGER.debug("Disconnected Nexus 1000v session."); |
| } |
| return; |
| } |
| |
| String currentVlan = portProfile.vlan; |
| String newVlan = Integer.toString(vlanId.intValue()); |
| if (currentVlan.equalsIgnoreCase(newVlan)) { |
| if (netconfClient != null) { |
| netconfClient.disconnect(); |
| LOGGER.debug("Disconnected Nexus 1000v session."); |
| } |
| return; |
| } |
| |
| List<Pair<OperationType, String>> params = new ArrayList<Pair<OperationType, String>>(); |
| params.add(new Pair<OperationType, String>(OperationType.addvlanid, newVlan)); |
| try { |
| LOGGER.info("Updating vEthernet port profile with VLAN " + vlanId.toString()); |
| netconfClient.updatePortProfile(ethPortProfileName, SwitchPortMode.trunk, params); |
| } catch (CloudRuntimeException e) { |
| msg = "Failed to update ethernet port profile " + ethPortProfileName + " with parameters " + params.toString() + ". Exception: " + e.toString(); |
| LOGGER.error(msg); |
| if (netconfClient != null) { |
| netconfClient.disconnect(); |
| LOGGER.debug("Disconnected Nexus 1000v session."); |
| } |
| throw new CloudRuntimeException(msg); |
| } |
| |
| try { |
| netconfClient.updatePortProfile(vethPortProfileName, SwitchPortMode.access, params); |
| } catch (CloudRuntimeException e) { |
| msg = "Failed to update vEthernet port profile " + vethPortProfileName + " with parameters " + params.toString() + ". Exception: " + e.toString(); |
| LOGGER.error(msg); |
| if (netconfClient != null) { |
| netconfClient.disconnect(); |
| LOGGER.debug("Disconnected Nexus 1000v session."); |
| } |
| throw new CloudRuntimeException(msg); |
| } |
| } |
| |
| /** |
| * Prepares network (for non-standard virtual switch) for the VM NIC based on the parameters. |
| * Can create a new portgroup or update an existing. |
| * @return Pair of network's ManagedObjectReference and name |
| * @throws Exception |
| */ |
| |
| public static Pair<ManagedObjectReference, String> prepareNetwork(String physicalNetwork, String namePrefix, HostMO hostMo, String vlanId, String secondaryvlanId, |
| Integer networkRateMbps, Integer networkRateMulticastMbps, long timeOutMs, VirtualSwitchType vSwitchType, int numPorts, String gateway, |
| boolean configureVServiceInNexus, BroadcastDomainType broadcastDomainType, Map<String, String> vsmCredentials, |
| Map<NetworkOffering.Detail, String> details, String netName) throws Exception { |
| ManagedObjectReference morNetwork = null; |
| VmwareContext context = hostMo.getContext(); |
| ManagedObjectReference dcMor = hostMo.getHyperHostDatacenter(); |
| DatacenterMO dataCenterMo = new DatacenterMO(context, dcMor); |
| DistributedVirtualSwitchMO dvSwitchMo = null; |
| ManagedObjectReference morEthernetPortProfile = null; |
| String ethPortProfileName = null; |
| ManagedObjectReference morDvSwitch = null; |
| String dvSwitchName = null; |
| boolean bWaitPortGroupReady = false; |
| boolean createGCTag = false; |
| String vcApiVersion; |
| String minVcApiVersionSupportingAutoExpand; |
| boolean autoExpandSupported; |
| String networkName; |
| Integer vid = null; |
| Integer spvlanid = null; // secondary pvlan id |
| |
| /** This is the list of BroadcastDomainTypes we can actually |
| * prepare networks for in this function. |
| */ |
| BroadcastDomainType[] supportedBroadcastTypes = |
| new BroadcastDomainType[] {BroadcastDomainType.Lswitch, BroadcastDomainType.LinkLocal, BroadcastDomainType.Native, BroadcastDomainType.Pvlan, |
| BroadcastDomainType.Storage, BroadcastDomainType.UnDecided, BroadcastDomainType.Vlan, BroadcastDomainType.NSX}; |
| |
| if (!Arrays.asList(supportedBroadcastTypes).contains(broadcastDomainType)) { |
| throw new InvalidParameterException("BroadcastDomainType " + broadcastDomainType + " it not supported on a VMWare hypervisor at this time."); |
| } |
| |
| if (broadcastDomainType == BroadcastDomainType.Lswitch) { |
| if (vSwitchType == VirtualSwitchType.NexusDistributedVirtualSwitch) { |
| throw new InvalidParameterException("Nexus Distributed Virtualswitch is not supported with BroadcastDomainType " + broadcastDomainType); |
| } |
| /** |
| * Nicira NVP requires all vms to be connected to a single port-group. |
| * A unique vlan needs to be set per port. This vlan is specific to |
| * this implementation and has no reference to other vlans in CS |
| */ |
| networkName = "br-int"; // FIXME Should be set via a configuration item in CS |
| // No doubt about this, depending on vid=null to avoid lots of code below |
| vid = null; |
| } else { |
| if (vlanId != null) { |
| vlanId = vlanId.replace("vlan://", ""); |
| } |
| networkName = composeCloudNetworkName(namePrefix, vlanId, secondaryvlanId, networkRateMbps, physicalNetwork); |
| |
| if (vlanId != null && !UNTAGGED_VLAN_NAME.equalsIgnoreCase(vlanId) && !StringUtils.containsAny(vlanId, ",-")) { |
| createGCTag = true; |
| vid = Integer.parseInt(vlanId); |
| } |
| if (vlanId != null && StringUtils.containsAny(vlanId, ",-")) { |
| createGCTag = true; |
| } |
| if (secondaryvlanId != null) { |
| spvlanid = Integer.parseInt(secondaryvlanId); |
| } |
| } |
| |
| if (vSwitchType == VirtualSwitchType.VMwareDistributedVirtualSwitch) { |
| vcApiVersion = getVcenterApiVersion(context); |
| minVcApiVersionSupportingAutoExpand = "5.0"; |
| autoExpandSupported = isFeatureSupportedInVcenterApiVersion(vcApiVersion, minVcApiVersionSupportingAutoExpand); |
| |
| dvSwitchName = physicalNetwork; |
| // TODO(sateesh): Remove this after ensuring proper default value for vSwitchName throughout traffic types |
| // and switch types. |
| if (dvSwitchName == null) { |
| LOGGER.warn("Detected null dvSwitch. Defaulting to dvSwitch0"); |
| dvSwitchName = "dvSwitch0"; |
| } |
| morDvSwitch = dataCenterMo.getDvSwitchMor(dvSwitchName); |
| if (morDvSwitch == null) { |
| String msg = "Unable to find distributed vSwitch " + dvSwitchName; |
| LOGGER.error(msg); |
| throw new Exception(msg); |
| } |
| dvSwitchMo = new DistributedVirtualSwitchMO(context, morDvSwitch); |
| String dvSwitchVersion = dvSwitchMo.getDVSProductVersion(morDvSwitch); |
| LOGGER.debug(String.format("Found distributed vSwitch: %s with product version: %s", dvSwitchName, dvSwitchVersion)); |
| |
| if (broadcastDomainType == BroadcastDomainType.Lswitch) { |
| if (!dataCenterMo.hasDvPortGroup(networkName)) { |
| throw new InvalidParameterException("NVP integration port-group " + networkName + " does not exist on the DVS " + dvSwitchName); |
| } |
| bWaitPortGroupReady = false; |
| } else if (BroadcastDomainType.NSX == broadcastDomainType && Objects.nonNull(netName)){ |
| networkName = netName; |
| bWaitPortGroupReady = false; |
| } else { |
| boolean dvSwitchSupportNewPolicies = (isFeatureSupportedInVcenterApiVersion(vcApiVersion, MINIMUM_VCENTER_API_VERSION_WITH_DVS_NEW_POLICIES_SUPPORT) |
| && isVersionEqualOrHigher(dvSwitchVersion, MINIMUM_DVS_VERSION_WITH_NEW_POLICIES_SUPPORT)); |
| DVSTrafficShapingPolicy shapingPolicy = getDVSShapingPolicy(networkRateMbps); |
| DVSSecurityPolicy secPolicy = createDVSSecurityPolicy(details); |
| DVSMacManagementPolicy macManagementPolicy = createDVSMacManagementPolicy(details); |
| |
| // First, if both vlan id and pvlan id are provided, we need to |
| // reconfigure the DVSwitch to have a tuple <vlan id, pvlan id> of |
| // type isolated. |
| String pvlanType = MapUtils.isNotEmpty(details) ? details.get(NetworkOffering.Detail.pvlanType) : null; |
| if (vid != null && spvlanid != null) { |
| setupPVlanPair(dvSwitchMo, morDvSwitch, vid, spvlanid, pvlanType); |
| } |
| |
| VMwareDVSPortgroupPolicy portGroupPolicy = null; |
| // Next, create the port group. For this, we need to create a VLAN spec. |
| createPortGroup(physicalNetwork, networkName, vlanId, vid, spvlanid, dataCenterMo, shapingPolicy, |
| secPolicy, macManagementPolicy, portGroupPolicy, dvSwitchMo, numPorts, autoExpandSupported, |
| dvSwitchSupportNewPolicies); |
| bWaitPortGroupReady = true; |
| } |
| } else if (vSwitchType == VirtualSwitchType.NexusDistributedVirtualSwitch) { |
| |
| ethPortProfileName = physicalNetwork; |
| // TODO(sateesh): Remove this after ensuring proper default value for vSwitchName throughout traffic types |
| // and switch types. |
| if (ethPortProfileName == null) { |
| LOGGER.warn("Detected null ethrenet port profile. Defaulting to epp0."); |
| ethPortProfileName = "epp0"; |
| } |
| morEthernetPortProfile = dataCenterMo.getDvPortGroupMor(ethPortProfileName); |
| if (morEthernetPortProfile == null) { |
| String msg = "Unable to find Ethernet port profile " + ethPortProfileName; |
| LOGGER.error(msg); |
| throw new Exception(msg); |
| } else { |
| LOGGER.info("Found Ethernet port profile " + ethPortProfileName); |
| } |
| long averageBandwidth = 0L; |
| if (networkRateMbps != null && networkRateMbps.intValue() > 0) { |
| averageBandwidth = networkRateMbps.intValue() * 1024L * 1024L; |
| } |
| // We chose 50% higher allocation than average bandwidth. |
| // TODO(sateesh): Optionally let user specify the peak coefficient |
| long peakBandwidth = (long)(averageBandwidth * 1.5); |
| // TODO(sateesh): Optionally let user specify the burst coefficient |
| long burstSize = 5 * averageBandwidth / 8; |
| if (vsmCredentials != null) { |
| LOGGER.info("Stocking credentials of Nexus VSM"); |
| context.registerStockObject("vsmcredentials", vsmCredentials); |
| } |
| |
| if (!dataCenterMo.hasDvPortGroup(networkName)) { |
| LOGGER.info("Port profile " + networkName + " not found."); |
| createPortProfile(context, physicalNetwork, networkName, vid, networkRateMbps, peakBandwidth, burstSize, gateway, configureVServiceInNexus); |
| bWaitPortGroupReady = true; |
| } else { |
| LOGGER.info("Port profile " + networkName + " found."); |
| updatePortProfile(context, physicalNetwork, networkName, vid, networkRateMbps, peakBandwidth, burstSize); |
| } |
| } |
| // Wait for dvPortGroup on vCenter |
| if (bWaitPortGroupReady) |
| morNetwork = waitForDvPortGroupReady(dataCenterMo, networkName, timeOutMs); |
| else |
| morNetwork = dataCenterMo.getDvPortGroupMor(networkName); |
| if (morNetwork == null) { |
| String msg = "Failed to create guest network " + networkName; |
| LOGGER.error(msg); |
| throw new Exception(msg); |
| } |
| |
| if (createGCTag) { |
| NetworkMO networkMo = new NetworkMO(hostMo.getContext(), morNetwork); |
| networkMo.setCustomFieldValue(CustomFieldConstants.CLOUD_GC_DVP, "true"); |
| LOGGER.debug("Added custom field : " + CustomFieldConstants.CLOUD_GC_DVP); |
| } |
| |
| return new Pair<ManagedObjectReference, String>(morNetwork, networkName); |
| } |
| |
| public static String getVcenterApiVersion(VmwareContext serviceContext) throws Exception { |
| String vcApiVersion = null; |
| if (serviceContext != null) { |
| vcApiVersion = serviceContext.getServiceContent().getAbout().getApiVersion(); |
| } |
| return vcApiVersion; |
| } |
| |
| public static boolean isFeatureSupportedInVcenterApiVersion(String vCenterApiVersion, String minVcenterApiVersionForFeature) { |
| return isVersionEqualOrHigher(vCenterApiVersion, minVcenterApiVersionForFeature); |
| } |
| |
| private static void setupPVlanPair(DistributedVirtualSwitchMO dvSwitchMo, ManagedObjectReference morDvSwitch, Integer vid, Integer spvlanid, String pvlanType) throws Exception { |
| LOGGER.debug(String.format("Setting up PVLAN on dvSwitch %s with the following information: %s %s %s", dvSwitchMo.getName(), vid, spvlanid, pvlanType)); |
| Map<Integer, HypervisorHostHelper.PvlanType> vlanmap = dvSwitchMo.retrieveVlanPvlan(vid, spvlanid, morDvSwitch); |
| if (!vlanmap.isEmpty()) { |
| // Then either vid or pvlanid or both are already being used. Check how. |
| // First the primary pvlan id. |
| if (vlanmap.containsKey(vid) && !vlanmap.get(vid).equals(HypervisorHostHelper.PvlanType.promiscuous)) { |
| // This VLAN ID is already setup as a non-promiscuous vlan id on the DVS. Throw an exception. |
| String msg = "Specified primary PVLAN ID " + vid + " is already in use as a " + vlanmap.get(vid).toString() + " VLAN on the DVSwitch"; |
| LOGGER.error(msg); |
| throw new Exception(msg); |
| } |
| // Next the secondary pvlan id. |
| if (spvlanid.equals(vid)) { |
| if (vlanmap.containsKey(spvlanid) && !vlanmap.get(spvlanid).equals(HypervisorHostHelper.PvlanType.promiscuous)) { |
| String msg = "Specified secondary PVLAN ID " + spvlanid + " is already in use as a " + vlanmap.get(spvlanid).toString() + " VLAN in the DVSwitch"; |
| LOGGER.error(msg); |
| throw new Exception(msg); |
| } |
| } |
| } |
| |
| // First create a DVSconfig spec. |
| VMwareDVSConfigSpec dvsSpec = new VMwareDVSConfigSpec(); |
| // Next, add the required primary and secondary vlan config specs to the dvs config spec. |
| |
| if (!vlanmap.containsKey(vid)) { |
| VMwareDVSPvlanConfigSpec ppvlanConfigSpec = createDVPortPvlanConfigSpec(vid, vid, PvlanType.promiscuous, PvlanOperation.add); |
| dvsSpec.getPvlanConfigSpec().add(ppvlanConfigSpec); |
| } |
| if (!vid.equals(spvlanid) && !vlanmap.containsKey(spvlanid)) { |
| PvlanType selectedType = StringUtils.isNotBlank(pvlanType) ? PvlanType.fromStr(pvlanType) : PvlanType.isolated; |
| VMwareDVSPvlanConfigSpec spvlanConfigSpec = createDVPortPvlanConfigSpec(vid, spvlanid, selectedType, PvlanOperation.add); |
| dvsSpec.getPvlanConfigSpec().add(spvlanConfigSpec); |
| } |
| |
| if (dvsSpec.getPvlanConfigSpec().size() > 0) { |
| // We have something to configure on the DVS... so send it the command. |
| // When reconfiguring a vmware DVSwitch, we need to send in the configVersion in the spec. |
| // Let's retrieve this switch's configVersion first. |
| String dvsConfigVersion = dvSwitchMo.getDVSConfigVersion(morDvSwitch); |
| dvsSpec.setConfigVersion(dvsConfigVersion); |
| |
| // Reconfigure the dvs using this spec. |
| try { |
| dvSwitchMo.updateVMWareDVSwitchGetTask(morDvSwitch, dvsSpec); |
| } catch (AlreadyExistsFaultMsg e) { |
| LOGGER.info("Specified vlan id (" + vid + ") private vlan id (" + spvlanid + ") tuple already configured on VMWare DVSwitch"); |
| // Do nothing, good if the tuple's already configured on the dvswitch. |
| } catch (Exception e) { |
| // Rethrow the exception |
| LOGGER.error("Failed to configure vlan/pvlan tuple on VMware DVSwitch: " + vid + "/" + spvlanid + ", failure message: ", e); |
| throw e; |
| } |
| } |
| |
| } |
| |
| private static void createPortGroup(String physicalNetwork, String networkName, String vlanRange, Integer vid, Integer spvlanid, DatacenterMO dataCenterMo, |
| DVSTrafficShapingPolicy shapingPolicy, DVSSecurityPolicy secPolicy, DVSMacManagementPolicy macManagementPolicy, |
| VMwareDVSPortgroupPolicy portGroupPolicy, DistributedVirtualSwitchMO dvSwitchMo, int numPorts, boolean autoExpandSupported, |
| boolean dvSwitchSupportNewPolicies) |
| throws Exception { |
| VmwareDistributedVirtualSwitchVlanSpec vlanSpec = null; |
| VmwareDistributedVirtualSwitchPvlanSpec pvlanSpec = null; |
| VMwareDVSPortSetting dvsPortSetting = null; |
| DVPortgroupConfigSpec newDvPortGroupSpec; |
| |
| // Next, create the port group. For this, we need to create a VLAN spec. |
| // NOTE - VmwareDistributedVirtualSwitchPvlanSpec extends VmwareDistributedVirtualSwitchVlanSpec. |
| if (vid == null || spvlanid == null) { |
| vlanSpec = createDVPortVlanSpec(vid, vlanRange); |
| dvsPortSetting = createVmwareDVPortSettingSpec(shapingPolicy, secPolicy, macManagementPolicy, vlanSpec, dvSwitchSupportNewPolicies); |
| } else if (spvlanid != null) { |
| // Create a pvlan spec. The pvlan spec is different from the pvlan config spec |
| // that we created earlier. The pvlan config spec is used to configure the switch |
| // with a <primary vlanId, secondary vlanId> tuple. The pvlan spec is used |
| // to configure a port group (i.e., a network) with a secondary vlan id. We don't |
| // need to mention more than the secondary vlan id because one secondary vlan id |
| // can be associated with only one primary vlan id. Give vCenter the secondary vlan id, |
| // and it will find out the associated primary vlan id and do the rest of the |
| // port group configuration. |
| pvlanSpec = createDVPortPvlanIdSpec(spvlanid); |
| dvsPortSetting = createVmwareDVPortSettingSpec(shapingPolicy, secPolicy, macManagementPolicy, pvlanSpec, dvSwitchSupportNewPolicies); |
| } |
| |
| newDvPortGroupSpec = createDvPortGroupSpec(networkName, dvsPortSetting, autoExpandSupported); |
| if (portGroupPolicy != null) { |
| newDvPortGroupSpec.setPolicy(portGroupPolicy); |
| } |
| |
| if (!dataCenterMo.hasDvPortGroup(networkName)) { |
| LOGGER.info("Distributed Virtual Port group " + networkName + " not found."); |
| // TODO(sateesh): Handle Exceptions |
| try { |
| newDvPortGroupSpec.setNumPorts(numPorts); |
| dvSwitchMo.createDVPortGroup(newDvPortGroupSpec); |
| } catch (Exception e) { |
| String msg = "Failed to create distributed virtual port group " + networkName + " on dvSwitch " + physicalNetwork; |
| msg += ". " + VmwareHelper.getExceptionMessage(e); |
| throw new Exception(msg); |
| } |
| } else { |
| LOGGER.info("Found Distributed Virtual Port group " + networkName); |
| DVPortgroupConfigInfo currentDvPortgroupInfo = dataCenterMo.getDvPortGroupSpec(networkName); |
| if (!isSpecMatch(currentDvPortgroupInfo, newDvPortGroupSpec, dvSwitchSupportNewPolicies)) { |
| LOGGER.info("Updating Distributed Virtual Port group " + networkName); |
| newDvPortGroupSpec.setDefaultPortConfig(dvsPortSetting); |
| newDvPortGroupSpec.setConfigVersion(currentDvPortgroupInfo.getConfigVersion()); |
| ManagedObjectReference morDvPortGroup = dataCenterMo.getDvPortGroupMor(networkName); |
| try { |
| dvSwitchMo.updateDvPortGroup(morDvPortGroup, newDvPortGroupSpec); |
| } catch (Exception e) { |
| String msg = "Failed to update distributed virtual port group " + networkName + " on dvSwitch " + physicalNetwork; |
| msg += ". " + VmwareHelper.getExceptionMessage(e); |
| throw new Exception(msg); |
| } |
| } |
| } |
| } |
| |
| private static boolean eitherObjectNull(Object obj1, Object obj2) { |
| return (obj1 == null && obj2 != null) || (obj1 != null && obj2 == null); |
| } |
| |
| private static boolean areBoolPoliciesDifferent(BoolPolicy currentPolicy, BoolPolicy newPolicy) { |
| return eitherObjectNull(currentPolicy, newPolicy) || |
| (newPolicy != null && newPolicy.isValue() != currentPolicy.isValue()); |
| } |
| |
| private static boolean areDVSSecurityPoliciesDifferent(DVSSecurityPolicy currentSecurityPolicy, DVSSecurityPolicy newSecurityPolicy) { |
| return eitherObjectNull(currentSecurityPolicy, newSecurityPolicy) || |
| (newSecurityPolicy != null && |
| (areBoolPoliciesDifferent(currentSecurityPolicy.getAllowPromiscuous(), newSecurityPolicy.getAllowPromiscuous()) || |
| areBoolPoliciesDifferent(currentSecurityPolicy.getForgedTransmits(), newSecurityPolicy.getForgedTransmits()) || |
| areBoolPoliciesDifferent(currentSecurityPolicy.getMacChanges(), newSecurityPolicy.getMacChanges()))); |
| } |
| |
| private static boolean areDVSMacLearningPoliciesDifferent(DVSMacLearningPolicy currentMacLearningPolicy, DVSMacLearningPolicy newMacLearningPolicy) { |
| return eitherObjectNull(currentMacLearningPolicy, newMacLearningPolicy) || |
| (newMacLearningPolicy != null && currentMacLearningPolicy.isEnabled() != newMacLearningPolicy.isEnabled()); |
| } |
| |
| private static boolean areDVSMacManagementPoliciesDifferent(DVSMacManagementPolicy currentMacManagementPolicy, DVSMacManagementPolicy newMacManagementPolicy) { |
| return eitherObjectNull(currentMacManagementPolicy, newMacManagementPolicy) || |
| (newMacManagementPolicy != null && |
| (currentMacManagementPolicy.isAllowPromiscuous() != newMacManagementPolicy.isAllowPromiscuous() || |
| currentMacManagementPolicy.isForgedTransmits() != newMacManagementPolicy.isForgedTransmits() || |
| currentMacManagementPolicy.isMacChanges() != newMacManagementPolicy.isMacChanges() || |
| areDVSMacLearningPoliciesDifferent(currentMacManagementPolicy.getMacLearningPolicy(), newMacManagementPolicy.getMacLearningPolicy()))); |
| } |
| |
| private static boolean isDVSPortConfigSame(String dvPortGroupName, VMwareDVSPortSetting currentPortSetting, VMwareDVSPortSetting newPortSetting, boolean dvSwitchSupportNewPolicies) { |
| if (areDVSSecurityPoliciesDifferent(currentPortSetting.getSecurityPolicy(), newPortSetting.getSecurityPolicy())) { |
| return false; |
| } |
| if (dvSwitchSupportNewPolicies && areDVSMacManagementPoliciesDifferent(currentPortSetting.getMacManagementPolicy(), newPortSetting.getMacManagementPolicy())) { |
| return false; |
| } |
| |
| VmwareDistributedVirtualSwitchVlanSpec oldVlanSpec = currentPortSetting.getVlan(); |
| VmwareDistributedVirtualSwitchVlanSpec newVlanSpec = newPortSetting.getVlan(); |
| |
| int oldVlanId, newVlanId; |
| if (oldVlanSpec instanceof VmwareDistributedVirtualSwitchPvlanSpec && newVlanSpec instanceof VmwareDistributedVirtualSwitchPvlanSpec) { |
| VmwareDistributedVirtualSwitchPvlanSpec oldpVlanSpec = (VmwareDistributedVirtualSwitchPvlanSpec) oldVlanSpec; |
| VmwareDistributedVirtualSwitchPvlanSpec newpVlanSpec = (VmwareDistributedVirtualSwitchPvlanSpec) newVlanSpec; |
| oldVlanId = oldpVlanSpec.getPvlanId(); |
| newVlanId = newpVlanSpec.getPvlanId(); |
| } else if (oldVlanSpec instanceof VmwareDistributedVirtualSwitchTrunkVlanSpec && newVlanSpec instanceof VmwareDistributedVirtualSwitchTrunkVlanSpec) { |
| VmwareDistributedVirtualSwitchTrunkVlanSpec oldpVlanSpec = (VmwareDistributedVirtualSwitchTrunkVlanSpec) oldVlanSpec; |
| VmwareDistributedVirtualSwitchTrunkVlanSpec newpVlanSpec = (VmwareDistributedVirtualSwitchTrunkVlanSpec) newVlanSpec; |
| oldVlanId = oldpVlanSpec.getVlanId().get(0).getStart(); |
| newVlanId = newpVlanSpec.getVlanId().get(0).getStart(); |
| } else if (oldVlanSpec instanceof VmwareDistributedVirtualSwitchVlanIdSpec && newVlanSpec instanceof VmwareDistributedVirtualSwitchVlanIdSpec) { |
| VmwareDistributedVirtualSwitchVlanIdSpec oldVlanIdSpec = (VmwareDistributedVirtualSwitchVlanIdSpec) oldVlanSpec; |
| VmwareDistributedVirtualSwitchVlanIdSpec newVlanIdSpec = (VmwareDistributedVirtualSwitchVlanIdSpec) newVlanSpec; |
| oldVlanId = oldVlanIdSpec.getVlanId(); |
| newVlanId = newVlanIdSpec.getVlanId(); |
| } else { |
| LOGGER.debug(String.format("Old and new vlan spec type mismatch found for dvPortGroup: %s. Old spec type is: %s, and new spec type is: %s", dvPortGroupName, oldVlanSpec.getClass(), newVlanSpec.getClass())); |
| return false; |
| } |
| |
| if (oldVlanId != newVlanId) { |
| LOGGER.info(String.format("Detected that new VLAN [%d] is different from current VLAN [%d] of dvPortGroup: %s", newVlanId, oldVlanId, dvPortGroupName)); |
| return false; |
| } |
| return true; |
| } |
| |
| public static boolean isSpecMatch(DVPortgroupConfigInfo currentDvPortgroupInfo, DVPortgroupConfigSpec newDvPortGroupSpec, boolean dvSwitchSupportNewPolicies) { |
| String dvPortGroupName = newDvPortGroupSpec.getName(); |
| LOGGER.debug("Checking if configuration of dvPortGroup [" + dvPortGroupName + "] has changed."); |
| DVSTrafficShapingPolicy currentTrafficShapingPolicy; |
| currentTrafficShapingPolicy = currentDvPortgroupInfo.getDefaultPortConfig().getInShapingPolicy(); |
| |
| assert (currentTrafficShapingPolicy != null); |
| |
| LongPolicy oldAverageBandwidthPolicy = currentTrafficShapingPolicy.getAverageBandwidth(); |
| LongPolicy oldBurstSizePolicy = currentTrafficShapingPolicy.getBurstSize(); |
| LongPolicy oldPeakBandwidthPolicy = currentTrafficShapingPolicy.getPeakBandwidth(); |
| BoolPolicy oldIsEnabledPolicy = currentTrafficShapingPolicy.getEnabled(); |
| Long oldAverageBandwidth = null; |
| Long oldBurstSize = null; |
| Long oldPeakBandwidth = null; |
| Boolean oldIsEnabled = null; |
| |
| if (oldAverageBandwidthPolicy != null) { |
| oldAverageBandwidth = oldAverageBandwidthPolicy.getValue(); |
| } |
| if (oldBurstSizePolicy != null) { |
| oldBurstSize = oldBurstSizePolicy.getValue(); |
| } |
| if (oldPeakBandwidthPolicy != null) { |
| oldPeakBandwidth = oldPeakBandwidthPolicy.getValue(); |
| } |
| if (oldIsEnabledPolicy != null) { |
| oldIsEnabled = oldIsEnabledPolicy.isValue(); |
| } |
| |
| DVSTrafficShapingPolicy newTrafficShapingPolicyInbound = newDvPortGroupSpec.getDefaultPortConfig().getInShapingPolicy(); |
| LongPolicy newAverageBandwidthPolicy = newTrafficShapingPolicyInbound.getAverageBandwidth(); |
| LongPolicy newBurstSizePolicy = newTrafficShapingPolicyInbound.getBurstSize(); |
| LongPolicy newPeakBandwidthPolicy = newTrafficShapingPolicyInbound.getPeakBandwidth(); |
| BoolPolicy newIsEnabledPolicy = newTrafficShapingPolicyInbound.getEnabled(); |
| Long newAverageBandwidth = null; |
| Long newBurstSize = null; |
| Long newPeakBandwidth = null; |
| Boolean newIsEnabled = null; |
| if (newAverageBandwidthPolicy != null) { |
| newAverageBandwidth = newAverageBandwidthPolicy.getValue(); |
| } |
| if (newBurstSizePolicy != null) { |
| newBurstSize = newBurstSizePolicy.getValue(); |
| } |
| if (newPeakBandwidthPolicy != null) { |
| newPeakBandwidth = newPeakBandwidthPolicy.getValue(); |
| } |
| if (newIsEnabledPolicy != null) { |
| newIsEnabled = newIsEnabledPolicy.isValue(); |
| } |
| |
| if (!oldIsEnabled.equals(newIsEnabled)) { |
| LOGGER.info("Detected change in state of shaping policy (enabled/disabled) [" + newIsEnabled + "]"); |
| return false; |
| } |
| |
| if (oldIsEnabled || newIsEnabled) { |
| if (oldAverageBandwidth != null && !oldAverageBandwidth.equals(newAverageBandwidth)) { |
| LOGGER.info("Average bandwidth setting in new shaping policy doesn't match the existing setting."); |
| return false; |
| } else if (oldBurstSize != null && !oldBurstSize.equals(newBurstSize)) { |
| LOGGER.info("Burst size setting in new shaping policy doesn't match the existing setting."); |
| return false; |
| } else if (oldPeakBandwidth != null && !oldPeakBandwidth.equals(newPeakBandwidth)) { |
| LOGGER.info("Peak bandwidth setting in new shaping policy doesn't match the existing setting."); |
| return false; |
| } |
| } |
| |
| boolean oldAutoExpandSetting = currentDvPortgroupInfo.isAutoExpand(); |
| boolean autoExpandEnabled = newDvPortGroupSpec.isAutoExpand(); |
| if (oldAutoExpandSetting != autoExpandEnabled) { |
| return false; |
| } |
| if (!autoExpandEnabled) { |
| // Allow update of number of dvports per dvPortGroup is auto expand is not enabled. |
| int oldNumPorts = currentDvPortgroupInfo.getNumPorts(); |
| int newNumPorts = newDvPortGroupSpec.getNumPorts(); |
| if (oldNumPorts < newNumPorts) { |
| LOGGER.info("Need to update the number of dvports for dvPortGroup :[" + dvPortGroupName + |
| "] from existing number of dvports " + oldNumPorts + " to " + newNumPorts); |
| return false; |
| } else if (oldNumPorts > newNumPorts) { |
| LOGGER.warn("Detected that new number of dvports [" + newNumPorts + "] in dvPortGroup [" + dvPortGroupName + |
| "] is less than existing number of dvports [" + oldNumPorts + "]. Attempt to update this dvPortGroup may fail!"); |
| return false; |
| } |
| } |
| |
| VMwareDVSPortSetting currentPortSetting = ((VMwareDVSPortSetting)currentDvPortgroupInfo.getDefaultPortConfig()); |
| VMwareDVSPortSetting newPortSetting = ((VMwareDVSPortSetting)newDvPortGroupSpec.getDefaultPortConfig()); |
| return isDVSPortConfigSame(dvPortGroupName, currentPortSetting, newPortSetting, dvSwitchSupportNewPolicies); |
| } |
| |
| public static ManagedObjectReference waitForDvPortGroupReady(DatacenterMO dataCenterMo, String dvPortGroupName, long timeOutMs) throws Exception { |
| ManagedObjectReference morDvPortGroup = null; |
| |
| // if DvPortGroup is just created, we may fail to retrieve it, we |
| // need to retry |
| long startTick = System.currentTimeMillis(); |
| while (System.currentTimeMillis() - startTick <= timeOutMs) { |
| morDvPortGroup = dataCenterMo.getDvPortGroupMor(dvPortGroupName); |
| if (morDvPortGroup != null) { |
| break; |
| } |
| |
| LOGGER.info("Waiting for dvPortGroup " + dvPortGroupName + " to be ready"); |
| Thread.sleep(1000); |
| } |
| return morDvPortGroup; |
| } |
| |
| public static boolean isSpecMatch(DVPortgroupConfigInfo configInfo, Integer vid, DVSTrafficShapingPolicy shapingPolicy) { |
| DVSTrafficShapingPolicy currentTrafficShapingPolicy; |
| currentTrafficShapingPolicy = configInfo.getDefaultPortConfig().getInShapingPolicy(); |
| |
| assert (currentTrafficShapingPolicy != null); |
| |
| LongPolicy averageBandwidth = currentTrafficShapingPolicy.getAverageBandwidth(); |
| LongPolicy burstSize = currentTrafficShapingPolicy.getBurstSize(); |
| LongPolicy peakBandwidth = currentTrafficShapingPolicy.getPeakBandwidth(); |
| BoolPolicy isEnabled = currentTrafficShapingPolicy.getEnabled(); |
| |
| if (!isEnabled.equals(shapingPolicy.getEnabled())) { |
| return false; |
| } |
| |
| if (averageBandwidth != null && !averageBandwidth.equals(shapingPolicy.getAverageBandwidth())) { |
| if (LOGGER.isInfoEnabled()) { |
| LOGGER.info("Average bandwidth setting in shaping policy doesn't match with existing setting."); |
| } |
| return false; |
| } else if (burstSize != null && !burstSize.equals(shapingPolicy.getBurstSize())) { |
| if (LOGGER.isInfoEnabled()) { |
| LOGGER.info("Burst size setting in shaping policy doesn't match with existing setting."); |
| } |
| return false; |
| } else if (peakBandwidth != null && !peakBandwidth.equals(shapingPolicy.getPeakBandwidth())) { |
| if (LOGGER.isInfoEnabled()) { |
| LOGGER.info("Peak bandwidth setting in shaping policy doesn't match with existing setting."); |
| } |
| return false; |
| } |
| |
| return true; |
| } |
| |
| public static DVPortgroupConfigSpec createDvPortGroupSpec(String dvPortGroupName, DVPortSetting portSetting, boolean autoExpandSupported) { |
| DVPortgroupConfigSpec spec = new DVPortgroupConfigSpec(); |
| spec.setName(dvPortGroupName); |
| spec.setDefaultPortConfig(portSetting); |
| spec.setPortNameFormat("vnic<portIndex>"); |
| spec.setType("earlyBinding"); |
| spec.setAutoExpand(autoExpandSupported); |
| return spec; |
| } |
| |
| public static VMwareDVSPortSetting createVmwareDVPortSettingSpec(DVSTrafficShapingPolicy shapingPolicy, DVSSecurityPolicy secPolicy, |
| DVSMacManagementPolicy macManagementPolicy, VmwareDistributedVirtualSwitchVlanSpec vlanSpec, boolean dvSwitchSupportNewPolicies) { |
| VMwareDVSPortSetting dvsPortSetting = new VMwareDVSPortSetting(); |
| dvsPortSetting.setVlan(vlanSpec); |
| dvsPortSetting.setSecurityPolicy(secPolicy); |
| if (dvSwitchSupportNewPolicies) { |
| dvsPortSetting.setMacManagementPolicy(macManagementPolicy); |
| } |
| dvsPortSetting.setInShapingPolicy(shapingPolicy); |
| dvsPortSetting.setOutShapingPolicy(shapingPolicy); |
| return dvsPortSetting; |
| } |
| |
| public static DVSTrafficShapingPolicy getDVSShapingPolicy(Integer networkRateMbps) { |
| DVSTrafficShapingPolicy shapingPolicy = new DVSTrafficShapingPolicy(); |
| BoolPolicy isEnabled = new BoolPolicy(); |
| if (networkRateMbps == null || networkRateMbps.intValue() <= 0) { |
| isEnabled.setValue(false); |
| shapingPolicy.setEnabled(isEnabled); |
| return shapingPolicy; |
| } |
| LongPolicy averageBandwidth = new LongPolicy(); |
| LongPolicy peakBandwidth = new LongPolicy(); |
| LongPolicy burstSize = new LongPolicy(); |
| |
| isEnabled.setValue(true); |
| averageBandwidth.setValue(networkRateMbps.intValue() * 1024L * 1024L); |
| // We chose 50% higher allocation than average bandwidth. |
| // TODO(sateesh): Also let user specify the peak coefficient |
| peakBandwidth.setValue((long)(averageBandwidth.getValue() * 1.5)); |
| // TODO(sateesh): Also let user specify the burst coefficient |
| burstSize.setValue(5 * averageBandwidth.getValue() / 8); |
| |
| shapingPolicy.setEnabled(isEnabled); |
| shapingPolicy.setAverageBandwidth(averageBandwidth); |
| shapingPolicy.setPeakBandwidth(peakBandwidth); |
| shapingPolicy.setBurstSize(burstSize); |
| |
| return shapingPolicy; |
| } |
| |
| public static VmwareDistributedVirtualSwitchPvlanSpec createDVPortPvlanIdSpec(int pvlanId) { |
| VmwareDistributedVirtualSwitchPvlanSpec pvlanIdSpec = new VmwareDistributedVirtualSwitchPvlanSpec(); |
| pvlanIdSpec.setPvlanId(pvlanId); |
| return pvlanIdSpec; |
| } |
| |
| public enum PvlanOperation { |
| add, edit, remove |
| } |
| |
| public enum PvlanType { |
| promiscuous, isolated, community; |
| |
| public static PvlanType fromStr(String val) { |
| if (StringUtils.isBlank(val)) { |
| return null; |
| } else if (val.equalsIgnoreCase("promiscuous")) { |
| return promiscuous; |
| } else if (val.equalsIgnoreCase("community")) { |
| return community; |
| } else if (val.equalsIgnoreCase("isolated")) { |
| return isolated; |
| } |
| return null; |
| } |
| } |
| |
| public static VMwareDVSPvlanConfigSpec createDVPortPvlanConfigSpec(int vlanId, int secondaryVlanId, PvlanType pvlantype, PvlanOperation operation) { |
| VMwareDVSPvlanConfigSpec pvlanConfigSpec = new VMwareDVSPvlanConfigSpec(); |
| VMwareDVSPvlanMapEntry map = new VMwareDVSPvlanMapEntry(); |
| map.setPvlanType(pvlantype.toString()); |
| map.setPrimaryVlanId(vlanId); |
| map.setSecondaryVlanId(secondaryVlanId); |
| pvlanConfigSpec.setPvlanEntry(map); |
| |
| pvlanConfigSpec.setOperation(operation.toString()); |
| return pvlanConfigSpec; |
| } |
| |
| public static VmwareDistributedVirtualSwitchVlanSpec createDVPortVlanSpec(Integer vlanId, String vlanRange) { |
| if (vlanId != null && vlanId == 4095){ |
| vlanId = null; |
| vlanRange = "0-4094"; |
| } |
| if (vlanId == null && vlanRange != null && !vlanRange.isEmpty()) { |
| LOGGER.debug("Creating dvSwitch port vlan-trunk spec with range: " + vlanRange); |
| VmwareDistributedVirtualSwitchTrunkVlanSpec trunkVlanSpec = new VmwareDistributedVirtualSwitchTrunkVlanSpec(); |
| for (final String vlanRangePart : vlanRange.split(",")) { |
| if (vlanRangePart == null || vlanRange.isEmpty()) { |
| continue; |
| } |
| final NumericRange numericRange = new NumericRange(); |
| if (vlanRangePart.contains("-")) { |
| final String[] range = vlanRangePart.split("-"); |
| if (range.length == 2 && range[0] != null && range[1] != null) { |
| numericRange.setStart(NumbersUtil.parseInt(range[0], 0)); |
| numericRange.setEnd(NumbersUtil.parseInt(range[1], 0)); |
| } else { |
| continue; |
| } |
| } else { |
| numericRange.setStart(NumbersUtil.parseInt(vlanRangePart, 0)); |
| numericRange.setEnd(NumbersUtil.parseInt(vlanRangePart, 0)); |
| } |
| trunkVlanSpec.getVlanId().add(numericRange); |
| } |
| if (trunkVlanSpec.getVlanId().size() != 0) { |
| return trunkVlanSpec; |
| } |
| } |
| VmwareDistributedVirtualSwitchVlanIdSpec vlanIdSpec = new VmwareDistributedVirtualSwitchVlanIdSpec(); |
| vlanIdSpec.setVlanId(vlanId == null ? 0 : vlanId); |
| LOGGER.debug("Creating dvSwitch port vlan-id spec with id: " + vlanIdSpec.getVlanId()); |
| return vlanIdSpec; |
| } |
| |
| public static Map<NetworkOffering.Detail, String> getDefaultSecurityDetails() { |
| final Map<NetworkOffering.Detail, String> details = new HashMap<>(); |
| details.put(NetworkOffering.Detail.PromiscuousMode, NetworkOrchestrationService.PromiscuousMode.value().toString()); |
| details.put(NetworkOffering.Detail.MacAddressChanges, NetworkOrchestrationService.MacAddressChanges.value().toString()); |
| details.put(NetworkOffering.Detail.ForgedTransmits, NetworkOrchestrationService.ForgedTransmits.value().toString()); |
| details.put(NetworkOffering.Detail.MacLearning, NetworkOrchestrationService.MacLearning.value().toString()); |
| return details; |
| } |
| |
| public static DVSSecurityPolicy createDVSSecurityPolicy(Map<NetworkOffering.Detail, String> nicDetails) { |
| DVSSecurityPolicy secPolicy = new DVSSecurityPolicy(); |
| BoolPolicy allow = new BoolPolicy(); |
| allow.setValue(true); |
| BoolPolicy deny = new BoolPolicy(); |
| deny.setValue(false); |
| secPolicy.setAllowPromiscuous(deny); |
| secPolicy.setForgedTransmits(allow); |
| secPolicy.setMacChanges(allow); |
| if (nicDetails == null) { |
| nicDetails = getDefaultSecurityDetails(); |
| } |
| if (nicDetails.containsKey(NetworkOffering.Detail.PromiscuousMode)) { |
| if (Boolean.parseBoolean(nicDetails.get(NetworkOffering.Detail.PromiscuousMode))) { |
| secPolicy.setAllowPromiscuous(allow); |
| } else { |
| secPolicy.setAllowPromiscuous(deny); |
| } |
| } |
| if (nicDetails.containsKey(NetworkOffering.Detail.ForgedTransmits)) { |
| if (Boolean.parseBoolean(nicDetails.get(NetworkOffering.Detail.ForgedTransmits))) { |
| secPolicy.setForgedTransmits(allow); |
| } else { |
| secPolicy.setForgedTransmits(deny); |
| } |
| } |
| if (nicDetails.containsKey(NetworkOffering.Detail.MacAddressChanges)) { |
| if (Boolean.parseBoolean(nicDetails.get(NetworkOffering.Detail.MacAddressChanges))) { |
| secPolicy.setMacChanges(allow); |
| } else { |
| secPolicy.setMacChanges(deny); |
| } |
| } |
| return secPolicy; |
| } |
| |
| public static DVSMacManagementPolicy createDVSMacManagementPolicy(Map<NetworkOffering.Detail, String> nicDetails) { |
| if (nicDetails == null) { |
| nicDetails = getDefaultSecurityDetails(); |
| } |
| DVSMacManagementPolicy macManagementPolicy = new DVSMacManagementPolicy(); |
| macManagementPolicy.setAllowPromiscuous(Boolean.valueOf(nicDetails.getOrDefault(NetworkOffering.Detail.PromiscuousMode, "false"))); |
| macManagementPolicy.setForgedTransmits(Boolean.valueOf(nicDetails.getOrDefault(NetworkOffering.Detail.ForgedTransmits, "false"))); |
| macManagementPolicy.setMacChanges(Boolean.valueOf(nicDetails.getOrDefault(NetworkOffering.Detail.MacAddressChanges, "false"))); |
| DVSMacLearningPolicy macLearningPolicy = new DVSMacLearningPolicy(); |
| macLearningPolicy.setEnabled(Boolean.parseBoolean(nicDetails.getOrDefault(NetworkOffering.Detail.MacLearning, "false"))); |
| macManagementPolicy.setMacLearningPolicy(macLearningPolicy); |
| return macManagementPolicy; |
| } |
| |
| public static HostNetworkSecurityPolicy createVSSecurityPolicy(Map<NetworkOffering.Detail, String> nicDetails) { |
| HostNetworkSecurityPolicy secPolicy = new HostNetworkSecurityPolicy(); |
| secPolicy.setAllowPromiscuous(Boolean.FALSE); |
| secPolicy.setForgedTransmits(Boolean.TRUE); |
| secPolicy.setMacChanges(Boolean.TRUE); |
| |
| if (nicDetails == null) { |
| nicDetails = getDefaultSecurityDetails(); |
| } |
| |
| if (nicDetails.containsKey(NetworkOffering.Detail.PromiscuousMode)) { |
| secPolicy.setAllowPromiscuous(Boolean.valueOf(nicDetails.get(NetworkOffering.Detail.PromiscuousMode))); |
| } |
| |
| if (nicDetails.containsKey(NetworkOffering.Detail.ForgedTransmits)) { |
| secPolicy.setForgedTransmits(Boolean.valueOf(nicDetails.get(NetworkOffering.Detail.ForgedTransmits))); |
| } |
| |
| if (nicDetails.containsKey(NetworkOffering.Detail.MacAddressChanges)) { |
| secPolicy.setMacChanges(Boolean.valueOf(nicDetails.get(NetworkOffering.Detail.MacAddressChanges))); |
| } |
| |
| return secPolicy; |
| } |
| |
| public static Pair<ManagedObjectReference, String> prepareNetwork(String vSwitchName, String namePrefix, HostMO hostMo, String vlanId, Integer networkRateMbps, |
| Integer networkRateMulticastMbps, long timeOutMs, boolean syncPeerHosts, BroadcastDomainType broadcastDomainType, |
| String nicUuid, Map<NetworkOffering.Detail, String> nicDetails) throws Exception { |
| |
| HostVirtualSwitch vSwitch; |
| if (vSwitchName == null) { |
| LOGGER.info("Detected vswitch name as undefined. Defaulting to vSwitch0"); |
| vSwitchName = "vSwitch0"; |
| } |
| vSwitch = hostMo.getHostVirtualSwitchByName(vSwitchName); |
| |
| if (vSwitch == null) { |
| String msg = "Unable to find vSwitch" + vSwitchName; |
| LOGGER.error(msg); |
| throw new Exception(msg); |
| } |
| |
| boolean createGCTag = false; |
| String networkName; |
| Integer vid = null; |
| |
| /** This is the list of BroadcastDomainTypes we can actually |
| * prepare networks for in this function. |
| */ |
| BroadcastDomainType[] supportedBroadcastTypes = |
| new BroadcastDomainType[] {BroadcastDomainType.Lswitch, BroadcastDomainType.LinkLocal, BroadcastDomainType.Native, BroadcastDomainType.Pvlan, |
| BroadcastDomainType.Storage, BroadcastDomainType.UnDecided, BroadcastDomainType.Vlan, BroadcastDomainType.NSX}; |
| |
| if (!Arrays.asList(supportedBroadcastTypes).contains(broadcastDomainType)) { |
| throw new InvalidParameterException("BroadcastDomainType " + broadcastDomainType + " it not supported on a VMWare hypervisor at this time."); |
| } |
| |
| if (broadcastDomainType == BroadcastDomainType.Lswitch) { |
| /** |
| * Nicira NVP requires each vm to have its own port-group with a dedicated |
| * vlan. We'll set the name of the pg to the uuid of the nic. |
| */ |
| networkName = nicUuid; |
| // No doubt about this, depending on vid=null to avoid lots of code below |
| vid = null; |
| } else { |
| networkName = composeCloudNetworkName(namePrefix, vlanId, null, networkRateMbps, vSwitchName); |
| |
| if (vlanId != null && !UNTAGGED_VLAN_NAME.equalsIgnoreCase(vlanId)) { |
| createGCTag = true; |
| vid = Integer.parseInt(vlanId); |
| } |
| } |
| |
| HostNetworkSecurityPolicy secPolicy = createVSSecurityPolicy(nicDetails); |
| |
| HostNetworkTrafficShapingPolicy shapingPolicy = null; |
| if (networkRateMbps != null && networkRateMbps.intValue() > 0) { |
| shapingPolicy = new HostNetworkTrafficShapingPolicy(); |
| shapingPolicy.setEnabled(true); |
| shapingPolicy.setAverageBandwidth(networkRateMbps.intValue() * 1024L * 1024L); |
| |
| // |
| // TODO : people may have different opinion on how to set the following |
| // |
| |
| // give 50% premium to peek |
| shapingPolicy.setPeakBandwidth((long)(shapingPolicy.getAverageBandwidth() * 1.5)); |
| |
| // allow 5 seconds of burst transfer |
| shapingPolicy.setBurstSize(5 * shapingPolicy.getAverageBandwidth() / 8); |
| } |
| |
| boolean bWaitPortGroupReady = false; |
| if (broadcastDomainType == BroadcastDomainType.Lswitch) { |
| //if NSX API VERSION >= 4.2, connect to br-int (nsx.network), do not create portgroup else previous behaviour |
| if (NiciraNvpApiVersion.isApiVersionLowerThan("4.2")){ |
| //Previous behaviour |
| if (!hostMo.hasPortGroup(vSwitch, networkName)) { |
| createNvpPortGroup(hostMo, vSwitch, networkName, shapingPolicy); |
| |
| bWaitPortGroupReady = true; |
| } else { |
| bWaitPortGroupReady = false; |
| } |
| } |
| } else { |
| if (!hostMo.hasPortGroup(vSwitch, networkName)) { |
| hostMo.createPortGroup(vSwitch, networkName, vid, secPolicy, shapingPolicy, timeOutMs); |
| // Setting flag "bWaitPortGroupReady" to false. |
| // This flag indicates whether we need to wait for portgroup on vCenter. |
| // Above createPortGroup() method itself ensures creation of portgroup as well as wait for portgroup. |
| bWaitPortGroupReady = false; |
| } else { |
| HostPortGroupSpec spec = hostMo.getPortGroupSpec(networkName); |
| if (!isSpecMatch(spec, vid, secPolicy, shapingPolicy)) { |
| hostMo.updatePortGroup(vSwitch, networkName, vid, secPolicy, shapingPolicy); |
| bWaitPortGroupReady = true; |
| } |
| } |
| } |
| |
| ManagedObjectReference morNetwork = null; |
| |
| if (broadcastDomainType != BroadcastDomainType.Lswitch || |
| (broadcastDomainType == BroadcastDomainType.Lswitch && NiciraNvpApiVersion.isApiVersionLowerThan("4.2"))) { |
| if (bWaitPortGroupReady) |
| morNetwork = waitForNetworkReady(hostMo, networkName, timeOutMs); |
| else |
| morNetwork = hostMo.getNetworkMor(networkName); |
| if (morNetwork == null) { |
| String msg = "Failed to create guest network " + networkName; |
| LOGGER.error(msg); |
| throw new Exception(msg); |
| } |
| |
| if (createGCTag) { |
| NetworkMO networkMo = new NetworkMO(hostMo.getContext(), morNetwork); |
| networkMo.setCustomFieldValue(CustomFieldConstants.CLOUD_GC, "true"); |
| } |
| } |
| |
| if (syncPeerHosts) { |
| ManagedObjectReference morParent = hostMo.getParentMor(); |
| if (morParent != null && morParent.getType().equals("ClusterComputeResource")) { |
| // to be conservative, lock cluster |
| GlobalLock lock = GlobalLock.getInternLock("ClusterLock." + morParent.getValue()); |
| try { |
| if (lock.lock(DEFAULT_LOCK_TIMEOUT_SECONDS)) { |
| try { |
| List<ManagedObjectReference> hosts = hostMo.getContext().getVimClient().getDynamicProperty(morParent, "host"); |
| if (hosts != null) { |
| for (ManagedObjectReference otherHost : hosts) { |
| if (!otherHost.getValue().equals(hostMo.getMor().getValue())) { |
| HostMO otherHostMo = new HostMO(hostMo.getContext(), otherHost); |
| try { |
| if (LOGGER.isDebugEnabled()) |
| LOGGER.debug("Prepare network on other host, vlan: " + vlanId + ", host: " + otherHostMo.getHostName()); |
| prepareNetwork(vSwitchName, namePrefix, otherHostMo, vlanId, networkRateMbps, networkRateMulticastMbps, timeOutMs, false, |
| broadcastDomainType, nicUuid, nicDetails); |
| } catch (Exception e) { |
| LOGGER.warn("Unable to prepare network on other host, vlan: " + vlanId + ", host: " + otherHostMo.getHostName()); |
| } |
| } |
| } |
| } |
| } finally { |
| lock.unlock(); |
| } |
| } else { |
| LOGGER.warn("Unable to lock cluster to prepare guest network, vlan: " + vlanId); |
| } |
| } finally { |
| lock.releaseRef(); |
| } |
| } |
| } |
| |
| LOGGER.info("Network " + networkName + " is ready on vSwitch " + vSwitchName); |
| return new Pair<ManagedObjectReference, String>(morNetwork, networkName); |
| } |
| |
| private static boolean isSpecMatch(HostPortGroupSpec spec, Integer vlanId, HostNetworkSecurityPolicy securityPolicy, HostNetworkTrafficShapingPolicy shapingPolicy) { |
| // check VLAN configuration |
| if (vlanId != null) { |
| if (vlanId.intValue() != spec.getVlanId()) |
| return false; |
| } else { |
| if (spec.getVlanId() != 0) |
| return false; |
| } |
| |
| // check security policy for the portgroup |
| HostNetworkSecurityPolicy secPolicyInSpec = null; |
| if (spec.getPolicy() != null) { |
| secPolicyInSpec = spec.getPolicy().getSecurity(); |
| } |
| |
| if ((secPolicyInSpec != null && securityPolicy == null) || (secPolicyInSpec == null && securityPolicy != null)) { |
| return false; |
| } |
| |
| if (secPolicyInSpec != null |
| && ((securityPolicy.isAllowPromiscuous() != null && !securityPolicy.isAllowPromiscuous().equals(secPolicyInSpec.isAllowPromiscuous())) |
| || (securityPolicy.isForgedTransmits() != null && !securityPolicy.isForgedTransmits().equals(secPolicyInSpec.isForgedTransmits())) |
| || (securityPolicy.isMacChanges() != null && !securityPolicy.isMacChanges().equals(secPolicyInSpec.isMacChanges())))) { |
| return false; |
| } |
| |
| // check traffic shaping configuration |
| HostNetworkTrafficShapingPolicy policyInSpec = null; |
| if (spec.getPolicy() != null) { |
| policyInSpec = spec.getPolicy().getShapingPolicy(); |
| } |
| |
| if ((policyInSpec != null && shapingPolicy == null) || (policyInSpec == null && shapingPolicy != null)) { |
| return false; |
| } |
| |
| if (policyInSpec == null && shapingPolicy == null) { |
| return true; |
| } |
| |
| // so far policyInSpec and shapingPolicy should both not be null |
| if (policyInSpec.isEnabled() == null || !policyInSpec.isEnabled().booleanValue()) |
| return false; |
| |
| if (policyInSpec.getAverageBandwidth() == null || policyInSpec.getAverageBandwidth().longValue() != shapingPolicy.getAverageBandwidth().longValue()) |
| return false; |
| |
| if (policyInSpec.getPeakBandwidth() == null || policyInSpec.getPeakBandwidth().longValue() != shapingPolicy.getPeakBandwidth().longValue()) |
| return false; |
| |
| if (policyInSpec.getBurstSize() == null || policyInSpec.getBurstSize().longValue() != shapingPolicy.getBurstSize().longValue()) |
| return false; |
| |
| return true; |
| } |
| |
| private static void createNvpPortGroup(HostMO hostMo, HostVirtualSwitch vSwitch, String networkName, HostNetworkTrafficShapingPolicy shapingPolicy) throws Exception { |
| /** |
| * No portgroup created yet for this nic |
| * We need to find an unused vlan and create the pg |
| * The vlan is limited to this vSwitch and the NVP vAPP, |
| * so no relation to the other vlans in use in CloudStack. |
| */ |
| String vSwitchName = vSwitch.getName(); |
| |
| // Find all vlanids that we have in use |
| List<Integer> usedVlans = new ArrayList<Integer>(); |
| for (HostPortGroup pg : hostMo.getHostNetworkInfo().getPortgroup()) { |
| HostPortGroupSpec hpgs = pg.getSpec(); |
| if (vSwitchName.equals(hpgs.getVswitchName())) |
| usedVlans.add(hpgs.getVlanId()); |
| } |
| |
| // Find the first free vlanid |
| int nvpVlanId = 0; |
| for (nvpVlanId = 1; nvpVlanId < 4095; nvpVlanId++) { |
| if (!usedVlans.contains(nvpVlanId)) { |
| break; |
| } |
| } |
| if (nvpVlanId == 4095) { |
| throw new InvalidParameterException("No free vlan numbers on " + vSwitchName + " to create a portgroup for nic " + networkName); |
| } |
| |
| // Strict security policy |
| HostNetworkSecurityPolicy secPolicy = new HostNetworkSecurityPolicy(); |
| secPolicy.setAllowPromiscuous(Boolean.FALSE); |
| secPolicy.setForgedTransmits(Boolean.FALSE); |
| secPolicy.setMacChanges(Boolean.FALSE); |
| |
| // Create a portgroup with the uuid of the nic and the vlanid found above |
| hostMo.createPortGroup(vSwitch, networkName, nvpVlanId, secPolicy, shapingPolicy); |
| } |
| |
| public static ManagedObjectReference waitForNetworkReady(HostMO hostMo, String networkName, long timeOutMs) throws Exception { |
| |
| ManagedObjectReference morNetwork = null; |
| |
| // if portGroup is just created, getNetwork may fail to retrieve it, we |
| // need to retry |
| long startTick = System.currentTimeMillis(); |
| while (System.currentTimeMillis() - startTick <= timeOutMs) { |
| morNetwork = hostMo.getNetworkMor(networkName); |
| if (morNetwork != null) { |
| break; |
| } |
| |
| LOGGER.info("Waiting for network " + networkName + " to be ready"); |
| Thread.sleep(1000); |
| } |
| |
| return morNetwork; |
| } |
| |
| 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 { |
| |
| 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); |
| if (vmInternalCSName == null) |
| vmInternalCSName = vmName; |
| |
| VmwareHelper.setBasicVmConfig(vmConfig, cpuCount, cpuSpeedMHz, cpuReservedMHz, memoryMB, memoryReserveMB, guestOsIdentifier, limitCpuUse, false); |
| |
| String newRootDiskController = controllerInfo.first(); |
| String newDataDiskController = controllerInfo.second(); |
| String recommendedController = null; |
| if (VmwareHelper.isControllerOsRecommended(newRootDiskController) || VmwareHelper.isControllerOsRecommended(newDataDiskController)) { |
| recommendedController = host.getRecommendedDiskController(guestOsIdentifier); |
| } |
| |
| Pair<String, String> updatedControllerInfo = new Pair<String, String>(newRootDiskController, newDataDiskController); |
| String scsiDiskController = HypervisorHostHelper.getScsiController(updatedControllerInfo, recommendedController); |
| // If there is requirement for a SCSI controller, ensure to create those. |
| if (scsiDiskController != null) { |
| int busNum = 0; |
| int maxControllerCount = VmwareHelper.MAX_SCSI_CONTROLLER_COUNT; |
| if (systemVm) { |
| maxControllerCount = 1; |
| } |
| while (busNum < maxControllerCount) { |
| VirtualDeviceConfigSpec scsiControllerSpec = new VirtualDeviceConfigSpec(); |
| scsiControllerSpec = getControllerSpec(DiskControllerType.getType(scsiDiskController).toString(), busNum); |
| |
| vmConfig.getDeviceChange().add(scsiControllerSpec); |
| busNum++; |
| } |
| } |
| |
| if (guestOsIdentifier.startsWith("darwin")) { //Mac OS |
| LOGGER.debug("Add USB Controller device for blank Mac OS VM " + vmName); |
| |
| //For Mac OS X systems, the EHCI+UHCI controller is enabled by default and is required for USB mouse and keyboard access. |
| VirtualDevice usbControllerDevice = VmwareHelper.prepareUSBControllerDevice(); |
| VirtualDeviceConfigSpec usbControllerSpec = new VirtualDeviceConfigSpec(); |
| usbControllerSpec.setDevice(usbControllerDevice); |
| usbControllerSpec.setOperation(VirtualDeviceConfigSpecOperation.ADD); |
| |
| vmConfig.getDeviceChange().add(usbControllerSpec); |
| } |
| |
| VirtualMachineFileInfo fileInfo = new VirtualMachineFileInfo(); |
| DatastoreMO dsMo = new DatastoreMO(host.getContext(), morDs); |
| fileInfo.setVmPathName(String.format("[%s]", dsMo.getName())); |
| vmConfig.setFiles(fileInfo); |
| |
| VirtualMachineVideoCard videoCard = new VirtualMachineVideoCard(); |
| videoCard.setControllerKey(100); |
| videoCard.setUseAutoDetect(true); |
| |
| VirtualDeviceConfigSpec videoDeviceSpec = new VirtualDeviceConfigSpec(); |
| videoDeviceSpec.setDevice(videoCard); |
| videoDeviceSpec.setOperation(VirtualDeviceConfigSpecOperation.ADD); |
| |
| vmConfig.getDeviceChange().add(videoDeviceSpec); |
| |
| ClusterMO clusterMo = new ClusterMO(host.getContext(), host.getHyperHostCluster()); |
| DatacenterMO dataCenterMo = new DatacenterMO(host.getContext(), host.getHyperHostDatacenter()); |
| setVMHardwareVersion(vmConfig, clusterMo, dataCenterMo); |
| |
| LOGGER.debug(LogUtils.logGsonWithoutException("Creating blank VM with configuration [%s].", vmConfig)); |
| if (host.createVm(vmConfig)) { |
| // Here, when attempting to find the VM, we need to use the name |
| // with which we created it. This is the only such place where |
| // we need to do this. At all other places, we always use the |
| // VM's internal cloudstack generated name. Here, we cannot use |
| // the internal name because we can set the internal name into the |
| // VM's custom field CLOUD_VM_INTERNAL_NAME only after we create |
| // the VM. |
| VirtualMachineMO vmMo = host.findVmOnHyperHost(vmName); |
| assert (vmMo != null); |
| |
| vmMo.setCustomFieldValue(CustomFieldConstants.CLOUD_VM_INTERNAL_NAME, vmInternalCSName); |
| |
| int ideControllerKey = -1; |
| while (ideControllerKey < 0) { |
| ideControllerKey = vmMo.tryGetIDEDeviceControllerKey(); |
| if (ideControllerKey >= 0) |
| break; |
| |
| LOGGER.info("Waiting for IDE controller be ready in VM: " + vmInternalCSName); |
| Thread.sleep(1000); |
| } |
| |
| return true; |
| } |
| return false; |
| } |
| |
| /** |
| * Set the VM hardware version based on the information retrieved by the cluster and datacenter: |
| * - If the cluster hardware version is set, then it is set to this hardware version on vmConfig |
| * - If the cluster hardware version is not set, check datacenter hardware version. If it is set, then it is set to vmConfig |
| * - In case both cluster and datacenter hardware version are not set, hardware version is not set to vmConfig |
| */ |
| public static void setVMHardwareVersion(VirtualMachineConfigSpec vmConfig, ClusterMO clusterMO, DatacenterMO datacenterMO) throws Exception { |
| String version = getNewVMHardwareVersion(clusterMO, datacenterMO); |
| if (StringUtils.isNotBlank(version)) { |
| vmConfig.setVersion(version); |
| } |
| } |
| |
| /** |
| * Return the VM hardware version based on the information retrieved by the cluster and datacenter: |
| * - If the cluster hardware version is set, then return this hardware version |
| * - If the cluster hardware version is not set, check datacenter hardware version. If it is set, then return it |
| * - In case both cluster and datacenter hardware version are not set, return null |
| */ |
| public static String getNewVMHardwareVersion(ClusterMO clusterMO, DatacenterMO datacenterMO) throws Exception { |
| String version = null; |
| ClusterConfigInfoEx clusterConfigInfo = clusterMO != null ? clusterMO.getClusterConfigInfo() : null; |
| String clusterHardwareVersion = clusterConfigInfo != null ? clusterConfigInfo.getDefaultHardwareVersionKey() : null; |
| if (StringUtils.isNotBlank(clusterHardwareVersion)) { |
| LOGGER.debug("Cluster hardware version found: " + clusterHardwareVersion + ". Creating VM with this hardware version"); |
| version = clusterHardwareVersion; |
| } else { |
| DatacenterConfigInfo datacenterConfigInfo = datacenterMO != null ? datacenterMO.getDatacenterConfigInfo() : null; |
| String datacenterHardwareVersion = datacenterConfigInfo != null ? datacenterConfigInfo.getDefaultHardwareVersionKey() : null; |
| if (StringUtils.isNotBlank(datacenterHardwareVersion)) { |
| LOGGER.debug("Datacenter hardware version found: " + datacenterHardwareVersion + ". Creating VM with this hardware version"); |
| version = datacenterHardwareVersion; |
| } |
| } |
| 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 |
| // create it successfully |
| ManagedObjectReference morCluster = hyperHost.getHyperHostCluster(); |
| if (morCluster != null) |
| hyperHost = new ClusterMO(hyperHost.getContext(), morCluster); |
| |
| if (dsMo.getDatastoreType().equalsIgnoreCase("VVOL") && !vmName.startsWith(CustomFieldConstants.CLOUD_UUID)) { |
| vmName = CustomFieldConstants.CLOUD_UUID + "-" + vmName; |
| } |
| VirtualMachineMO workingVM = null; |
| VirtualMachineConfigSpec vmConfig = new VirtualMachineConfigSpec(); |
| vmConfig.setName(vmName); |
| if (StringUtils.isNotBlank(vmxFormattedHardwareVersion)){ |
| vmConfig.setVersion(vmxFormattedHardwareVersion); |
| } else { |
| ClusterMO clusterMo = new ClusterMO(hyperHost.getContext(), hyperHost.getHyperHostCluster()); |
| DatacenterMO dataCenterMo = new DatacenterMO(hyperHost.getContext(), hyperHost.getHyperHostDatacenter()); |
| setVMHardwareVersion(vmConfig, clusterMo, dataCenterMo); |
| } |
| vmConfig.setMemoryMB((long)4); |
| vmConfig.setNumCPUs(1); |
| vmConfig.setGuestId(VirtualMachineGuestOsIdentifier.OTHER_GUEST.value()); |
| VirtualMachineFileInfo fileInfo = new VirtualMachineFileInfo(); |
| fileInfo.setVmPathName(dsMo.getDatastoreRootPath()); |
| vmConfig.setFiles(fileInfo); |
| |
| VirtualLsiLogicController scsiController = new VirtualLsiLogicController(); |
| scsiController.setSharedBus(VirtualSCSISharing.NO_SHARING); |
| scsiController.setBusNumber(0); |
| scsiController.setKey(1); |
| VirtualDeviceConfigSpec scsiControllerSpec = new VirtualDeviceConfigSpec(); |
| scsiControllerSpec.setDevice(scsiController); |
| scsiControllerSpec.setOperation(VirtualDeviceConfigSpecOperation.ADD); |
| |
| vmConfig.getDeviceChange().add(scsiControllerSpec); |
| if (hyperHost.createVm(vmConfig)) { |
| // Ugly work-around, it takes time for newly created VM to appear |
| for (int i = 0; i < 10 && workingVM == null; i++) { |
| workingVM = hyperHost.findVmOnHyperHost(vmName); |
| |
| try { |
| Thread.sleep(1000); |
| } catch (InterruptedException e) { |
| LOGGER.debug("[ignored] interrupted while waiting to config vm."); |
| } |
| } |
| } |
| |
| if (workingVM != null) { |
| workingVM.tagAsWorkerVM(); |
| } |
| return workingVM; |
| } |
| |
| public static String resolveHostNameInUrl(DatacenterMO dcMo, String url) { |
| LOGGER.info("Resolving host name in url through vCenter, url: " + url); |
| |
| URI uri; |
| try { |
| uri = new URI(url); |
| } catch (URISyntaxException e) { |
| LOGGER.warn("URISyntaxException on url " + url); |
| return url; |
| } |
| |
| String host = uri.getHost(); |
| if (NetUtils.isValidIp4(host)) { |
| LOGGER.info("host name in url is already in IP address, url: " + url); |
| return url; |
| } |
| |
| try { |
| ManagedObjectReference morHost = dcMo.findHost(host); |
| if (morHost != null) { |
| HostMO hostMo = new HostMO(dcMo.getContext(), morHost); |
| String managementPortGroupName; |
| if (hostMo.getHostType() == VmwareHostType.ESXi) |
| managementPortGroupName = (String)dcMo.getContext().getStockObject("manageportgroup"); |
| else |
| managementPortGroupName = (String)dcMo.getContext().getStockObject("serviceconsole"); |
| |
| VmwareHypervisorHostNetworkSummary summary = hostMo.getHyperHostNetworkSummary(managementPortGroupName); |
| if (summary == null) { |
| LOGGER.warn("Unable to resolve host name in url through vSphere, url: " + url); |
| return url; |
| } |
| |
| String hostIp = summary.getHostIp(); |
| |
| try { |
| URI resolvedUri = new URI(uri.getScheme(), uri.getUserInfo(), hostIp, uri.getPort(), uri.getPath(), uri.getQuery(), uri.getFragment()); |
| |
| LOGGER.info("url " + url + " is resolved to " + resolvedUri.toString() + " through vCenter"); |
| return resolvedUri.toString(); |
| } catch (URISyntaxException e) { |
| assert (false); |
| return url; |
| } |
| } |
| } catch (Exception e) { |
| LOGGER.warn("Unexpected exception ", e); |
| } |
| |
| return url; |
| } |
| |
| /** |
| * removes the NetworkSection element from the {ovfString} if it is an ovf xml file |
| * @param ovfString input string |
| * @return like the input string but if xml elements by name {NetworkSection} removed |
| */ |
| public static String removeOVFNetwork(final String ovfString) { |
| if (ovfString == null || ovfString.isEmpty()) { |
| return ovfString; |
| } |
| try { |
| final DocumentBuilderFactory factory = ParserUtils.getSaferDocumentBuilderFactory(); |
| final Document doc = factory.newDocumentBuilder().parse(new ByteArrayInputStream(ovfString.getBytes())); |
| final DocumentTraversal traversal = (DocumentTraversal) doc; |
| final NodeIterator iterator = traversal.createNodeIterator(doc.getDocumentElement(), NodeFilter.SHOW_ELEMENT, null, true); |
| for (Node n = iterator.nextNode(); n != null; n = iterator.nextNode()) { |
| final Element e = (Element) n; |
| if ("NetworkSection".equals(e.getTagName())) { |
| if (e.getParentNode() != null) { |
| e.getParentNode().removeChild(e); |
| } |
| } else if ("rasd:Connection".equals(e.getTagName())) { |
| if (e.getParentNode() != null && e.getParentNode().getParentNode() != null) { |
| e.getParentNode().getParentNode().removeChild(e.getParentNode()); |
| } |
| } |
| } |
| final DOMSource domSource = new DOMSource(doc); |
| final StringWriter writer = new StringWriter(); |
| final StreamResult result = new StreamResult(writer); |
| final TransformerFactory tf = ParserUtils.getSaferTransformerFactory(); |
| final Transformer transformer = tf.newTransformer(); |
| transformer.transform(domSource, result); |
| return writer.toString(); |
| } catch (SAXException | IOException | ParserConfigurationException | TransformerException e) { |
| LOGGER.warn("Unexpected exception caught while removing network elements from OVF:", e); |
| } |
| return ovfString; |
| } |
| |
| /** |
| * deploys a new VM from a ovf spec. It ignores network, defaults locale to 'US' |
| * @throws Exception should be a VmwareResourceException |
| */ |
| public static void importVmFromOVF(VmwareHypervisorHost host, String ovfFilePath, String vmName, DatastoreMO dsMo, String diskOption, ManagedObjectReference morRp, |
| ManagedObjectReference morHost, String configurationId) throws CloudRuntimeException, IOException { |
| |
| assert (morRp != null); |
| |
| OvfCreateImportSpecParams importSpecParams = new OvfCreateImportSpecParams(); |
| importSpecParams.setHostSystem(morHost); |
| importSpecParams.setLocale("US"); |
| importSpecParams.setEntityName(vmName); |
| String deploymentOption = StringUtils.isNotBlank(configurationId) ? configurationId : ""; |
| importSpecParams.setDeploymentOption(deploymentOption); |
| importSpecParams.setDiskProvisioning(diskOption); // diskOption: thin, thick, etc |
| |
| String ovfDescriptor = removeOVFNetwork(HttpNfcLeaseMO.readOvfContent(ovfFilePath)); |
| VmwareContext context = host.getContext(); |
| OvfCreateImportSpecResult ovfImportResult = null; |
| try { |
| ovfImportResult = context.getService().createImportSpec(context.getServiceContent().getOvfManager(), ovfDescriptor, morRp, dsMo.getMor(), importSpecParams); |
| } catch (ConcurrentAccessFaultMsg |
| | FileFaultFaultMsg |
| | InvalidDatastoreFaultMsg |
| | InvalidStateFaultMsg |
| | RuntimeFaultFaultMsg |
| | TaskInProgressFaultMsg |
| | VmConfigFaultFaultMsg error) { |
| throw new CloudRuntimeException("ImportSpec creation failed", error); |
| } |
| if (ovfImportResult == null) { |
| String msg = "createImportSpec() failed. ovfFilePath: " + ovfFilePath + ", vmName: " + vmName + ", diskOption: " + diskOption; |
| LOGGER.error(msg); |
| throw new CloudRuntimeException(msg); |
| } |
| if(!ovfImportResult.getError().isEmpty()) { |
| for (LocalizedMethodFault fault : ovfImportResult.getError()) { |
| LOGGER.error("createImportSpec error: " + fault.getLocalizedMessage()); |
| } |
| throw new CloudRuntimeException("Failed to create an import spec from " + ovfFilePath + ". Check log for details."); |
| } |
| |
| if (!ovfImportResult.getWarning().isEmpty()) { |
| for (LocalizedMethodFault fault : ovfImportResult.getError()) { |
| LOGGER.warn("createImportSpec warning: " + fault.getLocalizedMessage()); |
| } |
| } |
| |
| DatacenterMO dcMo = null; |
| try { |
| dcMo = new DatacenterMO(context, host.getHyperHostDatacenter()); |
| } catch (Exception e) { |
| throw new CloudRuntimeException(String.format("no datacenter for host '%s' available in context", context.getServerAddress()), e); |
| } |
| ManagedObjectReference folderMO = null; |
| try { |
| folderMO = dcMo.getVmFolder(); |
| } catch (Exception e) { |
| throw new CloudRuntimeException("no management handle for VmFolder", e); |
| } |
| ManagedObjectReference morLease = null; |
| try { |
| morLease = context.getService().importVApp(morRp, ovfImportResult.getImportSpec(), folderMO, morHost); |
| } catch (DuplicateNameFaultMsg |
| | FileFaultFaultMsg |
| | InsufficientResourcesFaultFaultMsg |
| | InvalidDatastoreFaultMsg |
| | InvalidNameFaultMsg |
| | OutOfBoundsFaultMsg |
| | RuntimeFaultFaultMsg |
| | VmConfigFaultFaultMsg fault) { |
| throw new CloudRuntimeException("import vApp failed",fault); |
| } |
| if (morLease == null) { |
| String msg = "importVApp() failed. ovfFilePath: " + ovfFilePath + ", vmName: " + vmName + ", diskOption: " + diskOption; |
| LOGGER.error(msg); |
| throw new CloudRuntimeException(msg); |
| } |
| boolean importSuccess = true; |
| final HttpNfcLeaseMO leaseMo = new HttpNfcLeaseMO(context, morLease); |
| HttpNfcLeaseState state = null; |
| try { |
| state = leaseMo.waitState(new HttpNfcLeaseState[] {HttpNfcLeaseState.READY, HttpNfcLeaseState.ERROR}); |
| } catch (Exception e) { |
| throw new CloudRuntimeException("exception while waiting for leaseMO", e); |
| } |
| try { |
| if (state == HttpNfcLeaseState.READY) { |
| final long totalBytes = HttpNfcLeaseMO.calcTotalBytes(ovfImportResult); |
| File ovfFile = new File(ovfFilePath); |
| |
| HttpNfcLeaseInfo httpNfcLeaseInfo = null; |
| try { |
| httpNfcLeaseInfo = leaseMo.getLeaseInfo(); |
| } catch (Exception e) { |
| throw new CloudRuntimeException("error waiting for lease info", e); |
| } |
| List<HttpNfcLeaseDeviceUrl> deviceUrls = httpNfcLeaseInfo.getDeviceUrl(); |
| long bytesAlreadyWritten = 0; |
| |
| final HttpNfcLeaseMO.ProgressReporter progressReporter = leaseMo.createProgressReporter(); |
| try { |
| for (HttpNfcLeaseDeviceUrl deviceUrl : deviceUrls) { |
| String deviceKey = deviceUrl.getImportKey(); |
| for (OvfFileItem ovfFileItem : ovfImportResult.getFileItem()) { |
| if (deviceKey.equals(ovfFileItem.getDeviceId())) { |
| String absoluteFile = ovfFile.getParent() + File.separator + ovfFileItem.getPath(); |
| LOGGER.info("Uploading file: " + absoluteFile); |
| File f = new File(absoluteFile); |
| if (f.exists()){ |
| String urlToPost = deviceUrl.getUrl(); |
| urlToPost = resolveHostNameInUrl(dcMo, urlToPost); |
| context.uploadVmdkFile(ovfFileItem.isCreate() ? "PUT" : "POST", urlToPost, absoluteFile, bytesAlreadyWritten, new ActionDelegate<Long>() { |
| @Override |
| public void action(Long param) { |
| progressReporter.reportProgress((int)(param * 100 / totalBytes)); |
| } |
| }); |
| bytesAlreadyWritten += ovfFileItem.getSize(); |
| } |
| } |
| } |
| } |
| } catch (Exception e) { |
| String erroMsg = "File upload task failed to complete due to: " + e.getMessage(); |
| LOGGER.error(erroMsg); |
| importSuccess = false; // Set flag to cleanup the stale template left due to failed import operation, if any |
| throw new CloudRuntimeException(erroMsg, e); |
| } catch (Throwable th) { |
| String errorMsg = "throwable caught during file upload task: " + th.getMessage(); |
| LOGGER.error(errorMsg); |
| importSuccess = false; // Set flag to cleanup the stale template left due to failed import operation, if any |
| throw new CloudRuntimeException(errorMsg, th); |
| } finally { |
| progressReporter.close(); |
| } |
| if (bytesAlreadyWritten == totalBytes) { |
| try { |
| leaseMo.updateLeaseProgress(100); |
| } catch (Exception e) { |
| throw new CloudRuntimeException("error while waiting for lease update", e); |
| } |
| } |
| } else if (state == HttpNfcLeaseState.ERROR) { |
| LocalizedMethodFault error = null; |
| try { |
| error = leaseMo.getLeaseError(); |
| } catch (Exception e) { |
| throw new CloudRuntimeException("error getting lease error", e); |
| } |
| MethodFault fault = error.getFault(); |
| String erroMsg = "Object creation on vCenter failed due to: Exception: " + fault.getClass().getName() + ", message: " + error.getLocalizedMessage(); |
| LOGGER.error(erroMsg); |
| throw new CloudRuntimeException(erroMsg); |
| } |
| } finally { |
| try { |
| if (!importSuccess) { |
| LOGGER.error("Aborting the lease on " + vmName + " after import operation failed."); |
| leaseMo.abortLease(); |
| } else { |
| leaseMo.completeLease(); |
| } |
| } catch (Exception e) { |
| throw new CloudRuntimeException("error completing lease", e); |
| } |
| } |
| } |
| |
| public static List<Pair<String, Boolean>> readOVF(VmwareHypervisorHost host, String ovfFilePath, DatastoreMO dsMo) throws Exception { |
| List<Pair<String, Boolean>> ovfVolumeInfos = new ArrayList<Pair<String, Boolean>>(); |
| List<String> files = new ArrayList<String>(); |
| |
| ManagedObjectReference morRp = host.getHyperHostOwnerResourcePool(); |
| assert (morRp != null); |
| ManagedObjectReference morHost = host.getMor(); |
| String importEntityName = UUID.randomUUID().toString(); |
| OvfCreateImportSpecParams importSpecParams = new OvfCreateImportSpecParams(); |
| importSpecParams.setHostSystem(morHost); |
| importSpecParams.setLocale("US"); |
| importSpecParams.setEntityName(importEntityName); |
| importSpecParams.setDeploymentOption(""); |
| |
| String ovfDescriptor = removeOVFNetwork(HttpNfcLeaseMO.readOvfContent(ovfFilePath)); |
| VmwareContext context = host.getContext(); |
| OvfCreateImportSpecResult ovfImportResult = context.getService().createImportSpec(context.getServiceContent().getOvfManager(), ovfDescriptor, morRp, dsMo.getMor(), |
| importSpecParams); |
| |
| if (ovfImportResult == null) { |
| String msg = "createImportSpec() failed. ovfFilePath: " + ovfFilePath; |
| LOGGER.error(msg); |
| throw new Exception(msg); |
| } |
| |
| if (!ovfImportResult.getError().isEmpty()) { |
| for (LocalizedMethodFault fault : ovfImportResult.getError()) { |
| LOGGER.error("createImportSpec error: " + fault.getLocalizedMessage()); |
| } |
| throw new CloudException("Failed to create an import spec from " + ovfFilePath + ". Check log for details."); |
| } |
| |
| if (!ovfImportResult.getWarning().isEmpty()) { |
| for (LocalizedMethodFault fault : ovfImportResult.getError()) { |
| LOGGER.warn("createImportSpec warning: " + fault.getLocalizedMessage()); |
| } |
| } |
| |
| VirtualMachineImportSpec importSpec = (VirtualMachineImportSpec)ovfImportResult.getImportSpec(); |
| if (importSpec == null) { |
| String msg = "createImportSpec() failed to create import specification for OVF template at " + ovfFilePath; |
| LOGGER.error(msg); |
| throw new Exception(msg); |
| } |
| |
| File ovfFile = new File(ovfFilePath); |
| for (OvfFileItem ovfFileItem : ovfImportResult.getFileItem()) { |
| String absFile = ovfFile.getParent() + File.separator + ovfFileItem.getPath(); |
| files.add(absFile); |
| } |
| |
| |
| int osDiskSeqNumber = 0; |
| VirtualMachineConfigSpec config = importSpec.getConfigSpec(); |
| String paramVal = getOVFParamValue(config); |
| if (paramVal != null && !paramVal.isEmpty()) { |
| try { |
| osDiskSeqNumber = getOsDiskFromOvfConf(config, paramVal); |
| } catch (Exception e) { |
| osDiskSeqNumber = 0; |
| } |
| } |
| |
| int diskCount = 0; |
| int deviceCount = 0; |
| List<VirtualDeviceConfigSpec> deviceConfigList = config.getDeviceChange(); |
| for (VirtualDeviceConfigSpec deviceSpec : deviceConfigList) { |
| Boolean osDisk = false; |
| VirtualDevice device = deviceSpec.getDevice(); |
| if (device instanceof VirtualDisk) { |
| if ((osDiskSeqNumber == 0 && diskCount == 0) || osDiskSeqNumber == deviceCount) { |
| osDisk = true; |
| } |
| Pair<String, Boolean> ovfVolumeInfo = new Pair<String, Boolean>(files.get(diskCount), osDisk); |
| ovfVolumeInfos.add(ovfVolumeInfo); |
| diskCount++; |
| } |
| deviceCount++; |
| } |
| return ovfVolumeInfos; |
| } |
| |
| public static void createOvfFile(VmwareHypervisorHost host, String diskFileName, String ovfName, String datastorePath, String templatePath, long diskCapacity, long fileSize, |
| ManagedObjectReference morDs) throws Exception { |
| VmwareContext context = host.getContext(); |
| ManagedObjectReference morOvf = context.getServiceContent().getOvfManager(); |
| VirtualMachineMO workerVmMo = HypervisorHostHelper.createWorkerVM(host, new DatastoreMO(context, morDs), ovfName, null); |
| if (workerVmMo == null) |
| throw new Exception("Unable to find just-created worker VM"); |
| |
| String[] disks = {datastorePath + File.separator + diskFileName}; |
| try { |
| VirtualMachineConfigSpec vmConfigSpec = new VirtualMachineConfigSpec(); |
| VirtualDeviceConfigSpec deviceConfigSpec = new VirtualDeviceConfigSpec(); |
| |
| // Reconfigure worker VM with datadisk |
| VirtualDevice device = VmwareHelper.prepareDiskDevice(workerVmMo, null, -1, disks, morDs, -1, 1, null); |
| deviceConfigSpec.setDevice(device); |
| deviceConfigSpec.setOperation(VirtualDeviceConfigSpecOperation.ADD); |
| vmConfigSpec.getDeviceChange().add(deviceConfigSpec); |
| workerVmMo.configureVm(vmConfigSpec); |
| |
| // Write OVF descriptor file |
| OvfCreateDescriptorParams ovfDescParams = new OvfCreateDescriptorParams(); |
| String deviceId = File.separator + workerVmMo.getMor().getValue() + File.separator + "VirtualIDEController0:0"; |
| OvfFile ovfFile = new OvfFile(); |
| ovfFile.setPath(diskFileName); |
| ovfFile.setDeviceId(deviceId); |
| ovfFile.setSize(fileSize); |
| ovfFile.setCapacity(diskCapacity); |
| ovfDescParams.getOvfFiles().add(ovfFile); |
| OvfCreateDescriptorResult ovfCreateDescriptorResult = context.getService().createDescriptor(morOvf, workerVmMo.getMor(), ovfDescParams); |
| |
| String ovfPath = templatePath + File.separator + ovfName + ".ovf"; |
| try { |
| FileWriter out = new FileWriter(ovfPath); |
| out.write(ovfCreateDescriptorResult.getOvfDescriptor()); |
| out.close(); |
| } catch (Exception e) { |
| throw e; |
| } |
| } finally { |
| workerVmMo.detachAllDisksAndDestroy(); |
| } |
| } |
| |
| public static int getOsDiskFromOvfConf(VirtualMachineConfigSpec config, String deviceLocation) { |
| List<VirtualDeviceConfigSpec> deviceConfigList = config.getDeviceChange(); |
| int controllerKey = 0; |
| int deviceSeqNumber = 0; |
| int controllerNumber = 0; |
| int deviceNodeNumber = 0; |
| int controllerCount = 0; |
| String[] virtualNodeInfo = deviceLocation.split(":"); |
| |
| if (deviceLocation.startsWith("scsi")) { |
| controllerNumber = Integer.parseInt(virtualNodeInfo[0].substring(4)); // get substring excluding prefix scsi |
| deviceNodeNumber = Integer.parseInt(virtualNodeInfo[1]); |
| |
| for (VirtualDeviceConfigSpec deviceConfig : deviceConfigList) { |
| VirtualDevice device = deviceConfig.getDevice(); |
| if (device instanceof VirtualSCSIController) { |
| if (controllerNumber == controllerCount) { //((VirtualSCSIController)device).getBusNumber()) { |
| controllerKey = device.getKey(); |
| break; |
| } |
| controllerCount++; |
| } |
| } |
| } else { |
| controllerNumber = Integer.parseInt(virtualNodeInfo[0].substring(3)); // get substring excluding prefix ide |
| deviceNodeNumber = Integer.parseInt(virtualNodeInfo[1]); |
| controllerCount = 0; |
| |
| for (VirtualDeviceConfigSpec deviceConfig : deviceConfigList) { |
| VirtualDevice device = deviceConfig.getDevice(); |
| if (device instanceof VirtualIDEController) { |
| if (controllerNumber == controllerCount) { //((VirtualIDEController)device).getBusNumber()) { |
| // Only 2 IDE controllers supported and they will have bus numbers 0 and 1 |
| controllerKey = device.getKey(); |
| break; |
| } |
| controllerCount++; |
| } |
| } |
| } |
| // Get devices on this controller at specific device node. |
| for (VirtualDeviceConfigSpec deviceConfig : deviceConfigList) { |
| VirtualDevice device = deviceConfig.getDevice(); |
| if (device instanceof VirtualDisk) { |
| if (controllerKey == device.getControllerKey() && deviceNodeNumber == device.getUnitNumber()) { |
| break; |
| } |
| deviceSeqNumber++; |
| } |
| } |
| return deviceSeqNumber; |
| } |
| |
| public static String getOVFParamValue(VirtualMachineConfigSpec config) { |
| String paramVal = ""; |
| List<OptionValue> options = config.getExtraConfig(); |
| for (OptionValue option : options) { |
| if (OVA_OPTION_KEY_BOOTDISK.equalsIgnoreCase(option.getKey())) { |
| paramVal = (String)option.getValue(); |
| break; |
| } |
| } |
| return paramVal; |
| } |
| |
| public static ManagedObjectReference getHypervisorHostMorFromGuid(String guid) { |
| if (guid == null) { |
| return null; |
| } |
| |
| String[] tokens = guid.split("@"); |
| if (tokens == null || tokens.length != 2) { |
| LOGGER.error("Invalid content in host guid"); |
| return null; |
| } |
| |
| String[] hostTokens = tokens[0].split(":"); |
| if (hostTokens == null || hostTokens.length != 2) { |
| LOGGER.error("Invalid content in host guid"); |
| return null; |
| } |
| |
| ManagedObjectReference morHyperHost = new ManagedObjectReference(); |
| morHyperHost.setType(hostTokens[0]); |
| morHyperHost.setValue(hostTokens[1]); |
| |
| return morHyperHost; |
| } |
| |
| public static String getScsiController(Pair<String, String> controllerInfo, String recommendedController) { |
| String rootDiskController = controllerInfo.first(); |
| String dataDiskController = controllerInfo.second(); |
| |
| // If "osdefault" is specified as controller type, then translate to actual recommended controller. |
| if (VmwareHelper.isControllerOsRecommended(rootDiskController)) { |
| rootDiskController = recommendedController; |
| } |
| if (VmwareHelper.isControllerOsRecommended(dataDiskController)) { |
| dataDiskController = recommendedController; |
| } |
| |
| String scsiDiskController = null; //If any of the controller provided is SCSI then return it's sub-type. |
| if (isIdeController(rootDiskController) && isIdeController(dataDiskController)) { |
| //Default controllers would exist |
| return null; |
| } else if (isIdeController(rootDiskController) || isIdeController(dataDiskController)) { |
| // Only one of the controller types is IDE. Pick the other controller type to create controller. |
| if (isIdeController(rootDiskController)) { |
| scsiDiskController = dataDiskController; |
| } else { |
| scsiDiskController = rootDiskController; |
| } |
| } else if (DiskControllerType.getType(rootDiskController) != DiskControllerType.getType(dataDiskController)) { |
| // Both ROOT and DATA controllers are SCSI controllers but different sub-types, then prefer ROOT controller |
| scsiDiskController = rootDiskController; |
| } else { |
| // Both are SCSI controllers. |
| scsiDiskController = rootDiskController; |
| } |
| return scsiDiskController; |
| } |
| |
| public static boolean isIdeController(String controller) { |
| return DiskControllerType.getType(controller) == DiskControllerType.ide; |
| } |
| |
| 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()); |
| List<ManagedObjectReference> datastoresInCluster = storagepodMO.getDatastoresInDatastoreCluster(); |
| for (ManagedObjectReference datastore : datastoresInCluster) { |
| DatastoreMO childDsMo = new DatastoreMO(hyperHost.getContext(), datastore); |
| createBaseFolderInDatastore(childDsMo, hyperHost.getHyperHostDatacenter()); |
| } |
| } else { |
| createBaseFolderInDatastore(dsMo, hyperHost.getHyperHostDatacenter()); |
| } |
| } |
| |
| public static void createBaseFolderInDatastore(DatastoreMO dsMo, ManagedObjectReference mor) throws Exception { |
| String dsPath = String.format("[%s]", dsMo.getName()); |
| String folderPath = String.format("[%s] %s", dsMo.getName(), VSPHERE_DATASTORE_BASE_FOLDER); |
| String hiddenFolderPath = String.format("%s/%s", folderPath, VSPHERE_DATASTORE_HIDDEN_FOLDER); |
| |
| if (!dsMo.folderExists(dsPath, VSPHERE_DATASTORE_BASE_FOLDER)) { |
| LOGGER.info(String.format("vSphere datastore base folder [%s] does not exist on datastore [%s]. We will create it.", VSPHERE_DATASTORE_BASE_FOLDER, dsMo.getName())); |
| dsMo.makeDirectory(folderPath, mor); |
| // Adding another directory so vCentre doesn't remove the fcd directory when it's empty |
| dsMo.makeDirectory(hiddenFolderPath, mor); |
| } |
| } |
| |
| public static Integer getHostHardwareVersion(VmwareHypervisorHost host) { |
| Integer version = null; |
| HostMO hostMo = new HostMO(host.getContext(), host.getMor()); |
| String hostApiVersion = ""; |
| try { |
| hostApiVersion = hostMo.getHostAboutInfo().getApiVersion(); |
| } catch (Exception ignored) {} |
| if (hostApiVersion == null) { |
| hostApiVersion = ""; |
| } |
| version = apiVersionHardwareVersionMap.get(hostApiVersion); |
| return version; |
| } |
| |
| /* |
| Finds minimum host hardware version as String, of two hosts when both of them are not null |
| and hardware version of both hosts is different. |
| Return null otherwise |
| */ |
| public static String getMinimumHostHardwareVersion(VmwareHypervisorHost host1, VmwareHypervisorHost host2) { |
| String hardwareVersion = null; |
| if (host1 != null & host2 != null) { |
| Integer host1Version = getHostHardwareVersion(host1); |
| Integer host2Version = getHostHardwareVersion(host2); |
| if (host1Version != null && host2Version != null && !host1Version.equals(host2Version)) { |
| hardwareVersion = VirtualMachineMO.getVmxFormattedVirtualHardwareVersion(Math.min(host1Version, host2Version)); |
| } |
| } |
| return hardwareVersion; |
| } |
| |
| public static VirtualMachineMO findVmOnHypervisorHostOrPeer(VmwareHypervisorHost hypervisorHost, String vmName) throws Exception { |
| VirtualMachineMO vmMo = hypervisorHost.findVmOnHyperHost(vmName); |
| if (vmMo == null) { |
| if (LOGGER.isDebugEnabled()) { |
| LOGGER.debug(String.format("Unable to find the VM on host %s, try within datacenter", hypervisorHost.getHyperHostName())); |
| } |
| vmMo = hypervisorHost.findVmOnPeerHyperHost(vmName); |
| } |
| return vmMo; |
| } |
| } |