From 2229dade5b178fa2f6a3b572f110eb4fe6677935 Mon Sep 17 00:00:00 2001 From: wang-guangge Date: Mon, 18 Dec 2023 21:59:46 +0800 Subject: [PATCH] improve task execution status code and hotupgrade logic of reinstalling active/accept hotpatch --- hotpatch/hotupgrade.py | 69 +++++++++++++++++++++++++++++++----------- hotpatch/upgrade_en.py | 6 ++-- 2 files changed, 54 insertions(+), 21 deletions(-) diff --git a/hotpatch/hotupgrade.py b/hotpatch/hotupgrade.py index 5dfee0d..37497cf 100644 --- a/hotpatch/hotupgrade.py +++ b/hotpatch/hotupgrade.py @@ -13,6 +13,7 @@ from __future__ import print_function from time import sleep +from typing import Tuple import dnf.base import dnf.exceptions @@ -28,6 +29,8 @@ from .upgrade_en import UpgradeEnhanceCommand from .version import Versions EMPTY_TAG = "-" +SUCCEED = 0 +FAIL = 255 @dnf.plugin.register_command @@ -94,9 +97,14 @@ class HotupgradeCommand(dnf.cli.Command): logger.info(_('No hot patches marked for install.')) return - applied_old_patches = self._get_applied_old_patch(list(available_hp_dict.values())) - if applied_old_patches: - self._remove_hot_patches(applied_old_patches) + uneffectived_old_patches, effectived_old_patches = self._get_applied_old_patch(list(available_hp_dict.values())) + # remove effectived hp from self.hp_list and available_hp_dict + effectived_hp_list = [key for key, value in available_hp_dict.items() if value in effectived_old_patches] + self.hp_list = [hp for hp in self.hp_list if hp not in effectived_hp_list] + available_hp_dict = {key: value for key, value in available_hp_dict.items() if value not in effectived_hp_list} + + if uneffectived_old_patches: + self._remove_hot_patches(uneffectived_old_patches) else: self.syscare.save() success = self._install_rpm_pkg(list(available_hp_dict.keys())) @@ -143,14 +151,24 @@ class HotupgradeCommand(dnf.cli.Command): if str(ts_item.pkg) == self.kernel_coldpatch: self.is_kernel_coldpatch_installed = True - self.keep_hp_operation_atomic(is_all_kernel_hp_actived, target_remove_hp) + is_task_success = True + is_task_success &= self.keep_hp_operation_atomic(is_all_kernel_hp_actived, target_remove_hp) if self.is_need_accept_kernel_hp and acceptable_hp: + is_accept_success = True logger.info(_('No available kernel cold patch for takeover, gonna accept available kernel hot patch.')) for hp in acceptable_hp: - self._accept_kernel_hp(hp) + pkg_info = self._parse_hp_name(hp) + if pkg_info['target_name'] != "kernel": + continue + is_accept_success &= False if self._accept_hp(hp) != SUCCEED else True + # if need accept operation but failed, it indicates hotupgrade task failed + is_task_success &= is_accept_success + + if not is_task_success: + exit(1) - def keep_hp_operation_atomic(self, is_all_kernel_hp_actived, target_remove_hp): + def keep_hp_operation_atomic(self, is_all_kernel_hp_actived, target_remove_hp) -> bool: """ Keep hotpatch related operation atomic. Once one kernel hotpatch is not successfully activated or kabi check fails, uninstall the kernel coldpatch. And unsuccessfully activated hotpatch package @@ -159,9 +177,13 @@ class HotupgradeCommand(dnf.cli.Command): Args: is_all_kernel_hp_actived(bool): are all kernel related hotpatches activated target_remove_hp(list): target remove hotpatch list + + Returns: + bool: if hotupgrade task success """ upgrade_en = UpgradeEnhanceCommand(self.cli) + is_task_success = True if self.is_kernel_coldpatch_installed: if not is_all_kernel_hp_actived: logger.info(_('Gonna remove %s due to some kernel hotpatch activation failed.'), self.kernel_coldpatch) @@ -177,8 +199,12 @@ class HotupgradeCommand(dnf.cli.Command): if target_remove_hp: logger.info(_('Gonna remove unsuccessfully activated hotpatch rpm.')) + # when processing remove operation, do not achieve the expected result of activating hotpatch rpm, + # it indicates that the hotupgrade task failed + is_task_success &= False for hotpatch in target_remove_hp: upgrade_en.remove_rpm(hotpatch) + return is_task_success def _apply_hp(self, hp_full_name): pkg_info = self._parse_hp_name(hp_full_name) @@ -264,7 +290,7 @@ class HotupgradeCommand(dnf.cli.Command): return hp_map @staticmethod - def _get_applied_old_patch(available_hp_list: list) -> list: + def _get_applied_old_patch(available_hp_list: list) -> Tuple[list, list]: """ get targets' applied accumulative hot patches. User can install and apply multiple sgl (single) hot patches because the rpm name is different, @@ -273,9 +299,11 @@ class HotupgradeCommand(dnf.cli.Command): available_hp_list: e.g. ['redis-1.0-1/ACC-1-1', 'redis-1.0-1/SGL_CVE_2022_1-1-1'] Returns: - list: applied hot patches. e.g. ['redis-1.0-1/ACC-1-1'] + Tuple[str, str]: a tuple containing two elements (uneffectived hot patches, effectived hot patches). + e.g. (['redis-1.0-1/ACC-1-1'], ['redis-1.0-1/SGL_CVE_2022_3047-1-1']) """ - hotpatch_set = set() + uneffectived_hotpatch_set = set() + effectived_hotpatch_set = set() hps_info = Syscare.list() for hp_info in hps_info: # hp_info[Name] is the middle column of syscare list. format: {target_rpm_name}/{hp_name}/{binary_file} @@ -297,9 +325,12 @@ class HotupgradeCommand(dnf.cli.Command): hp_info["Status"], binary_file, ) - if hotpatch not in hotpatch_set: - hotpatch_set.add(hotpatch) - return list(hotpatch_set) + if hp_info["Status"] in ["ACTIVED", "ACCEPTED"]: + effectived_hotpatch_set.add(hotpatch) + continue + if hotpatch not in uneffectived_hotpatch_set: + uneffectived_hotpatch_set.add(hotpatch) + return list(uneffectived_hotpatch_set), list(effectived_hotpatch_set) def _remove_hot_patches(self, applied_old_patches: list) -> None: # output = Output(self.base, dnf.conf.Conf()) @@ -548,19 +579,21 @@ class HotupgradeCommand(dnf.cli.Command): parsed_nevra = parsed_nevras[0] return parsed_nevra.name, "%s-%s" % (parsed_nevra.version, parsed_nevra.release) - def _accept_kernel_hp(self, hp_full_name: str): + def _accept_hp(self, hp_full_name: str) -> int: """ - accept kernel hot patch + accept hot patch Args: str: hp_full_name: full name of hot patch. e.g. patch-kernel-5.10.0-1-ACC-1-1.x86_64 + + Returns: + int: status """ pkg_info = self._parse_hp_name(hp_full_name) - if pkg_info['target_name'] != "kernel": - return hp_subname = self._get_hp_subname_for_syscare(pkg_info) output, status = self.syscare.accept(hp_subname) if status: - logger.info(_('Accept kernel hot patch failed: %s.'), hp_subname) + logger.info(_('Accept hot patch failed: %s.'), hp_subname) else: - logger.info(_('Accept kernel hot patch succeed: %s.'), hp_subname) + logger.info(_('Accept hot patch succeed: %s.'), hp_subname) + return status diff --git a/hotpatch/upgrade_en.py b/hotpatch/upgrade_en.py index 3053179..94ba4da 100644 --- a/hotpatch/upgrade_en.py +++ b/hotpatch/upgrade_en.py @@ -97,7 +97,10 @@ class UpgradeEnhanceCommand(dnf.cli.Command): print('Gonna remove %s due to kabi check failed.' % kernel_pkg) # rebuild rpm database for processing kernel rpm remove operation self.rebuild_rpm_db() + # when processing remove operation, do not achieve the expected result of installing related rpm, + # it indicates that the upgrade task failed self.remove_rpm(kernel_pkg) + exit(1) def remove_rpm(self, pkg: str): """ @@ -113,11 +116,8 @@ class UpgradeEnhanceCommand(dnf.cli.Command): if return_code != SUCCEED: print('Remove package failed: %s.' % pkg) print(output) - exit(1) else: print('Remove package succeed: %s.' % pkg) - # do not achieve the expected result of installing related rpm - exit(1) def rebuild_rpm_db(self): """ -- 2.27.0