Projet

Général

Profil

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

root / plugins / mysql / mysql_audit @ 852aa41a

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

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

    
4
"""
5
=head1 INTRODUCTION
6

    
7
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 HISTORY
45

    
46
2017-11-03: v 1.0 Bert Van de Poel <bert@bhack.net>: created
47
2020-07-19: v 1.1 pcy <pcy@ulyssis.org>: added config options
48

    
49
=head1 USAGE
50

    
51
Parameters understood:
52

    
53
  config   (required)
54
  autoconf (optional - used by munin-config)
55

    
56
=head1 MAGIC MARKERS
57

    
58
#%# family=auto
59
#%# capabilities=autoconf
60
"""
61

    
62

    
63
from datetime import datetime, timedelta, timezone
64
import operator
65
import os
66
import struct
67
import sys
68

    
69

    
70
def weakbool(x):
71
    return x.lower().strip() in {'true', 'yes', 'y', '1'}
72

    
73

    
74
logfile = os.getenv('logfile', '/var/log/mysql/audit.log')
75
toplist = int(os.getenv('toplist', '0'))
76
sortlist = weakbool(os.getenv('sorted', 'N')) or toplist > 0
77

    
78
STATEFILE = os.getenv('MUNIN_STATEFILE')
79

    
80

    
81
def loadstate():
82
    if not os.path.isfile(STATEFILE):
83
        return None
84

    
85
    with open(STATEFILE, 'rb') as f:
86
        tstamp = struct.unpack('d', f.read())[0]
87
        return datetime.fromtimestamp(tstamp, tz=timezone.utc)
88

    
89

    
90
def savestate(state):
91
    with open(STATEFILE, 'wb') as f:
92
        f.write(struct.pack('d', state.timestamp()))
93

    
94

    
95
def reverse_lines(filename, BUFSIZE=4096):
96
    with open(filename, "r") as f:
97
        f.seek(0, 2)
98
        p = f.tell()
99
        remainder = ""
100
        while True:
101
            sz = min(BUFSIZE, p)
102
            p -= sz
103
            f.seek(p)
104
            buf = f.read(sz) + remainder
105
            if '\n' not in buf:
106
                remainder = buf
107
            else:
108
                i = buf.index('\n')
109
                for L in buf[i + 1:].split("\n")[::-1]:
110
                    yield L
111
                remainder = buf[:i]
112
            if p == 0:
113
                break
114
        yield remainder
115

    
116

    
117
def get_data(do_save=True):
118
    occurrences = {}
119
    begin = datetime.now(timezone.utc)
120
    begin_local = datetime.now()
121

    
122
    state = loadstate()
123
    if state is None:
124
        # need to do something here to prevent reading indefinitely
125
        state = begin - timedelta(minutes=5)
126

    
127
    for line in reverse_lines(logfile):
128
        if ',CONNECT,' not in line:
129
            continue
130

    
131
        splitted = line.split(',')
132
        key = splitted[2]
133
        date = datetime.strptime(splitted[0], '%Y%m%d %H:%M:%S')
134
        # hack to add timezone data to the datetime
135
        date = begin + (date - begin_local)
136

    
137
        if date < state:
138
            break
139

    
140
        occurrences[key] = occurrences.get(key, 0) + 1
141

    
142
    if do_save:
143
        savestate(begin)
144

    
145
    return occurrences
146

    
147

    
148
def autoconf():
149
    print("no (logfile not found)" if os.path.isfile(logfile) else "yes")
150

    
151

    
152
def configure():
153
    print('graph_title MySQL Audit connect count')
154
    print('graph_vlabel Connections')
155
    print('graph_category mysql')
156

    
157
    occurrences = get_data(False)
158
    occitems = occurrences.items()
159
    occitems = sorted(occitems, key=operator.itemgetter(1 if sortlist else 0),
160
                      reverse=sortlist)
161
    if toplist > 0:
162
        occitems = occitems[:toplist]
163

    
164
    for key, value in occitems:
165
        print('{}.label {}'.format(key.lower(), key))
166
        print('{}.type GAUGE'.format(key.lower()))
167
        print('{}.draw AREASTACK'.format(key.lower()))
168

    
169

    
170
def fetch():
171
    occurrences = get_data()
172
    occitems = occurrences.items()
173
    occitems = sorted(occitems, key=operator.itemgetter(1 if sortlist else 0),
174
                      reverse=sortlist)
175
    if toplist > 0:
176
        occitems = occitems[:toplist]
177

    
178
    for key, value in occitems:
179
        print('{}.value {}'.format(key, value))
180

    
181

    
182
if len(sys.argv) == 2 and sys.argv[1] == "autoconf":
183
    autoconf()
184
elif len(sys.argv) == 2 and sys.argv[1] == "config":
185
    configure()
186
else:
187
    fetch()