kiwi.runtime_checker
1# Copyright (c) 2015 SUSE Linux GmbH. All rights reserved. 2# 3# This file is part of kiwi. 4# 5# kiwi is free software: you can redistribute it and/or modify 6# it under the terms of the GNU General Public License as published by 7# the Free Software Foundation, either version 3 of the License, or 8# (at your option) any later version. 9# 10# kiwi is distributed in the hope that it will be useful, 11# but WITHOUT ANY WARRANTY; without even the implied warranty of 12# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13# GNU General Public License for more details. 14# 15# You should have received a copy of the GNU General Public License 16# along with kiwi. If not, see <http://www.gnu.org/licenses/> 17# 18import os 19import json 20import re 21import logging 22from textwrap import dedent 23from typing import ( 24 NamedTuple, Dict, List, Any 25) 26 27# project 28from kiwi.version import __version__ 29from io import StringIO 30from kiwi.xml_description import XMLDescription 31from kiwi.firmware import FirmWare 32from kiwi.xml_state import XMLState 33from kiwi.system.uri import Uri 34from kiwi.defaults import Defaults 35from kiwi.command import Command 36from kiwi.path import Path 37from kiwi.utils.command_capabilities import CommandCapabilities 38from kiwi.runtime_config import RuntimeConfig 39from kiwi.exceptions import ( 40 KiwiRuntimeError 41) 42 43import kiwi.defaults as defaults 44 45dracut_module_type = NamedTuple( 46 'dracut_module_type', [ 47 ('package', str), 48 ('min_version', str) 49 ] 50) 51 52log = logging.getLogger('kiwi') 53 54 55class RuntimeChecker: 56 """ 57 **Implements build consistency checks at runtime** 58 """ 59 def __init__(self, xml_state: XMLState) -> None: 60 """ 61 The schema of an image description covers structure and syntax of 62 the provided data. The RuntimeChecker provides methods to perform 63 further semantic checks which allows to recognize potential build 64 or boot problems early. 65 66 :param object xml_state: Instance of XMLState 67 """ 68 self.xml_state = xml_state 69 70 def check_repositories_configured(self) -> None: 71 """ 72 Verify that there are repositories configured 73 """ 74 if not self.xml_state.get_repository_sections(): 75 raise KiwiRuntimeError( 76 'No repositories configured' 77 ) 78 79 @staticmethod 80 def check_target_dir_on_unsupported_filesystem(target_dir: str) -> None: 81 """ 82 Raise if the given target dir does not reside on a 83 filesystem that supports all important features like 84 extended permissions(fscaps), ACLs or xattrs. 85 """ 86 message = dedent('''\n 87 Target root/image directory is lacking filesystem features 88 89 The filesystem {0} in the target path {1} 90 does not support important features like extended permissions, 91 ACLs or xattrs. The image build may fail or the resulting 92 image misbehave. 93 ''') 94 target_dir = Path.first_exists(target_dir) 95 stat = Command.run(['stat', '-f', '-c', '%T', target_dir]) 96 if stat: 97 target_fs = stat.output.strip() 98 supported_target_filesystem = ( 99 'btrfs', 100 'ext2', 101 'ext2/ext3', 102 'ext3', 103 'ext4', 104 'overlayfs', 105 'tmpfs', 106 'xfs', 107 ) 108 if target_fs not in supported_target_filesystem: 109 raise KiwiRuntimeError(message.format(target_fs, target_dir)) 110 111 def check_include_references_unresolvable(self) -> None: 112 """ 113 Raise for still included <include> statements as not resolvable. 114 The KIWI XSLT processing replaces the specified include 115 directive(s) with the given file reference(s). If this action 116 did not happen for example on nested includes, it can happen 117 that they stay in the document as sort of waste. 118 """ 119 message = dedent('''\n 120 One ore more <include> statements are unresolvable 121 122 The following include references could not be resolved. 123 Please verify the specified location(s) and/or delete 124 the broken include directive(s) from the description. 125 Please also note, nested includes from other include 126 files are not supported: 127 128 {0} 129 ''') 130 include_files = \ 131 self.xml_state.get_include_section_reference_file_names() 132 if include_files: 133 raise KiwiRuntimeError( 134 message.format(json.dumps(include_files, indent=4)) 135 ) 136 137 def check_image_include_repos_publicly_resolvable(self) -> None: 138 """ 139 Verify that all repos marked with the imageinclude attribute 140 can be resolved into a http based web URL 141 """ 142 message = dedent('''\n 143 The use of imageinclude="true" in the repository definition 144 for the Repository: {0} 145 requires the repository to by publicly available. The source 146 locator of the repository however indicates it is private to 147 your local system. Therefore it can't be included into the 148 system image repository configuration. Please define a publicly 149 available repository in your image XML description. 150 ''') 151 152 repository_sections = self.xml_state.get_repository_sections() 153 for xml_repo in repository_sections: 154 repo_marked_for_image_include = xml_repo.get_imageinclude() 155 156 if repo_marked_for_image_include: 157 repo_source = xml_repo.get_source().get_path() 158 repo_type = xml_repo.get_type() 159 uri = Uri(repo_source, repo_type) 160 if not uri.is_public(): 161 raise KiwiRuntimeError( 162 message.format(repo_source) 163 ) 164 165 @staticmethod 166 def check_target_directory_not_in_shared_cache(target_dir: str) -> None: 167 """ 168 The target directory must be outside of the kiwi shared cache 169 directory in order to avoid busy mounts because kiwi bind mounts 170 the cache directory into the image root tree to access host 171 caching information 172 173 :param string target_dir: path name 174 """ 175 message = dedent('''\n 176 Target directory %s conflicts with kiwi's shared cache 177 directory %s. This is going to create a busy loop mount. 178 Please choose another target directory. 179 ''') 180 181 shared_cache_location = Defaults.get_shared_cache_location() 182 183 target_dir_stack = os.path.abspath( 184 os.path.normpath(target_dir) 185 ).replace(os.sep + os.sep, os.sep).split(os.sep) 186 if target_dir_stack[1:4] == shared_cache_location.split(os.sep): 187 raise KiwiRuntimeError( 188 message % (target_dir, shared_cache_location) 189 ) 190 191 def check_volume_label_used_with_lvm(self) -> None: 192 """ 193 The optional volume label in a systemdisk setup is only 194 effective if the LVM, logical volume manager system is 195 used. In any other case where the filesystem itself offers 196 volume management capabilities there are no extra filesystem 197 labels which can be applied per volume 198 """ 199 message = dedent('''\n 200 Custom volume label setup used without LVM 201 202 The optional volume label in a systemdisk setup is only 203 effective if the LVM, logical volume manager system is 204 used. Your setup uses the {0} filesystem which itself 205 offers volume management capabilities. Extra filesystem 206 labels cannot be applied in this case. 207 208 If you want to force LVM over the {0} volume management 209 system you can do so by specifying the following in 210 your KIWI XML description: 211 212 <systemdisk ... preferlvm="true"> 213 <volume .../> 214 </systemdisk> 215 ''') 216 volume_management = self.xml_state.get_volume_management() 217 if volume_management != 'lvm': 218 for volume in self.xml_state.get_volumes(): 219 if volume.label and volume.label != 'SWAP': 220 raise KiwiRuntimeError( 221 message.format(volume_management) 222 ) 223 224 def check_partuuid_persistency_type_used_with_mbr(self) -> None: 225 """ 226 The devicepersistency setting by-partuuid can only be 227 used in combination with a partition table type that 228 supports UUIDs. In any other case Linux creates artificial 229 values for PTUUID and PARTUUID from the disk signature 230 which can change without touching the actual partition 231 table. We consider this unsafe and only allow the use 232 of by-partuuid in combination with partition tables that 233 actually supports it properly. 234 """ 235 message = dedent('''\n 236 devicepersistency={0!r} used with non UUID capable partition table 237 238 PTUUID and PARTUUID exists in the GUID (GPT) partition table. 239 According to the firmware setting: {1!r}, the selected partition 240 table type is: {2!r}. This table type does not natively support 241 UUIDs. In such a case Linux creates artificial values for PTUUID 242 and PARTUUID from the disk signature which can change without 243 touching the actual partition table. This is considered unsafe 244 and KIWI only allows the use of by-partuuid in combination with 245 partition tables that actually supports UUIDs properly. 246 247 Please make sure to use one of the following firmware settings 248 which leads to an image using an UUID capable partition table 249 and therefore supporting consistent by-partuuid device names: 250 251 <type ... firmware="efi|uefi"> 252 ''') 253 persistency_type = self.xml_state.build_type.get_devicepersistency() 254 if persistency_type and persistency_type == 'by-partuuid': 255 supported_table_types = ['gpt'] 256 firmware = FirmWare(self.xml_state) 257 table_type = firmware.get_partition_table_type() 258 if table_type not in supported_table_types: 259 raise KiwiRuntimeError( 260 message.format( 261 persistency_type, firmware.firmware, table_type 262 ) 263 ) 264 265 def check_swap_name_used_with_lvm(self) -> None: 266 """ 267 The optional oem-swapname is only effective if used together 268 with the LVM volume manager. A name for the swap space can 269 only be set if it is created as a LVM volume. In any other 270 case the name does not apply to the system 271 """ 272 message = dedent('''\n 273 Specified swap space name: {0} will not be used 274 275 The specified oem-swapname is used without the LVM volume 276 manager. This means the swap space will be created as simple 277 partition for which no name assignment can take place. 278 The name specified in oem-swapname is used to give the 279 LVM swap volume a name. Outside of LVM the setting is 280 meaningless and should be removed. 281 282 Please delete the following setting from your image 283 description: 284 285 <oem-swapname>{0}</oem-swapname> 286 ''') 287 volume_management = self.xml_state.get_volume_management() 288 if volume_management != 'lvm': 289 oemconfig = self.xml_state.get_build_type_oemconfig_section() 290 if oemconfig and oemconfig.get_oem_swapname(): 291 raise KiwiRuntimeError( 292 message.format(oemconfig.get_oem_swapname()[0]) 293 ) 294 295 def check_volume_setup_defines_reserved_labels(self) -> None: 296 message = dedent('''\n 297 Reserved label name used in LVM volume setup 298 299 The label setup for volume {0} uses the reserved label {1}. 300 Reserved labels used by KIWI internally are {2}. Please 301 choose another label name for this volume. 302 ''') 303 reserved_labels = [ 304 self.xml_state.build_type.get_rootfs_label() or 'ROOT', 305 'SWAP', 'SPARE' 306 ] 307 volume_management = self.xml_state.get_volume_management() 308 if volume_management == 'lvm': 309 for volume in self.xml_state.get_volumes(): 310 # A swap volume is created implicitly if oem-swap is 311 # requested. This volume detected via realpath set to 312 # swap is skipped from the reserved label check as it 313 # intentionally uses the reserved label named SWAP 314 if volume.realpath != 'swap': 315 if volume.label and volume.label in reserved_labels: 316 raise KiwiRuntimeError( 317 message.format( 318 volume.name, volume.label, reserved_labels 319 ) 320 ) 321 322 def check_volume_setup_defines_multiple_fullsize_volumes(self) -> None: 323 """ 324 The volume size specification 'all' makes this volume to 325 take the rest space available on the system. It's only 326 allowed to specify one all size volume 327 """ 328 message = dedent('''\n 329 Multiple all size volumes found but only one is allowed 330 331 The volume size specification 'all' makes this volume to 332 take the rest space available on the system. It's only 333 allowed to specify one all size volume 334 ''') 335 systemdisk_section = self.xml_state.get_build_type_system_disk_section() 336 if systemdisk_section: 337 all_size_volume_count = 0 338 volumes = systemdisk_section.get_volume() or [] 339 for volume in volumes: 340 size = volume.get_size() or volume.get_freespace() 341 if size and 'all' in size: 342 all_size_volume_count += 1 343 if all_size_volume_count > 1: 344 raise KiwiRuntimeError(message) 345 346 def check_volume_setup_has_no_root_definition(self) -> None: 347 """ 348 The root volume in a systemdisk setup is handled in a special 349 way. It is not allowed to setup a custom name or mountpoint for 350 the root volume. Therefore the size of the root volume can be 351 setup via the @root volume name. This check looks up the volume 352 setup and searches if there is a configuration for the '/' 353 mountpoint which would cause the image build to fail 354 """ 355 message = dedent('''\n 356 Volume setup for "/" found. The size of the root volume 357 must be specified via the @root volume name like the 358 following example shows: 359 360 <volume name="@root" size="42G"/> 361 362 A custom name or mountpoint for the root volume is not 363 allowed. 364 ''') 365 for volume in self.xml_state.get_volumes(): 366 if volume.mountpoint == '/': 367 raise KiwiRuntimeError(message) 368 369 def check_container_tool_chain_installed(self) -> None: 370 """ 371 When creating container images the specific tools are used in order 372 to import and export OCI or Docker compatible images. This check 373 searches for those tools to be installed in the build system and 374 fails if it can't find them 375 """ 376 message_tool_not_found = dedent('''\n 377 Required tool {name} not found in caller environment 378 379 Creation of OCI or Docker images requires the tools {name} and 380 skopeo to be installed on the build system. For SUSE based systems 381 you can find the tools at: 382 383 http://download.opensuse.org/repositories/Virtualization:/containers 384 ''') 385 message_version_unsupported = dedent('''\n 386 {name} tool found with unknown version 387 ''') 388 message_unknown_tool = dedent('''\n 389 Unknown tool: {0}. 390 391 Please configure KIWI with an appropriate value (umoci or buildah). 392 Consider this runtime configuration file syntax (/etc/kiwi.yml): 393 394 oci: 395 - archive_tool: umoci | buildah 396 ''') 397 398 expected_version = (0, 1, 0) 399 400 if self.xml_state.get_build_type_name() in ['docker', 'oci']: 401 runtime_config = RuntimeConfig() 402 tool_name = runtime_config.get_oci_archive_tool() 403 if tool_name == 'buildah': 404 oci_tools = ['buildah', 'skopeo'] 405 elif tool_name == 'umoci': 406 oci_tools = ['umoci', 'skopeo'] 407 else: 408 raise KiwiRuntimeError(message_unknown_tool.format(tool_name)) 409 for tool in oci_tools: 410 if not Path.which(filename=tool, access_mode=os.X_OK): 411 raise KiwiRuntimeError( 412 message_tool_not_found.format(name=tool) 413 ) 414 elif not CommandCapabilities.check_version( 415 tool, expected_version, raise_on_error=False 416 ): 417 raise KiwiRuntimeError( 418 message_version_unsupported.format(name=tool) 419 ) 420 self._check_multitag_support() 421 422 def _check_multitag_support(self) -> None: 423 message = dedent('''\n 424 Using additionaltags attribute requires skopeo tool to be 425 capable to handle it, it must include the '--additional-tag' 426 option for copy subcommand (check it running 'skopeo copy 427 --help').\n 428 It is known to be present since v0.1.30 429 ''') 430 if 'additional_names' in self.xml_state.get_container_config(): 431 if not CommandCapabilities.has_option_in_help( 432 'skopeo', '--additional-tag', ['copy', '--help'], 433 raise_on_error=False 434 ): 435 raise KiwiRuntimeError(message) 436 437 def check_luksformat_options_valid(self) -> None: 438 """ 439 Options set via the luksformat element are passed along 440 to the cryptsetup tool. Only options that are known to 441 the tool should be allowed. Thus this runtime check looks 442 up the provided option names if they exist in the cryptsetup 443 version used on the build host 444 """ 445 message = dedent('''\n 446 Option {0!r} not found in cryptsetup 447 448 The Option {0!r} could not be found in the help output 449 of the cryptsetup tool. 450 ''') 451 luksformat = self.xml_state.build_type.get_luksformat() 452 if luksformat: 453 for option in luksformat[0].get_option(): 454 argument = option.get_name() 455 if not CommandCapabilities.has_option_in_help( 456 'cryptsetup', argument, ['--help'], 457 raise_on_error=False 458 ): 459 raise KiwiRuntimeError(message.format(argument)) 460 461 def check_appx_naming_conventions_valid(self) -> None: 462 """ 463 When building wsl images there are some naming conventions that 464 must be fulfilled to run the container on Microsoft Windows 465 """ 466 launcher_pattern = r'[^\\]+(\.[Ee][Xx][Ee])$' 467 message_container_launcher_invalid = dedent('''\n 468 Invalid WSL launcher name: {0} 469 470 WSL launcher name must match the pattern: {1} 471 ''') 472 id_pattern = r'^[a-zA-Z0-9]+$' 473 message_container_id_invalid = dedent('''\n 474 Invalid WSL container application id: {0} 475 476 WSL container id must match the pattern: {1} 477 ''') 478 build_type = self.xml_state.get_build_type_name() 479 container_config = self.xml_state.get_container_config() 480 container_history = container_config.get('history') or {} 481 if build_type == 'appx' and container_config: 482 launcher = container_history.get('launcher') 483 if launcher and not re.match(launcher_pattern, launcher): 484 raise KiwiRuntimeError( 485 message_container_launcher_invalid.format( 486 launcher, launcher_pattern 487 ) 488 ) 489 application_id = container_history.get('application_id') 490 if application_id and not re.match(id_pattern, application_id): 491 raise KiwiRuntimeError( 492 message_container_id_invalid.format( 493 application_id, id_pattern 494 ) 495 ) 496 497 def check_initrd_selection_required(self) -> None: 498 """ 499 If the boot attribute is used without selecting kiwi 500 as the initrd_system, the setting of the boot attribute 501 will not have any effect. We assume that configurations 502 which explicitly specify the boot attribute wants to use 503 the custom kiwi initrd system and not dracut. 504 """ 505 message_kiwi_initrd_system_not_selected = dedent('''\n 506 Missing initrd_system selection for boot attribute 507 508 The selected boot="'{0}'" boot description indicates 509 the custom kiwi initrd system should be used instead 510 of dracut. If this is correct please explicitly request 511 the kiwi initrd system as follows: 512 513 <type initrd_system="kiwi"/> 514 515 If this is not want you want and dracut should be used 516 as initrd system, please delete the boot attribute 517 as it is obsolete in this case. 518 ''') 519 initrd_system = self.xml_state.get_initrd_system() 520 boot_image_reference = self.xml_state.build_type.get_boot() 521 if initrd_system != 'kiwi' and boot_image_reference: 522 raise KiwiRuntimeError( 523 message_kiwi_initrd_system_not_selected.format( 524 boot_image_reference 525 ) 526 ) 527 528 def check_boot_description_exists(self) -> None: 529 """ 530 If a kiwi initrd is used, a lookup to the specified boot 531 description is done and fails early if it does not exist 532 """ 533 message_no_boot_reference = dedent('''\n 534 Boot description missing for '{0}' type 535 536 The selected '{1}' initrd_system requires a boot description 537 reference. Please update your type setup as follows 538 539 <type image="{0}" boot="{0}boot/..."/> 540 541 A collection of custom boot descriptions can be found 542 in the kiwi-boot-descriptions package 543 ''') 544 message_boot_description_not_found = dedent('''\n 545 Boot description '{0}' not found 546 547 The selected boot description could not be found on 548 the build host. A collection of custom boot descriptions 549 can be found in the kiwi-boot-descriptions package 550 ''') 551 image_types_supporting_custom_boot_description = ['oem', 'pxe'] 552 build_type = self.xml_state.get_build_type_name() 553 initrd_system = self.xml_state.get_initrd_system() 554 if initrd_system == 'kiwi' and \ 555 build_type in image_types_supporting_custom_boot_description: 556 557 boot_image_reference = self.xml_state.build_type.get_boot() 558 if not boot_image_reference: 559 raise KiwiRuntimeError( 560 message_no_boot_reference.format(build_type, initrd_system) 561 ) 562 563 if not boot_image_reference[0] == os.sep: 564 boot_image_reference = os.sep.join( 565 [ 566 Defaults.get_boot_image_description_path(), 567 boot_image_reference 568 ] 569 ) 570 if not os.path.exists(boot_image_reference): 571 raise KiwiRuntimeError( 572 message_boot_description_not_found.format( 573 boot_image_reference 574 ) 575 ) 576 577 def check_consistent_kernel_in_boot_and_system_image(self) -> None: 578 """ 579 If a kiwi initrd is used, the kernel used to build the kiwi 580 initrd and the kernel used in the system image must be the 581 same in order to avoid an inconsistent boot setup 582 """ 583 message = dedent('''\n 584 Possible kernel mismatch between kiwi initrd and system image 585 586 The selected '{0}' boot image kernel is '{1}'. However this 587 kernel package was not explicitly listed in the package list 588 of the system image. Please fixup your system image 589 description: 590 591 1) Add <package name="{1}"/> to your system XML description 592 593 2) Inherit kernel from system description to initrd via 594 the custom kernel profile: 595 596 <type ... bootkernel="custom" .../> 597 598 <packages type="image"/> 599 <package name="desired-kernel" bootinclude="true"/> 600 </packages> 601 ''') 602 boot_image_reference = self.xml_state.build_type.get_boot() 603 boot_kernel_package_name = None 604 if boot_image_reference: 605 if not boot_image_reference[0] == '/': 606 boot_image_reference = os.sep.join( 607 [ 608 Defaults.get_boot_image_description_path(), 609 boot_image_reference 610 ] 611 ) 612 boot_config_file = os.sep.join( 613 [boot_image_reference, 'config.xml'] 614 ) 615 if os.path.exists(boot_config_file): 616 boot_description = XMLDescription( 617 description=boot_config_file, 618 derived_from=self.xml_state.xml_data.description_dir 619 ) 620 boot_kernel_profile = \ 621 self.xml_state.build_type.get_bootkernel() 622 if not boot_kernel_profile: 623 boot_kernel_profile = 'std' 624 boot_xml_state = XMLState( 625 boot_description.load(), [boot_kernel_profile] 626 ) 627 kernel_package_sections = [] 628 for packages_section in boot_xml_state.xml_data.get_packages(): 629 # lookup package sections matching kernel profile in kiwi 630 # boot description. By definition this must be a packages 631 # section with a single profile name whereas the default 632 # profile name is 'std'. The section itself must contain 633 # one matching kernel package name for the desired 634 # architecture 635 if packages_section.get_profiles() == boot_kernel_profile: 636 for package in packages_section.get_package(): 637 kernel_package_sections.append(package) 638 639 for package in kernel_package_sections: 640 if boot_xml_state.package_matches_host_architecture( 641 package 642 ): 643 boot_kernel_package_name = package.get_name() 644 645 if boot_kernel_package_name: 646 # A kernel package name was found in the kiwi boot image 647 # description. Let's check if this kernel is also used 648 # in the system image 649 image_package_names = self.xml_state.get_system_packages() 650 if boot_kernel_package_name not in image_package_names: 651 raise KiwiRuntimeError( 652 message.format( 653 self.xml_state.build_type.get_boot(), 654 boot_kernel_package_name 655 ) 656 ) 657 658 def check_dracut_module_versions_compatible_to_kiwi( 659 self, root_dir: str 660 ) -> None: 661 """ 662 KIWI images which makes use of kiwi dracut modules 663 has to use module versions compatible with the version 664 of this KIWI builder code base. This is important to avoid 665 inconsistencies between the way how kiwi includes its own 666 dracut modules and former version of those dracut modules 667 which could be no longer compatible with the builder. 668 Therefore this runtime check maintains a min_version constraint 669 for which we know this KIWI builder to be compatible with. 670 """ 671 message = dedent('''\n 672 Incompatible dracut-kiwi module(s) found 673 674 The image was build with KIWI version={0}. The system 675 root tree has the following dracut-kiwi-* module packages 676 installed which are too old to work with this version of KIWI. 677 Please make sure to use dracut-kiwi-* module packages 678 which are >= than the versions listed below. 679 680 {1} 681 ''') 682 kiwi_dracut_modules = { 683 '55kiwi-dump': dracut_module_type( 684 'dracut-kiwi-oem-dump', '9.20.1' 685 ), 686 '55kiwi-live': dracut_module_type( 687 'dracut-kiwi-live', '9.20.1' 688 ), 689 '55kiwi-overlay': dracut_module_type( 690 'dracut-kiwi-overlay', '9.20.1' 691 ), 692 '55kiwi-repart': dracut_module_type( 693 'dracut-kiwi-oem-repart', '9.20.1' 694 ), 695 '59kiwi-dump-reboot': dracut_module_type( 696 'dracut-kiwi-oem-dump', '9.20.1' 697 ), 698 '59kiwi-lib': dracut_module_type( 699 'dracut-kiwi-lib', '9.20.1' 700 ) 701 } 702 dracut_module_dir = os.sep.join( 703 [root_dir, '/usr/lib/dracut/modules.d'] 704 ) 705 if not os.path.isdir(dracut_module_dir): 706 # no dracut module dir present 707 return 708 709 incompatible_modules = {} 710 for module in os.listdir(dracut_module_dir): 711 module_meta = kiwi_dracut_modules.get(module) 712 if module_meta: 713 module_version = self._get_dracut_module_version_from_pdb( 714 self.xml_state.get_package_manager(), 715 module_meta.package, root_dir 716 ) 717 if module_version: 718 module_version_nr = tuple( 719 int(it) for it in module_version.split('.') 720 ) 721 module_min_version_nr = tuple( 722 int(it) for it in module_meta.min_version.split('.') 723 ) 724 if module_version_nr < module_min_version_nr: 725 incompatible_modules[ 726 module_meta.package 727 ] = 'got:{0}, need:>={1}'.format( 728 module_version, module_meta.min_version 729 ) 730 if incompatible_modules: 731 raise KiwiRuntimeError( 732 message.format(__version__, incompatible_modules) 733 ) 734 735 def check_dracut_module_for_oem_install_in_package_list(self) -> None: 736 """ 737 OEM images if configured to use dracut as initrd system 738 and configured with one of the installiso, installstick 739 or installpxe attributes requires the KIWI provided 740 dracut-kiwi-oem-dump module to be installed at the time 741 dracut is called. Thus this runtime check examines if the 742 required package is part of the package list in the 743 image description. 744 """ 745 message = dedent('''\n 746 Required dracut module package missing in package list 747 748 One of the packages '{0}' is required 749 to build an installation image for the selected oem image type. 750 Depending on your distribution, add the following in the 751 <packages type="image"> section: 752 753 <package name="ONE_FROM_ABOVE"/> 754 ''') 755 meta = Defaults.get_runtime_checker_metadata() 756 required_dracut_packages = meta['package_names']['dracut_oem_dump'] 757 initrd_system = self.xml_state.get_initrd_system() 758 build_type = self.xml_state.get_build_type_name() 759 if build_type == 'oem' and initrd_system == 'dracut': 760 install_iso = self.xml_state.build_type.get_installiso() 761 install_stick = self.xml_state.build_type.get_installstick() 762 install_pxe = self.xml_state.build_type.get_installpxe() 763 if install_iso or install_stick or install_pxe: 764 package_names = \ 765 self.xml_state.get_bootstrap_packages() + \ 766 self.xml_state.get_system_packages() 767 if not RuntimeChecker._package_in_list( 768 package_names, required_dracut_packages 769 ): 770 raise KiwiRuntimeError( 771 message.format(required_dracut_packages) 772 ) 773 774 def check_dracut_module_for_disk_oem_in_package_list(self) -> None: 775 """ 776 OEM images if configured to use dracut as initrd system 777 requires the KIWI provided dracut-kiwi-oem-repart module 778 to be installed at the time dracut is called. Thus this 779 runtime check examines if the required package is part of 780 the package list in the image description. 781 """ 782 message = dedent('''\n 783 Required dracut module package missing in package list 784 785 One of the packages '{0}' is required 786 for the selected oem image type. Depending on your distribution, 787 add the following in the <packages type="image"> section: 788 789 <package name="ONE_FROM_ABOVE"/> 790 ''') 791 meta = Defaults.get_runtime_checker_metadata() 792 required_dracut_packages = meta['package_names']['dracut_oem_repart'] 793 initrd_system = self.xml_state.get_initrd_system() 794 disk_resize_requested = self.xml_state.get_oemconfig_oem_resize() 795 build_type = self.xml_state.get_build_type_name() 796 if build_type == 'oem' and initrd_system == 'dracut' and \ 797 disk_resize_requested: 798 package_names = \ 799 self.xml_state.get_bootstrap_packages() + \ 800 self.xml_state.get_system_packages() 801 if not RuntimeChecker._package_in_list( 802 package_names, required_dracut_packages 803 ): 804 raise KiwiRuntimeError( 805 message.format(required_dracut_packages) 806 ) 807 808 def check_dracut_module_for_live_iso_in_package_list(self) -> None: 809 """ 810 Live ISO images uses a dracut initrd to boot and requires 811 the KIWI provided kiwi-live dracut module to be installed 812 at the time dracut is called. Thus this runtime check 813 examines if the required package is part of the package 814 list in the image description. 815 """ 816 message = dedent('''\n 817 Required dracut module package missing in package list 818 819 One of the packages '{0}' is required 820 for the selected live iso image type. Depending on your distribution, 821 add the following in your <packages type="image"> section: 822 823 <package name="ONE_FROM_ABOVE"/> 824 ''') 825 meta = Defaults.get_runtime_checker_metadata() 826 required_dracut_packages = meta['package_names']['dracut_live'] 827 type_name = self.xml_state.get_build_type_name() 828 type_flag = self.xml_state.build_type.get_flags() 829 if type_name == 'iso' and type_flag != 'dmsquash': 830 package_names = \ 831 self.xml_state.get_bootstrap_packages() + \ 832 self.xml_state.get_system_packages() 833 if not RuntimeChecker._package_in_list( 834 package_names, required_dracut_packages 835 ): 836 raise KiwiRuntimeError( 837 message.format(required_dracut_packages) 838 ) 839 840 def check_dracut_module_for_disk_overlay_in_package_list(self) -> None: 841 """ 842 Disk images configured to use a root filesystem overlay 843 requires the KIWI provided kiwi-overlay dracut module to 844 be installed at the time dracut is called. Thus this 845 runtime check examines if the required package is part of 846 the package list in the image description. 847 """ 848 message = dedent('''\n 849 Required dracut module package missing in package list 850 851 The package '{0}' is required 852 for the selected overlayroot activated image type. 853 Depending on your distribution, add the following in your 854 <packages type="image"> section: 855 856 <package name="ONE_FROM_ABOVE"/> 857 ''') 858 initrd_system = self.xml_state.get_initrd_system() 859 meta = Defaults.get_runtime_checker_metadata() 860 required_dracut_packages = meta['package_names']['dracut_overlay'] 861 if initrd_system == 'dracut' and \ 862 self.xml_state.build_type.get_overlayroot(): 863 package_names = \ 864 self.xml_state.get_bootstrap_packages() + \ 865 self.xml_state.get_system_packages() 866 if not RuntimeChecker._package_in_list( 867 package_names, required_dracut_packages 868 ): 869 raise KiwiRuntimeError( 870 message.format(required_dracut_packages) 871 ) 872 873 def check_xen_uniquely_setup_as_server_or_guest(self) -> None: 874 """ 875 If the image is classified to be used as Xen image, it can 876 be either a Xen Server(dom0) or a Xen guest. The image 877 configuration is checked if the information uniquely identifies 878 the image as such 879 """ 880 xen_message = dedent('''\n 881 Inconsistent Xen setup found: 882 883 The use of the 'xen_server' or 'xen_loader' attributes indicates 884 the target system for this image is Xen. However the image 885 specifies both attributes at the same time which classifies 886 the image to be both, a Xen Server(dom0) and a Xen guest at 887 the same time, which is not supported. 888 889 Please cleanup your image description. Setup only one 890 of 'xen_server' or 'xen_loader'. 891 ''') 892 ec2_message = dedent('''\n 893 Inconsistent Amazon EC2 setup found: 894 895 The firmware setup indicates the target system for this image 896 is Amazon EC2, which uses a Xen based virtualisation technology. 897 Therefore the image must be classified as a Xen guest and can 898 not be a Xen server as indicated by the 'xen_server' attribute 899 900 Please cleanup your image description. Delete the 'xen_server' 901 attribute for images used with Amazon EC2. 902 ''') 903 if self.xml_state.is_xen_server() and self.xml_state.is_xen_guest(): 904 firmware = self.xml_state.build_type.get_firmware() 905 ec2_firmware_names = Defaults.get_ec2_capable_firmware_names() 906 if firmware and firmware in ec2_firmware_names: 907 raise KiwiRuntimeError(ec2_message) 908 else: 909 raise KiwiRuntimeError(xen_message) 910 911 def check_mediacheck_installed(self) -> None: 912 """ 913 If the image description enables the mediacheck attribute 914 the required tools to run this check must be installed 915 on the image build host 916 """ 917 message_tool_not_found = dedent('''\n 918 Required tool {name} not found in caller environment 919 920 The attribute 'mediacheck' is set to 'true' which requires 921 the above tool to be installed on the build system 922 ''') 923 if self.xml_state.build_type.get_mediacheck() is True: 924 tool = 'tagmedia' 925 media_tagger = RuntimeConfig().get_iso_media_tag_tool() 926 if media_tagger == 'checkmedia': 927 tool = 'tagmedia' 928 elif media_tagger == 'isomd5sum': 929 tool = 'implantisomd5' 930 if not Path.which(filename=tool, access_mode=os.X_OK): 931 raise KiwiRuntimeError( 932 message_tool_not_found.format(name=tool) 933 ) 934 935 def check_image_version_provided(self) -> None: 936 """ 937 Kiwi requires a <version> element to be specified as part 938 of at least one <preferences> section. 939 """ 940 message_missing_version = dedent('''\n 941 No version is defined in any of the <preferences> 942 sections. Please add 943 944 <version>image_version<version/> 945 946 inside the <preferences> section. 947 ''') 948 949 if not self.xml_state.get_image_version(): 950 raise KiwiRuntimeError(message_missing_version) 951 952 def check_image_type_unique(self) -> None: 953 """ 954 Verify that the selected image type is unique within 955 the range of the configured types and profiles. 956 """ 957 message = dedent('''\n 958 Conflicting image type setup detected 959 960 The selected image type '{0}' in the {1} profile 961 selection is not unique. There are the following type 962 settings which overrides each other: 963 {2} 964 To solve this conflict please move the image type 965 setup into its own profile and select them using 966 the --profile option at call time. 967 ''') 968 image_type_sections = [] 969 type_dict: Dict[str, List[Any]] = {} 970 for preferences in self.xml_state.get_preferences_sections(): 971 image_type_sections += preferences.get_type() 972 973 for image_type in image_type_sections: 974 type_name = image_type.get_image() 975 if type_dict.get(type_name): 976 type_dict[type_name].append(image_type) 977 else: 978 type_dict[type_name] = [image_type] 979 980 for type_name, type_list in list(type_dict.items()): 981 if len(type_list) > 1: 982 type_export = StringIO() 983 for image_type in type_list: 984 type_export.write(os.linesep) 985 image_type.export(type_export, 0) 986 raise KiwiRuntimeError( 987 message.format( 988 type_name, self.xml_state.profiles or ['Default'], 989 type_export.getvalue() 990 ) 991 ) 992 993 def check_efi_fat_image_has_correct_size(self) -> None: 994 """ 995 Verify that the efifatimagesize does not exceed the max 996 El Torito load size of 65535 * 512 bytes 997 """ 998 message = dedent('''\n 999 El Torito max load size exceeded 1000 1001 The configured efifatimagesize of '{0}MB' exceeds 1002 the El Torito max load size of 65535 * 512 bytes (~31MB). 1003 ''') 1004 fat_image_mbsize = int( 1005 self.xml_state.build_type 1006 .get_efifatimagesize() or defaults.EFI_FAT_IMAGE_SIZE 1007 ) 1008 if fat_image_mbsize > 31: 1009 raise KiwiRuntimeError( 1010 message.format(fat_image_mbsize) 1011 ) 1012 1013 def check_bootloader_env_compatible_with_loader(self) -> None: 1014 """ 1015 If there is an environment section as part of the bootloader 1016 section, check if the selected loader supports custom 1017 environment blobs 1018 """ 1019 message = dedent('''\n 1020 Selected loader does not support custom environments 1021 1022 The selected bootloader {} does not support custom 1023 environment settings. Please drop the <environment> 1024 setting from <bootloadersettings> for this loader 1025 ''') 1026 env_supported_loaders = [ 1027 'grub2', 1028 'grub2_s390x_emu', 1029 'custom' 1030 ] 1031 bootloader = self.xml_state.get_build_type_bootloader_name() 1032 variable_list = self.xml_state.\ 1033 get_build_type_bootloader_environment_variables() 1034 if variable_list and bootloader not in env_supported_loaders: 1035 raise KiwiRuntimeError( 1036 message.format(bootloader) 1037 ) 1038 1039 @staticmethod 1040 def _package_in_list( 1041 package_list: List[str], search_list: List[str] 1042 ) -> str: 1043 result = '' 1044 for search in search_list: 1045 if search in package_list: 1046 result = search 1047 break 1048 return result 1049 1050 @staticmethod 1051 def _get_dracut_module_version_from_pdb( 1052 package_manager: str, package_name: str, root_dir: str 1053 ) -> str: 1054 tool = Defaults.get_default_packager_tool(package_manager) 1055 package_query = None 1056 package_manager_query = None 1057 package_version = '' 1058 if tool == 'rpm': 1059 package_manager_query = [ 1060 'chroot', root_dir, tool, '-q', '--qf', 1061 '%{VERSION}', package_name 1062 ] 1063 elif tool == 'dpkg': 1064 package_manager_query = [ 1065 'chroot', root_dir, 'dpkg-query', '-W', '-f', 1066 '${Version}', package_name 1067 ] 1068 if package_manager_query: 1069 try: 1070 package_query = Command.run(package_manager_query) 1071 if package_query: 1072 package_version = package_query.output.split('-', 1)[0] 1073 except Exception as issue: 1074 log.debug(f'Package manager query failed with: {issue}') 1075 return package_version
dracut_module_type(package, min_version)
56class RuntimeChecker: 57 """ 58 **Implements build consistency checks at runtime** 59 """ 60 def __init__(self, xml_state: XMLState) -> None: 61 """ 62 The schema of an image description covers structure and syntax of 63 the provided data. The RuntimeChecker provides methods to perform 64 further semantic checks which allows to recognize potential build 65 or boot problems early. 66 67 :param object xml_state: Instance of XMLState 68 """ 69 self.xml_state = xml_state 70 71 def check_repositories_configured(self) -> None: 72 """ 73 Verify that there are repositories configured 74 """ 75 if not self.xml_state.get_repository_sections(): 76 raise KiwiRuntimeError( 77 'No repositories configured' 78 ) 79 80 @staticmethod 81 def check_target_dir_on_unsupported_filesystem(target_dir: str) -> None: 82 """ 83 Raise if the given target dir does not reside on a 84 filesystem that supports all important features like 85 extended permissions(fscaps), ACLs or xattrs. 86 """ 87 message = dedent('''\n 88 Target root/image directory is lacking filesystem features 89 90 The filesystem {0} in the target path {1} 91 does not support important features like extended permissions, 92 ACLs or xattrs. The image build may fail or the resulting 93 image misbehave. 94 ''') 95 target_dir = Path.first_exists(target_dir) 96 stat = Command.run(['stat', '-f', '-c', '%T', target_dir]) 97 if stat: 98 target_fs = stat.output.strip() 99 supported_target_filesystem = ( 100 'btrfs', 101 'ext2', 102 'ext2/ext3', 103 'ext3', 104 'ext4', 105 'overlayfs', 106 'tmpfs', 107 'xfs', 108 ) 109 if target_fs not in supported_target_filesystem: 110 raise KiwiRuntimeError(message.format(target_fs, target_dir)) 111 112 def check_include_references_unresolvable(self) -> None: 113 """ 114 Raise for still included <include> statements as not resolvable. 115 The KIWI XSLT processing replaces the specified include 116 directive(s) with the given file reference(s). If this action 117 did not happen for example on nested includes, it can happen 118 that they stay in the document as sort of waste. 119 """ 120 message = dedent('''\n 121 One ore more <include> statements are unresolvable 122 123 The following include references could not be resolved. 124 Please verify the specified location(s) and/or delete 125 the broken include directive(s) from the description. 126 Please also note, nested includes from other include 127 files are not supported: 128 129 {0} 130 ''') 131 include_files = \ 132 self.xml_state.get_include_section_reference_file_names() 133 if include_files: 134 raise KiwiRuntimeError( 135 message.format(json.dumps(include_files, indent=4)) 136 ) 137 138 def check_image_include_repos_publicly_resolvable(self) -> None: 139 """ 140 Verify that all repos marked with the imageinclude attribute 141 can be resolved into a http based web URL 142 """ 143 message = dedent('''\n 144 The use of imageinclude="true" in the repository definition 145 for the Repository: {0} 146 requires the repository to by publicly available. The source 147 locator of the repository however indicates it is private to 148 your local system. Therefore it can't be included into the 149 system image repository configuration. Please define a publicly 150 available repository in your image XML description. 151 ''') 152 153 repository_sections = self.xml_state.get_repository_sections() 154 for xml_repo in repository_sections: 155 repo_marked_for_image_include = xml_repo.get_imageinclude() 156 157 if repo_marked_for_image_include: 158 repo_source = xml_repo.get_source().get_path() 159 repo_type = xml_repo.get_type() 160 uri = Uri(repo_source, repo_type) 161 if not uri.is_public(): 162 raise KiwiRuntimeError( 163 message.format(repo_source) 164 ) 165 166 @staticmethod 167 def check_target_directory_not_in_shared_cache(target_dir: str) -> None: 168 """ 169 The target directory must be outside of the kiwi shared cache 170 directory in order to avoid busy mounts because kiwi bind mounts 171 the cache directory into the image root tree to access host 172 caching information 173 174 :param string target_dir: path name 175 """ 176 message = dedent('''\n 177 Target directory %s conflicts with kiwi's shared cache 178 directory %s. This is going to create a busy loop mount. 179 Please choose another target directory. 180 ''') 181 182 shared_cache_location = Defaults.get_shared_cache_location() 183 184 target_dir_stack = os.path.abspath( 185 os.path.normpath(target_dir) 186 ).replace(os.sep + os.sep, os.sep).split(os.sep) 187 if target_dir_stack[1:4] == shared_cache_location.split(os.sep): 188 raise KiwiRuntimeError( 189 message % (target_dir, shared_cache_location) 190 ) 191 192 def check_volume_label_used_with_lvm(self) -> None: 193 """ 194 The optional volume label in a systemdisk setup is only 195 effective if the LVM, logical volume manager system is 196 used. In any other case where the filesystem itself offers 197 volume management capabilities there are no extra filesystem 198 labels which can be applied per volume 199 """ 200 message = dedent('''\n 201 Custom volume label setup used without LVM 202 203 The optional volume label in a systemdisk setup is only 204 effective if the LVM, logical volume manager system is 205 used. Your setup uses the {0} filesystem which itself 206 offers volume management capabilities. Extra filesystem 207 labels cannot be applied in this case. 208 209 If you want to force LVM over the {0} volume management 210 system you can do so by specifying the following in 211 your KIWI XML description: 212 213 <systemdisk ... preferlvm="true"> 214 <volume .../> 215 </systemdisk> 216 ''') 217 volume_management = self.xml_state.get_volume_management() 218 if volume_management != 'lvm': 219 for volume in self.xml_state.get_volumes(): 220 if volume.label and volume.label != 'SWAP': 221 raise KiwiRuntimeError( 222 message.format(volume_management) 223 ) 224 225 def check_partuuid_persistency_type_used_with_mbr(self) -> None: 226 """ 227 The devicepersistency setting by-partuuid can only be 228 used in combination with a partition table type that 229 supports UUIDs. In any other case Linux creates artificial 230 values for PTUUID and PARTUUID from the disk signature 231 which can change without touching the actual partition 232 table. We consider this unsafe and only allow the use 233 of by-partuuid in combination with partition tables that 234 actually supports it properly. 235 """ 236 message = dedent('''\n 237 devicepersistency={0!r} used with non UUID capable partition table 238 239 PTUUID and PARTUUID exists in the GUID (GPT) partition table. 240 According to the firmware setting: {1!r}, the selected partition 241 table type is: {2!r}. This table type does not natively support 242 UUIDs. In such a case Linux creates artificial values for PTUUID 243 and PARTUUID from the disk signature which can change without 244 touching the actual partition table. This is considered unsafe 245 and KIWI only allows the use of by-partuuid in combination with 246 partition tables that actually supports UUIDs properly. 247 248 Please make sure to use one of the following firmware settings 249 which leads to an image using an UUID capable partition table 250 and therefore supporting consistent by-partuuid device names: 251 252 <type ... firmware="efi|uefi"> 253 ''') 254 persistency_type = self.xml_state.build_type.get_devicepersistency() 255 if persistency_type and persistency_type == 'by-partuuid': 256 supported_table_types = ['gpt'] 257 firmware = FirmWare(self.xml_state) 258 table_type = firmware.get_partition_table_type() 259 if table_type not in supported_table_types: 260 raise KiwiRuntimeError( 261 message.format( 262 persistency_type, firmware.firmware, table_type 263 ) 264 ) 265 266 def check_swap_name_used_with_lvm(self) -> None: 267 """ 268 The optional oem-swapname is only effective if used together 269 with the LVM volume manager. A name for the swap space can 270 only be set if it is created as a LVM volume. In any other 271 case the name does not apply to the system 272 """ 273 message = dedent('''\n 274 Specified swap space name: {0} will not be used 275 276 The specified oem-swapname is used without the LVM volume 277 manager. This means the swap space will be created as simple 278 partition for which no name assignment can take place. 279 The name specified in oem-swapname is used to give the 280 LVM swap volume a name. Outside of LVM the setting is 281 meaningless and should be removed. 282 283 Please delete the following setting from your image 284 description: 285 286 <oem-swapname>{0}</oem-swapname> 287 ''') 288 volume_management = self.xml_state.get_volume_management() 289 if volume_management != 'lvm': 290 oemconfig = self.xml_state.get_build_type_oemconfig_section() 291 if oemconfig and oemconfig.get_oem_swapname(): 292 raise KiwiRuntimeError( 293 message.format(oemconfig.get_oem_swapname()[0]) 294 ) 295 296 def check_volume_setup_defines_reserved_labels(self) -> None: 297 message = dedent('''\n 298 Reserved label name used in LVM volume setup 299 300 The label setup for volume {0} uses the reserved label {1}. 301 Reserved labels used by KIWI internally are {2}. Please 302 choose another label name for this volume. 303 ''') 304 reserved_labels = [ 305 self.xml_state.build_type.get_rootfs_label() or 'ROOT', 306 'SWAP', 'SPARE' 307 ] 308 volume_management = self.xml_state.get_volume_management() 309 if volume_management == 'lvm': 310 for volume in self.xml_state.get_volumes(): 311 # A swap volume is created implicitly if oem-swap is 312 # requested. This volume detected via realpath set to 313 # swap is skipped from the reserved label check as it 314 # intentionally uses the reserved label named SWAP 315 if volume.realpath != 'swap': 316 if volume.label and volume.label in reserved_labels: 317 raise KiwiRuntimeError( 318 message.format( 319 volume.name, volume.label, reserved_labels 320 ) 321 ) 322 323 def check_volume_setup_defines_multiple_fullsize_volumes(self) -> None: 324 """ 325 The volume size specification 'all' makes this volume to 326 take the rest space available on the system. It's only 327 allowed to specify one all size volume 328 """ 329 message = dedent('''\n 330 Multiple all size volumes found but only one is allowed 331 332 The volume size specification 'all' makes this volume to 333 take the rest space available on the system. It's only 334 allowed to specify one all size volume 335 ''') 336 systemdisk_section = self.xml_state.get_build_type_system_disk_section() 337 if systemdisk_section: 338 all_size_volume_count = 0 339 volumes = systemdisk_section.get_volume() or [] 340 for volume in volumes: 341 size = volume.get_size() or volume.get_freespace() 342 if size and 'all' in size: 343 all_size_volume_count += 1 344 if all_size_volume_count > 1: 345 raise KiwiRuntimeError(message) 346 347 def check_volume_setup_has_no_root_definition(self) -> None: 348 """ 349 The root volume in a systemdisk setup is handled in a special 350 way. It is not allowed to setup a custom name or mountpoint for 351 the root volume. Therefore the size of the root volume can be 352 setup via the @root volume name. This check looks up the volume 353 setup and searches if there is a configuration for the '/' 354 mountpoint which would cause the image build to fail 355 """ 356 message = dedent('''\n 357 Volume setup for "/" found. The size of the root volume 358 must be specified via the @root volume name like the 359 following example shows: 360 361 <volume name="@root" size="42G"/> 362 363 A custom name or mountpoint for the root volume is not 364 allowed. 365 ''') 366 for volume in self.xml_state.get_volumes(): 367 if volume.mountpoint == '/': 368 raise KiwiRuntimeError(message) 369 370 def check_container_tool_chain_installed(self) -> None: 371 """ 372 When creating container images the specific tools are used in order 373 to import and export OCI or Docker compatible images. This check 374 searches for those tools to be installed in the build system and 375 fails if it can't find them 376 """ 377 message_tool_not_found = dedent('''\n 378 Required tool {name} not found in caller environment 379 380 Creation of OCI or Docker images requires the tools {name} and 381 skopeo to be installed on the build system. For SUSE based systems 382 you can find the tools at: 383 384 http://download.opensuse.org/repositories/Virtualization:/containers 385 ''') 386 message_version_unsupported = dedent('''\n 387 {name} tool found with unknown version 388 ''') 389 message_unknown_tool = dedent('''\n 390 Unknown tool: {0}. 391 392 Please configure KIWI with an appropriate value (umoci or buildah). 393 Consider this runtime configuration file syntax (/etc/kiwi.yml): 394 395 oci: 396 - archive_tool: umoci | buildah 397 ''') 398 399 expected_version = (0, 1, 0) 400 401 if self.xml_state.get_build_type_name() in ['docker', 'oci']: 402 runtime_config = RuntimeConfig() 403 tool_name = runtime_config.get_oci_archive_tool() 404 if tool_name == 'buildah': 405 oci_tools = ['buildah', 'skopeo'] 406 elif tool_name == 'umoci': 407 oci_tools = ['umoci', 'skopeo'] 408 else: 409 raise KiwiRuntimeError(message_unknown_tool.format(tool_name)) 410 for tool in oci_tools: 411 if not Path.which(filename=tool, access_mode=os.X_OK): 412 raise KiwiRuntimeError( 413 message_tool_not_found.format(name=tool) 414 ) 415 elif not CommandCapabilities.check_version( 416 tool, expected_version, raise_on_error=False 417 ): 418 raise KiwiRuntimeError( 419 message_version_unsupported.format(name=tool) 420 ) 421 self._check_multitag_support() 422 423 def _check_multitag_support(self) -> None: 424 message = dedent('''\n 425 Using additionaltags attribute requires skopeo tool to be 426 capable to handle it, it must include the '--additional-tag' 427 option for copy subcommand (check it running 'skopeo copy 428 --help').\n 429 It is known to be present since v0.1.30 430 ''') 431 if 'additional_names' in self.xml_state.get_container_config(): 432 if not CommandCapabilities.has_option_in_help( 433 'skopeo', '--additional-tag', ['copy', '--help'], 434 raise_on_error=False 435 ): 436 raise KiwiRuntimeError(message) 437 438 def check_luksformat_options_valid(self) -> None: 439 """ 440 Options set via the luksformat element are passed along 441 to the cryptsetup tool. Only options that are known to 442 the tool should be allowed. Thus this runtime check looks 443 up the provided option names if they exist in the cryptsetup 444 version used on the build host 445 """ 446 message = dedent('''\n 447 Option {0!r} not found in cryptsetup 448 449 The Option {0!r} could not be found in the help output 450 of the cryptsetup tool. 451 ''') 452 luksformat = self.xml_state.build_type.get_luksformat() 453 if luksformat: 454 for option in luksformat[0].get_option(): 455 argument = option.get_name() 456 if not CommandCapabilities.has_option_in_help( 457 'cryptsetup', argument, ['--help'], 458 raise_on_error=False 459 ): 460 raise KiwiRuntimeError(message.format(argument)) 461 462 def check_appx_naming_conventions_valid(self) -> None: 463 """ 464 When building wsl images there are some naming conventions that 465 must be fulfilled to run the container on Microsoft Windows 466 """ 467 launcher_pattern = r'[^\\]+(\.[Ee][Xx][Ee])$' 468 message_container_launcher_invalid = dedent('''\n 469 Invalid WSL launcher name: {0} 470 471 WSL launcher name must match the pattern: {1} 472 ''') 473 id_pattern = r'^[a-zA-Z0-9]+$' 474 message_container_id_invalid = dedent('''\n 475 Invalid WSL container application id: {0} 476 477 WSL container id must match the pattern: {1} 478 ''') 479 build_type = self.xml_state.get_build_type_name() 480 container_config = self.xml_state.get_container_config() 481 container_history = container_config.get('history') or {} 482 if build_type == 'appx' and container_config: 483 launcher = container_history.get('launcher') 484 if launcher and not re.match(launcher_pattern, launcher): 485 raise KiwiRuntimeError( 486 message_container_launcher_invalid.format( 487 launcher, launcher_pattern 488 ) 489 ) 490 application_id = container_history.get('application_id') 491 if application_id and not re.match(id_pattern, application_id): 492 raise KiwiRuntimeError( 493 message_container_id_invalid.format( 494 application_id, id_pattern 495 ) 496 ) 497 498 def check_initrd_selection_required(self) -> None: 499 """ 500 If the boot attribute is used without selecting kiwi 501 as the initrd_system, the setting of the boot attribute 502 will not have any effect. We assume that configurations 503 which explicitly specify the boot attribute wants to use 504 the custom kiwi initrd system and not dracut. 505 """ 506 message_kiwi_initrd_system_not_selected = dedent('''\n 507 Missing initrd_system selection for boot attribute 508 509 The selected boot="'{0}'" boot description indicates 510 the custom kiwi initrd system should be used instead 511 of dracut. If this is correct please explicitly request 512 the kiwi initrd system as follows: 513 514 <type initrd_system="kiwi"/> 515 516 If this is not want you want and dracut should be used 517 as initrd system, please delete the boot attribute 518 as it is obsolete in this case. 519 ''') 520 initrd_system = self.xml_state.get_initrd_system() 521 boot_image_reference = self.xml_state.build_type.get_boot() 522 if initrd_system != 'kiwi' and boot_image_reference: 523 raise KiwiRuntimeError( 524 message_kiwi_initrd_system_not_selected.format( 525 boot_image_reference 526 ) 527 ) 528 529 def check_boot_description_exists(self) -> None: 530 """ 531 If a kiwi initrd is used, a lookup to the specified boot 532 description is done and fails early if it does not exist 533 """ 534 message_no_boot_reference = dedent('''\n 535 Boot description missing for '{0}' type 536 537 The selected '{1}' initrd_system requires a boot description 538 reference. Please update your type setup as follows 539 540 <type image="{0}" boot="{0}boot/..."/> 541 542 A collection of custom boot descriptions can be found 543 in the kiwi-boot-descriptions package 544 ''') 545 message_boot_description_not_found = dedent('''\n 546 Boot description '{0}' not found 547 548 The selected boot description could not be found on 549 the build host. A collection of custom boot descriptions 550 can be found in the kiwi-boot-descriptions package 551 ''') 552 image_types_supporting_custom_boot_description = ['oem', 'pxe'] 553 build_type = self.xml_state.get_build_type_name() 554 initrd_system = self.xml_state.get_initrd_system() 555 if initrd_system == 'kiwi' and \ 556 build_type in image_types_supporting_custom_boot_description: 557 558 boot_image_reference = self.xml_state.build_type.get_boot() 559 if not boot_image_reference: 560 raise KiwiRuntimeError( 561 message_no_boot_reference.format(build_type, initrd_system) 562 ) 563 564 if not boot_image_reference[0] == os.sep: 565 boot_image_reference = os.sep.join( 566 [ 567 Defaults.get_boot_image_description_path(), 568 boot_image_reference 569 ] 570 ) 571 if not os.path.exists(boot_image_reference): 572 raise KiwiRuntimeError( 573 message_boot_description_not_found.format( 574 boot_image_reference 575 ) 576 ) 577 578 def check_consistent_kernel_in_boot_and_system_image(self) -> None: 579 """ 580 If a kiwi initrd is used, the kernel used to build the kiwi 581 initrd and the kernel used in the system image must be the 582 same in order to avoid an inconsistent boot setup 583 """ 584 message = dedent('''\n 585 Possible kernel mismatch between kiwi initrd and system image 586 587 The selected '{0}' boot image kernel is '{1}'. However this 588 kernel package was not explicitly listed in the package list 589 of the system image. Please fixup your system image 590 description: 591 592 1) Add <package name="{1}"/> to your system XML description 593 594 2) Inherit kernel from system description to initrd via 595 the custom kernel profile: 596 597 <type ... bootkernel="custom" .../> 598 599 <packages type="image"/> 600 <package name="desired-kernel" bootinclude="true"/> 601 </packages> 602 ''') 603 boot_image_reference = self.xml_state.build_type.get_boot() 604 boot_kernel_package_name = None 605 if boot_image_reference: 606 if not boot_image_reference[0] == '/': 607 boot_image_reference = os.sep.join( 608 [ 609 Defaults.get_boot_image_description_path(), 610 boot_image_reference 611 ] 612 ) 613 boot_config_file = os.sep.join( 614 [boot_image_reference, 'config.xml'] 615 ) 616 if os.path.exists(boot_config_file): 617 boot_description = XMLDescription( 618 description=boot_config_file, 619 derived_from=self.xml_state.xml_data.description_dir 620 ) 621 boot_kernel_profile = \ 622 self.xml_state.build_type.get_bootkernel() 623 if not boot_kernel_profile: 624 boot_kernel_profile = 'std' 625 boot_xml_state = XMLState( 626 boot_description.load(), [boot_kernel_profile] 627 ) 628 kernel_package_sections = [] 629 for packages_section in boot_xml_state.xml_data.get_packages(): 630 # lookup package sections matching kernel profile in kiwi 631 # boot description. By definition this must be a packages 632 # section with a single profile name whereas the default 633 # profile name is 'std'. The section itself must contain 634 # one matching kernel package name for the desired 635 # architecture 636 if packages_section.get_profiles() == boot_kernel_profile: 637 for package in packages_section.get_package(): 638 kernel_package_sections.append(package) 639 640 for package in kernel_package_sections: 641 if boot_xml_state.package_matches_host_architecture( 642 package 643 ): 644 boot_kernel_package_name = package.get_name() 645 646 if boot_kernel_package_name: 647 # A kernel package name was found in the kiwi boot image 648 # description. Let's check if this kernel is also used 649 # in the system image 650 image_package_names = self.xml_state.get_system_packages() 651 if boot_kernel_package_name not in image_package_names: 652 raise KiwiRuntimeError( 653 message.format( 654 self.xml_state.build_type.get_boot(), 655 boot_kernel_package_name 656 ) 657 ) 658 659 def check_dracut_module_versions_compatible_to_kiwi( 660 self, root_dir: str 661 ) -> None: 662 """ 663 KIWI images which makes use of kiwi dracut modules 664 has to use module versions compatible with the version 665 of this KIWI builder code base. This is important to avoid 666 inconsistencies between the way how kiwi includes its own 667 dracut modules and former version of those dracut modules 668 which could be no longer compatible with the builder. 669 Therefore this runtime check maintains a min_version constraint 670 for which we know this KIWI builder to be compatible with. 671 """ 672 message = dedent('''\n 673 Incompatible dracut-kiwi module(s) found 674 675 The image was build with KIWI version={0}. The system 676 root tree has the following dracut-kiwi-* module packages 677 installed which are too old to work with this version of KIWI. 678 Please make sure to use dracut-kiwi-* module packages 679 which are >= than the versions listed below. 680 681 {1} 682 ''') 683 kiwi_dracut_modules = { 684 '55kiwi-dump': dracut_module_type( 685 'dracut-kiwi-oem-dump', '9.20.1' 686 ), 687 '55kiwi-live': dracut_module_type( 688 'dracut-kiwi-live', '9.20.1' 689 ), 690 '55kiwi-overlay': dracut_module_type( 691 'dracut-kiwi-overlay', '9.20.1' 692 ), 693 '55kiwi-repart': dracut_module_type( 694 'dracut-kiwi-oem-repart', '9.20.1' 695 ), 696 '59kiwi-dump-reboot': dracut_module_type( 697 'dracut-kiwi-oem-dump', '9.20.1' 698 ), 699 '59kiwi-lib': dracut_module_type( 700 'dracut-kiwi-lib', '9.20.1' 701 ) 702 } 703 dracut_module_dir = os.sep.join( 704 [root_dir, '/usr/lib/dracut/modules.d'] 705 ) 706 if not os.path.isdir(dracut_module_dir): 707 # no dracut module dir present 708 return 709 710 incompatible_modules = {} 711 for module in os.listdir(dracut_module_dir): 712 module_meta = kiwi_dracut_modules.get(module) 713 if module_meta: 714 module_version = self._get_dracut_module_version_from_pdb( 715 self.xml_state.get_package_manager(), 716 module_meta.package, root_dir 717 ) 718 if module_version: 719 module_version_nr = tuple( 720 int(it) for it in module_version.split('.') 721 ) 722 module_min_version_nr = tuple( 723 int(it) for it in module_meta.min_version.split('.') 724 ) 725 if module_version_nr < module_min_version_nr: 726 incompatible_modules[ 727 module_meta.package 728 ] = 'got:{0}, need:>={1}'.format( 729 module_version, module_meta.min_version 730 ) 731 if incompatible_modules: 732 raise KiwiRuntimeError( 733 message.format(__version__, incompatible_modules) 734 ) 735 736 def check_dracut_module_for_oem_install_in_package_list(self) -> None: 737 """ 738 OEM images if configured to use dracut as initrd system 739 and configured with one of the installiso, installstick 740 or installpxe attributes requires the KIWI provided 741 dracut-kiwi-oem-dump module to be installed at the time 742 dracut is called. Thus this runtime check examines if the 743 required package is part of the package list in the 744 image description. 745 """ 746 message = dedent('''\n 747 Required dracut module package missing in package list 748 749 One of the packages '{0}' is required 750 to build an installation image for the selected oem image type. 751 Depending on your distribution, add the following in the 752 <packages type="image"> section: 753 754 <package name="ONE_FROM_ABOVE"/> 755 ''') 756 meta = Defaults.get_runtime_checker_metadata() 757 required_dracut_packages = meta['package_names']['dracut_oem_dump'] 758 initrd_system = self.xml_state.get_initrd_system() 759 build_type = self.xml_state.get_build_type_name() 760 if build_type == 'oem' and initrd_system == 'dracut': 761 install_iso = self.xml_state.build_type.get_installiso() 762 install_stick = self.xml_state.build_type.get_installstick() 763 install_pxe = self.xml_state.build_type.get_installpxe() 764 if install_iso or install_stick or install_pxe: 765 package_names = \ 766 self.xml_state.get_bootstrap_packages() + \ 767 self.xml_state.get_system_packages() 768 if not RuntimeChecker._package_in_list( 769 package_names, required_dracut_packages 770 ): 771 raise KiwiRuntimeError( 772 message.format(required_dracut_packages) 773 ) 774 775 def check_dracut_module_for_disk_oem_in_package_list(self) -> None: 776 """ 777 OEM images if configured to use dracut as initrd system 778 requires the KIWI provided dracut-kiwi-oem-repart module 779 to be installed at the time dracut is called. Thus this 780 runtime check examines if the required package is part of 781 the package list in the image description. 782 """ 783 message = dedent('''\n 784 Required dracut module package missing in package list 785 786 One of the packages '{0}' is required 787 for the selected oem image type. Depending on your distribution, 788 add the following in the <packages type="image"> section: 789 790 <package name="ONE_FROM_ABOVE"/> 791 ''') 792 meta = Defaults.get_runtime_checker_metadata() 793 required_dracut_packages = meta['package_names']['dracut_oem_repart'] 794 initrd_system = self.xml_state.get_initrd_system() 795 disk_resize_requested = self.xml_state.get_oemconfig_oem_resize() 796 build_type = self.xml_state.get_build_type_name() 797 if build_type == 'oem' and initrd_system == 'dracut' and \ 798 disk_resize_requested: 799 package_names = \ 800 self.xml_state.get_bootstrap_packages() + \ 801 self.xml_state.get_system_packages() 802 if not RuntimeChecker._package_in_list( 803 package_names, required_dracut_packages 804 ): 805 raise KiwiRuntimeError( 806 message.format(required_dracut_packages) 807 ) 808 809 def check_dracut_module_for_live_iso_in_package_list(self) -> None: 810 """ 811 Live ISO images uses a dracut initrd to boot and requires 812 the KIWI provided kiwi-live dracut module to be installed 813 at the time dracut is called. Thus this runtime check 814 examines if the required package is part of the package 815 list in the image description. 816 """ 817 message = dedent('''\n 818 Required dracut module package missing in package list 819 820 One of the packages '{0}' is required 821 for the selected live iso image type. Depending on your distribution, 822 add the following in your <packages type="image"> section: 823 824 <package name="ONE_FROM_ABOVE"/> 825 ''') 826 meta = Defaults.get_runtime_checker_metadata() 827 required_dracut_packages = meta['package_names']['dracut_live'] 828 type_name = self.xml_state.get_build_type_name() 829 type_flag = self.xml_state.build_type.get_flags() 830 if type_name == 'iso' and type_flag != 'dmsquash': 831 package_names = \ 832 self.xml_state.get_bootstrap_packages() + \ 833 self.xml_state.get_system_packages() 834 if not RuntimeChecker._package_in_list( 835 package_names, required_dracut_packages 836 ): 837 raise KiwiRuntimeError( 838 message.format(required_dracut_packages) 839 ) 840 841 def check_dracut_module_for_disk_overlay_in_package_list(self) -> None: 842 """ 843 Disk images configured to use a root filesystem overlay 844 requires the KIWI provided kiwi-overlay dracut module to 845 be installed at the time dracut is called. Thus this 846 runtime check examines if the required package is part of 847 the package list in the image description. 848 """ 849 message = dedent('''\n 850 Required dracut module package missing in package list 851 852 The package '{0}' is required 853 for the selected overlayroot activated image type. 854 Depending on your distribution, add the following in your 855 <packages type="image"> section: 856 857 <package name="ONE_FROM_ABOVE"/> 858 ''') 859 initrd_system = self.xml_state.get_initrd_system() 860 meta = Defaults.get_runtime_checker_metadata() 861 required_dracut_packages = meta['package_names']['dracut_overlay'] 862 if initrd_system == 'dracut' and \ 863 self.xml_state.build_type.get_overlayroot(): 864 package_names = \ 865 self.xml_state.get_bootstrap_packages() + \ 866 self.xml_state.get_system_packages() 867 if not RuntimeChecker._package_in_list( 868 package_names, required_dracut_packages 869 ): 870 raise KiwiRuntimeError( 871 message.format(required_dracut_packages) 872 ) 873 874 def check_xen_uniquely_setup_as_server_or_guest(self) -> None: 875 """ 876 If the image is classified to be used as Xen image, it can 877 be either a Xen Server(dom0) or a Xen guest. The image 878 configuration is checked if the information uniquely identifies 879 the image as such 880 """ 881 xen_message = dedent('''\n 882 Inconsistent Xen setup found: 883 884 The use of the 'xen_server' or 'xen_loader' attributes indicates 885 the target system for this image is Xen. However the image 886 specifies both attributes at the same time which classifies 887 the image to be both, a Xen Server(dom0) and a Xen guest at 888 the same time, which is not supported. 889 890 Please cleanup your image description. Setup only one 891 of 'xen_server' or 'xen_loader'. 892 ''') 893 ec2_message = dedent('''\n 894 Inconsistent Amazon EC2 setup found: 895 896 The firmware setup indicates the target system for this image 897 is Amazon EC2, which uses a Xen based virtualisation technology. 898 Therefore the image must be classified as a Xen guest and can 899 not be a Xen server as indicated by the 'xen_server' attribute 900 901 Please cleanup your image description. Delete the 'xen_server' 902 attribute for images used with Amazon EC2. 903 ''') 904 if self.xml_state.is_xen_server() and self.xml_state.is_xen_guest(): 905 firmware = self.xml_state.build_type.get_firmware() 906 ec2_firmware_names = Defaults.get_ec2_capable_firmware_names() 907 if firmware and firmware in ec2_firmware_names: 908 raise KiwiRuntimeError(ec2_message) 909 else: 910 raise KiwiRuntimeError(xen_message) 911 912 def check_mediacheck_installed(self) -> None: 913 """ 914 If the image description enables the mediacheck attribute 915 the required tools to run this check must be installed 916 on the image build host 917 """ 918 message_tool_not_found = dedent('''\n 919 Required tool {name} not found in caller environment 920 921 The attribute 'mediacheck' is set to 'true' which requires 922 the above tool to be installed on the build system 923 ''') 924 if self.xml_state.build_type.get_mediacheck() is True: 925 tool = 'tagmedia' 926 media_tagger = RuntimeConfig().get_iso_media_tag_tool() 927 if media_tagger == 'checkmedia': 928 tool = 'tagmedia' 929 elif media_tagger == 'isomd5sum': 930 tool = 'implantisomd5' 931 if not Path.which(filename=tool, access_mode=os.X_OK): 932 raise KiwiRuntimeError( 933 message_tool_not_found.format(name=tool) 934 ) 935 936 def check_image_version_provided(self) -> None: 937 """ 938 Kiwi requires a <version> element to be specified as part 939 of at least one <preferences> section. 940 """ 941 message_missing_version = dedent('''\n 942 No version is defined in any of the <preferences> 943 sections. Please add 944 945 <version>image_version<version/> 946 947 inside the <preferences> section. 948 ''') 949 950 if not self.xml_state.get_image_version(): 951 raise KiwiRuntimeError(message_missing_version) 952 953 def check_image_type_unique(self) -> None: 954 """ 955 Verify that the selected image type is unique within 956 the range of the configured types and profiles. 957 """ 958 message = dedent('''\n 959 Conflicting image type setup detected 960 961 The selected image type '{0}' in the {1} profile 962 selection is not unique. There are the following type 963 settings which overrides each other: 964 {2} 965 To solve this conflict please move the image type 966 setup into its own profile and select them using 967 the --profile option at call time. 968 ''') 969 image_type_sections = [] 970 type_dict: Dict[str, List[Any]] = {} 971 for preferences in self.xml_state.get_preferences_sections(): 972 image_type_sections += preferences.get_type() 973 974 for image_type in image_type_sections: 975 type_name = image_type.get_image() 976 if type_dict.get(type_name): 977 type_dict[type_name].append(image_type) 978 else: 979 type_dict[type_name] = [image_type] 980 981 for type_name, type_list in list(type_dict.items()): 982 if len(type_list) > 1: 983 type_export = StringIO() 984 for image_type in type_list: 985 type_export.write(os.linesep) 986 image_type.export(type_export, 0) 987 raise KiwiRuntimeError( 988 message.format( 989 type_name, self.xml_state.profiles or ['Default'], 990 type_export.getvalue() 991 ) 992 ) 993 994 def check_efi_fat_image_has_correct_size(self) -> None: 995 """ 996 Verify that the efifatimagesize does not exceed the max 997 El Torito load size of 65535 * 512 bytes 998 """ 999 message = dedent('''\n 1000 El Torito max load size exceeded 1001 1002 The configured efifatimagesize of '{0}MB' exceeds 1003 the El Torito max load size of 65535 * 512 bytes (~31MB). 1004 ''') 1005 fat_image_mbsize = int( 1006 self.xml_state.build_type 1007 .get_efifatimagesize() or defaults.EFI_FAT_IMAGE_SIZE 1008 ) 1009 if fat_image_mbsize > 31: 1010 raise KiwiRuntimeError( 1011 message.format(fat_image_mbsize) 1012 ) 1013 1014 def check_bootloader_env_compatible_with_loader(self) -> None: 1015 """ 1016 If there is an environment section as part of the bootloader 1017 section, check if the selected loader supports custom 1018 environment blobs 1019 """ 1020 message = dedent('''\n 1021 Selected loader does not support custom environments 1022 1023 The selected bootloader {} does not support custom 1024 environment settings. Please drop the <environment> 1025 setting from <bootloadersettings> for this loader 1026 ''') 1027 env_supported_loaders = [ 1028 'grub2', 1029 'grub2_s390x_emu', 1030 'custom' 1031 ] 1032 bootloader = self.xml_state.get_build_type_bootloader_name() 1033 variable_list = self.xml_state.\ 1034 get_build_type_bootloader_environment_variables() 1035 if variable_list and bootloader not in env_supported_loaders: 1036 raise KiwiRuntimeError( 1037 message.format(bootloader) 1038 ) 1039 1040 @staticmethod 1041 def _package_in_list( 1042 package_list: List[str], search_list: List[str] 1043 ) -> str: 1044 result = '' 1045 for search in search_list: 1046 if search in package_list: 1047 result = search 1048 break 1049 return result 1050 1051 @staticmethod 1052 def _get_dracut_module_version_from_pdb( 1053 package_manager: str, package_name: str, root_dir: str 1054 ) -> str: 1055 tool = Defaults.get_default_packager_tool(package_manager) 1056 package_query = None 1057 package_manager_query = None 1058 package_version = '' 1059 if tool == 'rpm': 1060 package_manager_query = [ 1061 'chroot', root_dir, tool, '-q', '--qf', 1062 '%{VERSION}', package_name 1063 ] 1064 elif tool == 'dpkg': 1065 package_manager_query = [ 1066 'chroot', root_dir, 'dpkg-query', '-W', '-f', 1067 '${Version}', package_name 1068 ] 1069 if package_manager_query: 1070 try: 1071 package_query = Command.run(package_manager_query) 1072 if package_query: 1073 package_version = package_query.output.split('-', 1)[0] 1074 except Exception as issue: 1075 log.debug(f'Package manager query failed with: {issue}') 1076 return package_version
Implements build consistency checks at runtime
60 def __init__(self, xml_state: XMLState) -> None: 61 """ 62 The schema of an image description covers structure and syntax of 63 the provided data. The RuntimeChecker provides methods to perform 64 further semantic checks which allows to recognize potential build 65 or boot problems early. 66 67 :param object xml_state: Instance of XMLState 68 """ 69 self.xml_state = xml_state
The schema of an image description covers structure and syntax of the provided data. The RuntimeChecker provides methods to perform further semantic checks which allows to recognize potential build or boot problems early.
Parameters
- object xml_state: Instance of XMLState
71 def check_repositories_configured(self) -> None: 72 """ 73 Verify that there are repositories configured 74 """ 75 if not self.xml_state.get_repository_sections(): 76 raise KiwiRuntimeError( 77 'No repositories configured' 78 )
Verify that there are repositories configured
80 @staticmethod 81 def check_target_dir_on_unsupported_filesystem(target_dir: str) -> None: 82 """ 83 Raise if the given target dir does not reside on a 84 filesystem that supports all important features like 85 extended permissions(fscaps), ACLs or xattrs. 86 """ 87 message = dedent('''\n 88 Target root/image directory is lacking filesystem features 89 90 The filesystem {0} in the target path {1} 91 does not support important features like extended permissions, 92 ACLs or xattrs. The image build may fail or the resulting 93 image misbehave. 94 ''') 95 target_dir = Path.first_exists(target_dir) 96 stat = Command.run(['stat', '-f', '-c', '%T', target_dir]) 97 if stat: 98 target_fs = stat.output.strip() 99 supported_target_filesystem = ( 100 'btrfs', 101 'ext2', 102 'ext2/ext3', 103 'ext3', 104 'ext4', 105 'overlayfs', 106 'tmpfs', 107 'xfs', 108 ) 109 if target_fs not in supported_target_filesystem: 110 raise KiwiRuntimeError(message.format(target_fs, target_dir))
Raise if the given target dir does not reside on a filesystem that supports all important features like extended permissions(fscaps), ACLs or xattrs.
112 def check_include_references_unresolvable(self) -> None: 113 """ 114 Raise for still included <include> statements as not resolvable. 115 The KIWI XSLT processing replaces the specified include 116 directive(s) with the given file reference(s). If this action 117 did not happen for example on nested includes, it can happen 118 that they stay in the document as sort of waste. 119 """ 120 message = dedent('''\n 121 One ore more <include> statements are unresolvable 122 123 The following include references could not be resolved. 124 Please verify the specified location(s) and/or delete 125 the broken include directive(s) from the description. 126 Please also note, nested includes from other include 127 files are not supported: 128 129 {0} 130 ''') 131 include_files = \ 132 self.xml_state.get_include_section_reference_file_names() 133 if include_files: 134 raise KiwiRuntimeError( 135 message.format(json.dumps(include_files, indent=4)) 136 )
Raise for still included
138 def check_image_include_repos_publicly_resolvable(self) -> None: 139 """ 140 Verify that all repos marked with the imageinclude attribute 141 can be resolved into a http based web URL 142 """ 143 message = dedent('''\n 144 The use of imageinclude="true" in the repository definition 145 for the Repository: {0} 146 requires the repository to by publicly available. The source 147 locator of the repository however indicates it is private to 148 your local system. Therefore it can't be included into the 149 system image repository configuration. Please define a publicly 150 available repository in your image XML description. 151 ''') 152 153 repository_sections = self.xml_state.get_repository_sections() 154 for xml_repo in repository_sections: 155 repo_marked_for_image_include = xml_repo.get_imageinclude() 156 157 if repo_marked_for_image_include: 158 repo_source = xml_repo.get_source().get_path() 159 repo_type = xml_repo.get_type() 160 uri = Uri(repo_source, repo_type) 161 if not uri.is_public(): 162 raise KiwiRuntimeError( 163 message.format(repo_source) 164 )
Verify that all repos marked with the imageinclude attribute can be resolved into a http based web URL
192 def check_volume_label_used_with_lvm(self) -> None: 193 """ 194 The optional volume label in a systemdisk setup is only 195 effective if the LVM, logical volume manager system is 196 used. In any other case where the filesystem itself offers 197 volume management capabilities there are no extra filesystem 198 labels which can be applied per volume 199 """ 200 message = dedent('''\n 201 Custom volume label setup used without LVM 202 203 The optional volume label in a systemdisk setup is only 204 effective if the LVM, logical volume manager system is 205 used. Your setup uses the {0} filesystem which itself 206 offers volume management capabilities. Extra filesystem 207 labels cannot be applied in this case. 208 209 If you want to force LVM over the {0} volume management 210 system you can do so by specifying the following in 211 your KIWI XML description: 212 213 <systemdisk ... preferlvm="true"> 214 <volume .../> 215 </systemdisk> 216 ''') 217 volume_management = self.xml_state.get_volume_management() 218 if volume_management != 'lvm': 219 for volume in self.xml_state.get_volumes(): 220 if volume.label and volume.label != 'SWAP': 221 raise KiwiRuntimeError( 222 message.format(volume_management) 223 )
The optional volume label in a systemdisk setup is only effective if the LVM, logical volume manager system is used. In any other case where the filesystem itself offers volume management capabilities there are no extra filesystem labels which can be applied per volume
225 def check_partuuid_persistency_type_used_with_mbr(self) -> None: 226 """ 227 The devicepersistency setting by-partuuid can only be 228 used in combination with a partition table type that 229 supports UUIDs. In any other case Linux creates artificial 230 values for PTUUID and PARTUUID from the disk signature 231 which can change without touching the actual partition 232 table. We consider this unsafe and only allow the use 233 of by-partuuid in combination with partition tables that 234 actually supports it properly. 235 """ 236 message = dedent('''\n 237 devicepersistency={0!r} used with non UUID capable partition table 238 239 PTUUID and PARTUUID exists in the GUID (GPT) partition table. 240 According to the firmware setting: {1!r}, the selected partition 241 table type is: {2!r}. This table type does not natively support 242 UUIDs. In such a case Linux creates artificial values for PTUUID 243 and PARTUUID from the disk signature which can change without 244 touching the actual partition table. This is considered unsafe 245 and KIWI only allows the use of by-partuuid in combination with 246 partition tables that actually supports UUIDs properly. 247 248 Please make sure to use one of the following firmware settings 249 which leads to an image using an UUID capable partition table 250 and therefore supporting consistent by-partuuid device names: 251 252 <type ... firmware="efi|uefi"> 253 ''') 254 persistency_type = self.xml_state.build_type.get_devicepersistency() 255 if persistency_type and persistency_type == 'by-partuuid': 256 supported_table_types = ['gpt'] 257 firmware = FirmWare(self.xml_state) 258 table_type = firmware.get_partition_table_type() 259 if table_type not in supported_table_types: 260 raise KiwiRuntimeError( 261 message.format( 262 persistency_type, firmware.firmware, table_type 263 ) 264 )
The devicepersistency setting by-partuuid can only be used in combination with a partition table type that supports UUIDs. In any other case Linux creates artificial values for PTUUID and PARTUUID from the disk signature which can change without touching the actual partition table. We consider this unsafe and only allow the use of by-partuuid in combination with partition tables that actually supports it properly.
266 def check_swap_name_used_with_lvm(self) -> None: 267 """ 268 The optional oem-swapname is only effective if used together 269 with the LVM volume manager. A name for the swap space can 270 only be set if it is created as a LVM volume. In any other 271 case the name does not apply to the system 272 """ 273 message = dedent('''\n 274 Specified swap space name: {0} will not be used 275 276 The specified oem-swapname is used without the LVM volume 277 manager. This means the swap space will be created as simple 278 partition for which no name assignment can take place. 279 The name specified in oem-swapname is used to give the 280 LVM swap volume a name. Outside of LVM the setting is 281 meaningless and should be removed. 282 283 Please delete the following setting from your image 284 description: 285 286 <oem-swapname>{0}</oem-swapname> 287 ''') 288 volume_management = self.xml_state.get_volume_management() 289 if volume_management != 'lvm': 290 oemconfig = self.xml_state.get_build_type_oemconfig_section() 291 if oemconfig and oemconfig.get_oem_swapname(): 292 raise KiwiRuntimeError( 293 message.format(oemconfig.get_oem_swapname()[0]) 294 )
The optional oem-swapname is only effective if used together with the LVM volume manager. A name for the swap space can only be set if it is created as a LVM volume. In any other case the name does not apply to the system
296 def check_volume_setup_defines_reserved_labels(self) -> None: 297 message = dedent('''\n 298 Reserved label name used in LVM volume setup 299 300 The label setup for volume {0} uses the reserved label {1}. 301 Reserved labels used by KIWI internally are {2}. Please 302 choose another label name for this volume. 303 ''') 304 reserved_labels = [ 305 self.xml_state.build_type.get_rootfs_label() or 'ROOT', 306 'SWAP', 'SPARE' 307 ] 308 volume_management = self.xml_state.get_volume_management() 309 if volume_management == 'lvm': 310 for volume in self.xml_state.get_volumes(): 311 # A swap volume is created implicitly if oem-swap is 312 # requested. This volume detected via realpath set to 313 # swap is skipped from the reserved label check as it 314 # intentionally uses the reserved label named SWAP 315 if volume.realpath != 'swap': 316 if volume.label and volume.label in reserved_labels: 317 raise KiwiRuntimeError( 318 message.format( 319 volume.name, volume.label, reserved_labels 320 ) 321 )
323 def check_volume_setup_defines_multiple_fullsize_volumes(self) -> None: 324 """ 325 The volume size specification 'all' makes this volume to 326 take the rest space available on the system. It's only 327 allowed to specify one all size volume 328 """ 329 message = dedent('''\n 330 Multiple all size volumes found but only one is allowed 331 332 The volume size specification 'all' makes this volume to 333 take the rest space available on the system. It's only 334 allowed to specify one all size volume 335 ''') 336 systemdisk_section = self.xml_state.get_build_type_system_disk_section() 337 if systemdisk_section: 338 all_size_volume_count = 0 339 volumes = systemdisk_section.get_volume() or [] 340 for volume in volumes: 341 size = volume.get_size() or volume.get_freespace() 342 if size and 'all' in size: 343 all_size_volume_count += 1 344 if all_size_volume_count > 1: 345 raise KiwiRuntimeError(message)
The volume size specification 'all' makes this volume to take the rest space available on the system. It's only allowed to specify one all size volume
347 def check_volume_setup_has_no_root_definition(self) -> None: 348 """ 349 The root volume in a systemdisk setup is handled in a special 350 way. It is not allowed to setup a custom name or mountpoint for 351 the root volume. Therefore the size of the root volume can be 352 setup via the @root volume name. This check looks up the volume 353 setup and searches if there is a configuration for the '/' 354 mountpoint which would cause the image build to fail 355 """ 356 message = dedent('''\n 357 Volume setup for "/" found. The size of the root volume 358 must be specified via the @root volume name like the 359 following example shows: 360 361 <volume name="@root" size="42G"/> 362 363 A custom name or mountpoint for the root volume is not 364 allowed. 365 ''') 366 for volume in self.xml_state.get_volumes(): 367 if volume.mountpoint == '/': 368 raise KiwiRuntimeError(message)
The root volume in a systemdisk setup is handled in a special way. It is not allowed to setup a custom name or mountpoint for the root volume. Therefore the size of the root volume can be setup via the @root volume name. This check looks up the volume setup and searches if there is a configuration for the '/' mountpoint which would cause the image build to fail
370 def check_container_tool_chain_installed(self) -> None: 371 """ 372 When creating container images the specific tools are used in order 373 to import and export OCI or Docker compatible images. This check 374 searches for those tools to be installed in the build system and 375 fails if it can't find them 376 """ 377 message_tool_not_found = dedent('''\n 378 Required tool {name} not found in caller environment 379 380 Creation of OCI or Docker images requires the tools {name} and 381 skopeo to be installed on the build system. For SUSE based systems 382 you can find the tools at: 383 384 http://download.opensuse.org/repositories/Virtualization:/containers 385 ''') 386 message_version_unsupported = dedent('''\n 387 {name} tool found with unknown version 388 ''') 389 message_unknown_tool = dedent('''\n 390 Unknown tool: {0}. 391 392 Please configure KIWI with an appropriate value (umoci or buildah). 393 Consider this runtime configuration file syntax (/etc/kiwi.yml): 394 395 oci: 396 - archive_tool: umoci | buildah 397 ''') 398 399 expected_version = (0, 1, 0) 400 401 if self.xml_state.get_build_type_name() in ['docker', 'oci']: 402 runtime_config = RuntimeConfig() 403 tool_name = runtime_config.get_oci_archive_tool() 404 if tool_name == 'buildah': 405 oci_tools = ['buildah', 'skopeo'] 406 elif tool_name == 'umoci': 407 oci_tools = ['umoci', 'skopeo'] 408 else: 409 raise KiwiRuntimeError(message_unknown_tool.format(tool_name)) 410 for tool in oci_tools: 411 if not Path.which(filename=tool, access_mode=os.X_OK): 412 raise KiwiRuntimeError( 413 message_tool_not_found.format(name=tool) 414 ) 415 elif not CommandCapabilities.check_version( 416 tool, expected_version, raise_on_error=False 417 ): 418 raise KiwiRuntimeError( 419 message_version_unsupported.format(name=tool) 420 ) 421 self._check_multitag_support()
When creating container images the specific tools are used in order to import and export OCI or Docker compatible images. This check searches for those tools to be installed in the build system and fails if it can't find them
438 def check_luksformat_options_valid(self) -> None: 439 """ 440 Options set via the luksformat element are passed along 441 to the cryptsetup tool. Only options that are known to 442 the tool should be allowed. Thus this runtime check looks 443 up the provided option names if they exist in the cryptsetup 444 version used on the build host 445 """ 446 message = dedent('''\n 447 Option {0!r} not found in cryptsetup 448 449 The Option {0!r} could not be found in the help output 450 of the cryptsetup tool. 451 ''') 452 luksformat = self.xml_state.build_type.get_luksformat() 453 if luksformat: 454 for option in luksformat[0].get_option(): 455 argument = option.get_name() 456 if not CommandCapabilities.has_option_in_help( 457 'cryptsetup', argument, ['--help'], 458 raise_on_error=False 459 ): 460 raise KiwiRuntimeError(message.format(argument))
Options set via the luksformat element are passed along to the cryptsetup tool. Only options that are known to the tool should be allowed. Thus this runtime check looks up the provided option names if they exist in the cryptsetup version used on the build host
462 def check_appx_naming_conventions_valid(self) -> None: 463 """ 464 When building wsl images there are some naming conventions that 465 must be fulfilled to run the container on Microsoft Windows 466 """ 467 launcher_pattern = r'[^\\]+(\.[Ee][Xx][Ee])$' 468 message_container_launcher_invalid = dedent('''\n 469 Invalid WSL launcher name: {0} 470 471 WSL launcher name must match the pattern: {1} 472 ''') 473 id_pattern = r'^[a-zA-Z0-9]+$' 474 message_container_id_invalid = dedent('''\n 475 Invalid WSL container application id: {0} 476 477 WSL container id must match the pattern: {1} 478 ''') 479 build_type = self.xml_state.get_build_type_name() 480 container_config = self.xml_state.get_container_config() 481 container_history = container_config.get('history') or {} 482 if build_type == 'appx' and container_config: 483 launcher = container_history.get('launcher') 484 if launcher and not re.match(launcher_pattern, launcher): 485 raise KiwiRuntimeError( 486 message_container_launcher_invalid.format( 487 launcher, launcher_pattern 488 ) 489 ) 490 application_id = container_history.get('application_id') 491 if application_id and not re.match(id_pattern, application_id): 492 raise KiwiRuntimeError( 493 message_container_id_invalid.format( 494 application_id, id_pattern 495 ) 496 )
When building wsl images there are some naming conventions that must be fulfilled to run the container on Microsoft Windows
498 def check_initrd_selection_required(self) -> None: 499 """ 500 If the boot attribute is used without selecting kiwi 501 as the initrd_system, the setting of the boot attribute 502 will not have any effect. We assume that configurations 503 which explicitly specify the boot attribute wants to use 504 the custom kiwi initrd system and not dracut. 505 """ 506 message_kiwi_initrd_system_not_selected = dedent('''\n 507 Missing initrd_system selection for boot attribute 508 509 The selected boot="'{0}'" boot description indicates 510 the custom kiwi initrd system should be used instead 511 of dracut. If this is correct please explicitly request 512 the kiwi initrd system as follows: 513 514 <type initrd_system="kiwi"/> 515 516 If this is not want you want and dracut should be used 517 as initrd system, please delete the boot attribute 518 as it is obsolete in this case. 519 ''') 520 initrd_system = self.xml_state.get_initrd_system() 521 boot_image_reference = self.xml_state.build_type.get_boot() 522 if initrd_system != 'kiwi' and boot_image_reference: 523 raise KiwiRuntimeError( 524 message_kiwi_initrd_system_not_selected.format( 525 boot_image_reference 526 ) 527 )
If the boot attribute is used without selecting kiwi as the initrd_system, the setting of the boot attribute will not have any effect. We assume that configurations which explicitly specify the boot attribute wants to use the custom kiwi initrd system and not dracut.
529 def check_boot_description_exists(self) -> None: 530 """ 531 If a kiwi initrd is used, a lookup to the specified boot 532 description is done and fails early if it does not exist 533 """ 534 message_no_boot_reference = dedent('''\n 535 Boot description missing for '{0}' type 536 537 The selected '{1}' initrd_system requires a boot description 538 reference. Please update your type setup as follows 539 540 <type image="{0}" boot="{0}boot/..."/> 541 542 A collection of custom boot descriptions can be found 543 in the kiwi-boot-descriptions package 544 ''') 545 message_boot_description_not_found = dedent('''\n 546 Boot description '{0}' not found 547 548 The selected boot description could not be found on 549 the build host. A collection of custom boot descriptions 550 can be found in the kiwi-boot-descriptions package 551 ''') 552 image_types_supporting_custom_boot_description = ['oem', 'pxe'] 553 build_type = self.xml_state.get_build_type_name() 554 initrd_system = self.xml_state.get_initrd_system() 555 if initrd_system == 'kiwi' and \ 556 build_type in image_types_supporting_custom_boot_description: 557 558 boot_image_reference = self.xml_state.build_type.get_boot() 559 if not boot_image_reference: 560 raise KiwiRuntimeError( 561 message_no_boot_reference.format(build_type, initrd_system) 562 ) 563 564 if not boot_image_reference[0] == os.sep: 565 boot_image_reference = os.sep.join( 566 [ 567 Defaults.get_boot_image_description_path(), 568 boot_image_reference 569 ] 570 ) 571 if not os.path.exists(boot_image_reference): 572 raise KiwiRuntimeError( 573 message_boot_description_not_found.format( 574 boot_image_reference 575 ) 576 )
If a kiwi initrd is used, a lookup to the specified boot description is done and fails early if it does not exist
578 def check_consistent_kernel_in_boot_and_system_image(self) -> None: 579 """ 580 If a kiwi initrd is used, the kernel used to build the kiwi 581 initrd and the kernel used in the system image must be the 582 same in order to avoid an inconsistent boot setup 583 """ 584 message = dedent('''\n 585 Possible kernel mismatch between kiwi initrd and system image 586 587 The selected '{0}' boot image kernel is '{1}'. However this 588 kernel package was not explicitly listed in the package list 589 of the system image. Please fixup your system image 590 description: 591 592 1) Add <package name="{1}"/> to your system XML description 593 594 2) Inherit kernel from system description to initrd via 595 the custom kernel profile: 596 597 <type ... bootkernel="custom" .../> 598 599 <packages type="image"/> 600 <package name="desired-kernel" bootinclude="true"/> 601 </packages> 602 ''') 603 boot_image_reference = self.xml_state.build_type.get_boot() 604 boot_kernel_package_name = None 605 if boot_image_reference: 606 if not boot_image_reference[0] == '/': 607 boot_image_reference = os.sep.join( 608 [ 609 Defaults.get_boot_image_description_path(), 610 boot_image_reference 611 ] 612 ) 613 boot_config_file = os.sep.join( 614 [boot_image_reference, 'config.xml'] 615 ) 616 if os.path.exists(boot_config_file): 617 boot_description = XMLDescription( 618 description=boot_config_file, 619 derived_from=self.xml_state.xml_data.description_dir 620 ) 621 boot_kernel_profile = \ 622 self.xml_state.build_type.get_bootkernel() 623 if not boot_kernel_profile: 624 boot_kernel_profile = 'std' 625 boot_xml_state = XMLState( 626 boot_description.load(), [boot_kernel_profile] 627 ) 628 kernel_package_sections = [] 629 for packages_section in boot_xml_state.xml_data.get_packages(): 630 # lookup package sections matching kernel profile in kiwi 631 # boot description. By definition this must be a packages 632 # section with a single profile name whereas the default 633 # profile name is 'std'. The section itself must contain 634 # one matching kernel package name for the desired 635 # architecture 636 if packages_section.get_profiles() == boot_kernel_profile: 637 for package in packages_section.get_package(): 638 kernel_package_sections.append(package) 639 640 for package in kernel_package_sections: 641 if boot_xml_state.package_matches_host_architecture( 642 package 643 ): 644 boot_kernel_package_name = package.get_name() 645 646 if boot_kernel_package_name: 647 # A kernel package name was found in the kiwi boot image 648 # description. Let's check if this kernel is also used 649 # in the system image 650 image_package_names = self.xml_state.get_system_packages() 651 if boot_kernel_package_name not in image_package_names: 652 raise KiwiRuntimeError( 653 message.format( 654 self.xml_state.build_type.get_boot(), 655 boot_kernel_package_name 656 ) 657 )
If a kiwi initrd is used, the kernel used to build the kiwi initrd and the kernel used in the system image must be the same in order to avoid an inconsistent boot setup
659 def check_dracut_module_versions_compatible_to_kiwi( 660 self, root_dir: str 661 ) -> None: 662 """ 663 KIWI images which makes use of kiwi dracut modules 664 has to use module versions compatible with the version 665 of this KIWI builder code base. This is important to avoid 666 inconsistencies between the way how kiwi includes its own 667 dracut modules and former version of those dracut modules 668 which could be no longer compatible with the builder. 669 Therefore this runtime check maintains a min_version constraint 670 for which we know this KIWI builder to be compatible with. 671 """ 672 message = dedent('''\n 673 Incompatible dracut-kiwi module(s) found 674 675 The image was build with KIWI version={0}. The system 676 root tree has the following dracut-kiwi-* module packages 677 installed which are too old to work with this version of KIWI. 678 Please make sure to use dracut-kiwi-* module packages 679 which are >= than the versions listed below. 680 681 {1} 682 ''') 683 kiwi_dracut_modules = { 684 '55kiwi-dump': dracut_module_type( 685 'dracut-kiwi-oem-dump', '9.20.1' 686 ), 687 '55kiwi-live': dracut_module_type( 688 'dracut-kiwi-live', '9.20.1' 689 ), 690 '55kiwi-overlay': dracut_module_type( 691 'dracut-kiwi-overlay', '9.20.1' 692 ), 693 '55kiwi-repart': dracut_module_type( 694 'dracut-kiwi-oem-repart', '9.20.1' 695 ), 696 '59kiwi-dump-reboot': dracut_module_type( 697 'dracut-kiwi-oem-dump', '9.20.1' 698 ), 699 '59kiwi-lib': dracut_module_type( 700 'dracut-kiwi-lib', '9.20.1' 701 ) 702 } 703 dracut_module_dir = os.sep.join( 704 [root_dir, '/usr/lib/dracut/modules.d'] 705 ) 706 if not os.path.isdir(dracut_module_dir): 707 # no dracut module dir present 708 return 709 710 incompatible_modules = {} 711 for module in os.listdir(dracut_module_dir): 712 module_meta = kiwi_dracut_modules.get(module) 713 if module_meta: 714 module_version = self._get_dracut_module_version_from_pdb( 715 self.xml_state.get_package_manager(), 716 module_meta.package, root_dir 717 ) 718 if module_version: 719 module_version_nr = tuple( 720 int(it) for it in module_version.split('.') 721 ) 722 module_min_version_nr = tuple( 723 int(it) for it in module_meta.min_version.split('.') 724 ) 725 if module_version_nr < module_min_version_nr: 726 incompatible_modules[ 727 module_meta.package 728 ] = 'got:{0}, need:>={1}'.format( 729 module_version, module_meta.min_version 730 ) 731 if incompatible_modules: 732 raise KiwiRuntimeError( 733 message.format(__version__, incompatible_modules) 734 )
KIWI images which makes use of kiwi dracut modules has to use module versions compatible with the version of this KIWI builder code base. This is important to avoid inconsistencies between the way how kiwi includes its own dracut modules and former version of those dracut modules which could be no longer compatible with the builder. Therefore this runtime check maintains a min_version constraint for which we know this KIWI builder to be compatible with.
736 def check_dracut_module_for_oem_install_in_package_list(self) -> None: 737 """ 738 OEM images if configured to use dracut as initrd system 739 and configured with one of the installiso, installstick 740 or installpxe attributes requires the KIWI provided 741 dracut-kiwi-oem-dump module to be installed at the time 742 dracut is called. Thus this runtime check examines if the 743 required package is part of the package list in the 744 image description. 745 """ 746 message = dedent('''\n 747 Required dracut module package missing in package list 748 749 One of the packages '{0}' is required 750 to build an installation image for the selected oem image type. 751 Depending on your distribution, add the following in the 752 <packages type="image"> section: 753 754 <package name="ONE_FROM_ABOVE"/> 755 ''') 756 meta = Defaults.get_runtime_checker_metadata() 757 required_dracut_packages = meta['package_names']['dracut_oem_dump'] 758 initrd_system = self.xml_state.get_initrd_system() 759 build_type = self.xml_state.get_build_type_name() 760 if build_type == 'oem' and initrd_system == 'dracut': 761 install_iso = self.xml_state.build_type.get_installiso() 762 install_stick = self.xml_state.build_type.get_installstick() 763 install_pxe = self.xml_state.build_type.get_installpxe() 764 if install_iso or install_stick or install_pxe: 765 package_names = \ 766 self.xml_state.get_bootstrap_packages() + \ 767 self.xml_state.get_system_packages() 768 if not RuntimeChecker._package_in_list( 769 package_names, required_dracut_packages 770 ): 771 raise KiwiRuntimeError( 772 message.format(required_dracut_packages) 773 )
OEM images if configured to use dracut as initrd system and configured with one of the installiso, installstick or installpxe attributes requires the KIWI provided dracut-kiwi-oem-dump module to be installed at the time dracut is called. Thus this runtime check examines if the required package is part of the package list in the image description.
775 def check_dracut_module_for_disk_oem_in_package_list(self) -> None: 776 """ 777 OEM images if configured to use dracut as initrd system 778 requires the KIWI provided dracut-kiwi-oem-repart module 779 to be installed at the time dracut is called. Thus this 780 runtime check examines if the required package is part of 781 the package list in the image description. 782 """ 783 message = dedent('''\n 784 Required dracut module package missing in package list 785 786 One of the packages '{0}' is required 787 for the selected oem image type. Depending on your distribution, 788 add the following in the <packages type="image"> section: 789 790 <package name="ONE_FROM_ABOVE"/> 791 ''') 792 meta = Defaults.get_runtime_checker_metadata() 793 required_dracut_packages = meta['package_names']['dracut_oem_repart'] 794 initrd_system = self.xml_state.get_initrd_system() 795 disk_resize_requested = self.xml_state.get_oemconfig_oem_resize() 796 build_type = self.xml_state.get_build_type_name() 797 if build_type == 'oem' and initrd_system == 'dracut' and \ 798 disk_resize_requested: 799 package_names = \ 800 self.xml_state.get_bootstrap_packages() + \ 801 self.xml_state.get_system_packages() 802 if not RuntimeChecker._package_in_list( 803 package_names, required_dracut_packages 804 ): 805 raise KiwiRuntimeError( 806 message.format(required_dracut_packages) 807 )
OEM images if configured to use dracut as initrd system requires the KIWI provided dracut-kiwi-oem-repart module to be installed at the time dracut is called. Thus this runtime check examines if the required package is part of the package list in the image description.
809 def check_dracut_module_for_live_iso_in_package_list(self) -> None: 810 """ 811 Live ISO images uses a dracut initrd to boot and requires 812 the KIWI provided kiwi-live dracut module to be installed 813 at the time dracut is called. Thus this runtime check 814 examines if the required package is part of the package 815 list in the image description. 816 """ 817 message = dedent('''\n 818 Required dracut module package missing in package list 819 820 One of the packages '{0}' is required 821 for the selected live iso image type. Depending on your distribution, 822 add the following in your <packages type="image"> section: 823 824 <package name="ONE_FROM_ABOVE"/> 825 ''') 826 meta = Defaults.get_runtime_checker_metadata() 827 required_dracut_packages = meta['package_names']['dracut_live'] 828 type_name = self.xml_state.get_build_type_name() 829 type_flag = self.xml_state.build_type.get_flags() 830 if type_name == 'iso' and type_flag != 'dmsquash': 831 package_names = \ 832 self.xml_state.get_bootstrap_packages() + \ 833 self.xml_state.get_system_packages() 834 if not RuntimeChecker._package_in_list( 835 package_names, required_dracut_packages 836 ): 837 raise KiwiRuntimeError( 838 message.format(required_dracut_packages) 839 )
Live ISO images uses a dracut initrd to boot and requires the KIWI provided kiwi-live dracut module to be installed at the time dracut is called. Thus this runtime check examines if the required package is part of the package list in the image description.
841 def check_dracut_module_for_disk_overlay_in_package_list(self) -> None: 842 """ 843 Disk images configured to use a root filesystem overlay 844 requires the KIWI provided kiwi-overlay dracut module to 845 be installed at the time dracut is called. Thus this 846 runtime check examines if the required package is part of 847 the package list in the image description. 848 """ 849 message = dedent('''\n 850 Required dracut module package missing in package list 851 852 The package '{0}' is required 853 for the selected overlayroot activated image type. 854 Depending on your distribution, add the following in your 855 <packages type="image"> section: 856 857 <package name="ONE_FROM_ABOVE"/> 858 ''') 859 initrd_system = self.xml_state.get_initrd_system() 860 meta = Defaults.get_runtime_checker_metadata() 861 required_dracut_packages = meta['package_names']['dracut_overlay'] 862 if initrd_system == 'dracut' and \ 863 self.xml_state.build_type.get_overlayroot(): 864 package_names = \ 865 self.xml_state.get_bootstrap_packages() + \ 866 self.xml_state.get_system_packages() 867 if not RuntimeChecker._package_in_list( 868 package_names, required_dracut_packages 869 ): 870 raise KiwiRuntimeError( 871 message.format(required_dracut_packages) 872 )
Disk images configured to use a root filesystem overlay requires the KIWI provided kiwi-overlay dracut module to be installed at the time dracut is called. Thus this runtime check examines if the required package is part of the package list in the image description.
874 def check_xen_uniquely_setup_as_server_or_guest(self) -> None: 875 """ 876 If the image is classified to be used as Xen image, it can 877 be either a Xen Server(dom0) or a Xen guest. The image 878 configuration is checked if the information uniquely identifies 879 the image as such 880 """ 881 xen_message = dedent('''\n 882 Inconsistent Xen setup found: 883 884 The use of the 'xen_server' or 'xen_loader' attributes indicates 885 the target system for this image is Xen. However the image 886 specifies both attributes at the same time which classifies 887 the image to be both, a Xen Server(dom0) and a Xen guest at 888 the same time, which is not supported. 889 890 Please cleanup your image description. Setup only one 891 of 'xen_server' or 'xen_loader'. 892 ''') 893 ec2_message = dedent('''\n 894 Inconsistent Amazon EC2 setup found: 895 896 The firmware setup indicates the target system for this image 897 is Amazon EC2, which uses a Xen based virtualisation technology. 898 Therefore the image must be classified as a Xen guest and can 899 not be a Xen server as indicated by the 'xen_server' attribute 900 901 Please cleanup your image description. Delete the 'xen_server' 902 attribute for images used with Amazon EC2. 903 ''') 904 if self.xml_state.is_xen_server() and self.xml_state.is_xen_guest(): 905 firmware = self.xml_state.build_type.get_firmware() 906 ec2_firmware_names = Defaults.get_ec2_capable_firmware_names() 907 if firmware and firmware in ec2_firmware_names: 908 raise KiwiRuntimeError(ec2_message) 909 else: 910 raise KiwiRuntimeError(xen_message)
If the image is classified to be used as Xen image, it can be either a Xen Server(dom0) or a Xen guest. The image configuration is checked if the information uniquely identifies the image as such
912 def check_mediacheck_installed(self) -> None: 913 """ 914 If the image description enables the mediacheck attribute 915 the required tools to run this check must be installed 916 on the image build host 917 """ 918 message_tool_not_found = dedent('''\n 919 Required tool {name} not found in caller environment 920 921 The attribute 'mediacheck' is set to 'true' which requires 922 the above tool to be installed on the build system 923 ''') 924 if self.xml_state.build_type.get_mediacheck() is True: 925 tool = 'tagmedia' 926 media_tagger = RuntimeConfig().get_iso_media_tag_tool() 927 if media_tagger == 'checkmedia': 928 tool = 'tagmedia' 929 elif media_tagger == 'isomd5sum': 930 tool = 'implantisomd5' 931 if not Path.which(filename=tool, access_mode=os.X_OK): 932 raise KiwiRuntimeError( 933 message_tool_not_found.format(name=tool) 934 )
If the image description enables the mediacheck attribute the required tools to run this check must be installed on the image build host
936 def check_image_version_provided(self) -> None: 937 """ 938 Kiwi requires a <version> element to be specified as part 939 of at least one <preferences> section. 940 """ 941 message_missing_version = dedent('''\n 942 No version is defined in any of the <preferences> 943 sections. Please add 944 945 <version>image_version<version/> 946 947 inside the <preferences> section. 948 ''') 949 950 if not self.xml_state.get_image_version(): 951 raise KiwiRuntimeError(message_missing_version)
Kiwi requires a
953 def check_image_type_unique(self) -> None: 954 """ 955 Verify that the selected image type is unique within 956 the range of the configured types and profiles. 957 """ 958 message = dedent('''\n 959 Conflicting image type setup detected 960 961 The selected image type '{0}' in the {1} profile 962 selection is not unique. There are the following type 963 settings which overrides each other: 964 {2} 965 To solve this conflict please move the image type 966 setup into its own profile and select them using 967 the --profile option at call time. 968 ''') 969 image_type_sections = [] 970 type_dict: Dict[str, List[Any]] = {} 971 for preferences in self.xml_state.get_preferences_sections(): 972 image_type_sections += preferences.get_type() 973 974 for image_type in image_type_sections: 975 type_name = image_type.get_image() 976 if type_dict.get(type_name): 977 type_dict[type_name].append(image_type) 978 else: 979 type_dict[type_name] = [image_type] 980 981 for type_name, type_list in list(type_dict.items()): 982 if len(type_list) > 1: 983 type_export = StringIO() 984 for image_type in type_list: 985 type_export.write(os.linesep) 986 image_type.export(type_export, 0) 987 raise KiwiRuntimeError( 988 message.format( 989 type_name, self.xml_state.profiles or ['Default'], 990 type_export.getvalue() 991 ) 992 )
Verify that the selected image type is unique within the range of the configured types and profiles.
994 def check_efi_fat_image_has_correct_size(self) -> None: 995 """ 996 Verify that the efifatimagesize does not exceed the max 997 El Torito load size of 65535 * 512 bytes 998 """ 999 message = dedent('''\n 1000 El Torito max load size exceeded 1001 1002 The configured efifatimagesize of '{0}MB' exceeds 1003 the El Torito max load size of 65535 * 512 bytes (~31MB). 1004 ''') 1005 fat_image_mbsize = int( 1006 self.xml_state.build_type 1007 .get_efifatimagesize() or defaults.EFI_FAT_IMAGE_SIZE 1008 ) 1009 if fat_image_mbsize > 31: 1010 raise KiwiRuntimeError( 1011 message.format(fat_image_mbsize) 1012 )
Verify that the efifatimagesize does not exceed the max El Torito load size of 65535 * 512 bytes
1014 def check_bootloader_env_compatible_with_loader(self) -> None: 1015 """ 1016 If there is an environment section as part of the bootloader 1017 section, check if the selected loader supports custom 1018 environment blobs 1019 """ 1020 message = dedent('''\n 1021 Selected loader does not support custom environments 1022 1023 The selected bootloader {} does not support custom 1024 environment settings. Please drop the <environment> 1025 setting from <bootloadersettings> for this loader 1026 ''') 1027 env_supported_loaders = [ 1028 'grub2', 1029 'grub2_s390x_emu', 1030 'custom' 1031 ] 1032 bootloader = self.xml_state.get_build_type_bootloader_name() 1033 variable_list = self.xml_state.\ 1034 get_build_type_bootloader_environment_variables() 1035 if variable_list and bootloader not in env_supported_loaders: 1036 raise KiwiRuntimeError( 1037 message.format(bootloader) 1038 )
If there is an environment section as part of the bootloader section, check if the selected loader supports custom environment blobs