Commit Graph

9 Commits

Author SHA1 Message Date
marcus 59f7fc1d6c Fix create_network + delete_network: POST with DevTools-confirmed format
create_network:
  - Switch from GET request() to post_request()
  - name, subnet, gateway, iprange → json.dumps(value) per DSM convention
  - disable_masquerade=json.dumps(False) added as required fixed param

delete_network:
  - Switch from GET request() to post_request() with method "remove"
    (was "delete" — wrong method name)
  - Send full network object from list response as
    networks=json.dumps([{...}]) including all DSM fields plus _key=id
    (ipv6_gateway, ipv6_subnet, ipv6_iprange, disable_masquerade with
    safe defaults for fields absent from the list response)

Tests updated: mock post_request, assert correct method/params structure.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-13 18:56:21 +02:00
marcus 4cee16922f Add list_networks, create_network, delete_network tools (Gruppe 4)
list_networks:
  SYNO.Docker.Network/list → shows name, driver, subnet, gateway,
  attached containers. Response key is "network" (not "networks").

create_network(name, driver, subnet, gateway, ip_range, enable_ipv6, confirmed):
  Dry-run preview without confirmed=True. Passes enable_ipv6 as
  json.dumps(bool) per N4S4 reference. Optional params (subnet,
  gateway, iprange) omitted from request when not provided.

delete_network(name, confirmed):
  Validates network exists and has no attached containers before
  deleting. Clear error listing attached container names if blocked.

15 unit tests covering all paths.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-13 18:42:10 +02:00
marcus 6bdd2bcb6a Add system_df and system_prune tools (Gruppe 3)
system_df:
  Assembles disk-usage report from SYNO.Docker.Image/list and
  SYNO.Docker.Container/list. Reports image count/size/reclaimable
  (images not referenced by any container), container running/stopped.
  Gracefully degrades when one API is unavailable.

system_prune:
  Without confirmed=True: lists dangling/unused images and stopped
  containers with sizes (dry-run preview).
  With confirmed=True: calls SYNO.Docker.Utils/prune and reports
  reclaimed space from the response (SpaceReclaimed field).

10 unit tests: stats counts, reclaimable detection, preview content,
confirmed execution, missing-response-field graceful handling, API error.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-13 18:36:49 +02:00
marcus a8da306ce5 Add container_stats tool (Gruppe 2); remove rename_container
container_stats(container_name) calls SYNO.Docker.Container/stats,
locates the entry by name (stripping the DSM-added leading slash),
and reports:
  - CPU %  (standard Docker formula: cpu_delta / system_delta * cpus * 100)
  - Memory used / limit  (human-readable)
  - Network I/O rx / tx  (summed across all interfaces)
  - Block I/O read / write  (from io_service_bytes_recursive)

Gracefully handles first-poll (precpu system_cpu_usage absent → 0%).
7 unit tests covering: found, CPU formula, memory format, slash-strip,
not-found, API error, no-precpu fallback.

rename_container removed: DSM Container Manager offers no rename API.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-13 18:29:17 +02:00
marcus 5edd051830 Fix delete_image: POST with images JSON array (DevTools-confirmed format)
DSM Container Manager rejects name+tag and sha256 id params (error 114).
Browser DevTools capture shows the correct call is:

  POST SYNO.Docker.Image / delete / version=1
  images=[{"repository":"nouchka/sqlite3","tags":["latest"]}]

Changes:
- Add DsmClient.post_request() for form-encoded POST requests
- delete_image now calls post_request with version=1 and the images
  JSON array built from the resolved repository name and tag
- Remove unused docker error-code dict from _error_message()
- Tests mock post_request and assert the images param content

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-13 18:19:22 +02:00
marcus 2b1e2ead7d Fix delete_image: use sha256 image ID instead of name+tag
DSM SYNO.Docker.Image/delete returns error 114 when called with name+tag.
The API expects the sha256 hash from the image list (field "id") as the
"id" parameter.

- Look up the sha256 hash from SYNO.Docker.Image/list (already fetched
  for the in-use check), then pass params={"id": img_hash}
- Guard against missing hash with a clear error message
- Updated tests to assert id param is sent, name/tag are absent

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-13 18:03:18 +02:00
marcus 24c3059e83 Fix delete_image: use rpartition for name:tag splitting
partition(":") split at the first colon, so registry-prefixed images
like "ghcr.io/open-webui/open-webui:v0.8.10" produced name="ghcr"
instead of the correct repository name, causing DSM error 114.

rpartition(":") always splits at the last colon, correctly handling:
- plain images: "nginx:1.24" → name="nginx", tag="1.24"
- namespaced: "nouchka/sqlite3:latest" → correct split
- registry URLs: "ghcr.io/foo/bar:v1" → name="ghcr.io/foo/bar", tag="v1"
- no tag: "nginx" → name="nginx", tag="latest" (fallback)

Added regression test verifying correct params sent to DSM delete API.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-13 17:51:02 +02:00
marcus 06735bb447 Add list_images and delete_image tools (Gruppe 1)
- list_images: lists all local Docker images sorted by size desc,
  shows size (human-readable), creation date, in-use marker, and
  update-available marker; gracefully handles container list failure
- delete_image: accepts name:tag or image hash, blocks deletion when
  image is in use by a container, requires confirmed=True to execute;
  default shows a dry-run preview
- 16 unit tests covering all paths (mock DSM client)
- ruff format + check clean

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-13 17:44:57 +02:00
marcus a0c1b6ed93 Initial implementation 2026-04-13 14:22:37 +02:00