# Copyright (c) 2011 Citrix Systems, Inc. # Copyright 2011 OpenStack Foundation # # Licensed 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. """VIF drivers for VMware.""" from oslo_log import log as logging from oslo_utils import versionutils from oslo_vmware import vim_util import nova.conf from nova import exception from nova.i18n import _, _LI, _LW from nova.network import model from nova.virt.vmwareapi import constants from nova.virt.vmwareapi import network_util from nova.virt.vmwareapi import vm_util LOG = logging.getLogger(__name__) CONF = nova.conf.CONF def _get_associated_vswitch_for_interface(session, interface, cluster=None): # Check if the physical network adapter exists on the host. if not network_util.check_if_vlan_interface_exists(session, interface, cluster): raise exception.NetworkAdapterNotFound(adapter=interface) # Get the vSwitch associated with the Physical Adapter vswitch_associated = network_util.get_vswitch_for_vlan_interface( session, interface, cluster) if not vswitch_associated: raise exception.SwitchNotFoundForNetworkAdapter(adapter=interface) return vswitch_associated def ensure_vlan_bridge(session, vif, cluster=None, create_vlan=True): """Create a vlan and bridge unless they already exist.""" vlan_num = vif['network'].get_meta('vlan') bridge = vif['network']['bridge'] vlan_interface = CONF.vmware.vlan_interface network_ref = network_util.get_network_with_the_name(session, bridge, cluster) if network_ref and network_ref['type'] == 'DistributedVirtualPortgroup': return network_ref if not network_ref: # Create a port group on the vSwitch associated with the # vlan_interface corresponding physical network adapter on the ESX # host. vswitch_associated = _get_associated_vswitch_for_interface(session, vlan_interface, cluster) network_util.create_port_group(session, bridge, vswitch_associated, vlan_num if create_vlan else 0, cluster) network_ref = network_util.get_network_with_the_name(session, bridge, cluster) elif create_vlan: # Get the vSwitch associated with the Physical Adapter vswitch_associated = _get_associated_vswitch_for_interface(session, vlan_interface, cluster) # Get the vlan id and vswitch corresponding to the port group _get_pg_info = network_util.get_vlanid_and_vswitch_for_portgroup pg_vlanid, pg_vswitch = _get_pg_info(session, bridge, cluster) # Check if the vswitch associated is proper if pg_vswitch != vswitch_associated: raise exception.InvalidVLANPortGroup( bridge=bridge, expected=vswitch_associated, actual=pg_vswitch) # Check if the vlan id is proper for the port group if pg_vlanid != vlan_num: raise exception.InvalidVLANTag(bridge=bridge, tag=vlan_num, pgroup=pg_vlanid) return network_ref def _check_ovs_supported_version(session): # The port type 'ovs' is only support by the VC version 5.5 onwards min_version = versionutils.convert_version_to_int( constants.MIN_VC_OVS_VERSION) vc_version = versionutils.convert_version_to_int( vim_util.get_vc_version(session)) if vc_version < min_version: LOG.warning(_LW('VMware vCenter version less than %(version)s ' 'does not support the \'ovs\' port type.'), {'version': constants.MIN_VC_OVS_VERSION}) def _get_neutron_network(session, cluster, vif): if vif['type'] == model.VIF_TYPE_OVS: _check_ovs_supported_version(session) # Check if this is the NSX-MH plugin is used if CONF.vmware.integration_bridge: net_id = CONF.vmware.integration_bridge use_external_id = False network_type = 'opaque' else: # The NSX|V3 plugin will pass the nsx-logical-switch-id as part # of the port details. This will enable the VC to connect to # that specific opaque network net_id = (vif.get('details') and vif['details'].get('nsx-logical-switch-id')) if not net_id: # Make use of the original one, in the event that the # plugin does not pass the aforementioned id LOG.info(_LI('NSX Logical switch ID is not present. ' 'Using network ID to attach to the ' 'opaque network.')) net_id = vif['network']['id'] use_external_id = True network_type = 'nsx.LogicalSwitch' network_ref = {'type': 'OpaqueNetwork', 'network-id': net_id, 'network-type': network_type, 'use-external-id': use_external_id} elif vif['type'] == model.VIF_TYPE_DVS: # Port binding for DVS VIF types may pass the name # of the port group, so use it if present network_id = vif.get('details', {}).get('dvs_port_group_name') if network_id is None: # Make use of the original one, in the event that the # port binding does not provide this key in VIF details network_id = vif['network']['bridge'] network_ref = network_util.get_network_with_the_name( session, network_id, cluster) if not network_ref: raise exception.NetworkNotFoundForBridge(bridge=network_id) if vif.get('details') and vif['details'].get('dvs_port_key'): network_ref['dvs_port_key'] = vif['details']['dvs_port_key'] else: reason = _('vif type %s not supported') % vif['type'] raise exception.InvalidInput(reason=reason) return network_ref def get_network_ref(session, cluster, vif, is_neutron): if is_neutron: network_ref = _get_neutron_network(session, cluster, vif) else: create_vlan = vif['network'].get_meta('should_create_vlan', False) network_ref = ensure_vlan_bridge(session, vif, cluster=cluster, create_vlan=create_vlan) return network_ref def get_vif_dict(session, cluster, vif_model, is_neutron, vif): mac = vif['address'] name = vif['network']['bridge'] or CONF.vmware.integration_bridge ref = get_network_ref(session, cluster, vif, is_neutron) return {'network_name': name, 'mac_address': mac, 'network_ref': ref, 'iface_id': vif['id'], 'vif_model': vif_model} def get_vif_info(session, cluster, is_neutron, vif_model, network_info): vif_infos = [] if network_info is None: return vif_infos for vif in network_info: vif_infos.append(get_vif_dict(session, cluster, vif_model, is_neutron, vif)) return vif_infos def get_network_device(hardware_devices, mac_address): """Return the network device with MAC 'mac_address'.""" if hardware_devices.__class__.__name__ == "ArrayOfVirtualDevice": hardware_devices = hardware_devices.VirtualDevice for device in hardware_devices: if device.__class__.__name__ in vm_util.ALL_SUPPORTED_NETWORK_DEVICES: if hasattr(device, 'macAddress'): if device.macAddress == mac_address: return device