节点列表增加下行流量,节点列表可以按CPU、内存、下行流量排序

This commit is contained in:
GoEdgeLab
2021-07-07 15:17:41 +08:00
parent 623d2bf30f
commit b851fb7c52
10 changed files with 188 additions and 56 deletions

View File

@@ -44,7 +44,7 @@ func init() {
Post("/up", new(node.UpAction)).
Get("/thresholds", new(thresholds.IndexAction)).
Get("/detail", new(node.DetailAction)).
Get("/boards", new(nodeboards.IndexAction)).
GetPost("/boards", new(nodeboards.IndexAction)).
// 分组相关
Prefix("/clusters/cluster/groups").

View File

@@ -178,3 +178,31 @@ func (this *IndexAction) RunGet(params struct {
this.Show()
}
func (this *IndexAction) RunPost(params struct {
ClusterId int64
NodeId int64
}) {
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,
}
this.Success()
}

View File

@@ -29,6 +29,11 @@ func (this *NodesAction) RunGet(params struct {
InstalledState int
ActiveState int
Keyword string
CpuOrder string
MemoryOrder string
TrafficInOrder string
TrafficOutOrder string
}) {
this.Data["groupId"] = params.GroupId
this.Data["regionId"] = params.RegionId
@@ -61,7 +66,7 @@ func (this *NodesAction) RunGet(params struct {
page := this.NewPage(countResp.Count)
this.Data["page"] = page.AsHTML()
nodesResp, err := this.RPC().NodeRPC().ListEnabledNodesMatch(this.AdminContext(), &pb.ListEnabledNodesMatchRequest{
var req = &pb.ListEnabledNodesMatchRequest{
Offset: page.Offset,
Size: page.Size,
NodeClusterId: params.ClusterId,
@@ -70,7 +75,25 @@ func (this *NodesAction) RunGet(params struct {
InstallState: types.Int32(params.InstalledState),
ActiveState: types.Int32(params.ActiveState),
Keyword: params.Keyword,
})
}
if params.CpuOrder == "asc" {
req.CpuAsc = true
} else if params.CpuOrder == "desc" {
req.CpuDesc = true
} else if params.MemoryOrder == "asc" {
req.MemoryAsc = true
} else if params.MemoryOrder == "desc" {
req.MemoryDesc = true
} else if params.TrafficInOrder == "asc" {
req.TrafficInAsc = true
} else if params.TrafficInOrder == "desc" {
req.TrafficInDesc = true
} else if params.TrafficOutOrder == "asc" {
req.TrafficOutAsc = true
} else if params.TrafficOutOrder == "desc" {
req.TrafficOutDesc = true
}
nodesResp, err := this.RPC().NodeRPC().ListEnabledNodesMatch(this.AdminContext(), req)
if err != nil {
this.ErrorPage(err)
return
@@ -146,13 +169,15 @@ func (this *NodesAction) RunGet(params struct {
"error": node.InstallStatus.Error,
},
"status": maps.Map{
"isActive": status.IsActive,
"updatedAt": status.UpdatedAt,
"hostname": status.Hostname,
"cpuUsage": status.CPUUsage,
"cpuUsageText": fmt.Sprintf("%.2f%%", status.CPUUsage*100),
"memUsage": status.MemoryUsage,
"memUsageText": fmt.Sprintf("%.2f%%", status.MemoryUsage*100),
"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),
"trafficInBytes": status.TrafficInBytes,
"trafficOutBytes": status.TrafficOutBytes,
},
"cluster": maps.Map{
"id": node.NodeCluster.Id,

View File

@@ -0,0 +1,48 @@
// 排序使用的箭头
Vue.component("sort-arrow", {
props: ["name"],
data: function () {
let url = window.location.toString()
let order = ""
let newArgs = []
if (window.location.search != null && window.location.search.length > 0) {
let queryString = window.location.search.substring(1)
let pieces = queryString.split("&")
let that = this
pieces.forEach(function (v) {
let eqIndex = v.indexOf("=")
if (eqIndex > 0) {
let argName = v.substring(0, eqIndex)
let argValue = v.substring(eqIndex + 1)
if (argName == that.name) {
order = argValue
} else if (argValue != "asc" && argValue != "desc") {
newArgs.push(v)
}
} else {
newArgs.push(v)
}
})
}
if (order == "asc") {
newArgs.push(this.name + "=")
} else if (order == "desc") {
newArgs.push(this.name + "=asc")
} else {
newArgs.push(this.name + "=desc")
}
let qIndex = url.indexOf("?")
if (qIndex > 0) {
url = url.substring(0, qIndex) + "?" + newArgs.join("&")
} else {
url = url + "?" + newArgs.join("&")
}
return {
order: order,
url: url
}
},
template: `<a :href="url" title="排序">&nbsp; <i class="ui icon long arrow small" :class="{down: order == 'asc', up: order == 'desc', 'down grey': order == '' || order == null}"></i></a>`
})

View File

@@ -21,6 +21,8 @@ form .fields {
table th.center,
table td.center {
text-align: center !important;
padding-left: 0 !important;
padding-right: 0 !important;
}
table th.width10 {
width: 10em;

View File

@@ -1 +1 @@
{"version":3,"sources":["@layout_override.less"],"names":[],"mappings":"AACA,GAAG,OAAO,SAAU,MAAK,MAAM,QAAS,OAAM;AAAS,GAAG,OAAO,SAAU,MAAK,MAAM,QAAS,QAAO;EACrG,oCAAA;;AAGD,GAAG,OAAO,SAAU,MAAK,QAAS,OAAM;AAAS,GAAG,OAAO,SAAU,MAAK,QAAS,QAAO;EACzF,oCAAA;;AAGD,GAAG,MAAM;EACR,kCAAA;;AAID,IACC;EACC,2BAAA;;AAKF,KAAK;EACJ,sBAAA;;AAGD,KAAK,KAAK;EACT,yBAAA;;AAID,KACC,GAAE;AADH,KACY,GAAE;EACZ,6BAAA;;AAFF,KAKC,GAAE;EACD,WAAA;;AANF,KASC,GAAE;EACD,UAAA;;AAVF,KAaC,GAAE;EACD,UAAA;;AAKF,QAAQ;EACP,qBAAA;;AAID,MAAM;EACL,uBAAA","file":"@layout_override.css"}
{"version":3,"sources":["@layout_override.less"],"names":[],"mappings":"AACA,GAAG,OAAO,SAAU,MAAK,MAAM,QAAS,OAAM;AAAS,GAAG,OAAO,SAAU,MAAK,MAAM,QAAS,QAAO;EACrG,oCAAA;;AAGD,GAAG,OAAO,SAAU,MAAK,QAAS,OAAM;AAAS,GAAG,OAAO,SAAU,MAAK,QAAS,QAAO;EACzF,oCAAA;;AAGD,GAAG,MAAM;EACR,kCAAA;;AAID,IACC;EACC,2BAAA;;AAKF,KAAK;EACJ,sBAAA;;AAGD,KAAK,KAAK;EACT,yBAAA;;AAID,KACC,GAAE;AADH,KACY,GAAE;EACZ,6BAAA;EACA,0BAAA;EACA,2BAAA;;AAJF,KAOC,GAAE;EACD,WAAA;;AARF,KAWC,GAAE;EACD,UAAA;;AAZF,KAeC,GAAE;EACD,UAAA;;AAKF,QAAQ;EACP,qBAAA;;AAID,MAAM;EACL,uBAAA","file":"@layout_override.css"}

View File

@@ -31,6 +31,8 @@ form {
table {
th.center, td.center {
text-align: center !important;
padding-left: 0 !important;
padding-right: 0 !important;
}
th.width10 {

View File

@@ -9,17 +9,21 @@ Tea.context(function () {
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.loadBoard = function () {
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.loadBoard()
/**
* 流量统计
@@ -32,6 +36,26 @@ Tea.context(function () {
this.reloadTopDomainsChart()
this.reloadCPUChart()
})
this.$delay(function() {
this.refreshBoard()
}, 30000)
this.refreshBoard = function() {
this.$post("$")
.params({
clusterId: this.clusterId,
nodeId: this.node.id
})
.success(function (resp) {
this.board = resp.data.board
this.loadBoard()
})
.done(function () {
this.$delay(function () {
this.refreshBoard()
}, 60000)
})
}
this.selectTrafficTab = function (tab) {
this.trafficTab = tab

View File

@@ -2,7 +2,7 @@
{$template "menu"}
<form class="ui form" action="/clusters/cluster" v-show="countAll > 0">
<form class="ui form" action="/clusters/cluster/nodes" v-show="countAll > 0">
<input type="hidden" name="clusterId" :value="clusterId"/>
<div class="ui fields inline">
<div class="ui field" v-if="regions.length > 0">
@@ -35,7 +35,8 @@
<input type="text" name="keyword" placeholder="关键词" v-model="keyword" style="width:10em"/>
</div>
<div class="ui field">
<button class="ui button" type="submit">搜索</button>
<button class="ui button" type="submit">搜索</button> &nbsp;
<a :href="'/clusters/cluster/nodes?clusterId=' + clusterId" v-if="regionId >0 || groupId > 0 || installState > 0 || activeState > 0 || keyword.length > 0">[清除条件]</a>
</div>
</div>
</form>
@@ -55,28 +56,24 @@
<thead>
<tr>
<th>节点名称</th>
<th>所属区域</th>
<th>所属分组</th>
<th>IP</th>
<th class="width10">DNS线路</th>
<th class="width5 center">CPU</th>
<th class="width5 center">内存</th>
<!--<th>流量</th>
<th>连接数</th>-->
<th class="width5 center">CPU<sort-arrow name="cpuOrder"></sort-arrow></th>
<th class="width5 center">内存<sort-arrow name="memoryOrder"></sort-arrow></th>
<th class="center" style="width: 7em">下行流量<sort-arrow name="trafficOutOrder"></sort-arrow></th>
<th class="two wide center">状态</th>
<th class="two op">操作</th>
</tr>
</thead>
<tr v-for="node in nodes">
<td>{{node.name}}</td>
<td>
<span v-if="node.region != null">{{node.region.name}}</span>
<span v-else class="disabled">-</span>
<td>{{node.name}}
<div style="margin-top: 0.5em" v-if="node.region != null">
<tiny-label class="basic">区域:{{node.region.name}}</tiny-label>
</div>
<div style="margin-top: 0.5em" v-if="node.group != null">
<tiny-label class="basic">分组:{{node.group.name}}</tiny-label>
</div>
</td>
<td>
<span v-if="node.group != null">{{node.group.name}}</span>
<span v-else class="disabled">-</span>
</td>
<td>
<span v-if="node.ipAddresses.length == 0" class="disabled">-</span>
<div v-else class="address-box">
@@ -104,6 +101,10 @@
<span v-if="node.status.isActive" :class="{red:node.status.memUsage > 0.80}">{{node.status.memUsageText}}</span>
<span v-else class="disabled">-</span>
</td>
<td class="center">
<span v-if="node.status.isActive && node.status.trafficOutBytes > 0">{{teaweb.formatBytes(node.status.trafficOutBytes)}}<br/><span class="grey small">/分钟</span></span>
<span v-else class="disabled">-</span>
</td>
<td class="center">
<div v-if="!node.isUp">
<span class="red">健康问题下线</span>

View File

@@ -1,21 +1,23 @@
Tea.context(function () {
this.deleteNode = function (nodeId) {
teaweb.confirm("确定要删除这个节点吗?", function () {
this.$post("/nodes/delete")
.params({
nodeId: nodeId
})
.refresh();
})
}
this.teaweb = teaweb
this.upNode = function (nodeId) {
teaweb.confirm("确定要手动上线此节点吗?", function () {
this.$post("/clusters/cluster/node/up")
.params({
nodeId: nodeId
})
.refresh()
})
}
this.deleteNode = function (nodeId) {
teaweb.confirm("确定要删除这个节点吗?", function () {
this.$post("/nodes/delete")
.params({
nodeId: nodeId
})
.refresh();
})
}
this.upNode = function (nodeId) {
teaweb.confirm("确定要手动上线此节点吗?", function () {
this.$post("/clusters/cluster/node/up")
.params({
nodeId: nodeId
})
.refresh()
})
}
})