From e98562783e104879582520fa42d0f77cff85fcc2 Mon Sep 17 00:00:00 2001 From: TsMask <340112800@qq.com> Date: Sat, 16 Sep 2023 14:51:26 +0800 Subject: [PATCH 1/2] =?UTF-8?q?fix=20=E8=BF=9C=E7=A8=8B=E7=BD=91=E5=85=83s?= =?UTF-8?q?cp=E5=A4=8D=E5=88=B6=E6=96=87=E4=BB=B6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- features/udm_user/api_udm_user.go | 28 ++++++++++++------------ lib/core/file/ssh.go | 36 ++++++++++++++++++++++++++----- 2 files changed, 45 insertions(+), 19 deletions(-) diff --git a/features/udm_user/api_udm_user.go b/features/udm_user/api_udm_user.go index 4c5cb93f..cc96b39b 100644 --- a/features/udm_user/api_udm_user.go +++ b/features/udm_user/api_udm_user.go @@ -561,23 +561,23 @@ func (s *UdmUserApi) UdmAuthUserImport(w http.ResponseWriter, r *http.Request) { // 文件名 fileName := fmt.Sprintf("OMC_AUTH_USER_IMPORT_%s_%d_%s", neId, time.Now().UnixMilli(), fileHeader.Filename) - filePath := fmt.Sprintf("%s/upload/mml/%s", conf.Get("ne.omcdir"), fileName) - dstPath := conf.Get("mml.upload").(string) + localPath := fmt.Sprintf("%s/upload/mml/%s", conf.Get("ne.omcdir"), fileName) + nePath := conf.Get("mml.upload").(string) // 输出保存文件 - err = ctx.SaveUploadedFile(r, filePath) + err = ctx.SaveUploadedFile(r, localPath) if err != nil { ctx.JSON(w, 200, result.ErrMsg(err.Error())) return } // 复制到远程 - err = file.FileNeSCP(neInfo.Ip, filePath, dstPath) + err = file.FileSCPLocalToNe(neInfo.Ip, localPath, nePath) if err != nil { ctx.JSON(w, 200, result.ErrMsg(err.Error())) return } - msg := fmt.Sprintf("import authdat:path=%s", fmt.Sprintf("%s/%s", dstPath, fileName)) + msg := fmt.Sprintf("import authdat:path=%s", fmt.Sprintf("%s/%s", nePath, fileName)) // 发送MML data, err := mmlclient.MMLSendMsgToString(neInfo.Ip, msg) @@ -589,12 +589,12 @@ func (s *UdmUserApi) UdmAuthUserImport(w http.ResponseWriter, r *http.Request) { // 命令ok时 if strings.Contains(data, "ok") { if strings.HasSuffix(fileHeader.Filename, ".csv") { - data := file.ReadCSVFile(filePath) + data := file.ReadCSVFile(localPath) neId = "-" s.authUser.InsertCSV(neId, data) } if strings.HasSuffix(fileHeader.Filename, ".txt") { - data := file.ReadTxtFile(filePath) + data := file.ReadTxtFile(localPath) neId = "-" s.authUser.InsertTxt(neId, data) } @@ -1125,23 +1125,23 @@ func (s *UdmUserApi) UdmSubUserImport(w http.ResponseWriter, r *http.Request) { // 文件名 fileName := fmt.Sprintf("OMC_SUB_USER_IMPORT_%s_%d_%s", neId, time.Now().UnixMilli(), fileHeader.Filename) - filePath := fmt.Sprintf("%s/upload/mml/%s", conf.Get("ne.omcdir"), fileName) - dstPath := conf.Get("mml.upload").(string) + localPath := fmt.Sprintf("%s/upload/mml/%s", conf.Get("ne.omcdir"), fileName) + nePath := conf.Get("mml.upload").(string) // 输出保存文件 - err = ctx.SaveUploadedFile(r, filePath) + err = ctx.SaveUploadedFile(r, localPath) if err != nil { ctx.JSON(w, 200, result.ErrMsg(err.Error())) return } // 复制到远程 - err = file.FileNeSCP(neInfo.Ip, filePath, dstPath) + err = file.FileSCPLocalToNe(neInfo.Ip, localPath, nePath) if err != nil { ctx.JSON(w, 200, result.ErrMsg(err.Error())) return } - msg := fmt.Sprintf("import udmuser:path=%s", fmt.Sprintf("%s/%s", dstPath, fileName)) + msg := fmt.Sprintf("import udmuser:path=%s", fmt.Sprintf("%s/%s", nePath, fileName)) // 发送MML data, err := mmlclient.MMLSendMsgToString(neInfo.Ip, msg) @@ -1152,12 +1152,12 @@ func (s *UdmUserApi) UdmSubUserImport(w http.ResponseWriter, r *http.Request) { // 命令ok时 if strings.Contains(data, "ok") { if strings.HasSuffix(fileHeader.Filename, ".csv") { - data := file.ReadCSVFile(filePath) + data := file.ReadCSVFile(localPath) neId = "-" s.subUser.InsertCSV(neId, data) } if strings.HasSuffix(fileHeader.Filename, ".txt") { - data := file.ReadTxtFile(filePath) + data := file.ReadTxtFile(localPath) neId = "-" s.subUser.InsertTxt(neId, data) } diff --git a/lib/core/file/ssh.go b/lib/core/file/ssh.go index 07e4d7c2..c33a717a 100644 --- a/lib/core/file/ssh.go +++ b/lib/core/file/ssh.go @@ -2,22 +2,48 @@ package file import ( "fmt" + "os" "os/exec" + "path/filepath" "ems.agt/lib/core/conf" "ems.agt/lib/log" ) -// 网元NE 文件复制到远程文件夹 -func FileNeSCP(neIp, filePath, dstPath string) error { +// 网元NE 文件复制到远程文件 +func FileSCPLocalToNe(neIp, localPath, nePath string) error { usernameNe := conf.Get("ne.user").(string) // scp /path/to/local/file.txt user@remote-server:/path/to/remote/directory/ - dstDir := fmt.Sprintf("%s@%s:%s", usernameNe, neIp, dstPath) - cmd := exec.Command("scp", "-r", filePath, dstDir) + neDir := fmt.Sprintf("%s@%s:%s", usernameNe, neIp, nePath) + cmd := exec.Command("scp", "-r", localPath, neDir) out, err := cmd.CombinedOutput() if err != nil { return err } - log.Infof("FileNeSCP %s", string(out)) + log.Infof("FileSCPLocalToNe %s", string(out)) + return nil +} + +// 网元NE 远程文件复制到本地文件 +func FileSCPNeToLocal(neIp, nePath, localPath string) error { + // 获取文件所在的目录路径 + dirPath := filepath.Dir(localPath) + + // 确保文件夹路径存在 + err := os.MkdirAll(dirPath, os.ModePerm) + if err != nil { + log.Errorf("创建文件夹失败 CreateFile %v", err) + return err + } + + usernameNe := conf.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) + out, err := cmd.CombinedOutput() + if err != nil { + return err + } + log.Infof("FileSCPNeToLocal %s", string(out)) return nil } From a1e0cf47b6abaf5ebd1d78a9cf272ad48a442298 Mon Sep 17 00:00:00 2001 From: TsMask <340112800@qq.com> Date: Sat, 16 Sep 2023 14:51:56 +0800 Subject: [PATCH 2/2] =?UTF-8?q?feat:=20PCAP=E6=8D=95=E8=8E=B7?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- features/trace/tcpdump.go | 104 ++++++++++++++++++++++++++++++++++++++ lib/routes/routes.go | 7 +++ 2 files changed, 111 insertions(+) create mode 100644 features/trace/tcpdump.go diff --git a/features/trace/tcpdump.go b/features/trace/tcpdump.go new file mode 100644 index 00000000..8d95a2ed --- /dev/null +++ b/features/trace/tcpdump.go @@ -0,0 +1,104 @@ +package trace + +import ( + "fmt" + "net/http" + "time" + + "ems.agt/lib/core/cmd" + "ems.agt/lib/core/conf" + "ems.agt/lib/core/file" + "ems.agt/lib/core/utils/ctx" + "ems.agt/lib/core/vo/result" + "ems.agt/lib/dborm" + "ems.agt/lib/log" + "ems.agt/restagent/config" +) + +var ( + UriTcpdumpTask = config.DefaultUriPrefix + "/traceManagement/{apiVersion}/tcpdumpNeTask" + CustomUriTcpdumpTask = config.UriPrefix + "/traceManagement/{apiVersion}/tcpdumpNeTask" // decode message api + + UriTcpdumpPcapDownload = config.DefaultUriPrefix + "/traceManagement/{apiVersion}/tcpdumpPcapDownload" + CustomUriTcpdumpPcapDownload = config.UriPrefix + "/traceManagement/{apiVersion}/tcpdumpPcapDownload" // decode message api +) + +// NeInfo 网元信息 +func NeInfo(neType, neId string) (*dborm.NeInfo, error) { + neInfo, err := dborm.XormGetNeInfo(neType, neId) + if err != nil { + log.Error("dborm.XormGetNeInfo is failed:", err) + return nil, err + } + if neInfo == nil || neInfo.Ip == "" { + return nil, fmt.Errorf("not ne_info or not IP") + } + return neInfo, nil +} + +// TcpdumpNeTask 网元发送执行 pcap +func TcpdumpNeTask(w http.ResponseWriter, r *http.Request) { + var body struct { + NeType string `json:"neType"` // 网元类型 + NeId string `json:"neId"` // 网元ID + Timeout int `json:"timeout"` // 超时时间 + Cmd string `json:"cmd"` // 命令 + } + err := ctx.ShouldBindJSON(r, &body) + if err != nil || body.NeType == "" || body.NeId == "" || body.Timeout < 5 || body.Cmd == "" { + ctx.JSON(w, 400, result.CodeMsg(400, "参数错误")) + return + } + + neInfo, err := NeInfo(body.NeType, body.NeId) + if err != nil { + ctx.JSON(w, 200, result.ErrMsg(err.Error())) + return + } + + filePcapName := fmt.Sprintf("tmp_%s_%s_%d.pcap", body.NeType, body.NeId, time.Now().UnixMilli()) + fileLogName := fmt.Sprintf("tmp_%s_%s_%d.log", body.NeType, body.NeId, time.Now().UnixMilli()) + cmdStr := fmt.Sprintf("cd /tmp \n timeout %d tcpdump -i any %s -s0 -w %s >> %s 2>&1 \n cat %s", body.Timeout, body.Cmd, filePcapName, fileLogName, fileLogName) + usernameNe := conf.Get("ne.user").(string) // 网元统一用户 + sshHost := fmt.Sprintf("%s@%s", usernameNe, neInfo.Ip) + msg, err := cmd.ExecWithCheck("ssh", sshHost, cmdStr) + if err != nil { + ctx.JSON(w, 200, result.ErrMsg(err.Error())) + return + } + + ctx.JSON(w, 200, result.OkData(map[string]any{ + "msg": msg, + "fileName": filePcapName, + })) +} + +// TcpdumpPcapDownload 网元抓包pcap文件下载 +func TcpdumpPcapDownload(w http.ResponseWriter, r *http.Request) { + var body struct { + NeType string `json:"neType"` // 网元类型 + NeId string `json:"neId"` // 网元ID + FileName string `json:"fileName"` // 文件名 + } + err := ctx.ShouldBindJSON(r, &body) + if err != nil || body.NeType == "" || body.NeId == "" || body.FileName == "" { + ctx.JSON(w, 400, result.CodeMsg(400, "参数错误")) + return + } + + neInfo, err := NeInfo(body.NeType, body.NeId) + if err != nil { + ctx.JSON(w, 200, result.ErrMsg(err.Error())) + return + } + + nePath := fmt.Sprintf("/tmp/%s", body.FileName) + localPath := fmt.Sprintf("%s/tcpdump/pcap/%s", conf.Get("ne.omcdir"), body.FileName) + err = file.FileSCPNeToLocal(neInfo.Ip, nePath, localPath) + if err != nil { + ctx.JSON(w, 200, result.ErrMsg(err.Error())) + return + } + + ctx.FileAttachment(w, r, localPath, body.FileName) +} diff --git a/lib/routes/routes.go b/lib/routes/routes.go index 19101921..b81905c6 100644 --- a/lib/routes/routes.go +++ b/lib/routes/routes.go @@ -239,6 +239,13 @@ func init() { Register("PUT", trace.CustomUriTraceTask, trace.PutTraceTaskToNF, nil) Register("DELETE", trace.CustomUriTraceTask, trace.DeleteTraceTaskToNF, nil) + // 网元发送执行 pcap抓包 + Register("POST", trace.UriTcpdumpTask, trace.TcpdumpNeTask, nil) + Register("POST", trace.CustomUriTcpdumpTask, trace.TcpdumpNeTask, nil) + // 网元发送执行 抓包下载pcap文件 + Register("POST", trace.UriTcpdumpPcapDownload, trace.TcpdumpPcapDownload, nil) + Register("POST", trace.CustomUriTcpdumpPcapDownload, trace.TcpdumpPcapDownload, nil) + // file management Register("POST", file.UriFile, file.UploadFile, nil) Register("GET", file.UriFile, file.DownloadFile, nil)