blob: 589e1ee7755f34bec0c627e11430b5718cc02f11 [file] [log] [blame]
# Copyright 2016 Mirantis, Inc.
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
import datetime
import json
from devops import error
from functools32 import lru_cache
from tcp_tests import logger
from tcp_tests import settings
LOG = logger.logger
class RallyManager(object):
"""docstring for RallyManager"""
image_name = (
'docker-prod-virtual.docker.mirantis.net/'
'mirantis/oscore/rally-tempest')
image_version = 'latest'
tempest_tag = "16.0.0"
designate_tag = "0.2.0"
def __init__(self, underlay, rally_node='gtw01.'):
super(RallyManager, self).__init__()
self._underlay = underlay
self._node_name = self.get_target_node(target=rally_node)
@property
@lru_cache(maxsize=None)
def image_id(self):
LOG.info("Getting image id")
cmd = ("docker images | grep {0}| grep {1}| awk '{{print $3}}'"
.format(self.image_name, self.image_version))
res = self._underlay.check_call(cmd, node_name=self._node_name)
LOG.debug(res['stdout'])
image_id = res['stdout'][0].strip()
LOG.info("Image ID is {}".format(image_id))
return image_id
@property
@lru_cache(maxsize=None)
def docker_id(self):
cmd = ("docker ps | grep {image_id} | "
"awk '{{print $1}}'| head -1").format(
image_id=self.image_id)
LOG.info("Getting container id")
res = self._underlay.check_call(cmd, node_name=self._node_name)
docker_id = res['stdout'][0].strip()
LOG.info("Container ID is {}".format(docker_id))
return docker_id
# Move method to underlay
def get_target_node(self, target='gtw01.'):
return [node_name for node_name
in self._underlay.node_names()
if node_name.startswith(target)][0]
def _docker_exec(self, cmd, timeout=None, verbose=False):
docker_cmd = ('docker exec -i {docker_id} bash -c "{cmd}"'
.format(cmd=cmd, docker_id=self.docker_id))
LOG.info("Executing: {docker_cmd}".format(docker_cmd=docker_cmd))
return self._underlay.check_call(docker_cmd, node_name=self._node_name,
verbose=verbose, timeout=timeout)
def _run(self):
"""Start the rally container in the background"""
with self._underlay.remote(node_name=self._node_name) as remote:
cmd = ("docker run --net host -v /root/rally:/home/rally/.rally "
"-v /etc/ssl/certs/:/etc/ssl/certs/ "
"-tid -u root --entrypoint /bin/bash {image_id}"
.format(image_id=self.image_id))
LOG.info("Run Rally container")
remote.check_call(cmd)
def run_container(self, version=None):
"""Install docker, configure and run rally container"""
version = version or self.image_version
image = self.image_name
LOG.info("Pull {image}:{version}".format(image=image,
version=version))
try:
cmd = ("apt-get -y install docker-ce &&"
" docker pull {image}:{version}".format(image=image,
version=version))
self._underlay.check_call(cmd, node_name=self._node_name)
except Exception:
LOG.debug('Cannot install docker-ce')
cmd = ("apt-get -y install docker.io &&"
" docker pull {image}:{version}".format(image=image,
version=version))
self._underlay.check_call(cmd, node_name=self._node_name)
cmd_iptables = "iptables --policy FORWARD ACCEPT"
self._underlay.check_call(cmd_iptables, node_name=self._node_name)
LOG.info("Create rally workdir")
cmd = 'mkdir -p /root/rally; chown 65500 /root/rally'
self._underlay.check_call(cmd, node_name=self._node_name)
LOG.info("Copy keystonercv3")
tgt = self._node_name.split('.')[0]
cmd = "scp -3 ctl01:/root/keystonercv3 " \
"{tgt}:/root/rally/keystonercv3".format(
tgt=tgt)
domain = '.'.join(self._node_name.split('.')[1:])
self._underlay.check_call(cmd, node_name="cfg01.{domain}".format(
domain=domain))
self._run()
LOG.info("Create rally deployment")
self._docker_exec("rally-manage db recreate")
self._docker_exec("source /home/rally/.rally/keystonercv3;"
"rally deployment create --fromenv --name=Abathur")
self._docker_exec("rally deployment list")
def prepare_rally_task(self, target_node='ctl01.'):
"""Prepare cirros image and private network for rally task"""
ctl_node_name = self._underlay.get_target_node_names(
target=target_node)[0]
cmds = [
". keystonercv3 ; openstack flavor create --public m1.tiny",
("wget http://download.cirros-cloud.net/0.3.4/"
"cirros-0.3.4-i386-disk.img"),
(". /root/keystonercv3; glance --timeout 120 image-create "
"--name cirros-disk --visibility public --disk-format qcow2 "
"--container-format bare --progress "
"< /root/cirros-0.3.4-i386-disk.img"),
". /root/keystonercv3; neutron net-create net04",
]
for cmd in cmds:
self._underlay.check_call(cmd, node_name=ctl_node_name)
def prepare_tempest_task(self):
"""Configure rally.conf for tempest tests"""
pass
# LOG.info("Modify rally.conf")
# cmd = ("sed -i 's|#swift_operator_role = Member|"
# "swift_operator_role=SwiftOperator|g' "
# "/etc/rally/rally.conf")
# self._docker_exec(cmd)
def create_rally_task(self, task_path, task_content):
"""Create a file with rally task definition
:param task_path: path to JSON or YAML file on target node
:task_content: string with json or yaml content to store in file
"""
cmd = ("mkdir -p $(dirname {task_path}) && "
"cat > {task_path} << EOF\n{task_content}\nEOF").format(
task_path=task_path, task_content=task_content)
self._underlay.check_call(cmd, node_name=self._node_name)
def run_task(self, task='', timeout=None, raise_on_timeout=True,
verbose=False):
"""Run rally task
:param taks: path to json or yaml file with the task definition
:param raise_on_timeout: bool, ignore TimeoutError if False
:param verbose: show rally output to console if True
"""
try:
res = self._docker_exec(
"rally task start {task}".format(task=task),
timeout=timeout,
verbose=verbose)
except error.TimeoutError:
if raise_on_timeout:
raise
else:
res = None
pass
return res
# Updated to replace the OpenStackManager method run_tempest
def run_tempest(self, conf_name='/var/lib/lvm_mcp.conf',
pattern='set=smoke', concurrency=0, timeout=None,
report_prefix='', report_types=None,
designate_plugin=True):
"""Run tempest tests
:param conf_name: tempest config placed in the rally container
:param pattern: tempest testcase name or one of existing 'set=...'
:param concurrency: how many threads to use in parallel. 0 means
to take the amount of the cores on the node
<self._node_name>.
:param timeout: stop tempest tests after specified timeout.
:param designate_plugin: enabled by default plugin for designate
:param report_prefix: str, prefix for report filenames. Usually the
output of the fixture 'func_name'
:param report_types: list of the report types that need to download
from the environment: ['html', 'xml', 'json'].
None by default.
"""
report_types = report_types or []
if not designate_plugin:
cmd = (
"cat > /root/rally/install_tempest.sh << EOF\n"
"rally verify create-verifier"
" --type tempest "
" --name tempest-verifier"
" --source /var/lib/tempest"
" --version {tempest_tag}"
" --system-wide\n"
"rally verify configure-verifier --extend {tempest_conf}\n"
"rally verify configure-verifier --show\n"
"EOF".format(tempest_tag=self.tempest_tag,
tempest_conf=conf_name))
else:
cmd = (
"cat > /root/rally/install_tempest.sh << EOF\n"
"rally verify create-verifier"
" --type tempest "
" --name tempest-verifier"
" --source /var/lib/tempest"
" --version {tempest_tag}"
" --system-wide\n"
"rally verify add-verifier-ext"
" --source /var/lib/designate-tempest-plugin"
" --version {designate_tag}\n"
"rally verify configure-verifier --extend {tempest_conf}\n"
"rally verify configure-verifier --show\n"
"EOF".format(tempest_tag=self.tempest_tag,
designate_tag=self.designate_tag,
tempest_conf=conf_name))
with self._underlay.remote(node_name=self._node_name) as remote:
LOG.info("Create install_tempest.sh")
remote.check_call(cmd)
remote.check_call("chmod +x /root/rally/install_tempest.sh")
LOG.info("Run tempest inside Rally container")
self._docker_exec("/home/rally/.rally/install_tempest.sh")
self._docker_exec(
("source /home/rally/.rally/keystonercv3 && "
"rally verify start --skip-list /var/lib/mcp_skip.list "
" --concurrency {concurrency} --pattern {pattern}"
.format(concurrency=concurrency, pattern=pattern)),
timeout=timeout, verbose=True)
if report_prefix:
report_filename = '{0}_report_{1}'.format(
report_prefix,
datetime.datetime.now().strftime('%Y%m%d_%H%M%S'))
else:
report_filename = 'report_{1}'.format(
datetime.datetime.now().strftime('%Y%m%d_%H%M%S'))
docker_file_prefix = '/home/rally/.rally/' + report_filename
# Create reports
if 'xml' in report_types:
self._docker_exec(
"rally verify report --type junit-xml --to {0}.xml"
.format(docker_file_prefix))
if 'html' in report_types:
self._docker_exec(
"rally verify report --type html --to {0}.html"
.format(docker_file_prefix))
# Always create report in JSON to return results into test case
# However, it won't be downloaded until ('json' in report_prefix)
self._docker_exec("rally verify report --type json --to {0}.json"
.format(docker_file_prefix))
# Download reports to the settings.LOGS_DIR
file_src_prefix = '/root/rally/{0}'.format(report_filename)
file_dst_prefix = '{0}/{1}'.format(settings.LOGS_DIR, report_filename)
with self._underlay.remote(node_name=self._node_name) as remote:
for suffix in report_types:
remote.download(file_src_prefix + '.' + suffix,
file_dst_prefix + '.' + suffix)
res = json.load(remote.open(file_src_prefix + '.json'))
# Get latest verification ID to find the lates testcases in the report
vtime = {vdata['finished_at']: vid
for vid, vdata in res['verifications'].items()}
vlatest_id = vtime[max(vtime.keys())]
# Each status has the dict with pairs:
# <status>: {
# <case_name>: <case_details>,
# }
formatted_tc = {
'success': {},
'fail': {},
'xfail': {},
'skip': {}
}
for tname, tdata in res['tests'].items():
status = tdata['by_verification'][vlatest_id]['status']
details = tdata['by_verification'][vlatest_id].get('details', '')
if status not in formatted_tc:
# Fail if tempest return a new status that may be
# necessary to take into account in test cases
raise Exception("Unknown testcase {0} status: {1} "
.format(tname, status))
formatted_tc[status][tdata['name']] = details
LOG.debug("Formatted testcases: {0}".format(formatted_tc))
return formatted_tc