"""
The platform's health now, in one report: everything that the status services read, and what looks wrong.
- :func:`platform_status` reads the environment's pods and the cluster's nodes, with their CPU and
memory use, the namespace's warning events (:mod:`~smarter.apps.mcpserver.services.status.cluster`),
the dashboard's health checks, the Celery queues and the database's load
(:mod:`~smarter.apps.mcpserver.services.status.platform`), and the status pages of the AI
providers and of AWS (:mod:`~smarter.apps.mcpserver.services.status.feeds`). Its report starts
with its *observations*: what looks wrong, in plain sentences, from :func:`observations`.
- :func:`eks_status` reads the cluster as the cloud provider sees it: the control plane, the node
groups, the add-ons and their available updates, and the provider's insights, from
:meth:`~smarter.apps.infrastructure.providers.base.CloudProvider.get_kubernetes_cluster_health`,
and the recent AWS events of the platform's region.
The MCPServer's staff-only tools ``platform_status`` and ``eks_status``
(:mod:`smarter.apps.mcpserver.protocol.tools.status`) format these reports as Markdown. Nothing
adds cloud monitoring costs: the data comes from Kubernetes, metrics-server, the platform's own
services, the EKS api, and public status pages.
.. note::
**Experimental.** The MCPServer was designed and coded by Claude Code (Anthropic's
Claude Opus 5.5), with Lawrence McDaniel as co-author. It is experimental, and will
be documented.
"""
from dataclasses import dataclass
from datetime import datetime, timezone
from typing import Optional
from smarter.apps.infrastructure.cluster_health import ClusterHealth
from smarter.apps.infrastructure.services import infrastructure
from smarter.apps.mcpserver.services.status.cluster import (
ClusterSnapshot,
NodeStatus,
PodStatus,
cluster_snapshot,
percent,
)
from smarter.apps.mcpserver.services.status.feeds import (
AwsEvent,
ProviderStatus,
aws_service_events,
provider_statuses,
)
from smarter.apps.mcpserver.services.status.platform import (
DatabaseLoad,
database_load,
health_checks,
queue_depths,
)
from smarter.common.conf import smarter_settings
from smarter.lib import logging
from smarter.lib.django.waffle import SmarterWaffleSwitches
logger = logging.getSmarterLogger(__name__, any_switches=[SmarterWaffleSwitches.MCPSERVER_LOGGING])
logger_prefix = logging.SmarterFormattedText.text(__name__)
HIGH_USE_PERCENT = 90
"""
The use, as a percentage of what is allowed, at which a pod's memory, or a node's CPU or memory, is an observation.
A pod above its memory limit is killed (``OOMKilled``), and a node that is full cannot take new pods.
"""
DATABASE_BUSY_PERCENT = 80
"""The share of the database's connection limit in use at which the database is an observation."""
QUEUE_BACKLOG = 50
"""The number of tasks waiting in a Celery queue at which the queue is an observation: the workers are behind."""
[docs]
def utc(value: Optional[datetime]) -> str:
"""
Format a time for a report.
:param value: The time, or None if it is unknown.
:returns: The time in UTC, e.g. ``2026-11-02 14:05:00 UTC``, or ``?`` if it is unknown.
"""
if value is None:
return "?"
in_utc = value.astimezone(timezone.utc)
formatted = f"{in_utc:%Y-%m-%d %H:%M:%S} UTC"
return formatted
[docs]
def pod_observations(pods: list[PodStatus]) -> list[str]:
"""
Describe what is wrong with the environment's pods.
A pod is an observation when it is not ready, reports a problem, e.g. ``CrashLoopBackOff``, has
restarted, or uses at least :data:`HIGH_USE_PERCENT` of its memory limit.
:param pods: The pods, from :func:`~smarter.apps.mcpserver.services.status.cluster.cluster_snapshot`.
:returns: One sentence per problem, or an empty list if there is none.
"""
found = []
for pod in pods:
if not pod.ready or pod.problem:
reason = pod.problem or pod.phase or "not ready"
found.append(f"Pod {pod.name} ({pod.component}) is not healthy: {reason}.")
elif pod.restarts:
found.append(f"Pod {pod.name} ({pod.component}) has restarted {pod.restarts} times since it was created.")
memory_use = percent(pod.memory, pod.memory_limit)
if memory_use is not None and memory_use >= HIGH_USE_PERCENT:
found.append(
f"Pod {pod.name} uses {memory_use}% of its memory limit, and may be killed for lack of memory."
)
return found
[docs]
def node_observations(nodes: list[NodeStatus]) -> list[str]:
"""
Describe what is wrong with the cluster's nodes.
A node is an observation when it is not ready, reports a pressure condition, e.g.
``MemoryPressure``, is cordoned, or uses at least :data:`HIGH_USE_PERCENT` of its CPU or memory.
:param nodes: The nodes, from :func:`~smarter.apps.mcpserver.services.status.cluster.cluster_snapshot`.
:returns: One sentence per problem, or an empty list if there is none.
"""
found = []
for node in nodes:
if not node.ready:
found.append(f"Node {node.name} is not ready.")
if node.pressures:
pressures = ", ".join(node.pressures)
found.append(f"Node {node.name} reports {pressures}.")
if node.unschedulable:
found.append(f"Node {node.name} is cordoned: no new pods are scheduled on it.")
cpu_use = percent(node.cpu, node.cpu_allocatable)
if cpu_use is not None and cpu_use >= HIGH_USE_PERCENT:
found.append(f"Node {node.name} uses {cpu_use}% of its CPU.")
memory_use = percent(node.memory, node.memory_allocatable)
if memory_use is not None and memory_use >= HIGH_USE_PERCENT:
found.append(f"Node {node.name} uses {memory_use}% of its memory.")
return found
[docs]
def service_observations(
checks: list[tuple[str, bool]],
depths: dict[str, Optional[int]],
database: Optional[DatabaseLoad],
) -> list[str]:
"""
Describe what is wrong with the platform's backing services.
A failed health check is an observation, and so is a Celery queue that could not be read or has at
least :data:`QUEUE_BACKLOG` waiting tasks, and a database with at least
:data:`DATABASE_BUSY_PERCENT` of its connections in use.
:param checks: The dashboard's health checks, from :func:`~smarter.apps.mcpserver.services.status.platform.health_checks`.
:param depths: The Celery queues' depths, from :func:`~smarter.apps.mcpserver.services.status.platform.queue_depths`.
:param database: The database's load, or None if it could not be read.
:returns: One sentence per problem, or an empty list if there is none.
"""
found = []
for name, healthy in checks:
if not healthy:
found.append(f"The {name} health check failed.")
for queue, depth in depths.items():
if depth is None:
found.append(f"The Celery queue {queue} could not be read.")
elif depth >= QUEUE_BACKLOG:
found.append(f"{depth} tasks are waiting in the Celery queue {queue}: the workers are behind.")
if database is None:
found.append("The database's load could not be read.")
return found
connection_use = percent(database.connections, database.max_connections)
if connection_use is not None and connection_use >= DATABASE_BUSY_PERCENT:
found.append(f"The database has {database.connections} of {database.max_connections} connections in use.")
return found
[docs]
def upstream_observations(providers: list[ProviderStatus], aws_events: list[AwsEvent]) -> list[str]:
"""
Describe the problems that the AI providers and AWS report on their status pages.
A provider whose page could not be read is not an observation: its status is unknown, not bad, and
the report's upstream section says so.
:param providers: The AI providers' status, from :func:`~smarter.apps.mcpserver.services.status.feeds.provider_statuses`.
:param aws_events: The recent AWS events, from :func:`~smarter.apps.mcpserver.services.status.feeds.aws_service_events`.
:returns: One sentence per problem, or an empty list if there is none.
"""
found = []
for provider in providers:
if provider.operational:
continue
if provider.indicator == "unknown":
continue
found.append(f"{provider.name} reports {provider.description or provider.indicator} ({provider.url}).")
for event in aws_events:
found.append(f"AWS {event.service} event: {event.title} ({utc(event.published)}).")
return found
[docs]
def observations(
snapshot: ClusterSnapshot,
checks: list[tuple[str, bool]],
depths: dict[str, Optional[int]],
database: Optional[DatabaseLoad],
providers: list[ProviderStatus],
aws_events: list[AwsEvent],
) -> list[str]:
"""
Describe everything that looks wrong, for the top of the platform's status report.
It gathers the observations of the pods, the nodes, the warning events, the backing services and
the upstream status pages, and the parts of the cluster that could not be read.
:param snapshot: The pods, nodes and warning events, from :func:`~smarter.apps.mcpserver.services.status.cluster.cluster_snapshot`.
:param checks: The dashboard's health checks.
:param depths: The Celery queues' depths.
:param database: The database's load, or None if it could not be read.
:param providers: The AI providers' status.
:param aws_events: The recent AWS events.
:returns: One sentence per problem, or an empty list if nothing looks wrong.
"""
found = []
found += pod_observations(snapshot.pods)
found += node_observations(snapshot.nodes)
if snapshot.events:
found.append(f"{len(snapshot.events)} Kubernetes warning events in the period.")
found += service_observations(checks, depths, database)
found += upstream_observations(providers, aws_events)
for error in snapshot.errors:
found.append(f"Could not read {error}.")
return found
[docs]
def read_database() -> Optional[DatabaseLoad]:
"""
Read the database's load, or report that it could not be read.
:returns: The load, or None if the database could not be queried. The failure is logged, and
becomes an observation.
"""
try:
return database_load()
# pylint: disable=broad-except
except Exception as e:
logger.warning("%s.read_database() could not read the database's load: %s", logger_prefix, e)
return None
[docs]
@dataclass(frozen=True)
class EksStatus:
"""The cluster, as the cloud provider sees it, as :func:`eks_status` returns it."""
health: ClusterHealth
"""The cluster's control plane, node groups, add-ons and insights."""
region: str
"""The platform's AWS region, from :func:`platform_region`."""
aws_events: list[AwsEvent]
"""The recent AWS events of EKS and EC2 in the region."""
unread_feeds: list[str]
"""The AWS services whose status feed could not be read."""
[docs]
def eks_status() -> EksStatus:
"""
Read the cluster's health from the cloud provider, and the recent AWS events of its region.
**Side effects:** read-only calls to the EKS api (``DescribeCluster``, ``ListNodegroups``,
``DescribeNodegroup``, ``ListAddons``, ``DescribeAddon``, ``DescribeAddonVersions`` and
``ListInsights``), and up to two HTTPS GETs of AWS status feeds, which are cached for a minute.
:returns: The cluster's health, and the region's AWS events.
:raises InfrastructureNotReadyError: If the cloud provider is not ready.
:raises SmarterInfrastructureError: If the cluster cannot be described.
"""
health = infrastructure.provider.get_kubernetes_cluster_health()
region = platform_region()
aws_events, unread_feeds = aws_service_events(region)
status = EksStatus(health=health, region=region, aws_events=aws_events, unread_feeds=unread_feeds)
return status
__all__ = [
"DATABASE_BUSY_PERCENT",
"HIGH_USE_PERCENT",
"QUEUE_BACKLOG",
"EksStatus",
"PlatformStatus",
"eks_status",
"node_observations",
"observations",
"platform_region",
"platform_status",
"pod_observations",
"read_database",
"service_observations",
"upstream_observations",
"utc",
]