diff --git a/confluent_server/VERSION b/confluent_server/VERSION
index 9459d4ba..e173b1a0 100644
--- a/confluent_server/VERSION
+++ b/confluent_server/VERSION
@@ -1 +1 @@
-1.1
+1.1.post1
diff --git a/confluent_server/confluent/core.py b/confluent_server/confluent/core.py
index 837fb296..8c27e360 100644
--- a/confluent_server/confluent/core.py
+++ b/confluent_server/confluent/core.py
@@ -117,6 +117,14 @@ noderesources = {
# this is a dummy value, http or socket must handle special
'session': PluginRoute({}),
},
+ 'events': {
+ 'hardware': {
+ 'log': PluginRoute({
+ 'pluginattrs': ['hardwaremanagement.method'],
+ 'default': 'ipmi',
+ })
+ }
+ },
'power': {
'state': PluginRoute({
'pluginattrs': ['hardwaremanagement.method'],
diff --git a/confluent_server/confluent/messages.py b/confluent_server/confluent/messages.py
index 03175cc3..5a8c24cb 100644
--- a/confluent_server/confluent/messages.py
+++ b/confluent_server/confluent/messages.py
@@ -21,6 +21,13 @@
import confluent.exceptions as exc
import json
+valid_health_values = set([
+ 'ok',
+ 'warning',
+ 'critical',
+ 'failed',
+ 'unknown',
+])
def _htmlify_structure(indict):
ret = "
"
@@ -527,6 +534,44 @@ class PowerState(ConfluentChoiceMessage):
keyname = 'state'
+class EventCollection(ConfluentMessage):
+ """A collection of events
+
+ This conveys a representation of an iterable of events. The following
+ fields are supported:
+ id (some data giving the class of event without the specific data of the
+ event. For example, 'overtemp (1000 degrees celsius)' would have
+ the same 'id' as 'overtemp (200 degrees celsius)
+ component (specific name of the component this event references if any)
+ component_type (A description of the sort of device component is)
+ event (A text description of the event that occurred)
+ severity (The text 'ok', 'warning', 'critical', 'failed', or 'unknown')
+ timestamp (ISO 8601 compliant timestamp if available)
+ """
+ readonly = True
+
+ def __init__(self, events=(), name=None):
+ eventdata = []
+ self.notnode = name is None
+ for event in events:
+ entry = {
+ 'id': event.get('id', None),
+ 'component': event.get('component', None),
+ 'component_type': event.get('component_type', None),
+ 'event': event.get('event', None),
+ 'severity': event['severity'],
+ 'timestamp': event.get('timestamp', None),
+ }
+ if event['severity'] not in valid_health_values:
+ raise exc.NotImplementedException(
+ 'Invalid severity - ' + repr(event['severity']))
+ eventdata.append(entry)
+ if self.notnode:
+ self.kvpairs = {'events': eventdata}
+ else:
+ self.kvpairs = {name: {'events': eventdata}}
+
+
class SensorReadings(ConfluentMessage):
readonly = True
@@ -562,12 +607,7 @@ class KeyValueData(ConfluentMessage):
class HealthSummary(ConfluentMessage):
readonly = True
- valid_values = set([
- 'ok',
- 'warning',
- 'critical',
- 'failed',
- ])
+ valid_values = valid_health_values
def __init__(self, health, name=None):
self.stripped = False
diff --git a/confluent_server/confluent/plugins/hardwaremanagement/ipmi.py b/confluent_server/confluent/plugins/hardwaremanagement/ipmi.py
index b6f7423b..56258260 100644
--- a/confluent_server/confluent/plugins/hardwaremanagement/ipmi.py
+++ b/confluent_server/confluent/plugins/hardwaremanagement/ipmi.py
@@ -79,8 +79,6 @@ def sanitize_invdata(indata):
format(x, '02x') for x in indata[k][idx])
-
-
class IpmiCommandWrapper(ipmicommand.Command):
def __init__(self, node, cfm, **kwargs):
self._attribwatcher = cfm.watch_attributes(
@@ -262,7 +260,8 @@ def perform_request(operator, node, element,
results.put(msg.ConfluentTargetTimeout(node, str(tu)))
except Exception as e:
results.put(msg.ConfluentNodeError(
- node, 'IPMI PluginException: ' + str(e)))
+ node, 'IPMI PluginException (see stderr log): ' + str(e)))
+ raise
finally:
results.put('Done')
@@ -327,8 +326,8 @@ class IpmiHandler(object):
self._logevt = None
if self.broken:
if (self.error == 'timeout' or
- 'Insufficient resources' in self.error):
- self.error = self.error.replace(' reported in RAKP4','')
+ 'Insufficient resources' in self.error):
+ self.error = self.error.replace(' reported in RAKP4', '')
self.output.put(msg.ConfluentTargetTimeout(
self.node, self.error))
return
@@ -351,6 +350,22 @@ class IpmiHandler(object):
self.handle_sensors()
elif self.element[0] == 'inventory':
self.handle_inventory()
+ elif self.element == ['events', 'hardware', 'log']:
+ self.do_eventlog()
+ else:
+ raise Exception('Not Implemented')
+
+ def do_eventlog(self):
+ eventout = []
+ for event in self.ipmicmd.get_event_log():
+ event['severity'] = _str_health(event['severity'])
+ if 'event_data' in event:
+ event['event'] = '{0} - {1}'.format(
+ event['event'], event['event_data'])
+ event['id'] = '{0}.{1}'.format(event['event_id'],
+ event['component_type_id'])
+ eventout.append(event)
+ self.output.put(msg.EventCollection(eventout, name=self.node))
def make_inventory_map(self):
invnames = self.ipmicmd.get_inventory_descriptions()