2 # -*- encoding: utf-8; py-indent-offset: 4 -*-
3 # +------------------------------------------------------------------+
4 # | ____ _ _ __ __ _ __ |
5 # | / ___| |__ ___ ___| | __ | \/ | |/ / |
6 # | | | | '_ \ / _ \/ __| |/ / | |\/| | ' / |
7 # | | |___| | | | __/ (__| < | | | | . \ |
8 # | \____|_| |_|\___|\___|_|\_\___|_| |_|_|\_\ |
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.
29 import cmk
.man_pages
as man_pages
31 import cmk
.gui
.watolib
as watolib
32 import cmk
.gui
.table
as table
33 from cmk
.gui
.htmllib
import HTML
34 from cmk
.gui
.exceptions
import MKUserError
35 from cmk
.gui
.i18n
import _
36 from cmk
.gui
.globals import html
38 from cmk
.gui
.valuespec
import (
41 from cmk
.gui
.plugins
.wato
.utils
.main_menu
import (
46 from cmk
.gui
.plugins
.wato
import (
50 get_search_expression
,
56 # .--Check Plugins-------------------------------------------------------.
57 # | ____ _ _ ____ _ _ |
58 # | / ___| |__ ___ ___| | __ | _ \| |_ _ __ _(_)_ __ ___ |
59 # | | | | '_ \ / _ \/ __| |/ / | |_) | | | | |/ _` | | '_ \/ __| |
60 # | | |___| | | | __/ (__| < | __/| | |_| | (_| | | | | \__ \ |
61 # | \____|_| |_|\___|\___|_|\_\ |_| |_|\__,_|\__, |_|_| |_|___/ |
63 # +----------------------------------------------------------------------+
64 # | Catalog of check plugins |
65 # '----------------------------------------------------------------------'
68 @mode_registry.register
69 class ModeCheckPlugins(WatoMode
):
72 return "check_plugins"
79 self
._search
= get_search_expression()
80 self
._topic
= html
.get_ascii_input("topic")
81 if self
._topic
and not self
._search
:
82 if not re
.match("^[a-zA-Z0-9_./]+$", self
._topic
):
83 raise MKUserError("topic", _("Invalid topic"))
85 self
._path
= tuple(self
._topic
.split("/")) # e.g. [ "hw", "network" ]
89 for comp
in self
._path
:
90 ID().validate_value(comp
, None) # Beware against code injection!
92 self
._manpages
= self
._get
_check
_catalog
()
93 self
._titles
= man_pages
.man_page_catalog_titles()
95 self
._has
_second
_level
= None
96 if self
._topic
and not self
._search
:
97 for t
, has_second_level
, title
, _helptext
in self
._man
_page
_catalog
_topics
():
98 if t
== self
._path
[0]:
99 self
._has
_second
_level
= has_second_level
100 self
._topic
_title
= title
103 if len(self
._path
) == 2:
104 self
._topic
_title
= self
._titles
.get(self
._path
[1], self
._path
[1])
107 if self
._topic
and not self
._search
:
108 heading
= "%s - %s" % ( _("Catalog of Check Plugins"), self
._topic
_title
)
110 heading
= html
.render_text("%s: %s" % (_("Check plugins matching"), self
._search
))
112 heading
= _("Catalog of Check Plugins")
118 if len(self
._path
) == 2:
119 back_url
= html
.makeuri([("topic", self
._path
[0])])
121 back_url
= html
.makeuri([("topic", "")])
122 html
.context_button(_("Back"), back_url
, "back")
125 html
.help(_("This catalog of check plugins gives you a complete listing of all plugins "
126 "that are shipped with your Check_MK installation. It also allows you to "
127 "access the rule sets for configuring the parameters of the checks and to "
128 "manually create services in case you cannot or do not want to rely on the "
129 "automatic service discovery."))
131 search_form( "%s: " % _("Search for check plugins"), "check_plugins" )
133 # The maxium depth of the catalog paths is 3. The top level is being rendered
134 # like the WATO main menu. The second and third level are being rendered like
135 # the global settings.
137 if self
._topic
and not self
._search
:
138 self
._render
_manpage
_topic
()
141 for path
, manpages
in self
._get
_manpages
_after
_search
():
142 self
._render
_manpage
_list
(manpages
, path
, self
._titles
.get(path
, path
))
146 for topic
, _has_second_level
, title
, helptext
in self
._man
_page
_catalog
_topics
():
149 mode_or_url
=html
.makeuri([("topic", topic
)]),
151 icon
="plugins_" + topic
,
153 description
=helptext
))
156 def _get_manpages_after_search(self
):
158 handled_check_names
= set([])
160 # searches in {"name" : "asd", "title" : "das", ...}
161 def get_matched_entry(entry
):
162 if isinstance(entry
, dict):
163 name
= entry
.get("name", "")
164 if isinstance(name
, str):
165 name
= name
.decode("utf8")
167 title
= entry
.get("title", "")
168 if isinstance(title
, str):
169 title
= title
.decode("utf8")
170 if self
._search
in name
.lower() or self
._search
in title
.lower():
175 def check_entries(key
, entries
):
176 if isinstance(entries
, list):
178 for entry
in entries
:
179 match
= get_matched_entry(entry
)
181 these_matches
.append(match
)
184 collection
.setdefault(key
, [])
185 # avoid duplicates due to the fact that a man page can have more than
186 # one places in the global tree of man pages.
187 for match
in these_matches
:
188 name
= match
.get("name")
189 if name
and name
in handled_check_names
:
190 continue # avoid duplicate
192 collection
[key
].append(match
)
194 handled_check_names
.add(name
)
196 elif isinstance(entries
, dict):
197 for k
, subentries
in entries
.items():
198 check_entries(k
, subentries
)
200 for key
, entries
in self
._manpages
.items():
201 check_entries(key
, entries
)
203 return collection
.items()
205 def _get_check_catalog(self
):
206 def path_prefix_matches(p
, op
):
211 return p
[0] == op
[0] and path_prefix_matches(p
[1:], op
[1:])
213 def strip_manpage_entry(entry
):
214 return dict([(k
, v
) for (k
, v
) in entry
.items() if k
in ["name", "agents", "title"]])
217 if len(self
._path
) > 0:
218 only_path
= tuple(self
._path
)
222 for path
, entries
in man_pages
.load_man_page_catalog().items():
223 if not path_prefix_matches(path
, only_path
):
226 for component
in path
[:-1]:
227 subtree
= subtree
.setdefault(component
, {})
228 subtree
[path
[-1]] = map(strip_manpage_entry
, entries
)
238 def _render_manpage_topic(self
):
239 if isinstance(self
._manpages
, list):
240 self
._render
_manpage
_list
(self
._manpages
, self
._path
[-1], self
._topic
_title
)
243 if len(self
._path
) == 1 and self
._has
_second
_level
:
244 # For some topics we render a second level in the same optic as the first level
246 for path_comp
, subnode
in self
._manpages
.items():
247 url
= html
.makeuri([("topic", "%s/%s" % (self
._path
[0], path_comp
))])
248 title
= self
._titles
.get(path_comp
, path_comp
)
249 helptext
= self
._get
_check
_plugin
_stats
(subnode
)
255 icon
="check_plugins",
257 description
=helptext
,
262 # For the others we directly display the tables
264 for path_comp
, subnode
in self
._manpages
.items():
265 title
= self
._titles
.get(path_comp
, path_comp
)
266 entries
.append((title
, subnode
, path_comp
))
268 entries
.sort(cmp=lambda a
, b
: cmp(a
[0].lower(), b
[0].lower()))
270 for title
, subnode
, path_comp
in entries
:
271 self
._render
_manpage
_list
(subnode
, path_comp
, title
)
273 def _get_check_plugin_stats(self
, subnode
):
274 if isinstance(subnode
, list):
276 num_plugins
= len(subnode
)
278 num_cats
= len(subnode
)
280 for subcat
in subnode
.values():
281 num_plugins
+= len(subcat
)
285 text
+= "%d %s<br>" % (num_cats
, _("sub categories"))
286 text
+= "%d %s" % (num_plugins
, _("check plugins"))
289 def _render_manpage_list(self
, manpage_list
, path_comp
, heading
):
291 return self
._titles
.get(t
, t
)
294 table
.begin(searchable
=False, sortable
=False, css
="check_catalog")
295 for entry
in sorted(manpage_list
, cmp=lambda a
, b
: cmp(a
["title"], b
["title"])):
296 if not isinstance(entry
, dict):
299 url
= html
.makeuri([("mode", "check_manpage"), ("check_type", entry
["name"]),
300 ("back", html
.makeuri([]))])
301 table
.cell(_("Type of Check"), "<a href='%s'>%s</a>" % (url
, entry
["title"]), css
="title")
302 table
.cell(_("Plugin Name"), "<tt>%s</tt>" % entry
["name"], css
="name")
303 table
.cell(_("Agents"), ", ".join(map(translate
, sorted(entry
["agents"]))), css
="agents")
306 def _man_page_catalog_topics(self
):
307 # topic, has_second_level, title, description
309 ("hw", True, _("Appliances, other dedicated hardware"),
310 _("Switches, load balancers, storage, UPSes, "
311 "environmental sensors, etc. ")),
313 ("os", True, _("Operating systems"),
314 _("Plugins for operating systems, things "
315 "like memory, CPU, filesystems, etc.")),
317 ("app", False, _("Applications"),
318 _("Monitoring of applications such as "
319 "processes, services or databases")),
321 ("agentless", False, _("Networking checks without agent"),
322 _("Plugins that directly check networking "
323 "protocols like HTTP or IMAP")),
325 ("generic", False, _("Generic check plugins"),
326 _("Plugins for local agent extensions or "
327 "communication with the agent in general")),
331 @mode_registry.register
332 class ModeCheckManPage(WatoMode
):
335 return "check_manpage"
338 def permissions(cls
):
341 def _from_vars(self
):
342 self
._check
_type
= html
.get_ascii_input("check_type")
344 # TODO: There is one check "sap.value-groups" which will be renamed to "sap.value_groups".
345 # As long as the old one is available, allow a minus here.
346 if not re
.match("^[-a-zA-Z0-9_.]+$", self
._check
_type
):
347 raise MKUserError("check_type", _("Invalid check type"))
349 # TODO: remove call of automation and then the automation. This can be done once the check_info
350 # data is also available in the "cmk." module because the get-check-manpage automation not only
351 # fetches the man page. It also contains info from check_info. What a hack.
352 self
._manpage
= watolib
.check_mk_local_automation("get-check-manpage", [self
._check
_type
])
353 if self
._manpage
== None:
354 raise MKUserError(None, _("There is no manpage for this check."))
357 return _("Check plugin manual page") + " - " + self
._manpage
["header"]["title"]
361 if html
.has_var("back"):
362 back_url
= html
.get_url_input("back")
363 html
.context_button(_("Back"), back_url
, "back")
365 html
.context_button(_("All Check Plugins"), html
.makeuri_contextless([("mode", "check_plugins")]), "check_plugins")
367 if self
._check
_type
.startswith("check_"):
368 command
= "check_mk_active-" + self
._check
_type
[6:]
370 command
= "check_mk-" + self
._check
_type
372 url
= html
.makeuri_contextless([("view_name", "searchsvc"), ("check_command", command
),
373 ("filled_in", "filter")],
375 html
.context_button(_("Find usage"), url
, "status")
378 # We could simply detect on how many hosts and services this plugin
379 # is currently in use (Livestatus query) and display this information
380 # together with a link for searching. Then we can remove the dumb context
381 # button, that will always be shown - even if the plugin is not in use.
383 html
.open_table(class_
=["data", "headerleft"])
388 html
.b(self
._manpage
["header"]["title"])
393 html
.th(_("Name of plugin"))
395 html
.tt(self
._check
_type
)
400 html
.th(_("Description"))
401 html
.td(self
._manpage
_text
(self
._manpage
["header"]["description"]))
404 def show_ruleset(varname
):
405 if watolib
.g_rulespecs
.exists(varname
):
406 rulespec
= watolib
.g_rulespecs
.get(varname
)
407 url
= html
.makeuri_contextless([("mode", "edit_ruleset"), ("varname", varname
)])
408 param_ruleset
= html
.render_a(rulespec
.title
, url
)
410 html
.th(_("Parameter rule set"))
412 html
.icon_button(url
, _("Edit parameter rule set for this check type"), "check_parameters")
413 html
.write(param_ruleset
)
417 html
.th(_("Example for Parameters"))
419 vs
= rulespec
.valuespec
420 vs
.render_input("dummy", vs
.default_value())
424 if self
._manpage
["type"] == "check_mk":
426 html
.th(_("Service name"))
427 html
.td(HTML(self
._manpage
["service_description"].replace("%s", "☐")))
430 if self
._manpage
.get("group"):
431 group
= self
._manpage
["group"]
432 varname
= "checkgroup_parameters:" + group
433 show_ruleset(varname
)
436 varname
= "active_checks:" + self
._check
_type
[6:]
437 show_ruleset(varname
)
441 def _manpage_text(self
, text
):
442 html_code
= text
.replace("<br>", "\n")\
443 .replace("<", "<")\
444 .replace(">", ">")
445 html_code
= re
.sub("{(.*?)}", "<tt>\\1</tt>", html_code
)
446 html_code
= re
.sub("\n\n+", "<p>", html_code
)