2
0
mirror of https://github.com/xcat2/confluent.git synced 2026-02-20 14:44:27 +00:00

Merge branch 'master' into async

This commit is contained in:
Jarrod Johnson
2026-02-17 16:19:51 -05:00
4 changed files with 334 additions and 9 deletions

View File

@@ -84,13 +84,13 @@ def main():
healthexplanations[node] = []
for sensor in health[node]['sensors']:
explanation = sensor['name'] + ':'
if sensor['value'] is not None:
if sensor.get('value', None) is not None:
explanation += str(sensor['value'])
if sensor['units'] is not None:
if sensor.get('units', None) is not None:
explanation += sensor['units']
if sensor['states']:
if sensor.get('states', None):
explanation += ','
if sensor['states']:
if sensor.get('states', None):
explanation += ','.join(sensor['states'])
healthexplanations[node].append(explanation)
if node in healthbynode and node in healthexplanations:

View File

@@ -123,7 +123,7 @@ def sensorpass(showout=True, appendtime=False):
if 'sensors' not in reading[node]:
continue
for sensedata in reading[node]['sensors']:
if sensedata['value'] is None and options.skipnumberless:
if sensedata.get('value', None) is None and options.skipnumberless:
continue
for redundant_state in ('Non-Critical', 'Critical'):
try:
@@ -134,17 +134,17 @@ def sensorpass(showout=True, appendtime=False):
resultdata[node][sensedata['name']] = sensedata
sensorname = sensedata['name']
sensorheaders[sensorname] = sensorname
if sensedata['units'] not in (None, u''):
if sensedata.get('units', None) not in (None, u''):
sensorheaders[sensorname] += u' ({0})'.format(
sensedata['units'])
if showout:
if sensedata['value'] is None:
if sensedata.get('value', None) is None:
showval = ''
elif isinstance(sensedata['value'], float):
showval = u' {0} '.format(floatformat(sensedata['value']))
else:
showval = u' {0} '.format(sensedata['value'])
if sensedata['units'] not in (None, u''):
showval = u' {0} '.format(sensedata.get('value', ''))
if sensedata.get('units', None) not in (None, u''):
showval += sensedata['units']
if sensedata.get('health', 'ok') != 'ok':
datadescription = [sensedata['health']]

View File

@@ -0,0 +1,230 @@
import confluent.util as util
import eventlet
webclient = eventlet.import_patched('pyghmi.util.webclient')
class SRLinuxClient:
def __init__(self, switch, user, password, configmanager):
self.cachedurls = {}
self.switch = switch
if configmanager:
cv = util.TLSCertVerifier(
configmanager, switch, 'pubkeys.tls_hardwaremanager'
).verify_cert
else:
cv = lambda x: True
self.user = user
self.password = password
try:
self.user = self.user.decode()
self.password = self.password.decode()
except Exception:
pass
self.wc = webclient.SecureHTTPConnection(switch, port=443, verifycallback=cv)
self.wc.set_basic_credentials(self.user, self.password)
self.rpc_id = 1
self.login()
def login(self):
# Just a quick query to validate that credentials are correct and device is reachable and TLS works out however it is supposed to
self._get_state('/system/information')
def _rpc_call(self, method, params=None):
"""Make a JSON-RPC call to SR-Linux"""
payload = {
'jsonrpc': '2.0',
'id': self.rpc_id,
'method': method,
}
if params:
payload['params'] = params
self.rpc_id += 1
rsp = self.wc.grab_json_response_with_status('/jsonrpc', payload)
if rsp[1] != 200:
raise Exception(f"Failed RPC call: {method}, status: {rsp[1]}")
result = rsp[0]
if 'error' in result:
raise Exception(f"RPC error: {result['error']}")
return result.get('result', {})
def _get_state(self, path, datastore='state'):
"""Get state data from SR-Linux using JSON-RPC get method"""
params = {
'commands': [
{
'path': path,
'datastore': datastore
}
]
}
result = self._rpc_call('get', params)
return result
def get_firmware(self):
"""Get firmware/software version information"""
firmdata = {}
result = self._get_state('/system/information')
for item in result:
if 'version' in item:
firmdata['SR-Linux'] = {'version': item['version']}
if 'build-date' in item:
if 'SR-Linux' in firmdata:
firmdata['SR-Linux']['date'] = item['build-date']
return firmdata
def get_sensors(self):
"""Get sensor readings from the device"""
sensedata = []
result = self._get_state('/platform/control/temperature')
for item in result:
for pcc in item:
currreading = {}
for reading in item[pcc]:
if reading.get('temperature', {}).get('alarm-status', False):
currreading['health'] = 'critical'
else:
currreading['health'] = 'ok'
states = []
if reading.get('oper-state', 'up',) != 'up':
states = [reading.get('oper-reason', 'unknown')]
currreading['states'] = states
currreading['name'] = 'Slot {} Temperature'.format(reading.get('slot', 'Unknown'))
currreading['value'] = reading.get('temperature', {}).get('instant', 'Unknown')
currreading['units'] = '°C'
sensedata.append(currreading)
result = self._get_state('/platform/fan-tray')
for item in result:
for pft in item:
currreading = {}
for reading in item[pft]:
if reading.get('srl_nokia-platform-healthz:healthz', {}).get('status', 'healthy') != 'healthy':
currreading['health'] = 'critical'
else:
currreading['health'] = 'ok'
states = []
if reading.get('oper-state', 'up',) != 'up':
states = [reading.get('oper-reason', 'unknown')]
currreading['states'] = states
currreading['name'] = 'Fan Tray {}'.format(reading.get('id', 'Unknown'))
currreading['value'] = reading.get('fan', {}).get('speed', 'Unknown')
currreading['units'] = '%'
sensedata.append(currreading)
result = self._get_state('/platform/power-supply')
for item in result:
for pps in item:
for reading in item[pps]:
currreading = {}
if reading.get('srl_nokia-platform-healthz:healthz', {}).get('status', 'healthy') != 'healthy':
currreading['health'] = 'critical'
else:
currreading['health'] = 'ok'
states = []
if reading.get('oper-state', 'up',) != 'up':
states = [reading.get('oper-reason', 'unknown')]
currreading['states'] = states
currreading['name'] = 'Power Supply {} Health'.format(reading.get('id', 'Unknown'))
sensedata.append(currreading)
tempreading = {'health': 'ok'}
tempreading['name'] = 'Power Supply {} Temperature'.format(reading.get('id', 'Unknown'))
tempreading['value'] = reading.get('temperature', {}).get('instant', 'Unknown')
tempreading['units'] = '°C'
sensedata.append(tempreading)
for powstat in 'current', 'power', 'voltage':
powreading = {'health': 'ok'}
powreading['name'] = 'Power Supply {} {}'.format(reading.get('id', 'Unknown'), powstat.capitalize())
powreading['value'] = reading.get('input', {}).get(powstat, 'Unknown')
if powstat == 'current':
powreading['units'] = 'A'
elif powstat == 'power':
powreading['units'] = 'W'
elif powstat == 'voltage':
powreading['units'] = 'V'
sensedata.append(powreading)
return sensedata
def get_health(self):
healthdata = {'health': 'ok', 'sensors': []}
sensors = self.get_sensors()
for sensor in sensors:
currhealth = sensor.get('health', 'ok')
if currhealth != 'ok':
healthdata['sensors'].append(sensor)
if sensor['health'] == 'critical':
healthdata['health'] = 'critical'
elif sensor['health'] == 'warning' and healthdata['health'] != 'critical':
healthdata['health'] = 'warning'
return healthdata
def get_inventory(self):
invdata = []
results = self._get_state('/platform/chassis')
for result in results:
invinfo = {'name': 'System', 'present': True}
invinfo['information'] = {'Manufacturer': 'Nokia'}
if isinstance(result, dict):
for key, value in result.items():
if key == 'serial-number':
invinfo['information']['Serial Number'] = value
elif key == 'part-number':
invinfo['information']['Part Number'] = value
elif key == 'type':
invinfo['information']['Model'] = value
if invinfo['information']:
invdata.append(invinfo)
return invdata
def get_mac_table(self):
macdict = {}
response = self._get_state('/network-instance')
for datum in response:
for niname in datum:
for nin in datum[niname]:
if nin.get('type', '').endswith('mac-vrf'):
mactable = nin.get('bridge-table', {}).get('mac-table', {})
#TODO: process mac table
return macdict
def get_lldp(self):
lldpbyport = {}
response = self._get_state('/system/lldp/interface')
for datum in response:
for intfname in datum:
#TODO: actually process LLDP data
return lldpbyport
if __name__ == '__main__':
import sys
import os
from pprint import pprint
myuser = os.environ.get('SWITCHUSER')
mypass = os.environ.get('SWITCHPASS')
if not myuser or not mypass:
print("Set SWITCHUSER and SWITCHPASS environment variables")
sys.exit(1)
srl = SRLinuxClient(sys.argv[1], myuser, mypass, None)
pprint(srl.get_firmware())
pprint(srl.get_inventory())
pprint(srl.get_sensors())
pprint(srl.get_health())
pprint(srl.get_lldp())
pprint(srl.get_mac_table())

View File

@@ -0,0 +1,95 @@
import confluent.networking.srlinux as srlinux
import eventlet
import eventlet.queue as queue
import confluent.messages as msg
import traceback
def retrieve_node(node, element, user, pwd, configmanager, inputdata, results):
try:
retrieve_node_backend(node, element, user, pwd, configmanager, inputdata, results)
except Exception as e:
print(traceback.format_exc())
print(repr(e))
def simplify_name(name):
return name.lower().replace(' ', '_').replace('/', '-').replace(
'_-_', '-')
def retrieve_node_backend(node, element, user, pwd, configmanager, inputdata, results):
cli = srlinux.SRLinuxClient(node, user, pwd, configmanager)
if element == ['power', 'state']: # client initted successfully, must be on
results.put(msg.PowerState(node, 'on'))
elif element == ['health', 'hardware']:
hinfo = cli.get_health()
results.put(msg.HealthSummary(hinfo.get('health', 'unknown'), name=node))
results.put(msg.SensorReadings(hinfo.get('sensors', []), name=node))
elif element[:3] == ['inventory', 'hardware', 'all']:
if len(element) == 3:
results.put(msg.ChildCollection('all'))
return
invinfo = cli.get_inventory()
if invinfo:
results.put(msg.KeyValueData({'inventory': invinfo}, node))
elif element[:3] == ['inventory', 'firmware', 'all']:
if len(element) == 3:
results.put(msg.ChildCollection('all'))
return
fwinfo = []
for fwnam, fwdat in cli.get_firmware().items():
fwinfo.append({fwnam: fwdat})
if fwinfo:
results.put(msg.Firmware(fwinfo, node))
elif element == ['sensors', 'hardware', 'all']:
sensors = cli.get_sensors()
for sensor in sensors:
results.put(msg.ChildCollection(simplify_name(sensor['name'])))
elif element[:3] == ['sensors', 'hardware', 'all']:
sensors = cli.get_sensors()
for sensor in sensors:
if element[-1] == 'all' or simplify_name(sensor['name']) == element[-1]:
results.put(msg.SensorReadings([sensor], node))
else:
print(repr(element))
def retrieve(nodes, element, configmanager, inputdata):
results = queue.LightQueue()
workers = set([])
creds = configmanager.get_node_attributes(
nodes, ['secret.hardwaremanagementuser', 'secret.hardwaremanagementpassword'], decrypt=True)
for node in nodes:
cred = creds.get(node, {})
user = cred.get('secret.hardwaremanagementuser', {}).get('value')
pwd = cred.get('secret.hardwaremanagementpassword', {}).get('value')
try:
user = user.decode()
pwd = pwd.decode()
except Exception:
pass
if not user or not pwd:
yield msg.ConfluentTargetInvalidCredentials(node)
continue
workers.add(eventlet.spawn(retrieve_node, node, element, user, pwd, configmanager, inputdata, results))
while workers:
try:
datum = results.get(block=True, timeout=10)
while datum:
if datum:
yield datum
datum = results.get_nowait()
except queue.Empty:
pass
eventlet.sleep(0.001)
for t in list(workers):
if t.dead:
workers.discard(t)
try:
while True:
datum = results.get_nowait()
if datum:
yield datum
except queue.Empty:
pass