batman.py 3.0 KB

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