Correlate address scope with network

With address scope being enabled, networks now are in one ipv4
address scope and one ipv6 address scope.

This patch adds derived attributes to the network as part of the
address scopes extension that will show related address scopes
when viewing a network through the API.

APIImpact

Change-Id: Ib1657636033ad2c0009d50ebe7c5ae4f72f6b175
Closes-Bug: #1547380
This commit is contained in:
Hong Hui Xiao 2016-02-21 22:18:39 -05:00
parent 643a53da06
commit 635581912f
5 changed files with 88 additions and 0 deletions

View File

@ -18,6 +18,8 @@ from sqlalchemy.orm import exc
from neutron._i18n import _
from neutron.api.v2 import attributes as attr
from neutron.common import constants
from neutron.db import db_base_plugin_v2
from neutron.db import model_base
from neutron.extensions import address_scope as ext_address_scope
@ -124,3 +126,22 @@ class AddressScopeDbMixin(ext_address_scope.AddressScopePluginBase):
raise ext_address_scope.AddressScopeInUse(address_scope_id=id)
address_scope = self._get_address_scope(context, id)
context.session.delete(address_scope)
def _extend_network_dict_address_scope(self, network_res, network_db):
network_res[ext_address_scope.IPV4_ADDRESS_SCOPE] = None
network_res[ext_address_scope.IPV6_ADDRESS_SCOPE] = None
subnetpools = {subnet.subnetpool for subnet in network_db.subnets
if subnet.subnetpool}
for subnetpool in subnetpools:
# A network will be constrained to only one subnetpool per address
# family. Retrieve the address scope of subnetpools as the address
# scopes of network.
as_id = subnetpool[ext_address_scope.ADDRESS_SCOPE_ID]
if subnetpool['ip_version'] == constants.IP_VERSION_4:
network_res[ext_address_scope.IPV4_ADDRESS_SCOPE] = as_id
if subnetpool['ip_version'] == constants.IP_VERSION_6:
network_res[ext_address_scope.IPV6_ADDRESS_SCOPE] = as_id
return network_res
db_base_plugin_v2.NeutronDbPluginV2.register_dict_extend_funcs(
attr.NETWORKS, ['_extend_network_dict_address_scope'])

View File

@ -183,6 +183,13 @@ class Subnet(model_base.HasStandardAttributes, model_base.BASEV2,
name = sa.Column(sa.String(attr.NAME_MAX_LEN))
network_id = sa.Column(sa.String(36), sa.ForeignKey('networks.id'))
subnetpool_id = sa.Column(sa.String(36), index=True)
# NOTE: Explicitly specify join conditions for the relationship because
# subnetpool_id in subnet might be 'prefix_delegation' when the IPv6 Prefix
# Delegation is enabled
subnetpool = orm.relationship(
'SubnetPool', lazy='joined',
foreign_keys='Subnet.subnetpool_id',
primaryjoin='Subnet.subnetpool_id==SubnetPool.id')
ip_version = sa.Column(sa.Integer, nullable=False)
cidr = sa.Column(sa.String(64), nullable=False)
gateway_ip = sa.Column(sa.String(64))

View File

@ -26,6 +26,8 @@ from neutron import manager
ADDRESS_SCOPE = 'address_scope'
ADDRESS_SCOPES = '%ss' % ADDRESS_SCOPE
ADDRESS_SCOPE_ID = 'address_scope_id'
IPV4_ADDRESS_SCOPE = 'ipv4_%s' % ADDRESS_SCOPE
IPV6_ADDRESS_SCOPE = 'ipv6_%s' % ADDRESS_SCOPE
# Attribute Map
RESOURCE_ATTRIBUTE_MAP = {
@ -63,6 +65,14 @@ RESOURCE_ATTRIBUTE_MAP = {
'default': attr.ATTR_NOT_SPECIFIED,
'validate': {'type:uuid_or_none': None},
'is_visible': True}
},
attr.NETWORKS: {
IPV4_ADDRESS_SCOPE: {'allow_post': False,
'allow_put': False,
'is_visible': True},
IPV6_ADDRESS_SCOPE: {'allow_post': False,
'allow_put': False,
'is_visible': True},
}
}

View File

@ -430,6 +430,52 @@ class TestSubnetPoolsWithAddressScopes(AddressScopeTestCase):
def test_not_update_subnetpool_address_scope_not_notify(self):
self._test_update_subnetpool_address_scope_notify(False)
def test_network_create_contain_address_scope_attr(self):
with self.network() as network:
result = self._show('networks', network['network']['id'])
keys = [ext_address_scope.IPV4_ADDRESS_SCOPE,
ext_address_scope.IPV6_ADDRESS_SCOPE]
for k in keys:
# Correlated address scopes should initially be None
self.assertIsNone(result['network'][k])
def test_correlate_network_with_address_scope(self):
with self.address_scope(name='v4-as') as v4_addr_scope, \
self.address_scope(
name='v6-as',
ip_version=constants.IP_VERSION_6) as v6_addr_scope, \
self.network() as network:
v4_as_id = v4_addr_scope['address_scope']['id']
subnet = netaddr.IPNetwork('10.10.10.0/24')
v4_subnetpool = self._test_create_subnetpool(
[subnet.cidr], name='v4-sp',
min_prefixlen='24', address_scope_id=v4_as_id)
v4_subnetpool_id = v4_subnetpool['subnetpool']['id']
v6_as_id = v6_addr_scope['address_scope']['id']
subnet = netaddr.IPNetwork('fd5c:6ee1:c7ae::/64')
v6_subnetpool = self._test_create_subnetpool(
[subnet.cidr], name='v6-sp',
min_prefixlen='64', address_scope_id=v6_as_id)
v6_subnetpool_id = v6_subnetpool['subnetpool']['id']
data = {'subnet': {
'network_id': network['network']['id'],
'subnetpool_id': v4_subnetpool_id,
'ip_version': 4,
'tenant_id': network['network']['tenant_id']}}
req = self.new_create_request('subnets', data)
self.deserialize(self.fmt, req.get_response(self.api))
data['subnet']['subnetpool_id'] = v6_subnetpool_id
data['subnet']['ip_version'] = 6
req = self.new_create_request('subnets', data)
self.deserialize(self.fmt, req.get_response(self.api))
result = self._show('networks', network['network']['id'])
self.assertEqual(
v4_as_id,
result['network'][ext_address_scope.IPV4_ADDRESS_SCOPE])
self.assertEqual(
v6_as_id,
result['network'][ext_address_scope.IPV6_ADDRESS_SCOPE])
def test_delete_address_scope_in_use(self):
with self.address_scope(name='foo-address-scope') as addr_scope:
address_scope_id = addr_scope['address_scope']['id']

View File

@ -0,0 +1,4 @@
---
features:
- Add derived attributes to the network to tell users which address scopes
the network is in.