From 709fede534ec15e4f260c78019083aff9cf19282 Mon Sep 17 00:00:00 2001 From: vikaschoudhary16 Date: Fri, 17 Jun 2016 16:23:34 +0530 Subject: [PATCH] Short-Term fix for overlapping cidrs using docker options A quick alternative solution, until PRs to upstream Docker get accepted, can be Docker user passing pool name to both Kuryr ipam driver and Kuryr network driver using corresponding network and ipam options respectively: $sudo docker network create --driver=kuryr --ipam-driver=kuryr \ --subnet 10.0.0.0/16 --ip-range 10.0.0.0/24 \ -o neutron.pool.name=neutron_pool1 \ --ipam-opt=neutron.pool.name=neutron_pool1 \ foo eddb51ebca09339cb17aaec05e48ffe60659ced6f3fc41b020b0eb506d364 Now Docker user creates another network with same cidr as the previous, i.e 10.0.0.0/16, but with different pool name, neutron_pool2: $sudo docker network create --driver=kuryr --ipam-driver=kuryr \ --subnet 10.0.0.0/16 --ip-range 10.0.0.0/24 \ -o neutron.pool.name=neutron_pool2 \ --ipam-opt=neutron.pool.name=neutron_pool2 \ bar 397badb51ebca09339cb17aaec05e48ffe60659ced6f3fc41b020b0eb506d786 At ``/IpamDriver.RequestAddress``, correct subnet will be filtered out using corresponding pool name received from the libnetwork as explained above. Please refer https://review.openstack.org/#/c/326894/6 TODO: unit test cases covering docker option scenario DocImpact Change-Id: I7090027e68e8c78219a387da66e1bd30be900ab1 Closes-bug: #1585572 --- kuryr/common/constants.py | 1 + kuryr/controllers.py | 146 ++++++++---- kuryr/tests/unit/base.py | 18 +- kuryr/tests/unit/test_kuryr.py | 280 ++++++++++++++++++++++++ kuryr/tests/unit/test_kuryr_endpoint.py | 6 +- kuryr/tests/unit/test_kuryr_ipam.py | 100 ++++++++- 6 files changed, 490 insertions(+), 61 deletions(-) diff --git a/kuryr/common/constants.py b/kuryr/common/constants.py index 640e1a84..797ef064 100644 --- a/kuryr/common/constants.py +++ b/kuryr/common/constants.py @@ -43,3 +43,4 @@ NETWORK_GENERIC_OPTIONS = 'com.docker.network.generic' NEUTRON_UUID_OPTION = 'neutron.net.uuid' NEUTRON_NAME_OPTION = 'neutron.net.name' KURYR_EXISTING_NEUTRON_NET = 'kuryr.net.existing' +NEUTRON_POOL_NAME_OPTION = 'neutron.pool.name' diff --git a/kuryr/controllers.py b/kuryr/controllers.py index f64de894..1cd59695 100755 --- a/kuryr/controllers.py +++ b/kuryr/controllers.py @@ -171,11 +171,6 @@ def _get_networks_by_attrs(**attrs): def _get_subnets_by_attrs(**attrs): subnets = app.neutron.list_subnets(**attrs) - if len(subnets.get('subnets', [])) > 2: # subnets for IPv4 and/or IPv6 - raise exceptions.DuplicatedResourceException( - "Multiple Neutron subnets exist for the params {0} " - .format(', '.join(['{0}={1}'.format(k, v) - for k, v in attrs.items()]))) return subnets['subnets'] @@ -214,6 +209,11 @@ def _get_subnets_by_interface_cidr(neutron_network_id, str(cidr.prefixlen)]) subnets = _get_subnets_by_attrs( network_id=neutron_network_id, cidr=subnet_cidr) + if len(subnets) > 2: + raise exceptions.DuplicatedResourceException( + "Multiple Neutron subnets exist for the network_id={0}" + "and cidr={1}" + .format(neutron_network_id, cidr)) return subnets @@ -512,12 +512,24 @@ def network_driver_create_network(): neutron_uuid = None neutron_name = None + pool_name = '' + 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) + + 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)) if not neutron_uuid and not neutron_name: network = app.neutron.create_network( @@ -565,6 +577,11 @@ def network_driver_create_network(): subnet_cidr = '/'.join([subnet_network, str(cidr.prefixlen)]) subnets = _get_subnets_by_attrs( network_id=network_id, cidr=subnet_cidr) + if len(subnets) > 1: + raise exceptions.DuplicatedResourceException( + "Multiple Neutron subnets exist for the network_id={0}" + "and cidr={1}".format(network_id, cidr)) + if not subnets: new_subnets = [{ 'name': pool_cidr, @@ -573,6 +590,8 @@ def network_driver_create_network(): 'cidr': subnet_cidr, 'enable_dhcp': app.enable_dhcp, }] + if pool_id: + new_subnets[0]['subnetpool_id'] = pool_id if gateway_ip: new_subnets[0]['gateway_ip'] = gateway_ip @@ -642,6 +661,10 @@ def network_driver_delete_network(): neutron_network_id = filtered_networks[0]['id'] filtered_subnets = _get_subnets_by_attrs( network_id=neutron_network_id) + if len(filtered_subnets) > 2: # subnets for IPv4 and/or IPv6 + raise exceptions.DuplicatedResourceException( + "Multiple Neutron subnets exist for the network_id={0} " + .format(neutron_network_id)) for subnet in filtered_subnets: try: subnetpool_id = subnet.get('subnetpool_id', None) @@ -836,6 +859,10 @@ def network_driver_join(): .format(neutron_port_name)) neutron_port = filtered_ports[0] all_subnets = _get_subnets_by_attrs(network_id=neutron_network_id) + if len(all_subnets) > 2: # subnets for IPv4 and/or IPv6 + raise exceptions.DuplicatedResourceException( + "Multiple Neutron subnets exist for the network_id={0} " + .format(neutron_network_id)) try: ifname, peer_name, (stdout, stderr) = binding.port_bind( @@ -1046,6 +1073,11 @@ def ipam_request_pool(): v6 = json_data['V6'] pool_id = '' subnet_cidr = '' + pool_name = '' + pools = [] + options = json_data.get('Options') + if options: + pool_name = options.get(const.NEUTRON_POOL_NAME_OPTION) if requested_pool: app.logger.info(_LI("Creating subnetpool with the given pool CIDR")) if requested_subpool: @@ -1053,11 +1085,14 @@ def ipam_request_pool(): else: cidr = netaddr.IPNetwork(requested_pool) subnet_cidr = _get_subnet_cidr_using_cidr(cidr) - pool_name = utils.get_neutron_subnetpool_name(subnet_cidr) - # Check if requested pool already exist - pools = _get_subnetpools_by_attrs(name=pool_name) - if pools: - pool_id = pools[0]['id'] + if not pool_name: + pool_name = utils.get_neutron_subnetpool_name(subnet_cidr) + pools = _get_subnetpools_by_attrs(name=pool_name) + if len(pools): + raise exceptions.KuryrException( + "Another pool with same cidr exist. ipam and network" + " options not used to pass pool name") + if not pools: new_subnetpool = { 'name': pool_name, @@ -1085,6 +1120,7 @@ def ipam_request_pool(): subnet_cidr = _get_subnet_cidr_using_cidr(cidr) else: app.logger.error(_LE("Default neutron pools not found.")) + req_pool_res = {'PoolID': pool_id, 'Pool': subnet_cidr} return flask.jsonify(req_pool_res) @@ -1123,6 +1159,7 @@ def ipam_request_address(): is_gateway = False allocated_address = '' subnet_cidr = '' + subnet = {} pool_prefix_len = '' pools = _get_subnetpools_by_attrs(id=pool_id) if pools: @@ -1143,56 +1180,64 @@ def ipam_request_address(): "No subnetpools with id {0} is found." .format(pool_id)) # check if any subnet with matching cidr is present - subnets = _get_subnets_by_attrs(cidr=subnet_cidr) + subnets_by_cidr = _get_subnets_by_attrs(cidr=subnet_cidr) # Check if the port is gateway options = json_data.get('Options') if options: request_address_type = options.get(const.REQUEST_ADDRESS_TYPE) if request_address_type == const.NETWORK_GATEWAY_OPTIONS: is_gateway = True - if subnets: - subnet = subnets[0] - if is_gateway: + if subnets_by_cidr: + if len(subnets_by_cidr) > 1: + for tmp_subnet in subnets_by_cidr: + if tmp_subnet.get('subnetpool_id', '') == pool_id: + subnet = tmp_subnet + if not any(subnet) and not is_gateway: + raise exceptions.KuryrException( + ("Subnet with cidr({0}) and pool {1}, does not " + "exist.").format(cidr, pool_id)) + else: + subnet = subnets_by_cidr[0] + + if is_gateway: + if any(subnet): # check if request gateway ip same with existed gateway ip existed_gateway_ip = subnet.get('gateway_ip', '') if req_address == existed_gateway_ip: - allocated_address = '/'.join([req_address, pool_prefix_len]) + allocated_address = '/'.join( + [req_address, pool_prefix_len]) else: raise exceptions.GatewayConflictFailure( "Requested gateway {0} does not match with " "gateway {1} in existed " "network.".format(req_address, existed_gateway_ip)) else: - # allocating address for container port - neutron_network_id = subnet['network_id'] - try: - port = { - 'name': 'kuryr-unbound-port', - 'admin_state_up': True, - 'network_id': neutron_network_id, - 'binding:host_id': utils.get_hostname(), - } - fixed_ips = port['fixed_ips'] = [] - fixed_ip = {'subnet_id': subnet['id']} - if req_address: - fixed_ip['ip_address'] = req_address - fixed_ips.append(fixed_ip) - created_port_resp = app.neutron.create_port({'port': port}) - created_port = created_port_resp['port'] - allocated_address = created_port['fixed_ips'][0]['ip_address'] - allocated_address = '/'.join( - [allocated_address, str(cidr.prefixlen)]) - except n_exceptions.NeutronClientException as ex: - app.logger.error(_LE("Error happened during ip allocation on " - "Neutron side: %s"), ex) - raise - else: - # Auxiliary address or gw_address is received at network creation time. - # This address cannot be reserved with neutron at this time as subnet - # is not created yet. In /NetworkDriver.CreateNetwork this address will - # be reserved with neutron. - if req_address: allocated_address = '/'.join([req_address, pool_prefix_len]) + else: + # allocating address for container port + neutron_network_id = subnet['network_id'] + try: + port = { + 'name': 'kuryr-unbound-port', + 'admin_state_up': True, + 'network_id': neutron_network_id, + 'binding:host_id': utils.get_hostname(), + } + fixed_ips = port['fixed_ips'] = [] + fixed_ip = {'subnet_id': subnet['id']} + if req_address: + fixed_ip['ip_address'] = req_address + fixed_ips.append(fixed_ip) + created_port_resp = app.neutron.create_port({'port': port}) + created_port = created_port_resp['port'] + app.logger.debug("created port %s", created_port) + allocated_address = created_port['fixed_ips'][0]['ip_address'] + allocated_address = '/'.join( + [allocated_address, str(cidr.prefixlen)]) + except n_exceptions.NeutronClientException as ex: + app.logger.error(_LE("Error happened during ip allocation on " + "Neutron side: %s"), ex) + raise return flask.jsonify({'Address': allocated_address}) @@ -1277,7 +1322,18 @@ def ipam_release_address(): if not len(subnets): app.logger.info(_LI("Subnet already deleted.")) return flask.jsonify(const.SCHEMA['SUCCESS']) - subnet = subnets[0] + if len(subnets) > 1: + subnet = {} + for tmp_subnet in subnets: + if tmp_subnet['subnetpool_id'] == pool_id: + subnet = tmp_subnet + if not subnet: + raise exceptions.KuryrException( + ("Subnet with cidr({0}) and pool {1}, does not " + "exist.").format(cidr, pool_id)) + else: + subnet = subnets[0] + cidr_address = netaddr.IPNetwork(rel_address) rcvd_fixed_ips = [] fixed_ip = {'subnet_id': subnet['id']} diff --git a/kuryr/tests/unit/base.py b/kuryr/tests/unit/base.py index 138b165a..80a84210 100644 --- a/kuryr/tests/unit/base.py +++ b/kuryr/tests/unit/base.py @@ -140,8 +140,8 @@ class TestKuryrBase(TestCase): # The following fake response is retrieved from the Neutron doc: # http://developer.openstack.org/api-ref-networking-v2.html#createSubnet # noqa fake_subnet_response = { - "subnets": [{ - "name": '-'.join([docker_endpoint_id, '192.168.1.0']), + "subnets": [ + {"name": '-'.join([docker_endpoint_id, '192.168.1.0']), "network_id": neutron_network_id, "tenant_id": "c1210485b2424d48804aad5d39c61b8f", "allocation_pools": [{"start": "192.168.1.2", @@ -150,9 +150,9 @@ class TestKuryrBase(TestCase): "ip_version": 4, "cidr": "192.168.1.0/24", "id": fake_neutron_subnet_v4_id, - "enable_dhcp": True - }, { - "name": '-'.join([docker_endpoint_id, 'fe80::']), + "enable_dhcp": True, + "subnetpool_id": ''}, + {"name": '-'.join([docker_endpoint_id, 'fe80::']), "network_id": neutron_network_id, "tenant_id": "c1210485b2424d48804aad5d39c61b8f", "allocation_pools": [{"start": "fe80::f816:3eff:fe20:57c4", @@ -161,8 +161,9 @@ class TestKuryrBase(TestCase): "ip_version": 6, "cidr": "fe80::/64", "id": fake_neutron_subnet_v6_id, - "enable_dhcp": True - }] + "enable_dhcp": True, + "subnetpool_id": ''} + ] } return fake_subnet_response @@ -243,7 +244,8 @@ class TestKuryrBase(TestCase): "ip_version": 4, "cidr": '192.168.1.0/24', "id": subnet_v4_id, - "enable_dhcp": True + "enable_dhcp": True, + "subnetpool_id": '' } } if subnetpool_id: diff --git a/kuryr/tests/unit/test_kuryr.py b/kuryr/tests/unit/test_kuryr.py index 213dff16..19874886 100644 --- a/kuryr/tests/unit/test_kuryr.py +++ b/kuryr/tests/unit/test_kuryr.py @@ -144,6 +144,286 @@ class TestKuryr(base.TestKuryrBase): decoded_json = jsonutils.loads(response.data) self.assertEqual(constants.SCHEMA['SUCCESS'], decoded_json) + def test_network_driver_create_network_with_net_name_option(self): + docker_network_id = utils.get_hash() + fake_neutron_net_id = "4e8e5957-649f-477b-9e5b-f1f75b21c03c" + self.mox.StubOutWithMock(app.neutron, "list_networks") + fake_neutron_net_name = 'my_network_name' + fake_existing_networks_response = { + "networks": [{ + "status": "ACTIVE", + "subnets": [], + "admin_state_up": True, + "tenant_id": "9bacb3c5d39d41a79512987f338cf177", + "router:external": False, + "segments": [], + "shared": False, + "id": fake_neutron_net_id, + "name": "my_network_name" + }] + } + app.neutron.list_networks( + name=fake_neutron_net_name).AndReturn( + fake_existing_networks_response) + + self.mox.StubOutWithMock(app.neutron, "add_tag") + tags = utils.create_net_tags(docker_network_id) + for tag in tags: + app.neutron.add_tag('networks', fake_neutron_net_id, tag) + + app.neutron.add_tag( + 'networks', fake_neutron_net_id, 'kuryr.net.existing') + self.mox.StubOutWithMock(app.neutron, 'list_subnets') + fake_existing_subnets_response = { + "subnets": [] + } + fake_cidr_v4 = '192.168.42.0/24' + app.neutron.list_subnets( + network_id=fake_neutron_net_id, + cidr=fake_cidr_v4).AndReturn(fake_existing_subnets_response) + + self.mox.StubOutWithMock(app.neutron, 'create_subnet') + fake_subnet_request = { + "subnets": [{ + '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', + }] + } + subnet_v4_id = str(uuid.uuid4()) + 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 = { + 'subnets': [ + fake_v4_subnet['subnet'] + ] + } + app.neutron.create_subnet( + fake_subnet_request).AndReturn(fake_subnet_response) + + self.mox.ReplayAll() + + 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.net.name': 'my_network_name' + } + } + } + response = self.app.post('/NetworkDriver.CreateNetwork', + content_type='application/json', + data=jsonutils.dumps(network_request)) + + self.assertEqual(200, response.status_code) + decoded_json = jsonutils.loads(response.data) + self.assertEqual(constants.SCHEMA['SUCCESS'], decoded_json) + + def test_network_driver_create_network_with_netid_option(self): + docker_network_id = utils.get_hash() + fake_neutron_net_id = "4e8e5957-649f-477b-9e5b-f1f75b21c03c" + self.mox.StubOutWithMock(app.neutron, "list_networks") + fake_existing_networks_response = { + "networks": [{ + "status": "ACTIVE", + "subnets": [], + "admin_state_up": True, + "tenant_id": "9bacb3c5d39d41a79512987f338cf177", + "router:external": False, + "segments": [], + "shared": False, + "id": fake_neutron_net_id, + }] + } + app.neutron.list_networks( + id=fake_neutron_net_id).AndReturn( + fake_existing_networks_response) + + self.mox.StubOutWithMock(app.neutron, "add_tag") + tags = utils.create_net_tags(docker_network_id) + for tag in tags: + app.neutron.add_tag('networks', fake_neutron_net_id, tag) + + app.neutron.add_tag( + 'networks', fake_neutron_net_id, 'kuryr.net.existing') + self.mox.StubOutWithMock(app.neutron, 'list_subnets') + fake_existing_subnets_response = { + "subnets": [] + } + fake_cidr_v4 = '192.168.42.0/24' + app.neutron.list_subnets( + network_id=fake_neutron_net_id, + cidr=fake_cidr_v4).AndReturn(fake_existing_subnets_response) + + self.mox.StubOutWithMock(app.neutron, 'create_subnet') + fake_subnet_request = { + "subnets": [{ + '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', + }] + } + subnet_v4_id = str(uuid.uuid4()) + 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 = { + 'subnets': [ + fake_v4_subnet['subnet'] + ] + } + app.neutron.create_subnet( + fake_subnet_request).AndReturn(fake_subnet_response) + + self.mox.ReplayAll() + + 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.net.uuid': '4e8e5957-649f-477b-9e5b-f1f75b21c03c' + } + } + } + response = self.app.post('/NetworkDriver.CreateNetwork', + content_type='application/json', + data=jsonutils.dumps(network_request)) + + self.assertEqual(200, response.status_code) + decoded_json = jsonutils.loads(response.data) + self.assertEqual(constants.SCHEMA['SUCCESS'], decoded_json) + + def test_network_driver_create_network_with_pool_name_option(self): + + self.mox.StubOutWithMock(app.neutron, 'list_subnetpools') + fake_kuryr_subnetpool_id = str(uuid.uuid4()) + fake_name = "fake_pool_name" + kuryr_subnetpools = self._get_fake_v4_subnetpools( + fake_kuryr_subnetpool_id, name=fake_name) + app.neutron.list_subnetpools(name=fake_name).AndReturn( + {'subnetpools': kuryr_subnetpools['subnetpools']}) + docker_network_id = utils.get_hash() + self.mox.StubOutWithMock(app.neutron, "create_network") + fake_request = { + "network": { + "name": utils.make_net_name(docker_network_id), + "admin_state_up": True + } + } + # 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_response = { + "network": { + "status": "ACTIVE", + "subnets": [], + "name": utils.make_net_name(docker_network_id), + "admin_state_up": True, + "tenant_id": "9bacb3c5d39d41a79512987f338cf177", + "router:external": False, + "segments": [], + "shared": False, + "id": fake_neutron_net_id + } + } + app.neutron.create_network(fake_request).AndReturn(fake_response) + + self.mox.StubOutWithMock(app.neutron, "add_tag") + tags = utils.create_net_tags(docker_network_id) + for tag in tags: + app.neutron.add_tag('networks', fake_neutron_net_id, tag) + + self.mox.StubOutWithMock(app.neutron, 'list_subnets') + fake_existing_subnets_response = { + "subnets": [] + } + fake_cidr_v4 = '192.168.42.0/24' + app.neutron.list_subnets( + network_id=fake_neutron_net_id, + cidr=fake_cidr_v4).AndReturn(fake_existing_subnets_response) + + self.mox.StubOutWithMock(app.neutron, 'create_subnet') + fake_subnet_request = { + "subnets": [{ + '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, + }] + } + subnet_v4_id = str(uuid.uuid4()) + 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 = { + 'subnets': [ + fake_v4_subnet['subnet'] + ] + } + app.neutron.create_subnet( + fake_subnet_request).AndReturn(fake_subnet_response) + + self.mox.ReplayAll() + + 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' + } + } + } + response = self.app.post('/NetworkDriver.CreateNetwork', + content_type='application/json', + data=jsonutils.dumps(network_request)) + + self.assertEqual(200, response.status_code) + decoded_json = jsonutils.loads(response.data) + self.assertEqual(constants.SCHEMA['SUCCESS'], decoded_json) + def test_network_driver_create_network_wo_gw(self): docker_network_id = utils.get_hash() self.mox.StubOutWithMock(app.neutron, "create_network") diff --git a/kuryr/tests/unit/test_kuryr_endpoint.py b/kuryr/tests/unit/test_kuryr_endpoint.py index eb47ebfe..48305b17 100644 --- a/kuryr/tests/unit/test_kuryr_endpoint.py +++ b/kuryr/tests/unit/test_kuryr_endpoint.py @@ -39,13 +39,15 @@ class TestKuryrEndpointFailures(base.TestKuryrFailures): 'network_id': neutron_network_id, 'ip_version': 4, "cidr": '192.168.1.0/24', - 'enable_dhcp': 'False' + 'enable_dhcp': 'False', + 'subnetpool_id': '' }, { 'name': '-'.join([docker_endpoint_id, 'fe80::']), 'network_id': neutron_network_id, 'ip_version': 6, "cidr": 'fe80::/64', - 'enable_dhcp': 'False' + 'enable_dhcp': 'False', + 'subnetpool_id': '' }] } fake_subnets = self._get_fake_subnets( diff --git a/kuryr/tests/unit/test_kuryr_ipam.py b/kuryr/tests/unit/test_kuryr_ipam.py index 544f289c..1e17dc9f 100644 --- a/kuryr/tests/unit/test_kuryr_ipam.py +++ b/kuryr/tests/unit/test_kuryr_ipam.py @@ -94,23 +94,33 @@ class TestKuryrIpam(base.TestKuryrBase): decoded_json = jsonutils.loads(response.data) self.assertEqual(fake_kuryr_subnetpool_id, decoded_json['PoolID']) - def test_ipam_driver_request_pool_with_default_v4pool(self): + def test_ipam_driver_request_pool_with_pool_name_option(self): self.mox.StubOutWithMock(app.neutron, 'list_subnetpools') fake_kuryr_subnetpool_id = str(uuid.uuid4()) - fake_name = 'kuryr' + fake_name = 'fake_pool_name' + new_subnetpool = { + 'name': fake_name, + 'default_prefixlen': 16, + 'prefixes': [FAKE_IP4_CIDR]} + kuryr_subnetpools = self._get_fake_v4_subnetpools( fake_kuryr_subnetpool_id, prefixes=[FAKE_IP4_CIDR], name=fake_name) - app.neutron.list_subnetpools(name=fake_name).AndReturn( - {'subnetpools': kuryr_subnetpools['subnetpools']}) + fake_subnetpool_response = { + 'subnetpool': kuryr_subnetpools['subnetpools'][0] + } + + self.mox.StubOutWithMock(app.neutron, 'create_subnetpool') + app.neutron.create_subnetpool( + {'subnetpool': new_subnetpool}).AndReturn(fake_subnetpool_response) self.mox.ReplayAll() fake_request = { 'AddressSpace': '', - 'Pool': '', + 'Pool': FAKE_IP4_CIDR, 'SubPool': '', # In the case --ip-range is not given - 'Options': {}, + 'Options': {'neutron.pool.name': 'fake_pool_name'}, 'V6': False } response = self.app.post('/IpamDriver.RequestPool', @@ -228,6 +238,84 @@ class TestKuryrIpam(base.TestKuryrBase): decoded_json = jsonutils.loads(response.data) self.assertEqual('10.0.0.5/16', decoded_json['Address']) + def test_ipam_driver_request_address_overlapping_cidr(self): + # faking list_subnetpools + self.mox.StubOutWithMock(app.neutron, 'list_subnetpools') + + fake_kuryr_subnetpool_id = str(uuid.uuid4()) + fake_kuryr_subnetpool_id2 = str(uuid.uuid4()) + + fake_name = utils.get_neutron_subnetpool_name(FAKE_IP4_CIDR) + kuryr_subnetpools = self._get_fake_v4_subnetpools( + fake_kuryr_subnetpool_id, prefixes=[FAKE_IP4_CIDR], + name=fake_name) + app.neutron.list_subnetpools(id=fake_kuryr_subnetpool_id).AndReturn( + kuryr_subnetpools) + + # faking list_subnets + docker_endpoint_id = utils.get_hash() + + neutron_network_id = str(uuid.uuid4()) + neutron_network_id2 = str(uuid.uuid4()) + + neutron_subnet_v4_id = str(uuid.uuid4()) + neutron_subnet_v4_id2 = str(uuid.uuid4()) + + fake_v4_subnet = self._get_fake_v4_subnet( + neutron_network_id, docker_endpoint_id, neutron_subnet_v4_id, + subnetpool_id=fake_kuryr_subnetpool_id, + cidr=FAKE_IP4_CIDR) + + fake_v4_subnet2 = self._get_fake_v4_subnet( + neutron_network_id2, docker_endpoint_id, neutron_subnet_v4_id2, + subnetpool_id=fake_kuryr_subnetpool_id2, + cidr=FAKE_IP4_CIDR) + + fake_subnet_response = { + 'subnets': [ + fake_v4_subnet2['subnet'], + fake_v4_subnet['subnet'] + ] + } + self.mox.StubOutWithMock(app.neutron, 'list_subnets') + app.neutron.list_subnets(cidr=FAKE_IP4_CIDR).AndReturn( + fake_subnet_response) + # faking create_port + fake_neutron_port_id = str(uuid.uuid4()) + fake_port = base.TestKuryrBase._get_fake_port( + docker_endpoint_id, neutron_network_id, + fake_neutron_port_id, + neutron_subnet_v4_id=neutron_subnet_v4_id, + neutron_subnet_v4_address="10.0.0.5") + port_request = { + 'name': 'kuryr-unbound-port', + 'admin_state_up': True, + 'network_id': neutron_network_id, + 'binding:host_id': utils.get_hostname(), + } + port_request['fixed_ips'] = [] + fixed_ip = {'subnet_id': neutron_subnet_v4_id} + port_request['fixed_ips'].append(fixed_ip) + self.mox.StubOutWithMock(app.neutron, 'create_port') + app.neutron.create_port({'port': port_request}).AndReturn(fake_port) + + # Apply mocks + self.mox.ReplayAll() + + # Testing container ip allocation + fake_request = { + 'PoolID': fake_kuryr_subnetpool_id, + 'Address': '', # Querying for container address + 'Options': {} + } + response = self.app.post('/IpamDriver.RequestAddress', + content_type='application/json', + data=jsonutils.dumps(fake_request)) + + self.assertEqual(200, response.status_code) + decoded_json = jsonutils.loads(response.data) + self.assertEqual('10.0.0.5/16', decoded_json['Address']) + def test_ipam_driver_request_address_for_same_gateway(self): # faking list_subnetpools self.mox.StubOutWithMock(app.neutron, 'list_subnetpools')