diff --git a/plugins/hypervisors/vmware/src/com/cloud/hypervisor/vmware/manager/VmwareManagerImpl.java b/plugins/hypervisors/vmware/src/com/cloud/hypervisor/vmware/manager/VmwareManagerImpl.java index c872e93e1af..9eb6d34590d 100755 --- a/plugins/hypervisors/vmware/src/com/cloud/hypervisor/vmware/manager/VmwareManagerImpl.java +++ b/plugins/hypervisors/vmware/src/com/cloud/hypervisor/vmware/manager/VmwareManagerImpl.java @@ -352,7 +352,7 @@ public class VmwareManagerImpl extends ManagerBase implements VmwareManager, Vmw } s_logger.info("Preparing network on host " + hostMo.getContext().toString() + " for " + privateTrafficLabel); //The management network is probably always going to be a physical network with vlans, so assume BroadcastDomainType VLAN - HypervisorHostHelper.prepareNetwork(vSwitchName, "cloud.private", hostMo, vlanId, null, null, 180000, false, BroadcastDomainType.Vlan); + HypervisorHostHelper.prepareNetwork(vSwitchName, "cloud.private", hostMo, vlanId, null, null, 180000, false, BroadcastDomainType.Vlan, null); } @Override diff --git a/plugins/hypervisors/vmware/src/com/cloud/hypervisor/vmware/resource/VmwareResource.java b/plugins/hypervisors/vmware/src/com/cloud/hypervisor/vmware/resource/VmwareResource.java index 1f54f9cdbbc..57f312186e9 100755 --- a/plugins/hypervisors/vmware/src/com/cloud/hypervisor/vmware/resource/VmwareResource.java +++ b/plugins/hypervisors/vmware/src/com/cloud/hypervisor/vmware/resource/VmwareResource.java @@ -265,6 +265,7 @@ import com.cloud.hypervisor.vmware.mo.CustomFieldsManagerMO; import com.cloud.hypervisor.vmware.mo.DatacenterMO; import com.cloud.hypervisor.vmware.mo.DatastoreMO; import com.cloud.hypervisor.vmware.mo.DiskControllerType; +import com.cloud.hypervisor.vmware.mo.DistributedVirtualSwitchMO; import com.cloud.hypervisor.vmware.mo.FeatureKeyConstants; import com.cloud.hypervisor.vmware.mo.HostDatastoreSystemMO; import com.cloud.hypervisor.vmware.mo.HostFirewallSystemMO; @@ -319,6 +320,70 @@ import com.cloud.vm.VirtualMachine.State; import com.cloud.vm.VirtualMachineName; import com.cloud.vm.VmDetailConstants; +import com.google.gson.Gson; +import com.vmware.vim25.AboutInfo; +import com.vmware.vim25.BoolPolicy; +import com.vmware.vim25.ClusterDasConfigInfo; +import com.vmware.vim25.ComputeResourceSummary; +import com.vmware.vim25.DVPortConfigInfo; +import com.vmware.vim25.DVPortConfigSpec; +import com.vmware.vim25.DVPortSetting; +import com.vmware.vim25.DatastoreSummary; +import com.vmware.vim25.DistributedVirtualPort; +import com.vmware.vim25.DistributedVirtualSwitchPortConnection; +import com.vmware.vim25.DistributedVirtualSwitchPortCriteria; +import com.vmware.vim25.DynamicProperty; +import com.vmware.vim25.GuestInfo; +import com.vmware.vim25.GuestOsDescriptor; +import com.vmware.vim25.HostCapability; +import com.vmware.vim25.HostFirewallInfo; +import com.vmware.vim25.HostFirewallRuleset; +import com.vmware.vim25.HostHostBusAdapter; +import com.vmware.vim25.HostInternetScsiTargetTransport; +import com.vmware.vim25.HostScsiTopology; +import com.vmware.vim25.HostInternetScsiHba; +import com.vmware.vim25.HostInternetScsiHbaAuthenticationProperties; +import com.vmware.vim25.HostInternetScsiHbaStaticTarget; +import com.vmware.vim25.HostScsiDisk; +import com.vmware.vim25.HostScsiTopologyInterface; +import com.vmware.vim25.HostScsiTopologyLun; +import com.vmware.vim25.HostScsiTopologyTarget; +import com.vmware.vim25.ManagedObjectReference; +import com.vmware.vim25.ObjectContent; +import com.vmware.vim25.OptionValue; +import com.vmware.vim25.PerfCounterInfo; +import com.vmware.vim25.PerfEntityMetric; +import com.vmware.vim25.PerfEntityMetricBase; +import com.vmware.vim25.PerfMetricId; +import com.vmware.vim25.PerfMetricIntSeries; +import com.vmware.vim25.PerfMetricSeries; +import com.vmware.vim25.PerfQuerySpec; +import com.vmware.vim25.PerfSampleInfo; +import com.vmware.vim25.RuntimeFaultFaultMsg; +import com.vmware.vim25.ToolsUnavailableFaultMsg; +import com.vmware.vim25.VMwareDVSPortSetting; +import com.vmware.vim25.VimPortType; +import com.vmware.vim25.VirtualDevice; +import com.vmware.vim25.VirtualDeviceBackingInfo; +import com.vmware.vim25.VirtualDeviceConfigSpec; +import com.vmware.vim25.VirtualDeviceConfigSpecOperation; +import com.vmware.vim25.VirtualDisk; +import com.vmware.vim25.VirtualEthernetCard; +import com.vmware.vim25.VirtualEthernetCardDistributedVirtualPortBackingInfo; +import com.vmware.vim25.VirtualEthernetCardNetworkBackingInfo; +import com.vmware.vim25.VirtualLsiLogicController; +import com.vmware.vim25.VirtualMachineConfigOption; +import com.vmware.vim25.VirtualMachineConfigSpec; +import com.vmware.vim25.VirtualMachineFileInfo; +import com.vmware.vim25.VirtualMachineGuestOsIdentifier; +import com.vmware.vim25.VirtualMachinePowerState; +import com.vmware.vim25.VirtualMachineRelocateSpec; +import com.vmware.vim25.VirtualMachineRelocateSpecDiskLocator; +import com.vmware.vim25.VirtualMachineRuntimeInfo; +import com.vmware.vim25.VirtualSCSISharing; +import com.vmware.vim25.VmwareDistributedVirtualSwitchVlanIdSpec; + + public class VmwareResource implements StoragePoolResource, ServerResource, VmwareHostService { private static final Logger s_logger = Logger.getLogger(VmwareResource.class); @@ -1803,7 +1868,7 @@ public class VmwareResource implements StoragePoolResource, ServerResource, Vmwa */ if (VirtualSwitchType.StandardVirtualSwitch == vSwitchType) { networkInfo = HypervisorHostHelper.prepareNetwork(_publicTrafficInfo.getVirtualSwitchName(), "cloud.public", - vmMo.getRunningHost(), vlanId, null, null, _ops_timeout, true, BroadcastDomainType.Vlan); + vmMo.getRunningHost(), vlanId, null, null, _ops_timeout, true, BroadcastDomainType.Vlan, null); } else { networkInfo = HypervisorHostHelper.prepareNetwork(_publicTrafficInfo.getVirtualSwitchName(), "cloud.public", vmMo.getRunningHost(), vlanId, null, null, null, _ops_timeout, vSwitchType, _portsPerDvPortGroup, null, false, BroadcastDomainType.Vlan); @@ -2807,13 +2872,13 @@ public class VmwareResource implements StoragePoolResource, ServerResource, Vmwa extraOptions.add(newVal); /** - * Extra Config : nvp.iface-id = uuid + * Extra Config : nvp.iface-id. = uuid * - Required for Nicira NVP integration */ int nicNum = 0; for (NicTO nicTo : sortNicsByDeviceId(nics)) { newVal = new OptionValue(); - newVal.setKey("nvp.iface-id" + nicNum); + newVal.setKey("nvp.iface-id." + nicNum); newVal.setValue(nicTo.getUuid()); extraOptions.add(newVal); nicNum++; @@ -2837,9 +2902,86 @@ public class VmwareResource implements StoragePoolResource, ServerResource, Vmwa vmMo.setCustomFieldValue(CustomFieldConstants.CLOUD_NIC_MASK, String.valueOf(nicMask)); + int nicIndex = 0; + for (NicTO nicTo : sortNicsByDeviceId(nics)) { + s_logger.debug("Checking for port configuration on NIC device : " + nicTo.toString()); + if (nicTo.getBroadcastType() == BroadcastDomainType.Lswitch) { + // We need to create a port with a unique vlan and pass the key to the nic device + s_logger.debug("Nic " + nicTo.toString() + " needs to be configured for NVP"); + VirtualDevice nicVirtualDevice = vmMo.getNicDeviceByIndex(nicIndex); + if (nicVirtualDevice == null) { + throw new Exception("Failed to find a VirtualDevice for nic " + nicIndex); //FIXME Generic exceptions are bad + } + VirtualDeviceBackingInfo backing = nicVirtualDevice.getBacking(); + if (backing instanceof VirtualEthernetCardDistributedVirtualPortBackingInfo) { + VirtualEthernetCardDistributedVirtualPortBackingInfo portInfo = (VirtualEthernetCardDistributedVirtualPortBackingInfo) backing; + DistributedVirtualSwitchPortConnection port = portInfo.getPort(); + String portKey = port.getPortKey(); + String portGroupKey = port.getPortgroupKey(); + String dvSwitchUuid = port.getSwitchUuid(); + + s_logger.debug("NIC " + nicTo.toString() + " is connected to dvSwitch " + dvSwitchUuid + " pg " + portGroupKey + " port " + portKey); + + ManagedObjectReference dvSwitchManager = vmMo.getContext().getVimClient().getServiceContent().getDvSwitchManager(); + ManagedObjectReference dvSwitch = vmMo.getContext().getVimClient().getService().queryDvsByUuid(dvSwitchManager, dvSwitchUuid); + + DistributedVirtualSwitchPortCriteria criteria = new DistributedVirtualSwitchPortCriteria(); + criteria.setInside(true); + criteria.getPortgroupKey().add(portGroupKey); + criteria.getPortKey().add(portKey); + List dvPorts = vmMo.getContext().getVimClient().getService().fetchDVPorts(dvSwitch, criteria); + + if (dvPorts.isEmpty()) { + throw new Exception("Empty port list from dvSwitch for nic " + nicTo.toString()); + } else if (dvPorts.size() > 1) { + throw new Exception("Expected only one port in the list from dvSwitch for nic " + nicTo.toString()); + } + + DistributedVirtualPort dvPort = dvPorts.get(0); + DVPortConfigInfo dvPortConfigInfo = dvPort.getConfig(); + VMwareDVSPortSetting settings = (VMwareDVSPortSetting) dvPortConfigInfo.getSetting(); + + VmwareDistributedVirtualSwitchVlanIdSpec vlanId = (VmwareDistributedVirtualSwitchVlanIdSpec) settings.getVlan(); + BoolPolicy blocked = settings.getBlocked(); + if (blocked.isValue() == Boolean.TRUE) { + s_logger.debug("Port is blocked, we need to set a vlanid and unblock"); + DVPortConfigSpec dvPortConfigSpec = new DVPortConfigSpec(); + VMwareDVSPortSetting edittedSettings = new VMwareDVSPortSetting(); + // Unblock + blocked.setValue(Boolean.FALSE); + blocked.setInherited(Boolean.FALSE); + edittedSettings.setBlocked(blocked); + // Set vlan + vlanId.setVlanId(100); //FIXME should be a determined based on usage + vlanId.setInherited(false); + edittedSettings.setVlan(vlanId); + + dvPortConfigSpec.setSetting(edittedSettings); + dvPortConfigSpec.setOperation("edit"); + dvPortConfigSpec.setKey(portKey); + List dvPortConfigSpecs = new ArrayList(); + dvPortConfigSpecs.add(dvPortConfigSpec); + ManagedObjectReference task = vmMo.getContext().getVimClient().getService().reconfigureDVPortTask(dvSwitch, dvPortConfigSpecs); + if (!vmMo.getContext().getVimClient().waitForTask(task)) { + s_logger.error("Failed to configure the dvSwitch port for nic " + nicTo.toString()); + } + } else { + s_logger.trace("Port already configured and set to vlan " + vlanId.getVlanId()); + } + } + else { + s_logger.error("nic device backing is of type " + backing.getClass().getName()); + throw new Exception("Incompatible backing for a VirtualDevice for nic " + nicIndex); //FIXME Generic exceptions are bad + } + } + nicIndex++; + } + if (!vmMo.powerOn()) { throw new Exception("Failed to start VM. vmName: " + vmName); } + + state = State.Running; return new StartAnswer(cmd); @@ -2971,7 +3113,7 @@ public class VmwareResource implements StoragePoolResource, ServerResource, Vmwa if (nicTo.getBroadcastType() == BroadcastDomainType.Native) { return defaultVlan; } - if (nicTo.getBroadcastType() == BroadcastDomainType.Vlan || nicTo.getBroadcastType() == BroadcastDomainType.Pvlan) { + else if (nicTo.getBroadcastType() == BroadcastDomainType.Vlan || nicTo.getBroadcastType() == BroadcastDomainType.Pvlan) { if (nicTo.getBroadcastUri() != null) { if (nicTo.getBroadcastType() == BroadcastDomainType.Vlan) // For vlan, the broadcast uri is of the form vlan:// @@ -2983,6 +3125,9 @@ public class VmwareResource implements StoragePoolResource, ServerResource, Vmwa s_logger.warn("BroadcastType is not claimed as VLAN or PVLAN, but without vlan info in broadcast URI. Use vlan info from labeling: " + defaultVlan); return defaultVlan; } + } else if (nicTo.getBroadcastType() == BroadcastDomainType.Lswitch) { + // We don't need to set any VLAN id for an NVP logical switch + return null; } s_logger.warn("Unrecognized broadcast type in VmwareResource, type: " + nicTo.getBroadcastType().toString() + ". Use vlan info from labeling: " + defaultVlan); @@ -3015,7 +3160,7 @@ public class VmwareResource implements StoragePoolResource, ServerResource, Vmwa if (VirtualSwitchType.StandardVirtualSwitch == switchType) { networkInfo = HypervisorHostHelper.prepareNetwork(switchName.first(), namePrefix, hostMo, getVlanInfo(nicTo, switchName.second()), nicTo.getNetworkRateMbps(), nicTo.getNetworkRateMulticastMbps(), _ops_timeout, - !namePrefix.startsWith("cloud.private"), nicTo.getBroadcastType()); + !namePrefix.startsWith("cloud.private"), nicTo.getBroadcastType(), nicTo.getUuid()); } else { String vlanId = getVlanInfo(nicTo, switchName.second()); @@ -3029,7 +3174,7 @@ public class VmwareResource implements StoragePoolResource, ServerResource, Vmwa svlanId = getPvlanInfo(nicTo); } networkInfo = HypervisorHostHelper.prepareNetwork(switchName.first(), namePrefix, hostMo, vlanId, svlanId, - nicTo.getNetworkRateMbps(), nicTo.getNetworkRateMulticastMbps(), _ops_timeout, switchType, + nicTo.getNetworkRateMbps(), nicTo.getNetworkRateMulticastMbps(), _ops_timeout, switchType, _portsPerDvPortGroup, nicTo.getGateway(), configureVServiceInNexus, nicTo.getBroadcastType()); } diff --git a/vmware-base/src/com/cloud/hypervisor/vmware/mo/HypervisorHostHelper.java b/vmware-base/src/com/cloud/hypervisor/vmware/mo/HypervisorHostHelper.java index 9df7bc3af60..dc1486a2ee5 100755 --- a/vmware-base/src/com/cloud/hypervisor/vmware/mo/HypervisorHostHelper.java +++ b/vmware-base/src/com/cloud/hypervisor/vmware/mo/HypervisorHostHelper.java @@ -52,6 +52,7 @@ 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; @@ -467,8 +468,12 @@ public class HypervisorHostHelper { throw new InvalidParameterException("Nexus Distributed Virtualswitch is not supported with BroadcastDomainType " + broadcastDomainType); } - // Fixed name for the port-group on the vApp vswitch - networkName = "br-int"; + /** + * 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 { @@ -508,8 +513,7 @@ public class HypervisorHostHelper { if (broadcastDomainType == BroadcastDomainType.Lswitch) { if (!dataCenterMo.hasDvPortGroup(networkName)) { - // It'a bad thing if the integration bridge port-group does not exist - throw new InvalidParameterException("Unable to find port-group " + networkName + " on dvSwitch " + dvSwitchName); + throw new InvalidParameterException("NVP integration port-group " + networkName + " does not exist on the DVS " + dvSwitchName); } bWaitPortGroupReady = false; } else { @@ -856,7 +860,7 @@ public class HypervisorHostHelper { public static Pair prepareNetwork(String vSwitchName, String namePrefix, HostMO hostMo, String vlanId, Integer networkRateMbps, Integer networkRateMulticastMbps, - long timeOutMs, boolean syncPeerHosts, BroadcastDomainType broadcastDomainType) throws Exception { + long timeOutMs, boolean syncPeerHosts, BroadcastDomainType broadcastDomainType, String nicUuid) throws Exception { HostVirtualSwitch vSwitch; if (vSwitchName == null) { @@ -893,8 +897,11 @@ public class HypervisorHostHelper { } if (broadcastDomainType == BroadcastDomainType.Lswitch) { - // Fixed name for the port-group on the vApp vswitch - networkName = "br-int"; + /** + * 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 { @@ -933,10 +940,12 @@ public class HypervisorHostHelper { boolean bWaitPortGroupReady = false; if (broadcastDomainType == BroadcastDomainType.Lswitch) { if (!hostMo.hasPortGroup(vSwitch, networkName)) { - // It'a bad thing if the integration bridge port-group does not exist - throw new InvalidParameterException("Unable to find port-group " + networkName + " on dvSwitch " + vSwitchName); + createNvpPortGroup(hostMo, vSwitch, networkName, shapingPolicy); + + bWaitPortGroupReady = true; + } else { + bWaitPortGroupReady = false; } - bWaitPortGroupReady = false; } else { if (!hostMo.hasPortGroup(vSwitch, networkName)) { hostMo.createPortGroup(vSwitch, networkName, vid, secPolicy, shapingPolicy); @@ -982,7 +991,7 @@ public class HypervisorHostHelper { 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); + prepareNetwork(vSwitchName, namePrefix, otherHostMo, vlanId, networkRateMbps, networkRateMulticastMbps, timeOutMs, false, broadcastDomainType, nicUuid); } catch(Exception e) { s_logger.warn("Unable to prepare network on other host, vlan: " + vlanId + ", host: " + otherHostMo.getHostName()); } @@ -1041,6 +1050,44 @@ public class HypervisorHostHelper { 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 usedVlans = new ArrayList(); + 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 { diff --git a/vmware-base/src/com/cloud/hypervisor/vmware/mo/VirtualMachineMO.java b/vmware-base/src/com/cloud/hypervisor/vmware/mo/VirtualMachineMO.java index 5214f3da0df..e2dd789d23c 100644 --- a/vmware-base/src/com/cloud/hypervisor/vmware/mo/VirtualMachineMO.java +++ b/vmware-base/src/com/cloud/hypervisor/vmware/mo/VirtualMachineMO.java @@ -2048,47 +2048,53 @@ public class VirtualMachineMO extends BaseMO { return ++deviceNumber; } - public VirtualDevice[] getNicDevices() throws Exception { - List devices = (List)_context.getVimClient(). - getDynamicProperty(_mor, "config.hardware.device"); + private List getNicDevices(boolean sorted) throws Exception { + List devices = (List)_context.getVimClient(). + getDynamicProperty(_mor, "config.hardware.device"); - List nics = new ArrayList(); - if(devices != null) { - for(VirtualDevice device : devices) { - if(device instanceof VirtualEthernetCard) { - nics.add(device); + List nics = new ArrayList(); + if(devices != null) { + for(VirtualDevice device : devices) { + if(device instanceof VirtualEthernetCard) { + nics.add(device); + } } - } - } + } + + if (sorted) { + Collections.sort(nics, new Comparator() { + @Override + public int compare(VirtualDevice arg0, VirtualDevice arg1) { + int unitNumber0 = arg0.getUnitNumber() != null ? arg0.getUnitNumber().intValue() : -1; + int unitNumber1 = arg1.getUnitNumber() != null ? arg1.getUnitNumber().intValue() : -1; + if(unitNumber0 < unitNumber1) + return -1; + else if(unitNumber0 > unitNumber1) + return 1; + return 0; + } + }); + } + + return nics; + } - return nics.toArray(new VirtualDevice[0]); + public VirtualDevice[] getNicDevices() throws Exception { + return getNicDevices(false).toArray(new VirtualDevice[0]); + } + + public VirtualDevice getNicDeviceByIndex(int index) throws Exception { + List nics = getNicDevices(true); + try { + return nics.get(index); + } catch (IndexOutOfBoundsException e) { + // Not found + return null; + } } public Pair getNicDeviceIndex(String networkNamePrefix) throws Exception { - List devices = (List)_context.getVimClient(). - getDynamicProperty(_mor, "config.hardware.device"); - - List nics = new ArrayList(); - if(devices != null) { - for(VirtualDevice device : devices) { - if(device instanceof VirtualEthernetCard) { - nics.add(device); - } - } - } - - Collections.sort(nics, new Comparator() { - @Override - public int compare(VirtualDevice arg0, VirtualDevice arg1) { - int unitNumber0 = arg0.getUnitNumber() != null ? arg0.getUnitNumber().intValue() : -1; - int unitNumber1 = arg1.getUnitNumber() != null ? arg1.getUnitNumber().intValue() : -1; - if(unitNumber0 < unitNumber1) - return -1; - else if(unitNumber0 > unitNumber1) - return 1; - return 0; - } - }); + List nics = getNicDevices(true); int index = 0; String attachedNetworkSummary;