diff --git a/confluent_server/confluent/plugins/hardwaremanagement/geist.py b/confluent_server/confluent/plugins/hardwaremanagement/geist.py index 3af6fa49..3f086115 100644 --- a/confluent_server/confluent/plugins/hardwaremanagement/geist.py +++ b/confluent_server/confluent/plugins/hardwaremanagement/geist.py @@ -1,13 +1,13 @@ # Copyright 2022 Lenovo # -# Licensed under the Apache License, Version 2.0 (the "License"); +# Licensed under the Apache License, Version 2.0 (the 'License'); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, +# distributed under the License is distributed on an 'AS IS' BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. @@ -20,12 +20,15 @@ import eventlet.green.time as time import eventlet import eventlet.greenpool as greenpool + + def simplify_name(name): - return name.lower().replace(' ', '_').replace('/', '-').replace( - '_-_', '-') + return name.lower().replace(' ', '_').replace('/', '-').replace('_-_', '-') + pdupool = greenpool.GreenPool(128) + def data_by_type(indata): databytype = {} for keyname in indata: @@ -34,7 +37,9 @@ def data_by_type(indata): if not objtype: continue if objtype in databytype: - raise Exception("Multiple instances of type {} not yet supported".format(objtype)) + raise Exception( + 'Multiple instances of type {} not yet supported'.format(objtype) + ) databytype[objtype] = obj obj['keyname'] = keyname return databytype @@ -58,31 +63,30 @@ class GeistClient(object): def wc(self): if self._wc: return self._wc - targcfg = self.configmanager.get_node_attributes(self.node, - ['hardwaremanagement.manager'], - decrypt=True) + targcfg = self.configmanager.get_node_attributes( + self.node, ['hardwaremanagement.manager'], decrypt=True + ) targcfg = targcfg.get(self.node, {}) - target = targcfg.get( - 'hardwaremanagement.manager', {}).get('value', None) + target = targcfg.get('hardwaremanagement.manager', {}).get('value', None) if not target: target = self.node target = target.split('/', 1)[0] cv = util.TLSCertVerifier( - self.configmanager, self.node, - 'pubkeys.tls_hardwaremanager').verify_cert + self.configmanager, self.node, 'pubkeys.tls_hardwaremanager' + ).verify_cert self._wc = wc.SecureHTTPConnection(target, port=443, verifycallback=cv) return self._wc def login(self, configmanager): - credcfg = configmanager.get_node_attributes(self.node, - ['secret.hardwaremanagementuser', - 'secret.hardwaremanagementpassword'], - decrypt=True) + credcfg = configmanager.get_node_attributes( + self.node, + ['secret.hardwaremanagementuser', 'secret.hardwaremanagementpassword'], + decrypt=True, + ) credcfg = credcfg.get(self.node, {}) - username = credcfg.get( - 'secret.hardwaremanagementuser', {}).get('value', None) - passwd = credcfg.get( - 'secret.hardwaremanagementpassword', {}).get('value', None) + username = credcfg.get('secret.hardwaremanagementuser', {}).get('value', None) + passwd = credcfg.get('secret.hardwaremanagementpassword', {}).get('value', None) + if not isinstance(username, str): username = username.decode('utf8') if not isinstance(passwd, str): @@ -92,26 +96,32 @@ class GeistClient(object): self.username = username rsp = self.wc.grab_json_response( '/api/auth/{0}'.format(username), - {'cmd': 'login', 'data': {'password': passwd}}) + {'cmd': 'login', 'data': {'password': passwd}}, + ) + token = rsp['data']['token'] return token def logout(self): if self._token: - self.wc.grab_json_response('/api/auth/{0}'.format(self.username), - {'cmd': 'logout', 'token': self.token}) + self.wc.grab_json_response( + '/api/auth/{0}'.format(self.username), + {'cmd': 'logout', 'token': self.token}, + ) self._token = None def get_outlet(self, outlet): rsp = self.wc.grab_json_response('/api/dev') rsp = rsp['data'] dbt = data_by_type(rsp) + if 't3hd' in dbt: del dbt['t3hd'] if len(dbt) != 1: raise Exception('Multiple PDUs not supported per pdu') pdutype = list(dbt)[0] outlet = dbt[pdutype]['outlet'][str(int(outlet) - 1)] + state = outlet['state'].split('2')[-1] return state @@ -125,12 +135,20 @@ class GeistClient(object): raise Exception('Multiple PDUs per endpoint not supported') pdu = dbt[list(dbt)[0]]['keyname'] outlet = int(outlet) - 1 + rsp = self.wc.grab_json_response( '/api/dev/{0}/outlet/{1}'.format(pdu, outlet), - {'cmd': 'control', 'token': self.token, - 'data': {'action': state, 'delay': False}}) + { + 'cmd': 'control', + 'token': self.token, + 'data': {'action': state, 'delay': False}, + }, + ) -def process_measurement(keyname, name, enttype, entname, measurement, readings, category): + +def process_measurement( + keyname, name, enttype, entname, measurement, readings, category +): if measurement['type'] == 'realPower': if category not in ('all', 'power'): return @@ -147,6 +165,10 @@ def process_measurement(keyname, name, enttype, entname, measurement, readings, if category not in ('all',): return readtype = 'Voltage' + elif measurement['type'] == 'current': + if category not in ('all',): + return + readtype = 'Current' elif measurement['type'] == 'temperature': readtype = 'Temperature' elif measurement['type'] == 'dewpoint': @@ -158,23 +180,35 @@ def process_measurement(keyname, name, enttype, entname, measurement, readings, myname = entname + ' ' + readtype if name != 'all' and simplify_name(myname) != name: return - readings.append({ - 'name': myname, - 'value': float(measurement['value']), - 'units': measurement['units'], - 'type': readtype.split()[-1] - }) - + readings.append( + { + 'name': myname, + 'value': float(measurement['value']), + 'units': measurement['units'], + 'type': readtype.split()[-1], + } + ) + def process_measurements(name, category, measurements, enttype, readings): for measure in util.natural_sort(list(measurements)): measurement = measurements[measure]['measurement'] entname = measurements[measure]['name'] for measureid in measurement: - process_measurement(measure, name, enttype, entname, measurement[measureid], readings, category) - + process_measurement( + measure, + name, + enttype, + entname, + measurement[measureid], + readings, + category, + ) + _sensors_by_node = {} + + def read_sensors(element, node, configmanager): category, name = element[-2:] justnames = False @@ -192,10 +226,12 @@ def read_sensors(element, node, configmanager): _sensors_by_node[node] = (adev, time.time() + 1) sn = _sensors_by_node.get(node, None) dbt = data_by_type(sn[0]['data']) + readings = [] - for datatype in dbt: + for datatype in dbt: datum = dbt[datatype] process_measurements(name, category, datum['entity'], 'entity', readings) + if 'outlet' in datum: process_measurements(name, category, datum['outlet'], 'outlet', readings) if justnames: @@ -204,25 +240,78 @@ def read_sensors(element, node, configmanager): else: yield msg.SensorReadings(readings, name=node) -def get_outlet(node, configmanager, element): + +def get_outlet(element, node, configmanager): gc = GeistClient(node, configmanager) state = gc.get_outlet(element[-1]) + return msg.PowerState(node=node, state=state) + def read_firmware(node, configmanager): gc = GeistClient(node, configmanager) adev = gc.wc.grab_json_response('/api/sys') myversion = adev['data']['version'] yield msg.Firmware([{'PDU Firmware': {'version': myversion}}], node) + +def read_inventory(element, node, configmanager): + _inventory = {} + inventory = {} + gc = GeistClient(node, configmanager) + adev = gc.wc.grab_json_response('/api/sys') + basedata = adev['data'] + inventory['present'] = True + inventory['name'] = 'PDU' + for elem in basedata.items(): + + if ( + elem[0] != 'component' + and elem[0] != 'locale' + and elem[0] != 'state' + and elem[0] != 'contact' + and elem[0] != 'appVersion' + and elem[0] != 'build' + and elem[0] != 'version' + and elem[0] != 'apiVersion' + ): + temp = elem[0] + if elem[0] == 'serialNumber': + temp = 'Serial' + elif elem[0] == 'partNumber': + temp = 'P/N' + elif elem[0] == 'modelNumber': + temp = 'Lenovo P/N and Serial' + _inventory[temp] = elem[1] + elif elem[0] == 'component': + tempname = '' + for component in basedata['component'].items(): + for item in component: + if type(item) == str: + + tempname = item + else: + for entry in item.items(): + temp = entry[0] + if temp == 'sn': + temp = 'Serial' + _inventory[tempname + ' ' + temp] = entry[1] + + inventory['information'] = _inventory + + yield msg.KeyValueData({'inventory': [inventory]}, node) + + def retrieve(nodes, element, configmanager, inputdata): + if 'outlets' in element: gp = greenpool.GreenPile(pdupool) for node in nodes: - gp.spawn(get_outlet, node, configmanager, element) + + gp.spawn(get_outlet, element, node, configmanager) for res in gp: yield res - + return elif element[0] == 'sensors': gp = greenpool.GreenPile(pdupool) @@ -239,11 +328,20 @@ def retrieve(nodes, element, configmanager, inputdata): for rsp in gp: for datum in rsp: yield datum + + elif '/'.join(element).startswith('inventory/hardware/all'): + gp = greenpool.GreenPile(pdupool) + for node in nodes: + gp.spawn(read_inventory, element, node, configmanager) + for rsp in gp: + for datum in rsp: + yield datum else: for node in nodes: - yield msg.ConfluentResourceUnavailable(node, 'Not implemented') + yield msg.ConfluentResourceUnavailable(node, 'Not implemented') return - + + def update(nodes, element, configmanager, inputdata): if 'outlets' not in element: yield msg.ConfluentResourceUnavailable(node, 'Not implemented')