Refactoring: Moved check parameters from unsorted.py to dedicated modules (CMK-1393)
[check_mk.git] / checks / df
blob348ace0f185b4fbd83dec4af4af859c20c77874b
1 #!/usr/bin/python
2 # -*- encoding: utf-8; py-indent-offset: 4 -*-
3 # +------------------------------------------------------------------+
4 # | ____ _ _ __ __ _ __ |
5 # | / ___| |__ ___ ___| | __ | \/ | |/ / |
6 # | | | | '_ \ / _ \/ __| |/ / | |\/| | ' / |
7 # | | |___| | | | __/ (__| < | | | | . \ |
8 # | \____|_| |_|\___|\___|_|\_\___|_| |_|_|\_\ |
9 # | |
10 # | Copyright Mathias Kettner 2014 mk@mathias-kettner.de |
11 # +------------------------------------------------------------------+
13 # This file is part of Check_MK.
14 # The official homepage is at http://mathias-kettner.de/check_mk.
16 # check_mk is free software; you can redistribute it and/or modify it
17 # under the terms of the GNU General Public License as published by
18 # the Free Software Foundation in version 2. check_mk is distributed
19 # in the hope that it will be useful, but WITHOUT ANY WARRANTY; with-
20 # out even the implied warranty of MERCHANTABILITY or FITNESS FOR A
21 # PARTICULAR PURPOSE. See the GNU General Public License for more de-
22 # tails. You should have received a copy of the GNU General Public
23 # License along with GNU Make; see the file COPYING. If not, write
24 # to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
25 # Boston, MA 02110-1301 USA.
27 # <<<df>>>
28 # /dev/sda3 ext4 8123200 1207512 6496392 16% /
29 # /dev/sda6 ext3 117794932 192192 111522544 1% /data
30 # /dev/sda2 ext3 8123200 220388 7483516 3% /var
31 # /dev/sda1 reiserfs 256666 16052 227362 7% /boot
32 # /dev/mapper/mirrored-database ext3 20642428 1027112 19405604 6% /mirrored/database
34 # Another example from a Windows 7 system:
35 # <<<df>>>
36 # SYSTEM NTFS 312569172 180648472 131920700 58% C:\
37 # Data NTFS 976506816 528665344 447841472 55% D:\
38 # PS3 PlayStation(R)3 File System 0 0 0 0% P:\
40 # An example with btrfs (SLES 12). Here the same device is mounted
41 # several times at different mount point. But must only be monitored
42 # once. We use the device instead of the mount point in this case.
43 # <<<df>>>
44 # /dev/sda1 btrfs 20970496 4169036 16539348 21% /
45 # devtmpfs devtmpfs 497396 0 497396 0% /dev
46 # tmpfs tmpfs 506312 0 506312 0% /dev/shm
47 # tmpfs tmpfs 506312 6980 499332 2% /run
48 # tmpfs tmpfs 506312 0 506312 0% /sys/fs/cgroup
49 # /dev/sda1 btrfs 20970496 4169036 16539348 21% /.snapshots
50 # /dev/sda1 btrfs 20970496 4169036 16539348 21% /var/tmp
51 # /dev/sda1 btrfs 20970496 4169036 16539348 21% /var/spool
52 # /dev/sda1 btrfs 20970496 4169036 16539348 21% /var/opt
53 # /dev/sda1 btrfs 20970496 4169036 16539348 21% /var/log
55 # <<<df>>>
56 # dev 795652 0 795652 0% /dev
57 # run 811756 11848 799908 1% /run
58 # /dev/sda2 1040280 716340 271512 73% /
59 # devtmpfs 795652 0 795652 0% /dev
60 # tmpfs 811756 0 811756 0% /dev/shm
61 # tmpfs 811756 11848 799908 1% /run
62 # tmpfs 811756 0 811756 0% /sys/fs/cgroup
63 # none 811756 12 811744 0% /var/tmp
64 # none 811756 0 811756 0% /var/lock
65 # none 409600 95460 314140 23% /var/log
66 # tmpfs 811756 11848 799908 1% /var/run
67 # none 811756 56 811700 0% /tmp
68 # /dev/sda1 126931 33759 86619 28% /boot
69 # /dev/sda5 12668904 360184 11670236 3% /persist
70 # <<<df>>>
71 # [df_inodes_start]
72 # dev 198913 365 198548 0% /dev
73 # run 202939 336 202603 0% /run
74 # /dev/sda2 65536 25533 40003 39% /
75 # devtmpfs 198913 365 198548 0% /dev
76 # tmpfs 202939 1 202938 0% /dev/shm
77 # tmpfs 202939 336 202603 0% /run
78 # tmpfs 202939 7 202932 0% /sys/fs/cgroup
79 # none 202939 4 202935 0% /var/tmp
80 # none 202939 1 202938 0% /var/lock
81 # none 202939 28 202911 0% /var/log
82 # tmpfs 202939 336 202603 0% /var/run
83 # none 202939 27 202912 0% /tmp
84 # /dev/sda1 32768 25 32743 0% /boot
85 # /dev/sda5 799680 118 799562 0% /persist
86 # [df_inodes_end]
88 # <<<df>>>
89 # C:\ NTFS 41838588 21776048 20062540 53% C:\
90 # C:\Program Files\Vision Solutions\Double-Take\Service\MountDir\usauhtest0010_c061b170-ad3f-473f-92ce-088c97fce98e_C\ NTFS 41835516 11895180 29940336 29% C:\Program Files\Vision Solutions\Double-Take\Service\MountDir\usauhtest0010_c061b170-ad3f-473f-92ce-088c97fce98e_C\
92 inventory_df_rules = []
93 inventory_df_exclude_fs = ['tmpfs', 'nfs', 'smbfs', 'cifs', 'iso9660']
96 def parse_df(info):
97 def parse_blocks_subsection(blocks_subsection):
98 volume_info = {}
99 df_blocks = []
100 btrfs_devices = set()
101 for line in blocks_subsection:
102 try:
103 int(line[1])
104 except ValueError:
105 pass
106 else:
107 line = [line[0], None] + line[1:]
109 # Handle known cases, where the file system contains spaces
110 for index, entry in enumerate(line):
111 if entry == "NTFS":
112 line = [" ".join(line[:index])] + [line[index]] + line[index + 1:index + 5] + [
113 " ".join(line[index + 5:])
115 break
117 if line[2] == "File" and line[3] == "System":
118 line = [line[0], " ".join(line[1:4])] + line[4:]
120 fs_type = line[1]
121 # This particular bit of magic originated in Werk #2671 and has the purpose of avoiding duplicate checks,
122 # as btrfs filesystems are often mounted at multiple mountpoints. We keep it for compatibility.
123 if fs_type == "btrfs":
124 device = line[0]
125 if device not in btrfs_devices:
126 btrfs_devices.add(device)
127 mountpoint = "btrfs " + device
128 else:
129 continue
131 else:
132 mountpoint = " ".join(line[6:]).replace('\\', '/') # Windows \ is replaced with /
134 # exclude filesystems without size
135 try:
136 if int(line[2]) == 0:
137 continue
138 except ValueError:
139 continue
141 # Beware: the 6th column of df ("used perc") may includes 5% which are reserved
142 # for the superuser, whereas the 4th colum ("used MB") does *not* include that.
143 # Beware(2): the column used_mb does not account for the reserved space for
144 # superusers. So we rather use the column 'avail' and subtract that from total
145 # to compute the used space.
146 size_mb = int(line[2]) / 1024.0
147 avail_mb = int(line[4]) / 1024.0
148 used_mb = int(line[3]) / 1024.0
149 reserved_mb = size_mb - avail_mb - used_mb # reserved for root
150 df_blocks.append((mountpoint, size_mb, avail_mb, reserved_mb))
152 volume_name = line[0]
153 volume_info[mountpoint] = {
154 "volume_name": volume_name,
155 "fs_type": fs_type,
158 return df_blocks, volume_info
160 def parse_inodes_subsection(inodes_subsection):
161 df_inodes = []
162 for line in inodes_subsection:
163 try:
164 int(line[1])
165 except ValueError:
166 pass
167 else:
168 line = [line[0], None] + line[1:]
170 try:
171 inodes_total = int(line[2])
172 inodes_avail = int(line[4])
173 except ValueError:
174 continue
176 mountpoint = line[-1]
177 df_inodes.append((mountpoint, inodes_total, inodes_avail))
178 return df_inodes
180 blocks_subsection = []
181 inodes_subsection = []
183 is_inode = False
184 for line in info:
185 if line[-1] == '[df_inodes_start]':
186 is_inode = True
187 continue
188 elif line[-1] == '[df_inodes_end]':
189 is_inode = False
190 continue
192 if is_inode:
193 inodes_subsection.append(line)
194 else:
195 blocks_subsection.append(line)
197 return parse_blocks_subsection(blocks_subsection), parse_inodes_subsection(inodes_subsection)
200 def inventory_df(parsed):
201 def is_mountpoint(inventory_entry):
202 return "patterns" not in inventory_entry[1]
204 inventory_options = host_extra_conf_merged(host_name(), inventory_df_rules)
205 include_volume_name = inventory_options.get("include_volume_name", False)
206 ignore_fs_types = inventory_options.get("ignore_fs_types", inventory_df_exclude_fs)
207 never_ignore_mountpoints = inventory_options.get("never_ignore_mountpoints", [])
209 (df_blocks, volume_info), _ = parsed
211 mplist = []
212 for line in _filter_docker_filesystems(df_blocks):
214 mountpoint = line[0]
215 if mountpoint in inventory_df_exclude_mountpoints:
216 continue # exclude this mount point (/tmp, /proc, whatever user wants)
217 if volume_info[mountpoint][
218 "fs_type"] in ignore_fs_types and mountpoint not in never_ignore_mountpoints:
219 continue # ignore this filesystem type
220 mplist.append(mountpoint)
222 if include_volume_name:
223 inventory = []
224 for entry in df_inventory(mplist):
225 if is_mountpoint(entry):
226 mountpoint, params = entry
227 item = "%s %s" % (volume_info[mountpoint]["volume_name"], mountpoint)
228 inventory.append((item, params))
229 else:
230 inventory.append(entry)
231 else:
232 inventory = df_inventory(mplist)
233 return inventory
236 def check_df(item, params, parsed):
237 (df_blocks, volume_info), df_inodes = parsed
239 df_blocks = _filter_docker_filesystems(df_blocks)
240 volume_and_mp_to_mp = {
241 ("%s %s" % (volume_info[mp]["volume_name"], mp)): mp for mp in volume_info
243 if "patterns" in params or item in volume_info:
244 mountpoint = item
245 elif item in volume_and_mp_to_mp:
246 mountpoint = volume_and_mp_to_mp[item]
247 else:
248 mountpoint = item
249 return df_check_filesystem_list(mountpoint, params, df_blocks, df_inodes)
252 # Always exclude filesystems below dockers local storage area
253 # And also exclude docker mounts in containers which are reported
254 # by the agent when the agent is executed in the container context
255 def _filter_docker_filesystems(df_blocks):
256 return [
257 e for e in df_blocks if not e[0].startswith("/var/lib/docker/") and
258 e[0] not in ["/etc/resolv.conf", "/etc/hostname", "/etc/hosts"]
262 check_info['df'] = {
263 "parse_function": parse_df,
264 "inventory_function": inventory_df,
265 "check_function": check_df,
266 "service_description": "Filesystem %s",
267 "has_perfdata": True,
268 "group": "filesystem",
269 "default_levels_variable": "filesystem_default_levels",
270 "includes": ["size_trend.include", "df.include"],