@@ -22,8 +22,11 @@ import (
22
22
"fmt"
23
23
"math"
24
24
"net/url"
25
+ "os"
26
+ "os/signal"
25
27
"strconv"
26
28
"strings"
29
+ "syscall"
27
30
"text/tabwriter"
28
31
"text/template"
29
32
"time"
@@ -117,14 +120,15 @@ func (pr PingResult) JSON() string {
117
120
var colorMap = template.FuncMap {
118
121
"colorWhite" : color .New (color .FgWhite ).SprintfFunc (),
119
122
"colorRed" : color .New (color .FgRed ).SprintfFunc (),
123
+ "colorGreen" : color .New (color .FgGreen ).SprintfFunc (),
120
124
}
121
125
122
126
// 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}}
124
128
{{end}}`
125
129
126
130
// 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}}`
128
132
129
133
// PingTemplateDist - captures ping template
130
134
var PingTemplateDist = template .Must (template .New ("ping-list" ).Funcs (colorMap ).Parse (PingDist ))
@@ -149,14 +153,11 @@ func (pr PingResult) String() string {
149
153
150
154
// EndPointStats - container to hold server ping stats
151
155
type 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"`
160
161
}
161
162
162
163
// PingResult contains ping output
@@ -166,15 +167,71 @@ type PingResult struct {
166
167
EndPointsStats []EndPointStats `json:"servers"`
167
168
}
168
169
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
178
235
}
179
236
180
237
func fetchAdminInfo (admClnt * madmin.AdminClient ) (madmin.InfoMessage , error ) {
@@ -223,7 +280,7 @@ func filterAdminInfo(admClnt *madmin.AdminClient, nodeName string) (madmin.InfoM
223
280
return madmin.InfoMessage {}, e
224
281
}
225
282
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 ) {
227
284
var endPointStats []EndPointStats
228
285
var servers []madmin.ServerProperties
229
286
if cliCtx .Bool ("distributed" ) || cliCtx .IsSet ("node" ) {
@@ -232,28 +289,29 @@ func ping(ctx context.Context, cliCtx *cli.Context, anonClient *madmin.Anonymous
232
289
allOK := true
233
290
234
291
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
+ }
236
297
237
298
allOK = allOK && result .Online
238
299
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 ),
247
305
}
248
306
endPointStats = append (endPointStats , endPointStat )
249
- endPointMap [result .Endpoint .Host ] = stat
307
+ pingSummary . ServerMap [result .Endpoint .Host ] = stat
250
308
251
309
}
252
310
stop = stop || cliCtx .Bool ("exit" ) && allOK
253
311
254
312
printMsg (PingResult {
255
313
Status : "success" ,
256
- Counter : strconv .Itoa (index ),
314
+ Counter : pad ( strconv .Itoa (index ), " " , 3 - len ( strconv . Itoa ( index )), true ),
257
315
EndPointsStats : endPointStats ,
258
316
})
259
317
if ! stop {
@@ -302,7 +360,7 @@ func pad(s, p string, count int, left bool) string {
302
360
return string (ret )
303
361
}
304
362
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 {
306
364
var errorString string
307
365
var sum , avg , dns uint64
308
366
minPing := uint64 (math .MaxUint64 )
@@ -311,13 +369,13 @@ func pingStats(cliCtx *cli.Context, result madmin.AliveResult, serverMap map[str
311
369
312
370
if result .Error != nil {
313
371
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
321
379
322
380
} else {
323
381
minPing = 0
@@ -330,17 +388,17 @@ func pingStats(cliCtx *cli.Context, result madmin.AliveResult, serverMap map[str
330
388
} else {
331
389
// reset consecutive error count
332
390
errorCount = 0
333
- if stat , ok := serverMap [result .Endpoint .Host ]; ok {
391
+ if stat , ok := ps . ServerMap [result .Endpoint .Host ]; ok {
334
392
var minVal uint64
335
- if stat .min == 0 {
393
+ if stat .Min == 0 {
336
394
minVal = uint64 (result .ResponseTime )
337
395
} else {
338
- minVal = stat .min
396
+ minVal = stat .Min
339
397
}
340
398
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
344
402
345
403
} else {
346
404
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
351
409
avg = sum / uint64 (counter )
352
410
dns = uint64 (result .DNSResolveTime .Nanoseconds ())
353
411
}
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
+ }()
355
444
}
356
445
357
446
// mainPing is entry point for ping command.
@@ -383,9 +472,14 @@ func mainPing(cliCtx *cli.Context) error {
383
472
admInfo , e = filterAdminInfo (admClient , cliCtx .String ("node" ))
384
473
fatalIf (probe .NewError (e ).Trace (aliasedURL ), "Unable to get server info" )
385
474
}
475
+ pingSummary := PingSummary {
476
+ ServerMap : make (map [string ]ServerStats ),
477
+ Status : "success" ,
478
+ }
386
479
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 )
389
483
390
484
index := 1
391
485
if cliCtx .IsSet ("count" ) {
@@ -396,9 +490,10 @@ func mainPing(cliCtx *cli.Context) error {
396
490
for index <= count {
397
491
// return if consecutive error count more then specified value
398
492
if stop {
493
+ printMsg (pingSummary )
399
494
return nil
400
495
}
401
- ping (ctx , cliCtx , anonClient , admInfo , serverMap , index )
496
+ ping (ctx , cliCtx , anonClient , admInfo , pingSummary , index )
402
497
index ++
403
498
}
404
499
} else {
@@ -409,12 +504,14 @@ func mainPing(cliCtx *cli.Context) error {
409
504
default :
410
505
// return if consecutive error count more then specified value
411
506
if stop {
507
+ printMsg (pingSummary )
412
508
return nil
413
509
}
414
- ping (ctx , cliCtx , anonClient , admInfo , serverMap , index )
510
+ ping (ctx , cliCtx , anonClient , admInfo , pingSummary , index )
415
511
index ++
416
512
}
417
513
}
418
514
}
515
+ printMsg (pingSummary )
419
516
return nil
420
517
}
0 commit comments