Files
be.ems/src/modules/trace/service/tcpdump.impl.go
2024-02-20 20:20:03 +08:00

186 lines
6.1 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
package service
import (
"fmt"
"strings"
"time"
"ems.agt/src/framework/config"
"ems.agt/src/framework/logger"
"ems.agt/src/framework/utils/cmd"
"ems.agt/src/framework/utils/date"
neService "ems.agt/src/modules/network_element/service"
)
// 实例化服务层 TcpdumpImpl 结构体
var NewTcpdumpImpl = &TcpdumpImpl{
neInfoService: neService.NewNeInfoImpl,
tcpdumpPIDMap: map[string]string{},
}
// 信令抓包 服务层处理
type TcpdumpImpl struct {
// 网元信息服务
neInfoService neService.INeInfo
// 抓包进程PID
tcpdumpPIDMap map[string]string
}
// DumpStart 触发tcpdump开始抓包 filePcapName, err
func (s *TcpdumpImpl) DumpStart(neType, neId, cmdStr string) (string, error) {
// 检查网元信息
neInfo := s.neInfoService.SelectNeInfoByNeTypeAndNeID(neType, neId)
if neInfo.NeId != neId {
return "", fmt.Errorf("noData")
}
// SSH命令
usernameNe := config.Get("ne.user").(string) // 网元统一用户
sshHost := fmt.Sprintf("%s@%s", usernameNe, neInfo.IP)
// 是否拥有sudo权限并拼接
withSudo := ""
if _, err := cmd.ExecWithCheck("ssh", sshHost, "sudo -n uname"); err == nil {
withSudo = "sudo "
}
if msg, err := cmd.ExecWithCheck("ssh", sshHost, fmt.Sprintf("%s tcpdump --version", withSudo)); err != nil {
// stderr: bash: tcpdump未找到命令 => exit status 127
msg = strings.TrimSpace(msg)
logger.Warnf("DumpStart err: %s => %s", msg, err.Error())
return "", fmt.Errorf(msg)
}
// 拼装命令
neTypeID := fmt.Sprintf("%s_%s", neInfo.NeType, neInfo.NeId)
timeStr := date.ParseDateToStr(time.Now(), date.YYYYMMDDHHMMSS)
fileName := fmt.Sprintf("%s_%s", timeStr, neTypeID)
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 $!
msg, err := cmd.ExecWithCheck("ssh", sshHost, sendCmd)
msg = strings.TrimSpace(msg)
if err != nil || strings.HasPrefix(msg, "stderr:") {
logger.Warnf("DumpStart err: %s => %s", msg, err.Error())
return "", err
}
// 检查进程 ps aux | grep tcpdump
// 强杀 sudo pkill tcpdump
s.tcpdumpPIDMap[neTypeID] = msg
return fileName, err
}
// DumpStop 停止已存在抓包句柄
func (s *TcpdumpImpl) DumpStop(neType, neId, fileName string) (string, error) {
// 检查网元信息
neInfo := s.neInfoService.SelectNeInfoByNeTypeAndNeID(neType, neId)
if neInfo.NeId != neId {
return "", fmt.Errorf("noData")
}
// SSH命令
usernameNe := config.Get("ne.user").(string) // 网元统一用户
sshHost := fmt.Sprintf("%s@%s", usernameNe, neInfo.IP)
// 是否拥有sudo权限并拼接
withSudo := ""
if _, err := cmd.ExecWithCheck("ssh", sshHost, "sudo -n uname"); err == nil {
withSudo = "sudo "
}
// 是否存在进程
neTypeID := fmt.Sprintf("%s_%s", neInfo.NeType, neInfo.NeId)
pid, ok := s.tcpdumpPIDMap[neTypeID]
if !ok || pid == "" {
return "", fmt.Errorf("tcpdump is not running")
}
// 查看日志
viewLogFile := ""
if fileName != "" && strings.Contains(fileName, neTypeID) {
viewLogFile = fmt.Sprintf("\n cat %s.log", fileName)
}
// 拼装命令
sendCmd := fmt.Sprintf("cd /tmp \n %s kill %s %s", withSudo, pid, viewLogFile)
msg, err := cmd.ExecWithCheck("ssh", sshHost, 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 msg, nil
}
// DumpUPF UPF标准版抓包
func (s *TcpdumpImpl) DumpUPF(neType, neId, cmdStr string) (string, string, error) {
// 检查网元信息
neInfo := s.neInfoService.SelectNeInfoByNeTypeAndNeID(neType, neId)
if neInfo.NeId != neId {
return "", "", fmt.Errorf("noData")
}
// SSH命令
usernameNe := config.Get("ne.user").(string) // 网元统一用户
sshHost := fmt.Sprintf("%s@%s", usernameNe, neInfo.IP)
// 是否拥有sudo权限并拼接
withSudo := ""
if _, err := cmd.ExecWithCheck("ssh", sshHost, "sudo -n uname"); err == nil {
withSudo = "sudo "
}
if msg, err := cmd.ExecWithCheck("ssh", sshHost, fmt.Sprintf("%s expect -version", withSudo)); err != nil {
// stderr: bash: expect未找到命令 => exit status 127
msg = strings.TrimSpace(msg)
logger.Warnf("DumpUPF err: %s => %s", msg, err.Error())
return "", "", fmt.Errorf(msg)
}
// 拼装命令
neTypeID := fmt.Sprintf("%s_%s", neInfo.NeType, neInfo.NeId)
timeStr := date.ParseDateToStr(time.Now(), date.YYYYMMDDHHMMSS)
fileName := fmt.Sprintf("%s_%s", timeStr, neTypeID)
// UPF标准版本telnet脚本
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)
// 以off结尾是停止抓包不需要写文件
pcapCmd := cmdStr
if !strings.HasSuffix(pcapCmd, "off") {
pcapCmd = fmt.Sprintf("%s file %s.pcap", cmdStr, fileName)
}
sendCmd := fmt.Sprintf("cd /tmp \n%s\n expect ./pcapUPF.sh '%s' %s", writePcapFile, pcapCmd, writeLogFile)
// cd /tmp
// echo '' >
// expect ./cap.sh > pcapUPF.sh
// sudo chmod +x pcapUPF.sh
// expect ./cap.sh 'pcap dispatch trace off' > 20240115165701_UDM_001.log 2>&1
// cat 20240115165701_UDM_001.log
msg, err := cmd.ExecWithCheck("ssh", sshHost, 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")
}
// 以off结尾是停止抓包不需要写文件
if strings.HasSuffix(pcapCmd, "off") {
if strings.Contains(msg, "Write ") {
lastTmpIndex := strings.LastIndex(msg, "/tmp/")
text := msg[lastTmpIndex+5:]
extensionIndex := strings.LastIndex(text, ".pcap")
if extensionIndex != -1 {
fileName = text[:extensionIndex]
}
} else {
fileName = ""
}
}
return fileName, msg, err
}