ffmap-backend/lib/nodes.py

179 lines
5.4 KiB
Python
Raw Permalink Normal View History

from collections import Counter, defaultdict
from datetime import datetime
from functools import reduce
2015-03-24 16:49:37 +01:00
def build_mac_table(nodes):
2015-03-24 16:49:37 +01:00
macs = dict()
for node_id, node in nodes.items():
try:
for mac in node['nodeinfo']['network']['mesh_interfaces']:
macs[mac] = node_id
except KeyError:
pass
2015-05-09 22:04:45 +02:00
try:
for mac in node['nodeinfo']['network']['mesh']['bat0']['interfaces']['wireless']:
macs[mac] = node_id
except KeyError:
pass
try:
for mac in node['nodeinfo']['network']['mesh']['bat0']['interfaces']['tunnel']:
macs[mac] = node_id
except KeyError:
pass
try:
for mac in node['nodeinfo']['network']['mesh']['bat0']['interfaces']['other']:
macs[mac] = node_id
except KeyError:
pass
2015-03-24 16:49:37 +01:00
return macs
def prune_nodes(nodes, now, days):
2015-03-24 16:49:37 +01:00
prune = []
for node_id, node in nodes.items():
if 'lastseen' not in node:
prune.append(node_id)
continue
lastseen = datetime.strptime(node['lastseen'], '%Y-%m-%dT%H:%M:%S')
2015-03-26 14:21:11 +01:00
delta = (now - lastseen).days
2015-03-26 14:21:11 +01:00
if delta >= days:
2015-03-24 16:49:37 +01:00
prune.append(node_id)
2015-03-24 16:49:37 +01:00
for node_id in prune:
del nodes[node_id]
def mark_online(node, now):
2015-03-24 16:49:37 +01:00
node['lastseen'] = now.isoformat()
node.setdefault('firstseen', now.isoformat())
node['flags']['online'] = True
def import_nodeinfo(nodes, nodeinfos, now, assume_online=False):
2015-03-24 16:49:37 +01:00
for nodeinfo in filter(lambda d: 'node_id' in d, nodeinfos):
node = nodes.setdefault(nodeinfo['node_id'], {'flags': dict()})
node['nodeinfo'] = nodeinfo
node['flags']['online'] = False
node['flags']['gateway'] = False
if assume_online:
mark_online(node, now)
def reset_statistics(nodes):
2015-03-24 16:49:37 +01:00
for node in nodes.values():
node['statistics'] = {'clients': 0}
def import_statistics(nodes, stats):
2015-03-24 16:49:37 +01:00
def add(node, statistics, target, source, f=lambda d: d):
try:
node['statistics'][target] = f(reduce(dict.__getitem__,
source,
statistics))
except (KeyError, TypeError, ZeroDivisionError):
2015-03-24 16:49:37 +01:00
pass
macs = build_mac_table(nodes)
stats = filter(lambda d: 'node_id' in d, stats)
stats = filter(lambda d: d['node_id'] in nodes, stats)
for node, stats in map(lambda d: (nodes[d['node_id']], d), stats):
add(node, stats, 'clients', ['clients', 'total'])
add(node, stats, 'gateway', ['gateway'], lambda d: macs.get(d, d))
add(node, stats, 'uptime', ['uptime'])
add(node, stats, 'loadavg', ['loadavg'])
add(node, stats, 'memory_usage', ['memory'],
lambda d: 1 - d['free'] / d['total'])
add(node, stats, 'rootfs_usage', ['rootfs_usage'])
2015-06-07 23:52:32 +02:00
add(node, stats, 'traffic', ['traffic'])
2015-03-24 16:49:37 +01:00
def import_mesh_ifs_vis_data(nodes, vis_data):
2015-03-24 16:49:37 +01:00
macs = build_mac_table(nodes)
mesh_ifs = defaultdict(lambda: set())
for line in filter(lambda d: 'secondary' in d, vis_data):
primary = line['of']
mesh_ifs[primary].add(primary)
mesh_ifs[primary].add(line['secondary'])
2015-03-24 16:49:37 +01:00
def if_to_node(ifs):
a = filter(lambda d: d in macs, ifs)
a = map(lambda d: nodes[macs[d]], a)
try:
return next(a), ifs
except StopIteration:
return None
2015-03-24 16:49:37 +01:00
mesh_nodes = filter(lambda d: d, map(if_to_node, mesh_ifs.values()))
2015-03-24 16:49:37 +01:00
for v in mesh_nodes:
node = v[0]
2015-05-09 21:54:54 +02:00
ifs = set()
try:
ifs = ifs.union(set(node['nodeinfo']['network']['mesh_interfaces']))
except KeyError:
pass
try:
ifs = ifs.union(set(node['nodeinfo']['network']['mesh']['bat0']['interfaces']['wireless']))
except KeyError:
pass
2015-03-24 16:49:37 +01:00
try:
2015-05-09 21:54:54 +02:00
ifs = ifs.union(set(node['nodeinfo']['network']['mesh']['bat0']['interfaces']['tunnel']))
2015-03-24 16:49:37 +01:00
except KeyError:
2015-05-09 21:54:54 +02:00
pass
try:
ifs = ifs.union(set(node['nodeinfo']['network']['mesh']['bat0']['interfaces']['other']))
except KeyError:
pass
2015-03-21 15:17:50 +01:00
2015-05-09 21:54:54 +02:00
node['nodeinfo']['network']['mesh_interfaces'] = list(ifs | v[1])
2015-03-21 15:17:50 +01:00
def import_vis_clientcount(nodes, vis_data):
2015-03-24 16:49:37 +01:00
macs = build_mac_table(nodes)
data = filter(lambda d: d.get('label', None) == 'TT', vis_data)
data = filter(lambda d: d['router'] in macs, data)
data = map(lambda d: macs[d['router']], data)
for node_id, clientcount in Counter(data).items():
nodes[node_id]['statistics'].setdefault('clients', clientcount)
def mark_gateways(nodes, gateways):
2015-03-24 16:49:37 +01:00
macs = build_mac_table(nodes)
gateways = filter(lambda d: d in macs, gateways)
for node in map(lambda d: nodes[macs[d]], gateways):
node['flags']['gateway'] = True
def mark_vis_data_online(nodes, vis_data, now):
2015-03-24 16:49:37 +01:00
macs = build_mac_table(nodes)
online = set()
for line in vis_data:
if 'primary' in line:
online.add(line['primary'])
elif 'secondary' in line:
online.add(line['secondary'])
elif 'gateway' in line:
# This matches clients' MACs.
# On pre-Gluon nodes the primary MAC will be one of it.
online.add(line['gateway'])
for mac in filter(lambda d: d in macs, online):
mark_online(nodes[macs[mac]], now)