// Copyright 2022 Liuxiangchao iwind.liu@gmail.com. All rights reserved. //go:build plus package models import ( "encoding/json" "errors" teaconst "github.com/TeaOSLab/EdgeAPI/internal/const" "github.com/TeaOSLab/EdgeAPI/internal/db/models/dns" "github.com/TeaOSLab/EdgeAPI/internal/utils" "github.com/TeaOSLab/EdgeAPI/internal/utils/regexputils" "github.com/TeaOSLab/EdgeAPI/internal/zero" "github.com/TeaOSLab/EdgeCommon/pkg/nodeconfigs" "github.com/TeaOSLab/EdgeCommon/pkg/serverconfigs" "github.com/TeaOSLab/EdgeCommon/pkg/serverconfigs/shared" "github.com/iwind/TeaGo/dbs" "github.com/iwind/TeaGo/lists" "github.com/iwind/TeaGo/maps" "github.com/iwind/TeaGo/types" timeutil "github.com/iwind/TeaGo/utils/time" "net/http" "strings" "sync" "time" ) // 从集群加载其他服务 func (this *NodeDAO) loadServersFromCluster(tx *dbs.Tx, clusterId int64, serverIdMap map[int64]zero.Zero) ([]*Server, error) { // 高防实例 // TODO 将来支持部署到具体节点 objectCodes, err := SharedADPackageInstanceDAO.FindAvailableObjectCodesInCluster(tx, clusterId) if err != nil { return nil, err } var resultServers = []*Server{} for _, objectCode := range objectCodes { objectType, objectValue := SharedUserADInstanceDAO.SplitObjectCode(objectCode) if objectType == ADObjectTypeServer && len(objectValue) > 0 { var serverId = types.Int64(objectValue) if serverId > 0 { _, ok := serverIdMap[serverId] if !ok { serverIdMap[serverId] = zero.Zero{} // TODO 将来支持一次性读取一批server server, err := SharedServerDAO.FindEnabledServer(tx, serverId) if err != nil { return nil, err } if server != nil && server.IsOn { resultServers = append(resultServers, server) } } } } } return resultServers, nil } // 组合扩展配置 func (this *NodeDAO) composeExtConfig(tx *dbs.Tx, config *nodeconfigs.NodeConfig, clusterIds []int64, cacheMap *utils.CacheMap) error { // 脚本 // scripts scriptConfigs, err := SharedScriptHistoryDAO.ComposeScriptConfigs(tx, 0, cacheMap) if err != nil { return err } config.CommonScripts = scriptConfigs // 套餐 // plan plans, err := SharedPlanDAO.FindAllAvailableBasicPlans(tx) if err != nil { return err } var planMap = map[int64]*serverconfigs.PlanConfig{} for _, plan := range plans { planMap[int64(plan.Id)] = &serverconfigs.PlanConfig{ Id: int64(plan.Id), Name: plan.Name, BandwidthLimitPerNode: plan.DecodeBandwidthLimitPerNode(), TrafficLimit: plan.DecodeTrafficLimit(), MaxUploadSize: plan.DecodeMaxUploadSize(), } } config.Plans = planMap // 父节点 // 这里不需要进行 teaconst.IsPlus 判断,是为了防止在API节点升级过程中或者用户授权过期又激活时,节点无法正常更新 if config.Level == 1 { parentNodes, err := SharedNodeDAO.FindParentNodeConfigs(tx, config.Id, config.GroupId, clusterIds, types.Int(config.Level)) if err != nil { return err } config.ParentNodes = parentNodes } return nil } // UpdateNodeSchedule 修改智能调度信息 // offlineDay YYYYMMDD func (this *NodeDAO) UpdateNodeSchedule(tx *dbs.Tx, nodeId int64, offlineDay string, isBackupForCluster bool, isBackupForGroup bool, backupIPs []string) error { if nodeId <= 0 { return errors.New("invalid nodeId") } var op = NewNodeOperator() op.Id = nodeId if len(offlineDay) > 0 && !regexputils.YYYYMMDD.MatchString(offlineDay) { return errors.New("invalid 'offlineDay' value: " + offlineDay) } op.OfflineDay = offlineDay if offlineDay >= timeutil.Format("Ymd") { op.OfflineIsNotified = false } op.IsBackupForCluster = isBackupForCluster op.IsBackupForGroup = isBackupForGroup if backupIPs == nil { backupIPs = []string{} } backupIPsJSON, err := json.Marshal(backupIPs) if err != nil { return err } op.BackupIPs = backupIPsJSON err = this.Save(tx, op) if err != nil { return err } return this.NotifyDNSUpdate(tx, nodeId) } // FindAllNodeScheduleWithClusterId 查找集群下的节点 func (this *NodeDAO) FindAllNodeScheduleWithClusterId(tx *dbs.Tx, clusterId int64) (result []*Node, err error) { if clusterId <= 0 { return } _, err = this.Query(tx). Result("id", "name", "groupId", "offlineDay", "backupIPs", "isBackupForCluster", "isBackupForGroup", "actionStatus"). Attr("clusterId", clusterId). State(NodeStateEnabled). Slice(&result). FindAll() return } // FindNodeSchedule 查找智能调度信息 func (this *NodeDAO) FindNodeSchedule(tx *dbs.Tx, nodeId int64) (node *Node, err error) { if nodeId <= 0 { err = errors.New("invalid nodeId") return } one, err := this.Query(tx). Result("id", "offlineDay", "isBackupForCluster", "isBackupForGroup", "backupIPs", "actionStatus"). Pk(nodeId). Find() if err != nil || one == nil { return nil, err } return one.(*Node), nil } // NotifyNodesWithOfflineDay 通知租期到期的节点 var notifiedOfflineDayMap = map[string]bool{} // DAY_{END|00} var notifiedOfflineDayLocker = &sync.Mutex{} func (this *NodeDAO) NotifyNodesWithOfflineDay(tx *dbs.Tx) error { // 每天最多两次 var cacheKey = timeutil.Format("Ymd") var endHour = 23 if time.Now().Hour() == endHour { cacheKey += "_END" } else { cacheKey += "_00" } notifiedOfflineDayLocker.Lock() if notifiedOfflineDayMap[cacheKey] { notifiedOfflineDayLocker.Unlock() return nil } notifiedOfflineDayLocker.Unlock() var query = this.Query(tx). State(NodeStateEnabled). Result("id", "clusterId", "offlineDay"). Attr("offlineIsNotified", false) if time.Now().Hour() == endHour { // 提前将今天到期的也下线 query. Where("(LENGTH(offlineDay) > 0 AND offlineDay<=:offlineDay)"). Param("offlineDay", timeutil.Format("Ymd")) } else { // 将以往的下线 query. Where("(LENGTH(offlineDay) > 0 AND offlineDay<:offlineDay)"). Param("offlineDay", timeutil.Format("Ymd")) } nodeOnes, err := query. FindAll() if err != nil { return err } var clusterIdMap = map[int64]bool{} // clusterId => bool for _, nodeOne := range nodeOnes { var node = nodeOne.(*Node) var offlineDay = node.OfflineDay if len(offlineDay) != 8 { continue } var realOfflineDay = offlineDay[:4] + "-" + offlineDay[4:6] + "-" + offlineDay[6:] var nodeId = int64(node.Id) var clusterId = int64(node.ClusterId) if clusterId > 0 && !clusterIdMap[clusterId] { err = dns.SharedDNSTaskDAO.CreateClusterTask(tx, clusterId, dns.DNSTaskTypeClusterNodesChange) if err != nil { return err } clusterIdMap[clusterId] = true } // 设置为已通知 err = this.Query(tx). Pk(nodeId). Set("offlineIsNotified", true). UpdateQuickly() if err != nil { return err } // 发送消息 err = SharedMessageDAO.CreateNodeMessage(tx, nodeconfigs.NodeRoleNode, clusterId, nodeId, MessageTypeNodeOfflineDay, MessageLevelError, "节点租期已结束("+realOfflineDay+")", "节点租期已结束("+realOfflineDay+")", nil, true) if err != nil { return err } } notifiedOfflineDayLocker.Lock() notifiedOfflineDayMap[cacheKey] = true notifiedOfflineDayLocker.Unlock() return nil } // ResetNodeActionStatus 重置节点动作状态 // checkConds 是否检查条件 func (this *NodeDAO) ResetNodeActionStatus(tx *dbs.Tx, nodeId int64, checkConds bool) error { if nodeId <= 0 { return errors.New("invalid 'nodeId'") } nodeOne, err := this.Query(tx). Pk(nodeId). Result("clusterId", "actionStatus"). Find() if err != nil { return err } if nodeOne == nil { return nil } var node = nodeOne.(*Node) var clusterId = int64(node.ClusterId) if clusterId <= 0 { return nil } if len(node.ActionStatus) == 0 { return nil } if checkConds { var actionStatus = &nodeconfigs.NodeActionStatus{} err = json.Unmarshal(node.ActionStatus, actionStatus) if err != nil { return err } // 处理健康检查的特殊情况 if actionStatus.ActionId > 0 { upAddressId, err := SharedNodeIPAddressDAO.FindFirstNodeAccessIPAddressId(tx, nodeId, true, nodeconfigs.NodeRoleNode) if err != nil { return err } if upAddressId <= 0 { if actionStatus.DurationSeconds <= 0 || actionStatus.DurationSeconds > 3600 { actionStatus.DurationSeconds = 3600 } actionStatus.ExpiresAt += actionStatus.DurationSeconds actionStatusJSON, err := json.Marshal(actionStatus) if err != nil { return err } err = this.Query(tx). Pk(nodeId). Set("actionStatus", actionStatusJSON). UpdateQuickly() if err != nil { return err } // 发送通知 return SharedMessageDAO.CreateNodeMessage(tx, nodeconfigs.NodeRoleNode, clusterId, nodeId, MessageTypeNodeSchedule, MessageLevelError, "节点未能从智能调度状态中恢复", "节点由于没有可以正常访问的IP,所以未能从智能调度状态中恢复,将在下次循环中再次尝试。", nil, true) } } } err = this.Query(tx). Pk(nodeId). Set("actionStatus", dbs.SQL("NULL")). UpdateQuickly() if err != nil { return err } err = dns.SharedDNSTaskDAO.CreateClusterTask(tx, clusterId, dns.DNSTaskTypeClusterNodesChange) if err != nil { return err } // 发送通知 return SharedMessageDAO.CreateNodeMessage(tx, nodeconfigs.NodeRoleNode, clusterId, nodeId, MessageTypeNodeSchedule, MessageLevelSuccess, "节点已从智能调度状态中恢复", "节点已从智能调度状态中恢复。", nil, true) } // FindNodeIdsWithExpiredActions 查找动作状态已过期的节点ID func (this *NodeDAO) FindNodeIdsWithExpiredActions(tx *dbs.Tx) (nodeIds []int64, err error) { ones, err := this.Query(tx). Result("id"). State(NodeStateEnabled). Where("(actionStatus IS NOT NULL AND JSON_EXTRACT(actionStatus, '$.expiresAt')<:currentTime)"). Param("currentTime", time.Now().Unix()). FindAll() if err != nil { return nil, err } for _, one := range ones { nodeIds = append(nodeIds, int64(one.(*Node).Id)) } return } // CopyNodeActionsToGroup 复制动作设置到分组 func (this *NodeDAO) CopyNodeActionsToGroup(tx *dbs.Tx, nodeId int64) error { groupId, err := this.Query(tx). Pk(nodeId). Result("groupId"). FindInt64Col(0) if err != nil { return err } if groupId <= 0 { return nil } const nodeRole = nodeconfigs.NodeRoleNode actions, err := SharedNodeActionDAO.FindAllNodeActions(tx, nodeRole, nodeId) if err != nil { return err } nodeOnes, err := this.Query(tx). ResultPk(). Attr("groupId", groupId). Neq("id", nodeId). State(NodeStateEnabled). FindAll() if err != nil { return err } for _, nodeOne := range nodeOnes { var targetNodeId = int64(nodeOne.(*Node).Id) err = SharedNodeActionDAO.DisableAllNodeActions(tx, nodeRole, targetNodeId) if err != nil { return err } for _, action := range actions { err = SharedNodeActionDAO.DuplicateNodeAction(tx, action, nodeRole, targetNodeId) if err != nil { return err } } } return nil } // CopyNodeActionsToCluster 复制动作设置到集群 func (this *NodeDAO) CopyNodeActionsToCluster(tx *dbs.Tx, nodeId int64) error { clusterId, err := this.Query(tx). Pk(nodeId). Result("clusterId"). FindInt64Col(0) if err != nil { return err } if clusterId <= 0 { return nil } const nodeRole = nodeconfigs.NodeRoleNode actions, err := SharedNodeActionDAO.FindAllNodeActions(tx, nodeRole, nodeId) if err != nil { return err } nodeOnes, err := this.Query(tx). ResultPk(). Attr("clusterId", clusterId). Neq("id", nodeId). State(NodeStateEnabled). FindAll() if err != nil { return err } for _, nodeOne := range nodeOnes { var targetNodeId = int64(nodeOne.(*Node).Id) err = SharedNodeActionDAO.DisableAllNodeActions(tx, nodeRole, targetNodeId) if err != nil { return err } for _, action := range actions { err = SharedNodeActionDAO.DuplicateNodeAction(tx, action, nodeRole, targetNodeId) if err != nil { return err } } } return nil } // FireNodeActions 触发动作 func (this *NodeDAO) FireNodeActions(tx *dbs.Tx, nodeId int64, paramCode nodeconfigs.NodeActionParam, paramValue any) error { // 只有Plus会员才会触发 if !teaconst.IsPlus { return nil } nodeOne, err := this.Query(tx). Pk(nodeId). Result("isBackupForCluster", "isBackupForGroup", "actionStatus", "offlineDay"). State(NodeStateEnabled). Find() if err != nil { return err } if nodeOne == nil { return nil } var node = nodeOne.(*Node) // 备用节点不处理 if node.IsBackupForCluster || node.IsBackupForGroup { return nil } // 是否已下线 if node.CheckIsOffline() { return nil } // 当前节点是否已经在动作状态中 var statusJSON = node.ActionStatus if len(statusJSON) == 0 { return this.fireNodeActions(tx, nodeId, paramCode, paramValue) } var status = &nodeconfigs.NodeActionStatus{} err = json.Unmarshal(statusJSON, status) if err != nil { return err } if status.ActionId <= 0 { return this.fireNodeActions(tx, nodeId, paramCode, paramValue) } // 如果现在已经处于切换状态,则不做任何处理(使用任务来处理) return nil } // 触发某个参数相关的动作 func (this *NodeDAO) fireNodeActions(tx *dbs.Tx, nodeId int64, paramCode nodeconfigs.NodeActionParam, paramValue any) error { actions, err := SharedNodeActionDAO.FindAllAvailableNodeActionsWithParam(tx, nodeconfigs.NodeRoleNode, nodeId, paramCode) if err != nil { return err } if len(actions) == 0 { return nil } for _, action := range actions { var condsConfig = action.DecodeCondsConfig() b, err := condsConfig.Match(func(param nodeconfigs.NodeActionParam) (value any, err error) { if paramCode == param && paramValue != nil { return paramValue, nil } switch param { case nodeconfigs.NodeActionParamDailyTrafficOut: var today = timeutil.Format("Ymd") stat, err := SharedNodeTrafficDailyStatDAO.SumDailyStat(tx, nodeconfigs.NodeRoleNode, nodeId, today, today) if err != nil { return nil, err } if stat != nil { value = stat.Bytes } else { value = 0 } case nodeconfigs.NodeActionParamMonthlyTrafficOut: var monthBegin = timeutil.Format("Ym01") var today = timeutil.Format("Ymd") stat, err := SharedNodeTrafficDailyStatDAO.SumDailyStat(tx, nodeconfigs.NodeRoleNode, nodeId, monthBegin, today) if err != nil { return nil, err } if stat != nil { value = stat.Bytes } else { value = 0 } case nodeconfigs.NodeActionParamBandwidthIn: itemValue, err := SharedNodeValueDAO.FindLatestNodeValue(tx, nodeconfigs.NodeRoleNode, nodeId, nodeconfigs.NodeValueItemAllTraffic) if err != nil { return nil, err } if itemValue == nil { return 0, nil } var mapValue = itemValue.DecodeMapValue() if mapValue != nil { value = mapValue.GetInt64("avgInBytes") * 8 } else { value = 0 } case nodeconfigs.NodeActionParamBandwidthOut: itemValue, err := SharedNodeValueDAO.FindLatestNodeValue(tx, nodeconfigs.NodeRoleNode, nodeId, nodeconfigs.NodeValueItemAllTraffic) if err != nil { return nil, err } if itemValue == nil { return 0, nil } var mapValue = itemValue.DecodeMapValue() if mapValue != nil { value = mapValue.GetInt64("avgOutBytes") * 8 } else { value = 0 } case nodeconfigs.NodeActionParamUDPDPSIn: itemValue, err := SharedNodeValueDAO.FindLatestNodeValue(tx, nodeconfigs.NodeRoleNode, nodeId, nodeconfigs.NodeValueItemAllTraffic) if err != nil { return nil, err } if itemValue == nil { return 0, nil } var mapValue = itemValue.DecodeMapValue() if mapValue != nil { value = mapValue.GetInt64("avgUDPInDatagrams") } else { value = 0 } case nodeconfigs.NodeActionParamUDPDPSOut: itemValue, err := SharedNodeValueDAO.FindLatestNodeValue(tx, nodeconfigs.NodeRoleNode, nodeId, nodeconfigs.NodeValueItemAllTraffic) if err != nil { return nil, err } if itemValue == nil { return 0, nil } var mapValue = itemValue.DecodeMapValue() if mapValue != nil { value = mapValue.GetInt64("avgUDPOutDatagrams") } else { value = 0 } case nodeconfigs.NodeActionParamCPUUsage: itemValue, err := SharedNodeValueDAO.FindLatestNodeValue(tx, nodeconfigs.NodeRoleNode, nodeId, nodeconfigs.NodeValueItemCPU) if err != nil { return nil, err } if itemValue == nil { return 0, nil } var mapValue = itemValue.DecodeMapValue() if mapValue != nil { value = mapValue.GetFloat64("usage") * 100 } else { value = 0 } case nodeconfigs.NodeActionParamMemoryUsage: itemValue, err := SharedNodeValueDAO.FindLatestNodeValue(tx, nodeconfigs.NodeRoleNode, nodeId, nodeconfigs.NodeValueItemMemory) if err != nil { return nil, err } if itemValue == nil { return 0, nil } var mapValue = itemValue.DecodeMapValue() if mapValue != nil { value = mapValue.GetFloat64("usage") * 100 } else { value = 0 } case nodeconfigs.NodeActionParamLoad: itemValue, err := SharedNodeValueDAO.FindLatestNodeValue(tx, nodeconfigs.NodeRoleNode, nodeId, nodeconfigs.NodeValueItemLoad) if err != nil { return nil, err } if itemValue == nil { return 0, nil } var mapValue = itemValue.DecodeMapValue() if mapValue != nil { value = mapValue.GetInt64("load5m") } else { value = 0 } case nodeconfigs.NodeActionParamHealthCheckFailure: // 不需要返回值 } return }) if err != nil { return err } if b { return this.runAction(tx, nodeId, int64(action.Id), condsConfig, action.DecodeAction(), action.DecodeDuration()) } } return nil } // 执行动作 func (this *NodeDAO) runAction(tx *dbs.Tx, nodeId int64, actionId int64, condsConfig *nodeconfigs.NodeActionCondsConfig, actionConfig *nodeconfigs.NodeActionConfig, duration *shared.TimeDuration) error { if actionConfig == nil { return errors.New("'actionConfig' should not be nil") } // 暂时不处理secondary clusters clusterId, err := this.Query(tx). Result("clusterId"). Pk(nodeId). FindInt64Col(0) if err != nil { return err } if clusterId == 0 { return nil } var status = &nodeconfigs.NodeActionStatus{ ActionId: actionId, Conds: condsConfig, Action: actionConfig, CreatedAt: time.Now().Unix(), } if duration != nil { var seconds = duration.Seconds() if seconds > 0 { status.ExpiresAt = time.Now().Unix() + seconds status.DurationSeconds = seconds } } if status.ExpiresAt <= 0 { status.ExpiresAt = time.Now().Unix() + 3600 // 默认1个小时 } if status.DurationSeconds <= 0 { status.DurationSeconds = 3600 } // 特殊情况下的过期时间 if condsConfig != nil { var now = time.Now() for _, cond := range condsConfig.Conds { if cond.Param == nodeconfigs.NodeActionParamDailyTrafficOut { // 当天结束 var expiresAt = now.Unix() - int64(now.Hour()*3600) - int64(now.Minute()*60) - int64(now.Second()) + 86400 if expiresAt > status.ExpiresAt { status.ExpiresAt = expiresAt } } else if cond.Param == nodeconfigs.NodeActionParamMonthlyTrafficOut { // 当月结束 var endDay = 32 - time.Date(now.Year(), now.Month(), 32, 0, 0, 0, 0, time.Local).Day() var expiresAt = now.Unix() - int64(now.Hour()*3600) - int64(now.Minute()*60) - int64(now.Second()) + int64((endDay-now.Day()+1)*86400) if expiresAt > status.ExpiresAt { status.ExpiresAt = expiresAt } } } } statusJSON, err := json.Marshal(status) if err != nil { return err } err = this.Query(tx). Pk(nodeId). Set("actionStatus", statusJSON). UpdateQuickly() if err != nil { return err } switch actionConfig.Code { case nodeconfigs.NodeActionCodeWebHook: var url = maps.NewMap(actionConfig.Params).GetString("url") if len(url) > 0 { if strings.Contains(url, "?") { url += "&" } else { url += "?" } url += "role=node&clusterId=" + types.String(clusterId) + "&nodeId=" + types.String(nodeId) req, err := http.NewRequest(http.MethodGet, url, nil) if err != nil { return err } var httpClient = utils.SharedHttpClient(5 * time.Second) httpResp, httpErr := httpClient.Do(req) if httpErr == nil { _ = httpResp.Body.Close() } } default: // 通知更新 err = dns.SharedDNSTaskDAO.CreateClusterTask(tx, clusterId, dns.DNSTaskTypeClusterNodesChange) if err != nil { return err } } // 发送消息 return SharedMessageDAO.CreateNodeMessage(tx, nodeconfigs.NodeRoleNode, clusterId, nodeId, MessageTypeNodeSchedule, MessageLevelError, "节点已满足调度条件", "节点已满足调度条件,并执行动作:"+nodeconfigs.FindNodeActionName(actionConfig.Code), nil, true) } // CheckNodeIPAddresses 检查节点IP地址 func (this *NodeDAO) CheckNodeIPAddresses(tx *dbs.Tx, node *Node) (shouldSkip bool, shouldOverwrite bool, ipAddressStrings []string, err error) { // 是否到期 if len(node.OfflineDay) > 0 && node.OfflineDay < timeutil.Format("Ymd") { // 到期后直接跳过 shouldSkip = true return } // 是否为备用 if node.IsBackupForCluster || node.IsBackupForGroup { shouldSkip = true return } // 当前状态 if len(node.ActionStatus) > 0 { var actionStatus = node.DecodeActionStatus() if actionStatus.ActionId > 0 && actionStatus.Action != nil { switch actionStatus.Action.Code { case nodeconfigs.NodeActionCodeUp: // do nothing return case nodeconfigs.NodeActionCodeDown: shouldSkip = true return case nodeconfigs.NodeActionCodeSwitchToBackupNodesInCluster: if node.ClusterId <= 0 { shouldSkip = true return } addresses, addressesErr := SharedNodeIPAddressDAO.FindAllBackupNodeAccessAndUpIPAddresses(tx, int64(node.ClusterId), 0, nodeconfigs.NodeRoleNode) if addressesErr != nil { return false, false, nil, addressesErr } for _, address := range addresses { var ip = address.DNSIP() if !lists.ContainsString(ipAddressStrings, ip) { ipAddressStrings = append(ipAddressStrings, ip) } } if len(ipAddressStrings) > 0 { shouldOverwrite = true } return case nodeconfigs.NodeActionCodeSwitchToBackupNodesInGroup: if node.GroupId <= 0 { shouldSkip = true return } addresses, addressesErr := SharedNodeIPAddressDAO.FindAllBackupNodeAccessAndUpIPAddresses(tx, 0, int64(node.GroupId), nodeconfigs.NodeRoleNode) if addressesErr != nil { return false, false, nil, addressesErr } for _, address := range addresses { var ip = address.DNSIP() if !lists.ContainsString(ipAddressStrings, ip) { ipAddressStrings = append(ipAddressStrings, ip) } } if len(ipAddressStrings) > 0 { shouldOverwrite = true } return case nodeconfigs.NodeActionCodeSwitchToBackupIP: ipAddressStrings = node.DecodeBackupIPs() shouldOverwrite = true return case nodeconfigs.NodeActionCodeEnableBackupNodesInCluster: if node.ClusterId <= 0 { shouldSkip = true return } addresses, addressesErr := SharedNodeIPAddressDAO.FindAllBackupNodeAccessAndUpIPAddresses(tx, int64(node.ClusterId), 0, nodeconfigs.NodeRoleNode) if addressesErr != nil { return false, false, nil, addressesErr } for _, address := range addresses { var ip = address.DNSIP() if !lists.ContainsString(ipAddressStrings, ip) { ipAddressStrings = append(ipAddressStrings, ip) } } return case nodeconfigs.NodeActionCodeEnableBackupNodesInGroup: if node.GroupId <= 0 { // 不能跳过 return } addresses, addressesErr := SharedNodeIPAddressDAO.FindAllBackupNodeAccessAndUpIPAddresses(tx, 0, int64(node.GroupId), nodeconfigs.NodeRoleNode) if addressesErr != nil { return false, false, nil, addressesErr } for _, address := range addresses { var ip = address.DNSIP() if !lists.ContainsString(ipAddressStrings, ip) { ipAddressStrings = append(ipAddressStrings, ip) } } return case nodeconfigs.NodeActionCodeEnableBackupIP: ipAddressStrings = node.DecodeBackupIPs() return case nodeconfigs.NodeActionCodeWebHook: // do nothing return } } } return }