162 lines
5.8 KiB
Go
162 lines
5.8 KiB
Go
package tools
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
"net/url"
|
|
"strings"
|
|
|
|
"github.com/modelcontextprotocol/go-sdk/mcp"
|
|
)
|
|
|
|
// vm_net.go — сетевые интерфейсы ВМ (net0..netN): добавить/обновить/удалить.
|
|
// Это структурные изменения конфига → write gate + confirm + probe. Модель
|
|
// задаёт iface (net0), модель/бридж; guard валидирует имя интерфейса.
|
|
|
|
func registerVMNetTools(s *mcp.Server) {
|
|
registerPatternTool(s, &mcp.Tool{
|
|
Name: "vm_add_network",
|
|
Description: "Add a network interface (netN) to a VM, e.g. net0 via vmbr0. Requires confirm + write permission.",
|
|
InputSchema: schema(map[string]any{
|
|
"node": strProps("Node name", true),
|
|
"vmid": strProps("VM ID", true),
|
|
"iface": strProps("Interface slot (e.g. net0)", true),
|
|
"model": strProps("NIC model", true, "virtio", "e1000", "vmxnet3", "rtl8139"),
|
|
"bridge": strProps("Bridge (e.g. vmbr0)", true),
|
|
"mac": strProps("MAC (empty = auto)", false),
|
|
"firewall": boolProps("Enable firewall (default: true)", false),
|
|
"confirm": strProps("Set to \"true\" to confirm", true, "true"),
|
|
}, []string{"node", "vmid", "iface", "bridge", "confirm"}),
|
|
}, vmPatterns, vmAddNetworkHandler)
|
|
|
|
registerPatternTool(s, &mcp.Tool{
|
|
Name: "vm_update_network",
|
|
Description: "Update an existing network interface (model/bridge/mac/firewall). Requires confirm + write permission.",
|
|
InputSchema: schema(map[string]any{
|
|
"node": strProps("Node name", true),
|
|
"vmid": strProps("VM ID", true),
|
|
"iface": strProps("Interface slot (e.g. net0)", true),
|
|
"model": strProps("NIC model", false, "virtio", "e1000", "vmxnet3", "rtl8139"),
|
|
"bridge": strProps("Bridge (e.g. vmbr0)", false),
|
|
"mac": strProps("MAC address", false),
|
|
"firewall": boolProps("Enable firewall (default: true)", false),
|
|
"confirm": strProps("Set to \"true\" to confirm", true, "true"),
|
|
}, []string{"node", "vmid", "iface", "confirm"}),
|
|
}, vmPatterns, vmUpdateNetworkHandler)
|
|
|
|
registerPatternTool(s, &mcp.Tool{
|
|
Name: "vm_remove_network",
|
|
Description: "Remove a network interface from a VM. Requires confirm + write permission.",
|
|
InputSchema: schema(map[string]any{
|
|
"node": strProps("Node name", true),
|
|
"vmid": strProps("VM ID", true),
|
|
"iface": strProps("Interface slot (e.g. net0)", true),
|
|
"confirm": strProps("Set to \"true\" to confirm", true, "true"),
|
|
}, []string{"node", "vmid", "iface", "confirm"}),
|
|
}, vmPatterns, vmRemoveNetworkHandler)
|
|
}
|
|
|
|
func vmAddNetworkHandler(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, "vm_add_network"); err != nil {
|
|
return errorResult(err.Error()), nil
|
|
}
|
|
if err := gateVMWrite(t, host, vmid); err != nil {
|
|
return errorResult(err.Error()), nil
|
|
}
|
|
iface, err := requireName(args, "iface")
|
|
if err != nil {
|
|
return errorResult(err.Error()), nil
|
|
}
|
|
model := getString(args, "model", "virtio")
|
|
bridge := getString(args, "bridge", "")
|
|
if bridge == "" {
|
|
return errorResult("'bridge' is required"), nil
|
|
}
|
|
value := fmt.Sprintf("%s,bridge=%s", model, bridge)
|
|
if mac := getString(args, "mac", ""); mac != "" {
|
|
value += ",mac=" + mac
|
|
}
|
|
if getBool(args, "firewall", true) {
|
|
value += ",firewall=1"
|
|
}
|
|
upid, err := t.ConfigPost(ctx, host, node, vmTypeQEMU, vmid, url.Values{iface: {value}})
|
|
if err != nil {
|
|
return errorResult(err.Error()), nil
|
|
}
|
|
return textResult(upidMsg("vm_add_network", fmt.Sprintf("%s/%d %s", node, vmid, iface), upid)), nil
|
|
}
|
|
|
|
func vmUpdateNetworkHandler(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, "vm_update_network"); err != nil {
|
|
return errorResult(err.Error()), nil
|
|
}
|
|
if err := gateVMWrite(t, host, vmid); err != nil {
|
|
return errorResult(err.Error()), nil
|
|
}
|
|
iface, err := requireName(args, "iface")
|
|
if err != nil {
|
|
return errorResult(err.Error()), nil
|
|
}
|
|
// update перезаписывает весь сетевой спецификатор: собираем из явных полей.
|
|
parts := []string{getString(args, "model", "virtio")}
|
|
if b := getString(args, "bridge", ""); b != "" {
|
|
parts = append(parts, "bridge="+b)
|
|
}
|
|
if m := getString(args, "mac", ""); m != "" {
|
|
parts = append(parts, "mac="+m)
|
|
}
|
|
if getBool(args, "firewall", true) {
|
|
parts = append(parts, "firewall=1")
|
|
}
|
|
upid, err := t.ConfigPost(ctx, host, node, vmTypeQEMU, vmid, url.Values{iface: {strings.Join(parts, ",")}})
|
|
if err != nil {
|
|
return errorResult(err.Error()), nil
|
|
}
|
|
return textResult(upidMsg("vm_update_network", fmt.Sprintf("%s/%d %s", node, vmid, iface), upid)), nil
|
|
}
|
|
|
|
func vmRemoveNetworkHandler(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, "vm_remove_network"); err != nil {
|
|
return errorResult(err.Error()), nil
|
|
}
|
|
if err := gateVMWrite(t, host, vmid); err != nil {
|
|
return errorResult(err.Error()), nil
|
|
}
|
|
iface, err := requireName(args, "iface")
|
|
if err != nil {
|
|
return errorResult(err.Error()), nil
|
|
}
|
|
upid, err := t.ConfigPost(ctx, host, node, vmTypeQEMU, vmid, url.Values{"delete": {iface}})
|
|
if err != nil {
|
|
return errorResult(err.Error()), nil
|
|
}
|
|
return textResult(upidMsg("vm_remove_network", fmt.Sprintf("%s/%d %s", node, vmid, iface), upid)), nil
|
|
}
|