@cryptotaxi247 / netdata-1 / commits / 4444588a4

fail2ban_plugin: can read config files from "conf_dir" now

Ilya committed Feb 17, 2017 at 22:01 UTC 4444588a4e190b391945058b07a191525c23134e
1 file changed +51 -27
python.d/fail2ban.chart.py
+51 -27
@@ -4,16 +4,18 @@
4
5 from base import LogService
6 from re import compile
7 +
8 try:
9 from itertools import filterfalse
10 except ImportError:
11 from itertools import ifilterfalse as filterfalse
12 from os import access as is_accessible, R_OK
13 +from os.path import isdir
14 +from glob import glob
15
16 priority = 60000
17 retries = 60
15 -regex = compile(r'([A-Za-z-]+\]) enabled = ([a-z]+)')
16 -
18 +REGEX = compile(r'\[([A-Za-z-]+)][^\[\]]*? enabled = true')
19 ORDER = ['jails_group']
20
21
@@ -23,22 +25,17 @@ class Service(LogService):
25 self.order = ORDER
26 self.log_path = self.configuration.get('log_path', '/var/log/fail2ban.log')
27 self.conf_path = self.configuration.get('conf_path', '/etc/fail2ban/jail.local')
26 - self.default_jails = ['ssh']
28 + self.conf_dir = self.configuration.get('conf_dir', '')
29 try:
30 self.exclude = self.configuration['exclude'].split()
31 except (KeyError, AttributeError):
32 self.exclude = []
31 -
33
34 def _get_data(self):
35 """
36 Parse new log lines
37 :return: dict
38 """
38 -
39 - # If _get_raw_data returns empty list (no new lines in log file) we will send to Netdata this
40 - self.data = {jail: 0 for jail in self.jails_list}
41 -
39 try:
40 raw = self._get_raw_data()
41 if raw is None:
@@ -50,42 +47,69 @@ class Service(LogService):
47
48 # Fail2ban logs looks like
49 # 2016-12-25 12:36:04,711 fail2ban.actions[2455]: WARNING [ssh] Ban 178.156.32.231
53 - self.data = dict(
50 + data = dict(
51 zip(
52 self.jails_list,
53 [len(list(filterfalse(lambda line: (jail + '] Ban') not in line, raw))) for jail in self.jails_list]
54 ))
55
56 + for jail in data:
57 + self.data[jail] += data[jail]
58 +
59 return self.data
60
61 def check(self):
62 -
62 +
63 # Check "log_path" is accessible.
64 # If NOT STOP plugin
65 if not is_accessible(self.log_path, R_OK):
66 - self.error('Cannot access file %s' % (self.log_path))
66 + self.error('Cannot access file %s' % self.log_path)
67 return False
68 + if not isdir(self.conf_dir):
69 + self.conf_dir = None
70
69 - # Check "conf_path" is accessible.
70 - # If "conf_path" is accesible try to parse it to find enabled jails
71 - if is_accessible(self.conf_path, R_OK):
72 - with open(self.conf_path, 'rt') as jails_conf:
73 - jails_list = regex.findall(' '.join(jails_conf.read().split()))
74 - self.jails_list = [jail[:-1] for jail, status in jails_list if status == 'true']
71 + # If "conf_dir" not specified (or not a dir) plugin will use "conf_path"
72 + if not self.conf_dir:
73 + if is_accessible(self.conf_path, R_OK):
74 + with open(self.conf_path, 'rt') as jails_conf:
75 + jails_list = REGEX.findall(' '.join(jails_conf.read().split()))
76 + self.jails_list = jails_list
77 + else:
78 + self.jails_list = list()
79 + self.error('Cannot access jail configuration file %s.' % self.conf_path)
80 + # If "conf_dir" is specified and "conf_dir" is dir plugin will use "conf_dir"
81 else:
76 - self.jails_list = []
77 - self.error('Cannot access jail.local file %s.' % (self.conf_path))
78 -
82 + dot_local = glob(self.conf_dir + '/*.local') # *.local jail configurations files
83 + dot_conf = glob(self.conf_dir + '/*.conf') # *.conf jail configuration files
84 +
85 + if not any([dot_local, dot_conf]):
86 + self.error('%s is empty or not readable' % self.conf_dir)
87 + # According "man jail.conf" files could be *.local AND *.conf
88 + # *.conf files parsed first. Changes in *.local overrides configuration in *.conf
89 + if dot_conf:
90 + dot_local.extend([conf for conf in dot_conf if conf[:-5] not in [local[:-6] for local in dot_local]])
91 + # Make sure all files are readable
92 + dot_local = [conf for conf in dot_local if is_accessible(conf, R_OK)]
93 + if dot_local:
94 + enabled_jails = list()
95 + for jail_conf in dot_local:
96 + with open(jail_conf, 'rt') as conf:
97 + enabled_jails.extend(REGEX.findall(' '.join(conf.read().split())))
98 + self.jails_list = list(set(enabled_jails))
99 + else:
100 + self.jails_list = list()
101 + self.error('Files in %s not readable' % self.conf_dir)
102 +
103 # If for some reason parse failed we still can START with default jails_list.
80 - self.jails_list = [jail for jail in self.jails_list if jail not in self.exclude]\
81 - if self.jails_list else self.default_jails
104 + self.jails_list = list(set(self.jails_list) - set(self.exclude)) or ['ssh']
105 + self.data = dict([(jail, 0) for jail in self.jails_list])
106 self.create_dimensions()
83 - self.info('Plugin succefully started. Jails: %s' % (self.jails_list))
107 + self.info('Plugin successfully started. Jails: %s' % self.jails_list)
108 return True
109
110 def create_dimensions(self):
87 - self.definitions = {'jails_group':
88 - {'options':
89 - [None, "Jails ban statistics", "bans/s", 'Jails', 'jail.ban', 'line'], 'lines': []}}
111 + self.definitions = {
112 + 'jails_group': {'options': [None, "Jails ban statistics", "bans/s", 'Jails', 'jail.ban', 'line'],
113 + 'lines': []}}
114 for jail in self.jails_list:
91 - self.definitions['jails_group']['lines'].append([jail, jail, 'absolute'])
115 + self.definitions['jails_group']['lines'].append([jail, jail, 'incremental'])