From d051179d8c136321926752afaaab02d9d27477e7 Mon Sep 17 00:00:00 2001 From: Marek Blaha Date: Wed, 11 Dec 2024 10:46:09 +0100 Subject: [PATCH] automatic: Added feature to allow emitters to invoke on dnf error Resolves: RHEL-45505 Resolves: RHEL-61882 --- ...tomatic-emitters-send-error-messages.patch | 247 ++++++++++++++++++ 0035-automatic-Enhance-errors-reporting.patch | 60 +++++ dnf.spec | 8 +- 3 files changed, 314 insertions(+), 1 deletion(-) create mode 100644 0034-automatic-emitters-send-error-messages.patch create mode 100644 0035-automatic-Enhance-errors-reporting.patch diff --git a/0034-automatic-emitters-send-error-messages.patch b/0034-automatic-emitters-send-error-messages.patch new file mode 100644 index 0000000..f17bbf7 --- /dev/null +++ b/0034-automatic-emitters-send-error-messages.patch @@ -0,0 +1,247 @@ +From 130e44556d49cf9fa4a0468bb73b56182691ab86 Mon Sep 17 00:00:00 2001 +From: derickdiaz +Date: Sun, 15 Oct 2023 10:58:03 -0500 +Subject: [PATCH 1/4] Added feature to allow emitters to invoke on dnf error + +--- + AUTHORS | 1 + + dnf/automatic/emitter.py | 22 ++++++++++++++++++---- + dnf/automatic/main.py | 5 +++++ + 3 files changed, 24 insertions(+), 4 deletions(-) + +diff --git a/AUTHORS b/AUTHORS +index 699a92c4..2e16c8ae 100644 +--- a/AUTHORS ++++ b/AUTHORS +@@ -68,6 +68,7 @@ DNF CONTRIBUTORS + Christopher Meng + Daniel Mach + Dave Johansen ++ Derick Diaz + Dylan Pindur + Eduard Cuba + Evan Goode +diff --git a/dnf/automatic/emitter.py b/dnf/automatic/emitter.py +index 648f1a1d..673da082 100644 +--- a/dnf/automatic/emitter.py ++++ b/dnf/automatic/emitter.py +@@ -33,6 +33,7 @@ APPLIED = _("The following updates have been applied on '%s':") + APPLIED_TIMESTAMP = _("Updates completed at %s") + AVAILABLE = _("The following updates are available on '%s':") + DOWNLOADED = _("The following updates were downloaded on '%s':") ++ERROR = _("An error has occured on: '%s'") + + logger = logging.getLogger('dnf') + +@@ -44,10 +45,15 @@ class Emitter(object): + self._downloaded = False + self._system_name = system_name + self._trans_msg = None ++ self._error = False ++ self._error_msg = None + + def _prepare_msg(self): + msg = [] +- if self._applied: ++ if self._error: ++ msg.append(ERROR % self._system_name) ++ msg.append(self._error_msg) ++ elif self._applied: + msg.append(APPLIED % self._system_name) + msg.append(self._available_msg) + msg.append(APPLIED_TIMESTAMP % time.strftime("%c")) +@@ -72,6 +78,10 @@ class Emitter(object): + assert self._available_msg + self._downloaded = True + ++ def notify_error(self, msg): ++ self._error = True ++ self._error_msg = msg ++ + + class EmailEmitter(Emitter): + def __init__(self, system_name, conf): +@@ -79,7 +89,9 @@ class EmailEmitter(Emitter): + self._conf = conf + + def _prepare_msg(self): +- if self._applied: ++ if self._error: ++ subj = _("An error has occured on '%s'.") % self._system_name ++ elif self._applied: + subj = _("Updates applied on '%s'.") % self._system_name + elif self._downloaded: + subj = _("Updates downloaded on '%s'.") % self._system_name +@@ -95,6 +107,8 @@ class EmailEmitter(Emitter): + message.set_charset('utf-8') + email_from = self._conf.email_from + email_to = self._conf.email_to ++ email_host = self._conf.email_host ++ email_port = self._conf.email_port + message['Date'] = email.utils.formatdate() + message['From'] = email_from + message['Subject'] = subj +@@ -103,12 +117,12 @@ class EmailEmitter(Emitter): + + # Send the email + try: +- smtp = smtplib.SMTP(self._conf.email_host, timeout=300) ++ smtp = smtplib.SMTP(email_host, email_port, timeout=300) + smtp.sendmail(email_from, email_to, message.as_string()) + smtp.close() + except OSError as exc: + msg = _("Failed to send an email via '%s': %s") % ( +- self._conf.email_host, exc) ++ email_host, exc) + logger.error(msg) + + +diff --git a/dnf/automatic/main.py b/dnf/automatic/main.py +index bb0bd493..8aca210a 100644 +--- a/dnf/automatic/main.py ++++ b/dnf/automatic/main.py +@@ -314,6 +314,7 @@ def main(args): + try: + conf = AutomaticConfig(opts.conf_path, opts.downloadupdates, + opts.installupdates) ++ emitters = None + with dnf.Base() as base: + cli = dnf.cli.Cli(base) + cli._read_conf_file() +@@ -376,9 +377,13 @@ def main(args): + exit_code = os.waitstatus_to_exitcode(os.system(conf.commands.reboot_command)) + if exit_code != 0: + logger.error('Error: reboot command returned nonzero exit code: %d', exit_code) ++ emitters.notify_error('Error: reboot command returned nonzero exit code: %d', exit_code) ++ emitters.commit() + return 1 + except dnf.exceptions.Error as exc: + logger.error(_('Error: %s'), ucd(exc)) ++ emitters.notify_error(_('Error: %s') % str(exc)) ++ emitters.commit() + return 1 + return 0 + +-- +2.47.1 + + +From 1d18e7c6c03d6de084a6845db3a2dc50f02c8e4c Mon Sep 17 00:00:00 2001 +From: derickdiaz +Date: Thu, 19 Oct 2023 04:58:45 -0500 +Subject: [PATCH 2/4] Checks if emitter is null incase build_emitters throws a + ConfigError + +--- + dnf/automatic/main.py | 5 +++-- + 1 file changed, 3 insertions(+), 2 deletions(-) + +diff --git a/dnf/automatic/main.py b/dnf/automatic/main.py +index 8aca210a..04926346 100644 +--- a/dnf/automatic/main.py ++++ b/dnf/automatic/main.py +@@ -382,8 +382,9 @@ def main(args): + return 1 + except dnf.exceptions.Error as exc: + logger.error(_('Error: %s'), ucd(exc)) +- emitters.notify_error(_('Error: %s') % str(exc)) +- emitters.commit() ++ if conf.emitters != None: ++ emitters.notify_error(_('Error: %s') % str(exc)) ++ emitters.commit() + return 1 + return 0 + +-- +2.47.1 + + +From 3e45752f0b74d2c4297da429a57b3e5148374195 Mon Sep 17 00:00:00 2001 +From: derickdiaz +Date: Thu, 19 Oct 2023 05:21:23 -0500 +Subject: [PATCH 3/4] Added 'send_error_messages' Boolean Option and updated + man docs + +Added option 'send_error_messages' + +Fixed Option String List + +Changed option to Boolean +--- + dnf/automatic/main.py | 8 +++----- + doc/automatic.rst | 5 +++++ + 2 files changed, 8 insertions(+), 5 deletions(-) + +diff --git a/dnf/automatic/main.py b/dnf/automatic/main.py +index 04926346..0a9d5041 100644 +--- a/dnf/automatic/main.py ++++ b/dnf/automatic/main.py +@@ -239,6 +239,7 @@ class EmittersConfig(Config): + libdnf.conf.VectorString(['email', 'stdio']))) + self.add_option('output_width', libdnf.conf.OptionNumberInt32(80)) + self.add_option('system_name', libdnf.conf.OptionString(socket.gethostname())) ++ self.add_option('send_error_messages', libdnf.conf.OptionBool(False)) + + + def gpgsigcheck(base, pkgs): +@@ -376,13 +377,10 @@ def main(args): + (conf.commands.reboot == 'when-needed' and base.reboot_needed())): + exit_code = os.waitstatus_to_exitcode(os.system(conf.commands.reboot_command)) + if exit_code != 0: +- logger.error('Error: reboot command returned nonzero exit code: %d', exit_code) +- emitters.notify_error('Error: reboot command returned nonzero exit code: %d', exit_code) +- emitters.commit() +- return 1 ++ raise dnf.exceptions.Error('reboot command returned nonzero exit code: %d', exit_code) + except dnf.exceptions.Error as exc: + logger.error(_('Error: %s'), ucd(exc)) +- if conf.emitters != None: ++ if conf.emitters.send_error_messages and emitters != None: + emitters.notify_error(_('Error: %s') % str(exc)) + emitters.commit() + return 1 +diff --git a/doc/automatic.rst b/doc/automatic.rst +index 329c2f46..2d514b78 100644 +--- a/doc/automatic.rst ++++ b/doc/automatic.rst +@@ -120,6 +120,11 @@ Choosing how the results should be reported. + + How the system is called in the reports. + ++``send_error_messages`` ++ boolean, default: False ++ ++ Invokes emitters when an errors occurs ++ + --------------------- + ``[command]`` section + --------------------- +-- +2.47.1 + + +From 33d52a8072c47b74603cf38ec807c26657799578 Mon Sep 17 00:00:00 2001 +From: derickdiaz +Date: Fri, 27 Oct 2023 12:00:21 -0500 +Subject: [PATCH 4/4] Fixed Typo in docs + +--- + doc/automatic.rst | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/doc/automatic.rst b/doc/automatic.rst +index 2d514b78..4f8eec68 100644 +--- a/doc/automatic.rst ++++ b/doc/automatic.rst +@@ -123,7 +123,7 @@ Choosing how the results should be reported. + ``send_error_messages`` + boolean, default: False + +- Invokes emitters when an errors occurs ++ Invokes emitters when an error occurs. + + --------------------- + ``[command]`` section +-- +2.47.1 + diff --git a/0035-automatic-Enhance-errors-reporting.patch b/0035-automatic-Enhance-errors-reporting.patch new file mode 100644 index 0000000..d58f88c --- /dev/null +++ b/0035-automatic-Enhance-errors-reporting.patch @@ -0,0 +1,60 @@ +From 2a1046f4dbf855902056e463a9d35612e93f786e Mon Sep 17 00:00:00 2001 +From: Marek Blaha +Date: Mon, 9 Dec 2024 13:42:44 +0100 +Subject: [PATCH] automatic: Enhance errors reporting + +Emitters must be initialized early to ensure they can report errors that +might occur in earlier stages of execution, before transaction +resolution. Also a broader range of exceptions must be caught to +ensure they are communicated to the user through the configured +emitters. +For example "No space left on the device" error can be raised during the +fill_sack() call. This patch should report it via configured emitters. + +Resolves: https://issues.redhat.com/browse/RHEL-61882 +--- + dnf/automatic/main.py | 9 +++++---- + 1 file changed, 5 insertions(+), 4 deletions(-) + +diff --git a/dnf/automatic/main.py b/dnf/automatic/main.py +index 0a9d5041..5ca4ad39 100644 +--- a/dnf/automatic/main.py ++++ b/dnf/automatic/main.py +@@ -311,11 +311,13 @@ def wait_for_network(repos, timeout): + + def main(args): + (opts, parser) = parse_arguments(args) ++ conf = None ++ emitters = None + + try: + conf = AutomaticConfig(opts.conf_path, opts.downloadupdates, + opts.installupdates) +- emitters = None ++ emitters = build_emitters(conf) + with dnf.Base() as base: + cli = dnf.cli.Cli(base) + cli._read_conf_file() +@@ -349,7 +351,6 @@ def main(args): + return 0 + + lst = output.list_transaction(trans, total_width=80) +- emitters = build_emitters(conf) + emitters.notify_available(lst) + if not conf.commands.download_updates: + emitters.commit() +@@ -378,9 +379,9 @@ def main(args): + exit_code = os.waitstatus_to_exitcode(os.system(conf.commands.reboot_command)) + if exit_code != 0: + raise dnf.exceptions.Error('reboot command returned nonzero exit code: %d', exit_code) +- except dnf.exceptions.Error as exc: ++ except Exception as exc: + logger.error(_('Error: %s'), ucd(exc)) +- if conf.emitters.send_error_messages and emitters != None: ++ if conf is not None and conf.emitters.send_error_messages and emitters is not None: + emitters.notify_error(_('Error: %s') % str(exc)) + emitters.commit() + return 1 +-- +2.47.1 + diff --git a/dnf.spec b/dnf.spec index 42b2ccb..e4d0216 100644 --- a/dnf.spec +++ b/dnf.spec @@ -69,7 +69,7 @@ It supports RPMs, modules and comps groups & environments. Name: dnf Version: 4.14.0 -Release: 21%{?dist} +Release: 22%{?dist} Summary: %{pkg_summary} # For a breakdown of the licensing, see PACKAGE-LICENSING License: GPLv2+ @@ -108,6 +108,8 @@ Patch30: 0030-Allow-installroot-on-read-only-bootc-system.patch Patch31: 0031-smtplib-catch-OSError-not-SMTPException.patch Patch32: 0032-Allow-downloadonly-on-read-only-bootc-system.patch Patch33: 0033-automatic-Check-availability-of-config-file.patch +Patch34: 0034-automatic-emitters-send-error-messages.patch +Patch35: 0035-automatic-Enhance-errors-reporting.patch BuildArch: noarch BuildRequires: cmake @@ -396,6 +398,10 @@ popd %{python3_sitelib}/%{name}/automatic/ %changelog +* Wed Dec 11 2024 Marek Blaha - 4.14.0-22 +- automatic: Added feature to allow emitters to invoke on dnf error + (RHEL-45505, RHEL-61882) + * Mon Oct 21 2024 Marek Blaha - 4.14.0-21 - automatic: Check availability of config file (RHEL-49743)