blob: 358bd65999762cb8d9fd4ee566526f7af921fe75 [file] [log] [blame]
#!/usr/bin/env python3
import yaml
import argparse
from typing import NamedTuple
class RptSensor(NamedTuple):
name: str
entityId: int
typeId: int
evtType: int
sensorId: int
fru: int
targetPath: str
sampleDimmTemp = {
'bExp': 0,
'entityID': 32,
'entityInstance': 2,
'interfaces': {
'xyz.openbmc_project.Sensor.Value': {
'Value': {
'Offsets': {
255: {
'type': 'int64_t'
}
}
}
}
},
'multiplierM': 1,
'mutability': 'Mutability::Write|Mutability::Read',
'offsetB': -127,
'path': '/xyz/openbmc_project/sensors/temperature/dimm0_temp',
'rExp': 0,
'readingType': 'readingData',
'scale': -3,
'sensorNamePattern': 'nameLeaf',
'sensorReadingType': 1,
'sensorType': 1,
'serviceInterface': 'org.freedesktop.DBus.Properties',
'unit': 'xyz.openbmc_project.Sensor.Value.Unit.DegreesC'
}
sampleCoreTemp = {
'bExp': 0,
'entityID': 208,
'entityInstance': 2,
'interfaces': {
'xyz.openbmc_project.Sensor.Value': {
'Value': {
'Offsets': {
255: {
'type': 'int64_t'
}
}
}
}
},
'multiplierM': 1,
'mutability': 'Mutability::Write|Mutability::Read',
'offsetB': -127,
'path': '/xyz/openbmc_project/sensors/temperature/p0_core0_temp',
'rExp': 0,
'readingType': 'readingData',
'scale': -3,
'sensorNamePattern': 'nameLeaf',
'sensorReadingType': 1,
'sensorType': 1,
'serviceInterface': 'org.freedesktop.DBus.Properties',
'unit': 'xyz.openbmc_project.Sensor.Value.Unit.DegreesC'
}
samplePower = {
'bExp': 0,
'entityID': 10,
'entityInstance': 13,
'interfaces': {
'xyz.openbmc_project.Sensor.Value': {
'Value': {
'Offsets': {
255: {
'type': 'int64_t'
}
}
}
}
},
'multiplierM': 2,
'offsetB': 0,
'path': '/xyz/openbmc_project/sensors/power/p0_power',
'rExp': 0,
'readingType': 'readingData',
'scale': -6,
'sensorNamePattern': 'nameLeaf',
'sensorReadingType': 1,
'sensorType': 8,
'serviceInterface': 'org.freedesktop.DBus.Properties',
'unit': 'xyz.openbmc_project.Sensor.Value.Unit.Watts'
}
sampleDcmiSensor = {
"instance": 1,
"dbus": "/xyz/openbmc_project/sensors/temperature/p0_core0_temp",
"record_id": 91
}
def openYaml(f):
return yaml.load(open(f))
def saveYaml(y, f, safe=True):
if safe:
noaliasDumper = yaml.dumper.SafeDumper
noaliasDumper.ignore_aliases = lambda self, data: True
yaml.dump(y, open(f, "w"), default_flow_style=False,
Dumper=noaliasDumper)
else:
yaml.dump(y, open(f, "w"))
def getEntityIdAndNamePattern(p, intfs, m):
key = (p, intfs)
match = m.get(key, None)
if match is None:
# Workaround for P8's occ sensors, where the path look like
# /org/open_power/control/occ_3_0050
if '/org/open_power/control/occ' in p \
and 'org.open_power.OCC.Status' in intfs:
return (210, 'nameLeaf')
raise Exception('Unable to find sensor', key, 'from map')
return (m[key]['entityID'], m[key]['sensorNamePattern'])
# Global entity instances
entityInstances = {}
def getEntityInstance(id):
instanceId = entityInstances.get(id, 0)
instanceId = instanceId + 1
entityInstances[id] = instanceId
print("EntityId:", id, "InstanceId:", instanceId)
return instanceId
def loadRpt(rptFile):
sensors = []
with open(rptFile) as f:
next(f)
next(f)
for line in f:
fields = line.strip().split('|')
fields = list(map(str.strip, fields))
sensor = RptSensor(
fields[0],
int(fields[2], 16) if fields[2] else None,
int(fields[3], 16) if fields[3] else None,
int(fields[4], 16) if fields[4] else None,
int(fields[5], 16) if fields[5] else None,
int(fields[7], 16) if fields[7] else None,
fields[9])
# print(sensor)
sensors.append(sensor)
return sensors
def getDimmTempPath(p):
# Convert path like: /sys-0/node-0/motherboard-0/dimmconn-0/dimm-0
# to: /xyz/openbmc_project/sensors/temperature/dimm0_temp
import re
dimmconn = re.search(r'dimmconn-\d+', p).group()
dimmId = re.search(r'\d+', dimmconn).group()
return '/xyz/openbmc_project/sensors/temperature/dimm{}_temp'.format(dimmId)
def getMembufTempPath(name):
# Convert names like MEMBUF0_Temp or CENTAUR0_Temp
# to: /xyz/openbmc_project/sensors/temperature/membuf0_temp
# to: /xyz/openbmc_project/sensors/temperature/centaur0_temp
return '/xyz/openbmc_project/sensors/temperature/{}'.format(name.lower())
def getCoreTempPath(name, p):
# For different rpts:
# Convert path like:
# /sys-0/node-0/motherboard-0/proc_socket-0/module-0/p9_proc_s/eq0/ex0/core0 (for P9)
# to: /xyz/openbmc_project/sensors/temperature/p0_core0_temp
# or name like: CORE0_Temp (for P8)
# to: /xyz/openbmc_project/sensors/temperature/core0_temp (for P8)
import re
if 'p9_proc' in p:
splitted = p.split('/')
socket = re.search(r'\d+', splitted[4]).group()
core = re.search(r'\d+', splitted[9]).group()
return '/xyz/openbmc_project/sensors/temperature/p{}_core{}_temp'.format(socket, core)
else:
core = re.search(r'\d+', name).group()
return '/xyz/openbmc_project/sensors/temperature/core{}_temp'.format(core)
def getPowerPath(name):
# Convert name like Proc0_Power
# to: /xyz/openbmc_project/sensors/power/p0_power
import re
r = re.search(r'\d+', name)
if r:
index = r.group()
else:
# Handle cases like IO_A_Power, Storage_Power_A
r = re.search(r'_[A|B|C|D]', name).group()[-1]
index = str(ord(r) - ord('A'))
prefix = 'p'
m = None
if 'memory_proc' in name.lower():
prefix = None
m = 'centaur'
elif 'pcie_proc' in name.lower():
m = 'pcie'
elif 'io' in name.lower():
m = 'io'
elif 'fan' in name.lower():
m = 'fan'
elif 'storage' in name.lower():
m = 'disk'
elif 'total' in name.lower():
prefix = None
m = 'total'
elif 'proc' in name.lower():
# Default
pass
ret = '/xyz/openbmc_project/sensors/power/'
if prefix:
ret = ret + prefix + index
if m:
if prefix:
ret = ret + '_' + m
else:
ret = ret + m
if prefix is None:
ret = ret + index
ret = ret + '_power'
return ret
def getDimmTempConfig(s):
r = sampleDimmTemp.copy()
r['entityInstance'] = getEntityInstance(r['entityID'])
r['path'] = getDimmTempPath(s.targetPath)
return r
def getMembufTempConfig(s):
r = sampleDimmTemp.copy()
r['entityID'] = 0xD1
r['entityInstance'] = getEntityInstance(r['entityID'])
r['path'] = getMembufTempPath(s.name)
return r
def getCoreTempConfig(s):
r = sampleCoreTemp.copy()
r['entityInstance'] = getEntityInstance(r['entityID'])
r['path'] = getCoreTempPath(s.name, s.targetPath)
return r
def getPowerConfig(s):
r = samplePower.copy()
r['entityInstance'] = getEntityInstance(r['entityID'])
r['path'] = getPowerPath(s.name)
return r
def isCoreTemp(p):
import re
m = re.search(r'p\d+_core\d+_temp', p)
return m is not None
def getDcmiSensor(i, sensor):
import re
path = sensor['path']
name = path.split('/')[-1]
m = re.findall(r'\d+', name)
socket, core = int(m[0]), int(m[1])
instance = socket * 24 + core + 1
r = sampleDcmiSensor.copy()
r['instance'] = instance
r['dbus'] = path
r['record_id'] = i
return r
def saveJson(data, f):
import json
with open(f, 'w') as outfile:
json.dump(data, outfile, indent=4)
def main():
parser = argparse.ArgumentParser(
description='Yaml tool for updating ipmi sensor yaml config')
parser.add_argument('-i', '--input', required=True, dest='input',
help='The ipmi sensor yaml config')
parser.add_argument('-o', '--output', required=True, dest='output',
help='The output yaml file')
parser.add_argument('-m', '--map', dest='map', default='sensor_map.yaml',
help='The sample map yaml file')
parser.add_argument('-r', '--rpt', dest='rpt',
help='The .rpt file generated by op-build')
parser.add_argument('-f', '--fix', action='store_true',
help='Fix entities and sensorNamePattern')
# -g expects output as yaml for mapping of entityID/sensorNamePattern
# -d expects output as json config for dcmi sensors
# Do not mess the output by enforcing only one argument is passed
# TODO: -f and -r could be used together, and they are conflicted with -g or -d
group = parser.add_mutually_exclusive_group()
group.add_argument('-g', '--generate', action='store_true',
help='Generate maps for entityID and sensorNamePattern')
group.add_argument('-d', '--dcmi', action='store_true',
help='Generate dcmi sensors json config')
args = parser.parse_args()
args = vars(args)
if args['input'] is None or args['output'] is None:
parser.print_help()
exit(1)
y = openYaml(args['input'])
if args['fix']:
# Fix entities and sensorNamePattern
m = openYaml(args['map'])
for i in y:
path = y[i]['path']
intfs = tuple(sorted(list(y[i]['interfaces'].keys())))
entityId, namePattern = getEntityIdAndNamePattern(path, intfs, m)
y[i]['entityID'] = entityId
y[i]['entityInstance'] = getEntityInstance(entityId)
y[i]['sensorNamePattern'] = namePattern
print(y[i]['path'], "id:", entityId,
"instance:", y[i]['entityInstance'])
sensorIds = list(y.keys())
if args['rpt']:
unhandledSensors = []
rptSensors = loadRpt(args['rpt'])
for s in rptSensors:
if s.sensorId is not None and s.sensorId not in sensorIds:
print("Sensor ID", s.sensorId, "not in yaml:",
s.name, ", path:", s.targetPath)
isAdded = False
if 'temp' in s.name.lower():
if 'dimm' in s.targetPath.lower():
y[s.sensorId] = getDimmTempConfig(s)
isAdded = True
elif 'core' in s.targetPath.lower():
y[s.sensorId] = getCoreTempConfig(s)
isAdded = True
elif 'centaur' in s.name.lower() or 'membuf' in s.name.lower():
y[s.sensorId] = getMembufTempConfig(s)
isAdded = True
elif s.name.lower().endswith('_power'):
y[s.sensorId] = getPowerConfig(s)
isAdded = True
if isAdded:
print('Added sensor id:', s.sensorId,
', path:', y[s.sensorId]['path'])
else:
unhandledSensors.append(s)
print('Unhandled sensors:')
for s in unhandledSensors:
print(s)
if args['generate']:
m = {}
for i in y:
path = y[i]['path']
intfs = tuple(sorted(list(y[i]['interfaces'].keys())))
entityId = y[i]['entityID']
sensorNamePattern = y[i]['sensorNamePattern']
m[(path, intfs)] = {'entityID': entityId,
'sensorNamePattern': sensorNamePattern}
y = m
if args['dcmi']:
d = []
for i in y:
if isCoreTemp(y[i]['path']):
s = getDcmiSensor(i, y[i])
d.append(s)
print(s)
saveJson(d, args['output'])
return
safe = False if args['generate'] else True
saveYaml(y, args['output'], safe)
if __name__ == "__main__":
main()