diff --git a/0001-support-kabi-check.patch b/0001-support-kabi-check.patch new file mode 100644 index 0000000..06a974c --- /dev/null +++ b/0001-support-kabi-check.patch @@ -0,0 +1,628 @@ +From 0f0cbce6c93b97e312cafead937b46e6b2ceaf51 Mon Sep 17 00:00:00 2001 +From: wang-guangge +Date: Thu, 9 Nov 2023 10:46:33 +0800 +Subject: [PATCH] support kabi check + +--- + ceres/manages/vulnerability_manage.py | 2 +- + hotpatch/hotupgrade.py | 97 +++++- + hotpatch/updateinfo_parse.py | 3 + + hotpatch/upgrade_en.py | 413 ++++++++++++++++++++++++++ + 4 files changed, 506 insertions(+), 9 deletions(-) + create mode 100644 hotpatch/upgrade_en.py + +diff --git a/ceres/manages/vulnerability_manage.py b/ceres/manages/vulnerability_manage.py +index c41a7fa..bad2dee 100644 +--- a/ceres/manages/vulnerability_manage.py ++++ b/ceres/manages/vulnerability_manage.py +@@ -620,7 +620,7 @@ class VulnerabilityManage: + Tuple[str, str] + a tuple containing two elements (update result, log). + """ +- code, stdout, stderr = execute_shell_command(f"dnf update {rpm_name} -y") ++ code, stdout, stderr = execute_shell_command(f"dnf upgrade-en {rpm_name} -y") + if code != CommandExitCode.SUCCEED: + return TaskExecuteRes.FAIL, stderr + if "Complete" not in stdout: +diff --git a/hotpatch/hotupgrade.py b/hotpatch/hotupgrade.py +index f61e37f..c508e07 100644 +--- a/hotpatch/hotupgrade.py ++++ b/hotpatch/hotupgrade.py +@@ -12,16 +12,14 @@ + # ******************************************************************************/ + from __future__ import print_function + +-from time import sleep + import dnf.base + import dnf.exceptions + import hawkey ++from time import sleep + from dnf.cli import commands + from dnf.cli.option_parser import OptionParser +- +-# from dnf.cli.output import Output + from dnfpluginscore import _, logger +- ++from .upgrade_en import UpgradeEnhanceCommand + from .hot_updateinfo import HotUpdateinfoCommand + from .updateinfo_parse import HotpatchUpdateInfo + from .syscare import Syscare +@@ -37,6 +35,9 @@ class HotupgradeCommand(dnf.cli.Command): + usage = "" + syscare = Syscare() + hp_list = [] ++ is_need_accept_kernel_hp = False ++ is_kernel_coldpatch_installed = False ++ kernel_coldpatch = '' + + @staticmethod + def set_argparser(parser): +@@ -50,6 +51,13 @@ class HotupgradeCommand(dnf.cli.Command): + parser.add_argument( + "--takeover", default=False, action='store_true', help=_('kernel cold patch takeover operation') + ) ++ parser.add_argument( ++ "-f", ++ dest='force', ++ default=False, ++ action='store_true', ++ help=_('force retain kernel rpm package if kernel kabi check fails'), ++ ) + + def configure(self): + """Verify that conditions are met so that this command can run. +@@ -104,17 +112,72 @@ class HotupgradeCommand(dnf.cli.Command): + + def run_transaction(self) -> None: + """ +- apply hot patches ++ apply hot patches, and process kabi check for kernel package rpm. + Returns: + None + """ + # syscare need a little bit time to process the installed hot patch + sleep(0.5) ++ ++ is_all_kernel_hp_actived = True ++ # hotpatch that fail to be activated will be automatically uninstalled ++ target_remove_hp = [] ++ acceptable_hp = [] + for hp in self.hp_list: +- self._apply_hp(hp) +- if self.opts.takeover and self.is_need_accept_kernel_hp: ++ status = self._apply_hp(hp) ++ if status: ++ target_remove_hp.append(hp) ++ if not hp.startswith('patch-kernel-'): ++ continue ++ if status: ++ is_all_kernel_hp_actived &= False ++ else: ++ is_all_kernel_hp_actived &= True ++ acceptable_hp.append(hp) ++ ++ for ts_item in self.base.transaction: ++ if ts_item.action not in dnf.transaction.FORWARD_ACTIONS: ++ continue ++ 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) ++ ++ if self.is_need_accept_kernel_hp and acceptable_hp: ++ 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) + ++ def keep_hp_operation_atomic(self, is_all_kernel_hp_actived, target_remove_hp): ++ """ ++ 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 ++ will be removed. ++ ++ Args: ++ is_all_kernel_hp_actived(bool): are all kernel related hotpatches activated ++ target_remove_hp(list): target remove hotpatch list ++ """ ++ upgrade_en = UpgradeEnhanceCommand(self.cli) ++ ++ 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) ++ upgrade_en.remove_rpm(str(self.kernel_coldpatch)) ++ self.is_need_accept_kernel_hp = False ++ # process kabi check ++ elif not upgrade_en.kabi_check(str(self.kernel_coldpatch)) and not self.opts.force: ++ logger.info(_('Gonna remove %s due to Kabi check failed.'), self.kernel_coldpatch) ++ # rebuild rpm database for processing kernel rpm remove operation ++ upgrade_en.rebuild_rpm_db() ++ upgrade_en.remove_rpm(str(self.kernel_coldpatch)) ++ self.is_need_accept_kernel_hp = True ++ ++ if target_remove_hp: ++ logger.info(_('Gonna remove unsuccessfully activated hotpatch rpm.')) ++ for hotpatch in target_remove_hp: ++ upgrade_en.remove_rpm(hotpatch) ++ + def _apply_hp(self, hp_full_name): + pkg_info = self._parse_hp_name(hp_full_name) + hp_subname = self._get_hp_subname_for_syscare(pkg_info) +@@ -123,6 +186,7 @@ class HotupgradeCommand(dnf.cli.Command): + logger.info(_('Apply hot patch failed: %s.'), hp_subname) + else: + logger.info(_('Apply hot patch succeed: %s.'), hp_subname) ++ return status + + @staticmethod + def _get_hp_subname_for_syscare(pkg_info: dict) -> str: +@@ -394,9 +458,11 @@ class HotupgradeCommand(dnf.cli.Command): + """ + process takeover operation. + """ ++ if not self.get_kernel_hp_list(): ++ return + kernel_coldpatch = self.get_target_installed_kernel_coldpatch_of_hotpatch() +- self.is_need_accept_kernel_hp = False + if kernel_coldpatch: ++ self.kernel_coldpatch = kernel_coldpatch + logger.info(_("Gonna takeover kernel cold patch: ['%s']" % kernel_coldpatch)) + success = self._install_rpm_pkg([kernel_coldpatch]) + if success: +@@ -412,6 +478,21 @@ class HotupgradeCommand(dnf.cli.Command): + ) + return + ++ def get_kernel_hp_list(self) -> list: ++ """ ++ Get kernel hp list from self.hp_list. ++ ++ Returns: ++ list: kernel hp list ++ e.g. ++ ['patch-kernel-5.10.0-153.12.0.92.oe2203sp2-ACC-1-1.x86_64'] ++ """ ++ kernel_hp_list = [] ++ for hp in self.hp_list: ++ if hp.startswith('patch-kernel-'): ++ kernel_hp_list.append(hp) ++ return kernel_hp_list ++ + def get_target_installed_kernel_coldpatch_of_hotpatch(self) -> str: + """ + get the highest kernel cold patch of hot patch in "dnf hot-updateinfo list cves", if the corresponding +diff --git a/hotpatch/updateinfo_parse.py b/hotpatch/updateinfo_parse.py +index 4760378..fc39d48 100644 +--- a/hotpatch/updateinfo_parse.py ++++ b/hotpatch/updateinfo_parse.py +@@ -322,6 +322,9 @@ class HotpatchUpdateInfo(object): + cmd = ["uname", "-r"] + kernel_version = '' + kernel_version, return_code = cmd_output(cmd) ++ # 'uname -r' show the kernel version-release.arch of the current system ++ # [root@openEuler hotpatch]# uname -r ++ # 5.10.0-136.12.0.86.oe2203sp1.x86_64 + if return_code != SUCCEED: + return kernel_version + kernel_version = kernel_version.split('\n')[0] +diff --git a/hotpatch/upgrade_en.py b/hotpatch/upgrade_en.py +new file mode 100644 +index 0000000..266bcae +--- /dev/null ++++ b/hotpatch/upgrade_en.py +@@ -0,0 +1,413 @@ ++#!/usr/bin/python3 ++# ****************************************************************************** ++# Copyright (c) Huawei Technologies Co., Ltd. 2023-2023. All rights reserved. ++# licensed under the Mulan PSL v2. ++# You can use this software according to the terms and conditions of the Mulan PSL v2. ++# You may obtain a copy of Mulan PSL v2 at: ++# http://license.coscl.org.cn/MulanPSL2 ++# THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR ++# IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR ++# PURPOSE. ++# See the Mulan PSL v2 for more details. ++# ******************************************************************************/ ++import dnf ++import gzip ++import subprocess ++from dnfpluginscore import _ ++from dnf.cli import commands ++from dnf.cli.commands.upgrade import UpgradeCommand ++from dnf.cli.option_parser import OptionParser ++ ++SUCCEED = 0 ++FAIL = 255 ++ ++ ++def cmd_output(cmd): ++ try: ++ result = subprocess.Popen(cmd, shell=False, stdout=subprocess.PIPE, stderr=subprocess.PIPE) ++ result.wait() ++ return result.stdout.read().decode('utf-8'), result.returncode ++ except Exception as e: ++ print("error: ", e) ++ return str(e), FAIL ++ ++ ++@dnf.plugin.register_command ++class UpgradeEnhanceCommand(dnf.cli.Command): ++ SYMVERS_FILE = "/boot/symvers-%s.gz" ++ ++ aliases = ['upgrade-en'] ++ summary = _( ++ 'upgrade with KABI(Kernel Application Binary Interface) check. If the loaded kernel modules \ ++ have KABI compatibility with the new version kernel rpm, the kernel modules can be installed \ ++ and used in the new version kernel without recompling.' ++ ) ++ ++ @staticmethod ++ def set_argparser(parser): ++ parser.add_argument( ++ 'packages', ++ nargs='*', ++ help=_('Package to upgrade'), ++ action=OptionParser.ParseSpecGroupFileCallback, ++ metavar=_('PACKAGE'), ++ ) ++ parser.add_argument( ++ "-f", ++ dest='force', ++ default=False, ++ action='store_true', ++ help=_('force retain kernel rpm package if kernel kabi check fails'), ++ ) ++ ++ def configure(self): ++ """Verify that conditions are met so that this command can run. ++ ++ These include that there are enabled repositories with gpg ++ keys, and that this command is being run by the root user. ++ """ ++ demands = self.cli.demands ++ demands.sack_activation = True ++ demands.available_repos = True ++ demands.resolving = True ++ demands.root_user = True ++ commands._checkGPGKey(self.base, self.cli) ++ if not self.opts.filenames: ++ commands._checkEnabledRepo(self.base) ++ self.upgrade_minimal = None ++ self.all_security = None ++ self.skipped_grp_specs = None ++ ++ def run(self): ++ self.upgrade() ++ ++ def run_transaction(self): ++ """ ++ Process kabi check for kernel rpm package installed this time. If the kernel rpm pakcgae fails kabi check, ++ uninstall it. ++ """ ++ for ts_item in self.base.transaction: ++ if ts_item.action not in dnf.transaction.FORWARD_ACTIONS: ++ continue ++ if ts_item.pkg.name == 'kernel': ++ kernel_pkg = str(ts_item.pkg) ++ success = self.kabi_check(kernel_pkg) ++ if not success and not self.opts.force: ++ print('Gonna remove %s due to kabi check failed.' % kernel_pkg) ++ # rebuild rpm database for processing kernel rpm remove operation ++ self.rebuild_rpm_db() ++ self.remove_rpm(kernel_pkg) ++ ++ def remove_rpm(self, pkg: str): ++ """ ++ Remove rpm package via command line. ++ ++ Args: ++ pkg(str): package name ++ e.g. ++ kernel-5.10.0-153.18.0.94.oe2203sp2.x86_64 ++ """ ++ remove_cmd = ["dnf", "remove", pkg, "-y"] ++ output, return_code = cmd_output(remove_cmd) ++ if return_code != SUCCEED: ++ print('Remove package failed: %s.' % pkg) ++ exit(1) ++ else: ++ print('Remove package succeed: %s.' % pkg) ++ # do not achieve the expected result of installing related kernel rpm ++ exit(1) ++ ++ def rebuild_rpm_db(self): ++ """ ++ Rebuild rpm database for processing kernel rpm remove operation. ++ """ ++ rebuilddb_cmd = ["rpm", "--rebuilddb"] ++ output, return_code = cmd_output(rebuilddb_cmd) ++ if return_code != SUCCEED: ++ print('Rebuild rpm database failed.') ++ else: ++ print('Rebuild rpm database succeed.') ++ ++ def kabi_check(self, pkg: str) -> bool: ++ """ ++ Process kabi check after upgrading kernel rpm. ++ ++ Args: ++ pkg(str): package name ++ e.g. ++ kernel-5.10.0-153.18.0.94.oe2203sp2.x86_64 ++ ++ Returns: ++ bool: kabi check result ++ """ ++ print("Kabi check for %s:" % pkg) ++ # version-release.arch ++ evra = pkg.split("-", 1)[1] ++ symvers_file = self.SYMVERS_FILE % (evra) ++ ++ target_symvers_symbol_crc_mapping, return_code = self.get_target_symvers_symbol_crc_mapping(symvers_file) ++ if return_code != SUCCEED: ++ print('[Fail] Cannot find the symvers file of %s.', pkg) ++ return False ++ module_actual_symbol_crc_mapping = self.get_module_actual_symbol_crc_mapping() ++ ++ module_different_symbol_crc_mapping = self.compare_actual_and_target_symvers_symbol_crc_mapping( ++ module_actual_symbol_crc_mapping, target_symvers_symbol_crc_mapping ++ ) ++ ++ sum_module_num = len(module_actual_symbol_crc_mapping) ++ fail_module_num = len(module_different_symbol_crc_mapping) ++ pass_module_num = sum_module_num - fail_module_num ++ ++ reminder_statement = "Here are %s loaded kernel modules in this system, %s pass, %s fail." % ( ++ sum_module_num, ++ pass_module_num, ++ fail_module_num, ++ ) ++ ++ if fail_module_num > 0: ++ print('[Fail] %s' % reminder_statement) ++ self.output_symbol_crc_difference_report(module_different_symbol_crc_mapping) ++ return False ++ ++ print('[Success] %s' % reminder_statement) ++ return True ++ ++ def output_symbol_crc_difference_report(self, module_different_symbol_crc_mapping: dict): ++ """ ++ Format the output for symbol crc difference report. ++ The output is as follows: ++ ++ Failed modules are as follows: ++ No. Module Difference ++ 1 upatch ipv6_chk_custom_prefix : 0x0c994af2 != 0x0c994af3 ++ pcmcia_reset_card : 0xe9bed965 != null ++ 2 crct10dif_pclmul crypto_unregister_shash: 0x60f5b0b7 != 0x0c994af3 ++ __fentry__ : 0xbdfb6dbb != null ++ """ ++ print('Failed modules are as follows:') ++ ++ title = ['No.', 'Module', 'Difference'] ++ # column width ++ sequence_width = len(title[0]) ++ module_width = len(title[1]) ++ symbol_width = crc_info_width = 0 ++ ++ for seq, module_name in enumerate(module_different_symbol_crc_mapping): ++ # the sequence starts from 1 ++ seq = seq + 1 ++ sequence_width = max(sequence_width, len(str(seq))) ++ different_symbol_crc_mapping = module_different_symbol_crc_mapping[module_name] ++ module_width = max(module_width, len(module_name)) ++ for symbol, crc_list in different_symbol_crc_mapping.items(): ++ symbol_width = max(symbol_width, len(symbol)) ++ crc_info = "%s != %s" % (crc_list[0], crc_list[1]) ++ crc_info_width = max(crc_info_width, len(crc_info)) ++ ++ # print title ++ print('%-*s %-*s %s' % (sequence_width, title[0], module_width, title[1], title[2])) ++ ++ for seq, module_name in enumerate(module_different_symbol_crc_mapping): ++ seq = seq + 1 ++ print('%-*s %-*s' % (sequence_width, seq, module_width, module_name), end='') ++ different_symbol_crc_mapping = module_different_symbol_crc_mapping[module_name] ++ is_first_symbol = True ++ for symbol, crc_list in different_symbol_crc_mapping.items(): ++ crc_info = "%s != %s" % (crc_list[0], crc_list[1]) ++ if is_first_symbol: ++ print(' %-*s: %s' % (symbol_width, symbol, crc_info), end='') ++ is_first_symbol = False ++ else: ++ print( ++ ' %-*s %-*s: %s' % (sequence_width + module_width, "", symbol_width, symbol, crc_info), end='' ++ ) ++ print('') ++ ++ def compare_actual_and_target_symvers_symbol_crc_mapping( ++ self, module_actual_symbol_crc_mapping: dict, target_symvers_symbol_crc_mapping: dict ++ ) -> dict: ++ """ ++ Compare the actual symbol crc mapping with the target symvers symbol crc mapping. ++ ++ Args: ++ module_actual_symbol_crc_mapping(dict): module actual symbol crc mapping ++ e.g. ++ { ++ 'upatch': { ++ 'ipv6_chk_custom_prefix': '0x0c994af3', ++ 'pcmcia_reset_card': '0xe9bed965', ++ } ++ } ++ ++ target_symvers_symbol_crc_mapping(dict): target symvers symbol crc mapping ++ e.g. ++ { ++ 'ipv6_chk_custom_prefix': '0x0c994af2', ++ 'pcmcia_reset_card': '0xe9bed965', ++ } ++ ++ Returns: ++ dict: module different symbol crc mapping ++ e.g. ++ { ++ 'upatch': { ++ 'ipv6_chk_custom_prefix': ['0x0c994af3', '0x0c994af2']. ++ } ++ } ++ """ ++ module_different_symbol_crc_mapping = dict() ++ for module_name, actual_symbol_crc_mapping in module_actual_symbol_crc_mapping.items(): ++ different_symbol_crc_mapping = dict() ++ for actual_symbol, actual_crc in actual_symbol_crc_mapping.items(): ++ if actual_symbol not in target_symvers_symbol_crc_mapping: ++ continue ++ elif target_symvers_symbol_crc_mapping[actual_symbol] != actual_symbol_crc_mapping[actual_symbol]: ++ different_symbol_crc_mapping[actual_symbol] = [ ++ actual_crc, ++ target_symvers_symbol_crc_mapping[actual_symbol], ++ ] ++ if not different_symbol_crc_mapping: ++ continue ++ module_different_symbol_crc_mapping[module_name] = different_symbol_crc_mapping ++ return module_different_symbol_crc_mapping ++ ++ def get_module_actual_symbol_crc_mapping(self) -> dict: ++ """ ++ Get the module actual symbol crc mapping of the driver modules currently being loaded in the system. ++ ++ Returns: ++ dict: module actual symbol crc mapping ++ e.g. ++ { ++ 'upatch': { ++ 'ipv6_chk_custom_prefix': '0x0c994af3', ++ 'pcmcia_reset_card': '0xe9bed965', ++ } ++ } ++ """ ++ module_actual_symbol_crc_mapping = dict() ++ lsmod_cmd = ["lsmod"] ++ # 'lsmod' shows all modules loaded in the system ++ # e.g. ++ # [root@openEuler ~]# lsmod ++ # Module Size Used by ++ # upatch 53248 0 ++ # nft_fib_inet 16384 1 ++ # nft_fib_ipv4 16384 1 nft_fib_inet ++ list_output, return_code = cmd_output(lsmod_cmd) ++ if return_code != SUCCEED: ++ return module_actual_symbol_crc_mapping ++ ++ content = list_output.split('\n') ++ for line in content[1:]: ++ if not line: ++ continue ++ module_name = line.split()[0] ++ modinfo_cmd = ['modinfo', module_name, '-n'] ++ # 'modinfo module_name -n' shows module path information ++ # e.g. ++ # [root@openEuler ~]# modinfo upatch -n ++ # /lib/modules/5.10.0-153.12.0.92.oe2203sp2.x86_64/weak-updates/syscare/upatch.ko ++ module_path_output, return_code = cmd_output(modinfo_cmd) ++ if return_code != SUCCEED: ++ continue ++ ++ module_path = module_path_output.split('\n')[0] ++ actual_symbol_crc_mapping, return_code = self.get_actual_symbol_crc_mapping(module_path) ++ if return_code != SUCCEED: ++ continue ++ ++ module_actual_symbol_crc_mapping[module_name] = actual_symbol_crc_mapping ++ return module_actual_symbol_crc_mapping ++ ++ def get_actual_symbol_crc_mapping(self, module_path: str) -> (dict, int): ++ """ ++ Get actual symbol crc mapping for specific module. ++ ++ Args: ++ module_path(str): loaded module path ++ ++ Returns: ++ dict, bool: actual symbol crc mapping, return code ++ """ ++ actual_symbol_crc_mapping = dict() ++ modprobe_cmd = ['modprobe', '--dump', module_path] ++ # 'modprobe --dump module_path' shows module related kabi information ++ # e.g. ++ # [root@openEuler ~]# modprobe --dump \ ++ # /lib/modules/5.10.0-153.12.0.92.oe2203sp2.x86_64/weak-updates/syscare/upatch.ko ++ # 0xe32130cf module_layout ++ # 0x9c4befaf kmalloc_caches ++ # 0xeb233a45 __kmalloc ++ # 0xd6ee688f vmalloc ++ # 0x349cba85 strchr ++ # 0x754d539c strlen ++ crc_symbol_output_lines, return_code = cmd_output(modprobe_cmd) ++ if return_code != SUCCEED: ++ return actual_symbol_crc_mapping, return_code ++ ++ crc_symbol_output = crc_symbol_output_lines.split('\n') ++ for crc_symbol_line in crc_symbol_output: ++ if not crc_symbol_line: ++ continue ++ crc_symbol_line = crc_symbol_line.split() ++ crc, symbol = crc_symbol_line[0], crc_symbol_line[1] ++ actual_symbol_crc_mapping[symbol] = crc ++ return actual_symbol_crc_mapping, return_code ++ ++ def get_target_symvers_symbol_crc_mapping(self, symvers_file: str) -> (dict, int): ++ """ ++ Get target symbol crc mapping from symvers file of kernel rpm package. The symvers file content is ++ as follows(e.g.): ++ ++ 0x0c994af3 ipv6_chk_custom_prefix vmlinux EXPORT_SYMBOL ++ 0xe9bed965 pcmcia_reset_card vmlinux EXPORT_SYMBOL ++ 0x55417264 unregister_vt_notifier vmlinux EXPORT_SYMBOL_GPL ++ 0x8c8905c0 set_anon_super vmlinux EXPORT_SYMBOL ++ 0x3ba051a9 __cleancache_invalidate_page vmlinux EXPORT_SYMBOL ++ ++ the first column is crc(Cyclic Redundancy Check), and the second column is symbol. ++ ++ Args: ++ symvers_file(str): symvers file path ++ ++ Returns: ++ dict, int: target symvers symbol crc mapping, return_code ++ e.g. ++ { ++ 'ipv6_chk_custom_prefix': '0x0c994af3', ++ 'pcmcia_reset_card': '0xe9bed965', ++ }, ++ SUCCEED ++ """ ++ symvers_symbol_crc_mapping = dict() ++ try: ++ content = gzip.open(symvers_file, 'rb') ++ except FileNotFoundError as e: ++ print("error: ", e) ++ return symvers_symbol_crc_mapping, FAIL ++ ++ for line in content.readlines(): ++ line = line.decode() ++ line = line.split() ++ crc, symbol = line[0], line[1] ++ symvers_symbol_crc_mapping[symbol] = crc ++ content.close() ++ return symvers_symbol_crc_mapping, SUCCEED ++ ++ def upgrade(self): ++ """ ++ Use UpgradeCommand to process the upgrade operation. ++ """ ++ upgrade = UpgradeCommand(self.cli) ++ upgrade.upgrade_minimal = self.upgrade_minimal ++ upgrade.opts = self.opts ++ upgrade.opts.filenames = self.opts.filenames ++ upgrade.opts.pkg_specs = self.opts.pkg_specs ++ upgrade.opts.grp_specs = self.opts.grp_specs ++ ++ upgrade.upgrade_minimal = None ++ upgrade.all_security = None ++ upgrade.skipped_grp_specs = None ++ ++ upgrade.run() +-- +2.27.0 + diff --git a/aops-ceres.spec b/aops-ceres.spec index 9b7bf77..a901303 100644 --- a/aops-ceres.spec +++ b/aops-ceres.spec @@ -2,16 +2,17 @@ Name: aops-ceres Version: v1.3.4 -Release: 1 +Release: 2 Summary: An agent which needs to be adopted in client, it managers some plugins, such as gala-gopher(kpi collection), fluentd(log collection) and so on. License: MulanPSL2 URL: https://gitee.com/openeuler/%{name} Source0: %{name}-%{version}.tar.gz +Patch0001: 0001-support-kabi-check.patch BuildRequires: python3-setuptools Requires: python3-requests python3-jsonschema python3-libconf -Requires: python3-concurrent-log-handler dmidecode +Requires: python3-concurrent-log-handler dmidecode dnf-hotpatch-plugin Provides: aops-ceres Conflicts: aops-agent @@ -19,6 +20,13 @@ Conflicts: aops-agent %description An agent which needs to be adopted in client, it managers some plugins, such as gala-gopher(kpi collection), fluentd(log collection) and so on. +%package -n dnf-hotpatch-plugin +Summary: dnf hotpatch plugin +Requires: python3-hawkey python3-dnf syscare >= 1.1.0 + +%description -n dnf-hotpatch-plugin +dnf hotpatch plugin, it's about hotpatch query and fix + %package -n da-tool Summary: da-tool is a sampling and analysis tool for function delay. BuildRequires: gcc cmake make @@ -28,7 +36,7 @@ Requires: perf da-tool is a sampling and analysis tool for function delay. %prep -%autosetup -n %{name}-%{version} +%autosetup -n %{name}-%{version} -p1 # build for aops-ceres @@ -52,6 +60,9 @@ cd ../../../ # install for aops-ceres %py3_install +# install for aops-dnf-plugin +cp -r hotpatch %{buildroot}/%{python3_sitelib}/dnf-plugins/ + # install for da-tool mkdir -p ${RPM_BUILD_ROOT}%{_bindir} mkdir -p ${RPM_BUILD_ROOT}%{_sysconfdir} @@ -67,6 +78,9 @@ install -b -m755 ./extra-tools/da-tool/script/da-tool.sh ${RPM_BUILD_ROOT} %{python3_sitelib}/ceres/* %{_bindir}/aops-ceres +%files -n dnf-hotpatch-plugin +%{python3_sitelib}/dnf-plugins/* + %files -n da-tool %defattr (-, root, root) %config(noreplace) %{_sysconfdir}/da-tool.conf @@ -74,6 +88,9 @@ install -b -m755 ./extra-tools/da-tool/script/da-tool.sh ${RPM_BUILD_ROOT} %attr(755, root, root) %{_bindir}/da-tool-analysis %changelog +* Mon Nov 13 2023 wangguangge - v1.3.4-2 +- support kabi check for dnf-hotpatch-plugin + * Sun Nov 12 2023 liuchanggeng - v1.3.4-1 - update to v1.3.4 - bugfix: fix the bug that when multiple kernel versions coexist, the correct highest version cannot be identified