[PATCH] Log installation successes and failures via ipmitool (#782019).

Chris Lumens clumens at redhat.com
Fri Aug 8 17:53:36 UTC 2014


This adds a lot of calls to ipmi_report throughout anaconda where we call
sys.exit.  I'd prefer to do this in exitHandler but there's no good way to
get the exit code at that point, which means I don't know what context
(success vs. quit button vs. exception vs. ...) anaconda is quitting,
so there's no way to know what ipmi constant to use.
---
 anaconda                             |  8 ++++++++
 pyanaconda/constants.py              |  6 ++++++
 pyanaconda/exception.py              |  4 +++-
 pyanaconda/iutil.py                  | 27 ++++++++++++++++++++++++++-
 pyanaconda/kickstart.py              |  6 +++++-
 pyanaconda/packaging/dnfpayload.py   |  3 ++-
 pyanaconda/packaging/yumpayload.py   |  4 +++-
 pyanaconda/ui/gui/__init__.py        |  4 +++-
 pyanaconda/ui/gui/hubs/progress.py   |  5 ++++-
 pyanaconda/ui/gui/spokes/software.py |  3 ++-
 pyanaconda/ui/gui/spokes/storage.py  |  4 ++--
 pyanaconda/ui/gui/spokes/welcome.py  |  6 ++++--
 pyanaconda/ui/tui/simpleline/base.py |  4 +++-
 pyanaconda/ui/tui/spokes/progress.py |  5 ++++-
 pyanaconda/vnc.py                    |  7 ++++++-
 15 files changed, 81 insertions(+), 15 deletions(-)

diff --git a/anaconda b/anaconda
index 4d4676e..3b35561 100755
--- a/anaconda
+++ b/anaconda
@@ -633,6 +633,8 @@ def check_memory(anaconda, options, display_mode=None):
                                reason % reason_args,
                                buttons = (_("OK"),))
             screen.finish()
+
+        iutil.ipmi_report(constants.IPMI_ABORTED)
         sys.exit(1)
 
     # override display mode if machine cannot nicely run X
@@ -648,6 +650,7 @@ def check_memory(anaconda, options, display_mode=None):
                 stdoutLog.warning(reason % reason_args)
                 title = livecd_title
                 gtk_warning(title, reason % reason_args)
+                iutil.ipmi_report(constants.IPMI_ABORTED)
                 sys.exit(1)
             else:
                 reason += nolivecd_extra
@@ -835,6 +838,7 @@ def prompt_for_ssh():
 
     if not ip:
         stdoutLog.error("No IP addresses found, cannot continue installation.")
+        iutil.ipmi_report(constants.IPMI_ABORTED)
         sys.exit(1)
 
     ipstr = ip
@@ -960,8 +964,11 @@ if __name__ == "__main__":
 
     from pyanaconda import iutil
 
+    iutil.ipmi_report(constants.IPMI_STARTED)
+
     if opts.images and opts.dirinstall:
         stdoutLog.error("--images and --dirinstall cannot be used at the same time")
+        iutil.ipmi_report(constants.IPMI_ABORTED)
         sys.exit(1)
     elif opts.images:
         flags.imageInstall = True
@@ -1282,6 +1289,7 @@ if __name__ == "__main__":
             flags.imageInstall = True
     except ValueError as e:
         stdoutLog.error("error specifying image file: %s", e)
+        iutil.ipmi_report(constants.IPMI_ABORTED)
         sys.exit(1)
 
     if image_count:
diff --git a/pyanaconda/constants.py b/pyanaconda/constants.py
index 18c2a1f..ae3a750 100644
--- a/pyanaconda/constants.py
+++ b/pyanaconda/constants.py
@@ -161,3 +161,9 @@ DEFAULT_AUTOPART_TYPE = AUTOPART_TYPE_LVM
 
 import logging
 LOGLVL_LOCK = logging.DEBUG-1
+
+# Constants for reporting status to IPMI.  These are from the IPMI spec v2 rev1.1, page 512.
+IPMI_STARTED  = 0x7
+IPMI_FINISHED = 0x8
+IPMI_ABORTED  = 0x9
+IPMI_FAILED   = 0xA
diff --git a/pyanaconda/exception.py b/pyanaconda/exception.py
index fcc6c9b..4a10f08 100644
--- a/pyanaconda/exception.py
+++ b/pyanaconda/exception.py
@@ -37,7 +37,7 @@ import traceback
 import blivet.errors
 from pyanaconda.errors import CmdlineError
 from pyanaconda.ui.communication import hubQ
-from pyanaconda.constants import THREAD_EXCEPTION_HANDLING_TEST
+from pyanaconda.constants import THREAD_EXCEPTION_HANDLING_TEST, IPMI_FAILED
 from pyanaconda.threads import threadMgr
 from pyanaconda.i18n import _
 from pyanaconda import flags
@@ -179,6 +179,8 @@ class AnacondaExceptionHandler(ExceptionHandler):
         except:
             pass
 
+        iutil.ipmi_report(IPMI_FAILED)
+
     def runDebug(self, exc_info):
         if flags.can_touch_runtime_system("switch console") \
                 and self._intf_tty_num != 1:
diff --git a/pyanaconda/iutil.py b/pyanaconda/iutil.py
index e426511..1b71b06 100644
--- a/pyanaconda/iutil.py
+++ b/pyanaconda/iutil.py
@@ -1,7 +1,7 @@
 #
 # iutil.py - generic install utility functions
 #
-# Copyright (C) 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007
+# Copyright (C) 1999-2014
 # Red Hat, Inc.  All rights reserved.
 #
 # This program is free software; you can redistribute it and/or modify
@@ -28,6 +28,7 @@ import errno
 import subprocess
 import unicodedata
 import string
+import tempfile
 import types
 import re
 from threading import Thread
@@ -913,3 +914,27 @@ def persistent_root_image():
             return False
 
     return True
+
+_supports_ipmi = None
+
+def ipmi_report(event):
+    global _supports_ipmi
+    if _supports_ipmi is None:
+        _supports_ipmi = os.path.exists("/dev/ipmi0") and os.path.exists("/usr/bin/ipmitool")
+
+    if not _supports_ipmi:
+        return
+
+    (fd, path) = tempfile.mkstemp()
+
+    # EVM revision - always 0x4
+    # Sensor type - always 0x1F for Base OS Boot/Installation Status
+    # Sensor num - passed in event
+    # Event dir & type - always 0x0 for anaconda's purposes
+    # Event data 1, 2, 3 - 0x0 for now
+    os.write(fd, "0x4 0x1F %#x 0x0 0x0 0x0 0x0\n" % event)
+    os.close(fd)
+
+    execWithCapture("ipmitool", ["sel", "add", path])
+
+    os.remove(path)
diff --git a/pyanaconda/kickstart.py b/pyanaconda/kickstart.py
index b038f6e..6e4cb37 100644
--- a/pyanaconda/kickstart.py
+++ b/pyanaconda/kickstart.py
@@ -42,7 +42,7 @@ import os.path
 import tempfile
 import subprocess
 from pyanaconda.flags import flags, can_touch_runtime_system
-from pyanaconda.constants import ADDON_PATHS
+from pyanaconda.constants import ADDON_PATHS, IPMI_ABORTED
 import shlex
 import sys
 import urlgrabber
@@ -131,6 +131,7 @@ class AnacondaKSScript(KSScript):
                     err = "".join(fp.readlines())
 
                 errorHandler.cb(ScriptError(self.lineno, err))
+                iutil.ipmi_report(IPMI_ABORTED)
                 sys.exit(0)
 
 class AnacondaInternalScript(AnacondaKSScript):
@@ -1675,6 +1676,7 @@ class Upgrade(commands.upgrade.F20_Upgrade):
     def parse(self, *args):
         log.error("The upgrade kickstart command is no longer supported. Upgrade functionality is provided through fedup.")
         sys.stderr.write(_("The upgrade kickstart command is no longer supported. Upgrade functionality is provided through fedup."))
+        iutil.ipmi_report(IPMI_ABORTED)
         sys.exit(1)
 
 ###
@@ -1820,6 +1822,7 @@ def preScriptPass(f):
         # We do not have an interface here yet, so we cannot use our error
         # handling callback.
         print(e)
+        iutil.ipmi_report(IPMI_ABORTED)
         sys.exit(1)
 
     # run %pre scripts
@@ -1847,6 +1850,7 @@ def parseKickstart(f):
         # We do not have an interface here yet, so we cannot use our error
         # handling callback.
         print(e)
+        iutil.ipmi_report(IPMI_ABORTED)
         sys.exit(1)
 
     return handler
diff --git a/pyanaconda/packaging/dnfpayload.py b/pyanaconda/packaging/dnfpayload.py
index 4efc524..ec38a9a 100644
--- a/pyanaconda/packaging/dnfpayload.py
+++ b/pyanaconda/packaging/dnfpayload.py
@@ -31,7 +31,7 @@ import itertools
 import logging
 import multiprocessing
 import operator
-import pyanaconda.constants as constants
+from pyanaconda import constants
 from pykickstart.constants import GROUP_ALL, GROUP_DEFAULT, KS_MISSING_IGNORE
 import pyanaconda.errors as errors
 import pyanaconda.iutil
@@ -311,6 +311,7 @@ class DNFPayload(packaging.PackagePayload):
             # Doing a sys.exit also ensures the running thread quits before
             # it can do anything else.
             progressQ.send_quit(1)
+            pyanaconda.iutil.ipmi_report(constants.IPMI_ABORTED)
             sys.exit(1)
 
     def _pick_download_location(self):
diff --git a/pyanaconda/packaging/yumpayload.py b/pyanaconda/packaging/yumpayload.py
index 67f7a02..4d9f645 100644
--- a/pyanaconda/packaging/yumpayload.py
+++ b/pyanaconda/packaging/yumpayload.py
@@ -63,7 +63,7 @@ except ImportError:
     yum = None
 
 from pyanaconda.constants import BASE_REPO_NAME, DRACUT_ISODIR, INSTALL_TREE, ISO_DIR, MOUNT_DIR, \
-                                 LOGLVL_LOCK
+                                 LOGLVL_LOCK, IPMI_ABORTED
 from pyanaconda.flags import flags
 
 from pyanaconda import iutil
@@ -1424,6 +1424,7 @@ reposdir=%s
             exn = PayloadInstallError(str(e))
             if errorHandler.cb(exn) == ERROR_RAISE:
                 progressQ.send_quit(1)
+                iutil.ipmi_report(IPMI_ABORTED)
                 sys.exit(1)
         finally:
             # log the contents of the scriptlet logfile if any
@@ -1439,6 +1440,7 @@ reposdir=%s
             exn = PayloadInstallError("\n".join(install_errors))
             if errorHandler.cb(exn) == ERROR_RAISE:
                 progressQ.send_quit(1)
+                iutil.ipmi_report(IPMI_ABORTED)
                 sys.exit(1)
 
     def writeMultiLibConfig(self):
diff --git a/pyanaconda/ui/gui/__init__.py b/pyanaconda/ui/gui/__init__.py
index 3245d3e..117879b 100644
--- a/pyanaconda/ui/gui/__init__.py
+++ b/pyanaconda/ui/gui/__init__.py
@@ -26,7 +26,8 @@ from contextlib import contextmanager
 from gi.repository import Gdk, Gtk, AnacondaWidgets, Keybinder, GdkPixbuf
 
 from pyanaconda.i18n import _
-from pyanaconda import product
+from pyanaconda.constants import IPMI_ABORTED
+from pyanaconda import product, iutil
 
 from pyanaconda.ui import UserInterface, common
 from pyanaconda.ui.gui.utils import gtk_action_wait, busyCursor, unbusyCursor
@@ -707,6 +708,7 @@ class GraphicalUserInterface(UserInterface):
 
         if rc == 1:
             self._currentAction.exit_logger()
+            iutil.ipmi_report(IPMI_ABORTED)
             sys.exit(0)
 
 class GraphicalExceptionHandlingIface(meh.ui.gui.GraphicalIntf):
diff --git a/pyanaconda/ui/gui/hubs/progress.py b/pyanaconda/ui/gui/hubs/progress.py
index 2fa9dac..016d587 100644
--- a/pyanaconda/ui/gui/hubs/progress.py
+++ b/pyanaconda/ui/gui/hubs/progress.py
@@ -32,7 +32,8 @@ from pyanaconda.i18n import _, C_
 from pyanaconda.localization import find_best_locale_match
 from pyanaconda.product import productName
 from pyanaconda.flags import flags
-from pyanaconda.constants import THREAD_INSTALL, THREAD_CONFIGURATION, DEFAULT_LANG
+from pyanaconda import iutil
+from pyanaconda.constants import THREAD_INSTALL, THREAD_CONFIGURATION, DEFAULT_LANG, IPMI_FINISHED
 from pykickstart.constants import KS_SHUTDOWN, KS_REBOOT
 
 from pyanaconda.ui.gui.hubs import Hub
@@ -131,6 +132,8 @@ class ProgressHub(Hub):
         GLib.source_remove(self._rnotes_id)
         self._progressNotebook.set_current_page(1)
 
+        iutil.ipmi_report(IPMI_FINISHED)
+
         # kickstart install, continue automatically if reboot or shutdown selected
         if flags.automatedInstall and self.data.reboot.action in [KS_REBOOT, KS_SHUTDOWN]:
             self.window.emit("continue-clicked")
diff --git a/pyanaconda/ui/gui/spokes/software.py b/pyanaconda/ui/gui/spokes/software.py
index 4ca6378..2040bf2 100644
--- a/pyanaconda/ui/gui/spokes/software.py
+++ b/pyanaconda/ui/gui/spokes/software.py
@@ -25,7 +25,7 @@ from pyanaconda.flags import flags
 from pyanaconda.i18n import _, C_, CN_
 from pyanaconda.packaging import MetadataError, PackagePayload
 from pyanaconda.threads import threadMgr, AnacondaThread
-from pyanaconda import constants
+from pyanaconda import constants, iutil
 
 from pyanaconda.ui.communication import hubQ
 from pyanaconda.ui.gui.spokes import NormalSpoke
@@ -483,6 +483,7 @@ class SoftwareSelectionSpoke(NormalSpoke):
 
         if rc == 0:
             # Quit.
+            iutil.ipmi_report(constants.IPMI_ABORTED)
             sys.exit(0)
         elif rc == 1:
             # Send the user to the installation source spoke.
diff --git a/pyanaconda/ui/gui/spokes/storage.py b/pyanaconda/ui/gui/spokes/storage.py
index 33e15b3..c59188a 100644
--- a/pyanaconda/ui/gui/spokes/storage.py
+++ b/pyanaconda/ui/gui/spokes/storage.py
@@ -66,8 +66,7 @@ from pyanaconda.threads import threadMgr, AnacondaThread
 from pyanaconda.product import productName
 from pyanaconda.flags import flags
 from pyanaconda.i18n import _, C_, CN_, P_
-from pyanaconda import constants
-from pyanaconda import isys
+from pyanaconda import constants, iutil, isys
 from pyanaconda.bootloader import BootLoaderError
 
 from pykickstart.constants import CLEARPART_TYPE_NONE, AUTOPART_TYPE_LVM
@@ -919,6 +918,7 @@ class StorageSpoke(NormalSpoke, StorageChecker):
             if rc == 0:
                 # Quit.
                 sys.exit(0)
+                iutil.ipmi_report(constants.IPMI_ABORTED)
         elif self.warnings:
             label = _("The following warnings were encountered when checking your storage "
                       "configuration.  These are not fatal, but you may wish to make "
diff --git a/pyanaconda/ui/gui/spokes/welcome.py b/pyanaconda/ui/gui/spokes/welcome.py
index c04865a..76c06a8 100644
--- a/pyanaconda/ui/gui/spokes/welcome.py
+++ b/pyanaconda/ui/gui/spokes/welcome.py
@@ -36,8 +36,8 @@ from pyanaconda import keyboard
 from pyanaconda import flags
 from pyanaconda import geoloc
 from pyanaconda.i18n import _, C_
-from pyanaconda.iutil import is_unsupported_hw
-from pyanaconda.constants import DEFAULT_LANG, DEFAULT_KEYBOARD
+from pyanaconda.iutil import is_unsupported_hw, ipmi_report
+from pyanaconda.constants import DEFAULT_LANG, DEFAULT_KEYBOARD, IPMI_ABORTED
 
 import logging
 log = logging.getLogger("anaconda")
@@ -315,6 +315,7 @@ class WelcomeLanguageSpoke(LangLocaleHandler, StandaloneSpoke):
                 rc = dlg.run()
                 dlg.destroy()
             if rc != 1:
+                ipmi_report(IPMI_ABORTED)
                 sys.exit(0)
 
         if productName.startswith("Red Hat ") and \
@@ -324,6 +325,7 @@ class WelcomeLanguageSpoke(LangLocaleHandler, StandaloneSpoke):
                 rc = dlg.run()
                 dlg.destroy()
             if rc != 1:
+                ipmi_report(IPMI_ABORTED)
                 sys.exit(0)
 
         StandaloneSpoke._on_continue_clicked(self, window, user_data)
diff --git a/pyanaconda/ui/tui/simpleline/base.py b/pyanaconda/ui/tui/simpleline/base.py
index 1f839c9..572e512 100644
--- a/pyanaconda/ui/tui/simpleline/base.py
+++ b/pyanaconda/ui/tui/simpleline/base.py
@@ -28,7 +28,7 @@ import threading
 import functools
 from pyanaconda.threads import threadMgr, AnacondaThread
 from pyanaconda.ui.communication import hubQ
-from pyanaconda import constants
+from pyanaconda import constants, iutil
 from pyanaconda.i18n import _, N_, C_
 
 RAW_INPUT_LOCK = threading.Lock()
@@ -461,6 +461,8 @@ class App(object):
                 self.switch_screen_modal(d)
                 if d.answer:
                     raise ExitAllMainLoops()
+
+            iutil.ipmi_report(constants.IPMI_ABORTED)
             return True
 
         return False
diff --git a/pyanaconda/ui/tui/spokes/progress.py b/pyanaconda/ui/tui/spokes/progress.py
index 28376d3..92cb2f4 100644
--- a/pyanaconda/ui/tui/spokes/progress.py
+++ b/pyanaconda/ui/tui/spokes/progress.py
@@ -23,7 +23,8 @@ import sys
 
 from pyanaconda.flags import flags
 from pyanaconda.i18n import N_, _
-from pyanaconda.constants import THREAD_INSTALL, THREAD_CONFIGURATION
+from pyanaconda import iutil
+from pyanaconda.constants import THREAD_INSTALL, THREAD_CONFIGURATION, IPMI_FINISHED
 from pykickstart.constants import KS_SHUTDOWN, KS_REBOOT
 
 from pyanaconda.ui.tui.spokes import StandaloneTUISpoke
@@ -124,6 +125,8 @@ class ProgressSpoke(StandaloneTUISpoke):
         # This will run until we're all done with the configuration thread.
         self._update_progress()
 
+        iutil.ipmi_report(IPMI_FINISHED)
+
         # kickstart install, continue automatically if reboot or shutdown selected
         if flags.automatedInstall and self.data.reboot.action in [KS_REBOOT, KS_SHUTDOWN]:
             # Just pretend like we got input, and our input doesn't care
diff --git a/pyanaconda/vnc.py b/pyanaconda/vnc.py
index 2b567d4..16a3faf 100644
--- a/pyanaconda/vnc.py
+++ b/pyanaconda/vnc.py
@@ -21,7 +21,7 @@
 
 import os, sys
 import time
-from pyanaconda import network, product, iutil
+from pyanaconda import constants, network, product, iutil
 import socket
 import subprocess
 import dbus
@@ -170,6 +170,7 @@ class VncServer:
                 continue
             else:
                 log.critical(err)
+                iutil.ipmi_report(constants.IPMI_ABORTED)
                 sys.exit(1)
         self.log.error(P_("Giving up attempting to connect after %d try!\n",
                           "Giving up attempting to connect after %d tries!\n",
@@ -197,6 +198,7 @@ class VncServer:
             self.initialize()
         except (socket.herror, dbus.DBusException, ValueError) as e:
             stdoutLog.critical("Could not initialize the VNC server: %s", e)
+            iutil.ipmi_report(constants.IPMI_ABORTED)
             sys.exit(1)
 
         if self.password and (len(self.password) < 6 or len(self.password) > 8):
@@ -222,6 +224,7 @@ class VncServer:
             xvncp = subprocess.Popen(xvnccommand, stdout=self.openlogfile(), stderr=subprocess.STDOUT)
         except OSError:
             stdoutLog.critical("Could not start the VNC server.  Aborting.")
+            iutil.ipmi_report(constants.IPMI_ABORTED)
             sys.exit(1)
 
         # Lets give the xvnc time to initialize
@@ -229,6 +232,7 @@ class VncServer:
 
         # Make sure it hasn't blown up
         if xvncp.poll() != None:
+            iutil.ipmi_report(constants.IPMI_ABORTED)
             sys.exit(1)
         else:
             self.log.info(_("The VNC server is now running."))
@@ -247,6 +251,7 @@ class VncServer:
             self.log.warning(_("\n\nYou chose to execute vnc with a password. \n\n"))
         else:
             self.log.warning(_("\n\nUnknown Error.  Aborting. \n\n"))
+            iutil.ipmi_report(constants.IPMI_ABORTED)
             sys.exit(1)
 
         # Lets try to configure the vnc server to whatever the user specified
-- 
1.9.3



More information about the anaconda-patches mailing list