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