修复hotupgrade格式问题
(cherry picked from commit 4196e69f81ea59c953d33c1e57f7fc6bf8c01363)
This commit is contained in:
parent
61be517e12
commit
0ea7e5ac22
@ -1,10 +1,7 @@
|
|||||||
From dfdc428a9f9685d5186fcb8ca2aba612e7426907 Mon Sep 17 00:00:00 2001
|
From 7b986fb7739ddd7ab508c5e4e53e1d51442553f7 Mon Sep 17 00:00:00 2001
|
||||||
From: gongzt <gong_zhengtang@163.com>
|
From: gongzt <gong_zhengtang@163.com>
|
||||||
Date: Thu, 19 Oct 2023 09:26:26 +0800
|
Date: Thu, 19 Oct 2023 03:42:45 +0000
|
||||||
Subject: remove hotpatch
|
Subject: [PATCH 1/1] rm
|
||||||
MIME-Version: 1.0
|
|
||||||
Content-Type: text/plain; charset=UTF-8
|
|
||||||
Content-Transfer-Encoding: 8bit
|
|
||||||
|
|
||||||
---
|
---
|
||||||
aops-apollo.spec | 18 +-
|
aops-apollo.spec | 18 +-
|
||||||
@ -1172,488 +1169,488 @@ index f61e37f..0000000
|
|||||||
--- a/hotpatch/hotupgrade.py
|
--- a/hotpatch/hotupgrade.py
|
||||||
+++ /dev/null
|
+++ /dev/null
|
||||||
@@ -1,482 +0,0 @@
|
@@ -1,482 +0,0 @@
|
||||||
-#!/usr/bin/python3
|
-#!/usr/bin/python3
|
||||||
-# ******************************************************************************
|
-# ******************************************************************************
|
||||||
-# Copyright (c) Huawei Technologies Co., Ltd. 2023-2023. All rights reserved.
|
-# Copyright (c) Huawei Technologies Co., Ltd. 2023-2023. All rights reserved.
|
||||||
-# licensed under the Mulan PSL v2.
|
-# licensed under the Mulan PSL v2.
|
||||||
-# You can use this software according to the terms and conditions of 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:
|
-# You may obtain a copy of Mulan PSL v2 at:
|
||||||
-# http://license.coscl.org.cn/MulanPSL2
|
-# http://license.coscl.org.cn/MulanPSL2
|
||||||
-# THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR
|
-# 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
|
-# IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR
|
||||||
-# PURPOSE.
|
-# PURPOSE.
|
||||||
-# See the Mulan PSL v2 for more details.
|
-# See the Mulan PSL v2 for more details.
|
||||||
-# ******************************************************************************/
|
-# ******************************************************************************/
|
||||||
-from __future__ import print_function
|
-from __future__ import print_function
|
||||||
-
|
-
|
||||||
-from time import sleep
|
-from time import sleep
|
||||||
-import dnf.base
|
-import dnf.base
|
||||||
-import dnf.exceptions
|
-import dnf.exceptions
|
||||||
-import hawkey
|
-import hawkey
|
||||||
-from dnf.cli import commands
|
-from dnf.cli import commands
|
||||||
-from dnf.cli.option_parser import OptionParser
|
-from dnf.cli.option_parser import OptionParser
|
||||||
-
|
-
|
||||||
-# from dnf.cli.output import Output
|
-# from dnf.cli.output import Output
|
||||||
-from dnfpluginscore import _, logger
|
-from dnfpluginscore import _, logger
|
||||||
-
|
-
|
||||||
-from .hot_updateinfo import HotUpdateinfoCommand
|
-from .hot_updateinfo import HotUpdateinfoCommand
|
||||||
-from .updateinfo_parse import HotpatchUpdateInfo
|
-from .updateinfo_parse import HotpatchUpdateInfo
|
||||||
-from .syscare import Syscare
|
-from .syscare import Syscare
|
||||||
-from .version import Versions
|
-from .version import Versions
|
||||||
-
|
-
|
||||||
-EMPTY_TAG = "-"
|
-EMPTY_TAG = "-"
|
||||||
-
|
-
|
||||||
-
|
-
|
||||||
-@dnf.plugin.register_command
|
-@dnf.plugin.register_command
|
||||||
-class HotupgradeCommand(dnf.cli.Command):
|
-class HotupgradeCommand(dnf.cli.Command):
|
||||||
- aliases = ("hotupgrade",)
|
- aliases = ("hotupgrade",)
|
||||||
- summary = "Hot upgrade package using hot patch."
|
- summary = "Hot upgrade package using hot patch."
|
||||||
- usage = ""
|
- usage = ""
|
||||||
- syscare = Syscare()
|
- syscare = Syscare()
|
||||||
- hp_list = []
|
- hp_list = []
|
||||||
-
|
-
|
||||||
- @staticmethod
|
- @staticmethod
|
||||||
- def set_argparser(parser):
|
- def set_argparser(parser):
|
||||||
- parser.add_argument(
|
- parser.add_argument(
|
||||||
- 'packages',
|
- 'packages',
|
||||||
- nargs='*',
|
- nargs='*',
|
||||||
- help=_('Package to upgrade'),
|
- help=_('Package to upgrade'),
|
||||||
- action=OptionParser.ParseSpecGroupFileCallback,
|
- action=OptionParser.ParseSpecGroupFileCallback,
|
||||||
- metavar=_('PACKAGE'),
|
- metavar=_('PACKAGE'),
|
||||||
- )
|
- )
|
||||||
- parser.add_argument(
|
- parser.add_argument(
|
||||||
- "--takeover", default=False, action='store_true', help=_('kernel cold patch takeover operation')
|
- "--takeover", default=False, action='store_true', help=_('kernel cold patch takeover operation')
|
||||||
- )
|
- )
|
||||||
-
|
-
|
||||||
- def configure(self):
|
- def configure(self):
|
||||||
- """Verify that conditions are met so that this command can run.
|
- """Verify that conditions are met so that this command can run.
|
||||||
- These include that there are enabled repositories with gpg
|
- These include that there are enabled repositories with gpg
|
||||||
- keys, and that this command is being run by the root user.
|
- keys, and that this command is being run by the root user.
|
||||||
- """
|
- """
|
||||||
- demands = self.cli.demands
|
- demands = self.cli.demands
|
||||||
- demands.sack_activation = True
|
- demands.sack_activation = True
|
||||||
- demands.available_repos = True
|
- demands.available_repos = True
|
||||||
- demands.resolving = True
|
- demands.resolving = True
|
||||||
- demands.root_user = True
|
- demands.root_user = True
|
||||||
-
|
-
|
||||||
- commands._checkGPGKey(self.base, self.cli)
|
- commands._checkGPGKey(self.base, self.cli)
|
||||||
- if not self.opts.filenames:
|
- if not self.opts.filenames:
|
||||||
- commands._checkEnabledRepo(self.base)
|
- commands._checkEnabledRepo(self.base)
|
||||||
-
|
-
|
||||||
- def run(self):
|
- def run(self):
|
||||||
- if self.opts.pkg_specs:
|
- if self.opts.pkg_specs:
|
||||||
- self.hp_list = self.opts.pkg_specs
|
- self.hp_list = self.opts.pkg_specs
|
||||||
- elif self.opts.cves or self.opts.advisory:
|
- elif self.opts.cves or self.opts.advisory:
|
||||||
- cve_pkgs = self.get_hotpatch_based_on_cve(self.opts.cves)
|
- cve_pkgs = self.get_hotpatch_based_on_cve(self.opts.cves)
|
||||||
- advisory_pkgs = self.get_hotpatch_based_on_advisory(self.opts.advisory)
|
- advisory_pkgs = self.get_hotpatch_based_on_advisory(self.opts.advisory)
|
||||||
- self.hp_list = cve_pkgs + advisory_pkgs
|
- self.hp_list = cve_pkgs + advisory_pkgs
|
||||||
-
|
-
|
||||||
- else:
|
- else:
|
||||||
- self.hp_list = self.get_hotpatch_of_all_cve()
|
- self.hp_list = self.get_hotpatch_of_all_cve()
|
||||||
- if self.hp_list:
|
- if self.hp_list:
|
||||||
- logger.info(_("Gonna apply all available hot patches: %s"), self.hp_list)
|
- logger.info(_("Gonna apply all available hot patches: %s"), self.hp_list)
|
||||||
-
|
-
|
||||||
- available_hp_dict = self._get_available_hotpatches(self.hp_list)
|
- available_hp_dict = self._get_available_hotpatches(self.hp_list)
|
||||||
- if not available_hp_dict:
|
- if not available_hp_dict:
|
||||||
- logger.info(_('No hot patches marked for install.'))
|
- logger.info(_('No hot patches marked for install.'))
|
||||||
- return
|
- return
|
||||||
-
|
-
|
||||||
- applied_old_patches = self._get_applied_old_patch(list(available_hp_dict.values()))
|
- applied_old_patches = self._get_applied_old_patch(list(available_hp_dict.values()))
|
||||||
- if applied_old_patches:
|
- if applied_old_patches:
|
||||||
- self._remove_hot_patches(applied_old_patches)
|
- self._remove_hot_patches(applied_old_patches)
|
||||||
- else:
|
- else:
|
||||||
- self.syscare.save()
|
- self.syscare.save()
|
||||||
- success = self._install_rpm_pkg(list(available_hp_dict.keys()))
|
- success = self._install_rpm_pkg(list(available_hp_dict.keys()))
|
||||||
- if not success:
|
- if not success:
|
||||||
- logger.info(_("Error: Install hot patch failed, try to rollback."))
|
- logger.info(_("Error: Install hot patch failed, try to rollback."))
|
||||||
- output, status = self.syscare.restore()
|
- output, status = self.syscare.restore()
|
||||||
- if status:
|
- if status:
|
||||||
- raise dnf.exceptions.Error(_('Roll back failed.'))
|
- raise dnf.exceptions.Error(_('Roll back failed.'))
|
||||||
- logger.info(_("Roll back succeed."))
|
- logger.info(_("Roll back succeed."))
|
||||||
- return
|
- return
|
||||||
-
|
-
|
||||||
- if self.opts.takeover:
|
- if self.opts.takeover:
|
||||||
- self.takeover_operation()
|
- self.takeover_operation()
|
||||||
- return
|
- return
|
||||||
-
|
-
|
||||||
- def run_transaction(self) -> None:
|
- def run_transaction(self) -> None:
|
||||||
- """
|
- """
|
||||||
- apply hot patches
|
- apply hot patches
|
||||||
- Returns:
|
- Returns:
|
||||||
- None
|
- None
|
||||||
- """
|
- """
|
||||||
- # syscare need a little bit time to process the installed hot patch
|
- # syscare need a little bit time to process the installed hot patch
|
||||||
- sleep(0.5)
|
- sleep(0.5)
|
||||||
- for hp in self.hp_list:
|
- for hp in self.hp_list:
|
||||||
- self._apply_hp(hp)
|
- self._apply_hp(hp)
|
||||||
- if self.opts.takeover and self.is_need_accept_kernel_hp:
|
- if self.opts.takeover and self.is_need_accept_kernel_hp:
|
||||||
- self._accept_kernel_hp(hp)
|
- self._accept_kernel_hp(hp)
|
||||||
-
|
-
|
||||||
- def _apply_hp(self, hp_full_name):
|
- def _apply_hp(self, hp_full_name):
|
||||||
- pkg_info = self._parse_hp_name(hp_full_name)
|
- pkg_info = self._parse_hp_name(hp_full_name)
|
||||||
- hp_subname = self._get_hp_subname_for_syscare(pkg_info)
|
- hp_subname = self._get_hp_subname_for_syscare(pkg_info)
|
||||||
- output, status = self.syscare.apply(hp_subname)
|
- output, status = self.syscare.apply(hp_subname)
|
||||||
- if status:
|
- if status:
|
||||||
- logger.info(_('Apply hot patch failed: %s.'), hp_subname)
|
- logger.info(_('Apply hot patch failed: %s.'), hp_subname)
|
||||||
- else:
|
- else:
|
||||||
- logger.info(_('Apply hot patch succeed: %s.'), hp_subname)
|
- logger.info(_('Apply hot patch succeed: %s.'), hp_subname)
|
||||||
-
|
-
|
||||||
- @staticmethod
|
- @staticmethod
|
||||||
- def _get_hp_subname_for_syscare(pkg_info: dict) -> str:
|
- def _get_hp_subname_for_syscare(pkg_info: dict) -> str:
|
||||||
- """
|
- """
|
||||||
- get hotpatch's subname for syscare command. e.g. redis-1-1/ACC-1-1
|
- get hotpatch's subname for syscare command. e.g. redis-1-1/ACC-1-1
|
||||||
- Args:
|
- Args:
|
||||||
- pkg_info: out put of _parse_hp_name.
|
- pkg_info: out put of _parse_hp_name.
|
||||||
-
|
-
|
||||||
- Returns:
|
- Returns:
|
||||||
- str
|
- str
|
||||||
- """
|
- """
|
||||||
- hp_subname = (
|
- hp_subname = (
|
||||||
- "-".join([pkg_info["target_name"], pkg_info["target_version"], pkg_info["target_release"]])
|
- "-".join([pkg_info["target_name"], pkg_info["target_version"], pkg_info["target_release"]])
|
||||||
- + '/'
|
- + '/'
|
||||||
- + "-".join([pkg_info["hp_name"], pkg_info["hp_version"], pkg_info["hp_release"]])
|
- + "-".join([pkg_info["hp_name"], pkg_info["hp_version"], pkg_info["hp_release"]])
|
||||||
- )
|
- )
|
||||||
- return hp_subname
|
- return hp_subname
|
||||||
-
|
-
|
||||||
- def _get_available_hotpatches(self, pkg_specs: list) -> dict:
|
- def _get_available_hotpatches(self, pkg_specs: list) -> dict:
|
||||||
- """
|
- """
|
||||||
- check two conditions:
|
- check two conditions:
|
||||||
- 1. the hot patch rpm package exists in repositories
|
- 1. the hot patch rpm package exists in repositories
|
||||||
- 2. the hot patch's target package with specific version and release already installed
|
- 2. the hot patch's target package with specific version and release already installed
|
||||||
- Args:
|
- Args:
|
||||||
- pkg_specs: full names of hot patches' rpm packages
|
- pkg_specs: full names of hot patches' rpm packages
|
||||||
-
|
-
|
||||||
- Returns:
|
- Returns:
|
||||||
- dict: key is available hot patches' full name,
|
- dict: key is available hot patches' full name,
|
||||||
- value is hot patches' operate name. e.g. kernel-5.10.0-60.66.0.91.oe2203/ACC-1-1
|
- value is hot patches' operate name. e.g. kernel-5.10.0-60.66.0.91.oe2203/ACC-1-1
|
||||||
- """
|
- """
|
||||||
- hp_map = {}
|
- hp_map = {}
|
||||||
- installed_packages = self.base.sack.query().installed()
|
- installed_packages = self.base.sack.query().installed()
|
||||||
- for pkg_spec in set(pkg_specs):
|
- for pkg_spec in set(pkg_specs):
|
||||||
- query = self.base.sack.query()
|
- query = self.base.sack.query()
|
||||||
- # check the package exist in repo or not
|
- # check the package exist in repo or not
|
||||||
- subj = dnf.subject.Subject(pkg_spec)
|
- subj = dnf.subject.Subject(pkg_spec)
|
||||||
- parsed_nevras = subj.get_nevra_possibilities(forms=[hawkey.FORM_NEVRA])
|
- parsed_nevras = subj.get_nevra_possibilities(forms=[hawkey.FORM_NEVRA])
|
||||||
- if len(parsed_nevras) != 1:
|
- if len(parsed_nevras) != 1:
|
||||||
- logger.info(_('Cannot parse NEVRA for package "{nevra}"').format(nevra=pkg_spec))
|
- logger.info(_('Cannot parse NEVRA for package "{nevra}"').format(nevra=pkg_spec))
|
||||||
- continue
|
- continue
|
||||||
-
|
-
|
||||||
- parsed_nevra = parsed_nevras[0]
|
- parsed_nevra = parsed_nevras[0]
|
||||||
- available_hp = query.available().filter(
|
- available_hp = query.available().filter(
|
||||||
- name=parsed_nevra.name,
|
- name=parsed_nevra.name,
|
||||||
- version=parsed_nevra.version,
|
- version=parsed_nevra.version,
|
||||||
- release=parsed_nevra.release,
|
- release=parsed_nevra.release,
|
||||||
- arch=parsed_nevra.arch,
|
- arch=parsed_nevra.arch,
|
||||||
- )
|
- )
|
||||||
- if not available_hp:
|
- if not available_hp:
|
||||||
- logger.info(_('No match for argument: %s'), self.base.output.term.bold(pkg_spec))
|
- logger.info(_('No match for argument: %s'), self.base.output.term.bold(pkg_spec))
|
||||||
- continue
|
- continue
|
||||||
-
|
-
|
||||||
- # check the hot patch's target package installed or not
|
- # check the hot patch's target package installed or not
|
||||||
- pkg_info = self._parse_hp_name(pkg_spec)
|
- pkg_info = self._parse_hp_name(pkg_spec)
|
||||||
- installed_pkg = installed_packages.filter(
|
- installed_pkg = installed_packages.filter(
|
||||||
- name=pkg_info["target_name"], version=pkg_info["target_version"], release=pkg_info["target_release"]
|
- name=pkg_info["target_name"], version=pkg_info["target_version"], release=pkg_info["target_release"]
|
||||||
- ).run()
|
- ).run()
|
||||||
- if not installed_pkg:
|
- if not installed_pkg:
|
||||||
- logger.info(
|
- logger.info(
|
||||||
- _("The hot patch's target package is not installed: %s"), self.base.output.term.bold(pkg_spec)
|
- _("The hot patch's target package is not installed: %s"), self.base.output.term.bold(pkg_spec)
|
||||||
- )
|
- )
|
||||||
- continue
|
- continue
|
||||||
-
|
-
|
||||||
- if len(installed_pkg) != 1:
|
- if len(installed_pkg) != 1:
|
||||||
- logger.info(
|
- logger.info(
|
||||||
- _("The hot patch '%s' has multiple target packages, please check."),
|
- _("The hot patch '%s' has multiple target packages, please check."),
|
||||||
- self.base.output.term.bold(pkg_spec),
|
- self.base.output.term.bold(pkg_spec),
|
||||||
- )
|
- )
|
||||||
- continue
|
- continue
|
||||||
- hp_subname = self._get_hp_subname_for_syscare(pkg_info)
|
- hp_subname = self._get_hp_subname_for_syscare(pkg_info)
|
||||||
- hp_map[pkg_spec] = hp_subname
|
- hp_map[pkg_spec] = hp_subname
|
||||||
- return hp_map
|
- return hp_map
|
||||||
-
|
-
|
||||||
- @staticmethod
|
- @staticmethod
|
||||||
- def _get_applied_old_patch(available_hp_list: list) -> list:
|
- def _get_applied_old_patch(available_hp_list: list) -> list:
|
||||||
- """
|
- """
|
||||||
- get targets' applied accumulative hot patches.
|
- get targets' applied accumulative hot patches.
|
||||||
- User can install and apply multiple sgl (single) hot patches because the rpm name is different,
|
- User can install and apply multiple sgl (single) hot patches because the rpm name is different,
|
||||||
- but for acc (accumulative) hot patch, user can only install one for a specific target binary rpm.
|
- but for acc (accumulative) hot patch, user can only install one for a specific target binary rpm.
|
||||||
- Args:
|
- Args:
|
||||||
- available_hp_list: e.g. ['redis-1.0-1/ACC-1-1', 'redis-1.0-1/SGL_CVE_2022_1-1-1']
|
- available_hp_list: e.g. ['redis-1.0-1/ACC-1-1', 'redis-1.0-1/SGL_CVE_2022_1-1-1']
|
||||||
-
|
-
|
||||||
- Returns:
|
- Returns:
|
||||||
- list: applied hot patches. e.g. ['redis-1.0-1/ACC-1-1']
|
- list: applied hot patches. e.g. ['redis-1.0-1/ACC-1-1']
|
||||||
- """
|
- """
|
||||||
- hotpatch_set = set()
|
- hotpatch_set = set()
|
||||||
- hps_info = Syscare.list()
|
- hps_info = Syscare.list()
|
||||||
- for hp_info in hps_info:
|
- for hp_info in hps_info:
|
||||||
- # hp_info[Name] is the middle column of syscare list. format: {target_rpm_name}/{hp_name}/{binary_file}
|
- # hp_info[Name] is the middle column of syscare list. format: {target_rpm_name}/{hp_name}/{binary_file}
|
||||||
- # a hotpatch is mapped to a target binary rpm, and may affect multiple binary executable binary files
|
- # a hotpatch is mapped to a target binary rpm, and may affect multiple binary executable binary files
|
||||||
- # e.g. for hotpatch patch-redis-1-1-ACC-1-1.x86_64.rpm, it may provide 2 sub hotpatches in syscare list,
|
- # e.g. for hotpatch patch-redis-1-1-ACC-1-1.x86_64.rpm, it may provide 2 sub hotpatches in syscare list,
|
||||||
- # and SGL hotpatches may be installed at the same time
|
- # and SGL hotpatches may be installed at the same time
|
||||||
- # redis-1-1/ACC-1-1/redis
|
- # redis-1-1/ACC-1-1/redis
|
||||||
- # redis-1-1/ACC-1-1/redis-cli
|
- # redis-1-1/ACC-1-1/redis-cli
|
||||||
- # redis-1-1/SGL_CVE_2022_1-1-1/redis
|
- # redis-1-1/SGL_CVE_2022_1-1-1/redis
|
||||||
- # redis-1-1/SGL_CVE_2022_2-1-1/redis
|
- # redis-1-1/SGL_CVE_2022_2-1-1/redis
|
||||||
- target, hp_name, binary_file = hp_info["Name"].split('/')
|
- target, hp_name, binary_file = hp_info["Name"].split('/')
|
||||||
- hotpatch = target + '/' + hp_name
|
- hotpatch = target + '/' + hp_name
|
||||||
- # right now, if part of the hotpatch (for different binary file) is applied,
|
- # right now, if part of the hotpatch (for different binary file) is applied,
|
||||||
- # we consider the hotpatch is applied
|
- # we consider the hotpatch is applied
|
||||||
- if hotpatch in available_hp_list and hp_info["Status"] != "NOT-APPLIED":
|
- if hotpatch in available_hp_list and hp_info["Status"] != "NOT-APPLIED":
|
||||||
- logger.info(
|
- logger.info(
|
||||||
- _("The hotpatch '%s' already has a '%s' sub hotpatch of binary file '%s'"),
|
- _("The hotpatch '%s' already has a '%s' sub hotpatch of binary file '%s'"),
|
||||||
- hotpatch,
|
- hotpatch,
|
||||||
- hp_info["Status"],
|
- hp_info["Status"],
|
||||||
- binary_file,
|
- binary_file,
|
||||||
- )
|
- )
|
||||||
- if hotpatch not in hotpatch_set:
|
- if hotpatch not in hotpatch_set:
|
||||||
- hotpatch_set.add(hotpatch)
|
- hotpatch_set.add(hotpatch)
|
||||||
- return list(hotpatch_set)
|
- return list(hotpatch_set)
|
||||||
-
|
-
|
||||||
- def _remove_hot_patches(self, applied_old_patches: list) -> None:
|
- def _remove_hot_patches(self, applied_old_patches: list) -> None:
|
||||||
- # output = Output(self.base, dnf.conf.Conf())
|
- # output = Output(self.base, dnf.conf.Conf())
|
||||||
- logger.info(_("Gonna remove these hot patches: %s"), applied_old_patches)
|
- logger.info(_("Gonna remove these hot patches: %s"), applied_old_patches)
|
||||||
- # remove_flag = output.userconfirm()
|
- # remove_flag = output.userconfirm()
|
||||||
- # if not remove_flag:
|
- # if not remove_flag:
|
||||||
- # raise dnf.exceptions.Error(_('Operation aborted.'))
|
- # raise dnf.exceptions.Error(_('Operation aborted.'))
|
||||||
-
|
-
|
||||||
- self.syscare.save()
|
- self.syscare.save()
|
||||||
- for hp_name in applied_old_patches:
|
- for hp_name in applied_old_patches:
|
||||||
- logger.info(_("Remove hot patch %s."), hp_name)
|
- logger.info(_("Remove hot patch %s."), hp_name)
|
||||||
- output, status = self.syscare.remove(hp_name)
|
- output, status = self.syscare.remove(hp_name)
|
||||||
- if status:
|
- if status:
|
||||||
- logger.info(
|
- logger.info(
|
||||||
- _("Remove hot patch '%s' failed, roll back to original status."),
|
- _("Remove hot patch '%s' failed, roll back to original status."),
|
||||||
- self.base.output.term.bold(hp_name),
|
- self.base.output.term.bold(hp_name),
|
||||||
- )
|
- )
|
||||||
- output, status = self.syscare.restore()
|
- output, status = self.syscare.restore()
|
||||||
- if status:
|
- if status:
|
||||||
- raise dnf.exceptions.Error(_('Roll back failed.'))
|
- raise dnf.exceptions.Error(_('Roll back failed.'))
|
||||||
- raise dnf.exceptions.Error(_('Roll back succeed.'))
|
- raise dnf.exceptions.Error(_('Roll back succeed.'))
|
||||||
-
|
-
|
||||||
- @staticmethod
|
- @staticmethod
|
||||||
- def _parse_hp_name(hp_filename: str) -> dict:
|
- def _parse_hp_name(hp_filename: str) -> dict:
|
||||||
- """
|
- """
|
||||||
- parse hot patch's name, get target rpm's name, version, release and hp's name.
|
- parse hot patch's name, get target rpm's name, version, release and hp's name.
|
||||||
- Args:
|
- Args:
|
||||||
- hp_filename: hot patch's name, in the format of
|
- hp_filename: hot patch's name, in the format of
|
||||||
- 'patch-{pkg_name}-{pkg_version}-{pkg_release}-{patchname}-{patch_version}-{patch_release}'
|
- 'patch-{pkg_name}-{pkg_version}-{pkg_release}-{patchname}-{patch_version}-{patch_release}'
|
||||||
- e.g. patch-kernel-5.10.0-60.66.0.91.oe2203-ACC-1-1.x86_64
|
- e.g. patch-kernel-5.10.0-60.66.0.91.oe2203-ACC-1-1.x86_64
|
||||||
- patch-kernel-5.10.0-60.66.0.91.oe2203-SGL_CVE_2022_1-1-1.x86_64
|
- patch-kernel-5.10.0-60.66.0.91.oe2203-SGL_CVE_2022_1-1-1.x86_64
|
||||||
- pkg_name may have '-' in it, patch name cannot have '-'.
|
- pkg_name may have '-' in it, patch name cannot have '-'.
|
||||||
- Returns:
|
- Returns:
|
||||||
- dict: rpm info. {"target_name": "", "target_version": "", "target_release": "", "hp_name": "",
|
- dict: rpm info. {"target_name": "", "target_version": "", "target_release": "", "hp_name": "",
|
||||||
- "hp_version": "", "hp_release": ""}
|
- "hp_version": "", "hp_release": ""}
|
||||||
- """
|
- """
|
||||||
- hp_filename_format = (
|
- hp_filename_format = (
|
||||||
- "patch-{pkg_name}-{pkg_version}-{pkg_release}-{patch_name}-" "{patch_version}-{patch_release}.{arch}"
|
- "patch-{pkg_name}-{pkg_version}-{pkg_release}-{patch_name}-" "{patch_version}-{patch_release}.{arch}"
|
||||||
- )
|
- )
|
||||||
-
|
-
|
||||||
- remove_suffix_filename = hp_filename.rsplit(".", 1)[0]
|
- remove_suffix_filename = hp_filename.rsplit(".", 1)[0]
|
||||||
- splitted_hp_filename = remove_suffix_filename.split('-')
|
- splitted_hp_filename = remove_suffix_filename.split('-')
|
||||||
- try:
|
- try:
|
||||||
- rpm_info = {
|
- rpm_info = {
|
||||||
- "target_release": splitted_hp_filename[-4],
|
- "target_release": splitted_hp_filename[-4],
|
||||||
- "target_version": splitted_hp_filename[-5],
|
- "target_version": splitted_hp_filename[-5],
|
||||||
- "target_name": "-".join(splitted_hp_filename[1:-5]),
|
- "target_name": "-".join(splitted_hp_filename[1:-5]),
|
||||||
- "hp_name": splitted_hp_filename[-3],
|
- "hp_name": splitted_hp_filename[-3],
|
||||||
- "hp_version": splitted_hp_filename[-2],
|
- "hp_version": splitted_hp_filename[-2],
|
||||||
- "hp_release": splitted_hp_filename[-1],
|
- "hp_release": splitted_hp_filename[-1],
|
||||||
- }
|
- }
|
||||||
- except IndexError as e:
|
- except IndexError as e:
|
||||||
- raise dnf.exceptions.Error(
|
- raise dnf.exceptions.Error(
|
||||||
- _(
|
- _(
|
||||||
- "Parse hot patch name failed. Please insert correct hot patch name "
|
- "Parse hot patch name failed. Please insert correct hot patch name "
|
||||||
- "with the format: \n %s" % hp_filename_format
|
- "with the format: \n %s" % hp_filename_format
|
||||||
- )
|
- )
|
||||||
- )
|
- )
|
||||||
- return rpm_info
|
- return rpm_info
|
||||||
-
|
-
|
||||||
- def _install_rpm_pkg(self, pkg_specs: list) -> bool:
|
- def _install_rpm_pkg(self, pkg_specs: list) -> bool:
|
||||||
- """
|
- """
|
||||||
- install rpm package
|
- install rpm package
|
||||||
- Args:
|
- Args:
|
||||||
- pkg_specs: rpm package's full name
|
- pkg_specs: rpm package's full name
|
||||||
-
|
-
|
||||||
- Returns:
|
- Returns:
|
||||||
- bool
|
- bool
|
||||||
- """
|
- """
|
||||||
- success = True
|
- success = True
|
||||||
- for pkg_spec in pkg_specs:
|
- for pkg_spec in pkg_specs:
|
||||||
- try:
|
- try:
|
||||||
- self.base.install(pkg_spec)
|
- self.base.install(pkg_spec)
|
||||||
- except dnf.exceptions.MarkingError as e:
|
- except dnf.exceptions.MarkingError as e:
|
||||||
- logger.info(_('No match for argument: %s.'), self.base.output.term.bold(pkg_spec))
|
- logger.info(_('No match for argument: %s.'), self.base.output.term.bold(pkg_spec))
|
||||||
- success = False
|
- success = False
|
||||||
- return success
|
- return success
|
||||||
-
|
-
|
||||||
- def get_hotpatch_based_on_cve(self, cves: list) -> list:
|
- def get_hotpatch_based_on_cve(self, cves: list) -> list:
|
||||||
- """
|
- """
|
||||||
- Get the hot patches corresponding to CVEs
|
- Get the hot patches corresponding to CVEs
|
||||||
- Args:
|
- Args:
|
||||||
- cves: cve id list
|
- cves: cve id list
|
||||||
-
|
-
|
||||||
- Returns:
|
- Returns:
|
||||||
- list: list of hot patches full name. e.g.["tmp2-tss-3.1.0-3.oe2203sp1"]
|
- list: list of hot patches full name. e.g.["tmp2-tss-3.1.0-3.oe2203sp1"]
|
||||||
- """
|
- """
|
||||||
- updateinfo = HotpatchUpdateInfo(self.cli.base, self.cli)
|
- updateinfo = HotpatchUpdateInfo(self.cli.base, self.cli)
|
||||||
- hp_list = []
|
- hp_list = []
|
||||||
- cve_hp_dict = updateinfo.get_hotpatches_from_cve(cves)
|
- cve_hp_dict = updateinfo.get_hotpatches_from_cve(cves)
|
||||||
- for cve, hp in cve_hp_dict.items():
|
- for cve, hp in cve_hp_dict.items():
|
||||||
- if not hp:
|
- if not hp:
|
||||||
- logger.info(_("The cve doesn't exist or cannot be fixed by hotpatch: %s"), cve)
|
- logger.info(_("The cve doesn't exist or cannot be fixed by hotpatch: %s"), cve)
|
||||||
- continue
|
- continue
|
||||||
- hp_list += hp
|
- hp_list += hp
|
||||||
- return list(set(hp_list))
|
- return list(set(hp_list))
|
||||||
-
|
-
|
||||||
- def get_hotpatch_based_on_advisory(self, advisories: list) -> list:
|
- def get_hotpatch_based_on_advisory(self, advisories: list) -> list:
|
||||||
- """
|
- """
|
||||||
- Get the hot patches corresponding to advisories
|
- Get the hot patches corresponding to advisories
|
||||||
- Args:
|
- Args:
|
||||||
- advisories: advisory id list
|
- advisories: advisory id list
|
||||||
-
|
-
|
||||||
- Returns:
|
- Returns:
|
||||||
- list: list of hot patches full name. e.g.["tmp2-tss-3.1.0-3.oe2203sp1"]
|
- list: list of hot patches full name. e.g.["tmp2-tss-3.1.0-3.oe2203sp1"]
|
||||||
- """
|
- """
|
||||||
- updateinfo = HotpatchUpdateInfo(self.cli.base, self.cli)
|
- updateinfo = HotpatchUpdateInfo(self.cli.base, self.cli)
|
||||||
- hp_list = []
|
- hp_list = []
|
||||||
- advisory_hp_dict = updateinfo.get_hotpatches_from_advisories(advisories)
|
- advisory_hp_dict = updateinfo.get_hotpatches_from_advisories(advisories)
|
||||||
- for hp in advisory_hp_dict.values():
|
- for hp in advisory_hp_dict.values():
|
||||||
- hp_list += hp
|
- hp_list += hp
|
||||||
- return list(set(hp_list))
|
- return list(set(hp_list))
|
||||||
-
|
-
|
||||||
- def get_hotpatch_of_all_cve(self) -> list:
|
- def get_hotpatch_of_all_cve(self) -> list:
|
||||||
- """
|
- """
|
||||||
- upgrade all exist cve using hot patches
|
- upgrade all exist cve using hot patches
|
||||||
- 1. find all cves when init HotpatchUpdateInfo
|
- 1. find all cves when init HotpatchUpdateInfo
|
||||||
- 2. get the recommended hot patch for each cve
|
- 2. get the recommended hot patch for each cve
|
||||||
- 3. deduplication
|
- 3. deduplication
|
||||||
- Returns:
|
- Returns:
|
||||||
- ['patch-redis-6.2.5-1-HP2-1-1.x86_64']
|
- ['patch-redis-6.2.5-1-HP2-1-1.x86_64']
|
||||||
- """
|
- """
|
||||||
- updateinfo = HotpatchUpdateInfo(self.cli.base, self.cli)
|
- updateinfo = HotpatchUpdateInfo(self.cli.base, self.cli)
|
||||||
- cve_list = self.get_all_cve_which_can_be_fixed_by_hotpatch()
|
- cve_list = self.get_all_cve_which_can_be_fixed_by_hotpatch()
|
||||||
- hp_list = []
|
- hp_list = []
|
||||||
- cve_hp_dict = updateinfo.get_hotpatches_from_cve(cve_list)
|
- cve_hp_dict = updateinfo.get_hotpatches_from_cve(cve_list)
|
||||||
- for hp in cve_hp_dict.values():
|
- for hp in cve_hp_dict.values():
|
||||||
- if not hp:
|
- if not hp:
|
||||||
- continue
|
- continue
|
||||||
- hp_list += hp
|
- hp_list += hp
|
||||||
- return list(set(hp_list))
|
- return list(set(hp_list))
|
||||||
-
|
-
|
||||||
- def get_all_cve_which_can_be_fixed_by_hotpatch(self) -> list:
|
- def get_all_cve_which_can_be_fixed_by_hotpatch(self) -> list:
|
||||||
- """
|
- """
|
||||||
- get all unfixed cve which can be fixed by hotpatch
|
- get all unfixed cve which can be fixed by hotpatch
|
||||||
- use command : dnf hot-updateinfo list cves
|
- use command : dnf hot-updateinfo list cves
|
||||||
- Last metadata expiration check: 0:48:26 ago on 2023年06月01日 星期四 20时29分55秒.
|
- Last metadata expiration check: 0:48:26 ago on 2023年06月01日 星期四 20时29分55秒.
|
||||||
- CVE-2023-3332 Low/Sec. - -
|
- CVE-2023-3332 Low/Sec. - -
|
||||||
- CVE-2023-3331 Low/Sec. - -
|
- CVE-2023-3331 Low/Sec. - -
|
||||||
- CVE-2023-1111 Important/Sec. - patch-redis-6.2.5-1-ACC-1-1.x86_64
|
- CVE-2023-1111 Important/Sec. - patch-redis-6.2.5-1-ACC-1-1.x86_64
|
||||||
- CVE-2023-1111 Important/Sec. - patch-redis-cli-6.2.5-1-ACC-1-1.x86_64
|
- CVE-2023-1111 Important/Sec. - patch-redis-cli-6.2.5-1-ACC-1-1.x86_64
|
||||||
-
|
-
|
||||||
- Returns: list of unfixed cve. e.g.['CVE-2023-1111']
|
- Returns: list of unfixed cve. e.g.['CVE-2023-1111']
|
||||||
- """
|
- """
|
||||||
- hp_hawkey = HotpatchUpdateInfo(self.cli.base, self.cli)
|
- hp_hawkey = HotpatchUpdateInfo(self.cli.base, self.cli)
|
||||||
- hot_updateinfo = HotUpdateinfoCommand(self.cli)
|
- hot_updateinfo = HotUpdateinfoCommand(self.cli)
|
||||||
- hot_updateinfo.opts = self.opts
|
- hot_updateinfo.opts = self.opts
|
||||||
- hot_updateinfo.hp_hawkey = hp_hawkey
|
- hot_updateinfo.hp_hawkey = hp_hawkey
|
||||||
- hot_updateinfo.filter_cves = None
|
- hot_updateinfo.filter_cves = None
|
||||||
- hot_updateinfo.opts.availability = 'available'
|
- hot_updateinfo.opts.availability = 'available'
|
||||||
- all_cves = hot_updateinfo.get_formatting_parameters_and_display_lines()
|
- all_cves = hot_updateinfo.get_formatting_parameters_and_display_lines()
|
||||||
- cve_set = set()
|
- cve_set = set()
|
||||||
- for display_line in all_cves.display_lines:
|
- for display_line in all_cves.display_lines:
|
||||||
- if display_line[3] != EMPTY_TAG:
|
- if display_line[3] != EMPTY_TAG:
|
||||||
- cve_set.add(display_line[0])
|
- cve_set.add(display_line[0])
|
||||||
- return list(cve_set)
|
- return list(cve_set)
|
||||||
-
|
-
|
||||||
- def takeover_operation(self):
|
- def takeover_operation(self):
|
||||||
- """
|
- """
|
||||||
- process takeover operation.
|
- process takeover operation.
|
||||||
- """
|
- """
|
||||||
- kernel_coldpatch = self.get_target_installed_kernel_coldpatch_of_hotpatch()
|
- kernel_coldpatch = self.get_target_installed_kernel_coldpatch_of_hotpatch()
|
||||||
- self.is_need_accept_kernel_hp = False
|
- self.is_need_accept_kernel_hp = False
|
||||||
- if kernel_coldpatch:
|
- if kernel_coldpatch:
|
||||||
- logger.info(_("Gonna takeover kernel cold patch: ['%s']" % kernel_coldpatch))
|
- logger.info(_("Gonna takeover kernel cold patch: ['%s']" % kernel_coldpatch))
|
||||||
- success = self._install_rpm_pkg([kernel_coldpatch])
|
- success = self._install_rpm_pkg([kernel_coldpatch])
|
||||||
- if success:
|
- if success:
|
||||||
- return
|
- return
|
||||||
- logger.info(_("Takeover operation failed."))
|
- logger.info(_("Takeover operation failed."))
|
||||||
- else:
|
- else:
|
||||||
- logger.info(_('No kernel cold patch matched for takeover.'))
|
- logger.info(_('No kernel cold patch matched for takeover.'))
|
||||||
- self.is_need_accept_kernel_hp = True
|
- self.is_need_accept_kernel_hp = True
|
||||||
- logger.info(
|
- logger.info(
|
||||||
- _(
|
- _(
|
||||||
- 'To maintain the effectiveness of kernel hot patch after rebooting, gonna accept available kernel hot patch.'
|
- 'To maintain the effectiveness of kernel hot patch after rebooting, gonna accept available kernel hot patch.'
|
||||||
- )
|
- )
|
||||||
- )
|
- )
|
||||||
- return
|
- return
|
||||||
-
|
-
|
||||||
- def get_target_installed_kernel_coldpatch_of_hotpatch(self) -> str:
|
- 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
|
- get the highest kernel cold patch of hot patch in "dnf hot-updateinfo list cves", if the corresponding
|
||||||
- kernel cold patch exists.
|
- kernel cold patch exists.
|
||||||
-
|
-
|
||||||
- Returns:
|
- Returns:
|
||||||
- str: full name of cold patch. e.g. kernel-5.10.0-1.x86_64
|
- str: full name of cold patch. e.g. kernel-5.10.0-1.x86_64
|
||||||
- """
|
- """
|
||||||
- hp_hawkey = HotpatchUpdateInfo(self.cli.base, self.cli)
|
- hp_hawkey = HotpatchUpdateInfo(self.cli.base, self.cli)
|
||||||
- hot_updateinfo = HotUpdateinfoCommand(self.cli)
|
- hot_updateinfo = HotUpdateinfoCommand(self.cli)
|
||||||
- hot_updateinfo.opts = self.opts
|
- hot_updateinfo.opts = self.opts
|
||||||
- hot_updateinfo.hp_hawkey = hp_hawkey
|
- hot_updateinfo.hp_hawkey = hp_hawkey
|
||||||
- hot_updateinfo.filter_cves = None
|
- hot_updateinfo.filter_cves = None
|
||||||
- hot_updateinfo.opts.availability = 'available'
|
- hot_updateinfo.opts.availability = 'available'
|
||||||
- all_cves = hot_updateinfo.get_formatting_parameters_and_display_lines()
|
- all_cves = hot_updateinfo.get_formatting_parameters_and_display_lines()
|
||||||
- kernel_highest_vere = ""
|
- kernel_highest_vere = ""
|
||||||
- target_kernel_coldpatch = None
|
- target_kernel_coldpatch = None
|
||||||
- version = Versions()
|
- version = Versions()
|
||||||
- for display_line in all_cves.display_lines:
|
- for display_line in all_cves.display_lines:
|
||||||
- if display_line[3] not in self.hp_list:
|
- if display_line[3] not in self.hp_list:
|
||||||
- continue
|
- continue
|
||||||
- kernel_pkg_spec = display_line[2]
|
- kernel_pkg_spec = display_line[2]
|
||||||
- if kernel_pkg_spec == EMPTY_TAG:
|
- if kernel_pkg_spec == EMPTY_TAG:
|
||||||
- continue
|
- continue
|
||||||
- pkg_name, pkg_ver = self._get_pkg_name_and_ver(kernel_pkg_spec)
|
- pkg_name, pkg_ver = self._get_pkg_name_and_ver(kernel_pkg_spec)
|
||||||
- if pkg_name != "kernel":
|
- if pkg_name != "kernel":
|
||||||
- continue
|
- continue
|
||||||
- if kernel_highest_vere == "":
|
- if kernel_highest_vere == "":
|
||||||
- kernel_highest_vere = pkg_ver
|
- kernel_highest_vere = pkg_ver
|
||||||
- target_kernel_coldpatch = kernel_pkg_spec
|
- target_kernel_coldpatch = kernel_pkg_spec
|
||||||
- continue
|
- continue
|
||||||
- if version.larger_than(pkg_ver, kernel_highest_vere):
|
- if version.larger_than(pkg_ver, kernel_highest_vere):
|
||||||
- kernel_highest_vere = pkg_ver
|
- kernel_highest_vere = pkg_ver
|
||||||
- target_kernel_coldpatch = kernel_pkg_spec
|
- target_kernel_coldpatch = kernel_pkg_spec
|
||||||
-
|
-
|
||||||
- return target_kernel_coldpatch
|
- return target_kernel_coldpatch
|
||||||
-
|
-
|
||||||
- def _get_pkg_name_and_ver(self, pkg_spec: str):
|
- def _get_pkg_name_and_ver(self, pkg_spec: str):
|
||||||
- """
|
- """
|
||||||
- get the "name" and "version-release" string of package.
|
- get the "name" and "version-release" string of package.
|
||||||
-
|
-
|
||||||
- Args:
|
- Args:
|
||||||
- str: pkg_specs: full name of cold patch. e.g. kernel-5.10.0-1.x86_64
|
- str: pkg_specs: full name of cold patch. e.g. kernel-5.10.0-1.x86_64
|
||||||
- """
|
- """
|
||||||
- subj = dnf.subject.Subject(pkg_spec)
|
- subj = dnf.subject.Subject(pkg_spec)
|
||||||
- parsed_nevras = subj.get_nevra_possibilities(forms=[hawkey.FORM_NEVRA])
|
- parsed_nevras = subj.get_nevra_possibilities(forms=[hawkey.FORM_NEVRA])
|
||||||
- if len(parsed_nevras) != 1:
|
- if len(parsed_nevras) != 1:
|
||||||
- logger.info(_('Cannot parse NEVRA for package "{nevra}"').format(nevra=pkg_spec))
|
- logger.info(_('Cannot parse NEVRA for package "{nevra}"').format(nevra=pkg_spec))
|
||||||
- return ""
|
- return ""
|
||||||
- parsed_nevra = parsed_nevras[0]
|
- parsed_nevra = parsed_nevras[0]
|
||||||
- return parsed_nevra.name, "%s-%s" % (parsed_nevra.version, parsed_nevra.release)
|
- return parsed_nevra.name, "%s-%s" % (parsed_nevra.version, parsed_nevra.release)
|
||||||
-
|
-
|
||||||
- def _accept_kernel_hp(self, hp_full_name: str):
|
- def _accept_kernel_hp(self, hp_full_name: str):
|
||||||
- """
|
- """
|
||||||
- accept kernel hot patch
|
- accept kernel hot patch
|
||||||
-
|
-
|
||||||
- Args:
|
- Args:
|
||||||
- str: hp_full_name: full name of hot patch. e.g. patch-kernel-5.10.0-1-ACC-1-1.x86_64
|
- str: hp_full_name: full name of hot patch. e.g. patch-kernel-5.10.0-1-ACC-1-1.x86_64
|
||||||
- """
|
- """
|
||||||
- pkg_info = self._parse_hp_name(hp_full_name)
|
- pkg_info = self._parse_hp_name(hp_full_name)
|
||||||
- if pkg_info['target_name'] != "kernel":
|
- if pkg_info['target_name'] != "kernel":
|
||||||
- return
|
- return
|
||||||
- hp_subname = self._get_hp_subname_for_syscare(pkg_info)
|
- hp_subname = self._get_hp_subname_for_syscare(pkg_info)
|
||||||
- output, status = self.syscare.accept(hp_subname)
|
- output, status = self.syscare.accept(hp_subname)
|
||||||
- if status:
|
- if status:
|
||||||
- logger.info(_('Accept kernel hot patch failed: %s.'), hp_subname)
|
- logger.info(_('Accept kernel hot patch failed: %s.'), hp_subname)
|
||||||
- else:
|
- else:
|
||||||
- logger.info(_('Accept kernel hot patch succeed: %s.'), hp_subname)
|
- logger.info(_('Accept kernel hot patch succeed: %s.'), hp_subname)
|
||||||
diff --git a/hotpatch/syscare.py b/hotpatch/syscare.py
|
diff --git a/hotpatch/syscare.py b/hotpatch/syscare.py
|
||||||
deleted file mode 100644
|
deleted file mode 100644
|
||||||
index a858ed4..0000000
|
index a858ed4..0000000
|
||||||
@ -2652,5 +2649,5 @@ index 820b8d5..0000000
|
|||||||
- compare_version = self._order(compare_version)
|
- compare_version = self._order(compare_version)
|
||||||
- return version >= compare_version
|
- return version >= compare_version
|
||||||
--
|
--
|
||||||
Gitee
|
2.27.0
|
||||||
|
|
||||||
@ -7,7 +7,7 @@ URL: https://gitee.com/openeuler/%{name}
|
|||||||
Source0: %{name}-%{version}.tar.gz
|
Source0: %{name}-%{version}.tar.gz
|
||||||
Patch0001: 0001-optimize-cve-query-performance.patch
|
Patch0001: 0001-optimize-cve-query-performance.patch
|
||||||
Patch0002: 0002-suitable-for-2003-sp3.patch
|
Patch0002: 0002-suitable-for-2003-sp3.patch
|
||||||
Patch0003: 0003-remove-hotpatch.patch
|
Patch0003: 0003-rm-hotpatch.patch
|
||||||
|
|
||||||
BuildRequires: python3-setuptools
|
BuildRequires: python3-setuptools
|
||||||
Requires: aops-vulcanus >= v1.3.0
|
Requires: aops-vulcanus >= v1.3.0
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user