feat: 跟踪抓包重构
This commit is contained in:
@@ -1,9 +1,13 @@
|
|||||||
package controller
|
package controller
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"os"
|
||||||
|
"path/filepath"
|
||||||
|
|
||||||
"nms_cxy/src/framework/i18n"
|
"nms_cxy/src/framework/i18n"
|
||||||
"nms_cxy/src/framework/utils/ctx"
|
"nms_cxy/src/framework/utils/ctx"
|
||||||
"nms_cxy/src/framework/vo/result"
|
"nms_cxy/src/framework/vo/result"
|
||||||
|
neService "nms_cxy/src/modules/network_element/service"
|
||||||
traceService "nms_cxy/src/modules/trace/service"
|
traceService "nms_cxy/src/modules/trace/service"
|
||||||
|
|
||||||
"github.com/gin-gonic/gin"
|
"github.com/gin-gonic/gin"
|
||||||
@@ -13,6 +17,7 @@ import (
|
|||||||
// 实例化控制层 TcpdumpController 结构体
|
// 实例化控制层 TcpdumpController 结构体
|
||||||
var NewTcpdump = &TcpdumpController{
|
var NewTcpdump = &TcpdumpController{
|
||||||
TcpdumpService: traceService.NewTcpdumpImpl,
|
TcpdumpService: traceService.NewTcpdumpImpl,
|
||||||
|
neInfoService: neService.NewNeInfoImpl,
|
||||||
}
|
}
|
||||||
|
|
||||||
// 信令抓包请求
|
// 信令抓包请求
|
||||||
@@ -21,6 +26,8 @@ var NewTcpdump = &TcpdumpController{
|
|||||||
type TcpdumpController struct {
|
type TcpdumpController struct {
|
||||||
// 信令抓包服务
|
// 信令抓包服务
|
||||||
TcpdumpService traceService.ITcpdump
|
TcpdumpService traceService.ITcpdump
|
||||||
|
// 网元信息服务
|
||||||
|
neInfoService neService.INeInfo
|
||||||
}
|
}
|
||||||
|
|
||||||
// 网元抓包PACP 开始
|
// 网元抓包PACP 开始
|
||||||
@@ -31,7 +38,7 @@ func (s *TcpdumpController) DumpStart(c *gin.Context) {
|
|||||||
var body struct {
|
var body struct {
|
||||||
NeType string `json:"neType" binding:"required"` // 网元类型
|
NeType string `json:"neType" binding:"required"` // 网元类型
|
||||||
NeId string `json:"neId" binding:"required"` // 网元ID
|
NeId string `json:"neId" binding:"required"` // 网元ID
|
||||||
Cmd string `json:"cmd" binding:"required"` // 命令 "-n -s 0 -v -w"
|
Cmd string `json:"cmd" binding:"required"` // 命令 "-n -s 0 -v"
|
||||||
}
|
}
|
||||||
err := c.ShouldBindBodyWith(&body, binding.JSON)
|
err := c.ShouldBindBodyWith(&body, binding.JSON)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -39,21 +46,12 @@ func (s *TcpdumpController) DumpStart(c *gin.Context) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
fileName, err := s.TcpdumpService.DumpStart(body.NeType, body.NeId, body.Cmd)
|
taskCode, err := s.TcpdumpService.DumpStart(body.NeType, body.NeId, body.Cmd)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
msg := err.Error()
|
c.JSON(200, result.ErrMsg(i18n.TKey(language, err.Error())))
|
||||||
if msg == "noData" {
|
|
||||||
// 找不到 %s %s 对应网元信息
|
|
||||||
msg = i18n.TTemplate(language, "trace.tcpdump.noData", map[string]any{"type": body.NeType, "id": body.NeId})
|
|
||||||
}
|
|
||||||
c.JSON(200, result.ErrMsg(msg))
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
c.JSON(200, result.OkData(map[string]any{
|
c.JSON(200, result.OkData(taskCode))
|
||||||
"msg": "tcpdump started",
|
|
||||||
"out": fileName,
|
|
||||||
"log": "",
|
|
||||||
}))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// 网元抓包PACP 结束
|
// 网元抓包PACP 结束
|
||||||
@@ -62,9 +60,9 @@ func (s *TcpdumpController) DumpStart(c *gin.Context) {
|
|||||||
func (s *TcpdumpController) DumpStop(c *gin.Context) {
|
func (s *TcpdumpController) DumpStop(c *gin.Context) {
|
||||||
language := ctx.AcceptLanguage(c)
|
language := ctx.AcceptLanguage(c)
|
||||||
var body struct {
|
var body struct {
|
||||||
NeType string `json:"neType" binding:"required"` // 网元类型
|
NeType string `json:"neType" binding:"required"` // 网元类型
|
||||||
NeId string `json:"neId" binding:"required"` // 网元ID
|
NeId string `json:"neId" binding:"required"` // 网元ID
|
||||||
FileName string `json:"fileName"` // 文件名 查看日志信息
|
TaskCode string `json:"taskCode" binding:"required"` // 任务码,停止任务并查看日志信息
|
||||||
}
|
}
|
||||||
err := c.ShouldBindBodyWith(&body, binding.JSON)
|
err := c.ShouldBindBodyWith(&body, binding.JSON)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -72,29 +70,48 @@ func (s *TcpdumpController) DumpStop(c *gin.Context) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
logMsg, err := s.TcpdumpService.DumpStop(body.NeType, body.NeId, body.FileName)
|
taskLog, err := s.TcpdumpService.DumpStop(body.NeType, body.NeId, body.TaskCode)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
msg := err.Error()
|
c.JSON(200, result.ErrMsg(i18n.TKey(language, err.Error())))
|
||||||
if msg == "noData" {
|
|
||||||
// 找不到 %s %s 对应网元信息
|
|
||||||
msg := i18n.TTemplate(language, "trace.tcpdump.noData", map[string]any{"type": body.NeType, "id": body.NeId})
|
|
||||||
c.JSON(200, result.ErrMsg(msg))
|
|
||||||
return
|
|
||||||
}
|
|
||||||
c.JSON(200, result.ErrMsg(msg))
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
c.JSON(200, result.OkData(map[string]any{
|
c.JSON(200, result.OkMsg(taskLog))
|
||||||
"msg": "tcpdump stopped",
|
}
|
||||||
"out": body.FileName,
|
|
||||||
"log": logMsg,
|
// 网元抓包PACP 下载
|
||||||
}))
|
//
|
||||||
|
// GET /download
|
||||||
|
func (s *TcpdumpController) DumpDownload(c *gin.Context) {
|
||||||
|
language := ctx.AcceptLanguage(c)
|
||||||
|
var query struct {
|
||||||
|
NeType string `form:"neType" binding:"required"` // 网元类型
|
||||||
|
NeID string `form:"neId" binding:"required"` // 网元ID
|
||||||
|
TaskCode string `form:"taskCode" binding:"required"` // 任务码,停止任务并查看日志信息
|
||||||
|
DelTemp bool `form:"delTemp"` // 完成后是否删除本地临时zip文件
|
||||||
|
}
|
||||||
|
if err := c.ShouldBindQuery(&query); err != nil {
|
||||||
|
c.JSON(400, result.CodeMsg(400, i18n.TKey(language, "app.common.err400")))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
zipFilePath, err := s.TcpdumpService.DumpDownload(query.NeType, query.NeID, query.TaskCode)
|
||||||
|
if err != nil {
|
||||||
|
c.JSON(200, result.ErrMsg(i18n.TKey(language, err.Error())))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
defer func() {
|
||||||
|
if query.DelTemp {
|
||||||
|
_ = os.Remove(zipFilePath)
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
c.FileAttachment(zipFilePath, filepath.Base(zipFilePath))
|
||||||
}
|
}
|
||||||
|
|
||||||
// UPF标准版内部抓包
|
// UPF标准版内部抓包
|
||||||
//
|
//
|
||||||
// POST /traceUPF
|
// POST /upf
|
||||||
func (s *TcpdumpController) TraceUPF(c *gin.Context) {
|
func (s *TcpdumpController) UPFTrace(c *gin.Context) {
|
||||||
language := ctx.AcceptLanguage(c)
|
language := ctx.AcceptLanguage(c)
|
||||||
var body struct {
|
var body struct {
|
||||||
NeType string `json:"neType" binding:"required"` // 网元类型
|
NeType string `json:"neType" binding:"required"` // 网元类型
|
||||||
@@ -107,19 +124,10 @@ func (s *TcpdumpController) TraceUPF(c *gin.Context) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
fileName, logMsg, err := s.TcpdumpService.DumpUPF(body.NeType, body.NeId, body.Cmd)
|
msg, err := s.TcpdumpService.UPFTrace(body.NeType, body.NeId, body.Cmd)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
msg := err.Error()
|
c.JSON(200, result.ErrMsg(i18n.TKey(language, err.Error())))
|
||||||
if msg == "noData" {
|
|
||||||
// 找不到 %s %s 对应网元信息
|
|
||||||
msg = i18n.TTemplate(language, "trace.tcpdump.noData", map[string]any{"type": body.NeType, "id": body.NeId})
|
|
||||||
}
|
|
||||||
c.JSON(200, result.ErrMsg(msg))
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
c.JSON(200, result.OkData(map[string]any{
|
c.JSON(200, result.OkData(msg))
|
||||||
"msg": "trace UPF dump pacp",
|
|
||||||
"out": fileName,
|
|
||||||
"log": logMsg,
|
|
||||||
}))
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,12 +2,15 @@ package service
|
|||||||
|
|
||||||
// 信令抓包 服务层接口
|
// 信令抓包 服务层接口
|
||||||
type ITcpdump interface {
|
type ITcpdump interface {
|
||||||
// DumpStart 触发tcpdump开始抓包 filePcapName, err
|
// DumpStart 触发tcpdump开始抓包
|
||||||
DumpStart(neType, neId, cmdStr string) (string, error)
|
DumpStart(neType, neId, cmdStr string) (string, error)
|
||||||
|
|
||||||
// DumpStop 停止已存在抓包句柄
|
// DumpStop 停止已存在抓包句柄
|
||||||
DumpStop(neType, neId, fileName string) (string, error)
|
DumpStop(neType, neId, taskCode string) (string, error)
|
||||||
|
|
||||||
// DumpUPF UPF标准版抓包
|
// DumpDownload 抓包文件网元端复制到本地输出zip文件
|
||||||
DumpUPF(neType, neId, cmdStr string) (string, string, error)
|
DumpDownload(neType, neId, taskCode string) (string, error)
|
||||||
|
|
||||||
|
// UPFTrace UPF标准版内部抓包
|
||||||
|
UPFTrace(neType, neId, cmdStr string) (string, error)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,36 +2,45 @@ package service
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"os"
|
||||||
|
"path/filepath"
|
||||||
|
"regexp"
|
||||||
|
"runtime"
|
||||||
"strings"
|
"strings"
|
||||||
|
"sync"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"nms_cxy/src/framework/logger"
|
"nms_cxy/src/framework/logger"
|
||||||
"nms_cxy/src/framework/utils/date"
|
"nms_cxy/src/framework/utils/file"
|
||||||
neService "nms_cxy/src/modules/network_element/service"
|
neService "nms_cxy/src/modules/network_element/service"
|
||||||
)
|
)
|
||||||
|
|
||||||
// 实例化服务层 TcpdumpImpl 结构体
|
// 实例化服务层 TcpdumpImpl 结构体
|
||||||
var NewTcpdumpImpl = &TcpdumpImpl{
|
var NewTcpdumpImpl = &TcpdumpImpl{
|
||||||
neInfoService: neService.NewNeInfoImpl,
|
neInfoService: neService.NewNeInfoImpl,
|
||||||
tcpdumpPIDMap: map[string]string{},
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// 信令抓包 服务层处理
|
// 信令抓包 服务层处理
|
||||||
type TcpdumpImpl struct {
|
type TcpdumpImpl struct {
|
||||||
// 网元信息服务
|
// 网元信息服务
|
||||||
neInfoService neService.INeInfo
|
neInfoService neService.INeInfo
|
||||||
// 抓包进程PID
|
|
||||||
tcpdumpPIDMap map[string]string
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// DumpStart 触发tcpdump开始抓包 filePcapName, err
|
// 抓包进程PID
|
||||||
|
var dumpPIDMap sync.Map
|
||||||
|
|
||||||
|
// DumpStart 触发tcpdump开始抓包
|
||||||
func (s *TcpdumpImpl) DumpStart(neType, neId, cmdStr string) (string, error) {
|
func (s *TcpdumpImpl) DumpStart(neType, neId, cmdStr string) (string, error) {
|
||||||
|
// 命令检查
|
||||||
|
if strings.Contains(cmdStr, "w") {
|
||||||
|
return "", fmt.Errorf("command cannot contain -w")
|
||||||
|
}
|
||||||
|
|
||||||
// 查询网元获取IP
|
// 查询网元获取IP
|
||||||
neInfo := s.neInfoService.SelectNeInfoByNeTypeAndNeID(neType, neId)
|
neInfo := s.neInfoService.SelectNeInfoByNeTypeAndNeID(neType, neId)
|
||||||
if neInfo.NeId != neId || neInfo.IP == "" {
|
if neInfo.NeId != neId || neInfo.IP == "" {
|
||||||
return "", fmt.Errorf("noData")
|
return "", fmt.Errorf("app.common.noNEInfo")
|
||||||
}
|
}
|
||||||
|
|
||||||
// 网元主机的SSH客户端
|
// 网元主机的SSH客户端
|
||||||
sshClient, err := s.neInfoService.NeRunSSHClient(neInfo.NeType, neInfo.NeId)
|
sshClient, err := s.neInfoService.NeRunSSHClient(neInfo.NeType, neInfo.NeId)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -39,47 +48,53 @@ func (s *TcpdumpImpl) DumpStart(neType, neId, cmdStr string) (string, error) {
|
|||||||
}
|
}
|
||||||
defer sshClient.Close()
|
defer sshClient.Close()
|
||||||
|
|
||||||
// 是否拥有sudo权限并拼接
|
// 检查是否安装tcpdump
|
||||||
withSudo := ""
|
if msg, err := sshClient.RunCMD("sudo tcpdump --version"); err != nil {
|
||||||
if _, err := sshClient.RunCMD("sudo -n uname"); err == nil {
|
// bash: tcpdump: command not found
|
||||||
withSudo = "sudo "
|
|
||||||
}
|
|
||||||
|
|
||||||
if msg, err := sshClient.RunCMD(fmt.Sprintf("%s tcpdump --version", withSudo)); err != nil {
|
|
||||||
// stderr: bash: tcpdump:未找到命令 => exit status 127
|
|
||||||
msg = strings.TrimSpace(msg)
|
msg = strings.TrimSpace(msg)
|
||||||
logger.Warnf("DumpStart err: %s => %s", msg, err.Error())
|
logger.Errorf("DumpStart err: %s => %s", msg, err.Error())
|
||||||
return "", fmt.Errorf(msg)
|
return "", fmt.Errorf(msg)
|
||||||
}
|
}
|
||||||
|
|
||||||
// 拼装命令
|
taskCode := time.Now().Format("20060102150405")
|
||||||
neTypeID := fmt.Sprintf("%s_%s", neInfo.NeType, neInfo.NeId)
|
// 存放文件目录 /tmp/omc/tcpdump/udm/001/20240817104241
|
||||||
timeStr := date.ParseDateToStr(time.Now(), date.YYYYMMDDHHMMSS)
|
neDirTemp := fmt.Sprintf("/tmp/omc/tcpdump/%s/%s/%s", strings.ToLower(neInfo.NeType), neInfo.NeId, taskCode)
|
||||||
fileName := fmt.Sprintf("%s_%s", timeStr, neTypeID)
|
sshClient.RunCMD(fmt.Sprintf("mkdir -p %s && sudo chmod 777 -R /tmp/omc", neDirTemp))
|
||||||
sendCmd := fmt.Sprintf("cd /tmp \n %s nohup timeout 30m tcpdump -i any %s -s0 -w %s.pcap > %s.log 2>&1 & \necho $!", withSudo, cmdStr, fileName, fileName)
|
|
||||||
// cd /tmp
|
// 命令拼装
|
||||||
// sudo nohup timeout 60m tcpdump -i any -n -s 0 -v -w -s0 -w 20240115140822_UDM_001.pcap > 20240115140822_UDM_001.log 2>&1 & echo $!
|
logPath := fmt.Sprintf("%s/tcpdump.log", neDirTemp)
|
||||||
msg, err := sshClient.RunCMD(sendCmd)
|
filePath := fmt.Sprintf("%s/part_%s.pcap ", neDirTemp, taskCode)
|
||||||
msg = strings.TrimSpace(msg)
|
if strings.Contains(cmdStr, "-G") {
|
||||||
if err != nil || strings.HasPrefix(msg, "stderr:") {
|
filePath = fmt.Sprintf("%s/part_%%Y%%m%%d%%H%%M%%S.pcap ", neDirTemp)
|
||||||
logger.Warnf("DumpStart err: %s => %s", msg, err.Error())
|
}
|
||||||
|
sendCmd := fmt.Sprintf("sudo timeout 60m sudo tcpdump -i any %s -w %s > %s 2>&1 & echo $!", cmdStr, filePath, logPath)
|
||||||
|
// sudo timeout 60m sudo tcpdump -i any -n -s 0 -v -G 60 -W 6 -w /tmp/omc/tcpdump/udm/001/20240817104241/part_%Y-%m-%d_%H:%M:%S.pcap > /tmp/omc/tcpdump/udm/001/20240817104241/tcpdump.log 2>&1 & echo $!
|
||||||
|
// sudo timeout 60m sudo tcpdump -i any -n -s 0 -v -w /tmp/omc/tcpdump/udm/001/20240817105440/part_2024-08-17_10:54:40.pcap > /tmp/omc/tcpdump/udm/001/20240817105440/tcpdump.log 2>&1 & echo $!
|
||||||
|
//
|
||||||
|
// timeout 超时60分钟后发送kill命令,1分钟后强制终止命令。tcpdump -G 文件轮转间隔时间(秒) -W 文件轮转保留最近数量
|
||||||
|
// sudo timeout --kill-after=1m 60m sudo tcpdump -i any -n -s 0 -v -G 10 -W 7 -w /tmp/part_%Y%m%d%H%M%S.pcap > /tmp/part.log 2>&1 & echo $!
|
||||||
|
// sudo kill $(pgrep -P 722729)
|
||||||
|
outputPID, err := sshClient.RunCMD(sendCmd)
|
||||||
|
outputPID = strings.TrimSpace(outputPID)
|
||||||
|
if err != nil || strings.HasPrefix(outputPID, "stderr:") {
|
||||||
|
logger.Errorf("DumpStart err: %s => %s", outputPID, err.Error())
|
||||||
return "", err
|
return "", err
|
||||||
}
|
}
|
||||||
|
|
||||||
// 检查进程 ps aux | grep tcpdump
|
// 检查进程 ps aux | grep tcpdump
|
||||||
// 强杀 sudo pkill tcpdump
|
// 强杀 sudo pkill tcpdump
|
||||||
s.tcpdumpPIDMap[neTypeID] = msg
|
pidKey := fmt.Sprintf("%s_%s_%s", strings.ToLower(neInfo.NeType), neInfo.NeId, taskCode)
|
||||||
return fileName, err
|
dumpPIDMap.Store(pidKey, outputPID)
|
||||||
|
return taskCode, err
|
||||||
}
|
}
|
||||||
|
|
||||||
// DumpStop 停止已存在抓包句柄
|
// DumpStop 停止已存在抓包句柄
|
||||||
func (s *TcpdumpImpl) DumpStop(neType, neId, fileName string) (string, error) {
|
func (s *TcpdumpImpl) DumpStop(neType, neId, taskCode string) (string, error) {
|
||||||
// 查询网元获取IP
|
// 查询网元获取IP
|
||||||
neInfo := s.neInfoService.SelectNeInfoByNeTypeAndNeID(neType, neId)
|
neInfo := s.neInfoService.SelectNeInfoByNeTypeAndNeID(neType, neId)
|
||||||
if neInfo.NeId != neId || neInfo.IP == "" {
|
if neInfo.NeId != neId || neInfo.IP == "" {
|
||||||
return "", fmt.Errorf("noData")
|
return "", fmt.Errorf("app.common.noNEInfo")
|
||||||
}
|
}
|
||||||
|
|
||||||
// 网元主机的SSH客户端
|
// 网元主机的SSH客户端
|
||||||
sshClient, err := s.neInfoService.NeRunSSHClient(neInfo.NeType, neInfo.NeId)
|
sshClient, err := s.neInfoService.NeRunSSHClient(neInfo.NeType, neInfo.NeId)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -87,107 +102,147 @@ func (s *TcpdumpImpl) DumpStop(neType, neId, fileName string) (string, error) {
|
|||||||
}
|
}
|
||||||
defer sshClient.Close()
|
defer sshClient.Close()
|
||||||
|
|
||||||
// 是否拥有sudo权限并拼接
|
// 是否存在执行过的进程
|
||||||
withSudo := ""
|
pidKey := fmt.Sprintf("%s_%s_%s", strings.ToLower(neInfo.NeType), neInfo.NeId, taskCode)
|
||||||
if _, err := sshClient.RunCMD("sudo -n uname"); err == nil {
|
pid, ok := dumpPIDMap.Load(pidKey)
|
||||||
withSudo = "sudo "
|
|
||||||
}
|
|
||||||
|
|
||||||
// 是否存在进程
|
|
||||||
neTypeID := fmt.Sprintf("%s_%s", neInfo.NeType, neInfo.NeId)
|
|
||||||
pid, ok := s.tcpdumpPIDMap[neTypeID]
|
|
||||||
if !ok || pid == "" {
|
if !ok || pid == "" {
|
||||||
return "", fmt.Errorf("tcpdump is not running")
|
return "", fmt.Errorf("tcpdump is not running")
|
||||||
}
|
}
|
||||||
|
defer dumpPIDMap.Delete(pidKey)
|
||||||
|
|
||||||
// 查看日志
|
// 存放文件目录 /tmp/omc/tcpdump/udm/001/20240817104241
|
||||||
viewLogFile := ""
|
neDirTemp := fmt.Sprintf("/tmp/omc/tcpdump/%s/%s/%s", strings.ToLower(neInfo.NeType), neInfo.NeId, taskCode)
|
||||||
if fileName != "" && strings.Contains(fileName, neTypeID) {
|
// 命令拼装
|
||||||
viewLogFile = fmt.Sprintf("\n cat %s.log", fileName)
|
sendCmd := fmt.Sprintf("pids=$(pgrep -P %s) && [ -n \"$pids\" ] && sudo kill $pids;sudo timeout 2s cat %s/tcpdump.log", pid, neDirTemp)
|
||||||
}
|
// pids=$(pgrep -P 1914341) && [ -n "$pids" ] && sudo kill $pids;sudo timeout 2s cat tcpdump.log
|
||||||
|
output, err := sshClient.RunCMD(sendCmd)
|
||||||
// 拼装命令
|
if err != nil || strings.HasPrefix(output, "stderr:") {
|
||||||
sendCmd := fmt.Sprintf("cd /tmp \n %s kill %s %s", withSudo, pid, viewLogFile)
|
logger.Warnf("DumpStop err: %s => %s", strings.TrimSpace(output), err.Error())
|
||||||
msg, err := sshClient.RunCMD(sendCmd)
|
|
||||||
delete(s.tcpdumpPIDMap, neTypeID)
|
|
||||||
if err != nil || strings.HasPrefix(msg, "stderr:") {
|
|
||||||
logger.Warnf("DumpStop err: %s => %s", strings.TrimSpace(msg), err.Error())
|
|
||||||
return "", err
|
return "", err
|
||||||
}
|
}
|
||||||
return msg, nil
|
return output, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// DumpUPF UPF标准版抓包
|
// DumpDownload 抓包文件网元端复制到本地输出zip文件
|
||||||
func (s *TcpdumpImpl) DumpUPF(neType, neId, cmdStr string) (string, string, error) {
|
func (s *TcpdumpImpl) DumpDownload(neType, neId, taskCode string) (string, error) {
|
||||||
// 查询网元获取IP
|
// 查询网元获取IP
|
||||||
neInfo := s.neInfoService.SelectNeInfoByNeTypeAndNeID(neType, neId)
|
neInfo := s.neInfoService.SelectNeInfoByNeTypeAndNeID(neType, neId)
|
||||||
if neInfo.NeId != neId || neInfo.IP == "" {
|
if neInfo.NeId != neId || neInfo.IP == "" {
|
||||||
return "", "", fmt.Errorf("noData")
|
return "", fmt.Errorf("app.common.noNEInfo")
|
||||||
}
|
}
|
||||||
|
|
||||||
// 网元主机的SSH客户端
|
// 网元主机的SSH客户端
|
||||||
sshClient, err := s.neInfoService.NeRunSSHClient(neInfo.NeType, neInfo.NeId)
|
sshClient, err := s.neInfoService.NeRunSSHClient(neInfo.NeType, neInfo.NeId)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", "", err
|
return "", err
|
||||||
}
|
}
|
||||||
defer sshClient.Close()
|
defer sshClient.Close()
|
||||||
|
// 网元主机的SSH客户端进行文件传输
|
||||||
|
sftpClient, err := sshClient.NewClientSFTP()
|
||||||
|
if err != nil {
|
||||||
|
return "", fmt.Errorf("ne info sftp client err")
|
||||||
|
}
|
||||||
|
defer sftpClient.Close()
|
||||||
|
|
||||||
// 是否拥有sudo权限并拼接
|
neTypeLower := strings.ToLower(neInfo.NeType)
|
||||||
withSudo := ""
|
// 网管本地路径
|
||||||
if _, err := sshClient.RunCMD("sudo -n uname"); err == nil {
|
localDirPath := fmt.Sprintf("/tmp/omc/tcpdump/%s/%s", neTypeLower, neInfo.NeId)
|
||||||
withSudo = "sudo "
|
if runtime.GOOS == "windows" {
|
||||||
|
localDirPath = fmt.Sprintf("C:%s", localDirPath)
|
||||||
}
|
}
|
||||||
|
|
||||||
if msg, err := sshClient.RunCMD(fmt.Sprintf("%s expect -version", withSudo)); err != nil {
|
// 网元pcap目录 /tmp/omc/tcpdump/udm/001/20240817104241
|
||||||
// stderr: bash: expect:未找到命令 => exit status 127
|
sshClient.RunCMD("mkdir -p /tmp/omc && sudo chmod 777 -R /tmp/omc")
|
||||||
msg = strings.TrimSpace(msg)
|
neDirTemp := fmt.Sprintf("/tmp/omc/tcpdump/%s/%s/%s", neTypeLower, neInfo.NeId, taskCode)
|
||||||
logger.Warnf("DumpUPF err: %s => %s", msg, err.Error())
|
// 网元端复制到本地
|
||||||
return "", "", fmt.Errorf(msg)
|
localDirFilePath := filepath.Join(localDirPath, taskCode)
|
||||||
|
if err = sftpClient.CopyDirRemoteToLocal(neDirTemp, localDirFilePath); err != nil {
|
||||||
|
return "", fmt.Errorf("copy tcpdump file err")
|
||||||
}
|
}
|
||||||
|
|
||||||
// 拼装命令
|
// 压缩zip文件名
|
||||||
neTypeID := fmt.Sprintf("%s_%s", neInfo.NeType, neInfo.NeId)
|
zipFileName := fmt.Sprintf("%s-%s-pcap-%s.zip", neTypeLower, neInfo.NeId, taskCode)
|
||||||
timeStr := date.ParseDateToStr(time.Now(), date.YYYYMMDDHHMMSS)
|
zipFilePath := filepath.Join(localDirPath, zipFileName)
|
||||||
fileName := fmt.Sprintf("%s_%s", timeStr, neTypeID)
|
if err := file.CompressZipByDir(zipFilePath, localDirFilePath); err != nil {
|
||||||
// UPF标准版本telnet脚本
|
return "", fmt.Errorf("compress zip err")
|
||||||
scriptStr := "set pcapCmd [lindex $argv 0]\nspawn telnet " + neInfo.IP + " 5002\nexpect \"upfd1# \"\nsend \"$pcapCmd\\n\"\nexpect \"upfd1# \"\nsend \"quit\\n\"\nexpect \"eof\""
|
}
|
||||||
// scriptStr := "set pcapCmd [lindex $argv 0]\nspawn telnet localhost 5002\nexpect \"upfd1# \"\nsend \"$pcapCmd\\n\"\nexpect \"upfd1# \"\nsend \"quit\\n\"\nexpect \"eof\""
|
|
||||||
writePcapFile := fmt.Sprintf("echo '%s' > pcapUPF.sh\n %s chmod +x pcapUPF.sh", scriptStr, withSudo)
|
|
||||||
writeLogFile := fmt.Sprintf("> %s.log 2>&1 \ncat %s.log", fileName, fileName)
|
|
||||||
|
|
||||||
|
_ = os.RemoveAll(localDirFilePath) // 删除本地临时目录
|
||||||
|
return zipFilePath, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// UPFTrace UPF标准版内部抓包
|
||||||
|
func (s *TcpdumpImpl) UPFTrace(neType, neId, cmdStr string) (string, error) {
|
||||||
|
// 命令检查
|
||||||
|
if strings.Contains(cmdStr, "file") {
|
||||||
|
return "", fmt.Errorf("command cannot contain file")
|
||||||
|
}
|
||||||
|
|
||||||
|
// 查询网元获取IP
|
||||||
|
neInfo := s.neInfoService.SelectNeInfoByNeTypeAndNeID(neType, neId)
|
||||||
|
if neInfo.NeId != neId || neInfo.IP == "" {
|
||||||
|
return "", fmt.Errorf("app.common.noNEInfo")
|
||||||
|
}
|
||||||
|
// 网元主机的SSH客户端
|
||||||
|
sshClient, err := s.neInfoService.NeRunSSHClient(neInfo.NeType, neInfo.NeId)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
defer sshClient.Close()
|
||||||
|
// 网元主机的Telnet客户端
|
||||||
|
telnetClient, err := s.neInfoService.NeRunTelnetClient("UPF", neInfo.NeId, 2)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
defer telnetClient.Close()
|
||||||
|
|
||||||
|
// 命令拼装
|
||||||
|
fileName := fmt.Sprintf("%s_%s_part_%s.pcap ", neInfo.NeType, neInfo.NeId, time.Now().Format("20060102150405"))
|
||||||
|
pcapCmd := fmt.Sprintf("%s\r\n", cmdStr)
|
||||||
// 以off结尾是停止抓包,不需要写文件
|
// 以off结尾是停止抓包,不需要写文件
|
||||||
pcapCmd := cmdStr
|
if !strings.Contains(cmdStr, "off") {
|
||||||
if !strings.HasSuffix(pcapCmd, "off") {
|
// pcap trace rx tx max 100000 intfc any file UPF_001_part_20240817164516.pcap
|
||||||
pcapCmd = fmt.Sprintf("%s file %s.pcap", cmdStr, fileName)
|
pcapCmd = fmt.Sprintf("%s file %s\r\n", cmdStr, fileName)
|
||||||
}
|
}
|
||||||
sendCmd := fmt.Sprintf("cd /tmp \n%s\n expect ./pcapUPF.sh '%s' %s", writePcapFile, pcapCmd, writeLogFile)
|
// 发送命令 UPF内部默认输出路径/tmp只能写文件名
|
||||||
// cd /tmp
|
// pcap trace rx tx max 100000 intfc any file upf_test.pcap
|
||||||
// echo '' >
|
// pcap trace rx tx off
|
||||||
// expect ./cap.sh > pcapUPF.sh
|
output, err := telnetClient.RunCMD(pcapCmd)
|
||||||
// sudo chmod +x pcapUPF.sh
|
if err != nil {
|
||||||
// expect ./cap.sh 'pcap dispatch trace off' > 20240115165701_UDM_001.log 2>&1
|
logger.Warnf("DumpUPF err: %s => %s", output, err.Error())
|
||||||
// cat 20240115165701_UDM_001.log
|
return "", err
|
||||||
msg, err := sshClient.RunCMD(sendCmd)
|
|
||||||
msg = strings.TrimSpace(msg)
|
|
||||||
if err != nil || strings.HasPrefix(msg, "stderr:") {
|
|
||||||
logger.Warnf("DumpUPF err: %s => %s", msg, err.Error())
|
|
||||||
return "", "", err
|
|
||||||
}
|
}
|
||||||
if strings.Contains(msg, "Unable to connect to remote host") {
|
|
||||||
return "", "", fmt.Errorf("connection refused")
|
// 结果截取
|
||||||
|
arr := strings.Split(output, "\r\n")
|
||||||
|
if len(arr) == 2 {
|
||||||
|
return "", fmt.Errorf("trace pacp run failed")
|
||||||
}
|
}
|
||||||
// 以off结尾是停止抓包,不需要写文件
|
if len(arr) > 3 {
|
||||||
if strings.HasSuffix(pcapCmd, "off") {
|
resMsg := arr[2]
|
||||||
if strings.Contains(msg, "Write ") {
|
// pcap trace: unknown input `f file UPF_001_part_2024-08-19...'
|
||||||
lastTmpIndex := strings.LastIndex(msg, "/tmp/")
|
// pcap trace: dispatch trace already enabled...
|
||||||
text := msg[lastTmpIndex+5:]
|
// pcap trace: dispatch trace already disabled...
|
||||||
extensionIndex := strings.LastIndex(text, ".pcap")
|
// pcap trace: No packets captured...
|
||||||
if extensionIndex != -1 {
|
// Write 100000 packets to /tmp/UPF_001_part_20240817164516.pcap, and stop capture...
|
||||||
fileName = text[:extensionIndex]
|
if strings.Contains(resMsg, "unknown input") {
|
||||||
|
return "", fmt.Errorf("trace pacp unknown input")
|
||||||
|
}
|
||||||
|
if strings.Contains(resMsg, "already enabled") {
|
||||||
|
return "", fmt.Errorf("trace pacp already running")
|
||||||
|
}
|
||||||
|
if strings.Contains(resMsg, "already disabled") {
|
||||||
|
return "", fmt.Errorf("trace pacp not running")
|
||||||
|
}
|
||||||
|
if strings.Contains(resMsg, "No packets") {
|
||||||
|
return "", fmt.Errorf("trace pacp not packets")
|
||||||
|
}
|
||||||
|
if strings.Contains(resMsg, "packets to") {
|
||||||
|
matches := regexp.MustCompile(`(/tmp/[^,\s]+)`).FindStringSubmatch(resMsg)
|
||||||
|
if len(matches) == 0 {
|
||||||
|
return "", fmt.Errorf("file path not found")
|
||||||
}
|
}
|
||||||
} else {
|
return matches[0], nil
|
||||||
fileName = ""
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return fileName, msg, err
|
return "trace pacp running", nil
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -28,10 +28,14 @@ func Setup(router *gin.Engine) {
|
|||||||
collectlogs.OperateLog(collectlogs.OptionNew("log.operate.title.tcpdump", collectlogs.BUSINESS_TYPE_OTHER)),
|
collectlogs.OperateLog(collectlogs.OptionNew("log.operate.title.tcpdump", collectlogs.BUSINESS_TYPE_OTHER)),
|
||||||
controller.NewTcpdump.DumpStop,
|
controller.NewTcpdump.DumpStop,
|
||||||
)
|
)
|
||||||
tcpdumpGroup.POST("/traceUPF",
|
tcpdumpGroup.GET("/download",
|
||||||
|
middleware.PreAuthorize(nil),
|
||||||
|
controller.NewTcpdump.DumpDownload,
|
||||||
|
)
|
||||||
|
tcpdumpGroup.POST("/upf",
|
||||||
middleware.PreAuthorize(nil),
|
middleware.PreAuthorize(nil),
|
||||||
collectlogs.OperateLog(collectlogs.OptionNew("log.operate.title.tcpdump", collectlogs.BUSINESS_TYPE_OTHER)),
|
collectlogs.OperateLog(collectlogs.OptionNew("log.operate.title.tcpdump", collectlogs.BUSINESS_TYPE_OTHER)),
|
||||||
controller.NewTcpdump.TraceUPF,
|
controller.NewTcpdump.UPFTrace,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user