实现基础的IP地址阈值

This commit is contained in:
GoEdgeLab
2021-09-12 20:21:32 +08:00
parent 40c5c78d6e
commit d4494d0191
31 changed files with 788 additions and 267 deletions

View File

@@ -103,6 +103,10 @@ func (this *RPCClient) NodeIPAddressLogRPC() pb.NodeIPAddressLogServiceClient {
return pb.NewNodeIPAddressLogServiceClient(this.pickConn())
}
func (this *RPCClient) NodeIPAddressThresholdRPC() pb.NodeIPAddressThresholdServiceClient {
return pb.NewNodeIPAddressThresholdServiceClient(this.pickConn())
}
func (this *RPCClient) NodeValueRPC() pb.NodeValueServiceClient {
return pb.NewNodeValueServiceClient(this.pickConn())
}

View File

@@ -167,32 +167,48 @@ func (this *CreateNodeAction) RunPost(params struct {
nodeId := createResp.NodeId
// IP地址
for _, address := range ipAddresses {
addressId := address.GetInt64("id")
if addressId > 0 {
for _, addr := range ipAddresses {
addrId := addr.GetInt64("id")
if addrId > 0 {
_, err = this.RPC().NodeIPAddressRPC().UpdateNodeIPAddressNodeId(this.AdminContext(), &pb.UpdateNodeIPAddressNodeIdRequest{
NodeIPAddressId: addressId,
NodeIPAddressId: addrId,
NodeId: nodeId,
})
} else {
var thresholdsJSON = []byte{}
var thresholds = address.GetSlice("thresholds")
if len(thresholds) > 0 {
thresholdsJSON, _ = json.Marshal(thresholds)
if err != nil {
this.ErrorPage(err)
return
}
_, err = this.RPC().NodeIPAddressRPC().CreateNodeIPAddress(this.AdminContext(), &pb.CreateNodeIPAddressRequest{
NodeId: nodeId,
Role: nodeconfigs.NodeRoleNode,
Name: address.GetString("name"),
Ip: address.GetString("ip"),
CanAccess: address.GetBool("canAccess"),
ThresholdsJSON: thresholdsJSON,
} else {
createResp, err := this.RPC().NodeIPAddressRPC().CreateNodeIPAddress(this.AdminContext(), &pb.CreateNodeIPAddressRequest{
NodeId: nodeId,
Role: nodeconfigs.NodeRoleNode,
Name: addr.GetString("name"),
Ip: addr.GetString("ip"),
CanAccess: addr.GetBool("canAccess"),
})
if err != nil {
this.ErrorPage(err)
return
}
addrId = createResp.NodeIPAddressId
}
if err != nil {
this.ErrorPage(err)
return
// 阈值
var thresholds = addr.GetSlice("thresholds")
if len(thresholds) > 0 {
thresholdsJSON, err := json.Marshal(thresholds)
if err != nil {
this.ErrorPage(err)
return
}
_, err = this.RPC().NodeIPAddressThresholdRPC().UpdateAllNodeIPAddressThresholds(this.AdminContext(), &pb.UpdateAllNodeIPAddressThresholdsRequest{
NodeIPAddressId: addrId,
NodeIPAddressThresholdsJSON: thresholdsJSON,
})
if err != nil {
this.ErrorPage(err)
return
}
}
}

View File

@@ -7,6 +7,7 @@ import (
"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/EdgeAdmin/internal/web/actions/default/nodes/ipAddresses/ipaddressutils"
"github.com/TeaOSLab/EdgeCommon/pkg/nodeconfigs"
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
"github.com/iwind/TeaGo/maps"
@@ -79,14 +80,12 @@ func (this *DetailAction) RunGet(params struct {
var ipAddresses = ipAddressesResp.Addresses
ipAddressMaps := []maps.Map{}
for _, addr := range ipAddressesResp.Addresses {
var thresholds = []*nodeconfigs.NodeValueThresholdConfig{}
if len(addr.ThresholdsJSON) > 0 {
err = json.Unmarshal(addr.ThresholdsJSON, &thresholds)
if err != nil {
this.ErrorPage(err)
return
}
thresholds, err := ipaddressutils.InitNodeIPAddressThresholds(this.Parent(), addr.Id)
if err != nil {
this.ErrorPage(err)
return
}
ipAddressMaps = append(ipAddressMaps, maps.Map{
"id": addr.Id,
"name": addr.Name,

View File

@@ -57,13 +57,10 @@ func (this *UpdateAction) RunGet(params struct {
}
ipAddressMaps := []maps.Map{}
for _, addr := range ipAddressesResp.Addresses {
var thresholds = []*nodeconfigs.NodeValueThresholdConfig{}
if len(addr.ThresholdsJSON) > 0 {
err = json.Unmarshal(addr.ThresholdsJSON, &thresholds)
if err != nil {
this.ErrorPage(err)
return
}
thresholds, err := ipaddressutils.InitNodeIPAddressThresholds(this.Parent(), addr.Id)
if err != nil {
this.ErrorPage(err)
return
}
ipAddressMaps = append(ipAddressMaps, maps.Map{

View File

@@ -30,19 +30,21 @@ func (this *CreatePopupAction) RunPost(params struct {
IP string `alias:"ip"`
CanAccess bool
Name string
IsUp bool
ThresholdsJSON []byte
Must *actions.Must
}) {
ip := net.ParseIP(params.IP)
if len(ip) == 0 {
this.Fail("请输入正确的IP")
}
params.Must.
Field("ip", params.IP).
Require("请输入IP地址")
ip := net.ParseIP(params.IP)
if len(ip) == 0 {
this.FailField("ip", "请输入正确的IP")
}
// 阈值设置
var thresholds = []*nodeconfigs.NodeValueThresholdConfig{}
if teaconst.IsPlus && len(params.ThresholdsJSON) > 0 {
_ = json.Unmarshal(params.ThresholdsJSON, &thresholds)
@@ -54,7 +56,7 @@ func (this *CreatePopupAction) RunPost(params struct {
"ip": params.IP,
"id": 0,
"isOn": true,
"isUp": true,
"isUp": params.IsUp,
"thresholds": thresholds,
}
this.Success()

View File

@@ -16,12 +16,6 @@ func UpdateNodeIPAddresses(parentAction *actionutils.ParentAction, nodeId int64,
return err
}
for _, addr := range addresses {
var thresholdsJSON = []byte{}
var thresholds = addr.GetSlice("thresholds")
if len(thresholds) > 0 {
thresholdsJSON, _ = json.Marshal(thresholds)
}
addrId := addr.GetInt64("id")
if addrId > 0 {
var isOn = false
@@ -37,19 +31,34 @@ func UpdateNodeIPAddresses(parentAction *actionutils.ParentAction, nodeId int64,
Name: addr.GetString("name"),
CanAccess: addr.GetBool("canAccess"),
IsOn: isOn,
ThresholdsJSON: thresholdsJSON,
})
if err != nil {
return err
}
} else {
_, err = parentAction.RPC().NodeIPAddressRPC().CreateNodeIPAddress(parentAction.AdminContext(), &pb.CreateNodeIPAddressRequest{
NodeId: nodeId,
Role: role,
Name: addr.GetString("name"),
Ip: addr.GetString("ip"),
CanAccess: addr.GetBool("canAccess"),
ThresholdsJSON: thresholdsJSON,
createResp, err := parentAction.RPC().NodeIPAddressRPC().CreateNodeIPAddress(parentAction.AdminContext(), &pb.CreateNodeIPAddressRequest{
NodeId: nodeId,
Role: role,
Name: addr.GetString("name"),
Ip: addr.GetString("ip"),
CanAccess: addr.GetBool("canAccess"),
})
if err != nil {
return err
}
addrId = createResp.NodeIPAddressId
}
// 保存阈值
var thresholds = addr.GetSlice("thresholds")
if len(thresholds) > 0 {
thresholdsJSON, err := json.Marshal(thresholds)
if err != nil {
return err
}
_, err = parentAction.RPC().NodeIPAddressThresholdRPC().UpdateAllNodeIPAddressThresholds(parentAction.AdminContext(), &pb.UpdateAllNodeIPAddressThresholdsRequest{
NodeIPAddressId: addrId,
NodeIPAddressThresholdsJSON: thresholdsJSON,
})
if err != nil {
return err
@@ -58,3 +67,35 @@ func UpdateNodeIPAddresses(parentAction *actionutils.ParentAction, nodeId int64,
}
return nil
}
// InitNodeIPAddressThresholds 初始化IP阈值
func InitNodeIPAddressThresholds(parentAction *actionutils.ParentAction, addrId int64) ([]*nodeconfigs.NodeValueThresholdConfig, error) {
thresholdsResp, err := parentAction.RPC().NodeIPAddressThresholdRPC().FindAllEnabledNodeIPAddressThresholds(parentAction.AdminContext(), &pb.FindAllEnabledNodeIPAddressThresholdsRequest{NodeIPAddressId: addrId})
if err != nil {
return nil, err
}
var thresholds = []*nodeconfigs.NodeValueThresholdConfig{}
if len(thresholdsResp.NodeIPAddressThresholds) > 0 {
for _, pbThreshold := range thresholdsResp.NodeIPAddressThresholds {
var threshold = &nodeconfigs.NodeValueThresholdConfig{
Id: pbThreshold.Id,
Items: []*nodeconfigs.NodeValueThresholdItemConfig{},
Actions: []*nodeconfigs.NodeValueThresholdActionConfig{},
}
if len(pbThreshold.ItemsJSON) > 0 {
err = json.Unmarshal(pbThreshold.ItemsJSON, &threshold.Items)
if err != nil {
return nil, err
}
}
if len(pbThreshold.ActionsJSON) > 0 {
err = json.Unmarshal(pbThreshold.ActionsJSON, &threshold.Actions)
if err != nil {
return nil, err
}
}
thresholds = append(thresholds, threshold)
}
}
return thresholds, nil
}

View File

@@ -34,6 +34,7 @@ func (this *UpdatePopupAction) RunPost(params struct {
Name string
CanAccess bool
IsOn bool
IsUp bool
ThresholdsJSON []byte
Must *actions.Must
@@ -43,7 +44,7 @@ func (this *UpdatePopupAction) RunPost(params struct {
Require("请输入IP地址")
// 获取IP地址信息
var isUp = true
var isUp = params.IsUp
if params.AddressId > 0 {
addressResp, err := this.RPC().NodeIPAddressRPC().FindEnabledNodeIPAddress(this.AdminContext(), &pb.FindEnabledNodeIPAddressRequest{NodeIPAddressId: params.AddressId})
if err != nil {

View File

@@ -26,5 +26,5 @@ Vue.component("micro-basic-label", {
// 灰色的Label
Vue.component("grey-label", {
template: `<span class="grey small"><slot></slot></span>`
template: `<span class="ui label basic grey tiny" style="font-size: 0.7em; border: 1px solid #ddd"><slot></slot></span>`
})

View File

@@ -5,209 +5,459 @@ Vue.component("node-ip-address-thresholds-box", {
let thresholds = this.vThresholds
if (thresholds == null) {
thresholds = []
} else {
thresholds.forEach(function (v) {
if (v.items == null) {
v.items = []
}
if (v.actions == null) {
v.actions = []
}
})
}
let avgRequests = {
duration: "",
operator: "lte",
value: ""
}
let avgTrafficOut = {
duration: "",
operator: "lte",
value: ""
}
let avgTrafficIn = {
duration: "",
operator: "lte",
value: ""
}
thresholds.forEach(function (v) {
switch (v.item) {
case "avgRequests":
avgRequests.duration = v.duration
avgRequests.operator = v.operator
avgRequests.value = v.value.toString()
break
case "avgTrafficOut":
avgTrafficOut.duration = v.duration
avgTrafficOut.operator = v.operator
avgTrafficOut.value = v.value.toString()
break
case "avgTrafficIn":
avgTrafficIn.duration = v.duration
avgTrafficIn.operator = v.operator
avgTrafficIn.value = v.value.toString()
break
}
})
return {
editingIndex: -1,
thresholds: thresholds,
avgRequests: avgRequests,
avgTrafficOut: avgTrafficOut,
avgTrafficIn: avgTrafficIn
}
},
watch: {
"avgRequests.duration": function () {
this.compose()
},
"avgRequests.operator": function () {
this.compose()
},
"avgRequests.value": function () {
this.compose()
},
"avgTrafficOut.duration": function () {
this.compose()
},
"avgTrafficOut.operator": function () {
this.compose()
},
"avgTrafficOut.value": function () {
this.compose()
},
"avgTrafficIn.duration": function () {
this.compose()
},
"avgTrafficIn.operator": function () {
this.compose()
},
"avgTrafficIn.value": function () {
this.compose()
addingThreshold: {
items: [],
actions: []
},
isAdding: false,
isAddingItem: false,
isAddingAction: false,
itemCode: "avgRequests",
itemReportGroups: [],
itemOperator: "lte",
itemValue: "",
itemDuration: 5,
allItems: [
{
"name": "平均请求数",
"code": "avgRequests",
"description": "在单位时间内接收到的平均请求数。",
"unit": "个"
},
{
"name": "平均下行流量",
"code": "avgTrafficOut",
"description": "在单位时间内发送的下行流量。",
"unit": "M"
},
{
"name": "平均上行流量",
"code": "avgTrafficIn",
"description": "在单位时间内接收的上行流量。",
"unit": "M"
},
{
"name": "连通性",
"code": "connectivity",
"description": "通过区域监控得到的连通性数值取值在0和100之间。",
"unit": "%"
},
],
allOperators: [
{
"name": "小于等于",
"code": "lte"
},
{
"name": "大于",
"code": "gt"
},
{
"name": "不等于",
"code": "neq"
},
{
"name": "小于",
"code": "lt"
},
{
"name": "大于等于",
"code": "gte"
}
],
allActions: [
{
"name": "上线",
"code": "up",
"description": "上线当前IP。"
},
{
"name": "下线",
"code": "down",
"description": "下线当前IP。"
},
{
"name": "通知",
"code": "notify",
"description": "发送已达到阈值通知。"
}
],
actionCode: "up"
}
},
methods: {
compose: function () {
let thresholds = []
// avg requests
{
let duration = parseInt(this.avgRequests.duration)
let value = parseInt(this.avgRequests.value)
if (!isNaN(duration) && duration > 0 && !isNaN(value) && value > 0) {
thresholds.push({
item: "avgRequests",
operator: this.avgRequests.operator,
duration: duration,
durationUnit: "minute",
value: value
})
}
add: function () {
this.isAdding = !this.isAdding
},
cancel: function () {
this.isAdding = false
this.editingIndex = -1
this.addingThreshold = {
items: [],
actions: []
}
},
confirm: function () {
if (this.addingThreshold.items.length == 0) {
teaweb.warn("需要至少添加一个阈值")
return
}
if (this.addingThreshold.actions.length == 0) {
teaweb.warn("需要至少添加一个动作")
return
}
// avg traffic out
{
let duration = parseInt(this.avgTrafficOut.duration)
let value = parseInt(this.avgTrafficOut.value)
if (!isNaN(duration) && duration > 0 && !isNaN(value) && value > 0) {
thresholds.push({
item: "avgTrafficOut",
operator: this.avgTrafficOut.operator,
duration: duration,
durationUnit: "minute",
value: value
})
}
if (this.editingIndex >= 0) {
this.thresholds[this.editingIndex].items = this.addingThreshold.items
this.thresholds[this.editingIndex].actions = this.addingThreshold.actions
} else {
this.thresholds.push({
items: this.addingThreshold.items,
actions: this.addingThreshold.actions
})
}
// avg requests
{
let duration = parseInt(this.avgTrafficIn.duration)
let value = parseInt(this.avgTrafficIn.value)
if (!isNaN(duration) && duration > 0 && !isNaN(value) && value > 0) {
thresholds.push({
item: "avgTrafficIn",
operator: this.avgTrafficIn.operator,
duration: duration,
durationUnit: "minute",
value: value
})
}
// 还原
this.cancel()
},
remove: function (index) {
this.cancel()
this.thresholds.$remove(index)
},
update: function (index) {
this.editingIndex = index
this.addingThreshold = {
items: this.thresholds[index].items.$copy(),
actions: this.thresholds[index].actions.$copy()
}
this.isAdding = true
},
addItem: function () {
this.isAddingItem = !this.isAddingItem
let that = this
setTimeout(function () {
that.$refs.itemValue.focus()
}, 100)
},
cancelItem: function () {
this.isAddingItem = false
this.itemCode = "avgRequests"
this.itmeOperator = "lte"
this.itemValue = ""
this.itemDuration = "1"
this.itemReportGroups = []
},
confirmItem: function () {
if (this.itemDuration.length == 0) {
let that = this
teaweb.warn("请输入统计周期", function () {
that.$refs.itemDuration.focus()
})
return
}
let itemDuration = parseInt(this.itemDuration)
if (isNaN(itemDuration) || itemDuration <= 0) {
teaweb.warn("请输入正确的统计周期", function () {
that.$refs.itemDuration.focus()
})
return
}
this.thresholds = thresholds
if (this.itemValue.length == 0) {
let that = this
teaweb.warn("请输入对比值", function () {
that.$refs.itemValue.focus()
})
return
}
let itemValue = parseFloat(this.itemValue)
if (isNaN(itemValue)) {
teaweb.warn("请输入正确的对比值", function () {
that.$refs.itemValue.focus()
})
return
}
let options = {}
switch (this.itemCode) {
case "connectivity": // 连通性校验
if (itemValue > 100) {
let that = this
teaweb.warn("连通性对比值不能超过100", function () {
that.$refs.itemValue.focus()
})
return
}
options["groups"] = this.itemReportGroups
break
}
// 添加
this.addingThreshold.items.push({
item: this.itemCode,
operator: this.itemOperator,
value: itemValue,
duration: itemDuration,
durationUnit: "minute",
options: options
})
// 还原
this.cancelItem()
},
removeItem: function (index) {
this.cancelItem()
this.addingThreshold.items.$remove(index)
},
changeReportGroups: function (groups) {
this.itemReportGroups = groups
},
itemName: function (item) {
let result = ""
this.allItems.forEach(function (v) {
if (v.code == item) {
result = v.name
}
})
return result
},
itemUnitName: function (itemCode) {
let result = ""
this.allItems.forEach(function (v) {
if (v.code == itemCode) {
result = v.unit
}
})
return result
},
itemDurationUnitName: function (unit) {
switch (unit) {
case "minute":
return "分钟"
case "second":
return "秒"
case "hour":
return "小时"
case "day":
return "天"
}
return unit
},
itemOperatorName: function (operator) {
let result = ""
this.allOperators.forEach(function (v) {
if (v.code == operator) {
result = v.name
}
})
return result
},
addAction: function () {
this.isAddingAction = !this.isAddingAction
},
cancelAction: function () {
this.isAddingAction = false
this.actionCode = "up"
},
confirmAction: function () {
// 是否已存在
let exists = false
let that = this
this.addingThreshold.actions.forEach(function (v) {
if (v.action == that.actionCode) {
exists = true
}
})
if (exists) {
teaweb.warn("此动作已经添加过了,无需重复添加")
return
}
this.addingThreshold.actions.push({
action: this.actionCode,
options: {}
})
// 还原
this.cancelAction()
},
removeAction: function (index) {
this.cancelAction()
this.addingThreshold.actions.$remove(index)
},
actionName: function (actionCode) {
let result = ""
this.allActions.forEach(function (v) {
if (v.code == actionCode) {
result = v.name
}
})
return result
}
},
template: `<div>
<input type="hidden" name="thresholdsJSON" :value="JSON.stringify(thresholds)"/>
<table class="ui table celled">
<thead>
<!-- 已有条件 -->
<div v-if="thresholds.length > 0">
<div class="ui label basic small" v-for="(threshold, index) in thresholds" style="margin-bottom: 0.5em; font-weight: normal">
<span v-for="(item, itemIndex) in threshold.items">[{{item.duration}}{{itemDurationUnitName(item.durationUnit)}}] {{itemName(item.item)}}
<!-- 连通性 -->
<span v-if="item.item == 'connectivity' && item.options != null && item.options.groups != null && item.options.groups.length > 0">[<span v-for="(group, groupIndex) in item.options.groups">{{group.name}} <span v-if="groupIndex != item.options.groups.length - 1">&nbsp; </span></span>]</span>
<span class="grey">[{{itemOperatorName(item.operator)}}]</span> &nbsp;{{item.value}}{{itemUnitName(item.item)}} &nbsp;<span v-if="itemIndex != threshold.items.length - 1" style="font-style: italic">AND &nbsp;</span></span>
-&gt;
<span v-for="(action, actionIndex) in threshold.actions">{{actionName(action.action)}} &nbsp;<span v-if="actionIndex != threshold.actions.length - 1" style="font-style: italic">AND &nbsp;</span></span>
&nbsp;
<a href="" title="修改" @click.prevent="update(index)"><i class="icon pencil small"></i></a>
<a href="" title="删除" @click.prevent="remove(index)"><i class="icon small remove"></i></a>
</div>
</div>
<!-- 新阈值 -->
<div v-if="isAdding" style="margin-top: 0.5em">
<table class="ui table celled">
<thead>
<tr>
<td style="width: 50%; background: #f9fafb; border-bottom: 1px solid rgba(34,36,38,.1)">阈值</td>
<th>动作</th>
</tr>
</thead>
<tr>
<td>统计项目</td>
<th>统计周期</th>
<th>操作符</th>
<th>对比值</th>
</tr>
</thead>
<tr>
<td>平均请求数/秒</td>
<td>
<div class="ui input right labeled">
<input type="text" style="width: 5em" v-model="avgRequests.duration"/>
<span class="ui label">分钟</span>
</div>
</td>
<td>
<select class="ui dropdown auto-width" v-model="avgRequests.operator">
<option value="lte">小于等于</option>
<option value="gt">大于</option>
</select>
</td>
<td>
<div class="ui input right labeled">
<input type="text" style="width: 6em" v-model="avgRequests.value"/>
<span class="ui label">个</span>
</div>
</td>
</tr>
<tr>
<td class="title">平均下行流量/秒</td>
<td>
<div class="ui input right labeled">
<input type="text" style="width: 5em" v-model="avgTrafficOut.duration"/>
<span class="ui label">分钟</span>
</div>
</td>
<td>
<select class="ui dropdown auto-width" v-model="avgTrafficOut.operator">
<option value="lte">小于等于</option>
<option value="gt">大于</option>
</select>
</td>
<td>
<div class="ui input right labeled">
<input type="text" style="width: 6em" v-model="avgTrafficOut.value"/>
<span class="ui label">MB</span>
</div>
</td>
</tr>
<tr>
<td>平均上行流量/秒</td>
<td>
<div class="ui input right labeled">
<input type="text" style="width: 5em" v-model="avgTrafficIn.duration"/>
<span class="ui label">分钟</span>
</div>
</td>
<td>
<select class="ui dropdown auto-width" v-model="avgTrafficIn.operator">
<option value="lte">小于等于</option>
<option value="gt">大于</option>
</select>
</td>
<td>
<div class="ui input right labeled">
<input type="text" style="width: 6em" v-model="avgTrafficIn.value"/>
<span class="ui label">MB</span>
</div>
</td>
</tr>
</table>
<p class="comment">满足所有阈值条件时IP才会上线否则下线。统计周期和对比值设置为0表示没有限制。各个输入项只支持整数数字。</p>
<td style="background: white">
<!-- 已经添加的项目 -->
<div>
<div v-for="(item, index) in addingThreshold.items" class="ui label basic small" style="margin-bottom: 0.5em; font-weight: normal">
[{{item.duration}}{{itemDurationUnitName(item.durationUnit)}}] {{itemName(item.item)}}
<!-- 连通性 -->
<span v-if="item.item == 'connectivity' && item.options != null && item.options.groups != null && item.options.groups.length > 0">[<span v-for="(group, groupIndex) in item.options.groups">{{group.name}} <span v-if="groupIndex != item.options.groups.length - 1">&nbsp; </span></span>]</span>
<span class="grey">[{{itemOperatorName(item.operator)}}]</span> {{item.value}}{{itemUnitName(item.item)}} &nbsp;
<a href="" title="删除" @click.prevent="removeItem(index)"><i class="icon remove small"></i></a>
</div>
</div>
<!-- 正在添加的项目 -->
<div v-if="isAddingItem" style="margin-top: 0.8em">
<table class="ui table">
<tr>
<td style="width: 6em">统计项目</td>
<td>
<select class="ui dropdown auto-width" v-model="itemCode">
<option v-for="item in allItems" :value="item.code">{{item.name}}</option>
</select>
<p class="comment" v-for="item in allItems" v-if="item.code == itemCode">{{item.description}}</p>
</td>
</tr>
<tr>
<td>统计周期</td>
<td>
<div class="ui input right labeled">
<input type="text" v-model="itemDuration" style="width: 4em" maxlength="4" ref="itemDuration" @keyup.enter="confirmItem()" @keypress.enter.prevent="1"/>
<span class="ui label">分钟</span>
</div>
</td>
</tr>
<tr>
<td>操作符</td>
<td>
<select class="ui dropdown auto-width" v-model="itemOperator">
<option v-for="operator in allOperators" :value="operator.code">{{operator.name}}</option>
</select>
</td>
</tr>
<tr>
<td>对比值</td>
<td>
<div class="ui input right labeled">
<input type="text" maxlength="20" style="width: 5em" v-model="itemValue" ref="itemValue" @keyup.enter="confirmItem()" @keypress.enter.prevent="1"/>
<span class="ui label" v-for="item in allItems" v-if="item.code == itemCode">{{item.unit}}</span>
</div>
</td>
</tr>
<!-- 连通性 -->
<tr v-if="itemCode == 'connectivity'">
<td>终端分组</td>
<td style="font-weight: normal">
<div style="zoom: 0.8"><report-node-groups-selector @change="changeReportGroups"></report-node-groups-selector></div>
</td>
</tr>
</table>
<div style="margin-top: 0.8em">
<button class="ui button tiny" type="button" @click.prevent="confirmItem">确定</button> &nbsp;
<a href="" title="取消" @click.prevent="cancelItem"><i class="icon remove small"></i></a>
</div>
</div>
<div style="margin-top: 0.8em" v-if="!isAddingItem">
<button class="ui button tiny" type="button" @click.prevent="addItem">+</button>
</div>
</td>
<td style="background: white">
<!-- 已经添加的动作 -->
<div>
<div v-for="(action, index) in addingThreshold.actions" class="ui label basic small" style="margin-bottom: 0.5em">
{{actionName(action.action)}} &nbsp;
<a href="" title="删除" @click.prevent="removeAction(index)"><i class="icon remove small"></i></a>
</div>
</div>
<!-- 正在添加的动作 -->
<div v-if="isAddingAction" style="margin-top: 0.8em">
<table class="ui table">
<tr>
<td style="width: 6em">动作类型</td>
<td>
<select class="ui dropdown auto-width" v-model="actionCode">
<option v-for="action in allActions" :value="action.code">{{action.name}}</option>
</select>
<p class="comment" v-for="action in allActions" v-if="action.code == actionCode">{{action.description}}</p>
</td>
</tr>
</table>
<div style="margin-top: 0.8em">
<button class="ui button tiny" type="button" @click.prevent="confirmAction">确定</button> &nbsp;
<a href="" title="取消" @click.prevent="cancelAction"><i class="icon remove small"></i></a>
</div>
</div>
<div style="margin-top: 0.8em" v-if="!isAddingAction">
<button class="ui button tiny" type="button" @click.prevent="addAction">+</button>
</div>
</td>
</tr>
</table>
<!-- 添加阈值 -->
<div>
<button class="ui button tiny" :class="{disabled: (isAddingItem || isAddingAction)}" type="button" @click.prevent="confirm">确定</button> &nbsp;
<a href="" title="取消" @click.prevent="cancel"><i class="icon remove small"></i></a>
</div>
</div>
<div v-if="!isAdding" style="margin-top: 0.5em">
<button class="ui button tiny" type="button" @click.prevent="add">+</button>
</div>
</div>`
})

View File

@@ -0,0 +1,153 @@
// 节点IP阈值
Vue.component("node-ip-address-thresholds-view", {
props: ["v-thresholds"],
data: function () {
let thresholds = this.vThresholds
if (thresholds == null) {
thresholds = []
} else {
thresholds.forEach(function (v) {
if (v.items == null) {
v.items = []
}
if (v.actions == null) {
v.actions = []
}
})
}
return {
thresholds: thresholds,
allItems: [
{
"name": "平均请求数",
"code": "avgRequests",
"description": "在单位时间内接收到的平均请求数。",
"unit": "个"
},
{
"name": "平均下行流量",
"code": "avgTrafficOut",
"description": "在单位时间内发送的下行流量。",
"unit": "M"
},
{
"name": "平均上行流量",
"code": "avgTrafficIn",
"description": "在单位时间内接收的上行流量。",
"unit": "M"
},
{
"name": "连通性",
"code": "connectivity",
"description": "通过区域监控得到的连通性数值取值在0和100之间。",
"unit": "%"
},
],
allOperators: [
{
"name": "小于等于",
"code": "lte"
},
{
"name": "大于",
"code": "gt"
},
{
"name": "不等于",
"code": "neq"
},
{
"name": "小于",
"code": "lt"
},
{
"name": "大于等于",
"code": "gte"
}
],
allActions: [
{
"name": "上线",
"code": "up",
"description": "上线当前IP。"
},
{
"name": "下线",
"code": "down",
"description": "下线当前IP。"
},
{
"name": "通知",
"code": "notify",
"description": "发送已达到阈值通知。"
}
]
}
},
methods: {
itemName: function (item) {
let result = ""
this.allItems.forEach(function (v) {
if (v.code == item) {
result = v.name
}
})
return result
},
itemUnitName: function (itemCode) {
let result = ""
this.allItems.forEach(function (v) {
if (v.code == itemCode) {
result = v.unit
}
})
return result
},
itemDurationUnitName: function (unit) {
switch (unit) {
case "minute":
return "分钟"
case "second":
return "秒"
case "hour":
return "小时"
case "day":
return "天"
}
return unit
},
itemOperatorName: function (operator) {
let result = ""
this.allOperators.forEach(function (v) {
if (v.code == operator) {
result = v.name
}
})
return result
},
actionName: function (actionCode) {
let result = ""
this.allActions.forEach(function (v) {
if (v.code == actionCode) {
result = v.name
}
})
return result
}
},
template: `<div>
<!-- 已有条件 -->
<div v-if="thresholds.length > 0">
<div class="ui label basic small" v-for="(threshold, index) in thresholds" style="margin-bottom: 0.5em; font-weight: normal">
<span v-for="(item, itemIndex) in threshold.items">[{{item.duration}}{{itemDurationUnitName(item.durationUnit)}}] {{itemName(item.item)}}
<!-- 连通性 -->
<span v-if="item.item == 'connectivity' && item.options != null && item.options.groups != null && item.options.groups.length > 0">[<span v-for="(group, groupIndex) in item.options.groups">{{group.name}} <span v-if="groupIndex != item.options.groups.length - 1">&nbsp; </span></span>]</span>
<span class="grey">[{{itemOperatorName(item.operator)}}]</span> {{item.value}}{{itemUnitName(item.item)}} &nbsp;<span v-if="itemIndex != threshold.items.length - 1" style="font-style: italic">AND &nbsp;</span></span>
-&gt;
<span v-for="(action, actionIndex) in threshold.actions">{{actionName(action.action)}} &nbsp;<span v-if="actionIndex != threshold.actions.length - 1" style="font-style: italic">AND &nbsp;</span></span>
</div>
</div>
</div>`
})

View File

@@ -56,7 +56,7 @@ Vue.component("node-ip-addresses-box", {
<span class="small grey" v-if="address.name.length == 0 && !address.canAccess">(不可访问)</span>
<span class="small red" v-if="!address.isOn" title="未启用">[off]</span>
<span class="small red" v-if="!address.isUp" title="已下线">[down]</span>
<span class="small" v-if="address.thresholds != null && address.thresholds.length > 0">[阈值]</span>
<span class="small" v-if="address.thresholds != null && address.thresholds.length > 0">[{{address.thresholds.length}}个阈值]</span>
&nbsp;
<a href="" title="修改" @click.prevent="updateIPAddress(index, address)"><i class="icon pencil small"></i></a>
<a href="" title="删除" @click.prevent="removeIPAddress(index)"><i class="icon remove"></i></a>

View File

@@ -36,6 +36,24 @@ Vue.component("report-node-groups-selector", {
that.groupIds.push(v.id)
}
})
this.change()
},
change: function () {
let that = this
let groups = []
this.groupIds.forEach(function (groupId) {
let group = that.groups.$find(function (k, v) {
return v.id == groupId
})
if (group == null) {
return
}
groups.push({
id: group.id,
name: group.name
})
})
this.$emit("change", groups)
}
},
watch: {
@@ -46,6 +64,8 @@ Vue.component("report-node-groups-selector", {
v.isChecked = false
})
}
this.change()
}
},
template: `<div>

View File

@@ -31,6 +31,10 @@
</div>
</td>
</tr>
<tr v-if="domainId == 0">
<td>线路</td>
<td><span class="disabled">当前集群没有选择域名。</span></td>
</tr>
</table>
<submit-btn></submit-btn>

View File

@@ -11,6 +11,8 @@
</div>
<div class="ui field">
<button class="ui button" type="submit">搜索</button>
&nbsp;
<a :href="Tea.url('.')" v-if="keyword.length > 0">[清除条件]</a>
</div>
</div>
</form>

View File

@@ -11,7 +11,8 @@
<tr>
<td>状态</td>
<td>
<label-on :v-is-on="addr.isOn" v-if="!addr.isOn"></label-on>
<span v-if="!addr.canAccess" class="red">不可访问</span>
<span v-else-if="!addr.isOn" class="red">禁用</span>
<span v-else-if="addr.isUp" class="green">在线</span>
<span v-else class="red">离线</span>
</td>
@@ -33,20 +34,11 @@
<span v-else class="disabled">-</span>
</td>
</tr>
<tr>
<td>是否可以访问</td>
<td>
<span v-if="addr.canAccess" class="green">Y</span>
<span v-else class="disabled">N</span>
</td>
</tr>
<tr>
<td>阈值</td>
<td>
<div v-if="addr.thresholds.length > 0">
<span v-for="threshold in addr.thresholds" class="ui basic tiny label">
{{threshold.item}} {{threshold.operator}} {{threshold.value}}
</span>
<node-ip-address-thresholds-view :v-thresholds="addr.thresholds"></node-ip-address-thresholds-view>
</div>
<span v-else class="disabled">没有设置阈值。</span>
</td>

View File

@@ -42,7 +42,7 @@
<td>
<a :href="Tea.url('.reporter', {reporterId: reporter.id})"><keyword :v-word="keyword">{{reporter.name}}</keyword></a>
<div v-if="reporter.groups.length > 0" style="margin-top: 0.5em">
<div class="ui label basic tiny" v-for="group in reporter.groups"><span class="small">{{group.name}}</span></div>
<grey-label v-for="group in reporter.groups">{{group.name}}</grey-label>
</div>
<div v-if="reporter.shouldUpgrade">
<span class="red small" title="需要升级">v{{reporter.status.buildVersion}} -&gt; v{{reporter.newVersion}}</span>

View File

@@ -9,6 +9,8 @@
</div>
<div class="ui field">
<button class="ui button" type="submit">搜索</button>
&nbsp;
<a href="/dns" v-if="keyword.length > 0">[清除条件]</a>
</div>
</div>
</form>

View File

@@ -31,6 +31,10 @@
</div>
</td>
</tr>
<tr v-if="domainId == 0">
<td>线路</td>
<td><span class="disabled">当前集群没有选择域名。</span></td>
</tr>
</table>
<submit-btn></submit-btn>

View File

@@ -13,6 +13,8 @@
</div>
<div class="ui field">
<button class="ui button" type="submit">搜索</button>
&nbsp;
<a :href="Tea.url('.')" v-if="keyword.length > 0">[清除条件]</a>
</div>
</div>
</form>

View File

@@ -30,8 +30,17 @@
<input type="text" name="name" maxlength="50"/>
</td>
</tr>
<tr>
<td>在线状态</td>
<td>
<select class="ui dropdown auto-width" name="isUp">
<option value="1">在线</option>
<option value="0">离线</option>
</select>
</td>
</tr>
<tr v-if="teaIsPlus && supportThresholds">
<td>上线阈值</td>
<td>阈值设置</td>
<td>
<node-ip-address-thresholds-box></node-ip-address-thresholds-box>
</td>

View File

@@ -41,8 +41,17 @@
<input type="text" name="name" maxlength="50" v-model="address.name"/>
</td>
</tr>
<tr>
<td>在线状态</td>
<td>
<select class="ui dropdown auto-width" name="isUp" v-model="address.isUp">
<option value="1">在线</option>
<option value="0">离线</option>
</select>
</td>
</tr>
<tr v-if="teaIsPlus && supportThresholds">
<td>上线阈值</td>
<td>阈值设置</td>
<td>
<node-ip-address-thresholds-box :v-thresholds="address.thresholds"></node-ip-address-thresholds-box>
</td>

View File

@@ -2,4 +2,7 @@ Tea.context(function () {
this.success = NotifyPopup;
this.address = window.parent.UPDATING_NODE_IP_ADDRESS
});
if (this.address != null) {
this.address.isUp = (this.address.isUp ? 1 : 0)
}
})

View File

@@ -21,6 +21,8 @@
</div>
<div class="ui field">
<button class="ui button" type="submit">搜索</button>
&nbsp;
<a :href="Tea.url('.')" v-if="clusterId > 0 || userId > 0 || keyword.length > 0">[清除条件]</a>
</div>
</div>
</form>

View File

@@ -14,12 +14,15 @@
</second-menu>
<form class="ui form">
<input type="hidden" name="type" :value="type"/>
<div class="ui fields inline">
<div class="ui field">
<input type="text" name="keyword" placeholder="关键词" style="width:10em" v-model="keyword"/>
</div>
<div class="ui field">
<button type="submit" class="ui button">搜索</button>
&nbsp;
<a :href="Tea.url('.', { 'type':type })" v-if="keyword.length > 0">[清除条件]</a>
</div>
</div>
</form>
@@ -39,7 +42,7 @@
</tr>
</thead>
<tr v-for="(cert, index) in certs">
<td>{{cert.name}}
<td><keyword :v-word="keyword">{{cert.name}}</keyword>
<div v-if="cert.isCA" style="margin-top:0.5em">
<micro-basic-label :class="olive">CA</micro-basic-label>
</div>
@@ -52,7 +55,7 @@
</td>
<td>
<div v-for="dnsName in cert.dnsNames" style="margin-bottom:0.4em">
<span class="ui label tiny basic">{{dnsName}}</span>
<span class="ui label tiny basic"><keyword :v-word="keyword">{{dnsName}}</keyword></span>
</div>
</td>
<td>{{certInfos[index].beginDay}}</td>

View File

@@ -15,6 +15,8 @@
</div>
<div class="ui field">
<button type="submit" class="ui button">搜索</button>
&nbsp;
<a :href="Tea.url('.')" v-if="keyword.length > 0">[清除条件]</a>
</div>
</div>
</form>

View File

@@ -39,7 +39,7 @@
<tbody v-for="set in sets" :data-set-id="set.id">
<tr>
<td style="text-align: center;"><i class="icon bars handle grey"></i> </td>
<td nowrap=""><span :class="{disabled:!set.isOn}">{{set.name}}</span>
<td nowrap=""><a href="" @click.prevent="updateSet(set.id)"><span :class="{disabled:!set.isOn}">{{set.name}}</span></a>
<p style="margin-top:0.5em">
<label-on :v-is-on="set.isOn"></label-on>
</p>

View File

@@ -20,9 +20,9 @@
<tbody v-for="group in groups" :data-group-id="group.id">
<tr>
<td style="text-align: center;"><i class="icon bars handle grey" title="拖动排序"></i> </td>
<td><span :class="{disabled:!group.isOn}">{{group.name}}</span>
<td><a :href="'/servers/components/waf/group?firewallPolicyId=' + firewallPolicyId + '&type=' + type + '&groupId=' + group.id"><span :class="{disabled:!group.isOn}">{{group.name}}</span></a>
<p class="comment" v-if="group.description.length > 0" style="padding-bottom:0">{{group.description}}</p>
<p>
<p style="margin-top: 0.5em">
<span v-if="group.isOn" class="ui label tiny basic green">启用</span>
<span v-if="!group.isOn" class="ui label tiny basic red">停用</span>
<span v-if="group.code.length > 0" class="ui label basic tiny">预置</span>

View File

@@ -15,6 +15,8 @@
</div>
<div class="ui field">
<button type="submit" class="ui button">搜索</button>
&nbsp;
<a :href="Tea.url('.')" v-if="keyword.length > 0">[清除条件]</a>
</div>
</div>
</form>

View File

@@ -47,8 +47,10 @@
</div>
<div class="ui field">
<button type="submit" class="ui button">搜索</button>
&nbsp;
<a href="/servers" v-if="clusterId > 0 || groupId > 0 || keyword.length > 0">[清除条件]</a>
</div>
<div class="ui field" v-if="latestServers.length > 0">
<div class="ui field" v-if="clusterId == 0 && groupId == 0 && keyword.length == 0 && latestServers.length > 0">
<a href="" @click.prevent="showLatest()">常用<i class="icon angle" :class="{down: !latestVisible, up: latestVisible}"></i> </a>
</div>
</div>

View File

@@ -41,7 +41,7 @@
<tbody v-for="set in sets" :data-set-id="set.id">
<tr>
<td style="text-align: center;"><i class="icon bars handle grey"></i> </td>
<td nowrap=""><span :class="{disabled:!set.isOn}">{{set.name}}</span>
<td nowrap=""><a href="" @click.prevent="updateSet(set.id)"><span :class="{disabled:!set.isOn}">{{set.name}}</span></a>
<p style="margin-top:0.5em">
<label-on :v-is-on="set.isOn"></label-on>
</p>

View File

@@ -24,9 +24,9 @@
<tbody v-for="group in groups" :data-group-id="group.id">
<tr>
<td style="text-align: center;"><i class="icon bars handle grey" title="拖动排序"></i> </td>
<td><span :class="{disabled:!group.isOn}">{{group.name}}</span>
<td><a :href="'/servers/server/settings/waf/group?serverId=' + serverId + '&firewallPolicyId=' + firewallPolicyId + '&type=' + type + '&groupId=' + group.id"><span :class="{disabled:!group.isOn}">{{group.name}}</span></a>
<p class="comment" v-if="group.description.length > 0" style="padding-bottom:0">{{group.description}}</p>
<p>
<p style="margin-top: 0.5em">
<span v-if="group.isOn" class="ui label tiny basic green">启用</span>
<span v-if="!group.isOn" class="ui label tiny basic red">停用</span>
<span v-if="group.code.length > 0" class="ui label basic tiny">预置</span>