cloudpods_server_monitor_tool.go 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366
  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. "time"
  22. "github.com/mark3labs/mcp-go/mcp"
  23. "yunion.io/x/log"
  24. "yunion.io/x/onecloud/pkg/mcp-server/adapters"
  25. "yunion.io/x/onecloud/pkg/mcp-server/models"
  26. )
  27. // CloudpodsServerMonitorTool 用于获取Cloudpods虚拟机监控信息
  28. type CloudpodsServerMonitorTool struct {
  29. // adapter 用于与 Cloudpods API 进行交互
  30. adapter *adapters.CloudpodsAdapter
  31. }
  32. // NewCloudpodsServerMonitorTool 创建一个新的CloudpodsServerMonitorTool实例
  33. // adapter: 用于与Cloudpods API交互的适配器
  34. // 返回值: CloudpodsServerMonitorTool实例指针
  35. func NewCloudpodsServerMonitorTool(adapter *adapters.CloudpodsAdapter) *CloudpodsServerMonitorTool {
  36. return &CloudpodsServerMonitorTool{
  37. adapter: adapter,
  38. }
  39. }
  40. // GetTool 定义并返回获取虚拟机监控信息工具的元数据
  41. // 该工具用于获取Cloudpods虚拟机的监控信息,包括CPU、内存、磁盘、网络等指标
  42. // server_id: 虚拟机ID (必填)
  43. // start_time: 开始时间戳(秒),默认为1小时前
  44. // end_time: 结束时间戳(秒),默认为当前时间
  45. // metrics: 监控指标,多个用逗号分隔,例如:cpu_usage,mem_usage,disk_usage,net_bps_rx,net_bps_tx
  46. // ak: 用户登录cloudpods后获取的access key
  47. // sk: 用户登录cloudpods后获取的secret key
  48. func (c *CloudpodsServerMonitorTool) GetTool() mcp.Tool {
  49. return mcp.NewTool(
  50. "cloudpods_get_server_monitor",
  51. mcp.WithDescription("获取Cloudpods虚拟机监控信息,包括CPU、内存、磁盘、网络等指标"),
  52. mcp.WithString("server_id", mcp.Required(), mcp.Description("虚拟机ID")),
  53. mcp.WithString("start_time", mcp.Description("开始时间戳(秒),默认为1小时前")),
  54. mcp.WithString("end_time", mcp.Description("结束时间戳(秒),默认为当前时间")),
  55. mcp.WithString("metrics", mcp.Description("监控指标,多个用逗号分隔,例如:cpu_usage,mem_usage,disk_usage,net_bps_rx,net_bps_tx")),
  56. mcp.WithString("ak", mcp.Description("用户登录cloudpods后获取的access key")),
  57. mcp.WithString("sk", mcp.Description("用户登录cloudpods后获取的secret key")),
  58. )
  59. }
  60. // Handle 处理获取虚拟机监控信息的请求
  61. // ctx: 控制生命周期的上下文
  62. // req: 包含获取监控信息所需参数的请求对象
  63. // 返回值: 包含监控信息的响应对象和可能的错误
  64. func (c *CloudpodsServerMonitorTool) Handle(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolResult, error) {
  65. // 获取必填参数:虚拟机ID
  66. serverID, err := req.RequireString("server_id")
  67. if err != nil {
  68. return nil, err
  69. }
  70. // 设置默认时间范围:结束时间为当前时间,开始时间为1小时前
  71. now := time.Now().Unix()
  72. startTime := now - 3600
  73. // 解析开始时间参数,如果指定则使用指定值
  74. if startTimeStr := req.GetString("start_time", ""); startTimeStr != "" {
  75. if parsedStartTime, err := strconv.ParseInt(startTimeStr, 10, 64); err == nil {
  76. startTime = parsedStartTime
  77. }
  78. }
  79. // 解析结束时间参数,如果指定则使用指定值
  80. endTime := now
  81. if endTimeStr := req.GetString("end_time", ""); endTimeStr != "" {
  82. if parsedEndTime, err := strconv.ParseInt(endTimeStr, 10, 64); err == nil {
  83. endTime = parsedEndTime
  84. }
  85. }
  86. // 获取可选参数:监控指标
  87. var metrics []string
  88. if metricsStr := req.GetString("metrics", ""); metricsStr != "" {
  89. metrics = strings.Split(metricsStr, ",")
  90. for i, metric := range metrics {
  91. metrics[i] = strings.TrimSpace(metric)
  92. }
  93. } else {
  94. metrics = []string{"cpu_usage", "mem_usage", "disk_usage", "net_bps_rx", "net_bps_tx"}
  95. }
  96. // 获取ak和sk参数,用于认证
  97. ak := req.GetString("ak", "")
  98. sk := req.GetString("sk", "")
  99. // 调用适配器获取虚拟机监控信息
  100. monitorResponse, err := c.adapter.GetServerMonitor(ctx, serverID, startTime, endTime, metrics, ak, sk)
  101. if err != nil {
  102. log.Errorf("Fail to get server monitor: %s", err)
  103. return nil, fmt.Errorf("fail to get server monitor: %w", err)
  104. }
  105. // 格式化监控结果
  106. formattedResult := c.formatMonitorResult(monitorResponse, serverID, startTime, endTime, metrics)
  107. // 将结果序列化为JSON格式
  108. resultJSON, err := json.MarshalIndent(formattedResult, "", " ")
  109. if err != nil {
  110. log.Errorf("Fail to serialize result: %s", err)
  111. return nil, fmt.Errorf("fail to serialize result: %w", err)
  112. }
  113. // 返回格式化后的结果
  114. return mcp.NewToolResultText(string(resultJSON)), nil
  115. }
  116. // GetName 返回工具的名称标识符
  117. // 返回值: 工具名称字符串,用于唯一标识该工具
  118. func (c *CloudpodsServerMonitorTool) GetName() string {
  119. return "cloudpods_get_server_monitor"
  120. }
  121. // formatMonitorResult 格式化虚拟机监控信息的响应结果
  122. // response: 原始监控响应数据
  123. // serverID: 虚拟机ID
  124. // startTime: 监控开始时间
  125. // endTime: 监控结束时间
  126. // requestedMetrics: 请求的监控指标
  127. // 返回值: 包含监控信息的格式化结果
  128. func (c *CloudpodsServerMonitorTool) formatMonitorResult(response *models.MonitorResponse, serverID string, startTime, endTime int64, requestedMetrics []string) map[string]interface{} {
  129. // 初始化格式化结果结构
  130. formatted := map[string]interface{}{
  131. // 添加请求的基本信息
  132. "query_info": map[string]interface{}{
  133. "server_id": serverID,
  134. "start_time": startTime,
  135. "end_time": endTime,
  136. "start_time_human": time.Unix(startTime, 0).Format("2006-01-02 15:04:05"),
  137. "end_time_human": time.Unix(endTime, 0).Format("2006-01-02 15:04:05"),
  138. "requested_metrics": requestedMetrics,
  139. "duration_seconds": endTime - startTime,
  140. },
  141. "status": response.Status,
  142. "metrics": make([]map[string]interface{}, 0, len(response.Data.Metrics)),
  143. }
  144. for _, metric := range response.Data.Metrics {
  145. metricInfo := map[string]interface{}{
  146. "metric": metric.Metric,
  147. "unit": metric.Unit,
  148. "data_points": len(metric.Values),
  149. "values": make([]map[string]interface{}, 0, len(metric.Values)),
  150. }
  151. var totalValue float64
  152. var minValue, maxValue float64
  153. var latestValue float64
  154. var latestTime int64
  155. for i, value := range metric.Values {
  156. valueInfo := map[string]interface{}{
  157. "timestamp": value.Timestamp,
  158. "time_human": time.Unix(value.Timestamp, 0).Format("2006-01-02 15:04:05"),
  159. "value": value.Value,
  160. }
  161. metricInfo["values"] = append(metricInfo["values"].([]map[string]interface{}), valueInfo)
  162. totalValue += value.Value
  163. if i == 0 {
  164. minValue = value.Value
  165. maxValue = value.Value
  166. } else {
  167. if value.Value < minValue {
  168. minValue = value.Value
  169. }
  170. if value.Value > maxValue {
  171. maxValue = value.Value
  172. }
  173. }
  174. if value.Timestamp > latestTime {
  175. latestTime = value.Timestamp
  176. latestValue = value.Value
  177. }
  178. }
  179. if len(metric.Values) > 0 {
  180. metricInfo["statistics"] = map[string]interface{}{
  181. "min": minValue,
  182. "max": maxValue,
  183. "average": totalValue / float64(len(metric.Values)),
  184. "latest": latestValue,
  185. }
  186. }
  187. formatted["metrics"] = append(formatted["metrics"].([]map[string]interface{}), metricInfo)
  188. }
  189. formatted["summary"] = map[string]interface{}{
  190. "total_metrics": len(response.Data.Metrics),
  191. "query_successful": response.Status == 200,
  192. "time_range_hours": float64(endTime-startTime) / 3600,
  193. }
  194. return formatted
  195. }
  196. // CloudpodsServerStatsTool 用于获取Cloudpods虚拟机实时统计信息
  197. type CloudpodsServerStatsTool struct {
  198. // adapter 用于与 Cloudpods API 进行交互
  199. adapter *adapters.CloudpodsAdapter
  200. }
  201. // NewCloudpodsServerStatsTool 创建一个新的CloudpodsServerStatsTool实例
  202. // adapter: 用于与Cloudpods API交互的适配器
  203. // 返回值: CloudpodsServerStatsTool实例指针
  204. func NewCloudpodsServerStatsTool(adapter *adapters.CloudpodsAdapter) *CloudpodsServerStatsTool {
  205. return &CloudpodsServerStatsTool{
  206. adapter: adapter,
  207. }
  208. }
  209. // GetTool 定义并返回获取虚拟机统计信息工具的元数据
  210. // 该工具用于获取Cloudpods虚拟机的实时统计信息,包括CPU使用率、内存使用率、磁盘使用率和网络流量
  211. // server_id: 虚拟机ID (必填)
  212. // ak: 用户登录cloudpods后获取的access key
  213. // sk: 用户登录cloudpods后获取的secret key
  214. func (c *CloudpodsServerStatsTool) GetTool() mcp.Tool {
  215. return mcp.NewTool(
  216. "cloudpods_get_server_stats",
  217. mcp.WithDescription("获取Cloudpods虚拟机实时统计信息,包括CPU使用率、内存使用率、磁盘使用率和网络流量"),
  218. mcp.WithString("server_id", mcp.Required(), mcp.Description("虚拟机ID")),
  219. mcp.WithString("ak", mcp.Description("用户登录cloudpods后获取的access key")),
  220. mcp.WithString("sk", mcp.Description("用户登录cloudpods后获取的secret key")),
  221. )
  222. }
  223. // Handle 处理获取虚拟机统计信息的请求
  224. // ctx: 控制生命周期的上下文
  225. // req: 包含获取统计信息所需参数的请求对象
  226. // 返回值: 包含统计信息的响应对象和可能的错误
  227. func (c *CloudpodsServerStatsTool) Handle(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolResult, error) {
  228. // 获取必填参数:虚拟机ID
  229. serverID, err := req.RequireString("server_id")
  230. if err != nil {
  231. return nil, err
  232. }
  233. // 获取可选参数:访问凭证
  234. ak := req.GetString("ak", "")
  235. sk := req.GetString("sk", "")
  236. // 调用适配器获取虚拟机统计信息
  237. statsResponse, err := c.adapter.GetServerStats(ctx, serverID, ak, sk)
  238. if err != nil {
  239. log.Errorf("Fail to get server stats: %s", err)
  240. return nil, fmt.Errorf("fail to get server stats: %w", err)
  241. }
  242. // 格式化统计结果
  243. formattedResult := c.formatStatsResult(statsResponse, serverID)
  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 *CloudpodsServerStatsTool) GetName() string {
  256. return "cloudpods_get_server_stats"
  257. }
  258. // formatStatsResult 格式化虚拟机统计信息的响应结果
  259. // response: 原始统计响应数据
  260. // serverID: 虚拟机ID
  261. // 返回值: 包含统计信息的格式化结果
  262. func (c *CloudpodsServerStatsTool) formatStatsResult(response *models.ServerStatsResponse, serverID string) map[string]interface{} {
  263. // 初始化格式化结果结构
  264. formatted := map[string]interface{}{
  265. "server_id": serverID,
  266. "status": response.Status,
  267. // 添加统计信息
  268. "stats": map[string]interface{}{
  269. "cpu_usage": fmt.Sprintf("%.1f%%", response.Data.CPUUsage),
  270. "memory_usage": fmt.Sprintf("%.1f%%", response.Data.MemUsage),
  271. "disk_usage": fmt.Sprintf("%.1f%%", response.Data.DiskUsage),
  272. "network": map[string]interface{}{
  273. "receive_bps": response.Data.NetBpsRx,
  274. "transmit_bps": response.Data.NetBpsTx,
  275. "receive_mbps": fmt.Sprintf("%.2f Mbps", float64(response.Data.NetBpsRx)/(1024*1024)),
  276. "transmit_mbps": fmt.Sprintf("%.2f Mbps", float64(response.Data.NetBpsTx)/(1024*1024)),
  277. },
  278. "updated_at": response.Data.UpdatedAt,
  279. },
  280. // 添加原始数据
  281. "raw_data": map[string]interface{}{
  282. "cpu_usage": response.Data.CPUUsage,
  283. "mem_usage": response.Data.MemUsage,
  284. "disk_usage": response.Data.DiskUsage,
  285. "net_bps_rx": response.Data.NetBpsRx,
  286. "net_bps_tx": response.Data.NetBpsTx,
  287. },
  288. }
  289. // 评估虚拟机健康状态
  290. var healthStatus string
  291. var healthScore int
  292. if response.Data.CPUUsage > 90 || response.Data.MemUsage > 90 || response.Data.DiskUsage > 90 {
  293. healthStatus = "警告"
  294. healthScore = 1
  295. } else if response.Data.CPUUsage > 70 || response.Data.MemUsage > 70 || response.Data.DiskUsage > 80 {
  296. healthStatus = "注意"
  297. healthScore = 2
  298. } else {
  299. healthStatus = "正常"
  300. healthScore = 3
  301. }
  302. // 添加健康状态信息
  303. formatted["health"] = map[string]interface{}{
  304. "status": healthStatus,
  305. "score": healthScore,
  306. "notes": []string{},
  307. }
  308. // 添加健康状态建议
  309. notes := []string{}
  310. if response.Data.CPUUsage > 90 {
  311. notes = append(notes, "CPU使用率过高,建议检查系统负载")
  312. }
  313. if response.Data.MemUsage > 90 {
  314. notes = append(notes, "内存使用率过高,建议释放内存或增加内存")
  315. }
  316. if response.Data.DiskUsage > 90 {
  317. notes = append(notes, "磁盘使用率过高,建议清理磁盘空间")
  318. }
  319. formatted["health"].(map[string]interface{})["notes"] = notes
  320. return formatted
  321. }