From 564905e49a0d418bc891f12b560e291a9fdc4acb Mon Sep 17 00:00:00 2001 From: ABHISHEK SHARMA Date: Mon, 5 Aug 2019 16:46:51 -0700 Subject: [PATCH] Tempest Scenario test for Connection Tracking The scenario will start a ping on the remote server using floating IP in a seperate thread and start tracking the connectivity with the remote server. The ingress ICMP rule on the server will be deleted and at same time then the test asserts if connectivity breaks or not. The testcase will pass if ping stops working after deletion of the rule. Change-Id: I0e45a44cf20f68136720cd508cbea28a820f8ce0 --- gbpservice/tests/tempest/__init__.py | 0 gbpservice/tests/tempest/plugin.py | 34 ++++ .../tests/tempest/scenario/test_server_ep.py | 192 ++++++++++++++++++ setup.cfg | 3 +- 4 files changed, 228 insertions(+), 1 deletion(-) create mode 100644 gbpservice/tests/tempest/__init__.py create mode 100644 gbpservice/tests/tempest/plugin.py create mode 100644 gbpservice/tests/tempest/scenario/test_server_ep.py diff --git a/gbpservice/tests/tempest/__init__.py b/gbpservice/tests/tempest/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/gbpservice/tests/tempest/plugin.py b/gbpservice/tests/tempest/plugin.py new file mode 100644 index 000000000..976883a25 --- /dev/null +++ b/gbpservice/tests/tempest/plugin.py @@ -0,0 +1,34 @@ +# Copyright 2019 +# 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. + + +import os + +from tempest.test_discover import plugins + + +class GroupBasedPolicyTempestPlugin(plugins.TempestPlugin): + def load_tests(self): + base_path = os.path.split(os.path.dirname( + os.path.abspath(__file__)))[0] + test_dir = "gbpservice/tests/tempest" + full_test_dir = os.path.join(base_path, test_dir) + return full_test_dir, base_path + + def register_opts(self, conf): + pass + + def get_opt_lists(self): + pass diff --git a/gbpservice/tests/tempest/scenario/test_server_ep.py b/gbpservice/tests/tempest/scenario/test_server_ep.py new file mode 100644 index 000000000..16c6ee512 --- /dev/null +++ b/gbpservice/tests/tempest/scenario/test_server_ep.py @@ -0,0 +1,192 @@ +# 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. + +import collections +from oslo_log import log as logging +import subprocess +from tempest.common.utils import data_utils +from tempest import config +from tempest.scenario import manager +from tempest import test + +CONF = config.CONF +LOG = logging.getLogger(__name__) +Floating_IP_tuple = collections.namedtuple('Floating_IP_tuple', + ['floating_ip', 'server']) +MAX_RETRY = 50 + + +class TestServerEp(manager.NetworkScenarioTest): + def setUp(self): + super(TestServerEp, self).setUp() + self.keypairs = {} + self.servers = [] + self.deleted_rule = False + self.track_success = False + + def ping_remote(self, should_connect, should_check_floating_ip_status): + floating_ip, server = self.floating_ip_tuple + ip_address = floating_ip['floating_ip_address'] + private_key = None + floatingip_status = 'DOWN' + + if should_connect: + private_key = self._get_server_key(server) + if private_key is not None: + floatingip_status = 'ACTIVE' + + # Check FloatingIP Status before initiating a connection + if should_check_floating_ip_status: + self.check_floating_ip_status(floating_ip, floatingip_status) + LOG.debug('Starting Ping to Connect') + self.track_success = self._track_connectivity(ip_address) + return self.track_success + + def _track_connectivity(self, floating_ip): + ping_command = "ping -O " + floating_ip + process = subprocess.Popen( + ping_command, shell=True, stdout=subprocess.PIPE, close_fds=True) + LOG.debug('Tracking Connectivity') + error_msg = "no answer yet for icmp_seq" + success_msg = "bytes from " + floating_ip + retry = MAX_RETRY + + # To skip first line of ping command + output = process.stdout.readline() + + while process.poll() is None and retry > 0: + output = process.stdout.readline() + output = output.strip() + if not output: + LOG.error('Ping failed - Nothing to show') + break + if error_msg in output and self.deleted_rule is True: + self.track_success = True + LOG.debug("Rules Deleted Successfully") + break + elif success_msg in output and self.deleted_rule is True: + LOG.debug("Waiting for rule to be deleted in fabric") + retry -= 1 + if retry is 0: + LOG.error("Error - Still Pinging even after " + "deleting the security group rule") + break + elif success_msg in output and self.deleted_rule is False: + LOG.debug("Waiting for rules to be " + "deleted from security group") + self.deleted_rule = self._delete_security_group_rule( + self.servers) + retry -= 1 + if retry is 0: + LOG.error("Security rule was not deleted in time") + break + else: + LOG.debug("Waiting for Server to get Active") + retry -= 1 + if retry is 0: + LOG.error("Error - %s" % (output,)) + break + + LOG.debug("Closing Subprocess") + process.stdout.close() + process.kill() + + def _delete_security_group_rule(self, servers): + for server in servers: + sg = server['security_groups'] + LOG.debug("Security group is %s " % (sg,)) + rule_list_body = ( + self.security_group_rules_client.list_security_group_rules()) + for rule in rule_list_body['security_group_rules']: + if ("icmp" == rule["protocol"] and + rule["direction"] == "ingress"): + LOG.debug("The rule is %s " % (rule,)) + (self.security_group_rules_client. + delete_security_group_rule(rule["id"])) + + return True + + def _setup_network_and_servers(self, **kwargs): + boot_with_port = kwargs.pop('boot_with_port', False) + self.security_group = self._create_security_group( + tenant_id=self.tenant_id) + security_groups = [{'name': self.security_group['name']}] + self.network, self.subnet, self.router = self.create_networks(**kwargs) + self.check_networks() + + self.ports = [] + self.port_id = None + if boot_with_port: + # create a port on the network and boot with that + self.port_id = self._create_port(self.network['id'])['id'] + self.ports.append({'port': self.port_id}) + + name = data_utils.rand_name('server-smoke') + server = self._create_server(name, self.network, + security_groups, self.port_id) + + floating_ip = self.create_floating_ip(server) + self.floating_ip_tuple = Floating_IP_tuple(floating_ip, server) + + def check_networks(self): + """Checks that we see the newly created network/subnet/router + + via checking the result of list_[networks,routers,subnets] + """ + + seen_nets = self._list_networks() + seen_names = [n['name'] for n in seen_nets] + seen_ids = [n['id'] for n in seen_nets] + self.assertIn(self.network['name'], seen_names) + self.assertIn(self.network['id'], seen_ids) + + if self.subnet: + seen_subnets = self._list_subnets() + seen_net_ids = [n['network_id'] for n in seen_subnets] + seen_subnet_ids = [n['id'] for n in seen_subnets] + self.assertIn(self.network['id'], seen_net_ids) + self.assertIn(self.subnet['id'], seen_subnet_ids) + + if self.router: + seen_routers = self._list_routers() + seen_router_ids = [n['id'] for n in seen_routers] + seen_router_names = [n['name'] for n in seen_routers] + self.assertIn(self.router['name'], + seen_router_names) + self.assertIn(self.router['id'], + seen_router_ids) + + def _create_server(self, name, network, security_groups, port_id=None): + keypair = self.create_keypair() + self.keypairs[keypair['name']] = keypair + network = {'uuid': network['id']} + if port_id is not None: + network['port'] = port_id + + server = self.create_server( + name=name, + networks=[network], + key_name=keypair['name'], + security_groups=security_groups, + wait_until='ACTIVE') + self.servers.append(server) + return server + + def _get_server_key(self, server): + return self.keypairs[server['key_name']]['private_key'] + + @test.attr(type='smoke') + @test.services('compute', 'network') + def test_server_ep(self): + self._setup_network_and_servers() + self.assertTrue(self.ping_remote(True, True)) + LOG.debug("Testing Done") diff --git a/setup.cfg b/setup.cfg index 5b494dc02..70c6f9bb9 100644 --- a/setup.cfg +++ b/setup.cfg @@ -90,7 +90,8 @@ neutronclient.extension= port_pair_group_patch = gbpservice.neutron.services.sfc.aim.cli_patch openstack.cli.extension = port_pair_group_patch = gbpservice.neutron.services.sfc.aim.cli_patch - +tempest.test_plugins = + gbp_tests = gbpservice.tests.tempest.plugin:GroupBasedPolicyTempestPlugin [build_sphinx] source-dir = doc/source build-dir = doc/build