vm management

This commit is contained in:
Mateusz Gruszczyński
2025-10-17 15:55:21 +02:00
parent a3ea45b949
commit 8c545aca55
3 changed files with 162 additions and 1 deletions

68
app.py
View File

@@ -431,6 +431,72 @@ def api_disable():
ha_node_maint(False, node, log)
return jsonify(ok=True, log=log)
# --- VM/CT admin actions API ---
def vm_locked(typ: str, node: str, vmid: int) -> bool:
base = f"/nodes/{node}/{typ}/{vmid}"
cur = get_json(["pvesh", "get", f"{base}/status/current"]) or {}
return bool(cur.get("lock"))
def run_qm_pct(typ: str, vmid: int, subcmd: str) -> subprocess.CompletedProcess:
tool = "qm" if typ == "qemu" else "pct"
return run([tool, subcmd, str(vmid)])
@app.get("/api/list-all-vmct")
def api_list_all_vmct():
meta = cluster_vmct_meta()
nodes = [n.get("node") for n in (api_cluster_data().get("nodes") or []) if n.get("node")]
return jsonify({"all": list(meta.values()), "nodes": sorted(set(nodes))})
@app.post("/api/vm-action")
def api_vm_action():
if os.geteuid() != 0:
return jsonify(ok=False, error="run as root"), 403
data = request.get_json(force=True, silent=True) or {}
sid = data.get("sid", "")
action = data.get("action", "")
target = data.get("target", "")
meta = cluster_vmct_meta()
tup = sid_to_tuple(sid, meta)
if not tup:
return jsonify(ok=False, error="bad sid"), 400
typ, vmid, node = tup
if not node:
return jsonify(ok=False, error="unknown node"), 400
if action == "migrate_offline":
action = "migrate"
try:
if action == "unlock":
if typ != "qemu":
return jsonify(ok=False, error="unlock only for qemu"), 400
if not vm_locked(typ, node, vmid):
return jsonify(ok=True, msg="not locked")
r = run(["qm", "unlock", str(vmid)])
return jsonify(ok=(r.returncode == 0), stdout=r.stdout, stderr=r.stderr)
elif action in ("start", "stop", "shutdown"):
r = run_qm_pct(typ, vmid, action)
return jsonify(ok=(r.returncode == 0), stdout=r.stdout, stderr=r.stderr)
elif action == "migrate":
if not target or target == node:
return jsonify(ok=False, error="target required and must differ from source"), 400
base = f"/nodes/{node}/{typ}/{vmid}/migrate"
cmd = ["pvesh", "create", base, "-target", target, "-online", "0"]
res = post_json(cmd)
return jsonify(ok=True, result=res or {})
else:
return jsonify(ok=False, error="unknown action"), 400
except Exception as e:
return jsonify(ok=False, error=str(e)), 500
if __name__ == "__main__":
import argparse
p = argparse.ArgumentParser()
@@ -439,4 +505,4 @@ if __name__ == "__main__":
args = p.parse_args()
DEFAULT_NODE = args.node
host, port = args.bind.split(":")
app.run(host=host, port=int(port), debug=False, threaded=True)
app.run(host=host, port=int(port), debug=False, threaded=True)