diff --git a/neutron/api/v2/attributes.py b/neutron/api/v2/attributes.py index 0cf1816a4..83471f946 100644 --- a/neutron/api/v2/attributes.py +++ b/neutron/api/v2/attributes.py @@ -729,11 +729,11 @@ RESOURCE_ATTRIBUTE_MAP = { 'default': True, 'convert_to': convert_to_boolean, 'is_visible': True}, - 'ipv6_ra_mode': {'allow_post': True, 'allow_put': True, + 'ipv6_ra_mode': {'allow_post': True, 'allow_put': False, 'default': ATTR_NOT_SPECIFIED, 'validate': {'type:values': constants.IPV6_MODES}, 'is_visible': True}, - 'ipv6_address_mode': {'allow_post': True, 'allow_put': True, + 'ipv6_address_mode': {'allow_post': True, 'allow_put': False, 'default': ATTR_NOT_SPECIFIED, 'validate': {'type:values': constants.IPV6_MODES}, diff --git a/neutron/db/db_base_plugin_v2.py b/neutron/db/db_base_plugin_v2.py index 155b9f399..9582efed3 100644 --- a/neutron/db/db_base_plugin_v2.py +++ b/neutron/db/db_base_plugin_v2.py @@ -740,24 +740,17 @@ class NeutronDbPluginV2(neutron_plugin_base_v2.NeutronPluginBaseV2, raise n_exc.InvalidSharedSetting(network=original.name) def _validate_ipv6_attributes(self, subnet, cur_subnet): + if cur_subnet: + self._validate_ipv6_update_dhcp(subnet, cur_subnet) + return ra_mode_set = attributes.is_attr_set(subnet.get('ipv6_ra_mode')) address_mode_set = attributes.is_attr_set( subnet.get('ipv6_address_mode')) - if cur_subnet: - ra_mode = (subnet['ipv6_ra_mode'] if ra_mode_set - else cur_subnet['ipv6_ra_mode']) - addr_mode = (subnet['ipv6_address_mode'] if address_mode_set - else cur_subnet['ipv6_address_mode']) - if ra_mode_set or address_mode_set: - # Check that updated subnet ipv6 attributes do not conflict - self._validate_ipv6_combination(ra_mode, addr_mode) - self._validate_ipv6_update_dhcp(subnet, cur_subnet) - else: - self._validate_ipv6_dhcp(ra_mode_set, address_mode_set, - subnet['enable_dhcp']) - if ra_mode_set and address_mode_set: - self._validate_ipv6_combination(subnet['ipv6_ra_mode'], - subnet['ipv6_address_mode']) + self._validate_ipv6_dhcp(ra_mode_set, address_mode_set, + subnet['enable_dhcp']) + if ra_mode_set and address_mode_set: + self._validate_ipv6_combination(subnet['ipv6_ra_mode'], + subnet['ipv6_address_mode']) if address_mode_set: self._validate_eui64_applicable(subnet) diff --git a/neutron/tests/unit/test_db_plugin.py b/neutron/tests/unit/test_db_plugin.py index a391eefcc..f390be5af 100644 --- a/neutron/tests/unit/test_db_plugin.py +++ b/neutron/tests/unit/test_db_plugin.py @@ -15,6 +15,7 @@ import contextlib import copy +import itertools import mock from oslo.config import cfg @@ -3038,15 +3039,48 @@ class TestSubnetsV2(NeutronDbPluginV2TestCase): res = subnet_req.get_response(self.api) self.assertEqual(res.status_int, webob.exc.HTTPClientError.code) - def test_create_subnet_ipv6_attributes(self): - gateway_ip = 'fe80::1' - cidr = 'fe80::/64' + def _test_validate_subnet_ipv6_modes(self, cur_subnet=None, + expect_success=True, **modes): + plugin = manager.NeutronManager.get_plugin() + ctx = context.get_admin_context(load_admin_roles=False) + new_subnet = {'ip_version': 6, + 'cidr': 'fe80::/64', + 'enable_dhcp': True} + for mode, value in modes.items(): + new_subnet[mode] = value + if expect_success: + plugin._validate_subnet(ctx, new_subnet, cur_subnet) + else: + self.assertRaises(n_exc.InvalidInput, plugin._validate_subnet, + ctx, new_subnet, cur_subnet) - for mode in constants.IPV6_MODES: - self._test_create_subnet(gateway_ip=gateway_ip, - cidr=cidr, ip_version=6, - ipv6_ra_mode=mode, - ipv6_address_mode=mode) + def test_create_subnet_ipv6_ra_modes(self): + # Test all RA modes with no address mode specified + for ra_mode in constants.IPV6_MODES: + self._test_validate_subnet_ipv6_modes( + ipv6_ra_mode=ra_mode) + + def test_create_subnet_ipv6_addr_modes(self): + # Test all address modes with no RA mode specified + for addr_mode in constants.IPV6_MODES: + self._test_validate_subnet_ipv6_modes( + ipv6_address_mode=addr_mode) + + def test_create_subnet_ipv6_same_ra_and_addr_modes(self): + # Test all ipv6 modes with ra_mode==addr_mode + for ipv6_mode in constants.IPV6_MODES: + self._test_validate_subnet_ipv6_modes( + ipv6_ra_mode=ipv6_mode, + ipv6_address_mode=ipv6_mode) + + def test_create_subnet_ipv6_different_ra_and_addr_modes(self): + # Test all ipv6 modes with ra_mode!=addr_mode + for ra_mode, addr_mode in itertools.permutations( + constants.IPV6_MODES, 2): + self._test_validate_subnet_ipv6_modes( + expect_success=not (ra_mode and addr_mode), + ipv6_ra_mode=ra_mode, + ipv6_address_mode=addr_mode) def test_create_subnet_ipv6_out_of_cidr_global(self): gateway_ip = '2000::1' @@ -3108,31 +3142,6 @@ class TestSubnetsV2(NeutronDbPluginV2TestCase): self.assertEqual(ctx_manager.exception.code, webob.exc.HTTPClientError.code) - def test_create_subnet_invalid_ipv6_combination(self): - gateway_ip = 'fe80::1' - cidr = 'fe80::/80' - with testlib_api.ExpectedException( - webob.exc.HTTPClientError) as ctx_manager: - self._test_create_subnet(gateway_ip=gateway_ip, - cidr=cidr, ip_version=6, - ipv6_ra_mode='stateful', - ipv6_address_mode='stateless') - self.assertEqual(ctx_manager.exception.code, - webob.exc.HTTPClientError.code) - - def test_create_subnet_ipv6_single_attribute_set(self): - gateway_ip = 'fe80::1' - cidr = 'fe80::/64' - for mode in constants.IPV6_MODES: - self._test_create_subnet(gateway_ip=gateway_ip, - cidr=cidr, ip_version=6, - ipv6_ra_mode=None, - ipv6_address_mode=mode) - self._test_create_subnet(gateway_ip=gateway_ip, - cidr=cidr, ip_version=6, - ipv6_ra_mode=mode, - ipv6_address_mode=None) - def test_create_subnet_ipv6_ra_mode_ip_version_4(self): cidr = '10.0.2.0/24' with testlib_api.ExpectedException( @@ -3312,7 +3321,7 @@ class TestSubnetsV2(NeutronDbPluginV2TestCase): self.assertEqual(res.status_int, webob.exc.HTTPConflict.code) - def test_update_subnet_ipv6_attributes(self): + def test_update_subnet_ipv6_attributes_fails(self): with self.subnet(ip_version=6, cidr='fe80::/64', ipv6_ra_mode=constants.IPV6_SLAAC, ipv6_address_mode=constants.IPV6_SLAAC) as subnet: @@ -3320,16 +3329,13 @@ class TestSubnetsV2(NeutronDbPluginV2TestCase): 'ipv6_address_mode': constants.DHCPV6_STATEFUL}} req = self.new_update_request('subnets', data, subnet['subnet']['id']) - res = self.deserialize(self.fmt, req.get_response(self.api)) - self.assertEqual(res['subnet']['ipv6_ra_mode'], - data['subnet']['ipv6_ra_mode']) - self.assertEqual(res['subnet']['ipv6_address_mode'], - data['subnet']['ipv6_address_mode']) + res = req.get_response(self.api) + self.assertEqual(res.status_int, + webob.exc.HTTPClientError.code) - def test_update_subnet_ipv6_inconsistent_ra_attribute(self): + def test_update_subnet_ipv6_ra_mode_fails(self): with self.subnet(ip_version=6, cidr='fe80::/64', - ipv6_ra_mode=constants.IPV6_SLAAC, - ipv6_address_mode=constants.IPV6_SLAAC) as subnet: + ipv6_ra_mode=constants.IPV6_SLAAC) as subnet: data = {'subnet': {'ipv6_ra_mode': constants.DHCPV6_STATEFUL}} req = self.new_update_request('subnets', data, subnet['subnet']['id']) @@ -3337,9 +3343,8 @@ class TestSubnetsV2(NeutronDbPluginV2TestCase): self.assertEqual(res.status_int, webob.exc.HTTPClientError.code) - def test_update_subnet_ipv6_inconsistent_address_attribute(self): + def test_update_subnet_ipv6_ra_mode_fails(self): with self.subnet(ip_version=6, cidr='fe80::/64', - ipv6_ra_mode=constants.IPV6_SLAAC, ipv6_address_mode=constants.IPV6_SLAAC) as subnet: data = {'subnet': {'ipv6_address_mode': constants.DHCPV6_STATEFUL}} req = self.new_update_request('subnets', data, @@ -3348,7 +3353,7 @@ class TestSubnetsV2(NeutronDbPluginV2TestCase): self.assertEqual(res.status_int, webob.exc.HTTPClientError.code) - def test_update_subnet_ipv6_inconsistent_enable_dhcp(self): + def test_update_subnet_ipv6_cannot_disable_dhcp(self): with self.subnet(ip_version=6, cidr='fe80::/64', ipv6_ra_mode=constants.IPV6_SLAAC, ipv6_address_mode=constants.IPV6_SLAAC) as subnet: