diff --git a/src/modules/network_element/controller/action.go b/src/modules/network_element/controller/action.go index 3997abf..44483a9 100644 --- a/src/modules/network_element/controller/action.go +++ b/src/modules/network_element/controller/action.go @@ -2,6 +2,7 @@ package controller import ( "fmt" + "os" "path/filepath" "runtime" "strings" @@ -9,6 +10,7 @@ import ( "nms_cxy/src/framework/i18n" "nms_cxy/src/framework/utils/ctx" "nms_cxy/src/framework/utils/file" + "nms_cxy/src/framework/utils/generate" "nms_cxy/src/framework/utils/ssh" "nms_cxy/src/framework/vo/result" neService "nms_cxy/src/modules/network_element/service" @@ -30,7 +32,7 @@ type NeActionController struct { neInfoService neService.INeInfo } -// 发送文件到网元端 +// 发送文件从本地到网元 // // POST /pushFile func (s *NeActionController) PushFile(c *gin.Context) { @@ -39,6 +41,7 @@ func (s *NeActionController) PushFile(c *gin.Context) { NeType string `json:"neType" binding:"required"` NeID string `json:"neId" binding:"required"` UploadPath string `json:"uploadPath" binding:"required"` + DelTemp bool `json:"delTemp"` // 删除本地临时文件 } if err := c.ShouldBindBodyWith(&body, binding.JSON); err != nil { c.JSON(400, result.CodeMsg(400, i18n.TKey(language, "app.common.err400"))) @@ -69,13 +72,20 @@ func (s *NeActionController) PushFile(c *gin.Context) { // 本地文件 localFilePath := file.ParseUploadFilePath(body.UploadPath) - neFilePath := fmt.Sprintf("/tmp/%s", filepath.Base(localFilePath)) + // 网元端临时目录 + sshClient.RunCMD("mkdir -p /tmp/omc/push && sudo chmod 777 -R /tmp/omc") + neFilePath := filepath.ToSlash(filepath.Join("/tmp/omc/push", filepath.Base(localFilePath))) // 复制到远程 if err = sftpClient.CopyFileLocalToRemote(localFilePath, neFilePath); err != nil { - c.JSON(200, result.ErrMsg(fmt.Sprintf("%s : please check if scp remote copy is allowed", neInfo.NeType))) + c.JSON(200, result.ErrMsg("Please check if the file exists or if scp is allowed to copy remotely")) return } + defer func() { + if body.DelTemp { + _ = os.Remove(localFilePath) + } + }() c.JSON(200, result.OkData(filepath.ToSlash(neFilePath))) } @@ -89,6 +99,7 @@ func (s *NeActionController) PullFile(c *gin.Context) { NeID string `form:"neId" binding:"required"` Path string `form:"path" binding:"required"` FileName string `form:"fileName" binding:"required"` + DelTemp bool `form:"delTemp"` // 删除本地临时文件 } if err := c.ShouldBindQuery(&querys); err != nil { c.JSON(400, result.CodeMsg(400, i18n.TKey(language, "app.common.err400"))) @@ -117,8 +128,9 @@ func (s *NeActionController) PullFile(c *gin.Context) { } defer sftpClient.Close() - nePath := fmt.Sprintf("%s/%s", querys.Path, querys.FileName) - localFilePath := fmt.Sprintf("/tmp/omc/pullFile%s", nePath) + nePath := filepath.ToSlash(filepath.Join(querys.Path, querys.FileName)) + fileName := generate.Code(6) + "_" + querys.FileName + localFilePath := filepath.Join("/tmp/omc/pull", fileName) if runtime.GOOS == "windows" { localFilePath = fmt.Sprintf("C:%s", localFilePath) } @@ -127,7 +139,13 @@ func (s *NeActionController) PullFile(c *gin.Context) { c.JSON(200, result.ErrMsg(err.Error())) return } - c.FileAttachment(localFilePath, querys.FileName) + + defer func() { + if querys.DelTemp { + _ = os.Remove(localFilePath) + } + }() + c.FileAttachment(localFilePath, fileName) } // 网元端文件列表 diff --git a/src/modules/network_element/controller/ne_config.go b/src/modules/network_element/controller/ne_config.go index 32e1f58..d42c234 100644 --- a/src/modules/network_element/controller/ne_config.go +++ b/src/modules/network_element/controller/ne_config.go @@ -61,7 +61,7 @@ func (s *NeConfigController) Info(c *gin.Context) { } // 将字符串转json数据 - if err := json.Unmarshal([]byte(data.ParamJSONStr), &data.ParamData); err != nil { + if err := json.Unmarshal([]byte(data.ParamJson), &data.ParamData); err != nil { c.JSON(400, result.CodeMsg(400, err.Error())) return } @@ -85,7 +85,7 @@ func (s *NeConfigController) Add(c *gin.Context) { c.JSON(400, result.CodeMsg(400, err.Error())) return } - body.ParamJSONStr = string(paramDataByte) + body.ParamJson = string(paramDataByte) insertId := s.neConfigService.Insert(body) if insertId != "" { @@ -121,7 +121,7 @@ func (s *NeConfigController) Edit(c *gin.Context) { c.JSON(400, result.CodeMsg(400, err.Error())) return } - body.ParamJSONStr = string(paramDataByte) + body.ParamJson = string(paramDataByte) rows := s.neConfigService.Update(body) if rows > 0 { @@ -133,16 +133,16 @@ func (s *NeConfigController) Edit(c *gin.Context) { // 网元参数配置可用属性值删除 // -// DELETE /:ids +// DELETE / func (s *NeConfigController) Remove(c *gin.Context) { language := ctx.AcceptLanguage(c) - ids := c.Param("ids") - if ids == "" { + id, okId := c.GetQuery("id") + if id == "" || !okId { c.JSON(400, result.CodeMsg(400, i18n.TKey(language, "app.common.err400"))) return } // 处理字符转id数组后去重 - idsArr := strings.Split(ids, ",") + idsArr := strings.Split(id, ",") uniqueIDs := parse.RemoveDuplicates(idsArr) if len(uniqueIDs) <= 0 { c.JSON(200, result.Err(nil)) @@ -174,26 +174,26 @@ func (s *NeConfigController) ListByNeType(c *gin.Context) { // 网元参数配置数据信息 // // GET /data -func (s *NeConfigController) Data(c *gin.Context) { +func (s *NeConfigController) DataInfo(c *gin.Context) { language := ctx.AcceptLanguage(c) - var querys struct { - NeType string `form:"neType" binding:"required"` // 网元类型 - NeId string `form:"neId" binding:"required"` // 网元ID - TopTag string `form:"topTag" binding:"required"` // 可用属性 + var query struct { + NeType string `form:"neType" binding:"required"` // 网元类型 + NeId string `form:"neId" binding:"required"` // 网元ID + ParamName string `form:"paramName" binding:"required"` // 可用属性 } - if err := c.ShouldBindQuery(&querys); err != nil { + if err := c.ShouldBindQuery(&query); err != nil { c.JSON(400, result.CodeMsg(400, i18n.TKey(language, "app.common.err400"))) return } - neInfo := s.neInfoService.SelectNeInfoByNeTypeAndNeID(querys.NeType, querys.NeId) - if neInfo.NeId != querys.NeId || neInfo.IP == "" { + neInfo := s.neInfoService.SelectNeInfoByNeTypeAndNeID(query.NeType, query.NeId) + if neInfo.NeId != query.NeId || neInfo.IP == "" { c.JSON(200, result.ErrMsg(i18n.TKey(language, "app.common.noNEInfo"))) return } // 网元直连 - resData, err := neFetchlink.NeConfigInfo(neInfo, querys.TopTag) + resData, err := neFetchlink.NeConfigInfo(neInfo, query.ParamName) if err != nil { c.JSON(200, result.ErrMsg(err.Error())) return @@ -201,3 +201,118 @@ func (s *NeConfigController) Data(c *gin.Context) { c.JSON(200, result.Ok(resData)) } + +// 网元参数配置数据修改 +// +// PUT /data +func (s *NeConfigController) DataEdit(c *gin.Context) { + language := ctx.AcceptLanguage(c) + var body struct { + NeType string `json:"neType" binding:"required"` // 网元类型 + NeId string `json:"neId" binding:"required"` // 网元ID + ParamName string `json:"paramName" binding:"required"` + ParamData map[string]any `json:"paramData" binding:"required"` + Loc string `json:"loc"` // 仅array使用与数据对象内index一致,有多层时划分嵌套层(index/subParamName/index) + } + if err := c.ShouldBindBodyWith(&body, binding.JSON); err != nil { + c.JSON(400, result.CodeMsg(400, i18n.TKey(language, "app.common.err400"))) + return + } + + neInfo := s.neInfoService.SelectNeInfoByNeTypeAndNeID(body.NeType, body.NeId) + if neInfo.NeId != body.NeId || neInfo.IP == "" { + c.JSON(200, result.ErrMsg(i18n.TKey(language, "app.common.noNEInfo"))) + return + } + + // 网元直连 + 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) +// +// POST /data +func (s *NeConfigController) DataAdd(c *gin.Context) { + language := ctx.AcceptLanguage(c) + var body struct { + NeType string `json:"neType" binding:"required"` // 网元类型 + NeId string `json:"neId" binding:"required"` // 网元ID + ParamName string `json:"paramName" binding:"required"` // 根据配置可选值 + ParamData map[string]any `json:"paramData" binding:"required"` // 数据对象 + Loc string `json:"loc" binding:"required"` // 与数据对象内index一致,有多层时划分嵌套层(index/subParamName/index) + } + if err := c.ShouldBindBodyWith(&body, binding.JSON); err != nil { + c.JSON(400, result.CodeMsg(400, i18n.TKey(language, "app.common.err400"))) + return + } + + // 检查是否array + info := s.neConfigService.SelectNeConfigByNeTypeAndParamName(body.NeType, body.ParamName) + if info.ParamType != "array" { + c.JSON(400, result.CodeMsg(400, "this attribute does not support adding")) + return + } + // 必须含有index + _, idxOk := body.ParamData["index"] + if info.ParamType == "array" && !idxOk { + c.JSON(400, result.CodeMsg(400, i18n.TKey(language, "app.common.err400"))) + return + } + + neInfo := s.neInfoService.SelectNeInfoByNeTypeAndNeID(body.NeType, body.NeId) + if neInfo.NeId != body.NeId || neInfo.IP == "" { + c.JSON(200, result.ErrMsg(i18n.TKey(language, "app.common.noNEInfo"))) + return + } + + // 网元直连 + resData, err := neFetchlink.NeConfigInstall(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) +// +// DELETE /data +func (s *NeConfigController) DataRemove(c *gin.Context) { + language := ctx.AcceptLanguage(c) + var query struct { + NeType string `form:"neType" binding:"required"` // 网元类型 + NeId string `form:"neId" binding:"required"` // 网元ID + ParamName string `form:"paramName" binding:"required"` + Loc string `form:"loc" binding:"required"` // 与数据对象内index一致,有多层时划分嵌套层(index/subParamName/index) + } + if err := c.ShouldBindQuery(&query); err != nil { + c.JSON(400, result.CodeMsg(400, i18n.TKey(language, "app.common.err400"))) + return + } + + // 检查是否array + info := s.neConfigService.SelectNeConfigByNeTypeAndParamName(query.NeType, query.ParamName) + if info.ParamType != "array" { + c.JSON(400, result.CodeMsg(400, "this attribute does not support adding")) + return + } + + neInfo := s.neInfoService.SelectNeInfoByNeTypeAndNeID(query.NeType, query.NeId) + if neInfo.NeId != query.NeId || neInfo.IP == "" { + c.JSON(200, result.ErrMsg(i18n.TKey(language, "app.common.noNEInfo"))) + return + } + + // 网元直连 + resData, err := neFetchlink.NeConfigDelete(neInfo, query.ParamName, query.Loc) + if err != nil { + c.JSON(200, result.ErrMsg(err.Error())) + return + } + c.JSON(200, result.OkData(resData)) +} diff --git a/src/modules/network_element/controller/ne_config_backup.go b/src/modules/network_element/controller/ne_config_backup.go new file mode 100644 index 0000000..2e49698 --- /dev/null +++ b/src/modules/network_element/controller/ne_config_backup.go @@ -0,0 +1,205 @@ +package controller + +import ( + "os" + "path/filepath" + "strings" + + "nms_cxy/src/framework/i18n" + "nms_cxy/src/framework/utils/ctx" + "nms_cxy/src/framework/utils/file" + "nms_cxy/src/framework/utils/parse" + "nms_cxy/src/framework/vo/result" + "nms_cxy/src/modules/network_element/model" + neService "nms_cxy/src/modules/network_element/service" + + "github.com/gin-gonic/gin" + "github.com/gin-gonic/gin/binding" +) + +// NewNeConfigBackup 实例化控制层 NeConfigBackupController 结构体 +var NewNeConfigBackup = &NeConfigBackupController{ + neConfigBackupService: neService.NewNeConfigBackupImpl, + neInfoService: neService.NewNeInfoImpl, +} + +// 网元配置文件备份记录 +// +// PATH /config/backup +type NeConfigBackupController struct { + // 网元配置文件备份记录服务 + neConfigBackupService neService.INeConfigBackup + // 网元信息服务 + neInfoService neService.INeInfo +} + +// 网元配置文件备份记录列表 +// +// GET /list +func (s *NeConfigBackupController) List(c *gin.Context) { + querys := ctx.QueryMap(c) + data := s.neConfigBackupService.SelectPage(querys) + + c.JSON(200, result.Ok(data)) +} + +// 网元配置文件备份记录信息 +// +// GET /download?id=xx +func (s *NeConfigBackupController) Download(c *gin.Context) { + language := ctx.AcceptLanguage(c) + id, ok := c.GetQuery("id") + if !ok || id == "" { + c.JSON(400, result.CodeMsg(400, i18n.TKey(language, "app.common.err400"))) + return + } + + item := s.neConfigBackupService.SelectById(id) + if item.ID != id { + // 没有可访问主机命令数据! + c.JSON(200, result.ErrMsg(i18n.TKey(language, "neConfigBackup.noData"))) + return + } + + if _, err := os.Stat(item.Path); err != nil { + c.JSON(200, result.ErrMsg(i18n.TKey(language, "neConfigBackup.notFoundFile"))) + return + } + c.FileAttachment(item.Path, item.Name) +} + +// 网元配置文件备份记录修改 +// +// PUT / +func (s *NeConfigBackupController) Edit(c *gin.Context) { + language := ctx.AcceptLanguage(c) + var body struct { + ID string `json:"id" binding:"required"` // 记录ID + Name string `json:"name" binding:"required"` // 名称 + Remark string `json:"remark" binding:"required"` // 备注 + } + err := c.ShouldBindBodyWith(&body, binding.JSON) + if err != nil || body.ID == "" { + c.JSON(400, result.CodeMsg(400, i18n.TKey(language, "app.common.err400"))) + return + } + + // 检查是否存在 + data := s.neConfigBackupService.SelectById(body.ID) + if data.ID != body.ID { + // 没有可访问主机命令数据! + c.JSON(200, result.ErrMsg(i18n.TKey(language, "neConfig.noData"))) + return + } + + data.Name = body.Name + data.Remark = body.Remark + data.UpdateBy = ctx.LoginUserToUserName(c) + rows := s.neConfigBackupService.Update(data) + if rows > 0 { + c.JSON(200, result.Ok(nil)) + return + } + c.JSON(200, result.Err(nil)) +} + +// 网元配置文件备份记录删除 +// +// DELETE /?id=xx +func (s *NeConfigBackupController) Remove(c *gin.Context) { + language := ctx.AcceptLanguage(c) + id, ok := c.GetQuery("id") + if !ok || id == "" { + c.JSON(400, result.CodeMsg(400, i18n.TKey(language, "app.common.err400"))) + return + } + // 处理字符转id数组后去重 + ids := strings.Split(id, ",") + uniqueIDs := parse.RemoveDuplicates(ids) + if len(uniqueIDs) <= 0 { + c.JSON(200, result.Err(nil)) + return + } + rows, err := s.neConfigBackupService.DeleteByIds(uniqueIDs) + if err != nil { + c.JSON(200, result.ErrMsg(i18n.TKey(language, err.Error()))) + return + } + msg := i18n.TTemplate(language, "app.common.deleteSuccess", map[string]any{"num": rows}) + c.JSON(200, result.OkMsg(msg)) +} + +// 网元配置文件备份导入 +// +// POST /import +func (s *NeConfigBackupController) Import(c *gin.Context) { + language := ctx.AcceptLanguage(c) + var body struct { + NeType string `json:"neType" binding:"required"` + NeId string `json:"neId" binding:"required"` + Type string `json:"type" binding:"required,oneof=backup upload"` // 导入类型 + Path string `json:"path" binding:"required"` // 文件路径 + } + if err := c.ShouldBindBodyWith(&body, binding.JSON); err != nil { + c.JSON(200, result.ErrMsg(i18n.TKey(language, "app.common.noNEInfo"))) + return + } + if !strings.HasSuffix(body.Path, ".zip") { + c.JSON(200, result.ErrMsg("Only supports decompression of zip files")) + return + } + + // 查网元 + neInfo := s.neInfoService.SelectNeInfoByNeTypeAndNeID(body.NeType, body.NeId) + if neInfo.NeId != body.NeId || neInfo.IP == "" { + c.JSON(200, result.ErrMsg(i18n.TKey(language, "app.common.noNEInfo"))) + return + } + // 将zip文件解压到本地后复制到网元端 + localFilePath := body.Path + if body.Type == "upload" { + localFilePath = file.ParseUploadFilePath(body.Path) + } + if err := s.neConfigBackupService.NeConfigLocalToNe(neInfo, localFilePath); err != nil { + c.JSON(200, result.ErrMsg(err.Error())) + return + } + c.JSON(200, result.Ok(nil)) +} + +// 网元配置文件备份导出 +// +// POST /export +func (s *NeConfigBackupController) Export(c *gin.Context) { + language := ctx.AcceptLanguage(c) + var body struct { + NeType string `json:"neType" binding:"required"` + NeId string `json:"neId" binding:"required"` + } + if err := c.ShouldBindBodyWith(&body, binding.JSON); err != nil { + c.JSON(400, result.CodeMsg(400, i18n.TKey(language, "app.common.err400"))) + return + } + // 查网元 + neInfo := s.neInfoService.SelectNeInfoByNeTypeAndNeID(body.NeType, body.NeId) + if neInfo.NeId != body.NeId || neInfo.IP == "" { + c.JSON(200, result.ErrMsg(i18n.TKey(language, "app.common.noNEInfo"))) + return + } + // 将网元文件备份到本地 + zipFilePath, err := s.neConfigBackupService.NeConfigNeToLocal(neInfo) + if err != nil { + c.JSON(200, result.ErrMsg(err.Error())) + return + } + // 新增备份记录 + item := model.NeConfigBackup{ + NeType: neInfo.NeType, + NeId: neInfo.NeId, + Name: filepath.Base(zipFilePath), + Path: zipFilePath, + CreateBy: ctx.LoginUserToUserName(c), + } + s.neConfigBackupService.Insert(item) + c.FileAttachment(item.Path, item.Name) +} diff --git a/src/modules/network_element/fetch_link/ne_config.go b/src/modules/network_element/fetch_link/ne_config.go index 0457c56..cab5850 100644 --- a/src/modules/network_element/fetch_link/ne_config.go +++ b/src/modules/network_element/fetch_link/ne_config.go @@ -26,12 +26,13 @@ func NeConfigOMC(neInfo model.NeInfo) (map[string]any, error) { }, nil) var resData map[string]any if err != nil { - status := err.Error() - logger.Warnf("NeConfigOMC %s Put \"%s\"", status, neUrl) - if strings.HasPrefix(status, "201") || strings.HasPrefix(status, "204") { + errStr := err.Error() + logger.Warnf("NeConfigOMC Put \"%s\"", neUrl) + if strings.HasPrefix(errStr, "201") || strings.HasPrefix(errStr, "204") { return resData, nil } - return nil, err + logger.Errorf("NeConfigOMC %s", errStr) + return nil, fmt.Errorf("NeService Config OMC Update API Error") } // 200 成功无数据时 @@ -42,7 +43,7 @@ func NeConfigOMC(neInfo model.NeInfo) (map[string]any, error) { // 序列化结果 err = json.Unmarshal(resBytes, &resData) if err != nil { - logger.Warnf("NeConfigOMC Unmarshal %s", err.Error()) + logger.Errorf("NeConfigOMC Unmarshal %s", err.Error()) return nil, err } @@ -50,20 +51,115 @@ func NeConfigOMC(neInfo model.NeInfo) (map[string]any, error) { } // NeConfigInfo 网元配置信息 -func NeConfigInfo(neInfo model.NeInfo, name string) (map[string]any, error) { +func NeConfigInfo(neInfo model.NeInfo, paramName string) (map[string]any, error) { // 网元配置对端网管信息 - neUrl := fmt.Sprintf("http://%s:%d/api/rest/systemManagement/v1/elementType/%s/objectType/config/%s", neInfo.IP, neInfo.Port, strings.ToLower(neInfo.NeType), name) - resBytes, err := fetch.Get(neUrl, nil, 1000) + neUrl := fmt.Sprintf("http://%s:%d/api/rest/systemManagement/v1/elementType/%s/objectType/config/%s", neInfo.IP, neInfo.Port, strings.ToLower(neInfo.NeType), paramName) + resBytes, err := fetch.Get(neUrl, nil, 60_000) if err != nil { - logger.Warnf("NeConfigInfo %s Get \"%s\"", err.Error(), neUrl) - return nil, err + logger.Warnf("NeConfigInfo Get \"%s\"", neUrl) + logger.Errorf("NeConfigInfo %s", err.Error()) + return nil, fmt.Errorf("NeService Config Info API Error") } // 序列化结果 var resData map[string]any err = json.Unmarshal(resBytes, &resData) if err != nil { - logger.Warnf("NeConfigInfo Unmarshal %s", err.Error()) + logger.Errorf("NeConfigInfo Unmarshal %s", err.Error()) + return nil, err + } + return resData, nil +} + +// NeConfigUpdate 网元配置更新 +func NeConfigUpdate(neInfo model.NeInfo, paramName, loc string, data map[string]any) (map[string]any, error) { + // array需要层级 + if loc != "" { + loc = fmt.Sprintf("?loc=%v", loc) + } + // 网元参数配置新增(array) + neUrl := fmt.Sprintf("http://%s:%d/api/rest/systemManagement/v1/elementType/%s/objectType/config/%s%s", neInfo.IP, neInfo.Port, strings.ToLower(neInfo.NeType), paramName, loc) + resBytes, err := fetch.PutJSON(neUrl, data, nil) + var resData map[string]any + if err != nil { + errStr := err.Error() + logger.Warnf("NeConfigUpdate Put \"%s\"", neUrl) + if strings.HasPrefix(errStr, "201") || strings.HasPrefix(errStr, "204") { + return resData, nil + } + logger.Errorf("NeConfigUpdate %s", errStr) + return nil, fmt.Errorf("NeService Config Update API Error") + } + + // 200 成功无数据时 + if len(resBytes) == 0 { + return resData, nil + } + + // 序列化结果 + err = json.Unmarshal(resBytes, &resData) + if err != nil { + logger.Errorf("NeConfigUpdate Unmarshal %s", err.Error()) + return nil, err + } + return resData, nil +} + +// NeConfigInstall 网元配置新增 array +func NeConfigInstall(neInfo model.NeInfo, paramName, loc string, data map[string]any) (map[string]any, error) { + // 网元参数配置新增(array) + neUrl := fmt.Sprintf("http://%s:%d/api/rest/systemManagement/v1/elementType/%s/objectType/config/%s?loc=%v", neInfo.IP, neInfo.Port, strings.ToLower(neInfo.NeType), paramName, loc) + resBytes, err := fetch.PostJSON(neUrl, data, nil) + var resData map[string]any + if err != nil { + errStr := err.Error() + logger.Warnf("NeConfigInfoAdd Post \"%s\"", neUrl) + if strings.HasPrefix(errStr, "201") || strings.HasPrefix(errStr, "204") { + return resData, nil + } + logger.Errorf("NeConfigInfoAdd %s", errStr) + return nil, fmt.Errorf("NeService Config Add API Error") + } + + // 200 成功无数据时 + if len(resBytes) == 0 { + return resData, nil + } + + // 序列化结果 + err = json.Unmarshal(resBytes, &resData) + if err != nil { + logger.Errorf("NeConfigInfoAdd Unmarshal %s", err.Error()) + return nil, err + } + return resData, nil +} + +// NeConfigDelete 网元配置删除 array +func NeConfigDelete(neInfo model.NeInfo, paramName, loc string) (map[string]any, error) { + // 网元参数配置删除(array) + neUrl := fmt.Sprintf("http://%s:%d/api/rest/systemManagement/v1/elementType/%s/objectType/config/%s?loc=%v", neInfo.IP, neInfo.Port, strings.ToLower(neInfo.NeType), paramName, loc) + resBytes, err := fetch.Delete(neUrl, nil) + var resData map[string]any + if err != nil { + errStr := err.Error() + logger.Warnf("NeConfigDelete Delete \"%s\"", neUrl) + if strings.HasPrefix(errStr, "201") || strings.HasPrefix(errStr, "204") { + return resData, nil + } + logger.Errorf("NeConfigDelete %s", errStr) + return nil, fmt.Errorf("NeService Config Update API Error") + } + + // 200 成功无数据时 + if len(resBytes) == 0 { + return resData, nil + } + + // 序列化结果 + err = json.Unmarshal(resBytes, &resData) + if err != nil { + logger.Errorf("NeConfigInfoDel Unmarshal %s", err.Error()) return nil, err } return resData, nil diff --git a/src/modules/network_element/fetch_link/ne_state.go b/src/modules/network_element/fetch_link/ne_state.go index 5915b96..d15f5b2 100644 --- a/src/modules/network_element/fetch_link/ne_state.go +++ b/src/modules/network_element/fetch_link/ne_state.go @@ -17,15 +17,15 @@ func NeState(neInfo model.NeInfo) (map[string]any, error) { neUrl := fmt.Sprintf("http://%s:%d/api/rest/systemManagement/v1/elementType/%s/objectType/systemState", neInfo.IP, neInfo.Port, strings.ToLower(neInfo.NeType)) resBytes, err := fetch.Get(neUrl, nil, 1000) if err != nil { - logger.Warnf("NeState %s", err.Error()) - return nil, err + logger.Errorf("NeState %s", err.Error()) + return nil, fmt.Errorf("NeService System State API Error") } // 序列化结果 var resData map[string]any err = json.Unmarshal(resBytes, &resData) if err != nil { - logger.Warnf("NeState Unmarshal %s", err.Error()) + logger.Errorf("NeState Unmarshal %s", err.Error()) return nil, err } diff --git a/src/modules/network_element/fetch_link/udm.go b/src/modules/network_element/fetch_link/udm.go new file mode 100644 index 0000000..d3f13bd --- /dev/null +++ b/src/modules/network_element/fetch_link/udm.go @@ -0,0 +1,37 @@ +package fetchlink + +import ( + "encoding/json" + "fmt" + "strings" + + "nms_cxy/src/framework/logger" + "nms_cxy/src/framework/utils/fetch" +) + +// UDMImportAuth UDM导入鉴权数据 +// +// data参数 {path:"服务器文件路径", k4:"可选,k4为空时Ki不加密。"} +func UDMImportAuth(udmIP string, data map[string]any) (string, error) { + // 网元参数配置新增(array) + neUrl := fmt.Sprintf("http://%s:8080/ue-manage/v1/import-auth", udmIP) + resBytes, err := fetch.PostJSON(neUrl, data, nil) + var resData map[string]string + if err != nil { + errStr := err.Error() + logger.Warnf("UDMImportAuth Post \"%s\"", neUrl) + logger.Errorf("UDMImportAuth %s", errStr) + return "", fmt.Errorf("NeService UDM API Error") + } + + // 序列化结果 + err = json.Unmarshal(resBytes, &resData) + if err != nil { + logger.Errorf("UDMImportAuth Unmarshal %s", err.Error()) + return "", err + } + if v, ok := resData["code"]; ok && v == "00000" { + return strings.TrimSpace(strings.ToLower(resData["message"])), nil + } + return "", fmt.Errorf(resData["message"]) +} diff --git a/src/modules/network_element/model/ne_config.go b/src/modules/network_element/model/ne_config.go index dcc7e05..99ae994 100644 --- a/src/modules/network_element/model/ne_config.go +++ b/src/modules/network_element/model/ne_config.go @@ -1,21 +1,23 @@ package model -// NeConfig 网元参数配置可用属性值 +// NeConfig 网元_参数配置可用属性值 type NeConfig struct { - ID string `json:"id" gorm:"id"` - NeType string `json:"neType" binding:"required" gorm:"ne_type"` // 网元类型 - NeId string `json:"-" gorm:"ne_id"` - TopTag string `json:"topTag" binding:"required" gorm:"top_tag"` - TopDisplay string `json:"topDisplay" binding:"required" gorm:"top_display"` - Method string `json:"method" gorm:"method"` // 操作属性 get只读强制不可编辑删除 put可编辑 delete可删除 post可新增 - ParamJSONStr string `json:"-" gorm:"param_json"` // accesss属性控制:只读read-only/read/ro 读写read-write + ID string `json:"id" gorm:"column:id;primaryKey;autoIncrement"` + NeType string `json:"neType" binding:"required" gorm:"ne_type"` // 网元类型 + ParamName string `json:"paramName" binding:"required" gorm:"param_name"` // 参数名 + ParamDisplay string `json:"paramDisplay" binding:"required" gorm:"param_display"` // 参数显示名 + ParamType string `json:"paramType" gorm:"param_type"` // 参数类型 list列表单层 array数组多层 + ParamJson string `json:"-" gorm:"param_json"` // accesss属性控制:只读read-only/read/ro 读写read-write + ParamSort int64 `json:"paramSort" gorm:"param_sort"` // 参数排序 + ParamPerms string `json:"paramPerms" gorm:"param_perms"` // 操作权限 get只读 put可编辑 delete可删除 post可新增 + UpdateTime int64 `json:"updateTime" gorm:"update_time"` // 更新时间 // ====== 非数据库字段属性 ====== - ParamData map[string]any `json:"paramData,omitempty" binding:"required" gorm:"-"` // 与ParamJSONStr配合转换 + ParamData []map[string]any `json:"paramData,omitempty" binding:"required" gorm:"-"` // 与ParamJSONStr配合转换 } // TableName 表名称 func (*NeConfig) TableName() string { - return "param_config" + return "ne_config" } diff --git a/src/modules/network_element/model/ne_config_backup.go b/src/modules/network_element/model/ne_config_backup.go new file mode 100644 index 0000000..d8d5787 --- /dev/null +++ b/src/modules/network_element/model/ne_config_backup.go @@ -0,0 +1,23 @@ +package model + +// NeConfigBackup 网元配置文件备份记录 ne_config_backup +type NeConfigBackup struct { + ID string `json:"id" gorm:"column:id;primaryKey;autoIncrement"` + NeType string `json:"neType" gorm:"ne_type"` // 网元类型 + NeId string `json:"neId" gorm:"ne_id"` // 网元ID + Name string `json:"name" gorm:"name"` // 压缩包名称 + Path string `json:"path" gorm:"path"` // 压缩包位置 + Remark string `json:"remark" gorm:"remark"` // 备注 + CreateBy string `json:"createBy" gorm:"create_by"` // 创建者 + CreateTime int64 `json:"createTime" gorm:"create_time"` // 创建时间 + UpdateBy string `json:"updateBy" gorm:"update_by"` // 更新者 + UpdateTime int64 `json:"updateTime" gorm:"update_time"` // 更新时间 + + // ====== 非数据库字段属性 ====== + +} + +// TableName 表名称 +func (*NeConfigBackup) TableName() string { + return "ne_config_backup" +} diff --git a/src/modules/network_element/ne_config_test.go b/src/modules/network_element/ne_config_test.go new file mode 100644 index 0000000..ad23ed9 --- /dev/null +++ b/src/modules/network_element/ne_config_test.go @@ -0,0 +1,231 @@ +package networkelement + +import ( + "encoding/json" + "fmt" + "log" + "os" + "path/filepath" + "sort" + "strconv" + "strings" + "testing" + "time" + + "nms_cxy/src/modules/network_element/model" + + "gopkg.in/yaml.v3" + "gorm.io/driver/mysql" + "gorm.io/gorm" + "gorm.io/gorm/logger" +) + +const ( + // 数据库 + DbHost = "192.168.8.58" + DbPort = 33066 + DbUser = "root" + DbPassswd = "1000omc@kp!" + DbName = "omc_db" + // 配置文件路径 + configParamDir = "../../../config/param" + // configParamFile = "*" // 目录下全部更新 + configParamFile = "cbc_param_config.yaml" // 单文件更新 +) + +func TestEncrypt(t *testing.T) { + fileNameList, err := getDirFileNameList(configParamDir) + if err != nil { + log.Fatal(err) + return + } + + if configParamFile == "*" { + for _, v := range fileNameList { + params := parseData(filepath.Join(configParamDir, v)) + if params == nil { + return + } + saveData(params) + } + } else { + params := parseData(filepath.Join(configParamDir, configParamFile)) + if params == nil { + return + } + saveData(params) + } +} + +// ========= Main ============= + +// parseData 文件转map数据 +func parseData(filePaht string) []map[string]string { + data, err := parseStrToMap(filePaht) + if err != nil { + log.Printf("parseStrToMap => %s", err.Error()) + return nil + } + params, err := parseParamConfig(data) + if err != nil { + log.Printf("parseParamConfig => %s", err.Error()) + return nil + } + return params +} + +// saveData 保存数据 +func saveData(params []map[string]string) { + // 定义排序函数 + sort.Slice(params, func(i, j int) bool { + paramSortI := params[i]["paramSort"] + if len(paramSortI) == 0 || paramSortI == "" { + paramSortI = "0" + } + paramSortJ := params[j]["paramSort"] + if len(paramSortJ) == 0 || paramSortJ == "" { + paramSortJ = "0" + } + // 将 age 字段转换为整数进行比较 + si, _ := strconv.Atoi(paramSortI) + sj, _ := strconv.Atoi(paramSortJ) + return si < sj + }) + // 遍历插入 + for _, v := range params { + paramSort := v["paramSort"] + if len(paramSort) == 0 || paramSort == "" { + paramSort = "0" + } + sort, err := strconv.ParseInt(paramSort, 10, 64) + if err != nil { + sort = 0 + } + + neConfig := model.NeConfig{ + NeType: v["neType"], + ParamName: v["paramName"], + ParamDisplay: v["paramDisplay"], + ParamType: v["paramType"], + ParamJson: v["paramJson"], + ParamPerms: v["paramPerms"], + ParamSort: sort, + } + neConfig.ID = saveDB(neConfig) + log.Println(neConfig.ID, neConfig.NeType, neConfig.ParamDisplay) + } +} + +// ========= DB ============= + +var gdb *gorm.DB + +// connDB 连接到数据库 +func connDB() *gorm.DB { + if gdb != nil { + return gdb + } + + dsn := fmt.Sprintf("%s:%s@tcp(%s:%d)/%s?charset=utf8mb4&parseTime=True&loc=Local", DbUser, DbPassswd, DbHost, DbPort, DbName) + newLogger := logger.New( + log.New(os.Stdout, "\r\n", log.LstdFlags), // io writer + logger.Config{ + SlowThreshold: time.Minute, // Slow SQL threshold + LogLevel: logger.Error, // Log level + IgnoreRecordNotFoundError: true, // Ignore ErrRecordNotFound error for logger + ParameterizedQueries: true, // Don't include params in the SQL log + Colorful: false, // Disable color + }, + ) + db, err := gorm.Open(mysql.Open(dsn), &gorm.Config{Logger: newLogger}) + if err != nil { + log.Fatalln(err) + } + gdb = db + return gdb +} + +// saveDB 表插入或更新 +func saveDB(s model.NeConfig) string { + db := connDB() + // 检查是否存在 + var id string + db.Raw("SELECT id FROM ne_config WHERE ne_type = ? AND param_name = ?", s.NeType, s.ParamName).Scan(&id) + // 更新时间 + s.UpdateTime = time.Now().UnixMilli() + if id != "" { + s.ID = id + db.Save(&s) + } else { + db.Create(&s) + } + return s.ID +} + +// ========= Utils ============= + +// getDirFileNameList 获取文件目录下所有文件名称,不含目录名称 +func getDirFileNameList(dirPath string) ([]string, error) { + fileNames := []string{} + + dir, err := os.Open(dirPath) + if err != nil { + return fileNames, nil + } + defer dir.Close() + + fileInfos, err := dir.Readdir(-1) + if err != nil { + return fileNames, err + } + + for _, fileInfo := range fileInfos { + if fileInfo.Mode().IsRegular() { + fileNames = append(fileNames, fileInfo.Name()) + } + } + + return fileNames, nil +} + +// parseStrToMap 解析内容string到map +func parseStrToMap(filePath string) (map[string]any, error) { + // 读取文件内容 + bytes, err := os.ReadFile(filePath) + if err != nil { + return nil, err + } + content := string(bytes) + var configMap map[string]any + err = yaml.Unmarshal([]byte(content), &configMap) + + return configMap, err +} + +// parseParamConfig 解析内容文件数据 +func parseParamConfig(data map[string]any) ([]map[string]string, error) { + paramMapArr := make([]map[string]string, 0) + for k, v := range data { + for ik, iv := range v.(map[string]any) { + itemMap := make(map[string]string) + itemMap["neType"] = strings.ToUpper(k) + itemMap["paramName"] = ik + for iik, iiv := range iv.(map[string]any) { + switch iik { + case "display": + itemMap["paramDisplay"] = iiv.(string) + case "sort": + itemMap["paramSort"] = fmt.Sprint(iiv) + case "perms", "method": + itemMap["paramPerms"] = iiv.(string) + case "list", "array": // 参数类型为数组 + itemMap["paramType"] = iik + strByte, _ := json.Marshal(iiv) + itemMap["paramJson"] = string(strByte) + } + } + paramMapArr = append(paramMapArr, itemMap) + } + } + return paramMapArr, nil +} diff --git a/src/modules/network_element/network_element.go b/src/modules/network_element/network_element.go index 68508bc..9772a35 100644 --- a/src/modules/network_element/network_element.go +++ b/src/modules/network_element/network_element.go @@ -80,15 +80,18 @@ func Setup(router *gin.Engine) { controller.NewNeInfo.List, ) neInfoGroup.GET("/:infoId", + middleware.CryptoApi(false, true), middleware.PreAuthorize(nil), controller.NewNeInfo.Info, ) neInfoGroup.POST("", + middleware.CryptoApi(true, true), middleware.PreAuthorize(nil), collectlogs.OperateLog(collectlogs.OptionNew("log.operate.title.neInfo", collectlogs.BUSINESS_TYPE_INSERT)), controller.NewNeInfo.Add, ) neInfoGroup.PUT("", + middleware.CryptoApi(true, true), middleware.PreAuthorize(nil), collectlogs.OperateLog(collectlogs.OptionNew("log.operate.title.neInfo", collectlogs.BUSINESS_TYPE_UPDATE)), controller.NewNeInfo.Edit, @@ -108,6 +111,7 @@ func Setup(router *gin.Engine) { controller.NewNeHost.List, ) neHostGroup.GET("/:hostId", + middleware.CryptoApi(false, true), middleware.PreAuthorize(nil), controller.NewNeHost.Info, ) @@ -260,6 +264,7 @@ func Setup(router *gin.Engine) { // 网元参数配置 neConfigGroup := neGroup.Group("/config") { + // 网元参数配置可用属性值 neConfigGroup.GET("/list", middleware.PreAuthorize(nil), controller.NewNeConfig.List, @@ -278,7 +283,7 @@ func Setup(router *gin.Engine) { collectlogs.OperateLog(collectlogs.OptionNew("log.operate.title.neConfig", collectlogs.BUSINESS_TYPE_UPDATE)), controller.NewNeConfig.Edit, ) - neConfigGroup.DELETE("/:ids", + neConfigGroup.DELETE("", middleware.PreAuthorize(nil), collectlogs.OperateLog(collectlogs.OptionNew("log.operate.title.neConfig", collectlogs.BUSINESS_TYPE_DELETE)), controller.NewNeConfig.Remove, @@ -287,9 +292,58 @@ func Setup(router *gin.Engine) { middleware.PreAuthorize(nil), controller.NewNeConfig.ListByNeType, ) + // 网元参数配置数据 neConfigGroup.GET("/data", middleware.PreAuthorize(nil), - controller.NewNeConfig.Data, + controller.NewNeConfig.DataInfo, + ) + neConfigGroup.PUT("/data", + middleware.PreAuthorize(nil), + collectlogs.OperateLog(collectlogs.OptionNew("log.operate.title.neConfig", collectlogs.BUSINESS_TYPE_UPDATE)), + controller.NewNeConfig.DataEdit, + ) + neConfigGroup.POST("/data", + middleware.PreAuthorize(nil), + collectlogs.OperateLog(collectlogs.OptionNew("log.operate.title.neConfig", collectlogs.BUSINESS_TYPE_INSERT)), + controller.NewNeConfig.DataAdd, + ) + neConfigGroup.DELETE("/data", + middleware.PreAuthorize(nil), + collectlogs.OperateLog(collectlogs.OptionNew("log.operate.title.neConfig", collectlogs.BUSINESS_TYPE_DELETE)), + controller.NewNeConfig.DataRemove, + ) + } + + // 网元配置文件备份记录 + neConfigBackupGroup := neGroup.Group("/config/backup") + { + neConfigBackupGroup.GET("/list", + middleware.PreAuthorize(nil), + controller.NewNeConfigBackup.List, + ) + neConfigBackupGroup.GET("/download", + middleware.PreAuthorize(nil), + controller.NewNeConfigBackup.Download, + ) + neConfigBackupGroup.PUT("", + middleware.PreAuthorize(map[string][]string{"hasPerms": {"ne:neConfigBackup:edit"}}), + collectlogs.OperateLog(collectlogs.OptionNew("log.operate.title.neConfigBackup", collectlogs.BUSINESS_TYPE_UPDATE)), + controller.NewNeConfigBackup.Edit, + ) + neConfigBackupGroup.DELETE("", + middleware.PreAuthorize(map[string][]string{"hasPerms": {"ne:neConfigBackup:remove"}}), + collectlogs.OperateLog(collectlogs.OptionNew("log.operate.title.neConfigBackup", collectlogs.BUSINESS_TYPE_DELETE)), + controller.NewNeConfigBackup.Remove, + ) + neConfigBackupGroup.POST("/import", + middleware.PreAuthorize(nil), + collectlogs.OperateLog(collectlogs.OptionNew("log.operate.title.neConfigBackup", collectlogs.BUSINESS_TYPE_IMPORT)), + controller.NewNeConfigBackup.Import, + ) + neConfigBackupGroup.POST("/export", + middleware.PreAuthorize(nil), + collectlogs.OperateLog(collectlogs.OptionNew("log.operate.title.neConfigBackup", collectlogs.BUSINESS_TYPE_EXPORT)), + controller.NewNeConfigBackup.Export, ) } } diff --git a/src/modules/network_element/repository/ne_config.impl.go b/src/modules/network_element/repository/ne_config.impl.go index f60c818..bbac241 100644 --- a/src/modules/network_element/repository/ne_config.impl.go +++ b/src/modules/network_element/repository/ne_config.impl.go @@ -2,6 +2,7 @@ package repository import ( "strings" + "time" "nms_cxy/src/framework/datasource" "nms_cxy/src/framework/logger" @@ -12,18 +13,18 @@ import ( // NewNeConfigImpl 网元参数配置可用属性值 实例化数据层 var NewNeConfigImpl = &NeConfigImpl{ - selectSql: `select - id, ne_type, ne_id, top_tag, top_display, method, param_json - from param_config`, + selectSql: `select id, ne_type, param_name, param_display, param_type, param_json, param_sort, param_perms, update_time from ne_config`, resultMap: map[string]string{ - "id": "ID", - "ne_type": "NeType", - "ne_id": "NeId", - "top_tag": "TopTag", - "top_display": "TopDisplay", - "method": "Method", - "param_json": "ParamJSONStr", + "id": "ID", + "ne_type": "NeType", + "param_name": "ParamName", + "param_display": "ParamDisplay", + "param_type": "ParamType", + "param_json": "ParamJson", + "param_sort": "ParamSort", + "param_perms": "ParamPerms", + "update_time": "UpdateTime", }, } @@ -59,8 +60,8 @@ func (r *NeConfigImpl) SelectPage(query map[string]any) map[string]any { conditions = append(conditions, "ne_type = ?") params = append(params, v) } - if v, ok := query["topTag"]; ok && v != "" { - conditions = append(conditions, "top_tag = ?") + if v, ok := query["paramName"]; ok && v != "" { + conditions = append(conditions, "param_name = ?") params = append(params, v) } @@ -76,7 +77,7 @@ func (r *NeConfigImpl) SelectPage(query map[string]any) map[string]any { } // 查询数量 长度为0直接返回 - totalSql := "select count(id) as 'total' from param_config" + totalSql := "select count(id) as 'total' from ne_config" totalRows, err := datasource.RawDB("", totalSql+whereSql, params) if err != nil { logger.Errorf("total err => %v", err) @@ -117,9 +118,9 @@ func (r *NeConfigImpl) SelectList(param model.NeConfig) []model.NeConfig { conditions = append(conditions, "ne_type = ?") params = append(params, param.NeType) } - if param.TopTag != "" { - conditions = append(conditions, "top_tag = ?") - params = append(params, param.TopTag) + if param.ParamName != "" { + conditions = append(conditions, "param_name = ?") + params = append(params, param.ParamName) } // 构建查询条件语句 @@ -129,7 +130,7 @@ func (r *NeConfigImpl) SelectList(param model.NeConfig) []model.NeConfig { } // 查询数据 - querySql := r.selectSql + whereSql + " order by id asc " + querySql := r.selectSql + whereSql + " order by param_sort asc " results, err := datasource.RawDB("", querySql, params) if err != nil { logger.Errorf("query err => %v", err) @@ -160,25 +161,27 @@ func (r *NeConfigImpl) Insert(param model.NeConfig) string { if param.NeType != "" { params["ne_type"] = param.NeType } - if param.NeId != "" { - params["ne_id"] = param.NeId + if param.ParamName != "" { + params["param_name"] = param.ParamName } - if param.TopTag != "" { - params["top_tag"] = param.TopTag + if param.ParamDisplay != "" { + params["param_display"] = param.ParamDisplay } - if param.TopDisplay != "" { - params["top_display"] = param.TopDisplay + if param.ParamType != "" { + params["param_type"] = param.ParamType } - if param.Method != "" { - params["method"] = param.Method + if param.ParamJson != "" { + params["param_json"] = param.ParamJson } - if param.ParamJSONStr != "" { - params["param_json"] = param.ParamJSONStr + params["param_sort"] = param.ParamSort + if param.ParamPerms != "" { + params["param_perms"] = param.ParamPerms } + params["update_time"] = time.Now().UnixMilli() // 构建执行语句 keys, placeholder, values := repo.KeyPlaceholderValueByInsert(params) - sql := "insert into param_config (" + strings.Join(keys, ",") + ")values(" + placeholder + ")" + sql := "insert into ne_config (" + strings.Join(keys, ",") + ")values(" + placeholder + ")" db := datasource.DefaultDB() // 开启事务 @@ -210,25 +213,27 @@ func (r *NeConfigImpl) Update(param model.NeConfig) int64 { if param.NeType != "" { params["ne_type"] = param.NeType } - if param.NeId != "" { - params["ne_id"] = param.NeId + if param.ParamName != "" { + params["param_name"] = param.ParamName } - if param.TopTag != "" { - params["top_tag"] = param.TopTag + if param.ParamDisplay != "" { + params["param_display"] = param.ParamDisplay } - if param.TopDisplay != "" { - params["top_display"] = param.TopDisplay + if param.ParamType != "" { + params["param_type"] = param.ParamType } - if param.Method != "" { - params["method"] = param.Method + if param.ParamJson != "" { + params["param_json"] = param.ParamJson } - if param.ParamJSONStr != "" { - params["param_json"] = param.ParamJSONStr + params["param_sort"] = param.ParamSort + if param.ParamPerms != "" { + params["param_perms"] = param.ParamPerms } + params["update_time"] = time.Now().UnixMilli() // 构建执行语句 keys, values := repo.KeyValueByUpdate(params) - sql := "update param_config set " + strings.Join(keys, ",") + " where id = ?" + sql := "update ne_config set " + strings.Join(keys, ",") + " where id = ?" // 执行更新 values = append(values, param.ID) @@ -243,7 +248,7 @@ func (r *NeConfigImpl) Update(param model.NeConfig) int64 { // DeleteByIds 批量删除信息 func (r *NeConfigImpl) DeleteByIds(ids []string) int64 { placeholder := repo.KeyPlaceholderByQuery(len(ids)) - sql := "delete from param_config where id in (" + placeholder + ")" + sql := "delete from ne_config where id in (" + placeholder + ")" parameters := repo.ConvertIdsSlice(ids) results, err := datasource.ExecDB("", sql, parameters) if err != nil { diff --git a/src/modules/network_element/repository/ne_config_backup.go b/src/modules/network_element/repository/ne_config_backup.go new file mode 100644 index 0000000..83e4b1d --- /dev/null +++ b/src/modules/network_element/repository/ne_config_backup.go @@ -0,0 +1,24 @@ +package repository + +import "nms_cxy/src/modules/network_element/model" + +// INeConfigBackup 网元配置文件备份记录 数据层接口 +type INeConfigBackup interface { + // SelectPage 根据条件分页查询字典类型 + SelectPage(query map[string]any) map[string]any + + // SelectList 根据实体查询 + SelectList(item model.NeConfigBackup) []model.NeConfigBackup + + // SelectByIds 通过ID查询 + SelectByIds(ids []string) []model.NeConfigBackup + + // Insert 新增信息 + Insert(item model.NeConfigBackup) string + + // Update 修改信息 + Update(item model.NeConfigBackup) int64 + + // DeleteByIds 批量删除信息 + DeleteByIds(ids []string) int64 +} diff --git a/src/modules/network_element/repository/ne_config_backup.impl.go b/src/modules/network_element/repository/ne_config_backup.impl.go new file mode 100644 index 0000000..941cb75 --- /dev/null +++ b/src/modules/network_element/repository/ne_config_backup.impl.go @@ -0,0 +1,262 @@ +package repository + +import ( + "strings" + "time" + + "nms_cxy/src/framework/datasource" + "nms_cxy/src/framework/logger" + "nms_cxy/src/framework/utils/parse" + "nms_cxy/src/framework/utils/repo" + "nms_cxy/src/modules/network_element/model" +) + +// 实例化数据层 NewNeConfigBackupImpl 结构体 +var NewNeConfigBackupImpl = &NeConfigBackupImpl{ + selectSql: `select + id, ne_type, ne_id, name, path, remark, create_by, create_time, update_by, update_time + from ne_config_backup`, + + resultMap: map[string]string{ + "id": "ID", + "ne_type": "NeType", + "ne_id": "NeId", + "name": "Name", + "path": "Path", + "remark": "Remark", + "create_by": "CreateBy", + "create_time": "CreateTime", + "update_by": "UpdateBy", + "update_time": "UpdateTime", + }, +} + +// NeConfigBackupImpl 网元配置文件备份记录 数据层处理 +type NeConfigBackupImpl struct { + // 查询视图对象SQL + selectSql string + // 结果字段与实体映射 + resultMap map[string]string +} + +// convertResultRows 将结果记录转实体结果组 +func (r *NeConfigBackupImpl) convertResultRows(rows []map[string]any) []model.NeConfigBackup { + arr := make([]model.NeConfigBackup, 0) + for _, row := range rows { + item := model.NeConfigBackup{} + for key, value := range row { + if keyMapper, ok := r.resultMap[key]; ok { + repo.SetFieldValue(&item, keyMapper, value) + } + } + arr = append(arr, item) + } + return arr +} + +// SelectPage 根据条件分页查询字典类型 +func (r *NeConfigBackupImpl) SelectPage(query map[string]any) map[string]any { + // 查询条件拼接 + var conditions []string + var params []any + if v, ok := query["neType"]; ok && v != "" { + conditions = append(conditions, "ne_type = ?") + params = append(params, strings.Trim(v.(string), " ")) + } + if v, ok := query["neId"]; ok && v != "" { + conditions = append(conditions, "ne_id = ?") + params = append(params, strings.Trim(v.(string), " ")) + } + if v, ok := query["name"]; ok && v != "" { + conditions = append(conditions, "name like concat(concat('%', ?), '%')") + params = append(params, strings.Trim(v.(string), " ")) + } + + // 构建查询条件语句 + whereSql := "" + if len(conditions) > 0 { + whereSql += " where " + strings.Join(conditions, " and ") + } + + result := map[string]any{ + "total": 0, + "rows": []model.NeHost{}, + } + + // 查询数量 长度为0直接返回 + totalSql := "select count(1) as 'total' from ne_config_backup" + totalRows, err := datasource.RawDB("", totalSql+whereSql, params) + if err != nil { + logger.Errorf("total err => %v", err) + return result + } + total := parse.Number(totalRows[0]["total"]) + if total == 0 { + return result + } else { + result["total"] = total + } + + // 分页 + pageNum, pageSize := repo.PageNumSize(query["pageNum"], query["pageSize"]) + pageSql := " order by id desc limit ?,? " + params = append(params, pageNum*pageSize) + params = append(params, pageSize) + + // 查询数据 + querySql := r.selectSql + whereSql + pageSql + results, err := datasource.RawDB("", querySql, params) + if err != nil { + logger.Errorf("query err => %v", err) + return result + } + + // 转换实体 + result["rows"] = r.convertResultRows(results) + return result +} + +// SelectList 根据实体查询 +func (r *NeConfigBackupImpl) SelectList(item model.NeConfigBackup) []model.NeConfigBackup { + // 查询条件拼接 + var conditions []string + var params []any + if item.NeType != "" { + conditions = append(conditions, "ne_type = ?") + params = append(params, item.NeType) + } + if item.NeId != "" { + conditions = append(conditions, "ne_id = ?") + params = append(params, item.NeId) + } + + // 构建查询条件语句 + whereSql := "" + if len(conditions) > 0 { + whereSql += " where " + strings.Join(conditions, " and ") + } + + // 查询数据 + querySql := r.selectSql + whereSql + " order by id desc " + results, err := datasource.RawDB("", querySql, params) + if err != nil { + logger.Errorf("query err => %v", err) + } + + // 转换实体 + return r.convertResultRows(results) +} + +// SelectByIds 通过ID查询 +func (r *NeConfigBackupImpl) SelectByIds(cmdIds []string) []model.NeConfigBackup { + placeholder := repo.KeyPlaceholderByQuery(len(cmdIds)) + querySql := r.selectSql + " where id in (" + placeholder + ")" + parameters := repo.ConvertIdsSlice(cmdIds) + results, err := datasource.RawDB("", querySql, parameters) + if err != nil { + logger.Errorf("query err => %v", err) + return []model.NeConfigBackup{} + } + // 转换实体 + return r.convertResultRows(results) +} + +// Insert 新增信息 +func (r *NeConfigBackupImpl) Insert(item model.NeConfigBackup) string { + // 参数拼接 + params := make(map[string]any) + if item.NeType != "" { + params["ne_type"] = item.NeType + } + if item.NeId != "" { + params["ne_id"] = item.NeId + } + if item.Name != "" { + params["name"] = item.Name + } + if item.Path != "" { + params["path"] = item.Path + } + if item.Remark != "" { + params["remark"] = item.Remark + } + if item.CreateBy != "" { + params["create_by"] = item.CreateBy + params["create_time"] = time.Now().UnixMilli() + } + + // 构建执行语句 + keys, placeholder, values := repo.KeyPlaceholderValueByInsert(params) + sql := "insert into ne_config_backup (" + strings.Join(keys, ",") + ")values(" + placeholder + ")" + + db := datasource.DefaultDB() + // 开启事务 + tx := db.Begin() + // 执行插入 + err := tx.Exec(sql, values...).Error + if err != nil { + logger.Errorf("insert row : %v", err.Error()) + tx.Rollback() + return "" + } + // 获取生成的自增 ID + var insertedID string + err = tx.Raw("select last_insert_id()").Row().Scan(&insertedID) + if err != nil { + logger.Errorf("insert last id : %v", err.Error()) + tx.Rollback() + return "" + } + // 提交事务 + tx.Commit() + return insertedID +} + +// Update 修改信息 +func (r *NeConfigBackupImpl) Update(item model.NeConfigBackup) int64 { + // 参数拼接 + params := make(map[string]any) + if item.NeType != "" { + params["ne_type"] = item.NeType + } + if item.NeId != "" { + params["ne_id"] = item.NeId + } + if item.Name != "" { + params["name"] = item.Name + } + if item.Path != "" { + params["path"] = item.Path + } + params["remark"] = item.Remark + if item.UpdateBy != "" { + params["update_by"] = item.UpdateBy + params["update_time"] = time.Now().UnixMilli() + } + + // 构建执行语句 + keys, values := repo.KeyValueByUpdate(params) + sql := "update ne_config_backup set " + strings.Join(keys, ",") + " where id = ?" + + // 执行更新 + values = append(values, item.ID) + rows, err := datasource.ExecDB("", sql, values) + if err != nil { + logger.Errorf("update row : %v", err.Error()) + return 0 + } + return rows +} + +// DeleteByIds 批量删除信息 +func (r *NeConfigBackupImpl) DeleteByIds(ids []string) int64 { + placeholder := repo.KeyPlaceholderByQuery(len(ids)) + sql := "delete from ne_config_backup where id in (" + placeholder + ")" + parameters := repo.ConvertIdsSlice(ids) + results, err := datasource.ExecDB("", sql, parameters) + if err != nil { + logger.Errorf("delete err => %v", err) + return 0 + } + return results +} diff --git a/src/modules/network_element/repository/ne_host.impl.go b/src/modules/network_element/repository/ne_host.impl.go index 6ae63b8..adae7b4 100644 --- a/src/modules/network_element/repository/ne_host.impl.go +++ b/src/modules/network_element/repository/ne_host.impl.go @@ -7,7 +7,6 @@ import ( "nms_cxy/src/framework/datasource" "nms_cxy/src/framework/logger" - "nms_cxy/src/framework/utils/crypto" "nms_cxy/src/framework/utils/parse" "nms_cxy/src/framework/utils/repo" "nms_cxy/src/modules/network_element/model" @@ -170,32 +169,7 @@ func (r *NeHostImpl) SelectByIds(hostIds []string) []model.NeHost { return []model.NeHost{} } // 转换实体 - rows := r.convertResultRows(results) - arr := &rows - for i := range *arr { - passwordDe, err := crypto.StringDecryptByAES((*arr)[i].Password) - if err != nil { - logger.Errorf("selectById %s decrypt: %v", (*arr)[i].HostID, err.Error()) - (*arr)[i].Password = "" - } else { - (*arr)[i].Password = passwordDe - } - privateKeyDe, err := crypto.StringDecryptByAES((*arr)[i].PrivateKey) - if err != nil { - logger.Errorf("selectById %s decrypt: %v", (*arr)[i].HostID, err.Error()) - (*arr)[i].PrivateKey = "" - } else { - (*arr)[i].PrivateKey = privateKeyDe - } - passPhraseDe, err := crypto.StringDecryptByAES((*arr)[i].PassPhrase) - if err != nil { - logger.Errorf("selectById %s decrypt: %v", (*arr)[i].HostID, err.Error()) - (*arr)[i].PassPhrase = "" - } else { - (*arr)[i].PassPhrase = passPhraseDe - } - } - return rows + return r.convertResultRows(results) } // CheckUniqueNeHost 校验主机是否唯一 @@ -263,28 +237,13 @@ func (r *NeHostImpl) Insert(neHost model.NeHost) string { params["auth_mode"] = neHost.AuthMode } if neHost.Password != "" { - passwordEn, err := crypto.StringEncryptByAES(neHost.Password) - if err != nil { - logger.Errorf("insert encrypt: %v", err.Error()) - return "" - } - params["password"] = passwordEn + params["password"] = neHost.Password } if neHost.PrivateKey != "" { - privateKeyEn, err := crypto.StringEncryptByAES(neHost.PrivateKey) - if err != nil { - logger.Errorf("insert encrypt: %v", err.Error()) - return "" - } - params["private_key"] = privateKeyEn + params["private_key"] = neHost.PrivateKey } if neHost.PassPhrase != "" { - passPhraseEn, err := crypto.StringEncryptByAES(neHost.PassPhrase) - if err != nil { - logger.Errorf("insert encrypt: %v", err.Error()) - return "" - } - params["pass_phrase"] = passPhraseEn + params["pass_phrase"] = neHost.PassPhrase } if neHost.Remark != "" { params["remark"] = neHost.Remark @@ -361,28 +320,13 @@ func (r *NeHostImpl) Update(neHost model.NeHost) int64 { params["auth_mode"] = neHost.AuthMode } if neHost.Password != "" { - passwordEn, err := crypto.StringEncryptByAES(neHost.Password) - if err != nil { - logger.Errorf("update encrypt: %v", err.Error()) - return 0 - } - params["password"] = passwordEn + params["password"] = neHost.Password } if neHost.PrivateKey != "" { - privateKeyEn, err := crypto.StringEncryptByAES(neHost.PrivateKey) - if err != nil { - logger.Errorf("update encrypt: %v", err.Error()) - return 0 - } - params["private_key"] = privateKeyEn + params["private_key"] = neHost.PrivateKey } if neHost.PassPhrase != "" { - passPhraseEn, err := crypto.StringEncryptByAES(neHost.PassPhrase) - if err != nil { - logger.Errorf("update encrypt: %v", err.Error()) - return 0 - } - params["pass_phrase"] = passPhraseEn + params["pass_phrase"] = neHost.PassPhrase } params["remark"] = neHost.Remark if neHost.UpdateBy != "" { diff --git a/src/modules/network_element/repository/ne_info.impl.go b/src/modules/network_element/repository/ne_info.impl.go index 05e05da..b470441 100644 --- a/src/modules/network_element/repository/ne_info.impl.go +++ b/src/modules/network_element/repository/ne_info.impl.go @@ -31,6 +31,7 @@ var neListSort = []string{ "N3IWF", "MOCNGW", "SMSC", + "CBC", } // 实例化数据层 NeInfoImpl 结构体 diff --git a/src/modules/network_element/service/ne_config.go b/src/modules/network_element/service/ne_config.go index 9949ea6..2c31659 100644 --- a/src/modules/network_element/service/ne_config.go +++ b/src/modules/network_element/service/ne_config.go @@ -13,6 +13,9 @@ type INeConfig interface { // SelectNeConfigByNeType 查询网元类型参数配置 SelectNeConfigByNeType(neType string) []model.NeConfig + // SelectNeConfigByNeTypeAndParamName 查询网元类型参数配置By参数名 + SelectNeConfigByNeTypeAndParamName(neType, paramName string) model.NeConfig + // SelectNeHostPage 分页查询列表数据 SelectPage(query map[string]any) map[string]any diff --git a/src/modules/network_element/service/ne_config.impl.go b/src/modules/network_element/service/ne_config.impl.go index bff06bc..453a2dd 100644 --- a/src/modules/network_element/service/ne_config.impl.go +++ b/src/modules/network_element/service/ne_config.impl.go @@ -37,11 +37,11 @@ func (r *NeConfigImpl) RefreshByNeTypeAndNeID(neType string) []model.NeConfig { } } for k, v := range neConfigGroup { - key := fmt.Sprintf("%sparam_config:%s", cachekey.NE_DATA_KEY, strings.ToUpper(k)) + key := fmt.Sprintf("%sNeConfig:%s", cachekey.NE_DATA_KEY, strings.ToUpper(k)) redis.Del("", key) if len(v) > 0 { for i, item := range v { - if err := json.Unmarshal([]byte(item.ParamJSONStr), &item.ParamData); err != nil { + if err := json.Unmarshal([]byte(item.ParamJson), &item.ParamData); err != nil { continue } v[i] = item @@ -54,14 +54,14 @@ func (r *NeConfigImpl) RefreshByNeTypeAndNeID(neType string) []model.NeConfig { return neConfigList } // 单个 - key := fmt.Sprintf("%sparam_config:%s", cachekey.NE_DATA_KEY, strings.ToUpper(neType)) + key := fmt.Sprintf("%sNeConfig:%s", cachekey.NE_DATA_KEY, strings.ToUpper(neType)) redis.Del("", key) neConfigList := r.neConfigRepository.SelectList(model.NeConfig{ NeType: neType, }) if len(neConfigList) > 0 { for i, v := range neConfigList { - if err := json.Unmarshal([]byte(v.ParamJSONStr), &v.ParamData); err != nil { + if err := json.Unmarshal([]byte(v.ParamJson), &v.ParamData); err != nil { continue } neConfigList[i] = v @@ -74,9 +74,9 @@ func (r *NeConfigImpl) RefreshByNeTypeAndNeID(neType string) []model.NeConfig { // ClearNeCacheByNeType 清除网元类型参数配置缓存 func (r *NeConfigImpl) ClearNeCacheByNeType(neType string) bool { - key := fmt.Sprintf("%sparam_config:%s", cachekey.NE_DATA_KEY, neType) + key := fmt.Sprintf("%sNeConfig:%s", cachekey.NE_DATA_KEY, neType) if neType == "*" { - key = fmt.Sprintf("%sparam_config:*", cachekey.NE_DATA_KEY) + key = fmt.Sprintf("%sNeConfig:*", cachekey.NE_DATA_KEY) } keys, err := redis.GetKeys("", key) if err != nil { @@ -89,7 +89,7 @@ func (r *NeConfigImpl) ClearNeCacheByNeType(neType string) bool { // SelectNeConfigByNeType 查询网元类型参数配置 func (r *NeConfigImpl) SelectNeConfigByNeType(neType string) []model.NeConfig { var neConfigList []model.NeConfig - key := fmt.Sprintf("%sparam_config:%s", cachekey.NE_DATA_KEY, strings.ToUpper(neType)) + key := fmt.Sprintf("%sNeConfig:%s", cachekey.NE_DATA_KEY, strings.ToUpper(neType)) jsonStr, _ := redis.Get("", key) if len(jsonStr) > 7 { err := json.Unmarshal([]byte(jsonStr), &neConfigList) @@ -102,6 +102,19 @@ func (r *NeConfigImpl) SelectNeConfigByNeType(neType string) []model.NeConfig { return neConfigList } +// SelectNeConfigByNeTypeAndParamName 查询网元类型参数配置By参数名 +func (r *NeConfigImpl) SelectNeConfigByNeTypeAndParamName(neType, paramName string) model.NeConfig { + neConfigList := r.SelectNeConfigByNeType(neType) + var neConfig model.NeConfig + for _, v := range neConfigList { + if v.ParamName == paramName { + neConfig = v + break + } + } + return neConfig +} + // SelectNeHostPage 分页查询列表数据 func (r *NeConfigImpl) SelectPage(query map[string]any) map[string]any { return r.neConfigRepository.SelectPage(query) diff --git a/src/modules/network_element/service/ne_config_backup.go b/src/modules/network_element/service/ne_config_backup.go new file mode 100644 index 0000000..64d8eb3 --- /dev/null +++ b/src/modules/network_element/service/ne_config_backup.go @@ -0,0 +1,30 @@ +package service + +import "nms_cxy/src/modules/network_element/model" + +// INeConfigBackup 网元配置文件备份记录 服务层接口 +type INeConfigBackup interface { + // SelectNeHostPage 分页查询列表数据 + SelectPage(query map[string]any) map[string]any + + // SelectList 根据实体查询 + SelectList(item model.NeConfigBackup) []model.NeConfigBackup + + // SelectByIds 通过ID查询 + SelectById(id string) model.NeConfigBackup + + // Insert 新增信息 + Insert(item model.NeConfigBackup) string + + // Update 修改信息 + Update(item model.NeConfigBackup) int64 + + // DeleteByIds 批量删除信息 + DeleteByIds(ids []string) (int64, error) + + // NeConfigLocalToNe 网元配置文件复制到网元端覆盖 + NeConfigLocalToNe(neInfo model.NeInfo, localFile string) error + + // NeConfigNeToLocal 网元备份文件网元端复制到本地 + NeConfigNeToLocal(neInfo model.NeInfo) (string, error) +} diff --git a/src/modules/network_element/service/ne_config_backup.impl.go b/src/modules/network_element/service/ne_config_backup.impl.go new file mode 100644 index 0000000..edcccc1 --- /dev/null +++ b/src/modules/network_element/service/ne_config_backup.impl.go @@ -0,0 +1,202 @@ +package service + +import ( + "fmt" + "os" + "runtime" + "strings" + "time" + + "nms_cxy/src/framework/utils/date" + "nms_cxy/src/framework/utils/file" + "nms_cxy/src/modules/network_element/model" + "nms_cxy/src/modules/network_element/repository" +) + +// NewNeConfigBackupImpl 网元配置文件备份记录 实例化服务层 +var NewNeConfigBackupImpl = &NeConfigBackupImpl{ + neConfigBackupRepository: repository.NewNeConfigBackupImpl, +} + +// NeConfigBackupImpl 网元配置文件备份记录 服务层处理 +type NeConfigBackupImpl struct { + // 网元配置文件备份记录 + neConfigBackupRepository repository.INeConfigBackup +} + +// SelectNeHostPage 分页查询列表数据 +func (r *NeConfigBackupImpl) SelectPage(query map[string]any) map[string]any { + return r.neConfigBackupRepository.SelectPage(query) +} + +// SelectConfigList 查询列表 +func (r *NeConfigBackupImpl) SelectList(item model.NeConfigBackup) []model.NeConfigBackup { + return r.neConfigBackupRepository.SelectList(item) +} + +// SelectByIds 通过ID查询 +func (r *NeConfigBackupImpl) SelectById(id string) model.NeConfigBackup { + if id == "" { + return model.NeConfigBackup{} + } + arr := r.neConfigBackupRepository.SelectByIds([]string{id}) + if len(arr) > 0 { + return arr[0] + } + return model.NeConfigBackup{} +} + +// Insert 新增信息 +func (r *NeConfigBackupImpl) Insert(item model.NeConfigBackup) string { + return r.neConfigBackupRepository.Insert(item) +} + +// Update 修改信息 +func (r *NeConfigBackupImpl) Update(item model.NeConfigBackup) int64 { + return r.neConfigBackupRepository.Update(item) +} + +// DeleteByIds 批量删除信息 +func (r *NeConfigBackupImpl) DeleteByIds(ids []string) (int64, error) { + // 检查是否存在 + data := r.neConfigBackupRepository.SelectByIds(ids) + if len(data) <= 0 { + return 0, fmt.Errorf("neConfigBackup.noData") + } + + if len(data) == len(ids) { + rows := r.neConfigBackupRepository.DeleteByIds(ids) + return rows, nil + } + // 删除信息失败! + return 0, fmt.Errorf("delete fail") +} + +// NeConfigLocalToNe 网元配置文件复制到网元端覆盖 +func (r *NeConfigBackupImpl) NeConfigLocalToNe(neInfo model.NeInfo, localFile string) error { + neTypeLower := strings.ToLower(neInfo.NeType) + // 网管本地路径 + omcPath := "/usr/local/etc/omc/ne_config" + if runtime.GOOS == "windows" { + omcPath = fmt.Sprintf("C:%s", omcPath) + } + localDirPath := fmt.Sprintf("%s/%s/%s/backup/tmp_import", omcPath, neTypeLower, neInfo.NeId) + if err := file.UnZip(localFile, localDirPath); err != nil { + return fmt.Errorf("unzip err") + } + + // 网元主机的SSH客户端 + sshClient, err := NewNeInfoImpl.NeRunSSHClient(neInfo.NeType, neInfo.NeId) + if err != nil { + return fmt.Errorf("ne info ssh client err") + } + defer sshClient.Close() + // 网元主机的SSH客户端进行文件传输 + sftpClient, err := sshClient.NewClientSFTP() + if err != nil { + return fmt.Errorf("ne info sftp client err") + } + defer sftpClient.Close() + + // 网元配置端上的临时目录 + neDirTemp := fmt.Sprintf("/tmp/omc/ne_config/%s/%s", neTypeLower, neInfo.NeId) + sshClient.RunCMD(fmt.Sprintf("mkdir -p /tmp/omc && sudo chmod 777 -R /tmp/omc && sudo rm -rf %s", neDirTemp)) + // 复制到网元端 + if err = sftpClient.CopyDirLocalToRemote(localDirPath, neDirTemp); err != nil { + return fmt.Errorf("copy config to ne err") + } + + // 配置复制到网元内 + if neTypeLower == "ims" { + // ims目录 + imsDirArr := [...]string{"bgcf", "icscf", "ismc", "mmtel", "mrf", "oam_manager.yaml", "pcscf", "scscf", "vars.cfg", "zlog"} + for _, v := range imsDirArr { + sshClient.RunCMD(fmt.Sprintf("sudo mkdir -p /usr/local/etc/ims && sudo cp -rf %s/ims/%s /usr/local/etc/ims/%v && sudo chmod 755 -R /usr/local/etc/ims/%s", neDirTemp, v, v, v)) + } + // mf目录 + sshClient.RunCMD(fmt.Sprintf("sudo mkdir -p /usr/local/etc/mf && sudo cp -rf %s/mf/* /usr/local/etc/mf && sudo chmod 755 -R /usr/local/etc/mf", neDirTemp)) + // rtproxy目录 + sshClient.RunCMD(fmt.Sprintf("sudo mkdir -p /usr/local/etc/rtproxy && sudo cp -rf %s/rtproxy/* /usr/local/etc/rtproxy && sudo chmod 755 /usr/local/etc/rtproxy/rtproxy.conf", neDirTemp)) + // iwf目录 + 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 { + neEtcPath := fmt.Sprintf("/usr/local/etc/%s", neTypeLower) + chmodFile := fmt.Sprintf("sudo chmod 755 %s/*.yaml", neEtcPath) + if neTypeLower == "mme" { + chmodFile = fmt.Sprintf("sudo chmod 755 %s/*.{yaml,conf}", neEtcPath) + } + sshClient.RunCMD(fmt.Sprintf("sudo cp -rf %s/* %s && %s", neDirTemp, neEtcPath, chmodFile)) + } + + _ = os.RemoveAll(localDirPath) // 删除本地临时目录 + sshClient.RunCMD(fmt.Sprintf("sudo rm -rf %s", neDirTemp)) // 删除临时目录 + return nil +} + +// NeConfigNeToLocal 网元备份文件网元端复制到本地 +func (r *NeConfigBackupImpl) NeConfigNeToLocal(neInfo model.NeInfo) (string, error) { + // 网元主机的SSH客户端 + sshClient, err := NewNeInfoImpl.NeRunSSHClient(neInfo.NeType, neInfo.NeId) + if err != nil { + return "", fmt.Errorf("ne info ssh client err") + } + defer sshClient.Close() + // 网元主机的SSH客户端进行文件传输 + sftpClient, err := sshClient.NewClientSFTP() + if err != nil { + return "", fmt.Errorf("ne info sftp client err") + } + defer sftpClient.Close() + + neTypeLower := strings.ToLower(neInfo.NeType) + // 网管本地路径 + omcPath := "/usr/local/etc/omc/ne_config" + if runtime.GOOS == "windows" { + omcPath = fmt.Sprintf("C:%s", omcPath) + } + localDirPath := fmt.Sprintf("%s/%s/%s/backup/tmp_export", omcPath, neTypeLower, neInfo.NeId) + + // 网元配置文件先复制到临时目录 + sshClient.RunCMD("mkdir -p /tmp/omc && sudo chmod 777 -R /tmp/omc") + neDirTemp := fmt.Sprintf("/tmp/omc/ne_config/%s/%s", neTypeLower, neInfo.NeId) + if neTypeLower == "ims" { + // ims目录 + sshClient.RunCMD(fmt.Sprintf("mkdir -p %s/ims", neDirTemp)) + imsDirArr := [...]string{"bgcf", "icscf", "ismc", "mmtel", "mrf", "oam_manager.yaml", "pcscf", "scscf", "vars.cfg", "zlog"} + for _, v := range imsDirArr { + sshClient.RunCMD(fmt.Sprintf("sudo cp -rf /usr/local/etc/ims/%s %s/ims", v, neDirTemp)) + } + // mf目录 + sshClient.RunCMD(fmt.Sprintf("mkdir -p %s/mf && sudo cp -rf /usr/local/etc/mf %s", neDirTemp, neDirTemp)) + // rtproxy目录 + sshClient.RunCMD(fmt.Sprintf("mkdir -p %s/rtproxy && sudo cp -rf /usr/local/etc/rtproxy/rtproxy.conf %s/rtproxy", neDirTemp, neDirTemp)) + // iwf目录 + 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 { + nePath := fmt.Sprintf("/usr/local/etc/%s/*.yaml", neTypeLower) + if neTypeLower == "mme" { + nePath = fmt.Sprintf("/usr/local/etc/%s/*.{yaml,conf}", neTypeLower) + } + sshClient.RunCMD(fmt.Sprintf("mkdir -p %s && sudo cp -rf %s %s", neDirTemp, nePath, neDirTemp)) + } + + // 网元端复制到本地 + if err = sftpClient.CopyDirRemoteToLocal(neDirTemp, localDirPath); err != nil { + return "", fmt.Errorf("copy config err") + } + + // 压缩zip文件名 + zipFileName := fmt.Sprintf("%s-%s-etc-%s.zip", neTypeLower, neInfo.NeId, date.ParseDateToStr(time.Now(), date.YYYYMMDDHHMMSS)) + zipFilePath := fmt.Sprintf("%s/%s/%s/backup/%s", omcPath, neTypeLower, neInfo.NeId, zipFileName) + if err := file.CompressZipByDir(zipFilePath, localDirPath); err != nil { + return "", fmt.Errorf("compress zip err") + } + + _ = os.RemoveAll(localDirPath) // 删除本地临时目录 + sshClient.RunCMD(fmt.Sprintf("sudo rm -rf %s", neDirTemp)) // 删除临时目录 + return zipFilePath, nil +} diff --git a/src/modules/network_element/service/ne_host.impl.go b/src/modules/network_element/service/ne_host.impl.go index b1d0e98..84d47e0 100644 --- a/src/modules/network_element/service/ne_host.impl.go +++ b/src/modules/network_element/service/ne_host.impl.go @@ -3,6 +3,9 @@ package service import ( "fmt" + "nms_cxy/src/framework/config" + "nms_cxy/src/framework/logger" + "nms_cxy/src/framework/utils/crypto" "nms_cxy/src/modules/network_element/model" "nms_cxy/src/modules/network_element/repository" ) @@ -30,12 +33,39 @@ func (r *NeHostImpl) SelectList(neHost model.NeHost) []model.NeHost { // SelectByIds 通过ID查询 func (r *NeHostImpl) SelectById(hostId string) model.NeHost { + neHost := model.NeHost{} if hostId == "" { - return model.NeHost{} + return neHost } neHosts := r.neHostRepository.SelectByIds([]string{hostId}) if len(neHosts) > 0 { - return neHosts[0] + neHost := neHosts[0] + hostKey := config.Get("aes.hostKey").(string) + if neHost.Password != "" { + passwordDe, err := crypto.AESDecryptBase64(neHost.Password, hostKey) + if err != nil { + logger.Errorf("select encrypt: %v", err.Error()) + return neHost + } + neHost.Password = passwordDe + } + if neHost.PrivateKey != "" { + privateKeyDe, err := crypto.AESDecryptBase64(neHost.PrivateKey, hostKey) + if err != nil { + logger.Errorf("select encrypt: %v", err.Error()) + return neHost + } + neHost.PrivateKey = privateKeyDe + } + if neHost.PassPhrase != "" { + passPhraseDe, err := crypto.AESDecryptBase64(neHost.PassPhrase, hostKey) + if err != nil { + logger.Errorf("select encrypt: %v", err.Error()) + return neHost + } + neHost.PassPhrase = passPhraseDe + } + return neHost } return model.NeHost{} } @@ -54,11 +84,61 @@ func (r *NeHostImpl) Inserts(neHosts []model.NeHost) int64 { // Insert 新增信息 func (r *NeHostImpl) Insert(neHost model.NeHost) string { + hostKey := config.Get("aes.hostKey").(string) + if neHost.Password != "" { + passwordEn, err := crypto.AESEncryptBase64(neHost.Password, hostKey) + if err != nil { + logger.Errorf("insert encrypt: %v", err.Error()) + return "" + } + neHost.Password = passwordEn + } + if neHost.PrivateKey != "" { + privateKeyEn, err := crypto.AESEncryptBase64(neHost.PrivateKey, hostKey) + if err != nil { + logger.Errorf("insert encrypt: %v", err.Error()) + return "" + } + neHost.PrivateKey = privateKeyEn + } + if neHost.PassPhrase != "" { + passPhraseEn, err := crypto.AESEncryptBase64(neHost.PassPhrase, hostKey) + if err != nil { + logger.Errorf("insert encrypt: %v", err.Error()) + return "" + } + neHost.PassPhrase = passPhraseEn + } return r.neHostRepository.Insert(neHost) } // Update 修改信息 func (r *NeHostImpl) Update(neHost model.NeHost) int64 { + hostKey := config.Get("aes.hostKey").(string) + if neHost.Password != "" { + passwordEn, err := crypto.AESEncryptBase64(neHost.Password, hostKey) + if err != nil { + logger.Errorf("update password encrypt: %v", err.Error()) + return 0 + } + neHost.Password = passwordEn + } + if neHost.PrivateKey != "" { + privateKeyEn, err := crypto.AESEncryptBase64(neHost.PrivateKey, hostKey) + if err != nil { + logger.Errorf("update private key encrypt: %v", err.Error()) + return 0 + } + neHost.PrivateKey = privateKeyEn + } + if neHost.PassPhrase != "" { + passPhraseEn, err := crypto.AESEncryptBase64(neHost.PassPhrase, hostKey) + if err != nil { + logger.Errorf("update pass phrase encrypt: %v", err.Error()) + return 0 + } + neHost.PassPhrase = passPhraseEn + } return r.neHostRepository.Update(neHost) } diff --git a/src/modules/network_element/service/ne_info.impl.go b/src/modules/network_element/service/ne_info.impl.go index 25a6a91..ae528f2 100644 --- a/src/modules/network_element/service/ne_info.impl.go +++ b/src/modules/network_element/service/ne_info.impl.go @@ -185,7 +185,17 @@ func (r *NeInfoImpl) bandNeHosts(arr *[]model.NeInfo) { for i := range *arr { v := (*arr)[i] if v.HostIDs != "" { - (*arr)[i].Hosts = NewNeHostImpl.neHostRepository.SelectByIds(strings.Split(v.HostIDs, ",")) + hostIds := strings.Split(v.HostIDs, ",") + if len(hostIds) <= 1 { + continue + } + for _, hostId := range hostIds { + neHost := NewNeHostImpl.SelectById(hostId) + if neHost.HostID == "" || neHost.HostID != hostId { + continue + } + (*arr)[i].Hosts = append((*arr)[i].Hosts, neHost) + } } } } @@ -199,12 +209,11 @@ func (r *NeInfoImpl) SelectById(infoId string, bandHost bool) model.NeInfo { } neInfos := r.neInfoRepository.SelectByIds([]string{infoId}) if len(neInfos) > 0 { - neInfo := neInfos[0] // 带主机信息 - if neInfo.HostIDs != "" && bandHost { - neInfo.Hosts = NewNeHostImpl.neHostRepository.SelectByIds(strings.Split(neInfo.HostIDs, ",")) + if neInfos[0].HostIDs != "" && bandHost { + r.bandNeHosts(&neInfos) } - return neInfo + return neInfos[0] } return model.NeInfo{} } @@ -312,12 +321,17 @@ func (r *NeInfoImpl) NeRunSSHClient(neType, neId string) (*ssh.ConnSSH, error) { logger.Errorf("NeRunSSHClient NeType:%s NeID:%s hostId not found", neType, neId) return nil, fmt.Errorf("neinfo hostId not found") } - neInfo.Hosts = NewNeHostImpl.neHostRepository.SelectByIds(strings.Split(neInfo.HostIDs, ",")) - if len(neInfo.Hosts) <= 0 { - logger.Errorf("NeRunSSHClient Hosts %s not found", neInfo.HostIDs) + hostIds := strings.Split(neInfo.HostIDs, ",") + if len(hostIds) <= 1 { + logger.Errorf("NeRunTelnetClient hosts id %s not found", neInfo.HostIDs) + return nil, fmt.Errorf("neinfo host id not found") + } + hostId := hostIds[0] // 网元主机ssh 0:22 + neHost := NewNeHostImpl.SelectById(hostId) + if neHost.HostID == "" || neHost.HostID != hostId { + logger.Errorf("NeRunTelnetClient Hosts %s not found", neInfo.HostIDs) return nil, fmt.Errorf("neinfo host not found") } - neHost := neInfo.Hosts[0] // 网元主机ssh 0:22 if neHost.HostType != "ssh" { logger.Errorf("NeRunSSHClient Hosts first HostType %s not ssh", neHost.HostType) return nil, fmt.Errorf("neinfo host type not ssh") @@ -369,12 +383,17 @@ func (r *NeInfoImpl) NeRunTelnetClient(neType, neId string, num int) (*telnet.Co logger.Errorf("NeRunTelnetClient NeType:%s NeID:%s hostId not found", neType, neId) return nil, fmt.Errorf("neinfo hostId not found") } - neInfo.Hosts = NewNeHostImpl.neHostRepository.SelectByIds(strings.Split(neInfo.HostIDs, ",")) - if len(neInfo.Hosts) <= 0 { + hostIds := strings.Split(neInfo.HostIDs, ",") + if len(hostIds) <= 1 { + logger.Errorf("NeRunTelnetClient hosts id %s not found", neInfo.HostIDs) + return nil, fmt.Errorf("neinfo host id not found") + } + hostId := hostIds[num] // 网元主机telnet 1:4100 2:5200 + neHost := NewNeHostImpl.SelectById(hostId) + if neHost.HostID == "" || neHost.HostID != hostId { logger.Errorf("NeRunTelnetClient Hosts %s not found", neInfo.HostIDs) return nil, fmt.Errorf("neinfo host not found") } - neHost := neInfo.Hosts[num] // 创建链接Telnet客户端 var connTelnet telnet.ConnTelnet @@ -540,6 +559,7 @@ func (r *NeInfoImpl) NeConfOAMSync(neInfo model.NeInfo, content map[string]any, "pvFlag": neInfo.PvFlag, } + // 公共参数指定的OMC if omcIP, ok := r.Para5GData["OMC_IP"]; ok && omcIP != "" { if strings.Contains(omcIP, ":") { item["ipType"] = "ipv6" @@ -551,6 +571,17 @@ func (r *NeInfoImpl) NeConfOAMSync(neInfo model.NeInfo, content map[string]any, } } + if v, ok := content["omcIP"]; ok && v != "" && v != nil { + omcIP := v.(string) + if strings.Contains(omcIP, ":") { + item["ipType"] = "ipv6" + item["ipv6"] = omcIP + } + if strings.Contains(omcIP, ".") { + item["ipType"] = "ipv4" + item["ipv4"] = omcIP + } + } if oamEnable, ok := content["oamEnable"]; ok && oamEnable != nil { item["enable"] = parse.Boolean(oamEnable) } diff --git a/src/modules/network_element/service/ne_version.impl.go b/src/modules/network_element/service/ne_version.impl.go index 31737c3..13a281d 100644 --- a/src/modules/network_element/service/ne_version.impl.go +++ b/src/modules/network_element/service/ne_version.impl.go @@ -7,7 +7,6 @@ import ( "strings" "time" - "nms_cxy/src/framework/logger" "nms_cxy/src/framework/utils/file" "nms_cxy/src/framework/utils/ssh" "nms_cxy/src/modules/network_element/model" @@ -203,15 +202,15 @@ func (r *NeVersionImpl) operateCommand(action, neType string, neFilePaths []stri omcStrArr := []string{} omcStrArr = append(omcStrArr, pkgCmdStr) if action == "install" { - omcStrArr = append(omcStrArr, "sudo /usr/local/omc/bin/setomc.sh -m install") // 初始化数据库 + omcStrArr = append(omcStrArr, "/usr/local/omc/bin/setomc.sh -m install") // 初始化数据库 } else { - omcStrArr = append(omcStrArr, "sudo /usr/local/omc/bin/setomc.sh -m upgrade") // 升级数据库 + omcStrArr = append(omcStrArr, "/usr/local/omc/bin/setomc.sh -m upgrade") // 升级数据库 } omcStrArr = append(omcStrArr, "sudo systemctl restart omc") // 重启服务 omcStrArr = append(omcStrArr, fmt.Sprintf("sudo rm %s", strings.Join(neFilePaths, " "))) // 删除软件包 // 2s后安装 - cmdStrArr = append(cmdStrArr, fmt.Sprintf("nohup sh -c \"sleep 2s && %s\" > /dev/null 2>&1 & \n", strings.Join(omcStrArr, " && "))) + cmdStrArr = append(cmdStrArr, fmt.Sprintf("nohup sh -c \"sleep 2s && %s\" > /tmp/omc_%s.out 2>&1 & \n", strings.Join(omcStrArr, " && "), action)) // 结束 cmdStrArr = append(cmdStrArr, fmt.Sprintf("echo '%s' \n", okFlagStr)) return okFlagStr, cmdStrArr, nil @@ -516,8 +515,6 @@ func (r *NeVersionImpl) operateRun(sshClient *ssh.ConnSSH, preinput map[string]s for { select { case <-timeoutTicker.C: - logger.Warnf("NeVersion operateRun %s", commandLineText) - logger.Errorf("neinfo ssh client session read timeout") done <- true return case <-msTicker.C: