feat: 网元版本操作升级和回退
This commit is contained in:
@@ -150,3 +150,35 @@ func (s *NeVersionController) Remove(c *gin.Context) {
|
|||||||
msg := i18n.TTemplate(language, "app.common.deleteSuccess", map[string]any{"num": rows})
|
msg := i18n.TTemplate(language, "app.common.deleteSuccess", map[string]any{"num": rows})
|
||||||
c.JSON(200, result.OkMsg(msg))
|
c.JSON(200, result.OkMsg(msg))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 网元版本操作
|
||||||
|
//
|
||||||
|
// POST /operate
|
||||||
|
func (s *NeVersionController) Operate(c *gin.Context) {
|
||||||
|
language := ctx.AcceptLanguage(c)
|
||||||
|
var body struct {
|
||||||
|
Action string `json:"action" binding:"required,oneof=upgrade rollback"` // 操作行为
|
||||||
|
NeType string `json:"neType" gorm:"ne_type" binding:"required"` // 网元类型
|
||||||
|
NeId string `json:"neId" gorm:"ne_id" binding:"required"` // 网元ID
|
||||||
|
Preinput map[string]string `json:"preinput" ` // 预先输入参数
|
||||||
|
}
|
||||||
|
if err := c.ShouldBindBodyWith(&body, binding.JSON); err != nil {
|
||||||
|
c.JSON(400, result.CodeMsg(400, i18n.TKey(language, "app.common.err400")))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
neVersion := s.neVersionService.SelectByNeTypeAndNeID(body.NeType, body.NeId)
|
||||||
|
if neVersion.NeId != body.NeId {
|
||||||
|
// 没有可访问网元版本数据!
|
||||||
|
c.JSON(200, result.ErrMsg(i18n.TKey(language, "neVersion.noData")))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// 进行相关命令操作
|
||||||
|
output, err := s.neVersionService.Operate(body.Action, neVersion, body.Preinput)
|
||||||
|
if err != nil {
|
||||||
|
c.JSON(200, result.ErrMsg(i18n.TKey(language, err.Error())))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
c.JSON(200, result.OkData(output))
|
||||||
|
}
|
||||||
|
|||||||
@@ -188,6 +188,11 @@ func Setup(router *gin.Engine) {
|
|||||||
collectlogs.OperateLog(collectlogs.OptionNew("log.operate.title.neVersion", collectlogs.BUSINESS_TYPE_DELETE)),
|
collectlogs.OperateLog(collectlogs.OptionNew("log.operate.title.neVersion", collectlogs.BUSINESS_TYPE_DELETE)),
|
||||||
controller.NewNeVersion.Remove,
|
controller.NewNeVersion.Remove,
|
||||||
)
|
)
|
||||||
|
neVersionGroup.POST("/operate",
|
||||||
|
middleware.PreAuthorize(nil),
|
||||||
|
collectlogs.OperateLog(collectlogs.OptionNew("log.operate.title.neVersion", collectlogs.BUSINESS_TYPE_OTHER)),
|
||||||
|
controller.NewNeVersion.Operate,
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
// 网元软件包信息
|
// 网元软件包信息
|
||||||
|
|||||||
@@ -15,17 +15,20 @@ import (
|
|||||||
// 实例化数据层 NewNeVersion 结构体
|
// 实例化数据层 NewNeVersion 结构体
|
||||||
var NewNeVersionImpl = &NeVersionImpl{
|
var NewNeVersionImpl = &NeVersionImpl{
|
||||||
selectSql: `select
|
selectSql: `select
|
||||||
id, ne_type, ne_id, version, path, pre_version, pre_path, new_version, new_path, status, create_by, create_time, update_by, update_time
|
id, ne_type, ne_id, name, version, path, pre_name, pre_version, pre_path, new_name, new_version, new_path, status, create_by, create_time, update_by, update_time
|
||||||
from ne_version`,
|
from ne_version`,
|
||||||
|
|
||||||
resultMap: map[string]string{
|
resultMap: map[string]string{
|
||||||
"id": "ID",
|
"id": "ID",
|
||||||
"ne_type": "NeType",
|
"ne_type": "NeType",
|
||||||
"ne_id": "NeId",
|
"ne_id": "NeId",
|
||||||
|
"name": "name",
|
||||||
"version": "Version",
|
"version": "Version",
|
||||||
"path": "Path",
|
"path": "Path",
|
||||||
|
"pre_name": "preName",
|
||||||
"pre_version": "PreVersion",
|
"pre_version": "PreVersion",
|
||||||
"pre_path": "PrePath",
|
"pre_path": "PrePath",
|
||||||
|
"new_name": "NewName",
|
||||||
"new_version": "NewVersion",
|
"new_version": "NewVersion",
|
||||||
"new_path": "NewPath",
|
"new_path": "NewPath",
|
||||||
"status": "Status",
|
"status": "Status",
|
||||||
@@ -223,18 +226,27 @@ func (r *NeVersionImpl) Insert(neVersion model.NeVersion) string {
|
|||||||
if neVersion.NeId != "" {
|
if neVersion.NeId != "" {
|
||||||
params["ne_id"] = neVersion.NeId
|
params["ne_id"] = neVersion.NeId
|
||||||
}
|
}
|
||||||
|
if neVersion.Name != "" {
|
||||||
|
params["name"] = neVersion.Name
|
||||||
|
}
|
||||||
if neVersion.Version != "" {
|
if neVersion.Version != "" {
|
||||||
params["version"] = neVersion.Version
|
params["version"] = neVersion.Version
|
||||||
}
|
}
|
||||||
if neVersion.Path != "" {
|
if neVersion.Path != "" {
|
||||||
params["path"] = neVersion.Path
|
params["path"] = neVersion.Path
|
||||||
}
|
}
|
||||||
|
if neVersion.PreName != "" {
|
||||||
|
params["pre_name"] = neVersion.PreName
|
||||||
|
}
|
||||||
if neVersion.PreVersion != "" {
|
if neVersion.PreVersion != "" {
|
||||||
params["pre_version"] = neVersion.PreVersion
|
params["pre_version"] = neVersion.PreVersion
|
||||||
}
|
}
|
||||||
if neVersion.PrePath != "" {
|
if neVersion.PrePath != "" {
|
||||||
params["pre_path"] = neVersion.PrePath
|
params["pre_path"] = neVersion.PrePath
|
||||||
}
|
}
|
||||||
|
if neVersion.NewName != "" {
|
||||||
|
params["new_name"] = neVersion.NewName
|
||||||
|
}
|
||||||
if neVersion.NewVersion != "" {
|
if neVersion.NewVersion != "" {
|
||||||
params["new_version"] = neVersion.NewVersion
|
params["new_version"] = neVersion.NewVersion
|
||||||
}
|
}
|
||||||
@@ -286,18 +298,27 @@ func (r *NeVersionImpl) Update(neVersion model.NeVersion) int64 {
|
|||||||
if neVersion.NeId != "" {
|
if neVersion.NeId != "" {
|
||||||
params["ne_id"] = neVersion.NeId
|
params["ne_id"] = neVersion.NeId
|
||||||
}
|
}
|
||||||
|
if neVersion.Name != "" {
|
||||||
|
params["name"] = neVersion.Name
|
||||||
|
}
|
||||||
if neVersion.Version != "" {
|
if neVersion.Version != "" {
|
||||||
params["version"] = neVersion.Version
|
params["version"] = neVersion.Version
|
||||||
}
|
}
|
||||||
if neVersion.Path != "" {
|
if neVersion.Path != "" {
|
||||||
params["path"] = neVersion.Path
|
params["path"] = neVersion.Path
|
||||||
}
|
}
|
||||||
|
if neVersion.PreName != "" {
|
||||||
|
params["pre_name"] = neVersion.PreName
|
||||||
|
}
|
||||||
if neVersion.PreVersion != "" {
|
if neVersion.PreVersion != "" {
|
||||||
params["pre_version"] = neVersion.PreVersion
|
params["pre_version"] = neVersion.PreVersion
|
||||||
}
|
}
|
||||||
if neVersion.PrePath != "" {
|
if neVersion.PrePath != "" {
|
||||||
params["pre_path"] = neVersion.PrePath
|
params["pre_path"] = neVersion.PrePath
|
||||||
}
|
}
|
||||||
|
if neVersion.NewName != "" {
|
||||||
|
params["new_name"] = neVersion.NewName
|
||||||
|
}
|
||||||
if neVersion.NewVersion != "" {
|
if neVersion.NewVersion != "" {
|
||||||
params["new_version"] = neVersion.NewVersion
|
params["new_version"] = neVersion.NewVersion
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -27,4 +27,9 @@ type INeVersion interface {
|
|||||||
|
|
||||||
// CheckUniqueTypeAndID 校验网元类型和网元ID是否唯一
|
// CheckUniqueTypeAndID 校验网元类型和网元ID是否唯一
|
||||||
CheckUniqueTypeAndID(neType, neId, id string) bool
|
CheckUniqueTypeAndID(neType, neId, id string) bool
|
||||||
|
|
||||||
|
// Operate 操作版本上传到网元主机执行命令
|
||||||
|
//
|
||||||
|
// action 安装行为:upgrade rollback
|
||||||
|
Operate(action string, neVersion model.NeVersion, preinput map[string]string) (string, error)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,7 +2,12 @@ package service
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"os"
|
||||||
|
"path/filepath"
|
||||||
|
"strings"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"be.ems/src/framework/utils/file"
|
||||||
"be.ems/src/modules/network_element/model"
|
"be.ems/src/modules/network_element/model"
|
||||||
"be.ems/src/modules/network_element/repository"
|
"be.ems/src/modules/network_element/repository"
|
||||||
)
|
)
|
||||||
@@ -89,3 +94,146 @@ func (r *NeVersionImpl) CheckUniqueTypeAndID(neType, neId, id string) bool {
|
|||||||
}
|
}
|
||||||
return uniqueId == ""
|
return uniqueId == ""
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Operate 操作版本上传到网元主机执行命令
|
||||||
|
//
|
||||||
|
// action 安装行为:upgrade rollback
|
||||||
|
func (r *NeVersionImpl) Operate(action string, neVersion model.NeVersion, preinput map[string]string) (string, error) {
|
||||||
|
softwarePath := neVersion.Path
|
||||||
|
if action == "upgrade" {
|
||||||
|
softwarePath = neVersion.NewPath
|
||||||
|
}
|
||||||
|
if action == "rollback" {
|
||||||
|
softwarePath = neVersion.PrePath
|
||||||
|
}
|
||||||
|
// 检查文件是否存在
|
||||||
|
localFilePath := file.ParseUploadFilePath(softwarePath)
|
||||||
|
if _, err := os.Stat(localFilePath); err != nil {
|
||||||
|
return "", fmt.Errorf("file read failure")
|
||||||
|
}
|
||||||
|
fileName := filepath.Base(softwarePath)
|
||||||
|
if strings.Contains(fileName, "*") {
|
||||||
|
fileName = strings.ReplaceAll(fileName, "*", "_")
|
||||||
|
}
|
||||||
|
nePath := "/tmp"
|
||||||
|
neFilePath := fmt.Sprintf("%s/%s", nePath, fileName)
|
||||||
|
|
||||||
|
// 网元主机的SSH客户端
|
||||||
|
sshClient, err := NewNeInfoImpl.NeRunSSHclient(neVersion.NeType, neVersion.NeId)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
defer sshClient.Close()
|
||||||
|
// 网元主机的SSH客户端进行文件传输
|
||||||
|
sftpClient, err := sshClient.NewClientSFTP()
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
defer sftpClient.Close()
|
||||||
|
|
||||||
|
// 上传软件包到 /tmp
|
||||||
|
if err = sftpClient.CopyFileLocalToRemote(localFilePath, neFilePath); err != nil {
|
||||||
|
return "", fmt.Errorf("error uploading package")
|
||||||
|
}
|
||||||
|
|
||||||
|
// ========= 安装命令 start =========
|
||||||
|
// 命令终止结束标记
|
||||||
|
okFlagStr := fmt.Sprintf("%s version %s successful!", neVersion.NeType, action)
|
||||||
|
// 安装软件包
|
||||||
|
pkgCmdStr := fmt.Sprintf("sudo dpkg -i %s", neFilePath)
|
||||||
|
fileExt := filepath.Ext(strings.ToLower(fileName))
|
||||||
|
if strings.HasSuffix(fileExt, "rpm") {
|
||||||
|
pkgCmdStr = fmt.Sprintf("sudo rpm -Uvh %s", neFilePath)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 预先参数
|
||||||
|
cmdStrArr := []string{}
|
||||||
|
if neVersion.NeType == "OMC" {
|
||||||
|
cmdStrArr = append(cmdStrArr, "sudo /usr/local/omc/bin/omcsvc.sh stop")
|
||||||
|
cmdStrArr = append(cmdStrArr, pkgCmdStr)
|
||||||
|
cmdStrArr = append(cmdStrArr, "sudo /usr/local/omc/bin/omcsvc.sh restart")
|
||||||
|
return sshClient.RunCMD(fmt.Sprintf("nohup sh -c \"sleep 15s && %s\" > /dev/null 2>&1 & \n", strings.Join(cmdStrArr, " && ")))
|
||||||
|
} else if neVersion.NeType == "IMS" {
|
||||||
|
if !strings.Contains(strings.ToLower(fileName), "ims") {
|
||||||
|
return "", fmt.Errorf("error file package not ims")
|
||||||
|
}
|
||||||
|
cmdStrArr = append(cmdStrArr, "sudo ims-stop \n")
|
||||||
|
cmdStrArr = append(cmdStrArr, pkgCmdStr+" \n")
|
||||||
|
// P/I/S-CSCF Config 配置覆盖
|
||||||
|
if pisCSCF, ok := preinput["pisCSCF"]; ok && pisCSCF != "" {
|
||||||
|
cmdStrArr = append(cmdStrArr, fmt.Sprintf("%s \n", pisCSCF))
|
||||||
|
}
|
||||||
|
cmdStrArr = append(cmdStrArr, "sudo ims-start \n")
|
||||||
|
} else {
|
||||||
|
neTypeLower := strings.ToLower(neVersion.NeType)
|
||||||
|
cmdStrArr = append(cmdStrArr, fmt.Sprintf("sudo service %s stop \n", neTypeLower))
|
||||||
|
cmdStrArr = append(cmdStrArr, pkgCmdStr+" \n")
|
||||||
|
cmdStrArr = append(cmdStrArr, fmt.Sprintf("sudo service %s restart \n", neTypeLower))
|
||||||
|
}
|
||||||
|
|
||||||
|
// 删除软件包
|
||||||
|
cmdStrArr = append(cmdStrArr, fmt.Sprintf("sudo rm %s \n", neFilePath))
|
||||||
|
// 结束
|
||||||
|
cmdStrArr = append(cmdStrArr, fmt.Sprintf("echo '%s' \n", okFlagStr))
|
||||||
|
// ========= 安装命令 end =========
|
||||||
|
|
||||||
|
// ssh连接会话
|
||||||
|
clientSession, err := sshClient.NewClientSession(80, 24)
|
||||||
|
if err != nil {
|
||||||
|
return "", fmt.Errorf("neinfo ssh client session new err")
|
||||||
|
}
|
||||||
|
defer clientSession.Close()
|
||||||
|
|
||||||
|
firstRead := true // 首次命令进行记录日志信息
|
||||||
|
logMsg := "" // 日志信息
|
||||||
|
done := make(chan bool) // 完成信号
|
||||||
|
// 超时退出 30s
|
||||||
|
timeoutTicker := time.NewTicker(30 * time.Second)
|
||||||
|
defer timeoutTicker.Stop()
|
||||||
|
// 实时读取SSH消息直接输出
|
||||||
|
msTicker := time.NewTicker(100 * time.Millisecond)
|
||||||
|
defer msTicker.Stop()
|
||||||
|
go func() {
|
||||||
|
for {
|
||||||
|
select {
|
||||||
|
case <-timeoutTicker.C:
|
||||||
|
done <- true
|
||||||
|
return
|
||||||
|
case <-msTicker.C:
|
||||||
|
outputByte := clientSession.Read()
|
||||||
|
if len(outputByte) > 0 {
|
||||||
|
outputStr := string(outputByte)
|
||||||
|
// 非首次进行记录命令
|
||||||
|
if !firstRead {
|
||||||
|
logMsg += outputStr
|
||||||
|
}
|
||||||
|
// IMS预输入
|
||||||
|
if neVersion.NeType == "IMS" && strings.Contains(outputStr, "(P/I/S-CSCF Config)? <y/n>") {
|
||||||
|
shiftElement := cmdStrArr[0] // 获取第一个元素
|
||||||
|
cmdStrArr = cmdStrArr[1:] // 将第一个元素从切片中移除
|
||||||
|
clientSession.Write(shiftElement)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
// 命令终止符后继续执行命令
|
||||||
|
if len(cmdStrArr) > 0 && strings.LastIndex(outputStr, "~$ ") > 2 {
|
||||||
|
if firstRead {
|
||||||
|
firstRead = false
|
||||||
|
}
|
||||||
|
shiftElement := cmdStrArr[0] // 获取第一个元素
|
||||||
|
cmdStrArr = cmdStrArr[1:] // 将第一个元素从切片中移除
|
||||||
|
clientSession.Write(shiftElement)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
// 最后输出的退出标记
|
||||||
|
if strings.LastIndex(outputStr, okFlagStr) > 5 {
|
||||||
|
done <- true
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
// 等待写入协程完成
|
||||||
|
<-done
|
||||||
|
return logMsg, nil
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user