This file is indexed.

/usr/share/check_mk/checks/multipath is in check-mk-server 1.2.6p12-1.

This file is owned by root:root, with mode 0o755.

The actual contents of the file can be viewed below.

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
#!/usr/bin/python
# -*- encoding: utf-8; py-indent-offset: 4 -*-
# +------------------------------------------------------------------+
# |             ____ _               _        __  __ _  __           |
# |            / ___| |__   ___  ___| | __   |  \/  | |/ /           |
# |           | |   | '_ \ / _ \/ __| |/ /   | |\/| | ' /            |
# |           | |___| | | |  __/ (__|   <    | |  | | . \            |
# |            \____|_| |_|\___|\___|_|\_\___|_|  |_|_|\_\           |
# |                                                                  |
# | Copyright Mathias Kettner 2014             mk@mathias-kettner.de |
# +------------------------------------------------------------------+
#
# This file is part of Check_MK.
# The official homepage is at http://mathias-kettner.de/check_mk.
#
# check_mk is free software;  you can redistribute it and/or modify it
# under the  terms of the  GNU General Public License  as published by
# the Free Software Foundation in version 2.  check_mk is  distributed
# in the hope that it will be useful, but WITHOUT ANY WARRANTY;  with-
# out even the implied warranty of  MERCHANTABILITY  or  FITNESS FOR A
# PARTICULAR PURPOSE. See the  GNU General Public License for more de-
# ails.  You should have  received  a copy of the  GNU  General Public
# License along with GNU Make; see the file  COPYING.  If  not,  write
# to the Free Software Foundation, Inc., 51 Franklin St,  Fifth Floor,
# Boston, MA 02110-1301 USA.

# Configuration for using alias instead of UUID
inventory_multipath_rules = []

# Output from multipath -l has the following possible formats:

# orabase.lun50 (360a9800043346937686f456f59386741) dm-15 NETAPP,LUN
# [size=25G][features=1 queue_if_no_path][hwhandler=0]
# \_ round-robin 0 [prio=0][active]
#  \_ 1:0:3:50 sdy 65:128 [active][undef]
#  \_ 3:0:3:50 sdz 65:144 [active][undef]

# An alias might not be defined. The out is then:
# 360a980004334644d654a364469555a76
# [size=300 GB][features="0"][hwhandler="0"]
# \_ round-robin 0 [active]
#  \_ 1:0:2:13 sdc 8:32  [active][ready]
#  \_ 3:0:2:13 sdl 8:176 [active][ready]

# Might also be local disks:
# SFUJITSU_MAW3073NC_DBL2P62003VT
# [size=68 GB][features="0"][hwhandler="0"]
# \_ round-robin 0 [active]
#  \_ 0:0:3:0  sdb 8:16  [active][ready]

# Some very special header line
# (No space between uuid and dm-* - strange...)
# 360a980004334644d654a316e65306e51dm-4 NETAPP,LUN
# [size=30G][features=1 queue_if_no_path][hwhandler=0]
# \_ round-robin 0 [prio=0][active]
#  \_ 1:0:2:50 sdg 8:96  [active][undef]
# \_ round-robin 0 [prio=0][enabled]
#  \_ 4:0:1:50 sdl 8:176 [active][undef]

# And another one:
# 1494554000000000052303250303700000000000000000000 dm-0 IET,VIRTUAL-DISK
# [size=70G][features=0][hwhandler=0][rw]
# \_ round-robin 0 [prio=-1][active]
#  \_ 3:0:0:0 sdb 8:16  [active][undef]
# \_ round-robin 0 [prio=-1][enabled]
#  \_ 4:0:0:0 sdc 8:32  [active][undef]

# Other output from other host:
# anzvol2 (36005076306ffc648000000000000510a) dm-15 IBM,2107900
# [size=100G][features=0][hwhandler=0]
# \_ round-robin 0 [prio=-6][active]
#  \_ 4:0:5:1  sdbf 67:144 [active][undef]
#  \_ 4:0:4:1  sdau 66:224 [active][undef]
#  \_ 4:0:3:1  sdaj 66:48  [active][undef]
#  \_ 3:0:5:1  sdy  65:128 [active][undef]
#  \_ 3:0:4:1  sdn  8:208  [active][undef]
#  \_ 3:0:3:1  sdc  8:32   [active][undef]
# anzvol1 (36005076306ffc6480000000000005005) dm-16 IBM,2107900
# [size=165G][features=0][hwhandler=0]
# \_ round-robin 0 [prio=-6][active]
#  \_ 4:0:5:0  sdbe 67:128 [active][undef]
#  \_ 4:0:4:0  sdat 66:208 [active][undef]
#  \_ 4:0:3:0  sdai 66:32  [active][undef]
#  \_ 3:0:5:0  sdx  65:112 [active][undef]
#  \_ 3:0:4:0  sdm  8:192  [active][undef]
#  \_ 3:0:3:0  sdb  8:16   [active][undef]

# And one other output (ID has not 33 times A-Z0-9):
# mpath1 (SIBM_____SwapA__________DA02BF71)
# [size=41 GB][features="0"][hwhandler="0"]
# \_ round-robin 0 [active]
#  \_ 1:0:1:0 sdd 8:48  [active]

# Recently I've seen this output >:-P
# 360080e500017bd72000002eb4c1b1ae8 dm-1 IBM,1814      FAStT
# size=350G features='1 queue_if_no_path' hwhandler='1 rdac' wp=rw
# `-+- policy='round-robin 0' prio=-1 status=active
#   |- 7:0:2:81 sdd 8:48   active undef running
#   `- 8:0:2:81 sdp 8:240  active undef running

# And this has been seen on SLES 11 SP1 64 Bit:
# 3600508b40006d7da0001a00004740000 dm-0 HP,HSV210
# size=10G features='1 queue_if_no_path' hwhandler='0' wp=rw
# |-+- policy='round-robin 0' prio=-1 status=active
# | |- 2:0:0:1 sda        8:0    active undef running
# | `- 3:0:0:1 sdo        8:224  active undef running
# `-+- policy='round-robin 0' prio=-1 status=enabled
#   |- 3:0:1:1 sdv        65:80  active undef running
#   `- 2:0:1:1 sdh        8:112  active undef running

# This is another output, which made problems up to
# 1.1.12:
#
# SDDN_S2A_9900_1308xxxxxxxx dm-13 DDN,S2A 9900
# [size=7.3T][features=0][hwhandler=0][rw]
# \_ round-robin 0 [prio=0][active]
#  \_ 3:0:1:11 sdaj 66:48   [failed][undef]
#  \_ 4:0:0:11 sdbh 67:176  [failed][undef]
#  \_ 4:0:2:11 sddd 70:176  [active][undef]
#  \_ 3:0:2:11 sdeb 128:48  [active][undef]
# \_ round-robin 0 [prio=0][enabled]
#  \_ 4:0:1:11 sdcf 69:48   [active][undef]
#  \_ 3:0:0:11 sdl  8:176   [active][undef]

# Just an underscore and a dash in the LUN name
# SDataCoreSANsymphony_DAT05-fscl dm-6 DataCore,SANsymphony
# [size=600G][features=0][hwhandler=0]
# \_ round-robin 0 [prio=-1][enabled]
#  \_ 3:0:0:11 sdae 65:224 [active][undef]
# \_ round-robin 0 [prio=-1][enabled]
#  \_ 4:0:0:11 sdt  65:48  [active][undef]

# This one here is from RedHat 6. Very creative...
# 1IET     00010001 dm-4 IET,VIRTUAL-DISK
# size=200G features='0' hwhandler='0' wp=rw
# |-+- policy='round-robin 0' prio=0 status=active
# | `- 23:0:0:1   sdk  8:160   active undef running
# |-+- policy='round-robin 0' prio=0 status=enabled
# | `- 21:0:0:1   sdj  8:144   active undef running
# |-+- policy='round-robin 0' prio=0 status=enabled
# | `- 22:0:0:1   sdg  8:96    active undef running
# `-+- policy='round-robin 0' prio=0 status=enabled
#   `- 20:0:0:1   sdi  8:128   active undef running

# And a completely new situation:
# <<<multipath>>>
# Nov 05 17:17:03 | DM multipath kernel driver not loaded
# Nov 05 17:17:03 | /etc/multipath.conf does not exist, blacklisting all devices.
# Nov 05 17:17:03 | A sample multipath.conf file is located at
# Nov 05 17:17:03 | /usr/share/doc/device-mapper-multipath-0.4.9/multipath.conf
# Nov 05 17:17:03 | You can run /sbin/mpathconf to create or modify /etc/multipath.conf
# Nov 05 17:17:03 | DM multipath kernel driver not loaded

def parse_multipath(info):
    # New reported header lines need to be placed here
    # the matches need to be put in a list of tupples
    # while the structure of the tupple is:
    # 0: matching regex
    # 1: matched regex-group id of UUID
    # 2: matched regex-group id of alias (optional)
    reg_headers = [
        (get_regex(r"^[0-9a-z]{33}$"),                       0, None), # 1. (should be included in 3.)
        (get_regex(r"^([^\s]+)\s\(([0-9A-Za-z_-]+)\)"),      2, 1),    # 2.
        (get_regex(r"^[a-zA-Z0-9_]+$"),                      0, None), # 3.
        (get_regex(r"^([0-9a-z]{33}|[0-9a-z]{49})\s?dm.+$"), 1, None), # 4.
        (get_regex(r"^[a-zA-Z0-9_]+dm-.+$"),                 0, None), # 5. Remove this line in 1.2.0
        (get_regex(r"^([-a-zA-Z0-9_ ]+)\s?dm-[0-9]+.*$"),    1, None), # 6. and 7.
    ]

    reg_prio = get_regex("[[ ]prio=")
    reg_lun  = get_regex("[0-9]+:[0-9]+:[0-9]+:[0-9]+")
    uuid = None
    alias = None
    groups = {}
    group = {}
    numpaths = None
    for line in info:

        # Ignore error messages due to invalid multipath.conf
        if line[0] == "multipath.conf":
            continue

        # newer agent also output the device mapper table.
        # ignore those lines for now.
        if line[0] == "dm":
            # Reset current device and skip line
            uuid = None
            continue

        # restore original non-split line
        l = " ".join(line)

        # Skip output when multipath is not present
        if l.endswith('kernel driver not loaded') \
           or l.endswith('does not exist, blacklisting all devices.') \
           or l.endswith('A sample multipath.conf file is located at') \
           or l.endswith('multipath.conf'):
            uuid = None
            continue

        # First simply separate between data row and header row
        if line[0][0] not in [ '[', '`', '|', '\\' ] and not line[0].startswith("size="):
            # Try to match header lines
            matchobject = None
            for regex, uuid_pos, alias_pos in reg_headers:
                matchobject = regex.search(l)

                if matchobject:
                    uuid = matchobject.group(uuid_pos)

                    if alias_pos:
                        alias = matchobject.group(alias_pos)
                    else:
                        alias = None

                    break

            # No data row and no matching header row
            if not matchobject:
                raise Exception("Invalid line in agent output: " + l)

            # initialize information about next device
            numpaths = 0
            lun_info = []
            paths_info = []
            broken_paths = []
            group = {}
            group['paths'] = paths_info
            group['broken_paths'] = broken_paths
            group['luns'] = lun_info
            group['uuid'] = uuid
            group['state'] = None
            group['numpaths'] = 0
            groups[uuid] = group

            # If the device has an alias, then extract it
            if alias:
                group['alias'] = alias

            # Proceed with next line after init
            continue
        else:
            if uuid != None:
                # Handle special syntax | |- 2:0:0:1 sda  ...
                if line[0] == '|':
                    line = line[1:]
                if reg_prio.search(l):
                    group['state'] = "".join(line[3:])
                elif len(line) >= 4 and reg_lun.match(line[1]):
                    luninfo = "%s(%s)" % (line[1], line[2])
                    lun_info.append(luninfo)
                    state = line[4]
                    if not "active" in state:
                        broken_paths.append(luninfo)
                    numpaths += 1
                    group['numpaths'] = numpaths
                    paths_info.append(line[2])
    return groups

# Get list of UUIDs of all multipath devices
# Length of UUID is 360a9800043346937686f456f59386741
def inventory_multipath(parsed):
    settings = host_extra_conf_merged(g_hostname, inventory_multipath_rules)

    inventory = []
    for uuid, info in parsed.items():
        # take current number of paths as target value
        if "alias" in info and settings.get("use_alias"):
            item = info["alias"]
        else:
            item = uuid
        inventory.append( (item, info['numpaths']) )
    return inventory

# item is UUID (e.g. '360a9800043346937686f456f59386741') or alias (e.g. 'mpath0')
def check_multipath(item, target_numpaths, parsed):
    if target_numpaths == None:
        target_numpaths = 2 # default case: we need two paths

    # Keys in parsed are the UUIDs. First assume that we are
    # looking for a UUID. Then fall back to aliases
    if item in parsed:
        mmap = parsed[item]
    else:
        for mmap in parsed.values():
            if mmap.get("alias") == item:
                break
        else:
            return 3, "Multipath device not found in agent output"

    # If the item is the alias, then show the UUID in the plugin output.
    # If the item is the UUID, then vice versa.
    alias = mmap.get('alias')
    uuid = mmap.get('uuid')

    if item == uuid and alias:
        aliasinfo = "(%s) " % alias
    elif item == alias and uuid:
        aliasinfo = "(%s) " % uuid
    else:
        aliasinfo = ""

    numpaths = mmap['numpaths']
    broken = mmap['broken_paths']
    numbroken = len(broken)
    if numbroken > 0:
        return (2, "%sbroken paths: %s" % (aliasinfo, ",".join(broken)))

    info = "%spaths expected: %d, paths active: %d" % (aliasinfo, target_numpaths, numpaths)

    if numpaths < target_numpaths:
        return (2, info)
    elif numpaths > target_numpaths:
        return (1, info)
    else:
        return (0, info)

check_info["multipath"] = {
    'check_function':          check_multipath,
    'inventory_function':      inventory_multipath,
    'parse_function':          parse_multipath,
    'service_description':     'Multipath %s',
    'group':                   'multipath',
}