diff --git a/share/ramdisk.ltmpl b/share/ramdisk.ltmpl index a8da0155..1f73a879 100644 --- a/share/ramdisk.ltmpl +++ b/share/ramdisk.ltmpl @@ -161,6 +161,7 @@ install "xorg-x11-fonts-ethiopic" install "xorg-x11-fonts-misc" install "xorg-x11-server-Xorg" install "xorg-x11-server-utils" +install "xz" install "yum-langpacks" install "${product}-logos" install "${product}-release" diff --git a/src/pylorax/__init__.py b/src/pylorax/__init__.py index d5da4296..ff9bf3bb 100644 --- a/src/pylorax/__init__.py +++ b/src/pylorax/__init__.py @@ -102,8 +102,8 @@ class Lorax(BaseLoraxClass): self.conf.set("yum", "skipbroken", "0") self.conf.add_section("compression") - self.conf.set("compression", "type", "xz") - self.conf.set("compression", "args", "-9") + self.conf.set("compression", "type", "squashfs") + self.conf.set("compression", "args", "-comp xz") # read the config file if os.path.isfile(conf_file): @@ -293,6 +293,10 @@ class Lorax(BaseLoraxClass): logger.info("moving stubs") self.installtree.move_stubs() + if self.conf.get("compression", "type") == "squashfs": + # create dracut initramfs (before stuff gets shuffled/removed) + self.installtree.make_dracut_initramfs() + # get the list of required modules logger.info("getting list of required modules") modules = [f[1:] for f in template if f[0] == "module"] diff --git a/src/pylorax/constants.py b/src/pylorax/constants.py index c37f0db9..09467412 100644 --- a/src/pylorax/constants.py +++ b/src/pylorax/constants.py @@ -48,6 +48,8 @@ class LoraxRequiredCommands(dict): self["LOSETUP"] = "losetup" self["MKDOSFS"] = "mkdosfs" self["MKISOFS"] = "mkisofs" + self["MKFS_EXT4"] = "mkfs.ext4" + self["MKSQUASHFS"] = "mksquashfs" self["MODINFO"] = "modinfo" self["MOUNT"] = "mount" self["PARTED"] = "parted" diff --git a/src/pylorax/installtree.py b/src/pylorax/installtree.py index c3bfda4e..0e49213b 100644 --- a/src/pylorax/installtree.py +++ b/src/pylorax/installtree.py @@ -46,6 +46,7 @@ class LoraxInstallTree(BaseLoraxClass): self.basearch = basearch self.libdir = libdir self.workdir = workdir + self.initramfs = None self.lcmds = constants.LoraxRequiredCommands() @@ -512,7 +513,10 @@ class LoraxInstallTree(BaseLoraxClass): shutil.move(joinpaths(self.workdir, kernel.version), joinpaths(self.root, "modules")) - self.make_initramfs_runtime(initrd, kernel, type, args) + if type == "squashfs": + self.make_squashfs_runtime(initrd, kernel, type, args) + else: + self.make_initramfs_runtime(initrd, kernel, type, args) # move modules out of the tree again logger.debug("moving modules outside initrd") @@ -543,6 +547,99 @@ class LoraxInstallTree(BaseLoraxClass): logger.debug("compressing") rc = compressed.wait() + def make_dracut_initramfs(self): + outfile = "/tmp/initramfs.img" # inside the chroot + logger.debug("chrooting into installtree to create initramfs.img") + subprocess.check_call(["chroot", self.root, + "/sbin/dracut", "--nomdadmconf", "--nolvmconf", + "--xz", "--modules", "base dmsquash-live", + outfile, self.kernels[0].version]) + # move output file into installtree workdir + self.initramfs = joinpaths(self.workdir, "initramfs.img") + shutil.move(joinpaths(self.root, outfile), self.initramfs) + + def make_squashfs_runtime(self, runtime, kernel, type, args): + """This is a little complicated, but dracut wants to find a squashfs + image named "squashfs.img" which contains a filesystem image named + "LiveOS/rootfs.img". + Placing squashfs.img inside a cpio image and concatenating that + with the existing initramfs.img will make squashfs.img appear inside + initramfs at boot time. + """ + # Check to be sure we have a dracut initramfs to use + assert self.initramfs, "make_dracut_initramfs has not been run!" + + # These exact names are required by dracut + squashname = "squashfs.img" + imgname = "LiveOS/rootfs.img" + + # Create fs image of installtree (2GB sparse file) + fsimage = joinpaths(self.workdir, "installtree.img") + open(fsimage, "wb").truncate(2*1024**3) + mountpoint = joinpaths(self.workdir, "rootfs") + os.mkdir(mountpoint, 0755) + mkfs = [self.lcmds.MKFS_EXT4, "-q", "-L", "Anaconda", "-F", fsimage] + logger.debug("formatting rootfs image: %s" % " ".join(mkfs)) + subprocess.check_call(mkfs, stdout=subprocess.PIPE) + logger.debug("mounting rootfs image at %s", mountpoint) + subprocess.check_call([self.lcmds.MOUNT, "-o", "loop", + fsimage, mountpoint]) + try: + logger.info("copying installtree into rootfs image") + srcfiles = [joinpaths(self.root, f) for f in os.listdir(self.root)] + subprocess.check_call(["cp", "-a"] + srcfiles + [mountpoint]) + finally: + logger.debug("unmounting rootfs image") + rc = subprocess.call([self.lcmds.UMOUNT, mountpoint]) + if rc != 0: + logger.critical("umount %s failed (returncode %i)", mountpoint, rc) + sys.exit(rc) + os.rmdir(mountpoint) + + # Make squashfs with rootfs image inside + logger.info("creating %s containing %s", squashname, imgname) + squashtree = joinpaths(self.workdir, "squashfs") + os.makedirs(joinpaths(squashtree, os.path.dirname(imgname))) + shutil.move(fsimage, joinpaths(squashtree, imgname)) + squashimage = joinpaths(self.workdir, squashname) + cmd = [self.lcmds.MKSQUASHFS, squashtree, squashimage] + args.split() + subprocess.check_call(cmd) + shutil.rmtree(squashtree) + + # Put squashimage in a new initramfs image with dracut config + logger.debug("creating initramfs image containing %s", squashname) + initramfsdir = joinpaths(self.workdir, "initramfs") + # write boot cmdline for dracut + cmdline = joinpaths(initramfsdir, "etc/cmdline") + os.makedirs(os.path.dirname(cmdline)) + with open(cmdline, "wb") as fobj: + fobj.write("root=live:/{0}\n".format(squashname)) + # add squashimage to new cpio image + shutil.move(squashimage, initramfsdir) + # create cpio container + squash_cpio = joinpaths(self.workdir, "squashfs.cpio") + chdir = lambda: os.chdir(initramfsdir) + find = subprocess.Popen([self.lcmds.FIND, "."], stdout=subprocess.PIPE, + preexec_fn=chdir) + cpio = subprocess.Popen([self.lcmds.CPIO, "--quiet", "-c", "-o"], + stdin=find.stdout, + stdout=open(squash_cpio, "wb"), + preexec_fn=chdir) + cpio.communicate() + shutil.rmtree(initramfsdir) + + # create final image + logger.debug("concatenating dracut initramfs and squashfs initramfs") + logger.debug("initramfs.img size = %i", os.stat(self.initramfs).st_size) + with open(runtime.fpath, "wb") as output: + for f in self.initramfs, squash_cpio: + with open(f, "rb") as fobj: + data = fobj.read(4096) + while data: + output.write(data) + data = fobj.read(4096) + os.remove(self.initramfs) + os.remove(squash_cpio) @property def kernels(self):