package service import ( "encoding/json" "fmt" "os" "path/filepath" "runtime" "strings" "time" "be.ems/src/framework/constants" "be.ems/src/framework/database/redis" "be.ems/src/framework/logger" "be.ems/src/framework/ssh" "be.ems/src/framework/telnet" "be.ems/src/framework/utils/generate" "be.ems/src/framework/utils/parse" neFetchlink "be.ems/src/modules/ne/fetch_link" "be.ems/src/modules/ne/model" "be.ems/src/modules/ne/repository" ) // 实例化服务层 NeInfo 结构体 var NewNeInfo = &NeInfo{ neInfoRepository: repository.NewNeInfo, neHostService: NewNeHost, Para5GData: map[string]string{}, } // 网元信息 服务层处理 type NeInfo struct { neInfoRepository *repository.NeInfo // 网元信息数据信息 neHostService *NeHost // 网元主机连接服务 Para5GData map[string]string } // FindByCoreUidAndNeUid 通过core_uid和ne_uid查询网元信息 // coreUid 为*时,根据neUid查询 func (r NeInfo) FindByCoreUidAndNeUid(coreUid string, neUid string) model.NeInfo { var neInfo model.NeInfo key := fmt.Sprintf("%s:%s:*:%s", constants.CACHE_NE_INFO, coreUid, neUid) jsonStr, _ := redis.Get("", key) if len(jsonStr) > 7 { err := json.Unmarshal([]byte(jsonStr), &neInfo) if err != nil { neInfo = model.NeInfo{} } } else { neInfo = r.neInfoRepository.SelectNeInfoByCoreUidAndNeUid(coreUid, neUid) if neInfo.CoreUID == coreUid && neInfo.NeUID == neUid { redis.Del("", key) values, _ := json.Marshal(neInfo) redis.Set("", key, string(values), 0) } } return neInfo } // RefreshByCoreUidAndNeUid 通过core_id和ne_uid刷新缓存 func (r NeInfo) RefreshByCoreUidAndNeUid(coreuid string, neUid string) model.NeInfo { var neInfo model.NeInfo if coreuid == "*" || coreuid == "" { return neInfo } key := fmt.Sprintf("%s:%s:*:%s", constants.CACHE_NE_INFO, coreuid, neUid) redis.Del("", key) neInfo = r.neInfoRepository.SelectNeInfoByCoreUidAndNeUid(coreuid, neUid) if neInfo.CoreUID == coreuid && neInfo.NeUID == neUid { values, _ := json.Marshal(neInfo) redis.Set("", key, string(values), 0) } return neInfo } // FindByCoreUidAndNeType 通过core_uid和ne_type查询网元信息 // coreUid 为*时,根据neType查询 func (r NeInfo) FindByCoreUidAndNeType(coreUid string, neType string) []model.NeInfo { neInfo := make([]model.NeInfo, 0) key := fmt.Sprintf("%s:%s:%s:*", constants.CACHE_NE_INFO, coreUid, neType) cacheKeys, _ := redis.GetKeys("", 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) } neInfo = append(neInfo, v) } return neInfo } else { neInfo = r.neInfoRepository.SelectList(model.NeInfo{CoreUID: coreUid, NeType: neType}) for _, v := range neInfo { key := fmt.Sprintf("%s:%s:%s:%s", constants.CACHE_NE_INFO, v.CoreUID, v.NeType, v.NeUID) redis.Del("", key) values, _ := json.Marshal(v) redis.Set("", key, string(values), 0) } } return neInfo } // ClearNeCacheByCoreUidOrNeUid 清除核心网下网元信息缓存 // coreUid 核心网唯一标识 *表示清除所有 func (r NeInfo) ClearNeCacheByCoreUid(coreUid string) bool { key := fmt.Sprintf("%s:*", constants.CACHE_NE_INFO) if coreUid != "*" { key = fmt.Sprintf("%s:%s:*", constants.CACHE_NE_INFO, coreUid) } keys, err := redis.GetKeys("", key) if err != nil { return false } return redis.DelKeys("", keys) == nil } // RefreshNeCacheByCoreUid 刷新核心网下网元信息缓存 // coreUid 核心网唯一标识 *表示所有 func (r NeInfo) RefreshNeCacheByCoreUid(coreUid string) { if coreUid == "*" { coreUid = "" } neInfos := r.Find(model.NeInfo{CoreUID: coreUid}, false, false) for _, v := range neInfos { key := fmt.Sprintf("%s:%s:%s:%s", constants.CACHE_NE_INFO, v.CoreUID, v.NeType, v.NeUID) redis.Del("", key) values, _ := json.Marshal(v) redis.Set("", key, string(values), 0) } } // FindByPage 根据条件分页查询 // // bandStatus 带状态信息 func (r NeInfo) FindByPage(query map[string]string, bandStatus bool) ([]model.NeInfo, int64) { rows, total := r.neInfoRepository.SelectByPage(query) // 网元直连读取网元服务状态 if bandStatus { r.bandNeStatus(&rows) } return rows, total } // Find 查询列表 // // bandStatus 带状态信息 // bandHost 带主机信息 func (r NeInfo) Find(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 NeInfo) bandNeStatus(arr *[]model.NeInfo) { for i := range *arr { v := (*arr)[i] result, err := neFetchlink.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 (*arr)[i].UpdateTime = time.Now().UnixMilli() r.neInfoRepository.UpdateState(v.ID, v.Status) } continue } result["online"] = true (*arr)[i].ServerState = result // 网元状态设置为在线 var status int64 = 1 if parse.Boolean(result["standby"]) { status = 3 } // 下发网管配置信息给网元 if _, err = neFetchlink.NeConfigOMC(v); err != nil { status = 2 } (*arr)[i].Status = status if v.Status != status { (*arr)[i].UpdateTime = time.Now().UnixMilli() r.neInfoRepository.UpdateState(v.ID, status) } } } // bandNeHosts 网元列表项数据带网元主机信息 func (r NeInfo) bandNeHosts(arr *[]model.NeInfo) { for i := range *arr { v := (*arr)[i] if v.HostIDs != "" { hostIds := strings.Split(v.HostIDs, ",") if len(hostIds) <= 1 { continue } for _, v := range hostIds { hostId := parse.Number(v) neHost := r.neHostService.FindById(hostId) if neHost.ID == 0 || neHost.ID != hostId { continue } (*arr)[i].Hosts = append((*arr)[i].Hosts, neHost) } } } } // FindById 通过ID查询 // // bandHost 带主机信息 func (r NeInfo) FindById(id int64, bandHost bool) model.NeInfo { if id <= 0 { return model.NeInfo{} } neInfos := r.neInfoRepository.SelectByIds([]int64{id}) if len(neInfos) > 0 { // 带主机信息 if neInfos[0].HostIDs != "" && bandHost { r.bandNeHosts(&neInfos) } return neInfos[0] } return model.NeInfo{} } // Insert 新增信息 func (r NeInfo) Insert(neInfo model.NeInfo) int64 { if neInfo.CoreUID == "" { return 0 } neInfo.NeUID = strings.ToUpper(generate.Code(8)) // 获取网元状态是否正常 serverState, err := neFetchlink.NeState(neInfo) if err != nil { neInfo.Status = 0 } else { // 网元状态设置为在线 neInfo.Status = 1 if parse.Boolean(serverState["standby"]) { neInfo.Status = 3 } // 下发网管配置信息给网元 if _, err = neFetchlink.NeConfigOMC(neInfo); err != nil { neInfo.Status = 2 } } // 主机信息新增 if neInfo.Hosts != nil { uuid := generate.Code(4) var hostIDs []string for _, host := range neInfo.Hosts { host.Title = fmt.Sprintf("%s_%d_%s", neInfo.NeName, host.Port, uuid) host.GroupID = "1" host.CreateBy = neInfo.CreateBy hostId := r.neHostService.Insert(host) if hostId > 0 { hostIDs = append(hostIDs, fmt.Sprint(hostId)) } } neInfo.HostIDs = strings.Join(hostIDs, ",") } insertId := r.neInfoRepository.Insert(neInfo) if insertId > 0 { // 新增Version信息 neVersion := model.NeVersion{ CoreUID: neInfo.CoreUID, NeUID: neInfo.NeUID, NeType: neInfo.NeType, CreateBy: neInfo.CreateBy, } if v, ok := serverState["version"]; ok && v != nil { // neVersion.Name = "-" // neVersion.Path = "-" neVersion.Version = fmt.Sprint(v) } NewNeVersion.Insert(neVersion) // 新增License信息 neLicense := model.NeLicense{ CoreUID: neInfo.CoreUID, NeUID: neInfo.NeUID, NeType: neInfo.NeType, CreateBy: neInfo.CreateBy, } if v, ok := serverState["sn"]; ok && v != nil { neLicense.SerialNum = fmt.Sprint(v) } if v, ok := serverState["expire"]; ok && v != nil { neLicense.ExpiryDate = fmt.Sprint(v) neLicense.Status = "1" } if v, ok := serverState["ueNumber"]; ok && v != nil { neLicense.UeNumber = parse.Number(v) } if v, ok := serverState["nbNumber"]; ok && v != nil { neLicense.NbNumber = parse.Number(v) } NewNeLicense.Insert(neLicense) r.RefreshByCoreUidAndNeUid(neInfo.CoreUID, neInfo.NeUID) // 刷新缓存 } return insertId } // Update 修改信息 func (r NeInfo) Update(neInfo model.NeInfo) int64 { // 获取网元状态是否正常 serverState, err := neFetchlink.NeState(neInfo) if err != nil { neInfo.Status = 0 } else { // 网元状态设置为在线 neInfo.Status = 1 if parse.Boolean(serverState["standby"]) { neInfo.Status = 3 } // 下发网管配置信息给网元 if _, err = neFetchlink.NeConfigOMC(neInfo); err != nil { neInfo.Status = 2 } } // 主机信息更新 if neInfo.HostIDs != "" && len(neInfo.Hosts) > 0 { hostIDs := strings.Split(neInfo.HostIDs, ",") for index, id := range hostIDs { neInfo.Hosts[index].ID = parse.Number(id) } uuid := generate.Code(4) for _, host := range neInfo.Hosts { if host.ID != 0 { host.Title = fmt.Sprintf("%s_%d_%s", neInfo.NeType, host.Port, uuid) host.GroupID = "1" host.UpdateBy = neInfo.UpdateBy r.neHostService.Update(host) } } } num := r.neInfoRepository.Update(neInfo) if num > 0 { // 版本信息更新 neVersion := NewNeVersion.FindByCoreUidAndNeUid(neInfo.CoreUID, neInfo.NeUID) if neVersion.ID != 0 { if neVersion.NeType != neInfo.NeType { neVersion.NeType = neInfo.NeType } if v, ok := serverState["version"]; ok && v != neVersion.Version { // neVersion.Name = "-" // neVersion.Path = "-" neVersion.Version = fmt.Sprint(v) } neVersion.UpdateBy = neInfo.UpdateBy NewNeVersion.Update(neVersion) } // License信息更新 neLicense := NewNeLicense.FindByCoreUidAndNeUid(neInfo.CoreUID, neInfo.NeUID) if neLicense.ID != 0 { if neLicense.NeType != neInfo.NeType { neLicense.NeType = neInfo.NeType } if v, ok := serverState["sn"]; ok && v != nil { neLicense.SerialNum = fmt.Sprint(v) } if v, ok := serverState["expire"]; ok && v != nil { neLicense.ExpiryDate = fmt.Sprint(v) neLicense.Status = "1" } if v, ok := serverState["ueNumber"]; ok && v != nil { neLicense.UeNumber = parse.Number(v) } if v, ok := serverState["nbNumber"]; ok && v != nil { neLicense.NbNumber = parse.Number(v) } neLicense.UpdateBy = neInfo.UpdateBy NewNeLicense.Update(neLicense) } r.RefreshByCoreUidAndNeUid(neInfo.CoreUID, neInfo.NeUID) // 刷新缓存 } return num } // DeleteByIds 批量删除信息 func (r NeInfo) DeleteByIds(ids []int64, coreUid, neUid string) (int64, error) { // 检查是否存在 arr := r.neInfoRepository.SelectByIds(ids) if len(arr) != len(ids) { return 0, fmt.Errorf("not match id") } for _, v := range arr { if v.CoreUID != coreUid || v.NeUID != neUid { return 0, fmt.Errorf("data not match, id: %d", v.ID) } } for _, v := range arr { // 主机信息删除 if v.HostIDs != "" { hostIds := make([]int64, 0) arr := strings.Split(v.HostIDs, ",") for _, hostId := range arr { hostIds = append(hostIds, parse.Number(hostId)) } r.neHostService.DeleteByIds(hostIds, false) } // 删除License neLicense := NewNeLicense.FindByCoreUidAndNeUid(v.CoreUID, v.NeUID) if neLicense.CoreUID == v.CoreUID && neLicense.NeUID == v.NeUID { NewNeLicense.DeleteByIds([]int64{neLicense.ID}) } // 删除Version neVersion := NewNeVersion.FindByCoreUidAndNeUid(v.CoreUID, v.NeUID) if neVersion.CoreUID == v.CoreUID && neVersion.NeUID == v.NeUID { NewNeVersion.DeleteByIds([]int64{neVersion.ID}) } // 缓存信息删除 redis.Del("", fmt.Sprintf("%s:%s:%s:%s", constants.CACHE_NE_INFO, v.CoreUID, v.NeType, v.NeUID)) } rows := r.neInfoRepository.DeleteByIds(ids) return rows, nil } // NeRunSSHClient 网元主机的SSH客户端-为创建相关连接,注意结束后 Close() func (r NeInfo) NeRunSSHClient(coreUid, neUid string) (*ssh.ConnSSH, error) { neInfo := r.FindByCoreUidAndNeUid(coreUid, neUid) if neInfo.CoreUID != coreUid || neInfo.NeUID != neUid { logger.Errorf("NeRunSSHClient CoreUID:%s or NeUID:%s not found", coreUid, neUid) return nil, fmt.Errorf("neinfo not found") } // 取主机信息 if neInfo.HostIDs == "" { logger.Errorf("NeRunSSHClient CoreUID:%s or NeUID:%s hostId not found", coreUid, neUid) return nil, fmt.Errorf("neinfo hostId not found") } hostIds := strings.Split(neInfo.HostIDs, ",") if len(hostIds) <= 1 { logger.Errorf("NeRunTelnetClient hosts id %s not found", neInfo.HostIDs) return nil, fmt.Errorf("neinfo host id not found") } hostId := parse.Number(hostIds[0]) // 网元主机ssh 0:22 neHost := r.neHostService.FindById(hostId) if neHost.ID == 0 || neHost.ID != hostId { logger.Errorf("NeRunTelnetClient Hosts %s not found", neInfo.HostIDs) return nil, fmt.Errorf("neinfo host not found") } 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 } // NeRunSSHCmd 网元主机的SSH客户端发送cmd命令 func (r NeInfo) NeRunSSHCmd(coreUid, neUid string, cmdStr string) (string, error) { sshClient, err := r.NeRunSSHClient(coreUid, neUid) if err != nil { return "", err } defer sshClient.Close() // 执行命令 output, err := sshClient.RunCMD(cmdStr) if err != nil { logger.Errorf("NeRunSSHCmd RunCMD %s err => %s", output, err.Error()) return "", fmt.Errorf("neinfo ssh run cmd err") } return output, nil } // NeRunTelnetClient 网元主机的Telnet客户端-为创建相关连接,注意结束后 Close() // num 是网元主机telnet 1:4100 2:5200(UPF标准版) func (r NeInfo) NeRunTelnetClient(coreUid, neUid string, num int) (*telnet.ConnTelnet, error) { neInfo := r.FindByCoreUidAndNeUid(coreUid, neUid) if neInfo.CoreUID != coreUid || neInfo.NeUID != neUid { logger.Errorf("NeRunSSHClient CoreUID:%s or NeUID:%s not found", coreUid, neUid) return nil, fmt.Errorf("neinfo not found") } // 取主机信息 if neInfo.HostIDs == "" { logger.Errorf("NeRunTelnetClient CoreUID:%s or NeUID:%s hostId not found", coreUid, neUid) return nil, fmt.Errorf("neinfo hostId not found") } hostIds := strings.Split(neInfo.HostIDs, ",") if len(hostIds) <= 1 { logger.Errorf("NeRunTelnetClient hosts id %s not found", neInfo.HostIDs) return nil, fmt.Errorf("neinfo host id not found") } hostId := parse.Number(hostIds[num]) // 网元主机telnet 1:4100 2:5200 neHost := r.neHostService.FindById(hostId) if neHost.ID == 0 || neHost.ID != hostId { logger.Errorf("NeRunTelnetClient Hosts %s not found", neInfo.HostIDs) return nil, fmt.Errorf("neinfo host not found") } // 创建链接Telnet客户端 var connTelnet telnet.ConnTelnet neHost.CopyTo(&connTelnet) telnetClient, err := connTelnet.NewClient() if err != nil { logger.Errorf("NeRunTelnetClient NewClient err => %s", err.Error()) return nil, fmt.Errorf("neinfo telnet client new err") } return telnetClient, nil } // NeRunRedisClient 网元主机的Redis客户端-为创建相关连接,注意结束后 Close() // 暂时只有UDM有Redis配置项 func (r NeInfo) NeRunRedisClient(coreUid, neUid string) (*redis.ConnRedis, error) { neInfo := r.FindByCoreUidAndNeUid(coreUid, neUid) if neInfo.CoreUID != coreUid || neInfo.NeUID != neUid { logger.Errorf("NeRunRedisClient CoreUid:%s NeUid:%s not found", coreUid, neUid) return nil, fmt.Errorf("neinfo not found") } // 取主机信息 if neInfo.HostIDs == "" { logger.Errorf("NeRunRedisClient CoreUid:%s NeUid:%s hostId not found", coreUid, neUid) return nil, fmt.Errorf("neinfo hostId not found") } hostIds := strings.Split(neInfo.HostIDs, ",") if len(hostIds) <= 2 { logger.Errorf("NeRunRedisClient hosts id %s not found", neInfo.HostIDs) return nil, fmt.Errorf("neinfo host id not found") } hostId := parse.Number(hostIds[2]) neHost := r.neHostService.FindById(hostId) if neHost.ID == 0 || neHost.ID != hostId { logger.Errorf("NeRunRedisClient Hosts %s not found", neInfo.HostIDs) return nil, fmt.Errorf("neinfo host not found") } // 创建链接Redis客户端 var connRedis redis.ConnRedis neHost.CopyTo(&connRedis) redisClient, err := connRedis.NewClient() if err != nil { logger.Errorf("NeRunRedisClient NewClient err => %s", err.Error()) return nil, fmt.Errorf("neinfo redis client new err") } return redisClient, nil } // NeConfOAMReadSync 网元OAM配置文件读取 func (r NeInfo) NeConfOAMReadSync(coreUid, neUid string) (map[string]any, error) { neInfo := r.FindByCoreUidAndNeUid(coreUid, neUid) if neInfo.CoreUID != coreUid || neInfo.NeUID != neUid { logger.Errorf("NeRunRedisClient CoreUid:%s NeUid:%s not found", coreUid, neUid) return nil, fmt.Errorf("neinfo not found") } oamData, err := r.neConfOAMRead(neInfo, true) if err != nil { return nil, err } // UPF和SMF 全小写的key // 网元HTTP服务 if v, ok := oamData["httpmanagecfg"]; ok && v != nil { item := v.(map[string]any) if v, ok := item["iptype"]; ok && v != nil { item["ipType"] = v delete(item, "iptype") } oamData["httpManageCfg"] = item delete(oamData, "httpmanagecfg") r.neConfOAMWirte(neInfo, oamData, false) } // 对网管HTTP配置 if v, ok := oamData["oamconfig"]; ok && v != nil { item := v.(map[string]any) if v, ok := item["iptype"]; ok && v != nil { item["ipType"] = v delete(item, "iptype") } if v, ok := item["neconfig"]; ok && v != nil { item["neConfig"] = v delete(item, "neconfig") } oamData["oamConfig"] = item delete(oamData, "oamconfig") r.neConfOAMWirte(neInfo, oamData, false) } // 对网管SNMP配置 if v, ok := oamData["snmpconfig"]; ok && v != nil { item := v.(map[string]any) if v, ok := item["iptype"]; ok && v != nil { item["ipType"] = v delete(item, "iptype") } oamData["snmpConfig"] = item delete(oamData, "snmpconfig") r.neConfOAMWirte(neInfo, oamData, false) } // 对网管KPI上报配置 if v, ok := oamData["kpiconfig"]; ok && v != nil { item := v.(map[string]any) oamData["kpiConfig"] = item delete(oamData, "kpiconfig") r.neConfOAMWirte(neInfo, oamData, false) } // NSSF和MME 配置KPIconfig名不一致时 if v, ok := oamData["KPIconfig"]; ok && v != nil { item := v.(map[string]any) oamData["kpiConfig"] = item delete(oamData, "KPIconfig") r.neConfOAMWirte(neInfo, oamData, false) } return oamData, nil } // neConfOAMData 网元OAM配置文件默认格式数据 func (r NeInfo) neConfOAMData() map[string]any { return map[string]any{ "httpManageCfg": map[string]any{ "ipType": "ipv4", "ipv4": "172.16.5.1", // 必改 "ipv6": "", "port": 33030, "scheme": "http", }, "oamConfig": map[string]any{ "enable": true, "ipType": "ipv4", "ipv4": "172.16.5.100", // 必改 "ipv6": "", "port": 33030, "scheme": "http", // 必改 "neConfig": map[string]any{ "neId": "001", "rmUid": "4400HX1XXX001", "neName": "XXX_001", "dn": "-", "vendorName": "GD", "province": "-", "pvFlag": "PNF", }, }, "snmpConfig": map[string]any{ "enable": false, "ipType": "ipv4", "ipv4": "172.16.5.1", // 必改 "ipv6": "", "port": 4957, }, "kpiConfig": map[string]any{ "enable": true, "timer": 60, // 必改 }, // "pubConfigPath": "/usr/local/etc/conf/para5G.yaml", // 网元只会读一次后续会置空,建议不放 } } // neConfOAMRead 网元OAM配置文件读取 sync从网元端同步到本地 func (r NeInfo) neConfOAMRead(neInfo model.NeInfo, sync bool) (map[string]any, error) { neTypeLower := strings.ToLower(neInfo.NeType) fileName := "oam_manager.yaml" // 网管本地路径 localFilePath := fmt.Sprintf("/usr/local/omc/backup/ne_config/%s/%s/%s", neTypeLower, neInfo.NeUID, fileName) if runtime.GOOS == "windows" { localFilePath = fmt.Sprintf("C:%s", localFilePath) } // 从网元端同步到本地 if sync { // 网元主机的SSH客户端 sshClient, err := r.NeRunSSHClient(neInfo.CoreUID, neInfo.NeUID) if err != nil { return nil, fmt.Errorf("ne info ssh client err") } defer sshClient.Close() // 网元主机的SSH客户端进行文件传输 sftpClient, err := sshClient.NewClientSFTP() if err != nil { return nil, fmt.Errorf("ne info sftp client err") } defer sftpClient.Close() // 网元端文件路径 neFilePath := fmt.Sprintf("/usr/local/etc/%s/%s", neTypeLower, fileName) // 修改网元文件权限 sshClient.RunCMD(fmt.Sprintf("sudo touch %s && sudo chmod o+rw %s", neFilePath, neFilePath)) // 网元端复制到本地 if err = sftpClient.CopyFileRemoteToLocal(neFilePath, localFilePath); err != nil { return nil, fmt.Errorf("copy oam config err") } } // 读取文件内容 bytes, err := os.ReadFile(localFilePath) if err != nil || len(bytes) == 0 { // logger.Warnf("NeConfOAMRead ReadFile => %s", err.Error()) // return nil, fmt.Errorf("read file error") // 无保留文件时返回默认文件数据 oamData := r.neConfOAMData() r.neConfOAMWirte(neInfo, oamData, false) return oamData, nil } content := string(bytes) // 序列化Map mapData, err := parse.ConvertConfigToMap("yaml", content) if err != nil { logger.Warnf("NeConfOAMRead ConvertConfigToMap => %s", err.Error()) return nil, fmt.Errorf("content convert type error") } return mapData, nil } // neConfOAMWirte 网元OAM配置文件写入 content内容 sync同步到网元端 func (r NeInfo) neConfOAMWirte(neInfo model.NeInfo, content any, sync bool) error { neTypeLower := strings.ToLower(neInfo.NeType) fileName := "oam_manager.yaml" // 网管本地路径 omcPath := "/usr/local/omc/backup/ne_config" if runtime.GOOS == "windows" { omcPath = fmt.Sprintf("C:%s", omcPath) } localFilePath := fmt.Sprintf("%s/%s/%s/%s", omcPath, neTypeLower, neInfo.NeUID, fileName) // 写入文件 if err := parse.ConvertConfigToFile("yaml", localFilePath, content); err != nil { return fmt.Errorf("please check if the file exists or write permissions") } // 同步到网元端 if sync { // 网元主机的SSH客户端 sshClient, err := r.NeRunSSHClient(neInfo.CoreUID, neInfo.NeUID) 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, fileName) neFileDir := filepath.ToSlash(filepath.Dir(neFilePath)) // 修改网元文件权限 sshClient.RunCMD(fmt.Sprintf("sudo mkdir -p %s && sudo chmod 775 %s && sudo touch %s && sudo chmod o+rw %s", neFileDir, neFileDir, neFilePath, neFilePath)) // 复制到网元进行覆盖 if err = sftpClient.CopyFileLocalToRemote(localFilePath, neFilePath); err != nil { return fmt.Errorf("please check if scp remote copy is allowed") } } return nil } // NeConfOAMWirteSync 网元OAM配置文件生成并同步 func (r NeInfo) NeConfOAMWirteSync(neInfo model.NeInfo, content map[string]any, sync bool) error { oamData, err := r.neConfOAMRead(neInfo, false) if oamData == nil || err != nil { return fmt.Errorf("error read OAM file info") } // 网元HTTP服务 httpManageCfg, ok := oamData["httpManageCfg"].(map[string]any) if !ok { neConfOAMData := r.neConfOAMData() httpManageCfg = neConfOAMData["httpManageCfg"].(map[string]any) } httpManageCfg["port"] = neInfo.Port if strings.Contains(neInfo.IPAddr, ":") { httpManageCfg["ipType"] = "ipv6" httpManageCfg["ipv6"] = neInfo.IPAddr } if strings.Contains(neInfo.IPAddr, ".") { httpManageCfg["ipType"] = "ipv4" httpManageCfg["ipv4"] = neInfo.IPAddr } delete(httpManageCfg, "iptype") delete(oamData, "httpmanagecfg") oamData["httpManageCfg"] = httpManageCfg // 对网管HTTP配置 oamConfig, ok := oamData["oamConfig"].(map[string]any) if !ok { neConfOAMData := r.neConfOAMData() oamConfig = neConfOAMData["oamConfig"].(map[string]any) } delete(oamConfig, "neconfig") oamConfig["neConfig"] = map[string]string{ "coreId": neInfo.CoreUID, "neUid": neInfo.NeUID, "neName": neInfo.NeName, "dn": neInfo.Dn, "vendorName": neInfo.VendorName, "province": neInfo.Province, "pvFlag": neInfo.PvFlag, } // 公共参数指定的OMC if omcIP, ok := r.Para5GData["OMC_IP"]; ok && omcIP != "" { if strings.Contains(omcIP, ":") { oamConfig["ipType"] = "ipv6" oamConfig["ipv6"] = omcIP } if strings.Contains(omcIP, ".") { oamConfig["ipType"] = "ipv4" oamConfig["ipv4"] = omcIP } } // 传入的变更 if v, ok := content["omcIP"]; ok && v != "" && v != nil { omcIP := v.(string) if strings.Contains(omcIP, ":") { oamConfig["ipType"] = "ipv6" oamConfig["ipv6"] = omcIP } if strings.Contains(omcIP, ".") { oamConfig["ipType"] = "ipv4" oamConfig["ipv4"] = omcIP } } delete(oamConfig, "iptype") if oamEnable, ok := content["oamEnable"]; ok && oamEnable != nil { oamConfig["enable"] = parse.Boolean(oamEnable) } if oamPort, ok := content["oamPort"]; ok && oamPort != nil { oamConfig["port"] = parse.Number(oamPort) } delete(oamData, "oamconfig") oamData["oamConfig"] = oamConfig // 对网管SNMP配置 snmpConfig, ok := oamData["snmpConfig"].(map[string]any) if !ok { neConfOAMData := r.neConfOAMData() snmpConfig = neConfOAMData["snmpConfig"].(map[string]any) } if strings.Contains(neInfo.IPAddr, ":") { snmpConfig["ipType"] = "ipv6" snmpConfig["ipv6"] = neInfo.IPAddr } if strings.Contains(neInfo.IPAddr, ".") { snmpConfig["ipType"] = "ipv4" snmpConfig["ipv4"] = neInfo.IPAddr } delete(snmpConfig, "iptype") if snmpEnable, ok := content["snmpEnable"]; ok && snmpEnable != nil { snmpConfig["enable"] = parse.Boolean(snmpEnable) } if snmpPort, ok := content["snmpPort"]; ok && snmpPort != nil { snmpConfig["port"] = parse.Number(snmpPort) } delete(oamData, "snmpconfig") oamData["snmpConfig"] = snmpConfig // 对网管KPI上报配置 kpiConfig, ok := oamData["kpiConfig"].(map[string]any) if !ok { neConfOAMData := r.neConfOAMData() kpiConfig = neConfOAMData["kpiConfig"].(map[string]any) } if neInfo.NeType == "UPF" { kpiConfig["timer"] = 5 } else { kpiConfig["timer"] = 60 } if kpiEnable, ok := content["kpiEnable"]; ok && kpiEnable != nil { kpiConfig["enable"] = parse.Boolean(kpiEnable) } if kpiTimer, ok := content["kpiTimer"]; ok && kpiTimer != nil { kpiConfig["timer"] = parse.Number(kpiTimer) } delete(oamData, "kpiconfig") oamData["kpiConfig"] = kpiConfig if err := r.neConfOAMWirte(neInfo, oamData, sync); err != nil { return fmt.Errorf("error wirte OAM file info") } return nil } // NeConfPara5GRead 网元公共配置文件读取 func (r NeInfo) NeConfPara5GRead() (map[string]any, error) { // 网管本地路径 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 nil, fmt.Errorf("read file error") } content := string(bytes) // 序列化Map mapData, err := parse.ConvertConfigToMap("yaml", content) if err != nil { logger.Warnf("NeConfPara5GRead ConvertConfigToMap => %s", err.Error()) return nil, fmt.Errorf("content convert type error") } return mapData, nil } // NeConfPara5GWirte 网元公共配置文件写入 content内容 syncNE同步到网元端NeType@NeId func (r *NeInfo) NeConfPara5GWirte(content map[string]any, syncNE []string) error { // 网管本地路径 omcFilePath := "/usr/local/etc/omc/para5G.yaml" if runtime.GOOS == "windows" { omcFilePath = fmt.Sprintf("C:%s", omcFilePath) } if err := parse.ConvertConfigToFile("yaml", omcFilePath, content); 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("core_ne %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.ToSlash(filepath.Dir(neFilePath)) // 修改网元文件权限 sshClient.RunCMD(fmt.Sprintf("sudo mkdir -p %s && sudo chmod 775 %s && sudo touch %s && sudo chmod o+rw %s", neFileDir, neFileDir, neFilePath, 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("%s", strings.Join(errMsg, "\r\n")) } } // 转换一份数据到全局 r.Para5GData = r.neConfPara5GDataConvert(content) return nil } // NeConfPara5GConvert 网元公共配置数据转化 content网元公共配置文件读取内容 func (r NeInfo) neConfPara5GDataConvert(content map[string]any) map[string]string { defer func() { if err := recover(); err != nil { logger.Errorf("NeConfPara5GDataConvert panic: %v", err) // 文件异常就删除配置 omcFilePath := "/usr/local/etc/omc/para5G.yaml" if runtime.GOOS == "windows" { omcFilePath = fmt.Sprintf("C:%s", omcFilePath) } os.Remove(omcFilePath) } }() basic := content["basic"].(map[string]any) external := content["external"].(map[string]any) sbi := content["sbi"].(map[string]any) mcc := "460" mnc := "01" mncDomain := "001" if plmnId, plmnIdOk := basic["plmnId"].(map[string]any); plmnIdOk { mcc = plmnId["mcc"].(string) mnc = plmnId["mnc"].(string) // If a user input two digit MNC, add a leading zero if len(mnc) == 2 { mncDomain = fmt.Sprintf("0%s", mnc) } else { mncDomain = mnc } } sst := "1" sd := "000001" if plmnId, plmnIdOk := basic["snssai"].(map[string]any); plmnIdOk { sst = plmnId["sst"].(string) sd = plmnId["sd"].(string) } n3IPAmdMask := external["upfn3_ip"].(string) n3Arr := strings.SplitN(n3IPAmdMask, "/", 2) n3IP := n3Arr[0] n3Mask := "255.255.255.0" if len(n3Arr) > 1 { n3Mask = parse.ConvertIPMask(parse.Number(n3Arr[1])) } n6IPAmdMask := external["upfn6_ip"].(string) n6Arr := strings.SplitN(n6IPAmdMask, "/", 2) n6IP := n6Arr[0] n6Mask := "255.255.255.0" if len(n6Arr) > 1 { n6Mask = parse.ConvertIPMask(parse.Number(n6Arr[1])) } ueIPAmdMask := external["ue_pool"].(string) ueArr := strings.SplitN(ueIPAmdMask, "/", 2) ueIP := ueArr[0] ueCicr := "24" ueMask := "255.255.255.0" if len(ueArr) > 1 { ueCicr = ueArr[1] ueMask = parse.ConvertIPMask(parse.Number(ueArr[1])) } return map[string]string{ // basic "TAC": basic["tac"].(string), "MCC": mcc, "MNC": mnc, "MNC_DOMAIN": mncDomain, "SST": sst, "SD": sd, "DNN_DATA": basic["dnn_data"].(string), "DNN_IMS": basic["dnn_ims"].(string), // external "N2_IP": external["amfn2_ip"].(string), "UE_POOL": external["ue_pool"].(string), // 轻量版才用配置 "UE_IP": ueIP, "UE_MASK": ueMask, "UE_CIDR": ueCicr, "UPF_TYPE": external["upf_type"].(string), // 类型 StandardUPF LightUPF "UPF_DRIVER_TYPE": external["upf_driver_type"].(string), // 网卡驱动 vmxnet3 host dpdk "N3_IP": n3IP, "N3_MASK": n3Mask, "N3_GW": external["upfn3_gw"].(string), "N3_PCI": external["upfn3_pci"].(string), "N3_MAC": external["upfn3_mac"].(string), "N3_NIC_NAME": external["upfn3_card_name"].(string), // 网卡名 eth0 "N6_IP": n6IP, "N6_MASK": n6Mask, "N6_GW": external["upfn6_gw"].(string), "N6_PCI": external["upfn6_pci"].(string), "N6_MAC": external["upfn6_mac"].(string), "N6_NIC_NAME": external["upfn6_card_name"].(string), // 网卡名 eth0 "SIP_IP": external["ims_sip_ip"].(string), "S1_MMEIP": external["mmes1_ip"].(string), "S11_MMEIP": external["mmes11_ip"].(string), "S10_MMEIP": external["mmes10_ip"].(string), // sbi "OMC_IP": sbi["omc_ip"].(string), "IMS_IP": sbi["ims_ip"].(string), "AMF_IP": sbi["amf_ip"].(string), "AUSF_IP": sbi["ausf_ip"].(string), "UDM_IP": sbi["udm_ip"].(string), "SMF_IP": sbi["smf_ip"].(string), "PCF_IP": sbi["pcf_ip"].(string), "NSSF_IP": sbi["nssf_ip"].(string), "NRF_IP": sbi["nrf_ip"].(string), "UPF_IP": sbi["upf_ip"].(string), "LMF_IP": sbi["lmf_ip"].(string), "NEF_IP": sbi["nef_ip"].(string), "MME_IP": sbi["mme_ip"].(string), "N3IWF_IP": sbi["n3iwf_ip"].(string), "SMSC_IP": sbi["smsc_ip"].(string), "DB_IP": sbi["db_ip"].(string), } }