summaryrefslogtreecommitdiffstats
path: root/dovetail
diff options
context:
space:
mode:
Diffstat (limited to 'dovetail')
-rw-r--r--dovetail/__init__.py0
-rw-r--r--dovetail/cert/basic.yml4
-rw-r--r--dovetail/conf/__init__.py0
-rw-r--r--dovetail/conf/dovetail_config.py28
-rw-r--r--dovetail/conf/dovetail_config.yml20
-rw-r--r--dovetail/conf/functest_config.yml26
-rw-r--r--dovetail/conf/yardstick_config.yml29
-rw-r--r--dovetail/container.py69
-rw-r--r--dovetail/parser.py40
-rw-r--r--dovetail/prepare_env.py23
-rw-r--r--dovetail/report.py239
-rwxr-xr-xdovetail/run.py84
-rw-r--r--dovetail/testcase.py178
-rw-r--r--dovetail/testcase/ipv6.tc001.yml10
-rw-r--r--dovetail/utils/__init__.py0
-rw-r--r--dovetail/utils/dovetail_logger.py55
-rw-r--r--dovetail/utils/dovetail_utils.py87
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
+