354 lines
6.2 KiB
Python
Executable File
354 lines
6.2 KiB
Python
Executable File
#!/usr/bin/env python3
|
|
|
|
import os
|
|
from typing import Optional
|
|
|
|
from dotenv import load_dotenv
|
|
from proxmoxer import ProxmoxAPI
|
|
from mcp.server.fastmcp import FastMCP
|
|
|
|
load_dotenv("/opt/hermes/MCP_proxmox/.env")
|
|
|
|
mcp = FastMCP("proxmox-mcp")
|
|
|
|
PROXMOX_HOST = os.getenv("PROXMOX_HOST")
|
|
PROXMOX_USER = os.getenv("PROXMOX_USER")
|
|
PROXMOX_TOKEN_NAME = os.getenv("PROXMOX_TOKEN_NAME")
|
|
PROXMOX_TOKEN_VALUE = os.getenv("PROXMOX_TOKEN_VALUE")
|
|
|
|
VERIFY_SSL = (
|
|
os.getenv("PROXMOX_VERIFY_SSL", "true").lower() == "true"
|
|
)
|
|
|
|
ALLOW_POWER_ACTIONS = (
|
|
os.getenv("ALLOW_POWER_ACTIONS", "false").lower() == "true"
|
|
)
|
|
|
|
ALLOW_DELETE = (
|
|
os.getenv("ALLOW_DELETE", "false").lower() == "true"
|
|
)
|
|
|
|
ALLOW_ROLLBACK = (
|
|
os.getenv("ALLOW_ROLLBACK", "false").lower() == "true"
|
|
)
|
|
|
|
def get_proxmox():
|
|
return ProxmoxAPI(
|
|
PROXMOX_HOST,
|
|
user=PROXMOX_USER,
|
|
token_name=PROXMOX_TOKEN_NAME,
|
|
token_value=PROXMOX_TOKEN_VALUE,
|
|
verify_ssl=VERIFY_SSL,
|
|
backend="https",
|
|
)
|
|
def power_allowed():
|
|
if not ALLOW_POWER_ACTIONS:
|
|
raise PermissionError(
|
|
"Les actions d'alimentation sont désactivées."
|
|
)
|
|
|
|
|
|
@mcp.tool()
|
|
def list_nodes():
|
|
"""
|
|
Liste tous les nodes du cluster.
|
|
"""
|
|
|
|
proxmox = get_proxmox()
|
|
|
|
return proxmox.nodes.get()
|
|
|
|
|
|
@mcp.tool()
|
|
def cluster_status():
|
|
"""
|
|
Retourne l'état général du cluster.
|
|
"""
|
|
|
|
proxmox = get_proxmox()
|
|
|
|
return proxmox.cluster.status.get()
|
|
|
|
|
|
@mcp.tool()
|
|
def list_vms(node: Optional[str] = None):
|
|
"""
|
|
Liste les VMs QEMU.
|
|
"""
|
|
|
|
proxmox = get_proxmox()
|
|
|
|
result = []
|
|
|
|
if node:
|
|
nodes = [node]
|
|
else:
|
|
nodes = [
|
|
n["node"]
|
|
for n in proxmox.nodes.get()
|
|
]
|
|
|
|
for current_node in nodes:
|
|
|
|
try:
|
|
vms = proxmox.nodes(current_node).qemu.get()
|
|
|
|
for vm in vms:
|
|
|
|
result.append({
|
|
"node": current_node,
|
|
"vmid": vm.get("vmid"),
|
|
"name": vm.get("name"),
|
|
"status": vm.get("status"),
|
|
"cpus": vm.get("cpus"),
|
|
"mem": vm.get("mem"),
|
|
"maxmem": vm.get("maxmem"),
|
|
"uptime": vm.get("uptime")
|
|
})
|
|
|
|
except Exception as e:
|
|
|
|
result.append({
|
|
"node": current_node,
|
|
"error": str(e)
|
|
})
|
|
|
|
return result
|
|
|
|
|
|
@mcp.tool()
|
|
def list_lxc(node: Optional[str] = None):
|
|
"""
|
|
Liste les conteneurs LXC.
|
|
"""
|
|
|
|
proxmox = get_proxmox()
|
|
|
|
result = []
|
|
|
|
if node:
|
|
nodes = [node]
|
|
else:
|
|
nodes = [
|
|
n["node"]
|
|
for n in proxmox.nodes.get()
|
|
]
|
|
|
|
for current_node in nodes:
|
|
|
|
try:
|
|
lxcs = proxmox.nodes(current_node).lxc.get()
|
|
|
|
for ct in lxcs:
|
|
|
|
result.append({
|
|
"node": current_node,
|
|
"vmid": ct.get("vmid"),
|
|
"name": ct.get("name"),
|
|
"status": ct.get("status"),
|
|
"cpus": ct.get("cpus"),
|
|
"mem": ct.get("mem"),
|
|
"maxmem": ct.get("maxmem"),
|
|
})
|
|
|
|
except Exception as e:
|
|
|
|
result.append({
|
|
"node": current_node,
|
|
"error": str(e)
|
|
})
|
|
|
|
return result
|
|
|
|
|
|
@mcp.tool()
|
|
def vm_status(node: str, vmid: int):
|
|
"""
|
|
Statut détaillé d'une VM.
|
|
"""
|
|
|
|
proxmox = get_proxmox()
|
|
|
|
return proxmox.nodes(node).qemu(vmid).status.current.get()
|
|
|
|
|
|
@mcp.tool()
|
|
def lxc_status(node: str, vmid: int):
|
|
"""
|
|
Statut détaillé d'un LXC.
|
|
"""
|
|
|
|
proxmox = get_proxmox()
|
|
|
|
return proxmox.nodes(node).lxc(vmid).status.current.get()
|
|
|
|
|
|
@mcp.tool()
|
|
def node_resources(node: str):
|
|
"""
|
|
CPU / RAM / stockage du node.
|
|
"""
|
|
|
|
proxmox = get_proxmox()
|
|
|
|
return proxmox.nodes(node).status.get()
|
|
|
|
|
|
@mcp.tool()
|
|
def start_vm(node: str, vmid: int):
|
|
|
|
power_allowed()
|
|
|
|
proxmox = get_proxmox()
|
|
|
|
task = (
|
|
proxmox.nodes(node)
|
|
.qemu(vmid)
|
|
.status.start.post()
|
|
)
|
|
|
|
return {
|
|
"action": "start",
|
|
"vmid": vmid,
|
|
"task": task
|
|
}
|
|
|
|
|
|
@mcp.tool()
|
|
def stop_vm(node: str, vmid: int):
|
|
|
|
power_allowed()
|
|
|
|
proxmox = get_proxmox()
|
|
|
|
task = (
|
|
proxmox.nodes(node)
|
|
.qemu(vmid)
|
|
.status.stop.post()
|
|
)
|
|
|
|
return {
|
|
"action": "stop",
|
|
"vmid": vmid,
|
|
"task": task
|
|
}
|
|
|
|
|
|
@mcp.tool()
|
|
def shutdown_vm(node: str, vmid: int):
|
|
|
|
power_allowed()
|
|
|
|
proxmox = get_proxmox()
|
|
|
|
task = (
|
|
proxmox.nodes(node)
|
|
.qemu(vmid)
|
|
.status.shutdown.post()
|
|
)
|
|
|
|
return {
|
|
"action": "shutdown",
|
|
"vmid": vmid,
|
|
"task": task
|
|
}
|
|
|
|
|
|
@mcp.tool()
|
|
def reboot_vm(node: str, vmid: int):
|
|
|
|
power_allowed()
|
|
|
|
proxmox = get_proxmox()
|
|
|
|
task = (
|
|
proxmox.nodes(node)
|
|
.qemu(vmid)
|
|
.status.reboot.post()
|
|
)
|
|
|
|
return {
|
|
"action": "reboot",
|
|
"vmid": vmid,
|
|
"task": task
|
|
}
|
|
|
|
|
|
@mcp.tool()
|
|
def create_snapshot(
|
|
node: str,
|
|
vmid: int,
|
|
snapshot_name: str,
|
|
description: str = ""
|
|
):
|
|
"""
|
|
Création snapshot VM.
|
|
"""
|
|
|
|
proxmox = get_proxmox()
|
|
|
|
task = (
|
|
proxmox.nodes(node)
|
|
.qemu(vmid)
|
|
.snapshot.post(
|
|
snapname=snapshot_name,
|
|
description=description
|
|
)
|
|
)
|
|
|
|
return {
|
|
"action": "snapshot",
|
|
"vmid": vmid,
|
|
"snapshot": snapshot_name,
|
|
"task": task
|
|
}
|
|
|
|
|
|
@mcp.tool()
|
|
def list_snapshots(node: str, vmid: int):
|
|
|
|
proxmox = get_proxmox()
|
|
|
|
return (
|
|
proxmox.nodes(node)
|
|
.qemu(vmid)
|
|
.snapshot.get()
|
|
)
|
|
|
|
|
|
@mcp.tool()
|
|
def rollback_snapshot(
|
|
node: str,
|
|
vmid: int,
|
|
snapshot_name: str
|
|
):
|
|
"""
|
|
Rollback snapshot.
|
|
Désactivé par défaut.
|
|
"""
|
|
|
|
if not ALLOW_ROLLBACK:
|
|
raise PermissionError(
|
|
"Rollback désactivé."
|
|
)
|
|
|
|
proxmox = get_proxmox()
|
|
|
|
task = (
|
|
proxmox.nodes(node)
|
|
.qemu(vmid)
|
|
.snapshot(snapshot_name)
|
|
.rollback.post()
|
|
)
|
|
|
|
return {
|
|
"action": "rollback",
|
|
"vmid": vmid,
|
|
"snapshot": snapshot_name,
|
|
"task": task
|
|
}
|
|
|
|
|
|
if __name__ == "__main__":
|
|
mcp.run()
|