From a7bb0db03aaddb179fd94de05133ec241901d92c Mon Sep 17 00:00:00 2001 From: TsMask <340112800@qq.com> Date: Mon, 1 Apr 2024 16:44:27 +0800 Subject: [PATCH 01/12] =?UTF-8?q?fix:=20ws=E5=8F=96=E6=B6=88logout?= =?UTF-8?q?=E5=AD=97=E7=AC=A6=E4=B8=B2=E9=80=80=E5=87=BA=EF=BC=8C=E5=BD=93?= =?UTF-8?q?=E9=80=80=E5=87=BA=E5=90=8E=E6=B6=88=E6=81=AF=E8=AF=BB=E5=8F=96?= =?UTF-8?q?EOF=E9=80=80=E5=87=BA=E5=8D=8F=E7=A8=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/modules/ws/controller/ws.go | 20 ++++++++++---------- src/modules/ws/service/ws_receive.impl.go | 6 ++++++ 2 files changed, 16 insertions(+), 10 deletions(-) diff --git a/src/modules/ws/controller/ws.go b/src/modules/ws/controller/ws.go index b9051c40..50fd01fa 100644 --- a/src/modules/ws/controller/ws.go +++ b/src/modules/ws/controller/ws.go @@ -196,11 +196,11 @@ func (s *WSController) SSH(c *gin.Context) { wsClient.MsgChan <- msgByte // 退出ssh登录 - if strings.LastIndex(outputStr, "logout\r\n") != -1 { - time.Sleep(1 * time.Second) - s.wsService.CloseClient(wsClient.ID) - return - } + // if strings.LastIndex(outputStr, "logout\r\n") != -1 { + // time.Sleep(1 * time.Second) + // s.wsService.CloseClient(wsClient.ID) + // return + // } } } }() @@ -283,11 +283,11 @@ func (s *WSController) Telnet(c *gin.Context) { wsClient.MsgChan <- msgByte // 退出telnet登录 - if strings.LastIndex(outputStr, "logout\r\n") != -1 { - time.Sleep(1 * time.Second) - s.wsService.CloseClient(wsClient.ID) - return - } + // if strings.LastIndex(outputStr, "logout\r\n") != -1 { + // time.Sleep(1 * time.Second) + // s.wsService.CloseClient(wsClient.ID) + // return + // } } } }() diff --git a/src/modules/ws/service/ws_receive.impl.go b/src/modules/ws/service/ws_receive.impl.go index 00648e25..3df49304 100644 --- a/src/modules/ws/service/ws_receive.impl.go +++ b/src/modules/ws/service/ws_receive.impl.go @@ -3,6 +3,7 @@ package service import ( "encoding/json" "fmt" + "io" "time" "be.ems/src/framework/logger" @@ -83,6 +84,11 @@ func (s *WSReceiveImpl) AsyncReceive(client *model.WSClient, reqMsg model.WSRequ logger.Warnf("ws AsyncReceive 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 { From 218c9172f7af0d4fc0f2d2fa1efe8ac91ee14ead Mon Sep 17 00:00:00 2001 From: TsMask <340112800@qq.com> Date: Mon, 1 Apr 2024 16:46:09 +0800 Subject: [PATCH 02/12] =?UTF-8?q?fix:=20=20=E7=94=A8=E6=88=B7=E5=88=A0?= =?UTF-8?q?=E9=99=A4=E6=A0=87=E8=AE=B0del=5F=E7=94=A8=E6=88=B7=E5=90=8D?= =?UTF-8?q?=E9=81=BF=E5=85=8D=E6=9F=A5=E8=AF=A2=E5=AD=98=E5=9C=A8=E6=83=85?= =?UTF-8?q?=E5=86=B5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/modules/system/repository/sys_user.impl.go | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/modules/system/repository/sys_user.impl.go b/src/modules/system/repository/sys_user.impl.go index 89209e38..36c2bdde 100644 --- a/src/modules/system/repository/sys_user.impl.go +++ b/src/modules/system/repository/sys_user.impl.go @@ -515,7 +515,8 @@ func (r *SysUserImpl) UpdateUser(sysUser model.SysUser) int64 { // DeleteUserByIds 批量删除用户信息 func (r *SysUserImpl) DeleteUserByIds(userIds []string) int64 { placeholder := repo.KeyPlaceholderByQuery(len(userIds)) - sql := "update sys_user set del_flag = '1' where user_id in (" + placeholder + ")" + username := "CASE WHEN user_name = '' THEN user_name WHEN LENGTH(user_name) >= 36 THEN CONCAT('del_', SUBSTRING(user_name, 5, 36)) ELSE CONCAT('del_', user_name) END" + sql := fmt.Sprintf("update sys_user set del_flag = '1', user_name = %s where user_id in (%s)", username, placeholder) parameters := repo.ConvertIdsSlice(userIds) results, err := datasource.ExecDB("", sql, parameters) if err != nil { From 96ee5837c479e2a22798d7621b8de3f1ad5596fe Mon Sep 17 00:00:00 2001 From: TsMask <340112800@qq.com> Date: Mon, 1 Apr 2024 16:47:14 +0800 Subject: [PATCH 03/12] =?UTF-8?q?feat:=20=E6=96=87=E4=BB=B6=E4=B8=8A?= =?UTF-8?q?=E4=BC=A0=E6=94=AF=E6=8C=81ini=E6=A0=BC=E5=BC=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/framework/config/config/config.default.yaml | 2 ++ src/framework/constants/uploadsubpath/uploadsubpath.go | 4 ++++ 2 files changed, 6 insertions(+) diff --git a/src/framework/config/config/config.default.yaml b/src/framework/config/config/config.default.yaml index 4878ffaa..d61689d0 100644 --- a/src/framework/config/config/config.default.yaml +++ b/src/framework/config/config/config.default.yaml @@ -68,6 +68,8 @@ upload: # 软件包 - ".deb" - ".rpm" + # 验证文件 + - ".ini" # cors 跨域 cors: diff --git a/src/framework/constants/uploadsubpath/uploadsubpath.go b/src/framework/constants/uploadsubpath/uploadsubpath.go index c58e87f7..b4c181f4 100644 --- a/src/framework/constants/uploadsubpath/uploadsubpath.go +++ b/src/framework/constants/uploadsubpath/uploadsubpath.go @@ -26,6 +26,9 @@ const ( // 软件包 SOFTWARE = "software" + + // 授权文件 + LICENSE = "license" ) // 子路径类型映射 @@ -38,4 +41,5 @@ var UploadSubpath = map[string]string{ DOWNLOAD: "下载", CHUNK: "切片", SOFTWARE: "软件包", + LICENSE: "授权文件", } From bdd04105fe39553d0a8245164737a0c23fe5a37e Mon Sep 17 00:00:00 2001 From: TsMask <340112800@qq.com> Date: Mon, 1 Apr 2024 16:53:20 +0800 Subject: [PATCH 04/12] =?UTF-8?q?fix:=20=E7=A7=BB=E9=99=A4CheckExtValidReq?= =?UTF-8?q?uest=E6=A3=80=E6=9F=A5=E6=97=A7token?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- features/dbrest/dbrest.go | 65 +++++++++++++++++++-------------------- features/lm/logbak.go | 12 ++++---- features/mml/mml.go | 29 +++++++++-------- 3 files changed, 52 insertions(+), 54 deletions(-) diff --git a/features/dbrest/dbrest.go b/features/dbrest/dbrest.go index fd1fb523..71e95ac8 100644 --- a/features/dbrest/dbrest.go +++ b/features/dbrest/dbrest.go @@ -189,13 +189,13 @@ func ExtDatabaseExecSQL(w http.ResponseWriter, r *http.Request) { log.Debug("ExtDatabaseExecSQL processing... ") var sql []string - var err error + // var err error - _, err = services.CheckExtValidRequest(w, r) - if err != nil { - log.Error("Request error:", err) - return - } + // _, err = services.CheckExtValidRequest(w, r) + // if err != nil { + // log.Error("Request error:", err) + // return + // } //vars := mux.Vars(r) //tblName := vars["objectTypeValue"] @@ -250,11 +250,11 @@ func ExtDatabaseGetData(w http.ResponseWriter, r *http.Request) { log.Debug("ExtDatabaseGetData processing... ") var sql []string - token, err := services.CheckExtValidRequest(w, r) - if err != nil { - log.Error("Request error:", err) - return - } + // token, err := services.CheckExtValidRequest(w, r) + // if err != nil { + // log.Error("Request error:", err) + // return + // } pack := "dbrest" vars := mux.Vars(r) @@ -262,7 +262,7 @@ func ExtDatabaseGetData(w http.ResponseWriter, r *http.Request) { dbname := vars["elementTypeValue"] tbname := vars["objectTypeValue"] - log.Debugf("token:%s, method:%s, module:%s, dbname:%s, tbname:%s, pack:%s", token, r.Method, module, dbname, tbname, pack) + log.Debugf("method:%s, module:%s, dbname:%s, tbname:%s, pack:%s", r.Method, module, dbname, tbname, pack) // exist, err := services.CheckUserPermission(token, strings.ToLower(r.Method), module, dbname, tbname, pack) // if err != nil { @@ -296,12 +296,11 @@ func ExtDatabaseGetData(w http.ResponseWriter, r *http.Request) { for i, s := range sql { log.Tracef("SQL[%d]: %s", i, sql[i]) - rows := make([]map[string]interface{}, 0) mapRows := make(map[string]interface{}) if s != "" { // err = XEngine.SQL(s).Find(&rows) - if IsQuerySQL(s) == false { + if !IsQuerySQL(s) { services.ResponseNotAcceptable406QuerySQLError(w) return } @@ -311,7 +310,7 @@ func ExtDatabaseGetData(w http.ResponseWriter, r *http.Request) { querySQL = querySQL + " " + ls } log.Debug("querySQL:", querySQL) - rows, err = DbClient.XEngine.QueryInterface(querySQL) + rows, err := DbClient.XEngine.QueryInterface(querySQL) if err != nil { log.Error("SQL failed:", err) services.ResponseInternalServerError500DatabaseOperationFailed(w) @@ -333,11 +332,11 @@ func ExtDatabaseGetData(w http.ResponseWriter, r *http.Request) { func ExtDatabaseInsertData(w http.ResponseWriter, r *http.Request) { log.Debug("ExtDatabaseInsertData processing... ") - token, err := services.CheckExtValidRequest(w, r) - if err != nil { - log.Error("Request error:", err) - return - } + // token, err := services.CheckExtValidRequest(w, r) + // if err != nil { + // log.Error("Request error:", err) + // return + // } body, err := io.ReadAll(io.LimitReader(r.Body, global.RequestBodyMaxLen)) //io.LimitReader限制大小 if err != nil { @@ -352,7 +351,7 @@ func ExtDatabaseInsertData(w http.ResponseWriter, r *http.Request) { tbname := vars["objectTypeValue"] pack := "dbrest" - log.Debugf("token:%s, method:%s, module:%s, dbname:%s, tbname:%s, pack:%s", token, r.Method, module, dbname, tbname, pack) + log.Debugf("method:%s, module:%s, dbname:%s, tbname:%s, pack:%s", r.Method, module, dbname, tbname, pack) // exist, err := services.CheckUserPermission(token, strings.ToLower(r.Method), module, dbname, tbname, pack) // if err != nil { @@ -397,11 +396,11 @@ func ExtDatabaseInsertData(w http.ResponseWriter, r *http.Request) { func ExtDatabaseUpdateData(w http.ResponseWriter, r *http.Request) { log.Debug("ExtDatabaseUpdateData processing... ") - token, err := services.CheckExtValidRequest(w, r) - if err != nil { - log.Error("Request error:", err) - return - } + // token, err := services.CheckExtValidRequest(w, r) + // if err != nil { + // log.Error("Request error:", err) + // return + // } vars := mux.Vars(r) module := "" @@ -409,7 +408,7 @@ func ExtDatabaseUpdateData(w http.ResponseWriter, r *http.Request) { tbname := vars["objectTypeValue"] pack := "dbrest" - log.Debugf("token:%s, method:%s, module:%s, dbname:%s, tbname:%s, pack:%s", token, r.Method, module, dbname, tbname, pack) + log.Debugf("method:%s, module:%s, dbname:%s, tbname:%s, pack:%s", r.Method, module, dbname, tbname, pack) // exist, err := services.CheckUserPermission(token, strings.ToLower(r.Method), module, dbname, tbname, pack) // if err != nil { @@ -461,11 +460,11 @@ func ExtDatabaseUpdateData(w http.ResponseWriter, r *http.Request) { func ExtDatabaseDeleteData(w http.ResponseWriter, r *http.Request) { log.Debug("ExtDatabaseDeleteData processing... ") - token, err := services.CheckExtValidRequest(w, r) - if err != nil { - log.Error("Request error:", err) - return - } + // token, err := services.CheckExtValidRequest(w, r) + // if err != nil { + // log.Error("Request error:", err) + // return + // } vars := mux.Vars(r) module := "" @@ -473,7 +472,7 @@ func ExtDatabaseDeleteData(w http.ResponseWriter, r *http.Request) { tbname := vars["objectTypeValue"] pack := "dbreset" - log.Debugf("token:%s, method:%s, module:%, dbname:%s, tbname:%s pack:%s", token, r.Method, module, dbname, tbname, pack) + log.Debugf("method:%s, module:%, dbname:%s, tbname:%s pack:%s", r.Method, module, dbname, tbname, pack) // exist, err := services.CheckUserPermission(token, strings.ToLower(r.Method), module, dbname, tbname, pack) // if err != nil { diff --git a/features/lm/logbak.go b/features/lm/logbak.go index 1b8008a1..fc6d3697 100644 --- a/features/lm/logbak.go +++ b/features/lm/logbak.go @@ -82,18 +82,18 @@ func InitDbClient(dbType, dbUser, dbPassword, dbHost, dbPort, dbName, dbParam st func ExtDatabaseBackupData(w http.ResponseWriter, r *http.Request) { log.Debug("ExtDatabaseBackupData processing... ") - token, err := services.CheckExtValidRequest(w, r) - if err != nil { - log.Error("Request error:", err) - return - } + // token, err := services.CheckExtValidRequest(w, r) + // if err != nil { + // log.Error("Request error:", err) + // return + // } vars := mux.Vars(r) module := vars["managementModule"] dbname := vars["dataStorage"] tbname := vars["dataObject"] pack := "lm" - log.Debugf("token:%s, method:%s, module:%s dbname:%s, tbname:%s pack:%s", token, r.Method, module, dbname, tbname, pack) + log.Debugf("method:%s, module:%s dbname:%s, tbname:%s pack:%s", r.Method, module, dbname, tbname, pack) // exist, err := services.CheckUserPermission(token, strings.ToLower(r.Method), module, dbname, tbname, pack) // if err != nil { // log.Error("Failed to get permission:", err) diff --git a/features/mml/mml.go b/features/mml/mml.go index ad025b14..97e3c3e3 100644 --- a/features/mml/mml.go +++ b/features/mml/mml.go @@ -186,11 +186,11 @@ func PostMML2ToNF(w http.ResponseWriter, r *http.Request) { func PostMMLToNF(w http.ResponseWriter, r *http.Request) { log.Debug("PostMMLToNF processing... ") - token, err := services.CheckExtValidRequest(w, r) - if err != nil { - log.Error("Request error:", err) - return - } + // token, err := services.CheckExtValidRequest(w, r) + // if err != nil { + // log.Error("Request error:", err) + // return + // } // 经过测试,linux下,延时需要大于100ms // var TIME_DELAY_AFTER_WRITE time.Duration = 200 // var TIME_DEAD_LINE time.Duration = 10 @@ -214,8 +214,7 @@ func PostMMLToNF(w http.ResponseWriter, r *http.Request) { } log.Debug("neType:", neType, "neId", neId) - log.Debugf("token:%s, method:%s, managementType:%s dbname:%s, tbname:%s pack:%s", - token, r.Method, module, neType, neId[0], pack) + log.Debugf("method:%s, managementType:%s dbname:%s, tbname:%s pack:%s", r.Method, module, neType, neId[0], pack) var buf [20 * 1024]byte //buf := make([]byte, 0) @@ -250,7 +249,7 @@ func PostMMLToNF(w http.ResponseWriter, r *http.Request) { } neInfo := new(dborm.NeInfo) - neInfo, err = dborm.XormGetNeInfo(neType, neId[0]) + neInfo, err := dborm.XormGetNeInfo(neType, neId[0]) if err != nil { log.Error("dborm.XormGetNeInfo is failed:", err) services.ResponseInternalServerError500DatabaseOperationFailed(w) @@ -631,11 +630,11 @@ func PostMMLToNF(w http.ResponseWriter, r *http.Request) { func PostMMLToOMC(w http.ResponseWriter, r *http.Request) { log.Debug("PostMMLToOMC processing... ") - token, err := services.CheckExtValidRequest(w, r) - if err != nil { - log.Error("Failed to CheckMmlValidRequest:", err) - return - } + // token, err := services.CheckExtValidRequest(w, r) + // if err != nil { + // log.Error("Failed to CheckMmlValidRequest:", err) + // return + // } params := r.URL.Query() neId := params["ne_id"] @@ -646,7 +645,7 @@ func PostMMLToOMC(w http.ResponseWriter, r *http.Request) { } neInfo := new(dborm.NeInfo) - neInfo, err = dborm.XormGetNeInfo("OMC", neId[0]) + neInfo, err := dborm.XormGetNeInfo("OMC", neId[0]) if err != nil { log.Error("dborm.XormGetNeInfo is failed:", err) services.ResponseInternalServerError500DatabaseOperationFailed(w) @@ -670,7 +669,7 @@ func PostMMLToOMC(w http.ResponseWriter, r *http.Request) { MmlHome: config.GetYamlConfig().MML.MmlHome, Limit: 50, User: "", - SessionToken: token, // 旧token + SessionToken: "", // 旧token Authorization: r.Header.Get(tokenConst.HEADER_KEY), // 请求Token HttpUri: hostUri, UserAgent: config.GetDefaultUserAgent(), From a8280476d0a493888a72ddfed4ba03ee70b59928 Mon Sep 17 00:00:00 2001 From: TsMask <340112800@qq.com> Date: Mon, 1 Apr 2024 16:55:59 +0800 Subject: [PATCH 05/12] =?UTF-8?q?feat:=20=E7=BD=91=E5=85=83=E6=8E=88?= =?UTF-8?q?=E6=9D=83=E6=BF=80=E6=B4=BB=E4=BF=A1=E6=81=AF?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../network_element/controller/ne_license.go | 301 ++++++++++++++++ .../network_element/model/ne_license.go | 28 ++ .../network_element/repository/ne_license.go | 27 ++ .../repository/ne_license.impl.go | 333 ++++++++++++++++++ .../network_element/service/ne_license.go | 37 ++ .../service/ne_license.impl.go | 183 ++++++++++ 6 files changed, 909 insertions(+) create mode 100644 src/modules/network_element/controller/ne_license.go create mode 100644 src/modules/network_element/model/ne_license.go create mode 100644 src/modules/network_element/repository/ne_license.go create mode 100644 src/modules/network_element/repository/ne_license.impl.go create mode 100644 src/modules/network_element/service/ne_license.go create mode 100644 src/modules/network_element/service/ne_license.impl.go diff --git a/src/modules/network_element/controller/ne_license.go b/src/modules/network_element/controller/ne_license.go new file mode 100644 index 00000000..7022df85 --- /dev/null +++ b/src/modules/network_element/controller/ne_license.go @@ -0,0 +1,301 @@ +package controller + +import ( + "fmt" + "strings" + + "be.ems/src/framework/i18n" + "be.ems/src/framework/utils/ctx" + "be.ems/src/framework/utils/parse" + "be.ems/src/framework/vo/result" + "be.ems/src/modules/network_element/model" + neService "be.ems/src/modules/network_element/service" + "github.com/gin-gonic/gin" + "github.com/gin-gonic/gin/binding" +) + +// 实例化控制层 NeLicenseController 结构体 +var NewNeLicense = &NeLicenseController{ + neLicenseService: neService.NewNeLicenseImpl, + neInfoService: neService.NewNeInfoImpl, +} + +// 网元授权激活信息请求 +// +// PATH /license +type NeLicenseController struct { + // 网元授权激活信息服务 + neLicenseService neService.INeLicense + // 网元信息服务 + neInfoService neService.INeInfo +} + +// 网元授权激活信息列表 +// +// GET /list +func (s *NeLicenseController) List(c *gin.Context) { + querys := ctx.QueryMap(c) + data := s.neLicenseService.SelectPage(querys) + + c.JSON(200, result.Ok(data)) +} + +// 网元授权激活信息 +// +// GET /:licenseId +func (s *NeLicenseController) Info(c *gin.Context) { + language := ctx.AcceptLanguage(c) + licenseId := c.Param("licenseId") + if licenseId == "" { + c.JSON(400, result.CodeMsg(400, i18n.TKey(language, "app.common.err400"))) + return + } + + neLicense := s.neLicenseService.SelectById(licenseId) + if neLicense.ID != licenseId { + // 没有可访问网元授权激活数据! + c.JSON(200, result.ErrMsg(i18n.TKey(language, "neLicense.noData"))) + return + } + + c.JSON(200, result.OkData(neLicense)) +} + +// 网元授权激活信息新增 +// +// POST / +func (s *NeLicenseController) Add(c *gin.Context) { + language := ctx.AcceptLanguage(c) + var body model.NeLicense + err := c.ShouldBindBodyWith(&body, binding.JSON) + if err != nil || body.ID != "" { + 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 + } + + // 检查属性值唯一 + uniqueInfo := s.neLicenseService.CheckUniqueTypeAndID(neInfo.NeType, neInfo.NeId, "") + if !uniqueInfo { + // 网元授权激活操作【%s】失败,网元类型信息已存在 + msg := i18n.TTemplate(language, "neLicense.errKeyExists", map[string]any{"name": neInfo.NeType}) + c.JSON(200, result.ErrMsg(msg)) + return + } + + // 读取授权码 + code, _ := s.neLicenseService.ReadLicenseInfo(neInfo) + body.ActivationRequestCode = code + + body.CreateBy = ctx.LoginUserToUserName(c) + insertId := s.neLicenseService.Insert(body) + if insertId != "" { + c.JSON(200, result.Ok(nil)) + return + } + c.JSON(200, result.Err(nil)) +} + +// 网元授权激活信息修改 +// +// PUT / +func (s *NeLicenseController) Edit(c *gin.Context) { + language := ctx.AcceptLanguage(c) + var body model.NeLicense + err := c.ShouldBindBodyWith(&body, binding.JSON) + if err != nil || body.ID == "" { + c.JSON(400, result.CodeMsg(400, i18n.TKey(language, "app.common.err400"))) + return + } + + // 检查属性值唯一 + uniqueInfo := s.neLicenseService.CheckUniqueTypeAndID(body.NeType, body.NeId, body.ID) + if !uniqueInfo { + // 网元授权激活操作【%s】失败,网元类型信息已存在 + msg := i18n.TTemplate(language, "neLicense.errKeyExists", map[string]any{"name": body.NeType}) + c.JSON(200, result.ErrMsg(msg)) + return + } + + // 检查是否存在 + neLicense := s.neLicenseService.SelectById(body.ID) + if neLicense.ID != body.ID { + // 没有可访问网元授权激活数据! + c.JSON(200, result.ErrMsg(i18n.TKey(language, "neLicense.noData"))) + return + } + + body.UpdateBy = ctx.LoginUserToUserName(c) + rows := s.neLicenseService.Update(body) + if rows > 0 { + c.JSON(200, result.Ok(nil)) + return + } + c.JSON(200, result.Err(nil)) +} + +// 网元授权激活信息删除 +// +// DELETE /:licenseIds +func (s *NeLicenseController) Remove(c *gin.Context) { + language := ctx.AcceptLanguage(c) + licenseIds := c.Param("licenseIds") + if licenseIds == "" { + c.JSON(400, result.CodeMsg(400, i18n.TKey(language, "app.common.err400"))) + return + } + // 处理字符转id数组后去重 + ids := strings.Split(licenseIds, ",") + uniqueIDs := parse.RemoveDuplicates(ids) + if len(uniqueIDs) <= 0 { + c.JSON(200, result.Err(nil)) + return + } + rows, err := s.neLicenseService.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)) +} + +// 网元授权激活授权申请码 +// +// GET /code +func (s *NeLicenseController) Code(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 + } + + // 查询网元获取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 + } + + // 检查是否存在授权记录 + neLicense := s.neLicenseService.SelectByNeTypeAndNeID(neInfo.NeType, neInfo.NeId) + if neLicense.NeId != querys.NeId { + // 没有可访问网元授权激活数据! + c.JSON(200, result.ErrMsg(i18n.TKey(language, "neLicense.noData"))) + return + } + + // 更新授权码 + code, licensePath := s.neLicenseService.ReadLicenseInfo(neInfo) + neLicense.ActivationRequestCode = code + if licensePath != "" { + neLicense.LicensePath = licensePath + } else { + neLicense.SerialNum = "" + neLicense.ExpiryDate = "" + neLicense.Status = "0" + } + neLicense.UpdateBy = ctx.LoginUserToUserName(c) + s.neLicenseService.Update(neLicense) + + c.JSON(200, result.OkData(code)) +} + +// 网元授权激活授权文件替换 +// +// POST /change +func (s *NeLicenseController) Change(c *gin.Context) { + language := ctx.AcceptLanguage(c) + var body model.NeLicense + err := c.ShouldBindBodyWith(&body, binding.JSON) + if err != nil || body.HostId == "" { + c.JSON(400, result.CodeMsg(400, i18n.TKey(language, "app.common.err400"))) + return + } + + // 检查是否存在授权记录 + neLicense := s.neLicenseService.SelectByNeTypeAndNeID(body.NeType, body.NeId) + if neLicense.NeId != body.NeId { + body.Status = "0" + body.CreateBy = ctx.LoginUserToUserName(c) + body.ID = s.neLicenseService.Insert(body) + } else { + neLicense.LicensePath = body.LicensePath + neLicense.Status = "0" + neLicense.UpdateBy = ctx.LoginUserToUserName(c) + s.neLicenseService.Update(neLicense) + } + + // 进行上传替换 + err = s.neLicenseService.UploadToNeHost(body) + if err != nil { + c.JSON(200, result.ErrMsg(err.Error())) + return + } + c.JSON(200, result.Ok(nil)) +} + +// 网元授权激活状态 +// +// GET /state +func (s *NeLicenseController) State(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 + } + + // 查询网元获取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 + } + + // 检查是否存在授权记录 + neLicense := s.neLicenseService.SelectByNeTypeAndNeID(neInfo.NeType, neInfo.NeId) + if neLicense.NeId != querys.NeId { + // 没有可访问网元授权激活数据! + c.JSON(200, result.ErrMsg(i18n.TKey(language, "neLicense.noData"))) + return + } + + // 查询网元状态 + neState, err := neService.NeState(neInfo) + if err != nil { + c.JSON(200, result.ErrMsg("network element service anomaly")) + return + } + + // 更新授权信息 + neLicense.SerialNum = fmt.Sprint(neState["sn"]) + neLicense.ExpiryDate = fmt.Sprint(neState["expire"]) + code, licensePath := s.neLicenseService.ReadLicenseInfo(neInfo) + neLicense.ActivationRequestCode = code + neLicense.LicensePath = licensePath + neLicense.Status = "1" + neLicense.UpdateBy = ctx.LoginUserToUserName(c) + rows := s.neLicenseService.Update(neLicense) + if rows > 0 { + c.JSON(200, result.OkData(map[string]string{ + "sn": neLicense.SerialNum, + "expire": neLicense.ExpiryDate, + })) + return + } + c.JSON(200, result.Err(nil)) +} diff --git a/src/modules/network_element/model/ne_license.go b/src/modules/network_element/model/ne_license.go new file mode 100644 index 00000000..2f202bc6 --- /dev/null +++ b/src/modules/network_element/model/ne_license.go @@ -0,0 +1,28 @@ +package model + +// NeLicense 网元授权激活信息 ne_license +type NeLicense struct { + ID string `json:"id" gorm:"id"` + NeType string `json:"neType" gorm:"ne_type" binding:"required"` // 网元类型 + NeId string `json:"neId" gorm:"ne_id" binding:"required"` // 网元ID + ActivationRequestCode string `json:"activationRequestCode" gorm:"activation_request_code"` // 激活申请代码 + LicensePath string `json:"licensePath" gorm:"license_path"` // 激活授权文件 + SerialNum string `json:"serialNum" gorm:"serial_num"` // 序列号 + ExpiryDate string `json:"expiryDate" gorm:"expiry_date"` // 许可证到期日期 + Status string `json:"status" gorm:"status"` // 状态 ''ACTIVE'',''INACTIVE'',''PENDING'' + 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"` // 更新时间 + + // ====== 非数据库字段属性 ====== + + Reload bool `json:"reload,omitempty" gorm:"-"` // 刷新重启网元 + HostId string `json:"hostId,omitempty" gorm:"-"` // 已记录的主机ID +} + +// TableName 表名称 +func (*NeLicense) TableName() string { + return "ne_license" +} diff --git a/src/modules/network_element/repository/ne_license.go b/src/modules/network_element/repository/ne_license.go new file mode 100644 index 00000000..c185d67e --- /dev/null +++ b/src/modules/network_element/repository/ne_license.go @@ -0,0 +1,27 @@ +package repository + +import "be.ems/src/modules/network_element/model" + +// INeLicense 网元授权激活信息 数据层接口 +type INeLicense interface { + // SelectPage 根据条件分页查询字典类型 + SelectPage(query map[string]any) map[string]any + + // SelectList 根据实体查询 + SelectList(neLicense model.NeLicense) []model.NeLicense + + // SelectByIds 通过ID查询 + SelectByIds(ids []string) []model.NeLicense + + // Insert 新增信息 + Insert(neLicense model.NeLicense) string + + // Update 修改信息 + Update(neLicense model.NeLicense) int64 + + // DeleteByIds 批量删除信息 + DeleteByIds(ids []string) int64 + + // CheckUniqueTypeAndID 校验网元类型和网元ID是否唯一 + CheckUniqueTypeAndID(neLicense model.NeLicense) string +} diff --git a/src/modules/network_element/repository/ne_license.impl.go b/src/modules/network_element/repository/ne_license.impl.go new file mode 100644 index 00000000..fcbb1f0d --- /dev/null +++ b/src/modules/network_element/repository/ne_license.impl.go @@ -0,0 +1,333 @@ +package repository + +import ( + "fmt" + "strings" + "time" + + "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_element/model" +) + +// 实例化数据层 NewNeLicense 结构体 +var NewNeLicenseImpl = &NeLicenseImpl{ + selectSql: `select + id, ne_type, ne_id, activation_request_code, license_path, serial_num, expiry_date, status, remark, create_by, create_time, update_by, update_time + from ne_license`, + + resultMap: map[string]string{ + "id": "ID", + "ne_type": "NeType", + "ne_id": "NeId", + "activation_request_code": "ActivationRequestCode", + "license_path": "LicensePath", + "serial_num": "SerialNum", + "expiry_date": "ExpiryDate", + "status": "Status", + "remark": "Remark", + "create_by": "CreateBy", + "create_time": "CreateTime", + "update_by": "UpdateBy", + "update_time": "UpdateTime", + }, +} + +// NeLicenseImpl 网元授权激活信息 数据层处理 +type NeLicenseImpl struct { + // 查询视图对象SQL + selectSql string + // 结果字段与实体映射 + resultMap map[string]string +} + +// convertResultRows 将结果记录转实体结果组 +func (r *NeLicenseImpl) convertResultRows(rows []map[string]any) []model.NeLicense { + arr := make([]model.NeLicense, 0) + for _, row := range rows { + item := model.NeLicense{} + 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 *NeLicenseImpl) 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["expiryDate"]; ok && v != "" { + conditions = append(conditions, "expiry_date = ?") + params = append(params, strings.Trim(v.(string), " ")) + } + if v, ok := query["createBy"]; ok && v != "" { + conditions = append(conditions, "create_by like 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_license" + 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 := " 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 *NeLicenseImpl) SelectList(neLicense model.NeLicense) []model.NeLicense { + // 查询条件拼接 + var conditions []string + var params []any + if neLicense.NeType != "" { + conditions = append(conditions, "ne_type = ?") + params = append(params, neLicense.NeType) + } + if neLicense.NeId != "" { + conditions = append(conditions, "ne_id = ?") + params = append(params, neLicense.NeId) + } + if neLicense.ExpiryDate != "" { + conditions = append(conditions, "expiry_date = ?") + params = append(params, neLicense.ExpiryDate) + } + if neLicense.CreateBy != "" { + conditions = append(conditions, "create_by like concat(?, '%')") + params = append(params, neLicense.CreateBy) + } + + // 构建查询条件语句 + whereSql := "" + if len(conditions) > 0 { + whereSql += " where " + strings.Join(conditions, " and ") + } + + // 查询数据 + querySql := r.selectSql + whereSql + " order by id asc " + results, err := datasource.RawDB("", querySql, params) + if err != nil { + logger.Errorf("query err => %v", err) + } + + // 转换实体 + return r.convertResultRows(results) +} + +// SelectByIds 通过ID查询 +func (r *NeLicenseImpl) SelectByIds(cmdIds []string) []model.NeLicense { + 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.NeLicense{} + } + // 转换实体 + return r.convertResultRows(results) +} + +// CheckUniqueTypeAndID 校验网元类型和网元ID是否唯一 +func (r *NeLicenseImpl) CheckUniqueTypeAndID(neLicense model.NeLicense) string { + // 查询条件拼接 + var conditions []string + var params []any + if neLicense.NeType != "" { + conditions = append(conditions, "ne_type = ?") + params = append(params, neLicense.NeType) + } + if neLicense.NeId != "" { + conditions = append(conditions, "ne_id = ?") + params = append(params, neLicense.NeId) + } + + // 构建查询条件语句 + whereSql := "" + if len(conditions) > 0 { + whereSql += " where " + strings.Join(conditions, " and ") + } else { + return "" + } + + // 查询数据 + querySql := "select id as 'str' from ne_license " + whereSql + " limit 1" + results, err := datasource.RawDB("", querySql, params) + if err != nil { + logger.Errorf("query err %v", err) + return "" + } + if len(results) > 0 { + return fmt.Sprint(results[0]["str"]) + } + return "" +} + +// Insert 新增信息 +func (r *NeLicenseImpl) Insert(neLicense model.NeLicense) string { + // 参数拼接 + params := make(map[string]any) + if neLicense.NeType != "" { + params["ne_type"] = neLicense.NeType + } + if neLicense.NeId != "" { + params["ne_id"] = neLicense.NeId + } + if neLicense.ActivationRequestCode != "" { + params["activation_request_code"] = neLicense.ActivationRequestCode + } + if neLicense.LicensePath != "" { + params["license_path"] = neLicense.LicensePath + } + if neLicense.SerialNum != "" { + params["serial_num"] = neLicense.SerialNum + } + if neLicense.ExpiryDate != "" { + params["expiry_date"] = neLicense.ExpiryDate + } + if neLicense.Status != "" { + params["status"] = neLicense.Status + } + if neLicense.Remark != "" { + params["remark"] = neLicense.Remark + } + if neLicense.CreateBy != "" { + params["create_by"] = neLicense.CreateBy + params["create_time"] = time.Now().UnixMilli() + } + + // 构建执行语句 + keys, placeholder, values := repo.KeyPlaceholderValueByInsert(params) + sql := "insert into ne_license (" + 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 *NeLicenseImpl) Update(neLicense model.NeLicense) int64 { + // 参数拼接 + params := make(map[string]any) + if neLicense.NeType != "" { + params["ne_type"] = neLicense.NeType + } + if neLicense.NeId != "" { + params["ne_id"] = neLicense.NeId + } + if neLicense.ActivationRequestCode != "" { + params["activation_request_code"] = neLicense.ActivationRequestCode + } + if neLicense.LicensePath != "" { + params["license_path"] = neLicense.LicensePath + } + if neLicense.SerialNum != "" { + params["serial_num"] = neLicense.SerialNum + } + if neLicense.ExpiryDate != "" { + params["expiry_date"] = neLicense.ExpiryDate + } + if neLicense.Status != "" { + params["status"] = neLicense.Status + } + if neLicense.Remark != "" { + params["remark"] = neLicense.Remark + } + if neLicense.UpdateBy != "" { + params["update_by"] = neLicense.UpdateBy + params["update_time"] = time.Now().UnixMilli() + } + + // 构建执行语句 + keys, values := repo.KeyValueByUpdate(params) + sql := "update ne_license set " + strings.Join(keys, ",") + " where id = ?" + + // 执行更新 + values = append(values, neLicense.ID) + rows, err := datasource.ExecDB("", sql, values) + if err != nil { + logger.Errorf("update row : %v", err.Error()) + return 0 + } + return rows +} + +// DeleteByIds 批量删除信息 +func (r *NeLicenseImpl) DeleteByIds(cmdIds []string) int64 { + placeholder := repo.KeyPlaceholderByQuery(len(cmdIds)) + sql := "delete from ne_license where id in (" + placeholder + ")" + parameters := repo.ConvertIdsSlice(cmdIds) + 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/service/ne_license.go b/src/modules/network_element/service/ne_license.go new file mode 100644 index 00000000..c5ea23a1 --- /dev/null +++ b/src/modules/network_element/service/ne_license.go @@ -0,0 +1,37 @@ +package service + +import "be.ems/src/modules/network_element/model" + +// INeLicense 网元授权激活信息 服务层接口 +type INeLicense interface { + // SelectPage 根据条件分页查询字典类型 + SelectPage(query map[string]any) map[string]any + + // SelectList 根据实体查询 + SelectList(neLicense model.NeLicense) []model.NeLicense + + // SelectById 通过ID查询 + SelectById(id string) model.NeLicense + + // Insert 新增信息 + Insert(neLicense model.NeLicense) string + + // Update 修改信息 + Update(neLicense model.NeLicense) int64 + + // DeleteByIds 批量删除信息 + DeleteByIds(ids []string) (int64, error) + + // CheckUniqueTypeAndID 校验网元类型和网元ID是否唯一 + CheckUniqueTypeAndID(neType, neId, id string) bool + + // SelectByNeTypeAndNeID 通过ne_type和ne_id查询信息 + SelectByNeTypeAndNeID(neType, neId string) model.NeLicense + + // ReadLicenseInfo 读取授权文件信息 + // 激活申请码, 激活文件 + ReadLicenseInfo(neInfo model.NeInfo) (string, string) + + // UploadToNeHost 授权文件上传到网元主机 + UploadToNeHost(neLicense model.NeLicense) error +} diff --git a/src/modules/network_element/service/ne_license.impl.go b/src/modules/network_element/service/ne_license.impl.go new file mode 100644 index 00000000..d41b1918 --- /dev/null +++ b/src/modules/network_element/service/ne_license.impl.go @@ -0,0 +1,183 @@ +package service + +import ( + "fmt" + "os" + "runtime" + "strings" + "time" + + "be.ems/src/framework/utils/file" + "be.ems/src/framework/utils/ssh" + "be.ems/src/modules/network_element/model" + "be.ems/src/modules/network_element/repository" +) + +// 实例化服务层 NeLicenseImpl 结构体 +var NewNeLicenseImpl = &NeLicenseImpl{ + neLicenseRepository: repository.NewNeLicenseImpl, +} + +// NeLicenseImpl 网元授权激活信息 服务层处理 +type NeLicenseImpl struct { + // 网元授权激活信息表 + neLicenseRepository repository.INeLicense +} + +// SelectNeHostPage 分页查询列表数据 +func (r *NeLicenseImpl) SelectPage(query map[string]any) map[string]any { + return r.neLicenseRepository.SelectPage(query) +} + +// SelectConfigList 查询列表 +func (r *NeLicenseImpl) SelectList(neLicense model.NeLicense) []model.NeLicense { + return r.neLicenseRepository.SelectList(neLicense) +} + +// SelectByIds 通过ID查询 +func (r *NeLicenseImpl) SelectById(id string) model.NeLicense { + if id == "" { + return model.NeLicense{} + } + neLicenses := r.neLicenseRepository.SelectByIds([]string{id}) + if len(neLicenses) > 0 { + return neLicenses[0] + } + return model.NeLicense{} +} + +// Insert 新增信息 +func (r *NeLicenseImpl) Insert(neLicense model.NeLicense) string { + return r.neLicenseRepository.Insert(neLicense) +} + +// Update 修改信息 +func (r *NeLicenseImpl) Update(neLicense model.NeLicense) int64 { + return r.neLicenseRepository.Update(neLicense) +} + +// DeleteByIds 批量删除信息 +func (r *NeLicenseImpl) DeleteByIds(ids []string) (int64, error) { + // 检查是否存在 + rowIds := r.neLicenseRepository.SelectByIds(ids) + if len(rowIds) <= 0 { + return 0, fmt.Errorf("neLicense.noData") + } + + if len(rowIds) == len(ids) { + rows := r.neLicenseRepository.DeleteByIds(ids) + return rows, nil + } + // 删除信息失败! + return 0, fmt.Errorf("delete fail") +} + +// SelectByTypeAndID 通过网元类型和网元ID查询 +func (r *NeLicenseImpl) SelectByTypeAndID(neType, neId string) model.NeLicense { + neLicenses := r.neLicenseRepository.SelectList(model.NeLicense{ + NeType: neType, + NeId: neId, + }) + if len(neLicenses) > 0 { + return neLicenses[0] + } + return model.NeLicense{} +} + +// CheckUniqueTypeAndID 校验网元类型和网元ID是否唯一 +func (r *NeLicenseImpl) CheckUniqueTypeAndID(neType, neId, id string) bool { + uniqueId := r.neLicenseRepository.CheckUniqueTypeAndID(model.NeLicense{ + NeType: neType, + NeId: neId, + }) + if uniqueId == id { + return true + } + return uniqueId == "" +} + +// SelectByNeTypeAndNeID 通过ne_type和ne_id查询信息 +func (r *NeLicenseImpl) SelectByNeTypeAndNeID(neType, neId string) model.NeLicense { + neLicenses := r.neLicenseRepository.SelectList(model.NeLicense{ + NeType: neType, + NeId: neId, + }) + if len(neLicenses) > 0 { + return neLicenses[0] + } + return model.NeLicense{} +} + +// ReadLicenseInfo 读取授权文件信息 +// 激活申请码, 激活文件 +func (r *NeLicenseImpl) ReadLicenseInfo(neInfo model.NeInfo) (string, string) { + neTypeLower := strings.ToLower(neInfo.NeType) + // 网管本地路径 + omcPath := "/usr/local/etc/omc/ne_license" + if runtime.GOOS == "windows" { + omcPath = fmt.Sprintf("C:%s", omcPath) + } + omcPath = fmt.Sprintf("%s/%s/%s", omcPath, neTypeLower, neInfo.NeId) + // 网元端授权文件路径 + nePath := fmt.Sprintf("/usr/local/etc/%s/license", neTypeLower) + + // 复制授权申请码到本地 + err := ssh.FileSCPNeToLocal(neInfo.IP, nePath+"/Activation_request_code.txt", omcPath+"/Activation_request_code.txt") + if err != nil { + return "", "" + } + // 读取文件内容 + bytes, err := os.ReadFile(omcPath + "/Activation_request_code.txt") + if err != nil { + return "", "" + } + + // 激活文件 + licensePath := "" + if err = ssh.FileSCPNeToLocal(neInfo.IP, nePath+"/system.ini", omcPath+"/system.ini"); err == nil { + licensePath = omcPath + "/system.ini" + } + return strings.TrimSpace(string(bytes)), licensePath +} + +// UploadToNeHost 授权文件上传到网元主机 +func (r *NeLicenseImpl) UploadToNeHost(neLicense model.NeLicense) error { + + // 检查文件是否存在 + omcLicensePath := file.ParseUploadFilePath(neLicense.LicensePath) + if _, err := os.Stat(omcLicensePath); err != nil { + return fmt.Errorf("file read failure") + } + + // 检查网元主机 + neHostInfo := NewNeHostImpl.SelectById(neLicense.HostId) + if neHostInfo.HostType != "ssh" || neHostInfo.HostID != neLicense.HostId { + return fmt.Errorf("no found host info") + } + + // 网元端授权文件路径 + neTypeLower := strings.ToLower(neLicense.NeType) + neLicensePath := fmt.Sprintf("/usr/local/etc/%s/license", neTypeLower) + // 修改文件夹权限 + NewNeInfoImpl.NeRunCMD(neLicense.NeType, neLicense.NeId, fmt.Sprintf("sudo chmod o+w %s/", neLicensePath)) + // 尝试备份授权文件 + neLicensePathBack := fmt.Sprintf("%s/system_%s.ini", neLicensePath, time.Now().Format("20060102_150405")) + NewNeInfoImpl.NeRunCMD(neLicense.NeType, neLicense.NeId, fmt.Sprintf("sudo cp -rf %s/system.ini %s", neLicensePath, neLicensePathBack)) + // 上传授权文件去覆盖 + NewNeInfoImpl.NeRunCMD(neLicense.NeType, neLicense.NeId, fmt.Sprintf("sudo chmod o+w %s/system.ini", neLicensePath)) + if err := ssh.FileSCPLocalToNe(neHostInfo.Addr, omcLicensePath, neLicensePath+"/system.ini"); err != nil { + return fmt.Errorf("error uploading license") + } + + // 重启服务 + if neLicense.Reload { + cmdStr := fmt.Sprintf("sudo service %s restart", neTypeLower) + if neTypeLower == "ims" { + cmdStr = "sudo ims-stop && sudo ims-start" + } else if neTypeLower == "omc" { + cmdStr = "sudo /usr/local/omc/bin/omcsvc.sh restart" + } + NewNeInfoImpl.NeRunCMD(neLicense.NeType, neLicense.NeId, cmdStr) + } + return nil +} From 715cf8ab188ee2adbdeb0f5aeae2a1d85254bef3 Mon Sep 17 00:00:00 2001 From: TsMask <340112800@qq.com> Date: Mon, 1 Apr 2024 16:57:15 +0800 Subject: [PATCH 06/12] =?UTF-8?q?feat:=20=E7=BD=91=E5=85=83=E8=BD=AF?= =?UTF-8?q?=E4=BB=B6=E5=8C=85=E4=BF=A1=E6=81=AF?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../network_element/controller/ne_software.go | 56 +++++++------ .../network_element/model/ne_software.go | 25 +++--- .../network_element/repository/ne_software.go | 4 +- .../repository/ne_software.impl.go | 71 +++++++--------- .../network_element/service/ne_software.go | 12 ++- .../service/ne_software.impl.go | 80 ++++++++++++++----- 6 files changed, 142 insertions(+), 106 deletions(-) diff --git a/src/modules/network_element/controller/ne_software.go b/src/modules/network_element/controller/ne_software.go index 1001392f..03d1614c 100644 --- a/src/modules/network_element/controller/ne_software.go +++ b/src/modules/network_element/controller/ne_software.go @@ -49,7 +49,7 @@ func (s *NeSoftwareController) Info(c *gin.Context) { neSoftware := s.neSoftwareService.SelectById(softwareId) if neSoftware.ID != softwareId { - // 没有可访问网元版本数据! + // 没有可访问网元包信息数据! c.JSON(200, result.ErrMsg(i18n.TKey(language, "neSoftware.noData"))) return } @@ -64,20 +64,21 @@ func (s *NeSoftwareController) Add(c *gin.Context) { language := ctx.AcceptLanguage(c) var body model.NeSoftware err := c.ShouldBindBodyWith(&body, binding.JSON) - if err != nil || body.ID != "" { + if err != nil || body.Path == "" || body.ID != "" { c.JSON(400, result.CodeMsg(400, i18n.TKey(language, "app.common.err400"))) return } // 检查属性值唯一 - uniqueSoftware := s.neSoftwareService.CheckUniqueTypeAndFileNameAndVersion(body.NeType, body.FileName, body.Version, "") + uniqueSoftware := s.neSoftwareService.CheckUniqueTypeAndNameAndVersion(body.NeType, body.Name, body.Version, "") if !uniqueSoftware { // 网元软件包操作【%s】失败,网元类型与文件名版本已存在 - msg := i18n.TTemplate(language, "neSoftware.errKeyExists", map[string]any{"name": body.FileName}) + msg := i18n.TTemplate(language, "neSoftware.errKeyExists", map[string]any{"name": body.Name}) c.JSON(200, result.ErrMsg(msg)) return } + body.CreateBy = ctx.LoginUserToUserName(c) insertId := s.neSoftwareService.Insert(body) if insertId != "" { c.JSON(200, result.Ok(nil)) @@ -93,16 +94,16 @@ func (s *NeSoftwareController) Edit(c *gin.Context) { language := ctx.AcceptLanguage(c) var body model.NeSoftware err := c.ShouldBindBodyWith(&body, binding.JSON) - if err != nil || body.ID == "" { + if err != nil || body.Path == "" || body.ID == "" { c.JSON(400, result.CodeMsg(400, i18n.TKey(language, "app.common.err400"))) return } // 检查属性值唯一 - uniqueSoftware := s.neSoftwareService.CheckUniqueTypeAndFileNameAndVersion(body.NeType, body.FileName, body.Version, body.ID) + uniqueSoftware := s.neSoftwareService.CheckUniqueTypeAndNameAndVersion(body.NeType, body.Name, body.Version, body.ID) if !uniqueSoftware { // 网元软件包操作【%s】失败,网元类型与文件名版本已存在 - msg := i18n.TTemplate(language, "neSoftware.errKeyExists", map[string]any{"name": body.FileName}) + msg := i18n.TTemplate(language, "neSoftware.errKeyExists", map[string]any{"name": body.Name}) c.JSON(200, result.ErrMsg(msg)) return } @@ -110,11 +111,12 @@ func (s *NeSoftwareController) Edit(c *gin.Context) { // 检查是否存在 neSoftware := s.neSoftwareService.SelectById(body.ID) if neSoftware.ID != body.ID { - // 没有可访问网元版本数据! + // 没有可访问网元包信息数据! c.JSON(200, result.ErrMsg(i18n.TKey(language, "neSoftware.noData"))) return } + body.UpdateBy = ctx.LoginUserToUserName(c) rows := s.neSoftwareService.Update(body) if rows > 0 { c.JSON(200, result.Ok(nil)) @@ -149,32 +151,40 @@ func (s *NeSoftwareController) Remove(c *gin.Context) { c.JSON(200, result.OkMsg(msg)) } -// 网元软件包安装 +// 网元软件包安装检查 // -// POST /install -func (s *NeSoftwareController) Install(c *gin.Context) { +// POST /checkInstall +func (s *NeSoftwareController) CheckInstall(c *gin.Context) { language := ctx.AcceptLanguage(c) var body model.NeSoftware err := c.ShouldBindBodyWith(&body, binding.JSON) - if err != nil { + if err != nil || body.HostId == "" { c.JSON(400, result.CodeMsg(400, i18n.TKey(language, "app.common.err400"))) return } - // 检查是否存在 - neSoftwares := s.neSoftwareService.SelectList(body) - if len(neSoftwares) == 0 { - // 没有可访问网元版本数据! - c.JSON(200, result.ErrMsg(i18n.TKey(language, "neSoftware.noData"))) - return + // 检查是否存在软件包记录 + neSoftwares := s.neSoftwareService.SelectList(model.NeSoftware{ + NeType: body.NeType, + Name: body.Name, + Version: body.Version, + }) + if len(neSoftwares) <= 0 { + body.CreateBy = ctx.LoginUserToUserName(c) + body.ID = s.neSoftwareService.Insert(body) + } else { + neSoftware := neSoftwares[0] + neSoftware.Path = body.Path + neSoftware.Description = body.Description + neSoftware.UpdateBy = ctx.LoginUserToUserName(c) + s.neSoftwareService.Update(neSoftware) } - neSoftware := neSoftwares[0] - // 进行安装 - output, err := s.neSoftwareService.Install(neSoftware) + // 进行安装检查 + cmdStrArr, err := s.neSoftwareService.UploadToNeHost(body) if err != nil { - c.JSON(200, result.OkData(output)) + c.JSON(200, result.ErrMsg(err.Error())) return } - c.JSON(200, result.Err(nil)) + c.JSON(200, result.OkData(cmdStrArr)) } diff --git a/src/modules/network_element/model/ne_software.go b/src/modules/network_element/model/ne_software.go index afb6dff6..d0166163 100644 --- a/src/modules/network_element/model/ne_software.go +++ b/src/modules/network_element/model/ne_software.go @@ -1,18 +1,21 @@ package model -import "time" - // NeSoftware 网元软件包 ne_software type NeSoftware struct { - ID string `json:"id" gorm:"id"` - NeType string `json:"neType" gorm:"ne_type" binding:"required"` // 网元类型 - FileName string `json:"fileName" gorm:"file_name" binding:"required"` // 包名称 - Path string `json:"path" gorm:"path"` // 包路径 - Version string `json:"version" gorm:"version" binding:"required"` // 包版本 - Md5Sum string `json:"md5Sum" gorm:"md5_sum"` // --无使用 md5签名 - Status string `json:"status" gorm:"status"` // --无使用 - Comment string `json:"comment" gorm:"comment"` // 包说明 - UpdateTime time.Time `json:"updateTime" gorm:"update_time"` // 上传时间 + ID string `json:"id" gorm:"id"` + NeType string `json:"neType" gorm:"ne_type" binding:"required"` // 网元类型 + Name string `json:"name" gorm:"name" binding:"required"` // 包名称 + Path string `json:"path" gorm:"path"` // 包路径 + Version string `json:"version" gorm:"version" binding:"required"` // 包版本 + Description string `json:"description" gorm:"description"` // 包说明 + CreateBy string `json:"createBy" gorm:"column:create_by"` // 创建者 + CreateTime int64 `json:"createTime" gorm:"column:create_time"` // 创建时间 + UpdateBy string `json:"updateBy" gorm:"column:update_by"` // 更新者 + UpdateTime int64 `json:"updateTime" gorm:"column:update_time"` // 更新时间 + + // ====== 非数据库字段属性 ====== + + HostId string `json:"hostId,omitempty" gorm:"-"` // 已记录的主机ID } // TableName 表名称 diff --git a/src/modules/network_element/repository/ne_software.go b/src/modules/network_element/repository/ne_software.go index 98947d20..f6d0f23e 100644 --- a/src/modules/network_element/repository/ne_software.go +++ b/src/modules/network_element/repository/ne_software.go @@ -22,6 +22,6 @@ type INeSoftware interface { // DeleteByIds 批量删除信息 DeleteByIds(ids []string) int64 - // CheckUniqueTypeAndFileNameAndVersion 校验网元类型和文件名版本是否唯一 - CheckUniqueTypeAndFileNameAndVersion(neSoftware model.NeSoftware) string + // CheckUniqueTypeAndNameAndVersion 校验网元类型和文件名版本是否唯一 + CheckUniqueTypeAndNameAndVersion(neSoftware model.NeSoftware) string } diff --git a/src/modules/network_element/repository/ne_software.impl.go b/src/modules/network_element/repository/ne_software.impl.go index 3116b52e..133dd420 100644 --- a/src/modules/network_element/repository/ne_software.impl.go +++ b/src/modules/network_element/repository/ne_software.impl.go @@ -15,18 +15,19 @@ import ( // 实例化数据层 NewNeSoftware 结构体 var NewNeSoftwareImpl = &NeSoftwareImpl{ selectSql: `select - id, ne_type, file_name, path, version, md5_sum, status, comment, update_time + id, ne_type, name, path, version, description, create_by, create_time, update_by, update_time from ne_software`, resultMap: map[string]string{ "id": "ID", "ne_type": "NeType", - "file_name": "FileName", + "name": "Name", "path": "Path", "version": "Version", - "md5_sum": "Md5Sum", - "status": "Status", - "comment": "Comment", + "description": "Description", + "create_by": "CreateBy", + "create_time": "CreateTime", + "update_by": "UpdateBy", "update_time": "UpdateTime", }, } @@ -63,8 +64,8 @@ func (r *NeSoftwareImpl) SelectPage(query map[string]any) map[string]any { conditions = append(conditions, "ne_type = ?") params = append(params, strings.Trim(v.(string), " ")) } - if v, ok := query["fileName"]; ok && v != "" { - conditions = append(conditions, "file_name like concat(?, '%')") + if v, ok := query["name"]; ok && v != "" { + conditions = append(conditions, "name like concat(?, '%')") params = append(params, strings.Trim(v.(string), " ")) } if v, ok := query["version"]; ok && v != "" { @@ -133,9 +134,9 @@ func (r *NeSoftwareImpl) SelectList(neSoftware model.NeSoftware) []model.NeSoftw conditions = append(conditions, "version = ?") params = append(params, neSoftware.Version) } - if neSoftware.FileName != "" { - conditions = append(conditions, "file_name like concat(?, '%')") - params = append(params, neSoftware.FileName) + if neSoftware.Name != "" { + conditions = append(conditions, "name like concat(?, '%')") + params = append(params, neSoftware.Name) } // 构建查询条件语句 @@ -169,8 +170,8 @@ func (r *NeSoftwareImpl) SelectByIds(cmdIds []string) []model.NeSoftware { return r.convertResultRows(results) } -// CheckUniqueTypeAndFileNameAndVersion 校验网元类型和文件名版本是否唯一 -func (r *NeSoftwareImpl) CheckUniqueTypeAndFileNameAndVersion(neSoftware model.NeSoftware) string { +// CheckUniqueTypeAndNameAndVersion 校验网元类型和文件名版本是否唯一 +func (r *NeSoftwareImpl) CheckUniqueTypeAndNameAndVersion(neSoftware model.NeSoftware) string { // 查询条件拼接 var conditions []string var params []any @@ -182,9 +183,9 @@ func (r *NeSoftwareImpl) CheckUniqueTypeAndFileNameAndVersion(neSoftware model.N conditions = append(conditions, "version = ?") params = append(params, neSoftware.Version) } - if neSoftware.FileName != "" { - conditions = append(conditions, "file_name = ?") - params = append(params, neSoftware.FileName) + if neSoftware.Name != "" { + conditions = append(conditions, "name = ?") + params = append(params, neSoftware.Name) } // 构建查询条件语句 @@ -215,8 +216,8 @@ func (r *NeSoftwareImpl) Insert(neSoftware model.NeSoftware) string { if neSoftware.NeType != "" { params["ne_type"] = neSoftware.NeType } - if neSoftware.FileName != "" { - params["file_name"] = neSoftware.FileName + if neSoftware.Name != "" { + params["name"] = neSoftware.Name } if neSoftware.Path != "" { params["path"] = neSoftware.Path @@ -224,19 +225,11 @@ func (r *NeSoftwareImpl) Insert(neSoftware model.NeSoftware) string { if neSoftware.Version != "" { params["version"] = neSoftware.Version } - if neSoftware.Md5Sum != "" { - params["md5_sum"] = neSoftware.Md5Sum + params["description"] = neSoftware.Description + if neSoftware.CreateBy != "" { + params["create_by"] = neSoftware.CreateBy + params["create_time"] = time.Now().UnixMilli() } - if neSoftware.Status != "" { - params["status"] = neSoftware.Status - } - if neSoftware.Comment != "" { - params["comment"] = neSoftware.Comment - } - if neSoftware.Status != "" { - params["status"] = neSoftware.Status - } - params["update_time"] = time.Now() // 构建执行语句 keys, placeholder, values := repo.KeyPlaceholderValueByInsert(params) @@ -272,8 +265,8 @@ func (r *NeSoftwareImpl) Update(neSoftware model.NeSoftware) int64 { if neSoftware.NeType != "" { params["ne_type"] = neSoftware.NeType } - if neSoftware.FileName != "" { - params["file_name"] = neSoftware.FileName + if neSoftware.Name != "" { + params["name"] = neSoftware.Name } if neSoftware.Path != "" { params["path"] = neSoftware.Path @@ -281,19 +274,11 @@ func (r *NeSoftwareImpl) Update(neSoftware model.NeSoftware) int64 { if neSoftware.Version != "" { params["version"] = neSoftware.Version } - if neSoftware.Md5Sum != "" { - params["md5_sum"] = neSoftware.Md5Sum + params["description"] = neSoftware.Description + if neSoftware.UpdateBy != "" { + params["update_by"] = neSoftware.UpdateBy + params["update_time"] = time.Now().UnixMilli() } - if neSoftware.Status != "" { - params["status"] = neSoftware.Status - } - if neSoftware.Comment != "" { - params["comment"] = neSoftware.Comment - } - if neSoftware.Status != "" { - params["status"] = neSoftware.Status - } - params["update_time"] = time.Now() // 构建执行语句 keys, values := repo.KeyValueByUpdate(params) diff --git a/src/modules/network_element/service/ne_software.go b/src/modules/network_element/service/ne_software.go index c4936ac2..5f5e10f2 100644 --- a/src/modules/network_element/service/ne_software.go +++ b/src/modules/network_element/service/ne_software.go @@ -22,12 +22,10 @@ type INeSoftware interface { // DeleteByIds 批量删除信息 DeleteByIds(ids []string) (int64, error) - // SelectByVersionAndPath 通过文件版本和路径查询 - SelectByVersionAndPath(version, path string) model.NeSoftware + // CheckUniqueTypeAndNameAndVersion 校验网元类型和文件名版本是否唯一 + CheckUniqueTypeAndNameAndVersion(neType, name, version, id string) bool - // CheckUniqueTypeAndFileNameAndVersion 校验网元类型和文件名版本是否唯一 - CheckUniqueTypeAndFileNameAndVersion(neType, fileName, version, id string) bool - - // Install 安装软件包 - Install(neSoftware model.NeSoftware) (string, error) + // UploadToNeHost 安装包上传到网元主机 + // 返回执行命令步骤 + UploadToNeHost(neSoftware model.NeSoftware) ([]string, error) } diff --git a/src/modules/network_element/service/ne_software.impl.go b/src/modules/network_element/service/ne_software.impl.go index 9978f4bf..77783a33 100644 --- a/src/modules/network_element/service/ne_software.impl.go +++ b/src/modules/network_element/service/ne_software.impl.go @@ -2,7 +2,12 @@ package service import ( "fmt" + "os" + "path/filepath" + "strings" + "be.ems/src/framework/utils/file" + "be.ems/src/framework/utils/ssh" "be.ems/src/modules/network_element/model" "be.ems/src/modules/network_element/repository" ) @@ -66,24 +71,12 @@ func (r *NeSoftwareImpl) DeleteByIds(ids []string) (int64, error) { return 0, fmt.Errorf("delete fail") } -// SelectByVersionAndPath 通过文件版本和路径查询 -func (r *NeSoftwareImpl) SelectByVersionAndPath(version, path string) model.NeSoftware { - neSoftwares := r.neSoftwareRepository.SelectList(model.NeSoftware{ +// CheckUniqueTypeAndNameAndVersion 校验网元类型和文件名版本是否唯一 +func (r *NeSoftwareImpl) CheckUniqueTypeAndNameAndVersion(neType, name, version, id string) bool { + uniqueId := r.neSoftwareRepository.CheckUniqueTypeAndNameAndVersion(model.NeSoftware{ + NeType: neType, + Name: name, Version: version, - Path: path, - }) - if len(neSoftwares) > 0 { - return neSoftwares[0] - } - return model.NeSoftware{} -} - -// CheckUniqueTypeAndFileNameAndVersion 校验网元类型和文件名版本是否唯一 -func (r *NeSoftwareImpl) CheckUniqueTypeAndFileNameAndVersion(neType, fileName, version, id string) bool { - uniqueId := r.neSoftwareRepository.CheckUniqueTypeAndFileNameAndVersion(model.NeSoftware{ - NeType: neType, - FileName: fileName, - Version: version, }) if uniqueId == id { return true @@ -91,7 +84,54 @@ func (r *NeSoftwareImpl) CheckUniqueTypeAndFileNameAndVersion(neType, fileName, return uniqueId == "" } -// Install 安装软件包 -func (r *NeSoftwareImpl) Install(neSoftware model.NeSoftware) (string, error) { - return "", nil +// UploadToNeHost 安装包上传到网元主机 +// 返回执行命令步骤 +func (r *NeSoftwareImpl) UploadToNeHost(neSoftware model.NeSoftware) ([]string, error) { + cmdStrArr := []string{} + // 检查文件是否存在 + filePath := file.ParseUploadFilePath(neSoftware.Path) + if _, err := os.Stat(filePath); err != nil { + return cmdStrArr, fmt.Errorf("file read failure") + } + fileName := filepath.Base(neSoftware.Path) + if strings.Contains(fileName, "*") { + fileName = strings.ReplaceAll(fileName, "*", "_") + } + nePath := "/tmp" + neFilePath := fmt.Sprintf("%s/%s", nePath, fileName) + + // 检查网元主机 + neHostInfo := NewNeHostImpl.SelectById(neSoftware.HostId) + if neHostInfo.HostType != "ssh" || neHostInfo.HostID != neSoftware.HostId { + return cmdStrArr, fmt.Errorf("no found host info") + } + + // 上传软件包到 /tmp + if err := ssh.FileSCPLocalToNe(neHostInfo.Addr, filePath, neFilePath); err != nil { + return cmdStrArr, fmt.Errorf("error uploading package") + } + + // 安装软件包 + cmdStrArr = append(cmdStrArr, fmt.Sprintf("sudo dpkg -i %s", neFilePath)) + + if neSoftware.NeType == "IMS" { + // 公网 PLMN地址 + cmdStrArr = append(cmdStrArr, "sudo /usr/local/etc/ims/default/tools/modipplmn.sh {PUBIP} {MCC} {MNC}") + // 内网 服务地址 + cmdStrArr = append(cmdStrArr, "sudo /usr/local/etc/ims/default/tools/modintraip.sh {PRIIP}") + // 10s后停止服务 + cmdStrArr = append(cmdStrArr, "sudo ims-start") + cmdStrArr = append(cmdStrArr, `nohup sh -c "sleep 10s && sudo ims-stop" > /dev/null 2>&1 &`) + } else { + // 10s后停止服务 + cmdStrArr = append(cmdStrArr, fmt.Sprintf("sudo service %s restart", strings.ToLower(neSoftware.NeType))) + cmdStrArr = append(cmdStrArr, fmt.Sprintf(`nohup sh -c "sleep 10s && sudo service %s stop" > /dev/null 2>&1 &`, strings.ToLower(neSoftware.NeType))) + } + + // 删除软件包 + cmdStrArr = append(cmdStrArr, fmt.Sprintf("sudo rm %s", neFilePath)) + + // 结束 + cmdStrArr = append(cmdStrArr, fmt.Sprintf("echo '%s software install successful!'", neSoftware.NeType)) + return cmdStrArr, nil } 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 07/12] =?UTF-8?q?feat:=20=E7=BD=91=E5=85=83=E4=BF=A1?= =?UTF-8?q?=E6=81=AF=E9=85=8D=E7=BD=AE=E8=AF=BB=E5=86=99=EF=BC=8Ccmd?= =?UTF-8?q?=E5=91=BD=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 +} From 1d1c58475cde2a5270ea8c32aa962e3128a49993 Mon Sep 17 00:00:00 2001 From: TsMask <340112800@qq.com> Date: Mon, 1 Apr 2024 17:01:35 +0800 Subject: [PATCH 08/12] =?UTF-8?q?fix:=20=E7=BD=91=E5=85=83=E7=89=88?= =?UTF-8?q?=E6=9C=AC=E4=BF=A1=E6=81=AF=E5=8F=98=E6=9B=B4=E5=B1=9E=E6=80=A7?= =?UTF-8?q?=E6=B3=A8=E9=87=8A=E7=AD=89?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../network_element/controller/ne_version.go | 2 + .../network_element/model/ne_version.go | 27 +++++----- .../repository/ne_version.impl.go | 53 +++++++++++-------- 3 files changed, 47 insertions(+), 35 deletions(-) diff --git a/src/modules/network_element/controller/ne_version.go b/src/modules/network_element/controller/ne_version.go index 8e4710f7..5fab991e 100644 --- a/src/modules/network_element/controller/ne_version.go +++ b/src/modules/network_element/controller/ne_version.go @@ -78,6 +78,7 @@ func (s *NeVersionController) Add(c *gin.Context) { return } + body.CreateBy = ctx.LoginUserToUserName(c) insertId := s.neVersionService.Insert(body) if insertId != "" { c.JSON(200, result.Ok(nil)) @@ -115,6 +116,7 @@ func (s *NeVersionController) Edit(c *gin.Context) { return } + body.UpdateBy = ctx.LoginUserToUserName(c) rows := s.neVersionService.Update(body) if rows > 0 { c.JSON(200, result.Ok(nil)) diff --git a/src/modules/network_element/model/ne_version.go b/src/modules/network_element/model/ne_version.go index 11a00e67..50f21cc2 100644 --- a/src/modules/network_element/model/ne_version.go +++ b/src/modules/network_element/model/ne_version.go @@ -1,20 +1,21 @@ package model -import "time" - // NeVersion 网元版本信息 ne_version type NeVersion struct { - ID string `json:"id" gorm:"id"` - NeType string `json:"neType" gorm:"ne_type" binding:"required"` // 网元类型 - NeId string `json:"neId" gorm:"ne_id" binding:"required"` // 网元ID - Version string `json:"version" gorm:"version"` // 当前版本 - FilePath string `json:"filePath" gorm:"file_path"` // 当前软件包 - PreVersion string `json:"preVersion" gorm:"pre_version"` // 上一版本 - PreFile string `json:"preFile" gorm:"pre_file"` // 上一版本软件包 - NewVersion string `json:"newVersion" gorm:"new_version"` // 下一版本 - NewFile string `json:"newFile" gorm:"new_file"` // 下一版本软件包 - Status string `json:"status" gorm:"status" binding:"oneof=Uploaded Inactive Active"` // 当前状态 (Uploaded下一版本上传 Inactive下一版本待激活 Active当前已激活) - UpdateTime time.Time `json:"updateTime" gorm:"update_time"` // 更新时间 + ID string `json:"id" gorm:"id"` + NeType string `json:"neType" gorm:"ne_type" binding:"required"` // 网元类型 + NeId string `json:"neId" gorm:"ne_id" binding:"required"` // 网元ID + Version string `json:"version" gorm:"version" binding:"required"` // 当前版本 + Path string `json:"path" gorm:"path" binding:"required"` // 当前软件包 + PreVersion string `json:"preVersion" gorm:"pre_version"` // 上一版本 + PrePath string `json:"prePath" gorm:"pre_path"` // 上一版本软件包 + NewVersion string `json:"newVersion" gorm:"new_version"` // 下一版本 + NewPath string `json:"newPath" gorm:"new_path"` // 下一版本软件包 + Status string `json:"status" gorm:"status"` // 当前状态 (Uploaded下一版本上传 Inactive下一版本待激活 Active当前已激活) + CreateBy string `json:"createBy" gorm:"column:create_by"` // 创建者 + CreateTime int64 `json:"createTime" gorm:"column:create_time"` // 创建时间 + UpdateBy string `json:"updateBy" gorm:"column:update_by"` // 更新者 + UpdateTime int64 `json:"updateTime" gorm:"column:update_time"` // 更新时间 } // TableName 表名称 diff --git a/src/modules/network_element/repository/ne_version.impl.go b/src/modules/network_element/repository/ne_version.impl.go index 7bfefcd0..7eebfec7 100644 --- a/src/modules/network_element/repository/ne_version.impl.go +++ b/src/modules/network_element/repository/ne_version.impl.go @@ -15,7 +15,7 @@ import ( // 实例化数据层 NewNeVersion 结构体 var NewNeVersionImpl = &NeVersionImpl{ selectSql: `select - id, ne_type, ne_id, version, file_path, pre_version, pre_file, new_version, new_file, status, update_time + id, ne_type, ne_id, version, path, pre_version, pre_path, new_version, new_path, status, create_by, create_time, update_by, update_time from ne_version`, resultMap: map[string]string{ @@ -23,12 +23,15 @@ var NewNeVersionImpl = &NeVersionImpl{ "ne_type": "NeType", "ne_id": "NeId", "version": "Version", - "file_path": "FilePath", + "path": "Path", "pre_version": "PreVersion", - "pre_file": "PreFile", + "pre_path": "PrePath", "new_version": "NewVersion", - "new_file": "NewFile", + "new_path": "NewPath", "status": "Status", + "create_by": "CreateBy", + "create_time": "CreateTime", + "update_by": "UpdateBy", "update_time": "UpdateTime", }, } @@ -139,9 +142,9 @@ func (r *NeVersionImpl) SelectList(neVersion model.NeVersion) []model.NeVersion conditions = append(conditions, "version like concat(?, '%')") params = append(params, neVersion.Version) } - if neVersion.FilePath != "" { - conditions = append(conditions, "file_path like concat(?, '%')") - params = append(params, neVersion.FilePath) + if neVersion.Path != "" { + conditions = append(conditions, "path like concat(?, '%')") + params = append(params, neVersion.Path) } // 构建查询条件语句 @@ -151,7 +154,7 @@ func (r *NeVersionImpl) SelectList(neVersion model.NeVersion) []model.NeVersion } // 查询数据 - querySql := r.selectSql + whereSql + " order by update_time asc " + querySql := r.selectSql + whereSql + " order by id asc " results, err := datasource.RawDB("", querySql, params) if err != nil { logger.Errorf("query err => %v", err) @@ -223,25 +226,28 @@ func (r *NeVersionImpl) Insert(neVersion model.NeVersion) string { if neVersion.Version != "" { params["version"] = neVersion.Version } - if neVersion.FilePath != "" { - params["file_path"] = neVersion.FilePath + if neVersion.Path != "" { + params["path"] = neVersion.Path } if neVersion.PreVersion != "" { params["pre_version"] = neVersion.PreVersion } - if neVersion.PreFile != "" { - params["pre_file"] = neVersion.PreFile + if neVersion.PrePath != "" { + params["pre_path"] = neVersion.PrePath } if neVersion.NewVersion != "" { params["new_version"] = neVersion.NewVersion } - if neVersion.NewFile != "" { - params["new_file"] = neVersion.NewFile + if neVersion.NewPath != "" { + params["new_path"] = neVersion.NewPath } if neVersion.Status != "" { params["status"] = neVersion.Status } - params["update_time"] = time.Now() + if neVersion.CreateBy != "" { + params["create_by"] = neVersion.CreateBy + params["create_time"] = time.Now().UnixMilli() + } // 构建执行语句 keys, placeholder, values := repo.KeyPlaceholderValueByInsert(params) @@ -283,25 +289,28 @@ func (r *NeVersionImpl) Update(neVersion model.NeVersion) int64 { if neVersion.Version != "" { params["version"] = neVersion.Version } - if neVersion.FilePath != "" { - params["file_path"] = neVersion.FilePath + if neVersion.Path != "" { + params["path"] = neVersion.Path } if neVersion.PreVersion != "" { params["pre_version"] = neVersion.PreVersion } - if neVersion.PreFile != "" { - params["pre_file"] = neVersion.PreFile + if neVersion.PrePath != "" { + params["pre_path"] = neVersion.PrePath } if neVersion.NewVersion != "" { params["new_version"] = neVersion.NewVersion } - if neVersion.NewFile != "" { - params["new_file"] = neVersion.NewFile + if neVersion.NewPath != "" { + params["new_path"] = neVersion.NewPath } if neVersion.Status != "" { params["status"] = neVersion.Status } - params["update_time"] = time.Now() + if neVersion.UpdateBy != "" { + params["update_by"] = neVersion.UpdateBy + params["update_time"] = time.Now().UnixMilli() + } // 构建执行语句 keys, values := repo.KeyValueByUpdate(params) From fed2c577004a487f608fd20f3a79923a4c7a246b Mon Sep 17 00:00:00 2001 From: TsMask <340112800@qq.com> Date: Mon, 1 Apr 2024 17:03:22 +0800 Subject: [PATCH 09/12] =?UTF-8?q?fix:=20scp=E7=BD=91=E5=85=83=E6=96=87?= =?UTF-8?q?=E4=BB=B6=E5=A4=8D=E5=88=B6=E5=88=B0=E6=9C=AC=E5=9C=B0=E4=BF=9D?= =?UTF-8?q?=E7=95=99=E5=8E=9F=E5=85=88=E5=90=8C=E5=90=8D=E6=96=87=E4=BB=B6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/framework/utils/ssh/scp.go | 19 +++++++++++++++---- 1 file changed, 15 insertions(+), 4 deletions(-) diff --git a/src/framework/utils/ssh/scp.go b/src/framework/utils/ssh/scp.go index 834ba8ed..8151b3a4 100644 --- a/src/framework/utils/ssh/scp.go +++ b/src/framework/utils/ssh/scp.go @@ -5,6 +5,7 @@ import ( "os" "os/exec" "path/filepath" + "time" "be.ems/src/framework/config" "be.ems/src/framework/logger" @@ -16,9 +17,9 @@ func FileSCPLocalToNe(neIp, localPath, nePath string) error { // scp /path/to/local/file.txt user@remote-server:/path/to/remote/directory/ neDir := fmt.Sprintf("%s@%s:%s", usernameNe, neIp, nePath) cmd := exec.Command("scp", "-r", localPath, neDir) - _, err := cmd.CombinedOutput() + output, err := cmd.CombinedOutput() if err != nil { - logger.Errorf("FileSCPLocalToNe %s", err.Error()) + logger.Errorf("FileSCPLocalToNe %s => %s", output, err.Error()) return err } return nil @@ -31,13 +32,23 @@ func FileSCPNeToLocal(neIp, nePath, localPath string) error { logger.Errorf("FileSCPNeToLocal MkdirAll err %v", err) return err } + // 如果目标文件已经存在,先将目标文件重命名 + if info, err := os.Stat(localPath); err == nil && !info.IsDir() { + ext := filepath.Ext(localPath) + name := localPath[0 : len(localPath)-len(ext)] + newName := fmt.Sprintf("%s-%s%s", name, time.Now().Format("20060102_150405"), ext) + err := os.Rename(localPath, newName) + if err != nil { + return err + } + } usernameNe := config.Get("ne.user").(string) // scp user@remote-server:/path/to/remote/directory/ /path/to/local/file.txt neDir := fmt.Sprintf("%s@%s:%s", usernameNe, neIp, nePath) cmd := exec.Command("scp", "-r", neDir, localPath) - _, err := cmd.CombinedOutput() + output, err := cmd.CombinedOutput() if err != nil { - logger.Errorf("FileSCPNeToLocal %s", err.Error()) + logger.Errorf("FileSCPNeToLocal %s => %s", output, err.Error()) return err } return nil From 6cf2891fed4cb7dae1386d3457ad60492f9b5f1f Mon Sep 17 00:00:00 2001 From: TsMask <340112800@qq.com> Date: Mon, 1 Apr 2024 17:05:25 +0800 Subject: [PATCH 10/12] =?UTF-8?q?feat:=20ssh=E7=94=9F=E6=88=90=E6=9C=AC?= =?UTF-8?q?=E5=9C=B0RSA,=E6=94=AF=E6=8C=81=E6=9C=AC=E5=9C=B0=E7=9B=B4?= =?UTF-8?q?=E8=BF=9E=E6=8E=88=E6=9D=83=E9=85=8D=E7=BD=AE?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/framework/utils/ssh/ssh.go | 64 +++++++++++++++---- .../network_element/controller/ne_host.go | 50 +++++++++++++++ 2 files changed, 102 insertions(+), 12 deletions(-) diff --git a/src/framework/utils/ssh/ssh.go b/src/framework/utils/ssh/ssh.go index fbee80fb..15423619 100644 --- a/src/framework/utils/ssh/ssh.go +++ b/src/framework/utils/ssh/ssh.go @@ -11,6 +11,7 @@ import ( "time" "be.ems/src/framework/logger" + "be.ems/src/framework/utils/cmd" gossh "golang.org/x/crypto/ssh" ) @@ -96,20 +97,11 @@ func (c *ConnSSH) Close() { func (c *ConnSSH) NewClientByLocalPrivate() (*ConnSSH, error) { c.Port = 22 c.AuthMode = "1" - usr, err := user.Current() + privateKey, err := c.CurrentUserRsaKey(false) if err != nil { - logger.Errorf("NewClientByLocal get current user => %s", err.Error()) return nil, err } - - // 读取用户默认的私钥文件 - keyPath := fmt.Sprintf("%s/.ssh/id_rsa", usr.HomeDir) - key, err := os.ReadFile(keyPath) - if err != nil { - logger.Errorf("NewClientByLocal [%s] read private key => %s", usr.Username, err.Error()) - return nil, err - } - c.PrivateKey = string(key) + c.PrivateKey = privateKey return c.NewClient() } @@ -134,7 +126,55 @@ func (c *ConnSSH) RunCMD(cmd string) (string, error) { return c.LastResult, err } -// NewClient 创建SSH客户端会话对象 +// SendToAuthorizedKeys 发送当前用户私钥到远程服务器进行授权密钥 +func (c *ConnSSH) SendToAuthorizedKeys() error { + publicKey, err := c.CurrentUserRsaKey(true) + if err != nil { + return err + } + authorizedKeysEntry := fmt.Sprintln(strings.TrimSpace(publicKey)) + cmdStr := "echo '" + authorizedKeysEntry + "' >> ~/.ssh/authorized_keys" + _, err = c.RunCMD(cmdStr) + if err != nil { + logger.Errorf("SendAuthorizedKeys echo err %s", err.Error()) + return err + } + return nil +} + +// CurrentUserRsaKey 当前用户OMC使用的RSA私钥 +// 默认读取私钥 +// ssh-keygen -t rsa -P "" -f ~/.ssh/id_rsa +// ssh-keygen -y -f ~/.ssh/id_rsa > ~/.ssh/id_rsa.pub +func (c *ConnSSH) CurrentUserRsaKey(publicKey bool) (string, error) { + usr, err := user.Current() + if err != nil { + logger.Errorf("CurrentUserRsaKey get => %s", err.Error()) + return "", err + } + + // 是否存在私钥并创建 + keyPath := fmt.Sprintf("%s/.ssh/id_rsa", usr.HomeDir) + if _, err := os.Stat(keyPath); err != nil { + _, err2 := cmd.ExecWithCheck("ssh-keygen", "-t", "rsa", "-P", "", "-f", keyPath) + if err2 != nil { + logger.Errorf("CurrentUserPrivateKey ssh-keygen [%s] rsa => %s", usr.Username, err2.Error()) + } + } + + // 读取用户默认的文件 + if publicKey { + keyPath = keyPath + ".pub" + } + key, err := os.ReadFile(keyPath) + if err != nil { + logger.Errorf("CurrentUserRsaKey [%s] read => %s", usr.Username, err.Error()) + return "", fmt.Errorf("read file %s fail", keyPath) + } + return string(key), nil +} + +// NewClientSession 创建SSH客户端会话对象 func (c *ConnSSH) NewClientSession(cols, rows int) (*SSHClientSession, error) { sshSession, err := c.Client.NewSession() if err != nil { diff --git a/src/modules/network_element/controller/ne_host.go b/src/modules/network_element/controller/ne_host.go index 7bf39555..7baca4bd 100644 --- a/src/modules/network_element/controller/ne_host.go +++ b/src/modules/network_element/controller/ne_host.go @@ -354,3 +354,53 @@ func (s *NeHostController) CheckBySSH(c *gin.Context) { c.JSON(200, result.OkData(data)) } + +// 网元主机SSH方式授权免密发送 +// +// POST /authorizedBySSH +func (s *NeHostController) AuthorizedBySSH(c *gin.Context) { + language := ctx.AcceptLanguage(c) + var body model.NeHost + err := c.ShouldBindBodyWith(&body, binding.JSON) + if err != nil { + c.JSON(400, result.CodeMsg(400, i18n.TKey(language, "app.common.err400"))) + return + } + + // 本地免密创建链接直连 + sshLink := false + lcoalConnSSH := ssh.ConnSSH{ + User: body.User, + Addr: body.Addr, + Port: body.Port, + } + lcoalClient, err := lcoalConnSSH.NewClientByLocalPrivate() + if err == nil { + sshLink = true + } + defer lcoalClient.Close() + if sshLink { + // 连接主机成功,无需重复免密授权认证 + c.JSON(200, result.OkMsg(i18n.TKey(language, "neHost.okBySSHLink"))) + return + } + + // 创建链接SSH客户端 + var connSSH ssh.ConnSSH + body.CopyTo(&connSSH) + client, err := connSSH.NewClient() + if err != nil { + // 连接主机失败,请检查连接参数后重试 + c.JSON(200, result.ErrMsg(i18n.TKey(language, "neHost.errByHostInfo"))) + return + } + defer client.Close() + + // 发送密钥 + err = client.SendToAuthorizedKeys() + if err != nil { + c.JSON(200, result.ErrMsg(err.Error())) + return + } + c.JSON(200, result.Ok(nil)) +} From 851f50a3e6c0865c3fd2e24861ec9e3d06abf0f0 Mon Sep 17 00:00:00 2001 From: TsMask <340112800@qq.com> Date: Mon, 1 Apr 2024 17:05:49 +0800 Subject: [PATCH 11/12] =?UTF-8?q?style:=20udm=E7=BB=93=E6=9E=84=E4=BD=93?= =?UTF-8?q?=E6=B3=A8=E9=87=8A?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/modules/network_element/model/udm_auth.go | 16 ++++++++-------- src/modules/network_element/model/udm_sub.go | 2 ++ 2 files changed, 10 insertions(+), 8 deletions(-) diff --git a/src/modules/network_element/model/udm_auth.go b/src/modules/network_element/model/udm_auth.go index 2fe70e2c..9069858d 100644 --- a/src/modules/network_element/model/udm_auth.go +++ b/src/modules/network_element/model/udm_auth.go @@ -2,14 +2,14 @@ package model // UDMAuth UDM鉴权用户对象 u_auth_user type UDMAuth struct { - ID string `json:"id" gorm:"column:id;primaryKey;autoIncrement"` - Imsi string `json:"imsi" gorm:"column:imsi"` // SIM卡号 - Amf string `json:"amf" gorm:"column:amf"` // ANF - Status string `json:"status" gorm:"column:status"` // 状态 - Ki string `json:"ki" gorm:"column:ki"` // ki - AlgoIndex string `json:"algoIndex" gorm:"column:algo_index"` // AlgoIndex - Opc string `json:"opc" gorm:"column:opc"` - NeID string `json:"neId" gorm:"column:ne_id"` // UDM网元标识-子系统 + ID string `json:"id" gorm:"column:id;primaryKey;autoIncrement"` // 默认ID + Imsi string `json:"imsi" gorm:"column:imsi"` // SIM卡号 + Amf string `json:"amf" gorm:"column:amf"` // ANF + Status string `json:"status" gorm:"column:status"` // 状态 + Ki string `json:"ki" gorm:"column:ki"` // ki + AlgoIndex string `json:"algoIndex" gorm:"column:algo_index"` // AlgoIndex + Opc string `json:"opc" gorm:"column:opc"` // opc + NeID string `json:"neId" gorm:"column:ne_id"` // UDM网元标识-子系统 } func (UDMAuth) TableName() string { diff --git a/src/modules/network_element/model/udm_sub.go b/src/modules/network_element/model/udm_sub.go index 1aa345c1..55a56ef1 100644 --- a/src/modules/network_element/model/udm_sub.go +++ b/src/modules/network_element/model/udm_sub.go @@ -25,6 +25,8 @@ type UDMSub struct { ApnContext string `json:"apnContext" gorm:"column:apn_context"` StaticIp string `json:"staticIp" gorm:"column:static_ip"` + // ====== 非数据库字段属性 ====== + SubNum string `json:"subNum,omitempty" gorm:"-"` // 批量数 } From d3836dfa126914756bbf4dee04b03a74ada894bb Mon Sep 17 00:00:00 2001 From: TsMask <340112800@qq.com> Date: Mon, 1 Apr 2024 17:07:02 +0800 Subject: [PATCH 12/12] =?UTF-8?q?feat:=20=E7=BD=91=E5=85=83=E4=BF=A1?= =?UTF-8?q?=E6=81=AF=E5=AE=89=E8=A3=85=E7=9B=B8=E5=85=B3=E6=8E=A5=E5=8F=A3?= =?UTF-8?q?=E6=B3=A8=E5=86=8C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../network_element/network_element.go | 76 ++++++++++++++++--- 1 file changed, 65 insertions(+), 11 deletions(-) diff --git a/src/modules/network_element/network_element.go b/src/modules/network_element/network_element.go index e767da0f..e8776157 100644 --- a/src/modules/network_element/network_element.go +++ b/src/modules/network_element/network_element.go @@ -41,10 +41,26 @@ func Setup(router *gin.Engine) { // 网元信息 neInfoGroup := neGroup.Group("/info") { + neInfoGroup.GET("/state", + middleware.PreAuthorize(nil), + controller.NewNeInfo.State, + ) + neInfoGroup.GET("/byTypeAndID", + middleware.PreAuthorize(nil), + controller.NewNeInfo.NeTypeAndID, + ) neInfoGroup.GET("/listAll", middleware.PreAuthorize(nil), controller.NewNeInfo.ListAll, ) + neInfoGroup.GET("/configFile", + middleware.PreAuthorize(nil), + controller.NewNeInfo.ConfigFileRead, + ) + neInfoGroup.PUT("/configFile", + middleware.PreAuthorize(nil), + controller.NewNeInfo.ConfigFileWrite, + ) neInfoGroup.GET("/list", middleware.PreAuthorize(nil), controller.NewNeInfo.List, @@ -68,14 +84,6 @@ func Setup(router *gin.Engine) { collectlogs.OperateLog(collectlogs.OptionNew("log.operate.title.neInfo", collectlogs.BUSINESS_TYPE_DELETE)), controller.NewNeInfo.Remove, ) - neInfoGroup.GET("/state", - middleware.PreAuthorize(nil), - controller.NewNeInfo.State, - ) - neInfoGroup.GET("/byTypeAndID", - middleware.PreAuthorize(nil), - controller.NewNeInfo.NeTypeAndID, - ) } // 网元主机 @@ -119,6 +127,11 @@ func Setup(router *gin.Engine) { collectlogs.OperateLog(collectlogs.OptionNew("log.operate.title.neHost", collectlogs.BUSINESS_TYPE_OTHER)), controller.NewNeHost.CheckBySSH, ) + neHostGroup.POST("/authorizedBySSH", + middleware.PreAuthorize(nil), + collectlogs.OperateLog(collectlogs.OptionNew("log.operate.title.neHost", collectlogs.BUSINESS_TYPE_OTHER)), + controller.NewNeHost.AuthorizedBySSH, + ) } // 网元主机命令 @@ -203,10 +216,51 @@ func Setup(router *gin.Engine) { collectlogs.OperateLog(collectlogs.OptionNew("log.operate.title.neSoftware", collectlogs.BUSINESS_TYPE_DELETE)), controller.NewNeSoftware.Remove, ) - neSoftwareGroup.POST("/install", + neSoftwareGroup.POST("/checkInstall", middleware.PreAuthorize(nil), - collectlogs.OperateLog(collectlogs.OptionNew("log.operate.title.neSoftware", collectlogs.BUSINESS_TYPE_DELETE)), - controller.NewNeSoftware.Install, + collectlogs.OperateLog(collectlogs.OptionNew("log.operate.title.neSoftware", collectlogs.BUSINESS_TYPE_OTHER)), + controller.NewNeSoftware.CheckInstall, + ) + } + + // 网元授权激活信息 + neLicenseGroup := neGroup.Group("/license") + { + neLicenseGroup.GET("/list", + middleware.PreAuthorize(nil), + controller.NewNeLicense.List, + ) + neLicenseGroup.GET("/:licenseId", + middleware.PreAuthorize(nil), + controller.NewNeLicense.Info, + ) + neLicenseGroup.POST("", + middleware.PreAuthorize(nil), + collectlogs.OperateLog(collectlogs.OptionNew("log.operate.title.neLicense", collectlogs.BUSINESS_TYPE_INSERT)), + controller.NewNeLicense.Add, + ) + neLicenseGroup.PUT("", + middleware.PreAuthorize(nil), + collectlogs.OperateLog(collectlogs.OptionNew("log.operate.title.neLicense", collectlogs.BUSINESS_TYPE_UPDATE)), + controller.NewNeLicense.Edit, + ) + neLicenseGroup.DELETE("/:licenseIds", + middleware.PreAuthorize(nil), + collectlogs.OperateLog(collectlogs.OptionNew("log.operate.title.neLicense", collectlogs.BUSINESS_TYPE_DELETE)), + controller.NewNeLicense.Remove, + ) + neLicenseGroup.GET("/code", + middleware.PreAuthorize(nil), + controller.NewNeLicense.Code, + ) + neLicenseGroup.POST("/change", + middleware.PreAuthorize(nil), + collectlogs.OperateLog(collectlogs.OptionNew("log.operate.title.neLicense", collectlogs.BUSINESS_TYPE_OTHER)), + controller.NewNeLicense.Change, + ) + neLicenseGroup.GET("/state", + middleware.PreAuthorize(nil), + controller.NewNeLicense.State, ) }