package service import ( "encoding/json" "fmt" "os" "path/filepath" "runtime" "strings" "be.ems/src/framework/constants/cachekey" "be.ems/src/framework/logger" "be.ems/src/framework/redis" "be.ems/src/framework/utils/parse" "be.ems/src/framework/utils/ssh" "be.ems/src/modules/network_element/model" "be.ems/src/modules/network_element/repository" ) // 实例化服务层 NeInfoImpl 结构体 var NewNeInfoImpl = &NeInfoImpl{ neInfoRepository: repository.NewNeInfoImpl, } // 网元信息 服务层处理 type NeInfoImpl struct { // 网元信息数据信息 neInfoRepository repository.INeInfo } // SelectNeInfoByNeTypeAndNeID 通过ne_type和ne_id查询网元信息 func (r *NeInfoImpl) SelectNeInfoByNeTypeAndNeID(neType, neID string) model.NeInfo { var neInfo model.NeInfo key := fmt.Sprintf("%s%s:%s", cachekey.NE_KEY, strings.ToUpper(neType), neID) jsonStr, _ := redis.Get("", key) if len(jsonStr) > 7 { err := json.Unmarshal([]byte(jsonStr), &neInfo) if err != nil { neInfo = model.NeInfo{} } } else { neInfo = r.neInfoRepository.SelectNeInfoByNeTypeAndNeID(neType, neID) if neInfo.NeId == neID { redis.Del("", key) values, _ := json.Marshal(neInfo) redis.Set("", key, string(values)) } } return neInfo } // RefreshByNeTypeAndNeID 通过ne_type和ne_id刷新redis中的缓存 func (r *NeInfoImpl) RefreshByNeTypeAndNeID(neType, neID string) model.NeInfo { var neInfo model.NeInfo key := fmt.Sprintf("%s%s:%s", cachekey.NE_KEY, strings.ToUpper(neType), neID) redis.Del("", key) neInfo = r.neInfoRepository.SelectNeInfoByNeTypeAndNeID(neType, neID) if neInfo.NeId == neID { values, _ := json.Marshal(neInfo) redis.Set("", key, string(values)) } return neInfo } // ClearNeCacheByNeType 清除网元类型缓存 func (r *NeInfoImpl) ClearNeCacheByNeType(neType string) bool { key := fmt.Sprintf("%s*", cachekey.NE_KEY) if neType != "*" { key = fmt.Sprintf("%s%s*", cachekey.NE_KEY, neType) } keys, err := redis.GetKeys("", key) if err != nil { return false } delOk, _ := redis.DelKeys("", keys) return delOk } // SelectNeInfoByRmuid 通过rmUID查询网元信息 func (r *NeInfoImpl) SelectNeInfoByRmuid(rmUid string) model.NeInfo { var neInfo model.NeInfo cacheKeys, _ := redis.GetKeys("", cachekey.NE_KEY+"*") if len(cacheKeys) > 0 { for _, key := range cacheKeys { var v model.NeInfo jsonStr, _ := redis.Get("", key) if len(jsonStr) > 7 { json.Unmarshal([]byte(jsonStr), &v) } if v.RmUID == rmUid { neInfo = v break } } } else { neInfos := r.SelectList(neInfo, false, false) for _, v := range neInfos { key := fmt.Sprintf("%s%s:%s", cachekey.NE_KEY, strings.ToUpper(v.NeType), v.NeId) redis.Del("", key) values, _ := json.Marshal(v) redis.Set("", key, string(values)) if v.RmUID == rmUid { neInfo = v } } } return neInfo } // SelectPage 根据条件分页查询 // // bandStatus 带状态信息 func (r *NeInfoImpl) SelectPage(query map[string]any, bandStatus bool) map[string]any { data := r.neInfoRepository.SelectPage(query) // 网元直连读取网元服务状态 if bandStatus { rows := data["rows"].([]model.NeInfo) r.bandNeStatus(&rows) } return data } // SelectList 查询列表 // // bandStatus 带状态信息 // bandHost 带主机信息 func (r *NeInfoImpl) SelectList(ne model.NeInfo, bandStatus bool, bandHost bool) []model.NeInfo { list := r.neInfoRepository.SelectList(ne) // 网元直连读取网元服务状态 if bandStatus { r.bandNeStatus(&list) } // 网元主机信息 if bandHost { r.bandNeHosts(&list) } return list } // bandNeStatus 网元列表项数据带网元服务状态 func (r *NeInfoImpl) bandNeStatus(arr *[]model.NeInfo) { for i := range *arr { v := (*arr)[i] result, err := NeState(v) if err != nil { (*arr)[i].ServerState = map[string]any{ "online": false, } // 网元状态设置为离线 if v.Status != "0" { v.Status = "0" (*arr)[i].Status = v.Status r.neInfoRepository.Update(v) } continue } result["online"] = true (*arr)[i].ServerState = result // 网元状态设置为在线 if v.Status != "1" { // 下发网管配置信息给网元 _, err = NeConfigOMC(v) if err == nil { v.Status = "1" } else { v.Status = "2" } (*arr)[i].Status = v.Status r.neInfoRepository.Update(v) } } } // bandNeHosts 网元列表项数据带网元主机信息 func (r *NeInfoImpl) bandNeHosts(arr *[]model.NeInfo) { for i := range *arr { v := (*arr)[i] if v.HostIDs != "" { (*arr)[i].Hosts = NewNeHostImpl.neHostRepository.SelectByIds(strings.Split(v.HostIDs, ",")) } } } // SelectByIds 通过ID查询 // // bandHost 带主机信息 func (r *NeInfoImpl) SelectById(infoId string, bandHost bool) model.NeInfo { if infoId == "" { return model.NeInfo{} } neInfos := r.neInfoRepository.SelectByIds([]string{infoId}) if len(neInfos) > 0 { neInfo := neInfos[0] // 带主机信息 if neInfo.HostIDs != "" && bandHost { neInfo.Hosts = NewNeHostImpl.neHostRepository.SelectByIds(strings.Split(neInfo.HostIDs, ",")) } return neInfo } return model.NeInfo{} } // Insert 新增信息 func (r *NeInfoImpl) Insert(neInfo model.NeInfo) string { // 主机信息新增 if neInfo.Hosts != nil { var hostIDs []string for _, host := range neInfo.Hosts { host.Title = fmt.Sprintf("%s_%s_%d", strings.ToUpper(neInfo.NeType), neInfo.NeId, host.Port) host.GroupID = "1" hostId := NewNeHostImpl.Insert(host) if hostId != "" { hostIDs = append(hostIDs, hostId) } } neInfo.HostIDs = strings.Join(hostIDs, ",") } insertId := r.neInfoRepository.Insert(neInfo) if insertId != "" { // 刷新缓存 r.RefreshByNeTypeAndNeID(neInfo.NeType, neInfo.NeId) } return insertId } // Update 修改信息 func (r *NeInfoImpl) Update(neInfo model.NeInfo) int64 { // 主机信息更新 if neInfo.Hosts != nil { for _, host := range neInfo.Hosts { if host.HostID != "" { host.Title = fmt.Sprintf("%s_%s_%d", strings.ToUpper(neInfo.NeType), neInfo.NeId, host.Port) host.GroupID = "1" NewNeHostImpl.Update(host) } } } num := r.neInfoRepository.Update(neInfo) if num > 0 { // 刷新缓存 r.RefreshByNeTypeAndNeID(neInfo.NeType, neInfo.NeId) } return num } // DeleteByIds 批量删除信息 func (r *NeInfoImpl) DeleteByIds(infoIds []string) (int64, error) { // 检查是否存在 infos := r.neInfoRepository.SelectByIds(infoIds) if len(infos) <= 0 { return 0, fmt.Errorf("neHostCmd.noData") } if len(infos) == len(infoIds) { for _, v := range infos { // 主机信息删除 if v.HostIDs != "" { hostIds := strings.Split(v.HostIDs, ",") NewNeHostImpl.DeleteByIds(hostIds) } // 删除License neLicense := NewNeLicenseImpl.SelectByNeTypeAndNeID(v.NeType, v.NeId) if neLicense.NeId == v.NeId { NewNeLicenseImpl.DeleteByIds([]string{neLicense.ID}) } // 删除Version neVersion := NewNeVersionImpl.SelectByNeTypeAndNeID(v.NeType, v.NeId) if neVersion.NeId == v.NeId { NewNeVersionImpl.DeleteByIds([]string{neVersion.ID}) } // 缓存信息删除 key := fmt.Sprintf("%s%s:%s", cachekey.NE_KEY, v.NeType, v.NeId) redis.Del("", key) } rows := r.neInfoRepository.DeleteByIds(infoIds) return rows, nil } // 删除信息失败! return 0, fmt.Errorf("delete fail") } // CheckUniqueNeTypeAndNeId 校验同类型下标识是否唯一 func (r *NeInfoImpl) CheckUniqueNeTypeAndNeId(neType, neId, infoId string) bool { uniqueId := r.neInfoRepository.CheckUniqueNeTypeAndNeId(model.NeInfo{ NeType: neType, NeId: neId, }) if uniqueId == infoId { return true } return uniqueId == "" } // NeRunSSHclient 网元主机的SSH客户端-为创建相关连接 func (r *NeInfoImpl) NeRunSSHclient(neType, neId string) (*ssh.ConnSSH, error) { neInfo := r.SelectNeInfoByNeTypeAndNeID(neType, neId) if neInfo.NeId != neId { logger.Errorf("NeRunSSHclient NeType:%s NeID:%s not found", neType, neId) return nil, fmt.Errorf("neinfo not found") } // 取主机信息 if neInfo.HostIDs == "" { logger.Errorf("NeRunSSHclient NeType:%s NeID:%s hostId not found", neType, neId) return nil, fmt.Errorf("neinfo hostId not found") } neInfo.Hosts = NewNeHostImpl.neHostRepository.SelectByIds(strings.Split(neInfo.HostIDs, ",")) if len(neInfo.Hosts) <= 0 { logger.Errorf("NeRunSSHclient Hosts %s not found", neInfo.HostIDs) return nil, fmt.Errorf("neinfo host not found") } neHost := neInfo.Hosts[0] if neHost.HostType != "ssh" { logger.Errorf("NeRunSSHclient Hosts first HostType %s not ssh", neHost.HostType) return nil, fmt.Errorf("neinfo host type not ssh") } var connSSH ssh.ConnSSH neHost.CopyTo(&connSSH) var client *ssh.ConnSSH var err error if neHost.AuthMode == "2" { client, err = connSSH.NewClientByLocalPrivate() } else { client, err = connSSH.NewClient() } if err != nil { logger.Errorf("NeRunSSHclient NewClient err => %s", err.Error()) return nil, fmt.Errorf("neinfo ssh client new err") } return client, nil } // NeRunCMD 向网元发送cmd命令 func (r *NeInfoImpl) NeRunCMD(neType, neId, cmd string) (string, error) { sshClient, err := r.NeRunSSHclient(neType, neId) if err != nil { return "", err } defer sshClient.Close() // 执行命令 output, err := sshClient.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读取 // // 返回 string string[] map[string]any func (r *NeInfoImpl) NeConfigFileRead(neInfo model.NeInfo, filePath, fileType string) any { 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 "read file error" } content := string(bytes) if fileType == "" || fileType == "txt" { return content } // 序列化Map mapData, err := parse.ConvertConfigToMap(fileType, content) if err != nil { logger.Warnf("NeConfigFile ConvertConfigToMap => %s", err.Error()) return "content convert type error" } return mapData } // 文件列表 files := []string{} // 删除原有配置文件 // 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) // 网元主机的SSH客户端 sshClient, err := r.NeRunSSHclient(neInfo.NeType, neInfo.NeId) if err != nil { return err.Error() } defer sshClient.Close() // 网元主机的SSH客户端进行文件传输 sftpClient, err := sshClient.NewClientSFTP() if err != nil { return err.Error() } defer sftpClient.Close() // 各个网元与网管间约定配置文件 if err = sftpClient.CopyFileRemoteToLocal(nePath+"/oam_manager.yaml", omcPath+"/oam_manager.yaml"); err == nil { files = append(files, "oam_manager.yaml") } // 根据情况复制网元特殊配置 switch neTypeLower { case "ausf": cfgFile := "ausfcfg.yaml" if err = sftpClient.CopyFileRemoteToLocal(fmt.Sprintf("%s/%s", nePath, cfgFile), fmt.Sprintf("%s/%s", omcPath, cfgFile)); err == nil { files = append(files, cfgFile) } case "smf": cfgFile := "smf_conf.yaml" if err = sftpClient.CopyFileRemoteToLocal(fmt.Sprintf("%s/%s", nePath, cfgFile), fmt.Sprintf("%s/%s", omcPath, cfgFile)); err == nil { files = append(files, cfgFile) } case "ims": } return files } // NeConfigFileWirte 网元配置文件写入 content内容 sync同步到网元端 func (r *NeInfoImpl) NeConfigFileWirte(neInfo model.NeInfo, filePath, fileType string, content any, 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) var err error if fileType == "" || fileType == "txt" { err = parse.ConvertConfigToFile(fileType, localFilePath, content) } if fileType == "json" || fileType == "yaml" || fileType == "yml" { err = parse.ConvertConfigToFile(fileType, localFilePath, content) } if err != nil { return fmt.Errorf("please check if the file exists or write permissions") } // 同步到网元端 if sync { // 网元主机的SSH客户端 sshClient, err := r.NeRunSSHclient(neInfo.NeType, neInfo.NeId) if err != nil { return err } defer sshClient.Close() // 网元主机的SSH客户端进行文件传输 sftpClient, err := sshClient.NewClientSFTP() if err != nil { return err } defer sftpClient.Close() // 网元端配置路径 neFilePath := fmt.Sprintf("/usr/local/etc/%s/%s", neTypeLower, filePath) // 修改网元文件权限 sshClient.RunCMD(fmt.Sprintf("sudo chmod o+w %s", neFilePath)) // 复制到网元进行覆盖 if err = sftpClient.CopyFileLocalToRemote(localFilePath, neFilePath); err != nil { return fmt.Errorf("please check if scp remote copy is allowed") } } return nil } // NeConfPara5GRead 网元公共配置文件读取 // // 返回 string map[string]any func (r *NeInfoImpl) NeConfPara5GRead(fileType string) any { // 网管本地路径 omcFilePath := "/usr/local/etc/omc/para5G.yaml" if runtime.GOOS == "windows" { omcFilePath = fmt.Sprintf("C:%s", omcFilePath) } // 读取文件内容 bytes, err := os.ReadFile(omcFilePath) if err != nil { logger.Warnf("NeConfPara5GRead ReadFile => %s", err.Error()) return "read file error" } content := string(bytes) if fileType == "" || fileType == "txt" { return content } // 序列化Map mapData, err := parse.ConvertConfigToMap(fileType, content) if err != nil { logger.Warnf("NeConfPara5GRead ConvertConfigToMap => %s", err.Error()) return "content convert type error" } return mapData } // NeConfPara5GWirte 网元公共配置文件写入 content内容 syncNE同步到网元端NeType@NeId func (r *NeInfoImpl) NeConfPara5GWirte(fileType string, content any, syncNE []string) error { // 网管本地路径 omcFilePath := "/usr/local/etc/omc/para5G.yaml" if runtime.GOOS == "windows" { omcFilePath = fmt.Sprintf("C:%s", omcFilePath) } var err error if fileType == "" || fileType == "txt" { err = parse.ConvertConfigToFile(fileType, omcFilePath, content) } if fileType == "json" || fileType == "yaml" || fileType == "yml" { err = parse.ConvertConfigToFile(fileType, omcFilePath, content) } if err != nil { return fmt.Errorf("please check if the file exists or write permissions") } // 同步到网元端 if len(syncNE) > 0 { errMsg := []string{} for _, neTI := range syncNE { ti := strings.SplitN(neTI, "@", 2) // 网元主机的SSH客户端 sshClient, err := r.NeRunSSHclient(ti[0], ti[1]) if err != nil { errMsg = append(errMsg, fmt.Sprintf("%s : %s", ti, err.Error())) continue } defer sshClient.Close() // 网元主机的SSH客户端进行文件传输 sftpClient, err := sshClient.NewClientSFTP() if err != nil { errMsg = append(errMsg, fmt.Sprintf("%s : %s", ti, err.Error())) continue } defer sftpClient.Close() // 网元端配置路径 neFilePath := "/usr/local/etc/conf/para5G.yaml" neFileDir := filepath.Dir(neFilePath) // 修改网元文件权限 sshClient.RunCMD(fmt.Sprintf("sudo mkdir -p %s && sudo chmod o+w %s && sudo chmod o+w %s", neFileDir, neFileDir, neFilePath)) // 复制到网元进行覆盖 if err = sftpClient.CopyFileLocalToRemote(omcFilePath, neFilePath); err != nil { errMsg = append(errMsg, fmt.Sprintf("%s : please check if scp remote copy is allowed", ti)) continue } } if len(errMsg) > 0 { return fmt.Errorf(strings.Join(errMsg, "\r\n")) } } return nil }