support multi file image output

This commit is contained in:
Jay Greguske 2011-05-12 17:11:30 -04:00 committed by Mike McLean
parent ad567ff778
commit 0ca380a5b1
2 changed files with 103 additions and 97 deletions

View file

@ -1788,9 +1788,7 @@ class BuildApplianceTask(BuildImageTask):
# import the image (move it too)
if not opts.get('scratch'):
results['id'] = self.session.host.importImage(create_task_id,
build_id, results)
self.logger.info('image import complete: id %s' % results['id'])
self.session.host.importImage(create_task_id, build_id, results)
except (SystemExit,ServerExit,KeyboardInterrupt):
#we do not trap these
raise
@ -1811,15 +1809,16 @@ class BuildApplianceTask(BuildImageTask):
# report results
if opts.get('scratch'):
return 'Scratch appliance image created: %s' % \
os.path.join(koji.pathinfo.work(),
koji.pathinfo.taskrelpath(create_task_id),
results['name'])
respath = os.path.join(koji.pathinfo.work(),
koji.pathinfo.taskrelpath(create_task_id))
report = 'scratch'
else:
return 'Created appliance image: %s' % \
os.path.join(koji.pathinfo.imageFinalPath(),
koji.pathinfo.applianceRelPath(results['id']),
results['name'])
respath = os.path.join(koji.pathinfo.imageFinalPath(),
koji.pathinfo.applianceRelPath(build_id))
report = ''
files = [os.path.join(respath, f) for f in results['files']]
report += 'appliance build results:\n%s' % '\n'.join(files)
return report
class BuildLiveCDTask(BuildImageTask):
Methods = ['livecd']
@ -1856,9 +1855,7 @@ class BuildLiveCDTask(BuildImageTask):
# import it (and move)
if not opts.get('scratch'):
results['id'] = self.session.host.importImage(create_task_id,
build_id, results)
self.logger.info('image import complete: id %s' % results['id'])
self.session.host.importImage(create_task_id, build_id, results)
except (SystemExit,ServerExit,KeyboardInterrupt):
#we do not trap these
raise
@ -1882,12 +1879,12 @@ class BuildLiveCDTask(BuildImageTask):
return 'Scratch image created: %s' % \
os.path.join(koji.pathinfo.work(),
koji.pathinfo.taskrelpath(create_task_id),
results['name'])
results['files'][0])
else:
return 'Created image: %s' % \
os.path.join(koji.pathinfo.imageFinalPath(),
koji.pathinfo.livecdRelPath(results['id']),
results['name'])
koji.pathinfo.livecdRelPath(build_id),
results['files'][0])
# A generic task for building cd or disk images. Other handlers should inherit
# this.
@ -1957,7 +1954,38 @@ class ImageTask(BaseTaskHandler):
self.uploadFile(kspath) # upload the original ks file
return kspath # full absolute path to the file in the chroot
def prepareKickstart(self, repo_info, target_info, arch, broot, kspath):
def readKickstart(self, kspath, opts):
"""
Read a kickstart file and save the ks object as a task member.
@args:
kspath: path to a kickstart file
@returns: None
"""
# XXX: If the ks file came from a local path and has %include
# macros, *-creator will fail because the included
# kickstarts were not copied into the chroot. For now we
# require users to flatten their kickstart file if submitting
# the task with a local path.
#
# Note that if an SCM URL was used instead, %include macros
# may not be a problem if the included kickstarts are present
# in the repository we checked out.
if opts.get('ksversion'):
version = ksparser.makeVersion(ksparser.stringToVersion(opts['ksversion']))
else:
version = ksparser.makeVersion()
self.ks = ksparser.KickstartParser(version)
try:
self.ks.readKickstart(kspath)
except IOError, e:
raise koji.LiveCDError("Failed to read kickstart file "
"'%s' : %s" % (kspath, e))
except kserrors.KickstartError, e:
raise koji.LiveCDError("Failed to parse kickstart file "
"'%s' : %s" % (kspath, e))
def prepareKickstart(self, repo_info, target_info, arch, broot, opts):
"""
Process the ks file to be used for controlled image generation. This
method also uploads the modified kickstart file to the task output
@ -1972,42 +2000,17 @@ class ImageTask(BaseTaskHandler):
@returns:
absolute path to a processed kickstart file within the buildroot
"""
# XXX: If the ks file came from a local path and has %include
# macros, *-creator will fail because the included
# kickstarts were not copied into the chroot. For now we
# require users to flatten their kickstart file if submitting
# the task with a local path.
#
# Note that if an SCM URL was used instead, %include macros
# may not be a problem if the included kickstarts are present
# in the repository we checked out.
# Now we do some kickstart manipulation. If the user passed in a repo
# url with --repo, then we substitute that in for the repo(s) specified
# in the kickstart file. If --repo wasn't specified, then we use the
# repo associated with the target passed in initially.
if self.opts.get('ksversion'):
version = ksparser.makeVersion(ksparser.stringToVersion(self.opts['ksversion']))
else:
version = ksparser.makeVersion()
ks = ksparser.KickstartParser(version)
repo_class = kscontrol.dataMap[ks.version]['RepoData']
try:
ks.readKickstart(kspath)
except IOError, e:
raise koji.LiveCDError("Failed to read kickstart file "
"'%s' : %s" % (kspath, e))
except kserrors.KickstartError, e:
raise koji.LiveCDError("Failed to parse kickstart file "
"'%s' : %s" % (kspath, e))
ks.handler.repo.repoList = [] # delete whatever the ks file told us
if self.opts.get('repo'):
user_repos = self.opts['repo'].split(',')
repo_class = kscontrol.dataMap[self.ks.version]['RepoData']
self.ks.handler.repo.repoList = [] # delete whatever the ks file told us
if opts.get('repo'):
user_repos = opts['repo'].split(',')
index = 0
for user_repo in user_repos:
ks.handler.repo.repoList.append(repo_class(baseurl=user_repo, name='koji-override-%i' % index))
self.ks.handler.repo.repoList.append(repo_class(baseurl=user_repo, name='koji-override-%i' % index))
index += 1
else:
path_info = koji.PathInfo(topdir=self.options.topurl)
@ -2015,7 +2018,7 @@ class ImageTask(BaseTaskHandler):
target_info['build_tag_name'])
baseurl = '%s/%s' % (repopath, arch)
self.logger.debug('BASEURL: %s' % baseurl)
ks.handler.repo.repoList.append(repo_class(baseurl=baseurl, name='koji-%s-%i' % (target_info['build_tag_name'], repo_info['id'])))
self.ks.handler.repo.repoList.append(repo_class(baseurl=baseurl, name='koji-%s-%i' % (target_info['build_tag_name'], repo_info['id'])))
# Write out the new ks file. Note that things may not be in the same
# order and comments in the original ks file may be lost.
@ -2023,7 +2026,7 @@ class ImageTask(BaseTaskHandler):
(target_info['build_tag_name'], self.id))
kojikspath = os.path.join(broot.rootdir(), kskoji[1:])
outfile = open(kojikspath, 'w')
outfile.write(str(ks.handler))
outfile.write(str(self.ks.handler))
outfile.close()
# put the new ksfile in the output directory
@ -2064,6 +2067,16 @@ class ApplianceTask(ImageTask):
Methods = ['createAppliance']
_taskWeight = 1.5
def getRootDevice(self):
"""
Return the device name for the / partition, as specified in the
kickstart file. Appliances should have this defined.
"""
for part in self.ks.handler.partition.partitions:
if part.mountpoint == '/':
return part.disk
raise koji.ApplianceError, 'kickstart lacks a "/" mountpoint'
def handler(self, name, version, release, arch, target_info, build_tag, repo_info, ksfile, opts=None):
if opts == None:
@ -2071,8 +2084,8 @@ class ApplianceTask(ImageTask):
broot = self.makeImgBuildRoot(build_tag, repo_info, arch,
'appliance-build')
kspath = self.fetchKickstart(broot, ksfile)
kskoji = self.prepareKickstart(repo_info, target_info, arch, broot,
kspath)
self.readKickstart(kspath, opts)
kskoji = self.prepareKickstart(repo_info, target_info, arch, broot, opts)
# Figure out appliance-creator arguments, let it fail if something
# is wrong.
odir = 'app-output'
@ -2105,28 +2118,19 @@ class ApplianceTask(ImageTask):
results.append(os.path.join(broot.rootdir(), 'tmp',
directory, f))
self.logger.debug('output: %s' % results)
# TODO: allow for multiple disk images to be generated
if len(results) > 2:
raise koji.ApplianceError, \
"Only one image is allowed for output! found: %s" % results
app_path = None
if len(results) == 0:
raise koji.ApplianceError, "Could not find image build results!"
imgdata = {'arch': arch, 'rootdev': self.getRootDevice()}
imgdata['files'] = []
for ofile in results:
if ofile.endswith('.xml'):
self.uploadFile(ofile)
else:
app_path = ofile
if app_path == None:
raise koji.ApplianceError, "Could not find appliance image!"
self.uploadFile(app_path)
app_file = os.path.basename(app_path)
self.uploadFile(ofile)
imgdata['files'].append(os.path.basename(ofile))
# TODO: get file manifest from the appliance
imgdata = {'arch': arch, 'name': app_file}
if not opts.get('scratch'):
hdrlist = self.getImagePackages(
os.path.join(broot.rootdir(), cachedir[1:]))
hdrlist = self.getImagePackages(os.path.join(broot.rootdir(),
cachedir[1:]))
broot.markExternalRPMs(hdrlist)
imgdata['rpmlist'] = hdrlist
@ -2217,8 +2221,8 @@ class LiveCDTask(ImageTask):
broot = self.makeImgBuildRoot(build_tag, repo_info, arch,
'livecd-build')
kspath = self.fetchKickstart(broot, ksfile)
kskoji = self.prepareKickstart(repo_info, target_info, arch, broot,
kspath)
self.readKickstart(kspath, opts)
kskoji = self.prepareKickstart(repo_info, target_info, arch, broot, opts)
cachedir = '/tmp/koji-livecd' # arbitrary paths in chroot
livecd_log = '/tmp/livecd.log'
@ -2262,7 +2266,7 @@ class LiveCDTask(ImageTask):
self.uploadFile(manifest)
self.uploadFile(isosrc, remoteName=isoname)
imgdata = {'arch': arch, 'name': isoname}
imgdata = {'arch': arch, 'files': [isoname], 'rootdev': None}
if not opts.get('scratch'):
hdrlist = self.getImagePackages(os.path.join(broot.rootdir(),
cachedir[1:]))

View file

@ -4685,7 +4685,7 @@ def import_archive(filepath, buildinfo, type, typeInfo, buildroot_id=None):
filepath: full path to the archive file
buildinfo: dict of information about the build to associate the archive with (as returned by getBuild())
type: type of the archive being imported. Currently supported archive types: maven, win
type: type of the archive being imported. Currently supported archive types: maven, win, image
typeInfo: dict of type-specific information
buildroot_id: the id of the buildroot the archive was built in (may be null)
"""
@ -4779,6 +4779,7 @@ def import_archive(filepath, buildinfo, type, typeInfo, buildroot_id=None):
archiveinfo = get_archive(archive_id, strict=True)
koji.plugin.run_callbacks('postImport', type='archive', archive=archiveinfo, build=buildinfo,
build_type=type, filepath=filepath)
return archiveinfo
def _import_archive_file(filepath, destdir):
"""
@ -6583,33 +6584,33 @@ def importImageInternal(task_id, build_id, imgdata):
imgdata is:
arch - the arch if the image
name - file name of the image
files - files associated with the image (appliances have multiple files)
rpmlist - the list of RPM NVRs installed into the image
rootdev - root device image that mounts "/"; the archive id of this image
is what is referenced in the imageinfo_listing table. livecds set this
to None.
"""
host = Host()
host.verify()
task = Task(task_id)
# get these consistent...
relpath = os.path.join(koji.pathinfo.taskrelpath(task_id), imgdata['name'])
fullpath = os.path.join(koji.pathinfo.task(task_id), imgdata['name'])
koji.plugin.run_callbacks('preImport', type='image', image=imgdata,
fullpath=fullpath)
koji.plugin.run_callbacks('preImport', type='image', image=imgdata)
# import the build output
archivetype = get_archive_type(filename=relpath)
logger.info('image type is: %s' % archivetype)
if not archivetype:
raise koji.BuildError, 'Unsupported image type'
# TODO: import xml file too
build_info = get_build(build_id, strict=True)
imgdata['relpath'] = os.path.dirname(relpath)
logger.info('importing image as an archive')
import_archive(fullpath, build_info, 'image', imgdata)
imgdata['relpath'] = koji.pathinfo.taskrelpath(task_id)
archives = []
for imgfile in imgdata['files']:
relpath = os.path.join(koji.pathinfo.taskrelpath(task_id), imgfile)
fullpath = os.path.join(koji.pathinfo.task(task_id), imgfile)
archivetype = get_archive_type(filename=relpath)
logger.debug('image type we are importing is: %s' % archivetype)
if not archivetype:
raise koji.BuildError, 'Unsupported image type'
archives.append(import_archive(fullpath, build_info, 'image', imgdata))
# track the contents of the image
rpm_ids = []
logger.info('figuring out where rpms came from')
for an_rpm in imgdata['rpmlist']:
location = an_rpm.get('location')
if location:
@ -6618,16 +6619,19 @@ def importImageInternal(task_id, build_id, imgdata):
data = get_rpm(an_rpm, strict=True)
rpm_ids.append(data['id'])
logger.info('getting imageID')
query = QueryProcessor(tables=('archiveinfo',), columns=('id',),
clauses=('build_id = %(build_id)i',),
values={'build_id': build_info['id']})
results = query.executeOne()
logger.info('imageID is %s' % results['id'])
archive_id = archives[0]['id'] # satisfies the livecd case
if imgdata.get('rootdev'):
# but we are importing an appliance
for a in archives:
if imgdata['rootdev'] in a['filename']:
archive_id = a['id']
break
logger.debug('root archive id is %s' % archive_id)
q = """INSERT INTO imageinfo_listing (image_id,rpm_id)
VALUES (%(image_id)i,%(rpm_id)i)"""
for rpm_id in rpm_ids:
_dml(q, {'image_id': results['id'], 'rpm_id': rpm_id})
_dml(q, {'image_id': archive_id, 'rpm_id': rpm_id})
logger.info('updated imageinfo_listing')
# update build table
@ -6644,8 +6648,6 @@ def importImageInternal(task_id, build_id, imgdata):
koji.plugin.run_callbacks('postImport', type='image', image=imgdata,
fullpath=fullpath)
return results['id']
#
# XMLRPC Methods
#