"create_network": "",
"get_network": "rule:admin_or_owner or rule:shared or rule:external",
"get_network:router:external": "rule:regular_user",
+ "get_network:segments": "rule:admin_only",
"get_network:provider:network_type": "rule:admin_only",
"get_network:provider:physical_network": "rule:admin_only",
"get_network:provider:segmentation_id": "rule:admin_only",
"get_network:queue_id": "rule:admin_only",
"create_network:shared": "rule:admin_only",
"create_network:router:external": "rule:admin_only",
+ "create_network:segments": "rule:admin_only",
"create_network:provider:network_type": "rule:admin_only",
"create_network:provider:physical_network": "rule:admin_only",
"create_network:provider:segmentation_id": "rule:admin_only",
"update_network": "rule:admin_or_owner",
+ "update_network:segments": "rule:admin_only",
"update_network:provider:network_type": "rule:admin_only",
"update_network:provider:physical_network": "rule:admin_only",
"update_network:provider:segmentation_id": "rule:admin_only",
--- /dev/null
+# vim: tabstop=4 shiftwidth=4 softtabstop=4
+#
+# Copyright 2013 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.
+#
+
+"""add multiprovider
+
+Revision ID: 3c6e57a23db4
+Revises: 86cf4d88bd3
+Create Date: 2013-07-10 12:43:35.769283
+
+"""
+
+# revision identifiers, used by Alembic.
+revision = '3c6e57a23db4'
+down_revision = '86cf4d88bd3'
+
+# Change to ['*'] if this migration applies to all plugins
+
+migration_for_plugins = [
+ 'neutron.plugins.nicira.NeutronPlugin.NvpPluginV2'
+]
+
+from alembic import op
+import sqlalchemy as sa
+
+from neutron.db import migration
+
+
+def upgrade(active_plugins=None, options=None):
+ if not migration.should_run(active_plugins, migration_for_plugins):
+ return
+
+ op.create_table(
+ 'nvp_multi_provider_networks',
+ sa.Column('network_id', sa.String(length=36), nullable=False),
+ sa.ForeignKeyConstraint(['network_id'], ['networks.id'],
+ ondelete='CASCADE'),
+ sa.PrimaryKeyConstraint('network_id'),
+ mysql_engine='InnoDB'
+ )
+ op.create_table('rename_nvp_network_bindings',
+ sa.Column('network_id', sa.String(length=36),
+ primary_key=True),
+ sa.Column('binding_type',
+ sa.Enum('flat', 'vlan', 'stt', 'gre', 'l3_ext',
+ name=(
+ 'nvp_network_bindings_binding_type')),
+ nullable=False, primary_key=True),
+ sa.Column('phy_uuid', sa.String(36), primary_key=True,
+ nullable=True),
+ sa.Column('vlan_id', sa.Integer, primary_key=True,
+ nullable=True, autoincrement=False))
+ # copy data from nvp_network_bindings into rename_nvp_network_bindings
+ op.execute("INSERT INTO rename_nvp_network_bindings SELECT network_id, "
+ "binding_type, phy_uuid, vlan_id from nvp_network_bindings")
+
+ op.drop_table('nvp_network_bindings')
+ op.rename_table('rename_nvp_network_bindings', 'nvp_network_bindings')
+
+
+def downgrade(active_plugins=None, options=None):
+ if not migration.should_run(active_plugins, migration_for_plugins):
+ return
+
+ # Delete the multi_provider_network entries from nvp_network_bindings
+ op.execute("DELETE from nvp_network_bindings WHERE network_id IN "
+ "(SELECT network_id from nvp_multi_provider_networks)")
+
+ # create table with previous contains
+ op.create_table('rename_nvp_network_bindings',
+ sa.Column('network_id', sa.String(length=36),
+ primary_key=True),
+ sa.Column('binding_type',
+ sa.Enum('flat', 'vlan', 'stt', 'gre', 'l3_ext',
+ name=(
+ 'nvp_network_bindings_binding_type')),
+ nullable=False),
+ sa.Column('phy_uuid', sa.String(36),
+ nullable=True),
+ sa.Column('vlan_id', sa.Integer,
+ nullable=True, autoincrement=False))
+
+ # copy data from nvp_network_bindings into rename_nvp_network_bindings
+ op.execute("INSERT INTO rename_nvp_network_bindings SELECT network_id, "
+ "binding_type, phy_uuid, vlan_id from nvp_network_bindings")
+
+ op.drop_table('nvp_network_bindings')
+ op.rename_table('rename_nvp_network_bindings', 'nvp_network_bindings')
+ op.drop_table('nvp_multi_provider_networks')
--- /dev/null
+# vim: tabstop=4 shiftwidth=4 softtabstop=4
+
+# Copyright (c) 2013 OpenStack Foundation.
+# 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.
+
+import webob.exc
+
+from neutron.api import extensions
+from neutron.api.v2 import attributes as attr
+from neutron.common import exceptions as qexception
+from neutron.extensions import providernet as pnet
+
+SEGMENTS = 'segments'
+
+
+class SegmentsSetInConjunctionWithProviders(qexception.InvalidInput):
+ message = _("Segments and provider values cannot both be set.")
+
+
+class SegmentsContainDuplicateEntry(qexception.InvalidInput):
+ message = _("Duplicate segment entry in request.")
+
+
+def _convert_and_validate_segments(segments, valid_values=None):
+ unique = set()
+ for segment in segments:
+ unique.add(tuple(segment.iteritems()))
+ network_type = segment.get(pnet.NETWORK_TYPE,
+ attr.ATTR_NOT_SPECIFIED)
+ segment[pnet.NETWORK_TYPE] = network_type
+ physical_network = segment.get(pnet.PHYSICAL_NETWORK,
+ attr.ATTR_NOT_SPECIFIED)
+ segment[pnet.PHYSICAL_NETWORK] = physical_network
+ segmentation_id = segment.get(pnet.SEGMENTATION_ID)
+ if segmentation_id:
+ segment[pnet.SEGMENTATION_ID] = attr.convert_to_int(
+ segmentation_id)
+ else:
+ segment[pnet.SEGMENTATION_ID] = attr.ATTR_NOT_SPECIFIED
+ if len(segment.keys()) != 3:
+ msg = (_("Unrecognized attribute(s) '%s'") %
+ ', '.join(set(segment.keys()) -
+ set([pnet.NETWORK_TYPE, pnet.PHYSICAL_NETWORK,
+ pnet.SEGMENTATION_ID])))
+ raise webob.exc.HTTPBadRequest(msg)
+ if len(unique) != len(segments):
+ raise SegmentsContainDuplicateEntry()
+
+
+attr.validators['type:convert_segments'] = (
+ _convert_and_validate_segments)
+
+
+EXTENDED_ATTRIBUTES_2_0 = {
+ 'networks': {
+ SEGMENTS: {'allow_post': True, 'allow_put': True,
+ 'validate': {'type:convert_segments': None},
+ 'convert_list_to': attr.convert_kvp_list_to_dict,
+ 'default': attr.ATTR_NOT_SPECIFIED,
+ 'enforce_policy': True,
+ 'is_visible': True},
+ }
+}
+
+
+class Multiprovidernet(extensions.ExtensionDescriptor):
+ """Extension class supporting multiple provider networks.
+
+ This class is used by neutron's extension framework to make
+ metadata about the multiple provider network extension available to
+ clients. No new resources are defined by this extension. Instead,
+ the existing network resource's request and response messages are
+ extended with attributes in the provider namespace.
+
+ With admin rights, network dictionaries returned will also include
+ provider attributes.
+ """
+
+ @classmethod
+ def get_name(cls):
+ return "Multi Provider Network"
+
+ @classmethod
+ def get_alias(cls):
+ return "multi-provider"
+
+ @classmethod
+ def get_description(cls):
+ return ("Expose mapping of virtual networks to multiple physical "
+ "networks")
+
+ @classmethod
+ def get_namespace(cls):
+ return "http://docs.openstack.org/ext/multi-provider/api/v1.0"
+
+ @classmethod
+ def get_updated(cls):
+ return "2013-06-27T10:00:00-00:00"
+
+ def get_extended_resources(self, version):
+ if version == "2.0":
+ return EXTENDED_ATTRIBUTES_2_0
+ else:
+ return {}
from neutron.db import securitygroups_db
from neutron.extensions import extraroute
from neutron.extensions import l3
+from neutron.extensions import multiprovidernet as mpnet
from neutron.extensions import portbindings as pbin
from neutron.extensions import portsecurity as psec
from neutron.extensions import providernet as pnet
GRE = 'gre'
FLAT = 'flat'
VLAN = 'vlan'
+ BRIDGE = 'bridge'
def create_nvp_cluster(cluster_opts, concurrent_connections,
"ext-gw-mode",
"extraroute",
"mac-learning",
+ "multi-provider",
"network-gateway",
"nvp-qos",
"port-security",
def _nvp_find_lswitch_for_port(self, context, port_data):
network = self._get_network(context, port_data['network_id'])
- network_binding = nicira_db.get_network_binding(
+ network_bindings = nicira_db.get_network_bindings(
context.session, port_data['network_id'])
max_ports = self.nvp_opts.max_lp_per_overlay_ls
allow_extra_lswitches = False
- if (network_binding and
- network_binding.binding_type in (NetworkTypes.FLAT,
- NetworkTypes.VLAN)):
- max_ports = self.nvp_opts.max_lp_per_bridged_ls
- allow_extra_lswitches = True
+ for network_binding in network_bindings:
+ if network_binding.binding_type in (NetworkTypes.FLAT,
+ NetworkTypes.VLAN):
+ max_ports = self.nvp_opts.max_lp_per_bridged_ls
+ allow_extra_lswitches = True
+ break
try:
return self._handle_lswitch_selection(self.cluster, network,
- network_binding, max_ports,
+ network_bindings, max_ports,
allow_extra_lswitches)
except NvpApiClient.NvpApiException:
err_desc = _("An exception occured while selecting logical "
nvp_exc.MaintenanceInProgress:
webob.exc.HTTPServiceUnavailable})
- def _handle_provider_create(self, context, attrs):
- # NOTE(salvatore-orlando): This method has been borrowed from
- # the OpenvSwtich plugin, altough changed to match NVP specifics.
- network_type = attrs.get(pnet.NETWORK_TYPE)
- physical_network = attrs.get(pnet.PHYSICAL_NETWORK)
- segmentation_id = attrs.get(pnet.SEGMENTATION_ID)
- network_type_set = attr.is_attr_set(network_type)
- physical_network_set = attr.is_attr_set(physical_network)
- segmentation_id_set = attr.is_attr_set(segmentation_id)
- if not (network_type_set or physical_network_set or
- segmentation_id_set):
+ def _validate_provider_create(self, context, network):
+ if not attr.is_attr_set(network.get(mpnet.SEGMENTS)):
return
- err_msg = None
- if not network_type_set:
- err_msg = _("%s required") % pnet.NETWORK_TYPE
- elif network_type in (NetworkTypes.GRE, NetworkTypes.STT,
- NetworkTypes.FLAT):
- if segmentation_id_set:
- err_msg = _("Segmentation ID cannot be specified with "
- "flat network type")
- elif network_type == NetworkTypes.VLAN:
- if not segmentation_id_set:
- err_msg = _("Segmentation ID must be specified with "
- "vlan network type")
- elif (segmentation_id_set and
- not utils.is_valid_vlan_tag(segmentation_id)):
- err_msg = (_("%(segmentation_id)s out of range "
- "(%(min_id)s through %(max_id)s)") %
- {'segmentation_id': segmentation_id,
- 'min_id': constants.MIN_VLAN_TAG,
- 'max_id': constants.MAX_VLAN_TAG})
+ for segment in network[mpnet.SEGMENTS]:
+ network_type = segment.get(pnet.NETWORK_TYPE)
+ physical_network = segment.get(pnet.PHYSICAL_NETWORK)
+ segmentation_id = segment.get(pnet.SEGMENTATION_ID)
+ network_type_set = attr.is_attr_set(network_type)
+ segmentation_id_set = attr.is_attr_set(segmentation_id)
+
+ err_msg = None
+ if not network_type_set:
+ err_msg = _("%s required") % pnet.NETWORK_TYPE
+ elif network_type in (NetworkTypes.GRE, NetworkTypes.STT,
+ NetworkTypes.FLAT):
+ if segmentation_id_set:
+ err_msg = _("Segmentation ID cannot be specified with "
+ "flat network type")
+ elif network_type == NetworkTypes.VLAN:
+ if not segmentation_id_set:
+ err_msg = _("Segmentation ID must be specified with "
+ "vlan network type")
+ elif (segmentation_id_set and
+ not utils.is_valid_vlan_tag(segmentation_id)):
+ err_msg = (_("%(segmentation_id)s out of range "
+ "(%(min_id)s through %(max_id)s)") %
+ {'segmentation_id': segmentation_id,
+ 'min_id': constants.MIN_VLAN_TAG,
+ 'max_id': constants.MAX_VLAN_TAG})
+ else:
+ # Verify segment is not already allocated
+ bindings = nicira_db.get_network_bindings_by_vlanid(
+ context.session, segmentation_id)
+ if bindings:
+ raise q_exc.VlanIdInUse(
+ vlan_id=segmentation_id,
+ physical_network=physical_network)
+ elif network_type == NetworkTypes.L3_EXT:
+ if (segmentation_id_set and
+ not utils.is_valid_vlan_tag(segmentation_id)):
+ err_msg = (_("%(segmentation_id)s out of range "
+ "(%(min_id)s through %(max_id)s)") %
+ {'segmentation_id': segmentation_id,
+ 'min_id': constants.MIN_VLAN_TAG,
+ 'max_id': constants.MAX_VLAN_TAG})
else:
- # Verify segment is not already allocated
- binding = nicira_db.get_network_binding_by_vlanid(
- context.session, segmentation_id)
- if binding:
- raise q_exc.VlanIdInUse(vlan_id=segmentation_id,
- physical_network=physical_network)
- elif network_type == NetworkTypes.L3_EXT:
- if (segmentation_id_set and
- not utils.is_valid_vlan_tag(segmentation_id)):
- err_msg = (_("%(segmentation_id)s out of range "
- "(%(min_id)s through %(max_id)s)") %
- {'segmentation_id': segmentation_id,
- 'min_id': constants.MIN_VLAN_TAG,
- 'max_id': constants.MAX_VLAN_TAG})
- else:
- err_msg = _("%(net_type_param)s %(net_type_value)s not "
- "supported") % {'net_type_param': pnet.NETWORK_TYPE,
- 'net_type_value': network_type}
- if err_msg:
- raise q_exc.InvalidInput(error_message=err_msg)
- # TODO(salvatore-orlando): Validate tranport zone uuid
- # which should be specified in physical_network
-
- def _extend_network_dict_provider(self, context, network, binding=None):
- if not binding:
- binding = nicira_db.get_network_binding(context.session,
- network['id'])
+ err_msg = (_("%(net_type_param)s %(net_type_value)s not "
+ "supported") %
+ {'net_type_param': pnet.NETWORK_TYPE,
+ 'net_type_value': network_type})
+ if err_msg:
+ raise q_exc.InvalidInput(error_message=err_msg)
+ # TODO(salvatore-orlando): Validate tranport zone uuid
+ # which should be specified in physical_network
+
+ def _extend_network_dict_provider(self, context, network,
+ multiprovider=None, bindings=None):
+ if not bindings:
+ bindings = nicira_db.get_network_bindings(context.session,
+ network['id'])
+ if not multiprovider:
+ multiprovider = nicira_db.is_multiprovider_network(context.session,
+ network['id'])
# With NVP plugin 'normal' overlay networks will have no binding
# TODO(salvatore-orlando) make sure users can specify a distinct
# phy_uuid as 'provider network' for STT net type
- if binding:
- network[pnet.NETWORK_TYPE] = binding.binding_type
- network[pnet.PHYSICAL_NETWORK] = binding.phy_uuid
- network[pnet.SEGMENTATION_ID] = binding.vlan_id
+ if bindings:
+ if not multiprovider:
+ # network came in through provider networks api
+ network[pnet.NETWORK_TYPE] = bindings[0].binding_type
+ network[pnet.PHYSICAL_NETWORK] = bindings[0].phy_uuid
+ network[pnet.SEGMENTATION_ID] = bindings[0].vlan_id
+ else:
+ # network come in though multiprovider networks api
+ network[mpnet.SEGMENTS] = [
+ {pnet.NETWORK_TYPE: binding.binding_type,
+ pnet.PHYSICAL_NETWORK: binding.phy_uuid,
+ pnet.SEGMENTATION_ID: binding.vlan_id}
+ for binding in bindings]
def _handle_lswitch_selection(self, cluster, network,
- network_binding, max_ports,
+ network_bindings, max_ports,
allow_extra_lswitches):
lswitches = nvplib.get_lswitches(cluster, network.id)
try:
main_ls[0]['display_name'],
network['tenant_id'],
tags=tags)
+ transport_zone_config = self._convert_to_nvp_transport_zones(
+ cluster, bindings=network_bindings)
selected_lswitch = nvplib.create_lswitch(
cluster, network.tenant_id,
"%s-ext-%s" % (network.name, len(lswitches)),
- network_binding.binding_type,
- network_binding.phy_uuid,
- network_binding.vlan_id,
+ transport_zone_config,
network.id)
return selected_lswitch
else:
# Consume from all consumers in a thread
self.conn.consume_in_thread()
+ def _convert_to_nvp_transport_zones(self, cluster, network=None,
+ bindings=None):
+ nvp_transport_zones_config = []
+
+ # Convert fields from provider request to nvp format
+ if (network and not attr.is_attr_set(
+ network.get(mpnet.SEGMENTS))):
+ return [{"zone_uuid": cluster.default_tz_uuid,
+ "transport_type": cfg.CONF.NVP.default_transport_type}]
+
+ # Convert fields from db to nvp format
+ if bindings:
+ transport_entry = {}
+ for binding in bindings:
+ if binding.binding_type in [NetworkTypes.FLAT,
+ NetworkTypes.VLAN]:
+ transport_entry['transport_type'] = 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
+ nvp_transport_zones_config.append(transport_entry)
+ return nvp_transport_zones_config
+
+ for transport_zone in network.get(mpnet.SEGMENTS):
+ for value in [pnet.NETWORK_TYPE, pnet.PHYSICAL_NETWORK,
+ pnet.SEGMENTATION_ID]:
+ if transport_zone.get(value) == attr.ATTR_NOT_SPECIFIED:
+ transport_zone[value] = None
+
+ transport_entry = {}
+ transport_type = transport_zone.get(pnet.NETWORK_TYPE)
+ if transport_type in [NetworkTypes.FLAT, NetworkTypes.VLAN]:
+ transport_entry['transport_type'] = 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
+ cluster.deafult_tz_uuid)
+ nvp_transport_zones_config.append(transport_entry)
+ return nvp_transport_zones_config
+
+ def _convert_to_transport_zones_dict(self, network):
+ """Converts the provider request body to multiprovider.
+ Returns: True if request is multiprovider False if provider
+ and None if neither.
+ """
+ if any(attr.is_attr_set(network.get(f))
+ for f in (pnet.NETWORK_TYPE, pnet.PHYSICAL_NETWORK,
+ pnet.SEGMENTATION_ID)):
+ if attr.is_attr_set(network.get(mpnet.SEGMENTS)):
+ raise mpnet.SegmentsSetInConjunctionWithProviders()
+ # convert to transport zone list
+ network[mpnet.SEGMENTS] = [
+ {pnet.NETWORK_TYPE: network[pnet.NETWORK_TYPE],
+ pnet.PHYSICAL_NETWORK: network[pnet.PHYSICAL_NETWORK],
+ pnet.SEGMENTATION_ID: network[pnet.SEGMENTATION_ID]}]
+ del network[pnet.NETWORK_TYPE]
+ del network[pnet.PHYSICAL_NETWORK]
+ del network[pnet.SEGMENTATION_ID]
+ return False
+ if attr.is_attr_set(mpnet.SEGMENTS):
+ return True
+
def create_network(self, context, network):
net_data = network['network']
tenant_id = self._get_tenant_id_for_create(context, net_data)
self._ensure_default_security_group(context, tenant_id)
# Process the provider network extension
- self._handle_provider_create(context, net_data)
+ provider_type = self._convert_to_transport_zones_dict(net_data)
+ self._validate_provider_create(context, net_data)
# Replace ATTR_NOT_SPECIFIED with None before sending to NVP
for key, value in network['network'].iteritems():
if value is attr.ATTR_NOT_SPECIFIED:
LOG.warning(_("Network with admin_state_up=False are not yet "
"supported by this plugin. Ignoring setting for "
"network %s"), net_data.get('name', '<unknown>'))
+ transport_zone_config = self._convert_to_nvp_transport_zones(
+ self.cluster, net_data)
external = net_data.get(l3.EXTERNAL)
if (not attr.is_attr_set(external) or
attr.is_attr_set(external) and not external):
- nvp_binding_type = net_data.get(pnet.NETWORK_TYPE)
- if nvp_binding_type in ('flat', 'vlan'):
- nvp_binding_type = 'bridge'
lswitch = nvplib.create_lswitch(
self.cluster, tenant_id, net_data.get('name'),
- nvp_binding_type, net_data.get(pnet.PHYSICAL_NETWORK),
- net_data.get(pnet.SEGMENTATION_ID),
+ transport_zone_config,
shared=net_data.get(attr.SHARED))
net_data['id'] = lswitch['uuid']
self._process_network_queue_mapping(context, new_net)
self._extend_network_qos_queue(context, new_net)
- if net_data.get(pnet.NETWORK_TYPE):
- net_binding = nicira_db.add_network_binding(
- context.session, new_net['id'],
- net_data.get(pnet.NETWORK_TYPE),
- net_data.get(pnet.PHYSICAL_NETWORK),
- net_data.get(pnet.SEGMENTATION_ID, 0))
+ if (net_data.get(mpnet.SEGMENTS) and
+ isinstance(provider_type, bool)):
+ net_bindings = []
+ for tz in net_data[mpnet.SEGMENTS]:
+ net_bindings.append(nicira_db.add_network_binding(
+ context.session, new_net['id'],
+ tz.get(pnet.NETWORK_TYPE),
+ tz.get(pnet.PHYSICAL_NETWORK),
+ tz.get(pnet.SEGMENTATION_ID, 0)))
+ if provider_type:
+ nicira_db.set_multiprovider_network(context.session,
+ new_net['id'])
self._extend_network_dict_provider(context, new_net,
- net_binding)
+ provider_type,
+ net_bindings)
self.schedule_network(context, new_net)
return new_net
LOG = logging.getLogger(__name__)
-def get_network_binding(session, network_id):
+def get_network_bindings(session, network_id):
session = session or db.get_session()
- try:
- binding = (session.query(nicira_models.NvpNetworkBinding).
- filter_by(network_id=network_id).
- one())
- return binding
- except exc.NoResultFound:
- return
+ return (session.query(nicira_models.NvpNetworkBinding).
+ filter_by(network_id=network_id).
+ all())
-def get_network_binding_by_vlanid(session, vlan_id):
+def get_network_bindings_by_vlanid(session, vlan_id):
session = session or db.get_session()
- try:
- binding = (session.query(nicira_models.NvpNetworkBinding).
- filter_by(vlan_id=vlan_id).
- one())
- return binding
- except exc.NoResultFound:
- return
+ return (session.query(nicira_models.NvpNetworkBinding).
+ filter_by(vlan_id=vlan_id).
+ all())
def add_network_binding(session, network_id, binding_type, phy_uuid, vlan_id):
gw = (session.query(nicira_networkgw_db.NetworkGateway).
filter_by(id=gw_id).one())
gw['default'] = True
+
+
+def set_multiprovider_network(session, network_id):
+ with session.begin(subtransactions=True):
+ multiprovider_network = nicira_models.MultiProviderNetworks(
+ network_id)
+ session.add(multiprovider_network)
+ return multiprovider_network
+
+
+def is_multiprovider_network(session, network_id):
+ with session.begin(subtransactions=True):
+ return bool(
+ session.query(nicira_models.MultiProviderNetworks).filter_by(
+ network_id=network_id).first())
"""
__tablename__ = 'nvp_network_bindings'
+ # TODO(arosen) - it might be worth while refactoring the how this data
+ # is stored later so every column does not need to be a primary key.
network_id = Column(String(36),
ForeignKey('networks.id', ondelete="CASCADE"),
primary_key=True)
# 'flat', 'vlan', stt' or 'gre'
binding_type = Column(Enum('flat', 'vlan', 'stt', 'gre', 'l3_ext',
name='nvp_network_bindings_binding_type'),
- nullable=False)
- phy_uuid = Column(String(36))
- vlan_id = Column(Integer)
+ nullable=False, primary_key=True)
+ phy_uuid = Column(String(36), primary_key=True, nullable=True)
+ vlan_id = Column(Integer, primary_key=True, nullable=True,
+ autoincrement=False)
def __init__(self, network_id, binding_type, phy_uuid, vlan_id):
self.network_id = network_id
def __init__(self, quantum_id, nvp_id):
self.quantum_id = quantum_id
self.nvp_id = nvp_id
+
+
+class MultiProviderNetworks(model_base.BASEV2):
+ """Networks that were provision through multiprovider extension."""
+
+ __tablename__ = 'nvp_multi_provider_networks'
+ network_id = Column(String(36),
+ ForeignKey('networks.id', ondelete="CASCADE"),
+ primary_key=True)
+
+ def __init__(self, network_id):
+ self.network_id = network_id
import inspect
import json
-from oslo.config import cfg
-
#FIXME(danwent): I'd like this file to get to the point where it has
# no neutron-specific logic in it
from neutron.common import constants
def create_lswitch(cluster, tenant_id, display_name,
- transport_type=None,
- transport_zone_uuid=None,
- vlan_id=None,
+ transport_zones_config,
neutron_net_id=None,
shared=None,
**kwargs):
- nvp_binding_type = transport_type
- if transport_type in ('flat', 'vlan'):
- nvp_binding_type = 'bridge'
- transport_zone_config = (
- {"zone_uuid": (transport_zone_uuid or
- cluster.default_tz_uuid),
- "transport_type": (nvp_binding_type or
- cfg.CONF.NVP.default_transport_type)})
lswitch_obj = {"display_name": _check_and_truncate_name(display_name),
- "transport_zones": [transport_zone_config],
+ "transport_zones": transport_zones_config,
"tags": [{"tag": tenant_id, "scope": "os_tid"},
{"tag": NEUTRON_VERSION, "scope": "quantum"}]}
- if nvp_binding_type == 'bridge' and vlan_id:
- transport_zone_config["binding_config"] = {"vlan_translation":
- [{"transport": vlan_id}]}
if neutron_net_id:
lswitch_obj["tags"].append({"tag": neutron_net_id,
"scope": "quantum_net_id"})
import neutron.common.test_lib as test_lib
from neutron import context
from neutron.extensions import l3
+from neutron.extensions import multiprovidernet as mpnet
from neutron.extensions import portbindings
from neutron.extensions import providernet as pnet
from neutron.extensions import securitygroup as secgrp
def test_delete_network_gateway(self):
# The default gateway must still be there
self._test_delete_network_gateway(1)
+
+
+class TestNiciraMultiProviderNetworks(NiciraPluginV2TestCase):
+
+ def setUp(self, plugin=None):
+ cfg.CONF.set_override('api_extensions_path', NVPEXT_PATH)
+ super(TestNiciraMultiProviderNetworks, self).setUp()
+
+ def test_create_network_provider(self):
+ data = {'network': {'name': 'net1',
+ pnet.NETWORK_TYPE: 'vlan',
+ pnet.PHYSICAL_NETWORK: 'physnet1',
+ pnet.SEGMENTATION_ID: 1,
+ 'tenant_id': 'tenant_one'}}
+ network_req = self.new_create_request('networks', data)
+ network = self.deserialize(self.fmt,
+ network_req.get_response(self.api))
+ self.assertEqual(network['network'][pnet.NETWORK_TYPE], 'vlan')
+ self.assertEqual(network['network'][pnet.PHYSICAL_NETWORK], 'physnet1')
+ self.assertEqual(network['network'][pnet.SEGMENTATION_ID], 1)
+ self.assertNotIn(mpnet.SEGMENTS, network['network'])
+
+ def test_create_network_single_multiple_provider(self):
+ data = {'network': {'name': 'net1',
+ mpnet.SEGMENTS:
+ [{pnet.NETWORK_TYPE: 'vlan',
+ pnet.PHYSICAL_NETWORK: 'physnet1',
+ pnet.SEGMENTATION_ID: 1}],
+ 'tenant_id': 'tenant_one'}}
+ net_req = self.new_create_request('networks', data)
+ network = self.deserialize(self.fmt, net_req.get_response(self.api))
+ for provider_field in [pnet.NETWORK_TYPE, pnet.PHYSICAL_NETWORK,
+ pnet.SEGMENTATION_ID]:
+ self.assertTrue(provider_field not in network['network'])
+ tz = network['network'][mpnet.SEGMENTS][0]
+ self.assertEqual(tz[pnet.NETWORK_TYPE], 'vlan')
+ self.assertEqual(tz[pnet.PHYSICAL_NETWORK], 'physnet1')
+ self.assertEqual(tz[pnet.SEGMENTATION_ID], 1)
+
+ # Tests get_network()
+ net_req = self.new_show_request('networks', network['network']['id'])
+ network = self.deserialize(self.fmt, net_req.get_response(self.api))
+ tz = network['network'][mpnet.SEGMENTS][0]
+ self.assertEqual(tz[pnet.NETWORK_TYPE], 'vlan')
+ self.assertEqual(tz[pnet.PHYSICAL_NETWORK], 'physnet1')
+ self.assertEqual(tz[pnet.SEGMENTATION_ID], 1)
+
+ def test_create_network_multprovider(self):
+ data = {'network': {'name': 'net1',
+ mpnet.SEGMENTS:
+ [{pnet.NETWORK_TYPE: 'vlan',
+ pnet.PHYSICAL_NETWORK: 'physnet1',
+ pnet.SEGMENTATION_ID: 1},
+ {pnet.NETWORK_TYPE: 'stt',
+ pnet.PHYSICAL_NETWORK: 'physnet1'}],
+ 'tenant_id': 'tenant_one'}}
+ network_req = self.new_create_request('networks', data)
+ network = self.deserialize(self.fmt,
+ network_req.get_response(self.api))
+ tz = network['network'][mpnet.SEGMENTS]
+ for tz in data['network'][mpnet.SEGMENTS]:
+ for field in [pnet.NETWORK_TYPE, pnet.PHYSICAL_NETWORK,
+ pnet.SEGMENTATION_ID]:
+ self.assertEqual(tz.get(field), tz.get(field))
+
+ # Tests get_network()
+ net_req = self.new_show_request('networks', network['network']['id'])
+ network = self.deserialize(self.fmt, net_req.get_response(self.api))
+ tz = network['network'][mpnet.SEGMENTS]
+ for tz in data['network'][mpnet.SEGMENTS]:
+ for field in [pnet.NETWORK_TYPE, pnet.PHYSICAL_NETWORK,
+ pnet.SEGMENTATION_ID]:
+ self.assertEqual(tz.get(field), tz.get(field))
+
+ def test_create_network_with_provider_and_multiprovider_fail(self):
+ data = {'network': {'name': 'net1',
+ mpnet.SEGMENTS:
+ [{pnet.NETWORK_TYPE: 'vlan',
+ pnet.PHYSICAL_NETWORK: 'physnet1',
+ pnet.SEGMENTATION_ID: 1}],
+ pnet.NETWORK_TYPE: 'vlan',
+ pnet.PHYSICAL_NETWORK: 'physnet1',
+ pnet.SEGMENTATION_ID: 1,
+ 'tenant_id': 'tenant_one'}}
+
+ network_req = self.new_create_request('networks', data)
+ res = network_req.get_response(self.api)
+ self.assertEqual(res.status_int, 400)
+
+ def test_create_network_duplicate_segments(self):
+ data = {'network': {'name': 'net1',
+ mpnet.SEGMENTS:
+ [{pnet.NETWORK_TYPE: 'vlan',
+ pnet.PHYSICAL_NETWORK: 'physnet1',
+ pnet.SEGMENTATION_ID: 1},
+ {pnet.NETWORK_TYPE: 'vlan',
+ pnet.PHYSICAL_NETWORK: 'physnet1',
+ pnet.SEGMENTATION_ID: 1}],
+ 'tenant_id': 'tenant_one'}}
+ network_req = self.new_create_request('networks', data)
+ res = network_req.get_response(self.api)
+ self.assertEqual(res.status_int, 400)
def test_plug_l2_gw_port_attachment(self):
tenant_id = 'pippo'
node_uuid = _uuid()
+ transport_zones_config = [{'zone_uuid': _uuid(),
+ 'transport_type': 'stt'}]
lswitch = nvplib.create_lswitch(self.fake_cluster, tenant_id,
- 'fake-switch')
+ 'fake-switch', transport_zones_config)
gw_id = self._create_gw_service(node_uuid, 'fake-gw')['uuid']
lport = nvplib.create_lport(self.fake_cluster,
lswitch['uuid'],
def test_create_and_get_lswitches_single(self):
tenant_id = 'pippo'
+ transport_zones_config = [{'zone_uuid': _uuid(),
+ 'transport_type': 'stt'}]
lswitch = nvplib.create_lswitch(self.fake_cluster,
tenant_id,
- 'fake-switch')
+ 'fake-switch',
+ transport_zones_config)
res_lswitch = nvplib.get_lswitches(self.fake_cluster,
lswitch['uuid'])
self.assertEqual(len(res_lswitch), 1)
def test_create_and_get_lswitches_single_name_exceeds_40_chars(self):
tenant_id = 'pippo'
+ transport_zones_config = [{'zone_uuid': _uuid(),
+ 'transport_type': 'stt'}]
lswitch = nvplib.create_lswitch(self.fake_cluster,
tenant_id,
- '*' * 50)
+ '*' * 50,
+ transport_zones_config)
res_lswitch = nvplib.get_lswitches(self.fake_cluster,
lswitch['uuid'])
self.assertEqual(len(res_lswitch), 1)
def test_create_and_get_lswitches_multiple(self):
tenant_id = 'pippo'
+ transport_zones_config = [{'zone_uuid': _uuid(),
+ 'transport_type': 'stt'}]
main_lswitch = nvplib.create_lswitch(
self.fake_cluster, tenant_id, 'fake-switch',
+ transport_zones_config,
tags=[{'scope': 'multi_lswitch', 'tag': 'True'}])
# Create secondary lswitch
nvplib.create_lswitch(
self.fake_cluster, tenant_id, 'fake-switch-2',
+ transport_zones_config,
neutron_net_id=main_lswitch['uuid'])
res_lswitch = nvplib.get_lswitches(self.fake_cluster,
main_lswitch['uuid'])
def test_update_lswitch(self):
new_name = 'new-name'
new_tags = [{'scope': 'new_tag', 'tag': 'xxx'}]
+ transport_zones_config = [{'zone_uuid': _uuid(),
+ 'transport_type': 'stt'}]
lswitch = nvplib.create_lswitch(self.fake_cluster,
'pippo',
- 'fake-switch')
+ 'fake-switch',
+ transport_zones_config)
nvplib.update_lswitch(self.fake_cluster, lswitch['uuid'],
new_name, tags=new_tags)
res_lswitch = nvplib.get_lswitches(self.fake_cluster,
'foo', 'bar')
def test_delete_networks(self):
+ transport_zones_config = [{'zone_uuid': _uuid(),
+ 'transport_type': 'stt'}]
lswitch = nvplib.create_lswitch(self.fake_cluster,
'pippo',
- 'fake-switch')
+ 'fake-switch',
+ transport_zones_config)
nvplib.delete_networks(self.fake_cluster, lswitch['uuid'],
[lswitch['uuid']])
self.assertRaises(exceptions.NotFound,
def test_plug_lrouter_port_patch_attachment(self):
tenant_id = 'pippo'
+ transport_zones_config = [{'zone_uuid': _uuid(),
+ 'transport_type': 'stt'}]
lswitch = nvplib.create_lswitch(self.fake_cluster,
- tenant_id, 'fake-switch')
+ tenant_id, 'fake-switch',
+ transport_zones_config)
lport = nvplib.create_lport(self.fake_cluster, lswitch['uuid'],
tenant_id, 'xyz',
'name', 'device_id', True)
def _create_switch_and_port(self, tenant_id='pippo',
neutron_port_id='whatever'):
+ transport_zones_config = [{'zone_uuid': _uuid(),
+ 'transport_type': 'stt'}]
lswitch = nvplib.create_lswitch(self.fake_cluster,
- tenant_id, 'fake-switch')
+ tenant_id, 'fake-switch',
+ transport_zones_config)
lport = nvplib.create_lport(self.fake_cluster, lswitch['uuid'],
tenant_id, neutron_port_id,
'name', 'device_id', True)
def test_get_port_by_tag_not_found_returns_None(self):
tenant_id = 'pippo'
neutron_port_id = 'whatever'
+ transport_zones_config = [{'zone_uuid': _uuid(),
+ 'transport_type': 'stt'}]
lswitch = nvplib.create_lswitch(self.fake_cluster, tenant_id,
- 'fake-switch')
+ 'fake-switch', transport_zones_config)
lport = nvplib.get_port_by_neutron_tag(self.fake_cluster,
lswitch['uuid'],
neutron_port_id)