summaryrefslogtreecommitdiffstats
path: root/collectors/python.d.plugin/ovpn_status_log/ovpn_status_log.chart.py
blob: dc7a6002e48510b9d59234be9f3aab08b5bcd39b (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
# -*- coding: utf-8 -*-
# Description: openvpn status log netdata python.d module
# Author: l2isbad
# SPDX-License-Identifier: GPL-3.0-or-later

import re

from bases.FrameworkServices.SimpleService import SimpleService


update_every = 10

ORDER = [
    'users',
    'traffic',
]

CHARTS = {
    'users': {
        'options': [None, 'OpenVPN Active Users', 'active users', 'users', 'openvpn_status.users', 'line'],
        'lines': [
            ['users', None, 'absolute'],
        ]
    },
    'traffic': {
        'options': [None, 'OpenVPN Traffic', 'KiB/s', 'traffic', 'openvpn_status.traffic', 'area'],
        'lines': [
            ['bytes_in', 'in', 'incremental', 1, 1 << 10],
            ['bytes_out', 'out', 'incremental', -1, 1 << 10]
        ]
    }
}

TLS_REGEX = re.compile(
    r'(?:[0-9a-f]+:[0-9a-f:]+|(?:\d{1,3}(?:\.\d{1,3}){3}(?::\d+)?)) (?P<bytes_in>\d+) (?P<bytes_out>\d+)'
)
STATIC_KEY_REGEX = re.compile(
    r'TCP/[A-Z]+ (?P<direction>(?:read|write)) bytes,(?P<bytes>\d+)'
)


class Service(SimpleService):
    def __init__(self, configuration=None, name=None):
        SimpleService.__init__(self, configuration=configuration, name=name)
        self.order = ORDER
        self.definitions = CHARTS
        self.log_path = self.configuration.get('log_path')
        self.regex = {
            'tls': TLS_REGEX,
            'static_key': STATIC_KEY_REGEX
        }

    def check(self):
        if not (self.log_path and isinstance(self.log_path, str)):
            self.error("'log_path' is not defined")
            return False

        data = self._get_raw_data()
        if not data:
            self.error('Make sure that the openvpn status log file exists and netdata has permission to read it')
            return None

        found = None
        for row in data:
            if 'ROUTING' in row:
                self.get_data = self.get_data_tls
                found = True
                break
            elif 'STATISTICS' in row:
                self.get_data = self.get_data_static_key
                found = True
                break
        if found:
            return True
        self.error('Failed to parse ovpenvpn log file')
        return False

    def _get_raw_data(self):
        """
        Open log file
        :return: str
        """

        try:
            with open(self.log_path) as log:
                raw_data = log.readlines() or None
        except OSError:
            return None
        else:
            return raw_data

    def get_data_static_key(self):
        """
        Parse openvpn-status log file.
        """

        raw_data = self._get_raw_data()
        if not raw_data:
            return None

        data = dict(bytes_in=0, bytes_out=0)

        for row in raw_data:
            match = self.regex['static_key'].search(row)
            if match:
                match = match.groupdict()
                if match['direction'] == 'read':
                    data['bytes_in'] += int(match['bytes'])
                else:
                    data['bytes_out'] += int(match['bytes'])

        return data or None

    def get_data_tls(self):
        """
        Parse openvpn-status log file.
        """

        raw_data = self._get_raw_data()
        if not raw_data:
            return None

        data = dict(users=0, bytes_in=0, bytes_out=0)
        for row in raw_data:
            columns = row.split(',') if ',' in row else row.split()
            if 'UNDEF' in columns:
                # see https://openvpn.net/archive/openvpn-users/2004-08/msg00116.html
                continue

            match = self.regex['tls'].search(' '.join(columns))
            if match:
                match = match.groupdict()
                data['users'] += 1
                data['bytes_in'] += int(match['bytes_in'])
                data['bytes_out'] += int(match['bytes_out'])

        return data or None