iso: Extract volume id with xorriso if available

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ář <lsedlar@redhat.com>
This commit is contained in:
Lubomír Sedlář 2024-04-22 08:38:46 +02:00
parent 5c9e79f535
commit bc0334cc09
6 changed files with 54 additions and 15 deletions

View File

@ -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)

View File

@ -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":

View File

@ -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")

View File

@ -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,
[

View File

@ -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]

View File

@ -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):