Cleanup config.nodes_of
[check_mk.git] / web / app / index.wsgi
blob80db921252c6e74d6b40efaa72e2a1e0802535f3
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.
27 import httplib
28 import os
29 import traceback
31 import livestatus
33 import cmk.utils.paths
34 import cmk.utils.store as store
35 import cmk.utils.profile
37 import cmk.gui.i18n
38 import cmk.gui.sites as sites
39 import cmk.gui.config as config
40 import cmk.gui.modules as modules
41 import cmk.gui.pages as pages
42 import cmk.gui.userdb as userdb
43 import cmk.gui.login as login
44 import cmk.gui.log as log
45 import cmk.gui.htmllib
46 import cmk.gui.http
47 import cmk.gui.globals
48 from cmk.gui.log import logger
49 from cmk.gui.i18n import _
50 from cmk.gui.globals import html
52 from cmk.gui.exceptions import (
53 MKUserError,
54 MKConfigError,
55 MKGeneralException,
56 MKAuthException,
57 MKUnauthenticatedException,
58 FinalizeRequest,
59 HTTPRedirect,
63 class Application(object):
64 """The Check_MK GUI WSGI entry point"""
66 def __init__(self, environ, start_response):
67 self._environ = environ
68 self._start_response = start_response
69 self._request = cmk.gui.http.Request(environ)
70 self._response = cmk.gui.http.Response(is_secure=self._request.is_ssl_request)
72 # Register application object for access from other modules
73 cmk.gui.globals.current_app.set_current(self)
75 # Initialize some simple per request global namespace. Nothing fancy here.
76 # Each request starts with a fresh instance.
77 self.g = {}
79 # Create an object that contains all data about the request and
80 # helper functions for creating valid HTML. Parse URI and
81 # store results in the request object for later usage.
82 try:
83 h = cmk.gui.htmllib.html(self._request, self._response)
84 cmk.gui.globals.html.set_current(h)
85 except Exception:
86 logger.exception("Failed to process request")
87 self._response.headers["Content-type"] = "text/plain; charset=UTF-8"
88 self._response.stream.write(
89 "Failed to process request. Have a look at 'var/log/web.log' for more information.\n"
91 return
93 self._process_request()
95 def _process_request(self):
96 try:
97 config.initialize()
99 with cmk.utils.profile.Profile(
100 enabled=self._profiling_enabled(),
101 profile_file=os.path.join(cmk.utils.paths.var_dir, "multisite.profile")):
102 self._handle_request()
104 except HTTPRedirect, e:
105 self._response.status_code = e.status
106 self._response.headers["Location"] = e.url
108 except FinalizeRequest, e:
109 self._response.status_code = e.status
111 except (
112 MKUserError,
113 MKAuthException,
114 MKUnauthenticatedException,
115 MKConfigError,
116 MKGeneralException,
117 livestatus.MKLivestatusNotFoundError,
118 livestatus.MKLivestatusException,
119 ), e:
120 # TODO: Refactor all the special cases handled here to simplify the exception handling
121 ty = type(e)
122 if ty == livestatus.MKLivestatusNotFoundError:
123 title = _("Data not found")
124 plain_title = _("Livestatus-data not found")
125 elif isinstance(e, livestatus.MKLivestatusException):
126 title = _("Livestatus problem")
127 plain_title = _("Livestatus problem")
128 else:
129 title = e.title()
130 plain_title = e.plain_title()
132 if self._plain_error():
133 html.set_output_format("text")
134 html.write("%s: %s\n" % (plain_title, e))
135 elif not self._fail_silently():
136 html.header(title)
137 html.show_error(e)
138 html.footer()
140 # Some exception need to set a specific HTTP status code
141 if ty == MKUnauthenticatedException:
142 self._response.status_code = httplib.UNAUTHORIZED
143 elif ty == livestatus.MKLivestatusException:
144 self._response.status_code = httplib.BAD_GATEWAY
146 if ty in [MKConfigError, MKGeneralException]:
147 logger.error(_("%s: %s") % (plain_title, e))
149 except Exception, e:
150 logger.exception()
151 if self._plain_error():
152 html.set_output_format("text")
153 html.write(_("Internal error") + ": %s\n" % e)
154 elif not self._fail_silently():
155 crash_handler = pages.get_page_handler("gui_crash")
156 if not crash_handler:
157 raise
158 crash_handler()
160 finally:
161 try:
162 self._teardown()
163 except:
164 logger.exception()
165 raise
167 def _teardown(self):
168 """Final steps that are performed after each handled HTTP request"""
169 store.release_all_locks()
170 userdb.finalize()
171 sites.disconnect()
172 html.finalize()
173 cmk.gui.globals.html.unset_current()
175 def _handle_request(self):
176 html.init_modes()
178 # Make sure all plugins are avaiable as early as possible. At least
179 # we need the plugins (i.e. the permissions declared in these) at the
180 # time before the first login for generating auth.php.
181 modules.load_all_plugins()
183 # Clean up left over livestatus + connection objects (which are
184 # globally stored in sites module). This is a quick fix for the 1.5
185 # and will be cleaned up in 1.6 by storing these objects in the user
186 # request context instead of a global context.
187 sites.disconnect()
189 handler = pages.get_page_handler(html.myfile, self._page_not_found)
191 # Some pages do skip authentication. This is done by adding
192 # noauth: to the page hander, e.g. "noauth:run_cron" : ...
193 # TODO: Eliminate those "noauth:" pages. Eventually replace it by call using
194 # the now existing default automation user.
195 if handler == self._page_not_found:
196 handler = pages.get_page_handler("noauth:" + html.myfile, self._page_not_found)
197 if handler != self._page_not_found:
198 try:
199 handler()
200 except Exception, e:
201 self._show_exception_info(e)
202 raise FinalizeRequest(httplib.OK)
204 # Ensure the user is authenticated. This call is wrapping all the different
205 # authentication modes the Check_MK GUI supports and initializes the logged
206 # in user objects.
207 if not login.authenticate(self._request):
208 self._handle_not_authenticated()
210 # Initialize the multiste cmk.gui.i18n. This will be replaced by
211 # language settings stored in the user profile after the user
212 # has been initialized
213 self._localize_request()
215 # Update the UI theme with the attribute configured by the user
216 html.set_theme(config.user.get_attribute("ui_theme"))
218 self._ensure_general_access()
219 handler()
221 def _show_exception_info(self, e):
222 html.write_text("%s" % e)
223 if config.debug:
224 html.write_text(traceback.format_exc())
226 def _handle_not_authenticated(self):
227 if self._fail_silently():
228 # While api call don't show the login dialog
229 raise MKUnauthenticatedException(_('You are not authenticated.'))
231 # Redirect to the login-dialog with the current url as original target
232 # Never render the login form directly when accessing urls like "index.py"
233 # or "dashboard.py". This results in strange problems.
234 if html.myfile != 'login':
235 raise HTTPRedirect('%scheck_mk/login.py?_origtarget=%s' %
236 (config.url_prefix(), html.urlencode(html.makeuri([]))))
237 else:
238 # This either displays the login page or validates the information submitted
239 # to the login form. After successful login a http redirect to the originally
240 # requested page is performed.
241 login_page = login.LoginPage()
242 login_page.set_no_html_output(self._plain_error())
243 login_page.handle_page()
245 raise FinalizeRequest(httplib.OK)
247 def _localize_request(self):
248 previous_language = cmk.gui.i18n.get_current_language()
249 user_language = html.get_ascii_input("lang", config.user.language())
251 html.set_language_cookie(user_language)
252 cmk.gui.i18n.localize(user_language)
254 # All plugins might have to be reloaded due to a language change. Only trigger
255 # a second plugin loading when the user is really using a custom localized GUI.
256 # Otherwise the load_all_plugins() at the beginning of the request is sufficient.
257 if cmk.gui.i18n.get_current_language() != previous_language:
258 modules.load_all_plugins()
260 def _fail_silently(self):
261 """Ajax-Functions want no HTML output in case of an error but
262 just a plain server result code of 500"""
263 return html.request.has_var("_ajaxid")
265 def _plain_error(self):
266 """Webservice functions may decide to get a normal result code
267 but a text with an error message in case of an error"""
268 return html.request.has_var("_plain_error") or html.myfile == "webapi"
270 def _profiling_enabled(self):
271 if config.profile is False:
272 return False # Not enabled
274 if config.profile == "enable_by_var" and not html.request.has_var("_profile"):
275 return False # Not enabled by HTTP variable
277 return True
279 # TODO: This is a page handler. It should not be located in generic application
280 # object. Move it to another place
281 def _page_not_found(self):
282 if html.request.has_var("_plain_error"):
283 html.write(_("Page not found"))
284 else:
285 html.header(_("Page not found"))
286 html.show_error(_("This page was not found. Sorry."))
287 html.footer()
289 def _ensure_general_access(self):
290 if config.user.may("general.use"):
291 return
293 reason = [
294 _("You are not authorized to use the Check_MK GUI. Sorry. "
295 "You are logged in as <b>%s</b>.") % config.user.id
298 if config.user.role_ids:
299 reason.append(_("Your roles are <b>%s</b>.") % ", ".join(config.user.role_ids))
300 else:
301 reason.append(_("<b>You do not have any roles.</b>"))
303 reason.append(
304 _("If you think this is an error, please ask your administrator "
305 "to check the permissions configuration."))
307 if login.auth_type == 'cookie':
308 reason.append(
309 _("<p>You have been logged out. Please reload the page "
310 "to re-authenticate.</p>"))
311 login.del_auth_cookie()
313 raise MKAuthException(" ".join(reason))
315 def __iter__(self):
316 """Is called by the WSGI server to serve the current page"""
317 return self._response(self._environ, self._start_response)
320 # Early initialization upon first start of the application by the server
321 def _initialize():
322 log.init_logging()
323 modules.init_modules()
326 # Run the global application initialization code here. It is called
327 # only once during the startup of the application server.
328 _initialize()