statistics.py 4.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174
  1. #!/usr/bin/env python3
  2. import socket
  3. import re
  4. import sys
  5. import json
  6. from lib.respondd import Respondd
  7. import lib.helper
  8. class Statistics(Respondd):
  9. def __init__(self, config):
  10. Respondd.__init__(self, config)
  11. def getClients(self):
  12. ret = {'total': 0, 'wifi': 0}
  13. macBridge = lib.helper.getInterfaceMAC(self._config['bridge'])
  14. lines = lib.helper.call(['batctl', '-m', self._config['batman'], 'tl', '-n'])
  15. for line in lines:
  16. # batman-adv -> translation-table.c -> batadv_tt_local_seq_print_text
  17. # R = BATADV_TT_CLIENT_ROAM
  18. # P = BATADV_TT_CLIENT_NOPURGE
  19. # N = BATADV_TT_CLIENT_NEW
  20. # X = BATADV_TT_CLIENT_PENDING
  21. # W = BATADV_TT_CLIENT_WIFI
  22. # I = BATADV_TT_CLIENT_ISOLA
  23. # . = unset
  24. # * c0:11:73:b2:8f:dd -1 [.P..W.] 1.710 (0xe680a836)
  25. #d4:3d:7e:34:5c:b1 -1 [.P....] 0.000 (0x12a02817)
  26. lineMatch = re.match(r'^[\s*]*([0-9a-f:]+)\s+-\d\s\[([RPNXWI\.]+)\]', line, re.I)
  27. if lineMatch:
  28. mac = lineMatch.group(1)
  29. flags = lineMatch.group(2)
  30. if macBridge != mac and flags[0] != 'R': # Filter bridge and roaming clients
  31. if not mac.startswith('33:33:') and not mac.startswith('01:00:5e:'): # Filter Multicast
  32. ret['total'] += 1
  33. if flags[4] == 'W':
  34. ret['wifi'] += 1
  35. return ret
  36. def getTraffic(self):
  37. traffic = {}
  38. lines = lib.helper.call(['ethtool', '-S', self._config['batman']])
  39. if len(lines) == 0:
  40. return {}
  41. for line in lines[1:]:
  42. lineSplit = line.strip().split(':', 1)
  43. name = lineSplit[0]
  44. value = lineSplit[1].strip()
  45. traffic[name] = value
  46. ret = {
  47. 'tx': {
  48. 'packets': traffic['tx'],
  49. 'bytes': traffic['tx_bytes'],
  50. 'dropped': traffic['tx_dropped'],
  51. },
  52. 'rx': {
  53. 'packets': traffic['rx'],
  54. 'bytes': traffic['rx_bytes'],
  55. },
  56. 'forward': {
  57. 'packets': traffic['forward'],
  58. 'bytes': traffic['forward_bytes'],
  59. },
  60. 'mgmt_rx': {
  61. 'packets': traffic['mgmt_rx'],
  62. 'bytes': traffic['mgmt_rx_bytes'],
  63. },
  64. 'mgmt_tx': {
  65. 'packets': traffic['mgmt_tx'],
  66. 'bytes': traffic['mgmt_tx_bytes'],
  67. },
  68. }
  69. return ret
  70. @staticmethod
  71. def getMemory():
  72. ret = {}
  73. lines = open('/proc/meminfo').readlines()
  74. for line in lines:
  75. lineSplit = line.split(' ', 1)
  76. name = lineSplit[0][:-1]
  77. value = lineSplit[1].strip().split(' ', 1)[0]
  78. if name == 'MemTotal':
  79. ret['total'] = value
  80. elif name == 'MemFree':
  81. ret['free'] = value
  82. elif name == 'Buffers':
  83. ret['buffers'] = value
  84. elif name == 'Cached':
  85. ret['cached'] = value
  86. return ret
  87. def getFastd(self):
  88. dataFastd = b''
  89. try:
  90. sock = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
  91. sock.connect(self._config['fastd_socket'])
  92. except socket.error as err:
  93. print('socket error: ', sys.stderr, err)
  94. return None
  95. while True:
  96. data = sock.recv(1024)
  97. if not data:
  98. break
  99. dataFastd += data
  100. sock.close()
  101. return json.loads(dataFastd.decode('utf-8'))
  102. def getMeshVPNPeers(self):
  103. ret = {}
  104. if 'fastd_socket' in self._config:
  105. fastd = self.getFastd()
  106. for peer in fastd['peers'].values():
  107. if peer['connection']:
  108. ret[peer['name']] = {
  109. 'established': peer['connection']['established']
  110. }
  111. else:
  112. ret[peer['name']] = None
  113. return ret
  114. else:
  115. return None
  116. def getGateway(self):
  117. ret = None
  118. lines = lib.helper.call(['batctl', '-m', self._config['batman'], 'gwl', '-n'])
  119. for line in lines:
  120. lineMatch = re.match(r'^(\*|=>) +([0-9a-f:]+) \([\d ]+\) ([0-9a-f:]+)', line)
  121. if lineMatch:
  122. ret = {}
  123. ret['gateway'] = lineMatch.group(2)
  124. ret['gateway_nexthop'] = lineMatch.group(3)
  125. return ret
  126. def _get(self):
  127. ret = {
  128. 'clients': self.getClients(),
  129. 'traffic': self.getTraffic(),
  130. 'idletime': float(open('/proc/uptime').read().split(' ')[1]),
  131. 'loadavg': float(open('/proc/loadavg').read().split(' ')[0]),
  132. 'memory': self.getMemory(),
  133. 'processes': dict(zip(('running', 'total'), map(int, open('/proc/loadavg').read().split(' ')[3].split('/')))),
  134. 'uptime': float(open('/proc/uptime').read().split(' ')[0]),
  135. 'mesh_vpn' : { # HopGlass-Server: node.flags.uplink = parsePeerGroup(_.get(n, 'statistics.mesh_vpn'))
  136. 'groups': {
  137. 'backbone': {
  138. 'peers': self.getMeshVPNPeers()
  139. }
  140. }
  141. }
  142. }
  143. gateway = self.getGateway()
  144. if gateway != None:
  145. ret = lib.helper.merge(ret, gateway)
  146. return ret