2
0
mirror of https://github.com/xcat2/confluent.git synced 2025-04-09 17:34:14 +00:00

Rework ipmi plugin

IPMI plugin is simplified and able to do multi-node requests.
This commit is contained in:
Jarrod Johnson 2015-03-17 17:04:32 -04:00
parent 6a3025837b
commit 8d09fb4064

View File

@ -20,7 +20,7 @@ import eventlet
import eventlet.event
import eventlet.green.threading as threading
import eventlet.greenpool as greenpool
import eventlet.queue
import eventlet.queue as queue
import pyghmi.constants as pygconstants
import pyghmi.exceptions as pygexc
import pyghmi.ipmi.console as console
@ -30,6 +30,8 @@ import socket
console.session.select = eventlet.green.select
console.session.threading = eventlet.green.threading
_ipmiworkers = greenpool.GreenPool()
_ipmithread = None
_ipmiwaiters = []
@ -39,6 +41,7 @@ sensor_categories = {
'fans': frozenset(['Fan', 'Cooling Device']),
}
def simplify_name(name):
return name.lower().replace(' ', '_')
@ -46,9 +49,9 @@ def simplify_name(name):
class IpmiCommandWrapper(ipmicommand.Command):
def __init__(self, node, cfm, **kwargs):
self._attribwatcher = cfm.watch_attributes(
(node,),('secret.hardwaremanagementuser',
'secret.hardwaremanagementpassword', 'secret.ipmikg',
'hardwaremanagement.manager'), self._attribschanged)
(node,), ('secret.hardwaremanagementuser',
'secret.hardwaremanagementpassword', 'secret.ipmikg',
'hardwaremanagement.manager'), self._attribschanged)
super(self.__class__, self).__init__(**kwargs)
def _attribschanged(self, nodeattribs, configmanager, **kwargs):
@ -59,6 +62,7 @@ class IpmiCommandWrapper(ipmicommand.Command):
# then do nothing
pass
def _ipmi_evtloop():
while True:
try:
@ -89,8 +93,8 @@ def get_conn_params(node, configdata):
kg = configdata['secret.ipmikg']['value']
else:
kg = passphrase
#TODO(jbjohnso): check if the end has some number after a : without []
#for non default port
# TODO(jbjohnso): check if the end has some number after a : without []
# for non default port
return {
'username': username,
'passphrase': passphrase,
@ -189,55 +193,54 @@ class IpmiConsole(conapi.Console):
self.solconnection.send_break()
class IpmiIterator(object):
def __init__(self, operator, nodes, element, cfg, inputdata):
self.currdata = None
crypt = cfg.decrypt
cfg.decrypt = True
configdata = cfg.get_node_attributes(nodes, _configattributes)
cfg.decrypt = crypt
self.gpile = greenpool.GreenPile()
for node in nodes:
self.gpile.spawn(perform_request, operator, node, element,
configdata, inputdata, cfg)
def __iter__(self):
return self
def next(self):
if self.currdata is None:
self.currdata = self.gpile.next()
# need to apply any translations between pyghmi and confluent
try:
retdata = self.currdata.next()
except AttributeError:
if hasattr(self.currdata, 'next'):
# the attribute error is not the immediate
# one, raise it to be caught as normal
raise
retdata = self.currdata
self.currdata = None
return retdata
def perform_request(operator, node, element, configdata, inputdata, cfg):
try:
return IpmiHandler(operator, node, element, configdata, inputdata, cfg
).handle_request()
except pygexc.IpmiException as ipmiexc:
excmsg = str(ipmiexc)
if excmsg == 'Session no longer connected':
return msg.ConfluentTargetTimeout(node)
def perform_requests(operator, nodes, element, cfg, inputdata):
cryptit = cfg.decrypt
cfg.decrypt = True
configdata = cfg.get_node_attributes(nodes, _configattributes)
cfg.decrypt = cryptit
resultdata = queue.LightQueue()
pendingnum = len(nodes)
for node in nodes:
_ipmiworkers.spawn_n(
perform_request, operator, node, element, configdata, inputdata,
cfg, resultdata)
while pendingnum:
datum = resultdata.get()
if datum == 'Done':
pendingnum -= 1
else:
raise
yield datum
def perform_request(operator, node, element,
configdata, inputdata, cfg, results):
try:
return IpmiHandler(operator, node, element, configdata, inputdata,
cfg, results).handle_request()
except pygexc.IpmiException as ipmiexc:
excmsg = str(ipmiexc)
if excmsg == 'Session no longer connected':
results.put(msg.ConfluentTargetTimeout(node))
else:
raise
finally:
results.put('Done')
persistent_ipmicmds = {}
def _dict_sensor(pygreading):
retdict = {'name': pygreading.name, 'value': pygreading.value,
'states': pygreading.states, 'units': pygreading.units,
'health': _str_health(pygreading.health)}
return retdict
class IpmiHandler(object):
def __init__(self, operation, node, element, cfd, inputdata, cfg):
def __init__(self, operation, node, element, cfd, inputdata, cfg, output):
self.sensormap = {}
self.output = output
self.sensorcategory = None
self.broken = False
self.error = None
@ -284,24 +287,23 @@ class IpmiHandler(object):
self._logevt = None
if self.broken:
if self.error == 'timeout':
return iter([msg.ConfluentTargetTimeout(self.node)])
self.output.put(msg.ConfluentTargetTimeout(self.node))
elif ('Unauthorized' in self.error or
'Incorrect password' in self.error):
return iter([msg.ConfluentTargetInvalidCredentials(self.node)])
elif 'Incorrect password' in self.error:
return iter([C])
self.output.put(
msg.ConfluentTargetInvalidCredentials(self.node))
else:
raise Exception(self.error)
if self.element == ['power', 'state']:
return self.power()
self.power()
elif self.element == ['boot', 'nextdevice']:
return self.bootdevice()
self.bootdevice()
elif self.element == ['health', 'hardware']:
return self.health()
self.health()
elif self.element == ['identify']:
return self.identify()
self.identify()
elif self.element[0] == 'sensors':
return self.handle_sensors()
self.handle_sensors()
def make_sensor_map(self, sensors=None):
if sensors is None:
@ -310,7 +312,6 @@ class IpmiHandler(object):
resourcename = sensor['name']
self.sensormap[simplify_name(resourcename)] = resourcename
def read_sensors(self, sensorname):
try:
if sensorname == 'all':
@ -318,23 +319,25 @@ class IpmiHandler(object):
readings = []
for sensor in filter(self.match_sensor, sensors):
try:
reading = self.ipmicmd.get_sensor_reading(sensor['name'])
reading = self.ipmicmd.get_sensor_reading(
sensor['name'])
except pygexc.IpmiException as ie:
if ie.ipmicode == 203:
continue
raise
readings.append(self._dict_sensor(reading))
yield msg.SensorReadings(readings, name=self.node)
readings.append(_dict_sensor(reading))
self.output.put(msg.SensorReadings(readings, name=self.node))
else:
self.make_sensor_map()
if sensorname not in self.sensormap:
raise exc.NotFoundException('No such sensor')
reading = self.ipmicmd.get_sensor_reading(
self.sensormap[sensorname])
yield msg.SensorReadings([self._dict_sensor(reading)],
name=self.node)
self.output.put(
msg.SensorReadings([_dict_sensor(reading)],
name=self.node))
except pygexc.IpmiException:
yield msg.ConfluentTargetTimeout(self.node)
self.output.put(msg.ConfluentTargetTimeout(self.node))
def handle_sensors(self):
if self.element[-1] == '':
@ -358,45 +361,27 @@ class IpmiHandler(object):
try:
sensors = self.ipmicmd.get_sensor_descriptions()
except pygexc.IpmiException:
yield msg.ConfluentTargetTimeout(self.node)
yield msg.ChildCollection('all')
self.output.put(msg.ConfluentTargetTimeout(self.node))
return
self.output.put(msg.ChildCollection('all'))
for sensor in filter(self.match_sensor, sensors):
yield msg.ChildCollection(simplify_name(sensor['name']))
@staticmethod
def _str_health(health):
if pygconstants.Health.Failed & health:
health = 'failed'
elif pygconstants.Health.Critical & health:
health = 'critical'
elif pygconstants.Health.Warning & health:
health = 'warning'
else:
health = 'ok'
return health
def _dict_sensor(self, pygreading):
retdict = {'name': pygreading.name, 'value': pygreading.value,
'states': pygreading.states, 'units': pygreading.units,
'health': self._str_health(pygreading.health)}
return retdict
self.output.put(msg.ChildCollection(simplify_name(sensor['name'])))
def health(self):
if 'read' == self.op:
try:
response = self.ipmicmd.get_health()
except pygexc.IpmiException:
yield msg.ConfluentTargetTimeout(self.node)
self.output.put(msg.ConfluentTargetTimeout(self.node))
return
health = response['health']
health = self._str_health(health)
yield msg.HealthSummary(health, self.node)
health = _str_health(health)
self.output.put(msg.HealthSummary(health, self.node))
if 'badreadings' in response:
badsensors = []
for reading in response['badreadings']:
badsensors.append(self._dict_sensor(reading))
yield msg.SensorReadings(badsensors, name=self.node)
badsensors.append(_dict_sensor(reading))
self.output.put(msg.SensorReadings(badsensors, name=self.node))
else:
raise exc.InvalidArgumentException('health is read-only')
@ -411,40 +396,54 @@ class IpmiHandler(object):
bootmode = 'uefi'
else:
bootmode = 'bios'
return msg.BootDevice(node=self.node,
device=bootdev['bootdev'],
bootmode=bootmode)
self.output.put(msg.BootDevice(node=self.node,
device=bootdev['bootdev'],
bootmode=bootmode))
elif 'update' == self.op:
bootdev = self.inputdata.bootdevice(self.node)
bootmode = self.inputdata.bootmode(self.node)
bootdev = self.ipmicmd.set_bootdev(bootdev)
douefi = False
if self.inputdata.bootmode(self.node) == 'uefi':
douefi = True
bootdev = self.ipmicmd.set_bootdev(bootdev, uefiboot=douefi)
if bootdev['bootdev'] in self.bootdevices:
bootdev['bootdev'] = self.bootdevices[bootdev['bootdev']]
return msg.BootDevice(node=self.node,
device=bootdev['bootdev'])
self.output.put(msg.BootDevice(node=self.node,
device=bootdev['bootdev']))
def identify(self):
if 'update' == self.op:
identifystate = self.inputdata.inputbynode[self.node] == 'on'
self.ipmicmd.set_identify(on=identifystate)
return msg.IdentifyState(
node=self.node, state=self.inputdata.inputbynode[self.node])
self.output.put(msg.IdentifyState(
node=self.node, state=self.inputdata.inputbynode[self.node]))
elif 'read' == self.op:
# ipmi has identify as read-only for now
return msg.IdentifyState(node=self.node, state='')
self.output.put(msg.IdentifyState(node=self.node, state=''))
def power(self):
if 'read' == self.op:
power = self.ipmicmd.get_power()
return msg.PowerState(node=self.node,
state=power['powerstate'])
self.output.put(msg.PowerState(node=self.node,
state=power['powerstate']))
elif 'update' == self.op:
powerstate = self.inputdata.powerstate(self.node)
#TODO: call with wait argument
# TODO: call with wait argument
self.ipmicmd.set_power(powerstate)
power = self.ipmicmd.get_power()
return msg.PowerState(node=self.node,
state=power['powerstate'])
self.output.put(msg.PowerState(node=self.node,
state=power['powerstate']))
def _str_health(health):
if pygconstants.Health.Failed & health:
health = 'failed'
elif pygconstants.Health.Critical & health:
health = 'critical'
elif pygconstants.Health.Warning & health:
health = 'warning'
else:
health = 'ok'
return health
def initthread():
@ -460,7 +459,8 @@ def create(nodes, element, configmanager, inputdata):
raise Exception("_console/session does not support multiple nodes")
return IpmiConsole(nodes[0], configmanager)
else:
return IpmiIterator('update', nodes, element, configmanager, inputdata)
return perform_requests(
'update', nodes, element, configmanager, inputdata)
def update(nodes, element, configmanager, inputdata):
@ -470,4 +470,4 @@ def update(nodes, element, configmanager, inputdata):
def retrieve(nodes, element, configmanager, inputdata):
initthread()
return IpmiIterator('read', nodes, element, configmanager, inputdata)
return perform_requests('read', nodes, element, configmanager, inputdata)