package maintenance import ( "encoding/json" "fmt" "net/http" "os" "os/exec" "path" "runtime" "time" "be.ems/lib/core/utils/ctx" "be.ems/lib/dborm" "be.ems/lib/log" "be.ems/lib/services" "be.ems/restagent/config" "github.com/shirou/gopsutil/cpu" "github.com/shirou/gopsutil/disk" "github.com/shirou/gopsutil/mem" ) // (1) OMC能够对相关的文件系统资源、内存、CPU资源、数据存储空间、数据库空间等系统指标进行监控管理; // 对于虚拟化部署OMC系统,能够对虚机内存、虚机CPU、虚拟存储空间、文件系统资源、数据库空间等指标进行监控,提供界面截图 ; // (2) 系统监控指标的采样时间和阈值可由用户设定,超过阈值将产生不同级别的告警,提供界面截图 ; // (3) OMC能够方便的查询数据库连接情况;并可手工干预数据库的连接,能方便的终结非法的数据库连接 ; // (4) 用户能方便的查询系统进程、应用进程等的进程名、进程类型、开始时间、运行主机等信息,提供界面截图 // (5) 用户能方便的对系统进程、应用进程等做中断或者启动操作,提供界面截图 // (6) 对于文件系统资源、内存、CPU资源、数据存储空间、数据库空间等系统指标数据,要求OMC能够保存至少3个月,提供界面截图 ; // (7) 用户可以按照需求自定义报表模板并生成OMC系统维护数据报表,提供界面截图 ; // (8) OMC具备自身告警管理功能,对于传统OMC系统,如:服务器单电源告警,存储硬盘告警、OMC系统软件故障等; // 对于虚拟化OMC系统,如虚机告警、虚拟硬盘告警等,提供界面截图 。 var ( // parameter config management Uri = config.UriPrefix + "/maintenance/{apiVersion}/zz" // (1) OMC能够对相关的文件系统资源、内存、CPU资源、数据存储空间、数据库空间等系统指标进行监控管理; UriPref = config.UriPrefix + "/maintenance/{apiVersion}/pref" // (6) 对于文件系统资源、内存、CPU资源、数据存储空间、数据库空间等系统指标数据,要求OMC能够保存至少3个月,提供界面截图 ; UriPrefLog = config.UriPrefix + "/maintenance/{apiVersion}/prefLog" // (2) 系统监控指标的采样时间和阈值可由用户设定,超过阈值将产生不同级别的告警,提供界面截图 ; UriConfig = config.UriPrefix + "/maintenance/{apiVersion}/config" // (3) OMC能够方便的查询数据库连接情况;并可手工干预数据库的连接,能方便的终结非法的数据库连接 UriSqlClient = config.UriPrefix + "/maintenance/{apiVersion}/sqlClient" // (4) 用户能方便的查询系统进程、应用进程等的进程名、进程类型、开始时间、运行主机等信息,提供界面截图 // (5) 用户能方便的对系统进程、应用进程等做中断或者启动操作,提供界面截图 UriTop = config.UriPrefix + "/maintenance/{apiVersion}/top" ) func init() { // 定時收集 TODO prefLogSave("") } func List(w http.ResponseWriter, r *http.Request) { fmt.Println("zz List") services.ResponseStatusOK200Null(w) } // 性能指標 func prefInfo(dirName string) map[string]any { data := make(map[string]any) // 显示文件資源目录 dirPath := "D://" if runtime.GOOS == "linux" { dirPath = "/home" } // 訪問下級 if dirName != "" { dirPath = path.Join(dirPath, dirName) } dir_list, e := os.ReadDir(dirPath) if e != nil { log.Error(e) } list := make([]map[string]any, 0) for _, v := range dir_list { o, err := v.Info() if err != nil { continue } list = append(list, map[string]any{ "name": o.Name(), "size": o.Size(), "mode": o.Mode().String(), "modTime": o.ModTime().Format("2006-01-02 15:04:05"), "isDir": o.IsDir(), }) } data["dirList"] = list // 文件資源使用率 u, _ := disk.Usage(dirPath) usedGB := int(u.Used) / (1024 * 1024 * 1024 * 1) data["dirUse"] = fmt.Sprintf("%d", usedGB) // CPU使用率 percent, err := cpu.Percent(time.Second, false) if err != nil { log.Error(err) } data["cpuUse"] = fmt.Sprintf("%.2f", percent[0]) // 内存使用率 memInfo, err := mem.VirtualMemory() if err != nil { log.Error(err) } data["memUse"] = memInfo.UsedPercent // 獲取數據庫占用空間 if dborm.DbClient.XEngine != nil { conf := config.GetYamlConfig() result, err := dborm.DbClient.XEngine.QueryString(`SELECT CONCAT(TRUNCATE(SUM(data_length)/1024/1024,2),'MB') AS data_size, CONCAT(TRUNCATE(SUM(max_data_length)/1024/1024,2),'MB') AS max_data_size, CONCAT(TRUNCATE(SUM(data_free)/1024/1024,2),'MB') AS data_free, CONCAT(TRUNCATE(SUM(index_length)/1024/1024,2),'MB') AS index_size FROM information_schema.tables WHERE TABLE_SCHEMA = ?; `, conf.Database.Name) if err == nil { data["dbInfo"] = result[0] } else { data["dbInfo"] = map[string]string{} } } return data } // 性能指標存入數據庫 func prefLogSave(dirName string) { if dborm.DbClient.XEngine != nil { data := prefInfo(dirName) dirListByte, err := json.Marshal(data["dirList"]) if err != nil { log.Error(err) } dbInfoByte, err := json.Marshal(data["dbInfo"]) if err != nil { log.Error(err) } rse, err := dborm.DbClient.XEngine.Exec(`INSERT INTO sys_perf_data (id, create_time, dir_used, dir_list, db_info, mem_used, cpu_used) VALUES(NULL, NOW(), ?, ?, ?, ?, ?); `, data["dirUse"], string(dirListByte), string(dbInfoByte), data["memUse"], data["cpuUse"]) if err != nil { log.Error(err) } fmt.Println(rse.LastInsertId()) } } // GET http://192.168.21.183:3040/api/rest/maintenance/v1/pref?dir=true func Pref(w http.ResponseWriter, r *http.Request) { // 知道下級文件資源目录 dirName := r.URL.Query().Get("dirName") data := prefInfo(dirName) services.ResponseWithJson(w, http.StatusOK, data) } // POST http://192.168.21.183:3040/api/rest/maintenance/v1/config func Config(w http.ResponseWriter, r *http.Request) { // json 請求參數獲取 var bodyArgs struct { Key string `json:"key"` Value string `json:"value"` } err := ctx.ShouldBindJSON(r, &bodyArgs) if err != nil { log.Error("io.ReadAll is failed:", err) services.ResponseNotFound404UriNotExist(w, r) return } // 進行值更新 if dborm.DbClient.XEngine != nil { result, err := dborm.DbClient.XEngine.QueryString("SELECT * FROM information_schema.processlist") if err != nil { fmt.Println(err) } fmt.Println(result) rse, err := dborm.DbClient.XEngine.Exec("UPDATE sys_config SET value = ? where id = ?", "true", 100) if err != nil { fmt.Println(err) } fmt.Println(rse) } services.ResponseStatusOK200Null(w) } // http://192.168.21.183:3040/api/rest/maintenance/v1/sqlClient?type=close // http://192.168.21.183:3040/api/rest/maintenance/v1/sqlClient?type=connet // http://192.168.21.183:3040/api/rest/maintenance/v1/sqlClient?type=user func SqlClient(w http.ResponseWriter, r *http.Request) { // 关闭 isClose := r.URL.Query().Get("type") if isClose == "close" && dborm.DbClient.XEngine != nil { dborm.DbClient.XEngine.Close() } // 重连 isConnet := r.URL.Query().Get("type") if isConnet == "connet" && dborm.DbClient.XEngine == nil { conf := config.GetYamlConfig() err := dborm.InitDbClient(conf.Database.Type, conf.Database.User, conf.Database.Password, conf.Database.Host, conf.Database.Port, conf.Database.Name, conf.Database.ConnParam) if err != nil { fmt.Println("dborm.initDbClient err:", err) services.ResponseInternalServerError500DatabaseOperationFailed(w) return } } // 查询实例 isUser := r.URL.Query().Get("type") if isUser == "user" && dborm.DbClient.XEngine != nil { result, err := dborm.DbClient.XEngine.QueryString("SELECT * FROM information_schema.processlist") if err != nil { fmt.Println(err) } fmt.Println(result) rse, err := dborm.DbClient.XEngine.Exec("KILL CONNECTION CONNECTION_ID()") if err != nil { fmt.Println(err) } fmt.Println(rse) } // 进行连接测试 err := dborm.DbClient.XEngine.Ping() if err != nil { fmt.Println(err) } services.ResponseStatusOK200Null(w) } // GET http://192.168.21.183:3040/api/rest/maintenance/v1/top?grep= func Top(w http.ResponseWriter, r *http.Request) { // 過濾命令 grep := r.URL.Query().Get("grep") // 命令拼接 var cmd *exec.Cmd switch runtime.GOOS { case "linux": command := "ps -ef " if grep != "" { command += grep } cmd = exec.Command(command) case "windows": command := "wmic process list brief " if grep != "" { command += grep } cmd = exec.Command("cmd", "/C", command) } out, err := cmd.CombinedOutput() fmt.Println(string(out)) if err != nil { fmt.Println(err) } services.ResponseWithJson(w, http.StatusOK, string(out)) } // PATCH http://192.168.21.183:3040/api/rest/maintenance/v1/top?ops=&name= func TopOps(w http.ResponseWriter, r *http.Request) { // json 請求參數獲取 var bodyArgs struct { Ops string `json:"ops"` Pid string `json:"pid"` } err := ctx.ShouldBindJSON(r, &bodyArgs) if err != nil { log.Error("io.ReadAll is failed:", err) services.ResponseNotFound404UriNotExist(w, r) return } // 命令拼接 var cmd *exec.Cmd switch runtime.GOOS { case "linux": switch bodyArgs.Ops { case "kill": cmd = exec.Command("kill", "-9", bodyArgs.Pid) } case "windows": switch bodyArgs.Ops { case "kill": cmd = exec.Command("cmd", "/C", "taskkill", "-PID", bodyArgs.Pid, "-F") } } out, err := cmd.CombinedOutput() fmt.Println(string(out)) if err != nil { fmt.Println(err) } services.ResponseWithJson(w, http.StatusOK, string(out)) }