From 72da89435ac9ec503af088b1240bfb2f325d184a Mon Sep 17 00:00:00 2001 From: GoEdgeLab Date: Mon, 5 Jul 2021 11:38:07 +0800 Subject: [PATCH] =?UTF-8?q?=E5=AE=9E=E7=8E=B0=E9=9B=86=E7=BE=A4=E7=9C=8B?= =?UTF-8?q?=E6=9D=BF=EF=BC=88=E6=9A=82=E6=97=B6=E5=8F=AA=E5=AF=B9=E4=BC=81?= =?UTF-8?q?=E4=B8=9A=E7=89=88=E5=BC=80=E6=94=BE=EF=BC=89?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- build/configs/.gitignore | 2 +- internal/configs/plus_config.go | 40 ++ internal/rpc/rpc_client.go | 8 + internal/tasks/task_authority.go | 14 + .../default/clusters/cluster/boards/index.go | 132 ++++++ .../actions/default/clusters/cluster/index.go | 215 +--------- .../actions/default/clusters/cluster/init.go | 5 + .../actions/default/clusters/cluster/nodes.go | 224 ++++++++++ .../cluster/settings/metrics/createPopup.go | 21 +- .../cluster/settings/metrics/index.go | 19 +- .../clusters/clusterutils/cluster_helper.go | 5 +- .../web/actions/default/clusters/index.go | 7 + .../servers/metrics/metricutils/utils.go | 8 + internal/web/actions/default/ui/showTip.go | 2 +- web/public/js/utils.js | 119 ++++- .../@default/clusters/cluster/@menu.html | 2 +- .../clusters/cluster/boards/index.css | 32 ++ .../clusters/cluster/boards/index.css.map | 1 + .../clusters/cluster/boards/index.html | 69 +++ .../@default/clusters/cluster/boards/index.js | 405 ++++++++++++++++++ .../clusters/cluster/boards/index.less | 45 ++ .../@default/clusters/cluster/createBatch.js | 2 +- .../@default/clusters/cluster/createNode.js | 2 +- .../@default/clusters/cluster/index.css.map | 1 - .../clusters/cluster/node/@node_menu.html | 2 +- .../clusters/cluster/{index.css => nodes.css} | 2 +- .../@default/clusters/cluster/nodes.css.map | 1 + .../cluster/{index.html => nodes.html} | 0 .../clusters/cluster/{index.js => nodes.js} | 0 .../cluster/{index.less => nodes.less} | 0 .../cluster/settings/metrics/createPopup.html | 6 +- .../cluster/settings/metrics/index.html | 8 +- web/views/@default/clusters/index.html | 9 +- web/views/@default/dashboard/index.css.map | 2 +- web/views/@default/dashboard/index.html | 4 +- web/views/@default/dashboard/index.js | 1 - web/views/@default/dashboard/index.less | 1 - web/views/@default/servers/@menu.html | 2 +- .../@default/servers/metrics/@item_menu.html | 2 +- 39 files changed, 1171 insertions(+), 249 deletions(-) create mode 100644 internal/configs/plus_config.go create mode 100644 internal/web/actions/default/clusters/cluster/boards/index.go create mode 100644 internal/web/actions/default/clusters/cluster/nodes.go create mode 100644 web/views/@default/clusters/cluster/boards/index.css create mode 100644 web/views/@default/clusters/cluster/boards/index.css.map create mode 100644 web/views/@default/clusters/cluster/boards/index.html create mode 100644 web/views/@default/clusters/cluster/boards/index.js create mode 100644 web/views/@default/clusters/cluster/boards/index.less delete mode 100644 web/views/@default/clusters/cluster/index.css.map rename web/views/@default/clusters/cluster/{index.css => nodes.css} (70%) create mode 100644 web/views/@default/clusters/cluster/nodes.css.map rename web/views/@default/clusters/cluster/{index.html => nodes.html} (100%) rename web/views/@default/clusters/cluster/{index.js => nodes.js} (100%) rename web/views/@default/clusters/cluster/{index.less => nodes.less} (100%) diff --git a/build/configs/.gitignore b/build/configs/.gitignore index f839cc86..3c99e472 100644 --- a/build/configs/.gitignore +++ b/build/configs/.gitignore @@ -2,4 +2,4 @@ api.yaml server.yaml api_db.yaml *.pem -tip.json \ No newline at end of file +*.cache.json \ No newline at end of file diff --git a/internal/configs/plus_config.go b/internal/configs/plus_config.go new file mode 100644 index 00000000..fa5ab779 --- /dev/null +++ b/internal/configs/plus_config.go @@ -0,0 +1,40 @@ +// Copyright 2021 Liuxiangchao iwind.liu@gmail.com. All rights reserved. + +package configs + +import ( + "encoding/json" + "github.com/iwind/TeaGo/Tea" + "io/ioutil" +) + +var plusConfigFile = "plus.cache.json" + +type PlusConfig struct { + IsPlus bool `json:"isPlus"` +} + +func ReadPlusConfig() *PlusConfig { + data, err := ioutil.ReadFile(Tea.ConfigFile(plusConfigFile)) + if err != nil { + return &PlusConfig{IsPlus: false} + } + var config = &PlusConfig{IsPlus: false} + err = json.Unmarshal(data, config) + if err != nil { + return config + } + return config +} + +func WritePlusConfig(config *PlusConfig) error { + configJSON, err := json.Marshal(config) + if err != nil { + return err + } + err = ioutil.WriteFile(Tea.ConfigFile(plusConfigFile), configJSON, 0777) + if err != nil { + return err + } + return nil +} diff --git a/internal/rpc/rpc_client.go b/internal/rpc/rpc_client.go index 60e92eba..3962a09e 100644 --- a/internal/rpc/rpc_client.go +++ b/internal/rpc/rpc_client.go @@ -404,6 +404,14 @@ func (this *RPCClient) NodeClusterMetricItemRPC() pb.NodeClusterMetricItemServic return pb.NewNodeClusterMetricItemServiceClient(this.pickConn()) } +func (this *RPCClient) ServerStatBoardRPC() pb.ServerStatBoardServiceClient { + return pb.NewServerStatBoardServiceClient(this.pickConn()) +} + +func (this *RPCClient) ServerStatBoardChartRPC() pb.ServerStatBoardChartServiceClient { + return pb.NewServerStatBoardChartServiceClient(this.pickConn()) +} + // Context 构造Admin上下文 func (this *RPCClient) Context(adminId int64) context.Context { ctx := context.Background() diff --git a/internal/tasks/task_authority.go b/internal/tasks/task_authority.go index 0431e772..ee3e4336 100644 --- a/internal/tasks/task_authority.go +++ b/internal/tasks/task_authority.go @@ -3,6 +3,7 @@ package tasks import ( + "github.com/TeaOSLab/EdgeAdmin/internal/configs" teaconst "github.com/TeaOSLab/EdgeAdmin/internal/const" "github.com/TeaOSLab/EdgeAdmin/internal/events" "github.com/TeaOSLab/EdgeAdmin/internal/rpc" @@ -28,6 +29,13 @@ func NewAuthorityTask() *AuthorityTask { } func (this *AuthorityTask) Start() { + // 从缓存中读取 + config := configs.ReadPlusConfig() + if config != nil { + teaconst.IsPlus = config.IsPlus + } + + // 开始计时器 ticker := time.NewTicker(10 * time.Minute) if Tea.IsTesting() { // 快速测试 @@ -65,10 +73,16 @@ func (this *AuthorityTask) Loop() error { if err != nil { return err } + var oldState = teaconst.IsPlus if resp.AuthorityKey != nil { teaconst.IsPlus = true } else { teaconst.IsPlus = false } + + if oldState != teaconst.IsPlus { + _ = configs.WritePlusConfig(&configs.PlusConfig{IsPlus: teaconst.IsPlus}) + } + return nil } diff --git a/internal/web/actions/default/clusters/cluster/boards/index.go b/internal/web/actions/default/clusters/cluster/boards/index.go new file mode 100644 index 00000000..666d382a --- /dev/null +++ b/internal/web/actions/default/clusters/cluster/boards/index.go @@ -0,0 +1,132 @@ +// Copyright 2021 Liuxiangchao iwind.liu@gmail.com. All rights reserved. + +package boards + +import ( + "github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils" + "github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb" + "github.com/iwind/TeaGo/maps" + "github.com/iwind/TeaGo/types" + timeutil "github.com/iwind/TeaGo/utils/time" +) + +type IndexAction struct { + actionutils.ParentAction +} + +func (this *IndexAction) Init() { + this.Nav("", "board", "") +} + +func (this *IndexAction) RunGet(params struct { + ClusterId int64 +}) { + resp, err := this.RPC().ServerStatBoardRPC().ComposeServerStatNodeClusterBoard(this.AdminContext(), &pb.ComposeServerStatNodeClusterBoardRequest{NodeClusterId: params.ClusterId}) + if err != nil { + this.ErrorPage(err) + return + } + this.Data["board"] = maps.Map{ + "countUsers": resp.CountUsers, + "countActiveNodes": resp.CountActiveNodes, + "countInactiveNodes": resp.CountInactiveNodes, + "countServers": resp.CountServers, + } + + // 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.TopNodeStats { + statMaps = append(statMaps, maps.Map{ + "nodeId": stat.NodeId, + "nodeName": stat.NodeName, + "countRequests": stat.CountRequests, + "bytes": stat.Bytes, + }) + } + this.Data["topNodeStats"] = 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 + } + + this.Show() +} diff --git a/internal/web/actions/default/clusters/cluster/index.go b/internal/web/actions/default/clusters/cluster/index.go index cfb347b2..758544a2 100644 --- a/internal/web/actions/default/clusters/cluster/index.go +++ b/internal/web/actions/default/clusters/cluster/index.go @@ -1,16 +1,11 @@ +// Copyright 2021 Liuxiangchao iwind.liu@gmail.com. All rights reserved. + package cluster import ( - "encoding/json" - "fmt" + teaconst "github.com/TeaOSLab/EdgeAdmin/internal/const" "github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils" - "github.com/TeaOSLab/EdgeCommon/pkg/nodeconfigs" - "github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb" - "github.com/iwind/TeaGo/logs" - "github.com/iwind/TeaGo/maps" - "github.com/iwind/TeaGo/types" "strconv" - "time" ) type IndexAction struct { @@ -18,207 +13,15 @@ type IndexAction struct { } func (this *IndexAction) Init() { - this.Nav("", "node", "index") - this.SecondMenu("nodes") + this.Nav("", "", "") } func (this *IndexAction) RunGet(params struct { - ClusterId int64 - GroupId int64 - RegionId int64 - InstalledState int - ActiveState int - Keyword string + ClusterId int64 }) { - this.Data["groupId"] = params.GroupId - this.Data["regionId"] = params.RegionId - this.Data["installState"] = params.InstalledState - this.Data["activeState"] = params.ActiveState - this.Data["keyword"] = params.Keyword - - countAllResp, err := this.RPC().NodeRPC().CountAllEnabledNodesMatch(this.AdminContext(), &pb.CountAllEnabledNodesMatchRequest{ - NodeClusterId: params.ClusterId, - }) - if err != nil { - this.ErrorPage(err) - return + if teaconst.IsPlus { + this.RedirectURL("/clusters/cluster/boards?clusterId=" + strconv.FormatInt(params.ClusterId, 10)) + } else { + this.RedirectURL("/clusters/cluster/nodes?clusterId=" + strconv.FormatInt(params.ClusterId, 10)) } - this.Data["countAll"] = countAllResp.Count - - countResp, err := this.RPC().NodeRPC().CountAllEnabledNodesMatch(this.AdminContext(), &pb.CountAllEnabledNodesMatchRequest{ - NodeClusterId: params.ClusterId, - NodeGroupId: params.GroupId, - NodeRegionId: params.RegionId, - InstallState: types.Int32(params.InstalledState), - ActiveState: types.Int32(params.ActiveState), - Keyword: params.Keyword, - }) - if err != nil { - this.ErrorPage(err) - return - } - - page := this.NewPage(countResp.Count) - this.Data["page"] = page.AsHTML() - - nodesResp, err := this.RPC().NodeRPC().ListEnabledNodesMatch(this.AdminContext(), &pb.ListEnabledNodesMatchRequest{ - Offset: page.Offset, - Size: page.Size, - NodeClusterId: params.ClusterId, - NodeGroupId: params.GroupId, - NodeRegionId: params.RegionId, - InstallState: types.Int32(params.InstalledState), - ActiveState: types.Int32(params.ActiveState), - Keyword: params.Keyword, - }) - if err != nil { - this.ErrorPage(err) - return - } - nodeMaps := []maps.Map{} - for _, node := range nodesResp.Nodes { - // 状态 - isSynced := false - status := &nodeconfigs.NodeStatus{} - if len(node.StatusJSON) > 0 { - err = json.Unmarshal(node.StatusJSON, &status) - if err != nil { - logs.Error(err) - continue - } - status.IsActive = status.IsActive && time.Now().Unix()-status.UpdatedAt <= 60 // N秒之内认为活跃 - isSynced = status.ConfigVersion == node.Version - } - - // IP - ipAddressesResp, err := this.RPC().NodeIPAddressRPC().FindAllEnabledIPAddressesWithNodeId(this.AdminContext(), &pb.FindAllEnabledIPAddressesWithNodeIdRequest{ - NodeId: node.Id, - Role: nodeconfigs.NodeRoleNode, - }) - if err != nil { - this.ErrorPage(err) - return - } - ipAddresses := []maps.Map{} - for _, addr := range ipAddressesResp.Addresses { - ipAddresses = append(ipAddresses, maps.Map{ - "id": addr.Id, - "name": addr.Name, - "ip": addr.Ip, - "canAccess": addr.CanAccess, - }) - } - - // 分组 - 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, - } - } - - // DNS - dnsRouteNames := []string{} - for _, route := range node.DnsRoutes { - dnsRouteNames = append(dnsRouteNames, route.Name) - } - - nodeMaps = append(nodeMaps, maps.Map{ - "id": node.Id, - "name": node.Name, - "isInstalled": node.IsInstalled, - "isOn": node.IsOn, - "isUp": node.IsUp, - "installStatus": maps.Map{ - "isRunning": node.InstallStatus.IsRunning, - "isFinished": node.InstallStatus.IsFinished, - "isOk": node.InstallStatus.IsOk, - "error": node.InstallStatus.Error, - }, - "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), - }, - "cluster": maps.Map{ - "id": node.NodeCluster.Id, - "name": node.NodeCluster.Name, - }, - "isSynced": isSynced, - "ipAddresses": ipAddresses, - "group": groupMap, - "region": regionMap, - "dnsRouteNames": dnsRouteNames, - }) - } - this.Data["nodes"] = nodeMaps - - // 所有分组 - groupMaps := []maps.Map{} - groupsResp, err := this.RPC().NodeGroupRPC().FindAllEnabledNodeGroupsWithNodeClusterId(this.AdminContext(), &pb.FindAllEnabledNodeGroupsWithNodeClusterIdRequest{ - NodeClusterId: params.ClusterId, - }) - if err != nil { - this.ErrorPage(err) - return - } - for _, group := range groupsResp.NodeGroups { - countResp, err := this.RPC().NodeRPC().CountAllEnabledNodesWithNodeGroupId(this.AdminContext(), &pb.CountAllEnabledNodesWithNodeGroupIdRequest{NodeGroupId: group.Id}) - if err != nil { - this.ErrorPage(err) - return - } - countNodes := countResp.Count - groupName := group.Name - if countNodes > 0 { - groupName += "(" + strconv.FormatInt(countNodes, 10) + ")" - } - groupMaps = append(groupMaps, maps.Map{ - "id": group.Id, - "name": groupName, - "countNodes": countNodes, - }) - } - this.Data["groups"] = groupMaps - - // 所有区域 - regionsResp, err := this.RPC().NodeRegionRPC().FindAllEnabledAndOnNodeRegions(this.AdminContext(), &pb.FindAllEnabledAndOnNodeRegionsRequest{}) - if err != nil { - this.ErrorPage(err) - return - } - regionMaps := []maps.Map{} - for _, region := range regionsResp.NodeRegions { - regionMaps = append(regionMaps, maps.Map{ - "id": region.Id, - "name": region.Name, - }) - } - this.Data["regions"] = regionMaps - - // 记录最近访问 - _, err = this.RPC().LatestItemRPC().IncreaseLatestItem(this.AdminContext(), &pb.IncreaseLatestItemRequest{ - ItemType: "cluster", - ItemId: params.ClusterId, - }) - if err != nil { - this.ErrorPage(err) - return - } - - this.Show() } diff --git a/internal/web/actions/default/clusters/cluster/init.go b/internal/web/actions/default/clusters/cluster/init.go index 4eb61e1f..c5cd0bc0 100644 --- a/internal/web/actions/default/clusters/cluster/init.go +++ b/internal/web/actions/default/clusters/cluster/init.go @@ -2,6 +2,7 @@ package cluster import ( "github.com/TeaOSLab/EdgeAdmin/internal/configloaders" + "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" "github.com/TeaOSLab/EdgeAdmin/internal/web/actions/default/clusters/cluster/node/monitor" @@ -18,6 +19,7 @@ func init() { Helper(clusters.NewClusterHelper()). Prefix("/clusters/cluster"). Get("", new(IndexAction)). + Get("/nodes", new(NodesAction)). GetPost("/installNodes", new(InstallNodesAction)). GetPost("/installRemote", new(InstallRemoteAction)). Post("/installStatus", new(InstallStatusAction)). @@ -56,6 +58,9 @@ func init() { Post("/groups/sort", new(groups.SortAction)). GetPost("/groups/selectPopup", new(groups.SelectPopupAction)). + // 看板相关 + Get("/boards", new(boards.IndexAction)). + EndAll() }) } diff --git a/internal/web/actions/default/clusters/cluster/nodes.go b/internal/web/actions/default/clusters/cluster/nodes.go new file mode 100644 index 00000000..953d234a --- /dev/null +++ b/internal/web/actions/default/clusters/cluster/nodes.go @@ -0,0 +1,224 @@ +package cluster + +import ( + "encoding/json" + "fmt" + "github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils" + "github.com/TeaOSLab/EdgeCommon/pkg/nodeconfigs" + "github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb" + "github.com/iwind/TeaGo/logs" + "github.com/iwind/TeaGo/maps" + "github.com/iwind/TeaGo/types" + "strconv" + "time" +) + +type NodesAction struct { + actionutils.ParentAction +} + +func (this *NodesAction) Init() { + this.Nav("", "node", "index") + this.SecondMenu("nodes") +} + +func (this *NodesAction) RunGet(params struct { + ClusterId int64 + GroupId int64 + RegionId int64 + InstalledState int + ActiveState int + Keyword string +}) { + this.Data["groupId"] = params.GroupId + this.Data["regionId"] = params.RegionId + this.Data["installState"] = params.InstalledState + this.Data["activeState"] = params.ActiveState + this.Data["keyword"] = params.Keyword + + countAllResp, err := this.RPC().NodeRPC().CountAllEnabledNodesMatch(this.AdminContext(), &pb.CountAllEnabledNodesMatchRequest{ + NodeClusterId: params.ClusterId, + }) + if err != nil { + this.ErrorPage(err) + return + } + this.Data["countAll"] = countAllResp.Count + + countResp, err := this.RPC().NodeRPC().CountAllEnabledNodesMatch(this.AdminContext(), &pb.CountAllEnabledNodesMatchRequest{ + NodeClusterId: params.ClusterId, + NodeGroupId: params.GroupId, + NodeRegionId: params.RegionId, + InstallState: types.Int32(params.InstalledState), + ActiveState: types.Int32(params.ActiveState), + Keyword: params.Keyword, + }) + if err != nil { + this.ErrorPage(err) + return + } + + page := this.NewPage(countResp.Count) + this.Data["page"] = page.AsHTML() + + nodesResp, err := this.RPC().NodeRPC().ListEnabledNodesMatch(this.AdminContext(), &pb.ListEnabledNodesMatchRequest{ + Offset: page.Offset, + Size: page.Size, + NodeClusterId: params.ClusterId, + NodeGroupId: params.GroupId, + NodeRegionId: params.RegionId, + InstallState: types.Int32(params.InstalledState), + ActiveState: types.Int32(params.ActiveState), + Keyword: params.Keyword, + }) + if err != nil { + this.ErrorPage(err) + return + } + nodeMaps := []maps.Map{} + for _, node := range nodesResp.Nodes { + // 状态 + isSynced := false + status := &nodeconfigs.NodeStatus{} + if len(node.StatusJSON) > 0 { + err = json.Unmarshal(node.StatusJSON, &status) + if err != nil { + logs.Error(err) + continue + } + status.IsActive = status.IsActive && time.Now().Unix()-status.UpdatedAt <= 60 // N秒之内认为活跃 + isSynced = status.ConfigVersion == node.Version + } + + // IP + ipAddressesResp, err := this.RPC().NodeIPAddressRPC().FindAllEnabledIPAddressesWithNodeId(this.AdminContext(), &pb.FindAllEnabledIPAddressesWithNodeIdRequest{ + NodeId: node.Id, + Role: nodeconfigs.NodeRoleNode, + }) + if err != nil { + this.ErrorPage(err) + return + } + ipAddresses := []maps.Map{} + for _, addr := range ipAddressesResp.Addresses { + ipAddresses = append(ipAddresses, maps.Map{ + "id": addr.Id, + "name": addr.Name, + "ip": addr.Ip, + "canAccess": addr.CanAccess, + }) + } + + // 分组 + 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, + } + } + + // DNS + dnsRouteNames := []string{} + for _, route := range node.DnsRoutes { + dnsRouteNames = append(dnsRouteNames, route.Name) + } + + nodeMaps = append(nodeMaps, maps.Map{ + "id": node.Id, + "name": node.Name, + "isInstalled": node.IsInstalled, + "isOn": node.IsOn, + "isUp": node.IsUp, + "installStatus": maps.Map{ + "isRunning": node.InstallStatus.IsRunning, + "isFinished": node.InstallStatus.IsFinished, + "isOk": node.InstallStatus.IsOk, + "error": node.InstallStatus.Error, + }, + "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), + }, + "cluster": maps.Map{ + "id": node.NodeCluster.Id, + "name": node.NodeCluster.Name, + }, + "isSynced": isSynced, + "ipAddresses": ipAddresses, + "group": groupMap, + "region": regionMap, + "dnsRouteNames": dnsRouteNames, + }) + } + this.Data["nodes"] = nodeMaps + + // 所有分组 + groupMaps := []maps.Map{} + groupsResp, err := this.RPC().NodeGroupRPC().FindAllEnabledNodeGroupsWithNodeClusterId(this.AdminContext(), &pb.FindAllEnabledNodeGroupsWithNodeClusterIdRequest{ + NodeClusterId: params.ClusterId, + }) + if err != nil { + this.ErrorPage(err) + return + } + for _, group := range groupsResp.NodeGroups { + countResp, err := this.RPC().NodeRPC().CountAllEnabledNodesWithNodeGroupId(this.AdminContext(), &pb.CountAllEnabledNodesWithNodeGroupIdRequest{NodeGroupId: group.Id}) + if err != nil { + this.ErrorPage(err) + return + } + countNodes := countResp.Count + groupName := group.Name + if countNodes > 0 { + groupName += "(" + strconv.FormatInt(countNodes, 10) + ")" + } + groupMaps = append(groupMaps, maps.Map{ + "id": group.Id, + "name": groupName, + "countNodes": countNodes, + }) + } + this.Data["groups"] = groupMaps + + // 所有区域 + regionsResp, err := this.RPC().NodeRegionRPC().FindAllEnabledAndOnNodeRegions(this.AdminContext(), &pb.FindAllEnabledAndOnNodeRegionsRequest{}) + if err != nil { + this.ErrorPage(err) + return + } + regionMaps := []maps.Map{} + for _, region := range regionsResp.NodeRegions { + regionMaps = append(regionMaps, maps.Map{ + "id": region.Id, + "name": region.Name, + }) + } + this.Data["regions"] = regionMaps + + // 记录最近访问 + _, err = this.RPC().LatestItemRPC().IncreaseLatestItem(this.AdminContext(), &pb.IncreaseLatestItemRequest{ + ItemType: "cluster", + ItemId: params.ClusterId, + }) + if err != nil { + this.ErrorPage(err) + return + } + + this.Show() +} diff --git a/internal/web/actions/default/clusters/cluster/settings/metrics/createPopup.go b/internal/web/actions/default/clusters/cluster/settings/metrics/createPopup.go index 55b3b2e4..0e930af0 100644 --- a/internal/web/actions/default/clusters/cluster/settings/metrics/createPopup.go +++ b/internal/web/actions/default/clusters/cluster/settings/metrics/createPopup.go @@ -5,6 +5,7 @@ package metrics import ( "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/actions" "github.com/iwind/TeaGo/maps" ) @@ -59,15 +60,17 @@ func (this *CreatePopupAction) RunGet(params struct { var exists = existsResp.Exists itemMaps = append(itemMaps, maps.Map{ - "id": item.Id, - "name": item.Name, - "isOn": item.IsOn, - "period": item.Period, - "periodUnit": item.PeriodUnit, - "keys": item.Keys, - "value": item.Value, - "category": item.Category, - "isChecked": exists, + "id": item.Id, + "name": item.Name, + "isOn": item.IsOn, + "period": item.Period, + "periodUnit": item.PeriodUnit, + "periodUnitName": serverconfigs.FindMetricPeriodUnitName(item.PeriodUnit), + "keys": item.Keys, + "value": item.Value, + "valueName": serverconfigs.FindMetricValueName(item.Category, item.Value), + "category": item.Category, + "isChecked": exists, }) } this.Data["items"] = itemMaps diff --git a/internal/web/actions/default/clusters/cluster/settings/metrics/index.go b/internal/web/actions/default/clusters/cluster/settings/metrics/index.go index 9e247d82..d464dce9 100644 --- a/internal/web/actions/default/clusters/cluster/settings/metrics/index.go +++ b/internal/web/actions/default/clusters/cluster/settings/metrics/index.go @@ -5,6 +5,7 @@ package metrics import ( "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" ) @@ -38,14 +39,16 @@ func (this *IndexAction) RunGet(params struct { var itemMaps = []maps.Map{} for _, item := range itemsResp.MetricItems { itemMaps = append(itemMaps, maps.Map{ - "id": item.Id, - "name": item.Name, - "isOn": item.IsOn, - "period": item.Period, - "periodUnit": item.PeriodUnit, - "keys": item.Keys, - "value": item.Value, - "category": item.Category, + "id": item.Id, + "name": item.Name, + "isOn": item.IsOn, + "period": item.Period, + "periodUnit": item.PeriodUnit, + "periodUnitName": serverconfigs.FindMetricPeriodUnitName(item.PeriodUnit), + "keys": item.Keys, + "value": item.Value, + "valueName": serverconfigs.FindMetricValueName(item.Category, item.Value), + "category": item.Category, }) } this.Data["items"] = itemMaps diff --git a/internal/web/actions/default/clusters/clusterutils/cluster_helper.go b/internal/web/actions/default/clusters/clusterutils/cluster_helper.go index a3c2fab4..43c98831 100644 --- a/internal/web/actions/default/clusters/clusterutils/cluster_helper.go +++ b/internal/web/actions/default/clusters/clusterutils/cluster_helper.go @@ -59,7 +59,10 @@ func (this *ClusterHelper) BeforeAction(actionPtr actions.ActionWrapper) (goNext tabbar := actionutils.NewTabbar() tabbar.Add("集群列表", "", "/clusters", "", false) - tabbar.Add("集群节点", "", "/clusters/cluster?clusterId="+clusterIdString, "server", selectedTabbar == "node") + if teaconst.IsPlus { + tabbar.Add("集群看板", "", "/clusters/cluster/boards?clusterId="+clusterIdString, "board", selectedTabbar == "board") + } + tabbar.Add("集群节点", "", "/clusters/cluster/nodes?clusterId="+clusterIdString, "server", selectedTabbar == "node") tabbar.Add("集群设置", "", "/clusters/cluster/settings?clusterId="+clusterIdString, "setting", selectedTabbar == "setting") tabbar.Add("删除集群", "", "/clusters/cluster/delete?clusterId="+clusterIdString, "trash", selectedTabbar == "delete") diff --git a/internal/web/actions/default/clusters/index.go b/internal/web/actions/default/clusters/index.go index 38abfb86..93de9088 100644 --- a/internal/web/actions/default/clusters/index.go +++ b/internal/web/actions/default/clusters/index.go @@ -115,6 +115,12 @@ func (this *IndexAction) RunGet(params struct { } } + // 服务数 + countServersResp, err := this.RPC().ServerRPC().CountAllEnabledServersWithNodeClusterId(this.AdminContext(), &pb.CountAllEnabledServersWithNodeClusterIdRequest{NodeClusterId: cluster.Id}) + if err != nil { + this.ErrorPage(err) + } + clusterMaps = append(clusterMaps, maps.Map{ "id": cluster.Id, "name": cluster.Name, @@ -125,6 +131,7 @@ func (this *IndexAction) RunGet(params struct { "dnsDomainId": cluster.DnsDomainId, "dnsName": cluster.DnsName, "dnsDomainName": dnsDomainName, + "countServers": countServersResp.Count, }) } } diff --git a/internal/web/actions/default/servers/metrics/metricutils/utils.go b/internal/web/actions/default/servers/metrics/metricutils/utils.go index e3132053..61e3937a 100644 --- a/internal/web/actions/default/servers/metrics/metricutils/utils.go +++ b/internal/web/actions/default/servers/metrics/metricutils/utils.go @@ -25,6 +25,13 @@ func InitItem(parent *actionutils.ParentAction, itemId int64) (*pb.MetricItem, e if item == nil { return nil, errors.New("metric item not found") } + + countChartsResp, err := client.MetricChartRPC().CountEnabledMetricCharts(parent.AdminContext(), &pb.CountEnabledMetricChartsRequest{MetricItemId: item.Id}) + if err != nil { + return nil, err + } + var countCharts = countChartsResp.Count + parent.Data["item"] = maps.Map{ "id": item.Id, "name": item.Name, @@ -36,6 +43,7 @@ func InitItem(parent *actionutils.ParentAction, itemId int64) (*pb.MetricItem, e "periodUnit": item.PeriodUnit, "periodUnitName": serverconfigs.FindMetricPeriodUnitName(item.PeriodUnit), "category": item.Category, + "countCharts": countCharts, } return item, nil } diff --git a/internal/web/actions/default/ui/showTip.go b/internal/web/actions/default/ui/showTip.go index 24355d30..738b5844 100644 --- a/internal/web/actions/default/ui/showTip.go +++ b/internal/web/actions/default/ui/showTip.go @@ -13,7 +13,7 @@ import ( var tipKeyMap = map[string]bool{} var tipKeyLocker = sync.Mutex{} -var tipConfigFile = "tip.json" +var tipConfigFile = "tip.cache.json" func init() { TeaGo.BeforeStart(func(server *TeaGo.Server) { diff --git a/web/public/js/utils.js b/web/public/js/utils.js index 3bee6cc1..0a655daf 100644 --- a/web/public/js/utils.js +++ b/web/public/js/utils.js @@ -353,5 +353,122 @@ window.teaweb = { }, reload: function () { window.location.reload() + }, + renderBarChart: function (options) { + let chartId = options.id + let name = options.name + let values = options.values + let xFunc = options.x + let tooltipFunc = options.tooltip + let axis = options.axis + let valueFunc = options.value + + let chartBox = document.getElementById(chartId) + if (chartBox == null) { + return + } + let chart = echarts.init(chartBox) + let option = { + xAxis: { + data: values.map(xFunc), + axisLabel: { + interval: 0 + } + }, + yAxis: { + axisLabel: { + formatter: function (value) { + return value + axis.unit + } + } + }, + tooltip: { + show: true, + trigger: "item", + formatter: function (args) { + return tooltipFunc.apply(this, [args, values]) + } + }, + grid: { + left: 40, + top: 10, + right: 20, + bottom: 20 + }, + series: [ + { + name: name, + type: "bar", + data: values.map(valueFunc), + itemStyle: { + color: "#9DD3E8" + }, + barWidth: "20em" + } + ], + animation: true + } + chart.setOption(option) + chart.resize() + }, + renderLineChart: function (options) { + let chartId = options.id + let name = options.name + let values = options.values + let xFunc = options.x + let tooltipFunc = options.tooltip + let axis = options.axis + let valueFunc = options.value + let max = options.max + let interval = options.interval + + let chartBox = document.getElementById(chartId) + if (chartBox == null) { + return + } + let chart = echarts.init(chartBox) + let option = { + xAxis: { + data: values.map(xFunc), + axisLabel: { + interval: interval + } + }, + yAxis: { + axisLabel: { + formatter: function (value) { + return value + axis.unit + } + }, + max: max + }, + tooltip: { + show: true, + trigger: "item", + formatter: function (args) { + return tooltipFunc.apply(this, [args, values]) + } + }, + grid: { + left: 40, + top: 10, + right: 20, + bottom: 20 + }, + series: [ + { + name: name, + type: "line", + data: values.map(valueFunc), + itemStyle: { + color: "#9DD3E8" + }, + areaStyle: {} + } + ], + animation: true + } + chart.setOption(option) + chart.resize() } -}; +} diff --git a/web/views/@default/clusters/cluster/@menu.html b/web/views/@default/clusters/cluster/@menu.html index 76659ff6..a81dddc1 100644 --- a/web/views/@default/clusters/cluster/@menu.html +++ b/web/views/@default/clusters/cluster/@menu.html @@ -1,5 +1,5 @@ - 节点列表 + 节点列表 创建节点 安装升级 节点分组 diff --git a/web/views/@default/clusters/cluster/boards/index.css b/web/views/@default/clusters/cluster/boards/index.css new file mode 100644 index 00000000..34f8ab5b --- /dev/null +++ b/web/views/@default/clusters/cluster/boards/index.css @@ -0,0 +1,32 @@ +.grid { + margin-top: 2em !important; + margin-left: 2em !important; +} +.grid .column { + margin-bottom: 2em; + border-right: 1px #eee solid; +} +.grid .column div.value { + margin-top: 1.5em; +} +.grid .column div.value span { + font-size: 2em; + margin-right: 0.2em; +} +.grid .column.no-border { + border-right: 0; +} +.grid h4 a { + display: none; +} +.grid .column:hover a { + display: inline; +} +.chart-box { + height: 20em; +} +h4 span { + font-size: 0.8em; + color: grey; +} +/*# sourceMappingURL=index.css.map */ \ No newline at end of file diff --git a/web/views/@default/clusters/cluster/boards/index.css.map b/web/views/@default/clusters/cluster/boards/index.css.map new file mode 100644 index 00000000..1f562066 --- /dev/null +++ b/web/views/@default/clusters/cluster/boards/index.css.map @@ -0,0 +1 @@ +{"version":3,"sources":["index.less"],"names":[],"mappings":"AAAA;EACC,0BAAA;EACA,2BAAA;;AAFD,KAIC;EACC,kBAAA;EACA,4BAAA;;AANF,KAIC,QAIC,IAAG;EACF,iBAAA;;AATH,KAIC,QAIC,IAAG,MAGF;EACC,cAAA;EACA,mBAAA;;AAbJ,KAkBC,QAAO;EACN,eAAA;;AAnBF,KAsBC,GACC;EACC,aAAA;;AAxBH,KA4BC,QAAO,MACN;EACC,eAAA;;AAKH;EACC,YAAA;;AAGD,EACC;EACC,gBAAA;EACA,WAAA","file":"index.css"} \ No newline at end of file diff --git a/web/views/@default/clusters/cluster/boards/index.html b/web/views/@default/clusters/cluster/boards/index.html new file mode 100644 index 00000000..551bbb98 --- /dev/null +++ b/web/views/@default/clusters/cluster/boards/index.html @@ -0,0 +1,69 @@ +{$layout} +{$template "/echarts"} + +
+
+

在线节点

+
{{board.countActiveNodes}}
+
+
+

离线节点

+
{{board.countInactiveNodes}}
+
+
+

服务

+
{{board.countServers}}
+
+
+

用户

+
{{board.countUsers}}
+
+
+ + + + +
+ + +
+ +
+ + + + +
+ + +
+ +
+ + +

域名排行 (24小时)

+
+ +
+ + +

节点排行 (24小时)

+
+ +
+ + + +
+
+
\ No newline at end of file diff --git a/web/views/@default/clusters/cluster/boards/index.js b/web/views/@default/clusters/cluster/boards/index.js new file mode 100644 index 00000000..dd6f8cb7 --- /dev/null +++ b/web/views/@default/clusters/cluster/boards/index.js @@ -0,0 +1,405 @@ +Tea.context(function () { + /** + * 流量统计 + */ + this.trafficTab = "hourly" + + this.$delay(function () { + this.reloadHourlyTrafficChart() + this.reloadHourlyRequestsChart() + this.reloadTopNodeStatsChart() + this.reloadTopDomainsChart() + this.reloadCPUChart() + }) + + this.selectTrafficTab = function (tab) { + this.trafficTab = tab + if (tab == "hourly") { + this.$delay(function () { + this.reloadHourlyTrafficChart() + }) + } else if (tab == "daily") { + this.$delay(function () { + this.reloadDailyTrafficChart() + }) + } + } + + this.reloadHourlyTrafficChart = function () { + let stats = this.hourlyStats + this.reloadTrafficChart("hourly-traffic-chart", "流量统计", stats, function (args) { + if (args.seriesIndex == 0) { + return stats[args.dataIndex].day + " " + stats[args.dataIndex].hour + "时 流量: " + teaweb.formatBytes(stats[args.dataIndex].bytes) + } + if (args.seriesIndex == 1) { + let ratio = 0 + if (stats[args.dataIndex].bytes > 0) { + ratio = Math.round(stats[args.dataIndex].cachedBytes * 10000 / stats[args.dataIndex].bytes) / 100 + } + return stats[args.dataIndex].day + " " + stats[args.dataIndex].hour + "时 缓存流量: " + teaweb.formatBytes(stats[args.dataIndex].cachedBytes) + ", 命中率:" + ratio + "%" + } + return "" + }) + } + + this.reloadDailyTrafficChart = function () { + let stats = this.dailyStats + this.reloadTrafficChart("daily-traffic-chart", "流量统计", stats, function (args) { + if (args.seriesIndex == 0) { + return stats[args.dataIndex].day + " 流量: " + teaweb.formatBytes(stats[args.dataIndex].bytes) + } + if (args.seriesIndex == 1) { + let ratio = 0 + if (stats[args.dataIndex].bytes > 0) { + ratio = Math.round(stats[args.dataIndex].cachedBytes * 10000 / stats[args.dataIndex].bytes) / 100 + } + return stats[args.dataIndex].day + " 缓存流量: " + teaweb.formatBytes(stats[args.dataIndex].cachedBytes) + ", 命中率:" + ratio + "%" + } + return "" + }) + } + + this.reloadTrafficChart = function (chartId, name, stats, tooltipFunc) { + let chartBox = document.getElementById(chartId) + if (chartBox == null) { + return + } + + let axis = teaweb.bytesAxis(stats, function (v) { + return Math.max(v.bytes, v.cachedBytes) + }) + + let chart = echarts.init(chartBox) + let option = { + xAxis: { + data: stats.map(function (v) { + if (v.hour != null) { + return v.hour + } + return v.day + }) + }, + yAxis: { + axisLabel: { + formatter: function (value) { + return value + axis.unit + } + } + }, + tooltip: { + show: true, + trigger: "item", + formatter: tooltipFunc + }, + grid: { + left: 50, + top: 40, + right: 20, + bottom: 20 + }, + series: [ + { + name: "流量", + type: "line", + data: stats.map(function (v) { + return v.bytes / axis.divider + }), + itemStyle: { + color: "#9DD3E8" + }, + areaStyle: { + color: "#9DD3E8" + } + }, + { + name: "缓存流量", + type: "line", + data: stats.map(function (v) { + return v.cachedBytes / axis.divider + }), + itemStyle: { + color: "#61A0A8" + }, + areaStyle: { + color: "#61A0A8" + } + } + ], + legend: { + data: ['流量', '缓存流量'] + }, + animation: true + } + chart.setOption(option) + chart.resize() + } + + /** + * 请求数统计 + */ + this.requestsTab = "hourly" + + this.selectRequestsTab = function (tab) { + this.requestsTab = tab + if (tab == "hourly") { + this.$delay(function () { + this.reloadHourlyRequestsChart() + }) + } else if (tab == "daily") { + this.$delay(function () { + this.reloadDailyRequestsChart() + }) + } + } + + this.reloadHourlyRequestsChart = function () { + let stats = this.hourlyStats + this.reloadRequestsChart("hourly-requests-chart", "请求数统计", stats, function (args) { + if (args.seriesIndex == 0) { + return stats[args.dataIndex].day + " " + stats[args.dataIndex].hour + "时 请求数: " + teaweb.formatNumber(stats[args.dataIndex].countRequests) + } + if (args.seriesIndex == 1) { + let ratio = 0 + if (stats[args.dataIndex].countRequests > 0) { + ratio = Math.round(stats[args.dataIndex].countCachedRequests * 10000 / stats[args.dataIndex].countRequests) / 100 + } + return stats[args.dataIndex].day + " " + stats[args.dataIndex].hour + "时 缓存请求数: " + teaweb.formatNumber(stats[args.dataIndex].countCachedRequests) + ", 命中率:" + ratio + "%" + } + return "" + }) + } + + this.reloadDailyRequestsChart = function () { + let stats = this.dailyStats + this.reloadRequestsChart("daily-requests-chart", "请求数统计", stats, function (args) { + if (args.seriesIndex == 0) { + return stats[args.dataIndex].day + " 请求数: " + teaweb.formatNumber(stats[args.dataIndex].countRequests) + } + if (args.seriesIndex == 1) { + let ratio = 0 + if (stats[args.dataIndex].countRequests > 0) { + ratio = Math.round(stats[args.dataIndex].countCachedRequests * 10000 / stats[args.dataIndex].countRequests) / 100 + } + return stats[args.dataIndex].day + " 缓存请求数: " + teaweb.formatNumber(stats[args.dataIndex].countCachedRequests) + ", 命中率:" + ratio + "%" + } + return "" + }) + } + + this.reloadRequestsChart = function (chartId, name, stats, tooltipFunc) { + let chartBox = document.getElementById(chartId) + if (chartBox == null) { + return + } + + let axis = teaweb.countAxis(stats, function (v) { + return Math.max(v.countRequests, v.countCachedRequests) + }) + + let chart = echarts.init(chartBox) + let option = { + xAxis: { + data: stats.map(function (v) { + if (v.hour != null) { + return v.hour + } + if (v.day != null) { + return v.day + } + return "" + }) + }, + yAxis: { + axisLabel: { + formatter: function (value) { + return value + axis.unit + } + } + }, + tooltip: { + show: true, + trigger: "item", + formatter: tooltipFunc + }, + grid: { + left: 50, + top: 40, + right: 20, + bottom: 20 + }, + series: [ + { + name: "请求数", + type: "line", + data: stats.map(function (v) { + return v.countRequests / axis.divider + }), + itemStyle: { + color: "#9DD3E8" + }, + areaStyle: { + color: "#9DD3E8" + } + }, + { + name: "缓存请求数", + type: "line", + data: stats.map(function (v) { + return v.countCachedRequests / axis.divider + }), + itemStyle: { + color: "#61A0A8" + }, + areaStyle: { + color: "#61A0A8" + } + } + ], + legend: { + data: ['请求数', '缓存请求数'] + }, + animation: true + } + chart.setOption(option) + chart.resize() + } + + // 节点排行 + this.reloadTopNodeStatsChart = function () { + let that = this + let axis = teaweb.countAxis(this.topNodeStats, function (v) { + return v.countRequests + }) + teaweb.renderBarChart({ + id: "top-nodes-chart", + name: "节点", + values: this.topNodeStats, + x: function (v) { + return v.nodeName + }, + tooltip: function (args, stats) { + return stats[args.dataIndex].nodeName + "
请求数:" + " " + teaweb.formatNumber(stats[args.dataIndex].countRequests) + "
流量:" + teaweb.formatBytes(stats[args.dataIndex].bytes) + }, + value: function (v) { + return v.countRequests / axis.divider; + }, + axis: axis + }) + } + + // 域名排行 + this.reloadTopDomainsChart = function () { + let axis = teaweb.countAxis(this.topDomainStats, function (v) { + return v.countRequests + }) + teaweb.renderBarChart({ + id: "top-domains-chart", + name: "域名", + values: this.topDomainStats, + x: function (v) { + return v.domain + }, + tooltip: function (args, stats) { + return stats[args.dataIndex].domain + "
请求数:" + " " + teaweb.formatNumber(stats[args.dataIndex].countRequests) + "
流量:" + teaweb.formatBytes(stats[args.dataIndex].bytes) + }, + value: function (v) { + return v.countRequests / axis.divider; + }, + axis: axis + }) + } + + /** + * 系统信息 + */ + this.nodeStatusTab = "cpu" + + this.selectNodeStatusTab = function (tab) { + this.nodeStatusTab = tab + this.$delay(function () { + switch (tab) { + case "cpu": + this.reloadCPUChart() + break + case "memory": + this.reloadMemoryChart() + break + case "load": + this.reloadLoadChart() + break + } + }) + } + + this.reloadCPUChart = function () { + let axis = {unit: "", divider: 1} + teaweb.renderLineChart({ + id: "cpu-chart", + name: "CPU", + values: this.cpuValues, + x: function (v) { + return v.time + }, + tooltip: function (args, stats) { + return stats[args.dataIndex].time + ":" + (Math.ceil(stats[args.dataIndex].value * 100 * 100) / 100) + "%" + }, + value: function (v) { + return v.value * 100; + }, + axis: axis, + max: 100 + }) + } + + this.reloadMemoryChart = function () { + let axis = {unit: "", divider: 1} + teaweb.renderLineChart({ + id: "memory-chart", + name: "内存", + values: this.memoryValues, + x: function (v) { + return v.time + }, + tooltip: function (args, stats) { + return stats[args.dataIndex].time + ":" + (Math.ceil(stats[args.dataIndex].value * 100 * 100) / 100) + "%" + }, + value: function (v) { + return v.value * 100; + }, + axis: axis, + max: 100 + }) + } + + this.reloadLoadChart = function () { + let axis = {unit: "", divider: 1} + let max = this.loadValues.$map(function (k, v) { + return v.value + }).$max() + if (max < 10) { + max = 10 + } else if (max < 20) { + max = 20 + } else if (max < 100) { + max = 100 + } else { + max = null + } + teaweb.renderLineChart({ + id: "load-chart", + name: "负载", + values: this.loadValues, + x: function (v) { + return v.time + }, + tooltip: function (args, stats) { + return stats[args.dataIndex].time + ":" + (Math.ceil(stats[args.dataIndex].value * 100) / 100) + }, + value: function (v) { + return v.value; + }, + axis: axis, + max: max + }) + } +}) \ No newline at end of file diff --git a/web/views/@default/clusters/cluster/boards/index.less b/web/views/@default/clusters/cluster/boards/index.less new file mode 100644 index 00000000..5a20c7a5 --- /dev/null +++ b/web/views/@default/clusters/cluster/boards/index.less @@ -0,0 +1,45 @@ +.grid { + margin-top: 2em !important; + margin-left: 2em !important; + + .column { + margin-bottom: 2em; + border-right: 1px #eee solid; + + div.value { + margin-top: 1.5em; + + span { + font-size: 2em; + margin-right: 0.2em; + } + } + } + + .column.no-border { + border-right: 0; + } + + h4 { + a { + display: none; + } + } + + .column:hover { + a { + display: inline; + } + } +} + +.chart-box { + height: 20em; +} + +h4 { + span { + font-size: 0.8em; + color: grey; + } +} \ No newline at end of file diff --git a/web/views/@default/clusters/cluster/createBatch.js b/web/views/@default/clusters/cluster/createBatch.js index c87ab581..8fb6a365 100644 --- a/web/views/@default/clusters/cluster/createBatch.js +++ b/web/views/@default/clusters/cluster/createBatch.js @@ -1,5 +1,5 @@ Tea.context(function () { - this.success = NotifySuccess("保存成功", "/clusters/cluster?clusterId=" + this.clusterId) + this.success = NotifySuccess("保存成功", "/clusters/cluster/nodes?clusterId=" + this.clusterId) this.$delay(function () { this.$refs.ipList.focus() diff --git a/web/views/@default/clusters/cluster/createNode.js b/web/views/@default/clusters/cluster/createNode.js index ad374f8d..497b92c3 100644 --- a/web/views/@default/clusters/cluster/createNode.js +++ b/web/views/@default/clusters/cluster/createNode.js @@ -1,3 +1,3 @@ Tea.context(function () { - this.success = NotifySuccess("保存成功", "/clusters/cluster?clusterId=" + this.clusterId); + this.success = NotifySuccess("保存成功", "/clusters/cluster/nodes?clusterId=" + this.clusterId); }); \ No newline at end of file diff --git a/web/views/@default/clusters/cluster/index.css.map b/web/views/@default/clusters/cluster/index.css.map deleted file mode 100644 index b42db3f1..00000000 --- a/web/views/@default/clusters/cluster/index.css.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"sources":["index.less"],"names":[],"mappings":"AAAA,MAAO;EACN,oBAAA;;AAGD,CACC;EACC,iCAAA","file":"index.css"} \ No newline at end of file diff --git a/web/views/@default/clusters/cluster/node/@node_menu.html b/web/views/@default/clusters/cluster/node/@node_menu.html index 21833c54..8226377c 100644 --- a/web/views/@default/clusters/cluster/node/@node_menu.html +++ b/web/views/@default/clusters/cluster/node/@node_menu.html @@ -1,5 +1,5 @@ - 节点列表 + 节点列表 | 节点详情 监控图表 diff --git a/web/views/@default/clusters/cluster/index.css b/web/views/@default/clusters/cluster/nodes.css similarity index 70% rename from web/views/@default/clusters/cluster/index.css rename to web/views/@default/clusters/cluster/nodes.css index ae4b6bbb..7388dd91 100644 --- a/web/views/@default/clusters/cluster/index.css +++ b/web/views/@default/clusters/cluster/nodes.css @@ -4,4 +4,4 @@ a .red { border-bottom: 1px #db2828 dashed; } -/*# sourceMappingURL=index.css.map */ \ No newline at end of file +/*# sourceMappingURL=nodes.css.map */ \ No newline at end of file diff --git a/web/views/@default/clusters/cluster/nodes.css.map b/web/views/@default/clusters/cluster/nodes.css.map new file mode 100644 index 00000000..68cc3cd6 --- /dev/null +++ b/web/views/@default/clusters/cluster/nodes.css.map @@ -0,0 +1 @@ +{"version":3,"sources":["nodes.less"],"names":[],"mappings":"AAAA,MAAO;EACN,oBAAA;;AAGD,CACC;EACC,iCAAA","file":"nodes.css"} \ No newline at end of file diff --git a/web/views/@default/clusters/cluster/index.html b/web/views/@default/clusters/cluster/nodes.html similarity index 100% rename from web/views/@default/clusters/cluster/index.html rename to web/views/@default/clusters/cluster/nodes.html diff --git a/web/views/@default/clusters/cluster/index.js b/web/views/@default/clusters/cluster/nodes.js similarity index 100% rename from web/views/@default/clusters/cluster/index.js rename to web/views/@default/clusters/cluster/nodes.js diff --git a/web/views/@default/clusters/cluster/index.less b/web/views/@default/clusters/cluster/nodes.less similarity index 100% rename from web/views/@default/clusters/cluster/index.less rename to web/views/@default/clusters/cluster/nodes.less diff --git a/web/views/@default/clusters/cluster/settings/metrics/createPopup.html b/web/views/@default/clusters/cluster/settings/metrics/createPopup.html index d4b57be3..4b4a1aad 100644 --- a/web/views/@default/clusters/cluster/settings/metrics/createPopup.html +++ b/web/views/@default/clusters/cluster/settings/metrics/createPopup.html @@ -17,13 +17,13 @@ {{item.name}} - {{key}} +
- {{item.period}} {{item.periodUnit}} + {{item.period}} {{item.periodUnitName}} - {{item.value}} + {{item.valueName}} diff --git a/web/views/@default/clusters/cluster/settings/metrics/index.html b/web/views/@default/clusters/cluster/settings/metrics/index.html index 0d7141eb..860db1e0 100644 --- a/web/views/@default/clusters/cluster/settings/metrics/index.html +++ b/web/views/@default/clusters/cluster/settings/metrics/index.html @@ -9,7 +9,7 @@ | [添加指标] | - +

暂时还没有添加指标。

@@ -28,13 +28,13 @@ {{item.name}} - {{key}} +
- {{item.period}} {{item.periodUnit}} + {{item.period}} {{item.periodUnitName}} - {{item.value}} + {{item.valueName}} diff --git a/web/views/@default/clusters/index.html b/web/views/@default/clusters/index.html index 0bd89e0c..57c4fdc4 100644 --- a/web/views/@default/clusters/index.html +++ b/web/views/@default/clusters/index.html @@ -35,6 +35,7 @@ 集群名称 节点数 在线节点数 + 服务数 DNS域名 操作 @@ -42,7 +43,7 @@ {{cluster.name}} - {{cluster.countAllNodes}} + {{cluster.countAllNodes}} -
@@ -50,7 +51,11 @@
- {{cluster.countActiveNodes}} + {{cluster.countActiveNodes}} + - + + + {{cluster.countServers}} - diff --git a/web/views/@default/dashboard/index.css.map b/web/views/@default/dashboard/index.css.map index 4bc232da..53288e00 100644 --- a/web/views/@default/dashboard/index.css.map +++ b/web/views/@default/dashboard/index.css.map @@ -1 +1 @@ -{"version":3,"sources":["index.less"],"names":[],"mappings":"AAAA,GAAG,QACF;EACC,kBAAA;EACA,UAAA;EACA,UAAA;;AAIF;EACC,0BAAA;EACA,2BAAA;;AAFD,KAIC;EACC,kBAAA;EACA,4BAAA;;AANF,KAIC,QAIC,IAAG;EACF,iBAAA;;AATH,KAIC,QAIC,IAAG,MAGF;EACC,cAAA;EACA,mBAAA;;AAbJ,KAkBC,QAAO;EACN,eAAA;;AAnBF,KAsBC,GACC;EACC,aAAA;;AAxBH,KA4BC,QAAO,MACN;EACC,eAAA;;AAMH;EACC,YAAA","file":"index.css"} \ No newline at end of file +{"version":3,"sources":["index.less"],"names":[],"mappings":"AAAA,GAAG,QACF;EACC,kBAAA;EACA,UAAA;EACA,UAAA;;AAIF;EACC,0BAAA;EACA,2BAAA;;AAFD,KAIC;EACC,kBAAA;EACA,4BAAA;;AANF,KAIC,QAIC,IAAG;EACF,iBAAA;;AATH,KAIC,QAIC,IAAG,MAGF;EACC,cAAA;EACA,mBAAA;;AAbJ,KAkBC,QAAO;EACN,eAAA;;AAnBF,KAsBC,GACC;EACC,aAAA;;AAxBH,KA4BC,QAAO,MACN;EACC,eAAA;;AAKH;EACC,YAAA","file":"index.css"} \ No newline at end of file diff --git a/web/views/@default/dashboard/index.html b/web/views/@default/dashboard/index.html index 4b17d5be..0bb1062b 100644 --- a/web/views/@default/dashboard/index.html +++ b/web/views/@default/dashboard/index.html @@ -45,8 +45,8 @@
diff --git a/web/views/@default/dashboard/index.js b/web/views/@default/dashboard/index.js index 5746611c..fd3e5675 100644 --- a/web/views/@default/dashboard/index.js +++ b/web/views/@default/dashboard/index.js @@ -1,7 +1,6 @@ Tea.context(function () { this.trafficTab = "hourly" - this.$delay(function () { this.reloadHourlyTrafficChart() diff --git a/web/views/@default/dashboard/index.less b/web/views/@default/dashboard/index.less index 3466d0fc..fcb67336 100644 --- a/web/views/@default/dashboard/index.less +++ b/web/views/@default/dashboard/index.less @@ -41,7 +41,6 @@ } } - .chart-box { height: 20em; } \ No newline at end of file diff --git a/web/views/@default/servers/@menu.html b/web/views/@default/servers/@menu.html index 2ef0004e..cb1c9002 100644 --- a/web/views/@default/servers/@menu.html +++ b/web/views/@default/servers/@menu.html @@ -1,5 +1,5 @@ 服务列表 审核中({{countAuditing}}) - 创建服务 + 创建网站服务 \ No newline at end of file diff --git a/web/views/@default/servers/metrics/@item_menu.html b/web/views/@default/servers/metrics/@item_menu.html index 2d9efb4d..e4d0a16c 100644 --- a/web/views/@default/servers/metrics/@item_menu.html +++ b/web/views/@default/servers/metrics/@item_menu.html @@ -3,6 +3,6 @@ | "{{item.name}}"详情 修改 - 图表 + 图表({{item.countCharts}}) 数据 \ No newline at end of file