9
9
10
10
from elasticsearch import Elasticsearch
11
11
12
- # cluster_name status shards pri relo init unassign pending_tasks timestamp
13
- CLUSTER_TEMPLATE = """{cluster_name:33} {status:6} {active_shards:>6} {active_primary_shards:>4} {relocating_shards:>4} {initializing_shards:>4} {unassigned_shards:>8} {number_of_pending_tasks:>13} {timestamp:8}"""
12
+ CLUSTER_TEMPLATE = """{cluster_name:33} {status:6} {active_shards:>6} {active_primary_shards:>4} {relocating_shards:>4} {initializing_shards:>4} {unassigned_shards:>8} {number_of_pending_tasks:>13} {timestamp:8}"""
14
13
CLUSTER_HEADINGS = {}
15
14
CLUSTER_HEADINGS ["cluster_name" ] = "cluster"
16
15
CLUSTER_HEADINGS ["status" ] = "status"
22
21
CLUSTER_HEADINGS ["number_of_pending_tasks" ] = "pending tasks"
23
22
CLUSTER_HEADINGS ["timestamp" ] = "time"
24
23
25
- # node_name role load_avg mem% heap% old sz old gc young gc
26
24
NODES_TEMPLATE = {}
27
25
NODES_TEMPLATE ['general' ] = """{name:24} {role:<6}"""
28
26
NODES_TEMPLATE ['os' ] = """{load_avg:>18} {used_mem:>4}"""
31
29
NODES_TEMPLATE ['fielddata' ] = """{fielddata:^7}"""
32
30
NODES_TEMPLATE ['connections' ] = """{http_conn:>6} {transport_conn:>6}"""
33
31
NODES_TEMPLATE ['data_nodes' ] = """{merge_time:>8} {store_throttle:>8} {docs}"""
34
- NODES_FAILED_TEMPLATE = """{name:24} {role:<6} (No data received, node may have left cluster)"""
32
+ NODES_FAILED_TEMPLATE = """{name:24} {role:<6} (No data received, node may have left cluster)"""
35
33
NODE_HEADINGS = {}
36
34
NODE_HEADINGS ["name" ] = "nodes"
37
35
NODE_HEADINGS ["role" ] = "role"
@@ -61,10 +59,21 @@ def error(self, message):
61
59
self .print_help ()
62
60
sys .exit (2 )
63
61
62
+ class ESColors :
63
+ """ANSI escape codes for color output"""
64
+ END = '\033 [00m'
65
+ RED = '\033 [0;31m'
66
+ GREEN = '\033 [0;32m'
67
+ YELLOW = '\033 [0;33m'
68
+ GRAY = '\033 [1;30m'
69
+ WHITE = '\033 [1;37m'
70
+
64
71
class Elasticstat :
65
72
"""Elasticstat"""
66
73
67
- def __init__ (self , host , port , username , password , delay_interval , categories , threadpools ):
74
+ STATUS_COLOR = {'red' : ESColors .RED , 'green' : ESColors .GREEN , 'yellow' : ESColors .YELLOW }
75
+
76
+ def __init__ (self , host , port , username , password , delay_interval , categories , threadpools , no_color ):
68
77
69
78
self .sleep_interval = delay_interval
70
79
self .node_counters = {}
@@ -77,6 +86,7 @@ def __init__(self, host, port, username, password, delay_interval, categories, t
77
86
self .new_nodes = [] # used to track new nodes that join the cluster
78
87
self .active_master = ""
79
88
self .threadpools = threadpools
89
+ self .no_color = no_color
80
90
81
91
# categories for display
82
92
if categories == 'all' :
@@ -98,6 +108,12 @@ def __init__(self, host, port, username, password, delay_interval, categories, t
98
108
99
109
self .es_client = Elasticsearch ([host_dict ])
100
110
111
+ def colorize (self , msg , color ):
112
+ if self .no_color == True :
113
+ return (msg )
114
+ else :
115
+ return (color + msg + ESColors .END )
116
+
101
117
def thetime (self ):
102
118
return datetime .datetime .now ().strftime ("%H:%M:%S" )
103
119
@@ -213,9 +229,10 @@ def process_node_threads(self, role, node_id, node):
213
229
return (" " .join (thread_segments ))
214
230
215
231
def process_node_fielddata (self , role , node_id , node ):
216
- return (self .get_fd_stats (node_id ,
217
- node ['indices' ]['fielddata' ]['evictions' ],
218
- node ['breakers' ]['fielddata' ]['tripped' ]))
232
+ fielddata = self .get_fd_stats (node_id ,
233
+ node ['indices' ]['fielddata' ]['evictions' ],
234
+ node ['breakers' ]['fielddata' ]['tripped' ])
235
+ return (NODES_TEMPLATE ['fielddata' ].format (fielddata = fielddata ))
219
236
220
237
def process_node_connections (self , role , node_id , node ):
221
238
processed_node_conns = {}
@@ -229,12 +246,16 @@ def process_node_data_nodes(self, role, node_id, node):
229
246
if role in ['DATA' , 'ALL' ]:
230
247
processed_node_dn ['merge_time' ] = node ['indices' ]['merges' ]['total_time' ]
231
248
processed_node_dn ['store_throttle' ] = node ['indices' ]['store' ]['throttle_time' ]
232
- processed_node_dn ['docs' ] = "{0}|{1}" .format (node ['indices' ]['docs' ]['count' ],
233
- node ['indices' ]['docs' ]['deleted' ])
249
+ doc_count = node ['indices' ]['docs' ]['count' ]
250
+ deleted_count = node ['indices' ]['docs' ]['deleted' ]
251
+ if deleted_count > 0 :
252
+ processed_node_dn ['docs' ] = "{0}|{1}" .format (doc_count , deleted_count )
253
+ else :
254
+ processed_node_dn ['docs' ] = str (doc_count )
234
255
else :
235
256
processed_node_dn ['merge_time' ] = "-"
236
257
processed_node_dn ['store_throttle' ] = "-"
237
- processed_node_dn ['docs' ] = "-|- "
258
+ processed_node_dn ['docs' ] = "-"
238
259
return (NODES_TEMPLATE ['data_nodes' ].format (** processed_node_dn ))
239
260
240
261
def process_node (self , role , node_id , node ):
@@ -254,24 +275,27 @@ def process_role(self, role, nodes_stats):
254
275
new_nodes_by_name = {nodes_stats ['nodes' ][id ]['name' ]: id for id in self .new_nodes }
255
276
if failed_node_name in new_nodes_by_name :
256
277
# ...found it! Remove the old node_id, we've already added the new node_id at this point
257
- new_node_id = new_nodes_by_name [failed_node_name ]
258
- self .new_nodes .remove (new_node_id ) # So we don't flag this as a new node visually
278
+ new_node_id = new_nodes_by_name [failed_node_name ]
259
279
self .nodes_list .remove (node_id )
260
280
self .node_names .pop (node_id )
261
281
self .nodes_by_role [role ].remove (node_id )
262
282
else :
263
283
failed_node = {}
264
- failed_node ['name' ] = "-" + failed_node_name
284
+ failed_node ['name' ] = failed_node_name + '-'
265
285
failed_node ['role' ] = "({0})" .format (role ) # Role it had when we last saw this node in the cluster
266
- print NODES_FAILED_TEMPLATE .format (** failed_node )
286
+ print self . colorize ( NODES_FAILED_TEMPLATE .format (** failed_node ), ESColors . GRAY )
267
287
continue
268
288
# make sure node's role hasn't changed
269
289
current_role = self .get_role (nodes_stats ['nodes' ][node_id ]['attributes' ])
270
290
if current_role != role :
271
291
# Role changed, update lists so output will be correct on next iteration
272
292
self .nodes_by_role .setdefault (current_role , []).append (node_id ) # add to new role
273
293
self .nodes_by_role [role ].remove (node_id ) # remove from current role
274
- print self .process_node (current_role , node_id , nodes_stats ['nodes' ][node_id ])
294
+ row = self .process_node (current_role , node_id , nodes_stats ['nodes' ][node_id ])
295
+ if node_id in self .new_nodes :
296
+ print self .colorize (row , ESColors .WHITE )
297
+ else :
298
+ print row
275
299
276
300
def get_threads_headings (self ):
277
301
thread_segments = []
@@ -305,8 +329,10 @@ def print_stats(self):
305
329
306
330
# Print cluster health
307
331
cluster_health ['timestamp' ] = self .thetime ()
308
- print self .cluster_headings
309
- print CLUSTER_TEMPLATE .format (** cluster_health )
332
+ status = cluster_health ['status' ]
333
+ #cluster_health['status'] = self.colorize(status, self.STATUS_COLOR[status])
334
+ print self .colorize (self .cluster_headings , ESColors .GRAY )
335
+ print self .colorize (CLUSTER_TEMPLATE .format (** cluster_health ), self .STATUS_COLOR [status ])
310
336
print "" # space for readability
311
337
312
338
# Nodes can join and leave cluster with each iteration -- in order to report on nodes
@@ -331,7 +357,7 @@ def print_stats(self):
331
357
self .nodes_by_role .setdefault (node_role , []).append (node_id )
332
358
333
359
# Print node stats
334
- print self .node_headings
360
+ print self .colorize ( self . node_headings , ESColors . GRAY )
335
361
for role in self .nodes_by_role :
336
362
self .process_role (role , nodes_stats )
337
363
print "" # space out each run for readability
@@ -370,13 +396,19 @@ def main():
370
396
metavar = 'CATEGORY' ,
371
397
nargs = '+' ,
372
398
help = 'Statistic categories to show' )
373
- parser .add_argument ('-T ' ,
399
+ parser .add_argument ('-t ' ,
374
400
'--threadpools' ,
375
401
dest = 'threadpools' ,
376
402
default = DEFAULT_THREAD_POOLS ,
377
403
metavar = 'THREADPOOL' ,
378
404
nargs = '+' ,
379
405
help = 'Thread pools to show' )
406
+ parser .add_argument ('-C' ,
407
+ '--no-color' ,
408
+ dest = 'no_color' ,
409
+ action = 'store_true' ,
410
+ default = False ,
411
+ help = 'Display without color output' )
380
412
parser .add_argument ('delay_interval' ,
381
413
default = '1' ,
382
414
nargs = '?' ,
@@ -387,7 +419,7 @@ def main():
387
419
args = parser .parse_args ()
388
420
389
421
signal .signal (signal .SIGINT , lambda signum , frame : sys .exit ())
390
- elasticstat = Elasticstat (args .hostlist , args .port , args .username , args .password , args .delay_interval , args .categories , args .threadpools )
422
+ elasticstat = Elasticstat (args .hostlist , args .port , args .username , args .password , args .delay_interval , args .categories , args .threadpools , args . no_color )
391
423
elasticstat .format_headings ()
392
424
elasticstat .print_stats ()
393
425
0 commit comments