# Copyright 2013 VMware Inc. # All Rights Reserved # # 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. from neutron_lib.api.definitions import multiprovidernet as mpnet_apidef from neutron_lib.api.definitions import provider_net as pnet from neutron_lib.api import validators from neutron_lib import constants from oslo_log import log from vmware_nsx.api_client import client from vmware_nsx.common import utils as vmw_utils from vmware_nsx.db import db as nsx_db from vmware_nsx import nsx_cluster from vmware_nsx.nsxlib.mh import switch as switchlib LOG = log.getLogger(__name__) def get_nsx_switch_ids(session, cluster, neutron_network_id): """Return the NSX switch id for a given neutron network. First lookup for mappings in Neutron database. If no mapping is found, query the NSX backend and add the mappings. """ nsx_switch_ids = nsx_db.get_nsx_switch_ids( session, neutron_network_id) if not nsx_switch_ids: # Find logical switches from backend. # This is a rather expensive query, but it won't be executed # more than once for each network in Neutron's lifetime nsx_switches = switchlib.get_lswitches(cluster, neutron_network_id) if not nsx_switches: LOG.warning("Unable to find NSX switches for Neutron network " "%s", neutron_network_id) return nsx_switch_ids = [] with session.begin(subtransactions=True): for nsx_switch in nsx_switches: nsx_switch_id = nsx_switch['uuid'] nsx_switch_ids.append(nsx_switch_id) # Create DB mapping nsx_db.add_neutron_nsx_network_mapping( session, neutron_network_id, nsx_switch_id) return nsx_switch_ids def get_nsx_switch_and_port_id(session, cluster, neutron_port_id): """Return the NSX switch and port uuids for a given neutron port. First, look up the Neutron database. If not found, execute a query on NSX platform as the mapping might be missing because the port was created before upgrading to grizzly. This routine also retrieves the identifier of the logical switch in the backend where the port is plugged. Prior to Icehouse this information was not available in the Neutron Database. For dealing with pre-existing records, this routine will query the backend for retrieving the correct switch identifier. As of Icehouse release it is not indeed anymore possible to assume the backend logical switch identifier is equal to the neutron network identifier. """ nsx_switch_id, nsx_port_id = nsx_db.get_nsx_switch_and_port_id( session, neutron_port_id) if not nsx_switch_id: # Find logical switch for port from backend # This is a rather expensive query, but it won't be executed # more than once for each port in Neutron's lifetime nsx_ports = switchlib.query_lswitch_lports( cluster, '*', relations='LogicalSwitchConfig', filters={'tag': neutron_port_id, 'tag_scope': 'q_port_id'}) # Only one result expected # NOTE(salv-orlando): Not handling the case where more than one # port is found with the same neutron port tag if not nsx_ports: LOG.warning("Unable to find NSX port for Neutron port %s", neutron_port_id) # This method is supposed to return a tuple return None, None nsx_port = nsx_ports[0] nsx_switch_id = (nsx_port['_relations'] ['LogicalSwitchConfig']['uuid']) if nsx_port_id: # Mapping already exists. Delete before recreating nsx_db.delete_neutron_nsx_port_mapping( session, neutron_port_id) else: nsx_port_id = nsx_port['uuid'] # (re)Create DB mapping nsx_db.add_neutron_nsx_port_mapping( session, neutron_port_id, nsx_switch_id, nsx_port_id) return nsx_switch_id, nsx_port_id def create_nsx_cluster(cluster_opts, concurrent_connections, gen_timeout): cluster = nsx_cluster.NSXCluster(**cluster_opts) def _ctrl_split(x, y): return (x, int(y), True) api_providers = [_ctrl_split(*ctrl.split(':')) for ctrl in cluster.nsx_controllers] cluster.api_client = client.NsxApiClient( api_providers, cluster.nsx_user, cluster.nsx_password, http_timeout=cluster.http_timeout, retries=cluster.retries, redirects=cluster.redirects, concurrent_connections=concurrent_connections, gen_timeout=gen_timeout) return cluster def _convert_bindings_to_nsx_transport_zones(bindings): nsx_transport_zones_config = [] for binding in bindings: transport_entry = {} if binding.binding_type in [vmw_utils.NetworkTypes.FLAT, vmw_utils.NetworkTypes.VLAN]: transport_entry['transport_type'] = ( vmw_utils.NetworkTypes.BRIDGE) transport_entry['binding_config'] = {} vlan_id = binding.vlan_id if vlan_id: transport_entry['binding_config'] = ( {'vlan_translation': [{'transport': vlan_id}]}) else: transport_entry['transport_type'] = binding.binding_type transport_entry['zone_uuid'] = binding.phy_uuid nsx_transport_zones_config.append(transport_entry) return nsx_transport_zones_config def _convert_segments_to_nsx_transport_zones(segments, default_tz_uuid): nsx_transport_zones_config = [] for transport_zone in segments: for value in [pnet.NETWORK_TYPE, pnet.PHYSICAL_NETWORK, pnet.SEGMENTATION_ID]: if transport_zone.get(value) == constants.ATTR_NOT_SPECIFIED: transport_zone[value] = None transport_entry = {} transport_type = transport_zone.get(pnet.NETWORK_TYPE) if transport_type in [vmw_utils.NetworkTypes.FLAT, vmw_utils.NetworkTypes.VLAN]: transport_entry['transport_type'] = ( vmw_utils.NetworkTypes.BRIDGE) transport_entry['binding_config'] = {} vlan_id = transport_zone.get(pnet.SEGMENTATION_ID) if vlan_id: transport_entry['binding_config'] = ( {'vlan_translation': [{'transport': vlan_id}]}) else: transport_entry['transport_type'] = transport_type transport_entry['zone_uuid'] = ( transport_zone[pnet.PHYSICAL_NETWORK] or default_tz_uuid) nsx_transport_zones_config.append(transport_entry) return nsx_transport_zones_config def convert_to_nsx_transport_zones( default_tz_uuid, network=None, bindings=None, default_transport_type=None): # Convert fields from provider request to nsx format if (network and not validators.is_attr_set( network.get(mpnet_apidef.SEGMENTS))): return [{"zone_uuid": default_tz_uuid, "transport_type": default_transport_type}] # Convert fields from db to nsx format if bindings: return _convert_bindings_to_nsx_transport_zones(bindings) # If we end up here we need to convert multiprovider segments into nsx # transport zone configurations return _convert_segments_to_nsx_transport_zones( network.get(mpnet_apidef.SEGMENTS), default_tz_uuid)