diff --git a/confluent_server/confluent/plugins/hardwaremanagement/geist.py b/confluent_server/confluent/plugins/hardwaremanagement/geist.py index fc9116b1..48245b17 100644 --- a/confluent_server/confluent/plugins/hardwaremanagement/geist.py +++ b/confluent_server/confluent/plugins/hardwaremanagement/geist.py @@ -16,7 +16,14 @@ import pyghmi.util.webclient as wc import confluent.util as util import confluent.messages as msg import confluent.exceptions as exc +import eventlet.green.time as time +import eventlet.greenpool as greenpool +def simplify_name(name): + return name.lower().replace(' ', '_').replace('/', '-').replace( + '_-_', '-') + +pdupool = greenpool.GreenPool(128) class GeistClient(object): def __init__(self, pdu, configmanager): @@ -102,17 +109,160 @@ class GeistClient(object): {'cmd': 'control', 'token': self.token, 'data': {'action': state, 'delay': False}}) +_sensors_by_node = {} +def read_sensors(element, node, configmanager): + category, name = element[-2:] + justnames = False + if len(element) == 3: + # just get names + category = name + justnames = True + if category in ('leds, fans', 'temperature'): + return + sn = _sensors_by_node.get(node, None) + if not sn or sn[1] < time.time(): + gc = GeistClient(node, configmanager) + adev = gc.wc.grab_json_response('/api/dev') + _sensors_by_node[node] = (adev, time.time() + 1) + sn = _sensors_by_node.get(node, None) + if len(sn[0]['data']) != 1: + raise Exception('Unable to support multiple pdus at an ip') + print(repr(element)) + readings = [] + totalenergy = 0.0 + totalrealpower = 0.0 + totalapparentpower = 0.0 + for pduid in sn[0]['data']: + datum = sn[0]['data'][pduid] + for ent in util.natural_sort(list(datum['entity'])): + outsensors = datum['entity'][ent]['measurement'] + if ent.startswith('breaker'): + continue + if ent == 'total0': + continue + for outlet in util.natural_sort(list(datum['outlet'])): + outsensors = datum['outlet'][outlet]['measurement'] + for measure in outsensors: + measurement = outsensors[measure] + if measurement['type'] == 'energy' and category != 'power': + myname = 'Outlet {0} Energy'.format(int(outlet) + 1) + if justnames: + yield msg.ChildCollection(simplify_name(myname)) + continue + totalenergy += float(measurement['value']) + if name != 'all' and simplify_name(myname) != name: + continue + reading = { + 'name': myname, + 'value': float(measurement['value']), + 'units': measurement['units'], + 'type': 'Energy' + } + readings.append(reading) + if measurement['type'] == 'realPower' and category != 'energy': + myname = 'Outlet {0} Real Power'.format(int(outlet) + 1) + if justnames: + yield msg.ChildCollection(simplify_name(myname)) + continue + totalrealpower += float(measurement['value']) + if name != 'all' and simplify_name(myname) != name: + continue + reading = { + 'name': myname, + 'value': float(measurement['value']), + 'units': measurement['units'], + 'type': 'Current' + } + readings.append(reading) + if measurement['type'] == 'voltage' and category == 'all': + myname = 'Outlet {0} Voltage'.format(int(outlet) + 1) + if justnames: + yield msg.ChildCollection(simplify_name(myname)) + continue + if name != 'all' and simplify_name(myname) != name: + continue + reading = { + 'name': myname, + 'value': float(measurement['value']), + 'units': measurement['units'], + 'type': 'Voltage' + } + readings.append(reading) + if measurement['type'] == 'apparentPower' and category != 'energy': + myname = 'Outlet {0} Apparent Power'.format(int(outlet) + 1) + if justnames: + yield msg.ChildCollection(simplify_name(myname)) + continue + totalapparentpower += float(measurement['value']) + if name != 'all' and simplify_name(myname) != name: + continue + reading = { + 'name': myname, + 'value': float(measurement['value']), + 'units': measurement['units'], + 'type': 'Current' + } + readings.append(reading) + myname = 'Overall Energy' + if justnames and category != 'power': + yield msg.ChildCollection(simplify_name(myname)) + elif (name == 'all' or simplify_name(myname) == name) and category != 'power': + readings.append({ + 'name': 'Overall Energy', + 'value': totalenergy, + 'units': 'kWh', + 'type': 'Energy', + }) + myname = 'Overall Real Power' + if justnames and category != 'energy': + yield msg.ChildCollection(simplify_name(myname)) + elif (name == 'all' or simplify_name(myname) == name) and category != 'energy': + readings.append({ + 'name': 'Overall Real Power', + 'value': totalrealpower, + 'units': 'W', + 'type': 'Current', + }) + myname = 'Overall Apparent Power' + if justnames and category != 'energy': + yield msg.ChildCollection(simplify_name(myname)) + elif (name == 'all' or simplify_name(myname) == name) and category != 'energy': + readings.append({ + 'name': 'Overall Apparent Power', + 'value': totalapparentpower, + 'units': 'VA', + 'type': 'Current', + }) + if readings: + yield msg.SensorReadings(readings, name=node) + +def get_outlet(node, configmanager): + gc = GeistClient(node, configmanager) + state = gc.get_outlet(element[-1]) + return msg.PowerState(node=node, state=state) def retrieve(nodes, element, configmanager, inputdata): - if 'outlets' not in element: + if 'outlets' in element: + gp = greenpool.GreenPile(pdupool) + for node in nodes: + gp.spawn(get_outlet, node, configmanager) + for res in gp: + yield res + + return + elif element[0] == 'sensors': + gp = greenpool.GreenPile(pdupool) + for node in nodes: + gp.spawn(read_sensors, element, node, configmanager) + for rsp in gp: + for datum in rsp: + yield datum + return + else: for node in nodes: yield msg.ConfluentResourceUnavailable(node, 'Not implemented') return - for node in nodes: - gc = GeistClient(node, configmanager) - state = gc.get_outlet(element[-1]) - yield msg.PowerState(node=node, state=state) - + def update(nodes, element, configmanager, inputdata): if 'outlets' not in element: yield msg.ConfluentResourceUnavailable(node, 'Not implemented')