Initial commit: Hermes Agent Skills collection
This commit is contained in:
@@ -0,0 +1,59 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Parse qm guest exec JSON output and extract decoded stdout.
|
||||
|
||||
Use as a remote module or standalone:
|
||||
python3 parse_qm_guest_exec.py <vmid> "<command>"
|
||||
python3 parse_qm_guest_exec.py 106 "hostname"
|
||||
|
||||
Returns JSON:
|
||||
{"ok": true, "exitcode": 0, "stdout": "...", "stderr": "..."}
|
||||
{"ok": false, "error": "...", "raw": "..."}
|
||||
"""
|
||||
import json, base64, subprocess, sys
|
||||
|
||||
def qm_guest_exec(vmid: str, cmd: str, timeout: int = 15) -> dict:
|
||||
r = subprocess.run(
|
||||
["qm", "guest", "exec", vmid, "--", "/bin/sh", "-c", cmd],
|
||||
capture_output=True, text=True, timeout=timeout
|
||||
)
|
||||
try:
|
||||
data = json.loads(r.stdout)
|
||||
except json.JSONDecodeError as e:
|
||||
return {"ok": False, "error": f"JSON parse: {e}", "raw": r.stdout[:400]}
|
||||
|
||||
result = {"ok": True}
|
||||
result["exitcode"] = data.get("exitcode", 1)
|
||||
result["exited"] = data.get("exited", 1)
|
||||
|
||||
if "out-data" in data and data["out-data"]:
|
||||
try:
|
||||
result["stdout"] = base64.b64decode(data["out-data"]).decode("utf-8", errors="replace")
|
||||
except Exception as e:
|
||||
result["stdout"] = f"decode error: {e}"
|
||||
else:
|
||||
result["stdout"] = ""
|
||||
|
||||
if "err-data" in data and data["err-data"]:
|
||||
try:
|
||||
result["stderr"] = base64.b64decode(data["err-data"]).decode("utf-8", errors="replace")
|
||||
except Exception as e:
|
||||
result["stderr"] = f"decode error: {e}"
|
||||
else:
|
||||
result["stderr"] = ""
|
||||
|
||||
return result
|
||||
|
||||
|
||||
def main():
|
||||
if len(sys.argv) < 3:
|
||||
print("Usage: python3 parse_qm_guest_exec.py <vmid> '<command>'", file=sys.stderr)
|
||||
sys.exit(1)
|
||||
|
||||
vmid = sys.argv[1]
|
||||
cmd = sys.argv[2]
|
||||
result = qm_guest_exec(vmid, cmd)
|
||||
print(json.dumps(result, indent=2))
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
@@ -0,0 +1,63 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Proxmox CT/VM config scanner — run remotely on any PVE node via SSH.
|
||||
|
||||
Produces JSON:
|
||||
{
|
||||
"cts": [{"id": "...", "hostname": "...", "ip": "...", "tags": "...", "memory_mb": N, "cores": N}],
|
||||
"vms": [{"id": "...", "name": "...", "memory_mb": N, "cores": N, "vlan_tag": "..."}]
|
||||
}
|
||||
"""
|
||||
import json, os, re
|
||||
|
||||
def main():
|
||||
result = {"cts": [], "vms": []}
|
||||
ct_dir = "/etc/pve/lxc"
|
||||
if os.path.isdir(ct_dir):
|
||||
for f in sorted(os.listdir(ct_dir)):
|
||||
if not f.endswith(".conf"): continue
|
||||
ct_id = f.replace(".conf", "")
|
||||
try:
|
||||
with open(os.path.join(ct_dir, f)) as fh:
|
||||
cfg = fh.read()
|
||||
except OSError: continue
|
||||
h = re.search(r"^hostname:\s*(\S+)", cfg, re.MULTILINE)
|
||||
ipv4 = re.search(r"ip=(\d[\d.]+)", cfg)
|
||||
tags_m = re.search(r"^tags:\s*(.+)", cfg, re.MULTILINE)
|
||||
mem = re.search(r"^memory:\s*(\d+)", cfg, re.MULTILINE)
|
||||
cores = re.search(r"^cores:\s*(\d+)", cfg, re.MULTILINE)
|
||||
ostype = re.search(r"^ostype:\s*(\S+)", cfg, re.MULTILINE)
|
||||
result["cts"].append({
|
||||
"id": ct_id, "hostname": h.group(1) if h else "unknown",
|
||||
"ip": ipv4.group(1) if ipv4 else "dhcp",
|
||||
"tags": tags_m.group(1) if tags_m else "",
|
||||
"memory_mb": int(mem.group(1)) if mem else 0,
|
||||
"cores": int(cores.group(1)) if cores else 0,
|
||||
"ostype": ostype.group(1) if ostype else "",
|
||||
})
|
||||
vm_dir = "/etc/pve/qemu-server"
|
||||
if os.path.isdir(vm_dir):
|
||||
for f in sorted(os.listdir(vm_dir)):
|
||||
if not f.endswith(".conf"): continue
|
||||
vm_id = f.replace(".conf", "")
|
||||
try:
|
||||
with open(os.path.join(vm_dir, f)) as fh:
|
||||
cfg = fh.read()
|
||||
except OSError: continue
|
||||
n = re.search(r"^name:\s*(\S+)", cfg, re.MULTILINE)
|
||||
mem = re.search(r"^memory:\s*(\d+)", cfg, re.MULTILINE)
|
||||
cores = re.search(r"^cores:\s*(\d+)", cfg, re.MULTILINE)
|
||||
sockets = re.search(r"^sockets:\s*(\d+)", cfg, re.MULTILINE)
|
||||
tag = re.search(r"tag=(\d+)", cfg)
|
||||
mac = re.search(r"virtio=([A-Fa-f0-9:]+)", cfg)
|
||||
result["vms"].append({
|
||||
"id": vm_id, "name": n.group(1) if n else "unknown",
|
||||
"memory_mb": int(mem.group(1)) if mem else 0,
|
||||
"cores": int(cores.group(1)) if cores else 0,
|
||||
"sockets": int(sockets.group(1)) if sockets else 1,
|
||||
"vlan_tag": tag.group(1) if tag else "",
|
||||
"mac": mac.group(1).upper() if mac else "",
|
||||
})
|
||||
print(json.dumps(result))
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
Reference in New Issue
Block a user