@@ -22,8 +22,11 @@ import (
2222 "fmt"
2323 "math"
2424 "net/url"
25+ "os"
26+ "os/signal"
2527 "strconv"
2628 "strings"
29+ "syscall"
2730 "text/tabwriter"
2831 "text/template"
2932 "time"
@@ -117,14 +120,15 @@ func (pr PingResult) JSON() string {
117120var colorMap = template.FuncMap {
118121 "colorWhite" : color .New (color .FgWhite ).SprintfFunc (),
119122 "colorRed" : color .New (color .FgRed ).SprintfFunc (),
123+ "colorGreen" : color .New (color .FgGreen ).SprintfFunc (),
120124}
121125
122126// PingDist is the template for ping result in distributed mode
123- const PingDist = `{{$x := .Counter}}{{range .EndPointsStats}}{{if eq "0 " .CountErr }}{{colorWhite $x}}{{colorWhite ": "}}{{colorWhite .Endpoint.Scheme}}{{colorWhite "://"}}{{colorWhite .Endpoint.Host}}{{"\t"}}{{ colorWhite "min ="}}{{colorWhite .Min }}{{"\t"}}{{colorWhite "max=" }}{{colorWhite .Max}}{{"\t"}}{{colorWhite "average ="}}{{colorWhite .Average }}{{"\t"}}{{colorWhite "errors="}}{{colorWhite .CountErr}}{{" "}}{{colorWhite "roundtrip="}}{{colorWhite .Roundtrip}}{{ else}}{{colorRed $x}}{{colorRed ": "}}{{colorRed .Endpoint.Scheme}}{{colorRed "://"}}{{colorRed .Endpoint.Host}}{{"\t"}}{{ colorRed "min ="}}{{colorRed .Min }}{{"\t"}}{{colorRed "max=" }}{{colorRed .Max}}{{"\t"}}{{colorRed "average ="}}{{colorRed .Average}}{{"\t"}}{{colorRed "errors="}}{{colorRed .CountErr}}{{" "}}{{colorRed "roundtrip="}}{{colorRed .Roundtrip }}{{end}}
127+ const PingDist = `{{$x := .Counter}}{{range .EndPointsStats}}{{if eq "ok " .Status }}{{colorWhite $x}}{{colorWhite ": "}}{{colorWhite .Endpoint.Scheme}}{{colorWhite "://"}}{{colorWhite .Endpoint.Host}}{{"\t"}}{{colorWhite "status ="}}{{colorGreen .Status }}{{" " }}{{colorWhite "time ="}}{{colorWhite .Time }}{{else}}{{colorRed $x}}{{colorRed ": "}}{{colorRed .Endpoint.Scheme}}{{colorRed "://"}}{{colorRed .Endpoint.Host}}{{"\t"}}{{colorRed "status ="}}{{colorRed .Status }}{{" " }}{{colorRed "time ="}}{{colorRed .Time }}{{end}}
124128{{end}}`
125129
126130// Ping is the template for ping result
127- const Ping = `{{$x := .Counter}}{{range .EndPointsStats}}{{if eq "0 " .CountErr }}{{colorWhite $x}}{{colorWhite ": "}}{{colorWhite .Endpoint.Scheme}}{{colorWhite "://"}}{{colorWhite .Endpoint.Host}}{{"\t"}}{{ colorWhite "min ="}}{{colorWhite .Min }}{{"\t"}}{{colorWhite "max=" }}{{colorWhite .Max}}{{"\t"}}{{colorWhite "average ="}}{{colorWhite .Average }}{{"\t"}}{{colorWhite "errors="}}{{colorWhite .CountErr}}{{" "}}{{colorWhite "roundtrip="}}{{colorWhite .Roundtrip}}{{ else}}{{colorRed $x}}{{colorRed ": "}}{{colorRed .Endpoint.Scheme}}{{colorRed "://"}}{{colorRed .Endpoint.Host}}{{"\t"}}{{ colorRed "min ="}}{{colorRed .Min }}{{"\t"}}{{colorRed "max=" }}{{colorRed .Max}}{{"\t"}}{{colorRed "average ="}}{{colorRed .Average}}{{"\t"}}{{colorRed "errors="}}{{colorRed .CountErr}}{{" "}}{{colorRed "roundtrip="}}{{colorRed .Roundtrip }}{{end}}{{end}}`
131+ const Ping = `{{$x := .Counter}}{{range .EndPointsStats}}{{if eq "ok " .Status }}{{colorWhite $x}}{{colorWhite ": "}}{{colorWhite .Endpoint.Scheme}}{{colorWhite "://"}}{{colorWhite .Endpoint.Host}}{{"\t"}}{{colorWhite "status ="}}{{colorGreen .Status }}{{" " }}{{colorWhite "time ="}}{{colorWhite .Time }}{{else}}{{colorRed $x}}{{colorRed ": "}}{{colorRed .Endpoint.Scheme}}{{colorRed "://"}}{{colorRed .Endpoint.Host}}{{"\t"}}{{colorRed "status ="}}{{colorRed .Status }}{{" " }}{{colorRed "time ="}}{{colorRed .Time }}{{end}}{{end}}`
128132
129133// PingTemplateDist - captures ping template
130134var PingTemplateDist = template .Must (template .New ("ping-list" ).Funcs (colorMap ).Parse (PingDist ))
@@ -149,14 +153,11 @@ func (pr PingResult) String() string {
149153
150154// EndPointStats - container to hold server ping stats
151155type EndPointStats struct {
152- Endpoint * url.URL `json:"endpoint"`
153- Min string `json:"min"`
154- Max string `json:"max"`
155- Average string `json:"average"`
156- DNS string `json:"dns"`
157- CountErr string `json:"error-count,omitempty"`
158- Error string `json:"error,omitempty"`
159- Roundtrip string `json:"roundtrip"`
156+ Endpoint * url.URL `json:"endpoint"`
157+ DNS string `json:"dns"`
158+ Status string `json:"status,omitempty"`
159+ Error string `json:"error,omitempty"`
160+ Time string `json:"time"`
160161}
161162
162163// PingResult contains ping output
@@ -166,15 +167,71 @@ type PingResult struct {
166167 EndPointsStats []EndPointStats `json:"servers"`
167168}
168169
169- type serverStats struct {
170- min uint64
171- max uint64
172- sum uint64
173- avg uint64
174- dns uint64 // last DNS resolving time
175- errorCount int // used to keep a track of consecutive errors
176- err string
177- counter int // used to find the average, acts as denominator
170+ // PingSummary Summarizes the results of the ping execution.
171+ type PingSummary struct {
172+ Status string `json:"status"`
173+ // map to contain server stats for all the servers
174+ ServerMap map [string ]ServerStats `json:"serverMap"`
175+ }
176+
177+ // JSON jsonified ping summary message.
178+ func (ps PingSummary ) JSON () string {
179+ pingJSONBytes , e := json .MarshalIndent (ps , "" , " " )
180+ fatalIf (probe .NewError (e ), "Unable to marshal into JSON." )
181+
182+ return string (pingJSONBytes )
183+ }
184+
185+ // String colorized ping summary message.
186+ func (ps PingSummary ) String () string {
187+ dspOrder := []col {colGreen } // Header
188+ for i := 0 ; i < len (ps .ServerMap ); i ++ {
189+ dspOrder = append (dspOrder , colGrey )
190+ }
191+ var printColors []* color.Color
192+ for _ , c := range dspOrder {
193+ printColors = append (printColors , getPrintCol (c ))
194+ }
195+ tbl := console .NewTable (printColors , []bool {false , false , false , false , false , false }, 0 )
196+
197+ var builder strings.Builder
198+ cellText := make ([][]string , len (ps .ServerMap )+ 1 )
199+ cellText [0 ] = []string {
200+ "Endpoint" ,
201+ "Min" ,
202+ "Avg" ,
203+ "Max" ,
204+ "Error" ,
205+ "Count" ,
206+ }
207+ index := 0
208+ for endpoint , ping := range ps .ServerMap {
209+ index ++
210+ cellText [index ] = []string {
211+ ping .Endpoint .Scheme + "://" + endpoint ,
212+ trimToTwoDecimal (time .Duration (ping .Min )),
213+ trimToTwoDecimal (time .Duration (ping .Avg )),
214+ trimToTwoDecimal (time .Duration (ping .Max )),
215+ strconv .Itoa (ping .ErrorCount ),
216+ strconv .Itoa (ping .Counter ),
217+ }
218+ }
219+ e := tbl .PopulateTable (& builder , cellText )
220+ fatalIf (probe .NewError (e ), "unable to populate the table" )
221+ return builder .String ()
222+ }
223+
224+ // ServerStats ping result of each endpoint
225+ type ServerStats struct {
226+ Endpoint * url.URL `json:"endpoint"`
227+ Min uint64 `json:"min"`
228+ Max uint64 `json:"max"`
229+ Sum uint64 `json:"sum"`
230+ Avg uint64 `json:"avg"`
231+ DNS uint64 `json:"dns"` // last DNS resolving time
232+ ErrorCount int `json:"errorCount"` // used to keep a track of consecutive errors
233+ Err string `json:"err"`
234+ Counter int `json:"counter"` // used to find the average, acts as denominator
178235}
179236
180237func fetchAdminInfo (admClnt * madmin.AdminClient ) (madmin.InfoMessage , error ) {
@@ -223,7 +280,7 @@ func filterAdminInfo(admClnt *madmin.AdminClient, nodeName string) (madmin.InfoM
223280 return madmin.InfoMessage {}, e
224281}
225282
226- func ping (ctx context.Context , cliCtx * cli.Context , anonClient * madmin.AnonymousClient , admInfo madmin.InfoMessage , endPointMap map [ string ] serverStats , index int ) {
283+ func ping (ctx context.Context , cliCtx * cli.Context , anonClient * madmin.AnonymousClient , admInfo madmin.InfoMessage , pingSummary PingSummary , index int ) {
227284 var endPointStats []EndPointStats
228285 var servers []madmin.ServerProperties
229286 if cliCtx .Bool ("distributed" ) || cliCtx .IsSet ("node" ) {
@@ -232,28 +289,29 @@ func ping(ctx context.Context, cliCtx *cli.Context, anonClient *madmin.Anonymous
232289 allOK := true
233290
234291 for result := range anonClient .Alive (ctx , madmin.AliveOpts {}, servers ... ) {
235- stat := pingStats (cliCtx , result , endPointMap )
292+ stat := pingStats (cliCtx , result , pingSummary )
293+ status := "ok "
294+ if ! result .Online {
295+ status = "failed "
296+ }
236297
237298 allOK = allOK && result .Online
238299 endPointStat := EndPointStats {
239- Endpoint : result .Endpoint ,
240- Min : trimToTwoDecimal (time .Duration (stat .min )),
241- Max : trimToTwoDecimal (time .Duration (stat .max )),
242- Average : trimToTwoDecimal (time .Duration (stat .avg )),
243- DNS : time .Duration (stat .dns ).String (),
244- CountErr : strconv .Itoa (stat .errorCount ) + " " ,
245- Error : stat .err ,
246- Roundtrip : trimToTwoDecimal (result .ResponseTime ),
300+ Endpoint : result .Endpoint ,
301+ DNS : time .Duration (stat .DNS ).String (),
302+ Status : status ,
303+ Error : stat .Err ,
304+ Time : trimToTwoDecimal (result .ResponseTime ),
247305 }
248306 endPointStats = append (endPointStats , endPointStat )
249- endPointMap [result .Endpoint .Host ] = stat
307+ pingSummary . ServerMap [result .Endpoint .Host ] = stat
250308
251309 }
252310 stop = stop || cliCtx .Bool ("exit" ) && allOK
253311
254312 printMsg (PingResult {
255313 Status : "success" ,
256- Counter : strconv .Itoa (index ),
314+ Counter : pad ( strconv .Itoa (index ), " " , 3 - len ( strconv . Itoa ( index )), true ),
257315 EndPointsStats : endPointStats ,
258316 })
259317 if ! stop {
@@ -302,7 +360,7 @@ func pad(s, p string, count int, left bool) string {
302360 return string (ret )
303361}
304362
305- func pingStats (cliCtx * cli.Context , result madmin.AliveResult , serverMap map [ string ] serverStats ) serverStats {
363+ func pingStats (cliCtx * cli.Context , result madmin.AliveResult , ps PingSummary ) ServerStats {
306364 var errorString string
307365 var sum , avg , dns uint64
308366 minPing := uint64 (math .MaxUint64 )
@@ -311,13 +369,13 @@ func pingStats(cliCtx *cli.Context, result madmin.AliveResult, serverMap map[str
311369
312370 if result .Error != nil {
313371 errorString = result .Error .Error ()
314- if stat , ok := serverMap [result .Endpoint .Host ]; ok {
315- minPing = stat .min
316- maxPing = stat .max
317- sum = stat .sum
318- counter = stat .counter
319- avg = stat .avg
320- errorCount = stat .errorCount + 1
372+ if stat , ok := ps . ServerMap [result .Endpoint .Host ]; ok {
373+ minPing = stat .Min
374+ maxPing = stat .Max
375+ sum = stat .Sum
376+ counter = stat .Counter
377+ avg = stat .Avg
378+ errorCount = stat .ErrorCount + 1
321379
322380 } else {
323381 minPing = 0
@@ -330,17 +388,17 @@ func pingStats(cliCtx *cli.Context, result madmin.AliveResult, serverMap map[str
330388 } else {
331389 // reset consecutive error count
332390 errorCount = 0
333- if stat , ok := serverMap [result .Endpoint .Host ]; ok {
391+ if stat , ok := ps . ServerMap [result .Endpoint .Host ]; ok {
334392 var minVal uint64
335- if stat .min == 0 {
393+ if stat .Min == 0 {
336394 minVal = uint64 (result .ResponseTime )
337395 } else {
338- minVal = stat .min
396+ minVal = stat .Min
339397 }
340398 minPing = uint64 (math .Min (float64 (minVal ), float64 (uint64 (result .ResponseTime ))))
341- maxPing = uint64 (math .Max (float64 (stat .max ), float64 (uint64 (result .ResponseTime ))))
342- sum = stat .sum + uint64 (result .ResponseTime .Nanoseconds ())
343- counter = stat .counter + 1
399+ maxPing = uint64 (math .Max (float64 (stat .Max ), float64 (uint64 (result .ResponseTime ))))
400+ sum = stat .Sum + uint64 (result .ResponseTime .Nanoseconds ())
401+ counter = stat .Counter + 1
344402
345403 } else {
346404 minPing = uint64 (math .Min (float64 (minPing ), float64 (uint64 (result .ResponseTime ))))
@@ -351,7 +409,38 @@ func pingStats(cliCtx *cli.Context, result madmin.AliveResult, serverMap map[str
351409 avg = sum / uint64 (counter )
352410 dns = uint64 (result .DNSResolveTime .Nanoseconds ())
353411 }
354- return serverStats {minPing , maxPing , sum , avg , dns , errorCount , errorString , counter }
412+ return ServerStats {result .Endpoint , minPing , maxPing , sum , avg , dns , errorCount , errorString , counter }
413+ }
414+
415+ func watchSignals (ps PingSummary ) {
416+ c := make (chan os.Signal , 1 )
417+ signal .Notify (c , syscall .SIGTERM , syscall .SIGINT )
418+ go func () {
419+ s := <- c
420+ // Ensure that the table structure is not disrupted when manually canceling.
421+ fmt .Println ("" )
422+ printMsg (ps )
423+
424+ // Stop profiling if enabled, this needs to be before canceling the
425+ // global context to check for any unusual cpu/mem/goroutines usage
426+ stopProfiling ()
427+
428+ // Cancel the global context
429+ globalCancel ()
430+
431+ var exitCode int
432+ switch s .String () {
433+ case "interrupt" :
434+ exitCode = globalCancelExitStatus
435+ case "killed" :
436+ exitCode = globalKillExitStatus
437+ case "terminated" :
438+ exitCode = globalTerminatExitStatus
439+ default :
440+ exitCode = globalErrorExitStatus
441+ }
442+ os .Exit (exitCode )
443+ }()
355444}
356445
357446// mainPing is entry point for ping command.
@@ -383,9 +472,14 @@ func mainPing(cliCtx *cli.Context) error {
383472 admInfo , e = filterAdminInfo (admClient , cliCtx .String ("node" ))
384473 fatalIf (probe .NewError (e ).Trace (aliasedURL ), "Unable to get server info" )
385474 }
475+ pingSummary := PingSummary {
476+ ServerMap : make (map [string ]ServerStats ),
477+ Status : "success" ,
478+ }
386479
387- // map to contain server stats for all the servers
388- serverMap := make (map [string ]serverStats )
480+ // stop global signals trap.
481+ GlobalTrapSignals = false
482+ watchSignals (pingSummary )
389483
390484 index := 1
391485 if cliCtx .IsSet ("count" ) {
@@ -396,9 +490,10 @@ func mainPing(cliCtx *cli.Context) error {
396490 for index <= count {
397491 // return if consecutive error count more then specified value
398492 if stop {
493+ printMsg (pingSummary )
399494 return nil
400495 }
401- ping (ctx , cliCtx , anonClient , admInfo , serverMap , index )
496+ ping (ctx , cliCtx , anonClient , admInfo , pingSummary , index )
402497 index ++
403498 }
404499 } else {
@@ -409,12 +504,14 @@ func mainPing(cliCtx *cli.Context) error {
409504 default :
410505 // return if consecutive error count more then specified value
411506 if stop {
507+ printMsg (pingSummary )
412508 return nil
413509 }
414- ping (ctx , cliCtx , anonClient , admInfo , serverMap , index )
510+ ping (ctx , cliCtx , anonClient , admInfo , pingSummary , index )
415511 index ++
416512 }
417513 }
418514 }
515+ printMsg (pingSummary )
419516 return nil
420517}
0 commit comments