Delete port created by kuryr

In current code, we get the port's subnet by checking the
subnet's subnetpool_id equal with the given PoolID, but in
existing environment, the subnet may lack subnetpool_id,
hence we will not get the port's subnet and leaves the
kuryr-libnetwork port undeleted.

This patch checks the port's device owner and determines
the given POOL's subnet_id and ip_address is equal to
port's fixed_ips attribute.

Change-Id: I736adbb2409b50a5e11d3aa7d31778944cfdeae1
Co-Authored-By: Liping Mao <limao@cisco.com>
Closes-Bug: #1651015
This commit is contained in:
Dongcan Ye 2016-12-20 14:20:57 +08:00
parent e5f7672a20
commit 4af7e1cdd8
4 changed files with 138 additions and 83 deletions

View File

@ -1500,29 +1500,15 @@ def ipam_release_address():
app.logger.info(_LI("Subnet already deleted."))
return flask.jsonify(const.SCHEMA['SUCCESS'])
subnet = {}
for tmp_subnet in subnets:
if tmp_subnet['subnetpool_id'] == pool_id:
subnet = tmp_subnet
break
if not subnet:
app.logger.info(_LI("Subnet already deleted."))
return flask.jsonify(const.SCHEMA['SUCCESS'])
iface = ipaddress.ip_interface(six.text_type(rel_address))
rcvd_fixed_ips = []
fixed_ip = {'subnet_id': subnet['id']}
fixed_ip['ip_address'] = six.text_type(iface.ip)
rcvd_fixed_ips.append(fixed_ip)
rel_ip_address = six.text_type(iface.ip)
try:
filtered_ports = []
all_ports = app.neutron.list_ports()
all_ports = app.neutron.list_ports(device_owner=lib_const.DEVICE_OWNER)
for port in all_ports['ports']:
if port['fixed_ips'] == rcvd_fixed_ips:
filtered_ports.append(port)
for port in filtered_ports:
app.neutron.delete_port(port['id'])
for tmp_subnet in subnets:
if (port['fixed_ips'][0]['subnet_id'] == tmp_subnet['id'] and
port['fixed_ips'][0]['ip_address'] == rel_ip_address):
app.neutron.delete_port(port['id'])
except n_exceptions.NeutronClientException as ex:
app.logger.error(_LE("Error happened while fetching "
"and deleting port, %s"), ex)

View File

@ -0,0 +1,118 @@
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
from kuryr.lib import constants as lib_const
from kuryr.lib import utils as lib_utils
from kuryr_libnetwork.tests.fullstack import kuryr_base
from kuryr_libnetwork import utils
class IpamTest(kuryr_base.KuryrBaseTest):
"""Test Container IPAM related operations
Test container IPAM operations(request/release pool|address)
"""
def test_container_ipam_release_address(self):
# pre-created Neutron network and subnet
neutron_net_name = lib_utils.get_random_string(8)
neutron_network = self.neutron_client.create_network(
{'network': {'name': neutron_net_name,
"admin_state_up": True}})
neutron_subnet_name = lib_utils.get_random_string(8)
subnet_param = [{
'name': neutron_subnet_name,
'network_id': neutron_network['network']['id'],
'ip_version': 4,
'cidr': "10.10.0.0/24",
'enable_dhcp': True,
}]
self.neutron_client.create_subnet({'subnets': subnet_param})
fake_ipam = {
"Driver": "kuryr",
"Options": {},
"Config": [
{
"Subnet": "10.10.0.0/16",
"IPRange": "10.10.0.0/24",
"Gateway": "10.10.0.1"
}
]
}
# Create docker network using existing Neutron network
options = {'neutron.net.name': neutron_net_name}
container_net_name = lib_utils.get_random_string(8)
container_net = self.docker_client.create_network(
name=container_net_name,
driver='kuryr',
options=options,
ipam=fake_ipam)
container_net_id = container_net.get('Id')
try:
networks = self.neutron_client.list_networks(
tags=utils.make_net_tags(container_net_id))
except Exception as e:
self.docker_client.remove_network(container_net_id)
message = ("Failed to list neutron networks: %s")
self.fail(message % e.args[0])
self.assertEqual(1, len(networks['networks']))
# Boot a container, and connect to the docker network.
container_name = lib_utils.get_random_string(8)
container = self.docker_client.create_container(
image='kuryr/busybox',
command='/bin/sleep 600',
hostname='kuryr_test_container',
name=container_name)
warn_msg = container.get('Warning')
container_id = container.get('Id')
self.assertIsNone(warn_msg, 'Warn in creating container')
self.assertIsNotNone(container_id, 'Create container id must not '
'be None')
self.docker_client.start(container=container_id)
self.docker_client.connect_container_to_network(container_id,
container_net_id)
try:
ports = self.neutron_client.list_ports(
network_id=neutron_network['network']['id'])
except Exception as e:
self.docker_client.disconnect_container_from_network(
container_id,
container_net_id)
message = ("Failed to list neutron ports: %s")
self.fail(message % e.args[0])
# A dhcp port gets created as well; dhcp is enabled by default
self.assertEqual(2, len(ports['ports']))
# Find the kuryr port
kuryr_port_param = {"network_id": neutron_network['network']['id'],
"device_owner": lib_const.DEVICE_OWNER}
kuryr_ports = self.neutron_client.list_ports(
**kuryr_port_param)
self.assertEqual(1, len(kuryr_ports['ports']))
# Disconnect container from network, this release ip address.
self.docker_client.disconnect_container_from_network(container_id,
container_net_id)
ports = self.neutron_client.list_ports(
network_id=neutron_network['network']['id'])
self.assertEqual(1, len(ports['ports']))
kuryr_ports = self.neutron_client.list_ports(
**kuryr_port_param)
self.assertEqual(0, len(kuryr_ports['ports']))
# Cleanup resources
self.docker_client.stop(container=container_id)
self.docker_client.remove_network(container_net_id)
self.neutron_client.delete_network(neutron_network['network']['id'])

View File

@ -189,7 +189,8 @@ class TestKuryrBase(TestCase):
neutron_subnet_v4_id=None,
neutron_subnet_v6_id=None,
neutron_subnet_v4_address="192.168.1.2",
neutron_subnet_v6_address="fe80::f816:3eff:fe20:57c4"):
neutron_subnet_v6_address="fe80::f816:3eff:fe20:57c4",
device_owner=None):
# The following fake response is retrieved from the Neutron doc:
# http://developer.openstack.org/api-ref-networking-v2.html#createPort # noqa
fake_port = {
@ -200,7 +201,7 @@ class TestKuryrBase(TestCase):
"admin_state_up": True,
"network_id": neutron_network_id,
"tenant_id": "d6700c0c9ffa4f1cb322cd4a1f3906fa",
"device_owner": "",
"device_owner": device_owner,
"mac_address": "fa:16:3e:20:57:c3",
"fixed_ips": [],
"id": neutron_port_id,

View File

@ -571,12 +571,10 @@ class TestKuryrIpam(base.TestKuryrBase):
kuryr_subnetpools = self._get_fake_v4_subnetpools(
fake_kuryr_subnetpool_id, prefixes=[FAKE_IP4_CIDR], name=fake_name)
mock_list_subnetpools.return_value = kuryr_subnetpools
fake_ip4 = '10.0.0.5'
# faking list_subnets
docker_network_id = lib_utils.get_hash()
docker_endpoint_id = lib_utils.get_hash()
neutron_network_id = docker_network_id = uuidutils.generate_uuid()
subnet_v4_id = uuidutils.generate_uuid()
fake_v4_subnet = self._get_fake_v4_subnet(
docker_network_id, docker_endpoint_id, subnet_v4_id,
@ -589,22 +587,20 @@ class TestKuryrIpam(base.TestKuryrBase):
}
mock_list_subnets.return_value = fake_subnet_response
#faking list_ports and delete_port
fake_ip4 = '10.0.0.5'
# faking list_ports and delete_port
neutron_network_id = uuidutils.generate_uuid()
fake_neutron_port_id = uuidutils.generate_uuid()
fake_port = base.TestKuryrBase._get_fake_port(
docker_endpoint_id, neutron_network_id,
fake_neutron_port_id, lib_const.PORT_STATUS_ACTIVE,
subnet_v4_id,
neutron_subnet_v4_address=fake_ip4)
port_request = {
'name': 'demo-port',
'admin_state_up': True,
'network_id': neutron_network_id,
}
rel_fixed_ips = port_request['fixed_ips'] = []
fixed_ip = {'subnet_id': subnet_v4_id}
fixed_ip['ip_address'] = fake_ip4
rel_fixed_ips.append(fixed_ip)
neutron_subnet_v4_address=fake_ip4,
device_owner=lib_const.DEVICE_OWNER)
fake_port['port']['fixed_ips'] = [
{'subnet_id': subnet_v4_id, 'ip_address': fake_ip4}
]
list_port_response = {'ports': [fake_port['port']]}
mock_list_ports.return_value = list_port_response
@ -624,53 +620,7 @@ class TestKuryrIpam(base.TestKuryrBase):
id=fake_kuryr_subnetpool_id)
mock_list_subnets.assert_called_with(
cidr=FAKE_IP4_CIDR)
self.assertTrue(mock_list_ports.called)
mock_list_ports.assert_called_with(
device_owner=lib_const.DEVICE_OWNER)
mock_delete_port.assert_called_with(
fake_port['port']['id'])
@mock.patch('kuryr_libnetwork.controllers.app.neutron.list_subnets')
@mock.patch('kuryr_libnetwork.controllers.app.neutron.list_subnetpools')
def test_ipam_driver_release_address_with_one_subnet_deleted(
self, mock_list_subnetpools, mock_list_subnets):
"""Another unittest for release address.
For those two subnets have same cidrs, one of them had already deleted.
"""
# Faking list_subnetpools
fake_kuryr_subnetpool_id = uuidutils.generate_uuid()
fake_name = str('-'.join(['kuryrPool', FAKE_IP4_CIDR]))
kuryr_subnetpools = self._get_fake_v4_subnetpools(
fake_kuryr_subnetpool_id, prefixes=[FAKE_IP4_CIDR], name=fake_name)
mock_list_subnetpools.return_value = kuryr_subnetpools
# Faking list_subnets
docker_network_id = uuidutils.generate_uuid()
docker_endpoint_id = uuidutils.generate_uuid()
docker_network_id = uuidutils.generate_uuid()
subnet_v4_id = uuidutils.generate_uuid()
# Make two subnets have different subnetpool id
another_fake_kuryr_subnetpool_id = uuidutils.generate_uuid()
fake_v4_subnet = self._get_fake_v4_subnet(
docker_network_id, docker_endpoint_id, subnet_v4_id,
subnetpool_id=another_fake_kuryr_subnetpool_id,
cidr=FAKE_IP4_CIDR)
fake_subnet_response = {
'subnets': [
fake_v4_subnet['subnet']
]
}
mock_list_subnets.return_value = fake_subnet_response
fake_ip4 = '10.0.0.5'
fake_request = {
'PoolID': fake_kuryr_subnetpool_id,
'Address': fake_ip4
}
response = self.app.post('/IpamDriver.ReleaseAddress',
content_type='application/json',
data=jsonutils.dumps(fake_request))
self.assertEqual(200, response.status_code)
mock_list_subnetpools.assert_called_with(id=fake_kuryr_subnetpool_id)
mock_list_subnets.assert_called_with(cidr=FAKE_IP4_CIDR)