diff options
Diffstat (limited to 'ci')
-rw-r--r-- | ci/__init__.py | 0 | ||||
-rwxr-xr-x | ci/check_os.sh | 90 | ||||
-rw-r--r-- | ci/prepare_env.py | 301 | ||||
-rw-r--r-- | ci/testcases.yaml | 196 | ||||
-rw-r--r-- | ci/tier_builder.py | 61 | ||||
-rw-r--r-- | ci/tier_handler.py | 75 |
6 files changed, 723 insertions, 0 deletions
diff --git a/ci/__init__.py b/ci/__init__.py new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/ci/__init__.py diff --git a/ci/check_os.sh b/ci/check_os.sh new file mode 100755 index 000000000..bb1335306 --- /dev/null +++ b/ci/check_os.sh @@ -0,0 +1,90 @@ +#!/bin/bash +# +# Simple script to check the basic OpenStack clients +# +# Author: +# jose.lausuch@ericsson.com +# + +verify_connectivity() { + for i in $(seq 0 9); do + if echo "test" | nc -v -w 10 $1 $2 &>/dev/null; then + return 0 + fi + sleep 1 + done + return 1 +} + + +if [ -z $OS_AUTH_URL ];then + echo "ERROR: OS_AUTH_URL environment variable missing... Have you sourced the OpenStack credentials?" + exit 1 +fi + + +echo "Checking OpenStack endpoints:" +publicURL=$OS_AUTH_URL +publicIP=$(echo $publicURL|sed 's/^.*http\:\/\///'|sed 's/.[^:]*$//') +publicPort=$(echo $publicURL|sed 's/^.*://'|sed 's/.[^\/]*$//') +echo ">>Verifying connectivity to the public endpoint $publicIP:$publicPort..." +verify_connectivity $publicIP $publicPort +RETVAL=$? +if [ $RETVAL -ne 0 ]; then + echo "ERROR: Cannot talk to the public endpoint $publicIP:$publicPort ." + echo "OS_AUTH_URL=$OS_AUTH_URL" + exit 1 +fi +echo " ...OK" + +adminURL=$(openstack catalog show identity |grep adminURL|awk '{print $4}') +adminIP=$(echo $adminURL|sed 's/^.*http\:\/\///'|sed 's/.[^:]*$//') +adminPort=$(echo $adminURL|sed 's/^.*://'|sed 's/.[^\/]*$//') +echo ">>Verifying connectivity to the admin endpoint $adminIP:$adminPort..." +verify_connectivity $adminIP $adminPort +RETVAL=$? +if [ $RETVAL -ne 0 ]; then + echo "ERROR: Cannot talk to the admin endpoint $adminIP:$adminPort ." + echo "$adminURL" + exit 1 +fi +echo " ...OK" + + +echo "Checking OpenStack basic services:" +commands=('openstack endpoint list' 'nova list' 'neutron net-list' \ + 'glance image-list' 'cinder list') +for cmd in "${commands[@]}" +do + service=$(echo $cmd | awk '{print $1}') + echo ">>Checking $service service..." + $cmd &>/dev/null + result=$? + if [ $result -ne 0 ]; + then + echo "ERROR: Failed execution $cmd. The $service does not seem to be working." + exit 1 + else + echo " ...OK" + fi +done + +echo "OpenStack services are OK." + +echo "Checking External network..." +networks=($(neutron net-list | tail -n +4 | head -n -1 | awk '{print $2}')) +is_external=False +for net in "${networks[@]}" +do + is_external=$(neutron net-show $net|grep "router:external"|awk '{print $4}') + if [ $is_external == "True" ]; then + echo "External network found: $net" + break + fi +done +if [ $is_external == "False" ]; then + echo "ERROR: There are no external networks in the deployment." + exit 1 +fi + +exit 0 diff --git a/ci/prepare_env.py b/ci/prepare_env.py new file mode 100644 index 000000000..710a767e5 --- /dev/null +++ b/ci/prepare_env.py @@ -0,0 +1,301 @@ +#!/bin/bash +# +# Author: Jose Lausuch (jose.lausuch@ericsson.com) +# +# Installs the Functest framework within the Docker container +# and run the tests automatically +# +# +# 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 argparse +import os +import re +import subprocess +import sys +import yaml + +import functest.ci.tier_builder as tb +import functest.utils.functest_logger as ft_logger +import functest.utils.functest_utils as ft_utils +import functest.utils.generate_defaults as gen_def +import functest.utils.openstack_utils as os_utils + + +""" arguments """ +actions = ['start', 'check'] +parser = argparse.ArgumentParser() +parser.add_argument("action", help="Possible actions are: " + "'{d[0]}|{d[1]}' ".format(d=actions)) +parser.add_argument("-d", "--debug", help="Debug mode", action="store_true") +args = parser.parse_args() + + +""" logging configuration """ +logger = ft_logger.Logger("prepare_env").getLogger() + + +""" global variables """ +INSTALLERS = ['fuel', 'compass', 'apex', 'joid'] +CI_INSTALLER_TYPE = "" +CI_INSTALLER_IP = "" +CI_SCENARIO = "" +CI_DEBUG = False +REPOS_DIR = os.getenv('repos_dir') +FUNCTEST_REPO = REPOS_DIR + '/functest/' + +with open("/home/opnfv/repos/functest/testcases/config_functest.yaml") as f: + functest_yaml = yaml.safe_load(f) + +FUNCTEST_CONF_DIR = functest_yaml.get("general").get( + "directories").get("dir_functest_conf") + +FUNCTEST_DATA_DIR = functest_yaml.get("general").get( + "directories").get("dir_functest_data") +FUNCTEST_RESULTS_DIR = functest_yaml.get("general").get( + "directories").get("dir_results") +DEPLOYMENT_MAME = functest_yaml.get("rally").get("deployment_name") +TEMPEST_REPO_DIR = functest_yaml.get("general").get( + "directories").get("dir_repo_tempest") + +ENV_FILE = FUNCTEST_CONF_DIR + "/env_active" + + +def print_separator(): + logger.info("==============================================") + + +def check_env_variables(): + print_separator() + logger.info("Checking environment variables...") + global CI_INSTALLER_TYPE + global CI_INSTALLER_IP + global CI_DEBUG + global CI_SCENARIO + CI_INSTALLER_TYPE = os.getenv('INSTALLER_TYPE') + CI_INSTALLER_IP = os.getenv('INSTALLER_IP') + CI_SCENARIO = os.getenv('DEPLOY_SCENARIO') + CI_NODE = os.getenv('NODE_NAME') + CI_BUILD_TAG = os.getenv('BUILD_TAG') + CI_DEBUG = os.getenv('CI_DEBUG') + + if CI_INSTALLER_TYPE is None: + logger.warning("The env variable 'INSTALLER_TYPE' is not defined.") + CI_INSTALLER_TYPE = "undefined" + else: + if os.getenv('INSTALLER_TYPE') not in INSTALLERS: + logger.warning("INSTALLER_TYPE=%s is not a valid OPNFV installer. " + "Available OPNFV Installers are : %s." + "Setting INSTALLER_TYPE=undefined." % INSTALLERS) + CI_INSTALLER_TYPE = "undefined" + else: + logger.info(" INSTALLER_TYPE=%s" % CI_INSTALLER_TYPE) + + if CI_INSTALLER_IP is None: + logger.warning("The env variable 'INSTALLER_IP' is not defined. " + "It is needed to fetch the OpenStack credentials. " + "If the credentials are not provided to the " + "container as a volume, please add this env variable " + "to the 'docker run' command.") + else: + logger.info(" INSTALLER_IP=%s" % CI_INSTALLER_IP) + + if CI_SCENARIO is None: + logger.warning("The env variable 'DEPLOY_SCENARIO' is not defined. " + "Setting CE_SCENARIO=undefined.") + CI_SCENARIO = "undefined" + else: + logger.info(" DEPLOY_SCENARIO=%s" % CI_SCENARIO) + if CI_DEBUG: + logger.info(" CI_DEBUG=%s" % CI_DEBUG) + + if CI_NODE: + logger.info(" NODE_NAME=%s" % CI_NODE) + + if CI_BUILD_TAG: + logger.info(" BUILD_TAG=%s" % CI_BUILD_TAG) + + +def create_directories(): + print_separator() + logger.info("Creating needed directories...") + if not os.path.exists(FUNCTEST_CONF_DIR): + os.makedirs(FUNCTEST_CONF_DIR) + logger.info(" %s created." % FUNCTEST_CONF_DIR) + else: + logger.debug(" %s already exists." % FUNCTEST_CONF_DIR) + + if not os.path.exists(FUNCTEST_DATA_DIR): + os.makedirs(FUNCTEST_DATA_DIR) + logger.info(" %s created." % FUNCTEST_DATA_DIR) + else: + logger.debug(" %s already exists." % FUNCTEST_DATA_DIR) + + ODL_RESULTS_DIR = FUNCTEST_RESULTS_DIR + "/ODL/" + if not os.path.exists(ODL_RESULTS_DIR): + os.makedirs(ODL_RESULTS_DIR) + logger.info(" %s created." % ODL_RESULTS_DIR) + else: + logger.debug(" %s already exists." % ODL_RESULTS_DIR) + + +def source_rc_file(): + print_separator() + logger.info("Fetching RC file...") + rc_file = os.getenv('creds') + if rc_file is None: + logger.warning("The environment variable 'creds' must be set and" + "pointing to the local RC file. Using default: " + "/home/opnfv/functest/conf/openstack.creds ...") + rc_file = "/home/opnfv/functest/conf/openstack.creds ..." + + if not os.path.isfile(rc_file): + logger.info("RC file not provided. " + "Fetching it from the installer...") + if CI_INSTALLER_IP is None: + logger.error("The env variable CI_INSTALLER_IP must be provided in" + " order to fetch the credentials from the installer.") + sys.exit("Missing CI_INSTALLER_IP.") + if CI_INSTALLER_TYPE not in INSTALLERS: + logger.error("Cannot fetch credentials. INSTALLER_TYPE=%s is " + "not a valid OPNFV installer. Available " + "installers are : %s." % INSTALLERS) + sys.exit("Wrong INSTALLER_TYPE.") + + cmd = ("/home/opnfv/repos/releng/utils/fetch_os_creds.sh " + "-d %s -i %s -a %s" + % (rc_file, CI_INSTALLER_TYPE, CI_INSTALLER_IP)) + logger.debug("Executing command: %s" % cmd) + p = subprocess.Popen(cmd.split(), stdout=subprocess.PIPE) + output = p.communicate()[0] + logger.debug(output) + if p.returncode != 0: + logger.error("Failed to fetch credentials from installer.") + sys.exit(1) + else: + logger.info("RC file provided in %s." % rc_file) + if os.path.getsize(rc_file) == 0: + logger.error("The file %s is empty." % rc_file) + sys.exit(1) + + logger.info("Sourcing the OpenStack RC file...") + creds = os_utils.source_credentials(rc_file) + str = "" + for key, value in creds.iteritems(): + if re.search("OS_", key): + str += "\n\t\t\t\t\t\t " + key + "=" + value + logger.debug("Used credentials: %s" % str) + + +def verify_deployment(): + print_separator() + logger.info("Verifying OpenStack services...") + cmd = ("%s/ci/check_os.sh" % FUNCTEST_REPO) + + logger.debug("Executing command: %s" % cmd) + p = subprocess.Popen(cmd, stdout=subprocess.PIPE, shell=True) + + while p.poll() is None: + line = p.stdout.readline().rstrip() + if "ERROR" in line: + logger.error(line) + sys.exit("Problem while running 'check_os.sh'.") + logger.debug(line) + + +def install_rally(): + print_separator() + logger.info("Creating Rally environment...") + + cmd = "rally deployment destroy opnfv-rally" + ft_utils.execute_command(cmd, logger=None, exit_on_error=False) + + cmd = "rally deployment create --fromenv --name=" + DEPLOYMENT_MAME + if not ft_utils.execute_command(cmd, logger): + logger.error("Problem while creating Rally deployment.") + sys.exit(cmd) + + logger.info("Installing tempest from existing repo...") + cmd = ("rally verify install --source " + TEMPEST_REPO_DIR + + " --system-wide") + if not ft_utils.execute_command(cmd, logger): + logger.error("Problem while installing Tempest.") + sys.exit(cmd) + + cmd = "rally deployment check" + if not ft_utils.execute_command(cmd, logger): + logger.error("OpenStack not responding or faulty Rally deployment.") + sys.exit(cmd) + + cmd = "rally show images" + if not ft_utils.execute_command(cmd, logger): + logger.error("Problem while listing OpenStack images.") + sys.exit(cmd) + + cmd = "rally show flavors" + if not ft_utils.execute_command(cmd, logger): + logger.error("Problem while showing OpenStack flavors.") + sys.exit(cmd) + + +def generate_os_defaults(): + print_separator() + logger.info("Generating OpenStack defaults...") + gen_def.main() + + +def generate_tiers(): + print_separator() + logger.info("Generating Tiers and test cases...") + file = FUNCTEST_REPO + "/ci/testcases.yaml" + + t = tb.TierBuilder(CI_INSTALLER_TYPE, CI_SCENARIO, file) + logger.info("Tiers and tests to be executed:\n\n%s" % t) + + +def check_environment(): + msg_not_active = "The Functest environment is not installed." + if not os.path.isfile(ENV_FILE): + logger.error(msg_not_active) + sys.exit(1) + + with open(ENV_FILE, "r") as env_file: + s = env_file.read() + if not re.search("1", s): + logger.error(msg_not_active) + sys.exit(1) + + logger.info("Functest environment installed.") + + +def main(): + if not (args.action in actions): + logger.error('Argument not valid.') + sys.exit() + + if args.action == "start": + print ("\n######### Preparing Functest environment #########\n") + check_env_variables() + create_directories() + source_rc_file() + verify_deployment() + install_rally() + generate_os_defaults() + generate_tiers() + + with open(ENV_FILE, "w") as env_file: + env_file.write("1") + + check_environment() + + if args.action == "check": + check_environment() + + exit(0) + +if __name__ == '__main__': + main() diff --git a/ci/testcases.yaml b/ci/testcases.yaml new file mode 100644 index 000000000..42458bbef --- /dev/null +++ b/ci/testcases.yaml @@ -0,0 +1,196 @@ +tiers: + - + name: healthcheck + order: 0 + ci: daily + description : >- + This is the first tier to be executed to verify the basic + operations in the VIM. + testcases: + - + name: healthcheck + description: >- + This test case verifies the basic OpenStack services like + Keystone, Glance, Cinder, Neutron and Nova. + + dependencies: + installer: '' + scenario: '' + + - + name: smoke + order: 1 + ci: daily + description : >- + This is the second tier in Functest and consist of a set of basic + Functional tests to validate the OpenStack deployment. + testcases: + - + name: vping_ssh + description: |- + This test case verifies: + ····1) SSH to an instance using floating IPs over the public network. + ····2) Connectivity between 2 instances over a private network. + dependencies: + installer: '' + scenario: '^((?!bgpvpn).)*$' + + - + name: vping_userdata + description: |- + This test case verifies: + ····1) Boot a VM with given userdata. + ····2) Connectivity between 2 instances over a private network. + dependencies: + installer: '' + scenario: '' + + - + name: tempest_smoke_serial + description: >- + This test case runs the smoke subset of the OpenStack + Tempest suite. The list of test cases is generated by + Tempest automatically and depend on the parameters of + the OpenStack deplopyment. + dependencies: + installer: '' + scenario: '' + + - + name: rally_sanity + description: >- + This test case runs a sub group of tests of the OpenStack + Rally suite in smoke mode. + dependencies: + installer: '' + scenario: '' + + - + name: security_groups + description: >- + This test case verifies the functionality of the OpenStack + security groups and that the port rules created are + fullfilled. + dependencies: + installer: '' + scenario: '' + + - + name: sdn_suites + order: 2 + ci: daily + description : >- + This tier contains the test suites corresponding to the different + SDN Controllers existing in OPNFV. + testcases: + - + name: odl + description: >- + Test Suite for the OpenDaylight SDN Controller. It integrates + some test suites from upstream using Robot as the test + framework. + dependencies: + installer: '' + scenario: 'odl' + + - + name: onos + description: >- + Test Suite for the ONOS SDN Controller. It integrates + some test suites from upstream using TestON as the test + framework. + dependencies: + installer: '' + scenario: 'onos' + + - + name: ovno + description: >- + Test Suite for the Open Contrail SDN Controller. + framework. + dependencies: + installer: '' + scenario: 'ocl' + + - + name: features + order: 3 + ci: daily + description : >- + This tier contains the test suites from feature projects + integrated in functest + testcases: + - + name: promise + description: >- + Test suite from Promise project. + dependencies: + installer: '(fuel)|(joid)' + scenario: '' + + - + name: doctor + description: >- + Test suite from Dcotor project. + dependencies: + installer: 'apex' + scenario: '' + + - + name: sdnvpn + description: >- + Test suite from SDNVPN project. + dependencies: + installer: '(fuel)|(apex)' + scenario: 'bgpvpn' + + - + name: tempest + order: 4 + ci: weekly + description : >- + This tier contains the test suites from feature projects + integrated in functest + testcases: + - + name: tempest_full_parallel + description: >- + This test case runs the full set of the OpenStack + Tempest suite. The list of test cases is generated by + Tempest automatically and depend on the parameters of + the OpenStack deplopyment. + dependencies: + installer: '' + scenario: '' + + - + name: rally + order: 5 + ci: weekly + description : >- + This tier contains the Rally suite from the OpenStack community. + testcases: + - + name: rally_full + description: >- + This test case runs the full suite of scenarios of the OpenStack + Rally suite using several threads and iterations. + dependencies: + installer: '' + scenario: '' + + - + name: vnf + order: 6 + ci: weekly + description : >- + This tier contains a collection of VNF test cases. + testcases: + - + name: vims + description: >- + This test case deploys an OpenSource vIMS solution from Clearwater + using the Cloudify orchestrator. It also runs some signaling traffic. + dependencies: + installer: '' + scenario: '(ocl)|(nosdn)|^(os-odl)((?!bgpvpn).)*$' diff --git a/ci/tier_builder.py b/ci/tier_builder.py new file mode 100644 index 000000000..e66e97a38 --- /dev/null +++ b/ci/tier_builder.py @@ -0,0 +1,61 @@ +#!/usr/bin/env python +# +# jose.lausuch@ericsson.com +# 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 yaml + +import tier_handler as th + + +class TierBuilder: + def __init__(self, ci_installer, ci_scenario, testcases_file): + self.ci_installer = ci_installer + self.ci_scenario = ci_scenario + self.testcases_file = testcases_file + self.dic_tier_array = None + self.tier_objects = [] + self.testcases_yaml = None + self.generate_tiers() + + def read_test_yaml(self): + with open(self.testcases_file) as f: + self.testcases_yaml = yaml.safe_load(f) + + self.dic_tier_array = [] + for tier in self.testcases_yaml.get("tiers"): + self.dic_tier_array.append(tier) + + def generate_tiers(self): + if self.dic_tier_array is None: + self.read_test_yaml() + + del self.tier_objects[:] + for dic_tier in self.dic_tier_array: + tier = th.Tier(name=dic_tier['name'], + order=dic_tier['order'], + ci=dic_tier['ci'], + description=dic_tier['description']) + + for dic_testcase in dic_tier['testcases']: + installer = dic_testcase['dependencies']['installer'] + scenario = dic_testcase['dependencies']['scenario'] + dep = th.Dependency(installer, scenario) + + testcase = th.TestCase(name=dic_testcase['name'], + dependency=dep, + description=dic_testcase['description']) + if testcase.is_compatible(self.ci_installer, self.ci_scenario): + tier.add_test(testcase) + + self.tier_objects.append(tier) + + def __str__(self): + output = "" + for i in range(0, len(self.tier_objects)): + output += str(self.tier_objects[i]) + "\n" + return output diff --git a/ci/tier_handler.py b/ci/tier_handler.py new file mode 100644 index 000000000..0b7559498 --- /dev/null +++ b/ci/tier_handler.py @@ -0,0 +1,75 @@ +#!/usr/bin/env python +# +# jose.lausuch@ericsson.com +# 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 re + + +class Tier: + def __init__(self, name, order, ci, description=""): + self.tests_array = [] + self.name = name + self.order = order + self.ci = ci + self.description = description + + def add_test(self, testcase): + self.tests_array.append(testcase) + + def get_tests(self): + array_str = [] + for test in self.tests_array: + array_str.append(test.name) + return array_str + + def __str__(self): + return ("Tier info:\n" + " Name: " + self.name + "\n" + " Description: " + self.description + "\n" + " Order: " + str(self.order) + "\n" + " Test cases: " + str(self.get_tests()) + "\n") + + +class TestCase: + def __init__(self, name, dependency, description=""): + self.name = name + self.dependency = dependency + self.description = description + + def is_compatible(self, ci_installer, ci_scenario): + if re.search(self.dependency.get_installer(), ci_installer) is None: + return False + + if re.search(self.dependency.get_scenario(), ci_scenario) is None: + return False + + return True + + def __str__(self): + return ("Testcase info:\n" + " Name: " + self.name + "\n" + " Description: " + self.description + "\n" + " " + str(self.dependency) + "\n") + + +class Dependency: + def __init__(self, installer, scenario): + self.installer = installer + self.scenario = scenario + + def get_installer(self): + return self.installer + + def get_scenario(self): + return self.scenario + + def __str__(self): + return ("Dependency info:\n" + " installer: " + self.installer + "\n" + " scenario: " + self.scenario + "\n") |