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:
parent
451193c850
commit
6cb0c49857
58
neutron/extensions/l2_adjacency.py
Normal file
58
neutron/extensions/l2_adjacency.py
Normal 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 {}
|
@ -20,11 +20,22 @@ from neutron.api.v2 import attributes
|
|||||||
from neutron.db import common_db_mixin
|
from neutron.db import common_db_mixin
|
||||||
from neutron.db import models_v2
|
from neutron.db import models_v2
|
||||||
from neutron.extensions import ip_allocation
|
from neutron.extensions import ip_allocation
|
||||||
|
from neutron.extensions import l2_adjacency
|
||||||
from neutron.extensions import segment
|
from neutron.extensions import segment
|
||||||
from neutron import manager
|
from neutron import manager
|
||||||
from neutron.services.segments import db
|
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):
|
def _extend_subnet_dict_binding(plugin, subnet_res, subnet_db):
|
||||||
subnet_res['segment_id'] = subnet_db.get('segment_id')
|
subnet_res['segment_id'] = subnet_db.get('segment_id')
|
||||||
|
|
||||||
@ -50,9 +61,11 @@ class Plugin(db.SegmentDbMixin, segment.SegmentPluginBase):
|
|||||||
|
|
||||||
_instance = None
|
_instance = None
|
||||||
|
|
||||||
supported_extension_aliases = ["segment", "ip_allocation"]
|
supported_extension_aliases = ["segment", "ip_allocation", "l2_adjacency"]
|
||||||
|
|
||||||
def __init__(self):
|
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(
|
common_db_mixin.CommonDbMixin.register_dict_extend_funcs(
|
||||||
attributes.SUBNETS, [_extend_subnet_dict_binding])
|
attributes.SUBNETS, [_extend_subnet_dict_binding])
|
||||||
common_db_mixin.CommonDbMixin.register_dict_extend_funcs(
|
common_db_mixin.CommonDbMixin.register_dict_extend_funcs(
|
||||||
|
@ -30,6 +30,7 @@ from neutron.db import db_base_plugin_v2
|
|||||||
from neutron.db import portbindings_db
|
from neutron.db import portbindings_db
|
||||||
from neutron.db import segments_db
|
from neutron.db import segments_db
|
||||||
from neutron.extensions import ip_allocation
|
from neutron.extensions import ip_allocation
|
||||||
|
from neutron.extensions import l2_adjacency
|
||||||
from neutron.extensions import portbindings
|
from neutron.extensions import portbindings
|
||||||
from neutron.extensions import segment as ext_segment
|
from neutron.extensions import segment as ext_segment
|
||||||
from neutron.plugins.common import constants as p_constants
|
from neutron.plugins.common import constants as p_constants
|
||||||
@ -670,6 +671,8 @@ class TestSegmentAwareIpam(SegmentTestCase):
|
|||||||
segment_id=segment['segment']['id'],
|
segment_id=segment['segment']['id'],
|
||||||
ip_version=ip_version,
|
ip_version=ip_version,
|
||||||
cidr=cidr) as subnet:
|
cidr=cidr) as subnet:
|
||||||
|
self._validate_l2_adjacency(network['network']['id'],
|
||||||
|
is_adjacent=False)
|
||||||
return network, segment, subnet
|
return network, segment, subnet
|
||||||
|
|
||||||
def _create_test_segments_with_subnets(self, num):
|
def _create_test_segments_with_subnets(self, num):
|
||||||
@ -753,6 +756,8 @@ class TestSegmentAwareIpam(SegmentTestCase):
|
|||||||
network_id=network['network']['id'],
|
network_id=network['network']['id'],
|
||||||
physical_network='physnet')
|
physical_network='physnet')
|
||||||
|
|
||||||
|
self._validate_l2_adjacency(network['network']['id'], is_adjacent=True)
|
||||||
|
|
||||||
# Map the host to the segment
|
# Map the host to the segment
|
||||||
self._setup_host_mappings([(segment['segment']['id'], 'fakehost')])
|
self._setup_host_mappings([(segment['segment']['id'], 'fakehost')])
|
||||||
|
|
||||||
@ -877,6 +882,8 @@ class TestSegmentAwareIpam(SegmentTestCase):
|
|||||||
with self.subnet(network=network,
|
with self.subnet(network=network,
|
||||||
segment_id=segment['segment']['id']) as subnet:
|
segment_id=segment['segment']['id']) as subnet:
|
||||||
self._validate_deferred_ip_allocation(port['port']['id'])
|
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)
|
# Try requesting an IP (but the only subnet is on a segment)
|
||||||
data = {'port': {
|
data = {'port': {
|
||||||
'fixed_ips': [{'subnet_id': subnet['subnet']['id']}]}}
|
'fixed_ips': [{'subnet_id': subnet['subnet']['id']}]}}
|
||||||
@ -888,6 +895,12 @@ class TestSegmentAwareIpam(SegmentTestCase):
|
|||||||
self.assertEqual(webob.exc.HTTPOk.code, response.status_int)
|
self.assertEqual(webob.exc.HTTPOk.code, response.status_int)
|
||||||
self._assert_one_ip_in_subnet(response, subnet['subnet']['cidr'])
|
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):
|
def _validate_deferred_ip_allocation(self, port_id):
|
||||||
request = self.new_show_request('ports', port_id)
|
request = self.new_show_request('ports', port_id)
|
||||||
response = self.deserialize(self.fmt, request.get_response(self.api))
|
response = self.deserialize(self.fmt, request.get_response(self.api))
|
||||||
|
7
releasenotes/notes/l2_adjacency-e6e54e5ff9aad9b7.yaml
Normal file
7
releasenotes/notes/l2_adjacency-e6e54e5ff9aad9b7.yaml
Normal 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.
|
Loading…
Reference in New Issue
Block a user