def page(self) -> None: file_name = self.file_name(self._request) file_content = self._get_agent_output_file() response.set_content_type("text/plain") response.headers["Content-Disposition"] = "Attachment; filename=%s" % file_name response.set_data(file_content)
def _ajax_tag_tree_enter(self): response.set_content_type("application/json") self._load() path = request.get_str_input_mandatory("path").split("|") if request.var("path") else [] self._cwds[self._current_tree_id] = path self._save_user_settings() response.set_data("OK")
def _send_download(self, keys, key_id): key = keys[key_id] response.headers[ "Content-Disposition"] = "Attachment; filename=%s" % self._file_name( key_id, key) response.headers["Content-type"] = "application/x-pem-file" response.set_data(key["private_key"] + key["certificate"])
def show(self, view: "View", rows: Rows) -> None: csv_separator = request.get_str_input_mandatory("csv_separator", ";") first = True resp = [] for cell in view.group_cells + view.row_cells: if first: first = False else: resp.append(csv_separator) title = cell.export_title() resp.append('"%s"' % self._format_for_csv(title)) for row in rows: resp.append("\n") first = True for cell in view.group_cells + view.row_cells: if first: first = False else: resp.append(csv_separator) joined_row = join_row(row, cell) content = cell.render_for_export(joined_row) resp.append('"%s"' % self._format_for_csv(content)) response.set_data("".join(resp))
def ajax_tree_openclose() -> None: tree = request.get_str_input_mandatory("tree") name = request.get_unicode_input_mandatory("name") config.user.set_tree_state(tree, name, request.get_str_input("state")) config.user.save_tree_states() response.set_data('OK') # Write out something to make debugging easier
def handle_page(self) -> None: """The page handler, called by the page registry""" response.set_content_type("application/json") try: action_response = self.page() resp = { "result_code": 0, "result": action_response, "severity": "success" } except MKMissingDataError as e: resp = {"result_code": 1, "result": str(e), "severity": "success"} except MKException as e: resp = {"result_code": 1, "result": str(e), "severity": "error"} except Exception as e: if active_config.debug: raise logger.exception("error calling AJAX page handler") handle_exception_as_gui_crash_report( plain_error=True, show_crash_link=getattr(g, "may_see_crash_reports", False), ) resp = {"result_code": 1, "result": str(e), "severity": "error"} response.set_data(json.dumps(resp))
def page_run_cron() -> None: lock_file = _lock_file() # Prevent cron jobs from being run too often, also we need # locking in order to prevent overlapping runs if lock_file.exists(): last_run = lock_file.stat().st_mtime if time.time() - last_run < 59: raise MKGeneralException("Cron called too early. Skipping.") with lock_file.open("wb"): pass # touches the file # The cron page is accessed unauthenticated. After leaving the page_run_cron area # into the job functions we always want to have a user context initialized to keep # the code free from special cases (if no user logged in, then...). # The jobs need to be run in privileged mode in general. Some jobs, like the network # scan, switch the user context to a specific other user during execution. with store.locked(lock_file), SuperUserContext(): logger.debug("Starting cron jobs") for cron_job in multisite_cronjobs: try: job_name = cron_job.__name__ logger.debug("Starting [%s]", job_name) cron_job() logger.debug("Finished [%s]", job_name) except Exception: response.set_data("An exception occured. Take a look at the web.log.\n") logger.exception("Exception in cron job [%s]", job_name) logger.debug("Finished all cron jobs") response.set_data("OK\n")
def serve_json(data, profile=None): content_type = 'application/json' if profile is not None: content_type += ';profile="%s"' % (profile,) response.set_content_type(content_type) response.set_data(json.dumps(data)) return response._get_current_object()
def _write_csv(self, csv_separator: str) -> None: rows = self.rows limit = self.limit omit_headers = self.options["omit_headers"] # Apply limit after search / sorting etc. if limit is not None: rows = rows[:limit] resp = [] # If we have no group headers then paint the headers now if not omit_headers and self.rows and not isinstance( self.rows[0], GroupHeader): resp.append( csv_separator.join([ escaping.strip_tags(header.title) or "" for header in self.headers ]) + "\n") for row in rows: if isinstance(row, GroupHeader): continue resp.append( csv_separator.join( [escaping.strip_tags(cell.content) for cell in row.cells])) resp.append("\n") response.set_data("".join(resp))
def _execute_push_profile(self): try: response.set_data(str(watolib.mk_repr(self._automation_push_profile()))) except Exception as e: logger.exception("error pushing profile") if config.debug: raise response.set_data(_("Internal automation error: %s\n%s") % (e, traceback.format_exc()))
def page_api() -> None: try: if not request.has_var("output_format"): response.set_content_type("application/json") output_format = "json" else: output_format = request.get_ascii_input_mandatory( "output_format", "json").lower() if output_format not in _FORMATTERS: response.set_content_type("text/plain") raise MKUserError( None, "Only %s are supported as output formats" % " and ".join('"%s"' % f for f in _FORMATTERS), ) # TODO: Add some kind of helper for boolean-valued variables? pretty_print = False pretty_print_var = request.get_str_input_mandatory( "pretty_print", "no").lower() if pretty_print_var not in ("yes", "no"): raise MKUserError(None, 'pretty_print must be "yes" or "no"') pretty_print = pretty_print_var == "yes" api_call = _get_api_call() _check_permissions(api_call) request_object = _get_request(api_call) _check_formats(output_format, api_call, request_object) _check_request_keys(api_call, request_object) resp = _execute_action(api_call, request_object) except MKAuthException as e: resp = { "result_code": 1, "result": _("Authorization Error. Insufficent permissions for '%s'") % e, } except MKException as e: resp = { "result_code": 1, "result": _("Checkmk exception: %s\n%s") % (e, "".join(traceback.format_exc())), } except Exception: if active_config.debug: raise logger.exception("error handling web API call") resp = { "result_code": 1, "result": _("Unhandled exception: %s") % traceback.format_exc(), } response.set_data( _FORMATTERS[output_format][1 if pretty_print else 0](resp))
def _answer_graph_image_request() -> None: try: host_name = request.var("host") if not host_name: raise MKGeneralException(_("Missing mandatory \"host\" parameter")) service_description = request.var("service", "_HOST_") site = request.var("site") # FIXME: We should really enforce site here. But it seems that the notification context # has no idea about the site of the host. This could be optimized later. #if not site: # raise MKGeneralException("Missing mandatory \"site\" parameter") try: row = get_graph_data_from_livestatus(site, host_name, service_description) except livestatus.MKLivestatusNotFoundError: if config.debug: raise raise Exception( _("Cannot render graph: host %s, service %s not found.") % (host_name, service_description)) site = row["site"] # Always use 25h graph in notifications end_time = time.time() start_time = end_time - (25 * 3600) graph_render_options = graph_image_render_options() graph_identification = ( "template", { "site": site, "host_name": host_name, "service_description": service_description, "graph_index": None, # all graphs }) graph_data_range = graph_image_data_range(graph_render_options, start_time, end_time) graph_recipes = graph_identification_types.create_graph_recipes( graph_identification, destination=html_render.GraphDestinations.notification) num_graphs = request.get_integer_input("num_graphs") or len(graph_recipes) graphs = [] for graph_recipe in graph_recipes[:num_graphs]: graph_artwork = artwork.compute_graph_artwork(graph_recipe, graph_data_range, graph_render_options) graph_png = render_graph_image(graph_artwork, graph_data_range, graph_render_options) graphs.append(base64.b64encode(graph_png).decode("ascii")) response.set_data(json.dumps(graphs)) except Exception as e: logger.error("Call to ajax_graph_images.py failed: %s\n%s", e, traceback.format_exc()) if config.debug: raise
def _ajax_speedometer(self): response.set_content_type("application/json") try: # Try to get values from last call in order to compute # driftig speedometer-needle and to reuse the scheduled # check reate. # TODO: Do we need a get_float_input_mandatory? last_perc = float(request.get_str_input_mandatory("last_perc")) scheduled_rate = float( request.get_str_input_mandatory("scheduled_rate")) last_program_start = request.get_integer_input_mandatory( "program_start") # Get the current rates and the program start time. If there # are more than one site, we simply add the start times. data = sites.live().query_summed_stats( "GET status\n" "Columns: service_checks_rate program_start") current_rate = data[0] program_start = data[1] # Recompute the scheduled_rate only if it is not known (first call) # or if one of the sites has been restarted. The computed value cannot # change during the monitoring since it just reflects the configuration. # That way we save CPU resources since the computation of the # scheduled checks rate needs to loop over all hosts and services. if last_program_start != program_start: # These days, we configure the correct check interval for Checkmk checks. # We do this correctly for active and for passive ones. So we can simply # use the check_interval of all services. Hosts checks are ignored. # # Manually added services without check_interval could be a problem, but # we have no control there. scheduled_rate = (sites.live().query_summed_stats( "GET services\n" "Stats: suminv check_interval\n")[0] / 60.0) percentage = 100.0 * current_rate / scheduled_rate title = _( "Scheduled service check rate: %.1f/s, current rate: %.1f/s, that is " "%.0f%% of the scheduled rate") % (scheduled_rate, current_rate, percentage) except Exception as e: scheduled_rate = 0.0 program_start = 0 percentage = 0 last_perc = 0.0 title = _("No performance data: %s") % e response.set_data( json.dumps({ "scheduled_rate": scheduled_rate, "program_start": program_start, "percentage": percentage, "last_perc": last_perc, "title": title, }))
def _execute_cmk_automation(self): cmk_command = request.get_str_input_mandatory("automation") args = watolib.mk_eval(request.get_str_input_mandatory("arguments")) indata = watolib.mk_eval(request.get_str_input_mandatory("indata")) stdin_data = watolib.mk_eval(request.get_str_input_mandatory("stdin_data")) timeout = watolib.mk_eval(request.get_str_input_mandatory("timeout")) result = watolib.check_mk_local_automation(cmk_command, args, indata, stdin_data, timeout) # Don't use write_text() here (not needed, because no HTML document is rendered) response.set_data(repr(result))
def _export_json_export(view: "View", rows: Rows) -> None: filename = '%s-%s.json' % (view.name, time.strftime('%Y-%m-%d_%H-%M-%S', time.localtime(time.time()))) response.headers[ "Content-Disposition"] = "Attachment; filename=\"%s\"" % ensure_str( filename) response.set_data(_get_json_body(view, rows))
def _export_json_export(view: "View", rows: Rows) -> None: filename = "%s-%s.json" % ( view.name, time.strftime("%Y-%m-%d_%H-%M-%S", time.localtime(time.time())), ) response.headers[ "Content-Disposition"] = 'Attachment; filename="%s"' % filename response.set_data(_get_json_body(view, rows))
def _export_audit_log(self, audit: List[AuditLogStore.Entry]) -> ActionResult: response.set_content_type("text/csv") if self._options["display"] == "daily": filename = "wato-auditlog-%s_%s.csv" % ( render.date(time.time()), render.time_of_day(time.time()), ) else: filename = "wato-auditlog-%s_%s_days.csv" % ( render.date(time.time()), self._options["display"][1], ) response.headers[ "Content-Disposition"] = 'attachment; filename="%s"' % filename titles = [ _("Date"), _("Time"), _("Object type"), _("Object"), _("User"), _("Action"), _("Summary"), ] if self._show_details: titles.append(_("Details")) resp = [] resp.append(",".join(titles) + "\n") for entry in audit: columns = [ render.date(int(entry.time)), render.time_of_day(int(entry.time)), entry.object_ref.object_type.name if entry.object_ref else "", entry.object_ref.ident if entry.object_ref else "", entry.user_id, entry.action, '"' + escaping.strip_tags(entry.text).replace('"', "'") + '"', ] if self._show_details: columns.append( '"' + escaping.strip_tags(entry.diff_text).replace('"', "'") + '"') resp.append(",".join(columns) + "\n") response.set_data("".join(resp)) return FinalizeRequest(code=200)
def serve_json(data, profile=None): content_type = 'application/json' if profile is not None: content_type += ';profile="%s"' % (profile, ) response.set_content_type(content_type) response.set_data(json.dumps(data)) # HACK: See wrap_with_validation. response.original_data = data # type: ignore[attr-defined] # TODO: Why don't we just return response? We access a private method of LocalProxy here... return response._get_current_object() # type: ignore[attr-defined]
def _execute_automation_command(self, automation_command): try: # Don't use write_text() here (not needed, because no HTML document is rendered) automation = automation_command() response.set_data(repr(automation.execute(automation.get_request()))) except Exception as e: logger.exception("error executing automation command") if config.debug: raise response.set_data(_("Internal automation error: %s\n%s") % (e, traceback.format_exc()))
def handle_page(self) -> None: try: response.set_content_type("application/cbor") response.set_data(cbor.encode(self.page())) except Exception as e: response.status_code = http_client.INTERNAL_SERVER_ERROR handle_exception_as_gui_crash_report( plain_error=True, show_crash_link=getattr(g, "may_see_crash_reports", False), ) response.set_data(str(e))
def page(self): config.user.need_permission("general.see_crash_reports") filename = "Checkmk_Crash_%s_%s_%s.tar.gz" % \ (urlencode(self._site_id), urlencode(self._crash_id), time.strftime("%Y-%m-%d_%H-%M-%S")) response.headers[ 'Content-Disposition'] = 'Attachment; filename=%s' % filename response.headers['Content-Type'] = 'application/x-tar' response.set_data( _pack_crash_report(self._get_serialized_crash_report()))
def _ajax_tag_tree(self): response.set_content_type("application/json") self._load() new_tree = request.var("tree_id") if new_tree not in self._trees: raise MKUserError("conf", _("This virtual host tree does not exist.")) self._current_tree_id = new_tree self._save_user_settings() response.set_data("OK")
def page(self): if not user.may("wato.automation"): raise MKAuthException(_("This account has no permission for automation.")) response.set_content_type("text/plain") _set_version_headers() # Parameter was added with 1.5.0p10 if not request.has_var("_version"): raise MKGeneralException(_("Your central site is incompatible with this remote site")) # - _version and _edition_short were added with 1.5.0p10 to the login call only # - x-checkmk-version and x-checkmk-edition were added with 2.0.0p1 # Prefer the headers and fall back to the request variables for now. central_version = ( request.headers["x-checkmk-version"] if "x-checkmk-version" in request.headers else request.get_ascii_input_mandatory("_version") ) central_edition_short = ( request.headers["x-checkmk-edition"] if "x-checkmk-edition" in request.headers else request.get_ascii_input_mandatory("_edition_short") ) if not compatible_with_central_site( central_version, central_edition_short, cmk_version.__version__, cmk_version.edition_short(), ): raise MKGeneralException( _( "Your central site (Version: %s, Edition: %s) is incompatible with this " "remote site (Version: %s, Edition: %s)" ) % ( central_version, central_edition_short, cmk_version.__version__, cmk_version.edition_short(), ) ) response.set_data( repr( { "version": cmk_version.__version__, "edition_short": cmk_version.edition_short(), "login_secret": _get_login_secret(create_on_demand=True), } ) )
def ajax_graph(): response.set_content_type("application/json") try: context_var = request.get_str_input_mandatory("context") context = json.loads(context_var) response_data = render_ajax_graph(context) response.set_data(json.dumps(response_data)) except Exception as e: logger.error("Ajax call ajax_graph.py failed: %s\n%s", e, traceback.format_exc()) if config.debug: raise response.set_data("ERROR: %s" % e)
def page(self) -> None: if not user.may("wato.diagnostics"): raise MKAuthException( _("Sorry, you lack the permission for downloading diagnostics dumps.") ) site = request.get_ascii_input_mandatory("site") tarfile_name = request.get_ascii_input_mandatory("tarfile_name") file_content = self._get_diagnostics_dump_file(site, tarfile_name) response.set_content_type("application/x-tgz") response.headers["Content-Disposition"] = "Attachment; filename=%s" % tarfile_name response.set_data(file_content)
def _show_crash_dump_message( crash: "GUICrashReport", plain_text: bool, fail_silently: bool, show_crash_link: Optional[bool] ) -> None: """Create a crash dump from a GUI exception and display a message to the user""" if show_crash_link is None: show_crash_link = user.may("general.see_crash_reports") title = _("Internal error") message = "%s: %s<br>\n<br>\n" % (title, crash.crash_info["exc_value"]) # Do not reveal crash context information to unauthenticated users or not permitted # users to prevent disclosure of internal information if not show_crash_link: message += _( "An internal error occurred while processing your request. " "You can report this issue to your Checkmk administrator. " "Detailed information can be found on the crash report page " "or in <tt>var/log/web.log</tt>." ) else: crash_url = makeuri( request, [ ("site", omd_site()), ("crash_id", crash.ident_to_text()), ], filename="crash.py", ) message += ( _( "An internal error occured while processing your request. " "You can report this issue to the Checkmk team to help " 'fixing this issue. Please open the <a href="%s">crash report page</a> ' "and use the form for reporting the problem." ) % crash_url ) if plain_text: response.set_content_type("text/plain") response.set_data("%s\n" % escaping.strip_tags(message)) return if fail_silently: return html.header(title, Breadcrumb()) html.show_error(message) html.footer()
def ajax_snapin(): """Renders and returns the contents of the requested sidebar snapin(s) in JSON format""" response.set_content_type("application/json") user_config = UserSidebarConfig(user, config.sidebar) snapin_id = request.var("name") snapin_ids = ([snapin_id] if snapin_id else request.get_str_input_mandatory("names", "").split(",")) snapin_code: List[str] = [] for snapin_id in snapin_ids: try: snapin_instance = user_config.get_snapin(snapin_id).snapin_type() except KeyError: continue # Skip not existing snapins if not snapin_instance.may_see(): continue # When restart snapins are about to be refreshed, only render # them, when the core has been restarted after their initial # rendering if not snapin_instance.refresh_regularly( ) and snapin_instance.refresh_on_restart(): since = request.get_float_input_mandatory("since", 0) newest = since for site in sites.states().values(): prog_start = site.get("program_start", 0) if prog_start > newest: newest = prog_start if newest <= since: # no restart snapin_code.append("") continue with output_funnel.plugged(): try: snapin_instance.show() except Exception as e: write_snapin_exception(e) e_message = ( _("Exception during element refresh (element '%s')") % snapin_instance.type_name()) logger.error("%s %s: %s", request.requested_url, e_message, traceback.format_exc()) finally: snapin_code.append(output_funnel.drain()) response.set_data(json.dumps(snapin_code))
def page(self): if not user.may("wato.automation"): raise MKAuthException( _("This account has no permission for automation.")) response.set_content_type("text/plain") # Parameter was added with 1.5.0p10 if not request.has_var("_version"): raise MKGeneralException( _("Your central site is incompatible with this remote site")) response.set_data( repr({ "version": cmk_version.__version__, "edition_short": cmk_version.edition_short(), "login_secret": _get_login_secret(create_on_demand=True), }))
def page(self): if not config.user.may("wato.automation"): raise MKAuthException(_("This account has no permission for automation.")) response.set_content_type("text/plain") if not request.has_var("_version"): # Be compatible to calls from sites using versions before 1.5.0p10. # Deprecate with 1.7 by throwing an exception in this situation. resp = _get_login_secret(create_on_demand=True) else: resp = { "version": cmk_version.__version__, "edition_short": cmk_version.edition_short(), "login_secret": _get_login_secret(create_on_demand=True), } response.set_data(repr(resp))
def robotmk_download_page() -> cmk.gui.pages.PageResult: user.need_permission("general.see_crash_reports") site_id, host_name, service_description = _get_mandatory_request_vars() filename = "RobotMK_report_%s_%s_%s_%s.tar.gz" % ( urlencode(site_id), urlencode(host_name), urlencode(service_description), time.strftime("%Y-%m-%d_%H-%M-%S"), ) response.headers[ "Content-Disposition"] = "Attachment; filename=%s" % filename response.headers["Content-Type"] = "application/x-tar" html_content: bytes = _get_html_from_livestatus(site_id, host_name, service_description)[0] response.set_data(_pack_html_content(html_content))