statistics.py 4.8 KB

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