mirror of
https://github.com/xcat2/confluent.git
synced 2026-08-27 17:16:42 +00:00
d9d9fa3df7
The unhandled tails of handle_request, handle_configuration and handle_alerts
raised a bare Exception('Not implemented'), so asking for a resource the
transport has no code for was reported as an unexpected error and logged with a
traceback. management_controller/location over ipmi is one such resource: R5
implemented it for redfish only.
Raise UnsupportedFunctionality naming the resource instead, which the plugins
already treat as its own case rather than a fault, and give decode_alert over
redfish the same treatment. Any resource added to the tree without an
implementation on one transport now reports that plainly.
1786 lines
75 KiB
Python
1786 lines
75 KiB
Python
# Copyright 2014 IBM Corporation
|
|
# Copyright 2015-2018 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 asyncio
|
|
import confluent.exceptions as exc
|
|
from confluent.exceptions import exc_text
|
|
import confluent.firmwaremanager as firmwaremanager
|
|
import confluent.interface.console as conapi
|
|
import confluent.messages as msg
|
|
import confluent.util as util
|
|
import copy
|
|
import errno
|
|
from fnmatch import fnmatch
|
|
import io
|
|
import os
|
|
import pwd
|
|
import aiohmi.constants as pygconstants
|
|
import aiohmi.exceptions as pygexc
|
|
import aiohmi.storage as storage
|
|
import aiohmi.ipmi.console as console
|
|
import aiohmi.ipmi.command as ipmicommand
|
|
import socket
|
|
import ssl
|
|
import traceback
|
|
import confluent.vinzmanager as vinzmanager
|
|
|
|
|
|
if not hasattr(ssl, 'SSLEOFError'):
|
|
ssl.SSLEOFError = None
|
|
|
|
try:
|
|
range = xrange
|
|
except NameError:
|
|
pass
|
|
|
|
pci_cache = {}
|
|
|
|
class RetainedIO(io.BytesIO):
|
|
# Need to retain buffer after close
|
|
def __init__(self):
|
|
self.resultbuffer = None
|
|
def close(self):
|
|
buf = self.getbuffer()
|
|
self.resultbuffer = bytes(buf)
|
|
del buf
|
|
super().close()
|
|
|
|
def get_dns_txt(qstring):
|
|
return None
|
|
# return support.greendns.resolver.query(
|
|
# qstring, 'TXT')[0].strings[0].replace('i=', '')
|
|
|
|
|
|
def match_aliases(first, second):
|
|
aliases = {
|
|
('bmc', 'xcc')
|
|
}
|
|
for alias in aliases:
|
|
if first in alias and second in alias:
|
|
return True
|
|
return False
|
|
|
|
def get_pci_text_from_ids(subdevice, subvendor, device, vendor):
|
|
fqpi = '{0}.{1}.{2}.{3}'.format(subdevice, subvendor, device, vendor)
|
|
if fqpi in pci_cache:
|
|
return pci_cache[fqpi]
|
|
vendorstr = None
|
|
try:
|
|
vendorstr = get_dns_txt('{0}.pci.id.ucw.cz'.format(subvendor))
|
|
except Exception:
|
|
try:
|
|
vendorstr = get_dns_txt('{0}.pci.id.ucw.cz'.format(vendor))
|
|
except Exception:
|
|
pass
|
|
devstr = None
|
|
try:
|
|
devstr = get_dns_txt(fqpi + '.pci.id.ucw.cz')
|
|
except Exception:
|
|
try:
|
|
devstr = get_dns_txt('{0}.{1}.pci.id.ucw.cz'.format(
|
|
device, vendor))
|
|
except Exception:
|
|
pass
|
|
if vendorstr and devstr:
|
|
pci_cache[fqpi] = vendorstr, devstr
|
|
return vendorstr, devstr
|
|
|
|
|
|
class NullLock(object):
|
|
|
|
def donothing(self, *args, **kwargs):
|
|
return 1
|
|
|
|
__enter__ = donothing
|
|
__exit__ = donothing
|
|
acquire = donothing
|
|
release = donothing
|
|
|
|
|
|
_ipmithread = None
|
|
_ipmiwaiters = []
|
|
|
|
sensor_categories = {
|
|
'temperature': frozenset(['Temperature']),
|
|
'energy': frozenset(['Energy']),
|
|
'power': frozenset(['Power', 'Current']),
|
|
'fans': frozenset(['Fan', 'Cooling Device']),
|
|
}
|
|
|
|
|
|
class EmptySensor(object):
|
|
def __init__(self, name):
|
|
self.name = name
|
|
self.value = None
|
|
self.states = ['Unavailable']
|
|
self.units = None
|
|
self.health = 'ok'
|
|
|
|
|
|
def hex2bin(hexstring):
|
|
hexvals = hexstring.split(':')
|
|
if len(hexvals) < 2:
|
|
hexvals = hexstring.split(' ')
|
|
if len(hexvals) < 2:
|
|
hexvals = [hexstring[i:i+2] for i in range(0, len(hexstring), 2)]
|
|
bytedata = [int(i, 16) for i in hexvals]
|
|
return bytearray(bytedata)
|
|
|
|
|
|
def simplify_name(name):
|
|
return name.lower().replace(' ', '_').replace('/', '-').replace(
|
|
'_-_', '-')
|
|
|
|
|
|
def sanitize_invdata(indata):
|
|
"""Sanitize pyghmi data
|
|
|
|
pyghmi will return bytearrays when it has no idea what to do. In our
|
|
case, we will change those to hex strings. Additionally, ignore 'extra'
|
|
fields if the oem_parser is set
|
|
"""
|
|
if 'oem_parser' in indata and indata['oem_parser'] is not None:
|
|
if 'board_extra' in indata:
|
|
del indata['board_extra']
|
|
if 'chassis_extra' in indata:
|
|
del indata['chassis_extra']
|
|
if 'product_extra' in indata:
|
|
del indata['product_extra']
|
|
for k in indata:
|
|
if isinstance(indata[k], bytearray):
|
|
indata[k] = '0x' + ''.join(format(x, '02x') for x in indata[k])
|
|
elif isinstance(indata[k], dict):
|
|
sanitize_invdata(indata[k])
|
|
elif isinstance(indata[k], list):
|
|
for idx, value in enumerate(indata[k]):
|
|
if isinstance(value, bytearray):
|
|
indata[k][idx] = '0x' + ''.join(
|
|
format(x, '02x') for x in indata[k][idx])
|
|
|
|
|
|
class IpmiCommandWrapper(ipmicommand.Command):
|
|
@classmethod
|
|
async def create(cls, node, cfm, **kwargs):
|
|
kwargs['keepalive'] = False
|
|
self = await super().create(**kwargs)
|
|
self.confluentbmcname = kwargs['bmc']
|
|
self.cfm = cfm
|
|
self.node = node
|
|
self.sensormap = {}
|
|
self._inhealth = False
|
|
self._lasthealth = None
|
|
self._attribwatcher = cfm.watch_attributes(
|
|
(node,), ('secret.hardwaremanagementuser', 'collective.manager',
|
|
'secret.hardwaremanagementpassword', 'secret.ipmikg',
|
|
'hardwaremanagement.manager'), self._attribschanged)
|
|
self.setup_confluent_keyhandler()
|
|
try:
|
|
os.makedirs('/var/cache/confluent/ipmi/')
|
|
except OSError as e:
|
|
if e.errno != errno.EEXIST or not os.path.isdir(
|
|
'/var/cache/confluent/ipmi/'):
|
|
raise
|
|
try:
|
|
self.set_sdr_cachedir('/var/cache/confluent/ipmi/')
|
|
except Exception:
|
|
pass
|
|
return self
|
|
|
|
|
|
def setup_confluent_keyhandler(self):
|
|
self.register_key_handler(util.TLSCertVerifier(
|
|
self.cfm, self.node, 'pubkeys.tls_hardwaremanager').verify_cert)
|
|
|
|
def close_confluent(self):
|
|
if self._attribwatcher:
|
|
self.cfm.remove_watcher(self._attribwatcher)
|
|
self._attribwatcher = None
|
|
|
|
async def _attribschanged(self, nodeattribs, configmanager, **kwargs):
|
|
try:
|
|
await self.ipmi_session._mark_broken()
|
|
except AttributeError:
|
|
# if ipmi_session doesn't already exist,
|
|
# then do nothing
|
|
pass
|
|
|
|
async def get_health(self):
|
|
if self._inhealth:
|
|
while self._inhealth:
|
|
await asyncio.sleep(0.1)
|
|
return self._lasthealth
|
|
self._inhealth = True
|
|
try:
|
|
self._lasthealth = await super(IpmiCommandWrapper, self).get_health()
|
|
except Exception:
|
|
self._inhealth = False
|
|
raise
|
|
self._inhealth = False
|
|
return self._lasthealth
|
|
|
|
|
|
async def _ipmi_evtloop():
|
|
while True:
|
|
try:
|
|
await console.session.Session.wait_for_rsp(timeout=600)
|
|
while _ipmiwaiters:
|
|
waiter = _ipmiwaiters.pop()
|
|
waiter.send()
|
|
except asyncio.CancelledError:
|
|
break
|
|
except: # TODO(jbjohnso): log the trace into the log
|
|
|
|
traceback.print_exc()
|
|
|
|
|
|
def get_conn_params(node, configdata):
|
|
if 'secret.hardwaremanagementuser' in configdata:
|
|
username = configdata['secret.hardwaremanagementuser']['value']
|
|
else:
|
|
username = 'USERID'
|
|
if 'secret.hardwaremanagementpassword' in configdata:
|
|
passphrase = configdata['secret.hardwaremanagementpassword']['value']
|
|
else:
|
|
passphrase = 'PASSW0RD' # for lack of a better guess
|
|
if 'hardwaremanagement.manager' in configdata:
|
|
bmc = configdata['hardwaremanagement.manager']['value']
|
|
else:
|
|
bmc = node
|
|
bmc = bmc.split('/', 1)[0]
|
|
if 'secret.ipmikg' in 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
|
|
return {
|
|
'username': username,
|
|
'passphrase': passphrase,
|
|
'kg': kg,
|
|
'bmc': bmc,
|
|
'port': 623,
|
|
}
|
|
|
|
|
|
_configattributes = ('secret.hardwaremanagementuser',
|
|
'secret.hardwaremanagementpassword',
|
|
'secret.ipmikg', 'hardwaremanagement.manager')
|
|
|
|
|
|
def _donothing(data):
|
|
# a dummy function to avoid some awkward exceptions from
|
|
# zombie pyghmi console objects
|
|
pass
|
|
|
|
|
|
class IpmiConsole(conapi.Console):
|
|
configattributes = frozenset(_configattributes)
|
|
bmctonodemapping = {}
|
|
|
|
def __init__(self, node, config):
|
|
self.error = None
|
|
self.datacallback = None
|
|
crypt = config.decrypt
|
|
self.solconnection = None
|
|
config.decrypt = True
|
|
self.broken = False
|
|
configdata = config.get_node_attributes([node], _configattributes)
|
|
connparams = get_conn_params(node, configdata[node])
|
|
config.decrypt = crypt
|
|
self.username = connparams['username']
|
|
self.password = connparams['passphrase']
|
|
self.kg = connparams['kg']
|
|
self.bmc = connparams['bmc']
|
|
self.port = connparams['port']
|
|
self.connected = False
|
|
# ok, is self.bmc unique among nodes already
|
|
# Cannot actually create console until 'connect', when we get callback
|
|
if (self.bmc in self.bmctonodemapping and
|
|
self.bmctonodemapping[self.bmc] != node):
|
|
raise Exception(
|
|
"Duplicate hardwaremanagement.manager attribute for {0} and {1}".format(
|
|
node, self.bmctonodemapping[self.bmc]))
|
|
self.bmctonodemapping[self.bmc] = node
|
|
|
|
def __del__(self):
|
|
self.solconnection = None
|
|
try:
|
|
del self.bmctonodemapping[self.bmc]
|
|
except KeyError:
|
|
pass
|
|
|
|
async def handle_data(self, data):
|
|
if type(data) == dict:
|
|
if 'error' in data:
|
|
self.solconnection = None
|
|
self.broken = True
|
|
self.error = data['error']
|
|
if self.connected:
|
|
self.connected = False
|
|
await self.datacallback(conapi.ConsoleEvent.Disconnect)
|
|
else:
|
|
await self.datacallback(data)
|
|
|
|
async def connect(self, callback):
|
|
self.datacallback = callback
|
|
# we provide a weak reference to pyghmi as otherwise we'd
|
|
# have a circular reference and reference counting would never get
|
|
# out...
|
|
try:
|
|
self.solconnection = console.Console(bmc=self.bmc, port=self.port,
|
|
userid=self.username,
|
|
password=self.password,
|
|
kg=self.kg, force=True,
|
|
iohandler=self.handle_data)
|
|
self.solconnection.outputlock = NullLock()
|
|
await self.solconnection.connect()
|
|
if (self.broken or not self.solconnection or
|
|
self.solconnection.broken or
|
|
self.solconnection.ipmi_session.broken):
|
|
if not self.error:
|
|
self.error = 'Unknown error'
|
|
if (self.error.startswith('Incorrect password') or
|
|
self.error.startswith('Unauthorized name')):
|
|
raise exc.TargetEndpointBadCredentials()
|
|
else:
|
|
raise exc.TargetEndpointUnreachable(self.error)
|
|
self.connected = True
|
|
except socket.gaierror as err:
|
|
raise exc.TargetEndpointUnreachable(str(err))
|
|
|
|
async def close(self):
|
|
if self.solconnection is not None:
|
|
# break the circular reference here
|
|
self.solconnection.out_handler = _donothing
|
|
await self.solconnection.close()
|
|
self.solconnection = None
|
|
self.datacallback = None
|
|
self.broken = True
|
|
self.error = "closed"
|
|
|
|
async def write(self, data):
|
|
if self.solconnection:
|
|
await self.solconnection.send_data(data)
|
|
|
|
async def send_break(self):
|
|
if self.solconnection:
|
|
await self.solconnection.send_break()
|
|
|
|
|
|
async def perform_requests(operator, nodes, element, cfg, inputdata, realop):
|
|
cryptit = cfg.decrypt
|
|
cfg.decrypt = True
|
|
configdata = cfg.get_node_attributes(nodes, _configattributes)
|
|
cfg.decrypt = cryptit
|
|
resultdata = asyncio.Queue()
|
|
livingthreads = set([])
|
|
numnodes = len(nodes)
|
|
for node in nodes:
|
|
livingthreads.add(asyncio.create_task(perform_request(operator, node, element, configdata, inputdata,
|
|
cfg, resultdata, realop)))
|
|
#livingthreads.add(_ipmiworkers.spawn(
|
|
# perform_request, operator, node, element, configdata, inputdata,
|
|
# cfg, resultdata, realop))
|
|
while livingthreads:
|
|
try:
|
|
bundle = []
|
|
datum = await asyncio.wait_for(resultdata.get(), timeout=10.0)
|
|
while datum:
|
|
if datum == 'Done':
|
|
datum = None
|
|
break
|
|
if isinstance(datum, Exception):
|
|
raise datum
|
|
if (hasattr(datum, 'kvpairs') and datum.kvpairs and
|
|
len(datum.kvpairs) == 1):
|
|
bundle.append((list(datum.kvpairs)[0], datum))
|
|
numnodes -= 1
|
|
else:
|
|
yield datum
|
|
timeout = 0.1 if numnodes else 0.001
|
|
datum = await asyncio.wait_for(resultdata.get(), timeout=timeout)
|
|
except asyncio.QueueEmpty:
|
|
pass
|
|
except asyncio.TimeoutError:
|
|
# the timeout is just a sign to push out pending bundled stuff
|
|
pass
|
|
finally:
|
|
for datum in sorted(
|
|
bundle, key=lambda x: util.naturalize_string(x[0])):
|
|
yield datum[1]
|
|
for t in list(livingthreads):
|
|
if t.done():
|
|
livingthreads.discard(t)
|
|
try:
|
|
# drain queue if a thread put something on the queue and died
|
|
while True:
|
|
datum = resultdata.get_nowait()
|
|
if datum != 'Done':
|
|
yield datum
|
|
await asyncio.sleep(0)
|
|
except asyncio.QueueEmpty:
|
|
pass
|
|
|
|
|
|
async def perform_request(operator, node, element,
|
|
configdata, inputdata, cfg, results, realop):
|
|
try:
|
|
ih = await IpmiHandler.create(
|
|
operator, node, element, configdata, inputdata, cfg, results,
|
|
realop)
|
|
return await ih.handle_request()
|
|
except pygexc.UnsupportedFunctionality as ue:
|
|
# Not an unexpected error, just something this target cannot do, so say
|
|
# so plainly and without a traceback in the log
|
|
await results.put(msg.ConfluentNodeError(node, exc_text(ue)))
|
|
except pygexc.IpmiException as ipmiexc:
|
|
excmsg = exc_text(ipmiexc)
|
|
if excmsg in ('Session no longer connected', 'timeout'):
|
|
await results.put(msg.ConfluentTargetTimeout(node))
|
|
elif 'Unauthorized name' in excmsg or 'Incorrect password provided' in excmsg:
|
|
await results.put(msg.ConfluentTargetInvalidCredentials(node))
|
|
else:
|
|
await results.put(msg.ConfluentNodeError(node, excmsg))
|
|
#raise
|
|
except exc.TargetEndpointUnreachable as tu:
|
|
await results.put(msg.ConfluentTargetTimeout(node, str(tu)))
|
|
except ssl.SSLEOFError:
|
|
await results.put(msg.ConfluentNodeError(
|
|
node, 'Unable to communicate with the https server on '
|
|
'the target BMC'))
|
|
except exc.PubkeyInvalid:
|
|
await results.put(msg.ConfluentNodeError(
|
|
node,
|
|
'Mismatch detected between target certificate fingerprint '
|
|
'and pubkeys.tls_hardwaremanager attribute'))
|
|
except pygexc.InvalidParameterValue as e:
|
|
await results.put(msg.ConfluentNodeError(node, exc_text(e)))
|
|
except Exception as e:
|
|
await results.put(msg.ConfluentNodeError(
|
|
node, 'Unexpected Error: {0}'.format(exc_text(e))))
|
|
traceback.print_exc()
|
|
finally:
|
|
await results.put('Done')
|
|
|
|
persistent_ipmicmds = {}
|
|
|
|
class IpmiHandler:
|
|
@classmethod
|
|
async def create(cls, operation, node, element, cfd, inputdata, cfg, output,
|
|
realop):
|
|
self = cls()
|
|
self.cfm = cfg
|
|
self.invmap = {}
|
|
self.output = output
|
|
self.sensorcategory = None
|
|
self.broken = False
|
|
self.error = None
|
|
await asyncio.sleep(0)
|
|
self.cfg = cfd[node]
|
|
self.current_user = cfg.current_user
|
|
self.loggedin = False
|
|
self.node = node
|
|
self.element = element
|
|
self.op = operation
|
|
self.realop = realop
|
|
connparams = get_conn_params(node, self.cfg)
|
|
self.ipmicmd = None
|
|
self.inputdata = inputdata
|
|
self.tenant = cfg.tenant
|
|
tenant = cfg.tenant
|
|
while ((node, tenant) not in persistent_ipmicmds or
|
|
not (persistent_ipmicmds[(node, tenant)].ipmi_session.logged or persistent_ipmicmds[(node, tenant)].ipmi_session.logging) or
|
|
persistent_ipmicmds[(node, tenant)].ipmi_session.broken):
|
|
try:
|
|
persistent_ipmicmds[(node, tenant)].close_confluent()
|
|
await persistent_ipmicmds[(node, tenant)].ipmi_session._mark_broken()
|
|
persistent_ipmicmds[(node, tenant)].ipmi_session.logonwaiters = []
|
|
except KeyError: # was no previous session
|
|
pass
|
|
try:
|
|
persistent_ipmicmds[(node, tenant)] = await IpmiCommandWrapper.create(
|
|
node, cfg, bmc=connparams['bmc'],
|
|
userid=connparams['username'],
|
|
password=connparams['passphrase'], kg=connparams['kg'],
|
|
port=connparams['port'])
|
|
ipmisess = persistent_ipmicmds[(node, tenant)].ipmi_session
|
|
self.ipmicmd = persistent_ipmicmds[(node, tenant)]
|
|
self.loggedin = True
|
|
begin = util.monotonic_time()
|
|
while ((not (ipmisess.broken or self.loggedin)) and
|
|
(util.monotonic_time() - begin) < 30):
|
|
await ipmisess.wait_for_rsp(31 - (util.monotonic_time() - begin))
|
|
if self.broken or self.loggedin:
|
|
break
|
|
cfd = cfg.get_node_attributes(node, _configattributes, decrypt=True)
|
|
self.cfg = cfd[node]
|
|
connparams = get_conn_params(node, self.cfg)
|
|
await ipmisess._mark_broken()
|
|
# raise exc.TargetEndpointUnreachable(
|
|
# "Login process to " + connparams['bmc'] + " died")
|
|
except socket.gaierror as ge:
|
|
if ge.errno == -2:
|
|
raise exc.TargetEndpointUnreachable(ge.strerror)
|
|
raise
|
|
self.ipmicmd = persistent_ipmicmds[(node, tenant)]
|
|
giveup = util.monotonic_time() + 60
|
|
while not self.ipmicmd.ipmi_session.broken and not self.ipmicmd.ipmi_session.logged and self.ipmicmd.ipmi_session.logging:
|
|
await self.ipmicmd.ipmi_session.wait_for_rsp(3)
|
|
if util.monotonic_time() > giveup:
|
|
self.ipmicmd.ipmi_session.broken = True
|
|
return self
|
|
|
|
bootdevices = {
|
|
'optical': 'cd'
|
|
}
|
|
|
|
def logged(self, response, ipmicmd):
|
|
if 'error' in response:
|
|
self.broken = True
|
|
self.error = response['error']
|
|
else:
|
|
self.ipmicmd = ipmicmd
|
|
self.loggedin = True
|
|
|
|
def _unsupported(self):
|
|
"""Say that the requested resource has no ipmi implementation.
|
|
|
|
Falling through to a bare exception would be reported as an unexpected
|
|
error and logged with a traceback, when all that happened is that this
|
|
transport does not offer the resource.
|
|
"""
|
|
return pygexc.UnsupportedFunctionality(
|
|
'"{0}" is not implemented for ipmi'.format(
|
|
'/'.join([str(x) for x in self.element])))
|
|
|
|
async def handle_request(self):
|
|
if self.broken:
|
|
if (self.error == 'timeout' or
|
|
'Insufficient resources' in self.error):
|
|
self.error = self.error.replace(' reported in RAKP4', '')
|
|
await self.output.put(msg.ConfluentTargetTimeout(
|
|
self.node, self.error))
|
|
return
|
|
elif 'Invalid Session ID' in self.error:
|
|
await self.output.put(msg.ConfluentTargetTimeout(
|
|
self.node, 'Temporary Login Error'))
|
|
return
|
|
elif ('Unauthorized' in self.error or
|
|
'Incorrect password' in self.error):
|
|
await self.output.put(
|
|
msg.ConfluentTargetInvalidCredentials(self.node))
|
|
return
|
|
else:
|
|
raise Exception(self.error)
|
|
if self.element == ['power', 'state']:
|
|
await self.power()
|
|
elif self.element == ['_enclosure', 'reseat_bay']:
|
|
await self.reseat_bay()
|
|
elif self.element == ['boot', 'nextdevice']:
|
|
await self.bootdevice()
|
|
elif self.element == ['health', 'hardware']:
|
|
await self.health()
|
|
elif self.element == ['identify']:
|
|
await self.identify()
|
|
elif self.element[0] == 'sensors':
|
|
await self.handle_sensors()
|
|
elif self.element[:2] == ['configuration', 'storage']:
|
|
await self.handle_storage()
|
|
elif self.element[0] == 'configuration':
|
|
await self.handle_configuration()
|
|
elif self.element[:3] == ['inventory', 'firmware', 'updates']:
|
|
await self.handle_update()
|
|
elif self.element[:3] == ['inventory', 'firmware', 'updatestatus']:
|
|
await self.handle_update_status()
|
|
elif self.element[0] == 'inventory':
|
|
await self.handle_inventory()
|
|
elif self.element == ['media', 'attach']:
|
|
await self.handle_attach_media()
|
|
elif self.element == ['media', 'detach']:
|
|
await self.handle_detach_media()
|
|
elif self.element == ['media', 'uploads']:
|
|
await self.handle_media_upload()
|
|
elif self.element == ['media', 'current']:
|
|
await self.handle_list_media()
|
|
elif self.element == ['events', 'hardware', 'log']:
|
|
await self.do_eventlog()
|
|
elif self.element == ['events', 'hardware', 'decode']:
|
|
await self.decode_alert()
|
|
elif self.element == ['console', 'license']:
|
|
await self.handle_license()
|
|
elif self.element == ['console', 'graphical']:
|
|
await self.handle_graphical_console()
|
|
elif self.element == ['support', 'servicedata']:
|
|
await self.handle_servicedata_fetch()
|
|
elif self.element == ['description']:
|
|
await self.handle_description()
|
|
elif self.element == ['console', 'ikvm_methods']:
|
|
await self.handle_ikvm_methods()
|
|
elif self.element == ['console', 'ikvm_screenshot']:
|
|
await self.handle_ikvm_screenshot()
|
|
elif self.element == ['console', 'ikvm']:
|
|
await self.handle_ikvm()
|
|
else:
|
|
raise self._unsupported()
|
|
|
|
async def handle_update(self):
|
|
u = firmwaremanager.Updater(self.node, self.ipmicmd.update_firmware,
|
|
self.inputdata.nodefile(self.node), self.tenant,
|
|
bank=self.inputdata.bank, configmanager=self.cfm)
|
|
await self.output.put(
|
|
msg.CreatedResource(
|
|
'nodes/{0}/inventory/firmware/updates/active/{1}'.format(
|
|
self.node, u.name)))
|
|
|
|
async def handle_media_upload(self):
|
|
u = firmwaremanager.Updater(self.node, self.ipmicmd.upload_media,
|
|
self.inputdata.nodefile(self.node), self.tenant,
|
|
type='mediaupload', configmanager=self.cfm)
|
|
await self.output.put(msg.CreatedResource(
|
|
'nodes/{0}/media/uploads/{1}'.format(self.node, u.name)))
|
|
|
|
def get_diags(self, savefile, progress, data=None):
|
|
return self.ipmicmd.get_diagnostic_data(
|
|
savefile, progress=progress, autosuffix=True)
|
|
|
|
async def handle_servicedata_fetch(self):
|
|
u = firmwaremanager.Updater(
|
|
self.node, self.get_diags,
|
|
self.inputdata.nodefile(self.node), self.tenant, type='ffdc',
|
|
owner=self.current_user)
|
|
await self.output.put(msg.CreatedResource(
|
|
'nodes/{0}/support/servicedata/{1}'.format(self.node, u.name)))
|
|
|
|
async def handle_attach_media(self):
|
|
try:
|
|
await self.ipmicmd.attach_remote_media(self.inputdata.nodefile(
|
|
self.node))
|
|
except pygexc.UnsupportedFunctionality as uf:
|
|
await self.output.put(msg.ConfluentNodeError(self.node, str(uf)))
|
|
|
|
async def handle_detach_media(self):
|
|
await self.ipmicmd.detach_remote_media()
|
|
|
|
async def handle_list_media(self):
|
|
async for media in self.ipmicmd.list_media():
|
|
await self.output.put(msg.Media(self.node, media))
|
|
|
|
async def handle_configuration(self):
|
|
if self.element[1:3] == ['management_controller', 'alerts']:
|
|
return await self.handle_alerts()
|
|
elif self.element[1:3] == ['management_controller', 'users']:
|
|
return await self.handle_users()
|
|
elif self.element[1:3] == ['management_controller', 'net_interfaces']:
|
|
return await self.handle_nets()
|
|
elif self.element[1:3] == ['management_controller', 'reset']:
|
|
return await self.handle_reset()
|
|
elif self.element[1:3] == ['management_controller', 'identifier']:
|
|
return await self.handle_identifier()
|
|
elif self.element[1:3] == ['management_controller', 'hostname']:
|
|
return await self.handle_hostname()
|
|
elif self.element[1:3] == ['management_controller', 'domain_name']:
|
|
return await self.handle_domain_name()
|
|
elif self.element[1:3] == ['management_controller', 'ntp']:
|
|
return await self.handle_ntp()
|
|
elif self.element[1:4] == ['management_controller', 'extended', 'all']:
|
|
return await self.handle_bmcconfig()
|
|
elif self.element[1:4] == ['management_controller', 'extended', 'advanced']:
|
|
return await self.handle_bmcconfig(True)
|
|
elif self.element[1:4] == ['management_controller', 'extended', 'extra']:
|
|
return await self.handle_bmcconfig(advanced=False, extended=True)
|
|
elif self.element[1:4] == ['management_controller', 'extended', 'extra_advanced']:
|
|
return await self.handle_bmcconfig(advanced=True, extended=True)
|
|
elif self.element[1:3] == ['system', 'all']:
|
|
return await self.handle_sysconfig()
|
|
elif self.element[1:3] == ['system', 'advanced']:
|
|
return await self.handle_sysconfig(True)
|
|
elif self.element[1:3] == ['system', 'clear']:
|
|
return await self.handle_sysconfigclear()
|
|
elif self.element[1:3] == ['management_controller', 'clear']:
|
|
return await self.handle_bmcconfigclear()
|
|
elif self.element[1:3] == ['management_controller', 'licenses']:
|
|
return await self.handle_licenses()
|
|
elif self.element[1:3] == ['management_controller', 'save_licenses']:
|
|
return await self.save_licenses()
|
|
raise self._unsupported()
|
|
|
|
async def decode_alert(self):
|
|
inputdata = self.inputdata.get_alert(self.node)
|
|
specifictrap = int(inputdata['.1.3.6.1.6.3.1.1.4.1.0'].rpartition(
|
|
'.')[-1])
|
|
for tmpvarbind in inputdata:
|
|
if tmpvarbind.endswith('3183.1.1'):
|
|
varbinddata = inputdata[tmpvarbind]
|
|
varbinddata = hex2bin(varbinddata)
|
|
event = await self.ipmicmd.decode_pet(specifictrap, varbinddata)
|
|
self.pyghmi_event_to_confluent(event)
|
|
await self.output.put(msg.EventCollection((event,), name=self.node))
|
|
|
|
async def handle_alerts(self):
|
|
if self.element[3] == 'destinations':
|
|
if len(self.element) == 4:
|
|
# A list of destinations
|
|
maxdest = await self.ipmicmd.get_alert_destination_count()
|
|
for alertidx in range(0, maxdest + 1):
|
|
await self.output.put(msg.ChildCollection(alertidx))
|
|
return
|
|
elif len(self.element) == 5:
|
|
alertidx = int(self.element[-1])
|
|
if self.op == 'read':
|
|
destdata = await self.ipmicmd.get_alert_destination(alertidx)
|
|
await self.output.put(msg.AlertDestination(
|
|
ip=destdata['address'],
|
|
acknowledge=destdata['acknowledge_required'],
|
|
acknowledge_timeout=destdata.get('acknowledge_timeout', None),
|
|
retries=destdata['retries'],
|
|
name=self.node))
|
|
return
|
|
elif self.op == 'update':
|
|
alertparms = self.inputdata.alert_params_by_node(
|
|
self.node)
|
|
alertargs = {}
|
|
if 'acknowledge' in alertparms:
|
|
alertargs['acknowledge_required'] = alertparms['acknowledge']
|
|
if 'acknowledge_timeout' in alertparms:
|
|
alertargs['acknowledge_timeout'] = alertparms['acknowledge_timeout']
|
|
if 'ip' in alertparms:
|
|
alertargs['ip'] = alertparms['ip']
|
|
if 'retries' in alertparms:
|
|
alertargs['retries'] = alertparms['retries']
|
|
await self.ipmicmd.set_alert_destination(destination=alertidx,
|
|
**alertargs)
|
|
return
|
|
elif self.op == 'delete':
|
|
await self.ipmicmd.clear_alert_destination(alertidx)
|
|
return
|
|
raise self._unsupported()
|
|
|
|
async def handle_nets(self):
|
|
if len(self.element) == 3:
|
|
if self.op != 'read':
|
|
await self.output.put(
|
|
msg.ConfluentNodeError(self.node, 'Unsupported operation'))
|
|
return
|
|
await self.output.put(msg.ChildCollection('management'))
|
|
elif len(self.element) == 4 and self.element[-1] == 'management':
|
|
if self.op == 'read':
|
|
lancfg = await self.ipmicmd.get_net_configuration()
|
|
v6cfg = await self.ipmicmd.get_net6_configuration()
|
|
await self.output.put(msg.NetworkConfiguration(
|
|
self.node, ipv4addr=lancfg['ipv4_address'],
|
|
ipv4gateway=lancfg['ipv4_gateway'],
|
|
ipv4cfgmethod=lancfg['ipv4_configuration'],
|
|
hwaddr=lancfg['mac_address'],
|
|
staticv6addrs=v6cfg.get('static_addrs', ''),
|
|
staticv6gateway=v6cfg.get('static_gateway', ''),
|
|
vlan_id=lancfg.get('vlan_id', None)
|
|
))
|
|
elif self.op == 'update':
|
|
config = self.inputdata.netconfig(self.node)
|
|
try:
|
|
await self.ipmicmd.set_net_configuration(
|
|
ipv4_address=config['ipv4_address'],
|
|
ipv4_configuration=config['ipv4_configuration'],
|
|
ipv4_gateway=config['ipv4_gateway'],
|
|
vlan_id=config.get('vlan_id', None))
|
|
v6addrs = config.get('static_v6_addresses', None)
|
|
if v6addrs is not None:
|
|
v6addrs = v6addrs.split(',')
|
|
v6gw = config.get('static_v6_gateway', None)
|
|
await self.ipmicmd.set_net6_configuration(static_addresses=v6addrs, static_gateway=v6gw)
|
|
except socket.error as se:
|
|
await self.output.put(msg.ConfluentNodeError(self.node,
|
|
se.message))
|
|
except ValueError as e:
|
|
if e.message == 'negative shift count':
|
|
await self.output.put(msg.ConfluentNodeError(
|
|
self.node, 'Invalid prefix length given'))
|
|
else:
|
|
raise
|
|
elif len(self.element) == 4 and self.element[-1] != 'management':
|
|
await self.output.put(
|
|
msg.ConfluentTargetNotFound(self.node,
|
|
'Interface not found'))
|
|
|
|
async def handle_users(self):
|
|
# Create user
|
|
if len(self.element) == 3:
|
|
if self.op == 'update':
|
|
user = self.inputdata.credentials[self.node]
|
|
await self.ipmicmd.create_user(uid=user['uid'], name=user['username'],
|
|
password=user['password'],
|
|
callback=True,link_auth=True, ipmi_msg=True,
|
|
privilege_level=user['privilege_level'])
|
|
# A list of users
|
|
await self.output.put(msg.ChildCollection('all'))
|
|
for user in await self.ipmicmd.get_users():
|
|
await self.output.put(msg.ChildCollection(user, candelete=True))
|
|
return
|
|
# List all users
|
|
elif len(self.element) == 4 and self.element[-1] == 'all':
|
|
users = []
|
|
usersfrombmc = await self.ipmicmd.get_users()
|
|
for user in usersfrombmc:
|
|
users.append(await self.ipmicmd.get_user(uid=user))
|
|
await self.output.put(msg.UserCollection(users=users, name=self.node))
|
|
return
|
|
# Update user
|
|
elif len(self.element) == 4:
|
|
user = int(self.element[-1])
|
|
if self.op == 'read':
|
|
data = await self.ipmicmd.get_user(uid=user)
|
|
await self.output.put(msg.User(
|
|
uid=data['uid'],
|
|
username=data['name'],
|
|
privilege_level=data['access']['privilege_level'],
|
|
expiration=data['expiration'],
|
|
name=self.node))
|
|
return
|
|
elif self.op == 'update':
|
|
user = self.inputdata.credentials[self.node]
|
|
|
|
if 'username' in user:
|
|
await self.ipmicmd.set_user_name(uid=user['uid'],
|
|
name=user['username'])
|
|
|
|
if 'password' in user:
|
|
await self.ipmicmd.set_user_password(uid=user['uid'],
|
|
password=user['password'])
|
|
await self.ipmicmd.set_user_password(uid=user['uid'],
|
|
mode='enable', password=user['password'])
|
|
if 'privilege_level' in user:
|
|
await self.ipmicmd.set_user_access(uid=user['uid'],
|
|
privilege_level=user[
|
|
'privilege_level'])
|
|
if 'enabled' in user:
|
|
if user['enabled'] == 'yes':
|
|
mode = 'enable'
|
|
else:
|
|
mode = 'disable'
|
|
await self.ipmicmd.disable_user(user['uid'], mode)
|
|
return
|
|
elif self.op == 'delete':
|
|
await self.ipmicmd.user_delete(uid=user)
|
|
return
|
|
|
|
async def do_eventlog(self):
|
|
eventout = []
|
|
clear = False
|
|
if self.op == 'delete':
|
|
clear = True
|
|
async for event in self.ipmicmd.get_event_log(clear):
|
|
self.pyghmi_event_to_confluent(event)
|
|
eventout.append(event)
|
|
await self.output.put(msg.EventCollection(eventout, name=self.node))
|
|
|
|
def pyghmi_event_to_confluent(self, event):
|
|
event['severity'] = _str_health(event.get('severity', 'unknown'))
|
|
if 'event_data' in event:
|
|
event['event'] = '{0} - {1}'.format(
|
|
event['event'], event['event_data'])
|
|
if 'event_id' in event:
|
|
event['id'] = '{0}.{1}'.format(event['event_id'],
|
|
event['component_type_id'])
|
|
|
|
async def make_inventory_map(self):
|
|
invnames = self.ipmicmd.get_inventory_descriptions()
|
|
async for name in invnames:
|
|
self.invmap[simplify_name(name)] = name
|
|
|
|
async def make_sensor_map(self, sensors=None):
|
|
if sensors is None:
|
|
sensors = self.ipmicmd.get_sensor_descriptions()
|
|
async for sensor in sensors:
|
|
resourcename = sensor['name']
|
|
self.ipmicmd.sensormap[simplify_name(resourcename)] = resourcename
|
|
|
|
async def read_normalized(self, sensorname):
|
|
readings = None
|
|
if sensorname == 'average_cpu_temp':
|
|
cputemp = await self.ipmicmd.get_average_processor_temperature()
|
|
readings = [cputemp]
|
|
elif sensorname == 'inlet_temp':
|
|
inltemp = await self.ipmicmd.get_inlet_temperature()
|
|
readings = [inltemp]
|
|
elif sensorname == 'total_power':
|
|
sensor = EmptySensor('Total Power')
|
|
sensor.states = []
|
|
sensor.units = 'W'
|
|
sensor.value = await self.ipmicmd.get_system_power_watts()
|
|
readings = [sensor]
|
|
if readings:
|
|
await self.output.put(msg.SensorReadings(readings, name=self.node))
|
|
|
|
async def read_sensors(self, sensorname):
|
|
if sensorname == 'all':
|
|
sensors = [x async for x in self.ipmicmd.get_sensor_descriptions()]
|
|
readings = []
|
|
for sensor in filter(self.match_sensor, sensors):
|
|
try:
|
|
reading = await self.ipmicmd.get_sensor_reading(
|
|
sensor['name'])
|
|
except pygexc.IpmiException as ie:
|
|
if ie.ipmicode == 203:
|
|
await self.output.put(msg.SensorReadings([EmptySensor(
|
|
sensor['name'])], name=self.node))
|
|
continue
|
|
raise
|
|
if hasattr(reading, 'health'):
|
|
reading.health = _str_health(reading.health)
|
|
if hasattr(reading, 'unavailable') and reading.unavailable:
|
|
await self.output.put(msg.SensorReadings([EmptySensor(
|
|
reading.name)], name=self.node))
|
|
continue
|
|
readings.append(reading)
|
|
await self.output.put(msg.SensorReadings(readings, name=self.node))
|
|
else:
|
|
if sensorname not in self.ipmicmd.sensormap:
|
|
await self.make_sensor_map()
|
|
if sensorname not in self.ipmicmd.sensormap:
|
|
await self.output.put(
|
|
msg.ConfluentTargetNotFound(self.node,
|
|
'Sensor not found'))
|
|
return
|
|
try:
|
|
reading = await self.ipmicmd.get_sensor_reading(
|
|
self.ipmicmd.sensormap[sensorname])
|
|
if hasattr(reading, 'health'):
|
|
reading.health = _str_health(reading.health)
|
|
if hasattr(reading, 'unavailable') and reading.unavailable:
|
|
await self.output.put(msg.SensorReadings([EmptySensor(
|
|
reading.name)], name=self.node))
|
|
else:
|
|
await self.output.put(
|
|
msg.SensorReadings([reading],
|
|
name=self.node))
|
|
except pygexc.IpmiException as ie:
|
|
if ie.ipmicode == 203:
|
|
await self.output.put(msg.ConfluentResourceUnavailable(
|
|
self.node, 'Unavailable'
|
|
))
|
|
else:
|
|
await self.output.put(msg.ConfluentTargetTimeout(self.node))
|
|
|
|
async def list_inventory(self):
|
|
components = []
|
|
try:
|
|
async for component in self.ipmicmd.get_inventory_descriptions():
|
|
components.append(component)
|
|
except pygexc.IpmiException:
|
|
await self.output.put(msg.ConfluentTargetTimeout(self.node))
|
|
return
|
|
await self.output.put(msg.ChildCollection('all'))
|
|
for component in components:
|
|
await self.output.put(msg.ChildCollection(simplify_name(component)))
|
|
|
|
async def list_firmware(self):
|
|
await self.output.put(msg.ChildCollection('all'))
|
|
async for id, data in self.ipmicmd.get_firmware():
|
|
await self.output.put(msg.ChildCollection(simplify_name(id)))
|
|
|
|
async def read_firmware(self, component, category):
|
|
items = []
|
|
errorneeded = False
|
|
try:
|
|
complist = () if component == 'all' else (component,)
|
|
async for id, data in self.ipmicmd.get_firmware(complist, category):
|
|
if (component in ('core', 'all') or
|
|
component == simplify_name(id) or
|
|
match_aliases(component, simplify_name(id))):
|
|
items.append({id: data})
|
|
except ssl.SSLEOFError:
|
|
errorneeded = msg.ConfluentNodeError(
|
|
self.node, 'Unable to communicate with the https server on '
|
|
'the target BMC while trying to read extended '
|
|
'information')
|
|
except exc.PubkeyInvalid:
|
|
errorneeded = msg.ConfluentNodeError(
|
|
self.node,
|
|
'Extended information unavailable, mismatch detected between '
|
|
'target certificate fingerprint and '
|
|
'pubkeys.tls_hardwaremanager attribute')
|
|
except pygexc.TemporaryError as e:
|
|
errorneeded = msg.ConfluentNodeError(
|
|
self.node, str(e))
|
|
await self.output.put(msg.Firmware(items, self.node))
|
|
if errorneeded:
|
|
await self.output.put(errorneeded)
|
|
|
|
async def handle_update_status(self):
|
|
activeupdates = list(firmwaremanager.list_updates([self.node], None, []))
|
|
if activeupdates:
|
|
await self.output.put(msg.KeyValueData({'status': 'active'}, self.node))
|
|
else:
|
|
status = await self.ipmicmd.get_update_status()
|
|
await self.output.put(msg.KeyValueData({'status': status}, self.node))
|
|
|
|
async def handle_inventory(self):
|
|
if self.element[1] == 'firmware':
|
|
if len(self.element) == 3:
|
|
return await self.list_firmware()
|
|
elif len(self.element) == 4:
|
|
return await self.read_firmware(self.element[-1], self.element[-2])
|
|
elif self.element[1] == 'hardware':
|
|
if len(self.element) == 3: # list things in inventory
|
|
return await self.list_inventory()
|
|
elif len(self.element) == 4: # actually read inventory data
|
|
return await self.read_inventory(self.element[-1])
|
|
raise Exception('Unsupported scenario...')
|
|
|
|
async def list_leds(self):
|
|
await self.output.put(msg.ChildCollection('all'))
|
|
async for category, info in self.ipmicmd.get_leds():
|
|
await self.output.put(msg.ChildCollection(simplify_name(category)))
|
|
|
|
async def read_leds(self, component):
|
|
led_categories = []
|
|
async for category, info in self.ipmicmd.get_leds():
|
|
if component == 'all' or component == simplify_name(category):
|
|
led_categories.append({category: info})
|
|
await self.output.put(msg.LEDStatus(led_categories, self.node))
|
|
|
|
async def read_inventory(self, component):
|
|
errorneeded = False
|
|
try:
|
|
invitems = []
|
|
if component == 'all':
|
|
async for invdata in self.ipmicmd.get_inventory():
|
|
if invdata[1] is None:
|
|
newinf = {'present': False, 'information': None,
|
|
'name': invdata[0]}
|
|
|
|
else:
|
|
sanitize_invdata(invdata[1])
|
|
newinf = {'present': True, 'information': invdata[1]}
|
|
newinf['name'] = invdata[1].get('name', invdata[0])
|
|
self.add_invitem(invitems, newinf)
|
|
else:
|
|
await self.make_inventory_map()
|
|
compname = self.invmap.get(component, None)
|
|
if compname is None:
|
|
await self.output.put(msg.ConfluentTargetNotFound(
|
|
self.node,
|
|
"No component named '{0}' found".format(component)))
|
|
return
|
|
invdata = await self.ipmicmd.get_inventory_of_component(compname)
|
|
if invdata is None:
|
|
newinf = {'present': False, 'information': None,
|
|
'name': compname}
|
|
else:
|
|
sanitize_invdata(invdata)
|
|
newinf = {'present': True, 'information': invdata,
|
|
'name': invdata.get('name', compname)}
|
|
self.add_invitem(invitems, newinf)
|
|
except ssl.SSLEOFError:
|
|
errorneeded = msg.ConfluentNodeError(
|
|
self.node, 'Unable to communicate with the https server on '
|
|
'the target BMC while trying to read extended '
|
|
'information')
|
|
except exc.PubkeyInvalid:
|
|
errorneeded = msg.ConfluentNodeError(
|
|
self.node,
|
|
'Extended information unavailable, mismatch detected between '
|
|
'target certificate fingerprint and '
|
|
'pubkeys.tls_hardwaremanager attribute')
|
|
newinvdata = {'inventory': invitems}
|
|
await self.output.put(msg.KeyValueData(newinvdata, self.node))
|
|
if errorneeded:
|
|
await self.output.put(errorneeded)
|
|
|
|
def add_invitem(self, invitems, newinf):
|
|
if newinf.get('information', None) and 'name' in newinf['information']:
|
|
newinf = copy.deepcopy(newinf)
|
|
del newinf['information']['name']
|
|
if (fnmatch(newinf['name'], 'Adapter ??:??:??') or fnmatch(
|
|
newinf['name'], 'PCIeGen? x*') or not newinf['name']):
|
|
myinf = newinf.get('information', {})
|
|
sdid = myinf.get('PCI Subsystem Device ID', None)
|
|
svid = myinf.get('PCI Subsystem Vendor ID', None)
|
|
did = myinf.get('PCI Device ID', None)
|
|
vid = myinf.get('PCI Vendor ID', None)
|
|
vstr, dstr = get_pci_text_from_ids(sdid, svid, did, vid)
|
|
if vstr:
|
|
newinf['information']['PCI Vendor'] = vstr
|
|
if dstr:
|
|
newinf['name'] = dstr
|
|
invitems.append(newinf)
|
|
|
|
async def handle_storage(self):
|
|
if self.element[-1] == '':
|
|
self.element = self.element[:-1]
|
|
storelem = self.element[2:]
|
|
if 'read' == self.op:
|
|
return await self._show_storage(storelem)
|
|
elif 'update' == self.realop:
|
|
return await self._update_storage(storelem)
|
|
elif 'delete' == self.op:
|
|
return await self._delete_storage(storelem)
|
|
elif 'create' == self.realop:
|
|
return await self._create_storage(storelem)
|
|
|
|
async def _delete_storage(self, storelem):
|
|
if len(storelem) < 2:
|
|
storelem.append('')
|
|
if len(storelem) < 2 or storelem[0] != 'volumes':
|
|
raise exc.InvalidArgumentException('Must target a specific volume')
|
|
volname = storelem[-1]
|
|
curr = await self.ipmicmd.get_storage_configuration()
|
|
volumes = []
|
|
volsfound = False
|
|
toremove = storage.ConfigSpec(arrays=[storage.Array(volumes=volumes)])
|
|
for pool in curr.arrays:
|
|
for vol in pool.volumes:
|
|
if simplify_name(vol.name) == volname:
|
|
volsfound = True
|
|
volumes.append(vol)
|
|
if not volsfound:
|
|
await self.output.put(msg.ConfluentTargetNotFound(
|
|
self.node, "No volume named '{0}' found".format(volname)))
|
|
return
|
|
await self.ipmicmd.remove_storage_configuration(toremove)
|
|
await self.output.put(msg.DeletedResource(volname))
|
|
|
|
async def _create_storage(self, storelem):
|
|
if 'volumes' not in storelem:
|
|
raise exc.InvalidArgumentException('Can only create volumes')
|
|
vols = []
|
|
thedisks = None
|
|
currcfg = await self.ipmicmd.get_storage_configuration()
|
|
currnames = []
|
|
for arr in currcfg.arrays:
|
|
arrname = '{0}-{1}'.format(*arr.id)
|
|
for vol in arr.volumes:
|
|
currnames.append(vol.name)
|
|
disks = []
|
|
vols = []
|
|
vol = self.inputdata.inputbynode[self.node][0]
|
|
raidlvl = vol['raidlevel']
|
|
for disk in currcfg.disks:
|
|
if simplify_name(disk.name) in vol['disks']:
|
|
disks.append(disk)
|
|
elif (disk.status == 'Unconfigured Good' and
|
|
vol['disks'][0] in ('remainder', 'rest')):
|
|
disks.append(disk)
|
|
elif vol['disks'][0] == 'all':
|
|
disks.append(disk)
|
|
for vol in self.inputdata.inputbynode[self.node]:
|
|
if thedisks and thedisks != vol['disks']:
|
|
raise exc.InvalidArgumentException(
|
|
'Not currently supported to create multiple arrays '
|
|
'in a single request')
|
|
if raidlvl and vol['raidlevel'] != raidlvl:
|
|
raise exc.InvalidArgumentException('Cannot mix raid levels in '
|
|
'a single array')
|
|
vols.append(storage.Volume(name=vol['name'], size=vol['size'], stripsize=vol['stripsize']))
|
|
newcfg = storage.ConfigSpec(
|
|
arrays=(storage.Array(raid=raidlvl, disks=disks, volumes=vols),))
|
|
await self.ipmicmd.apply_storage_configuration(newcfg)
|
|
for vol in self.inputdata.inputbynode[self.node]:
|
|
if vol['name'] is None:
|
|
newcfg = await self.ipmicmd.get_storage_configuration()
|
|
for arr in newcfg.arrays:
|
|
arrname = '{0}-{1}'.format(*arr.id)
|
|
for vol in arr.volumes:
|
|
if vol.name not in currnames:
|
|
await self.output.put(
|
|
msg.Volume(self.node, vol.name, vol.size,
|
|
vol.status, arrname))
|
|
return
|
|
else:
|
|
await self._show_storage(storelem[:1] + [vol['name']])
|
|
|
|
async def _update_storage(self, storelem):
|
|
if storelem[0] == 'disks':
|
|
if len(storelem) == 1:
|
|
raise exc.InvalidArgumentException('Must target a disk')
|
|
await self.set_disk(storelem[-1],
|
|
self.inputdata.inputbynode[self.node])
|
|
await self._show_storage(storelem)
|
|
|
|
async def _show_storage(self, storelem):
|
|
if storelem[0] == 'disks':
|
|
if len(storelem) == 1:
|
|
return await self.list_disks()
|
|
return await self.show_disk(storelem[1])
|
|
elif storelem[0] == 'arrays':
|
|
if len(storelem) == 1:
|
|
return await self.list_arrays()
|
|
return await self.show_array(storelem[1])
|
|
elif storelem[0] == 'volumes':
|
|
if len(storelem) == 1:
|
|
return await self.list_volumes()
|
|
return await self.show_volume(storelem[1])
|
|
elif storelem[0] == 'all':
|
|
return await self._show_all_storage()
|
|
|
|
|
|
async def handle_sensors(self):
|
|
if self.element[-1] == '':
|
|
self.element = self.element[:-1]
|
|
if len(self.element) < 3:
|
|
return
|
|
self.sensorcategory = self.element[2]
|
|
if self.sensorcategory == 'normalized':
|
|
return await self.read_normalized(self.element[-1])
|
|
# list sensors per category
|
|
if len(self.element) == 3 and self.element[-2] == 'hardware':
|
|
if self.sensorcategory == 'leds':
|
|
return await self.list_leds()
|
|
return await self.list_sensors()
|
|
elif len(self.element) == 4: # resource requested
|
|
if self.sensorcategory == 'leds':
|
|
return await self.read_leds(self.element[-1])
|
|
return await self.read_sensors(self.element[-1])
|
|
|
|
def match_sensor(self, sensor):
|
|
if self.sensorcategory == 'all':
|
|
return True
|
|
if sensor['type'] in sensor_categories[self.sensorcategory]:
|
|
return True
|
|
return False
|
|
|
|
async def set_disk(self, name, state):
|
|
scfg = await self.ipmicmd.get_storage_configuration()
|
|
for disk in scfg.disks:
|
|
if (name == 'all' or simplify_name(disk.name) == name or
|
|
disk == name):
|
|
disk.status = state
|
|
await self.ipmicmd.apply_storage_configuration(
|
|
storage.ConfigSpec(disks=scfg.disks))
|
|
|
|
async def _show_all_storage(self):
|
|
scfg = await self.ipmicmd.get_storage_configuration()
|
|
for disk in scfg.disks:
|
|
await self.output.put(
|
|
msg.Disk(self.node, disk.name, disk.description,
|
|
disk.id, disk.status, disk.serial,
|
|
disk.fru))
|
|
for arr in scfg.arrays:
|
|
for disk in arr.disks:
|
|
await self.output.put(
|
|
msg.Disk(self.node, disk.name, disk.description,
|
|
disk.id, disk.status, disk.serial,
|
|
disk.fru, array='{0}-{1}'.format(*arr.id)))
|
|
for disk in arr.hotspares:
|
|
await self.output.put(
|
|
msg.Disk(self.node, disk.name, disk.description,
|
|
disk.id, disk.status, disk.serial,
|
|
disk.fru, array='{0}-{1}'.format(*arr.id)))
|
|
for arr in scfg.arrays:
|
|
arrname = '{0}-{1}'.format(*arr.id)
|
|
await self._detail_array(arr, arrname, True)
|
|
|
|
async def show_disk(self, name):
|
|
scfg = await self.ipmicmd.get_storage_configuration()
|
|
for disk in scfg.disks:
|
|
if simplify_name(disk.name) == name or disk == name:
|
|
await self.output.put(
|
|
msg.Disk(self.node, disk.name, disk.description,
|
|
disk.id, disk.status, disk.serial,
|
|
disk.fru))
|
|
for arr in scfg.arrays:
|
|
arrname = '{0}-{1}'.format(*arr.id)
|
|
for disk in arr.disks:
|
|
if (name == 'all' or simplify_name(disk.name) == name or
|
|
disk == name):
|
|
await self.output.put(
|
|
msg.Disk(self.node, disk.name, disk.description,
|
|
disk.id, disk.status, disk.serial,
|
|
disk.fru, arrname))
|
|
for disk in arr.hotspares:
|
|
if (name == 'all' or simplify_name(disk.name) == name or
|
|
disk == name):
|
|
await self.output.put(
|
|
msg.Disk(self.node, disk.name, disk.description,
|
|
disk.id, disk.status, disk.serial,
|
|
disk.fru, arrname))
|
|
|
|
async def list_disks(self):
|
|
scfg = await self.ipmicmd.get_storage_configuration()
|
|
for disk in scfg.disks:
|
|
await self.output.put(msg.ChildCollection(simplify_name(disk.name)))
|
|
for arr in scfg.arrays:
|
|
for disk in arr.disks:
|
|
await self.output.put(msg.ChildCollection(simplify_name(disk.name)))
|
|
for disk in arr.hotspares:
|
|
await self.output.put(msg.ChildCollection(simplify_name(disk.name)))
|
|
|
|
async def list_arrays(self):
|
|
scfg = await self.ipmicmd.get_storage_configuration()
|
|
for arr in scfg.arrays:
|
|
await self.output.put(msg.ChildCollection('{0}-{1}'.format(*arr.id)))
|
|
|
|
async def show_array(self, name):
|
|
scfg = await self.ipmicmd.get_storage_configuration()
|
|
for arr in scfg.arrays:
|
|
arrname = '{0}-{1}'.format(*arr.id)
|
|
if arrname == name:
|
|
await self._detail_array(arr, arrname)
|
|
|
|
async def _detail_array(self, arr, arrname, detailvol=False):
|
|
vols = []
|
|
for vol in arr.volumes:
|
|
vols.append(simplify_name(vol.name))
|
|
disks = []
|
|
for disk in arr.disks:
|
|
disks.append(simplify_name(disk.name))
|
|
for disk in arr.hotspares:
|
|
disks.append(simplify_name(disk.name))
|
|
await self.output.put(msg.Array(self.node, disks, arr.raid,
|
|
vols, arrname, arr.capacity,
|
|
arr.available_capacity))
|
|
if detailvol:
|
|
for vol in arr.volumes:
|
|
await self.output.put(msg.Volume(self.node, vol.name, vol.size,
|
|
vol.status, arrname))
|
|
|
|
async def show_volume(self, name):
|
|
scfg = await self.ipmicmd.get_storage_configuration()
|
|
for arr in scfg.arrays:
|
|
arrname = '{0}-{1}'.format(*arr.id)
|
|
for vol in arr.volumes:
|
|
if name == simplify_name(vol.name):
|
|
await self.output.put(msg.Volume(self.node, vol.name, vol.size,
|
|
vol.status, arrname))
|
|
|
|
async def list_volumes(self):
|
|
scfg = await self.ipmicmd.get_storage_configuration()
|
|
for arr in scfg.arrays:
|
|
for vol in arr.volumes:
|
|
await self.output.put(msg.ChildCollection(simplify_name(vol.name)))
|
|
|
|
async def list_sensors(self):
|
|
try:
|
|
sensors = [sensor async for sensor in self.ipmicmd.get_sensor_descriptions()]
|
|
except pygexc.IpmiException:
|
|
await self.output.put(msg.ConfluentTargetTimeout(self.node))
|
|
return
|
|
await self.output.put(msg.ChildCollection('all'))
|
|
for sensor in filter(self.match_sensor, sensors):
|
|
await self.output.put(msg.ChildCollection(simplify_name(sensor['name'])))
|
|
|
|
async def health(self):
|
|
if 'read' == self.op:
|
|
try:
|
|
response = await self.ipmicmd.get_health()
|
|
except pygexc.IpmiException:
|
|
await self.output.put(msg.ConfluentTargetTimeout(self.node))
|
|
return
|
|
health = response['health']
|
|
health = _str_health(health)
|
|
await self.output.put(msg.HealthSummary(health, self.node))
|
|
if 'badreadings' in response:
|
|
badsensors = []
|
|
for reading in response['badreadings']:
|
|
if hasattr(reading, 'health'):
|
|
reading.health = _str_health(reading.health)
|
|
badsensors.append(reading)
|
|
await self.output.put(msg.SensorReadings(badsensors, name=self.node))
|
|
else:
|
|
raise exc.InvalidArgumentException('health is read-only')
|
|
|
|
async def reseat_bay(self):
|
|
bay = self.inputdata.inputbynode[self.node]
|
|
try:
|
|
await self.ipmicmd.reseat_bay(bay)
|
|
await self.output.put(msg.ReseatResult(self.node, 'success'))
|
|
except pygexc.UnsupportedFunctionality as uf:
|
|
await self.output.put(uf)
|
|
|
|
async def bootdevice(self):
|
|
if 'read' == self.op:
|
|
bootdev = await self.ipmicmd.get_bootdev()
|
|
if bootdev['bootdev'] in self.bootdevices:
|
|
bootdev['bootdev'] = self.bootdevices[bootdev['bootdev']]
|
|
bootmode = 'unspecified'
|
|
if 'uefimode' in bootdev:
|
|
if bootdev['uefimode']:
|
|
bootmode = 'uefi'
|
|
else:
|
|
bootmode = 'bios'
|
|
persistent = False
|
|
if 'persistent' in bootdev:
|
|
persistent = bootdev['persistent']
|
|
await self.output.put(msg.BootDevice(node=self.node,
|
|
device=bootdev['bootdev'],
|
|
bootmode=bootmode,
|
|
persistent=persistent))
|
|
return
|
|
elif 'update' == self.op:
|
|
bootdev = self.inputdata.bootdevice(self.node)
|
|
douefi = False
|
|
if self.inputdata.bootmode(self.node) == 'uefi':
|
|
douefi = True
|
|
persistent = self.inputdata.persistent(self.node)
|
|
bootdev = await self.ipmicmd.set_bootdev(bootdev, uefiboot=douefi,
|
|
persist=persistent)
|
|
if bootdev['bootdev'] in self.bootdevices:
|
|
bootdev['bootdev'] = self.bootdevices[bootdev['bootdev']]
|
|
await self.output.put(msg.BootDevice(node=self.node,
|
|
device=bootdev['bootdev']))
|
|
|
|
async def identify(self):
|
|
if 'update' == self.op:
|
|
identifystate = self.inputdata.inputbynode[self.node] == 'on'
|
|
blinkstate = self.inputdata.inputbynode[self.node] == 'blink'
|
|
await self.ipmicmd.set_identify(on=identifystate, blink=blinkstate)
|
|
await self.output.put(msg.IdentifyState(
|
|
node=self.node, state=self.inputdata.inputbynode[self.node]))
|
|
return
|
|
elif 'read' == self.op:
|
|
# ipmi has identify as read-only for now
|
|
await self.output.put(msg.IdentifyState(node=self.node, state=''))
|
|
return
|
|
|
|
async def power(self):
|
|
if 'read' == self.op:
|
|
power = await self.ipmicmd.get_power()
|
|
await self.output.put(msg.PowerState(node=self.node,
|
|
state=power['powerstate']))
|
|
return
|
|
elif 'update' == self.op:
|
|
powerstate = self.inputdata.powerstate(self.node)
|
|
oldpower = None
|
|
waitamount = 30
|
|
if powerstate == 'boot':
|
|
oldpower = await self.ipmicmd.get_power()
|
|
if 'powerstate' in oldpower:
|
|
oldpower = oldpower['powerstate']
|
|
elif powerstate == 'shutdown':
|
|
waitamount = True
|
|
await self.ipmicmd.set_power(powerstate, wait=waitamount)
|
|
if powerstate == 'boot' and oldpower == 'on':
|
|
power = {'powerstate': 'reset'}
|
|
else:
|
|
power = await self.ipmicmd.get_power()
|
|
if powerstate == 'reset' and power['powerstate'] == 'on':
|
|
power['powerstate'] = 'reset'
|
|
|
|
await self.output.put(
|
|
msg.PowerState(node=self.node,
|
|
state=power['powerstate'],
|
|
oldstate=oldpower))
|
|
return
|
|
|
|
async def handle_reset(self):
|
|
if 'read' == self.op:
|
|
await self.output.put(msg.BMCReset(node=self.node,
|
|
state='reset'))
|
|
return
|
|
elif 'update' == self.op:
|
|
await self.ipmicmd.reset_bmc()
|
|
return
|
|
|
|
async def handle_identifier(self):
|
|
if 'read' == self.op:
|
|
mci = await self.ipmicmd.get_mci()
|
|
await self.output.put(msg.MCI(self.node, mci))
|
|
return
|
|
elif 'update' == self.op:
|
|
mci = self.inputdata.mci(self.node)
|
|
await self.ipmicmd.set_mci(mci)
|
|
return
|
|
|
|
async def handle_hostname(self):
|
|
if 'read' == self.op:
|
|
hostname = await self.ipmicmd.get_hostname()
|
|
await self.output.put(msg.Hostname(self.node, hostname))
|
|
return
|
|
elif 'update' == self.op:
|
|
hostname = self.inputdata.hostname(self.node)
|
|
await self.ipmicmd.set_hostname(hostname)
|
|
return
|
|
|
|
async def handle_domain_name(self):
|
|
if 'read' == self.op:
|
|
dn = await self.ipmicmd.get_domain_name()
|
|
await self.output.put(msg.DomainName(self.node, dn))
|
|
return
|
|
elif 'update' == self.op:
|
|
dn = self.inputdata.domain_name(self.node)
|
|
await self.ipmicmd.set_domain_name(dn)
|
|
return
|
|
|
|
async def handle_bmcconfigclear(self):
|
|
if 'read' == self.op:
|
|
raise exc.InvalidArgumentException(
|
|
'Cannot read the "clear" resource')
|
|
await self.ipmicmd.clear_bmc_configuration()
|
|
|
|
async def handle_sysconfigclear(self):
|
|
if 'read' == self.op:
|
|
raise exc.InvalidArgumentException(
|
|
'Cannot read the "clear" resource')
|
|
await self.ipmicmd.clear_system_configuration()
|
|
|
|
async def handle_bmcconfig(self, advanced=False, extended=False):
|
|
if 'read' == self.op:
|
|
try:
|
|
if extended:
|
|
bmccfg = await self.ipmicmd.get_extended_bmc_configuration(
|
|
hideadvanced=(not advanced))
|
|
else:
|
|
bmccfg = await self.ipmicmd.get_bmc_configuration()
|
|
await self.output.put(msg.ConfigSet(self.node, bmccfg))
|
|
except Exception as e:
|
|
await self.output.put(
|
|
msg.ConfluentNodeError(self.node, str(e)))
|
|
elif 'update' == self.op:
|
|
await self.ipmicmd.set_bmc_configuration(
|
|
self.inputdata.get_attributes(self.node))
|
|
|
|
async def handle_sysconfig(self, advanced=False):
|
|
if 'read' == self.op:
|
|
try:
|
|
syscfg = await self.ipmicmd.get_system_configuration(
|
|
hideadvanced=not advanced)
|
|
await self.output.put(msg.ConfigSet(
|
|
self.node, syscfg))
|
|
except Exception as e:
|
|
await self.output.put(
|
|
msg.ConfluentNodeError(self.node, str(e)))
|
|
elif 'update' == self.op:
|
|
await self.ipmicmd.set_system_configuration(
|
|
self.inputdata.get_attributes(self.node))
|
|
|
|
async def handle_ntp(self):
|
|
if self.element[3] == 'enabled':
|
|
if 'read' == self.op:
|
|
enabled = await self.ipmicmd.get_ntp_enabled()
|
|
if enabled is None:
|
|
# None is how the platform says it cannot tell us, and
|
|
# reporting that as the text "None" tells the caller nothing
|
|
raise pygexc.UnsupportedFunctionality(
|
|
'NTP state is not reported by this platform')
|
|
await self.output.put(msg.NTPEnabled(self.node, enabled))
|
|
return
|
|
elif 'update' == self.op:
|
|
enabled = self.inputdata.ntp_enabled(self.node)
|
|
await self.ipmicmd.set_ntp_enabled(enabled == 'True')
|
|
return
|
|
elif self.element[3] == 'servers':
|
|
if len(self.element) == 4:
|
|
await self.output.put(msg.ChildCollection('all'))
|
|
size = len(await self.ipmicmd.get_ntp_servers())
|
|
for idx in range(1, size + 1):
|
|
await self.output.put(msg.ChildCollection(idx))
|
|
else:
|
|
if 'read' == self.op:
|
|
if self.element[-1] == 'all':
|
|
servers = await self.ipmicmd.get_ntp_servers()
|
|
await self.output.put(msg.NTPServers(self.node, servers))
|
|
return
|
|
else:
|
|
idx = int(self.element[-1]) - 1
|
|
servers = await self.ipmicmd.get_ntp_servers()
|
|
if len(servers) > idx:
|
|
await self.output.put(msg.NTPServer(self.node, servers[idx]))
|
|
else:
|
|
await self.output.put(
|
|
msg.ConfluentTargetNotFound(
|
|
self.node, 'Requested NTP configuration not found'))
|
|
return
|
|
elif self.op in ('update', 'create'):
|
|
if self.element[-1] == 'all':
|
|
servers = self.inputdata.ntp_servers(self.node)
|
|
for idx in servers:
|
|
await self.ipmicmd.set_ntp_server(servers[idx],
|
|
int(idx[-1])-1)
|
|
return
|
|
else:
|
|
idx = int(self.element[-1]) - 1
|
|
server = self.inputdata.ntp_server(self.node)
|
|
await self.ipmicmd.set_ntp_server(server, idx)
|
|
return
|
|
|
|
async def handle_license(self):
|
|
available = await self.ipmicmd.get_remote_kvm_available()
|
|
await self.output.put(msg.License(self.node, available))
|
|
return
|
|
|
|
async def save_licenses(self):
|
|
directory = self.inputdata.nodefile(self.node)
|
|
checkdir = directory
|
|
if not os.access(directory, os.W_OK):
|
|
raise exc.InvalidArgumentException(
|
|
'The confluent system user/group is unable to write to '
|
|
'directory {0}, check ownership and permissions'.format(
|
|
checkdir))
|
|
async for saved in self.ipmicmd.save_licenses(directory):
|
|
if self.current_user:
|
|
try:
|
|
pwent = pwd.getpwnam(self.current_user)
|
|
os.chown(saved, pwent.pw_uid, pwent.pw_gid)
|
|
except KeyError:
|
|
pass
|
|
await self.output.put(msg.SavedFile(self.node, saved))
|
|
|
|
async def handle_licenses(self):
|
|
if self.element[-1] == '':
|
|
self.element = self.element[:-1]
|
|
if self.op in ('create', 'update'):
|
|
filename = self.inputdata.nodefile(self.node)
|
|
datfile = None
|
|
if filename in self.cfm.clientfiles:
|
|
cf = self.cfm.clientfiles[filename]
|
|
datfile = os.fdopen(os.dup(cf.fileno()), cf.mode)
|
|
if datfile is None and not os.access(filename, os.R_OK):
|
|
errstr = ('{0} is not readable by confluent on {1} '
|
|
'(ensure confluent user or group can access file '
|
|
'and parent directories)').format(
|
|
filename, socket.gethostname())
|
|
await self.output.put(msg.ConfluentNodeError(self.node, errstr))
|
|
return
|
|
try:
|
|
async for x in self.ipmicmd.apply_license(filename, data=datfile):
|
|
pass
|
|
finally:
|
|
if datfile is not None:
|
|
datfile.close()
|
|
if len(self.element) == 3:
|
|
await self.output.put(msg.ChildCollection('all'))
|
|
i = 1
|
|
async for lic in self.ipmicmd.get_licenses():
|
|
await self.output.put(msg.ChildCollection(str(i)))
|
|
i += 1
|
|
return
|
|
licname = self.element[3]
|
|
if licname == 'all':
|
|
async for lic in self.ipmicmd.get_licenses():
|
|
if self.op == 'delete':
|
|
await self.ipmicmd.delete_license(lic['name'])
|
|
else:
|
|
await self.output.put(msg.License(self.node, feature=lic['name'], state=lic.get('state', 'Active')))
|
|
else:
|
|
index = int(licname)
|
|
lic = list([x async for x in self.ipmicmd.get_licenses()])[index - 1]
|
|
if self.op == 'delete':
|
|
await self.ipmicmd.delete_license(lic['name'])
|
|
else:
|
|
await self.output.put(msg.License(self.node, feature=lic['name'], state=lic.get('state', 'Active')))
|
|
|
|
async def handle_description(self):
|
|
dsc = await self.ipmicmd.get_description()
|
|
await self.output.put(msg.KeyValueData(dsc, self.node))
|
|
|
|
async def handle_ikvm_methods(self):
|
|
dsc = await self.ipmicmd.get_ikvm_methods()
|
|
dsc = {'ikvm_methods': dsc}
|
|
await self.output.put(msg.KeyValueData(dsc, self.node))
|
|
|
|
async def handle_ikvm_screenshot(self):
|
|
# good background for the webui, and kitty
|
|
imgdata = RetainedIO()
|
|
imgformat = await self.ipmicmd.get_screenshot(imgdata)
|
|
imgdata = imgdata.resultbuffer
|
|
if imgdata:
|
|
await self.output.put(msg.ScreenShot(imgdata, self.node, imgformat=imgformat))
|
|
|
|
async def handle_ikvm(self):
|
|
methods = await self.ipmicmd.get_ikvm_methods()
|
|
if 'openbmc' in methods:
|
|
url = await vinzmanager.get_url(self.node, self.inputdata)
|
|
await self.output.put(msg.ChildCollection(url))
|
|
return
|
|
launchdata = await self.ipmicmd.get_ikvm_launchdata()
|
|
if 'url' in launchdata and not launchdata['url'].startswith('https://'):
|
|
mybmc = self.ipmicmd.confluentbmcname
|
|
if mybmc.startswith('fe80::'): # link local, need to adjust
|
|
lancfg = await self.ipmicmd.get_net_configuration()
|
|
mybmc = lancfg['ipv4_address'].split('/')[0]
|
|
if ':' in mybmc and '[' not in mybmc:
|
|
mybmc = '[{}]'.format(mybmc)
|
|
launchdata['url'] = 'https://{}{}'.format(mybmc, launchdata['url'])
|
|
await self.output.put(msg.KeyValueData(launchdata, self.node))
|
|
|
|
|
|
async def handle_graphical_console(self):
|
|
args = await self.ipmicmd.get_graphical_console()
|
|
m = msg.GraphicalConsole(self.node, *args)
|
|
await self.output.put(m)
|
|
return
|
|
|
|
|
|
def _str_health(health):
|
|
if isinstance(health, str):
|
|
return 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():
|
|
global _ipmithread
|
|
if _ipmithread is None:
|
|
_ipmithread = asyncio.create_task(_ipmi_evtloop())
|
|
|
|
|
|
async def create(nodes, element, configmanager, inputdata, realop='create'):
|
|
initthread()
|
|
if element == ['_console', 'session']:
|
|
if len(nodes) > 1:
|
|
raise Exception("_console/session does not support multiple nodes")
|
|
yield IpmiConsole(nodes[0], configmanager)
|
|
return
|
|
else:
|
|
async for ret in perform_requests(
|
|
'update', nodes, element, configmanager, inputdata, realop):
|
|
yield ret
|
|
|
|
|
|
def update(nodes, element, configmanager, inputdata):
|
|
initthread()
|
|
return create(nodes, element, configmanager, inputdata, 'update')
|
|
|
|
|
|
async def retrieve(nodes, element, configmanager, inputdata):
|
|
initthread()
|
|
if '/'.join(element).startswith('inventory/firmware/updates/active'):
|
|
for ret in firmwaremanager.list_updates(nodes, configmanager.tenant,
|
|
element):
|
|
yield ret
|
|
elif '/'.join(element).startswith('media/uploads'):
|
|
for ret in firmwaremanager.list_updates(nodes, configmanager.tenant,
|
|
element, 'mediaupload'):
|
|
yield ret
|
|
elif '/'.join(element).startswith('support/servicedata'):
|
|
for ret in firmwaremanager.list_updates(nodes, configmanager.tenant,
|
|
element, 'ffdc'):
|
|
yield ret
|
|
else:
|
|
async for ret in perform_requests('read', nodes, element, configmanager,
|
|
inputdata, 'read'):
|
|
yield ret
|
|
|
|
def delete(nodes, element, configmanager, inputdata):
|
|
initthread()
|
|
if '/'.join(element).startswith('inventory/firmware/updates/active'):
|
|
return firmwaremanager.remove_updates(nodes, configmanager.tenant,
|
|
element)
|
|
elif '/'.join(element).startswith('media/uploads'):
|
|
return firmwaremanager.remove_updates(nodes, configmanager.tenant,
|
|
element, type='mediaupload')
|
|
elif '/'.join(element).startswith('support/servicedata'):
|
|
return firmwaremanager.remove_updates(nodes, configmanager.tenant,
|
|
element, type='ffdc')
|
|
return perform_requests(
|
|
'delete', nodes, element, configmanager, inputdata, 'delete')
|