cloudpods_server_create_tool.go 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350
  1. // Copyright 2019 Yunion
  2. //
  3. // Licensed under the Apache License, Version 2.0 (the "License");
  4. // you may not use this file except in compliance with the License.
  5. // You may obtain a copy of the License at
  6. //
  7. // http://www.apache.org/licenses/LICENSE-2.0
  8. //
  9. // Unless required by applicable law or agreed to in writing, software
  10. // distributed under the License is distributed on an "AS IS" BASIS,
  11. // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  12. // See the License for the specific language governing permissions and
  13. // limitations under the License.
  14. package tools
  15. import (
  16. "context"
  17. "encoding/json"
  18. "fmt"
  19. "strconv"
  20. "strings"
  21. "github.com/mark3labs/mcp-go/mcp"
  22. "yunion.io/x/log"
  23. "yunion.io/x/onecloud/pkg/mcp-server/adapters"
  24. "yunion.io/x/onecloud/pkg/mcp-server/models"
  25. )
  26. // CloudpodsServerCreateTool 用于创建Cloudpods虚拟机实例
  27. type CloudpodsServerCreateTool struct {
  28. // adapter 用于与 Cloudpods API 进行交互
  29. adapter *adapters.CloudpodsAdapter
  30. }
  31. // NewCloudpodsServerCreateTool 创建一个新的CloudpodsServerCreateTool实例
  32. // adapter: 用于与Cloudpods API交互的适配器
  33. // 返回值: CloudpodsServerCreateTool实例指针
  34. func NewCloudpodsServerCreateTool(adapter *adapters.CloudpodsAdapter) *CloudpodsServerCreateTool {
  35. return &CloudpodsServerCreateTool{
  36. adapter: adapter,
  37. }
  38. }
  39. // GetTool 定义并返回创建虚拟机工具的元数据
  40. // 该工具用于创建Cloudpods虚拟机实例,支持指定各种配置参数
  41. // name: 虚拟机名称 (必填)
  42. // vcpu_count: CPU核心数 (必填)
  43. // vmem_size: 内存大小(MB) (必填)
  44. // image_id: 镜像ID (必填)
  45. // disk_size: 系统盘大小(GB),不指定则使用镜像默认大小
  46. // network_id: 网络ID (必填)
  47. // serversku_id: 套餐ID,指定后将忽略vcpu_count和vmem_size参数
  48. // password: 虚拟机密码,长度8-30个字符
  49. // count: 创建数量,默认为1
  50. // auto_start: 是否自动启动,默认为true
  51. // billing_type: 计费类型,例如:postpaid、prepaid
  52. // duration: 包年包月时长,例如:1M、1Y
  53. // description: 描述信息
  54. // hostname: 主机名
  55. // hypervisor: 虚拟化技术,如kvm, esxi等,默认为kvm
  56. // metadata: 标签列表,格式为JSON字符串,例如:{"key1":"value1","key2":"value2"}
  57. // secgroup_id: 安全组ID
  58. // secgroups: 安全组ID列表,多个ID用逗号分隔
  59. // user_data: 用户自定义启动脚本
  60. // keypair_id: 秘钥对ID
  61. // project_id: 项目ID
  62. // zone_id: 可用区ID
  63. // region_id: 区域ID
  64. // disable_delete: 是否开启删除保护,默认为true
  65. // boot_order: 启动顺序,如cdn
  66. // data_disks: 数据盘配置,格式为JSON字符串数组,例如:[{"size":100,"disk_type":"data"}]
  67. // ak: 用户登录cloudpods后获取的access key
  68. // sk: 用户登录cloudpods后获取的secret key
  69. func (c *CloudpodsServerCreateTool) GetTool() mcp.Tool {
  70. return mcp.NewTool(
  71. "cloudpods_create_server",
  72. mcp.WithDescription("创建Cloudpods虚拟机实例"),
  73. mcp.WithString("name", mcp.Required(), mcp.Description("虚拟机名称")),
  74. mcp.WithString("vcpu_count", mcp.Required(), mcp.Description("CPU核心数")),
  75. mcp.WithString("vmem_size", mcp.Required(), mcp.Description("内存大小(MB)")),
  76. mcp.WithString("image_id", mcp.Required(), mcp.Description("镜像ID")),
  77. mcp.WithString("disk_size", mcp.Description("系统盘大小(GB),不指定则使用镜像默认大小")),
  78. mcp.WithString("network_id", mcp.Required(), mcp.Description("网络ID")),
  79. mcp.WithString("serversku_id", mcp.Description("套餐ID,指定后将忽略vcpu_count和vmem_size参数")),
  80. mcp.WithString("password", mcp.Description("虚拟机密码,长度8-30个字符")),
  81. mcp.WithString("count", mcp.Description("创建数量,默认为1")),
  82. mcp.WithString("auto_start", mcp.Description("是否自动启动,默认为true")),
  83. mcp.WithString("billing_type", mcp.Description("计费类型,例如:postpaid、prepaid")),
  84. mcp.WithString("duration", mcp.Description("包年包月时长,例如:1M、1Y")),
  85. mcp.WithString("description", mcp.Description("描述信息")),
  86. mcp.WithString("hostname", mcp.Description("主机名")),
  87. mcp.WithString("hypervisor", mcp.Description("虚拟化技术,如kvm, esxi等,默认为kvm")),
  88. mcp.WithString("metadata", mcp.Description("标签列表,格式为JSON字符串,例如:{\"key1\":\"value1\",\"key2\":\"value2\"}")),
  89. mcp.WithString("secgroup_id", mcp.Description("安全组ID")),
  90. mcp.WithString("secgroups", mcp.Description("安全组ID列表,多个ID用逗号分隔")),
  91. mcp.WithString("user_data", mcp.Description("用户自定义启动脚本")),
  92. mcp.WithString("keypair_id", mcp.Description("秘钥对ID")),
  93. mcp.WithString("project_id", mcp.Description("项目ID")),
  94. mcp.WithString("zone_id", mcp.Description("可用区ID")),
  95. mcp.WithString("region_id", mcp.Description("区域ID")),
  96. mcp.WithString("disable_delete", mcp.Description("是否开启删除保护,默认为true")),
  97. mcp.WithString("boot_order", mcp.Description("启动顺序,如cdn")),
  98. mcp.WithString("data_disks", mcp.Description("数据盘配置,格式为JSON字符串数组,例如:[{\"size\":100,\"disk_type\":\"data\"}]")),
  99. mcp.WithString("ak", mcp.Description("用户登录cloudpods后获取的access key")),
  100. mcp.WithString("sk", mcp.Description("用户登录cloudpods后获取的secret key")),
  101. )
  102. }
  103. // Handle 处理创建虚拟机的请求
  104. // ctx: 上下文,用于控制请求的生命周期
  105. // req: 包含创建虚拟机所需参数的请求对象
  106. // 返回值: 包含创建结果的工具结果对象或错误信息
  107. func (c *CloudpodsServerCreateTool) Handle(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolResult, error) {
  108. // 获取必填参数:虚拟机名称
  109. name, err := req.RequireString("name")
  110. if err != nil {
  111. return nil, err
  112. }
  113. // 获取必填参数:镜像ID
  114. imageID, err := req.RequireString("image_id")
  115. if err != nil {
  116. return nil, err
  117. }
  118. // 获取必填参数:网络ID
  119. networkID, err := req.RequireString("network_id")
  120. if err != nil {
  121. return nil, err
  122. }
  123. // 获取必填参数:CPU核心数并转换为整数
  124. vcpuCountStr, err := req.RequireString("vcpu_count")
  125. if err != nil {
  126. return nil, err
  127. }
  128. vcpuCount, err := strconv.ParseInt(vcpuCountStr, 10, 64)
  129. if err != nil {
  130. return nil, fmt.Errorf("无效的CPU核心数: %s", vcpuCountStr)
  131. }
  132. // 获取必填参数:内存大小并转换为整数
  133. vmemSizeStr, err := req.RequireString("vmem_size")
  134. if err != nil {
  135. return nil, err
  136. }
  137. vmemSize, err := strconv.ParseInt(vmemSizeStr, 10, 64)
  138. if err != nil {
  139. return nil, fmt.Errorf("无效的内存大小: %s", vmemSizeStr)
  140. }
  141. // 获取可选参数:套餐ID
  142. serverSkuID := req.GetString("serversku_id", "")
  143. // 获取可选参数:磁盘大小,如果指定则转换为整数
  144. diskSize := int64(0)
  145. if diskSizeStr := req.GetString("disk_size", ""); diskSizeStr != "" {
  146. if parsedSize, err := strconv.ParseInt(diskSizeStr, 10, 64); err == nil && parsedSize > 0 {
  147. diskSize = parsedSize
  148. }
  149. }
  150. // 获取可选参数:虚拟机密码,并验证长度
  151. password := req.GetString("password", "")
  152. if password != "" && (len(password) < 8 || len(password) > 30) {
  153. return nil, fmt.Errorf("密码长度必须在8-30个字符之间")
  154. }
  155. // 获取可选参数:创建数量,默认为1
  156. count := 1
  157. if countStr := req.GetString("count", "1"); countStr != "1" {
  158. if parsedCount, err := strconv.Atoi(countStr); err == nil && parsedCount > 0 {
  159. count = parsedCount
  160. }
  161. }
  162. // 获取可选参数:是否自动启动,默认为true
  163. autoStart := true
  164. if autoStartStr := req.GetString("auto_start", "true"); autoStartStr == "false" {
  165. autoStart = false
  166. }
  167. // 获取可选参数:是否开启删除保护,默认为true
  168. disableDelete := true
  169. if disableDeleteStr := req.GetString("disable_delete", "true"); disableDeleteStr == "false" {
  170. disableDelete = false
  171. }
  172. // 获取其他可选参数
  173. billingType := req.GetString("billing_type", "")
  174. duration := req.GetString("duration", "")
  175. description := req.GetString("description", "")
  176. hostname := req.GetString("hostname", "")
  177. hypervisor := req.GetString("hypervisor", "")
  178. secgroupID := req.GetString("secgroup_id", "")
  179. userData := req.GetString("user_data", "")
  180. keypairID := req.GetString("keypair_id", "")
  181. projectID := req.GetString("project_id", "")
  182. zoneID := req.GetString("zone_id", "")
  183. regionID := req.GetString("region_id", "")
  184. bootOrder := req.GetString("boot_order", "")
  185. // 获取安全组ID列表,并按逗号分割
  186. var secgroups []string
  187. if secgroupsStr := req.GetString("secgroups", ""); secgroupsStr != "" {
  188. secgroups = strings.Split(secgroupsStr, ",")
  189. }
  190. // 解析元数据JSON字符串
  191. metadata := make(map[string]string)
  192. if metadataStr := req.GetString("metadata", ""); metadataStr != "" {
  193. if err := json.Unmarshal([]byte(metadataStr), &metadata); err != nil {
  194. return nil, fmt.Errorf("无效的元数据JSON格式: %w", err)
  195. }
  196. }
  197. // 解析数据盘配置JSON数组
  198. var dataDisks []models.DiskConfig
  199. if dataDisksStr := req.GetString("data_disks", ""); dataDisksStr != "" {
  200. if err := json.Unmarshal([]byte(dataDisksStr), &dataDisks); err != nil {
  201. return nil, fmt.Errorf("无效的数据盘配置JSON格式: %w", err)
  202. }
  203. }
  204. // 构造创建虚拟机的请求对象
  205. createRequest := models.CreateServerRequest{
  206. Name: name,
  207. VcpuCount: vcpuCount,
  208. VmemSize: vmemSize,
  209. ImageId: imageID,
  210. DiskSize: diskSize,
  211. NetworkId: networkID,
  212. ServerskuId: serverSkuID,
  213. Count: count,
  214. Password: password,
  215. AutoStart: autoStart,
  216. BillingType: billingType,
  217. Duration: duration,
  218. Description: description,
  219. Hostname: hostname,
  220. Hypervisor: hypervisor,
  221. Metadata: metadata,
  222. SecgroupId: secgroupID,
  223. Secgroups: secgroups,
  224. UserData: userData,
  225. KeypairId: keypairID,
  226. ProjectId: projectID,
  227. ZoneId: zoneID,
  228. RegionId: regionID,
  229. DisableDelete: disableDelete,
  230. BootOrder: bootOrder,
  231. DataDisks: dataDisks,
  232. }
  233. // 获取访问凭证
  234. ak := req.GetString("ak", "")
  235. sk := req.GetString("sk", "")
  236. // 调用适配器创建虚拟机
  237. response, err := c.adapter.CreateServer(ctx, createRequest, ak, sk)
  238. if err != nil {
  239. log.Errorf("Fail to create server: %s", err)
  240. return nil, fmt.Errorf("fail to create server: %w", err)
  241. }
  242. // 格式化创建结果
  243. formattedResult := c.formatCreateResult(response, &createRequest)
  244. // 将结果序列化为JSON格式
  245. resultJSON, err := json.MarshalIndent(formattedResult, "", " ")
  246. if err != nil {
  247. log.Errorf("Fail to serialize result: %s", err)
  248. return nil, fmt.Errorf("fail to serialize result: %w", err)
  249. }
  250. // 返回格式化后的结果
  251. return mcp.NewToolResultText(string(resultJSON)), nil
  252. }
  253. // GetName 返回工具的名称标识符
  254. // 返回值: 工具名称字符串,用于唯一标识该工具
  255. func (c *CloudpodsServerCreateTool) GetName() string {
  256. return "cloudpods_create_server"
  257. }
  258. // formatCreateResult 格式化创建虚拟机的响应结果
  259. // response: 原始的创建虚拟机响应数据
  260. // request: 原始的创建虚拟机请求数据
  261. // 返回值: 格式化后的结果,包含创建信息、结果详情和摘要
  262. func (c *CloudpodsServerCreateTool) formatCreateResult(response *models.CreateServerResponse, request *models.CreateServerRequest) map[string]interface{} {
  263. // 初始化格式化结果结构
  264. formatted := map[string]interface{}{
  265. // 创建请求的基本信息
  266. "create_info": map[string]interface{}{
  267. "name": request.Name,
  268. "vcpu_count": request.VcpuCount,
  269. "vmem_size": request.VmemSize,
  270. "image_id": request.ImageId,
  271. "disk_size": request.DiskSize,
  272. "network_id": request.NetworkId,
  273. "serversku_id": request.ServerskuId,
  274. "count": request.Count,
  275. "auto_start": request.AutoStart,
  276. "billing_type": request.BillingType,
  277. "duration": request.Duration,
  278. "description": request.Description,
  279. "hostname": request.Hostname,
  280. "hypervisor": request.Hypervisor,
  281. "secgroup_id": request.SecgroupId,
  282. "keypair_id": request.KeypairId,
  283. "project_id": request.ProjectId,
  284. "zone_id": request.ZoneId,
  285. "region_id": request.RegionId,
  286. "disable_delete": request.DisableDelete,
  287. "boot_order": request.BootOrder,
  288. },
  289. // 创建响应的结果信息
  290. "result": map[string]interface{}{
  291. "status": response.Status,
  292. "message": response.Message,
  293. "servers": make([]map[string]interface{}, 0, len(response.Data.Servers)),
  294. },
  295. }
  296. // 遍历创建的虚拟机列表,构造每个虚拟机的详细信息
  297. for _, server := range response.Data.Servers {
  298. serverInfo := map[string]interface{}{
  299. "id": server.ID,
  300. "name": server.Name,
  301. "status": server.Status,
  302. "task_id": server.TaskID,
  303. }
  304. formatted["result"].(map[string]interface{})["servers"] = append(
  305. formatted["result"].(map[string]interface{})["servers"].([]map[string]interface{}),
  306. serverInfo,
  307. )
  308. }
  309. // 构造摘要信息
  310. formatted["summary"] = map[string]interface{}{
  311. "requested_count": request.Count, // 请求创建的虚拟机数量
  312. "created_count": len(response.Data.Servers), // 实际创建的虚拟机数量
  313. "success": response.Status == 200, // 创建是否成功
  314. }
  315. return formatted
  316. }