]> review.fuel-infra Code Review - openstack-build/neutron-build.git/commitdiff
Add support for provider-network extension in nuage Plugin
authorDivya ChanneGowda <divya.hc@gmail.com>
Thu, 14 Aug 2014 19:03:13 +0000 (12:03 -0700)
committerDivya ChanneGowda <divya.hc@gmail.com>
Wed, 27 Aug 2014 01:10:10 +0000 (18:10 -0700)
This implements support for creating provider networks
with Nuage plugin.

Implements: blueprint providernet-ext-support-for-nuage-plugin
Change-Id: Ibabc1561fc7b6bd5ea38617f145af1d0d4545a4f

neutron/db/migration/alembic_migrations/versions/HEAD
neutron/db/migration/alembic_migrations/versions/aae5706a396_nuage_provider_networks.py [new file with mode: 0644]
neutron/plugins/nuage/common/exceptions.py
neutron/plugins/nuage/nuage_models.py
neutron/plugins/nuage/nuagedb.py
neutron/plugins/nuage/plugin.py
neutron/tests/unit/nuage/fake_nuageclient.py
neutron/tests/unit/nuage/test_nuage_plugin.py

index 7c2f9210a5b0ce1dc3d741ee759d6809d43baaf9..b340f2714ce235539ab62a0b59d6e7065e51924d 100644 (file)
@@ -1 +1 @@
-3b85b693a95f
+aae5706a396
diff --git a/neutron/db/migration/alembic_migrations/versions/aae5706a396_nuage_provider_networks.py b/neutron/db/migration/alembic_migrations/versions/aae5706a396_nuage_provider_networks.py
new file mode 100644 (file)
index 0000000..56d5d88
--- /dev/null
@@ -0,0 +1,45 @@
+# Copyright 2014 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.
+#
+
+"""nuage_provider_networks
+
+Revision ID: aae5706a396
+Revises: 3b85b693a95f
+Create Date: 2014-08-18 16:00:21.898795
+
+"""
+
+revision = 'aae5706a396'
+down_revision = '3b85b693a95f'
+
+from alembic import op
+import sqlalchemy as sa
+
+
+def upgrade(active_plugins=None, options=None):
+    op.create_table(
+        'nuage_provider_net_bindings',
+        sa.Column('network_id', sa.String(length=36), nullable=False),
+        sa.Column('network_type', sa.String(length=32), nullable=False),
+        sa.Column('physical_network', sa.String(length=64), nullable=False),
+        sa.Column('vlan_id', sa.Integer(), nullable=False),
+        sa.ForeignKeyConstraint(
+            ['network_id'], ['networks.id'], ondelete='CASCADE'),
+        sa.PrimaryKeyConstraint('network_id')
+    )
+
+
+def downgrade(active_plugins=None, options=None):
+    op.drop_table('nuage_provider_net_bindings')
index 2e11588967918af1a4bfce61f1a6b28c1b59940f..0e1e5460a1318977dbdd71cdaca9e9005cb630cc 100644 (file)
@@ -22,3 +22,7 @@ from neutron.common import exceptions as n_exc
 
 class OperationNotSupported(n_exc.InvalidConfigurationOption):
     message = _("Nuage Plugin does not support this operation: %(msg)s")
+
+
+class NuageBadRequest(n_exc.BadRequest):
+    message = _("Bad request: %(msg)s")
index 4c901cc012ebc26db78b714c1ed0eb42bf7f15f9..dbf7c0703f3ef62ba7568f6c07cca632e010f128 100644 (file)
@@ -40,6 +40,18 @@ class NetPartitionRouter(model_base.BASEV2):
     nuage_router_id = sa.Column(sa.String(36))
 
 
+class ProviderNetBinding(model_base.BASEV2):
+    """Represents binding of virtual network to physical_network and vlan."""
+    __tablename__ = 'nuage_provider_net_bindings'
+
+    network_id = sa.Column(sa.String(36),
+                           sa.ForeignKey('networks.id', ondelete="CASCADE"),
+                           primary_key=True)
+    network_type = sa.Column(sa.String(32), nullable=False)
+    physical_network = sa.Column(sa.String(64), nullable=False)
+    vlan_id = sa.Column(sa.Integer, nullable=False)
+
+
 class SubnetL2Domain(model_base.BASEV2):
     __tablename__ = 'nuage_subnet_l2dom_mapping'
     subnet_id = sa.Column(sa.String(36),
@@ -51,4 +63,4 @@ class SubnetL2Domain(model_base.BASEV2):
     nuage_subnet_id = sa.Column(sa.String(36))
     nuage_l2dom_tmplt_id = sa.Column(sa.String(36))
     nuage_user_id = sa.Column(sa.String(36))
-    nuage_group_id = sa.Column(sa.String(36))
\ No newline at end of file
+    nuage_group_id = sa.Column(sa.String(36))
index 458fd4349532267b90f0113a70adb8888ff5ce91..d7ef52f0e73b65d52733770e670d32338b9dd939 100644 (file)
@@ -98,4 +98,20 @@ def get_ent_rtr_mapping_by_entid(session,
 
 def get_ent_rtr_mapping_by_rtrid(session, rtrid):
     query = session.query(nuage_models.NetPartitionRouter)
-    return query.filter_by(router_id=rtrid).first()
\ No newline at end of file
+    return query.filter_by(router_id=rtrid).first()
+
+
+def add_network_binding(session, network_id, network_type, physical_network,
+                        vlan_id):
+    binding = nuage_models.ProviderNetBinding(
+                            network_id=network_id,
+                            network_type=network_type,
+                            physical_network=physical_network,
+                            vlan_id=vlan_id)
+    session.add(binding)
+
+
+def get_network_binding(session, network_id):
+    return (session.query(nuage_models.ProviderNetBinding).
+            filter_by(network_id=network_id).
+            first())
index 23119f2b017422c28d23c8e31e40408733ab5601..565ec4502d4858f24524bf1fe795069247ba10f7 100644 (file)
@@ -36,6 +36,7 @@ from neutron.db import securitygroups_db as sg_db
 from neutron.extensions import external_net
 from neutron.extensions import l3
 from neutron.extensions import portbindings
+from neutron.extensions import providernet as pnet
 from neutron.extensions import securitygroup as ext_sg
 from neutron.openstack.common import excutils
 from neutron.openstack.common import importutils
@@ -58,7 +59,7 @@ class NuagePlugin(db_base_plugin_v2.NeutronDbPluginV2,
     """Class that implements Nuage Networks' plugin functionality."""
     supported_extension_aliases = ["router", "binding", "external-net",
                                    "net-partition", "nuage-router",
-                                   "nuage-subnet", "quotas",
+                                   "nuage-subnet", "quotas", "provider",
                                    "extraroute", "security-group"]
 
     binding_view = "extension:port_binding:view"
@@ -378,8 +379,49 @@ class NuagePlugin(db_base_plugin_v2.NeutronDbPluginV2,
         subnets = self.get_subnets(context, filters=filters)
         return bool(routers or subnets)
 
+    def _extend_network_dict_provider(self, context, network):
+        binding = nuagedb.get_network_binding(context.session, network['id'])
+        if binding:
+            network[pnet.NETWORK_TYPE] = binding.network_type
+            network[pnet.PHYSICAL_NETWORK] = binding.physical_network
+            network[pnet.SEGMENTATION_ID] = binding.vlan_id
+
+    def _process_provider_create(self, context, attrs):
+        network_type = attrs.get(pnet.NETWORK_TYPE)
+        physical_network = attrs.get(pnet.PHYSICAL_NETWORK)
+        segmentation_id = attrs.get(pnet.SEGMENTATION_ID)
+
+        network_type_set = attributes.is_attr_set(network_type)
+        physical_network_set = attributes.is_attr_set(physical_network)
+        segmentation_id_set = attributes.is_attr_set(segmentation_id)
+
+        if not (network_type_set or physical_network_set or
+                segmentation_id_set):
+            return None, None, None
+        if not network_type_set:
+            msg = _("provider:network_type required")
+            raise n_exc.InvalidInput(error_message=msg)
+        elif network_type != 'vlan':
+            msg = (_("provider:network_type %s not supported in VSP")
+                   % network_type)
+            raise nuage_exc.NuageBadRequest(msg=msg)
+        if not physical_network_set:
+            msg = _("provider:physical_network required")
+            raise nuage_exc.NuageBadRequest(msg=msg)
+        if not segmentation_id_set:
+            msg = _("provider:segmentation_id required")
+            raise nuage_exc.NuageBadRequest(msg=msg)
+
+        self.nuageclient.validate_provider_network(network_type,
+                                                   physical_network,
+                                                   segmentation_id)
+
+        return network_type, physical_network, segmentation_id
+
     def create_network(self, context, network):
-        net = network['network']
+        (network_type, physical_network,
+         vlan_id) = self._process_provider_create(context,
+                                                  network['network'])
         with context.session.begin(subtransactions=True):
             self._ensure_default_security_group(
                 context,
@@ -388,6 +430,11 @@ class NuagePlugin(db_base_plugin_v2.NeutronDbPluginV2,
             net = super(NuagePlugin, self).create_network(context,
                                                           network)
             self._process_l3_create(context, net, network['network'])
+            if network_type == 'vlan':
+                nuagedb.add_network_binding(context.session, net['id'],
+                                            network_type,
+                                            physical_network, vlan_id)
+            self._extend_network_dict_provider(context, net)
         return net
 
     def _validate_update_network(self, context, id, network):
@@ -413,7 +460,25 @@ class NuagePlugin(db_base_plugin_v2.NeutronDbPluginV2,
                     raise n_exc.NetworkInUse(net_id=id)
         return (is_external_set, subnet)
 
+    def get_network(self, context, net_id, fields=None):
+        net = super(NuagePlugin, self).get_network(context,
+                                                   net_id,
+                                                   None)
+        self._extend_network_dict_provider(context, net)
+        return self._fields(net, fields)
+
+    def get_networks(self, context, filters=None, fields=None,
+                     sorts=None, limit=None, marker=None, page_reverse=False):
+        nets = super(NuagePlugin,
+                     self).get_networks(context, filters, None, sorts,
+                                        limit, marker, page_reverse)
+        for net in nets:
+            self._extend_network_dict_provider(context, net)
+
+        return [self._fields(net, fields) for net in nets]
+
     def update_network(self, context, id, network):
+        pnet._raise_if_updates_provider_attributes(network['network'])
         with context.session.begin(subtransactions=True):
             is_external_set, subnet = self._validate_update_network(context,
                                                                     id,
@@ -477,6 +542,14 @@ class NuagePlugin(db_base_plugin_v2.NeutronDbPluginV2,
             msg = "no-gateway option not supported with subnets"
             raise nuage_exc.OperationNotSupported(msg=msg)
 
+    def _validate_create_provider_subnet(self, context, net_id):
+        net_filter = {'network_id': [net_id]}
+        existing_subn = self.get_subnets(context, filters=net_filter)
+        if len(existing_subn) > 0:
+            msg = _('Only one subnet is allowed per '
+                    'Provider network %s') % net_id
+            raise nuage_exc.OperationNotSupported(msg=msg)
+
     def _delete_nuage_sharedresource(self, net_id):
         self.nuageclient.delete_nuage_sharedresource(net_id)
 
@@ -508,14 +581,15 @@ class NuagePlugin(db_base_plugin_v2.NeutronDbPluginV2,
             self._add_nuage_sharedresource(subn, net_id, type)
             return subn
 
-    def _create_nuage_subnet(self, context, neutron_subnet,
-                             netpart_id, l2dom_template_id):
+    def _create_nuage_subnet(self, context, neutron_subnet, netpart_id,
+                             l2dom_template_id, pnet_binding):
         net = netaddr.IPNetwork(neutron_subnet['cidr'])
         params = {
             'netpart_id': netpart_id,
             'tenant_id': neutron_subnet['tenant_id'],
             'net': net,
-            'l2dom_tmplt_id': l2dom_template_id
+            'l2dom_tmplt_id': l2dom_template_id,
+            'pnet_binding': pnet_binding
         }
         try:
             nuage_subnet = self.nuageclient.create_subnet(neutron_subnet,
@@ -547,6 +621,9 @@ class NuagePlugin(db_base_plugin_v2.NeutronDbPluginV2,
         if self._network_is_external(context, net_id):
             return self._create_nuage_sharedresource(
                 context, subnet, constants.SR_TYPE_FLOATING)
+        pnet_binding = nuagedb.get_network_binding(context.session, net_id)
+        if pnet_binding:
+            self._validate_create_provider_subnet(context, net_id)
 
         self._validate_create_subnet(subn)
 
@@ -554,7 +631,8 @@ class NuagePlugin(db_base_plugin_v2.NeutronDbPluginV2,
         neutron_subnet = super(NuagePlugin, self).create_subnet(context,
                                                                 subnet)
         self._create_nuage_subnet(context, neutron_subnet, net_partition['id'],
-                                  subn['nuage_subnet_template'])
+                                  subn['nuage_subnet_template'],
+                                  pnet_binding)
         return neutron_subnet
 
     def delete_subnet(self, context, id):
@@ -637,13 +715,17 @@ class NuagePlugin(db_base_plugin_v2.NeutronDbPluginV2,
             self.nuageclient.delete_subnet(nuage_subnet_id,
                                            nuage_l2dom_tmplt_id)
             net = netaddr.IPNetwork(subn['cidr'])
+            pnet_binding = nuagedb.get_network_binding(context.session,
+                                                       subn['network_id'])
             params = {
                 'net': net,
                 'zone_id': nuage_zone['nuage_zone_id'],
-                'neutron_subnet_id': subnet_id
+                'neutron_subnet_id': subnet_id,
+                'pnet_binding': pnet_binding
             }
             if not attributes.is_attr_set(subn['gateway_ip']):
                 subn['gateway_ip'] = str(netaddr.IPAddress(net.first + 1))
+
             try:
                 nuage_subnet = self.nuageclient.create_domain_subnet(subn,
                                                                    params)
@@ -723,14 +805,17 @@ class NuagePlugin(db_base_plugin_v2.NeutronDbPluginV2,
 
             net = netaddr.IPNetwork(neutron_subnet['cidr'])
             netpart_id = ent_rtr_mapping['net_partition_id']
+            pnet_binding = nuagedb.get_network_binding(
+                context.session, neutron_subnet['network_id'])
             params = {
                 'tenant_id': neutron_subnet['tenant_id'],
                 'net': net,
-                'netpart_id': netpart_id
+                'netpart_id': netpart_id,
+                'nuage_subn_id': nuage_subn_id,
+                'neutron_subnet': neutron_subnet,
+                'pnet_binding': pnet_binding
             }
-            nuage_subnet = self.nuageclient.create_subnet(neutron_subnet,
-                                                          params)
-            self.nuageclient.delete_domain_subnet(nuage_subn_id)
+            nuage_subnet = self.nuageclient.remove_router_interface(params)
             info = super(NuagePlugin,
                          self).remove_router_interface(context, router_id,
                                                        interface_info)
index bbfecd487bd0c153577a7fef177fdf5fe6021ebe..6ccb76244e398bd423a18f48ed5ea4cc22826cdf 100644 (file)
@@ -185,3 +185,9 @@ class FakeNuageClient(object):
 
     def delete_port_security_group_bindings(self, params):
         pass
+
+    def validate_provider_network(self, net_type, phy_net, vlan_id):
+        pass
+
+    def remove_router_interface(self, params):
+        pass
index d79933b130d41863d1259b3167415627544ba01c..cdb562396f57bf72dda00a71e4bd38899a45a44d 100644 (file)
@@ -22,9 +22,11 @@ import mock
 from oslo.config import cfg
 from webob import exc
 
+from neutron import context
 from neutron.extensions import external_net
 from neutron.extensions import l3
 from neutron.extensions import portbindings
+from neutron.extensions import providernet as pnet
 from neutron.openstack.common import uuidutils
 from neutron.plugins.nuage import extensions
 from neutron.plugins.nuage.extensions import nuage_router
@@ -348,3 +350,32 @@ class TestNuageExtrarouteTestCase(NuagePluginV2TestCase,
 class TestNuageSecurityGroupTestCase(NuagePluginV2TestCase,
                                      test_sg.TestSecurityGroups):
     pass
+
+
+class TestNuageProviderNetTestCase(NuagePluginV2TestCase):
+
+    def test_create_provider_network(self):
+        phy_net = uuidutils.generate_uuid()
+        data = {'network': {'name': 'pnet1',
+                            'tenant_id': 'admin',
+                            pnet.NETWORK_TYPE: 'vlan',
+                            pnet.PHYSICAL_NETWORK: phy_net,
+                            pnet.SEGMENTATION_ID: 123}}
+        network_req = self.new_create_request('networks', data, self.fmt)
+        net = self.deserialize(self.fmt, network_req.get_response(self.api))
+        self.assertEqual('vlan', net['network'][pnet.NETWORK_TYPE])
+        self.assertEqual(phy_net, net['network'][pnet.PHYSICAL_NETWORK])
+        self.assertEqual(123, net['network'][pnet.SEGMENTATION_ID])
+
+    def test_create_provider_network_no_admin(self):
+        phy_net = uuidutils.generate_uuid()
+        data = {'network': {'name': 'pnet1',
+                            'tenant_id': 'no_admin',
+                            pnet.NETWORK_TYPE: 'vlan',
+                            pnet.PHYSICAL_NETWORK: phy_net,
+                            pnet.SEGMENTATION_ID: 123}}
+        network_req = self.new_create_request('networks', data, self.fmt)
+        network_req.environ['neutron.context'] = context.Context(
+                                    '', 'no_admin', is_admin=False)
+        res = network_req.get_response(self.api)
+        self.assertEqual(exc.HTTPForbidden.code, res.status_int)