Fix typecheck idiom in cmk/gui/{plugins,wato}
[check_mk.git] / cmk / gui / wato / pages / sites.py
blob11e09658feb514fa1e9a618bd50732a8af01f4aa
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.
26 """Mode for managing sites"""
28 import re
29 import traceback
31 import cmk.gui.config as config
32 import cmk.gui.watolib as watolib
33 import cmk.gui.userdb as userdb
34 import cmk.gui.forms as forms
35 import cmk.gui.table as table
37 from cmk.gui.plugins.wato.utils import mode_registry, sort_sites
38 from cmk.gui.plugins.wato.utils.base_modes import WatoMode
39 from cmk.gui.plugins.wato.utils.html_elements import wato_html_head, wato_confirm
40 from cmk.gui.plugins.wato.utils.context_buttons import global_buttons
41 from cmk.gui.i18n import _
42 from cmk.gui.globals import html
43 from cmk.gui.exceptions import MKUserError
44 from cmk.gui.log import logger
46 from cmk.gui.wato.pages.global_settings import GlobalSettingsMode
49 # TODO: Rename to SitesMode()
50 class ModeSites(WatoMode):
51 def __init__(self):
52 super(ModeSites, self).__init__()
53 self._site_mgmt = watolib.SiteManagementFactory().factory()
55 def buttons(self):
56 global_buttons()
59 @mode_registry.register
60 class ModeEditSite(ModeSites):
61 @classmethod
62 def name(cls):
63 return "edit_site"
65 @classmethod
66 def permissions(cls):
67 return ["sites"]
69 def __init__(self):
70 super(ModeEditSite, self).__init__()
71 self._from_html_vars()
73 self._new = self._site_id == None
74 self._new_site = {}
75 configured_sites = self._site_mgmt.load_sites()
77 if self._clone_id:
78 self._site = configured_sites[self._clone_id]
80 elif self._new:
81 self._site = {"replicate_mkps": True}
83 else:
84 self._site = configured_sites.get(self._site_id, {})
86 def _from_html_vars(self):
87 self._site_id = html.var("edit")
88 self._clone_id = html.var("clone")
89 self._id = html.var("id")
90 self._url_prefix = html.var("url_prefix", "").strip()
91 self._timeout = html.var("timeout", "").strip()
92 self._sh_site = html.var("sh_site")
93 self._sh_host = html.var("sh_host")
94 self._repl = html.var("replication")
95 self._multisiteurl = html.var("multisiteurl", "").strip()
97 def title(self):
98 if self._new:
99 return _("Create new site connection")
100 return _("Edit site connection %s") % html.render_tt(self._site_id)
102 def buttons(self):
103 super(ModeEditSite, self).buttons()
104 html.context_button(_("All Sites"), watolib.folder_preserving_link([("mode", "sites")]), "back")
105 if not self._new and self._site.get("replication"):
106 html.context_button(_("Site-Globals"),
107 watolib.folder_preserving_link([("mode", "edit_site_globals"),
108 ("site", self._site_id)]), "configuration")
110 def action(self):
111 if not html.check_transaction():
112 return "sites"
114 if self._new:
115 self._id = self._id.strip()
116 else:
117 self._id = self._site_id
119 configured_sites = self._site_mgmt.load_sites()
120 if self._new and self._id in configured_sites:
121 raise MKUserError("id", _("This id is already being used by another connection."))
123 if not re.match("^[-a-z0-9A-Z_]+$", self._id):
124 raise MKUserError("id", _("The site id must consist only of letters, digit and the underscore."))
126 detail_msg = self._set_site_attributes()
127 configured_sites[self._id] = self._new_site
128 self._site_mgmt.save_sites(configured_sites)
130 if self._new:
131 msg = _("Created new connection to site %s") % html.render_tt(self._id)
132 else:
133 msg = _("Modified site connection %s") % html.render_tt(self._id)
135 # Don't know exactly what have been changed, so better issue a change
136 # affecting all domains
137 watolib.add_change(
138 "edit-sites", msg, sites=[self._id], domains=watolib.ConfigDomain.enabled_domains())
140 # In case a site is not being replicated anymore, confirm all changes for this site!
141 if not self._repl:
142 watolib.clear_site_replication_status(self._id)
144 if self._id != config.omd_site():
145 # On central site issue a change only affecting the GUI
146 watolib.add_change(
147 "edit-sites", msg, sites=[config.omd_site()], domains=[watolib.ConfigDomainGUI])
149 return "sites", detail_msg
151 def _set_site_attributes(self):
152 # Save copy of old site for later
153 configured_sites = self._site_mgmt.load_sites()
154 if not self._new:
155 old_site = configured_sites[self._site_id]
157 self._new_site = {}
158 self._new_site["alias"] = html.get_unicode_input("alias", "").strip()
159 if self._url_prefix:
160 self._new_site["url_prefix"] = self._url_prefix
161 self._new_site["disabled"] = html.get_checkbox("disabled")
163 # Connection
164 vs_connection = self._site_mgmt.connection_method_valuespec()
165 method = vs_connection.from_html_vars("method")
166 vs_connection.validate_value(method, "method")
167 if isinstance(method, tuple) and method[0] in ["unix", "tcp"]:
168 if method[0] == "unix":
169 self._new_site["socket"] = "unix:" + method[1]
170 else:
171 self._new_site["socket"] = "tcp:%s:%d" % method[1]
172 elif method:
173 self._new_site["socket"] = method
175 elif "socket" in self._new_site:
176 del self._new_site["socket"]
178 # Timeout
179 if self._timeout != "":
180 try:
181 self._new_site["timeout"] = int(self._timeout)
182 except ValueError:
183 raise MKUserError("timeout", _("The timeout %s is not a valid integer number.") % self._timeout)
185 # Persist
186 self._new_site["persist"] = html.get_checkbox("persist")
188 # Status host
189 if self._sh_site:
190 self._new_site["status_host"] = (self._sh_site, self._sh_host)
191 else:
192 self._new_site["status_host"] = None
194 # Replication
195 if self._repl == "none":
196 self._repl = None
197 self._new_site["replication"] = self._repl
198 self._new_site["multisiteurl"] = self._multisiteurl
200 # Save Multisite-URL even if replication is turned off. That way that
201 # setting is not lost if replication is turned off for a while.
203 # Disabling of WATO
204 self._new_site["disable_wato"] = html.get_checkbox("disable_wato")
206 # Handle the insecure replication flag
207 self._new_site["insecure"] = html.get_checkbox("insecure")
209 # Allow direct user login
210 self._new_site["user_login"] = html.get_checkbox("user_login")
212 # User synchronization
213 user_sync = self._site_mgmt.user_sync_valuespec().from_html_vars("user_sync")
214 self._new_site["user_sync"] = user_sync
216 # Event Console Replication
217 self._new_site["replicate_ec"] = html.get_checkbox("replicate_ec")
219 # MKPs and ~/local/
220 self._new_site["replicate_mkps"] = html.get_checkbox("replicate_mkps")
222 # Secret is not checked here, just kept
223 if not self._new and "secret" in old_site:
224 self._new_site["secret"] = old_site["secret"]
226 # Do not forget to add those settings (e.g. "globals") that
227 # are not edited with this dialog
228 if not self._new:
229 for key in old_site.keys():
230 if key not in self._new_site and key != "socket":
231 self._new_site[key] = old_site[key]
233 self._site_mgmt.validate_configuration(self._site_id or self._id, self._new_site,
234 configured_sites)
236 def page(self):
237 html.begin_form("site")
239 self._page_basic_settings()
240 self._page_livestatus_settings()
241 self._page_replication_configuration()
243 forms.end()
244 html.button("save", _("Save"))
245 html.hidden_fields()
246 html.end_form()
248 def _page_basic_settings(self):
249 forms.header(_("Basic settings"))
250 # ID
251 forms.section(_("Site ID"), simple = not self._new)
252 if self._new:
253 html.text_input("id", self._site_id or self._clone_id)
254 html.set_focus("id")
255 else:
256 html.write_text(self._site_id)
258 html.help(_("The site ID must be identical (case sensitive) with the instance's exact name."))
259 # Alias
260 forms.section(_("Alias"))
261 html.text_input("alias", self._site.get("alias", ""), size=60)
262 if not self._new:
263 html.set_focus("alias")
264 html.help(_("An alias or description of the site."))
266 def _page_livestatus_settings(self):
267 forms.header(_("Livestatus settings"))
268 forms.section(_("Connection"))
269 method = self._site.get("socket", None)
271 if isinstance(method, str) and method.startswith("unix:"):
272 method = ('unix', method[5:])
274 elif isinstance(method, str) and method.startswith("tcp:"):
275 parts = method.split(":")[1:] # pylint: disable=no-member
276 method = ('tcp', (parts[0], int(parts[1])))
278 self._site_mgmt.connection_method_valuespec().render_input("method", method)
279 html.help( _("When connecting to remote site please make sure "
280 "that Livestatus over TCP is activated there. You can use UNIX sockets "
281 "to connect to foreign sites on localhost. Please make sure that this "
282 "site has proper read and write permissions to the UNIX socket of the "
283 "foreign site."))
285 # Timeout
286 forms.section(_("Connect Timeout"))
287 timeout = self._site.get("timeout", 10)
288 html.number_input("timeout", timeout, size=2)
289 html.write_text(_(" seconds"))
290 html.help(_("This sets the time that Multisite waits for a connection "
291 "to the site to be established before the site is considered to be unreachable. "
292 "If not set, the operating system defaults are begin used and just one login attempt is being. "
293 "performed."))
295 # Persistent connections
296 forms.section(_("Persistent Connection"), simple=True)
297 html.checkbox("persist", self._site.get("persist", False), label=_("Use persistent connections"))
298 html.help(_("If you enable persistent connections then Multisite will try to keep open "
299 "the connection to the remote sites. This brings a great speed up in high-latency "
300 "situations but locks a number of threads in the Livestatus module of the target site."))
302 # URL-Prefix
303 docu_url = "https://mathias-kettner.com/checkmk_multisite_modproxy.html"
304 forms.section(_("URL prefix"))
305 html.text_input("url_prefix", self._site.get("url_prefix", ""), size=60)
306 html.help(_("The URL prefix will be prepended to links of addons like PNP4Nagios "
307 "or the classical Nagios GUI when a link to such applications points to a host or "
308 "service on that site. You can either use an absolute URL prefix like <tt>http://some.host/mysite/</tt> "
309 "or a relative URL like <tt>/mysite/</tt>. When using relative prefixes you needed a mod_proxy "
310 "configuration in your local system apache that proxies such URLs to the according remote site. "
311 "Please refer to the <a target=_blank href='%s'>online documentation</a> for details. "
312 "The prefix should end with a slash. Omit the <tt>/pnp4nagios/</tt> from the prefix.") % docu_url)
314 # Status-Host
315 docu_url = "https://mathias-kettner.com/checkmk_multisite_statushost.html"
316 forms.section(_("Status host"))
318 sh = self._site.get("status_host")
319 if sh:
320 self._sh_site, self._sh_host = sh
321 else:
322 self._sh_site = ""
323 self._sh_host = ""
325 html.write_text(_("host: "))
326 html.text_input("sh_host", self._sh_host, size=10)
327 html.write_text(_(" on monitoring site: "))
329 choices = [ ("", _("(no status host)")) ] + [ (sk, si.get("alias", sk)) for (sk, si) in self._site_mgmt.load_sites().items() ]
330 html.dropdown("sh_site", choices, deflt=self._sh_site, ordered=True)
332 html.help( _("By specifying a status host for each non-local connection "
333 "you prevent Multisite from running into timeouts when remote sites do not respond. "
334 "You need to add the remote monitoring servers as hosts into your local monitoring "
335 "site and use their host state as a reachability state of the remote site. Please "
336 "refer to the <a target=_blank href='%s'>online documentation</a> for details.") % docu_url)
338 # Disabled
339 forms.section(_("Disable"), simple=True)
340 html.checkbox("disabled", self._site.get("disabled", False), label = _("Temporarily disable this connection"))
341 html.help( _("If you disable a connection, then no data of this site will be shown in the status GUI. "
342 "The replication is not affected by this, however."))
344 def _page_replication_configuration(self):
345 # Replication
346 forms.header(_("Configuration Replication (Distributed WATO)"))
347 forms.section(_("Replication method"))
348 html.dropdown("replication",
349 [ (None, _("No replication with this site")),
350 ("slave", _("Slave: push configuration to this site"))
351 ], deflt=self._site.get("replication"))
352 html.help( _("WATO replication allows you to manage several monitoring sites with a "
353 "logically centralized WATO. Slave sites receive their configuration "
354 "from master sites. <br><br>Note: Slave sites "
355 "do not need any replication configuration. They will be remote-controlled "
356 "by the master sites."))
358 forms.section(_("Multisite-URL of remote site"))
359 html.text_input("multisiteurl", self._site.get("multisiteurl", ""), size=60)
360 html.help( _("URL of the remote Check_MK including <tt>/check_mk/</tt>. "
361 "This URL is in many cases the same as the URL-Prefix but with <tt>check_mk/</tt> "
362 "appended, but it must always be an absolute URL. Please note, that "
363 "that URL will be fetched by the Apache server of the local "
364 "site itself, whilst the URL-Prefix is used by your local Browser."))
366 forms.section(_("WATO"), simple=True)
367 html.checkbox("disable_wato", self._site.get("disable_wato", True),
368 label = _('Disable configuration via WATO on this site'))
369 html.help( _('It is a good idea to disable access to WATO completely on the slave site. '
370 'Otherwise a user who does not now about the replication could make local '
371 'changes that are overridden at the next configuration activation.'))
373 forms.section(_("SSL"), simple=True)
374 html.checkbox("insecure", self._site.get("insecure", False),
375 label = _('Ignore SSL certificate errors'))
376 html.help( _('This might be needed to make the synchronization accept problems with '
377 'SSL certificates when using an SSL secured connection.'))
379 forms.section(_('Direct login to Web GUI allowed'), simple=True)
380 html.checkbox('user_login', self._site.get('user_login', True),
381 label = _('Users are allowed to directly login into the Web GUI of this site'))
382 html.help(_('When enabled, this site is marked for synchronisation every time a Web GUI '
383 'related option is changed in the master site.'))
385 forms.section(_("Sync with LDAP connections"), simple=True)
386 self._site_mgmt.user_sync_valuespec().render_input(
387 "user_sync",
388 self._site.get("user_sync",
389 None if self._new else userdb.user_sync_default_config(self._site_id)))
390 html.br()
391 html.help(_('By default the users are synchronized automatically in the interval configured '
392 'in the connection. For example the LDAP connector synchronizes the users every '
393 'five minutes by default. The interval can be changed for each connection '
394 'individually in the <a href="wato.py?mode=ldap_config">connection settings</a>. '
395 'Please note that the synchronization is only performed on the master site in '
396 'distributed setups by default.<br>'
397 'The remote sites don\'t perform automatic user synchronizations with the '
398 'configured connections. But you can configure each site to either '
399 'synchronize the users with all configured connections or a specific list of '
400 'connections.'))
402 if config.mkeventd_enabled:
403 forms.section(_('Event Console'), simple=True)
404 html.checkbox('replicate_ec', self._site.get("replicate_ec", True),
405 label = _("Replicate Event Console configuration to this site"))
406 html.help(_("This option enables the distribution of global settings and rules of the Event Console "
407 "to the remote site. Any change in the local Event Console settings will mark the site "
408 "as <i>need sync</i>. A synchronization will automatically reload the Event Console of "
409 "the remote site."))
411 forms.section(_("Extensions"), simple=True)
412 html.checkbox("replicate_mkps", self._site.get("replicate_mkps", False),
413 label = _("Replicate extensions (MKPs and files in <tt>~/local/</tt>)"))
414 html.help(_("If you enable the replication of MKPs then during each <i>Activate Changes</i> MKPs "
415 "that are installed on your master site and all other files below the <tt>~/local/</tt> "
416 "directory will be also transferred to the slave site. Note: <b>all other MKPs and files "
417 "below <tt>~/local/</tt> on the slave will be removed</b>."))
420 @mode_registry.register
421 class ModeDistributedMonitoring(ModeSites):
422 @classmethod
423 def name(cls):
424 return "sites"
426 @classmethod
427 def permissions(cls):
428 return ["sites"]
430 def title(self):
431 return _("Distributed Monitoring")
433 def buttons(self):
434 super(ModeDistributedMonitoring, self).buttons()
435 html.context_button(_("New connection"),
436 watolib.folder_preserving_link([("mode", "edit_site")]),
437 "new")
439 def action(self):
440 delete_id = html.var("_delete")
441 if delete_id and html.transaction_valid():
442 self._action_delete(delete_id)
444 logout_id = html.var("_logout")
445 if logout_id:
446 return self._action_logout(logout_id)
448 login_id = html.var("_login")
449 if login_id:
450 return self._action_login(login_id)
452 def _action_delete(self, delete_id):
453 configured_sites = self._site_mgmt.load_sites()
454 # The last connection can always be deleted. In that case we
455 # fall back to non-distributed-WATO and the site attribute
456 # will be removed.
457 test_sites = dict(configured_sites.items())
458 del test_sites[delete_id]
460 # Make sure that site is not being used by hosts and folders
461 if delete_id in watolib.Folder.root_folder().all_site_ids():
462 search_url = html.makeactionuri([
463 ("host_search_change_site", "on"),
464 ("host_search_site", delete_id),
465 ("host_search", "1"),
466 ("folder", ""),
467 ("mode", "search"),
468 ("filled_in", "edit_host"),
470 raise MKUserError(None,
471 _("You cannot delete this connection. It has folders/hosts "
472 "assigned to it. You can use the <a href=\"%s\">host "
473 "search</a> to get a list of the hosts.") % search_url)
476 c = wato_confirm(_("Confirm deletion of site %s") % html.render_tt(delete_id),
477 _("Do you really want to delete the connection to the site %s?") % \
478 html.render_tt(delete_id))
479 if c:
480 self._site_mgmt.delete_site(delete_id)
481 return None
483 elif c == False:
484 return ""
486 return None
488 def _action_logout(self, logout_id):
489 configured_sites = self._site_mgmt.load_sites()
490 site = configured_sites[logout_id]
491 c = wato_confirm(_("Confirm logout"),
492 _("Do you really want to log out of '%s'?") % \
493 html.render_tt(site["alias"]))
494 if c:
495 if "secret" in site:
496 del site["secret"]
497 self._site_mgmt.save_sites(configured_sites)
498 watolib.add_change("edit-site", _("Logged out of remote site %s") % html.render_tt(site["alias"]),
499 domains=[watolib.ConfigDomainGUI], sites=[watolib.default_site()])
500 return None, _("Logged out.")
502 elif c == False:
503 return ""
505 else:
506 return None
508 def _action_login(self, login_id):
509 configured_sites = self._site_mgmt.load_sites()
510 if html.var("_abort"):
511 return "sites"
513 if not html.check_transaction():
514 return
516 site = configured_sites[login_id]
517 error = None
518 # Fetch name/password of admin account
519 if html.has_var("_name"):
520 name = html.var("_name", "").strip()
521 passwd = html.var("_passwd", "").strip()
522 try:
523 secret = watolib.do_site_login(login_id, name, passwd)
524 site["secret"] = secret
525 self._site_mgmt.save_sites(configured_sites)
526 message = _("Successfully logged into remote site %s.") % html.render_tt(site["alias"])
527 watolib.log_audit(None, "edit-site", message)
528 return None, message
530 except watolib.MKAutomationException, e:
531 error = _("Cannot connect to remote site: %s") % e
533 except MKUserError, e:
534 html.add_user_error(e.varname, e)
535 error = "%s" % e
537 except Exception, e:
538 logger.exception()
539 if config.debug:
540 raise
541 html.add_user_error("_name", error)
542 error = (_("Internal error: %s\n%s") % (e, traceback.format_exc())).replace("\n", "\n<br>")
544 wato_html_head(_("Login into site \"%s\"") % site["alias"])
545 if error:
546 html.show_error(error)
548 html.p(_("For the initial login into the slave site %s "
549 "we need once your administration login for the Multsite "
550 "GUI on that site. Your credentials will only be used for "
551 "the initial handshake and not be stored. If the login is "
552 "successful then both side will exchange a login secret "
553 "which is used for the further remote calls.") % html.render_tt(site["alias"]))
555 html.begin_form("login", method="POST")
556 forms.header(_('Login credentials'))
557 forms.section(_('Administrator name:'))
558 html.text_input("_name")
559 html.set_focus("_name")
560 forms.section(_('Administrator password:'))
561 html.password_input("_passwd")
562 forms.end()
563 html.button("_do_login", _("Login"))
564 html.button("_abort", _("Abort"))
565 html.hidden_field("_login", login_id)
566 html.hidden_fields()
567 html.end_form()
568 html.footer()
569 return False
571 def page(self):
572 table.begin("sites", _("Connections to local and remote sites"),
573 empty_text = _("You have not configured any local or remotes sites. Multisite will "
574 "implicitely add the data of the local monitoring site. If you add remotes "
575 "sites, please do not forget to add your local monitoring site also, if "
576 "you want to display its data."))
578 sites = sort_sites(self._site_mgmt.load_sites().items())
579 for site_id, site in sites:
580 table.row()
582 self._page_buttons(site_id, site)
583 self._page_basic_settings(site_id, site)
584 self._page_livestatus_settings(site_id, site)
585 self._page_replication_configuration(site_id, site)
587 table.end()
589 def _page_buttons(self, site_id, site):
590 table.cell(_("Actions"), css="buttons")
591 edit_url = watolib.folder_preserving_link([("mode", "edit_site"), ("edit", site_id)])
592 html.icon_button(edit_url, _("Properties"), "edit")
594 clone_url = watolib.folder_preserving_link([("mode", "edit_site"), ("clone", site_id)])
595 html.icon_button(clone_url, _("Clone this connection in order to create a new one"), "clone")
597 delete_url = html.makeactionuri([("_delete", site_id)])
598 html.icon_button(delete_url, _("Delete"), "delete")
600 if (config.has_wato_slave_sites()
601 and (site.get("replication") or config.site_is_local(site_id))) \
602 or watolib.is_wato_slave_site():
603 globals_url = watolib.folder_preserving_link([("mode", "edit_site_globals"),
604 ("site", site_id)])
606 has_site_globals = bool(site.get("globals"))
607 title = _("Site specific global configuration")
608 if has_site_globals:
609 icon = "site_globals_modified"
610 title += " (%s)" % (_("%d specific settings") % len(site.get("globals")))
611 else:
612 icon = "site_globals"
614 html.icon_button(globals_url, title, icon)
616 def _page_basic_settings(self, site_id, site):
617 table.text_cell(_("ID"), site_id)
618 table.text_cell(_("Alias"), site.get("alias", ""))
620 def _page_livestatus_settings(self, site_id, site):
621 # Socket
622 socket = site.get("socket", _("local site"))
623 if socket == "disabled:":
624 socket = _("don't query status")
625 table.cell(_("Socket"))
626 if isinstance(socket, tuple) and socket[0] == "proxy":
627 html.write_text(_("Use livestatus Proxy-Daemon"))
628 else:
629 html.write_text(socket)
631 # Status host
632 if site.get("status_host"):
633 sh_site, sh_host = site["status_host"]
634 table.text_cell(_("Status host"), "%s/%s" % (sh_site, sh_host))
635 else:
636 table.text_cell(_("Status host"))
638 # Disabled
639 if site.get("disabled", False) == True:
640 table.text_cell(_("Disabled"), "<b>%s</b>" % _("yes"))
641 else:
642 table.text_cell(_("Disabled"), _("no"))
644 # Timeout
645 if "timeout" in site:
646 table.text_cell(_("Timeout"), _("%d sec") % int(site["timeout"]), css="number")
647 else:
648 table.text_cell(_("Timeout"), "")
650 # Persist
651 if site.get("persist", False):
652 table.text_cell(_("Pers."), "<b>%s</b>" % _("yes"))
653 else:
654 table.text_cell(_("Pers."), _("no"))
656 def _page_replication_configuration(self, site_id, site):
657 # Replication
658 if site.get("replication"):
659 repl = _("Slave")
660 if site.get("replicate_ec"):
661 repl += ", " + _("EC")
662 if site.get("replicate_mkps"):
663 repl += ", " + _("MKPs")
664 else:
665 repl = ""
666 table.text_cell(_("Replication"), repl)
668 # Login-Button for Replication
669 table.cell(_("Login"))
670 if repl:
671 if site.get("secret"):
672 logout_url = watolib.make_action_link([("mode", "sites"), ("_logout", site_id)])
673 html.buttonlink(logout_url, _("Logout"))
674 else:
675 login_url = watolib.make_action_link([("mode", "sites"), ("_login", site_id)])
676 html.buttonlink(login_url, _("Login"))
679 @mode_registry.register
680 class ModeEditSiteGlobals(ModeSites, GlobalSettingsMode):
681 @classmethod
682 def name(cls):
683 return "edit_site_globals"
685 @classmethod
686 def permissions(cls):
687 return ["sites"]
689 def __init__(self):
690 super(ModeEditSiteGlobals, self).__init__()
691 self._site_id = html.var("site")
692 self._configured_sites = self._site_mgmt.load_sites()
693 try:
694 self._site = self._configured_sites[self._site_id]
695 except KeyError:
696 raise MKUserError("site", _("This site does not exist."))
698 # 2. Values of global settings
699 self._global_settings = watolib.load_configuration_settings()
701 # 3. Site specific global settings
703 if watolib.is_wato_slave_site():
704 self._current_settings = watolib.load_configuration_settings(site_specific=True)
705 else:
706 self._current_settings = self._site.get("globals", {})
708 def title(self):
709 return _("Edit site specific global settings of %s") % \
710 html.render_tt(self._site_id)
712 def buttons(self):
713 super(ModeEditSiteGlobals, self).buttons()
714 html.context_button(_("All Sites"),
715 watolib.folder_preserving_link([("mode", "sites")]),
716 "back")
717 html.context_button(_("Connection"),
718 watolib.folder_preserving_link([("mode", "edit_site"),
719 ("edit", self._site_id)]), "sites")
721 # TODO: Consolidate with ModeEditGlobals.action()
722 def action(self):
723 varname = html.var("_varname")
724 action = html.var("_action")
725 if not varname:
726 return
728 _domain, valuespec, need_restart, _allow_reset, _in_global_settings = watolib.configvars(
729 )[varname]
730 def_value = self._global_settings.get(varname, self._default_values[varname])
732 if action == "reset" and not watolib.is_a_checkbox(valuespec):
733 c = wato_confirm(
734 _("Removing site specific configuration variable"),
735 _("Do you really want to remove the configuration variable <b>%s</b> "
736 "of the specific configuration of this site and that way use the global value "
737 "of <b><tt>%s</tt></b>?") %
738 (varname, valuespec.value_to_text(def_value)))
740 else:
741 if not html.check_transaction():
742 return
743 # No confirmation for direct toggle
744 c = True
746 if c:
747 if varname in self._current_settings:
748 self._current_settings[varname] = not self._current_settings[varname]
749 else:
750 self._current_settings[varname] = not def_value
752 msg = _("Changed site specific configuration variable %s to %s.") % \
753 (varname, _("on") if self._current_settings[varname] else _("off"))
755 self._site.setdefault("globals", {})[varname] = self._current_settings[varname]
756 self._site_mgmt.save_sites(self._configured_sites, activate=False)
758 watolib.add_change(
759 "edit-configvar", msg, sites=[self._site_id], need_restart=need_restart)
761 if action == "_reset":
762 return "edit_site_globals", msg
763 return "edit_site_globals"
765 elif c == False:
766 return ""
768 else:
769 return None
771 def _edit_mode(self):
772 return "edit_site_configvar"
774 def page(self):
775 html.help(_("Here you can configure global settings, that should just be applied "
776 "on that site. <b>Note</b>: this only makes sense if the site "
777 "is part of a distributed setup."))
779 if not watolib.is_wato_slave_site():
780 if not config.has_wato_slave_sites():
781 html.show_error(_("You can not configure site specific global settings "
782 "in non distributed setups."))
783 return
785 if not self._site.get("replication") and not config.site_is_local(self._site_id):
786 html.show_error(_("This site is not the master site nor a replication slave. "
787 "You cannot configure specific settings for it."))
788 return
790 group_names = self._group_names(show_all=True)
791 self._show_configuration_variables(group_names)