diff --git a/kuryr_libnetwork/constants.py b/kuryr_libnetwork/constants.py index cd16ca33..5efcedaa 100644 --- a/kuryr_libnetwork/constants.py +++ b/kuryr_libnetwork/constants.py @@ -40,6 +40,7 @@ NETWORK_GENERIC_OPTIONS = 'com.docker.network.generic' NEUTRON_NAME_OPTION = 'neutron.net.name' NEUTRON_POOL_NAME_OPTION = 'neutron.pool.name' NEUTRON_POOL_UUID_OPTION = 'neutron.pool.uuid' +NEUTRON_V6_POOL_NAME_OPTION = 'neutron.pool.v6.name' NEUTRON_UUID_OPTION = 'neutron.net.uuid' REQUEST_ADDRESS_TYPE = 'RequestAddressType' KURYR_UNBOUND_PORT = 'kuryr-unbound-port' diff --git a/kuryr_libnetwork/controllers.py b/kuryr_libnetwork/controllers.py index ebe59cb4..434b71c6 100644 --- a/kuryr_libnetwork/controllers.py +++ b/kuryr_libnetwork/controllers.py @@ -509,6 +509,22 @@ def revoke_expose_ports(port_id): "Neutron security group: {0}").format(ex)) +def _create_kuryr_subnet(pool_cidr, subnet_cidr, pool_id, network_id, gateway): + new_kuryr_subnet = [{ + 'name': utils.make_subnet_name(pool_cidr), + 'network_id': network_id, + 'ip_version': subnet_cidr.version, + 'cidr': six.text_type(subnet_cidr), + 'enable_dhcp': app.enable_dhcp, + }] + new_kuryr_subnet[0]['subnetpool_id'] = pool_id + if gateway: + new_kuryr_subnet[0]['gateway_ip'] = gateway + + app.neutron.create_subnet({'subnets': new_kuryr_subnet}) + LOG.debug("Created kuryr subnet %s", new_kuryr_subnet) + + @app.route('/Plugin.Activate', methods=['POST']) def plugin_activate(): """Returns the list of the implemented drivers. @@ -620,41 +636,73 @@ def network_driver_create_network(): jsonschema.validate(json_data, schemata.NETWORK_CREATE_SCHEMA) container_net_id = json_data['NetworkID'] neutron_network_name = utils.make_net_name(container_net_id, tags=app.tag) - pool_cidr = json_data['IPv4Data'][0]['Pool'] - gateway_ip = '' - if 'Gateway' in json_data['IPv4Data'][0]: - gateway_cidr = json_data['IPv4Data'][0]['Gateway'] - gateway_ip = gateway_cidr.split('/')[0] - LOG.debug("gateway_cidr %(gateway_cidr)s, " - "gateway_ip %(gateway_ip)s", - {'gateway_cidr': gateway_cidr, 'gateway_ip': gateway_ip}) + v4_pool_cidr = None + v6_pool_cidr = None + v4_gateway_ip = '' + v6_gateway_ip = '' + + def _get_gateway_ip(ip_data): + gateway_ip = '' + if 'Gateway' in ip_data: + gateway_cidr = ip_data['Gateway'] + gateway_ip = gateway_cidr.split('/')[0] + return gateway_ip + + if json_data['IPv4Data']: + v4_pool_cidr = json_data['IPv4Data'][0]['Pool'] + v4_gateway_ip = _get_gateway_ip(json_data['IPv4Data'][0]) + + if json_data['IPv6Data']: + v6_pool_cidr = json_data['IPv6Data'][0]['Pool'] + v6_gateway_ip = _get_gateway_ip(json_data['IPv6Data'][0]) neutron_uuid = None neutron_name = None - pool_name = '' - pool_id = '' + existing_pool_id = '' + v4_pool_name = '' + v4_pool_id = '' + v6_pool_name = '' + v6_pool_id = '' options = json_data.get('Options') if options: generic_options = options.get(const.NETWORK_GENERIC_OPTIONS) if generic_options: neutron_uuid = generic_options.get(const.NEUTRON_UUID_OPTION) neutron_name = generic_options.get(const.NEUTRON_NAME_OPTION) - pool_name = generic_options.get(const.NEUTRON_POOL_NAME_OPTION) - pool_id = generic_options.get(const.NEUTRON_POOL_UUID_OPTION) + v4_pool_name = generic_options.get(const.NEUTRON_POOL_NAME_OPTION) + v6_pool_name = generic_options.get( + const.NEUTRON_V6_POOL_NAME_OPTION) + existing_pool_id = generic_options.get( + const.NEUTRON_POOL_UUID_OPTION) - if pool_id: - pools = _get_subnetpools_by_attrs(id=pool_id) - elif pool_name: - pools = _get_subnetpools_by_attrs(name=pool_name) + def _get_pool_id(pool_name, pool_cidr): + pool_id = '' + if not pool_name and pool_cidr: + pool_name = lib_utils.get_neutron_subnetpool_name(pool_cidr) + if pool_name: + pools = _get_subnetpools_by_attrs(name=pool_name) + if pools: + pool_id = pools[0]['id'] + else: + raise exceptions.KuryrException( + ("Specified pool name({0}) does not " + "exist.").format(pool_name)) + return pool_id + + if existing_pool_id: + pools = _get_subnetpools_by_attrs(id=existing_pool_id) + if pools: + existing_pool_id = pools[0]['id'] + else: + raise exceptions.KuryrException( + ("Specified pool id({0}) does not " + "exist.").format(existing_pool_id)) + + if existing_pool_id: + v4_pool_id = existing_pool_id else: - pool_name = lib_utils.get_neutron_subnetpool_name(pool_cidr) - pools = _get_subnetpools_by_attrs(name=pool_name) - - if not pools: - raise exceptions.KuryrException( - ("Specified pool id/name({0}) does not " - "exist.").format(pool_id or pool_name)) - pool_id = pools[0]['id'] + v4_pool_id = _get_pool_id(v4_pool_name, v4_pool_cidr) + v6_pool_id = _get_pool_id(v6_pool_name, v6_pool_cidr) # let the user override the driver default if not neutron_uuid and not neutron_name: @@ -707,32 +755,40 @@ def network_driver_create_network(): LOG.info(_LI("Using existing network %s " "successfully"), specified_network) - cidr = ipaddress.ip_network(six.text_type(pool_cidr)) - subnets = _get_subnets_by_attrs(network_id=network_id, - cidr=six.text_type(cidr)) - if len(subnets) > 1: - raise exceptions.DuplicatedResourceException( - "Multiple Neutron subnets exist for the network_id={0}" - "and cidr={1}".format(network_id, cidr)) + def _get_existing_neutron_subnets(pool_cidr, network_id): + cidr = None + subnets = [] + if pool_cidr: + cidr = ipaddress.ip_network(six.text_type(pool_cidr)) + subnets = _get_subnets_by_attrs(network_id=network_id, + cidr=six.text_type(cidr)) + if len(subnets) > 1: + raise exceptions.DuplicatedResourceException( + "Multiple Neutron subnets exist for the network_id={0}" + "and cidr={1}".format(network_id, cidr)) + return cidr, subnets + + v4_cidr, v4_subnets = _get_existing_neutron_subnets(v4_pool_cidr, + network_id) + v6_cidr, v6_subnets = _get_existing_neutron_subnets(v6_pool_cidr, + network_id) + + def _add_tag_for_existing_subnet(subnet, pool_id): + if len(subnet) == 1: + _neutron_subnet_add_tag(subnet[0]['id'], pool_id) # This will add a subnetpool_id(created by kuryr) tag - # for existing Neutron subnet. - if app.tag and len(subnets) == 1: - _neutron_subnet_add_tag(subnets[0]['id'], pool_id) + # for existing Neutron subnets. + if app.tag: + _add_tag_for_existing_subnet(v4_subnets, v4_pool_id) + _add_tag_for_existing_subnet(v6_subnets, v6_pool_id) - if not subnets: - new_subnets = [{ - 'name': utils.make_subnet_name(pool_cidr), - 'network_id': network_id, - 'ip_version': cidr.version, - 'cidr': six.text_type(cidr), - 'enable_dhcp': app.enable_dhcp, - }] - new_subnets[0]['subnetpool_id'] = pool_id - if gateway_ip: - new_subnets[0]['gateway_ip'] = gateway_ip - - app.neutron.create_subnet({'subnets': new_subnets}) + if not v4_subnets and v4_pool_cidr: + _create_kuryr_subnet(v4_pool_cidr, v4_cidr, v4_pool_id, + network_id, v4_gateway_ip) + if not v6_subnets and v6_pool_cidr: + _create_kuryr_subnet(v6_pool_cidr, v6_cidr, v6_pool_id, + network_id, v6_gateway_ip) return flask.jsonify(const.SCHEMA['SUCCESS']) diff --git a/kuryr_libnetwork/tests/unit/test_kuryr.py b/kuryr_libnetwork/tests/unit/test_kuryr.py index c572eef7..522895f2 100644 --- a/kuryr_libnetwork/tests/unit/test_kuryr.py +++ b/kuryr_libnetwork/tests/unit/test_kuryr.py @@ -66,7 +66,7 @@ class TestKuryr(base.TestKuryrBase): @mock.patch( 'kuryr_libnetwork.controllers.app.driver.get_default_network_id') @ddt.data((True), (False)) - def test_network_driver_create_network(self, + def test_network_driver_create_v4_network(self, driver_default_net, mock_get_default_network_id, mock_list_networks, mock_create_network, mock_add_tag, mock_create_subnet, mock_list_subnetpools, mock_list_subnets): @@ -80,11 +80,7 @@ class TestKuryr(base.TestKuryrBase): 'Pool': '192.168.42.0/24', 'Gateway': '192.168.42.1/24', }], - 'IPv6Data': [{ - 'AddressSpace': 'bar', - 'Pool': 'fe80::/64', - 'Gateway': 'fe80::f816:3eff:fe20:57c3/64', - }], + 'IPv6Data': [], 'Options': {} } @@ -94,7 +90,7 @@ class TestKuryr(base.TestKuryrBase): kuryr_subnetpools = self._get_fake_v4_subnetpools( fake_kuryr_subnetpool_id, name=fake_name) - fake_neutron_net_id = "4e8e5957-649f-477b-9e5b-f1f75b21c03c" + fake_neutron_net_id = uuidutils.generate_uuid() driver_value = fake_neutron_net_id if driver_default_net else None mock_get_default_network_id.return_value = driver_value fake_network = { @@ -186,6 +182,296 @@ class TestKuryr(base.TestKuryrBase): decoded_json = jsonutils.loads(response.data) self.assertEqual(constants.SCHEMA['SUCCESS'], decoded_json) + @mock.patch('kuryr_libnetwork.controllers.app.neutron.list_subnets') + @mock.patch('kuryr_libnetwork.controllers.app.neutron.list_subnetpools') + @mock.patch('kuryr_libnetwork.controllers.app.neutron.create_subnet') + @mock.patch('kuryr_libnetwork.controllers.app.neutron.add_tag') + @mock.patch('kuryr_libnetwork.controllers.app.neutron.create_network') + @mock.patch('kuryr_libnetwork.controllers.app.neutron.list_networks') + @mock.patch( + 'kuryr_libnetwork.controllers.app.driver.get_default_network_id') + @ddt.data((True), (False)) + def test_network_driver_create_v6_network(self, + driver_default_net, mock_get_default_network_id, + mock_list_networks, mock_create_network, mock_add_tag, + mock_create_subnet, mock_list_subnetpools, mock_list_subnets): + docker_network_id = lib_utils.get_hash() + docker_endpoint_id = lib_utils.get_hash() + + network_request = { + 'NetworkID': docker_network_id, + 'IPv4Data': [], + 'IPv6Data': [{ + 'AddressSpace': 'bar', + 'Pool': 'fe80::/64', + 'Gateway': 'fe80::f816:3eff:fe20:57c3/64', + }], + 'Options': {} + } + + fake_kuryr_subnetpool_id = uuidutils.generate_uuid() + fake_name = lib_utils.get_neutron_subnetpool_name( + network_request['IPv6Data'][0]['Pool']) + kuryr_subnetpools = self._get_fake_v6_subnetpools( + fake_kuryr_subnetpool_id, name=fake_name) + + fake_neutron_net_id = uuidutils.generate_uuid() + driver_value = fake_neutron_net_id if driver_default_net else None + mock_get_default_network_id.return_value = driver_value + fake_network = { + "status": "ACTIVE", + "subnets": [], + "admin_state_up": True, + "tenant_id": "9bacb3c5d39d41a79512987f338cf177", + "router:external": False, + "segments": [], + "shared": False, + "id": fake_neutron_net_id, + } + + if driver_value: + fake_existing_networks_response = { + "networks": [fake_network] + } + mock_list_networks.return_value = fake_existing_networks_response + else: + fake_create_network_request = { + "network": { + "name": utils.make_net_name(docker_network_id), + "admin_state_up": True + } + } + fake_network['name'] = utils.make_net_name(docker_network_id) + # The following fake response is retrieved from the Neutron doc: + # http://developer.openstack.org/api-ref-networking-v2.html#createNetwork # noqa + fake_create_network_response = { + "network": fake_network + } + mock_create_network.return_value = fake_create_network_response + + tags = utils.create_net_tags(docker_network_id) + + fake_existing_subnets_response = { + "subnets": [] + } + + fake_cidr_v6 = 'fe80::/64' + fake_subnet_request = { + "subnets": [{ + 'name': utils.make_subnet_name(fake_cidr_v6), + 'network_id': fake_neutron_net_id, + 'ip_version': 6, + 'cidr': fake_cidr_v6, + 'enable_dhcp': app.enable_dhcp, + 'gateway_ip': 'fe80::f816:3eff:fe20:57c3', + 'subnetpool_id': fake_kuryr_subnetpool_id + }] + } + + subnet_v6_id = uuidutils.generate_uuid() + fake_v6_subnet = self._get_fake_v6_subnet( + fake_neutron_net_id, docker_endpoint_id, subnet_v6_id, + fake_kuryr_subnetpool_id, + name=fake_cidr_v6, cidr=fake_cidr_v6) + + fake_subnet_response = { + 'subnets': [ + fake_v6_subnet['subnet'] + ] + } + mock_create_subnet.return_value = fake_subnet_response + mock_list_subnetpools.return_value = {'subnetpools': + kuryr_subnetpools['subnetpools']} + mock_list_subnets.return_value = fake_existing_subnets_response + + response = self.app.post('/NetworkDriver.CreateNetwork', + content_type='application/json', + data=jsonutils.dumps(network_request)) + + self.assertEqual(200, response.status_code) + + mock_get_default_network_id.assert_any_call() + + for tag in tags: + mock_add_tag.assert_any_call('networks', + fake_neutron_net_id, tag) + if driver_value: + mock_list_networks.assert_called_with(id=fake_neutron_net_id) + mock_add_tag.assert_any_call('networks', fake_neutron_net_id, + 'kuryr.net.existing') + else: + mock_create_network.assert_called_with(fake_create_network_request) + mock_create_subnet.assert_called_with(fake_subnet_request) + mock_list_subnetpools.assert_called_with(name=fake_name) + mock_list_subnets.assert_called_with(network_id=fake_neutron_net_id, + cidr=fake_cidr_v6) + + decoded_json = jsonutils.loads(response.data) + self.assertEqual(constants.SCHEMA['SUCCESS'], decoded_json) + + @mock.patch('kuryr_libnetwork.controllers.app.neutron.list_subnets') + @mock.patch('kuryr_libnetwork.controllers.app.neutron.list_subnetpools') + @mock.patch('kuryr_libnetwork.controllers.app.neutron.create_subnet') + @mock.patch('kuryr_libnetwork.controllers.app.neutron.add_tag') + @mock.patch('kuryr_libnetwork.controllers.app.neutron.create_network') + @mock.patch('kuryr_libnetwork.controllers.app.neutron.list_networks') + @mock.patch( + 'kuryr_libnetwork.controllers.app.driver.get_default_network_id') + @ddt.data((True), (False)) + def test_network_driver_create_v4_v6_network(self, + driver_default_net, mock_get_default_network_id, + mock_list_networks, mock_create_network, mock_add_tag, + mock_create_subnet, mock_list_subnetpools, mock_list_subnets): + docker_network_id = lib_utils.get_hash() + docker_endpoint_id = lib_utils.get_hash() + + network_request = { + 'NetworkID': docker_network_id, + 'IPv4Data': [{ + 'AddressSpace': 'foo', + 'Pool': '192.168.42.0/24', + 'Gateway': '192.168.42.1/24', + }], + 'IPv6Data': [{ + 'AddressSpace': 'bar', + 'Pool': 'fe80::/64', + 'Gateway': 'fe80::f816:3eff:fe20:57c3/64', + }], + 'Options': {} + } + + fake_kuryr_v4_subnetpool_id = uuidutils.generate_uuid() + fake_v4_pool_name = lib_utils.get_neutron_subnetpool_name( + network_request['IPv4Data'][0]['Pool']) + kuryr_v4_subnetpools = self._get_fake_v4_subnetpools( + fake_kuryr_v4_subnetpool_id, name=fake_v4_pool_name) + + fake_kuryr_v6_subnetpool_id = uuidutils.generate_uuid() + fake_v6_pool_name = lib_utils.get_neutron_subnetpool_name( + network_request['IPv6Data'][0]['Pool']) + kuryr_v6_subnetpools = self._get_fake_v6_subnetpools( + fake_kuryr_v6_subnetpool_id, name=fake_v6_pool_name) + + fake_neutron_net_id = uuidutils.generate_uuid() + driver_value = fake_neutron_net_id if driver_default_net else None + mock_get_default_network_id.return_value = driver_value + fake_network = { + "status": "ACTIVE", + "subnets": [], + "admin_state_up": True, + "tenant_id": "9bacb3c5d39d41a79512987f338cf177", + "router:external": False, + "segments": [], + "shared": False, + "id": fake_neutron_net_id, + } + + if driver_value: + fake_existing_networks_response = { + "networks": [fake_network] + } + mock_list_networks.return_value = fake_existing_networks_response + else: + fake_create_network_request = { + "network": { + "name": utils.make_net_name(docker_network_id), + "admin_state_up": True + } + } + fake_network['name'] = utils.make_net_name(docker_network_id) + # The following fake response is retrieved from the Neutron doc: + # http://developer.openstack.org/api-ref-networking-v2.html#createNetwork # noqa + fake_create_network_response = { + "network": fake_network + } + mock_create_network.return_value = fake_create_network_response + + tags = utils.create_net_tags(docker_network_id) + + fake_existing_subnets_response = { + "subnets": [] + } + fake_cidr_v4 = '192.168.42.0/24' + fake_cidr_v6 = 'fe80::/64' + + fake_v4_subnet_request = { + "subnets": [{ + 'name': utils.make_subnet_name(fake_cidr_v4), + 'network_id': fake_neutron_net_id, + 'ip_version': 4, + 'cidr': fake_cidr_v4, + 'enable_dhcp': app.enable_dhcp, + 'gateway_ip': '192.168.42.1', + 'subnetpool_id': fake_kuryr_v4_subnetpool_id + }] + } + subnet_v4_id = uuidutils.generate_uuid() + fake_v4_subnet = self._get_fake_v4_subnet( + fake_neutron_net_id, docker_endpoint_id, subnet_v4_id, + fake_kuryr_v4_subnetpool_id, + name=fake_cidr_v4, cidr=fake_cidr_v4) + + fake_v6_subnet_request = { + "subnets": [{ + 'name': utils.make_subnet_name(fake_cidr_v6), + 'network_id': fake_neutron_net_id, + 'ip_version': 6, + 'cidr': fake_cidr_v6, + 'enable_dhcp': app.enable_dhcp, + 'gateway_ip': 'fe80::f816:3eff:fe20:57c3', + 'subnetpool_id': fake_kuryr_v6_subnetpool_id + }] + } + + subnet_v6_id = uuidutils.generate_uuid() + fake_v6_subnet = self._get_fake_v6_subnet( + fake_neutron_net_id, docker_endpoint_id, subnet_v6_id, + fake_kuryr_v6_subnetpool_id, + name=fake_cidr_v6, cidr=fake_cidr_v6) + + fake_v4_v6_subnets_response = { + 'subnets': [ + fake_v4_subnet['subnet'], + fake_v6_subnet['subnet'] + ] + } + mock_create_subnet.return_value = fake_v4_v6_subnets_response + + mock_list_subnetpools.side_effect = [ + {'subnetpools': kuryr_v4_subnetpools['subnetpools']}, + {'subnetpools': kuryr_v6_subnetpools['subnetpools']} + ] + mock_list_subnets.return_value = fake_existing_subnets_response + + response = self.app.post('/NetworkDriver.CreateNetwork', + content_type='application/json', + data=jsonutils.dumps(network_request)) + + self.assertEqual(200, response.status_code) + + mock_get_default_network_id.assert_any_call() + + for tag in tags: + mock_add_tag.assert_any_call('networks', + fake_neutron_net_id, tag) + if driver_value: + mock_list_networks.assert_called_with(id=fake_neutron_net_id) + mock_add_tag.assert_any_call('networks', fake_neutron_net_id, + 'kuryr.net.existing') + else: + mock_create_network.assert_called_with(fake_create_network_request) + mock_create_subnet.assert_any_call(fake_v4_subnet_request) + mock_create_subnet.assert_any_call(fake_v6_subnet_request) + mock_list_subnetpools.assert_any_call(name=fake_v4_pool_name) + mock_list_subnetpools.assert_any_call(name=fake_v6_pool_name) + mock_list_subnets.assert_any_call(network_id=fake_neutron_net_id, + cidr=fake_cidr_v4) + mock_list_subnets.assert_any_call(network_id=fake_neutron_net_id, + cidr=fake_cidr_v6) + + decoded_json = jsonutils.loads(response.data) + self.assertEqual(constants.SCHEMA['SUCCESS'], decoded_json) + @mock.patch('kuryr_libnetwork.controllers.app.neutron.add_tag') @mock.patch('kuryr_libnetwork.controllers.app.neutron.create_subnet') @mock.patch('kuryr_libnetwork.controllers.app.neutron.list_subnets') @@ -210,20 +496,26 @@ class TestKuryr(base.TestKuryrBase): 'Gateway': 'fe80::f816:3eff:fe20:57c3/64', }], 'Options': { - 'com.docker.network.enable_ipv6': False, + 'com.docker.network.enable_ipv6': True, 'com.docker.network.generic': { 'neutron.net.name': 'my_network_name' } } } - fake_kuryr_subnetpool_id = uuidutils.generate_uuid() - fake_name = lib_utils.get_neutron_subnetpool_name( + fake_kuryr_v4_subnetpool_id = uuidutils.generate_uuid() + fake_v4_pool_name = lib_utils.get_neutron_subnetpool_name( network_request['IPv4Data'][0]['Pool']) - kuryr_subnetpools = self._get_fake_v4_subnetpools( - fake_kuryr_subnetpool_id, name=fake_name) + kuryr_v4_subnetpools = self._get_fake_v4_subnetpools( + fake_kuryr_v4_subnetpool_id, name=fake_v4_pool_name) - fake_neutron_net_id = "4e8e5957-649f-477b-9e5b-f1f75b21c03c" + fake_kuryr_v6_subnetpool_id = uuidutils.generate_uuid() + fake_v6_pool_name = lib_utils.get_neutron_subnetpool_name( + network_request['IPv6Data'][0]['Pool']) + kuryr_v6_subnetpools = self._get_fake_v6_subnetpools( + fake_kuryr_v6_subnetpool_id, name=fake_v6_pool_name) + + fake_neutron_net_id = uuidutils.generate_uuid() fake_neutron_net_name = 'my_network_name' fake_existing_networks_response = { "networks": [{ @@ -235,7 +527,7 @@ class TestKuryr(base.TestKuryrBase): "segments": [], "shared": False, "id": fake_neutron_net_id, - "name": "my_network_name" + "name": fake_neutron_net_name }] } tags = utils.create_net_tags(docker_network_id) @@ -243,7 +535,8 @@ class TestKuryr(base.TestKuryrBase): "subnets": [] } fake_cidr_v4 = '192.168.42.0/24' - fake_subnet_request = { + fake_cidr_v6 = 'fe80::/64' + fake_v4_subnet_request = { "subnets": [{ 'name': utils.make_subnet_name(fake_cidr_v4), 'network_id': fake_neutron_net_id, @@ -251,24 +544,48 @@ class TestKuryr(base.TestKuryrBase): 'cidr': fake_cidr_v4, 'enable_dhcp': app.enable_dhcp, 'gateway_ip': '192.168.42.1', - 'subnetpool_id': fake_kuryr_subnetpool_id + 'subnetpool_id': fake_kuryr_v4_subnetpool_id }] } subnet_v4_id = uuidutils.generate_uuid() fake_v4_subnet = self._get_fake_v4_subnet( fake_neutron_net_id, docker_endpoint_id, subnet_v4_id, - fake_kuryr_subnetpool_id, + fake_kuryr_v4_subnetpool_id, name=fake_cidr_v4, cidr=fake_cidr_v4) - fake_subnet_response = { + + fake_v6_subnet_request = { + "subnets": [{ + 'name': utils.make_subnet_name(fake_cidr_v6), + 'network_id': fake_neutron_net_id, + 'ip_version': 6, + 'cidr': fake_cidr_v6, + 'enable_dhcp': app.enable_dhcp, + 'gateway_ip': 'fe80::f816:3eff:fe20:57c3', + 'subnetpool_id': fake_kuryr_v6_subnetpool_id + }] + } + + subnet_v6_id = uuidutils.generate_uuid() + fake_v6_subnet = self._get_fake_v6_subnet( + fake_neutron_net_id, docker_endpoint_id, subnet_v6_id, + fake_kuryr_v6_subnetpool_id, + name=fake_cidr_v6, cidr=fake_cidr_v6) + + fake_v4_v6_subnets_response = { 'subnets': [ - fake_v4_subnet['subnet'] + fake_v4_subnet['subnet'], + fake_v6_subnet['subnet'] ] } mock_list_networks.return_value = fake_existing_networks_response - mock_list_subnetpools.return_value = {'subnetpools': - kuryr_subnetpools['subnetpools']} + + mock_list_subnetpools.side_effect = [ + {'subnetpools': kuryr_v4_subnetpools['subnetpools']}, + {'subnetpools': kuryr_v6_subnetpools['subnetpools']} + ] + mock_list_subnets.return_value = fake_existing_subnets_response - mock_create_subnet.return_value = fake_subnet_response + mock_create_subnet.return_value = fake_v4_v6_subnets_response response = self.app.post('/NetworkDriver.CreateNetwork', content_type='application/json', @@ -280,11 +597,16 @@ class TestKuryr(base.TestKuryrBase): fake_neutron_net_id, tag) mock_add_tag.assert_called_with('networks', fake_neutron_net_id, 'kuryr.net.existing') - mock_create_subnet.assert_called_with(fake_subnet_request) + mock_create_subnet.assert_any_call(fake_v4_subnet_request) + mock_create_subnet.assert_any_call(fake_v6_subnet_request) mock_list_networks.assert_called_with(name=fake_neutron_net_name) - mock_list_subnetpools.assert_called_with(name=fake_name) - mock_list_subnets.assert_called_with(network_id=fake_neutron_net_id, + mock_list_subnetpools.assert_any_call(name=fake_v4_pool_name) + mock_list_subnetpools.assert_any_call(name=fake_v6_pool_name) + mock_list_subnets.assert_any_call(network_id=fake_neutron_net_id, cidr=fake_cidr_v4) + mock_list_subnets.assert_any_call(network_id=fake_neutron_net_id, + cidr=fake_cidr_v6) + decoded_json = jsonutils.loads(response.data) self.assertEqual(constants.SCHEMA['SUCCESS'], decoded_json) @@ -297,7 +619,7 @@ class TestKuryr(base.TestKuryrBase): mock_list_subnets, mock_list_subnetpools, mock_list_networks, mock_add_tag, mock_create_subnet): docker_network_id = lib_utils.get_hash() - fake_neutron_net_id = "4e8e5957-649f-477b-9e5b-f1f75b21c03c" + fake_neutron_net_id = uuidutils.generate_uuid() network_request = { 'NetworkID': docker_network_id, 'IPv4Data': [{ @@ -311,18 +633,24 @@ class TestKuryr(base.TestKuryrBase): 'Gateway': 'fe80::f816:3eff:fe20:57c3/64', }], 'Options': { - 'com.docker.network.enable_ipv6': False, + 'com.docker.network.enable_ipv6': True, 'com.docker.network.generic': { 'neutron.net.uuid': fake_neutron_net_id } } } - fake_kuryr_subnetpool_id = uuidutils.generate_uuid() - fake_name = lib_utils.get_neutron_subnetpool_name( + fake_kuryr_v4_subnetpool_id = uuidutils.generate_uuid() + fake_v4_pool_name = lib_utils.get_neutron_subnetpool_name( network_request['IPv4Data'][0]['Pool']) - kuryr_subnetpools = self._get_fake_v4_subnetpools( - fake_kuryr_subnetpool_id, name=fake_name) + kuryr_v4_subnetpools = self._get_fake_v4_subnetpools( + fake_kuryr_v4_subnetpool_id, name=fake_v4_pool_name) + + fake_kuryr_v6_subnetpool_id = uuidutils.generate_uuid() + fake_v6_pool_name = lib_utils.get_neutron_subnetpool_name( + network_request['IPv6Data'][0]['Pool']) + kuryr_v6_subnetpools = self._get_fake_v6_subnetpools( + fake_kuryr_v6_subnetpool_id, name=fake_v6_pool_name) fake_existing_networks_response = { "networks": [{ @@ -343,7 +671,7 @@ class TestKuryr(base.TestKuryrBase): } fake_cidr_v4 = '192.168.42.0/24' - fake_subnet_request = { + fake_v4_subnet_request = { "subnets": [{ 'name': utils.make_subnet_name(fake_cidr_v4), 'network_id': fake_neutron_net_id, @@ -351,23 +679,46 @@ class TestKuryr(base.TestKuryrBase): 'cidr': fake_cidr_v4, 'enable_dhcp': app.enable_dhcp, 'gateway_ip': '192.168.42.1', - 'subnetpool_id': fake_kuryr_subnetpool_id + 'subnetpool_id': fake_kuryr_v4_subnetpool_id }] } subnet_v4_id = uuidutils.generate_uuid() fake_v4_subnet = self._get_fake_v4_subnet( fake_neutron_net_id, subnet_v4_id, - fake_kuryr_subnetpool_id, + fake_kuryr_v4_subnetpool_id, name=fake_cidr_v4, cidr=fake_cidr_v4) - fake_subnet_response = { + + fake_cidr_v6 = 'fe80::/64' + fake_v6_subnet_request = { + "subnets": [{ + 'name': utils.make_subnet_name(fake_cidr_v6), + 'network_id': fake_neutron_net_id, + 'ip_version': 6, + 'cidr': fake_cidr_v6, + 'enable_dhcp': app.enable_dhcp, + 'gateway_ip': 'fe80::f816:3eff:fe20:57c3', + 'subnetpool_id': fake_kuryr_v6_subnetpool_id + }] + } + + subnet_v6_id = uuidutils.generate_uuid() + fake_v6_subnet = self._get_fake_v6_subnet( + fake_neutron_net_id, subnet_v6_id, + fake_kuryr_v6_subnetpool_id, + name=fake_cidr_v6, cidr=fake_cidr_v6) + + fake_v4_v6_subnets_response = { 'subnets': [ - fake_v4_subnet['subnet'] + fake_v4_subnet['subnet'], + fake_v6_subnet['subnet'] ] } - mock_create_subnet.return_value = fake_subnet_response + mock_create_subnet.return_value = fake_v4_v6_subnets_response mock_list_networks.return_value = fake_existing_networks_response - mock_list_subnetpools.return_value = {'subnetpools': - kuryr_subnetpools['subnetpools']} + mock_list_subnetpools.side_effect = [ + {'subnetpools': kuryr_v4_subnetpools['subnetpools']}, + {'subnetpools': kuryr_v6_subnetpools['subnetpools']} + ] mock_list_subnets.return_value = fake_existing_subnets_response response = self.app.post('/NetworkDriver.CreateNetwork', @@ -379,15 +730,18 @@ class TestKuryr(base.TestKuryrBase): mock_add_tag.assert_any_call('networks', fake_neutron_net_id, tag) mock_add_tag.assert_any_call( 'networks', fake_neutron_net_id, 'kuryr.net.existing') - mock_create_subnet.assert_called_with(fake_subnet_request) + mock_create_subnet.assert_any_call(fake_v4_subnet_request) + mock_create_subnet.assert_any_call(fake_v6_subnet_request) mock_list_networks.assert_called_with(id=fake_neutron_net_id) - mock_list_subnetpools.assert_called_with(name=fake_name) - mock_list_subnets.assert_called_with(network_id=fake_neutron_net_id, + mock_list_subnetpools.assert_any_call(name=fake_v4_pool_name) + mock_list_subnetpools.assert_any_call(name=fake_v6_pool_name) + mock_list_subnets.assert_any_call(network_id=fake_neutron_net_id, cidr=fake_cidr_v4) + mock_list_subnets.assert_any_call(network_id=fake_neutron_net_id, + cidr=fake_cidr_v6) decoded_json = jsonutils.loads(response.data) self.assertEqual(constants.SCHEMA['SUCCESS'], decoded_json) - @ddt.data((True), (False)) @mock.patch('kuryr_libnetwork.controllers.app.neutron.list_subnets') @mock.patch('kuryr_libnetwork.controllers.app.neutron.list_subnetpools') @mock.patch('kuryr_libnetwork.controllers.app.neutron.create_subnet') @@ -396,20 +750,47 @@ class TestKuryr(base.TestKuryrBase): @mock.patch('kuryr_libnetwork.controllers.app.neutron.list_networks') @mock.patch( 'kuryr_libnetwork.controllers.app.driver.get_default_network_id') - def test_network_driver_create_network_with_pool_name_option(self, + @ddt.data((True), (False)) + def test_network_driver_create_network_with_v4_pool_name_option(self, driver_default_net, mock_get_default_network_id, mock_list_networks, mock_create_network, mock_add_tag, mock_create_subnet, mock_list_subnetpools, mock_list_subnets): - fake_kuryr_subnetpool_id = uuidutils.generate_uuid() - fake_name = "fake_pool_name" - kuryr_subnetpools = self._get_fake_v4_subnetpools( - fake_kuryr_subnetpool_id, name=fake_name) + """Test for create network with v4 subnetpool name""" + fake_kuryr_v4_subnetpool_id = uuidutils.generate_uuid() + fake_v4_pool_name = "fake_pool_name" + kuryr_v4_subnetpools = self._get_fake_v4_subnetpools( + fake_kuryr_v4_subnetpool_id, name=fake_v4_pool_name) + docker_network_id = lib_utils.get_hash() + network_request = { + 'NetworkID': docker_network_id, + 'IPv4Data': [{ + 'AddressSpace': 'foo', + 'Pool': '192.168.42.0/24', + 'Gateway': '192.168.42.1/24', + }], + 'IPv6Data': [{ + 'AddressSpace': 'bar', + 'Pool': 'fe80::/64', + 'Gateway': 'fe80::f816:3eff:fe20:57c3/64', + }], + 'Options': { + 'com.docker.network.enable_ipv6': True, + 'com.docker.network.generic': { + 'neutron.pool.name': fake_v4_pool_name + } + } + } + fake_kuryr_v6_subnetpool_id = uuidutils.generate_uuid() + fake_v6_pool_name = lib_utils.get_neutron_subnetpool_name( + network_request['IPv6Data'][0]['Pool']) + kuryr_v6_subnetpools = self._get_fake_v6_subnetpools( + fake_kuryr_v6_subnetpool_id, name=fake_v6_pool_name) # The following fake response is retrieved from the Neutron doc: # http://developer.openstack.org/api-ref-networking-v2.html#createNetwork # noqa - fake_neutron_net_id = "4e8e5957-649f-477b-9e5b-f1f75b21c03c" + fake_neutron_net_id = uuidutils.generate_uuid() driver_value = fake_neutron_net_id if driver_default_net else None mock_get_default_network_id.return_value = driver_value @@ -451,7 +832,7 @@ class TestKuryr(base.TestKuryrBase): } fake_cidr_v4 = '192.168.42.0/24' - fake_subnet_request = { + fake_v4_subnet_request = { "subnets": [{ 'name': utils.make_subnet_name(fake_cidr_v4), 'network_id': fake_neutron_net_id, @@ -459,40 +840,44 @@ class TestKuryr(base.TestKuryrBase): 'cidr': fake_cidr_v4, 'enable_dhcp': app.enable_dhcp, 'gateway_ip': '192.168.42.1', - 'subnetpool_id': fake_kuryr_subnetpool_id, + 'subnetpool_id': fake_kuryr_v4_subnetpool_id, }] } subnet_v4_id = uuidutils.generate_uuid() fake_v4_subnet = self._get_fake_v4_subnet( fake_neutron_net_id, subnet_v4_id, name=fake_cidr_v4, cidr=fake_cidr_v4) - fake_subnet_response = { + + fake_cidr_v6 = 'fe80::/64' + fake_v6_subnet_request = { + "subnets": [{ + 'name': utils.make_subnet_name(fake_cidr_v6), + 'network_id': fake_neutron_net_id, + 'ip_version': 6, + 'cidr': fake_cidr_v6, + 'enable_dhcp': app.enable_dhcp, + 'gateway_ip': 'fe80::f816:3eff:fe20:57c3', + 'subnetpool_id': fake_kuryr_v6_subnetpool_id + }] + } + + subnet_v6_id = uuidutils.generate_uuid() + fake_v6_subnet = self._get_fake_v6_subnet( + fake_neutron_net_id, subnet_v6_id, + fake_kuryr_v6_subnetpool_id, + name=fake_cidr_v6, cidr=fake_cidr_v6) + fake_v4_v6_subnets_response = { 'subnets': [ - fake_v4_subnet['subnet'] + fake_v4_subnet['subnet'], + fake_v6_subnet['subnet'] ] } - network_request = { - 'NetworkID': docker_network_id, - 'IPv4Data': [{ - 'AddressSpace': 'foo', - 'Pool': '192.168.42.0/24', - 'Gateway': '192.168.42.1/24', - }], - 'IPv6Data': [{ - 'AddressSpace': 'bar', - 'Pool': 'fe80::/64', - 'Gateway': 'fe80::f816:3eff:fe20:57c3/64', - }], - 'Options': { - 'com.docker.network.enable_ipv6': False, - 'com.docker.network.generic': { - 'neutron.pool.name': 'fake_pool_name' - } - } - } - mock_create_subnet.return_value = fake_subnet_response - mock_list_subnetpools.return_value = {'subnetpools': - kuryr_subnetpools['subnetpools']} + + mock_create_subnet.return_value = fake_v4_v6_subnets_response + mock_list_subnetpools.side_effect = [ + {'subnetpools': kuryr_v4_subnetpools['subnetpools']}, + {'subnetpools': kuryr_v6_subnetpools['subnetpools']} + ] mock_list_subnets.return_value = fake_existing_subnets_response response = self.app.post('/NetworkDriver.CreateNetwork', content_type='application/json', @@ -512,10 +897,14 @@ class TestKuryr(base.TestKuryrBase): else: mock_create_network.assert_called_with(fake_create_network_request) - mock_create_subnet.assert_called_with(fake_subnet_request) - mock_list_subnetpools.assert_called_with(name=fake_name) - mock_list_subnets.assert_called_with(network_id=fake_neutron_net_id, + mock_create_subnet.assert_any_call(fake_v4_subnet_request) + mock_create_subnet.assert_any_call(fake_v6_subnet_request) + mock_list_subnetpools.assert_any_call(name=fake_v4_pool_name) + mock_list_subnetpools.assert_any_call(name=fake_v6_pool_name) + mock_list_subnets.assert_any_call(network_id=fake_neutron_net_id, cidr=fake_cidr_v4) + mock_list_subnets.assert_any_call(network_id=fake_neutron_net_id, + cidr=fake_cidr_v6) decoded_json = jsonutils.loads(response.data) self.assertEqual(constants.SCHEMA['SUCCESS'], decoded_json) @@ -533,10 +922,35 @@ class TestKuryr(base.TestKuryrBase): mock_list_networks, mock_create_network, mock_add_tag, mock_create_subnet, mock_list_subnetpools, mock_list_subnets): - fake_kuryr_subnetpool_id = uuidutils.generate_uuid() - kuryr_subnetpools = self._get_fake_v4_subnetpools( - fake_kuryr_subnetpool_id) + fake_kuryr_v4_subnetpool_id = uuidutils.generate_uuid() + kuryr_v4_subnetpools = self._get_fake_v4_subnetpools( + fake_kuryr_v4_subnetpool_id) + docker_network_id = lib_utils.get_hash() + network_request = { + 'NetworkID': docker_network_id, + 'IPv4Data': [{ + 'AddressSpace': 'foo', + 'Pool': '192.168.1.0/24', + 'Gateway': '192.168.1.1/24', + }], + 'IPv6Data': [{ + 'AddressSpace': 'bar', + 'Pool': 'fe80::/64', + 'Gateway': 'fe80::f816:3eff:fe20:57c3/64', + }], + 'Options': { + 'com.docker.network.enable_ipv6': False, + 'com.docker.network.generic': { + 'neutron.pool.uuid': fake_kuryr_v4_subnetpool_id, + } + } + } + fake_kuryr_v6_subnetpool_id = uuidutils.generate_uuid() + fake_v6_pool_name = lib_utils.get_neutron_subnetpool_name( + network_request['IPv6Data'][0]['Pool']) + kuryr_v6_subnetpools = self._get_fake_v6_subnetpools( + fake_kuryr_v6_subnetpool_id, name=fake_v6_pool_name) # The following fake response is retrieved from the Neutron doc: # http://developer.openstack.org/api-ref-networking-v2.html#createNetwork # noqa @@ -580,50 +994,54 @@ class TestKuryr(base.TestKuryrBase): fake_existing_subnets_response = { "subnets": [] } - fake_cidr_v4 = '192.168.42.0/24' + fake_cidr_v4 = '192.168.1.0/24' - fake_subnet_request = { + fake_v4_subnet_request = { "subnets": [{ 'name': utils.make_subnet_name(fake_cidr_v4), 'network_id': fake_neutron_net_id, 'ip_version': 4, 'cidr': fake_cidr_v4, 'enable_dhcp': app.enable_dhcp, - 'gateway_ip': '192.168.42.1', - 'subnetpool_id': fake_kuryr_subnetpool_id, + 'gateway_ip': '192.168.1.1', + 'subnetpool_id': fake_kuryr_v4_subnetpool_id, }] } subnet_v4_id = uuidutils.generate_uuid() fake_v4_subnet = self._get_fake_v4_subnet( fake_neutron_net_id, subnet_v4_id, + fake_kuryr_v4_subnetpool_id, name=fake_cidr_v4, cidr=fake_cidr_v4) - fake_subnet_response = { + + fake_cidr_v6 = 'fe80::/64' + fake_v6_subnet_request = { + "subnets": [{ + 'name': utils.make_subnet_name(fake_cidr_v6), + 'network_id': fake_neutron_net_id, + 'ip_version': 6, + 'cidr': fake_cidr_v6, + 'enable_dhcp': app.enable_dhcp, + 'gateway_ip': 'fe80::f816:3eff:fe20:57c3', + 'subnetpool_id': fake_kuryr_v6_subnetpool_id + }] + } + + subnet_v6_id = uuidutils.generate_uuid() + fake_v6_subnet = self._get_fake_v6_subnet( + fake_neutron_net_id, subnet_v6_id, + fake_kuryr_v6_subnetpool_id, + name=fake_cidr_v6, cidr=fake_cidr_v6) + fake_v4_v6_subnets_response = { 'subnets': [ - fake_v4_subnet['subnet'] + fake_v4_subnet['subnet'], + fake_v6_subnet['subnet'] ] } - network_request = { - 'NetworkID': docker_network_id, - 'IPv4Data': [{ - 'AddressSpace': 'foo', - 'Pool': '192.168.42.0/24', - 'Gateway': '192.168.42.1/24', - }], - 'IPv6Data': [{ - 'AddressSpace': 'bar', - 'Pool': 'fe80::/64', - 'Gateway': 'fe80::f816:3eff:fe20:57c3/64', - }], - 'Options': { - 'com.docker.network.enable_ipv6': False, - 'com.docker.network.generic': { - 'neutron.pool.uuid': fake_kuryr_subnetpool_id, - } - } - } - mock_create_subnet.return_value = fake_subnet_response - mock_list_subnetpools.return_value = {'subnetpools': - kuryr_subnetpools['subnetpools']} + mock_create_subnet.return_value = fake_v4_v6_subnets_response + mock_list_subnetpools.side_effect = [ + {'subnetpools': kuryr_v4_subnetpools['subnetpools']}, + {'subnetpools': kuryr_v6_subnetpools['subnetpools']} + ] mock_list_subnets.return_value = fake_existing_subnets_response response = self.app.post('/NetworkDriver.CreateNetwork', content_type='application/json', @@ -643,10 +1061,13 @@ class TestKuryr(base.TestKuryrBase): else: mock_create_network.assert_called_with(fake_create_network_request) - mock_create_subnet.assert_called_with(fake_subnet_request) - mock_list_subnetpools.assert_called_with(id=fake_kuryr_subnetpool_id) - mock_list_subnets.assert_called_with(network_id=fake_neutron_net_id, + mock_create_subnet.assert_any_call(fake_v4_subnet_request) + mock_create_subnet.assert_any_call(fake_v6_subnet_request) + mock_list_subnetpools.assert_any_call(id=fake_kuryr_v4_subnetpool_id) + mock_list_subnets.assert_any_call(network_id=fake_neutron_net_id, cidr=fake_cidr_v4) + mock_list_subnets.assert_any_call(network_id=fake_neutron_net_id, + cidr=fake_cidr_v6) decoded_json = jsonutils.loads(response.data) self.assertEqual(constants.SCHEMA['SUCCESS'], decoded_json) @@ -679,15 +1100,21 @@ class TestKuryr(base.TestKuryrBase): 'Options': {} } - fake_kuryr_subnetpool_id = uuidutils.generate_uuid() - fake_name = lib_utils.get_neutron_subnetpool_name( + fake_kuryr_v4_subnetpool_id = uuidutils.generate_uuid() + fake_v4_pool_name = lib_utils.get_neutron_subnetpool_name( network_request['IPv4Data'][0]['Pool']) - kuryr_subnetpools = self._get_fake_v4_subnetpools( - fake_kuryr_subnetpool_id, name=fake_name) + kuryr_v4_subnetpools = self._get_fake_v4_subnetpools( + fake_kuryr_v4_subnetpool_id, name=fake_v4_pool_name) + + fake_kuryr_v6_subnetpool_id = uuidutils.generate_uuid() + fake_v6_pool_name = lib_utils.get_neutron_subnetpool_name( + network_request['IPv6Data'][0]['Pool']) + kuryr_v6_subnetpools = self._get_fake_v6_subnetpools( + fake_kuryr_v6_subnetpool_id, name=fake_v6_pool_name) # The following fake response is retrieved from the Neutron doc: # http://developer.openstack.org/api-ref-networking-v2.html#createNetwork # noqa - fake_neutron_net_id = "4e8e5957-649f-477b-9e5b-f1f75b21c03c" + fake_neutron_net_id = uuidutils.generate_uuid() driver_value = fake_neutron_net_id if driver_default_net else None mock_get_default_network_id.return_value = driver_value @@ -729,29 +1156,51 @@ class TestKuryr(base.TestKuryrBase): } fake_cidr_v4 = '192.168.42.0/24' - fake_subnet_request = { + fake_v4_subnet_request = { "subnets": [{ 'name': utils.make_subnet_name(fake_cidr_v4), 'network_id': fake_neutron_net_id, 'ip_version': 4, 'cidr': fake_cidr_v4, 'enable_dhcp': app.enable_dhcp, - 'subnetpool_id': fake_kuryr_subnetpool_id + 'subnetpool_id': fake_kuryr_v4_subnetpool_id }] } subnet_v4_id = uuidutils.generate_uuid() fake_v4_subnet = self._get_fake_v4_subnet( fake_neutron_net_id, subnet_v4_id, - fake_kuryr_subnetpool_id, + fake_kuryr_v4_subnetpool_id, name=fake_cidr_v4, cidr=fake_cidr_v4) - fake_subnet_response = { + + fake_cidr_v6 = 'fe80::/64' + fake_v6_subnet_request = { + "subnets": [{ + 'name': utils.make_subnet_name(fake_cidr_v6), + 'network_id': fake_neutron_net_id, + 'ip_version': 6, + 'cidr': fake_cidr_v6, + 'enable_dhcp': app.enable_dhcp, + 'gateway_ip': 'fe80::f816:3eff:fe20:57c3', + 'subnetpool_id': fake_kuryr_v6_subnetpool_id + }] + } + + subnet_v6_id = uuidutils.generate_uuid() + fake_v6_subnet = self._get_fake_v6_subnet( + fake_neutron_net_id, subnet_v6_id, + fake_kuryr_v6_subnetpool_id, + name=fake_cidr_v6, cidr=fake_cidr_v6) + fake_v4_v6_subnets_response = { 'subnets': [ - fake_v4_subnet['subnet'] + fake_v4_subnet['subnet'], + fake_v6_subnet['subnet'] ] } - mock_create_subnet.return_value = fake_subnet_response - mock_list_subnetpools.return_value = {'subnetpools': - kuryr_subnetpools['subnetpools']} + mock_create_subnet.return_value = fake_v4_v6_subnets_response + mock_list_subnetpools.side_effect = [ + {'subnetpools': kuryr_v4_subnetpools['subnetpools']}, + {'subnetpools': kuryr_v6_subnetpools['subnetpools']} + ] mock_list_subnets.return_value = fake_existing_subnets_response response = self.app.post('/NetworkDriver.CreateNetwork', @@ -773,10 +1222,14 @@ class TestKuryr(base.TestKuryrBase): else: mock_create_network.assert_called_with(fake_create_network_request) - mock_create_subnet.assert_called_with(fake_subnet_request) - mock_list_subnetpools.assert_called_with(name=fake_name) - mock_list_subnets.assert_called_with(network_id=fake_neutron_net_id, + mock_create_subnet.assert_any_call(fake_v4_subnet_request) + mock_create_subnet.assert_any_call(fake_v6_subnet_request) + mock_list_subnetpools.assert_any_call(name=fake_v4_pool_name) + mock_list_subnetpools.assert_any_call(name=fake_v6_pool_name) + mock_list_subnets.assert_any_call(network_id=fake_neutron_net_id, cidr=fake_cidr_v4) + mock_list_subnets.assert_any_call(network_id=fake_neutron_net_id, + cidr=fake_cidr_v6) self.assertEqual(constants.SCHEMA['SUCCESS'], decoded_json) @mock.patch('kuryr_libnetwork.controllers.app.neutron.list_networks') @@ -803,24 +1256,33 @@ class TestKuryr(base.TestKuryrBase): } } - fake_kuryr_subnetpool_id = uuidutils.generate_uuid() - fake_name = lib_utils.get_neutron_subnetpool_name( + fake_kuryr_v4_subnetpool_id = uuidutils.generate_uuid() + fake_v4_pool_name = lib_utils.get_neutron_subnetpool_name( network_request['IPv4Data'][0]['Pool']) - kuryr_subnetpools = self._get_fake_v4_subnetpools( - fake_kuryr_subnetpool_id, name=fake_name) + kuryr_v4_subnetpools = self._get_fake_v4_subnetpools( + fake_kuryr_v4_subnetpool_id, name=fake_v4_pool_name) + + fake_kuryr_v6_subnetpool_id = uuidutils.generate_uuid() + fake_v6_pool_name = lib_utils.get_neutron_subnetpool_name( + network_request['IPv6Data'][0]['Pool']) + kuryr_v6_subnetpools = self._get_fake_v6_subnetpools( + fake_kuryr_v6_subnetpool_id, name=fake_v6_pool_name) fake_existing_networks_response = { "networks": [] } mock_list_networks.return_value = fake_existing_networks_response - mock_list_subnetpools.return_value = {'subnetpools': - kuryr_subnetpools['subnetpools']} + mock_list_subnetpools.side_effect = [ + {'subnetpools': kuryr_v4_subnetpools['subnetpools']}, + {'subnetpools': kuryr_v6_subnetpools['subnetpools']} + ] response = self.app.post('/NetworkDriver.CreateNetwork', content_type='application/json', data=jsonutils.dumps(network_request)) self.assertEqual(500, response.status_code) mock_list_networks.assert_called_with(id=fake_neutron_net_id) - mock_list_subnetpools.assert_called_with(name=fake_name) + mock_list_subnetpools.assert_any_call(name=fake_v4_pool_name) + mock_list_subnetpools.assert_any_call(name=fake_v6_pool_name) decoded_json = jsonutils.loads(response.data) self.assertIn('Err', decoded_json) err_message = ("Specified network id/name({0}) does not " @@ -850,24 +1312,34 @@ class TestKuryr(base.TestKuryrBase): } } } - fake_kuryr_subnetpool_id = uuidutils.generate_uuid() - fake_name = lib_utils.get_neutron_subnetpool_name( + + fake_kuryr_v4_subnetpool_id = uuidutils.generate_uuid() + fake_v4_pool_name = lib_utils.get_neutron_subnetpool_name( network_request['IPv4Data'][0]['Pool']) - kuryr_subnetpools = self._get_fake_v4_subnetpools( - fake_kuryr_subnetpool_id, name=fake_name) + kuryr_v4_subnetpools = self._get_fake_v4_subnetpools( + fake_kuryr_v4_subnetpool_id, name=fake_v4_pool_name) + + fake_kuryr_v6_subnetpool_id = uuidutils.generate_uuid() + fake_v6_pool_name = lib_utils.get_neutron_subnetpool_name( + network_request['IPv6Data'][0]['Pool']) + kuryr_v6_subnetpools = self._get_fake_v6_subnetpools( + fake_kuryr_v6_subnetpool_id, name=fake_v6_pool_name) fake_existing_networks_response = { "networks": [] } mock_list_networks.return_value = fake_existing_networks_response - mock_list_subnetpools.return_value = {'subnetpools': - kuryr_subnetpools['subnetpools']} + mock_list_subnetpools.side_effect = [ + {'subnetpools': kuryr_v4_subnetpools['subnetpools']}, + {'subnetpools': kuryr_v6_subnetpools['subnetpools']} + ] response = self.app.post('/NetworkDriver.CreateNetwork', content_type='application/json', data=jsonutils.dumps(network_request)) self.assertEqual(500, response.status_code) mock_list_networks.assert_called_with(name=fake_neutron_network_name) - mock_list_subnetpools.assert_called_with(name=fake_name) + mock_list_subnetpools.assert_any_call(name=fake_v4_pool_name) + mock_list_subnetpools.assert_any_call(name=fake_v6_pool_name) decoded_json = jsonutils.loads(response.data) self.assertIn('Err', decoded_json) err_message = ("Specified network id/name({0}) does not " diff --git a/kuryr_libnetwork/tests/unit/test_kuryr_existing_network.py b/kuryr_libnetwork/tests/unit/test_kuryr_existing_network.py index d64aebee..0aafb6d1 100644 --- a/kuryr_libnetwork/tests/unit/test_kuryr_existing_network.py +++ b/kuryr_libnetwork/tests/unit/test_kuryr_existing_network.py @@ -83,13 +83,21 @@ class TestKuryrNetworkPreExisting(base.TestKuryrBase): } } - fake_subnetpool_name = lib_utils.get_neutron_subnetpool_name( + fake_kuryr_v4_subnetpool_id = uuidutils.generate_uuid() + fake_v4_pool_name = lib_utils.get_neutron_subnetpool_name( network_request['IPv4Data'][0]['Pool']) - kuryr_subnetpools = self._get_fake_v4_subnetpools( - fake_kuryr_subnetpool_id, name=fake_subnetpool_name) - mock_list_subnetpools.return_value = { - 'subnetpools': kuryr_subnetpools['subnetpools']} + kuryr_v4_subnetpools = self._get_fake_v4_subnetpools( + fake_kuryr_v4_subnetpool_id, name=fake_v4_pool_name) + fake_kuryr_v6_subnetpool_id = uuidutils.generate_uuid() + fake_v6_pool_name = lib_utils.get_neutron_subnetpool_name( + network_request['IPv6Data'][0]['Pool']) + kuryr_v6_subnetpools = self._get_fake_v6_subnetpools( + fake_kuryr_v6_subnetpool_id, name=fake_v6_pool_name) + mock_list_subnetpools.side_effect = [ + {'subnetpools': kuryr_v4_subnetpools['subnetpools']}, + {'subnetpools': kuryr_v6_subnetpools['subnetpools']} + ] mock_list_networks.return_value = fake_response fake_existing_subnets_response = { @@ -98,7 +106,7 @@ class TestKuryrNetworkPreExisting(base.TestKuryrBase): fake_cidr_v4 = '192.168.42.0/24' mock_list_subnets.return_value = fake_existing_subnets_response - fake_subnet_request = { + fake_v4_subnet_request = { "subnets": [{ 'name': utils.make_subnet_name(fake_cidr_v4), 'network_id': fake_neutron_net_id, @@ -106,7 +114,7 @@ class TestKuryrNetworkPreExisting(base.TestKuryrBase): 'cidr': fake_cidr_v4, 'enable_dhcp': mock_tag.enable_dhcp, 'gateway_ip': '192.168.42.1', - 'subnetpool_id': fake_kuryr_subnetpool_id + 'subnetpool_id': fake_kuryr_v4_subnetpool_id }] } subnet_v4_id = uuidutils.generate_uuid() @@ -114,24 +122,47 @@ class TestKuryrNetworkPreExisting(base.TestKuryrBase): fake_neutron_net_id, subnet_v4_id, fake_kuryr_subnetpool_id, name=fake_cidr_v4, cidr=fake_cidr_v4) - fake_subnet_response = { + fake_cidr_v6 = 'fe80::/64' + fake_v6_subnet_request = { + "subnets": [{ + 'name': utils.make_subnet_name(fake_cidr_v6), + 'network_id': fake_neutron_net_id, + 'ip_version': 6, + 'cidr': fake_cidr_v6, + 'enable_dhcp': mock_tag.enable_dhcp, + 'gateway_ip': 'fe80::f816:3eff:fe20:57c3', + 'subnetpool_id': fake_kuryr_v6_subnetpool_id + }] + } + + subnet_v6_id = uuidutils.generate_uuid() + fake_v6_subnet = self._get_fake_v6_subnet( + fake_neutron_net_id, subnet_v6_id, + fake_kuryr_v6_subnetpool_id, + name=fake_cidr_v6, cidr=fake_cidr_v6) + fake_v4_v6_subnets_response = { 'subnets': [ - fake_v4_subnet['subnet'] + fake_v4_subnet['subnet'], + fake_v6_subnet['subnet'] ] } - mock_create_subnet.return_value = fake_subnet_response + mock_create_subnet.return_value = fake_v4_v6_subnets_response response = self.app.post('/NetworkDriver.CreateNetwork', content_type='application/json', data=jsonutils.dumps(network_request)) self.assertEqual(200, response.status_code) - mock_list_subnetpools.assert_called_with(name=fake_subnetpool_name) + mock_list_subnetpools.assert_any_call(name=fake_v4_pool_name) + mock_list_subnetpools.assert_any_call(name=fake_v6_pool_name) mock_list_networks.assert_called_with(id=fake_neutron_net_id) - mock_list_subnets.assert_called_with( + mock_list_subnets.assert_any_call( network_id=fake_neutron_net_id, cidr=fake_cidr_v4) - mock_create_subnet.assert_called_with(fake_subnet_request) + mock_list_subnets.assert_any_call(network_id=fake_neutron_net_id, + cidr=fake_cidr_v6) + mock_create_subnet.assert_any_call(fake_v4_subnet_request) + mock_create_subnet.assert_any_call(fake_v6_subnet_request) if mock_tag.tag: tags = utils.create_net_tags(docker_network_id) for tag in tags: @@ -191,31 +222,47 @@ class TestKuryrNetworkPreExisting(base.TestKuryrBase): } } - fake_kuryr_subnetpool_id = uuidutils.generate_uuid() - fake_subnetpool_name = lib_utils.get_neutron_subnetpool_name( + fake_kuryr_v4_subnetpool_id = uuidutils.generate_uuid() + fake_v4_pool_name = lib_utils.get_neutron_subnetpool_name( network_request['IPv4Data'][0]['Pool']) - kuryr_subnetpools = self._get_fake_v4_subnetpools( - fake_kuryr_subnetpool_id, name=fake_subnetpool_name) - mock_list_subnetpools.return_value = { - 'subnetpools': kuryr_subnetpools['subnetpools']} + kuryr_v4_subnetpools = self._get_fake_v4_subnetpools( + fake_kuryr_v4_subnetpool_id, name=fake_v4_pool_name) + + fake_kuryr_v6_subnetpool_id = uuidutils.generate_uuid() + fake_v6_pool_name = lib_utils.get_neutron_subnetpool_name( + network_request['IPv6Data'][0]['Pool']) + kuryr_v6_subnetpools = self._get_fake_v6_subnetpools( + fake_kuryr_v6_subnetpool_id, name=fake_v6_pool_name) + mock_list_subnetpools.side_effect = [ + {'subnetpools': kuryr_v4_subnetpools['subnetpools']}, + {'subnetpools': kuryr_v6_subnetpools['subnetpools']} + ] subnet_v4_id = uuidutils.generate_uuid() + subnet_v6_id = uuidutils.generate_uuid() fake_cidr_v4 = '192.168.42.0/24' + fake_cidr_v6 = 'fe80::/64' fake_v4_subnet = self._get_fake_v4_subnet( fake_neutron_net_id, docker_endpoint_id="fake_id", subnet_v4_id=subnet_v4_id, - subnetpool_id=fake_kuryr_subnetpool_id, + subnetpool_id=fake_kuryr_v4_subnetpool_id, cidr=fake_cidr_v4) + fake_v6_subnet = self._get_fake_v6_subnet( + fake_neutron_net_id, + docker_endpoint_id="fake_id", + subnet_v6_id=subnet_v6_id, + subnetpool_id=fake_kuryr_v6_subnetpool_id, + cidr=fake_cidr_v6) - fake_existing_subnets_response = { - 'subnets': [ - fake_v4_subnet['subnet'] - ] - } - mock_list_subnets.return_value = fake_existing_subnets_response + fake_existing_subnets_response = [ + {'subnets': [fake_v4_subnet['subnet']]}, + {'subnets': [fake_v6_subnet['subnet']]} + ] + mock_list_subnets.side_effect = fake_existing_subnets_response fake_response['networks'][0]['subnets'].append(subnet_v4_id) + fake_response['networks'][0]['subnets'].append(subnet_v6_id) mock_list_networks.return_value = fake_response def mock_exception(*args, **kwargs): @@ -228,11 +275,15 @@ class TestKuryrNetworkPreExisting(base.TestKuryrBase): data=jsonutils.dumps(network_request)) self.assertEqual(200, response.status_code) - mock_list_subnetpools.assert_called_with(name=fake_subnetpool_name) + mock_list_subnetpools.assert_any_call(name=fake_v4_pool_name) + mock_list_subnetpools.assert_any_call(name=fake_v6_pool_name) mock_list_networks.assert_called_with(id=fake_neutron_net_id) - mock_list_subnets.assert_called_with( + mock_list_subnets.assert_any_call( network_id=fake_neutron_net_id, cidr=fake_cidr_v4) + mock_list_subnets.assert_any_call( + network_id=fake_neutron_net_id, + cidr=fake_cidr_v6) if mock_tag.tag: tags = utils.create_net_tags(docker_network_id) @@ -241,9 +292,12 @@ class TestKuryrNetworkPreExisting(base.TestKuryrBase): 'networks', fake_neutron_net_id, tag) mock_add_tag.assert_any_call('networks', fake_neutron_net_id, const.KURYR_EXISTING_NEUTRON_NET) - mock_add_tag.assert_called_with('subnets', - subnet_v4_id, - fake_kuryr_subnetpool_id) + mock_add_tag.assert_any_call('subnets', + subnet_v4_id, + fake_kuryr_v4_subnetpool_id) + mock_add_tag.assert_any_call('subnets', + subnet_v6_id, + fake_kuryr_v6_subnetpool_id) else: mock_update_network.assert_called_with( fake_neutron_net_id, {'network': diff --git a/kuryr_libnetwork/tests/unit/test_kuryr_network.py b/kuryr_libnetwork/tests/unit/test_kuryr_network.py index c4a12348..c856b8fc 100644 --- a/kuryr_libnetwork/tests/unit/test_kuryr_network.py +++ b/kuryr_libnetwork/tests/unit/test_kuryr_network.py @@ -60,13 +60,21 @@ class TestKuryrNetworkCreateFailures(base.TestKuryrFailures): 'Options': {} } - fake_subnetpool_name = lib_utils.get_neutron_subnetpool_name( + fake_kuryr_v4_subnetpool_id = uuidutils.generate_uuid() + fake_v4_pool_name = lib_utils.get_neutron_subnetpool_name( network_request['IPv4Data'][0]['Pool']) - fake_kuryr_subnetpool_id = uuidutils.generate_uuid() - kuryr_subnetpools = self._get_fake_v4_subnetpools( - fake_kuryr_subnetpool_id, name=fake_subnetpool_name) - mock_list_subnetpools.return_value = { - 'subnetpools': kuryr_subnetpools['subnetpools']} + kuryr_v4_subnetpools = self._get_fake_v4_subnetpools( + fake_kuryr_v4_subnetpool_id, name=fake_v4_pool_name) + + fake_kuryr_v6_subnetpool_id = uuidutils.generate_uuid() + fake_v6_pool_name = lib_utils.get_neutron_subnetpool_name( + network_request['IPv6Data'][0]['Pool']) + kuryr_v6_subnetpools = self._get_fake_v6_subnetpools( + fake_kuryr_v6_subnetpool_id, name=fake_v6_pool_name) + mock_list_subnetpools.side_effect = [ + {'subnetpools': kuryr_v4_subnetpools['subnetpools']}, + {'subnetpools': kuryr_v6_subnetpools['subnetpools']} + ] fake_request = { "network": { @@ -78,7 +86,8 @@ class TestKuryrNetworkCreateFailures(base.TestKuryrFailures): response = self._invoke_create_request(network_request) self.assertEqual(401, response.status_code) decoded_json = jsonutils.loads(response.data) - mock_list_subnetpools.assert_called_with(name=fake_subnetpool_name) + mock_list_subnetpools.assert_any_call(name=fake_v4_pool_name) + mock_list_subnetpools.assert_any_call(name=fake_v6_pool_name) mock_create_network.assert_called_with(fake_request) self.assertIn('Err', decoded_json) self.assertEqual( @@ -107,19 +116,28 @@ class TestKuryrNetworkCreateFailures(base.TestKuryrFailures): 'Options': {} } - fake_subnetpool_name = lib_utils.get_neutron_subnetpool_name( + fake_kuryr_v4_subnetpool_id = uuidutils.generate_uuid() + fake_v4_pool_name = lib_utils.get_neutron_subnetpool_name( network_request['IPv4Data'][0]['Pool']) - fake_kuryr_subnetpool_id = uuidutils.generate_uuid() - kuryr_subnetpools = self._get_fake_v4_subnetpools( - fake_kuryr_subnetpool_id, name=fake_subnetpool_name) - mock_list_subnetpools.return_value = { - 'subnetpools': kuryr_subnetpools['subnetpools']} + kuryr_v4_subnetpools = self._get_fake_v4_subnetpools( + fake_kuryr_v4_subnetpool_id, name=fake_v4_pool_name) + + fake_kuryr_v6_subnetpool_id = uuidutils.generate_uuid() + fake_v6_pool_name = lib_utils.get_neutron_subnetpool_name( + network_request['IPv6Data'][0]['Pool']) + kuryr_v6_subnetpools = self._get_fake_v6_subnetpools( + fake_kuryr_v6_subnetpool_id, name=fake_v6_pool_name) + mock_list_subnetpools.side_effect = [ + {'subnetpools': kuryr_v4_subnetpools['subnetpools']}, + {'subnetpools': kuryr_v6_subnetpools['subnetpools']} + ] mock_get_default_network_id.side_effect = exceptions.Unauthorized response = self._invoke_create_request(network_request) self.assertEqual(401, response.status_code) decoded_json = jsonutils.loads(response.data) - mock_list_subnetpools.assert_called_with(name=fake_subnetpool_name) + mock_list_subnetpools.assert_any_call(name=fake_v4_pool_name) + mock_list_subnetpools.assert_any_call(name=fake_v6_pool_name) mock_get_default_network_id.assert_called() self.assertIn('Err', decoded_json) self.assertEqual( diff --git a/releasenotes/notes/add_support_ipv6_subnet-a024ffd6f7acc883.yaml b/releasenotes/notes/add_support_ipv6_subnet-a024ffd6f7acc883.yaml new file mode 100644 index 00000000..c7c76f9e --- /dev/null +++ b/releasenotes/notes/add_support_ipv6_subnet-a024ffd6f7acc883.yaml @@ -0,0 +1,10 @@ +--- +features: + - | + Support creating an IPv6 subnet from Docker client. Users can pass --ipv6 and + IPv6 subnet attribute in CLI. +issues: + - | + Docker version 1.12 and 1.13 have problem to pass ipv6 tag [1], according to + this limitation, current support is for dual-stack only. + [1] https://github.com/docker/docker/issues/28055