Cleanup config.nodes_of
[check_mk.git] / checks / esx_vsphere_vm
blob3695db02475dcead6604f4dc316884a5f309673b
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.
28 # TODO: name the next new subcheck "esx_vsphere_vm" and introduce a parse function
29 def esx_vsphere_vm_convert(info):
30 data = {}
31 for line in info:
32 # Do not monitor VM templates
33 if line[0] == "config.template" and line[1] == "true":
34 return {}
35 data[line[0]] = line[1:]
36 return data
39 # .--Memory--------------------------------------------------------------.
40 # | __ __ |
41 # | | \/ | ___ _ __ ___ ___ _ __ _ _ |
42 # | | |\/| |/ _ \ '_ ` _ \ / _ \| '__| | | | |
43 # | | | | | __/ | | | | | (_) | | | |_| | |
44 # | |_| |_|\___|_| |_| |_|\___/|_| \__, | |
45 # | |___/ |
46 # '----------------------------------------------------------------------'
49 def inventory_esx_vsphere_vm_mem(info):
50 data = esx_vsphere_vm_convert(info)
51 if 'summary.quickStats.guestMemoryUsage' in data:
52 return [(None, {})]
55 def check_esx_vsphere_vm_mem(_no_item, _no_params, info):
56 data = esx_vsphere_vm_convert(info)
58 # If the machine is powered of, we do not get data
59 powerstate = data["runtime.powerState"][0]
60 if powerstate != "poweredOn":
61 raise MKCounterWrapped("VM is %s, skipping this check" % powerstate)
63 try:
64 #consumed host memory
65 host_memory_usage = savefloat(data["summary.quickStats.hostMemoryUsage"][0]) * 1024 * 1024
66 #active guest memory
67 guest_memory_usage = savefloat(data["summary.quickStats.guestMemoryUsage"][0]) * 1024 * 1024
68 #size of the balloon driver in the VM
69 ballooned_memory = savefloat(data["summary.quickStats.balloonedMemory"][0]) * 1024 * 1024
70 #The portion of memory, in MB, that is granted to this VM from non-shared host memor(musst not be set)
71 shared_memory = savefloat(data["summary.quickStats.sharedMemory"][0]) * 1024 * 1024
72 #The portion of memory, in MB, that is granted to this VM from host memory that is shared between VMs.
73 private_memory = savefloat(data.get("summary.quickStats.privateMemory",
74 [0])[0]) * 1024 * 1024
75 except:
76 raise MKCounterWrapped(
77 "Hostsystem did not provide memory information (reason may be high load)")
79 perf = [
80 ("host", host_memory_usage),
81 ("guest", guest_memory_usage),
82 ("ballooned", ballooned_memory),
83 ("shared", shared_memory),
84 ("private", private_memory),
87 message = "Host: %s, Guest: %s, " \
88 "Ballooned: %s, Private: %s, Shared: %s" % \
89 (get_bytes_human_readable(host_memory_usage), \
90 get_bytes_human_readable(guest_memory_usage), get_bytes_human_readable(ballooned_memory), \
91 get_bytes_human_readable(private_memory), get_bytes_human_readable(shared_memory) )
92 return (0, message, perf)
95 check_info['esx_vsphere_vm.mem_usage'] = {
96 "inventory_function": inventory_esx_vsphere_vm_mem,
97 "check_function": check_esx_vsphere_vm_mem,
98 "service_description": "ESX Memory",
99 "has_perfdata": True
103 # .--Name----------------------------------------------------------------.
104 # | _ _ |
105 # | | \ | | __ _ _ __ ___ ___ |
106 # | | \| |/ _` | '_ ` _ \ / _ \ |
107 # | | |\ | (_| | | | | | | __/ |
108 # | |_| \_|\__,_|_| |_| |_|\___| |
109 # | |
110 # '----------------------------------------------------------------------'
113 def inventory_esx_vsphere_vm_name(info):
114 data = esx_vsphere_vm_convert(info)
115 if 'name' in data:
116 return [(None, None)]
119 def check_esx_vsphere_vm_name(_no_item, _no_params, info):
120 data = esx_vsphere_vm_convert(info)
121 return (0, " ".join(data['name']))
124 check_info['esx_vsphere_vm.name'] = {
125 "inventory_function": inventory_esx_vsphere_vm_name,
126 "check_function": check_esx_vsphere_vm_name,
127 "service_description": "ESX Name",
131 # .--Runtime Host--------------------------------------------------------.
132 # | ____ _ _ _ _ _ |
133 # | | _ \ _ _ _ __ | |_(_)_ __ ___ ___ | | | | ___ ___| |_ |
134 # | | |_) | | | | '_ \| __| | '_ ` _ \ / _ \ | |_| |/ _ \/ __| __| |
135 # | | _ <| |_| | | | | |_| | | | | | | __/ | _ | (_) \__ \ |_ |
136 # | |_| \_\\__,_|_| |_|\__|_|_| |_| |_|\___| |_| |_|\___/|___/\__| |
137 # | |
138 # +----------------------------------------------------------------------+
139 # | |
140 # '----------------------------------------------------------------------'
143 def inventory_esx_vsphere_vm_running_on(info):
144 data = esx_vsphere_vm_convert(info)
145 if 'runtime.host' in data:
146 return [(None, None)]
149 def check_esx_vsphere_vm_running_on(no_item, no_params, info):
150 data = esx_vsphere_vm_convert(info)
152 running_on = data.get("runtime.host")
153 if not running_on:
154 return 3, "Runtime host information is missing"
156 return 0, "Running on %s" % running_on[0]
159 check_info['esx_vsphere_vm.running_on'] = {
160 "inventory_function": inventory_esx_vsphere_vm_running_on,
161 "check_function": check_esx_vsphere_vm_running_on,
162 "service_description": "ESX Hostsystem",
166 # .--VM Datastores--------------------------------------------------------.
167 # | __ ____ __ ____ _ _ |
168 # | \ \ / / \/ | | _ \ __ _| |_ __ _ ___| |_ ___ _ __ ___ |
169 # | \ \ / /| |\/| | | | | |/ _` | __/ _` / __| __/ _ \| '__/ _ \ |
170 # | \ V / | | | | | |_| | (_| | || (_| \__ \ || (_) | | | __/ |
171 # | \_/ |_| |_| |____/ \__,_|\__\__,_|___/\__\___/|_| \___| |
172 # | |
173 # +----------------------------------------------------------------------+
174 # | |
175 # '----------------------------------------------------------------------'
178 def inventory_esx_vsphere_vm_datastores(info):
179 data = esx_vsphere_vm_convert(info)
180 # Right now we only handle one datastore per vm
181 if 'config.datastoreUrl' in data:
182 return [(None, None)]
185 def check_esx_vsphere_vm_datastores(no_item, no_params, info):
186 data = esx_vsphere_vm_convert(info)
188 datastore_urls = data.get("config.datastoreUrl")
189 if not datastore_urls:
190 return 3, "Datastore information is missing"
192 output = []
193 for datastore_url in " ".join(datastore_urls).split("@@"):
194 datastore_url = datastore_url.split("|")
195 output_store = []
197 # datastore_url looks like
198 #['url /vmfs/volumes/513df1e9-12fd7366-ac5a-e41f13e69eaa',
199 # 'uncommitted 51973812224',
200 # 'name zmucvm99-lds',
201 # 'type VMFS',
202 # 'accessible true',
203 # 'capacity 578478407680',
204 # 'freeSpace 68779245568']
206 # Convert datastore_url to dict
207 datastore_dict = dict(x.split(" ", 1) for x in datastore_url)
209 capacity = saveint(datastore_dict.get("capacity", 0)) * 1.0
210 if capacity:
211 free_perc = int(datastore_dict.get("freeSpace", 0)) / capacity * 100
212 else:
213 free_perc = 0.0
215 output_store = "Stored on %s (%s/%0.1f%% free)" %\
216 (datastore_dict.get("name"),
217 get_bytes_human_readable(capacity),
218 free_perc)
219 output.append(output_store)
220 return 0, ", ".join(output)
223 check_info['esx_vsphere_vm.datastores'] = {
224 "inventory_function": inventory_esx_vsphere_vm_datastores,
225 "check_function": check_esx_vsphere_vm_datastores,
226 "service_description": "ESX Datastores",
230 # .--GuestTools----------------------------------------------------------.
231 # | ____ _ _____ _ |
232 # | / ___|_ _ ___ ___| ||_ _|__ ___ | |___ |
233 # | | | _| | | |/ _ \/ __| __|| |/ _ \ / _ \| / __| |
234 # | | |_| | |_| | __/\__ \ |_ | | (_) | (_) | \__ \ |
235 # | \____|\__,_|\___||___/\__||_|\___/ \___/|_|___/ |
236 # | |
237 # +----------------------------------------------------------------------+
238 # | |
239 # '----------------------------------------------------------------------'
242 def inventory_esx_vsphere_vm_guest_tools(info):
243 data = esx_vsphere_vm_convert(info)
244 if 'guest.toolsVersionStatus' in data:
245 return [(None, {})]
248 def check_esx_vsphere_vm_guest_tools(_no_item, params, info):
249 data = esx_vsphere_vm_convert(info)
251 try:
252 vm_status = data['guest.toolsVersionStatus'][0]
253 except KeyError:
254 return
256 guest_tools_map = {
257 "guestToolsCurrent": (0, "VMware Tools are installed and the version is current"),
258 "guestToolsNeedUpgrade": (1, "VMware Tools are installed, but the version is not current"),
259 "guestToolsNotInstalled": (2, "VMware Tools are not installed"),
260 "guestToolsUnmanaged": (1, "VMware Tools are installed, but are not managed by VMWare")
262 state, info = guest_tools_map.get(vm_status, (3, "Unknown status for VMware Tools"))
264 if params:
265 state = params.get(vm_status, state)
267 return state, info
270 check_info['esx_vsphere_vm.guest_tools'] = {
271 "inventory_function": inventory_esx_vsphere_vm_guest_tools,
272 "check_function": check_esx_vsphere_vm_guest_tools,
273 "service_description": "ESX Guest Tools",
274 "group": "vm_guest_tools"
278 # .--Heartbeat-----------------------------------------------------------.
279 # | _ _ _ _ _ |
280 # | | | | | ___ __ _ _ __| |_| |__ ___ __ _| |_ |
281 # | | |_| |/ _ \/ _` | '__| __| '_ \ / _ \/ _` | __| |
282 # | | _ | __/ (_| | | | |_| |_) | __/ (_| | |_ |
283 # | |_| |_|\___|\__,_|_| \__|_.__/ \___|\__,_|\__| |
284 # | |
285 # '----------------------------------------------------------------------'
288 # Possible values (this list is taken from the official documentation)
289 # gray - VMware Tools are not installed or not running.
290 # red - No heartbeat. Guest operating system may have stopped responding.
291 # yellow - Intermittent heartbeat. May be due to guest load.
292 # green - Guest operating system is responding normally.
294 def inventory_esx_vsphere_vm_hb_status(info):
295 data = esx_vsphere_vm_convert(info)
296 if 'guestHeartbeatStatus' in data:
297 return [(None, {})]
300 def check_esx_vsphere_vm_hb_status(_no_item, params, info):
301 data = esx_vsphere_vm_convert(info)
303 if data.get('guestHeartbeatStatus') is None:
304 return
306 vm_status = data['guestHeartbeatStatus'][0]
307 state = 3
309 vm_heartbeat_map = {
310 "gray": (1, "heartbeat_no_tools"),
311 "green": (0, "heartbeat_ok"),
312 "red": (2, "heartbeat_missing"),
313 "yellow": (1, "heartbeat_intermittend")
315 if vm_status in vm_heartbeat_map:
316 if params:
317 state = params.get(vm_heartbeat_map.get(vm_status)[1], 3)
318 else:
319 state = vm_heartbeat_map.get(vm_status)[0]
320 if vm_status == 'gray':
321 return state, "No VMWare Tools installed, outdated or not running"
322 return state, "Heartbeat status is %s" % vm_status
323 else:
324 return 3, "Unknown heartbeat status %s" % vm_status
327 check_info['esx_vsphere_vm.heartbeat'] = {
328 "inventory_function": inventory_esx_vsphere_vm_hb_status,
329 "check_function": check_esx_vsphere_vm_hb_status,
330 "service_description": "ESX Heartbeat",
331 "group": "vm_heartbeat"
335 # .--CPU-----------------------------------------------------------------.
336 # | ____ ____ _ _ |
337 # | / ___| _ \| | | | |
338 # | | | | |_) | | | | |
339 # | | |___| __/| |_| | |
340 # | \____|_| \___/ |
341 # | |
342 # +----------------------------------------------------------------------+
343 # | |
344 # '----------------------------------------------------------------------'
346 # <<<esx_vsphere_vm>>>
347 # config.hardware.numCPU 8
348 # config.hardware.numCoresPerSocket 2
349 # summary.quickStats.overallCpuUsage 8
352 def inventory_esx_vsphere_vm_cpu(info):
353 data = esx_vsphere_vm_convert(info)
354 if 'summary.quickStats.overallCpuUsage' in data:
355 return [(None, None)]
358 def check_esx_vsphere_vm_cpu(_no_item, _no_params, info):
359 data = esx_vsphere_vm_convert(info)
360 # VMs that are currently down do not have this entry
361 if 'summary.quickStats.overallCpuUsage' not in data:
362 raise MKCounterWrapped("No information about CPU usage. VM is probably powered off.")
364 usage_mhz = int(data['summary.quickStats.overallCpuUsage'][0])
365 cpus = int(data['config.hardware.numCPU'][0])
366 return 0, "demand is %.3f Ghz, %d virtual CPUs" % (usage_mhz / 1000.0, cpus), [("demand",
367 usage_mhz)]
370 check_info['esx_vsphere_vm.cpu'] = {
371 "inventory_function": inventory_esx_vsphere_vm_cpu,
372 "check_function": check_esx_vsphere_vm_cpu,
373 "service_description": "ESX CPU",
374 "has_perfdata": True,
378 # .--Snapshots-----------------------------------------------------------.
379 # | ____ _ _ |
380 # | / ___| _ __ __ _ _ __ ___| |__ ___ | |_ ___ |
381 # | \___ \| '_ \ / _` | '_ \/ __| '_ \ / _ \| __/ __| |
382 # | ___) | | | | (_| | |_) \__ \ | | | (_) | |_\__ \ |
383 # | |____/|_| |_|\__,_| .__/|___/_| |_|\___/ \__|___/ |
384 # | |_| |
385 # +----------------------------------------------------------------------+
387 # <<<esx_vsphere_vm>>>
388 # snapshot.rootSnapshotList 1 1363596734 poweredOff 20130318_105600_snapshot_LinuxI|2 1413977827 poweredOn LinuxI Testsnapshot
391 def inventory_esx_vsphere_vm_snapshots(info):
392 data = esx_vsphere_vm_convert(info)
393 if data:
394 return [(None, {})]
397 def check_esx_vsphere_vm_snapshots(_no_item, params, info):
398 def _check_levels(age, warn, crit):
399 if age >= crit:
400 return (2, "snapshot is older than %s" % get_age_human_readable(age))
401 elif age >= warn:
402 return (1, "snapshot is older than %s" % get_age_human_readable(age))
403 return (0, "")
405 data = esx_vsphere_vm_convert(info)
407 if 'snapshot.rootSnapshotList' not in data:
408 yield 0, "No snapshots found"
409 return
411 Snapshot = collections.namedtuple("Snapshot", ["time", "state", "name"])
413 snapshots = (x.split(" ", 3) for x in " ".join(data["snapshot.rootSnapshotList"]).split("|"))
414 snapshots = [Snapshot(int(x[1]), x[2], x[3]) for x in snapshots]
415 yield 0, "Number of Snapshots %d" % len(snapshots)
417 if not snapshots:
418 return
420 powerd_on_list = [s for s in snapshots if s.state == "poweredOn"]
421 yield 0, "Powered On: %s" % (', '.join(s.name for s in powerd_on_list) or "None")
423 latest_snapshot = max(snapshots, key=lambda s: s.time)
424 latest_snapshot_age = time.time() - latest_snapshot.time
425 perfdata = []
426 if params.get("age"):
427 warn, crit = params["age"]
428 state, message = _check_levels(latest_snapshot_age, warn, crit)
429 if state > 0:
430 yield state, "Latest %s" % message,
431 perfdata = [("age", latest_snapshot_age, warn, crit)]
432 else:
433 perfdata = [("age", latest_snapshot_age)]
435 timestamp = get_timestamp_human_readable(latest_snapshot.time)
436 yield 0, "Latest Snapshot: %s %s" % (latest_snapshot.name, timestamp), perfdata
438 oldest_snapshot = min(snapshots, key=lambda s: s.time)
439 # Display oldest snapshot only, if it is not identical with the last snapshot
440 if oldest_snapshot != latest_snapshot:
441 perfdata = []
442 oldest_snapshot_age = time.time() - oldest_snapshot.time
443 if params.get("age_oldest"):
444 warn, crit = params["age_oldest"]
445 state, message = _check_levels(oldest_snapshot_age, warn, crit)
446 if state > 0:
447 yield state, "Oldest %s" % message,
448 perfdata = [("age_oldest", oldest_snapshot_age, warn, crit)]
449 else:
450 perfdata = [("age_oldest", oldest_snapshot_age)]
452 timestamp = get_timestamp_human_readable(oldest_snapshot.time)
453 yield 0, "Oldest Snapshot: %s %s" % (oldest_snapshot.name, timestamp), perfdata
456 check_info['esx_vsphere_vm.snapshots'] = {
457 "inventory_function": inventory_esx_vsphere_vm_snapshots,
458 "check_function": check_esx_vsphere_vm_snapshots,
459 "service_description": "ESX Snapshots",
460 "group": "vm_snapshots",
461 "has_perfdata": True,
465 # .--VM devices----------------------------------------------------------.
466 # | __ ____ __ _ _ |
467 # | \ \ / / \/ | __| | _____ _(_) ___ ___ ___ |
468 # | \ \ / /| |\/| | / _` |/ _ \ \ / / |/ __/ _ \/ __| |
469 # | \ V / | | | | | (_| | __/\ V /| | (_| __/\__ \ |
470 # | \_/ |_| |_| \__,_|\___| \_/ |_|\___\___||___/ |
471 # | |
472 # '----------------------------------------------------------------------'
475 def parse_esx_vsphere_vm_mounted_devices(info):
476 data = esx_vsphere_vm_convert(info).get("config.hardware.device", [])
477 parsed = {}
478 for device_data in " ".join(data).split("@@"):
479 if "|" not in device_data:
480 continue
481 device_attrs = {}
482 for entry in device_data.split("|"):
483 k, v = entry.split(" ", 1)
484 device_attrs.setdefault(k, v)
485 device_name = device_attrs["label"]
486 del device_attrs["label"]
487 parsed.setdefault(device_name, device_attrs)
488 return parsed
491 def inventory_esx_vsphere_vm_mounted_devices(info):
492 if parse_esx_vsphere_vm_mounted_devices(info):
493 return [(None, None)]
496 def check_esx_vsphere_vm_mounted_devices(item, params, info):
497 device_types = ['VirtualCdrom', 'VirtualFloppy']
498 mounted_devices = []
499 for device_name, attrs in parse_esx_vsphere_vm_mounted_devices(info).iteritems():
500 if attrs['virtualDeviceType'] in device_types and \
501 attrs['connected'] == 'true':
502 mounted_devices.append(device_name)
504 if mounted_devices:
505 return 1, "HA functionality not guaranteed, Mounted devices: %s" % \
506 ", ".join(mounted_devices)
507 return 0, "HA functionality guaranteed"
510 check_info['esx_vsphere_vm.mounted_devices'] = {
511 "inventory_function": inventory_esx_vsphere_vm_mounted_devices,
512 "check_function": check_esx_vsphere_vm_mounted_devices,
513 "service_description": "ESX Mounted Devices",