2
0
mirror of https://github.com/xcat2/confluent.git synced 2026-04-27 19:07:46 +00:00
Files
confluent/confluent_server/confluent/plugins/hardwaremanagement/geist.py
weragrzeda f633c93d0f Geit PDU nodeinvntory
Please enter the commit message for your changes. Lines starting
 with '' will be ignored, and an empty message aborts the commit.

 On branch master
 Your branch is up to date with 'origin/master'.

 Changes to be committed:
	modified:   geist.py
2023-08-25 08:27:27 +02:00

326 lines
10 KiB
Python

# Copyright 2022 Lenovo
#
# 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,
# 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.
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
import eventlet.greenpool as greenpool
def simplify_name(name):
return name.lower().replace(" ", "_").replace("/", "-").replace("_-_", "-")
pdupool = greenpool.GreenPool(128)
def data_by_type(indata):
databytype = {}
for keyname in indata:
obj = indata[keyname]
objtype = obj.get("type", None)
if not objtype:
continue
if objtype in databytype:
raise Exception(
"Multiple instances of type {} not yet supported".format(objtype)
)
databytype[objtype] = obj
obj["keyname"] = keyname
return databytype
class GeistClient(object):
def __init__(self, pdu, configmanager):
self.node = pdu
self.configmanager = configmanager
self._token = None
self._wc = None
self.username = None
@property
def token(self):
if not self._token:
self._token = self.login(self.configmanager)
return self._token
@property
def wc(self):
if self._wc:
return self._wc
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)
if not target:
target = self.node
target = target.split("/", 1)[0]
cv = util.TLSCertVerifier(
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 = credcfg.get(self.node, {})
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):
passwd = passwd.decode("utf8")
if not username or not passwd:
raise Exception("Missing username or password")
self.username = username
rsp = self.wc.grab_json_response(
"/api/auth/{0}".format(username),
{"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._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
def set_outlet(self, outlet, state):
rsp = self.wc.grab_json_response("/api/dev")
dbt = data_by_type(rsp["data"])
if "t3hd" in dbt:
del dbt["t3hd"]
if len(dbt) != 1:
self.logout()
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},
},
)
def process_measurement(
keyname, name, enttype, entname, measurement, readings, category
):
if measurement["type"] == "realPower":
if category not in ("all", "power"):
return
readtype = "Real Power"
elif measurement["type"] == "apparentPower":
if category not in ("all", "power"):
return
readtype = "Apparent Power"
elif measurement["type"] == "energy":
if category not in ("all", "energy"):
return
readtype = "Energy"
elif measurement["type"] == "voltage":
if category not in ("all",):
return
readtype = "Voltage"
elif measurement["type"] == "temperature":
readtype = "Temperature"
elif measurement["type"] == "dewpoint":
readtype = "Dewpoint"
elif measurement["type"] == "humidity":
readtype = "Humidity"
else:
return
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],
}
)
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,
)
_sensors_by_node = {}
def read_sensors(element, node, configmanager):
category, name = element[-2:]
justnames = False
if len(element) == 3:
# just get names
category = name
name = "all"
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)
dbt = data_by_type(sn[0]["data"])
readings = []
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:
for reading in readings:
yield msg.ChildCollection(simplify_name(reading["name"]))
else:
yield msg.SensorReadings(readings, name=node)
def get_outlet(node, configmanager, element):
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] !='name':
_inventory[elem[0]] = 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():
_inventory[tempname + " " + entry[0]] = 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, element, 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
elif "/".join(element).startswith("inventory/firmware/all"):
gp = greenpool.GreenPile(pdupool)
for node in nodes:
gp.spawn(read_firmware, node, configmanager)
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")
return
def update(nodes, element, configmanager, inputdata):
if "outlets" not in element:
yield msg.ConfluentResourceUnavailable(node, "Not implemented")
return
for node in nodes:
gc = GeistClient(node, configmanager)
newstate = inputdata.powerstate(node)
gc.set_outlet(element[-1], newstate)
eventlet.sleep(1)
for res in retrieve(nodes, element, configmanager, inputdata):
yield res