diff --git a/documentation/Installation.md b/documentation/Installation.md index 65848cc94..ca42109dc 100644 --- a/documentation/Installation.md +++ b/documentation/Installation.md @@ -168,7 +168,7 @@ For cluster machines, ensure the following requirements are met: * CentOS Stream 9 * RHEL 8.4, 8.6, 8.7, 8.8, 8.9, 8.10, 9.2, 9.3, 9.4 * Oracle Linux 8.4, 9.2 - * RockyLinux 8.6, 8.7, 8.8, 9.2, 9.3, 9.4, 9.5, 9.6 + * RockyLinux 8.6, 8.7, 8.8, 9.2, 9.3, 9.4, 9.5, 9.6, 10.1 * Ubuntu 20.04, 22.04.1, 24.04.1 **Note**: Ubuntu 24.04 is supported only with kubernetes versions starting from v1.29.7 and above. diff --git a/kubemarine/core/cluster.py b/kubemarine/core/cluster.py index fa852726d..9d76f63a6 100755 --- a/kubemarine/core/cluster.py +++ b/kubemarine/core/cluster.py @@ -589,7 +589,7 @@ def get_os_family_for_nodes(self, hosts: Iterable[str]) -> str: The method skips inaccessible nodes unless all nodes are inaccessible. :return: Detected OS family, possible values: "debian", "rhel8", "rhel9", - "multiple", "unknown", "unsupported", "". + "rhel10","multiple", "unknown", "unsupported", "". """ os_families = {self.get_os_family_for_node(host) for host in hosts} if os_families == {''}: @@ -609,7 +609,7 @@ def get_os_family(self) -> str: The method skips inaccessible nodes unless all nodes are inaccessible. :return: Detected OS family, possible values: "debian", "rhel8", "rhel9", - "multiple", "unknown", "unsupported", "". + "rhel10","multiple", "unknown", "unsupported", "". """ return self.nodes['all'].get_nodes_os() diff --git a/kubemarine/core/group.py b/kubemarine/core/group.py index 090210370..c50a5d092 100755 --- a/kubemarine/core/group.py +++ b/kubemarine/core/group.py @@ -634,7 +634,7 @@ def get_nodes_os(self) -> str: Returns the detected operating system family for group. :return: Detected OS family, possible values: "debian", "rhel8", "rhel9", - "multiple", "unknown", "unsupported", "". + "rhel10","multiple", "unknown", "unsupported", "". """ return self.cluster.get_os_family_for_nodes(self.nodes) @@ -655,7 +655,7 @@ def get_subgroup_with_os(self: GROUP_SELF, os_families: Union[str, List[str]]) - os_families = [os_families] for os_family in os_families: - if os_family not in ['debian', 'rhel8', 'rhel9']: + if os_family not in ['debian', 'rhel8', 'rhel9', 'rhel10']: raise Exception('Unsupported OS family provided') hosts = [] for host in self.nodes: diff --git a/kubemarine/modprobe.py b/kubemarine/modprobe.py index 53382b9f1..52562e909 100644 --- a/kubemarine/modprobe.py +++ b/kubemarine/modprobe.py @@ -31,7 +31,7 @@ def enrich_kernel_modules(cluster: KubernetesCluster) -> None: The method enrich the list of kernel modules ('services.modprobe') according to OS family """ - for os_family in ('debian', 'rhel8', 'rhel9'): + for os_family in ('debian', 'rhel8', 'rhel9', 'rhel10'): # Remove the section for OS families if no node has these OS families. modprobe_config = cluster.inventory["services"]["modprobe"] if cluster.nodes['all'].get_subgroup_with_os(os_family).is_empty(): diff --git a/kubemarine/packages.py b/kubemarine/packages.py index fa87b6f43..8722bd2b6 100644 --- a/kubemarine/packages.py +++ b/kubemarine/packages.py @@ -481,8 +481,8 @@ def disable_unattended_upgrade(group: NodeGroup) -> None: def get_associations_os_family_keys() -> List[str]: - # Generic 'rhel' is removed; use rhel8/rhel9. - return ['debian', 'rhel8', 'rhel9'] + # Generic 'rhel' is removed; use rhel8/rhel9/rhel10. + return ['debian', 'rhel8', 'rhel9', 'rhel10'] def get_compatibility_version_key(os_family: str) -> str: @@ -536,7 +536,7 @@ def search(self, group: DeferredGroup, package: str, callback: Callback = None) def get_package_manager(group: AbstractGroup[GROUP_RUN_TYPE]) -> PackageManager: os_family = group.get_nodes_os() - if os_family in ['rhel8', 'rhel9']: + if os_family in ['rhel8', 'rhel9', 'rhel10']: return yum elif os_family == 'debian': return apt @@ -596,7 +596,7 @@ def search_package(group: DeferredGroup, package: str, callback: Callback = None def get_detect_package_version_cmd(os_family: str, package_name: str) -> str: - if os_family in ["rhel8", "rhel9"]: + if os_family in ["rhel8", "rhel9", "rhel10"]: cmd = r"rpm -q %s" % package_name else: cmd = r"dpkg-query -f '${Package}=${Version}\n' -W %s" % package_name @@ -682,7 +682,7 @@ def get_package_name(os_family: str, package: str) -> str: package_name = "" if package: - if os_family in ["rhel8", "rhel9"]: + if os_family in ["rhel8", "rhel9", "rhel10"]: # regexp is needed to split package and its version, the pattern start with '-' then should be number or '*' package_name = re.split(r'-[\d,\*]', package)[0] else: diff --git a/kubemarine/patches/software_upgrade.yaml b/kubemarine/patches/software_upgrade.yaml index c31257e7a..035554ee9 100644 --- a/kubemarine/patches/software_upgrade.yaml +++ b/kubemarine/patches/software_upgrade.yaml @@ -17,13 +17,16 @@ packages: containerdio: version_rhel8: [] version_rhel9: [] + version_rhel10: [] haproxy: version_rhel8: false version_rhel9: false + version_rhel10: false version_debian: false keepalived: version_rhel8: false version_rhel9: false + version_rhel10: false version_debian: false plugins: calico: [] diff --git a/kubemarine/procedures/backup.py b/kubemarine/procedures/backup.py index 48c588605..feb59a16e 100755 --- a/kubemarine/procedures/backup.py +++ b/kubemarine/procedures/backup.py @@ -97,7 +97,7 @@ def export_ansible_inventory(cluster: KubernetesCluster) -> None: def export_packages_list(cluster: KubernetesCluster) -> None: cluster.context['backup_descriptor']['nodes']['packages'] = {} - if cluster.get_os_family() in ['rhel8', 'rhel9']: + if cluster.get_os_family() in ['rhel8', 'rhel9', 'rhel10']: cmd = r"rpm -qa" else: cmd = r"dpkg-query -f '${Package}=${Version}\n' -W" diff --git a/kubemarine/procedures/check_iaas.py b/kubemarine/procedures/check_iaas.py index 55af9605c..d41d264ba 100755 --- a/kubemarine/procedures/check_iaas.py +++ b/kubemarine/procedures/check_iaas.py @@ -477,7 +477,7 @@ def check_access_to_package_repositories(cluster: KubernetesCluster) -> None: # TODO: think about better parsing repository_urls: List[str] = [] repositories = cluster.inventory['services']['packages']['package_manager'].get("repositories") - if cluster.get_os_family() not in ['debian', 'rhel8', 'rhel9']: + if cluster.get_os_family() not in ['debian', 'rhel8', 'rhel9', 'rhel10']: # Skip check in case of multiply or unknown OS raise TestWarn("Can't check package repositories on multiple or unknown OS") if isinstance(repositories, list): diff --git a/kubemarine/procedures/check_paas.py b/kubemarine/procedures/check_paas.py index af9492d2b..376696cc6 100755 --- a/kubemarine/procedures/check_paas.py +++ b/kubemarine/procedures/check_paas.py @@ -873,7 +873,7 @@ def verify_selinux_status(cluster: KubernetesCluster) -> None: :return: None """ with TestCase(cluster, '213', "Security", "Selinux security policy") as tc: - group = cluster.nodes['all'].get_subgroup_with_os(['rhel8', 'rhel9']) + group = cluster.nodes['all'].get_subgroup_with_os(['rhel8', 'rhel9', 'rhel10']) if group.is_empty(): return tc.success("No RHEL nodes found") _, selinux_result, selinux_parsed_result = \ @@ -931,7 +931,7 @@ def verify_selinux_config(cluster: KubernetesCluster) -> None: :return: None """ with TestCase(cluster, '214', "Security", "Selinux configuration") as tc: - group = cluster.nodes['all'].get_subgroup_with_os(['rhel8', 'rhel9']) + group = cluster.nodes['all'].get_subgroup_with_os(['rhel8', 'rhel9', 'rhel10']) if group.is_empty(): return tc.success("No RHEL nodes found") selinux_configured, selinux_result, _ = \ diff --git a/kubemarine/procedures/install.py b/kubemarine/procedures/install.py index 4ae7091f6..7eefd1b4c 100755 --- a/kubemarine/procedures/install.py +++ b/kubemarine/procedures/install.py @@ -255,11 +255,16 @@ def system_prepare_package_manager_disable_unattended_upgrades(group: NodeGroup) @_applicable_for_new_nodes_with_roles('all') def system_prepare_package_manager_manage_packages(group: NodeGroup) -> None: + cluster = group.cluster group.call_batch([ manage_mandatory_packages, manage_custom_packages ]) - + affected_hosts = system.detect_kernel_upgrade(group) #detecting if kernel is upgraded on any node. + #scheduling reboot if kernel upgrade detected, as it is required to apply new kernel version. + if affected_hosts: + cluster.log.debug(f"Scheduling reboot to apply updated kernel version.") + cluster.schedule_cumulative_point(system.reboot_nodes) def manage_mandatory_packages(group: NodeGroup) -> RunnersGroupResult: cluster: KubernetesCluster = group.cluster @@ -566,6 +571,7 @@ def overview(cluster: KubernetesCluster) -> None: # Reboot and verify that the most crucial system settings are applied on boot. # This is done before `prepare.system.audit`. system.reboot_nodes: [ + "prepare.system.modprobe", "prepare.system.audit" ], system.verify_system: [ diff --git a/kubemarine/procedures/migrate_kubemarine.py b/kubemarine/procedures/migrate_kubemarine.py index 8bf3608a5..1ed92218b 100644 --- a/kubemarine/procedures/migrate_kubemarine.py +++ b/kubemarine/procedures/migrate_kubemarine.py @@ -393,7 +393,7 @@ def resolve_upgrade_patches() -> List[_SoftwareUpgradePatch]: k8s_versions = [version for pkg in ('containerd', 'containerdio') - for v_key in ('version_rhel8', 'version_rhel9', 'version_debian') + for v_key in ('version_rhel8', 'version_rhel9', 'version_rhel10', 'version_debian') for version in upgrade_config['packages'][pkg].get(v_key, [])] if k8s_versions: verify_allowed_kubernetes_versions(k8s_versions) @@ -401,7 +401,7 @@ def resolve_upgrade_patches() -> List[_SoftwareUpgradePatch]: for package_name in ['haproxy', 'keepalived']: if any(upgrade_config['packages'][package_name].get(v_key) - for v_key in ('version_rhel8', 'version_rhel9', 'version_debian')): + for v_key in ('version_rhel8', 'version_rhel9', 'version_rhel10', 'version_debian')): upgrade_patches.append(BalancerUpgradePatch(upgrade_config, package_name)) default_plugins = static.DEFAULTS['plugins'] diff --git a/kubemarine/resources/configurations/compatibility/internal/packages.yaml b/kubemarine/resources/configurations/compatibility/internal/packages.yaml index 295cdab42..f1ccd8483 100644 --- a/kubemarine/resources/configurations/compatibility/internal/packages.yaml +++ b/kubemarine/resources/configurations/compatibility/internal/packages.yaml @@ -25,32 +25,42 @@ containerdio: v1.32.0: version_rhel8: 1.6* version_rhel9: 1.7* + version_rhel10: 1.7* v1.32.2: version_rhel8: 1.6* version_rhel9: 1.7* + version_rhel10: 1.7* v1.32.10: version_rhel8: 1.6* version_rhel9: 1.7* + version_rhel10: 1.7* v1.33.0: version_rhel8: 1.6* version_rhel9: 1.6* + version_rhel10: 1.7* v1.33.6: version_rhel8: 1.6* version_rhel9: 1.6* + version_rhel10: 1.7* v1.34.1: version_rhel8: 1.6* version_rhel9: 1.7* + version_rhel10: 1.7* v1.34.2: version_rhel8: 1.6* version_rhel9: 1.7* + version_rhel10: 1.7* v1.35.0: version_rhel8: 1.6* version_rhel9: 1.7* + version_rhel10: 1.7* haproxy: version_rhel8: 1.8* version_rhel9: 2.4* + version_rhel10: 3.2* version_debian: 2.* keepalived: version_rhel8: 2.1* version_rhel9: 2.2* + version_rhel10: 2.3* version_debian: 1:2.* diff --git a/kubemarine/resources/configurations/defaults.yaml b/kubemarine/resources/configurations/defaults.yaml index c1ef8dd07..ea09fd4a2 100644 --- a/kubemarine/resources/configurations/defaults.yaml +++ b/kubemarine/resources/configurations/defaults.yaml @@ -159,6 +159,7 @@ services: - modulename: nf_defrag_ipv6 install: '{{ nodes[0]["internal_address"] | isipv6 }}' rhel9: *modprobe-default-modules + rhel10: *modprobe-default-modules debian: *modprobe-default-modules sysctl: @@ -502,6 +503,20 @@ services: package_name: 'policycoreutils-python-utils' iptables: package_name: 'iptables-nft' + rhel10: + containerd: {} + haproxy: + executable_name: '/usr/sbin/haproxy' + service_name: 'haproxy' + keepalived: {} + audit: + package_name: 'audit' + conntrack: + package_name: 'conntrack-tools' + semanage: + package_name: 'policycoreutils-python-utils' + iptables: + package_name: 'iptables-nft' plugin_defaults: installation: {} diff --git a/kubemarine/resources/configurations/globals.yaml b/kubemarine/resources/configurations/globals.yaml index 3cc282ef1..603b6112b 100644 --- a/kubemarine/resources/configurations/globals.yaml +++ b/kubemarine/resources/configurations/globals.yaml @@ -194,6 +194,16 @@ packages: keepalived: package_name: - keepalived: keepalived + rhel10: + containerd: + package_name: + - containerd.io: containerdio + haproxy: + package_name: + - haproxy: haproxy + keepalived: + package_name: + - keepalived: keepalived common_associations: containerd: executable_name: 'containerd' @@ -324,6 +334,9 @@ compatibility_map: - '9.4' - '9.5' - '9.6' + - os_family: 'rhel10' + versions: + - '10.1' ubuntu: - os_family: 'debian' versions: diff --git a/kubemarine/resources/etalons/patches/software_upgrade.yaml b/kubemarine/resources/etalons/patches/software_upgrade.yaml index c31257e7a..035554ee9 100644 --- a/kubemarine/resources/etalons/patches/software_upgrade.yaml +++ b/kubemarine/resources/etalons/patches/software_upgrade.yaml @@ -17,13 +17,16 @@ packages: containerdio: version_rhel8: [] version_rhel9: [] + version_rhel10: [] haproxy: version_rhel8: false version_rhel9: false + version_rhel10: false version_debian: false keepalived: version_rhel8: false version_rhel9: false + version_rhel10: false version_debian: false plugins: calico: [] diff --git a/kubemarine/resources/schemas/definitions/services/modprobe.json b/kubemarine/resources/schemas/definitions/services/modprobe.json index 9fbdb79c2..7fb10d3e6 100644 --- a/kubemarine/resources/schemas/definitions/services/modprobe.json +++ b/kubemarine/resources/schemas/definitions/services/modprobe.json @@ -4,11 +4,12 @@ "properties": { "debian": {"$ref": "#/definitions/OSFamilyModules"}, "rhel8": {"$ref": "#/definitions/OSFamilyModules"}, - "rhel9": {"$ref": "#/definitions/OSFamilyModules"} + "rhel9": {"$ref": "#/definitions/OSFamilyModules"}, + "rhel10": {"$ref": "#/definitions/OSFamilyModules"} }, "propertyNames": { "anyOf": [ - {"enum": ["debian", "rhel8", "rhel9"]} + {"enum": ["debian", "rhel8", "rhel9", "rhel10"]} ] }, "definitions": { diff --git a/kubemarine/resources/schemas/definitions/services/packages/associations.json b/kubemarine/resources/schemas/definitions/services/packages/associations.json index 862640ad1..83dc54c8d 100644 --- a/kubemarine/resources/schemas/definitions/services/packages/associations.json +++ b/kubemarine/resources/schemas/definitions/services/packages/associations.json @@ -5,12 +5,13 @@ "properties": { "debian": {"$ref": "#/definitions/OSFamilyAssociations"}, "rhel8": {"$ref": "#/definitions/OSFamilyAssociations"}, - "rhel9": {"$ref": "#/definitions/OSFamilyAssociations"} + "rhel9": {"$ref": "#/definitions/OSFamilyAssociations"}, + "rhel10": {"$ref": "#/definitions/OSFamilyAssociations"} }, "propertyNames": { "anyOf": [ {"$ref": "#/definitions/AssociationsNames"}, - {"enum": ["debian", "rhel8", "rhel9"]} + {"enum": ["debian", "rhel8", "rhel9", "rhel10"]} ] }, "definitions": { diff --git a/kubemarine/selinux.py b/kubemarine/selinux.py index 70585f96a..bcdd54119 100644 --- a/kubemarine/selinux.py +++ b/kubemarine/selinux.py @@ -178,7 +178,7 @@ def setup_selinux(group: NodeGroup) -> bool: log = group.cluster.log # this method handles cluster with multiple os, suppressing should be enabled - if group.get_nodes_os() not in ['rhel8', 'rhel9']: + if group.get_nodes_os() not in ['rhel8', 'rhel9', 'rhel10']: log.debug("Skipped - selinux is not supported on Ubuntu/Debian os family") return False diff --git a/kubemarine/system.py b/kubemarine/system.py index df94292d0..4e2f932fb 100644 --- a/kubemarine/system.py +++ b/kubemarine/system.py @@ -341,6 +341,27 @@ def disable_swap(group: NodeGroup) -> Optional[RunnersGroupResult]: def reboot_nodes(cluster: KubernetesCluster) -> None: cluster.get_new_nodes_or_self().call(reboot_group) +def detect_kernel_upgrade(group: NodeGroup) -> List[str]: + cluster = group.cluster + affected_hosts = [] + + os_family = group.get_nodes_os() + + if os_family in ["rhel8", "rhel9", "rhel10"]: + try: + result = group.sudo("needs-restarting -r", hide=True, warn=True) + for host, res in result.items(): + if res.exited == 1: + affected_hosts.append(host) + cluster.log.debug( + f"{host}: Reboot required (detected via needs-restarting -r)" + ) + except GroupResultException: + cluster.log.debug( + "Skipping reboot detection (needs-restarting not available in test environment)" + ) + return [] + return affected_hosts #list of nodes with detected kernel upgrade def reboot_group(group: NodeGroup, try_graceful: bool = None) -> RunnersGroupResult: from kubemarine import kubernetes # pylint: disable=cyclic-import @@ -497,7 +518,7 @@ def verify_system(cluster: KubernetesCluster) -> None: # this method handles clusters with multiple OS os_family = group.get_nodes_os() - if os_family in ['rhel8', 'rhel9'] and cluster.is_task_completed('prepare.system.setup_selinux'): + if os_family in ['rhel8', 'rhel9', 'rhel10'] and cluster.is_task_completed('prepare.system.setup_selinux'): log.debug("Verifying Selinux...") selinux_configured, selinux_result, _ = \ selinux.is_config_valid(group, diff --git a/scripts/thirdparties/src/software/packages.py b/scripts/thirdparties/src/software/packages.py index ddc366f85..388c39f08 100644 --- a/scripts/thirdparties/src/software/packages.py +++ b/scripts/thirdparties/src/software/packages.py @@ -84,11 +84,13 @@ def get_compatibility_version_keys(package_name: str) -> List[str]: keys = [ 'version_rhel8', 'version_rhel9', + 'version_rhel10', 'version_debian', ] if package_name == 'containerd': keys.remove('version_rhel8') keys.remove('version_rhel9') + keys.remove('version_rhel10') elif package_name == 'containerdio': keys.remove('version_debian') diff --git a/test/unit/test_modprobe.py b/test/unit/test_modprobe.py index 395a54af7..e1b9c2e19 100644 --- a/test/unit/test_modprobe.py +++ b/test/unit/test_modprobe.py @@ -215,7 +215,8 @@ def _get_os_context(self, os_family: str) -> Tuple[str, str]: return { 'debian': ('ubuntu', '22.04'), 'rhel8': ('rhel', '8.7'), - 'rhel9': ('rhel', '9.2') + 'rhel9': ('rhel', '9.2'), + 'rhel10': ('rocky', '10.1') }[os_family] def _nodes_having_modules(self, cluster: demo.FakeKubernetesCluster, module_name: str) -> Set[str]: