1
0

merge: 合并代码20241211

This commit is contained in:
TsMask
2024-12-11 16:15:13 +08:00
parent aedea2ce2d
commit 1fa9442c1f
43 changed files with 885 additions and 479 deletions

View File

@@ -1,3 +0,0 @@
{
"recommendations": []
}

25
.vscode/launch.json vendored
View File

@@ -18,7 +18,7 @@
"type": "go",
"request": "launch",
"mode": "debug",
"program": "d:/local.git/ems.agt/restagent/",
"program": "d:/omc.git/be.ems/restagent/",
"console": "integratedTerminal"
},
{
@@ -26,16 +26,7 @@
"type": "go",
"request": "launch",
"mode": "debug",
"program": "d:/local.git/ems.agt/sshsvc/sshsvc.go",
"console": "integratedTerminal"
},
{
"name": "debug loadpconf",
"type": "go",
"request": "launch",
"mode": "debug",
"program": "d:/local.git/ems.agt/tools/loadpconf",
"args": ["-p","../../config/param/upf_param_config.yaml"],
"program": "d:/omc.git/be.ems/sshsvc/sshsvc.go",
"console": "integratedTerminal"
},
{
@@ -43,8 +34,16 @@
"type": "go",
"request": "launch",
"mode": "debug",
"program": "d:/local.git/ems.agt/crontask",
"program": "d:/omc.git/be.ems/crontask",
"console": "integratedTerminal"
}
},
{
"name": "debug encyaml",
"type": "go",
"request": "launch",
"mode": "debug",
"program": "d:/omc.git/be.ems/tools/encode",
"console": "integratedTerminal"
}
]
}

11
.vscode/settings.json vendored
View File

@@ -1,11 +0,0 @@
{
"go.toolsEnvVars": {
"GOARCH": "amd64",
"GOOS": "windows"
},
"go.testEnvVars": {
"GOARCH": "wasm",
"GOOS": "js"
},
"commentTranslate.hover.enabled": true
}

View File

@@ -40,13 +40,14 @@ func (k *KpiCReport) Get(c *gin.Context) {
return
}
if querys.StartTime != "" {
conditions = append(conditions, "created_at >= ?")
conditions = append(conditions, "(UNIX_TIMESTAMP(created_at) * 1000) >= ?")
params = append(params, querys.StartTime)
}
if querys.EndTime != "" {
conditions = append(conditions, "created_at <= ?")
conditions = append(conditions, "(UNIX_TIMESTAMP(created_at) * 1000) <= ?")
params = append(params, querys.EndTime)
}
conditions = append(conditions, "kpi_values != 'null'")
whereSql := ""
if len(conditions) > 0 {
@@ -106,13 +107,14 @@ func (k *KpiCReport) GetReport2FE(c *gin.Context) {
return
}
if querys.StartTime != "" {
conditions = append(conditions, "created_at >= ?")
conditions = append(conditions, "(UNIX_TIMESTAMP(created_at) * 1000) >= ?")
params = append(params, querys.StartTime)
}
if querys.EndTime != "" {
conditions = append(conditions, "created_at <= ?")
conditions = append(conditions, "(UNIX_TIMESTAMP(created_at) * 1000) <= ?")
params = append(params, querys.EndTime)
}
conditions = append(conditions, "kpi_values != 'null'")
whereSql := ""
if len(conditions) > 0 {
@@ -185,13 +187,14 @@ func (k *KpiCReport) GetTotalList(c *gin.Context) {
dbg := dborm.DefaultDB().Table(tableName)
if querys.StartTime != "" {
conditions = append(conditions, "created_at >= ?")
conditions = append(conditions, "(UNIX_TIMESTAMP(created_at) * 1000) >= ?")
params = append(params, querys.StartTime)
}
if querys.EndTime != "" {
conditions = append(conditions, "created_at <= ?")
conditions = append(conditions, "(UNIX_TIMESTAMP(created_at) * 1000) <= ?")
params = append(params, querys.EndTime)
}
conditions = append(conditions, "kpi_values != 'null'")
whereSql := ""
if len(conditions) > 0 {
@@ -253,13 +256,14 @@ func (k *KpiCReport) Total(c *gin.Context) {
dbg := dborm.DefaultDB().Table(tableName)
if querys.StartTime != "" {
conditions = append(conditions, "created_at >= ?")
conditions = append(conditions, "(UNIX_TIMESTAMP(created_at) * 1000) >= ?")
params = append(params, querys.StartTime)
}
if querys.EndTime != "" {
conditions = append(conditions, "created_at <= ?")
conditions = append(conditions, "(UNIX_TIMESTAMP(created_at) * 1000) <= ?")
params = append(params, querys.EndTime)
}
conditions = append(conditions, "kpi_values != 'null'")
whereSql := ""
if len(conditions) > 0 {

View File

@@ -3,17 +3,23 @@ package kpi_c_title
import (
"fmt"
"net/http"
"regexp"
"strconv"
"strings"
"be.ems/lib/dborm"
"be.ems/lib/log"
"be.ems/lib/services"
"be.ems/src/framework/utils/ctx"
"github.com/gin-gonic/gin"
)
// get customize kpi total and list
func (k *KpiCTitle) GetToalList(c *gin.Context) {
var titles []KpiCTitle
var conditions []string
var params []any
i18n := ctx.AcceptLanguage(c)
var querys KpiCTitleQuery
if err := c.ShouldBindQuery(&querys); err != nil {
@@ -30,7 +36,10 @@ func (k *KpiCTitle) GetToalList(c *gin.Context) {
if status := querys.Status; status != "" {
conditions = append(conditions, "status = ?")
params = append(params, status)
} else {
conditions = append(conditions, "status != 'Deleted'")
}
whereSql := ""
if len(conditions) > 0 {
whereSql += strings.Join(conditions, " and ")
@@ -62,6 +71,8 @@ func (k *KpiCTitle) GetToalList(c *gin.Context) {
return
}
k.expressionAlias(titles, i18n)
c.JSON(http.StatusOK, services.TotalDataResp(titles, total))
//c.JSON(http.StatusOK, titles)
}
@@ -70,6 +81,7 @@ func (k *KpiCTitle) Get(c *gin.Context) {
var titles []KpiCTitle
var conditions []string
var params []any
i18n := ctx.AcceptLanguage(c)
// construct condition to get
if neType := c.Query("neType"); neType != "" {
@@ -79,7 +91,10 @@ func (k *KpiCTitle) Get(c *gin.Context) {
if status := c.Query("status"); status != "" {
conditions = append(conditions, "status = ?")
params = append(params, status)
} else {
conditions = append(conditions, "status != 'Deleted'")
}
whereSql := ""
if len(conditions) > 0 {
whereSql += strings.Join(conditions, " and ")
@@ -89,10 +104,38 @@ func (k *KpiCTitle) Get(c *gin.Context) {
return
}
k.expressionAlias(titles, i18n)
c.JSON(http.StatusOK, services.DataResp(titles))
//c.JSON(http.StatusOK, titles)
}
// alias customized kpi expression with cn/en title
func (k *KpiCTitle) expressionAlias(titles []KpiCTitle, i18n string) {
var title *KpiCTitle
for i := 0; i < len(titles); i++ {
title = &titles[i]
title.ExprAlias = *title.Expression
re := regexp.MustCompile(`'([^']+)'`)
matches := re.FindAllStringSubmatch(title.ExprAlias, -1)
for _, match := range matches {
var alias, sql string
if i18n == "zh" {
sql = fmt.Sprintf("SELECT cn_title FROM kpi_title WHERE kpi_id='%s'", match[1])
} else {
sql = fmt.Sprintf("SELECT en_title FROM kpi_title WHERE kpi_id='%s'", match[1])
}
err := dborm.XCoreDB().QueryRow(sql).Scan(&alias)
if err != nil {
log.Warn("Failed to QueryRow:", err)
continue
}
title.ExprAlias = regexp.MustCompile(match[1]).ReplaceAllString(title.ExprAlias, alias)
}
}
}
func (k *KpiCTitle) Total(c *gin.Context) {
var conditions []string
var params []any
@@ -105,7 +148,10 @@ func (k *KpiCTitle) Total(c *gin.Context) {
if status := c.Query("status"); status != "" {
conditions = append(conditions, "status = ?")
params = append(params, status)
} else {
conditions = append(conditions, "status != 'Deleted'")
}
whereSql := ""
if len(conditions) > 0 {
whereSql += strings.Join(conditions, " and ")
@@ -120,17 +166,49 @@ func (k *KpiCTitle) Total(c *gin.Context) {
}
func (k *KpiCTitle) Post(c *gin.Context) {
var title KpiCTitle
var title, res KpiCTitle
if err := c.ShouldBindJSON(&title); err != nil {
c.JSON(http.StatusOK, services.ErrResp(err.Error()))
return
}
userName := ctx.LoginUserToUserName(c)
title.CreatedBy = &userName
result := dborm.DefaultDB().Where("ne_type=? and (kpi_id=? or title=?)", title.NeType, title.KpiID, title.Title).First(&title)
if result.RowsAffected > 0 {
c.JSON(http.StatusOK, services.ErrResp("custom indicator already exist"))
return
}
// Regexp match like AMF.C.01
kpiIDRegexp := "^" + *title.NeType + "\\.C\\.[0-9]{2}$"
ret := dborm.DefaultDB().Table("kpi_c_title").
Where("ne_type=? and kpi_id REGEXP ? ORDER BY kpi_id DESC LIMIT 1", title.NeType, kpiIDRegexp).Scan(&res)
if err := ret.Error; err != nil {
c.JSON(http.StatusOK, services.ErrResp(err.Error()))
return
}
newKpiID := *title.NeType + ".C" + ".01"
if ret.RowsAffected != 0 {
maxKpiID := *res.KpiID
prefix := maxKpiID[:len(maxKpiID)-2]
suffix := maxKpiID[len(maxKpiID)-2:]
suffixInt, err := strconv.Atoi(suffix)
if err != nil {
c.JSON(http.StatusOK, services.ErrResp(err.Error()))
return
}
if suffixInt >= MAX_KPI_C_ID {
err := fmt.Errorf("exceed the max customized KPI ID")
c.JSON(http.StatusOK, services.ErrResp(err.Error()))
return
}
suffixInt++
newSuffix := fmt.Sprintf("%02d", suffixInt)
newKpiID = prefix + newSuffix
}
title.KpiID = &newKpiID
if err := dborm.DefaultDB().Create(&title).Error; err != nil {
c.JSON(http.StatusOK, services.ErrResp(err.Error()))
return
@@ -178,8 +256,8 @@ func (k *KpiCTitle) Put(c *gin.Context) {
func (k *KpiCTitle) Delete(c *gin.Context) {
id := c.Param("id")
if err := dborm.DefaultDB().Delete(&KpiCTitle{}, id).Error; err != nil {
c.JSON(http.StatusOK, services.ErrResp("custom indicator not found"))
if err := dborm.DefaultDB().Table(k.TableName()).Where("id=?", id).Update("status", "Deleted").Error; err != nil {
c.JSON(http.StatusOK, services.ErrResp(err.Error()))
return
}

View File

@@ -2,12 +2,17 @@ package kpi_c_title
import "time"
const (
MAX_KPI_C_ID = 99
)
type KpiCTitle struct {
ID int `gorm:"column:id;primary_key;auto_increment" json:"id"`
NeType *string `gorm:"column:ne_type;default:NULL," json:"neType,omitempty"`
KpiID *string `gorm:"column:kpi_id;default:NULL," json:"kpiId,omitempty"`
Title *string `gorm:"column:title;default:NULL," json:"title,omitempty"`
Expression *string `gorm:"column:expression;default:NULL," json:"expression,omitempty"`
ExprAlias string `gorm:"-" json:"exprAlias"`
Status string `gorm:"column:status;default:'Active'" json:"status"`
Unit *string `gorm:"column:unit" json:"unit,omitempty"`
Description *string `gorm:"column:description;default:NULL," json:"description,omitempty"`

View File

@@ -762,14 +762,14 @@ func GetStateFromNF(w http.ResponseWriter, r *http.Request) {
SetHeaders(map[string]string{"Content-Type": "application/json;charset=UTF-8"}).
Get(requestURI2NF)
if err != nil {
log.Error("Get system state from NF is failed:", err)
log.Error("Fail to get state:", err)
errorMessage := services.ErrorMessage{
ErrorCode: "1", ErrorInfo: "Internal server error, NF connnect refused",
}
result["error"] = errorMessage
SN, Version, _ := dborm.XormGetNEStateInfo(ne.NeType, ne.NeId)
result["serialNum"] = SN
result["version"] = Version
systemState := make(map[string]interface{})
systemState["error"] = errorMessage
result["systemState"] = systemState
} else {
systemState := make(map[string]interface{})
_ = json.Unmarshal(resp.Body(), &systemState)
@@ -786,49 +786,62 @@ func GetStateFromNF(w http.ResponseWriter, r *http.Request) {
response.Data = data
services.ResponseWithJson(w, http.StatusOK, response)
return
}
if neType == "omc" {
} else if neType == "omc" {
emsState := GetEMSState("127.0.0.1")
services.ResponseWithJson(w, http.StatusOK, emsState)
return
}
var neList []dborm.NeInfo
err := dborm.XormGetNeInfoByNeType(neType, &neList)
if err != nil {
log.Error("Failed to dborm.XormGetNeInfoByNeType:", err)
services.ResponseInternalServerError500ProcessError(w, err)
return
}
data := make([]map[string]interface{}, 0)
for _, ne := range neList {
hostUri := fmt.Sprintf("http://%s:%v", ne.Ip, ne.Port)
requestURI2NF := fmt.Sprintf("%s/api/rest/systemManagement/v1/elementType/%s/objectType/systemState",
hostUri, strings.ToLower(ne.NeType))
log.Debug("requestURI2NF:", requestURI2NF)
// only support omc and all elementType
err := fmt.Errorf("only support omc or all elementTypeValue")
log.Error("Fail to get state:", err)
services.ResponseInternalServerError500ProcessError(w, err)
resp, err := client.R().
EnableTrace().
SetHeaders(map[string]string{"User-Agent": config.GetDefaultUserAgent()}).
SetHeaders(map[string]string{"Content-Type": "application/json;charset=UTF-8"}).
Get(requestURI2NF)
if err != nil {
log.Error("Get system state from NF is failed:", err)
} else {
systemState := make(map[string]interface{})
_ = json.Unmarshal(resp.Body(), &systemState)
data = append(data, systemState)
}
}
// var neList []dborm.NeInfo
// err := dborm.XormGetNeInfoByNeType(neType, &neList)
// if err != nil {
// log.Error("Failed to dborm.XormGetNeInfoByNeType:", err)
// services.ResponseInternalServerError500ProcessError(w, err)
// return
// }
// var omcList []dborm.NeInfo
// err = dborm.XormGetNeInfoByNeType("omc", &omcList)
// if err != nil {
// log.Error("Failed to omc ne list:", err)
// services.ResponseInternalServerError500ProcessError(w, err)
// return
// }
// for i, ne := range neList {
if len(data) == 1 {
services.ResponseWithJson(w, http.StatusOK, data[0])
return
}
var response Response
response.Data = data
services.ResponseWithJson(w, http.StatusOK, response)
// }
// data := make([]map[string]interface{}, 0)
// for _, ne := range neList {
// hostUri := fmt.Sprintf("http://%s:%v", ne.Ip, ne.Port)
// requestURI2NF := fmt.Sprintf("%s/api/rest/systemManagement/v1/elementType/%s/objectType/systemState",
// hostUri, strings.ToLower(ne.NeType))
// log.Debug("requestURI2NF:", requestURI2NF)
// resp, err := client.R().
// EnableTrace().
// SetHeaders(map[string]string{"User-Agent": config.GetDefaultUserAgent()}).
// SetHeaders(map[string]string{"Content-Type": "application/json;charset=UTF-8"}).
// Get(requestURI2NF)
// if err != nil {
// log.Error("Get system state from NF is failed:", err)
// } else {
// systemState := make(map[string]interface{})
// _ = json.Unmarshal(resp.Body(), &systemState)
// data = append(data, systemState)
// }
// }
// if len(data) == 1 {
// services.ResponseWithJson(w, http.StatusOK, data[0])
// return
// }
// var response Response
// response.Data = data
// services.ResponseWithJson(w, http.StatusOK, response)
}
// GetStateFromNF 旧函数

View File

@@ -25,7 +25,7 @@ func InitConfig(configFile string) {
// Get 获取配置信息
//
// Get("framework.name")
// Get("server.port")
func Get(key string) any {
return v.Get(key)
}

View File

@@ -5,6 +5,7 @@ import (
"go/ast"
"go/parser"
"go/token"
"math"
"regexp"
"strconv"
"strings"
@@ -29,6 +30,9 @@ func CalcExpr(expr string, paramValues map[string]any) (float64, error) {
// expression to evaluate
result, err := evalExpr(expr)
if math.IsNaN(result) {
return 0.0, err
}
return result, err
}
@@ -87,6 +91,10 @@ func evalNode(node ast.Node) (float64, error) {
case token.MUL:
result = left * right
case token.QUO:
if right == 0 {
return math.NaN(), fmt.Errorf("divisor cannot be zero")
}
result = left / right
}
case *ast.BasicLit:

89
lib/log/syslogger.go.bak Normal file
View File

@@ -0,0 +1,89 @@
//go:build !windows && !nacl && !plan9
// +build !windows,!nacl,!plan9
package log
import (
"fmt"
"log/syslog"
)
var _ Logger = &SyslogLogger{}
// SyslogLogger will be depricated
type SyslogLogger struct {
w *syslog.Writer
}
// NewSyslogLogger implements Logger
func NewSyslogLogger(w *syslog.Writer) *SyslogLogger {
return &SyslogLogger{w: w}
}
// Trace log content as Trace
func (s *SyslogLogger) Trace(v ...interface{}) {
_ = s.w.Trace(fmt.Sprint(v...))
}
// Tracef log content as Trace and format
func (s *SyslogLogger) Tracef(format string, v ...interface{}) {
_ = s.w.Trace(fmt.Sprintf(format, v...))
}
// Debug log content as Debug
func (s *SyslogLogger) Debug(v ...interface{}) {
_ = s.w.Debug(fmt.Sprint(v...))
}
// Debugf log content as Debug and format
func (s *SyslogLogger) Debugf(format string, v ...interface{}) {
_ = s.w.Debug(fmt.Sprintf(format, v...))
}
// Error log content as Error
func (s *SyslogLogger) Error(v ...interface{}) {
_ = s.w.Err(fmt.Sprint(v...))
}
// Errorf log content as Errorf and format
func (s *SyslogLogger) Errorf(format string, v ...interface{}) {
_ = s.w.Err(fmt.Sprintf(format, v...))
}
// Info log content as Info
func (s *SyslogLogger) Info(v ...interface{}) {
_ = s.w.Info(fmt.Sprint(v...))
}
// Infof log content as Infof and format
func (s *SyslogLogger) Infof(format string, v ...interface{}) {
_ = s.w.Info(fmt.Sprintf(format, v...))
}
// Warn log content as Warn
func (s *SyslogLogger) Warn(v ...interface{}) {
_ = s.w.Warn(fmt.Sprint(v...))
}
// Warnf log content as Warnf and format
func (s *SyslogLogger) Warnf(format string, v ...interface{}) {
_ = s.w.Warn(fmt.Sprintf(format, v...))
}
// Fatal log content as Fatal
func (s *SyslogLogger) Fatal(v ...interface{}) {
_ = s.w.Fatal(fmt.Sprint(v...))
}
// Fatalf log content as Fatalf and format
func (s *SyslogLogger) Fatalf(format string, v ...interface{}) {
_ = s.w.Fatal(fmt.Sprintf(format, v...))
}
// Level shows log level
func (s *SyslogLogger) Level() LogLevel {
return LOG_NODEF
}
// SetLevel always return error, as current log/syslog package doesn't allow to set priority level after syslog.Writer created
func (s *SyslogLogger) SetLevel(l LogLevel) {}

View File

@@ -1,7 +1,7 @@
# Makefile for rest agent project
PROJECT = OMC
VERSION = 2.2410.4
VERSION = 2.2412.1
PLATFORM = amd64
ARMPLATFORM = aarch64
BUILDDIR = ../../build

View File

@@ -142,7 +142,7 @@ func RunTime() time.Time {
// Get 获取配置信息
//
// Get("framework.name")
// Get("server.port")
func Get(key string) any {
return viper.Get(key)
}

View File

@@ -1,8 +1,3 @@
# 项目信息
framework:
name: "OMC"
version: "2.2410.4"
# 应用服务配置
server:
# 服务端口
@@ -181,6 +176,10 @@ aes:
# 用户配置
user:
# 登录认证,默认打开
loginAuth: true
# 接口加密,默认打开
cryptoApi: true
# 密码
password:
# 密码最大错误次数

View File

@@ -117,6 +117,9 @@ func DefaultDB() *gorm.DB {
// 获取数据源
func DB(source string) *gorm.DB {
if source == "" {
source = config.Get("gorm.defaultDataSourceName").(string)
}
return dbMap[source]
}

View File

@@ -24,6 +24,16 @@ import (
// 请将中间件放在最前置,对请求优先处理
func CryptoApi(requestDecrypt, responseEncrypt bool) gin.HandlerFunc {
return func(c *gin.Context) {
// 登录认证,默认打开
enable := true
if v := config.Get("user.cryptoApi"); v != nil && enable {
enable = v.(bool)
}
if !enable {
c.Next()
return
}
// 请求解密时对请求data注入
if requestDecrypt {
method := c.Request.Method

View File

@@ -3,6 +3,7 @@ package middleware
import (
"strings"
"be.ems/src/framework/config"
AdminConstants "be.ems/src/framework/constants/admin"
commonConstants "be.ems/src/framework/constants/common"
"be.ems/src/framework/i18n"
@@ -36,6 +37,22 @@ var URL_WHITE_LIST = []string{
// 同时匹配其中权限 "matchPerms": {"xxx"},
func PreAuthorize(options map[string][]string) gin.HandlerFunc {
return func(c *gin.Context) {
// 登录认证,默认打开
enable := true
if v := config.Get("user.loginAuth"); v != nil {
enable = v.(bool)
}
if !enable {
loginUser, _ := ctxUtils.LoginUser(c)
loginUser.UserID = "2"
loginUser.User.UserID = "2"
loginUser.User.UserName = "admin"
loginUser.User.NickName = "admin"
c.Set(commonConstants.CTX_LOGIN_USER, loginUser)
c.Next()
return
}
language := ctxUtils.AcceptLanguage(c)
requestURI := c.Request.RequestURI

View File

@@ -59,3 +59,21 @@ func (c *ConnRedis) Close() {
c.Client.Close()
}
}
// RunCMD 执行单次命令 "GET key"
func (c *ConnRedis) RunCMD(cmd string) (any, error) {
if c.Client == nil {
return "", fmt.Errorf("redis client not connected")
}
// 写入命令
cmdArr := strings.Fields(cmd)
if len(cmdArr) == 0 {
return "", fmt.Errorf("redis command is empty")
}
conn := *c.Client
args := make([]any, 0)
for _, v := range cmdArr {
args = append(args, v)
}
return conn.Do(context.Background(), args...).Result()
}

View File

@@ -4,6 +4,7 @@ import (
"context"
"fmt"
"strings"
"sync"
"time"
"be.ems/src/framework/config"
@@ -179,31 +180,22 @@ func GetExpire(source string, key string) (float64, error) {
}
// 获得缓存数据的key列表
func GetKeys(source string, pattern string) ([]string, error) {
func GetKeys(source string, match string) ([]string, error) {
// 数据源
rdb := DefaultRDB()
if source != "" {
rdb = RDB(source)
}
// 初始化变量
var keys []string
var cursor uint64 = 0
keys := make([]string, 0)
ctx := context.Background()
// 循环遍历获取匹配的键
for {
// 使用 SCAN 命令获取匹配的键
batchKeys, nextCursor, err := rdb.Scan(ctx, cursor, pattern, 1000).Result()
if err != nil {
logger.Errorf("Failed to scan keys: %v", err)
return keys, err
}
cursor = nextCursor
keys = append(keys, batchKeys...)
// 当 cursor 为 0表示遍历完成
if cursor == 0 {
break
}
iter := rdb.Scan(ctx, 0, match, 1000).Iterator()
if err := iter.Err(); err != nil {
logger.Errorf("Failed to scan keys: %v", err)
return keys, err
}
for iter.Next(ctx) {
keys = append(keys, iter.Val())
}
return keys, nil
}
@@ -261,6 +253,89 @@ func GetHash(source, key string) (map[string]string, error) {
return value, nil
}
// 批量获得缓存数据 [key]result
func GetHashBatch(source string, keys []string) (map[string]map[string]string, error) {
result := make(map[string]map[string]string, 0)
if len(keys) == 0 {
return result, fmt.Errorf("not keys")
}
// 数据源
rdb := DefaultRDB()
if source != "" {
rdb = RDB(source)
}
// 创建一个有限的并发控制信号通道
sem := make(chan struct{}, 10)
var wg sync.WaitGroup
var mt sync.Mutex
batchSize := 1000
total := len(keys)
if total < batchSize {
batchSize = total
}
for i := 0; i < total; i += batchSize {
wg.Add(1)
go func(start int) {
ctx := context.Background()
// 并发控制,限制同时执行的 Goroutine 数量
sem <- struct{}{}
defer func() {
<-sem
ctx.Done()
wg.Done()
}()
// 检查索引是否越界
end := start + batchSize
if end > total {
end = total
}
pipe := rdb.Pipeline()
for _, key := range keys[start:end] {
pipe.HGetAll(ctx, key)
}
cmds, err := pipe.Exec(ctx)
if err != nil {
logger.Errorf("Failed to get hash batch exec err: %v", err)
return
}
// 将结果添加到 result map 并发访问
mt.Lock()
defer mt.Unlock()
// 处理命令结果
for _, cmd := range cmds {
if cmd.Err() != nil {
logger.Errorf("Failed to get hash batch cmds err: %v", cmd.Err())
continue
}
// 将结果转换为 *redis.StringStringMapCmd 类型
rcmd, ok := cmd.(*redis.MapStringStringCmd)
if !ok {
logger.Errorf("Failed to get hash batch type err: %v", cmd.Err())
continue
}
key := "-"
args := rcmd.Args()
if len(args) > 0 {
key = fmt.Sprint(args[1])
}
result[key] = rcmd.Val()
}
}(i)
}
wg.Wait()
return result, nil
}
// 判断是否存在
func Has(source string, keys ...string) (bool, error) {
// 数据源

View File

@@ -3,7 +3,7 @@ package controller
import (
"fmt"
"be.ems/src/framework/config"
libGlobal "be.ems/lib/global"
"be.ems/src/framework/vo/result"
"github.com/gin-gonic/gin"
@@ -21,8 +21,8 @@ type IndexController struct{}
//
// GET /
func (s *IndexController) Handler(c *gin.Context) {
name := config.Get("framework.name").(string)
version := config.Get("framework.version").(string)
name := "OMC"
version := libGlobal.Version
// str := "欢迎使用%s核心网管理平台当前版本%s请通过前台地址访问。"
str := "Welcome to the %s Core Network Management Platform, current version: %s, please access via the frontend address."
c.JSON(200, result.OkMsg(fmt.Sprintf(str, name, version)))

View File

@@ -30,7 +30,6 @@ func (s *CommontImpl) SystemConfigInfo() map[string]string {
// 获取打包注入的全局变量信息
infoMap["version"] = global.Version
infoMap["buildTime"] = global.BuildTime
infoMap["goVer"] = global.GoVer
// 系统首次使用标记
launchInfo := machine.LaunchInfo
if launchInfo != nil {
@@ -42,6 +41,8 @@ func (s *CommontImpl) SystemConfigInfo() map[string]string {
} else {
infoMap[common.LAUNCH_BOOTLOADER] = "true"
}
// 用户登录认证
infoMap["loginAuth"] = fmt.Sprint(config.Get("user.loginAuth"))
// 序列号
infoMap["serialNum"] = fmt.Sprint(config.Get("omc.sn"))
// 获取LOGO类型

View File

@@ -55,8 +55,8 @@ func (s *UDMAuthController) ResetData(c *gin.Context) {
// GET /list
func (s *UDMAuthController) List(c *gin.Context) {
querys := ctx.QueryMap(c)
data := s.udmAuthService.SelectPage(querys)
c.JSON(200, result.Ok(data))
total, rows := s.udmAuthService.SelectPage(querys)
c.JSON(200, result.Ok(map[string]any{"total": total, "rows": rows}))
}
// UDM鉴权用户信息
@@ -364,13 +364,12 @@ func (s *UDMAuthController) Export(c *gin.Context) {
querys["pageNum"] = 1
querys["pageSize"] = 10000
data := s.udmAuthService.SelectPage(querys)
if parse.Number(data["total"]) == 0 {
total, rows := s.udmAuthService.SelectPage(querys)
if total == 0 {
// 导出数据记录为空
c.JSON(200, result.ErrMsg(i18n.TKey(language, "app.common.exportEmpty")))
return
}
rows := data["rows"].([]model.UDMAuthUser)
// rows := s.udmAuthService.SelectList(model.UDMAuthUser{NeId: neId})
if len(rows) <= 0 {

View File

@@ -54,8 +54,8 @@ func (s *UDMSubController) ResetData(c *gin.Context) {
// GET /list
func (s *UDMSubController) List(c *gin.Context) {
querys := ctx.QueryMap(c)
data := s.udmSubService.SelectPage(querys)
c.JSON(200, result.Ok(data))
total, rows := s.udmSubService.SelectPage(querys)
c.JSON(200, result.Ok(map[string]any{"total": total, "rows": rows}))
}
// UDM签约用户信息
@@ -188,10 +188,9 @@ func (s *UDMSubController) Adds(c *gin.Context) {
// 发送MML
cmd := fmt.Sprintf("baa udmuser:start_imsi=%s,start_msisdn=%s,sub_num=%s,", body.IMSI, body.MSISDN, num)
cmd += s.udmSubService.ParseCommandParams(body)
// static_ip指给4G UE分配的静态IP没有可不带此字段名批量添加IP会自动递增
if body.StaticIp != "" {
cmd += fmt.Sprintf(",static_ip=%s", body.StaticIp)
}
// 去除msisdn参数避免重复
omemsisdn := fmt.Sprintf(",msisdn=%s,", body.MSISDN)
cmd = strings.Replace(cmd, omemsisdn, ",", 1)
data, err := telnet.ConvertToStr(telnetClient, cmd)
if err != nil {
c.JSON(200, result.ErrMsg(err.Error()))
@@ -369,13 +368,12 @@ func (s *UDMSubController) Export(c *gin.Context) {
querys["pageNum"] = 1
querys["pageSize"] = 10000
data := s.udmSubService.SelectPage(querys)
if parse.Number(data["total"]) == 0 {
total, rows := s.udmSubService.SelectPage(querys)
if total == 0 {
// 导出数据记录为空
c.JSON(200, result.ErrMsg(i18n.TKey(language, "app.common.exportEmpty")))
return
}
rows := data["rows"].([]model.UDMSubUser)
// rows := s.udmSubService.SelectList(model.UDMSubUser{NeId: neId})
if len(rows) <= 0 {

View File

@@ -7,7 +7,7 @@ type CDREventIMS struct {
ID string `json:"id" gorm:"column:id;primaryKey;autoIncrement"`
NeType string `json:"neType" gorm:"column:ne_type"`
NeName string `json:"neName" gorm:"column:ne_name"`
RmUID string `json:"rmUID" gorm:"column:rm_uid"` // 可能没有
RmUID string `json:"rmUID" gorm:"column:rm_uid"`
Timestamp int64 `json:"timestamp" gorm:"column:timestamp"`
CDRJSONStr string `json:"cdrJSON" gorm:"column:cdr_json"`
CreatedAt time.Time `json:"createdAt" gorm:"column:created_at;default:CURRENT_TIMESTAMP"`
@@ -18,7 +18,7 @@ type CDREventIMSQuery struct {
NeType string `json:"neType" form:"neType" binding:"required"` // 网元类型IMS
NeID string `json:"neId" form:"neId" binding:"required"`
RmUID string `json:"rmUID" form:"rmUID"`
RecordType string `json:"recordType" form:"recordType"` // 记录行为 MOC MTC MOSM MTSM
RecordType string `json:"recordType" form:"recordType"` // 记录行为 MOC MTC
CallerParty string `json:"callerParty" form:"callerParty"` // 主叫号码
CalledParty string `json:"calledParty" form:"calledParty"` // 被叫号码
StartTime string `json:"startTime" form:"startTime"`

View File

@@ -1,120 +1,57 @@
package repository
import (
"fmt"
"strings"
"be.ems/src/framework/datasource"
"be.ems/src/framework/logger"
"be.ems/src/framework/utils/parse"
"be.ems/src/framework/utils/repo"
"be.ems/src/modules/network_data/model"
)
// 实例化数据层 UDMAuthUser 结构体
var NewUDMAuthUser = &UDMAuthUser{
selectSql: `select id, imsi, ne_id, amf, status, ki, algo_index, opc from u_auth_user`,
resultMap: map[string]string{
"id": "ID",
"imsi": "IMSI",
"ne_id": "NeId",
"amf": "Amf",
"status": "Status",
"ki": "Ki",
"algo_index": "AlgoIndex",
"opc": "Opc",
},
}
var NewUDMAuthUser = &UDMAuthUser{}
// UDMAuthUser UDM鉴权信息表 数据层处理
type UDMAuthUser struct {
// 查询视图对象SQL
selectSql string
// 结果字段与实体映射
resultMap map[string]string
}
// convertResultRows 将结果记录转实体结果组
func (r *UDMAuthUser) convertResultRows(rows []map[string]any) []model.UDMAuthUser {
arr := make([]model.UDMAuthUser, 0)
for _, row := range rows {
item := model.UDMAuthUser{}
for key, value := range row {
if keyMapper, ok := r.resultMap[key]; ok {
repo.SetFieldValue(&item, keyMapper, value)
}
}
arr = append(arr, item)
}
return arr
}
type UDMAuthUser struct{}
// ClearAndInsert 清空ne_id后新增实体
func (r *UDMAuthUser) ClearAndInsert(neId string, uArr []model.UDMAuthUser) int64 {
// 不指定neID时用 TRUNCATE 清空表快
// _, err := datasource.ExecDB("", "TRUNCATE TABLE u_auth_user", nil)
_, err := datasource.ExecDB("", "DELETE FROM u_auth_user WHERE ne_id = ?", []any{neId})
if err != nil {
logger.Errorf("TRUNCATE err => %v", err)
result := datasource.DB("").Where("ne_id = ?", neId).Unscoped().Delete(&model.UDMAuthUser{})
if result.Error != nil {
logger.Errorf("Delete err => %v", result.Error)
}
return r.Inserts(uArr)
}
// SelectPage 根据条件分页查询
func (r *UDMAuthUser) SelectPage(query map[string]any) map[string]any {
func (r *UDMAuthUser) SelectPage(query map[string]any) (int64, []model.UDMAuthUser) {
tx := datasource.DB("").Model(&model.UDMAuthUser{})
// 查询条件拼接
var conditions []string
var params []any
if v, ok := query["imsi"]; ok && v != "" {
conditions = append(conditions, "imsi like concat(concat('%', ?), '%')")
params = append(params, strings.Trim(v.(string), " "))
tx = tx.Where("imsi like concat(concat('%',?), '%')", v)
}
if v, ok := query["neId"]; ok && v != "" {
conditions = append(conditions, "ne_id = ?")
params = append(params, v)
tx = tx.Where("ne_id =?", v)
}
if v, ok := query["imsis"]; ok && v != "" {
placeholder := repo.KeyPlaceholderByQuery(len(v.([]any)))
conditions = append(conditions, fmt.Sprintf("imsi in (%s)", placeholder))
for _, v := range v.([]any) {
params = append(params, v.(string))
}
tx = tx.Where("imsi in ?", v)
}
// 构建查询条件语句
whereSql := ""
if len(conditions) > 0 {
whereSql += " where " + strings.Join(conditions, " and ")
}
result := map[string]any{
"total": 0,
"rows": []model.UDMAuthUser{},
}
var total int64 = 0
rows := []model.UDMAuthUser{}
// 查询数量 长度为0直接返回
totalSql := "select count(1) as 'total' from u_auth_user"
totalRows, err := datasource.RawDB("", totalSql+whereSql, params)
if err != nil {
if err := tx.Count(&total).Error; err != nil || total <= 0 {
logger.Errorf("total err => %v", err)
return result
}
total := parse.Number(totalRows[0]["total"])
if total == 0 {
return result
} else {
result["total"] = total
return total, rows
}
// 分页
pageNum, pageSize := repo.PageNumSize(query["pageNum"], query["pageSize"])
pageSql := " limit ?,? "
params = append(params, pageNum*pageSize)
params = append(params, pageSize)
tx = tx.Offset(int(pageNum * pageSize)).Limit(int(pageSize))
// 排序
orderSql := ""
if v, ok := query["sortField"]; ok && v != "" {
sortSql := v.(string)
if o, ok := query["sortOrder"]; ok && o != nil && v != "" {
@@ -124,71 +61,52 @@ func (r *UDMAuthUser) SelectPage(query map[string]any) map[string]any {
sortSql += " asc "
}
}
orderSql = fmt.Sprintf(" order by %s ", sortSql)
tx = tx.Order(sortSql)
}
// 查询数据
querySql := r.selectSql + whereSql + orderSql + pageSql
results, err := datasource.RawDB("", querySql, params)
if err != nil {
if err := tx.Find(&rows).Error; err != nil {
logger.Errorf("query err => %v", err)
}
// 转换实体
result["rows"] = r.convertResultRows(results)
return result
return total, rows
}
// SelectList 根据实体查询
func (r *UDMAuthUser) SelectList(u model.UDMAuthUser) []model.UDMAuthUser {
tx := datasource.DB("").Model(&model.UDMAuthUser{})
// 查询条件拼接
var conditions []string
var params []any
if u.IMSI != "" {
conditions = append(conditions, "imsi = ?")
params = append(params, u.IMSI)
tx = tx.Where("imsi = ?", u.IMSI)
}
if u.NeId != "" {
conditions = append(conditions, "ne_id = ?")
params = append(params, u.NeId)
}
// 构建查询条件语句
whereSql := ""
if len(conditions) > 0 {
whereSql += " where " + strings.Join(conditions, " and ")
tx = tx.Where("ne_id = ?", u.NeId)
}
// 查询数据
querySql := r.selectSql + whereSql + " order by imsi asc "
results, err := datasource.RawDB("", querySql, params)
if err != nil {
arr := []model.UDMAuthUser{}
if err := tx.Order("imsi asc").Find(&arr).Error; err != nil {
logger.Errorf("query err => %v", err)
}
// 转换实体
return r.convertResultRows(results)
return arr
}
// SelectByIMSIAndNeID 通过imsi和ne_id查询
func (r *UDMAuthUser) SelectByIMSIAndNeID(imsi, neId string) model.UDMAuthUser {
querySql := r.selectSql + " where imsi = ? and ne_id = ?"
results, err := datasource.RawDB("", querySql, []any{imsi, neId})
if err != nil {
tx := datasource.DB("").Model(&model.UDMAuthUser{})
item := model.UDMAuthUser{}
// 查询条件拼接
tx = tx.Where("imsi = ? and ne_id = ?", imsi, neId)
// 查询数据
if err := tx.Order("imsi asc").Limit(1).Find(&item).Error; err != nil {
logger.Errorf("query err => %v", err)
return model.UDMAuthUser{}
}
// 转换实体
rows := r.convertResultRows(results)
if len(rows) > 0 {
return rows[0]
}
return model.UDMAuthUser{}
return item
}
// Insert 批量添加
func (r *UDMAuthUser) Inserts(uArr []model.UDMAuthUser) int64 {
tx := datasource.DefaultDB().CreateInBatches(uArr, 3000)
tx := datasource.DB("").CreateInBatches(uArr, 3000)
if err := tx.Error; err != nil {
logger.Errorf("CreateInBatches err => %v", err)
}

View File

@@ -1,156 +1,60 @@
package repository
import (
"fmt"
"strings"
"be.ems/src/framework/datasource"
"be.ems/src/framework/logger"
"be.ems/src/framework/utils/parse"
"be.ems/src/framework/utils/repo"
"be.ems/src/modules/network_data/model"
)
// 实例化数据层 UDMSubUser 结构体
var NewUDMSub = &UDMSubUser{
selectSql: `select
id, imsi, msisdn, ne_id,
am_dat, ambr, nssai, rat, arfb, sar, cn_type, rfsp_index, reg_timer, ue_usage_type, active_time, mico, odb_ps, group_id,
eps_dat, eps_flag, eps_odb, hplmn_odb, ard, epstpl, context_id, apn_mum, apn_context, static_ip,
sm_data, smf_sel, cag
from u_sub_user`,
resultMap: map[string]string{
"id": "ID",
"imsi": "IMSI",
"msisdn": "MSISDN",
"ne_id": "NeId",
"am_dat": "AmDat",
"ambr": "UeAmbrTpl",
"nssai": "NssaiTpl",
"rat": "RatRestrictions",
"arfb": "AreaForbiddenTpl",
"sar": "ServiceAreaRestrictionTpl",
"cn_type": "CnTypeRestrictions",
"rfsp_index": "RfspIndex",
"reg_timer": "SubsRegTime",
"ue_usage_type": "UeUsageType",
"active_time": "ActiveTime",
"mico": "MicoAllowed",
"odb_ps": "OdbPs",
"group_id": "GroupId",
"eps_dat": "EpsDat",
"eps_flag": "EpsFlag",
"eps_odb": "EpsOdb",
"hplmn_odb": "HplmnOdb",
"ard": "Ard",
"epstpl": "Epstpl",
"context_id": "ContextId",
"apn_mum": "ApnNum",
"apn_context": "ApnContext",
"static_ip": "StaticIp",
"sm_data": "SmData",
"smf_sel": "SmfSel",
"cag": "Cag",
},
}
var NewUDMSub = &UDMSubUser{}
// UDMSubUser UDM签约信息表 数据层处理
type UDMSubUser struct {
// 查询视图对象SQL
selectSql string
// 结果字段与实体映射
resultMap map[string]string
}
// convertResultRows 将结果记录转实体结果组
func (r *UDMSubUser) convertResultRows(rows []map[string]any) []model.UDMSubUser {
arr := make([]model.UDMSubUser, 0)
for _, row := range rows {
item := model.UDMSubUser{}
for key, value := range row {
if keyMapper, ok := r.resultMap[key]; ok {
repo.SetFieldValue(&item, keyMapper, value)
}
}
arr = append(arr, item)
}
return arr
}
type UDMSubUser struct{}
// ClearAndInsert 清空ne_id后新增实体
func (r *UDMSubUser) ClearAndInsert(neId string, u []model.UDMSubUser) int64 {
// 不指定neID时用 TRUNCATE 清空表快
// _, err := datasource.ExecDB("", "TRUNCATE TABLE u_sub_user", nil)
_, err := datasource.ExecDB("", "DELETE FROM u_sub_user WHERE ne_id = ?", []any{neId})
if err != nil {
logger.Errorf("TRUNCATE err => %v", err)
result := datasource.DB("").Where("ne_id = ?", neId).Unscoped().Delete(&model.UDMSubUser{})
if result.Error != nil {
logger.Errorf("Delete err => %v", result.Error)
}
return r.Inserts(u)
}
// SelectPage 根据条件分页查询字典类型
func (r *UDMSubUser) SelectPage(query map[string]any) map[string]any {
func (r *UDMSubUser) SelectPage(query map[string]any) (int64, []model.UDMSubUser) {
tx := datasource.DB("").Model(&model.UDMSubUser{})
// 查询条件拼接
var conditions []string
var params []any
if v, ok := query["imsi"]; ok && v != "" {
conditions = append(conditions, "imsi like concat(concat('%', ?), '%')")
params = append(params, strings.Trim(v.(string), " "))
tx = tx.Where("imsi like concat(concat('%', ?), '%')", v)
}
if v, ok := query["msisdn"]; ok && v != "" {
conditions = append(conditions, "msisdn like concat(concat('%', ?), '%')")
params = append(params, strings.Trim(v.(string), " "))
tx = tx.Where("msisdn like concat(concat('%', ?), '%')", v)
}
if v, ok := query["neId"]; ok && v != "" {
conditions = append(conditions, "ne_id = ?")
params = append(params, v)
tx = tx.Where("ne_id =?", v)
}
if v, ok := query["imsis"]; ok && v != "" {
placeholder := repo.KeyPlaceholderByQuery(len(v.([]any)))
conditions = append(conditions, fmt.Sprintf("imsi in (%s)", placeholder))
for _, v := range v.([]any) {
params = append(params, v.(string))
}
tx = tx.Where("imsi in ?", v)
}
// 构建查询条件语句
whereSql := ""
if len(conditions) > 0 {
whereSql += " where " + strings.Join(conditions, " and ")
}
result := map[string]any{
"total": 0,
"rows": []model.UDMSubUser{},
}
var total int64 = 0
rows := []model.UDMSubUser{}
// 查询数量 长度为0直接返回
totalSql := "select count(1) as 'total' from u_sub_user"
totalRows, err := datasource.RawDB("", totalSql+whereSql, params)
if err != nil {
if err := tx.Count(&total).Error; err != nil || total <= 0 {
logger.Errorf("total err => %v", err)
return result
}
total := parse.Number(totalRows[0]["total"])
if total == 0 {
return result
} else {
result["total"] = total
return total, rows
}
// 分页
pageNum, pageSize := repo.PageNumSize(query["pageNum"], query["pageSize"])
pageSql := " limit ?,? "
params = append(params, pageNum*pageSize)
params = append(params, pageSize)
tx = tx.Offset(int(pageNum * pageSize)).Limit(int(pageSize))
// 排序
orderSql := ""
if v, ok := query["sortField"]; ok && v != "" {
sortSql := v.(string)
if o, ok := query["sortOrder"]; ok && o != nil && v != "" {
@@ -160,72 +64,52 @@ func (r *UDMSubUser) SelectPage(query map[string]any) map[string]any {
sortSql += " asc "
}
}
orderSql = fmt.Sprintf(" order by %s ", sortSql)
tx = tx.Order(sortSql)
}
// 查询数据
querySql := r.selectSql + whereSql + orderSql + pageSql
results, err := datasource.RawDB("", querySql, params)
if err != nil {
if err := tx.Find(&rows).Error; err != nil {
logger.Errorf("query err => %v", err)
return result
}
// 转换实体
result["rows"] = r.convertResultRows(results)
return result
return total, rows
}
// SelectList 根据实体查询
func (r *UDMSubUser) SelectList(u model.UDMSubUser) []model.UDMSubUser {
tx := datasource.DB("").Model(&model.UDMSubUser{})
// 查询条件拼接
var conditions []string
var params []any
if u.IMSI != "" {
conditions = append(conditions, "imsi = ?")
params = append(params, u.IMSI)
tx = tx.Where("imsi = ?", u.IMSI)
}
if u.NeId != "" {
conditions = append(conditions, "ne_id = ?")
params = append(params, u.NeId)
}
// 构建查询条件语句
whereSql := ""
if len(conditions) > 0 {
whereSql += " where " + strings.Join(conditions, " and ")
tx = tx.Where("ne_id = ?", u.NeId)
}
// 查询数据
querySql := r.selectSql + whereSql + " order by imsi asc "
results, err := datasource.RawDB("", querySql, params)
if err != nil {
arr := []model.UDMSubUser{}
if err := tx.Order("imsi asc").Find(&arr).Error; err != nil {
logger.Errorf("query err => %v", err)
}
// 转换实体
return r.convertResultRows(results)
return arr
}
// SelectByIMSIAndNeID 通过imsi和ne_id查询
func (r *UDMSubUser) SelectByIMSIAndNeID(imsi, neId string) model.UDMSubUser {
querySql := r.selectSql + " where imsi = ? and ne_id = ?"
results, err := datasource.RawDB("", querySql, []any{imsi, neId})
if err != nil {
tx := datasource.DB("").Model(&model.UDMSubUser{})
item := model.UDMSubUser{}
// 查询条件拼接
tx = tx.Where("imsi = ? and ne_id = ?", imsi, neId)
// 查询数据
if err := tx.Order("imsi asc").Limit(1).Find(&item).Error; err != nil {
logger.Errorf("query err => %v", err)
return model.UDMSubUser{}
}
// 转换实体
rows := r.convertResultRows(results)
if len(rows) > 0 {
return rows[0]
}
return model.UDMSubUser{}
return item
}
// Insert 批量添加
func (r *UDMSubUser) Inserts(uArr []model.UDMSubUser) int64 {
tx := datasource.DefaultDB().CreateInBatches(uArr, 2000)
tx := datasource.DB("").CreateInBatches(uArr, 2000)
if err := tx.Error; err != nil {
logger.Errorf("CreateInBatches err => %v", err)
}

View File

@@ -43,14 +43,18 @@ func (r *UDMAuthUser) dataByRedis(imsi, neId string) []model.UDMAuthUser {
if err != nil {
return arr
}
for _, key := range ausfArr {
m, err := redis.GetHash(source, key)
if err != nil {
mkv, err := redis.GetHashBatch(source, ausfArr)
if err != nil {
return arr
}
for k, m := range mkv {
if k == "-" {
continue
}
// 跳过-号数据 ausf:360000100000130
imsi := key[5:]
imsi := k[5:]
if strings.Contains(imsi, "-") {
continue
}
@@ -97,7 +101,7 @@ func (r *UDMAuthUser) ParseInfo(imsi, neId string, data map[string]string) model
}
// SelectPage 分页查询数据库
func (r *UDMAuthUser) SelectPage(query map[string]any) map[string]any {
func (r *UDMAuthUser) SelectPage(query map[string]any) (int64, []model.UDMAuthUser) {
return r.udmAuthRepository.SelectPage(query)
}

View File

@@ -44,14 +44,24 @@ func (r *UDMSubUser) dataByRedis(imsi, neId string) []model.UDMSubUser {
if err != nil {
return arr
}
for _, key := range udmsdArr {
m, err := redis.GetHash(source, key)
if err != nil {
mkv, err := redis.GetHashBatch(source, udmsdArr)
if err != nil {
return arr
}
for k, m := range mkv {
if k == "-" {
continue
}
// 跳过-号数据 udm-sd:360000100000130
imsi := k[7:]
if strings.Contains(imsi, "-") {
continue
}
a := model.UDMSubUser{
IMSI: key[7:], // udm-sd:360000100000130
IMSI: imsi, // udm-sd:360000100000130
MSISDN: m["gpsi"], // 8612300000130
NeId: neId,
SmfSel: m["smf-sel"], // def_snssai
@@ -170,7 +180,7 @@ func (r *UDMSubUser) ParseInfo(imsi, neId string, data map[string]string) model.
}
// SelectPage 分页查询数据库
func (r *UDMSubUser) SelectPage(query map[string]any) map[string]any {
func (r *UDMSubUser) SelectPage(query map[string]any) (int64, []model.UDMSubUser) {
return r.udmSubRepository.SelectPage(query)
}
@@ -337,6 +347,7 @@ func (r *UDMSubUser) ParseCommandParams(item model.UDMSubUser) string {
if item.ApnContext != "" {
conditions = append(conditions, fmt.Sprintf("apn_context=%s", item.ApnContext))
}
// static_ip指给4G UE分配的静态IP没有可不带此字段名批量添加IP会自动递增
if item.StaticIp != "" {
conditions = append(conditions, fmt.Sprintf("static_ip=%s", item.StaticIp))
}
@@ -348,8 +359,7 @@ func (r *UDMSubUser) ParseCommandParams(item model.UDMSubUser) string {
if item.SmData != "" {
conditions = append(conditions, fmt.Sprintf("sm_data=%s", item.SmData))
}
if item.Cag != "" {
conditions = append(conditions, fmt.Sprintf("cag=%s", item.Cag))
}
conditions = append(conditions, fmt.Sprintf("cag=%s", item.Cag))
return strings.Join(conditions, ",")
}

View File

@@ -199,16 +199,16 @@ func (s *NeConfigController) DataInfo(c *gin.Context) {
}
c.JSON(200, result.OkData(resData))
return
} else {
// 网元直连
resData, err := neFetchlink.NeConfigInfo(neInfo, query.ParamName)
if err != nil {
c.JSON(200, result.ErrMsg(err.Error()))
return
}
c.JSON(200, result.Ok(resData))
}
// 网元直连
resData, err := neFetchlink.NeConfigInfo(neInfo, query.ParamName)
if err != nil {
c.JSON(200, result.ErrMsg(err.Error()))
return
}
c.JSON(200, result.Ok(resData))
}
// 网元参数配置数据修改
@@ -242,15 +242,15 @@ func (s *NeConfigController) DataEdit(c *gin.Context) {
}
c.JSON(200, result.OkData(resData))
return
} else {
// 网元直连
resData, err := neFetchlink.NeConfigUpdate(neInfo, body.ParamName, body.Loc, body.ParamData)
if err != nil {
c.JSON(200, result.ErrMsg(err.Error()))
return
}
c.JSON(200, result.OkData(resData))
}
// 网元直连
resData, err := neFetchlink.NeConfigUpdate(neInfo, body.ParamName, body.Loc, body.ParamData)
if err != nil {
c.JSON(200, result.ErrMsg(err.Error()))
return
}
c.JSON(200, result.OkData(resData))
}
// 网元参数配置数据新增array

View File

@@ -79,6 +79,13 @@ func (s *NeHostController) Add(c *gin.Context) {
return
}
if body.GroupID == "1" {
// 主机信息操作【%s】失败禁止操作网元
msg := i18n.TKey(language, "neHost.banNE")
c.JSON(200, result.ErrMsg(msg))
return
}
// 检查属性值唯一
uniqueHost := s.neHostService.CheckUniqueHostTitle(body.GroupID, body.Title, body.HostType, "")
if !uniqueHost {

View File

@@ -111,8 +111,28 @@ func (r *NeHost) SelectPage(query map[string]any) map[string]any {
params = append(params, pageNum*pageSize)
params = append(params, pageSize)
// 排序
orderSql := ""
if sv, ok := query["sortField"]; ok && sv != "" {
sortSql := fmt.Sprint(sv)
if sortSql == "updateTime" {
sortSql = "update_time"
}
if sortSql == "createTime" {
sortSql = "create_time"
}
if ov, ok := query["sortOrder"]; ok && ov != "" {
if fmt.Sprint(ov) == "desc" {
sortSql += " desc "
} else {
sortSql += " asc "
}
}
orderSql = fmt.Sprintf(" order by %s ", sortSql)
}
// 查询数据
querySql := r.selectSql + whereSql + pageSql
querySql := r.selectSql + whereSql + orderSql + pageSql
results, err := datasource.RawDB("", querySql, params)
if err != nil {
logger.Errorf("query err => %v", err)

View File

@@ -18,6 +18,7 @@ var neListSort = []string{
"IMS",
"AMF",
"AUSF",
"UDR",
"UDM",
"SMF",
"PCF",

View File

@@ -120,6 +120,9 @@ func (r *NeConfigBackup) NeConfigLocalToNe(neInfo model.NeInfo, localFile string
sshClient.RunCMD(fmt.Sprintf("sudo mkdir -p /usr/local/etc/iwf && sudo cp -rf %s/iwf/* /usr/local/etc/iwf && sudo chmod 755 /usr/local/etc/iwf/*.yaml", neDirTemp))
} else if neTypeLower == "omc" {
sshClient.RunCMD(fmt.Sprintf("sudo mkdir -p /usr/local/omc/etc && sudo cp -rf %s/* /usr/local/omc/etc && sudo chmod 755 /usr/local/omc/etc/*.{yaml,conf}", neDirTemp))
} else if neTypeLower == "smsc" {
chmodFile := "sudo chmod 755 /usr/local/etc/smsc/{*sys.conf,*conf.txt,conf/is41_operation.conf}"
sshClient.RunCMD(fmt.Sprintf("sudo mkdir -p /usr/local/etc/smsc/conf && sudo cp -rf %s/* /usr/local/etc/smsc && %s", neDirTemp, chmodFile))
} else {
neEtcPath := fmt.Sprintf("/usr/local/etc/%s", neTypeLower)
chmodFile := fmt.Sprintf("sudo chmod 755 %s/*.yaml", neEtcPath)
@@ -175,6 +178,9 @@ func (r *NeConfigBackup) NeConfigNeToLocal(neInfo model.NeInfo) (string, error)
sshClient.RunCMD(fmt.Sprintf("mkdir -p %s/iwf && sudo cp -rf /usr/local/etc/iwf/*.yaml %s/iwf", neDirTemp, neDirTemp))
} else if neTypeLower == "omc" {
sshClient.RunCMD(fmt.Sprintf("mkdir -p %s && sudo cp -rf /usr/local/omc/etc/*.{yaml,conf} %s", neDirTemp, neDirTemp))
} else if neTypeLower == "smsc" {
sshClient.RunCMD(fmt.Sprintf("mkdir -p %s && sudo cp -rf /usr/local/etc/smsc/{*.yaml,*.conf,*conf.txt} %s", neDirTemp, neDirTemp))
sshClient.RunCMD(fmt.Sprintf("sudo cp -rf /usr/local/etc/smsc/conf %s/conf", neDirTemp))
} else {
nePath := fmt.Sprintf("/usr/local/etc/%s/*.yaml", neTypeLower)
if neTypeLower == "mme" {

View File

@@ -149,6 +149,13 @@ func (r *NeHost) DeleteByIds(hostIds []string) (int64, error) {
return 0, fmt.Errorf("neHost.noData")
}
for _, v := range ids {
if v.GroupID == "1" {
// 主机信息操作【%s】失败禁止操作网元
return 0, fmt.Errorf("neHost.banNE")
}
}
if len(ids) == len(hostIds) {
rows := r.neHostRepository.DeleteByIds(hostIds)
return rows, nil

View File

@@ -79,6 +79,28 @@ func (r *NeInfo) ClearNeCacheByNeType(neType string) bool {
return delOk
}
// SelectNeInfoByNeType 通过ne_type查询网元信息
func (r *NeInfo) SelectNeInfoByNeType(neType string) []model.NeInfo {
neInfo := make([]model.NeInfo, 0)
key := fmt.Sprintf("%s%s:*", cachekey.NE_KEY, strings.ToUpper(neType))
jsonStr, _ := redis.Get("", key)
if len(jsonStr) > 7 {
err := json.Unmarshal([]byte(jsonStr), &neInfo)
if err != nil {
return neInfo
}
} else {
neInfo = r.neInfoRepository.SelectList(model.NeInfo{NeType: neType})
for _, v := range neInfo {
key := fmt.Sprintf("%s%s:%s", cachekey.NE_KEY, strings.ToUpper(v.NeType), v.NeId)
redis.Del("", key)
values, _ := json.Marshal(v)
redis.Set("", key, string(values))
}
}
return neInfo
}
// SelectNeInfoByRmuid 通过rmUID查询网元信息
func (r *NeInfo) SelectNeInfoByRmuid(rmUid string) model.NeInfo {
var neInfo model.NeInfo
@@ -910,22 +932,25 @@ func (r *NeInfo) neConfPara5GDataConvert(content map[string]any) map[string]stri
"DNN_IMS": basic["dnn_ims"].(string),
// external
"N2_IP": external["amfn2_ip"].(string),
"UE_POOL": external["ue_pool"].(string),
"UE_IP": ueIP,
"UE_MASK": ueMask,
"UE_CIDR": ueCicr,
"UPF_TYPE": external["upf_type"].(string), // StandardUPF LightUPF
"N3_IP": n3IP,
"N3_MASK": n3Mask,
"N3_GW": external["upfn3_gw"].(string),
"N3_PCI": external["upfn3_pci"].(string),
"N3_MAC": external["upfn3_mac"].(string),
"N6_IP": n6IP,
"N6_MASK": n6Mask,
"N6_GW": external["upfn6_gw"].(string),
"N6_PCI": external["upfn6_pci"].(string),
"N6_MAC": external["upfn6_mac"].(string),
"N2_IP": external["amfn2_ip"].(string),
"UE_POOL": external["ue_pool"].(string), // 轻量版才用配置
"UE_IP": ueIP,
"UE_MASK": ueMask,
"UE_CIDR": ueCicr,
"UPF_TYPE": external["upf_type"].(string), // 类型 StandardUPF LightUPF
"UPF_DRIVER_TYPE": external["upf_driver_type"].(string), // 网卡驱动 vmxnet3 host dpdk
"N3_IP": n3IP,
"N3_MASK": n3Mask,
"N3_GW": external["upfn3_gw"].(string),
"N3_PCI": external["upfn3_pci"].(string),
"N3_MAC": external["upfn3_mac"].(string),
"N3_NIC_NAME": external["upfn3_card_name"].(string), // 网卡名 eth0
"N6_IP": n6IP,
"N6_MASK": n6Mask,
"N6_GW": external["upfn6_gw"].(string),
"N6_PCI": external["upfn6_pci"].(string),
"N6_MAC": external["upfn6_mac"].(string),
"N6_NIC_NAME": external["upfn6_card_name"].(string), // 网卡名 eth0
"SIP_IP": external["ims_sip_ip"].(string),

View File

@@ -239,7 +239,7 @@ func (r *NeVersion) operateCommand(action, neType string, neFilePaths []string)
pkgCmdStr := fmt.Sprintf("sudo dpkg -i %s", strings.Join(neFilePaths, " "))
fileExt := filepath.Ext(strings.ToLower(neFilePaths[0]))
if strings.HasSuffix(fileExt, "rpm") {
pkgCmdStr = fmt.Sprintf("sudo rpm -Uvh %s", strings.Join(neFilePaths, " "))
pkgCmdStr = fmt.Sprintf("sudo rpm -Uvh --reinstall %s", strings.Join(neFilePaths, " "))
}
// 组合命令输入
@@ -257,7 +257,7 @@ func (r *NeVersion) operateCommand(action, neType string, neFilePaths []string)
// 升级软件包
pkgCmdStr = fmt.Sprintf("sudo M_PARAM=upgrade dpkg -i %s", strings.Join(neFilePaths, " "))
if strings.HasSuffix(fileExt, "rpm") {
pkgCmdStr = fmt.Sprintf("sudo M_PARAM=upgrade rpm -Uvh %s", strings.Join(neFilePaths, " "))
pkgCmdStr = fmt.Sprintf("sudo M_PARAM=upgrade rpm -Uvh --reinstall %s", strings.Join(neFilePaths, " "))
}
omcStrArr = append(omcStrArr, pkgCmdStr)
}
@@ -428,22 +428,35 @@ func (r *NeVersion) operateCommand(action, neType string, neFilePaths []string)
cmdStrArr = append(cmdStrArr, "sudo cp /usr/local/etc/upf/default/upfForwarder_1.yaml /usr/local/etc/upf/upfForwarder_1.yaml \n")
cmdStrArr = append(cmdStrArr, fmt.Sprintf("sudo sed -i \"s/172.16.5.190/%s/g\" /usr/local/etc/upf/upfcfg.yaml \n", para5GData["UPF_IP"]))
cmdStrArr = append(cmdStrArr, fmt.Sprintf("sudo sed -i \"s/localhost/%s/g\" /usr/local/etc/upf/upfForwarder_1.yaml \n", para5GData["UPF_IP"]))
// UE
cmdStrArr = append(cmdStrArr, fmt.Sprintf("sudo sed -i '/- interfaceType: \"N6\"/,/ueIpv4: 10.2.1.0/s/ueIpv4: 10.2.1.0/ueIpv4: %s/' /usr/local/etc/upf/upfForwarder_1.yaml \n", para5GData["UE_IP"]))
cmdStrArr = append(cmdStrArr, fmt.Sprintf("sudo sed -i '/- interfaceType: \"N6\"/,/ueIpv4Mask: 255.255.255.0/s/ueIpv4Mask: 255.255.255.0/ueIpv4Mask: %s/' /usr/local/etc/upf/upfForwarder_1.yaml \n", para5GData["UE_MASK"]))
// N3
cmdStrArr = append(cmdStrArr, fmt.Sprintf("sudo sed -i \"s/192.168.8.190/%s/g\" /usr/local/etc/upf/upfForwarder_1.yaml \n", para5GData["N3_IP"]))
cmdStrArr = append(cmdStrArr, fmt.Sprintf("sudo sed -i '/- interfaceType: \"N3\"/,/ipv4Mask: 255.255.240.0/s/ipv4Mask: 255.255.240.0/ipv4Mask: %s/' /usr/local/etc/upf/upfForwarder_1.yaml \n", para5GData["N3_MASK"]))
cmdStrArr = append(cmdStrArr, fmt.Sprintf("sudo sed -i '/- interfaceType: \"N3\"/,/gatewayIpv4: 192.168.1.254/s/gatewayIpv4: 192.168.1.254/gatewayIpv4: %s/' /usr/local/etc/upf/upfForwarder_1.yaml \n", para5GData["N3_GW"]))
cmdStrArr = append(cmdStrArr, fmt.Sprintf("sudo sed -i '/- interfaceType: \"N3\"/,/interfacePCI: \"0000:00:00.0\"/s/interfacePCI: \"0000:00:00.0\"/interfacePCI: \"%s\"/' /usr/local/etc/upf/upfForwarder_1.yaml \n", para5GData["N3_PCI"]))
cmdStrArr = append(cmdStrArr, fmt.Sprintf("sudo sed -i '/- interfaceType: \"N3\"/,/macAddr: \"00:00:00:00:00:00\"/s/macAddr: \"00:00:00:00:00:00\"/macAddr: \"%s\"/' /usr/local/etc/upf/upfForwarder_1.yaml \n", para5GData["N3_MAC"]))
cmdStrArr = append(cmdStrArr, fmt.Sprintf("sudo sed -i '/- interfaceType: N3/,/ipv4Mask: 255.255.240.0/s/ipv4Mask: 255.255.240.0/ipv4Mask: %s/' /usr/local/etc/upf/upfForwarder_1.yaml \n", para5GData["N3_MASK"]))
cmdStrArr = append(cmdStrArr, fmt.Sprintf("sudo sed -i '/- interfaceType: N3/,/gatewayIpv4: 192.168.1.254/s/gatewayIpv4: 192.168.1.254/gatewayIpv4: %s/' /usr/local/etc/upf/upfForwarder_1.yaml \n", para5GData["N3_GW"]))
cmdStrArr = append(cmdStrArr, fmt.Sprintf("sudo sed -i '/- interfaceType: N3/,/interfacePCI: \"0000:00:00.0\"/s/interfacePCI: \"0000:00:00.0\"/interfacePCI: \"%s\"/' /usr/local/etc/upf/upfForwarder_1.yaml \n", para5GData["N3_PCI"]))
cmdStrArr = append(cmdStrArr, fmt.Sprintf("sudo sed -i '/- interfaceType: N3/,/macAddr: \"00:00:00:00:00:00\"/s/macAddr: \"00:00:00:00:00:00\"/macAddr: \"%s\"/' /usr/local/etc/upf/upfForwarder_1.yaml \n", para5GData["N3_MAC"]))
// 标准版 N6
if para5GData["UPF_TYPE"] == "StandardUPF" {
cmdStrArr = append(cmdStrArr, fmt.Sprintf("sudo sed -i \"s/192.168.8.191/%s/g\" /usr/local/etc/upf/upfForwarder_1.yaml \n", para5GData["N6_IP"]))
cmdStrArr = append(cmdStrArr, fmt.Sprintf("sudo sed -i '/- interfaceType: \"N3\"/,/driverType: .*/s/driverType: .*/driverType: \"%s\"/' /usr/local/etc/upf/upfForwarder_1.yaml \n", para5GData["UPF_DRIVER_TYPE"]))
cmdStrArr = append(cmdStrArr, fmt.Sprintf("sudo sed -i '/- interfaceType: \"N3\"/,/systemNetworkCardName: .*/s/systemNetworkCardName: .*/systemNetworkCardName: \"%s\"/' /usr/local/etc/upf/upfForwarder_1.yaml \n", para5GData["N3_NIC_NAME"]))
cmdStrArr = append(cmdStrArr, fmt.Sprintf("sudo sed -i '/- interfaceType: \"N6\"/,/driverType: .*/s/driverType: .*/driverType: \"%s\"/' /usr/local/etc/upf/upfForwarder_1.yaml \n", para5GData["UPF_DRIVER_TYPE"]))
cmdStrArr = append(cmdStrArr, fmt.Sprintf("sudo sed -i '/- interfaceType: \"N6\"/,/systemNetworkCardName: .*/s/systemNetworkCardName: .*/systemNetworkCardName: \"%s\"/' /usr/local/etc/upf/upfForwarder_1.yaml \n", para5GData["N6_NIC_NAME"]))
cmdStrArr = append(cmdStrArr, fmt.Sprintf("sudo sed -i '/- interfaceType: \"N6\"/,/ipv4Mask: 255.255.240.0/s/ipv4Mask: 255.255.240.0/ipv4Mask: %s/' /usr/local/etc/upf/upfForwarder_1.yaml \n", para5GData["N6_MASK"]))
cmdStrArr = append(cmdStrArr, fmt.Sprintf("sudo sed -i '/- interfaceType: \"N6\"/,/gatewayIpv4: 192.168.1.254/s/gatewayIpv4: 192.168.1.254/gatewayIpv4: %s/' /usr/local/etc/upf/upfForwarder_1.yaml \n", para5GData["N6_GW"]))
cmdStrArr = append(cmdStrArr, fmt.Sprintf("sudo sed -i '/- interfaceType: \"N6\"/,/interfacePCI: \"0000:00:00.0\"/s/interfacePCI: \"0000:00:00.0\"/interfacePCI: \"%s\"/' /usr/local/etc/upf/upfForwarder_1.yaml \n", para5GData["N6_PCI"]))
cmdStrArr = append(cmdStrArr, fmt.Sprintf("sudo sed -i '/- interfaceType: \"N6\"/,/macAddr: \"00:00:00:00:00:00\"/s/macAddr: \"00:00:00:00:00:00\"/macAddr: \"%s\"/' /usr/local/etc/upf/upfForwarder_1.yaml \n", para5GData["N6_MAC"]))
cmdStrArr = append(cmdStrArr, fmt.Sprintf("sudo sed -i '/- interfaceType: N3/,/driverType: .*/s/driverType: .*/driverType: \"%s\"/' /usr/local/etc/upf/upfForwarder_1.yaml \n", para5GData["UPF_DRIVER_TYPE"]))
cmdStrArr = append(cmdStrArr, fmt.Sprintf("sudo sed -i '/- interfaceType: N3/,/systemNetworkCardName: .*/s/systemNetworkCardName: .*/systemNetworkCardName: \"%s\"/' /usr/local/etc/upf/upfForwarder_1.yaml \n", para5GData["N3_NIC_NAME"]))
cmdStrArr = append(cmdStrArr, fmt.Sprintf("sudo sed -i '/- interfaceType: N6/,/driverType: .*/s/driverType: .*/driverType: \"%s\"/' /usr/local/etc/upf/upfForwarder_1.yaml \n", para5GData["UPF_DRIVER_TYPE"]))
cmdStrArr = append(cmdStrArr, fmt.Sprintf("sudo sed -i '/- interfaceType: N6/,/systemNetworkCardName: .*/s/systemNetworkCardName: .*/systemNetworkCardName: \"%s\"/' /usr/local/etc/upf/upfForwarder_1.yaml \n", para5GData["N6_NIC_NAME"]))
cmdStrArr = append(cmdStrArr, fmt.Sprintf("sudo sed -i '/- interfaceType: N6/,/ipv4Mask: 255.255.240.0/s/ipv4Mask: 255.255.240.0/ipv4Mask: %s/' /usr/local/etc/upf/upfForwarder_1.yaml \n", para5GData["N6_MASK"]))
cmdStrArr = append(cmdStrArr, fmt.Sprintf("sudo sed -i '/- interfaceType: N6/,/gatewayIpv4: 192.168.1.254/s/gatewayIpv4: 192.168.1.254/gatewayIpv4: %s/' /usr/local/etc/upf/upfForwarder_1.yaml \n", para5GData["N6_GW"]))
cmdStrArr = append(cmdStrArr, fmt.Sprintf("sudo sed -i '/- interfaceType: N6/,/interfacePCI: \"0000:00:00.0\"/s/interfacePCI: \"0000:00:00.0\"/interfacePCI: \"%s\"/' /usr/local/etc/upf/upfForwarder_1.yaml \n", para5GData["N6_PCI"]))
cmdStrArr = append(cmdStrArr, fmt.Sprintf("sudo sed -i '/- interfaceType: N6/,/macAddr: \"00:00:00:00:00:00\"/s/macAddr: \"00:00:00:00:00:00\"/macAddr: \"%s\"/' /usr/local/etc/upf/upfForwarder_1.yaml \n", para5GData["N6_MAC"]))
// 路由
cmdStrArr = append(cmdStrArr, fmt.Sprintf("sudo ip route add '%s/%s' via '%s' \n", para5GData["UE_IP"], para5GData["UE_CIDR"], para5GData["N6_IP"]))
}
@@ -451,7 +464,12 @@ func (r *NeVersion) operateCommand(action, neType string, neFilePaths []string)
if para5GData["UPF_TYPE"] == "LightUPF" {
cmdStrArr = append(cmdStrArr, "sudo sed -i \"s/192.168.8.191/0.0.0.0/g\" /usr/local/etc/upf/upfForwarder_1.yaml \n")
cmdStrArr = append(cmdStrArr, "sudo sed -i \"s/type: upfd/type: tun/g\" /usr/local/etc/upf/upfForwarder_1.yaml \n")
cmdStrArr = append(cmdStrArr, "sudo sed -i 's/driverType: vmxnet3/driverType: \"\"/g' /usr/local/etc/upf/upfForwarder_1.yaml \n")
cmdStrArr = append(cmdStrArr, "sudo sed -i 's/driverType: .*/driverType: \"\"/g' /usr/local/etc/upf/upfForwarder_1.yaml \n")
// UE
cmdStrArr = append(cmdStrArr, fmt.Sprintf("sudo sed -i '/- interfaceType: N6/,/ueIpv4: 10.2.1.0/s/ueIpv4: 10.2.1.0/ueIpv4: %s/' /usr/local/etc/upf/upfForwarder_1.yaml \n", para5GData["UE_IP"]))
cmdStrArr = append(cmdStrArr, fmt.Sprintf("sudo sed -i '/- interfaceType: N6/,/ueIpv4Mask: 255.255.255.0/s/ueIpv4Mask: 255.255.255.0/ueIpv4Mask: %s/' /usr/local/etc/upf/upfForwarder_1.yaml \n", para5GData["UE_MASK"]))
cmdStrArr = append(cmdStrArr, fmt.Sprintf("sudo sed -i '/- interfaceType: \"N6\"/,/ueIpv4: 10.2.1.0/s/ueIpv4: 10.2.1.0/ueIpv4: %s/' /usr/local/etc/upf/upfForwarder_1.yaml \n", para5GData["UE_IP"]))
cmdStrArr = append(cmdStrArr, fmt.Sprintf("sudo sed -i '/- interfaceType: \"N6\"/,/ueIpv4Mask: 255.255.255.0/s/ueIpv4Mask: 255.255.255.0/ueIpv4Mask: %s/' /usr/local/etc/upf/upfForwarder_1.yaml \n", para5GData["UE_MASK"]))
}
cmdStrArr = append(cmdStrArr, fmt.Sprintf("grep -qxF '%s upf' /etc/hosts || echo '%s upf' | sudo tee -a /etc/hosts \n", para5GData["UPF_IP"], para5GData["UPF_IP"]))
cmdStrArr = append(cmdStrArr, fmt.Sprintf("grep -qxF '%s upfn3' /etc/hosts || echo '%s upfn3' | sudo tee -a /etc/hosts \n", para5GData["N3_IP"], para5GData["N3_IP"]))
@@ -539,14 +557,14 @@ func (r *NeVersion) operateCommand(action, neType string, neFilePaths []string)
if strings.Contains(pkgCmdStr, "adb") {
para5GData := NewNeInfo.Para5GData
cmdStrArr = append(cmdStrArr, "sudo cp /usr/local/etc/adb/default/adb.conf /usr/local/etc/adb/adb.conf \n")
cmdStrArr = append(cmdStrArr, fmt.Sprintf("sudo sed -i \"s/bind 127.0.0.1/bind 127.0.0.1 %s/g\" /usr/local/etc/adb/adb.conf \n", para5GData["DB_IP"]))
cmdStrArr = append(cmdStrArr, fmt.Sprintf("sudo sed -i \"s/bind 127.0.0.1/bind %s/g\" /usr/local/etc/adb/adb.conf \n", para5GData["DB_IP"]))
cmdStrArr = append(cmdStrArr, "sudo service adb restart \n")
}
// kvdb
if strings.Contains(pkgCmdStr, "kvdb") {
para5GData := NewNeInfo.Para5GData
cmdStrArr = append(cmdStrArr, "sudo cp /usr/local/etc/kvdb/default/kvdb.conf /usr/local/etc/kvdb/kvdb.conf \n")
cmdStrArr = append(cmdStrArr, fmt.Sprintf("sudo sed -i \"s/bind 127.0.0.1/bind 127.0.0.1 %s/g\" /usr/local/etc/kvdb/kvdb.conf \n", para5GData["DB_IP"]))
cmdStrArr = append(cmdStrArr, fmt.Sprintf("sudo sed -i \"s/bind 127.0.0.1/bind %s/g\" /usr/local/etc/kvdb/kvdb.conf \n", para5GData["DB_IP"]))
cmdStrArr = append(cmdStrArr, "sudo service kvdb restart \n")
}
}
@@ -671,17 +689,20 @@ func (r *NeVersion) operateDome(action string, neVersion model.NeVersion) error
smscHost := fmt.Sprintf("%s smsc.ims.%s.3gppnetwork.org", para5GData["SMSC_IP"], mnc_mcc)
smscHostCMD := fmt.Sprintf("grep -qxF '%s' /etc/hosts || echo '%s' | sudo tee -a /etc/hosts \n", smscHost, smscHost)
smscIPCMD := fmt.Sprintf("grep -qxF '%s smsc' /etc/hosts || echo '%s smsc' | sudo tee -a /etc/hosts \n", para5GData["SMSC_IP"], para5GData["SMSC_IP"])
smsHost := fmt.Sprintf("sudo sed -i '/^%s smsc.*smsc$/c\\' /etc/hosts", para5GData["SIP_IP"])
// IMS 配置
imsNEs := NewNeInfo.SelectList(model.NeInfo{NeType: "IMS"}, false, false)
for _, v := range imsNEs {
NewNeInfo.NeRunSSHCmd(v.NeType, v.NeId, smscIPCMD)
NewNeInfo.NeRunSSHCmd(v.NeType, v.NeId, smscHostCMD)
NewNeInfo.NeRunSSHCmd(v.NeType, v.NeId, smsHost)
NewNeInfo.NeRunSSHCmd(v.NeType, v.NeId, "sudo sed -i '/^#!define WITH_SMS/ s/^/#/' /usr/local/etc/ims/vars.cfg")
NewNeInfo.NeRunSSHCmd(v.NeType, v.NeId, "ims-stop || true && ims-start")
}
// UDM 配置
smscASName := fmt.Sprintf("sudo sed -i '/- name: sms_as/{n;s|serverName: .*|serverName: sip:%s:5060|}' /usr/local/etc/udm/as.yaml", para5GData["SMSC_IP"])
smscASAddress := fmt.Sprintf("sudo sed -i '/- name: sms_as/{n;s|diameterAddress: .*|diameterAddress: smsc.ims.%s.3gppnetwork.org|}' /usr/local/etc/udm/as.yaml", mnc_mcc)
smscASName := fmt.Sprintf("sudo sed -i \"/- name: 'sms_as'/{n;s|serverName: .*|serverName: 'sip:%s:5060'|}\" /usr/local/etc/udm/as.yaml", para5GData["SMSC_IP"])
smscASAddress := fmt.Sprintf("sudo sed -i \"/- name: 'sms_as'/{n;n;n;s|diameterAddress: .*|diameterAddress: 'smsc.ims.%s.3gppnetwork.org'|}\" /usr/local/etc/udm/as.yaml", mnc_mcc)
udmNEs := NewNeInfo.SelectList(model.NeInfo{NeType: "UDM"}, false, false)
for _, v := range udmNEs {
NewNeInfo.NeRunSSHCmd(v.NeType, v.NeId, smscIPCMD)

View File

@@ -37,15 +37,16 @@ type IPerfController struct {
func (s *IPerfController) Version(c *gin.Context) {
language := ctx.AcceptLanguage(c)
var query struct {
NeType string `form:"neType" binding:"required"` // 网元类型
NeID string `form:"neId" binding:"required"` // 网元ID
NeType string `form:"neType" binding:"required"` // 网元类型
NeId string `form:"neId" binding:"required"` // 网元ID
Version string `form:"version" binding:"required,oneof=V2 V3"` // 版本
}
if err := c.ShouldBindQuery(&query); err != nil {
c.JSON(400, result.CodeMsg(400, i18n.TKey(language, "app.common.err400")))
return
}
output, err := s.iperfService.Version(query.NeType, query.NeID)
output, err := s.iperfService.Version(query.NeType, query.NeId, query.Version)
if err != nil {
c.JSON(200, result.ErrMsg(i18n.TKey(language, err.Error())))
return
@@ -60,15 +61,16 @@ func (s *IPerfController) Version(c *gin.Context) {
func (s *IPerfController) Install(c *gin.Context) {
language := ctx.AcceptLanguage(c)
var body struct {
NeType string `json:"neType" binding:"required"` // 网元类型
NeID string `json:"neId" binding:"required"` // 网元ID
NeType string `json:"neType" binding:"required"` // 网元类型
NeId string `json:"neId" binding:"required"` // 网元ID
Version string `form:"version" binding:"required,oneof=V2 V3"` // 版本
}
if err := c.ShouldBindBodyWithJSON(&body); err != nil {
c.JSON(400, result.CodeMsg(400, i18n.TKey(language, "app.common.err400")))
return
}
if err := s.iperfService.Install(body.NeType, body.NeID); err != nil {
if err := s.iperfService.Install(body.NeType, body.NeId, body.Version); err != nil {
c.JSON(200, result.ErrMsg(i18n.TKey(language, err.Error())))
return
}

View File

@@ -22,17 +22,39 @@ var NewIPerf = &IPerf{}
type IPerf struct{}
// Version 查询版本信息
func (s *IPerf) Version(meType, neId string) (string, error) {
// 检查是否安装iperf3
output, err := neService.NewNeInfo.NeRunSSHCmd(meType, neId, "iperf3 --version")
func (s *IPerf) Version(meType, neId, version string) (string, error) {
if version != "V2" && version != "V3" {
return "", fmt.Errorf("iperf version is required V2 or V3")
}
cmd := "iperf3 --version"
if version == "V2" {
cmd = "iperf -v"
}
// 网元主机的SSH客户端
sshClient, err := neService.NewNeInfo.NeRunSSHClient(meType, neId)
if err != nil {
return "", fmt.Errorf("iperf3 not installed")
return "", err
}
defer sshClient.Close()
// 检查是否安装iperf
output, err := sshClient.RunCMD(cmd)
if err != nil {
if version == "V2" && strings.HasSuffix(err.Error(), "status 1") { // V2 版本
return strings.TrimSpace(output), nil
}
return "", fmt.Errorf("iperf %s not installed", version)
}
return strings.TrimSpace(output), err
}
// Install 安装iperf3
func (s *IPerf) Install(meType, neId string) error {
func (s *IPerf) Install(meType, neId, version string) error {
if version != "V2" && version != "V3" {
return fmt.Errorf("iperf version is required V2 or V3")
}
// 网元主机的SSH客户端
sshClient, err := neService.NewNeInfo.NeRunSSHClient(meType, neId)
if err != nil {
@@ -56,9 +78,16 @@ func (s *IPerf) Install(meType, neId string) error {
depDir = "assets/dependency/iperf3/deb"
// sudo apt remove iperf3 libiperf0 libsctp1 libsctp-dev lksctp-tools
} else if _, err := sshClient.RunCMD("sudo yum --version"); err == nil {
depPkg = "sudo rpm -Uvh --force"
depPkg = "sudo rpm -Uvh --nosignature --reinstall --force"
depDir = "assets/dependency/iperf3/rpm"
// yum remove iperf3 iperf3-help.noarch
} else {
return fmt.Errorf("iperf %s not supported install", version)
}
// V2版本和V3版本的安装包路径不同
if version == "V2" {
depDir = strings.Replace(depDir, "iperf3", "iperf", 1)
}
// 从 embed.FS 中读取默认配置文件内容
@@ -72,19 +101,19 @@ func (s *IPerf) Install(meType, neId string) error {
// 打开本地文件
localFile, err := assetsDir.Open(fmt.Sprintf("%s/%s", depDir, d.Name()))
if err != nil {
return fmt.Errorf("iperf3 file local error")
return fmt.Errorf("iperf %s file local error", version)
}
defer localFile.Close()
// 创建远程文件
remotePath := fmt.Sprintf("%s/%s", nePath, d.Name())
remoteFile, err := sftpClient.Client.Create(remotePath)
if err != nil {
return fmt.Errorf("iperf3 file remote error")
return fmt.Errorf("iperf %s file remote error", version)
}
defer remoteFile.Close()
// 使用 io.Copy 将嵌入的文件内容复制到目标文件
if _, err := io.Copy(remoteFile, localFile); err != nil {
return fmt.Errorf("iperf3 file copy error")
return fmt.Errorf("iperf %s file copy error", version)
}
neFilePaths = append(neFilePaths, remotePath)
}
@@ -98,7 +127,7 @@ func (s *IPerf) Install(meType, neId string) error {
// 安装软件包
pkgInstall := fmt.Sprintf("%s %s", depPkg, strings.Join(neFilePaths, " "))
if _, err := sshClient.RunCMD(pkgInstall); err != nil {
return fmt.Errorf("iperf3 install error")
return fmt.Errorf("iperf %s install error", version)
}
return err
}
@@ -108,7 +137,7 @@ func (s *IPerf) Run(client *wsModel.WSClient, reqMsg wsModel.WSRequest) {
// 必传requestId确认消息
if reqMsg.RequestID == "" {
msg := "message requestId is required"
logger.Infof("ws IPerf3 Run UID %s err: %s", client.BindUid, msg)
logger.Infof("ws IPerf Run UID %s err: %s", client.BindUid, msg)
msgByte, _ := json.Marshal(result.ErrMsg(msg))
client.MsgChan <- msgByte
return
@@ -126,7 +155,7 @@ func (s *IPerf) Run(client *wsModel.WSClient, reqMsg wsModel.WSRequest) {
time.Sleep(1 * time.Second)
client.StopChan <- struct{}{}
return
case "iperf3":
case "iperf":
// SSH会话消息接收写入会话
var command string
command, err = s.parseOptions(reqMsg.Data)
@@ -155,7 +184,7 @@ func (s *IPerf) Run(client *wsModel.WSClient, reqMsg wsModel.WSRequest) {
}
if err != nil {
logger.Warnf("ws IPerf3 Run UID %s err: %s", client.BindUid, err.Error())
logger.Warnf("ws IPerf Run UID %s err: %s", client.BindUid, err.Error())
msgByte, _ := json.Marshal(result.ErrMsg(err.Error()))
client.MsgChan <- msgByte
if err == io.EOF {
@@ -175,7 +204,8 @@ func (s *IPerf) parseOptions(reqData any) (string, error) {
msgByte, _ := json.Marshal(reqData)
var data struct {
Command string `json:"command"` // 命令字符串
Client bool `json:"client"` // 服务端或客户端,默认服务端
Version string `json:"version"` // 服务版本默认V3
Mode string `json:"mode"` // 服务端或客户端默认客户端client
Host string `json:"host"` // 客户端连接到的服务端IP地址
// Server or Client
Port int `json:"port"` // 服务端口
@@ -183,17 +213,25 @@ func (s *IPerf) parseOptions(reqData any) (string, error) {
// Server
OneOff bool `json:"oneOff"` // 只进行一次连接
// Client
UDP bool `json:"udp"` // use UDP rather than TCP
Time int `json:"time"` // 以秒为单位的传输时间(默认为 10 秒)
Reverse bool `json:"reverse"` // 以反向模式运行(服务器发送,客户端接收)
Window string `json:"window"` // 设置窗口大小/套接字缓冲区大小
UDP bool `json:"udp"` // use UDP rather than TCP
Time int `json:"time"` // 以秒为单位的传输时间(默认为 10 秒)
Reverse bool `json:"reverse"` // 以反向模式运行(服务器发送,客户端接收)
Window string `json:"window"` // 设置窗口大小/套接字缓冲区大小
Parallel int `json:"parallel"` // 运行的并行客户端流数量
Bitrate int `json:"bitrate"` // 以比特/秒为单位0 表示无限制)
}
if err := json.Unmarshal(msgByte, &data); err != nil {
logger.Warnf("ws processor parseClient err: %s", err.Error())
return "", fmt.Errorf("query data structure error")
}
if data.Version != "V3" && data.Version != "V2" {
return "", fmt.Errorf("query data version support V3 or V2")
}
command := []string{"iperf3"}
if data.Version == "V2" {
command = []string{"iperf"}
}
// 命令字符串高优先级
if data.Command != "" {
command = append(command, data.Command)
@@ -201,16 +239,14 @@ func (s *IPerf) parseOptions(reqData any) (string, error) {
return strings.Join(command, " "), nil
}
if data.Client && data.Host == "" {
if data.Mode != "client" && data.Mode != "server" {
return "", fmt.Errorf("query data mode support client or server")
}
if data.Mode == "client" && data.Host == "" {
return "", fmt.Errorf("query data client host empty")
}
if !data.Client {
command = append(command, "-s")
// Server
if data.OneOff {
command = append(command, "-1")
}
} else {
if data.Mode == "client" {
command = append(command, "-c")
command = append(command, data.Host)
// Client
@@ -220,6 +256,12 @@ func (s *IPerf) parseOptions(reqData any) (string, error) {
if data.Time > 0 {
command = append(command, fmt.Sprintf("-t %d", data.Time))
}
if data.Bitrate > 0 {
command = append(command, fmt.Sprintf("-b %d", data.Bitrate))
}
if data.Parallel > 0 {
command = append(command, fmt.Sprintf("-P %d", data.Parallel))
}
if data.Reverse {
command = append(command, "-R")
}
@@ -227,6 +269,13 @@ func (s *IPerf) parseOptions(reqData any) (string, error) {
command = append(command, fmt.Sprintf("-w %s", data.Window))
}
}
if data.Mode == "server" {
command = append(command, "-s")
// Server
if data.OneOff {
command = append(command, "-1")
}
}
// Server or Client
if data.Port > 0 {

View File

@@ -0,0 +1,69 @@
package controller
import (
"be.ems/src/framework/i18n"
"be.ems/src/framework/logger"
"be.ems/src/framework/redis"
"be.ems/src/framework/utils/ctx"
"be.ems/src/framework/vo/result"
neService "be.ems/src/modules/network_element/service"
"github.com/gin-gonic/gin"
)
// Redis 终端
//
// GET /redis?hostId=1
func (s *WSController) Redis(c *gin.Context) {
language := ctx.AcceptLanguage(c)
var query struct {
HostId string `form:"hostId" binding:"required"` // 连接主机ID
}
if err := c.ShouldBindQuery(&query); err != nil {
c.JSON(400, result.CodeMsg(400, i18n.TKey(language, "app.common.err400")))
return
}
// 登录用户信息
loginUser, err := ctx.LoginUser(c)
if err != nil {
c.JSON(401, result.CodeMsg(401, i18n.TKey(language, err.Error())))
return
}
neHost := neService.NewNeHost.SelectById(query.HostId)
if neHost.HostID != query.HostId || neHost.HostType != "redis" {
// 没有可访问主机信息数据!
c.JSON(200, result.ErrMsg(i18n.TKey(language, "neHost.noData")))
return
}
// 创建链接Redis客户端
var connRedis redis.ConnRedis
neHost.CopyTo(&connRedis)
client, err := connRedis.NewClient()
if err != nil {
// 连接主机失败,请检查连接参数后重试
c.JSON(200, result.ErrMsg(i18n.TKey(language, "neHost.errByHostInfo")))
return
}
defer client.Close()
// 将 HTTP 连接升级为 WebSocket 连接
wsConn := s.wsService.UpgraderWs(c.Writer, c.Request)
if wsConn == nil {
return
}
defer wsConn.Close()
wsClient := s.wsService.ClientCreate(loginUser.UserID, nil, wsConn, client)
go s.wsService.ClientWriteListen(wsClient)
go s.wsService.ClientReadListen(wsClient, s.wsReceiveService.Redis)
// 等待停止信号
for value := range wsClient.StopChan {
s.wsService.ClientClose(wsClient.ID)
logger.Infof("ws Stop Client UID %s %s", wsClient.BindUid, value)
return
}
}

View File

@@ -4,9 +4,12 @@ import (
"encoding/json"
"fmt"
"io"
"reflect"
"strings"
"time"
"be.ems/src/framework/logger"
"be.ems/src/framework/redis"
"be.ems/src/framework/telnet"
"be.ems/src/framework/utils/ssh"
"be.ems/src/framework/vo/result"
@@ -104,7 +107,7 @@ func (s *WSReceive) Shell(client *model.WSClient, reqMsg model.WSRequest) {
command := reqMsg.Data.(string)
sshClientSession := client.ChildConn.(*ssh.SSHClientSession)
_, err = sshClientSession.Write(command)
case "ssh_resize":
case "resize":
// SSH会话窗口重置
msgByte, _ := json.Marshal(reqMsg.Data)
var data struct {
@@ -225,7 +228,7 @@ func (s *WSReceive) Telnet(client *model.WSClient, reqMsg model.WSRequest) {
command := reqMsg.Data.(string)
telnetClientSession := client.ChildConn.(*telnet.TelnetClientSession)
_, err = telnetClientSession.Write(command)
case "telnet_resize":
case "resize":
// Telnet会话窗口重置
msgByte, _ := json.Marshal(reqMsg.Data)
var data struct {
@@ -256,3 +259,76 @@ func (s *WSReceive) Telnet(client *model.WSClient, reqMsg model.WSRequest) {
client.MsgChan <- resByte
}
}
// Redis 接收终端交互业务处理
func (s *WSReceive) Redis(client *model.WSClient, reqMsg model.WSRequest) {
// 必传requestId确认消息
if reqMsg.RequestID == "" {
msg := "message requestId is required"
logger.Infof("ws Shell UID %s err: %s", client.BindUid, msg)
msgByte, _ := json.Marshal(result.ErrMsg(msg))
client.MsgChan <- msgByte
return
}
var resByte []byte
var err error
switch reqMsg.Type {
case "close":
s.close(client)
return
case "redis":
// Redis会话消息接收写入会话
command := fmt.Sprint(reqMsg.Data)
redisClientSession := client.ChildConn.(*redis.ConnRedis)
output, err := redisClientSession.RunCMD(command)
dataStr := ""
if err != nil {
dataStr = fmt.Sprintf("%s \r\n", err.Error())
} else {
// 获取结果的反射类型
resultType := reflect.TypeOf(output)
switch resultType.Kind() {
case reflect.Slice:
// 如果是切片类型需要进一步判断是否是 []string 或 []interface{}
if resultType.Elem().Kind() == reflect.String {
dataStr = fmt.Sprintf("%s \r\n", strings.Join(output.([]string), "\r\n"))
} else if resultType.Elem().Kind() == reflect.Interface {
arr := []string{}
for _, v := range output.([]any) {
arr = append(arr, fmt.Sprintf("%s", v))
}
dataStr = fmt.Sprintf("%s \r\n", strings.Join(arr, "\r\n"))
}
case reflect.Ptr:
dataStr = "\r\n"
case reflect.String, reflect.Int64:
dataStr = fmt.Sprintf("%s \r\n", output)
default:
dataStr = fmt.Sprintf("%s \r\n", output)
}
}
resByte, _ = json.Marshal(result.Ok(map[string]any{
"requestId": reqMsg.RequestID,
"data": dataStr,
}))
default:
err = fmt.Errorf("message type %s not supported", reqMsg.Type)
}
if err != nil {
logger.Warnf("ws Shell UID %s err: %s", client.BindUid, err.Error())
msgByte, _ := json.Marshal(result.ErrMsg(err.Error()))
client.MsgChan <- msgByte
if err == io.EOF {
// 等待1s后关闭连接
time.Sleep(1 * time.Second)
client.StopChan <- struct{}{}
}
return
}
if len(resByte) > 0 {
client.MsgChan <- resByte
}
}

View File

@@ -35,6 +35,11 @@ func Setup(router *gin.Engine) {
collectlogs.OperateLog(collectlogs.OptionNew("log.operate.title.ws", collectlogs.BUSINESS_TYPE_OTHER)),
controller.NewWSController.Telnet,
)
wsGroup.GET("/redis",
middleware.PreAuthorize(nil),
collectlogs.OperateLog(collectlogs.OptionNew("log.operate.title.ws", collectlogs.BUSINESS_TYPE_OTHER)),
controller.NewWSController.Redis,
)
wsGroup.GET("/view",
middleware.PreAuthorize(nil),
collectlogs.OperateLog(collectlogs.OptionNew("log.operate.title.ws", collectlogs.BUSINESS_TYPE_OTHER)),