diff --git a/TODO b/TODO
index cc774508..2c246bc0 100644
--- a/TODO
+++ b/TODO
@@ -36,3 +36,7 @@ Traceback (most recent call last):
passvalue = pluginmap[plugpath].__dict__[operation](
KeyError: ''
+
+-reuse SDR to make health faster
+-have pyghmi and friends do multiprocessing pools (particularly the PBKDF stuff in auth)
+-bad password retrie
diff --git a/confluent/messages.py b/confluent/messages.py
index 1ded84b6..c6eaa25d 100644
--- a/confluent/messages.py
+++ b/confluent/messages.py
@@ -21,7 +21,27 @@ import confluent.exceptions as exc
import json
+def _htmlify_structure(indict):
+ ret = "
"
+ if isinstance(indict, dict):
+ for key in indict.iterkeys():
+ ret += "
{0}: ".format(key)
+ if type(indict[key]) in (str, unicode):
+ ret += indict[key]
+ else:
+ ret += _htmlify_structure(indict[key])
+ elif isinstance(indict, list):
+ if type(indict[0]) in (str, unicode):
+ ret += ",".join(indict)
+ else:
+ for v in indict:
+ ret += _htmlify_structure(v)
+ return ret + '
'
+
+
+
class ConfluentMessage(object):
+ readonly = False
defaultvalue = ''
defaulttype = 'text'
@@ -72,24 +92,31 @@ class ConfluentMessage(object):
value = '********'
if isinstance(val, list):
snippet += key + ":"
- if len(val) == 0:
+ if len(val) == 0 and not self.readonly:
snippet += (''
).format(type, key, self.desc)
for v in val:
- snippet += (''
- ).format(type, key, v, self.desc)
- snippet += (
- ''
- '').format(type, key, self.desc)
+ if self.readonly:
+ snippet += _htmlify_structure(v)
+ else:
+ snippet += (''
+ ).format(type, key, v, self.desc)
+ if not self.readonly:
+ snippet += (
+ ''
+ '').format(type, key, self.desc)
return snippet
- snippet += (key + ":" +
- ''
- ).format(type, key, value, self.desc)
+ if self.readonly:
+ snippet += "{0}: {1}".format(key, value)
+ else:
+ snippet += (key + ":" +
+ ''
+ ).format(type, key, value, self.desc)
if len(notes) > 0:
snippet += '(' + ','.join(notes) + ')'
return snippet
@@ -325,6 +352,45 @@ class PowerState(ConfluentChoiceMessage):
}
+class SensorReadings(ConfluentMessage):
+ readonly = True
+
+ def __init__(self, sensors=[], name=None):
+ readings = []
+ for sensor in sensors:
+ sensordict = {'name': sensor['name']}
+ if 'value' in sensor:
+ sensordict['value'] = sensor['value']
+ if 'units' in sensor:
+ sensordict['units'] = sensor['units']
+ if 'states' in sensor:
+ sensordict['states'] = sensor['states']
+ if 'health' in sensor:
+ sensordict['health'] = sensor['health']
+ readings.append(sensordict)
+ if name is None:
+ self.kvpairs = {'sensors': readings}
+ else:
+ self.kvpairs = {name: {'sensors': readings}}
+
+
+class HealthSummary(ConfluentMessage):
+ readonly = True
+ valid_values = set([
+ 'ok',
+ 'warning',
+ 'critical',
+ 'failed',
+ ])
+
+ def __init__(self, health, name=None):
+ if health not in self.valid_values:
+ raise ValueError("%d is not a valid health state" % health)
+ if name is None:
+ self.kvpairs = {'health': {'value': health}}
+ else:
+ self.kvpairs = {name: {'health': {'value': health}}}
+
class Attributes(ConfluentMessage):
def __init__(self, name=None, kv=None, desc=''):
self.desc = desc
diff --git a/confluent/pluginapi.py b/confluent/pluginapi.py
index 19ad6bec..49db747c 100644
--- a/confluent/pluginapi.py
+++ b/confluent/pluginapi.py
@@ -98,6 +98,12 @@ noderesources = {
'default': 'ipmi',
}),
},
+ 'health': {
+ 'hardware': PluginRoute({
+ 'pluginattrs': ['hardwaremanagement.method'],
+ 'default': 'ipmi',
+ }),
+ },
'boot': {
'nextdevice': PluginRoute({
'pluginattrs': ['hardwaremanagement.method'],
diff --git a/plugins/hardwaremanagement/ipmi.py b/plugins/hardwaremanagement/ipmi.py
index 5dad6416..505e4e63 100644
--- a/plugins/hardwaremanagement/ipmi.py
+++ b/plugins/hardwaremanagement/ipmi.py
@@ -12,7 +12,6 @@
# See the License for the specific language governing permissions and
# limitations under the License.
-import collections
import confluent.exceptions as exc
import confluent.interface.console as conapi
import confluent.messages as msg
@@ -21,7 +20,7 @@ import eventlet.event
import eventlet.green.threading as threading
import eventlet.greenpool as greenpool
import eventlet.queue
-import os
+import pyghmi.constants as pygconstants
import pyghmi.exceptions as pygexc
import pyghmi.ipmi.console as console
import pyghmi.ipmi.command as ipmicommand
@@ -156,6 +155,7 @@ class IpmiConsole(conapi.Console):
class IpmiIterator(object):
def __init__(self, operator, nodes, element, cfg, inputdata):
+ self.currdata = None
crypt = cfg.decrypt
cfg.decrypt = True
configdata = cfg.get_node_attributes(nodes, _configattributes)
@@ -168,10 +168,15 @@ class IpmiIterator(object):
return self
def next(self):
- ndata = self.gpile.next()
+ if self.currdata is None:
+ self.currdata = self.gpile.next()
# need to apply any translations between pyghmi and confluent
- return ndata
-
+ try:
+ retdata = self.currdata.next()
+ except AttributeError:
+ retdata = self.currdata
+ self.currdata = None
+ return retdata
def perform_request(operator, node, element, configdata, inputdata):
return IpmiHandler(operator, node, element, configdata, inputdata).handle_request()
@@ -220,6 +225,41 @@ class IpmiHandler(object):
return self.power()
elif self.element == [ 'boot', 'nextdevice' ]:
return self.bootdevice()
+ elif self.element == [ 'health', 'hardware' ]:
+ return self.health()
+
+ def _str_health(self, health):
+ if pygconstants.Health.Failed & health:
+ health = 'failed'
+ elif pygconstants.Health.Critical & health:
+ health = 'critical'
+ elif pygconstants.Health.Warning & health:
+ health = 'warning'
+ else:
+ health = 'ok'
+ return health
+
+ def _dict_sensor(self, pygreading):
+ retdict = {}
+ retdict['name'] = pygreading.name
+ retdict['value'] = pygreading.value
+ retdict['states'] = pygreading.states
+ retdict['health'] = self._str_health(pygreading.health)
+ return retdict
+
+ def health(self):
+ if 'read' == self.op:
+ response = self.ipmicmd.get_health()
+ health = response['health']
+ health = self._str_health(health)
+ yield msg.HealthSummary(health, self.node)
+ if 'badreadings' in response:
+ badsensors = []
+ for reading in response['badreadings']:
+ badsensors.append(self._dict_sensor(reading))
+ yield msg.SensorReadings(badsensors, name=self.node)
+ else:
+ raise exc.InvalidArgumentException('health is read-only')
def bootdevice(self):
if 'read' == self.op:
@@ -275,4 +315,3 @@ def update(nodes, element, configmanager, inputdata):
def retrieve(nodes, element, configmanager, inputdata):
initthread()
return IpmiIterator('read', nodes, element, configmanager, inputdata)
-