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.
27 from typing
import List
, Tuple
# pylint: disable=unused-import
29 # suppress "Cannot find module" error from mypy
30 import livestatus
# type: ignore
34 import cmk
.gui
.config
as config
35 from cmk
.gui
.globals import html
37 # .--API-----------------------------------------------------------------.
44 # +----------------------------------------------------------------------+
45 # | Functions und names for the public |
46 # '----------------------------------------------------------------------'
50 """Get Livestatus connection object matching the current site configuration
51 and user settings. On the first call the actual connection is being made."""
57 # Accessor for the status of a single site
58 def state(site_id
, deflt
=None):
59 """Get the status of a certain site. Returns a dictionary with various
60 entries. deflt is being returned in case the specified site doe not
61 exist or has no state."""
64 return _site_status
.get(site_id
, deflt
)
68 """Returns dictionary of all known site states."""
75 """Actively closes all Livestatus connections."""
76 global _live
, _site_status
81 def sites_using_foreign_cores():
83 for site_id
, core
in cores_by_site().items():
84 if core
not in ["cmc", None]:
85 site_ids
.append(site_id
)
87 return sorted(site_ids
)
90 def all_sites_use_foreign_cores():
91 return "cmc" not in cores_by_site().itervalues()
96 for site_id
, site_state
in states().items():
97 # Offline sites don't provide core info. Assume CMC is this case
98 if site_state
["state"] == "dead":
101 core
= site_state
.get("core")
103 cores
[site_id
] = core
108 def all_groups(what
):
109 # type: (str) -> List[Tuple[str, str]]
110 """Returns a list of host/service/contact groups (pairs of name/alias)
112 Groups are collected via livestatus from all sites. In case no alias is defined
113 the name is used as second element. The list is sorted by lower case alias in the first place."""
114 groups
= live().query("GET %sgroups\nCache: reload\nColumns: name alias\n" % what
)
115 return sorted([(name
, alias
or name
) for name
, alias
in groups
], key
=lambda e
: e
[1].lower())
119 # .--Internal------------------------------------------------------------.
121 # | |_ _|_ __ | |_ ___ _ __ _ __ __ _| | |
122 # | | || '_ \| __/ _ \ '__| '_ \ / _` | | |
123 # | | || | | | || __/ | | | | | (_| | | |
124 # | |___|_| |_|\__\___|_| |_| |_|\__,_|_| |
126 # +----------------------------------------------------------------------+
127 # | Internal functiions and variables |
128 # '----------------------------------------------------------------------'
130 # The global livestatus object. This is initialized automatically upon first access
131 # to the accessor function live()
134 # _site_status keeps a dictionary for each site with the following keys:
135 # "state" --> "online", "disabled", "down", "unreach", "dead" or "waiting"
136 # "exception" --> An error exception in case of down, unreach, dead or waiting
137 # "status_host_state" --> host state of status host (0, 1, 2 or None)
138 # "livestatus_version" --> Version of sites livestatus if "online"
139 # "program_version" --> Version of Nagios if "online"
143 # Build up a connection to livestatus to either a single site or multiple sites.
146 _connect_multiple_sites()
147 _set_livestatus_auth()
150 def _connect_multiple_sites():
152 enabled_sites
, disabled_sites
= _get_enabled_and_disabled_sites()
153 _set_initial_site_states(enabled_sites
, disabled_sites
)
155 if cmk
.is_managed_edition():
156 import cmk
.gui
.cme
.managed
as managed
157 _live
= managed
.CMEMultiSiteConnection(enabled_sites
, disabled_sites
)
159 _live
= livestatus
.MultiSiteConnection(enabled_sites
, disabled_sites
)
161 # Fetch status of sites by querying the version of Nagios and livestatus
162 # This may be cached by a proxy for up to the next configuration reload.
163 _live
.set_prepend_site(True)
164 for response
in _live
.query(
167 "Columns: livestatus_version program_version program_start num_hosts num_services"):
170 site_id
, v1
, v2
, ps
, num_hosts
, num_services
= response
172 e
= livestatus
.MKLivestatusQueryError("Invalid response to status query: %s" % response
)
174 site_id
= response
[0]
175 _update_site_status(site_id
, {
177 "status_host_state": None,
178 "state": _status_host_state_name(None),
185 "livestatus_version": v1
,
186 "program_version": v2
,
188 "num_hosts": num_hosts
,
189 "num_services": num_services
,
190 "core": v2
.startswith("Check_MK") and "cmc" or "nagios",
192 _live
.set_prepend_site(False)
194 # TODO(lm): Find a better way to make the Livestatus object trigger the update
195 # once self.deadsites is updated.
196 update_site_states_from_dead_sites()
199 def _get_enabled_and_disabled_sites():
200 enabled_sites
, disabled_sites
= {}, {}
202 for site_id
, site
in config
.user
.authorized_sites():
203 site
= _site_config_for_livestatus(site_id
, site
)
205 if config
.user
.is_site_disabled(site_id
):
206 disabled_sites
[site_id
] = site
208 enabled_sites
[site_id
] = site
210 return enabled_sites
, disabled_sites
213 def _site_config_for_livestatus(site_id
, site
):
216 if site
["socket"][0] == "proxy":
217 site
["cache"] = site
["socket"][1].get("cache", True)
219 site
["socket"] = encode_socket_for_livestatus(site_id
, site
["socket"])
224 def encode_socket_for_livestatus(site_id
, socket_spec
):
225 family_spec
, address_spec
= socket_spec
227 if family_spec
== "local":
228 return "unix:%s" % cmk
.utils
.paths
.livestatus_unix_socket
230 if family_spec
== "proxy":
231 return "unix:%sproxy/%s" % (cmk
.utils
.paths
.livestatus_unix_socket
, site_id
)
233 if family_spec
== "unix":
234 return "%s:%s" % (family_spec
, address_spec
["path"])
236 if family_spec
in ["tcp", "tcp6"]:
237 return "%s:%s:%d" % (family_spec
, address_spec
["address"][0], address_spec
["address"][1])
239 raise NotImplementedError()
242 def update_site_states_from_dead_sites():
243 # Get exceptions in case of dead sites
244 for site_id
, deadinfo
in live().dead_sites().items():
245 status_host_state
= deadinfo
.get("status_host_state")
248 "exception": deadinfo
["exception"],
249 "status_host_state": status_host_state
,
250 "state": _status_host_state_name(status_host_state
),
254 def _status_host_state_name(shs
):
261 }.get(shs
, "unknown")
264 def _init_site_status():
269 def _set_initial_site_states(enabled_sites
, disabled_sites
):
270 for site_id
, site
in enabled_sites
.items():
271 _set_site_status(site_id
, {"state": "dead", "site": site
})
273 for site_id
, site
in disabled_sites
.items():
274 _set_site_status(site_id
, {"state": "disabled", "site": site
})
277 def _set_site_status(site_id
, status
):
278 _site_status
[site_id
] = status
281 def _update_site_status(site_id
, status
):
282 _site_status
[site_id
].update(status
)
285 # If Multisite is retricted to data the user is a contact for, we need to set an
286 # AuthUser: header for livestatus.
287 def _set_livestatus_auth():
288 user_id
= _livestatus_auth_user()
289 if user_id
is not None:
290 _live
.set_auth_user('read', user_id
)
291 _live
.set_auth_user('action', user_id
)
293 # May the user see all objects in BI aggregations or only some?
294 if not config
.user
.may("bi.see_all"):
295 _live
.set_auth_user('bi', user_id
)
297 # May the user see all Event Console events or only some?
298 if not config
.user
.may("mkeventd.seeall"):
299 _live
.set_auth_user('ec', user_id
)
301 # Default auth domain is read. Please set to None to switch off authorization
302 _live
.set_auth_domain('read')
305 # Returns either None when no auth user shal be set or the name of the user
306 # to be used as livestatus auth user
307 def _livestatus_auth_user():
308 if not config
.user
.may("general.see_all"):
309 return config
.user
.id
311 force_authuser
= html
.request
.var("force_authuser")
312 if force_authuser
== "1":
313 return config
.user
.id
314 elif force_authuser
== "0":
317 return force_authuser
# set a different user
319 # TODO: Remove this with 1.5.0/1.6.0
320 if html
.output_format
!= 'html' \
321 and config
.user
.get_attribute("force_authuser_webservice"):
322 return config
.user
.id
324 if config
.user
.get_attribute("force_authuser"):
325 return config
.user
.id