diff options
Diffstat (limited to 'dovetail')
-rw-r--r-- | dovetail/__init__.py | 0 | ||||
-rw-r--r-- | dovetail/cert/basic.yml | 4 | ||||
-rw-r--r-- | dovetail/conf/__init__.py | 0 | ||||
-rw-r--r-- | dovetail/conf/dovetail_config.py | 28 | ||||
-rw-r--r-- | dovetail/conf/dovetail_config.yml | 20 | ||||
-rw-r--r-- | dovetail/conf/functest_config.yml | 26 | ||||
-rw-r--r-- | dovetail/conf/yardstick_config.yml | 29 | ||||
-rw-r--r-- | dovetail/container.py | 69 | ||||
-rw-r--r-- | dovetail/parser.py | 40 | ||||
-rw-r--r-- | dovetail/prepare_env.py | 23 | ||||
-rw-r--r-- | dovetail/report.py | 239 | ||||
-rwxr-xr-x | dovetail/run.py | 84 | ||||
-rw-r--r-- | dovetail/testcase.py | 178 | ||||
-rw-r--r-- | dovetail/testcase/ipv6.tc001.yml | 10 | ||||
-rw-r--r-- | dovetail/utils/__init__.py | 0 | ||||
-rw-r--r-- | dovetail/utils/dovetail_logger.py | 55 | ||||
-rw-r--r-- | dovetail/utils/dovetail_utils.py | 87 |
17 files changed, 892 insertions, 0 deletions
diff --git a/dovetail/__init__.py b/dovetail/__init__.py new file mode 100644 index 00000000..e69de29b --- /dev/null +++ b/dovetail/__init__.py diff --git a/dovetail/cert/basic.yml b/dovetail/cert/basic.yml new file mode 100644 index 00000000..25ebc7ae --- /dev/null +++ b/dovetail/cert/basic.yml @@ -0,0 +1,4 @@ +certification_basic: + name: certification_basic + testcase_list: + - dovetail.ipv6.tc001 diff --git a/dovetail/conf/__init__.py b/dovetail/conf/__init__.py new file mode 100644 index 00000000..e69de29b --- /dev/null +++ b/dovetail/conf/__init__.py diff --git a/dovetail/conf/dovetail_config.py b/dovetail/conf/dovetail_config.py new file mode 100644 index 00000000..e7942f52 --- /dev/null +++ b/dovetail/conf/dovetail_config.py @@ -0,0 +1,28 @@ +#!/usr/bin/env python +# +# grakiss.wanglei@huawei.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 +# + +CERT_PATH = './cert/' +TESTCASE_PATH = './testcase/' +SCENARIO_NAMING_FMT = 'certification_%s' + +import yaml +import os + +with open(os.path.join(os.getcwd(),'conf','dovetail_config.yml')) as f: + dovetail_config = yaml.safe_load(f) + +for extra_config_file in dovetail_config['include_config']: + with open(os.path.join(os.getcwd(),'conf',extra_config_file)) as f: + extra_config = yaml.safe_load(f) + dovetail_config.update(extra_config) + +container_config = {} + +container_config['functest'] = dovetail_config['functest'] +container_config['yardstick'] = dovetail_config['yardstick'] diff --git a/dovetail/conf/dovetail_config.yml b/dovetail/conf/dovetail_config.yml new file mode 100644 index 00000000..901988f8 --- /dev/null +++ b/dovetail/conf/dovetail_config.yml @@ -0,0 +1,20 @@ + +work_dir: /home/opnfv/dovetail +result_dir: /home/opnfv/dovetail/results +report_file: 'dovetail_report.txt' + +# used for testcase cmd template in jinja2 format +# we have two variables available now +# parameter path, use this path to walk through python object and get value +# and the python object is "testcase" object by hard-coded +parameters: + - name: testcase + path: '("name",)' + - name: script_testcase + path: '("scripts", "testcase")' + +include_config: + - functest_config.yml + - yardstick_config.yml + + diff --git a/dovetail/conf/functest_config.yml b/dovetail/conf/functest_config.yml new file mode 100644 index 00000000..cd33dc59 --- /dev/null +++ b/dovetail/conf/functest_config.yml @@ -0,0 +1,26 @@ +functest: + image_name: opnfv/functest + docker_tag: latest + envs: '-e INSTALLER_TYPE=compass -e INSTALLER_IP=192.168.200.2 + -e NODE_NAME=dovetail-pod -e DEPLOY_SCENARIO=ha_nosdn + -e BUILD_TAG=dovetail -e CI_DEBUG=true -e DEPLOY_TYPE=baremetal' + opts: '-id --privileged=true' + result_dir: '/home/opnfv/functest/results' + pre_condition: + cmds: + - 'echo test for precondition' + testcase: + pre_cmd: 'python /home/opnfv/repos/functest/ci/prepare_env.py start' + exec_cmd: 'python /home/opnfv/repos/functest/ci/run_tests.py -t {{script_testcase}} -r' + post_cmd: '' + cmds: + - 'python /home/opnfv/repos/functest/ci/prepare_env.py start' + - 'python /home/opnfv/repos/functest/ci/run_tests.py -t {{script_testcase}} -r' + post_condition: + cmds: + - '' + result: + dir: '/home/opnfv/functest/results' + store_type: 'file' + file_path: 'tempest/tempest.log' + db_url: 'http://testresults.opnfv.org/test/api/v1/results?case=%s&last=1' diff --git a/dovetail/conf/yardstick_config.yml b/dovetail/conf/yardstick_config.yml new file mode 100644 index 00000000..f7f05bcc --- /dev/null +++ b/dovetail/conf/yardstick_config.yml @@ -0,0 +1,29 @@ +yardstick: + image_name: opnfv/yardstick + docker_tag: latest + envs: '-e INSTALLER_TYPE=compass -e INSTALLER_IP=192.168.200.2 + -e NODE_NAME=dovetail-pod -e DEPLOY_SCENARIO=ha_nosdn + -e BUILD_TAG=dovetail -e CI_DEBUG=true -e DEPLOY_TYPE=baremetal + -e EXTERNAL_NETWORK=ext-net' + opts: '-id --privileged=true' + pre_condition: + cmds: + - 'source /home/opnfv/repos/yardstick/tests/ci/prepare_env.sh && + source /home/opnfv/repos/yardstick/tests/ci/clean_images.sh && cleanup' + - 'source /home/opnfv/repos/yardstick/tests/ci/prepare_env.sh && + cd /home/opnfv/repos/yardstick && source tests/ci/load_images.sh' + testcase: + cmds: + - 'mkdir -p /home/opnfv/yardstick/results/' + - 'cd /home/opnfv/repos/yardstick && source tests/ci/prepare_env.sh && + yardstick task start tests/opnfv/test_cases/{{script_testcase}}.yaml + --output-file /home/opnfv/yardstick/results/{{script_testcase}}.out &> + /home/opnfv/yardstick/results/yardstick.log' + post_condition: + cmds: + - '' + result: + dir: '/home/opnfv/yardstick/results' + store_type: 'file' + file_path: 'yardstick.log' + db_url: 'http://testresults.opnfv.org/test/api/v1/results?case=%s&last=1' diff --git a/dovetail/container.py b/dovetail/container.py new file mode 100644 index 00000000..918edb33 --- /dev/null +++ b/dovetail/container.py @@ -0,0 +1,69 @@ +#!/usr/bin/env python +# +# grakiss.wanglei@huawei.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 utils.dovetail_logger as dt_logger +import utils.dovetail_utils as dt_utils +from conf.dovetail_config import * + +logger = dt_logger.Logger('container.py').getLogger() + +class Container: + + container_list = {} + has_pull_latest_image = {'yardstick':False, 'functest':False} + + def __init__(cls): + pass + + def __str__(cls): + pass + + @classmethod + def get(cls, type): + return cls.container_list[type] + + @classmethod + def get_docker_image(cls, type): + return '%s:%s' % (dovetail_config[type]['image_name'], dovetail_config[type]['docker_tag']) + + @classmethod + def create(cls, type): + #sshkey="-v /root/.ssh/id_rsa:/root/.ssh/id_rsa " + docker_image = cls.get_docker_image(type) + envs = dovetail_config[type]['envs'] + opts = dovetail_config[type]['opts'] + sshkey = '' + result_volume = ' -v %s:%s ' % (dovetail_config['result_dir'],dovetail_config[type]['result']['dir']) + cmd = 'sudo docker run %s %s %s %s %s /bin/bash' % (opts, envs, sshkey, result_volume, docker_image) + dt_utils.exec_cmd(cmd,logger) + ret, container_id=dt_utils.exec_cmd("sudo docker ps | grep "+ docker_image + " | awk '{print $1}' | head -1",logger) + cls.container_list[type] = container_id + return container_id + + @classmethod + def pull_image(cls, type): + docker_image = cls.get_docker_image(type) + if cls.has_pull_latest_image[type] == True: + logger.debug('%s is already the newest version.' % (docker_image)) + else: + cmd = 'sudo docker pull %s' % (docker_image) + dt_utils.exec_cmd(cmd,logger) + cls.has_pull_latest_image[type] = True + + @classmethod + def clean(cls, container_id): + cmd1 = 'sudo docker stop %s' % (container_id) + dt_utils.exec_cmd(cmd1,logger) + cmd2 = 'sudo docker rm %s' % (container_id) + dt_utils.exec_cmd(cmd2,logger) + + @classmethod + def exec_cmd(cls, container_id, sub_cmd, exit_on_error=False): + cmd = 'sudo docker exec %s /bin/bash -c "%s"' % (container_id, sub_cmd) + dt_utils.exec_cmd(cmd,logger,exit_on_error) diff --git a/dovetail/parser.py b/dovetail/parser.py new file mode 100644 index 00000000..1c0c0450 --- /dev/null +++ b/dovetail/parser.py @@ -0,0 +1,40 @@ +#!/usr/bin/env python +# +# grakiss.wanglei@huawei.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 jinja2 + + +import utils.dovetail_logger as dt_logger +import utils.dovetail_utils as dt_utils + +logger = dt_logger.Logger('parser.py').getLogger() + +from conf.dovetail_config import * + +class Parser: + '''preprocess configuration files''' + + @classmethod + def parse_cmd(cls, cmd, testcase): + cmd_lines = None + try: + template = jinja2.Template(cmd, undefined=jinja2.StrictUndefined) + kwargs = {} + for arg in dovetail_config['parameters']: + path = eval(arg['path']) + logger.debug('name: %s, eval path: %s ' % (arg['name'], path)) + kwargs[arg['name']] = dt_utils.get_obj_by_path(testcase.testcase,path) + + logger.debug('kwargs: %s' % kwargs) + cmd_lines = template.render(**kwargs) + except Exception as e: + logger.error('failed to parse cmd %s, exception:%s' % (cmd, e)) + return None + + return cmd_lines diff --git a/dovetail/prepare_env.py b/dovetail/prepare_env.py new file mode 100644 index 00000000..bd484302 --- /dev/null +++ b/dovetail/prepare_env.py @@ -0,0 +1,23 @@ +#!/usr/bin/env python +# +# grakiss.wanglei@huawei.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 os + +import utils.dovetail_logger as dt_logger +import utils.dovetail_utils as dt_utils + + +logger = dt_logger.Logger('prepare_env.py').getLogger() + +cmd = "sudo apt-get -y install docker.io python-pip" +dt_utils.exec_cmd(cmd, logger) + +cmd = "sudo pip install click pyyaml jinja2" +dt_utils.exec_cmd(cmd, logger) + diff --git a/dovetail/report.py b/dovetail/report.py new file mode 100644 index 00000000..5dfa5890 --- /dev/null +++ b/dovetail/report.py @@ -0,0 +1,239 @@ +#!/usr/bin/env python +# +# grakiss.wanglei@huawei.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 urllib2 +import re + +import utils.dovetail_logger as dt_logger +import utils.dovetail_utils as dt_utils + +from conf.dovetail_config import * +from testcase import * + +logger = dt_logger.Logger('report.py').getLogger() + +def get_pass_str(passed): + if passed: + return 'PASS' + else: + return 'FAIL' + +class Report: + + results = {'functest':{},'yardstick':{}} + + @classmethod + def check_result(cls, testcase, db_result): + checker = CheckerFactory.create(testcase.script_type()) + checker.check(testcase, db_result) + + @classmethod + def generate(cls, scenario_yaml): + report = '' + + report += '\n\ ++=============================================================================+\n\ +| report | \n\ ++-----------------------------------------------------------------------------+\n' + report += '|scenario: %s\n' % scenario_yaml['name'] + for testcase_name in scenario_yaml['testcase_list']: + testcase = Testcase.get(testcase_name) + report += '| [testcase]: %s\t\t\t\t[%s]\n' % (testcase_name, get_pass_str(testcase.passed())) + report += '| |-objective: %s\n' % testcase.objective() + if testcase.sub_testcase() is not None: + for subtest in testcase.sub_testcase(): + report += '| |-%s \t\t [%s]\n' % (subtest, get_pass_str(testcase.sub_testcase_passed(subtest))) + report += '+-----------------------------------------------------------------------------+\n' + + logger.info(report) + cls.save(report) + return report + + #save to disk as default + @classmethod + def save(cls, report): + report_file_path = dovetail_config['report_file'] + try: + with open(os.path.join(dovetail_config['result_dir'], report_file_path),'w') as report_file: + report_file.write(report) + logger.info('save report to %s' % report_file_path) + except Exception as e: + logger.error('Failed to save: %s' % report_file_path) + + @classmethod + def get_result(cls, testcase): + script_testcase = testcase.script_testcase() + type = testcase.script_type() + crawler = CrawlerFactory.create(type) + + if script_testcase in cls.results[type]: + return cls.results[type][script_testcase] + + result = crawler.crawl(script_testcase) + + if result is not None: + cls.results[type][script_testcase] = result + testcase.script_result_acquired(True) + logger.debug('testcase: %s -> result acquired' % script_testcase) + else: + retry = testcase.increase_retry() + logger.debug('testcase: %s -> result acquired retry:%d' % (script_testcase, retry)) + return result + +class CrawlerFactory: + + @classmethod + def create(cls, type): + if type == 'functest': + return FunctestCrawler() + + if type == 'yardstick': + return YardstickCrawler() + + return None + +class FunctestCrawler: + + def __init__(self): + self.type = 'functest' + + def crawl(self, testcase=None): + store_type = dovetail_config[self.type]['result']['store_type'] + if store_type == 'file': + return self.crawl_from_file(testcase) + + if store_type == 'url': + return self.crawl_from_url(testcase) + + def crawl_from_file(self, testcase=None): + file_path = os.path.join(dovetail_config['result_dir'],dovetail_config[self.type]['result']['file_path']) + if not os.path.exists(file_path): + logger.info('result file not found: %s' % file_path) + return None + + try: + with open(file_path, 'r') as myfile: + output = myfile.read() + error_logs = "" + + for match in re.findall('(.*?)[. ]*FAILED', output): + error_logs += match + + criteria = 'PASS' + failed_num = int(re.findall(' - Failed: (\d*)', output)[0]) + if failed_num != 0: + criteria = 'FAIL' + + match = re.findall('Ran: (\d*) tests in (\d*)\.\d* sec.', output) + num_tests, dur_sec_int = match[0] + json_results = {'criteria':criteria,'details':{"timestart": '', "duration": int(dur_sec_int), + "tests": int(num_tests), "failures": failed_num, + "errors": error_logs}} + logger.debug('Results: %s' % str(json_results)) + return json_results + except Exception as e: + logger.error('Cannot read content from the file: %s, exception: %s' % (file_path, e)) + return None + + def crawl_from_url(self, testcase=None): + url = dovetail_config[self.type]['result']['db_url'] % testcase + logger.debug("Query to rest api: %s" % url) + try: + data = json.load(urllib2.urlopen(url)) + return data['results'][0] + except Exception as e: + logger.error("Cannot read content from the url: %s, exception: %s" % (url, e)) + return None + +class YardstickCrawler: + + def __init__(self): + self.type = 'yardstick' + + def crawl(self, testcase=None): + store_type = dovetail_config[self.type]['result']['store_type'] + if store_type == 'file': + return self.crawl_from_file(testcase) + + if store_type == 'url': + return self.crawl_from_url(testcase) + + def crawl_from_file(self, testcase=None): + file_path = os.path.join(dovetail_config['result_dir'], testcase+'.out') + if not os.path.exists(file_path): + logger.info('result file not found: %s' % file_path) + return None + try: + with open(file_path, 'r') as myfile: + output = myfile.read() + criteria = 'PASS' + json_results = {'criteria':criteria} + logger.debug('Results: %s' % str(json_results)) + return json_results + except Exception as e: + logger.error('Cannot read content from the file: %s, exception: %s' % (file_path, e)) + return None + + def crawl_from_url(self, testcase=None): + return None + +class CheckerFactory: + + @classmethod + def create(cls,type): + if type == 'functest': + return FunctestChecker() + + if type == 'yardstick': + return YardstickChecker() + + return None + +class ResultChecker: + + def check(cls): + return 'PASS' + +class FunctestChecker: + + def check(cls, testcase, db_result): + if not db_result: + for sub_testcase in testcase.sub_testcase(): + testcase.sub_testcase_passed(sub_testcase,False) + return + + testcase.passed(db_result['criteria'] == 'PASS') + + if testcase.sub_testcase() is None: + return + + if testcase.testcase['passed'] == True: + for sub_testcase in testcase.sub_testcase(): + testcase.sub_testcase_passed(sub_testcase, True) + return + + all_passed = True + for sub_testcase in testcase.sub_testcase(): + logger.debug('check sub_testcase:%s' % sub_testcase) + if sub_testcase in db_result['details']['errors']: + testcase.sub_testcase_passed(sub_testcase, False) + all_passed = False + else: + testcase.sub_testcase_passed(sub_testcase, True) + + testcase.passed(all_passed) + +class YardstickChecker: + + def check(cls, testcase, result): + if not result: + testcase.passed(False) + else: + testcase.passed(result['criteria'] == 'PASS') + return diff --git a/dovetail/run.py b/dovetail/run.py new file mode 100755 index 00000000..85871fab --- /dev/null +++ b/dovetail/run.py @@ -0,0 +1,84 @@ +#!/usr/bin/env python +# +# grakiss.wanglei@huawei.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 click +import yaml +import os +import time + +import utils.dovetail_logger as dt_logger +import utils.dovetail_utils as dt_utils + + +from container import Container +from testcase import * +from report import * +from conf.dovetail_config import * + +logger = dt_logger.Logger('run.py').getLogger() + +def load_scenario(scenario): + Scenario.load() + return Scenario.get(SCENARIO_NAMING_FMT % scenario) + +def load_testcase(): + Testcase.load() + +def run_test(scenario): + for testcase_name in scenario['testcase_list']: + logger.info('>>[testcase]: %s' % (testcase_name)) + testcase = Testcase.get(testcase_name) + run_testcase = True + + if testcase.exceed_max_retry_times(): + run_testcase = False + + if testcase.script_result_acquired(): + run_testcase = False + + if run_testcase: + Container.pull_image(testcase.script_type()) + container_id = Container.create(testcase.script_type()) + logger.debug('container id:%s' % container_id) + + if not Testcase.prepared(testcase.script_type()): + cmds = Testcase.pre_condition(testcase.script_type())['cmds'] + if cmds: + for cmd in cmds: + Container.exec_cmd(container_id, cmd) + Testcase.prepared(testcase.script_type(),True) + + if not testcase.prepare_cmd(): + logger.error('failed to prepare testcase:%s' % testcase.name()) + else: + for cmd in testcase.cmds: + Container.exec_cmd(container_id, cmd) + + #testcase.post_condition() + + Container.clean(container_id) + + db_result = Report.get_result(testcase) + Report.check_result(testcase, db_result) + +@click.command() +@click.option('--scenario', default='basic', help='certification scenario') +def main(scenario): + """Dovetail certification test entry!""" + logger.info('=======================================') + logger.info('Dovetail certification: %s!' % scenario) + logger.info('=======================================') + load_testcase() + scenario_yaml = load_scenario(scenario) + run_test(scenario_yaml) + Report.generate(scenario_yaml) + +if __name__ == '__main__': + main() diff --git a/dovetail/testcase.py b/dovetail/testcase.py new file mode 100644 index 00000000..4deabe2e --- /dev/null +++ b/dovetail/testcase.py @@ -0,0 +1,178 @@ +#!/usr/bin/env python +# +# grakiss.wanglei@huawei.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 jinja2 + +import utils.dovetail_logger as dt_logger +import utils.dovetail_utils as dt_utils + +from parser import * + +logger = dt_logger.Logger('testcase.py').getLogger() + +from conf.dovetail_config import * + +class Testcase: + + def __init__(self, testcase_yaml): + self.testcase = testcase_yaml.values()[0] + self.testcase['passed'] = False + self.cmds = [] + self.sub_testcase_status = {} + Testcase.update_script_testcase(self.script_type(), self.script_testcase()) + + def prepare_cmd(self): + for cmd in dovetail_config[self.script_type()]['testcase']['cmds']: + cmd_lines = Parser.parse_cmd(cmd,self) + if not cmd_lines: + return False + self.cmds.append(cmd_lines) + + return True + + def __str__(self): + return self.testcase + + def name(self): + return self.testcase['name'] + + def objective(self): + return self.testcase['objective'] + + def sub_testcase(self): + return self.testcase['scripts']['sub_testcase_list'] + + def sub_testcase_passed(self, name, passed=None): + if passed is not None: + logger.debug('sub_testcase_passed:%s %s' % (name, passed)) + self.sub_testcase_status[name] = passed + return self.sub_testcase_status[name] + + def script_type(self): + return self.testcase['scripts']['type'] + + def script_testcase(self): + return self.testcase['scripts']['testcase'] + + def exceed_max_retry_times(self): + #logger.debug('retry times:%d' % self.testcase['retry']) + return Testcase._exceed_max_retry_times(self.script_type(), self.script_testcase()) + + def increase_retry(self): + #self.testcase['retry'] = self.testcase['retry'] + 1 + #return self.testcase['retry'] + return Testcase._increase_retry(self.script_type(), self.script_testcase()) + + def passed(self, passed = None): + if passed is not None: + self.testcase['passed'] = passed + return self.testcase['passed'] + + def script_result_acquired(self, acquired=None): + return Testcase._result_acquired(self.script_type(), self.script_testcase(), acquired) + + def pre_condition(self): + return Testcase.pre_condition(self.script_type()) + + def post_condition(self): + return Testcase.post_condition(self.script_type()) + + + #testcase in upstream testing project + script_testcase_list = {'functest':{}, 'yardstick':{}} + + #testcase in dovetail + testcase_list = {} + + @classmethod + def prepared(cls, script_type, prepared=None): + if prepared is not None: + cls.script_testcase_list[script_type]['prepared'] = prepared + return cls.script_testcase_list[script_type]['prepared'] + + @classmethod + def cleaned(cls, script_type, cleaned=None): + if cleaned is not None: + cls.scrpit_testcase_list[script_type]['cleaned'] = cleaned + return cls.script_testcase_list[script_type]['cleaned'] + + @classmethod + def pre_condition(cls, script_type): + return dovetail_config[script_type]['pre_condition'] + + def post_condition(cls, script_type): + return dovetail_config[script_type]['post_condition'] + + + @classmethod + def update_script_testcase(cls,script_type, script_testcase): + if script_testcase not in cls.script_testcase_list[script_type]: + cls.script_testcase_list[script_type][script_testcase] = {'retry':0, 'acquired':False} + cls.script_testcase_list[script_type]['prepared'] = False + cls.script_testcase_list[script_type]['cleaned'] = False + + @classmethod + def _exceed_max_retry_times(cls, script_type, script_testcase ): + return cls.script_testcase_list[script_type][script_testcase]['retry'] > 1 + + @classmethod + def _increase_retry(cls, script_type, script_testcase): + cls.script_testcase_list[script_type][script_testcase]['retry'] += 1 + return cls.script_testcase_list[script_type][script_testcase]['retry'] + + @classmethod + def _result_acquired(cls, script_type, script_testcase, acquired=None): + if acquired is not None: + cls.script_testcase_list[script_type][script_testcase]['acquired'] = acquired + return cls.script_testcase_list[script_type][script_testcase]['acquired'] + + @classmethod + def load(cls): + for root, dirs, files in os.walk(TESTCASE_PATH): + for testcase_file in files: + with open(os.path.join(root, testcase_file)) as f: + testcase_yaml = yaml.safe_load(f) + cls.testcase_list[testcase_yaml.keys()[0]] = Testcase(testcase_yaml) + logger.debug( cls.testcase_list ) + + @classmethod + def get(cls, testcase_name): + if testcase_name in cls.testcase_list: + return cls.testcase_list[testcase_name] + return None + + +class Scenario: + + def __init__(self, scenario): + self.scenario = scenario + self.testcase_list = {} + + def get_test(self, testcase_name): + if testcase_name in self.testcase_list: + return self.testcase_list[testcase_name] + return None + + scenario_list = {} + @classmethod + def load(cls): + for root, dirs, files in os.walk(CERT_PATH): + for scenario_yaml in files: + with open(os.path.join(root, scenario_yaml)) as f: + scenario_yaml = yaml.safe_load(f) + cls.scenario_list.update(scenario_yaml) + + logger.debug(cls.scenario_list) + + @classmethod + def get(cls, scenario_name): + if scenario_name in cls.scenario_list: + return cls.scenario_list[scenario_name] + return None + diff --git a/dovetail/testcase/ipv6.tc001.yml b/dovetail/testcase/ipv6.tc001.yml new file mode 100644 index 00000000..9f11ac76 --- /dev/null +++ b/dovetail/testcase/ipv6.tc001.yml @@ -0,0 +1,10 @@ +dovetail.ipv6.tc001: + name: dovetail.ipv6.tc001 + objective: VIM ipv6 operations, to create/delete network, port and subnet in bulk operation + scripts: + type: functest + testcase: tempest_smoke_serial + sub_testcase_list: + - tempest.api.network.test_networks.BulkNetworkOpsIpV6Test.test_bulk_create_delete_network + - tempest.api.network.test_networks.BulkNetworkOpsIpV6Test.test_bulk_create_delete_port + - tempest.api.network.test_networks.BulkNetworkOpsIpV6Test.test_bulk_create_delete_subnet diff --git a/dovetail/utils/__init__.py b/dovetail/utils/__init__.py new file mode 100644 index 00000000..e69de29b --- /dev/null +++ b/dovetail/utils/__init__.py diff --git a/dovetail/utils/dovetail_logger.py b/dovetail/utils/dovetail_logger.py new file mode 100644 index 00000000..9b20225c --- /dev/null +++ b/dovetail/utils/dovetail_logger.py @@ -0,0 +1,55 @@ +#!/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 +# +# Logging levels: +# Level Numeric value +# CRITICAL 50 +# ERROR 40 +# WARNING 30 +# INFO 20 +# DEBUG 10 +# NOTSET 0 +# +# Usage: +# import dovetail_logger as dl +# logger = dl.Logger("script_name").getLogger() +# logger.info("message to be shown with - INFO - ") +# logger.debug("message to be shown with - DEBUG -") + +import logging +import os + +class Logger: + def __init__(self, logger_name): + + CI_DEBUG = os.getenv('CI_DEBUG') + + self.logger = logging.getLogger(logger_name) + self.logger.propagate = 0 + self.logger.setLevel(logging.DEBUG) + + ch = logging.StreamHandler() + formatter = logging.Formatter('%(asctime)s - %(name)s - ' + '%(levelname)s - %(message)s') + ch.setFormatter(formatter) + if CI_DEBUG is not None and CI_DEBUG.lower() == "true": + ch.setLevel(logging.DEBUG) + else: + ch.setLevel(logging.INFO) + self.logger.addHandler(ch) + + if not os.path.exists('/home/opnfv/dovetail/results/'): + os.makedirs('/home/opnfv/dovetail/results/') + hdlr = logging.FileHandler('/home/opnfv/dovetail/results/dovetail.log') + hdlr.setFormatter(formatter) + hdlr.setLevel(logging.DEBUG) + self.logger.addHandler(hdlr) + + def getLogger(self): + return self.logger + diff --git a/dovetail/utils/dovetail_utils.py b/dovetail/utils/dovetail_utils.py new file mode 100644 index 00000000..4c671552 --- /dev/null +++ b/dovetail/utils/dovetail_utils.py @@ -0,0 +1,87 @@ +#!/usr/bin/env python +# +# jose.lausuch@ericsson.com +# valentin.boucher@orange.com +# grakiss.wanglei@huawei.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 sys +import subprocess + +def exec_cmd(cmd, logger=None, + exit_on_error=True, + info=False, + error_msg="", + verbose=True): + if not error_msg: + error_msg = ("The command '%s' failed." % cmd) + msg_exec = ("Executing command: '%s'" % cmd) + if verbose: + if logger: + if info: + logger.info(msg_exec) + else: + logger.debug(msg_exec) + else: + print(msg_exec) + p = subprocess.Popen(cmd, shell=True, stdout=subprocess.PIPE, + stderr=subprocess.STDOUT) + output = p.communicate() + for line in output[0].strip().split('\n'): + line = line.replace('\n', '') + if logger: + if info: + logger.info(line) + else: + logger.debug(line) + else: + print line + sys.stdout.flush() + + returncode = p.returncode + if returncode != 0: + if verbose: + if logger: + logger.error(error_msg) + else: + print(error_msg) + if exit_on_error: + sys.exit(1) + + return returncode, output[0].strip() + + +#walkthrough the object, yield path and value +from collections import Mapping, Set, Sequence + +# dual python 2/3 compatability, inspired by the "six" library +string_types = (str, unicode) if str is bytes else (str, bytes) +iteritems = lambda mapping: getattr(mapping, 'iteritems', mapping.items)() + +def objwalk(obj, path=(), memo=None): + if memo is None: + memo = set() + iterator = None + if isinstance(obj, Mapping): + iterator = iteritems + elif isinstance(obj, (Sequence, Set)) and not isinstance(obj, string_types): + iterator = enumerate + if iterator: + if id(obj) not in memo: + memo.add(id(obj)) + for path_component, value in iterator(obj): + for result in objwalk(value, path + (path_component,), memo): + yield result + memo.remove(id(obj)) + else: + yield path, obj + +def get_obj_by_path(obj,dst_path): + for path, obj in objwalk(obj): + if path == dst_path: + return obj + |