package tools import ( "context" "fmt" "net/url" "github.com/modelcontextprotocol/go-sdk/mcp" ) // backup.go — бэкапы (vzdump): list (read) и create (async UPID). // В v1 НЕ реализованы restore/delete/prune: это тяжёлые необратимые // операции, требующие выверенной семантики; их лучше сделать отдельным // модулем-уточнением, чем рисковать в базовом. (см. README «Roadmap».) func registerBackupTools(s *mcp.Server) { s.AddTool(&mcp.Tool{ Name: "backup_list", Description: "List recent backup (vzdump) tasks. Read-only.", InputSchema: schema(map[string]any{ "node": strProps("Node name (empty = whole cluster)", false), "host": strProps("Cluster alias (default: primary)", false), }, nil), }, backupListHandler) registerPatternTool(s, &mcp.Tool{ Name: "backup_create", Description: "Create a backup (vzdump) of a VM/CT. Async — returns UPID; poll via task_status(wait). Requires confirm + write permission.", InputSchema: schema(map[string]any{ "node": strProps("Node name", true), "vmid": strProps("VM/CT ID", true), "storage": strProps("Backup storage (default: node local)", false), "mode": strProps("Backup mode", true, "snapshot", "suspend", "stop"), "compress": strProps("Compression", false, "zstd", "gzip", "lzo", "none"), "kind": strProps("Guest type", false, "qemu", "lxc"), "confirm": strProps("Set to \"true\" to confirm", true, "true"), }, []string{"node", "vmid", "confirm"}), }, vmPatterns, backupCreateHandler) } func backupListHandler(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) host, err := resolveHost(t, args) if err != nil { return errorResult(err.Error()), nil } cctx, cancel := timeout(ctx, t) defer cancel() data, err := t.BackupList(cctx, host, getString(args, "node", "")) if err != nil { return errorResult(err.Error()), nil } return textResult(pretty(data)), nil } func backupCreateHandler(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, "backup_create"); err != nil { return errorResult(err.Error()), nil } if err := gateVMWrite(t, host, vmid); err != nil { return errorResult(err.Error()), nil } kind := guestKind(args) // mode: snapshot/suspend/stop. Если задан storage/compress — кладём в form. form := url.Values{"mode": {getString(args, "mode", "snapshot")}} if s := getString(args, "storage", ""); s != "" { form.Set("storage", s) } if c := getString(args, "compress", ""); c != "" && c != "none" { form.Set("compress", c) } upid, err := t.BackupCreate(ctx, host, node, kind, vmid, form) if err != nil { return errorResult(err.Error()), nil } return textResult(upidMsg("backup_create", fmt.Sprintf("%s/%d", node, vmid), upid)), nil }