mirror of
https://github.com/TeaOSLab/EdgeAdmin.git
synced 2025-11-02 11:50:28 +08:00
实现节点看板(仅对企业版开放)
This commit is contained in:
@@ -1,7 +1,7 @@
|
||||
# GoEdge目标
|
||||
做一款人人用得起的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/iwind/TeaGo/actions"
|
||||
"github.com/iwind/TeaGo/logs"
|
||||
"github.com/iwind/TeaGo/maps"
|
||||
"net/http"
|
||||
"strconv"
|
||||
)
|
||||
@@ -126,3 +127,8 @@ func (this *ParentAction) AdminContext() context.Context {
|
||||
}
|
||||
return this.rpcClient.Context(this.AdminId())
|
||||
}
|
||||
|
||||
// ViewData 视图里可以使用的数据
|
||||
func (this *ParentAction) ViewData() maps.Map {
|
||||
return this.Data
|
||||
}
|
||||
|
||||
@@ -3,11 +3,14 @@
|
||||
package boards
|
||||
|
||||
import (
|
||||
teaconst "github.com/TeaOSLab/EdgeAdmin/internal/const"
|
||||
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
|
||||
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
|
||||
"github.com/TeaOSLab/EdgeCommon/pkg/serverconfigs"
|
||||
"github.com/iwind/TeaGo/maps"
|
||||
"github.com/iwind/TeaGo/types"
|
||||
timeutil "github.com/iwind/TeaGo/utils/time"
|
||||
"strconv"
|
||||
)
|
||||
|
||||
type IndexAction struct {
|
||||
@@ -21,6 +24,11 @@ func (this *IndexAction) Init() {
|
||||
func (this *IndexAction) RunGet(params struct {
|
||||
ClusterId int64
|
||||
}) {
|
||||
if !teaconst.IsPlus {
|
||||
this.RedirectURL("/clusters/cluster?clusterId=" + strconv.FormatInt(params.ClusterId, 10))
|
||||
return
|
||||
}
|
||||
|
||||
resp, err := this.RPC().ServerStatBoardRPC().ComposeServerStatNodeClusterBoard(this.AdminContext(), &pb.ComposeServerStatNodeClusterBoardRequest{NodeClusterId: params.ClusterId})
|
||||
if err != nil {
|
||||
this.ErrorPage(err)
|
||||
@@ -128,5 +136,41 @@ func (this *IndexAction) RunGet(params struct {
|
||||
this.Data["loadValues"] = statMaps
|
||||
}
|
||||
|
||||
// 指标
|
||||
{
|
||||
var chartMaps = []maps.Map{}
|
||||
for _, chart := range resp.MetricCharts {
|
||||
var statMaps = []maps.Map{}
|
||||
for _, stat := range chart.MetricStats {
|
||||
statMaps = append(statMaps, maps.Map{
|
||||
"keys": stat.Keys,
|
||||
"time": stat.Time,
|
||||
"value": stat.Value,
|
||||
"count": stat.SumCount,
|
||||
"total": stat.SumTotal,
|
||||
})
|
||||
}
|
||||
chartMaps = append(chartMaps, maps.Map{
|
||||
"chart": maps.Map{
|
||||
"id": chart.MetricChart.Id,
|
||||
"name": chart.MetricChart.Name,
|
||||
"widthDiv": chart.MetricChart.WidthDiv,
|
||||
"isOn": chart.MetricChart.IsOn,
|
||||
"maxItems": chart.MetricChart.MaxItems,
|
||||
"type": chart.MetricChart.Type,
|
||||
},
|
||||
"item": maps.Map{
|
||||
"id": chart.MetricChart.MetricItem.Id,
|
||||
"name": chart.MetricChart.MetricItem.Name,
|
||||
"period": chart.MetricChart.MetricItem.Period,
|
||||
"periodUnit": chart.MetricChart.MetricItem.PeriodUnit,
|
||||
"valueType": serverconfigs.FindMetricValueType(chart.MetricChart.MetricItem.Category, chart.MetricChart.MetricItem.Value),
|
||||
},
|
||||
"stats": statMaps,
|
||||
})
|
||||
}
|
||||
this.Data["metricCharts"] = chartMaps
|
||||
}
|
||||
|
||||
this.Show()
|
||||
}
|
||||
|
||||
@@ -5,6 +5,7 @@ import (
|
||||
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/default/clusters/cluster/boards"
|
||||
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/default/clusters/cluster/groups"
|
||||
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/default/clusters/cluster/node"
|
||||
nodeboards "github.com/TeaOSLab/EdgeAdmin/internal/web/actions/default/clusters/cluster/node/boards"
|
||||
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/default/clusters/cluster/node/monitor"
|
||||
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/default/clusters/cluster/node/thresholds"
|
||||
clusters "github.com/TeaOSLab/EdgeAdmin/internal/web/actions/default/clusters/clusterutils"
|
||||
@@ -32,34 +33,39 @@ func init() {
|
||||
GetPost("/installManual", new(InstallManualAction)).
|
||||
|
||||
// 节点相关
|
||||
Get("/node", new(node.IndexAction)).
|
||||
GetPost("/node/update", new(node.UpdateAction)).
|
||||
GetPost("/node/install", new(node.InstallAction)).
|
||||
Post("/node/updateInstallStatus", new(node.UpdateInstallStatusAction)).
|
||||
Post("/node/status", new(node.StatusAction)).
|
||||
Get("/node/logs", new(node.LogsAction)).
|
||||
Post("/node/start", new(node.StartAction)).
|
||||
Post("/node/stop", new(node.StopAction)).
|
||||
Post("/node/up", new(node.UpAction)).
|
||||
Get("/node/monitor", new(monitor.IndexAction)).
|
||||
Post("/node/monitor/cpu", new(monitor.CpuAction)).
|
||||
Post("/node/monitor/memory", new(monitor.MemoryAction)).
|
||||
Post("/node/monitor/load", new(monitor.LoadAction)).
|
||||
Post("/node/monitor/trafficIn", new(monitor.TrafficInAction)).
|
||||
Post("/node/monitor/trafficOut", new(monitor.TrafficOutAction)).
|
||||
Post("/node/monitor/connections", new(monitor.ConnectionsAction)).
|
||||
Get("/node/thresholds", new(thresholds.IndexAction)).
|
||||
Prefix("/clusters/cluster/node").
|
||||
Get("", new(node.IndexAction)).
|
||||
GetPost("/update", new(node.UpdateAction)).
|
||||
GetPost("/install", new(node.InstallAction)).
|
||||
Post("/updateInstallStatus", new(node.UpdateInstallStatusAction)).
|
||||
Post("/status", new(node.StatusAction)).
|
||||
Get("/logs", new(node.LogsAction)).
|
||||
Post("/start", new(node.StartAction)).
|
||||
Post("/stop", new(node.StopAction)).
|
||||
Post("/up", new(node.UpAction)).
|
||||
Get("/monitor", new(monitor.IndexAction)).
|
||||
Post("/monitor/cpu", new(monitor.CpuAction)).
|
||||
Post("/monitor/memory", new(monitor.MemoryAction)).
|
||||
Post("/monitor/load", new(monitor.LoadAction)).
|
||||
Post("/monitor/trafficIn", new(monitor.TrafficInAction)).
|
||||
Post("/monitor/trafficOut", new(monitor.TrafficOutAction)).
|
||||
Post("/monitor/connections", new(monitor.ConnectionsAction)).
|
||||
Get("/thresholds", new(thresholds.IndexAction)).
|
||||
Get("/detail", new(node.DetailAction)).
|
||||
Get("/boards", new(nodeboards.IndexAction)).
|
||||
|
||||
// 分组相关
|
||||
Get("/groups", new(groups.IndexAction)).
|
||||
GetPost("/groups/createPopup", new(groups.CreatePopupAction)).
|
||||
GetPost("/groups/updatePopup", new(groups.UpdatePopupAction)).
|
||||
Post("/groups/delete", new(groups.DeleteAction)).
|
||||
Post("/groups/sort", new(groups.SortAction)).
|
||||
GetPost("/groups/selectPopup", new(groups.SelectPopupAction)).
|
||||
Prefix("/clusters/cluster/groups").
|
||||
Get("", new(groups.IndexAction)).
|
||||
GetPost("/createPopup", new(groups.CreatePopupAction)).
|
||||
GetPost("/updatePopup", new(groups.UpdatePopupAction)).
|
||||
Post("/delete", new(groups.DeleteAction)).
|
||||
Post("/sort", new(groups.SortAction)).
|
||||
GetPost("/selectPopup", new(groups.SelectPopupAction)).
|
||||
|
||||
// 看板相关
|
||||
Get("/boards", new(boards.IndexAction)).
|
||||
Prefix("/clusters/cluster/boards").
|
||||
Get("", new(boards.IndexAction)).
|
||||
|
||||
EndAll()
|
||||
})
|
||||
|
||||
@@ -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
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"github.com/TeaOSLab/EdgeAdmin/internal/utils/numberutils"
|
||||
teaconst "github.com/TeaOSLab/EdgeAdmin/internal/const"
|
||||
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
|
||||
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/default/clusters/grants/grantutils"
|
||||
"github.com/TeaOSLab/EdgeCommon/pkg/nodeconfigs"
|
||||
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
|
||||
"github.com/iwind/TeaGo/maps"
|
||||
"time"
|
||||
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/default/clusters/cluster/node/nodeutils"
|
||||
"strconv"
|
||||
)
|
||||
|
||||
type IndexAction struct {
|
||||
@@ -24,236 +20,15 @@ func (this *IndexAction) Init() {
|
||||
func (this *IndexAction) RunGet(params struct {
|
||||
NodeId int64
|
||||
}) {
|
||||
this.Data["nodeId"] = params.NodeId
|
||||
|
||||
nodeResp, err := this.RPC().NodeRPC().FindEnabledNode(this.AdminContext(), &pb.FindEnabledNodeRequest{NodeId: params.NodeId})
|
||||
err := nodeutils.InitNodeInfo(this, params.NodeId)
|
||||
if err != nil {
|
||||
this.ErrorPage(err)
|
||||
return
|
||||
}
|
||||
node := nodeResp.Node
|
||||
if node == nil {
|
||||
this.WriteString("找不到要操作的节点")
|
||||
return
|
||||
}
|
||||
|
||||
var clusterMap maps.Map = nil
|
||||
if node.NodeCluster != nil {
|
||||
clusterId := node.NodeCluster.Id
|
||||
clusterResp, err := this.RPC().NodeClusterRPC().FindEnabledNodeCluster(this.AdminContext(), &pb.FindEnabledNodeClusterRequest{NodeClusterId: clusterId})
|
||||
if err != nil {
|
||||
this.ErrorPage(err)
|
||||
return
|
||||
}
|
||||
cluster := clusterResp.NodeCluster
|
||||
if cluster != nil {
|
||||
clusterMap = maps.Map{
|
||||
"id": cluster.Id,
|
||||
"name": cluster.Name,
|
||||
"installDir": cluster.InstallDir,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// IP地址
|
||||
ipAddressesResp, err := this.RPC().NodeIPAddressRPC().FindAllEnabledIPAddressesWithNodeId(this.AdminContext(), &pb.FindAllEnabledIPAddressesWithNodeIdRequest{
|
||||
NodeId: params.NodeId,
|
||||
Role: nodeconfigs.NodeRoleNode,
|
||||
})
|
||||
if err != nil {
|
||||
this.ErrorPage(err)
|
||||
return
|
||||
}
|
||||
ipAddressMaps := []maps.Map{}
|
||||
for _, addr := range ipAddressesResp.Addresses {
|
||||
ipAddressMaps = append(ipAddressMaps, maps.Map{
|
||||
"id": addr.Id,
|
||||
"name": addr.Name,
|
||||
"ip": addr.Ip,
|
||||
"canAccess": addr.CanAccess,
|
||||
})
|
||||
}
|
||||
|
||||
// DNS相关
|
||||
dnsInfoResp, err := this.RPC().NodeRPC().FindEnabledNodeDNS(this.AdminContext(), &pb.FindEnabledNodeDNSRequest{NodeId: params.NodeId})
|
||||
if err != nil {
|
||||
this.ErrorPage(err)
|
||||
return
|
||||
}
|
||||
dnsRouteMaps := []maps.Map{}
|
||||
recordName := ""
|
||||
recordValue := ""
|
||||
if dnsInfoResp.Node != nil {
|
||||
recordName = dnsInfoResp.Node.NodeClusterDNSName + "." + dnsInfoResp.Node.DnsDomainName
|
||||
recordValue = dnsInfoResp.Node.IpAddr
|
||||
for _, dnsInfo := range dnsInfoResp.Node.Routes {
|
||||
dnsRouteMaps = append(dnsRouteMaps, maps.Map{
|
||||
"name": dnsInfo.Name,
|
||||
"code": dnsInfo.Code,
|
||||
})
|
||||
}
|
||||
}
|
||||
if len(dnsRouteMaps) == 0 {
|
||||
dnsRouteMaps = append(dnsRouteMaps, maps.Map{
|
||||
"name": "",
|
||||
"code": "",
|
||||
})
|
||||
}
|
||||
this.Data["dnsRoutes"] = dnsRouteMaps
|
||||
this.Data["dnsRecordName"] = recordName
|
||||
this.Data["dnsRecordValue"] = recordValue
|
||||
|
||||
// 登录信息
|
||||
var loginMap maps.Map = nil
|
||||
if node.Login != nil {
|
||||
loginParams := maps.Map{}
|
||||
if len(node.Login.Params) > 0 {
|
||||
err = json.Unmarshal(node.Login.Params, &loginParams)
|
||||
if err != nil {
|
||||
this.ErrorPage(err)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
grantMap := maps.Map{}
|
||||
grantId := loginParams.GetInt64("grantId")
|
||||
if grantId > 0 {
|
||||
grantResp, err := this.RPC().NodeGrantRPC().FindEnabledNodeGrant(this.AdminContext(), &pb.FindEnabledNodeGrantRequest{NodeGrantId: grantId})
|
||||
if err != nil {
|
||||
this.ErrorPage(err)
|
||||
return
|
||||
}
|
||||
if grantResp.NodeGrant != nil {
|
||||
grantMap = maps.Map{
|
||||
"id": grantResp.NodeGrant.Id,
|
||||
"name": grantResp.NodeGrant.Name,
|
||||
"method": grantResp.NodeGrant.Method,
|
||||
"methodName": grantutils.FindGrantMethodName(grantResp.NodeGrant.Method),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
loginMap = maps.Map{
|
||||
"id": node.Login.Id,
|
||||
"name": node.Login.Name,
|
||||
"type": node.Login.Type,
|
||||
"params": loginParams,
|
||||
"grant": grantMap,
|
||||
}
|
||||
}
|
||||
|
||||
// 运行状态
|
||||
status := &nodeconfigs.NodeStatus{}
|
||||
if len(node.StatusJSON) > 0 {
|
||||
err = json.Unmarshal(node.StatusJSON, &status)
|
||||
if err != nil {
|
||||
this.ErrorPage(err)
|
||||
return
|
||||
}
|
||||
status.IsActive = status.IsActive && time.Now().Unix()-status.UpdatedAt <= 60 // N秒之内认为活跃
|
||||
}
|
||||
|
||||
// 检查是否有新版本
|
||||
if len(status.OS) > 0 {
|
||||
checkVersionResp, err := this.RPC().NodeRPC().CheckNodeLatestVersion(this.AdminContext(), &pb.CheckNodeLatestVersionRequest{
|
||||
Os: status.OS,
|
||||
Arch: status.Arch,
|
||||
CurrentVersion: status.BuildVersion,
|
||||
})
|
||||
if err != nil {
|
||||
this.ErrorPage(err)
|
||||
return
|
||||
}
|
||||
this.Data["shouldUpgrade"] = checkVersionResp.HasNewVersion
|
||||
this.Data["newVersion"] = checkVersionResp.NewVersion
|
||||
if teaconst.IsPlus {
|
||||
this.RedirectURL("/clusters/cluster/node/boards?clusterId=" + fmt.Sprintf("%d", this.Data["clusterId"]) + "&nodeId=" + strconv.FormatInt(params.NodeId, 10))
|
||||
} else {
|
||||
this.Data["shouldUpgrade"] = false
|
||||
this.Data["newVersion"] = ""
|
||||
this.RedirectURL("/clusters/cluster/node/detail?clusterId=" + fmt.Sprintf("%d", this.Data["clusterId"]) + strconv.FormatInt(params.NodeId, 10))
|
||||
}
|
||||
|
||||
// 分组
|
||||
var groupMap maps.Map = nil
|
||||
if node.NodeGroup != nil {
|
||||
groupMap = maps.Map{
|
||||
"id": node.NodeGroup.Id,
|
||||
"name": node.NodeGroup.Name,
|
||||
}
|
||||
}
|
||||
|
||||
// 区域
|
||||
var regionMap maps.Map = nil
|
||||
if node.NodeRegion != nil {
|
||||
regionMap = maps.Map{
|
||||
"id": node.NodeRegion.Id,
|
||||
"name": node.NodeRegion.Name,
|
||||
}
|
||||
}
|
||||
|
||||
// 缓存硬盘 & 内存容量
|
||||
var maxCacheDiskCapacity maps.Map = nil
|
||||
if node.MaxCacheDiskCapacity != nil {
|
||||
maxCacheDiskCapacity = maps.Map{
|
||||
"count": node.MaxCacheDiskCapacity.Count,
|
||||
"unit": node.MaxCacheDiskCapacity.Unit,
|
||||
}
|
||||
} else {
|
||||
maxCacheDiskCapacity = maps.Map{
|
||||
"count": 0,
|
||||
"unit": "gb",
|
||||
}
|
||||
}
|
||||
|
||||
var maxCacheMemoryCapacity maps.Map = nil
|
||||
if node.MaxCacheMemoryCapacity != nil {
|
||||
maxCacheMemoryCapacity = maps.Map{
|
||||
"count": node.MaxCacheMemoryCapacity.Count,
|
||||
"unit": node.MaxCacheMemoryCapacity.Unit,
|
||||
}
|
||||
} else {
|
||||
maxCacheMemoryCapacity = maps.Map{
|
||||
"count": 0,
|
||||
"unit": "gb",
|
||||
}
|
||||
}
|
||||
|
||||
this.Data["node"] = maps.Map{
|
||||
"id": node.Id,
|
||||
"name": node.Name,
|
||||
"ipAddresses": ipAddressMaps,
|
||||
"cluster": clusterMap,
|
||||
"login": loginMap,
|
||||
"installDir": node.InstallDir,
|
||||
"isInstalled": node.IsInstalled,
|
||||
"uniqueId": node.UniqueId,
|
||||
"secret": node.Secret,
|
||||
"maxCPU": node.MaxCPU,
|
||||
"isOn": node.IsOn,
|
||||
|
||||
"status": maps.Map{
|
||||
"isActive": status.IsActive,
|
||||
"updatedAt": status.UpdatedAt,
|
||||
"hostname": status.Hostname,
|
||||
"cpuUsage": status.CPUUsage,
|
||||
"cpuUsageText": fmt.Sprintf("%.2f%%", status.CPUUsage*100),
|
||||
"memUsage": status.MemoryUsage,
|
||||
"memUsageText": fmt.Sprintf("%.2f%%", status.MemoryUsage*100),
|
||||
"connectionCount": status.ConnectionCount,
|
||||
"buildVersion": status.BuildVersion,
|
||||
"cpuPhysicalCount": status.CPUPhysicalCount,
|
||||
"cpuLogicalCount": status.CPULogicalCount,
|
||||
"load1m": fmt.Sprintf("%.2f", status.Load1m),
|
||||
"load5m": fmt.Sprintf("%.2f", status.Load5m),
|
||||
"load15m": fmt.Sprintf("%.2f", status.Load15m),
|
||||
"cacheTotalDiskSize": numberutils.FormatBytes(status.CacheTotalDiskSize),
|
||||
"cacheTotalMemorySize": numberutils.FormatBytes(status.CacheTotalMemorySize),
|
||||
},
|
||||
|
||||
"group": groupMap,
|
||||
"region": regionMap,
|
||||
|
||||
"maxCacheDiskCapacity": maxCacheDiskCapacity,
|
||||
"maxCacheMemoryCapacity": maxCacheMemoryCapacity,
|
||||
}
|
||||
|
||||
this.Show()
|
||||
}
|
||||
|
||||
@@ -2,6 +2,7 @@ package node
|
||||
|
||||
import (
|
||||
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
|
||||
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/default/clusters/cluster/node/nodeutils"
|
||||
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
|
||||
"github.com/iwind/TeaGo/maps"
|
||||
timeutil "github.com/iwind/TeaGo/utils/time"
|
||||
@@ -23,6 +24,13 @@ func (this *LogsAction) RunGet(params struct {
|
||||
Keyword string
|
||||
Level string
|
||||
}) {
|
||||
// 初始化节点信息(用于菜单)
|
||||
err := nodeutils.InitNodeInfo(this, params.NodeId)
|
||||
if err != nil {
|
||||
this.ErrorPage(err)
|
||||
return
|
||||
}
|
||||
|
||||
this.Data["nodeId"] = params.NodeId
|
||||
this.Data["dayFrom"] = params.DayFrom
|
||||
this.Data["dayTo"] = params.DayTo
|
||||
|
||||
@@ -2,7 +2,10 @@
|
||||
|
||||
package monitor
|
||||
|
||||
import "github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
|
||||
import (
|
||||
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
|
||||
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/default/clusters/cluster/node/nodeutils"
|
||||
)
|
||||
|
||||
type IndexAction struct {
|
||||
actionutils.ParentAction
|
||||
@@ -17,5 +20,12 @@ func (this *IndexAction) RunGet(params struct {
|
||||
}) {
|
||||
this.Data["nodeId"] = params.NodeId
|
||||
|
||||
// 初始化节点信息(用于菜单)
|
||||
err := nodeutils.InitNodeInfo(this, params.NodeId)
|
||||
if err != nil {
|
||||
this.ErrorPage(err)
|
||||
return
|
||||
}
|
||||
|
||||
this.Show()
|
||||
}
|
||||
|
||||
@@ -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 (
|
||||
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
|
||||
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/default/clusters/cluster/node/nodeutils"
|
||||
"github.com/TeaOSLab/EdgeCommon/pkg/nodeconfigs"
|
||||
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
|
||||
"github.com/iwind/TeaGo/maps"
|
||||
@@ -23,6 +24,13 @@ func (this *IndexAction) RunGet(params struct {
|
||||
}) {
|
||||
this.Data["nodeId"] = params.NodeId
|
||||
|
||||
// 初始化节点信息(用于菜单)
|
||||
err := nodeutils.InitNodeInfo(this, params.NodeId)
|
||||
if err != nil {
|
||||
this.ErrorPage(err)
|
||||
return
|
||||
}
|
||||
|
||||
// 列出所有阈值
|
||||
thresholdsResp, err := this.RPC().NodeThresholdRPC().FindAllEnabledNodeThresholds(this.AdminContext(), &pb.FindAllEnabledNodeThresholdsRequest{
|
||||
Role: "node",
|
||||
|
||||
@@ -2,7 +2,6 @@ package clusterutils
|
||||
|
||||
import (
|
||||
teaconst "github.com/TeaOSLab/EdgeAdmin/internal/const"
|
||||
"github.com/TeaOSLab/EdgeAdmin/internal/rpc"
|
||||
"github.com/TeaOSLab/EdgeAdmin/internal/utils/numberutils"
|
||||
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
|
||||
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/dao"
|
||||
@@ -36,7 +35,7 @@ func (this *ClusterHelper) BeforeAction(actionPtr actions.ActionWrapper) (goNext
|
||||
action.Data["clusterId"] = clusterId
|
||||
|
||||
if clusterId > 0 {
|
||||
var ctx = actionPtr.(rpc.ContextInterface).AdminContext()
|
||||
var ctx = actionPtr.(actionutils.ActionInterface).AdminContext()
|
||||
cluster, err := dao.SharedNodeClusterDAO.FindEnabledNodeCluster(ctx, clusterId)
|
||||
if err != nil {
|
||||
logs.Error(err)
|
||||
|
||||
@@ -39,7 +39,7 @@ func (this *ClusterHelper) BeforeAction(actionPtr actions.ActionWrapper) (goNext
|
||||
logs.Error(err)
|
||||
return
|
||||
}
|
||||
clusterResp, err := rpcClient.NSClusterRPC().FindEnabledNSCluster(actionPtr.(rpc.ContextInterface).AdminContext(), &pb.FindEnabledNSClusterRequest{
|
||||
clusterResp, err := rpcClient.NSClusterRPC().FindEnabledNSCluster(actionPtr.(actionutils.ActionInterface).AdminContext(), &pb.FindEnabledNSClusterRequest{
|
||||
NsClusterId: clusterId,
|
||||
})
|
||||
if err != nil {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
// 指标图表
|
||||
Vue.component("metric-chart", {
|
||||
props: ["v-chart", "v-stats", "v-period-unit"],
|
||||
props: ["v-chart", "v-stats", "v-period-unit", "v-value-type"],
|
||||
mounted: function () {
|
||||
this.load()
|
||||
},
|
||||
@@ -88,7 +88,7 @@ Vue.component("metric-chart", {
|
||||
if (stat.total > 0) {
|
||||
percent = Math.round((stat.value * 100 / stat.total) * 100) / 100
|
||||
}
|
||||
return stat.keys[0] + ": " + stat.value + ",占比:" + percent + "%"
|
||||
return stat.keys[0] + ":" + stat.value + ",占比:" + percent + "%"
|
||||
}
|
||||
},
|
||||
series: [
|
||||
@@ -108,6 +108,21 @@ Vue.component("metric-chart", {
|
||||
let values = this.stats.map(function (v) {
|
||||
return v.value
|
||||
})
|
||||
|
||||
let axis = {unit: "", divider: 1}
|
||||
switch (this.vValueType) {
|
||||
case "count":
|
||||
axis = teaweb.countAxis(values, function (v) {
|
||||
return v
|
||||
})
|
||||
break
|
||||
case "bytes":
|
||||
axis = teaweb.bytesAxis(values, function (v) {
|
||||
return v
|
||||
})
|
||||
break
|
||||
}
|
||||
|
||||
let that = this
|
||||
chart.setOption({
|
||||
xAxis: {
|
||||
@@ -115,7 +130,13 @@ Vue.component("metric-chart", {
|
||||
return that.formatTime(v.time)
|
||||
})
|
||||
},
|
||||
yAxis: {},
|
||||
yAxis: {
|
||||
axisLabel: {
|
||||
formatter: function (value) {
|
||||
return value + axis.unit
|
||||
}
|
||||
}
|
||||
},
|
||||
tooltip: {
|
||||
show: true,
|
||||
trigger: "item",
|
||||
@@ -134,7 +155,9 @@ Vue.component("metric-chart", {
|
||||
{
|
||||
name: name,
|
||||
type: "bar",
|
||||
data: values,
|
||||
data: values.map(function (v){
|
||||
return v/axis.divider
|
||||
}),
|
||||
itemStyle: {
|
||||
color: "#9DD3E8"
|
||||
},
|
||||
@@ -151,6 +174,21 @@ Vue.component("metric-chart", {
|
||||
let values = this.stats.map(function (v) {
|
||||
return v.value
|
||||
})
|
||||
|
||||
let axis = {unit: "", divider: 1}
|
||||
switch (this.vValueType) {
|
||||
case "count":
|
||||
axis = teaweb.countAxis(values, function (v) {
|
||||
return v
|
||||
})
|
||||
break
|
||||
case "bytes":
|
||||
axis = teaweb.bytesAxis(values, function (v) {
|
||||
return v
|
||||
})
|
||||
break
|
||||
}
|
||||
|
||||
let that = this
|
||||
chart.setOption({
|
||||
xAxis: {
|
||||
@@ -158,7 +196,13 @@ Vue.component("metric-chart", {
|
||||
return that.formatTime(v.time)
|
||||
})
|
||||
},
|
||||
yAxis: {},
|
||||
yAxis: {
|
||||
axisLabel: {
|
||||
formatter: function (value) {
|
||||
return value + axis.unit
|
||||
}
|
||||
}
|
||||
},
|
||||
tooltip: {
|
||||
show: true,
|
||||
trigger: "item",
|
||||
@@ -177,7 +221,9 @@ Vue.component("metric-chart", {
|
||||
{
|
||||
name: name,
|
||||
type: "line",
|
||||
data: values,
|
||||
data: values.map(function (v) {
|
||||
return v / axis.divider
|
||||
}),
|
||||
itemStyle: {
|
||||
color: "#9DD3E8"
|
||||
},
|
||||
@@ -190,6 +236,20 @@ Vue.component("metric-chart", {
|
||||
let values = this.stats.map(function (v) {
|
||||
return v.value
|
||||
})
|
||||
let axis = {unit: "", divider: 1}
|
||||
switch (this.vValueType) {
|
||||
case "count":
|
||||
axis = teaweb.countAxis(values, function (v) {
|
||||
return v
|
||||
})
|
||||
break
|
||||
case "bytes":
|
||||
axis = teaweb.bytesAxis(values, function (v) {
|
||||
return v
|
||||
})
|
||||
break
|
||||
}
|
||||
|
||||
let that = this
|
||||
chart.setOption({
|
||||
xAxis: {
|
||||
@@ -212,7 +272,13 @@ Vue.component("metric-chart", {
|
||||
return stat.keys[0] + ": " + stat.value + ",占比:" + percent + "%"
|
||||
}
|
||||
},
|
||||
yAxis: {},
|
||||
yAxis: {
|
||||
axisLabel: {
|
||||
formatter: function (value) {
|
||||
return value + axis.unit
|
||||
}
|
||||
}
|
||||
},
|
||||
grid: {
|
||||
left: 40,
|
||||
top: 10,
|
||||
@@ -223,7 +289,9 @@ Vue.component("metric-chart", {
|
||||
{
|
||||
name: name,
|
||||
type: "bar",
|
||||
data: values,
|
||||
data: values.map(function (v) {
|
||||
return v / axis.divider
|
||||
}),
|
||||
itemStyle: {
|
||||
color: "#9DD3E8"
|
||||
},
|
||||
@@ -272,9 +340,9 @@ Vue.component("metric-chart", {
|
||||
}
|
||||
},
|
||||
template: `<div style="float: left" :style="{'width': width}">
|
||||
<div>{{chart.name}}</div>
|
||||
<h4>{{chart.name}} <span>(指标)</span></h4>
|
||||
<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>`
|
||||
})
|
||||
|
||||
|
||||
@@ -362,6 +362,7 @@ window.teaweb = {
|
||||
let tooltipFunc = options.tooltip
|
||||
let axis = options.axis
|
||||
let valueFunc = options.value
|
||||
let click = options.click
|
||||
|
||||
let chartBox = document.getElementById(chartId)
|
||||
if (chartBox == null) {
|
||||
@@ -406,9 +407,14 @@ window.teaweb = {
|
||||
barWidth: "20em"
|
||||
}
|
||||
],
|
||||
animation: true
|
||||
animation: true,
|
||||
}
|
||||
chart.setOption(option)
|
||||
if (click != null) {
|
||||
chart.on("click", function (args) {
|
||||
click.call(this, args, values)
|
||||
})
|
||||
}
|
||||
chart.resize()
|
||||
},
|
||||
renderLineChart: function (options) {
|
||||
|
||||
@@ -669,6 +669,9 @@ td {
|
||||
.source-code-box .CodeMirror-vscrollbar::-webkit-scrollbar-thumb {
|
||||
border-radius: 2px;
|
||||
}
|
||||
.scroll-box {
|
||||
overflow-y: auto;
|
||||
}
|
||||
.scroll-box::-webkit-scrollbar {
|
||||
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 {
|
||||
width: 4px;
|
||||
}
|
||||
|
||||
@@ -47,13 +47,13 @@
|
||||
<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="ui divider"></div>
|
||||
|
||||
<!-- 节点排行 -->
|
||||
<h4>节点排行 <span>(24小时)</span></h4>
|
||||
<h4>节点访问排行 <span>(24小时)</span></h4>
|
||||
<div class="chart-box" id="top-nodes-chart"></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="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.reloadHourlyTrafficChart()
|
||||
this.reloadHourlyRequestsChart()
|
||||
this.reloadTopNodeStatsChart()
|
||||
this.reloadTopNodesChart()
|
||||
this.reloadTopDomainsChart()
|
||||
this.reloadCPUChart()
|
||||
})
|
||||
@@ -265,7 +265,7 @@ Tea.context(function () {
|
||||
}
|
||||
|
||||
// 节点排行
|
||||
this.reloadTopNodeStatsChart = function () {
|
||||
this.reloadTopNodesChart = function () {
|
||||
let that = this
|
||||
let axis = teaweb.countAxis(this.topNodeStats, function (v) {
|
||||
return v.countRequests
|
||||
@@ -283,7 +283,10 @@ Tea.context(function () {
|
||||
value: function (v) {
|
||||
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 () {
|
||||
let axis = {unit: "", divider: 1}
|
||||
let axis = {unit: "%", divider: 1}
|
||||
teaweb.renderLineChart({
|
||||
id: "cpu-chart",
|
||||
name: "CPU",
|
||||
@@ -352,7 +355,7 @@ Tea.context(function () {
|
||||
}
|
||||
|
||||
this.reloadMemoryChart = function () {
|
||||
let axis = {unit: "", divider: 1}
|
||||
let axis = {unit: "%", divider: 1}
|
||||
teaweb.renderLineChart({
|
||||
id: "memory-chart",
|
||||
name: "内存",
|
||||
|
||||
@@ -1,10 +1,12 @@
|
||||
<second-menu>
|
||||
<menu-item :href="'/clusters/cluster/nodes?clusterId=' + clusterId">节点列表</menu-item>
|
||||
<span class="item">|</span>
|
||||
<menu-item :href="'/clusters/cluster/node?clusterId=' + clusterId + '&nodeId=' + nodeId" code="node">节点详情</menu-item>
|
||||
<menu-item :href="'/clusters/cluster/node/monitor?clusterId=' + clusterId + '&nodeId=' + nodeId" code="monitor" v-if="teaIsPlus">监控图表</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/logs?clusterId=' + clusterId + '&nodeId=' + nodeId" code="log">运行日志</menu-item>
|
||||
<menu-item :href="'/clusters/cluster/node/update?clusterId=' + clusterId + '&nodeId=' + nodeId" code="update">修改设置</menu-item>
|
||||
<menu-item :href="'/clusters/cluster/node/install?clusterId=' + clusterId + '&nodeId=' + nodeId" code="install">安装节点</menu-item>
|
||||
<menu-item :href="'/clusters/cluster/node?clusterId=' + clusterId + '&nodeId=' + node.id" code="node"
|
||||
v-if="!teaIsPlus">"{{node.name}}"节点详情</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/detail?clusterId=' + clusterId + '&nodeId=' + node.id" code="node" v-if="teaIsPlus">节点详情</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/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>
|
||||
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 {
|
||||
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