From fde0b2d469b1e99c34675650177f4732e1c140c9 Mon Sep 17 00:00:00 2001 From: sascha Date: Fri, 14 Aug 2026 06:51:02 +0200 Subject: [PATCH 1/2] Ansible-NFS-Aktion: app.py aktualisieren --- app.py | 31 ++++++++++++++++++++----------- 1 file changed, 20 insertions(+), 11 deletions(-) diff --git a/app.py b/app.py index 0e2999c..187d584 100644 --- a/app.py +++ b/app.py @@ -1571,25 +1571,34 @@ async def ansible_run(request: Request, _=Depends(_verify)): if not hostname: return JSONResponse({"error": "limit/hostname required"}, status_code=400) action = body.get("action", "setup") - if action not in {"setup", "tune", "pvetune"}: - return JSONResponse({"error": "action must be setup, tune or pvetune"}, status_code=400) + if action not in {"setup", "tune", "pvetune", "nfs"}: + return JSONResponse({"error": "action must be setup, tune, pvetune or nfs"}, status_code=400) if not re.fullmatch(r"[a-zA-Z0-9_.:-]+", hostname): return JSONResponse({"error": "invalid hostname/limit"}, status_code=400) - if action in {"tune", "pvetune"}: - approved_files = ( - "roles/sysctl/defaults/main.yml", - "roles/sysctl/tasks/main.yml", - "group_vars/vps/sysctl.yml", - "sysctl-proxmox.yaml", - "roles/sysctl_proxmox/tasks/main.yml", - ) + if action in {"tune", "pvetune", "nfs"}: + if action == "nfs": + approved_files = ( + "roles/nfs_stability/tasks/main.yml", + "nfs-stability.yml", + "pfannkuchen.sh", + ) + prepare = "mkdir -p roles/nfs_stability/tasks && " + else: + approved_files = ( + "roles/sysctl/defaults/main.yml", + "roles/sysctl/tasks/main.yml", + "group_vars/vps/sysctl.yml", + "sysctl-proxmox.yaml", + "roles/sysctl_proxmox/tasks/main.yml", + ) + prepare = "" file_sync = " && ".join( f"git show origin/master:{path} > {path}" for path in approved_files ) command = ( "cd /app-config/ansible && " "git fetch origin master && " - f"{file_sync} && " + f"{prepare}{file_sync} && " f"bash pfannkuchen.sh {action} {hostname}" ) else: -- 2.49.1 From ad863eac15abc1278c60d3410eaa0344a7b7a229 Mon Sep 17 00:00:00 2001 From: sascha Date: Fri, 14 Aug 2026 06:51:03 +0200 Subject: [PATCH 2/2] Ansible-NFS-Aktion: tests/test_app.py aktualisieren --- tests/test_app.py | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/tests/test_app.py b/tests/test_app.py index 3a3be69..34932ce 100644 --- a/tests/test_app.py +++ b/tests/test_app.py @@ -235,6 +235,31 @@ def test_ansible_run_supports_safe_tune_action_and_syncs_approved_files(monkeypa assert len(calls) == 1 +def test_ansible_run_supports_scoped_nfs_action(monkeypatch): + calls = [] + + def fake_ssh(host, command, timeout=600): + calls.append((host, command, timeout)) + return 0, "changed=1 failed=0", "" + + monkeypatch.setattr(app, "_ssh", fake_ssh) + with TestClient(app.app) as client: + response = client.post( + "/ansible/run", + headers={"Authorization": "Bearer test-token"}, + json={"hostname": "arrapps", "action": "nfs"}, + ) + assert response.status_code == 200 + assert response.json()["action"] == "nfs" + command = calls[0][1] + assert "mkdir -p roles/nfs_stability/tasks" in command + assert "git show origin/master:roles/nfs_stability/tasks/main.yml" in command + assert "git show origin/master:nfs-stability.yml" in command + assert "bash pfannkuchen.sh nfs arrapps" in command + assert "git pull --ff-only" not in command + assert len(calls) == 1 + + def test_ansible_run_rejects_unknown_action_and_shell_metacharacters(monkeypatch): monkeypatch.setattr(app, "_ssh", lambda *_args, **_kwargs: (_ for _ in ()).throw(AssertionError("must not SSH"))) with TestClient(app.app) as client: -- 2.49.1