feat(docker): collect + persist /system/df image/disk usage (agent 1.6.0)
Backend for the image/disk panel (milestone 77 #942). Agent gains collect_disk_usage() — one /system/df call (gated on containers existing, so Docker-less hosts pay nothing), surfacing reclaimable bytes (image size held by unreferenced images), layers/containers/volumes/build-cache sizes, and the top 50 images by size. Emitted as sample["docker_disk"]; host_agent ingest tracks the newest sample's copy and hands it to the docker capability as a 5th arg. New current-state tables docker_disk_usage (one row/host) + docker_images (per-host image rows), docker_007 migration; ingest upserts the summary and replaces the image set per host (stale images pruned). Unit tests for the df parsing/reclaimable math + build_sample gating; integration test for persistence + image-set replacement. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_016Jg27rgypiW2efULXJDtMC
This commit is contained in:
@@ -0,0 +1,55 @@
|
||||
"""Docker disk usage + image storage tables
|
||||
|
||||
Adds docker_disk_usage (one per-host /system/df summary row) and docker_images
|
||||
(the heavy-hitter image storage records). Both current-state, host-scoped,
|
||||
re-reported each interval. Additive create_table.
|
||||
|
||||
Revision ID: docker_007_disk_usage
|
||||
Revises: docker_006_metric_rollup
|
||||
Create Date: 2026-06-19
|
||||
"""
|
||||
from typing import Sequence, Union
|
||||
from alembic import op
|
||||
import sqlalchemy as sa
|
||||
|
||||
revision: str = "docker_007_disk_usage"
|
||||
down_revision: Union[str, None] = "docker_006_metric_rollup"
|
||||
branch_labels: Union[str, Sequence[str], None] = None
|
||||
depends_on: Union[str, Sequence[str], None] = None
|
||||
|
||||
|
||||
def upgrade() -> None:
|
||||
op.create_table(
|
||||
"docker_disk_usage",
|
||||
sa.Column("host_id", sa.String(length=36), nullable=False),
|
||||
sa.Column("layers_size", sa.BigInteger(), nullable=False, server_default="0"),
|
||||
sa.Column("images_size", sa.BigInteger(), nullable=False, server_default="0"),
|
||||
sa.Column("images_reclaimable", sa.BigInteger(), nullable=False, server_default="0"),
|
||||
sa.Column("containers_size", sa.BigInteger(), nullable=False, server_default="0"),
|
||||
sa.Column("volumes_size", sa.BigInteger(), nullable=False, server_default="0"),
|
||||
sa.Column("build_cache_size", sa.BigInteger(), nullable=False, server_default="0"),
|
||||
sa.Column("images_total", sa.Integer(), nullable=False, server_default="0"),
|
||||
sa.Column("images_active", sa.Integer(), nullable=False, server_default="0"),
|
||||
sa.Column("containers_count", sa.Integer(), nullable=False, server_default="0"),
|
||||
sa.Column("volumes_count", sa.Integer(), nullable=False, server_default="0"),
|
||||
sa.Column("updated_at", sa.DateTime(timezone=True), nullable=False),
|
||||
sa.ForeignKeyConstraint(["host_id"], ["hosts.id"], ondelete="CASCADE"),
|
||||
sa.PrimaryKeyConstraint("host_id"),
|
||||
)
|
||||
op.create_table(
|
||||
"docker_images",
|
||||
sa.Column("host_id", sa.String(length=36), nullable=False),
|
||||
sa.Column("image_id", sa.String(length=64), nullable=False),
|
||||
sa.Column("repo_tag", sa.String(length=512), nullable=False, server_default="<none>"),
|
||||
sa.Column("size", sa.BigInteger(), nullable=False, server_default="0"),
|
||||
sa.Column("shared_size", sa.BigInteger(), nullable=False, server_default="0"),
|
||||
sa.Column("containers", sa.Integer(), nullable=False, server_default="0"),
|
||||
sa.Column("updated_at", sa.DateTime(timezone=True), nullable=False),
|
||||
sa.ForeignKeyConstraint(["host_id"], ["hosts.id"], ondelete="CASCADE"),
|
||||
sa.PrimaryKeyConstraint("host_id", "image_id"),
|
||||
)
|
||||
|
||||
|
||||
def downgrade() -> None:
|
||||
op.drop_table("docker_images")
|
||||
op.drop_table("docker_disk_usage")
|
||||
Reference in New Issue
Block a user