mirror of
https://github.com/httprunner/httprunner.git
synced 2026-05-27 11:20:08 +08:00
feat: support traceroute command
This commit is contained in:
@@ -10,8 +10,9 @@ import (
|
||||
)
|
||||
|
||||
var (
|
||||
pingOptions dial.PingOptions
|
||||
dnsOptions dial.DnsOptions
|
||||
pingOptions dial.PingOptions
|
||||
dnsOptions dial.DnsOptions
|
||||
traceRouteOptions dial.TraceRouteOptions
|
||||
)
|
||||
|
||||
var pingCmd = &cobra.Command{
|
||||
@@ -44,16 +45,31 @@ var dnsCmd = &cobra.Command{
|
||||
},
|
||||
}
|
||||
|
||||
var traceRouteCmd = &cobra.Command{
|
||||
Use: "traceroute $url",
|
||||
Short: "run integrated traceroute command",
|
||||
Args: cobra.ExactArgs(1),
|
||||
PreRun: func(cmd *cobra.Command, args []string) {
|
||||
setLogLevel(logLevel)
|
||||
},
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
return dial.DoTraceRoute(&traceRouteOptions, args)
|
||||
},
|
||||
}
|
||||
|
||||
func init() {
|
||||
rootCmd.AddCommand(pingCmd)
|
||||
pingCmd.Flags().IntVarP(&pingOptions.Count, "count", "c", 10, "Stop after sending (and receiving) N packets")
|
||||
pingCmd.Flags().DurationVarP(&pingOptions.Timeout, "timeout", "t", 20*time.Second, "Ping exits after N seconds")
|
||||
pingCmd.Flags().DurationVarP(&pingOptions.Interval, "interval", "i", 1*time.Second, "Wait N seconds between sending each packet")
|
||||
pingCmd.Flags().BoolVar(&pingOptions.SaveTests, "save-tests", false, "Save ping results json")
|
||||
pingCmd.Flags().BoolVar(&pingOptions.SaveTests, "save-tests", false, "Save ping result as json")
|
||||
|
||||
rootCmd.AddCommand(dnsCmd)
|
||||
dnsCmd.Flags().IntVar(&dnsOptions.DnsSourceType, "dns-source", 0, "DNS source type\n0: local DNS\n1: http DNS\n2: google DNS")
|
||||
dnsCmd.Flags().IntVar(&dnsOptions.DnsRecordType, "dns-record", 1, "DNS record type\n1: A\n28: AAAA\n5: CNAME")
|
||||
dnsCmd.Flags().StringVar(&dnsOptions.DnsServer, "dns-server", "", "DNS server, only available for local DNS source")
|
||||
dnsCmd.Flags().BoolVar(&dnsOptions.SaveTests, "save-tests", false, "Save DNS resolution results json")
|
||||
dnsCmd.Flags().BoolVar(&dnsOptions.SaveTests, "save-tests", false, "Save DNS resolution result as json")
|
||||
|
||||
rootCmd.AddCommand(traceRouteCmd)
|
||||
traceRouteCmd.Flags().BoolVar(&traceRouteOptions.SaveTests, "save-tests", false, "Save traceroute result as json")
|
||||
}
|
||||
|
||||
@@ -214,7 +214,7 @@ func DoDns(dnsOptions *DnsOptions, args []string) (err error) {
|
||||
dnsResultPath := filepath.Join(dir, dnsResultName)
|
||||
err = builtin.Dump2JSON(dnsResult, dnsResultPath)
|
||||
if err != nil {
|
||||
log.Error().Err(err).Msg("save ping result failed")
|
||||
log.Error().Err(err).Msg("save dns resolution result failed")
|
||||
}
|
||||
}
|
||||
}()
|
||||
|
||||
138
hrp/internal/dial/traceroute.go
Normal file
138
hrp/internal/dial/traceroute.go
Normal file
@@ -0,0 +1,138 @@
|
||||
package dial
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/url"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/mehrdadrad/mylg/cli"
|
||||
"github.com/mehrdadrad/mylg/icmp"
|
||||
"github.com/pkg/errors"
|
||||
"github.com/rs/zerolog/log"
|
||||
|
||||
"github.com/httprunner/httprunner/v4/hrp/internal/builtin"
|
||||
"github.com/httprunner/httprunner/v4/hrp/internal/json"
|
||||
)
|
||||
|
||||
type TraceRouteOptions struct {
|
||||
SaveTests bool
|
||||
}
|
||||
|
||||
type TraceRouteResult struct {
|
||||
IP string `json:"ip"`
|
||||
Details []TraceRouteResultNode `json:"details"`
|
||||
Suc bool `json:"suc"`
|
||||
ErrMsg string `json:"errMsg"`
|
||||
}
|
||||
|
||||
type TraceRouteResultNode struct {
|
||||
Id int `json:"id"`
|
||||
Ip string `json:"ip"`
|
||||
Time string `json:"time"`
|
||||
}
|
||||
|
||||
type HopResp struct {
|
||||
Num int `json:"Id"`
|
||||
Hop string `json:"Hop"`
|
||||
Ip string `json:"Ip"`
|
||||
Elapsed float64 `json:"Elapsed"`
|
||||
Holder string `json:"Holder"`
|
||||
ASN float64 `json:"ASN"`
|
||||
Last bool `json:"Last"`
|
||||
}
|
||||
|
||||
func DoTraceRoute(traceRouteOptions *TraceRouteOptions, args []string) (err error) {
|
||||
if len(args) != 1 {
|
||||
return errors.New("there should be one argument")
|
||||
}
|
||||
|
||||
var traceRouteResult TraceRouteResult
|
||||
defer func() {
|
||||
if traceRouteOptions.SaveTests {
|
||||
dir, _ := os.Getwd()
|
||||
traceRouteResultName := fmt.Sprintf("traceroute_result_%v.json", time.Now().Format("20060102150405"))
|
||||
traceRouteResultPath := filepath.Join(dir, traceRouteResultName)
|
||||
err = builtin.Dump2JSON(traceRouteResult, traceRouteResultPath)
|
||||
if err != nil {
|
||||
log.Error().Err(err).Msg("save traceroute result failed")
|
||||
}
|
||||
}
|
||||
}()
|
||||
|
||||
traceRouteTarget := args[0]
|
||||
parsedURL, err := url.Parse(traceRouteTarget)
|
||||
if err == nil && parsedURL.Host != "" {
|
||||
log.Info().Msgf("parse input url %v and extract host %v", traceRouteTarget, parsedURL.Host)
|
||||
traceRouteTarget = strings.Split(parsedURL.Host, ":")[0]
|
||||
}
|
||||
|
||||
cfg, err := cli.ReadDefaultConfig()
|
||||
if err != nil {
|
||||
log.Error().Err(err).Msgf("fail to read default config")
|
||||
traceRouteResult.Suc = false
|
||||
traceRouteResult.ErrMsg = err.Error()
|
||||
return
|
||||
}
|
||||
|
||||
traceRouter, err := icmp.NewTrace(traceRouteTarget, cfg)
|
||||
if err != nil {
|
||||
log.Error().Err(err).Msgf("fail to new traceRouter for %s", traceRouteTarget)
|
||||
traceRouteResult.Suc = false
|
||||
traceRouteResult.ErrMsg = err.Error()
|
||||
return
|
||||
}
|
||||
|
||||
startT := time.Now()
|
||||
defer func() {
|
||||
log.Info().Msgf("for target %s, traceroute costs %v", traceRouteTarget, time.Since(startT))
|
||||
}()
|
||||
|
||||
log.Info().Msgf("start to trace route of %v", traceRouteTarget)
|
||||
hopRespChan, err := traceRouter.MRun()
|
||||
if err != nil {
|
||||
log.Error().Err(err).Msgf("fail to trace route of %v", traceRouteTarget)
|
||||
traceRouteResult.Suc = false
|
||||
traceRouteResult.ErrMsg = err.Error()
|
||||
}
|
||||
count := 0
|
||||
t := time.NewTicker(2 * time.Minute)
|
||||
for {
|
||||
select {
|
||||
case <-t.C:
|
||||
log.Error().Err(err).Msgf("fail to do traceroute for %s because timeout", traceRouteTarget)
|
||||
traceRouteResult.Suc = false
|
||||
traceRouteResult.ErrMsg = "timeout"
|
||||
return
|
||||
case resp := <-hopRespChan:
|
||||
respJSON := resp.Marshal()
|
||||
fmt.Printf("traceroute hop: %v\n", respJSON)
|
||||
var hopResp HopResp
|
||||
err = json.Unmarshal([]byte(respJSON), &hopResp)
|
||||
if err != nil {
|
||||
log.Error().Err(err).Msgf("fail to do traceroute for %s because of hop response %v unmarshal error", traceRouteTarget, respJSON)
|
||||
traceRouteResult.Suc = false
|
||||
traceRouteResult.ErrMsg = "hop response unmarshal error"
|
||||
}
|
||||
traceRouteResult.Details = append(traceRouteResult.Details, TraceRouteResultNode{
|
||||
Id: hopResp.Num,
|
||||
Ip: hopResp.Ip,
|
||||
Time: fmt.Sprintf("%.2f", hopResp.Elapsed),
|
||||
})
|
||||
traceRouteResult.Suc = true
|
||||
traceRouteResult.ErrMsg = ""
|
||||
if hopResp.Last {
|
||||
traceRouteResult.IP = hopResp.Ip
|
||||
log.Info().Msgf("for target %s, traceroute completed", traceRouteTarget)
|
||||
return
|
||||
}
|
||||
count += 1
|
||||
if count > 30 {
|
||||
log.Info().Msgf("for target %s, traceroute hop counts reach limit", traceRouteTarget)
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user