def get_extra_pkgs(dbo, share_dir, compose_type): """Return extra packages needed for the output type :param dbo: dnf base object :type dbo: dnf.Base :param share_dir: Path to the top level share directory :type share_dir: str :param compose_type: The type of output to create from the recipe :type compose_type: str :returns: List of package names (name only, not NEVRA) :rtype: list Currently this is only needed by live-iso, it reads ./live/live-install.tmpl and processes only the installpkg lines. It lists the packages needed to complete creation of the iso using the templates such as x86.tmpl Keep in mind that the live-install.tmpl is shared between livemedia-creator and lorax-composer, even though the results are applied differently. """ if compose_type != "live-iso": return [] # get the arch information to pass to the runner arch = ArchData(get_buildarch(dbo)) defaults = DataHolder(basearch=arch.basearch) templatedir = joinpaths(find_templates(share_dir), "live") runner = LiveTemplateRunner(dbo, templatedir=templatedir, defaults=defaults) runner.run("live-install.tmpl") log.debug("extra pkgs = %s", runner.pkgs) return runner.pkgnames
def test_make_appliance(self): """Test creating the appliance description XML file""" lorax_templates = find_templates("./share/") appliance_template = joinpaths(lorax_templates, "appliance/libvirt.tmpl") self.assertTrue(os.path.exists(appliance_template)) # A fake disk image with tempfile.NamedTemporaryFile(prefix="lorax.test.disk.") as disk_img: with open(disk_img.name, "wb") as f: f.write(b"THIS IS A FAKE DISK IMAGE FILE") with tempfile.NamedTemporaryFile(prefix="lorax.test.appliance.") as output_xml: make_appliance(disk_img.name, "test-appliance", appliance_template, output_xml.name, ["eth0", "eth1"], ram=4096, vcpus=8, arch="x86_64", title="Lorax Test", project="Fedora", releasever="30") with open(output_xml.name) as f: print(f.read()) # Parse the XML and check for known fields tree = ET.parse(output_xml.name) image = tree.getroot() self.assertEqual(image.find("name").text, "test-appliance") boot = image.find("./domain/boot") self.assertEqual(boot.get("type"), "hvm") self.assertEqual(boot.find("./guest/arch").text, "x86_64") self.assertEqual(boot.find("./os/loader").get("dev"), "hd") self.assertTrue(boot.find("drive").get("disk").startswith("lorax.test.disk.")) self.assertEqual(boot.find("drive").get("target"), "hda") devices = image.find("./domain/devices") self.assertEqual(devices.find("vcpu").text, "8") self.assertEqual(devices.find("memory").text, "4096") self.assertTrue(len(devices.findall("interface")), 2) storage = image.find("storage") self.assertTrue(storage.find("disk").get("file").startswith("lorax.test.disk.")) self.assertEqual(storage.find("./disk/checksum").get("type"), "sha256") self.assertEqual(storage.find("./disk/checksum").text, "90611458b33009998f73e25ccc3766b31a8b548cc6c2d84f78ae0e84d64e10a5")
def test_make_livecd_dracut(self): """Test the make_livecd function with dracut options""" with tempfile.TemporaryDirectory(prefix="lorax.test.") as tmpdir: # Make a fake kernel and initrd mkFakeBoot(joinpaths(tmpdir, "mount_dir")) os.makedirs(joinpaths(tmpdir, "mount_dir/tmp/config_files")) lorax_templates = os.path.abspath(find_templates("./share/")) with mock.patch('pylorax.treebuilder.TreeBuilder.build'): with mock.patch( 'pylorax.treebuilder.TreeBuilder.rebuild_initrds' ) as ri: # Test with no dracut args opts = DataHolder(project="Fedora", releasever="32", lorax_templates=lorax_templates, volid=None, domacboot=False, extra_boot_args="", dracut_args=None, dracut_conf=None) make_livecd(opts, joinpaths(tmpdir, "mount_dir"), joinpaths(tmpdir, "work_dir")) ri.assert_called_with(add_args=DRACUT_DEFAULT) # Test with --dracut-arg opts = DataHolder( project="Fedora", releasever="32", lorax_templates=lorax_templates, volid=None, domacboot=False, extra_boot_args="", dracut_args=[ "--xz", "--omit plymouth", "--add livenet dmsquash-live dmsquash-live-ntfs" ], dracut_conf=None) make_livecd(opts, joinpaths(tmpdir, "mount_dir"), joinpaths(tmpdir, "work_dir")) ri.assert_called_with(add_args=[ "--xz", "--omit", "plymouth", "--add", "livenet dmsquash-live dmsquash-live-ntfs" ]) # Test with --dracut-conf opts = DataHolder( project="Fedora", releasever="32", lorax_templates=lorax_templates, volid=None, domacboot=False, extra_boot_args="", dracut_args=None, dracut_conf="/var/tmp/project/lmc-dracut.conf") make_livecd(opts, joinpaths(tmpdir, "mount_dir"), joinpaths(tmpdir, "work_dir")) ri.assert_called_with(add_args=[ "--conf", "/var/tmp/project/lmc-dracut.conf" ])
def test_pxe_config(self): """Test creation of a PXE config file""" with tempfile.TemporaryDirectory(prefix="lorax.test.") as work_dir: live_image_name = "live-rootfs.squashfs.img" add_pxe_args = ["ostree=/mnt/sysimage/"] lorax_templates = find_templates("./share/") template = joinpaths(lorax_templates, "pxe-live/pxe-config.tmpl") # Make a fake kernel and initrd with open(joinpaths(work_dir, "vmlinuz-4.18.13-200.fc28.x86_64"), "w") as f: f.write("I AM A FAKE KERNEL") with open(joinpaths(work_dir, "initramfs-4.18.13-200.fc28.x86_64.img"), "w") as f: f.write("I AM A FAKE INITRD") # Create the PXE_CONFIG in work_dir create_pxe_config(template, work_dir, live_image_name, add_pxe_args) with open(joinpaths(work_dir, "PXE_CONFIG")) as f: pxe_config = f.read() print(pxe_config) self.assertTrue("vmlinuz-4.18.13-200.fc28.x86_64" in pxe_config) self.assertTrue("initramfs-4.18.13-200.fc28.x86_64.img" in pxe_config) self.assertTrue("/live-rootfs.squashfs.img ostree=/mnt/sysimage/" in pxe_config)
def make_compose(cfg, results_dir): """Run anaconda with the final-kickstart.ks from results_dir :param cfg: Configuration settings :type cfg: DataHolder :param results_dir: The directory containing the metadata and results for the build :type results_dir: str :returns: Nothing :raises: May raise various exceptions This takes the final-kickstart.ks, and the settings in config.toml and runs Anaconda in no-virt mode (directly on the host operating system). Exceptions should be caught at the higer level. If there is a failure, the build artifacts will be cleaned up, and any logs will be moved into logs/anaconda/ and their ownership will be set to the user from the cfg object. """ # Check on the ks's presence ks_path = joinpaths(results_dir, "final-kickstart.ks") if not os.path.exists(ks_path): raise RuntimeError("Missing kickstart file at %s" % ks_path) # The anaconda logs are copied into ./anaconda/ in this directory log_dir = joinpaths(results_dir, "logs/") if not os.path.exists(log_dir): os.makedirs(log_dir) # Load the compose configuration cfg_path = joinpaths(results_dir, "config.toml") if not os.path.exists(cfg_path): raise RuntimeError("Missing config.toml for %s" % results_dir) cfg_dict = toml.loads(open(cfg_path, "r").read()) # The keys in cfg_dict correspond to the arguments setup in livemedia-creator # keys that define what to build should be setup in compose_args, and keys with # defaults should be setup here. # Make sure that image_name contains no path components cfg_dict["image_name"] = os.path.basename(cfg_dict["image_name"]) # Only support novirt installation, set some other defaults cfg_dict["no_virt"] = True cfg_dict["disk_image"] = None cfg_dict["fs_image"] = None cfg_dict["keep_image"] = False cfg_dict["domacboot"] = False cfg_dict["anaconda_args"] = "" cfg_dict["proxy"] = "" cfg_dict["armplatform"] = "" cfg_dict["squashfs_args"] = None cfg_dict["lorax_templates"] = find_templates(cfg.share_dir) cfg_dict["tmp"] = cfg.tmp cfg_dict["dracut_args"] = None # Use default args for dracut # TODO How to support other arches? cfg_dict["arch"] = None # Compose things in a temporary directory inside the results directory cfg_dict["result_dir"] = joinpaths(results_dir, "compose") os.makedirs(cfg_dict["result_dir"]) install_cfg = DataHolder(**cfg_dict) # Some kludges for the 99-copy-logs %post, failure in it will crash the build for f in ["/tmp/NOSAVE_INPUT_KS", "/tmp/NOSAVE_LOGS"]: open(f, "w") # Placing a CANCEL file in the results directory will make execWithRedirect send anaconda a SIGTERM def cancel_build(): return os.path.exists(joinpaths(results_dir, "CANCEL")) log.debug("cfg = %s", install_cfg) try: test_path = joinpaths(results_dir, "TEST") if os.path.exists(test_path): # Pretend to run the compose time.sleep(10) try: test_mode = int(open(test_path, "r").read()) except Exception: test_mode = 1 if test_mode == 1: raise RuntimeError("TESTING FAILED compose") else: open(joinpaths(results_dir, install_cfg.image_name), "w").write("TEST IMAGE") else: run_creator(install_cfg, callback_func=cancel_build) # Extract the results of the compose into results_dir and cleanup the compose directory move_compose_results(install_cfg, results_dir) finally: # Make sure any remaining temporary directories are removed (eg. if there was an exception) for d in glob(joinpaths(cfg.tmp, "lmc-*")): if os.path.isdir(d): shutil.rmtree(d) elif os.path.isfile(d): os.unlink(d) # Make sure that everything under the results directory is owned by the user user = pwd.getpwuid(cfg.uid).pw_name group = grp.getgrgid(cfg.gid).gr_name log.debug("Install finished, chowning results to %s:%s", user, group) subprocess.call(["chown", "-R", "%s:%s" % (user, group), results_dir])