First commit new Samba python plugin for smb2 uses smbd -P 1 profiling
Christopher Cox committed
Apr 16, 2017 at 15:32 UTC
02386eb5518e9045da22eb4bf5a59c455b842f9c
1 file changed
+200
python.d/Samba.chart.py
new
+200
@@ -0,0 +1,200 @@
1
+# -*- coding: utf-8 -*-
2
+# Description: samba netdata python.d module
3
+# Author: Christopher Cox <chris_cox@endlessnow.com>
4
+#
5
+# The netdata user needs to be able to be able to sudo the smbstatus program without password
6
+# netdata ALL=(ALL) NOPASSWD: /usr/bin/smbstatus -P
7
+#
8
+# This makes calls to smbstatus -P (note smbd needs to be run with the -P 1 option) a lot.
9
+#
10
+# Right now this just looks for the smb2_ counters, but adjust the regex to get what you need.
11
+#
12
+# The first chart makes a bad assumption that somehow readout/readin and writeout/writein
13
+# have something to do with actual reads and writes. While it seems to be mostly true for reads,
14
+# I am not sure what the write values are. Perhaps they shouldn't be paired?
15
+#
16
+# create and close are paired, maybe they shouldn't be?
17
+#
18
+# getinfo and setinfo are paired, maybe they shouldn't be?
19
+#
20
+# The Other Smb2 chart is merely a display of current counter values. They didn't seem to change
21
+# much to me. However, if you notice something changing a lot there, bring one or more out into its own
22
+# chart and make it incremental (like find and notify... good examples).
23
+
24
+
25
+from base import SimpleService
26
+from re import compile
27
+from subprocess import Popen, PIPE
28
+
29
+# default module values (can be overridden per job in `config`)
30
+# update_every = 2
31
+priority = 60000
32
+retries = 60
33
+
34
+ORDER = ['smb2_rw','smb2_create_close','smb2_info','smb2_find','smb2_notify','smb2_sm_count']
35
+
36
+# The weird dummy1, etc. is for color selection.
37
+
38
+CHARTS = {
39
+ 'smb2_rw': {
40
+ 'lines': [
41
+ ['smb2_read_outbytes', 'readout', 'incremental', 1, 1024],
42
+ ['smb2_write_inbytes', 'writein', 'incremental', -1, 1024],
43
+ ['smb2_read_inbytes', 'readin', 'incremental', 1, 1024],
44
+ ['smb2_write_outbytes', 'writeout', 'incremental', -1, 1024]
45
+ ],
46
+ 'options': [None, 'R/Ws', 'kilobytes/s', 'Smb2', 'smb2.readwrite', 'area']
47
+ },
48
+ 'smb2_create_close': {
49
+ 'lines': [
50
+ ['dummy1', ' ', 'incremental', 1, 1],
51
+ ['dummy2', ' ', 'incremental', 1, 1],
52
+ ['smb2_create_count', 'create', 'incremental', 1, 1],
53
+ ['smb2_close_count', 'close', 'incremental', -1, 1]
54
+ ],
55
+ 'options': [None, 'Create/Close', 'operations/s', 'Smb2', 'smb2.create_close', 'area']
56
+ },
57
+ 'smb2_info': {
58
+ 'lines': [
59
+ ['dummy1', ' ', 'incremental', 1, 1],
60
+ ['dummy2', ' ', 'incremental', 1, 1],
61
+ ['dummy3', ' ', 'incremental', 1, 1],
62
+ ['dummy4', ' ', 'incremental', 1, 1],
63
+ ['smb2_getinfo_count', 'getinfo', 'incremental', 1, 1],
64
+ ['smb2_setinfo_count', 'setinfo', 'incremental', -1, 1]
65
+ ],
66
+ 'options': [None, 'Info', 'operations/s', 'Smb2', 'smb2.get_set_info', 'area']
67
+ },
68
+ 'smb2_find': {
69
+ 'lines': [
70
+ ['dummy1', ' ', 'incremental', 1, 1],
71
+ ['dummy2', ' ', 'incremental', 1, 1],
72
+ ['dummy3', ' ', 'incremental', 1, 1],
73
+ ['dummy4', ' ', 'incremental', 1, 1],
74
+ ['dummy5', ' ', 'incremental', 1, 1],
75
+ ['dummy6', ' ', 'incremental', 1, 1],
76
+ ['smb2_find_count', 'find', 'incremental', 1, 1]
77
+ ],
78
+ 'options': [None, 'Find', 'operations/s', 'Smb2', 'smb2.find', 'area']
79
+ },
80
+ 'smb2_notify': {
81
+ 'lines': [
82
+ ['dummy1', ' ', 'incremental', 1, 1],
83
+ ['dummy2', ' ', 'incremental', 1, 1],
84
+ ['dummy3', ' ', 'incremental', 1, 1],
85
+ ['dummy4', ' ', 'incremental', 1, 1],
86
+ ['dummy5', ' ', 'incremental', 1, 1],
87
+ ['dummy6', ' ', 'incremental', 1, 1],
88
+ ['dummy7', ' ', 'incremental', 1, 1],
89
+ ['smb2_notify_count', 'notify', 'incremental', 1, 1]
90
+ ],
91
+ 'options': [None, 'Notify', 'operations/s', 'Smb2', 'smb2.notify', 'area']
92
+ },
93
+ 'smb2_sm_count': {
94
+ 'lines': [
95
+ ['smb2_tcon_count', 'tcon', 'absolute', 1, 1],
96
+ ['smb2_negprot_count', 'negprot', 'absolute', 1, 1],
97
+ ['smb2_tdis_count', 'tdis', 'absolute', 1, 1],
98
+ ['smb2_cancel_count', 'cancel', 'absolute', 1, 1],
99
+ ['smb2_logoff_count', 'logoff', 'absolute', 1, 1],
100
+ ['smb2_flush_count', 'flush', 'absolute', 1, 1],
101
+ ['smb2_lock_count', 'lock', 'absolute', 1, 1],
102
+ ['smb2_keepalive_count', 'keepalive', 'absolute', 1, 1],
103
+ ['smb2_break_count', 'break', 'absolute', 1, 1],
104
+ ['smb2_sessetup_count', 'sessetup', 'absolute', 1, 1]
105
+ ],
106
+ 'options': [None, 'Lesser Ops', 'count', 'Other Smb2', 'smb2.sm_counters', 'stacked']
107
+ }
108
+ }
109
+
110
+
111
+class Service(SimpleService):
112
+ def __init__(self, configuration=None, name=None):
113
+ SimpleService.__init__(self, configuration=configuration, name=name)
114
+ self.smbstatus = self.find_binary('smbstatus')
115
+ self.rgx_smb2 = compile(r'(smb2_[^:]+):\s+(\d+)')
116
+ self.cache_prev = list()
117
+
118
+ def check(self):
119
+ # Cant start without 'smbstatus' command
120
+ if not self.smbstatus:
121
+ self.error('Can\'t locate \'smbstatus\' binary or binary is not executable by netdata')
122
+ return False
123
+
124
+ # If command is present and we can execute it we need to make sure..
125
+ # 1. STDOUT is not empty
126
+ reply = self._get_raw_data()
127
+ if not reply:
128
+ self.error('No output from \'smbstatus\' (not enough privileges?)')
129
+ return False
130
+ self.error(reply)
131
+
132
+ # 2. Output is parsable (list is not empty after regex findall)
133
+ is_parsable = self.rgx_smb2.findall(reply)
134
+ if not is_parsable:
135
+ self.error('Cant parse output...')
136
+ return False
137
+
138
+ # We are about to start!
139
+ self.create_charts()
140
+
141
+ self.info('Plugin was started successfully')
142
+ return True
143
+
144
+ def _get_raw_data(self):
145
+ try:
146
+ reply = Popen(['/usr/bin/sudo', self.smbstatus, '-P'], stdout=PIPE, stderr=PIPE, shell=False)
147
+ except OSError:
148
+ return None
149
+
150
+ raw_data = reply.communicate()[0]
151
+
152
+ if not raw_data:
153
+ return None
154
+
155
+ return raw_data.decode()
156
+
157
+ def _get_data(self):
158
+ """
159
+ Format data received from shell command
160
+ :return: dict
161
+ """
162
+ raw_data = self._get_raw_data()
163
+ data_all = self.rgx_smb2.findall(raw_data)
164
+
165
+ if not data_all:
166
+ return None
167
+
168
+ # 1. ALL data from 'smbstatus -P'.
169
+ to_netdata = dict([(k, int(v)) for k, v in data_all])
170
+ # Add dummy values that are always zero (forcing color selection in charts)
171
+ to_netdata['dummy1'] = 0
172
+ to_netdata['dummy2'] = 0
173
+ to_netdata['dummy3'] = 0
174
+ to_netdata['dummy4'] = 0
175
+ to_netdata['dummy5'] = 0
176
+ to_netdata['dummy6'] = 0
177
+ to_netdata['dummy7'] = 0
178
+ to_netdata['dummy8'] = 0
179
+
180
+ # Ready steady go!
181
+ return to_netdata
182
+
183
+ def create_charts(self):
184
+ # If 'all_charts' is true...ALL charts are displayed. If no only default + 'extra_charts'
185
+ #if self.configuration.get('all_charts'):
186
+ # self.order = EXTRA_ORDER
187
+ #else:
188
+ # try:
189
+ # extra_charts = list(filter(lambda chart: chart in EXTRA_ORDER, self.extra_charts.split()))
190
+ # except (AttributeError, NameError, ValueError):
191
+ # self.error('Extra charts disabled.')
192
+ # extra_charts = []
193
+
194
+ self.order = ORDER[:]
195
+ #self.order.extend(extra_charts)
196
+
197
+ # Create static charts
198
+ #self.definitions = {chart: values for chart, values in CHARTS.items() if chart in self.order}
199
+ self.definitions = CHARTS
200
+