[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