]> review.fuel-infra Code Review - openstack-build/neutron-build.git/commitdiff
Add support for the multiprovider API to ML2
authorKyle Mestery <kmestery@cisco.com>
Fri, 23 Aug 2013 09:03:49 +0000 (09:03 +0000)
committerKyle Mestery <kmestery@cisco.com>
Wed, 4 Sep 2013 14:53:10 +0000 (14:53 +0000)
This implements support for creating provider networks with
multiple different segments with the ML2 plugin.

Implements: blueprint ml2-multi-segment-api

Change-Id: Id6fc22841fddabfbe685de9605d4a4682ee1102d

neutron/plugins/ml2/plugin.py
neutron/tests/unit/ml2/test_ml2_plugin.py

index 892685ef5c7bbb6c172fb12d5d85cfdc98ee088b..9d9b01f67113cb09711d1d43cc0eab649578a2c1 100644 (file)
@@ -29,6 +29,7 @@ from neutron.db import l3_gwmode_db
 from neutron.db import models_v2
 from neutron.db import quota_db  # noqa
 from neutron.db import securitygroups_rpc_base as sg_db_rpc
+from neutron.extensions import multiprovidernet as mpnet
 from neutron.extensions import portbindings
 from neutron.extensions import providernet as provider
 from neutron.openstack.common import excutils
@@ -77,7 +78,8 @@ class Ml2Plugin(db_base_plugin_v2.NeutronDbPluginV2,
     _supported_extension_aliases = ["provider", "router", "extraroute",
                                     "binding", "quotas", "security-group",
                                     "agent", "l3_agent_scheduler",
-                                    "dhcp_agent_scheduler", "ext-gw-mode"]
+                                    "dhcp_agent_scheduler", "ext-gw-mode",
+                                    "multi-provider"]
 
     @property
     def supported_extension_aliases(self):
@@ -123,25 +125,48 @@ class Ml2Plugin(db_base_plugin_v2.NeutronDbPluginV2,
                                   fanout=False)
         self.conn.consume_in_thread()
 
-    def _process_provider_create(self, context, attrs):
-        network_type = self._get_attribute(attrs, provider.NETWORK_TYPE)
-        physical_network = self._get_attribute(attrs,
+    def _process_provider_segment(self, segment):
+        network_type = self._get_attribute(segment, provider.NETWORK_TYPE)
+        physical_network = self._get_attribute(segment,
                                                provider.PHYSICAL_NETWORK)
-        segmentation_id = self._get_attribute(attrs, provider.SEGMENTATION_ID)
+        segmentation_id = self._get_attribute(segment,
+                                              provider.SEGMENTATION_ID)
 
         if attributes.is_attr_set(network_type):
             segment = {api.NETWORK_TYPE: network_type,
                        api.PHYSICAL_NETWORK: physical_network,
                        api.SEGMENTATION_ID: segmentation_id}
             self.type_manager.validate_provider_segment(segment)
-
             return segment
 
-        if (attributes.is_attr_set(attrs.get(provider.PHYSICAL_NETWORK)) or
-            attributes.is_attr_set(attrs.get(provider.SEGMENTATION_ID))):
-            msg = _("network_type required if other provider attributes "
-                    "specified")
-            raise exc.InvalidInput(error_message=msg)
+        msg = _("network_type required")
+        raise exc.InvalidInput(error_message=msg)
+
+    def _process_provider_create(self, network):
+        segments = []
+
+        if any(attributes.is_attr_set(network.get(f))
+               for f in (provider.NETWORK_TYPE, provider.PHYSICAL_NETWORK,
+                         provider.SEGMENTATION_ID)):
+            # Verify that multiprovider and provider attributes are not set
+            # at the same time.
+            if attributes.is_attr_set(network.get(mpnet.SEGMENTS)):
+                raise mpnet.SegmentsSetInConjunctionWithProviders()
+
+            network_type = self._get_attribute(network, provider.NETWORK_TYPE)
+            physical_network = self._get_attribute(network,
+                                                   provider.PHYSICAL_NETWORK)
+            segmentation_id = self._get_attribute(network,
+                                                  provider.SEGMENTATION_ID)
+            segments = [{provider.NETWORK_TYPE: network_type,
+                         provider.PHYSICAL_NETWORK: physical_network,
+                         provider.SEGMENTATION_ID: segmentation_id}]
+        elif attributes.is_attr_set(network.get(mpnet.SEGMENTS)):
+            segments = network[mpnet.SEGMENTS]
+        else:
+            return
+
+        return [self._process_provider_segment(s) for s in segments]
 
     def _get_attribute(self, attrs, key):
         value = attrs.get(key)
@@ -158,9 +183,11 @@ class Ml2Plugin(db_base_plugin_v2.NeutronDbPluginV2,
             network[provider.PHYSICAL_NETWORK] = None
             network[provider.SEGMENTATION_ID] = None
         elif len(segments) > 1:
-            network[provider.NETWORK_TYPE] = TYPE_MULTI_SEGMENT
-            network[provider.PHYSICAL_NETWORK] = None
-            network[provider.SEGMENTATION_ID] = None
+            network[mpnet.SEGMENTS] = [
+                {provider.NETWORK_TYPE: segment[api.NETWORK_TYPE],
+                 provider.PHYSICAL_NETWORK: segment[api.PHYSICAL_NETWORK],
+                 provider.SEGMENTATION_ID: segment[api.SEGMENTATION_ID]}
+                for segment in segments]
         else:
             segment = segments[0]
             network[provider.NETWORK_TYPE] = segment[api.NETWORK_TYPE]
@@ -260,23 +287,26 @@ class Ml2Plugin(db_base_plugin_v2.NeutronDbPluginV2,
     # TODO(apech): Need to override bulk operations
 
     def create_network(self, context, network):
-        attrs = network['network']
-        segment = self._process_provider_create(context, attrs)
-        tenant_id = self._get_tenant_id_for_create(context, attrs)
+        net_data = network['network']
+        segments = self._process_provider_create(net_data)
+        tenant_id = self._get_tenant_id_for_create(context, net_data)
 
         session = context.session
         with session.begin(subtransactions=True):
             self._ensure_default_security_group(context, tenant_id)
-            if segment:
-                self.type_manager.reserve_provider_segment(session, segment)
-            else:
-                segment = self.type_manager.allocate_tenant_segment(session)
             result = super(Ml2Plugin, self).create_network(context, network)
-            id = result['id']
-            self._process_l3_create(context, result, attrs)
+            network_id = result['id']
+            self._process_l3_create(context, result, net_data)
             # REVISIT(rkukura): Consider moving all segment management
             # to TypeManager.
-            db.add_network_segment(session, id, segment)
+            if segments:
+                for segment in segments:
+                    self.type_manager.reserve_provider_segment(session,
+                                                               segment)
+                    db.add_network_segment(session, network_id, segment)
+            else:
+                segment = self.type_manager.allocate_tenant_segment(session)
+                db.add_network_segment(session, network_id, segment)
             self._extend_network_dict_provider(context, result)
             mech_context = driver_context.NetworkContext(self, context,
                                                          result)
index 202230d416c12838cecb38987c275e00fe072e76..e93bcb328e7ae26bc5cba0bc5d9395be918c9c53 100644 (file)
 #    License for the specific language governing permissions and limitations
 #    under the License.
 
+from neutron.extensions import multiprovidernet as mpnet
 from neutron.extensions import portbindings
-from neutron.plugins.ml2 import config as config
+from neutron.extensions import providernet as pnet
+from neutron.plugins.ml2 import config
 from neutron.tests.unit import _test_extension_portbindings as test_bindings
 from neutron.tests.unit import test_db_plugin as test_plugin
 from neutron.tests.unit import test_extension_ext_gw_mode
@@ -34,7 +36,12 @@ class Ml2PluginV2TestCase(test_plugin.NeutronDbPluginV2TestCase):
         # driver apis.
         config.cfg.CONF.set_override('mechanism_drivers',
                                      ['logger', 'test'],
-                                     'ml2')
+                                     group='ml2')
+        self.physnet = 'physnet1'
+        self.vlan_range = '1:100'
+        self.phys_vrange = ':'.join([self.physnet, self.vlan_range])
+        config.cfg.CONF.set_override('network_vlan_ranges', [self.phys_vrange],
+                                     group='ml2_type_vlan')
         self.addCleanup(config.cfg.CONF.reset)
         super(Ml2PluginV2TestCase, self).setUp(PLUGIN_NAME)
         self.port_create_status = 'DOWN'
@@ -89,3 +96,102 @@ class TestMl2PortBindingHost(Ml2PluginV2TestCase,
 class TestMl2ExtGwModeSupport(Ml2PluginV2TestCase,
                               test_extension_ext_gw_mode.ExtGwModeTestCase):
     pass
+
+
+class TestMultiSegmentNetworks(Ml2PluginV2TestCase):
+
+    def setUp(self, plugin=None):
+        super(TestMultiSegmentNetworks, 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_multiprovider(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))
+        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'])
+
+        # Tests get_network()
+        net_req = self.new_show_request('networks', network['network']['id'])
+        network = self.deserialize(self.fmt, net_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_multiprovider(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: 2}],
+                            '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)