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.
31 from typing
import Callable
, Union
, Tuple
, Dict
# pylint: disable=unused-import
34 import cmk
.gui
.utils
as utils
36 from cmk
.gui
.i18n
import _
37 import cmk
.gui
.log
as log
38 import cmk
.utils
.paths
39 import cmk
.utils
.store
as store
40 from cmk
.gui
.exceptions
import MKConfigError
, MKAuthException
41 import cmk
.gui
.permissions
as permissions
43 import cmk
.gui
.plugins
.config
45 # This import is added for static analysis tools like pylint to make them
46 # know about all shipped config options. The default config options are
47 # later handled with the default_config dict and _load_default_config()
48 from cmk
.gui
.plugins
.config
.base
import * # pylint: disable=wildcard-import,unused-wildcard-import
50 if not cmk
.is_raw_edition():
51 from cmk
.gui
.cee
.plugins
.config
.cee
import * # pylint: disable=wildcard-import,unused-wildcard-import
53 if cmk
.is_managed_edition():
54 from cmk
.gui
.cme
.plugins
.config
.cme
import * # pylint: disable=wildcard-import,unused-wildcard-import
56 # .--Declarations--------------------------------------------------------.
58 # | | _ \ ___ ___| | __ _ _ __ __ _| |_(_) ___ _ __ ___ |
59 # | | | | |/ _ \/ __| |/ _` | '__/ _` | __| |/ _ \| '_ \/ __| |
60 # | | |_| | __/ (__| | (_| | | | (_| | |_| | (_) | | | \__ \ |
61 # | |____/ \___|\___|_|\__,_|_| \__,_|\__|_|\___/|_| |_|___/ |
63 # +----------------------------------------------------------------------+
64 # | Declarations of global variables and constants |
65 # '----------------------------------------------------------------------'
71 # hard coded in various permissions
72 builtin_role_ids
= ["user", "admin", "guest"]
74 # Base directory of dynamic configuration
75 config_dir
= cmk
.utils
.paths
.var_dir
+ "/web"
77 # Stores the initial configuration values
81 permission_declaration_functions
= []
85 HOST_STATE
= ('__HOST_STATE__',)
86 HIDDEN
= ('__HIDDEN__',)
89 class FOREACH_HOST(object):
93 class FOREACH_CHILD(object):
97 class FOREACH_CHILD_WITH(object):
101 class FOREACH_PARENT(object):
105 class FOREACH_SERVICE(object):
109 class REMAINING(object):
113 class DISABLED(object):
117 class HARD_STATES(object):
121 class DT_AGGR_WARN(object):
125 # Has to be declared here once since the functions can be assigned in
126 # bi.py and also in multisite.mk. "Double" declarations are no problem
127 # here since this is a dict (List objects have problems with duplicate
129 aggregation_functions
= {}
132 # .--Functions-----------------------------------------------------------.
134 # | | ___| _ _ __ ___| |_(_) ___ _ __ ___ |
135 # | | |_ | | | | '_ \ / __| __| |/ _ \| '_ \/ __| |
136 # | | _|| |_| | | | | (__| |_| | (_) | | | \__ \ |
137 # | |_| \__,_|_| |_|\___|\__|_|\___/|_| |_|___/ |
139 # +----------------------------------------------------------------------+
140 # | Helper functions for config parsing, login, etc. |
141 # '----------------------------------------------------------------------'
147 log
.set_log_levels(log_levels
)
148 cmk
.gui
.i18n
.set_user_localizations(user_localizations
)
151 # Read in a multisite.d/*.mk file
152 def include(filename
):
153 if not filename
.startswith("/"):
154 filename
= cmk
.utils
.paths
.default_config_dir
+ "/" + filename
156 # Config file is obligatory. An empty example is installed
157 # during setup.sh. Better signal an error then simply ignore
160 execfile(filename
, globals(), globals())
161 except Exception as e
:
162 raise MKConfigError(_("Cannot read configuration file %s: %s:") % (filename
, e
))
165 # Load multisite.mk and all files in multisite.d/. This will happen
166 # for *each* HTTP request.
167 # FIXME: Optimize this to cache the config etc. until either the config files or plugins
168 # have changed. We could make this being cached for multiple requests just like the
169 # plugins of other modules. This may save significant time in case of small requests like
170 # the graph ajax page or similar.
174 # Set default values for all user-changable configuration settings
175 _initialize_with_default_config()
177 # Initialze sites with default site configuration. Need to do it here to
178 # override possibly deleted sites
179 sites
= default_single_site_configuration()
181 # First load main file
182 include("multisite.mk")
184 # Load also recursively all files below multisite.d
185 conf_dir
= cmk
.utils
.paths
.default_config_dir
+ "/multisite.d"
187 if os
.path
.isdir(conf_dir
):
188 for root
, _directories
, files
in os
.walk(conf_dir
):
189 for filename
in files
:
190 if filename
.endswith(".mk"):
191 filelist
.append(root
+ "/" + filename
)
198 sites
= migrate_old_site_config(sites
)
200 sites
= default_single_site_configuration()
202 migrate_old_sample_config_tag_groups(wato_host_tags
, wato_aux_tags
)
204 execute_post_config_load_hooks()
207 def execute_post_config_load_hooks():
208 for func
in _post_config_load_hooks
:
212 _post_config_load_hooks
= []
215 def register_post_config_load_hook(func
):
216 # type: (Callable) -> None
217 _post_config_load_hooks
.append(func
)
220 def _initialize_with_default_config():
221 vars_before_plugins
= all_nonfunction_vars(globals())
223 vars_after_plugins
= all_nonfunction_vars(globals())
224 _load_default_config(vars_before_plugins
, vars_after_plugins
)
226 _apply_default_config()
229 def _apply_default_config():
230 for k
, v
in default_config
.items():
231 if isinstance(v
, (dict, list)):
236 def _load_default_config(vars_before_plugins
, vars_after_plugins
):
237 default_config
.clear()
238 _load_default_config_from_module_plugins()
239 _load_default_config_from_legacy_plugins(vars_before_plugins
, vars_after_plugins
)
242 def _load_default_config_from_module_plugins():
243 # TODO: Find a better solution for this. Probably refactor declaration of default
245 config_plugin_vars
= {}
246 for module
in _config_plugin_modules():
247 config_plugin_vars
.update(module
.__dict
__)
249 for k
, v
in config_plugin_vars
.items():
253 if isinstance(v
, (dict, list)):
256 default_config
[k
] = v
259 def _load_default_config_from_legacy_plugins(vars_before_plugins
, vars_after_plugins
):
260 new_vars
= vars_after_plugins
.difference(vars_before_plugins
)
261 default_config
.update(dict([(k
, copy
.deepcopy(globals()[k
])) for k
in new_vars
]))
264 def _config_plugin_modules():
266 module
for name
, module
in sys
.modules
.items()
267 if (name
.startswith("cmk.gui.plugins.config.") or name
.startswith(
268 "cmk.gui.cee.plugins.config.") or name
.startswith("cmk.gui.cme.plugins.config.")) and
273 def reporting_available():
275 # Check the existance of one arbitrary config variable from the
277 _dummy
= reporting_filename
283 def combined_graphs_available():
285 _dummy
= have_combined_graphs
291 def hide_language(lang
):
292 return lang
in hide_languages
295 def all_nonfunction_vars(var_dict
):
296 return set([name
for name
, value
in var_dict
.items() if name
[0] != '_' and not callable(value
)])
299 def get_language(default
=None):
301 return default_language
306 for entry
in host_tag_groups():
307 tag_id
, _title
, tags
= entry
[:3]
312 for tag_id
, alias
in aux_tags():
317 def tag_group_title(tag
):
318 for entry
in host_tag_groups():
319 _tag_id
, title
, tags
= entry
[:3]
326 # .--Permissions---------------------------------------------------------.
328 # | | _ \ ___ _ __ _ __ ___ (_)___ ___(_) ___ _ __ ___ |
329 # | | |_) / _ \ '__| '_ ` _ \| / __/ __| |/ _ \| '_ \/ __| |
330 # | | __/ __/ | | | | | | | \__ \__ \ | (_) | | | \__ \ |
331 # | |_| \___|_| |_| |_| |_|_|___/___/_|\___/|_| |_|___/ |
333 # +----------------------------------------------------------------------+
334 # | Declarations of permissions and roles |
335 # '----------------------------------------------------------------------'
337 # Kept for compatibility with pre 1.6 GUI plugins
338 declare_permission
= permissions
.declare_permission
339 declare_permission_section
= permissions
.declare_permission_section
342 # Some module have a non-fixed list of permissions. For example for
343 # each user defined view there is also a permission. This list is
344 # not known at the time of the loading of the module - though. For
345 # that purpose module can register functions. These functions should
346 # just call declare_permission(). They are being called in the correct
348 # TODO: Clean this up
349 def declare_dynamic_permissions(func
):
350 permission_declaration_functions
.append(func
)
353 # This function needs to be called by all code that needs access
354 # to possible dynamic permissions
355 # TODO: Clean this up
356 def load_dynamic_permissions():
357 for func
in permission_declaration_functions
:
361 def get_role_permissions():
362 """Returns the set of permissions for all roles"""
363 role_permissions
= {}
364 roleids
= roles
.keys()
365 for perm_class
in permissions
.permission_registry
.values():
367 for role_id
in roleids
:
368 if not role_id
in role_permissions
:
369 role_permissions
[role_id
] = []
371 if _may_with_roles([role_id
], perm
.name
):
372 role_permissions
[role_id
].append(perm
.name
)
373 return role_permissions
376 def _may_with_roles(some_role_ids
, pname
):
377 # If at least one of the given roles has this permission, it's fine
378 for role_id
in some_role_ids
:
379 role
= roles
[role_id
]
381 he_may
= role
.get("permissions", {}).get(pname
)
382 # Handle compatibility with permissions without "general." that
383 # users might have saved in their own custom roles.
384 if he_may
is None and pname
.startswith("general."):
385 he_may
= role
.get("permissions", {}).get(pname
[8:])
387 if he_may
is None: # not explicitely listed -> take defaults
388 if "basedon" in role
:
389 base_role_id
= role
["basedon"]
391 base_role_id
= role_id
392 if pname
not in permissions
.permission_registry
:
393 return False # Permission unknown. Assume False. Functionality might be missing
394 perm
= permissions
.permission_registry
[pname
]()
395 he_may
= base_role_id
in perm
.defaults
402 # .--User Login----------------------------------------------------------.
404 # | | | | |___ ___ _ __ | | ___ __ _(_)_ __ |
405 # | | | | / __|/ _ \ '__| | | / _ \ / _` | | '_ \ |
406 # | | |_| \__ \ __/ | | |__| (_) | (_| | | | | | |
407 # | \___/|___/\___|_| |_____\___/ \__, |_|_| |_| |
409 # +----------------------------------------------------------------------+
410 # | Managing the currently logged in user |
411 # '----------------------------------------------------------------------'
412 # TODO: Shouldn't this be moved to e.g. login.py or userdb.py?
415 # This objects intention is currently only to handle the currently logged in user after authentication.
416 # But maybe this can be used for managing all user objects in future.
417 # TODO: Cleanup accesses to module global vars and functions
418 class LoggedInUser(object):
419 def __init__(self
, user_id
):
424 self
._load
_attributes
()
425 self
._load
_permissions
()
426 self
._load
_site
_config
()
427 self
._button
_counts
= None
429 # TODO: Clean up that baserole_* stuff?
430 def _load_roles(self
):
431 # Determine the roles of the user. If the user is listed in
432 # users, admin_users or guest_users in multisite.mk then we
433 # give him the according roles. If the user has an explicit
434 # profile in multisite_users (e.g. due to WATO), we rather
435 # use that profile. Remaining (unknown) users get the default_user_role.
436 # That can be set to None -> User has no permissions at all.
437 self
.role_ids
= self
._gather
_roles
()
439 # Get base roles (admin/user/guest)
440 self
._load
_base
_roles
()
442 # Get best base roles and use as "the" role of the user
443 if "admin" in self
.baserole_ids
:
444 self
.baserole_id
= "admin"
445 elif "user" in self
.baserole_ids
:
446 self
.baserole_id
= "user"
448 self
.baserole_id
= "guest"
450 def _gather_roles(self
):
451 return roles_of_user(self
.id)
453 def _load_base_roles(self
):
455 for r
in self
.role_ids
:
456 if r
in builtin_role_ids
:
459 base_roles
.add(roles
[r
]["basedon"])
461 self
.baserole_ids
= list(base_roles
)
463 def _load_attributes(self
):
464 self
.attributes
= self
.load_file("cached_profile", None)
465 if self
.attributes
is None:
466 if self
.id in multisite_users
:
467 self
.attributes
= multisite_users
[self
.id]
470 "roles": self
.role_ids
,
473 self
.alias
= self
.attributes
.get("alias", self
.id)
474 self
.email
= self
.attributes
.get("email", self
.id)
476 def _load_permissions(self
):
477 # Prepare cache of already computed permissions
478 # Make sure, admin can restore permissions in any case!
479 if self
.id in admin_users
:
481 "general.use": True, # use Multisite
482 "wato.use": True, # enter WATO
483 "wato.edit": True, # make changes in WATO...
484 "wato.users": True, # ... with access to user management
487 self
.permissions
= {}
489 def _load_confdir(self
):
490 self
.confdir
= config_dir
+ "/" + self
.id.encode("utf-8")
491 store
.mkdir(self
.confdir
)
493 def _load_site_config(self
):
494 self
.siteconf
= self
.load_file("siteconfig", {})
496 def get_button_counts(self
):
497 if not self
._button
_counts
:
498 self
._button
_counts
= self
.load_file("buttoncounts", {})
499 return self
._button
_counts
501 def save_site_config(self
):
502 self
.save_file("siteconfig", self
.siteconf
)
504 def get_attribute(self
, key
, deflt
=None):
505 return self
.attributes
.get(key
, deflt
)
507 def set_attribute(self
, key
, value
):
508 self
.attributes
[key
] = value
510 def unset_attribute(self
, key
):
512 del self
.attributes
[key
]
516 def language(self
, default
=None):
517 return self
.get_attribute("language", get_language(default
))
519 def contact_groups(self
):
520 return self
.get_attribute("contactgroups", [])
522 def load_stars(self
):
523 return set(self
.load_file("favorites", []))
525 def save_stars(self
, stars
):
526 self
.save_file("favorites", list(stars
))
528 def is_site_disabled(self
, site_id
):
529 siteconf
= self
.siteconf
.get(site_id
, {})
530 return siteconf
.get("disabled", False)
532 def authorized_sites(self
, unfiltered_sites
=None):
533 if unfiltered_sites
is None:
534 unfiltered_sites
= allsites().items()
536 authorized_sites
= self
.get_attribute("authorized_sites")
537 if authorized_sites
is None:
538 return unfiltered_sites
540 return [(site_id
, s
) for site_id
, s
in unfiltered_sites
if site_id
in authorized_sites
]
542 def authorized_login_sites(self
):
543 login_site_ids
= get_login_slave_sites()
545 (site_id
, s
) for site_id
, s
in allsites().items() if site_id
in login_site_ids
547 return self
.authorized_sites(login_sites
)
549 def may(self
, pname
):
550 if pname
in self
.permissions
:
551 return self
.permissions
[pname
]
552 he_may
= _may_with_roles(user
.role_ids
, pname
)
553 self
.permissions
[pname
] = he_may
556 def need_permission(self
, pname
):
557 if not self
.may(pname
):
558 perm
= permissions
.permission_registry
[pname
]()
559 raise MKAuthException(
560 _("We are sorry, but you lack the permission "
561 "for this operation. If you do not like this "
562 "then please ask you administrator to provide you with "
563 "the following permission: '<b>%s</b>'.") % perm
.title
)
565 def load_file(self
, name
, deflt
, lock
=False):
566 # In some early error during login phase there are cases where it might
567 # happen that a user file is requested but the user is not yet
568 # set. We have all information to set it, then do it.
570 return deflt
# No user known at this point of time
572 path
= self
.confdir
+ "/" + name
+ ".mk"
573 return store
.load_data_from_file(path
, deflt
, lock
)
575 def save_file(self
, name
, content
, unlock
=False):
576 save_user_file(name
, content
, self
.id, unlock
)
578 def file_modified(self
, name
):
579 if self
.confdir
is None:
583 return os
.stat(self
.confdir
+ "/" + name
+ ".mk").st_mtime
585 if e
.errno
== errno
.ENOENT
:
591 # Login a user that has all permissions. This is needed for making
592 # Livestatus queries from unauthentiated page handlers
593 # TODO: Can we somehow get rid of this?
594 class LoggedInSuperUser(LoggedInUser
):
596 super(LoggedInSuperUser
, self
).__init
__(None)
597 self
.alias
= "Superuser for unauthenticated pages"
600 def _gather_roles(self
):
603 def _load_confdir(self
):
606 def _load_site_config(self
):
609 def load_file(self
, name
, deflt
, lock
=False):
613 class LoggedInNobody(LoggedInUser
):
615 super(LoggedInNobody
, self
).__init
__(None)
616 self
.alias
= "Unauthenticated user"
617 self
.email
= "nobody"
619 def _gather_roles(self
):
622 def _load_confdir(self
):
625 def _load_site_config(self
):
628 def load_file(self
, name
, deflt
, lock
=False):
632 def clear_user_login():
633 _set_user(LoggedInNobody())
636 def set_user_by_id(user_id
):
637 _set_user(LoggedInUser(user_id
))
640 def set_super_user():
641 _set_user(LoggedInSuperUser())
644 def _set_user(_user
):
649 # This holds the currently logged in user object
650 user
= LoggedInNobody()
653 # .--User Handling-------------------------------------------------------.
655 # | | | | |___ ___ _ __ | | | | __ _ _ __ __| | (_)_ __ __ _ |
656 # | | | | / __|/ _ \ '__| | |_| |/ _` | '_ \ / _` | | | '_ \ / _` | |
657 # | | |_| \__ \ __/ | | _ | (_| | | | | (_| | | | | | | (_| | |
658 # | \___/|___/\___|_| |_| |_|\__,_|_| |_|\__,_|_|_|_| |_|\__, | |
660 # +----------------------------------------------------------------------+
661 # | General user handling of all users, not only the currently logged |
662 # | in user. These functions are mostly working with the loaded multisite|
663 # | configuration data (multisite_users, admin_users, ...), so they are |
664 # | more related to this module than to the userdb module. |
665 # '----------------------------------------------------------------------'
668 def roles_of_user(user_id
):
669 def existing_role_ids(role_ids
):
670 return [role_id
for role_id
in role_ids
if role_id
in roles
]
672 if user_id
in multisite_users
:
673 return existing_role_ids(multisite_users
[user_id
]["roles"])
674 elif user_id
in admin_users
:
676 elif user_id
in guest_users
:
678 elif users
is not None and user_id
in users
:
680 elif os
.path
.exists(config_dir
+ "/" + user_id
.encode("utf-8") + "/automation.secret"):
681 return ["guest"] # unknown user with automation account
682 elif 'roles' in default_user_profile
:
683 return existing_role_ids(default_user_profile
['roles'])
684 elif default_user_role
:
685 return existing_role_ids([default_user_role
])
689 def alias_of_user(user_id
):
690 if user_id
in multisite_users
:
691 return multisite_users
[user_id
].get("alias", user_id
)
695 def user_may(user_id
, pname
):
696 return _may_with_roles(roles_of_user(user_id
), pname
)
699 # TODO: Check all calls for arguments (changed optional user to 3rd positional)
700 def save_user_file(name
, data
, user_id
, unlock
=False):
701 path
= config_dir
+ "/" + user_id
.encode("utf-8") + "/" + name
+ ".mk"
702 store
.mkdir(os
.path
.dirname(path
))
703 store
.save_data_to_file(path
, data
)
707 # .--Host tags-----------------------------------------------------------.
709 # | | | | | ___ ___| |_ | |_ __ _ __ _ ___ |
710 # | | |_| |/ _ \/ __| __| | __/ _` |/ _` / __| |
711 # | | _ | (_) \__ \ |_ | || (_| | (_| \__ \ |
712 # | |_| |_|\___/|___/\__| \__\__,_|\__, |___/ |
714 # +----------------------------------------------------------------------+
715 # | Helper functions for dealing with host tags |
716 # '----------------------------------------------------------------------'
719 class BuiltinTags(object):
722 ("agent", "%s/%s" % (_("Data sources"), _("Check_MK Agent")), [
723 ("cmk-agent", _("Contact either Check_MK Agent or use datasource program"),
725 ("all-agents", _("Contact Check_MK agent and all enabled datasource programs"),
727 ("special-agents", _("Use all enabled datasource programs"), ["tcp"]),
728 ("no-agent", _("No agent"), []),
730 ("snmp", "%s/%s" % (_("Data sources"), _("SNMP")), [
731 ("no-snmp", _("No SNMP"), []),
732 ("snmp-v2", _("SNMP v2 or v3"), ["snmp"]),
733 ("snmp-v1", _("SNMP v1"), ["snmp"]),
735 ("address_family", "%s/%s " % (_("Address"), _("IP Address Family")), [
736 ("ip-v4-only", _("IPv4 only"), ["ip-v4"]),
737 ("ip-v6-only", _("IPv6 only"), ["ip-v6"]),
738 ("ip-v4v6", _("IPv4/IPv6 dual-stack"), ["ip-v4", "ip-v6"]),
739 ("no-ip", _("No IP"), []),
745 ("ip-v4", "%s/%s" % (_("Address"), _("IPv4"))),
746 ("ip-v6", "%s/%s" % (_("Address"), _("IPv6"))),
747 ("snmp", "%s/%s" % (_("Data sources"), _("Monitor via SNMP"))),
748 ("tcp", "%s/%s" % (_("Data sources"), _("Monitor via Check_MK Agent"))),
749 ("ping", "%s/%s" % (_("Data sources"), _("Only ping this device"))),
752 def get_effective_tag_groups(self
, tag_groups
):
753 """Extend the given tag group definitions with the builtin tag groups
754 and return the extended list"""
755 tag_groups
= tag_groups
[:]
756 tag_group_ids
= set([tg
[0] for tg
in tag_groups
])
758 for tag_group
in self
.host_tags():
759 if tag_group
[0] not in tag_group_ids
:
760 tag_groups
.append(tag_group
)
764 def get_effective_aux_tags(self
, aux_tag_list
):
765 aux_tags_
= aux_tag_list
[:]
766 aux_tag_ids
= set([at
[0] for at
in aux_tag_list
])
768 for aux_tag
in self
.aux_tags():
769 if aux_tag
[0] not in aux_tag_ids
:
770 aux_tags_
.append(aux_tag
)
775 # During development of the 1.6 version the site configuration has been cleaned up in several ways:
776 # 1. The "socket" attribute could be "disabled" to disable a site connection. This has already been
777 # deprecated long time ago and was not configurable in WATO. This has now been superceeded by
778 # the dedicated "disabled" attribute.
779 # 2. The "socket" attribute was optional. A not present socket meant "connect to local unix" socket.
780 # This is now replaced with a value like this ("local", None) to reflect the generic
781 # CascadingDropdown() data structure of "(type, attributes)".
782 # 3. The "socket" attribute was stored in the livestatus.py socketurl encoded format, at least when
783 # livestatus proxy was not used. This is now stored in the CascadingDropdown() native format and
784 # converted here to the correct format.
785 def migrate_old_site_config(site_config
):
787 # Prevent problem when user has deleted all sites from his
788 # configuration and sites is {}. We assume a default single site
789 # configuration in that case.
790 return default_single_site_configuration()
792 for site_cfg
in site_config
.itervalues():
793 if site_cfg
.get("socket") is None:
794 site_cfg
["socket"] = ("local", None)
797 socket
= site_cfg
["socket"]
798 if socket
== 'disabled':
799 site_cfg
['disabled'] = True
800 site_cfg
['socket'] = ("local", None)
803 if isinstance(socket
, six
.string_types
):
804 site_cfg
["socket"] = _migrate_string_encoded_socket(socket
)
809 def _migrate_string_encoded_socket(value
):
810 # type: (str) -> Tuple[str, Union[Dict]]
811 family_txt
, address
= value
.split(":", 1) # pylint: disable=no-member
813 if family_txt
== "unix":
815 "path": value
.split(":", 1)[1],
818 if family_txt
in ["tcp", "tcp6"]:
819 host
, port
= address
.rsplit(":", 1)
821 "address": (host
, int(port
)),
824 raise NotImplementedError()
827 # Previous to 1.5 the "Agent type" tag group was created as sample config and was not
828 # a builtin tag group (which can not be modified by the user). With werk #5535 we changed
829 # the tag scheme and need to deal with the user config (which might extend the original tag group).
830 # Use two strategies:
832 # a) Check whether or not the tag group has been modified. If not, simply remove it from the user
833 # config and use the builtin tag group in the future.
834 # b) Extend the tag group in the user configuration with the tag configuration we need for 1.5.
835 # TODO: Move to wato/watolib and register using register_post_config_load_hook()
836 def migrate_old_sample_config_tag_groups(host_tags
, aux_tags_
):
837 remove_old_sample_config_tag_groups(host_tags
, aux_tags_
)
838 extend_user_modified_tag_groups(host_tags
)
841 def remove_old_sample_config_tag_groups(host_tags
, aux_tags_
):
842 legacy_tag_group_default
= (
846 ('cmk-agent', u
'Check_MK Agent (Server)', ['tcp']),
847 ('snmp-only', u
'SNMP (Networking device, Appliance)', ['snmp']),
848 ('snmp-v1', u
'Legacy SNMP device (using V1)', ['snmp']),
849 ('snmp-tcp', u
'Dual: Check_MK Agent + SNMP', ['snmp', 'tcp']),
850 ('ping', u
'No Agent', []),
855 host_tags
.remove(legacy_tag_group_default
)
857 # Former tag choices (see above) are added as aux tags to allow the user to migrate
858 # these tags and the objects that use them
860 ("snmp-only", "Data sources/Legacy: SNMP (Networking device, Appliance)"))
861 aux_tags_
.insert(0, ("snmp-tcp", "Data sources/Legacy: Dual: Check_MK Agent + SNMP"))
863 pass # Not there or modified
865 legacy_aux_tag_ids
= [
870 for aux_tag
in aux_tags_
[:]:
871 if aux_tag
[0] in legacy_aux_tag_ids
:
872 aux_tags_
.remove(aux_tag
)
875 def extend_user_modified_tag_groups(host_tags
):
876 """This method supports migration from <1.5 to 1.5 in case the user has a customized "Agent type" tag group
877 See help of migrate_old_sample_config_tag_groups() and werk #5535 and #6446 for further information.
879 Disclaimer: The host_tags data structure is a mess which will hopefully be cleaned up during 1.6 development.
880 Basically host_tags is a list of configured tag groups. Each tag group is represented by a tuple like this:
882 # tag_group_id, tag_group_title, tag_choices
883 ('agent', u'Agent type',
885 # tag_id, tag_title, aux_tag_ids
886 ('cmk-agent', u'Check_MK Agent (Server)', ['tcp']),
887 ('snmp-only', u'SNMP (Networking device, Appliance)', ['snmp']),
888 ('snmp-v1', u'Legacy SNMP device (using V1)', ['snmp']),
889 ('snmp-tcp', u'Dual: Check_MK Agent + SNMP', ['snmp', 'tcp']),
890 ('ping', u'No Agent', []),
895 for this_tag_group
in host_tags
:
896 if this_tag_group
[0] == "agent":
897 tag_group
= this_tag_group
899 if tag_group
is None:
900 return # Tag group does not exist
902 # Mark all existing tag choices as legacy to help the user that this should be cleaned up
903 for index
, tag_choice
in enumerate(tag_group
[2][:]):
904 if tag_choice
[0] in ["no-agent", "special-agents", "all-agents", "cmk-agent"]:
905 continue # Don't prefix the standard choices
907 if tag_choice
[1].startswith("Legacy: "):
908 continue # Don't prefix already prefixed choices
910 tag_choice_list
= list(tag_choice
)
911 tag_choice_list
[1] = "Legacy: %s" % tag_choice_list
[1]
912 tag_group
[2][index
] = tuple(tag_choice_list
)
914 tag_choices
= [c
[0] for c
in tag_group
[2]]
916 if "no-agent" not in tag_choices
:
917 tag_group
[2].insert(0, ("no-agent", _("No agent"), []))
919 if "special-agents" not in tag_choices
:
920 tag_group
[2].insert(0,
921 ("special-agents", _("Use all enabled datasource programs"), ["tcp"]))
923 if "all-agents" not in tag_choices
:
925 0, ("all-agents", _("Contact Check_MK agent and all enabled datasource programs"),
928 if "cmk-agent" not in tag_choices
:
930 0, ("cmk-agent", _("Contact either Check_MK Agent or use datasource program"), ["tcp"]))
932 # Change title of cmk-agent tag choice and move to top
933 for index
, tag_choice
in enumerate(tag_group
[2]):
934 if tag_choice
[0] == "cmk-agent":
935 tag_choice_list
= list(tag_group
[2].pop(index
))
936 tag_choice_list
[1] = _("Contact either Check_MK Agent or use datasource program")
937 tag_group
[2].insert(0, tuple(tag_choice_list
))
941 def host_tag_groups():
942 """Returns the effective set of tag groups defined. This includes
943 the implicitly declared builtin host tags. This function must be used by
944 the GUI code to get the tag group definitions."""
945 return BuiltinTags().get_effective_tag_groups(wato_host_tags
)
949 """Returns the effective set of auxiliary tags defined. This includes
950 the implicitly declared builtin host tags. This function must be used by
951 the GUI code to get the auxiliay tag definitions."""
952 return BuiltinTags().get_effective_aux_tags(wato_aux_tags
)
956 # .--Sites---------------------------------------------------------------.
958 # | / ___|(_) |_ ___ ___ |
959 # | \___ \| | __/ _ \/ __| |
960 # | ___) | | || __/\__ \ |
961 # | |____/|_|\__\___||___/ |
963 # +----------------------------------------------------------------------+
964 # | The config module provides some helper functions for sites. |
965 # '----------------------------------------------------------------------'
969 return os
.environ
["OMD_SITE"]
973 return "/%s/" % omd_site()
976 use_siteicons
= False
979 def default_single_site_configuration():
982 'alias': _("Local site %s") % omd_site(),
983 'socket': ("local", None),
984 'disable_wato': True,
989 'replicate_ec': False,
1004 # TODO: Cleanup: Make clear that this function is used by the status GUI (and not WATO)
1005 # and only returns the currently enabled sites. Or should we redeclare the "disabled" state
1006 # to disable the sites at all?
1007 # TODO: Rename this!
1008 # TODO: All site listing functions should return the same data structure, e.g. a list of
1009 # pairs (site_id, site)
1012 [(name
, site(name
)) for name
in sitenames() if not site(name
).get("disabled", False)])
1015 def configured_sites():
1016 return [(site_id
, site(site_id
)) for site_id
in sitenames()]
1019 def has_wato_slave_sites():
1020 return bool(wato_slave_sites())
1023 def is_wato_slave_site():
1024 return _has_distributed_wato_file() and not has_wato_slave_sites()
1027 def _has_distributed_wato_file():
1028 return os
.path
.exists(cmk
.utils
.paths
.check_mk_config_dir
+ "/distributed_wato.mk") \
1029 and os
.stat(cmk
.utils
.paths
.check_mk_config_dir
+ "/distributed_wato.mk").st_size
!= 0
1032 def get_login_sites():
1033 """Returns the WATO slave sites a user may login and the local site"""
1034 return get_login_slave_sites() + [omd_site()]
1037 # TODO: All site listing functions should return the same data structure, e.g. a list of
1038 # pairs (site_id, site)
1039 def get_login_slave_sites():
1040 """Returns a list of site ids which are WATO slave sites and users can login"""
1042 for site_id
, site_spec
in wato_slave_sites():
1043 if site_spec
.get('user_login', True) and not site_is_local(site_id
):
1044 login_sites
.append(site_id
)
1048 def wato_slave_sites():
1049 return [(site_id
, s
) for site_id
, s
in sites
.items() if s
.get("replication")]
1054 for site_id
, s
in user
.authorized_sites():
1055 sorted_choices
.append((site_id
, s
['alias']))
1056 return sorted(sorted_choices
, key
=lambda k
: k
[1], cmp=lambda a
, b
: cmp(a
.lower(), b
.lower()))
1060 s
= dict(sites
.get(site_id
, {}))
1061 # Now make sure that all important keys are available.
1062 # Add missing entries by supplying default values.
1063 s
.setdefault("alias", site_id
)
1064 s
.setdefault("socket", ("local", None))
1065 s
.setdefault("url_prefix", "../") # relative URL from /check_mk/
1070 def site_is_local(site_id
):
1071 family_spec
, address_spec
= site(site_id
)["socket"]
1073 if _is_local_socket_spec(family_spec
, address_spec
):
1076 if family_spec
== "proxy" and _is_local_socket_spec(*address_spec
["socket"]):
1082 def _is_local_socket_spec(family_spec
, address_spec
):
1083 if family_spec
== "local":
1086 if family_spec
== "unix" and address_spec
["path"] == cmk
.utils
.paths
.livestatus_unix_socket
:
1093 for site_name
, _site
in sites
.items():
1094 if site_is_local(site_name
):
1099 def is_single_local_site():
1102 elif len(sites
) == 0:
1105 # Also use Multisite mode if the one and only site is not local
1106 sitename
= sites
.keys()[0]
1107 return site_is_local(sitename
)
1110 def site_attribute_default_value():
1111 def_site
= default_site()
1112 authorized_site_ids
= [x
[0] for x
in user
.authorized_sites(unfiltered_sites
=configured_sites())]
1113 if def_site
and def_site
in authorized_site_ids
:
1117 def site_attribute_choices():
1118 authorized_site_ids
= [x
[0] for x
in user
.authorized_sites(unfiltered_sites
=configured_sites())]
1119 return site_choices(filter_func
=lambda site_id
, site
: site_id
in authorized_site_ids
)
1122 def site_choices(filter_func
=None):
1124 for site_id
, site_spec
in sites
.items():
1125 if filter_func
and not filter_func(site_id
, site_spec
):
1129 if site_spec
.get("alias"):
1130 title
+= " - " + site_spec
["alias"]
1132 choices
.append((site_id
, title
))
1134 return sorted(choices
, key
=lambda s
: s
[1])
1137 def get_event_console_site_choices():
1138 return site_choices(
1139 filter_func
=lambda site_id
, site
: site_is_local(site_id
) or site
.get("replicate_ec"))
1143 # .--Plugins-------------------------------------------------------------.
1145 # | | _ \| |_ _ __ _(_)_ __ ___ |
1146 # | | |_) | | | | |/ _` | | '_ \/ __| |
1147 # | | __/| | |_| | (_| | | | | \__ \ |
1148 # | |_| |_|\__,_|\__, |_|_| |_|___/ |
1150 # +----------------------------------------------------------------------+
1151 # | Handling of our own plugins. In plugins other software pieces can |
1152 # | declare defaults for configuration variables. |
1153 # '----------------------------------------------------------------------'
1156 def load_plugins(force
):
1157 utils
.load_web_plugins("config", globals())
1159 # Make sure, builtin roles are present, even if not modified and saved with WATO.
1160 for br
in builtin_role_ids
:
1161 roles
.setdefault(br
, {})
1164 def theme_choices():
1166 ("classic", _("Classic")),
1167 ("facelift", _("Modern")),
1171 def get_page_heading():
1172 if "%s" in page_heading
:
1173 return page_heading
% (site(omd_site()).get('alias', _("GUI")))