kiwi.xml_state
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 19from typing import ( 20 List, Optional, Any, Dict, NamedTuple, Callable, Union 21) 22import re 23import logging 24import copy 25from textwrap import dedent 26 27# project 28import kiwi.defaults as defaults 29 30from kiwi import xml_parse 31from kiwi.storage.disk import ptable_entry_type 32from kiwi.system.uri import Uri 33from kiwi.defaults import Defaults 34from kiwi.utils.size import StringToSize 35from kiwi.command import Command 36 37from kiwi.exceptions import ( 38 KiwiProfileNotFound, 39 KiwiTypeNotFound, 40 KiwiDistributionNameError, 41 KiwiFileAccessError 42) 43 44log = logging.getLogger('kiwi') 45 46description_type = NamedTuple( 47 'description_type', [ 48 ('author', str), 49 ('contact', str), 50 ('specification', str) 51 ] 52) 53 54package_type = NamedTuple( 55 'package_type', [ 56 ('packages_section', xml_parse.packages), 57 ('package_section', xml_parse.package) 58 ] 59) 60 61size_type = NamedTuple( 62 'size_type', [ 63 ('mbytes', int), 64 ('additive', str) 65 ] 66) 67 68volume_type = NamedTuple( 69 'volume_type', [ 70 ('name', str), 71 ('parent', str), 72 ('size', str), 73 ('realpath', str), 74 ('mountpoint', Optional[str]), 75 ('fullsize', bool), 76 ('label', Optional[str]), 77 ('attributes', list), 78 ('is_root_volume', bool) 79 ] 80) 81 82 83class DracutT(NamedTuple): 84 uefi: bool 85 modules: List[str] 86 drivers: List[str] 87 88 89class FileT(NamedTuple): 90 target: str 91 owner: str 92 permissions: str 93 94 95class ContainerT(NamedTuple): 96 name: str 97 backend: str 98 container_file: str 99 fetch_only: bool 100 fetch_command: Callable 101 load_command: List[str] 102 103 104class XMLState: 105 """ 106 **Implements methods to get stateful information from the XML data** 107 108 :param object xml_data: parse result from XMLDescription.load() 109 :param list profiles: list of used profiles 110 :param object build_type: build <type> section reference 111 """ 112 def __init__( 113 self, xml_data: Any, profiles: List = None, 114 build_type: Any = None 115 ): 116 self.root_partition_uuid: Optional[str] = None 117 self.root_filesystem_uuid: Optional[str] = None 118 self.host_architecture = defaults.PLATFORM_MACHINE 119 self.xml_data = xml_data 120 self.profiles = self._used_profiles(profiles) 121 self.build_type = self._build_type_section( 122 build_type 123 ) 124 self.resolve_this_path() 125 126 def get_preferences_sections(self) -> List: 127 """ 128 All preferences sections for the selected profiles that match the 129 host architecture 130 131 :return: list of <preferences> section reference(s) 132 133 :rtype: list 134 """ 135 preferences_list = [] 136 for preferences in self._profiled(self.xml_data.get_preferences()): 137 if self.preferences_matches_host_architecture(preferences): 138 preferences_list.append(preferences) 139 return preferences_list 140 141 def get_description_section(self) -> description_type: 142 """ 143 The description section 144 145 :return: 146 description_type tuple providing the elements 147 author contact and specification 148 149 :rtype: tuple 150 """ 151 description = self.xml_data.get_description()[0] 152 return description_type( 153 author=description.get_author()[0], 154 contact=description.get_contact()[0], 155 specification=description.get_specification()[0].strip() 156 ) 157 158 def get_users_sections(self) -> List: 159 """ 160 All users sections for the selected profiles 161 162 :return: list of <users> section reference(s) 163 164 :rtype: list 165 """ 166 users = [] 167 for users_section in self._profiled(self.xml_data.get_users()): 168 if self.users_matches_host_architecture(users_section): 169 users.append(users_section) 170 return users 171 172 def get_build_type_bundle_format(self) -> str: 173 """ 174 Return bundle_format for build type 175 176 The bundle_format string is validated against the available 177 name tags from kiwi.system.result::result_name_tags. 178 179 :return: bundle format string 180 181 :rtype: str 182 """ 183 return self.build_type.get_bundle_format() 184 185 def get_build_type_name(self) -> str: 186 """ 187 Default build type name 188 189 :return: Content of image attribute from build type 190 191 :rtype: str 192 """ 193 return self.build_type.get_image() 194 195 def btrfs_default_volume_requested(self) -> bool: 196 """ 197 Check if setting a default volume for btrfs is requested 198 """ 199 if self.build_type.get_btrfs_set_default_volume() is False: 200 # Setting a default volume is explicitly switched off 201 return False 202 else: 203 # In any other case (True | None) a default volume 204 # is wanted and will be set 205 return True 206 207 def get_image_version(self) -> str: 208 """ 209 Image version from preferences section. 210 211 Multiple occurences of version in preferences sections are not 212 forbidden, however only the first version found defines the 213 final image version 214 215 :return: Content of <version> section 216 217 :rtype: str 218 """ 219 for preferences in self.get_preferences_sections(): 220 version = preferences.get_version() 221 if version: 222 return version[0] 223 return '' 224 225 def get_initrd_system(self) -> str: 226 """ 227 Name of initrd system to use 228 229 Depending on the image type a specific initrd system is 230 either pre selected or free of choice according to the 231 XML type setup. 232 233 :return: 'dracut', 'kiwi' or 'none' 234 235 :rtype: str 236 """ 237 pre_selection_map = { 238 'vmx': 'dracut', 239 'oem': 'dracut', 240 'iso': 'dracut', 241 'kis': 'dracut', 242 'pxe': 'kiwi', 243 } 244 build_type = self.get_build_type_name() 245 default_initrd_system = pre_selection_map.get(build_type) or 'none' 246 247 if build_type == 'iso': 248 # iso type always use dracut as initrd system 249 return default_initrd_system 250 251 # Allow to choose for any other build type 252 return self.build_type.get_initrd_system() or default_initrd_system 253 254 def get_locale(self) -> Optional[List]: 255 """ 256 Gets list of locale names if configured. Takes 257 the first locale setup from the existing preferences 258 sections into account. 259 260 :return: List of names or None 261 262 :rtype: list|None 263 """ 264 for preferences in self.get_preferences_sections(): 265 locale_section = preferences.get_locale() 266 if locale_section: 267 return locale_section[0].split(',') 268 return None 269 270 def get_rpm_locale(self) -> Optional[List]: 271 """ 272 Gets list of locale names to filter out by rpm 273 if rpm-locale-filtering is switched on the 274 the list always contains: [POSIX, C, C.UTF-8] 275 and is extended by the optionaly configured 276 locale 277 278 :return: List of names or None 279 280 :rtype: list|None 281 """ 282 if self.get_rpm_locale_filtering(): 283 rpm_locale = ['POSIX', 'C', 'C.UTF-8'] 284 configured_locale = self.get_locale() 285 if configured_locale: 286 for locale in configured_locale: 287 rpm_locale.append(locale) 288 return rpm_locale 289 return None 290 291 def get_rpm_locale_filtering(self) -> bool: 292 """ 293 Gets the rpm-locale-filtering configuration flag. Returns 294 False if not present. 295 296 :return: True or False 297 298 :rtype: bool 299 """ 300 for preferences in self.get_preferences_sections(): 301 locale_filtering = preferences.get_rpm_locale_filtering() 302 if locale_filtering: 303 return locale_filtering[0] 304 return False 305 306 def get_rpm_excludedocs(self) -> bool: 307 """ 308 Gets the rpm-excludedocs configuration flag. Returns 309 False if not present. 310 311 :return: True or False 312 313 :rtype: bool 314 """ 315 for preferences in self.get_preferences_sections(): 316 exclude_docs = preferences.get_rpm_excludedocs() 317 if exclude_docs: 318 return exclude_docs[0] 319 return False 320 321 def get_rpm_check_signatures(self) -> bool: 322 """ 323 Gets the rpm-check-signatures configuration flag. Returns 324 False if not present. 325 326 :return: True or False 327 328 :rtype: bool 329 """ 330 for preferences in self.get_preferences_sections(): 331 check_signatures = preferences.get_rpm_check_signatures() 332 if check_signatures: 333 return check_signatures[0] 334 return False 335 336 def get_package_manager(self) -> str: 337 """ 338 Get configured package manager from selected preferences section 339 340 :return: Content of the <packagemanager> section 341 342 :rtype: str 343 """ 344 for preferences in self.get_preferences_sections(): 345 package_manager = preferences.get_packagemanager() 346 if package_manager: 347 return package_manager[0] 348 return Defaults.get_default_package_manager() 349 350 def get_release_version(self) -> str: 351 """ 352 Get configured release version from selected preferences section 353 354 :return: Content of the <release-version> section or '' 355 356 :rtype: str 357 """ 358 release_version = '' 359 for preferences in self.get_preferences_sections(): 360 release_version = preferences.get_release_version() 361 if release_version: 362 release_version = release_version[0] 363 break 364 return release_version 365 366 def get_packages_sections(self, section_types: List) -> List: 367 """ 368 List of packages sections matching given section type(s) 369 370 :param list section_types: type name(s) from packages sections 371 372 :return: list of <packages> section reference(s) 373 374 :rtype: list 375 """ 376 result = [] 377 packages_sections = self._profiled( 378 self.xml_data.get_packages() 379 ) 380 for packages in packages_sections: 381 packages_type = packages.get_type() 382 if packages_type in section_types: 383 result.append(packages) 384 return result 385 386 def requires_matches_host_architecture(self, requires: Any) -> bool: 387 """ 388 Tests if the given profile requires section is applicable for 389 the current host architecture. If no architecture is specified 390 within the section it is considered as a match returning True. 391 392 Note: The XML section pointer must provide an arch attribute 393 394 :param section: XML section object 395 396 :return: True or False 397 398 :rtype: bool 399 """ 400 return self._section_matches_host_architecture(requires) 401 402 def volume_matches_host_architecture(self, volume: Any) -> bool: 403 """ 404 Tests if the given volume section is applicable for the current host 405 architecture. If no architecture is specified within the section 406 it is considered as a match returning True. 407 408 Note: The XML section pointer must provide an arch attribute 409 410 :param section: XML section object 411 412 :return: True or False 413 414 :rtype: bool 415 """ 416 return self._section_matches_host_architecture(volume) 417 418 def package_matches_host_architecture(self, package: Any) -> bool: 419 """ 420 Tests if the given package section is applicable for the current host 421 architecture. If no architecture is specified within the section 422 it is considered as a match returning True. 423 424 Note: The XML section pointer must provide an arch attribute 425 426 :param section: XML section object 427 428 :return: True or False 429 430 :rtype: bool 431 """ 432 return self._section_matches_host_architecture(package) 433 434 def users_matches_host_architecture(self, users: Any) -> bool: 435 """ 436 Tests if the given users section is applicable for the current host 437 architecture. If no architecture is specified within the section 438 it is considered as a match returning True. 439 440 Note: The XML section pointer must provide an arch attribute 441 442 :param section: XML section object 443 444 :return: True or False 445 446 :rtype: bool 447 """ 448 return self._section_matches_host_architecture(users) 449 450 def collection_matches_host_architecture(self, collection: Any) -> bool: 451 """ 452 Tests if the given namedcollection section is applicable for 453 the current host architecture. If no architecture is specified 454 within the section it is considered as a match returning True. 455 456 Note: The XML section pointer must provide an arch attribute 457 458 :param section: XML section object 459 460 :return: True or False 461 462 :rtype: bool 463 """ 464 return self._section_matches_host_architecture(collection) 465 466 def profile_matches_host_architecture(self, profile: Any) -> bool: 467 """ 468 Tests if the given profile section is applicable for the current host 469 architecture. If no architecture is specified within the section 470 it is considered as a match returning True. 471 472 Note: The XML section pointer must provide an arch attribute 473 474 :param section: XML section object 475 476 :return: True or False 477 478 :rtype: bool 479 """ 480 return self._section_matches_host_architecture(profile) 481 482 def preferences_matches_host_architecture(self, preferences: Any) -> bool: 483 """ 484 Tests if the given preferences section is applicable for the 485 current host architecture. If no architecture is specified within 486 the section it is considered as a match returning True. 487 488 Note: The XML section pointer must provide an arch attribute 489 490 :param section: XML section object 491 492 :return: True or False 493 494 :rtype: bool 495 """ 496 return self._section_matches_host_architecture(preferences) 497 498 def repository_matches_host_architecture(self, repository: Any) -> bool: 499 """ 500 Tests if the given repository section is applicable for the 501 current host architecture. If no architecture is specified within 502 the section it is considered as a match returning True. 503 504 Note: The XML section pointer must provide an arch attribute 505 506 :param section: XML section object 507 508 :return: True or False 509 510 :rtype: bool 511 """ 512 return self._section_matches_host_architecture(repository) 513 514 def containers_matches_host_architecture(self, containers: Any) -> bool: 515 """ 516 Tests if the given containers section is applicable for the 517 current host architecture. If no arch attribute is provided in 518 the section it is considered as a match and returns: True. 519 520 :param section: XML section object 521 522 :return: True or False 523 524 :rtype: bool 525 """ 526 return self._section_matches_host_architecture(containers) 527 528 def container_matches_host_architecture(self, container: Any) -> bool: 529 """ 530 Tests if the given container section is applicable for the 531 current host architecture. If no arch attribute is provided in 532 the section it is considered as a match and returns: True. 533 534 :param section: XML section object 535 536 :return: True or False 537 538 :rtype: bool 539 """ 540 return self._section_matches_host_architecture(container) 541 542 def get_package_sections( 543 self, packages_sections: List 544 ) -> List[package_type]: 545 """ 546 List of package sections from the given packages sections. 547 Each list element contains a tuple with the <package> section 548 reference and the <packages> section this package belongs to 549 550 If a package entry specfies an architecture, it is only taken if 551 the host architecture matches the configured architecture 552 553 :param list packages_sections: <packages> 554 555 :return: 556 Contains list of package_type tuples 557 558 .. code:: python 559 560 [package_type(packages_section=object, package_section=object)] 561 562 :rtype: list 563 """ 564 result = [] 565 if packages_sections: 566 for packages_section in packages_sections: 567 package_list = packages_section.get_package() 568 if package_list: 569 for package in package_list: 570 if self.package_matches_host_architecture(package): 571 result.append( 572 package_type( 573 packages_section=packages_section, 574 package_section=package 575 ) 576 ) 577 return result 578 579 def get_to_become_deleted_packages(self, force: bool = True) -> List: 580 """ 581 List of package names from the type="delete" or type="uninstall" 582 packages section(s) 583 584 :param bool force: return "delete" type if True, "uninstall" type 585 otherwise 586 587 :return: package names 588 589 :rtype: list 590 """ 591 result = [] 592 to_become_deleted_packages_sections = self.get_packages_sections( 593 ['delete' if force else 'uninstall'] 594 ) 595 package_list = self.get_package_sections( 596 to_become_deleted_packages_sections 597 ) 598 if package_list: 599 for package in package_list: 600 result.append(package.package_section.get_name()) 601 return sorted(list(set(result))) 602 603 def get_bootstrap_packages_sections(self) -> List: 604 """ 605 List of packages sections matching type="bootstrap" 606 607 :return: list of <packages> section reference(s) 608 609 :rtype: list 610 """ 611 return self.get_packages_sections(['bootstrap']) 612 613 def get_image_packages_sections(self) -> List: 614 """ 615 List of packages sections matching type="image" 616 617 :return: list of <packages> section reference(s) 618 619 :rtype: list 620 """ 621 return self.get_packages_sections(['image']) 622 623 def get_bootstrap_packages(self, plus_packages: List = None) -> List: 624 """ 625 List of package names from the type="bootstrap" packages section(s) 626 627 The list gets the selected package manager appended 628 if there is a request to install packages inside of 629 the image via a chroot operation 630 631 :param list plus_packages: list of additional packages 632 633 :return: package names 634 635 :rtype: list 636 """ 637 result = [] 638 bootstrap_packages_sections = self.get_bootstrap_packages_sections() 639 package_list = self.get_package_sections( 640 bootstrap_packages_sections 641 ) 642 if package_list: 643 for package in package_list: 644 result.append(package.package_section.get_name().strip()) 645 if self.get_system_packages(): 646 package_manager_name = self.get_package_manager() 647 if package_manager_name == 'dnf4': 648 # The package name for dnf4 is just dnf. Thus 649 # the name must be adapted in this case 650 package_manager_name = 'dnf' 651 elif package_manager_name == 'apk': 652 package_manager_name = 'apk-tools' 653 result.append(package_manager_name) 654 if plus_packages: 655 result += plus_packages 656 return sorted(list(set(result))) 657 658 def get_system_packages(self) -> List: 659 """ 660 List of package names from the packages sections matching 661 type="image" and type=build_type 662 663 :return: package names 664 665 :rtype: list 666 """ 667 result = [] 668 image_packages_sections = self.get_packages_sections( 669 ['image', self.get_build_type_name()] 670 ) 671 package_list = self.get_package_sections( 672 image_packages_sections 673 ) 674 if package_list: 675 for package in package_list: 676 result.append(package.package_section.get_name().strip()) 677 return sorted(list(set(result))) 678 679 def get_bootstrap_files(self) -> Dict[str, FileT]: 680 """ 681 List of file names from the type="bootstrap" packages section(s) 682 683 :return: file names 684 685 :rtype: dict 686 """ 687 result = {} 688 bootstrap_packages_sections = self.get_bootstrap_packages_sections() 689 if bootstrap_packages_sections: 690 for bootstrap_packages_section in bootstrap_packages_sections: 691 file_list = bootstrap_packages_section.get_file() or [] 692 for file in file_list: 693 result[file.get_name()] = FileT( 694 target=file.get_target() or '', 695 owner=file.get_owner() or '', 696 permissions=file.get_permissions() or '' 697 ) 698 return result 699 700 def get_system_files(self) -> Dict[str, FileT]: 701 """ 702 List of file names from the packages sections matching 703 type="image" and type=build_type 704 705 :return: file names 706 707 :rtype: dict 708 """ 709 result = {} 710 image_packages_sections = self.get_packages_sections( 711 ['image', self.get_build_type_name()] 712 ) 713 for packages in image_packages_sections: 714 for file in packages.get_file(): 715 result[file.get_name()] = FileT( 716 target=file.get_target() or '', 717 owner=file.get_owner() or '', 718 permissions=file.get_permissions() or '' 719 ) 720 return result 721 722 def get_bootstrap_archives(self) -> List: 723 """ 724 List of archive names from the type="bootstrap" packages section(s) 725 726 :return: archive names 727 728 :rtype: list 729 """ 730 result = [] 731 bootstrap_packages_sections = self.get_bootstrap_packages_sections() 732 if bootstrap_packages_sections: 733 for bootstrap_packages_section in bootstrap_packages_sections: 734 archive_list = bootstrap_packages_section.get_archive() 735 if archive_list: 736 for archive in archive_list: 737 result.append(archive.get_name().strip()) 738 return sorted(result) 739 740 def get_system_archives(self) -> List: 741 """ 742 List of archive names from the packages sections matching 743 type="image" and type=build_type 744 745 :return: archive names 746 747 :rtype: list 748 """ 749 result = [] 750 image_packages_sections = self.get_packages_sections( 751 ['image', self.get_build_type_name()] 752 ) 753 for packages in image_packages_sections: 754 for archive in packages.get_archive(): 755 result.append(archive.get_name().strip()) 756 return sorted(result) 757 758 def get_ignore_packages(self, section_type: str) -> List: 759 """ 760 List of ignore package names from the packages sections matching 761 section_type and type=build_type 762 763 :return: package names 764 765 :rtype: list 766 """ 767 result = [] 768 image_packages_sections = self.get_packages_sections( 769 [section_type, self.get_build_type_name()] 770 ) 771 for packages in image_packages_sections: 772 for package in packages.get_ignore(): 773 if self.package_matches_host_architecture(package): 774 result.append(package.get_name().strip()) 775 return sorted(result) 776 777 def get_system_files_ignore_packages(self) -> List[str]: 778 """ 779 List of ignore package names from the type="systemfiles" 780 packages section(s) 781 782 :return: package names 783 784 :rtype: list 785 """ 786 return self.get_ignore_packages('systemfiles') 787 788 def get_system_ignore_packages(self) -> List: 789 """ 790 List of ignore package names from the packages sections matching 791 type="image" and type=build_type 792 793 :return: package names 794 795 :rtype: list 796 """ 797 return self.get_ignore_packages('image') 798 799 def get_bootstrap_ignore_packages(self) -> List: 800 """ 801 List of ignore package names from the packages sections matching 802 type="image" and type=build_type 803 804 :return: package names 805 806 :rtype: list 807 """ 808 return self.get_ignore_packages('bootstrap') 809 810 def get_bootstrap_package_name(self) -> str: 811 """ 812 bootstrap_package name from type="bootstrap" packages section 813 814 :return: bootstrap_package name 815 816 :rtype: str 817 """ 818 typed_packages_sections = self.get_packages_sections( 819 ['bootstrap', self.get_build_type_name()] 820 ) 821 bootstrap_package = '' 822 for packages in typed_packages_sections: 823 bootstrap_package = packages.get_bootstrap_package() 824 if bootstrap_package: 825 break 826 return bootstrap_package 827 828 def get_collection_type(self, section_type: str = 'image') -> str: 829 """ 830 Collection type from packages sections matching given section 831 type. 832 833 If no collection type is specified the default collection 834 type is set to: onlyRequired 835 836 :param str section_type: type name from packages section 837 838 :return: collection type name 839 840 :rtype: str 841 """ 842 typed_packages_sections = self.get_packages_sections( 843 [section_type, self.get_build_type_name()] 844 ) 845 collection_type = 'onlyRequired' 846 for packages in typed_packages_sections: 847 packages_collection_type = packages.get_patternType() 848 if packages_collection_type: 849 collection_type = packages_collection_type 850 break 851 return collection_type 852 853 def get_bootstrap_collection_type(self) -> str: 854 """ 855 Collection type for packages sections matching type="bootstrap" 856 857 :return: collection type name 858 859 :rtype: str 860 """ 861 return self.get_collection_type('bootstrap') 862 863 def get_system_collection_type(self) -> str: 864 """ 865 Collection type for packages sections matching type="image" 866 867 :return: collection type name 868 869 :rtype: str 870 """ 871 return self.get_collection_type('image') 872 873 def get_collection_modules(self) -> Dict[str, List[str]]: 874 """ 875 Dict of collection modules to enable and/or disable 876 877 :return: 878 Dict of the form: 879 880 .. code:: python 881 882 { 883 'enable': [ 884 "module:stream", "module" 885 ], 886 'disable': [ 887 "module" 888 ] 889 } 890 891 :rtype: dict 892 """ 893 modules: Dict[str, List[str]] = { 894 'disable': [], 895 'enable': [] 896 } 897 for packages in self.get_bootstrap_packages_sections(): 898 for collection_module in packages.get_collectionModule(): 899 module_name = collection_module.get_name() 900 if collection_module.get_enable() is False: 901 modules['disable'].append(module_name) 902 else: 903 stream = collection_module.get_stream() 904 if stream: 905 modules['enable'].append(f'{module_name}:{stream}') 906 else: 907 modules['enable'].append(module_name) 908 return modules 909 910 def get_collections(self, section_type: str = 'image') -> List: 911 """ 912 List of collection names from the packages sections matching 913 type=section_type and type=build_type 914 915 :return: collection names 916 917 :rtype: list 918 """ 919 result = [] 920 typed_packages_sections = self.get_packages_sections( 921 [section_type, self.get_build_type_name()] 922 ) 923 for packages in typed_packages_sections: 924 for collection in packages.get_namedCollection(): 925 if self.collection_matches_host_architecture(collection): 926 result.append(collection.get_name()) 927 return sorted(list(set(result))) 928 929 def get_bootstrap_collections(self) -> List: 930 """ 931 List of collection names from the packages sections 932 matching type="bootstrap" 933 934 :return: collection names 935 936 :rtype: list 937 """ 938 return self.get_collections('bootstrap') 939 940 def get_system_collections(self) -> List: 941 """ 942 List of collection names from the packages sections 943 matching type="image" 944 945 :return: collection names 946 947 :rtype: list 948 """ 949 return self.get_collections('image') 950 951 def get_products(self, section_type: str = 'image') -> List: 952 """ 953 List of product names from the packages sections matching 954 type=section_type and type=build_type 955 956 :param str section_type: type name from packages section 957 958 :return: product names 959 960 :rtype: list 961 """ 962 result = [] 963 typed_packages_sections = self.get_packages_sections( 964 [section_type, self.get_build_type_name()] 965 ) 966 for packages in typed_packages_sections: 967 for product in packages.get_product(): 968 result.append(product.get_name()) 969 return list(set(result)) 970 971 def get_bootstrap_products(self) -> List: 972 """ 973 List of product names from the packages sections 974 matching type="bootstrap" 975 976 :return: product names 977 978 :rtype: list 979 """ 980 return self.get_products('bootstrap') 981 982 def get_system_products(self) -> List: 983 """ 984 List of product names from the packages sections 985 matching type="image" 986 987 :return: product names 988 989 :rtype: list 990 """ 991 return self.get_products('image') 992 993 def is_xen_server(self) -> bool: 994 """ 995 Check if build type domain setup specifies a Xen Server (dom0) 996 997 :return: True or False 998 999 :rtype: bool 1000 """ 1001 return self.build_type.get_xen_server() 1002 1003 def is_xen_guest(self) -> bool: 1004 """ 1005 Check if build type setup specifies a Xen Guest (domX) 1006 The check is based on the architecture, the firmware and 1007 xen_loader configuration values: 1008 1009 * We only support Xen setup on the x86_64 architecture 1010 1011 * Firmware pointing to ec2 means the image is targeted to run 1012 in Amazon EC2 which is a Xen guest 1013 1014 * Machine setup with a xen_loader attribute also indicates a 1015 Xen guest target 1016 1017 :return: True or False 1018 1019 :rtype: bool 1020 """ 1021 if self.host_architecture != 'x86_64': 1022 # We only support Xen stuff on x86_64 1023 return False 1024 firmware = self.build_type.get_firmware() 1025 machine_section = self.get_build_type_machine_section() 1026 if firmware and firmware in Defaults.get_ec2_capable_firmware_names(): 1027 # the image is targeted to run in Amazon EC2 which is a Xen system 1028 return True 1029 elif machine_section and machine_section.get_xen_loader(): 1030 # the image provides a machine section with a guest loader setup 1031 return True 1032 return False 1033 1034 def get_build_type_partitions_section(self) -> Any: 1035 """ 1036 First partitions section from the build type section 1037 1038 :return: <partitions> section reference 1039 1040 :rtype: xml_parse::partitions 1041 """ 1042 partitions_sections = self.build_type.get_partitions() 1043 if partitions_sections: 1044 return partitions_sections[0] 1045 return None 1046 1047 def get_build_type_system_disk_section(self) -> Any: 1048 """ 1049 First system disk section from the build type section 1050 1051 :return: <systemdisk> section reference 1052 1053 :rtype: xml_parse::systemdisk 1054 """ 1055 systemdisk_sections = self.build_type.get_systemdisk() 1056 if systemdisk_sections: 1057 return systemdisk_sections[0] 1058 return None 1059 1060 def get_build_type_machine_section(self) -> Any: 1061 """ 1062 First machine section from the build type section 1063 1064 :return: <machine> section reference 1065 1066 :rtype: xml_parse::machine 1067 """ 1068 machine_sections = self.build_type.get_machine() 1069 if machine_sections: 1070 return machine_sections[0] 1071 return None 1072 1073 def get_build_type_vagrant_config_section(self) -> Any: 1074 """ 1075 First vagrantconfig section from the build type section 1076 1077 :return: <vagrantconfig> section reference 1078 1079 :rtype: xml_parse::vagrantconfig 1080 """ 1081 vagrant_config_sections = self.build_type.get_vagrantconfig() 1082 if vagrant_config_sections: 1083 return vagrant_config_sections[0] 1084 return None 1085 1086 def get_vagrant_config_virtualbox_guest_additions(self) -> bool: 1087 """ 1088 Attribute virtualbox_guest_additions_present from the first 1089 vagrantconfig section. 1090 1091 :return: True|False 1092 1093 :rtype: bool 1094 """ 1095 vagrant_config_sections = self.get_build_type_vagrant_config_section() 1096 if not vagrant_config_sections.virtualbox_guest_additions_present: 1097 return Defaults.get_vagrant_config_virtualbox_guest_additions() 1098 else: 1099 return vagrant_config_sections.virtualbox_guest_additions_present 1100 1101 def get_build_type_vmdisk_section(self) -> Any: 1102 """ 1103 First vmdisk section from the first machine section in the 1104 build type section 1105 1106 :return: <vmdisk> section reference 1107 1108 :rtype: xml_parse::vmdisk 1109 """ 1110 machine_section = self.get_build_type_machine_section() 1111 if machine_section: 1112 vmdisk_sections = machine_section.get_vmdisk() 1113 if vmdisk_sections: 1114 return vmdisk_sections[0] 1115 return None 1116 1117 def get_build_type_vmnic_entries(self) -> List: 1118 """ 1119 vmnic section(s) from the first machine section in the 1120 build type section 1121 1122 :return: list of <vmnic> section reference(s) 1123 1124 :rtype: list 1125 """ 1126 machine_section = self.get_build_type_machine_section() 1127 if machine_section: 1128 return machine_section.get_vmnic() 1129 else: 1130 return [] 1131 1132 def get_build_type_vmdvd_section(self) -> Any: 1133 """ 1134 First vmdvd section from the first machine section in the 1135 build type section 1136 1137 :return: <vmdvd> section reference 1138 1139 :rtype: xml_parse::vmdvd 1140 """ 1141 machine_section = self.get_build_type_machine_section() 1142 if machine_section: 1143 vmdvd_sections = machine_section.get_vmdvd() 1144 if vmdvd_sections: 1145 return vmdvd_sections[0] 1146 return None 1147 1148 def get_build_type_vmconfig_entries(self) -> List: 1149 """ 1150 List of vmconfig-entry section values from the first 1151 machine section in the build type section 1152 1153 :return: <vmconfig_entry> section reference(s) 1154 1155 :rtype: list 1156 """ 1157 machine_section = self.get_build_type_machine_section() 1158 if machine_section: 1159 vmconfig_entries = machine_section.get_vmconfig_entry() 1160 if vmconfig_entries: 1161 return vmconfig_entries 1162 1163 return [] 1164 1165 def get_build_type_bootloader_section(self) -> Any: 1166 """ 1167 First bootloader section from the build type section 1168 1169 :return: <bootloader> section reference 1170 1171 :rtype: xml_parse::bootloader 1172 """ 1173 bootloader_sections = self.build_type.get_bootloader() 1174 if bootloader_sections: 1175 return bootloader_sections[0] 1176 return None 1177 1178 def get_build_type_bootloader_name(self) -> str: 1179 """ 1180 Return bootloader name for selected build type 1181 1182 :return: bootloader name 1183 1184 :rtype: str 1185 """ 1186 bootloader = self.get_build_type_bootloader_section() 1187 return bootloader.get_name() if bootloader else \ 1188 Defaults.get_default_bootloader() 1189 1190 def get_build_type_bootloader_bls(self) -> bool: 1191 """ 1192 Return bootloader bls setting for selected build type 1193 1194 :return: True or False 1195 1196 :rtype: bool 1197 """ 1198 bootloader = self.get_build_type_bootloader_section() 1199 if bootloader and bootloader.get_bls() is not None: 1200 return bootloader.get_bls() 1201 return True 1202 1203 def get_build_type_bootloader_console(self) -> List[str]: 1204 """ 1205 Return bootloader console setting for selected build type 1206 1207 :return: 1208 list of console settings for output (first element) 1209 and input (second element) 1210 1211 :rtype: list 1212 """ 1213 result = ['', ''] 1214 bootloader = self.get_build_type_bootloader_section() 1215 if bootloader: 1216 console_out = bootloader.get_output_console() 1217 console_in = bootloader.get_input_console() 1218 console_in = console_in if console_in else console_out 1219 result = [ 1220 console_out if console_out and console_out != 'none' else '', 1221 console_in if console_in and console_in != 'none' else '' 1222 ] 1223 return result 1224 1225 def get_build_type_bootloader_serial_line_setup(self) -> Optional[str]: 1226 """ 1227 Return bootloader serial line setup parameters for the 1228 selected build type 1229 1230 :return: serial line setup 1231 1232 :rtype: str 1233 """ 1234 bootloader = self.get_build_type_bootloader_section() 1235 if bootloader: 1236 return bootloader.get_serial_line() 1237 return None 1238 1239 def get_build_type_bootloader_timeout(self) -> Optional[str]: 1240 """ 1241 Return bootloader timeout setting for selected build type 1242 1243 :return: timeout string 1244 1245 :rtype: str 1246 """ 1247 bootloader = self.get_build_type_bootloader_section() 1248 if bootloader: 1249 return bootloader.get_timeout() 1250 return None 1251 1252 def get_build_type_bootloader_timeout_style(self) -> Optional[str]: 1253 """ 1254 Return bootloader timeout style setting for selected build type 1255 1256 :return: timeout_style string 1257 1258 :rtype: str 1259 """ 1260 bootloader = self.get_build_type_bootloader_section() 1261 if bootloader: 1262 return bootloader.get_timeout_style() 1263 return None 1264 1265 def get_build_type_bootloader_targettype(self) -> Optional[str]: 1266 """ 1267 Return bootloader target type setting. Only relevant for 1268 the zipl bootloader because zipl is installed differently 1269 depending on the storage target it runs later 1270 1271 :return: target type string 1272 1273 :rtype: str 1274 """ 1275 bootloader = self.get_build_type_bootloader_section() 1276 if bootloader: 1277 return bootloader.get_targettype() 1278 return None 1279 1280 def get_build_type_bootloader_settings_section(self) -> Any: 1281 """ 1282 First bootloadersettings section from the build 1283 type bootloader section 1284 1285 :return: <bootloadersettings> section reference 1286 1287 :rtype: xml_parse::bootloadersettings 1288 """ 1289 bootloader_section = self.get_build_type_bootloader_section() 1290 bootloader_settings_section = None 1291 if bootloader_section and bootloader_section.get_bootloadersettings(): 1292 bootloader_settings_section = \ 1293 bootloader_section.get_bootloadersettings()[0] 1294 return bootloader_settings_section 1295 1296 def get_build_type_bootloader_securelinux_section(self) -> List[Any]: 1297 """ 1298 First securelinux section from the build 1299 type bootloader section 1300 1301 :return: <securelinux> section reference 1302 1303 :rtype: xml_parse::securelinux 1304 """ 1305 bootloader_section = self.get_build_type_bootloader_section() 1306 bootloader_securelinux_section = [] 1307 if bootloader_section and bootloader_section.get_securelinux(): 1308 bootloader_securelinux_section = \ 1309 bootloader_section.get_securelinux() 1310 return bootloader_securelinux_section 1311 1312 def get_build_type_bootloader_environment_variables(self) -> List[str]: 1313 """ 1314 List of bootloader variables from the build 1315 type > bootloader > bootloadersettings section 1316 """ 1317 variable_list = [] 1318 bootloader_settings_section = \ 1319 self.get_build_type_bootloader_settings_section() 1320 if bootloader_settings_section: 1321 environment = bootloader_settings_section.get_environment() 1322 if environment and environment[0].get_env(): 1323 for env in environment[0].get_env(): 1324 variable_list.append( 1325 '{}{}'.format( 1326 env.get_name(), 1327 f'={env.get_value()}' if env.get_value() else '' 1328 ) 1329 ) 1330 return variable_list 1331 1332 def get_bootloader_options(self, option_type: str) -> List[str]: 1333 """ 1334 List of custom options used in the process to 1335 run bootloader setup workloads 1336 """ 1337 result: List[str] = [] 1338 bootloader_settings = self.get_build_type_bootloader_settings_section() 1339 if bootloader_settings: 1340 options = [] 1341 if option_type == 'shim': 1342 options = bootloader_settings.get_shimoption() 1343 elif option_type == 'install': 1344 options = bootloader_settings.get_installoption() 1345 elif option_type == 'config': 1346 options = bootloader_settings.get_configoption() 1347 for option in options: 1348 result.append(option.get_name()) 1349 if option.get_value(): 1350 result.append(option.get_value()) 1351 return result 1352 1353 def get_bootloader_shim_options(self) -> List[str]: 1354 """ 1355 List of custom options used in the process to setup secure boot 1356 """ 1357 return self.get_bootloader_options('shim') 1358 1359 def get_bootloader_install_options(self) -> List[str]: 1360 """ 1361 List of custom options used in the bootloader installation 1362 """ 1363 return self.get_bootloader_options('install') 1364 1365 def get_bootloader_config_options(self) -> List[str]: 1366 """ 1367 List of custom options used in the bootloader configuration 1368 """ 1369 return self.get_bootloader_options('config') 1370 1371 def get_build_type_bootloader_use_disk_password(self) -> bool: 1372 """ 1373 Indicate whether the bootloader configuration should use the 1374 password protecting the encrypted root volume. 1375 1376 :return: True|False 1377 1378 :rtype: bool 1379 """ 1380 bootloader = self.get_build_type_bootloader_section() 1381 if bootloader: 1382 return bootloader.get_use_disk_password() 1383 return False 1384 1385 def get_build_type_oemconfig_section(self) -> Any: 1386 """ 1387 First oemconfig section from the build type section 1388 1389 :return: <oemconfig> section reference 1390 1391 :rtype: xml_parse::oemconfig 1392 """ 1393 oemconfig_sections = self.build_type.get_oemconfig() 1394 if oemconfig_sections: 1395 return oemconfig_sections[0] 1396 return None 1397 1398 def get_oemconfig_oem_resize(self) -> bool: 1399 """ 1400 State value to activate/deactivate disk resize. Returns a 1401 boolean value if specified or True to set resize on by default 1402 1403 :return: Content of <oem-resize> section value 1404 1405 :rtype: bool 1406 """ 1407 oemconfig = self.get_build_type_oemconfig_section() 1408 if oemconfig and oemconfig.get_oem_resize(): 1409 return oemconfig.get_oem_resize()[0] 1410 else: 1411 return True 1412 1413 def get_oemconfig_oem_systemsize(self) -> int: 1414 """ 1415 State value to retrieve root partition size 1416 1417 :return: Content of <oem-systemsize> section value 1418 1419 :rtype: int 1420 """ 1421 oemconfig = self.get_build_type_oemconfig_section() 1422 if oemconfig and oemconfig.get_oem_systemsize(): 1423 return int(oemconfig.get_oem_systemsize()[0]) 1424 else: 1425 return 0 1426 1427 def get_oemconfig_oem_multipath_scan(self) -> bool: 1428 """ 1429 State value to activate multipath maps. Returns a boolean 1430 value if specified or False 1431 1432 :return: Content of <oem-multipath-scan> section value 1433 1434 :rtype: bool 1435 """ 1436 oemconfig = self.get_build_type_oemconfig_section() 1437 if oemconfig and oemconfig.get_oem_multipath_scan(): 1438 return oemconfig.get_oem_multipath_scan()[0] 1439 return False 1440 1441 def get_oemconfig_swap_mbytes(self) -> Optional[int]: 1442 """ 1443 Return swapsize in MB if requested or None 1444 1445 Operates on the value of oem-swap and if set to true 1446 returns the given size or the default value. 1447 1448 :return: Content of <oem-swapsize> section value or default 1449 1450 :rtype: int 1451 """ 1452 oemconfig = self.get_build_type_oemconfig_section() 1453 if oemconfig and oemconfig.get_oem_swap(): 1454 swap_requested = oemconfig.get_oem_swap()[0] 1455 if swap_requested: 1456 swapsize = oemconfig.get_oem_swapsize() 1457 if swapsize: 1458 return swapsize[0] 1459 else: 1460 return Defaults.get_swapsize_mbytes() 1461 return None 1462 1463 def get_oemconfig_swap_name(self) -> str: 1464 """ 1465 Return the swap space name 1466 1467 Operates on the value of oem-swapname and if set 1468 returns the configured name or the default name: LVSwap 1469 1470 The name of the swap space is used only if the 1471 image is configured to use the LVM volume manager. 1472 In this case swap is a volume and the volume takes 1473 a name. In any other case the given name will have 1474 no effect. 1475 1476 :return: Content of <oem-swapname> section value or default 1477 1478 :rtype: str 1479 """ 1480 oemconfig = self.get_build_type_oemconfig_section() 1481 if oemconfig and oemconfig.get_oem_swapname(): 1482 return oemconfig.get_oem_swapname()[0] 1483 return 'LVSwap' 1484 1485 def get_build_type_containerconfig_section(self) -> Any: 1486 """ 1487 First containerconfig section from the build type section 1488 1489 :return: <containerconfig> section reference 1490 1491 :rtype: xml_parse::containerconfig 1492 """ 1493 container_config_sections = self.build_type.get_containerconfig() 1494 if container_config_sections: 1495 return container_config_sections[0] 1496 return None 1497 1498 def get_dracut_config(self, action: str) -> DracutT: 1499 """ 1500 Get dracut initrd config for the specified action 1501 """ 1502 uefi = False 1503 modules = [] 1504 drivers = [] 1505 initrd_sections = self.build_type.get_initrd() 1506 for initrd_section in initrd_sections: 1507 if initrd_section.get_action() == action: 1508 for dracut in initrd_section.get_dracut(): 1509 uefi = bool(dracut.get_uefi()) 1510 if dracut.get_module(): 1511 modules.append(dracut.get_module()) 1512 if dracut.get_driver(): 1513 drivers.append(dracut.get_driver()) 1514 return DracutT( 1515 uefi=uefi, modules=modules, drivers=drivers 1516 ) 1517 1518 def get_installmedia_initrd_modules(self, action: str) -> List[str]: 1519 """ 1520 Gets the list of modules to append in installation initrds 1521 1522 :return: a list of dracut module names 1523 1524 :rtype: list 1525 """ 1526 modules: List[str] = [] 1527 installmedia = self.build_type.get_installmedia() 1528 if not installmedia: 1529 return modules 1530 initrd_sections = installmedia[0].get_initrd() 1531 for initrd_section in initrd_sections: 1532 if initrd_section.get_action() == action: 1533 for module in initrd_section.get_dracut(): 1534 if module.get_module(): 1535 modules.append(module.get_module()) 1536 return modules 1537 1538 def get_installmedia_initrd_drivers(self, action: str) -> List[str]: 1539 """ 1540 Gets the list of drivers to append in installation initrds 1541 1542 :return: a list of dracut driver names 1543 1544 :rtype: list 1545 """ 1546 drivers: List[str] = [] 1547 installmedia = self.build_type.get_installmedia() 1548 if not installmedia: 1549 return drivers 1550 initrd_sections = installmedia[0].get_initrd() 1551 for initrd_section in initrd_sections: 1552 if initrd_section.get_action() == action: 1553 for driver in initrd_section.get_dracut(): 1554 if driver.get_driver(): 1555 drivers.append(driver.get_driver()) 1556 return drivers 1557 1558 def get_build_type_size( 1559 self, include_unpartitioned: bool = False 1560 ) -> Optional[size_type]: 1561 """ 1562 Size information from the build type section. 1563 If no unit is set the value is treated as mbytes 1564 1565 :param bool include_unpartitioned: sets if the unpartitioned area 1566 should be included in the computed size or not 1567 1568 :return: mbytes 1569 1570 :rtype: int 1571 """ 1572 size_section = self.build_type.get_size() 1573 if size_section: 1574 unit = size_section[0].get_unit() 1575 additive = size_section[0].get_additive() 1576 unpartitioned = size_section[0].get_unpartitioned() 1577 value = int(size_section[0].get_valueOf_()) 1578 if not include_unpartitioned and unpartitioned is not None: 1579 value -= unpartitioned 1580 if unit == 'G': 1581 value *= 1024 1582 return size_type( 1583 mbytes=value, additive=additive 1584 ) 1585 return None 1586 1587 def get_build_type_unpartitioned_bytes(self) -> int: 1588 """ 1589 Size of the unpartitioned area for image in megabytes 1590 1591 :return: mbytes 1592 1593 :rtype: int 1594 """ 1595 size_section = self.build_type.get_size() 1596 if size_section: 1597 unit = size_section[0].get_unit() or 'M' 1598 unpartitioned = size_section[0].get_unpartitioned() or 0 1599 return StringToSize.to_bytes('{0}{1}'.format(unpartitioned, unit)) 1600 return 0 1601 1602 def get_disk_start_sector(self) -> int: 1603 """ 1604 First disk sector number to be used by the first disk partition. 1605 1606 :return: number 1607 1608 :rtype: int 1609 """ 1610 disk_start_sector = self.build_type.get_disk_start_sector() 1611 if disk_start_sector is None: 1612 disk_start_sector = Defaults.get_default_disk_start_sector() 1613 return disk_start_sector 1614 1615 def get_build_type_spare_part_size(self) -> Optional[int]: 1616 """ 1617 Size information for the spare_part size from the build 1618 type. If no unit is set the value is treated as mbytes 1619 1620 :return: mbytes 1621 1622 :rtype: int 1623 """ 1624 spare_part_size = self.build_type.get_spare_part() 1625 if spare_part_size: 1626 return self._to_mega_byte(spare_part_size) 1627 return None 1628 1629 def get_build_type_spare_part_fs_attributes(self) -> Optional[List]: 1630 """ 1631 Build type specific list of filesystem attributes applied to 1632 the spare partition. 1633 1634 :return: list of strings or empty list 1635 1636 :rtype: list 1637 """ 1638 spare_part_attributes = self.build_type.get_spare_part_fs_attributes() 1639 if spare_part_attributes: 1640 return spare_part_attributes.strip().split(',') 1641 return None 1642 1643 def get_build_type_format_options(self) -> Dict: 1644 """ 1645 Disk format options returned as a dictionary 1646 1647 :return: format options 1648 1649 :rtype: dict 1650 """ 1651 result = {} 1652 format_options = self.build_type.get_formatoptions() 1653 if format_options: 1654 for option in format_options.split(','): 1655 key_value_list = option.split('=') 1656 if len(key_value_list) == 2: 1657 result[key_value_list[0]] = key_value_list[1] 1658 else: 1659 result[key_value_list[0]] = None 1660 return result 1661 1662 def get_volume_group_name(self) -> str: 1663 """ 1664 Volume group name from selected <systemdisk> section 1665 1666 :return: volume group name 1667 1668 :rtype: str 1669 """ 1670 systemdisk_section = self.get_build_type_system_disk_section() 1671 volume_group_name = None 1672 if systemdisk_section: 1673 volume_group_name = systemdisk_section.get_name() 1674 if not volume_group_name: 1675 volume_group_name = Defaults.get_default_volume_group_name() 1676 return volume_group_name 1677 1678 def get_users(self) -> List: 1679 """ 1680 List of configured users. 1681 1682 Each entry in the list is a single xml_parse::user instance. 1683 1684 :return: list of <user> section reference(s) 1685 1686 :rtype: list 1687 """ 1688 users_list = [] 1689 users_names_added = [] 1690 for users_section in self.get_users_sections(): 1691 for user in users_section.get_user(): 1692 if user.get_name() not in users_names_added: 1693 users_list.append(user) 1694 users_names_added.append(user.get_name()) 1695 1696 return users_list 1697 1698 def get_user_groups(self, user_name) -> List[str]: 1699 """ 1700 List of group names matching specified user 1701 1702 Each entry in the list is the name of a group and optionally its 1703 group ID separated by a colon, that the specified user belongs to. 1704 The first item in the list is the login or primary group. The 1705 list will be empty if no groups are specified in the 1706 description file. 1707 1708 :return: groups data for the given user 1709 1710 :rtype: list 1711 """ 1712 groups_list = [] 1713 for users_section in self.get_users_sections(): 1714 for user in users_section.get_user(): 1715 if user.get_name() == user_name: 1716 user_groups = user.get_groups() 1717 if user_groups: 1718 groups_list += user.get_groups().split(',') 1719 1720 # order of list items matter, thus we don't use set() here 1721 # better faster, nicer solutions welcome :) 1722 result_group_list = [] 1723 for item in groups_list: 1724 if item not in result_group_list: 1725 result_group_list.append(item) 1726 1727 return result_group_list 1728 1729 def get_container_config(self) -> Dict: 1730 """ 1731 Dictionary of containerconfig information 1732 1733 Takes attributes and subsection data from the selected 1734 <containerconfig> section and stores it in a dictionary 1735 """ 1736 container_config = self._match_docker_base_data() 1737 container_config.update( 1738 self._match_docker_entrypoint() 1739 ) 1740 container_config.update( 1741 self._match_docker_subcommand() 1742 ) 1743 container_config.update( 1744 self._match_docker_expose_ports() 1745 ) 1746 container_config.update( 1747 self._match_docker_volumes() 1748 ) 1749 container_config.update( 1750 self._match_docker_stopsignal() 1751 ) 1752 container_config.update( 1753 self._match_docker_environment() 1754 ) 1755 container_config.update( 1756 self._match_docker_labels() 1757 ) 1758 container_config.update( 1759 self._match_docker_history() 1760 ) 1761 1762 desc = self.get_description_section() 1763 author_contact = "{0} <{1}>".format(desc.author, desc.contact) 1764 if 'history' not in container_config: 1765 container_config['history'] = {} 1766 if 'author' not in container_config['history']: 1767 container_config['history']['author'] = author_contact 1768 if 'maintainer' not in container_config: 1769 container_config['maintainer'] = author_contact 1770 1771 return container_config 1772 1773 def set_container_config_tag(self, tag: str) -> None: 1774 """ 1775 Set new tag name in containerconfig section 1776 1777 In order to set a new tag value an existing containerconfig and 1778 tag setup is required 1779 1780 :param str tag: tag name 1781 """ 1782 container_config_section = self.get_build_type_containerconfig_section() 1783 if container_config_section and container_config_section.get_tag(): 1784 container_config_section.set_tag(tag) 1785 else: 1786 message = dedent('''\n 1787 No <containerconfig> section and/or tag attribute configured 1788 1789 In order to set the tag {0} as new container tag, 1790 an initial containerconfig section including a tag 1791 setup is required 1792 ''') 1793 log.warning(message.format(tag)) 1794 1795 def add_container_config_label(self, label_name: str, value: str) -> None: 1796 """ 1797 Adds a new label in the containerconfig section, if a label with the 1798 same name is already defined in containerconfig it gets overwritten by 1799 this method. 1800 1801 :param str label_name: the string representing the label name 1802 :param str value: the value of the label 1803 """ 1804 if self.get_build_type_name() not in ['docker', 'oci']: 1805 message = dedent('''\n 1806 Labels can only be configured for container image types 1807 docker and oci. 1808 ''') 1809 log.warning(message) 1810 return 1811 1812 container_config_section = self.get_build_type_containerconfig_section() 1813 if not container_config_section: 1814 container_config_section = xml_parse.containerconfig( 1815 name=Defaults.get_default_container_name(), 1816 tag=Defaults.get_default_container_tag() 1817 ) 1818 self.build_type.set_containerconfig([container_config_section]) 1819 1820 labels = container_config_section.get_labels() 1821 if not labels: 1822 labels = [xml_parse.labels()] 1823 1824 label_names = [] 1825 for label in labels[0].get_label(): 1826 label_names.append(label.get_name()) 1827 1828 if label_name in label_names: 1829 labels[0].replace_label_at( 1830 label_names.index(label_name), 1831 xml_parse.label(label_name, value) 1832 ) 1833 else: 1834 labels[0].add_label(xml_parse.label(label_name, value)) 1835 1836 container_config_section.set_labels(labels) 1837 1838 def get_partitions(self) -> Dict[str, ptable_entry_type]: 1839 """ 1840 Dictionary of configured partitions. 1841 1842 Each entry in the dict references a ptable_entry_type 1843 Each key in the dict references the name of the 1844 partition entry as handled by KIWI 1845 1846 :return: 1847 Contains dict of ptable_entry_type tuples 1848 1849 .. code:: python 1850 1851 { 1852 'NAME': ptable_entry_type( 1853 mbsize=int, 1854 clone=int, 1855 partition_name=str, 1856 partition_type=str, 1857 partition_id=Optional[int], 1858 mountpoint=str, 1859 filesystem=str, 1860 label=str 1861 ) 1862 } 1863 1864 :rtype: dict 1865 """ 1866 partitions: Dict[str, ptable_entry_type] = {} 1867 partitions_section = self.get_build_type_partitions_section() 1868 if not partitions_section: 1869 return partitions 1870 for partition in partitions_section.get_partition(): 1871 name = partition.get_name() 1872 partition_name = partition.get_partition_name() or f'p.lx{name}' 1873 partitions[name] = ptable_entry_type( 1874 mbsize=self._to_mega_byte(partition.get_size()), 1875 clone=int(partition.get_clone()) if partition.get_clone() else 0, 1876 partition_name=partition_name, 1877 partition_type=partition.get_partition_type() or 't.linux', 1878 partition_id=partition.get_part_id(), 1879 mountpoint=partition.get_mountpoint(), 1880 filesystem=partition.get_filesystem(), 1881 label=partition.get_label() or '' 1882 ) 1883 return partitions 1884 1885 def get_host_key_certificates( 1886 self 1887 ) -> Union[List[Dict[str, List[str]]], List[Dict[str, str]]]: 1888 cc_result = [] 1889 cc_certificates: Dict[str, List[str]] = {} 1890 securelinux_list = \ 1891 self.get_build_type_bootloader_securelinux_section() 1892 for securelinux in securelinux_list: 1893 cc_certificates = { 1894 'hkd_cert': [], 1895 'hkd_revocation_list': [], 1896 'hkd_ca_cert': securelinux.get_hkd_ca_cert(), 1897 'hkd_sign_cert': securelinux.get_hkd_sign_cert() 1898 } 1899 for hkd_cert in securelinux.get_hkd_cert(): 1900 cc_certificates['hkd_cert'].append(hkd_cert.get_name()) 1901 for hkd_revocation_list in securelinux.get_hkd_revocation_list(): 1902 cc_certificates['hkd_revocation_list'].append( 1903 hkd_revocation_list.get_name() 1904 ) 1905 cc_result.append(cc_certificates) 1906 return cc_result 1907 1908 def get_containers(self) -> List[ContainerT]: 1909 containers = [] 1910 1911 def build_fetch_command( 1912 root_dir: str, 1913 container_uri: str = '', 1914 container_file_name: str = '', 1915 container_endpoint: str = '' 1916 ): 1917 pass # pragma: nocover 1918 for containers_section in self.get_containers_sections(): 1919 for container in containers_section.get_container(): 1920 if self.container_matches_host_architecture(container): 1921 fetch_command = build_fetch_command 1922 load_command = [] 1923 container_tag = container.get_tag() or 'latest' 1924 container_path = container.get_path() or '' 1925 container_endpoint = os.path.normpath( 1926 '{0}/{1}/{2}:{3}'.format( 1927 containers_section.get_source(), container_path, 1928 container.name, container_tag 1929 ) 1930 ) 1931 container_file_name = '{0}/{1}_{2}'.format( 1932 defaults.LOCAL_CONTAINERS, container.name, container_tag 1933 ) 1934 container_backend = containers_section.get_backend() or '' 1935 if container_backend in ['podman', 'docker', 'container-snap']: 1936 if Defaults.is_buildservice_worker(): 1937 container_uri = Uri( 1938 'obsrepositories:/{0}'.format( 1939 container_endpoint 1940 ), 'container' 1941 ).translate() 1942 1943 def build_fetch_command( 1944 root_dir: str, 1945 container_uri: str = container_uri, 1946 container_file_name: str = container_file_name, 1947 container_endpoint: str = container_endpoint 1948 ): 1949 def perform(): 1950 Command.run( 1951 [ 1952 'cp', '{0}.ociarchive'.format( 1953 container_uri 1954 ), os.path.normpath( 1955 '{0}/{1}'.format( 1956 root_dir, 1957 container_file_name 1958 ) 1959 ) 1960 ] 1961 ) 1962 perform() 1963 fetch_command = build_fetch_command 1964 else: 1965 1966 def build_fetch_command( 1967 root_dir: str, 1968 container_uri: str = '', 1969 container_file_name: str = container_file_name, 1970 container_endpoint: str = container_endpoint 1971 ): 1972 def perform(): 1973 Command.run( 1974 [ 1975 'chroot', root_dir, 1976 '/usr/bin/skopeo', 'copy', 1977 'docker://{0}'.format( 1978 container_endpoint 1979 ), 1980 'oci-archive:{0}:{1}'.format( 1981 container_file_name, 1982 container_endpoint 1983 ) 1984 ] 1985 ) 1986 perform() 1987 fetch_command = build_fetch_command 1988 if not container.get_fetch_only(): 1989 load_command = [ 1990 f'/usr/bin/{container_backend}', 1991 'load', '-i', container_file_name 1992 ] 1993 containers.append( 1994 ContainerT( 1995 name=f'{container.name}_{container_tag}', 1996 backend=container_backend, 1997 container_file=container_file_name, 1998 fetch_only=bool(container.get_fetch_only()), 1999 fetch_command=fetch_command, 2000 load_command=load_command 2001 ) 2002 ) 2003 return containers 2004 2005 def get_volumes(self) -> List[volume_type]: 2006 """ 2007 List of configured systemdisk volumes. 2008 2009 Each entry in the list is a tuple with the following information 2010 2011 * name: name of the volume 2012 * size: size of the volume 2013 * realpath: system path to lookup volume data. If no mountpoint 2014 is set the volume name is used as data path. 2015 * mountpoint: volume mount point and volume data path 2016 * fullsize: takes all space True|False 2017 * attributes: list of volume attributes handled via chattr 2018 2019 :return: 2020 Contains list of volume_type tuples 2021 2022 .. code:: python 2023 2024 [ 2025 volume_type( 2026 name=volume_name, 2027 parent=volume_parent, 2028 size=volume_size, 2029 realpath=path, 2030 mountpoint=path, 2031 fullsize=True, 2032 label=volume_label, 2033 attributes=['no-copy-on-write'], 2034 is_root_volume=True|False 2035 ) 2036 ] 2037 2038 :rtype: list 2039 """ 2040 volume_type_list: List[volume_type] = [] 2041 systemdisk_section = self.get_build_type_system_disk_section() 2042 selected_filesystem = self.build_type.get_filesystem() 2043 swap_mbytes = self.get_oemconfig_swap_mbytes() 2044 swap_name = self.get_oemconfig_swap_name() 2045 if not systemdisk_section: 2046 return volume_type_list 2047 volumes = systemdisk_section.get_volume() 2048 have_root_volume_setup = False 2049 have_full_size_volume = False 2050 if volumes: 2051 for volume in volumes: 2052 if not self.volume_matches_host_architecture(volume): 2053 continue 2054 # volume setup for a full qualified volume with name and 2055 # mountpoint information. See below for exceptions 2056 name = volume.get_name() 2057 parent = volume.get_parent() or '' 2058 mountpoint = volume.get_mountpoint() 2059 realpath = mountpoint 2060 size = volume.get_size() 2061 freespace = volume.get_freespace() 2062 fullsize = False 2063 label = volume.get_label() 2064 attributes = [] 2065 is_root_volume = False 2066 2067 if volume.get_quota(): 2068 attributes.append(f'quota={volume.get_quota()}') 2069 2070 if volume.get_copy_on_write() is False: 2071 # by default copy-on-write is switched on for any 2072 # filesystem. Thus only if no copy on write is requested 2073 # the attribute is handled 2074 attributes.append('no-copy-on-write') 2075 2076 if volume.get_filesystem_check() is True: 2077 # by default filesystem check is switched off for any 2078 # filesystem except the rootfs. Thus only if filesystem 2079 # check is requested the attribute is handled 2080 attributes.append('enable-for-filesystem-check') 2081 2082 if '@root' in name: 2083 # setup root volume, it takes an optional volume 2084 # name if specified as @root=name and has no specific 2085 # mountpoint. The default name is set to 2086 # defaults.ROOT_VOLUME_NAME if no other root volume 2087 # name is provided 2088 mountpoint = None 2089 realpath = '/' 2090 is_root_volume = True 2091 root_volume_expression = re.match( 2092 r'@root=(.+)', name 2093 ) 2094 if root_volume_expression: 2095 name = root_volume_expression.group(1) 2096 else: 2097 name = defaults.ROOT_VOLUME_NAME 2098 have_root_volume_setup = True 2099 elif not mountpoint: 2100 # setup volume without mountpoint. In this case the name 2101 # attribute is used as mountpoint path and a name for the 2102 # volume is created from that path information 2103 mountpoint = name 2104 realpath = mountpoint 2105 name = self._to_volume_name(name) 2106 2107 if size: 2108 size = 'size:' + format( 2109 self._to_mega_byte(size) 2110 ) 2111 elif freespace: 2112 size = 'freespace:' + format( 2113 self._to_mega_byte(freespace) 2114 ) 2115 else: 2116 size = 'freespace:' + format( 2117 Defaults.get_min_volume_mbytes(selected_filesystem) 2118 ) 2119 2120 if ':all' in size: 2121 size = None 2122 fullsize = True 2123 have_full_size_volume = True 2124 2125 volume_type_list.append( 2126 volume_type( 2127 name=name, 2128 parent=parent, 2129 size=size, 2130 fullsize=fullsize, 2131 mountpoint=mountpoint, 2132 realpath=realpath, 2133 label=label, 2134 attributes=attributes, 2135 is_root_volume=is_root_volume 2136 ) 2137 ) 2138 2139 if not have_root_volume_setup: 2140 # There must always be a root volume setup. It will be the 2141 # full size volume if no other volume has this setup 2142 volume_management = self.get_volume_management() 2143 root_volume_name = \ 2144 defaults.ROOT_VOLUME_NAME if volume_management == 'lvm' else '' 2145 if have_full_size_volume: 2146 size = 'freespace:' + format( 2147 Defaults.get_min_volume_mbytes(selected_filesystem) 2148 ) 2149 fullsize = False 2150 else: 2151 size = None 2152 fullsize = True 2153 volume_type_list.append( 2154 volume_type( 2155 name=root_volume_name, 2156 parent='', 2157 size=size, 2158 fullsize=fullsize, 2159 mountpoint=None, 2160 realpath='/', 2161 label=None, 2162 attributes=[], 2163 is_root_volume=True 2164 ) 2165 ) 2166 2167 if swap_mbytes and self.get_volume_management() == 'lvm': 2168 volume_type_list.append( 2169 volume_type( 2170 name=swap_name, 2171 parent='', 2172 size='size:{0}'.format(swap_mbytes), 2173 fullsize=False, 2174 mountpoint=None, 2175 realpath='swap', 2176 label='SWAP', 2177 attributes=[], 2178 is_root_volume=False 2179 ) 2180 ) 2181 2182 return volume_type_list 2183 2184 def get_volume_management(self) -> Optional[str]: 2185 """ 2186 Provides information which volume management system is used 2187 2188 :return: name of volume manager 2189 2190 :rtype: str 2191 """ 2192 volume_filesystems = ['btrfs'] 2193 selected_filesystem = self.build_type.get_filesystem() 2194 selected_system_disk = self.get_build_type_system_disk_section() 2195 volume_management = None 2196 if selected_system_disk and selected_system_disk.get_preferlvm(): 2197 # LVM volume management is preferred, use it 2198 volume_management = 'lvm' 2199 elif selected_filesystem in volume_filesystems and selected_system_disk: 2200 # specified filesystem has its own volume management system 2201 volume_management = selected_filesystem 2202 elif selected_system_disk: 2203 # systemdisk section is specified with non volume capable 2204 # filesystem and no volume management preference. So let's 2205 # use LVM by default 2206 volume_management = 'lvm' 2207 return volume_management 2208 2209 def get_drivers_list(self) -> List: 2210 """ 2211 List of driver names from all drivers sections matching 2212 configured profiles 2213 2214 :return: driver names 2215 2216 :rtype: list 2217 """ 2218 drivers_sections = self._profiled( 2219 self.xml_data.get_drivers() 2220 ) 2221 result = [] 2222 if drivers_sections: 2223 for driver in drivers_sections: 2224 for file_section in driver.get_file(): 2225 result.append(file_section.get_name()) 2226 return result 2227 2228 def get_strip_list(self, section_type: str) -> List: 2229 """ 2230 List of strip names matching the given section type 2231 and profiles 2232 2233 :param str section_type: type name from packages section 2234 2235 :return: strip names 2236 2237 :rtype: list 2238 """ 2239 strip_sections = self._profiled( 2240 self.xml_data.get_strip() 2241 ) 2242 result = [] 2243 if strip_sections: 2244 for strip in strip_sections: 2245 if strip.get_type() == section_type: 2246 for file_section in strip.get_file(): 2247 result.append(file_section.get_name()) 2248 return result 2249 2250 def get_strip_files_to_delete(self) -> List: 2251 """ 2252 Items to delete from strip section 2253 2254 :return: item names 2255 2256 :rtype: list 2257 """ 2258 return self.get_strip_list('delete') 2259 2260 def get_strip_tools_to_keep(self) -> List: 2261 """ 2262 Tools to keep from strip section 2263 2264 :return: tool names 2265 2266 :rtype: list 2267 """ 2268 return self.get_strip_list('tools') 2269 2270 def get_strip_libraries_to_keep(self) -> List: 2271 """ 2272 Libraries to keep from strip section 2273 2274 :return: librarie names 2275 2276 :rtype: list 2277 """ 2278 return self.get_strip_list('libs') 2279 2280 def get_include_section_reference_file_names(self) -> List[str]: 2281 """ 2282 List of all <include> section file name references 2283 2284 :return: List[str] 2285 2286 :rtype: list 2287 """ 2288 include_files = [] 2289 for include in self.xml_data.get_include(): 2290 include_files.append(include.get_from()) 2291 return include_files 2292 2293 def get_repository_sections(self) -> List: 2294 """ 2295 List of all repository sections for the selected profiles that 2296 matches the host architecture 2297 2298 :return: <repository> section reference(s) 2299 2300 :rtype: list 2301 """ 2302 repository_list = [] 2303 for repository in self._profiled(self.xml_data.get_repository()): 2304 if self.repository_matches_host_architecture(repository): 2305 repository_list.append(repository) 2306 return repository_list 2307 2308 def get_containers_sections(self) -> List: 2309 """ 2310 List of all containers sections for the selected profiles that 2311 matches the host architecture 2312 2313 :return: <containers> section reference(s) 2314 2315 :rtype: list 2316 """ 2317 containers_list = [] 2318 for containers in self._profiled(self.xml_data.get_containers()): 2319 if self.containers_matches_host_architecture(containers): 2320 containers_list.append(containers) 2321 return containers_list 2322 2323 def get_repository_sections_used_for_build(self) -> List: 2324 """ 2325 List of all repositorys sections used to build the image and 2326 matching configured profiles. 2327 2328 :return: <repository> section reference(s) 2329 2330 :rtype: list 2331 """ 2332 repos = self.get_repository_sections() 2333 return list( 2334 repo for repo in repos if not repo.get_imageonly() 2335 ) 2336 2337 def get_repository_sections_used_in_image(self) -> List: 2338 """ 2339 List of all repositorys sections to be configured in the resulting 2340 image matching configured profiles. 2341 2342 :return: <repository> section reference(s) 2343 2344 :rtype: list 2345 """ 2346 repos = self.get_repository_sections() 2347 return list( 2348 repo for repo in repos 2349 if repo.get_imageinclude() or repo.get_imageonly() 2350 ) 2351 2352 def delete_repository_sections(self) -> None: 2353 """ 2354 Delete all repository sections matching configured profiles 2355 """ 2356 self.xml_data.set_repository([]) 2357 2358 def delete_repository_sections_used_for_build(self) -> None: 2359 """ 2360 Delete all repository sections used to build the image matching 2361 configured profiles 2362 """ 2363 used_for_build = self.get_repository_sections_used_for_build() 2364 all_repos = self.get_repository_sections() 2365 self.xml_data.set_repository( 2366 [ 2367 repo for repo in all_repos if repo not in used_for_build 2368 ] 2369 ) 2370 2371 def get_repositories_signing_keys(self) -> List[str]: 2372 """ 2373 Get list of signing keys specified on the repositories 2374 """ 2375 key_file_list: List[str] = [] 2376 release_version = self.get_release_version() 2377 release_vars = [ 2378 '$releasever', 2379 '${releasever}' 2380 ] 2381 for repository in self.get_repository_sections() or []: 2382 for signing in repository.get_source().get_signing() or []: 2383 normalized_key_url = Uri(signing.get_key()).translate() 2384 if release_version: 2385 for release_var in release_vars: 2386 if release_var in normalized_key_url: 2387 normalized_key_url = normalized_key_url.replace( 2388 release_var, release_version 2389 ) 2390 if normalized_key_url not in key_file_list: 2391 key_file_list.append(normalized_key_url) 2392 return key_file_list 2393 2394 def set_repository( 2395 self, repo_source: str, repo_type: str, repo_alias: str, 2396 repo_prio: str, repo_imageinclude: bool = False, 2397 repo_package_gpgcheck: Optional[bool] = None, 2398 repo_signing_keys: List[str] = [], components: str = None, 2399 distribution: str = None, repo_gpgcheck: Optional[bool] = None, 2400 repo_sourcetype: str = None 2401 ) -> None: 2402 """ 2403 Overwrite repository data of the first repository 2404 2405 :param str repo_source: repository URI 2406 :param str repo_type: type name defined by schema 2407 :param str repo_alias: alias name 2408 :param str repo_prio: priority number, package manager specific 2409 :param bool repo_imageinclude: setup repository inside of the image 2410 :param bool repo_package_gpgcheck: enable/disable package gpg checks 2411 :param list repo_signing_keys: list of signing key file names 2412 :param str components: component names for debian repos 2413 :param str distribution: base distribution name for debian repos 2414 :param bool repo_gpgcheck: enable/disable repo gpg checks 2415 """ 2416 repository_sections = self.get_repository_sections() 2417 if repository_sections: 2418 repository = repository_sections[0] 2419 if repo_alias: 2420 repository.set_alias(repo_alias) 2421 if repo_type: 2422 repository.set_type(repo_type) 2423 if repo_source: 2424 repository.get_source().set_path(repo_source) 2425 if repo_prio: 2426 repository.set_priority(int(repo_prio)) 2427 if repo_imageinclude: 2428 repository.set_imageinclude(repo_imageinclude) 2429 if repo_package_gpgcheck is not None: 2430 repository.set_package_gpgcheck(repo_package_gpgcheck) 2431 if repo_signing_keys: 2432 repository.get_source().set_signing( 2433 [xml_parse.signing(key=k) for k in repo_signing_keys] 2434 ) 2435 if components: 2436 repository.set_components(components) 2437 if distribution: 2438 repository.set_distribution(distribution) 2439 if repo_gpgcheck is not None: 2440 repository.set_repository_gpgcheck(repo_gpgcheck) 2441 if repo_sourcetype: 2442 repository.set_sourcetype(repo_sourcetype) 2443 2444 def add_repository( 2445 self, repo_source: str, repo_type: str, repo_alias: str = None, 2446 repo_prio: str = '', repo_imageinclude: bool = False, 2447 repo_package_gpgcheck: Optional[bool] = None, 2448 repo_signing_keys: List[str] = [], components: str = None, 2449 distribution: str = None, repo_gpgcheck: Optional[bool] = None, 2450 repo_sourcetype: str = None 2451 ) -> None: 2452 """ 2453 Add a new repository section at the end of the list 2454 2455 :param str repo_source: repository URI 2456 :param str repo_type: type name defined by schema 2457 :param str repo_alias: alias name 2458 :param str repo_prio: priority number, package manager specific 2459 :param bool repo_imageinclude: setup repository inside of the image 2460 :param bool repo_package_gpgcheck: enable/disable package gpg checks 2461 :param list repo_signing_keys: list of signing key file names 2462 :param str components: component names for debian repos 2463 :param str distribution: base distribution name for debian repos 2464 :param bool repo_gpgcheck: enable/disable repo gpg checks 2465 """ 2466 priority_number: Optional[int] = None 2467 try: 2468 priority_number = int(repo_prio) 2469 except Exception: 2470 pass 2471 2472 self.xml_data.add_repository( 2473 xml_parse.repository( 2474 type_=repo_type, 2475 alias=repo_alias, 2476 priority=priority_number, 2477 source=xml_parse.source( 2478 path=repo_source, 2479 signing=[ 2480 xml_parse.signing(key=k) for k in repo_signing_keys 2481 ] 2482 ), 2483 imageinclude=repo_imageinclude, 2484 package_gpgcheck=repo_package_gpgcheck, 2485 repository_gpgcheck=repo_gpgcheck, 2486 components=components, 2487 distribution=distribution, 2488 sourcetype=repo_sourcetype 2489 ) 2490 ) 2491 2492 def add_certificate(self, cert_file: str, target_distribution: str) -> None: 2493 """ 2494 Add <certificate name="cert_file"> to main <certificates> section 2495 The main section will be created if it does not exist. Also 2496 setup the target_distribution in the resulting main section. 2497 """ 2498 certificates_section = self._profiled( 2499 self.xml_data.get_certificates() 2500 ) 2501 if not certificates_section: 2502 self.xml_data.set_certificates( 2503 [ 2504 xml_parse.certificates( 2505 target_distribution=target_distribution, 2506 certificate=[xml_parse.certificate(name=cert_file)] 2507 ) 2508 ] 2509 ) 2510 else: 2511 certificates_section[0].set_target_distribution( 2512 target_distribution 2513 ) 2514 certificates_section[0].add_certificate( 2515 xml_parse.certificate( 2516 name=cert_file 2517 ) 2518 ) 2519 2520 def get_certificates(self) -> List[str]: 2521 """ 2522 Read list of certificates 2523 """ 2524 cert_list = [] 2525 certificates_section = self._profiled( 2526 self.xml_data.get_certificates() 2527 ) 2528 if certificates_section: 2529 for certificate in certificates_section[0].get_certificate(): 2530 cert_list.append(certificate.get_name()) 2531 return sorted(list(set(cert_list))) 2532 2533 def get_certificates_target_distribution(self) -> str: 2534 """ 2535 Read CA target distribution 2536 """ 2537 target_distribution = '' 2538 certificates_section = self._profiled( 2539 self.xml_data.get_certificates() 2540 ) 2541 if certificates_section: 2542 target_distribution = \ 2543 certificates_section[0].get_target_distribution() 2544 return target_distribution 2545 2546 def resolve_this_path(self) -> None: 2547 """ 2548 Resolve any this:// repo source path into the path 2549 representing the target inside of the image description 2550 directory 2551 """ 2552 for repository in self.get_repository_sections() or []: 2553 repo_source = repository.get_source() 2554 repo_path = repo_source.get_path() 2555 if repo_path.startswith('this://'): 2556 repo_path = repo_path.replace('this://', '') 2557 repo_source.set_path( 2558 'dir://{0}'.format( 2559 os.path.realpath( 2560 os.path.join( 2561 self.xml_data.description_dir, repo_path 2562 ) 2563 ) 2564 ) 2565 ) 2566 2567 def copy_displayname(self, target_state: Any) -> None: 2568 """ 2569 Copy image displayname from this xml state to the target xml state 2570 2571 :param object target_state: XMLState instance 2572 """ 2573 displayname = self.xml_data.get_displayname() 2574 if displayname: 2575 target_state.xml_data.set_displayname(displayname) 2576 2577 def copy_name(self, target_state: Any) -> None: 2578 """ 2579 Copy image name from this xml state to the target xml state 2580 2581 :param object target_state: XMLState instance 2582 """ 2583 target_state.xml_data.set_name( 2584 self.xml_data.get_name() 2585 ) 2586 2587 def copy_drivers_sections(self, target_state: Any) -> None: 2588 """ 2589 Copy drivers sections from this xml state to the target xml state 2590 2591 :param object target_state: XMLState instance 2592 """ 2593 drivers_sections = self._profiled( 2594 self.xml_data.get_drivers() 2595 ) 2596 if drivers_sections: 2597 for drivers_section in drivers_sections: 2598 target_state.xml_data.add_drivers(drivers_section) 2599 2600 def copy_systemdisk_section(self, target_state: Any) -> None: 2601 """ 2602 Copy systemdisk sections from this xml state to the target xml state 2603 2604 :param object target_state: XMLState instance 2605 """ 2606 systemdisk_section = self.get_build_type_system_disk_section() 2607 if systemdisk_section: 2608 target_state.build_type.set_systemdisk( 2609 [systemdisk_section] 2610 ) 2611 2612 def copy_strip_sections(self, target_state: Any) -> None: 2613 """ 2614 Copy strip sections from this xml state to the target xml state 2615 2616 :param object target_state: XMLState instance 2617 """ 2618 strip_sections = self._profiled( 2619 self.xml_data.get_strip() 2620 ) 2621 if strip_sections: 2622 for strip_section in strip_sections: 2623 target_state.xml_data.add_strip(strip_section) 2624 2625 def copy_machine_section(self, target_state: Any) -> None: 2626 """ 2627 Copy machine sections from this xml state to the target xml state 2628 2629 :param object target_state: XMLState instance 2630 """ 2631 machine_section = self.get_build_type_machine_section() 2632 if machine_section: 2633 target_state.build_type.set_machine( 2634 [machine_section] 2635 ) 2636 2637 def copy_bootloader_section(self, target_state: Any) -> None: 2638 """ 2639 Copy bootloader section from this xml state to the target xml state 2640 2641 :param object target_state: XMLState instance 2642 """ 2643 bootloader_section = self.get_build_type_bootloader_section() 2644 if bootloader_section: 2645 target_state.build_type.set_bootloader( 2646 [bootloader_section] 2647 ) 2648 2649 def copy_oemconfig_section(self, target_state: Any) -> None: 2650 """ 2651 Copy oemconfig sections from this xml state to the target xml state 2652 2653 :param object target_state: XMLState instance 2654 """ 2655 oemconfig_section = self.get_build_type_oemconfig_section() 2656 if oemconfig_section: 2657 target_state.build_type.set_oemconfig( 2658 [oemconfig_section] 2659 ) 2660 2661 def copy_repository_sections( 2662 self, target_state: Any, wipe: bool = False 2663 ) -> None: 2664 """ 2665 Copy repository sections from this xml state to the target xml state 2666 2667 :param object target_state: XMLState instance 2668 :param bool wipe: delete all repos in target prior to copy 2669 """ 2670 repository_sections = self._profiled( 2671 self.xml_data.get_repository() 2672 ) 2673 if repository_sections: 2674 if wipe: 2675 target_state.xml_data.set_repository([]) 2676 for repository_section in repository_sections: 2677 repository_copy = copy.deepcopy(repository_section) 2678 # profiles are not copied because they might not exist 2679 # in the target description 2680 repository_copy.set_profiles(None) 2681 target_state.xml_data.add_repository(repository_copy) 2682 2683 def copy_preferences_subsections( 2684 self, section_names: List, target_state: Any 2685 ) -> None: 2686 """ 2687 Copy subsections of the preferences sections, matching given 2688 section names, from this xml state to the target xml state 2689 2690 :param list section_names: preferences subsection names 2691 :param object target_state: XMLState instance 2692 """ 2693 target_preferences_sections = target_state.get_preferences_sections() 2694 if target_preferences_sections: 2695 target_preferences_section = target_preferences_sections[0] 2696 for preferences_section in self.get_preferences_sections(): 2697 for section_name in section_names: 2698 get_section_method = getattr( 2699 preferences_section, 'get_' + section_name 2700 ) 2701 section = get_section_method() 2702 if section: 2703 set_section_method = getattr( 2704 target_preferences_section, 'set_' + section_name 2705 ) 2706 set_section_method(section) 2707 2708 def copy_build_type_attributes( 2709 self, attribute_names: List, target_state: Any 2710 ) -> None: 2711 """ 2712 Copy specified attributes from this build type section to the 2713 target xml state build type section 2714 2715 :param list attribute_names: type section attributes 2716 :param object target_state: XMLState instance 2717 """ 2718 for attribute in attribute_names: 2719 get_type_method = getattr( 2720 self.build_type, 'get_' + attribute 2721 ) 2722 attribute_value = get_type_method() 2723 if attribute_value: 2724 set_type_method = getattr( 2725 target_state.build_type, 'set_' + attribute 2726 ) 2727 set_type_method(attribute_value) 2728 2729 def copy_bootincluded_packages(self, target_state: Any) -> None: 2730 """ 2731 Copy packages marked as bootinclude to the packages 2732 type=bootstrap section in the target xml state. The package 2733 will also be removed from the packages type=delete section 2734 in the target xml state if present there 2735 2736 :param object target_state: XMLState instance 2737 """ 2738 target_packages_sections = \ 2739 target_state.get_bootstrap_packages_sections() 2740 if target_packages_sections: 2741 target_packages_section = \ 2742 target_packages_sections[0] 2743 package_names_added = [] 2744 packages_sections = self.get_packages_sections( 2745 ['image', 'bootstrap', self.get_build_type_name()] 2746 ) 2747 package_list = self.get_package_sections( 2748 packages_sections 2749 ) 2750 if package_list: 2751 for package in package_list: 2752 if package.package_section.get_bootinclude(): 2753 target_packages_section.add_package( 2754 xml_parse.package( 2755 name=package.package_section.get_name() 2756 ) 2757 ) 2758 package_names_added.append( 2759 package.package_section.get_name() 2760 ) 2761 delete_packages_sections = target_state.get_packages_sections( 2762 ['delete'] 2763 ) 2764 package_list = self.get_package_sections( 2765 delete_packages_sections 2766 ) 2767 if package_list: 2768 for package in package_list: 2769 package_name = package.package_section.get_name() 2770 if package_name in package_names_added: 2771 package.packages_section.package.remove( 2772 package.package_section 2773 ) 2774 2775 def copy_bootincluded_archives(self, target_state: Any) -> None: 2776 """ 2777 Copy archives marked as bootinclude to the packages type=bootstrap 2778 section in the target xml state 2779 2780 :param object target_state: XMLState instance 2781 """ 2782 target_bootstrap_packages_sections = \ 2783 target_state.get_bootstrap_packages_sections() 2784 if target_bootstrap_packages_sections: 2785 target_bootstrap_packages_section = \ 2786 target_bootstrap_packages_sections[0] 2787 packages_sections = self.get_packages_sections( 2788 ['image', 'bootstrap', self.get_build_type_name()] 2789 ) 2790 for packages_section in packages_sections: 2791 archive_list = packages_section.get_archive() 2792 if archive_list: 2793 for archive in archive_list: 2794 if archive.get_bootinclude(): 2795 target_bootstrap_packages_section.add_archive( 2796 xml_parse.archive( 2797 name=archive.get_name() 2798 ) 2799 ) 2800 2801 def copy_bootdelete_packages(self, target_state: Any) -> None: 2802 """ 2803 Copy packages marked as bootdelete to the packages type=delete 2804 section in the target xml state 2805 2806 :param object target_state: XMLState instance 2807 """ 2808 target_delete_packages_sections = target_state.get_packages_sections( 2809 ['delete'] 2810 ) 2811 if not target_delete_packages_sections: 2812 target_delete_packages_sections = [ 2813 xml_parse.packages(type_='delete') 2814 ] 2815 target_state.xml_data.add_packages( 2816 target_delete_packages_sections[0] 2817 ) 2818 2819 target_delete_packages_section = \ 2820 target_delete_packages_sections[0] 2821 packages_sections = self.get_packages_sections( 2822 ['image', 'bootstrap', self.get_build_type_name()] 2823 ) 2824 package_list = self.get_package_sections( 2825 packages_sections 2826 ) 2827 if package_list: 2828 for package in package_list: 2829 if package.package_section.get_bootdelete(): 2830 target_delete_packages_section.add_package( 2831 xml_parse.package( 2832 name=package.package_section.get_name() 2833 ) 2834 ) 2835 2836 def get_distribution_name_from_boot_attribute(self) -> str: 2837 """ 2838 Extract the distribution name from the boot attribute of the 2839 build type section. 2840 2841 If no boot attribute is configured or the contents does not 2842 match the kiwi defined naming schema for boot image descriptions, 2843 an exception is thrown 2844 2845 :return: lowercase distribution name 2846 2847 :rtype: str 2848 """ 2849 boot_attribute = self.build_type.get_boot() 2850 if not boot_attribute: 2851 raise KiwiDistributionNameError( 2852 'No boot attribute to extract distribution name from found' 2853 ) 2854 boot_attribute_format = '^.*-(.*)$' 2855 boot_attribute_expression = re.match( 2856 boot_attribute_format, boot_attribute 2857 ) 2858 if not boot_attribute_expression: 2859 raise KiwiDistributionNameError( 2860 'Boot attribute "%s" does not match expected format %s' % 2861 (boot_attribute, boot_attribute_format) 2862 ) 2863 return boot_attribute_expression.group(1).lower() 2864 2865 def get_fs_mount_option_list(self) -> List: 2866 """ 2867 List of root filesystem mount options 2868 2869 The list contains one element with the information from the 2870 fsmountoptions attribute. The value there is passed along to 2871 the -o mount option 2872 2873 :return: max one element list with mount option string 2874 2875 :rtype: list 2876 """ 2877 option_list = [] 2878 mount_options = self.build_type.get_fsmountoptions() 2879 if mount_options: 2880 option_list = [mount_options] 2881 2882 return option_list 2883 2884 def get_fs_create_option_list(self) -> List: 2885 """ 2886 List of root filesystem creation options 2887 2888 The list contains elements with the information from the 2889 fscreateoptions attribute string that got split into its 2890 substring components 2891 2892 :return: list with create options 2893 2894 :rtype: list 2895 """ 2896 option_list = [] 2897 create_options = self.build_type.get_fscreateoptions() 2898 if create_options: 2899 option_list = create_options.split() 2900 2901 return option_list 2902 2903 def get_luks_credentials(self) -> Optional[str]: 2904 """ 2905 Return key or passphrase credentials to open the luks pool 2906 2907 :return: data 2908 2909 :rtype: str 2910 """ 2911 data = self.build_type.get_luks() 2912 if data: 2913 keyfile_name = None 2914 try: 2915 # try to interpret data as an URI 2916 uri = Uri(data) 2917 if not uri.is_remote(): 2918 keyfile_name = uri.translate() 2919 except Exception: 2920 # this doesn't look like a valid URI, continue as just data 2921 pass 2922 if keyfile_name: 2923 try: 2924 with open(keyfile_name) as keyfile: 2925 return keyfile.read() 2926 except Exception as issue: 2927 raise KiwiFileAccessError( 2928 f'Failed to read from {keyfile_name!r}: {issue}' 2929 ) 2930 return data 2931 2932 def get_luks_format_options(self) -> List[str]: 2933 """ 2934 Return list of luks format options 2935 2936 :return: list of options 2937 2938 :rtype: list 2939 """ 2940 result = [] 2941 luksversion = self.build_type.get_luks_version() 2942 luksformat = self.build_type.get_luksformat() 2943 luks_pbkdf = self.build_type.get_luks_pbkdf() 2944 if luksversion: 2945 result.append('--type') 2946 result.append(luksversion) 2947 if luksformat: 2948 for option in luksformat[0].get_option(): 2949 result.append(option.get_name()) 2950 if option.get_value(): 2951 result.append(option.get_value()) 2952 if luks_pbkdf: 2953 # Allow to override the pbkdf algorithm that cryptsetup 2954 # uses by default. Cryptsetup may use argon2i by default, 2955 # which is not supported by all bootloaders. 2956 result.append('--pbkdf') 2957 result.append(luks_pbkdf) 2958 return result 2959 2960 def get_derived_from_image_uri(self) -> List[Uri]: 2961 """ 2962 Uri object(s) of derived image if configured 2963 2964 Specific image types can be based on one ore more derived 2965 images. This method returns the location of this image(s) 2966 when configured in the XML description 2967 2968 :return: List of Uri instances 2969 2970 :rtype: list 2971 """ 2972 image_uris = [] 2973 derived_images = self.build_type.get_derived_from() 2974 if derived_images: 2975 for derived_image in derived_images.split(','): 2976 image_uris.append( 2977 Uri(derived_image, repo_type='container') 2978 ) 2979 return image_uris 2980 2981 def set_derived_from_image_uri(self, uri: str) -> None: 2982 """ 2983 Set derived_from attribute to a new value 2984 2985 In order to set a new value the derived_from attribute 2986 must be already present in the image configuration 2987 2988 :param str uri: URI 2989 """ 2990 if self.build_type.get_derived_from(): 2991 self.build_type.set_derived_from(uri) 2992 else: 2993 message = dedent('''\n 2994 No derived_from attribute configured in image <type> 2995 2996 In order to set the uri {0} as base container reference 2997 an initial derived_from attribute must be set in the 2998 type section 2999 ''') 3000 log.warning(message.format(uri)) 3001 3002 def set_root_partition_uuid(self, uuid: str) -> None: 3003 """ 3004 Store PARTUUID provided in uuid as state information 3005 3006 :param str uuid: PARTUUID 3007 """ 3008 self.root_partition_uuid = uuid 3009 3010 def get_root_partition_uuid(self) -> Optional[str]: 3011 """ 3012 Return preserved PARTUUID 3013 """ 3014 return self.root_partition_uuid 3015 3016 def set_root_filesystem_uuid(self, uuid: str) -> None: 3017 """ 3018 Store UUID provided in uuid as state information 3019 3020 :param str uuid: UUID 3021 """ 3022 self.root_filesystem_uuid = uuid 3023 3024 def get_root_filesystem_uuid(self) -> Optional[str]: 3025 """ 3026 Return preserved UUID 3027 """ 3028 return self.root_filesystem_uuid 3029 3030 @staticmethod 3031 def get_archives_target_dirs( 3032 packages_sections_names: Optional[List[xml_parse.packages]] 3033 ) -> Dict: 3034 """ 3035 Dict of archive names and target dirs for packages section(s), if any 3036 :return: archive names and its target dir 3037 :rtype: dict 3038 """ 3039 result = {} 3040 if packages_sections_names: 3041 for package_section_name in packages_sections_names: 3042 for archive in package_section_name.get_archive(): 3043 result[archive.get_name().strip()] = archive.get_target_dir() 3044 3045 return result 3046 3047 def get_bootstrap_archives_target_dirs(self) -> Dict: 3048 """ 3049 Dict of archive names and target dirs from the type="bootstrap" 3050 packages section(s) 3051 :return: archive names and its target dir 3052 :rtype: dict 3053 """ 3054 return self.get_archives_target_dirs( 3055 self.get_packages_sections(['bootstrap']) 3056 ) 3057 3058 def get_system_archives_target_dirs(self) -> Dict: 3059 """ 3060 Dict of archive names and its target dir from the packages sections matching 3061 type="image" and type=build_type 3062 :return: archive names and its target dir 3063 :rtype: dict 3064 """ 3065 return self.get_archives_target_dirs( 3066 self.get_packages_sections(['image', self.get_build_type_name()]) 3067 ) 3068 3069 def _used_profiles(self, profiles=None): 3070 """ 3071 return list of profiles to use. The method looks up the 3072 profiles section in the XML description and searches for 3073 profiles matching the architecture. If no arch specifier 3074 is set the profile is considered to be valid for any arch 3075 3076 If the profiles argument is not set only profiles 3077 marked with the attribute import=true will be selected. 3078 Profiles specified in the argument will take the highest 3079 priority and causes to skip the lookup of import profiles 3080 in the XML description 3081 3082 :param list profiles: selected profile names 3083 """ 3084 available_profiles = dict() 3085 import_profiles = [] 3086 for profiles_section in self.xml_data.get_profiles(): 3087 for profile in profiles_section.get_profile(): 3088 if self.profile_matches_host_architecture(profile): 3089 name = profile.get_name() 3090 available_profiles[name] = profile 3091 if profile.get_import(): 3092 import_profiles.append(name) 3093 3094 if not profiles: 3095 return import_profiles 3096 else: 3097 resolved_profiles = [] 3098 for profile in profiles: 3099 resolved_profiles += self._solve_profile_dependencies( 3100 profile, available_profiles, resolved_profiles 3101 ) 3102 return resolved_profiles 3103 3104 def _section_matches_host_architecture(self, section): 3105 architectures = section.get_arch() 3106 if architectures: 3107 if self.host_architecture not in architectures.split(','): 3108 return False 3109 return True 3110 3111 def _match_docker_base_data(self): 3112 container_config_section = self.get_build_type_containerconfig_section() 3113 container_base = {} 3114 if container_config_section: 3115 name = container_config_section.get_name() 3116 tag = container_config_section.get_tag() 3117 maintainer = container_config_section.get_maintainer() 3118 user = container_config_section.get_user() 3119 workingdir = container_config_section.get_workingdir() 3120 additional_names = container_config_section.get_additionalnames() 3121 if name: 3122 container_base['container_name'] = name 3123 3124 if tag: 3125 container_base['container_tag'] = tag 3126 3127 if additional_names: 3128 container_base['additional_names'] = additional_names.split(',') 3129 3130 if maintainer: 3131 container_base['maintainer'] = maintainer 3132 3133 if user: 3134 container_base['user'] = user 3135 3136 if workingdir: 3137 container_base['workingdir'] = workingdir 3138 3139 return container_base 3140 3141 def _match_docker_entrypoint(self): 3142 container_config_section = self.get_build_type_containerconfig_section() 3143 container_entry = {} 3144 if container_config_section: 3145 entrypoint = container_config_section.get_entrypoint() 3146 if entrypoint and entrypoint[0].get_execute(): 3147 container_entry['entry_command'] = [ 3148 entrypoint[0].get_execute() 3149 ] 3150 argument_list = entrypoint[0].get_argument() 3151 if argument_list: 3152 for argument in argument_list: 3153 container_entry['entry_command'].append( 3154 argument.get_name() 3155 ) 3156 elif entrypoint and entrypoint[0].get_clear(): 3157 container_entry['entry_command'] = [] 3158 return container_entry 3159 3160 def _match_docker_subcommand(self): 3161 container_config_section = self.get_build_type_containerconfig_section() 3162 container_subcommand = {} 3163 if container_config_section: 3164 subcommand = container_config_section.get_subcommand() 3165 if subcommand and subcommand[0].get_execute(): 3166 container_subcommand['entry_subcommand'] = [ 3167 subcommand[0].get_execute() 3168 ] 3169 argument_list = subcommand[0].get_argument() 3170 if argument_list: 3171 for argument in argument_list: 3172 container_subcommand['entry_subcommand'].append( 3173 argument.get_name() 3174 ) 3175 elif subcommand and subcommand[0].get_clear(): 3176 container_subcommand['entry_subcommand'] = [] 3177 return container_subcommand 3178 3179 def _match_docker_expose_ports(self): 3180 container_config_section = self.get_build_type_containerconfig_section() 3181 container_expose = {} 3182 if container_config_section: 3183 expose = container_config_section.get_expose() 3184 if expose and expose[0].get_port(): 3185 container_expose['expose_ports'] = [] 3186 for port in expose[0].get_port(): 3187 container_expose['expose_ports'].append( 3188 format(port.get_number()) 3189 ) 3190 return container_expose 3191 3192 def _match_docker_volumes(self): 3193 container_config_section = self.get_build_type_containerconfig_section() 3194 container_volumes = {} 3195 if container_config_section: 3196 volumes = container_config_section.get_volumes() 3197 if volumes and volumes[0].get_volume(): 3198 container_volumes['volumes'] = [] 3199 for volume in volumes[0].get_volume(): 3200 container_volumes['volumes'].append(volume.get_name()) 3201 return container_volumes 3202 3203 def _match_docker_stopsignal(self) -> dict: 3204 container_config_section = self.get_build_type_containerconfig_section() 3205 container_stopsignal = {} 3206 if container_config_section: 3207 stopsignal_section = container_config_section.get_stopsignal() 3208 if stopsignal_section: 3209 container_stopsignal['stopsignal'] = stopsignal_section[0] 3210 return container_stopsignal 3211 3212 def _match_docker_environment(self): 3213 container_config_section = self.get_build_type_containerconfig_section() 3214 container_env = {} 3215 if container_config_section: 3216 environment = container_config_section.get_environment() 3217 if environment and environment[0].get_env(): 3218 container_env['environment'] = {} 3219 for env in environment[0].get_env(): 3220 container_env['environment'][env.get_name()] = \ 3221 env.get_value() 3222 return container_env 3223 3224 def _match_docker_labels(self): 3225 container_config_section = self.get_build_type_containerconfig_section() 3226 container_labels = {} 3227 if container_config_section: 3228 labels = container_config_section.get_labels() 3229 if labels and labels[0].get_label(): 3230 container_labels['labels'] = {} 3231 for label in labels[0].get_label(): 3232 container_labels['labels'][label.get_name()] = \ 3233 label.get_value() 3234 return container_labels 3235 3236 def _match_docker_history(self): 3237 container_config_section = self.get_build_type_containerconfig_section() 3238 container_history = {} 3239 if container_config_section: 3240 history = container_config_section.get_history() 3241 if history: 3242 container_history['history'] = {} 3243 if history[0].get_created_by(): 3244 container_history['history']['created_by'] = \ 3245 history[0].get_created_by() 3246 if history[0].get_author(): 3247 container_history['history']['author'] = \ 3248 history[0].get_author() 3249 if history[0].get_launcher(): 3250 container_history['history']['launcher'] = \ 3251 history[0].get_launcher() 3252 if history[0].get_application_id(): 3253 container_history['history']['application_id'] = \ 3254 history[0].get_application_id() 3255 if history[0].get_package_version(): 3256 container_history['history']['package_version'] = \ 3257 history[0].get_package_version() 3258 container_history['history']['comment'] = \ 3259 history[0].get_valueOf_() 3260 return container_history 3261 3262 def _solve_profile_dependencies( 3263 self, profile, available_profiles, current_profiles 3264 ): 3265 if profile not in available_profiles: 3266 raise KiwiProfileNotFound( 3267 'profile {0} not found for host arch {1}'.format( 3268 profile, self.host_architecture 3269 ) 3270 ) 3271 profiles_to_add = [] 3272 if profile not in current_profiles: 3273 profiles_to_add.append(profile) 3274 for required in available_profiles[profile].get_requires(): 3275 if self.requires_matches_host_architecture(required): 3276 if required.get_profile() not in current_profiles: 3277 profiles_to_add += self._solve_profile_dependencies( 3278 required.get_profile(), available_profiles, 3279 current_profiles + profiles_to_add 3280 ) 3281 return profiles_to_add 3282 3283 def _build_type_section(self, build_type=None): 3284 """ 3285 find type section matching build type and profiles or default 3286 """ 3287 # lookup all preferences sections for selected profiles 3288 image_type_sections = [] 3289 for preferences in self.get_preferences_sections(): 3290 image_type_sections += preferences.get_type() 3291 3292 # lookup if build type matches provided type 3293 if build_type: 3294 for image_type in image_type_sections: 3295 if build_type == image_type.get_image(): 3296 return image_type 3297 raise KiwiTypeNotFound( 3298 'Build type {0!r} not found for applied profiles: {1!r}'.format( 3299 build_type, self.profiles 3300 ) 3301 ) 3302 3303 # lookup if build type matches primary type 3304 for image_type in image_type_sections: 3305 if image_type.get_primary(): 3306 return image_type 3307 3308 # build type is first type section in XML sequence 3309 if image_type_sections: 3310 return image_type_sections[0] 3311 raise KiwiTypeNotFound( 3312 'No build type defined with applied profiles: {0!r}'.format( 3313 self.profiles 3314 ) 3315 ) 3316 3317 def _profiled(self, xml_abstract): 3318 """ 3319 return only those sections matching the instance stored 3320 profile list from the given XML abstract. Sections without 3321 a profile are wildcard sections and will be used in any 3322 case 3323 """ 3324 result = [] 3325 for section in xml_abstract: 3326 profiles = section.get_profiles() 3327 if profiles: 3328 for profile in profiles.split(','): 3329 if self.profiles and profile in self.profiles: 3330 result.append(section) 3331 break 3332 else: 3333 result.append(section) 3334 return result 3335 3336 def _to_volume_name(self, name): 3337 name = name.strip() 3338 name = re.sub(r'^\/+', r'', name) 3339 name = name.replace('/', '_') 3340 return name 3341 3342 def _to_mega_byte(self, size): 3343 value = re.search(r'(\d+)([MG]*)', format(size)) 3344 if value: 3345 number = value.group(1) 3346 unit = value.group(2) 3347 if unit == 'G': 3348 return int(number) * 1024 3349 else: 3350 return int(number) 3351 else: 3352 return size
description_type(author, contact, specification)
package_type(packages_section, package_section)
size_type(mbytes, additive)
volume_type(name, parent, size, realpath, mountpoint, fullsize, label, attributes, is_root_volume)
DracutT(uefi, modules, drivers)
FileT(target, owner, permissions)
96class ContainerT(NamedTuple): 97 name: str 98 backend: str 99 container_file: str 100 fetch_only: bool 101 fetch_command: Callable 102 load_command: List[str]
ContainerT(name, backend, container_file, fetch_only, fetch_command, load_command)
105class XMLState: 106 """ 107 **Implements methods to get stateful information from the XML data** 108 109 :param object xml_data: parse result from XMLDescription.load() 110 :param list profiles: list of used profiles 111 :param object build_type: build <type> section reference 112 """ 113 def __init__( 114 self, xml_data: Any, profiles: List = None, 115 build_type: Any = None 116 ): 117 self.root_partition_uuid: Optional[str] = None 118 self.root_filesystem_uuid: Optional[str] = None 119 self.host_architecture = defaults.PLATFORM_MACHINE 120 self.xml_data = xml_data 121 self.profiles = self._used_profiles(profiles) 122 self.build_type = self._build_type_section( 123 build_type 124 ) 125 self.resolve_this_path() 126 127 def get_preferences_sections(self) -> List: 128 """ 129 All preferences sections for the selected profiles that match the 130 host architecture 131 132 :return: list of <preferences> section reference(s) 133 134 :rtype: list 135 """ 136 preferences_list = [] 137 for preferences in self._profiled(self.xml_data.get_preferences()): 138 if self.preferences_matches_host_architecture(preferences): 139 preferences_list.append(preferences) 140 return preferences_list 141 142 def get_description_section(self) -> description_type: 143 """ 144 The description section 145 146 :return: 147 description_type tuple providing the elements 148 author contact and specification 149 150 :rtype: tuple 151 """ 152 description = self.xml_data.get_description()[0] 153 return description_type( 154 author=description.get_author()[0], 155 contact=description.get_contact()[0], 156 specification=description.get_specification()[0].strip() 157 ) 158 159 def get_users_sections(self) -> List: 160 """ 161 All users sections for the selected profiles 162 163 :return: list of <users> section reference(s) 164 165 :rtype: list 166 """ 167 users = [] 168 for users_section in self._profiled(self.xml_data.get_users()): 169 if self.users_matches_host_architecture(users_section): 170 users.append(users_section) 171 return users 172 173 def get_build_type_bundle_format(self) -> str: 174 """ 175 Return bundle_format for build type 176 177 The bundle_format string is validated against the available 178 name tags from kiwi.system.result::result_name_tags. 179 180 :return: bundle format string 181 182 :rtype: str 183 """ 184 return self.build_type.get_bundle_format() 185 186 def get_build_type_name(self) -> str: 187 """ 188 Default build type name 189 190 :return: Content of image attribute from build type 191 192 :rtype: str 193 """ 194 return self.build_type.get_image() 195 196 def btrfs_default_volume_requested(self) -> bool: 197 """ 198 Check if setting a default volume for btrfs is requested 199 """ 200 if self.build_type.get_btrfs_set_default_volume() is False: 201 # Setting a default volume is explicitly switched off 202 return False 203 else: 204 # In any other case (True | None) a default volume 205 # is wanted and will be set 206 return True 207 208 def get_image_version(self) -> str: 209 """ 210 Image version from preferences section. 211 212 Multiple occurences of version in preferences sections are not 213 forbidden, however only the first version found defines the 214 final image version 215 216 :return: Content of <version> section 217 218 :rtype: str 219 """ 220 for preferences in self.get_preferences_sections(): 221 version = preferences.get_version() 222 if version: 223 return version[0] 224 return '' 225 226 def get_initrd_system(self) -> str: 227 """ 228 Name of initrd system to use 229 230 Depending on the image type a specific initrd system is 231 either pre selected or free of choice according to the 232 XML type setup. 233 234 :return: 'dracut', 'kiwi' or 'none' 235 236 :rtype: str 237 """ 238 pre_selection_map = { 239 'vmx': 'dracut', 240 'oem': 'dracut', 241 'iso': 'dracut', 242 'kis': 'dracut', 243 'pxe': 'kiwi', 244 } 245 build_type = self.get_build_type_name() 246 default_initrd_system = pre_selection_map.get(build_type) or 'none' 247 248 if build_type == 'iso': 249 # iso type always use dracut as initrd system 250 return default_initrd_system 251 252 # Allow to choose for any other build type 253 return self.build_type.get_initrd_system() or default_initrd_system 254 255 def get_locale(self) -> Optional[List]: 256 """ 257 Gets list of locale names if configured. Takes 258 the first locale setup from the existing preferences 259 sections into account. 260 261 :return: List of names or None 262 263 :rtype: list|None 264 """ 265 for preferences in self.get_preferences_sections(): 266 locale_section = preferences.get_locale() 267 if locale_section: 268 return locale_section[0].split(',') 269 return None 270 271 def get_rpm_locale(self) -> Optional[List]: 272 """ 273 Gets list of locale names to filter out by rpm 274 if rpm-locale-filtering is switched on the 275 the list always contains: [POSIX, C, C.UTF-8] 276 and is extended by the optionaly configured 277 locale 278 279 :return: List of names or None 280 281 :rtype: list|None 282 """ 283 if self.get_rpm_locale_filtering(): 284 rpm_locale = ['POSIX', 'C', 'C.UTF-8'] 285 configured_locale = self.get_locale() 286 if configured_locale: 287 for locale in configured_locale: 288 rpm_locale.append(locale) 289 return rpm_locale 290 return None 291 292 def get_rpm_locale_filtering(self) -> bool: 293 """ 294 Gets the rpm-locale-filtering configuration flag. Returns 295 False if not present. 296 297 :return: True or False 298 299 :rtype: bool 300 """ 301 for preferences in self.get_preferences_sections(): 302 locale_filtering = preferences.get_rpm_locale_filtering() 303 if locale_filtering: 304 return locale_filtering[0] 305 return False 306 307 def get_rpm_excludedocs(self) -> bool: 308 """ 309 Gets the rpm-excludedocs configuration flag. Returns 310 False if not present. 311 312 :return: True or False 313 314 :rtype: bool 315 """ 316 for preferences in self.get_preferences_sections(): 317 exclude_docs = preferences.get_rpm_excludedocs() 318 if exclude_docs: 319 return exclude_docs[0] 320 return False 321 322 def get_rpm_check_signatures(self) -> bool: 323 """ 324 Gets the rpm-check-signatures configuration flag. Returns 325 False if not present. 326 327 :return: True or False 328 329 :rtype: bool 330 """ 331 for preferences in self.get_preferences_sections(): 332 check_signatures = preferences.get_rpm_check_signatures() 333 if check_signatures: 334 return check_signatures[0] 335 return False 336 337 def get_package_manager(self) -> str: 338 """ 339 Get configured package manager from selected preferences section 340 341 :return: Content of the <packagemanager> section 342 343 :rtype: str 344 """ 345 for preferences in self.get_preferences_sections(): 346 package_manager = preferences.get_packagemanager() 347 if package_manager: 348 return package_manager[0] 349 return Defaults.get_default_package_manager() 350 351 def get_release_version(self) -> str: 352 """ 353 Get configured release version from selected preferences section 354 355 :return: Content of the <release-version> section or '' 356 357 :rtype: str 358 """ 359 release_version = '' 360 for preferences in self.get_preferences_sections(): 361 release_version = preferences.get_release_version() 362 if release_version: 363 release_version = release_version[0] 364 break 365 return release_version 366 367 def get_packages_sections(self, section_types: List) -> List: 368 """ 369 List of packages sections matching given section type(s) 370 371 :param list section_types: type name(s) from packages sections 372 373 :return: list of <packages> section reference(s) 374 375 :rtype: list 376 """ 377 result = [] 378 packages_sections = self._profiled( 379 self.xml_data.get_packages() 380 ) 381 for packages in packages_sections: 382 packages_type = packages.get_type() 383 if packages_type in section_types: 384 result.append(packages) 385 return result 386 387 def requires_matches_host_architecture(self, requires: Any) -> bool: 388 """ 389 Tests if the given profile requires section is applicable for 390 the current host architecture. If no architecture is specified 391 within the section it is considered as a match returning True. 392 393 Note: The XML section pointer must provide an arch attribute 394 395 :param section: XML section object 396 397 :return: True or False 398 399 :rtype: bool 400 """ 401 return self._section_matches_host_architecture(requires) 402 403 def volume_matches_host_architecture(self, volume: Any) -> bool: 404 """ 405 Tests if the given volume section is applicable for the current host 406 architecture. If no architecture is specified within the section 407 it is considered as a match returning True. 408 409 Note: The XML section pointer must provide an arch attribute 410 411 :param section: XML section object 412 413 :return: True or False 414 415 :rtype: bool 416 """ 417 return self._section_matches_host_architecture(volume) 418 419 def package_matches_host_architecture(self, package: Any) -> bool: 420 """ 421 Tests if the given package section is applicable for the current host 422 architecture. If no architecture is specified within the section 423 it is considered as a match returning True. 424 425 Note: The XML section pointer must provide an arch attribute 426 427 :param section: XML section object 428 429 :return: True or False 430 431 :rtype: bool 432 """ 433 return self._section_matches_host_architecture(package) 434 435 def users_matches_host_architecture(self, users: Any) -> bool: 436 """ 437 Tests if the given users section is applicable for the current host 438 architecture. If no architecture is specified within the section 439 it is considered as a match returning True. 440 441 Note: The XML section pointer must provide an arch attribute 442 443 :param section: XML section object 444 445 :return: True or False 446 447 :rtype: bool 448 """ 449 return self._section_matches_host_architecture(users) 450 451 def collection_matches_host_architecture(self, collection: Any) -> bool: 452 """ 453 Tests if the given namedcollection section is applicable for 454 the current host architecture. If no architecture is specified 455 within the section it is considered as a match returning True. 456 457 Note: The XML section pointer must provide an arch attribute 458 459 :param section: XML section object 460 461 :return: True or False 462 463 :rtype: bool 464 """ 465 return self._section_matches_host_architecture(collection) 466 467 def profile_matches_host_architecture(self, profile: Any) -> bool: 468 """ 469 Tests if the given profile section is applicable for the current host 470 architecture. If no architecture is specified within the section 471 it is considered as a match returning True. 472 473 Note: The XML section pointer must provide an arch attribute 474 475 :param section: XML section object 476 477 :return: True or False 478 479 :rtype: bool 480 """ 481 return self._section_matches_host_architecture(profile) 482 483 def preferences_matches_host_architecture(self, preferences: Any) -> bool: 484 """ 485 Tests if the given preferences section is applicable for the 486 current host architecture. If no architecture is specified within 487 the section it is considered as a match returning True. 488 489 Note: The XML section pointer must provide an arch attribute 490 491 :param section: XML section object 492 493 :return: True or False 494 495 :rtype: bool 496 """ 497 return self._section_matches_host_architecture(preferences) 498 499 def repository_matches_host_architecture(self, repository: Any) -> bool: 500 """ 501 Tests if the given repository section is applicable for the 502 current host architecture. If no architecture is specified within 503 the section it is considered as a match returning True. 504 505 Note: The XML section pointer must provide an arch attribute 506 507 :param section: XML section object 508 509 :return: True or False 510 511 :rtype: bool 512 """ 513 return self._section_matches_host_architecture(repository) 514 515 def containers_matches_host_architecture(self, containers: Any) -> bool: 516 """ 517 Tests if the given containers section is applicable for the 518 current host architecture. If no arch attribute is provided in 519 the section it is considered as a match and returns: True. 520 521 :param section: XML section object 522 523 :return: True or False 524 525 :rtype: bool 526 """ 527 return self._section_matches_host_architecture(containers) 528 529 def container_matches_host_architecture(self, container: Any) -> bool: 530 """ 531 Tests if the given container section is applicable for the 532 current host architecture. If no arch attribute is provided in 533 the section it is considered as a match and returns: True. 534 535 :param section: XML section object 536 537 :return: True or False 538 539 :rtype: bool 540 """ 541 return self._section_matches_host_architecture(container) 542 543 def get_package_sections( 544 self, packages_sections: List 545 ) -> List[package_type]: 546 """ 547 List of package sections from the given packages sections. 548 Each list element contains a tuple with the <package> section 549 reference and the <packages> section this package belongs to 550 551 If a package entry specfies an architecture, it is only taken if 552 the host architecture matches the configured architecture 553 554 :param list packages_sections: <packages> 555 556 :return: 557 Contains list of package_type tuples 558 559 .. code:: python 560 561 [package_type(packages_section=object, package_section=object)] 562 563 :rtype: list 564 """ 565 result = [] 566 if packages_sections: 567 for packages_section in packages_sections: 568 package_list = packages_section.get_package() 569 if package_list: 570 for package in package_list: 571 if self.package_matches_host_architecture(package): 572 result.append( 573 package_type( 574 packages_section=packages_section, 575 package_section=package 576 ) 577 ) 578 return result 579 580 def get_to_become_deleted_packages(self, force: bool = True) -> List: 581 """ 582 List of package names from the type="delete" or type="uninstall" 583 packages section(s) 584 585 :param bool force: return "delete" type if True, "uninstall" type 586 otherwise 587 588 :return: package names 589 590 :rtype: list 591 """ 592 result = [] 593 to_become_deleted_packages_sections = self.get_packages_sections( 594 ['delete' if force else 'uninstall'] 595 ) 596 package_list = self.get_package_sections( 597 to_become_deleted_packages_sections 598 ) 599 if package_list: 600 for package in package_list: 601 result.append(package.package_section.get_name()) 602 return sorted(list(set(result))) 603 604 def get_bootstrap_packages_sections(self) -> List: 605 """ 606 List of packages sections matching type="bootstrap" 607 608 :return: list of <packages> section reference(s) 609 610 :rtype: list 611 """ 612 return self.get_packages_sections(['bootstrap']) 613 614 def get_image_packages_sections(self) -> List: 615 """ 616 List of packages sections matching type="image" 617 618 :return: list of <packages> section reference(s) 619 620 :rtype: list 621 """ 622 return self.get_packages_sections(['image']) 623 624 def get_bootstrap_packages(self, plus_packages: List = None) -> List: 625 """ 626 List of package names from the type="bootstrap" packages section(s) 627 628 The list gets the selected package manager appended 629 if there is a request to install packages inside of 630 the image via a chroot operation 631 632 :param list plus_packages: list of additional packages 633 634 :return: package names 635 636 :rtype: list 637 """ 638 result = [] 639 bootstrap_packages_sections = self.get_bootstrap_packages_sections() 640 package_list = self.get_package_sections( 641 bootstrap_packages_sections 642 ) 643 if package_list: 644 for package in package_list: 645 result.append(package.package_section.get_name().strip()) 646 if self.get_system_packages(): 647 package_manager_name = self.get_package_manager() 648 if package_manager_name == 'dnf4': 649 # The package name for dnf4 is just dnf. Thus 650 # the name must be adapted in this case 651 package_manager_name = 'dnf' 652 elif package_manager_name == 'apk': 653 package_manager_name = 'apk-tools' 654 result.append(package_manager_name) 655 if plus_packages: 656 result += plus_packages 657 return sorted(list(set(result))) 658 659 def get_system_packages(self) -> List: 660 """ 661 List of package names from the packages sections matching 662 type="image" and type=build_type 663 664 :return: package names 665 666 :rtype: list 667 """ 668 result = [] 669 image_packages_sections = self.get_packages_sections( 670 ['image', self.get_build_type_name()] 671 ) 672 package_list = self.get_package_sections( 673 image_packages_sections 674 ) 675 if package_list: 676 for package in package_list: 677 result.append(package.package_section.get_name().strip()) 678 return sorted(list(set(result))) 679 680 def get_bootstrap_files(self) -> Dict[str, FileT]: 681 """ 682 List of file names from the type="bootstrap" packages section(s) 683 684 :return: file names 685 686 :rtype: dict 687 """ 688 result = {} 689 bootstrap_packages_sections = self.get_bootstrap_packages_sections() 690 if bootstrap_packages_sections: 691 for bootstrap_packages_section in bootstrap_packages_sections: 692 file_list = bootstrap_packages_section.get_file() or [] 693 for file in file_list: 694 result[file.get_name()] = FileT( 695 target=file.get_target() or '', 696 owner=file.get_owner() or '', 697 permissions=file.get_permissions() or '' 698 ) 699 return result 700 701 def get_system_files(self) -> Dict[str, FileT]: 702 """ 703 List of file names from the packages sections matching 704 type="image" and type=build_type 705 706 :return: file names 707 708 :rtype: dict 709 """ 710 result = {} 711 image_packages_sections = self.get_packages_sections( 712 ['image', self.get_build_type_name()] 713 ) 714 for packages in image_packages_sections: 715 for file in packages.get_file(): 716 result[file.get_name()] = FileT( 717 target=file.get_target() or '', 718 owner=file.get_owner() or '', 719 permissions=file.get_permissions() or '' 720 ) 721 return result 722 723 def get_bootstrap_archives(self) -> List: 724 """ 725 List of archive names from the type="bootstrap" packages section(s) 726 727 :return: archive names 728 729 :rtype: list 730 """ 731 result = [] 732 bootstrap_packages_sections = self.get_bootstrap_packages_sections() 733 if bootstrap_packages_sections: 734 for bootstrap_packages_section in bootstrap_packages_sections: 735 archive_list = bootstrap_packages_section.get_archive() 736 if archive_list: 737 for archive in archive_list: 738 result.append(archive.get_name().strip()) 739 return sorted(result) 740 741 def get_system_archives(self) -> List: 742 """ 743 List of archive names from the packages sections matching 744 type="image" and type=build_type 745 746 :return: archive names 747 748 :rtype: list 749 """ 750 result = [] 751 image_packages_sections = self.get_packages_sections( 752 ['image', self.get_build_type_name()] 753 ) 754 for packages in image_packages_sections: 755 for archive in packages.get_archive(): 756 result.append(archive.get_name().strip()) 757 return sorted(result) 758 759 def get_ignore_packages(self, section_type: str) -> List: 760 """ 761 List of ignore package names from the packages sections matching 762 section_type and type=build_type 763 764 :return: package names 765 766 :rtype: list 767 """ 768 result = [] 769 image_packages_sections = self.get_packages_sections( 770 [section_type, self.get_build_type_name()] 771 ) 772 for packages in image_packages_sections: 773 for package in packages.get_ignore(): 774 if self.package_matches_host_architecture(package): 775 result.append(package.get_name().strip()) 776 return sorted(result) 777 778 def get_system_files_ignore_packages(self) -> List[str]: 779 """ 780 List of ignore package names from the type="systemfiles" 781 packages section(s) 782 783 :return: package names 784 785 :rtype: list 786 """ 787 return self.get_ignore_packages('systemfiles') 788 789 def get_system_ignore_packages(self) -> List: 790 """ 791 List of ignore package names from the packages sections matching 792 type="image" and type=build_type 793 794 :return: package names 795 796 :rtype: list 797 """ 798 return self.get_ignore_packages('image') 799 800 def get_bootstrap_ignore_packages(self) -> List: 801 """ 802 List of ignore package names from the packages sections matching 803 type="image" and type=build_type 804 805 :return: package names 806 807 :rtype: list 808 """ 809 return self.get_ignore_packages('bootstrap') 810 811 def get_bootstrap_package_name(self) -> str: 812 """ 813 bootstrap_package name from type="bootstrap" packages section 814 815 :return: bootstrap_package name 816 817 :rtype: str 818 """ 819 typed_packages_sections = self.get_packages_sections( 820 ['bootstrap', self.get_build_type_name()] 821 ) 822 bootstrap_package = '' 823 for packages in typed_packages_sections: 824 bootstrap_package = packages.get_bootstrap_package() 825 if bootstrap_package: 826 break 827 return bootstrap_package 828 829 def get_collection_type(self, section_type: str = 'image') -> str: 830 """ 831 Collection type from packages sections matching given section 832 type. 833 834 If no collection type is specified the default collection 835 type is set to: onlyRequired 836 837 :param str section_type: type name from packages section 838 839 :return: collection type name 840 841 :rtype: str 842 """ 843 typed_packages_sections = self.get_packages_sections( 844 [section_type, self.get_build_type_name()] 845 ) 846 collection_type = 'onlyRequired' 847 for packages in typed_packages_sections: 848 packages_collection_type = packages.get_patternType() 849 if packages_collection_type: 850 collection_type = packages_collection_type 851 break 852 return collection_type 853 854 def get_bootstrap_collection_type(self) -> str: 855 """ 856 Collection type for packages sections matching type="bootstrap" 857 858 :return: collection type name 859 860 :rtype: str 861 """ 862 return self.get_collection_type('bootstrap') 863 864 def get_system_collection_type(self) -> str: 865 """ 866 Collection type for packages sections matching type="image" 867 868 :return: collection type name 869 870 :rtype: str 871 """ 872 return self.get_collection_type('image') 873 874 def get_collection_modules(self) -> Dict[str, List[str]]: 875 """ 876 Dict of collection modules to enable and/or disable 877 878 :return: 879 Dict of the form: 880 881 .. code:: python 882 883 { 884 'enable': [ 885 "module:stream", "module" 886 ], 887 'disable': [ 888 "module" 889 ] 890 } 891 892 :rtype: dict 893 """ 894 modules: Dict[str, List[str]] = { 895 'disable': [], 896 'enable': [] 897 } 898 for packages in self.get_bootstrap_packages_sections(): 899 for collection_module in packages.get_collectionModule(): 900 module_name = collection_module.get_name() 901 if collection_module.get_enable() is False: 902 modules['disable'].append(module_name) 903 else: 904 stream = collection_module.get_stream() 905 if stream: 906 modules['enable'].append(f'{module_name}:{stream}') 907 else: 908 modules['enable'].append(module_name) 909 return modules 910 911 def get_collections(self, section_type: str = 'image') -> List: 912 """ 913 List of collection names from the packages sections matching 914 type=section_type and type=build_type 915 916 :return: collection names 917 918 :rtype: list 919 """ 920 result = [] 921 typed_packages_sections = self.get_packages_sections( 922 [section_type, self.get_build_type_name()] 923 ) 924 for packages in typed_packages_sections: 925 for collection in packages.get_namedCollection(): 926 if self.collection_matches_host_architecture(collection): 927 result.append(collection.get_name()) 928 return sorted(list(set(result))) 929 930 def get_bootstrap_collections(self) -> List: 931 """ 932 List of collection names from the packages sections 933 matching type="bootstrap" 934 935 :return: collection names 936 937 :rtype: list 938 """ 939 return self.get_collections('bootstrap') 940 941 def get_system_collections(self) -> List: 942 """ 943 List of collection names from the packages sections 944 matching type="image" 945 946 :return: collection names 947 948 :rtype: list 949 """ 950 return self.get_collections('image') 951 952 def get_products(self, section_type: str = 'image') -> List: 953 """ 954 List of product names from the packages sections matching 955 type=section_type and type=build_type 956 957 :param str section_type: type name from packages section 958 959 :return: product names 960 961 :rtype: list 962 """ 963 result = [] 964 typed_packages_sections = self.get_packages_sections( 965 [section_type, self.get_build_type_name()] 966 ) 967 for packages in typed_packages_sections: 968 for product in packages.get_product(): 969 result.append(product.get_name()) 970 return list(set(result)) 971 972 def get_bootstrap_products(self) -> List: 973 """ 974 List of product names from the packages sections 975 matching type="bootstrap" 976 977 :return: product names 978 979 :rtype: list 980 """ 981 return self.get_products('bootstrap') 982 983 def get_system_products(self) -> List: 984 """ 985 List of product names from the packages sections 986 matching type="image" 987 988 :return: product names 989 990 :rtype: list 991 """ 992 return self.get_products('image') 993 994 def is_xen_server(self) -> bool: 995 """ 996 Check if build type domain setup specifies a Xen Server (dom0) 997 998 :return: True or False 999 1000 :rtype: bool 1001 """ 1002 return self.build_type.get_xen_server() 1003 1004 def is_xen_guest(self) -> bool: 1005 """ 1006 Check if build type setup specifies a Xen Guest (domX) 1007 The check is based on the architecture, the firmware and 1008 xen_loader configuration values: 1009 1010 * We only support Xen setup on the x86_64 architecture 1011 1012 * Firmware pointing to ec2 means the image is targeted to run 1013 in Amazon EC2 which is a Xen guest 1014 1015 * Machine setup with a xen_loader attribute also indicates a 1016 Xen guest target 1017 1018 :return: True or False 1019 1020 :rtype: bool 1021 """ 1022 if self.host_architecture != 'x86_64': 1023 # We only support Xen stuff on x86_64 1024 return False 1025 firmware = self.build_type.get_firmware() 1026 machine_section = self.get_build_type_machine_section() 1027 if firmware and firmware in Defaults.get_ec2_capable_firmware_names(): 1028 # the image is targeted to run in Amazon EC2 which is a Xen system 1029 return True 1030 elif machine_section and machine_section.get_xen_loader(): 1031 # the image provides a machine section with a guest loader setup 1032 return True 1033 return False 1034 1035 def get_build_type_partitions_section(self) -> Any: 1036 """ 1037 First partitions section from the build type section 1038 1039 :return: <partitions> section reference 1040 1041 :rtype: xml_parse::partitions 1042 """ 1043 partitions_sections = self.build_type.get_partitions() 1044 if partitions_sections: 1045 return partitions_sections[0] 1046 return None 1047 1048 def get_build_type_system_disk_section(self) -> Any: 1049 """ 1050 First system disk section from the build type section 1051 1052 :return: <systemdisk> section reference 1053 1054 :rtype: xml_parse::systemdisk 1055 """ 1056 systemdisk_sections = self.build_type.get_systemdisk() 1057 if systemdisk_sections: 1058 return systemdisk_sections[0] 1059 return None 1060 1061 def get_build_type_machine_section(self) -> Any: 1062 """ 1063 First machine section from the build type section 1064 1065 :return: <machine> section reference 1066 1067 :rtype: xml_parse::machine 1068 """ 1069 machine_sections = self.build_type.get_machine() 1070 if machine_sections: 1071 return machine_sections[0] 1072 return None 1073 1074 def get_build_type_vagrant_config_section(self) -> Any: 1075 """ 1076 First vagrantconfig section from the build type section 1077 1078 :return: <vagrantconfig> section reference 1079 1080 :rtype: xml_parse::vagrantconfig 1081 """ 1082 vagrant_config_sections = self.build_type.get_vagrantconfig() 1083 if vagrant_config_sections: 1084 return vagrant_config_sections[0] 1085 return None 1086 1087 def get_vagrant_config_virtualbox_guest_additions(self) -> bool: 1088 """ 1089 Attribute virtualbox_guest_additions_present from the first 1090 vagrantconfig section. 1091 1092 :return: True|False 1093 1094 :rtype: bool 1095 """ 1096 vagrant_config_sections = self.get_build_type_vagrant_config_section() 1097 if not vagrant_config_sections.virtualbox_guest_additions_present: 1098 return Defaults.get_vagrant_config_virtualbox_guest_additions() 1099 else: 1100 return vagrant_config_sections.virtualbox_guest_additions_present 1101 1102 def get_build_type_vmdisk_section(self) -> Any: 1103 """ 1104 First vmdisk section from the first machine section in the 1105 build type section 1106 1107 :return: <vmdisk> section reference 1108 1109 :rtype: xml_parse::vmdisk 1110 """ 1111 machine_section = self.get_build_type_machine_section() 1112 if machine_section: 1113 vmdisk_sections = machine_section.get_vmdisk() 1114 if vmdisk_sections: 1115 return vmdisk_sections[0] 1116 return None 1117 1118 def get_build_type_vmnic_entries(self) -> List: 1119 """ 1120 vmnic section(s) from the first machine section in the 1121 build type section 1122 1123 :return: list of <vmnic> section reference(s) 1124 1125 :rtype: list 1126 """ 1127 machine_section = self.get_build_type_machine_section() 1128 if machine_section: 1129 return machine_section.get_vmnic() 1130 else: 1131 return [] 1132 1133 def get_build_type_vmdvd_section(self) -> Any: 1134 """ 1135 First vmdvd section from the first machine section in the 1136 build type section 1137 1138 :return: <vmdvd> section reference 1139 1140 :rtype: xml_parse::vmdvd 1141 """ 1142 machine_section = self.get_build_type_machine_section() 1143 if machine_section: 1144 vmdvd_sections = machine_section.get_vmdvd() 1145 if vmdvd_sections: 1146 return vmdvd_sections[0] 1147 return None 1148 1149 def get_build_type_vmconfig_entries(self) -> List: 1150 """ 1151 List of vmconfig-entry section values from the first 1152 machine section in the build type section 1153 1154 :return: <vmconfig_entry> section reference(s) 1155 1156 :rtype: list 1157 """ 1158 machine_section = self.get_build_type_machine_section() 1159 if machine_section: 1160 vmconfig_entries = machine_section.get_vmconfig_entry() 1161 if vmconfig_entries: 1162 return vmconfig_entries 1163 1164 return [] 1165 1166 def get_build_type_bootloader_section(self) -> Any: 1167 """ 1168 First bootloader section from the build type section 1169 1170 :return: <bootloader> section reference 1171 1172 :rtype: xml_parse::bootloader 1173 """ 1174 bootloader_sections = self.build_type.get_bootloader() 1175 if bootloader_sections: 1176 return bootloader_sections[0] 1177 return None 1178 1179 def get_build_type_bootloader_name(self) -> str: 1180 """ 1181 Return bootloader name for selected build type 1182 1183 :return: bootloader name 1184 1185 :rtype: str 1186 """ 1187 bootloader = self.get_build_type_bootloader_section() 1188 return bootloader.get_name() if bootloader else \ 1189 Defaults.get_default_bootloader() 1190 1191 def get_build_type_bootloader_bls(self) -> bool: 1192 """ 1193 Return bootloader bls setting for selected build type 1194 1195 :return: True or False 1196 1197 :rtype: bool 1198 """ 1199 bootloader = self.get_build_type_bootloader_section() 1200 if bootloader and bootloader.get_bls() is not None: 1201 return bootloader.get_bls() 1202 return True 1203 1204 def get_build_type_bootloader_console(self) -> List[str]: 1205 """ 1206 Return bootloader console setting for selected build type 1207 1208 :return: 1209 list of console settings for output (first element) 1210 and input (second element) 1211 1212 :rtype: list 1213 """ 1214 result = ['', ''] 1215 bootloader = self.get_build_type_bootloader_section() 1216 if bootloader: 1217 console_out = bootloader.get_output_console() 1218 console_in = bootloader.get_input_console() 1219 console_in = console_in if console_in else console_out 1220 result = [ 1221 console_out if console_out and console_out != 'none' else '', 1222 console_in if console_in and console_in != 'none' else '' 1223 ] 1224 return result 1225 1226 def get_build_type_bootloader_serial_line_setup(self) -> Optional[str]: 1227 """ 1228 Return bootloader serial line setup parameters for the 1229 selected build type 1230 1231 :return: serial line setup 1232 1233 :rtype: str 1234 """ 1235 bootloader = self.get_build_type_bootloader_section() 1236 if bootloader: 1237 return bootloader.get_serial_line() 1238 return None 1239 1240 def get_build_type_bootloader_timeout(self) -> Optional[str]: 1241 """ 1242 Return bootloader timeout setting for selected build type 1243 1244 :return: timeout string 1245 1246 :rtype: str 1247 """ 1248 bootloader = self.get_build_type_bootloader_section() 1249 if bootloader: 1250 return bootloader.get_timeout() 1251 return None 1252 1253 def get_build_type_bootloader_timeout_style(self) -> Optional[str]: 1254 """ 1255 Return bootloader timeout style setting for selected build type 1256 1257 :return: timeout_style string 1258 1259 :rtype: str 1260 """ 1261 bootloader = self.get_build_type_bootloader_section() 1262 if bootloader: 1263 return bootloader.get_timeout_style() 1264 return None 1265 1266 def get_build_type_bootloader_targettype(self) -> Optional[str]: 1267 """ 1268 Return bootloader target type setting. Only relevant for 1269 the zipl bootloader because zipl is installed differently 1270 depending on the storage target it runs later 1271 1272 :return: target type string 1273 1274 :rtype: str 1275 """ 1276 bootloader = self.get_build_type_bootloader_section() 1277 if bootloader: 1278 return bootloader.get_targettype() 1279 return None 1280 1281 def get_build_type_bootloader_settings_section(self) -> Any: 1282 """ 1283 First bootloadersettings section from the build 1284 type bootloader section 1285 1286 :return: <bootloadersettings> section reference 1287 1288 :rtype: xml_parse::bootloadersettings 1289 """ 1290 bootloader_section = self.get_build_type_bootloader_section() 1291 bootloader_settings_section = None 1292 if bootloader_section and bootloader_section.get_bootloadersettings(): 1293 bootloader_settings_section = \ 1294 bootloader_section.get_bootloadersettings()[0] 1295 return bootloader_settings_section 1296 1297 def get_build_type_bootloader_securelinux_section(self) -> List[Any]: 1298 """ 1299 First securelinux section from the build 1300 type bootloader section 1301 1302 :return: <securelinux> section reference 1303 1304 :rtype: xml_parse::securelinux 1305 """ 1306 bootloader_section = self.get_build_type_bootloader_section() 1307 bootloader_securelinux_section = [] 1308 if bootloader_section and bootloader_section.get_securelinux(): 1309 bootloader_securelinux_section = \ 1310 bootloader_section.get_securelinux() 1311 return bootloader_securelinux_section 1312 1313 def get_build_type_bootloader_environment_variables(self) -> List[str]: 1314 """ 1315 List of bootloader variables from the build 1316 type > bootloader > bootloadersettings section 1317 """ 1318 variable_list = [] 1319 bootloader_settings_section = \ 1320 self.get_build_type_bootloader_settings_section() 1321 if bootloader_settings_section: 1322 environment = bootloader_settings_section.get_environment() 1323 if environment and environment[0].get_env(): 1324 for env in environment[0].get_env(): 1325 variable_list.append( 1326 '{}{}'.format( 1327 env.get_name(), 1328 f'={env.get_value()}' if env.get_value() else '' 1329 ) 1330 ) 1331 return variable_list 1332 1333 def get_bootloader_options(self, option_type: str) -> List[str]: 1334 """ 1335 List of custom options used in the process to 1336 run bootloader setup workloads 1337 """ 1338 result: List[str] = [] 1339 bootloader_settings = self.get_build_type_bootloader_settings_section() 1340 if bootloader_settings: 1341 options = [] 1342 if option_type == 'shim': 1343 options = bootloader_settings.get_shimoption() 1344 elif option_type == 'install': 1345 options = bootloader_settings.get_installoption() 1346 elif option_type == 'config': 1347 options = bootloader_settings.get_configoption() 1348 for option in options: 1349 result.append(option.get_name()) 1350 if option.get_value(): 1351 result.append(option.get_value()) 1352 return result 1353 1354 def get_bootloader_shim_options(self) -> List[str]: 1355 """ 1356 List of custom options used in the process to setup secure boot 1357 """ 1358 return self.get_bootloader_options('shim') 1359 1360 def get_bootloader_install_options(self) -> List[str]: 1361 """ 1362 List of custom options used in the bootloader installation 1363 """ 1364 return self.get_bootloader_options('install') 1365 1366 def get_bootloader_config_options(self) -> List[str]: 1367 """ 1368 List of custom options used in the bootloader configuration 1369 """ 1370 return self.get_bootloader_options('config') 1371 1372 def get_build_type_bootloader_use_disk_password(self) -> bool: 1373 """ 1374 Indicate whether the bootloader configuration should use the 1375 password protecting the encrypted root volume. 1376 1377 :return: True|False 1378 1379 :rtype: bool 1380 """ 1381 bootloader = self.get_build_type_bootloader_section() 1382 if bootloader: 1383 return bootloader.get_use_disk_password() 1384 return False 1385 1386 def get_build_type_oemconfig_section(self) -> Any: 1387 """ 1388 First oemconfig section from the build type section 1389 1390 :return: <oemconfig> section reference 1391 1392 :rtype: xml_parse::oemconfig 1393 """ 1394 oemconfig_sections = self.build_type.get_oemconfig() 1395 if oemconfig_sections: 1396 return oemconfig_sections[0] 1397 return None 1398 1399 def get_oemconfig_oem_resize(self) -> bool: 1400 """ 1401 State value to activate/deactivate disk resize. Returns a 1402 boolean value if specified or True to set resize on by default 1403 1404 :return: Content of <oem-resize> section value 1405 1406 :rtype: bool 1407 """ 1408 oemconfig = self.get_build_type_oemconfig_section() 1409 if oemconfig and oemconfig.get_oem_resize(): 1410 return oemconfig.get_oem_resize()[0] 1411 else: 1412 return True 1413 1414 def get_oemconfig_oem_systemsize(self) -> int: 1415 """ 1416 State value to retrieve root partition size 1417 1418 :return: Content of <oem-systemsize> section value 1419 1420 :rtype: int 1421 """ 1422 oemconfig = self.get_build_type_oemconfig_section() 1423 if oemconfig and oemconfig.get_oem_systemsize(): 1424 return int(oemconfig.get_oem_systemsize()[0]) 1425 else: 1426 return 0 1427 1428 def get_oemconfig_oem_multipath_scan(self) -> bool: 1429 """ 1430 State value to activate multipath maps. Returns a boolean 1431 value if specified or False 1432 1433 :return: Content of <oem-multipath-scan> section value 1434 1435 :rtype: bool 1436 """ 1437 oemconfig = self.get_build_type_oemconfig_section() 1438 if oemconfig and oemconfig.get_oem_multipath_scan(): 1439 return oemconfig.get_oem_multipath_scan()[0] 1440 return False 1441 1442 def get_oemconfig_swap_mbytes(self) -> Optional[int]: 1443 """ 1444 Return swapsize in MB if requested or None 1445 1446 Operates on the value of oem-swap and if set to true 1447 returns the given size or the default value. 1448 1449 :return: Content of <oem-swapsize> section value or default 1450 1451 :rtype: int 1452 """ 1453 oemconfig = self.get_build_type_oemconfig_section() 1454 if oemconfig and oemconfig.get_oem_swap(): 1455 swap_requested = oemconfig.get_oem_swap()[0] 1456 if swap_requested: 1457 swapsize = oemconfig.get_oem_swapsize() 1458 if swapsize: 1459 return swapsize[0] 1460 else: 1461 return Defaults.get_swapsize_mbytes() 1462 return None 1463 1464 def get_oemconfig_swap_name(self) -> str: 1465 """ 1466 Return the swap space name 1467 1468 Operates on the value of oem-swapname and if set 1469 returns the configured name or the default name: LVSwap 1470 1471 The name of the swap space is used only if the 1472 image is configured to use the LVM volume manager. 1473 In this case swap is a volume and the volume takes 1474 a name. In any other case the given name will have 1475 no effect. 1476 1477 :return: Content of <oem-swapname> section value or default 1478 1479 :rtype: str 1480 """ 1481 oemconfig = self.get_build_type_oemconfig_section() 1482 if oemconfig and oemconfig.get_oem_swapname(): 1483 return oemconfig.get_oem_swapname()[0] 1484 return 'LVSwap' 1485 1486 def get_build_type_containerconfig_section(self) -> Any: 1487 """ 1488 First containerconfig section from the build type section 1489 1490 :return: <containerconfig> section reference 1491 1492 :rtype: xml_parse::containerconfig 1493 """ 1494 container_config_sections = self.build_type.get_containerconfig() 1495 if container_config_sections: 1496 return container_config_sections[0] 1497 return None 1498 1499 def get_dracut_config(self, action: str) -> DracutT: 1500 """ 1501 Get dracut initrd config for the specified action 1502 """ 1503 uefi = False 1504 modules = [] 1505 drivers = [] 1506 initrd_sections = self.build_type.get_initrd() 1507 for initrd_section in initrd_sections: 1508 if initrd_section.get_action() == action: 1509 for dracut in initrd_section.get_dracut(): 1510 uefi = bool(dracut.get_uefi()) 1511 if dracut.get_module(): 1512 modules.append(dracut.get_module()) 1513 if dracut.get_driver(): 1514 drivers.append(dracut.get_driver()) 1515 return DracutT( 1516 uefi=uefi, modules=modules, drivers=drivers 1517 ) 1518 1519 def get_installmedia_initrd_modules(self, action: str) -> List[str]: 1520 """ 1521 Gets the list of modules to append in installation initrds 1522 1523 :return: a list of dracut module names 1524 1525 :rtype: list 1526 """ 1527 modules: List[str] = [] 1528 installmedia = self.build_type.get_installmedia() 1529 if not installmedia: 1530 return modules 1531 initrd_sections = installmedia[0].get_initrd() 1532 for initrd_section in initrd_sections: 1533 if initrd_section.get_action() == action: 1534 for module in initrd_section.get_dracut(): 1535 if module.get_module(): 1536 modules.append(module.get_module()) 1537 return modules 1538 1539 def get_installmedia_initrd_drivers(self, action: str) -> List[str]: 1540 """ 1541 Gets the list of drivers to append in installation initrds 1542 1543 :return: a list of dracut driver names 1544 1545 :rtype: list 1546 """ 1547 drivers: List[str] = [] 1548 installmedia = self.build_type.get_installmedia() 1549 if not installmedia: 1550 return drivers 1551 initrd_sections = installmedia[0].get_initrd() 1552 for initrd_section in initrd_sections: 1553 if initrd_section.get_action() == action: 1554 for driver in initrd_section.get_dracut(): 1555 if driver.get_driver(): 1556 drivers.append(driver.get_driver()) 1557 return drivers 1558 1559 def get_build_type_size( 1560 self, include_unpartitioned: bool = False 1561 ) -> Optional[size_type]: 1562 """ 1563 Size information from the build type section. 1564 If no unit is set the value is treated as mbytes 1565 1566 :param bool include_unpartitioned: sets if the unpartitioned area 1567 should be included in the computed size or not 1568 1569 :return: mbytes 1570 1571 :rtype: int 1572 """ 1573 size_section = self.build_type.get_size() 1574 if size_section: 1575 unit = size_section[0].get_unit() 1576 additive = size_section[0].get_additive() 1577 unpartitioned = size_section[0].get_unpartitioned() 1578 value = int(size_section[0].get_valueOf_()) 1579 if not include_unpartitioned and unpartitioned is not None: 1580 value -= unpartitioned 1581 if unit == 'G': 1582 value *= 1024 1583 return size_type( 1584 mbytes=value, additive=additive 1585 ) 1586 return None 1587 1588 def get_build_type_unpartitioned_bytes(self) -> int: 1589 """ 1590 Size of the unpartitioned area for image in megabytes 1591 1592 :return: mbytes 1593 1594 :rtype: int 1595 """ 1596 size_section = self.build_type.get_size() 1597 if size_section: 1598 unit = size_section[0].get_unit() or 'M' 1599 unpartitioned = size_section[0].get_unpartitioned() or 0 1600 return StringToSize.to_bytes('{0}{1}'.format(unpartitioned, unit)) 1601 return 0 1602 1603 def get_disk_start_sector(self) -> int: 1604 """ 1605 First disk sector number to be used by the first disk partition. 1606 1607 :return: number 1608 1609 :rtype: int 1610 """ 1611 disk_start_sector = self.build_type.get_disk_start_sector() 1612 if disk_start_sector is None: 1613 disk_start_sector = Defaults.get_default_disk_start_sector() 1614 return disk_start_sector 1615 1616 def get_build_type_spare_part_size(self) -> Optional[int]: 1617 """ 1618 Size information for the spare_part size from the build 1619 type. If no unit is set the value is treated as mbytes 1620 1621 :return: mbytes 1622 1623 :rtype: int 1624 """ 1625 spare_part_size = self.build_type.get_spare_part() 1626 if spare_part_size: 1627 return self._to_mega_byte(spare_part_size) 1628 return None 1629 1630 def get_build_type_spare_part_fs_attributes(self) -> Optional[List]: 1631 """ 1632 Build type specific list of filesystem attributes applied to 1633 the spare partition. 1634 1635 :return: list of strings or empty list 1636 1637 :rtype: list 1638 """ 1639 spare_part_attributes = self.build_type.get_spare_part_fs_attributes() 1640 if spare_part_attributes: 1641 return spare_part_attributes.strip().split(',') 1642 return None 1643 1644 def get_build_type_format_options(self) -> Dict: 1645 """ 1646 Disk format options returned as a dictionary 1647 1648 :return: format options 1649 1650 :rtype: dict 1651 """ 1652 result = {} 1653 format_options = self.build_type.get_formatoptions() 1654 if format_options: 1655 for option in format_options.split(','): 1656 key_value_list = option.split('=') 1657 if len(key_value_list) == 2: 1658 result[key_value_list[0]] = key_value_list[1] 1659 else: 1660 result[key_value_list[0]] = None 1661 return result 1662 1663 def get_volume_group_name(self) -> str: 1664 """ 1665 Volume group name from selected <systemdisk> section 1666 1667 :return: volume group name 1668 1669 :rtype: str 1670 """ 1671 systemdisk_section = self.get_build_type_system_disk_section() 1672 volume_group_name = None 1673 if systemdisk_section: 1674 volume_group_name = systemdisk_section.get_name() 1675 if not volume_group_name: 1676 volume_group_name = Defaults.get_default_volume_group_name() 1677 return volume_group_name 1678 1679 def get_users(self) -> List: 1680 """ 1681 List of configured users. 1682 1683 Each entry in the list is a single xml_parse::user instance. 1684 1685 :return: list of <user> section reference(s) 1686 1687 :rtype: list 1688 """ 1689 users_list = [] 1690 users_names_added = [] 1691 for users_section in self.get_users_sections(): 1692 for user in users_section.get_user(): 1693 if user.get_name() not in users_names_added: 1694 users_list.append(user) 1695 users_names_added.append(user.get_name()) 1696 1697 return users_list 1698 1699 def get_user_groups(self, user_name) -> List[str]: 1700 """ 1701 List of group names matching specified user 1702 1703 Each entry in the list is the name of a group and optionally its 1704 group ID separated by a colon, that the specified user belongs to. 1705 The first item in the list is the login or primary group. The 1706 list will be empty if no groups are specified in the 1707 description file. 1708 1709 :return: groups data for the given user 1710 1711 :rtype: list 1712 """ 1713 groups_list = [] 1714 for users_section in self.get_users_sections(): 1715 for user in users_section.get_user(): 1716 if user.get_name() == user_name: 1717 user_groups = user.get_groups() 1718 if user_groups: 1719 groups_list += user.get_groups().split(',') 1720 1721 # order of list items matter, thus we don't use set() here 1722 # better faster, nicer solutions welcome :) 1723 result_group_list = [] 1724 for item in groups_list: 1725 if item not in result_group_list: 1726 result_group_list.append(item) 1727 1728 return result_group_list 1729 1730 def get_container_config(self) -> Dict: 1731 """ 1732 Dictionary of containerconfig information 1733 1734 Takes attributes and subsection data from the selected 1735 <containerconfig> section and stores it in a dictionary 1736 """ 1737 container_config = self._match_docker_base_data() 1738 container_config.update( 1739 self._match_docker_entrypoint() 1740 ) 1741 container_config.update( 1742 self._match_docker_subcommand() 1743 ) 1744 container_config.update( 1745 self._match_docker_expose_ports() 1746 ) 1747 container_config.update( 1748 self._match_docker_volumes() 1749 ) 1750 container_config.update( 1751 self._match_docker_stopsignal() 1752 ) 1753 container_config.update( 1754 self._match_docker_environment() 1755 ) 1756 container_config.update( 1757 self._match_docker_labels() 1758 ) 1759 container_config.update( 1760 self._match_docker_history() 1761 ) 1762 1763 desc = self.get_description_section() 1764 author_contact = "{0} <{1}>".format(desc.author, desc.contact) 1765 if 'history' not in container_config: 1766 container_config['history'] = {} 1767 if 'author' not in container_config['history']: 1768 container_config['history']['author'] = author_contact 1769 if 'maintainer' not in container_config: 1770 container_config['maintainer'] = author_contact 1771 1772 return container_config 1773 1774 def set_container_config_tag(self, tag: str) -> None: 1775 """ 1776 Set new tag name in containerconfig section 1777 1778 In order to set a new tag value an existing containerconfig and 1779 tag setup is required 1780 1781 :param str tag: tag name 1782 """ 1783 container_config_section = self.get_build_type_containerconfig_section() 1784 if container_config_section and container_config_section.get_tag(): 1785 container_config_section.set_tag(tag) 1786 else: 1787 message = dedent('''\n 1788 No <containerconfig> section and/or tag attribute configured 1789 1790 In order to set the tag {0} as new container tag, 1791 an initial containerconfig section including a tag 1792 setup is required 1793 ''') 1794 log.warning(message.format(tag)) 1795 1796 def add_container_config_label(self, label_name: str, value: str) -> None: 1797 """ 1798 Adds a new label in the containerconfig section, if a label with the 1799 same name is already defined in containerconfig it gets overwritten by 1800 this method. 1801 1802 :param str label_name: the string representing the label name 1803 :param str value: the value of the label 1804 """ 1805 if self.get_build_type_name() not in ['docker', 'oci']: 1806 message = dedent('''\n 1807 Labels can only be configured for container image types 1808 docker and oci. 1809 ''') 1810 log.warning(message) 1811 return 1812 1813 container_config_section = self.get_build_type_containerconfig_section() 1814 if not container_config_section: 1815 container_config_section = xml_parse.containerconfig( 1816 name=Defaults.get_default_container_name(), 1817 tag=Defaults.get_default_container_tag() 1818 ) 1819 self.build_type.set_containerconfig([container_config_section]) 1820 1821 labels = container_config_section.get_labels() 1822 if not labels: 1823 labels = [xml_parse.labels()] 1824 1825 label_names = [] 1826 for label in labels[0].get_label(): 1827 label_names.append(label.get_name()) 1828 1829 if label_name in label_names: 1830 labels[0].replace_label_at( 1831 label_names.index(label_name), 1832 xml_parse.label(label_name, value) 1833 ) 1834 else: 1835 labels[0].add_label(xml_parse.label(label_name, value)) 1836 1837 container_config_section.set_labels(labels) 1838 1839 def get_partitions(self) -> Dict[str, ptable_entry_type]: 1840 """ 1841 Dictionary of configured partitions. 1842 1843 Each entry in the dict references a ptable_entry_type 1844 Each key in the dict references the name of the 1845 partition entry as handled by KIWI 1846 1847 :return: 1848 Contains dict of ptable_entry_type tuples 1849 1850 .. code:: python 1851 1852 { 1853 'NAME': ptable_entry_type( 1854 mbsize=int, 1855 clone=int, 1856 partition_name=str, 1857 partition_type=str, 1858 partition_id=Optional[int], 1859 mountpoint=str, 1860 filesystem=str, 1861 label=str 1862 ) 1863 } 1864 1865 :rtype: dict 1866 """ 1867 partitions: Dict[str, ptable_entry_type] = {} 1868 partitions_section = self.get_build_type_partitions_section() 1869 if not partitions_section: 1870 return partitions 1871 for partition in partitions_section.get_partition(): 1872 name = partition.get_name() 1873 partition_name = partition.get_partition_name() or f'p.lx{name}' 1874 partitions[name] = ptable_entry_type( 1875 mbsize=self._to_mega_byte(partition.get_size()), 1876 clone=int(partition.get_clone()) if partition.get_clone() else 0, 1877 partition_name=partition_name, 1878 partition_type=partition.get_partition_type() or 't.linux', 1879 partition_id=partition.get_part_id(), 1880 mountpoint=partition.get_mountpoint(), 1881 filesystem=partition.get_filesystem(), 1882 label=partition.get_label() or '' 1883 ) 1884 return partitions 1885 1886 def get_host_key_certificates( 1887 self 1888 ) -> Union[List[Dict[str, List[str]]], List[Dict[str, str]]]: 1889 cc_result = [] 1890 cc_certificates: Dict[str, List[str]] = {} 1891 securelinux_list = \ 1892 self.get_build_type_bootloader_securelinux_section() 1893 for securelinux in securelinux_list: 1894 cc_certificates = { 1895 'hkd_cert': [], 1896 'hkd_revocation_list': [], 1897 'hkd_ca_cert': securelinux.get_hkd_ca_cert(), 1898 'hkd_sign_cert': securelinux.get_hkd_sign_cert() 1899 } 1900 for hkd_cert in securelinux.get_hkd_cert(): 1901 cc_certificates['hkd_cert'].append(hkd_cert.get_name()) 1902 for hkd_revocation_list in securelinux.get_hkd_revocation_list(): 1903 cc_certificates['hkd_revocation_list'].append( 1904 hkd_revocation_list.get_name() 1905 ) 1906 cc_result.append(cc_certificates) 1907 return cc_result 1908 1909 def get_containers(self) -> List[ContainerT]: 1910 containers = [] 1911 1912 def build_fetch_command( 1913 root_dir: str, 1914 container_uri: str = '', 1915 container_file_name: str = '', 1916 container_endpoint: str = '' 1917 ): 1918 pass # pragma: nocover 1919 for containers_section in self.get_containers_sections(): 1920 for container in containers_section.get_container(): 1921 if self.container_matches_host_architecture(container): 1922 fetch_command = build_fetch_command 1923 load_command = [] 1924 container_tag = container.get_tag() or 'latest' 1925 container_path = container.get_path() or '' 1926 container_endpoint = os.path.normpath( 1927 '{0}/{1}/{2}:{3}'.format( 1928 containers_section.get_source(), container_path, 1929 container.name, container_tag 1930 ) 1931 ) 1932 container_file_name = '{0}/{1}_{2}'.format( 1933 defaults.LOCAL_CONTAINERS, container.name, container_tag 1934 ) 1935 container_backend = containers_section.get_backend() or '' 1936 if container_backend in ['podman', 'docker', 'container-snap']: 1937 if Defaults.is_buildservice_worker(): 1938 container_uri = Uri( 1939 'obsrepositories:/{0}'.format( 1940 container_endpoint 1941 ), 'container' 1942 ).translate() 1943 1944 def build_fetch_command( 1945 root_dir: str, 1946 container_uri: str = container_uri, 1947 container_file_name: str = container_file_name, 1948 container_endpoint: str = container_endpoint 1949 ): 1950 def perform(): 1951 Command.run( 1952 [ 1953 'cp', '{0}.ociarchive'.format( 1954 container_uri 1955 ), os.path.normpath( 1956 '{0}/{1}'.format( 1957 root_dir, 1958 container_file_name 1959 ) 1960 ) 1961 ] 1962 ) 1963 perform() 1964 fetch_command = build_fetch_command 1965 else: 1966 1967 def build_fetch_command( 1968 root_dir: str, 1969 container_uri: str = '', 1970 container_file_name: str = container_file_name, 1971 container_endpoint: str = container_endpoint 1972 ): 1973 def perform(): 1974 Command.run( 1975 [ 1976 'chroot', root_dir, 1977 '/usr/bin/skopeo', 'copy', 1978 'docker://{0}'.format( 1979 container_endpoint 1980 ), 1981 'oci-archive:{0}:{1}'.format( 1982 container_file_name, 1983 container_endpoint 1984 ) 1985 ] 1986 ) 1987 perform() 1988 fetch_command = build_fetch_command 1989 if not container.get_fetch_only(): 1990 load_command = [ 1991 f'/usr/bin/{container_backend}', 1992 'load', '-i', container_file_name 1993 ] 1994 containers.append( 1995 ContainerT( 1996 name=f'{container.name}_{container_tag}', 1997 backend=container_backend, 1998 container_file=container_file_name, 1999 fetch_only=bool(container.get_fetch_only()), 2000 fetch_command=fetch_command, 2001 load_command=load_command 2002 ) 2003 ) 2004 return containers 2005 2006 def get_volumes(self) -> List[volume_type]: 2007 """ 2008 List of configured systemdisk volumes. 2009 2010 Each entry in the list is a tuple with the following information 2011 2012 * name: name of the volume 2013 * size: size of the volume 2014 * realpath: system path to lookup volume data. If no mountpoint 2015 is set the volume name is used as data path. 2016 * mountpoint: volume mount point and volume data path 2017 * fullsize: takes all space True|False 2018 * attributes: list of volume attributes handled via chattr 2019 2020 :return: 2021 Contains list of volume_type tuples 2022 2023 .. code:: python 2024 2025 [ 2026 volume_type( 2027 name=volume_name, 2028 parent=volume_parent, 2029 size=volume_size, 2030 realpath=path, 2031 mountpoint=path, 2032 fullsize=True, 2033 label=volume_label, 2034 attributes=['no-copy-on-write'], 2035 is_root_volume=True|False 2036 ) 2037 ] 2038 2039 :rtype: list 2040 """ 2041 volume_type_list: List[volume_type] = [] 2042 systemdisk_section = self.get_build_type_system_disk_section() 2043 selected_filesystem = self.build_type.get_filesystem() 2044 swap_mbytes = self.get_oemconfig_swap_mbytes() 2045 swap_name = self.get_oemconfig_swap_name() 2046 if not systemdisk_section: 2047 return volume_type_list 2048 volumes = systemdisk_section.get_volume() 2049 have_root_volume_setup = False 2050 have_full_size_volume = False 2051 if volumes: 2052 for volume in volumes: 2053 if not self.volume_matches_host_architecture(volume): 2054 continue 2055 # volume setup for a full qualified volume with name and 2056 # mountpoint information. See below for exceptions 2057 name = volume.get_name() 2058 parent = volume.get_parent() or '' 2059 mountpoint = volume.get_mountpoint() 2060 realpath = mountpoint 2061 size = volume.get_size() 2062 freespace = volume.get_freespace() 2063 fullsize = False 2064 label = volume.get_label() 2065 attributes = [] 2066 is_root_volume = False 2067 2068 if volume.get_quota(): 2069 attributes.append(f'quota={volume.get_quota()}') 2070 2071 if volume.get_copy_on_write() is False: 2072 # by default copy-on-write is switched on for any 2073 # filesystem. Thus only if no copy on write is requested 2074 # the attribute is handled 2075 attributes.append('no-copy-on-write') 2076 2077 if volume.get_filesystem_check() is True: 2078 # by default filesystem check is switched off for any 2079 # filesystem except the rootfs. Thus only if filesystem 2080 # check is requested the attribute is handled 2081 attributes.append('enable-for-filesystem-check') 2082 2083 if '@root' in name: 2084 # setup root volume, it takes an optional volume 2085 # name if specified as @root=name and has no specific 2086 # mountpoint. The default name is set to 2087 # defaults.ROOT_VOLUME_NAME if no other root volume 2088 # name is provided 2089 mountpoint = None 2090 realpath = '/' 2091 is_root_volume = True 2092 root_volume_expression = re.match( 2093 r'@root=(.+)', name 2094 ) 2095 if root_volume_expression: 2096 name = root_volume_expression.group(1) 2097 else: 2098 name = defaults.ROOT_VOLUME_NAME 2099 have_root_volume_setup = True 2100 elif not mountpoint: 2101 # setup volume without mountpoint. In this case the name 2102 # attribute is used as mountpoint path and a name for the 2103 # volume is created from that path information 2104 mountpoint = name 2105 realpath = mountpoint 2106 name = self._to_volume_name(name) 2107 2108 if size: 2109 size = 'size:' + format( 2110 self._to_mega_byte(size) 2111 ) 2112 elif freespace: 2113 size = 'freespace:' + format( 2114 self._to_mega_byte(freespace) 2115 ) 2116 else: 2117 size = 'freespace:' + format( 2118 Defaults.get_min_volume_mbytes(selected_filesystem) 2119 ) 2120 2121 if ':all' in size: 2122 size = None 2123 fullsize = True 2124 have_full_size_volume = True 2125 2126 volume_type_list.append( 2127 volume_type( 2128 name=name, 2129 parent=parent, 2130 size=size, 2131 fullsize=fullsize, 2132 mountpoint=mountpoint, 2133 realpath=realpath, 2134 label=label, 2135 attributes=attributes, 2136 is_root_volume=is_root_volume 2137 ) 2138 ) 2139 2140 if not have_root_volume_setup: 2141 # There must always be a root volume setup. It will be the 2142 # full size volume if no other volume has this setup 2143 volume_management = self.get_volume_management() 2144 root_volume_name = \ 2145 defaults.ROOT_VOLUME_NAME if volume_management == 'lvm' else '' 2146 if have_full_size_volume: 2147 size = 'freespace:' + format( 2148 Defaults.get_min_volume_mbytes(selected_filesystem) 2149 ) 2150 fullsize = False 2151 else: 2152 size = None 2153 fullsize = True 2154 volume_type_list.append( 2155 volume_type( 2156 name=root_volume_name, 2157 parent='', 2158 size=size, 2159 fullsize=fullsize, 2160 mountpoint=None, 2161 realpath='/', 2162 label=None, 2163 attributes=[], 2164 is_root_volume=True 2165 ) 2166 ) 2167 2168 if swap_mbytes and self.get_volume_management() == 'lvm': 2169 volume_type_list.append( 2170 volume_type( 2171 name=swap_name, 2172 parent='', 2173 size='size:{0}'.format(swap_mbytes), 2174 fullsize=False, 2175 mountpoint=None, 2176 realpath='swap', 2177 label='SWAP', 2178 attributes=[], 2179 is_root_volume=False 2180 ) 2181 ) 2182 2183 return volume_type_list 2184 2185 def get_volume_management(self) -> Optional[str]: 2186 """ 2187 Provides information which volume management system is used 2188 2189 :return: name of volume manager 2190 2191 :rtype: str 2192 """ 2193 volume_filesystems = ['btrfs'] 2194 selected_filesystem = self.build_type.get_filesystem() 2195 selected_system_disk = self.get_build_type_system_disk_section() 2196 volume_management = None 2197 if selected_system_disk and selected_system_disk.get_preferlvm(): 2198 # LVM volume management is preferred, use it 2199 volume_management = 'lvm' 2200 elif selected_filesystem in volume_filesystems and selected_system_disk: 2201 # specified filesystem has its own volume management system 2202 volume_management = selected_filesystem 2203 elif selected_system_disk: 2204 # systemdisk section is specified with non volume capable 2205 # filesystem and no volume management preference. So let's 2206 # use LVM by default 2207 volume_management = 'lvm' 2208 return volume_management 2209 2210 def get_drivers_list(self) -> List: 2211 """ 2212 List of driver names from all drivers sections matching 2213 configured profiles 2214 2215 :return: driver names 2216 2217 :rtype: list 2218 """ 2219 drivers_sections = self._profiled( 2220 self.xml_data.get_drivers() 2221 ) 2222 result = [] 2223 if drivers_sections: 2224 for driver in drivers_sections: 2225 for file_section in driver.get_file(): 2226 result.append(file_section.get_name()) 2227 return result 2228 2229 def get_strip_list(self, section_type: str) -> List: 2230 """ 2231 List of strip names matching the given section type 2232 and profiles 2233 2234 :param str section_type: type name from packages section 2235 2236 :return: strip names 2237 2238 :rtype: list 2239 """ 2240 strip_sections = self._profiled( 2241 self.xml_data.get_strip() 2242 ) 2243 result = [] 2244 if strip_sections: 2245 for strip in strip_sections: 2246 if strip.get_type() == section_type: 2247 for file_section in strip.get_file(): 2248 result.append(file_section.get_name()) 2249 return result 2250 2251 def get_strip_files_to_delete(self) -> List: 2252 """ 2253 Items to delete from strip section 2254 2255 :return: item names 2256 2257 :rtype: list 2258 """ 2259 return self.get_strip_list('delete') 2260 2261 def get_strip_tools_to_keep(self) -> List: 2262 """ 2263 Tools to keep from strip section 2264 2265 :return: tool names 2266 2267 :rtype: list 2268 """ 2269 return self.get_strip_list('tools') 2270 2271 def get_strip_libraries_to_keep(self) -> List: 2272 """ 2273 Libraries to keep from strip section 2274 2275 :return: librarie names 2276 2277 :rtype: list 2278 """ 2279 return self.get_strip_list('libs') 2280 2281 def get_include_section_reference_file_names(self) -> List[str]: 2282 """ 2283 List of all <include> section file name references 2284 2285 :return: List[str] 2286 2287 :rtype: list 2288 """ 2289 include_files = [] 2290 for include in self.xml_data.get_include(): 2291 include_files.append(include.get_from()) 2292 return include_files 2293 2294 def get_repository_sections(self) -> List: 2295 """ 2296 List of all repository sections for the selected profiles that 2297 matches the host architecture 2298 2299 :return: <repository> section reference(s) 2300 2301 :rtype: list 2302 """ 2303 repository_list = [] 2304 for repository in self._profiled(self.xml_data.get_repository()): 2305 if self.repository_matches_host_architecture(repository): 2306 repository_list.append(repository) 2307 return repository_list 2308 2309 def get_containers_sections(self) -> List: 2310 """ 2311 List of all containers sections for the selected profiles that 2312 matches the host architecture 2313 2314 :return: <containers> section reference(s) 2315 2316 :rtype: list 2317 """ 2318 containers_list = [] 2319 for containers in self._profiled(self.xml_data.get_containers()): 2320 if self.containers_matches_host_architecture(containers): 2321 containers_list.append(containers) 2322 return containers_list 2323 2324 def get_repository_sections_used_for_build(self) -> List: 2325 """ 2326 List of all repositorys sections used to build the image and 2327 matching configured profiles. 2328 2329 :return: <repository> section reference(s) 2330 2331 :rtype: list 2332 """ 2333 repos = self.get_repository_sections() 2334 return list( 2335 repo for repo in repos if not repo.get_imageonly() 2336 ) 2337 2338 def get_repository_sections_used_in_image(self) -> List: 2339 """ 2340 List of all repositorys sections to be configured in the resulting 2341 image matching configured profiles. 2342 2343 :return: <repository> section reference(s) 2344 2345 :rtype: list 2346 """ 2347 repos = self.get_repository_sections() 2348 return list( 2349 repo for repo in repos 2350 if repo.get_imageinclude() or repo.get_imageonly() 2351 ) 2352 2353 def delete_repository_sections(self) -> None: 2354 """ 2355 Delete all repository sections matching configured profiles 2356 """ 2357 self.xml_data.set_repository([]) 2358 2359 def delete_repository_sections_used_for_build(self) -> None: 2360 """ 2361 Delete all repository sections used to build the image matching 2362 configured profiles 2363 """ 2364 used_for_build = self.get_repository_sections_used_for_build() 2365 all_repos = self.get_repository_sections() 2366 self.xml_data.set_repository( 2367 [ 2368 repo for repo in all_repos if repo not in used_for_build 2369 ] 2370 ) 2371 2372 def get_repositories_signing_keys(self) -> List[str]: 2373 """ 2374 Get list of signing keys specified on the repositories 2375 """ 2376 key_file_list: List[str] = [] 2377 release_version = self.get_release_version() 2378 release_vars = [ 2379 '$releasever', 2380 '${releasever}' 2381 ] 2382 for repository in self.get_repository_sections() or []: 2383 for signing in repository.get_source().get_signing() or []: 2384 normalized_key_url = Uri(signing.get_key()).translate() 2385 if release_version: 2386 for release_var in release_vars: 2387 if release_var in normalized_key_url: 2388 normalized_key_url = normalized_key_url.replace( 2389 release_var, release_version 2390 ) 2391 if normalized_key_url not in key_file_list: 2392 key_file_list.append(normalized_key_url) 2393 return key_file_list 2394 2395 def set_repository( 2396 self, repo_source: str, repo_type: str, repo_alias: str, 2397 repo_prio: str, repo_imageinclude: bool = False, 2398 repo_package_gpgcheck: Optional[bool] = None, 2399 repo_signing_keys: List[str] = [], components: str = None, 2400 distribution: str = None, repo_gpgcheck: Optional[bool] = None, 2401 repo_sourcetype: str = None 2402 ) -> None: 2403 """ 2404 Overwrite repository data of the first repository 2405 2406 :param str repo_source: repository URI 2407 :param str repo_type: type name defined by schema 2408 :param str repo_alias: alias name 2409 :param str repo_prio: priority number, package manager specific 2410 :param bool repo_imageinclude: setup repository inside of the image 2411 :param bool repo_package_gpgcheck: enable/disable package gpg checks 2412 :param list repo_signing_keys: list of signing key file names 2413 :param str components: component names for debian repos 2414 :param str distribution: base distribution name for debian repos 2415 :param bool repo_gpgcheck: enable/disable repo gpg checks 2416 """ 2417 repository_sections = self.get_repository_sections() 2418 if repository_sections: 2419 repository = repository_sections[0] 2420 if repo_alias: 2421 repository.set_alias(repo_alias) 2422 if repo_type: 2423 repository.set_type(repo_type) 2424 if repo_source: 2425 repository.get_source().set_path(repo_source) 2426 if repo_prio: 2427 repository.set_priority(int(repo_prio)) 2428 if repo_imageinclude: 2429 repository.set_imageinclude(repo_imageinclude) 2430 if repo_package_gpgcheck is not None: 2431 repository.set_package_gpgcheck(repo_package_gpgcheck) 2432 if repo_signing_keys: 2433 repository.get_source().set_signing( 2434 [xml_parse.signing(key=k) for k in repo_signing_keys] 2435 ) 2436 if components: 2437 repository.set_components(components) 2438 if distribution: 2439 repository.set_distribution(distribution) 2440 if repo_gpgcheck is not None: 2441 repository.set_repository_gpgcheck(repo_gpgcheck) 2442 if repo_sourcetype: 2443 repository.set_sourcetype(repo_sourcetype) 2444 2445 def add_repository( 2446 self, repo_source: str, repo_type: str, repo_alias: str = None, 2447 repo_prio: str = '', repo_imageinclude: bool = False, 2448 repo_package_gpgcheck: Optional[bool] = None, 2449 repo_signing_keys: List[str] = [], components: str = None, 2450 distribution: str = None, repo_gpgcheck: Optional[bool] = None, 2451 repo_sourcetype: str = None 2452 ) -> None: 2453 """ 2454 Add a new repository section at the end of the list 2455 2456 :param str repo_source: repository URI 2457 :param str repo_type: type name defined by schema 2458 :param str repo_alias: alias name 2459 :param str repo_prio: priority number, package manager specific 2460 :param bool repo_imageinclude: setup repository inside of the image 2461 :param bool repo_package_gpgcheck: enable/disable package gpg checks 2462 :param list repo_signing_keys: list of signing key file names 2463 :param str components: component names for debian repos 2464 :param str distribution: base distribution name for debian repos 2465 :param bool repo_gpgcheck: enable/disable repo gpg checks 2466 """ 2467 priority_number: Optional[int] = None 2468 try: 2469 priority_number = int(repo_prio) 2470 except Exception: 2471 pass 2472 2473 self.xml_data.add_repository( 2474 xml_parse.repository( 2475 type_=repo_type, 2476 alias=repo_alias, 2477 priority=priority_number, 2478 source=xml_parse.source( 2479 path=repo_source, 2480 signing=[ 2481 xml_parse.signing(key=k) for k in repo_signing_keys 2482 ] 2483 ), 2484 imageinclude=repo_imageinclude, 2485 package_gpgcheck=repo_package_gpgcheck, 2486 repository_gpgcheck=repo_gpgcheck, 2487 components=components, 2488 distribution=distribution, 2489 sourcetype=repo_sourcetype 2490 ) 2491 ) 2492 2493 def add_certificate(self, cert_file: str, target_distribution: str) -> None: 2494 """ 2495 Add <certificate name="cert_file"> to main <certificates> section 2496 The main section will be created if it does not exist. Also 2497 setup the target_distribution in the resulting main section. 2498 """ 2499 certificates_section = self._profiled( 2500 self.xml_data.get_certificates() 2501 ) 2502 if not certificates_section: 2503 self.xml_data.set_certificates( 2504 [ 2505 xml_parse.certificates( 2506 target_distribution=target_distribution, 2507 certificate=[xml_parse.certificate(name=cert_file)] 2508 ) 2509 ] 2510 ) 2511 else: 2512 certificates_section[0].set_target_distribution( 2513 target_distribution 2514 ) 2515 certificates_section[0].add_certificate( 2516 xml_parse.certificate( 2517 name=cert_file 2518 ) 2519 ) 2520 2521 def get_certificates(self) -> List[str]: 2522 """ 2523 Read list of certificates 2524 """ 2525 cert_list = [] 2526 certificates_section = self._profiled( 2527 self.xml_data.get_certificates() 2528 ) 2529 if certificates_section: 2530 for certificate in certificates_section[0].get_certificate(): 2531 cert_list.append(certificate.get_name()) 2532 return sorted(list(set(cert_list))) 2533 2534 def get_certificates_target_distribution(self) -> str: 2535 """ 2536 Read CA target distribution 2537 """ 2538 target_distribution = '' 2539 certificates_section = self._profiled( 2540 self.xml_data.get_certificates() 2541 ) 2542 if certificates_section: 2543 target_distribution = \ 2544 certificates_section[0].get_target_distribution() 2545 return target_distribution 2546 2547 def resolve_this_path(self) -> None: 2548 """ 2549 Resolve any this:// repo source path into the path 2550 representing the target inside of the image description 2551 directory 2552 """ 2553 for repository in self.get_repository_sections() or []: 2554 repo_source = repository.get_source() 2555 repo_path = repo_source.get_path() 2556 if repo_path.startswith('this://'): 2557 repo_path = repo_path.replace('this://', '') 2558 repo_source.set_path( 2559 'dir://{0}'.format( 2560 os.path.realpath( 2561 os.path.join( 2562 self.xml_data.description_dir, repo_path 2563 ) 2564 ) 2565 ) 2566 ) 2567 2568 def copy_displayname(self, target_state: Any) -> None: 2569 """ 2570 Copy image displayname from this xml state to the target xml state 2571 2572 :param object target_state: XMLState instance 2573 """ 2574 displayname = self.xml_data.get_displayname() 2575 if displayname: 2576 target_state.xml_data.set_displayname(displayname) 2577 2578 def copy_name(self, target_state: Any) -> None: 2579 """ 2580 Copy image name from this xml state to the target xml state 2581 2582 :param object target_state: XMLState instance 2583 """ 2584 target_state.xml_data.set_name( 2585 self.xml_data.get_name() 2586 ) 2587 2588 def copy_drivers_sections(self, target_state: Any) -> None: 2589 """ 2590 Copy drivers sections from this xml state to the target xml state 2591 2592 :param object target_state: XMLState instance 2593 """ 2594 drivers_sections = self._profiled( 2595 self.xml_data.get_drivers() 2596 ) 2597 if drivers_sections: 2598 for drivers_section in drivers_sections: 2599 target_state.xml_data.add_drivers(drivers_section) 2600 2601 def copy_systemdisk_section(self, target_state: Any) -> None: 2602 """ 2603 Copy systemdisk sections from this xml state to the target xml state 2604 2605 :param object target_state: XMLState instance 2606 """ 2607 systemdisk_section = self.get_build_type_system_disk_section() 2608 if systemdisk_section: 2609 target_state.build_type.set_systemdisk( 2610 [systemdisk_section] 2611 ) 2612 2613 def copy_strip_sections(self, target_state: Any) -> None: 2614 """ 2615 Copy strip sections from this xml state to the target xml state 2616 2617 :param object target_state: XMLState instance 2618 """ 2619 strip_sections = self._profiled( 2620 self.xml_data.get_strip() 2621 ) 2622 if strip_sections: 2623 for strip_section in strip_sections: 2624 target_state.xml_data.add_strip(strip_section) 2625 2626 def copy_machine_section(self, target_state: Any) -> None: 2627 """ 2628 Copy machine sections from this xml state to the target xml state 2629 2630 :param object target_state: XMLState instance 2631 """ 2632 machine_section = self.get_build_type_machine_section() 2633 if machine_section: 2634 target_state.build_type.set_machine( 2635 [machine_section] 2636 ) 2637 2638 def copy_bootloader_section(self, target_state: Any) -> None: 2639 """ 2640 Copy bootloader section from this xml state to the target xml state 2641 2642 :param object target_state: XMLState instance 2643 """ 2644 bootloader_section = self.get_build_type_bootloader_section() 2645 if bootloader_section: 2646 target_state.build_type.set_bootloader( 2647 [bootloader_section] 2648 ) 2649 2650 def copy_oemconfig_section(self, target_state: Any) -> None: 2651 """ 2652 Copy oemconfig sections from this xml state to the target xml state 2653 2654 :param object target_state: XMLState instance 2655 """ 2656 oemconfig_section = self.get_build_type_oemconfig_section() 2657 if oemconfig_section: 2658 target_state.build_type.set_oemconfig( 2659 [oemconfig_section] 2660 ) 2661 2662 def copy_repository_sections( 2663 self, target_state: Any, wipe: bool = False 2664 ) -> None: 2665 """ 2666 Copy repository sections from this xml state to the target xml state 2667 2668 :param object target_state: XMLState instance 2669 :param bool wipe: delete all repos in target prior to copy 2670 """ 2671 repository_sections = self._profiled( 2672 self.xml_data.get_repository() 2673 ) 2674 if repository_sections: 2675 if wipe: 2676 target_state.xml_data.set_repository([]) 2677 for repository_section in repository_sections: 2678 repository_copy = copy.deepcopy(repository_section) 2679 # profiles are not copied because they might not exist 2680 # in the target description 2681 repository_copy.set_profiles(None) 2682 target_state.xml_data.add_repository(repository_copy) 2683 2684 def copy_preferences_subsections( 2685 self, section_names: List, target_state: Any 2686 ) -> None: 2687 """ 2688 Copy subsections of the preferences sections, matching given 2689 section names, from this xml state to the target xml state 2690 2691 :param list section_names: preferences subsection names 2692 :param object target_state: XMLState instance 2693 """ 2694 target_preferences_sections = target_state.get_preferences_sections() 2695 if target_preferences_sections: 2696 target_preferences_section = target_preferences_sections[0] 2697 for preferences_section in self.get_preferences_sections(): 2698 for section_name in section_names: 2699 get_section_method = getattr( 2700 preferences_section, 'get_' + section_name 2701 ) 2702 section = get_section_method() 2703 if section: 2704 set_section_method = getattr( 2705 target_preferences_section, 'set_' + section_name 2706 ) 2707 set_section_method(section) 2708 2709 def copy_build_type_attributes( 2710 self, attribute_names: List, target_state: Any 2711 ) -> None: 2712 """ 2713 Copy specified attributes from this build type section to the 2714 target xml state build type section 2715 2716 :param list attribute_names: type section attributes 2717 :param object target_state: XMLState instance 2718 """ 2719 for attribute in attribute_names: 2720 get_type_method = getattr( 2721 self.build_type, 'get_' + attribute 2722 ) 2723 attribute_value = get_type_method() 2724 if attribute_value: 2725 set_type_method = getattr( 2726 target_state.build_type, 'set_' + attribute 2727 ) 2728 set_type_method(attribute_value) 2729 2730 def copy_bootincluded_packages(self, target_state: Any) -> None: 2731 """ 2732 Copy packages marked as bootinclude to the packages 2733 type=bootstrap section in the target xml state. The package 2734 will also be removed from the packages type=delete section 2735 in the target xml state if present there 2736 2737 :param object target_state: XMLState instance 2738 """ 2739 target_packages_sections = \ 2740 target_state.get_bootstrap_packages_sections() 2741 if target_packages_sections: 2742 target_packages_section = \ 2743 target_packages_sections[0] 2744 package_names_added = [] 2745 packages_sections = self.get_packages_sections( 2746 ['image', 'bootstrap', self.get_build_type_name()] 2747 ) 2748 package_list = self.get_package_sections( 2749 packages_sections 2750 ) 2751 if package_list: 2752 for package in package_list: 2753 if package.package_section.get_bootinclude(): 2754 target_packages_section.add_package( 2755 xml_parse.package( 2756 name=package.package_section.get_name() 2757 ) 2758 ) 2759 package_names_added.append( 2760 package.package_section.get_name() 2761 ) 2762 delete_packages_sections = target_state.get_packages_sections( 2763 ['delete'] 2764 ) 2765 package_list = self.get_package_sections( 2766 delete_packages_sections 2767 ) 2768 if package_list: 2769 for package in package_list: 2770 package_name = package.package_section.get_name() 2771 if package_name in package_names_added: 2772 package.packages_section.package.remove( 2773 package.package_section 2774 ) 2775 2776 def copy_bootincluded_archives(self, target_state: Any) -> None: 2777 """ 2778 Copy archives marked as bootinclude to the packages type=bootstrap 2779 section in the target xml state 2780 2781 :param object target_state: XMLState instance 2782 """ 2783 target_bootstrap_packages_sections = \ 2784 target_state.get_bootstrap_packages_sections() 2785 if target_bootstrap_packages_sections: 2786 target_bootstrap_packages_section = \ 2787 target_bootstrap_packages_sections[0] 2788 packages_sections = self.get_packages_sections( 2789 ['image', 'bootstrap', self.get_build_type_name()] 2790 ) 2791 for packages_section in packages_sections: 2792 archive_list = packages_section.get_archive() 2793 if archive_list: 2794 for archive in archive_list: 2795 if archive.get_bootinclude(): 2796 target_bootstrap_packages_section.add_archive( 2797 xml_parse.archive( 2798 name=archive.get_name() 2799 ) 2800 ) 2801 2802 def copy_bootdelete_packages(self, target_state: Any) -> None: 2803 """ 2804 Copy packages marked as bootdelete to the packages type=delete 2805 section in the target xml state 2806 2807 :param object target_state: XMLState instance 2808 """ 2809 target_delete_packages_sections = target_state.get_packages_sections( 2810 ['delete'] 2811 ) 2812 if not target_delete_packages_sections: 2813 target_delete_packages_sections = [ 2814 xml_parse.packages(type_='delete') 2815 ] 2816 target_state.xml_data.add_packages( 2817 target_delete_packages_sections[0] 2818 ) 2819 2820 target_delete_packages_section = \ 2821 target_delete_packages_sections[0] 2822 packages_sections = self.get_packages_sections( 2823 ['image', 'bootstrap', self.get_build_type_name()] 2824 ) 2825 package_list = self.get_package_sections( 2826 packages_sections 2827 ) 2828 if package_list: 2829 for package in package_list: 2830 if package.package_section.get_bootdelete(): 2831 target_delete_packages_section.add_package( 2832 xml_parse.package( 2833 name=package.package_section.get_name() 2834 ) 2835 ) 2836 2837 def get_distribution_name_from_boot_attribute(self) -> str: 2838 """ 2839 Extract the distribution name from the boot attribute of the 2840 build type section. 2841 2842 If no boot attribute is configured or the contents does not 2843 match the kiwi defined naming schema for boot image descriptions, 2844 an exception is thrown 2845 2846 :return: lowercase distribution name 2847 2848 :rtype: str 2849 """ 2850 boot_attribute = self.build_type.get_boot() 2851 if not boot_attribute: 2852 raise KiwiDistributionNameError( 2853 'No boot attribute to extract distribution name from found' 2854 ) 2855 boot_attribute_format = '^.*-(.*)$' 2856 boot_attribute_expression = re.match( 2857 boot_attribute_format, boot_attribute 2858 ) 2859 if not boot_attribute_expression: 2860 raise KiwiDistributionNameError( 2861 'Boot attribute "%s" does not match expected format %s' % 2862 (boot_attribute, boot_attribute_format) 2863 ) 2864 return boot_attribute_expression.group(1).lower() 2865 2866 def get_fs_mount_option_list(self) -> List: 2867 """ 2868 List of root filesystem mount options 2869 2870 The list contains one element with the information from the 2871 fsmountoptions attribute. The value there is passed along to 2872 the -o mount option 2873 2874 :return: max one element list with mount option string 2875 2876 :rtype: list 2877 """ 2878 option_list = [] 2879 mount_options = self.build_type.get_fsmountoptions() 2880 if mount_options: 2881 option_list = [mount_options] 2882 2883 return option_list 2884 2885 def get_fs_create_option_list(self) -> List: 2886 """ 2887 List of root filesystem creation options 2888 2889 The list contains elements with the information from the 2890 fscreateoptions attribute string that got split into its 2891 substring components 2892 2893 :return: list with create options 2894 2895 :rtype: list 2896 """ 2897 option_list = [] 2898 create_options = self.build_type.get_fscreateoptions() 2899 if create_options: 2900 option_list = create_options.split() 2901 2902 return option_list 2903 2904 def get_luks_credentials(self) -> Optional[str]: 2905 """ 2906 Return key or passphrase credentials to open the luks pool 2907 2908 :return: data 2909 2910 :rtype: str 2911 """ 2912 data = self.build_type.get_luks() 2913 if data: 2914 keyfile_name = None 2915 try: 2916 # try to interpret data as an URI 2917 uri = Uri(data) 2918 if not uri.is_remote(): 2919 keyfile_name = uri.translate() 2920 except Exception: 2921 # this doesn't look like a valid URI, continue as just data 2922 pass 2923 if keyfile_name: 2924 try: 2925 with open(keyfile_name) as keyfile: 2926 return keyfile.read() 2927 except Exception as issue: 2928 raise KiwiFileAccessError( 2929 f'Failed to read from {keyfile_name!r}: {issue}' 2930 ) 2931 return data 2932 2933 def get_luks_format_options(self) -> List[str]: 2934 """ 2935 Return list of luks format options 2936 2937 :return: list of options 2938 2939 :rtype: list 2940 """ 2941 result = [] 2942 luksversion = self.build_type.get_luks_version() 2943 luksformat = self.build_type.get_luksformat() 2944 luks_pbkdf = self.build_type.get_luks_pbkdf() 2945 if luksversion: 2946 result.append('--type') 2947 result.append(luksversion) 2948 if luksformat: 2949 for option in luksformat[0].get_option(): 2950 result.append(option.get_name()) 2951 if option.get_value(): 2952 result.append(option.get_value()) 2953 if luks_pbkdf: 2954 # Allow to override the pbkdf algorithm that cryptsetup 2955 # uses by default. Cryptsetup may use argon2i by default, 2956 # which is not supported by all bootloaders. 2957 result.append('--pbkdf') 2958 result.append(luks_pbkdf) 2959 return result 2960 2961 def get_derived_from_image_uri(self) -> List[Uri]: 2962 """ 2963 Uri object(s) of derived image if configured 2964 2965 Specific image types can be based on one ore more derived 2966 images. This method returns the location of this image(s) 2967 when configured in the XML description 2968 2969 :return: List of Uri instances 2970 2971 :rtype: list 2972 """ 2973 image_uris = [] 2974 derived_images = self.build_type.get_derived_from() 2975 if derived_images: 2976 for derived_image in derived_images.split(','): 2977 image_uris.append( 2978 Uri(derived_image, repo_type='container') 2979 ) 2980 return image_uris 2981 2982 def set_derived_from_image_uri(self, uri: str) -> None: 2983 """ 2984 Set derived_from attribute to a new value 2985 2986 In order to set a new value the derived_from attribute 2987 must be already present in the image configuration 2988 2989 :param str uri: URI 2990 """ 2991 if self.build_type.get_derived_from(): 2992 self.build_type.set_derived_from(uri) 2993 else: 2994 message = dedent('''\n 2995 No derived_from attribute configured in image <type> 2996 2997 In order to set the uri {0} as base container reference 2998 an initial derived_from attribute must be set in the 2999 type section 3000 ''') 3001 log.warning(message.format(uri)) 3002 3003 def set_root_partition_uuid(self, uuid: str) -> None: 3004 """ 3005 Store PARTUUID provided in uuid as state information 3006 3007 :param str uuid: PARTUUID 3008 """ 3009 self.root_partition_uuid = uuid 3010 3011 def get_root_partition_uuid(self) -> Optional[str]: 3012 """ 3013 Return preserved PARTUUID 3014 """ 3015 return self.root_partition_uuid 3016 3017 def set_root_filesystem_uuid(self, uuid: str) -> None: 3018 """ 3019 Store UUID provided in uuid as state information 3020 3021 :param str uuid: UUID 3022 """ 3023 self.root_filesystem_uuid = uuid 3024 3025 def get_root_filesystem_uuid(self) -> Optional[str]: 3026 """ 3027 Return preserved UUID 3028 """ 3029 return self.root_filesystem_uuid 3030 3031 @staticmethod 3032 def get_archives_target_dirs( 3033 packages_sections_names: Optional[List[xml_parse.packages]] 3034 ) -> Dict: 3035 """ 3036 Dict of archive names and target dirs for packages section(s), if any 3037 :return: archive names and its target dir 3038 :rtype: dict 3039 """ 3040 result = {} 3041 if packages_sections_names: 3042 for package_section_name in packages_sections_names: 3043 for archive in package_section_name.get_archive(): 3044 result[archive.get_name().strip()] = archive.get_target_dir() 3045 3046 return result 3047 3048 def get_bootstrap_archives_target_dirs(self) -> Dict: 3049 """ 3050 Dict of archive names and target dirs from the type="bootstrap" 3051 packages section(s) 3052 :return: archive names and its target dir 3053 :rtype: dict 3054 """ 3055 return self.get_archives_target_dirs( 3056 self.get_packages_sections(['bootstrap']) 3057 ) 3058 3059 def get_system_archives_target_dirs(self) -> Dict: 3060 """ 3061 Dict of archive names and its target dir from the packages sections matching 3062 type="image" and type=build_type 3063 :return: archive names and its target dir 3064 :rtype: dict 3065 """ 3066 return self.get_archives_target_dirs( 3067 self.get_packages_sections(['image', self.get_build_type_name()]) 3068 ) 3069 3070 def _used_profiles(self, profiles=None): 3071 """ 3072 return list of profiles to use. The method looks up the 3073 profiles section in the XML description and searches for 3074 profiles matching the architecture. If no arch specifier 3075 is set the profile is considered to be valid for any arch 3076 3077 If the profiles argument is not set only profiles 3078 marked with the attribute import=true will be selected. 3079 Profiles specified in the argument will take the highest 3080 priority and causes to skip the lookup of import profiles 3081 in the XML description 3082 3083 :param list profiles: selected profile names 3084 """ 3085 available_profiles = dict() 3086 import_profiles = [] 3087 for profiles_section in self.xml_data.get_profiles(): 3088 for profile in profiles_section.get_profile(): 3089 if self.profile_matches_host_architecture(profile): 3090 name = profile.get_name() 3091 available_profiles[name] = profile 3092 if profile.get_import(): 3093 import_profiles.append(name) 3094 3095 if not profiles: 3096 return import_profiles 3097 else: 3098 resolved_profiles = [] 3099 for profile in profiles: 3100 resolved_profiles += self._solve_profile_dependencies( 3101 profile, available_profiles, resolved_profiles 3102 ) 3103 return resolved_profiles 3104 3105 def _section_matches_host_architecture(self, section): 3106 architectures = section.get_arch() 3107 if architectures: 3108 if self.host_architecture not in architectures.split(','): 3109 return False 3110 return True 3111 3112 def _match_docker_base_data(self): 3113 container_config_section = self.get_build_type_containerconfig_section() 3114 container_base = {} 3115 if container_config_section: 3116 name = container_config_section.get_name() 3117 tag = container_config_section.get_tag() 3118 maintainer = container_config_section.get_maintainer() 3119 user = container_config_section.get_user() 3120 workingdir = container_config_section.get_workingdir() 3121 additional_names = container_config_section.get_additionalnames() 3122 if name: 3123 container_base['container_name'] = name 3124 3125 if tag: 3126 container_base['container_tag'] = tag 3127 3128 if additional_names: 3129 container_base['additional_names'] = additional_names.split(',') 3130 3131 if maintainer: 3132 container_base['maintainer'] = maintainer 3133 3134 if user: 3135 container_base['user'] = user 3136 3137 if workingdir: 3138 container_base['workingdir'] = workingdir 3139 3140 return container_base 3141 3142 def _match_docker_entrypoint(self): 3143 container_config_section = self.get_build_type_containerconfig_section() 3144 container_entry = {} 3145 if container_config_section: 3146 entrypoint = container_config_section.get_entrypoint() 3147 if entrypoint and entrypoint[0].get_execute(): 3148 container_entry['entry_command'] = [ 3149 entrypoint[0].get_execute() 3150 ] 3151 argument_list = entrypoint[0].get_argument() 3152 if argument_list: 3153 for argument in argument_list: 3154 container_entry['entry_command'].append( 3155 argument.get_name() 3156 ) 3157 elif entrypoint and entrypoint[0].get_clear(): 3158 container_entry['entry_command'] = [] 3159 return container_entry 3160 3161 def _match_docker_subcommand(self): 3162 container_config_section = self.get_build_type_containerconfig_section() 3163 container_subcommand = {} 3164 if container_config_section: 3165 subcommand = container_config_section.get_subcommand() 3166 if subcommand and subcommand[0].get_execute(): 3167 container_subcommand['entry_subcommand'] = [ 3168 subcommand[0].get_execute() 3169 ] 3170 argument_list = subcommand[0].get_argument() 3171 if argument_list: 3172 for argument in argument_list: 3173 container_subcommand['entry_subcommand'].append( 3174 argument.get_name() 3175 ) 3176 elif subcommand and subcommand[0].get_clear(): 3177 container_subcommand['entry_subcommand'] = [] 3178 return container_subcommand 3179 3180 def _match_docker_expose_ports(self): 3181 container_config_section = self.get_build_type_containerconfig_section() 3182 container_expose = {} 3183 if container_config_section: 3184 expose = container_config_section.get_expose() 3185 if expose and expose[0].get_port(): 3186 container_expose['expose_ports'] = [] 3187 for port in expose[0].get_port(): 3188 container_expose['expose_ports'].append( 3189 format(port.get_number()) 3190 ) 3191 return container_expose 3192 3193 def _match_docker_volumes(self): 3194 container_config_section = self.get_build_type_containerconfig_section() 3195 container_volumes = {} 3196 if container_config_section: 3197 volumes = container_config_section.get_volumes() 3198 if volumes and volumes[0].get_volume(): 3199 container_volumes['volumes'] = [] 3200 for volume in volumes[0].get_volume(): 3201 container_volumes['volumes'].append(volume.get_name()) 3202 return container_volumes 3203 3204 def _match_docker_stopsignal(self) -> dict: 3205 container_config_section = self.get_build_type_containerconfig_section() 3206 container_stopsignal = {} 3207 if container_config_section: 3208 stopsignal_section = container_config_section.get_stopsignal() 3209 if stopsignal_section: 3210 container_stopsignal['stopsignal'] = stopsignal_section[0] 3211 return container_stopsignal 3212 3213 def _match_docker_environment(self): 3214 container_config_section = self.get_build_type_containerconfig_section() 3215 container_env = {} 3216 if container_config_section: 3217 environment = container_config_section.get_environment() 3218 if environment and environment[0].get_env(): 3219 container_env['environment'] = {} 3220 for env in environment[0].get_env(): 3221 container_env['environment'][env.get_name()] = \ 3222 env.get_value() 3223 return container_env 3224 3225 def _match_docker_labels(self): 3226 container_config_section = self.get_build_type_containerconfig_section() 3227 container_labels = {} 3228 if container_config_section: 3229 labels = container_config_section.get_labels() 3230 if labels and labels[0].get_label(): 3231 container_labels['labels'] = {} 3232 for label in labels[0].get_label(): 3233 container_labels['labels'][label.get_name()] = \ 3234 label.get_value() 3235 return container_labels 3236 3237 def _match_docker_history(self): 3238 container_config_section = self.get_build_type_containerconfig_section() 3239 container_history = {} 3240 if container_config_section: 3241 history = container_config_section.get_history() 3242 if history: 3243 container_history['history'] = {} 3244 if history[0].get_created_by(): 3245 container_history['history']['created_by'] = \ 3246 history[0].get_created_by() 3247 if history[0].get_author(): 3248 container_history['history']['author'] = \ 3249 history[0].get_author() 3250 if history[0].get_launcher(): 3251 container_history['history']['launcher'] = \ 3252 history[0].get_launcher() 3253 if history[0].get_application_id(): 3254 container_history['history']['application_id'] = \ 3255 history[0].get_application_id() 3256 if history[0].get_package_version(): 3257 container_history['history']['package_version'] = \ 3258 history[0].get_package_version() 3259 container_history['history']['comment'] = \ 3260 history[0].get_valueOf_() 3261 return container_history 3262 3263 def _solve_profile_dependencies( 3264 self, profile, available_profiles, current_profiles 3265 ): 3266 if profile not in available_profiles: 3267 raise KiwiProfileNotFound( 3268 'profile {0} not found for host arch {1}'.format( 3269 profile, self.host_architecture 3270 ) 3271 ) 3272 profiles_to_add = [] 3273 if profile not in current_profiles: 3274 profiles_to_add.append(profile) 3275 for required in available_profiles[profile].get_requires(): 3276 if self.requires_matches_host_architecture(required): 3277 if required.get_profile() not in current_profiles: 3278 profiles_to_add += self._solve_profile_dependencies( 3279 required.get_profile(), available_profiles, 3280 current_profiles + profiles_to_add 3281 ) 3282 return profiles_to_add 3283 3284 def _build_type_section(self, build_type=None): 3285 """ 3286 find type section matching build type and profiles or default 3287 """ 3288 # lookup all preferences sections for selected profiles 3289 image_type_sections = [] 3290 for preferences in self.get_preferences_sections(): 3291 image_type_sections += preferences.get_type() 3292 3293 # lookup if build type matches provided type 3294 if build_type: 3295 for image_type in image_type_sections: 3296 if build_type == image_type.get_image(): 3297 return image_type 3298 raise KiwiTypeNotFound( 3299 'Build type {0!r} not found for applied profiles: {1!r}'.format( 3300 build_type, self.profiles 3301 ) 3302 ) 3303 3304 # lookup if build type matches primary type 3305 for image_type in image_type_sections: 3306 if image_type.get_primary(): 3307 return image_type 3308 3309 # build type is first type section in XML sequence 3310 if image_type_sections: 3311 return image_type_sections[0] 3312 raise KiwiTypeNotFound( 3313 'No build type defined with applied profiles: {0!r}'.format( 3314 self.profiles 3315 ) 3316 ) 3317 3318 def _profiled(self, xml_abstract): 3319 """ 3320 return only those sections matching the instance stored 3321 profile list from the given XML abstract. Sections without 3322 a profile are wildcard sections and will be used in any 3323 case 3324 """ 3325 result = [] 3326 for section in xml_abstract: 3327 profiles = section.get_profiles() 3328 if profiles: 3329 for profile in profiles.split(','): 3330 if self.profiles and profile in self.profiles: 3331 result.append(section) 3332 break 3333 else: 3334 result.append(section) 3335 return result 3336 3337 def _to_volume_name(self, name): 3338 name = name.strip() 3339 name = re.sub(r'^\/+', r'', name) 3340 name = name.replace('/', '_') 3341 return name 3342 3343 def _to_mega_byte(self, size): 3344 value = re.search(r'(\d+)([MG]*)', format(size)) 3345 if value: 3346 number = value.group(1) 3347 unit = value.group(2) 3348 if unit == 'G': 3349 return int(number) * 1024 3350 else: 3351 return int(number) 3352 else: 3353 return size
Implements methods to get stateful information from the XML data
Parameters
- object xml_data: parse result from XMLDescription.load()
- list profiles: list of used profiles
- object build_type: build
section reference
113 def __init__( 114 self, xml_data: Any, profiles: List = None, 115 build_type: Any = None 116 ): 117 self.root_partition_uuid: Optional[str] = None 118 self.root_filesystem_uuid: Optional[str] = None 119 self.host_architecture = defaults.PLATFORM_MACHINE 120 self.xml_data = xml_data 121 self.profiles = self._used_profiles(profiles) 122 self.build_type = self._build_type_section( 123 build_type 124 ) 125 self.resolve_this_path()
127 def get_preferences_sections(self) -> List: 128 """ 129 All preferences sections for the selected profiles that match the 130 host architecture 131 132 :return: list of <preferences> section reference(s) 133 134 :rtype: list 135 """ 136 preferences_list = [] 137 for preferences in self._profiled(self.xml_data.get_preferences()): 138 if self.preferences_matches_host_architecture(preferences): 139 preferences_list.append(preferences) 140 return preferences_list
All preferences sections for the selected profiles that match the host architecture
Returns
list of
section reference(s)
142 def get_description_section(self) -> description_type: 143 """ 144 The description section 145 146 :return: 147 description_type tuple providing the elements 148 author contact and specification 149 150 :rtype: tuple 151 """ 152 description = self.xml_data.get_description()[0] 153 return description_type( 154 author=description.get_author()[0], 155 contact=description.get_contact()[0], 156 specification=description.get_specification()[0].strip() 157 )
The description section
Returns
description_type tuple providing the elements author contact and specification
159 def get_users_sections(self) -> List: 160 """ 161 All users sections for the selected profiles 162 163 :return: list of <users> section reference(s) 164 165 :rtype: list 166 """ 167 users = [] 168 for users_section in self._profiled(self.xml_data.get_users()): 169 if self.users_matches_host_architecture(users_section): 170 users.append(users_section) 171 return users
All users sections for the selected profiles
Returns
list of
section reference(s)
173 def get_build_type_bundle_format(self) -> str: 174 """ 175 Return bundle_format for build type 176 177 The bundle_format string is validated against the available 178 name tags from kiwi.system.result::result_name_tags. 179 180 :return: bundle format string 181 182 :rtype: str 183 """ 184 return self.build_type.get_bundle_format()
Return bundle_format for build type
The bundle_format string is validated against the available name tags from kiwi.system.result::result_name_tags.
Returns
bundle format string
186 def get_build_type_name(self) -> str: 187 """ 188 Default build type name 189 190 :return: Content of image attribute from build type 191 192 :rtype: str 193 """ 194 return self.build_type.get_image()
Default build type name
Returns
Content of image attribute from build type
196 def btrfs_default_volume_requested(self) -> bool: 197 """ 198 Check if setting a default volume for btrfs is requested 199 """ 200 if self.build_type.get_btrfs_set_default_volume() is False: 201 # Setting a default volume is explicitly switched off 202 return False 203 else: 204 # In any other case (True | None) a default volume 205 # is wanted and will be set 206 return True
Check if setting a default volume for btrfs is requested
208 def get_image_version(self) -> str: 209 """ 210 Image version from preferences section. 211 212 Multiple occurences of version in preferences sections are not 213 forbidden, however only the first version found defines the 214 final image version 215 216 :return: Content of <version> section 217 218 :rtype: str 219 """ 220 for preferences in self.get_preferences_sections(): 221 version = preferences.get_version() 222 if version: 223 return version[0] 224 return ''
Image version from preferences section.
Multiple occurences of version in preferences sections are not forbidden, however only the first version found defines the final image version
Returns
Content of
section
226 def get_initrd_system(self) -> str: 227 """ 228 Name of initrd system to use 229 230 Depending on the image type a specific initrd system is 231 either pre selected or free of choice according to the 232 XML type setup. 233 234 :return: 'dracut', 'kiwi' or 'none' 235 236 :rtype: str 237 """ 238 pre_selection_map = { 239 'vmx': 'dracut', 240 'oem': 'dracut', 241 'iso': 'dracut', 242 'kis': 'dracut', 243 'pxe': 'kiwi', 244 } 245 build_type = self.get_build_type_name() 246 default_initrd_system = pre_selection_map.get(build_type) or 'none' 247 248 if build_type == 'iso': 249 # iso type always use dracut as initrd system 250 return default_initrd_system 251 252 # Allow to choose for any other build type 253 return self.build_type.get_initrd_system() or default_initrd_system
Name of initrd system to use
Depending on the image type a specific initrd system is either pre selected or free of choice according to the XML type setup.
Returns
'dracut', 'kiwi' or 'none'
255 def get_locale(self) -> Optional[List]: 256 """ 257 Gets list of locale names if configured. Takes 258 the first locale setup from the existing preferences 259 sections into account. 260 261 :return: List of names or None 262 263 :rtype: list|None 264 """ 265 for preferences in self.get_preferences_sections(): 266 locale_section = preferences.get_locale() 267 if locale_section: 268 return locale_section[0].split(',') 269 return None
Gets list of locale names if configured. Takes the first locale setup from the existing preferences sections into account.
Returns
List of names or None
271 def get_rpm_locale(self) -> Optional[List]: 272 """ 273 Gets list of locale names to filter out by rpm 274 if rpm-locale-filtering is switched on the 275 the list always contains: [POSIX, C, C.UTF-8] 276 and is extended by the optionaly configured 277 locale 278 279 :return: List of names or None 280 281 :rtype: list|None 282 """ 283 if self.get_rpm_locale_filtering(): 284 rpm_locale = ['POSIX', 'C', 'C.UTF-8'] 285 configured_locale = self.get_locale() 286 if configured_locale: 287 for locale in configured_locale: 288 rpm_locale.append(locale) 289 return rpm_locale 290 return None
Gets list of locale names to filter out by rpm if rpm-locale-filtering is switched on the the list always contains: [POSIX, C, C.UTF-8] and is extended by the optionaly configured locale
Returns
List of names or None
292 def get_rpm_locale_filtering(self) -> bool: 293 """ 294 Gets the rpm-locale-filtering configuration flag. Returns 295 False if not present. 296 297 :return: True or False 298 299 :rtype: bool 300 """ 301 for preferences in self.get_preferences_sections(): 302 locale_filtering = preferences.get_rpm_locale_filtering() 303 if locale_filtering: 304 return locale_filtering[0] 305 return False
Gets the rpm-locale-filtering configuration flag. Returns False if not present.
Returns
True or False
307 def get_rpm_excludedocs(self) -> bool: 308 """ 309 Gets the rpm-excludedocs configuration flag. Returns 310 False if not present. 311 312 :return: True or False 313 314 :rtype: bool 315 """ 316 for preferences in self.get_preferences_sections(): 317 exclude_docs = preferences.get_rpm_excludedocs() 318 if exclude_docs: 319 return exclude_docs[0] 320 return False
Gets the rpm-excludedocs configuration flag. Returns False if not present.
Returns
True or False
322 def get_rpm_check_signatures(self) -> bool: 323 """ 324 Gets the rpm-check-signatures configuration flag. Returns 325 False if not present. 326 327 :return: True or False 328 329 :rtype: bool 330 """ 331 for preferences in self.get_preferences_sections(): 332 check_signatures = preferences.get_rpm_check_signatures() 333 if check_signatures: 334 return check_signatures[0] 335 return False
Gets the rpm-check-signatures configuration flag. Returns False if not present.
Returns
True or False
337 def get_package_manager(self) -> str: 338 """ 339 Get configured package manager from selected preferences section 340 341 :return: Content of the <packagemanager> section 342 343 :rtype: str 344 """ 345 for preferences in self.get_preferences_sections(): 346 package_manager = preferences.get_packagemanager() 347 if package_manager: 348 return package_manager[0] 349 return Defaults.get_default_package_manager()
Get configured package manager from selected preferences section
Returns
Content of the
section
351 def get_release_version(self) -> str: 352 """ 353 Get configured release version from selected preferences section 354 355 :return: Content of the <release-version> section or '' 356 357 :rtype: str 358 """ 359 release_version = '' 360 for preferences in self.get_preferences_sections(): 361 release_version = preferences.get_release_version() 362 if release_version: 363 release_version = release_version[0] 364 break 365 return release_version
Get configured release version from selected preferences section
Returns
Content of the
section or ''
367 def get_packages_sections(self, section_types: List) -> List: 368 """ 369 List of packages sections matching given section type(s) 370 371 :param list section_types: type name(s) from packages sections 372 373 :return: list of <packages> section reference(s) 374 375 :rtype: list 376 """ 377 result = [] 378 packages_sections = self._profiled( 379 self.xml_data.get_packages() 380 ) 381 for packages in packages_sections: 382 packages_type = packages.get_type() 383 if packages_type in section_types: 384 result.append(packages) 385 return result
List of packages sections matching given section type(s)
Parameters
- list section_types: type name(s) from packages sections
Returns
list of
section reference(s)
387 def requires_matches_host_architecture(self, requires: Any) -> bool: 388 """ 389 Tests if the given profile requires section is applicable for 390 the current host architecture. If no architecture is specified 391 within the section it is considered as a match returning True. 392 393 Note: The XML section pointer must provide an arch attribute 394 395 :param section: XML section object 396 397 :return: True or False 398 399 :rtype: bool 400 """ 401 return self._section_matches_host_architecture(requires)
Tests if the given profile requires section is applicable for the current host architecture. If no architecture is specified within the section it is considered as a match returning True.
Note: The XML section pointer must provide an arch attribute
Parameters
- section: XML section object
Returns
True or False
403 def volume_matches_host_architecture(self, volume: Any) -> bool: 404 """ 405 Tests if the given volume section is applicable for the current host 406 architecture. If no architecture is specified within the section 407 it is considered as a match returning True. 408 409 Note: The XML section pointer must provide an arch attribute 410 411 :param section: XML section object 412 413 :return: True or False 414 415 :rtype: bool 416 """ 417 return self._section_matches_host_architecture(volume)
Tests if the given volume section is applicable for the current host architecture. If no architecture is specified within the section it is considered as a match returning True.
Note: The XML section pointer must provide an arch attribute
Parameters
- section: XML section object
Returns
True or False
419 def package_matches_host_architecture(self, package: Any) -> bool: 420 """ 421 Tests if the given package section is applicable for the current host 422 architecture. If no architecture is specified within the section 423 it is considered as a match returning True. 424 425 Note: The XML section pointer must provide an arch attribute 426 427 :param section: XML section object 428 429 :return: True or False 430 431 :rtype: bool 432 """ 433 return self._section_matches_host_architecture(package)
Tests if the given package section is applicable for the current host architecture. If no architecture is specified within the section it is considered as a match returning True.
Note: The XML section pointer must provide an arch attribute
Parameters
- section: XML section object
Returns
True or False
435 def users_matches_host_architecture(self, users: Any) -> bool: 436 """ 437 Tests if the given users section is applicable for the current host 438 architecture. If no architecture is specified within the section 439 it is considered as a match returning True. 440 441 Note: The XML section pointer must provide an arch attribute 442 443 :param section: XML section object 444 445 :return: True or False 446 447 :rtype: bool 448 """ 449 return self._section_matches_host_architecture(users)
Tests if the given users section is applicable for the current host architecture. If no architecture is specified within the section it is considered as a match returning True.
Note: The XML section pointer must provide an arch attribute
Parameters
- section: XML section object
Returns
True or False
451 def collection_matches_host_architecture(self, collection: Any) -> bool: 452 """ 453 Tests if the given namedcollection section is applicable for 454 the current host architecture. If no architecture is specified 455 within the section it is considered as a match returning True. 456 457 Note: The XML section pointer must provide an arch attribute 458 459 :param section: XML section object 460 461 :return: True or False 462 463 :rtype: bool 464 """ 465 return self._section_matches_host_architecture(collection)
Tests if the given namedcollection section is applicable for the current host architecture. If no architecture is specified within the section it is considered as a match returning True.
Note: The XML section pointer must provide an arch attribute
Parameters
- section: XML section object
Returns
True or False
467 def profile_matches_host_architecture(self, profile: Any) -> bool: 468 """ 469 Tests if the given profile section is applicable for the current host 470 architecture. If no architecture is specified within the section 471 it is considered as a match returning True. 472 473 Note: The XML section pointer must provide an arch attribute 474 475 :param section: XML section object 476 477 :return: True or False 478 479 :rtype: bool 480 """ 481 return self._section_matches_host_architecture(profile)
Tests if the given profile section is applicable for the current host architecture. If no architecture is specified within the section it is considered as a match returning True.
Note: The XML section pointer must provide an arch attribute
Parameters
- section: XML section object
Returns
True or False
483 def preferences_matches_host_architecture(self, preferences: Any) -> bool: 484 """ 485 Tests if the given preferences section is applicable for the 486 current host architecture. If no architecture is specified within 487 the section it is considered as a match returning True. 488 489 Note: The XML section pointer must provide an arch attribute 490 491 :param section: XML section object 492 493 :return: True or False 494 495 :rtype: bool 496 """ 497 return self._section_matches_host_architecture(preferences)
Tests if the given preferences section is applicable for the current host architecture. If no architecture is specified within the section it is considered as a match returning True.
Note: The XML section pointer must provide an arch attribute
Parameters
- section: XML section object
Returns
True or False
499 def repository_matches_host_architecture(self, repository: Any) -> bool: 500 """ 501 Tests if the given repository section is applicable for the 502 current host architecture. If no architecture is specified within 503 the section it is considered as a match returning True. 504 505 Note: The XML section pointer must provide an arch attribute 506 507 :param section: XML section object 508 509 :return: True or False 510 511 :rtype: bool 512 """ 513 return self._section_matches_host_architecture(repository)
Tests if the given repository section is applicable for the current host architecture. If no architecture is specified within the section it is considered as a match returning True.
Note: The XML section pointer must provide an arch attribute
Parameters
- section: XML section object
Returns
True or False
515 def containers_matches_host_architecture(self, containers: Any) -> bool: 516 """ 517 Tests if the given containers section is applicable for the 518 current host architecture. If no arch attribute is provided in 519 the section it is considered as a match and returns: True. 520 521 :param section: XML section object 522 523 :return: True or False 524 525 :rtype: bool 526 """ 527 return self._section_matches_host_architecture(containers)
Tests if the given containers section is applicable for the current host architecture. If no arch attribute is provided in the section it is considered as a match and returns: True.
Parameters
- section: XML section object
Returns
True or False
529 def container_matches_host_architecture(self, container: Any) -> bool: 530 """ 531 Tests if the given container section is applicable for the 532 current host architecture. If no arch attribute is provided in 533 the section it is considered as a match and returns: True. 534 535 :param section: XML section object 536 537 :return: True or False 538 539 :rtype: bool 540 """ 541 return self._section_matches_host_architecture(container)
Tests if the given container section is applicable for the current host architecture. If no arch attribute is provided in the section it is considered as a match and returns: True.
Parameters
- section: XML section object
Returns
True or False
543 def get_package_sections( 544 self, packages_sections: List 545 ) -> List[package_type]: 546 """ 547 List of package sections from the given packages sections. 548 Each list element contains a tuple with the <package> section 549 reference and the <packages> section this package belongs to 550 551 If a package entry specfies an architecture, it is only taken if 552 the host architecture matches the configured architecture 553 554 :param list packages_sections: <packages> 555 556 :return: 557 Contains list of package_type tuples 558 559 .. code:: python 560 561 [package_type(packages_section=object, package_section=object)] 562 563 :rtype: list 564 """ 565 result = [] 566 if packages_sections: 567 for packages_section in packages_sections: 568 package_list = packages_section.get_package() 569 if package_list: 570 for package in package_list: 571 if self.package_matches_host_architecture(package): 572 result.append( 573 package_type( 574 packages_section=packages_section, 575 package_section=package 576 ) 577 ) 578 return result
List of package sections from the given packages sections.
Each list element contains a tuple with the
If a package entry specfies an architecture, it is only taken if the host architecture matches the configured architecture
Parameters
- list packages_sections:
Returns
Contains list of package_type tuples .. code:: python [package_type(packages_section=object, package_section=object)]
580 def get_to_become_deleted_packages(self, force: bool = True) -> List: 581 """ 582 List of package names from the type="delete" or type="uninstall" 583 packages section(s) 584 585 :param bool force: return "delete" type if True, "uninstall" type 586 otherwise 587 588 :return: package names 589 590 :rtype: list 591 """ 592 result = [] 593 to_become_deleted_packages_sections = self.get_packages_sections( 594 ['delete' if force else 'uninstall'] 595 ) 596 package_list = self.get_package_sections( 597 to_become_deleted_packages_sections 598 ) 599 if package_list: 600 for package in package_list: 601 result.append(package.package_section.get_name()) 602 return sorted(list(set(result)))
List of package names from the type="delete" or type="uninstall" packages section(s)
Parameters
- bool force: return "delete" type if True, "uninstall" type otherwise
Returns
package names
604 def get_bootstrap_packages_sections(self) -> List: 605 """ 606 List of packages sections matching type="bootstrap" 607 608 :return: list of <packages> section reference(s) 609 610 :rtype: list 611 """ 612 return self.get_packages_sections(['bootstrap'])
List of packages sections matching type="bootstrap"
Returns
list of
section reference(s)
614 def get_image_packages_sections(self) -> List: 615 """ 616 List of packages sections matching type="image" 617 618 :return: list of <packages> section reference(s) 619 620 :rtype: list 621 """ 622 return self.get_packages_sections(['image'])
List of packages sections matching type="image"
Returns
list of
section reference(s)
624 def get_bootstrap_packages(self, plus_packages: List = None) -> List: 625 """ 626 List of package names from the type="bootstrap" packages section(s) 627 628 The list gets the selected package manager appended 629 if there is a request to install packages inside of 630 the image via a chroot operation 631 632 :param list plus_packages: list of additional packages 633 634 :return: package names 635 636 :rtype: list 637 """ 638 result = [] 639 bootstrap_packages_sections = self.get_bootstrap_packages_sections() 640 package_list = self.get_package_sections( 641 bootstrap_packages_sections 642 ) 643 if package_list: 644 for package in package_list: 645 result.append(package.package_section.get_name().strip()) 646 if self.get_system_packages(): 647 package_manager_name = self.get_package_manager() 648 if package_manager_name == 'dnf4': 649 # The package name for dnf4 is just dnf. Thus 650 # the name must be adapted in this case 651 package_manager_name = 'dnf' 652 elif package_manager_name == 'apk': 653 package_manager_name = 'apk-tools' 654 result.append(package_manager_name) 655 if plus_packages: 656 result += plus_packages 657 return sorted(list(set(result)))
List of package names from the type="bootstrap" packages section(s)
The list gets the selected package manager appended if there is a request to install packages inside of the image via a chroot operation
Parameters
- list plus_packages: list of additional packages
Returns
package names
659 def get_system_packages(self) -> List: 660 """ 661 List of package names from the packages sections matching 662 type="image" and type=build_type 663 664 :return: package names 665 666 :rtype: list 667 """ 668 result = [] 669 image_packages_sections = self.get_packages_sections( 670 ['image', self.get_build_type_name()] 671 ) 672 package_list = self.get_package_sections( 673 image_packages_sections 674 ) 675 if package_list: 676 for package in package_list: 677 result.append(package.package_section.get_name().strip()) 678 return sorted(list(set(result)))
List of package names from the packages sections matching type="image" and type=build_type
Returns
package names
680 def get_bootstrap_files(self) -> Dict[str, FileT]: 681 """ 682 List of file names from the type="bootstrap" packages section(s) 683 684 :return: file names 685 686 :rtype: dict 687 """ 688 result = {} 689 bootstrap_packages_sections = self.get_bootstrap_packages_sections() 690 if bootstrap_packages_sections: 691 for bootstrap_packages_section in bootstrap_packages_sections: 692 file_list = bootstrap_packages_section.get_file() or [] 693 for file in file_list: 694 result[file.get_name()] = FileT( 695 target=file.get_target() or '', 696 owner=file.get_owner() or '', 697 permissions=file.get_permissions() or '' 698 ) 699 return result
List of file names from the type="bootstrap" packages section(s)
Returns
file names
701 def get_system_files(self) -> Dict[str, FileT]: 702 """ 703 List of file names from the packages sections matching 704 type="image" and type=build_type 705 706 :return: file names 707 708 :rtype: dict 709 """ 710 result = {} 711 image_packages_sections = self.get_packages_sections( 712 ['image', self.get_build_type_name()] 713 ) 714 for packages in image_packages_sections: 715 for file in packages.get_file(): 716 result[file.get_name()] = FileT( 717 target=file.get_target() or '', 718 owner=file.get_owner() or '', 719 permissions=file.get_permissions() or '' 720 ) 721 return result
List of file names from the packages sections matching type="image" and type=build_type
Returns
file names
723 def get_bootstrap_archives(self) -> List: 724 """ 725 List of archive names from the type="bootstrap" packages section(s) 726 727 :return: archive names 728 729 :rtype: list 730 """ 731 result = [] 732 bootstrap_packages_sections = self.get_bootstrap_packages_sections() 733 if bootstrap_packages_sections: 734 for bootstrap_packages_section in bootstrap_packages_sections: 735 archive_list = bootstrap_packages_section.get_archive() 736 if archive_list: 737 for archive in archive_list: 738 result.append(archive.get_name().strip()) 739 return sorted(result)
List of archive names from the type="bootstrap" packages section(s)
Returns
archive names
741 def get_system_archives(self) -> List: 742 """ 743 List of archive names from the packages sections matching 744 type="image" and type=build_type 745 746 :return: archive names 747 748 :rtype: list 749 """ 750 result = [] 751 image_packages_sections = self.get_packages_sections( 752 ['image', self.get_build_type_name()] 753 ) 754 for packages in image_packages_sections: 755 for archive in packages.get_archive(): 756 result.append(archive.get_name().strip()) 757 return sorted(result)
List of archive names from the packages sections matching type="image" and type=build_type
Returns
archive names
759 def get_ignore_packages(self, section_type: str) -> List: 760 """ 761 List of ignore package names from the packages sections matching 762 section_type and type=build_type 763 764 :return: package names 765 766 :rtype: list 767 """ 768 result = [] 769 image_packages_sections = self.get_packages_sections( 770 [section_type, self.get_build_type_name()] 771 ) 772 for packages in image_packages_sections: 773 for package in packages.get_ignore(): 774 if self.package_matches_host_architecture(package): 775 result.append(package.get_name().strip()) 776 return sorted(result)
List of ignore package names from the packages sections matching section_type and type=build_type
Returns
package names
778 def get_system_files_ignore_packages(self) -> List[str]: 779 """ 780 List of ignore package names from the type="systemfiles" 781 packages section(s) 782 783 :return: package names 784 785 :rtype: list 786 """ 787 return self.get_ignore_packages('systemfiles')
List of ignore package names from the type="systemfiles" packages section(s)
Returns
package names
789 def get_system_ignore_packages(self) -> List: 790 """ 791 List of ignore package names from the packages sections matching 792 type="image" and type=build_type 793 794 :return: package names 795 796 :rtype: list 797 """ 798 return self.get_ignore_packages('image')
List of ignore package names from the packages sections matching type="image" and type=build_type
Returns
package names
800 def get_bootstrap_ignore_packages(self) -> List: 801 """ 802 List of ignore package names from the packages sections matching 803 type="image" and type=build_type 804 805 :return: package names 806 807 :rtype: list 808 """ 809 return self.get_ignore_packages('bootstrap')
List of ignore package names from the packages sections matching type="image" and type=build_type
Returns
package names
811 def get_bootstrap_package_name(self) -> str: 812 """ 813 bootstrap_package name from type="bootstrap" packages section 814 815 :return: bootstrap_package name 816 817 :rtype: str 818 """ 819 typed_packages_sections = self.get_packages_sections( 820 ['bootstrap', self.get_build_type_name()] 821 ) 822 bootstrap_package = '' 823 for packages in typed_packages_sections: 824 bootstrap_package = packages.get_bootstrap_package() 825 if bootstrap_package: 826 break 827 return bootstrap_package
bootstrap_package name from type="bootstrap" packages section
Returns
bootstrap_package name
829 def get_collection_type(self, section_type: str = 'image') -> str: 830 """ 831 Collection type from packages sections matching given section 832 type. 833 834 If no collection type is specified the default collection 835 type is set to: onlyRequired 836 837 :param str section_type: type name from packages section 838 839 :return: collection type name 840 841 :rtype: str 842 """ 843 typed_packages_sections = self.get_packages_sections( 844 [section_type, self.get_build_type_name()] 845 ) 846 collection_type = 'onlyRequired' 847 for packages in typed_packages_sections: 848 packages_collection_type = packages.get_patternType() 849 if packages_collection_type: 850 collection_type = packages_collection_type 851 break 852 return collection_type
Collection type from packages sections matching given section type.
If no collection type is specified the default collection type is set to: onlyRequired
Parameters
- str section_type: type name from packages section
Returns
collection type name
854 def get_bootstrap_collection_type(self) -> str: 855 """ 856 Collection type for packages sections matching type="bootstrap" 857 858 :return: collection type name 859 860 :rtype: str 861 """ 862 return self.get_collection_type('bootstrap')
Collection type for packages sections matching type="bootstrap"
Returns
collection type name
864 def get_system_collection_type(self) -> str: 865 """ 866 Collection type for packages sections matching type="image" 867 868 :return: collection type name 869 870 :rtype: str 871 """ 872 return self.get_collection_type('image')
Collection type for packages sections matching type="image"
Returns
collection type name
874 def get_collection_modules(self) -> Dict[str, List[str]]: 875 """ 876 Dict of collection modules to enable and/or disable 877 878 :return: 879 Dict of the form: 880 881 .. code:: python 882 883 { 884 'enable': [ 885 "module:stream", "module" 886 ], 887 'disable': [ 888 "module" 889 ] 890 } 891 892 :rtype: dict 893 """ 894 modules: Dict[str, List[str]] = { 895 'disable': [], 896 'enable': [] 897 } 898 for packages in self.get_bootstrap_packages_sections(): 899 for collection_module in packages.get_collectionModule(): 900 module_name = collection_module.get_name() 901 if collection_module.get_enable() is False: 902 modules['disable'].append(module_name) 903 else: 904 stream = collection_module.get_stream() 905 if stream: 906 modules['enable'].append(f'{module_name}:{stream}') 907 else: 908 modules['enable'].append(module_name) 909 return modules
Dict of collection modules to enable and/or disable
Returns
Dict of the form: .. code:: python { 'enable': [ "module:stream", "module" ], 'disable': [ "module" ] }
911 def get_collections(self, section_type: str = 'image') -> List: 912 """ 913 List of collection names from the packages sections matching 914 type=section_type and type=build_type 915 916 :return: collection names 917 918 :rtype: list 919 """ 920 result = [] 921 typed_packages_sections = self.get_packages_sections( 922 [section_type, self.get_build_type_name()] 923 ) 924 for packages in typed_packages_sections: 925 for collection in packages.get_namedCollection(): 926 if self.collection_matches_host_architecture(collection): 927 result.append(collection.get_name()) 928 return sorted(list(set(result)))
List of collection names from the packages sections matching type=section_type and type=build_type
Returns
collection names
930 def get_bootstrap_collections(self) -> List: 931 """ 932 List of collection names from the packages sections 933 matching type="bootstrap" 934 935 :return: collection names 936 937 :rtype: list 938 """ 939 return self.get_collections('bootstrap')
List of collection names from the packages sections matching type="bootstrap"
Returns
collection names
941 def get_system_collections(self) -> List: 942 """ 943 List of collection names from the packages sections 944 matching type="image" 945 946 :return: collection names 947 948 :rtype: list 949 """ 950 return self.get_collections('image')
List of collection names from the packages sections matching type="image"
Returns
collection names
952 def get_products(self, section_type: str = 'image') -> List: 953 """ 954 List of product names from the packages sections matching 955 type=section_type and type=build_type 956 957 :param str section_type: type name from packages section 958 959 :return: product names 960 961 :rtype: list 962 """ 963 result = [] 964 typed_packages_sections = self.get_packages_sections( 965 [section_type, self.get_build_type_name()] 966 ) 967 for packages in typed_packages_sections: 968 for product in packages.get_product(): 969 result.append(product.get_name()) 970 return list(set(result))
List of product names from the packages sections matching type=section_type and type=build_type
Parameters
- str section_type: type name from packages section
Returns
product names
972 def get_bootstrap_products(self) -> List: 973 """ 974 List of product names from the packages sections 975 matching type="bootstrap" 976 977 :return: product names 978 979 :rtype: list 980 """ 981 return self.get_products('bootstrap')
List of product names from the packages sections matching type="bootstrap"
Returns
product names
983 def get_system_products(self) -> List: 984 """ 985 List of product names from the packages sections 986 matching type="image" 987 988 :return: product names 989 990 :rtype: list 991 """ 992 return self.get_products('image')
List of product names from the packages sections matching type="image"
Returns
product names
994 def is_xen_server(self) -> bool: 995 """ 996 Check if build type domain setup specifies a Xen Server (dom0) 997 998 :return: True or False 999 1000 :rtype: bool 1001 """ 1002 return self.build_type.get_xen_server()
Check if build type domain setup specifies a Xen Server (dom0)
Returns
True or False
1004 def is_xen_guest(self) -> bool: 1005 """ 1006 Check if build type setup specifies a Xen Guest (domX) 1007 The check is based on the architecture, the firmware and 1008 xen_loader configuration values: 1009 1010 * We only support Xen setup on the x86_64 architecture 1011 1012 * Firmware pointing to ec2 means the image is targeted to run 1013 in Amazon EC2 which is a Xen guest 1014 1015 * Machine setup with a xen_loader attribute also indicates a 1016 Xen guest target 1017 1018 :return: True or False 1019 1020 :rtype: bool 1021 """ 1022 if self.host_architecture != 'x86_64': 1023 # We only support Xen stuff on x86_64 1024 return False 1025 firmware = self.build_type.get_firmware() 1026 machine_section = self.get_build_type_machine_section() 1027 if firmware and firmware in Defaults.get_ec2_capable_firmware_names(): 1028 # the image is targeted to run in Amazon EC2 which is a Xen system 1029 return True 1030 elif machine_section and machine_section.get_xen_loader(): 1031 # the image provides a machine section with a guest loader setup 1032 return True 1033 return False
Check if build type setup specifies a Xen Guest (domX) The check is based on the architecture, the firmware and xen_loader configuration values:
We only support Xen setup on the x86_64 architecture
Firmware pointing to ec2 means the image is targeted to run in Amazon EC2 which is a Xen guest
Machine setup with a xen_loader attribute also indicates a Xen guest target
Returns
True or False
1035 def get_build_type_partitions_section(self) -> Any: 1036 """ 1037 First partitions section from the build type section 1038 1039 :return: <partitions> section reference 1040 1041 :rtype: xml_parse::partitions 1042 """ 1043 partitions_sections = self.build_type.get_partitions() 1044 if partitions_sections: 1045 return partitions_sections[0] 1046 return None
First partitions section from the build type section
Returns
section reference
1048 def get_build_type_system_disk_section(self) -> Any: 1049 """ 1050 First system disk section from the build type section 1051 1052 :return: <systemdisk> section reference 1053 1054 :rtype: xml_parse::systemdisk 1055 """ 1056 systemdisk_sections = self.build_type.get_systemdisk() 1057 if systemdisk_sections: 1058 return systemdisk_sections[0] 1059 return None
First system disk section from the build type section
Returns
section reference
1061 def get_build_type_machine_section(self) -> Any: 1062 """ 1063 First machine section from the build type section 1064 1065 :return: <machine> section reference 1066 1067 :rtype: xml_parse::machine 1068 """ 1069 machine_sections = self.build_type.get_machine() 1070 if machine_sections: 1071 return machine_sections[0] 1072 return None
First machine section from the build type section
Returns
section reference
1074 def get_build_type_vagrant_config_section(self) -> Any: 1075 """ 1076 First vagrantconfig section from the build type section 1077 1078 :return: <vagrantconfig> section reference 1079 1080 :rtype: xml_parse::vagrantconfig 1081 """ 1082 vagrant_config_sections = self.build_type.get_vagrantconfig() 1083 if vagrant_config_sections: 1084 return vagrant_config_sections[0] 1085 return None
First vagrantconfig section from the build type section
Returns
section reference
1087 def get_vagrant_config_virtualbox_guest_additions(self) -> bool: 1088 """ 1089 Attribute virtualbox_guest_additions_present from the first 1090 vagrantconfig section. 1091 1092 :return: True|False 1093 1094 :rtype: bool 1095 """ 1096 vagrant_config_sections = self.get_build_type_vagrant_config_section() 1097 if not vagrant_config_sections.virtualbox_guest_additions_present: 1098 return Defaults.get_vagrant_config_virtualbox_guest_additions() 1099 else: 1100 return vagrant_config_sections.virtualbox_guest_additions_present
Attribute virtualbox_guest_additions_present from the first vagrantconfig section.
Returns
True|False
1102 def get_build_type_vmdisk_section(self) -> Any: 1103 """ 1104 First vmdisk section from the first machine section in the 1105 build type section 1106 1107 :return: <vmdisk> section reference 1108 1109 :rtype: xml_parse::vmdisk 1110 """ 1111 machine_section = self.get_build_type_machine_section() 1112 if machine_section: 1113 vmdisk_sections = machine_section.get_vmdisk() 1114 if vmdisk_sections: 1115 return vmdisk_sections[0] 1116 return None
First vmdisk section from the first machine section in the build type section
Returns
section reference
1118 def get_build_type_vmnic_entries(self) -> List: 1119 """ 1120 vmnic section(s) from the first machine section in the 1121 build type section 1122 1123 :return: list of <vmnic> section reference(s) 1124 1125 :rtype: list 1126 """ 1127 machine_section = self.get_build_type_machine_section() 1128 if machine_section: 1129 return machine_section.get_vmnic() 1130 else: 1131 return []
vmnic section(s) from the first machine section in the build type section
Returns
list of
section reference(s)
1133 def get_build_type_vmdvd_section(self) -> Any: 1134 """ 1135 First vmdvd section from the first machine section in the 1136 build type section 1137 1138 :return: <vmdvd> section reference 1139 1140 :rtype: xml_parse::vmdvd 1141 """ 1142 machine_section = self.get_build_type_machine_section() 1143 if machine_section: 1144 vmdvd_sections = machine_section.get_vmdvd() 1145 if vmdvd_sections: 1146 return vmdvd_sections[0] 1147 return None
First vmdvd section from the first machine section in the build type section
Returns
section reference
1149 def get_build_type_vmconfig_entries(self) -> List: 1150 """ 1151 List of vmconfig-entry section values from the first 1152 machine section in the build type section 1153 1154 :return: <vmconfig_entry> section reference(s) 1155 1156 :rtype: list 1157 """ 1158 machine_section = self.get_build_type_machine_section() 1159 if machine_section: 1160 vmconfig_entries = machine_section.get_vmconfig_entry() 1161 if vmconfig_entries: 1162 return vmconfig_entries 1163 1164 return []
List of vmconfig-entry section values from the first machine section in the build type section
Returns
section reference(s)
1166 def get_build_type_bootloader_section(self) -> Any: 1167 """ 1168 First bootloader section from the build type section 1169 1170 :return: <bootloader> section reference 1171 1172 :rtype: xml_parse::bootloader 1173 """ 1174 bootloader_sections = self.build_type.get_bootloader() 1175 if bootloader_sections: 1176 return bootloader_sections[0] 1177 return None
First bootloader section from the build type section
Returns
section reference
1179 def get_build_type_bootloader_name(self) -> str: 1180 """ 1181 Return bootloader name for selected build type 1182 1183 :return: bootloader name 1184 1185 :rtype: str 1186 """ 1187 bootloader = self.get_build_type_bootloader_section() 1188 return bootloader.get_name() if bootloader else \ 1189 Defaults.get_default_bootloader()
Return bootloader name for selected build type
Returns
bootloader name
1191 def get_build_type_bootloader_bls(self) -> bool: 1192 """ 1193 Return bootloader bls setting for selected build type 1194 1195 :return: True or False 1196 1197 :rtype: bool 1198 """ 1199 bootloader = self.get_build_type_bootloader_section() 1200 if bootloader and bootloader.get_bls() is not None: 1201 return bootloader.get_bls() 1202 return True
Return bootloader bls setting for selected build type
Returns
True or False
1204 def get_build_type_bootloader_console(self) -> List[str]: 1205 """ 1206 Return bootloader console setting for selected build type 1207 1208 :return: 1209 list of console settings for output (first element) 1210 and input (second element) 1211 1212 :rtype: list 1213 """ 1214 result = ['', ''] 1215 bootloader = self.get_build_type_bootloader_section() 1216 if bootloader: 1217 console_out = bootloader.get_output_console() 1218 console_in = bootloader.get_input_console() 1219 console_in = console_in if console_in else console_out 1220 result = [ 1221 console_out if console_out and console_out != 'none' else '', 1222 console_in if console_in and console_in != 'none' else '' 1223 ] 1224 return result
Return bootloader console setting for selected build type
Returns
list of console settings for output (first element) and input (second element)
1226 def get_build_type_bootloader_serial_line_setup(self) -> Optional[str]: 1227 """ 1228 Return bootloader serial line setup parameters for the 1229 selected build type 1230 1231 :return: serial line setup 1232 1233 :rtype: str 1234 """ 1235 bootloader = self.get_build_type_bootloader_section() 1236 if bootloader: 1237 return bootloader.get_serial_line() 1238 return None
Return bootloader serial line setup parameters for the selected build type
Returns
serial line setup
1240 def get_build_type_bootloader_timeout(self) -> Optional[str]: 1241 """ 1242 Return bootloader timeout setting for selected build type 1243 1244 :return: timeout string 1245 1246 :rtype: str 1247 """ 1248 bootloader = self.get_build_type_bootloader_section() 1249 if bootloader: 1250 return bootloader.get_timeout() 1251 return None
Return bootloader timeout setting for selected build type
Returns
timeout string
1253 def get_build_type_bootloader_timeout_style(self) -> Optional[str]: 1254 """ 1255 Return bootloader timeout style setting for selected build type 1256 1257 :return: timeout_style string 1258 1259 :rtype: str 1260 """ 1261 bootloader = self.get_build_type_bootloader_section() 1262 if bootloader: 1263 return bootloader.get_timeout_style() 1264 return None
Return bootloader timeout style setting for selected build type
Returns
timeout_style string
1266 def get_build_type_bootloader_targettype(self) -> Optional[str]: 1267 """ 1268 Return bootloader target type setting. Only relevant for 1269 the zipl bootloader because zipl is installed differently 1270 depending on the storage target it runs later 1271 1272 :return: target type string 1273 1274 :rtype: str 1275 """ 1276 bootloader = self.get_build_type_bootloader_section() 1277 if bootloader: 1278 return bootloader.get_targettype() 1279 return None
Return bootloader target type setting. Only relevant for the zipl bootloader because zipl is installed differently depending on the storage target it runs later
Returns
target type string
1281 def get_build_type_bootloader_settings_section(self) -> Any: 1282 """ 1283 First bootloadersettings section from the build 1284 type bootloader section 1285 1286 :return: <bootloadersettings> section reference 1287 1288 :rtype: xml_parse::bootloadersettings 1289 """ 1290 bootloader_section = self.get_build_type_bootloader_section() 1291 bootloader_settings_section = None 1292 if bootloader_section and bootloader_section.get_bootloadersettings(): 1293 bootloader_settings_section = \ 1294 bootloader_section.get_bootloadersettings()[0] 1295 return bootloader_settings_section
First bootloadersettings section from the build type bootloader section
Returns
section reference
1297 def get_build_type_bootloader_securelinux_section(self) -> List[Any]: 1298 """ 1299 First securelinux section from the build 1300 type bootloader section 1301 1302 :return: <securelinux> section reference 1303 1304 :rtype: xml_parse::securelinux 1305 """ 1306 bootloader_section = self.get_build_type_bootloader_section() 1307 bootloader_securelinux_section = [] 1308 if bootloader_section and bootloader_section.get_securelinux(): 1309 bootloader_securelinux_section = \ 1310 bootloader_section.get_securelinux() 1311 return bootloader_securelinux_section
First securelinux section from the build type bootloader section
Returns
section reference
1313 def get_build_type_bootloader_environment_variables(self) -> List[str]: 1314 """ 1315 List of bootloader variables from the build 1316 type > bootloader > bootloadersettings section 1317 """ 1318 variable_list = [] 1319 bootloader_settings_section = \ 1320 self.get_build_type_bootloader_settings_section() 1321 if bootloader_settings_section: 1322 environment = bootloader_settings_section.get_environment() 1323 if environment and environment[0].get_env(): 1324 for env in environment[0].get_env(): 1325 variable_list.append( 1326 '{}{}'.format( 1327 env.get_name(), 1328 f'={env.get_value()}' if env.get_value() else '' 1329 ) 1330 ) 1331 return variable_list
List of bootloader variables from the build type > bootloader > bootloadersettings section
1333 def get_bootloader_options(self, option_type: str) -> List[str]: 1334 """ 1335 List of custom options used in the process to 1336 run bootloader setup workloads 1337 """ 1338 result: List[str] = [] 1339 bootloader_settings = self.get_build_type_bootloader_settings_section() 1340 if bootloader_settings: 1341 options = [] 1342 if option_type == 'shim': 1343 options = bootloader_settings.get_shimoption() 1344 elif option_type == 'install': 1345 options = bootloader_settings.get_installoption() 1346 elif option_type == 'config': 1347 options = bootloader_settings.get_configoption() 1348 for option in options: 1349 result.append(option.get_name()) 1350 if option.get_value(): 1351 result.append(option.get_value()) 1352 return result
List of custom options used in the process to run bootloader setup workloads
1354 def get_bootloader_shim_options(self) -> List[str]: 1355 """ 1356 List of custom options used in the process to setup secure boot 1357 """ 1358 return self.get_bootloader_options('shim')
List of custom options used in the process to setup secure boot
1360 def get_bootloader_install_options(self) -> List[str]: 1361 """ 1362 List of custom options used in the bootloader installation 1363 """ 1364 return self.get_bootloader_options('install')
List of custom options used in the bootloader installation
1366 def get_bootloader_config_options(self) -> List[str]: 1367 """ 1368 List of custom options used in the bootloader configuration 1369 """ 1370 return self.get_bootloader_options('config')
List of custom options used in the bootloader configuration
1372 def get_build_type_bootloader_use_disk_password(self) -> bool: 1373 """ 1374 Indicate whether the bootloader configuration should use the 1375 password protecting the encrypted root volume. 1376 1377 :return: True|False 1378 1379 :rtype: bool 1380 """ 1381 bootloader = self.get_build_type_bootloader_section() 1382 if bootloader: 1383 return bootloader.get_use_disk_password() 1384 return False
Indicate whether the bootloader configuration should use the password protecting the encrypted root volume.
Returns
True|False
1386 def get_build_type_oemconfig_section(self) -> Any: 1387 """ 1388 First oemconfig section from the build type section 1389 1390 :return: <oemconfig> section reference 1391 1392 :rtype: xml_parse::oemconfig 1393 """ 1394 oemconfig_sections = self.build_type.get_oemconfig() 1395 if oemconfig_sections: 1396 return oemconfig_sections[0] 1397 return None
First oemconfig section from the build type section
Returns
section reference
1399 def get_oemconfig_oem_resize(self) -> bool: 1400 """ 1401 State value to activate/deactivate disk resize. Returns a 1402 boolean value if specified or True to set resize on by default 1403 1404 :return: Content of <oem-resize> section value 1405 1406 :rtype: bool 1407 """ 1408 oemconfig = self.get_build_type_oemconfig_section() 1409 if oemconfig and oemconfig.get_oem_resize(): 1410 return oemconfig.get_oem_resize()[0] 1411 else: 1412 return True
State value to activate/deactivate disk resize. Returns a boolean value if specified or True to set resize on by default
Returns
Content of
section value
1414 def get_oemconfig_oem_systemsize(self) -> int: 1415 """ 1416 State value to retrieve root partition size 1417 1418 :return: Content of <oem-systemsize> section value 1419 1420 :rtype: int 1421 """ 1422 oemconfig = self.get_build_type_oemconfig_section() 1423 if oemconfig and oemconfig.get_oem_systemsize(): 1424 return int(oemconfig.get_oem_systemsize()[0]) 1425 else: 1426 return 0
State value to retrieve root partition size
Returns
Content of
section value
1428 def get_oemconfig_oem_multipath_scan(self) -> bool: 1429 """ 1430 State value to activate multipath maps. Returns a boolean 1431 value if specified or False 1432 1433 :return: Content of <oem-multipath-scan> section value 1434 1435 :rtype: bool 1436 """ 1437 oemconfig = self.get_build_type_oemconfig_section() 1438 if oemconfig and oemconfig.get_oem_multipath_scan(): 1439 return oemconfig.get_oem_multipath_scan()[0] 1440 return False
State value to activate multipath maps. Returns a boolean value if specified or False
Returns
Content of
section value
1442 def get_oemconfig_swap_mbytes(self) -> Optional[int]: 1443 """ 1444 Return swapsize in MB if requested or None 1445 1446 Operates on the value of oem-swap and if set to true 1447 returns the given size or the default value. 1448 1449 :return: Content of <oem-swapsize> section value or default 1450 1451 :rtype: int 1452 """ 1453 oemconfig = self.get_build_type_oemconfig_section() 1454 if oemconfig and oemconfig.get_oem_swap(): 1455 swap_requested = oemconfig.get_oem_swap()[0] 1456 if swap_requested: 1457 swapsize = oemconfig.get_oem_swapsize() 1458 if swapsize: 1459 return swapsize[0] 1460 else: 1461 return Defaults.get_swapsize_mbytes() 1462 return None
Return swapsize in MB if requested or None
Operates on the value of oem-swap and if set to true returns the given size or the default value.
Returns
Content of
section value or default
1464 def get_oemconfig_swap_name(self) -> str: 1465 """ 1466 Return the swap space name 1467 1468 Operates on the value of oem-swapname and if set 1469 returns the configured name or the default name: LVSwap 1470 1471 The name of the swap space is used only if the 1472 image is configured to use the LVM volume manager. 1473 In this case swap is a volume and the volume takes 1474 a name. In any other case the given name will have 1475 no effect. 1476 1477 :return: Content of <oem-swapname> section value or default 1478 1479 :rtype: str 1480 """ 1481 oemconfig = self.get_build_type_oemconfig_section() 1482 if oemconfig and oemconfig.get_oem_swapname(): 1483 return oemconfig.get_oem_swapname()[0] 1484 return 'LVSwap'
Return the swap space name
Operates on the value of oem-swapname and if set returns the configured name or the default name: LVSwap
The name of the swap space is used only if the image is configured to use the LVM volume manager. In this case swap is a volume and the volume takes a name. In any other case the given name will have no effect.
Returns
Content of
section value or default
1486 def get_build_type_containerconfig_section(self) -> Any: 1487 """ 1488 First containerconfig section from the build type section 1489 1490 :return: <containerconfig> section reference 1491 1492 :rtype: xml_parse::containerconfig 1493 """ 1494 container_config_sections = self.build_type.get_containerconfig() 1495 if container_config_sections: 1496 return container_config_sections[0] 1497 return None
First containerconfig section from the build type section
Returns
section reference
1499 def get_dracut_config(self, action: str) -> DracutT: 1500 """ 1501 Get dracut initrd config for the specified action 1502 """ 1503 uefi = False 1504 modules = [] 1505 drivers = [] 1506 initrd_sections = self.build_type.get_initrd() 1507 for initrd_section in initrd_sections: 1508 if initrd_section.get_action() == action: 1509 for dracut in initrd_section.get_dracut(): 1510 uefi = bool(dracut.get_uefi()) 1511 if dracut.get_module(): 1512 modules.append(dracut.get_module()) 1513 if dracut.get_driver(): 1514 drivers.append(dracut.get_driver()) 1515 return DracutT( 1516 uefi=uefi, modules=modules, drivers=drivers 1517 )
Get dracut initrd config for the specified action
1519 def get_installmedia_initrd_modules(self, action: str) -> List[str]: 1520 """ 1521 Gets the list of modules to append in installation initrds 1522 1523 :return: a list of dracut module names 1524 1525 :rtype: list 1526 """ 1527 modules: List[str] = [] 1528 installmedia = self.build_type.get_installmedia() 1529 if not installmedia: 1530 return modules 1531 initrd_sections = installmedia[0].get_initrd() 1532 for initrd_section in initrd_sections: 1533 if initrd_section.get_action() == action: 1534 for module in initrd_section.get_dracut(): 1535 if module.get_module(): 1536 modules.append(module.get_module()) 1537 return modules
Gets the list of modules to append in installation initrds
Returns
a list of dracut module names
1539 def get_installmedia_initrd_drivers(self, action: str) -> List[str]: 1540 """ 1541 Gets the list of drivers to append in installation initrds 1542 1543 :return: a list of dracut driver names 1544 1545 :rtype: list 1546 """ 1547 drivers: List[str] = [] 1548 installmedia = self.build_type.get_installmedia() 1549 if not installmedia: 1550 return drivers 1551 initrd_sections = installmedia[0].get_initrd() 1552 for initrd_section in initrd_sections: 1553 if initrd_section.get_action() == action: 1554 for driver in initrd_section.get_dracut(): 1555 if driver.get_driver(): 1556 drivers.append(driver.get_driver()) 1557 return drivers
Gets the list of drivers to append in installation initrds
Returns
a list of dracut driver names
1559 def get_build_type_size( 1560 self, include_unpartitioned: bool = False 1561 ) -> Optional[size_type]: 1562 """ 1563 Size information from the build type section. 1564 If no unit is set the value is treated as mbytes 1565 1566 :param bool include_unpartitioned: sets if the unpartitioned area 1567 should be included in the computed size or not 1568 1569 :return: mbytes 1570 1571 :rtype: int 1572 """ 1573 size_section = self.build_type.get_size() 1574 if size_section: 1575 unit = size_section[0].get_unit() 1576 additive = size_section[0].get_additive() 1577 unpartitioned = size_section[0].get_unpartitioned() 1578 value = int(size_section[0].get_valueOf_()) 1579 if not include_unpartitioned and unpartitioned is not None: 1580 value -= unpartitioned 1581 if unit == 'G': 1582 value *= 1024 1583 return size_type( 1584 mbytes=value, additive=additive 1585 ) 1586 return None
Size information from the build type section. If no unit is set the value is treated as mbytes
Parameters
- bool include_unpartitioned: sets if the unpartitioned area should be included in the computed size or not
Returns
mbytes
1588 def get_build_type_unpartitioned_bytes(self) -> int: 1589 """ 1590 Size of the unpartitioned area for image in megabytes 1591 1592 :return: mbytes 1593 1594 :rtype: int 1595 """ 1596 size_section = self.build_type.get_size() 1597 if size_section: 1598 unit = size_section[0].get_unit() or 'M' 1599 unpartitioned = size_section[0].get_unpartitioned() or 0 1600 return StringToSize.to_bytes('{0}{1}'.format(unpartitioned, unit)) 1601 return 0
Size of the unpartitioned area for image in megabytes
Returns
mbytes
1603 def get_disk_start_sector(self) -> int: 1604 """ 1605 First disk sector number to be used by the first disk partition. 1606 1607 :return: number 1608 1609 :rtype: int 1610 """ 1611 disk_start_sector = self.build_type.get_disk_start_sector() 1612 if disk_start_sector is None: 1613 disk_start_sector = Defaults.get_default_disk_start_sector() 1614 return disk_start_sector
First disk sector number to be used by the first disk partition.
Returns
number
1616 def get_build_type_spare_part_size(self) -> Optional[int]: 1617 """ 1618 Size information for the spare_part size from the build 1619 type. If no unit is set the value is treated as mbytes 1620 1621 :return: mbytes 1622 1623 :rtype: int 1624 """ 1625 spare_part_size = self.build_type.get_spare_part() 1626 if spare_part_size: 1627 return self._to_mega_byte(spare_part_size) 1628 return None
Size information for the spare_part size from the build type. If no unit is set the value is treated as mbytes
Returns
mbytes
1630 def get_build_type_spare_part_fs_attributes(self) -> Optional[List]: 1631 """ 1632 Build type specific list of filesystem attributes applied to 1633 the spare partition. 1634 1635 :return: list of strings or empty list 1636 1637 :rtype: list 1638 """ 1639 spare_part_attributes = self.build_type.get_spare_part_fs_attributes() 1640 if spare_part_attributes: 1641 return spare_part_attributes.strip().split(',') 1642 return None
Build type specific list of filesystem attributes applied to the spare partition.
Returns
list of strings or empty list
1644 def get_build_type_format_options(self) -> Dict: 1645 """ 1646 Disk format options returned as a dictionary 1647 1648 :return: format options 1649 1650 :rtype: dict 1651 """ 1652 result = {} 1653 format_options = self.build_type.get_formatoptions() 1654 if format_options: 1655 for option in format_options.split(','): 1656 key_value_list = option.split('=') 1657 if len(key_value_list) == 2: 1658 result[key_value_list[0]] = key_value_list[1] 1659 else: 1660 result[key_value_list[0]] = None 1661 return result
Disk format options returned as a dictionary
Returns
format options
1663 def get_volume_group_name(self) -> str: 1664 """ 1665 Volume group name from selected <systemdisk> section 1666 1667 :return: volume group name 1668 1669 :rtype: str 1670 """ 1671 systemdisk_section = self.get_build_type_system_disk_section() 1672 volume_group_name = None 1673 if systemdisk_section: 1674 volume_group_name = systemdisk_section.get_name() 1675 if not volume_group_name: 1676 volume_group_name = Defaults.get_default_volume_group_name() 1677 return volume_group_name
Volume group name from selected
Returns
volume group name
1679 def get_users(self) -> List: 1680 """ 1681 List of configured users. 1682 1683 Each entry in the list is a single xml_parse::user instance. 1684 1685 :return: list of <user> section reference(s) 1686 1687 :rtype: list 1688 """ 1689 users_list = [] 1690 users_names_added = [] 1691 for users_section in self.get_users_sections(): 1692 for user in users_section.get_user(): 1693 if user.get_name() not in users_names_added: 1694 users_list.append(user) 1695 users_names_added.append(user.get_name()) 1696 1697 return users_list
List of configured users.
Each entry in the list is a single xml_parse::user instance.
Returns
list of
section reference(s)
1699 def get_user_groups(self, user_name) -> List[str]: 1700 """ 1701 List of group names matching specified user 1702 1703 Each entry in the list is the name of a group and optionally its 1704 group ID separated by a colon, that the specified user belongs to. 1705 The first item in the list is the login or primary group. The 1706 list will be empty if no groups are specified in the 1707 description file. 1708 1709 :return: groups data for the given user 1710 1711 :rtype: list 1712 """ 1713 groups_list = [] 1714 for users_section in self.get_users_sections(): 1715 for user in users_section.get_user(): 1716 if user.get_name() == user_name: 1717 user_groups = user.get_groups() 1718 if user_groups: 1719 groups_list += user.get_groups().split(',') 1720 1721 # order of list items matter, thus we don't use set() here 1722 # better faster, nicer solutions welcome :) 1723 result_group_list = [] 1724 for item in groups_list: 1725 if item not in result_group_list: 1726 result_group_list.append(item) 1727 1728 return result_group_list
List of group names matching specified user
Each entry in the list is the name of a group and optionally its group ID separated by a colon, that the specified user belongs to. The first item in the list is the login or primary group. The list will be empty if no groups are specified in the description file.
Returns
groups data for the given user
1730 def get_container_config(self) -> Dict: 1731 """ 1732 Dictionary of containerconfig information 1733 1734 Takes attributes and subsection data from the selected 1735 <containerconfig> section and stores it in a dictionary 1736 """ 1737 container_config = self._match_docker_base_data() 1738 container_config.update( 1739 self._match_docker_entrypoint() 1740 ) 1741 container_config.update( 1742 self._match_docker_subcommand() 1743 ) 1744 container_config.update( 1745 self._match_docker_expose_ports() 1746 ) 1747 container_config.update( 1748 self._match_docker_volumes() 1749 ) 1750 container_config.update( 1751 self._match_docker_stopsignal() 1752 ) 1753 container_config.update( 1754 self._match_docker_environment() 1755 ) 1756 container_config.update( 1757 self._match_docker_labels() 1758 ) 1759 container_config.update( 1760 self._match_docker_history() 1761 ) 1762 1763 desc = self.get_description_section() 1764 author_contact = "{0} <{1}>".format(desc.author, desc.contact) 1765 if 'history' not in container_config: 1766 container_config['history'] = {} 1767 if 'author' not in container_config['history']: 1768 container_config['history']['author'] = author_contact 1769 if 'maintainer' not in container_config: 1770 container_config['maintainer'] = author_contact 1771 1772 return container_config
Dictionary of containerconfig information
Takes attributes and subsection data from the selected
1774 def set_container_config_tag(self, tag: str) -> None: 1775 """ 1776 Set new tag name in containerconfig section 1777 1778 In order to set a new tag value an existing containerconfig and 1779 tag setup is required 1780 1781 :param str tag: tag name 1782 """ 1783 container_config_section = self.get_build_type_containerconfig_section() 1784 if container_config_section and container_config_section.get_tag(): 1785 container_config_section.set_tag(tag) 1786 else: 1787 message = dedent('''\n 1788 No <containerconfig> section and/or tag attribute configured 1789 1790 In order to set the tag {0} as new container tag, 1791 an initial containerconfig section including a tag 1792 setup is required 1793 ''') 1794 log.warning(message.format(tag))
Set new tag name in containerconfig section
In order to set a new tag value an existing containerconfig and tag setup is required
Parameters
- str tag: tag name
1796 def add_container_config_label(self, label_name: str, value: str) -> None: 1797 """ 1798 Adds a new label in the containerconfig section, if a label with the 1799 same name is already defined in containerconfig it gets overwritten by 1800 this method. 1801 1802 :param str label_name: the string representing the label name 1803 :param str value: the value of the label 1804 """ 1805 if self.get_build_type_name() not in ['docker', 'oci']: 1806 message = dedent('''\n 1807 Labels can only be configured for container image types 1808 docker and oci. 1809 ''') 1810 log.warning(message) 1811 return 1812 1813 container_config_section = self.get_build_type_containerconfig_section() 1814 if not container_config_section: 1815 container_config_section = xml_parse.containerconfig( 1816 name=Defaults.get_default_container_name(), 1817 tag=Defaults.get_default_container_tag() 1818 ) 1819 self.build_type.set_containerconfig([container_config_section]) 1820 1821 labels = container_config_section.get_labels() 1822 if not labels: 1823 labels = [xml_parse.labels()] 1824 1825 label_names = [] 1826 for label in labels[0].get_label(): 1827 label_names.append(label.get_name()) 1828 1829 if label_name in label_names: 1830 labels[0].replace_label_at( 1831 label_names.index(label_name), 1832 xml_parse.label(label_name, value) 1833 ) 1834 else: 1835 labels[0].add_label(xml_parse.label(label_name, value)) 1836 1837 container_config_section.set_labels(labels)
Adds a new label in the containerconfig section, if a label with the same name is already defined in containerconfig it gets overwritten by this method.
Parameters
- str label_name: the string representing the label name
- str value: the value of the label
1839 def get_partitions(self) -> Dict[str, ptable_entry_type]: 1840 """ 1841 Dictionary of configured partitions. 1842 1843 Each entry in the dict references a ptable_entry_type 1844 Each key in the dict references the name of the 1845 partition entry as handled by KIWI 1846 1847 :return: 1848 Contains dict of ptable_entry_type tuples 1849 1850 .. code:: python 1851 1852 { 1853 'NAME': ptable_entry_type( 1854 mbsize=int, 1855 clone=int, 1856 partition_name=str, 1857 partition_type=str, 1858 partition_id=Optional[int], 1859 mountpoint=str, 1860 filesystem=str, 1861 label=str 1862 ) 1863 } 1864 1865 :rtype: dict 1866 """ 1867 partitions: Dict[str, ptable_entry_type] = {} 1868 partitions_section = self.get_build_type_partitions_section() 1869 if not partitions_section: 1870 return partitions 1871 for partition in partitions_section.get_partition(): 1872 name = partition.get_name() 1873 partition_name = partition.get_partition_name() or f'p.lx{name}' 1874 partitions[name] = ptable_entry_type( 1875 mbsize=self._to_mega_byte(partition.get_size()), 1876 clone=int(partition.get_clone()) if partition.get_clone() else 0, 1877 partition_name=partition_name, 1878 partition_type=partition.get_partition_type() or 't.linux', 1879 partition_id=partition.get_part_id(), 1880 mountpoint=partition.get_mountpoint(), 1881 filesystem=partition.get_filesystem(), 1882 label=partition.get_label() or '' 1883 ) 1884 return partitions
Dictionary of configured partitions.
Each entry in the dict references a ptable_entry_type Each key in the dict references the name of the partition entry as handled by KIWI
Returns
Contains dict of ptable_entry_type tuples .. code:: python { 'NAME': ptable_entry_type( mbsize=int, clone=int, partition_name=str, partition_type=str, partition_id=Optional[int], mountpoint=str, filesystem=str, label=str ) }
1886 def get_host_key_certificates( 1887 self 1888 ) -> Union[List[Dict[str, List[str]]], List[Dict[str, str]]]: 1889 cc_result = [] 1890 cc_certificates: Dict[str, List[str]] = {} 1891 securelinux_list = \ 1892 self.get_build_type_bootloader_securelinux_section() 1893 for securelinux in securelinux_list: 1894 cc_certificates = { 1895 'hkd_cert': [], 1896 'hkd_revocation_list': [], 1897 'hkd_ca_cert': securelinux.get_hkd_ca_cert(), 1898 'hkd_sign_cert': securelinux.get_hkd_sign_cert() 1899 } 1900 for hkd_cert in securelinux.get_hkd_cert(): 1901 cc_certificates['hkd_cert'].append(hkd_cert.get_name()) 1902 for hkd_revocation_list in securelinux.get_hkd_revocation_list(): 1903 cc_certificates['hkd_revocation_list'].append( 1904 hkd_revocation_list.get_name() 1905 ) 1906 cc_result.append(cc_certificates) 1907 return cc_result
1909 def get_containers(self) -> List[ContainerT]: 1910 containers = [] 1911 1912 def build_fetch_command( 1913 root_dir: str, 1914 container_uri: str = '', 1915 container_file_name: str = '', 1916 container_endpoint: str = '' 1917 ): 1918 pass # pragma: nocover 1919 for containers_section in self.get_containers_sections(): 1920 for container in containers_section.get_container(): 1921 if self.container_matches_host_architecture(container): 1922 fetch_command = build_fetch_command 1923 load_command = [] 1924 container_tag = container.get_tag() or 'latest' 1925 container_path = container.get_path() or '' 1926 container_endpoint = os.path.normpath( 1927 '{0}/{1}/{2}:{3}'.format( 1928 containers_section.get_source(), container_path, 1929 container.name, container_tag 1930 ) 1931 ) 1932 container_file_name = '{0}/{1}_{2}'.format( 1933 defaults.LOCAL_CONTAINERS, container.name, container_tag 1934 ) 1935 container_backend = containers_section.get_backend() or '' 1936 if container_backend in ['podman', 'docker', 'container-snap']: 1937 if Defaults.is_buildservice_worker(): 1938 container_uri = Uri( 1939 'obsrepositories:/{0}'.format( 1940 container_endpoint 1941 ), 'container' 1942 ).translate() 1943 1944 def build_fetch_command( 1945 root_dir: str, 1946 container_uri: str = container_uri, 1947 container_file_name: str = container_file_name, 1948 container_endpoint: str = container_endpoint 1949 ): 1950 def perform(): 1951 Command.run( 1952 [ 1953 'cp', '{0}.ociarchive'.format( 1954 container_uri 1955 ), os.path.normpath( 1956 '{0}/{1}'.format( 1957 root_dir, 1958 container_file_name 1959 ) 1960 ) 1961 ] 1962 ) 1963 perform() 1964 fetch_command = build_fetch_command 1965 else: 1966 1967 def build_fetch_command( 1968 root_dir: str, 1969 container_uri: str = '', 1970 container_file_name: str = container_file_name, 1971 container_endpoint: str = container_endpoint 1972 ): 1973 def perform(): 1974 Command.run( 1975 [ 1976 'chroot', root_dir, 1977 '/usr/bin/skopeo', 'copy', 1978 'docker://{0}'.format( 1979 container_endpoint 1980 ), 1981 'oci-archive:{0}:{1}'.format( 1982 container_file_name, 1983 container_endpoint 1984 ) 1985 ] 1986 ) 1987 perform() 1988 fetch_command = build_fetch_command 1989 if not container.get_fetch_only(): 1990 load_command = [ 1991 f'/usr/bin/{container_backend}', 1992 'load', '-i', container_file_name 1993 ] 1994 containers.append( 1995 ContainerT( 1996 name=f'{container.name}_{container_tag}', 1997 backend=container_backend, 1998 container_file=container_file_name, 1999 fetch_only=bool(container.get_fetch_only()), 2000 fetch_command=fetch_command, 2001 load_command=load_command 2002 ) 2003 ) 2004 return containers
2006 def get_volumes(self) -> List[volume_type]: 2007 """ 2008 List of configured systemdisk volumes. 2009 2010 Each entry in the list is a tuple with the following information 2011 2012 * name: name of the volume 2013 * size: size of the volume 2014 * realpath: system path to lookup volume data. If no mountpoint 2015 is set the volume name is used as data path. 2016 * mountpoint: volume mount point and volume data path 2017 * fullsize: takes all space True|False 2018 * attributes: list of volume attributes handled via chattr 2019 2020 :return: 2021 Contains list of volume_type tuples 2022 2023 .. code:: python 2024 2025 [ 2026 volume_type( 2027 name=volume_name, 2028 parent=volume_parent, 2029 size=volume_size, 2030 realpath=path, 2031 mountpoint=path, 2032 fullsize=True, 2033 label=volume_label, 2034 attributes=['no-copy-on-write'], 2035 is_root_volume=True|False 2036 ) 2037 ] 2038 2039 :rtype: list 2040 """ 2041 volume_type_list: List[volume_type] = [] 2042 systemdisk_section = self.get_build_type_system_disk_section() 2043 selected_filesystem = self.build_type.get_filesystem() 2044 swap_mbytes = self.get_oemconfig_swap_mbytes() 2045 swap_name = self.get_oemconfig_swap_name() 2046 if not systemdisk_section: 2047 return volume_type_list 2048 volumes = systemdisk_section.get_volume() 2049 have_root_volume_setup = False 2050 have_full_size_volume = False 2051 if volumes: 2052 for volume in volumes: 2053 if not self.volume_matches_host_architecture(volume): 2054 continue 2055 # volume setup for a full qualified volume with name and 2056 # mountpoint information. See below for exceptions 2057 name = volume.get_name() 2058 parent = volume.get_parent() or '' 2059 mountpoint = volume.get_mountpoint() 2060 realpath = mountpoint 2061 size = volume.get_size() 2062 freespace = volume.get_freespace() 2063 fullsize = False 2064 label = volume.get_label() 2065 attributes = [] 2066 is_root_volume = False 2067 2068 if volume.get_quota(): 2069 attributes.append(f'quota={volume.get_quota()}') 2070 2071 if volume.get_copy_on_write() is False: 2072 # by default copy-on-write is switched on for any 2073 # filesystem. Thus only if no copy on write is requested 2074 # the attribute is handled 2075 attributes.append('no-copy-on-write') 2076 2077 if volume.get_filesystem_check() is True: 2078 # by default filesystem check is switched off for any 2079 # filesystem except the rootfs. Thus only if filesystem 2080 # check is requested the attribute is handled 2081 attributes.append('enable-for-filesystem-check') 2082 2083 if '@root' in name: 2084 # setup root volume, it takes an optional volume 2085 # name if specified as @root=name and has no specific 2086 # mountpoint. The default name is set to 2087 # defaults.ROOT_VOLUME_NAME if no other root volume 2088 # name is provided 2089 mountpoint = None 2090 realpath = '/' 2091 is_root_volume = True 2092 root_volume_expression = re.match( 2093 r'@root=(.+)', name 2094 ) 2095 if root_volume_expression: 2096 name = root_volume_expression.group(1) 2097 else: 2098 name = defaults.ROOT_VOLUME_NAME 2099 have_root_volume_setup = True 2100 elif not mountpoint: 2101 # setup volume without mountpoint. In this case the name 2102 # attribute is used as mountpoint path and a name for the 2103 # volume is created from that path information 2104 mountpoint = name 2105 realpath = mountpoint 2106 name = self._to_volume_name(name) 2107 2108 if size: 2109 size = 'size:' + format( 2110 self._to_mega_byte(size) 2111 ) 2112 elif freespace: 2113 size = 'freespace:' + format( 2114 self._to_mega_byte(freespace) 2115 ) 2116 else: 2117 size = 'freespace:' + format( 2118 Defaults.get_min_volume_mbytes(selected_filesystem) 2119 ) 2120 2121 if ':all' in size: 2122 size = None 2123 fullsize = True 2124 have_full_size_volume = True 2125 2126 volume_type_list.append( 2127 volume_type( 2128 name=name, 2129 parent=parent, 2130 size=size, 2131 fullsize=fullsize, 2132 mountpoint=mountpoint, 2133 realpath=realpath, 2134 label=label, 2135 attributes=attributes, 2136 is_root_volume=is_root_volume 2137 ) 2138 ) 2139 2140 if not have_root_volume_setup: 2141 # There must always be a root volume setup. It will be the 2142 # full size volume if no other volume has this setup 2143 volume_management = self.get_volume_management() 2144 root_volume_name = \ 2145 defaults.ROOT_VOLUME_NAME if volume_management == 'lvm' else '' 2146 if have_full_size_volume: 2147 size = 'freespace:' + format( 2148 Defaults.get_min_volume_mbytes(selected_filesystem) 2149 ) 2150 fullsize = False 2151 else: 2152 size = None 2153 fullsize = True 2154 volume_type_list.append( 2155 volume_type( 2156 name=root_volume_name, 2157 parent='', 2158 size=size, 2159 fullsize=fullsize, 2160 mountpoint=None, 2161 realpath='/', 2162 label=None, 2163 attributes=[], 2164 is_root_volume=True 2165 ) 2166 ) 2167 2168 if swap_mbytes and self.get_volume_management() == 'lvm': 2169 volume_type_list.append( 2170 volume_type( 2171 name=swap_name, 2172 parent='', 2173 size='size:{0}'.format(swap_mbytes), 2174 fullsize=False, 2175 mountpoint=None, 2176 realpath='swap', 2177 label='SWAP', 2178 attributes=[], 2179 is_root_volume=False 2180 ) 2181 ) 2182 2183 return volume_type_list
List of configured systemdisk volumes.
Each entry in the list is a tuple with the following information
- name: name of the volume
- size: size of the volume
- realpath: system path to lookup volume data. If no mountpoint is set the volume name is used as data path.
- mountpoint: volume mount point and volume data path
- fullsize: takes all space True|False
- attributes: list of volume attributes handled via chattr
Returns
Contains list of volume_type tuples .. code:: python [ volume_type( name=volume_name, parent=volume_parent, size=volume_size, realpath=path, mountpoint=path, fullsize=True, label=volume_label, attributes=['no-copy-on-write'], is_root_volume=True|False ) ]
2185 def get_volume_management(self) -> Optional[str]: 2186 """ 2187 Provides information which volume management system is used 2188 2189 :return: name of volume manager 2190 2191 :rtype: str 2192 """ 2193 volume_filesystems = ['btrfs'] 2194 selected_filesystem = self.build_type.get_filesystem() 2195 selected_system_disk = self.get_build_type_system_disk_section() 2196 volume_management = None 2197 if selected_system_disk and selected_system_disk.get_preferlvm(): 2198 # LVM volume management is preferred, use it 2199 volume_management = 'lvm' 2200 elif selected_filesystem in volume_filesystems and selected_system_disk: 2201 # specified filesystem has its own volume management system 2202 volume_management = selected_filesystem 2203 elif selected_system_disk: 2204 # systemdisk section is specified with non volume capable 2205 # filesystem and no volume management preference. So let's 2206 # use LVM by default 2207 volume_management = 'lvm' 2208 return volume_management
Provides information which volume management system is used
Returns
name of volume manager
2210 def get_drivers_list(self) -> List: 2211 """ 2212 List of driver names from all drivers sections matching 2213 configured profiles 2214 2215 :return: driver names 2216 2217 :rtype: list 2218 """ 2219 drivers_sections = self._profiled( 2220 self.xml_data.get_drivers() 2221 ) 2222 result = [] 2223 if drivers_sections: 2224 for driver in drivers_sections: 2225 for file_section in driver.get_file(): 2226 result.append(file_section.get_name()) 2227 return result
List of driver names from all drivers sections matching configured profiles
Returns
driver names
2229 def get_strip_list(self, section_type: str) -> List: 2230 """ 2231 List of strip names matching the given section type 2232 and profiles 2233 2234 :param str section_type: type name from packages section 2235 2236 :return: strip names 2237 2238 :rtype: list 2239 """ 2240 strip_sections = self._profiled( 2241 self.xml_data.get_strip() 2242 ) 2243 result = [] 2244 if strip_sections: 2245 for strip in strip_sections: 2246 if strip.get_type() == section_type: 2247 for file_section in strip.get_file(): 2248 result.append(file_section.get_name()) 2249 return result
List of strip names matching the given section type and profiles
Parameters
- str section_type: type name from packages section
Returns
strip names
2251 def get_strip_files_to_delete(self) -> List: 2252 """ 2253 Items to delete from strip section 2254 2255 :return: item names 2256 2257 :rtype: list 2258 """ 2259 return self.get_strip_list('delete')
Items to delete from strip section
Returns
item names
2261 def get_strip_tools_to_keep(self) -> List: 2262 """ 2263 Tools to keep from strip section 2264 2265 :return: tool names 2266 2267 :rtype: list 2268 """ 2269 return self.get_strip_list('tools')
Tools to keep from strip section
Returns
tool names
2271 def get_strip_libraries_to_keep(self) -> List: 2272 """ 2273 Libraries to keep from strip section 2274 2275 :return: librarie names 2276 2277 :rtype: list 2278 """ 2279 return self.get_strip_list('libs')
Libraries to keep from strip section
Returns
librarie names
2281 def get_include_section_reference_file_names(self) -> List[str]: 2282 """ 2283 List of all <include> section file name references 2284 2285 :return: List[str] 2286 2287 :rtype: list 2288 """ 2289 include_files = [] 2290 for include in self.xml_data.get_include(): 2291 include_files.append(include.get_from()) 2292 return include_files
List of all
Returns
List[str]
2294 def get_repository_sections(self) -> List: 2295 """ 2296 List of all repository sections for the selected profiles that 2297 matches the host architecture 2298 2299 :return: <repository> section reference(s) 2300 2301 :rtype: list 2302 """ 2303 repository_list = [] 2304 for repository in self._profiled(self.xml_data.get_repository()): 2305 if self.repository_matches_host_architecture(repository): 2306 repository_list.append(repository) 2307 return repository_list
List of all repository sections for the selected profiles that matches the host architecture
Returns
section reference(s)
2309 def get_containers_sections(self) -> List: 2310 """ 2311 List of all containers sections for the selected profiles that 2312 matches the host architecture 2313 2314 :return: <containers> section reference(s) 2315 2316 :rtype: list 2317 """ 2318 containers_list = [] 2319 for containers in self._profiled(self.xml_data.get_containers()): 2320 if self.containers_matches_host_architecture(containers): 2321 containers_list.append(containers) 2322 return containers_list
List of all containers sections for the selected profiles that matches the host architecture
Returns
section reference(s)
2324 def get_repository_sections_used_for_build(self) -> List: 2325 """ 2326 List of all repositorys sections used to build the image and 2327 matching configured profiles. 2328 2329 :return: <repository> section reference(s) 2330 2331 :rtype: list 2332 """ 2333 repos = self.get_repository_sections() 2334 return list( 2335 repo for repo in repos if not repo.get_imageonly() 2336 )
List of all repositorys sections used to build the image and matching configured profiles.
Returns
section reference(s)
2338 def get_repository_sections_used_in_image(self) -> List: 2339 """ 2340 List of all repositorys sections to be configured in the resulting 2341 image matching configured profiles. 2342 2343 :return: <repository> section reference(s) 2344 2345 :rtype: list 2346 """ 2347 repos = self.get_repository_sections() 2348 return list( 2349 repo for repo in repos 2350 if repo.get_imageinclude() or repo.get_imageonly() 2351 )
List of all repositorys sections to be configured in the resulting image matching configured profiles.
Returns
section reference(s)
2353 def delete_repository_sections(self) -> None: 2354 """ 2355 Delete all repository sections matching configured profiles 2356 """ 2357 self.xml_data.set_repository([])
Delete all repository sections matching configured profiles
2359 def delete_repository_sections_used_for_build(self) -> None: 2360 """ 2361 Delete all repository sections used to build the image matching 2362 configured profiles 2363 """ 2364 used_for_build = self.get_repository_sections_used_for_build() 2365 all_repos = self.get_repository_sections() 2366 self.xml_data.set_repository( 2367 [ 2368 repo for repo in all_repos if repo not in used_for_build 2369 ] 2370 )
Delete all repository sections used to build the image matching configured profiles
2372 def get_repositories_signing_keys(self) -> List[str]: 2373 """ 2374 Get list of signing keys specified on the repositories 2375 """ 2376 key_file_list: List[str] = [] 2377 release_version = self.get_release_version() 2378 release_vars = [ 2379 '$releasever', 2380 '${releasever}' 2381 ] 2382 for repository in self.get_repository_sections() or []: 2383 for signing in repository.get_source().get_signing() or []: 2384 normalized_key_url = Uri(signing.get_key()).translate() 2385 if release_version: 2386 for release_var in release_vars: 2387 if release_var in normalized_key_url: 2388 normalized_key_url = normalized_key_url.replace( 2389 release_var, release_version 2390 ) 2391 if normalized_key_url not in key_file_list: 2392 key_file_list.append(normalized_key_url) 2393 return key_file_list
Get list of signing keys specified on the repositories
2395 def set_repository( 2396 self, repo_source: str, repo_type: str, repo_alias: str, 2397 repo_prio: str, repo_imageinclude: bool = False, 2398 repo_package_gpgcheck: Optional[bool] = None, 2399 repo_signing_keys: List[str] = [], components: str = None, 2400 distribution: str = None, repo_gpgcheck: Optional[bool] = None, 2401 repo_sourcetype: str = None 2402 ) -> None: 2403 """ 2404 Overwrite repository data of the first repository 2405 2406 :param str repo_source: repository URI 2407 :param str repo_type: type name defined by schema 2408 :param str repo_alias: alias name 2409 :param str repo_prio: priority number, package manager specific 2410 :param bool repo_imageinclude: setup repository inside of the image 2411 :param bool repo_package_gpgcheck: enable/disable package gpg checks 2412 :param list repo_signing_keys: list of signing key file names 2413 :param str components: component names for debian repos 2414 :param str distribution: base distribution name for debian repos 2415 :param bool repo_gpgcheck: enable/disable repo gpg checks 2416 """ 2417 repository_sections = self.get_repository_sections() 2418 if repository_sections: 2419 repository = repository_sections[0] 2420 if repo_alias: 2421 repository.set_alias(repo_alias) 2422 if repo_type: 2423 repository.set_type(repo_type) 2424 if repo_source: 2425 repository.get_source().set_path(repo_source) 2426 if repo_prio: 2427 repository.set_priority(int(repo_prio)) 2428 if repo_imageinclude: 2429 repository.set_imageinclude(repo_imageinclude) 2430 if repo_package_gpgcheck is not None: 2431 repository.set_package_gpgcheck(repo_package_gpgcheck) 2432 if repo_signing_keys: 2433 repository.get_source().set_signing( 2434 [xml_parse.signing(key=k) for k in repo_signing_keys] 2435 ) 2436 if components: 2437 repository.set_components(components) 2438 if distribution: 2439 repository.set_distribution(distribution) 2440 if repo_gpgcheck is not None: 2441 repository.set_repository_gpgcheck(repo_gpgcheck) 2442 if repo_sourcetype: 2443 repository.set_sourcetype(repo_sourcetype)
Overwrite repository data of the first repository
Parameters
- str repo_source: repository URI
- str repo_type: type name defined by schema
- str repo_alias: alias name
- str repo_prio: priority number, package manager specific
- bool repo_imageinclude: setup repository inside of the image
- bool repo_package_gpgcheck: enable/disable package gpg checks
- list repo_signing_keys: list of signing key file names
- str components: component names for debian repos
- str distribution: base distribution name for debian repos
- bool repo_gpgcheck: enable/disable repo gpg checks
2445 def add_repository( 2446 self, repo_source: str, repo_type: str, repo_alias: str = None, 2447 repo_prio: str = '', repo_imageinclude: bool = False, 2448 repo_package_gpgcheck: Optional[bool] = None, 2449 repo_signing_keys: List[str] = [], components: str = None, 2450 distribution: str = None, repo_gpgcheck: Optional[bool] = None, 2451 repo_sourcetype: str = None 2452 ) -> None: 2453 """ 2454 Add a new repository section at the end of the list 2455 2456 :param str repo_source: repository URI 2457 :param str repo_type: type name defined by schema 2458 :param str repo_alias: alias name 2459 :param str repo_prio: priority number, package manager specific 2460 :param bool repo_imageinclude: setup repository inside of the image 2461 :param bool repo_package_gpgcheck: enable/disable package gpg checks 2462 :param list repo_signing_keys: list of signing key file names 2463 :param str components: component names for debian repos 2464 :param str distribution: base distribution name for debian repos 2465 :param bool repo_gpgcheck: enable/disable repo gpg checks 2466 """ 2467 priority_number: Optional[int] = None 2468 try: 2469 priority_number = int(repo_prio) 2470 except Exception: 2471 pass 2472 2473 self.xml_data.add_repository( 2474 xml_parse.repository( 2475 type_=repo_type, 2476 alias=repo_alias, 2477 priority=priority_number, 2478 source=xml_parse.source( 2479 path=repo_source, 2480 signing=[ 2481 xml_parse.signing(key=k) for k in repo_signing_keys 2482 ] 2483 ), 2484 imageinclude=repo_imageinclude, 2485 package_gpgcheck=repo_package_gpgcheck, 2486 repository_gpgcheck=repo_gpgcheck, 2487 components=components, 2488 distribution=distribution, 2489 sourcetype=repo_sourcetype 2490 ) 2491 )
Add a new repository section at the end of the list
Parameters
- str repo_source: repository URI
- str repo_type: type name defined by schema
- str repo_alias: alias name
- str repo_prio: priority number, package manager specific
- bool repo_imageinclude: setup repository inside of the image
- bool repo_package_gpgcheck: enable/disable package gpg checks
- list repo_signing_keys: list of signing key file names
- str components: component names for debian repos
- str distribution: base distribution name for debian repos
- bool repo_gpgcheck: enable/disable repo gpg checks
2493 def add_certificate(self, cert_file: str, target_distribution: str) -> None: 2494 """ 2495 Add <certificate name="cert_file"> to main <certificates> section 2496 The main section will be created if it does not exist. Also 2497 setup the target_distribution in the resulting main section. 2498 """ 2499 certificates_section = self._profiled( 2500 self.xml_data.get_certificates() 2501 ) 2502 if not certificates_section: 2503 self.xml_data.set_certificates( 2504 [ 2505 xml_parse.certificates( 2506 target_distribution=target_distribution, 2507 certificate=[xml_parse.certificate(name=cert_file)] 2508 ) 2509 ] 2510 ) 2511 else: 2512 certificates_section[0].set_target_distribution( 2513 target_distribution 2514 ) 2515 certificates_section[0].add_certificate( 2516 xml_parse.certificate( 2517 name=cert_file 2518 ) 2519 )
Add
2521 def get_certificates(self) -> List[str]: 2522 """ 2523 Read list of certificates 2524 """ 2525 cert_list = [] 2526 certificates_section = self._profiled( 2527 self.xml_data.get_certificates() 2528 ) 2529 if certificates_section: 2530 for certificate in certificates_section[0].get_certificate(): 2531 cert_list.append(certificate.get_name()) 2532 return sorted(list(set(cert_list)))
Read list of certificates
2534 def get_certificates_target_distribution(self) -> str: 2535 """ 2536 Read CA target distribution 2537 """ 2538 target_distribution = '' 2539 certificates_section = self._profiled( 2540 self.xml_data.get_certificates() 2541 ) 2542 if certificates_section: 2543 target_distribution = \ 2544 certificates_section[0].get_target_distribution() 2545 return target_distribution
Read CA target distribution
2547 def resolve_this_path(self) -> None: 2548 """ 2549 Resolve any this:// repo source path into the path 2550 representing the target inside of the image description 2551 directory 2552 """ 2553 for repository in self.get_repository_sections() or []: 2554 repo_source = repository.get_source() 2555 repo_path = repo_source.get_path() 2556 if repo_path.startswith('this://'): 2557 repo_path = repo_path.replace('this://', '') 2558 repo_source.set_path( 2559 'dir://{0}'.format( 2560 os.path.realpath( 2561 os.path.join( 2562 self.xml_data.description_dir, repo_path 2563 ) 2564 ) 2565 ) 2566 )
Resolve any this:// repo source path into the path representing the target inside of the image description directory
2568 def copy_displayname(self, target_state: Any) -> None: 2569 """ 2570 Copy image displayname from this xml state to the target xml state 2571 2572 :param object target_state: XMLState instance 2573 """ 2574 displayname = self.xml_data.get_displayname() 2575 if displayname: 2576 target_state.xml_data.set_displayname(displayname)
Copy image displayname from this xml state to the target xml state
Parameters
- object target_state: XMLState instance
2578 def copy_name(self, target_state: Any) -> None: 2579 """ 2580 Copy image name from this xml state to the target xml state 2581 2582 :param object target_state: XMLState instance 2583 """ 2584 target_state.xml_data.set_name( 2585 self.xml_data.get_name() 2586 )
Copy image name from this xml state to the target xml state
Parameters
- object target_state: XMLState instance
2588 def copy_drivers_sections(self, target_state: Any) -> None: 2589 """ 2590 Copy drivers sections from this xml state to the target xml state 2591 2592 :param object target_state: XMLState instance 2593 """ 2594 drivers_sections = self._profiled( 2595 self.xml_data.get_drivers() 2596 ) 2597 if drivers_sections: 2598 for drivers_section in drivers_sections: 2599 target_state.xml_data.add_drivers(drivers_section)
Copy drivers sections from this xml state to the target xml state
Parameters
- object target_state: XMLState instance
2601 def copy_systemdisk_section(self, target_state: Any) -> None: 2602 """ 2603 Copy systemdisk sections from this xml state to the target xml state 2604 2605 :param object target_state: XMLState instance 2606 """ 2607 systemdisk_section = self.get_build_type_system_disk_section() 2608 if systemdisk_section: 2609 target_state.build_type.set_systemdisk( 2610 [systemdisk_section] 2611 )
Copy systemdisk sections from this xml state to the target xml state
Parameters
- object target_state: XMLState instance
2613 def copy_strip_sections(self, target_state: Any) -> None: 2614 """ 2615 Copy strip sections from this xml state to the target xml state 2616 2617 :param object target_state: XMLState instance 2618 """ 2619 strip_sections = self._profiled( 2620 self.xml_data.get_strip() 2621 ) 2622 if strip_sections: 2623 for strip_section in strip_sections: 2624 target_state.xml_data.add_strip(strip_section)
Copy strip sections from this xml state to the target xml state
Parameters
- object target_state: XMLState instance
2626 def copy_machine_section(self, target_state: Any) -> None: 2627 """ 2628 Copy machine sections from this xml state to the target xml state 2629 2630 :param object target_state: XMLState instance 2631 """ 2632 machine_section = self.get_build_type_machine_section() 2633 if machine_section: 2634 target_state.build_type.set_machine( 2635 [machine_section] 2636 )
Copy machine sections from this xml state to the target xml state
Parameters
- object target_state: XMLState instance
2638 def copy_bootloader_section(self, target_state: Any) -> None: 2639 """ 2640 Copy bootloader section from this xml state to the target xml state 2641 2642 :param object target_state: XMLState instance 2643 """ 2644 bootloader_section = self.get_build_type_bootloader_section() 2645 if bootloader_section: 2646 target_state.build_type.set_bootloader( 2647 [bootloader_section] 2648 )
Copy bootloader section from this xml state to the target xml state
Parameters
- object target_state: XMLState instance
2650 def copy_oemconfig_section(self, target_state: Any) -> None: 2651 """ 2652 Copy oemconfig sections from this xml state to the target xml state 2653 2654 :param object target_state: XMLState instance 2655 """ 2656 oemconfig_section = self.get_build_type_oemconfig_section() 2657 if oemconfig_section: 2658 target_state.build_type.set_oemconfig( 2659 [oemconfig_section] 2660 )
Copy oemconfig sections from this xml state to the target xml state
Parameters
- object target_state: XMLState instance
2662 def copy_repository_sections( 2663 self, target_state: Any, wipe: bool = False 2664 ) -> None: 2665 """ 2666 Copy repository sections from this xml state to the target xml state 2667 2668 :param object target_state: XMLState instance 2669 :param bool wipe: delete all repos in target prior to copy 2670 """ 2671 repository_sections = self._profiled( 2672 self.xml_data.get_repository() 2673 ) 2674 if repository_sections: 2675 if wipe: 2676 target_state.xml_data.set_repository([]) 2677 for repository_section in repository_sections: 2678 repository_copy = copy.deepcopy(repository_section) 2679 # profiles are not copied because they might not exist 2680 # in the target description 2681 repository_copy.set_profiles(None) 2682 target_state.xml_data.add_repository(repository_copy)
Copy repository sections from this xml state to the target xml state
Parameters
- object target_state: XMLState instance
- bool wipe: delete all repos in target prior to copy
2684 def copy_preferences_subsections( 2685 self, section_names: List, target_state: Any 2686 ) -> None: 2687 """ 2688 Copy subsections of the preferences sections, matching given 2689 section names, from this xml state to the target xml state 2690 2691 :param list section_names: preferences subsection names 2692 :param object target_state: XMLState instance 2693 """ 2694 target_preferences_sections = target_state.get_preferences_sections() 2695 if target_preferences_sections: 2696 target_preferences_section = target_preferences_sections[0] 2697 for preferences_section in self.get_preferences_sections(): 2698 for section_name in section_names: 2699 get_section_method = getattr( 2700 preferences_section, 'get_' + section_name 2701 ) 2702 section = get_section_method() 2703 if section: 2704 set_section_method = getattr( 2705 target_preferences_section, 'set_' + section_name 2706 ) 2707 set_section_method(section)
Copy subsections of the preferences sections, matching given section names, from this xml state to the target xml state
Parameters
- list section_names: preferences subsection names
- object target_state: XMLState instance
2709 def copy_build_type_attributes( 2710 self, attribute_names: List, target_state: Any 2711 ) -> None: 2712 """ 2713 Copy specified attributes from this build type section to the 2714 target xml state build type section 2715 2716 :param list attribute_names: type section attributes 2717 :param object target_state: XMLState instance 2718 """ 2719 for attribute in attribute_names: 2720 get_type_method = getattr( 2721 self.build_type, 'get_' + attribute 2722 ) 2723 attribute_value = get_type_method() 2724 if attribute_value: 2725 set_type_method = getattr( 2726 target_state.build_type, 'set_' + attribute 2727 ) 2728 set_type_method(attribute_value)
Copy specified attributes from this build type section to the target xml state build type section
Parameters
- list attribute_names: type section attributes
- object target_state: XMLState instance
2730 def copy_bootincluded_packages(self, target_state: Any) -> None: 2731 """ 2732 Copy packages marked as bootinclude to the packages 2733 type=bootstrap section in the target xml state. The package 2734 will also be removed from the packages type=delete section 2735 in the target xml state if present there 2736 2737 :param object target_state: XMLState instance 2738 """ 2739 target_packages_sections = \ 2740 target_state.get_bootstrap_packages_sections() 2741 if target_packages_sections: 2742 target_packages_section = \ 2743 target_packages_sections[0] 2744 package_names_added = [] 2745 packages_sections = self.get_packages_sections( 2746 ['image', 'bootstrap', self.get_build_type_name()] 2747 ) 2748 package_list = self.get_package_sections( 2749 packages_sections 2750 ) 2751 if package_list: 2752 for package in package_list: 2753 if package.package_section.get_bootinclude(): 2754 target_packages_section.add_package( 2755 xml_parse.package( 2756 name=package.package_section.get_name() 2757 ) 2758 ) 2759 package_names_added.append( 2760 package.package_section.get_name() 2761 ) 2762 delete_packages_sections = target_state.get_packages_sections( 2763 ['delete'] 2764 ) 2765 package_list = self.get_package_sections( 2766 delete_packages_sections 2767 ) 2768 if package_list: 2769 for package in package_list: 2770 package_name = package.package_section.get_name() 2771 if package_name in package_names_added: 2772 package.packages_section.package.remove( 2773 package.package_section 2774 )
Copy packages marked as bootinclude to the packages type=bootstrap section in the target xml state. The package will also be removed from the packages type=delete section in the target xml state if present there
Parameters
- object target_state: XMLState instance
2776 def copy_bootincluded_archives(self, target_state: Any) -> None: 2777 """ 2778 Copy archives marked as bootinclude to the packages type=bootstrap 2779 section in the target xml state 2780 2781 :param object target_state: XMLState instance 2782 """ 2783 target_bootstrap_packages_sections = \ 2784 target_state.get_bootstrap_packages_sections() 2785 if target_bootstrap_packages_sections: 2786 target_bootstrap_packages_section = \ 2787 target_bootstrap_packages_sections[0] 2788 packages_sections = self.get_packages_sections( 2789 ['image', 'bootstrap', self.get_build_type_name()] 2790 ) 2791 for packages_section in packages_sections: 2792 archive_list = packages_section.get_archive() 2793 if archive_list: 2794 for archive in archive_list: 2795 if archive.get_bootinclude(): 2796 target_bootstrap_packages_section.add_archive( 2797 xml_parse.archive( 2798 name=archive.get_name() 2799 ) 2800 )
Copy archives marked as bootinclude to the packages type=bootstrap section in the target xml state
Parameters
- object target_state: XMLState instance
2802 def copy_bootdelete_packages(self, target_state: Any) -> None: 2803 """ 2804 Copy packages marked as bootdelete to the packages type=delete 2805 section in the target xml state 2806 2807 :param object target_state: XMLState instance 2808 """ 2809 target_delete_packages_sections = target_state.get_packages_sections( 2810 ['delete'] 2811 ) 2812 if not target_delete_packages_sections: 2813 target_delete_packages_sections = [ 2814 xml_parse.packages(type_='delete') 2815 ] 2816 target_state.xml_data.add_packages( 2817 target_delete_packages_sections[0] 2818 ) 2819 2820 target_delete_packages_section = \ 2821 target_delete_packages_sections[0] 2822 packages_sections = self.get_packages_sections( 2823 ['image', 'bootstrap', self.get_build_type_name()] 2824 ) 2825 package_list = self.get_package_sections( 2826 packages_sections 2827 ) 2828 if package_list: 2829 for package in package_list: 2830 if package.package_section.get_bootdelete(): 2831 target_delete_packages_section.add_package( 2832 xml_parse.package( 2833 name=package.package_section.get_name() 2834 ) 2835 )
Copy packages marked as bootdelete to the packages type=delete section in the target xml state
Parameters
- object target_state: XMLState instance
2837 def get_distribution_name_from_boot_attribute(self) -> str: 2838 """ 2839 Extract the distribution name from the boot attribute of the 2840 build type section. 2841 2842 If no boot attribute is configured or the contents does not 2843 match the kiwi defined naming schema for boot image descriptions, 2844 an exception is thrown 2845 2846 :return: lowercase distribution name 2847 2848 :rtype: str 2849 """ 2850 boot_attribute = self.build_type.get_boot() 2851 if not boot_attribute: 2852 raise KiwiDistributionNameError( 2853 'No boot attribute to extract distribution name from found' 2854 ) 2855 boot_attribute_format = '^.*-(.*)$' 2856 boot_attribute_expression = re.match( 2857 boot_attribute_format, boot_attribute 2858 ) 2859 if not boot_attribute_expression: 2860 raise KiwiDistributionNameError( 2861 'Boot attribute "%s" does not match expected format %s' % 2862 (boot_attribute, boot_attribute_format) 2863 ) 2864 return boot_attribute_expression.group(1).lower()
Extract the distribution name from the boot attribute of the build type section.
If no boot attribute is configured or the contents does not match the kiwi defined naming schema for boot image descriptions, an exception is thrown
Returns
lowercase distribution name
2866 def get_fs_mount_option_list(self) -> List: 2867 """ 2868 List of root filesystem mount options 2869 2870 The list contains one element with the information from the 2871 fsmountoptions attribute. The value there is passed along to 2872 the -o mount option 2873 2874 :return: max one element list with mount option string 2875 2876 :rtype: list 2877 """ 2878 option_list = [] 2879 mount_options = self.build_type.get_fsmountoptions() 2880 if mount_options: 2881 option_list = [mount_options] 2882 2883 return option_list
List of root filesystem mount options
The list contains one element with the information from the fsmountoptions attribute. The value there is passed along to the -o mount option
Returns
max one element list with mount option string
2885 def get_fs_create_option_list(self) -> List: 2886 """ 2887 List of root filesystem creation options 2888 2889 The list contains elements with the information from the 2890 fscreateoptions attribute string that got split into its 2891 substring components 2892 2893 :return: list with create options 2894 2895 :rtype: list 2896 """ 2897 option_list = [] 2898 create_options = self.build_type.get_fscreateoptions() 2899 if create_options: 2900 option_list = create_options.split() 2901 2902 return option_list
List of root filesystem creation options
The list contains elements with the information from the fscreateoptions attribute string that got split into its substring components
Returns
list with create options
2904 def get_luks_credentials(self) -> Optional[str]: 2905 """ 2906 Return key or passphrase credentials to open the luks pool 2907 2908 :return: data 2909 2910 :rtype: str 2911 """ 2912 data = self.build_type.get_luks() 2913 if data: 2914 keyfile_name = None 2915 try: 2916 # try to interpret data as an URI 2917 uri = Uri(data) 2918 if not uri.is_remote(): 2919 keyfile_name = uri.translate() 2920 except Exception: 2921 # this doesn't look like a valid URI, continue as just data 2922 pass 2923 if keyfile_name: 2924 try: 2925 with open(keyfile_name) as keyfile: 2926 return keyfile.read() 2927 except Exception as issue: 2928 raise KiwiFileAccessError( 2929 f'Failed to read from {keyfile_name!r}: {issue}' 2930 ) 2931 return data
Return key or passphrase credentials to open the luks pool
Returns
data
2933 def get_luks_format_options(self) -> List[str]: 2934 """ 2935 Return list of luks format options 2936 2937 :return: list of options 2938 2939 :rtype: list 2940 """ 2941 result = [] 2942 luksversion = self.build_type.get_luks_version() 2943 luksformat = self.build_type.get_luksformat() 2944 luks_pbkdf = self.build_type.get_luks_pbkdf() 2945 if luksversion: 2946 result.append('--type') 2947 result.append(luksversion) 2948 if luksformat: 2949 for option in luksformat[0].get_option(): 2950 result.append(option.get_name()) 2951 if option.get_value(): 2952 result.append(option.get_value()) 2953 if luks_pbkdf: 2954 # Allow to override the pbkdf algorithm that cryptsetup 2955 # uses by default. Cryptsetup may use argon2i by default, 2956 # which is not supported by all bootloaders. 2957 result.append('--pbkdf') 2958 result.append(luks_pbkdf) 2959 return result
Return list of luks format options
Returns
list of options
2961 def get_derived_from_image_uri(self) -> List[Uri]: 2962 """ 2963 Uri object(s) of derived image if configured 2964 2965 Specific image types can be based on one ore more derived 2966 images. This method returns the location of this image(s) 2967 when configured in the XML description 2968 2969 :return: List of Uri instances 2970 2971 :rtype: list 2972 """ 2973 image_uris = [] 2974 derived_images = self.build_type.get_derived_from() 2975 if derived_images: 2976 for derived_image in derived_images.split(','): 2977 image_uris.append( 2978 Uri(derived_image, repo_type='container') 2979 ) 2980 return image_uris
Uri object(s) of derived image if configured
Specific image types can be based on one ore more derived images. This method returns the location of this image(s) when configured in the XML description
Returns
List of Uri instances
2982 def set_derived_from_image_uri(self, uri: str) -> None: 2983 """ 2984 Set derived_from attribute to a new value 2985 2986 In order to set a new value the derived_from attribute 2987 must be already present in the image configuration 2988 2989 :param str uri: URI 2990 """ 2991 if self.build_type.get_derived_from(): 2992 self.build_type.set_derived_from(uri) 2993 else: 2994 message = dedent('''\n 2995 No derived_from attribute configured in image <type> 2996 2997 In order to set the uri {0} as base container reference 2998 an initial derived_from attribute must be set in the 2999 type section 3000 ''') 3001 log.warning(message.format(uri))
Set derived_from attribute to a new value
In order to set a new value the derived_from attribute must be already present in the image configuration
Parameters
- str uri: URI
3003 def set_root_partition_uuid(self, uuid: str) -> None: 3004 """ 3005 Store PARTUUID provided in uuid as state information 3006 3007 :param str uuid: PARTUUID 3008 """ 3009 self.root_partition_uuid = uuid
Store PARTUUID provided in uuid as state information
Parameters
- str uuid: PARTUUID
3011 def get_root_partition_uuid(self) -> Optional[str]: 3012 """ 3013 Return preserved PARTUUID 3014 """ 3015 return self.root_partition_uuid
Return preserved PARTUUID
3017 def set_root_filesystem_uuid(self, uuid: str) -> None: 3018 """ 3019 Store UUID provided in uuid as state information 3020 3021 :param str uuid: UUID 3022 """ 3023 self.root_filesystem_uuid = uuid
Store UUID provided in uuid as state information
Parameters
- str uuid: UUID
3025 def get_root_filesystem_uuid(self) -> Optional[str]: 3026 """ 3027 Return preserved UUID 3028 """ 3029 return self.root_filesystem_uuid
Return preserved UUID
3031 @staticmethod 3032 def get_archives_target_dirs( 3033 packages_sections_names: Optional[List[xml_parse.packages]] 3034 ) -> Dict: 3035 """ 3036 Dict of archive names and target dirs for packages section(s), if any 3037 :return: archive names and its target dir 3038 :rtype: dict 3039 """ 3040 result = {} 3041 if packages_sections_names: 3042 for package_section_name in packages_sections_names: 3043 for archive in package_section_name.get_archive(): 3044 result[archive.get_name().strip()] = archive.get_target_dir() 3045 3046 return result
Dict of archive names and target dirs for packages section(s), if any
Returns
archive names and its target dir
3048 def get_bootstrap_archives_target_dirs(self) -> Dict: 3049 """ 3050 Dict of archive names and target dirs from the type="bootstrap" 3051 packages section(s) 3052 :return: archive names and its target dir 3053 :rtype: dict 3054 """ 3055 return self.get_archives_target_dirs( 3056 self.get_packages_sections(['bootstrap']) 3057 )
Dict of archive names and target dirs from the type="bootstrap" packages section(s)
Returns
archive names and its target dir
3059 def get_system_archives_target_dirs(self) -> Dict: 3060 """ 3061 Dict of archive names and its target dir from the packages sections matching 3062 type="image" and type=build_type 3063 :return: archive names and its target dir 3064 :rtype: dict 3065 """ 3066 return self.get_archives_target_dirs( 3067 self.get_packages_sections(['image', self.get_build_type_name()]) 3068 )
Dict of archive names and its target dir from the packages sections matching type="image" and type=build_type
Returns
archive names and its target dir