vm management
This commit is contained in:
68
app.py
68
app.py
@@ -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)
|
Reference in New Issue
Block a user