batman.py 3.0 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798
  1. #!/usr/bin/python
  2. # -*- coding: utf-8 -*-
  3. from __future__ import print_function
  4. import io
  5. import json
  6. import string
  7. import subprocess
  8. import time
  9. import ffstatus
  10. from .exceptions import SanityCheckError
  11. class BatmanParser:
  12. batadv_vis = 'batadv-vis'
  13. mactranslation = string.maketrans('2367abef', '014589cd')
  14. def __str__(self):
  15. return 'BatmanParser \'{0}\''.format(self.batadv_vis)
  16. def sanitycheck(self):
  17. """Checks that batadv-vis is executable and gives sane output."""
  18. testdata = None
  19. try:
  20. cmd = [self.batadv_vis, '-f', 'jsondoc']
  21. testdata = subprocess.check_output(cmd)
  22. except Exception as err:
  23. raise SanityCheckError(
  24. self, "batadv-vis not found or incompatible", err)
  25. try:
  26. json.loads(testdata)
  27. except Exception as err:
  28. raise SanityCheckError(
  29. self, "batadv-vis does not return valid JSON data", err)
  30. return True
  31. def fetch(self, batadv_dump=None):
  32. """Fetches the current data from batadv-vis."""
  33. data = {}
  34. timestamp = int(time.time())
  35. # call batadv-vis and parse output as unicode JSON
  36. rawdata = subprocess.check_output([self.batadv_vis, '-f', 'jsondoc'])
  37. rawdata = unicode(rawdata, encoding='latin1', errors='replace')
  38. batmandata = json.loads(rawdata)
  39. # dump raw data into file if requested
  40. if batadv_dump is not None:
  41. dumpfile = io.open(batadv_dump, 'w')
  42. dumpfile.write(rawdata)
  43. dumpfile.close()
  44. # parse raw data, convert all MAC into nodeid
  45. for item in batmandata['vis']:
  46. itemid = ffstatus.mac2id(item['primary'])
  47. aliases = []
  48. if 'secondary' in item:
  49. for mac in item['secondary']:
  50. aliases.append(ffstatus.mac2id(mac))
  51. neighbours = {}
  52. if 'neighbors' in item:
  53. for neighbour in item['neighbors']:
  54. #if neighbour['router'] != item['primary']:
  55. # print('node {0}\'s neighbor {1} has unexpected router {2}'.format(itemid, neighbour['neighbor'], neighbour['router']))
  56. metric = float(neighbour['metric'])
  57. neighbours[neighbour['neighbor']] = metric
  58. # construct dict entry as expected by BATCAVE
  59. data[itemid] = {
  60. 'aliases': aliases,
  61. 'neighbours': neighbours,
  62. 'clients': [x for x in item.get('clients', [])],
  63. '__UPDATED__': {'batadv': timestamp},
  64. '__RAW__': {'batadv': {itemid: item}},
  65. }
  66. return data
  67. # standalone test mode
  68. if __name__ == "__main__":
  69. parser = BatmanParser()
  70. try:
  71. parser.sanitycheck()
  72. except SanityCheckError as err:
  73. print('SANITY-CHECK failed:', str(err))
  74. import sys
  75. sys.exit(1)
  76. bdata = parser.fetch()
  77. print(json.dumps(bdata))