1 | #! /usr/bin/env python |
---|
2 | # |
---|
3 | # This version of pbsmon is base on the new_rack_pbsmon.py |
---|
4 | # |
---|
5 | # Authors: |
---|
6 | # Bas van der Vlies |
---|
7 | # Dennis Stam |
---|
8 | # |
---|
9 | # SVN Info: |
---|
10 | # $Id: new_rack_pbsmon.py 321 2014-09-29 13:12:50Z dennis $ |
---|
11 | # $URL$ |
---|
12 | # |
---|
13 | |
---|
14 | """ |
---|
15 | specifying hostnames: |
---|
16 | To specify a range use the [] to indicate a range, a couple of examples: |
---|
17 | |
---|
18 | The first five nodes of rack 16 |
---|
19 | - gb-r16n[1-5] |
---|
20 | |
---|
21 | The first five nodes and node 12 and 18 of rack 16 to 20 |
---|
22 | - gb-r[16-20]n[1-5,12,18] |
---|
23 | |
---|
24 | The first five nodes de in rack 16 with padding enabled |
---|
25 | - gb-r[16]n[01-5] |
---|
26 | |
---|
27 | The ranges ([]) are not only limited to numbers, letters can also be used. |
---|
28 | """ |
---|
29 | |
---|
30 | import sys |
---|
31 | import re |
---|
32 | import re |
---|
33 | import types |
---|
34 | |
---|
35 | import pbs |
---|
36 | from PBSQuery import PBSQuery |
---|
37 | from PBSQuery import PBSError |
---|
38 | |
---|
39 | ## This RE pattern is used for the hostrange |
---|
40 | HOSTRANGE = r'\[([0-9az\-,]+)\]' |
---|
41 | |
---|
42 | # Remark: When both are True, extended view is being printed |
---|
43 | PRINT_TABLE = True |
---|
44 | PRINT_EXTENDED = False |
---|
45 | |
---|
46 | # Which nodes must be skipped |
---|
47 | EXCLUDE_NODES = [ 'login' ] |
---|
48 | |
---|
49 | # Some global OPTS |
---|
50 | OPT_SKIP_EMPTY_RACKS = True |
---|
51 | OPT_SERVERNAME = None |
---|
52 | |
---|
53 | ## Begin: TABLE view opts |
---|
54 | |
---|
55 | # A node has the following syntax gb-r10n10 |
---|
56 | # r10 is rack name -> skip one char --> gives us rack number = 10 |
---|
57 | # n10 is node name -> skip one char --> gives us node number = 10 |
---|
58 | # Then we have to set these variables to determine automatically the |
---|
59 | # number of nodes and racks |
---|
60 | # |
---|
61 | NODE_EXPR = "r(?P<racknr>[0-9]+)n(?P<nodenr>[0-9]+)" |
---|
62 | |
---|
63 | START_RACK = 1 |
---|
64 | |
---|
65 | ## End: TABLE view opts |
---|
66 | |
---|
67 | ## Begin: EXTENDED view opts |
---|
68 | |
---|
69 | LENGTH_NODE = 0 |
---|
70 | LENGTH_STATE = 0 |
---|
71 | |
---|
72 | EXTENDED_PATTERNS = { |
---|
73 | 'header' : ' %-*s | %-*s | %s', |
---|
74 | 'row': ' %-*s | %-*s | %s', |
---|
75 | 'line': ' %s', |
---|
76 | 'line_char': '-', |
---|
77 | } |
---|
78 | |
---|
79 | ## End: EXTENDED view opts |
---|
80 | |
---|
81 | pbs_ND_single = 'job (single)' |
---|
82 | pbs_ND_total = 'total' |
---|
83 | pbs_ND_free_serial = 'free serial' |
---|
84 | pbs_ND_free_parallel = 'free parallel' |
---|
85 | |
---|
86 | PBS_STATES = { |
---|
87 | pbs.ND_free : '_', |
---|
88 | pbs.ND_down : 'X', |
---|
89 | pbs.ND_offline : '.', |
---|
90 | pbs.ND_reserve : 'R', |
---|
91 | pbs.ND_job_exclusive : 'J', |
---|
92 | pbs.ND_job_sharing : 'S', |
---|
93 | pbs.ND_busy : '*', |
---|
94 | pbs.ND_state_unknown : '?', |
---|
95 | pbs.ND_timeshared : 'T', |
---|
96 | pbs.ND_cluster : 'C', |
---|
97 | pbs_ND_single : 'j', |
---|
98 | pbs_ND_free_serial : '_', |
---|
99 | pbs_ND_free_parallel : '_', |
---|
100 | pbs_ND_total : ' ' |
---|
101 | } |
---|
102 | |
---|
103 | ## Color support? |
---|
104 | import curses |
---|
105 | curses.setupterm() |
---|
106 | if curses.tigetnum("colors") > 2: |
---|
107 | TERMINAL_COLOR=True |
---|
108 | else: |
---|
109 | TERMINAL_COLOR=False |
---|
110 | |
---|
111 | #### |
---|
112 | ## Rewriting the print function, so it will work with all versions of Python |
---|
113 | def _print(*args, **kwargs): |
---|
114 | '''A wrapper function to make the functionality for the print function the same for Python2.4 and higher''' |
---|
115 | ## First try if we are running in Python3 and higher |
---|
116 | try: |
---|
117 | Print = eval('print') |
---|
118 | Print(*args, **kwargs) |
---|
119 | except SyntaxError: |
---|
120 | ## Then Python2.6 and Python2.7 |
---|
121 | try: |
---|
122 | D = dict() |
---|
123 | exec('from __future__ import print_function\np=print', D) |
---|
124 | D['p'](*args, **kwargs) |
---|
125 | del D |
---|
126 | ## Finally Python2.5 or lower |
---|
127 | except SyntaxError: |
---|
128 | del D |
---|
129 | fout = kwargs.get('file', sys.stdout) |
---|
130 | write = fout.write |
---|
131 | if args: |
---|
132 | write(str(args[0])) |
---|
133 | sep = kwargs.get('sep', ' ') |
---|
134 | for arg in args[1:]: |
---|
135 | write(sep) |
---|
136 | write(str(a)) |
---|
137 | write(kwargs.get('end', '\n')) |
---|
138 | |
---|
139 | class color: |
---|
140 | PURPLE = '\033[95m' |
---|
141 | CYAN = '\033[96m' |
---|
142 | DARKCYAN = '\033[36m' |
---|
143 | BLUE = '\033[94m' |
---|
144 | GREEN = '\033[92m' |
---|
145 | YELLOW = '\033[93m' |
---|
146 | RED = '\033[91m' |
---|
147 | BOLD = '\033[1m' |
---|
148 | UNDERLINE = '\033[4m' |
---|
149 | END = '\033[0m' |
---|
150 | |
---|
151 | def compare_lists(list_a, list_b): |
---|
152 | return not any(x not in list_b for x in list_a) |
---|
153 | |
---|
154 | ## Import argparse here, as I need the _print function |
---|
155 | try: |
---|
156 | import argparse |
---|
157 | except ImportError: |
---|
158 | _print('Cannot find argparse module', file=sys.stderr) |
---|
159 | sys.exit(1) |
---|
160 | |
---|
161 | #### |
---|
162 | ## BEGIN functions for hostrange parsing |
---|
163 | def l_range(start, end): |
---|
164 | '''The equivalent for the range function, but then with letters, uses the ord function''' |
---|
165 | start = ord(start) |
---|
166 | end = ord(end) |
---|
167 | rlist = list() |
---|
168 | |
---|
169 | ## A ord number must be between 96 (a == 97) and 122 (z == 122) |
---|
170 | if start < 96 or start > 122 and end < 96 or end > 122: |
---|
171 | raise Exception('You can only use letters a to z') |
---|
172 | ## If start is greater then end, then the range is invalid |
---|
173 | elif start > end: |
---|
174 | raise Exception('The first letter must be smaller then the second one') |
---|
175 | ## Just revert the ord number to the char |
---|
176 | for letter in range(start, end + 1): |
---|
177 | rlist.append(chr(letter)) |
---|
178 | return rlist |
---|
179 | |
---|
180 | def return_range(string): |
---|
181 | '''This function will return the possible values for the given ranges''' |
---|
182 | |
---|
183 | ## First check if the first char is valid |
---|
184 | if string.startswith(',') or string.startswith('-'): |
---|
185 | raise Exception('Given pattern is invalid, you can\'t use , and - at the beginning') |
---|
186 | |
---|
187 | numbers_chars = list() |
---|
188 | equal_width_length = 0 |
---|
189 | |
---|
190 | ## First splitup the sections (divided by ,) |
---|
191 | for section in string.split(','): |
---|
192 | ## Within a section you can have a range, max two values |
---|
193 | chars = section.split('-') |
---|
194 | if len(chars) == 2: |
---|
195 | ## When range is a digit, simply use the range function |
---|
196 | if chars[0].isdigit() and chars[1].isdigit(): |
---|
197 | ## Owke, check for equal_width_length |
---|
198 | if chars[0][0] == '0' or chars[1][0] == '0': |
---|
199 | if len(chars[0]) >= len(chars[1]): |
---|
200 | equal_width_length = len(chars[0]) |
---|
201 | else: |
---|
202 | equal_width_length = len(chars[1]) |
---|
203 | ## Don't forget the +1 |
---|
204 | numbers_chars += range(int(chars[0]), int(chars[1])+1) |
---|
205 | ## If one of the two is a digit, raise an exceptio |
---|
206 | elif chars[0].isdigit() or chars[1].isdigit(): |
---|
207 | raise Exception('I can\'t combine integers with letters, change your range please') |
---|
208 | ## Else use the l_range |
---|
209 | else: |
---|
210 | numbers_chars += l_range(chars[0], chars[1]) |
---|
211 | else: |
---|
212 | ## If the value of the section is a integer value, check if it has a 0 |
---|
213 | if section.isdigit() and section[0] == '0': |
---|
214 | if len(section) > equal_width_length: |
---|
215 | equal_width_length = len(section) |
---|
216 | numbers_chars.append(section) |
---|
217 | |
---|
218 | ## if the equal_width length is greater then 0, rebuild the list |
---|
219 | ## 01, 02, 03, ... 10 |
---|
220 | if equal_width_length > 0: |
---|
221 | tmp_list = list() |
---|
222 | for number_char in numbers_chars: |
---|
223 | if type(number_char) is types.IntType or number_char.isdigit(): |
---|
224 | tmp_list.append('%0*d' % ( equal_width_length, int(number_char))) |
---|
225 | else: |
---|
226 | tmp_list.append(number_char) |
---|
227 | numbers_chars = tmp_list |
---|
228 | |
---|
229 | return numbers_chars |
---|
230 | |
---|
231 | def product(*args, **kwargs): |
---|
232 | '''Taken from the python docs, does the same as itertools.product, |
---|
233 | but this also works for py2.5''' |
---|
234 | pools = map(tuple, args) * kwargs.get('repeat', 1) |
---|
235 | result = [[]] |
---|
236 | for pool in pools: |
---|
237 | result = [x+[y] for x in result for y in pool] |
---|
238 | for prod in result: |
---|
239 | yield tuple(prod) |
---|
240 | |
---|
241 | def parse_args(args): |
---|
242 | rlist = list() |
---|
243 | for arg in args: |
---|
244 | parts = re.findall(HOSTRANGE, arg) |
---|
245 | if parts: |
---|
246 | ## create a formatter string, sub the matched patternn with %s |
---|
247 | string_format = re.sub(HOSTRANGE, '%s', arg) |
---|
248 | ranges = list() |
---|
249 | |
---|
250 | ## detect the ranges in the parts |
---|
251 | for part in parts: |
---|
252 | ranges.append(return_range(part)) |
---|
253 | |
---|
254 | ## produce the hostnames |
---|
255 | for combination in product(*ranges): |
---|
256 | rlist.append(string_format % combination) |
---|
257 | else: |
---|
258 | rlist.append(arg) |
---|
259 | return rlist |
---|
260 | |
---|
261 | ## END functions for hostrange parsing |
---|
262 | #### |
---|
263 | |
---|
264 | def sanitize_jobs( jobs ): |
---|
265 | |
---|
266 | ljobs = list() |
---|
267 | |
---|
268 | for job in jobs: |
---|
269 | ljobs.extend( re.findall( r'[0-9]+\/([0-9]+)\.*.', job ) ) |
---|
270 | |
---|
271 | return list( set( ljobs ) ) |
---|
272 | |
---|
273 | def parse_nodename( nodename ): |
---|
274 | global NODE_EXPR |
---|
275 | |
---|
276 | parts = re.search( r'%s' % NODE_EXPR, nodename, re.VERBOSE ) |
---|
277 | |
---|
278 | try: |
---|
279 | racknr = parts.group( 'racknr' ) |
---|
280 | except Exception: |
---|
281 | racknr = 0 |
---|
282 | |
---|
283 | try: |
---|
284 | nodenr = parts.group( 'nodenr' ) |
---|
285 | except Exception: |
---|
286 | nodenr = 0 |
---|
287 | |
---|
288 | return int( racknr ), int( nodenr ) |
---|
289 | |
---|
290 | def get_nodes( racknode=False, hosts=None ): |
---|
291 | global LENGTH_NODE |
---|
292 | global LENGTH_STATE |
---|
293 | global OPT_SERVERNAME |
---|
294 | |
---|
295 | nodes_dict = dict() |
---|
296 | |
---|
297 | try: |
---|
298 | if not OPT_SERVERNAME: |
---|
299 | p = PBSQuery() |
---|
300 | else: |
---|
301 | p = PBSQuery( OPT_SERVERNAME ) |
---|
302 | except PBSError, reason: |
---|
303 | _print('Error: %s' % reason) |
---|
304 | sys.exit( -1 ) |
---|
305 | |
---|
306 | p.new_data_structure() |
---|
307 | |
---|
308 | attr = [ 'state', 'jobs', 'properties', 'jobs' ] |
---|
309 | |
---|
310 | try: |
---|
311 | nodes = p.getnodes( attr ) |
---|
312 | except PBSError, reason: |
---|
313 | _print('Error: %s' % reason) |
---|
314 | sys.exit( -1 ) |
---|
315 | |
---|
316 | number_of_racks = 0 |
---|
317 | nodes_per_rack = 0 |
---|
318 | hosts_list = list() |
---|
319 | |
---|
320 | for node, attr in nodes.items(): |
---|
321 | if node in EXCLUDE_NODES: |
---|
322 | continue |
---|
323 | |
---|
324 | if hosts and node not in hosts: |
---|
325 | continue |
---|
326 | |
---|
327 | if pbs.ND_down in attr.state: |
---|
328 | state = pbs.ND_down |
---|
329 | else: |
---|
330 | state = attr.state[ 0 ] |
---|
331 | |
---|
332 | state_char = PBS_STATES[ state ] |
---|
333 | |
---|
334 | if attr.is_free() and attr.has_job(): |
---|
335 | state = pbs.ND_busy |
---|
336 | state_char = PBS_STATES[ pbs_ND_single ] |
---|
337 | |
---|
338 | if not nodes_dict.has_key( node ): |
---|
339 | nodes_dict[ node ] = dict() |
---|
340 | |
---|
341 | # Setting the longest lenght |
---|
342 | if len( node ) > LENGTH_NODE: |
---|
343 | LENGTH_NODE = len( node ) |
---|
344 | |
---|
345 | if len( state ) > LENGTH_STATE: |
---|
346 | LENGTH_STATE = len( state ) |
---|
347 | |
---|
348 | if racknode: |
---|
349 | racknr, nodenr = parse_nodename( node ) |
---|
350 | |
---|
351 | if racknr > number_of_racks: |
---|
352 | number_of_racks = racknr |
---|
353 | |
---|
354 | if nodenr > nodes_per_rack: |
---|
355 | nodes_per_rack = nodenr |
---|
356 | |
---|
357 | if not nodes_dict.has_key( racknr ): |
---|
358 | nodes_dict[ racknr ] = dict() |
---|
359 | |
---|
360 | if not nodes_dict[ racknr ].has_key( nodenr ): |
---|
361 | nodes_dict[ racknr ][ nodenr ] = dict() |
---|
362 | |
---|
363 | nodes_dict[ racknr ][ nodenr ][ 'state_char' ] = state_char |
---|
364 | nodes_dict[ racknr ][ nodenr ][ 'state' ] = state |
---|
365 | |
---|
366 | if attr.has_key( 'jobs' ): |
---|
367 | nodes_dict[ racknr ][ nodenr ][ 'jobs' ] = sanitize_jobs( attr.jobs ) |
---|
368 | else: |
---|
369 | nodes_dict[ racknr ][ nodenr ][ 'jobs' ] = [] |
---|
370 | |
---|
371 | if attr.has_key( 'properties' ): |
---|
372 | nodes_dict[ racknr ][ nodenr ][ 'properties' ] = attr.properties |
---|
373 | else: |
---|
374 | nodes_dict[ racknr ][ nodenr ][ 'properties' ] = [] |
---|
375 | |
---|
376 | else: |
---|
377 | hosts_list.append( node ) |
---|
378 | nodes_dict[ node ][ 'state_char' ] = state_char |
---|
379 | nodes_dict[ node ][ 'state' ] = state |
---|
380 | |
---|
381 | if attr.has_key( 'jobs' ): |
---|
382 | nodes_dict[ node ][ 'jobs' ] = sanitize_jobs( attr.jobs ) |
---|
383 | else: |
---|
384 | nodes_dict[ node ][ 'jobs' ] = [] |
---|
385 | |
---|
386 | if attr.has_key( 'properties' ): |
---|
387 | nodes_dict[ node ][ 'properties' ] = attr.properties |
---|
388 | else: |
---|
389 | nodes_dict[ node ][ 'properties' ] = [] |
---|
390 | |
---|
391 | if not racknode: |
---|
392 | return nodes_dict, hosts_list |
---|
393 | |
---|
394 | return nodes_dict, number_of_racks, nodes_per_rack |
---|
395 | |
---|
396 | def _generate_index( str ): |
---|
397 | index = [] |
---|
398 | |
---|
399 | def _append( fragment, alist=index ): |
---|
400 | if fragment.isdigit(): |
---|
401 | fragment = int( fragment ) |
---|
402 | alist.append( fragment ) |
---|
403 | |
---|
404 | prev_isdigit = str[0].isdigit() |
---|
405 | current_fragment = '' |
---|
406 | |
---|
407 | for char in str: |
---|
408 | curr_isdigit = char.isdigit() |
---|
409 | |
---|
410 | if curr_isdigit == prev_isdigit: |
---|
411 | current_fragment += char |
---|
412 | else: |
---|
413 | _append( current_fragment ) |
---|
414 | current_fragment = char |
---|
415 | prev_isdigit = curr_isdigit |
---|
416 | |
---|
417 | _append( current_fragment ) |
---|
418 | |
---|
419 | return tuple( index ) |
---|
420 | |
---|
421 | def real_sort( inlist ): |
---|
422 | indices = map(_generate_index, inlist ) |
---|
423 | decorated = zip( indices, inlist ) |
---|
424 | decorated.sort() |
---|
425 | |
---|
426 | return [ item for index, item in decorated ] |
---|
427 | |
---|
428 | def print_table(properties=None, jobs=None): |
---|
429 | global START_RACK |
---|
430 | global OPT_SKIP_EMPTY_RACKS |
---|
431 | |
---|
432 | nodes, racknr, nodenr = get_nodes( True ) |
---|
433 | |
---|
434 | ## Code herebelow has been taken from the new_rack_pbsmon.py |
---|
435 | save_column = None |
---|
436 | |
---|
437 | _print() |
---|
438 | _print(' ', end=' ') |
---|
439 | for rack in xrange( START_RACK, racknr + 1 ): |
---|
440 | |
---|
441 | if not ( rack % 10 ): |
---|
442 | char = '%d' % ( rack / 10 ) |
---|
443 | save_column = char |
---|
444 | else: |
---|
445 | char = ' ' |
---|
446 | |
---|
447 | if OPT_SKIP_EMPTY_RACKS: |
---|
448 | if nodes.has_key( rack ): |
---|
449 | if save_column: |
---|
450 | char = save_column |
---|
451 | save_column = None |
---|
452 | _print(char, end=' ') |
---|
453 | else: |
---|
454 | _print(char, end=' ') |
---|
455 | _print() |
---|
456 | |
---|
457 | _print(' ', end=' ') |
---|
458 | for rack in xrange( START_RACK, racknr + 1 ): |
---|
459 | |
---|
460 | char = rack % 10 |
---|
461 | if OPT_SKIP_EMPTY_RACKS: |
---|
462 | if nodes.has_key( rack ): |
---|
463 | _print(char, end=' ') |
---|
464 | else: |
---|
465 | _print(char, end=' ') |
---|
466 | _print() |
---|
467 | |
---|
468 | for node in xrange( 1, nodenr + 1 ): |
---|
469 | _print('%2d' % node, end=' ') |
---|
470 | |
---|
471 | for rack in xrange( START_RACK, racknr + 1 ): |
---|
472 | if OPT_SKIP_EMPTY_RACKS: |
---|
473 | if not nodes.has_key( rack ): |
---|
474 | continue |
---|
475 | try: |
---|
476 | if properties and compare_lists(properties,nodes[ rack ][ node ]['properties']): |
---|
477 | prop_color = True |
---|
478 | else: |
---|
479 | prop_color = False |
---|
480 | |
---|
481 | if jobs and compare_lists(jobs, nodes[ rack ][ node ]['jobs']): |
---|
482 | job_color = True |
---|
483 | else: |
---|
484 | job_color = False |
---|
485 | |
---|
486 | if prop_color or job_color: |
---|
487 | if TERMINAL_COLOR and prop_color and job_color: |
---|
488 | _print(color.GREEN + nodes[ rack ][ node ][ 'state_char' ] + color.END, end=' ') |
---|
489 | elif TERMINAL_COLOR and prop_color: |
---|
490 | _print(color.BLUE + nodes[ rack ][ node ][ 'state_char' ] + color.END, end=' ') |
---|
491 | elif TERMINAL_COLOR and job_color: |
---|
492 | _print(color.YELLOW + nodes[ rack ][ node ][ 'state_char' ] + color.END, end=' ') |
---|
493 | else: |
---|
494 | _print('M', end=' ') |
---|
495 | else: |
---|
496 | _print(nodes[ rack ][ node ][ 'state_char' ], end=' ') |
---|
497 | except KeyError: |
---|
498 | _print(' ', end=' ') |
---|
499 | _print() |
---|
500 | _print() |
---|
501 | |
---|
502 | def print_table_summary(): |
---|
503 | global PBS_STATES |
---|
504 | global OPT_SERVERNAME |
---|
505 | |
---|
506 | try: |
---|
507 | if not OPT_SERVERNAME: |
---|
508 | p = PBSQuery() |
---|
509 | else: |
---|
510 | p = PBSQuery( OPT_SERVERNAME ) |
---|
511 | except PBSError, reason: |
---|
512 | _print('error: %s' % reason) |
---|
513 | sys.exit(-1) |
---|
514 | |
---|
515 | # get the state of the nodes |
---|
516 | attr = [ 'state', 'jobs', 'properties' ] |
---|
517 | try: |
---|
518 | nodes = p.getnodes(attr) |
---|
519 | except PBSError, reason: |
---|
520 | _print('error: %s' % reason) |
---|
521 | sys.exit(-1) |
---|
522 | |
---|
523 | node_dict = {} |
---|
524 | |
---|
525 | count_states = {} |
---|
526 | for key in PBS_STATES.keys(): |
---|
527 | count_states[key] = 0 |
---|
528 | |
---|
529 | for nodename, node in nodes.items(): |
---|
530 | |
---|
531 | # Skip login nodes in status display |
---|
532 | # |
---|
533 | if not nodename.find('login'): |
---|
534 | continue |
---|
535 | |
---|
536 | state = node['state'][ 0 ] |
---|
537 | |
---|
538 | state_char = PBS_STATES[state] |
---|
539 | count_states[state] += 1 |
---|
540 | count_states[pbs_ND_total] += 1 |
---|
541 | |
---|
542 | if node.is_free(): # can happen for single CPU jobs |
---|
543 | if node.has_job(): |
---|
544 | # _print('TD: %s' % nodename, node) |
---|
545 | state_char = PBS_STATES[pbs_ND_single] |
---|
546 | count_states[pbs.ND_free] -= 1 |
---|
547 | count_states[pbs_ND_single] += 1 |
---|
548 | else: |
---|
549 | if 'infiniband' in node['properties']: |
---|
550 | count_states[pbs_ND_free_parallel] += 1 |
---|
551 | elif 'ifiniband' in node['properties']: |
---|
552 | count_states[pbs_ND_free_serial] += 1 |
---|
553 | #else: |
---|
554 | # count_states[pbs_ND_free_serial] += 1 |
---|
555 | |
---|
556 | # print_('TD: %s %s' % (nodename, state_char)) |
---|
557 | dummy = nodename.split('-') |
---|
558 | if len( dummy ) > 1: |
---|
559 | node_dict[dummy[1]] = state_char |
---|
560 | else: |
---|
561 | node_dict[dummy[0]] = state_char |
---|
562 | |
---|
563 | legend = PBS_STATES.keys() |
---|
564 | legend.sort() |
---|
565 | |
---|
566 | n = 0 |
---|
567 | for state in legend: |
---|
568 | _print(' %s %-13s : %-5d' % (PBS_STATES[state], state, count_states[state]), end=' ') |
---|
569 | |
---|
570 | n = n + 1 |
---|
571 | if not (n & 1): |
---|
572 | _print() |
---|
573 | |
---|
574 | if TERMINAL_COLOR: |
---|
575 | _print() |
---|
576 | _print('Colors has been enabled for your terminal:') |
---|
577 | _print(' - ' + color.YELLOW + 'Matched jobs' + color.END) |
---|
578 | _print(' - ' + color.BLUE + 'Matched properties' + color.END) |
---|
579 | _print(' - ' + color.GREEN + 'Matched jobs and properties' + color.END) |
---|
580 | |
---|
581 | def print_extended(hosts=None, properties=None, jobs=None): |
---|
582 | global LENGTH_NODE |
---|
583 | global LENGTH_STATE |
---|
584 | global EXTENDED_PATTERNS |
---|
585 | |
---|
586 | nodes, ihosts = get_nodes( hosts=hosts ) |
---|
587 | row_header = EXTENDED_PATTERNS[ 'header' ] % ( ( LENGTH_NODE + 2 ), 'Node', ( LENGTH_STATE + 2 ), 'State', 'Jobs' ) |
---|
588 | LENGTH_ROW = len( row_header ) |
---|
589 | |
---|
590 | rows_str = list() |
---|
591 | ihosts = real_sort( ihosts ) |
---|
592 | for node in ihosts: |
---|
593 | attr = nodes[ node ] |
---|
594 | |
---|
595 | if jobs and not compare_lists(jobs, attr['jobs']): |
---|
596 | continue |
---|
597 | |
---|
598 | if properties and not compare_lists(properties, attr['properties']): |
---|
599 | continue |
---|
600 | |
---|
601 | row_str = EXTENDED_PATTERNS[ 'row' ] % ( ( LENGTH_NODE + 2 ), node, ( LENGTH_STATE + 2 ), attr[ 'state' ], ','.join( attr[ 'jobs' ] ) ) |
---|
602 | |
---|
603 | if len( row_str ) > LENGTH_ROW: |
---|
604 | LENGTH_ROW = len( row_str ) |
---|
605 | |
---|
606 | rows_str.append( row_str ) |
---|
607 | |
---|
608 | _print() |
---|
609 | _print(row_header) |
---|
610 | _print(EXTENDED_PATTERNS[ 'line' ] % ( EXTENDED_PATTERNS[ 'line_char' ] * LENGTH_ROW )) |
---|
611 | _print('\n'.join( rows_str )) |
---|
612 | _print() |
---|
613 | |
---|
614 | if __name__ == '__main__': |
---|
615 | |
---|
616 | parser = argparse.ArgumentParser( |
---|
617 | formatter_class=argparse.RawDescriptionHelpFormatter, |
---|
618 | description=__doc__, |
---|
619 | ) |
---|
620 | |
---|
621 | parser.add_argument('nodes', metavar='NODES', nargs='*', type=str) |
---|
622 | parser.add_argument("-t", "--table", dest="table", action="store_true", help="Show an table", default=PRINT_TABLE ) |
---|
623 | parser.add_argument("-l", "--list", dest="extended", action="store_true", help="Show node rows with state and jobinfo", default=PRINT_EXTENDED ) |
---|
624 | parser.add_argument("-s", "--summary", dest="summary", action="store_true", help="Display a short summary", default=False ) |
---|
625 | parser.add_argument("-a", "--all", dest="summary", action="store_true", help="Display a short summary" ) |
---|
626 | parser.add_argument("-w", "--wide", dest="wide", action="store_true", help="Wide display for node status ( only when -t is used )" ) |
---|
627 | parser.add_argument("-S", "--servername", dest="servername", help="Change the default servername", default=None ) |
---|
628 | parser.add_argument("-p", "--properties", dest="properties", help="Show nodes with property, you can use more than 1 property by using , (this is always een and) ie. -p infiniband,mem64gb", default=None) |
---|
629 | parser.add_argument("-j", "--job", dest="jobs", help="Show which nodes are uses by a job", default=None) |
---|
630 | parser.add_argument('--version', action='version', version=pbs.version) |
---|
631 | |
---|
632 | args = parser.parse_args() |
---|
633 | |
---|
634 | if args.nodes: |
---|
635 | args.nodes = parse_args(args.nodes) |
---|
636 | |
---|
637 | if args.servername: |
---|
638 | OPT_SERVERNAME = args.servername |
---|
639 | |
---|
640 | if args.wide: |
---|
641 | OPT_SKIP_EMPTY_RACKS = False |
---|
642 | |
---|
643 | if args.nodes: |
---|
644 | args.extended = True |
---|
645 | |
---|
646 | if args.extended and PRINT_TABLE: |
---|
647 | args.table = False |
---|
648 | |
---|
649 | if args.table and PRINT_EXTENDED: |
---|
650 | args.extended = False |
---|
651 | |
---|
652 | if args.properties: |
---|
653 | args.properties = [ item.strip() for item in args.properties.split(',') ] |
---|
654 | |
---|
655 | if args.jobs: |
---|
656 | args.jobs = [ item.strip() for item in args.jobs.split(',') ] |
---|
657 | |
---|
658 | if args.extended: |
---|
659 | print_extended(args.nodes, args.properties, args.jobs) |
---|
660 | elif args.table: |
---|
661 | print_table(args.properties, args.jobs) |
---|
662 | else: |
---|
663 | _print('Something is wrong, bye!', file=sys.stderr) |
---|
664 | sys.exit( -1 ) |
---|
665 | |
---|
666 | if args.summary: |
---|
667 | print_table_summary() |
---|