def mount_test(self): """Test the Mount context manager (requires loop)""" with tempfile.NamedTemporaryFile(prefix="lorax.test.disk.") as disk_img: mksparse(disk_img.name, 42 * 1024**2) runcmd(["mkfs.ext4", "-L", "Anaconda", "-b", "4096", "-m", "0", disk_img.name]) with LoopDev(disk_img.name) as loopdev: self.assertTrue(loopdev is not None) with Mount(loopdev) as mnt: self.assertTrue(mnt is not None)
def run_creator(opts, cancel_func=None): """Run the image creator process :param opts: Commandline options to control the process :type opts: Either a DataHolder or ArgumentParser :param cancel_func: Function that returns True to cancel build :type cancel_func: function :returns: The result directory and the disk image path. :rtype: Tuple of str This function takes the opts arguments and creates the selected output image. See the cmdline --help for livemedia-creator for the possible options (Yes, this is not ideal, but we can fix that later) """ result_dir = None # Parse the kickstart if opts.ks: ks_version = makeVersion() ks = KickstartParser(ks_version, errorsAreFatal=False, missingIncludeIsFatal=False) ks.readKickstart(opts.ks[0]) # live iso usually needs dracut-live so warn the user if it is missing if opts.ks and opts.make_iso: if "dracut-live" not in ks.handler.packages.packageList: log.error("dracut-live package is missing from the kickstart.") raise RuntimeError( "dracut-live package is missing from the kickstart.") # Make the disk or filesystem image if not opts.disk_image and not opts.fs_image: if not opts.ks: raise RuntimeError("Image creation requires a kickstart file") # Check the kickstart for problems errors = check_kickstart(ks, opts) if errors: list(log.error(e) for e in errors) raise RuntimeError("\n".join(errors)) # Make the image. Output of this is either a partitioned disk image or a fsimage try: disk_img = make_image(opts, ks, cancel_func=cancel_func) except InstallError as e: log.error("ERROR: Image creation failed: %s", e) raise RuntimeError("Image creation failed: %s" % e) if opts.image_only: return (result_dir, disk_img) if opts.make_iso: work_dir = tempfile.mkdtemp(prefix="lmc-work-") log.info("working dir is %s", work_dir) if (opts.fs_image or opts.no_virt) and not opts.disk_image: # Create iso from a filesystem image disk_img = opts.fs_image or disk_img if not make_squashfs(opts, disk_img, work_dir): log.error("squashfs.img creation failed") raise RuntimeError("squashfs.img creation failed") if cancel_func and cancel_func(): raise RuntimeError("ISO creation canceled") with Mount(disk_img, opts="loop") as mount_dir: result_dir = make_livecd(opts, mount_dir, work_dir) else: # Create iso from a partitioned disk image disk_img = opts.disk_image or disk_img with PartitionMount(disk_img) as img_mount: if img_mount and img_mount.mount_dir: make_runtime(opts, img_mount.mount_dir, work_dir, calculate_disk_size(opts, ks) / 1024.0) result_dir = make_livecd(opts, img_mount.mount_dir, work_dir) # --iso-only removes the extra build artifacts, keeping only the boot.iso if opts.iso_only and result_dir: boot_iso = joinpaths(result_dir, "images/boot.iso") if not os.path.exists(boot_iso): log.error("%s is missing, skipping --iso-only.", boot_iso) else: iso_dir = tempfile.mkdtemp(prefix="lmc-result-") dest_file = joinpaths(iso_dir, opts.iso_name or "boot.iso") shutil.move(boot_iso, dest_file) shutil.rmtree(result_dir) result_dir = iso_dir # cleanup the mess # cleanup work_dir? if disk_img and not (opts.keep_image or opts.disk_image or opts.fs_image): os.unlink(disk_img) log.info("Disk image erased") disk_img = None elif opts.make_appliance: if not opts.ks: networks = [] else: networks = ks.handler.network.network make_appliance(opts.disk_image or disk_img, opts.app_name, opts.app_template, opts.app_file, networks, opts.ram, opts.vcpus or 1, opts.arch, opts.title, opts.project, opts.releasever) elif opts.make_pxe_live: work_dir = tempfile.mkdtemp(prefix="lmc-work-") log.info("working dir is %s", work_dir) disk_img = opts.fs_image or opts.disk_image or disk_img log.debug("disk image is %s", disk_img) result_dir = make_live_images(opts, work_dir, disk_img) if result_dir is None: log.error("Creating PXE live image failed.") raise RuntimeError("Creating PXE live image failed.") if opts.result_dir != opts.tmp and result_dir: copytree(result_dir, opts.result_dir, preserve=False) shutil.rmtree(result_dir) result_dir = None return (result_dir, disk_img)
def make_live_images(opts, work_dir, disk_img): """ Create live images from direcory or rootfs image :param opts: options passed to livemedia-creator :type opts: argparse options :param str work_dir: Directory for storing results :param str disk_img: Path to disk image (fsimage or partitioned) :returns: Path of directory with created images or None :rtype: str fsck.ext4 is run on the rootfs_image to make sure there are no errors and to zero out any deleted blocks to make it compress better. If this fails for any reason it will return None and log the error. """ sys_root = "" squashfs_root_dir = joinpaths(work_dir, "squashfs_root") liveos_dir = joinpaths(squashfs_root_dir, "LiveOS") os.makedirs(liveos_dir) rootfs_img = joinpaths(liveos_dir, "rootfs.img") if opts.fs_image or opts.no_virt: # Find the ostree root in the fsimage if opts.ostree: with Mount(disk_img, opts="loop") as mnt_dir: sys_root = find_ostree_root(mnt_dir) # Try to hardlink the image, if that fails, copy it rc = execWithRedirect("/bin/ln", [disk_img, rootfs_img]) if rc != 0: shutil.copy2(disk_img, rootfs_img) else: is_root_part = None if opts.ostree: is_root_part = lambda dir: os.path.exists(dir + "/ostree/deploy") with PartitionMount(disk_img, mount_ok=is_root_part) as img_mount: if img_mount and img_mount.mount_dir: try: mounted_sysroot_boot_dir = None if opts.ostree: sys_root = find_ostree_root(img_mount.mount_dir) mounted_sysroot_boot_dir = mount_boot_part_over_root( img_mount) if opts.live_rootfs_keep_size: size = img_mount.mount_size / 1024**3 else: size = opts.live_rootfs_size or None log.info("Creating live rootfs image") mkrootfsimg(img_mount.mount_dir, rootfs_img, "LiveOS", size=size, sysroot=sys_root) finally: if mounted_sysroot_boot_dir: umount(mounted_sysroot_boot_dir) log.debug("sys_root = %s", sys_root) # Make sure free blocks are actually zeroed so it will compress rc = execWithRedirect("/usr/sbin/fsck.ext4", ["-y", "-f", "-E", "discard", rootfs_img]) if rc != 0: log.error("Problem zeroing free blocks of %s", disk_img) return None log.info("Packing live rootfs image") add_pxe_args = [] live_image_name = "live-rootfs.squashfs.img" compression, compressargs = squashfs_args(opts) mksquashfs(squashfs_root_dir, joinpaths(work_dir, live_image_name), compression, compressargs) log.info("Rebuilding initramfs for live") with Mount(rootfs_img, opts="loop") as mnt_dir: try: mount(joinpaths(mnt_dir, "boot"), opts="bind", mnt=joinpaths(mnt_dir, sys_root, "boot")) rebuild_initrds_for_live(opts, joinpaths(mnt_dir, sys_root), work_dir) finally: umount(joinpaths(mnt_dir, sys_root, "boot"), delete=False) remove(squashfs_root_dir) if opts.ostree: add_pxe_args.append("ostree=/%s" % sys_root) template = joinpaths(opts.lorax_templates, "pxe-live/pxe-config.tmpl") create_pxe_config(template, work_dir, live_image_name, add_pxe_args) return work_dir
def run_creator(opts, callback_func=None): """Run the image creator process :param opts: Commandline options to control the process :type opts: Either a DataHolder or ArgumentParser :returns: The result directory and the disk image path. :rtype: Tuple of str This function takes the opts arguments and creates the selected output image. See the cmdline --help for livemedia-creator for the possible options (Yes, this is not ideal, but we can fix that later) """ result_dir = None # Parse the kickstart if opts.ks: ks_version = makeVersion() ks = KickstartParser(ks_version, errorsAreFatal=False, missingIncludeIsFatal=False) ks.readKickstart(opts.ks[0]) # live iso usually needs dracut-live so warn the user if it is missing if opts.ks and opts.make_iso: if "dracut-live" not in ks.handler.packages.packageList: log.error("dracut-live package is missing from the kickstart.") raise RuntimeError("dracut-live package is missing from the kickstart.") # Make the disk or filesystem image if not opts.disk_image and not opts.fs_image: if not opts.ks: raise RuntimeError("Image creation requires a kickstart file") errors = [] if opts.no_virt and ks.handler.method.method not in ("url", "nfs") \ and not ks.handler.ostreesetup.seen: errors.append("Only url, nfs and ostreesetup install methods are currently supported." "Please fix your kickstart file." ) if ks.handler.method.method in ("url", "nfs") and not ks.handler.network.seen: errors.append("The kickstart must activate networking if " "the url or nfs install method is used.") if ks.handler.displaymode.displayMode is not None: errors.append("The kickstart must not set a display mode (text, cmdline, " "graphical), this will interfere with livemedia-creator.") if opts.make_fsimage or (opts.make_pxe_live and opts.no_virt): # Make sure the kickstart isn't using autopart and only has a / mountpoint part_ok = not any(p for p in ks.handler.partition.partitions if p.mountpoint not in ["/", "swap"]) if not part_ok or ks.handler.autopart.seen: errors.append("Filesystem images must use a single / part, not autopart or " "multiple partitions. swap is allowed but not used.") if not opts.no_virt and ks.handler.reboot.action != KS_SHUTDOWN: errors.append("The kickstart must include shutdown when using virt installation.") if errors: list(log.error(e) for e in errors) raise RuntimeError("\n".join(errors)) # Make the image. Output of this is either a partitioned disk image or a fsimage try: disk_img = make_image(opts, ks) except InstallError as e: log.error("ERROR: Image creation failed: %s", e) raise RuntimeError("Image creation failed: %s" % e) if opts.image_only: return (result_dir, disk_img) if opts.make_iso: work_dir = tempfile.mkdtemp(prefix="lmc-work-") log.info("working dir is %s", work_dir) if (opts.fs_image or opts.no_virt) and not opts.disk_image: # Create iso from a filesystem image disk_img = opts.fs_image or disk_img if not make_squashfs(opts, disk_img, work_dir): log.error("squashfs.img creation failed") raise RuntimeError("squashfs.img creation failed") with Mount(disk_img, opts="loop") as mount_dir: result_dir = make_livecd(opts, mount_dir, work_dir) else: # Create iso from a partitioned disk image disk_img = opts.disk_image or disk_img with PartitionMount(disk_img) as img_mount: if img_mount and img_mount.mount_dir: make_runtime(opts, img_mount.mount_dir, work_dir, calculate_disk_size(opts, ks)/1024.0) result_dir = make_livecd(opts, img_mount.mount_dir, work_dir) # --iso-only removes the extra build artifacts, keeping only the boot.iso if opts.iso_only and result_dir: boot_iso = joinpaths(result_dir, "images/boot.iso") if not os.path.exists(boot_iso): log.error("%s is missing, skipping --iso-only.", boot_iso) else: iso_dir = tempfile.mkdtemp(prefix="lmc-result-") dest_file = joinpaths(iso_dir, opts.iso_name or "boot.iso") shutil.move(boot_iso, dest_file) shutil.rmtree(result_dir) result_dir = iso_dir # cleanup the mess # cleanup work_dir? if disk_img and not (opts.keep_image or opts.disk_image or opts.fs_image): os.unlink(disk_img) log.info("Disk image erased") disk_img = None elif opts.make_appliance: if not opts.ks: networks = [] else: networks = ks.handler.network.network make_appliance(opts.disk_image or disk_img, opts.app_name, opts.app_template, opts.app_file, networks, opts.ram, opts.vcpus or 1, opts.arch, opts.title, opts.project, opts.releasever) elif opts.make_pxe_live: work_dir = tempfile.mkdtemp(prefix="lmc-work-") log.info("working dir is %s", work_dir) disk_img = opts.fs_image or opts.disk_image or disk_img log.debug("disk image is %s", disk_img) result_dir = make_live_images(opts, work_dir, disk_img) if result_dir is None: log.error("Creating PXE live image failed.") raise RuntimeError("Creating PXE live image failed.") if opts.result_dir != opts.tmp and result_dir: copytree(result_dir, opts.result_dir, preserve=False) shutil.rmtree(result_dir) result_dir = None return (result_dir, disk_img)