mirror of
https://github.com/ansible-collections/community.general.git
synced 2026-05-07 13:52:54 +00:00
iso_create: add bootable ISO support via boot_options (#11991)
* feat(iso_create): add bootable ISO support via El Torito boot_options Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * feat(changelogs): add fragment for iso_create bootable ISO support #11991 Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * Update plugins/modules/iso_create.py Co-authored-by: Felix Fontein <felix@fontein.de> --------- Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com> Co-authored-by: Felix Fontein <felix@fontein.de>
This commit is contained in:
4
changelogs/fragments/11991-iso-create-bootable.yml
Normal file
4
changelogs/fragments/11991-iso-create-bootable.yml
Normal file
@@ -0,0 +1,4 @@
|
||||
minor_changes:
|
||||
- iso_create - add ``boot_options`` parameter to support creating bootable ISOs using El Torito boot records
|
||||
(https://github.com/ansible-collections/community.general/issues/1685,
|
||||
https://github.com/ansible-collections/community.general/pull/11991).
|
||||
@@ -79,6 +79,43 @@ options:
|
||||
- If not specified or set to V(false), then no UDF support is added.
|
||||
type: bool
|
||||
default: false
|
||||
boot_options:
|
||||
description:
|
||||
- Options for making the ISO bootable using El Torito boot support.
|
||||
- If not specified, the ISO will not be bootable.
|
||||
type: dict
|
||||
version_added: 13.0.0
|
||||
suboptions:
|
||||
boot_file:
|
||||
description:
|
||||
- Local path to the boot image file to embed in the ISO.
|
||||
- The file is added to the root of the ISO and referenced as the El Torito boot image.
|
||||
- The boot image must not share a filename with any file added via O(src_files), or pycdlib will raise a duplicate entry error.
|
||||
type: path
|
||||
required: true
|
||||
boot_catalog:
|
||||
description:
|
||||
- ISO9660 path for the El Torito boot catalog file inside the ISO.
|
||||
type: str
|
||||
default: '/BOOT.CAT;1'
|
||||
media_name:
|
||||
description:
|
||||
- Media emulation type for the El Torito boot entry.
|
||||
type: str
|
||||
default: 'noemul'
|
||||
choices: ['noemul', 'floppy', '1.2m', '1.44m', '2.88m']
|
||||
platform_id:
|
||||
description:
|
||||
- Target platform for the El Torito boot entry.
|
||||
type: str
|
||||
default: 'x86'
|
||||
choices: ['x86', 'efi', 'mac']
|
||||
boot_info_table:
|
||||
description:
|
||||
- Whether to add a boot info table to the boot image.
|
||||
- Required by some bootloaders such as ISOLINUX.
|
||||
type: bool
|
||||
default: false
|
||||
"""
|
||||
|
||||
EXAMPLES = r"""
|
||||
@@ -106,6 +143,17 @@ EXAMPLES = r"""
|
||||
interchange_level: 3
|
||||
joliet: 3
|
||||
vol_ident: WIN_AUTOINSTALL
|
||||
|
||||
- name: Create a bootable ISO file using ISOLINUX
|
||||
community.general.iso_create:
|
||||
src_files:
|
||||
- /root/isocontents/isolinux.cfg
|
||||
dest_iso: /tmp/boot.iso
|
||||
interchange_level: 3
|
||||
boot_options:
|
||||
boot_file: /root/isocontents/isolinux.bin
|
||||
media_name: noemul
|
||||
boot_info_table: true
|
||||
"""
|
||||
|
||||
RETURN = r"""
|
||||
@@ -145,11 +193,43 @@ udf:
|
||||
returned: on success
|
||||
type: bool
|
||||
sample: false
|
||||
boot_options:
|
||||
description: Configured El Torito boot options, or V(null) if the ISO is not bootable.
|
||||
returned: on success
|
||||
type: dict
|
||||
version_added: 13.0.0
|
||||
contains:
|
||||
boot_file:
|
||||
description: Local path to the boot image file.
|
||||
type: str
|
||||
sample: "/root/isolinux/isolinux.bin"
|
||||
boot_catalog:
|
||||
description: ISO9660 path of the boot catalog inside the ISO.
|
||||
type: str
|
||||
sample: "/BOOT.CAT;1"
|
||||
media_name:
|
||||
description: Media emulation type.
|
||||
type: str
|
||||
sample: "noemul"
|
||||
platform_id:
|
||||
description: Target platform identifier.
|
||||
type: str
|
||||
sample: "x86"
|
||||
boot_info_table:
|
||||
description: Whether a boot info table was added to the boot image.
|
||||
type: bool
|
||||
sample: false
|
||||
"""
|
||||
|
||||
import os
|
||||
import traceback
|
||||
|
||||
PLATFORM_ID_MAP = {
|
||||
"x86": b"\x00",
|
||||
"efi": b"\xef",
|
||||
"mac": b"\x02",
|
||||
}
|
||||
|
||||
PYCDLIB_IMP_ERR = None
|
||||
try:
|
||||
import pycdlib
|
||||
@@ -213,6 +293,16 @@ def main():
|
||||
rock_ridge=dict(type="str", choices=["1.09", "1.10", "1.12"]),
|
||||
joliet=dict(type="int", choices=[1, 2, 3]),
|
||||
udf=dict(type="bool", default=False),
|
||||
boot_options=dict(
|
||||
type="dict",
|
||||
options=dict(
|
||||
boot_file=dict(type="path", required=True),
|
||||
boot_catalog=dict(type="str", default="/BOOT.CAT;1"),
|
||||
media_name=dict(type="str", default="noemul", choices=["noemul", "floppy", "1.2m", "1.44m", "2.88m"]),
|
||||
platform_id=dict(type="str", default="x86", choices=["x86", "efi", "mac"]),
|
||||
boot_info_table=dict(type="bool", default=False),
|
||||
),
|
||||
),
|
||||
)
|
||||
module = AnsibleModule(
|
||||
argument_spec=argument_spec,
|
||||
@@ -228,6 +318,12 @@ def main():
|
||||
if not os.path.exists(src_file):
|
||||
module.fail_json(msg=f"Specified source file/directory path does not exist on local machine, {src_file}")
|
||||
|
||||
boot_options = module.params.get("boot_options")
|
||||
if boot_options:
|
||||
boot_file = boot_options["boot_file"]
|
||||
if not os.path.exists(boot_file):
|
||||
module.fail_json(msg=f"Specified boot file path does not exist on local machine, {boot_file}")
|
||||
|
||||
dest_iso = module.params.get("dest_iso")
|
||||
if dest_iso and len(dest_iso) == 0:
|
||||
module.fail_json(msg="Please specify the absolute path of the new created ISO file using dest_iso parameter.")
|
||||
@@ -259,6 +355,7 @@ def main():
|
||||
rock_ridge=rock_ridge,
|
||||
joliet=use_joliet,
|
||||
udf=use_udf,
|
||||
boot_options=boot_options,
|
||||
)
|
||||
if not module.check_mode:
|
||||
iso_file = pycdlib.PyCdlib(always_consistent=True)
|
||||
@@ -319,6 +416,38 @@ def main():
|
||||
use_udf=use_udf,
|
||||
)
|
||||
|
||||
if boot_options:
|
||||
boot_file = boot_options["boot_file"]
|
||||
boot_file_basename = os.path.basename(boot_file)
|
||||
if "." not in boot_file_basename:
|
||||
boot_iso_path = f"/{boot_file_basename.upper()}.;1"
|
||||
else:
|
||||
boot_iso_path = f"/{boot_file_basename.upper()};1"
|
||||
add_file(
|
||||
module,
|
||||
iso_file=iso_file,
|
||||
src_file=boot_file,
|
||||
file_path=f"/{boot_file_basename}",
|
||||
rock_ridge=rock_ridge,
|
||||
use_joliet=use_joliet,
|
||||
use_udf=use_udf,
|
||||
)
|
||||
boot_catalog_basename = os.path.basename(boot_options["boot_catalog"]).split(";")[0].lower()
|
||||
eltorito_kwargs = dict(
|
||||
bootcatfile=boot_options["boot_catalog"],
|
||||
media_name=boot_options["media_name"],
|
||||
platform_id=PLATFORM_ID_MAP[boot_options["platform_id"]],
|
||||
boot_info_table=boot_options["boot_info_table"],
|
||||
)
|
||||
if rock_ridge:
|
||||
eltorito_kwargs["rr_bootcatfile"] = boot_catalog_basename
|
||||
if use_joliet:
|
||||
eltorito_kwargs["joliet_bootcatfile"] = f"/{boot_catalog_basename}"
|
||||
try:
|
||||
iso_file.add_eltorito(boot_iso_path, **eltorito_kwargs)
|
||||
except Exception as err:
|
||||
module.fail_json(msg=f"Failed to add El Torito boot record to ISO file: {err}")
|
||||
|
||||
iso_file.write(dest_iso)
|
||||
iso_file.close()
|
||||
|
||||
|
||||
Reference in New Issue
Block a user