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
This commit is contained in:
reedip 2016-04-12 12:51:30 +09:00 committed by Carl Baldwin
parent 451193c850
commit 6cb0c49857
4 changed files with 92 additions and 1 deletions

View File

@ -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 {}

View File

@ -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(

View File

@ -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))

View File

@ -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.