mirror of
https://opendev.org/x/pyghmi
synced 2025-01-27 19:37:44 +00:00
Add support for Lenovo Energy Meters
Energy meters are available through OEM commands for Lenovo enterprise servers. This enables those data to appear as sensors when enumerated. Change-Id: I28d59b24cf49642e82d5b71f7c34f94d858796dd
This commit is contained in:
parent
9131df4ced
commit
243cf797de
81
pyghmi/ipmi/oem/lenovo/energy.py
Normal file
81
pyghmi/ipmi/oem/lenovo/energy.py
Normal file
@ -0,0 +1,81 @@
|
||||
# vim: tabstop=4 shiftwidth=4 softtabstop=4
|
||||
|
||||
# Copyright 2017 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.exceptions as pygexc
|
||||
import struct
|
||||
|
||||
|
||||
class EnergyManager(object):
|
||||
|
||||
def __init__(self, ipmicmd):
|
||||
# there are two IANA possible for the command set, start with
|
||||
# the Lenovo, then fallback to IBM
|
||||
# We start with a 'find firmware instance' to test the water and
|
||||
# get the handle (which has always been the same, but just in case
|
||||
self.iana = bytearray('\x66\x4a\x00')
|
||||
try:
|
||||
rsp = ipmicmd.xraw_command(netfn=0x2e, command=0x82,
|
||||
data=self.iana + '\x00\x00\x01')
|
||||
except pygexc.IpmiException as ie:
|
||||
if ie.ipmicode == 193: # try again with IBM IANA
|
||||
self.iana = bytearray('\x4d\x4f\x00')
|
||||
rsp = ipmicmd.xraw_command(netfn=0x2e, command=0x82,
|
||||
data=self.iana + '\x00\x00\x01')
|
||||
else:
|
||||
raise
|
||||
if rsp['data'][4:6] not in (b'\x02\x01', b'\x02\x06', b'\x02\x09'):
|
||||
raise pygexc.UnsupportedFunctionality(
|
||||
"Energy Control {0}.{1} not recognized".format(rsp['data'][4],
|
||||
rsp['data'][5]))
|
||||
self.modhandle = bytearray(rsp['data'][6:7])
|
||||
if self.get_ac_energy(ipmicmd):
|
||||
self.supportedmeters = ('AC Energy', 'DC Energy')
|
||||
else:
|
||||
self.supportedmeters = ('DC Energy',)
|
||||
|
||||
def get_energy_precision(self, ipmicmd):
|
||||
rsp = ipmicmd.xraw_command(
|
||||
netfn=0x2e, command=0x81,
|
||||
data=self.iana + self.modhandle + b'\x01\x80')
|
||||
print(repr(rsp['data'][:]))
|
||||
|
||||
def get_ac_energy(self, ipmicmd):
|
||||
try:
|
||||
rsp = ipmicmd.xraw_command(
|
||||
netfn=0x2e, command=0x81,
|
||||
data=self.iana + self.modhandle + b'\x01\x82\x01\x08')
|
||||
# data is in millijoules, convert to the more recognizable kWh
|
||||
return float(
|
||||
struct.unpack('!Q', rsp['data'][3:])[0]) / 1000000 / 3600
|
||||
except pygexc.IpmiException as ie:
|
||||
if ie.ipmicode == 0xcb:
|
||||
return 0.0
|
||||
raise
|
||||
|
||||
def get_dc_energy(self, ipmicmd):
|
||||
rsp = ipmicmd.xraw_command(
|
||||
netfn=0x2e, command=0x81,
|
||||
data=self.iana + self.modhandle + b'\x01\x82\x00\x08')
|
||||
# data is in millijoules, convert to the more recognizable kWh
|
||||
return float(struct.unpack('!Q', rsp['data'][3:])[0]) / 1000000 / 3600
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
import os
|
||||
import pyghmi.ipmi.command as cmd
|
||||
import sys
|
||||
c = cmd.Command(sys.argv[1], os.environ['BMCUSER'], os.environ['BMCPASS'])
|
||||
EnergyManager(c).get_dc_energy(c)
|
@ -1,6 +1,6 @@
|
||||
# vim: tabstop=4 shiftwidth=4 softtabstop=4
|
||||
|
||||
# Copyright 2015-2016 Lenovo
|
||||
# Copyright 2015-2017 Lenovo
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
@ -355,16 +355,25 @@ class OEMHandler(generic.OEMHandler):
|
||||
for name in nextscale.get_sensor_names(self._fpc_variant):
|
||||
yield nextscale.get_sensor_reading(name, self.ipmicmd,
|
||||
self._fpc_variant)
|
||||
elif self.has_imm:
|
||||
for name in self.immhandler.get_oem_sensor_names(self.ipmicmd):
|
||||
yield self.immhandler.get_oem_sensor_reading(name,
|
||||
self.ipmicmd)
|
||||
|
||||
def get_sensor_descriptions(self):
|
||||
if self.is_fpc:
|
||||
return nextscale.get_sensor_descriptions(self._fpc_variant)
|
||||
elif self.has_imm:
|
||||
return self.immhandler.get_oem_sensor_descriptions(self.ipmicmd)
|
||||
return ()
|
||||
|
||||
def get_sensor_reading(self, sensorname):
|
||||
if self.is_fpc:
|
||||
return nextscale.get_sensor_reading(sensorname, self.ipmicmd,
|
||||
self._fpc_variant)
|
||||
elif self.has_imm:
|
||||
return self.immhandler.get_oem_sensor_reading(sensorname,
|
||||
self.ipmicmd)
|
||||
return ()
|
||||
|
||||
def get_inventory_of_component(self, component):
|
||||
|
@ -17,9 +17,12 @@
|
||||
from datetime import datetime
|
||||
import json
|
||||
import os.path
|
||||
import pyghmi.constants as pygconst
|
||||
import pyghmi.exceptions as pygexc
|
||||
import pyghmi.ipmi.oem.lenovo.energy as energy
|
||||
import pyghmi.ipmi.private.session as ipmisession
|
||||
import pyghmi.ipmi.private.util as util
|
||||
import pyghmi.ipmi.sdr as sdr
|
||||
import pyghmi.util.webclient as webclient
|
||||
import random
|
||||
import struct
|
||||
@ -70,6 +73,7 @@ class IMMClient(object):
|
||||
self.username = ipmicmd.ipmi_session.userid
|
||||
self.password = ipmicmd.ipmi_session.password
|
||||
self._wc = None # The webclient shall be initiated on demand
|
||||
self._energymanager = None
|
||||
self.datacache = {}
|
||||
|
||||
@staticmethod
|
||||
@ -290,6 +294,30 @@ class IMMClient(object):
|
||||
except KeyError:
|
||||
return None
|
||||
|
||||
def get_oem_sensor_names(self, ipmicmd):
|
||||
if self._energymanager is None:
|
||||
self._energymanager = energy.EnergyManager(ipmicmd)
|
||||
return self._energymanager.supportedmeters
|
||||
|
||||
def get_oem_sensor_descriptions(self, ipmicmd):
|
||||
return [{'name': x, 'type': 'Power'
|
||||
} for x in self.get_oem_sensor_names(ipmicmd)]
|
||||
|
||||
def get_oem_sensor_reading(self, name, ipmicmd):
|
||||
if self._energymanager is None:
|
||||
self._energymanager = energy.EnergyManager(ipmicmd)
|
||||
if name == 'AC Energy':
|
||||
kwh = self._energymanager.get_ac_energy(ipmicmd)
|
||||
elif name == 'DC Energy':
|
||||
kwh = self._energymanager.get_dc_energy(ipmicmd)
|
||||
else:
|
||||
raise pygexc.UnsupportedFunctionality('No sunch sensor ' + name)
|
||||
return sdr.SensorReading({'name': name, 'imprecision': None,
|
||||
'value': kwh, 'states': [],
|
||||
'state_ids': [],
|
||||
'health': pygconst.Health.Ok,
|
||||
'type': 'Power'}, 'kWh')
|
||||
|
||||
def weblogout(self):
|
||||
if self._wc:
|
||||
try:
|
||||
|
Loading…
x
Reference in New Issue
Block a user