diff --git a/kuryr_libnetwork/constants.py b/kuryr_libnetwork/constants.py index cc742fb9..28c45e47 100644 --- a/kuryr_libnetwork/constants.py +++ b/kuryr_libnetwork/constants.py @@ -40,6 +40,7 @@ KURYR_EXISTING_NEUTRON_PORT = 'kuryr.port.existing' NETWORK_GATEWAY_OPTIONS = 'com.docker.network.gateway' NETWORK_GENERIC_OPTIONS = 'com.docker.network.generic' NEUTRON_NAME_OPTION = 'neutron.net.name' +NEUTRON_SHARED_OPTION = 'neutron.net.shared' NEUTRON_SUBNET_NAME_OPTION = 'neutron.subnet.name' NEUTRON_SUBNET_UUID_OPTION = 'neutron.subnet.uuid' NEUTRON_V6_SUBNET_NAME_OPTION = 'neutron.subnet.v6.name' diff --git a/kuryr_libnetwork/controllers.py b/kuryr_libnetwork/controllers.py index d7c9d1c1..e776c31b 100644 --- a/kuryr_libnetwork/controllers.py +++ b/kuryr_libnetwork/controllers.py @@ -25,6 +25,7 @@ from oslo_concurrency import processutils from oslo_config import cfg from oslo_log import log from oslo_utils import excutils +from oslo_utils import strutils from kuryr.lib import constants as lib_const from kuryr.lib import exceptions @@ -563,7 +564,7 @@ def _create_kuryr_subnet(pool_cidr, subnet_cidr, pool_id, network_id, gateway): LOG.debug("Created kuryr subnet %s", new_kuryr_subnet) -def _create_kuryr_subnetpool(pool_cidr, pool_tag): +def _create_kuryr_subnetpool(pool_cidr, pool_tag, shared): pool_name = lib_utils.get_neutron_subnetpool_name(pool_cidr) kwargs = {'name': pool_name} @@ -579,7 +580,9 @@ def _create_kuryr_subnetpool(pool_cidr, pool_tag): new_subnetpool = { 'name': pool_name, 'default_prefixlen': cidr.prefixlen, - 'prefixes': [pool_cidr]} + 'prefixes': [pool_cidr], + 'shared': shared + } LOG.info("Creating subnetpool with the given pool CIDR") created_subnetpool_response = app.neutron.create_subnetpool( {'subnetpool': new_subnetpool}) @@ -769,6 +772,7 @@ def network_driver_create_network(): v4_subnet_id = '' v6_subnet_name = '' v6_subnet_id = '' + shared = False options = json_data.get('Options') if options: generic_options = options.get(const.NETWORK_GENERIC_OPTIONS) @@ -790,6 +794,8 @@ def network_driver_create_network(): const.NEUTRON_POOL_UUID_OPTION) v6_pool_id = generic_options.get( const.NEUTRON_V6_POOL_UUID_OPTION) + shared = strutils.bool_from_string(generic_options.get( + const.NEUTRON_SHARED_OPTION, 'False')) def _get_pool_id(pool_name, pool_cidr, pool_tags): pool_id = '' @@ -843,7 +849,8 @@ def network_driver_create_network(): if not neutron_uuid and not neutron_name: network = app.neutron.create_network( {'network': {'name': neutron_network_name, - "admin_state_up": True}}) + "admin_state_up": True, + 'shared': shared}}) network_id = network['network']['id'] _neutron_net_add_tags(network['network']['id'], container_net_id, tags=app.tag) @@ -865,6 +872,7 @@ def network_driver_create_network(): ("Specified network id/name({0}) does not " "exist.").format(specified_network)) network_id = networks[0]['id'] + network_shared = networks[0]['shared'] except n_exceptions.NeutronClientException as ex: LOG.error("Error happened during listing " "Neutron networks: %s", ex) @@ -880,6 +888,19 @@ def network_driver_create_network(): "%(neutron_network_name)s successfully: %(network)s", {'neutron_network_name': neutron_network_name, 'network': network}) + if network_shared != shared: + # NOTE(kiennt): Use generic KuryrException to unblock + # patch 516228 for merging. Will change + # it to ConflictOption exception in the + # follow-up. + raise exceptions.KuryrException( + 'Network %(network_id)s had option ' + 'shared=%(network_shared)s, conflict with the given option ' + 'shared=%(shared)s', { + 'network_id': network_id, + 'network_shared': network_shared, + 'shared': shared + }) LOG.info("Using existing network %s " "successfully", specified_network) @@ -1472,8 +1493,11 @@ def ipam_request_pool(): pool_id = '' subnet_id = '' subnet_name = '' + shared = False options = json_data.get('Options') if options: + shared = strutils.bool_from_string(options.get( + const.NEUTRON_SHARED_OPTION, 'False')) if v6: subnet_name = options.get(const.NEUTRON_V6_SUBNET_NAME_OPTION) subnet_id = options.get(const.NEUTRON_V6_SUBNET_UUID_OPTION) @@ -1500,7 +1524,8 @@ def ipam_request_pool(): "in Options.") if not pool_name and not pool_id: pool_id = _create_kuryr_subnetpool(subnet_cidr, - subnet_id)['id'] + subnet_id, + shared)['id'] else: if pool_id: existing_pools = _get_subnetpools_by_attrs(id=pool_id) @@ -1518,11 +1543,22 @@ def ipam_request_pool(): prefixes = existing_pools[0]['prefixes'] pool_cidr = ipaddress.ip_network(six.text_type(prefixes[0])) if pool_cidr == cidr: + if shared != existing_pools[0]['shared']: + # NOTE(kiennt): Use generic KuryrException to unblock + # patch 516228 for merging. Will change + # it to ConflictOption exception in the + # follow-up. + raise exceptions.KuryrException( + 'There is already existing subnet pool ' + 'with %(cidr)s but with shared = %(shared)s', + {'cidr': cidr, + 'shared': existing_pools[0]['shared']}) LOG.info("Using existing Neutron subnetpool %s successfully", pool_id) else: pool_id = _create_kuryr_subnetpool(subnet_cidr, - subnet_id)['id'] + subnet_id, + shared)['id'] else: if v6: default_pool_list = SUBNET_POOLS_V6 diff --git a/kuryr_libnetwork/tests/unit/test_ipam_pool.py b/kuryr_libnetwork/tests/unit/test_ipam_pool.py index 13793385..29ea2ebb 100644 --- a/kuryr_libnetwork/tests/unit/test_ipam_pool.py +++ b/kuryr_libnetwork/tests/unit/test_ipam_pool.py @@ -50,7 +50,8 @@ class TestIpamRequestPoolFailures(base.TestKuryrFailures): new_subnetpool = { 'name': pool_name, 'default_prefixlen': 16, - 'prefixes': ['10.0.0.0/16']} + 'prefixes': ['10.0.0.0/16'], + 'shared': False} fake_subnet = {"subnets": []} mock_list_subnets.return_value = fake_subnet diff --git a/kuryr_libnetwork/tests/unit/test_kuryr.py b/kuryr_libnetwork/tests/unit/test_kuryr.py index 02ba1694..3dc98ead 100644 --- a/kuryr_libnetwork/tests/unit/test_kuryr.py +++ b/kuryr_libnetwork/tests/unit/test_kuryr.py @@ -115,7 +115,8 @@ class TestKuryr(base.TestKuryrBase): fake_create_network_request = { "network": { "name": utils.make_net_name(docker_network_id), - "admin_state_up": True + "admin_state_up": True, + "shared": False } } fake_network['name'] = utils.make_net_name(docker_network_id) @@ -241,7 +242,8 @@ class TestKuryr(base.TestKuryrBase): fake_create_network_request = { "network": { "name": utils.make_net_name(docker_network_id), - "admin_state_up": True + "admin_state_up": True, + "shared": False } } fake_network['name'] = utils.make_net_name(docker_network_id) @@ -379,7 +381,8 @@ class TestKuryr(base.TestKuryrBase): fake_create_network_request = { "network": { "name": utils.make_net_name(docker_network_id), - "admin_state_up": True + "admin_state_up": True, + "shared": False } } fake_network['name'] = utils.make_net_name(docker_network_id) @@ -821,7 +824,8 @@ class TestKuryr(base.TestKuryrBase): fake_create_network_request = { "network": { "name": utils.make_net_name(docker_network_id), - "admin_state_up": True + "admin_state_up": True, + "shared": False } } fake_network['name'] = utils.make_net_name(docker_network_id) @@ -986,7 +990,8 @@ class TestKuryr(base.TestKuryrBase): fake_create_network_request = { "network": { "name": utils.make_net_name(docker_network_id), - "admin_state_up": True + "admin_state_up": True, + "shared": False } } fake_network['name'] = utils.make_net_name(docker_network_id) @@ -1147,7 +1152,8 @@ class TestKuryr(base.TestKuryrBase): fake_create_network_request = { "network": { "name": utils.make_net_name(docker_network_id), - "admin_state_up": True + "admin_state_up": True, + "shared": False } } # The following fake response is retrieved from the Neutron doc: diff --git a/kuryr_libnetwork/tests/unit/test_kuryr_ipam.py b/kuryr_libnetwork/tests/unit/test_kuryr_ipam.py index 2623add3..2bfbaade 100644 --- a/kuryr_libnetwork/tests/unit/test_kuryr_ipam.py +++ b/kuryr_libnetwork/tests/unit/test_kuryr_ipam.py @@ -75,7 +75,8 @@ class TestKuryrIpam(base.TestKuryrBase): new_subnetpool = { 'name': pool_name, 'default_prefixlen': prefixlen, - 'prefixes': [pool_cidr]} + 'prefixes': [pool_cidr], + 'shared': False} fake_kuryr_subnetpool_id = uuidutils.generate_uuid() fake_name = pool_name @@ -117,6 +118,64 @@ class TestKuryrIpam(base.TestKuryrBase): decoded_json = jsonutils.loads(response.data) self.assertEqual(fake_kuryr_subnetpool_id, decoded_json['PoolID']) + @mock.patch('kuryr_libnetwork.controllers.app.neutron.add_tag') + @mock.patch('kuryr_libnetwork.controllers.app.neutron.create_subnetpool') + @mock.patch('kuryr_libnetwork.controllers.app.neutron.list_subnetpools') + @ddt.data((FAKE_IP4_CIDR), (FAKE_IP6_CIDR)) + def test_ipam_driver_request_pool_with_existing_subnet_id_and_shared(self, + pool_cidr, mock_list_subnetpools, + mock_create_subnetpool, mock_add_tag): + neutron_subnet_v4_id = uuidutils.generate_uuid() + + pool_name = lib_utils.get_neutron_subnetpool_name(pool_cidr) + prefixlen = ipaddress.ip_network(six.text_type(pool_cidr)).prefixlen + new_subnetpool = { + 'name': pool_name, + 'default_prefixlen': prefixlen, + 'prefixes': [pool_cidr], + 'shared': True} + + fake_kuryr_subnetpool_id = uuidutils.generate_uuid() + fake_name = pool_name + if pool_cidr == FAKE_IP4_CIDR: + kuryr_subnetpools = self._get_fake_v4_subnetpools( + fake_kuryr_subnetpool_id, prefixes=[pool_cidr], + name=fake_name) + else: + kuryr_subnetpools = self._get_fake_v6_subnetpools( + fake_kuryr_subnetpool_id, prefixes=[pool_cidr], + name=fake_name) + mock_list_subnetpools.return_value = {'subnetpools': []} + fake_subnetpool_response = { + 'subnetpool': kuryr_subnetpools['subnetpools'][0] + } + + mock_create_subnetpool.return_value = fake_subnetpool_response + + fake_request = { + 'AddressSpace': '', + 'Pool': pool_cidr, + 'SubPool': '', # In the case --ip-range is not given + 'Options': { + 'neutron.subnet.uuid': neutron_subnet_v4_id, + 'neutron.net.shared': True + }, + 'V6': False + } + response = self.app.post('/IpamDriver.RequestPool', + content_type='application/json', + data=jsonutils.dumps(fake_request)) + + self.assertEqual(200, response.status_code) + mock_list_subnetpools.assert_called_with( + name=pool_name, tags=[str(neutron_subnet_v4_id)]) + mock_create_subnetpool.assert_called_with( + {'subnetpool': new_subnetpool}) + mock_add_tag.assert_called_once_with( + 'subnetpools', fake_kuryr_subnetpool_id, neutron_subnet_v4_id) + decoded_json = jsonutils.loads(response.data) + self.assertEqual(fake_kuryr_subnetpool_id, decoded_json['PoolID']) + @mock.patch('kuryr_libnetwork.controllers.app.neutron.add_tag') @mock.patch('kuryr_libnetwork.controllers.app.neutron.create_subnetpool') @mock.patch('kuryr_libnetwork.controllers.app.neutron.list_subnetpools') @@ -149,7 +208,8 @@ class TestKuryrIpam(base.TestKuryrBase): new_subnetpool = { 'name': pool_name, 'default_prefixlen': prefixlen, - 'prefixes': [pool_cidr]} + 'prefixes': [pool_cidr], + 'shared': False} fake_kuryr_subnetpool_id = uuidutils.generate_uuid() fake_name = pool_name @@ -205,7 +265,8 @@ class TestKuryrIpam(base.TestKuryrBase): new_subnetpool = { 'name': pool_name, 'default_prefixlen': prefixlen, - 'prefixes': [pool_cidr]} + 'prefixes': [pool_cidr], + 'shared': False} fake_kuryr_subnetpool_id = uuidutils.generate_uuid() fake_name = pool_name @@ -369,7 +430,8 @@ class TestKuryrIpam(base.TestKuryrBase): new_subnetpool = { 'name': pool_name, 'default_prefixlen': prefixlen, - 'prefixes': [subnet_cidr]} + 'prefixes': [subnet_cidr], + 'shared': False} fake_kuryr_subnetpool_id = uuidutils.generate_uuid() fake_existing_subnetpool_id = uuidutils.generate_uuid() diff --git a/kuryr_libnetwork/tests/unit/test_kuryr_network.py b/kuryr_libnetwork/tests/unit/test_kuryr_network.py index 18e156b8..7a0b103e 100644 --- a/kuryr_libnetwork/tests/unit/test_kuryr_network.py +++ b/kuryr_libnetwork/tests/unit/test_kuryr_network.py @@ -81,7 +81,8 @@ class TestKuryrNetworkCreateFailures(base.TestKuryrFailures): fake_request = { "network": { "name": utils.make_net_name(docker_network_id), - "admin_state_up": True + "admin_state_up": True, + "shared": False } } mock_create_network.side_effect = exceptions.Unauthorized