Compare commits
1 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| f5799d404d |
3
.gitignore
vendored
3
.gitignore
vendored
@ -90,3 +90,6 @@ target/
|
||||
# Rust
|
||||
Cargo.lock
|
||||
vendor/
|
||||
|
||||
*.rpm
|
||||
kiwi-descriptions
|
||||
|
||||
@ -49,7 +49,8 @@ class BootLoaderConfig(metaclass=ABCMeta):
|
||||
'grub2': {'grub2': 'BootLoaderConfigGrub2'},
|
||||
'grub2_s390x_emu': {'grub2': 'BootLoaderConfigGrub2'},
|
||||
'isolinux': {'isolinux': 'BootLoaderConfigIsoLinux'},
|
||||
'systemd_boot': {'systemd_boot': 'BootLoaderSystemdBoot'}
|
||||
'systemd_boot': {'systemd_boot': 'BootLoaderSystemdBoot'},
|
||||
'zipl': {'zipl': 'BootLoaderZipl'}
|
||||
}
|
||||
try:
|
||||
(bootloader_namespace, bootloader_name) = \
|
||||
|
||||
@ -46,6 +46,7 @@ class BootLoaderConfigBase:
|
||||
self.root_dir = root_dir
|
||||
self.boot_dir = boot_dir or root_dir
|
||||
self.xml_state = xml_state
|
||||
self.bootloader = xml_state.get_build_type_bootloader_name()
|
||||
self.arch = Defaults.get_platform_name()
|
||||
|
||||
self.volumes_mount = []
|
||||
@ -380,10 +381,13 @@ class BootLoaderConfigBase:
|
||||
disk_setup = DiskSetup(self.xml_state, self.boot_dir)
|
||||
need_boot_partition = disk_setup.need_boot_partition()
|
||||
if need_boot_partition:
|
||||
# if an extra boot partition is used we will find the
|
||||
# data directly in the root of this partition and not
|
||||
# below the boot/ directory
|
||||
bootpath = '/'
|
||||
if self.bootloader != 'zipl':
|
||||
# if an extra boot partition is used we will find the
|
||||
# data directly in the root of this partition and not
|
||||
# below the boot/ directory. An exception to this case
|
||||
# is the zipl bootloader which finds its target
|
||||
# according to the mount path.
|
||||
bootpath = '/'
|
||||
|
||||
if target == 'disk':
|
||||
if not need_boot_partition:
|
||||
@ -507,7 +511,7 @@ class BootLoaderConfigBase:
|
||||
self.root_mount = MountManager(
|
||||
device=root_device
|
||||
)
|
||||
if 's390' in self.arch:
|
||||
if 's390' in self.arch and self.bootloader == 'grub2_s390x_emu':
|
||||
self.boot_mount = MountManager(
|
||||
device=boot_device,
|
||||
mountpoint=self.root_mount.mountpoint + '/boot/zipl'
|
||||
|
||||
@ -15,12 +15,18 @@
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with kiwi. If not, see <http://www.gnu.org/licenses/>
|
||||
#
|
||||
import os
|
||||
import glob
|
||||
from typing import (
|
||||
Dict, NamedTuple
|
||||
)
|
||||
|
||||
# project
|
||||
from kiwi.firmware import FirmWare
|
||||
from kiwi.bootloader.config.base import BootLoaderConfigBase
|
||||
from kiwi.defaults import Defaults
|
||||
from kiwi.utils.os_release import OsRelease
|
||||
from kiwi.exceptions import KiwiKernelLookupError
|
||||
|
||||
target_type = NamedTuple(
|
||||
'target_type', [
|
||||
@ -56,11 +62,15 @@ class BootLoaderSpecBase(BootLoaderConfigBase):
|
||||
Store custom arguments in an instance dict and initialize
|
||||
target identifiers
|
||||
"""
|
||||
self.entries_dir = Defaults.get_bls_loader_entries_dir()
|
||||
self.target = target_type(
|
||||
disk='disk', install='install(iso)', live='live(iso)'
|
||||
)
|
||||
self.custom_args = custom_args
|
||||
self.timeout = self.get_boot_timeout_seconds()
|
||||
self.disk_type = self.xml_state.get_build_type_bootloader_targettype()
|
||||
self.disk_blocksize = self.xml_state.build_type.get_target_blocksize()
|
||||
self.firmware = FirmWare(self.xml_state)
|
||||
self.cmdline = ''
|
||||
|
||||
def write(self) -> None:
|
||||
@ -77,6 +87,19 @@ class BootLoaderSpecBase(BootLoaderConfigBase):
|
||||
"""
|
||||
pass
|
||||
|
||||
def write_meta_data(
|
||||
self, root_device=None, write_device=None, boot_options=''
|
||||
):
|
||||
"""
|
||||
For bootloaders following the bootloader spec take over
|
||||
the cmdline options and store them for later use
|
||||
|
||||
:param string root_device: unused
|
||||
:param string write_device: unused
|
||||
:param string boot_options: kernel options as string
|
||||
"""
|
||||
self.cmdline = f'{self.cmdline} {boot_options}'.strip()
|
||||
|
||||
def setup_disk_image_config(
|
||||
self, boot_uuid: str = '', root_uuid: str = '', hypervisor: str = '',
|
||||
kernel: str = '', initrd: str = '', boot_options: Dict = {}
|
||||
@ -99,7 +122,7 @@ class BootLoaderSpecBase(BootLoaderConfigBase):
|
||||
self.custom_args['kernel'] = kernel
|
||||
self.custom_args['initrd'] = initrd
|
||||
self.custom_args['boot_options'] = boot_options
|
||||
self.cmdline = ' '.join(
|
||||
plus_cmdline = ' '.join(
|
||||
[
|
||||
self.get_boot_cmdline(
|
||||
boot_options.get('root_device'),
|
||||
@ -107,6 +130,7 @@ class BootLoaderSpecBase(BootLoaderConfigBase):
|
||||
)
|
||||
]
|
||||
)
|
||||
self.cmdline = f'{self.cmdline} {plus_cmdline}'.strip()
|
||||
self.setup_loader(self.target.disk)
|
||||
|
||||
def setup_install_image_config(
|
||||
@ -142,12 +166,13 @@ class BootLoaderSpecBase(BootLoaderConfigBase):
|
||||
pass
|
||||
|
||||
def setup_disk_boot_images(
|
||||
self, boot_uuid: str, lookup_path: str = ''
|
||||
self, boot_uuid: str, efi_uuid: str = '', lookup_path: str = ''
|
||||
) -> None:
|
||||
"""
|
||||
Create bootloader image(s) for disk boot
|
||||
|
||||
:param string mbrid: unused
|
||||
:param string boot_uuid: unused
|
||||
:param string efi_uuid: unused
|
||||
:param str lookup_path: unused
|
||||
|
||||
Targeted to bootloader spec interface
|
||||
@ -218,3 +243,30 @@ class BootLoaderSpecBase(BootLoaderConfigBase):
|
||||
Implementation in specialized loader class
|
||||
"""
|
||||
raise NotImplementedError
|
||||
|
||||
def get_entry_name(self) -> str:
|
||||
"""
|
||||
Construct entry filename of the form
|
||||
[OS_release_ID]-[kernel-version].conf or use the
|
||||
naming policy of an existing entries file
|
||||
|
||||
:return: file basename
|
||||
|
||||
:rtype: str
|
||||
"""
|
||||
os_release = OsRelease(self.root_dir)
|
||||
try:
|
||||
kernel_name = os.path.basename(
|
||||
list(glob.iglob(f'{self.root_dir}/lib/modules/*'))[0]
|
||||
)
|
||||
except Exception as issue:
|
||||
raise KiwiKernelLookupError(
|
||||
'Kernel lookup in {0}/lib/modules failed with: {1}'.format(
|
||||
self.root_dir, issue
|
||||
)
|
||||
)
|
||||
entry_filename = f'{os_release.get("ID")}-{kernel_name}.conf'
|
||||
for dist_entry in glob.iglob(f'{self.root_dir}/{self.entries_dir}/*'):
|
||||
entry_filename = os.path.basename(dist_entry)
|
||||
break
|
||||
return entry_filename
|
||||
|
||||
359
kiwi/bootloader/config/zipl.py
Normal file
359
kiwi/bootloader/config/zipl.py
Normal file
@ -0,0 +1,359 @@
|
||||
# Copyright (c) 2024 SUSE Software Solutions Germany GmbH
|
||||
#
|
||||
# This file is part of kiwi.
|
||||
#
|
||||
# kiwi is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# kiwi is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with kiwi. If not, see <http://www.gnu.org/licenses/>
|
||||
#
|
||||
import os
|
||||
import copy
|
||||
import logging
|
||||
import shutil
|
||||
from string import Template
|
||||
from typing import Dict
|
||||
|
||||
# project
|
||||
from kiwi.path import Path
|
||||
from kiwi.boot.image.base import BootImageBase
|
||||
from kiwi.bootloader.template.zipl import BootLoaderTemplateZipl
|
||||
from kiwi.bootloader.config.bootloader_spec_base import BootLoaderSpecBase
|
||||
from kiwi.command import Command
|
||||
from kiwi.utils.temporary import Temporary
|
||||
|
||||
from kiwi.exceptions import (
|
||||
KiwiTemplateError,
|
||||
KiwiBootLoaderTargetError,
|
||||
KiwiDiskGeometryError
|
||||
)
|
||||
|
||||
log = logging.getLogger('kiwi')
|
||||
|
||||
|
||||
class BootLoaderZipl(BootLoaderSpecBase):
|
||||
def create_loader_image(self, target: str) -> None:
|
||||
"""
|
||||
Nothing to be done here for zipl
|
||||
|
||||
:param str target: unused
|
||||
"""
|
||||
pass # pragma: nocover
|
||||
|
||||
def setup_loader(self, target: str) -> None:
|
||||
"""
|
||||
Setup temporary zipl config and install zipl for supported targets.
|
||||
Please note we are not touching the main zipl.conf file provided
|
||||
by the distributors
|
||||
|
||||
:param str target:
|
||||
target identifier, one of disk, live(iso) or install(iso)
|
||||
Currently only the disk identifier is supported
|
||||
"""
|
||||
if target != self.target.disk:
|
||||
raise KiwiBootLoaderTargetError(
|
||||
'zipl is only supported with the disk image target'
|
||||
)
|
||||
boot_path = self.get_boot_path()
|
||||
boot_options = self.custom_args['boot_options']
|
||||
self._mount_system(
|
||||
boot_options.get('root_device'),
|
||||
boot_options.get('boot_device'),
|
||||
boot_options.get('efi_device'),
|
||||
boot_options.get('system_volumes'),
|
||||
boot_options.get('system_root_volume')
|
||||
)
|
||||
root_dir = self.root_mount.mountpoint
|
||||
kernel_info = BootImageBase(
|
||||
self.xml_state, root_dir, root_dir
|
||||
).get_boot_names()
|
||||
self.custom_args['secure_linux'] = False
|
||||
self.custom_args['kernel'] = \
|
||||
f'{boot_path}/{os.path.basename(kernel_info.kernel_filename)}'
|
||||
self.custom_args['initrd'] = \
|
||||
f'{boot_path}/{kernel_info.initrd_name}'
|
||||
|
||||
host_key_certificates = self.xml_state.get_host_key_certificates()
|
||||
cc_boot_image = f'{self.custom_args["kernel"]}.cc'
|
||||
if host_key_certificates:
|
||||
with Temporary(
|
||||
path=self.root_mount.mountpoint, prefix='kiwi_zipl_parmfile_'
|
||||
).new_file() as hkd_parm_file:
|
||||
with open(hkd_parm_file.name, 'w') as parm_file:
|
||||
parm_file.write(f'{self.cmdline}{os.linesep}')
|
||||
genprotimg_init = [
|
||||
'chroot', root_dir, 'genprotimg',
|
||||
'--offline', '--verbose',
|
||||
'-o', cc_boot_image,
|
||||
'-i', self.custom_args['kernel'],
|
||||
'-r', self.custom_args['initrd'],
|
||||
'-p', hkd_parm_file.name.replace(root_dir, '')
|
||||
]
|
||||
for host_key_certificate in host_key_certificates:
|
||||
genprotimg_host_key_check = copy.deepcopy(genprotimg_init)
|
||||
genprotimg_host_key_check.append('--cert')
|
||||
genprotimg_host_key_check.append(
|
||||
host_key_certificates[0]['hkd_ca_cert']
|
||||
)
|
||||
genprotimg_host_key_check.append('--cert')
|
||||
genprotimg_host_key_check.append(
|
||||
host_key_certificate['hkd_sign_cert']
|
||||
)
|
||||
for host_key in host_key_certificate.get('hkd_cert'):
|
||||
genprotimg_host_key_check.append('-k')
|
||||
genprotimg_host_key_check.append(host_key)
|
||||
for host_crl in host_key_certificate.get('hkd_revocation_list'):
|
||||
genprotimg_host_key_check.append('--crl')
|
||||
genprotimg_host_key_check.append(host_crl)
|
||||
log.info(
|
||||
'Checking HKDs for signing cert: {}'.format(
|
||||
host_key_certificate['hkd_sign_cert']
|
||||
)
|
||||
)
|
||||
Command.run(genprotimg_host_key_check)
|
||||
os.unlink(f'{root_dir}/{cc_boot_image}')
|
||||
|
||||
genprotimg = genprotimg_init
|
||||
genprotimg.append('--no-verify')
|
||||
for host_key_certificate in host_key_certificates:
|
||||
for host_key in host_key_certificate.get('hkd_cert'):
|
||||
genprotimg.append('-k')
|
||||
genprotimg.append(host_key)
|
||||
for host_crl in host_key_certificate.get('hkd_revocation_list'):
|
||||
genprotimg.append('--crl')
|
||||
genprotimg.append(host_crl)
|
||||
Command.run(genprotimg)
|
||||
|
||||
secure_execution_header = 'secure_execution_header.bin'
|
||||
Command.run(
|
||||
[
|
||||
'chroot', root_dir, 'pvextract-hdr',
|
||||
'-o', secure_execution_header,
|
||||
cc_boot_image
|
||||
]
|
||||
)
|
||||
shutil.move(
|
||||
os.sep.join([root_dir, secure_execution_header]),
|
||||
os.sep.join(
|
||||
[
|
||||
self.custom_args['target_dir'],
|
||||
'{0}.{1}-{2}.{3}'.format(
|
||||
self.xml_state.xml_data.get_name(),
|
||||
self.arch,
|
||||
self.xml_state.get_image_version(),
|
||||
secure_execution_header
|
||||
)
|
||||
]
|
||||
)
|
||||
)
|
||||
|
||||
attestation_request = 'attestation_request.bin'
|
||||
attestation_response_key = 'attestation_response.key'
|
||||
pvattest = [
|
||||
'chroot', root_dir, 'pvattest', 'create',
|
||||
'--no-verify', '--verbose',
|
||||
'-o', attestation_request,
|
||||
'--arpk', attestation_response_key
|
||||
]
|
||||
for host_key_certificate in host_key_certificates:
|
||||
for host_key in host_key_certificate.get('hkd_cert'):
|
||||
pvattest.append('-k')
|
||||
pvattest.append(host_key)
|
||||
Command.run(pvattest)
|
||||
shutil.move(
|
||||
os.sep.join([root_dir, attestation_request]),
|
||||
os.sep.join(
|
||||
[
|
||||
self.custom_args['target_dir'],
|
||||
'{0}.{1}-{2}.{3}'.format(
|
||||
self.xml_state.xml_data.get_name(),
|
||||
self.arch,
|
||||
self.xml_state.get_image_version(),
|
||||
attestation_request
|
||||
)
|
||||
]
|
||||
)
|
||||
)
|
||||
shutil.move(
|
||||
os.sep.join([root_dir, attestation_response_key]),
|
||||
os.sep.join(
|
||||
[
|
||||
self.custom_args['target_dir'],
|
||||
'{0}.{1}-{2}.{3}'.format(
|
||||
self.xml_state.xml_data.get_name(),
|
||||
self.arch,
|
||||
self.xml_state.get_image_version(),
|
||||
attestation_response_key
|
||||
)
|
||||
]
|
||||
)
|
||||
)
|
||||
|
||||
self.custom_args['secure_linux'] = True
|
||||
self.custom_args['secure_image_file'] = cc_boot_image
|
||||
os.unlink(f'{root_dir}/{self.custom_args["kernel"]}')
|
||||
os.unlink(f'{root_dir}/{self.custom_args["initrd"]}')
|
||||
self.custom_args['kernel'] = ''
|
||||
self.custom_args['initrd'] = ''
|
||||
|
||||
with Temporary(
|
||||
path=self.root_mount.mountpoint, prefix='kiwi_zipl.conf_'
|
||||
).new_file() as runtime_zipl_config_file:
|
||||
BootLoaderZipl._write_config_file(
|
||||
BootLoaderTemplateZipl().get_loader_template(),
|
||||
runtime_zipl_config_file.name,
|
||||
self._get_template_parameters()
|
||||
)
|
||||
self.set_loader_entry(
|
||||
self.root_mount.mountpoint, self.target.disk
|
||||
)
|
||||
self._install_zipl(root_dir, runtime_zipl_config_file.name)
|
||||
|
||||
def set_loader_entry(self, root_dir: str, target: str) -> None:
|
||||
"""
|
||||
Setup/update loader entries of the form
|
||||
{boot_path}/loader/entries/{get_entry_name}
|
||||
|
||||
:param str target:
|
||||
target identifier, one of disk, live(iso) or install(iso)
|
||||
"""
|
||||
entry_name = self.get_entry_name()
|
||||
BootLoaderZipl._write_config_file(
|
||||
BootLoaderTemplateZipl().get_entry_template(
|
||||
self.custom_args['secure_linux']
|
||||
),
|
||||
root_dir + f'{self.entries_dir}/{entry_name}',
|
||||
self._get_template_parameters(entry_name)
|
||||
)
|
||||
|
||||
def _install_zipl(self, root_dir: str, zipl_config: str) -> None:
|
||||
"""
|
||||
Install zipl on target
|
||||
"""
|
||||
zipl = [
|
||||
'chroot', root_dir, 'zipl',
|
||||
'--noninteractive',
|
||||
'--config', zipl_config.replace(root_dir, ''),
|
||||
'--blsdir', self.entries_dir,
|
||||
'--verbose'
|
||||
]
|
||||
self.sys_mount.umount()
|
||||
Command.run(zipl)
|
||||
|
||||
def _get_template_parameters(
|
||||
self, default_entry: str = ''
|
||||
) -> Dict[str, str]:
|
||||
disk_type = self.disk_type or 'SCSI'
|
||||
disk_type = disk_type if disk_type != 'GPT' else 'SCSI'
|
||||
blocksize = self.disk_blocksize or 512
|
||||
unsupported_for_target_geometry = ['FBA', 'SCSI']
|
||||
targetbase = f'targetbase={self.custom_args.get("targetbase")}'
|
||||
geometry = ''
|
||||
if disk_type not in unsupported_for_target_geometry:
|
||||
geometry = f'targetgeometry={self._get_disk_geometry()}'
|
||||
return {
|
||||
'secure_image_file': self.custom_args.get('secure_image_file') or '',
|
||||
'kernel_file': self.custom_args['kernel'] or 'vmlinuz',
|
||||
'initrd_file': self.custom_args['initrd'] or 'initrd',
|
||||
'boot_options': self.cmdline,
|
||||
'boot_timeout': self.timeout,
|
||||
'bootpath': self.get_boot_path(),
|
||||
'targetbase': targetbase,
|
||||
'targettype': disk_type,
|
||||
'targetblocksize': format(blocksize),
|
||||
'targetoffset': self._get_partition_start(),
|
||||
'targetgeometry': geometry,
|
||||
'title': self.get_menu_entry_title(),
|
||||
'default_entry': default_entry
|
||||
}
|
||||
|
||||
def _get_disk_geometry(self) -> str:
|
||||
target_table_type = self.firmware.get_partition_table_type()
|
||||
disk_device = self.custom_args['targetbase']
|
||||
disk_geometry = ''
|
||||
if target_table_type == 'dasd':
|
||||
disk_geometry = '{0},{1},{2}'.format(
|
||||
self._get_dasd_disk_geometry_element(
|
||||
disk_device, 'cylinders'
|
||||
),
|
||||
self._get_dasd_disk_geometry_element(
|
||||
disk_device, 'tracks per cylinder'
|
||||
),
|
||||
self._get_dasd_disk_geometry_element(
|
||||
disk_device, 'blocks per track'
|
||||
)
|
||||
)
|
||||
return disk_geometry
|
||||
|
||||
def _get_partition_start(self) -> str:
|
||||
target_table_type = self.firmware.get_partition_table_type()
|
||||
disk_device = self.custom_args['targetbase']
|
||||
if target_table_type == 'dasd':
|
||||
blocks = self._get_dasd_disk_geometry_element(
|
||||
disk_device, 'blocks per track'
|
||||
)
|
||||
fdasd_command = [
|
||||
'fdasd', '-f', '-s', '-p', disk_device,
|
||||
'|', 'grep', '"^ "',
|
||||
'|', 'head', '-n', '1',
|
||||
'|', 'tr', '-s', '" "'
|
||||
]
|
||||
fdasd_call = Command.run(
|
||||
['bash', '-c', ' '.join(fdasd_command)]
|
||||
)
|
||||
fdasd_output = fdasd_call.output
|
||||
try:
|
||||
start_track = int(fdasd_output.split(' ')[2].lstrip())
|
||||
except Exception:
|
||||
raise KiwiDiskGeometryError(
|
||||
f'unknown partition format: {fdasd_output}'
|
||||
)
|
||||
return '{0}'.format(start_track * blocks)
|
||||
else:
|
||||
sfdisk_command = ' '.join(
|
||||
[
|
||||
'sfdisk', '--dump', disk_device,
|
||||
'|', 'grep', '"1 :"',
|
||||
'|', 'cut', '-f1', '-d,',
|
||||
'|', 'cut', '-f2', '-d='
|
||||
]
|
||||
)
|
||||
return Command.run(
|
||||
['bash', '-c', sfdisk_command]
|
||||
).output.strip()
|
||||
|
||||
def _get_dasd_disk_geometry_element(self, disk_device, search) -> int:
|
||||
fdasd = ['fdasd', '-f', '-p', disk_device]
|
||||
bash_command = fdasd + ['|', 'grep', '"' + search + '"']
|
||||
fdasd_call = Command.run(
|
||||
['bash', '-c', ' '.join(bash_command)]
|
||||
)
|
||||
fdasd_output = fdasd_call.output
|
||||
try:
|
||||
return int(fdasd_output.split(':')[1].lstrip())
|
||||
except Exception:
|
||||
raise KiwiDiskGeometryError(
|
||||
f'unknown format for disk geometry: {fdasd_output}'
|
||||
)
|
||||
|
||||
@staticmethod
|
||||
def _write_config_file(
|
||||
template: Template, filename: str, parameters: Dict[str, str]
|
||||
) -> None:
|
||||
try:
|
||||
config_data = template.substitute(parameters)
|
||||
Path.create(os.path.dirname(filename))
|
||||
with open(filename, 'w') as config:
|
||||
config.write(config_data)
|
||||
except Exception as issue:
|
||||
raise KiwiTemplateError(
|
||||
'{0}: {1}'.format(type(issue).__name__, issue)
|
||||
)
|
||||
@ -48,7 +48,8 @@ class BootLoaderInstall(metaclass=ABCMeta):
|
||||
name_map = {
|
||||
'grub2': {'grub2': 'BootLoaderInstallGrub2'},
|
||||
'grub2_s390x_emu': {'grub2': 'BootLoaderInstallGrub2'},
|
||||
'systemd_boot': {'systemd_boot': 'BootLoaderInstallSystemdBoot'}
|
||||
'systemd_boot': {'systemd_boot': 'BootLoaderInstallSystemdBoot'},
|
||||
'zipl': {'zipl': 'BootLoaderInstallZipl'}
|
||||
}
|
||||
try:
|
||||
(bootloader_namespace, bootloader_name) = \
|
||||
|
||||
66
kiwi/bootloader/install/zipl.py
Normal file
66
kiwi/bootloader/install/zipl.py
Normal file
@ -0,0 +1,66 @@
|
||||
# Copyright (c) 2024 SUSE Software Solutions Germany GmbH. All rights reserved.
|
||||
#
|
||||
# This file is part of kiwi.
|
||||
#
|
||||
# kiwi is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# kiwi is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with kiwi. If not, see <http://www.gnu.org/licenses/>
|
||||
#
|
||||
import logging
|
||||
from typing import Dict
|
||||
|
||||
# project
|
||||
from kiwi.bootloader.install.base import BootLoaderInstallBase
|
||||
|
||||
log = logging.getLogger('kiwi')
|
||||
|
||||
|
||||
class BootLoaderInstallZipl(BootLoaderInstallBase):
|
||||
"""
|
||||
**zipl bootloader installation**
|
||||
"""
|
||||
def post_init(self, custom_args: Dict):
|
||||
"""
|
||||
zipl post initialization method
|
||||
|
||||
:param dict custom_args: unused
|
||||
"""
|
||||
self.custom_args = custom_args
|
||||
|
||||
def install_required(self) -> bool:
|
||||
"""
|
||||
Check if zipl needs to install boot code
|
||||
|
||||
zipl requires boot code installation, but it is done as
|
||||
part of the BLS implementation in bootloader/config/zipl.py
|
||||
Thus this method always returns: False
|
||||
|
||||
:return: False
|
||||
|
||||
:rtype: bool
|
||||
"""
|
||||
return False
|
||||
|
||||
def install(self):
|
||||
"""
|
||||
Not required for zipl, handled in config stage
|
||||
"""
|
||||
pass
|
||||
|
||||
def secure_boot_install(self):
|
||||
"""
|
||||
Run shim installation for secure boot setup
|
||||
|
||||
For zipl this is skipped since details for
|
||||
secure boot are not yet clear.
|
||||
"""
|
||||
pass
|
||||
79
kiwi/bootloader/template/zipl.py
Normal file
79
kiwi/bootloader/template/zipl.py
Normal file
@ -0,0 +1,79 @@
|
||||
# Copyright (c) 2024 SUSE Software Solutions Germany GmbH. All rights reserved.
|
||||
#
|
||||
# This file is part of kiwi.
|
||||
#
|
||||
# kiwi is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# kiwi is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with kiwi. If not, see <http://www.gnu.org/licenses/>
|
||||
#
|
||||
from string import Template
|
||||
from textwrap import dedent
|
||||
|
||||
|
||||
class BootLoaderTemplateZipl:
|
||||
"""
|
||||
**zipl configuraton file templates**
|
||||
"""
|
||||
def __init__(self):
|
||||
self.cr = '\n'
|
||||
|
||||
self.main_conf = dedent('''
|
||||
[defaultboot]
|
||||
defaultauto
|
||||
prompt=1
|
||||
target=${bootpath}
|
||||
${targetbase}
|
||||
targettype=${targettype}
|
||||
targetblocksize=${targetblocksize}
|
||||
targetoffset=${targetoffset}
|
||||
${targetgeometry}
|
||||
timeout=${boot_timeout}
|
||||
secure=auto
|
||||
''').strip() + self.cr
|
||||
|
||||
self.entry_secure = dedent('''
|
||||
title ${title}
|
||||
options ${boot_options}
|
||||
linux ${secure_image_file}
|
||||
''').strip() + self.cr
|
||||
|
||||
self.entry_standard = dedent('''
|
||||
title ${title}
|
||||
options ${boot_options}
|
||||
linux ${kernel_file}
|
||||
initrd ${initrd_file}
|
||||
''').strip() + self.cr
|
||||
|
||||
def get_loader_template(self) -> Template:
|
||||
"""
|
||||
Bootloader main configuration template
|
||||
|
||||
:return: instance of :class:`Template`
|
||||
|
||||
:rtype: Template
|
||||
"""
|
||||
template_data = self.main_conf
|
||||
return Template(template_data)
|
||||
|
||||
def get_entry_template(self, secure: bool = False) -> Template:
|
||||
"""
|
||||
Bootloader entry configuration template
|
||||
|
||||
:return: instance of :class:`Template`
|
||||
|
||||
:rtype: Template
|
||||
"""
|
||||
if secure:
|
||||
template_data = self.entry_secure
|
||||
else:
|
||||
template_data = self.entry_standard
|
||||
return Template(template_data)
|
||||
@ -731,7 +731,8 @@ class DiskBuilder:
|
||||
exclude_list.append(
|
||||
'{0}/.*'.format(self.spare_part_mountpoint.lstrip(os.sep))
|
||||
)
|
||||
if 'boot' in device_map and 's390' in self.arch:
|
||||
if 'boot' in device_map \
|
||||
and 's390' in self.arch and self.bootloader == 'grub2_s390x_emu':
|
||||
exclude_list.append('boot/zipl/*')
|
||||
exclude_list.append('boot/zipl/.*')
|
||||
elif 'boot' in device_map:
|
||||
@ -846,7 +847,7 @@ class DiskBuilder:
|
||||
if not boot_filesystem:
|
||||
boot_filesystem = self.requested_filesystem
|
||||
boot_directory = self.root_dir + '/boot/'
|
||||
if 's390' in self.arch:
|
||||
if 's390' in self.arch and self.bootloader == 'grub2_s390x_emu':
|
||||
boot_directory = self.root_dir + '/boot/zipl/'
|
||||
log.info(
|
||||
'Creating boot(%s) filesystem on %s',
|
||||
@ -1138,7 +1139,7 @@ class DiskBuilder:
|
||||
custom_root_mount_args, fs_check_interval
|
||||
)
|
||||
if device_map.get('boot'):
|
||||
if 's390' in self.arch:
|
||||
if 's390' in self.arch and self.bootloader == 'grub2_s390x_emu':
|
||||
boot_mount_point = '/boot/zipl'
|
||||
else:
|
||||
boot_mount_point = '/boot'
|
||||
|
||||
@ -570,6 +570,17 @@ class Defaults:
|
||||
"""
|
||||
return 'grub2'
|
||||
|
||||
@staticmethod
|
||||
def get_bls_loader_entries_dir() -> str:
|
||||
"""
|
||||
Provide default loader entries directory for BLS loaders
|
||||
|
||||
:return: directory name
|
||||
|
||||
:rtype: str
|
||||
"""
|
||||
return '/boot/loader/entries'
|
||||
|
||||
@staticmethod
|
||||
def get_grub_boot_directory_name(lookup_path):
|
||||
"""
|
||||
|
||||
@ -379,6 +379,12 @@ class KiwiKernelLookupError(KiwiError):
|
||||
"""
|
||||
|
||||
|
||||
class KiwiOSReleaseImportError(KiwiError):
|
||||
"""
|
||||
Exception raised if the os-release file could not be imported
|
||||
"""
|
||||
|
||||
|
||||
class KiwiLiveBootImageError(KiwiError):
|
||||
"""
|
||||
Exception raised if an attempt was made to use an
|
||||
|
||||
@ -65,6 +65,8 @@ class FirmWare:
|
||||
if 's390' in self.arch:
|
||||
if self.zipl_target_type and 'CDL' in self.zipl_target_type:
|
||||
return 'dasd'
|
||||
elif self.zipl_target_type and 'GPT' in self.zipl_target_type:
|
||||
return 'gpt'
|
||||
else:
|
||||
return 'msdos'
|
||||
elif 'ppc64' in self.arch:
|
||||
|
||||
@ -2741,7 +2741,7 @@ div {
|
||||
## user which can be done by using the editbootinstall and
|
||||
## editbootconfig custom scripts
|
||||
attribute name {
|
||||
"grub2" | "isolinux" | "grub2_s390x_emu" | "systemd_boot" | "custom"
|
||||
"grub2" | "isolinux" | "grub2_s390x_emu" | "systemd_boot" | "custom" | "zipl"
|
||||
}
|
||||
>> sch:pattern [ id = "loader_name" is-a = "bootloader_image_type"
|
||||
sch:param [ name = "attr" value = "name" ]
|
||||
@ -2785,7 +2785,7 @@ div {
|
||||
attribute timeout { xsd:nonNegativeInteger }
|
||||
>> sch:pattern [ id = "timeout" is-a = "bootloader_name_type"
|
||||
sch:param [ name = "attr" value = "timeout" ]
|
||||
sch:param [ name = "types" value = "grub2 isolinux grub2_s390x_emu systemd_boot" ]
|
||||
sch:param [ name = "types" value = "grub2 isolinux grub2_s390x_emu systemd_boot zipl" ]
|
||||
]
|
||||
k.bootloader.timeout_style.attribute =
|
||||
## Specifies the boot timeout style to control the way in which
|
||||
@ -2805,11 +2805,11 @@ div {
|
||||
## devices use SCSI, for emulated DASD devices use FBA,
|
||||
## for 4k DASD devices use CDL
|
||||
attribute targettype {
|
||||
"FBA" | "SCSI" | "CDL"
|
||||
"FBA" | "SCSI" | "CDL" | "GPT"
|
||||
}
|
||||
>> sch:pattern [ id = "targettype" is-a = "bootloader_name_type"
|
||||
sch:param [ name = "attr" value = "targettype" ]
|
||||
sch:param [ name = "types" value = "grub2_s390x_emu" ]
|
||||
sch:param [ name = "types" value = "grub2_s390x_emu zipl" ]
|
||||
]
|
||||
k.bootloader.use_disk_password.attribute =
|
||||
## When /boot is encrypted, make the boot loader store the
|
||||
@ -2831,7 +2831,8 @@ div {
|
||||
## and to provide configuration parameters for it
|
||||
element bootloader {
|
||||
k.bootloader.attlist &
|
||||
k.bootloadersettings?
|
||||
k.bootloadersettings? &
|
||||
k.securelinux*
|
||||
}
|
||||
}
|
||||
|
||||
@ -3093,6 +3094,74 @@ div {
|
||||
}
|
||||
}
|
||||
|
||||
#==========================================
|
||||
# main block: <securelinux>
|
||||
#
|
||||
div {
|
||||
sch:pattern [
|
||||
abstract = "true"
|
||||
id = "securelinux_loader_requirement"
|
||||
sch:rule [
|
||||
context = "securelinux"
|
||||
sch:assert [
|
||||
test = "../@name='zipl' or ../@name='grub2-s390x-emu'"
|
||||
"<securelinux> section only available for the "
|
||||
"bootloader types: zipl and grub2-s390x-emu"
|
||||
]
|
||||
]
|
||||
]
|
||||
k.securelinux.hkd_ca_cert.attribute =
|
||||
attribute hkd_ca_cert { text }
|
||||
k.securelinux.hkd_sign_cert.attribute =
|
||||
attribute hkd_sign_cert { text }
|
||||
k.securelinux.attlist =
|
||||
k.securelinux.hkd_ca_cert.attribute &
|
||||
k.securelinux.hkd_sign_cert.attribute
|
||||
k.securelinux =
|
||||
## securelinux contains all elements to describe
|
||||
## data required to setup a secure linux execution
|
||||
## process for the individual architecture in the scope
|
||||
## of the bootloader process
|
||||
element securelinux {
|
||||
k.securelinux.attlist &
|
||||
k.hkd_cert+ &
|
||||
k.hkd_revocation_list*
|
||||
}
|
||||
>> sch:pattern [
|
||||
id = "bootloader_matches" is-a = "securelinux_loader_requirement"
|
||||
]
|
||||
}
|
||||
|
||||
#==========================================
|
||||
# common element <hkd_cert>
|
||||
#
|
||||
div {
|
||||
k.hkd_cert.name.attribute =
|
||||
attribute name { text }
|
||||
k.hkd_cert.attlist =
|
||||
k.hkd_cert.name.attribute
|
||||
k.hkd_cert =
|
||||
element hkd_cert {
|
||||
k.hkd_cert.attlist,
|
||||
empty
|
||||
}
|
||||
}
|
||||
|
||||
#==========================================
|
||||
# common element <hkd_revocation_list>
|
||||
#
|
||||
div {
|
||||
k.hkd_revocation_list.name.attribute =
|
||||
attribute name { text }
|
||||
k.hkd_revocation_list.attlist =
|
||||
k.hkd_revocation_list.name.attribute
|
||||
k.hkd_revocation_list =
|
||||
element hkd_revocation_list {
|
||||
k.hkd_revocation_list.attlist,
|
||||
empty
|
||||
}
|
||||
}
|
||||
|
||||
#==========================================
|
||||
# main block: <environment>
|
||||
#
|
||||
|
||||
@ -4111,6 +4111,7 @@ editbootconfig custom scripts</a:documentation>
|
||||
<value>grub2_s390x_emu</value>
|
||||
<value>systemd_boot</value>
|
||||
<value>custom</value>
|
||||
<value>zipl</value>
|
||||
</choice>
|
||||
</attribute>
|
||||
<sch:pattern id="loader_name" is-a="bootloader_image_type">
|
||||
@ -4165,7 +4166,7 @@ to 10sec</a:documentation>
|
||||
</attribute>
|
||||
<sch:pattern id="timeout" is-a="bootloader_name_type">
|
||||
<sch:param name="attr" value="timeout"/>
|
||||
<sch:param name="types" value="grub2 isolinux grub2_s390x_emu systemd_boot"/>
|
||||
<sch:param name="types" value="grub2 isolinux grub2_s390x_emu systemd_boot zipl"/>
|
||||
</sch:pattern>
|
||||
</define>
|
||||
<define name="k.bootloader.timeout_style.attribute">
|
||||
@ -4195,11 +4196,12 @@ for 4k DASD devices use CDL</a:documentation>
|
||||
<value>FBA</value>
|
||||
<value>SCSI</value>
|
||||
<value>CDL</value>
|
||||
<value>GPT</value>
|
||||
</choice>
|
||||
</attribute>
|
||||
<sch:pattern id="targettype" is-a="bootloader_name_type">
|
||||
<sch:param name="attr" value="targettype"/>
|
||||
<sch:param name="types" value="grub2_s390x_emu"/>
|
||||
<sch:param name="types" value="grub2_s390x_emu zipl"/>
|
||||
</sch:pattern>
|
||||
</define>
|
||||
<define name="k.bootloader.use_disk_password.attribute">
|
||||
@ -4245,6 +4247,9 @@ and to provide configuration parameters for it</a:documentation>
|
||||
<optional>
|
||||
<ref name="k.bootloadersettings"/>
|
||||
</optional>
|
||||
<zeroOrMore>
|
||||
<ref name="k.securelinux"/>
|
||||
</zeroOrMore>
|
||||
</interleave>
|
||||
</element>
|
||||
</define>
|
||||
@ -4612,6 +4617,74 @@ of the storage device</a:documentation>
|
||||
</element>
|
||||
</define>
|
||||
</div>
|
||||
<!--
|
||||
==========================================
|
||||
main block: <securelinux>
|
||||
|
||||
-->
|
||||
<div>
|
||||
<sch:pattern abstract="true" id="securelinux_loader_requirement">
|
||||
<sch:rule context="securelinux">
|
||||
<sch:assert test="../@name='zipl' or ../@name='grub2-s390x-emu'"><securelinux> section only available for the bootloader types: zipl and grub2-s390x-emu</sch:assert>
|
||||
</sch:rule>
|
||||
</sch:pattern>
|
||||
<define name="k.securelinux.attlist">
|
||||
<interleave>
|
||||
<attribute name="hkd_ca_cert"/>
|
||||
<attribute name="hkd_sign_cert"/>
|
||||
</interleave>
|
||||
</define>
|
||||
<define name="k.securelinux">
|
||||
<element name="securelinux">
|
||||
<a:documentation>securelinux contains all elements to describe
|
||||
data required to setup a secure linux execution
|
||||
process for the individual architecture in the scope
|
||||
of the bootloader process</a:documentation>
|
||||
<interleave>
|
||||
<ref name="k.securelinux.attlist"/>
|
||||
<oneOrMore>
|
||||
<ref name="k.hkd_cert"/>
|
||||
</oneOrMore>
|
||||
<zeroOrMore>
|
||||
<ref name="k.hkd_revocation_list"/>
|
||||
</zeroOrMore>
|
||||
</interleave>
|
||||
</element>
|
||||
<sch:pattern id="bootloader_matches" is-a="securelinux_loader_requirement"/>
|
||||
</define>
|
||||
</div>
|
||||
<!--
|
||||
==========================================
|
||||
common element <hkd_cert>
|
||||
|
||||
-->
|
||||
<div>
|
||||
<define name="k.hkd_cert.attlist">
|
||||
<attribute name="name"/>
|
||||
</define>
|
||||
<define name="k.hkd_cert">
|
||||
<element name="hkd_cert">
|
||||
<ref name="k.hkd_cert.attlist"/>
|
||||
<empty/>
|
||||
</element>
|
||||
</define>
|
||||
</div>
|
||||
<!--
|
||||
==========================================
|
||||
common element <hkd_revocation_list>
|
||||
|
||||
-->
|
||||
<div>
|
||||
<define name="k.hkd_revocation_list.attlist">
|
||||
<attribute name="name"/>
|
||||
</define>
|
||||
<define name="k.hkd_revocation_list">
|
||||
<element name="hkd_revocation_list">
|
||||
<ref name="k.hkd_revocation_list.attlist"/>
|
||||
<empty/>
|
||||
</element>
|
||||
</define>
|
||||
</div>
|
||||
<!--
|
||||
==========================================
|
||||
main block: <environment>
|
||||
|
||||
63
kiwi/utils/os_release.py
Normal file
63
kiwi/utils/os_release.py
Normal file
@ -0,0 +1,63 @@
|
||||
# Copyright (c) 2024 SUSE Software Solutions Germany GmbH. All rights reserved.
|
||||
#
|
||||
# This file is part of kiwi.
|
||||
#
|
||||
# kiwi is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# kiwi is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with kiwi. If not, see <http://www.gnu.org/licenses/>
|
||||
#
|
||||
import csv
|
||||
from io import TextIOWrapper
|
||||
from typing import Iterable
|
||||
|
||||
# project
|
||||
from kiwi.exceptions import KiwiOSReleaseImportError
|
||||
|
||||
|
||||
class OsRelease:
|
||||
"""
|
||||
**Read os-release information**
|
||||
"""
|
||||
def __init__(self, root_dir: str):
|
||||
self.data = {}
|
||||
os_release = root_dir + '/etc/os-release'
|
||||
try:
|
||||
with open(os_release) as osdata:
|
||||
reader = csv.reader(OsRelease._rip(osdata), delimiter='=')
|
||||
self.data = dict(reader)
|
||||
except Exception as issue:
|
||||
raise KiwiOSReleaseImportError(
|
||||
f'Import of {os_release} failed with {issue}'
|
||||
)
|
||||
|
||||
@staticmethod
|
||||
def _is_comment(line: str) -> bool:
|
||||
return line.startswith('#')
|
||||
|
||||
@staticmethod
|
||||
def _is_whitespace(line: str) -> bool:
|
||||
return line.isspace()
|
||||
|
||||
@staticmethod
|
||||
def _rip(csvfile: TextIOWrapper) -> Iterable[str]:
|
||||
for row in csvfile:
|
||||
if not OsRelease._is_comment(row) \
|
||||
and not OsRelease._is_whitespace(row):
|
||||
yield row
|
||||
|
||||
def get(self, key: str) -> str:
|
||||
"""
|
||||
Return value for key or an empty string if not present
|
||||
|
||||
:param string key: key name from os-release
|
||||
"""
|
||||
return self.data.get(key) or ''
|
||||
@ -17,7 +17,7 @@
|
||||
#
|
||||
import os
|
||||
from typing import (
|
||||
List, Optional, Any, Dict, NamedTuple
|
||||
List, Optional, Any, Dict, NamedTuple, Union
|
||||
)
|
||||
import re
|
||||
import logging
|
||||
@ -1123,6 +1123,47 @@ class XMLState:
|
||||
return bootloader.get_use_disk_password()
|
||||
return False
|
||||
|
||||
def get_build_type_bootloader_securelinux_section(self) -> List[Any]:
|
||||
"""
|
||||
First securelinux section from the build
|
||||
type bootloader section
|
||||
|
||||
:return: <securelinux> section reference
|
||||
|
||||
:rtype: xml_parse::securelinux
|
||||
"""
|
||||
bootloader_section = self.get_build_type_bootloader_section()
|
||||
bootloader_securelinux_section = []
|
||||
get_securelinux = getattr(
|
||||
bootloader_section, 'get_securelinux', None
|
||||
)
|
||||
if get_securelinux and get_securelinux():
|
||||
bootloader_securelinux_section = get_securelinux()
|
||||
return bootloader_securelinux_section
|
||||
|
||||
def get_host_key_certificates(
|
||||
self
|
||||
) -> Union[List[Dict[str, List[str]]], List[Dict[str, str]]]:
|
||||
cc_result = []
|
||||
cc_certificates: Dict[str, List[str]] = {}
|
||||
securelinux_list = \
|
||||
self.get_build_type_bootloader_securelinux_section()
|
||||
for securelinux in securelinux_list:
|
||||
cc_certificates = {
|
||||
'hkd_cert': [],
|
||||
'hkd_revocation_list': [],
|
||||
'hkd_ca_cert': securelinux.get_hkd_ca_cert(),
|
||||
'hkd_sign_cert': securelinux.get_hkd_sign_cert()
|
||||
}
|
||||
for hkd_cert in securelinux.get_hkd_cert():
|
||||
cc_certificates['hkd_cert'].append(hkd_cert.get_name())
|
||||
for hkd_revocation_list in securelinux.get_hkd_revocation_list():
|
||||
cc_certificates['hkd_revocation_list'].append(
|
||||
hkd_revocation_list.get_name()
|
||||
)
|
||||
cc_result.append(cc_certificates)
|
||||
return cc_result
|
||||
|
||||
def get_build_type_oemconfig_section(self) -> Any:
|
||||
"""
|
||||
First oemconfig section from the build type section
|
||||
|
||||
@ -353,6 +353,7 @@ class TestBootLoaderConfigBase:
|
||||
return mount_managers.pop()
|
||||
|
||||
self.bootloader.arch = 's390x'
|
||||
self.bootloader.bootloader = 'grub2_s390x_emu'
|
||||
|
||||
mock_MountManager.side_effect = mount_managers_effect
|
||||
self.bootloader._mount_system(
|
||||
|
||||
@ -17,6 +17,12 @@ class TestBootLoaderSystemdBoot:
|
||||
self.state.xml_data.get_name.return_value = 'image-name'
|
||||
self.state.get_image_version.return_value = 'image-version'
|
||||
self.state.build_type.get_efifatimagesize.return_value = None
|
||||
self.state.build_type.get_firmware.return_value = None
|
||||
self.state.build_type.get_efipartsize.return_value = None
|
||||
self.state.build_type.get_efiparttable.return_value = None
|
||||
self.state.build_type.get_target_blocksize.return_value = None
|
||||
self.state.get_build_type_bootloader_targettype.return_value = None
|
||||
self.state.get_build_type_bootloader_name.return_value = 'systemd_boot'
|
||||
self.bootloader = BootLoaderSystemdBoot(self.state, 'root_dir')
|
||||
self.bootloader.custom_args['kernel'] = None
|
||||
self.bootloader.custom_args['initrd'] = None
|
||||
|
||||
344
test/unit/bootloader/config/zipl_test.py
Normal file
344
test/unit/bootloader/config/zipl_test.py
Normal file
@ -0,0 +1,344 @@
|
||||
import io
|
||||
from pytest import raises
|
||||
from unittest.mock import (
|
||||
Mock, patch, call, MagicMock
|
||||
)
|
||||
from kiwi.bootloader.config.zipl import BootLoaderZipl
|
||||
from kiwi.bootloader.config.bootloader_spec_base import BootLoaderSpecBase
|
||||
from kiwi.command import command_type
|
||||
|
||||
from kiwi.exceptions import (
|
||||
KiwiTemplateError,
|
||||
KiwiBootLoaderTargetError,
|
||||
KiwiDiskGeometryError
|
||||
)
|
||||
|
||||
|
||||
class TestBootLoaderZipl:
|
||||
@patch('kiwi.bootloader.config.bootloader_spec_base.FirmWare')
|
||||
def setup(self, mock_FirmWare):
|
||||
self.firmware = Mock()
|
||||
mock_FirmWare.return_value = self.firmware
|
||||
self.state = Mock()
|
||||
self.state.get_host_key_certificates.return_value = [
|
||||
{
|
||||
'hkd_cert': ['/path/to/host.crt'],
|
||||
'hkd_revocation_list': ['/path/to/revocation-list.crl'],
|
||||
'hkd_ca_cert': '/path/to/DigiCertCA.crt',
|
||||
'hkd_sign_cert': '/path/to/ibm-z-host-key-signing.crt'
|
||||
}
|
||||
]
|
||||
self.state.get_build_type_bootloader_targettype.return_value = 'CDL'
|
||||
self.state.build_type.get_target_blocksize.return_value = 4096
|
||||
self.state.xml_data.get_name.return_value = 'image-name'
|
||||
self.state.get_image_version.return_value = 'image-version'
|
||||
self.state.build_type.get_efifatimagesize.return_value = None
|
||||
self.bootloader = BootLoaderZipl(self.state, 'root_dir')
|
||||
self.bootloader.cmdline = 'options'
|
||||
self.bootloader.custom_args['kernel'] = None
|
||||
self.bootloader.custom_args['initrd'] = None
|
||||
self.bootloader.custom_args['boot_options'] = {}
|
||||
self.bootloader.custom_args['targetbase'] = '/dev/disk'
|
||||
self.bootloader.custom_args['target_dir'] = 'target_dir'
|
||||
self.bootloader.sys_mount = Mock(
|
||||
mountpoint='sys_mount'
|
||||
)
|
||||
self.bootloader.root_mount = Mock(
|
||||
mountpoint='system_root_mount'
|
||||
)
|
||||
self.bootloader._mount_system = Mock()
|
||||
self.bootloader.create_efi_path = Mock()
|
||||
self.bootloader.get_boot_path = Mock(
|
||||
return_value='bootpath'
|
||||
)
|
||||
self.bootloader.get_menu_entry_title = Mock(
|
||||
return_value='title'
|
||||
)
|
||||
self.bootloader.arch = 's390x'
|
||||
|
||||
@patch('kiwi.bootloader.config.bootloader_spec_base.FirmWare')
|
||||
def setup_method(self, cls, mock_FirmWare):
|
||||
self.setup()
|
||||
|
||||
@patch('os.unlink')
|
||||
def test_setup_loader_raises_invalid_target(self, mock_os_unlink):
|
||||
with raises(KiwiBootLoaderTargetError):
|
||||
self.bootloader.setup_loader('iso')
|
||||
|
||||
@patch('shutil.move')
|
||||
@patch('os.unlink')
|
||||
@patch('os.path.exists')
|
||||
@patch('kiwi.bootloader.config.zipl.BootImageBase.get_boot_names')
|
||||
@patch('kiwi.bootloader.config.zipl.Path.create')
|
||||
@patch('kiwi.bootloader.config.zipl.Command.run')
|
||||
@patch('kiwi.bootloader.config.zipl.BootLoaderTemplateZipl')
|
||||
@patch('kiwi.bootloader.config.zipl.Temporary.new_file')
|
||||
@patch.object(BootLoaderZipl, '_write_config_file')
|
||||
@patch.object(BootLoaderZipl, '_get_template_parameters')
|
||||
@patch.object(BootLoaderSpecBase, 'get_entry_name')
|
||||
def test_setup_loader(
|
||||
self, mock_get_entry_name, mock_get_template_parameters,
|
||||
mock_write_config_file, mock_Temporary_new_file,
|
||||
mock_BootLoaderTemplateZipl, mock_Command_run,
|
||||
mock_Path_create, mock_BootImageBase_get_boot_names,
|
||||
mock_os_path_exists, mock_os_unlink, mock_shutil_move
|
||||
):
|
||||
temporary = Mock()
|
||||
temporary.name = 'system_root_mount/kiwi_zipl.conf_'
|
||||
mock_Temporary_new_file.return_value.__enter__.return_value = temporary
|
||||
mock_get_entry_name.return_value = \
|
||||
'opensuse-leap-5.3.18-59.10-default.conf'
|
||||
|
||||
mock_get_template_parameters.return_value = {
|
||||
'targetbase': '/dev/loop'
|
||||
}
|
||||
kernel_info = Mock()
|
||||
kernel_info.kernel_version = 'kernel-version'
|
||||
kernel_info.kernel_filename = 'kernel-filename'
|
||||
kernel_info.initrd_name = 'initrd-name'
|
||||
mock_os_path_exists.return_value = True
|
||||
mock_BootImageBase_get_boot_names.return_value = kernel_info
|
||||
with patch('builtins.open', create=True) as mock_open:
|
||||
mock_open.return_value = MagicMock(spec=io.IOBase)
|
||||
file_handle = mock_open.return_value.__enter__.return_value
|
||||
self.bootloader.setup_loader('disk')
|
||||
file_handle.write.assert_called_once_with('options\n')
|
||||
assert mock_write_config_file.call_args_list == [
|
||||
call(
|
||||
mock_BootLoaderTemplateZipl.
|
||||
return_value.get_loader_template.return_value,
|
||||
'system_root_mount/kiwi_zipl.conf_',
|
||||
mock_get_template_parameters.return_value
|
||||
),
|
||||
call(
|
||||
mock_BootLoaderTemplateZipl.
|
||||
return_value.get_entry_template.return_value,
|
||||
'system_root_mount/boot/loader/entries/'
|
||||
'opensuse-leap-5.3.18-59.10-default.conf',
|
||||
mock_get_template_parameters.return_value
|
||||
)
|
||||
]
|
||||
assert self.bootloader._mount_system.called
|
||||
assert self.bootloader.sys_mount.umount.called
|
||||
assert mock_Command_run.call_args_list == [
|
||||
call(
|
||||
[
|
||||
'chroot', 'system_root_mount', 'genprotimg',
|
||||
'--offline', '--verbose',
|
||||
'-o', 'bootpath/kernel-filename.cc',
|
||||
'-i', 'bootpath/kernel-filename',
|
||||
'-r', 'bootpath/initrd-name',
|
||||
'-p', temporary.name.replace('system_root_mount', ''),
|
||||
'--cert', '/path/to/DigiCertCA.crt',
|
||||
'--cert', '/path/to/ibm-z-host-key-signing.crt',
|
||||
'-k', '/path/to/host.crt',
|
||||
'--crl', '/path/to/revocation-list.crl'
|
||||
]
|
||||
),
|
||||
call(
|
||||
[
|
||||
'chroot', 'system_root_mount', 'genprotimg',
|
||||
'--offline', '--verbose',
|
||||
'-o', 'bootpath/kernel-filename.cc',
|
||||
'-i', 'bootpath/kernel-filename',
|
||||
'-r', 'bootpath/initrd-name',
|
||||
'-p', temporary.name.replace('system_root_mount', ''),
|
||||
'--no-verify',
|
||||
'-k', '/path/to/host.crt',
|
||||
'--crl', '/path/to/revocation-list.crl',
|
||||
]
|
||||
),
|
||||
call(
|
||||
[
|
||||
'chroot', 'system_root_mount', 'pvextract-hdr',
|
||||
'-o', 'secure_execution_header.bin',
|
||||
'bootpath/kernel-filename.cc'
|
||||
]
|
||||
),
|
||||
call(
|
||||
[
|
||||
'chroot', 'system_root_mount', 'pvattest', 'create',
|
||||
'--no-verify', '--verbose',
|
||||
'-o', 'attestation_request.bin',
|
||||
'--arpk', 'attestation_response.key',
|
||||
'-k', '/path/to/host.crt'
|
||||
]
|
||||
),
|
||||
call(
|
||||
[
|
||||
'chroot', 'system_root_mount', 'zipl',
|
||||
'--noninteractive',
|
||||
'--config', '/kiwi_zipl.conf_',
|
||||
'--blsdir', '/boot/loader/entries',
|
||||
'--verbose'
|
||||
]
|
||||
)
|
||||
]
|
||||
assert mock_shutil_move.call_args_list == [
|
||||
call(
|
||||
'system_root_mount/secure_execution_header.bin',
|
||||
'target_dir/image-name.s390x-image-version.'
|
||||
'secure_execution_header.bin'
|
||||
),
|
||||
call(
|
||||
'system_root_mount/attestation_request.bin',
|
||||
'target_dir/image-name.s390x-image-version.'
|
||||
'attestation_request.bin'
|
||||
),
|
||||
call(
|
||||
'system_root_mount/attestation_response.key',
|
||||
'target_dir/image-name.s390x-image-version.'
|
||||
'attestation_response.key'
|
||||
)
|
||||
]
|
||||
assert mock_os_unlink.call_args_list == [
|
||||
call('system_root_mount/bootpath/kernel-filename.cc'),
|
||||
call('system_root_mount/bootpath/kernel-filename'),
|
||||
call('system_root_mount/bootpath/initrd-name')
|
||||
]
|
||||
|
||||
@patch.object(BootLoaderZipl, '_get_disk_geometry')
|
||||
@patch.object(BootLoaderZipl, '_get_partition_start')
|
||||
def test_get_template_parameters(
|
||||
self, mock_get_partition_start, mock_get_disk_geometry
|
||||
):
|
||||
mock_get_partition_start.return_value = '42'
|
||||
mock_get_disk_geometry.return_value = '123,53,9'
|
||||
self.bootloader.timeout = 0
|
||||
self.bootloader.disk_type = 'CDL'
|
||||
self.bootloader.disk_blocksize = 4096
|
||||
assert self.bootloader._get_template_parameters() == {
|
||||
'secure_image_file': 'bootpath/kernel-filename.cc',
|
||||
'kernel_file': 'vmlinuz',
|
||||
'initrd_file': 'initrd',
|
||||
'boot_options': 'options',
|
||||
'boot_timeout': 0,
|
||||
'bootpath': 'bootpath',
|
||||
'targetbase': 'targetbase=/dev/disk',
|
||||
'targettype': 'CDL',
|
||||
'targetblocksize': '4096',
|
||||
'targetoffset': '42',
|
||||
'targetgeometry': 'targetgeometry=123,53,9',
|
||||
'title': 'title',
|
||||
'default_entry': ''
|
||||
}
|
||||
|
||||
@patch('kiwi.bootloader.config.zipl.Path.create')
|
||||
@patch.object(BootLoaderZipl, '_get_template_parameters')
|
||||
def test_write_config_file(
|
||||
self, mock_get_template_parameters, mock_Path_create
|
||||
):
|
||||
template = Mock()
|
||||
template.substitute.return_value = 'data'
|
||||
with patch('builtins.open', create=True) as mock_open:
|
||||
mock_open.return_value = MagicMock(spec=io.IOBase)
|
||||
file_handle = mock_open.return_value.__enter__.return_value
|
||||
self.bootloader._write_config_file(template, 'path/some-file', {})
|
||||
mock_Path_create.assert_called_once_with('path')
|
||||
mock_open.assert_called_once_with('path/some-file', 'w')
|
||||
file_handle.write.assert_called_once_with('data')
|
||||
|
||||
def test_write_config_file_raises(self):
|
||||
template = Mock()
|
||||
template.substitute.side_effect = Exception
|
||||
with raises(KiwiTemplateError):
|
||||
self.bootloader._write_config_file(template, 'some-file', {})
|
||||
|
||||
@patch('kiwi.bootloader.config.zipl.Command.run')
|
||||
def test_get_disk_geometry(self, mock_Command_run):
|
||||
command_return_values = [
|
||||
command_type(
|
||||
output=' cylinders ............: 10017\n',
|
||||
error='', returncode=0
|
||||
),
|
||||
command_type(
|
||||
output=' tracks per cylinder ..: 15\n',
|
||||
error='', returncode=0
|
||||
),
|
||||
command_type(
|
||||
output=' blocks per track .....: 12\n',
|
||||
error='', returncode=0
|
||||
)
|
||||
]
|
||||
|
||||
def command_run(arg):
|
||||
return command_return_values.pop(0)
|
||||
|
||||
self.firmware.get_partition_table_type.return_value = 'dasd'
|
||||
mock_Command_run.side_effect = command_run
|
||||
assert self.bootloader._get_disk_geometry() == '10017,15,12'
|
||||
|
||||
@patch('kiwi.bootloader.config.zipl.Command.run')
|
||||
def test_get_partition_start_dasd(self, mock_Command_run):
|
||||
self.firmware.get_partition_table_type.return_value = 'dasd'
|
||||
command_return_values = [
|
||||
command_type(
|
||||
output=' blocks per track .....: 12\n',
|
||||
error='', returncode=0
|
||||
),
|
||||
command_type(
|
||||
output=' /dev/loop01 2 6401 6400 1 Linux native\n',
|
||||
error='', returncode=0
|
||||
)
|
||||
]
|
||||
|
||||
def command_run(arg):
|
||||
return command_return_values.pop(0)
|
||||
|
||||
mock_Command_run.side_effect = command_run
|
||||
assert self.bootloader._get_partition_start() == '24'
|
||||
assert mock_Command_run.call_args_list == [
|
||||
call(
|
||||
[
|
||||
'bash', '-c',
|
||||
'fdasd -f -p /dev/disk | grep "blocks per track"'
|
||||
]
|
||||
),
|
||||
call(
|
||||
[
|
||||
'bash', '-c',
|
||||
'fdasd -f -s -p /dev/disk | grep "^ " |'
|
||||
' head -n 1 | tr -s " "'
|
||||
]
|
||||
)
|
||||
]
|
||||
|
||||
@patch('kiwi.bootloader.config.zipl.Command.run')
|
||||
def test_get_partition_start_not_dasd(self, mock_Command_run):
|
||||
self.firmware.get_partition_table_type.return_value = 'msdos'
|
||||
command = Mock()
|
||||
command.output = ' 2048'
|
||||
mock_Command_run.return_value = command
|
||||
assert self.bootloader._get_partition_start() == '2048'
|
||||
mock_Command_run.assert_called_once_with(
|
||||
[
|
||||
'bash', '-c',
|
||||
'sfdisk --dump /dev/disk | grep "1 :" |'
|
||||
' cut -f1 -d, | cut -f2 -d='
|
||||
]
|
||||
)
|
||||
|
||||
@patch('kiwi.bootloader.config.zipl.Command.run')
|
||||
@patch.object(BootLoaderZipl, '_get_dasd_disk_geometry_element')
|
||||
def test_get_partition_start_raises(
|
||||
self, mock_get_dasd_disk_geometry_element, mock_Command_run
|
||||
):
|
||||
self.firmware.get_partition_table_type.return_value = 'dasd'
|
||||
mock_Command_run.return_value = command_type(
|
||||
output='bogus data', error='', returncode=0
|
||||
)
|
||||
with raises(KiwiDiskGeometryError):
|
||||
self.bootloader._get_partition_start()
|
||||
|
||||
@patch('kiwi.bootloader.config.zipl.Command.run')
|
||||
def test_get_dasd_disk_geometry_element_raises(
|
||||
self, mock_Command_run
|
||||
):
|
||||
self.bootloader.target_table_type = 'dasd'
|
||||
mock_Command_run.return_value = command_type(
|
||||
output='bogus data', error='', returncode=0
|
||||
)
|
||||
with raises(KiwiDiskGeometryError):
|
||||
self.bootloader._get_dasd_disk_geometry_element(
|
||||
'/dev/disk', 'tracks per cylinder'
|
||||
)
|
||||
22
test/unit/bootloader/install/zipl_test.py
Normal file
22
test/unit/bootloader/install/zipl_test.py
Normal file
@ -0,0 +1,22 @@
|
||||
from unittest.mock import Mock
|
||||
|
||||
from kiwi.bootloader.install.zipl import BootLoaderInstallZipl
|
||||
|
||||
|
||||
class TestBootLoaderInstallZipl:
|
||||
def setup(self):
|
||||
self.bootloader = BootLoaderInstallZipl(
|
||||
'root_dir', Mock()
|
||||
)
|
||||
|
||||
def setup_method(self, cls):
|
||||
self.setup()
|
||||
|
||||
def test_install_required(self):
|
||||
assert self.bootloader.install_required() is False
|
||||
|
||||
def test_install(self):
|
||||
self.bootloader.install()
|
||||
|
||||
def test_secure_boot_install(self):
|
||||
self.bootloader.secure_boot_install()
|
||||
35
test/unit/bootloader/template/zipl_test.py
Normal file
35
test/unit/bootloader/template/zipl_test.py
Normal file
@ -0,0 +1,35 @@
|
||||
from kiwi.bootloader.template.zipl import BootLoaderTemplateZipl
|
||||
|
||||
|
||||
class TestBootLoaderTemplateZipl:
|
||||
def setup(self):
|
||||
self.zipl = BootLoaderTemplateZipl()
|
||||
|
||||
def setup_method(self, cls):
|
||||
self.setup()
|
||||
|
||||
def test_get_loader_template(self):
|
||||
assert self.zipl.get_loader_template().substitute(
|
||||
boot_timeout='10',
|
||||
bootpath='/boot',
|
||||
targetbase='',
|
||||
targettype='SCSI',
|
||||
targetblocksize='512',
|
||||
targetoffset='2048',
|
||||
targetgeometry=''
|
||||
)
|
||||
|
||||
def test_get_entry_template_standard(self):
|
||||
assert self.zipl.get_entry_template().substitute(
|
||||
title='title',
|
||||
boot_options='',
|
||||
kernel_file='linux',
|
||||
initrd_file='initrd'
|
||||
)
|
||||
|
||||
def test_get_entry_template_secure(self):
|
||||
assert self.zipl.get_entry_template(secure=True).substitute(
|
||||
title='title',
|
||||
boot_options='',
|
||||
secure_image_file='linux'
|
||||
)
|
||||
@ -24,6 +24,9 @@ class TestCloneDevice:
|
||||
self.storage_device, 'root_dir'
|
||||
)
|
||||
|
||||
def setup_method(self, cls):
|
||||
self.setup()
|
||||
|
||||
@patch('kiwi.storage.clone_device.Command.run')
|
||||
@patch('kiwi.storage.clone_device.BlockID')
|
||||
@patch('kiwi.storage.clone_device.FileSystem.new')
|
||||
|
||||
Loading…
Reference in New Issue
Block a user