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()
def _init_new_host_object(cls): return Host( folder=Folder.current(), host_name=request.var("host"), attributes={}, cluster_nodes=[], )
def bulk_delete(params): """Bulk delete hosts""" user.need_permission("wato.edit") body = params["body"] for host_name in body["entries"]: host = Host.load_host(host_name) host.folder().delete_hosts([host.name()], automation=delete_hosts) return Response(status=204)
def is_ineffective(self): hosts = Host.all() for host_name, host in hosts.items(): if self.matches_host_and_item(host.folder(), host_name, service_description=None): return False return True
def delete(params): """Delete a host""" user.need_permission("wato.edit") host_name = params["host_name"] # Parameters can't be validated through marshmallow yet. check_hostname(host_name, should_exist=True) host: CREHost = Host.load_host(host_name) host.folder().delete_hosts([host.name()], automation=delete_hosts) return Response(status=204)
def show_host(params) -> Response: """Show a host""" host = Host.load_host(params["host_name"]) return constructors.serve_json( { "site": host.site_id(), "is_cluster": host.is_cluster(), } )
def _get_cluster_nodes(self): if not self._is_cluster(): return None cluster_nodes = self._vs_cluster_nodes().from_html_vars("nodes") self._vs_cluster_nodes().validate_value(cluster_nodes, "nodes") if len(cluster_nodes) < 1: raise MKUserError("nodes_0", _("The cluster must have at least one node")) # Fake a cluster host in order to get calculated tag groups via effective attributes... cluster_computed_datasources = cmk.utils.tags.compute_datasources( Host( Folder.current(), self._host.name(), collect_attributes("cluster", new=False), [], ).tag_groups()) for nr, cluster_node in enumerate(cluster_nodes): if cluster_node == self._host.name(): raise MKUserError( "nodes_%d" % nr, _("The cluster can not be a node of it's own")) if not Host.host_exists(cluster_node): raise MKUserError( "nodes_%d" % nr, _("The node <b>%s</b> does not exist " " (must be a host that is configured with WATO)") % cluster_node, ) node_computed_datasources = cmk.utils.tags.compute_datasources( Host.load_host(cluster_node).tag_groups()) if datasource_differences := cluster_computed_datasources.get_differences_to( node_computed_datasources): raise MKUserError( "nodes_%d" % nr, _("Cluster and nodes must have the same datasource. ") + self._format_datasource_differences( cluster_node, datasource_differences), )
def is_ineffective(self): """Whether or not this rule does not match at all Interesting: This has always tried host matching. Whether or not a service ruleset does not match any service has never been tested. Probably because this would be too expensive.""" hosts = Host.all() for host_name, host in hosts.items(): if self.matches_host_conditions(host.folder(), host_name): return False return True
def _validate(self, value): super()._validate(value) # Regex gets checked through the `pattern` of the String instance if self._should_exist is not None: host = Host.host(value) if self._should_exist and not host: raise self.make_error("should_exist", host_name=value) if not self._should_exist and host: raise self.make_error("should_not_exist", host_name=value) if self._should_be_cluster is not None and ( host := Host.host(value)) is not None: if self._should_be_cluster and not host.is_cluster(): raise self.make_error("should_be_cluster", host_name=value) if not self._should_be_cluster and host.is_cluster(): raise self.make_error("should_not_be_cluster", host_name=value)
def _get_hosts_from_request(self, request: Dict) -> List[DiscoveryHost]: if not request["hostnames"]: raise MKUserError(None, _("You have to specify some hosts")) hosts_to_discover = [] for host_name in request["hostnames"]: host = Host.host(host_name) if host is None: raise MKUserError(None, _("The host '%s' does not exist") % host_name) host.need_permission("write") hosts_to_discover.append(DiscoveryHost(host.site_id(), host.folder().path(), host_name)) return hosts_to_discover
def _get_object_reference( object_ref: Optional[ObjectRef] ) -> Tuple[Optional[str], Optional[str]]: if object_ref is None: return None, None if object_ref.object_type is ObjectRefType.Host: host = Host.host(object_ref.ident) if host: return host.edit_url(), host.name() return None, object_ref.ident if object_ref.object_type is ObjectRefType.Folder: if Folder.folder_exists(object_ref.ident): folder = Folder.folder(object_ref.ident) return folder.url(), folder.title() return None, object_ref.ident if object_ref.object_type is ObjectRefType.User: url = makeuri_contextless( request, [ ("mode", "edit_user"), ("edit", object_ref.ident), ], filename="wato.py", ) return url, object_ref.ident if object_ref.object_type is ObjectRefType.Rule: url = makeuri_contextless( request, [ ("mode", "edit_rule"), ("varname", object_ref.labels["ruleset"]), ("rule_id", object_ref.ident), ], filename="wato.py", ) return url, object_ref.ident if object_ref.object_type is ObjectRefType.Ruleset: url = makeuri_contextless( request, [ ("mode", "edit_ruleset"), ("varname", object_ref.ident), ], filename="wato.py", ) return url, object_ref.ident return None, object_ref.ident
def create_host(params): """Create a host""" user.need_permission("wato.edit") body = params["body"] host_name = body["host_name"] folder: CREFolder = body["folder"] # is_cluster is defined as "cluster_hosts is not None" folder.create_hosts([(host_name, body["attributes"], None)], bake_hosts=params[BAKE_AGENT_PARAM_NAME]) host = Host.load_host(host_name) return _serve_host(host, False)
def prepare_hosts_for_discovery( hostnames: Sequence[str]) -> List[DiscoveryHost]: hosts_to_discover = [] for host_name in hostnames: host = Host.host(host_name) if host is None: raise MKUserError(None, _("The host '%s' does not exist") % host_name) host.need_permission("write") hosts_to_discover.append( DiscoveryHost(host.site_id(), host.folder().path(), host_name)) return hosts_to_discover
def _known_ip_addresses(): addresses = set() for host in Host.all().values(): attributes = host.attributes() address = attributes.get("ipaddress") if address: addresses.add(address) addresses.update(attributes.get("additional_ipv4addresses", [])) return addresses
def action(self) -> ActionResult: folder = Folder.current() if not transactions.check_transaction(): return redirect(mode_url("folder", folder=folder.path())) if request.var("_update_dns_cache") and self._should_use_dns_cache(): user.need_permission("wato.update_dns_cache") update_dns_cache_result = update_dns_cache(self._host.site_id()) infotext = (_("Successfully updated IP addresses of %d hosts.") % update_dns_cache_result.n_updated) if update_dns_cache_result.failed_hosts: infotext += "<br><br><b>Hostnames failed to lookup:</b> " + ", ".join( [ "<tt>%s</tt>" % h for h in update_dns_cache_result.failed_hosts ]) flash(infotext) return None if request.var("delete"): # Delete this host folder.delete_hosts([self._host.name()], automation=delete_hosts) return redirect(mode_url("folder", folder=folder.path())) if request.var("_remove_tls_registration"): remove_tls_registration( {self._host.site_id(): [self._host.name()]}) return None attributes = collect_attributes( "host" if not self._is_cluster() else "cluster", new=False) host = Host.host(self._host.name()) if host is None: flash(f"Host {self._host.name()} could not be found.") return None host.edit(attributes, self._get_cluster_nodes()) self._host = folder.load_host(self._host.name()) if request.var("_save"): return redirect( mode_url("inventory", folder=folder.path(), host=self._host.name())) if request.var("diag_host"): return redirect( mode_url("diag_host", folder=folder.path(), host=self._host.name(), _start_on_load="1")) return redirect(mode_url("folder", folder=folder.path()))
def update_service_phase(params) -> Response: """Update the phase of a service""" body = params["body"] host = Host.load_host(params["host_name"]) target_phase = body["target_phase"] check_type = body["check_type"] service_item = body["service_item"] _update_single_service_phase( SERVICE_DISCOVERY_PHASES[target_phase], host, check_type, service_item, ) return Response(status=204)
def create_cluster_host(params): """Create a cluster host A cluster host groups many hosts (called nodes in this context) into a conceptual cluster. All the services of the individual nodes will be collated on the cluster host.""" user.need_permission("wato.edit") body = params["body"] host_name = body["host_name"] folder: CREFolder = body["folder"] folder.create_hosts([(host_name, body["attributes"], body["nodes"])], bake_hosts=params[BAKE_AGENT_PARAM_NAME]) host = Host.load_host(host_name) return _serve_host(host, effective_attributes=False)
def config_hostname_autocompleter(value: str, params: Dict) -> Choices: """Return the matching list of dropdown choices Called by the webservice with the current input field value and the completions_params to get the list of choices""" all_hosts: Dict[str, CREHost] = Host.all() match_pattern = re.compile(value, re.IGNORECASE) match_list: Choices = [] for host_name, host_object in all_hosts.items(): if match_pattern.search(host_name) is not None and host_object.may( "read"): match_list.append((host_name, host_name)) if not any(x[0] == value for x in match_list): match_list.insert( 0, (value, value)) # User is allowed to enter anything they want return match_list
def deserialize(cls, serialized: Dict[str, Any]) -> 'SiteRequest': enforce_host = EnforcedHostRequest( **serialized["enforce_host"]) if serialized["enforce_host"] else None if enforce_host: host = Host.host(enforce_host.host_name) if host is None: raise MKGeneralException( _("Host %s does not exist on remote site %s. This " "may be caused by a failed configuration synchronization. Have a look at " "the <a href=\"wato.py?folder=&mode=changelog\">activate changes page</a> " "for further information.") % (enforce_host.host_name, enforce_host.site_id)) host.need_permission("read") newest_host_labels = serialized["newest_host_labels"] assert isinstance(newest_host_labels, float) return cls(newest_host_labels, enforce_host)
def update_nodes(params): """Update the nodes of a cluster host""" user.need_permission("wato.edit") user.need_permission("wato.edit_hosts") host_name = params["host_name"] body = params["body"] nodes = body["nodes"] host: CREHost = Host.load_host(host_name) _require_host_etag(host) host.edit(host.attributes(), nodes) return constructors.serve_json( constructors.object_sub_property( domain_type="host_config", ident=host_name, name="nodes", value=host.cluster_nodes(), ))
def bulk_update_hosts(params): """Bulk update hosts Please be aware that when doing bulk updates, it is not possible to prevent the [Updating Values]("lost update problem"), which is normally prevented by the ETag locking mechanism. Use at your own risk. """ user.need_permission("wato.edit") user.need_permission("wato.edit_hosts") body = params["body"] entries = body["entries"] succeeded_hosts: List[CREHost] = [] failed_hosts: Dict[HostName, str] = {} for update_detail in entries: host_name = update_detail["host_name"] new_attributes = update_detail["attributes"] update_attributes = update_detail["update_attributes"] remove_attributes = update_detail["remove_attributes"] check_hostname(host_name) host: CREHost = Host.load_host(host_name) if new_attributes: host.edit(new_attributes, None) if update_attributes: host.update_attributes(update_attributes) faulty_attributes = [] for attribute in remove_attributes: if not host.has_explicit_attribute(attribute): faulty_attributes.append(attribute) if faulty_attributes: failed_hosts[ host_name] = f"Failed to remove {', '.join(faulty_attributes)}" continue if remove_attributes: host.clean_attributes(remove_attributes) succeeded_hosts.append(host) return _bulk_host_action_response(failed_hosts, succeeded_hosts)
def _renaming_collision_error(self, renamings): name_collisions = set() new_names = [new_name for _folder, _old_name, new_name in renamings] all_host_names = Host.all().keys() for name in new_names: if name in all_host_names: name_collisions.add(name) for name in new_names: if new_names.count(name) > 1: name_collisions.add(name) if name_collisions: warning = "<b>%s</b><ul>" % _( "You cannot do this renaming since the following host names would collide:" ) for name in sorted(list(name_collisions)): warning += "<li>%s</li>" % name warning += "</ul>" return warning return None
def show_services(params) -> Response: """Show all services of specific phase""" host = Host.load_host(params["host_name"]) discovery_request = StartDiscoveryRequest( host=host, folder=host.folder(), options=DiscoveryOptions( action="", show_checkboxes=False, show_parameters=False, show_discovered_labels=False, show_plugin_names=False, ignore_errors=True, ), ) discovery_result = get_check_table(discovery_request) return _serve_services( host, discovery_result.check_table, [params["discovery_phase"]], )
def execute(params) -> Response: """Execute a service discovery on a host""" host = Host.load_host(params["host_name"]) body = params["body"] discovery_request = StartDiscoveryRequest( host=host, folder=host.folder(), options=DiscoveryOptions( action=DISCOVERY_ACTION[body["mode"]], show_checkboxes=False, show_parameters=False, show_discovered_labels=False, show_plugin_names=False, ignore_errors=True, ), ) discovery_result = get_check_table(discovery_request) return _serve_services( host, discovery_result.check_table, list(SERVICE_DISCOVERY_PHASES.keys()), )
def update_host(params): """Update a host""" user.need_permission("wato.edit") user.need_permission("wato.edit_hosts") host_name = params["host_name"] body = params["body"] new_attributes = body["attributes"] update_attributes = body["update_attributes"] remove_attributes = body["remove_attributes"] check_hostname(host_name, should_exist=True) host: CREHost = Host.load_host(host_name) _require_host_etag(host) if new_attributes: new_attributes["meta_data"] = host.attributes().get("meta_data", {}) host.edit(new_attributes, None) if update_attributes: host.update_attributes(update_attributes) faulty_attributes = [] for attribute in remove_attributes: if not host.has_explicit_attribute(attribute): faulty_attributes.append(attribute) if remove_attributes: host.clean_attributes( remove_attributes) # silently ignores missing attributes if faulty_attributes: return problem( status=400, title="Some attributes were not removed", detail= f"The following attributes were not removed since they didn't exist: {', '.join(faulty_attributes)}", ) return _serve_host(host, effective_attributes=False)
def validate_host_parents(host): for parent_name in host.parents(): if parent_name == host.name(): raise MKUserError( None, _("You configured the host to be it's own parent, which is not allowed." )) parent = Host.host(parent_name) if not parent: raise MKUserError( None, _("You defined the non-existing host '%s' as a parent.") % parent_name) if host.site_id() != parent.site_id(): raise MKUserError( None, _("The parent '%s' is monitored on site '%s' while the host itself " "is monitored on site '%s'. Both must be monitored on the same site. Remember: The parent/child " "relation is used to describe the reachability of hosts by one monitoring daemon." ) % (parent_name, parent.site_id(), host.site_id()), )
def rename_host(params): """Rename a host""" user.need_permission("wato.edit") user.need_permission("wato.rename_hosts") if activate_changes.get_pending_changes_info(): return problem( status=409, title="Pending changes are present", detail= "Please activate all pending changes before executing a host rename process", ) host_name = params["host_name"] host: CREHost = Host.load_host(host_name) new_name = params["body"]["new_name"] _, auth_problems = perform_rename_hosts([(host.folder(), host_name, new_name)]) if auth_problems: return problem( status=422, title="Rename process failed", detail= f"It was not possible to rename the host {host_name} to {new_name}", ) return _serve_host(host, effective_attributes=False)
def perform_rename_hosts(renamings, job_interface=None): """Rename hosts mechanism Args: renamings: tuple consisting of folder, oldname, newname job_interface: only relevant for WATO interaction, allows to update the interface with the current update info """ def update_interface(message: str) -> None: if job_interface is None: return job_interface.send_progress_update(message) actions = [] all_hosts = Host.all() # 1. Fix WATO configuration itself ---------------- auth_problems = [] successful_renamings = [] update_interface(_("Renaming WATO configuration...")) for folder, oldname, newname in renamings: try: this_host_actions = [] update_interface(_("Renaming host(s) in folders...")) this_host_actions += _rename_host_in_folder(folder, oldname, newname) update_interface(_("Renaming host(s) in cluster nodes...")) this_host_actions += _rename_host_as_cluster_node(all_hosts, oldname, newname) update_interface(_("Renaming host(s) in parents...")) this_host_actions += _rename_host_in_parents(oldname, newname) update_interface(_("Renaming host(s) in rulesets...")) this_host_actions += _rename_host_in_rulesets(folder, oldname, newname) update_interface(_("Renaming host(s) in BI aggregations...")) this_host_actions += _rename_host_in_bi(oldname, newname) actions += this_host_actions successful_renamings.append((folder, oldname, newname)) except MKAuthException as e: auth_problems.append((oldname, e)) # 2. Checkmk stuff ------------------------------------------------ update_interface(_("Renaming host(s) in base configuration, rrd, history files, etc.")) update_interface(_("This might take some time and involves a core restart...")) action_counts = _rename_hosts_in_check_mk(successful_renamings) # 3. Notification settings ---------------------------------------------- # Notification rules - both global and users' ones update_interface(_("Renaming host(s) in notification rules...")) for folder, oldname, newname in successful_renamings: actions += _rename_host_in_event_rules(oldname, newname) actions += _rename_host_in_multisite(oldname, newname) for action in actions: action_counts.setdefault(action, 0) action_counts[action] += 1 update_interface(_("Calling final hooks")) call_hook_hosts_changed(Folder.root_folder()) return action_counts, auth_problems
def show_host(params): """Show a host""" host_name = params["host_name"] host: CREHost = Host.load_host(host_name) return _serve_host(host, effective_attributes=params["effective_attributes"])
def is_ineffective(self): hosts = Host.all() for host_name, host in hosts.items(): if self.matches_host_and_item(host.folder(), host_name, NO_ITEM): return False return True