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