Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
59 changes: 40 additions & 19 deletions cobbler/actions/buildiso/netboot.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,9 @@
from cobbler.actions import buildiso
from cobbler.actions.buildiso import BootFilesCopyset, LoaderCfgsParts
from cobbler.enums import Archs
from cobbler.items.profile import Profile
from cobbler.items.system import System


class AppendLineBuilder:
"""
Expand Down Expand Up @@ -465,37 +468,37 @@ def make_shorter(self, distname: str) -> str:
return str(self.distctr)

def _generate_boot_loader_configs(
self, profile_names: List[str], system_names: List[str], exclude_dns: bool
self, profiles: List[Profile], systems: List[System], exclude_dns: bool
) -> LoaderCfgsParts:
"""Generate boot loader configuration.

The configuration is placed as parts into a list. The elements expect to
be joined by newlines for writing.

:param profile_names: Profile filter, can be an empty list for "all profiles".
:param system_names: System filter, can be an empty list for "all systems".
:param profiles: List of profiles to prepare.
:param systems: List of systems to prepare.
:param exclude_dns: Used for system kernel cmdline.
"""
loader_config_parts = LoaderCfgsParts([self.iso_template], [], [])
loader_config_parts.isolinux.append("MENU SEPARATOR")
self._generate_profiles_loader_configs(profile_names, loader_config_parts)
self._generate_systems_loader_configs(
system_names, exclude_dns, loader_config_parts
)

self._generate_profiles_loader_configs(profiles, loader_config_parts)
self._generate_systems_loader_configs(systems, exclude_dns, loader_config_parts)

return loader_config_parts

def _generate_profiles_loader_configs(
self, profiles: List[str], loader_cfg_parts: LoaderCfgsParts
self, profiles: List[Profile], loader_cfg_parts: LoaderCfgsParts
) -> None:
"""Generate isolinux configuration for profiles.

The passed in isolinux_cfg_parts list is changed in-place.

:param profiles: Profile filter, can be empty for "all profiles".
:param profiles: List of profiles to prepare.
:param isolinux_cfg_parts: Output parameter for isolinux configuration.
:param bootfiles_copyset: Output parameter for bootfiles copyset.
"""
for profile in self.filter_profiles(profiles):
for profile in profiles:
isolinux, grub, to_copy = self._generate_profile_config(profile)
loader_cfg_parts.isolinux.append(isolinux)
loader_cfg_parts.grub.append(grub)
Expand Down Expand Up @@ -544,19 +547,19 @@ def _generate_profile_config(self, profile) -> Tuple[str, str, BootFilesCopyset]

def _generate_systems_loader_configs(
self,
system_names: List[str],
systems: List[System],
exclude_dns: bool,
loader_cfg_parts: LoaderCfgsParts,
) -> None:
"""Generate isolinux configuration for systems.

The passed in isolinux_cfg_parts list is changed in-place.

:param systems: System filter, can be empty for "all profiles".
:param systems: List of systems to prepare
:param isolinux_cfg_parts: Output parameter for isolinux configuration.
:param bootfiles_copyset: Output parameter for bootfiles copyset.
"""
for system in self.filter_systems(system_names):
for system in systems:
isolinux, grub, to_copy = self._generate_system_config(
system, exclude_dns=exclude_dns
)
Expand Down Expand Up @@ -611,7 +614,6 @@ def _copy_esp(self, esp_source: str, buildisodir: str):
"""Copy existing EFI System Partition into the buildisodir."""
utils.copyfile(esp_source, buildisodir + "/efi")


def run(
self,
iso: str = "autoinst.iso",
Expand All @@ -621,6 +623,7 @@ def run(
distro_name: str = "",
systems: List[str] = None,
exclude_dns: bool = False,
exclude_systems: bool = False,
**kwargs,
):
"""
Expand All @@ -634,13 +637,33 @@ def run(
:param buildisodir: This overwrites the directory from the settings in which the iso is built in.
:param profiles: The filter to generate the ISO only for selected profiles.
:param xorrisofs_opts: ``xorrisofs`` options to include additionally.
:param distro_name: For detecting the architecture of the ISO.
:param distro_name: For detecting the architecture of the ISO. If not set, taken from first profile or system item
:param systems: Don't use that when building standalone ISOs. The filter to generate the ISO only for selected
systems.
:param exclude_dns: Whether the repositories have to be locally available or the internet is reachable.
:param exclude_systems: Whether system entries should not be exported.
"""
del kwargs # just accepted for polymorphism
distro_obj = self.parse_distro(distro_name)

system_names = utils.input_string_or_list_no_inherit(systems)
profile_names = utils.input_string_or_list_no_inherit(profiles)

profile_list = list(self.filter_profiles(profile_names))
system_list = list()
if not exclude_systems:
system_list = list(self.filter_systems(system_names))

distro_obj = None
if distro_name:
distro_obj = self.parse_distro(distro_name)
elif len(profile_list) > 0:
distro_obj = profile_list[0].get_conceptual_parent()
elif len(system_list) > 0:
distro_obj = system_list[0].get_conceptual_parent()

if distro_obj is None:
raise ValueError("Unable to find suitable distro and none set by caller")

if distro_obj.arch not in (
Archs.X86_64,
Archs.PPC,
Expand All @@ -652,10 +675,8 @@ def run(
"cobbler buildiso does not work for arch={distro_obj.arch}"
)

system_names = utils.input_string_or_list_no_inherit(systems)
profile_names = utils.input_string_or_list_no_inherit(profiles)
loader_config_parts = self._generate_boot_loader_configs(
profile_names, system_names, exclude_dns
profile_list, system_list, exclude_dns
)
buildisodir = self._prepare_buildisodir(buildisodir)
buildiso_dirs = None
Expand Down
4 changes: 3 additions & 1 deletion cobbler/api.py
Original file line number Diff line number Diff line change
Expand Up @@ -1907,7 +1907,7 @@ def authorize(self, user: str, resource: str, arg1=None, arg2=None) -> int:

def build_iso(self, iso: str = "autoinst.iso", profiles=None, systems=None, buildisodir: str = "",
distro_name: str = "", standalone: bool = False, airgapped: bool = False, source: str = "",
exclude_dns: bool = False, xorrisofs_opts: str = ""):
exclude_dns: bool = False, exclude_systems: bool = False, xorrisofs_opts: str = ""):
r"""
Build an iso image which may be network bootable or not.

Expand All @@ -1921,6 +1921,7 @@ def build_iso(self, iso: str = "autoinst.iso", profiles=None, systems=None, buil
:param airgapped: This option implies ``standalone=True``.
:param source: If the iso should be offline available this is the path to the sources of the image.
:param exclude_dns: Whether the repositories have to be locally available or the internet is reachable.
:param exclude_systems: Whether system entries should be skipped or generated.
:param xorrisofs_opts: ``xorrisofs`` options to include additionally.
"""
if not isinstance(standalone, bool):
Expand All @@ -1940,6 +1941,7 @@ def build_iso(self, iso: str = "autoinst.iso", profiles=None, systems=None, buil
source=source,
systems=systems,
exclude_dns=exclude_dns,
exclude_systems=exclude_systems,
)

# ==========================================================================
Expand Down
4 changes: 3 additions & 1 deletion cobbler/cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -1103,7 +1103,7 @@ def direct_command(self, action_name: str):
self.parser.add_option("--systems", dest="systems", help="(OPTIONAL) use these systems only")
self.parser.add_option("--tempdir", dest="buildisodir", help="(OPTIONAL) working directory")
self.parser.add_option("--distro", dest="distro",
help="Must be specified to choose the Kernel and Initrd for the ISO being built.")
help="(OPTIONAL) choose the Kernel and Initrd for the ISO being built.")
self.parser.add_option("--standalone", dest="standalone", action="store_true",
help="(OPTIONAL) creates a standalone ISO with all required distro files, "
"but without any added repos")
Expand All @@ -1116,6 +1116,8 @@ def direct_command(self, action_name: str):
self.parser.add_option("--exclude-dns", dest="exclude_dns", action="store_true",
help="(OPTIONAL) prevents addition of name server addresses to the kernel boot"
"options")
self.parser.add_option("--exclude-systems", dest="exclude_systems", action="store_true",
help="(OPTIONAL) prevents writing system records")
self.parser.add_option("--mkisofs-opts", dest="mkisofs_opts", help="(OPTIONAL) extra options for xorrisofs")

(options, args) = self.parser.parse_args(self.args)
Expand Down
1 change: 1 addition & 0 deletions cobbler/remote.py
Original file line number Diff line number Diff line change
Expand Up @@ -172,6 +172,7 @@ def runner(self):
self.options.get("airgapped", False),
self.options.get("source", ""),
self.options.get("exclude_dns", False),
self.options.get("exclude_systems", False),
self.options.get("xorrisofs_opts", ""),
)

Expand Down
85 changes: 84 additions & 1 deletion tests/actions/buildiso/buildiso_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -102,7 +102,7 @@ def test_netboot_generate_boot_loader_configs(

# Act
result = build_iso._generate_boot_loader_configs(
[test_profile.name], [test_system.name], True
[test_profile], [test_system], True
)
matching_isolinux_kernel = [
part for part in result.isolinux if "KERNEL /1.krn" in part
Expand Down Expand Up @@ -152,6 +152,69 @@ def test_netboot_generate_boot_loader_configs(
assert len(matching_grub_system_kopts) == 1
assert len(matching_isolinux_system_kopts) == 1

def test_netboot_generate_boot_loader_config_for_profile_only(
cobbler_api, create_distro, create_profile, create_system
):
test_distro = create_distro()
test_distro.kernel_options = 'test_distro_option=distro'
test_profile = create_profile(test_distro.name)
test_profile.kernel_options = 'test_profile_option=profile'
test_system = create_system(test_profile.name)
test_system.kernel_options = 'test_system_option=system'
build_iso = NetbootBuildiso(cobbler_api)

# Act
result = build_iso._generate_boot_loader_configs(
[test_profile], [], True
)
matching_isolinux_kernel = [
part for part in result.isolinux if "KERNEL /1.krn" in part
]
matching_isolinux_initrd = [
part for part in result.isolinux if "initrd=/1.img" in part
]
matching_grub_kernel = [part for part in result.grub if "linux /1.krn" in part]
matching_grub_initrd = [part for part in result.grub if "initrd /1.img" in part]
matching_grub_distro_kopts = [
part for part in result.grub if "test_distro_option=distro" in part
]
matching_grub_profile_kopts = [
part for part in result.grub if "test_profile_option=profile" in part
]
matching_grub_system_kopts = [
part for part in result.grub if "test_system_option=system" in part
]
matching_isolinux_distro_kopts = [
part for part in result.isolinux if "test_distro_option=distro" in part
]
matching_isolinux_profile_kopts = [
part for part in result.isolinux if "test_profile_option=profile" in part
]
matching_isolinux_system_kopts = [
part for part in result.isolinux if "test_system_option=system" in part
]

# Assert
assert isinstance(result, LoaderCfgsParts)
for iterable_to_check in [
matching_isolinux_kernel,
matching_isolinux_initrd,
matching_grub_kernel,
matching_grub_initrd,
result.bootfiles_copysets,
matching_grub_distro_kopts,
matching_grub_profile_kopts,
matching_isolinux_distro_kopts,
matching_isolinux_profile_kopts
]:
print(iterable_to_check)
# one entry for the profile, and none for the system
assert len(iterable_to_check) == 1

# there are no system entries
assert len(matching_grub_system_kopts) == 0
assert len(matching_isolinux_system_kopts) == 0

def test_filter_system(cobbler_api, create_distro, create_profile, create_system):
# Arrange
test_distro = create_distro()
Expand Down Expand Up @@ -201,6 +264,26 @@ def test_netboot_run(
assert iso_location.exists()


def test_netboot_run_nodistro(
cobbler_api,
create_distro,
create_profile,
create_loaders,
tmpdir,
):
# Arrange
test_distro = create_distro()
test_profile = create_profile(test_distro.name)
build_iso = NetbootBuildiso(cobbler_api)
iso_location = tmpdir.join("autoinst.iso")

# Act
build_iso.run(iso=str(iso_location))

# Assert
assert iso_location.exists()


def test_standalone_run(
cobbler_api,
create_distro,
Expand Down