Licenses: Updated the list of licenses and added a PDF containing all license texts
[check_mk.git] / cmk_base / automations / check_mk.py
blob8483e1bda06d802e1fe47987a2a84d0fa0706917
1 #!/usr/bin/env 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 import ast
28 import glob
29 import os
30 import subprocess
31 import sys
32 import time
33 import shutil
34 import cStringIO
35 import contextlib
37 import cmk.utils.paths
38 import cmk.utils.debug
39 import cmk.utils.man_pages as man_pages
40 from cmk.utils.exceptions import MKGeneralException
42 import cmk_base.utils
43 import cmk_base.config as config
44 import cmk_base.core
45 import cmk_base.core_config as core_config
46 import cmk_base.snmp as snmp
47 import cmk_base.snmp_utils as snmp_utils
48 import cmk_base.discovery as discovery
49 import cmk_base.check_table as check_table
50 from cmk_base.automations import automations, Automation, MKAutomationError
51 import cmk_base.check_utils
52 import cmk_base.autochecks
53 import cmk_base.nagios_utils
54 from cmk_base.core_factory import create_core
55 import cmk_base.check_api_utils as check_api_utils
56 import cmk_base.check_api as check_api
57 import cmk_base.parent_scan
58 import cmk_base.notify as notify
59 import cmk_base.ip_lookup as ip_lookup
60 import cmk_base.data_sources as data_sources
63 class DiscoveryAutomation(Automation):
64 # if required, schedule an inventory check
65 def _trigger_discovery_check(self, hostname):
66 if (config.inventory_check_autotrigger and config.inventory_check_interval) and\
67 (not config.is_cluster(hostname) or config.nodes_of(hostname)):
68 discovery.schedule_discovery_check(hostname)
71 class AutomationDiscovery(DiscoveryAutomation):
72 cmd = "inventory" # TODO: Rename!
73 needs_config = True
74 needs_checks = True # TODO: Can we change this?
76 # Does discovery for a list of hosts. Possible values for how:
77 # "new" - find only new services (like -I)
78 # "remove" - remove exceeding services
79 # "fixall" - find new, remove exceeding
80 # "refresh" - drop all services and reinventorize
81 # Hosts on the list that are offline (unmonitored) will
82 # be skipped.
83 def execute(self, args):
84 # Error sensivity
85 if args[0] == "@raiseerrors":
86 args = args[1:]
87 on_error = "raise"
88 os.dup2(os.open("/dev/null", os.O_WRONLY), 2)
89 else:
90 on_error = "ignore"
92 # perform full SNMP scan on SNMP devices?
93 if args[0] == "@scan":
94 do_snmp_scan = True
95 args = args[1:]
96 else:
97 do_snmp_scan = False
99 # use cache files if present?
100 # TODO: Why is this handling inconsistent with try-inventory?
101 if args[0] == "@cache":
102 args = args[1:]
103 use_caches = True
104 else:
105 use_caches = False
107 if len(args) < 2:
108 raise MKAutomationError("Need two arguments: new|remove|fixall|refresh HOSTNAME")
110 how = args[0]
111 hostnames = args[1:]
113 counts = {}
114 failed_hosts = {}
116 for hostname in hostnames:
117 result, error = discovery.discover_on_host(how, hostname, do_snmp_scan, use_caches,
118 on_error)
119 counts[hostname] = [
120 result["self_new"], result["self_removed"], result["self_kept"],
121 result["self_total"]
124 if error is not None:
125 failed_hosts[hostname] = error
126 else:
127 self._trigger_discovery_check(hostname)
129 return counts, failed_hosts
132 automations.register(AutomationDiscovery())
135 # Python 3? use contextlib.redirect_stdout
136 @contextlib.contextmanager
137 def redirect_output(where):
138 """Redirects stdout/stderr to the given file like object"""
139 prev_stdout, prev_stderr = sys.stdout, sys.stderr
140 prev_stdout.flush()
141 prev_stderr.flush()
142 sys.stdout = sys.stderr = where
143 try:
144 yield where
145 finally:
146 where.flush()
147 sys.stdout, sys.stderr = prev_stdout, prev_stderr
150 class AutomationTryDiscovery(Automation):
151 cmd = "try-inventory" # TODO: Rename!
152 needs_config = True
153 needs_checks = True # TODO: Can we change this?
155 def execute(self, args):
156 with redirect_output(cStringIO.StringIO()) as buf:
157 cmk.utils.log.setup_console_logging()
158 cmk.utils.log.set_verbosity(1)
159 result = self._execute_discovery(args)
160 return {"output": buf.getvalue(), "check_table": result}
162 def _execute_discovery(self, args):
163 use_caches = False
164 do_snmp_scan = False
165 if args[0] == '@noscan':
166 args = args[1:]
167 do_snmp_scan = False
168 use_caches = True
169 data_sources.abstract.DataSource.set_use_outdated_cache_file()
170 data_sources.tcp.TCPDataSource.use_only_cache()
172 elif args[0] == '@scan':
173 args = args[1:]
174 do_snmp_scan = True
175 use_caches = False
177 if args[0] == '@raiseerrors':
178 on_error = "raise"
179 args = args[1:]
180 else:
181 on_error = "ignore"
183 data_sources.abstract.DataSource.set_may_use_cache_file(use_caches)
184 hostname = args[0]
185 table = discovery.get_check_preview(
186 hostname, use_caches=use_caches, do_snmp_scan=do_snmp_scan, on_error=on_error)
188 # Content of one row
189 # check_source, check_plugin_name, checkgroup, item, paramstring, params, descr, exitcode, output, perfdata
191 now = time.time()
192 for idx, row in enumerate(table):
193 params = row[5]
194 # This isinstance check is also done within determine check_params,
195 # but the explicit check here saves performance
196 if isinstance(params, cmk_base.config.TimespecificParamList):
197 new_params = cmk_base.checking.determine_check_params(params)
198 # Since the row is a tuple, we cannot simply replace an entry..
199 new_row = list(row)
200 new_row[5] = {"tp_computed_params": {"params": new_params, "computed_at": now}}
201 table[idx] = tuple(new_row)
203 return table
206 automations.register(AutomationTryDiscovery())
209 class AutomationSetAutochecks(DiscoveryAutomation):
210 cmd = "set-autochecks"
211 needs_config = True
212 needs_checks = True # TODO: Can we change this?
214 # Set the new list of autochecks. This list is specified by a
215 # table of (checktype, item). No parameters are specified. Those
216 # are either (1) kept from existing autochecks or (2) computed
217 # from a new inventory. Note: we must never convert check parameters
218 # from python source code to actual values.
219 def execute(self, args):
220 hostname = args[0]
221 new_items = ast.literal_eval(sys.stdin.read())
222 discovery.set_autochecks_of(hostname, new_items)
223 self._trigger_discovery_check(hostname)
224 return None
227 automations.register(AutomationSetAutochecks())
230 # TODO: Is this automation still needed?
231 class AutomationGetAutochecks(Automation):
232 cmd = "get-autochecks"
233 needs_config = True
234 needs_checks = True # TODO: Can we change this?
236 def execute(self, args):
237 hostname = args[0]
238 result = []
239 for ct, item, paramstring in discovery.parse_autochecks_file(hostname):
240 result.append((ct, item, discovery.resolve_paramstring(ct, paramstring), paramstring))
241 return result
244 automations.register(AutomationGetAutochecks())
247 class AutomationRenameHosts(Automation):
248 cmd = "rename-hosts"
249 needs_config = True
250 needs_checks = True
252 def __init__(self):
253 super(AutomationRenameHosts, self).__init__()
254 self._finished_history_files = {}
256 # WATO calls this automation when hosts have been renamed. We need to change
257 # several file and directory names. This function has no argument but reads
258 # Python pair-list from stdin:
259 # [("old1", "new1"), ("old2", "new2")])
260 def execute(self, args):
261 renamings = ast.literal_eval(sys.stdin.read())
263 actions = []
265 # The history archive can be renamed with running core. We need to keep
266 # the list of already handled history archive files, because a new history
267 # file may be created by the core during this step. All unhandled files,
268 # including the current history files will be handled later when the core
269 # is stopped.
270 for oldname, newname in renamings:
271 self._finished_history_files[(oldname, newname)] = \
272 self._rename_host_in_core_history_archive(oldname, newname)
273 if self._finished_history_files[(oldname, newname)]:
274 actions.append("history")
276 # At this place WATO already has changed it's configuration. All further
277 # data might be changed by the still running core. So we need to stop
278 # it now.
279 core_was_running = self._core_is_running()
280 if core_was_running:
281 cmk_base.core.do_core_action("stop", quiet=True)
283 try:
284 for oldname, newname in renamings:
285 # Autochecks: simply read and write out the file again. We do
286 # not store a host name here anymore - but old versions did.
287 # by rewriting we get rid of the host name.
288 actions += self._rename_host_autochecks(oldname, newname)
289 actions += self._rename_host_files(oldname, newname)
290 finally:
291 # Start monitoring again
292 if core_was_running:
293 # force config generation to succeed. The core *must* start.
294 # TODO: Can't we drop this hack since we have config warnings now?
295 core_config.ignore_ip_lookup_failures()
296 # TODO: Clean this up!
297 restart = AutomationRestart()
298 restart._mode = lambda: "start"
299 restart.execute([])
301 for hostname in core_config.failed_ip_lookups():
302 actions.append("dnsfail-" + hostname)
304 # Convert actions into a dictionary { "what" : count }
305 action_counts = {}
306 for action in actions:
307 action_counts.setdefault(action, 0)
308 action_counts[action] += 1
310 return action_counts
312 def _core_is_running(self):
313 if config.monitoring_core == "nagios":
314 command = cmk.utils.paths.nagios_startscript + " status >/dev/null 2>&1"
315 else:
316 command = "omd status cmc >/dev/null 2>&1"
317 code = os.system(command) # nosec
318 return not code
320 def _rename_host_autochecks(self, oldname, newname):
321 actions = []
322 acpath = cmk.utils.paths.autochecks_dir + "/" + oldname + ".mk"
323 if os.path.exists(acpath):
324 old_autochecks = discovery.parse_autochecks_file(oldname)
325 out = file(cmk.utils.paths.autochecks_dir + "/" + newname + ".mk", "w")
326 out.write("[\n")
327 for ct, item, paramstring in old_autochecks:
328 out.write(" (%r, %r, %s),\n" % (ct, item, paramstring))
329 out.write("]\n")
330 out.close()
331 os.remove(acpath) # Remove old file
332 actions.append("autochecks")
333 return actions
335 def _rename_host_files(self, oldname, newname):
336 actions = []
338 # Rename temporary files of the host
339 for d in ["cache", "counters"]:
340 if self._rename_host_file(cmk.utils.paths.tmp_dir + "/" + d + "/", oldname, newname):
341 actions.append(d)
343 if self._rename_host_dir(cmk.utils.paths.tmp_dir + "/piggyback/", oldname, newname):
344 actions.append("piggyback-load")
346 # Rename piggy files *created* by the host
347 piggybase = cmk.utils.paths.tmp_dir + "/piggyback/"
348 if os.path.exists(piggybase):
349 for piggydir in os.listdir(piggybase):
350 if self._rename_host_file(piggybase + piggydir, oldname, newname):
351 actions.append("piggyback-pig")
353 # Logwatch
354 if self._rename_host_dir(cmk.utils.paths.logwatch_dir, oldname, newname):
355 actions.append("logwatch")
357 # SNMP walks
358 if self._rename_host_file(cmk.utils.paths.snmpwalks_dir, oldname, newname):
359 actions.append("snmpwalk")
361 # HW/SW-Inventory
362 if self._rename_host_file(cmk.utils.paths.var_dir + "/inventory", oldname, newname):
363 self._rename_host_file(cmk.utils.paths.var_dir + "/inventory", oldname + ".gz",
364 newname + ".gz")
365 actions.append("inv")
367 if self._rename_host_dir(cmk.utils.paths.var_dir + "/inventory_archive", oldname, newname):
368 actions.append("invarch")
370 # Baked agents
371 baked_agents_dir = cmk.utils.paths.var_dir + "/agents/"
372 have_renamed_agent = False
373 if os.path.exists(baked_agents_dir):
374 for opsys in os.listdir(baked_agents_dir):
375 if self._rename_host_file(baked_agents_dir + opsys, oldname, newname):
376 have_renamed_agent = True
377 if have_renamed_agent:
378 actions.append("agent")
380 # Agent deployment
381 deployment_dir = cmk.utils.paths.var_dir + "/agent_deployment/"
382 if self._rename_host_file(deployment_dir, oldname, newname):
383 actions.append("agent_deployment")
385 actions += self._omd_rename_host(oldname, newname)
387 return actions
389 def _rename_host_dir(self, basedir, oldname, newname):
390 if os.path.exists(basedir + "/" + oldname):
391 if os.path.exists(basedir + "/" + newname):
392 shutil.rmtree(basedir + "/" + newname)
393 os.rename(basedir + "/" + oldname, basedir + "/" + newname)
394 return 1
395 return 0
397 def _rename_host_file(self, basedir, oldname, newname):
398 if os.path.exists(basedir + "/" + oldname):
399 if os.path.exists(basedir + "/" + newname):
400 os.remove(basedir + "/" + newname)
401 os.rename(basedir + "/" + oldname, basedir + "/" + newname)
402 return 1
403 return 0
405 # This functions could be moved out of Check_MK.
406 def _omd_rename_host(self, oldname, newname):
407 oldregex = self._escape_name_for_regex_matching(oldname)
408 actions = []
410 # Temporarily stop processing of performance data
411 npcd_running = os.path.exists(cmk.utils.paths.omd_root + "/tmp/pnp4nagios/run/npcd.pid")
412 if npcd_running:
413 os.system("omd stop npcd >/dev/null 2>&1 </dev/null")
415 rrdcache_running = os.path.exists(cmk.utils.paths.omd_root + "/tmp/run/rrdcached.sock")
416 if rrdcache_running:
417 os.system("omd stop rrdcached >/dev/null 2>&1 </dev/null")
419 try:
420 # Fix pathnames in XML files
421 self.rename_host_in_files(
422 os.path.join(cmk.utils.paths.omd_root, "var/pnp4nagios/perfdata", oldname, "*.xml"),
423 "/perfdata/%s/" % oldregex, "/perfdata/%s/" % newname)
425 # RRD files
426 if self._rename_host_dir(cmk.utils.paths.omd_root + "/var/pnp4nagios/perfdata", oldname,
427 newname):
428 actions.append("rrd")
430 # RRD files
431 if self._rename_host_dir(cmk.utils.paths.omd_root + "/var/check_mk/rrd", oldname,
432 newname):
433 actions.append("rrd")
435 # entries of rrdcached journal
436 if self.rename_host_in_files(
437 os.path.join(cmk.utils.paths.omd_root, "var/rrdcached/rrd.journal.*"),
438 "/(perfdata|rrd)/%s/" % oldregex,
439 "/\\1/%s/" % newname,
440 extended_regex=True):
441 actions.append("rrdcached")
443 # Spoolfiles of NPCD
444 if self.rename_host_in_files("%s/var/pnp4nagios/perfdata.dump" % cmk.utils.paths.omd_root,
445 "HOSTNAME::%s " % oldregex,
446 "HOSTNAME::%s " % newname) or \
447 self.rename_host_in_files("%s/var/pnp4nagios/spool/perfdata.*" % cmk.utils.paths.omd_root,
448 "HOSTNAME::%s " % oldregex,
449 "HOSTNAME::%s " % newname):
450 actions.append("pnpspool")
451 finally:
452 if rrdcache_running:
453 os.system("omd start rrdcached >/dev/null 2>&1 </dev/null")
455 if npcd_running:
456 os.system("omd start npcd >/dev/null 2>&1 </dev/null")
458 self._rename_host_in_remaining_core_history_files(oldname, newname)
460 # State retention (important for Downtimes, Acknowledgements, etc.)
461 if config.monitoring_core == "nagios":
462 if self.rename_host_in_files(
463 "%s/var/nagios/retention.dat" % cmk.utils.paths.omd_root,
464 "^host_name=%s$" % oldregex,
465 "host_name=%s" % newname,
466 extended_regex=True):
467 actions.append("retention")
469 else: # CMC
470 # Create a file "renamed_hosts" with the information about the
471 # renaming of the hosts. The core will honor this file when it
472 # reads the status file with the saved state.
473 file(cmk.utils.paths.var_dir + "/core/renamed_hosts",
474 "w").write("%s\n%s\n" % (oldname, newname))
475 actions.append("retention")
477 # NagVis maps
478 if self.rename_host_in_files(
479 "%s/etc/nagvis/maps/*.cfg" % cmk.utils.paths.omd_root,
480 "^[[:space:]]*host_name=%s[[:space:]]*$" % oldregex,
481 "host_name=%s" % newname,
482 extended_regex=True):
483 actions.append("nagvis")
485 return actions
487 def _rename_host_in_remaining_core_history_files(self, oldname, newname):
488 """Perform the rename operation in all history archive files that have not been handled yet"""
489 finished_file_paths = self._finished_history_files[(oldname, newname)]
490 all_file_paths = set(self._get_core_history_files(only_archive=False))
491 todo_file_paths = list(all_file_paths.difference(finished_file_paths))
492 return self._rename_host_in_core_history_files(todo_file_paths, oldname, newname)
494 def _rename_host_in_core_history_archive(self, oldname, newname):
495 """Perform the rename operation in all history archive files"""
496 file_paths = self._get_core_history_files(only_archive=True)
497 return self._rename_host_in_core_history_files(file_paths, oldname, newname)
499 def _get_core_history_files(self, only_archive):
500 path_patterns = [
501 "var/check_mk/core/archive/*",
502 "var/nagios/archive/*",
505 if not only_archive:
506 path_patterns += [
507 "var/check_mk/core/history",
508 "var/nagios/nagios.log",
511 file_paths = []
512 for path_pattern in path_patterns:
513 file_paths += glob.glob("%s/%s" % (cmk.utils.paths.omd_root, path_pattern))
514 return file_paths
516 def _rename_host_in_core_history_files(self, file_paths, oldname, newname):
517 oldregex = self._escape_name_for_regex_matching(oldname)
519 # Logfiles and history files of CMC and Nagios. Problem
520 # here: the exact place of the hostname varies between the
521 # various log entry lines
522 sed_commands = r'''
523 s/(INITIAL|CURRENT) (HOST|SERVICE) STATE: %(old)s;/\1 \2 STATE: %(new)s;/
524 s/(HOST|SERVICE) (DOWNTIME |FLAPPING |)ALERT: %(old)s;/\1 \2ALERT: %(new)s;/
525 s/PASSIVE (HOST|SERVICE) CHECK: %(old)s;/PASSIVE \1 CHECK: %(new)s;/
526 s/(HOST|SERVICE) NOTIFICATION: ([^;]+);%(old)s;/\1 NOTIFICATION: \2;%(new)s;/
527 ''' % {
528 "old": oldregex,
529 "new": newname
532 handled_files = []
534 command = ["sed", "-ri", "--file=/dev/fd/0"]
535 p = subprocess.Popen(
536 command + file_paths,
537 stdin=subprocess.PIPE,
538 stdout=open(os.devnull, "w"),
539 stderr=subprocess.STDOUT,
540 close_fds=True)
541 p.communicate(sed_commands)
542 # TODO: error handling?
544 handled_files += file_paths
546 return handled_files
548 # Returns True in case files were found, otherwise False
549 def rename_host_in_files(self, path_pattern, old, new, extended_regex=False):
550 paths = glob.glob(path_pattern)
551 if paths:
552 extended = ["-r"] if extended_regex else []
553 subprocess.call(
554 ["sed", "-i"] + extended + ["s@%s@%s@" % (old, new)] + paths,
555 stderr=open(os.devnull, "w"))
556 return True
558 return False
560 def _escape_name_for_regex_matching(self, name):
561 return name.replace(".", "[.]")
564 automations.register(AutomationRenameHosts())
567 class AutomationAnalyseServices(Automation):
568 cmd = "analyse-service"
569 needs_config = True
570 needs_checks = True # TODO: Can we change this?
572 def execute(self, args):
573 config_cache = config.get_config_cache()
574 hostname = args[0]
575 servicedesc = args[1].decode("utf-8")
577 service_info = self._get_service_info(config_cache, hostname, servicedesc)
578 if service_info:
579 service_info.update({
580 "labels": config_cache.labels_of_service(hostname, servicedesc),
581 "label_sources": config_cache.label_sources_of_service(hostname, servicedesc),
583 return service_info
585 # Determine the type of the check, and how the parameters are being
586 # constructed
587 # TODO: Refactor this huge function
588 # TODO: Was ist mit Clustern???
589 # TODO: Klappt das mit automatischen verschatten von SNMP-Checks (bei dual Monitoring)
590 def _get_service_info(self, config_cache, hostname, servicedesc):
591 check_api_utils.set_hostname(hostname)
593 # We just consider types of checks that are managed via WATO.
594 # We have the following possible types of services:
595 # 1. manual checks (static_checks) (currently overriding inventorized checks)
596 # 2. inventorized check
597 # 3. classical checks
598 # 4. active checks
600 # Compute effective check table, in order to remove SNMP duplicates
601 table = check_table.get_check_table(hostname, remove_duplicates=True)
603 # 1. Manual checks
604 for checkgroup_name in config.static_checks:
605 for value in self.static_check_rules_of(checkgroup_name, hostname):
606 # Parameters are optional
607 if len(value) == 2:
608 checktype, item = value
609 params = None
610 else:
611 checktype, item, params = value
613 descr = config.service_description(hostname, checktype, item)
614 if descr == servicedesc:
615 return {
616 "origin": "static",
617 "checkgroup": checkgroup_name,
618 "checktype": checktype,
619 "item": item,
620 "parameters": params,
623 # TODO: There is a lot of duplicated logic with discovery.py/check_table.py. Clean this
624 # whole function up.
625 if config.is_cluster(hostname):
626 autochecks = []
627 for node in config.nodes_of(hostname):
628 for check_plugin_name, item, paramstring in cmk_base.autochecks.read_autochecks_of(
629 node):
630 descr = config.service_description(node, check_plugin_name, item)
631 if hostname == config_cache.host_of_clustered_service(node, descr):
632 autochecks.append((check_plugin_name, item, paramstring))
633 else:
634 autochecks = cmk_base.autochecks.read_autochecks_of(hostname)
636 # 2. Load all autochecks of the host in question and try to find
637 # our service there
638 for entry in autochecks:
639 ct, item, params = entry # new format without host name
641 if (ct, item) not in table:
642 continue # this is a removed duplicate or clustered service
644 descr = config.service_description(hostname, ct, item)
645 if descr == servicedesc:
646 dlv = config.check_info[ct].get("default_levels_variable")
647 if dlv:
648 fs = config.factory_settings.get(dlv, None)
649 else:
650 fs = None
652 check_parameters = config.compute_check_parameters(hostname, ct, item, params)
653 if isinstance(check_parameters, cmk_base.config.TimespecificParamList):
654 check_parameters = cmk_base.checking.determine_check_params(check_parameters)
655 check_parameters = {
656 "tp_computed_params": {
657 "params": check_parameters,
658 "computed_at": time.time()
662 return {
663 "origin": "auto",
664 "checktype": ct,
665 "checkgroup": config.check_info[ct].get("group"),
666 "item": item,
667 "inv_parameters": params,
668 "factory_settings": fs,
669 "parameters": check_parameters,
672 # 3. Classical checks
673 for nr, entry in enumerate(config.custom_checks):
674 if len(entry) == 4:
675 rule, tags, hosts, options = entry
676 if options.get("disabled"):
677 continue
678 else:
679 rule, tags, hosts = entry
681 matching_hosts = config.all_matching_hosts(tags, hosts, with_foreign_hosts=True)
682 if hostname in matching_hosts:
683 desc = rule["service_description"]
684 if desc == servicedesc:
685 result = {
686 "origin": "classic",
687 "rule_nr": nr,
689 if "command_line" in rule: # Only active checks have a command line
690 result["command_line"] = rule["command_line"]
691 return result
693 # 4. Active checks
694 for acttype, rules in config.active_checks.items():
695 entries = config_cache.host_extra_conf(hostname, rules)
696 if entries:
697 for params in entries:
698 description = config.active_check_service_description(hostname, acttype, params)
699 if description == servicedesc:
700 return {
701 "origin": "active",
702 "checktype": acttype,
703 "parameters": params,
706 return {} # not found
708 def static_check_rules_of(self, checkgroup_name, hostname):
709 config_cache = config.get_config_cache()
710 return config_cache.host_extra_conf(hostname, config.static_checks.get(checkgroup_name, []))
713 automations.register(AutomationAnalyseServices())
716 class AutomationAnalyseHost(Automation):
717 cmd = "analyse-host"
718 needs_config = True
719 needs_checks = False
721 def execute(self, args):
722 host_name = args[0]
723 config_cache = config.get_config_cache()
724 return {
725 "labels": config_cache.get_host_config(host_name).labels,
726 "label_sources": config_cache.get_host_config(host_name).label_sources,
730 automations.register(AutomationAnalyseHost())
733 class AutomationDeleteHosts(Automation):
734 cmd = "delete-hosts"
735 needs_config = True
736 needs_checks = True # TODO: Can we change this?
738 def execute(self, args):
739 for hostname in args:
740 self._delete_host_files(hostname)
741 return None
743 def _delete_host_files(self, hostname):
744 # The inventory_archive as well as the performance data is kept
745 # we do not want to loose any historic data for accidently deleted hosts.
747 # These files are cleaned up by the disk space mechanism.
749 # single files
750 for path in [
751 "%s/%s" % (cmk.utils.paths.precompiled_hostchecks_dir, hostname),
752 "%s/%s.py" % (cmk.utils.paths.precompiled_hostchecks_dir, hostname),
753 "%s/%s.mk" % (cmk.utils.paths.autochecks_dir, hostname),
754 "%s/%s" % (cmk.utils.paths.counters_dir, hostname),
755 "%s/%s" % (cmk.utils.paths.tcp_cache_dir, hostname),
756 "%s/persisted/%s" % (cmk.utils.paths.var_dir, hostname),
757 "%s/inventory/%s" % (cmk.utils.paths.var_dir, hostname),
758 "%s/inventory/%s.gz" % (cmk.utils.paths.var_dir, hostname),
759 "%s/agent_deployment/%s" % (cmk.utils.paths.var_dir, hostname),
761 if os.path.exists(path):
762 os.unlink(path)
764 try:
765 ds_directories = os.listdir(cmk.utils.paths.data_source_cache_dir)
766 except OSError as e:
767 if e.errno == 2:
768 ds_directories = []
769 else:
770 raise
772 for data_source_name in ds_directories:
773 filename = "%s/%s/%s" % (cmk.utils.paths.data_source_cache_dir, data_source_name,
774 hostname)
775 try:
776 os.unlink(filename)
777 except OSError as e:
778 if e.errno == 2:
779 pass
780 else:
781 raise
783 # softlinks for baked agents. obsolete packages are removed upon next bake action
784 # TODO: Move to bakery code
785 baked_agents_dir = cmk.utils.paths.var_dir + "/agents/"
786 if os.path.exists(baked_agents_dir):
787 for folder in os.listdir(baked_agents_dir):
788 if os.path.exists("%s/%s" % (folder, hostname)):
789 os.unlink("%s/%s" % (folder, hostname))
791 # logwatch and piggyback folders
792 for what_dir in [
793 "%s/%s" % (cmk.utils.paths.logwatch_dir, hostname),
794 "%s/piggyback/%s" % (cmk.utils.paths.tmp_dir, hostname),
796 if os.path.exists(what_dir):
797 shutil.rmtree(what_dir)
799 return None
802 automations.register(AutomationDeleteHosts())
805 class AutomationRestart(Automation):
806 cmd = "restart"
807 needs_config = True
808 needs_checks = True # TODO: Can we change this?
810 def _mode(self):
811 if config.monitoring_core == "cmc" and not self._check_plugins_have_changed():
812 return "reload" # force reload for cmc
813 return "restart"
815 # TODO: Cleanup duplicate code with cmk_base.core.do_restart()
816 def execute(self, args):
817 # make sure, Nagios does not inherit any open
818 # filedescriptors. This really happens, e.g. if
819 # check_mk is called by WATO via Apache. Nagios inherits
820 # the open file where Apache is listening for incoming
821 # HTTP connections. Really.
822 if config.monitoring_core == "nagios":
823 objects_file = cmk.utils.paths.nagios_objects_file
824 for fd in range(3, 256):
825 try:
826 os.close(fd)
827 except:
828 pass
829 else:
830 objects_file = cmk.utils.paths.var_dir + "/core/config"
832 # os.closerange(3, 256) --> not available in older Python versions
834 class null_file(object):
835 def write(self, stuff):
836 pass
838 def flush(self):
839 pass
841 # Deactivate stdout by introducing fake file without filedescriptor
842 old_stdout = sys.stdout
843 sys.stdout = null_file()
845 try:
846 backup_path = None
847 if cmk_base.core.try_get_activation_lock():
848 raise MKAutomationError("Cannot activate changes. "
849 "Another activation process is currently in progresss")
851 if os.path.exists(objects_file):
852 backup_path = objects_file + ".save"
853 os.rename(objects_file, backup_path)
854 else:
855 backup_path = None
857 core = create_core()
858 try:
859 configuration_warnings = core_config.create_core_config(core)
861 try:
862 from cmk_base.cee.agent_bakery import bake_on_restart
863 bake_on_restart()
864 except ImportError:
865 pass
867 except Exception as e:
868 if backup_path:
869 os.rename(backup_path, objects_file)
870 if cmk.utils.debug.enabled():
871 raise
872 raise MKAutomationError("Error creating configuration: %s" % e)
874 if config.monitoring_core == "cmc" or cmk_base.nagios_utils.do_check_nagiosconfig():
875 if backup_path:
876 os.remove(backup_path)
878 core.precompile()
880 cmk_base.core.do_core_action(self._mode())
881 else:
882 broken_config_path = "%s/check_mk_objects.cfg.broken" % cmk.utils.paths.tmp_dir
883 file(broken_config_path, "w").write(
884 file(cmk.utils.paths.nagios_objects_file).read())
886 if backup_path:
887 os.rename(backup_path, objects_file)
888 else:
889 os.remove(objects_file)
891 raise MKAutomationError(
892 "Configuration for monitoring core is invalid. Rolling back. "
893 "The broken file has been copied to \"%s\" for analysis." % broken_config_path)
895 except Exception as e:
896 if backup_path and os.path.exists(backup_path):
897 os.remove(backup_path)
898 if cmk.utils.debug.enabled():
899 raise
900 raise MKAutomationError(str(e))
902 sys.stdout = old_stdout
903 return configuration_warnings
905 def _check_plugins_have_changed(self):
906 this_time = self._last_modification_in_dir(cmk.utils.paths.local_checks_dir)
907 last_time = self._time_of_last_core_restart()
908 return this_time > last_time
910 def _last_modification_in_dir(self, dir_path):
911 max_time = os.stat(dir_path).st_mtime
912 for file_name in os.listdir(dir_path):
913 max_time = max(max_time, os.stat(dir_path + "/" + file_name).st_mtime)
914 return max_time
916 def _time_of_last_core_restart(self):
917 if config.monitoring_core == "cmc":
918 pidfile_path = cmk.utils.paths.omd_root + "/tmp/run/cmc.pid"
919 else:
920 pidfile_path = cmk.utils.paths.omd_root + "/tmp/lock/nagios.lock"
922 if os.path.exists(pidfile_path):
923 return os.stat(pidfile_path).st_mtime
925 return 0
928 automations.register(AutomationRestart())
931 class AutomationReload(AutomationRestart):
932 cmd = "reload"
934 def _mode(self):
935 if self._check_plugins_have_changed():
936 return "restart"
937 return "reload"
940 automations.register(AutomationReload())
943 class AutomationGetConfiguration(Automation):
944 cmd = "get-configuration"
945 needs_config = False
946 # This needed the checks in the past. This was necessary to get the
947 # default values of check related global settings. This kind of
948 # global settings have been removed from the global settings page
949 # of WATO. We can now disable this (by default).
950 # We need to be careful here, because users may have added their own
951 # global settings related to checks. To deal with this, we check
952 # for requested but missing global variables and load the checks in
953 # case one is missing. When it's still missing then, we silenlty skip
954 # this option (like before).
955 needs_checks = False
957 def execute(self, args):
958 config.load(with_conf_d=False)
960 # We read the list of variable names from stdin since
961 # that could be too much for the command line
962 variable_names = ast.literal_eval(sys.stdin.read())
964 missing_variables = [v for v in variable_names if not hasattr(config, v)]
966 if missing_variables:
967 config.load_all_checks(check_api.get_check_api_context)
968 config.load(with_conf_d=False)
970 result = {}
971 for varname in variable_names:
972 if hasattr(config, varname):
973 value = getattr(config, varname)
974 if not hasattr(value, '__call__'):
975 result[varname] = value
976 return result
979 automations.register(AutomationGetConfiguration())
982 class AutomationGetCheckInformation(Automation):
983 cmd = "get-check-information"
984 needs_config = False
985 needs_checks = True
987 def execute(self, args):
988 manuals = man_pages.all_man_pages()
990 check_infos = {}
991 for check_plugin_name, check in config.check_info.items():
992 try:
993 manfile = manuals.get(check_plugin_name)
994 # TODO: Use cmk.utils.man_pages module standard functions to read the title
995 if manfile:
996 title = file(manfile).readline().strip().split(":", 1)[1].strip()
997 else:
998 title = check_plugin_name
999 check_infos[check_plugin_name] = {"title": title.decode("utf-8")}
1000 if check["group"]:
1001 check_infos[check_plugin_name]["group"] = check["group"]
1002 check_infos[check_plugin_name]["service_description"] = check.get(
1003 "service_description", "%s")
1004 check_infos[check_plugin_name]["snmp"] = cmk_base.check_utils.is_snmp_check(
1005 check_plugin_name)
1006 except Exception as e:
1007 if cmk.utils.debug.enabled():
1008 raise
1009 raise MKAutomationError(
1010 "Failed to parse man page '%s': %s" % (check_plugin_name, e))
1011 return check_infos
1014 automations.register(AutomationGetCheckInformation())
1017 class AutomationGetRealTimeChecks(Automation):
1018 cmd = "get-real-time-checks"
1019 needs_config = False
1020 needs_checks = True
1022 def execute(self, args):
1023 manuals = man_pages.all_man_pages()
1025 rt_checks = []
1026 for check_plugin_name, check in config.check_info.items():
1027 if check["handle_real_time_checks"]:
1028 # TODO: Use cmk.utils.man_pages module standard functions to read the title
1029 title = check_plugin_name
1030 try:
1031 manfile = manuals.get(check_plugin_name)
1032 if manfile:
1033 title = file(manfile).readline().strip().split(":", 1)[1].strip()
1034 except Exception:
1035 if cmk.utils.debug.enabled():
1036 raise
1038 rt_checks.append((check_plugin_name,
1039 "%s - %s" % (check_plugin_name, title.decode("utf-8"))))
1041 return rt_checks
1044 automations.register(AutomationGetRealTimeChecks())
1047 class AutomationGetCheckManPage(Automation):
1048 cmd = "get-check-manpage"
1049 needs_config = False
1050 needs_checks = True
1052 def execute(self, args):
1053 if len(args) != 1:
1054 raise MKAutomationError("Need exactly one argument.")
1056 check_plugin_name = args[0]
1057 manpage = man_pages.load_man_page(args[0])
1059 # Add a few informations from check_info. Note: active checks do not
1060 # have an entry in check_info
1061 if check_plugin_name in config.check_info:
1062 manpage["type"] = "check_mk"
1063 info = config.check_info[check_plugin_name]
1064 for key in ["snmp_info", "has_perfdata", "service_description"]:
1065 if key in info:
1066 manpage[key] = info[key]
1067 if "." in check_plugin_name:
1068 section_name = cmk_base.check_utils.section_name_of(check_plugin_name)
1069 if section_name in config.check_info and "snmp_info" in config.check_info[
1070 section_name]:
1071 manpage["snmp_info"] = config.check_info[section_name]["snmp_info"]
1073 if "group" in info:
1074 manpage["group"] = info["group"]
1076 # Assume active check
1077 elif check_plugin_name.startswith("check_"):
1078 manpage["type"] = "active"
1079 else:
1080 raise MKAutomationError("Could not detect type of manpage: %s. "
1081 "Maybe the check is missing." % check_plugin_name)
1083 return manpage
1086 automations.register(AutomationGetCheckManPage())
1089 class AutomationScanParents(Automation):
1090 cmd = "scan-parents"
1091 needs_config = True
1092 needs_checks = True
1094 def execute(self, args):
1095 settings = {
1096 "timeout": int(args[0]),
1097 "probes": int(args[1]),
1098 "max_ttl": int(args[2]),
1099 "ping_probes": int(args[3]),
1101 hostnames = args[4:]
1102 if not cmk_base.parent_scan.traceroute_available():
1103 raise MKAutomationError("Cannot find binary <tt>traceroute</tt> in search path.")
1105 try:
1106 gateways = cmk_base.parent_scan.scan_parents_of(
1107 hostnames, silent=True, settings=settings)
1108 return gateways
1109 except Exception as e:
1110 raise MKAutomationError("%s" % e)
1113 automations.register(AutomationScanParents())
1116 class AutomationDiagHost(Automation):
1117 cmd = "diag-host"
1118 needs_config = True
1119 needs_checks = True
1121 def execute(self, args):
1122 hostname, test, ipaddress, snmp_community = args[:4]
1123 agent_port, snmp_timeout, snmp_retries = map(int, args[4:7])
1125 # In 1.5 the tcp connect timeout has been added. The automation may
1126 # be called from a remote site with an older version. For this reason
1127 # we need to deal with the old args.
1128 if len(args) == 14:
1129 tcp_connect_timeout = None
1130 cmd = args[7]
1131 else:
1132 tcp_connect_timeout = float(args[7])
1133 cmd = args[8]
1135 snmpv3_use = None
1136 snmpv3_auth_proto = None
1137 snmpv3_security_name = None
1138 snmpv3_security_password = None
1139 snmpv3_privacy_proto = None
1140 snmpv3_privacy_password = None
1142 if len(args) > 9:
1143 snmpv3_use = args[9]
1144 if snmpv3_use in ["authNoPriv", "authPriv"]:
1145 snmpv3_auth_proto, snmpv3_security_name, snmpv3_security_password = args[10:13]
1146 else:
1147 snmpv3_security_name = args[11]
1148 if snmpv3_use == "authPriv":
1149 snmpv3_privacy_proto, snmpv3_privacy_password = args[13:15]
1151 if not ipaddress:
1152 try:
1153 ipaddress = ip_lookup.lookup_ip_address(hostname)
1154 except:
1155 raise MKGeneralException("Cannot resolve hostname %s into IP address" % hostname)
1157 ipv6_primary = config.is_ipv6_primary(hostname)
1159 try:
1160 if test == 'ping':
1161 base_cmd = "ping6" if ipv6_primary else "ping"
1162 p = subprocess.Popen([base_cmd, "-A", "-i", "0.2", "-c", "2", "-W", "5", ipaddress],
1163 stdout=subprocess.PIPE,
1164 stderr=subprocess.STDOUT)
1165 response = p.stdout.read()
1166 return (p.wait(), response)
1168 elif test == 'agent':
1169 sources = data_sources.DataSources(hostname, ipaddress)
1170 sources.set_max_cachefile_age(config.check_max_cachefile_age)
1172 output = ""
1173 for source in sources.get_data_sources():
1174 if isinstance(source, data_sources.DSProgramDataSource) and cmd:
1175 source = data_sources.DSProgramDataSource(hostname, ipaddress, cmd)
1176 elif isinstance(source, data_sources.TCPDataSource):
1177 source.set_port(agent_port)
1178 if tcp_connect_timeout is not None:
1179 source.set_timeout(tcp_connect_timeout)
1181 output += source.run_raw()
1182 if source.exception():
1183 output += "%s" % source.exception()
1185 return 0, output
1187 elif test == 'traceroute':
1188 family_flag = "-6" if ipv6_primary else "-4"
1189 try:
1190 p = subprocess.Popen(['traceroute', family_flag, '-n', ipaddress],
1191 stdout=subprocess.PIPE,
1192 stderr=subprocess.STDOUT)
1193 except OSError as e:
1194 if e.errno == 2:
1195 return 1, "Cannot find binary <tt>traceroute</tt>."
1196 else:
1197 raise
1198 response = p.stdout.read()
1199 return (p.wait(), response)
1201 elif test.startswith('snmp'):
1202 # SNMPv3 tuples
1203 # ('noAuthNoPriv', "username")
1204 # ('authNoPriv', 'md5', '11111111', '22222222')
1205 # ('authPriv', 'md5', '11111111', '22222222', 'DES', '33333333')
1207 credentials = config.snmp_credentials_of(hostname)
1209 # Insert preconfigured communitiy
1210 if test == "snmpv3":
1211 if snmpv3_use:
1212 snmpv3_credentials = [snmpv3_use]
1213 if snmpv3_use in ["authNoPriv", "authPriv"]:
1214 snmpv3_credentials.extend(
1215 [snmpv3_auth_proto, snmpv3_security_name, snmpv3_security_password])
1216 else:
1217 snmpv3_credentials.extend([snmpv3_security_name])
1218 if snmpv3_use == "authPriv":
1219 snmpv3_credentials.extend(
1220 [snmpv3_privacy_proto, snmpv3_privacy_password])
1221 credentials = tuple(snmpv3_credentials)
1222 elif snmp_community:
1223 credentials = snmp_community
1225 # Determine SNMPv2/v3 community
1226 if hostname not in config.explicit_snmp_communities:
1227 communities = config.host_extra_conf(hostname, config.snmp_communities)
1228 for entry in communities:
1229 if (test == "snmpv3") and not isinstance(entry, tuple):
1230 continue
1232 if (test != "snmpv3") and isinstance(entry, tuple):
1233 continue
1235 credentials = entry
1236 break
1238 # SNMP versions
1239 if test in ['snmpv2', 'snmpv3']:
1240 is_bulkwalk_host = True
1241 is_snmpv2or3_without_bulkwalk_host = False
1242 elif test == 'snmpv2_nobulk':
1243 is_bulkwalk_host = False
1244 is_snmpv2or3_without_bulkwalk_host = True
1245 elif test == 'snmpv1':
1246 is_bulkwalk_host = False
1247 is_snmpv2or3_without_bulkwalk_host = False
1249 else:
1250 return 1, "SNMP command not implemented"
1252 #TODO: What about SNMP management boards?
1253 host_config = snmp_utils.SNMPHostConfig(
1254 is_ipv6_primary=ipv6_primary,
1255 hostname=hostname,
1256 ipaddress=ipaddress,
1257 credentials=credentials,
1258 port=config.snmp_port_of(hostname),
1259 is_bulkwalk_host=is_bulkwalk_host,
1260 is_snmpv2or3_without_bulkwalk_host=is_snmpv2or3_without_bulkwalk_host,
1261 bulk_walk_size_of=config.bulk_walk_size_of(hostname),
1262 timing={
1263 'timeout': snmp_timeout,
1264 'retries': snmp_retries,
1266 oid_range_limits=config.oid_range_limits_of(hostname),
1268 data = snmp.get_snmp_table(
1269 host_config,
1270 None, ('.1.3.6.1.2.1.1', ['1.0', '4.0', '5.0', '6.0']),
1271 use_snmpwalk_cache=True)
1273 if data:
1274 return 0, 'sysDescr:\t%s\nsysContact:\t%s\nsysName:\t%s\nsysLocation:\t%s\n' % tuple(
1275 data[0])
1277 return 1, 'Got empty SNMP response'
1279 else:
1280 return 1, "Command not implemented"
1282 except Exception as e:
1283 if cmk.utils.debug.enabled():
1284 raise
1285 return 1, str(e)
1288 automations.register(AutomationDiagHost())
1291 class AutomationActiveCheck(Automation):
1292 cmd = "active-check"
1293 needs_config = True
1294 needs_checks = True
1296 def execute(self, args):
1297 hostname, plugin, item = args
1298 item = item.decode("utf-8")
1300 if plugin == "custom":
1301 custchecks = config.host_extra_conf(hostname, config.custom_checks)
1302 for entry in custchecks:
1303 if entry["service_description"] == item:
1304 command_line = self._replace_core_macros(hostname, entry.get(
1305 "command_line", ""))
1306 if command_line:
1307 command_line = core_config.autodetect_plugin(command_line)
1308 return self._execute_check_plugin(command_line)
1310 return -1, "Passive check - cannot be executed"
1311 else:
1312 rules = config.active_checks.get(plugin)
1313 if rules:
1314 entries = config.host_extra_conf(hostname, rules)
1315 if entries:
1316 act_info = config.active_check_info[plugin]
1317 for params in entries:
1318 description = config.active_check_service_description(
1319 hostname, plugin, params)
1320 if description == item:
1321 args = core_config.active_check_arguments(
1322 hostname, description, act_info["argument_function"](params))
1323 command_line = self._replace_core_macros(
1324 hostname, act_info["command_line"].replace("$ARG1$", args))
1325 return self._execute_check_plugin(command_line)
1327 def _load_resource_file(self, macros):
1328 try:
1329 for line in file(cmk.utils.paths.omd_root + "/etc/nagios/resource.cfg"):
1330 line = line.strip()
1331 if not line or line[0] == '#':
1332 continue
1333 varname, value = line.split('=', 1)
1334 macros[varname] = value
1335 except:
1336 if cmk.utils.debug.enabled():
1337 raise
1339 # Simulate replacing some of the more important macros of hosts. We
1340 # cannot use dynamic macros, of course. Note: this will not work
1341 # without OMD, since we do not know the value of $USER1$ and $USER2$
1342 # here. We could read the Nagios resource.cfg file, but we do not
1343 # know for sure the place of that either.
1344 def _replace_core_macros(self, hostname, commandline):
1345 config_cache = config.get_config_cache()
1346 macros = core_config.get_host_macros_from_attributes(
1347 hostname, core_config.get_host_attributes(hostname, config_cache))
1348 self._load_resource_file(macros)
1349 for varname, value in macros.items():
1350 commandline = commandline.replace(varname, "%s" % value)
1351 return commandline
1353 def _execute_check_plugin(self, commandline):
1354 try:
1355 p = os.popen(commandline + " 2>&1") # nosec
1356 output = p.read().strip()
1357 ret = p.close()
1358 if not ret:
1359 status = 0
1360 else:
1361 if ret & 0xff == 0:
1362 status = ret / 256
1363 else:
1364 status = 3
1365 if status < 0 or status > 3:
1366 status = 3
1367 output = output.split("|", 1)[0] # Drop performance data
1368 return status, output
1370 except Exception as e:
1371 if cmk.utils.debug.enabled():
1372 raise
1373 return 3, "UNKNOWN - Cannot execute command: %s" % e
1376 automations.register(AutomationActiveCheck())
1379 class AutomationUpdateDNSCache(Automation):
1380 cmd = "update-dns-cache"
1381 needs_config = True
1382 needs_checks = True # TODO: Can we change this?
1384 def execute(self, args):
1385 return ip_lookup.update_dns_cache()
1388 automations.register(AutomationUpdateDNSCache())
1391 class AutomationGetAgentOutput(Automation):
1392 cmd = "get-agent-output"
1393 needs_config = True
1394 needs_checks = True # TODO: Can we change this?
1396 def execute(self, args):
1397 hostname, ty = args
1399 success = True
1400 output = ""
1401 info = ""
1403 try:
1404 if ty == "agent":
1405 data_sources.abstract.DataSource.set_may_use_cache_file(
1406 not data_sources.abstract.DataSource.is_agent_cache_disabled())
1408 ipaddress = ip_lookup.lookup_ip_address(hostname)
1409 sources = data_sources.DataSources(hostname, ipaddress)
1410 sources.set_max_cachefile_age(config.check_max_cachefile_age)
1412 agent_output = ""
1413 for source in sources.get_data_sources():
1414 if isinstance(source, data_sources.abstract.CheckMKAgentDataSource):
1415 agent_output += source.run(hostname, ipaddress, get_raw_data=True)
1416 info = agent_output
1418 # Optionally show errors of problematic data sources
1419 for source in sources.get_data_sources():
1420 source_state, source_output, _source_perfdata = source.get_summary_result_for_checking(
1422 if source_state != 0:
1423 success = False
1424 output += "[%s] %s\n" % (source.id(), source_output)
1425 else:
1426 host_config = snmp.create_snmp_host_config(hostname)
1428 lines = []
1429 for walk_oid in snmp.oids_to_walk():
1430 try:
1431 for oid, value in snmp.walk_for_export(host_config, walk_oid):
1432 lines.append("%s %s\n" % (oid, value))
1433 except Exception as e:
1434 if cmk.utils.debug.enabled():
1435 raise
1436 success = False
1437 output += "OID '%s': %s\n" % (oid, e)
1439 info = "".join(lines)
1440 except Exception as e:
1441 success = False
1442 output = "Failed to fetch data from %s: %s\n" % (hostname, e)
1443 if cmk.utils.debug.enabled():
1444 raise
1446 return success, output, info
1449 automations.register(AutomationGetAgentOutput())
1452 class AutomationNotificationReplay(Automation):
1453 cmd = "notification-replay"
1454 needs_config = True
1455 needs_checks = True # TODO: Can we change this?
1457 def execute(self, args):
1458 nr = args[0]
1459 return notify.notification_replay_backlog(int(nr))
1462 automations.register(AutomationNotificationReplay())
1465 class AutomationNotificationAnalyse(Automation):
1466 cmd = "notification-analyse"
1467 needs_config = True
1468 needs_checks = True # TODO: Can we change this?
1470 def execute(self, args):
1471 nr = args[0]
1472 return notify.notification_analyse_backlog(int(nr))
1475 automations.register(AutomationNotificationAnalyse())
1478 class AutomationGetBulks(Automation):
1479 cmd = "notification-get-bulks"
1480 needs_config = False
1481 needs_checks = False
1483 def execute(self, args):
1484 only_ripe = args[0] == "1"
1485 return notify.find_bulks(only_ripe)
1488 automations.register(AutomationGetBulks())
1491 class AutomationGetServiceConfigurations(Automation):
1492 cmd = "get-service-configurations"
1493 needs_config = True
1494 needs_checks = True
1496 def execute(self, args):
1497 result = {"hosts": {}}
1498 for hostname in config.all_active_hosts():
1499 result["hosts"][hostname] = self._get_config_for_host(hostname)
1501 result["checkgroup_of_checks"] = self._get_checkgroup_of_checks()
1502 return result
1504 def _get_config_for_host(self, hostname):
1505 return {
1506 "checks": check_table.get_check_table(hostname, remove_duplicates=True),
1507 "active_checks": self._get_active_checks(hostname)
1510 def _get_active_checks(self, hostname):
1511 # legacy checks via active_checks
1512 actchecks = []
1513 config_cache = config.get_config_cache()
1514 for acttype, rules in config.active_checks.iteritems():
1515 entries = config_cache.host_extra_conf(hostname, rules)
1516 for params in entries:
1517 description = config.active_check_service_description(hostname, acttype, params)
1518 actchecks.append((acttype, description, params))
1519 return actchecks
1521 def _get_checkgroup_of_checks(self):
1522 checkgroup_of_checks = {}
1523 for check_plugin_name, check in config.check_info.items():
1524 checkgroup_of_checks[check_plugin_name] = check.get("group")
1525 return checkgroup_of_checks
1528 automations.register(AutomationGetServiceConfigurations())
1531 class AutomationGetLabelsOf(Automation):
1532 cmd = "get-labels-of"
1533 needs_config = True
1534 needs_checks = False
1536 def execute(self, args):
1537 object_type, host_name = args[:2]
1539 config_cache = config.get_config_cache()
1541 if object_type == "host":
1542 return {
1543 "labels": config_cache.get_host_config(host_name).labels,
1544 "label_sources": config_cache.get_host_config(host_name).label_sources,
1547 if object_type == "service":
1548 service_description = args[2].decode("utf-8")
1549 return {
1550 "labels": config_cache.labels_of_service(host_name, service_description),
1551 "label_sources": config_cache.label_sources_of_service(
1552 host_name, service_description),
1555 raise NotImplementedError()
1558 automations.register(AutomationGetLabelsOf())