diff options
Diffstat (limited to 'functest/utils/functest_utils.py')
-rw-r--r-- | functest/utils/functest_utils.py | 449 |
1 files changed, 449 insertions, 0 deletions
diff --git a/functest/utils/functest_utils.py b/functest/utils/functest_utils.py new file mode 100644 index 00000000..2d4a652d --- /dev/null +++ b/functest/utils/functest_utils.py @@ -0,0 +1,449 @@ +#!/usr/bin/env python +# +# jose.lausuch@ericsson.com +# valentin.boucher@orange.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 json +import os +import re +import shutil +import subprocess +import sys +import urllib2 +from datetime import datetime as dt + +import dns.resolver +import requests +import yaml +from git import Repo + +import functest.utils.functest_logger as ft_logger + +logger = ft_logger.Logger("functest_utils").getLogger() + +REPOS_DIR = os.getenv('repos_dir') +FUNCTEST_REPO = ("%s/functest" % REPOS_DIR) + + +# ---------------------------------------------------------- +# +# INTERNET UTILS +# +# ----------------------------------------------------------- +def check_internet_connectivity(url='http://www.opnfv.org/'): + """ + Check if there is access to the internet + """ + try: + urllib2.urlopen(url, timeout=5) + return True + except urllib2.URLError: + return False + + +def download_url(url, dest_path): + """ + Download a file to a destination path given a URL + """ + name = url.rsplit('/')[-1] + dest = dest_path + "/" + name + try: + response = urllib2.urlopen(url) + except (urllib2.HTTPError, urllib2.URLError): + return False + + with open(dest, 'wb') as f: + shutil.copyfileobj(response, f) + return True + + +# ---------------------------------------------------------- +# +# CI UTILS +# +# ----------------------------------------------------------- +def get_git_branch(repo_path): + """ + Get git branch name + """ + repo = Repo(repo_path) + branch = repo.active_branch + return branch.name + + +def get_installer_type(): + """ + Get installer type (fuel, apex, joid, compass) + """ + try: + installer = os.environ['INSTALLER_TYPE'] + except KeyError: + logger.error("Impossible to retrieve the installer type") + installer = "Unknown_installer" + + return installer + + +def get_scenario(): + """ + Get scenario + """ + try: + scenario = os.environ['DEPLOY_SCENARIO'] + except KeyError: + logger.error("Impossible to retrieve the scenario") + scenario = "Unknown_scenario" + + return scenario + + +def get_version(): + """ + Get version + """ + # Use the build tag to retrieve the version + # By default version is unknown + # if launched through CI the build tag has the following format + # jenkins-<project>-<installer>-<pod>-<job>-<branch>-<id> + # e.g. jenkins-functest-fuel-opnfv-jump-2-daily-master-190 + # use regex to match branch info + rule = "daily-(.+?)-[0-9]*" + build_tag = get_build_tag() + m = re.search(rule, build_tag) + if m: + return m.group(1) + else: + return "unknown" + + +def get_pod_name(): + """ + Get PoD Name from env variable NODE_NAME + """ + try: + return os.environ['NODE_NAME'] + except KeyError: + logger.error( + "Unable to retrieve the POD name from environment. " + + "Using pod name 'unknown-pod'") + return "unknown-pod" + + +def get_build_tag(): + """ + Get build tag of jenkins jobs + """ + try: + build_tag = os.environ['BUILD_TAG'] + except KeyError: + logger.error("Impossible to retrieve the build tag") + build_tag = "unknown_build_tag" + + return build_tag + + +def get_db_url(): + """ + Returns DB URL + """ + return get_functest_config('results.test_db_url') + + +def logger_test_results(project, case_name, status, details): + pod_name = get_pod_name() + scenario = get_scenario() + version = get_version() + build_tag = get_build_tag() + + logger.info( + "\n" + "****************************************\n" + "\t %(p)s/%(n)s results \n\n" + "****************************************\n" + "DB:\t%(db)s\n" + "pod:\t%(pod)s\n" + "version:\t%(v)s\n" + "scenario:\t%(s)s\n" + "status:\t%(c)s\n" + "build tag:\t%(b)s\n" + "details:\t%(d)s\n" + % {'p': project, + 'n': case_name, + 'db': get_db_url(), + 'pod': pod_name, + 'v': version, + 's': scenario, + 'c': status, + 'b': build_tag, + 'd': details}) + + +def push_results_to_db(project, case_name, + start_date, stop_date, criteria, details): + """ + POST results to the Result target DB + """ + # Retrieve params from CI and conf + url = get_db_url() + "/results" + + try: + installer = os.environ['INSTALLER_TYPE'] + scenario = os.environ['DEPLOY_SCENARIO'] + pod_name = os.environ['NODE_NAME'] + build_tag = os.environ['BUILD_TAG'] + except KeyError as e: + logger.error("Please set env var: " + str(e)) + return False + rule = "daily-(.+?)-[0-9]*" + m = re.search(rule, build_tag) + if m: + version = m.group(1) + else: + logger.error("Please fix BUILD_TAG env var: " + build_tag) + return False + test_start = dt.fromtimestamp(start_date).strftime('%Y-%m-%d %H:%M:%S') + test_stop = dt.fromtimestamp(stop_date).strftime('%Y-%m-%d %H:%M:%S') + + params = {"project_name": project, "case_name": case_name, + "pod_name": pod_name, "installer": installer, + "version": version, "scenario": scenario, "criteria": criteria, + "build_tag": build_tag, "start_date": test_start, + "stop_date": test_stop, "details": details} + + error = None + headers = {'Content-Type': 'application/json'} + try: + r = requests.post(url, data=json.dumps(params), headers=headers) + logger.debug(r) + r.raise_for_status() + except requests.RequestException as exc: + if 'r' in locals(): + error = ("Pushing Result to DB(%s) failed: %s" % + (r.url, r.content)) + else: + error = ("Pushing Result to DB(%s) failed: %s" % (url, exc)) + except Exception as e: + error = ("Error [push_results_to_db(" + "DB: '%(db)s', " + "project: '%(project)s', " + "case: '%(case)s', " + "pod: '%(pod)s', " + "version: '%(v)s', " + "scenario: '%(s)s', " + "criteria: '%(c)s', " + "build_tag: '%(t)s', " + "details: '%(d)s')]: " + "%(error)s" % + { + 'db': url, + 'project': project, + 'case': case_name, + 'pod': pod_name, + 'v': version, + 's': scenario, + 'c': criteria, + 't': build_tag, + 'd': details, + 'error': e + }) + finally: + if error: + logger.error(error) + return False + return True + + +def get_resolvconf_ns(): + """ + Get nameservers from current resolv.conf + """ + nameservers = [] + rconf = open("/etc/resolv.conf", "r") + line = rconf.readline() + resolver = dns.resolver.Resolver() + while line: + ip = re.search(r"\b(?:[0-9]{1,3}\.){3}[0-9]{1,3}\b", line) + if ip: + resolver.nameservers = [str(ip)] + try: + result = resolver.query('opnfv.org')[0] + if result != "": + nameservers.append(ip.group()) + except dns.exception.Timeout: + pass + line = rconf.readline() + return nameservers + + +def get_ci_envvars(): + """ + Get the CI env variables + """ + ci_env_var = { + "installer": os.environ.get('INSTALLER_TYPE'), + "scenario": os.environ.get('DEPLOY_SCENARIO')} + return ci_env_var + + +def execute_command(cmd, info=False, error_msg="", + verbose=True, output_file=None): + if not error_msg: + error_msg = ("The command '%s' failed." % cmd) + msg_exec = ("Executing command: '%s'" % cmd) + if verbose: + if info: + logger.info(msg_exec) + else: + logger.debug(msg_exec) + p = subprocess.Popen(cmd, shell=True, stdout=subprocess.PIPE, + stderr=subprocess.STDOUT) + if output_file: + f = open(output_file, "w") + for line in iter(p.stdout.readline, b''): + if output_file: + f.write(line) + else: + line = line.replace('\n', '') + print line + sys.stdout.flush() + if output_file: + f.close() + p.stdout.close() + returncode = p.wait() + if returncode != 0: + if verbose: + logger.error(error_msg) + + return returncode + + +def get_deployment_dir(): + """ + Returns current Rally deployment directory + """ + deployment_name = get_functest_config('rally.deployment_name') + rally_dir = get_functest_config('general.directories.dir_rally_inst') + cmd = ("rally deployment list | awk '/" + deployment_name + + "/ {print $2}'") + p = subprocess.Popen(cmd, shell=True, + stdout=subprocess.PIPE, + stderr=subprocess.STDOUT) + deployment_uuid = p.stdout.readline().rstrip() + if deployment_uuid == "": + logger.error("Rally deployment not found.") + exit(-1) + deployment_dir = (rally_dir + "/tempest/for-deployment-" + + deployment_uuid) + return deployment_dir + + +def get_dict_by_test(testname): + with open(get_testcases_file()) as f: + testcases_yaml = yaml.safe_load(f) + + for dic_tier in testcases_yaml.get("tiers"): + for dic_testcase in dic_tier['testcases']: + if dic_testcase['name'] == testname: + return dic_testcase + + logger.error('Project %s is not defined in testcases.yaml' % testname) + return None + + +def get_criteria_by_test(testname): + dict = get_dict_by_test(testname) + if dict: + return dict['criteria'] + return None + + +# ---------------------------------------------------------- +# +# YAML UTILS +# +# ----------------------------------------------------------- +def get_parameter_from_yaml(parameter, file): + """ + Returns the value of a given parameter in file.yaml + parameter must be given in string format with dots + Example: general.openstack.image_name + """ + with open(file) as f: + file_yaml = yaml.safe_load(f) + f.close() + value = file_yaml + for element in parameter.split("."): + value = value.get(element) + if value is None: + raise ValueError("The parameter %s is not defined in" + " config_functest.yaml" % parameter) + return value + + +def get_functest_config(parameter): + yaml_ = os.environ["CONFIG_FUNCTEST_YAML"] + return get_parameter_from_yaml(parameter, yaml_) + + +def check_success_rate(case_name, success_rate): + success_rate = float(success_rate) + criteria = get_criteria_by_test(case_name) + + def get_criteria_value(op): + return float(criteria.split(op)[1].rstrip('%')) + + status = 'FAIL' + ops = ['==', '>='] + for op in ops: + if op in criteria: + c_value = get_criteria_value(op) + if eval("%s %s %s" % (success_rate, op, c_value)): + status = 'PASS' + break + + return status + + +def merge_dicts(dict1, dict2): + for k in set(dict1.keys()).union(dict2.keys()): + if k in dict1 and k in dict2: + if isinstance(dict1[k], dict) and isinstance(dict2[k], dict): + yield (k, dict(merge_dicts(dict1[k], dict2[k]))) + else: + yield (k, dict2[k]) + elif k in dict1: + yield (k, dict1[k]) + else: + yield (k, dict2[k]) + + +def check_test_result(test_name, ret, start_time, stop_time): + def get_criteria_value(): + return get_criteria_by_test(test_name).split('==')[1].strip() + + status = 'FAIL' + if str(ret) == get_criteria_value(): + status = 'PASS' + + details = { + 'timestart': start_time, + 'duration': round(stop_time - start_time, 1), + 'status': status, + } + + return status, details + + +def get_testcases_file(): + return FUNCTEST_REPO + "/functest/ci/testcases.yaml" + + +def get_functest_yaml(): + with open(os.environ["CONFIG_FUNCTEST_YAML"]) as f: + functest_yaml = yaml.safe_load(f) + f.close() + return functest_yaml |