Projet

Général

Profil

Paste
Télécharger au format
Statistiques
| Branche: | Révision:

root / plugins / mysql / mysql_audit @ 95003946

Historique | Voir | Annoter | Télécharger (4,28 ko)

1 10ca1256 pcy
#!/usr/bin/env python3
2
# -*- python -*-
3
4
"""
5 c6f88968 Lars Kruse
=head1 NAME
6 10ca1256 pcy
7 c6f88968 Lars Kruse
mysql_audit - Plugin to monitor the MySQL audit log connection count
8 10ca1256 pcy
9
=head1 APPLICABLE SYSTEMS
10
11
MySQL needs to be configured manually in order to enable the audit log. This
12
can be done as follows:
13
14
=over 2
15
16
      plugin-load = server_audit=server_audit.so
17
      server_audit_events = connect
18
      server_audit_output_type=file
19
      server_audit_file_path = /var/log/mysql/audit.log
20
      server_audit_file_rotate_size = 512M
21
      server_audit_logging = ON
22
      server_audit_file_rotations = 5
23
24
=back
25
26
=head1 INSTALLATION
27
28
Place in /etc/munin/plugins/ (or link it there using ln -s)
29
30
=head1 CONFIGURATION
31
32
Add this to your /etc/munin/plugin-conf.d/munin-node:
33
34
=over 2
35
36
      [mysql_audit]
37
      user mysql
38
      env.logfile /var/log/mysql/audit.log
39
      env.sorted 1 # sort output (if not: unsorted)
40
      env.toplist 10 # only the top 10 (if unset: all of them). implies sorted
41
42
=back
43
44 c6f88968 Lars Kruse
=head1 AUTHORS
45 10ca1256 pcy
46 c6f88968 Lars Kruse
Copyright (C) 2017 Bert Van de Poel <bert@bhack.net>
47 10ca1256 pcy
48 c6f88968 Lars Kruse
Copyright (C) 2019 pcy <pcy@ulyssis.org>
49 10ca1256 pcy
50
=head1 MAGIC MARKERS
51
52 c6f88968 Lars Kruse
 #%# family=auto
53
 #%# capabilities=autoconf
54
55
=cut
56 10ca1256 pcy
"""
57
58
59
from datetime import datetime, timedelta, timezone
60
import operator
61
import os
62
import struct
63
import sys
64
65
66
def weakbool(x):
67
    return x.lower().strip() in {'true', 'yes', 'y', '1'}
68
69
70
logfile = os.getenv('logfile', '/var/log/mysql/audit.log')
71
toplist = int(os.getenv('toplist', '0'))
72
sortlist = weakbool(os.getenv('sorted', 'N')) or toplist > 0
73
74
STATEFILE = os.getenv('MUNIN_STATEFILE')
75
76
77
def loadstate():
78
    if not os.path.isfile(STATEFILE):
79
        return None
80
81
    with open(STATEFILE, 'rb') as f:
82
        tstamp = struct.unpack('d', f.read())[0]
83
        return datetime.fromtimestamp(tstamp, tz=timezone.utc)
84
85
86
def savestate(state):
87
    with open(STATEFILE, 'wb') as f:
88
        f.write(struct.pack('d', state.timestamp()))
89
90
91
def reverse_lines(filename, BUFSIZE=4096):
92
    with open(filename, "r") as f:
93
        f.seek(0, 2)
94
        p = f.tell()
95
        remainder = ""
96
        while True:
97
            sz = min(BUFSIZE, p)
98
            p -= sz
99
            f.seek(p)
100
            buf = f.read(sz) + remainder
101
            if '\n' not in buf:
102
                remainder = buf
103
            else:
104
                i = buf.index('\n')
105
                for L in buf[i + 1:].split("\n")[::-1]:
106
                    yield L
107
                remainder = buf[:i]
108
            if p == 0:
109
                break
110
        yield remainder
111
112
113
def get_data(do_save=True):
114
    occurrences = {}
115
    begin = datetime.now(timezone.utc)
116
    begin_local = datetime.now()
117
118
    state = loadstate()
119
    if state is None:
120
        # need to do something here to prevent reading indefinitely
121
        state = begin - timedelta(minutes=5)
122
123
    for line in reverse_lines(logfile):
124
        if ',CONNECT,' not in line:
125
            continue
126
127 36748826 Lars Kruse
        split = line.split(',')
128
        key = split[2]
129
        date = datetime.strptime(split[0], '%Y%m%d %H:%M:%S')
130 10ca1256 pcy
        # hack to add timezone data to the datetime
131
        date = begin + (date - begin_local)
132
133
        if date < state:
134
            break
135
136
        occurrences[key] = occurrences.get(key, 0) + 1
137
138
    if do_save:
139
        savestate(begin)
140
141
    return occurrences
142
143
144
def autoconf():
145
    print("no (logfile not found)" if os.path.isfile(logfile) else "yes")
146
147
148
def configure():
149
    print('graph_title MySQL Audit connect count')
150
    print('graph_vlabel Connections')
151
    print('graph_category mysql')
152
153
    occurrences = get_data(False)
154
    occitems = occurrences.items()
155
    occitems = sorted(occitems, key=operator.itemgetter(1 if sortlist else 0),
156
                      reverse=sortlist)
157
    if toplist > 0:
158
        occitems = occitems[:toplist]
159
160
    for key, value in occitems:
161
        print('{}.label {}'.format(key.lower(), key))
162
        print('{}.type GAUGE'.format(key.lower()))
163
        print('{}.draw AREASTACK'.format(key.lower()))
164
165
166
def fetch():
167
    occurrences = get_data()
168
    occitems = occurrences.items()
169
    occitems = sorted(occitems, key=operator.itemgetter(1 if sortlist else 0),
170
                      reverse=sortlist)
171
    if toplist > 0:
172
        occitems = occitems[:toplist]
173
174
    for key, value in occitems:
175
        print('{}.value {}'.format(key, value))
176
177
178
if len(sys.argv) == 2 and sys.argv[1] == "autoconf":
179
    autoconf()
180
elif len(sys.argv) == 2 and sys.argv[1] == "config":
181
    configure()
182
else:
183
    fetch()