NetworkManager/contrib/rh-bkr/bkr.py
2016-07-18 09:58:41 +02:00

1038 lines
44 KiB
Python
Executable file

#!/usr/bin/env python
import sys
import argparse
import subprocess
import os
import re
import kobo.xmlrpc
import xmlrpclib
import termcolor
import os
import tempfile
import datetime
import random
import string
import urllib
import glob
import uuid
import nitrate
devnull = open(os.devnull, 'w')
timestamp = datetime.datetime.now().strftime('%Y%m%d-%H%M%S');
def _check_output(*popenargs, **kwargs):
if "check_output" in dir(subprocess):
return subprocess.check_output(*popenargs, **kwargs)
# check_output is Python 2.7, reimplement it for older version.
# See https://hg.python.org/cpython/file/d37f963394aa/Lib/subprocess.py#l544
if 'stdout' in kwargs:
raise ValueError('stdout argument not allowed, it will be overridden.')
process = subprocess.Popen(stdout=subprocess.PIPE, *popenargs, **kwargs)
output, unused_err = process.communicate()
retcode = process.poll()
if retcode:
cmd = kwargs.get("args")
if cmd is None:
cmd = popenargs[0]
error = subprocess.CalledProcessError(retcode, cmd)
error.output = output
raise error
return output
def id_generator(size=6, chars=string.ascii_lowercase + string.ascii_uppercase + string.digits):
return ''.join(random.choice(chars) for x in range(size))
def is_sequence(arg):
return (not hasattr(arg, "strip") and
hasattr(arg, "__getitem__") or
hasattr(arg, "__iter__"))
def seq_unique(seq):
s = set()
for i in seq:
if i not in s:
s.add(i)
yield i
def sub_dict(diction, keys, default=None):
r = dict(diction)
return dict([ (k, r.pop(k, default)) for k in keys ]), r
def nitrate_get_script_name_for_case(case):
name = case.get('script_name', None)
if name is None:
name = case['script']
if name is None:
if '__script_name_hack' not in case:
print("WARNING: test case has no script name. It cannot be used: %s" % (str(case)))
case['__script_name_hack'] = ''
return ""
name = name.split('=')[-1]
case['script_name'] = name
return name
_nitrate_tags = {}
def nitrate_get_tag_by_id(tag_id, fail_on_not_exists=False):
if tag_id not in _nitrate_tags:
tags = nitrate.Nitrate()._server.Tag.get_tags({'ids': [tag_id]})
if not tags:
_nitrate_tags[tag_id] = None
elif len(tags) > 1:
raise Error("tag with id %d appears more then once: %s" % (tag_id, repr(tags)))
else:
_nitrate_tags[tag_id] = tags[0]
t = _nitrate_tags.get(tag_id, None)
if fail_on_not_exists and t is None:
raise Exception("Tag with id='%s' does not exist" % (tag_id))
return t
_nitrate_tags_searched_name = {}
def nitrate_get_tag_by_name(tag_name, fail_on_not_exists=False):
if tag_name not in _nitrate_tags_searched_name:
tags = nitrate.Nitrate()._server.Tag.get_tags({'names': [tag_name]})
for t in tags:
_nitrate_tags[t['id']] = t
_nitrate_tags_searched_name[tag_name] = 1
tags = [ tag for tag_id, tag in _nitrate_tags.iteritems() if tag['name'] == tag_name ]
t = None
if tags:
if len(tags) > 1:
raise Error("tag with name %d appears more then once: %s" % (tag_name, repr(tags)))
t = tags[0]
if fail_on_not_exists and t is None:
raise Exception("Tag with name='%s' does not exist" % (tag_name))
return t
_nitrate_cases = {}
def _nitrate_add_case(case, tag_id=None):
case_id = case['case_id']
tags = None
if case_id in _nitrate_cases:
case2 = _nitrate_cases[case_id]
tags = case['tag'] + case2['tag']
case = case2
else:
_nitrate_cases[case_id] = case
if tag_id is not None:
if tags is None:
tags = [tag_id]
else:
tags.append(tag_id)
if tags is not None:
case['tag'] = list(sorted(set(tags)))
def _nitrate_base_filter(test_plan, additional=None, default=None):
# see https://tcms.engineering.redhat.com/plan/6726/networkmanager#treeview
# see https://tcms.engineering.redhat.com/plan/18716/networkmanager#treeview
if default is None:
# f = {'plan__component__name': 'NetworkManager'}
if test_plan is None or test_plan == 'devel':
f = {'plan__parent_id': '18716'}
elif test_plan == 'rhel-7.1':
f = {'plan__parent_id': '6726'}
else:
f = {'plan__parent_id': test_plan}
else:
f = dict(default);
if additional:
for key,value in additional.iteritems():
f[key] = value
return f
_nitrate_cases_searched_by_tag = {}
def nitrate_get_cases_by_tag(test_plan, tag=None, tag_name=None, tag_id=None):
if (0 if tag is None else 1) + \
(0 if tag_name is None else 1) + \
(0 if tag_id is None else 1) != 1:
raise Error("Need one filter argument")
if tag is None:
if tag_name is not None:
tag = nitrate_get_tag_by_name(tag_name, True)
if tag_id is not None:
tag = nitrate_get_tag_by_id(tag_id, True)
tag_id = tag['id']
if tag_id not in _nitrate_cases_searched_by_tag:
cases = nitrate.Nitrate()._server.TestCase.filter(_nitrate_base_filter(test_plan, {'tag' : tag_id}))
for case in cases:
_nitrate_add_case(case, tag_id)
_nitrate_cases_searched_by_tag[tag_id] = 1
return [ case for case_id, case in _nitrate_cases.iteritems() if tag_id in case['tag'] ]
_nitrate_get_cases_all = False
def nitrate_get_cases_all(test_plan):
global _nitrate_get_cases_all
if not _nitrate_get_cases_all:
cases = nitrate.Nitrate()._server.TestCase.filter(_nitrate_base_filter(test_plan))
for case in cases:
_nitrate_add_case(case)
_nitrate_get_cases_all = True
return [ case for case_id, case in _nitrate_cases.iteritems() ]
def nitrate_merge_cases(cases, new):
for case in new:
cases[case['case_id']] = case
def nitrate_subtract_cases(cases, remove, no_cases=None):
for case in remove:
case_id = case['case_id']
case = cases.get(case_id, None)
if case is not None:
del cases[case_id]
if no_cases is not None:
no_cases[case_id] = case
def nitrate_cases_get(test_plan, tags=None, no_tags=None, include_all=False):
cases = {}
no_cases = {}
if isinstance(tags, basestring):
tags = [tags]
if isinstance(no_tags, basestring):
no_tags = [no_tags]
cases_tag = []
cases_no_tag = []
# we have to fetch all the cases by tags esplicitly to merge them properly
# in our cache.
if not tags:
cases_tag = []
else:
cases_tag = [ nitrate_get_cases_by_tag(test_plan, tag_name=tag) for tag in tags ]
if include_all:
# only blacklist of ~all~. Fetch first all.
cases_tag.append(nitrate_get_cases_all(test_plan))
if no_tags:
cases_no_tag = [ nitrate_get_cases_by_tag(test_plan, tag_name=tag) for tag in no_tags ]
for c in cases_tag:
nitrate_merge_cases(cases, c)
for c in cases_no_tag:
nitrate_subtract_cases(cases, c, no_cases)
return cases, no_cases
def nitrate_filter_by_status(cases, whitelist=None, blacklist=None):
cases = _nitrate_index_cases_by_case_id(cases)
inc = set(cases.keys())
exc = set()
if whitelist:
l = []
for wl in whitelist:
l.extend([case_id for case_id, case in cases.iteritems() if case['case_status'] == wl])
new_inc = inc.intersection(l)
exc.update(inc.difference(new_inc))
inc = new_inc
if blacklist:
l = []
for wl in blacklist:
l.extend([case_id for case_id, case in cases.iteritems() if case['case_status'] == wl])
new_exc = inc.intersection(l)
inc.difference_update(new_exc)
exc.update(new_exc)
no_cases, cases = sub_dict(cases, exc)
return cases, no_cases
def _nitrate_index_cases_by_case_id(cases):
if not isinstance(cases, dict):
cases = dict([(case['case_id'], case) for case in cases])
return cases
def _nitrate_index_cases_by_tag_id(cases):
cases = _nitrate_index_cases_by_case_id(cases)
by_tag = {}
for case_id, case in cases.iteritems():
for tag_id in case['tag']:
t = by_tag.get(tag_id, None)
if t is None:
t = {}
by_tag[tag_id] = t
t[case_id] = case
t2 = {}
for tag_id, t in by_tag.iteritems():
l = list(t.values())
by_tag[tag_id] = l
t2[nitrate_get_tag_by_id(tag_id, True)['name']] = l
return t2, by_tag
def nitrate_print_cases(cases, prefix=""):
cases = _nitrate_index_cases_by_case_id(cases)
tags = {}
for case_id in sorted(cases.keys(), key=lambda i: (tuple(sorted(cases[i]["plan"])), i)):
case = cases[case_id]
t = case.get('tag_names', None)
if t is None:
tag_ids = tuple(case['tag'])
t = tags.get(tag_ids, None)
if t is None:
t = ','.join(sorted(set([nitrate_get_tag_by_id(tag_id, True)['name'] for tag_id in tag_ids])))
tags[tag_ids] = t
case['tag_names'] = t
print("%s[%10s/%7d] = %-60s - %-20s - [ %s %s]" % (prefix, \
",".join([str(i) for i in sorted(case["plan"])]),
case_id,
nitrate_get_script_name_for_case(case), case['case_status'], \
t, "(?) " if not _nitrate_get_cases_all else ""))
def nitrate_get_cases_by_one_tag(test_plan, tag_name):
tag = nitrate_get_tag_by_name(tag_name, True)
cases = nitrate_get_cases_all(test_plan)
cases_with_tag = nitrate_get_cases_by_tag(test_plan, tag)
cases = _nitrate_index_cases_by_case_id(cases)
cases_with_tag = _nitrate_index_cases_by_case_id(cases_with_tag)
return sub_dict(cases, cases_with_tag.keys())
def _call(args, stderr=None, reason=None, dry_run=False, verbose=False):
if verbose:
print(">%s '%s'" % ('x' if dry_run else '>', "' '".join(args)))
elif stderr is None:
stderr = devnull
try:
if dry_run:
output = ''
else:
output = _check_output(args, stderr=stderr)
except subprocess.CalledProcessError, e:
print "Error invoking command for %s: %s" % (reason, ' '.join(args))
print ''.join(['++ ' + x + '\n' for x in e.output.splitlines()])
sys.exit("invoking command failed");
return output
def bkr_wait_completion(job_id):
import pexpect
print(">> bkr-wait-completion: job %s : wait for job completion..." % (job_id))
command = "bkr job-watch J:%s" % job_id
process = pexpect.spawn(command)
r = process.expect(['--> Reserved', pexpect.EOF], timeout=None)
if r == 0:
print(">> bkr-wait-completion: job %s : completed and system is now reserved" % (job_id))
process.terminate()
if r == 1:
print(">> bkr-wait-completion: job %s : completed" % (job_id))
class RpmScheme:
def __init__(self, uri, arch):
self.uri = uri
self.arch = arch if arch is not None else 'x86_64'
self.arch_re = re.escape(self.arch)
def urls(self):
raise NotImplementedError("not implemented")
class RpmSchemeNone(RpmScheme):
def __init__(self, uri, arch):
RpmScheme.__init__(self, uri, arch)
def urls(self):
return []
class RpmSchemeUrl(RpmScheme):
def __init__(self, uri, arch):
RpmScheme.__init__(self, uri, arch)
def urls(self):
return [self.uri]
class RpmSchemeRpm(RpmScheme):
def __init__(self, uri, arch):
RpmScheme.__init__(self, uri, arch)
def urls(self):
if not hasattr(self, '_urls'):
u = self.uri
if u.startswith("rpm://"):
u = u[len("rpm://"):]
self._urls = u.replace(',',' ').split(' ')
return self._urls
class RpmScheme_ParseWebsite(RpmScheme):
def __init__(self, uri, arch):
self._pattern = None
RpmScheme.__init__(self, uri, arch)
@property
def pattern(self):
if self._pattern is not None:
return self._pattern
return '^.*/NetworkManager(-adsl|-bluetooth|-config-connectivity-fedora|-debuginfo|-glib|-libnm|-team|-tui|-wifi|-wwan)?-[0-9]+\.[0-9]+\.[0-9]+(\.[0-9]+)?-[^ /]*\.%s\.rpm$' % (self.arch_re)
def set_pattern(self, value):
if not value:
self._pattern = None
else:
try:
re.match(value, '')
except:
raise Exception("Error in uri scheme '%s': expects a valid regular expression" % uri)
self._pattern = value
@property
def pattern_is_default(self):
return self._pattern is None
def is_matching_url(self, url):
p = '^.*/([^/]+\.(src|noarch|%s)\.rpm)$' % (self.arch_re)
if re.match(p, url):
if re.search(self.pattern, url):
return True
return False
def read_page(self, url = None, follow_gz = True):
if url is None:
url = self._mainpage
p = urllib.urlopen(url)
page = p.read()
p.close()
if follow_gz and re.match('.*\.gz$', url):
import gzip, StringIO
p = StringIO.StringIO(page)
page = gzip.GzipFile(fileobj=p).read()
p.close()
return page
def urls(self):
if not hasattr(self, '_urls'):
self.parse_uri()
page = self.read_page()
urls = list(self.parse_urls(page))
if not urls:
self.raise_no_urls()
self._urls = urls
return self._urls
class RpmSchemeJenkins(RpmScheme_ParseWebsite):
jenkins_base_url = 'http://testuslav.usersys.redhat.com:8080/job/NetworkManager/'
def __init__(self, uri, arch):
RpmScheme_ParseWebsite.__init__(self, uri, arch)
def parse_uri(self):
m = re.match('^jenkins://([0-9]+)(/(.+)|/?)?$', self.uri)
if not m:
raise Exception("Error detecting uri scheme jenkins:// from '%s'. Expected is 'jenkins://[ID]/[regex-wildcard]" % (self.uri))
self._id = int(m.group(1))
self.set_pattern(m.group(3))
self._mainpage = '%s%d/' % (RpmSchemeJenkins.jenkins_base_url, self._id)
def parse_urls(self, page):
for a in re.finditer('href=[\'"](artifact/[^\'"]*\.rpm)[\'"]', page):
url = self._mainpage + a.group(1)
if self.is_matching_url(url):
yield url
def raise_no_urls(self):
raise Exception("Could not detect any URLs on jenkins for '%s' (see %s%s/)" % (self.uri, RpmSchemeJenkins.jenkins_base_url, self._id))
class RpmSchemeBrew(RpmScheme_ParseWebsite):
brew_base_url = 'https://brewweb.engineering.redhat.com/brew/'
def __init__(self, uri, arch):
RpmScheme_ParseWebsite.__init__(self, uri, arch)
def parse_uri(self):
if self.uri.startswith('brew://'):
self._type = "brew"
elif self.uri.startswith('brewtask://'):
self._type = "brewtask"
else:
raise Exception("Unexpected URI %s" % (self.uri))
if self._type == "brew":
m = re.match('^brew://([0-9]+)(/(.+)|/?)?$', self.uri)
elif self._type == "brewtask":
m = re.match('^brewtask://([0-9]+)(/(.+)|/?)?$', self.uri)
if not m:
raise Exception("Error detecting uri scheme %s:// from '%s'. Expected is '%s://[ID]/[regex-wildcard]" % (self._type, self.uri, self._type))
self._id = int(m.group(1))
self.set_pattern(m.group(3))
if self._type == "brew":
self._mainpage = '%sbuildinfo?buildID=%s' % (RpmSchemeBrew.brew_base_url, self._id)
elif self._type == "brewtask":
self._mainpage = '%staskinfo?taskID=%s' % (RpmSchemeBrew.brew_base_url, self._id)
def parse_urls(self, page):
found_anything = False
if self._type == "brew":
p = 'href=[\'"](http://download.eng.bos.redhat.com/brewroot/packages/[^\'"]*\.rpm)[\'"]'
elif self._type == "brewtask":
p = 'href=[\'"](http://download.eng.bos.redhat.com/brewroot/work/tasks/[^\'"]*\.rpm)[\'"]'
for a in re.finditer(p, page):
found_anything = True
url = a.group(1)
if self.is_matching_url(url):
yield url
if not found_anything and self._type == "brewtask":
# when the task-id is the main-page, we have to repeat... search deeper.
p2 = '<a href="(taskinfo\?taskID=[0-9]+)" class="taskclosed" title="closed">buildArch \(.*.rpm, %s\)</a>' % (self.arch_re)
for a in re.finditer(p2, page):
page = self.read_page('https://brewweb.engineering.redhat.com/brew/%s' % (a.group(1)))
for a in re.finditer(p, page):
url = a.group(1)
if self.is_matching_url(url):
yield url
return
return
def raise_no_urls(self):
if self.pattern_is_default:
raise Exception("Could not detect any URLs on brew for '%s' (see \"%s\"). Try giving a pattern \"%s://%s/.*\"" % (self.uri, self._mainpage, self._type, self._id))
raise Exception("Could not detect any URLs on brew for '%s' (see \"%s\")" % (self.uri, self._mainpage))
class RpmSchemeRepo(RpmScheme_ParseWebsite):
def __init__(self, uri, arch):
RpmScheme_ParseWebsite.__init__(self, uri, arch)
def parse_uri(self):
m = re.match('^repo:(.+?)/?([^\/]+rpm)?$', self.uri)
if not m:
raise Exception("Error detecting scheme repo: from '%s'. Expected is 'repo:<baseurl>[/<regex-wildcard>.rpm]" % (self.uri))
self._baseurl = m.group(1) + '/'
self.set_pattern(m.group(2))
p = urllib.urlopen(self._baseurl + 'repodata/repomd.xml')
r = re.compile('(.*)')
r = re.compile('.*<location href="([^"]*primary.xml[^"]*)"/>.*')
for line in p:
m = r.match(line)
if m:
self._mainpage = self._baseurl + m.group(1)
break
p.close()
if not hasattr(self, '_mainpage'):
raise Exception("Could not find primary.xml in %s" % self._baseurl + 'repodata/repomd.xml')
def parse_urls(self, page):
latest = {}
for a in re.finditer('href=[\'"]([^\'"]*?([^\'"/]+)-[^\'"-]+-[^\'"-]+\.rpm)[\'"]', page):
url = self._baseurl + a.group(1)
if self.is_matching_url(url):
latest[a.group(2)] = self._baseurl + a.group(1)
return latest.values()
def raise_no_urls(self):
raise Exception("Could not detect any URLs in '%s' repository" % self.uri)
class CmdBase:
def __init__(self, name):
self.name = name
self.parser = None
def run(self, argv):
print_usage()
class CmdSubmit(CmdBase):
def __init__(self, name):
CmdBase.__init__(self, name)
self.parser = argparse.ArgumentParser(prog=sys.argv[0] + " " + name, description='Submit job to beaker.')
self.parser.add_argument('--no-test', action='store_true', help='do submit the job to beaker')
self.parser.add_argument('--rpm', '-r', action='append', help='Filenames of RPMs. Supports (local) files, rpm://, jenkins://, brew://, brewtask:// and repo: URI schemes')
self.parser.add_argument('--build-id', '-b', help='Set to a git commit id or branch name of the upstream git repository of NM. If present, the script will build NM from source')
self.parser.add_argument('--nitrate-tag', '-t', action='append', help='Query nitrate for tests having this tag. Output is appended to $TESTS. Specifying more then once combines them as AND')
self.parser.add_argument('--nitrate-all', '-a', action='store_true', help='Query all nitrate tests')
self.parser.add_argument('--nitrate-exclude-tag', '-T', action='append', help='Query nitrate for tests not having this tag. Output is appended to $TESTS. In combination with --nitrate-tag this blacklists cases (after selecting then)')
self.parser.add_argument('--nitrate-status', '-s', action='append', help='After selecting the tests by via --nitrate-tag, --nitrate-all, or --nitrate-exclude-tag, further whitelist by status')
self.parser.add_argument('--nitrate-exclude-status', '-S', action='append', help='After selecting the tests by via --nitrate-tag, --nitrate-all, --nitrate-exclude-tag, further blacklist by status')
self.parser.add_argument('--nitrate-test-plan', '-P', help='Select the nitrate-test-plan for loading tests. Currently supported: \'devel\' (\'18716\'), \'rhel-7.1\' (\'6726\') or the numeric id of parent plan. The default depends on the target-branch. See for example https://tcms.engineering.redhat.com/plan/18716/networkmanager#treeview')
self.parser.add_argument('--tests', '-c', action='append', help='Append argument to $TESTS')
self.parser.add_argument('--job', '-j', help='beaker xml job file')
self.parser.add_argument('--job-default', '-J', action='store_true', help='Use default job file. Only has effect if --job is not specified')
self.parser.add_argument('--verbose', '-v', action='count', help='print more information')
self.parser.add_argument('--reservesys', '-R', nargs='?', choices=['if_fail', 'new'], default=argparse.SUPPRESS, help='add task /distribution/reservesys (same as --reservesys-time=86400')
self.parser.add_argument('--reservesys-time', help='add task /distribution/reservesys with a duration in second')
self.parser.add_argument('--disable-selinux', action='store_true', help='add kernel option selinux=0 to disable AVC checks ($SELINUX_DISABLED)')
self.parser.add_argument('--var', '-V', action='append', help='Set template replacements (alternative to setting via environment variables')
self.parser.add_argument('--hosttype', help='The host type. Known values are \'veth\', \'dcb\', \'infiniband\', and \'wifi\'. Anything else uses the default. This determines the $HOSTREQUIRES template')
self.parser.add_argument('--jobtype', help='The job type. Known values are \'rhel70\'. Anything else uses the default to create a retention=scratch job. This determines the $JOBTYPE template')
self.parser.add_argument('--profile', '-p', help='A predefined set of arguments. Known values are \'default\', \'veth\', \'wifi\', \'infiniband\', \'dcb\'.')
self.parser.add_argument('--bkr-write-job-id', help='If specified, write the job ID to the specified file.')
self.parser.add_argument('--bkr-wait-completion', action='store_true', help='Whether to wait for completion of the beaker job')
self.parser.add_argument('--bkr-job-results', help='If specified, write the job results to the specified file. Implies --bkr-wait-completion.')
self.parser.add_argument('--valgrind', action='store_true', help='setup the valgrind wrapper')
def _prepare_rpms(self):
if self.options.rpm is None:
self.rpm = None
return
self.rpm = []
for r in self.options.rpm:
if r.startswith('http://') or r.startswith('https://'):
ctor = RpmSchemeUrl
elif r.startswith('jenkins://'):
ctor = RpmSchemeJenkins
elif r.startswith('brew://') or r.startswith('brewtask://'):
ctor = RpmSchemeBrew
elif r.startswith('repo:'):
ctor = RpmSchemeRepo
elif r == 'none':
ctor = RpmSchemeNone
else:
ctor = RpmSchemeRpm
uf = ctor(r, self._get_var ("ARCH"))
self.rpm.append((r, uf))
def _print_substitution(self, k, v):
if is_sequence(v):
print("$%s = [" % (k))
for s in v:
print(" %s" % (s))
print(" ]")
else:
print("$%s = %r" % (k, v))
def _prepare_substitutions(self):
self.subs = {}
if self.rpm is not None:
self.subs['RPM_LIST'] = [ u for x in self.rpm for u in x[1].urls() ]
tests = []
t = self._get_var("TESTS")
if t:
tests.extend([t])
if self.options.tests:
tests.extend(self.options.tests)
if self.options.nitrate_all or self.options.nitrate_tag or self.options.nitrate_exclude_tag:
cases, no_cases = nitrate_cases_get(self._get_nitrate_test_plan(), self.options.nitrate_tag, self.options.nitrate_exclude_tag, self.options.nitrate_all)
cases, no_case_by_status = nitrate_filter_by_status(cases, self.options.nitrate_status, self.options.nitrate_exclude_status)
if self.options.verbose >= 1:
if self.options.nitrate_exclude_tag:
print("Blacklisted %d cases for tags %s..." % (len(no_cases), sorted(set(self.options.nitrate_exclude_tag))))
nitrate_print_cases(no_cases, prefix=" - ")
if self.options.nitrate_status or self.options.nitrate_exclude_status:
print("Excluded %d cases after %s%s%s" % (len(no_case_by_status), \
(("--nitrate-status=" + ",".join(self.options.nitrate_status)) if self.options.nitrate_status else ""), \
(" and " if (self.options.nitrate_status and self.options.nitrate_exclude_status) else ""), \
(("--nitrate-exclude-status=" + ",".join(self.options.nitrate_exclude_status)) if self.options.nitrate_exclude_status else "")))
nitrate_print_cases(no_case_by_status, prefix=" - ")
if self.options.nitrate_tag:
print("Selected %d cases for tags %s..." % (len(cases), sorted(set(self.options.nitrate_tag))))
else:
print("Selected %d cases..." % (len(cases)))
nitrate_print_cases(cases, prefix=" + ")
tests.extend(sorted(set([nitrate_get_script_name_for_case(case) for case_id, case in cases.iteritems()])))
elif self.options.nitrate_status or self.options.nitrate_exclude_status:
raise Exception("--nitrate-status or --nitrate-exclude-status makes only sense with selecting nitrate tags")
self.subs['TESTS'] = ','.join(tests)
self.subs['ARGV'] = ("\"" + "\" \"".join(sys.argv) + "\"").replace('"', '&quot;') if sys.argv else ''
self.subs['ARGV_PROFILE'] = ("\"" + "\" \"".join(self.argv_profile) + "\"").replace('"', '&quot;') if hasattr(self, 'argv_profile') else ''
for (k,v) in self.subs.iteritems():
self._print_substitution(k, v)
def _get_var(self, key_name, default_fallback=True):
if not hasattr(self, '_var'):
# Lazily set self._var from the command line arguments
# the first time we need it
self._var = {}
self._var_opts = {}
if self.options.var is not None:
for v0 in self.options.var:
v = v0.split('=', 1)
if len(v) != 2:
raise Exception("Invalid --var option %s. Should be NAME=VALUE" % (v0))
self._var[v[0]] = v[1]
self._var_opts[v[0]] = v[1]
if not default_fallback:
if key_name in self._var_opts:
return self._var_opts[key_name]
else:
if key_name in self._var:
return self._var[key_name]
v = os.environ.get(key_name)
if not default_fallback:
return v
if v is None and key_name in CmdSubmit.DefaultReplacements:
v = CmdSubmit.DefaultReplacements[key_name]
if not isinstance(v, basestring):
self._var[key_name] = None
v = v(self, key_name)
self._var[key_name] = v
return v
def _get_nitrate_test_plan(self):
if self.options.nitrate_test_plan:
return self.options.nitrate_test_plan
# if unspecified, detect the test-plan based on the target-branch.
try:
target_branch = self.__process_line_get_GIT_TEST_BRANCH_detect("nitrate-test-plan")
except:
target_branch = None
if target_branch == 'rhel-7.0' or \
target_branch == 'rhel-7.1':
t = 'rhel-7.1'
else:
t = 'devel'
print("Detected nitrate-test-plan=%s" % (t))
return t
def __process_line_get_GIT_TEST_BRANCH_detect(self, key_name):
# we default to 'master', unless there is an RPM that looks like it's from
# rhel-7.0.
v = self._get_var('GIT_TEST_BRANCH')
if v is not None:
return v
if self.rpm is not None:
for x in self.rpm:
for u in x[1].urls():
if re.match(r'^.*/NetworkManager-0.9.9.1-[1-9][0-9]*\.git20140326\.4dba720\.el7\.[^.]+\.rpm$', u):
return 'rhel-7.0' # stable rhel-7.0 release
if re.match(r'^.*/NetworkManager-0.9.11.0-[0-9]+\.[a-f0-9]+\.el7\.[^.]+\.rpm$', u):
return 'rhel-7.1' # upstream pre 1.0
if re.match(r'^.*/NetworkManager-0.9[0-9][0-9]+.0.0-[0-9]+\.[a-f0-9]+\.el7\.[^.]+\.rpm$', u):
return 'rhel-7.1' # upstream 1.0-beta
if re.match(r'^.*/NetworkManager-0.9.10.[0-9]+-[0-9]+\.[a-f0-9]+\.el7\.[^.]+\.rpm$', u):
return 'rhel-7.1' # 0.9.10
if re.match(r'^.*/NetworkManager-0.9.9.9[0-9]+-[0-9]+\.[a-f0-9]+\.el7\.[^.]+\.rpm$', u):
return 'rhel-7.1' # 0.9.10-rc
if re.match(r'^.*/NetworkManager-0\.9\.11\..*\.git20141022.e28ee14.el7\.[^.]+\.rpm$', u):
return 'rhel-7.1' # rhel-7.1-rc
if re.match(r'^.*/NetworkManager-1.0.[0-9]+-[0-9]+\.git20150121\.b4ea599c\.el7\.[^.]+\.rpm$', u):
return 'rhel-7.1' # rhel-7.1-rc
if re.match(r'^.*/NetworkManager-1.0.[0-9]+-[0-9]+\.[a-f0-9]+\.el7\.[^.]+\.rpm$', u):
return 'rhel-7' # upstream 1.0
if re.match(r'^.*/NetworkManager-1.0.[0-9]+-[0-9]+\.git20160622\.9c83d18d\.el7\.[^.]+\.rpm$', u):
return 'rhel-7' # rhel-7.2-rc
if re.match(r'^.*/NetworkManager-1.0.[0-9]+-[0-9]+\.git20160624\.f245b49a\.el7\.[^.]+\.rpm$', u):
return 'rhel-7' # rhel-7.2-rc
if re.match(r'^.*/NetworkManager-1.0.4-[0-9]+\.el7\.[^.]+\.rpm$', u) or \
re.match(r'^.*/NetworkManager-1.0.6-[0-9]+\.el7\.[^.]+\.rpm$', u):
return 'rhel-7' # rhel-7.2
# Master now tests everything
return 'master'
def _detect_hosttype(self):
return 'default'
def _get_var_for_JOBTYPE(self, key):
v = self._get_var('JOBTYPE')
if v is not None:
return v;
jobtype = self.options.jobtype
if jobtype == 'rhel70':
return 'product="cpe:/o:redhat:enterprise_linux:7.0" retention_tag="active+1"'
return 'retention_tag="scratch"'
def _get_var_for_HOSTREQUIRES(self, key):
v = self._get_var('HOSTREQUIRES')
if v is not None:
return v;
hosttype = self.options.hosttype
if hosttype == 'veth':
return '<group op="=" value="desktop"/>'
elif hosttype == 'dcb':
return '<hostname op="=" value="wsfd-netdev7.lab.bos.redhat.com"/>'
elif hosttype == 'infiniband':
return '<group op="=" value="RDMA - ib0"/>'
elif hosttype == 'wifi':
return '''
<group op="=" value="wireless"/>
<hostname op="like" value="wlan-r2%.wlan.rhts.eng.bos.redhat.com"/>
<!-- 8086:08ae (iwlwifi,iwldvm) Intel Corporation Centrino Wireless-N 100 doesn't support AP mode -->
<device op="!=" vendor_id="8086" device_id="08ae"/>
<!-- 8086:08b3 (iwlwifi,iwlmvm) Ooops-es: https://bugzilla.redhat.com/show_bug.cgi?id=1235694 -->
<device op="!=" vendor_id="8086" device_id="08b3"/>
<!-- Pick an Intel, so that we're not scheduled on some poor Realtek chip -->
<device op="==" driver="iwlwifi"/>
'''
else:
return '<group op="=" value="desktopqe-net"/>'
def _get_var_for_GIT_TEST_BRANCH(self, key):
return self.__process_line_get_GIT_TEST_BRANCH_detect("GIT_TEST_BRANCH")
def _get_var_for_DISTRO_NAME(self, key):
v = self._get_var('DISTO_NAME')
if v is not None:
return v
target_branch = self.__process_line_get_GIT_TEST_BRANCH_detect("DISTRO_NAME")
if target_branch == 'rhel-7.0':
return 'RHEL-7.0-20140507.0'
if target_branch == 'rhel-7.1':
return 'RHEL-7.1-20141023.n.1'
if target_branch == 'rhel-7':
pass
return 'RHEL-7.2-20150907.n.0'
def _get_var_for_DISTRO_TAG(self, key):
v = self._get_var('DISTRO_TAG')
if v:
return v
target_branch = self.__process_line_get_GIT_TEST_BRANCH_detect("DISTRO_TAG")
if target_branch == 'rhel-7.0':
return 'RHEL-7_0-Z-branch'
if target_branch == 'rhel-7.1':
return 'RHEL-7_1-Z-branch'
if target_branch == 'rhel-7':
pass
return 'RTT_ACCEPTED'
def _get_var_for_DISTROREQUIRES(self, key):
v = self._get_var('DISTROREQUIRES')
if v is not None:
return v
vf = self._get_var('DISTRO_FAMILY', False)
vn = self._get_var('DISTRO_NAME', False)
if vf:
vn = None
elif vn:
vf = None
if vn is not None:
return '<distro_name op="=" value="%s"/>' % (vn)
ret = '<distro_family op="=" value="%s"/>' % self._get_var('DISTRO_FAMILY')
if vf is None:
ret = ret + '<distro_tag op="=" value="%s"/>' % self._get_var('DISTRO_TAG', True)
return ret
def _get_var_for_RESERVESYS(self, key):
v = self._get_var('RESERVESYS')
if v is not None:
return v
duration = self._get_var('RESERVE')
if self.options.reservesys_time:
duration = self.options.reservesys_time
if not hasattr (self.options, 'reservesys'):
return ""
elif self.options.reservesys == "new":
if not duration:
duration = '86400'
return '<reservesys duration="%s"/>' % (duration)
else:
return '<task name="/distribution/reservesys" role="STANDALONE"><params>%s%s</params></task>' % (
('<param name="RESERVETIME" value="%d" />' % (duration) if duration else ''),
('<param name="RESERVE_IF_FAIL" value="True" />'if self.options.reservesys == "if_fail" else ''));
def _get_var_for_ARCH(self, key):
v = self._get_var('ARCH')
if v:
return v
v = self._get_var('DISTRO_ARCH')
if v is not None:
return v
return 'x86_64'
def _get_var_for_SELINUX_DISABLED(self, key):
v = self._get_var('SELINUX_DISABLED')
if v is not None:
return v
if self.options.disable_selinux or self._get_var('SELINUX') == 'false':
return 'selinux=0'
return ''
def _get_var_for_BUILD_ID(self, key):
v = self._get_var('BUILD_ID')
if v is not None:
return v
if self.options.build_id:
return self.options.build_id
return ''
def _get_var_for_RPM_LIST(self, key):
# RPM_LIST is provided by subs. If it is not,
# we want to fail gracefully if BUILD_ID is set.
# This avoids a warning.
v = self._get_var_for_BUILD_ID (key)
if v:
return ''
return None
def _get_var_for_VALGRIND(self, key):
if self._get_var('VALGRIND') is not None or self.options.valgrind:
if self._get_var_for_SELINUX_DISABLED('SELINUX_DISABLED') == '':
raise Exception("Valgrind wrapping won't work with SELinux enabled")
return 'valgrind'
return ''
DefaultReplacements = {
'WHITEBOARD' : 'Test NetworkManager',
'DISTRO_FAMILY' : 'RedHatEnterpriseLinux7',
'DISTRO_VARIANT' : 'Server',
'DISTRO_NAME' : _get_var_for_DISTRO_NAME,
'DISTRO_TAG' : _get_var_for_DISTRO_TAG,
'DISTRO_METHOD' : 'nfs',
'DISTRO_ARCH' : 'x86_64',
'ARCH' : _get_var_for_ARCH,
'HOSTREQUIRES' : _get_var_for_HOSTREQUIRES,
'JOBTYPE' : _get_var_for_JOBTYPE,
'DISTROREQUIRES' : _get_var_for_DISTROREQUIRES,
'TEST_URL' : 'http://download.eng.brq.redhat.com/scratch/vbenes/NetworkManager-rhel-7.tar.gz',
'GIT_TEST_REPO' : 'http://code.engineering.redhat.com/gerrit/desktopqe/NetworkManager',
'GIT_TEST_BRANCH' : _get_var_for_GIT_TEST_BRANCH,
'UUID' : str(uuid.uuid4()),
'RESERVESYS' : _get_var_for_RESERVESYS,
'SELINUX_DISABLED' : _get_var_for_SELINUX_DISABLED,
'BUILD_ID' : _get_var_for_BUILD_ID,
'BUILD_TEST' : 'true',
'BUILD_REPO' : 'git://anongit.freedesktop.org/NetworkManager/NetworkManager',
'CONF_LOGLEVEL' : 'DEBUG',
'CONF_DHCP' : 'dhclient',
'CONF_DEBUG' : 'RLIMIT_CORE,fatal-warnings',
'RPM_LIST' : _get_var_for_RPM_LIST,
'VALGRIND' : _get_var_for_VALGRIND,
}
def _process_line_get(self, key, replacements):
if key in replacements:
v = replacements[key]
else:
if key in self.subs:
v = self.subs[key];
if is_sequence(v):
v = " \\\n\t".join(v)
else:
v = self._get_var(key)
replacements[key] = v
return v if v is not None else ''
re_subs0 = re.compile('^(?P<prefix>[^$]*)(?P<rest>\$.*\n?)$')
re_subs1 = re.compile('^\$(?P<var>\$|[a-zA-Z_]+)(?P<rest>.*\n?$)')
def _process_line(self, l, replacements):
r = ''
while True:
m = CmdSubmit.re_subs0.match(l)
if m is None:
return r + l
r = r + m.group('prefix')
l = m.group('rest')
m = CmdSubmit.re_subs1.match(l)
if m is None:
l = l[1:]
r = r + '$'
continue
var = m.group('var')
if var == '$':
r = r + '$'
elif var:
r = r + self._process_line_get(var, replacements)
else:
r = r + '$' + var
l = m.group('rest')
if not l:
return r
def run(self, argv):
self.options = self.parser.parse_args(argv)
if self.options.profile:
argv_profiles = {
'default': [ "-s", "CONFIRMED", "-a", "-t", "t-master", "-T", "wifi", "-T", "infiniband", "-T", "dcb", "-T", "no-t-master" ],
'veth': [ "-s", "CONFIRMED", "--hosttype", "veth", "-a", "-t", "t-master", "-T", "wifi", "-T", "infiniband", "-T", "dcb", "-T", "no-t-master" ],
'wifi': [ "-s", "CONFIRMED", "--hosttype", "wifi", "-t", "wifi" ],
'infiniband': [ "-s", "CONFIRMED", "--hosttype", "infiniband", "-t", "infiniband" ],
'dcb': [ "-s", "CONFIRMED", "--hosttype", "dcb", "-t", "dcb" ],
}
if self.options.profile not in argv_profiles:
raise Exception("Unknown profile \"%s\". Valid values are %s" % (self.options.profile, argv_profiles.keys()))
self.argv_profile = argv_profiles[self.options.profile]
self.options = self.parser.parse_args(self.argv_profile + argv)
if not self.options.job and self.options.job_default:
self.options.job = os.path.dirname(os.path.abspath(__file__)) + '/job01.xml'
if self.options.job:
with open(self.options.job) as f:
job0 = list(f)
self._prepare_rpms()
self._prepare_substitutions()
if self.options.job:
job = []
replacements = {}
for l in job0:
job.append(self._process_line(l, replacements))
replacements = sorted(replacements.iteritems(), key=lambda x: x[0])
for (k,v) in [ (k,v) for (k,v) in replacements if v is not None ]:
print("replace \'%s\' => '%s'" % (k, v))
for k in [ k for (k,v) in replacements if v is None ]:
print("replace \'%s\' %s" % (k, termcolor.colored("not found", 'yellow')))
temp = tempfile.NamedTemporaryFile(prefix='bkr_job.xml.', delete=False)
for l in job:
temp.write(l)
temp.close()
print("Write job '%s' to file '%s'" % (self.options.job, temp.name));
if self.options.job:
args = ['bkr', 'job-submit', temp.name]
if not self.options.no_test:
out = _call(args, dry_run=True, verbose=True)
else:
out = _call(args, dry_run=False, verbose=True)
print("Job successfully submitted: " + out)
m = re.match('.*J:([0-9]+).*', out)
if not m:
raise Exception("Failed to submit job. Command did't return a job-id")
job_id = m.group(1)
print("URL: https://beaker.engineering.redhat.com/jobs/%s" % (job_id))
print(" https://beaker.engineering.redhat.com/jobs/mine");
if self.options.bkr_write_job_id:
with open(self.options.bkr_write_job_id, "w") as text_file:
text_file.write("J:%s\n" % (job_id))
if self.options.bkr_wait_completion or self.options.bkr_job_results:
bkr_wait_completion(job_id)
if self.options.bkr_job_results:
print(">> bkr-job-results: job %s : retrieve job results in file %s" % (job_id, self.options.bkr_job_results))
with open(self.options.bkr_job_results, "w") as text_file:
r = subprocess.call(["bkr", "job-results", "--prettyxml", "J:%s" % (job_id)], stdout=text_file)
if r != 0:
raise Exception("getting job results failed")
class CmdHelp(CmdBase):
def __init__(self, name):
CmdBase.__init__(self, name)
def run(self, argv):
print_usage()
if len(argv) >= 1:
commands = find_cmds_by_name(argv[0])
if len(commands) == 1:
parser = commands[0].parser
if parser:
print
parser.print_help()
if __name__ == "__main__":
commands = {}
def commands_add(name, t, realname=None):
commands[name] = t(realname if realname else name)
commands_add('submit', CmdSubmit)
commands_add('help', CmdHelp)
commands_add('?', CmdHelp, realname='help')
def find_cmds_by_name(command_name):
return list([commands[cmd] for cmd in commands.keys() if cmd.startswith(command_name)])
def print_usage():
print("%s [%s] [OPTIONS]" % (sys.argv[0], '|'.join(commands.keys())))
if len(sys.argv) < 2:
print_usage()
sys.exit(1)
commands_matches = find_cmds_by_name(sys.argv[1])
if len(commands_matches) == 0:
print("Invalid command \"%s\". Try one of [ %s ]" % (sys.argv[1], ', '.join(commands.keys())))
print_usage();
sys.exit(1)
elif len(commands_matches) > 1:
print("Invalid command \"%s\". Not exact match of [ %s ]" % (sys.argv[1], ', '.join(commands.keys())))
print_usage();
sys.exit(1)
else:
commands_matches[0].run(sys.argv[2:])