示例#1
0
    def _bulk_discovery_start(self, request):
        job = BulkDiscoveryBackgroundJob()
        if job.is_active():
            raise MKUserError(
                None,
                _("A bulk discovery job is already running. Please use the "
                  '"bulk_discovery_status" call to get the curent status.'),
            )

        discovery_mode, do_full_scan, bulk_size, ignore_errors = self._get_parameters_from_request(
            request)
        try:
            start_bulk_discovery(
                job,
                self._get_hosts_from_request(request),
                discovery_mode,
                do_full_scan,
                ignore_errors,
                bulk_size,
            )
        except Exception as e:
            logger.exception("Failed to start bulk discovery")
            raise MKUserError(None, _("Failed to start discovery: %s") % e)

        return {
            "started": True,
        }
示例#2
0
 def _bulk_discovery_status(self, request):
     job = BulkDiscoveryBackgroundJob()
     status = job.get_status()
     return {
         "is_active": job.is_active(),
         "job": {
             "state": status["state"],
             "result_msg": "\n".join(status["loginfo"]["JobResult"]),
             "output": "\n".join(status["loginfo"]["JobProgressUpdate"]),
         },
     }
示例#3
0
def show_bulk_discovery_status(params) -> Response:
    """Show the status of a bulk discovery job"""

    job_id = params["job_id"]
    job = BulkDiscoveryBackgroundJob()
    if job.get_job_id() != job_id:
        raise ProblemException(
            status=404,
            title=f"Background job {job_id} not found.",
        )
    return _serve_background_job(job)
示例#4
0
def execute_bulk_discovery(params) -> Response:
    """Start a bulk discovery job"""
    body = params["body"]
    job = BulkDiscoveryBackgroundJob()
    if job.is_active():
        return Response(status=409)

    hosts_to_discover = prepare_hosts_for_discovery(body["hostnames"])
    start_bulk_discovery(
        job,
        hosts_to_discover,
        body["mode"],
        body["do_full_scan"],
        body["ignore_errors"],
        body["bulk_size"],
    )

    return _serve_background_job(job)
示例#5
0
 def _bulk_discovery_status(self, request):
     job = BulkDiscoveryBackgroundJob()
     status_details = bulk_discovery_job_status(job)
     return {
         "is_active": status_details["is_active"],
         "job": {
             "state": status_details["job_state"],
             "result_msg": "\n".join(status_details["logs"]["result"]),
             "output": "\n".join(status_details["logs"]["progress"]),
         },
     }
示例#6
0
def _serve_background_job(job: BulkDiscoveryBackgroundJob) -> Response:
    job_id = job.get_job_id()
    status_details = bulk_discovery_job_status(job)
    return constructors.serve_json(
        constructors.domain_object(
            domain_type="discovery_run",
            identifier=job_id,
            title=
            f"Background job {job_id} {'is active' if status_details['is_active'] else 'is finished'}",
            extensions={
                "active": status_details["is_active"],
                "state": status_details["job_state"],
                "logs": status_details["logs"],
            },
            deletable=False,
            editable=False,
        ))
示例#7
0
    def _bulk_discovery_start(self, request):
        job = BulkDiscoveryBackgroundJob()
        if job.is_active():
            raise MKUserError(
                None,
                _("A bulk discovery job is already running. Please use the "
                  "\"bulk_discovery_status\" call to get the curent status."))

        mode, do_scan, bulk_size, error_handling = self._get_parameters_from_request(request)
        tasks = get_tasks(self._get_hosts_from_request(request), bulk_size)

        try:
            job.set_function(job.do_execute, mode, do_scan, error_handling, tasks)
            job.start()
            return {
                "started": True,
            }
        except Exception as e:
            logger.exception("Failed to start bulk discovery")
            raise MKUserError(None, _("Failed to start discovery: %s") % e)
示例#8
0
 def _from_vars(self):
     self._start = bool(request.var("_save"))
     self._all = bool(request.var("all"))
     self._just_started = False
     self._get_bulk_discovery_params()
     self._job = BulkDiscoveryBackgroundJob()
示例#9
0
class ModeBulkDiscovery(WatoMode):
    @classmethod
    def name(cls):
        return "bulkinventory"

    @classmethod
    def permissions(cls):
        return ["hosts", "services"]

    @classmethod
    def parent_mode(cls) -> Optional[Type[WatoMode]]:
        return ModeFolder

    def _from_vars(self):
        self._start = bool(request.var("_save"))
        self._all = bool(request.var("all"))
        self._just_started = False
        self._get_bulk_discovery_params()
        self._job = BulkDiscoveryBackgroundJob()

    def _get_bulk_discovery_params(self):
        self._bulk_discovery_params = copy.deepcopy(config.bulk_discovery_default_settings)

        if self._start:
            # Only do this when the start form has been submitted
            bulk_discover_params = vs_bulk_discovery().from_html_vars("bulkinventory")
            vs_bulk_discovery().validate_value(bulk_discover_params, "bulkinventory")
            self._bulk_discovery_params.update(bulk_discover_params)

        # The cast is needed for the moment, because mypy does not understand our data structure here
        (self._recurse, self._only_failed, self._only_failed_invcheck, self._only_ok_agent) = cast(
            Tuple[bool, bool, bool, bool], self._bulk_discovery_params["selection"]
        )

        self._do_scan, self._bulk_size = self._get_performance_params()
        self._mode = self._bulk_discovery_params["mode"]
        self._error_handling = self._bulk_discovery_params["error_handling"]

    def _get_performance_params(self) -> Tuple[bool, int]:
        performance_params = self._bulk_discovery_params["performance"]
        assert isinstance(performance_params, tuple)

        if len(performance_params) == 3:
            # In previous Checkmk versions (< 2.0) there was a third performance parameter:
            # 'use_cache' in the first place.
            do_scan, bulk_size = performance_params[1:]
        else:
            do_scan, bulk_size = performance_params

        assert isinstance(do_scan, bool)
        assert isinstance(bulk_size, int)
        return do_scan, bulk_size

    def title(self):
        return _("Bulk discovery")

    def page_menu(self, breadcrumb: Breadcrumb) -> PageMenu:
        return make_simple_form_page_menu(
            _("Discovery"),
            breadcrumb,
            form_name="bulkinventory",
            button_name="_save",
            save_title=_("Start"),
        )

    def action(self) -> ActionResult:
        user.need_permission("wato.services")

        tasks = get_tasks(self._get_hosts_to_discover(), self._bulk_size)

        try:
            transactions.check_transaction()
            self._job.set_function(
                self._job.do_execute, self._mode, self._do_scan, self._error_handling, tasks
            )
            self._job.start()
        except Exception as e:
            if config.debug:
                raise
            logger.exception("Failed to start bulk discovery")
            raise MKUserError(
                None, _("Failed to start discovery: %s") % ("%s" % e).replace("\n", "\n<br>")
            )

        raise HTTPRedirect(self._job.detail_url())

    def page(self):
        user.need_permission("wato.services")

        job_status_snapshot = self._job.get_status_snapshot()
        if job_status_snapshot.is_active():
            html.show_message(
                _('Bulk discovery currently running in <a href="%s">background</a>.')
                % self._job.detail_url()
            )
            return

        self._show_start_form()

    def _show_start_form(self):
        html.begin_form("bulkinventory", method="POST")

        msgs = []
        if self._all:
            vs = vs_bulk_discovery(render_form=True)
        else:
            # "Include subfolders" does not make sense for a selection of hosts
            # which is already given in the following situations:
            # - in the current folder below 'Selected hosts: Discovery'
            # - Below 'Bulk import' a automatic service discovery for
            #   imported/selected hosts can be executed
            vs = vs_bulk_discovery(render_form=True, include_subfolders=False)
            msgs.append(
                _("You have selected <b>%d</b> hosts for bulk discovery.")
                % len(self._get_hosts_to_discover())
            )
            # The cast is needed for the moment, because mypy does not understand our data structure here
            selection = cast(
                Tuple[bool, bool, bool, bool], self._bulk_discovery_params["selection"]
            )
            self._bulk_discovery_params["selection"] = [False] + list(selection[1:])

        msgs.append(
            _(
                "The Checkmk discovery will automatically find and configure services "
                "to be checked on your hosts and may also discover host labels."
            )
        )
        html.open_p()
        html.write_text(" ".join(msgs))
        vs.render_input("bulkinventory", self._bulk_discovery_params)
        forms.end()

        html.hidden_fields()
        html.end_form()

    def _get_hosts_to_discover(self) -> List[DiscoveryHost]:
        if self._only_failed_invcheck:
            restrict_to_hosts = self._find_hosts_with_failed_discovery_check()
        else:
            restrict_to_hosts = None

        if self._only_ok_agent:
            skip_hosts = self._find_hosts_with_failed_agent()
        else:
            skip_hosts = []

        # 'all' not set -> only inventorize checked hosts
        hosts_to_discover = []

        if not self._all:
            filterfunc = None
            if self._only_failed:
                filterfunc = lambda host: host.discovery_failed()

            for host_name in get_hostnames_from_checkboxes(filterfunc):
                if restrict_to_hosts and host_name not in restrict_to_hosts:
                    continue
                if host_name in skip_hosts:
                    continue
                host = Folder.current().load_host(host_name)
                host.need_permission("write")
                hosts_to_discover.append(
                    DiscoveryHost(host.site_id(), host.folder().path(), host_name)
                )

        else:
            # all host in this folder, maybe recursively. New: we always group
            # a bunch of subsequent hosts of the same folder into one item.
            # That saves automation calls and speeds up mass inventories.
            entries = self._recurse_hosts(Folder.current())
            for host_name, folder in entries:
                if restrict_to_hosts is not None and host_name not in restrict_to_hosts:
                    continue
                if host_name in skip_hosts:
                    continue
                host = folder.host(host_name)
                host.need_permission("write")
                hosts_to_discover.append(
                    DiscoveryHost(host.site_id(), host.folder().path(), host_name)
                )

        return hosts_to_discover

    def _recurse_hosts(self, folder):
        entries = []
        for host_name, host in folder.hosts().items():
            if not self._only_failed or host.discovery_failed():
                entries.append((host_name, folder))
        if self._recurse:
            for subfolder in folder.subfolders():
                entries += self._recurse_hosts(subfolder)
        return entries

    def _find_hosts_with_failed_discovery_check(self):
        # Old service name "Check_MK inventory" needs to be kept because old
        # installations may still use that name
        return sites.live().query_column(
            "GET services\n"
            "Filter: description = Check_MK inventory\n"
            "Filter: description = Check_MK Discovery\n"
            "Or: 2\n"
            "Filter: state > 0\n"
            "Columns: host_name"
        )

    def _find_hosts_with_failed_agent(self):
        return sites.live().query_column(
            "GET services\n"
            "Filter: description = Check_MK\n"
            "Filter: state >= 2\n"
            "Columns: host_name"
        )
示例#10
0
class ModeBulkDiscovery(WatoMode):
    @classmethod
    def name(cls):
        return "bulkinventory"

    @classmethod
    def permissions(cls):
        return ["hosts", "services"]

    def _from_vars(self):
        self._start = bool(html.request.var("_start"))
        self._all = bool(html.request.var("all"))
        self._just_started = False
        self._get_bulk_discovery_params()
        self._job = BulkDiscoveryBackgroundJob()

    def _get_bulk_discovery_params(self):
        self._bulk_discovery_params = copy.deepcopy(
            config.bulk_discovery_default_settings)

        if self._start:
            # Only do this when the start form has been submitted
            bulk_discover_params = vs_bulk_discovery().from_html_vars(
                "bulkinventory")
            vs_bulk_discovery().validate_value(bulk_discover_params,
                                               "bulkinventory")
            self._bulk_discovery_params.update(bulk_discover_params)

        self._recurse, self._only_failed, self._only_failed_invcheck, \
            self._only_ok_agent = self._bulk_discovery_params["selection"]
        self._use_cache, self._do_scan, self._bulk_size = \
            self._bulk_discovery_params["performance"]
        self._mode = self._bulk_discovery_params["mode"]
        self._error_handling = self._bulk_discovery_params["error_handling"]

    def title(self):
        return _("Bulk Service Discovery")

    def buttons(self):
        html.context_button(_("Folder"), Folder.current().url(), "back")

    def action(self):
        config.user.need_permission("wato.services")

        tasks = get_tasks(self._get_hosts_to_discover(), self._bulk_size)

        try:
            html.check_transaction()
            self._job.set_function(self._job.do_execute, self._mode,
                                   self._use_cache, self._do_scan,
                                   self._error_handling, tasks)
            self._job.start()
        except Exception as e:
            if config.debug:
                raise
            logger.exception("Failed to start bulk discovery")
            raise MKUserError(
                None,
                _("Failed to start discovery: %s") %
                ("%s" % e).replace("\n", "\n<br>"))

        raise HTTPRedirect(self._job.detail_url())

    def page(self):
        config.user.need_permission("wato.services")

        job_status_snapshot = self._job.get_status_snapshot()
        if job_status_snapshot.is_running():
            html.message(
                _("Bulk discovery currently running in <a href=\"%s\">background</a>."
                  ) % self._job.detail_url())
            return

        self._show_start_form()

    def _show_start_form(self):
        html.begin_form("bulkinventory", method="POST")

        msgs = []
        if self._all:
            vs = vs_bulk_discovery(render_form=True)
        else:
            # "Include subfolders" does not make sense for a selection of hosts
            # which is already given in the following situations:
            # - in the current folder below 'Selected hosts: Discovery'
            # - Below 'Bulk import' a automatic service discovery for
            #   imported/selected hosts can be executed
            vs = vs_bulk_discovery(render_form=True, include_subfolders=False)
            msgs.append(
                _("You have selected <b>%d</b> hosts for bulk discovery.") %
                len(self._get_hosts_to_discover()))
            selection = self._bulk_discovery_params["selection"]
            self._bulk_discovery_params["selection"] = [False] + list(
                selection[1:])

        msgs.append(
            _("Check_MK service discovery will automatically find and "
              "configure services to be checked on your hosts."))
        html.open_p()
        html.write_text(" ".join(msgs))
        vs.render_input("bulkinventory", self._bulk_discovery_params)
        forms.end()

        html.button("_start", _("Start"))
        html.hidden_fields()
        html.end_form()

    def _get_hosts_to_discover(self):
        # type: () -> List[DiscoveryHost]
        if self._only_failed_invcheck:
            restrict_to_hosts = self._find_hosts_with_failed_discovery_check()
        else:
            restrict_to_hosts = None

        if self._only_ok_agent:
            skip_hosts = self._find_hosts_with_failed_agent()
        else:
            skip_hosts = []

        # 'all' not set -> only inventorize checked hosts
        hosts_to_discover = []

        if not self._all:
            if self._only_failed:
                filterfunc = lambda host: host.discovery_failed()
            else:
                filterfunc = None

            for host_name in get_hostnames_from_checkboxes(filterfunc):
                if restrict_to_hosts and host_name not in restrict_to_hosts:
                    continue
                if host_name in skip_hosts:
                    continue
                host = Folder.current().host(host_name)
                host.need_permission("write")
                hosts_to_discover.append(
                    DiscoveryHost(host.site_id(), host.folder(), host_name))

        else:
            # all host in this folder, maybe recursively. New: we always group
            # a bunch of subsequent hosts of the same folder into one item.
            # That saves automation calls and speeds up mass inventories.
            entries = self._recurse_hosts(Folder.current())
            for host_name, folder in entries:
                if restrict_to_hosts is not None and host_name not in restrict_to_hosts:
                    continue
                if host_name in skip_hosts:
                    continue
                host = folder.host(host_name)
                host.need_permission("write")
                hosts_to_discover.append(
                    DiscoveryHost(host.site_id(), host.folder(), host_name))

        return hosts_to_discover

    def _recurse_hosts(self, folder):
        entries = []
        for host_name, host in folder.hosts().items():
            if not self._only_failed or host.discovery_failed():
                entries.append((host_name, folder))
        if self._recurse:
            for subfolder in folder.all_subfolders().values():
                entries += self._recurse_hosts(subfolder)
        return entries

    def _find_hosts_with_failed_discovery_check(self):
        # Old service name "Check_MK inventory" needs to be kept because old
        # installations may still use that name
        return sites.live().query_column(
            "GET services\n"
            "Filter: description = Check_MK inventory\n"
            "Filter: description = Check_MK Discovery\n"
            "Or: 2\n"
            "Filter: state > 0\n"
            "Columns: host_name")

    def _find_hosts_with_failed_agent(self):
        return sites.live().query_column("GET services\n"
                                         "Filter: description = Check_MK\n"
                                         "Filter: state >= 2\n"
                                         "Columns: host_name")