605 lines
26 KiB
Python
605 lines
26 KiB
Python
# Copyright 2015 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 os
|
|
import re
|
|
import sys
|
|
import yaml
|
|
import zlib
|
|
from urllib2 import urlopen
|
|
from urlparse import urlparse
|
|
from xml.dom.minidom import parseString
|
|
|
|
from proboscis import register
|
|
from proboscis import TestProgram
|
|
from proboscis.asserts import assert_equal
|
|
from proboscis.asserts import assert_is_not_none
|
|
from proboscis.asserts import assert_not_equal
|
|
from proboscis.asserts import assert_true
|
|
|
|
from fuelweb_test import logger
|
|
from fuelweb_test import settings
|
|
|
|
|
|
patching_validation_schema = {
|
|
'type': {
|
|
'required': True,
|
|
'values': ['service_stop', 'service_start', 'service_restart',
|
|
'server_down', 'server_up', 'server_reboot',
|
|
'run_command', 'upload_script', 'run_tasks'],
|
|
'data_type': str
|
|
},
|
|
'target': {
|
|
'required': True,
|
|
'values': {'master', 'slaves', 'controller_role', 'compute_role',
|
|
'cinder_role', 'ceph-osd_role', 'mongo_role',
|
|
'zabbix-server_role', 'base-os_role'},
|
|
'data_type': list
|
|
},
|
|
'service': {
|
|
'required': False,
|
|
'data_type': str
|
|
},
|
|
'command': {
|
|
'required': False,
|
|
'data_type': str
|
|
},
|
|
'script': {
|
|
'required': False,
|
|
'data_type': str
|
|
},
|
|
'upload_path': {
|
|
'required': False,
|
|
'data_type': str
|
|
},
|
|
'id': {
|
|
'required': True,
|
|
'data_type': int
|
|
},
|
|
'tasks': {
|
|
'required': False,
|
|
'data_type': list
|
|
},
|
|
'tasks_timeout': {
|
|
'required': False,
|
|
'data_type': int
|
|
},
|
|
}
|
|
|
|
|
|
def map_test(target):
|
|
assert_is_not_none(settings.PATCHING_BUG_ID,
|
|
"Bug ID wasn't specified, can't start patching tests!")
|
|
errata = get_errata(path=settings.PATCHING_APPLY_TESTS,
|
|
bug_id=settings.PATCHING_BUG_ID)
|
|
verify_errata(errata)
|
|
if not any(target == e_target['type'] for e_target in errata['targets']):
|
|
skip_patching_test(target, errata['target'])
|
|
env_distro = settings.OPENSTACK_RELEASE
|
|
master_distro = settings.OPENSTACK_RELEASE_CENTOS
|
|
if 'affected-pkgs' in errata.keys():
|
|
if target == 'master':
|
|
settings.PATCHING_PKGS = set(
|
|
[re.split('=|<|>', package)[0] for package
|
|
in errata['affected-pkgs'][master_distro.lower()]])
|
|
else:
|
|
settings.PATCHING_PKGS = set(
|
|
[re.split('=|<|>', package)[0] for package
|
|
in errata['affected-pkgs'][env_distro.lower()]])
|
|
available_env_packages = set()
|
|
available_master_packages = set()
|
|
for repo in settings.PATCHING_MIRRORS:
|
|
logger.debug(
|
|
'Checking packages from "{0}" repository'.format(repo))
|
|
available_env_packages.update(get_repository_packages(repo,
|
|
env_distro))
|
|
for repo in settings.PATCHING_MASTER_MIRRORS:
|
|
logger.debug(
|
|
'Checking packages from "{0}" repository'.format(repo))
|
|
available_master_packages.update(get_repository_packages(
|
|
repo, master_distro))
|
|
available_packages = available_env_packages | available_master_packages
|
|
if not settings.PATCHING_PKGS:
|
|
if target == 'master':
|
|
settings.PATCHING_PKGS = available_master_packages
|
|
else:
|
|
settings.PATCHING_PKGS = available_env_packages
|
|
else:
|
|
assert_true(settings.PATCHING_PKGS <= available_packages,
|
|
"Patching repositories don't contain all packages need"
|
|
"ed for tests. Need: {0}, available: {1}, missed: {2}."
|
|
"".format(settings.PATCHING_PKGS,
|
|
available_packages,
|
|
settings.PATCHING_PKGS - available_packages))
|
|
assert_not_equal(len(settings.PATCHING_PKGS), 0,
|
|
"No packages found in repository(s) for patching:"
|
|
" '{0} {1}'".format(settings.PATCHING_MIRRORS,
|
|
settings.PATCHING_MASTER_MIRRORS))
|
|
if target == 'master':
|
|
tests_groups = get_packages_tests(settings.PATCHING_PKGS,
|
|
master_distro,
|
|
target)
|
|
else:
|
|
tests_groups = get_packages_tests(settings.PATCHING_PKGS,
|
|
env_distro,
|
|
target)
|
|
|
|
if 'rally' in errata.keys():
|
|
if len(errata['rally']) > 0:
|
|
settings.PATCHING_RUN_RALLY = True
|
|
settings.RALLY_TAGS = errata['rally']
|
|
|
|
if settings.PATCHING_CUSTOM_TEST:
|
|
deployment_test = settings.PATCHING_CUSTOM_TEST
|
|
settings.PATCHING_SNAPSHOT = \
|
|
'patching_after_{0}'.format(deployment_test)
|
|
register(groups=['prepare_patching_environment'],
|
|
depends_on_groups=[deployment_test])
|
|
register(groups=['prepare_patching_master_environment'],
|
|
depends_on_groups=[deployment_test])
|
|
else:
|
|
program = TestProgram(argv=['none'])
|
|
deployment_test = None
|
|
for my_test in program.plan.tests:
|
|
if all(patching_group in my_test.entry.info.groups for
|
|
patching_group in tests_groups):
|
|
deployment_test = my_test
|
|
break
|
|
if deployment_test:
|
|
settings.PATCHING_SNAPSHOT = 'patching_after_{0}'.format(
|
|
deployment_test.entry.method.im_func.func_name)
|
|
if target == 'master':
|
|
register(groups=['prepare_patching_master_environment'],
|
|
depends_on=[deployment_test.entry.home])
|
|
else:
|
|
register(groups=['prepare_patching_environment'],
|
|
depends_on=[deployment_test.entry.home])
|
|
else:
|
|
raise Exception(
|
|
"Test with groups {0} not found.".format(tests_groups))
|
|
|
|
|
|
def get_repository_packages(remote_repo_url, repo_type):
|
|
repo_url = urlparse(remote_repo_url)
|
|
packages = []
|
|
if repo_type == settings.OPENSTACK_RELEASE_UBUNTU:
|
|
packages_url = '{0}/Packages'.format(repo_url.geturl())
|
|
pkgs_raw = urlopen(packages_url).read()
|
|
for pkg in pkgs_raw.split('\n'):
|
|
match = re.search(r'^Package: (\S+)\s*$', pkg)
|
|
if match:
|
|
packages.append(match.group(1))
|
|
else:
|
|
packages_url = '{0}/repodata/primary.xml.gz'.format(repo_url.geturl())
|
|
pkgs_xml = parseString(zlib.decompressobj(zlib.MAX_WBITS | 32).
|
|
decompress(urlopen(packages_url).read()))
|
|
for pkg in pkgs_xml.getElementsByTagName('package'):
|
|
packages.append(
|
|
pkg.getElementsByTagName('name')[0].firstChild.nodeValue)
|
|
return packages
|
|
|
|
|
|
def _get_target_and_project(_pkg, _all_pkgs):
|
|
for _installation_target in _all_pkgs.keys():
|
|
for _project in _all_pkgs[_installation_target]['projects']:
|
|
if _pkg in _project['packages']:
|
|
return _installation_target, _project['name']
|
|
|
|
|
|
def get_package_test_info(package, pkg_type, tests_path, patch_target):
|
|
packages_path = "{0}/{1}/packages.yaml".format(tests_path, pkg_type)
|
|
tests = set()
|
|
tests_file = 'test.yaml'
|
|
all_packages = yaml.load(open(packages_path).read())
|
|
assert_is_not_none(_get_target_and_project(package, all_packages),
|
|
"Package '{0}' doesn't belong to any installation "
|
|
"target / project".format(package))
|
|
target, project = _get_target_and_project(package, all_packages)
|
|
if patch_target == 'master':
|
|
if target not in ['master', 'bootstrap']:
|
|
return set([None])
|
|
if patch_target == 'environment':
|
|
if target not in ['deployment', 'provisioning']:
|
|
return set([None])
|
|
target_tests_path = "/".join((tests_path, pkg_type, target, tests_file))
|
|
project_tests_path = "/".join((tests_path, pkg_type, target, project,
|
|
tests_file))
|
|
package_tests_path = "/".join((tests_path, pkg_type, target, project,
|
|
package, tests_file))
|
|
for path in (target_tests_path, project_tests_path, package_tests_path):
|
|
try:
|
|
test = yaml.load(open(path).read())
|
|
if 'system_tests' in test.keys():
|
|
tests.update(test['system_tests']['tags'])
|
|
except IOError:
|
|
pass
|
|
return tests
|
|
|
|
|
|
def get_packages_tests(packages, distro, target):
|
|
assert_true(os.path.isdir(settings.PATCHING_PKGS_TESTS),
|
|
"Path for packages tests doesn't exist: '{0}'".format(
|
|
settings.PATCHING_PKGS_TESTS))
|
|
if distro == settings.OPENSTACK_RELEASE_UBUNTU:
|
|
pkg_type = 'deb'
|
|
else:
|
|
pkg_type = 'rpm'
|
|
packages_tests = set()
|
|
for package in packages:
|
|
tests = get_package_test_info(package,
|
|
pkg_type,
|
|
settings.PATCHING_PKGS_TESTS,
|
|
target)
|
|
assert_true(len(tests) > 0,
|
|
"Tests for package {0} not found".format(package))
|
|
if None in tests:
|
|
continue
|
|
packages_tests.update(tests)
|
|
return packages_tests
|
|
|
|
|
|
def mirror_remote_repository(admin_remote, remote_repo_url, local_repo_path):
|
|
repo_url = urlparse(remote_repo_url)
|
|
cut_dirs = len(repo_url.path.strip('/').split('/'))
|
|
download_cmd = ('wget --recursive --no-parent --no-verbose --reject "index'
|
|
'.html*,*.gif" --exclude-directories "{pwd}/repocache" '
|
|
'--directory-prefix {path} -nH --cut-dirs={cutd} {url}').\
|
|
format(pwd=repo_url.path.rstrip('/'), path=local_repo_path,
|
|
cutd=cut_dirs, url=repo_url.geturl())
|
|
result = admin_remote.execute(download_cmd)
|
|
assert_equal(result['exit_code'], 0, 'Mirroring of remote packages '
|
|
'repository failed: {0}'.format(
|
|
result))
|
|
|
|
|
|
def add_remote_repositories(environment, mirrors, prefix_name='custom_repo'):
|
|
repositories = set()
|
|
for mir in mirrors:
|
|
name = '{0}_{1}'.format(prefix_name, mirrors.index(mir))
|
|
local_repo_path = '/'.join([settings.PATCHING_WEB_DIR, name])
|
|
remote_repo_url = mir
|
|
mirror_remote_repository(
|
|
admin_remote=environment.d_env.get_admin_remote(),
|
|
remote_repo_url=remote_repo_url,
|
|
local_repo_path=local_repo_path)
|
|
repositories.add(name)
|
|
return repositories
|
|
|
|
|
|
def connect_slaves_to_repo(environment, nodes, repo_name):
|
|
repo_ip = environment.get_admin_node_ip()
|
|
repo_port = '8080'
|
|
repourl = 'http://{master_ip}:{repo_port}/{repo_name}/'.format(
|
|
master_ip=repo_ip, repo_name=repo_name, repo_port=repo_port)
|
|
if settings.OPENSTACK_RELEASE == settings.OPENSTACK_RELEASE_UBUNTU:
|
|
cmds = [
|
|
"echo -e '\ndeb {repourl} /' > /etc/apt/sources.list.d/{repo_name}"
|
|
".list".format(repourl=repourl, repo_name=repo_name),
|
|
"apt-key add <(curl -s '{repourl}/Release.key') || :".format(
|
|
repourl=repourl),
|
|
# Set highest priority to all repositories located on master node
|
|
"echo -e 'Package: *\nPin: origin {0}\nPin-Priority: 1060' > "
|
|
"/etc/apt/preferences.d/custom_repo".format(
|
|
environment.get_admin_node_ip()),
|
|
"apt-get update"
|
|
]
|
|
else:
|
|
cmds = [
|
|
"yum-config-manager --add-repo {url}".format(url=repourl),
|
|
"echo -e 'gpgcheck=0\npriority=20' >>/etc/yum.repos.d/{ip}_{port}_"
|
|
"{repo}_.repo".format(ip=repo_ip, repo=repo_name, port=repo_port),
|
|
"yum -y clean all",
|
|
]
|
|
|
|
for slave in nodes:
|
|
remote = environment.d_env.get_ssh_to_remote(slave['ip'])
|
|
for cmd in cmds:
|
|
environment.execute_remote_cmd(remote, cmd, exit_code=0)
|
|
|
|
|
|
def connect_admin_to_repo(environment, repo_name):
|
|
repo_ip = environment.get_admin_node_ip()
|
|
repo_port = '8080'
|
|
repourl = 'http://{master_ip}:{repo_port}/{repo_name}/'.format(
|
|
master_ip=repo_ip, repo_name=repo_name, repo_port=repo_port)
|
|
|
|
cmds = [
|
|
"yum-config-manager --add-repo {url}".format(url=repourl),
|
|
"echo -e 'gpgcheck=0\npriority=20' >>/etc/yum.repos.d/{ip}_{port}_"
|
|
"{repo}_.repo".format(ip=repo_ip, repo=repo_name, port=repo_port),
|
|
"yum -y clean all",
|
|
# FIXME(apanchenko):
|
|
# Temporary disable this check in order to test packages update
|
|
# inside Docker containers. When building of new images for containers
|
|
# is implemented, we should check here that `yum check-update` returns
|
|
# ONLY `100` exit code (updates are available for master node).
|
|
"yum check-update; [[ $? -eq 100 || $? -eq 0 ]]"
|
|
]
|
|
|
|
remote = environment.d_env.get_admin_remote()
|
|
for cmd in cmds:
|
|
environment.execute_remote_cmd(remote, cmd, exit_code=0)
|
|
|
|
|
|
def update_packages(environment, remote, packages, exclude_packages=None):
|
|
if settings.OPENSTACK_RELEASE == settings.OPENSTACK_RELEASE_UBUNTU:
|
|
cmds = [
|
|
'apt-get -o Dpkg::Options::="--force-confdef" '
|
|
'-o Dpkg::Options::="--force-confold" -y install '
|
|
'--only-upgrade {0}'.format(' '.join(packages))
|
|
]
|
|
if exclude_packages:
|
|
exclude_commands = ["apt-mark hold {0}".format(pkg)
|
|
for pkg in exclude_packages]
|
|
cmds = exclude_commands + cmds
|
|
else:
|
|
cmds = [
|
|
"yum -y update --nogpgcheck {0} -x '{1}'".format(
|
|
' '.join(packages), ','.join(exclude_packages or []))
|
|
]
|
|
for cmd in cmds:
|
|
environment.execute_remote_cmd(remote, cmd, exit_code=0)
|
|
|
|
|
|
def update_packages_on_slaves(environment, slaves, packages=None,
|
|
exclude_packages=None):
|
|
if not packages:
|
|
# Install all updates
|
|
packages = ' '
|
|
for slave in slaves:
|
|
remote = environment.d_env.get_ssh_to_remote(slave['ip'])
|
|
update_packages(environment, remote, packages, exclude_packages)
|
|
|
|
|
|
def get_slaves_ips_by_role(slaves, role=None):
|
|
if role:
|
|
return [slave['ip'] for slave in slaves if role in slave['roles']]
|
|
return [slave['ip'] for slave in slaves]
|
|
|
|
|
|
def get_devops_slaves_by_role(env, slaves, role=None):
|
|
if role:
|
|
return [env.fuel_web.find_devops_node_by_nailgun_fqdn(slave['fqdn'],
|
|
env.d_env.nodes().slaves)
|
|
for slave in slaves if role in slave['roles']]
|
|
return [env.fuel_web.find_devops_node_by_nailgun_fqdn(slave['fqdn'],
|
|
env.d_env.nodes().slaves) for slave in slaves]
|
|
|
|
|
|
def get_slaves_ids_by_role(slaves, role=None):
|
|
if role:
|
|
return [slave['id'] for slave in slaves if role in slave['roles']]
|
|
return [slave['id'] for slave in slaves]
|
|
|
|
|
|
def verify_fix_apply_step(apply_step):
|
|
validation_schema = patching_validation_schema
|
|
for key in validation_schema.keys():
|
|
if key in apply_step.keys():
|
|
is_exists = apply_step[key] is not None
|
|
else:
|
|
is_exists = False
|
|
if validation_schema[key]['required']:
|
|
assert_true(is_exists, "Required field '{0}' not found in patch "
|
|
"apply scenario step".format(key))
|
|
if not is_exists:
|
|
continue
|
|
is_valid = True
|
|
if 'values' in validation_schema[key].keys():
|
|
if validation_schema[key]['data_type'] == str:
|
|
is_valid = apply_step[key] in validation_schema[key]['values']
|
|
elif validation_schema[key]['data_type'] in (list, set):
|
|
is_valid = set(apply_step[key]) <= \
|
|
validation_schema[key]['values']
|
|
|
|
assert_true(is_valid, 'Step in patch apply actions scenario '
|
|
'contains incorrect data: "{key}": "{value}"'
|
|
'. Supported values for "{key}" are '
|
|
'"{valid}"'.format(
|
|
key=key,
|
|
value=apply_step[key],
|
|
valid=validation_schema[key]['values']))
|
|
if 'data_type' in validation_schema[key].keys():
|
|
assert_true(type(apply_step[key]) is
|
|
validation_schema[key]['data_type'],
|
|
"Unexpected data type in patch apply scenario step: '"
|
|
"{key}' is '{type}', but expecting '{expect}'.".format(
|
|
key=key,
|
|
type=type(apply_step[key]),
|
|
expect=validation_schema[key]['data_type']))
|
|
|
|
|
|
def validate_fix_apply_step(apply_step, environment, slaves):
|
|
verify_fix_apply_step(apply_step)
|
|
slaves = [] if not slaves else slaves
|
|
command = ''
|
|
remotes_ips = set()
|
|
devops_action = ''
|
|
devops_nodes = set()
|
|
nodes_ids = set()
|
|
|
|
if apply_step['type'] == 'run_tasks':
|
|
remotes_ips.add(environment.get_admin_node_ip())
|
|
assert_true('master' not in apply_step['target'],
|
|
"Action type 'run_tasks' accepts only slaves (roles) "
|
|
"as target value, but 'master' is specified!")
|
|
|
|
for target in apply_step['target']:
|
|
if target == 'slaves':
|
|
nodes_ids.update(get_slaves_ids_by_role(slaves, role=None))
|
|
else:
|
|
role = target.split('_role')[0]
|
|
nodes_ids.update(get_slaves_ids_by_role(slaves, role=role))
|
|
else:
|
|
for target in apply_step['target']:
|
|
if target == 'master':
|
|
remotes_ips.add(environment.get_admin_node_ip())
|
|
devops_nodes.add(
|
|
environment.d_env.nodes().admin)
|
|
elif target == 'slaves':
|
|
remotes_ips.update(get_slaves_ips_by_role(slaves, role=None))
|
|
devops_nodes.update(get_devops_slaves_by_role(environment,
|
|
slaves))
|
|
else:
|
|
role = target.split('_role')[0]
|
|
remotes_ips.update(get_slaves_ips_by_role(slaves, role))
|
|
devops_nodes.update(get_devops_slaves_by_role(environment,
|
|
slaves,
|
|
role=role))
|
|
if apply_step['type'] in ('service_stop', 'service_start',
|
|
'service_restart'):
|
|
assert_true(len(apply_step['service'] or '') > 0,
|
|
"Step #{0} in apply patch scenario perform '{1}', but "
|
|
"service isn't specified".format(apply_step['id'],
|
|
apply_step['type']))
|
|
action = apply_step['type'].split('service_')[1]
|
|
command = ("find /etc/init.d/ -regex '/etc/init.d/{service}' -printf "
|
|
"'%f\n' -quit | xargs -i service {{}} {action}").format(
|
|
service=apply_step['service'], action=action)
|
|
elif apply_step['type'] in ('server_down', 'server_up', 'server_reboot'):
|
|
assert_true('master' not in apply_step['target'],
|
|
'Action type "{0}" doesn\'t accept "master" node as '
|
|
'target! Use action "run_command" instead.'.format(
|
|
apply_step['type']))
|
|
devops_action = apply_step['type'].split('server_')[1]
|
|
elif apply_step['type'] == 'upload_script':
|
|
assert_true(len(apply_step['script'] or '') > 0,
|
|
"Step #{0} in apply patch scenario perform '{1}', but "
|
|
"script isn't specified".format(apply_step['id'],
|
|
apply_step['type']))
|
|
assert_true(len(apply_step['upload_path'] or '') > 0,
|
|
"Step #{0} in apply patch scenario perform '{1}', but "
|
|
"upload path isn't specified".format(apply_step['id'],
|
|
apply_step['type']))
|
|
command = ('UPLOAD', apply_step['script'], apply_step['upload_path'])
|
|
elif apply_step['type'] == 'run_tasks':
|
|
assert_true(len(apply_step['tasks'] or '') > 0,
|
|
"Step #{0} in apply patch scenario perform '{1}', but "
|
|
"tasks aren't specified".format(apply_step['id'],
|
|
apply_step['type']))
|
|
tasks_timeout = apply_step['tasks_timeout'] if 'tasks_timeout' in \
|
|
apply_step.keys() else 60 * 30
|
|
command = [
|
|
'RUN_TASKS',
|
|
nodes_ids,
|
|
apply_step['tasks'],
|
|
tasks_timeout
|
|
]
|
|
else:
|
|
assert_true(len(apply_step['command'] or '') > 0,
|
|
"Step #{0} in apply patch scenario perform '{1}', but "
|
|
"command isn't specified".format(apply_step['id'],
|
|
apply_step['type']))
|
|
command = apply_step['command']
|
|
remotes = [environment.d_env.get_ssh_to_remote(ip) for ip in remotes_ips] \
|
|
if command else []
|
|
devops_nodes = devops_nodes if devops_action else []
|
|
return command, remotes, devops_action, devops_nodes
|
|
|
|
|
|
def get_errata(path, bug_id):
|
|
scenario_path = '{0}/bugs/{1}/erratum.yaml'.format(path, bug_id)
|
|
assert_true(os.path.exists(scenario_path),
|
|
"Erratum for bug #{0} is not found in '{0}' "
|
|
"directory".format(bug_id, settings.PATCHING_APPLY_TESTS))
|
|
with open(scenario_path) as f:
|
|
return yaml.load(f.read())
|
|
|
|
|
|
def verify_errata(errata):
|
|
actions_types = ('patch-scenario', 'verify-scenario')
|
|
distro = settings.OPENSTACK_RELEASE.lower()
|
|
for target in errata['targets']:
|
|
for action_type in actions_types:
|
|
assert_true(distro in target[action_type].keys(),
|
|
"Steps for '{0}' not found for '{1}' distro!".format(
|
|
action_type, distro))
|
|
scenario = sorted(target[action_type][distro],
|
|
key=lambda k: k['id'])
|
|
for step in scenario:
|
|
verify_fix_apply_step(step)
|
|
|
|
|
|
def run_actions(environment, target, slaves, action_type='patch-scenario'):
|
|
errata = get_errata(path=settings.PATCHING_APPLY_TESTS,
|
|
bug_id=settings.PATCHING_BUG_ID)
|
|
distro = settings.OPENSTACK_RELEASE.lower()
|
|
target_scenarios = [e_target for e_target in errata['targets']
|
|
if target == e_target['type']]
|
|
assert_true(len(target_scenarios) > 0,
|
|
"Can't found patch scenario for '{0}' target in erratum "
|
|
"for bug #{1}!".format(target, settings.PATCHING_BUG_ID))
|
|
scenario = sorted(target_scenarios[0][action_type][distro],
|
|
key=lambda k: k['id'])
|
|
|
|
for step in scenario:
|
|
command, remotes, devops_action, devops_nodes = \
|
|
validate_fix_apply_step(step, environment, slaves)
|
|
if 'UPLOAD' in command:
|
|
file_name = command[1]
|
|
upload_path = command[2]
|
|
source_path = '{0}/bugs/{1}/tests/{2}'.format(
|
|
settings.PATCHING_APPLY_TESTS,
|
|
settings.PATCHING_BUG_ID,
|
|
file_name)
|
|
assert_true(os.path.exists(source_path),
|
|
'File for uploading "{0}" doesn\'t exist!'.format(
|
|
source_path))
|
|
for remote in remotes:
|
|
remote.upload(source_path, upload_path)
|
|
continue
|
|
elif 'RUN_TASKS' in command:
|
|
nodes_ids = command[1]
|
|
tasks = command[2]
|
|
timeout = command[3]
|
|
nodes = [node for node in environment.fuel_web.client.list_nodes()
|
|
if node['id'] in nodes_ids]
|
|
assert_true(len(nodes_ids) == len(nodes),
|
|
'Get nodes with ids: {0} for deployment task, but '
|
|
'found {1}!'.format(nodes_ids,
|
|
[n['id'] for n in nodes]))
|
|
assert_true(len(set([node['cluster'] for node in nodes])) == 1,
|
|
'Slaves for patching actions belong to different '
|
|
'environments, can\'t run deployment tasks!')
|
|
cluster_id = nodes[0]['cluster']
|
|
environment.fuel_web.wait_deployment_tasks(cluster_id, nodes_ids,
|
|
tasks, timeout)
|
|
continue
|
|
for remote in remotes:
|
|
environment.execute_remote_cmd(remote, command)
|
|
if devops_action == 'down':
|
|
environment.fuel_web.warm_shutdown_nodes(devops_nodes)
|
|
elif devops_action == 'up':
|
|
environment.fuel_web.warm_start_nodes(devops_nodes)
|
|
elif devops_action == 'reboot':
|
|
environment.fuel_web.warm_restart_nodes(devops_nodes)
|
|
|
|
|
|
def apply_patches(environment, target, slaves=None):
|
|
run_actions(environment, target, slaves, action_type='patch-scenario')
|
|
|
|
|
|
def verify_fix(environment, target, slaves=None):
|
|
run_actions(environment, target, slaves, action_type='verify-scenario')
|
|
|
|
|
|
def skip_patching_test(target, errata_target):
|
|
# TODO(apanchenko):
|
|
# If 'target' from erratum doesn't match 'target' from tests we need to
|
|
# skip tests and return special exit code, so Jenkins is able to recognize
|
|
# test were skipped and it shouldn't vote to CRs (just leave comment)
|
|
logger.error('Tests for "{0}" were started, but patches are targeted to '
|
|
'"{1}" according to erratum.'.format(target, errata_target))
|
|
sys.exit(123)
|