From 2b56065d82fa7cdb7063ff48933643f9aa994881 Mon Sep 17 00:00:00 2001 From: Romanos Skiadas Date: Fri, 9 Sep 2016 15:59:16 +0300 Subject: Refactoring - Wrap common methods that use globals in a Class `get_ping_status` and `add_to_summary` depended on globals for statefulness. The easiest way to refactor them was to convert them into methods and the globals they used into instance variables. - Move `generate_userdata_common` and `generate_userdata_with_ssh` to the utils module. - Make flavor a kwarg in create_instance. Add a defaults section in config.yaml with flavor. This value is used if flavor is not passed via kwargs. - Substitute some sleeps with `wait_for_instances_up` which will wait until a dhcp lease success message appears in the instance's log. - Add functions that wait until router and network association is completed. These do not replace the sleeps but make the tests more deterministic. - Remove unused ping_timeout from config.yaml Change-Id: Ia68ebf47aaa5a58c4e20267f1476db626f015a7c Signed-off-by: Romanos Skiadas Co-Authored-by: George Paraskevopoulos --- test/functest/config.yaml | 10 +-- test/functest/results.py | 147 ++++++++++++++++++++++++++++++++++ test/functest/testcase_1.py | 173 +++++++++++++--------------------------- test/functest/testcase_2.py | 189 +++++++++++--------------------------------- test/functest/testcase_4.py | 172 +++++++++++++--------------------------- test/functest/utils.py | 139 +++++++++++++++++++++++++++++++- 6 files changed, 440 insertions(+), 390 deletions(-) create mode 100644 test/functest/results.py (limited to 'test') diff --git a/test/functest/config.yaml b/test/functest/config.yaml index 93f3552..d91c6ff 100644 --- a/test/functest/config.yaml +++ b/test/functest/config.yaml @@ -1,3 +1,6 @@ +defaults: + flavor: m1.tiny # adapt to your environment + testcases: tempest: enabled: true @@ -11,8 +14,6 @@ testcases: description: VPN provides connectivity between subnets succes_criteria: 75 # we let fail 25% of the subtests testname_db: functest_testcase_1 - ping_timeout: 200 - flavor: m1.tiny # adapt to your environment instance_1_name: sdnvpn-1-1 instance_2_name: sdnvpn-1-2 instance_3_name: sdnvpn-1-3 @@ -37,8 +38,6 @@ testcases: description: Tenant separation succes_criteria: 100 # all the subtests must pass testname_db: functest_testcase_2 - ping_timeout: 200 - flavor: m1.tiny # adapt to your environment instance_1_name: sdnvpn-2-1 instance_2_name: sdnvpn-2-2 instance_3_name: sdnvpn-2-3 @@ -73,8 +72,6 @@ testcases: description: VPN provides connectivity between subnets using router association succes_criteria: 75 # we let fail 25% of the subtests testname_db: functest_testcase_4 - ping_timeout: 200 - flavor: m1.tiny # adapt to your environment instance_1_name: sdnvpn-4-1 instance_2_name: sdnvpn-4-2 instance_3_name: sdnvpn-4-3 @@ -93,4 +90,3 @@ testcases: sdnvpn_sg_descr: Security group for SDNVPN test cases targets1: '88:88' targets2: '55:55' - diff --git a/test/functest/results.py b/test/functest/results.py new file mode 100644 index 0000000..baa6af7 --- /dev/null +++ b/test/functest/results.py @@ -0,0 +1,147 @@ +#!/usr/bin/python +# +# Copyright (c) 2016 All rights reserved +# This program and the accompanying materials +# are made available under the terms of the Apache License, Version 2.0 +# which accompanies this distribution, and is available at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +import time + +import functest.utils.functest_logger as ft_logger + +logger = ft_logger.Logger("sdnvpn-results").getLogger() + + +class Results(object): + + def __init__(self, line_length): + self.line_length = line_length + self.test_result = "PASS" + self.summary = "" + self.details = [] + self.num_tests = 0 + self.num_tests_failed = 0 + + def get_ping_status(self, + vm_source, ip_source, + vm_target, ip_target, + expected="PASS", timeout=30): + console_log = vm_source.get_console_output() + + if "request failed" in console_log: + # Normally, cirros displays this message when userdata fails + logger.debug("It seems userdata is not supported in " + "nova boot...") + return False + else: + tab = ("%s" % (" " * 53)) + expected_result = 'can ping' if expected == 'PASS' \ + else 'cannot ping' + test_case_name = ("'%s' %s '%s'" % + (vm_source.name, + expected_result, + vm_target.name)) + logger.debug("%sPing\n%sfrom '%s' (%s)\n%sto '%s' (%s).\n" + "%s-->Expected result: %s.\n" + % (tab, tab, vm_source.name, ip_source, + tab, vm_target.name, ip_target, + tab, expected_result)) + while True: + console_log = vm_source.get_console_output() + # the console_log is a long string, we want to take + # the last 4 lines (for example) + lines = console_log.split('\n') + last_n_lines = lines[-5:] + if ("ping %s OK" % ip_target) in last_n_lines: + msg = ("'%s' can ping '%s'" + % (vm_source.name, vm_target.name)) + if expected == "PASS": + logger.debug("[PASS] %s" % msg) + self.add_to_summary(2, "PASS", test_case_name) + else: + logger.debug("[FAIL] %s" % msg) + self.test_result = "FAIL" + self.add_to_summary(2, "FAIL", test_case_name) + logger.debug("\n%s" % last_n_lines) + break + elif ("ping %s KO" % ip_target) in last_n_lines: + msg = ("'%s' cannot ping '%s'" % + (vm_source.name, vm_target.name)) + if expected == "FAIL": + logger.debug("[PASS] %s" % msg) + self.add_to_summary(2, "PASS", test_case_name) + else: + logger.debug("[FAIL] %s" % msg) + self.test_result = "FAIL" + self.add_to_summary(2, "FAIL", test_case_name) + break + time.sleep(1) + timeout -= 1 + if timeout == 0: + self.test_result = "FAIL" + logger.debug("[FAIL] Timeout reached for '%s'. " + "No ping output captured in the console log" + % vm_source.name) + self.add_to_summary(2, "FAIL", test_case_name) + break + + def add_to_summary(self, num_cols, col1, col2=""): + if num_cols == 0: + self.summary += ("+%s+\n" % (col1 * (self.line_length - 2))) + elif num_cols == 1: + self.summary += ("| " + col1.ljust(self.line_length - 3) + "|\n") + elif num_cols == 2: + self.summary += ("| %s" % col1.ljust(7) + "| ") + self.summary += (col2.ljust(self.line_length - 12) + "|\n") + if col1 in ("FAIL", "PASS"): + self.details.append({col2: col1}) + self.num_tests += 1 + if col1 == "FAIL": + self.num_tests_failed += 1 + + def check_ssh_output(self, vm_source, ip_source, + vm_target, ip_target, + expected, timeout=30): + console_log = vm_source.get_console_output() + + if "request failed" in console_log: + # Normally, cirros displays this message when userdata fails + logger.debug("It seems userdata is not supported in " + "nova boot...") + return False + else: + tab = ("%s" % (" " * 53)) + test_case_name = ("[%s] returns 'I am %s' to '%s'[%s]" % + (ip_target, expected, + vm_source.name, ip_source)) + logger.debug("%sSSH\n%sfrom '%s' (%s)\n%sto '%s' (%s).\n" + "%s-->Expected result: %s.\n" + % (tab, tab, vm_source.name, ip_source, + tab, vm_target.name, ip_target, + tab, expected)) + while True: + console_log = vm_source.get_console_output() + # the console_log is a long string, we want to take + # the last 4 lines (for example) + lines = console_log.split('\n') + last_n_lines = lines[-5:] + if ("%s %s" % (ip_target, expected)) in last_n_lines: + logger.debug("[PASS] %s" % test_case_name) + self.add_to_summary(2, "PASS", test_case_name) + break + elif ("%s not reachable" % ip_target) in last_n_lines: + logger.debug("[FAIL] %s" % test_case_name) + self.add_to_summary(2, "FAIL", test_case_name) + self.test_result = "FAIL" + break + time.sleep(1) + timeout -= 1 + if timeout == 0: + self.test_result = "FAIL" + logger.debug("[FAIL] Timeout reached for '%s'." + " No ping output captured in the console log" + % vm_source.name) + self.add_to_summary(2, "FAIL", test_case_name) + break diff --git a/test/functest/testcase_1.py b/test/functest/testcase_1.py index 1356518..4f23bdc 100644 --- a/test/functest/testcase_1.py +++ b/test/functest/testcase_1.py @@ -19,6 +19,7 @@ import functest.utils.functest_utils as ft_utils import functest.utils.openstack_utils as os_utils import utils as test_utils +from results import Results parser = argparse.ArgumentParser() @@ -46,8 +47,6 @@ INSTANCE_4_NAME = ft_utils.get_parameter_from_yaml( "testcases.testcase_1.instance_4_name", config_file) INSTANCE_5_NAME = ft_utils.get_parameter_from_yaml( "testcases.testcase_1.instance_5_name", config_file) -FLAVOR = ft_utils.get_parameter_from_yaml( - "testcases.testcase_1.flavor", config_file) IMAGE_NAME = ft_utils.get_parameter_from_yaml( "testcases.testcase_1.image_name", config_file) IMAGE_FILENAME = ft_utils.get_functest_config( @@ -87,96 +86,16 @@ SUCCESS_CRITERIA = ft_utils.get_parameter_from_yaml( "testcases.testcase_1.succes_criteria", config_file) TEST_DB = ft_utils.get_functest_config("results.test_db_url") -TEST_RESULT = "PASS" -SUMMARY = "" -LINE_LENGTH = 60 # length for the summary table -DETAILS = [] -NUM_TESTS = 0 -NUM_TESTS_FAILED = 0 - - -def get_ping_status(vm_source, ip_source, - vm_target, ip_target, - expected="PASS", timeout=30): - console_log = vm_source.get_console_output() - - global TEST_RESULT - - if "request failed" in console_log: - # Normally, cirros displays this message when userdata fails - logger.debug("It seems userdata is not supported in " - "nova boot...") - return False - else: - tab = ("%s" % (" " * 53)) - expected_result = 'can ping' if expected == 'PASS' else 'cannot ping' - test_case_name = ("'%s' %s '%s'" % - (vm_source.name, expected_result, vm_target.name)) - logger.debug("%sPing\n%sfrom '%s' (%s)\n%sto '%s' (%s).\n" - "%s-->Expected result: %s.\n" - % (tab, tab, vm_source.name, ip_source, - tab, vm_target.name, ip_target, - tab, expected_result)) - while True: - console_log = vm_source.get_console_output() - # the console_log is a long string, we want to take - # the last 4 lines (for example) - lines = console_log.split('\n') - last_n_lines = lines[-5:] - if ("ping %s OK" % ip_target) in last_n_lines: - msg = ("'%s' can ping '%s'" % (vm_source.name, vm_target.name)) - if expected == "PASS": - logger.debug("[PASS] %s" % msg) - add_to_summary(2, "PASS", test_case_name) - else: - logger.debug("[FAIL] %s" % msg) - TEST_RESULT = "FAIL" - add_to_summary(2, "FAIL", test_case_name) - logger.debug("\n%s" % last_n_lines) - break - elif ("ping %s KO" % ip_target) in last_n_lines: - msg = ("'%s' cannot ping '%s'" % - (vm_source.name, vm_target.name)) - if expected == "FAIL": - logger.debug("[PASS] %s" % msg) - add_to_summary(2, "PASS", test_case_name) - else: - logger.debug("[FAIL] %s" % msg) - TEST_RESULT = "FAIL" - add_to_summary(2, "FAIL", test_case_name) - break - time.sleep(1) - timeout -= 1 - if timeout == 0: - TEST_RESULT = "FAIL" - logger.debug("[FAIL] Timeout reached for '%s'. No ping output " - "captured in the console log" % vm_source.name) - add_to_summary(2, "FAIL", test_case_name) - break - - -def add_to_summary(num_cols, col1, col2=""): - global SUMMARY, LINE_LENGTH, DETAILS, NUM_TESTS, NUM_TESTS_FAILED - if num_cols == 0: - SUMMARY += ("+%s+\n" % (col1 * (LINE_LENGTH - 2))) - elif num_cols == 1: - SUMMARY += ("| " + col1.ljust(LINE_LENGTH - 3) + "|\n") - elif num_cols == 2: - SUMMARY += ("| %s" % col1.ljust(7) + "| ") - SUMMARY += (col2.ljust(LINE_LENGTH - 12) + "|\n") - if col1 in ("FAIL", "PASS"): - DETAILS.append({col2: col1}) - NUM_TESTS += 1 - if col1 == "FAIL": - NUM_TESTS_FAILED += 1 +LINE_LENGTH = 60 def main(): - global TEST_RESULT, SUMMARY + global LINE_LENGTH + results = Results(LINE_LENGTH) - add_to_summary(0, "=") - add_to_summary(2, "STATUS", "SUBTEST") - add_to_summary(0, "=") + results.add_to_summary(0, "=") + results.add_to_summary(2, "STATUS", "SUBTEST") + results.add_to_summary(0, "=") nova_client = os_utils.get_nova_client() neutron_client = os_utils.get_neutron_client() @@ -217,7 +136,6 @@ def main(): # boot INTANCES vm_2 = test_utils.create_instance(nova_client, INSTANCE_2_NAME, - FLAVOR, image_id, network_1_id, sg_id, @@ -229,7 +147,7 @@ def main(): vm_3 = test_utils.create_instance(nova_client, INSTANCE_3_NAME, - FLAVOR, image_id, + image_id, network_1_id, sg_id, secgroup_name=SECGROUP_NAME, @@ -240,7 +158,6 @@ def main(): vm_5 = test_utils.create_instance(nova_client, INSTANCE_5_NAME, - FLAVOR, image_id, network_2_id, sg_id, @@ -254,7 +171,6 @@ def main(): u4 = test_utils.generate_ping_userdata([vm_5_ip]) vm_4 = test_utils.create_instance(nova_client, INSTANCE_4_NAME, - FLAVOR, image_id, network_2_id, sg_id, @@ -273,7 +189,6 @@ def main(): vm_5_ip]) vm_1 = test_utils.create_instance(nova_client, INSTANCE_1_NAME, - FLAVOR, image_id, network_1_id, sg_id, @@ -285,7 +200,7 @@ def main(): (INSTANCE_1_NAME, vm_1_ip)) msg = ("Create VPN with eRT<>iRT") logger.info(msg) - add_to_summary(1, msg) + results.add_to_summary(1, msg) vpn_name = "sdnvpn-" + str(randint(100000, 999999)) kwargs = {"import_targets": TARGETS_1, "export_targets": TARGETS_2, @@ -296,71 +211,93 @@ def main(): msg = ("Associate network '%s' to the VPN." % NET_1_NAME) logger.info(msg) - add_to_summary(1, msg) - add_to_summary(0, "-") + results.add_to_summary(1, msg) + results.add_to_summary(0, "-") os_utils.create_network_association( neutron_client, bgpvpn_id, network_1_id) # Wait for VMs to get ips. - time.sleep(80) + instances_up = test_utils.wait_for_instances_up(vm_1, vm_2, + vm_3, vm_4, + vm_5) + + if not instances_up: + logger.error("One or more instances is down") + # TODO: Handle this appropriately # Ping from VM1 to VM2 should work - get_ping_status(vm_1, vm_1_ip, vm_2, vm_2_ip, expected="PASS", timeout=200) + results.get_ping_status(vm_1, vm_1_ip, vm_2, vm_2_ip, + expected="PASS", timeout=200) # Ping from VM1 to VM3 should work - get_ping_status(vm_1, vm_1_ip, vm_3, vm_3_ip, expected="PASS", timeout=30) + results.get_ping_status(vm_1, vm_1_ip, vm_3, vm_3_ip, + expected="PASS", timeout=30) # Ping from VM1 to VM4 should not work - get_ping_status(vm_1, vm_1_ip, vm_4, vm_4_ip, expected="FAIL", timeout=30) + results.get_ping_status(vm_1, vm_1_ip, vm_4, vm_4_ip, + expected="FAIL", timeout=30) msg = ("Associate network '%s' to the VPN." % NET_2_NAME) logger.info(msg) - add_to_summary(0, "-") - add_to_summary(1, msg) - add_to_summary(0, "-") + results.add_to_summary(0, "-") + results.add_to_summary(1, msg) + results.add_to_summary(0, "-") os_utils.create_network_association( neutron_client, bgpvpn_id, network_2_id) - # Wait a bit for this to take effect + test_utils.wait_for_bgp_net_assocs(neutron_client, + bgpvpn_id, + network_1_id, + network_2_id) + + logger.info("Waiting for the VMs to connect to each other using the" + " updated network configuration") time.sleep(30) # Ping from VM4 to VM5 should work - get_ping_status(vm_4, vm_4_ip, vm_5, vm_5_ip, expected="PASS", timeout=30) + results.get_ping_status(vm_4, vm_4_ip, vm_5, vm_5_ip, + expected="PASS", timeout=30) # Ping from VM1 to VM4 should not work - get_ping_status(vm_1, vm_1_ip, vm_4, vm_4_ip, expected="FAIL", timeout=30) + results.get_ping_status(vm_1, vm_1_ip, vm_4, vm_4_ip, + expected="FAIL", timeout=30) # Ping from VM1 to VM5 should not work - get_ping_status(vm_1, vm_1_ip, vm_5, vm_5_ip, expected="FAIL", timeout=30) + results.get_ping_status(vm_1, vm_1_ip, vm_5, vm_5_ip, + expected="FAIL", timeout=30) msg = ("Update VPN with eRT=iRT ...") logger.info(msg) - add_to_summary(0, "-") - add_to_summary(1, msg) - add_to_summary(0, "-") + results.add_to_summary(0, "-") + results.add_to_summary(1, msg) + results.add_to_summary(0, "-") kwargs = {"import_targets": TARGETS_1, "export_targets": TARGETS_1, "name": vpn_name} bgpvpn = os_utils.update_bgpvpn(neutron_client, bgpvpn_id, **kwargs) - # Wait a bit for this to take effect + logger.info("Waiting for the VMs to connect to each other using the" + " updated network configuration") time.sleep(30) # Ping from VM1 to VM4 should work - get_ping_status(vm_1, vm_1_ip, vm_4, vm_4_ip, expected="PASS", timeout=30) + results.get_ping_status(vm_1, vm_1_ip, vm_4, vm_4_ip, + expected="PASS", timeout=30) # Ping from VM1 to VM5 should work - get_ping_status(vm_1, vm_1_ip, vm_5, vm_5_ip, expected="PASS", timeout=30) + results.get_ping_status(vm_1, vm_1_ip, vm_5, vm_5_ip, + expected="PASS", timeout=30) - add_to_summary(0, "=") - logger.info("\n%s" % SUMMARY) + results.add_to_summary(0, "=") + logger.info("\n%s" % results.summary) - if TEST_RESULT == "PASS": + if results.test_result == "PASS": logger.info("All the ping tests have passed as expected.") else: logger.info("One or more ping tests have failed.") status = "PASS" - success = 100 - (100 * int(NUM_TESTS_FAILED) / int(NUM_TESTS)) + success = 100 - \ + (100 * int(results.num_tests_failed) / int(results.num_tests)) if success < int(SUCCESS_CRITERIA): status = "FAILED" - return {"status": status, "details": DETAILS} + return {"status": status, "details": results.details} if __name__ == '__main__': diff --git a/test/functest/testcase_2.py b/test/functest/testcase_2.py index 5f127d7..ed86b02 100644 --- a/test/functest/testcase_2.py +++ b/test/functest/testcase_2.py @@ -19,6 +19,7 @@ import functest.utils.functest_utils as ft_utils import functest.utils.openstack_utils as os_utils import utils as test_utils +from results import Results parser = argparse.ArgumentParser() @@ -51,8 +52,6 @@ INSTANCE_4_IP = "10.10.10.12" INSTANCE_5_NAME = ft_utils.get_parameter_from_yaml( "testcases.testcase_2.instance_5_name", config_file) INSTANCE_5_IP = "10.10.11.13" -FLAVOR = ft_utils.get_parameter_from_yaml( - "testcases.testcase_2.flavor", config_file) IMAGE_NAME = ft_utils.get_parameter_from_yaml( "testcases.testcase_2.image_name", config_file) IMAGE_FILENAME = ft_utils.get_functest_config( @@ -104,126 +103,17 @@ SUCCESS_CRITERIA = ft_utils.get_parameter_from_yaml( "testcases.testcase_1.succes_criteria", config_file) TEST_DB = ft_utils.get_functest_config("results.test_db_url") -TEST_RESULT = "PASS" -SUMMARY = "" LINE_LENGTH = 90 # length for the summary table -DETAILS = [] -NUM_TESTS = 0 -NUM_TESTS_FAILED = 0 - - -def generate_userdata_common(): - return ("#!/bin/sh\n" - "sudo mkdir -p /home/cirros/.ssh/\n" - "sudo chown cirros:cirros /home/cirros/.ssh/\n" - "sudo chown cirros:cirros /home/cirros/id_rsa\n" - "mv /home/cirros/id_rsa /home/cirros/.ssh/\n" - "sudo echo ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAAAgnWtSS98Am516e" - "stBsq0jbyOB4eLMUYDdgzsUHsnxFQCtACwwAg9/2uq3FoGUBUWeHZNsT6jcK9" - "sCMEYiS479CUCzbrxcd8XaIlK38HECcDVglgBNwNzX/WDfMejXpKzZG61s98rU" - "ElNvZ0YDqhaqZGqxIV4ejalqLjYrQkoly3R+2k= " - "cirros@test1>/home/cirros/.ssh/authorized_keys\n" - "sudo chown cirros:cirros /home/cirros/.ssh/authorized_keys\n" - "chmod 700 /home/cirros/.ssh\n" - "chmod 644 /home/cirros/.ssh/authorized_keys\n" - "chmod 600 /home/cirros/.ssh/id_rsa\n" - ) - - -def generate_userdata_with_ssh(ips_array): - u1 = generate_userdata_common() - - ips = "" - for ip in ips_array: - ips = ("%s %s" % (ips, ip)) - - ips = ips.replace(' ', ' ') - u2 = ("#!/bin/sh\n" - "set%s\n" - "while true; do\n" - " for i do\n" - " ip=$i\n" - " hostname=$(ssh -y -i /home/cirros/.ssh/id_rsa " - "cirros@$ip 'hostname' /dev/null)\n" - " RES=$?\n" - " if [ \"Z$RES\" = \"Z0\" ]; then echo $ip $hostname;\n" - " else echo $ip 'not reachable';fi;\n" - " done\n" - " sleep 1\n" - "done\n" - % ips) - return (u1 + u2) - - -def check_ssh_output(vm_source, ip_source, - vm_target, ip_target, - expected, timeout=30): - console_log = vm_source.get_console_output() - - global TEST_RESULT - - if "request failed" in console_log: - # Normally, cirros displays this message when userdata fails - logger.debug("It seems userdata is not supported in " - "nova boot...") - return False - else: - tab = ("%s" % (" " * 53)) - test_case_name = ("[%s] returns 'I am %s' to '%s'[%s]" % - (ip_target, expected, - vm_source.name, ip_source)) - logger.debug("%sSSH\n%sfrom '%s' (%s)\n%sto '%s' (%s).\n" - "%s-->Expected result: %s.\n" - % (tab, tab, vm_source.name, ip_source, - tab, vm_target.name, ip_target, - tab, expected)) - while True: - console_log = vm_source.get_console_output() - # the console_log is a long string, we want to take - # the last 4 lines (for example) - lines = console_log.split('\n') - last_n_lines = lines[-5:] - if ("%s %s" % (ip_target, expected)) in last_n_lines: - logger.debug("[PASS] %s" % test_case_name) - add_to_summary(2, "PASS", test_case_name) - break - elif ("%s not reachable" % ip_target) in last_n_lines: - logger.debug("[FAIL] %s" % test_case_name) - add_to_summary(2, "FAIL", test_case_name) - TEST_RESULT = "FAIL" - break - time.sleep(1) - timeout -= 1 - if timeout == 0: - TEST_RESULT = "FAIL" - logger.debug("[FAIL] Timeout reached for '%s'. No ping output " - "captured in the console log" % vm_source.name) - add_to_summary(2, "FAIL", test_case_name) - break - - -def add_to_summary(num_cols, col1, col2=""): - global SUMMARY, LINE_LENGTH, NUM_TESTS, NUM_TESTS_FAILED - if num_cols == 0: - SUMMARY += ("+%s+\n" % (col1 * (LINE_LENGTH - 2))) - elif num_cols == 1: - SUMMARY += ("| " + col1.ljust(LINE_LENGTH - 3) + "|\n") - elif num_cols == 2: - SUMMARY += ("| %s" % col1.ljust(7) + "| ") - SUMMARY += (col2.ljust(LINE_LENGTH - 12) + "|\n") - if col1 in ("FAIL", "PASS"): - DETAILS.append({col2: col1}) - NUM_TESTS += 1 - if col1 == "FAIL": - NUM_TESTS_FAILED += 1 def main(): - global TEST_RESULT, SUMMARY + global LINE_LENGTH + + results = Results(LINE_LENGTH) - add_to_summary(0, "=") - add_to_summary(2, "STATUS", "SUBTEST") - add_to_summary(0, "=") + results.add_to_summary(0, "=") + results.add_to_summary(2, "STATUS", "SUBTEST") + results.add_to_summary(0, "=") nova_client = os_utils.get_nova_client() neutron_client = os_utils.get_neutron_client() @@ -272,10 +162,9 @@ def main(): av_zone_2 = "nova:" + compute_nodes[1] # boot INTANCES - userdata_common = generate_userdata_common() + userdata_common = test_utils.generate_userdata_common() vm_2 = test_utils.create_instance(nova_client, INSTANCE_2_NAME, - FLAVOR, image_id, network_1_id, sg_id, @@ -289,7 +178,7 @@ def main(): vm_3 = test_utils.create_instance(nova_client, INSTANCE_3_NAME, - FLAVOR, image_id, + image_id, network_1_id, sg_id, fixed_ip=INSTANCE_3_IP, @@ -302,7 +191,6 @@ def main(): vm_5 = test_utils.create_instance(nova_client, INSTANCE_5_NAME, - FLAVOR, image_id, network_2_id, sg_id, @@ -315,11 +203,10 @@ def main(): (INSTANCE_5_NAME, vm_5_ip)) # We boot vm5 first because we need vm5_ip for vm4 userdata - u4 = generate_userdata_with_ssh( + u4 = test_utils.generate_userdata_with_ssh( [INSTANCE_1_IP, INSTANCE_3_IP, INSTANCE_5_IP]) vm_4 = test_utils.create_instance(nova_client, INSTANCE_4_NAME, - FLAVOR, image_id, network_2_id, sg_id, @@ -334,11 +221,10 @@ def main(): # We boot VM1 at the end because we need to get the IPs first to generate # the userdata - u1 = generate_userdata_with_ssh( + u1 = test_utils.generate_userdata_with_ssh( [INSTANCE_2_IP, INSTANCE_3_IP, INSTANCE_4_IP, INSTANCE_5_IP]) vm_1 = test_utils.create_instance(nova_client, INSTANCE_1_NAME, - FLAVOR, image_id, network_1_id, sg_id, @@ -353,7 +239,7 @@ def main(): msg = ("Create VPN1 with eRT=iRT") logger.info(msg) - add_to_summary(1, msg) + results.add_to_summary(1, msg) vpn1_name = "sdnvpn-1-" + str(randint(100000, 999999)) kwargs = {"import_targets": TARGETS_2, "export_targets": TARGETS_2, @@ -365,26 +251,36 @@ def main(): msg = ("Associate network '%s' to the VPN." % NET_1_NAME) logger.info(msg) - add_to_summary(1, msg) - add_to_summary(0, "-") + results.add_to_summary(1, msg) + results.add_to_summary(0, "-") os_utils.create_network_association( neutron_client, bgpvpn1_id, network_1_id) # Wait for VMs to get ips. - time.sleep(80) + instances_up = test_utils.wait_for_instances_up(vm_1, vm_2, + vm_3, vm_4, + vm_5) + + if not instances_up: + logger.error("One or more instances is down") + sys.exit(-1) + + logger.info("Waiting for the VMs to connect to each other using the" + " updated network configuration") + time.sleep(30) # 10.10.10.12 should return sdnvpn-2 to sdnvpn-1 - check_ssh_output( + results.check_ssh_output( vm_1, vm_1_ip, vm_2, vm_2_ip, expected=INSTANCE_2_NAME, timeout=200) # 10.10.11.13 should return sdnvpn-3 to sdnvpn-1 - check_ssh_output( + results.check_ssh_output( vm_1, vm_1_ip, vm_3, vm_3_ip, expected=INSTANCE_3_NAME, timeout=30) - add_to_summary(0, "-") + results.add_to_summary(0, "-") msg = ("Create VPN2 with eRT=iRT") logger.info(msg) - add_to_summary(1, msg) + results.add_to_summary(1, msg) vpn2_name = "sdnvpn-2-" + str(randint(100000, 999999)) kwargs = {"import_targets": TARGETS_1, "export_targets": TARGETS_1, @@ -396,37 +292,42 @@ def main(): msg = ("Associate network '%s' to the VPN2." % NET_2_NAME) logger.info(msg) - add_to_summary(1, msg) - add_to_summary(0, "-") + results.add_to_summary(1, msg) + results.add_to_summary(0, "-") os_utils.create_network_association( neutron_client, bgpvpn2_id, network_2_id) - # Wait a bit for this to take effect - time.sleep(80) + test_utils.wait_for_bgp_net_assoc(neutron_client, bgpvpn1_id, network_1_id) + test_utils.wait_for_bgp_net_assoc(neutron_client, bgpvpn2_id, network_2_id) + + logger.info("Waiting for the VMs to connect to each other using the" + " updated network configuration") + time.sleep(30) # 10.10.11.13 should return sdnvpn-5 to sdnvpn-4 - check_ssh_output( + results.check_ssh_output( vm_4, vm_4_ip, vm_5, vm_5_ip, expected=INSTANCE_5_NAME, timeout=30) # 10.10.10.11 should return "not reachable" to sdnvpn-4 - check_ssh_output( + results.check_ssh_output( vm_4, vm_4_ip, vm_1, vm_1_ip, expected="not reachable", timeout=30) - add_to_summary(0, "=") - logger.info("\n%s" % SUMMARY) + results.add_to_summary(0, "=") + logger.info("\n%s" % results.summary) - if TEST_RESULT == "PASS": + if results.test_result == "PASS": logger.info("All the sub tests have passed as expected.") else: logger.info("One or more sub tests have failed.") status = "PASS" - success = 100 - (100 * int(NUM_TESTS_FAILED) / int(NUM_TESTS)) + success = 100 - \ + (100 * int(results.num_tests_failed) / int(results.num_tests)) if success < int(SUCCESS_CRITERIA): status = "FAILED" - return {"status": status, "details": DETAILS} + return {"status": status, "details": results.details} if __name__ == '__main__': diff --git a/test/functest/testcase_4.py b/test/functest/testcase_4.py index 3419182..13f531f 100644 --- a/test/functest/testcase_4.py +++ b/test/functest/testcase_4.py @@ -19,6 +19,7 @@ import functest.utils.functest_utils as ft_utils import functest.utils.openstack_utils as os_utils import utils as test_utils +from results import Results parser = argparse.ArgumentParser() @@ -46,8 +47,6 @@ INSTANCE_4_NAME = ft_utils.get_parameter_from_yaml( "testcases.testcase_4.instance_4_name", config_file) INSTANCE_5_NAME = ft_utils.get_parameter_from_yaml( "testcases.testcase_4.instance_5_name", config_file) -FLAVOR = ft_utils.get_parameter_from_yaml( - "testcases.testcase_4.flavor", config_file) IMAGE_NAME = ft_utils.get_parameter_from_yaml( "testcases.testcase_4.image_name", config_file) IMAGE_FILENAME = ft_utils.get_functest_config( @@ -87,96 +86,17 @@ SUCCESS_CRITERIA = ft_utils.get_parameter_from_yaml( "testcases.testcase_4.succes_criteria", config_file) TEST_DB = ft_utils.get_functest_config("results.test_db_url") -TEST_RESULT = "PASS" -SUMMARY = "" LINE_LENGTH = 60 # length for the summary table -DETAILS = [] -NUM_TESTS = 0 -NUM_TESTS_FAILED = 0 - - -def get_ping_status(vm_source, ip_source, - vm_target, ip_target, - expected="PASS", timeout=30): - console_log = vm_source.get_console_output() - - global TEST_RESULT - - if "request failed" in console_log: - # Normally, cirros displays this message when userdata fails - logger.debug("It seems userdata is not supported in " - "nova boot...") - return False - else: - tab = ("%s" % (" " * 53)) - expected_result = 'can ping' if expected == 'PASS' else 'cannot ping' - test_case_name = ("'%s' %s '%s'" % - (vm_source.name, expected_result, vm_target.name)) - logger.debug("%sPing\n%sfrom '%s' (%s)\n%sto '%s' (%s).\n" - "%s-->Expected result: %s.\n" - % (tab, tab, vm_source.name, ip_source, - tab, vm_target.name, ip_target, - tab, expected_result)) - while True: - console_log = vm_source.get_console_output() - # the console_log is a long string, we want to take - # the last 4 lines (for example) - lines = console_log.split('\n') - last_n_lines = lines[-5:] - if ("ping %s OK" % ip_target) in last_n_lines: - msg = ("'%s' can ping '%s'" % (vm_source.name, vm_target.name)) - if expected == "PASS": - logger.debug("[PASS] %s" % msg) - add_to_summary(2, "PASS", test_case_name) - else: - logger.debug("[FAIL] %s" % msg) - TEST_RESULT = "FAIL" - add_to_summary(2, "FAIL", test_case_name) - logger.debug("\n%s" % last_n_lines) - break - elif ("ping %s KO" % ip_target) in last_n_lines: - msg = ("'%s' cannot ping '%s'" % - (vm_source.name, vm_target.name)) - if expected == "FAIL": - logger.debug("[PASS] %s" % msg) - add_to_summary(2, "PASS", test_case_name) - else: - logger.debug("[FAIL] %s" % msg) - TEST_RESULT = "FAIL" - add_to_summary(2, "FAIL", test_case_name) - break - time.sleep(1) - timeout -= 1 - if timeout == 0: - TEST_RESULT = "FAIL" - logger.debug("[FAIL] Timeout reached for '%s'. No ping output " - "captured in the console log" % vm_source.name) - add_to_summary(2, "FAIL", test_case_name) - break - - -def add_to_summary(num_cols, col1, col2=""): - global SUMMARY, LINE_LENGTH, DETAILS, NUM_TESTS, NUM_TESTS_FAILED - if num_cols == 0: - SUMMARY += ("+%s+\n" % (col1 * (LINE_LENGTH - 2))) - elif num_cols == 1: - SUMMARY += ("| " + col1.ljust(LINE_LENGTH - 3) + "|\n") - elif num_cols == 2: - SUMMARY += ("| %s" % col1.ljust(7) + "| ") - SUMMARY += (col2.ljust(LINE_LENGTH - 12) + "|\n") - if col1 in ("FAIL", "PASS"): - DETAILS.append({col2: col1}) - NUM_TESTS += 1 - if col1 == "FAIL": - NUM_TESTS_FAILED += 1 def main(): - global TEST_RESULT, SUMMARY + global LINE_LENGTH + + results = Results(LINE_LENGTH) - add_to_summary(0, "=") - add_to_summary(2, "STATUS", "SUBTEST") - add_to_summary(0, "=") + results.add_to_summary(0, "=") + results.add_to_summary(2, "STATUS", "SUBTEST") + results.add_to_summary(0, "=") nova_client = os_utils.get_nova_client() neutron_client = os_utils.get_neutron_client() @@ -217,7 +137,6 @@ def main(): # boot INTANCES vm_2 = test_utils.create_instance(nova_client, INSTANCE_2_NAME, - FLAVOR, image_id, network_1_id, sg_id, @@ -229,7 +148,6 @@ def main(): vm_3 = test_utils.create_instance(nova_client, INSTANCE_3_NAME, - FLAVOR, image_id, network_1_id, sg_id, @@ -241,7 +159,6 @@ def main(): vm_5 = test_utils.create_instance(nova_client, INSTANCE_5_NAME, - FLAVOR, image_id, network_2_id, sg_id, @@ -255,7 +172,6 @@ def main(): u4 = test_utils.generate_ping_userdata([vm_5_ip]) vm_4 = test_utils.create_instance(nova_client, INSTANCE_4_NAME, - FLAVOR, image_id, network_2_id, sg_id, @@ -274,7 +190,6 @@ def main(): vm_5_ip]) vm_1 = test_utils.create_instance(nova_client, INSTANCE_1_NAME, - FLAVOR, image_id, network_1_id, sg_id, @@ -286,7 +201,7 @@ def main(): (INSTANCE_1_NAME, vm_1_ip)) msg = ("Create VPN with eRT<>iRT") logger.info(msg) - add_to_summary(1, msg) + results.add_to_summary(1, msg) vpn_name = "sdnvpn-" + str(randint(100000, 999999)) kwargs = {"import_targets": TARGETS_1, "export_targets": TARGETS_2, @@ -297,71 +212,94 @@ def main(): msg = ("Associate router '%s' to the VPN." % ROUTER_1_NAME) logger.info(msg) - add_to_summary(1, msg) - add_to_summary(0, "-") + results.add_to_summary(1, msg) + results.add_to_summary(0, "-") os_utils.create_router_association( neutron_client, bgpvpn_id, router_1_id) # Wait for VMs to get ips. - time.sleep(80) + instances_up = test_utils.wait_for_instances_up(vm_1, vm_2, + vm_3, vm_4, + vm_5) + + if not instances_up: + logger.error("One or more instances is down") + # TODO Handle appropriately # Ping from VM1 to VM2 should work - get_ping_status(vm_1, vm_1_ip, vm_2, vm_2_ip, expected="PASS", timeout=200) + results.get_ping_status(vm_1, vm_1_ip, vm_2, vm_2_ip, + expected="PASS", timeout=200) # Ping from VM1 to VM3 should work - get_ping_status(vm_1, vm_1_ip, vm_3, vm_3_ip, expected="PASS", timeout=30) + results.get_ping_status(vm_1, vm_1_ip, vm_3, vm_3_ip, + expected="PASS", timeout=30) # Ping from VM1 to VM4 should not work - get_ping_status(vm_1, vm_1_ip, vm_4, vm_4_ip, expected="FAIL", timeout=30) + results.get_ping_status(vm_1, vm_1_ip, vm_4, vm_4_ip, + expected="FAIL", timeout=30) msg = ("Associate network '%s' to the VPN." % NET_2_NAME) logger.info(msg) - add_to_summary(0, "-") - add_to_summary(1, msg) - add_to_summary(0, "-") + results.add_to_summary(0, "-") + results.add_to_summary(1, msg) + results.add_to_summary(0, "-") os_utils.create_network_association( neutron_client, bgpvpn_id, network_2_id) - # Wait a bit for this to take effect + test_utils.wait_for_bgp_router_assoc( + neutron_client, bgpvpn_id, router_1_id) + test_utils.wait_for_bgp_net_assoc( + neutron_client, bgpvpn_id, network_2_id) + + logger.info("Waiting for the VMs to connect to each other using the" + " updated network configuration") time.sleep(30) # Ping from VM4 to VM5 should work - get_ping_status(vm_4, vm_4_ip, vm_5, vm_5_ip, expected="PASS", timeout=30) + results.get_ping_status(vm_4, vm_4_ip, vm_5, vm_5_ip, + expected="PASS", timeout=30) # Ping from VM1 to VM4 should not work - get_ping_status(vm_1, vm_1_ip, vm_4, vm_4_ip, expected="FAIL", timeout=30) + results.get_ping_status(vm_1, vm_1_ip, vm_4, vm_4_ip, + expected="FAIL", timeout=30) # Ping from VM1 to VM5 should not work - get_ping_status(vm_1, vm_1_ip, vm_5, vm_5_ip, expected="FAIL", timeout=30) + results.get_ping_status(vm_1, vm_1_ip, vm_5, vm_5_ip, + expected="FAIL", timeout=30) msg = ("Update VPN with eRT=iRT ...") logger.info(msg) - add_to_summary(0, "-") - add_to_summary(1, msg) - add_to_summary(0, "-") + results.add_to_summary(0, "-") + results.add_to_summary(1, msg) + results.add_to_summary(0, "-") kwargs = {"import_targets": TARGETS_1, "export_targets": TARGETS_1, "name": vpn_name} bgpvpn = os_utils.update_bgpvpn(neutron_client, bgpvpn_id, **kwargs) - # Wait a bit for this to take effect + + logger.info("Waiting for the VMs to connect to each other using the" + " updated network configuration") time.sleep(30) # Ping from VM1 to VM4 should work - get_ping_status(vm_1, vm_1_ip, vm_4, vm_4_ip, expected="PASS", timeout=30) + results.get_ping_status(vm_1, vm_1_ip, vm_4, vm_4_ip, + expected="PASS", timeout=30) # Ping from VM1 to VM5 should work - get_ping_status(vm_1, vm_1_ip, vm_5, vm_5_ip, expected="PASS", timeout=30) + results.get_ping_status(vm_1, vm_1_ip, vm_5, vm_5_ip, + expected="PASS", timeout=30) - add_to_summary(0, "=") - logger.info("\n%s" % SUMMARY) + results.add_to_summary(0, "=") + logger.info("\n%s" % results.summary) - if TEST_RESULT == "PASS": + if results.test_result == "PASS": logger.info("All the ping tests have passed as expected.") else: logger.info("One or more ping tests have failed.") status = "PASS" - success = 100 - (100 * int(NUM_TESTS_FAILED) / int(NUM_TESTS)) + success = 100 - \ + (100 * int(results.num_tests_failed) / int(results.num_tests_failed)) if success < int(SUCCESS_CRITERIA): status = "FAILED" - return {"status": status, "details": DETAILS} + return {"status": status, "details": results.details} if __name__ == '__main__': diff --git a/test/functest/utils.py b/test/functest/utils.py index 4d921c8..58155ac 100644 --- a/test/functest/utils.py +++ b/test/functest/utils.py @@ -8,13 +8,23 @@ # http://www.apache.org/licenses/LICENSE-2.0 # import sys +import time +import os import functest.utils.functest_logger as ft_logger +import functest.utils.functest_utils as ft_utils import functest.utils.openstack_utils as os_utils +import re logger = ft_logger.Logger("sndvpn_test_utils").getLogger() +REPO_PATH = os.environ['repos_dir'] + '/sdnvpn/' +config_file = REPO_PATH + 'test/functest/config.yaml' + +DEFAULT_FLAVOR = ft_utils.get_parameter_from_yaml( + "defaults.flavor", config_file) + def create_network(neutron_client, net, subnet1, cidr1, router, subnet2=None, cidr2=None): @@ -45,7 +55,6 @@ def create_network(neutron_client, net, subnet1, cidr1, def create_instance(nova_client, name, - flavor, image_id, network_id, sg_id, @@ -53,16 +62,21 @@ def create_instance(nova_client, fixed_ip=None, compute_node='', userdata=None, - files=None): + files=None, + **kwargs + ): + if 'flavor' not in kwargs: + kwargs['flavor'] = DEFAULT_FLAVOR + logger.info("Creating instance '%s'..." % name) logger.debug( "Configuration:\n name=%s \n flavor=%s \n image=%s \n" " network=%s\n secgroup=%s \n hypervisor=%s \n" " fixed_ip=%s\n files=%s\n userdata=\n%s\n" - % (name, flavor, image_id, network_id, sg_id, + % (name, kwargs['flavor'], image_id, network_id, sg_id, compute_node, fixed_ip, files, userdata)) instance = os_utils.create_instance_and_wait_for_active( - flavor, + kwargs['flavor'], image_id, network_id, name, @@ -110,3 +124,120 @@ def generate_ping_userdata(ips_array): " sleep 1\n" "done\n" % ips) + + +def generate_userdata_common(): + return ("#!/bin/sh\n" + "sudo mkdir -p /home/cirros/.ssh/\n" + "sudo chown cirros:cirros /home/cirros/.ssh/\n" + "sudo chown cirros:cirros /home/cirros/id_rsa\n" + "mv /home/cirros/id_rsa /home/cirros/.ssh/\n" + "sudo echo ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAAAgnWtSS98Am516e" + "stBsq0jbyOB4eLMUYDdgzsUHsnxFQCtACwwAg9/2uq3FoGUBUWeHZNsT6jcK9" + "sCMEYiS479CUCzbrxcd8XaIlK38HECcDVglgBNwNzX/WDfMejXpKzZG61s98rU" + "ElNvZ0YDqhaqZGqxIV4ejalqLjYrQkoly3R+2k= " + "cirros@test1>/home/cirros/.ssh/authorized_keys\n" + "sudo chown cirros:cirros /home/cirros/.ssh/authorized_keys\n" + "chmod 700 /home/cirros/.ssh\n" + "chmod 644 /home/cirros/.ssh/authorized_keys\n" + "chmod 600 /home/cirros/.ssh/id_rsa\n" + ) + + +def generate_userdata_with_ssh(ips_array): + u1 = generate_userdata_common() + + ips = "" + for ip in ips_array: + ips = ("%s %s" % (ips, ip)) + + ips = ips.replace(' ', ' ') + u2 = ("#!/bin/sh\n" + "set%s\n" + "while true; do\n" + " for i do\n" + " ip=$i\n" + " hostname=$(ssh -y -i /home/cirros/.ssh/id_rsa " + "cirros@$ip 'hostname' /dev/null)\n" + " RES=$?\n" + " if [ \"Z$RES\" = \"Z0\" ]; then echo $ip $hostname;\n" + " else echo $ip 'not reachable';fi;\n" + " done\n" + " sleep 1\n" + "done\n" + % ips) + return (u1 + u2) + + +def wait_for_instance(instance): + logger.info("Waiting for instance %s to get a DHCP lease..." % instance.id) + # The sleep this function replaced waited for 80s + tries = 40 + sleep_time = 2 + pattern = "Lease of .* obtained, lease time" + expected_regex = re.compile(pattern) + console_log = "" + while tries > 0 and not expected_regex.search(console_log): + console_log = instance.get_console_output() + time.sleep(sleep_time) + tries -= 1 + + if not expected_regex.search(console_log): + logger.error("Instance %s seems to have failed leasing an IP." + % instance.id) + return False + return True + + +def wait_for_instances_up(*args): + check = [wait_for_instance(instance) for instance in args] + return all(check) + + +def wait_for_bgp_net_assoc(neutron_client, bgpvpn_id, net_id): + tries = 30 + sleep_time = 1 + nets = [] + logger.debug("Waiting for network %s to associate with BGPVPN %s " + % (bgpvpn_id, net_id)) + + while tries > 0 and net_id not in nets: + nets = os_utils.get_bgpvpn_networks(neutron_client, bgpvpn_id) + time.sleep(sleep_time) + tries -= 1 + if net_id not in nets: + logger.error("Association of network %s with BGPVPN %s failed" % + (net_id, bgpvpn_id)) + return False + return True + + +def wait_for_bgp_net_assocs(neutron_client, bgpvpn_id, *args): + check = [wait_for_bgp_net_assoc(neutron_client, bgpvpn_id, id) + for id in args] + # Return True if all associations succeeded + return all(check) + + +def wait_for_bgp_router_assoc(neutron_client, bgpvpn_id, router_id): + tries = 30 + sleep_time = 1 + routers = [] + logger.debug("Waiting for router %s to associate with BGPVPN %s " + % (bgpvpn_id, router_id)) + while tries > 0 and router_id not in routers: + routers = os_utils.get_bgpvpn_routers(neutron_client, bgpvpn_id) + time.sleep(sleep_time) + tries -= 1 + if router_id not in routers: + logger.error("Association of router %s with BGPVPN %s failed" % + (router_id, bgpvpn_id)) + return False + return True + + +def wait_for_bgp_router_assocs(neutron_client, bgpvpn_id, *args): + check = [wait_for_bgp_router_assoc(neutron_client, bgpvpn_id, id) + for id in args] + # Return True if all associations succeeded + return all(check) -- cgit 1.2.3-korg