From 6cb0c498578d08c4fea29f08c96a864b3c9cc1dd Mon Sep 17 00:00:00 2001 From: reedip Date: Tue, 12 Apr 2016 12:51:30 +0900 Subject: [PATCH] L2-Adjacency support The following patch adds the support for L2-Adjacency to indicate if there is L2 adjacency between the ports on a network. Partially-Implements: blueprint routed-networks Change-Id: Id2d4331568886bee52e78e1c138f1475cc89342b --- neutron/extensions/l2_adjacency.py | 58 +++++++++++++++++++ neutron/services/segments/plugin.py | 15 ++++- neutron/tests/unit/extensions/test_segment.py | 13 +++++ .../notes/l2_adjacency-e6e54e5ff9aad9b7.yaml | 7 +++ 4 files changed, 92 insertions(+), 1 deletion(-) create mode 100644 neutron/extensions/l2_adjacency.py create mode 100644 releasenotes/notes/l2_adjacency-e6e54e5ff9aad9b7.yaml diff --git a/neutron/extensions/l2_adjacency.py b/neutron/extensions/l2_adjacency.py new file mode 100644 index 00000000000..b02be5444ab --- /dev/null +++ b/neutron/extensions/l2_adjacency.py @@ -0,0 +1,58 @@ +# Copyright (c) 2016 NEC Technologies Ltd. +# 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. + +from neutron.api import extensions + + +L2_ADJACENCY = 'l2_adjacency' +EXTENDED_ATTRIBUTES_2_0 = { + 'networks': { + L2_ADJACENCY: {'allow_post': False, + 'allow_put': False, + 'is_visible': True} + } +} + + +class L2_adjacency(extensions.ExtensionDescriptor): + """Extension class supporting L2 Adjacency for Routed Networks + + The following class is used by neutron's extension framework + to provide metadata related to the L2 Adjacency for Neutron + Routed Network, exposing the same to clients. + No new resources have been defined by this extension. + """ + + @classmethod + def get_name(cls): + return "L2 Adjacency" + + @classmethod + def get_alias(cls): + return "l2_adjacency" + + @classmethod + def get_description(cls): + return "Display L2 Adjacency for Neutron Networks." + + @classmethod + def get_updated(cls): + return "2016-04-12T16:00:00-00:00" + + def get_extended_resources(self, version): + if version == "2.0": + return EXTENDED_ATTRIBUTES_2_0 + else: + return {} diff --git a/neutron/services/segments/plugin.py b/neutron/services/segments/plugin.py index da51a2cac59..7dcc77c12f6 100644 --- a/neutron/services/segments/plugin.py +++ b/neutron/services/segments/plugin.py @@ -20,11 +20,22 @@ from neutron.api.v2 import attributes from neutron.db import common_db_mixin from neutron.db import models_v2 from neutron.extensions import ip_allocation +from neutron.extensions import l2_adjacency from neutron.extensions import segment from neutron import manager from neutron.services.segments import db +def _extend_network_dict_binding(plugin, network_res, network_db): + if not manager.NeutronManager.get_service_plugins().get('segments'): + return + + # TODO(carl_baldwin) Make this work with service subnets when it's a thing. + is_adjacent = (not network_db.subnets + or not network_db.subnets[0].segment_id) + network_res[l2_adjacency.L2_ADJACENCY] = is_adjacent + + def _extend_subnet_dict_binding(plugin, subnet_res, subnet_db): subnet_res['segment_id'] = subnet_db.get('segment_id') @@ -50,9 +61,11 @@ class Plugin(db.SegmentDbMixin, segment.SegmentPluginBase): _instance = None - supported_extension_aliases = ["segment", "ip_allocation"] + supported_extension_aliases = ["segment", "ip_allocation", "l2_adjacency"] def __init__(self): + common_db_mixin.CommonDbMixin.register_dict_extend_funcs( + attributes.NETWORKS, [_extend_network_dict_binding]) common_db_mixin.CommonDbMixin.register_dict_extend_funcs( attributes.SUBNETS, [_extend_subnet_dict_binding]) common_db_mixin.CommonDbMixin.register_dict_extend_funcs( diff --git a/neutron/tests/unit/extensions/test_segment.py b/neutron/tests/unit/extensions/test_segment.py index 4d4fbc672ee..30065a19a4a 100644 --- a/neutron/tests/unit/extensions/test_segment.py +++ b/neutron/tests/unit/extensions/test_segment.py @@ -30,6 +30,7 @@ from neutron.db import db_base_plugin_v2 from neutron.db import portbindings_db from neutron.db import segments_db from neutron.extensions import ip_allocation +from neutron.extensions import l2_adjacency from neutron.extensions import portbindings from neutron.extensions import segment as ext_segment from neutron.plugins.common import constants as p_constants @@ -670,6 +671,8 @@ class TestSegmentAwareIpam(SegmentTestCase): segment_id=segment['segment']['id'], ip_version=ip_version, cidr=cidr) as subnet: + self._validate_l2_adjacency(network['network']['id'], + is_adjacent=False) return network, segment, subnet def _create_test_segments_with_subnets(self, num): @@ -753,6 +756,8 @@ class TestSegmentAwareIpam(SegmentTestCase): network_id=network['network']['id'], physical_network='physnet') + self._validate_l2_adjacency(network['network']['id'], is_adjacent=True) + # Map the host to the segment self._setup_host_mappings([(segment['segment']['id'], 'fakehost')]) @@ -877,6 +882,8 @@ class TestSegmentAwareIpam(SegmentTestCase): with self.subnet(network=network, segment_id=segment['segment']['id']) as subnet: self._validate_deferred_ip_allocation(port['port']['id']) + self._validate_l2_adjacency(network['network']['id'], + is_adjacent=False) # Try requesting an IP (but the only subnet is on a segment) data = {'port': { 'fixed_ips': [{'subnet_id': subnet['subnet']['id']}]}} @@ -888,6 +895,12 @@ class TestSegmentAwareIpam(SegmentTestCase): self.assertEqual(webob.exc.HTTPOk.code, response.status_int) self._assert_one_ip_in_subnet(response, subnet['subnet']['cidr']) + def _validate_l2_adjacency(self, network_id, is_adjacent): + request = self.new_show_request('networks', network_id) + response = self.deserialize(self.fmt, request.get_response(self.api)) + self.assertEqual(is_adjacent, + response['network'][l2_adjacency.L2_ADJACENCY]) + def _validate_deferred_ip_allocation(self, port_id): request = self.new_show_request('ports', port_id) response = self.deserialize(self.fmt, request.get_response(self.api)) diff --git a/releasenotes/notes/l2_adjacency-e6e54e5ff9aad9b7.yaml b/releasenotes/notes/l2_adjacency-e6e54e5ff9aad9b7.yaml new file mode 100644 index 00000000000..a490c91c69f --- /dev/null +++ b/releasenotes/notes/l2_adjacency-e6e54e5ff9aad9b7.yaml @@ -0,0 +1,7 @@ +--- +features: + - | + The new l2_adjacency extension adds an l2_adjacency field to the network, + to indicate whether or not there is guaranteed L2 adjacency between the + ports on that Network. Routed network implementations would typically set + l2_adjacency to False.