]> review.fuel-infra Code Review - openstack-build/neutron-build.git/commitdiff
Add filter for provider network attributes in ML2
authorManish Godara <manishg@yahoo-inc.com>
Thu, 22 Jan 2015 00:24:15 +0000 (16:24 -0800)
committerManish Godara <manishg@yahoo-inc.com>
Fri, 27 Feb 2015 17:44:57 +0000 (09:44 -0800)
Added code to implement filters for provider network attributes
in ML2 plugin. There was a todo item in code but never got done.
It is needed to lookup networks based on provider network attributes.

Change-Id: I9eaa8bd8fa35cfe2b94551f9c27336503f118ce6
Closes-Bug: 1333475

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

index 408d22a95d4aef85b8a6dcea3340243e683f0e07..15a7eace8360275ff1bba2c52b928a7921a25bf9 100644 (file)
@@ -96,7 +96,8 @@ class TypeManager(stevedore.named.NamedExtensionManager):
             # at the same time.
             if attributes.is_attr_set(network.get(mpnet.SEGMENTS)):
                 raise mpnet.SegmentsSetInConjunctionWithProviders()
-            return [self._process_provider_segment(network)]
+            segment = self._get_provider_segment(network)
+            return [self._process_provider_segment(segment)]
         elif attributes.is_attr_set(network.get(mpnet.SEGMENTS)):
             segments = [self._process_provider_segment(s)
                         for s in network[mpnet.SEGMENTS]]
@@ -105,6 +106,31 @@ class TypeManager(stevedore.named.NamedExtensionManager):
                 self.is_partial_segment)
             return segments
 
+    def _match_segment(self, segment, filters):
+        return all(not filters.get(attr) or segment.get(attr) in filters[attr]
+                   for attr in provider.ATTRIBUTES)
+
+    def _get_provider_segment(self, network):
+        # TODO(manishg): Placeholder method
+        # Code intended for operating on a provider segment should use
+        # this method to extract the segment, even though currently the
+        # segment attributes are part of the network dictionary. In the
+        # future, network and segment information will be decoupled and
+        # here we will do the job of extracting the segment information.
+        return network
+
+    def network_matches_filters(self, network, filters):
+        if not filters:
+            return True
+        if any(attributes.is_attr_set(network.get(attr))
+               for attr in provider.ATTRIBUTES):
+            segments = [self._get_provider_segment(network)]
+        elif attributes.is_attr_set(network.get(mpnet.SEGMENTS)):
+            segments = self._get_attribute(network, mpnet.SEGMENTS)
+        else:
+            return True
+        return any(self._match_segment(s, filters) for s in segments)
+
     def _get_attribute(self, attrs, key):
         value = attrs.get(key)
         if value is attributes.ATTR_NOT_SPECIFIED:
index 85b87c76af2af059c9c091167f9c9a19ed50965f..9f31a8b6edc09d11b9ec65bdd0d6d03d1c35688f 100644 (file)
@@ -156,9 +156,11 @@ class Ml2Plugin(db_base_plugin_v2.NeutronDbPluginV2,
                                   fanout=False)
         return self.conn.consume_in_threads()
 
-    def _filter_nets_provider(self, context, nets, filters):
-        # TODO(rkukura): Implement filtering.
-        return nets
+    def _filter_nets_provider(self, context, networks, filters):
+        return [network
+                for network in networks
+                if self.type_manager.network_matches_filters(network, filters)
+                ]
 
     def _notify_l3_agent_new_port(self, context, port):
         if not port:
index 33d7b0d4906103d9862ccc4792bb0c426c80c731..afeb640dd9255e5442fa002b1151479ffd125418 100644 (file)
@@ -16,6 +16,7 @@
 import contextlib
 import functools
 import mock
+import six
 import testtools
 import uuid
 import webob
@@ -137,6 +138,38 @@ class TestMl2V2HTTPResponse(test_plugin.TestV2HTTPResponse,
 
 class TestMl2NetworksV2(test_plugin.TestNetworksV2,
                         Ml2PluginV2TestCase):
+    def setUp(self, plugin=None):
+        super(TestMl2NetworksV2, self).setUp()
+        # provider networks
+        self.pnets = [{'name': 'net1',
+                       pnet.NETWORK_TYPE: 'vlan',
+                       pnet.PHYSICAL_NETWORK: 'physnet1',
+                       pnet.SEGMENTATION_ID: 1,
+                       'tenant_id': 'tenant_one'},
+                      {'name': 'net2',
+                       pnet.NETWORK_TYPE: 'vlan',
+                       pnet.PHYSICAL_NETWORK: 'physnet2',
+                       pnet.SEGMENTATION_ID: 210,
+                       'tenant_id': 'tenant_one'},
+                      {'name': 'net3',
+                       pnet.NETWORK_TYPE: 'vlan',
+                       pnet.PHYSICAL_NETWORK: 'physnet2',
+                       pnet.SEGMENTATION_ID: 220,
+                       'tenant_id': 'tenant_one'}
+                      ]
+        # multiprovider networks
+        self.mp_nets = [{'name': 'net4',
+                         mpnet.SEGMENTS:
+                             [{pnet.NETWORK_TYPE: 'vlan',
+                               pnet.PHYSICAL_NETWORK: 'physnet2',
+                               pnet.SEGMENTATION_ID: 1},
+                              {pnet.NETWORK_TYPE: 'vlan',
+                               pnet.PHYSICAL_NETWORK: 'physnet2',
+                               pnet.SEGMENTATION_ID: 202}],
+                         'tenant_id': 'tenant_one'}
+                        ]
+        self.nets = self.mp_nets + self.pnets
+
     def test_port_delete_helper_tolerates_failure(self):
         plugin = manager.NeutronManager.get_plugin()
         with mock.patch.object(plugin, "delete_port",
@@ -149,6 +182,66 @@ class TestMl2NetworksV2(test_plugin.TestNetworksV2,
                                side_effect=exc.SubnetNotFound(subnet_id="1")):
             plugin._delete_subnets(None, [mock.MagicMock()])
 
+    def _create_and_verify_networks(self, networks):
+        for net_idx, net in enumerate(networks):
+            # create
+            req = self.new_create_request('networks',
+                                          {'network': net})
+            # verify
+            network = self.deserialize(self.fmt,
+                                       req.get_response(self.api))['network']
+            if mpnet.SEGMENTS not in net:
+                for k, v in six.iteritems(net):
+                    self.assertEqual(net[k], network[k])
+                    self.assertNotIn(mpnet.SEGMENTS, network)
+            else:
+                segments = network[mpnet.SEGMENTS]
+                expected_segments = net[mpnet.SEGMENTS]
+                self.assertEqual(len(expected_segments), len(segments))
+                for expected, actual in zip(expected_segments, segments):
+                    self.assertEqual(expected, actual)
+
+    def _lookup_network_by_segmentation_id(self, seg_id, num_expected_nets):
+        params_str = "%s=%s" % (pnet.SEGMENTATION_ID, seg_id)
+        net_req = self.new_list_request('networks', None,
+                                        params=params_str)
+        networks = self.deserialize(self.fmt, net_req.get_response(self.api))
+        if num_expected_nets:
+            self.assertIsNotNone(networks)
+            self.assertEqual(num_expected_nets, len(networks['networks']))
+        else:
+            self.assertIsNone(networks)
+        return networks
+
+    def test_list_networks_with_segmentation_id(self):
+        self._create_and_verify_networks(self.pnets)
+        # verify we can find the network that we expect
+        lookup_vlan_id = 1
+        expected_net = [n for n in self.pnets
+                        if n[pnet.SEGMENTATION_ID] == lookup_vlan_id].pop()
+        networks = self._lookup_network_by_segmentation_id(lookup_vlan_id, 1)
+        # verify all provider attributes
+        network = networks['networks'][0]
+        for attr in pnet.ATTRIBUTES:
+            self.assertEqual(expected_net[attr], network[attr])
+
+    def test_list_mpnetworks_with_segmentation_id(self):
+        self._create_and_verify_networks(self.nets)
+
+        # get all networks with seg_id=1 (including multisegment networks)
+        lookup_vlan_id = 1
+        networks = self._lookup_network_by_segmentation_id(lookup_vlan_id, 2)
+
+        # get the mpnet
+        networks = [n for n in networks['networks'] if mpnet.SEGMENTS in n]
+        network = networks.pop()
+        # verify attributes of the looked up item
+        segments = network[mpnet.SEGMENTS]
+        expected_segments = self.mp_nets[0][mpnet.SEGMENTS]
+        self.assertEqual(len(expected_segments), len(segments))
+        for expected, actual in zip(expected_segments, segments):
+            self.assertEqual(expected, actual)
+
 
 class TestMl2SubnetsV2(test_plugin.TestSubnetsV2,
                        Ml2PluginV2TestCase):