# # images.py # # Copyright (C) 2011 Red Hat, Inc. # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program. If not, see . # # Red Hat Author(s): Martin Gracik # import logging logger = logging.getLogger("pylorax.images") import os import subprocess import shutil import glob import collections from base import DataHolder from sysutils import joinpaths, cpfile, replace import constants ##### constants ##### ANABOOTDIR = "usr/share/anaconda/boot" # ppc ETCDIR = "etc" PPCPARENT = "ppc" CHRPDIR = "ppc/chrp" IMAGESDIR = "images" PPC32DIR = "ppc/ppc32" PPC64DIR = "ppc/ppc64" MACDIR = "ppc/mac" NETBOOTDIR = "images/netboot" MKZIMAGE = "usr/bin/mkzimage" ZIMAGE_STUB = "usr/share/ppc64-utils/zImage.stub" WRAPPER = "usr/sbin/wrapper" ISOPATHDIR = "isopath" MKISOFS = "mkisofs" MAPPING = joinpaths(ANABOOTDIR, "mapping") MAGIC = joinpaths(ANABOOTDIR, "magic") IMPLANTISOMD5 = "implantisomd5" # x86 ISOLINUXDIR = "isolinux" PXEBOOTDIR = "images/pxeboot" ISOLINUX_BIN = "usr/share/syslinux/isolinux.bin" SYSLINUX_CFG = "usr/share/anaconda/boot/syslinux.cfg" ISOHYBRID = "isohybrid" # s390 INITRD_ADDRESS = "0x02000000" class PPC(object): def __init__(self, kernellist, installtree, outputroot, product, version, treeinfo, basearch): self.kernellist = kernellist self.installtree = installtree self.outputroot = outputroot self.product = product self.version = version self.treeinfo = treeinfo self.basearch = basearch self.kernels, self.initrds = [], [] self.reqs = collections.defaultdict(str) def backup_required(self, workdir): # yaboot.conf yabootconf = joinpaths(self.installtree.root, ANABOOTDIR, "yaboot.conf.in") self.reqs["yabootconf"] = cpfile(yabootconf, workdir) # bootinfo.txt bootinfo_txt = joinpaths(self.installtree.root, ANABOOTDIR, "bootinfo.txt") self.reqs["bootinfo_txt"] = cpfile(bootinfo_txt, workdir) # efika.forth efika_forth = joinpaths(self.installtree.root, "boot", "efika.forth") self.reqs["efika_forth"] = cpfile(efika_forth, workdir) # yaboot yaboot = joinpaths(self.installtree.root, "usr/lib/yaboot/yaboot") self.reqs["yaboot"] = cpfile(yaboot, workdir) # ofboot.b ofboot_b = joinpaths(self.installtree.root, ANABOOTDIR, "ofboot.b") self.reqs["ofboot_b"] = cpfile(ofboot_b, workdir) # yaboot.conf.3264 yabootconf3264 = joinpaths(self.installtree.root, ANABOOTDIR, "yaboot.conf.3264") self.reqs["yabootconf3264"] = cpfile(yabootconf3264, workdir) def create_initrd(self, libdir): # create directories logger.info("creating required directories") os.makedirs(joinpaths(self.outputroot, ETCDIR)) os.makedirs(joinpaths(self.outputroot, PPCPARENT)) os.makedirs(joinpaths(self.outputroot, CHRPDIR)) os.makedirs(joinpaths(self.outputroot, IMAGESDIR)) # set up biarch test ppc32dir = joinpaths(self.outputroot, PPC32DIR) ppc64dir = joinpaths(self.outputroot, PPC64DIR) biarch = lambda: (os.path.exists(ppc32dir) and os.path.exists(ppc64dir)) # create images for kernel in self.kernellist: # set up bits kernel_arch = kernel.version.split(".")[-1] if (kernel_arch == "ppc"): bits = 32 ppcdir = PPC32DIR fakearch = "ppc" elif (kernel_arch == "ppc64"): bits = 64 ppcdir = PPC64DIR fakearch = "" else: raise Exception("unknown kernel arch {0}".format(kernel_arch)) # create ppc dir os.makedirs(joinpaths(self.outputroot, ppcdir)) if (kernel_arch == "ppc"): # create mac dir os.makedirs(joinpaths(self.outputroot, MACDIR)) # create netboot dir os.makedirs(joinpaths(self.outputroot, NETBOOTDIR)) # copy kernel logger.info("copying kernel image") kernel.fname = "vmlinuz" dst = joinpaths(self.outputroot, ppcdir, kernel.fname) kernel.fpath = cpfile(kernel.fpath, dst) # create and copy initrd initrd = DataHolder() initrd.fname = "ramdisk.image.gz" initrd.fpath = joinpaths(self.outputroot, ppcdir, initrd.fname) initrd.itype = kernel.ktype logger.info("compressing the install tree") self.installtree.compress(initrd, kernel) # add kernel and initrd to the list self.kernels.append(kernel) self.initrds.append(initrd) # add kernel and initrd to .treeinfo section = "images-{0}".format(kernel_arch) data = {"kernel": joinpaths(ppcdir, kernel.fname)} self.treeinfo.add_section(section, data) data = {"initrd": joinpaths(ppcdir, initrd.fname)} self.treeinfo.add_section(section, data) # copy yaboot.conf dst = joinpaths(self.outputroot, ppcdir, "yaboot.conf") yabootconf = cpfile(self.reqs["yabootconf"], dst) mkzimage = joinpaths(self.installtree.root, MKZIMAGE) zimage_stub = joinpaths(self.installtree.root, ZIMAGE_STUB) wrapper = joinpaths(self.installtree.root, WRAPPER) # XXX wrapper_a = joinpaths(self.installtree.root, "usr/%s/kernel-wrapper/wrapper.a" % libdir) ppc_img_fname = "ppc{0}.img".format(bits) ppc_img_fpath = joinpaths(self.outputroot, NETBOOTDIR, ppc_img_fname) if (os.path.exists(mkzimage) and os.path.exists(zimage_stub)): logger.info("creating the z image") # XXX copy zImage.lds zimage_lds = joinpaths(self.installtree.root, "usr/%s/kernel-wrapper/zImage.lds" % libdir) zimage_lds = cpfile(zimage_lds, joinpaths(self.outputroot, ppcdir)) # change current working directory cwd = os.getcwd() os.chdir(joinpaths(self.outputroot, ppcdir)) # run mkzimage cmd = [mkzimage, kernel.fpath, "no", "no", initrd.fpath, zimage_stub, ppc_img_fpath] p = subprocess.Popen(cmd, stdin=subprocess.PIPE, stdout=subprocess.PIPE) p.wait() # remove zImage.lds os.unlink(zimage_lds) # return to former working directory os.chdir(cwd) elif (os.path.exists(wrapper) and os.path.exists(wrapper_a)): logger.info("running kernel wrapper") # run wrapper cmd = [wrapper, "-o", ppc_img_fpath, "-i", initrd.fpath, "-D", os.path.dirname(wrapper_a), kernel.fpath] p = subprocess.Popen(cmd, stdin=subprocess.PIPE, stdout=subprocess.PIPE) p.wait() if os.path.exists(ppc_img_fpath): # add ppc image to .treeinfo section = "images-{0}".format(kernel_arch) data = {"zimage": joinpaths(NETBOOTDIR, ppc_img_fname)} self.treeinfo.add_section(section, data) if (bits == 32): # set up prepboot p = joinpaths(NETBOOTDIR, ppc_img_fname) prepboot = ["-prep-boot {0}".format(p)] # remove netboot dir if empty try: os.rmdir(joinpaths(self.outputroot, NETBOOTDIR)) except OSError: pass # copy bootinfo.txt cpfile(self.reqs["bootinfo_txt"], joinpaths(self.outputroot, PPCPARENT)) # copy efika.forth cpfile(self.reqs["efika_forth"], joinpaths(self.outputroot, PPCPARENT)) # copy yaboot to chrp dir yaboot = cpfile(self.reqs["yaboot"], joinpaths(self.outputroot, CHRPDIR)) if (os.path.exists(joinpaths(self.outputroot, MACDIR))): # copy yaboot and ofboot.b to mac dir cpfile(yaboot, joinpaths(self.outputroot, MACDIR)) cpfile(self.reqs["ofboot_b"], joinpaths(self.outputroot, MACDIR)) # set up macboot p = joinpaths(self.outputroot, ISOPATHDIR, MACDIR) macboot = ["-hfs-volid {0}".format(self.version), "-hfs-bless {0}".format(p)] # add note to yaboot cmd = [joinpaths(self.installtree.root, "usr/lib/yaboot/addnote"), yaboot] p = subprocess.Popen(cmd, stdin=subprocess.PIPE, stdout=subprocess.PIPE) p.wait() # copy yaboot.conf to etc dir if (biarch): yabootconf = cpfile(self.reqs["yabootconf3264"], joinpaths(self.outputroot, ETCDIR, "yaboot.conf")) replace(yabootconf, r"%BITS%", "32") replace(yabootconf, r"%PRODUCT%", self.product) replace(yabootconf, r"%VERSION%", self.version) else: cpfile(joinpaths(self.outputroot, ppcdir, "yaboot.conf"), joinpaths(self.outputroot, ETCDIR)) def create_boot(self, efiboot=None): # create isopath dir isopathdir = joinpaths(self.outputroot, ISOPATHDIR) os.makedirs(isopathdir) # copy etc dir and ppc dir to isopath dir shutil.copytree(joinpaths(self.outputroot, ETCDIR), joinpaths(isopathdir, ETCDIR)) shutil.copytree(joinpaths(self.outputroot, PPCPARENT), joinpaths(isopathdir, PPCPARENT)) if (os.path.exists(joinpaths(self.outputroot, NETBOOTDIR))): # create images dir in isopath dir if we have ppc images imagesdir = joinpaths(isopathdir, IMAGESDIR) os.makedirs(imagesdir) # copy netboot dir to images dir shutil.copytree(joinpaths(self.outputroot, NETBOOTDIR), joinpaths(imagesdir, os.path.basename(NETBOOTDIR))) # define prepboot and macboot prepboot = [] if "prepboot" not in locals() else locals()["prepboot"] macboot = [] if "macboot" not in locals() else locals()["macboot"] # create boot image boot_fpath = joinpaths(self.outputroot, IMAGESDIR, "boot.iso") # run mkisofs cmd = [MKISOFS, "-o", boot_fpath, "-chrp-boot", "-U"] + prepboot + \ ["-part", "-hfs", "-T", "-r", "-l", "-J", "-A", '"%s %s"' % (self.product, self.version), "-sysid", "PPC", "-V", '"PBOOT"', "-volset", '"%s"' % self.version, "-volset-size", "1", "-volset-seqno", "1"] + macboot + \ ["-map", joinpaths(self.installtree.root, MAPPING), "-magic", joinpaths(self.installtree.root, MAGIC), "-no-desktop", "-allow-multidot", "-graft-points", isopathdir] p = subprocess.Popen(cmd, stdin=subprocess.PIPE, stdout=subprocess.PIPE) p.wait() # run implantisomd5 cmd = [IMPLANTISOMD5, boot_fpath] p = subprocess.Popen(cmd, stdin=subprocess.PIPE, stdout=subprocess.PIPE) p.wait() # remove isopath dir shutil.rmtree(isopathdir) class X86(object): def __init__(self, kernellist, installtree, outputroot, product, version, treeinfo, basearch): self.kernellist = kernellist self.installtree = installtree self.outputroot = outputroot self.product = product self.version = version self.treeinfo = treeinfo self.basearch = basearch self.kernels, self.initrds = [], [] self.reqs = collections.defaultdict(str) def backup_required(self, workdir): # isolinux.bin isolinux_bin = joinpaths(self.installtree.root, ISOLINUX_BIN) if not os.path.exists(isolinux_bin): raise Exception("isolinux.bin not present") self.reqs["isolinux_bin"] = cpfile(isolinux_bin, workdir) # syslinux.cfg syslinux_cfg = joinpaths(self.installtree.root, SYSLINUX_CFG) self.reqs["syslinux_cfg"] = cpfile(syslinux_cfg, workdir) # memtest memtest = glob.glob(joinpaths(self.installtree.root, "boot", "memtest*")) if memtest: self.reqs["memtest"] = cpfile(memtest[-1], joinpaths(workdir, "memtest")) # *.msg files msgfiles = glob.glob(joinpaths(self.installtree.root, ANABOOTDIR, "*.msg")) if not msgfiles: raise Exception("message files not present") self.reqs["msgfiles"] = [] for src in msgfiles: self.reqs["msgfiles"].append(cpfile(src, workdir)) # splash splash = joinpaths(self.installtree.root, ANABOOTDIR, "syslinux-vesa-splash.jpg") if not splash: raise Exception("syslinux-vesa-splash.jpg not present") self.reqs["splash"] = cpfile(splash, workdir) # vesamenu.c32 vesamenu = joinpaths(self.installtree.root, "usr/share/syslinux/vesamenu.c32") self.reqs["vesamenu"] = cpfile(vesamenu, workdir) # grub.conf grubconf = joinpaths(self.installtree.root, ANABOOTDIR, "grub.conf") self.reqs["grubconf"] = cpfile(grubconf, workdir) def create_initrd(self, libdir): # create directories logger.info("creating required directories") os.makedirs(joinpaths(self.outputroot, ISOLINUXDIR)) os.makedirs(joinpaths(self.outputroot, PXEBOOTDIR)) # copy isolinux.bin to isolinux dir cpfile(self.reqs["isolinux_bin"], joinpaths(self.outputroot, ISOLINUXDIR)) # copy syslinux.cfg to isolinux dir (XXX rename to isolinux.cfg) isolinux_cfg = cpfile(self.reqs["syslinux_cfg"], joinpaths(self.outputroot, ISOLINUXDIR, "isolinux.cfg")) replace(isolinux_cfg, r"@PRODUCT@", self.product) replace(isolinux_cfg, r"@VERSION@", self.version) # copy memtest if self.reqs["memtest"]: cpfile(self.reqs["memtest"], joinpaths(self.outputroot, ISOLINUXDIR)) with open(isolinux_cfg, "a") as f: f.write("label memtest86\n") f.write(" menu label ^Memory test\n") f.write(" kernel memtest\n") f.write(" append -\n") # copy *.msg files for src in self.reqs["msgfiles"]: dst = cpfile(src, joinpaths(self.outputroot, ISOLINUXDIR)) replace(dst, r"@VERSION@", self.version) # copy syslinux-vesa-splash.jpg splash = cpfile(self.reqs["splash"], joinpaths(self.outputroot, ISOLINUXDIR, "splash.jpg")) # copy vesamenu.c32 cpfile(self.reqs["vesamenu"], joinpaths(self.outputroot, ISOLINUXDIR)) # set up isolinux.cfg replace(isolinux_cfg, r"default linux", "default vesamenu.c32") replace(isolinux_cfg, r"prompt 1", "#prompt 1") # copy grub.conf grubconf = cpfile(self.reqs["grubconf"], joinpaths(self.outputroot, ISOLINUXDIR)) replace(grubconf, r"@PRODUCT@", self.product) replace(grubconf, r"@VERSION@", self.version) # create images for kernel in self.kernellist: # set up file names suffix = "" if (kernel.ktype == constants.K_PAE): suffix = "-PAE" elif (kernel.ktype == constants.K_XEN): suffix = "-XEN" logger.info("copying kernel image") kernel.fname = "vmlinuz{0}".format(suffix) if not suffix: # copy kernel to isolinux dir kernel.fpath = cpfile(kernel.fpath, joinpaths(self.outputroot, ISOLINUXDIR, kernel.fname)) # create link in pxeboot dir os.link(kernel.fpath, joinpaths(self.outputroot, PXEBOOTDIR, kernel.fname)) else: # copy kernel to pxeboot dir kernel.fpath = cpfile(kernel.fpath, joinpaths(self.outputroot, PXEBOOTDIR, kernel.fname)) # create and copy initrd to pxeboot dir initrd = DataHolder() initrd.fname = "initrd{0}.img".format(suffix) initrd.fpath = joinpaths(self.outputroot, PXEBOOTDIR, initrd.fname) initrd.itype = kernel.ktype logger.info("compressing the install tree") self.installtree.compress(initrd, kernel) # add kernel and initrd to the list self.kernels.append(kernel) self.initrds.append(initrd) if not suffix: # create link in isolinux dir os.link(initrd.fpath, joinpaths(self.outputroot, ISOLINUXDIR, initrd.fname)) # add kernel and initrd to .treeinfo section = "images-{0}".format("xen" if suffix else self.basearch) data = {"kernel": joinpaths(PXEBOOTDIR, kernel.fname)} self.treeinfo.add_section(section, data) data = {"initrd": joinpaths(PXEBOOTDIR, initrd.fname)} self.treeinfo.add_section(section, data) if not suffix: # add boot.iso to .treeinfo data = {"boot.iso": joinpaths(IMAGESDIR, "boot.iso")} self.treeinfo.add_section(section, data) # create images-xen section on x86_64 if self.basearch == "x86_64": section = "images-xen" data = {"kernel": joinpaths(PXEBOOTDIR, kernel.fname)} self.treeinfo.add_section(section, data) data = {"initrd": joinpaths(PXEBOOTDIR, initrd.fname)} self.treeinfo.add_section(section, data) def create_boot(self, efiboot=None): # define efiargs and efigraft efiargs, efigraft = [], [] if efiboot: efiargs = ["-eltorito-alt-boot", "-e", joinpaths(IMAGESDIR, "efiboot.img"), "-no-emul-boot"] efigraft = ["EFI/BOOT={0}/EFI/BOOT".format(self.outputroot)] # create boot image boot_fpath = joinpaths(self.outputroot, IMAGESDIR, "boot.iso") # run mkisofs cmd = [MKISOFS, "-v", "-o", boot_fpath, "-b", "{0}/isolinux.bin".format(ISOLINUXDIR), "-c", "{0}/boot.cat".format(ISOLINUXDIR), "-no-emul-boot", "-boot-load-size", "4", "-boot-info-table"] + efiargs + \ ["-R", "-J", "-V", "'{0}'".format(self.product), "-T", "-graft-points", "isolinux={0}".format(joinpaths(self.outputroot, ISOLINUXDIR)), "images={0}".format(joinpaths(self.outputroot, IMAGESDIR))] + \ efigraft p = subprocess.Popen(cmd, stdin=subprocess.PIPE, stdout=subprocess.PIPE) p.wait() if os.path.exists(ISOHYBRID): # run isohybrid cmd = [ISOHYBRID, boot_fpath] p = subprocess.Popen(cmd, stdin=subprocess.PIPE, stdout=subprocess.PIPE) p.wait() # run implantisomd5 cmd = [IMPLANTISOMD5, boot_fpath] p = subprocess.Popen(cmd, stdin=subprocess.PIPE, stdout=subprocess.PIPE) p.wait() class S390(object): def __init__(self, kernellist, installtree, outputroot, product, version, treeinfo, basearch): self.kernellist = kernellist self.installtree = installtree self.outputroot = outputroot self.product = product self.version = version self.treeinfo = treeinfo self.basearch = basearch self.kernels, self.initrds = [], [] self.reqs = collections.defaultdict(str) def backup_required(self, workdir): pass def create_initrd(self, libdir): # create directories os.makedirs(joinpaths(self.outputroot, IMAGESDIR)) # copy redhat.exec cpfile(joinpaths(self.installtree.root, ANABOOTDIR, "redhat.exec"), joinpaths(self.outputroot, IMAGESDIR)) # copy generic.prm generic_prm = cpfile(joinpaths(self.installtree.root, ANABOOTDIR, "generic.prm"), joinpaths(self.outputroot, IMAGESDIR)) # copy generic.ins generic_ins = cpfile(joinpaths(self.installtree.root, ANABOOTDIR, "generic.ins"), self.outputroot) replace(generic_ins, r"@INITRD_LOAD_ADDRESS@", INITRD_ADDRESS) for kernel in self.kernellist: # copy kernel kernel.fname = "kernel.img" kernel.fpath = cpfile(kernel.fpath, joinpaths(self.outputroot, IMAGESDIR, kernel.fname)) # create and copy initrd initrd = DataHolder() initrd.fname = "initrd.img" initrd.fpath = joinpaths(self.outputroot, IMAGESDIR, initrd.fname) logger.info("compressing the install tree") self.installtree.compress(initrd, kernel) # run addrsize addrsize = joinpaths(self.installtree.root, "usr", libdir, "anaconda", "addrsize") cmd = [addrsize, INITRD_ADDRESS, initrd.fpath, joinpaths(self.outputroot, IMAGESDIR, "initrd_addrsize")] p = subprocess.Popen(cmd, stdin=subprocess.PIPE, stdout=subprocess.PIPE) p.wait() # add kernel and initrd to .treeinfo kernel_arch = kernel.version.split(".")[-1] section = "images-{0}".format(kernel_arch) data = {"kernel": joinpaths(IMAGESDIR, kernel.fname), "initrd": joinpaths(IMAGESDIR, initrd.fname), "initrd.addrsize": joinpaths(IMAGESDIR, "initrd_addrsize"), "generic.prm": joinpaths(IMAGESDIR, os.path.basename(generic_prm)), "generic.ins": os.path.basename(generic_ins)} self.treeinfo.add_section(section, data) # create cdboot.img bootiso_fpath = joinpaths(self.outputroot, IMAGESDIR, "cdboot.img") # run mks390cdboot mks390cdboot = joinpaths(self.installtree.root, "usr", libdir, "anaconda", "mk-s390-cdboot") cmd = [mks390cdboot, "-i", kernel.fpath, "-r", initrd.fpath, "-p", generic_prm, "-o", bootiso_fpath] p = subprocess.Popen(cmd, stdin=subprocess.PIPE, stdout=subprocess.PIPE) # add cdboot.img to treeinfo data = {"cdboot.img": joinpaths(IMAGESDIR, "cdboot.img")} self.treeinfo.add_section(section, data) def create_boot(self, efiboot=None): pass class Factory(object): DISPATCH_MAP = {"ppc": PPC, "i386": X86, "x86_64": X86, "s390": S390, "s390x": S390} def __init__(self): pass def get_class(self, arch): if arch in self.DISPATCH_MAP: return self.DISPATCH_MAP[arch] else: raise Exception("no support for {0}".format(arch))