Initial commit: forge-tools-proxmox — MCP-сервер для Proxmox VE
This commit is contained in:
@@ -0,0 +1,181 @@
|
||||
package tools
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"net/url"
|
||||
|
||||
"github.com/modelcontextprotocol/go-sdk/mcp"
|
||||
)
|
||||
|
||||
// snapshot.go — снапшоты VM/CT: list (read), create, delete, rollback.
|
||||
// Снапшот — точка восстановления гостя; delete/rollback необратимы →
|
||||
// confirm + gate + probe.
|
||||
|
||||
func registerSnapshotTools(s *mcp.Server) {
|
||||
s.AddTool(&mcp.Tool{
|
||||
Name: "snapshot_list",
|
||||
Description: "List snapshots of a VM/CT. Read-only.",
|
||||
InputSchema: schema(map[string]any{
|
||||
"node": strProps("Node name", true),
|
||||
"vmid": strProps("VM/CT ID", true),
|
||||
"kind": strProps("Guest type", false, "qemu", "lxc"),
|
||||
"host": strProps("Cluster alias (default: primary)", false),
|
||||
}, []string{"node", "vmid"}),
|
||||
}, snapshotListHandler)
|
||||
|
||||
registerPatternTool(s, &mcp.Tool{
|
||||
Name: "snapshot_create",
|
||||
Description: "Create a snapshot of a VM/CT. Requires confirm + write permission.",
|
||||
InputSchema: schema(map[string]any{
|
||||
"node": strProps("Node name", true),
|
||||
"vmid": strProps("VM/CT ID", true),
|
||||
"name": strProps("Snapshot name", true),
|
||||
"desc": strProps("Snapshot description", false),
|
||||
"vmstate": boolProps("Include running state (RAM). Default: false", false),
|
||||
"kind": strProps("Guest type", false, "qemu", "lxc"),
|
||||
"confirm": strProps("Set to \"true\" to confirm", true, "true"),
|
||||
}, []string{"node", "vmid", "name", "confirm"}),
|
||||
}, vmPatterns, snapshotCreateHandler)
|
||||
|
||||
registerPatternTool(s, &mcp.Tool{
|
||||
Name: "snapshot_delete",
|
||||
Description: "Delete a snapshot. Requires confirm + write permission.",
|
||||
InputSchema: schema(map[string]any{
|
||||
"node": strProps("Node name", true),
|
||||
"vmid": strProps("VM/CT ID", true),
|
||||
"name": strProps("Snapshot name", true),
|
||||
"kind": strProps("Guest type", false, "qemu", "lxc"),
|
||||
"confirm": strProps("Set to \"true\" to confirm permanent deletion", true, "true"),
|
||||
}, []string{"node", "vmid", "name", "confirm"}),
|
||||
}, vmPatterns, snapshotDeleteHandler)
|
||||
|
||||
registerPatternTool(s, &mcp.Tool{
|
||||
Name: "snapshot_rollback",
|
||||
Description: "Roll back a VM/CT to a snapshot (restores disk/state). Requires confirm + write permission.",
|
||||
InputSchema: schema(map[string]any{
|
||||
"node": strProps("Node name", true),
|
||||
"vmid": strProps("VM/CT ID", true),
|
||||
"name": strProps("Snapshot name", true),
|
||||
"kind": strProps("Guest type", false, "qemu", "lxc"),
|
||||
"confirm": strProps("Set to \"true\" to confirm destructive rollback", true, "true"),
|
||||
}, []string{"node", "vmid", "name", "confirm"}),
|
||||
}, vmPatterns, snapshotRollbackHandler)
|
||||
}
|
||||
|
||||
func snapshotListHandler(ctx context.Context, req *mcp.CallToolRequest) (*mcp.CallToolResult, error) {
|
||||
t, err := tenantFor(ctx, req)
|
||||
if err != nil {
|
||||
return errorResult(err.Error()), nil
|
||||
}
|
||||
args := requestArgs(req)
|
||||
node, vmid, host, r := resolveVMArgs(t, args)
|
||||
if r != nil {
|
||||
return r, nil
|
||||
}
|
||||
kind := guestKind(args)
|
||||
cctx, cancel := timeout(ctx, t)
|
||||
defer cancel()
|
||||
data, err := t.SnapshotList(cctx, host, node, kind, vmid)
|
||||
if err != nil {
|
||||
return errorResult(err.Error()), nil
|
||||
}
|
||||
return textResult(pretty(data)), nil
|
||||
}
|
||||
|
||||
func snapshotCreateHandler(ctx context.Context, req *mcp.CallToolRequest) (*mcp.CallToolResult, error) {
|
||||
t, err := tenantFor(ctx, req)
|
||||
if err != nil {
|
||||
return errorResult(err.Error()), nil
|
||||
}
|
||||
args := requestArgs(req)
|
||||
node, vmid, host, r := resolveVMArgs(t, args)
|
||||
if r != nil {
|
||||
return r, nil
|
||||
}
|
||||
if err := confirm(args, "snapshot_create"); err != nil {
|
||||
return errorResult(err.Error()), nil
|
||||
}
|
||||
if err := gateVMWrite(t, host, vmid); err != nil {
|
||||
return errorResult(err.Error()), nil
|
||||
}
|
||||
name, err := requireName(args, "name")
|
||||
if err != nil {
|
||||
return errorResult(err.Error()), nil
|
||||
}
|
||||
form := url.Values{"snapname": {name}}
|
||||
if d := getString(args, "desc", ""); d != "" {
|
||||
form.Set("description", d)
|
||||
}
|
||||
if getBool(args, "vmstate", false) {
|
||||
form.Set("vmstate", "1")
|
||||
}
|
||||
upid, err := t.SnapshotCreate(ctx, host, node, guestKind(args), vmid, form)
|
||||
if err != nil {
|
||||
return errorResult(err.Error()), nil
|
||||
}
|
||||
return textResult(upidMsg("snapshot_create", fmt.Sprintf("%s/%d %s", node, vmid, name), upid)), nil
|
||||
}
|
||||
|
||||
func snapshotDeleteHandler(ctx context.Context, req *mcp.CallToolRequest) (*mcp.CallToolResult, error) {
|
||||
t, err := tenantFor(ctx, req)
|
||||
if err != nil {
|
||||
return errorResult(err.Error()), nil
|
||||
}
|
||||
args := requestArgs(req)
|
||||
node, vmid, host, r := resolveVMArgs(t, args)
|
||||
if r != nil {
|
||||
return r, nil
|
||||
}
|
||||
if err := confirm(args, "snapshot_delete"); err != nil {
|
||||
return errorResult(err.Error()), nil
|
||||
}
|
||||
if err := gateVMWrite(t, host, vmid); err != nil {
|
||||
return errorResult(err.Error()), nil
|
||||
}
|
||||
name, err := requireName(args, "name")
|
||||
if err != nil {
|
||||
return errorResult(err.Error()), nil
|
||||
}
|
||||
upid, err := t.SnapshotDelete(ctx, host, node, guestKind(args), vmid, name)
|
||||
if err != nil {
|
||||
return errorResult(err.Error()), nil
|
||||
}
|
||||
return textResult(upidMsg("snapshot_delete", fmt.Sprintf("%s/%d %s", node, vmid, name), upid)), nil
|
||||
}
|
||||
|
||||
func snapshotRollbackHandler(ctx context.Context, req *mcp.CallToolRequest) (*mcp.CallToolResult, error) {
|
||||
t, err := tenantFor(ctx, req)
|
||||
if err != nil {
|
||||
return errorResult(err.Error()), nil
|
||||
}
|
||||
args := requestArgs(req)
|
||||
node, vmid, host, r := resolveVMArgs(t, args)
|
||||
if r != nil {
|
||||
return r, nil
|
||||
}
|
||||
if err := confirm(args, "snapshot_rollback"); err != nil {
|
||||
return errorResult(err.Error()), nil
|
||||
}
|
||||
if err := gateVMWrite(t, host, vmid); err != nil {
|
||||
return errorResult(err.Error()), nil
|
||||
}
|
||||
name, err := requireName(args, "name")
|
||||
if err != nil {
|
||||
return errorResult(err.Error()), nil
|
||||
}
|
||||
upid, err := t.SnapshotRollback(ctx, host, node, guestKind(args), vmid, name)
|
||||
if err != nil {
|
||||
return errorResult(err.Error()), nil
|
||||
}
|
||||
return textResult(upidMsg("snapshot_rollback", fmt.Sprintf("%s/%d %s", node, vmid, name), upid)), nil
|
||||
}
|
||||
|
||||
// guestKind возвращает тип гостя (qemu/lxc) из аргумента, по умолчанию qemu.
|
||||
func guestKind(args map[string]any) string {
|
||||
k := getString(args, "kind", vmTypeQEMU)
|
||||
if k != vmTypeQEMU && k != vmTypeLXC {
|
||||
return vmTypeQEMU
|
||||
}
|
||||
return k
|
||||
}
|
||||
Reference in New Issue
Block a user