alfred.py 5.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162
  1. #!/usr/bin/python
  2. # -*- coding: utf-8 -*-
  3. from __future__ import print_function
  4. import io
  5. import json
  6. import subprocess
  7. import time
  8. from .exceptions import SanityCheckError
  9. class AlfredParser:
  10. alfred_json = 'alfred-json'
  11. alfred_datatypes = [('static', 158), ('dynamic', 159)]
  12. def __str__(self):
  13. types = ['{1}=>{0}'.format(x[0], x[1]) for x in self.alfred_datatypes]
  14. return 'AlfredParser \'{0}\' {1}'.format(
  15. self.alfred_json,
  16. str.join(' ', types),
  17. )
  18. def sanitycheck(self):
  19. """
  20. Checks that the necessary tool 'alfred-json' is callable
  21. and outputs syntactically correct JSON data.
  22. """
  23. testdata = None
  24. try:
  25. first_datatype = int(self.alfred_datatypes[0][1])
  26. cmd = [self.alfred_json, '-z', '-r', str(first_datatype)]
  27. testdata = subprocess.check_output(cmd)
  28. except Exception as err:
  29. raise SanityCheckError(
  30. self, "alfred-json not found or incompatible", err)
  31. try:
  32. json.loads(testdata)
  33. except Exception as err:
  34. raise SanityCheckError(
  35. self, "alfred-json does not return valid JSON data", err)
  36. return True
  37. def fetch(self, alfred_dump=None):
  38. """Fetches the current data from alfred-json."""
  39. data = {}
  40. timestamp = int(time.time())
  41. alfreddata = {}
  42. for datatype in self.alfred_datatypes:
  43. # call 'alfred-json' and fetch the output
  44. cmd = [self.alfred_json, '-z', '-r', str(int(datatype[1]))]
  45. rawdata = subprocess.check_output(cmd)
  46. # convert raw input into unicode
  47. rawdata = unicode(rawdata, encoding='latin1', errors='replace')
  48. # parse it as JSON
  49. newdata = json.loads(rawdata)
  50. for item in newdata:
  51. if not item in alfreddata:
  52. alfreddata[item] = {}
  53. alfreddata[item][datatype[0]] = newdata[item]
  54. if alfred_dump is not None:
  55. jsondata = json.dumps(alfreddata, ensure_ascii=False)
  56. dumpfile = io.open(alfred_dump, 'w')
  57. dumpfile.write(jsondata)
  58. dumpfile.close()
  59. for alfredid in alfreddata:
  60. alfredinfo = alfreddata[alfredid]
  61. nodestatic = alfredinfo.get('static', {})
  62. if len(nodestatic) == 0:
  63. print('Warning: ALFRED data for {id} does not contain static data.'.format(id=alfredid))
  64. myid = nodestatic.get('node_id', alfredid.lower().replace(':', ''))
  65. nodeinfo = {
  66. 'hostname': None,
  67. 'mac': None,
  68. 'software': {},
  69. 'statistics': {},
  70. '__UPDATED__': {'alfred': timestamp},
  71. '__RAW__': {'alfred': alfredinfo},
  72. }
  73. data[myid] = nodeinfo
  74. if 'hostname' in nodestatic:
  75. nodeinfo['hostname'] = nodestatic['hostname']
  76. if 'network' in nodestatic:
  77. if 'mac' in nodestatic['network']:
  78. nodeinfo['mac'] = nodestatic['network']['mac']
  79. if 'mesh_interfaces' in nodestatic['network']:
  80. macs = [x for x in nodestatic['network']['mesh_interfaces']]
  81. nodeinfo['macs'] = macs
  82. elif 'mesh' in nodestatic['network']:
  83. ifaces = nodestatic['network']['mesh']
  84. iftypes = [iftype for iftype in [ifaces[ifname]['interfaces'] for ifname in ifaces]]
  85. tmp = [x for x in iftypes]
  86. macs = []
  87. for x in tmp:
  88. for iftype in x:
  89. for mac in x[iftype]:
  90. macs.append(mac)
  91. nodeinfo['macs'] = macs
  92. else:
  93. nodeinfo['macs'] = []
  94. if 'hardware' in nodestatic:
  95. hardware = nodestatic['hardware']
  96. nodeinfo['hardware'] = hardware.get('model')
  97. if 'software' in nodestatic:
  98. software = nodestatic['software']
  99. nodeinfo['software']['firmware'] = \
  100. software.get('firmware', {}).get('release')
  101. autoupdater = software.get('autoupdater', {})
  102. autoupdater_enabled = autoupdater.get('enabled', False)
  103. nodeinfo['software']['autoupdater'] = \
  104. autoupdater.get('branch') if autoupdater_enabled else 'off'
  105. nodedyn = alfredinfo.get('dynamic',
  106. nodestatic.get('statistics', {}))
  107. if 'uptime' in nodedyn:
  108. nodeinfo['uptime'] = int(float(nodedyn['uptime']))
  109. if 'gateway' in nodedyn:
  110. nodeinfo['gateway'] = nodedyn['gateway']
  111. traffic = nodedyn["traffic"] if "traffic" in nodedyn else None
  112. if traffic is not None:
  113. if not 'traffic' in nodeinfo['statistics']:
  114. nodeinfo['statistics']['traffic'] = {}
  115. item = nodeinfo['statistics']['traffic']
  116. item['rxbytes'] = int(traffic["rx"]["bytes"])
  117. item['txbytes'] = int(traffic["tx"]["bytes"])
  118. if 'location' in nodestatic:
  119. nodeinfo['location'] = nodestatic['location']
  120. return data
  121. if __name__ == "__main__":
  122. parser = AlfredParser()
  123. try:
  124. parser.sanitycheck()
  125. except SanityCheckError as err:
  126. print('SANITY-CHECK failed:', str(err))
  127. import sys
  128. sys.exit(1)
  129. adata = parser.fetch()
  130. print(json.dumps(adata))