Projet

Général

Profil

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

root / plugins / mysql / mysql_audit @ c6f88968

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

1
#!/usr/bin/env python3
2
# -*- python -*-
3

    
4
"""
5
=head1 NAME
6

    
7
mysql_audit - Plugin to monitor the MySQL audit log connection count
8

    
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
=head1 AUTHORS
45

    
46
Copyright (C) 2017 Bert Van de Poel <bert@bhack.net>
47

    
48
Copyright (C) 2019 pcy <pcy@ulyssis.org>
49

    
50
=head1 MAGIC MARKERS
51

    
52
 #%# family=auto
53
 #%# capabilities=autoconf
54

    
55
=cut
56
"""
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
        split = line.split(',')
128
        key = split[2]
129
        date = datetime.strptime(split[0], '%Y%m%d %H:%M:%S')
130
        # 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()