240 lines
7.1 KiB
Go
240 lines
7.1 KiB
Go
package service
|
||
|
||
import (
|
||
"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/repository"
|
||
)
|
||
|
||
// 实例化服务层 NeVersionImpl 结构体
|
||
var NewNeVersionImpl = &NeVersionImpl{
|
||
neVersionRepository: repository.NewNeVersionImpl,
|
||
}
|
||
|
||
// NeVersionImpl 网元版本信息 服务层处理
|
||
type NeVersionImpl struct {
|
||
// 网元版本信息表
|
||
neVersionRepository repository.INeVersion
|
||
}
|
||
|
||
// SelectNeHostPage 分页查询列表数据
|
||
func (r *NeVersionImpl) SelectPage(query map[string]any) map[string]any {
|
||
return r.neVersionRepository.SelectPage(query)
|
||
}
|
||
|
||
// SelectConfigList 查询列表
|
||
func (r *NeVersionImpl) SelectList(neVersion model.NeVersion) []model.NeVersion {
|
||
return r.neVersionRepository.SelectList(neVersion)
|
||
}
|
||
|
||
// SelectByIds 通过ID查询
|
||
func (r *NeVersionImpl) SelectById(id string) model.NeVersion {
|
||
if id == "" {
|
||
return model.NeVersion{}
|
||
}
|
||
neVersions := r.neVersionRepository.SelectByIds([]string{id})
|
||
if len(neVersions) > 0 {
|
||
return neVersions[0]
|
||
}
|
||
return model.NeVersion{}
|
||
}
|
||
|
||
// Insert 新增信息
|
||
func (r *NeVersionImpl) Insert(neVersion model.NeVersion) string {
|
||
return r.neVersionRepository.Insert(neVersion)
|
||
}
|
||
|
||
// Update 修改信息
|
||
func (r *NeVersionImpl) Update(neVersion model.NeVersion) int64 {
|
||
return r.neVersionRepository.Update(neVersion)
|
||
}
|
||
|
||
// DeleteByIds 批量删除信息
|
||
func (r *NeVersionImpl) DeleteByIds(ids []string) (int64, error) {
|
||
// 检查是否存在
|
||
rowIds := r.neVersionRepository.SelectByIds(ids)
|
||
if len(rowIds) <= 0 {
|
||
return 0, fmt.Errorf("neVersion.noData")
|
||
}
|
||
|
||
if len(rowIds) == len(ids) {
|
||
rows := r.neVersionRepository.DeleteByIds(ids)
|
||
return rows, nil
|
||
}
|
||
// 删除信息失败!
|
||
return 0, fmt.Errorf("delete fail")
|
||
}
|
||
|
||
// SelectByNeTypeAndNeID 通过网元类型和网元ID查询
|
||
func (r *NeVersionImpl) SelectByNeTypeAndNeID(neType, neId string) model.NeVersion {
|
||
neVersions := r.neVersionRepository.SelectList(model.NeVersion{
|
||
NeType: neType,
|
||
NeId: neId,
|
||
})
|
||
if len(neVersions) > 0 {
|
||
return neVersions[0]
|
||
}
|
||
return model.NeVersion{}
|
||
}
|
||
|
||
// CheckUniqueTypeAndID 校验网元类型和网元ID是否唯一
|
||
func (r *NeVersionImpl) CheckUniqueTypeAndID(neType, neId, id string) bool {
|
||
uniqueId := r.neVersionRepository.CheckUniqueTypeAndID(model.NeVersion{
|
||
NeType: neType,
|
||
NeId: neId,
|
||
})
|
||
if uniqueId == id {
|
||
return true
|
||
}
|
||
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
|
||
}
|