Skip to content
Merged
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
6 changes: 6 additions & 0 deletions doc/source/image_description/elements.rst
Original file line number Diff line number Diff line change
Expand Up @@ -1331,6 +1331,7 @@ Used to specify custom arguments for the initrd tooling e.g. dracut
<initrd action="add|omit">
<dracut module="some_a"/>
<dracut module="some_b"/>
<dracut driver="driver_name"/>
</initrd>

dracut.uefi.true:
Expand All @@ -1340,6 +1341,11 @@ dracut.module.NAME:
As part of the `add` or `omit` action, adds or omits the given
module name. The element can be specified multiple times

dracut.driver.NAME:
As part of the `add` or `omit` action, adds or omits the given
kernel driver. The element can be specified multiple times and
accepts kernel driver names without the .ko extension

.. _sec.registry:

<containers>
Expand Down
24 changes: 24 additions & 0 deletions kiwi/boot/image/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -176,6 +176,30 @@ def set_static_modules(
"""
pass

def include_driver(self, driver: str, install_media: bool = False) -> None:
"""
Include driver to the boot image

For kiwi boot no drivers configuration is required. Thus in
such a case this method is a noop.

:param str driver: driver to include
:param bool install_media: include the driver for install initrds
"""
pass

def omit_driver(self, driver: str, install_media: bool = False) -> None:
"""
Omit driver to the boot image

For kiwi boot no drivers configuration is required. Thus in
such a case this method is a noop.

:param str driver: driver to omit
:param bool install_media: omit the driver for install initrds
"""
pass

def write_system_config_file(
self, config: Dict, config_file: Optional[str] = None
) -> None:
Expand Down
88 changes: 86 additions & 2 deletions kiwi/boot/image/dracut.py
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,9 @@ def post_init(self) -> None:
self.add_modules: List[str] = []
self.omit_modules: List[str] = []
self.available_modules = self._get_modules()
self.add_drivers: List[str] = []
self.omit_drivers: List[str] = []
self.available_drivers = self._get_drivers()

def include_file(
self, filename: str, install_media: bool = False,
Expand Down Expand Up @@ -117,6 +120,30 @@ def set_static_modules(
"""
self.modules = modules

def include_driver(self, driver: str, install_media: bool = False) -> None:
"""
Include driver to dracut boot image

:param str driver: driver to include
:param bool install_media: unused
"""
warn_msg = 'driver "{0}" not included in initrd'.format(driver)
if self._driver_available(driver):
if driver not in self.add_drivers:
self.add_drivers.append(driver)
else:
log.warning(warn_msg)

def omit_driver(self, driver: str, install_media: bool = False) -> None:
"""
Omit driver to dracut boot image

:param str driver: driver to omit
:param bool install_media: unused
"""
if driver not in self.omit_drivers:
self.omit_drivers.append(driver)

def write_system_config_file(
self, config: Dict, config_file: Optional[str] = None
) -> None:
Expand Down Expand Up @@ -151,6 +178,20 @@ def write_system_config_file(
' '.join(config['install_items'])
)
)
if config.get('drivers'):
drivers = [
driver for driver in config['drivers']
if self._driver_available(driver)
]
dracut_config.append(
'add_drivers+=" {0} "\n'.format(' '.join(drivers))
)
if config.get('omit_drivers'):
dracut_config.append(
'omit_drivers+=" {0} "\n'.format(
' '.join(config['omit_drivers'])
)
)
if dracut_config and config_file:
with open(config_file, 'w') as config_handle:
config_handle.writelines(dracut_config)
Expand Down Expand Up @@ -204,7 +245,14 @@ def create_uki(self, cmdline: str) -> str:
modules_args += [
'--omit', ' {0} '.format(' '.join(self.omit_modules))
] if self.omit_modules else []
options = self.dracut_options + modules_args + included_files
drivers_arg = [
'--drivers', ' {0} '.format(' '.join(self.add_drivers))
] if self.add_drivers else []
drivers_arg += [
'--omit-drivers', ' {0} '.format(' '.join(self.omit_drivers))
] if self.omit_drivers else []

options = self.dracut_options + modules_args + drivers_arg + included_files
if kernel_details:
with ExitStack() as stack:
device_mount = MountManager(
Expand Down Expand Up @@ -284,7 +332,13 @@ def create_initrd(
modules_args += [
'--omit', ' {0} '.format(' '.join(self.omit_modules))
] if self.omit_modules else []
options = self.dracut_options + modules_args + included_files
drivers_arg = [
'--drivers', ' {0} '.format(' '.join(self.add_drivers))
] if self.add_drivers else []
drivers_arg += [
'--omit-drivers', ' {0} '.format(' '.join(self.omit_drivers))
] if self.omit_drivers else []
options = self.dracut_options + modules_args + drivers_arg + included_files
if kernel_details:
with ExitStack() as stack:
device_mount = MountManager(
Expand Down Expand Up @@ -340,13 +394,43 @@ def _get_modules(self) -> List[str]:
)
return cmd.output.splitlines()

def _get_drivers(self) -> List[str]:
try:
kernel_info = Kernel(self.boot_root_directory).get_kernel()
if not kernel_info:
log.warning("No kernel found in boot directory")
return []

cmd = Command.run(
[
'chroot', self.boot_root_directory,
'cat', f'/lib/modules/{kernel_info.version}/modules.dep'
]
)
drivers = [
os.path.basename(line.split(':')[0].strip()).split('.ko')[0]
for line in cmd.output.splitlines()
if line.strip()
]
return drivers
except Exception as e:
log.warning(f"Error reading drivers: {str(e)}")
return []

def _module_available(self, module: str) -> bool:
warn_msg = 'dracut module "{0}" not found in the root tree'
if module in self.available_modules:
return True
log.warning(warn_msg.format(module))
return False

def _driver_available(self, driver: str) -> bool:
warn_msg = 'dracut driver "{0}" not found in the root tree'
if driver in self.available_drivers:
return True
log.warning(warn_msg.format(driver))
return False

def _create_profile_environment(self) -> None:
profile = Profile(self.xml_state)
defaults = Defaults()
Expand Down
6 changes: 6 additions & 0 deletions kiwi/builder/disk.py
Original file line number Diff line number Diff line change
Expand Up @@ -158,6 +158,8 @@ def __init__(
self.dracut_setup = xml_state.get_dracut_config('setup')
self.dracut_add_modules = xml_state.get_dracut_config('add').modules
self.dracut_omit_modules = xml_state.get_dracut_config('omit').modules
self.dracut_add_drivers = xml_state.get_dracut_config('add').drivers
self.dracut_omit_drivers = xml_state.get_dracut_config('omit').drivers
self.mdraid = xml_state.build_type.get_mdraid()
self.hybrid_mbr = xml_state.build_type.get_gpt_hybrid_mbr()
self.force_mbr = xml_state.build_type.get_force_mbr()
Expand Down Expand Up @@ -803,6 +805,10 @@ def _build_main_system(
self.boot_image.include_module(module)
for module in self.dracut_omit_modules:
self.boot_image.omit_module(module)
for driver in self.dracut_add_drivers:
self.boot_image.include_driver(driver)
for driver in self.dracut_omit_drivers:
self.boot_image.omit_driver(driver)
if self.root_filesystem_is_multipath is False:
self.boot_image.omit_module('multipath')
if self.root_filesystem_is_overlay:
Expand Down
11 changes: 11 additions & 0 deletions kiwi/builder/install.py
Original file line number Diff line number Diff line change
Expand Up @@ -413,6 +413,11 @@ def _create_pxe_install_kernel_and_initrd(self) -> None:
self.boot_image_task.set_static_modules(
self.xml_state.get_installmedia_initrd_modules('set')
)
for drv in self.xml_state.get_installmedia_initrd_drivers('add'):
self.boot_image_task.include_driver(drv)
for drv in self.xml_state.get_installmedia_initrd_drivers('omit'):
self.boot_image_task.omit_driver(drv)

self.boot_image_task.create_initrd(
self.mbrid, 'initrd_kiwi_install',
install_initrd=True
Expand Down Expand Up @@ -456,6 +461,12 @@ def _create_iso_install_kernel_and_initrd(self) -> None:
self.boot_image_task.set_static_modules(
self.xml_state.get_installmedia_initrd_modules('set')
)

for drv in self.xml_state.get_installmedia_initrd_drivers('add'):
self.boot_image_task.include_driver(drv)
for drv in self.xml_state.get_installmedia_initrd_drivers('omit'):
self.boot_image_task.omit_driver(drv)

self._add_system_image_boot_options_to_boot_image()
self._add_system_identifier_to_boot_image()
self.boot_image_task.create_initrd(
Expand Down
4 changes: 4 additions & 0 deletions kiwi/schema/kiwi.rnc
Original file line number Diff line number Diff line change
Expand Up @@ -3629,10 +3629,14 @@ div {
k.dracut.module.attribute =
## A module name
attribute module { text }
k.dracut.driver.attribute =
## A kernel driver name
attribute driver { text }
k.dracut.uefi.attribute =
attribute uefi { xsd:boolean }
k.dracut.attlist =
k.dracut.module.attribute? &
k.dracut.driver.attribute? &
k.dracut.uefi.attribute?
k.dracut =
## A dracut module
Expand Down
8 changes: 8 additions & 0 deletions kiwi/schema/kiwi.rng
Original file line number Diff line number Diff line change
Expand Up @@ -5448,6 +5448,11 @@ for the installation media.</a:documentation>
<a:documentation>A module name</a:documentation>
</attribute>
</define>
<define name="k.dracut.driver.attribute">
<attribute name="driver">
<a:documentation>A kernel driver name</a:documentation>
</attribute>
</define>
<define name="k.dracut.uefi.attribute">
<attribute name="uefi">
<data type="boolean"/>
Expand All @@ -5458,6 +5463,9 @@ for the installation media.</a:documentation>
<optional>
<ref name="k.dracut.module.attribute"/>
</optional>
<optional>
<ref name="k.dracut.driver.attribute"/>
</optional>
<optional>
<ref name="k.dracut.uefi.attribute"/>
</optional>
Expand Down
16 changes: 13 additions & 3 deletions kiwi/xml_parse.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@

#
# Generated by generateDS.py version 2.29.24.
# Python 3.11.11 (main, Dec 06 2024, 17:06:18) [GCC]
# Python 3.11.12 (main, Apr 29 2025, 00:00:00) [GCC]
#
# Command line options:
# ('-f', '')
Expand All @@ -16,7 +16,7 @@
# kiwi/schema/kiwi_for_generateDS.xsd
#
# Command line:
# /home/ms/.cache/pypoetry/virtualenvs/kiwi-Btua-i95-py3.11/bin/generateDS.py -f --external-encoding="utf-8" --no-dates --no-warnings -o "kiwi/xml_parse.py" kiwi/schema/kiwi_for_generateDS.xsd
# /home/al/.cache/pypoetry/virtualenvs/kiwi-7Qlgdzr3-py3.11/bin/generateDS.py -f --external-encoding="utf-8" --no-dates --no-warnings -o "kiwi/xml_parse.py" kiwi/schema/kiwi_for_generateDS.xsd
#
# Current working directory (os.getcwd()):
# kiwi
Expand Down Expand Up @@ -9007,9 +9007,10 @@ class dracut(GeneratedsSuper):
"""A dracut module"""
subclass = None
superclass = None
def __init__(self, module=None, uefi=None):
def __init__(self, module=None, driver=None, uefi=None):
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe a stupid question and just to put me right. The xml_parse.py is like the schema/kiwi.rng an auto generated file. So am I correct that you only modified schema/kiwi.rnc and then called make kiwi/schema/kiwi.rng to auto generated the API ? If yes, all is good, if not and you have manually modified xml_parse.py or schema/kiwi.rng then please let it create through the make target. Thanks much

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In the latest commit, I ran make kiwi/schema/kiwi.rng, but I had to manually adjust the output in kiwi/xml_parse.py to use f-string formatting because generateDS.py tool doesn't generate the output using f-string format

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@aale24 I can keep this for now but please have in mind that the next schema changes might revert this back to the way how the generatedDS module handles it. Did you had a programmatic way to move from format sequences into f strings ? If yes we should add that to the Makefile as a step after generateDS.

In addition I could not find an issue in the auto generated API with regards to format sequences vs. f-strings. So did you had a specific reason why this change was required ?

Thanks

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@schaefi. You're correct that this situation requires some clarification:

  1. The change to f-strings was initially made in this commit: 28ed67e
  2. When I ran make kiwi/schema/kiwi.rng, the generateDS tool reverted the f-strings back to format sequences in kiwi/xml_parse.py.
  3. To maintain consistency with the earlier commit and to avoid introducing unnecessary changes in the PR, I manually adjusted the output in kiwi/xml_parse.py back to f-strings after running the make command.

You're right that this manual adjustment isn't ideal, as future schema changes might revert it again. Currently, I don't have a programmatic way to convert format sequences to f-strings automatically but I could create an issue and work on that. Let me know if you want to use the f-string format in kiwi/xml_parse.py

self.original_tagname_ = None
self.module = _cast(None, module)
self.driver = _cast(None, driver)
self.uefi = _cast(bool, uefi)
def factory(*args_, **kwargs_):
if CurrentSubclassModule_ is not None:
Expand All @@ -9024,6 +9025,8 @@ def factory(*args_, **kwargs_):
factory = staticmethod(factory)
def get_module(self): return self.module
def set_module(self, module): self.module = module
def get_driver(self): return self.driver
def set_driver(self, driver): self.driver = driver
def get_uefi(self): return self.uefi
def set_uefi(self, uefi): self.uefi = uefi
def hasContent_(self):
Expand Down Expand Up @@ -9057,6 +9060,9 @@ def exportAttributes(self, outfile, level, already_processed, namespaceprefix_='
if self.module is not None and 'module' not in already_processed:
already_processed.add('module')
outfile.write(f" module={self.gds_encode(self.gds_format_string(quote_attrib(self.module), input_name='module'))}")
if self.driver is not None and 'driver' not in already_processed:
already_processed.add('driver')
outfile.write(f" driver={self.gds_encode(self.gds_format_string(quote_attrib(self.driver), input_name='driver'))}")
if self.uefi is not None and 'uefi' not in already_processed:
already_processed.add('uefi')
outfile.write(f" uefi=\"{self.gds_format_boolean(self.uefi, input_name='uefi')}\"")
Expand All @@ -9074,6 +9080,10 @@ def buildAttributes(self, node, attrs, already_processed):
if value is not None and 'module' not in already_processed:
already_processed.add('module')
self.module = value
value = find_attr_value_('driver', node)
if value is not None and 'driver' not in already_processed:
already_processed.add('driver')
self.driver = value
value = find_attr_value_('uefi', node)
if value is not None and 'uefi' not in already_processed:
already_processed.add('uefi')
Expand Down
29 changes: 27 additions & 2 deletions kiwi/xml_state.py
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,7 @@
class DracutT(NamedTuple):
uefi: bool
modules: List[str]
drivers: List[str]


class FileT(NamedTuple):
Expand Down Expand Up @@ -1465,15 +1466,18 @@ def get_dracut_config(self, action: str) -> DracutT:
"""
uefi = False
modules = []
drivers = []
initrd_sections = self.build_type.get_initrd()
for initrd_section in initrd_sections:
if initrd_section.get_action() == action:
for dracut in initrd_section.get_dracut():
uefi = bool(dracut.get_uefi())
if dracut.get_module():
modules.append(dracut.get_module())
if dracut.get_driver():
drivers.append(dracut.get_driver())
return DracutT(
uefi=uefi, modules=modules
uefi=uefi, modules=modules, drivers=drivers
)

def get_installmedia_initrd_modules(self, action: str) -> List[str]:
Expand All @@ -1492,9 +1496,30 @@ def get_installmedia_initrd_modules(self, action: str) -> List[str]:
for initrd_section in initrd_sections:
if initrd_section.get_action() == action:
for module in initrd_section.get_dracut():
modules.append(module.get_module())
if module.get_module():
modules.append(module.get_module())
return modules

def get_installmedia_initrd_drivers(self, action: str) -> List[str]:
"""
Gets the list of drivers to append in installation initrds

:return: a list of dracut driver names

:rtype: list
"""
drivers: List[str] = []
installmedia = self.build_type.get_installmedia()
if not installmedia:
return drivers
initrd_sections = installmedia[0].get_initrd()
for initrd_section in initrd_sections:
if initrd_section.get_action() == action:
for driver in initrd_section.get_dracut():
if driver.get_driver():
drivers.append(driver.get_driver())
return drivers

def get_build_type_size(
self, include_unpartitioned: bool = False
) -> Optional[size_type]:
Expand Down
Loading