ovpn_status_log.chart.py 3.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136
  1. # -*- coding: utf-8 -*-
  2. # Description: openvpn status log netdata python.d module
  3. # Author: ilyam8
  4. # SPDX-License-Identifier: GPL-3.0-or-later
  5. import re
  6. from bases.FrameworkServices.SimpleService import SimpleService
  7. update_every = 10
  8. ORDER = [
  9. 'users',
  10. 'traffic',
  11. ]
  12. CHARTS = {
  13. 'users': {
  14. 'options': [None, 'OpenVPN Active Users', 'active users', 'users', 'openvpn_status.users', 'line'],
  15. 'lines': [
  16. ['users', None, 'absolute'],
  17. ]
  18. },
  19. 'traffic': {
  20. 'options': [None, 'OpenVPN Traffic', 'KiB/s', 'traffic', 'openvpn_status.traffic', 'area'],
  21. 'lines': [
  22. ['bytes_in', 'in', 'incremental', 1, 1 << 10],
  23. ['bytes_out', 'out', 'incremental', -1, 1 << 10]
  24. ]
  25. }
  26. }
  27. TLS_REGEX = re.compile(
  28. r'(?:[0-9a-f]+:[0-9a-f:]+|(?:\d{1,3}(?:\.\d{1,3}){3}(?::\d+)?)) (?P<bytes_in>\d+) (?P<bytes_out>\d+)'
  29. )
  30. STATIC_KEY_REGEX = re.compile(
  31. r'TCP/[A-Z]+ (?P<direction>(?:read|write)) bytes,(?P<bytes>\d+)'
  32. )
  33. class Service(SimpleService):
  34. def __init__(self, configuration=None, name=None):
  35. SimpleService.__init__(self, configuration=configuration, name=name)
  36. self.order = ORDER
  37. self.definitions = CHARTS
  38. self.log_path = self.configuration.get('log_path')
  39. self.regex = {
  40. 'tls': TLS_REGEX,
  41. 'static_key': STATIC_KEY_REGEX
  42. }
  43. def check(self):
  44. if not (self.log_path and isinstance(self.log_path, str)):
  45. self.error("'log_path' is not defined")
  46. return False
  47. data = self._get_raw_data()
  48. if not data:
  49. self.error('Make sure that the openvpn status log file exists and netdata has permission to read it')
  50. return None
  51. found = None
  52. for row in data:
  53. if 'ROUTING' in row:
  54. self.get_data = self.get_data_tls
  55. found = True
  56. break
  57. elif 'STATISTICS' in row:
  58. self.get_data = self.get_data_static_key
  59. found = True
  60. break
  61. if found:
  62. return True
  63. self.error('Failed to parse openvpn log file')
  64. return False
  65. def _get_raw_data(self):
  66. """
  67. Open log file
  68. :return: str
  69. """
  70. try:
  71. with open(self.log_path) as log:
  72. raw_data = log.readlines() or None
  73. except OSError:
  74. return None
  75. else:
  76. return raw_data
  77. def get_data_static_key(self):
  78. """
  79. Parse openvpn-status log file.
  80. """
  81. raw_data = self._get_raw_data()
  82. if not raw_data:
  83. return None
  84. data = dict(bytes_in=0, bytes_out=0)
  85. for row in raw_data:
  86. match = self.regex['static_key'].search(row)
  87. if match:
  88. match = match.groupdict()
  89. if match['direction'] == 'read':
  90. data['bytes_in'] += int(match['bytes'])
  91. else:
  92. data['bytes_out'] += int(match['bytes'])
  93. return data or None
  94. def get_data_tls(self):
  95. """
  96. Parse openvpn-status log file.
  97. """
  98. raw_data = self._get_raw_data()
  99. if not raw_data:
  100. return None
  101. data = dict(users=0, bytes_in=0, bytes_out=0)
  102. for row in raw_data:
  103. columns = row.split(',') if ',' in row else row.split()
  104. if 'UNDEF' in columns:
  105. # see https://openvpn.net/archive/openvpn-users/2004-08/msg00116.html
  106. continue
  107. match = self.regex['tls'].search(' '.join(columns))
  108. if match:
  109. match = match.groupdict()
  110. data['users'] += 1
  111. data['bytes_in'] += int(match['bytes_in'])
  112. data['bytes_out'] += int(match['bytes_out'])
  113. return data or None