Add specific visualization for labels depending on their source
[check_mk.git] / cmk / gui / plugins / wato / builtin_attributes.py
blobcc898f5cbcac4a542ead2c86369974f03bc868a5
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 import time
29 import cmk.gui.tags
30 import cmk.gui.config as config
31 import cmk.gui.watolib as watolib
32 import cmk.gui.hooks as hooks
33 import cmk.gui.userdb as userdb
34 from cmk.gui.i18n import _
35 from cmk.gui.globals import html
36 from cmk.gui.htmllib import HTML
37 from cmk.gui.valuespec import (
38 HostAddress,
39 ListOf,
40 ListOfStrings,
41 Dictionary,
42 Age,
43 TimeofdayRange,
44 Checkbox,
45 DropdownChoice,
46 Integer,
47 CascadingDropdown,
48 Tuple,
49 IPv4Address,
50 RegExp,
51 Alternative,
52 FixedValue,
53 AbsoluteDate,
54 TextUnicode,
55 SiteChoice,
56 ID,
57 Transform,
58 Labels,
60 from cmk.gui.exceptions import MKUserError
62 from cmk.gui.plugins.wato import (
63 HostAttributeTopicBasicSettings,
64 HostAttributeTopicNetworkScan,
65 HostAttributeTopicAddress,
66 HostAttributeTopicManagementBoard,
67 HostAttributeTopicMetaData,
68 ABCHostAttributeValueSpec,
69 ABCHostAttributeNagiosText,
70 host_attribute_registry,
71 SNMPCredentials,
72 IPMIParameters,
73 HostnameTranslation,
74 ConfigHostname,
78 @host_attribute_registry.register
79 class HostAttributeAlias(ABCHostAttributeNagiosText):
80 def topic(self):
81 return HostAttributeTopicBasicSettings
83 def name(self):
84 return "alias"
86 def nagios_name(self):
87 return "alias"
89 def title(self):
90 return _("Alias")
92 def help(self):
93 return _("A comment or description of this host")
95 def show_in_folder(self):
96 return False
99 @host_attribute_registry.register
100 class HostAttributeIPv4Address(ABCHostAttributeValueSpec):
101 def topic(self):
102 return HostAttributeTopicAddress
104 def name(self):
105 return "ipaddress"
107 def show_in_folder(self):
108 return False
110 def depends_on_tags(self):
111 return ["ip-v4"]
113 def valuespec(self):
114 return HostAddress(
115 title=_("IPv4 Address"),
116 help=_("In case the name of the host is not resolvable via <tt>/etc/hosts</tt> "
117 "or DNS by your monitoring server, you can specify an explicit IP "
118 "address or a resolvable DNS name of the host here.<br> <b>Notes</b>:<br> "
119 "1. If you leave this attribute empty, hostname resolution will be done when "
120 "you activate the configuration. "
121 "Check_MKs builtin DNS cache is activated per default in the global "
122 "configuration to speed up the activation process. The cache is normally "
123 "updated daily with a cron job. You can manually update the cache with the "
124 "command <tt>cmk -v --update-dns-cache</tt>.<br>"
125 "2. If you enter a DNS name here, the DNS resolution will be carried out "
126 "each time the host is checked. Check_MKs DNS cache will NOT be queried. "
127 "Use this only for hosts with dynamic IP addresses."),
128 allow_empty=False,
129 allow_ipv6_address=False,
133 @host_attribute_registry.register
134 class HostAttributeIPv6Address(ABCHostAttributeValueSpec):
135 def topic(self):
136 return HostAttributeTopicAddress
138 def name(self):
139 return "ipv6address"
141 def show_in_folder(self):
142 return False
144 def depends_on_tags(self):
145 return ["ip-v6"]
147 def valuespec(self):
148 return HostAddress(
149 title=_("IPv6 Address"),
150 help=_("In case the name of the host is not resolvable via <tt>/etc/hosts</tt> "
151 "or DNS by your monitoring server, you can specify an explicit IPv6 "
152 "address or a resolvable DNS name of the host here.<br> <b>Notes</b>:<br> "
153 "1. If you leave this attribute empty, hostname resolution will be done when "
154 "you activate the configuration. "
155 "Check_MKs builtin DNS cache is activated per default in the global "
156 "configuration to speed up the activation process. The cache is normally "
157 "updated daily with a cron job. You can manually update the cache with the "
158 "command <tt>cmk -v --update-dns-cache</tt>.<br>"
159 "2. If you enter a DNS name here, the DNS resolution will be carried out "
160 "each time the host is checked. Check_MKs DNS cache will NOT be queried. "
161 "Use this only for hosts with dynamic IP addresses."),
162 allow_empty=False,
163 allow_ipv4_address=False,
167 @host_attribute_registry.register
168 class HostAttributeAdditionalIPv4Addresses(ABCHostAttributeValueSpec):
169 def topic(self):
170 return HostAttributeTopicAddress
172 def name(self):
173 return "additional_ipv4addresses"
175 def show_in_table(self):
176 return False
178 def show_in_folder(self):
179 return False
181 def depends_on_tags(self):
182 return ["ip-v4"]
184 def valuespec(self):
185 return ListOf(
186 HostAddress(
187 allow_empty=False,
188 allow_ipv6_address=False,
190 title=_("Additional IPv4 addresses"),
191 help=_("Here you can specify additional IPv4 addresses. "
192 "These can be used in some active checks like ICMP."),
196 @host_attribute_registry.register
197 class HostAttributeAdditionalIPv6Addresses(ABCHostAttributeValueSpec):
198 def topic(self):
199 return HostAttributeTopicAddress
201 def name(self):
202 return "additional_ipv6addresses"
204 def show_in_table(self):
205 return False
207 def show_in_folder(self):
208 return False
210 def depends_on_tags(self):
211 return ["ip-v6"]
213 def valuespec(self):
214 return ListOf(
215 HostAddress(
216 allow_empty=False,
217 allow_ipv4_address=False,
219 title=_("Additional IPv6 addresses"),
220 help=_("Here you can specify additional IPv6 addresses. "
221 "These can be used in some active checks like ICMP."),
225 @host_attribute_registry.register
226 class HostAttributeSNMPCommunity(ABCHostAttributeValueSpec):
227 def topic(self):
228 return HostAttributeTopicBasicSettings
230 def name(self):
231 return "snmp_community"
233 def show_in_table(self):
234 return False
236 def show_in_folder(self):
237 return True
239 def depends_on_tags(self):
240 return ["snmp"]
242 def valuespec(self):
243 return SNMPCredentials(
244 help = _("Using this option you can configure the community which should be used when "
245 "contacting this host via SNMP v1/v2 or v3. It is possible to configure the SNMP community by "
246 "using the <a href=\"%s\">SNMP Communities</a> ruleset, but when you configure "
247 "a community here, this will override the community defined by the rules.") % \
248 "wato.py?mode=edit_ruleset&varname=snmp_communities",
249 default_value = None,
253 @host_attribute_registry.register
254 class HostAttributeParents(ABCHostAttributeValueSpec):
255 def name(self):
256 return "parents"
258 def topic(self):
259 return HostAttributeTopicBasicSettings
261 def show_in_table(self):
262 return True
264 def show_in_folder(self):
265 return True
267 def valuespec(self):
268 return ListOfStrings(
269 valuespec=ConfigHostname(),
270 title=_("Parents"),
271 help=_("Parents are used to configure the reachability of hosts by the "
272 "monitoring server. A host is considered to be <b>unreachable</b> if all "
273 "of its parents are unreachable or down. Unreachable hosts will not be "
274 "actively monitored.<br><br><b>Clusters</b> automatically configure all "
275 "of their nodes as parents, but only if you do not configure parents "
276 "manually.<br><br>In a distributed setup make sure that the host and all "
277 "of its parents are monitored by the same site."),
278 orientation="horizontal",
281 def is_visible(self, for_what):
282 return for_what != "cluster"
284 def to_nagios(self, value):
285 if value:
286 return ",".join(value)
288 def nagios_name(self):
289 return "parents"
291 def paint(self, value, hostname):
292 parts = [
293 html.render_a(hn, "wato.py?" + html.urlencode_vars([("mode", "edit_host"),
294 ("host", hn)])) for hn in value
296 return "", HTML(", ").join(parts)
299 def validate_host_parents(host):
300 for parent_name in host.parents():
301 if parent_name == host.name():
302 raise MKUserError(
303 None, _("You configured the host to be it's own parent, which is not allowed."))
305 parent = watolib.Host.host(parent_name)
306 if not parent:
307 raise MKUserError(
308 None,
309 _("You defined the non-existing host '%s' as a parent.") % parent_name)
311 if host.site_id() != parent.site_id():
312 raise MKUserError(
313 None,
314 _("The parent '%s' is monitored on site '%s' while the host itself "
315 "is monitored on site '%s'. Both must be monitored on the same site. Remember: The parent/child "
316 "relation is used to describe the reachability of hosts by one monitoring daemon."
317 ) % (parent_name, parent.site_id(), host.site_id()))
320 hooks.register_builtin('validate-host', validate_host_parents)
323 @host_attribute_registry.register
324 class HostAttributeNetworkScan(ABCHostAttributeValueSpec):
325 def name(self):
326 return "network_scan"
328 def may_edit(self):
329 return config.user.may("wato.manage_hosts")
331 def topic(self):
332 return HostAttributeTopicNetworkScan
334 def show_in_table(self):
335 return False
337 def show_in_form(self):
338 return False
340 def show_in_folder(self):
341 return True
343 def show_in_host_search(self):
344 return False
346 def show_inherited_value(self):
347 return False
349 def valuespec(self):
350 return Dictionary(
351 elements=self._network_scan_elements,
352 title=_("Network Scan"),
353 help=_("For each folder an automatic network scan can be configured. It will "
354 "try to detect new hosts in the configured IP ranges by sending pings "
355 "to each IP address to check whether or not a host is using this ip "
356 "address. Each new found host will be added to the current folder by "
357 "it's hostname, when resolvable via DNS, or by it's IP address."),
358 optional_keys=["max_parallel_pings", "translate_names"],
359 default_text=_("Not configured."),
362 def _network_scan_elements(self):
363 elements = [
364 ("ip_ranges",
365 ListOf(
366 self._vs_ip_range(),
367 title=_("IP ranges to scan"),
368 add_label=_("Add new IP range"),
369 text_if_empty=_("No IP range configured"),
371 ("exclude_ranges",
372 ListOf(
373 self._vs_ip_range(),
374 title=_("IP ranges to exclude"),
375 add_label=_("Add new IP range"),
376 text_if_empty=_("No exclude range configured"),
379 "scan_interval",
380 Age(
381 title=_("Scan interval"),
382 display=["days", "hours"],
383 default_value=60 * 60 * 24,
384 minvalue=3600, # 1 hour
386 ("time_allowed",
387 Transform(
388 ListOf(
389 TimeofdayRange(allow_empty=False,),
390 title=_("Time allowed"),
391 help=_("Limit the execution of the scan to this time range."),
392 allow_empty=False,
393 style=ListOf.Style.FLOATING,
394 movable=False,
395 default_value=[((0, 0), (24, 0))],
397 forth=lambda x: [x] if isinstance(x, tuple) else x,
398 back=sorted,
400 ("set_ipaddress",
401 Checkbox(
402 title=_("Set IPv4 address"),
403 help=_("Whether or not to configure the found IP address as the IPv4 "
404 "address of the found hosts."),
405 default_value=True,
409 elements += self._optional_tag_criticality_element()
410 elements += [
411 ("max_parallel_pings",
412 Integer(
413 title=_("Parallel pings to send"),
414 help=_("Set the maximum number of concurrent pings sent to target IP "
415 "addresses."),
416 minvalue=1,
417 maxvalue=200,
418 default_value=100,
420 ("run_as",
421 DropdownChoice(
422 title=_("Run as"),
423 help=_("Execute the network scan in the Check_MK user context of the "
424 "choosen user. This user needs the permission to add new hosts "
425 "to this folder."),
426 choices=self._get_all_user_ids,
427 default_value=lambda: config.user.id,
429 ("translate_names", HostnameTranslation(title=_("Translate Hostnames"),)),
432 return elements
434 def _get_all_user_ids(self):
435 return [(user_id, "%s (%s)" % (user_id, user.get("alias", user_id)))
436 for user_id, user in userdb.load_users(lock=False).items()]
438 def _get_criticality_choices(self):
439 """Returns the current configuration of the tag_group criticality"""
440 tags = cmk.gui.tags.TagConfig()
441 tags.parse_config(watolib.TagConfigFile().load_for_reading())
442 criticality_group = tags.get_tag_group("criticality")
443 if not criticality_group:
444 return []
445 return criticality_group.get_tag_choices()
447 def _optional_tag_criticality_element(self):
448 """This element is optional. The user may have deleted the tag group criticality"""
449 tags = cmk.gui.tags.TagConfig()
450 tags.parse_config(watolib.TagConfigFile().load_for_reading())
451 criticality_group = tags.get_tag_group("criticality")
452 if not criticality_group:
453 return []
455 return [("tag_criticality",
456 DropdownChoice(
457 title=_("Set criticality host tag"),
458 help=_("Added hosts will be created as \"offline\" host by default. You "
459 "can change this option to activate monitoring of new hosts after "
460 "next activation of the configuration after the scan."),
461 choices=self._get_criticality_choices,
462 default_value="offline",
465 def _vs_ip_range(self):
466 return CascadingDropdown(choices=[
467 ("ip_range", _("IP-Range"),
468 Tuple(
469 elements=[
470 IPv4Address(title=_("From:"),),
471 IPv4Address(title=_("To:"),),
473 orientation="horizontal",
475 ("ip_network", _("IP Network"),
476 Tuple(
477 elements=[
478 IPv4Address(title=_("Network address:"),),
479 Integer(
480 title=_("Netmask"),
481 minvalue=8,
482 maxvalue=30,
485 orientation="horizontal",
487 ("ip_list", _("Explicit List of IP Addresses"),
488 ListOfStrings(
489 valuespec=IPv4Address(),
490 orientation="horizontal",
492 ("ip_regex_list", _("List of patterns to exclude"),
493 ListOfStrings(
494 valuespec=RegExp(mode=RegExp.prefix,),
495 orientation="horizontal",
496 help=_("A list of regular expressions which are matched against the found "
497 "IP addresses to exclude them. The matched addresses are excluded."),
502 @host_attribute_registry.register
503 class HostAttributeNetworkScanResult(ABCHostAttributeValueSpec):
504 def name(self):
505 return "network_scan_result"
507 def topic(self):
508 return HostAttributeTopicNetworkScan
510 def show_in_table(self):
511 return False
513 def show_in_form(self):
514 return False
516 def show_in_folder(self):
517 return True
519 def show_in_host_search(self):
520 return False
522 def show_inherited_value(self):
523 return False
525 def editable(self):
526 return False
528 def valuespec(self):
529 return Dictionary(
530 elements=[
532 "start",
533 Alternative(
534 title=_("Started"),
535 elements=[
536 FixedValue(
537 None,
538 totext=_("No scan has been started yet."),
540 AbsoluteDate(
541 include_time=True,
542 default_value=0,
548 "end",
549 Alternative(
550 title=_("Finished"),
551 elements=[
552 FixedValue(
553 None,
554 totext=_("No scan has finished yet."),
556 FixedValue(
557 True,
558 totext="", # currently running
560 AbsoluteDate(
561 include_time=True,
562 default_value=0,
568 "state",
569 Alternative(
570 title=_("State"),
571 elements=[
572 FixedValue(
573 None,
574 totext="", # Not started or currently running
576 FixedValue(
577 True,
578 totext=_("Succeeded"),
580 FixedValue(
581 False,
582 totext=_("Failed"),
587 ("output", TextUnicode(title=_("Output"),)),
589 title=_("Last Scan Result"),
590 optional_keys=[],
591 default_text=_("No scan performed yet."),
595 @host_attribute_registry.register
596 class HostAttributeManagementAddress(ABCHostAttributeValueSpec):
597 def name(self):
598 return "management_address"
600 def topic(self):
601 return HostAttributeTopicManagementBoard
603 def show_in_table(self):
604 return False
606 def show_in_folder(self):
607 return False
609 def valuespec(self):
610 return HostAddress(
611 title=_("Address"),
612 help=_("Address (IPv4 or IPv6) or dns name under which the "
613 "management board can be reached. If this is not set, "
614 "the same address as that of the host will be used."),
615 allow_empty=False,
619 @host_attribute_registry.register
620 class HostAttributeManagementProtocol(ABCHostAttributeValueSpec):
621 def name(self):
622 return "management_protocol"
624 def topic(self):
625 return HostAttributeTopicManagementBoard
627 def show_in_table(self):
628 return False
630 def show_in_folder(self):
631 return True
633 def valuespec(self):
634 return DropdownChoice(
635 title=_("Protocol"),
636 help=_("Specify the protocol used to connect to the management board."),
637 choices=[
638 (None, _("No management board")),
639 ("snmp", _("SNMP")),
640 ("ipmi", _("IPMI")),
641 #("ping", _("Ping-only"))
646 @host_attribute_registry.register
647 class HostAttributeManagementSNMPCommunity(ABCHostAttributeValueSpec):
648 def name(self):
649 return "management_snmp_community"
651 def topic(self):
652 return HostAttributeTopicManagementBoard
654 def show_in_table(self):
655 return False
657 def show_in_folder(self):
658 return True
660 def valuespec(self):
661 return SNMPCredentials(
662 default_value=None,
663 allow_none=True,
667 class IPMICredentials(Alternative):
668 def __init__(self, **kwargs):
669 kwargs["style"] = "dropdown"
670 kwargs["elements"] = [
671 FixedValue(
672 None,
673 title=_("No explicit credentials"),
674 totext="",
676 IPMIParameters(),
678 super(IPMICredentials, self).__init__(**kwargs)
681 @host_attribute_registry.register
682 class HostAttributeManagementIPMICredentials(ABCHostAttributeValueSpec):
683 def name(self):
684 return "management_ipmi_credentials"
686 def topic(self):
687 return HostAttributeTopicManagementBoard
689 def show_in_table(self):
690 return False
692 def show_in_folder(self):
693 return True
695 def valuespec(self):
696 return IPMICredentials(
697 title=_("IPMI credentials"),
698 default_value=None,
702 @host_attribute_registry.register
703 class HostAttributeSite(ABCHostAttributeValueSpec):
704 def name(self):
705 return "site"
707 def topic(self):
708 return HostAttributeTopicBasicSettings
710 def show_in_table(self):
711 return True
713 def show_in_folder(self):
714 return True
716 def valuespec(self):
717 return SiteChoice(
718 title=_("Monitored on site"),
719 help=_("Specify the site that should monitor this host."),
720 invalid_choice_error=_("The configured site is not known to this site. In case you "
721 "are configuring in a distributed slave, this may be a host "
722 "monitored by another site. If you want to modify this "
723 "host, you will have to change the site attribute to the "
724 "local site. But this may make the host be monitored from "
725 "multiple sites."),
728 def get_tag_groups(self, value):
729 if value is False:
730 return {"site": ""}
732 if value is not None:
733 return {"site": value}
735 return {}
738 @host_attribute_registry.register
739 class HostAttributeLockedBy(ABCHostAttributeValueSpec):
740 def name(self):
741 return "locked_by"
743 def topic(self):
744 return HostAttributeTopicMetaData
746 def show_in_table(self):
747 return False
749 def show_in_form(self):
750 return True
752 def show_in_folder(self):
753 return False
755 def show_in_host_search(self):
756 return True
758 def show_inherited_value(self):
759 return False
761 def editable(self):
762 return False
764 def valuespec(self):
765 return Transform(
766 LockedByValuespec(),
767 forth=tuple,
768 back=list,
772 class LockedByValuespec(Tuple):
773 def __init__(self):
774 super(LockedByValuespec, self).__init__(
775 orientation="horizontal",
776 title_br=False,
777 elements=[
778 SiteChoice(),
779 ID(title=_("Program"),),
780 ID(title=_("Connection ID"),),
782 title=_("Locked by"),
783 help=_("The host is (partially) managed by an automatic data source like the "
784 "Dynamic Configuration."),
787 def value_to_text(self, value):
788 if not value or not value[1] or not value[2]:
789 return _("Not locked")
790 return super(LockedByValuespec, self).value_to_text(value)
793 @host_attribute_registry.register
794 class HostAttributeLockedAttributes(ABCHostAttributeValueSpec):
795 def name(self):
796 return "locked_attributes"
798 def topic(self):
799 return HostAttributeTopicMetaData
801 def show_in_table(self):
802 return False
804 def show_in_form(self):
805 return True
807 def show_in_folder(self):
808 return False
810 def show_in_host_search(self):
811 return False
813 def show_inherited_value(self):
814 return False
816 def editable(self):
817 return False
819 def valuespec(self):
820 return ListOf(
821 DropdownChoice(choices=host_attribute_registry.get_choices),
822 title=_("Locked attributes"),
823 text_if_empty=_("Not locked"),
827 @host_attribute_registry.register
828 class HostAttributeMetaData(ABCHostAttributeValueSpec):
829 def name(self):
830 return "meta_data"
832 def topic(self):
833 return HostAttributeTopicMetaData
835 def show_in_table(self):
836 return False
838 def show_in_form(self):
839 return True
841 def show_in_folder(self):
842 return True
844 def show_in_host_search(self):
845 return False
847 def show_inherited_value(self):
848 return False
850 def editable(self):
851 return False
853 def valuespec(self):
854 return Dictionary(
855 elements=[
857 "created_at",
858 Alternative(
859 title=_("Created at"),
860 elements=[
861 FixedValue(
862 None,
863 totext=_("Sometime before 1.6"),
865 AbsoluteDate(
866 include_time=True,
867 default_value=0,
870 default_value=time.time(),
874 "created_by",
875 Alternative(
876 title=_("Created by"),
877 elements=[
878 FixedValue(
879 None,
880 totext=_("Someone before 1.6"),
882 TextUnicode(
883 title=_("Created by"),
884 default_value="unknown",
887 default_value=config.user.id,
891 title=_("Meta data"),
892 optional_keys=[],
896 @host_attribute_registry.register
897 class HostAttributeLabels(ABCHostAttributeValueSpec):
898 def name(self):
899 return "labels"
901 def title(self):
902 return _("Labels")
904 def topic(self):
905 return HostAttributeTopicBasicSettings
907 def help(self):
908 return _("With the help of labels you can flexibly group your hosts in "
909 "order to refer to them later at other places in Check_MK, e.g. in rule chains. "
910 "A label always consists of a combination of key and value in the format "
911 "\"key:value\". A host can only have one value per key. Check_MK will not perform "
912 "any validation on the labels you use.")
914 def show_in_table(self):
915 return False
917 def show_in_folder(self):
918 return True
920 def valuespec(self):
921 return Labels(world=Labels.World.CONFIG, label_source=Labels.Source.EXPLICIT)
923 def filter_matches(self, crit, value, hostname):
924 return set(value).issuperset(set(crit))