예제 #1
0
def _add_scanned_hosts_to_folder(folder: "CREFolder",
                                 found: NetworkScanFoundHosts) -> None:
    network_scan_properties = folder.attribute("network_scan")

    translation = network_scan_properties.get("translate_names", {})

    entries = []
    for host_name, ipaddr in found:
        host_name = translate_hostname(translation, host_name)

        attrs = update_metadata({}, created_by=_("Network scan"))

        if "tag_criticality" in network_scan_properties:
            attrs["tag_criticality"] = network_scan_properties.get(
                "tag_criticality", "offline")

        if network_scan_properties.get("set_ipaddress", True):
            attrs["ipaddress"] = ipaddr

        if not Host.host_exists(host_name):
            entries.append((host_name, attrs, None))

    with store.lock_checkmk_configuration():
        folder.create_hosts(entries)
        folder.save()
예제 #2
0
 def _wrapper(param: typing.Mapping[str, Any]) -> cmk_http.Response:
     if not self.skip_locking and self.method != "get":
         with store.lock_checkmk_configuration():
             response = func(param)
     else:
         response = func(param)
     return response
예제 #3
0
파일: sites.py 프로젝트: epasham/checkmk
    def execute(self, request):
        # type: (PushSnapshotRequest) -> bool
        with store.lock_checkmk_configuration():
            multitar.extract_from_buffer(request.tar_content,
                                         cmk.gui.watolib.activate_changes.get_replication_paths())

            try:
                self._save_site_globals_on_slave_site(request.tar_content)

                # pending changes are lost
                cmk.gui.watolib.activate_changes.confirm_all_local_changes()

                hooks.call("snapshot-pushed")

                # Create rule making this site only monitor our hosts
                create_distributed_wato_file(request.site_id, is_slave=True)
            except Exception:
                raise MKGeneralException(
                    _("Failed to deploy configuration: \"%s\". "
                      "Please note that the site configuration has been synchronized "
                      "partially.") % traceback.format_exc())

            cmk.gui.watolib.changes.log_audit(
                None, "replication",
                _("Synchronized with master (my site id is %s.)") % request.site_id)

            return True
예제 #4
0
def page_handler() -> None:
    initialize_wato_html_head()

    if not config.wato_enabled:
        raise MKGeneralException(
            _("WATO is disabled. Please set <tt>wato_enabled = True</tt>"
              " in your <tt>multisite.mk</tt> if you want to use WATO."))

    # config.current_customer can not be checked with CRE repos
    if cmk_version.is_managed_edition() and not managed.is_provider(
            config.current_customer):  # type: ignore[attr-defined]
        raise MKGeneralException(
            _("Check_MK can only be configured on "
              "the managers central site."))

    current_mode = html.request.var("mode") or "main"
    mode_permissions, mode_class = _get_mode_permission_and_class(current_mode)

    display_options.load_from_html(html)

    if display_options.disabled(display_options.N):
        html.add_body_css_class("inline")

    # If we do an action, we aquire an exclusive lock on the complete WATO.
    if html.is_transaction():
        with store.lock_checkmk_configuration():
            _wato_page_handler(current_mode, mode_permissions, mode_class)
    else:
        _wato_page_handler(current_mode, mode_permissions, mode_class)
예제 #5
0
    def _create_snapshots(self):
        with store.lock_checkmk_configuration():
            if not self._changes:
                raise MKUserError(None, _("Currently there are no changes to activate."))

            if self._get_last_change_id() != self._activate_until:
                raise MKUserError(
                    None,
                    _("Another change has been made in the meantime. Please review it "
                      "to ensure you also want to activate it now and start the "
                      "activation again."))

            # Create (legacy) WATO config snapshot
            start = time.time()
            logger.debug("Snapshot creation started")
            # TODO: Remove/Refactor once new changes mechanism has been implemented
            #       This single function is responsible for the slow activate changes (python tar packaging..)
            snapshot_name = cmk.gui.watolib.snapshots.create_snapshot(self._comment)
            log_audit(None, "snapshot-created", _("Created snapshot %s") % snapshot_name)

            work_dir = os.path.join(self.activation_tmp_base_dir, self._activation_id)
            if cmk_version.is_managed_edition():
                import cmk.gui.cme.managed_snapshots as managed_snapshots  # pylint: disable=no-name-in-module
                managed_snapshots.CMESnapshotManager(
                    work_dir, self._get_site_configurations()).generate_snapshots()
            else:
                self._generate_snapshots(work_dir)

            logger.debug("Snapshot creation took %.4f", time.time() - start)
예제 #6
0
    def _process_parent_scan_results(
        self, task: ParentScanTask, settings: ParentScanSettings, gateways: List
    ) -> None:
        gateway = ParentScanResult(*gateways[0][0]) if gateways[0][0] else None
        state, skipped_gateways, error = gateways[0][1:]

        if state in ["direct", "root", "gateway"]:
            # The following code updates the host config. The progress from loading the WATO folder
            # until it has been saved needs to be locked.
            with store.lock_checkmk_configuration():
                self._configure_host_and_gateway(task, settings, gateway)
        else:
            self._logger.error(error)

        if gateway:
            self._num_gateways_found += 1

        if state in ["direct", "root"]:
            self._num_directly_reachable_hosts += 1

        self._num_unreachable_gateways += skipped_gateways

        if state == "notfound":
            self._num_no_gateway_found += 1

        if state in ["failed", "dnserror", "garbled"]:
            self._num_errors += 1
예제 #7
0
    def page(self):
        # To prevent mixups in written files we use the same lock here as for
        # the normal WATO page processing. This might not be needed for some
        # special automation requests, like inventory e.g., but to keep it simple,
        # we request the lock in all cases.
        with store.lock_checkmk_configuration():
            watolib.init_wato_datastructures(with_wato_lock=False)

            # TODO: Refactor these two calls to also use the automation_command_registry
            if self._command == "checkmk-automation":
                self._execute_cmk_automation()
                return

            elif self._command == "push-profile":
                self._execute_push_profile()
                return

            try:
                automation_command = watolib.automation_command_registry[
                    self._command]
            except KeyError:
                raise MKGeneralException(
                    _("Invalid automation command: %s.") % self._command)

            self._execute_automation_command(automation_command)
예제 #8
0
    def _do_housekeeping(self):
        """Cleanup stale activations in case it is needed"""
        with store.lock_checkmk_configuration():
            for activation_id in self._existing_activation_ids():
                # skip the current activation_id
                if self._activation_id == activation_id:
                    continue

                delete = False
                manager = ActivateChangesManager()
                manager.load()

                try:
                    try:
                        manager.load_activation(activation_id)
                    except RequestTimeout:
                        raise
                    except Exception:
                        # Not existant anymore!
                        delete = True
                        raise

                    delete = not manager.is_running()
                finally:
                    if delete:
                        shutil.rmtree(
                            "%s/%s" %
                            (ActivateChangesManager.activation_tmp_base_dir, activation_id))
예제 #9
0
 def _wrapper(param):
     if not self.skip_locking and self.method != "get":
         with store.lock_checkmk_configuration():
             response = func(param)
     else:
         response = func(param)
     return response
예제 #10
0
 def execute(self, request):
     # type: (PushSnapshotRequest) -> bool
     with store.lock_checkmk_configuration():
         return cmk.gui.watolib.activate_changes.apply_pre_17_sync_snapshot(
             request.site_id, request.tar_content,
             Path(cmk.utils.paths.omd_root),
             cmk.gui.watolib.activate_changes.get_replication_paths())
예제 #11
0
def _execute_action(
    api_call: APICallDefinitionDict, request_object: dict[str, Any]
) -> dict[str, Any]:
    if api_call.get("locking", True):
        with store.lock_checkmk_configuration():
            return _execute_action_no_lock(api_call, request_object)
    return _execute_action_no_lock(api_call, request_object)
예제 #12
0
def save_network_scan_result(folder: 'CREFolder', result: NetworkScanResult) -> None:
    # Reload the folder, lock WATO before to protect against concurrency problems.
    with store.lock_checkmk_configuration():
        # A user might have changed the folder somehow since starting the scan. Load the
        # folder again to get the current state.
        write_folder = watolib.Folder.folder(folder.path())
        write_folder.set_attribute("network_scan_result", result)
        write_folder.save()
예제 #13
0
 def page(self):
     # To prevent mixups in written files we use the same lock here as for
     # the normal WATO page processing. This might not be needed for some
     # special automation requests, like inventory e.g., but to keep it simple,
     # we request the lock in all cases.
     lock_config = not (self._command == "checkmk-automation"
                        and request.get_str_input_mandatory("automation")
                        == "active-check")
     with store.lock_checkmk_configuration(
     ) if lock_config else nullcontext():
         self._execute_automation()
예제 #14
0
 def _process_discovery_results(self, task, job_interface, counts, failed_hosts):
     # The following code updates the host config. The progress from loading the WATO folder
     # until it has been saved needs to be locked.
     with store.lock_checkmk_configuration():
         Folder.invalidate_caches()
         folder = Folder.folder(task.folder_path)
         for hostname in task.host_names:
             self._process_service_counts_for_host(counts[hostname])
             msg = self._process_discovery_result_for_host(folder.host(hostname),
                                                           failed_hosts.get(hostname, False),
                                                           counts[hostname])
             job_interface.send_progress_update("%s: %s" % (hostname, msg))
예제 #15
0
def init_wato_datastructures(with_wato_lock=False):
    if os.path.exists(ConfigDomainCACertificates.trusted_cas_file) and\
        not _need_to_create_sample_config():
        return

    def init():
        if not os.path.exists(ConfigDomainCACertificates.trusted_cas_file):
            ConfigDomainCACertificates().activate()
        _create_sample_config()

    if with_wato_lock:
        with store.lock_checkmk_configuration():
            init()
    else:
        init()
예제 #16
0
 def _process_discovery_results(
         self, task, job_interface,
         response: AutomationDiscoveryResponse) -> None:
     # The following code updates the host config. The progress from loading the WATO folder
     # until it has been saved needs to be locked.
     with store.lock_checkmk_configuration():
         Folder.invalidate_caches()
         folder = Folder.folder(task.folder_path)
         for count, hostname in enumerate(task.host_names,
                                          self._num_hosts_processed + 1):
             self._process_service_counts_for_host(
                 response.results[hostname])
             msg = self._process_discovery_result_for_host(
                 folder.host(hostname), response.results[hostname])
             job_interface.send_progress_update(
                 f"[{count}/{self._num_hosts_total}] {hostname}: {msg}")
예제 #17
0
    def _do_housekeeping(self):
        # type: () -> None
        """Cleanup stale activations in case it is needed"""
        with store.lock_checkmk_configuration():
            for activation_id in self._existing_activation_ids():
                self._logger.info("Check activation: %s", activation_id)
                delete = False
                manager = ActivateChangesManager()
                manager.load()

                # Try to detect whether or not the activation is still in progress. In case the
                # activation information can not be read, it is likely that the activation has
                # just finished while we were iterating the activations.
                # In case loading fails continue with the next activations
                try:
                    delete = True

                    try:
                        manager.load_activation(activation_id)
                        delete = not manager.is_running()
                    except MKUserError:
                        # "Unknown activation process", is normal after activation -> Delete, but no
                        # error message logging
                        self._logger.debug("Is not running")
                except Exception as e:
                    self._logger.warning(
                        "  Failed to load activation (%s), trying to delete...",
                        e,
                        exc_info=True)

                self._logger.info("  -> %s", "Delete" if delete else "Keep")
                if not delete:
                    continue

                activation_dir = os.path.join(
                    ActivateChangesManager.activation_tmp_base_dir,
                    activation_id)
                try:
                    shutil.rmtree(activation_dir)
                except Exception:
                    self._logger.error(
                        "  Failed to delete the activation directory '%s'" %
                        activation_dir,
                        exc_info=True)
예제 #18
0
파일: webapi.py 프로젝트: stmps/checkmk
def _execute_action(api_call, request_object):
    if api_call.get("locking", True):
        with store.lock_checkmk_configuration():
            return _execute_action_no_lock(api_call, request_object)
    return _execute_action_no_lock(api_call, request_object)
예제 #19
0
        def _validating_wrapper(param):
            # TODO: Better error messages, pointing to the location where variables are missing

            def _format_fields(_messages: Union[List, Dict]) -> str:
                if isinstance(_messages, list):
                    return ", ".join(_messages)
                if isinstance(_messages, dict):
                    return ", ".join(_messages.keys())
                return ""

            def _problem(exc_, status_code=400):
                if isinstance(exc_.messages, dict):
                    messages = exc_.messages
                else:
                    messages = {"exc": exc_.messages}
                return problem(
                    status=status_code,
                    title=http.client.responses[status_code],
                    detail=
                    f"These fields have problems: {_format_fields(exc_.messages)}",
                    ext={"fields": messages},
                )

            if self.method in ("post", "put") and request.get_data(cache=True):
                try:
                    self._is_expected_content_type(request.content_type)
                except ValueError as exc:
                    return problem(
                        status=415,
                        detail=str(exc),
                        title="Content type not valid for this endpoint.",
                    )

            try:
                if path_schema:
                    param.update(path_schema().load(param))
            except ValidationError as exc:
                return _problem(exc, status_code=404)

            try:
                if query_schema:
                    param.update(query_schema().load(
                        _from_multi_dict(request.args)))

                if header_schema:
                    param.update(header_schema().load(request.headers))

                if request_schema:
                    # Try to decode only when there is data. Decoding an empty string will fail.
                    if request.get_data(cache=True):
                        json_data = request.json or {}
                    else:
                        json_data = {}
                    param["body"] = request_schema().load(json_data)
            except ValidationError as exc:
                return _problem(exc, status_code=400)

            # make pylint happy
            assert callable(self.func)

            if self.tag_group == "Setup" and not config.wato_enabled:
                return problem(
                    status=403,
                    title="Forbidden: WATO is disabled",
                    detail="This endpoint is currently disabled via the "
                    "'Disable remote configuration' option in 'Distributed Monitoring'. "
                    "You may be able to query the central site.",
                )

            if not self.skip_locking and self.method != "get":
                with store.lock_checkmk_configuration():
                    response = self.func(param)
            else:
                response = self.func(param)

            response.freeze()

            if self.output_empty and response.status_code < 400 and response.data:
                return problem(
                    status=500,
                    title="Unexpected data was sent.",
                    detail=(f"Endpoint {self.operation_id}\n"
                            "This is a bug, please report."),
                    ext={"data_sent": str(response.data)},
                )

            if self.output_empty:
                response.content_type = None

            if response.status_code not in self._expected_status_codes:
                return problem(
                    status=500,
                    title=
                    f"Unexpected status code returned: {response.status_code}",
                    detail=(f"Endpoint {self.operation_id}\n"
                            "This is a bug, please report."),
                    ext={"codes": self._expected_status_codes},
                )

            # We assume something has been modified and increase the config generation ID
            # by one. This is necessary to ensure a warning in the "Activate Changes" GUI
            # about there being new changes to activate can be given to the user.
            if request.method != "get" and response.status_code < 300:
                update_config_generation()

                # We assume no configuration change on GET and no configuration change on
                # non-ok responses.
                if config.wato_use_git:
                    do_git_commit()

            # We assume something has been modified and increase the config generation ID
            # by one. This is necessary to ensure a warning in the "Activate Changes" GUI
            # about there being new changes to activate can be given to the user.
            if request.method != "get" and response.status_code < 300:
                update_config_generation()

                # We assume no configuration change on GET and no configuration change on
                # non-ok responses.
                if config.wato_use_git:
                    do_git_commit()

            # We assume something has been modified and increase the config generation ID
            # by one. This is necessary to ensure a warning in the "Activate Changes" GUI
            # about there being new changes to activate can be given to the user.
            if request.method != "get" and response.status_code < 300:
                update_config_generation()

                # We assume no configuration change on GET and no configuration change on
                # non-ok responses.
                if config.wato_use_git:
                    do_git_commit()

            if (self.content_type == "application/json"
                    and response.status_code < 300 and response_schema
                    and response.data):
                try:
                    data = json.loads(response.data.decode("utf-8"))
                except json.decoder.JSONDecodeError as exc:
                    return problem(
                        status=500,
                        title="Server was about to send invalid JSON data.",
                        detail="This is an error of the implementation.",
                        ext={
                            "errors": str(exc),
                            "orig": response.data,
                        },
                    )
                try:
                    outbound = response_schema().dump(data)
                except ValidationError as exc:
                    return problem(
                        status=500,
                        title="Server was about to send an invalid response.",
                        detail="This is an error of the implementation.",
                        ext={
                            "errors": exc.messages,
                            "orig": data,
                        },
                    )

                if self.convert_response:
                    response.set_data(json.dumps(outbound))

            response.freeze()
            return response
예제 #20
0
        def _validating_wrapper(param):
            # TODO: Better error messages, pointing to the location where variables are missing

            def _format_fields(_messages: Union[List, Dict]) -> str:
                if isinstance(_messages, list):
                    return ', '.join(_messages)
                if isinstance(_messages, dict):
                    return ', '.join(_messages.keys())
                return ''

            def _problem(exc_, status_code=400):
                if isinstance(exc_.messages, dict):
                    messages = exc_.messages
                else:
                    messages = {'exc': exc_.messages}
                return problem(
                    status=status_code,
                    title=http.client.responses[status_code],
                    detail=
                    f"These fields have problems: {_format_fields(exc_.messages)}",
                    ext={'fields': messages},
                )

            if (self.method in ("post", "put") and request.get_data(cache=True)
                    and request.content_type != self.content_type):
                return problem(
                    status=415,
                    title=
                    f"Content type {request.content_type!r} not supported on this endpoint.",
                )

            try:
                if path_schema:
                    param.update(path_schema().load(param))
            except ValidationError as exc:
                return _problem(exc, status_code=404)

            try:
                if query_schema:
                    param.update(query_schema().load(
                        _from_multi_dict(request.args)))

                if header_schema:
                    param.update(header_schema().load(request.headers))

                if request_schema:
                    # Try to decode only when there is data. Decoding an empty string will fail.
                    if request.get_data(cache=True):
                        json = request.json or {}
                    else:
                        json = {}
                    param['body'] = request_schema().load(json)
            except ValidationError as exc:
                return _problem(exc, status_code=400)

            # make pylint happy
            assert callable(self.func)
            # FIXME
            # We need to get the "original data" somewhere and are currently "piggy-backing"
            # it on the response instance. This is somewhat problematic because it's not
            # expected behaviour and not a valid interface of Response. Needs refactoring.

            if not self.skip_locking and self.method != 'get':
                with store.lock_checkmk_configuration():
                    response = self.func(param)
            else:
                response = self.func(param)

            response.freeze()

            if self.output_empty and response.status_code < 400 and response.data:
                return problem(status=500,
                               title="Unexpected data was sent.",
                               detail=(f"Endpoint {self.operation_id}\n"
                                       "This is a bug, please report."),
                               ext={'data_sent': str(response.data)})

            if self.output_empty:
                response.content_type = None

            if response.status_code not in self._expected_status_codes:
                return problem(
                    status=500,
                    title=
                    f"Unexpected status code returned: {response.status_code}",
                    detail=(f"Endpoint {self.operation_id}\n"
                            "This is a bug, please report."),
                    ext={'codes': self._expected_status_codes})

            # We assume something has been modified and increase the config generation ID
            # by one. This is necessary to ensure a warning in the "Activate Changes" GUI
            # about there being new changes to activate can be given to the user.
            if request.method != 'get' and response.status_code < 300:
                update_config_generation()

                # We assume no configuration change on GET and no configuration change on
                # non-ok responses.
                if config.wato_use_git:
                    do_git_commit()

            if hasattr(response, 'original_data') and response_schema:
                try:
                    response_schema().load(response.original_data)
                    return response
                except ValidationError as exc:
                    # Hope we never get here in production.
                    return problem(
                        status=500,
                        title="Server was about to send an invalid response.",
                        detail="This is an error of the implementation.",
                        ext={
                            'errors': exc.messages,
                            'orig': response.original_data
                        },
                    )

            return response