Пример #1
0
    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)
Пример #2
0
 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")
Пример #3
0
 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"])
Пример #4
0
    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))
Пример #5
0
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
Пример #6
0
    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))
Пример #7
0
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")
Пример #8
0
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()
Пример #9
0
    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))
Пример #10
0
 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()))
Пример #11
0
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))
Пример #12
0
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
Пример #13
0
    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,
            }))
Пример #14
0
 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))
Пример #15
0
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))
Пример #16
0
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))
Пример #17
0
    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)
Пример #18
0
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]
Пример #19
0
 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()))
Пример #20
0
 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))
Пример #21
0
    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()))
Пример #22
0
    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")
Пример #23
0
    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),
                }
            )
        )
Пример #24
0
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)
Пример #25
0
    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)
Пример #26
0
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()
Пример #27
0
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))
Пример #28
0
    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),
            }))
Пример #29
0
    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))
Пример #30
0
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))