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')