diff --git a/README.md b/README.md index 5f6b6532..444d6722 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,7 @@ # GoEdge目标 做一款人人用得起的CDN & WAF系统。 - + ## 特性 * `免费` - 开源、免费、自由、开放 diff --git a/internal/rpc/context_interface.go b/internal/rpc/context_interface.go deleted file mode 100644 index a311f1f5..00000000 --- a/internal/rpc/context_interface.go +++ /dev/null @@ -1,7 +0,0 @@ -package rpc - -import "context" - -type ContextInterface interface { - AdminContext() context.Context -} diff --git a/internal/web/actions/actionutils/action_interface.go b/internal/web/actions/actionutils/action_interface.go new file mode 100644 index 00000000..83bdb8a0 --- /dev/null +++ b/internal/web/actions/actionutils/action_interface.go @@ -0,0 +1,15 @@ +package actionutils + +import ( + "context" + "github.com/TeaOSLab/EdgeAdmin/internal/rpc" + "github.com/iwind/TeaGo/maps" +) + +type ActionInterface interface { + RPC() *rpc.RPCClient + + AdminContext() context.Context + + ViewData() maps.Map +} diff --git a/internal/web/actions/actionutils/parent_action.go b/internal/web/actions/actionutils/parent_action.go index 3fbf2310..3ef8fed4 100644 --- a/internal/web/actions/actionutils/parent_action.go +++ b/internal/web/actions/actionutils/parent_action.go @@ -10,6 +10,7 @@ import ( "github.com/TeaOSLab/EdgeCommon/pkg/rpc/dao" "github.com/iwind/TeaGo/actions" "github.com/iwind/TeaGo/logs" + "github.com/iwind/TeaGo/maps" "net/http" "strconv" ) @@ -126,3 +127,8 @@ func (this *ParentAction) AdminContext() context.Context { } return this.rpcClient.Context(this.AdminId()) } + +// ViewData 视图里可以使用的数据 +func (this *ParentAction) ViewData() maps.Map { + return this.Data +} diff --git a/internal/web/actions/default/clusters/cluster/boards/index.go b/internal/web/actions/default/clusters/cluster/boards/index.go index 666d382a..7b572016 100644 --- a/internal/web/actions/default/clusters/cluster/boards/index.go +++ b/internal/web/actions/default/clusters/cluster/boards/index.go @@ -3,11 +3,14 @@ package boards import ( + teaconst "github.com/TeaOSLab/EdgeAdmin/internal/const" "github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils" "github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb" + "github.com/TeaOSLab/EdgeCommon/pkg/serverconfigs" "github.com/iwind/TeaGo/maps" "github.com/iwind/TeaGo/types" timeutil "github.com/iwind/TeaGo/utils/time" + "strconv" ) type IndexAction struct { @@ -21,6 +24,11 @@ func (this *IndexAction) Init() { func (this *IndexAction) RunGet(params struct { ClusterId int64 }) { + if !teaconst.IsPlus { + this.RedirectURL("/clusters/cluster?clusterId=" + strconv.FormatInt(params.ClusterId, 10)) + return + } + resp, err := this.RPC().ServerStatBoardRPC().ComposeServerStatNodeClusterBoard(this.AdminContext(), &pb.ComposeServerStatNodeClusterBoardRequest{NodeClusterId: params.ClusterId}) if err != nil { this.ErrorPage(err) @@ -128,5 +136,41 @@ func (this *IndexAction) RunGet(params struct { this.Data["loadValues"] = statMaps } + // 指标 + { + var chartMaps = []maps.Map{} + for _, chart := range resp.MetricCharts { + var statMaps = []maps.Map{} + for _, stat := range chart.MetricStats { + statMaps = append(statMaps, maps.Map{ + "keys": stat.Keys, + "time": stat.Time, + "value": stat.Value, + "count": stat.SumCount, + "total": stat.SumTotal, + }) + } + chartMaps = append(chartMaps, maps.Map{ + "chart": maps.Map{ + "id": chart.MetricChart.Id, + "name": chart.MetricChart.Name, + "widthDiv": chart.MetricChart.WidthDiv, + "isOn": chart.MetricChart.IsOn, + "maxItems": chart.MetricChart.MaxItems, + "type": chart.MetricChart.Type, + }, + "item": maps.Map{ + "id": chart.MetricChart.MetricItem.Id, + "name": chart.MetricChart.MetricItem.Name, + "period": chart.MetricChart.MetricItem.Period, + "periodUnit": chart.MetricChart.MetricItem.PeriodUnit, + "valueType": serverconfigs.FindMetricValueType(chart.MetricChart.MetricItem.Category, chart.MetricChart.MetricItem.Value), + }, + "stats": statMaps, + }) + } + this.Data["metricCharts"] = chartMaps + } + this.Show() } diff --git a/internal/web/actions/default/clusters/cluster/init.go b/internal/web/actions/default/clusters/cluster/init.go index c5cd0bc0..ed04ec4b 100644 --- a/internal/web/actions/default/clusters/cluster/init.go +++ b/internal/web/actions/default/clusters/cluster/init.go @@ -5,6 +5,7 @@ import ( "github.com/TeaOSLab/EdgeAdmin/internal/web/actions/default/clusters/cluster/boards" "github.com/TeaOSLab/EdgeAdmin/internal/web/actions/default/clusters/cluster/groups" "github.com/TeaOSLab/EdgeAdmin/internal/web/actions/default/clusters/cluster/node" + nodeboards "github.com/TeaOSLab/EdgeAdmin/internal/web/actions/default/clusters/cluster/node/boards" "github.com/TeaOSLab/EdgeAdmin/internal/web/actions/default/clusters/cluster/node/monitor" "github.com/TeaOSLab/EdgeAdmin/internal/web/actions/default/clusters/cluster/node/thresholds" clusters "github.com/TeaOSLab/EdgeAdmin/internal/web/actions/default/clusters/clusterutils" @@ -32,34 +33,39 @@ func init() { GetPost("/installManual", new(InstallManualAction)). // 节点相关 - Get("/node", new(node.IndexAction)). - GetPost("/node/update", new(node.UpdateAction)). - GetPost("/node/install", new(node.InstallAction)). - Post("/node/updateInstallStatus", new(node.UpdateInstallStatusAction)). - Post("/node/status", new(node.StatusAction)). - Get("/node/logs", new(node.LogsAction)). - Post("/node/start", new(node.StartAction)). - Post("/node/stop", new(node.StopAction)). - Post("/node/up", new(node.UpAction)). - Get("/node/monitor", new(monitor.IndexAction)). - Post("/node/monitor/cpu", new(monitor.CpuAction)). - Post("/node/monitor/memory", new(monitor.MemoryAction)). - Post("/node/monitor/load", new(monitor.LoadAction)). - Post("/node/monitor/trafficIn", new(monitor.TrafficInAction)). - Post("/node/monitor/trafficOut", new(monitor.TrafficOutAction)). - Post("/node/monitor/connections", new(monitor.ConnectionsAction)). - Get("/node/thresholds", new(thresholds.IndexAction)). + Prefix("/clusters/cluster/node"). + Get("", new(node.IndexAction)). + GetPost("/update", new(node.UpdateAction)). + GetPost("/install", new(node.InstallAction)). + Post("/updateInstallStatus", new(node.UpdateInstallStatusAction)). + Post("/status", new(node.StatusAction)). + Get("/logs", new(node.LogsAction)). + Post("/start", new(node.StartAction)). + Post("/stop", new(node.StopAction)). + Post("/up", new(node.UpAction)). + Get("/monitor", new(monitor.IndexAction)). + Post("/monitor/cpu", new(monitor.CpuAction)). + Post("/monitor/memory", new(monitor.MemoryAction)). + Post("/monitor/load", new(monitor.LoadAction)). + Post("/monitor/trafficIn", new(monitor.TrafficInAction)). + Post("/monitor/trafficOut", new(monitor.TrafficOutAction)). + Post("/monitor/connections", new(monitor.ConnectionsAction)). + Get("/thresholds", new(thresholds.IndexAction)). + Get("/detail", new(node.DetailAction)). + Get("/boards", new(nodeboards.IndexAction)). // 分组相关 - Get("/groups", new(groups.IndexAction)). - GetPost("/groups/createPopup", new(groups.CreatePopupAction)). - GetPost("/groups/updatePopup", new(groups.UpdatePopupAction)). - Post("/groups/delete", new(groups.DeleteAction)). - Post("/groups/sort", new(groups.SortAction)). - GetPost("/groups/selectPopup", new(groups.SelectPopupAction)). + Prefix("/clusters/cluster/groups"). + Get("", new(groups.IndexAction)). + GetPost("/createPopup", new(groups.CreatePopupAction)). + GetPost("/updatePopup", new(groups.UpdatePopupAction)). + Post("/delete", new(groups.DeleteAction)). + Post("/sort", new(groups.SortAction)). + GetPost("/selectPopup", new(groups.SelectPopupAction)). // 看板相关 - Get("/boards", new(boards.IndexAction)). + Prefix("/clusters/cluster/boards"). + Get("", new(boards.IndexAction)). EndAll() }) diff --git a/internal/web/actions/default/clusters/cluster/node/boards/index.go b/internal/web/actions/default/clusters/cluster/node/boards/index.go new file mode 100644 index 00000000..0f61b31d --- /dev/null +++ b/internal/web/actions/default/clusters/cluster/node/boards/index.go @@ -0,0 +1,180 @@ +// Copyright 2021 Liuxiangchao iwind.liu@gmail.com. All rights reserved. + +package boards + +import ( + teaconst "github.com/TeaOSLab/EdgeAdmin/internal/const" + "github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils" + "github.com/TeaOSLab/EdgeAdmin/internal/web/actions/default/clusters/cluster/node/nodeutils" + "github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb" + "github.com/TeaOSLab/EdgeCommon/pkg/serverconfigs" + "github.com/iwind/TeaGo/maps" + "github.com/iwind/TeaGo/types" + timeutil "github.com/iwind/TeaGo/utils/time" + "strconv" +) + +type IndexAction struct { + actionutils.ParentAction +} + +func (this *IndexAction) Init() { + this.Nav("", "node", "board") + this.SecondMenu("nodes") +} + +func (this *IndexAction) RunGet(params struct { + ClusterId int64 + NodeId int64 +}) { + err := nodeutils.InitNodeInfo(this, params.NodeId) + if err != nil { + this.ErrorPage(err) + return + } + + if !teaconst.IsPlus { + this.RedirectURL("/clusters/cluster/node?clusterId=" + strconv.FormatInt(params.ClusterId, 10) + "&nodeId=" + strconv.FormatInt(params.NodeId, 10)) + return + } + + resp, err := this.RPC().ServerStatBoardRPC().ComposeServerStatNodeBoard(this.AdminContext(), &pb.ComposeServerStatNodeBoardRequest{NodeId: params.NodeId}) + if err != nil { + this.ErrorPage(err) + return + } + + this.Data["board"] = maps.Map{ + "isActive": resp.IsActive, + "trafficInBytes": resp.TrafficInBytes, + "trafficOutBytes": resp.TrafficOutBytes, + "countConnections": resp.CountConnections, + "countRequests": resp.CountRequests, + "countAttackRequests": resp.CountAttackRequests, + "cpuUsage": resp.CpuUsage, + "memoryUsage": resp.MemoryUsage, + "memoryTotalSize": resp.MemoryTotalSize, + "load": resp.Load, + "cacheDiskSize": resp.CacheDiskSize, + "cacheMemorySize": resp.CacheMemorySize, + } + + // 24小时流量趋势 + { + var statMaps = []maps.Map{} + for _, stat := range resp.HourlyTrafficStats { + statMaps = append(statMaps, maps.Map{ + "bytes": stat.Bytes, + "cachedBytes": stat.CachedBytes, + "countRequests": stat.CountRequests, + "countCachedRequests": stat.CountCachedRequests, + "day": stat.Hour[4:6] + "月" + stat.Hour[6:8] + "日", + "hour": stat.Hour[8:], + }) + } + this.Data["hourlyStats"] = statMaps + } + + // 15天流量趋势 + { + var statMaps = []maps.Map{} + for _, stat := range resp.DailyTrafficStats { + statMaps = append(statMaps, maps.Map{ + "bytes": stat.Bytes, + "cachedBytes": stat.CachedBytes, + "countRequests": stat.CountRequests, + "countCachedRequests": stat.CountCachedRequests, + "day": stat.Day[4:6] + "月" + stat.Day[6:] + "日", + }) + } + this.Data["dailyStats"] = statMaps + } + + // 域名排行 + { + var statMaps = []maps.Map{} + for _, stat := range resp.TopDomainStats { + statMaps = append(statMaps, maps.Map{ + "serverId": stat.ServerId, + "domain": stat.Domain, + "countRequests": stat.CountRequests, + "bytes": stat.Bytes, + }) + } + this.Data["topDomainStats"] = statMaps + } + + // CPU + { + var statMaps = []maps.Map{} + for _, stat := range resp.CpuNodeValues { + statMaps = append(statMaps, maps.Map{ + "time": timeutil.FormatTime("H:i", stat.CreatedAt), + "value": types.Float32(string(stat.ValueJSON)), + }) + } + this.Data["cpuValues"] = statMaps + } + + // Memory + { + var statMaps = []maps.Map{} + for _, stat := range resp.MemoryNodeValues { + statMaps = append(statMaps, maps.Map{ + "time": timeutil.FormatTime("H:i", stat.CreatedAt), + "value": types.Float32(string(stat.ValueJSON)), + }) + } + this.Data["memoryValues"] = statMaps + } + + // Load + { + var statMaps = []maps.Map{} + for _, stat := range resp.LoadNodeValues { + statMaps = append(statMaps, maps.Map{ + "time": timeutil.FormatTime("H:i", stat.CreatedAt), + "value": types.Float32(string(stat.ValueJSON)), + }) + } + this.Data["loadValues"] = statMaps + } + + // 指标 + { + var chartMaps = []maps.Map{} + for _, chart := range resp.MetricCharts { + var statMaps = []maps.Map{} + for _, stat := range chart.MetricStats { + statMaps = append(statMaps, maps.Map{ + "keys": stat.Keys, + "time": stat.Time, + "value": stat.Value, + "count": stat.SumCount, + "total": stat.SumTotal, + }) + } + chartMaps = append(chartMaps, maps.Map{ + "chart": maps.Map{ + "id": chart.MetricChart.Id, + "name": chart.MetricChart.Name, + "widthDiv": chart.MetricChart.WidthDiv, + "isOn": chart.MetricChart.IsOn, + "maxItems": chart.MetricChart.MaxItems, + "type": chart.MetricChart.Type, + }, + "item": maps.Map{ + "id": chart.MetricChart.MetricItem.Id, + "name": chart.MetricChart.MetricItem.Name, + "period": chart.MetricChart.MetricItem.Period, + "periodUnit": chart.MetricChart.MetricItem.PeriodUnit, + "valueType": serverconfigs.FindMetricValueType(chart.MetricChart.MetricItem.Category, chart.MetricChart.MetricItem.Value), + }, + "stats": statMaps, + }) + } + this.Data["metricCharts"] = chartMaps + } + + this.Show() +} diff --git a/internal/web/actions/default/clusters/cluster/node/detail.go b/internal/web/actions/default/clusters/cluster/node/detail.go new file mode 100644 index 00000000..d5f7b398 --- /dev/null +++ b/internal/web/actions/default/clusters/cluster/node/detail.go @@ -0,0 +1,259 @@ +package node + +import ( + "encoding/json" + "fmt" + "github.com/TeaOSLab/EdgeAdmin/internal/utils/numberutils" + "github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils" + "github.com/TeaOSLab/EdgeAdmin/internal/web/actions/default/clusters/grants/grantutils" + "github.com/TeaOSLab/EdgeCommon/pkg/nodeconfigs" + "github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb" + "github.com/iwind/TeaGo/maps" + "time" +) + +type DetailAction struct { + actionutils.ParentAction +} + +func (this *DetailAction) Init() { + this.Nav("", "node", "node") + this.SecondMenu("nodes") +} + +func (this *DetailAction) RunGet(params struct { + NodeId int64 +}) { + this.Data["nodeId"] = params.NodeId + + nodeResp, err := this.RPC().NodeRPC().FindEnabledNode(this.AdminContext(), &pb.FindEnabledNodeRequest{NodeId: params.NodeId}) + if err != nil { + this.ErrorPage(err) + return + } + node := nodeResp.Node + if node == nil { + this.WriteString("找不到要操作的节点") + return + } + + var clusterMap maps.Map = nil + if node.NodeCluster != nil { + clusterId := node.NodeCluster.Id + clusterResp, err := this.RPC().NodeClusterRPC().FindEnabledNodeCluster(this.AdminContext(), &pb.FindEnabledNodeClusterRequest{NodeClusterId: clusterId}) + if err != nil { + this.ErrorPage(err) + return + } + cluster := clusterResp.NodeCluster + if cluster != nil { + clusterMap = maps.Map{ + "id": cluster.Id, + "name": cluster.Name, + "installDir": cluster.InstallDir, + } + } + } + + // IP地址 + ipAddressesResp, err := this.RPC().NodeIPAddressRPC().FindAllEnabledIPAddressesWithNodeId(this.AdminContext(), &pb.FindAllEnabledIPAddressesWithNodeIdRequest{ + NodeId: params.NodeId, + Role: nodeconfigs.NodeRoleNode, + }) + if err != nil { + this.ErrorPage(err) + return + } + ipAddressMaps := []maps.Map{} + for _, addr := range ipAddressesResp.Addresses { + ipAddressMaps = append(ipAddressMaps, maps.Map{ + "id": addr.Id, + "name": addr.Name, + "ip": addr.Ip, + "canAccess": addr.CanAccess, + }) + } + + // DNS相关 + dnsInfoResp, err := this.RPC().NodeRPC().FindEnabledNodeDNS(this.AdminContext(), &pb.FindEnabledNodeDNSRequest{NodeId: params.NodeId}) + if err != nil { + this.ErrorPage(err) + return + } + dnsRouteMaps := []maps.Map{} + recordName := "" + recordValue := "" + if dnsInfoResp.Node != nil { + recordName = dnsInfoResp.Node.NodeClusterDNSName + "." + dnsInfoResp.Node.DnsDomainName + recordValue = dnsInfoResp.Node.IpAddr + for _, dnsInfo := range dnsInfoResp.Node.Routes { + dnsRouteMaps = append(dnsRouteMaps, maps.Map{ + "name": dnsInfo.Name, + "code": dnsInfo.Code, + }) + } + } + if len(dnsRouteMaps) == 0 { + dnsRouteMaps = append(dnsRouteMaps, maps.Map{ + "name": "", + "code": "", + }) + } + this.Data["dnsRoutes"] = dnsRouteMaps + this.Data["dnsRecordName"] = recordName + this.Data["dnsRecordValue"] = recordValue + + // 登录信息 + var loginMap maps.Map = nil + if node.Login != nil { + loginParams := maps.Map{} + if len(node.Login.Params) > 0 { + err = json.Unmarshal(node.Login.Params, &loginParams) + if err != nil { + this.ErrorPage(err) + return + } + } + + grantMap := maps.Map{} + grantId := loginParams.GetInt64("grantId") + if grantId > 0 { + grantResp, err := this.RPC().NodeGrantRPC().FindEnabledNodeGrant(this.AdminContext(), &pb.FindEnabledNodeGrantRequest{NodeGrantId: grantId}) + if err != nil { + this.ErrorPage(err) + return + } + if grantResp.NodeGrant != nil { + grantMap = maps.Map{ + "id": grantResp.NodeGrant.Id, + "name": grantResp.NodeGrant.Name, + "method": grantResp.NodeGrant.Method, + "methodName": grantutils.FindGrantMethodName(grantResp.NodeGrant.Method), + } + } + } + + loginMap = maps.Map{ + "id": node.Login.Id, + "name": node.Login.Name, + "type": node.Login.Type, + "params": loginParams, + "grant": grantMap, + } + } + + // 运行状态 + status := &nodeconfigs.NodeStatus{} + if len(node.StatusJSON) > 0 { + err = json.Unmarshal(node.StatusJSON, &status) + if err != nil { + this.ErrorPage(err) + return + } + status.IsActive = status.IsActive && time.Now().Unix()-status.UpdatedAt <= 60 // N秒之内认为活跃 + } + + // 检查是否有新版本 + if len(status.OS) > 0 { + checkVersionResp, err := this.RPC().NodeRPC().CheckNodeLatestVersion(this.AdminContext(), &pb.CheckNodeLatestVersionRequest{ + Os: status.OS, + Arch: status.Arch, + CurrentVersion: status.BuildVersion, + }) + if err != nil { + this.ErrorPage(err) + return + } + this.Data["shouldUpgrade"] = checkVersionResp.HasNewVersion + this.Data["newVersion"] = checkVersionResp.NewVersion + } else { + this.Data["shouldUpgrade"] = false + this.Data["newVersion"] = "" + } + + // 分组 + var groupMap maps.Map = nil + if node.NodeGroup != nil { + groupMap = maps.Map{ + "id": node.NodeGroup.Id, + "name": node.NodeGroup.Name, + } + } + + // 区域 + var regionMap maps.Map = nil + if node.NodeRegion != nil { + regionMap = maps.Map{ + "id": node.NodeRegion.Id, + "name": node.NodeRegion.Name, + } + } + + // 缓存硬盘 & 内存容量 + var maxCacheDiskCapacity maps.Map = nil + if node.MaxCacheDiskCapacity != nil { + maxCacheDiskCapacity = maps.Map{ + "count": node.MaxCacheDiskCapacity.Count, + "unit": node.MaxCacheDiskCapacity.Unit, + } + } else { + maxCacheDiskCapacity = maps.Map{ + "count": 0, + "unit": "gb", + } + } + + var maxCacheMemoryCapacity maps.Map = nil + if node.MaxCacheMemoryCapacity != nil { + maxCacheMemoryCapacity = maps.Map{ + "count": node.MaxCacheMemoryCapacity.Count, + "unit": node.MaxCacheMemoryCapacity.Unit, + } + } else { + maxCacheMemoryCapacity = maps.Map{ + "count": 0, + "unit": "gb", + } + } + + this.Data["node"] = maps.Map{ + "id": node.Id, + "name": node.Name, + "ipAddresses": ipAddressMaps, + "cluster": clusterMap, + "login": loginMap, + "installDir": node.InstallDir, + "isInstalled": node.IsInstalled, + "uniqueId": node.UniqueId, + "secret": node.Secret, + "maxCPU": node.MaxCPU, + "isOn": node.IsOn, + + "status": maps.Map{ + "isActive": status.IsActive, + "updatedAt": status.UpdatedAt, + "hostname": status.Hostname, + "cpuUsage": status.CPUUsage, + "cpuUsageText": fmt.Sprintf("%.2f%%", status.CPUUsage*100), + "memUsage": status.MemoryUsage, + "memUsageText": fmt.Sprintf("%.2f%%", status.MemoryUsage*100), + "connectionCount": status.ConnectionCount, + "buildVersion": status.BuildVersion, + "cpuPhysicalCount": status.CPUPhysicalCount, + "cpuLogicalCount": status.CPULogicalCount, + "load1m": fmt.Sprintf("%.2f", status.Load1m), + "load5m": fmt.Sprintf("%.2f", status.Load5m), + "load15m": fmt.Sprintf("%.2f", status.Load15m), + "cacheTotalDiskSize": numberutils.FormatBytes(status.CacheTotalDiskSize), + "cacheTotalMemorySize": numberutils.FormatBytes(status.CacheTotalMemorySize), + }, + + "group": groupMap, + "region": regionMap, + + "maxCacheDiskCapacity": maxCacheDiskCapacity, + "maxCacheMemoryCapacity": maxCacheMemoryCapacity, + } + + this.Show() +} diff --git a/internal/web/actions/default/clusters/cluster/node/index.go b/internal/web/actions/default/clusters/cluster/node/index.go index 2e358c2f..a0e7fe06 100644 --- a/internal/web/actions/default/clusters/cluster/node/index.go +++ b/internal/web/actions/default/clusters/cluster/node/index.go @@ -1,15 +1,11 @@ package node import ( - "encoding/json" "fmt" - "github.com/TeaOSLab/EdgeAdmin/internal/utils/numberutils" + teaconst "github.com/TeaOSLab/EdgeAdmin/internal/const" "github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils" - "github.com/TeaOSLab/EdgeAdmin/internal/web/actions/default/clusters/grants/grantutils" - "github.com/TeaOSLab/EdgeCommon/pkg/nodeconfigs" - "github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb" - "github.com/iwind/TeaGo/maps" - "time" + "github.com/TeaOSLab/EdgeAdmin/internal/web/actions/default/clusters/cluster/node/nodeutils" + "strconv" ) type IndexAction struct { @@ -24,236 +20,15 @@ func (this *IndexAction) Init() { func (this *IndexAction) RunGet(params struct { NodeId int64 }) { - this.Data["nodeId"] = params.NodeId - - nodeResp, err := this.RPC().NodeRPC().FindEnabledNode(this.AdminContext(), &pb.FindEnabledNodeRequest{NodeId: params.NodeId}) + err := nodeutils.InitNodeInfo(this, params.NodeId) if err != nil { this.ErrorPage(err) return } - node := nodeResp.Node - if node == nil { - this.WriteString("找不到要操作的节点") - return - } - var clusterMap maps.Map = nil - if node.NodeCluster != nil { - clusterId := node.NodeCluster.Id - clusterResp, err := this.RPC().NodeClusterRPC().FindEnabledNodeCluster(this.AdminContext(), &pb.FindEnabledNodeClusterRequest{NodeClusterId: clusterId}) - if err != nil { - this.ErrorPage(err) - return - } - cluster := clusterResp.NodeCluster - if cluster != nil { - clusterMap = maps.Map{ - "id": cluster.Id, - "name": cluster.Name, - "installDir": cluster.InstallDir, - } - } - } - - // IP地址 - ipAddressesResp, err := this.RPC().NodeIPAddressRPC().FindAllEnabledIPAddressesWithNodeId(this.AdminContext(), &pb.FindAllEnabledIPAddressesWithNodeIdRequest{ - NodeId: params.NodeId, - Role: nodeconfigs.NodeRoleNode, - }) - if err != nil { - this.ErrorPage(err) - return - } - ipAddressMaps := []maps.Map{} - for _, addr := range ipAddressesResp.Addresses { - ipAddressMaps = append(ipAddressMaps, maps.Map{ - "id": addr.Id, - "name": addr.Name, - "ip": addr.Ip, - "canAccess": addr.CanAccess, - }) - } - - // DNS相关 - dnsInfoResp, err := this.RPC().NodeRPC().FindEnabledNodeDNS(this.AdminContext(), &pb.FindEnabledNodeDNSRequest{NodeId: params.NodeId}) - if err != nil { - this.ErrorPage(err) - return - } - dnsRouteMaps := []maps.Map{} - recordName := "" - recordValue := "" - if dnsInfoResp.Node != nil { - recordName = dnsInfoResp.Node.NodeClusterDNSName + "." + dnsInfoResp.Node.DnsDomainName - recordValue = dnsInfoResp.Node.IpAddr - for _, dnsInfo := range dnsInfoResp.Node.Routes { - dnsRouteMaps = append(dnsRouteMaps, maps.Map{ - "name": dnsInfo.Name, - "code": dnsInfo.Code, - }) - } - } - if len(dnsRouteMaps) == 0 { - dnsRouteMaps = append(dnsRouteMaps, maps.Map{ - "name": "", - "code": "", - }) - } - this.Data["dnsRoutes"] = dnsRouteMaps - this.Data["dnsRecordName"] = recordName - this.Data["dnsRecordValue"] = recordValue - - // 登录信息 - var loginMap maps.Map = nil - if node.Login != nil { - loginParams := maps.Map{} - if len(node.Login.Params) > 0 { - err = json.Unmarshal(node.Login.Params, &loginParams) - if err != nil { - this.ErrorPage(err) - return - } - } - - grantMap := maps.Map{} - grantId := loginParams.GetInt64("grantId") - if grantId > 0 { - grantResp, err := this.RPC().NodeGrantRPC().FindEnabledNodeGrant(this.AdminContext(), &pb.FindEnabledNodeGrantRequest{NodeGrantId: grantId}) - if err != nil { - this.ErrorPage(err) - return - } - if grantResp.NodeGrant != nil { - grantMap = maps.Map{ - "id": grantResp.NodeGrant.Id, - "name": grantResp.NodeGrant.Name, - "method": grantResp.NodeGrant.Method, - "methodName": grantutils.FindGrantMethodName(grantResp.NodeGrant.Method), - } - } - } - - loginMap = maps.Map{ - "id": node.Login.Id, - "name": node.Login.Name, - "type": node.Login.Type, - "params": loginParams, - "grant": grantMap, - } - } - - // 运行状态 - status := &nodeconfigs.NodeStatus{} - if len(node.StatusJSON) > 0 { - err = json.Unmarshal(node.StatusJSON, &status) - if err != nil { - this.ErrorPage(err) - return - } - status.IsActive = status.IsActive && time.Now().Unix()-status.UpdatedAt <= 60 // N秒之内认为活跃 - } - - // 检查是否有新版本 - if len(status.OS) > 0 { - checkVersionResp, err := this.RPC().NodeRPC().CheckNodeLatestVersion(this.AdminContext(), &pb.CheckNodeLatestVersionRequest{ - Os: status.OS, - Arch: status.Arch, - CurrentVersion: status.BuildVersion, - }) - if err != nil { - this.ErrorPage(err) - return - } - this.Data["shouldUpgrade"] = checkVersionResp.HasNewVersion - this.Data["newVersion"] = checkVersionResp.NewVersion + if teaconst.IsPlus { + this.RedirectURL("/clusters/cluster/node/boards?clusterId=" + fmt.Sprintf("%d", this.Data["clusterId"]) + "&nodeId=" + strconv.FormatInt(params.NodeId, 10)) } else { - this.Data["shouldUpgrade"] = false - this.Data["newVersion"] = "" + this.RedirectURL("/clusters/cluster/node/detail?clusterId=" + fmt.Sprintf("%d", this.Data["clusterId"]) + strconv.FormatInt(params.NodeId, 10)) } - - // 分组 - var groupMap maps.Map = nil - if node.NodeGroup != nil { - groupMap = maps.Map{ - "id": node.NodeGroup.Id, - "name": node.NodeGroup.Name, - } - } - - // 区域 - var regionMap maps.Map = nil - if node.NodeRegion != nil { - regionMap = maps.Map{ - "id": node.NodeRegion.Id, - "name": node.NodeRegion.Name, - } - } - - // 缓存硬盘 & 内存容量 - var maxCacheDiskCapacity maps.Map = nil - if node.MaxCacheDiskCapacity != nil { - maxCacheDiskCapacity = maps.Map{ - "count": node.MaxCacheDiskCapacity.Count, - "unit": node.MaxCacheDiskCapacity.Unit, - } - } else { - maxCacheDiskCapacity = maps.Map{ - "count": 0, - "unit": "gb", - } - } - - var maxCacheMemoryCapacity maps.Map = nil - if node.MaxCacheMemoryCapacity != nil { - maxCacheMemoryCapacity = maps.Map{ - "count": node.MaxCacheMemoryCapacity.Count, - "unit": node.MaxCacheMemoryCapacity.Unit, - } - } else { - maxCacheMemoryCapacity = maps.Map{ - "count": 0, - "unit": "gb", - } - } - - this.Data["node"] = maps.Map{ - "id": node.Id, - "name": node.Name, - "ipAddresses": ipAddressMaps, - "cluster": clusterMap, - "login": loginMap, - "installDir": node.InstallDir, - "isInstalled": node.IsInstalled, - "uniqueId": node.UniqueId, - "secret": node.Secret, - "maxCPU": node.MaxCPU, - "isOn": node.IsOn, - - "status": maps.Map{ - "isActive": status.IsActive, - "updatedAt": status.UpdatedAt, - "hostname": status.Hostname, - "cpuUsage": status.CPUUsage, - "cpuUsageText": fmt.Sprintf("%.2f%%", status.CPUUsage*100), - "memUsage": status.MemoryUsage, - "memUsageText": fmt.Sprintf("%.2f%%", status.MemoryUsage*100), - "connectionCount": status.ConnectionCount, - "buildVersion": status.BuildVersion, - "cpuPhysicalCount": status.CPUPhysicalCount, - "cpuLogicalCount": status.CPULogicalCount, - "load1m": fmt.Sprintf("%.2f", status.Load1m), - "load5m": fmt.Sprintf("%.2f", status.Load5m), - "load15m": fmt.Sprintf("%.2f", status.Load15m), - "cacheTotalDiskSize": numberutils.FormatBytes(status.CacheTotalDiskSize), - "cacheTotalMemorySize": numberutils.FormatBytes(status.CacheTotalMemorySize), - }, - - "group": groupMap, - "region": regionMap, - - "maxCacheDiskCapacity": maxCacheDiskCapacity, - "maxCacheMemoryCapacity": maxCacheMemoryCapacity, - } - - this.Show() } diff --git a/internal/web/actions/default/clusters/cluster/node/logs.go b/internal/web/actions/default/clusters/cluster/node/logs.go index 09cf715f..aa6b28de 100644 --- a/internal/web/actions/default/clusters/cluster/node/logs.go +++ b/internal/web/actions/default/clusters/cluster/node/logs.go @@ -2,6 +2,7 @@ package node import ( "github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils" + "github.com/TeaOSLab/EdgeAdmin/internal/web/actions/default/clusters/cluster/node/nodeutils" "github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb" "github.com/iwind/TeaGo/maps" timeutil "github.com/iwind/TeaGo/utils/time" @@ -23,6 +24,13 @@ func (this *LogsAction) RunGet(params struct { Keyword string Level string }) { + // 初始化节点信息(用于菜单) + err := nodeutils.InitNodeInfo(this, params.NodeId) + if err != nil { + this.ErrorPage(err) + return + } + this.Data["nodeId"] = params.NodeId this.Data["dayFrom"] = params.DayFrom this.Data["dayTo"] = params.DayTo diff --git a/internal/web/actions/default/clusters/cluster/node/monitor/index.go b/internal/web/actions/default/clusters/cluster/node/monitor/index.go index cdef3fd6..061cd9c9 100644 --- a/internal/web/actions/default/clusters/cluster/node/monitor/index.go +++ b/internal/web/actions/default/clusters/cluster/node/monitor/index.go @@ -2,7 +2,10 @@ package monitor -import "github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils" +import ( + "github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils" + "github.com/TeaOSLab/EdgeAdmin/internal/web/actions/default/clusters/cluster/node/nodeutils" +) type IndexAction struct { actionutils.ParentAction @@ -17,5 +20,12 @@ func (this *IndexAction) RunGet(params struct { }) { this.Data["nodeId"] = params.NodeId + // 初始化节点信息(用于菜单) + err := nodeutils.InitNodeInfo(this, params.NodeId) + if err != nil { + this.ErrorPage(err) + return + } + this.Show() } diff --git a/internal/web/actions/default/clusters/cluster/node/nodeutils/utils.go b/internal/web/actions/default/clusters/cluster/node/nodeutils/utils.go new file mode 100644 index 00000000..d5439b17 --- /dev/null +++ b/internal/web/actions/default/clusters/cluster/node/nodeutils/utils.go @@ -0,0 +1,32 @@ +// Copyright 2021 Liuxiangchao iwind.liu@gmail.com. All rights reserved. + +package nodeutils + +import ( + "errors" + "github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils" + "github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb" + "github.com/iwind/TeaGo/maps" + "strconv" +) + +// InitNodeInfo 初始化节点信息 +func InitNodeInfo(action actionutils.ActionInterface, nodeId int64) error { + // 节点信息(用于菜单) + nodeResp, err := action.RPC().NodeRPC().FindEnabledNode(action.AdminContext(), &pb.FindEnabledNodeRequest{NodeId: nodeId}) + if err != nil { + return err + } + if nodeResp.Node == nil { + return errors.New("node '" + strconv.FormatInt(nodeId, 10) + "' not found") + } + var node = nodeResp.Node + action.ViewData()["node"] = maps.Map{ + "id": node.Id, + "name": node.Name, + } + if node.NodeCluster != nil { + action.ViewData()["clusterId"] = node.NodeCluster.Id + } + return nil +} diff --git a/internal/web/actions/default/clusters/cluster/node/thresholds/index.go b/internal/web/actions/default/clusters/cluster/node/thresholds/index.go index e4254402..18e99c58 100644 --- a/internal/web/actions/default/clusters/cluster/node/thresholds/index.go +++ b/internal/web/actions/default/clusters/cluster/node/thresholds/index.go @@ -4,6 +4,7 @@ package thresholds import ( "github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils" + "github.com/TeaOSLab/EdgeAdmin/internal/web/actions/default/clusters/cluster/node/nodeutils" "github.com/TeaOSLab/EdgeCommon/pkg/nodeconfigs" "github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb" "github.com/iwind/TeaGo/maps" @@ -23,6 +24,13 @@ func (this *IndexAction) RunGet(params struct { }) { this.Data["nodeId"] = params.NodeId + // 初始化节点信息(用于菜单) + err := nodeutils.InitNodeInfo(this, params.NodeId) + if err != nil { + this.ErrorPage(err) + return + } + // 列出所有阈值 thresholdsResp, err := this.RPC().NodeThresholdRPC().FindAllEnabledNodeThresholds(this.AdminContext(), &pb.FindAllEnabledNodeThresholdsRequest{ Role: "node", diff --git a/internal/web/actions/default/clusters/clusterutils/cluster_helper.go b/internal/web/actions/default/clusters/clusterutils/cluster_helper.go index 43c98831..be4928f5 100644 --- a/internal/web/actions/default/clusters/clusterutils/cluster_helper.go +++ b/internal/web/actions/default/clusters/clusterutils/cluster_helper.go @@ -2,7 +2,6 @@ package clusterutils import ( teaconst "github.com/TeaOSLab/EdgeAdmin/internal/const" - "github.com/TeaOSLab/EdgeAdmin/internal/rpc" "github.com/TeaOSLab/EdgeAdmin/internal/utils/numberutils" "github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils" "github.com/TeaOSLab/EdgeCommon/pkg/rpc/dao" @@ -36,7 +35,7 @@ func (this *ClusterHelper) BeforeAction(actionPtr actions.ActionWrapper) (goNext action.Data["clusterId"] = clusterId if clusterId > 0 { - var ctx = actionPtr.(rpc.ContextInterface).AdminContext() + var ctx = actionPtr.(actionutils.ActionInterface).AdminContext() cluster, err := dao.SharedNodeClusterDAO.FindEnabledNodeCluster(ctx, clusterId) if err != nil { logs.Error(err) diff --git a/internal/web/actions/default/ns/clusters/clusterutils/cluster_helper.go b/internal/web/actions/default/ns/clusters/clusterutils/cluster_helper.go index 209b03f1..87b28537 100644 --- a/internal/web/actions/default/ns/clusters/clusterutils/cluster_helper.go +++ b/internal/web/actions/default/ns/clusters/clusterutils/cluster_helper.go @@ -39,7 +39,7 @@ func (this *ClusterHelper) BeforeAction(actionPtr actions.ActionWrapper) (goNext logs.Error(err) return } - clusterResp, err := rpcClient.NSClusterRPC().FindEnabledNSCluster(actionPtr.(rpc.ContextInterface).AdminContext(), &pb.FindEnabledNSClusterRequest{ + clusterResp, err := rpcClient.NSClusterRPC().FindEnabledNSCluster(actionPtr.(actionutils.ActionInterface).AdminContext(), &pb.FindEnabledNSClusterRequest{ NsClusterId: clusterId, }) if err != nil { diff --git a/web/public/js/components/server/metric-charts.js b/web/public/js/components/server/metric-charts.js index e06af056..6fc4b1f8 100644 --- a/web/public/js/components/server/metric-charts.js +++ b/web/public/js/components/server/metric-charts.js @@ -1,6 +1,6 @@ // 指标图表 Vue.component("metric-chart", { - props: ["v-chart", "v-stats", "v-period-unit"], + props: ["v-chart", "v-stats", "v-period-unit", "v-value-type"], mounted: function () { this.load() }, @@ -88,7 +88,7 @@ Vue.component("metric-chart", { if (stat.total > 0) { percent = Math.round((stat.value * 100 / stat.total) * 100) / 100 } - return stat.keys[0] + ": " + stat.value + ",占比:" + percent + "%" + return stat.keys[0] + ":" + stat.value + ",占比:" + percent + "%" } }, series: [ @@ -108,6 +108,21 @@ Vue.component("metric-chart", { let values = this.stats.map(function (v) { return v.value }) + + let axis = {unit: "", divider: 1} + switch (this.vValueType) { + case "count": + axis = teaweb.countAxis(values, function (v) { + return v + }) + break + case "bytes": + axis = teaweb.bytesAxis(values, function (v) { + return v + }) + break + } + let that = this chart.setOption({ xAxis: { @@ -115,7 +130,13 @@ Vue.component("metric-chart", { return that.formatTime(v.time) }) }, - yAxis: {}, + yAxis: { + axisLabel: { + formatter: function (value) { + return value + axis.unit + } + } + }, tooltip: { show: true, trigger: "item", @@ -134,7 +155,9 @@ Vue.component("metric-chart", { { name: name, type: "bar", - data: values, + data: values.map(function (v){ + return v/axis.divider + }), itemStyle: { color: "#9DD3E8" }, @@ -151,6 +174,21 @@ Vue.component("metric-chart", { let values = this.stats.map(function (v) { return v.value }) + + let axis = {unit: "", divider: 1} + switch (this.vValueType) { + case "count": + axis = teaweb.countAxis(values, function (v) { + return v + }) + break + case "bytes": + axis = teaweb.bytesAxis(values, function (v) { + return v + }) + break + } + let that = this chart.setOption({ xAxis: { @@ -158,7 +196,13 @@ Vue.component("metric-chart", { return that.formatTime(v.time) }) }, - yAxis: {}, + yAxis: { + axisLabel: { + formatter: function (value) { + return value + axis.unit + } + } + }, tooltip: { show: true, trigger: "item", @@ -177,7 +221,9 @@ Vue.component("metric-chart", { { name: name, type: "line", - data: values, + data: values.map(function (v) { + return v / axis.divider + }), itemStyle: { color: "#9DD3E8" }, @@ -190,6 +236,20 @@ Vue.component("metric-chart", { let values = this.stats.map(function (v) { return v.value }) + let axis = {unit: "", divider: 1} + switch (this.vValueType) { + case "count": + axis = teaweb.countAxis(values, function (v) { + return v + }) + break + case "bytes": + axis = teaweb.bytesAxis(values, function (v) { + return v + }) + break + } + let that = this chart.setOption({ xAxis: { @@ -212,7 +272,13 @@ Vue.component("metric-chart", { return stat.keys[0] + ": " + stat.value + ",占比:" + percent + "%" } }, - yAxis: {}, + yAxis: { + axisLabel: { + formatter: function (value) { + return value + axis.unit + } + } + }, grid: { left: 40, top: 10, @@ -223,7 +289,9 @@ Vue.component("metric-chart", { { name: name, type: "bar", - data: values, + data: values.map(function (v) { + return v / axis.divider + }), itemStyle: { color: "#9DD3E8" }, @@ -272,9 +340,9 @@ Vue.component("metric-chart", { } }, template: `