mirror of
https://opendev.org/x/pyghmi
synced 2025-01-27 19:37:44 +00:00
Implement support for ThinkSystem servers
Change-Id: Ic45943c4b882bb13390722cca58b8b1785386e86
This commit is contained in:
parent
4c366d2f47
commit
d1f48b02c0
@ -1490,9 +1490,7 @@ class Command(object):
|
||||
raise Exception('name must be less than or = 16 chars')
|
||||
name = name.ljust(16, "\x00")
|
||||
data.extend([ord(x) for x in name])
|
||||
response = self.raw_command(netfn=0x06, command=0x45, data=data)
|
||||
if 'error' in response:
|
||||
raise Exception(response['error'])
|
||||
self.xraw_command(netfn=0x06, command=0x45, data=data)
|
||||
return True
|
||||
|
||||
def get_user_name(self, uid, return_none_on_error=True):
|
||||
@ -1554,12 +1552,16 @@ class Command(object):
|
||||
else:
|
||||
password = password.ljust(16, "\x00")
|
||||
data.extend([ord(x) for x in password])
|
||||
response = self.raw_command(netfn=0x06, command=0x47, data=data)
|
||||
if 'error' in response:
|
||||
try:
|
||||
self.xraw_command(netfn=0x06, command=0x47, data=data)
|
||||
except exc.IpmiException as ie:
|
||||
if mode == 'test_password':
|
||||
# return false if password test failed
|
||||
return False
|
||||
raise Exception(response['error'])
|
||||
elif mode in ('enable', 'disable') and ie.ipmicode == 0xcc:
|
||||
# Some BMCs see redundant calls to password disable/enable
|
||||
# as invalid
|
||||
return True
|
||||
raise
|
||||
return True
|
||||
|
||||
def get_channel_max_user_count(self, channel=None):
|
||||
@ -1689,7 +1691,9 @@ class Command(object):
|
||||
try:
|
||||
# First try to set name to all \x00 explicitly
|
||||
self.set_user_name(uid, '')
|
||||
except Exception:
|
||||
except exc.IpmiException as ie:
|
||||
if ie.ipmicode != 0xcc:
|
||||
raise
|
||||
# An invalid data field in request is frequently reported.
|
||||
# however another convention that exists is all '\xff'
|
||||
# if this fails, pass up the error so that calling code knows
|
||||
|
@ -143,12 +143,16 @@ class OEMHandler(generic.OEMHandler):
|
||||
# will need to retain data to differentiate
|
||||
# variations. For example System X versus Thinkserver
|
||||
self.oemid = oemid
|
||||
self._fpc_variant = None
|
||||
self.ipmicmd = weakref.proxy(ipmicmd)
|
||||
self._has_megarac = None
|
||||
self.oem_inventory_info = None
|
||||
self._mrethidx = None
|
||||
self._hasimm = None
|
||||
if self.has_imm:
|
||||
self._hasxcc = None
|
||||
if self.has_xcc:
|
||||
self.immhandler = imm.XCCClient(ipmicmd)
|
||||
elif self.has_imm:
|
||||
self.immhandler = imm.IMMClient(ipmicmd)
|
||||
|
||||
@property
|
||||
@ -281,9 +285,15 @@ class OEMHandler(generic.OEMHandler):
|
||||
def is_fpc(self):
|
||||
"""True if the target is a Lenovo nextscale fan power controller
|
||||
"""
|
||||
fpc_ids = ((19046, 32, 1063),)
|
||||
return (self.oemid['manufacturer_id'], self.oemid['device_id'],
|
||||
self.oemid['product_id']) in fpc_ids
|
||||
fpc_id = (19046, 32, 1063)
|
||||
smm_id = (19046, 32, 1180)
|
||||
currid = (self.oemid['manufacturer_id'], self.oemid['device_id'],
|
||||
self.oemid['product_id'])
|
||||
if currid == fpc_id:
|
||||
self._fpc_variant = 6
|
||||
elif currid == smm_id:
|
||||
self._fpc_variant = 2
|
||||
return self._fpc_variant
|
||||
|
||||
@property
|
||||
def is_sd350(self):
|
||||
@ -327,17 +337,19 @@ class OEMHandler(generic.OEMHandler):
|
||||
|
||||
def get_sensor_data(self):
|
||||
if self.is_fpc:
|
||||
for name in nextscale.get_sensor_names():
|
||||
yield nextscale.get_sensor_reading(name, self.ipmicmd)
|
||||
for name in nextscale.get_sensor_names(self._fpc_variant):
|
||||
yield nextscale.get_sensor_reading(name, self.ipmicmd,
|
||||
self._fpc_variant)
|
||||
|
||||
def get_sensor_descriptions(self):
|
||||
if self.is_fpc:
|
||||
return nextscale.get_sensor_descriptions()
|
||||
return nextscale.get_sensor_descriptions(self._fpc_variant)
|
||||
return ()
|
||||
|
||||
def get_sensor_reading(self, sensorname):
|
||||
if self.is_fpc:
|
||||
return nextscale.get_sensor_reading(sensorname, self.ipmicmd)
|
||||
return nextscale.get_sensor_reading(sensorname, self.ipmicmd,
|
||||
self._fpc_variant)
|
||||
return ()
|
||||
|
||||
def get_inventory_of_component(self, component):
|
||||
@ -486,6 +498,30 @@ class OEMHandler(generic.OEMHandler):
|
||||
fru['oem_parser'] = None
|
||||
return fru
|
||||
|
||||
@property
|
||||
def has_xcc(self):
|
||||
if self._hasxcc is not None:
|
||||
return self._hasxcc
|
||||
try:
|
||||
bdata = self.ipmicmd.xraw_command(netfn=0x3a, command=0xc1)
|
||||
except pygexc.IpmiException:
|
||||
self._hasxcc = False
|
||||
self._hasimm = False
|
||||
return False
|
||||
if len(bdata['data'][:]) != 3:
|
||||
self._hasimm = False
|
||||
self._hasxcc = False
|
||||
return False
|
||||
rdata = bytearray(bdata['data'][:])
|
||||
self._hasxcc = rdata[1] & 16 == 16
|
||||
if self._hasxcc:
|
||||
# For now, have imm calls go to xcc, since they are providing same
|
||||
# interface. Longer term the hope is that all the Lenovo
|
||||
# stuff will branch at init, and not have conditionals
|
||||
# in all the functions
|
||||
self._hasimm = self._hasxcc
|
||||
return self._hasxcc
|
||||
|
||||
@property
|
||||
def has_imm(self):
|
||||
if self._hasimm is not None:
|
||||
@ -499,7 +535,7 @@ class OEMHandler(generic.OEMHandler):
|
||||
self._hasimm = False
|
||||
return False
|
||||
rdata = bytearray(bdata['data'][:])
|
||||
self._hasimm = (rdata[1] & 1 == 1) or (rdata[1] & 8 == 8)
|
||||
self._hasimm = (rdata[1] & 1 == 1) or (rdata[1] & 16 == 16)
|
||||
return self._hasimm
|
||||
|
||||
def get_oem_firmware(self, bmcver):
|
||||
@ -509,6 +545,9 @@ class OEMHandler(generic.OEMHandler):
|
||||
return command["parser"](rsp["data"])
|
||||
elif self.has_imm:
|
||||
return self.immhandler.get_firmware_inventory(bmcver)
|
||||
elif self.is_fpc:
|
||||
return nextscale.get_fpc_firmware(bmcver, self.ipmicmd,
|
||||
self._fpc_variant)
|
||||
return super(OEMHandler, self).get_oem_firmware(bmcver)
|
||||
|
||||
def get_oem_capping_enabled(self):
|
||||
|
@ -16,13 +16,15 @@
|
||||
|
||||
from datetime import datetime
|
||||
import json
|
||||
from pyghmi.ipmi.private.util import _monotonic_time
|
||||
import pyghmi.ipmi.private.util as util
|
||||
import pyghmi.util.webclient as webclient
|
||||
import urllib
|
||||
import weakref
|
||||
|
||||
|
||||
class IMMClient(object):
|
||||
logouturl = '/data/logout'
|
||||
bmcname = 'IMM'
|
||||
|
||||
def __init__(self, ipmicmd):
|
||||
self.ipmicmd = weakref.proxy(ipmicmd)
|
||||
@ -50,6 +52,10 @@ class IMMClient(object):
|
||||
return datetime.strptime(strval, '%m/%d/%Y')
|
||||
except ValueError:
|
||||
pass
|
||||
try:
|
||||
return datetime.strptime(strval, '%Y-%m-%d')
|
||||
except ValueError:
|
||||
pass
|
||||
try:
|
||||
return datetime.strptime(strval, '%m %d %Y')
|
||||
except ValueError:
|
||||
@ -133,7 +139,7 @@ class IMMClient(object):
|
||||
def get_cached_data(self, attribute):
|
||||
try:
|
||||
kv = self.datacache[attribute]
|
||||
if kv[1] > _monotonic_time() - 30:
|
||||
if kv[1] > util._monotonic_time() - 30:
|
||||
return kv[0]
|
||||
except KeyError:
|
||||
return None
|
||||
@ -147,7 +153,7 @@ class IMMClient(object):
|
||||
result = self.wc.grab_json_response('/data?set', params)
|
||||
if result['return'] != 'Success':
|
||||
raise Exception(result['reason'])
|
||||
self.wc.grab_json_response('/data/logout')
|
||||
self.weblogout()
|
||||
|
||||
def detach_remote_media(self):
|
||||
mnt = self.wc.grab_json_response(
|
||||
@ -165,7 +171,7 @@ class IMMClient(object):
|
||||
result = self.wc.grab_json_response('/data?set', params)
|
||||
if result['return'] != 'Success':
|
||||
raise Exception(result['reason'])
|
||||
self.wc.grab_json_response('/data/logout')
|
||||
self.weblogout()
|
||||
|
||||
def fetch_agentless_firmware(self):
|
||||
adapterdata = self.get_cached_data('lenovo_cached_adapters')
|
||||
@ -175,7 +181,7 @@ class IMMClient(object):
|
||||
'/designs/imm/dataproviders/imm_adapters.php')
|
||||
if adapterdata:
|
||||
self.datacache['lenovo_cached_adapters'] = (
|
||||
adapterdata, _monotonic_time())
|
||||
adapterdata, util._monotonic_time())
|
||||
if adapterdata and 'items' in adapterdata:
|
||||
for adata in adapterdata['items']:
|
||||
aname = adata['adapter.adapterName']
|
||||
@ -209,7 +215,7 @@ class IMMClient(object):
|
||||
'/designs/imm/dataproviders/raid_alldevices.php')
|
||||
if storagedata:
|
||||
self.datacache['lenovo_cached_storage'] = (
|
||||
storagedata, _monotonic_time())
|
||||
storagedata, util._monotonic_time())
|
||||
if storagedata and 'items' in storagedata:
|
||||
for adp in storagedata['items']:
|
||||
if 'storage.vpd.productName' not in adp:
|
||||
@ -227,9 +233,7 @@ class IMMClient(object):
|
||||
bdata['version'] = diskent['storage.firmwares'][0][
|
||||
'versionStr']
|
||||
yield (diskname, bdata)
|
||||
if self.wc:
|
||||
self.wc.grab_json_response('/data/logout')
|
||||
self._wc = None
|
||||
self.weblogout()
|
||||
|
||||
def get_hw_inventory(self):
|
||||
hwmap = self.hardware_inventory_map()
|
||||
@ -250,7 +254,7 @@ class IMMClient(object):
|
||||
|
||||
def weblogout(self):
|
||||
if self._wc:
|
||||
self._wc.grab_json_response('/data/logout')
|
||||
self._wc.grab_json_response(self.logouturl)
|
||||
self._wc = None
|
||||
|
||||
def hardware_inventory_map(self):
|
||||
@ -265,7 +269,7 @@ class IMMClient(object):
|
||||
'/designs/imm/dataproviders/imm_adapters.php')
|
||||
if adapterdata:
|
||||
self.datacache['lenovo_cached_adapters'] = (
|
||||
adapterdata, _monotonic_time())
|
||||
adapterdata, util._monotonic_time())
|
||||
if adapterdata and 'items' in adapterdata:
|
||||
for adata in adapterdata['items']:
|
||||
skipadapter = False
|
||||
@ -305,7 +309,8 @@ class IMMClient(object):
|
||||
skipadapter = True
|
||||
if not skipadapter:
|
||||
hwmap[aname] = bdata
|
||||
self.datacache['lenovo_cached_hwmap'] = (hwmap, _monotonic_time())
|
||||
self.datacache['lenovo_cached_hwmap'] = (hwmap,
|
||||
util._monotonic_time())
|
||||
self.weblogout()
|
||||
return hwmap
|
||||
|
||||
@ -317,24 +322,26 @@ class IMMClient(object):
|
||||
immverdata = self.parse_imm_buildinfo(rsp['data'])
|
||||
bdata = {
|
||||
'version': bmcver, 'build': immverdata[0], 'date': immverdata[1]}
|
||||
yield ('IMM', bdata)
|
||||
yield (self.bmcname, bdata)
|
||||
bdata = self.fetch_grouped_properties({
|
||||
'build': '/v2/ibmc/dm/fw/imm2/backup_build_id',
|
||||
'version': '/v2/ibmc/dm/fw/imm2/backup_build_version',
|
||||
'date': '/v2/ibmc/dm/fw/imm2/backup_build_date'})
|
||||
if bdata:
|
||||
yield ('IMM Backup', bdata)
|
||||
yield ('{0} Backup'.format(self.bmcname), bdata)
|
||||
bdata = self.fetch_grouped_properties({
|
||||
'build': '/v2/ibmc/trusted_buildid',
|
||||
})
|
||||
if bdata:
|
||||
yield ('IMM Trusted Image', bdata)
|
||||
yield ('{0} Trusted Image'.format(self.bmcname), bdata)
|
||||
bdata = self.fetch_grouped_properties({
|
||||
'build': '/v2/bios/build_id',
|
||||
'version': '/v2/bios/build_version',
|
||||
'date': '/v2/bios/build_date'})
|
||||
if bdata:
|
||||
yield ('UEFI', bdata)
|
||||
else:
|
||||
yield ('UEFI', {'version': 'unknown'})
|
||||
bdata = self.fetch_grouped_properties({
|
||||
'build': '/v2/ibmc/dm/fw/bios/backup_build_id',
|
||||
'version': '/v2/ibmc/dm/fw/bios/backup_build_version'})
|
||||
@ -346,5 +353,130 @@ class IMMClient(object):
|
||||
'build': '/v2/bios/pending_build_id'})
|
||||
if bdata:
|
||||
yield ('UEFI Pending Update', bdata)
|
||||
fpga = self.ipmicmd.xraw_command(netfn=0x3a, command=0x6b, data=(0,))
|
||||
fpga = '{0}.{1}.{2}'.format(*[ord(x) for x in fpga['data']])
|
||||
yield ('FPGA', {'version': fpga})
|
||||
for firm in self.fetch_agentless_firmware():
|
||||
yield firm
|
||||
|
||||
|
||||
class XCCClient(IMMClient):
|
||||
logouturl = '/api/providers/logout'
|
||||
bmcname = 'XCC'
|
||||
|
||||
def get_webclient(self):
|
||||
cv = self.ipmicmd.certverify
|
||||
wc = webclient.SecureHTTPConnection(self.imm, 443, verifycallback=cv)
|
||||
try:
|
||||
wc.connect()
|
||||
except Exception:
|
||||
return None
|
||||
adata = json.dumps({'username': self.username,
|
||||
'password': self.password
|
||||
})
|
||||
headers = {'Connection': 'keep-alive',
|
||||
'Content-Type': 'application/json'}
|
||||
wc.request('POST', '/api/login', adata, headers)
|
||||
rsp = wc.getresponse()
|
||||
if rsp.status == 200:
|
||||
rspdata = json.loads(rsp.read())
|
||||
wc.set_header('Content-Type', 'application/json')
|
||||
wc.set_header('Authorization', 'Bearer ' + rspdata['access_token'])
|
||||
if '_csrf_token' in wc.cookies:
|
||||
wc.set_header('X-XSRF-TOKEN', wc.cookies['_csrf_token'])
|
||||
return wc
|
||||
|
||||
def attach_remote_media(self, url, user, password):
|
||||
proto, host, path = util.urlsplit(url)
|
||||
if proto == 'smb':
|
||||
proto = 'cifs'
|
||||
rq = {'Option': '', 'Domain': '', 'Write': 0}
|
||||
# nfs == 1, cifs == 0
|
||||
if proto == 'nfs':
|
||||
rq['Protocol'] = 1
|
||||
rq['Url'] = '{0}:{1}'.format(host, path)
|
||||
elif proto == 'cifs':
|
||||
rq['Protocol'] = 0
|
||||
rq['Credential'] = '{0}:{1}'.format(user, password)
|
||||
rq['Url'] = '//{0}{1}'.format(host, path)
|
||||
elif proto in ('http', 'https'):
|
||||
rq['Protocol'] = 7
|
||||
rq['Url'] = url
|
||||
else:
|
||||
raise Exception('TODO')
|
||||
rt = self.wc.grab_json_response('/api/providers/rp_vm_remote_connect',
|
||||
json.dumps(rq))
|
||||
if 'return' not in rt or rt['return'] != 0:
|
||||
raise Exception('Unhandled return: ' + repr(rt))
|
||||
rt = self.wc.grab_json_response('/api/providers/rp_vm_remote_mountall',
|
||||
'{}')
|
||||
if 'return' not in rt or rt['return'] != 0:
|
||||
raise Exception('Unhandled return: ' + repr(rt))
|
||||
|
||||
def get_firmware_inventory(self, bmcver):
|
||||
# First we fetch the system firmware found in imm properties
|
||||
# then check for agentless, if agentless, get adapter info using
|
||||
# https, using the caller TLS verification scheme
|
||||
rsp = self.ipmicmd.xraw_command(netfn=0x3a, command=0x50)
|
||||
immverdata = self.parse_imm_buildinfo(rsp['data'])
|
||||
bdata = {
|
||||
'version': bmcver, 'build': immverdata[0], 'date': immverdata[1]}
|
||||
yield (self.bmcname, bdata)
|
||||
bdata = self.fetch_grouped_properties({
|
||||
'build': '/v2/ibmc/dm/fw/imm3/backup_pending_build_id',
|
||||
'version': '/v2/ibmc/dm/fw/imm3/backup_pending_build_version',
|
||||
'date': '/v2/ibmc/dm/fw/imm3/backup_pending_build_date'})
|
||||
if bdata:
|
||||
yield ('{0} Backup'.format(self.bmcname), bdata)
|
||||
else:
|
||||
bdata = self.fetch_grouped_properties({
|
||||
'build': '/v2/ibmc/dm/fw/imm3/backup_build_id',
|
||||
'version': '/v2/ibmc/dm/fw/imm3/backup_build_version',
|
||||
'date': '/v2/ibmc/dm/fw/imm3/backup_build_date'})
|
||||
if bdata:
|
||||
yield ('{0} Backup'.format(self.bmcname), bdata)
|
||||
bdata = self.fetch_grouped_properties({
|
||||
'build': '/v2/ibmc/trusted_buildid',
|
||||
})
|
||||
if bdata:
|
||||
bdata = self.fetch_grouped_properties({
|
||||
'build': '/v2/ibmc/trusted_buildid',
|
||||
})
|
||||
if bdata:
|
||||
yield ('{0} Trusted Image'.format(self.bmcname), bdata)
|
||||
bdata = self.fetch_grouped_properties({
|
||||
'build': '/v2/bios/build_id',
|
||||
'version': '/v2/bios/build_version',
|
||||
'date': '/v2/bios/build_date'})
|
||||
if bdata:
|
||||
yield ('UEFI', bdata)
|
||||
# Note that the next pending could be pending for either primary
|
||||
# or backup, so can't promise where it will go
|
||||
bdata = self.fetch_grouped_properties({
|
||||
'build': '/v2/bios/pending_build_id'})
|
||||
if bdata:
|
||||
yield ('UEFI Pending Update', bdata)
|
||||
bdata = self.fetch_grouped_properties({
|
||||
'build': '/v2/tdm/build_id',
|
||||
'version': '/v2/tdm/build_version',
|
||||
'date': '/v2/tdm/build_date'})
|
||||
if bdata:
|
||||
yield ('LXPM', bdata)
|
||||
fpga = self.ipmicmd.xraw_command(netfn=0x3a, command=0x6b, data=(0,))
|
||||
fpga = '{0}.{1}.{2}'.format(*[ord(x) for x in fpga['data']])
|
||||
yield ('FPGA', {'version': fpga})
|
||||
for firm in self.fetch_agentless_firmware():
|
||||
yield firm
|
||||
|
||||
def detach_remote_media(self):
|
||||
rt = self.wc.grab_json_response('/api/providers/rp_vm_remote_getdisk')
|
||||
if 'items' in rt:
|
||||
slots = []
|
||||
for mount in rt['items']:
|
||||
slots.append(mount['slotId'])
|
||||
for slot in slots:
|
||||
rt = self.wc.grab_json_response(
|
||||
'/api/providers/rp_vm_remote_unmount',
|
||||
json.dumps({'Slot': slot}))
|
||||
if 'return' not in rt or rt['return'] != 0:
|
||||
raise Exception("Unrecognized return: " + repr(rt))
|
||||
|
@ -1,6 +1,6 @@
|
||||
# vim: tabstop=4 shiftwidth=4 softtabstop=4
|
||||
|
||||
# Copyright 2016 Lenovo
|
||||
# Copyright 2016-2017 Lenovo
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
@ -15,6 +15,7 @@
|
||||
# limitations under the License.
|
||||
|
||||
import pyghmi.constants as pygconst
|
||||
import pyghmi.exceptions as pygexc
|
||||
import pyghmi.ipmi.sdr as sdr
|
||||
import struct
|
||||
|
||||
@ -27,12 +28,16 @@ except NameError:
|
||||
def fpc_read_ac_input(ipmicmd):
|
||||
rsp = ipmicmd.xraw_command(netfn=0x32, command=0x90, data=(1,))
|
||||
rsp = rsp['data']
|
||||
if len(rsp) == 6:
|
||||
rsp = b'\x00' + bytes(rsp)
|
||||
return struct.unpack_from('<H', rsp[3:5])[0]
|
||||
|
||||
|
||||
def fpc_read_dc_output(ipmicmd):
|
||||
rsp = ipmicmd.xraw_command(netfn=0x32, command=0x90, data=(2,))
|
||||
rsp = rsp['data']
|
||||
if len(rsp) == 6:
|
||||
rsp = b'\x00' + bytes(rsp)
|
||||
return struct.unpack_from('<H', rsp[3:5])[0]
|
||||
|
||||
|
||||
@ -43,16 +48,20 @@ def fpc_read_fan_power(ipmicmd):
|
||||
return struct.unpack_from('<I', rsp[1:])[0] / 100.0
|
||||
|
||||
|
||||
def fpc_read_psu_fan(ipmicmd, number):
|
||||
def fpc_read_psu_fan(ipmicmd, number, sz):
|
||||
rsp = ipmicmd.xraw_command(netfn=0x32, command=0xa5, data=(number,))
|
||||
rsp = rsp['data']
|
||||
return struct.unpack_from('<H', rsp[:2])[0]
|
||||
|
||||
|
||||
def fpc_get_psustatus(ipmicmd, number):
|
||||
def fpc_get_psustatus(ipmicmd, number, sz):
|
||||
rsp = ipmicmd.xraw_command(netfn=0x32, command=0x91)
|
||||
mask = 1 << (number - 1)
|
||||
statdata = bytearray(rsp['data'])
|
||||
if len(rsp['data']) == 6:
|
||||
statdata = bytearray([0])
|
||||
else:
|
||||
statdata = bytearray()
|
||||
statdata += bytearray(rsp['data'])
|
||||
presence = statdata[3] & mask == mask
|
||||
pwrgood = statdata[4] & mask == mask
|
||||
throttle = (statdata[6] | statdata[2]) & mask == mask
|
||||
@ -72,12 +81,23 @@ def fpc_get_psustatus(ipmicmd, number):
|
||||
return (health, states)
|
||||
|
||||
|
||||
def fpc_get_nodeperm(ipmicmd, number):
|
||||
rsp = ipmicmd.xraw_command(netfn=0x32, command=0xa7, data=(number,))
|
||||
def fpc_get_nodeperm(ipmicmd, number, sz):
|
||||
try:
|
||||
rsp = ipmicmd.xraw_command(netfn=0x32, command=0xa7, data=(number,))
|
||||
except pygexc.IpmiException as ie:
|
||||
if ie.ipmicode == 0xd5: # no node present
|
||||
return (pygconst.Health.Ok, ['Absent'])
|
||||
raise
|
||||
perminfo = ord(rsp['data'][1])
|
||||
health = pygconst.Health.Ok
|
||||
states = []
|
||||
if rsp['data'][4] in ('\x02', '\x03'):
|
||||
if len(rsp['data']) == 4: # different gens handled rc differently
|
||||
rsp['data'] = b'\x00' + bytes(rsp['data'])
|
||||
if sz == 6: # FPC
|
||||
permfail = ('\x02', '\x03')
|
||||
elif sz == 2: # SMM
|
||||
permfail = ('\x02',)
|
||||
if rsp['data'][4] in permfail:
|
||||
states.append('Insufficient Power')
|
||||
health = pygconst.Health.Failed
|
||||
if perminfo & 0x40:
|
||||
@ -111,7 +131,7 @@ fpc_sensors = {
|
||||
'type': 'Fan',
|
||||
'units': 'RPM',
|
||||
'provider': fpc_read_psu_fan,
|
||||
'elements': 6,
|
||||
'elements': 1,
|
||||
},
|
||||
'Total Power Capacity': {
|
||||
'type': 'Power',
|
||||
@ -123,36 +143,40 @@ fpc_sensors = {
|
||||
'returns': 'tuple',
|
||||
'units': None,
|
||||
'provider': fpc_get_nodeperm,
|
||||
'elements': 12,
|
||||
'elements': 2,
|
||||
},
|
||||
'Power Supply': {
|
||||
'type': 'Power Supply',
|
||||
'returns': 'tuple',
|
||||
'units': None,
|
||||
'provider': fpc_get_psustatus,
|
||||
'elements': 6,
|
||||
'elements': 1,
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
def get_sensor_names():
|
||||
def get_sensor_names(size):
|
||||
global fpc_sensors
|
||||
for name in fpc_sensors:
|
||||
if size == 2 and name in ('Fan Power', 'Total Power Capacity'):
|
||||
continue
|
||||
sensor = fpc_sensors[name]
|
||||
if 'elements' in sensor:
|
||||
for elemidx in range(sensor['elements']):
|
||||
for elemidx in range(sensor['elements'] * size):
|
||||
elemidx += 1
|
||||
yield '{0} {1}'.format(name, elemidx)
|
||||
else:
|
||||
yield name
|
||||
|
||||
|
||||
def get_sensor_descriptions():
|
||||
def get_sensor_descriptions(size):
|
||||
global fpc_sensors
|
||||
for name in fpc_sensors:
|
||||
if size == 2 and name in ('Fan Power', 'Total Power Capacity'):
|
||||
continue
|
||||
sensor = fpc_sensors[name]
|
||||
if 'elements' in sensor:
|
||||
for elemidx in range(sensor['elements']):
|
||||
for elemidx in range(sensor['elements'] * size):
|
||||
elemidx += 1
|
||||
yield {'name': '{0} {1}'.format(name, elemidx),
|
||||
'type': sensor['type']}
|
||||
@ -160,7 +184,23 @@ def get_sensor_descriptions():
|
||||
yield {'name': name, 'type': sensor['type']}
|
||||
|
||||
|
||||
def get_sensor_reading(name, ipmicmd):
|
||||
def get_fpc_firmware(bmcver, ipmicmd, fpcorsmm):
|
||||
mymsg = ipmicmd.xraw_command(netfn=0x32, command=0xa8)
|
||||
builddata = bytearray(mymsg['data'])
|
||||
name = None
|
||||
if fpcorsmm == 2: # SMM
|
||||
name = 'SMM'
|
||||
buildid = '{0}{1}{2}{3}{4}{5}{6}'.format(
|
||||
*[chr(x) for x in builddata[-7:]])
|
||||
elif len(builddata) == 8:
|
||||
builddata = builddata[1:] # discard the 'completion code'
|
||||
name = 'FPC'
|
||||
buildid = '{0}{1}'.format(builddata[-2], chr(builddata[-1]))
|
||||
yield (name, {'version': bmcver, 'build': buildid})
|
||||
yield ('PSOC', {'version': '{0}.{1}'.format(builddata[2], builddata[3])})
|
||||
|
||||
|
||||
def get_sensor_reading(name, ipmicmd, sz):
|
||||
value = None
|
||||
sensor = None
|
||||
health = pygconst.Health.Ok
|
||||
@ -169,14 +209,14 @@ def get_sensor_reading(name, ipmicmd):
|
||||
sensor = fpc_sensors[name]
|
||||
value = sensor['provider'](ipmicmd)
|
||||
else:
|
||||
bname, _, idx = name.rpartition(' ')
|
||||
bnam, _, idx = name.rpartition(' ')
|
||||
idx = int(idx)
|
||||
if bname in fpc_sensors and idx <= fpc_sensors[bname]['elements']:
|
||||
sensor = fpc_sensors[bname]
|
||||
if bnam in fpc_sensors and idx <= fpc_sensors[bnam]['elements'] * sz:
|
||||
sensor = fpc_sensors[bnam]
|
||||
if 'returns' in sensor:
|
||||
health, states = sensor['provider'](ipmicmd, idx)
|
||||
health, states = sensor['provider'](ipmicmd, idx, sz)
|
||||
else:
|
||||
value = sensor['provider'](ipmicmd, idx)
|
||||
value = sensor['provider'](ipmicmd, idx, sz)
|
||||
if sensor is not None:
|
||||
return sdr.SensorReading({'name': name, 'imprecision': None,
|
||||
'value': value, 'states': states,
|
||||
|
@ -372,6 +372,15 @@ class Session(object):
|
||||
# is given in the same thread as was called
|
||||
return
|
||||
|
||||
@classmethod
|
||||
def _is_session_valid(cls, session):
|
||||
sess = cls.keepalive_sessions.get(session, None)
|
||||
if sess is not None and 'timeout' in sess:
|
||||
if sess['timeout'] < _monotonic_time():
|
||||
# session would have timed out by now, don't use it
|
||||
return False
|
||||
return True
|
||||
|
||||
def __new__(cls,
|
||||
bmc,
|
||||
userid,
|
||||
@ -390,7 +399,8 @@ class Session(object):
|
||||
self = cls.bmc_handlers[sockaddr]
|
||||
if (self.bmc == bmc and self.userid == userid and
|
||||
self.password == password and self.kgo == kg and
|
||||
(self.logged or self.logging)):
|
||||
(self.logged or self.logging) and
|
||||
cls._is_session_valid(self)):
|
||||
trueself = self
|
||||
else:
|
||||
del cls.bmc_handlers[sockaddr]
|
||||
|
@ -77,7 +77,9 @@ class SecureHTTPConnection(httplib.HTTPConnection, object):
|
||||
|
||||
def request(self, method, url, body=None, headers=None):
|
||||
if headers is None:
|
||||
headers = self.stdheaders
|
||||
headers = self.stdheaders.copy()
|
||||
if method == 'GET' and 'Content-Type' in headers:
|
||||
del headers['Content-Type']
|
||||
if self.cookies:
|
||||
cookies = []
|
||||
for ckey in self.cookies:
|
||||
|
Loading…
x
Reference in New Issue
Block a user