Projet

Général

Profil

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

root / plugins / disk / scsi_queue @ 17f78427

Historique | Voir | Annoter | Télécharger (8,22 ko)

1 db0871ee Troels Arvin
#!/usr/bin/env python
2
3
"""
4 17f78427 Lars Kruse
Munin plugin which reports queue busy-values per online SCSI
5 db0871ee Troels Arvin
device on Linux, as seen in /proc/scsi/sg/devices
6
7
If the busy-values often reach the queue depth of the device,
8
one might consider increasing the queue depth. Hence, this
9
plugin.
10
11
Wildcard use:
12
If your system has many SCSI-like devices, filtering may be needed
13
to make the resulting graphs readable.
14 17f78427 Lars Kruse
If you symlink the plugin, so that it's executed as
15 db0871ee Troels Arvin
  scsi_queue_X_through_Y
16
then the plugin will only look at devices
17
  /dev/sdX .. /dev/sdY
18
X and Y may only be one-character values.
19
X and Y are translated into a regular expression like:
20
  sd[X-Y]
21
"""
22
23
# Author: Troels Arvin <tra@sst.dk>
24
# See http://troels.arvin.dk/code/munin/ for latest version.
25
26
# Only tested with Red Hat Enterprise Linux 5 / CentOS 5, currently.
27
28
# Released according to the "New BSD License" AKA the 3-clause
29
# BSD License:
30 17f78427 Lars Kruse
# ====================================================================
31 db0871ee Troels Arvin
# Copyright (c) 2010, Danish National Board of Health.
32
# All rights reserved.
33
#
34
# Redistribution and use in source and binary forms, with or without
35
# modification, are permitted provided that the following conditions are met:
36
#     * Redistributions of source code must retain the above copyright
37
#       notice, this list of conditions and the following disclaimer.
38
#     * Redistributions in binary form must reproduce the above copyright
39
#       notice, this list of conditions and the following disclaimer in the
40
#       documentation and/or other materials provided with the distribution.
41
#     * Neither the name of the  the Danish National Board of Health nor the
42
#       names of its contributors may be used to endorse or promote products
43
#       derived from this software without specific prior written permission.
44
#
45
# THIS SOFTWARE IS PROVIDED BY the Danish National Board of Health ''AS IS'' AND ANY
46
# EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
47
# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
48
# DISCLAIMED. IN NO EVENT SHALL the Danish National Board of Health BE LIABLE FOR ANY
49
# DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
50
# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
51
# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
52
# ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
53
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
54
# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
55 17f78427 Lars Kruse
# ====================================================================
56 db0871ee Troels Arvin
57 4323adbf Troels Arvin
# $Id: scsi_queue 13630 2010-08-31 15:29:14Z tra $
58 db0871ee Troels Arvin
59
# Note to self:
60
# The fields in /proc/scsi/sg/devices are:
61
# host chan id lun type opens qdepth busy online
62
63
# TODO:
64
#  - Make it possible to group by multipath group. Might be
65
#    hard, though, because determining path groups seems
66
#    to require root privileges.
67
#  - Support autoconf
68
#  - How to support filtering on installations which have
69
#    many SCSI devices, beyond /dev/sdz?
70
71
import os, sys, re
72
73
procfile          = '/proc/scsi/sg/devices'
74
sysfs_base        = '/sys/bus/scsi/devices'
75
76
my_canonical_name = 'scsi_queue'         # If called as - e.g. - scsi_queue_foo, then
77
                                         # foo will be interpreted as a device filter.
78
                                         # For this, we need a base name.
79
80
def bailout(msg):
81
    sys.stderr.write(msg+"\n")
82
    sys.exit(1)
83
84
def print_config(devices,filter_from,filter_through):
85
86
    title_qualification = ''
87 4323adbf Troels Arvin
    if filter_from and filter_through:
88 db0871ee Troels Arvin
        title_qualification = ' for devices sd%s through sd%s' % (filter_from,filter_through)
89
90
    print 'graph_title SCSI queue busy values' + title_qualification
91
    print 'graph_vlabel busy count'
92
    print 'graph_args --base 1000 -l 0'
93
    print 'graph_category disk'
94
    print 'graph_info This graph shows the queue busy values, as seen in /prod/scsi/sg/devices'
95
96
    keys = devices.keys()
97
    keys.sort()
98
    for key in keys:
99
        qdepth = devices[key]['qdepth']
100
        print '%s.min 0' % key
101
        print '%s.type GAUGE' % key
102
        print '%s.label %s (%s %s); qdepth=%s' % (
103
            key,
104
            key,
105
            devices[key]['vendor'],
106
            devices[key]['model'],
107
            qdepth
108
        )
109
        print '%s.max %s' % (key,qdepth)
110
111
# Return a list of lists representing interesting parts from procfile
112
def parse_procfile():
113
    retval = []
114
    try:
115
        fh = open(procfile)
116
        for line in fh:
117
            retval.append(line.split())
118
119
    except IOError, e:
120
        bailout('IO error: '+str(e))
121
    return retval
122
123
# Try to read a file's content. If any I/O problem: return empty string
124
def readfile(path):
125
    try:
126
        f = open(path)
127
        retval = f.read().rstrip()
128
        f.close()
129
    except IOError, e:
130
        return ''
131
    return retval
132
133
# Return dict of dicts, indexed by device name
134
def map_procentries_to_devices(list_of_dicts,devfilter_regex):
135
    device_dict={}
136
137
    if devfilter_regex:
138
        regex_compiled = re.compile(devfilter_regex)
139
140
    for elem in list_of_dicts:
141
        # In /sys/bus/scsi/devices we see a number of directory
142
        # entries, such as:
143
        # 0:0:0:0
144
        # 2:0:0:0
145
        # 3:0:0:0
146
        #
147 17f78427 Lars Kruse
        # The colon-separated values map to the first four parts
148 db0871ee Troels Arvin
        # of /proc/scsi/sg/devices
149 17f78427 Lars Kruse
        # And the directory entries are symlinks which point to directories
150
        # in /sys/devices. By following a symlink, we may end up in
151 db0871ee Troels Arvin
        # a directory which contains directory entries like:
152
        #  - block:sdb
153
        # ...
154
        #  - model
155
        # ...
156
        #  - vendor
157
        sys_pathname = sysfs_base + '/' + ':'.join(elem[:4]) # isolate stuff like 2:0:0:0
158
159 fba800ae Veres Lajos
        # Should actually not happen, but nonetheless:
160 db0871ee Troels Arvin
        if not os.path.islink(sys_pathname):
161
            continue
162
163
        # Search for dirent called block:SOMETHING
164
        # Put SOMETHING into blockdev_name
165 17f78427 Lars Kruse
        # Couldn't make glob.glob() work: The length of the result
166 db0871ee Troels Arvin
        # of glob() returned TypeError: len() of unsized object on
167
        # RHEL 5's python...
168
        dirents = os.listdir(sys_pathname)
169
        num_blocklines=0
170
        for dirent in dirents:
171
            if dirent.startswith('block:'):
172
                block_line = dirent
173
                num_blocklines += 1
174
        if num_blocklines == 0:
175
            continue
176
        if num_blocklines > 1:
177
            bailout("Got more than one result when globbing for '%s'" % glob_for)
178
        blockdev_name = block_line.split(':')[1]
179
180
        # If device filtering is active, filter now
181
        if devfilter_regex:
182
            if not regex_compiled.match(blockdev_name):
183
                continue
184
185
        # Merge info from the /proc and /sys sources
186
        device_dict[blockdev_name] = {
187
            'model' : readfile(sys_pathname+'/model'),
188
            'vendor': readfile(sys_pathname+'/vendor'),
189
            'qdepth': elem[6],
190
            'busy'  : elem[7]
191
        }
192
    return device_dict
193
194
def print_values(devices):
195
    devnames = devices.keys()
196
    devnames.sort()
197
    retval = ''
198
    for devname in devnames:
199
        print "%s.value %s" % (
200
            devname,
201
            devices[devname]['busy']
202
        )
203
204
205
206
207
# Initial sanity check
208
n_args=len(sys.argv)
209
if n_args > 2:
210
    # At most one arg expected
211
    print '%d arguments given - expecting only one' % n_args
212
    sys.exit(1)
213
214
# See if we were called with a Munin wildcard-style 'arg0-argument'
215
# E.g., if called as scsi_queue_a_through_c, then consider only
216
# devices sda, sdb, sdc.
217
devfilter_regex = None
218
called_as = os.path.basename(sys.argv[0])
219
match = re.match(my_canonical_name+'_([^_])_through_([^_])', called_as)
220 4323adbf Troels Arvin
filter_from = None
221
filter_through = None
222 db0871ee Troels Arvin
if match:
223
    filter_from    = match.group(1)
224
    filter_through = match.group(2)
225
    devfilter_regex = 'sd['+filter_from+'-'+filter_through+']'
226
227
# Perform main piece of work
228
devices = map_procentries_to_devices(
229
    parse_procfile(),
230
    devfilter_regex
231
)
232
233
# See how we were called
234
if n_args == 2:
235
    # An argument was given, so let's not simply print
236
    # values.
237
    arg = sys.argv[1]
238
    if arg == 'config':
239
        print_config(devices,filter_from,filter_through)
240
        sys.exit(0)
241
    else:
242
        print "Unknown argument '%s'" % arg
243
        sys.exit(1)
244
245
# No arguments given; print values
246
print_values(devices)