From 91f4db75f1ce1bf6df9476187678a54c4fb926f9 Mon Sep 17 00:00:00 2001 From: TsMask <340112800@qq.com> Date: Mon, 1 Apr 2024 17:00:27 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E7=BD=91=E5=85=83=E4=BF=A1=E6=81=AF?= =?UTF-8?q?=E9=85=8D=E7=BD=AE=E8=AF=BB=E5=86=99=EF=BC=8Ccmd=E5=91=BD?= =?UTF-8?q?=E4=BB=A4=E7=9B=B4=E5=8F=91=E5=87=BD=E6=95=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../network_element/controller/ne_info.go | 134 ++++++++++++----- .../network_element/service/ne_info.go | 9 ++ .../network_element/service/ne_info.impl.go | 140 ++++++++++++++++++ 3 files changed, 249 insertions(+), 34 deletions(-) diff --git a/src/modules/network_element/controller/ne_info.go b/src/modules/network_element/controller/ne_info.go index f575ab6e..55c10c3f 100644 --- a/src/modules/network_element/controller/ne_info.go +++ b/src/modules/network_element/controller/ne_info.go @@ -39,15 +39,15 @@ func (s *NeInfoController) State(c *gin.Context) { language := ctx.AcceptLanguage(c) var querys struct { NeType string `form:"neType" binding:"required"` - NeID string `form:"neId" binding:"required"` + NeId string `form:"neId" binding:"required"` } if err := c.ShouldBindQuery(&querys); 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(querys.NeType, querys.NeId) + if neInfo.NeId != querys.NeId || neInfo.IP == "" { c.JSON(200, result.ErrMsg(i18n.TKey(language, "app.common.noNEInfo"))) return } @@ -84,17 +84,26 @@ func (s *NeInfoController) State(c *gin.Context) { c.JSON(200, result.OkData(resData)) } -// 网元信息列表 +// 网元neType和neID查询 // -// GET /list -func (s *NeInfoController) List(c *gin.Context) { - querys := ctx.QueryMap(c) - bandStatus := false - if v, ok := querys["bandStatus"]; ok && v != nil { - bandStatus = parse.Boolean(v) +// GET /byTypeAndID +func (s *NeInfoController) NeTypeAndID(c *gin.Context) { + language := ctx.AcceptLanguage(c) + var querys struct { + NeType string `form:"neType" binding:"required"` + NeID string `form:"neId" binding:"required"` } - data := s.neInfoService.SelectPage(querys, bandStatus) - c.JSON(200, result.Ok(data)) + if err := c.ShouldBindQuery(&querys); 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 == "" { + c.JSON(200, result.ErrMsg(i18n.TKey(language, "app.common.noNEInfo"))) + return + } + c.JSON(200, result.OkData(neInfo)) } // 网元信息列表全部无分页 @@ -129,6 +138,85 @@ func (s *NeInfoController) ListAll(c *gin.Context) { c.JSON(200, result.OkData(neList)) } +// 网元端配置文件读取 +// +// GET /configFile +func (s *NeInfoController) ConfigFileRead(c *gin.Context) { + language := ctx.AcceptLanguage(c) + var querys struct { + NeType string `form:"neType" binding:"required"` + NeID string `form:"neId" binding:"required"` + FilePath string `form:"filePath"` // 不带文件路径时进行复制覆盖本地网元配置目录 + } + if err := c.ShouldBindQuery(&querys); err != nil { + c.JSON(400, result.CodeMsg(400, i18n.TKey(language, "app.common.err400"))) + return + } + + // 查询网元获取IP + neInfo := s.neInfoService.SelectNeInfoByNeTypeAndNeID(querys.NeType, querys.NeID) + if neInfo.NeId != querys.NeID || neInfo.IP == "" { + c.JSON(200, result.ErrMsg(i18n.TKey(language, "app.common.noNEInfo"))) + return + } + + data := s.neInfoService.NeConfigFileRead(neInfo, querys.FilePath) + if querys.FilePath == "" { + c.JSON(200, result.OkData(data)) + return + } + if len(data) > 0 { + c.JSON(200, result.OkData(data[0])) + return + } + c.JSON(200, result.ErrMsg("no data")) +} + +// 网元端配置文件写入 +// +// PUT /configFile +func (s *NeInfoController) ConfigFileWrite(c *gin.Context) { + language := ctx.AcceptLanguage(c) + var body struct { + NeType string `json:"neType" binding:"required"` + NeID string `json:"neId" binding:"required"` + FilePath string `json:"filePath" binding:"required"` + Content string `json:"content" binding:"required"` + Sync bool `json:"sync"` + } + if err := c.ShouldBindJSON(&body); err != nil { + c.JSON(400, result.CodeMsg(400, i18n.TKey(language, "app.common.err400"))) + return + } + + // 查询网元获取IP + 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 + } + + err := s.neInfoService.NeConfigFileWirte(neInfo, body.FilePath, body.Content, body.Sync) + if err != nil { + c.JSON(200, result.ErrMsg(err.Error())) + return + } + c.JSON(200, result.Ok(nil)) +} + +// 网元信息列表 +// +// GET /list +func (s *NeInfoController) List(c *gin.Context) { + querys := ctx.QueryMap(c) + bandStatus := false + if v, ok := querys["bandStatus"]; ok && v != nil { + bandStatus = parse.Boolean(v) + } + data := s.neInfoService.SelectPage(querys, bandStatus) + c.JSON(200, result.Ok(data)) +} + // 网元信息 // // GET /:infoId @@ -150,28 +238,6 @@ func (s *NeInfoController) Info(c *gin.Context) { c.JSON(200, result.OkData(neHost)) } -// 网元neType和neID查询 -// -// GET / -func (s *NeInfoController) NeTypeAndID(c *gin.Context) { - language := ctx.AcceptLanguage(c) - var querys struct { - NeType string `form:"neType" binding:"required"` - NeID string `form:"neId" binding:"required"` - } - if err := c.ShouldBindQuery(&querys); 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 == "" { - c.JSON(200, result.ErrMsg(i18n.TKey(language, "app.common.noNEInfo"))) - return - } - c.JSON(200, result.OkData(neInfo)) -} - // 网元信息新增 // // POST / diff --git a/src/modules/network_element/service/ne_info.go b/src/modules/network_element/service/ne_info.go index a231b167..a667c017 100644 --- a/src/modules/network_element/service/ne_info.go +++ b/src/modules/network_element/service/ne_info.go @@ -42,4 +42,13 @@ type INeInfo interface { // CheckUniqueNeTypeAndNeId 校验同类型下标识是否唯一 CheckUniqueNeTypeAndNeId(neType, neId, infoId string) bool + + // NeRunCMD 向网元发送cmd命令 + NeRunCMD(neType, neId, cmd string) (string, error) + + // NeConfigFileRead 网元配置文件读取 网元配置yaml文件复制到本地后通过filePath读取 + NeConfigFileRead(neInfo model.NeInfo, filePath string) []string + + // NeConfigFileWirte 网元配置文件写入 content内容 sync同步到网元端 + NeConfigFileWirte(neInfo model.NeInfo, filePath, content string, sync bool) error } diff --git a/src/modules/network_element/service/ne_info.impl.go b/src/modules/network_element/service/ne_info.impl.go index f1e69055..41b9ef46 100644 --- a/src/modules/network_element/service/ne_info.impl.go +++ b/src/modules/network_element/service/ne_info.impl.go @@ -3,10 +3,14 @@ package service import ( "encoding/json" "fmt" + "os" + "runtime" "strings" "be.ems/src/framework/constants/cachekey" + "be.ems/src/framework/logger" "be.ems/src/framework/redis" + "be.ems/src/framework/utils/ssh" "be.ems/src/modules/network_element/model" "be.ems/src/modules/network_element/repository" ) @@ -294,3 +298,139 @@ func (r *NeInfoImpl) CheckUniqueNeTypeAndNeId(neType, neId, infoId string) bool } return uniqueId == "" } + +// NeRunCMD 向网元发送cmd命令 +func (r *NeInfoImpl) NeRunCMD(neType, neId, cmd string) (string, error) { + neInfo := r.SelectNeInfoByNeTypeAndNeID(neType, neId) + if neInfo.NeId != neId { + logger.Errorf("NeRunCMD NeType:%s NeID:%s not found", neType, neId) + return "", fmt.Errorf("neinfo not found") + } + // 带主机信息 + if neInfo.HostIDs != "" { + neInfo.Hosts = r.neHostRepository.SelectByIds(strings.Split(neInfo.HostIDs, ",")) + if len(neInfo.Hosts) <= 0 { + logger.Errorf("NeRunCMD Hosts %s not found", neInfo.HostIDs) + return "", fmt.Errorf("neinfo host not found") + } + } + + neHost := neInfo.Hosts[0] + if neHost.HostType != "ssh" { + logger.Errorf("NeRunCMD Hosts first HostType %s not ssh", neHost.HostType) + return "", fmt.Errorf("neinfo host type not ssh") + } + var connSSH ssh.ConnSSH + neHost.CopyTo(&connSSH) + client, err := connSSH.NewClient() + if err != nil { + logger.Errorf("NeRunCMD NewClient err => %s", err.Error()) + return "", fmt.Errorf("neinfo ssh client new err") + } + defer client.Close() + + // 执行命令 + output, err := client.RunCMD(cmd) + if err != nil { + logger.Errorf("NeRunCMD RunCMD %s err => %s", output, err.Error()) + return "", fmt.Errorf("neinfo ssh run cmd err") + } + + return output, nil +} + +// NeConfigFileRead 网元配置文件读取 网元配置yaml文件复制到本地后通过filePath读取 +func (r *NeInfoImpl) NeConfigFileRead(neInfo model.NeInfo, filePath string) []string { + files := []string{} + neTypeLower := strings.ToLower(neInfo.NeType) + + // 网管本地路径 + omcPath := "/usr/local/etc/omc/ne_config" + if runtime.GOOS == "windows" { + omcPath = fmt.Sprintf("C:%s", omcPath) + } + omcPath = fmt.Sprintf("%s/%s/%s", omcPath, neTypeLower, neInfo.NeId) + + // 读取文件内容 + if filePath != "" { + bytes, err := os.ReadFile(fmt.Sprintf("%s/%s", omcPath, filePath)) + if err != nil { + logger.Warnf("NeConfigFile ReadFile => %s", err.Error()) + return files + } + files = append(files, string(bytes)) + return files + } + + // 删除原有配置文件 + // err := os.RemoveAll(omcPath) + // if err != nil { + // logger.Warnf("NeConfigFile Remove => %s", err.Error()) + // return files + // } + + // 网元端配置路径 + nePath := "/usr/local/etc" + nePath = fmt.Sprintf("%s/%s", nePath, neTypeLower) + + // 各个网元与网元间约定配置文件 + err := ssh.FileSCPNeToLocal(neInfo.IP, nePath+"/oam_manager.yaml", omcPath+"/oam_manager.yaml") + if err == nil { + files = append(files, "oam_manager.yaml") + } + + // 根据情况复制网元特殊配置 + switch neTypeLower { + case "ausf": + err = ssh.FileSCPNeToLocal(neInfo.IP, nePath+"/ausfcfg.yaml", omcPath+"/ausfcfg.yaml") + if err == nil { + files = append(files, "ausfcfg.yaml") + } + case "smf": + ssh.FileSCPNeToLocal(neInfo.IP, nePath+"/smf_conf.yaml", omcPath+"/smf_conf.yaml") + if err == nil { + files = append(files, "smf_conf.yaml") + } + ssh.FileSCPNeToLocal(neInfo.IP, nePath+"/smf_policy.yaml", omcPath+"/smf_policy.yaml") + if err == nil { + files = append(files, "smf_policy.yaml") + } + case "ims": + } + + return files +} + +// NeConfigFileWirte 网元配置文件写入 content内容 sync同步到网元端 +func (r *NeInfoImpl) NeConfigFileWirte(neInfo model.NeInfo, filePath, content string, sync bool) error { + neTypeLower := strings.ToLower(neInfo.NeType) + + // 网管本地路径 + omcPath := "/usr/local/etc/omc/ne_config" + if runtime.GOOS == "windows" { + omcPath = fmt.Sprintf("C:%s", omcPath) + } + localFilePath := fmt.Sprintf("%s/%s/%s/%s", omcPath, neTypeLower, neInfo.NeId, filePath) + + err := os.WriteFile(localFilePath, []byte(content), 0644) + if err != nil { + logger.Warnf("NeConfigFile WriteFile => %s", err.Error()) + return fmt.Errorf("please check if the file exists or write permissions") + } + + // 同步到网元端 + if sync { + // 网元端配置路径 + neFilePath := fmt.Sprintf("/usr/local/etc/%s/%s", neTypeLower, filePath) + // 修改网元文件权限 + r.NeRunCMD(neInfo.NeType, neInfo.NeId, fmt.Sprintf("sudo chmod o+w %s", neFilePath)) + // 复制到网元进行覆盖 + err = ssh.FileSCPLocalToNe(neInfo.IP, localFilePath, neFilePath) + if err != nil { + logger.Warnf("NeConfigFile SyncFile => %s", err.Error()) + return fmt.Errorf("please check if scp remote copy is allowed") + } + } + + return nil +}