diff --git a/docs/core/mcp.md b/docs/core/mcp.md index 03ddd0a..ae9aedb 100644 --- a/docs/core/mcp.md +++ b/docs/core/mcp.md @@ -83,6 +83,7 @@ Each role includes the tools of the roles above it in this list. | Traefik plugin | `list_routers` (each router graded as the Traefik page grades it, with the status codes behind a problem, what they mean and the swarm service behind it), `get_router` (one router's history, codes, normal and anomalies), `list_certificates` (served certificates, days left, the routers they cover), `get_traffic` (the access log's clients, countries and routers, and Traefik's own figures) | | UniFi plugin | `get_network` (the WAN, clients, Wi-Fi networks, speed tests and devices by status), `list_devices`, `list_clients` (everyone seen, or the newest poll's top talkers), `list_network_events`, `list_alarms`, `get_network_config` (networks and VLANs, port forwards, firewall rules) | | SNMP plugin | `list_snmp_devices` (each device's state, newest readings in their units and the readings it didn't return), `get_snmp_device` (one device's readings over a range). Never the community or v3 keys | +| Syslog plugin | `list_syslog_sources` (the devices sending syslog: added, offered or ignored, each with its rate, last heard and newest line), `search_syslog` (an added source's kept lines, or a host's, filtered by text, severity and worse, app and time, newest first) | | Prometheus plugin | `list_prometheus_targets` (each target's state and its newest scrape: series kept, dropped and over the cap), `get_prometheus_target` (a target's stored series by metric, with labels and newest values). Never the token, and a password in a URL is masked | ### Operator: run playbooks, change monitoring diff --git a/plugins/docker/mcp_tools.py b/plugins/docker/mcp_tools.py index 15adc85..2f049d4 100644 --- a/plugins/docker/mcp_tools.py +++ b/plugins/docker/mcp_tools.py @@ -17,7 +17,7 @@ from steward.ansible.sources import BUILTIN_SOURCE_NAME from steward.mcp._context import current_caller, mcp_app from steward.mcp.tools._audit import audit_mcp from steward.mcp.tools._shape import ( - age_s, bounded, clamp_limit, db_session, iso, lookback, now_utc, pick, rnd, summarize, + age_s, bounded, clamp_limit, cut_middle, db_session, iso, lookback, now_utc, pick, rnd, summarize, ) from steward.mcp.tools.overview import finding from steward.models.ansible_inventory import AnsibleTarget @@ -31,19 +31,10 @@ from .models import DockerContainer, DockerDiskUsage, DockerEvent, DockerSwarmNo from .swarm_view import build_swarm_services, replaced_tasks, task_time CRASH_LOOP_DEATHS = docker_svc.CRASH_LOOP_DEATHS -LOG_LINE_CHARS = 2000 # ── pure helpers ───────────────────────────────────────────────────────────── -def cut_middle(text: str, limit: int = LOG_LINE_CHARS) -> str: - """Keep both ends of an over-long line and say how much of the middle went.""" - if text is None or len(text) <= limit: - return text - keep = limit // 2 - return f"{text[:keep]} [… {len(text) - 2 * keep} characters omitted …] {text[-keep:]}" - - def container_row(c, host_names: dict, problems: list[str], now: datetime, replaced: bool = False, since: datetime | None = None) -> dict: running = (c.status or "").lower() == "running" diff --git a/plugins/docker/service.py b/plugins/docker/service.py index accb7a1..ac3d151 100644 --- a/plugins/docker/service.py +++ b/plugins/docker/service.py @@ -12,6 +12,7 @@ from datetime import datetime, timedelta, timezone from sqlalchemy import func, select from steward.core import metric_series +from steward.core.like import escape_like from steward.models.hosts import Host from steward.models.metrics import PluginMetric @@ -274,11 +275,6 @@ async def host_map(db, host_ids: set[str]) -> dict[str, Host]: } -def escape_like(text: str) -> str: - """Make `text` match literally inside a LIKE/ILIKE pattern (escape char \\).""" - return text.replace("\\", "\\\\").replace("%", "\\%").replace("_", "\\_") - - async def list_containers(db, host_id: str | None = None) -> list[DockerContainer]: """Containers, running first then by name; one host's when host_id is given.""" stmt = select(DockerContainer) diff --git a/plugins/snmp/routes.py b/plugins/snmp/routes.py index d8232e5..ffe65f4 100644 --- a/plugins/snmp/routes.py +++ b/plugins/snmp/routes.py @@ -50,6 +50,7 @@ def view(device: SnmpDevice, values: dict, now: datetime) -> dict: state = device_state(device, _poll_seconds(), now) names = {r.key: r.name for r in readings_for(device)} return {"id": device.id, "name": device.name, "address": device.address, "port": device.port, + "host_id": device.host_id, "version": device.version, "reach": state.reach, "says": state.says, "polled_at": device.last_polled_at, "ok_at": device.last_ok_at, "readings": shown_readings(device, values), diff --git a/plugins/snmp/templates/snmp/device.html b/plugins/snmp/templates/snmp/device.html index 77dd7fd..ac6cfe8 100644 --- a/plugins/snmp/templates/snmp/device.html +++ b/plugins/snmp/templates/snmp/device.html @@ -40,6 +40,11 @@
This device has no readings chosen{% if session.user_role == 'admin' %}; Edit to choose some{% endif %}.
{% endif %} +{# The device's own log, when it sends syslog to an added source (#6000). #} +{% if "syslog" in enabled_plugins %} +
+{% endif %} +