Exemplo n.º 1
0
def get_only_sites_from_context(context: VisualContext) -> Optional[List[SiteId]]:
    """Gather possible existing "only sites" information from context

    We need to deal with all possible site filters (sites, site and siteopt).

    VisualContext is structured like this:

    {"site": {"site": "sitename"}}
    {"siteopt": {"site": "sitename"}}
    {"sites": {"sites": "sitename|second"}}

    The difference is no fault or "old" data structure. We can have both kind of structures.
    These are the data structure the visuals work with.

    "site" and "sites" are conflicting filters. The new optional filter
    "sites" for many sites filter is only used if the view is configured
    to only this filter.
    """

    if "sites" in context and "site" not in context:
        only_sites = context["sites"]["sites"]
        only_sites_list = [SiteId(site) for site in only_sites.strip().split("|") if site]
        return only_sites_list if only_sites_list else None

    for var in ["site", "siteopt"]:
        if site_name := context.get(var, {}).get("site"):
            return [SiteId(site_name)]
def test_rewrite_host_explicit_site():
    _write_hosts_mk("""# Created by WATO
# encoding: utf-8

all_hosts += ['ag']

host_tags.update({'ag': {'site': 'stable', 'address_family': 'ip-v4-only', 'ip-v4': 'ip-v4', 'agent': 'cmk-agent', 'tcp': 'tcp', 'piggyback': 'auto-piggyback', 'snmp_ds': 'no-snmp', 'criticality': 'prod', 'networking': 'lan'}})

host_labels.update({})

# Explicit IPv4 addresses
ipaddresses.update({'ag': '127.0.0.1'})

# Host attributes (needed for WATO)
host_attributes.update(
{'ag': {'ipaddress': '127.0.0.1', 'site': 'stable', 'meta_data': {'created_at': 1627486290.0, 'created_by': 'cmkadmin', 'updated_at': 1627993165.0079741}}})
""")

    assert Folder.root_folder().host("ag").attribute("site") == "stable"
    update_hosts_and_folders(SiteId("stable"), SiteId("dingdong"))
    assert Folder.root_folder().host("ag").attribute("site") == "dingdong"

    # also verify that the attributes (host_tags) not read by WATO have been updated
    hosts_config = Folder.root_folder()._load_hosts_file()
    assert hosts_config is not None
    assert hosts_config["host_tags"]["ag"]["site"] == "dingdong"
Exemplo n.º 3
0
def test_update_basic_site_config():
    _write_site_config({
        "heute": {
            "alias": "Die central Site",
            "disable_wato": True,
            "disabled": False,
            "insecure": False,
            "multisiteurl": "",
            "persist": False,
            "replicate_ec": False,
            "replication": None,
            "timeout": 10,
            "user_login": True,
            "url_prefix": "/heute/",
            "proxy": None,
            "socket": ("local", None),
        },
    })

    update_site_config(SiteId("heute"), SiteId("haha"))

    site_mgmt = SiteManagementFactory().factory()
    all_sites = site_mgmt.load_sites()

    # Site entry has been renamed
    assert "heute" not in all_sites
    assert "haha" in all_sites

    # url_prefix is updated
    assert all_sites["haha"]["url_prefix"] == "/haha/"
def test_rewrite_tags_no_explicit_site_set(monkeypatch):
    _write_folder_attributes({
        "title": "Main",
        "attributes": {
            "meta_data": {
                "created_at": 1627991988.6232662,
                "updated_at": 1627991994.7575116,
                "created_by": None,
            }
        },
        "num_hosts": 0,
        "lock": False,
        "lock_subfolders": False,
        "__id": "9f5a85386b7c4ad68738d66a49a4bfa9",
    })

    _write_hosts_mk("""# Created by WATO
# encoding: utf-8

all_hosts += ['ag']

host_tags.update({'ag': {'site': 'NO_SITE', 'address_family': 'ip-v4-only', 'ip-v4': 'ip-v4', 'agent': 'cmk-agent', 'tcp': 'tcp', 'piggyback': 'auto-piggyback', 'snmp_ds': 'no-snmp', 'criticality': 'prod', 'networking': 'lan'}})

host_labels.update({})

# Explicit IPv4 addresses
ipaddresses.update({'ag': '127.0.0.1'})

# Host attributes (needed for WATO)
host_attributes.update(
{'ag': {'ipaddress': '127.0.0.1', 'meta_data': {'created_at': 1627486290.0, 'created_by': 'cmkadmin', 'updated_at': 1627993165.0079741}}})
""")

    assert Folder.root_folder().attribute("site") is None
    assert Folder.root_folder().load_host("ag").attribute("site") is None
    assert Folder.root_folder().load_host("ag").site_id() == "NO_SITE"

    # Simulate changed omd_site that we would have in application code in the moment the rename
    # action is executed.
    monkeypatch.setattr(cmk.gui.watolib.hosts_and_folders, "omd_site",
                        lambda: "dingdong")
    monkeypatch.setattr(HostAttributeSite, "default_value",
                        lambda self: "dingdong")

    update_hosts_and_folders(SiteId("NO_SITE"), SiteId("dingdong"))

    Folder.invalidate_caches()

    assert Folder.root_folder().attribute("site") is None
    assert Folder.root_folder().load_host("ag").attribute("site") is None
    assert Folder.root_folder().load_host("ag").site_id() == "dingdong"
    assert Folder.root_folder().load_host(
        "ag").tag_groups()["site"] == "dingdong"

    # also verify that the attributes (host_tags) not read by WATO have been updated
    hosts_config = Folder.root_folder()._load_hosts_file()
    assert hosts_config is not None
    assert hosts_config["host_tags"]["ag"]["site"] == "dingdong"
Exemplo n.º 5
0
def test_run_executes_plugins(capsys, test_registry, mocker):
    handler_mock = mocker.MagicMock()
    test_registry.register(
        RenameAction(name="test", title="Test Title", sort_index=0, handler=handler_mock)
    )

    assert main.main(["-v", "old"]) == 0

    output = capsys.readouterr()
    assert output.err == ""
    assert "1/1 Test Title..." in output.out
    assert output.out.endswith("Done\n")

    assert handler_mock.called_once_with(SiteId("old"), SiteId("NO_SITE"))
Exemplo n.º 6
0
def test_disable_activate_changes_writer(mocker: MockerFixture) -> None:
    add_to_site_mock = mocker.patch.object(ActivateChangesWriter,
                                           "_add_change_to_site")

    add_change("ding", "dong", sites=[SiteId("a")])
    add_to_site_mock.assert_called_once()
    add_to_site_mock.reset_mock()

    with ActivateChangesWriter.disable():
        add_change("ding", "dong", sites=[SiteId("a")])
    add_to_site_mock.assert_not_called()
    add_to_site_mock.reset_mock()

    add_change("ding", "dong", sites=[SiteId("a")])
    add_to_site_mock.assert_called_once()
Exemplo n.º 7
0
    def generate_response_data(cls, properties, context, settings):
        if config.is_single_local_site():
            site_id: Optional[SiteId] = config.omd_site()
        else:
            site_filter = context.get("site", {}).get("site")
            site_id = SiteId(site_filter) if site_filter else None

        render_mode = "hosts" if site_id else "sites"

        if render_mode == "hosts":
            assert site_id is not None
            elements = cls._collect_hosts_data(site_id)
            default_title = _("Host overview")
        elif render_mode == "sites":
            elements = cls._collect_sites_data()
            default_title = _("Site overview")
        else:
            raise NotImplementedError()

        return {
            # TODO: This should all be done inside the dashlet class once it is instantiated by the
            #  ajax call
            "title":
            render_title_with_macros_string(
                context,
                settings["single_infos"],
                settings.get("title", default_title),
                default_title,
            ),
            "render_mode":
            render_mode,
            "plot_definitions": [],
            "data": [e.serialize() for e in elements],
        }
Exemplo n.º 8
0
def macro_mapping_from_context(
    context: VisualContext,
    single_infos: SingleInfos,
    title: str,
    default_title: str,
    **additional_macros: str,
) -> MacroMapping:
    macro_mapping = {"$DEFAULT_TITLE$": default_title}
    macro_mapping.update({
        macro: context[key][key]
        for macro, key in (
            ("$HOST_NAME$", "host"),
            ("$SERVICE_DESCRIPTION$", "service"),
        ) if key in context and key in context[key] and key in single_infos
    })

    if "$HOST_ALIAS$" in title and "$HOST_NAME$" in macro_mapping:
        macro_mapping["$HOST_ALIAS$"] = get_alias_of_host(
            SiteId(additional_macros.get("$SITE$", "")),
            macro_mapping["$HOST_NAME$"],
        )

    macro_mapping.update(additional_macros)

    return macro_mapping
Exemplo n.º 9
0
def _query_site(connection, host_name: str) -> SiteId:
    with detailed_connection(connection) as conn:
        site_id = Query([Hosts.name],
                        Hosts.name.equals(host_name)).first_value(conn)
        if not isinstance(site_id, str):
            raise QueryException
    return SiteId(site_id)
Exemplo n.º 10
0
 def test_get_request(
     self,
     monkeypatch: pytest.MonkeyPatch,
 ) -> None:
     request = Request({})
     request.set_var("site_id", "NO_SITE")
     request.set_var("to_delete", "['x/y/z.txt', 'abc.ending', '/ä/☃/☕']")
     request.set_var("config_generation", "123")
     request.files = werkzeug_datastructures.ImmutableMultiDict({
         "sync_archive":
         werkzeug_datastructures.FileStorage(
             stream=io.BytesIO(b"some data"),
             filename="sync_archive",
             name="sync_archive",
         )
     })
     monkeypatch.setattr(
         activate_changes,
         "_request",
         request,
     )
     assert (activate_changes.AutomationReceiveConfigSync().get_request() ==
             activate_changes.ReceiveConfigSyncRequest(
                 site_id=SiteId("NO_SITE"),
                 sync_archive=b"some data",
                 to_delete=["x/y/z.txt", "abc.ending", "/ä/☃/☕"],
                 config_generation=123,
             ))
Exemplo n.º 11
0
def main(args: List[str]) -> int:
    arguments = parse_arguments(args)
    setup_logging(arguments)

    if arguments.debug:
        cmk.utils.debug.enable()
    logger.debug("parsed arguments: %s", arguments)

    new_site_id = SiteId(cmk.utils.site.omd_site())
    if arguments.old_site_id == new_site_id:
        logger.info(
            "OLD_SITE_ID is equal to current OMD_SITE - Nothing to do.")
        return 0

    load_plugins()

    try:
        has_errors = run(arguments, arguments.old_site_id, new_site_id)
    except Exception:
        if arguments.debug:
            raise
        logger.exception(
            'ERROR: Please repair this and run "cmk-post-rename-site -v" '
            "BEFORE starting the site again.")
        return 1
    return 1 if has_errors else 0
Exemplo n.º 12
0
def omd_site() -> SiteId:
    try:
        return SiteId(os.environ["OMD_SITE"])
    except KeyError:
        raise MKGeneralException(
            _("OMD_SITE environment variable not set. You can "
              "only execute this in an OMD site."))
Exemplo n.º 13
0
    def generate_response_data(cls, properties, context, settings):
        site_id = context.get("site", {}).get("site")

        time_range, range_title = Timerange().compute_range(
            properties["time_range"])
        default_title = _("Top alerters - %s") % range_title

        elements = cls._collect_data(
            only_sites=[SiteId(site_id)] if site_id else None,
            since=time_range[0],
            limit=properties.get("limit_objects"),
        )

        return {
            "render_mode":
            "alert_statistics",
            # TODO: This should all be done inside the dashlet class once it is instantiated by the
            #  ajax call
            "title":
            render_title_with_macros_string(
                context,
                settings["single_infos"],
                settings.get("title", default_title),
                default_title,
            ),
            "title_url":
            settings.get("title_url"),
            "plot_definitions": [],
            "data": [e.serialize() for e in elements],
            "upper_bound":
            max([100] + [e.num_problems + 1 for e in elements]),
        }
Exemplo n.º 14
0
    def generate_response_data(cls, properties, context, settings):
        if config.is_single_local_site():
            site_id: Optional[SiteId] = config.omd_site()
        else:
            site_filter = context.get("site", {}).get("site")
            site_id = SiteId(site_filter) if site_filter else None

        render_mode = "hosts" if site_id else "sites"

        if render_mode == "hosts":
            assert site_id is not None
            elements = cls._collect_hosts_data(site_id)
        elif render_mode == "sites":
            elements = cls._collect_sites_data()
        else:
            raise NotImplementedError()

        return {
            # TODO: Get the correct dashlet title. This needs to use the general dashlet title
            # calculation. We somehow have to get the title from
            # cmk.gui.dashboard._render_dashlet_title.
            "title": _("Site overview"),
            "render_mode": render_mode,
            "plot_definitions": [],
            "data": [e.serialize() for e in elements],
        }
Exemplo n.º 15
0
 def _get_crash_report_row(self, crash_id: str,
                           site_id: str) -> Optional[Dict[str, str]]:
     rows = CrashReportsRowTable().get_crash_report_rows(
         only_sites=[SiteId(site_id)],
         filter_headers="Filter: id = %s" % livestatus.lqencode(crash_id))
     if not rows:
         return None
     return rows[0]
Exemplo n.º 16
0
def test_key_mgmt_create_key(monkeypatch: pytest.MonkeyPatch, tmp_path: Path) -> None:
    monkeypatch.setattr(time, "time", lambda: 123)

    key = key_mgmt.generate_key("älias", "passphra$e", UserId("dingdöng"), SiteId("test-site"))
    assert isinstance(key, key_mgmt.Key)
    assert key.alias == "älias"
    assert key.date == 123
    assert key.owner == "dingdöng"
    assert key.certificate.startswith("-----BEGIN CERTIFICATE---")
    assert key.private_key.startswith("-----BEGIN ENCRYPTED PRIVATE KEY---")
Exemplo n.º 17
0
    def get_request(self) -> PushSnapshotRequest:
        site_id = SiteId(request.get_ascii_input_mandatory("siteid"))
        cmk.gui.watolib.activate_changes.verify_remote_site_config(site_id)

        snapshot = request.uploaded_file("snapshot")
        if not snapshot:
            raise MKGeneralException(
                _("Invalid call: The snapshot is missing."))

        return PushSnapshotRequest(site_id=site_id, tar_content=snapshot[2])
Exemplo n.º 18
0
    def _get_alert_stats(cls, only_sites: OnlySites,
                         since: int) -> Dict[Tuple[SiteId, HostName, str], AlertStats]:
        try:
            sites.live().set_only_sites(only_sites)
            sites.live().set_prepend_site(True)
            rows: LivestatusResponse = sites.live().query(cls._alert_stats_query(since))
        finally:
            sites.live().set_prepend_site(False)
            sites.live().set_only_sites(None)

        return {(SiteId(row[0]), HostName(row[1]), row[2]): AlertStats(*row[3:]) for row in rows}
Exemplo n.º 19
0
    def _get_hostnames_from_filters(
        self, context: VisualContext, filters: List[Filter]
    ) -> Set[HostName]:
        filter_headers = "".join(get_livestatus_filter_headers(context, filters))

        query = "GET hosts\nColumns: name"
        if filter_headers:
            query += "\n%s" % filter_headers

        site = request.var("site")
        with sites.only_sites(None if site is None else SiteId(site)):
            return {HostName(x) for x in sites.live().query_column_unique(query)}
Exemplo n.º 20
0
    def page(self):
        check_csrf_token()
        user.need_permission("wato.activate")

        api_request = self.webapi_request()
        # ? type of activate_until is unclear
        activate_until = api_request.get("activate_until")
        if not activate_until:
            raise MKUserError("activate_until",
                              _('Missing parameter "%s".') % "activate_until")

        manager = activate_changes.ActivateChangesManager()
        manager.load()
        # ? type of api_request is unclear
        affected_sites_request = ensure_str(  # pylint: disable= six-ensure-str-bin-call
            api_request.get("sites", "").strip())
        if not affected_sites_request:
            affected_sites = manager.dirty_and_active_activation_sites()
        else:
            affected_sites = [
                SiteId(s) for s in affected_sites_request.split(",")
            ]

        comment: Optional[str] = api_request.get("comment", "").strip()

        activate_foreign = api_request.get("activate_foreign", "0") == "1"

        valuespec = _vs_activation("", manager.has_foreign_changes())
        if valuespec:
            valuespec.validate_value(
                {
                    "comment": comment,
                    "foreign": activate_foreign,
                },
                "activate",
            )

        if comment == "":
            comment = None

        activation_id = manager.start(
            sites=affected_sites,
            activate_until=ensure_str(activate_until),  # pylint: disable= six-ensure-str-bin-call
            comment=comment,
            activate_foreign=activate_foreign,
        )

        return {
            "activation_id": activation_id,
        }
Exemplo n.º 21
0
    def page(self) -> None:
        if not user.may("wato.diagnostics"):
            raise MKAuthException(
                _("Sorry, you lack the permission for downloading diagnostics dumps."
                  ))

        site = SiteId(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)
Exemplo n.º 22
0
    def _verify_cache_integrity(self) -> IntegrityCheckResponse:
        """Verify last program start value in redis with current value"""
        last_program_starts = self._redis_get_last_program_starts()

        if not last_program_starts:
            all_sites = self._get_site_ids()
            self._sites_to_update.update(all_sites)
            return IntegrityCheckResponse.UPDATE

        for site_id, last_program_start in last_program_starts.items():

            # How can last_program_start be None if it is a Dict[str, str]?
            # Perhaps _redis_get_last_program_starts() should return something like an Optional[Mapping[SiteId, Optional[int]].
            if last_program_start is None or (
                    int(last_program_start) !=
                    self._livestatus_get_last_program_start(SiteId(site_id))):

                self._sites_to_update.update([SiteId(site_id)])

        if self._sites_to_update:
            return IntegrityCheckResponse.UPDATE

        return IntegrityCheckResponse.USE
def test_rewrite_folder_explicit_site():
    _write_folder_attributes({
        "title": "Main directory",
        "attributes": {
            "site": "stable",
            "meta_data": {
                "created_at": 1627991988.6232662,
                "updated_at": 1627991994.7575116,
                "created_by": None,
            },
        },
        "num_hosts": 0,
        "lock": False,
        "lock_subfolders": False,
        "__id": "9f5a85386b7c4ad68738d66a49a4bfa9",
    })

    folder = Folder.root_folder()
    folder.load_instance()
    assert folder.attribute("site") == "stable"

    update_hosts_and_folders(SiteId("stable"), SiteId("dingdong"))
    assert folder.attribute("site") == "dingdong"
Exemplo n.º 24
0
    def _get_default_view_hostnames(self, max_nodes: int) -> Set[HostName]:
        """Returns all hosts without any parents"""
        query = "GET hosts\nColumns: name\nFilter: parents ="
        site = request.var("site")
        with sites.prepend_site(), sites.only_sites(None if site is None else SiteId(site)):
            hosts = [(x[0], x[1]) for x in sites.live().query(query)]

        # If no explicit site is set and the number of initially displayed hosts
        # exceeds the auto growth range, only the hosts of the master site are shown
        if len(hosts) > max_nodes:
            hostnames = {HostName(x[1]) for x in hosts if x[0] == omd_site()}
        else:
            hostnames = {HostName(x[1]) for x in hosts}

        return hostnames
Exemplo n.º 25
0
    def get_crash_report_rows(self, only_sites: Optional[List[SiteId]],
                              filter_headers: str) -> List[Dict[str, str]]:

        # First fetch the information that is needed to query for the dynamic columns (crash_info,
        # ...)
        crash_infos = self._get_crash_report_info(only_sites, filter_headers)
        if not crash_infos:
            return []

        rows = []
        for crash_info in crash_infos:
            file_path = "/".join(
                [crash_info["crash_type"], crash_info["crash_id"]])

            headers = ["crash_info"]
            columns = [
                "file:crash_info:%s/crash.info" %
                livestatus.lqencode(file_path)
            ]

            if crash_info["crash_type"] == "check":
                headers += ["agent_output", "snmp_info"]
                columns += [
                    "file:agent_output:%s/agent_output" %
                    livestatus.lqencode(file_path),
                    "file:snmp_info:%s/snmp_info" %
                    livestatus.lqencode(file_path),
                ]

            try:
                sites.live().set_prepend_site(False)
                sites.live().set_only_sites(
                    [SiteId(ensure_str(crash_info["site"]))])

                raw_row = sites.live().query_row(
                    "GET crashreports\n"
                    "Columns: %s\n"
                    "Filter: id = %s" %
                    (" ".join(columns),
                     livestatus.lqencode(crash_info["crash_id"])))
            finally:
                sites.live().set_only_sites(None)
                sites.live().set_prepend_site(False)

            crash_info.update(dict(zip(headers, raw_row)))
            rows.append(crash_info)

        return rows
Exemplo n.º 26
0
def ajax_set_snapin_site():
    response.set_content_type("application/json")
    ident = request.var("ident")
    if ident not in snapin_registry:
        raise MKUserError(None, _("Invalid ident"))

    site = request.var("site")
    site_choices = dict([(SiteId(""), _("All sites"))] +
                        user_sites.get_configured_site_choices())

    if site not in site_choices:
        raise MKUserError(None, _("Invalid site"))

    snapin_sites = user.load_file("sidebar_sites", {}, lock=True)
    snapin_sites[ident] = site
    user.save_file("sidebar_sites", snapin_sites)
Exemplo n.º 27
0
    def _get_site_data_files(self) -> List[Tuple[Path, SiteProgramStart]]:
        data_files = []
        for path_object in self._path_site_structure_data.iterdir():
            if path_object.is_dir():
                continue

            name = path_object.name
            if not name.startswith(self._site_cache_prefix):
                continue

            try:
                _prefix, site_id, timestamp = name.split(".", 2)
            except ValueError:
                path_object.unlink(missing_ok=True)
                continue

            data_files.append((path_object, (SiteId(site_id), int(timestamp))))
        return data_files
Exemplo n.º 28
0
    def get_request(self):
        site_id = SiteId(request.get_ascii_input_mandatory("site_id"))
        activate_changes.verify_remote_site_config(site_id)

        try:
            serialized_domain_requests = ast.literal_eval(
                request.get_ascii_input_mandatory("domains")
            )
            if serialized_domain_requests and isinstance(serialized_domain_requests[0], str):
                serialized_domain_requests = [
                    asdict(DomainRequest(x)) for x in serialized_domain_requests
                ]
        except SyntaxError:
            raise watolib.MKAutomationException(
                _("Invalid request: %r") % request.get_ascii_input_mandatory("domains")
            )

        return ActivateChangesRequest(site_id=site_id, domains=serialized_domain_requests)
Exemplo n.º 29
0
def _create_tasks_from_hosts(hosts_to_discover: List[DiscoveryHost],
                             bulk_size: BulkSize) -> List[DiscoveryTask]:
    """Create a list of tasks for the job

    Each task groups the hosts together that are in the same folder and site. This is
    mainly done to reduce the overhead of site communication and loading/saving of files
    """
    current_site_and_folder = None
    tasks: List[DiscoveryTask] = []

    for site_id, folder_path, host_name in sorted(hosts_to_discover):
        if (not tasks or (site_id, folder_path) != current_site_and_folder
                or len(tasks[-1].host_names) >= bulk_size):
            tasks.append(
                DiscoveryTask(SiteId(site_id), folder_path, [host_name]))
        else:
            tasks[-1].host_names.append(host_name)
        current_site_and_folder = site_id, folder_path
    return tasks
Exemplo n.º 30
0
def get_alias_of_host(site_id: Optional[SiteId], host_name: str) -> SiteId:
    query = ("GET hosts\n"
             "Cache: reload\n"
             "Columns: alias\n"
             "Filter: name = %s" % lqencode(host_name))

    with only_sites(site_id):
        try:
            return live().query_value(query)
        except Exception as e:
            logger.warning(
                "Could not determine alias of host %s on site %s: %s",
                host_name,
                site_id,
                e,
            )
            if config.debug:
                raise
            return SiteId(host_name)