1.4.5.2
This commit is contained in:
30
EdgeAdmin/internal/web/actions/default/nodes/delete.go
Normal file
30
EdgeAdmin/internal/web/actions/default/nodes/delete.go
Normal file
@@ -0,0 +1,30 @@
|
||||
package nodes
|
||||
|
||||
import (
|
||||
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
|
||||
"github.com/TeaOSLab/EdgeCommon/pkg/langs/codes"
|
||||
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
|
||||
)
|
||||
|
||||
type DeleteAction struct {
|
||||
actionutils.ParentAction
|
||||
}
|
||||
|
||||
func (this *DeleteAction) RunPost(params struct {
|
||||
ClusterId int64
|
||||
NodeId int64
|
||||
}) {
|
||||
// 创建日志
|
||||
defer this.CreateLogInfo(codes.Node_LogDeleteNodeFromCluster, params.ClusterId, params.NodeId)
|
||||
|
||||
_, err := this.RPC().NodeRPC().DeleteNodeFromNodeCluster(this.AdminContext(), &pb.DeleteNodeFromNodeClusterRequest{
|
||||
NodeId: params.NodeId,
|
||||
NodeClusterId: params.ClusterId,
|
||||
})
|
||||
if err != nil {
|
||||
this.ErrorPage(err)
|
||||
return
|
||||
}
|
||||
|
||||
this.Success()
|
||||
}
|
||||
12
EdgeAdmin/internal/web/actions/default/nodes/helper.go
Normal file
12
EdgeAdmin/internal/web/actions/default/nodes/helper.go
Normal file
@@ -0,0 +1,12 @@
|
||||
package nodes
|
||||
|
||||
import (
|
||||
"github.com/iwind/TeaGo/actions"
|
||||
)
|
||||
|
||||
type Helper struct {
|
||||
}
|
||||
|
||||
func (this *Helper) BeforeAction(action *actions.ActionObject) {
|
||||
action.Data["teaMenu"] = "nodes"
|
||||
}
|
||||
23
EdgeAdmin/internal/web/actions/default/nodes/init.go
Normal file
23
EdgeAdmin/internal/web/actions/default/nodes/init.go
Normal file
@@ -0,0 +1,23 @@
|
||||
package nodes
|
||||
|
||||
import (
|
||||
"github.com/TeaOSLab/EdgeAdmin/internal/configloaders"
|
||||
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/default/nodes/ipAddresses"
|
||||
"github.com/TeaOSLab/EdgeAdmin/internal/web/helpers"
|
||||
"github.com/iwind/TeaGo"
|
||||
)
|
||||
|
||||
func init() {
|
||||
TeaGo.BeforeStart(func(server *TeaGo.Server) {
|
||||
server.
|
||||
Helper(helpers.NewUserMustAuth(configloaders.AdminModuleCodeNode)).
|
||||
Helper(new(Helper)).
|
||||
Prefix("/nodes").
|
||||
Post("/delete", new(DeleteAction)).
|
||||
|
||||
// IP地址
|
||||
GetPost("/ipAddresses/createPopup", new(ipAddresses.CreatePopupAction)).
|
||||
GetPost("/ipAddresses/updatePopup", new(ipAddresses.UpdatePopupAction)).
|
||||
EndAll()
|
||||
})
|
||||
}
|
||||
@@ -0,0 +1,91 @@
|
||||
package ipAddresses
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
teaconst "github.com/TeaOSLab/EdgeAdmin/internal/const"
|
||||
"github.com/TeaOSLab/EdgeAdmin/internal/utils"
|
||||
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
|
||||
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/default/nodes/ipAddresses/ipaddressutils"
|
||||
"github.com/TeaOSLab/EdgeCommon/pkg/nodeconfigs"
|
||||
"github.com/iwind/TeaGo/actions"
|
||||
"github.com/iwind/TeaGo/maps"
|
||||
"net"
|
||||
)
|
||||
|
||||
type CreatePopupAction struct {
|
||||
actionutils.ParentAction
|
||||
}
|
||||
|
||||
func (this *CreatePopupAction) Init() {
|
||||
this.Nav("", "", "")
|
||||
}
|
||||
|
||||
func (this *CreatePopupAction) RunGet(params struct {
|
||||
NodeId int64
|
||||
SupportThresholds bool
|
||||
}) {
|
||||
// 专属集群
|
||||
clusterMaps, err := ipaddressutils.FindNodeClusterMapsWithNodeId(this.Parent(), params.NodeId)
|
||||
if err != nil {
|
||||
this.ErrorPage(err)
|
||||
return
|
||||
}
|
||||
this.Data["clusters"] = clusterMaps
|
||||
|
||||
// 阈值
|
||||
this.Data["supportThresholds"] = params.SupportThresholds
|
||||
|
||||
this.Show()
|
||||
}
|
||||
|
||||
func (this *CreatePopupAction) RunPost(params struct {
|
||||
IP string `alias:"ip"`
|
||||
CanAccess bool
|
||||
Name string
|
||||
IsUp bool
|
||||
ThresholdsJSON []byte
|
||||
ClusterIds []int64
|
||||
|
||||
Must *actions.Must
|
||||
}) {
|
||||
params.Must.
|
||||
Field("ip", params.IP).
|
||||
Require("请输入IP地址")
|
||||
|
||||
result, err := utils.ExtractIP(params.IP)
|
||||
if err != nil {
|
||||
this.Fail("IP格式错误'" + params.IP + "'")
|
||||
}
|
||||
|
||||
for _, ip := range result {
|
||||
if len(net.ParseIP(ip)) == 0 {
|
||||
this.FailField("ip", "请输入正确的IP")
|
||||
}
|
||||
}
|
||||
|
||||
// 阈值设置
|
||||
var thresholds = []*nodeconfigs.IPAddressThresholdConfig{}
|
||||
if teaconst.IsPlus && len(params.ThresholdsJSON) > 0 {
|
||||
_ = json.Unmarshal(params.ThresholdsJSON, &thresholds)
|
||||
}
|
||||
|
||||
// 专属集群
|
||||
// 目前只考虑CDN边缘集群
|
||||
clusterMaps, err := ipaddressutils.FindNodeClusterMaps(this.Parent(), params.ClusterIds)
|
||||
if err != nil {
|
||||
this.ErrorPage(err)
|
||||
return
|
||||
}
|
||||
|
||||
this.Data["ipAddress"] = maps.Map{
|
||||
"name": params.Name,
|
||||
"canAccess": params.CanAccess,
|
||||
"ip": params.IP,
|
||||
"id": 0,
|
||||
"isOn": true,
|
||||
"isUp": params.IsUp,
|
||||
"thresholds": thresholds,
|
||||
"clusters": clusterMaps,
|
||||
}
|
||||
this.Success()
|
||||
}
|
||||
@@ -0,0 +1,209 @@
|
||||
package ipaddressutils
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"github.com/TeaOSLab/EdgeAdmin/internal/utils"
|
||||
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
|
||||
"github.com/TeaOSLab/EdgeCommon/pkg/nodeconfigs"
|
||||
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
|
||||
"github.com/iwind/TeaGo/maps"
|
||||
)
|
||||
|
||||
// UpdateNodeIPAddresses 保存一组IP地址
|
||||
func UpdateNodeIPAddresses(parentAction *actionutils.ParentAction, nodeId int64, role nodeconfigs.NodeRole, ipAddressesJSON []byte) error {
|
||||
var addresses = []maps.Map{}
|
||||
err := json.Unmarshal(ipAddressesJSON, &addresses)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
for _, addr := range addresses {
|
||||
var resultAddrIds = []int64{}
|
||||
var addrId = addr.GetInt64("id")
|
||||
|
||||
// 专属集群
|
||||
var addrClusterIds = []int64{}
|
||||
var addrClusters = addr.GetSlice("clusters")
|
||||
if len(addrClusters) > 0 {
|
||||
for _, addrCluster := range addrClusters {
|
||||
var m = maps.NewMap(addrCluster)
|
||||
var clusterId = m.GetInt64("id")
|
||||
if clusterId > 0 {
|
||||
addrClusterIds = append(addrClusterIds, clusterId)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if addrId > 0 {
|
||||
resultAddrIds = append(resultAddrIds, addrId)
|
||||
|
||||
var isOn bool
|
||||
if !addr.Has("isOn") { // 兼容老版本
|
||||
isOn = true
|
||||
} else {
|
||||
isOn = addr.GetBool("isOn")
|
||||
}
|
||||
|
||||
_, err = parentAction.RPC().NodeIPAddressRPC().UpdateNodeIPAddress(parentAction.AdminContext(), &pb.UpdateNodeIPAddressRequest{
|
||||
NodeIPAddressId: addrId,
|
||||
Ip: addr.GetString("ip"),
|
||||
Name: addr.GetString("name"),
|
||||
CanAccess: addr.GetBool("canAccess"),
|
||||
IsOn: isOn,
|
||||
IsUp: addr.GetBool("isUp"),
|
||||
ClusterIds: addrClusterIds,
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
} else {
|
||||
var ipStrings = addr.GetString("ip")
|
||||
result, _ := utils.ExtractIP(ipStrings)
|
||||
|
||||
if len(result) == 1 {
|
||||
// 单个创建
|
||||
createResp, err := parentAction.RPC().NodeIPAddressRPC().CreateNodeIPAddress(parentAction.AdminContext(), &pb.CreateNodeIPAddressRequest{
|
||||
NodeId: nodeId,
|
||||
Role: role,
|
||||
Name: addr.GetString("name"),
|
||||
Ip: result[0],
|
||||
CanAccess: addr.GetBool("canAccess"),
|
||||
IsUp: addr.GetBool("isUp"),
|
||||
NodeClusterIds: addrClusterIds,
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
addrId = createResp.NodeIPAddressId
|
||||
resultAddrIds = append(resultAddrIds, addrId)
|
||||
} else if len(result) > 1 {
|
||||
// 批量创建
|
||||
createResp, err := parentAction.RPC().NodeIPAddressRPC().CreateNodeIPAddresses(parentAction.AdminContext(), &pb.CreateNodeIPAddressesRequest{
|
||||
NodeId: nodeId,
|
||||
Role: role,
|
||||
Name: addr.GetString("name"),
|
||||
IpList: result,
|
||||
CanAccess: addr.GetBool("canAccess"),
|
||||
IsUp: addr.GetBool("isUp"),
|
||||
GroupValue: ipStrings,
|
||||
NodeClusterIds: addrClusterIds,
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
resultAddrIds = append(resultAddrIds, createResp.NodeIPAddressIds...)
|
||||
}
|
||||
}
|
||||
|
||||
// 保存阈值
|
||||
var thresholds = addr.GetSlice("thresholds")
|
||||
if len(thresholds) > 0 {
|
||||
thresholdsJSON, err := json.Marshal(thresholds)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
for _, addrId := range resultAddrIds {
|
||||
_, err = parentAction.RPC().NodeIPAddressThresholdRPC().UpdateAllNodeIPAddressThresholds(parentAction.AdminContext(), &pb.UpdateAllNodeIPAddressThresholdsRequest{
|
||||
NodeIPAddressId: addrId,
|
||||
NodeIPAddressThresholdsJSON: thresholdsJSON,
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
} else {
|
||||
for _, addrId := range resultAddrIds {
|
||||
_, err = parentAction.RPC().NodeIPAddressThresholdRPC().UpdateAllNodeIPAddressThresholds(parentAction.AdminContext(), &pb.UpdateAllNodeIPAddressThresholdsRequest{
|
||||
NodeIPAddressId: addrId,
|
||||
NodeIPAddressThresholdsJSON: []byte("[]"),
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// InitNodeIPAddressThresholds 初始化IP阈值
|
||||
func InitNodeIPAddressThresholds(parentAction *actionutils.ParentAction, addrId int64) ([]*nodeconfigs.IPAddressThresholdConfig, error) {
|
||||
thresholdsResp, err := parentAction.RPC().NodeIPAddressThresholdRPC().FindAllEnabledNodeIPAddressThresholds(parentAction.AdminContext(), &pb.FindAllEnabledNodeIPAddressThresholdsRequest{NodeIPAddressId: addrId})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
var thresholds = []*nodeconfigs.IPAddressThresholdConfig{}
|
||||
if len(thresholdsResp.NodeIPAddressThresholds) > 0 {
|
||||
for _, pbThreshold := range thresholdsResp.NodeIPAddressThresholds {
|
||||
var threshold = &nodeconfigs.IPAddressThresholdConfig{
|
||||
Id: pbThreshold.Id,
|
||||
Items: []*nodeconfigs.IPAddressThresholdItemConfig{},
|
||||
Actions: []*nodeconfigs.IPAddressThresholdActionConfig{},
|
||||
}
|
||||
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
|
||||
}
|
||||
|
||||
// FindNodeClusterMapsWithNodeId 根据节点读取集群信息
|
||||
func FindNodeClusterMapsWithNodeId(parentAction *actionutils.ParentAction, nodeId int64) ([]maps.Map, error) {
|
||||
var clusterMaps = []maps.Map{}
|
||||
if nodeId > 0 { // CDN边缘节点
|
||||
nodeResp, err := parentAction.RPC().NodeRPC().FindEnabledNode(parentAction.AdminContext(), &pb.FindEnabledNodeRequest{NodeId: nodeId})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
var node = nodeResp.Node
|
||||
if node != nil {
|
||||
var clusters = []*pb.NodeCluster{}
|
||||
if node.NodeCluster != nil {
|
||||
clusters = append(clusters, nodeResp.Node.NodeCluster)
|
||||
}
|
||||
if len(node.SecondaryNodeClusters) > 0 {
|
||||
clusters = append(clusters, node.SecondaryNodeClusters...)
|
||||
}
|
||||
for _, cluster := range clusters {
|
||||
clusterMaps = append(clusterMaps, maps.Map{
|
||||
"id": cluster.Id,
|
||||
"name": cluster.Name,
|
||||
"isChecked": false,
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
return clusterMaps, nil
|
||||
}
|
||||
|
||||
// FindNodeClusterMaps 根据一组集群ID读取集群信息
|
||||
func FindNodeClusterMaps(parentAction *actionutils.ParentAction, clusterIds []int64) ([]maps.Map, error) {
|
||||
var clusterMaps = []maps.Map{}
|
||||
if len(clusterIds) > 0 {
|
||||
for _, clusterId := range clusterIds {
|
||||
clusterResp, err := parentAction.RPC().NodeClusterRPC().FindEnabledNodeCluster(parentAction.AdminContext(), &pb.FindEnabledNodeClusterRequest{NodeClusterId: clusterId})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
var cluster = clusterResp.NodeCluster
|
||||
if cluster != nil {
|
||||
clusterMaps = append(clusterMaps, maps.Map{
|
||||
"id": cluster.Id,
|
||||
"name": cluster.Name,
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
return clusterMaps, nil
|
||||
}
|
||||
@@ -0,0 +1,115 @@
|
||||
package ipAddresses
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
teaconst "github.com/TeaOSLab/EdgeAdmin/internal/const"
|
||||
"github.com/TeaOSLab/EdgeAdmin/internal/utils"
|
||||
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
|
||||
"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/actions"
|
||||
"github.com/iwind/TeaGo/maps"
|
||||
"net"
|
||||
)
|
||||
|
||||
type UpdatePopupAction struct {
|
||||
actionutils.ParentAction
|
||||
}
|
||||
|
||||
func (this *UpdatePopupAction) Init() {
|
||||
this.Nav("", "", "")
|
||||
}
|
||||
|
||||
func (this *UpdatePopupAction) RunGet(params struct {
|
||||
NodeId int64
|
||||
AddressId int64
|
||||
SupportThresholds bool
|
||||
}) {
|
||||
// 专属集群
|
||||
clusterMaps, err := ipaddressutils.FindNodeClusterMapsWithNodeId(this.Parent(), params.NodeId)
|
||||
if err != nil {
|
||||
this.ErrorPage(err)
|
||||
return
|
||||
}
|
||||
this.Data["clusters"] = clusterMaps
|
||||
|
||||
this.Data["supportThresholds"] = params.SupportThresholds
|
||||
|
||||
this.Show()
|
||||
}
|
||||
|
||||
func (this *UpdatePopupAction) RunPost(params struct {
|
||||
AddressId int64
|
||||
IP string `alias:"ip"`
|
||||
Name string
|
||||
CanAccess bool
|
||||
IsOn bool
|
||||
IsUp bool
|
||||
ThresholdsJSON []byte
|
||||
ClusterIds []int64
|
||||
|
||||
Must *actions.Must
|
||||
}) {
|
||||
params.Must.
|
||||
Field("ip", params.IP).
|
||||
Require("请输入IP地址")
|
||||
|
||||
// 获取IP地址信息
|
||||
var isUp = params.IsUp
|
||||
if params.AddressId > 0 {
|
||||
addressResp, err := this.RPC().NodeIPAddressRPC().FindEnabledNodeIPAddress(this.AdminContext(), &pb.FindEnabledNodeIPAddressRequest{NodeIPAddressId: params.AddressId})
|
||||
if err != nil {
|
||||
this.ErrorPage(err)
|
||||
return
|
||||
}
|
||||
var address = addressResp.NodeIPAddress
|
||||
if address == nil {
|
||||
this.Fail("找不到要修改的地址")
|
||||
}
|
||||
}
|
||||
|
||||
if params.AddressId > 0 {
|
||||
ip := net.ParseIP(params.IP)
|
||||
if len(ip) == 0 {
|
||||
this.Fail("请输入正确的IP")
|
||||
}
|
||||
} else {
|
||||
result, err := utils.ExtractIP(params.IP)
|
||||
if err != nil {
|
||||
this.Fail("IP格式错误'" + params.IP + "'")
|
||||
}
|
||||
|
||||
for _, ip := range result {
|
||||
if len(net.ParseIP(ip)) == 0 {
|
||||
this.FailField("ip", "请输入正确的IP")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var thresholds = []*nodeconfigs.IPAddressThresholdConfig{}
|
||||
if teaconst.IsPlus && len(params.ThresholdsJSON) > 0 {
|
||||
_ = json.Unmarshal(params.ThresholdsJSON, &thresholds)
|
||||
}
|
||||
|
||||
// 专属集群
|
||||
// 目前只考虑CDN边缘集群
|
||||
clusterMaps, err := ipaddressutils.FindNodeClusterMaps(this.Parent(), params.ClusterIds)
|
||||
if err != nil {
|
||||
this.ErrorPage(err)
|
||||
return
|
||||
}
|
||||
|
||||
this.Data["ipAddress"] = maps.Map{
|
||||
"name": params.Name,
|
||||
"ip": params.IP,
|
||||
"id": params.AddressId,
|
||||
"canAccess": params.CanAccess,
|
||||
"isOn": params.IsOn,
|
||||
"isUp": isUp,
|
||||
"thresholds": thresholds,
|
||||
"clusters": clusterMaps,
|
||||
}
|
||||
|
||||
this.Success()
|
||||
}
|
||||
371
EdgeAdmin/internal/web/actions/default/nodes/nodeutils/utils.go
Normal file
371
EdgeAdmin/internal/web/actions/default/nodes/nodeutils/utils.go
Normal file
@@ -0,0 +1,371 @@
|
||||
package nodeutils
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"github.com/TeaOSLab/EdgeAdmin/internal/configs"
|
||||
"github.com/TeaOSLab/EdgeAdmin/internal/rpc"
|
||||
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
|
||||
"sort"
|
||||
"strconv"
|
||||
"sync"
|
||||
)
|
||||
|
||||
// MessageResult 和节点消息通讯结果定义
|
||||
type MessageResult struct {
|
||||
NodeId int64 `json:"nodeId"`
|
||||
NodeName string `json:"nodeName"`
|
||||
IsOK bool `json:"isOk"`
|
||||
Message string `json:"message"`
|
||||
}
|
||||
|
||||
// SendMessageToCluster 向集群发送命令消息
|
||||
func SendMessageToCluster(ctx context.Context, clusterId int64, code string, msg any, timeoutSeconds int32, availableNodesOnly bool) (results []*MessageResult, err error) {
|
||||
results = []*MessageResult{}
|
||||
|
||||
msgJSON, err := json.Marshal(msg)
|
||||
if err != nil {
|
||||
return results, err
|
||||
}
|
||||
|
||||
defaultRPCClient, err := rpc.SharedRPC()
|
||||
if err != nil {
|
||||
return results, err
|
||||
}
|
||||
|
||||
// 获取所有节点
|
||||
nodesResp, err := defaultRPCClient.NodeRPC().FindAllEnabledNodesWithNodeClusterId(ctx, &pb.FindAllEnabledNodesWithNodeClusterIdRequest{
|
||||
NodeClusterId: clusterId,
|
||||
IncludeSecondary: true,
|
||||
})
|
||||
if err != nil {
|
||||
return results, err
|
||||
}
|
||||
var nodes = nodesResp.Nodes
|
||||
if len(nodes) == 0 {
|
||||
return results, nil
|
||||
}
|
||||
|
||||
if availableNodesOnly {
|
||||
var newNodes []*pb.Node
|
||||
for _, node := range nodes {
|
||||
if !node.IsOn {
|
||||
continue
|
||||
}
|
||||
newNodes = append(newNodes, node)
|
||||
}
|
||||
nodes = newNodes
|
||||
}
|
||||
|
||||
var rpcMap = map[int64]*rpc.RPCClient{} // apiNodeId => RPCClient
|
||||
var locker = &sync.Mutex{}
|
||||
|
||||
var wg = &sync.WaitGroup{}
|
||||
wg.Add(len(nodes))
|
||||
|
||||
for _, node := range nodes {
|
||||
// TODO 检查是否在线
|
||||
|
||||
if !node.IsOn {
|
||||
locker.Lock()
|
||||
results = append(results, &MessageResult{
|
||||
NodeId: node.Id,
|
||||
NodeName: node.Name,
|
||||
IsOK: false,
|
||||
Message: "节点尚未启用",
|
||||
})
|
||||
locker.Unlock()
|
||||
wg.Done()
|
||||
continue
|
||||
}
|
||||
|
||||
if len(node.ConnectedAPINodeIds) == 0 {
|
||||
locker.Lock()
|
||||
results = append(results, &MessageResult{
|
||||
NodeId: node.Id,
|
||||
NodeName: node.Name,
|
||||
IsOK: false,
|
||||
Message: "节点尚未连接到API",
|
||||
})
|
||||
locker.Unlock()
|
||||
wg.Done()
|
||||
continue
|
||||
}
|
||||
|
||||
// 获取API节点信息
|
||||
apiNodeId := node.ConnectedAPINodeIds[0]
|
||||
rpcClient, ok := rpcMap[apiNodeId]
|
||||
if !ok {
|
||||
apiNodeResp, err := defaultRPCClient.APINodeRPC().FindEnabledAPINode(ctx, &pb.FindEnabledAPINodeRequest{ApiNodeId: apiNodeId})
|
||||
if err != nil {
|
||||
locker.Lock()
|
||||
results = append(results, &MessageResult{
|
||||
NodeId: node.Id,
|
||||
NodeName: node.Name,
|
||||
IsOK: false,
|
||||
Message: "无法读取对应的API节点信息:" + err.Error(),
|
||||
})
|
||||
locker.Unlock()
|
||||
wg.Done()
|
||||
continue
|
||||
}
|
||||
|
||||
if apiNodeResp.ApiNode == nil {
|
||||
locker.Lock()
|
||||
results = append(results, &MessageResult{
|
||||
NodeId: node.Id,
|
||||
NodeName: node.Name,
|
||||
IsOK: false,
|
||||
Message: "无法读取对应的API节点信息:API节点ID:" + strconv.FormatInt(apiNodeId, 10),
|
||||
})
|
||||
locker.Unlock()
|
||||
wg.Done()
|
||||
continue
|
||||
}
|
||||
apiNode := apiNodeResp.ApiNode
|
||||
|
||||
apiRPCClient, err := rpc.NewRPCClient(&configs.APIConfig{
|
||||
RPCEndpoints: apiNode.AccessAddrs,
|
||||
NodeId: apiNode.UniqueId,
|
||||
Secret: apiNode.Secret,
|
||||
}, false)
|
||||
if err != nil {
|
||||
locker.Lock()
|
||||
results = append(results, &MessageResult{
|
||||
NodeId: node.Id,
|
||||
NodeName: node.Name,
|
||||
IsOK: false,
|
||||
Message: "初始化API节点错误:API节点ID:" + strconv.FormatInt(apiNodeId, 10) + ":" + err.Error(),
|
||||
})
|
||||
locker.Unlock()
|
||||
wg.Done()
|
||||
continue
|
||||
}
|
||||
rpcMap[apiNodeId] = apiRPCClient
|
||||
rpcClient = apiRPCClient
|
||||
}
|
||||
|
||||
// 发送消息
|
||||
go func(node *pb.Node) {
|
||||
defer wg.Done()
|
||||
|
||||
result, err := rpcClient.NodeRPC().SendCommandToNode(ctx, &pb.NodeStreamMessage{
|
||||
NodeId: node.Id,
|
||||
TimeoutSeconds: timeoutSeconds,
|
||||
Code: code,
|
||||
DataJSON: msgJSON,
|
||||
})
|
||||
if err != nil {
|
||||
locker.Lock()
|
||||
results = append(results, &MessageResult{
|
||||
NodeId: node.Id,
|
||||
NodeName: node.Name,
|
||||
IsOK: false,
|
||||
Message: "API返回错误:" + err.Error(),
|
||||
})
|
||||
locker.Unlock()
|
||||
return
|
||||
}
|
||||
|
||||
locker.Lock()
|
||||
results = append(results, &MessageResult{
|
||||
NodeId: node.Id,
|
||||
NodeName: node.Name,
|
||||
IsOK: result.IsOk,
|
||||
Message: result.Message,
|
||||
})
|
||||
locker.Unlock()
|
||||
}(node)
|
||||
}
|
||||
wg.Wait()
|
||||
|
||||
// 对结果进行排序
|
||||
if len(results) > 0 {
|
||||
sort.Slice(results, func(i, j int) bool {
|
||||
return results[i].NodeId < results[j].NodeId
|
||||
})
|
||||
}
|
||||
|
||||
// 关闭RPC
|
||||
for _, rpcClient := range rpcMap {
|
||||
_ = rpcClient.Close()
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// SendMessageToNodeIds 向一组节点发送命令消息
|
||||
func SendMessageToNodeIds(ctx context.Context, nodeIds []int64, code string, msg interface{}, timeoutSeconds int32) (results []*MessageResult, err error) {
|
||||
results = []*MessageResult{}
|
||||
if len(nodeIds) == 0 {
|
||||
return
|
||||
}
|
||||
|
||||
msgJSON, err := json.Marshal(msg)
|
||||
if err != nil {
|
||||
return results, err
|
||||
}
|
||||
|
||||
defaultRPCClient, err := rpc.SharedRPC()
|
||||
if err != nil {
|
||||
return results, err
|
||||
}
|
||||
|
||||
// 获取所有节点
|
||||
nodesResp, err := defaultRPCClient.NodeRPC().FindEnabledNodesWithIds(ctx, &pb.FindEnabledNodesWithIdsRequest{NodeIds: nodeIds})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
nodes := nodesResp.Nodes
|
||||
if len(nodes) == 0 {
|
||||
return results, nil
|
||||
}
|
||||
|
||||
rpcMap := map[int64]*rpc.RPCClient{} // apiNodeId => RPCClient
|
||||
locker := &sync.Mutex{}
|
||||
|
||||
wg := &sync.WaitGroup{}
|
||||
wg.Add(len(nodes))
|
||||
for _, node := range nodes {
|
||||
if !node.IsActive {
|
||||
locker.Lock()
|
||||
results = append(results, &MessageResult{
|
||||
NodeId: node.Id,
|
||||
NodeName: node.Name,
|
||||
IsOK: false,
|
||||
Message: "节点不在线",
|
||||
})
|
||||
locker.Unlock()
|
||||
wg.Done()
|
||||
continue
|
||||
}
|
||||
|
||||
if !node.IsOn {
|
||||
if !node.IsActive {
|
||||
locker.Lock()
|
||||
results = append(results, &MessageResult{
|
||||
NodeId: node.Id,
|
||||
NodeName: node.Name,
|
||||
IsOK: false,
|
||||
Message: "节点未启用",
|
||||
})
|
||||
locker.Unlock()
|
||||
wg.Done()
|
||||
continue
|
||||
}
|
||||
}
|
||||
|
||||
if len(node.ConnectedAPINodeIds) == 0 {
|
||||
locker.Lock()
|
||||
results = append(results, &MessageResult{
|
||||
NodeId: node.Id,
|
||||
NodeName: node.Name,
|
||||
IsOK: false,
|
||||
Message: "节点尚未连接到API",
|
||||
})
|
||||
locker.Unlock()
|
||||
wg.Done()
|
||||
continue
|
||||
}
|
||||
|
||||
// 获取API节点信息
|
||||
apiNodeId := node.ConnectedAPINodeIds[0]
|
||||
rpcClient, ok := rpcMap[apiNodeId]
|
||||
if !ok {
|
||||
apiNodeResp, err := defaultRPCClient.APINodeRPC().FindEnabledAPINode(ctx, &pb.FindEnabledAPINodeRequest{ApiNodeId: apiNodeId})
|
||||
if err != nil {
|
||||
locker.Lock()
|
||||
results = append(results, &MessageResult{
|
||||
NodeId: node.Id,
|
||||
NodeName: node.Name,
|
||||
IsOK: false,
|
||||
Message: "无法读取对应的API节点信息:" + err.Error(),
|
||||
})
|
||||
locker.Unlock()
|
||||
wg.Done()
|
||||
continue
|
||||
}
|
||||
|
||||
if apiNodeResp.ApiNode == nil {
|
||||
locker.Lock()
|
||||
results = append(results, &MessageResult{
|
||||
NodeId: node.Id,
|
||||
NodeName: node.Name,
|
||||
IsOK: false,
|
||||
Message: "无法读取对应的API节点信息:API节点ID:" + strconv.FormatInt(apiNodeId, 10),
|
||||
})
|
||||
locker.Unlock()
|
||||
wg.Done()
|
||||
continue
|
||||
}
|
||||
apiNode := apiNodeResp.ApiNode
|
||||
|
||||
apiRPCClient, err := rpc.NewRPCClient(&configs.APIConfig{
|
||||
RPCEndpoints: apiNode.AccessAddrs,
|
||||
NodeId: apiNode.UniqueId,
|
||||
Secret: apiNode.Secret,
|
||||
}, false)
|
||||
if err != nil {
|
||||
locker.Lock()
|
||||
results = append(results, &MessageResult{
|
||||
NodeId: node.Id,
|
||||
NodeName: node.Name,
|
||||
IsOK: false,
|
||||
Message: "初始化API节点错误:API节点ID:" + strconv.FormatInt(apiNodeId, 10) + ":" + err.Error(),
|
||||
})
|
||||
locker.Unlock()
|
||||
wg.Done()
|
||||
continue
|
||||
}
|
||||
rpcMap[apiNodeId] = apiRPCClient
|
||||
rpcClient = apiRPCClient
|
||||
}
|
||||
|
||||
// 发送消息
|
||||
go func(node *pb.Node) {
|
||||
defer wg.Done()
|
||||
|
||||
result, err := rpcClient.NodeRPC().SendCommandToNode(ctx, &pb.NodeStreamMessage{
|
||||
NodeId: node.Id,
|
||||
TimeoutSeconds: timeoutSeconds,
|
||||
Code: code,
|
||||
DataJSON: msgJSON,
|
||||
})
|
||||
if err != nil {
|
||||
locker.Lock()
|
||||
results = append(results, &MessageResult{
|
||||
NodeId: node.Id,
|
||||
NodeName: node.Name,
|
||||
IsOK: false,
|
||||
Message: "API返回错误:" + err.Error(),
|
||||
})
|
||||
locker.Unlock()
|
||||
return
|
||||
}
|
||||
|
||||
locker.Lock()
|
||||
results = append(results, &MessageResult{
|
||||
NodeId: node.Id,
|
||||
NodeName: node.Name,
|
||||
IsOK: result.IsOk,
|
||||
Message: result.Message,
|
||||
})
|
||||
locker.Unlock()
|
||||
}(node)
|
||||
}
|
||||
wg.Wait()
|
||||
|
||||
// 对结果进行排序
|
||||
if len(results) > 0 {
|
||||
sort.Slice(results, func(i, j int) bool {
|
||||
return results[i].NodeId < results[j].NodeId
|
||||
})
|
||||
}
|
||||
|
||||
// 关闭RPC
|
||||
for _, rpcClient := range rpcMap {
|
||||
_ = rpcClient.Close()
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
package nodeutils
|
||||
|
||||
import (
|
||||
"github.com/TeaOSLab/EdgeAdmin/internal/rpc"
|
||||
_ "github.com/iwind/TeaGo/bootstrap"
|
||||
"github.com/iwind/TeaGo/logs"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestSendMessageToCluster(t *testing.T) {
|
||||
rpcClient, err := rpc.SharedRPC()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
ctx := rpcClient.Context(1)
|
||||
|
||||
results, err := SendMessageToCluster(ctx, 1, "test", nil, 30, false)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
logs.PrintAsJSON(results, t)
|
||||
}
|
||||
Reference in New Issue
Block a user