def extractMediaTemplate(self, topdir): tmpRoot = tempfile.mkdtemp(dir=constants.tmpDir) try: client = self.getConaryClient(\ tmpRoot, getArchFlavor(self.baseFlavor).freeze()) log.info("extracting ad-hoc content from " \ "media-template=%s" % client.cfg.installLabelPath[0].asString()) uJob = self._getUpdateJob(client, "media-template") if uJob: client.applyUpdate(uJob, callback = self.callback) log.info("success: copying media template data to unified tree") # copy content into unified tree root. add recurse and no-deref # flags to command. following symlinks is really bad in this case. oldTemplateDir = os.path.join(tmpRoot, 'usr', 'lib', 'media-template') if os.path.exists(oldTemplateDir): call('cp', '-R', '--no-dereference', oldTemplateDir, topdir) for tDir in ('all', 'disc1'): srcDir = os.path.join(tmpRoot, tDir) destDir = os.path.join(topdir, 'media-template2') if os.path.exists(srcDir): util.mkdirChain(destDir) call('cp', '-R', '--no-dereference', srcDir, destDir) else: log.info("media-template not found on repository") finally: util.rmtree(tmpRoot, ignore_errors = True)
def extractPublicKeys(self, keyDir, topdir, csdir): self.status('Extracting Public Keys') homeDir = tempfile.mkdtemp(dir = constants.tmpDir) tmpRoot = tempfile.mkdtemp(dir = constants.tmpDir) try: client = self.getConaryClient( \ tmpRoot, getArchFlavor(self.baseFlavor).freeze()) fingerprints = {} fpTrovespecs = {} for filename in [x for x in os.listdir(csdir) if x.endswith('.ccs')]: cs = changeset.ChangeSetFromFile(os.path.join(csdir, filename)) troves = [trove.Trove(x) for x in cs.iterNewTroveList()] for trv in troves: label = trv.version.v.trailingLabel() for sig in trv.troveInfo.sigs.digitalSigs.iter(): tspecList = fpTrovespecs.get(sig[0], set()) tspecList.add('%s=%s[%s]' % (trv.getName(), str(trv.getVersion()), str(trv.getFlavor()))) fpTrovespecs[sig[0]] = tspecList if fingerprints.has_key(label): if sig[0] not in fingerprints[label]: fingerprints[label].append(sig[0]) else: fingerprints.update({label:[sig[0]]}) missingKeys = [] for label, fingerprints in fingerprints.items(): for fp in fingerprints: try: key = client.repos.getAsciiOpenPGPKey(label, fp) fd, fname = tempfile.mkstemp(dir = constants.tmpDir) os.close(fd) fd = open(fname, 'w') fd.write(key) fd.close() call('gpg', '--home', homeDir, '--trust-model', 'always', '--import', fname) os.unlink(fname) except openpgpfile.KeyNotFound: missingKeys.append(fp) if missingKeys: errorMessage = 'The following troves do not have keys in ' \ 'their associated repositories:\n' for fingerprint in missingKeys: errorMessage += '%s requires %s\n' % \ (', '.join(fpTrovespecs[fingerprint]), fingerprint) raise RuntimeError(errorMessage) call('gpg', '--home', homeDir, '--export', '--no-auto-check-trustdb', '-o', os.path.join(topdir, 'public_keys.gpg')) finally: util.rmtree(homeDir, ignore_errors = True) util.rmtree(tmpRoot, ignore_errors = True)
def prepareTemplates(self, topdir, templateDir): self.status("Preparing ISO template") _linkRecurse(templateDir, topdir) productDir = os.path.join(topdir, self.productDir) # replace isolinux.bin with a real copy, since it's modified call('cp', '--remove-destination', '-a', templateDir + '/isolinux/isolinux.bin', topdir + '/isolinux/isolinux.bin') if os.path.exists(os.path.join(templateDir, 'isolinux')): for msgFile in [x for x in os.listdir(os.path.join(templateDir, 'isolinux')) if x.endswith('.msg')]: call('cp', '--remove-destination', '-a', os.path.join(templateDir, 'isolinux', msgFile), os.path.join(topdir, 'isolinux', msgFile)) csdir = os.path.join(topdir, self.productDir, 'changesets') util.mkdirChain(csdir) return csdir
def convertSplash(self, topdir, tmpPath): # convert syslinux-splash.png to splash.lss, if exists if os.path.exists(tmpPath + '/pixmaps/syslinux-splash.png'): log.info("found syslinux-splash.png, converting to splash.lss") splash = file(tmpPath + '/pixmaps/splash.lss', 'w') palette = [] # '#000000=0', '#cdcfd5=7', '#c90000=2', '#ffffff=15', '#5b6c93=9'] pngtopnm = subprocess.Popen(['pngtopnm', tmpPath + '/pixmaps/syslinux-splash.png'], stdout = subprocess.PIPE) ppmtolss16 = subprocess.Popen(['ppmtolss16'] + palette, stdin = pngtopnm.stdout, stdout = splash) ppmtolss16.communicate() # copy the splash.lss files to the appropriate place if os.path.exists(tmpPath + '/pixmaps/splash.lss'): log.info("found splash.lss; moving to isolinux directory") splashTarget = os.path.join(topdir, 'isolinux') call('cp', '--remove-destination', tmpPath + '/pixmaps/splash.lss', splashTarget) # FIXME: regenerate boot.iso here # Locate splash for isolinux vesamenu background. for name in ('vesamenu-splash.jpg', 'vesamenu-splash.png'): sourcePath = os.path.join(tmpPath, 'pixmaps', name) if os.path.exists(sourcePath): ext = name.split('.')[-1] name = 'splash.%s' % ext util.copyfile(sourcePath, os.path.join(topdir, 'isolinux', name)) # Rewrite isolinux.cfg to point to the background image. isolinux = os.path.join(topdir, 'isolinux', 'isolinux.cfg') lines = open(isolinux).readlines() for n, line in enumerate(lines): if line.startswith('menu background '): lines[n] = 'menu background %s\n' % name open(isolinux, 'w').write(''.join(lines)) break
def buildIsos(self, topdir): outputDir = os.path.join(constants.finishedDir, self.UUID) self.outputDir = outputDir util.mkdirChain(outputDir) # add the group writeable bit os.chmod(outputDir, os.stat(outputDir)[0] & 0777 | 0020) isoList = [] if self.basefilename: isoNameTemplate = self.basefilename + '-' else: isoNameTemplate = "%s-%s-%s-" % \ (self.jobData['project']['hostname'], upstream(self.baseVersion), self.arch) sourceDir = os.path.normpath(topdir + "/../") for d in sorted(os.listdir(sourceDir)): if not d.startswith('disc'): continue discNum = d.split("disc")[-1] discNumStr = "Disc %d" % int(discNum) truncatedName = self.jobData['name'][:31-len(discNumStr)] volumeId = "%s %s" % (truncatedName, discNumStr) outputIsoName = isoNameTemplate + d + ".iso" if os.access(os.path.join(sourceDir, d, "isolinux/isolinux.bin"), os.R_OK): os.chdir(os.path.join(sourceDir, d)) call("mkisofs", "-o", outputDir + "/" + outputIsoName, "-b", "isolinux/isolinux.bin", "-c", "isolinux/boot.cat", "-no-emul-boot", "-boot-load-size", "4", "-boot-info-table", "-R", "-J", "-V", volumeId, "-T", ".") else: os.chdir(os.path.join(sourceDir, d)) call("mkisofs", "-o", outputDir + "/" + outputIsoName, "-R", "-J", "-V", volumeId, "-T", ".") # mkisofs will happily ignore out of space conditions and produce a # truncated ISO file, so assume that if there's less than 1MB of # free space afterwards that it failed. fst = os.statvfs(outputDir) if fst.f_bsize * fst.f_bfree < 1000000: raise RuntimeError( "Not enough scratch space while running mkisofs") isoList.append((outputIsoName, "%s Disc %s" % (self.jobData['project']['name'], discNum))) isoList = [ (os.path.join(outputDir, iso[0]), iso[1]) for iso in isoList ] # this for loop re-identifies any iso greater than 700MB as a DVD for index, (iso, name) in zip(range(len(isoList)), isoList[:]): szPipe = os.popen('isosize %s' % iso, 'r') isoSize = int(szPipe.read()) szPipe.close() if isoSize > 734003200: # 700 MB in bytes newIso = iso.replace('disc', 'dvd') newName = name.replace('Disc', 'DVD') os.rename(iso, newIso) isoList[index] = (newIso, newName) for iso, name in isoList: if not os.access(iso, os.R_OK): raise RuntimeError, "ISO generation failed" else: cmd = [constants.implantIsoMd5] if not self.showMediaCheck: cmd.append('--supported-iso') cmd.append(iso) call(*cmd) # add the netboot images for f in ('boot.iso', 'diskboot.img'): inF = os.path.join(topdir, 'images', f) outF = os.path.join(outputDir, f) if os.path.exists(inF): gencslist._linkOrCopyFile(inF, outF) isoList += ( (outF, f), ) return isoList
def writeProductImage(self, topdir, arch): """write the product.img cramfs""" baseDir = os.path.join(topdir, self.productDir, 'base') tmpPath = tempfile.mkdtemp(dir=constants.tmpDir) self.writeBuildStamp(tmpPath) # RHEL 6 anaconda looks for product.img in a different location than # rPL 1 or rPL 2. To aid in detection, the path to product.img is # stashed in .treeinfo. treeInfo = os.path.join(topdir, '.treeinfo') productPath = os.path.join(baseDir, "product.img") stage2 = None if os.path.exists(treeInfo): try: parser = ConfigParser.SafeConfigParser() parser.read(treeInfo) # Look for an images-ARCH section, which may include a # path to product.img for section in parser.sections(): if not section.startswith('images-'): continue if parser.has_option(section, 'product.img'): productPath = os.path.normpath(os.path.join(topdir, parser.get(section, 'product.img'))) # There may also be a pointer to the stage2 if parser.has_option('stage2', 'mainimage'): stage2 = parser.get('stage2', 'mainimage') except: log.exception("Failed to parse .treeinfo; " "using default paths.") log.info("Target path for product.img is %s", productPath) # extract anaconda-images from repository, if exists tmpRoot = tempfile.mkdtemp(dir=constants.tmpDir) util.mkdirChain(os.path.join(tmpRoot, 'usr', 'share', 'anaconda', 'pixmaps')) cclient = self.getConaryClient(tmpRoot, arch) cclient.setUpdateCallback(self.callback) log.info("generating anaconda artwork.") autoGenPath = tempfile.mkdtemp(dir=constants.tmpDir) ai = AnacondaImages( \ self.jobData['name'], indir = constants.anacondaImagesPath, outdir = autoGenPath, fontfile = '/usr/share/fonts/bitstream-vera/Vera.ttf') ai.processImages() uJob = None log.info("checking for artwork from anaconda-custom=%s" % cclient.cfg.installLabelPath[0].asString()) uJob = self._getUpdateJob(cclient, "anaconda-custom") if uJob: log.info("custom artwork found. applying on top of generated artwork") cclient.applyUpdate(uJob, callback = self.callback, replaceFiles = True) log.info("success.") # if syslinux-splash.png does not exist in the anaconda-custom trove, change the # syslinux messages to fit our autogenerated palette. if not os.path.exists(os.path.join(tmpRoot, 'usr', 'share', 'anaconda', 'pixmaps', 'syslinux-splash.png')) and \ os.path.isdir(os.path.join(topdir, 'isolinux')): # do this here because we know we don't have custom artwork. # modify isolinux message colors to match default splash palette. for msgFile in [x for x in os.listdir( \ os.path.join(topdir, 'isolinux')) if x.endswith('.msg')]: call('sed', '-i', 's/07/0a/g;s/02/0e/g', os.path.join(topdir, 'isolinux', msgFile)) # copy autogenerated pixmaps into cramfs root util.mkdirChain(os.path.join(tmpPath, 'pixmaps')) tmpTar = tempfile.mktemp(dir = constants.tmpDir, suffix = '.tar') call('tar', 'cf', tmpTar, '-C', autoGenPath, '.') call('tar', 'xf', tmpTar, '-C', os.path.join(tmpPath, 'pixmaps')) os.unlink(tmpTar) if uJob: # copy pixmaps and scripts into cramfs root tmpTar = tempfile.mktemp(dir = constants.tmpDir, suffix = '.tar') call('tar', 'cf', tmpTar, '-C', os.path.join(tmpRoot, 'usr', 'share', 'anaconda'), '.') call('tar', 'xf', tmpTar, '-C', tmpPath) os.unlink(tmpTar) self.convertSplash(topdir, tmpPath) self.changeIsolinuxMenuTitle(topdir) self.writeConaryRc(os.path.join(tmpPath, 'conaryrc'), cclient) # extract constants.py from the stage2.img template and override the BETANAG flag # this would be better if constants.py could load a secondary constants.py for path in (stage2, 'images/stage2.img', 'rPath/base/stage2.img'): if not path: # stage2 is None if .treeinfo was not found continue fullpath = os.path.join(topdir, path) if not os.path.exists(fullpath): continue betaNag = int(self.getBuildData('betaNag')) log.info('Copying constants.py from %s and setting beta nag to %s', path, betaNag) stage2Path = tempfile.mkdtemp(dir=constants.tmpDir) logCall(['/bin/mount', '-o', 'loop,ro', fullpath, stage2Path]) out = open(tmpPath + '/constants.py', 'w') for line in open(os.path.join(stage2Path + '/usr/lib/anaconda/constants.py')): if line.startswith('BETANAG ='): line = 'BETANAG = %d\n' % betaNag out.write(line) out.close() logCall(['/bin/umount', '-d', stage2Path]) os.rmdir(stage2Path) break else: log.info('Could not find stage2.img; not changing beta nag') # create cramfs logCall(['/sbin/mkfs.cramfs', tmpPath, productPath]) # clean up util.rmtree(tmpPath, ignore_errors = True) util.rmtree(tmpRoot, ignore_errors = True) util.rmtree(autoGenPath, ignore_errors = True)