ref: 重构网元状态,提升加载网元列表带状态速度

Refactor network element state management

- Removed the NE state endpoint and related service logic from the network_data module.
- Introduced a new NEStateController to handle network element state records.
- Implemented NEState service and repository for managing state records in the database.
- Updated NEInfo and NeLicense controllers to utilize the new NEState service for fetching and saving state information.
- Enhanced state handling in the websocket processor to reflect the latest state of network elements.
- Added caching logic for network element states using Redis.
- Improved error handling and response formatting for state queries.
This commit is contained in:
TsMask
2025-10-27 15:15:27 +08:00
parent 667d0fdad8
commit e7ae390f6e
18 changed files with 428 additions and 343 deletions

View File

@@ -6,16 +6,20 @@ CREATE TABLE "ne_state" (
"id" integer NOT NULL,
"ne_type" text(16),
"ne_id" text(32),
"version" text(16),
"capability" integer,
"ne_version" text(16),
"standby" integer,
"nb_number" integer,
"ue_number" integer,
"serial_num" text(16),
"expiry_date" text(10),
"hostname" text(32),
"os" text(196),
"sys_cpu_usage" integer,
"sys_mem_usage" integer,
"sys_disk_usage" integer,
"nf_cpu_usage" integer,
"nf_mem_used" integer,
"create_time" integer,
"refresh_time" integer,
PRIMARY KEY ("id")
);
@@ -26,7 +30,7 @@ CREATE INDEX "idx_type_id_time"
ON "ne_state" (
"ne_type" ASC,
"ne_id" ASC,
"create_time" ASC
"refresh_time" ASC
);
-- ----------------------------

View File

@@ -0,0 +1,38 @@
-- ----------------------------
-- Table structure for ne_state
-- ----------------------------
DROP TABLE IF EXISTS "ne_state";
CREATE TABLE "ne_state" (
"id" integer NOT NULL,
"ne_type" text(16),
"ne_id" text(32),
"ne_version" text(16),
"standby" integer,
"nb_number" integer,
"ue_number" integer,
"serial_num" text(16),
"expiry_date" text(10),
"hostname" text(32),
"os" text(196),
"sys_cpu_usage" integer,
"sys_mem_usage" integer,
"sys_disk_usage" integer,
"nf_cpu_usage" integer,
"nf_mem_used" integer,
"refresh_time" integer,
PRIMARY KEY ("id")
);
-- ----------------------------
-- Indexes structure for table ne_state
-- ----------------------------
CREATE INDEX IF NOT EXISTS "idx_type_id_time"
ON "ne_state" (
"ne_type" ASC,
"ne_id" ASC,
"refresh_time" ASC
);
-- ----------------------------
-- Records of ne_state
-- ----------------------------

View File

@@ -7,20 +7,24 @@ SET FOREIGN_KEY_CHECKS = 0;
DROP TABLE IF EXISTS `ne_state`;
CREATE TABLE `ne_state` (
`id` int NOT NULL AUTO_INCREMENT,
`ne_type` varchar(16) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT '',
`ne_id` varchar(32) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT '',
`version` varchar(16) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT '' COMMENT '版本',
`capability` bigint DEFAULT '0' COMMENT '用户容量',
`ne_type` varchar(16) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT '' COMMENT '网元类型',
`ne_id` varchar(32) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT '' COMMENT '网元ID',
`ne_version` varchar(16) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT '' COMMENT '网元版本',
`standby` int DEFAULT '0' COMMENT '主备状态 0-主 1-备',
`nb_number` int DEFAULT '0' COMMENT '基站容量',
`ue_number` int DEFAULT '0' COMMENT '用户容量',
`serial_num` varchar(16) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT '' COMMENT '序列号',
`expiry_date` varchar(10) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT '' COMMENT '许可证到期日期',
`hostname` varchar(32) COLLATE utf8mb4_general_ci DEFAULT '' COMMENT '主机名 hostname',
`os` varchar(196) COLLATE utf8mb4_general_ci DEFAULT '' COMMENT '操作系统 uname',
`sys_cpu_usage` float DEFAULT '0' COMMENT 'cpu使用率-sys',
`sys_mem_usage` float DEFAULT '0' COMMENT '内存使用率-sys',
`sys_disk_usage` float DEFAULT '0' COMMENT '磁盘使用率-sys',
`nf_cpu_usage` float DEFAULT '0' COMMENT 'cpu使用率-nf',
`nf_mem_used` bigint DEFAULT '0' COMMENT '内存使用KB-nf',
`create_time` bigint DEFAULT '0' COMMENT '创建时间',
`refresh_time` bigint DEFAULT '0' COMMENT '刷新时间',
PRIMARY KEY (`id`) USING BTREE,
KEY `idx_type_id_time` (`ne_type`,`ne_id`,`create_time`) USING BTREE COMMENT 'idx_state_ne_type_id_at'
KEY `idx_type_id_time` (`ne_type`,`ne_id`,`refresh_time`) USING BTREE COMMENT 'idx_state_ne_type_id_at'
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci COMMENT='网元_状态记录内存/CPU/磁盘';
SET FOREIGN_KEY_CHECKS=1;

View File

@@ -1,49 +1,29 @@
SET NAMES utf8mb4;
SET FOREIGN_KEY_CHECKS = 0;
--
-- Table structure for table `ne_state`
--
CREATE TABLE IF NOT EXISTS `ne_state` (
DROP TABLE IF EXISTS `ne_state`;
CREATE TABLE `ne_state` (
`id` int NOT NULL AUTO_INCREMENT,
`ne_type` varchar(16) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT '',
`ne_id` varchar(32) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT '',
`version` varchar(16) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT '' COMMENT '版本',
`capability` bigint DEFAULT '0' COMMENT '用户容量',
`ne_type` varchar(16) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT '' COMMENT '网元类型',
`ne_id` varchar(32) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT '' COMMENT '网元ID',
`ne_version` varchar(16) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT '' COMMENT '网元版本',
`standby` int DEFAULT '0' COMMENT '主备状态 0-主 1-备',
`nb_number` int DEFAULT '0' COMMENT '基站容量',
`ue_number` int DEFAULT '0' COMMENT '用户容量',
`serial_num` varchar(16) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT '' COMMENT '序列号',
`expiry_date` varchar(10) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT '' COMMENT '许可证到期日期',
`hostname` varchar(32) COLLATE utf8mb4_general_ci DEFAULT '' COMMENT '主机名 hostname',
`os` varchar(196) COLLATE utf8mb4_general_ci DEFAULT '' COMMENT '操作系统 uname',
`sys_cpu_usage` float DEFAULT '0' COMMENT 'cpu使用率-sys',
`sys_mem_usage` float DEFAULT '0' COMMENT '内存使用率-sys',
`sys_disk_usage` float DEFAULT '0' COMMENT '磁盘使用率-sys',
`nf_cpu_usage` float DEFAULT '0' COMMENT 'cpu使用率-nf',
`nf_mem_used` bigint DEFAULT '0' COMMENT '内存使用KB-nf',
`create_time` bigint DEFAULT '0' COMMENT '创建时间',
`refresh_time` bigint DEFAULT '0' COMMENT '刷新时间',
PRIMARY KEY (`id`) USING BTREE,
KEY `idx_type_id_time` (`ne_type`,`ne_id`,`create_time`) USING BTREE COMMENT 'idx_state_ne_type_id_at'
KEY `idx_type_id_time` (`ne_type`,`ne_id`,`refresh_time`) USING BTREE COMMENT 'idx_state_ne_type_id_at'
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci COMMENT='网元_状态记录内存/CPU/磁盘';
-- ----------------------------
-- COLUMN for ne_state
-- ----------------------------
ALTER TABLE `ne_state` DROP COLUMN `cpu_usage`;
ALTER TABLE `ne_state` DROP COLUMN `mem_usage`;
ALTER TABLE `ne_state` DROP COLUMN `disk_space`;
ALTER TABLE `ne_state` DROP COLUMN `timestamp`;
ALTER TABLE `ne_state` DROP INDEX `idx_ne_type_id`;
ALTER TABLE `ne_state` DROP INDEX `idx_timestamp`;
ALTER TABLE `ne_state` MODIFY COLUMN `ne_type` varchar(16) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT '' AFTER `id`;
ALTER TABLE `ne_state` MODIFY COLUMN `ne_id` varchar(32) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT '' AFTER `ne_type`;
ALTER TABLE `ne_state` MODIFY COLUMN `version` varchar(16) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT '' COMMENT '版本' AFTER `ne_id`;
ALTER TABLE `ne_state` MODIFY COLUMN `capability` bigint(20) NULL DEFAULT 0 COMMENT '用户容量' AFTER `version`;
ALTER TABLE `ne_state` MODIFY COLUMN `serial_num` varchar(16) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT '' COMMENT '序列号' AFTER `capability`;
ALTER TABLE `ne_state` MODIFY COLUMN `expiry_date` varchar(10) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT '' COMMENT '许可证到期日期' AFTER `serial_num`;
ALTER TABLE `ne_state` ADD COLUMN `sys_cpu_usage` float NULL DEFAULT 0 COMMENT 'cpu使用率-sys' AFTER `expiry_date`;
ALTER TABLE `ne_state` ADD COLUMN `sys_mem_usage` float NULL DEFAULT 0 COMMENT '内存使用率-sys' AFTER `sys_cpu_usage`;
ALTER TABLE `ne_state` ADD COLUMN `sys_disk_usage` float NULL DEFAULT 0 COMMENT '磁盘使用率-sys' AFTER `sys_mem_usage`;
ALTER TABLE `ne_state` ADD COLUMN `nf_cpu_usage` float NULL DEFAULT 0 COMMENT 'cpu使用率-nf' AFTER `sys_disk_usage`;
ALTER TABLE `ne_state` ADD COLUMN `nf_mem_used` bigint(20) NULL DEFAULT 0 COMMENT '内存使用KB-nf' AFTER `nf_cpu_usage`;
ALTER TABLE `ne_state` ADD COLUMN `create_time` bigint(20) NULL DEFAULT 0 COMMENT '创建时间' AFTER `nf_mem_used`;
ALTER TABLE `ne_state` ADD INDEX `idx_type_id_time`(`ne_type`, `ne_id`, `create_time`) USING BTREE COMMENT 'idx_state_ne_type_id_at';
ALTER TABLE `ne_state` COMMENT = '网元_状态记录内存/CPU/磁盘';
SET FOREIGN_KEY_CHECKS=1;

View File

@@ -4,8 +4,6 @@ import (
"encoding/json"
"fmt"
"runtime"
"sort"
"strconv"
"strings"
"sync"
"time"
@@ -17,8 +15,7 @@ import (
"be.ems/src/framework/database/redis"
"be.ems/src/framework/logger"
"be.ems/src/framework/utils/parse"
neDataModel "be.ems/src/modules/network_data/model"
neDataService "be.ems/src/modules/network_data/service"
neFetchlink "be.ems/src/modules/network_element/fetch_link"
neModel "be.ems/src/modules/network_element/model"
neService "be.ems/src/modules/network_element/service"
oamService "be.ems/src/modules/oam/service"
@@ -27,7 +24,7 @@ import (
var NewProcessor = &NeAlarmStateCheckCMDProcessor{
neInfoService: neService.NewNeInfo,
neStateService: neDataService.NewNEState,
neStateService: neService.NewNEState,
wsSendService: wsService.NewWSSend,
count: 0,
triggerMax: 3,
@@ -36,12 +33,12 @@ var NewProcessor = &NeAlarmStateCheckCMDProcessor{
// NeAlarmStateCheckCMDProcessor 网元告警内存/CPU/磁盘检查
type NeAlarmStateCheckCMDProcessor struct {
neInfoService *neService.NeInfo // 网元信息服务
neStateService *neDataService.NEState // 网元状态信息服务
wsSendService *wsService.WSSend // ws发送服务
count int // 执行次数
triggerMax int64 // 阈值:连续触发次数大于该值才会产生告警
triggerCount sync.Map // 阈值连续触发次数,存储每个事件的触发记录
neInfoService *neService.NeInfo // 网元信息服务
neStateService *neService.NEState // 网元状态信息服务
wsSendService *wsService.WSSend // ws发送服务
count int // 执行次数
triggerMax int64 // 阈值:连续触发次数大于该值才会产生告警
triggerCount sync.Map // 阈值连续触发次数,存储每个事件的触发记录
}
// alarmParams 告警参数
@@ -95,20 +92,23 @@ func (s *NeAlarmStateCheckCMDProcessor) Execute(data any) (any, error) {
return nil, fmt.Errorf("diskUseGt must be between 0 and 100")
}
neList := s.neInfoService.Find(neModel.NeInfo{}, true, false)
neList := s.neInfoService.Find(neModel.NeInfo{}, false, false)
for _, neInfo := range neList {
if neInfo.CreateTime == 0 {
continue
}
// 网元在线状态
isOnline := parse.Boolean(neInfo.ServerState["online"])
if !isOnline {
key := fmt.Sprintf("%s:%s:%s_status", constants.CACHE_NE_INFO, neInfo.NeType, neInfo.NeId)
state, stateErr := neFetchlink.NeState(neInfo)
if stateErr != nil {
redis.Del("", key)
continue
}
redis.Set("", key, neInfo.RmUID, 0)
// 检查状态
sysCpuUsage, sysMemUsage, sysDiskUsage := s.serverState(neInfo.ServerState)
sysCpuUsage, sysMemUsage, sysDiskUsage := s.serverState(state)
// 检查CPU/Memory/Disk使用率
warnMsg := []string{}
if int64(sysCpuUsage) >= params.CPUUseGt {
@@ -193,68 +193,17 @@ func (s *NeAlarmStateCheckCMDProcessor) Execute(data any) (any, error) {
// serverState 网元状态记录
func (s *NeAlarmStateCheckCMDProcessor) serverState(state map[string]any) (float64, float64, float64) {
// 网元CPU使用率
var nfCpuUsage float64 = 0
// CPU使用率
var sysCpuUsage float64 = 0
if state["cpu"] != nil {
cpu := state["cpu"].(map[string]any)
v := parse.Number(cpu["sysCpuUsage"])
sysCpuUsage = float64(v) / 100
nfv := parse.Number(cpu["nfCpuUsage"])
nfCpuUsage = float64(nfv) / 100
neState := s.neStateService.SaveState(state)
if neState.ID <= 0 {
return 0, 0, 0
}
// 网元内存使用KB
var nfMemUsed int64 = 0
// 内存使用率
var sysMemUsage float64 = 0
if state["mem"] != nil {
mem := state["mem"].(map[string]any)
v := parse.Number(mem["sysMemUsage"])
sysMemUsage = float64(v) / 100
nfMemUsed = parse.Number(mem["nfUsedMem"])
}
// 磁盘使用率
var sysDiskUsage float64 = 0
if state["disk"] != nil {
mem := state["disk"].(map[string]any)
disks := mem["partitionInfo"].([]any)
sort.Slice(disks, func(i, j int) bool {
iUsed := parse.Number(disks[i].(map[string]any)["used"])
jUsed := parse.Number(disks[j].(map[string]any)["used"])
return iUsed > jUsed
})
disk := disks[0].(map[string]any)
total := parse.Number(disk["total"])
used := parse.Number(disk["used"])
sysDiskUsage = (float64(used) / float64(total)) * 100
sysDiskUsage, _ = strconv.ParseFloat(fmt.Sprintf("%.2f", sysDiskUsage), 64)
}
// 插入网元状态记录
neState := neDataModel.NEState{
NeType: fmt.Sprint(state["neType"]),
NeId: fmt.Sprint(state["neId"]),
Version: fmt.Sprint(state["version"]),
Capability: parse.Number(state["capability"]),
SerialNum: fmt.Sprint(state["sn"]),
ExpiryDate: fmt.Sprint(state["expire"]),
SysCpuUsage: sysCpuUsage,
SysMemUsage: sysMemUsage,
SysDiskUsage: sysDiskUsage,
NfCpuUsage: nfCpuUsage,
NfMemUsed: nfMemUsed,
CreateTime: parse.Number(state["refreshTime"]),
}
s.neStateService.Insert(neState)
// 删除网元状态记录7天前
ltTime := time.Now().AddDate(0, 0, -7).UnixMilli()
s.neStateService.DeleteByTime(ltTime)
s.neStateService.DeleteByTime(neState.NeType, neState.NeId, ltTime)
// 推送ws消息
groupID := fmt.Sprintf("%s_%s_%s", wsService.GROUP_NE_STATE, neState.NeType, neState.NeId)
s.wsSendService.ByGroupID(groupID, neState)
return sysCpuUsage, sysMemUsage, sysDiskUsage
return neState.SysCpuUsage, neState.SysMemUsage, neState.SysDiskUsage
}

View File

@@ -137,15 +137,6 @@ func Setup(router *gin.Engine) {
)
}
// 网元状态记录信息
neStateGroup := neDataGroup.Group("/ne-state")
{
neStateGroup.GET("/list",
middleware.AuthorizeUser(nil),
controller.NewNEState.List,
)
}
// 基站状态历史记录信息 含AMF/MME
nbStateGroup := neDataGroup.Group("/nb-state")
{

View File

@@ -1,31 +0,0 @@
package service
import (
"be.ems/src/modules/network_data/model"
"be.ems/src/modules/network_data/repository"
)
// 实例化数据层 NEState 结构体
var NewNEState = &NEState{
neStateRepository: repository.NewNEState,
}
// NEState 网元状态记录表 服务层处理
type NEState struct {
neStateRepository *repository.NEState // 网元状态记录信息
}
// FindByPage 根据条件分页查询
func (r NEState) FindByPage(query model.NEStateQuery) ([]model.NEState, int64) {
return r.neStateRepository.SelectByPage(query)
}
// Insert 插入数据
func (r NEState) Insert(item model.NEState) int64 {
return r.neStateRepository.Insert(item)
}
// DeleteByTime 删除数据
func (r NEState) DeleteByTime(ltTime int64) int64 {
return r.neStateRepository.DeleteByTime(ltTime)
}

View File

@@ -3,8 +3,9 @@ package controller
import (
"fmt"
"strings"
"sync"
"be.ems/src/framework/constants"
"be.ems/src/framework/database/redis"
"be.ems/src/framework/i18n"
"be.ems/src/framework/reqctx"
"be.ems/src/framework/resp"
@@ -21,6 +22,7 @@ var NewNeInfo = &NeInfoController{
neInfoService: neService.NewNeInfo,
neLicenseService: neService.NewNeLicense,
neVersionService: neService.NewNeVersion,
neStateService: neService.NewNEState,
}
// 网元信息请求
@@ -30,12 +32,9 @@ type NeInfoController struct {
neInfoService *neService.NeInfo // 网元信息服务
neLicenseService *neService.NeLicense // 网元授权激活信息服务
neVersionService *neService.NeVersion // 网元版本信息服务
neStateService *neService.NEState // 网元状态服务
}
// neStateCacheMap 网元状态缓存最后一次成功的信息
var neStateCacheMap sync.Map
var mutex sync.Mutex
// 网元信息状态
//
// GET /state
@@ -67,37 +66,18 @@ func (s NeInfoController) State(c *gin.Context) {
c.JSON(200, resp.ErrMsg(i18n.TKey(language, "app.common.noNEInfo")))
return
}
neKey := fmt.Sprintf("%s_%s", neInfo.NeType, neInfo.NeId)
state := s.neStateService.Last(neInfo.NeType, neInfo.NeId)
result := s.neStateService.ParseState(neInfo, state)
// 网元直连
resData, err := neFetchlink.NeState(neInfo)
if err != nil {
mutex.Lock()
// 异常取上次缓存
resDataCache, ok := neStateCacheMap.Load(neKey)
if ok && resDataCache != nil {
resDataCache.(map[string]any)["online"] = false
} else {
resDataCache = map[string]any{
"online": false,
"neId": neInfo.NeId,
"neName": neInfo.NeName,
"neType": neInfo.NeType,
"neIP": neInfo.IP,
}
}
neStateCacheMap.Store(neKey, resDataCache)
mutex.Unlock()
c.JSON(200, resp.OkData(resDataCache))
return
// 没状态标记,视为离线
key := fmt.Sprintf("%s:%s:%s_status", constants.CACHE_NE_INFO, neInfo.NeType, neInfo.NeId)
keyNum, err := redis.Has("", key)
if keyNum == 0 || err != nil {
result["online"] = false
} else {
result["online"] = true
}
// 存入缓存
resData["online"] = true
mutex.Lock()
neStateCacheMap.Store(neKey, resData)
mutex.Unlock()
c.JSON(200, resp.OkData(resData))
c.JSON(200, resp.OkData(result))
}
// 网元neType和neID查询
@@ -362,22 +342,6 @@ func (s NeInfoController) Add(c *gin.Context) {
return
}
// 获取网元状态是否正常
body.ServerState, err = neFetchlink.NeState(body)
if err != nil {
body.Status = 0
} else {
// 网元状态设置为在线
body.Status = 1
if parse.Boolean(body.ServerState["standby"]) {
body.Status = 3
}
// 下发网管配置信息给网元
if _, err = neFetchlink.NeConfigOMC(body); err != nil {
body.Status = 2
}
}
loginUserName := reqctx.LoginUserToUserName(c)
// 新增Version信息
neVersion := model.NeVersion{
@@ -392,24 +356,34 @@ func (s NeInfoController) Add(c *gin.Context) {
CreateBy: loginUserName,
}
// 已有网元可获取的信息
if body.ServerState != nil {
if v, ok := body.ServerState["version"]; ok && v != nil {
body.NeVersion = fmt.Sprint(v)
neVersion.Version = fmt.Sprint(v)
// 获取网元状态是否正常
key := fmt.Sprintf("%s:%s:%s_status", constants.CACHE_NE_INFO, body.NeType, body.NeId)
state, err := neFetchlink.NeState(body)
if err != nil {
redis.Del("", key)
body.Status = 0
} else {
redis.Set("", key, body.RmUID, 0)
// 网元状态设置为在线
body.Status = 1
if parse.Boolean(state["standby"]) {
body.Status = 3
}
if v, ok := body.ServerState["sn"]; ok && v != nil {
neLicense.SerialNum = fmt.Sprint(v)
// 下发网管配置信息给网元
if _, err = neFetchlink.NeConfigOMC(body); err != nil {
body.Status = 2
}
if v, ok := body.ServerState["expire"]; ok && v != nil {
neLicense.ExpiryDate = fmt.Sprint(v)
// 网元状态记录
neState := s.neStateService.SaveState(state)
body.ServerState = s.neStateService.ParseState(body, neState)
if neState.ID > 0 {
body.NeVersion = neState.NeVersion
neVersion.Version = neState.NeVersion
neLicense.SerialNum = neState.SerialNum
neLicense.ExpiryDate = neState.ExpiryDate
neLicense.Status = "1"
}
if v, ok := body.ServerState["ueNumber"]; ok && v != nil {
neLicense.UeNumber = parse.Number(v)
}
if v, ok := body.ServerState["nbNumber"]; ok && v != nil {
neLicense.NbNumber = parse.Number(v)
neLicense.UeNumber = neState.UENumber
neLicense.NbNumber = neState.NbNumber
}
}
@@ -475,62 +449,41 @@ func (s NeInfoController) Edit(c *gin.Context) {
}
}
loginUserName := reqctx.LoginUserToUserName(c)
neLicense := s.neLicenseService.FindByNeTypeAndNeID(neInfo.NeType, neInfo.NeId)
neVersion := s.neVersionService.FindByNeTypeAndNeID(neInfo.NeType, neInfo.NeId)
// 获取网元状态是否正常
body.ServerState, err = neFetchlink.NeState(neInfo)
key := fmt.Sprintf("%s:%s:%s_status", constants.CACHE_NE_INFO, body.NeType, body.NeId)
state, err := neFetchlink.NeState(body)
if err != nil {
redis.Del("", key)
body.Status = 0
} else {
redis.Set("", key, body.RmUID, 0)
// 网元状态设置为在线
body.Status = 1
if parse.Boolean(body.ServerState["standby"]) {
if parse.Boolean(state["standby"]) {
body.Status = 3
}
// 下发网管配置信息给网元
if _, err = neFetchlink.NeConfigOMC(body); err != nil {
body.Status = 2
}
}
loginUserName := reqctx.LoginUserToUserName(c)
neLicense := s.neLicenseService.FindByNeTypeAndNeID(neInfo.NeType, neInfo.NeId)
neVersion := s.neVersionService.FindByNeTypeAndNeID(neInfo.NeType, neInfo.NeId)
// 已有网元可获取的信息
if body.ServerState != nil {
if v, ok := body.ServerState["version"]; ok && v != nil {
body.NeVersion = fmt.Sprint(v)
neVersion.Version = fmt.Sprint(v)
neVersion.UpdateBy = loginUserName
}
if v, ok := body.ServerState["sn"]; ok && v != nil {
neLicense.SerialNum = fmt.Sprint(v)
}
if v, ok := body.ServerState["expire"]; ok && v != nil {
neLicense.ExpiryDate = fmt.Sprint(v)
// 网元状态记录
neState := s.neStateService.SaveState(state)
body.ServerState = s.neStateService.ParseState(body, neState)
if neState.ID > 0 {
body.NeVersion = neState.NeVersion
neVersion.Version = neState.NeVersion
s.neVersionService.Update(neVersion)
neLicense.SerialNum = neState.SerialNum
neLicense.ExpiryDate = neState.ExpiryDate
neLicense.Status = "1"
neLicense.UpdateBy = loginUserName
neLicense.UeNumber = neState.UENumber
neLicense.NbNumber = neState.NbNumber
s.neLicenseService.Update(neLicense)
}
if v, ok := body.ServerState["ueNumber"]; ok && v != nil {
neLicense.UeNumber = parse.Number(v)
}
if v, ok := body.ServerState["nbNumber"]; ok && v != nil {
neLicense.NbNumber = parse.Number(v)
}
}
if neVersion.ID <= 0 {
if neVersion.NeType != body.NeType || neVersion.NeId != body.NeId {
neVersion.NeType = body.NeType
neVersion.NeId = body.NeId
}
s.neVersionService.Update(neVersion)
}
if neLicense.ID <= 0 {
if neLicense.NeType != body.NeType || neLicense.NeId != body.NeId {
neLicense.NeType = body.NeType
neLicense.NeId = body.NeId
}
s.neLicenseService.Update(neLicense)
}
body.UpdateBy = loginUserName

View File

@@ -6,11 +6,12 @@ import (
"github.com/gin-gonic/gin"
"be.ems/src/framework/constants"
"be.ems/src/framework/database/redis"
"be.ems/src/framework/i18n"
"be.ems/src/framework/reqctx"
"be.ems/src/framework/resp"
"be.ems/src/framework/utils/parse"
neFetchlink "be.ems/src/modules/network_element/fetch_link"
"be.ems/src/modules/network_element/model"
neService "be.ems/src/modules/network_element/service"
)
@@ -19,6 +20,7 @@ import (
var NewNeLicense = &NeLicenseController{
neLicenseService: neService.NewNeLicense,
neInfoService: neService.NewNeInfo,
neStateService: neService.NewNEState,
}
// 网元授权激活请求
@@ -27,6 +29,7 @@ var NewNeLicense = &NeLicenseController{
type NeLicenseController struct {
neLicenseService *neService.NeLicense // 网元授权激活服务
neInfoService *neService.NeInfo // 网元信息服务
neStateService *neService.NEState // 网元状态服务
}
// 网元授权激活列表
@@ -260,15 +263,21 @@ func (s *NeLicenseController) State(c *gin.Context) {
c.JSON(200, resp.ErrMsg(i18n.TKey(language, "app.common.noNEInfo")))
return
}
if neState, err := neFetchlink.NeState(neInfo); err == nil && neState["sn"] != nil {
neLicense.Status = "1"
neLicense.SerialNum = fmt.Sprint(neState["sn"])
neLicense.ExpiryDate = fmt.Sprint(neState["expire"])
neLicense.UeNumber = parse.Number(neState["ueNumber"])
neLicense.NbNumber = parse.Number(neState["nbNumber"])
} else {
// 没状态标记,视为离线
key := fmt.Sprintf("%s:%s:%s_status", constants.CACHE_NE_INFO, neInfo.NeType, neInfo.NeId)
keyNum, err := redis.Has("", key)
if keyNum == 0 || err != nil {
neLicense.Status = "0"
} else {
state := s.neStateService.Last(neInfo.NeType, neInfo.NeId)
neLicense.Status = "1"
neLicense.SerialNum = state.SerialNum
neLicense.ExpiryDate = state.ExpiryDate
neLicense.UeNumber = state.UENumber
neLicense.NbNumber = state.NbNumber
}
// 更新授权码
if neLicense.ActivationRequestCode == "" || strings.HasPrefix(neLicense.ActivationRequestCode, "SN") || neLicense.Status == "0" {
code, licensePath := s.neLicenseService.ReadLicenseInfo(neLicense)

View File

@@ -6,23 +6,22 @@ import (
"github.com/gin-gonic/gin"
"be.ems/src/framework/resp"
"be.ems/src/modules/network_data/model"
neDataService "be.ems/src/modules/network_data/service"
"be.ems/src/modules/network_element/model"
neService "be.ems/src/modules/network_element/service"
)
// 实例化控制层 NEStateController 结构体
var NewNEState = &NEStateController{
neInfoService: neService.NewNeInfo,
neStateService: neDataService.NewNEState,
neStateService: neService.NewNEState,
}
// 网元状态记录
//
// PATH /ne-state
// PATH /ne/state
type NEStateController struct {
neInfoService *neService.NeInfo // 网元信息服务
neStateService *neDataService.NEState // 网元状态服务
neInfoService *neService.NeInfo // 网元信息服务
neStateService *neService.NEState // 网元状态服务
}
// 网元状态记录-内存/CPU/磁盘列表
@@ -44,7 +43,7 @@ type NEStateController struct {
// @Security TokenAuth
// @Summary NE Status Record - Memory/CPU/Disk List
// @Description NE Status Record - Memory/CPU/Disk List
// @Router /ne-state/list [get]
// @Router /ne/state/list [get]
func (s NEStateController) List(c *gin.Context) {
var query model.NEStateQuery
if err := c.ShouldBindQuery(&query); err != nil {

View File

@@ -3,18 +3,22 @@ package model
// NEState 网元状态记录表 ne_state
type NEState struct {
ID int64 `json:"id" gorm:"column:id;primaryKey;autoIncrement"`
NeType string `json:"neType" gorm:"column:ne_type"`
NeId string `json:"neId" gorm:"column:ne_id"`
Version string `json:"version" gorm:"column:version"` // 版本
Capability int64 `json:"capability" gorm:"column:capability"` // 用户容量
NeType string `json:"neType" gorm:"column:ne_type"` // 网元类型
NeId string `json:"neId" gorm:"column:ne_id"` // 网元ID
NeVersion string `json:"neVersion" gorm:"column:ne_version"` // 网元版本
Standby int64 `json:"standby" gorm:"column:standby"` // 主备状态 0-主 1-备
NbNumber int64 `json:"nbNumber" gorm:"column:nb_number"` // 基站容量
UENumber int64 `json:"ueNumber" gorm:"column:ue_number"` // 用户容量
SerialNum string `json:"serialNum" gorm:"column:serial_num"` // 序列号
ExpiryDate string `json:"expiryDate" gorm:"column:expiry_date"` // 许可证到期日期
HostName string `json:"hostname" gorm:"column:hostname"` // 主机名 hostname
OS string `json:"os" gorm:"column:os"` // 操作系统 uname
SysCpuUsage float64 `json:"sysCpuUsage" gorm:"column:sys_cpu_usage"` // cpu使用率-sys
SysMemUsage float64 `json:"sysMemUsage" gorm:"column:sys_mem_usage"` // 内存使用率-sys
SysDiskUsage float64 `json:"sysDiskUsage" gorm:"column:sys_disk_usage"` // 磁盘使用率-sys
NfCpuUsage float64 `json:"nfCpuUsage" gorm:"column:nf_cpu_usage"` // cpu使用率-nf
NfMemUsed int64 `json:"nfMemUsed" gorm:"column:nf_mem_used"` // 内存使用KB-nf
CreateTime int64 `json:"createTime" gorm:"column:create_time"` // 创建时间
RefreshTime int64 `json:"refreshTime" gorm:"column:refresh_time"` // 刷新时间
}
// TableName 表名称

View File

@@ -48,6 +48,15 @@ func Setup(router *gin.Engine) {
)
}
// 网元状态记录信息
neStateGroup := router.Group("/ne/state")
{
neStateGroup.GET("/list",
middleware.AuthorizeUser(nil),
controller.NewNEState.List,
)
}
// 网元信息
neInfoGroup := router.Group("/ne/info")
{

View File

@@ -5,7 +5,7 @@ import (
"be.ems/src/framework/database/db"
"be.ems/src/framework/logger"
"be.ems/src/modules/network_data/model"
"be.ems/src/modules/network_element/model"
)
// 实例化数据层 NEState 结构体
@@ -25,18 +25,10 @@ func (r NEState) SelectByPage(query model.NEStateQuery) ([]model.NEState, int64)
tx = tx.Where("ne_id = ?", query.NeID)
}
if query.BeginTime != "" {
startTime := query.BeginTime
if len(startTime) == 10 {
startTime = startTime + "000"
}
tx = tx.Where("create_time >= ?", startTime)
tx = tx.Where("refresh_time >= ?", query.BeginTime)
}
if query.EndTime != "" {
endTime := query.EndTime
if len(endTime) == 10 {
endTime = endTime + "999"
}
tx = tx.Where("create_time <= ?", endTime)
tx = tx.Where("refresh_time <= ?", query.EndTime)
}
// 查询结果
@@ -85,12 +77,30 @@ func (r NEState) SelectByIds(ids []string) []model.NEState {
return rows
}
// SelectByLast 查询最新状态
func (r NEState) SelectByLast(neType, neId string) model.NEState {
item := model.NEState{}
if neType == "" || neId == "" {
return item
}
tx := db.DB("").Model(item)
// 构建查询条件
tx = tx.Where("ne_type = ? and ne_id = ?", neType, neId)
tx = tx.Order("refresh_time desc")
// 查询数据
if err := tx.Limit(1).Find(&item).Error; err != nil {
logger.Errorf("query find err => %v", err.Error())
return item
}
return item
}
// DeleteByTime 删除信息
func (r NEState) DeleteByTime(ltTime int64) int64 {
func (r NEState) DeleteByTime(neType, neId string, ltTime int64) int64 {
if ltTime <= 0 {
return 0
}
tx := db.DB("").Where("create_time < ?", ltTime)
tx := db.DB("").Where("ne_type = ? and ne_id = ? and refresh_time < ?", neType, neId, ltTime)
if err := tx.Delete(&model.NEState{}).Error; err != nil {
logger.Errorf("delete err => %v", err.Error())
return 0
@@ -100,7 +110,9 @@ func (r NEState) DeleteByTime(ltTime int64) int64 {
// Insert 新增信息
func (r NEState) Insert(param model.NEState) int64 {
param.CreateTime = time.Now().UnixMilli()
if param.RefreshTime <= 0 {
param.RefreshTime = time.Now().UnixMilli()
}
// 执行插入
if err := db.DB("").Create(&param).Error; err != nil {
logger.Errorf("insert err => %v", err.Error())

View File

@@ -147,3 +147,23 @@ func (r NeVersion) DeleteByIds(ids []int64) int64 {
}
return tx.RowsAffected
}
// UpdateVersion 修改网元版本
func (r NeVersion) UpdateVersion(id int64, neVersion string) int64 {
if id <= 0 {
return 0
}
tx := db.DB("").Model(&model.NeVersion{})
// 构建查询条件
tx = tx.Where("id = ?", id)
tx.Updates(map[string]any{
"version": neVersion,
"update_time": time.Now().UnixMilli(),
})
// 执行更新
if err := tx.Error; err != nil {
logger.Errorf("update err => %v", err.Error())
return 0
}
return tx.RowsAffected
}

View File

@@ -24,6 +24,7 @@ import (
var NewNeInfo = &NeInfo{
neInfoRepository: repository.NewNeInfo,
neHostService: NewNeHost,
neStateService: NewNEState,
Para5GData: map[string]string{},
}
@@ -31,6 +32,7 @@ var NewNeInfo = &NeInfo{
type NeInfo struct {
neInfoRepository *repository.NeInfo // 网元信息数据信息
neHostService *NeHost // 网元主机连接服务
neStateService *NEState // 网元状态服务
Para5GData map[string]string
}
@@ -127,10 +129,22 @@ func (r NeInfo) FindByRmuid(rmUid string) model.NeInfo {
} else {
neInfos := r.Find(neInfo, false, false)
for _, v := range neInfos {
key := fmt.Sprintf("%s:%s:%s", constants.CACHE_NE_INFO, strings.ToUpper(v.NeType), v.NeId)
neType := strings.ToUpper(v.NeType)
// 网元信息
key := fmt.Sprintf("%s:%s:%s", constants.CACHE_NE_INFO, neType, v.NeId)
redis.Del("", key)
values, _ := json.Marshal(v)
redis.Set("", key, string(values), 0)
// 网元在线状态
stateKey := fmt.Sprintf("%s:%s:%s_status", constants.CACHE_NE_INFO, neType, v.NeId)
state, stateErr := neFetchlink.NeState(v)
if stateErr != nil {
redis.Del("", stateKey)
} else {
redis.Set("", stateKey, neInfo.RmUID, 0)
r.neStateService.SaveState(state)
}
if v.RmUID == rmUid {
neInfo = v
}
@@ -177,11 +191,15 @@ func (r NeInfo) Find(ne model.NeInfo, bandStatus bool, bandHost bool) []model.Ne
func (r NeInfo) bandNeStatus(arr *[]model.NeInfo) {
for i := range *arr {
v := (*arr)[i]
result, err := neFetchlink.NeState(v)
if err != nil {
(*arr)[i].ServerState = map[string]any{
"online": false,
}
state := r.neStateService.Last(v.NeType, v.NeId)
result := r.neStateService.ParseState(v, state)
// 没状态标记,视为离线
key := fmt.Sprintf("%s:%s:%s_status", constants.CACHE_NE_INFO, v.NeType, v.NeId)
keyNum, err := redis.Has("", key)
if keyNum == 0 || err != nil {
result["online"] = false
(*arr)[i].ServerState = result
// 网元状态设置为离线
if v.Status != 0 {
v.Status = 0
@@ -190,17 +208,23 @@ func (r NeInfo) bandNeStatus(arr *[]model.NeInfo) {
}
continue
}
result["online"] = true
(*arr)[i].ServerState = result
// 网元状态设置为在线
var status int64 = 1
status := v.Status
if status == 0 || status == 2 {
// 下发网管配置信息给网元
_, err := neFetchlink.NeConfigOMC(v)
if err != nil {
status = 2
} else {
// 网元状态设置为在线
status = 1
}
}
if parse.Boolean(result["standby"]) {
status = 3
}
// 下发网管配置信息给网元
if _, err = neFetchlink.NeConfigOMC(v); err != nil {
status = 2
}
(*arr)[i].Status = status
if v.Status != status {
r.neInfoRepository.UpdateState(v.ID, status)

View File

@@ -0,0 +1,137 @@
package service
import (
"fmt"
"sort"
"strconv"
"be.ems/src/modules/network_element/model"
"be.ems/src/modules/network_element/repository"
"github.com/tsmask/go-oam/framework/utils/parse"
)
// 实例化数据层 NEState 结构体
var NewNEState = &NEState{
neStateRepository: repository.NewNEState,
}
// NEState 网元状态记录表 服务层处理
type NEState struct {
neStateRepository *repository.NEState // 网元状态记录信息
}
// FindByPage 根据条件分页查询
func (r NEState) FindByPage(query model.NEStateQuery) ([]model.NEState, int64) {
return r.neStateRepository.SelectByPage(query)
}
// Insert 插入数据
func (r NEState) Insert(item model.NEState) int64 {
return r.neStateRepository.Insert(item)
}
// DeleteByTime 删除数据
func (r NEState) DeleteByTime(neType, neId string, ltTime int64) int64 {
return r.neStateRepository.DeleteByTime(neType, neId, ltTime)
}
// Last 查询最新状态
func (r NEState) Last(neType, neId string) model.NEState {
return r.neStateRepository.SelectByLast(neType, neId)
}
// ParseState 解析状态数据
func (r NEState) ParseState(neInfo model.NeInfo, state model.NEState) map[string]any {
if state.ID == 0 {
return map[string]any{}
}
return map[string]any{
"neId": neInfo.NeId,
"neName": neInfo.NeName,
"neType": neInfo.NeType,
"neIP": neInfo.IP,
"standby": state.Standby,
"version": state.NeVersion,
"sn": state.SerialNum,
"expire": state.ExpiryDate,
"refreshTime": state.RefreshTime,
"ueNumber": state.UENumber,
"nbNumber": state.NbNumber,
"hostname": state.HostName,
"os": state.OS,
"sysCpuUsage": state.SysCpuUsage,
"sysMemUsage": state.SysMemUsage,
"sysDiskUsage": state.SysDiskUsage,
"nfCpuUsage": state.NfCpuUsage,
"nfMemUsed": state.NfMemUsed,
}
}
// SaveState 网元直连返回的状态数据保存
func (r NEState) SaveState(state map[string]any) model.NEState {
// 网元CPU使用率
var nfCpuUsage float64 = 0
// CPU使用率
var sysCpuUsage float64 = 0
if state["cpu"] != nil {
cpu := state["cpu"].(map[string]any)
v := parse.Number(cpu["sysCpuUsage"])
sysCpuUsage = float64(v) / 100
nfv := parse.Number(cpu["nfCpuUsage"])
nfCpuUsage = float64(nfv) / 100
}
// 网元内存使用KB
var nfMemUsed int64 = 0
// 内存使用率
var sysMemUsage float64 = 0
if state["mem"] != nil {
mem := state["mem"].(map[string]any)
v := parse.Number(mem["sysMemUsage"])
sysMemUsage = float64(v) / 100
nfMemUsed = parse.Number(mem["nfUsedMem"])
}
// 磁盘使用率
var sysDiskUsage float64 = 0
if state["disk"] != nil {
mem := state["disk"].(map[string]any)
disks := mem["partitionInfo"].([]any)
sort.Slice(disks, func(i, j int) bool {
iUsed := parse.Number(disks[i].(map[string]any)["used"])
jUsed := parse.Number(disks[j].(map[string]any)["used"])
return iUsed > jUsed
})
disk := disks[0].(map[string]any)
total := parse.Number(disk["total"])
used := parse.Number(disk["used"])
sysDiskUsage = (float64(used) / float64(total)) * 100
sysDiskUsage, _ = strconv.ParseFloat(fmt.Sprintf("%.2f", sysDiskUsage), 64)
}
// 插入网元状态记录
neState := model.NEState{
NeType: fmt.Sprint(state["neType"]),
NeId: fmt.Sprint(state["neId"]),
NeVersion: fmt.Sprint(state["version"]),
Standby: parse.Number(state["standby"]),
UENumber: parse.Number(state["ueNumber"]),
NbNumber: parse.Number(state["nbNumber"]),
SerialNum: fmt.Sprint(state["sn"]),
ExpiryDate: fmt.Sprint(state["expire"]),
HostName: fmt.Sprint(state["hostname"]),
OS: fmt.Sprint(state["os"]),
SysCpuUsage: sysCpuUsage,
SysMemUsage: sysMemUsage,
SysDiskUsage: sysDiskUsage,
NfCpuUsage: nfCpuUsage,
NfMemUsed: nfMemUsed,
RefreshTime: parse.Number(state["refreshTime"]),
}
insertId := r.neStateRepository.Insert(neState)
if insertId > 0 {
neState.ID = insertId
}
return neState
}

View File

@@ -9,7 +9,6 @@ import (
"be.ems/src/framework/ssh"
"be.ems/src/framework/utils/file"
neFetchlink "be.ems/src/modules/network_element/fetch_link"
"be.ems/src/modules/network_element/model"
"be.ems/src/modules/network_element/repository"
)
@@ -18,12 +17,14 @@ import (
var NewNeVersion = &NeVersion{
neVersionRepository: repository.NewNeVersion,
neInfoService: NewNeInfo,
neStateService: NewNEState,
}
// NeVersion 网元版本信息 服务层处理
type NeVersion struct {
neVersionRepository *repository.NeVersion // 网元版本信息表
neInfoService *NeInfo // 网元信息数据信息
neStateService *NEState // 网元状态服务
}
// FindByPage 分页查询列表数据
@@ -54,29 +55,15 @@ func (r NeVersion) Find(neVersion model.NeVersion, checkVersion bool) []model.Ne
func (r NeVersion) checkNeVersion(arr *[]model.NeVersion) {
for i := range *arr {
item := (*arr)[i]
// 查询网元获取IP
neInfo := r.neInfoService.FindByNeTypeAndNeID(item.NeType, item.NeId)
if neInfo.NeId != item.NeId || neInfo.IP == "" {
state := r.neStateService.Last(item.NeType, item.NeId)
if state.RefreshTime <= 0 {
continue
}
result, err := neFetchlink.NeState(neInfo)
if err != nil {
if state.NeVersion == item.Version {
continue
}
if v, ok := result["version"]; ok && v != nil {
ver := fmt.Sprint(v)
if ver == item.Version {
continue
}
// item.Name = "-"
// item.Path = "-"
item.Version = ver
}
if item.NeType != neInfo.NeType || item.NeId != neInfo.NeId {
item.NeType = neInfo.NeType
item.NeId = neInfo.NeId
}
r.Update(item)
item.Version = state.NeVersion
r.neVersionRepository.UpdateVersion(item.ID, item.Version)
(*arr)[i] = item
}
}

View File

@@ -4,9 +4,10 @@ import (
"encoding/json"
"fmt"
"be.ems/src/framework/constants"
"be.ems/src/framework/database/redis"
"be.ems/src/framework/logger"
"be.ems/src/framework/resp"
neFetchlink "be.ems/src/modules/network_element/fetch_link"
neService "be.ems/src/modules/network_element/service"
)
@@ -33,26 +34,21 @@ func GetNeState(requestID string, data any) ([]byte, error) {
return nil, fmt.Errorf("no matching network element information found")
}
// 网元直连
resData, err := neFetchlink.NeState(neInfo)
if err != nil {
resultByte, err := json.Marshal(resp.Ok(map[string]any{
"requestId": requestID,
"data": map[string]any{
"online": false,
"neId": neInfo.NeId,
"neName": neInfo.NeName,
"neType": neInfo.NeType,
"neIP": neInfo.IP,
},
}))
return resultByte, err
state := neService.NewNEState.Last(neInfo.NeType, neInfo.NeId)
result := neService.NewNEState.ParseState(neInfo, state)
// 没状态标记,视为离线
key := fmt.Sprintf("%s:%s:%s_status", constants.CACHE_NE_INFO, neInfo.NeType, neInfo.NeId)
keyNum, err := redis.Has("", key)
if keyNum == 0 || err != nil {
result["online"] = false
} else {
result["online"] = true
}
resData["online"] = true
resultByte, err := json.Marshal(resp.Ok(map[string]any{
"requestId": requestID,
"data": resData,
"data": result,
}))
return resultByte, err
}