From b143e3f2e5b062072d611ef99199c6e627163cd5 Mon Sep 17 00:00:00 2001 From: Martin Weinelt Date: Wed, 25 Mar 2015 13:27:54 +0100 Subject: [PATCH 1/3] batman: add batadv-vis socket support, needs at least alfred 2014.4.0 --- backend.py | 2 +- lib/batman.py | 9 ++++++--- 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/backend.py b/backend.py index 3eaeaf1..173b127 100755 --- a/backend.py +++ b/backend.py @@ -50,7 +50,7 @@ def main(params): bm = list(map(lambda d: (d.vis_data(True), d.gateway_list()), - map(Batman, params['mesh']))) + map(Batman, params['mesh'], params['alfred_sock']))) for vis_data, gateway_list in bm: nodes.import_mesh_ifs_vis_data(nodedb['nodes'], vis_data) nodes.import_vis_clientcount(nodedb['nodes'], vis_data) diff --git a/lib/batman.py b/lib/batman.py index ddd0bd5..a4aeca2 100644 --- a/lib/batman.py +++ b/lib/batman.py @@ -8,8 +8,9 @@ class Batman(object): Bindings for B.A.T.M.A.N. Advanced commandline interface "batctl" """ - def __init__(self, mesh_interface='bat0'): + def __init__(self, mesh_interface='bat0', alfred_sockpath=None): self.mesh_interface = mesh_interface + self.alfred_sock = alfred_sockpath def vis_data(self, batadv_vis=False): vds = self.vis_data_batctl_legacy() @@ -44,8 +45,10 @@ class Batman(object): Parse "batadv-vis -i -f json" into an array of dictionaries. """ - output = subprocess.check_output( - ['batadv-vis', '-i', self.mesh_interface, '-f', 'json']) + cmd = ['batadv-vis', '-i', self.mesh_interface, '-f', 'json'] + if self.alfred_sock: + cmd.extend(['-u', self.alfred_sock]) + output = subprocess.check_output(cmd) lines = output.splitlines() return self.vis_data_helper(lines) From 8d4856db56a6d7b1cc13e865c489f745579628b7 Mon Sep 17 00:00:00 2001 From: Martin Weinelt Date: Wed, 25 Mar 2015 14:11:00 +0100 Subject: [PATCH 2/3] lib/batman: refactor gateway handling --- lib/batman.py | 38 ++++++++++++++++++++------------------ 1 file changed, 20 insertions(+), 18 deletions(-) diff --git a/lib/batman.py b/lib/batman.py index a4aeca2..4f6b943 100644 --- a/lib/batman.py +++ b/lib/batman.py @@ -12,6 +12,9 @@ class Batman(object): self.mesh_interface = mesh_interface self.alfred_sock = alfred_sockpath + # compile regular expressions only once on startup + self.mac_addr_pattern = re.compile(r'(([a-z0-9]{2}:){5}[a-z0-9]{2})') + def vis_data(self, batadv_vis=False): vds = self.vis_data_batctl_legacy() if batadv_vis: @@ -60,36 +63,35 @@ class Batman(object): output = subprocess.check_output( ['batctl', '-m', self.mesh_interface, 'gwl', '-n']) output_utf8 = output.decode('utf-8') - lines = output_utf8.splitlines() - - own_mac = re.match(r"^.*MainIF/MAC: [^/]+/([0-9a-f:]+).*$", - lines[0]).group(1) + rows = output_utf8.splitlines() gateways = [] - gw_mode = self.gateway_mode() - if gw_mode['mode'] == 'server': - gateways.append(own_mac) - for line in lines: - gw_line = re.match(r"^(?:=>)? +([0-9a-f:]+) ", line) - if gw_line: - gateways.append(gw_line.group(1)) + # local gateway + header = rows.pop(0) + mode, bandwidth = self.gateway_mode() + if mode == 'server': + local_gw_mac = self.mac_addr_pattern.search(header).group(0) + gateways.append(local_gw_mac) + + # remote gateway(s) + for row in rows: + match = self.mac_addr_pattern.search(row) + if match: + gateways.append(match.group(1)) return gateways def gateway_mode(self): """ Parse "batctl -m gw" + return: tuple mode, bandwidth, if mode != server then bandwidth is None """ output = subprocess.check_output( ['batctl', '-m', self.mesh_interface, 'gw']) - elements = output.decode("utf-8").split() - mode = elements[0] - if mode == 'server': - return {'mode': 'server', - 'bandwidth': elements[3]} - else: - return {'mode': mode} + chunks = output.decode("utf-8").split() + + return chunks[0], chunks[3] if 3 in chunks else None if __name__ == "__main__": bc = Batman() From eb26ea9a5f7920b8ea132ebec6bdf26765b2bf1b Mon Sep 17 00:00:00 2001 From: Martin Weinelt Date: Wed, 25 Mar 2015 14:33:54 +0100 Subject: [PATCH 3/3] pep8: fix remaining line length issues and update travis to include lib/*.py --- .travis.yml | 2 +- lib/NodeRRD.py | 6 ++++-- lib/RRD.py | 23 +++++++++++++++-------- lib/graph.py | 29 +++++++++++++++++++---------- lib/nodes.py | 25 ++++++++++++++----------- lib/rrddb.py | 9 ++++++--- 6 files changed, 59 insertions(+), 35 deletions(-) diff --git a/.travis.yml b/.travis.yml index cb081fa..dcaceb4 100644 --- a/.travis.yml +++ b/.travis.yml @@ -3,4 +3,4 @@ language: python python: - "3.4" install: "pip install pep8" -script: "pep8 *.py" +script: "pep8 *.py lib/*.py" diff --git a/lib/NodeRRD.py b/lib/NodeRRD.py index 37bc6f9..afabe6f 100644 --- a/lib/NodeRRD.py +++ b/lib/NodeRRD.py @@ -32,11 +32,13 @@ class NodeRRD(RRD): @property def imagename(self): - return "{basename}.png".format(basename=os.path.basename(self.filename).rsplit('.', 2)[0]) + return "{basename}.png".format( + basename=os.path.basename(self.filename).rsplit('.', 2)[0]) # TODO: fix this, python does not support function overloading def update(self): - super().update({'upstate': int(self.node['flags']['online']), 'clients': self.node['statistics']['clients']}) + super().update({'upstate': int(self.node['flags']['online']), + 'clients': self.node['statistics']['clients']}) def graph(self, directory, timeframe): """ diff --git a/lib/RRD.py b/lib/RRD.py index 799338c..3c406ac 100644 --- a/lib/RRD.py +++ b/lib/RRD.py @@ -83,9 +83,11 @@ class RRD(object): info = self.info() if set(ds_list) - set(info['ds'].values()) != set(): for ds in ds_list: - if ds.name in info['ds'] and ds.type != info['ds'][ds.name].type: - raise RRDIncompatibleException("%s is %s but should be %s" % - (ds.name, ds.type, info['ds'][ds.name].type)) + if ds.name in info['ds'] and\ + ds.type != info['ds'][ds.name].type: + raise RRDIncompatibleException( + "{} is {} but should be {}".format( + ds.name, ds.type, info['ds'][ds.name].type)) else: raise RRDOutdatedException() @@ -108,8 +110,10 @@ class RRD(object): if ds.name in info['ds']: old_ds = info['ds'][ds.name] if info['ds'][ds.name].type != ds.type: - raise RuntimeError('Cannot convert existing DS "%s" from type "%s" to "%s"' % - (ds.name, old_ds.type, ds.type)) + raise RuntimeError( + "Cannot convert existing DS '{}'" + "from type '{}' to '{}'".format( + ds.name, old_ds.type, ds.type)) ds.index = old_ds.index new_ds[ds.index] = ds else: @@ -237,7 +241,8 @@ class RRD(object): for line in out.splitlines(): base = info for match in self._info_regex.finditer(line): - section, key, name, value = match.group("section", "key", "name", "value") + section, key, name, value = match.group( + "section", "key", "name", "value") if section and key: try: key = int(key) @@ -258,7 +263,8 @@ class RRD(object): base[name] = value dss = {} for name, ds in info['ds'].items(): - ds_obj = DS(name, ds['type'], ds['minimal_heartbeat'], ds['min'], ds['max']) + ds_obj = DS(name, ds['type'], ds['minimal_heartbeat'], + ds['min'], ds['max']) ds_obj.index = ds['index'] ds_obj.last_ds = ds['last_ds'] ds_obj.value = ds['value'] @@ -267,7 +273,8 @@ class RRD(object): info['ds'] = dss rras = [] for rra in info['rra'].values(): - rras.append(RRA(rra['cf'], rra['xff'], rra['pdp_per_row'], rra['rows'])) + rras.append(RRA(rra['cf'], rra['xff'], + rra['pdp_per_row'], rra['rows'])) info['rra'] = rras self._cached_info = info return info diff --git a/lib/graph.py b/lib/graph.py index d5163ff..7b45828 100644 --- a/lib/graph.py +++ b/lib/graph.py @@ -8,12 +8,17 @@ from lib.nodes import build_mac_table def import_vis_data(graph, nodes, vis_data): macs = build_mac_table(nodes) - nodes_a = map(lambda d: 2*[d['primary']], filter(lambda d: 'primary' in d, vis_data)) - nodes_b = map(lambda d: [d['secondary'], d['of']], filter(lambda d: 'secondary' in d, vis_data)) - graph.add_nodes_from(map(lambda a, b: (a, dict(primary=b, node_id=macs.get(b))), *zip(*chain(nodes_a, nodes_b)))) + nodes_a = map(lambda d: 2*[d['primary']], + filter(lambda d: 'primary' in d, vis_data)) + nodes_b = map(lambda d: [d['secondary'], d['of']], + filter(lambda d: 'secondary' in d, vis_data)) + graph.add_nodes_from(map(lambda a, b: + (a, dict(primary=b, node_id=macs.get(b))), + *zip(*chain(nodes_a, nodes_b)))) edges = filter(lambda d: 'neighbor' in d, vis_data) - graph.add_edges_from(map(lambda d: (d['router'], d['neighbor'], dict(tq=float(d['label']))), edges)) + graph.add_edges_from(map(lambda d: (d['router'], d['neighbor'], + dict(tq=float(d['label']))), edges)) def mark_vpn(graph, vpn_macs): @@ -32,11 +37,13 @@ def to_multigraph(graph): return node['primary'] if node else a def map_node(node, data): - return (data['primary'], dict(node_id=data['node_id'])) if data else (node, dict()) + return (data['primary'], + dict(node_id=data['node_id'])) if data else (node, dict()) digraph = nx.MultiDiGraph() digraph.add_nodes_from(map(map_node, *zip(*graph.nodes_iter(data=True)))) - digraph.add_edges_from(map(lambda a, b, data: (f(a), f(b), data), *zip(*graph.edges_iter(data=True)))) + digraph.add_edges_from(map(lambda a, b, data: (f(a), f(b), data), + *zip(*graph.edges_iter(data=True)))) return digraph @@ -50,8 +57,9 @@ def merge_nodes(graph): multigraph = to_multigraph(graph) digraph = nx.DiGraph() digraph.add_nodes_from(multigraph.nodes_iter(data=True)) - edges = chain.from_iterable([[(e, d, merge_edges(multigraph[e][d].values())) - for d in multigraph[e]] for e in multigraph]) + edges = chain.from_iterable([[(e, d, merge_edges( + multigraph[e][d].values())) + for d in multigraph[e]] for e in multigraph]) digraph.add_edges_from(edges) return digraph @@ -69,8 +77,9 @@ def to_undirected(graph): graph = nx.Graph() graph.add_nodes_from(multigraph.nodes_iter(data=True)) - edges = chain.from_iterable([[(e, d, merge_edges(multigraph[e][d].values())) - for d in multigraph[e]] for e in multigraph]) + edges = chain.from_iterable([[(e, d, merge_edges( + multigraph[e][d].values())) + for d in multigraph[e]] for e in multigraph]) graph.add_edges_from(edges) return graph diff --git a/lib/nodes.py b/lib/nodes.py index 23a2b0e..bb1e129 100644 --- a/lib/nodes.py +++ b/lib/nodes.py @@ -53,23 +53,26 @@ def reset_statistics(nodes): node['statistics'] = {'clients': 0} -def import_statistics(nodes, statistics): +def import_statistics(nodes, stats): def add(node, statistics, target, source, f=lambda d: d): try: - node['statistics'][target] = f(reduce(dict.__getitem__, source, statistics)) + node['statistics'][target] = f(reduce(dict.__getitem__, + source, + statistics)) except (KeyError, TypeError): pass macs = build_mac_table(nodes) - statistics = filter(lambda d: 'node_id' in d, statistics) - statistics = filter(lambda d: d['node_id'] in nodes, statistics) - for node, statistics in map(lambda d: (nodes[d['node_id']], d), statistics): - add(node, statistics, 'clients', ['clients', 'total']) - add(node, statistics, 'gateway', ['gateway'], lambda d: macs.get(d, d)) - add(node, statistics, 'uptime', ['uptime']) - add(node, statistics, 'loadavg', ['loadavg']) - add(node, statistics, 'memory_usage', ['memory'], lambda d: 1 - d['free'] / d['total']) - add(node, statistics, 'rootfs_usage', ['rootfs_usage']) + 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']) def import_mesh_ifs_vis_data(nodes, vis_data): diff --git a/lib/rrddb.py b/lib/rrddb.py index bcd33b3..57437a7 100644 --- a/lib/rrddb.py +++ b/lib/rrddb.py @@ -28,8 +28,10 @@ class RRD(object): os.mkdir(self.imagePath) def update_database(self, nodes): - online_nodes = dict(filter(lambda d: d[1]['flags']['online'], nodes.items())) - client_count = sum(map(lambda d: d['statistics']['clients'], online_nodes.values())) + online_nodes = dict(filter( + lambda d: d[1]['flags']['online'], nodes.items())) + client_count = sum(map( + lambda d: d['statistics']['clients'], online_nodes.values())) self.globalDb.update(len(online_nodes), client_count) for node_id, node in online_nodes.items(): @@ -37,7 +39,8 @@ class RRD(object): rrd.update() def update_images(self): - self.globalDb.graph(os.path.join(self.imagePath, "globalGraph.png"), self.displayTimeGlobal) + self.globalDb.graph(os.path.join(self.imagePath, "globalGraph.png"), + self.displayTimeGlobal) nodedb_files = os.listdir(self.dbPath)