class RuntimeBuilder(object): '''Builds the anaconda runtime image.''' def __init__(self, product, arch, yum, templatedir=None): root = yum.conf.installroot # use a copy of product so we can modify it locally product = product.copy() product.name = product.name.lower() self.vars = DataHolder(arch=arch, product=product, yum=yum, root=root, basearch=arch.basearch, libdir=arch.libdir) self.yum = yum self._runner = LoraxTemplateRunner(inroot=root, outroot=root, yum=yum, templatedir=templatedir) self._runner.defaults = self.vars def _install_branding(self): release = None for pkg in self.yum.whatProvides('/etc/system-release', None, None): if pkg.name.startswith('generic'): continue else: release = pkg.name break if not release: logger.error('could not get the release') return # release logger.info('got release: %s', release) self._runner.installpkg(release) # logos release, _suffix = release.split('-', 1) self._runner.installpkg('%s-logos' % release) def install(self): '''Install packages and do initial setup with runtime-install.tmpl''' self._install_branding() self._runner.run("runtime-install.tmpl") def writepkglists(self, pkglistdir): '''debugging data: write out lists of package contents''' if not os.path.isdir(pkglistdir): os.makedirs(pkglistdir) for pkgobj in self.yum.doPackageLists(pkgnarrow='installed').installed: with open(joinpaths(pkglistdir, pkgobj.name), "w") as fobj: for fname in pkgobj.filelist + pkgobj.dirlist: fobj.write("{0}\n".format(fname)) def postinstall(self): '''Do some post-install setup work with runtime-postinstall.tmpl''' # copy configdir into runtime root beforehand configdir = joinpaths(self._runner.templatedir,"config_files") configdir_path = "tmp/config_files" fullpath = joinpaths(self.vars.root, configdir_path) if os.path.exists(fullpath): remove(fullpath) copytree(configdir, fullpath) self._runner.run("runtime-postinstall.tmpl", configdir=configdir_path) def cleanup(self): '''Remove unneeded packages and files with runtime-cleanup.tmpl''' self._runner.run("runtime-cleanup.tmpl") def writepkgsizes(self, pkgsizefile): '''debugging data: write a big list of pkg sizes''' fobj = open(pkgsizefile, "w") getsize = lambda f: os.lstat(f).st_size if os.path.exists(f) else 0 for p in sorted(self.yum.doPackageLists(pkgnarrow='installed').installed): pkgsize = sum(getsize(joinpaths(self.vars.root,f)) for f in p.filelist) fobj.write("{0.name}.{0.arch}: {1}\n".format(p, pkgsize)) def generate_module_data(self): root = self.vars.root moddir = joinpaths(root, "lib/modules/") # Generate_module_data creates a file called "module-info" in this # directory. If we don't do something to exclude this file, depmod will fail # on the second path of this loop. Let's check to see if kver is a directory # before generating module info from it. for kver in os.listdir(moddir): if os.path.isdir(kver): ksyms = joinpaths(root, "boot/System.map-%s" % kver) logger.info("doing depmod and module-info for %s", kver) runcmd(["depmod", "-a", "-F", ksyms, "-b", root, kver]) generate_module_info(moddir+kver, outfile=moddir+"module-info") def create_runtime(self, outfile="/var/tmp/squashfs.img", compression="xz", compressargs=[], size=2): # make live rootfs image - must be named "LiveOS/rootfs.img" for dracut workdir = joinpaths(os.path.dirname(outfile), "runtime-workdir") if size: fssize = size * (1024*1024*1024) # 2GB sparse file compresses down to nothin' else: fssize = None # Let mkext4img figure out the needed size os.makedirs(joinpaths(workdir, "LiveOS")) imgutils.mkext4img(self.vars.root, joinpaths(workdir, "LiveOS/rootfs.img"), label="Anaconda", size=fssize) # Reset selinux context on new rootfs with imgutils.LoopDev( joinpaths(workdir, "LiveOS/rootfs.img") ) as loopdev: with imgutils.Mount(loopdev) as mnt: cmd = [ "setfiles", "-e", "/proc", "-e", "/sys", "-e", "/dev", "/etc/selinux/targeted/contexts/files/file_contexts", "/"] runcmd(cmd, root=mnt) # squash the live rootfs and clean up workdir imgutils.mksquashfs(workdir, outfile, compression, compressargs) remove(workdir)
class RuntimeBuilder(object): '''Builds the anaconda runtime image.''' def __init__(self, product, arch, yum, templatedir=None, add_templates=None, add_template_vars=None): root = yum.conf.installroot # use a copy of product so we can modify it locally product = product.copy() product.name = product.name.lower() self.vars = DataHolder(arch=arch, product=product, yum=yum, root=root, basearch=arch.basearch, libdir=arch.libdir) self.yum = yum self._runner = LoraxTemplateRunner(inroot=root, outroot=root, yum=yum, templatedir=templatedir) self.add_templates = add_templates or [] self.add_template_vars = add_template_vars or {} self._runner.defaults = self.vars def _install_branding(self): release = None for pkg in self.yum.whatProvides('/etc/system-release', None, None): if pkg.name.startswith('generic'): continue else: release = pkg.name break if not release: logger.error('could not get the release') return # release logger.info('got release: %s', release) self._runner.installpkg(release) # logos release, _suffix = release.split('-', 1) self._runner.installpkg('%s-logos' % release) def install(self): '''Install packages and do initial setup with runtime-install.tmpl''' self._install_branding() self._runner.run("runtime-install.tmpl") for tmpl in self.add_templates: self._runner.run(tmpl, **self.add_template_vars) def writepkglists(self, pkglistdir): '''debugging data: write out lists of package contents''' if not os.path.isdir(pkglistdir): os.makedirs(pkglistdir) for pkgobj in self.yum.doPackageLists(pkgnarrow='installed').installed: with open(joinpaths(pkglistdir, pkgobj.name), "w") as fobj: for fname in pkgobj.filelist + pkgobj.dirlist: fobj.write("{0}\n".format(fname)) def postinstall(self): '''Do some post-install setup work with runtime-postinstall.tmpl''' # copy configdir into runtime root beforehand configdir = joinpaths(self._runner.templatedir, "config_files") configdir_path = "tmp/config_files" fullpath = joinpaths(self.vars.root, configdir_path) if os.path.exists(fullpath): remove(fullpath) copytree(configdir, fullpath) self._runner.run("runtime-postinstall.tmpl", configdir=configdir_path) def cleanup(self): '''Remove unneeded packages and files with runtime-cleanup.tmpl''' self._runner.run("runtime-cleanup.tmpl") def writepkgsizes(self, pkgsizefile): '''debugging data: write a big list of pkg sizes''' fobj = open(pkgsizefile, "w") getsize = lambda f: os.lstat(f).st_size if os.path.exists(f) else 0 for p in sorted( self.yum.doPackageLists(pkgnarrow='installed').installed): pkgsize = sum( getsize(joinpaths(self.vars.root, f)) for f in p.filelist) fobj.write("{0.name}.{0.arch}: {1}\n".format(p, pkgsize)) def generate_module_data(self): root = self.vars.root moddir = joinpaths(root, "lib/modules/") # Generate_module_data creates a file called "module-info" in this # directory. If we don't do something to exclude this file, depmod will fail # on the second path of this loop. Let's check to see if kver is a directory # before generating module info from it. for kver in os.listdir(moddir): if os.path.isdir(kver): ksyms = joinpaths(root, "boot/System.map-%s" % kver) logger.info("doing depmod and module-info for %s", kver) runcmd(["depmod", "-a", "-F", ksyms, "-b", root, kver]) generate_module_info(moddir + kver, outfile=moddir + "module-info") def create_runtime(self, outfile="/var/tmp/squashfs.img", compression="xz", compressargs=[], size=2): # make live rootfs image - must be named "LiveOS/rootfs.img" for dracut workdir = joinpaths(os.path.dirname(outfile), "runtime-workdir") os.makedirs(joinpaths(workdir, "LiveOS")) imgutils.mkrootfsimg(self.vars.root, joinpaths(workdir, "LiveOS/rootfs.img"), "Anaconda", size=size) # squash the live rootfs and clean up workdir imgutils.mksquashfs(workdir, outfile, compression, compressargs) remove(workdir)
class TreeBuilder(object): '''Builds the arch-specific boot images. inroot should be the installtree root (the newly-built runtime dir)''' def __init__(self, product, arch, inroot, outroot, runtime, isolabel, domacboot=False, doupgrade=True, templatedir=None): # NOTE: if you pass an arg named "runtime" to a mako template it'll # clobber some mako internal variables - hence "runtime_img". self.vars = DataHolder(arch=arch, product=product, runtime_img=runtime, runtime_base=basename(runtime), inroot=inroot, outroot=outroot, basearch=arch.basearch, libdir=arch.libdir, isolabel=isolabel, udev=udev_escape, domacboot=domacboot, doupgrade=doupgrade) self._runner = LoraxTemplateRunner(inroot, outroot, templatedir=templatedir) self._runner.defaults = self.vars self.templatedir = templatedir @property def kernels(self): return findkernels(root=self.vars.inroot) def rebuild_initrds(self, add_args=[], backup="", prefix=""): '''Rebuild all the initrds in the tree. If backup is specified, each initrd will be renamed with backup as a suffix before rebuilding. If backup is empty, the existing initrd files will be overwritten. If suffix is specified, the existing initrd is untouched and a new image is built with the filename "${prefix}-${kernel.version}.img" ''' dracut = ["dracut", "--nomdadmconf", "--nolvmconf"] + add_args if not backup: dracut.append("--force") kernels = [kernel for kernel in self.kernels if hasattr(kernel, "initrd")] if not kernels: raise Exception("No initrds found, cannot rebuild_initrds") # Hush some dracut warnings. TODO: bind-mount proc in place? open(joinpaths(self.vars.inroot,"/proc/modules"),"w") for kernel in kernels: if prefix: idir = os.path.dirname(kernel.initrd.path) outfile = joinpaths(idir, prefix+'-'+kernel.version+'.img') else: outfile = kernel.initrd.path logger.info("rebuilding %s", outfile) if backup: initrd = joinpaths(self.vars.inroot, outfile) os.rename(initrd, initrd + backup) cmd = dracut + [outfile, kernel.version] runcmd(cmd, root=self.vars.inroot) # ppc64 cannot boot images > 32MiB, check size and warn if self.vars.arch.basearch in ("ppc64", "ppc64le") and os.path.exists(outfile): st = os.stat(outfile) if st.st_size > 32 * 1024 * 1024: logging.warning("ppc64 initrd %s is > 32MiB", outfile) os.unlink(joinpaths(self.vars.inroot,"/proc/modules")) def build(self): templatefile = templatemap[self.vars.arch.basearch] self._runner.run(templatefile, kernels=self.kernels) self.treeinfo_data = self._runner.results.treeinfo self.implantisomd5() def implantisomd5(self): for section, data in self.treeinfo_data.items(): if 'boot.iso' in data: iso = joinpaths(self.vars.outroot, data['boot.iso']) runcmd(["implantisomd5", iso]) @property def dracut_hooks_path(self): """ Return the path to the lorax dracut hooks scripts Use the configured share dir if it is setup, otherwise default to /usr/share/lorax/dracut_hooks """ if self.templatedir: return joinpaths(self.templatedir, "dracut_hooks") else: return "/usr/share/lorax/dracut_hooks" def copy_dracut_hooks(self, hooks): """ Copy the hook scripts in hooks into the installroot's /tmp/ and return a list of commands to pass to dracut when creating the initramfs hooks is a list of tuples with the name of the hook script and the target dracut hook directory (eg. [("99anaconda-copy-ks.sh", "/lib/dracut/hooks/pre-pivot")]) """ dracut_commands = [] for hook_script, dracut_path in hooks: src = joinpaths(self.dracut_hooks_path, hook_script) if not os.path.exists(src): logger.error("Missing lorax dracut hook script %s" % (src)) continue dst = joinpaths(self.vars.inroot, "/tmp/", hook_script) copy2(src, dst) dracut_commands += ["--include", joinpaths("/tmp/", hook_script), dracut_path] return dracut_commands
class TreeBuilder(object): '''Builds the arch-specific boot images. inroot should be the installtree root (the newly-built runtime dir)''' def __init__(self, product, arch, inroot, outroot, runtime, isolabel, domacboot=False, doupgrade=True, templatedir=None, add_templates=None, add_template_vars=None, workdir=None): # NOTE: if you pass an arg named "runtime" to a mako template it'll # clobber some mako internal variables - hence "runtime_img". self.vars = DataHolder(arch=arch, product=product, runtime_img=runtime, runtime_base=basename(runtime), inroot=inroot, outroot=outroot, basearch=arch.basearch, libdir=arch.libdir, isolabel=isolabel, udev=udev_escape, domacboot=domacboot, doupgrade=doupgrade, workdir=workdir) self._runner = LoraxTemplateRunner(inroot, outroot, templatedir=templatedir) self._runner.defaults = self.vars self.add_templates = add_templates or [] self.add_template_vars = add_template_vars or {} self.templatedir = templatedir @property def kernels(self): return findkernels(root=self.vars.inroot) def rebuild_initrds(self, add_args=[], backup="", prefix=""): '''Rebuild all the initrds in the tree. If backup is specified, each initrd will be renamed with backup as a suffix before rebuilding. If backup is empty, the existing initrd files will be overwritten. If suffix is specified, the existing initrd is untouched and a new image is built with the filename "${prefix}-${kernel.version}.img" ''' dracut = ["dracut", "--nomdadmconf", "--nolvmconf"] + add_args if not backup: dracut.append("--force") kernels = [ kernel for kernel in self.kernels if hasattr(kernel, "initrd") ] if not kernels: raise Exception("No initrds found, cannot rebuild_initrds") # Hush some dracut warnings. TODO: bind-mount proc in place? open(joinpaths(self.vars.inroot, "/proc/modules"), "w") for kernel in kernels: if prefix: idir = os.path.dirname(kernel.initrd.path) outfile = joinpaths(idir, prefix + '-' + kernel.version + '.img') else: outfile = kernel.initrd.path logger.info("rebuilding %s", outfile) if backup: initrd = joinpaths(self.vars.inroot, outfile) os.rename(initrd, initrd + backup) cmd = dracut + [outfile, kernel.version] runcmd(cmd, root=self.vars.inroot) # ppc64 cannot boot images > 32MiB, check size and warn if self.vars.arch.basearch in ( "ppc64", "ppc64le") and os.path.exists(outfile): st = os.stat(outfile) if st.st_size > 32 * 1024 * 1024: logging.warning("ppc64 initrd %s is > 32MiB", outfile) os.unlink(joinpaths(self.vars.inroot, "/proc/modules")) def build(self): templatefile = templatemap[self.vars.arch.basearch] for tmpl in self.add_templates: self._runner.run(tmpl, **self.add_template_vars) self._runner.run(templatefile, kernels=self.kernels) self.treeinfo_data = self._runner.results.treeinfo self.implantisomd5() def implantisomd5(self): for section, data in self.treeinfo_data.items(): if 'boot.iso' in data: iso = joinpaths(self.vars.outroot, data['boot.iso']) runcmd(["implantisomd5", iso]) @property def dracut_hooks_path(self): """ Return the path to the lorax dracut hooks scripts Use the configured share dir if it is setup, otherwise default to /usr/share/lorax/dracut_hooks """ if self.templatedir: return joinpaths(self.templatedir, "dracut_hooks") else: return "/usr/share/lorax/dracut_hooks" def copy_dracut_hooks(self, hooks): """ Copy the hook scripts in hooks into the installroot's /tmp/ and return a list of commands to pass to dracut when creating the initramfs hooks is a list of tuples with the name of the hook script and the target dracut hook directory (eg. [("99anaconda-copy-ks.sh", "/lib/dracut/hooks/pre-pivot")]) """ dracut_commands = [] for hook_script, dracut_path in hooks: src = joinpaths(self.dracut_hooks_path, hook_script) if not os.path.exists(src): logger.error("Missing lorax dracut hook script %s" % (src)) continue dst = joinpaths(self.vars.inroot, "/tmp/", hook_script) copy2(src, dst) dracut_commands += [ "--include", joinpaths("/tmp/", hook_script), dracut_path ] return dracut_commands