| // 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.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 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.commons.lang.StringUtils; |
| import org.apache.log4j.Logger; |
| 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.utils.ActionDelegate; |
| 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.OvfCreateDescriptorParams; |
| import com.vmware.vim25.OvfCreateDescriptorResult; |
| import com.vmware.vim25.AlreadyExistsFaultMsg; |
| import com.vmware.vim25.BoolPolicy; |
| import com.vmware.vim25.CustomFieldStringValue; |
| import com.vmware.vim25.DVPortSetting; |
| import com.vmware.vim25.DVPortgroupConfigInfo; |
| import com.vmware.vim25.DVPortgroupConfigSpec; |
| import com.vmware.vim25.DVSSecurityPolicy; |
| import com.vmware.vim25.DVSTrafficShapingPolicy; |
| import com.vmware.vim25.DynamicProperty; |
| 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.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.OvfCreateImportSpecParams; |
| import com.vmware.vim25.OvfCreateImportSpecResult; |
| import com.vmware.vim25.OvfFileItem; |
| import com.vmware.vim25.OvfFile; |
| import com.vmware.vim25.ParaVirtualSCSIController; |
| 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.VirtualDisk; |
| import com.vmware.vim25.VirtualDeviceConfigSpec; |
| import com.vmware.vim25.VirtualDeviceConfigSpecOperation; |
| 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.VirtualMachineVideoCard; |
| import com.vmware.vim25.VirtualSCSIController; |
| import com.vmware.vim25.VirtualSCSISharing; |
| import com.vmware.vim25.VirtualMachineImportSpec; |
| import com.vmware.vim25.VmwareDistributedVirtualSwitchPvlanSpec; |
| import com.vmware.vim25.VmwareDistributedVirtualSwitchTrunkVlanSpec; |
| import com.vmware.vim25.VmwareDistributedVirtualSwitchVlanIdSpec; |
| import com.vmware.vim25.VmwareDistributedVirtualSwitchVlanSpec; |
| import java.io.FileWriter; |
| import java.util.UUID; |
| |
| public class HypervisorHostHelper { |
| private static final Logger s_logger = Logger.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 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."; |
| s_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."; |
| s_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 { |
| s_logger.info("Connecting to Nexus 1000v: " + vsmIp); |
| netconfClient = new NetconfHelper(vsmIp, vsmUserName, vsmPassword); |
| s_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(); |
| s_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) { |
| s_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(); |
| s_logger.error(msg); |
| if (netconfClient != null) { |
| netconfClient.disconnect(); |
| s_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 { |
| s_logger.info("Updating Ethernet port profile " + ethPortProfileName + " with VLAN " + vlanId); |
| netconfClient.updatePortProfile(ethPortProfileName, SwitchPortMode.trunk, params); |
| s_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(); |
| s_logger.error(msg); |
| if (netconfClient != null) { |
| netconfClient.disconnect(); |
| s_logger.debug("Disconnected Nexus 1000v session."); |
| } |
| throw new CloudRuntimeException(msg); |
| } |
| } |
| |
| try { |
| if (vlanId == null) { |
| s_logger.info("Adding port profile configured over untagged VLAN."); |
| netconfClient.addPortProfile(networkName, PortProfileType.vethernet, BindingType.portbindingstatic, SwitchPortMode.access, 0); |
| } else { |
| if (!configureVServiceInNexus) { |
| s_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; |
| s_logger.info("Adding vservice node in Nexus VSM for VLAN : " + vlanId.toString()); |
| netconfClient.addVServiceNode(vlanId.toString(), gateway); |
| s_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(); |
| s_logger.error(msg); |
| if (netconfClient != null) { |
| netconfClient.disconnect(); |
| s_logger.debug("Disconnected Nexus 1000v session."); |
| } |
| throw new CloudRuntimeException(msg); |
| } |
| |
| try { |
| if (averageBandwidth > 0) { |
| s_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(); |
| s_logger.error(msg); |
| throw new CloudRuntimeException(msg); |
| } finally { |
| if (netconfClient != null) { |
| netconfClient.disconnect(); |
| s_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(); |
| s_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) { |
| s_logger.debug("Detected that policy map is already applied to port profile " + vethPortProfileName); |
| if (netconfClient != null) { |
| netconfClient.disconnect(); |
| s_logger.debug("Disconnected Nexus 1000v session."); |
| } |
| return; |
| } else { |
| try { |
| // TODO(sateesh): Change the type of peakBandwidth & |
| // burstRate in PolicyMap to long. |
| s_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(); |
| s_logger.error(msg); |
| if (netconfClient != null) { |
| netconfClient.disconnect(); |
| s_logger.debug("Disconnected Nexus 1000v session."); |
| } |
| throw new CloudRuntimeException(msg); |
| } |
| |
| try { |
| s_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(); |
| s_logger.error(msg); |
| if (netconfClient != null) { |
| netconfClient.disconnect(); |
| s_logger.debug("Disconnected Nexus 1000v session."); |
| } |
| throw new CloudRuntimeException(msg); |
| } |
| } |
| } |
| |
| if (vlanId == null) { |
| s_logger.info("Skipping update operation over ethernet port profile " + ethPortProfileName + " for untagged VLAN."); |
| if (netconfClient != null) { |
| netconfClient.disconnect(); |
| s_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(); |
| s_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 { |
| s_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(); |
| s_logger.error(msg); |
| if (netconfClient != null) { |
| netconfClient.disconnect(); |
| s_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(); |
| s_logger.error(msg); |
| if (netconfClient != null) { |
| netconfClient.disconnect(); |
| s_logger.debug("Disconnected Nexus 1000v session."); |
| } |
| throw new CloudRuntimeException(msg); |
| } |
| } |
| |
| /** |
| * @param ethPortProfileName |
| * @param namePrefix |
| * @param hostMo |
| * @param vlanId |
| * @param networkRateMbps |
| * @param networkRateMulticastMbps |
| * @param timeOutMs |
| * @param vSwitchType |
| * @param numPorts |
| * @param details |
| * @return |
| * @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) 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.Vsp}; |
| |
| 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) { |
| DVSTrafficShapingPolicy shapingPolicy; |
| DVSSecurityPolicy secPolicy; |
| 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) { |
| s_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; |
| s_logger.error(msg); |
| throw new Exception(msg); |
| } else { |
| s_logger.debug("Found distributed vSwitch " + dvSwitchName); |
| } |
| |
| 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 { |
| dvSwitchMo = new DistributedVirtualSwitchMO(context, morDvSwitch); |
| |
| shapingPolicy = getDVSShapingPolicy(networkRateMbps); |
| secPolicy = createDVSSecurityPolicy(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. |
| if (vid != null && spvlanid != null) { |
| setupPVlanPair(dvSwitchMo, morDvSwitch, vid, spvlanid); |
| } |
| |
| VMwareDVSPortgroupPolicy portGroupPolicy = null; |
| if (broadcastDomainType == BroadcastDomainType.Vsp) { |
| //If the broadcastDomainType is Vsp, then set the VMwareDVSPortgroupPolicy |
| portGroupPolicy = new VMwareDVSPortgroupPolicy(); |
| portGroupPolicy.setVlanOverrideAllowed(true); |
| portGroupPolicy.setBlockOverrideAllowed(true); |
| portGroupPolicy.setPortConfigResetAtDisconnect(true); |
| } |
| // Next, create the port group. For this, we need to create a VLAN spec. |
| createPortGroup(physicalNetwork, networkName, vlanId, vid, spvlanid, dataCenterMo, shapingPolicy, secPolicy, portGroupPolicy, dvSwitchMo, numPorts, autoExpandSupported); |
| 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) { |
| s_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; |
| s_logger.error(msg); |
| throw new Exception(msg); |
| } else { |
| s_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) { |
| s_logger.info("Stocking credentials of Nexus VSM"); |
| context.registerStockObject("vsmcredentials", vsmCredentials); |
| } |
| |
| if (!dataCenterMo.hasDvPortGroup(networkName)) { |
| s_logger.info("Port profile " + networkName + " not found."); |
| createPortProfile(context, physicalNetwork, networkName, vid, networkRateMbps, peakBandwidth, burstSize, gateway, configureVServiceInNexus); |
| bWaitPortGroupReady = true; |
| } else { |
| s_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; |
| s_logger.error(msg); |
| throw new Exception(msg); |
| } |
| |
| if (createGCTag) { |
| NetworkMO networkMo = new NetworkMO(hostMo.getContext(), morNetwork); |
| networkMo.setCustomFieldValue(CustomFieldConstants.CLOUD_GC_DVP, "true"); |
| s_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 vCenterApiVersion.compareTo(minVcenterApiVersionForFeature) >= 0 ? true : false; |
| } |
| |
| private static void setupPVlanPair(DistributedVirtualSwitchMO dvSwitchMo, ManagedObjectReference morDvSwitch, Integer vid, Integer spvlanid) throws Exception { |
| 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"; |
| s_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"; |
| s_logger.error(msg); |
| throw new Exception(msg); |
| } |
| } else { |
| if (vlanmap.containsKey(spvlanid) && !vlanmap.get(spvlanid).equals(HypervisorHostHelper.PvlanType.isolated)) { |
| // This PVLAN ID is already setup as a non-isolated vlan id on the DVS. Throw an exception. |
| String msg = "Specified secondary PVLAN ID " + spvlanid + " is already in use as a " + vlanmap.get(spvlanid).toString() + " VLAN in the DVSwitch"; |
| s_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)) { |
| VMwareDVSPvlanConfigSpec spvlanConfigSpec = createDVPortPvlanConfigSpec(vid, spvlanid, PvlanType.isolated, 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) { |
| s_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 |
| s_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, VMwareDVSPortgroupPolicy portGroupPolicy, DistributedVirtualSwitchMO dvSwitchMo, int numPorts, boolean autoExpandSupported) |
| 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, vlanSpec); |
| } 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, pvlanSpec); |
| } |
| |
| newDvPortGroupSpec = createDvPortGroupSpec(networkName, dvsPortSetting, autoExpandSupported); |
| if (portGroupPolicy != null) { |
| newDvPortGroupSpec.setPolicy(portGroupPolicy); |
| } |
| |
| if (!dataCenterMo.hasDvPortGroup(networkName)) { |
| s_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 { |
| s_logger.info("Found Distributed Virtual Port group " + networkName); |
| DVPortgroupConfigInfo currentDvPortgroupInfo = dataCenterMo.getDvPortGroupSpec(networkName); |
| if (!isSpecMatch(currentDvPortgroupInfo, newDvPortGroupSpec)) { |
| s_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); |
| } |
| } |
| } |
| } |
| |
| public static boolean isSpecMatch(DVPortgroupConfigInfo currentDvPortgroupInfo, DVPortgroupConfigSpec newDvPortGroupSpec) { |
| String dvPortGroupName = newDvPortGroupSpec.getName(); |
| s_logger.debug("Checking if configuration of dvPortGroup [" + dvPortGroupName + "] has changed."); |
| boolean specMatches = true; |
| 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)) { |
| s_logger.info("Detected change in state of shaping policy (enabled/disabled) [" + newIsEnabled + "]"); |
| specMatches = false; |
| } |
| |
| if (oldIsEnabled || newIsEnabled) { |
| if (oldAverageBandwidth != null && !oldAverageBandwidth.equals(newAverageBandwidth)) { |
| s_logger.info("Average bandwidth setting in new shaping policy doesn't match the existing setting."); |
| specMatches = false; |
| } else if (oldBurstSize != null && !oldBurstSize.equals(newBurstSize)) { |
| s_logger.info("Burst size setting in new shaping policy doesn't match the existing setting."); |
| specMatches = false; |
| } else if (oldPeakBandwidth != null && !oldPeakBandwidth.equals(newPeakBandwidth)) { |
| s_logger.info("Peak bandwidth setting in new shaping policy doesn't match the existing setting."); |
| specMatches = false; |
| } |
| } |
| |
| boolean oldAutoExpandSetting = currentDvPortgroupInfo.isAutoExpand(); |
| boolean autoExpandEnabled = newDvPortGroupSpec.isAutoExpand(); |
| if (oldAutoExpandSetting != autoExpandEnabled) { |
| specMatches = 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) { |
| s_logger.info("Need to update the number of dvports for dvPortGroup :[" + dvPortGroupName + |
| "] from existing number of dvports " + oldNumPorts + " to " + newNumPorts); |
| specMatches = false; |
| } else if (oldNumPorts > newNumPorts) { |
| s_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!"); |
| specMatches = false; |
| } |
| } |
| |
| VMwareDVSPortSetting currentPortSetting = ((VMwareDVSPortSetting)currentDvPortgroupInfo.getDefaultPortConfig()); |
| VMwareDVSPortSetting newPortSetting = ((VMwareDVSPortSetting)newDvPortGroupSpec.getDefaultPortConfig()); |
| if ((currentPortSetting.getSecurityPolicy() == null && newPortSetting.getSecurityPolicy() != null) || |
| (currentPortSetting.getSecurityPolicy() != null && newPortSetting.getSecurityPolicy() == null)) { |
| specMatches = false; |
| } |
| if (currentPortSetting.getSecurityPolicy() != null && newPortSetting.getSecurityPolicy() != null) { |
| if (currentPortSetting.getSecurityPolicy().getAllowPromiscuous() != null && |
| newPortSetting.getSecurityPolicy().getAllowPromiscuous() != null && |
| newPortSetting.getSecurityPolicy().getAllowPromiscuous().isValue() != null && |
| !newPortSetting.getSecurityPolicy().getAllowPromiscuous().isValue().equals(currentPortSetting.getSecurityPolicy().getAllowPromiscuous().isValue())) { |
| specMatches = false; |
| } |
| if (currentPortSetting.getSecurityPolicy().getForgedTransmits() != null && |
| newPortSetting.getSecurityPolicy().getForgedTransmits() != null && |
| newPortSetting.getSecurityPolicy().getForgedTransmits().isValue() != null && |
| !newPortSetting.getSecurityPolicy().getForgedTransmits().isValue().equals(currentPortSetting.getSecurityPolicy().getForgedTransmits().isValue())) { |
| specMatches = false; |
| } |
| if (currentPortSetting.getSecurityPolicy().getMacChanges() != null && |
| newPortSetting.getSecurityPolicy().getMacChanges() != null && |
| newPortSetting.getSecurityPolicy().getMacChanges().isValue() != null && |
| !newPortSetting.getSecurityPolicy().getMacChanges().isValue().equals(currentPortSetting.getSecurityPolicy().getMacChanges().isValue())) { |
| specMatches = 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 { |
| s_logger.debug("Old and new vlan spec type mismatch found for [" + dvPortGroupName + "] has changed. Old spec type is: " + oldVlanSpec.getClass() + ", and new spec type is:" + newVlanSpec.getClass()); |
| return false; |
| } |
| |
| if (oldVlanId != newVlanId) { |
| s_logger.info("Detected that new VLAN [" + newVlanId + "] of dvPortGroup [" + dvPortGroupName + |
| "] is different from current VLAN [" + oldVlanId + "]"); |
| specMatches = false; |
| } |
| |
| return specMatches; |
| } |
| |
| 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; |
| } |
| |
| s_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 (s_logger.isInfoEnabled()) { |
| s_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 (s_logger.isInfoEnabled()) { |
| s_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 (s_logger.isInfoEnabled()) { |
| s_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, |
| VmwareDistributedVirtualSwitchVlanSpec vlanSpec) { |
| VMwareDVSPortSetting dvsPortSetting = new VMwareDVSPortSetting(); |
| dvsPortSetting.setVlan(vlanSpec); |
| dvsPortSetting.setSecurityPolicy(secPolicy); |
| 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, // We don't use Community |
| } |
| |
| 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 && vlanRange != null && !vlanRange.isEmpty()) { |
| s_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); |
| s_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()); |
| 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.valueOf(nicDetails.get(NetworkOffering.Detail.PromiscuousMode))) { |
| secPolicy.setAllowPromiscuous(allow); |
| } else { |
| secPolicy.setAllowPromiscuous(deny); |
| } |
| } |
| if (nicDetails.containsKey(NetworkOffering.Detail.ForgedTransmits)) { |
| if (Boolean.valueOf(nicDetails.get(NetworkOffering.Detail.ForgedTransmits))) { |
| secPolicy.setForgedTransmits(allow); |
| } else { |
| secPolicy.setForgedTransmits(deny); |
| } |
| } |
| if (nicDetails.containsKey(NetworkOffering.Detail.MacAddressChanges)) { |
| if (Boolean.valueOf(nicDetails.get(NetworkOffering.Detail.MacAddressChanges))) { |
| secPolicy.setMacChanges(allow); |
| } else { |
| secPolicy.setMacChanges(deny); |
| } |
| } |
| |
| return secPolicy; |
| } |
| |
| 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) { |
| s_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; |
| s_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.Vsp}; |
| |
| 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; |
| s_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 (s_logger.isDebugEnabled()) |
| s_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) { |
| s_logger.warn("Unable to prepare network on other host, vlan: " + vlanId + ", host: " + otherHostMo.getHostName()); |
| } |
| } |
| } |
| } |
| } finally { |
| lock.unlock(); |
| } |
| } else { |
| s_logger.warn("Unable to lock cluster to prepare guest network, vlan: " + vlanId); |
| } |
| } finally { |
| lock.releaseRef(); |
| } |
| } |
| } |
| |
| s_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 != 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; |
| } |
| |
| s_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 (s_logger.isInfoEnabled()) |
| s_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); |
| |
| String recommendedController = host.getRecommendedDiskController(guestOsIdentifier); |
| String newRootDiskController = controllerInfo.first(); |
| String newDataDiskController = controllerInfo.second(); |
| if (DiskControllerType.getType(controllerInfo.first()) == DiskControllerType.osdefault) { |
| newRootDiskController = recommendedController; |
| } |
| if (DiskControllerType.getType(controllerInfo.second()) == DiskControllerType.osdefault) { |
| newDataDiskController = recommendedController; |
| } |
| |
| 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 |
| s_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); |
| 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; |
| |
| s_logger.info("Waiting for IDE controller be ready in VM: " + vmInternalCSName); |
| Thread.sleep(1000); |
| } |
| |
| return true; |
| } |
| return false; |
| } |
| |
| 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) 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); |
| |
| VirtualMachineMO workingVM = null; |
| VirtualMachineConfigSpec vmConfig = new VirtualMachineConfigSpec(); |
| vmConfig.setName(vmName); |
| 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) { |
| s_logger.debug("[ignored] interupted while waiting to config vm."); |
| } |
| } |
| } |
| |
| if (workingVM != null) { |
| workingVM.setCustomFieldValue(CustomFieldConstants.CLOUD_WORKER, "true"); |
| String workerTag = String.format("%d-%s", System.currentTimeMillis(), hyperHost.getContext().getStockObject("noderuninfo")); |
| workingVM.setCustomFieldValue(CustomFieldConstants.CLOUD_WORKER_TAG, workerTag); |
| } |
| return workingVM; |
| } |
| |
| public static String resolveHostNameInUrl(DatacenterMO dcMo, String url) { |
| s_logger.info("Resolving host name in url through vCenter, url: " + url); |
| |
| URI uri; |
| try { |
| uri = new URI(url); |
| } catch (URISyntaxException e) { |
| s_logger.warn("URISyntaxException on url " + url); |
| return url; |
| } |
| |
| String host = uri.getHost(); |
| if (NetUtils.isValidIp4(host)) { |
| s_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) { |
| s_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()); |
| |
| s_logger.info("url " + url + " is resolved to " + resolvedUri.toString() + " through vCenter"); |
| return resolvedUri.toString(); |
| } catch (URISyntaxException e) { |
| assert (false); |
| return url; |
| } |
| } |
| } catch (Exception e) { |
| s_logger.warn("Unexpected exception ", e); |
| } |
| |
| return url; |
| } |
| |
| public static String removeOVFNetwork(final String ovfString) { |
| if (ovfString == null || ovfString.isEmpty()) { |
| return ovfString; |
| } |
| try { |
| final DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance(); |
| 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 = TransformerFactory.newInstance(); |
| final Transformer transformer = tf.newTransformer(); |
| transformer.transform(domSource, result); |
| return writer.toString(); |
| } catch (SAXException | IOException | ParserConfigurationException | TransformerException e) { |
| s_logger.warn("Unexpected exception caught while removing network elements from OVF:", e); |
| } |
| return ovfString; |
| } |
| |
| public static void importVmFromOVF(VmwareHypervisorHost host, String ovfFilePath, String vmName, DatastoreMO dsMo, String diskOption, ManagedObjectReference morRp, |
| ManagedObjectReference morHost) throws Exception { |
| |
| assert (morRp != null); |
| |
| OvfCreateImportSpecParams importSpecParams = new OvfCreateImportSpecParams(); |
| importSpecParams.setHostSystem(morHost); |
| importSpecParams.setLocale("US"); |
| importSpecParams.setEntityName(vmName); |
| importSpecParams.setDeploymentOption(""); |
| importSpecParams.setDiskProvisioning(diskOption); // diskOption: thin, thick, etc |
| |
| 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 + ", vmName: " + vmName + ", diskOption: " + diskOption; |
| s_logger.error(msg); |
| throw new Exception(msg); |
| } |
| if(!ovfImportResult.getError().isEmpty()) { |
| for (LocalizedMethodFault fault : ovfImportResult.getError()) { |
| s_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()) { |
| s_logger.warn("createImportSpec warning: " + fault.getLocalizedMessage()); |
| } |
| } |
| |
| DatacenterMO dcMo = new DatacenterMO(context, host.getHyperHostDatacenter()); |
| ManagedObjectReference morLease = context.getService().importVApp(morRp, ovfImportResult.getImportSpec(), dcMo.getVmFolder(), morHost); |
| if (morLease == null) { |
| String msg = "importVApp() failed. ovfFilePath: " + ovfFilePath + ", vmName: " + vmName + ", diskOption: " + diskOption; |
| s_logger.error(msg); |
| throw new Exception(msg); |
| } |
| boolean importSuccess = true; |
| final HttpNfcLeaseMO leaseMo = new HttpNfcLeaseMO(context, morLease); |
| HttpNfcLeaseState state = leaseMo.waitState(new HttpNfcLeaseState[] {HttpNfcLeaseState.READY, HttpNfcLeaseState.ERROR}); |
| try { |
| if (state == HttpNfcLeaseState.READY) { |
| final long totalBytes = HttpNfcLeaseMO.calcTotalBytes(ovfImportResult); |
| File ovfFile = new File(ovfFilePath); |
| |
| HttpNfcLeaseInfo httpNfcLeaseInfo = leaseMo.getLeaseInfo(); |
| 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(); |
| 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(); |
| s_logger.error(erroMsg); |
| importSuccess = false; // Set flag to cleanup the stale template left due to failed import operation, if any |
| throw new Exception(erroMsg, e); |
| } catch (Throwable th) { |
| String errorMsg = "throwable caught during file upload task: " + th.getMessage(); |
| s_logger.error(errorMsg); |
| importSuccess = false; // Set flag to cleanup the stale template left due to failed import operation, if any |
| throw new Exception(errorMsg, th); |
| } finally { |
| progressReporter.close(); |
| } |
| if (bytesAlreadyWritten == totalBytes) { |
| leaseMo.updateLeaseProgress(100); |
| } |
| } else if (state == HttpNfcLeaseState.ERROR) { |
| LocalizedMethodFault error = leaseMo.getLeaseError(); |
| MethodFault fault = error.getFault(); |
| String erroMsg = "Object creation on vCenter failed due to: Exception: " + fault.getClass().getName() + ", message: " + error.getLocalizedMessage(); |
| s_logger.error(erroMsg); |
| throw new Exception(erroMsg); |
| } |
| } finally { |
| if (!importSuccess) { |
| s_logger.error("Aborting the lease on " + vmName + " after import operation failed."); |
| leaseMo.abortLease(); |
| } else { |
| leaseMo.completeLease(); |
| } |
| } |
| } |
| |
| 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; |
| s_logger.error(msg); |
| throw new Exception(msg); |
| } |
| |
| if (!ovfImportResult.getError().isEmpty()) { |
| for (LocalizedMethodFault fault : ovfImportResult.getError()) { |
| s_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()) { |
| s_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; |
| s_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); |
| 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); |
| 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.detachAllDisks(); |
| workerVmMo.destroy(); |
| } |
| } |
| |
| 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 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; |
| } |
| |
| } |