From: Manish Godara Date: Thu, 22 Jan 2015 00:24:15 +0000 (-0800) Subject: Add filter for provider network attributes in ML2 X-Git-Url: https://review.fuel-infra.org/gitweb?a=commitdiff_plain;h=d7d2f1daedbafa7952185d14a0df584c03a6cf06;p=openstack-build%2Fneutron-build.git Add filter for provider network attributes in ML2 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 --- diff --git a/neutron/plugins/ml2/managers.py b/neutron/plugins/ml2/managers.py index 408d22a95..15a7eace8 100644 --- a/neutron/plugins/ml2/managers.py +++ b/neutron/plugins/ml2/managers.py @@ -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: diff --git a/neutron/plugins/ml2/plugin.py b/neutron/plugins/ml2/plugin.py index 85b87c76a..9f31a8b6e 100644 --- a/neutron/plugins/ml2/plugin.py +++ b/neutron/plugins/ml2/plugin.py @@ -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: diff --git a/neutron/tests/unit/ml2/test_ml2_plugin.py b/neutron/tests/unit/ml2/test_ml2_plugin.py index 33d7b0d49..afeb640dd 100644 --- a/neutron/tests/unit/ml2/test_ml2_plugin.py +++ b/neutron/tests/unit/ml2/test_ml2_plugin.py @@ -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):