2016-01-28 15:03:20 +00:00
|
|
|
# -*- coding: utf-8 -*-
|
|
|
|
|
|
|
|
import os
|
|
|
|
import time
|
|
|
|
from kobo import shortcuts
|
|
|
|
|
2017-08-18 07:33:51 +00:00
|
|
|
from pungi.util import makedirs, get_mtime, get_file_size, failable
|
2017-03-27 21:53:08 +00:00
|
|
|
from pungi.util import translate_path, get_repo_urls
|
2016-11-18 20:33:03 +00:00
|
|
|
from pungi.phases.base import ConfigGuardedPhase, ImageConfigMixin, PhaseLoggerMixin
|
2016-01-28 15:03:20 +00:00
|
|
|
from pungi.linker import Linker
|
|
|
|
from pungi.wrappers.kojiwrapper import KojiWrapper
|
|
|
|
from kobo.threads import ThreadPool, WorkerThread
|
|
|
|
from productmd.images import Image
|
|
|
|
|
|
|
|
|
2016-11-18 20:33:03 +00:00
|
|
|
class LiveMediaPhase(PhaseLoggerMixin, ImageConfigMixin, ConfigGuardedPhase):
|
2016-01-28 15:03:20 +00:00
|
|
|
"""class for wrapping up koji spin-livemedia"""
|
|
|
|
name = 'live_media'
|
|
|
|
|
|
|
|
def __init__(self, compose):
|
|
|
|
super(LiveMediaPhase, self).__init__(compose)
|
2016-11-18 20:33:03 +00:00
|
|
|
self.pool = ThreadPool(logger=self.logger)
|
2016-01-28 15:03:20 +00:00
|
|
|
|
|
|
|
def _get_repos(self, image_conf, variant):
|
|
|
|
"""
|
2017-03-27 21:53:08 +00:00
|
|
|
Get a list of repo urls. First included are those explicitly listed in config,
|
|
|
|
followed by repo for current variant if it's not present in the list.
|
2016-01-28 15:03:20 +00:00
|
|
|
"""
|
2017-03-27 21:53:08 +00:00
|
|
|
repos = shortcuts.force_list(image_conf.get('repo', []))
|
2016-01-28 15:03:20 +00:00
|
|
|
|
2016-02-18 17:02:46 +00:00
|
|
|
if not variant.is_empty:
|
2017-03-27 21:53:08 +00:00
|
|
|
if variant.uid not in repos:
|
|
|
|
repos.append(variant.uid)
|
2016-01-28 15:03:20 +00:00
|
|
|
|
2017-03-27 21:53:08 +00:00
|
|
|
return get_repo_urls(self.compose, repos)
|
2016-01-28 15:03:20 +00:00
|
|
|
|
|
|
|
def _get_arches(self, image_conf, arches):
|
|
|
|
if 'arches' in image_conf:
|
|
|
|
arches = set(image_conf.get('arches', [])) & arches
|
|
|
|
return sorted(arches)
|
|
|
|
|
2016-02-11 09:21:56 +00:00
|
|
|
def _get_install_tree(self, image_conf, variant):
|
|
|
|
if 'install_tree_from' in image_conf:
|
|
|
|
variant_uid = image_conf['install_tree_from']
|
|
|
|
try:
|
2016-11-09 09:20:55 +00:00
|
|
|
variant = self.compose.all_variants[variant_uid]
|
2016-02-11 09:21:56 +00:00
|
|
|
except KeyError:
|
|
|
|
raise RuntimeError(
|
|
|
|
'There is no variant %s to get repo from when building live media for %s.'
|
|
|
|
% (variant_uid, variant.uid))
|
|
|
|
return translate_path(
|
|
|
|
self.compose,
|
2016-02-19 08:01:45 +00:00
|
|
|
self.compose.paths.compose.os_tree('$basearch', variant, create_dir=False)
|
2016-02-11 09:21:56 +00:00
|
|
|
)
|
|
|
|
|
2016-01-28 15:03:20 +00:00
|
|
|
def run(self):
|
|
|
|
for variant in self.compose.get_variants():
|
|
|
|
arches = set([x for x in variant.arches if x != 'src'])
|
2017-08-18 07:33:51 +00:00
|
|
|
for image_conf in self.get_config_block(variant):
|
2016-03-10 18:53:40 +00:00
|
|
|
subvariant = image_conf.get('subvariant', variant.uid)
|
|
|
|
name = image_conf.get(
|
|
|
|
'name', "%s-%s-Live" % (self.compose.ci_base.release.short, subvariant))
|
2016-01-28 15:03:20 +00:00
|
|
|
config = {
|
2016-04-14 12:23:42 +00:00
|
|
|
'target': self.get_config(image_conf, 'target'),
|
2016-01-28 15:03:20 +00:00
|
|
|
'arches': self._get_arches(image_conf, arches),
|
2016-02-11 09:39:32 +00:00
|
|
|
'ksfile': image_conf['kickstart'],
|
2016-04-14 12:23:42 +00:00
|
|
|
'ksurl': self.get_ksurl(image_conf),
|
2016-01-28 15:03:20 +00:00
|
|
|
'ksversion': image_conf.get('ksversion'),
|
|
|
|
'scratch': image_conf.get('scratch', False),
|
2016-04-14 12:23:42 +00:00
|
|
|
'release': self.get_release(image_conf),
|
2016-01-28 15:03:20 +00:00
|
|
|
'skip_tag': image_conf.get('skip_tag'),
|
2016-03-10 18:53:40 +00:00
|
|
|
'name': name,
|
|
|
|
'subvariant': subvariant,
|
2016-01-28 15:03:20 +00:00
|
|
|
'title': image_conf.get('title'),
|
|
|
|
'repo': self._get_repos(image_conf, variant),
|
2016-02-11 09:21:56 +00:00
|
|
|
'install_tree': self._get_install_tree(image_conf, variant),
|
2016-08-30 07:51:36 +00:00
|
|
|
'version': self.get_version(image_conf),
|
2016-06-24 07:44:40 +00:00
|
|
|
'failable_arches': image_conf.get('failable', []),
|
2016-01-28 15:03:20 +00:00
|
|
|
}
|
2016-11-28 13:25:35 +00:00
|
|
|
if config['failable_arches'] == ['*']:
|
|
|
|
config['failable_arches'] = config['arches']
|
2016-01-28 15:03:20 +00:00
|
|
|
self.pool.add(LiveMediaThread(self.pool))
|
|
|
|
self.pool.queue_put((self.compose, variant, config))
|
|
|
|
|
|
|
|
self.pool.start()
|
|
|
|
|
|
|
|
|
|
|
|
class LiveMediaThread(WorkerThread):
|
|
|
|
def process(self, item, num):
|
|
|
|
compose, variant, config = item
|
2016-03-17 08:02:17 +00:00
|
|
|
subvariant = config.pop('subvariant')
|
2016-06-24 07:44:40 +00:00
|
|
|
self.failable_arches = config.pop('failable_arches')
|
2016-01-28 15:03:20 +00:00
|
|
|
self.num = num
|
2016-11-28 13:25:35 +00:00
|
|
|
can_fail = set(self.failable_arches) == set(config['arches'])
|
|
|
|
with failable(compose, can_fail, variant, '*', 'live-media', subvariant,
|
2016-11-18 20:33:03 +00:00
|
|
|
logger=self.pool._logger):
|
2016-03-17 08:02:17 +00:00
|
|
|
self.worker(compose, variant, subvariant, config)
|
2016-01-28 15:03:20 +00:00
|
|
|
|
2016-03-17 08:02:17 +00:00
|
|
|
def _get_log_file(self, compose, variant, subvariant, config):
|
2016-01-28 15:03:20 +00:00
|
|
|
arches = '-'.join(config['arches'])
|
2016-03-17 08:02:17 +00:00
|
|
|
return compose.paths.log.log_file(arches, 'livemedia-%s-%s'
|
|
|
|
% (variant.uid, subvariant))
|
2016-01-28 15:03:20 +00:00
|
|
|
|
|
|
|
def _run_command(self, koji_wrapper, cmd, compose, log_file):
|
|
|
|
time.sleep(self.num * 3)
|
|
|
|
output = koji_wrapper.run_blocking_cmd(cmd, log_file=log_file)
|
|
|
|
self.pool.log_debug('live media outputs: %s' % (output))
|
|
|
|
if output['retcode'] != 0:
|
2016-11-18 20:33:03 +00:00
|
|
|
self.pool.log_error('Live media task failed.')
|
2016-01-28 15:03:20 +00:00
|
|
|
raise RuntimeError('Live media task failed: %s. See %s for more details.'
|
|
|
|
% (output['task_id'], log_file))
|
|
|
|
return output
|
|
|
|
|
2016-02-11 06:50:02 +00:00
|
|
|
def _get_cmd(self, koji_wrapper, config):
|
|
|
|
"""Replace `arches` (as list) with `arch` as a comma-separated string."""
|
|
|
|
copy = dict(config)
|
|
|
|
copy['arch'] = ','.join(copy.pop('arches', []))
|
2016-11-28 13:25:35 +00:00
|
|
|
copy['can_fail'] = self.failable_arches
|
2016-02-11 06:50:02 +00:00
|
|
|
return koji_wrapper.get_live_media_cmd(copy)
|
|
|
|
|
2016-03-17 08:02:17 +00:00
|
|
|
def worker(self, compose, variant, subvariant, config):
|
|
|
|
msg = ('Live media: %s (arches: %s, variant: %s, subvariant: %s)'
|
|
|
|
% (config['name'], ' '.join(config['arches']), variant.uid, subvariant))
|
2016-01-28 15:03:20 +00:00
|
|
|
self.pool.log_info('[BEGIN] %s' % msg)
|
|
|
|
|
|
|
|
koji_wrapper = KojiWrapper(compose.conf['koji_profile'])
|
2016-02-11 06:50:02 +00:00
|
|
|
cmd = self._get_cmd(koji_wrapper, config)
|
2016-01-28 15:03:20 +00:00
|
|
|
|
2016-03-17 08:02:17 +00:00
|
|
|
log_file = self._get_log_file(compose, variant, subvariant, config)
|
2016-01-28 15:03:20 +00:00
|
|
|
output = self._run_command(koji_wrapper, cmd, compose, log_file)
|
|
|
|
|
|
|
|
# collect results and update manifest
|
|
|
|
image_infos = []
|
|
|
|
|
|
|
|
paths = koji_wrapper.get_image_paths(output['task_id'])
|
|
|
|
|
2017-09-05 08:01:21 +00:00
|
|
|
for arch, paths in paths.items():
|
2016-01-28 15:03:20 +00:00
|
|
|
for path in paths:
|
|
|
|
if path.endswith('.iso'):
|
|
|
|
image_infos.append({'path': path, 'arch': arch})
|
|
|
|
|
2016-11-28 13:25:35 +00:00
|
|
|
if len(image_infos) < len(config['arches']) - len(self.failable_arches):
|
2016-01-28 15:03:20 +00:00
|
|
|
self.pool.log_error(
|
2016-11-28 13:25:35 +00:00
|
|
|
'Error in koji task %s. Expected to find at least one image '
|
|
|
|
'for each required arch (%s). Got %s.'
|
2016-01-28 15:03:20 +00:00
|
|
|
% (output['task_id'], len(config['arches']), len(image_infos)))
|
|
|
|
raise RuntimeError('Image count mismatch in task %s.' % output['task_id'])
|
|
|
|
|
2016-11-18 20:33:03 +00:00
|
|
|
linker = Linker(logger=self.pool._logger)
|
2016-08-22 14:08:25 +00:00
|
|
|
link_type = compose.conf["link_type"]
|
2016-01-28 15:03:20 +00:00
|
|
|
for image_info in image_infos:
|
2016-02-11 12:58:42 +00:00
|
|
|
image_dir = compose.paths.compose.iso_dir(image_info['arch'], variant)
|
2016-01-28 15:03:20 +00:00
|
|
|
makedirs(image_dir)
|
|
|
|
relative_image_dir = (
|
2016-02-11 12:58:42 +00:00
|
|
|
compose.paths.compose.iso_dir(image_info['arch'], variant, relative=True)
|
2016-01-28 15:03:20 +00:00
|
|
|
)
|
|
|
|
|
|
|
|
# let's not change filename of koji outputs
|
|
|
|
image_dest = os.path.join(image_dir, os.path.basename(image_info['path']))
|
|
|
|
linker.link(image_info['path'], image_dest, link_type=link_type)
|
|
|
|
|
|
|
|
# Update image manifest
|
|
|
|
img = Image(compose.im)
|
|
|
|
img.type = 'live'
|
|
|
|
img.format = 'iso'
|
|
|
|
img.path = os.path.join(relative_image_dir, os.path.basename(image_dest))
|
2016-02-11 14:15:36 +00:00
|
|
|
img.mtime = get_mtime(image_dest)
|
|
|
|
img.size = get_file_size(image_dest)
|
2016-01-28 15:03:20 +00:00
|
|
|
img.arch = image_info['arch']
|
|
|
|
img.disc_number = 1 # We don't expect multiple disks
|
|
|
|
img.disc_count = 1
|
|
|
|
img.bootable = True
|
2016-03-10 18:53:40 +00:00
|
|
|
img.subvariant = subvariant
|
2016-06-24 07:44:40 +00:00
|
|
|
setattr(img, 'can_fail', bool(self.failable_arches))
|
2016-05-03 14:31:20 +00:00
|
|
|
setattr(img, 'deliverable', 'live-media')
|
2016-01-28 15:03:20 +00:00
|
|
|
compose.im.add(variant=variant.uid, arch=image_info['arch'], image=img)
|
|
|
|
|
2017-03-15 15:16:47 +00:00
|
|
|
self.pool.log_info('[DONE ] %s (task id: %s)' % (msg, output['task_id']))
|