mirror of
https://github.com/TeaOSLab/EdgeAdmin.git
synced 2025-11-02 20:00:26 +08:00
实现节点看板(仅对企业版开放)
This commit is contained in:
@@ -1,7 +1,7 @@
|
|||||||
# GoEdge目标
|
# GoEdge目标
|
||||||
做一款人人用得起的CDN & WAF系统。
|
做一款人人用得起的CDN & WAF系统。
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
## 特性
|
## 特性
|
||||||
* `免费` - 开源、免费、自由、开放
|
* `免费` - 开源、免费、自由、开放
|
||||||
|
|||||||
@@ -1,7 +0,0 @@
|
|||||||
package rpc
|
|
||||||
|
|
||||||
import "context"
|
|
||||||
|
|
||||||
type ContextInterface interface {
|
|
||||||
AdminContext() context.Context
|
|
||||||
}
|
|
||||||
15
internal/web/actions/actionutils/action_interface.go
Normal file
15
internal/web/actions/actionutils/action_interface.go
Normal file
@@ -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
|
||||||
|
}
|
||||||
@@ -10,6 +10,7 @@ import (
|
|||||||
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/dao"
|
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/dao"
|
||||||
"github.com/iwind/TeaGo/actions"
|
"github.com/iwind/TeaGo/actions"
|
||||||
"github.com/iwind/TeaGo/logs"
|
"github.com/iwind/TeaGo/logs"
|
||||||
|
"github.com/iwind/TeaGo/maps"
|
||||||
"net/http"
|
"net/http"
|
||||||
"strconv"
|
"strconv"
|
||||||
)
|
)
|
||||||
@@ -126,3 +127,8 @@ func (this *ParentAction) AdminContext() context.Context {
|
|||||||
}
|
}
|
||||||
return this.rpcClient.Context(this.AdminId())
|
return this.rpcClient.Context(this.AdminId())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ViewData 视图里可以使用的数据
|
||||||
|
func (this *ParentAction) ViewData() maps.Map {
|
||||||
|
return this.Data
|
||||||
|
}
|
||||||
|
|||||||
@@ -3,11 +3,14 @@
|
|||||||
package boards
|
package boards
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
teaconst "github.com/TeaOSLab/EdgeAdmin/internal/const"
|
||||||
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
|
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
|
||||||
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
|
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
|
||||||
|
"github.com/TeaOSLab/EdgeCommon/pkg/serverconfigs"
|
||||||
"github.com/iwind/TeaGo/maps"
|
"github.com/iwind/TeaGo/maps"
|
||||||
"github.com/iwind/TeaGo/types"
|
"github.com/iwind/TeaGo/types"
|
||||||
timeutil "github.com/iwind/TeaGo/utils/time"
|
timeutil "github.com/iwind/TeaGo/utils/time"
|
||||||
|
"strconv"
|
||||||
)
|
)
|
||||||
|
|
||||||
type IndexAction struct {
|
type IndexAction struct {
|
||||||
@@ -21,6 +24,11 @@ func (this *IndexAction) Init() {
|
|||||||
func (this *IndexAction) RunGet(params struct {
|
func (this *IndexAction) RunGet(params struct {
|
||||||
ClusterId int64
|
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})
|
resp, err := this.RPC().ServerStatBoardRPC().ComposeServerStatNodeClusterBoard(this.AdminContext(), &pb.ComposeServerStatNodeClusterBoardRequest{NodeClusterId: params.ClusterId})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
this.ErrorPage(err)
|
this.ErrorPage(err)
|
||||||
@@ -128,5 +136,41 @@ func (this *IndexAction) RunGet(params struct {
|
|||||||
this.Data["loadValues"] = statMaps
|
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()
|
this.Show()
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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/boards"
|
||||||
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/default/clusters/cluster/groups"
|
"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"
|
||||||
|
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/monitor"
|
||||||
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/default/clusters/cluster/node/thresholds"
|
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/default/clusters/cluster/node/thresholds"
|
||||||
clusters "github.com/TeaOSLab/EdgeAdmin/internal/web/actions/default/clusters/clusterutils"
|
clusters "github.com/TeaOSLab/EdgeAdmin/internal/web/actions/default/clusters/clusterutils"
|
||||||
@@ -32,34 +33,39 @@ func init() {
|
|||||||
GetPost("/installManual", new(InstallManualAction)).
|
GetPost("/installManual", new(InstallManualAction)).
|
||||||
|
|
||||||
// 节点相关
|
// 节点相关
|
||||||
Get("/node", new(node.IndexAction)).
|
Prefix("/clusters/cluster/node").
|
||||||
GetPost("/node/update", new(node.UpdateAction)).
|
Get("", new(node.IndexAction)).
|
||||||
GetPost("/node/install", new(node.InstallAction)).
|
GetPost("/update", new(node.UpdateAction)).
|
||||||
Post("/node/updateInstallStatus", new(node.UpdateInstallStatusAction)).
|
GetPost("/install", new(node.InstallAction)).
|
||||||
Post("/node/status", new(node.StatusAction)).
|
Post("/updateInstallStatus", new(node.UpdateInstallStatusAction)).
|
||||||
Get("/node/logs", new(node.LogsAction)).
|
Post("/status", new(node.StatusAction)).
|
||||||
Post("/node/start", new(node.StartAction)).
|
Get("/logs", new(node.LogsAction)).
|
||||||
Post("/node/stop", new(node.StopAction)).
|
Post("/start", new(node.StartAction)).
|
||||||
Post("/node/up", new(node.UpAction)).
|
Post("/stop", new(node.StopAction)).
|
||||||
Get("/node/monitor", new(monitor.IndexAction)).
|
Post("/up", new(node.UpAction)).
|
||||||
Post("/node/monitor/cpu", new(monitor.CpuAction)).
|
Get("/monitor", new(monitor.IndexAction)).
|
||||||
Post("/node/monitor/memory", new(monitor.MemoryAction)).
|
Post("/monitor/cpu", new(monitor.CpuAction)).
|
||||||
Post("/node/monitor/load", new(monitor.LoadAction)).
|
Post("/monitor/memory", new(monitor.MemoryAction)).
|
||||||
Post("/node/monitor/trafficIn", new(monitor.TrafficInAction)).
|
Post("/monitor/load", new(monitor.LoadAction)).
|
||||||
Post("/node/monitor/trafficOut", new(monitor.TrafficOutAction)).
|
Post("/monitor/trafficIn", new(monitor.TrafficInAction)).
|
||||||
Post("/node/monitor/connections", new(monitor.ConnectionsAction)).
|
Post("/monitor/trafficOut", new(monitor.TrafficOutAction)).
|
||||||
Get("/node/thresholds", new(thresholds.IndexAction)).
|
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)).
|
Prefix("/clusters/cluster/groups").
|
||||||
GetPost("/groups/createPopup", new(groups.CreatePopupAction)).
|
Get("", new(groups.IndexAction)).
|
||||||
GetPost("/groups/updatePopup", new(groups.UpdatePopupAction)).
|
GetPost("/createPopup", new(groups.CreatePopupAction)).
|
||||||
Post("/groups/delete", new(groups.DeleteAction)).
|
GetPost("/updatePopup", new(groups.UpdatePopupAction)).
|
||||||
Post("/groups/sort", new(groups.SortAction)).
|
Post("/delete", new(groups.DeleteAction)).
|
||||||
GetPost("/groups/selectPopup", new(groups.SelectPopupAction)).
|
Post("/sort", new(groups.SortAction)).
|
||||||
|
GetPost("/selectPopup", new(groups.SelectPopupAction)).
|
||||||
|
|
||||||
// 看板相关
|
// 看板相关
|
||||||
Get("/boards", new(boards.IndexAction)).
|
Prefix("/clusters/cluster/boards").
|
||||||
|
Get("", new(boards.IndexAction)).
|
||||||
|
|
||||||
EndAll()
|
EndAll()
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -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()
|
||||||
|
}
|
||||||
259
internal/web/actions/default/clusters/cluster/node/detail.go
Normal file
259
internal/web/actions/default/clusters/cluster/node/detail.go
Normal file
@@ -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()
|
||||||
|
}
|
||||||
@@ -1,15 +1,11 @@
|
|||||||
package node
|
package node
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"encoding/json"
|
|
||||||
"fmt"
|
"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/actionutils"
|
||||||
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/default/clusters/grants/grantutils"
|
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/default/clusters/cluster/node/nodeutils"
|
||||||
"github.com/TeaOSLab/EdgeCommon/pkg/nodeconfigs"
|
"strconv"
|
||||||
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
|
|
||||||
"github.com/iwind/TeaGo/maps"
|
|
||||||
"time"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
type IndexAction struct {
|
type IndexAction struct {
|
||||||
@@ -24,236 +20,15 @@ func (this *IndexAction) Init() {
|
|||||||
func (this *IndexAction) RunGet(params struct {
|
func (this *IndexAction) RunGet(params struct {
|
||||||
NodeId int64
|
NodeId int64
|
||||||
}) {
|
}) {
|
||||||
this.Data["nodeId"] = params.NodeId
|
err := nodeutils.InitNodeInfo(this, params.NodeId)
|
||||||
|
|
||||||
nodeResp, err := this.RPC().NodeRPC().FindEnabledNode(this.AdminContext(), &pb.FindEnabledNodeRequest{NodeId: params.NodeId})
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
this.ErrorPage(err)
|
this.ErrorPage(err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
node := nodeResp.Node
|
|
||||||
if node == nil {
|
|
||||||
this.WriteString("找不到要操作的节点")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
var clusterMap maps.Map = nil
|
if teaconst.IsPlus {
|
||||||
if node.NodeCluster != nil {
|
this.RedirectURL("/clusters/cluster/node/boards?clusterId=" + fmt.Sprintf("%d", this.Data["clusterId"]) + "&nodeId=" + strconv.FormatInt(params.NodeId, 10))
|
||||||
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 {
|
} else {
|
||||||
this.Data["shouldUpgrade"] = false
|
this.RedirectURL("/clusters/cluster/node/detail?clusterId=" + fmt.Sprintf("%d", this.Data["clusterId"]) + strconv.FormatInt(params.NodeId, 10))
|
||||||
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()
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ package node
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
|
"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/rpc/pb"
|
||||||
"github.com/iwind/TeaGo/maps"
|
"github.com/iwind/TeaGo/maps"
|
||||||
timeutil "github.com/iwind/TeaGo/utils/time"
|
timeutil "github.com/iwind/TeaGo/utils/time"
|
||||||
@@ -23,6 +24,13 @@ func (this *LogsAction) RunGet(params struct {
|
|||||||
Keyword string
|
Keyword string
|
||||||
Level string
|
Level string
|
||||||
}) {
|
}) {
|
||||||
|
// 初始化节点信息(用于菜单)
|
||||||
|
err := nodeutils.InitNodeInfo(this, params.NodeId)
|
||||||
|
if err != nil {
|
||||||
|
this.ErrorPage(err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
this.Data["nodeId"] = params.NodeId
|
this.Data["nodeId"] = params.NodeId
|
||||||
this.Data["dayFrom"] = params.DayFrom
|
this.Data["dayFrom"] = params.DayFrom
|
||||||
this.Data["dayTo"] = params.DayTo
|
this.Data["dayTo"] = params.DayTo
|
||||||
|
|||||||
@@ -2,7 +2,10 @@
|
|||||||
|
|
||||||
package monitor
|
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 {
|
type IndexAction struct {
|
||||||
actionutils.ParentAction
|
actionutils.ParentAction
|
||||||
@@ -17,5 +20,12 @@ func (this *IndexAction) RunGet(params struct {
|
|||||||
}) {
|
}) {
|
||||||
this.Data["nodeId"] = params.NodeId
|
this.Data["nodeId"] = params.NodeId
|
||||||
|
|
||||||
|
// 初始化节点信息(用于菜单)
|
||||||
|
err := nodeutils.InitNodeInfo(this, params.NodeId)
|
||||||
|
if err != nil {
|
||||||
|
this.ErrorPage(err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
this.Show()
|
this.Show()
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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
|
||||||
|
}
|
||||||
@@ -4,6 +4,7 @@ package thresholds
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
|
"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/nodeconfigs"
|
||||||
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
|
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
|
||||||
"github.com/iwind/TeaGo/maps"
|
"github.com/iwind/TeaGo/maps"
|
||||||
@@ -23,6 +24,13 @@ func (this *IndexAction) RunGet(params struct {
|
|||||||
}) {
|
}) {
|
||||||
this.Data["nodeId"] = params.NodeId
|
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{
|
thresholdsResp, err := this.RPC().NodeThresholdRPC().FindAllEnabledNodeThresholds(this.AdminContext(), &pb.FindAllEnabledNodeThresholdsRequest{
|
||||||
Role: "node",
|
Role: "node",
|
||||||
|
|||||||
@@ -2,7 +2,6 @@ package clusterutils
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
teaconst "github.com/TeaOSLab/EdgeAdmin/internal/const"
|
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/utils/numberutils"
|
||||||
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
|
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
|
||||||
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/dao"
|
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/dao"
|
||||||
@@ -36,7 +35,7 @@ func (this *ClusterHelper) BeforeAction(actionPtr actions.ActionWrapper) (goNext
|
|||||||
action.Data["clusterId"] = clusterId
|
action.Data["clusterId"] = clusterId
|
||||||
|
|
||||||
if clusterId > 0 {
|
if clusterId > 0 {
|
||||||
var ctx = actionPtr.(rpc.ContextInterface).AdminContext()
|
var ctx = actionPtr.(actionutils.ActionInterface).AdminContext()
|
||||||
cluster, err := dao.SharedNodeClusterDAO.FindEnabledNodeCluster(ctx, clusterId)
|
cluster, err := dao.SharedNodeClusterDAO.FindEnabledNodeCluster(ctx, clusterId)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
logs.Error(err)
|
logs.Error(err)
|
||||||
|
|||||||
@@ -39,7 +39,7 @@ func (this *ClusterHelper) BeforeAction(actionPtr actions.ActionWrapper) (goNext
|
|||||||
logs.Error(err)
|
logs.Error(err)
|
||||||
return
|
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,
|
NsClusterId: clusterId,
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
// 指标图表
|
// 指标图表
|
||||||
Vue.component("metric-chart", {
|
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 () {
|
mounted: function () {
|
||||||
this.load()
|
this.load()
|
||||||
},
|
},
|
||||||
@@ -88,7 +88,7 @@ Vue.component("metric-chart", {
|
|||||||
if (stat.total > 0) {
|
if (stat.total > 0) {
|
||||||
percent = Math.round((stat.value * 100 / stat.total) * 100) / 100
|
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: [
|
series: [
|
||||||
@@ -108,6 +108,21 @@ Vue.component("metric-chart", {
|
|||||||
let values = this.stats.map(function (v) {
|
let values = this.stats.map(function (v) {
|
||||||
return v.value
|
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
|
let that = this
|
||||||
chart.setOption({
|
chart.setOption({
|
||||||
xAxis: {
|
xAxis: {
|
||||||
@@ -115,7 +130,13 @@ Vue.component("metric-chart", {
|
|||||||
return that.formatTime(v.time)
|
return that.formatTime(v.time)
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
yAxis: {},
|
yAxis: {
|
||||||
|
axisLabel: {
|
||||||
|
formatter: function (value) {
|
||||||
|
return value + axis.unit
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
tooltip: {
|
tooltip: {
|
||||||
show: true,
|
show: true,
|
||||||
trigger: "item",
|
trigger: "item",
|
||||||
@@ -134,7 +155,9 @@ Vue.component("metric-chart", {
|
|||||||
{
|
{
|
||||||
name: name,
|
name: name,
|
||||||
type: "bar",
|
type: "bar",
|
||||||
data: values,
|
data: values.map(function (v){
|
||||||
|
return v/axis.divider
|
||||||
|
}),
|
||||||
itemStyle: {
|
itemStyle: {
|
||||||
color: "#9DD3E8"
|
color: "#9DD3E8"
|
||||||
},
|
},
|
||||||
@@ -151,6 +174,21 @@ Vue.component("metric-chart", {
|
|||||||
let values = this.stats.map(function (v) {
|
let values = this.stats.map(function (v) {
|
||||||
return v.value
|
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
|
let that = this
|
||||||
chart.setOption({
|
chart.setOption({
|
||||||
xAxis: {
|
xAxis: {
|
||||||
@@ -158,7 +196,13 @@ Vue.component("metric-chart", {
|
|||||||
return that.formatTime(v.time)
|
return that.formatTime(v.time)
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
yAxis: {},
|
yAxis: {
|
||||||
|
axisLabel: {
|
||||||
|
formatter: function (value) {
|
||||||
|
return value + axis.unit
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
tooltip: {
|
tooltip: {
|
||||||
show: true,
|
show: true,
|
||||||
trigger: "item",
|
trigger: "item",
|
||||||
@@ -177,7 +221,9 @@ Vue.component("metric-chart", {
|
|||||||
{
|
{
|
||||||
name: name,
|
name: name,
|
||||||
type: "line",
|
type: "line",
|
||||||
data: values,
|
data: values.map(function (v) {
|
||||||
|
return v / axis.divider
|
||||||
|
}),
|
||||||
itemStyle: {
|
itemStyle: {
|
||||||
color: "#9DD3E8"
|
color: "#9DD3E8"
|
||||||
},
|
},
|
||||||
@@ -190,6 +236,20 @@ Vue.component("metric-chart", {
|
|||||||
let values = this.stats.map(function (v) {
|
let values = this.stats.map(function (v) {
|
||||||
return v.value
|
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
|
let that = this
|
||||||
chart.setOption({
|
chart.setOption({
|
||||||
xAxis: {
|
xAxis: {
|
||||||
@@ -212,7 +272,13 @@ Vue.component("metric-chart", {
|
|||||||
return stat.keys[0] + ": " + stat.value + ",占比:" + percent + "%"
|
return stat.keys[0] + ": " + stat.value + ",占比:" + percent + "%"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
yAxis: {},
|
yAxis: {
|
||||||
|
axisLabel: {
|
||||||
|
formatter: function (value) {
|
||||||
|
return value + axis.unit
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
grid: {
|
grid: {
|
||||||
left: 40,
|
left: 40,
|
||||||
top: 10,
|
top: 10,
|
||||||
@@ -223,7 +289,9 @@ Vue.component("metric-chart", {
|
|||||||
{
|
{
|
||||||
name: name,
|
name: name,
|
||||||
type: "bar",
|
type: "bar",
|
||||||
data: values,
|
data: values.map(function (v) {
|
||||||
|
return v / axis.divider
|
||||||
|
}),
|
||||||
itemStyle: {
|
itemStyle: {
|
||||||
color: "#9DD3E8"
|
color: "#9DD3E8"
|
||||||
},
|
},
|
||||||
@@ -272,9 +340,9 @@ Vue.component("metric-chart", {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
template: `<div style="float: left" :style="{'width': width}">
|
template: `<div style="float: left" :style="{'width': width}">
|
||||||
<div>{{chart.name}}</div>
|
<h4>{{chart.name}} <span>(指标)</span></h4>
|
||||||
<div class="ui divider"></div>
|
<div class="ui divider"></div>
|
||||||
<div style="height: 20em; overflow-y: auto" :id="chartId" class="scroll-box"></div>
|
<div style="height: 20em; padding-bottom: 1em; " :id="chartId" :class="{'scroll-box': chart.type == 'table'}"></div>
|
||||||
</div>`
|
</div>`
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|||||||
@@ -362,6 +362,7 @@ window.teaweb = {
|
|||||||
let tooltipFunc = options.tooltip
|
let tooltipFunc = options.tooltip
|
||||||
let axis = options.axis
|
let axis = options.axis
|
||||||
let valueFunc = options.value
|
let valueFunc = options.value
|
||||||
|
let click = options.click
|
||||||
|
|
||||||
let chartBox = document.getElementById(chartId)
|
let chartBox = document.getElementById(chartId)
|
||||||
if (chartBox == null) {
|
if (chartBox == null) {
|
||||||
@@ -406,9 +407,14 @@ window.teaweb = {
|
|||||||
barWidth: "20em"
|
barWidth: "20em"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
animation: true
|
animation: true,
|
||||||
}
|
}
|
||||||
chart.setOption(option)
|
chart.setOption(option)
|
||||||
|
if (click != null) {
|
||||||
|
chart.on("click", function (args) {
|
||||||
|
click.call(this, args, values)
|
||||||
|
})
|
||||||
|
}
|
||||||
chart.resize()
|
chart.resize()
|
||||||
},
|
},
|
||||||
renderLineChart: function (options) {
|
renderLineChart: function (options) {
|
||||||
|
|||||||
@@ -669,6 +669,9 @@ td {
|
|||||||
.source-code-box .CodeMirror-vscrollbar::-webkit-scrollbar-thumb {
|
.source-code-box .CodeMirror-vscrollbar::-webkit-scrollbar-thumb {
|
||||||
border-radius: 2px;
|
border-radius: 2px;
|
||||||
}
|
}
|
||||||
|
.scroll-box {
|
||||||
|
overflow-y: auto;
|
||||||
|
}
|
||||||
.scroll-box::-webkit-scrollbar {
|
.scroll-box::-webkit-scrollbar {
|
||||||
width: 4px;
|
width: 4px;
|
||||||
}
|
}
|
||||||
|
|||||||
File diff suppressed because one or more lines are too long
@@ -747,6 +747,10 @@ td {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 表格
|
// 表格
|
||||||
|
.scroll-box {
|
||||||
|
overflow-y: auto;
|
||||||
|
}
|
||||||
|
|
||||||
.scroll-box::-webkit-scrollbar {
|
.scroll-box::-webkit-scrollbar {
|
||||||
width: 4px;
|
width: 4px;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -47,13 +47,13 @@
|
|||||||
<div class="ui divider"></div>
|
<div class="ui divider"></div>
|
||||||
|
|
||||||
<!-- 域名排行 -->
|
<!-- 域名排行 -->
|
||||||
<h4>域名排行 <span>(24小时)</span></h4>
|
<h4>域名访问排行 <span>(24小时)</span></h4>
|
||||||
<div class="chart-box" id="top-domains-chart"></div>
|
<div class="chart-box" id="top-domains-chart"></div>
|
||||||
|
|
||||||
<div class="ui divider"></div>
|
<div class="ui divider"></div>
|
||||||
|
|
||||||
<!-- 节点排行 -->
|
<!-- 节点排行 -->
|
||||||
<h4>节点排行 <span>(24小时)</span></h4>
|
<h4>节点访问排行 <span>(24小时)</span></h4>
|
||||||
<div class="chart-box" id="top-nodes-chart"></div>
|
<div class="chart-box" id="top-nodes-chart"></div>
|
||||||
|
|
||||||
<div class="ui divider"></div>
|
<div class="ui divider"></div>
|
||||||
@@ -66,4 +66,10 @@
|
|||||||
|
|
||||||
<div class="chart-box" id="cpu-chart" v-show="nodeStatusTab == 'cpu'"></div>
|
<div class="chart-box" id="cpu-chart" v-show="nodeStatusTab == 'cpu'"></div>
|
||||||
<div class="chart-box" id="memory-chart" v-show="nodeStatusTab == 'memory'"></div>
|
<div class="chart-box" id="memory-chart" v-show="nodeStatusTab == 'memory'"></div>
|
||||||
<div class="chart-box" id="load-chart" v-show="nodeStatusTab == 'load'"></div>
|
<div class="chart-box" id="load-chart" v-show="nodeStatusTab == 'load'"></div>
|
||||||
|
|
||||||
|
<!-- 指标 -->
|
||||||
|
<div class="ui divider" v-if="metricCharts.length > 0"></div>
|
||||||
|
<metric-board>
|
||||||
|
<metric-chart v-for="chart in metricCharts" :key="chart.id" :v-chart="chart.chart" :v-stats="chart.stats" :v-period="chart.item.period" :v-period-unit="chart.item.periodUnit" :v-value-type="chart.item.valueType"></metric-chart>
|
||||||
|
</metric-board>
|
||||||
@@ -7,7 +7,7 @@ Tea.context(function () {
|
|||||||
this.$delay(function () {
|
this.$delay(function () {
|
||||||
this.reloadHourlyTrafficChart()
|
this.reloadHourlyTrafficChart()
|
||||||
this.reloadHourlyRequestsChart()
|
this.reloadHourlyRequestsChart()
|
||||||
this.reloadTopNodeStatsChart()
|
this.reloadTopNodesChart()
|
||||||
this.reloadTopDomainsChart()
|
this.reloadTopDomainsChart()
|
||||||
this.reloadCPUChart()
|
this.reloadCPUChart()
|
||||||
})
|
})
|
||||||
@@ -265,7 +265,7 @@ Tea.context(function () {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 节点排行
|
// 节点排行
|
||||||
this.reloadTopNodeStatsChart = function () {
|
this.reloadTopNodesChart = function () {
|
||||||
let that = this
|
let that = this
|
||||||
let axis = teaweb.countAxis(this.topNodeStats, function (v) {
|
let axis = teaweb.countAxis(this.topNodeStats, function (v) {
|
||||||
return v.countRequests
|
return v.countRequests
|
||||||
@@ -283,7 +283,10 @@ Tea.context(function () {
|
|||||||
value: function (v) {
|
value: function (v) {
|
||||||
return v.countRequests / axis.divider;
|
return v.countRequests / axis.divider;
|
||||||
},
|
},
|
||||||
axis: axis
|
axis: axis,
|
||||||
|
click: function (args, stats) {
|
||||||
|
window.location = "/clusters/cluster/node?nodeId=" + stats[args.dataIndex].nodeId + "&clusterId=" + that.clusterId
|
||||||
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -332,7 +335,7 @@ Tea.context(function () {
|
|||||||
}
|
}
|
||||||
|
|
||||||
this.reloadCPUChart = function () {
|
this.reloadCPUChart = function () {
|
||||||
let axis = {unit: "", divider: 1}
|
let axis = {unit: "%", divider: 1}
|
||||||
teaweb.renderLineChart({
|
teaweb.renderLineChart({
|
||||||
id: "cpu-chart",
|
id: "cpu-chart",
|
||||||
name: "CPU",
|
name: "CPU",
|
||||||
@@ -352,7 +355,7 @@ Tea.context(function () {
|
|||||||
}
|
}
|
||||||
|
|
||||||
this.reloadMemoryChart = function () {
|
this.reloadMemoryChart = function () {
|
||||||
let axis = {unit: "", divider: 1}
|
let axis = {unit: "%", divider: 1}
|
||||||
teaweb.renderLineChart({
|
teaweb.renderLineChart({
|
||||||
id: "memory-chart",
|
id: "memory-chart",
|
||||||
name: "内存",
|
name: "内存",
|
||||||
|
|||||||
@@ -1,10 +1,12 @@
|
|||||||
<second-menu>
|
<second-menu>
|
||||||
<menu-item :href="'/clusters/cluster/nodes?clusterId=' + clusterId">节点列表</menu-item>
|
<menu-item :href="'/clusters/cluster/nodes?clusterId=' + clusterId">节点列表</menu-item>
|
||||||
<span class="item">|</span>
|
<span class="item">|</span>
|
||||||
<menu-item :href="'/clusters/cluster/node?clusterId=' + clusterId + '&nodeId=' + nodeId" code="node">节点详情</menu-item>
|
<menu-item :href="'/clusters/cluster/node?clusterId=' + clusterId + '&nodeId=' + node.id" code="node"
|
||||||
<menu-item :href="'/clusters/cluster/node/monitor?clusterId=' + clusterId + '&nodeId=' + nodeId" code="monitor" v-if="teaIsPlus">监控图表</menu-item>
|
v-if="!teaIsPlus">"{{node.name}}"节点详情</menu-item>
|
||||||
<menu-item :href="'/clusters/cluster/node/thresholds?clusterId=' + clusterId + '&nodeId=' + nodeId" code="threshold" v-if="teaIsPlus">阈值设置</menu-item>
|
<menu-item :href="'/clusters/cluster/node/boards?clusterId=' + clusterId + '&nodeId=' + node.id" code="board" v-if="teaIsPlus">"{{node.name}}" 节点看板</menu-item>
|
||||||
<menu-item :href="'/clusters/cluster/node/logs?clusterId=' + clusterId + '&nodeId=' + nodeId" code="log">运行日志</menu-item>
|
<menu-item :href="'/clusters/cluster/node/detail?clusterId=' + clusterId + '&nodeId=' + node.id" code="node" v-if="teaIsPlus">节点详情</menu-item>
|
||||||
<menu-item :href="'/clusters/cluster/node/update?clusterId=' + clusterId + '&nodeId=' + nodeId" code="update">修改设置</menu-item>
|
<menu-item :href="'/clusters/cluster/node/thresholds?clusterId=' + clusterId + '&nodeId=' + node.id" code="threshold" v-if="teaIsPlus">阈值设置</menu-item>
|
||||||
<menu-item :href="'/clusters/cluster/node/install?clusterId=' + clusterId + '&nodeId=' + nodeId" code="install">安装节点</menu-item>
|
<menu-item :href="'/clusters/cluster/node/logs?clusterId=' + clusterId + '&nodeId=' + node.id" code="log">运行日志</menu-item>
|
||||||
|
<menu-item :href="'/clusters/cluster/node/update?clusterId=' + clusterId + '&nodeId=' + node.id" code="update">修改设置</menu-item>
|
||||||
|
<menu-item :href="'/clusters/cluster/node/install?clusterId=' + clusterId + '&nodeId=' + node.id" code="install">安装节点</menu-item>
|
||||||
</second-menu>
|
</second-menu>
|
||||||
32
web/views/@default/clusters/cluster/node/boards/index.css
Normal file
32
web/views/@default/clusters/cluster/node/boards/index.css
Normal file
@@ -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 */
|
||||||
@@ -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"}
|
||||||
109
web/views/@default/clusters/cluster/node/boards/index.html
Normal file
109
web/views/@default/clusters/cluster/node/boards/index.html
Normal file
@@ -0,0 +1,109 @@
|
|||||||
|
{$layout}
|
||||||
|
{$template "../node_menu"}
|
||||||
|
{$template "/echarts"}
|
||||||
|
|
||||||
|
<!-- 概况 -->
|
||||||
|
<div class="ui four columns grid">
|
||||||
|
<div class="ui column">
|
||||||
|
<h4>在线状态</h4>
|
||||||
|
<div class="value">
|
||||||
|
<span class="green" v-if="board.isActive">在线</span>
|
||||||
|
<span class="red" v-else>离线</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="ui column">
|
||||||
|
<h4>下行流量</h4>
|
||||||
|
<div class="value"><span>{{board.trafficOutBytes}}</span>/分钟</div>
|
||||||
|
</div>
|
||||||
|
<div class="ui column">
|
||||||
|
<h4>上行流量</h4>
|
||||||
|
<div class="value"><span>{{board.trafficInBytes}}</span>/分钟</div>
|
||||||
|
</div>
|
||||||
|
<div class="ui column">
|
||||||
|
<h4>连接数</h4>
|
||||||
|
<div class="value"><span>{{board.countConnections}}</span>/分钟</div>
|
||||||
|
</div>
|
||||||
|
<div class="ui column">
|
||||||
|
<h4>当前访问量</h4>
|
||||||
|
<div class="value"><span>{{board.countRequests}}</span>/分钟</div>
|
||||||
|
</div>
|
||||||
|
<div class="ui column">
|
||||||
|
<h4>当前攻击访问量</h4>
|
||||||
|
<div class="value"><span :class="{red: board.countAttackRequests != '0'}">{{board.countAttackRequests}}</span>/分钟</div>
|
||||||
|
</div>
|
||||||
|
<div class="ui column">
|
||||||
|
<h4>磁盘缓存用量</h4>
|
||||||
|
<div class="value"><span>{{board.cacheDiskSize}}</span></div>
|
||||||
|
</div>
|
||||||
|
<div class="ui column">
|
||||||
|
<h4>内存缓存用量</h4>
|
||||||
|
<div class="value"><span>{{board.cacheMemorySize}}</span></div>
|
||||||
|
</div>
|
||||||
|
<div class="ui column">
|
||||||
|
<h4>CPU</h4>
|
||||||
|
<div class="value"><span :class="{red: board.cpuUsage > 80}">{{board.cpuUsage}}</span>%</div>
|
||||||
|
</div>
|
||||||
|
<div class="ui column">
|
||||||
|
<h4>内存</h4>
|
||||||
|
<div class="value"><span :class="{red: board.memoryUsage > 80}">{{board.memoryUsage}}</span>%</div>
|
||||||
|
</div>
|
||||||
|
<div class="ui column">
|
||||||
|
<h4>总内存</h4>
|
||||||
|
<div class="value"><span>{{board.memoryTotalSize}}</span>G</div>
|
||||||
|
</div>
|
||||||
|
<div class="ui column">
|
||||||
|
<h4>负载</h4>
|
||||||
|
<div class="value"><span :class="{red: board.load > 20}">{{board.load}}</span>/分钟</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="ui divider"></div>
|
||||||
|
|
||||||
|
<div class="ui menu tabular">
|
||||||
|
<a href="" class="item" :class="{active: trafficTab == 'hourly'}" @click.prevent="selectTrafficTab('hourly')">24小时流量趋势</a>
|
||||||
|
<a href="" class="item" :class="{active: trafficTab == 'daily'}" @click.prevent="selectTrafficTab('daily')">15天流量趋势</a>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- 按小时统计流量 -->
|
||||||
|
<div class="chart-box" id="hourly-traffic-chart" v-show="trafficTab == 'hourly'"></div>
|
||||||
|
|
||||||
|
<!-- 按日统计流量 -->
|
||||||
|
<div class="chart-box" id="daily-traffic-chart" v-show="trafficTab == 'daily'"></div>
|
||||||
|
|
||||||
|
<div class="ui divider"></div>
|
||||||
|
|
||||||
|
<div class="ui menu tabular">
|
||||||
|
<a href="" class="item" :class="{active: requestsTab == 'hourly'}" @click.prevent="selectRequestsTab('hourly')">24小时访问量趋势</a>
|
||||||
|
<a href="" class="item" :class="{active: requestsTab == 'daily'}" @click.prevent="selectRequestsTab('daily')">15天访问量趋势</a>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- 按小时统计访问量 -->
|
||||||
|
<div class="chart-box" id="hourly-requests-chart" v-show="requestsTab == 'hourly'"></div>
|
||||||
|
|
||||||
|
<!-- 按日统计访问量 -->
|
||||||
|
<div class="chart-box" id="daily-requests-chart" v-show="requestsTab == 'daily'"></div>
|
||||||
|
|
||||||
|
<div class="ui divider"></div>
|
||||||
|
|
||||||
|
<!-- 域名排行 -->
|
||||||
|
<h4>域名访问排行 <span>(24小时)</span></h4>
|
||||||
|
<div class="chart-box" id="top-domains-chart"></div>
|
||||||
|
|
||||||
|
<div class="ui divider"></div>
|
||||||
|
|
||||||
|
<!-- CPU、内存、负载 -->
|
||||||
|
<div class="ui menu tabular">
|
||||||
|
<a href="" class="item" :class="{active: nodeStatusTab == 'cpu'}" @click.prevent="selectNodeStatusTab('cpu')">节点CPU</a>
|
||||||
|
<a href="" class="item" :class="{active: nodeStatusTab == 'memory'}" @click.prevent="selectNodeStatusTab('memory')">节点内存</a>
|
||||||
|
<a href="" class="item" :class="{active: nodeStatusTab == 'load'}" @click.prevent="selectNodeStatusTab('load')">节点负载</a>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="chart-box" id="cpu-chart" v-show="nodeStatusTab == 'cpu'"></div>
|
||||||
|
<div class="chart-box" id="memory-chart" v-show="nodeStatusTab == 'memory'"></div>
|
||||||
|
<div class="chart-box" id="load-chart" v-show="nodeStatusTab == 'load'"></div>
|
||||||
|
|
||||||
|
<!-- 指标 -->
|
||||||
|
<div class="ui divider" v-if="metricCharts.length > 0"></div>
|
||||||
|
<metric-board>
|
||||||
|
<metric-chart v-for="chart in metricCharts" :key="chart.id" :v-chart="chart.chart" :v-stats="chart.stats" :v-period="chart.item.period" :v-period-unit="chart.item.periodUnit" :v-value-type="chart.item.valueType"></metric-chart>
|
||||||
|
</metric-board>
|
||||||
403
web/views/@default/clusters/cluster/node/boards/index.js
Normal file
403
web/views/@default/clusters/cluster/node/boards/index.js
Normal file
@@ -0,0 +1,403 @@
|
|||||||
|
Tea.context(function () {
|
||||||
|
this.formatCount = function (count) {
|
||||||
|
if (count < 1000) {
|
||||||
|
return count.toString()
|
||||||
|
}
|
||||||
|
if (count < 1000 * 1000) {
|
||||||
|
return (Math.round(count / 1000 * 100) / 100) + "K"
|
||||||
|
}
|
||||||
|
return (Math.round(count / 1000 / 1000 * 100) / 100) + "M"
|
||||||
|
}
|
||||||
|
|
||||||
|
this.board.trafficInBytes = teaweb.formatBytes(this.board.trafficInBytes)
|
||||||
|
this.board.trafficOutBytes = teaweb.formatBytes(this.board.trafficOutBytes)
|
||||||
|
this.board.countConnections = this.formatCount(this.board.countConnections)
|
||||||
|
this.board.countRequests = this.formatCount(this.board.countRequests)
|
||||||
|
this.board.countAttackRequests = this.formatCount(this.board.countAttackRequests)
|
||||||
|
this.board.cpuUsage = Math.round(this.board.cpuUsage * 100 * 100) / 100
|
||||||
|
this.board.memoryUsage = Math.round(this.board.memoryUsage * 100 * 100) / 100
|
||||||
|
this.board.memoryTotalSize = Math.round(this.board.memoryTotalSize / 1024 / 1024 / 1024)
|
||||||
|
this.board.load = Math.round(this.board.load * 100) / 100
|
||||||
|
this.board.cacheDiskSize = teaweb.formatBytes(this.board.cacheDiskSize)
|
||||||
|
this.board.cacheMemorySize = teaweb.formatBytes(this.board.cacheMemorySize)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 流量统计
|
||||||
|
*/
|
||||||
|
this.trafficTab = "hourly"
|
||||||
|
|
||||||
|
this.$delay(function () {
|
||||||
|
this.reloadHourlyTrafficChart()
|
||||||
|
this.reloadHourlyRequestsChart()
|
||||||
|
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.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 + "<br/>请求数:" + " " + teaweb.formatNumber(stats[args.dataIndex].countRequests) + "<br/>流量:" + 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
|
||||||
|
})
|
||||||
|
}
|
||||||
|
})
|
||||||
45
web/views/@default/clusters/cluster/node/boards/index.less
Normal file
45
web/views/@default/clusters/cluster/node/boards/index.less
Normal file
@@ -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;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
a.underline {
|
a.underline {
|
||||||
border-bottom: 1px #db2828 dashed;
|
border-bottom: 1px #db2828 dashed;
|
||||||
}
|
}
|
||||||
/*# sourceMappingURL=index.css.map */
|
/*# sourceMappingURL=detail.css.map */
|
||||||
1
web/views/@default/clusters/cluster/node/detail.css.map
Normal file
1
web/views/@default/clusters/cluster/node/detail.css.map
Normal file
@@ -0,0 +1 @@
|
|||||||
|
{"version":3,"sources":["detail.less"],"names":[],"mappings":"AAAA,CAAC;EACA,iCAAA","file":"detail.css"}
|
||||||
@@ -1 +0,0 @@
|
|||||||
{"version":3,"sources":["index.less"],"names":[],"mappings":"AAAA,CAAC;EACA,iCAAA","file":"index.css"}
|
|
||||||
Reference in New Issue
Block a user