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