From 3e4e6a612f2c3b328aade283c85786033a50b10c Mon Sep 17 00:00:00 2001 From: Liping Mao Date: Tue, 9 Aug 2016 16:15:14 +0800 Subject: [PATCH] RequestAddress when subnet has not been created gw_address is received in IpamDriver.RequestAddress when create network. In Docker 1.12+, it will have NETWORK_GATEWAY_OPTIONS, but in lower docker version, such as 1.9.1, it will not carry this option. This change check if subnet exist first, this makes it can work for both docker version. Change-Id: I1c741172e927179f83284e8a23afce5f07b130f9 Closes-bug: #1611261 --- kuryr_libnetwork/controllers.py | 110 +++++++++--------- .../tests/unit/test_kuryr_ipam.py | 35 ++++++ 2 files changed, 93 insertions(+), 52 deletions(-) diff --git a/kuryr_libnetwork/controllers.py b/kuryr_libnetwork/controllers.py index 718de8ef..038314ff 100755 --- a/kuryr_libnetwork/controllers.py +++ b/kuryr_libnetwork/controllers.py @@ -1318,8 +1318,8 @@ def ipam_request_address(): else: subnet = subnets_by_cidr[0] - if is_gateway: - if any(subnet): + if any(subnet): + if is_gateway: # check if request gateway ip same with existed gateway ip existed_gateway_ip = subnet.get('gateway_ip', '') if req_address == existed_gateway_ip: @@ -1331,59 +1331,65 @@ def ipam_request_address(): "gateway {1} in existed " "network.".format(req_address, existed_gateway_ip)) else: - 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']} - num_ports = 0 - if req_address: - fixed_ip['ip_address'] = req_address - fixed_ip_existing = [('subnet_id=%s' % subnet['id'])] - fixed_ip_existing.append('ip_address=%s' % str(req_address)) - filtered_ports = app.neutron.list_ports( - fixed_ips=fixed_ip_existing) - num_ports = len(filtered_ports.get('ports', [])) - fixed_ips.append(fixed_ip) + # 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']} + num_ports = 0 + if req_address: + fixed_ip['ip_address'] = req_address + fixed_ip_existing = [('subnet_id=%s' % subnet['id'])] + fixed_ip_existing.append('ip_address=' + '%s' % str(req_address)) + filtered_ports = app.neutron.list_ports( + fixed_ips=fixed_ip_existing) + num_ports = len(filtered_ports.get('ports', [])) + fixed_ips.append(fixed_ip) - if num_ports: - existing_port = filtered_ports['ports'][0] - created_port_resp = {'port': existing_port} - host = existing_port.get('binding:host_id') - vif_type = existing_port.get('binding:vif_type') - if not host and vif_type == 'unbound': - updated_port = { - 'admin_state_up': True, - 'binding:host_id': utils.get_hostname(), - } - created_port_resp = app.neutron.update_port( - existing_port['id'], - {'port': updated_port}) + if num_ports: + existing_port = filtered_ports['ports'][0] + created_port_resp = {'port': existing_port} + host = existing_port.get('binding:host_id') + vif_type = existing_port.get('binding:vif_type') + if not host and vif_type == 'unbound': + updated_port = { + 'admin_state_up': True, + 'binding:host_id': utils.get_hostname(), + } + created_port_resp = app.neutron.update_port( + existing_port['id'], + {'port': updated_port}) + else: + raise exceptions.AddressInUseException( + "Requested ip address {0} already belongs to a " + "bound Neutron port: {1}".format(fixed_ip, + existing_port['id'])) else: - raise exceptions.AddressInUseException( - "Requested ip address {0} already belongs to a bound " - "Neutron port: {1}".format(fixed_ip, - existing_port['id'])) - else: - created_port_resp = app.neutron.create_port({'port': port}) + 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 + 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 + 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]) return flask.jsonify({'Address': allocated_address}) diff --git a/kuryr_libnetwork/tests/unit/test_kuryr_ipam.py b/kuryr_libnetwork/tests/unit/test_kuryr_ipam.py index 87686199..0c52a8a9 100644 --- a/kuryr_libnetwork/tests/unit/test_kuryr_ipam.py +++ b/kuryr_libnetwork/tests/unit/test_kuryr_ipam.py @@ -238,6 +238,41 @@ 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_when_subnet_not_exist(self): + requested_address = '10.0.0.5' + # faking list_subnetpools + self.mox.StubOutWithMock(app.neutron, 'list_subnetpools') + fake_kuryr_subnetpool_id = 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 + fake_subnet_response = {'subnets': []} + self.mox.StubOutWithMock(app.neutron, 'list_subnets') + app.neutron.list_subnets(cidr=FAKE_IP4_CIDR).AndReturn( + fake_subnet_response) + + # Apply mocks + self.mox.ReplayAll() + + # Testing container ip allocation + fake_request = { + 'PoolID': fake_kuryr_subnetpool_id, + 'Address': requested_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(requested_address + '/16', decoded_json['Address']) + @ddt.data((False), (True)) def test_ipam_driver_request_specific_address(self, existing_port): requested_address = '10.0.0.5'