diff --git a/confluent_server/confluent/core.py b/confluent_server/confluent/core.py index 0cb320dc..9dcd3d36 100644 --- a/confluent_server/confluent/core.py +++ b/confluent_server/confluent/core.py @@ -42,11 +42,20 @@ import sys pluginmap = {} +def seek_element(currplace, currkey): + try: + return currplace[currkey] + except TypeError: + if isinstance(currplace, PluginCollection): + # we hit a plugin curated collection, all children + # are up to the plugin to comprehend + return currplace + raise def nested_lookup(nestdict, key): try: - return reduce(dict.__getitem__, key, nestdict) - except TypeError: + return reduce(seek_element, key, nestdict) + except TypeError as e: raise exc.NotFoundException("Invalid element requested") @@ -86,6 +95,10 @@ class PluginRoute(object): def __init__(self, routedict): self.routeinfo = routedict +class PluginCollection(object): + def __init__(self, routedict): + self.routeinfo = routedict + # _ prefix indicates internal use (e.g. special console scheme) and should not # be enumerated in any collection noderesources = { @@ -124,6 +137,26 @@ noderesources = { 'all': PluginRoute({'handler': 'attributes'}), 'current': PluginRoute({'handler': 'attributes'}), }, + 'sensors': { + 'hardware': { + 'all': PluginCollection({ + 'pluginattrs': ['hardwaremanagement.method'], + 'default': 'ipmi', + }), + 'temperature': PluginCollection({ + 'pluginattrs': ['hardwaremanagement.method'], + 'default': 'ipmi', + }), + 'power': PluginCollection({ + 'pluginattrs': ['hardwaremanagement.method'], + 'default': 'ipmi', + }), + 'fans': PluginCollection({ + 'pluginattrs': ['hardwaremanagement.method'], + 'default': 'ipmi', + }), + }, + }, } nodegroupresources = { @@ -336,6 +369,8 @@ def handle_path(path, operation, configmanager, inputdata=None): raise exc.NotFoundException("Invalid element requested") if isinstance(routespec, dict): iscollection = True + elif isinstance(routespec, PluginCollection): + iscollection = False # it is a collection, but plugin defined if iscollection: if operation == "delete": return delete_node_collection(pathcomponents, configmanager) diff --git a/confluent_server/confluent/messages.py b/confluent_server/confluent/messages.py index 206f4edc..55e356f1 100644 --- a/confluent_server/confluent/messages.py +++ b/confluent_server/confluent/messages.py @@ -61,7 +61,8 @@ class ConfluentMessage(object): return self.kvpairs def strip_node(self, node): - self.kvpairs = self.kvpairs[node] + if self.kvpairs is not None: + self.kvpairs = self.kvpairs[node] def html(self, extension=''): #this is used to facilitate the api explorer feature @@ -199,10 +200,13 @@ class ConfluentChoiceMessage(ConfluentMessage): class LinkRelation(ConfluentMessage): + kvpairs = None + def __init__(self): self.href = '' self.rel = '' + def json(self): """Provide json_hal style representation of the relation. diff --git a/confluent_server/confluent/plugins/hardwaremanagement/ipmi.py b/confluent_server/confluent/plugins/hardwaremanagement/ipmi.py index 98950df3..14b4ae16 100644 --- a/confluent_server/confluent/plugins/hardwaremanagement/ipmi.py +++ b/confluent_server/confluent/plugins/hardwaremanagement/ipmi.py @@ -32,6 +32,15 @@ console.session.threading = eventlet.green.threading _ipmithread = None _ipmiwaiters = [] +sensor_categories = { + 'temperature': frozenset(['Temperature']), + 'power': frozenset(['Current', 'Battery']), + 'fans': frozenset(['Fan', 'Cooling Device']), +} + +def simplify_name(name): + return name.lower().replace(' ', '_') + class IpmiCommandWrapper(ipmicommand.Command): def __init__(self, node, cfm, **kwargs): @@ -222,6 +231,7 @@ persistent_ipmicmds = {} class IpmiHandler(object): def __init__(self, operation, node, element, cfd, inputdata, cfg): + self.sensormap = {} self.broken = False self.error = None eventlet.sleep(0) @@ -279,6 +289,35 @@ class IpmiHandler(object): return self.health() elif self.element == ['identify']: return self.identify() + elif self.element[0] == 'sensors': + return self.handle_sensors() + + def make_sensor_map(self, sensors=None): + if sensors is None: + sensors = self.ipmicmd.get_sensor_descriptions() + for sensor in sensors: + resourcename = sensor['name'] + self.sensormap[simplify_name(resourcename)] = resourcename + + + def handle_sensors(self): + if self.element[-1] == '': + self.element = self.element[:-1] + if len(self.element) == 3: # list sensors per category + category = self.element[2] + return self.list_sensors(category) + + def list_sensors(self, category): + sensors = self.ipmicmd.get_sensor_descriptions() + yield msg.ChildCollection('all') + if category == 'all': + for sensor in sensors: + yield msg.ChildCollection(simplify_name(sensor['name'])) + else: + for sensor in sensors: + if sensor['type'] in sensor_categories[category]: + yield msg.ChildCollection(simplify_name(sensor['name'])) + @staticmethod def _str_health(health):