mirror of
https://github.com/xcat2/confluent.git
synced 2026-09-01 15:06:06 +00:00
Merge branch 'master' into ruff
This commit is contained in:
@@ -147,6 +147,41 @@ def existing_regen_args(targetpath):
|
||||
|
||||
|
||||
|
||||
def warn_conflicts(hostentries):
|
||||
"""Warn about reservations that conflict with one another: the same
|
||||
hostname on different IPs, one IP reserved more than once (dnsmasq
|
||||
refuses to start on a duplicate dhcp-host IP), or one MAC reserved
|
||||
more than once. Diagnostics only; every entry is still emitted."""
|
||||
byname = {}
|
||||
byip = {}
|
||||
bymac = {}
|
||||
for e in hostentries:
|
||||
if e['hostname']:
|
||||
prev = byname.setdefault(e['hostname'], e)
|
||||
if prev is not e and prev['ip'] != e['ip']:
|
||||
sys.stderr.write(
|
||||
"WARNING: hostname '{0}' maps to both {1} ({2}) and "
|
||||
'{3} ({4})\n'.format(
|
||||
e['hostname'], prev['ip'], prev['node'],
|
||||
e['ip'], e['node']))
|
||||
prev = byip.setdefault(e['ip'], e)
|
||||
if prev is not e:
|
||||
sys.stderr.write(
|
||||
'WARNING: address {0} is reserved for both {1} ({2}) and '
|
||||
'{3} ({4}); dnsmasq refuses to start on a duplicate '
|
||||
'dhcp-host IP address\n'.format(
|
||||
e['ip'], ','.join(prev['macs']), prev['node'],
|
||||
','.join(e['macs']), e['node']))
|
||||
for mac in e['macs']:
|
||||
prev = bymac.setdefault(mac.lower(), e)
|
||||
if prev is not e:
|
||||
sys.stderr.write(
|
||||
'WARNING: MAC {0} is reserved for both {1} ({2}) and '
|
||||
'{3} ({4})\n'.format(
|
||||
mac, prev['ip'], prev['node'],
|
||||
e['ip'], e['node']))
|
||||
|
||||
|
||||
def main():
|
||||
ap = argparse.ArgumentParser(
|
||||
description='Create /etc/dnsmasq.d static DHCP reservations for a '
|
||||
@@ -336,7 +371,10 @@ def main():
|
||||
|
||||
hostentries.append({'subnetkey': subnetkey,
|
||||
'macs': split_list(hwaddr),
|
||||
'ip': ip, 'hostname': hostname})
|
||||
'ip': ip, 'hostname': hostname,
|
||||
'node': node})
|
||||
|
||||
warn_conflicts(hostentries)
|
||||
|
||||
# Resolve optional per-subnet options from the aggregated values.
|
||||
def resolve(values, what, subnetkey):
|
||||
|
||||
@@ -33,6 +33,8 @@ class HostMerger(object):
|
||||
self.byip = {}
|
||||
self.byname = {}
|
||||
self.byname6 = {}
|
||||
self.ipbyname = {}
|
||||
self.ipbyname6 = {}
|
||||
self.sourcelines = []
|
||||
self.targlines = []
|
||||
|
||||
@@ -54,14 +56,21 @@ class HostMerger(object):
|
||||
|
||||
def add_entry(self, ip, names):
|
||||
targ = self.byname
|
||||
ipbyname = self.ipbyname
|
||||
if ':' in ip:
|
||||
targ = self.byname6
|
||||
ipbyname = self.ipbyname6
|
||||
line = '{:<39} {}'.format(ip, names)
|
||||
x = len(self.sourcelines)
|
||||
self.sourcelines.append(line)
|
||||
for name in names.split():
|
||||
if not name:
|
||||
continue
|
||||
if ipbyname.setdefault(name, ip) != ip:
|
||||
sys.stderr.write(
|
||||
'WARNING: {0} is mapped to both {1} and {2}; all entries '
|
||||
'will be written to /etc/hosts\n'.format(
|
||||
name, ipbyname[name], ip))
|
||||
targ[name] = x
|
||||
self.byip[ip] = x
|
||||
|
||||
|
||||
@@ -53,6 +53,8 @@ def run():
|
||||
help='Specify a custom port for ssh')
|
||||
argparser.add_option('-s', '--substitutename',
|
||||
help='Use a different name other than the nodename for ssh')
|
||||
argparser.add_option('-t', '--timeout', type='int', default=0,
|
||||
help='Timeout in seconds for each node ssh connection')
|
||||
argparser.add_option('-x', '--noexpression', action='store_true',
|
||||
help='Suppress expression expansion of command')
|
||||
argparser.add_option('-m', '--maxnodes', type='int',
|
||||
@@ -119,6 +121,8 @@ def run():
|
||||
cmdv += ['-p', '{0}'.format(options.port)]
|
||||
if options.loginname:
|
||||
cmdv += ['-l', options.loginname]
|
||||
if options.timeout:
|
||||
cmdv += ['-o', 'ConnectTimeout={0}'.format(options.timeout)]
|
||||
cmdv += [sshnode, cmd]
|
||||
if currprocs < concurrentprocs:
|
||||
currprocs += 1
|
||||
|
||||
@@ -53,6 +53,8 @@ _confluent_get_args()
|
||||
CMPARGS+=("")
|
||||
fi
|
||||
GENNED=""
|
||||
# Whitespace separates candidate groups, while commas group synonyms.
|
||||
# shellcheck disable=SC2068
|
||||
for CAND in ${COMP_CANDIDATES[@]}; do
|
||||
candarray=(${CAND//,/ })
|
||||
matched=0
|
||||
|
||||
@@ -130,6 +130,21 @@ to a blank value will allow masking a group defined attribute with an empty valu
|
||||
`node12: net.compute.ipv4_address: 172.17.0.12/16`
|
||||
`node12: net.compute.team_mode: lacp`
|
||||
|
||||
* Passing additional settings to the network backend of the deployed OS with `net.extra_settings`
|
||||
(semicolon-delimited key=value pairs, keys in the native syntax of the respective backend).
|
||||
On a NetworkManager based OS (e.g. Enterprise Linux), keys are nmcli properties:
|
||||
`# nodeattrib node12 net.mgmt.extra_settings='connection.zone=internal;ipv4.routes=10.0.0.0/8 192.168.1.254, 172.16.0.0/12 192.168.1.254;ipv4.route-metric=200'`
|
||||
`node12: net.mgmt.extra_settings: connection.zone=internal;ipv4.routes=10.0.0.0/8 192.168.1.254, 172.16.0.0/12 192.168.1.254;ipv4.route-metric=200`
|
||||
|
||||
* On a netplan based OS (e.g. Ubuntu), keys are netplan YAML paths (nested keys dotted, values in YAML flow syntax).
|
||||
Since Confluent uses braces for attribute expressions, literal braces must be escaped as `{{` and `}}` when setting the attribute:
|
||||
`# nodeattrib node13 net.mgmt.extra_settings='routes=[{{to: 10.0.0.0/8, via: 192.168.1.254}}];nameservers.search=[lab.example.com]'`
|
||||
`node13: net.mgmt.extra_settings: routes=[{to: 10.0.0.0/8, via: 192.168.1.254}];nameservers.search=[lab.example.com]`
|
||||
|
||||
* On a wicked based OS (e.g. SUSE), keys are ifcfg variables (routes are not supported through this mechanism on wicked):
|
||||
`# nodeattrib node14 net.mgmt.extra_settings='ZONE=internal;ETHTOOL_OPTIONS=-K iface tso off'`
|
||||
`node14: net.mgmt.extra_settings: ZONE=internal;ETHTOOL_OPTIONS=-K iface tso off`
|
||||
|
||||
* Clear attribute on nodes of a simple noderange, if you want to retain the variable set the attribute to "":
|
||||
`# nodeattrib n1-n2 -c console.method`
|
||||
`# nodeattrib n1-n2 console.method`
|
||||
|
||||
@@ -34,9 +34,12 @@ as stderr, unlike psh which combines all stdout and stderr into stdout.
|
||||
Specify a substitution name instead of the nodename. If no {} are in the substitution,
|
||||
it is considered to be an append. For example, '-s -ib' would produce 'node1-ib' from 'node1'.
|
||||
Full expression syntax is supported, in which case the substitution is considered to be the entire
|
||||
new name. {node}-ib would be equivalent to -ib. For example, nodeshell -s {bmc} node1
|
||||
new name. {node}-ib would be equivalent to -ib. For example, nodeshell -s {bmc} node1
|
||||
would ssh to the BMC instead of the node.
|
||||
|
||||
* `-t TIMEOUT`, `--timeout=TIMEOUT`
|
||||
Timeout in seconds for each node ssh connection attempt
|
||||
|
||||
## EXAMPLES
|
||||
|
||||
* Running `echo hi` on for nodes:
|
||||
|
||||
@@ -114,6 +114,14 @@ def get_interface_name(iname, settings):
|
||||
return iname
|
||||
return None
|
||||
|
||||
def parse_extra_settings(stgs):
|
||||
extras = {}
|
||||
for kv in stgs.get('extra_settings', '').split(';'):
|
||||
k, _, v = kv.partition('=')
|
||||
if k.strip():
|
||||
extras[k.strip()] = v.strip()
|
||||
return extras
|
||||
|
||||
class NetplanManager(object):
|
||||
def __init__(self, deploycfg):
|
||||
self.cfgbydev = {}
|
||||
@@ -229,6 +237,24 @@ class NetplanManager(object):
|
||||
if dnsdomain not in currdnsdomain:
|
||||
needcfgwrite = True
|
||||
currdnsdomain.append(dnsdomain)
|
||||
extras = parse_extra_settings(stgs)
|
||||
if extras:
|
||||
currcfg = self.cfgbybond if devname in self.cfgbybond else self.cfgbydev
|
||||
devdict = currcfg.setdefault(devname, {})
|
||||
for key in extras:
|
||||
try:
|
||||
val = yaml.safe_load(extras[key])
|
||||
except yaml.YAMLError:
|
||||
val = extras[key]
|
||||
keyptr = devdict
|
||||
keypath = key.split('.')
|
||||
for k in keypath[:-1]:
|
||||
if not isinstance(keyptr.get(k, None), dict):
|
||||
keyptr[k] = {}
|
||||
keyptr = keyptr[k]
|
||||
if keyptr.get(keypath[-1], None) != val:
|
||||
needcfgwrite = True
|
||||
keyptr[keypath[-1]] = val
|
||||
prune_from_cloudinit = []
|
||||
if needcfgwrite:
|
||||
needcfgapply = True
|
||||
@@ -336,6 +362,9 @@ class WickedManager(object):
|
||||
if stgs.get('ipv6_address', None):
|
||||
ipcfg += 'IPADDR_V6=' + stgs['ipv6_address'] + '\n'
|
||||
v6gw = stgs.get('ipv6_gateway', None)
|
||||
extras = parse_extra_settings(stgs)
|
||||
for key in extras:
|
||||
ipcfg += '{0}={1}\n'.format(key, shlex.quote(extras[key]))
|
||||
cname = None
|
||||
if len(cfg['interfaces']) > 1: # creating new team
|
||||
if not stgs.get('team_mode', None):
|
||||
@@ -475,6 +504,7 @@ class NetworkManager(object):
|
||||
cmdargs['ipv4.dns'] = ','.join(dns4)
|
||||
if dns6:
|
||||
cmdargs['ipv6.dns'] = ','.join(dns6)
|
||||
cmdargs.update(parse_extra_settings(stgs))
|
||||
if len(cfg['interfaces']) > 1: # team time.. should be..
|
||||
if not cfg['settings'].get('team_mode', None):
|
||||
sys.stderr.write("Warning, multiple interfaces ({0}) without a team_mode, skipping setup\n".format(','.join(cfg['interfaces'])))
|
||||
|
||||
@@ -111,6 +111,14 @@ def get_interface_name(iname, settings):
|
||||
return iname
|
||||
return None
|
||||
|
||||
def parse_extra_settings(stgs):
|
||||
extras = {}
|
||||
for kv in stgs.get('extra_settings', '').split(';'):
|
||||
k, _, v = kv.partition('=')
|
||||
if k.strip():
|
||||
extras[k.strip()] = v.strip()
|
||||
return extras
|
||||
|
||||
class NetplanManager(object):
|
||||
def __init__(self, deploycfg):
|
||||
self.cfgbydev = {}
|
||||
@@ -192,6 +200,23 @@ class NetplanManager(object):
|
||||
if dnsdomain not in currdnsdomain:
|
||||
needcfgwrite = True
|
||||
currdnsdomain.append(dnsdomain)
|
||||
extras = parse_extra_settings(stgs)
|
||||
if extras:
|
||||
devdict = self.cfgbydev.setdefault(devname, {})
|
||||
for key in extras:
|
||||
try:
|
||||
val = yaml.safe_load(extras[key])
|
||||
except yaml.YAMLError:
|
||||
val = extras[key]
|
||||
keyptr = devdict
|
||||
keypath = key.split('.')
|
||||
for k in keypath[:-1]:
|
||||
if not isinstance(keyptr.get(k, None), dict):
|
||||
keyptr[k] = {}
|
||||
keyptr = keyptr[k]
|
||||
if keyptr.get(keypath[-1], None) != val:
|
||||
needcfgwrite = True
|
||||
keyptr[keypath[-1]] = val
|
||||
if needcfgwrite:
|
||||
needcfgapply = True
|
||||
newcfg = {'network': {'version': 2, 'ethernets': {devname: self.cfgbydev[devname]}}}
|
||||
@@ -261,6 +286,9 @@ class WickedManager(object):
|
||||
if stgs.get('ipv6_address', None):
|
||||
ipcfg += 'IPADDR_V6=' + stgs['ipv6_address'] + '\n'
|
||||
v6gw = stgs.get('ipv6_gateway', None)
|
||||
extras = parse_extra_settings(stgs)
|
||||
for key in extras:
|
||||
ipcfg += '{0}={1}\n'.format(key, shlex.quote(extras[key]))
|
||||
cname = None
|
||||
if len(cfg['interfaces']) > 1: # creating new team
|
||||
if not stgs.get('team_mode', None):
|
||||
@@ -400,6 +428,7 @@ class NetworkManager(object):
|
||||
cmdargs['ipv4.dns'] = ','.join(dns4)
|
||||
if dns6:
|
||||
cmdargs['ipv6.dns'] = ','.join(dns6)
|
||||
cmdargs.update(parse_extra_settings(stgs))
|
||||
if len(cfg['interfaces']) > 1: # team time.. should be..
|
||||
if not cfg['settings'].get('team_mode', None):
|
||||
sys.stderr.write("Warning, multiple interfaces ({0}) without a team_mode, skipping setup\n".format(','.join(cfg['interfaces'])))
|
||||
|
||||
@@ -34,8 +34,8 @@ done
|
||||
if [ -e /tmp/installdisk ]; then
|
||||
instdisk=$(cat /tmp/installdisk)
|
||||
else
|
||||
for blockdev in $(ls /sys/class/block/); do
|
||||
shortname=$(basename $blockdev)
|
||||
for blockdev in /sys/class/block/*; do
|
||||
shortname=$(basename "$blockdev")
|
||||
if [ "$shortname" != "${shortname%loop*}" ]; then
|
||||
continue
|
||||
fi
|
||||
@@ -62,7 +62,7 @@ else
|
||||
done
|
||||
fi
|
||||
if [ -z "$instdisk" ]; then
|
||||
if [ ! -z "$sraid"]; then
|
||||
if [ ! -z "$sraid" ]; then
|
||||
instdisk=$sraid
|
||||
elif [ ! -z "$onbdisk" ]; then
|
||||
instdisk=$onbdisk
|
||||
|
||||
@@ -34,8 +34,8 @@ done
|
||||
if [ -e /tmp/installdisk ]; then
|
||||
instdisk=$(cat /tmp/installdisk)
|
||||
else
|
||||
for blockdev in $(ls /sys/class/block/); do
|
||||
shortname=$(basename $blockdev)
|
||||
for blockdev in /sys/class/block/*; do
|
||||
shortname=$(basename "$blockdev")
|
||||
if [ "$shortname" != "${shortname%loop*}" ]; then
|
||||
continue
|
||||
fi
|
||||
@@ -62,7 +62,7 @@ else
|
||||
done
|
||||
fi
|
||||
if [ -z "$instdisk" ]; then
|
||||
if [ ! -z "$sraid"]; then
|
||||
if [ ! -z "$sraid" ]; then
|
||||
instdisk=$sraid
|
||||
elif [ ! -z "$onbdisk" ]; then
|
||||
instdisk=$onbdisk
|
||||
|
||||
@@ -329,8 +329,8 @@ class Command(object):
|
||||
await self.oem_init()
|
||||
|
||||
if hasattr(self._oem, 'set_power'):
|
||||
return self._oem.set_power(powerstate,
|
||||
bridge_request=bridge_request)
|
||||
return await self._oem.set_power(powerstate,
|
||||
bridge_request=bridge_request)
|
||||
|
||||
if hasattr(self._oem, 'process_power_state'):
|
||||
powerstate = self._oem.process_power_state(
|
||||
@@ -675,7 +675,7 @@ class Command(object):
|
||||
await self.init_sdr()
|
||||
for fruid in self._sdr.fru:
|
||||
if self._sdr.fru[fruid].fru_name == component:
|
||||
return self._oem.process_fru(fru.FRU(
|
||||
return await self._oem.process_fru(fru.FRU(
|
||||
ipmicmd=self, fruid=fruid,
|
||||
sdr=self._sdr.fru[fruid]).info, component)
|
||||
return await self._oem.get_inventory_of_component(component)
|
||||
@@ -738,7 +738,8 @@ class Command(object):
|
||||
This provides a detailed view of the LEDs of the managed system.
|
||||
"""
|
||||
await self.oem_init()
|
||||
return await self._oem.get_leds()
|
||||
async for led in self._oem.get_leds():
|
||||
yield led
|
||||
|
||||
async def get_ntp_enabled(self):
|
||||
await self.oem_init()
|
||||
@@ -806,7 +807,7 @@ class Command(object):
|
||||
rsp = await self.raw_command(command=0x2d, netfn=4,
|
||||
rslun=currsensor.sensor_lun,
|
||||
data=(currsensor.sensor_number,))
|
||||
return self._sdr.sensors[sensor].decode_sensor_reading(
|
||||
return await self._sdr.sensors[sensor].decode_sensor_reading(
|
||||
self, rsp['data'])
|
||||
await self.oem_init()
|
||||
return await self._oem.get_sensor_reading(sensorname)
|
||||
@@ -853,9 +854,9 @@ class Command(object):
|
||||
else:
|
||||
raise Exception("Unrecognized data format " + repr(fetchdata))
|
||||
|
||||
def get_extended_bmc_configuration(self):
|
||||
self.oem_init()
|
||||
return self._oem.get_extended_bmc_configuration()
|
||||
async def get_extended_bmc_configuration(self):
|
||||
await self.oem_init()
|
||||
return await self._oem.get_extended_bmc_configuration()
|
||||
|
||||
async def get_bmc_configuration(self):
|
||||
await self.oem_init()
|
||||
@@ -1090,7 +1091,7 @@ class Command(object):
|
||||
if rsp['code'] == 203: # Sensor does not exist, optional dev
|
||||
continue
|
||||
raise exc.IpmiException(rsp['error'], code=rsp['code'])
|
||||
yield self._sdr.sensors[sensor].\
|
||||
yield await self._sdr.sensors[sensor].\
|
||||
decode_sensor_reading(self, rsp['data'])
|
||||
await self.oem_init()
|
||||
async for reading in self._oem.get_sensor_data():
|
||||
@@ -1580,7 +1581,7 @@ class Command(object):
|
||||
}
|
||||
b |= privilege_levels[privilege_level] & 0b00000111
|
||||
data.append(b)
|
||||
response = self.raw_command(netfn=0x06, command=0x40, data=data)
|
||||
response = await self.raw_command(netfn=0x06, command=0x40, data=data)
|
||||
if 'error' in response:
|
||||
raise Exception(response['error'])
|
||||
return True
|
||||
@@ -2311,4 +2312,4 @@ class Command(object):
|
||||
|
||||
"""
|
||||
await self.oem_init()
|
||||
return await self._oem.set_oem_extended_privilleges(uid)
|
||||
return await self._oem.set_oem_extended_privilleges(uid)
|
||||
|
||||
@@ -133,7 +133,7 @@ class Console(object):
|
||||
response['code'])
|
||||
return
|
||||
if 'error' in response:
|
||||
self._print_error(response['error'])
|
||||
await self._print_error(response['error'])
|
||||
return
|
||||
self.activated = True
|
||||
# data[0:3] is reserved except for the test mode, which we don't use
|
||||
@@ -381,7 +381,7 @@ class Console(object):
|
||||
await self.send_payload(ackpayload, retry=False)
|
||||
except exc.IpmiException:
|
||||
# if the session is broken, then close the SOL session
|
||||
self.close()
|
||||
await self.close()
|
||||
if self.myseq != 0 and ackseq == self.myseq: # the bmc has something
|
||||
# to say about last xmit
|
||||
self.awaitingack = False
|
||||
|
||||
@@ -519,15 +519,38 @@ class EventHandler(object):
|
||||
selentry = bytearray(origselentry)
|
||||
event = {}
|
||||
event['record_id'] = struct.unpack_from('<H', origselentry[:2])[0]
|
||||
if selentry[2] == 2 or (0xc0 <= selentry[2] <= 0xdf):
|
||||
if selentry[2] < 0xe0 and len(selentry) >= 7:
|
||||
# Either standard, or at least the timestamp is standard
|
||||
event['timecode'] = struct.unpack_from('<I', buffer(selentry[3:7])
|
||||
)[0]
|
||||
if selentry[2] == 2: # ipmi defined standard format
|
||||
self._decode_standard_event(selentry[7:], event)
|
||||
if selentry[2] < 0xc0:
|
||||
# ipmi defined standard format (0x02); like ipmitool, extend the
|
||||
# same treatment to reserved types some BMCs use (e.g. AMI 0x04)
|
||||
try:
|
||||
self._decode_standard_event(selentry[7:], event)
|
||||
except Exception:
|
||||
# a record body that does not actually follow the standard
|
||||
# layout; discard any partially decoded fields and pass it
|
||||
# through raw rather than aborting the fetch
|
||||
for key in list(event):
|
||||
if key not in ('record_id', 'timecode'):
|
||||
del event[key]
|
||||
event['oemdata'] = selentry[3:]
|
||||
elif 0xc0 <= selentry[2] <= 0xdf:
|
||||
event['oemid'] = selentry[7:10]
|
||||
event['oemdata'] = selentry[10:]
|
||||
elif selentry[2] == 0xf0:
|
||||
# De facto standard from the Linux kernel ipmi panic logger,
|
||||
# also recognized by ipmitool and freeipmi: byte 4 is a chunk
|
||||
# sequence number and bytes 5-15 carry a piece of the panic
|
||||
# string
|
||||
event['event'] = 'Linux kernel panic: {0}'.format(
|
||||
selentry[5:16].partition(b'\x00')[0].decode(
|
||||
'utf-8', 'replace'))
|
||||
event['severity'] = pygconst.Health.Critical
|
||||
# this layout is defined by the kernel convention rather than
|
||||
# the BMC vendor, so bypass the OEM handler
|
||||
return event
|
||||
elif selentry[2] >= 0xe0:
|
||||
# In this class of OEM message, all bytes are OEM, interpretation
|
||||
# is wholly left up to the OEM layer, using the OEM ID of the BMC
|
||||
|
||||
@@ -142,7 +142,9 @@ class OEMHandler(object):
|
||||
to apply some transform to some field to suit their conventions.
|
||||
"""
|
||||
event['oem_handler'] = None
|
||||
evdata = event['event_data_bytes']
|
||||
evdata = event.get('event_data_bytes')
|
||||
if evdata is None:
|
||||
return
|
||||
if evdata[0] & 0b11000000 == 0b10000000:
|
||||
event['oem_byte2'] = evdata[1]
|
||||
if evdata[0] & 0b110000 == 0b100000:
|
||||
|
||||
@@ -73,7 +73,7 @@ async def run_command_with_retry(connection, data):
|
||||
except pygexc.IpmiException as e:
|
||||
if e.ipmicode != 0xa or not tries:
|
||||
raise
|
||||
connection.ipmi_session.pause(1)
|
||||
await connection.ipmi_session.pause(1)
|
||||
|
||||
|
||||
def _convert_syntax(raw):
|
||||
@@ -197,7 +197,7 @@ class LenovoFirmwareConfig(object):
|
||||
break
|
||||
except KeyError:
|
||||
pass
|
||||
self.connection.ipmi_session.pause(5)
|
||||
await self.connection.ipmi_session.pause(5)
|
||||
filehandle = response['data'][3:7]
|
||||
filehandle = struct.unpack("<I", filehandle)[0]
|
||||
return filehandle
|
||||
|
||||
@@ -38,7 +38,7 @@ class EnergyManager(object):
|
||||
'Node Power', 'Total Power')
|
||||
self._mypowermeters = ('node power', 'total power', 'gpu power', 'riser 1 power', 'riser 2 power')
|
||||
self._usefapm = True
|
||||
return
|
||||
return self
|
||||
except pygexc.IpmiException:
|
||||
pass
|
||||
|
||||
@@ -80,7 +80,7 @@ class EnergyManager(object):
|
||||
break
|
||||
except pygexc.IpmiException as ie:
|
||||
if tries and ie.ipmicode == 0xc3:
|
||||
ipmicmd.ipmi_session.pause(0.1)
|
||||
await ipmicmd.ipmi_session.pause(0.1)
|
||||
continue
|
||||
raise
|
||||
if rsp is None:
|
||||
@@ -159,8 +159,14 @@ class Energy(object):
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
import asyncio
|
||||
import os
|
||||
import aiohmi.ipmi.command as cmd
|
||||
import sys
|
||||
c = cmd.Command(sys.argv[1], os.environ['BMCUSER'], os.environ['BMCPASS'])
|
||||
EnergyManager(c).get_dc_energy(c)
|
||||
|
||||
async def main():
|
||||
c = await cmd.Command.create(sys.argv[1], os.environ['BMCUSER'], os.environ['BMCPASS'])
|
||||
manager = await EnergyManager.create(c)
|
||||
print(await manager.get_dc_energy(c))
|
||||
|
||||
asyncio.run(main())
|
||||
|
||||
@@ -336,9 +336,9 @@ class OEMHandler(generic.OEMHandler):
|
||||
event['component'] += ' {0}'.format(evdata[1] & 0b11111)
|
||||
|
||||
async def reseat_bay(self, bay):
|
||||
if self.is_fpc:
|
||||
if await self.is_fpc():
|
||||
return await self.smmhandler.reseat_bay(bay)
|
||||
elif self.has_xcc and bay == -1:
|
||||
elif await self.has_xcc() and bay == -1:
|
||||
return await self.immhandler.reseat()
|
||||
return await super(OEMHandler, self).reseat_bay(bay)
|
||||
|
||||
@@ -349,7 +349,7 @@ class OEMHandler(generic.OEMHandler):
|
||||
elif await self.is_fpc():
|
||||
return await self.smmhandler.get_ntp_enabled(self._fpc_variant)
|
||||
elif self.has_tsma:
|
||||
return self.tsmahandler.get_ntp_enabled()
|
||||
return await self.tsmahandler.get_ntp_enabled()
|
||||
return None
|
||||
|
||||
async def get_ntp_servers(self):
|
||||
@@ -360,9 +360,9 @@ class OEMHandler(generic.OEMHandler):
|
||||
srvs.append(ntpres['data'][129:257].rstrip('\x00'))
|
||||
return srvs
|
||||
if await self.is_fpc():
|
||||
return await self.smmhandler.get_ntp_servers()
|
||||
return self.smmhandler.get_ntp_servers()
|
||||
if self.has_tsma:
|
||||
return self.tsmahandler.get_ntp_servers()
|
||||
return await self.tsmahandler.get_ntp_servers()
|
||||
return ()
|
||||
|
||||
async def set_ntp_enabled(self, enabled):
|
||||
@@ -375,7 +375,7 @@ class OEMHandler(generic.OEMHandler):
|
||||
netfn=0x32, command=0xa8, data=(3, 0), timeout=15)
|
||||
return True
|
||||
if await self.is_fpc():
|
||||
await self.smmhandler.set_ntp_enabled(enabled)
|
||||
self.smmhandler.set_ntp_enabled(enabled)
|
||||
return True
|
||||
if self.has_tsma:
|
||||
await self.tsmahandler.set_ntp_enabled(enabled)
|
||||
@@ -393,7 +393,7 @@ class OEMHandler(generic.OEMHandler):
|
||||
if not 0 <= index <= 2:
|
||||
raise pygexc.InvalidParameterValue(
|
||||
'SMM supports indexes 0 through 2')
|
||||
await self.smmhandler.set_ntp_server(server, index)
|
||||
self.smmhandler.set_ntp_server(server, index)
|
||||
return True
|
||||
elif self.has_tsma:
|
||||
if not (0 <= index <= 1):
|
||||
@@ -493,8 +493,7 @@ class OEMHandler(generic.OEMHandler):
|
||||
if await self.has_tsm() or await self.has_ami() or await self.has_asrock():
|
||||
# Thinkserver with TSM
|
||||
if not self.oem_inventory_info:
|
||||
async for desc in self._collect_tsm_inventory():
|
||||
yield desc
|
||||
await self._collect_tsm_inventory()
|
||||
for compname in self.oem_inventory_info:
|
||||
yield compname
|
||||
elif await self.has_imm():
|
||||
@@ -516,21 +515,22 @@ class OEMHandler(generic.OEMHandler):
|
||||
elif await self.is_fpc():
|
||||
async for compname in self.smmhandler.get_inventory_descriptions(
|
||||
self.ipmicmd, await self.is_fpc()):
|
||||
yield (compname, self.smmhandler.get_inventory_of_component(
|
||||
yield (compname, await self.smmhandler.get_inventory_of_component(
|
||||
self.ipmicmd, compname))
|
||||
|
||||
async def get_sensor_data(self):
|
||||
if await self.has_imm():
|
||||
async for name in self.immhandler.get_oem_sensor_names(self.ipmicmd):
|
||||
yield self.immhandler.get_oem_sensor_reading(name,
|
||||
self.ipmicmd)
|
||||
yield await self.immhandler.get_oem_sensor_reading(name,
|
||||
self.ipmicmd)
|
||||
elif await self.is_fpc():
|
||||
for name in nextscale.get_sensor_names(self.ipmicmd,
|
||||
self._fpc_variant):
|
||||
yield nextscale.get_sensor_reading(name, self.ipmicmd,
|
||||
self._fpc_variant)
|
||||
yield await nextscale.get_sensor_reading(name, self.ipmicmd,
|
||||
self._fpc_variant)
|
||||
elif await self.has_ami():
|
||||
self.get_ami_sensor_data()
|
||||
async for reading in self.get_ami_sensor_data():
|
||||
yield reading
|
||||
|
||||
async def get_sensor_descriptions(self):
|
||||
if await self.has_imm():
|
||||
@@ -548,26 +548,26 @@ class OEMHandler(generic.OEMHandler):
|
||||
|
||||
async def get_sensor_reading(self, sensorname):
|
||||
if await self.has_imm():
|
||||
return self.immhandler.get_oem_sensor_reading(sensorname,
|
||||
self.ipmicmd)
|
||||
return await self.immhandler.get_oem_sensor_reading(sensorname,
|
||||
self.ipmicmd)
|
||||
elif await self.is_fpc():
|
||||
return nextscale.get_sensor_reading(sensorname, self.ipmicmd,
|
||||
self._fpc_variant)
|
||||
return await nextscale.get_sensor_reading(sensorname, self.ipmicmd,
|
||||
self._fpc_variant)
|
||||
elif await self.has_ami():
|
||||
self.get_ami_sensor_reading(sensorname)
|
||||
return await self.get_ami_sensor_reading(sensorname)
|
||||
return ()
|
||||
|
||||
async def get_inventory_of_component(self, component):
|
||||
if await self.has_tsm() or await self.has_ami() or await self.has_asrock():
|
||||
await self._collect_tsm_inventory()
|
||||
return await self.oem_inventory_info.get(component, None)
|
||||
return self.oem_inventory_info.get(component, None)
|
||||
if await self.has_imm():
|
||||
return await self.immhandler.get_component_inventory(component)
|
||||
if await self.is_fpc():
|
||||
return await self.smmhandler.get_inventory_of_component(component)
|
||||
return await self.smmhandler.get_inventory_of_component(self.ipmicmd, component)
|
||||
|
||||
def get_cmd_type(self, categorie_item, catspec):
|
||||
if self.has_asrock:
|
||||
async def get_cmd_type(self, categorie_item, catspec):
|
||||
if await self.has_asrock():
|
||||
cmd_type = catspec["command"]["asrock"]
|
||||
elif categorie_item in categorie_items:
|
||||
cmd_type = catspec["command"]["lenovo"]
|
||||
@@ -578,9 +578,7 @@ class OEMHandler(generic.OEMHandler):
|
||||
|
||||
async def _collect_tsm_inventory(self):
|
||||
self.oem_inventory_info = {}
|
||||
asrock = False
|
||||
if self.has_asrock:
|
||||
asrock = True
|
||||
asrock = await self.has_asrock()
|
||||
for catid, catspec in inventory.categories.items():
|
||||
# skip the inventory fields if the system is RS160
|
||||
if asrock and catid not in categorie_items:
|
||||
@@ -589,7 +587,7 @@ class OEMHandler(generic.OEMHandler):
|
||||
and catspec["workaround_bmc_bug"](
|
||||
"ami" if await self.has_ami() else "lenovo")):
|
||||
rsp = None
|
||||
cmd = self.get_cmd_type(catid, catspec)
|
||||
cmd = await self.get_cmd_type(catid, catspec)
|
||||
tmp_command = dict(cmd)
|
||||
tmp_command["data"] = list(tmp_command["data"])
|
||||
count = 0
|
||||
@@ -616,7 +614,7 @@ class OEMHandler(generic.OEMHandler):
|
||||
rsp["data"] = buffer(bytearray(rsp["data"]))
|
||||
else:
|
||||
try:
|
||||
cmd = self.get_cmd_type(catid, catspec)
|
||||
cmd = await self.get_cmd_type(catid, catspec)
|
||||
rsp = await self.ipmicmd.raw_command(**cmd)
|
||||
except pygexc.IpmiException:
|
||||
continue
|
||||
@@ -768,7 +766,7 @@ class OEMHandler(generic.OEMHandler):
|
||||
except (AttributeError, KeyError, IndexError):
|
||||
pass
|
||||
if await self.has_xcc() and name and name.startswith('PSU '):
|
||||
self.immhandler.augment_psu_info(fru, name)
|
||||
await self.immhandler.augment_psu_info(fru, name)
|
||||
if (await self.has_xcc() and 'memory_type' in fru
|
||||
and fru['memory_type'] == 'Unknown'):
|
||||
await self.immhandler.fetch_dimm(name, fru)
|
||||
@@ -851,7 +849,7 @@ class OEMHandler(generic.OEMHandler):
|
||||
async def get_oem_firmware(self, bmcver, components, category):
|
||||
if await self.has_tsm() or await self.has_ami() or await self.has_asrock():
|
||||
command = firmware.get_categories()["firmware"]
|
||||
fw_cmd = self.get_cmd_type("firmware", command)
|
||||
fw_cmd = await self.get_cmd_type("firmware", command)
|
||||
rsp = await self.ipmicmd.raw_command(**fw_cmd)
|
||||
|
||||
# the newest Lenovo ThinkServer versions are returning Bios version
|
||||
@@ -859,7 +857,7 @@ class OEMHandler(generic.OEMHandler):
|
||||
bios_versions = None
|
||||
if await self.has_tsm() or await self.has_asrock():
|
||||
bios_command = firmware.get_categories()["bios_version"]
|
||||
bios_cmd = self.get_cmd_type("bios_version", bios_command)
|
||||
bios_cmd = await self.get_cmd_type("bios_version", bios_command)
|
||||
bios_rsp = await self.ipmicmd.raw_command(**bios_cmd)
|
||||
if await self.has_asrock():
|
||||
bios_versions = bios_rsp['data']
|
||||
@@ -942,7 +940,7 @@ class OEMHandler(generic.OEMHandler):
|
||||
name += rsp['data'][:]
|
||||
return name.rstrip('\x00')
|
||||
elif await self.is_fpc():
|
||||
return await self.smmhandler.get_domain()
|
||||
return self.smmhandler.get_domain()
|
||||
|
||||
async def set_oem_domain_name(self, name):
|
||||
if await self.has_tsm():
|
||||
@@ -961,20 +959,20 @@ class OEMHandler(generic.OEMHandler):
|
||||
await self._restart_dns()
|
||||
return
|
||||
elif await self.is_fpc():
|
||||
await self.smmhandler.set_domain(name)
|
||||
self.smmhandler.set_domain(name)
|
||||
|
||||
async def set_hostname(self, hostname):
|
||||
if await self.has_xcc():
|
||||
return await self.immhandler.set_hostname(hostname)
|
||||
elif await self.is_fpc():
|
||||
return await self.smmhandler.set_hostname(hostname)
|
||||
return self.smmhandler.set_hostname(hostname)
|
||||
return await super(OEMHandler, self).set_hostname(hostname)
|
||||
|
||||
async def get_hostname(self):
|
||||
if await self.has_xcc():
|
||||
return await self.immhandler.get_hostname()
|
||||
elif await self.is_fpc():
|
||||
return await self.smmhandler.get_hostname()
|
||||
return self.smmhandler.get_hostname()
|
||||
return await super(OEMHandler, self).get_hostname()
|
||||
|
||||
""" Gets a remote console launcher for a Lenovo ThinkServer.
|
||||
@@ -1029,14 +1027,14 @@ class OEMHandler(generic.OEMHandler):
|
||||
|
||||
async def add_extra_net_configuration(self, netdata, channel=None):
|
||||
if await self.has_tsm():
|
||||
ipv6_addr = await self.ipmicmd.raw_command(
|
||||
ipv6_addr = (await self.ipmicmd.raw_command(
|
||||
netfn=0x0c, command=0x02,
|
||||
data=(0x01, 0xc5, 0x00, 0x00))["data"][1:]
|
||||
data=(0x01, 0xc5, 0x00, 0x00)))['data'][1:]
|
||||
if not ipv6_addr:
|
||||
return
|
||||
rspdata = await self.ipmicmd.raw_command(
|
||||
rspdata = (await self.ipmicmd.raw_command(
|
||||
netfn=0xc, command=0x02,
|
||||
data=(0x1, 0xc6, 0, 0))['data']
|
||||
data=(0x1, 0xc6, 0, 0)))['data']
|
||||
ipv6_prefix_ba = bytearray(rspdata)
|
||||
ipv6_prefix = ipv6_prefix_ba[1]
|
||||
|
||||
@@ -1135,9 +1133,9 @@ class OEMHandler(generic.OEMHandler):
|
||||
targshortname = _megarac_abbrev_image(imagename)
|
||||
shortnames = await self._megarac_fetch_image_shortnames()
|
||||
while targshortname not in shortnames:
|
||||
self.ipmicmd.wait_for_rsp(1)
|
||||
await self.ipmicmd.wait_for_rsp(1)
|
||||
shortnames = await self._megarac_fetch_image_shortnames()
|
||||
self.ipmicmd.ipmi_session.pause(10)
|
||||
await self.ipmicmd.ipmi_session.pause(10)
|
||||
try:
|
||||
await self.ipmicmd.raw_command(netfn=0x32, command=0xa0, data=(1, 0))
|
||||
await self.ipmicmd.ipmi_session.pause(5)
|
||||
|
||||
@@ -837,7 +837,7 @@ class IMMClient(object):
|
||||
hwmap[aname] = bdata
|
||||
self.datacache['lenovo_cached_hwmap'] = (hwmap,
|
||||
util._monotonic_time())
|
||||
self.weblogout()
|
||||
await self.weblogout()
|
||||
return hwmap
|
||||
|
||||
async def get_firmware_inventory(self, bmcver, components, category):
|
||||
@@ -1432,7 +1432,7 @@ class XCCClient(IMMClient):
|
||||
if controller != disk.id[0]:
|
||||
raise pygexc.UnsupportedFunctionality(
|
||||
'Cannot span arrays across controllers')
|
||||
raidmap = self._raid_number_map(controller)
|
||||
raidmap = await self._raid_number_map(controller)
|
||||
if not raidmap:
|
||||
raise pygexc.InvalidParameterValue(
|
||||
'There are no available drives for a new array')
|
||||
@@ -1485,21 +1485,21 @@ class XCCClient(IMMClient):
|
||||
# TODO(): adding new volume to existing array would be here
|
||||
pass
|
||||
|
||||
def _make_jbod(self, disk, realcfg):
|
||||
async def _make_jbod(self, disk, realcfg):
|
||||
currstatus = self._get_status(disk, realcfg)
|
||||
if currstatus.lower() == 'jbod':
|
||||
return
|
||||
self._make_available(disk, realcfg)
|
||||
self._set_drive_state(disk, 16)
|
||||
await self._make_available(disk, realcfg)
|
||||
await self._set_drive_state(disk, 16)
|
||||
|
||||
def _make_global_hotspare(self, disk, realcfg):
|
||||
async def _make_global_hotspare(self, disk, realcfg):
|
||||
currstatus = self._get_status(disk, realcfg)
|
||||
if currstatus.lower() == 'global hot spare':
|
||||
return
|
||||
self._make_available(disk, realcfg)
|
||||
self._set_drive_state(disk, 1)
|
||||
await self._make_available(disk, realcfg)
|
||||
await self._set_drive_state(disk, 1)
|
||||
|
||||
def _make_available(self, disk, realcfg):
|
||||
async def _make_available(self, disk, realcfg):
|
||||
# 8 if jbod, 4 if hotspare.., leave alone if already...
|
||||
currstatus = self._get_status(disk, realcfg)
|
||||
newstate = None
|
||||
@@ -1509,7 +1509,7 @@ class XCCClient(IMMClient):
|
||||
newstate = 4
|
||||
elif currstatus.lower() == 'jbod':
|
||||
newstate = 8
|
||||
self._set_drive_state(disk, newstate)
|
||||
await self._set_drive_state(disk, newstate)
|
||||
|
||||
def _get_status(self, disk, realcfg):
|
||||
for cfgdisk in realcfg.disks:
|
||||
@@ -1534,7 +1534,7 @@ class XCCClient(IMMClient):
|
||||
wc = await self.wc()
|
||||
rsp = await wc.grab_json_response(
|
||||
'/api/function', {'raidlink_ClearRaidConf': '1'})
|
||||
self.weblogout()
|
||||
await self.weblogout()
|
||||
if rsp['return'] != 0:
|
||||
raise Exception('Unexpected return to clear config: ' + repr(rsp))
|
||||
|
||||
@@ -1558,10 +1558,10 @@ class XCCClient(IMMClient):
|
||||
if rsp.get('return', -1) != 0:
|
||||
raise Exception(
|
||||
'Unexpected return to volume deletion: ' + repr(rsp))
|
||||
self._wait_storage_async()
|
||||
await self._wait_storage_async()
|
||||
for disk in cfgspec.disks:
|
||||
await self._make_available(disk, realcfg)
|
||||
self.weblogout()
|
||||
await self.weblogout()
|
||||
|
||||
async def apply_storage_configuration(self, cfgspec):
|
||||
realcfg = await self.get_storage_configuration(False)
|
||||
@@ -2166,7 +2166,7 @@ class XCCClient(IMMClient):
|
||||
retry = 3
|
||||
while not complete and retry > 0:
|
||||
try:
|
||||
pgress, status = self.grab_redfish_response_with_status(
|
||||
pgress, status = await self.grab_redfish_response_with_status(
|
||||
monitorurl)
|
||||
except socket.timeout:
|
||||
pgress = None
|
||||
@@ -2203,10 +2203,10 @@ class XCCClient(IMMClient):
|
||||
return 'complete'
|
||||
return 'pending'
|
||||
finally:
|
||||
self.grab_redfish_response_with_status(
|
||||
await self.grab_redfish_response_with_status(
|
||||
'/redfish/v1/UpdateService',
|
||||
{'HttpPushUriTargetsBusy': False}, method='PATCH')
|
||||
self.grab_redfish_response_with_status(
|
||||
await self.grab_redfish_response_with_status(
|
||||
'/redfish/v1/UpdateService',
|
||||
{'HttpPushUriTargets': []}, method='PATCH')
|
||||
|
||||
@@ -2363,7 +2363,7 @@ class XCCClient(IMMClient):
|
||||
if verifystatus == 2:
|
||||
raise Exception('Failed to verify firmware image')
|
||||
if verifystatus != 1:
|
||||
ipmisession.Session.pause(1)
|
||||
await ipmisession.Session.pause(1)
|
||||
if verifystatus not in (0, 1, 255):
|
||||
errmsg = repr(rsp) if rsp else wc.lastjsonerror
|
||||
raise Exception(
|
||||
@@ -2408,7 +2408,7 @@ class XCCClient(IMMClient):
|
||||
errmsg = repr(rsp) if rsp else wc.lastjsonerror
|
||||
raise Exception(
|
||||
'Unexpected result from PCI select: ' + errmsg)
|
||||
self.set_property('/v2/ibmc/uefi/force-inventory', 1)
|
||||
await self.set_property('/v2/ibmc/uefi/force-inventory', 1)
|
||||
else:
|
||||
await self._refresh_token()
|
||||
rsp = await wc.grab_json_response(
|
||||
@@ -2433,7 +2433,7 @@ class XCCClient(IMMClient):
|
||||
complete = False
|
||||
while not complete:
|
||||
await self._refresh_token()
|
||||
ipmisession.Session.pause(3)
|
||||
await ipmisession.Session.pause(3)
|
||||
rsp = await wc.grab_json_response(
|
||||
'/api/dataset/imm_firmware_progress')
|
||||
progress({'phase': 'apply',
|
||||
|
||||
@@ -671,14 +671,14 @@ class SMMClient(object):
|
||||
for bayn in range(1, numbays + 1):
|
||||
if fnmatch.fnmatch('bay{0}_user_cap'.format(bayn),
|
||||
key.lower()):
|
||||
self.set_bay_cap(bayn, changeset[key]['value'])
|
||||
await self.set_bay_cap(bayn, changeset[key]['value'])
|
||||
if fnmatch.fnmatch(
|
||||
'bay{0}_user_cap_active'.format(bayn), key.lower()):
|
||||
self.set_bay_cap_active(bayn, changeset[key]['value'])
|
||||
await self.set_bay_cap_active(bayn, changeset[key]['value'])
|
||||
if fnmatch.fnmatch('chassis_user_cap', key.lower()):
|
||||
self.set_bay_cap(numbays + 1, changeset[key]['value'])
|
||||
await self.set_bay_cap(numbays + 1, changeset[key]['value'])
|
||||
if fnmatch.fnmatch('chassis_user_cap_active', key.lower()):
|
||||
self.set_bay_cap_active(numbays + 1, changeset[key]['value'])
|
||||
await self.set_bay_cap_active(numbays + 1, changeset[key]['value'])
|
||||
if fnmatch.fnmatch('fanspeed', key.lower()):
|
||||
for mode in self.fanmodes:
|
||||
byteval = mode
|
||||
@@ -784,7 +784,7 @@ class SMMClient(object):
|
||||
if progress:
|
||||
progress({'phase': 'initializing', 'progress': initpct})
|
||||
while bytearray(rsp['data'])[0] != 0:
|
||||
ipmisession.Session.pause(3)
|
||||
await ipmisession.Session.pause(3)
|
||||
initpct += 3.0
|
||||
if initpct > 99.0:
|
||||
initpct = 99.0
|
||||
@@ -816,10 +816,10 @@ class SMMClient(object):
|
||||
async def process_fru(self, fru):
|
||||
smmv1 = self.smm_variant & 0xf0 == 0
|
||||
# TODO(jjohnson2): can also get EIOM, SMM, and riser data if warranted
|
||||
snum = bytes(await self.ipmicmd.raw_command(
|
||||
netfn=0x32, command=0xb0, data=(5, 1))['data'][:])
|
||||
mnum = bytes(await self.ipmicmd.raw_command(
|
||||
netfn=0x32, command=0xb0, data=(5, 0))['data'][:])
|
||||
snum = bytes((await self.ipmicmd.raw_command(
|
||||
netfn=0x32, command=0xb0, data=(5, 1)))['data'][:])
|
||||
mnum = bytes((await self.ipmicmd.raw_command(
|
||||
netfn=0x32, command=0xb0, data=(5, 0)))['data'][:])
|
||||
if not smmv1:
|
||||
snum = snum[2:]
|
||||
mnum = mnum[2:]
|
||||
@@ -1039,7 +1039,7 @@ class SMMClient(object):
|
||||
complete = False
|
||||
tries = 0
|
||||
while not complete:
|
||||
ipmisession.Session.pause(3)
|
||||
await ipmisession.Session.pause(3)
|
||||
wc.request('POST', '/data', 'get=fwProgress,fwUpdate')
|
||||
try:
|
||||
rsp = wc.getresponse()
|
||||
@@ -1061,16 +1061,16 @@ class SMMClient(object):
|
||||
complete = percent >= 100.0
|
||||
return 'complete'
|
||||
|
||||
def get_inventory_descriptions(self, ipmicmd, variant):
|
||||
async def get_inventory_descriptions(self, ipmicmd, variant):
|
||||
if variant >> 5 == 0:
|
||||
return
|
||||
psucount = get_psu_count(ipmicmd, variant)
|
||||
psucount = await get_psu_count(ipmicmd, variant)
|
||||
for idx in range(psucount):
|
||||
yield 'PSU {}'.format(idx + 1)
|
||||
|
||||
def get_inventory_of_component(self, ipmicmd, component):
|
||||
async def get_inventory_of_component(self, ipmicmd, component):
|
||||
psuidx = int(component.replace('PSU ', ''))
|
||||
return self.get_psu_info(ipmicmd, psuidx)
|
||||
return await self.get_psu_info(ipmicmd, psuidx)
|
||||
|
||||
async def get_psu_info(self, ipmicmd, psunum):
|
||||
psuinfo = await ipmicmd.raw_command(0x34, 0x6, data=(psunum,))
|
||||
|
||||
@@ -12,6 +12,7 @@
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
import asyncio
|
||||
import ctypes
|
||||
import fcntl
|
||||
from select import select
|
||||
|
||||
@@ -1021,11 +1021,11 @@ class Session(object):
|
||||
# tried ipmi 2.0 against a 1.5 which should work, but some bmcs
|
||||
# thought 'reserved' meant 'must be zero'
|
||||
self.ipmi15only = 1
|
||||
return self._get_channel_auth_cap()
|
||||
return await self._get_channel_auth_cap()
|
||||
mysuffix = " while trying to get channel authentication capabalities"
|
||||
errstr = get_ipmi_error(response, suffix=mysuffix)
|
||||
if errstr:
|
||||
self.onlogon({'error': errstr})
|
||||
await self.onlogon({'error': errstr})
|
||||
return
|
||||
data = response['data']
|
||||
self.currentchannel = data[0]
|
||||
@@ -1033,7 +1033,7 @@ class Session(object):
|
||||
self.ipmiversion = 2.0
|
||||
if self.ipmiversion == 1.5:
|
||||
if not (data[1] & 0b100):
|
||||
self.onlogon(
|
||||
await self.onlogon(
|
||||
{'error':
|
||||
"MD5 required but not enabled/available on target BMC"})
|
||||
return
|
||||
@@ -1041,16 +1041,16 @@ class Session(object):
|
||||
elif self.ipmiversion == 2.0:
|
||||
await self._open_rmcpplus_request()
|
||||
|
||||
def _got_session_challenge(self, response):
|
||||
async def _got_session_challenge(self, response):
|
||||
errstr = get_ipmi_error(response,
|
||||
suffix=" while getting session challenge")
|
||||
if errstr:
|
||||
self.onlogon({'error': errstr})
|
||||
await self.onlogon({'error': errstr})
|
||||
return
|
||||
data = response['data']
|
||||
self.sessionid = struct.unpack("<I", bytes(data[0:4]))[0]
|
||||
self.authtype = 2
|
||||
self._activate_session(data[4:])
|
||||
await self._activate_session(data[4:])
|
||||
|
||||
# NOTE(jbjohnso):
|
||||
# This sends the activate session payload. We pick '1' as the requested
|
||||
@@ -1065,7 +1065,7 @@ class Session(object):
|
||||
async def _activated_session(self, response):
|
||||
errstr = get_ipmi_error(response)
|
||||
if errstr:
|
||||
self.onlogon({'error': errstr})
|
||||
await self.onlogon({'error': errstr})
|
||||
return
|
||||
data = response['data']
|
||||
self.sessionid = struct.unpack("<I", bytes(data[1:5]))[0]
|
||||
@@ -1083,8 +1083,8 @@ class Session(object):
|
||||
# some implementations will let us get this far,
|
||||
# but suddenly get skiddish. Try again in such a case
|
||||
self.privlevel = 3
|
||||
response = self.raw_command(netfn=0x6, command=0x3b,
|
||||
data=[self.privlevel])
|
||||
response = await self.raw_command(netfn=0x6, command=0x3b,
|
||||
data=[self.privlevel])
|
||||
if response['code']:
|
||||
self.logged = 0
|
||||
self.onlogpayload = None
|
||||
@@ -1095,7 +1095,7 @@ class Session(object):
|
||||
self.privlevel, self.userid)
|
||||
errstr = get_ipmi_error(response, suffix=mysuffix)
|
||||
if errstr:
|
||||
self.onlogon({'error': errstr})
|
||||
await self.onlogon({'error': errstr})
|
||||
return
|
||||
self.logging = False
|
||||
self.logoutexpiry = None
|
||||
@@ -1546,7 +1546,7 @@ class Session(object):
|
||||
errstr = constants.rmcp_codes[data[1]]
|
||||
else:
|
||||
errstr = "Unrecognized RMCP code %d" % data[1]
|
||||
self.onlogon({'error': errstr})
|
||||
await self.onlogon({'error': errstr})
|
||||
return -9
|
||||
self.allowedpriv = data[2]
|
||||
# NOTE(jbjohnso): At this point, the BMC has no idea about what user
|
||||
@@ -1690,7 +1690,7 @@ class Session(object):
|
||||
aclen = len(expectedauthcode)
|
||||
authcode = struct.pack("%dB" % aclen, *data[8:aclen + 8])
|
||||
if authcode != expectedauthcode:
|
||||
self.onlogon({'error': "Invalid RAKP4 integrity code (wrong Kg?)"})
|
||||
await self.onlogon({'error': "Invalid RAKP4 integrity code (wrong Kg?)"})
|
||||
return
|
||||
self.sessionid = self.pendingsessionid
|
||||
self.integrityalgo = self.attemptedhash
|
||||
|
||||
@@ -475,7 +475,7 @@ class SDREntry(object):
|
||||
health = const.Health.Ok
|
||||
return desc, health
|
||||
|
||||
def decode_sensor_reading(self, ipmicmd, reading):
|
||||
async def decode_sensor_reading(self, ipmicmd, reading):
|
||||
numeric = None
|
||||
output = {
|
||||
'name': self.sensor_name,
|
||||
@@ -495,8 +495,8 @@ class SDREntry(object):
|
||||
if numeric is not None:
|
||||
lowerbound = numeric - (0.5 + (self.tolerance / 2.0))
|
||||
upperbound = numeric + (0.5 + (self.tolerance / 2.0))
|
||||
lowerbound = self.decode_value(ipmicmd, lowerbound)
|
||||
upperbound = self.decode_value(ipmicmd, upperbound)
|
||||
lowerbound = await self.decode_value(ipmicmd, lowerbound)
|
||||
upperbound = await self.decode_value(ipmicmd, upperbound)
|
||||
output['value'] = (lowerbound + upperbound) / 2.0
|
||||
output['imprecision'] = output['value'] - lowerbound
|
||||
discrete = False
|
||||
@@ -552,13 +552,13 @@ class SDREntry(object):
|
||||
output['state_ids'].append(self.assert_trap_value(6))
|
||||
return SensorReading(output, self.unit_suffix)
|
||||
|
||||
def _set_tmp_formula(self, ipmicmd, value):
|
||||
rsp = ipmicmd.raw_command(netfn=4, command=0x23,
|
||||
data=(self.sensor_number, value))
|
||||
async def _set_tmp_formula(self, ipmicmd, value):
|
||||
rsp = await ipmicmd.raw_command(netfn=4, command=0x23,
|
||||
data=(self.sensor_number, value))
|
||||
# skip next reading field, not used in on-demand situation
|
||||
self.decode_formula(rsp['data'][1:])
|
||||
|
||||
def decode_value(self, ipmicmd, value):
|
||||
async def decode_value(self, ipmicmd, value):
|
||||
# Take the input value and return meaningful value
|
||||
linearization = self.linearization
|
||||
if linearization > 11: # direct calling code to get factors
|
||||
@@ -568,7 +568,7 @@ class SDREntry(object):
|
||||
# fashion. However for now opt for retrieving rows as needed
|
||||
# rather than tracking all that information for a relatively
|
||||
# rare behavior
|
||||
self._set_tmp_formula(ipmicmd, value)
|
||||
await self._set_tmp_formula(ipmicmd, value)
|
||||
linearization = 0
|
||||
# time to compute the pre-linearization value.
|
||||
decoded = float((value * self.m + self.b)
|
||||
|
||||
@@ -1030,7 +1030,7 @@ class Command(object):
|
||||
'Address': static_gateway,
|
||||
}]
|
||||
if patch:
|
||||
nicurl = self._get_bmc_nic_url(name)
|
||||
nicurl = await self._get_bmc_nic_url(name)
|
||||
await self._do_web_request(nicurl, patch, 'PATCH')
|
||||
|
||||
async def set_net_configuration(self, ipv4_address=None, ipv4_configuration=None,
|
||||
@@ -1453,22 +1453,22 @@ class Command(object):
|
||||
del self.wc.stdheaders['Authorization']
|
||||
return
|
||||
for vmurl in vmurls:
|
||||
vminfo = self._do_web_request(vmurl, cache=False)
|
||||
vminfo = await self._do_web_request(vmurl, cache=False)
|
||||
if vminfo.get('ConnectedVia', None) != 'NotConnected':
|
||||
continue
|
||||
inserturl = vminfo.get(
|
||||
'Actions', {}).get(
|
||||
'#VirtualMedia.InsertMedia', {}).get('target', None)
|
||||
if inserturl:
|
||||
self._do_web_request(inserturl, {'Image': url})
|
||||
await self._do_web_request(inserturl, {'Image': url})
|
||||
else:
|
||||
try:
|
||||
self._do_web_request(vmurl,
|
||||
{'Image': url, 'Inserted': True},
|
||||
'PATCH')
|
||||
await self._do_web_request(vmurl,
|
||||
{'Image': url, 'Inserted': True},
|
||||
'PATCH')
|
||||
except exc.RedfishError as re:
|
||||
if re.msgid.endswith(u'PropertyUnknown'):
|
||||
self._do_web_request(vmurl, {'Image': url}, 'PATCH')
|
||||
await self._do_web_request(vmurl, {'Image': url}, 'PATCH')
|
||||
else:
|
||||
raise
|
||||
break
|
||||
@@ -1582,6 +1582,10 @@ class Command(object):
|
||||
return await oem.apply_license(filename, self, progress, data)
|
||||
|
||||
if __name__ == '__main__':
|
||||
print(repr(
|
||||
Command(sys.argv[1], os.environ['BMCUSER'], os.environ['BMCPASS'],
|
||||
verifycallback=lambda x: True).get_power()))
|
||||
async def main():
|
||||
cmd = await Command.create(
|
||||
sys.argv[1], os.environ['BMCUSER'], os.environ['BMCPASS'],
|
||||
verifycallback=lambda x: True)
|
||||
print(repr(await cmd.get_power()))
|
||||
|
||||
asyncio.run(main())
|
||||
|
||||
@@ -17,8 +17,8 @@ import aiohmi.redfish.oem.generic as generic
|
||||
|
||||
class OEMHandler(generic.OEMHandler):
|
||||
|
||||
def set_bootdev(self, bootdev, persist=False, uefiboot=None,
|
||||
fishclient=None):
|
||||
async def set_bootdev(self, bootdev, persist=False, uefiboot=None,
|
||||
fishclient=None):
|
||||
# gleaned from web console, under configuration, system settings,
|
||||
# hardware, first boot device. iDrac presumes that the standard
|
||||
# explicitly refers only to physical devices. I think the intent
|
||||
@@ -26,8 +26,8 @@ class OEMHandler(generic.OEMHandler):
|
||||
# the 'physical' standard to the vFDD/VCD-DVD seen in the idrac
|
||||
# web gui
|
||||
if bootdev not in ('floppy', 'cd'):
|
||||
return super(OEMHandler, self).set_bootdev(bootdev, persist,
|
||||
uefiboot, fishclient)
|
||||
return await super(OEMHandler, self).set_bootdev(bootdev, persist,
|
||||
uefiboot, fishclient)
|
||||
payload = {'Attributes': {}}
|
||||
if persist:
|
||||
payload['Attributes']['ServerBoot.1.BootOnce'] = 'Disabled'
|
||||
@@ -37,7 +37,7 @@ class OEMHandler(generic.OEMHandler):
|
||||
payload['Attributes']['ServerBoot.1.FirstBootDevice'] = 'vFDD'
|
||||
elif bootdev == 'cd':
|
||||
payload['Attributes']['ServerBoot.1.FirstBootDevice'] = 'VCD-DVD'
|
||||
fishclient._do_web_request(
|
||||
await fishclient._do_web_request(
|
||||
'/redfish/v1/Managers/iDRAC.Embedded.1/Attributes',
|
||||
payload, method='PATCH')
|
||||
return {'bootdev': bootdev}
|
||||
|
||||
@@ -659,8 +659,8 @@ class OEMHandler(object):
|
||||
procurl = sysinfo.get('Processors', {}).get('@odata.id',
|
||||
None)
|
||||
if procurl:
|
||||
for cpu in await fishclient._do_web_request(procurl).get(
|
||||
'Members', []):
|
||||
procinfo = await fishclient._do_web_request(procurl)
|
||||
for cpu in procinfo.get('Members', []):
|
||||
cinfo = await fishclient._do_web_request(cpu['@odata.id'])
|
||||
if cinfo.get('Status', {}).get(
|
||||
'State', None) == 'Absent':
|
||||
@@ -1003,8 +1003,9 @@ class OEMHandler(object):
|
||||
def _extract_fwinfo(self, inf):
|
||||
return {}
|
||||
|
||||
def get_firmware_inventory(self, components, fishclient, category=None):
|
||||
return []
|
||||
async def get_firmware_inventory(self, components, fishclient, category=None):
|
||||
return
|
||||
yield
|
||||
|
||||
def set_credentials(self, username, password):
|
||||
try:
|
||||
@@ -1239,7 +1240,7 @@ class OEMHandler(object):
|
||||
if not foundmacs:
|
||||
# No PCIe device inventory, but *maybe* ethernet inventory...
|
||||
idxsbyname = {}
|
||||
for nicinfo in self._get_eth_urls():
|
||||
for nicinfo in await self._get_eth_urls():
|
||||
nicinfo = await self._do_web_request(nicinfo)
|
||||
nicname = nicinfo.get('Name', None)
|
||||
nicinfo = nicinfo.get('MACAddress', nicinfo.get('PermanentAddress', None))
|
||||
@@ -1673,7 +1674,7 @@ class OEMHandler(object):
|
||||
await fishclient._do_web_request(licenses, licinfo)
|
||||
|
||||
|
||||
def get_user_expiration(self, uid):
|
||||
async def get_user_expiration(self, uid):
|
||||
return None
|
||||
|
||||
async def reseat_bay(self, bay):
|
||||
|
||||
@@ -39,7 +39,7 @@ async def get_handler(sysinfo, sysurl, webclient, cache, cmd, rootinfo={}):
|
||||
if not leninf:
|
||||
bmcinfo = await cmd.bmcinfo()
|
||||
if 'Ami' in bmcinfo.get('Oem', {}):
|
||||
return tsma.TsmHandler(sysinfo, sysurl, webclient, cache)
|
||||
return await tsma.TsmHandler.create(sysinfo, sysurl, webclient, cache, gpool=cmd._gpool)
|
||||
elif 'xclarity controller' in mgrinfo.get('Model', '').lower():
|
||||
if mgrinfo['Model'].endswith('3'):
|
||||
return await xcc3.OEMHandler.create(sysinfo, sysurl, webclient, cache,
|
||||
@@ -60,9 +60,9 @@ async def get_handler(sysinfo, sysurl, webclient, cache, cmd, rootinfo={}):
|
||||
devdesc = await webclient.grab_json_response_with_status('/DeviceDescription.json')
|
||||
if devdesc[1] == 200:
|
||||
if devdesc[0]['type'].lower() in ('lenovo-smm3', 'smm3'):
|
||||
return smm3.OEMHandler(sysinfo, sysurl, webclient, cache,
|
||||
gpool=cmd._gpool)
|
||||
return await smm3.OEMHandler.create(sysinfo, sysurl, webclient, cache,
|
||||
gpool=cmd._gpool)
|
||||
except Exception:
|
||||
pass
|
||||
return generic.OEMHandler(sysinfo, sysurl, webclient, cache,
|
||||
gpool=cmd._gpool)
|
||||
return await generic.OEMHandler.create(sysinfo, sysurl, webclient, cache,
|
||||
gpool=cmd._gpool)
|
||||
|
||||
@@ -285,7 +285,8 @@ class OEMHandler(generic.OEMHandler):
|
||||
await self._do_web_request(url, method='POST')
|
||||
|
||||
async def get_event_log(self, clear=False, fishclient=None):
|
||||
return await super().get_event_log(clear, fishclient, extraurls=[{'@odata.id':'/redfish/v1/Chassis/chassis1/LogServices/EventLog'}])
|
||||
async for event in super().get_event_log(clear, fishclient, extraurls=[{'@odata.id':'/redfish/v1/Chassis/chassis1/LogServices/EventLog'}]):
|
||||
yield event
|
||||
|
||||
async def get_description(self, fishclient):
|
||||
return {'height': 13, 'slot': 0, 'slots': [8, 2]}
|
||||
|
||||
@@ -100,8 +100,8 @@ class TsmHandler(generic.OEMHandler):
|
||||
@classmethod
|
||||
async def create(cls, sysinfo, sysurl, webclient, cache=None, fish=None,
|
||||
gpool=None):
|
||||
self = await super(TsmHandler, cls).create(sysinfo, sysurl, webclient, cache, fish,
|
||||
gpool)
|
||||
self = await super(TsmHandler, cls).create(sysinfo, sysurl, webclient, cache,
|
||||
gpool=gpool)
|
||||
if cache is None:
|
||||
cache = {}
|
||||
self._wc = None
|
||||
@@ -185,7 +185,7 @@ class TsmHandler(generic.OEMHandler):
|
||||
if 'dns_domain'.startswith(key.lower()):
|
||||
dnschgs['domain_name'] = currval
|
||||
if 'password_complexity'.startswith(key.lower()):
|
||||
self._set_pass_complexity(currval, wc)
|
||||
await self._set_pass_complexity(currval, wc)
|
||||
if 'password_login_failures'.startswith(key.lower()):
|
||||
await self._set_pass_lockout(currval, wc)
|
||||
if dnschgs:
|
||||
@@ -730,7 +730,7 @@ class TsmHandler(generic.OEMHandler):
|
||||
hddslots += 1
|
||||
else:
|
||||
raise exc.UnsupportedFunctionality('Unknown slot type requested')
|
||||
gensettings = wc.grab_json_response('/api/settings/media/general')
|
||||
gensettings = await wc.grab_json_response('/api/settings/media/general')
|
||||
samesettings = gensettings['same_settings'] == 1
|
||||
if samesettings:
|
||||
hds = gensettings['cd_remote_server_address']
|
||||
@@ -771,10 +771,10 @@ class TsmHandler(generic.OEMHandler):
|
||||
gensettings['remote_media_support'] = 1
|
||||
gensettings['cd_remote_password'] = ''
|
||||
gensettings['hd_remote_password'] = ''
|
||||
wc.grab_json_response_with_status('/api/settings/media/general',
|
||||
await wc.grab_json_response_with_status('/api/settings/media/general',
|
||||
gensettings, method='PUT')
|
||||
# need to calibrate instances correctly
|
||||
currinfo, status = wc.grab_json_response_with_status(
|
||||
currinfo, status = await wc.grab_json_response_with_status(
|
||||
'/api/settings/media/instance')
|
||||
currinfo['num_cd'] = cdslots
|
||||
currinfo['num_hd'] = hddslots
|
||||
@@ -782,9 +782,9 @@ class TsmHandler(generic.OEMHandler):
|
||||
currinfo['kvm_num_cd'] = cdslots
|
||||
if currinfo['kvm_num_hd'] > hddslots:
|
||||
currinfo['kvm_num_hd'] = hddslots
|
||||
wc.grab_json_response_with_status(
|
||||
await wc.grab_json_response_with_status(
|
||||
'/api/settings/media/instance', currinfo, method='PUT')
|
||||
images = wc.grab_json_response('/api/settings/media/remote/images')
|
||||
images = await wc.grab_json_response('/api/settings/media/remote/images')
|
||||
tries = 20
|
||||
while tries and not images:
|
||||
tries -= 1
|
||||
|
||||
@@ -182,7 +182,7 @@ class OEMHandler(generic.OEMHandler):
|
||||
try:
|
||||
self.fwo = await self.fwc.get_fw_options(fetchimm=fetchimm)
|
||||
except config.Unsupported:
|
||||
return super(OEMHandler, self).get_system_configuration(
|
||||
return await super(OEMHandler, self).get_system_configuration(
|
||||
hideadvanced, fishclient)
|
||||
except Exception:
|
||||
raise Exception('%s failed to retrieve UEFI configuration'
|
||||
@@ -827,7 +827,7 @@ class OEMHandler(generic.OEMHandler):
|
||||
wc.set_header('X-XSRF-TOKEN', cookie.value)
|
||||
wc.vintage = util._monotonic_time()
|
||||
|
||||
def _make_available(self, disk, realcfg):
|
||||
async def _make_available(self, disk, realcfg):
|
||||
# 8 if jbod, 4 if hotspare.., leave alone if already...
|
||||
currstatus = self._get_status(disk, realcfg)
|
||||
newstate = None
|
||||
@@ -837,21 +837,21 @@ class OEMHandler(generic.OEMHandler):
|
||||
newstate = 4
|
||||
elif currstatus.lower() == 'jbod':
|
||||
newstate = 8
|
||||
self._set_drive_state(disk, newstate)
|
||||
await self._set_drive_state(disk, newstate)
|
||||
|
||||
def _make_jbod(self, disk, realcfg):
|
||||
async def _make_jbod(self, disk, realcfg):
|
||||
currstatus = self._get_status(disk, realcfg)
|
||||
if currstatus.lower() == 'jbod':
|
||||
return
|
||||
self._make_available(disk, realcfg)
|
||||
self._set_drive_state(disk, 16)
|
||||
await self._make_available(disk, realcfg)
|
||||
await self._set_drive_state(disk, 16)
|
||||
|
||||
def _make_global_hotspare(self, disk, realcfg):
|
||||
async def _make_global_hotspare(self, disk, realcfg):
|
||||
currstatus = self._get_status(disk, realcfg)
|
||||
if currstatus.lower() == 'global hot spare':
|
||||
return
|
||||
self._make_available(disk, realcfg)
|
||||
self._set_drive_state(disk, 1)
|
||||
await self._make_available(disk, realcfg)
|
||||
await self._set_drive_state(disk, 1)
|
||||
|
||||
def _get_status(self, disk, realcfg):
|
||||
for cfgdisk in realcfg.disks:
|
||||
@@ -974,7 +974,7 @@ class OEMHandler(generic.OEMHandler):
|
||||
pass
|
||||
|
||||
async def _create_array(self, pool):
|
||||
params = self._parse_array_spec(pool)
|
||||
params = await self._parse_array_spec(pool)
|
||||
cid = params['controller'].split(',')[0]
|
||||
cslotno = params['controller'].split(',')[1]
|
||||
url = '/api/function/raid_conf?params=raidlink_GetDefaultVolProp'
|
||||
@@ -1438,7 +1438,7 @@ class OEMHandler(generic.OEMHandler):
|
||||
|
||||
async def update_firmware_backend(self, filename, data=None, progress=None,
|
||||
bank=None):
|
||||
self._refresh_token()
|
||||
await self._refresh_token()
|
||||
wc = await self.wc()
|
||||
rsv = await wc.grab_json_response('/api/providers/fwupdate', json.dumps(
|
||||
{'UPD_WebReserve': 1}))
|
||||
@@ -1465,7 +1465,7 @@ class OEMHandler(generic.OEMHandler):
|
||||
raise Exception('File is larger than supported')
|
||||
raise Exception('Unexpected result:' + repr(rsp))
|
||||
uploadstate = rsp['state']
|
||||
self._refresh_token()
|
||||
await self._refresh_token()
|
||||
while uploadstate != 'done':
|
||||
rsp = await wc.grab_json_response(
|
||||
'/upload/progress?X-Progress-ID={0}'.format(xid))
|
||||
@@ -1752,18 +1752,18 @@ class OEMHandler(generic.OEMHandler):
|
||||
else:
|
||||
return days
|
||||
|
||||
def get_inventory_descriptions(self, withids=False):
|
||||
hwmap = self.hardware_inventory_map()
|
||||
async def get_inventory_descriptions(self, withids=False):
|
||||
hwmap = await self.hardware_inventory_map()
|
||||
yield "System"
|
||||
for key in natural_sort(hwmap):
|
||||
yield key
|
||||
for cpuinv in self._get_cpu_inventory():
|
||||
async for cpuinv in self._get_cpu_inventory():
|
||||
yield cpuinv[0]
|
||||
for meminv in self._get_mem_inventory():
|
||||
async for meminv in self._get_mem_inventory():
|
||||
yield meminv[0]
|
||||
|
||||
|
||||
def get_inventory_of_component(self, compname):
|
||||
async def get_inventory_of_component(self, compname):
|
||||
if compname.lower() == 'system':
|
||||
sysinfo = {
|
||||
'UUID': self._varsysinfo.get('UUID', ''),
|
||||
@@ -1774,14 +1774,14 @@ class OEMHandler(generic.OEMHandler):
|
||||
'SKU', self._varsysinfo.get('PartNumber', '')),
|
||||
}
|
||||
return sysinfo
|
||||
hwmap = self.hardware_inventory_map()
|
||||
hwmap = await self.hardware_inventory_map()
|
||||
try:
|
||||
return hwmap[compname]
|
||||
except KeyError:
|
||||
for cpuinv in self._get_cpu_inventory():
|
||||
async for cpuinv in self._get_cpu_inventory():
|
||||
if cpuinv[0] == compname:
|
||||
return cpuinv[1]
|
||||
for meminv in self._get_mem_inventory():
|
||||
async for meminv in self._get_mem_inventory():
|
||||
if meminv[0] == compname:
|
||||
return meminv[1]
|
||||
|
||||
|
||||
@@ -431,7 +431,7 @@ class OEMHandler(generic.OEMHandler):
|
||||
await self._make_available(disk, realcfg)
|
||||
self._urlcache.clear()
|
||||
|
||||
def _parse_array_spec(self, arrayspec):
|
||||
async def _parse_array_spec(self, arrayspec):
|
||||
controller = None
|
||||
if arrayspec.disks:
|
||||
for disk in list(arrayspec.disks) + list(arrayspec.hotspares):
|
||||
@@ -440,7 +440,7 @@ class OEMHandler(generic.OEMHandler):
|
||||
if controller != disk.id[0]:
|
||||
raise pygexc.UnsupportedFunctionality(
|
||||
'Cannot span arrays across controllers')
|
||||
raidmap = self._raid_number_map(controller)
|
||||
raidmap = await self._raid_number_map(controller)
|
||||
if not raidmap:
|
||||
raise pygexc.InvalidParameterValue(
|
||||
'No RAID Type supported on this controller')
|
||||
@@ -510,7 +510,7 @@ class OEMHandler(generic.OEMHandler):
|
||||
return themap
|
||||
|
||||
async def _create_array(self, pool):
|
||||
params = self._parse_array_spec(pool)
|
||||
params = await self._parse_array_spec(pool)
|
||||
cid = params['controller'].split(',')[0]
|
||||
c_capabilities, code = await self.webclient.grab_json_response_with_status(
|
||||
f'/redfish/v1/Systems/1/Storage/{cid}/Volumes/Capabilities')
|
||||
@@ -695,14 +695,14 @@ class OEMHandler(generic.OEMHandler):
|
||||
if write_policy:
|
||||
request_data["WriteCachePolicy"] = write_policy
|
||||
|
||||
msg, code=self.webclient.grab_json_response_with_status(
|
||||
msg, code = await self.webclient.grab_json_response_with_status(
|
||||
f'/redfish/v1/Systems/1/Storage/{cid}/Volumes',
|
||||
method='POST',
|
||||
data=request_data)
|
||||
if code == 500 and not stripsize:
|
||||
# Mystery error can be a mandatory strip size, default to 64k to match WebUI behavior
|
||||
request_data["StripSizeBytes"] = 65536
|
||||
msg, code=self.webclient.grab_json_response_with_status(
|
||||
msg, code = await self.webclient.grab_json_response_with_status(
|
||||
f'/redfish/v1/Systems/1/Storage/{cid}/Volumes',
|
||||
method='POST',
|
||||
data=request_data)
|
||||
@@ -903,7 +903,7 @@ class OEMHandler(generic.OEMHandler):
|
||||
cache=False)
|
||||
rawsettings = rawsettings.get('Attributes', {})
|
||||
pendingsettings = {}
|
||||
ret = self._set_redfish_settings(
|
||||
ret = await self._set_redfish_settings(
|
||||
changeset, fishclient, currsettings, rawsettings,
|
||||
pendingsettings, self.lenovobmcattrdeps, reginfo,
|
||||
'/redfish/v1/Managers/1/Oem/Lenovo/BMCSettings')
|
||||
@@ -1036,7 +1036,7 @@ class OEMHandler(generic.OEMHandler):
|
||||
if usbsettings:
|
||||
await self.apply_usb_configuration(usbsettings)
|
||||
if bmchangeset:
|
||||
self._set_xcc3_settings(bmchangeset, self)
|
||||
await self._set_xcc3_settings(bmchangeset, self)
|
||||
|
||||
async def apply_usb_configuration(self, usbsettings):
|
||||
bmcattribs = {}
|
||||
@@ -1216,5 +1216,3 @@ class OEMHandler(generic.OEMHandler):
|
||||
'Name': 'HPM-FPGA Pending',
|
||||
'build': pendinghpm}
|
||||
raise pygexc.BypassGenericBehavior()
|
||||
|
||||
|
||||
|
||||
@@ -27,23 +27,23 @@ OEMMAP = {
|
||||
}
|
||||
|
||||
|
||||
def get_oem_handler(sysinfo, sysurl, webclient, cache, cmd, rootinfo={}):
|
||||
async def get_oem_handler(sysinfo, sysurl, webclient, cache, cmd, rootinfo={}):
|
||||
if rootinfo.get('Vendor', None) in OEMMAP:
|
||||
return OEMMAP[rootinfo['Vendor']].get_handler(sysinfo, sysurl,
|
||||
webclient, cache, cmd, rootinfo)
|
||||
return await OEMMAP[rootinfo['Vendor']].get_handler(sysinfo, sysurl,
|
||||
webclient, cache, cmd, rootinfo)
|
||||
for oem in sysinfo.get('Oem', {}):
|
||||
if oem in OEMMAP:
|
||||
return OEMMAP[oem].get_handler(sysinfo, sysurl, webclient, cache,
|
||||
cmd, rootinfo)
|
||||
return await OEMMAP[oem].get_handler(sysinfo, sysurl, webclient, cache,
|
||||
cmd, rootinfo)
|
||||
for oem in sysinfo.get('Links', {}).get('OEM', []):
|
||||
if oem in OEMMAP:
|
||||
return OEMMAP[oem].get_handler(sysinfo, sysurl, webclient, cache,
|
||||
cmd, rootinfo)
|
||||
return await OEMMAP[oem].get_handler(sysinfo, sysurl, webclient, cache,
|
||||
cmd, rootinfo)
|
||||
if rootinfo: # rootinfo indicates early invocation, bmcinfo not ready yet
|
||||
return generic.OEMHandler(sysinfo, sysurl, webclient, cache, cmd._gpool, rootinfo)
|
||||
bmcinfo = cmd.bmcinfo
|
||||
return await generic.OEMHandler.create(sysinfo, sysurl, webclient, cache, cmd._gpool, rootinfo)
|
||||
bmcinfo = await cmd.bmcinfo()
|
||||
for oem in bmcinfo.get('Oem', {}):
|
||||
if oem in OEMMAP:
|
||||
return OEMMAP[oem].get_handler(sysinfo, sysurl, webclient, cache,
|
||||
cmd, rootinfo)
|
||||
return generic.OEMHandler(sysinfo, sysurl, webclient, cache, cmd._gpool, rootinfo)
|
||||
return await OEMMAP[oem].get_handler(sysinfo, sysurl, webclient, cache,
|
||||
cmd, rootinfo)
|
||||
return await generic.OEMHandler.create(sysinfo, sysurl, webclient, cache, cmd._gpool, rootinfo)
|
||||
|
||||
@@ -55,8 +55,8 @@ class AsyncTermRelation(object):
|
||||
self.asynchdl = asynchdl
|
||||
self.termid = termid
|
||||
|
||||
def got_data(self, data):
|
||||
self.asynchdl.add(self.termid, data)
|
||||
async def got_data(self, data):
|
||||
await self.asynchdl.add(self.termid, data)
|
||||
|
||||
|
||||
class AsyncSession(object):
|
||||
|
||||
@@ -184,7 +184,7 @@ async def connect_to_leader(cert=None, name=None, leader=None, remote=None, isre
|
||||
await cfm.ConfigManager(tenant=None)._load_from_json(dbjson,
|
||||
sync=False)
|
||||
cfm.commit_clear()
|
||||
except Exception:
|
||||
except Exception as e:
|
||||
print(repr(e))
|
||||
await cfm.stop_following()
|
||||
cfm.rollback_clear()
|
||||
@@ -653,8 +653,8 @@ async def handle_connection(connection, cert, request, local=False):
|
||||
retrythread = None
|
||||
async with leader_init:
|
||||
cnn = tlvdata.get_socket(connection)
|
||||
cfm.update_collective_address(request['name'],
|
||||
cnn.getpeername()[0])
|
||||
await cfm.update_collective_address(request['name'],
|
||||
cnn.getpeername()[0])
|
||||
await tlvdata.send(connection, cfm._dump_keys(None, False))
|
||||
await tlvdata.send(connection, cfm._cfgstore['collective'])
|
||||
await tlvdata.send(connection, {'confluent_uuid': cfm.get_global('confluent_uuid')}) # cfm.get_globals())
|
||||
@@ -892,7 +892,7 @@ async def check_managers():
|
||||
targets = sorted(expandednoderanges[targets], key=availmanagers.get)
|
||||
if not targets:
|
||||
continue
|
||||
c.set_node_attributes({node: {'collective.manager': {'value': targets[0]}}})
|
||||
await c.set_node_attributes({node: {'collective.manager': {'value': targets[0]}}})
|
||||
availmanagers[targets[0]] += 1
|
||||
await _assimilate_missing()
|
||||
failovercheck = None
|
||||
|
||||
@@ -589,6 +589,16 @@ node = {
|
||||
'To support this scenario, the switch should be set up to allow independent operation of member ports (e.g. lacp bypass mode or fallback mode).',
|
||||
'validvalues': ('lacp', 'loadbalance', 'roundrobin', 'activebackup', 'none')
|
||||
},
|
||||
'net.extra_settings': {
|
||||
'description': 'Additional network settings to apply to the connection, as '
|
||||
'semicolon-delimited key=value pairs (e.g. '
|
||||
'"connection.zone=internal;ipv4.routes=10.0.0.0/8 192.168.1.254"). '
|
||||
'The keys are passed through to the network configuration backend of '
|
||||
'the deployed OS and use its native syntax: nmcli property names on '
|
||||
'NetworkManager based systems, netplan YAML keys (values in YAML flow '
|
||||
'syntax, nested keys dotted) on netplan based systems, or ifcfg '
|
||||
'variables on wicked based systems.',
|
||||
},
|
||||
'power.pdu': {
|
||||
'description': 'Specifies the managed PDU associated with a power input on the node'
|
||||
},
|
||||
|
||||
@@ -743,7 +743,7 @@ async def relay_slaved_requests(name, listener):
|
||||
_newquorum = has_quorum()
|
||||
_hasquorum = _newquorum
|
||||
if _hasquorum and _pending_collective_updates:
|
||||
apply_pending_collective_updates()
|
||||
await apply_pending_collective_updates()
|
||||
msg = await lh.get_next_msg()
|
||||
while msg:
|
||||
if name not in cfgstreams:
|
||||
@@ -1050,23 +1050,23 @@ def _true_del_collective_member(name, sync=True):
|
||||
_pending_collective_updates = {}
|
||||
|
||||
|
||||
def update_collective_address(name ,address):
|
||||
async def update_collective_address(name, address):
|
||||
fprint = _cfgstore['collective'][name]['fingerprint']
|
||||
oldaddress = _cfgstore['collective'][name]['address']
|
||||
if oldaddress == address:
|
||||
return
|
||||
try:
|
||||
check_quorum()
|
||||
add_collective_member(name, address, fprint)
|
||||
await add_collective_member(name, address, fprint)
|
||||
except exc.DegradedCollective:
|
||||
_pending_collective_updates[name] = address
|
||||
|
||||
|
||||
def apply_pending_collective_updates():
|
||||
async def apply_pending_collective_updates():
|
||||
for name in list(_pending_collective_updates):
|
||||
fprint = _cfgstore['collective'][name]['fingerprint']
|
||||
address = _pending_collective_updates[name]
|
||||
add_collective_member(name, address, fprint)
|
||||
await add_collective_member(name, address, fprint)
|
||||
del _pending_collective_updates[name]
|
||||
|
||||
|
||||
@@ -3232,7 +3232,7 @@ async def dump_db_to_directory(location, password, redact=None, skipkeys=False,
|
||||
try:
|
||||
for tenant in os.listdir(
|
||||
os.path.join(ConfigManager._cfgdir, '/tenants/')):
|
||||
tenant_data = ConfigManager(tenant=tenant)._dump_to_json(redact=redact)
|
||||
tenant_data = await ConfigManager(tenant=tenant)._dump_to_json(redact=redact)
|
||||
with open(os.path.join(location, 'tenants', tenant, f'main.{format}'), 'wb' if format == 'json' else 'w') as cfgfile:
|
||||
if format == 'json':
|
||||
cfgfile.write(tenant_data)
|
||||
|
||||
@@ -225,13 +225,13 @@ class ConsoleHandler(object):
|
||||
'value', None)
|
||||
if list(configmodule.list_collective()) and not myc:
|
||||
self._is_local = False
|
||||
self._detach()
|
||||
await self._detach()
|
||||
await self._disconnect()
|
||||
if myc and myc != collective.get_myname():
|
||||
# Do not do console connect for nodes managed by another
|
||||
# confluent collective member
|
||||
self._is_local = False
|
||||
self._detach()
|
||||
await self._detach()
|
||||
await self._disconnect()
|
||||
else:
|
||||
self._is_local = True
|
||||
@@ -301,9 +301,9 @@ class ConsoleHandler(object):
|
||||
'or the console simply not having any output since last connection]')
|
||||
self.clearpending = True
|
||||
|
||||
def _detach(self):
|
||||
async def _detach(self):
|
||||
for ses in list(self.livesessions):
|
||||
ses.detach()
|
||||
await ses.detach()
|
||||
|
||||
async def _disconnect(self):
|
||||
if self.connectionthread:
|
||||
@@ -451,8 +451,7 @@ class ConsoleHandler(object):
|
||||
await self._send_rcpts({'deleting': True})
|
||||
await self._disconnect()
|
||||
if self._console:
|
||||
|
||||
self._console.close()
|
||||
await self._console.close()
|
||||
self._console = None
|
||||
if self.connectionthread:
|
||||
self.connectionthread.cancel()
|
||||
@@ -670,7 +669,7 @@ class ProxyConsole(object):
|
||||
|
||||
def _attribschanged(self, nodeattribs, configmanager, **kwargs):
|
||||
if self.clisession:
|
||||
self.clisession.detach()
|
||||
tasks.spawn(self.clisession.detach())
|
||||
self.clisession = None
|
||||
|
||||
async def relay_data(self):
|
||||
@@ -743,15 +742,15 @@ class ProxyConsole(object):
|
||||
pass
|
||||
self.clisession = None
|
||||
|
||||
def send_break(self):
|
||||
tlvdata.send(self.remote, {'operation': 'break'})
|
||||
async def send_break(self):
|
||||
await tlvdata.send(self.remote, {'operation': 'break'})
|
||||
|
||||
def reopen(self):
|
||||
tlvdata.send(self.remote, {'operation': 'reopen'})
|
||||
async def reopen(self):
|
||||
await tlvdata.send(self.remote, {'operation': 'reopen'})
|
||||
|
||||
def resize(self, width, height):
|
||||
tlvdata.send(self.remote, {'operation': 'resize', 'width': width,
|
||||
'height': height})
|
||||
tasks.spawn(tlvdata.send(self.remote, {'operation': 'resize', 'width': width,
|
||||
'height': height}))
|
||||
|
||||
|
||||
# this represents some api view of a console handler. This handles things like
|
||||
@@ -825,10 +824,10 @@ class ConsoleSession(object):
|
||||
self.conshdl = await connect_node(self.node, self.configmanager,
|
||||
self.username, self.direct, self.width,
|
||||
self.height)
|
||||
def send_break(self):
|
||||
async def send_break(self):
|
||||
"""Send break to remote system
|
||||
"""
|
||||
self.conshdl.send_break()
|
||||
await self.conshdl.send_break()
|
||||
|
||||
def resize(self, width, height):
|
||||
self.conshdl.resize(width, height)
|
||||
@@ -871,7 +870,7 @@ class ConsoleSession(object):
|
||||
await self.conshdl.attachsession(self)
|
||||
self.write = self.conshdl.write
|
||||
|
||||
def got_data(self, data):
|
||||
async def got_data(self, data):
|
||||
"""Receive data from console and buffer
|
||||
|
||||
If the caller does not provide a callback and instead will be polling
|
||||
|
||||
@@ -102,7 +102,7 @@ class CredServer(object):
|
||||
now = datetime.datetime.utcnow()
|
||||
expiry = datetime.datetime.strptime(apiarmed, "%Y-%m-%dT%H:%M:%SZ")
|
||||
if now > expiry:
|
||||
self.cfm.set_node_attributes({nodename: {'deployment.apiarmed': ''}})
|
||||
await self.cfm.set_node_attributes({nodename: {'deployment.apiarmed': ''}})
|
||||
client.close()
|
||||
return
|
||||
await cloop.sock_sendall(client, b'\x02\x20')
|
||||
@@ -132,7 +132,7 @@ class CredServer(object):
|
||||
await cloop.sock_recv(client, 2) # drain end of message
|
||||
await cloop.sock_sendall(client, b'\x05\x00') # report success
|
||||
if hmackey and apiarmed != 'continuous':
|
||||
self.cfm.clear_node_attributes([nodename], ['secret.selfapiarmtoken'])
|
||||
await self.cfm.clear_node_attributes([nodename], ['secret.selfapiarmtoken'])
|
||||
if apiarmed != 'continuous':
|
||||
disarm = {nodename: {'deployment.sealedapikey': '', 'deployment.apiarmed': ''}}
|
||||
finally:
|
||||
|
||||
@@ -580,14 +580,14 @@ async def handle_api_request(configmanager, inputdata, operation, pathcomponents
|
||||
return (msg.KeyValueData({'rescan': 'started'}),)
|
||||
elif operation in ('update', 'create') and pathcomponents[:2] == ['discovery', 'subscriptions']:
|
||||
target = pathcomponents[2]
|
||||
affluent.subscribe_discovery(target, configmanager, collective.get_myname())
|
||||
await affluent.subscribe_discovery(target, configmanager, collective.get_myname())
|
||||
currsubs = get_subscriptions()
|
||||
currsubs[target] = {}
|
||||
save_subscriptions(currsubs)
|
||||
return (msg.KeyValueData({'status': 'subscribed'}),)
|
||||
elif operation == 'delete' and pathcomponents[:2] == ['discovery', 'subscriptions']:
|
||||
target = pathcomponents[2]
|
||||
affluent.unsubscribe_discovery(target, configmanager, collective.get_myname())
|
||||
await affluent.unsubscribe_discovery(target, configmanager, collective.get_myname())
|
||||
currsubs = get_subscriptions()
|
||||
if target in currsubs:
|
||||
del currsubs[target]
|
||||
@@ -597,7 +597,7 @@ async def handle_api_request(configmanager, inputdata, operation, pathcomponents
|
||||
if pathcomponents == ['discovery', 'register']:
|
||||
if 'addresses' not in inputdata:
|
||||
raise exc.InvalidArgumentException('Missing address in input')
|
||||
return await register_remote_addrs(inputdata['addresses'], configmanager)
|
||||
return register_remote_addrs(inputdata['addresses'], configmanager)
|
||||
if 'node' not in inputdata:
|
||||
raise exc.InvalidArgumentException('Missing node name in input')
|
||||
mac = _get_mac_from_query(pathcomponents)
|
||||
@@ -920,7 +920,7 @@ async def detected(info):
|
||||
else:
|
||||
policies = set([])
|
||||
if policies & {'open', 'permissive'}:
|
||||
cfg.set_node_attributes({nodename: {'id.uuid': info['uuid']}})
|
||||
await cfg.set_node_attributes({nodename: {'id.uuid': info['uuid']}})
|
||||
return # already known, no need for more
|
||||
#TODO(jjohnson2): We might have to get UUID for certain searches...
|
||||
#for now defer probe until inside eval_node. We might not have
|
||||
@@ -975,7 +975,7 @@ async def get_chained_smm_name(nodename, cfg, handler, nl=None, checkswitch=True
|
||||
mycert = await handler.get_https_cert()
|
||||
if checkswitch:
|
||||
fprints = macmap.get_node_fingerprints(nodename, cfg)
|
||||
for fprint in fprints:
|
||||
async for fprint in fprints:
|
||||
if util.cert_matches(fprint[0], mycert):
|
||||
# ok we have a direct match, it is this node
|
||||
return nodename, fprint[1]
|
||||
@@ -1380,7 +1380,7 @@ async def eval_node(cfg, handler, info, nodename, manual=False):
|
||||
# The candidate nodename is the head of a chain, we must
|
||||
# validate the smm certificate by the switch
|
||||
fprints = macmap.get_node_fingerprints(nodename, cfg)
|
||||
for fprint in fprints:
|
||||
async for fprint in fprints:
|
||||
if util.cert_matches(fprint[0], await handler.get_https_cert()):
|
||||
if not await discover_node(cfg, handler, info,
|
||||
nodename, manual):
|
||||
@@ -1434,7 +1434,7 @@ async def discover_node(cfg, handler, info, nodename, manual):
|
||||
# in some product or another.
|
||||
curruuid = info.get('uuid', False)
|
||||
if 'pxe' in policies and info['handler'] == pxeh:
|
||||
return do_pxe_discovery(cfg, handler, info, manual, nodename, policies)
|
||||
return await do_pxe_discovery(cfg, handler, info, manual, nodename, policies)
|
||||
elif ('permissive' in policies and handler.https_supported and lastfp and
|
||||
not util.cert_matches(lastfp, await handler.get_https_cert()) and not manual):
|
||||
info['discofailure'] = 'fingerprint'
|
||||
@@ -1455,7 +1455,7 @@ async def discover_node(cfg, handler, info, nodename, manual):
|
||||
return False
|
||||
info['nodename'] = nodename
|
||||
if info['handler'] == pxeh:
|
||||
return do_pxe_discovery(cfg, handler, info, manual, nodename, policies)
|
||||
return await do_pxe_discovery(cfg, handler, info, manual, nodename, policies)
|
||||
elif manual or not util.cert_matches(lastfp, await handler.get_https_cert()):
|
||||
# only 'discover' if it is not the same as last time
|
||||
try:
|
||||
@@ -1554,7 +1554,7 @@ async def wait_for_connection(bmcaddr):
|
||||
continue
|
||||
await asyncio.sleep(1)
|
||||
|
||||
def do_pxe_discovery(cfg, handler, info, manual, nodename, policies):
|
||||
async def do_pxe_discovery(cfg, handler, info, manual, nodename, policies):
|
||||
# use uuid based scheme in lieu of tls cert, ideally only
|
||||
# for stateless 'discovery' targets like pxe, where data does not
|
||||
# change
|
||||
@@ -1585,7 +1585,7 @@ def do_pxe_discovery(cfg, handler, info, manual, nodename, policies):
|
||||
for checkattr in attribs:
|
||||
checkval = currattrs.get(nodename, {}).get(checkattr, {}).get('value', None)
|
||||
if checkval != attribs[checkattr]:
|
||||
cfg.set_node_attributes({nodename: attribs})
|
||||
await cfg.set_node_attributes({nodename: attribs})
|
||||
break
|
||||
if info['uuid'] in known_pxe_uuids:
|
||||
return True
|
||||
|
||||
@@ -48,8 +48,8 @@ class NodeHandler(generic.NodeHandler):
|
||||
return
|
||||
# TODO(jjohnson2): probe serial number and uuid
|
||||
|
||||
def config(self, nodename, reset=False):
|
||||
self._bmcconfig(nodename, reset)
|
||||
async def config(self, nodename, reset=False):
|
||||
await self._bmcconfig(nodename, reset)
|
||||
|
||||
async def _bmcconfig(self, nodename, reset=False, customconfig=None, vc=None):
|
||||
# TODO(jjohnson2): set ip parameters, user/pass, alert cfg maybe
|
||||
@@ -108,7 +108,7 @@ class NodeHandler(generic.NodeHandler):
|
||||
# Use existing account that has been created
|
||||
newuserslot = uid
|
||||
if newpass != passwd: # don't mess with existing if no change
|
||||
ic.set_user_password(newuserslot, password=newpass)
|
||||
await ic.set_user_password(newuserslot, password=newpass)
|
||||
ic = await self._get_ipmicmd(user, passwd)
|
||||
if vc:
|
||||
ic.register_key_handler(vc)
|
||||
@@ -118,8 +118,8 @@ class NodeHandler(generic.NodeHandler):
|
||||
if newuserslot < 2:
|
||||
newuserslot = 2
|
||||
if newpass != passwd: # don't mess with existing if no change
|
||||
ic.set_user_password(newuserslot, password=newpass)
|
||||
ic.set_user_name(newuserslot, newuser)
|
||||
await ic.set_user_password(newuserslot, password=newpass)
|
||||
await ic.set_user_name(newuserslot, newuser)
|
||||
if havecustomcreds:
|
||||
ic = await self._get_ipmicmd(user, passwd)
|
||||
if vc:
|
||||
@@ -132,17 +132,17 @@ class NodeHandler(generic.NodeHandler):
|
||||
for uid in currusers:
|
||||
if uid != newuserslot:
|
||||
if uid <= lockedusers: # we cannot delete, settle for disable
|
||||
ic.disable_user(uid, 'disable')
|
||||
await ic.disable_user(uid, 'disable')
|
||||
else:
|
||||
# lead with the most critical thing, removing user access
|
||||
ic.set_user_access(uid, channel=None, callback=False,
|
||||
link_auth=False, ipmi_msg=False,
|
||||
privilege_level='no_access')
|
||||
await ic.set_user_access(uid, channel=None, callback=False,
|
||||
link_auth=False, ipmi_msg=False,
|
||||
privilege_level='no_access')
|
||||
# next, try to disable the password
|
||||
ic.set_user_password(uid, mode='disable', password=None)
|
||||
await ic.set_user_password(uid, mode='disable', password=None)
|
||||
# ok, now we can be less paranoid
|
||||
try:
|
||||
ic.user_delete(uid)
|
||||
await ic.user_delete(uid)
|
||||
except pygexc.IpmiException as ie:
|
||||
if ie.ipmicode != 0xd5: # some response to the 0xff
|
||||
# name...
|
||||
@@ -165,20 +165,20 @@ class NodeHandler(generic.NodeHandler):
|
||||
netconfig = await netutil.get_nic_config(cfg, nodename, ip=newip)
|
||||
plen = netconfig['prefix']
|
||||
newip = '{0}/{1}'.format(newip, plen)
|
||||
currcfg = ic.get_net_configuration()
|
||||
currcfg = await ic.get_net_configuration()
|
||||
if currcfg['ipv4_address'] != newip:
|
||||
# do not change the ipv4_config if the current config looks
|
||||
# like it is already accurate
|
||||
ic.set_net_configuration(ipv4_address=newip,
|
||||
ipv4_configuration='static',
|
||||
ipv4_gateway=netconfig[
|
||||
'ipv4_gateway'])
|
||||
await ic.set_net_configuration(ipv4_address=newip,
|
||||
ipv4_configuration='static',
|
||||
ipv4_gateway=netconfig[
|
||||
'ipv4_gateway'])
|
||||
elif self.ipaddr.startswith('fe80::'):
|
||||
cfg.set_node_attributes(
|
||||
await cfg.set_node_attributes(
|
||||
{nodename: {'hardwaremanagement.manager': self.ipaddr}})
|
||||
else:
|
||||
raise exc.TargetEndpointUnreachable(
|
||||
'hardwaremanagement.manager must be set to desired address')
|
||||
if reset:
|
||||
ic.reset_bmc()
|
||||
await ic.reset_bmc()
|
||||
return ic
|
||||
|
||||
@@ -89,13 +89,13 @@ class NodeHandler(bmchandler.NodeHandler):
|
||||
guiddata = await ipmicmd.xraw_command(netfn=6, command=8)
|
||||
self.info['uuid'] = pygutil.decode_wireformat_uuid(
|
||||
guiddata['data']).lower()
|
||||
ipmicmd.oem_init()
|
||||
bayid = ipmicmd._oem.immhandler.get_property(
|
||||
await ipmicmd.oem_init()
|
||||
bayid = await ipmicmd._oem.immhandler.get_property(
|
||||
'/v2/cmm/sp/7')
|
||||
if not bayid:
|
||||
return
|
||||
self.info['enclosure.bay'] = int(bayid)
|
||||
smmid = ipmicmd._oem.immhandler.get_property(
|
||||
smmid = await ipmicmd._oem.immhandler.get_property(
|
||||
'/v2/ibmc/smm/chassis/uuid')
|
||||
if not smmid:
|
||||
return
|
||||
@@ -108,4 +108,3 @@ class NodeHandler(bmchandler.NodeHandler):
|
||||
except pygexc.IpmiException as ie:
|
||||
print(repr(ie))
|
||||
raise
|
||||
|
||||
|
||||
@@ -263,8 +263,9 @@ class NodeHandler(generic.NodeHandler):
|
||||
newip = newipinfo[-1][0]
|
||||
if ':' in newip:
|
||||
raise exc.NotImplementedException('IPv6 remote config TODO')
|
||||
hifurls = await get_host_interface_urls(wc, self.mgrinfo(wc))
|
||||
mgtnicinfo = self.mgrinfo(wc)['EthernetInterfaces']['@odata.id']
|
||||
mgrinfo = await self.mgrinfo(wc)
|
||||
hifurls = await get_host_interface_urls(wc, mgrinfo)
|
||||
mgtnicinfo = mgrinfo['EthernetInterfaces']['@odata.id']
|
||||
mgtnicinfo = await wc.grab_json_response(mgtnicinfo)
|
||||
mgtnics = [x['@odata.id'] for x in mgtnicinfo.get('Members', [])]
|
||||
actualnics = []
|
||||
|
||||
@@ -99,7 +99,7 @@ class NodeHandler(bmchandler.NodeHandler):
|
||||
if smmip:
|
||||
smmip = smmip.split('/', 1)[0]
|
||||
if smmip and ':' not in smmip:
|
||||
smmip = await asyncio.get_running_loop().getaddrinfo(smmip, 0)[0]
|
||||
smmip = (await asyncio.get_running_loop().getaddrinfo(smmip, 0))[0]
|
||||
smmip = smmip[-1][0]
|
||||
if smmip and ':' in smmip:
|
||||
raise exc.NotImplementedException('IPv6 not supported')
|
||||
@@ -125,7 +125,7 @@ class NodeHandler(bmchandler.NodeHandler):
|
||||
if smmip and ':' in smmip and not smmip.startswith('fe80::'):
|
||||
raise exc.NotImplementedException('IPv6 configuration TODO')
|
||||
if self.ipaddr.startswith('fe80::'):
|
||||
cfg.set_node_attributes(
|
||||
await cfg.set_node_attributes(
|
||||
{nodename: {'hardwaremanagement.manager': self.ipaddr}})
|
||||
|
||||
def _webconfigcreds(self, username, password):
|
||||
|
||||
@@ -45,7 +45,7 @@ async def remote_nodecfg(nodename, cfm):
|
||||
ipaddr = cfg.get(nodename, {}).get('hardwaremanagement.manager', {}).get(
|
||||
'value', None)
|
||||
ipaddr = ipaddr.split('/', 1)[0]
|
||||
ipaddr = await asyncio.get_running_loop().getaddrinfo(ipaddr, 0)[0][-1]
|
||||
ipaddr = (await asyncio.get_running_loop().getaddrinfo(ipaddr, 0))[0][-1]
|
||||
if not ipaddr:
|
||||
raise Exception('Cannot remote configure a system without known '
|
||||
'address')
|
||||
@@ -62,4 +62,3 @@ if __name__ == '__main__':
|
||||
print(repr(info))
|
||||
testr = NodeHandler(info, c)
|
||||
asyncio.run(testr.config(sys.argv[2]))
|
||||
|
||||
|
||||
@@ -225,7 +225,7 @@ class NodeHandler(generic.NodeHandler):
|
||||
rsp, status = await wc.grab_json_response_with_status('/api/session', method='DELETE')
|
||||
|
||||
|
||||
def remote_nodecfg(nodename, cfm):
|
||||
async def remote_nodecfg(nodename, cfm):
|
||||
cfg = cfm.get_node_attributes(
|
||||
nodename, 'hardwaremanagement.manager')
|
||||
ipaddr = cfg.get(nodename, {}).get('hardwaremanagement.manager', {}).get(
|
||||
@@ -237,7 +237,7 @@ def remote_nodecfg(nodename, cfm):
|
||||
'address')
|
||||
info = {'addresses': [ipaddr]}
|
||||
nh = NodeHandler(info, cfm)
|
||||
nh.config(nodename)
|
||||
await nh.config(nodename)
|
||||
|
||||
if __name__ == '__main__':
|
||||
import confluent.config.configmanager as cfm
|
||||
|
||||
@@ -170,8 +170,8 @@ class NodeHandler(immhandler.NodeHandler):
|
||||
{'IPMI': {'ProtocolEnabled': True}}, method='PATCH')
|
||||
ipmicmd = None
|
||||
try:
|
||||
ipmicmd = self._get_ipmicmd(self._currcreds[0], self._currcreds[1])
|
||||
ipmicmd.xraw_command(netfn=0x3a, command=0xf1, data=(1,))
|
||||
ipmicmd = await self._get_ipmicmd(self._currcreds[0], self._currcreds[1])
|
||||
await ipmicmd.xraw_command(netfn=0x3a, command=0xf1, data=(1,))
|
||||
except pygexc.IpmiException as e:
|
||||
if (e.ipmicode != 193 and 'Unauthorized name' not in str(e) and
|
||||
'Incorrect password' not in str(e) and
|
||||
@@ -709,4 +709,3 @@ async def remote_nodecfg(nodename, cfm):
|
||||
else:
|
||||
nh = xcc3handler.NodeHandler(info, cfm)
|
||||
await nh.config(nodename)
|
||||
|
||||
|
||||
@@ -331,7 +331,7 @@ async def check_fish(urldata, port=443, verifycallback=None):
|
||||
except (IndexError, KeyError):
|
||||
return None
|
||||
url = '/redfish/v1/'
|
||||
peerinfo = wc.grab_json_response('/redfish/v1/')
|
||||
peerinfo = await wc.grab_json_response('/redfish/v1/')
|
||||
if url == '/redfish/v1/':
|
||||
if 'UUID' in peerinfo:
|
||||
if 'services' not in data:
|
||||
|
||||
@@ -298,7 +298,7 @@ async def snoop(handler, byehandler=None, protocol=None, uuidlookup=None):
|
||||
if await netutil.ip_on_same_subnet(theip, 'fe80::', 64):
|
||||
if '%' in peer[0]:
|
||||
ifidx = peer[0].split('%', 1)[1]
|
||||
iface = await cloop.getaddrinfo(peer[0], 0, socket.AF_INET6, socket.SOCK_DGRAM)[0][-1][-1]
|
||||
iface = (await cloop.getaddrinfo(peer[0], 0, socket.AF_INET6, socket.SOCK_DGRAM))[0][-1][-1]
|
||||
else:
|
||||
ifidx = '{}'.format(peer[-1])
|
||||
iface = peer[-1]
|
||||
@@ -503,7 +503,7 @@ async def check_fish(urldata, port=443, verifycallback=None):
|
||||
if url == '/DeviceDescription.json':
|
||||
if not peerinfo:
|
||||
if data.get('services', None) == ['urn::dmtf-org:service:redfish-rest:']:
|
||||
peerinfo = wc.grab_json_response('/redfish/v1/')
|
||||
peerinfo = await wc.grab_json_response('/redfish/v1/')
|
||||
if peerinfo:
|
||||
data['services'] = ['lenovo-smm3']
|
||||
data['uuid'] = peerinfo['UUID'].lower()
|
||||
@@ -610,4 +610,3 @@ if __name__ == '__main__':
|
||||
def printit(rsp):
|
||||
pass # print(repr(rsp))
|
||||
asyncio.run(active_scan(printit))
|
||||
|
||||
|
||||
@@ -570,7 +570,7 @@ async def wsock_handler(req):
|
||||
width=msg['width'], height=msg['height'])
|
||||
if action == 'break':
|
||||
clientsessid = '{0}'.format(msg['sessid'])
|
||||
myconsoles[clientsessid].send_break()
|
||||
await myconsoles[clientsessid].send_break()
|
||||
elif action == 'stop':
|
||||
sessid = '{0}'.format(msg.get('sessid', None))
|
||||
if sessid in myconsoles:
|
||||
@@ -584,21 +584,24 @@ async def wsock_handler(req):
|
||||
if asess:
|
||||
await asess.destroy()
|
||||
return rsp
|
||||
if '/console/session' in ws.path or '/shell/sessions/' in ws.path:
|
||||
def datacallback(data):
|
||||
ws.send(websockify_data(data))
|
||||
geom = ws.wait()
|
||||
geom = geom[1:]
|
||||
path = req.rel_url.path
|
||||
if '/console/session' in path or '/shell/sessions/' in path:
|
||||
async def datacallback(data):
|
||||
await rsp.send_str(websockify_data(data))
|
||||
geom = await rsp.receive()
|
||||
if geom.type != WSMsgType.TEXT:
|
||||
return rsp
|
||||
geom = geom.data[1:]
|
||||
geom = json.loads(geom)
|
||||
width = geom['width']
|
||||
height = geom['height']
|
||||
skipreplay = geom.get('skipreplay', False)
|
||||
#hard bake JSON into this path, do not support other incarnations
|
||||
if '/console/session' in ws.path:
|
||||
prefix, _, _ = ws.path.partition('/console/session')
|
||||
if '/console/session' in path:
|
||||
prefix, _, _ = path.partition('/console/session')
|
||||
shellsession = False
|
||||
elif '/shell/sessions/' in ws.path:
|
||||
prefix, _, _ = ws.path.partition('/shell/sessions')
|
||||
elif '/shell/sessions/' in path:
|
||||
prefix, _, _ = path.partition('/shell/sessions')
|
||||
shellsession = True
|
||||
_, _, nodename = prefix.rpartition('/')
|
||||
|
||||
@@ -617,24 +620,26 @@ async def wsock_handler(req):
|
||||
)
|
||||
except exc.NotFoundException:
|
||||
return
|
||||
clientmsg = ws.wait()
|
||||
clientmsg = await rsp.receive()
|
||||
try:
|
||||
while clientmsg is not None:
|
||||
while clientmsg.type == WSMsgType.TEXT:
|
||||
clientmsg = clientmsg.data
|
||||
if clientmsg[0] == ' ':
|
||||
consession.write(clientmsg[1:])
|
||||
await consession.write(clientmsg[1:])
|
||||
elif clientmsg[0] == '!':
|
||||
cmd = json.loads(clientmsg[1:])
|
||||
action = cmd.get('action', None)
|
||||
if action == 'break':
|
||||
consession.send_break()
|
||||
await consession.send_break()
|
||||
elif action == 'resize':
|
||||
consession.resize(
|
||||
width=cmd['width'], height=cmd['height'])
|
||||
elif clientmsg[0] == '?':
|
||||
ws.send(u'?')
|
||||
clientmsg = ws.wait()
|
||||
await rsp.send_str(u'?')
|
||||
clientmsg = await rsp.receive()
|
||||
finally:
|
||||
consession.destroy()
|
||||
await consession.destroy()
|
||||
return rsp
|
||||
|
||||
|
||||
async def resourcehandler(request):
|
||||
@@ -926,14 +931,14 @@ async def resourcehandler_backend(req, make_response):
|
||||
await rsp.write(json.dumps({'session': querydict['session']}))
|
||||
return rsp # client has requests to send or receive, not both...
|
||||
elif 'closesession' in querydict:
|
||||
consolesessions[querydict['session']]['session'].destroy()
|
||||
await consolesessions[querydict['session']]['session'].destroy()
|
||||
del consolesessions[querydict['session']]
|
||||
rsp = await make_response('application/json', 200)
|
||||
await rsp.write(b'{"sessionclosed": true}')
|
||||
return rsp
|
||||
elif 'action' in querydict:
|
||||
if querydict['action'] == 'break':
|
||||
consolesessions[querydict['session']]['session'].send_break()
|
||||
await consolesessions[querydict['session']]['session'].send_break()
|
||||
elif querydict['action'] == 'resize':
|
||||
consolesessions[querydict['session']]['session'].resize(
|
||||
width=querydict['width'], height=querydict['height'])
|
||||
|
||||
@@ -246,6 +246,9 @@ class NetManager(object):
|
||||
teammod = attribs.get('team_mode', None)
|
||||
if teammod:
|
||||
myattribs['team_mode'] = teammod
|
||||
extrastgs = attribs.get('extra_settings', None)
|
||||
if extrastgs:
|
||||
myattribs['extra_settings'] = extrastgs
|
||||
method = attribs.get('ipv4_method', None)
|
||||
if method != 'dhcp':
|
||||
ipv4addr = attribs.get('ipv4_address', None)
|
||||
|
||||
@@ -131,8 +131,8 @@ def b64tohex(b64str):
|
||||
bd = bytearray(bd)
|
||||
return ''.join(['{0:02x}'.format(x) for x in bd])
|
||||
|
||||
def get_fingerprint(switch, port, configmanager, portmatch):
|
||||
update_switch_data(switch, configmanager)
|
||||
async def get_fingerprint(switch, port, configmanager, portmatch):
|
||||
await update_switch_data(switch, configmanager)
|
||||
for neigh in _neighbypeerid:
|
||||
info = _neighbypeerid[neigh]
|
||||
if neigh == '!!vintage' or info.get('switch', None) != switch:
|
||||
@@ -269,6 +269,7 @@ async def _extract_neighbor_data_affluent(switch, user, password, cfm, lldpdata,
|
||||
'peerportid': record['peerportid'],
|
||||
'port': record['localport'],
|
||||
'peerid': peerid,
|
||||
'peeraddresses': record.get('peeraddresses', []),
|
||||
}
|
||||
_extract_extended_desc(portdata, portdata['peerdescription'], True)
|
||||
_neighbypeerid[peerid] = portdata
|
||||
@@ -490,4 +491,3 @@ async def _handle_neighbor_query(pathcomponents, configmanager):
|
||||
if isinstance(x, Exception):
|
||||
raise x
|
||||
return list_info(parms, listrequested)
|
||||
|
||||
|
||||
@@ -254,6 +254,7 @@ async def _start_offloader():
|
||||
|
||||
|
||||
async def _recv_offload():
|
||||
global _offloader
|
||||
try:
|
||||
upacker = msgpack.Unpacker(encoding='utf8')
|
||||
except TypeError:
|
||||
@@ -261,6 +262,15 @@ async def _recv_offload():
|
||||
#instream = _offloader.stdout.fileno()
|
||||
while True:
|
||||
datum = await _offloader.stdout.read(512)
|
||||
if not datum:
|
||||
_offloader = None
|
||||
pending = list(_offloadevts.values())
|
||||
_offloadevts.clear()
|
||||
for future in pending:
|
||||
if not future.done():
|
||||
future.set_exception(
|
||||
RuntimeError('MAC map offload process exited'))
|
||||
return
|
||||
upacker.feed(datum)
|
||||
for result in upacker:
|
||||
if result[0] not in _offloadevts:
|
||||
@@ -626,15 +636,15 @@ async def handle_api_request(configmanager, inputdata, operation, pathcomponents
|
||||
operation, '/'.join(pathcomponents)))
|
||||
|
||||
|
||||
def get_node_fingerprints(nodename, configmanager):
|
||||
async def get_node_fingerprints(nodename, configmanager):
|
||||
cfg = configmanager.get_node_attributes(nodename, ['net*.switch',
|
||||
'net*.switchport'])
|
||||
for attrkey in cfg[nodename]:
|
||||
if attrkey.endswith('switch'):
|
||||
switch = cfg[nodename][attrkey]['value']
|
||||
port = cfg[nodename][attrkey + 'port']['value']
|
||||
yield get_fingerprint(switch, port, configmanager,
|
||||
_namesmatch)
|
||||
yield await get_fingerprint(switch, port, configmanager,
|
||||
_namesmatch)
|
||||
|
||||
|
||||
async def handle_read_api_request(pathcomponents, configmanager):
|
||||
|
||||
@@ -254,10 +254,10 @@ def yield_rename_resources(namemap, isnode):
|
||||
else:
|
||||
yield msg.RenamedResource(node, namemap[node])
|
||||
|
||||
def update_locks(nodes, configmanager, inputdata):
|
||||
async def update_locks(nodes, configmanager, inputdata):
|
||||
for node in nodes:
|
||||
updatestate = inputdata.inputbynode[node]
|
||||
configmanager.set_node_attributes({node: {'deployment.lock': updatestate}})
|
||||
await configmanager.set_node_attributes({node: {'deployment.lock': updatestate}})
|
||||
yield msg.DeploymentLock(node, updatestate)
|
||||
|
||||
async def update_nodes(nodes, element, configmanager, inputdata):
|
||||
|
||||
@@ -46,7 +46,7 @@ async def create_ident_image(node, configmanager):
|
||||
tmpd = tempfile.mkdtemp()
|
||||
ident = { 'nodename': node }
|
||||
apikey = create_apikey()
|
||||
configmanager.set_node_attributes({node: {'secret.selfapiarmtoken': apikey}})
|
||||
await configmanager.set_node_attributes({node: {'secret.selfapiarmtoken': apikey}})
|
||||
ident['apitoken'] = apikey
|
||||
# This particular mechanism does not (yet) do anything smart with collective
|
||||
# It would be a reasonable enhancement to list all collective server addresses
|
||||
@@ -79,4 +79,3 @@ async def update(nodes, element, configmanager, inputdata):
|
||||
yield msg.CreatedResource(
|
||||
'nodes/{0}/deployment/ident_image'.format(node))
|
||||
|
||||
|
||||
|
||||
@@ -72,10 +72,10 @@ async def renotify_me(node, configmanager, myname):
|
||||
creds = configmanager.get_node_attributes(
|
||||
node, ['secret.hardwaremanagementuser', 'secret.hardwaremanagementpassword'], decrypt=True)
|
||||
wc = WebClient(node, configmanager, creds)
|
||||
res, status = wc.wc.grab_json_response_with_status('/affluent/systems/renotify', {'subscriber': myname})
|
||||
await wc.wc.grab_json_response_with_status('/affluent/systems/renotify', {'subscriber': myname})
|
||||
|
||||
|
||||
def subscribe_discovery(node, configmanager, myname):
|
||||
async def subscribe_discovery(node, configmanager, myname):
|
||||
creds = configmanager.get_node_attributes(
|
||||
node, ['secret.hardwaremanagementuser', 'secret.hardwaremanagementpassword'], decrypt=True)
|
||||
tsock = socket.create_connection((node, 443))
|
||||
@@ -87,19 +87,19 @@ def subscribe_discovery(node, configmanager, myname):
|
||||
wc = WebClient(node, configmanager, creds)
|
||||
with open('/etc/confluent/tls/cacert.pem') as cain:
|
||||
cacert = cain.read()
|
||||
wc.wc.grab_json_response('/affluent/cert_authorities/{0}'.format(myname), cacert)
|
||||
res, status = wc.wc.grab_json_response_with_status('/affluent/discovery_subscribers/{0}'.format(myname), {'url': myurl, 'authname': node})
|
||||
await wc.wc.grab_json_response('/affluent/cert_authorities/{0}'.format(myname), cacert)
|
||||
res, status = await wc.wc.grab_json_response_with_status('/affluent/discovery_subscribers/{0}'.format(myname), {'url': myurl, 'authname': node})
|
||||
if status == 200:
|
||||
agentkey = res['cryptkey']
|
||||
configmanager.set_node_attributes({node: {'crypted.selfapikey': {'hashvalue': agentkey}}})
|
||||
res, status = wc.wc.grab_json_response_with_status('/affluent/systems/renotify', {'subscriber': myname})
|
||||
await configmanager.set_node_attributes({node: {'crypted.selfapikey': {'hashvalue': agentkey}}})
|
||||
await wc.wc.grab_json_response_with_status('/affluent/systems/renotify', {'subscriber': myname})
|
||||
|
||||
def unsubscribe_discovery(node, configmanager, myname):
|
||||
async def unsubscribe_discovery(node, configmanager, myname):
|
||||
creds = configmanager.get_node_attributes(
|
||||
node, ['secret.hardwaremanagementuser', 'secret.hardwaremanagementpassword'], decrypt=True)
|
||||
wc = WebClient(node, configmanager, creds)
|
||||
res, status = wc.wc.grab_json_response_with_status('/affluent/cert_authorities/{0}'.format(myname), method='DELETE')
|
||||
res, status = wc.wc.grab_json_response_with_status('/affluent/discovery_subscribers/{0}'.format(myname), method='DELETE')
|
||||
await wc.wc.grab_json_response_with_status('/affluent/cert_authorities/{0}'.format(myname), method='DELETE')
|
||||
await wc.wc.grab_json_response_with_status('/affluent/discovery_subscribers/{0}'.format(myname), method='DELETE')
|
||||
|
||||
|
||||
def update(nodes, element, configmanager, inputdata):
|
||||
|
||||
@@ -162,7 +162,7 @@ class PDUClient(object):
|
||||
self.wc.grab_response('/logout_wait.htm')
|
||||
|
||||
async def get_outlet(self, outlet):
|
||||
rsp = await self.wc.grab_response('/setting_admin4.xml')
|
||||
rsp = self.wc.grab_response('/setting_admin4.xml')
|
||||
xd = fromstring(rsp[0])
|
||||
for ch in xd:
|
||||
if 'relay' not in ch.tag:
|
||||
@@ -176,7 +176,7 @@ class PDUClient(object):
|
||||
outlet = int(outlet)
|
||||
ident = self.map_outlets[outlet]
|
||||
sitem = '/SetParm?item={}?content={}'.format(ident, state)
|
||||
await self.wc.grab_response(sitem)
|
||||
self.wc.grab_response(sitem)
|
||||
|
||||
async def retrieve(nodes, element, configmanager, inputdata):
|
||||
if 'outlets' not in element:
|
||||
|
||||
@@ -553,16 +553,16 @@ class IpmiHandler:
|
||||
if (self.error == 'timeout' or
|
||||
'Insufficient resources' in self.error):
|
||||
self.error = self.error.replace(' reported in RAKP4', '')
|
||||
self.output.put(msg.ConfluentTargetTimeout(
|
||||
await self.output.put(msg.ConfluentTargetTimeout(
|
||||
self.node, self.error))
|
||||
return
|
||||
elif 'Invalid Session ID' in self.error:
|
||||
self.output.put(msg.ConfluentTargetTimeout(
|
||||
await self.output.put(msg.ConfluentTargetTimeout(
|
||||
self.node, 'Temporary Login Error'))
|
||||
return
|
||||
elif ('Unauthorized' in self.error or
|
||||
'Incorrect password' in self.error):
|
||||
self.output.put(
|
||||
await self.output.put(
|
||||
msg.ConfluentTargetInvalidCredentials(self.node))
|
||||
return
|
||||
else:
|
||||
@@ -707,7 +707,7 @@ class IpmiHandler:
|
||||
if tmpvarbind.endswith('3183.1.1'):
|
||||
varbinddata = inputdata[tmpvarbind]
|
||||
varbinddata = hex2bin(varbinddata)
|
||||
event = self.ipmicmd.decode_pet(specifictrap, 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))
|
||||
|
||||
@@ -1006,7 +1006,7 @@ class IpmiHandler:
|
||||
if activeupdates:
|
||||
await self.output.put(msg.KeyValueData({'status': 'active'}, self.node))
|
||||
else:
|
||||
status = self.ipmicmd.get_update_status()
|
||||
status = await self.ipmicmd.get_update_status()
|
||||
await self.output.put(msg.KeyValueData({'status': status}, self.node))
|
||||
|
||||
async def handle_inventory(self):
|
||||
@@ -1024,12 +1024,12 @@ class IpmiHandler:
|
||||
|
||||
async def list_leds(self):
|
||||
await self.output.put(msg.ChildCollection('all'))
|
||||
for category, info in self.ipmicmd.get_leds():
|
||||
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 = []
|
||||
for category, info in self.ipmicmd.get_leds():
|
||||
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))
|
||||
@@ -1053,7 +1053,7 @@ class IpmiHandler:
|
||||
await self.make_inventory_map()
|
||||
compname = self.invmap.get(component, None)
|
||||
if compname is None:
|
||||
self.output.put(msg.ConfluentTargetNotFound())
|
||||
await self.output.put(msg.ConfluentTargetNotFound())
|
||||
return
|
||||
invdata = await self.ipmicmd.get_inventory_of_component(compname)
|
||||
if invdata is None:
|
||||
@@ -1117,7 +1117,7 @@ class IpmiHandler:
|
||||
if len(storelem) < 2 or storelem[0] != 'volumes':
|
||||
raise exc.InvalidArgumentException('Must target a specific volume')
|
||||
volname = storelem[-1]
|
||||
curr = self.ipmicmd.get_storage_configuration()
|
||||
curr = await self.ipmicmd.get_storage_configuration()
|
||||
volumes = []
|
||||
volsfound = False
|
||||
toremove = storage.ConfigSpec(arrays=[storage.Array(volumes=volumes)])
|
||||
@@ -1130,7 +1130,7 @@ class IpmiHandler:
|
||||
await self.output.put(msg.ConfluentTargetNotFound(
|
||||
self.node, "No volume named '{0}' found".format(volname)))
|
||||
return
|
||||
self.ipmicmd.remove_storage_configuration(toremove)
|
||||
await self.ipmicmd.remove_storage_configuration(toremove)
|
||||
await self.output.put(msg.DeletedResource(volname))
|
||||
|
||||
async def _create_storage(self, storelem):
|
||||
@@ -1138,7 +1138,7 @@ class IpmiHandler:
|
||||
raise exc.InvalidArgumentException('Can only create volumes')
|
||||
vols = []
|
||||
thedisks = None
|
||||
currcfg = self.ipmicmd.get_storage_configuration()
|
||||
currcfg = await self.ipmicmd.get_storage_configuration()
|
||||
currnames = []
|
||||
for arr in currcfg.arrays:
|
||||
arrname = '{0}-{1}'.format(*arr.id)
|
||||
@@ -1344,7 +1344,7 @@ class IpmiHandler:
|
||||
|
||||
async def list_sensors(self):
|
||||
try:
|
||||
sensors = await self.ipmicmd.get_sensor_descriptions()
|
||||
sensors = [sensor async for sensor in self.ipmicmd.get_sensor_descriptions()]
|
||||
except pygexc.IpmiException:
|
||||
await self.output.put(msg.ConfluentTargetTimeout(self.node))
|
||||
return
|
||||
@@ -1667,7 +1667,7 @@ class IpmiHandler:
|
||||
async def handle_ikvm(self):
|
||||
methods = await self.ipmicmd.get_ikvm_methods()
|
||||
if 'openbmc' in methods:
|
||||
url = vinzmanager.get_url(self.node, self.inputdata)
|
||||
url = await vinzmanager.get_url(self.node, self.inputdata)
|
||||
await self.output.put(msg.ChildCollection(url))
|
||||
return
|
||||
launchdata = await self.ipmicmd.get_ikvm_launchdata()
|
||||
@@ -1759,4 +1759,3 @@ def delete(nodes, element, configmanager, inputdata):
|
||||
element, type='ffdc')
|
||||
return perform_requests(
|
||||
'delete', nodes, element, configmanager, inputdata, 'delete')
|
||||
|
||||
|
||||
@@ -71,9 +71,10 @@ async def readpdu(pdu, outletmap, configmanager, rspq):
|
||||
for outlet in outletmap:
|
||||
node, pgroup = outletmap[outlet]
|
||||
try:
|
||||
for rsp in core.handle_path(
|
||||
responses = await core.handle_path(
|
||||
'/nodes/{0}/power/outlets/{1}'.format(pdu, outlet),
|
||||
'retrieve', configmanager):
|
||||
'retrieve', configmanager)
|
||||
async for rsp in core.iterate_responses(responses):
|
||||
await rspq.put(msg.KeyValueData({pgroup: rsp.kvpairs['state']['value']}, node))
|
||||
except exc.TargetEndpointBadCredentials:
|
||||
await rspq.put(msg.ConfluentTargetInvalidCredentials(pdu))
|
||||
@@ -137,8 +138,9 @@ async def updatepdu(pdu, outletmap, configmanager, inputdata, rspq):
|
||||
try:
|
||||
for outlet in outletmap:
|
||||
node, pgroup = outletmap[outlet]
|
||||
for rsp in core.handle_path('/nodes/{0}/power/outlets/{1}'.format(pdu, outlet),
|
||||
'update', configmanager, inputdata={'state': inputdata.powerstate(node)}):
|
||||
responses = await core.handle_path('/nodes/{0}/power/outlets/{1}'.format(pdu, outlet),
|
||||
'update', configmanager, inputdata={'state': inputdata.powerstate(node)})
|
||||
async for rsp in core.iterate_responses(responses):
|
||||
await rspq.put(msg.KeyValueData({pgroup: rsp.kvpairs['state']['value']}, node))
|
||||
finally:
|
||||
await rspq.put(TaskDone())
|
||||
|
||||
@@ -399,7 +399,7 @@ class IpmiHandler:
|
||||
tenant = cfg.tenant
|
||||
if (node, tenant) not in persistent_ipmicmds:
|
||||
try:
|
||||
await persistent_ipmicmds[(node, tenant)].close_confluent()
|
||||
persistent_ipmicmds[(node, tenant)].close_confluent()
|
||||
except KeyError: # was no previous session
|
||||
pass
|
||||
try:
|
||||
@@ -830,7 +830,7 @@ class IpmiHandler:
|
||||
|
||||
async def make_sensor_map(self, sensors=None):
|
||||
if sensors is None:
|
||||
sensors = await self.ipmicmd.get_sensor_descriptions()
|
||||
sensors = self.ipmicmd.get_sensor_descriptions()
|
||||
async for sensor in sensors:
|
||||
resourcename = sensor['name']
|
||||
self.sensormap[simplify_name(resourcename)] = resourcename
|
||||
@@ -1075,7 +1075,7 @@ class IpmiHandler:
|
||||
volsfound = True
|
||||
volumes.append(vol)
|
||||
if not volsfound:
|
||||
self.output.put(msg.ConfluentTargetNotFound(
|
||||
await self.output.put(msg.ConfluentTargetNotFound(
|
||||
self.node, "No volume named '{0}' found".format(volname)))
|
||||
return
|
||||
await self.ipmicmd.remove_storage_configuration(toremove)
|
||||
@@ -1128,14 +1128,14 @@ class IpmiHandler:
|
||||
vol.status, arrname))
|
||||
return
|
||||
else:
|
||||
self._show_storage(storelem[:1] + [vol['name']])
|
||||
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')
|
||||
self.set_disk(storelem[-1],
|
||||
self.inputdata.inputbynode[self.node])
|
||||
await self.set_disk(storelem[-1],
|
||||
self.inputdata.inputbynode[self.node])
|
||||
await self._show_storage(storelem)
|
||||
|
||||
async def _show_storage(self, storelem):
|
||||
@@ -1292,7 +1292,7 @@ class IpmiHandler:
|
||||
|
||||
async def list_sensors(self):
|
||||
try:
|
||||
sensors = await self.ipmicmd.get_sensor_descriptions()
|
||||
sensors = [sensor async for sensor in self.ipmicmd.get_sensor_descriptions()]
|
||||
except pygexc.IpmiException:
|
||||
await self.output.put(msg.ConfluentTargetTimeout(self.node))
|
||||
return
|
||||
|
||||
@@ -55,7 +55,7 @@ async def retrieve_node_backend(node, element, user, pwd, configmanager, inputda
|
||||
if element[-1] == 'all' or simplify_name(sensor['name']) == element[-1]:
|
||||
await results.put(msg.SensorReadings([sensor], node))
|
||||
else:
|
||||
results.put(msg.ConfluentNodeError(node, 'Not supported'))
|
||||
await results.put(msg.ConfluentNodeError(node, 'Not supported'))
|
||||
|
||||
|
||||
async def retrieve(nodes, element, configmanager, inputdata):
|
||||
|
||||
@@ -142,7 +142,7 @@ class SshShell(conapi.Console):
|
||||
if b'\r' in self.keyaction:
|
||||
action = self.keyaction.split(b'\r')[0]
|
||||
if action.lower() == b'accept':
|
||||
self.nodeconfig.set_node_attributes(
|
||||
await self.nodeconfig.set_node_attributes(
|
||||
{self.node:
|
||||
{self.keyattrname: self.candidatefprint}})
|
||||
await self.datacallback('\r\n')
|
||||
@@ -153,10 +153,10 @@ class SshShell(conapi.Console):
|
||||
self.keyaction = b''
|
||||
await self.datacallback('\r\nEnter "disconnect" or "accept": ')
|
||||
elif len(data) > 0:
|
||||
self.datacallback(data)
|
||||
await self.datacallback(data)
|
||||
elif self.inputmode == 0:
|
||||
while len(data) and data[0:1] == b'\x7f' and len(self.username):
|
||||
self.datacallback('\b \b') # erase previously echoed value
|
||||
await self.datacallback('\b \b') # erase previously echoed value
|
||||
self.username = self.username[:-1]
|
||||
data = data[1:]
|
||||
while len(data) and data[0:1] == b'\x7f':
|
||||
|
||||
@@ -252,7 +252,7 @@ async def handle_request(req, make_response, mimetype):
|
||||
if not bmcaddr:
|
||||
return await make_response(mimetype, 500, 'Internal Server Error', body='Missing value in hardwaremanagement.manager')
|
||||
bmcaddr = bmcaddr.split('/', 1)[0]
|
||||
bmcaddr = await asyncio.get_running_loop().getaddrinfo(bmcaddr, 0)[0]
|
||||
bmcaddr = (await asyncio.get_running_loop().getaddrinfo(bmcaddr, 0))[0]
|
||||
bmcaddr = bmcaddr[-1][0]
|
||||
if '.' in bmcaddr: # ipv4 is allowed
|
||||
netconfig = await netutil.get_nic_config(cfg, nodename, ip=bmcaddr)
|
||||
@@ -456,9 +456,9 @@ async def handle_request(req, make_response, mimetype):
|
||||
reqbody = None
|
||||
cfgmod = reqbody.get('configmod', 'unspecified')
|
||||
if cfgmod == 'xcc':
|
||||
xcc.remote_nodecfg(nodename, cfg)
|
||||
await xcc.remote_nodecfg(nodename, cfg)
|
||||
elif cfgmod == 'tsm':
|
||||
tsm.remote_nodecfg(nodename, cfg)
|
||||
await tsm.remote_nodecfg(nodename, cfg)
|
||||
else:
|
||||
return await make_response(mimetype, 500, 'unsupported configmod', body='Unsupported configmod "{}"'.format(cfgmod))
|
||||
return await make_response(mimetype, 200, 'Ok', body='complete')
|
||||
|
||||
@@ -30,6 +30,7 @@ import fcntl
|
||||
import os
|
||||
import pty
|
||||
import random
|
||||
import select
|
||||
import subprocess
|
||||
|
||||
|
||||
@@ -54,12 +55,12 @@ class ExecConsole(conapi.Console):
|
||||
try:
|
||||
somedata = os.read(self._master, 128)
|
||||
while somedata:
|
||||
self._datacallback(somedata)
|
||||
await self._datacallback(somedata)
|
||||
await asyncio.sleep(0)
|
||||
somedata = os.read(self._master, 128)
|
||||
except OSError as e:
|
||||
if e.errno == 5:
|
||||
self._datacallback(conapi.ConsoleEvent.Disconnect)
|
||||
await self._datacallback(conapi.ConsoleEvent.Disconnect)
|
||||
self.subproc = None
|
||||
return
|
||||
if e.errno != 11:
|
||||
@@ -68,7 +69,7 @@ class ExecConsole(conapi.Console):
|
||||
try:
|
||||
somedata = self.subproc.stderr.read()
|
||||
while somedata:
|
||||
self._datacallback(somedata)
|
||||
await self._datacallback(somedata)
|
||||
await asyncio.sleep(0)
|
||||
somedata = self.subproc.stderr.read()
|
||||
except IOError as e:
|
||||
@@ -76,10 +77,10 @@ class ExecConsole(conapi.Console):
|
||||
raise
|
||||
childstate = self.subproc.poll()
|
||||
if childstate is not None:
|
||||
self._datacallback(conapi.ConsoleEvent.Disconnect)
|
||||
await self._datacallback(conapi.ConsoleEvent.Disconnect)
|
||||
self.subproc = None
|
||||
|
||||
def connect(self, callback):
|
||||
async def connect(self, callback):
|
||||
self._datacallback = callback
|
||||
master, slave = pty.openpty()
|
||||
self._master = master
|
||||
@@ -90,14 +91,16 @@ class ExecConsole(conapi.Console):
|
||||
stderr=subprocess.PIPE, close_fds=True)
|
||||
except OSError:
|
||||
print("Unable to execute " + self.executable + " (permissions?)")
|
||||
self.close()
|
||||
os.close(master)
|
||||
os.close(slave)
|
||||
self._master = None
|
||||
return
|
||||
os.close(slave)
|
||||
fcntl.fcntl(master, fcntl.F_SETFL, os.O_NONBLOCK)
|
||||
fcntl.fcntl(self.subproc.stderr.fileno(), fcntl.F_SETFL, os.O_NONBLOCK)
|
||||
self.readerthread = tasks.spawn(self.relaydata())
|
||||
|
||||
def write(self, data):
|
||||
async def write(self, data):
|
||||
os.write(self._master, data)
|
||||
|
||||
async def close(self):
|
||||
|
||||
@@ -37,7 +37,7 @@ async def reapsessions():
|
||||
for sesshdl in list(currcli):
|
||||
currsess = currcli[sesshdl]
|
||||
if currsess.numusers == 0 and currsess.expiry < time.time():
|
||||
currsess.close()
|
||||
await currsess.close()
|
||||
del activesessions[clientid][sesshdl]
|
||||
|
||||
class _ShellHandler(consoleserver.ConsoleHandler):
|
||||
@@ -54,7 +54,7 @@ class _ShellHandler(consoleserver.ConsoleHandler):
|
||||
_reaper = tasks.spawn(reapsessions())
|
||||
|
||||
|
||||
def check_collective(self, attrvalue):
|
||||
async def check_collective(self, attrvalue):
|
||||
return
|
||||
|
||||
def log(self, *args, **kwargs):
|
||||
@@ -69,7 +69,7 @@ class _ShellHandler(consoleserver.ConsoleHandler):
|
||||
# #retdata, connstate = await super(_ShellHandler, self).get_recent()
|
||||
# return '', {} # connstate
|
||||
|
||||
def _got_disconnected(self):
|
||||
async def _got_disconnected(self):
|
||||
self.connectstate = 'closed'
|
||||
tasks.spawn(self._bgdisconnect())
|
||||
|
||||
@@ -77,7 +77,7 @@ class _ShellHandler(consoleserver.ConsoleHandler):
|
||||
await self._send_rcpts({'connectstate': self.connectstate})
|
||||
for session in list(self.livesessions):
|
||||
await session.destroy()
|
||||
self.feedbuffer('\x1bc')
|
||||
await self.feedbuffer('\x1bc')
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -318,7 +318,7 @@ async def term_interact(authdata, authname, ccons, cfm, connection, consession,
|
||||
await consession.destroy()
|
||||
break
|
||||
elif data['operation'] == 'break':
|
||||
consession.send_break()
|
||||
await consession.send_break()
|
||||
continue
|
||||
elif data['operation'] == 'reopen':
|
||||
await consession.reopen()
|
||||
|
||||
@@ -7,6 +7,7 @@ import confluent.collective.manager as collective
|
||||
import confluent.util as util
|
||||
import glob
|
||||
import os
|
||||
import signal
|
||||
import shutil
|
||||
import subprocess
|
||||
import tempfile
|
||||
|
||||
@@ -246,7 +246,7 @@ async def handle_api_request(url, req, username, cfm, reqbody, authorized):
|
||||
if url == '/registration_options':
|
||||
userinfo = cfm.get_user(username)
|
||||
if not userinfo:
|
||||
cfm.create_user(username, role='Stub')
|
||||
await cfm.create_user(username, role='Stub')
|
||||
userinfo = cfm.get_user(username)
|
||||
authid = userinfo.get('webauthid', None)
|
||||
if not authid: # TODO: index users by authid as well as name
|
||||
|
||||
+13
-2
@@ -1674,10 +1674,14 @@ async def pack_image(args):
|
||||
pass
|
||||
|
||||
def gather_bootloader(outdir, rootpath='/'):
|
||||
shimdestfilename = 'BOOTX64.EFI'
|
||||
grubdestfilename = 'grubx64.efi'
|
||||
shimlocation = os.path.join(rootpath, 'boot/efi/EFI/BOOT/BOOTX64.EFI')
|
||||
if not os.path.exists(shimlocation):
|
||||
shimlocation = os.path.join(rootpath, 'boot/efi/EFI/BOOT/BOOTAA64.EFI')
|
||||
shimdestfilename = os.path.basename(shimlocation)
|
||||
if not os.path.exists(shimlocation):
|
||||
shimdestfilename = 'BOOTX64.EFI'
|
||||
shimlocation = os.path.join(rootpath, 'usr/lib64/efi/shim.efi')
|
||||
if not os.path.exists(shimlocation):
|
||||
shimlocation = os.path.join(rootpath, 'usr/lib/shim/shimx64.efi.signed.latest')
|
||||
@@ -1686,13 +1690,19 @@ def gather_bootloader(outdir, rootpath='/'):
|
||||
if not os.path.exists(shimlocation):
|
||||
shimlocation = os.path.join(rootpath, 'usr/lib/shim/shimaa64.efi.signed.latest')
|
||||
mkdirp(os.path.join(outdir, 'boot/efi/boot'))
|
||||
shutil.copyfile(shimlocation, os.path.join(outdir, 'boot/efi/boot/BOOTX64.EFI'))
|
||||
shutil.copyfile(shimlocation, os.path.join(outdir, 'boot/efi/boot/{0}'.format(shimdestfilename)))
|
||||
for maybemokmanager in glob.glob(os.path.join(rootpath, 'boot/efi/EFI/*/mmx64.efi')):
|
||||
shutil.copyfile(maybemokmanager, os.path.join(outdir, 'boot/efi/boot/mmx64.efi'))
|
||||
break
|
||||
else:
|
||||
if os.path.exists(os.path.join(rootpath, 'usr/lib/shim/mmx64.efi')):
|
||||
shutil.copyfile(os.path.join(rootpath, 'usr/lib/shim/mmx64.efi'), os.path.join(outdir, 'boot/efi/boot/mmx64.efi'))
|
||||
for maybemokmanager in glob.glob(os.path.join(rootpath, 'boot/efi/EFI/*/mmaa64.efi')):
|
||||
shutil.copyfile(maybemokmanager, os.path.join(outdir, 'boot/efi/boot/mmaa64.efi'))
|
||||
break
|
||||
else:
|
||||
if os.path.exists(os.path.join(rootpath, 'usr/lib/shim/mmaa64.efi')):
|
||||
shutil.copyfile(os.path.join(rootpath, 'usr/lib/shim/mmaa64.efi'), os.path.join(outdir, 'boot/efi/boot/mmaa64.efi'))
|
||||
grubbin = None
|
||||
for candidate in glob.glob(os.path.join(rootpath, 'boot/efi/EFI/*')):
|
||||
if 'BOOT' not in candidate:
|
||||
@@ -1701,6 +1711,7 @@ def gather_bootloader(outdir, rootpath='/'):
|
||||
break
|
||||
grubbin = os.path.join(candidate, 'grubaa64.efi')
|
||||
if os.path.exists(grubbin):
|
||||
grubdestfilename = os.path.basename(grubbin)
|
||||
break
|
||||
if not grubbin:
|
||||
grubbin = os.path.join(rootpath, 'usr/lib64/efi/grub.efi')
|
||||
@@ -1717,7 +1728,7 @@ def gather_bootloader(outdir, rootpath='/'):
|
||||
mkdirp(os.path.join(outdir, 'boot/EFI/ubuntu/'))
|
||||
with open(os.path.join(outdir, 'boot/EFI/ubuntu/grub.cfg'), 'w') as wo:
|
||||
wo.write('')
|
||||
shutil.copyfile(grubbin, os.path.join(outdir, 'boot/efi/boot/grubx64.efi'))
|
||||
shutil.copyfile(grubbin, os.path.join(outdir, 'boot/efi/boot/{0}'.format(grubdestfilename)))
|
||||
shutil.copyfile(grubbin, os.path.join(outdir, 'boot/efi/boot/grub.efi'))
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,157 @@
|
||||
#!/usr/bin/python
|
||||
|
||||
import asyncio
|
||||
import re
|
||||
import sys
|
||||
try:
|
||||
import confluent.asynclient as asynclient
|
||||
except ImportError:
|
||||
sys.path.append('/opt/confluent/lib/python')
|
||||
import confluent.asynclient as asynclient
|
||||
|
||||
_whitelistnames = (
|
||||
# 3com
|
||||
re.compile(r'^RMON Port (\d+) on unit \d+'),
|
||||
# Dell
|
||||
re.compile(r'^Unit \d+ Port (\d+)\Z'),
|
||||
)
|
||||
|
||||
_blacklistnames = (
|
||||
re.compile(r'vl'),
|
||||
re.compile(r'Nu'),
|
||||
re.compile(r'RMON'),
|
||||
re.compile(r'onsole'),
|
||||
re.compile(r'Stack'),
|
||||
re.compile(r'Trunk'),
|
||||
re.compile(r'po\d'),
|
||||
re.compile(r'XGE'),
|
||||
re.compile(r'LAG'),
|
||||
re.compile(r'CPU'),
|
||||
re.compile(r'Management'),
|
||||
)
|
||||
|
||||
def mac2lla(mac):
|
||||
"""Convert MAC address to IPv6 link-local address."""
|
||||
# Remove colons from MAC address
|
||||
mac_clean = mac.replace(':', '')
|
||||
# Insert fe80:: prefix and convert to lowercase
|
||||
# Split MAC into first 3 octets and last 3 octets
|
||||
first_half = mac_clean[:6]
|
||||
second_half = mac_clean[6:]
|
||||
# Flip the universal/local bit in the first octet
|
||||
first_octet = int(first_half[:2], 16)
|
||||
first_octet ^= 0x02
|
||||
first_half = f'{first_octet:02x}' + first_half[2:]
|
||||
# Format as IPv6 link-local address
|
||||
lla = f'fe80::{first_half[0:4]}:{first_half[4:]}ff:fe{second_half[0:2]}:{second_half[2:6]}'
|
||||
return lla
|
||||
|
||||
async def is_reachable(address):
|
||||
try:
|
||||
await asyncio.wait_for(asyncio.open_connection(address, 22), timeout=0.5)
|
||||
return True
|
||||
except (asyncio.TimeoutError, OSError):
|
||||
return False
|
||||
|
||||
async def add_zone(lla):
|
||||
if '%' in lla:
|
||||
return lla
|
||||
proc = await asyncio.create_subprocess_exec(
|
||||
'ip', '-json', 'link',
|
||||
stdout=asyncio.subprocess.PIPE,
|
||||
stderr=asyncio.subprocess.PIPE
|
||||
)
|
||||
stdout, _ = await proc.communicate()
|
||||
import json
|
||||
links = json.loads(stdout)
|
||||
tocheck = []
|
||||
for link in links:
|
||||
if 'LOOPBACK' in link.get('flags', []):
|
||||
continue
|
||||
if 'LOWER_UP' not in link.get('flags', []):
|
||||
continue
|
||||
tocheck.append(link)
|
||||
|
||||
tasks = []
|
||||
for link in tocheck:
|
||||
zone_addr = lla + '%' + link['ifname']
|
||||
tasks.append(is_reachable(zone_addr))
|
||||
|
||||
results = await asyncio.gather(*tasks)
|
||||
for link, reachable in zip(tocheck, results):
|
||||
if reachable:
|
||||
return lla + '%' + link['ifname']
|
||||
|
||||
return lla
|
||||
|
||||
def _namesmatch(switchdesc, userdesc):
|
||||
if switchdesc is None:
|
||||
return False
|
||||
if switchdesc == userdesc:
|
||||
return True
|
||||
try:
|
||||
portnum = int(userdesc)
|
||||
except ValueError:
|
||||
portnum = None
|
||||
if portnum is not None:
|
||||
for exp in _whitelistnames:
|
||||
match = exp.match(switchdesc)
|
||||
if match:
|
||||
snum = int(match.groups()[0])
|
||||
if snum == portnum:
|
||||
return True
|
||||
anymatch = re.search(r'[^0123456789]' + userdesc + r'(\.0)?\Z', switchdesc)
|
||||
if anymatch:
|
||||
for blexp in _blacklistnames:
|
||||
if blexp.match(switchdesc):
|
||||
return False
|
||||
return True
|
||||
return False
|
||||
|
||||
async def main(switch, port):
|
||||
portname = None
|
||||
client = asynclient.Command()
|
||||
async for rsp in client.read(f'/networking/neighbors/by-switch/{switch}/by-port/'):
|
||||
portcandidate = rsp.get('item', {}).get('href')[:-1]
|
||||
if _namesmatch(portcandidate, port):
|
||||
if portname:
|
||||
sys.stderr.write(f"Multiple matches found for port {port} on switch {switch}\n")
|
||||
portname = None
|
||||
else:
|
||||
portname = portcandidate
|
||||
peerid = None
|
||||
if portname:
|
||||
async for rsp in client.read(f'/networking/neighbors/by-switch/{switch}/by-port/{portname}/by-peerid/'):
|
||||
peerid = rsp.get('item', {}).get('href')
|
||||
if peerid:
|
||||
fe80found = False
|
||||
maybemac = None
|
||||
async for rsp in client.read(f'/networking/neighbors/by-switch/{switch}/by-port/{portname}/by-peerid/{peerid}'):
|
||||
if 'peeraddresses' in rsp:
|
||||
for addr in rsp['peeraddresses']:
|
||||
if addr.startswith('fe80::'):
|
||||
fe80found = True
|
||||
addr = await add_zone(addr)
|
||||
print(addr)
|
||||
if 'peerchassisid' in rsp:
|
||||
maybemac = rsp['peerchassisid']
|
||||
if not fe80found and maybemac:
|
||||
try:
|
||||
maybella = mac2lla(maybemac)
|
||||
maybella = await add_zone(maybella)
|
||||
print(maybella)
|
||||
except Exception as e:
|
||||
pass
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
if len(sys.argv) < 3:
|
||||
sys.stderr.write(f"Usage: {sys.argv[0]} <switch> <port>\n")
|
||||
sys.exit(1)
|
||||
switch = sys.argv[1]
|
||||
port = sys.argv[2]
|
||||
asyncio.run(main(switch, port))
|
||||
|
||||
Reference in New Issue
Block a user