From bc0334cc096f2c8f83fdfbe8b99106fc18fdb7ed Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lubom=C3=ADr=20Sedl=C3=A1=C5=99?= Date: Mon, 22 Apr 2024 08:38:46 +0200 Subject: [PATCH] iso: Extract volume id with xorriso if available MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Pungi can use either genisoimage or xorriso to create ISOs. It also needed isoinfo utility for querying volume ID from the ISO image. However, the utility is part of the genisoimage suite of tools. On systems that no longer provide genisoimage, the image would be successfully generate with xorriso, but then pungi would fail to extract the volume id leading to metadata with missing values. Signed-off-by: Lubomír Sedlář --- pungi/phases/buildinstall.py | 5 ++++- pungi/phases/createiso.py | 5 ++++- pungi/wrappers/iso.py | 21 ++++++++++++++------- tests/test_buildinstall.py | 4 ++-- tests/test_createiso_phase.py | 16 ++++++++++++---- tests/test_iso_wrapper.py | 18 ++++++++++++++++++ 6 files changed, 54 insertions(+), 15 deletions(-) diff --git a/pungi/phases/buildinstall.py b/pungi/phases/buildinstall.py index cbbf0dac..49c05d11 100644 --- a/pungi/phases/buildinstall.py +++ b/pungi/phases/buildinstall.py @@ -521,7 +521,10 @@ def link_boot_iso(compose, arch, variant, can_fail): setattr(img, "can_fail", can_fail) setattr(img, "deliverable", "buildinstall") try: - img.volume_id = iso.get_volume_id(new_boot_iso_path) + img.volume_id = iso.get_volume_id( + new_boot_iso_path, + compose.conf.get("createiso_use_xorrisofs"), + ) except RuntimeError: pass compose.im.add(variant.uid, arch, img) diff --git a/pungi/phases/createiso.py b/pungi/phases/createiso.py index 8890d847..dd197899 100644 --- a/pungi/phases/createiso.py +++ b/pungi/phases/createiso.py @@ -524,7 +524,10 @@ def add_iso_to_metadata( setattr(img, "can_fail", compose.can_fail(variant, arch, "iso")) setattr(img, "deliverable", "iso") try: - img.volume_id = iso.get_volume_id(iso_path) + img.volume_id = iso.get_volume_id( + iso_path, + compose.conf.get("createiso_use_xorrisofs"), + ) except RuntimeError: pass if arch == "src": diff --git a/pungi/wrappers/iso.py b/pungi/wrappers/iso.py index 0b46ea54..ab273027 100644 --- a/pungi/wrappers/iso.py +++ b/pungi/wrappers/iso.py @@ -280,14 +280,21 @@ def get_manifest_cmd(iso_name, xorriso=False, output_file=None): ) -def get_volume_id(path): - cmd = ["isoinfo", "-d", "-i", path] - retcode, output = run(cmd, universal_newlines=True) +def get_volume_id(path, xorriso=False): + if xorriso: + cmd = ["xorriso", "-indev", path] + retcode, output = run(cmd, universal_newlines=True) + for line in output.splitlines(): + if line.startswith("Volume id"): + return line.split("'")[1] + else: + cmd = ["isoinfo", "-d", "-i", path] + retcode, output = run(cmd, universal_newlines=True) - for line in output.splitlines(): - line = line.strip() - if line.startswith("Volume id:"): - return line[11:].strip() + for line in output.splitlines(): + line = line.strip() + if line.startswith("Volume id:"): + return line[11:].strip() raise RuntimeError("Could not read Volume ID") diff --git a/tests/test_buildinstall.py b/tests/test_buildinstall.py index 28a169b6..bea99747 100644 --- a/tests/test_buildinstall.py +++ b/tests/test_buildinstall.py @@ -1859,7 +1859,7 @@ class TestSymlinkIso(PungiTestCase): ) self.assertEqual(iso.get_implanted_md5.mock_calls, [mock.call(tgt)]) self.assertEqual(iso.get_manifest_cmd.mock_calls, [mock.call("image-name")]) - self.assertEqual(iso.get_volume_id.mock_calls, [mock.call(tgt)]) + self.assertEqual(iso.get_volume_id.mock_calls, [mock.call(tgt, None)]) self.assertEqual( run.mock_calls, [ @@ -1924,7 +1924,7 @@ class TestSymlinkIso(PungiTestCase): ) self.assertEqual(iso.get_implanted_md5.mock_calls, [mock.call(tgt)]) self.assertEqual(iso.get_manifest_cmd.mock_calls, [mock.call("image-name")]) - self.assertEqual(iso.get_volume_id.mock_calls, [mock.call(tgt)]) + self.assertEqual(iso.get_volume_id.mock_calls, [mock.call(tgt, None)]) self.assertEqual( run.mock_calls, [ diff --git a/tests/test_createiso_phase.py b/tests/test_createiso_phase.py index 02ff8c90..d135f811 100644 --- a/tests/test_createiso_phase.py +++ b/tests/test_createiso_phase.py @@ -613,7 +613,9 @@ class CreateisoThreadTest(helpers.PungiTestCase): iso.get_implanted_md5.call_args_list, [mock.call(cmd["iso_path"], logger=compose._logger)], ) - self.assertEqual(iso.get_volume_id.call_args_list, [mock.call(cmd["iso_path"])]) + self.assertEqual( + iso.get_volume_id.call_args_list, [mock.call(cmd["iso_path"], False)] + ) self.assertEqual(len(compose.im.add.call_args_list), 1) args, _ = compose.im.add.call_args_list[0] @@ -696,7 +698,9 @@ class CreateisoThreadTest(helpers.PungiTestCase): iso.get_implanted_md5.call_args_list, [mock.call(cmd["iso_path"], logger=compose._logger)], ) - self.assertEqual(iso.get_volume_id.call_args_list, [mock.call(cmd["iso_path"])]) + self.assertEqual( + iso.get_volume_id.call_args_list, [mock.call(cmd["iso_path"], False)] + ) self.assertEqual(len(compose.im.add.call_args_list), 2) for args, _ in compose.im.add.call_args_list: @@ -787,7 +791,9 @@ class CreateisoThreadTest(helpers.PungiTestCase): iso.get_implanted_md5.call_args_list, [mock.call(cmd["iso_path"], logger=compose._logger)], ) - self.assertEqual(iso.get_volume_id.call_args_list, [mock.call(cmd["iso_path"])]) + self.assertEqual( + iso.get_volume_id.call_args_list, [mock.call(cmd["iso_path"], False)] + ) self.assertEqual(len(compose.im.add.call_args_list), 1) args, _ = compose.im.add.call_args_list[0] @@ -968,7 +974,9 @@ class CreateisoThreadTest(helpers.PungiTestCase): iso.get_implanted_md5.call_args_list, [mock.call(cmd["iso_path"], logger=compose._logger)], ) - self.assertEqual(iso.get_volume_id.call_args_list, [mock.call(cmd["iso_path"])]) + self.assertEqual( + iso.get_volume_id.call_args_list, [mock.call(cmd["iso_path"], False)] + ) self.assertEqual(len(compose.im.add.call_args_list), 1) args, _ = compose.im.add.call_args_list[0] diff --git a/tests/test_iso_wrapper.py b/tests/test_iso_wrapper.py index 8338007f..04c0eb8f 100644 --- a/tests/test_iso_wrapper.py +++ b/tests/test_iso_wrapper.py @@ -24,6 +24,19 @@ Supported ISO: no INCORRECT_OUTPUT = """This should never happen: File not found""" +XORRISO_LOAD_OUTPUT = """\ +xorriso 1.5.4 : RockRidge filesystem manipulator, libburnia project. + +xorriso : NOTE : Loading ISO image tree from LBA 0 +xorriso : UPDATE : 7074 nodes read in 1 seconds +Drive current: -indev 'dummy.iso' +Media current: stdio file, overwriteable +Media status : is written , is appendable +Boot record : El Torito , MBR isohybrid cyl-align-off GPT +Media summary: 1 session, 5415454 data blocks, 10.3g data, 4086g free +Volume id : 'My volume id' +""" + # Cached to use in tests that mock os.listdir orig_listdir = os.listdir @@ -187,6 +200,11 @@ class TestIsoUtils(unittest.TestCase): self.assertEqual(len(mock_unmount.call_args_list), 0) self.assertEqual(len(log.mock_calls), 1) + @mock.patch("pungi.wrappers.iso.run") + def test_get_volume_id_xorriso(self, mock_run): + mock_run.return_value = (0, XORRISO_LOAD_OUTPUT) + self.assertEqual(iso.get_volume_id("/dummy.iso", True), "My volume id") + class TestCmpGraftPoints(unittest.TestCase): def assertSorted(self, *args):