From c9782faa9a5dda329b6c2c13457350548a652f27 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Harald=20Jens=C3=A5s?= Date: Mon, 3 Jun 2019 22:35:41 +0200 Subject: [PATCH] Add tests for L3 conntrack helper API tests for L3 Conntrack Helper plugin. Related-Bug: #1823633 Depends-On: https://review.opendev.org/670837 Change-Id: Ie085100f508f7a1cdb0fd4efbcffa1e2b485fbba --- .zuul.yaml | 3 + neutron_tempest_plugin/api/base.py | 54 +++++++ .../api/test_conntrack_helper.py | 135 ++++++++++++++++++ .../services/network/json/network_client.py | 43 ++++++ 4 files changed, 235 insertions(+) create mode 100644 neutron_tempest_plugin/api/test_conntrack_helper.py diff --git a/.zuul.yaml b/.zuul.yaml index 44de1bfb..c79e02b8 100644 --- a/.zuul.yaml +++ b/.zuul.yaml @@ -27,6 +27,7 @@ - dns-integration - empty-string-filtering - expose-port-forwarding-in-fip + - expose-l3-conntrack-helper - ext-gw-mode - external-net - extra_dhcp_opt @@ -38,6 +39,7 @@ - floating-ip-port-forwarding - floatingip-pools - ip-substring-filtering + - l3-conntrack-helper - l3-flavors - l3-ha - l3_agent_scheduler @@ -100,6 +102,7 @@ neutron-uplink-status-propagation: true neutron-network-segment-range: true neutron-port-forwarding: true + neutron-conntrack-helper: true devstack_local_conf: post-config: $NEUTRON_CONF: diff --git a/neutron_tempest_plugin/api/base.py b/neutron_tempest_plugin/api/base.py index 71a0e5ee..4441dd16 100644 --- a/neutron_tempest_plugin/api/base.py +++ b/neutron_tempest_plugin/api/base.py @@ -137,6 +137,7 @@ class BaseNetworkTest(test.BaseTestCase): cls.keypairs = [] cls.trunks = [] cls.network_segment_ranges = [] + cls.conntrack_helpers = [] @classmethod def resource_cleanup(cls): @@ -153,6 +154,10 @@ class BaseNetworkTest(test.BaseTestCase): for floating_ip in cls.floating_ips: cls._try_delete_resource(cls.delete_floatingip, floating_ip) + # Clean up conntrack helpers + for cth in cls.conntrack_helpers: + cls._try_delete_resource(cls.delete_conntrack_helper, cth) + # Clean up routers for router in cls.routers: cls._try_delete_resource(cls.delete_router, @@ -960,6 +965,55 @@ class BaseNetworkTest(test.BaseTestCase): client.delete_trunk(trunk['id']) + @classmethod + def create_conntrack_helper(cls, router_id, helper, protocol, port, + client=None): + """Create a conntrack helper + + Create a conntrack helper and schedule it for later deletion. If a + client is passed, then it is used for deleteing the CTH too. + + :param router_id: The ID of the Neutron router associated to the + conntrack helper. + + :param helper: The conntrack helper module alias + + :param protocol: The conntrack helper IP protocol used in the conntrack + helper. + + :param port: The conntrack helper IP protocol port number for the + conntrack helper. + + :param client: network client to be used for creating and cleaning up + the conntrack helper. + """ + + client = client or cls.client + + cth = client.create_conntrack_helper(router_id, helper, protocol, + port)['conntrack_helper'] + + # save ID of router associated with conntrack helper for final cleanup + cth['router_id'] = router_id + + # save client to be used later in cls.delete_conntrack_helper for final + # cleanup + cth['client'] = client + cls.conntrack_helpers.append(cth) + return cth + + @classmethod + def delete_conntrack_helper(cls, cth, client=None): + """Delete conntrack helper + + :param client: Client to be used + If client is not given it will use the client used to create the + conntrack helper, or cls.client if unknown. + """ + + client = client or cth.get('client') or cls.client + client.delete_conntrack_helper(cth['router_id'], cth['id']) + class BaseAdminNetworkTest(BaseNetworkTest): diff --git a/neutron_tempest_plugin/api/test_conntrack_helper.py b/neutron_tempest_plugin/api/test_conntrack_helper.py new file mode 100644 index 00000000..900851a6 --- /dev/null +++ b/neutron_tempest_plugin/api/test_conntrack_helper.py @@ -0,0 +1,135 @@ +# Copyright (c) 2019 Red Hat, Inc. +# All rights reserved. +# +# 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 tempest.common import utils +from tempest.lib.common.utils import data_utils +from tempest.lib import decorators +from tempest.lib import exceptions + +from neutron_tempest_plugin.api import base +from neutron_tempest_plugin import config + +CONF = config.CONF + + +class ConntrackHelperTestJSON(base.BaseNetworkTest): + + required_extensions = ['router', 'l3-conntrack-helper', + 'expose-l3-conntrack-helper'] + + @classmethod + def resource_setup(cls): + super(ConntrackHelperTestJSON, cls).resource_setup() + cls.ext_net_id = CONF.network.public_network_id + + # Create network, subnet + cls.network = cls.create_network() + cls.subnet = cls.create_subnet(cls.network) + + @decorators.idempotent_id('6361c80e-902d-4c2a-88b4-ea8066507eee') + def test_create_list_update_show_delete_conntrack_helper(self): + # Create a router + router = self.create_router(data_utils.rand_name('router'), + external_network_id=self.ext_net_id) + + # Create conntrack helper + created_cth = self.create_conntrack_helper(router['id'], helper='tftp', + protocol='udp', port=69) + self.assertEqual('tftp', created_cth['helper']) + self.assertEqual('udp', created_cth['protocol']) + self.assertEqual(69, created_cth['port']) + + # List conntrack helpers + conntrack_helpers = self.client.list_conntrack_helpers( + router['id'])['conntrack_helpers'] + self.assertIn(created_cth['id'], + {cth['id'] for cth in conntrack_helpers}) + + # Update conntrack helper + updated_conntrack_helper = self.client.update_conntrack_helper( + router['id'], created_cth['id'], port=6969)['conntrack_helper'] + self.assertEqual(updated_conntrack_helper['port'], 6969) + + # Show conntrack helper + conntrack_helper = self.client.get_conntrack_helper( + router['id'], created_cth['id'])['conntrack_helper'] + self.assertEqual('tftp', conntrack_helper['helper']) + self.assertEqual('udp', conntrack_helper['protocol']) + self.assertEqual(6969, conntrack_helper['port']) + + # Delete conntrack helper + self.client.delete_conntrack_helper(router['id'], created_cth['id']) + self.assertRaises( + exceptions.NotFound, + self.client.get_conntrack_helper, router['id'], created_cth['id']) + + @decorators.idempotent_id('0a6ae20c-3f66-423e-93c6-cfedd1c93b8d') + def test_conntrack_helper_info_in_router_details(self): + # Create a router + router = self.create_router(data_utils.rand_name('router'), + external_network_id=self.ext_net_id) + + # Ensure routerd does not have information about any conntrack helper + router = self.client.show_router(router['id'])['router'] + self.assertEqual(0, len(router['conntrack_helpers'])) + + # Now create conntrack helper and ensure it's visible in Router details + cth = self.create_conntrack_helper(router['id'], helper='ftp', + protocol='tcp', port=21) + router = self.client.show_router(router['id'])['router'] + self.assertEqual(1, len(router['conntrack_helpers'])) + self.assertEqual('ftp', router['conntrack_helpers'][0]['helper']) + self.assertEqual('tcp', router['conntrack_helpers'][0]['protocol']) + self.assertEqual(21, router['conntrack_helpers'][0]['port']) + + # Delete conntrack_helper and ensure it's no longer in Router details + self.client.delete_conntrack_helper(router['id'], cth['id']) + router = self.client.show_router(router['id'])['router'] + self.assertEqual(0, len(router['conntrack_helpers'])) + + @decorators.idempotent_id('134469d9-fb25-4165-adc8-f4747f07caf1') + def test_2_conntrack_helpers_to_same_router(self): + # Create a router + router = self.create_router(data_utils.rand_name('router'), + external_network_id=self.ext_net_id) + + cth_data = [{'helper': 'tftp', 'protocol': 'udp', 'port': 60}, + {'helper': 'ftp', 'protocol': 'tcp', 'port': 21}] + created_cths = [] + for cth in cth_data: + created_cth = self.create_conntrack_helper( + router_id=router['id'], + helper=cth['helper'], + protocol=cth['protocol'], + port=cth['port']) + self.assertEqual(cth['helper'], created_cth['helper']) + self.assertEqual(cth['protocol'], created_cth['protocol']) + self.assertEqual(cth['port'], created_cth['port']) + created_cths.append(created_cth) + + # Check that conntrack helpers are in Router details + router = self.client.show_router(router['id'])['router'] + self.assertEqual(len(cth_data), len(router['conntrack_helpers'])) + for cth in created_cths: + expected_cth = cth.copy() + expected_cth.pop('id') + expected_cth.pop('client') + expected_cth.pop('router_id') + self.assertIn(expected_cth, router['conntrack_helpers']) + + # Test list of conntrack helpers + conntrack_helpers = self.client.list_conntrack_helpers( + router['id'])['conntrack_helpers'] + self.assertEqual(len(cth_data), len(conntrack_helpers)) diff --git a/neutron_tempest_plugin/services/network/json/network_client.py b/neutron_tempest_plugin/services/network/json/network_client.py index dabf3afe..05095a76 100644 --- a/neutron_tempest_plugin/services/network/json/network_client.py +++ b/neutron_tempest_plugin/services/network/json/network_client.py @@ -1020,6 +1020,49 @@ class NetworkClientJSON(service_client.RestClient): self.expected_success(204, resp.status) service_client.ResponseBody(resp, body) + def create_conntrack_helper(self, router_id, helper, protocol, port): + post_body = {'conntrack_helper': { + 'helper': helper, + 'protocol': protocol, + 'port': port}} + body = jsonutils.dumps(post_body) + uri = '%s/routers/%s/conntrack_helpers' % (self.uri_prefix, router_id) + resp, body = self.post(uri, body) + self.expected_success(201, resp.status) + body = jsonutils.loads(body) + return service_client.ResponseBody(resp, body) + + def get_conntrack_helper(self, router_id, cth_id): + uri = '%s/routers/%s/conntrack_helpers/%s' % (self.uri_prefix, + router_id, cth_id) + get_resp, get_resp_body = self.get(uri) + self.expected_success(200, get_resp.status) + body = jsonutils.loads(get_resp_body) + return service_client.ResponseBody(get_resp, body) + + def list_conntrack_helpers(self, router_id): + uri = '%s/routers/%s/conntrack_helpers' % (self.uri_prefix, router_id) + resp, body = self.get(uri) + self.expected_success(200, resp.status) + body = jsonutils.loads(body) + return service_client.ResponseBody(resp, body) + + def update_conntrack_helper(self, router_id, cth_id, **kwargs): + uri = '%s/routers/%s/conntrack_helpers/%s' % (self.uri_prefix, + router_id, cth_id) + put_body = jsonutils.dumps({'conntrack_helper': kwargs}) + put_resp, resp_body = self.put(uri, put_body) + self.expected_success(200, put_resp.status) + body = jsonutils.loads(resp_body) + return service_client.ResponseBody(put_resp, body) + + def delete_conntrack_helper(self, router_id, cth_id): + uri = '%s/routers/%s/conntrack_helpers/%s' % (self.uri_prefix, + router_id, cth_id) + resp, body = self.delete(uri) + self.expected_success(204, resp.status) + service_client.ResponseBody(resp, body) + def create_network_keystone_v3(self, name, project_id, tenant_id=None): uri = '%s/networks' % self.uri_prefix post_data = {