mirror of
https://hubproxy.babadafafafafa.cn/https://github.com/yunionio/cloudpods.git
synced 2026-09-20 08:03:53 +08:00
* feat(mcp-server): 增加mcclient sdk适配器结构体以及对应的认证方法 * feat(mcp-server): 增加资源查询的sdk适配器方法 * feat(mcp-server): 增加资源操作的sdk适配器方法 * feat(mcp-server): 增加区域资源查询工具 * feat(mcp-server): 增加网络资源查询工具 * feat(mcp-server): 增加镜像资源查询工具 * feat(mcp-server): 增加虚拟机资源查询工具 * feat(mcp-server): 增加vpc资源查询工具 * feat(mcp-server): 增加存储资源查询工具 * feat(mcp-server): 增加套餐资源查询工具 * feat(mcp-server): 增加虚拟机创建工具 * feat(mcp-server): 增加虚拟机监控工具 * feat(mcp-server): 增加虚拟机操作工具,包括启动、重启、停止、重置密码和删除 * optimize(mcp-server): 增加工具函数接口定义 * feat(mcp-server): 增加工具函数所使用的结构体模型 * feat(mcp-server): 新增工具统一注册中心 * feat(mcp-server): 增加mcp服务中心 * feat(mcp-server): 增加统一配置中心 * feat(mcp-server): 增加服务启动主入口 * doc(mcp-server): 增加mcp-server相关的说明,安装和使用文档 * fix(mcp-server): 更正文档位置以及补充图片 * refactor(mcp-server): 修正了service以及配置解析的逻辑 * refactor(mcp-server): 将日志打印相关代码改成使用log * feat(mcp-server): 增加mcclient sdk适配器结构体以及对应的认证方法 * feat(mcp-server): 增加资源查询的sdk适配器方法 * feat(mcp-server): 增加资源操作的sdk适配器方法 * feat(mcp-server): 增加区域资源查询工具 * feat(mcp-server): 增加网络资源查询工具 * feat(mcp-server): 增加镜像资源查询工具 * feat(mcp-server): 增加虚拟机资源查询工具 * feat(mcp-server): 增加vpc资源查询工具 * feat(mcp-server): 增加存储资源查询工具 * feat(mcp-server): 增加套餐资源查询工具 * feat(mcp-server): 增加虚拟机创建工具 * feat(mcp-server): 增加虚拟机监控工具 * feat(mcp-server): 增加虚拟机操作工具,包括启动、重启、停止、重置密码和删除 * optimize(mcp-server): 增加工具函数接口定义 * feat(mcp-server): 增加工具函数所使用的结构体模型 * feat(mcp-server): 新增工具统一注册中心 * feat(mcp-server): 增加mcp服务中心 * feat(mcp-server): 增加统一配置中心 * feat(mcp-server): 增加服务启动主入口 * doc(mcp-server): 增加mcp-server相关的说明,安装和使用文档 * fix(mcp-server): 更正文档位置以及补充图片 * refactor(mcp-server): 修正了service以及配置解析的逻辑 * refactor(mcp-server): 将日志打印相关代码改成使用log * fix(mcp-server): 修复依赖导入以及缺失等问题 * refactor(mcp-server): 复用common_options * fix: 修复配置结构体字段重复的问题 * doc(mcp-server): 更正文档错误 * style(mcp-server): 格式化import顺序 * style(mcp-server): 格式化import导入 * style(mcp-server): 规范import语句 * doc(mcp-server): 给目录生成doc文件 --------- Co-authored-by: 屈轩 <qu_xuan@icloud.com>
351 lines
13 KiB
Go
351 lines
13 KiB
Go
// Copyright 2019 Yunion
|
||
//
|
||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||
// you may not use this file except in compliance with the License.
|
||
// You may obtain a copy of the License at
|
||
//
|
||
// http://www.apache.org/licenses/LICENSE-2.0
|
||
//
|
||
// Unless required by applicable law or agreed to in writing, software
|
||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||
// See the License for the specific language governing permissions and
|
||
// limitations under the License.
|
||
|
||
package tools
|
||
|
||
import (
|
||
"context"
|
||
"encoding/json"
|
||
"fmt"
|
||
"strconv"
|
||
"strings"
|
||
|
||
"github.com/mark3labs/mcp-go/mcp"
|
||
|
||
"yunion.io/x/log"
|
||
|
||
"yunion.io/x/onecloud/pkg/mcp-server/adapters"
|
||
"yunion.io/x/onecloud/pkg/mcp-server/models"
|
||
)
|
||
|
||
// CloudpodsServerCreateTool 用于创建Cloudpods虚拟机实例
|
||
type CloudpodsServerCreateTool struct {
|
||
// adapter 用于与 Cloudpods API 进行交互
|
||
adapter *adapters.CloudpodsAdapter
|
||
}
|
||
|
||
// NewCloudpodsServerCreateTool 创建一个新的CloudpodsServerCreateTool实例
|
||
// adapter: 用于与Cloudpods API交互的适配器
|
||
// 返回值: CloudpodsServerCreateTool实例指针
|
||
func NewCloudpodsServerCreateTool(adapter *adapters.CloudpodsAdapter) *CloudpodsServerCreateTool {
|
||
return &CloudpodsServerCreateTool{
|
||
adapter: adapter,
|
||
}
|
||
}
|
||
|
||
// GetTool 定义并返回创建虚拟机工具的元数据
|
||
// 该工具用于创建Cloudpods虚拟机实例,支持指定各种配置参数
|
||
// name: 虚拟机名称 (必填)
|
||
// vcpu_count: CPU核心数 (必填)
|
||
// vmem_size: 内存大小(MB) (必填)
|
||
// image_id: 镜像ID (必填)
|
||
// disk_size: 系统盘大小(GB),不指定则使用镜像默认大小
|
||
// network_id: 网络ID (必填)
|
||
// serversku_id: 套餐ID,指定后将忽略vcpu_count和vmem_size参数
|
||
// password: 虚拟机密码,长度8-30个字符
|
||
// count: 创建数量,默认为1
|
||
// auto_start: 是否自动启动,默认为true
|
||
// billing_type: 计费类型,例如:postpaid、prepaid
|
||
// duration: 包年包月时长,例如:1M、1Y
|
||
// description: 描述信息
|
||
// hostname: 主机名
|
||
// hypervisor: 虚拟化技术,如kvm, esxi等,默认为kvm
|
||
// metadata: 标签列表,格式为JSON字符串,例如:{"key1":"value1","key2":"value2"}
|
||
// secgroup_id: 安全组ID
|
||
// secgroups: 安全组ID列表,多个ID用逗号分隔
|
||
// user_data: 用户自定义启动脚本
|
||
// keypair_id: 秘钥对ID
|
||
// project_id: 项目ID
|
||
// zone_id: 可用区ID
|
||
// region_id: 区域ID
|
||
// disable_delete: 是否开启删除保护,默认为true
|
||
// boot_order: 启动顺序,如cdn
|
||
// data_disks: 数据盘配置,格式为JSON字符串数组,例如:[{"size":100,"disk_type":"data"}]
|
||
// ak: 用户登录cloudpods后获取的access key
|
||
// sk: 用户登录cloudpods后获取的secret key
|
||
func (c *CloudpodsServerCreateTool) GetTool() mcp.Tool {
|
||
return mcp.NewTool(
|
||
"cloudpods_create_server",
|
||
mcp.WithDescription("创建Cloudpods虚拟机实例"),
|
||
mcp.WithString("name", mcp.Required(), mcp.Description("虚拟机名称")),
|
||
mcp.WithString("vcpu_count", mcp.Required(), mcp.Description("CPU核心数")),
|
||
mcp.WithString("vmem_size", mcp.Required(), mcp.Description("内存大小(MB)")),
|
||
mcp.WithString("image_id", mcp.Required(), mcp.Description("镜像ID")),
|
||
mcp.WithString("disk_size", mcp.Description("系统盘大小(GB),不指定则使用镜像默认大小")),
|
||
mcp.WithString("network_id", mcp.Required(), mcp.Description("网络ID")),
|
||
mcp.WithString("serversku_id", mcp.Description("套餐ID,指定后将忽略vcpu_count和vmem_size参数")),
|
||
mcp.WithString("password", mcp.Description("虚拟机密码,长度8-30个字符")),
|
||
mcp.WithString("count", mcp.Description("创建数量,默认为1")),
|
||
mcp.WithString("auto_start", mcp.Description("是否自动启动,默认为true")),
|
||
mcp.WithString("billing_type", mcp.Description("计费类型,例如:postpaid、prepaid")),
|
||
mcp.WithString("duration", mcp.Description("包年包月时长,例如:1M、1Y")),
|
||
mcp.WithString("description", mcp.Description("描述信息")),
|
||
mcp.WithString("hostname", mcp.Description("主机名")),
|
||
mcp.WithString("hypervisor", mcp.Description("虚拟化技术,如kvm, esxi等,默认为kvm")),
|
||
mcp.WithString("metadata", mcp.Description("标签列表,格式为JSON字符串,例如:{\"key1\":\"value1\",\"key2\":\"value2\"}")),
|
||
mcp.WithString("secgroup_id", mcp.Description("安全组ID")),
|
||
mcp.WithString("secgroups", mcp.Description("安全组ID列表,多个ID用逗号分隔")),
|
||
mcp.WithString("user_data", mcp.Description("用户自定义启动脚本")),
|
||
mcp.WithString("keypair_id", mcp.Description("秘钥对ID")),
|
||
mcp.WithString("project_id", mcp.Description("项目ID")),
|
||
mcp.WithString("zone_id", mcp.Description("可用区ID")),
|
||
mcp.WithString("region_id", mcp.Description("区域ID")),
|
||
mcp.WithString("disable_delete", mcp.Description("是否开启删除保护,默认为true")),
|
||
mcp.WithString("boot_order", mcp.Description("启动顺序,如cdn")),
|
||
mcp.WithString("data_disks", mcp.Description("数据盘配置,格式为JSON字符串数组,例如:[{\"size\":100,\"disk_type\":\"data\"}]")),
|
||
mcp.WithString("ak", mcp.Description("用户登录cloudpods后获取的access key")),
|
||
mcp.WithString("sk", mcp.Description("用户登录cloudpods后获取的secret key")),
|
||
)
|
||
}
|
||
|
||
// Handle 处理创建虚拟机的请求
|
||
// ctx: 上下文,用于控制请求的生命周期
|
||
// req: 包含创建虚拟机所需参数的请求对象
|
||
// 返回值: 包含创建结果的工具结果对象或错误信息
|
||
func (c *CloudpodsServerCreateTool) Handle(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolResult, error) {
|
||
// 获取必填参数:虚拟机名称
|
||
name, err := req.RequireString("name")
|
||
if err != nil {
|
||
return nil, err
|
||
}
|
||
|
||
// 获取必填参数:镜像ID
|
||
imageID, err := req.RequireString("image_id")
|
||
if err != nil {
|
||
return nil, err
|
||
}
|
||
|
||
// 获取必填参数:网络ID
|
||
networkID, err := req.RequireString("network_id")
|
||
if err != nil {
|
||
return nil, err
|
||
}
|
||
|
||
// 获取必填参数:CPU核心数并转换为整数
|
||
vcpuCountStr, err := req.RequireString("vcpu_count")
|
||
if err != nil {
|
||
return nil, err
|
||
}
|
||
vcpuCount, err := strconv.ParseInt(vcpuCountStr, 10, 64)
|
||
if err != nil {
|
||
return nil, fmt.Errorf("无效的CPU核心数: %s", vcpuCountStr)
|
||
}
|
||
|
||
// 获取必填参数:内存大小并转换为整数
|
||
vmemSizeStr, err := req.RequireString("vmem_size")
|
||
if err != nil {
|
||
return nil, err
|
||
}
|
||
vmemSize, err := strconv.ParseInt(vmemSizeStr, 10, 64)
|
||
if err != nil {
|
||
return nil, fmt.Errorf("无效的内存大小: %s", vmemSizeStr)
|
||
}
|
||
|
||
// 获取可选参数:套餐ID
|
||
serverSkuID := req.GetString("serversku_id", "")
|
||
|
||
// 获取可选参数:磁盘大小,如果指定则转换为整数
|
||
diskSize := int64(0)
|
||
if diskSizeStr := req.GetString("disk_size", ""); diskSizeStr != "" {
|
||
if parsedSize, err := strconv.ParseInt(diskSizeStr, 10, 64); err == nil && parsedSize > 0 {
|
||
diskSize = parsedSize
|
||
}
|
||
}
|
||
|
||
// 获取可选参数:虚拟机密码,并验证长度
|
||
password := req.GetString("password", "")
|
||
if password != "" && (len(password) < 8 || len(password) > 30) {
|
||
return nil, fmt.Errorf("密码长度必须在8-30个字符之间")
|
||
}
|
||
|
||
// 获取可选参数:创建数量,默认为1
|
||
count := 1
|
||
if countStr := req.GetString("count", "1"); countStr != "1" {
|
||
if parsedCount, err := strconv.Atoi(countStr); err == nil && parsedCount > 0 {
|
||
count = parsedCount
|
||
}
|
||
}
|
||
|
||
// 获取可选参数:是否自动启动,默认为true
|
||
autoStart := true
|
||
if autoStartStr := req.GetString("auto_start", "true"); autoStartStr == "false" {
|
||
autoStart = false
|
||
}
|
||
|
||
// 获取可选参数:是否开启删除保护,默认为true
|
||
disableDelete := true
|
||
if disableDeleteStr := req.GetString("disable_delete", "true"); disableDeleteStr == "false" {
|
||
disableDelete = false
|
||
}
|
||
|
||
// 获取其他可选参数
|
||
billingType := req.GetString("billing_type", "")
|
||
duration := req.GetString("duration", "")
|
||
description := req.GetString("description", "")
|
||
hostname := req.GetString("hostname", "")
|
||
hypervisor := req.GetString("hypervisor", "")
|
||
secgroupID := req.GetString("secgroup_id", "")
|
||
userData := req.GetString("user_data", "")
|
||
keypairID := req.GetString("keypair_id", "")
|
||
projectID := req.GetString("project_id", "")
|
||
zoneID := req.GetString("zone_id", "")
|
||
regionID := req.GetString("region_id", "")
|
||
bootOrder := req.GetString("boot_order", "")
|
||
|
||
// 获取安全组ID列表,并按逗号分割
|
||
var secgroups []string
|
||
if secgroupsStr := req.GetString("secgroups", ""); secgroupsStr != "" {
|
||
secgroups = strings.Split(secgroupsStr, ",")
|
||
}
|
||
|
||
// 解析元数据JSON字符串
|
||
metadata := make(map[string]string)
|
||
if metadataStr := req.GetString("metadata", ""); metadataStr != "" {
|
||
if err := json.Unmarshal([]byte(metadataStr), &metadata); err != nil {
|
||
return nil, fmt.Errorf("无效的元数据JSON格式: %w", err)
|
||
}
|
||
}
|
||
|
||
// 解析数据盘配置JSON数组
|
||
var dataDisks []models.DiskConfig
|
||
if dataDisksStr := req.GetString("data_disks", ""); dataDisksStr != "" {
|
||
if err := json.Unmarshal([]byte(dataDisksStr), &dataDisks); err != nil {
|
||
return nil, fmt.Errorf("无效的数据盘配置JSON格式: %w", err)
|
||
}
|
||
}
|
||
|
||
// 构造创建虚拟机的请求对象
|
||
createRequest := models.CreateServerRequest{
|
||
Name: name,
|
||
VcpuCount: vcpuCount,
|
||
VmemSize: vmemSize,
|
||
ImageId: imageID,
|
||
DiskSize: diskSize,
|
||
NetworkId: networkID,
|
||
ServerskuId: serverSkuID,
|
||
Count: count,
|
||
Password: password,
|
||
AutoStart: autoStart,
|
||
BillingType: billingType,
|
||
Duration: duration,
|
||
Description: description,
|
||
Hostname: hostname,
|
||
Hypervisor: hypervisor,
|
||
Metadata: metadata,
|
||
SecgroupId: secgroupID,
|
||
Secgroups: secgroups,
|
||
UserData: userData,
|
||
KeypairId: keypairID,
|
||
ProjectId: projectID,
|
||
ZoneId: zoneID,
|
||
RegionId: regionID,
|
||
DisableDelete: disableDelete,
|
||
BootOrder: bootOrder,
|
||
DataDisks: dataDisks,
|
||
}
|
||
|
||
// 获取访问凭证
|
||
ak := req.GetString("ak", "")
|
||
sk := req.GetString("sk", "")
|
||
|
||
// 调用适配器创建虚拟机
|
||
response, err := c.adapter.CreateServer(ctx, createRequest, ak, sk)
|
||
if err != nil {
|
||
log.Errorf("Fail to create server: %s", err)
|
||
return nil, fmt.Errorf("fail to create server: %w", err)
|
||
}
|
||
|
||
// 格式化创建结果
|
||
formattedResult := c.formatCreateResult(response, &createRequest)
|
||
|
||
// 将结果序列化为JSON格式
|
||
resultJSON, err := json.MarshalIndent(formattedResult, "", " ")
|
||
if err != nil {
|
||
log.Errorf("Fail to serialize result: %s", err)
|
||
return nil, fmt.Errorf("fail to serialize result: %w", err)
|
||
}
|
||
|
||
// 返回格式化后的结果
|
||
return mcp.NewToolResultText(string(resultJSON)), nil
|
||
}
|
||
|
||
// GetName 返回工具的名称标识符
|
||
// 返回值: 工具名称字符串,用于唯一标识该工具
|
||
func (c *CloudpodsServerCreateTool) GetName() string {
|
||
return "cloudpods_create_server"
|
||
}
|
||
|
||
// formatCreateResult 格式化创建虚拟机的响应结果
|
||
// response: 原始的创建虚拟机响应数据
|
||
// request: 原始的创建虚拟机请求数据
|
||
// 返回值: 格式化后的结果,包含创建信息、结果详情和摘要
|
||
func (c *CloudpodsServerCreateTool) formatCreateResult(response *models.CreateServerResponse, request *models.CreateServerRequest) map[string]interface{} {
|
||
// 初始化格式化结果结构
|
||
formatted := map[string]interface{}{
|
||
// 创建请求的基本信息
|
||
"create_info": map[string]interface{}{
|
||
"name": request.Name,
|
||
"vcpu_count": request.VcpuCount,
|
||
"vmem_size": request.VmemSize,
|
||
"image_id": request.ImageId,
|
||
"disk_size": request.DiskSize,
|
||
"network_id": request.NetworkId,
|
||
"serversku_id": request.ServerskuId,
|
||
"count": request.Count,
|
||
"auto_start": request.AutoStart,
|
||
"billing_type": request.BillingType,
|
||
"duration": request.Duration,
|
||
"description": request.Description,
|
||
"hostname": request.Hostname,
|
||
"hypervisor": request.Hypervisor,
|
||
"secgroup_id": request.SecgroupId,
|
||
"keypair_id": request.KeypairId,
|
||
"project_id": request.ProjectId,
|
||
"zone_id": request.ZoneId,
|
||
"region_id": request.RegionId,
|
||
"disable_delete": request.DisableDelete,
|
||
"boot_order": request.BootOrder,
|
||
},
|
||
// 创建响应的结果信息
|
||
"result": map[string]interface{}{
|
||
"status": response.Status,
|
||
"message": response.Message,
|
||
"servers": make([]map[string]interface{}, 0, len(response.Data.Servers)),
|
||
},
|
||
}
|
||
|
||
// 遍历创建的虚拟机列表,构造每个虚拟机的详细信息
|
||
for _, server := range response.Data.Servers {
|
||
serverInfo := map[string]interface{}{
|
||
"id": server.ID,
|
||
"name": server.Name,
|
||
"status": server.Status,
|
||
"task_id": server.TaskID,
|
||
}
|
||
formatted["result"].(map[string]interface{})["servers"] = append(
|
||
formatted["result"].(map[string]interface{})["servers"].([]map[string]interface{}),
|
||
serverInfo,
|
||
)
|
||
}
|
||
|
||
// 构造摘要信息
|
||
formatted["summary"] = map[string]interface{}{
|
||
"requested_count": request.Count, // 请求创建的虚拟机数量
|
||
"created_count": len(response.Data.Servers), // 实际创建的虚拟机数量
|
||
"success": response.Status == 200, // 创建是否成功
|
||
}
|
||
|
||
return formatted
|
||
}
|