Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(311)

Unified Diff: client/tests/kvm/kvm_utils.py

Issue 6883035: Merge remote branch 'autotest-upstream/master' into autotest-merge (Closed) Base URL: ssh://gitrw.chromium.org:9222/autotest.git@master
Patch Set: patch Created 9 years, 8 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
Index: client/tests/kvm/kvm_utils.py
diff --git a/client/tests/kvm/kvm_utils.py b/client/tests/kvm/kvm_utils.py
index de52b65c4b99e40a74cff019a255b467740814c1..38c4afe335ce7bcb720749416b9bc82777d95010 100644
--- a/client/tests/kvm/kvm_utils.py
+++ b/client/tests/kvm/kvm_utils.py
@@ -6,6 +6,7 @@ KVM test utility functions.
import time, string, random, socket, os, signal, re, logging, commands, cPickle
import fcntl, shelve, ConfigParser, rss_file_transfer, threading, sys, UserDict
+import inspect
from autotest_lib.client.bin import utils, os_dep
from autotest_lib.client.common_lib import error, logging_config
import kvm_subprocess
@@ -675,7 +676,7 @@ def wait_for_login(client, host, port, username, password, prompt, linesep="\n",
linesep, log_filename, internal_timeout)
-def _remote_scp(session, password, transfer_timeout=600, login_timeout=10):
+def _remote_scp(session, password_list, transfer_timeout=600, login_timeout=10):
"""
Transfer file(s) to a remote host (guest) using SCP. Wait for questions
and provide answers. If login_timeout expires while waiting for output
@@ -685,7 +686,7 @@ def _remote_scp(session, password, transfer_timeout=600, login_timeout=10):
@brief: Transfer files using SCP, given a command line.
@param session: An Expect or ShellSession instance to operate on
- @param password: The password to send in reply to a password prompt.
+ @param password_list: Password list to send in reply to the password prompt
@param transfer_timeout: The time duration (in seconds) to wait for the
transfer to complete.
@param login_timeout: The maximal time duration (in seconds) to wait for
@@ -701,6 +702,8 @@ def _remote_scp(session, password, transfer_timeout=600, login_timeout=10):
timeout = login_timeout
authentication_done = False
+ scp_type = len(password_list)
+
while True:
try:
match, text = session.read_until_last_line_matches(
@@ -712,8 +715,18 @@ def _remote_scp(session, password, transfer_timeout=600, login_timeout=10):
continue
elif match == 1: # "password:"
if password_prompt_count == 0:
- logging.debug("Got password prompt; sending '%s'", password)
- session.sendline(password)
+ logging.debug("Got password prompt; sending '%s'" %
+ password_list[password_prompt_count])
+ session.sendline(password_list[password_prompt_count])
+ password_prompt_count += 1
+ timeout = transfer_timeout
+ if scp_type == 1:
+ authentication_done = True
+ continue
+ elif password_prompt_count == 1 and scp_type == 2:
+ logging.debug("Got password prompt; sending '%s'" %
+ password_list[password_prompt_count])
+ session.sendline(password_list[password_prompt_count])
password_prompt_count += 1
timeout = transfer_timeout
authentication_done = True
@@ -736,7 +749,7 @@ def _remote_scp(session, password, transfer_timeout=600, login_timeout=10):
raise SCPTransferFailedError(e.status, e.output)
-def remote_scp(command, password, log_filename=None, transfer_timeout=600,
+def remote_scp(command, password_list, log_filename=None, transfer_timeout=600,
login_timeout=10):
"""
Transfer file(s) to a remote host (guest) using SCP.
@@ -745,7 +758,7 @@ def remote_scp(command, password, log_filename=None, transfer_timeout=600,
@param command: The command to execute
(e.g. "scp -r foobar root@localhost:/tmp/").
- @param password: The password to send in reply to a password prompt.
+ @param password_list: Password list to send in reply to a password prompt.
@param log_filename: If specified, log all output to this file
@param transfer_timeout: The time duration (in seconds) to wait for the
transfer to complete.
@@ -766,7 +779,7 @@ def remote_scp(command, password, log_filename=None, transfer_timeout=600,
output_func=output_func,
output_params=output_params)
try:
- _remote_scp(session, password, transfer_timeout, login_timeout)
+ _remote_scp(session, password_list, transfer_timeout, login_timeout)
finally:
session.close()
@@ -789,7 +802,10 @@ def scp_to_remote(host, port, username, password, local_path, remote_path,
command = ("scp -v -o UserKnownHostsFile=/dev/null "
"-o PreferredAuthentications=password -r -P %s %s %s@%s:%s" %
(port, local_path, username, host, remote_path))
- remote_scp(command, password, log_filename, timeout)
+ password_list = []
+ password_list.append(password)
+ return remote_scp(command, password_list, log_filename, timeout)
+
def scp_from_remote(host, port, username, password, remote_path, local_path,
@@ -810,7 +826,34 @@ def scp_from_remote(host, port, username, password, remote_path, local_path,
command = ("scp -v -o UserKnownHostsFile=/dev/null "
"-o PreferredAuthentications=password -r -P %s %s@%s:%s %s" %
(port, username, host, remote_path, local_path))
- remote_scp(command, password, log_filename, timeout)
+ password_list = []
+ password_list.append(password)
+ remote_scp(command, password_list, log_filename, timeout)
+
+
+def scp_between_remotes(src, dst, port, s_passwd, d_passwd, s_name, d_name,
+ s_path, d_path, log_filename=None, timeout=600):
+ """
+ Copy files from a remote host (guest) to another remote host (guest).
+
+ @param src/dst: Hostname or IP address of src and dst
+ @param s_name/d_name: Username (if required)
+ @param s_passwd/d_passwd: Password (if required)
+ @param s_path/d_path: Path on the remote machine where we are copying
+ from/to
+ @param log_filename: If specified, log all output to this file
+ @param timeout: The time duration (in seconds) to wait for the transfer
+ to complete.
+
+ @return: True on success and False on failure.
+ """
+ command = ("scp -v -o UserKnownHostsFile=/dev/null -o "
+ "PreferredAuthentications=password -r -P %s %s@%s:%s %s@%s:%s" %
+ (port, s_name, src, s_path, d_name, dst, d_path))
+ password_list = []
+ password_list.append(s_passwd)
+ password_list.append(d_passwd)
+ return remote_scp(command, password_list, log_filename, timeout)
def copy_files_to(address, client, username, password, port, local_path,
@@ -1108,30 +1151,38 @@ def run_tests(parser, job):
for test_name in status_dict.keys():
if not dep in test_name:
continue
- if not status_dict[test_name]:
+ # So the only really non-fatal state is WARN,
+ # All the others make it not safe to proceed with dependency
+ # execution
+ if status_dict[test_name] not in ['GOOD', 'WARN']:
dependencies_satisfied = False
break
+ test_iterations = int(dict.get("iterations", 1))
+ test_tag = dict.get("shortname")
+
if dependencies_satisfied:
- test_iterations = int(dict.get("iterations", 1))
- test_tag = dict.get("shortname")
# Setting up profilers during test execution.
profilers = dict.get("profilers", "").split()
for profiler in profilers:
job.profilers.add(profiler)
-
# We need only one execution, profiled, hence we're passing
# the profile_only parameter to job.run_test().
- current_status = job.run_test("kvm", params=dict, tag=test_tag,
- iterations=test_iterations,
- profile_only= bool(profilers) or None)
-
+ profile_only = bool(profilers) or None
+ current_status = job.run_test_detail("kvm", params=dict,
+ tag=test_tag,
+ iterations=test_iterations,
+ profile_only=profile_only)
for profiler in profilers:
job.profilers.delete(profiler)
-
- if not current_status:
- failed = True
else:
- current_status = False
+ # We will force the test to fail as TestNA during preprocessing
+ dict['dependency_failed'] = 'yes'
+ current_status = job.run_test_detail("kvm", params=dict,
+ tag=test_tag,
+ iterations=test_iterations)
+
+ if not current_status:
+ failed = True
status_dict[dict.get("name")] = current_status
return not failed
@@ -1149,6 +1200,19 @@ def create_report(report_dir, results_dir):
os.system('%s -r %s -f %s -R' % (reporter, results_dir, html_file))
+def display_attributes(instance):
+ """
+ Inspects a given class instance attributes and displays them, convenient
+ for debugging.
+ """
+ logging.debug("Attributes set:")
+ for member in inspect.getmembers(instance):
+ name, value = member
+ attribute = getattr(instance, name)
+ if not (name.startswith("__") or callable(attribute) or not value):
+ logging.debug(" %s: %s", name, value)
+
+
def get_full_pci_id(pci_id):
"""
Get full PCI ID of pci_id.
@@ -1535,142 +1599,597 @@ class PciAssignable(object):
return
-class KojiDownloader(object):
+class KojiClient(object):
"""
- Stablish a connection with the build system, either koji or brew.
+ Stablishes a connection with the build system, either koji or brew.
- This class provides a convenience methods to retrieve packages hosted on
- the build system.
+ This class provides convenience methods to retrieve information on packages
+ and the packages themselves hosted on the build system. Packages should be
+ specified in the KojiPgkSpec syntax.
"""
- def __init__(self, cmd):
+
+ CMD_LOOKUP_ORDER = ['/usr/bin/brew', '/usr/bin/koji' ]
+
+ CONFIG_MAP = {'/usr/bin/brew': '/etc/brewkoji.conf',
+ '/usr/bin/koji': '/etc/koji.conf'}
+
+
+ def __init__(self, cmd=None):
"""
Verifies whether the system has koji or brew installed, then loads
the configuration file that will be used to download the files.
- @param cmd: Command name, either 'brew' or 'koji'. It is important
- to figure out the appropriate configuration used by the
- downloader.
- @param dst_dir: Destination dir for the packages.
+ @type cmd: string
+ @param cmd: Optional command name, either 'brew' or 'koji'. If not
+ set, get_default_command() is used and to look for
+ one of them.
+ @raise: ValueError
"""
if not KOJI_INSTALLED:
raise ValueError('No koji/brew installed on the machine')
- if os.path.isfile(cmd):
- koji_cmd = cmd
+ # Instance variables used by many methods
+ self.command = None
+ self.config = None
+ self.config_options = {}
+ self.session = None
+
+ # Set koji command or get default
+ if cmd is None:
+ self.command = self.get_default_command()
else:
- koji_cmd = os_dep.command(cmd)
+ self.command = cmd
- logging.debug("Found %s as the buildsystem interface", koji_cmd)
+ # Check koji command
+ if not self.is_command_valid():
+ raise ValueError('Koji command "%s" is not valid' % self.command)
- config_map = {'/usr/bin/koji': '/etc/koji.conf',
- '/usr/bin/brew': '/etc/brewkoji.conf'}
+ # Assuming command is valid, set configuration file and read it
+ self.config = self.CONFIG_MAP[self.command]
+ self.read_config()
- try:
- config_file = config_map[koji_cmd]
- except IndexError:
- raise ValueError('Could not find config file for %s' % koji_cmd)
-
- base_name = os.path.basename(koji_cmd)
- if os.access(config_file, os.F_OK):
- f = open(config_file)
- config = ConfigParser.ConfigParser()
- config.readfp(f)
- f.close()
- else:
- raise IOError('Configuration file %s missing or with wrong '
- 'permissions' % config_file)
-
- if config.has_section(base_name):
- self.koji_options = {}
- session_options = {}
- server = None
- for name, value in config.items(base_name):
- if name in ('user', 'password', 'debug_xmlrpc', 'debug'):
- session_options[name] = value
- self.koji_options[name] = value
- self.session = koji.ClientSession(self.koji_options['server'],
- session_options)
- else:
- raise ValueError('Koji config file %s does not have a %s '
- 'session' % (config_file, base_name))
+ # Setup koji session
+ server_url = self.config_options['server']
+ session_options = self.get_session_options()
+ self.session = koji.ClientSession(server_url,
+ session_options)
- def get(self, src_package, dst_dir, rfilter=None, tag=None, build=None,
- arch=None):
- """
- Download a list of packages from the build system.
-
- This will download all packages originated from source package [package]
- with given [tag] or [build] for the architecture reported by the
- machine.
-
- @param src_package: Source package name.
- @param dst_dir: Destination directory for the downloaded packages.
- @param rfilter: Regexp filter, only download the packages that match
- that particular filter.
- @param tag: Build system tag.
- @param build: Build system ID.
- @param arch: Package arch. Useful when you want to download noarch
- packages.
-
- @return: List of paths with the downloaded rpm packages.
- """
- if build and build.isdigit():
- build = int(build)
-
- if tag and build:
- logging.info("Both tag and build parameters provided, ignoring tag "
- "parameter...")
-
- if not tag and not build:
- raise ValueError("Koji install selected but neither koji_tag "
- "nor koji_build parameters provided. Please "
- "provide an appropriate tag or build name.")
-
- if not build:
- builds = self.session.listTagged(tag, latest=True, inherit=True,
- package=src_package)
- if not builds:
- raise ValueError("Tag %s has no builds of %s" % (tag,
- src_package))
- info = builds[0]
- else:
- info = self.session.getBuild(build)
+ def read_config(self, check_is_valid=True):
+ '''
+ Reads options from the Koji configuration file
+
+ By default it checks if the koji configuration is valid
+
+ @type check_valid: boolean
+ @param check_valid: whether to include a check on the configuration
+ @raises: ValueError
+ @returns: None
+ '''
+ if check_is_valid:
+ if not self.is_config_valid():
+ raise ValueError('Koji config "%s" is not valid' % self.config)
- if info is None:
- raise ValueError('No such brew/koji build: %s' % build)
+ config = ConfigParser.ConfigParser()
+ config.read(self.config)
+ basename = os.path.basename(self.command)
+ for name, value in config.items(basename):
+ self.config_options[name] = value
+
+
+ def get_session_options(self):
+ '''
+ Filter only options necessary for setting up a cobbler client session
+
+ @returns: only the options used for session setup
+ '''
+ session_options = {}
+ for name, value in self.config_options.items():
+ if name in ('user', 'password', 'debug_xmlrpc', 'debug'):
+ session_options[name] = value
+ return session_options
+
+
+ def is_command_valid(self):
+ '''
+ Checks if the currently set koji command is valid
+
+ @returns: True or False
+ '''
+ koji_command_ok = True
+
+ if not os.path.isfile(self.command):
+ logging.error('Koji command "%s" is not a regular file',
+ self.command)
+ koji_command_ok = False
+
+ if not os.access(self.command, os.X_OK):
+ logging.warn('Koji command "%s" is not executable: this is '
+ 'not fatal but indicates an unexpected situation',
+ self.command)
+
+ if not self.command in self.CONFIG_MAP.keys():
+ logging.error('Koji command "%s" does not have a configuration '
+ 'file associated to it', self.command)
+ koji_command_ok = False
+
+ return koji_command_ok
+
+
+ def is_config_valid(self):
+ '''
+ Checks if the currently set koji configuration is valid
+
+ @returns: True or False
+ '''
+ koji_config_ok = True
+
+ if not os.path.isfile(self.config):
+ logging.error('Koji config "%s" is not a regular file', self.config)
+ koji_config_ok = False
+
+ if not os.access(self.config, os.R_OK):
+ logging.error('Koji config "%s" is not readable', self.config)
+ koji_config_ok = False
+
+ config = ConfigParser.ConfigParser()
+ config.read(self.config)
+ basename = os.path.basename(self.command)
+ if not config.has_section(basename):
+ logging.error('Koji configuration file "%s" does not have a '
+ 'section "%s", named after the base name of the '
+ 'currently set koji command "%s"', self.config,
+ basename, self.command)
+ koji_config_ok = False
+
+ return koji_config_ok
+
+
+ def get_default_command(self):
+ '''
+ Looks up for koji or brew "binaries" on the system
+
+ Systems with plain koji usually don't have a brew cmd, while systems
+ with koji, have *both* koji and brew utilities. So we look for brew
+ first, and if found, we consider that the system is configured for
+ brew. If not, we consider this is a system with plain koji.
+
+ @returns: either koji or brew command line executable path, or None
+ '''
+ koji_command = None
+ for command in self.CMD_LOOKUP_ORDER:
+ if os.path.isfile(command):
+ koji_command = command
+ break
+ else:
+ koji_command_basename = os.path.basename(koji_command)
+ try:
+ koji_command = os_dep.command(koji_command_basename)
+ break
+ except ValueError:
+ pass
+ return koji_command
+
+
+ def get_pkg_info(self, pkg):
+ '''
+ Returns information from Koji on the package
+
+ @type pkg: KojiPkgSpec
+ @param pkg: information about the package, as a KojiPkgSpec instance
+
+ @returns: information from Koji about the specified package
+ '''
+ info = {}
+ if pkg.build is not None:
+ info = self.session.getBuild(int(pkg.build))
+ elif pkg.tag is not None and pkg.package is not None:
+ builds = self.session.listTagged(pkg.tag,
+ latest=True,
+ inherit=True,
+ package=pkg.package)
+ if builds:
+ info = builds[0]
+ return info
+
+
+ def is_pkg_valid(self, pkg):
+ '''
+ Checks if this package is altogether valid on Koji
+
+ This verifies if the build or tag specified in the package
+ specification actually exist on the Koji server
+
+ @returns: True or False
+ '''
+ valid = True
+ if not self.is_pkg_spec_build_valid(pkg):
+ valid = False
+ if not self.is_pkg_spec_tag_valid(pkg):
+ valid = False
+ return valid
+
+
+ def is_pkg_spec_build_valid(self, pkg):
+ '''
+ Checks if build is valid on Koji
+
+ @param pkg: a Pkg instance
+ '''
+ if pkg.build is not None:
+ info = self.session.getBuild(int(pkg.build))
+ if info:
+ return True
+ return False
+
+
+ def is_pkg_spec_tag_valid(self, pkg):
+ '''
+ Checks if tag is valid on Koji
+
+ @type pkg: KojiPkgSpec
+ @param pkg: a package specification
+ '''
+ if pkg.tag is not None:
+ tag = self.session.getTag(pkg.tag)
+ if tag:
+ return True
+ return False
+
+
+ def get_pkg_rpm_info(self, pkg, arch=None):
+ '''
+ Returns a list of infomation on the RPM packages found on koji
+
+ @type pkg: KojiPkgSpec
+ @param pkg: a package specification
+ @type arch: string
+ @param arch: packages built for this architecture, but also including
+ architecture independent (noarch) packages
+ '''
if arch is None:
arch = utils.get_arch()
+ rpms = []
+ info = self.get_pkg_info(pkg)
+ if info:
+ rpms = self.session.listRPMs(buildID=info['id'],
+ arches=[arch, 'noarch'])
+ if pkg.subpackages:
+ rpms = [d for d in rpms if d['name'] in pkg.subpackages]
+ return rpms
+
+
+ def get_pkg_rpm_names(self, pkg, arch=None):
+ '''
+ Gets the names for the RPM packages specified in pkg
+
+ @type pkg: KojiPkgSpec
+ @param pkg: a package specification
+ @type arch: string
+ @param arch: packages built for this architecture, but also including
+ architecture independent (noarch) packages
+ '''
+ if arch is None:
+ arch = utils.get_arch()
+ rpms = self.get_pkg_rpm_info(pkg, arch)
+ return [rpm['name'] for rpm in rpms]
+
- rpms = self.session.listRPMs(buildID=info['id'],
- arches=arch)
- if not rpms:
- raise ValueError("No %s packages available for %s" %
- arch, koji.buildLabel(info))
+ def get_pkg_rpm_file_names(self, pkg, arch=None):
+ '''
+ Gets the file names for the RPM packages specified in pkg
- rpm_paths = []
+ @type pkg: KojiPkgSpec
+ @param pkg: a package specification
+ @type arch: string
+ @param arch: packages built for this architecture, but also including
+ architecture independent (noarch) packages
+ '''
+ if arch is None:
+ arch = utils.get_arch()
+ rpm_names = []
+ rpms = self.get_pkg_rpm_info(pkg, arch)
+ for rpm in rpms:
+ arch_rpm_name = koji.pathinfo.rpm(rpm)
+ rpm_name = os.path.basename(arch_rpm_name)
+ rpm_names.append(rpm_name)
+ return rpm_names
+
+
+ def get_pkg_urls(self, pkg, arch=None):
+ '''
+ Gets the urls for the packages specified in pkg
+
+ @type pkg: KojiPkgSpec
+ @param pkg: a package specification
+ @type arch: string
+ @param arch: packages built for this architecture, but also including
+ architecture independent (noarch) packages
+ '''
+ info = self.get_pkg_info(pkg)
+ rpms = self.get_pkg_rpm_info(pkg, arch)
+ rpm_urls = []
for rpm in rpms:
rpm_name = koji.pathinfo.rpm(rpm)
- url = ("%s/%s/%s/%s/%s" % (self.koji_options['pkgurl'],
+ url = ("%s/%s/%s/%s/%s" % (self.config_options['pkgurl'],
info['package_name'],
info['version'], info['release'],
rpm_name))
- if rfilter:
- filter_regexp = re.compile(rfilter, re.IGNORECASE)
- if filter_regexp.match(os.path.basename(rpm_name)):
- download = True
- else:
- download = False
+ rpm_urls.append(url)
+ return rpm_urls
+
+
+ def get_pkgs(self, pkg, dst_dir, arch=None):
+ '''
+ Download the packages
+
+ @type pkg: KojiPkgSpec
+ @param pkg: a package specification
+ @type dst_dir: string
+ @param dst_dir: the destination directory, where the downloaded
+ packages will be saved on
+ @type arch: string
+ @param arch: packages built for this architecture, but also including
+ architecture independent (noarch) packages
+ '''
+ rpm_urls = self.get_pkg_urls(pkg, arch)
+ for url in rpm_urls:
+ utils.get_file(url,
+ os.path.join(dst_dir, os.path.basename(url)))
+
+
+DEFAULT_KOJI_TAG = None
+def set_default_koji_tag(tag):
+ '''
+ Sets the default tag that will be used
+ '''
+ global DEFAULT_KOJI_TAG
+ DEFAULT_KOJI_TAG = tag
+
+
+def get_default_koji_tag():
+ return DEFAULT_KOJI_TAG
+
+
+class KojiPkgSpec:
+ '''
+ A package specification syntax parser for Koji
+
+ This holds information on either tag or build, and packages to be fetched
+ from koji and possibly installed (features external do this class).
+
+ New objects can be created either by providing information in the textual
+ format or by using the actual parameters for tag, build, package and sub-
+ packages. The textual format is useful for command line interfaces and
+ configuration files, while using parameters is better for using this in
+ a programatic fashion.
+
+ The following sets of examples are interchangeable. Specifying all packages
+ part of build number 1000:
+
+ >>> from kvm_utils import KojiPkgSpec
+ >>> pkg = KojiPkgSpec('1000')
+
+ >>> pkg = KojiPkgSpec(build=1000)
+
+ Specifying only a subset of packages of build number 1000:
+
+ >>> pkg = KojiPkgSpec('1000:kernel,kernel-devel')
+
+ >>> pkg = KojiPkgSpec(build=1000,
+ subpackages=['kernel', 'kernel-devel'])
+
+ Specifying the latest build for the 'kernel' package tagged with 'dist-f14':
+
+ >>> pkg = KojiPkgSpec('dist-f14:kernel')
+
+ >>> pkg = KojiPkgSpec(tag='dist-f14', package='kernel')
+
+ Specifying the 'kernel' package using the default tag:
+
+ >>> kvm_utils.set_default_koji_tag('dist-f14')
+ >>> pkg = KojiPkgSpec('kernel')
+
+ >>> pkg = KojiPkgSpec(package='kernel')
+
+ Specifying the 'kernel' package using the default tag:
+
+ >>> kvm_utils.set_default_koji_tag('dist-f14')
+ >>> pkg = KojiPkgSpec('kernel')
+
+ >>> pkg = KojiPkgSpec(package='kernel')
+
+ If you do not specify a default tag, and give a package name without an
+ explicit tag, your package specification is considered invalid:
+
+ >>> print kvm_utils.get_default_koji_tag()
+ None
+ >>> print kvm_utils.KojiPkgSpec('kernel').is_valid()
+ False
+
+ >>> print kvm_utils.KojiPkgSpec(package='kernel').is_valid()
+ False
+ '''
+
+ SEP = ':'
+
+ def __init__(self, text='', tag=None, build=None,
+ package=None, subpackages=[]):
+ '''
+ Instantiates a new KojiPkgSpec object
+
+ @type text: string
+ @param text: a textual representation of a package on Koji that
+ will be parsed
+ @type tag: string
+ @param tag: a koji tag, example: Fedora-14-RELEASE
+ (see U{http://fedoraproject.org/wiki/Koji#Tags_and_Targets})
+ @type build: number
+ @param build: a koji build, example: 1001
+ (see U{http://fedoraproject.org/wiki/Koji#Koji_Architecture})
+ @type package: string
+ @param package: a koji package, example: python
+ (see U{http://fedoraproject.org/wiki/Koji#Koji_Architecture})
+ @type subpackages: list of strings
+ @param subpackages: a list of package names, usually a subset of
+ the RPM packages generated by a given build
+ '''
+
+ # Set to None to indicate 'not set' (and be able to use 'is')
+ self.tag = None
+ self.build = None
+ self.package = None
+ self.subpackages = []
+
+ self.default_tag = None
+
+ # Textual representation takes precedence (most common use case)
+ if text:
+ self.parse(text)
+ else:
+ self.tag = tag
+ self.build = build
+ self.package = package
+ self.subpackages = subpackages
+
+ # Set the default tag, if set, as a fallback
+ if not self.build and not self.tag:
+ default_tag = get_default_koji_tag()
+ if default_tag is not None:
+ self.tag = default_tag
+
+
+ def parse(self, text):
+ '''
+ Parses a textual representation of a package specification
+
+ @type text: string
+ @param text: textual representation of a package in koji
+ '''
+ parts = text.count(self.SEP) + 1
+ if parts == 1:
+ if text.isdigit():
+ self.build = text
+ else:
+ self.package = text
+ elif parts == 2:
+ part1, part2 = text.split(self.SEP)
+ if part1.isdigit():
+ self.build = part1
+ self.subpackages = part2.split(',')
+ else:
+ self.tag = part1
+ self.package = part2
+ elif parts >= 3:
+ # Instead of erroring on more arguments, we simply ignore them
+ # This makes the parser suitable for future syntax additions, such
+ # as specifying the package architecture
+ part1, part2, part3 = text.split(self.SEP)[0:3]
+ self.tag = part1
+ self.package = part2
+ self.subpackages = part3.split(',')
+
+
+ def _is_invalid_neither_tag_or_build(self):
+ '''
+ Checks if this package is invalid due to not having either a valid
+ tag or build set, that is, both are empty.
+
+ @returns: True if this is invalid and False if it's valid
+ '''
+ return (self.tag is None and self.build is None)
+
+
+ def _is_invalid_package_but_no_tag(self):
+ '''
+ Checks if this package is invalid due to having a package name set
+ but tag or build set, that is, both are empty.
+
+ @returns: True if this is invalid and False if it's valid
+ '''
+ return (self.package and not self.tag)
+
+
+ def _is_invalid_subpackages_but_no_main_package(self):
+ '''
+ Checks if this package is invalid due to having a tag set (this is Ok)
+ but specifying subpackage names without specifying the main package
+ name.
+
+ Specifying subpackages without a main package name is only valid when
+ a build is used instead of a tag.
+
+ @returns: True if this is invalid and False if it's valid
+ '''
+ return (self.tag and self.subpackages and not self.package)
+
+
+ def is_valid(self):
+ '''
+ Checks if this package specification is valid.
+
+ Being valid means that it has enough and not conflicting information.
+ It does not validate that the packages specified actually existe on
+ the Koji server.
+
+ @returns: True or False
+ '''
+ if self._is_invalid_neither_tag_or_build():
+ return False
+ elif self._is_invalid_package_but_no_tag():
+ return False
+ elif self._is_invalid_subpackages_but_no_main_package():
+ return False
+
+ return True
+
+
+ def describe_invalid(self):
+ '''
+ Describes why this is not valid, in a human friendly way
+ '''
+ if self._is_invalid_neither_tag_or_build():
+ return 'neither a tag or build are set, and of them should be set'
+ elif self._is_invalid_package_but_no_tag():
+ return 'package name specified but no tag is set'
+ elif self._is_invalid_subpackages_but_no_main_package():
+ return 'subpackages specified but no main package is set'
+
+ return 'unkwown reason, seems to be valid'
+
+
+ def describe(self):
+ '''
+ Describe this package specification, in a human friendly way
+
+ @returns: package specification description
+ '''
+ if self.is_valid():
+ description = ''
+ if not self.subpackages:
+ description += 'all subpackages from %s ' % self.package
+ else:
+ description += ('only subpackage(s) %s from package %s ' %
+ (', '.join(self.subpackages), self.package))
+
+ if self.build:
+ description += 'from build %s' % self.build
+ elif self.tag:
+ description += 'tagged with %s' % self.tag
else:
- download = True
+ raise ValueError, 'neither build or tag is set'
+
+ return description
+ else:
+ return ('Invalid package specification: %s' %
+ self.describe_invalid())
- if download:
- r = utils.get_file(url,
- os.path.join(dst_dir, os.path.basename(url)))
- rpm_paths.append(r)
- return rpm_paths
+ def __repr__(self):
+ return ("<KojiPkgSpec tag=%s build=%s pkg=%s subpkgs=%s>" %
+ (self.tag, self.build, self.package,
+ ", ".join(self.subpackages)))
def umount(src, mount_point, type):
@@ -1726,3 +2245,73 @@ def mount(src, mount_point, type, perm="rw"):
logging.error("Can't find mounted NFS share - /etc/mtab contents \n%s",
file("/etc/mtab").read())
return False
+
+
+def install_host_kernel(job, params):
+ """
+ Install a host kernel, given the appropriate params.
+
+ @param job: Job object.
+ @param params: Dict with host kernel install params.
+ """
+ install_type = params.get('host_kernel_install_type')
+
+ rpm_url = params.get('host_kernel_rpm_url')
+
+ koji_cmd = params.get('host_kernel_koji_cmd')
+ koji_build = params.get('host_kernel_koji_build')
+ koji_tag = params.get('host_kernel_koji_tag')
+
+ git_repo = params.get('host_kernel_git_repo')
+ git_branch = params.get('host_kernel_git_branch')
+ git_commit = params.get('host_kernel_git_commit')
+ patch_list = params.get('host_kernel_patch_list')
+ if patch_list:
+ patch_list = patch_list.split()
+ kernel_config = params.get('host_kernel_config')
+
+ if install_type == 'rpm':
+ logging.info('Installing host kernel through rpm')
+ dst = os.path.join("/tmp", os.path.basename(rpm_url))
+ k = utils.get_file(rpm_url, dst)
+ host_kernel = job.kernel(k)
+ host_kernel.install(install_vmlinux=False)
+ host_kernel.boot()
+
+ elif install_type in ['koji', 'brew']:
+ k_deps = KojiPkgSpec(tag=koji_tag, package='kernel',
+ subpackages=['kernel-devel', 'kernel-firmware'])
+ k = KojiPkgSpec(tag=koji_tag, package='kernel',
+ subpackages=['kernel'])
+
+ c = KojiClient(koji_cmd)
+ logging.info('Fetching kernel dependencies (-devel, -firmware)')
+ c.get_pkgs(k_deps, job.tmpdir)
+ logging.info('Installing kernel dependencies (-devel, -firmware) '
+ 'through %s', install_type)
+ k_deps_rpm_file_names = [os.path.join(job.tmpdir, rpm_file_name) for
+ rpm_file_name in c.get_pkg_rpm_file_names(k_deps)]
+ utils.run('rpm -U --force %s' % " ".join(k_deps_rpm_file_names))
+
+ c.get_pkgs(k, job.tmpdir)
+ k_rpm = os.path.join(job.tmpdir,
+ c.get_pkg_rpm_file_names(k)[0])
+ host_kernel = job.kernel(k_rpm)
+ host_kernel.install(install_vmlinux=False)
+ host_kernel.boot()
+
+ elif install_type == 'git':
+ logging.info('Chose to install host kernel through git, proceeding')
+ repodir = os.path.join("/tmp", 'kernel_src')
+ r = get_git_branch(git_repo, git_branch, repodir, git_commit)
+ host_kernel = job.kernel(r)
+ if patch_list:
+ host_kernel.patch(patch_list)
+ host_kernel.config(kernel_config)
+ host_kernel.build()
+ host_kernel.install()
+ host_kernel.boot()
+
+ else:
+ logging.info('Chose %s, using the current kernel for the host',
+ install_type)

Powered by Google App Engine
This is Rietveld 408576698