This file is indexed.

/usr/share/check_mk/checks/multipath is in check-mk-server 1.1.12-1ubuntu1.

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
#!/usr/bin/python
# -*- encoding: utf-8; py-indent-offset: 4 -*-
# +------------------------------------------------------------------+
# |             ____ _               _        __  __ _  __           |
# |            / ___| |__   ___  ___| | __   |  \/  | |/ /           |
# |           | |   | '_ \ / _ \/ __| |/ /   | |\/| | ' /            |
# |           | |___| | | |  __/ (__|   <    | |  | | . \            |
# |            \____|_| |_|\___|\___|_|\_\___|_|  |_|_|\_\           |
# |                                                                  |
# | Copyright Mathias Kettner 2010             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.


# Output from multipath -l has the following format:

# 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]


def parse_multipath_output(info, only_uuid = None):
    # only_uuid --> look only for data of this uuid or alias

    # 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-.+$"),            1, None), # 6.
    ]

    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:
        # 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('DM multipath kernel driver not loaded'):
            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)

            # Skip unwanted devices in one device mode
            if only_uuid and only_uuid not in [ uuid, alias ]:
                uuid = None # wrong uuid, ignore this one
                continue

            # 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(info):
    inventory = []
    parsed = parse_multipath_output(info)
    for uuid, info in parsed.items():
        # take current number of paths as target value
        inventory.append( (uuid, " ".join(info['luns']), info['numpaths']) )
    return inventory

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

    parsed = parse_multipath_output(info, item) # we look for one specific uuid/alias
    if len(parsed) == 0:
        return (3, "UNKNOWN - no map with uuid/alias %s" % item)
    mmap = parsed.values()[0]

    alias = mmap.get('alias')
    if alias:
        aliasinfo = "(%s) " % alias
    else:
        aliasinfo = ""

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

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

    if numpaths < target_numpaths:
        return (2, "CRIT - " + info)
    elif numpaths > target_numpaths:
        return (1, "WARN - " + info)
    else:
        return (0, "OK - " + info)


check_info['multipath'] = (
    check_multipath,
    "Multipath %s",
    0,
    inventory_multipath)