|
@@ -4,6 +4,7 @@
|
|
|
from __future__ import print_function
|
|
|
import io
|
|
|
import json
|
|
|
+import re
|
|
|
import string
|
|
|
import subprocess
|
|
|
import time
|
|
@@ -14,13 +15,15 @@ from .exceptions import SanityCheckError
|
|
|
|
|
|
class BatmanParser:
|
|
|
batadv_vis = 'batadv-vis'
|
|
|
+ batctl = 'batctl'
|
|
|
mactranslation = string.maketrans('2367abef', '014589cd')
|
|
|
|
|
|
def __str__(self):
|
|
|
- return 'BatmanParser \'{0}\''.format(self.batadv_vis)
|
|
|
+ return 'BatmanParser using \'{0}\' and \'{1}\''.format(
|
|
|
+ self.batadv_vis, self.batctl)
|
|
|
|
|
|
def sanitycheck(self):
|
|
|
- """Checks that batadv-vis is executable and gives sane output."""
|
|
|
+ """Checks that programs are executable and give sane output."""
|
|
|
|
|
|
testdata = None
|
|
|
try:
|
|
@@ -36,6 +39,24 @@ class BatmanParser:
|
|
|
raise SanityCheckError(
|
|
|
self, "batadv-vis does not return valid JSON data", err)
|
|
|
|
|
|
+ try:
|
|
|
+ cmd = [self.batctl, 'vd', 'json', '-n']
|
|
|
+ testdata = subprocess.check_output(cmd)
|
|
|
+ except Exception as err:
|
|
|
+ raise SanityCheckError(
|
|
|
+ self, "batctl not found or incompatible", err)
|
|
|
+
|
|
|
+ lineno = 0
|
|
|
+ try:
|
|
|
+ for line in testdata.splitlines():
|
|
|
+ lineno += 1
|
|
|
+ json.loads(line)
|
|
|
+ except Exception as err:
|
|
|
+ raise SanityCheckError(
|
|
|
+ self,
|
|
|
+ "batctl does not return valid JSON data (line " + lineno + ")",
|
|
|
+ err)
|
|
|
+
|
|
|
return True
|
|
|
|
|
|
def fetch(self):
|
|
@@ -44,11 +65,61 @@ class BatmanParser:
|
|
|
data = {}
|
|
|
timestamp = int(time.time())
|
|
|
|
|
|
+ # call 'batctl vd json -n' and parse output line-wise as JSON
|
|
|
+ rawvisdata = subprocess.check_output([self.batctl, 'vd', 'json', '-n'])
|
|
|
+ visdata = [json.loads(line) for line in rawvisdata.splitlines()]
|
|
|
+
|
|
|
+ # call 'batctl gwl' and parse output lines
|
|
|
+ rawgwdata = subprocess.check_output([self.batctl, 'gwl'])
|
|
|
+ gateways = []
|
|
|
+ for l in rawgwdata.splitlines()[2:]:
|
|
|
+ m = re.search("^\s*(([a-fA-F0-9]{2}:){5}[a-fA-F0-9]{2})\s+", l)
|
|
|
+ if m is not None:
|
|
|
+ gateways.append(ffstatus.mac2id(m.group(1)))
|
|
|
+
|
|
|
# call batadv-vis and parse output as unicode JSON
|
|
|
rawdata = subprocess.check_output([self.batadv_vis, '-f', 'jsondoc'])
|
|
|
rawdata = unicode(rawdata, encoding='latin1', errors='replace')
|
|
|
batmandata = json.loads(rawdata)
|
|
|
|
|
|
+ # parse vis data
|
|
|
+ for item in visdata:
|
|
|
+ itemid = ffstatus.mac2id(item.get('primary', item.get('router')))
|
|
|
+ content = {
|
|
|
+ 'aliases': [],
|
|
|
+ 'neighbours': {},
|
|
|
+ '__UPDATED__': {'batctl': timestamp},
|
|
|
+ }
|
|
|
+
|
|
|
+ if itemid in gateways:
|
|
|
+ content['type'] = 'gateway'
|
|
|
+
|
|
|
+ neigh_id = ffstatus.mac2id(item.get('neighbor'))
|
|
|
+ if neigh_id is not None:
|
|
|
+ neigh_q = item.get('label', '0')
|
|
|
+ if neigh_q != 'TT':
|
|
|
+ neigh_q = float(neigh_q)
|
|
|
+ else:
|
|
|
+ neigh_q = None
|
|
|
+ content['neighbours'][neigh_id] = neigh_q
|
|
|
+
|
|
|
+ if itemid is None:
|
|
|
+ # assumption: this is a secondary
|
|
|
+ secid = ffstatus.mac2id(item.get('secondary'))
|
|
|
+ ofid = ffstatus.mac2id(item.get('of'))
|
|
|
+ if secid is None or ofid is None:
|
|
|
+ print('BATCTL-VD threw garbage: ' + json.dumps(item))
|
|
|
+ continue
|
|
|
+
|
|
|
+ itemid = ofid
|
|
|
+ content['aliases'].append(secid)
|
|
|
+
|
|
|
+ if itemid in data:
|
|
|
+ data[itemid] = ffstatus.dict_merge(
|
|
|
+ data[itemid], content, overwrite_lists=False)
|
|
|
+ else:
|
|
|
+ data[itemid] = content
|
|
|
+
|
|
|
# parse raw data, convert all MAC into nodeid
|
|
|
for item in batmandata['vis']:
|
|
|
itemid = ffstatus.mac2id(item['primary'])
|
|
@@ -67,7 +138,7 @@ class BatmanParser:
|
|
|
neighbours[neighbour['neighbor']] = metric
|
|
|
|
|
|
# construct dict entry as expected by BATCAVE
|
|
|
- data[itemid] = {
|
|
|
+ content = {
|
|
|
'aliases': aliases,
|
|
|
'neighbours': neighbours,
|
|
|
'clients': [x for x in item.get('clients', [])],
|
|
@@ -75,6 +146,12 @@ class BatmanParser:
|
|
|
'__RAW__': {'batadv': {itemid: item}},
|
|
|
}
|
|
|
|
|
|
+ if itemid in data:
|
|
|
+ data[itemid] = ffstatus.dict_merge(
|
|
|
+ data[itemid], content, overwrite_lists=False)
|
|
|
+ else:
|
|
|
+ data[itemid] = content
|
|
|
+
|
|
|
return data
|
|
|
|
|
|
# standalone test mode
|