diff --git a/neutron/db/address_scope_db.py b/neutron/db/address_scope_db.py index 5152f74b6cb..0ec0bc9458b 100644 --- a/neutron/db/address_scope_db.py +++ b/neutron/db/address_scope_db.py @@ -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']) diff --git a/neutron/db/models_v2.py b/neutron/db/models_v2.py index 03a3a82dc3f..3451902bbe2 100644 --- a/neutron/db/models_v2.py +++ b/neutron/db/models_v2.py @@ -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)) diff --git a/neutron/extensions/address_scope.py b/neutron/extensions/address_scope.py index 392cf16f004..5898078f81e 100644 --- a/neutron/extensions/address_scope.py +++ b/neutron/extensions/address_scope.py @@ -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}, } } diff --git a/neutron/tests/unit/extensions/test_address_scope.py b/neutron/tests/unit/extensions/test_address_scope.py index 1bff73ed1d9..75291d55f6e 100644 --- a/neutron/tests/unit/extensions/test_address_scope.py +++ b/neutron/tests/unit/extensions/test_address_scope.py @@ -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'] diff --git a/releasenotes/notes/correlate-address-scope-with-network-ea16e16b0154ac21.yaml b/releasenotes/notes/correlate-address-scope-with-network-ea16e16b0154ac21.yaml new file mode 100644 index 00000000000..c8888ee26ea --- /dev/null +++ b/releasenotes/notes/correlate-address-scope-with-network-ea16e16b0154ac21.yaml @@ -0,0 +1,4 @@ +--- +features: + - Add derived attributes to the network to tell users which address scopes + the network is in.