nodeocc 1.0.4__tar.gz → 1.0.6__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (23) hide show
  1. {nodeocc-1.0.4 → nodeocc-1.0.6}/PKG-INFO +1 -1
  2. {nodeocc-1.0.4 → nodeocc-1.0.6}/pyproject.toml +1 -1
  3. {nodeocc-1.0.4 → nodeocc-1.0.6}/src/controller/controller.py +2 -2
  4. {nodeocc-1.0.4 → nodeocc-1.0.6}/src/model/infrastructure.py +1 -1
  5. {nodeocc-1.0.4 → nodeocc-1.0.6}/src/nodeocc.egg-info/PKG-INFO +1 -1
  6. {nodeocc-1.0.4 → nodeocc-1.0.6}/src/readers/slurmreader.py +14 -1
  7. {nodeocc-1.0.4 → nodeocc-1.0.6}/src/view/curses_multiwindow.py +66 -26
  8. {nodeocc-1.0.4 → nodeocc-1.0.6}/src/view/slurm_list.py +1 -1
  9. {nodeocc-1.0.4 → nodeocc-1.0.6}/src/view/slurm_viz.py +69 -0
  10. {nodeocc-1.0.4 → nodeocc-1.0.6}/README.md +0 -0
  11. {nodeocc-1.0.4 → nodeocc-1.0.6}/setup.cfg +0 -0
  12. {nodeocc-1.0.4 → nodeocc-1.0.6}/setup.py +0 -0
  13. {nodeocc-1.0.4 → nodeocc-1.0.6}/src/__init__.py +0 -0
  14. {nodeocc-1.0.4 → nodeocc-1.0.6}/src/model/job.py +0 -0
  15. {nodeocc-1.0.4 → nodeocc-1.0.6}/src/nodeocc.egg-info/SOURCES.txt +0 -0
  16. {nodeocc-1.0.4 → nodeocc-1.0.6}/src/nodeocc.egg-info/dependency_links.txt +0 -0
  17. {nodeocc-1.0.4 → nodeocc-1.0.6}/src/nodeocc.egg-info/entry_points.txt +0 -0
  18. {nodeocc-1.0.4 → nodeocc-1.0.6}/src/nodeocc.egg-info/requires.txt +0 -0
  19. {nodeocc-1.0.4 → nodeocc-1.0.6}/src/nodeocc.egg-info/top_level.txt +0 -0
  20. {nodeocc-1.0.4 → nodeocc-1.0.6}/src/readers/compute_wait_time.py +0 -0
  21. {nodeocc-1.0.4 → nodeocc-1.0.6}/src/view/__init__.py +0 -0
  22. {nodeocc-1.0.4 → nodeocc-1.0.6}/src/view/styles.py +0 -0
  23. {nodeocc-1.0.4 → nodeocc-1.0.6}/src/view/utils.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: nodeocc
3
- Version: 1.0.4
3
+ Version: 1.0.6
4
4
  Summary: TUI application for viewing the status of GPU allocations on a Slurm cluster
5
5
  Author: Matteo Boschini
6
6
  Maintainer: Lorenzo Bonicelli, Aniello Panariello
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "nodeocc"
7
- version = "1.0.4"
7
+ version = "1.0.6"
8
8
  authors = [{ name = "Matteo Boschini" }]
9
9
  maintainers = [{ name = "Lorenzo Bonicelli" }, { name = "Aniello Panariello" }]
10
10
  description = "TUI application for viewing the status of GPU allocations on a Slurm cluster"
@@ -120,9 +120,9 @@ def update_data_master(instance):
120
120
  running_jobs = [j.to_nested_dict() for j in jobs if j.state == 'CG' or j.state == 'R']
121
121
  queue_jobs = [j.to_nested_dict() for j in jobs if j.state != 'R' and j.state != 'CG']
122
122
  queue_jobs = sorted(queue_jobs, key=lambda x: x['priority'], reverse=True)
123
- maxlen = min(120, len(running_jobs) + len(queue_jobs))
123
+ maxlen = min(100, len(running_jobs) + len(queue_jobs))
124
124
 
125
- msg = json.dumps({'inf': inf.to_nested_dict(), 'jobs': running_jobs + queue_jobs[:maxlen]})
125
+ msg = json.dumps({'inf': inf.to_nested_dict(), 'jobs': running_jobs + queue_jobs[:maxlen - len(running_jobs)]})
126
126
 
127
127
  # msg to bytes
128
128
  msg = msg.encode('utf-8')
@@ -92,4 +92,4 @@ class Infrastructure:
92
92
 
93
93
  def get_sorted_nodes(self):
94
94
 
95
- return sorted(self.nodes, key=lambda x: self.prior.index(x.gpu_model) if x.gpu_model in self.prior else -1)
95
+ return sorted(self.nodes, key=lambda x: self.prior.index(x.gpu_model.split(',')[0]) if x.gpu_model.split(',')[0] in self.prior else -1)
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: nodeocc
3
- Version: 1.0.4
3
+ Version: 1.0.6
4
4
  Summary: TUI application for viewing the status of GPU allocations on a Slurm cluster
5
5
  Author: Matteo Boschini
6
6
  Maintainer: Lorenzo Bonicelli, Aniello Panariello
@@ -41,11 +41,24 @@ def _node_from_sinfo(x):
41
41
  return n
42
42
 
43
43
 
44
+ def _parse_gpu_models(line):
45
+ line = line[0].split(',')
46
+ if line[0] == '(null)':
47
+ return 'n/a'
48
+ else:
49
+ if len(line) == 1:
50
+ return line[0].split(':')[1]
51
+ else:
52
+ # return line[0].split(':')[1] + ',' + line[1].split(':')[1]
53
+ return ','.join([x.split(':')[1] for x in line])
54
+
55
+
44
56
  def _read_nodes(reservations, pending_res):
45
57
  sinfo_df = pd.read_csv(StringIO(os.popen(r'sinfo -N -o "%N;%G;%t;%m;%c"').read()), sep=';')
46
58
  sinfo_df = _split_column(sinfo_df, 'NODELIST').drop_duplicates()
47
59
  sinfo_df['n_gpu'] = sinfo_df['GRES'].apply(lambda x: 0 if x.split('(')[0].split(':')[0] in ('(null)', '') else int(x.split('(')[0].split(':')[-1]))
48
- sinfo_df['m_gpu'] = sinfo_df['GRES'].str.split(':').apply(lambda x: 'n/a' if x[0] in ('(null)', '') else x[1])
60
+ # sinfo_df['m_gpu'] = sinfo_df['GRES'].str.split(':').apply(lambda x: 'n/a' if x[0] in ('(null)', '') else x[1])
61
+ sinfo_df['m_gpu'] = sinfo_df['GRES'].str.split(';').apply(_parse_gpu_models)
49
62
  sinfo_df['reserved'] = 'no'
50
63
  if reservations is not None:
51
64
  sinfo_df.loc[sinfo_df['NODELIST'].isin(reservations.Nodes.unique()), 'reserved'] = 'yes'
@@ -15,6 +15,24 @@ from view import update_views
15
15
 
16
16
  a_filter_values = [None, 'me', 'prod', 'stud', 'cvcs']
17
17
 
18
+ BUTTON_ACTIONS = {
19
+ 'up_1': [ord('w'), curses.KEY_UP, ord('k')],
20
+ 'down_1': [ord('s'), curses.KEY_DOWN, ord('j')],
21
+ 'left_1': [ord('a'), curses.KEY_LEFT, ord('h')],
22
+ 'right_1': [ord('d'), curses.KEY_RIGHT, ord('l')],
23
+ 'up_tab': [curses.KEY_NPAGE],
24
+ 'down_tab': [curses.KEY_PPAGE],
25
+ 'quit': [ord('q'), 'q'],
26
+ 'sort_prio': [ord('S')],
27
+ 'res_view_mode': [ord('g')],
28
+ 'job_id_type': [ord('b')],
29
+ 'show_starttime': [ord('z')],
30
+ 'show_account': [ord('t')],
31
+ 'show_prio': [ord('p')],
32
+ 'refresh': [ord('y')],
33
+ 'info': [ord('i')],
34
+ }
35
+
18
36
 
19
37
  def try_open_socket_as_slave(instance):
20
38
  if not instance.port_file_exists():
@@ -289,8 +307,8 @@ class Buffer(object):
289
307
  xacc += len(chunk)
290
308
  if len(self.buffer) > self.lines - 2:
291
309
  self.screen.addstr(self.lines - 2, instance.xoffset + instance.left_width // 2 - 6, ' ▼ SCROLL ▲ ', curses.color_pair(2) | curses.A_REVERSE)
292
- instance.add_button(self.lines - 2, instance.xoffset + 31, 'D', ord('s'))
293
- instance.add_button(self.lines - 2, instance.xoffset + 40, 'U', ord('w'))
310
+ instance.add_button(self.lines - 2, instance.xoffset + 31, 'D', BUTTON_ACTIONS['down_1'][0])
311
+ instance.add_button(self.lines - 2, instance.xoffset + 40, 'U', BUTTON_ACTIONS['up_1'][0])
294
312
 
295
313
  self.window.border()
296
314
  self.window.noutrefresh()
@@ -330,35 +348,50 @@ def handle_keys(stdscr, instance):
330
348
 
331
349
  # process input
332
350
  # RIGHT
333
- if k == ord('d') or k == 261:
351
+ if k in BUTTON_ACTIONS['right_1']:
334
352
  instance.a_filter = (instance.a_filter + 1) % len(a_filter_values)
335
353
  instance.voff = 0
336
354
  # LEFT
337
- elif k == ord('a') or k == 260:
355
+ elif k in BUTTON_ACTIONS['left_1']:
338
356
  instance.a_filter = (instance.a_filter + (len(a_filter_values) - 1)) % len(a_filter_values)
339
357
  instance.voff = 0
340
358
  elif valid_mouse and isinstance(k, str) and k.startswith('AF_'):
341
359
  instance.a_filter = int(k.split('AF_')[1])
342
360
  # DOWN
343
- elif k == ord('s') or k == 258:
361
+ elif k in BUTTON_ACTIONS['down_1']:
344
362
  instance.voff += 1
345
363
  # UP
346
- elif k == ord('w') or k == 259:
364
+ elif k in BUTTON_ACTIONS['up_1']:
347
365
  instance.voff -= 1
348
366
 
349
- if k == ord('S'):
367
+ elif k in BUTTON_ACTIONS['sort_prio']:
350
368
  instance.sort_by_prio = not instance.sort_by_prio
351
- if k == ord('g'):
369
+ elif k in BUTTON_ACTIONS['res_view_mode']:
352
370
  instance.view_mode = {"gpu": "ram", "ram": "cpu", "cpu": "gpu"}[instance.view_mode]
353
- if k == ord('j'):
371
+ instance.right_width = 33 if instance.view_mode != 'info' else 39
372
+ stdscr.clear()
373
+ elif k in BUTTON_ACTIONS['job_id_type']:
354
374
  instance.job_id_type = "true" if instance.job_id_type == "agg" else "agg"
355
- if k == ord('z'):
375
+ elif k in BUTTON_ACTIONS['show_starttime']:
356
376
  # instance.show_starttime = not instance.show_starttime
357
377
  pass
358
- if k == ord('t'):
378
+ elif k in BUTTON_ACTIONS['show_account']:
359
379
  instance.show_account = not instance.show_account
360
- if k == ord('p'):
380
+ elif k in BUTTON_ACTIONS['show_prio']:
361
381
  instance.show_prio = not instance.show_prio
382
+ elif k in BUTTON_ACTIONS['info']:
383
+ instance.view_mode = 'info' if instance.view_mode != 'info' else 'gpu'
384
+ instance.right_width = 33 if instance.view_mode != 'info' else 39
385
+ stdscr.clear()
386
+ elif k in BUTTON_ACTIONS['up_tab']:
387
+ # get screen size
388
+ height, width = stdscr.getmaxyx()
389
+
390
+ instance.voff += ((height - 4) // 2) + 1
391
+ elif k in BUTTON_ACTIONS['down_tab']:
392
+ height, width = stdscr.getmaxyx()
393
+
394
+ instance.voff -= ((height - 4) // 2) + 1
362
395
 
363
396
 
364
397
  def update_screen(stdscr, instance):
@@ -369,7 +402,7 @@ def update_screen(stdscr, instance):
369
402
  lines, columns = os.get_terminal_size().lines, os.get_terminal_size().columns
370
403
  s_lines, s_columns = stdscr.getmaxyx()
371
404
  instance.max_columns = s_columns
372
- if instance.k == ord('y'):
405
+ if instance.k in BUTTON_ACTIONS['refresh']:
373
406
  stdscr.clear()
374
407
 
375
408
  totsize = 106
@@ -395,11 +428,12 @@ def update_screen(stdscr, instance):
395
428
 
396
429
  stdscr.refresh()
397
430
 
398
- left_width = columns - 33 # 72
431
+ left_width = columns - instance.right_width # 72
432
+ right_width = instance.right_width # 33
399
433
  instance.left_width = left_width
400
434
  left_window = curses.newwin(lines - 1, left_width, 0, xoffset)
401
435
  left_buffer = Buffer(left_window, lines, stdscr)
402
- right_window = curses.newwin(lines - 1, 31, 0, xoffset + left_width + 1)
436
+ right_window = curses.newwin(lines - 1, right_width - 2, 0, xoffset + left_width + 1)
403
437
  right_buffer = Buffer(right_window, lines, stdscr)
404
438
 
405
439
  left_buffer.write(instance.rens)
@@ -409,7 +443,7 @@ def update_screen(stdscr, instance):
409
443
 
410
444
  # render menu
411
445
  stdscr.addstr(lines - 1, xoffset + 1 + 0, '◀')
412
- instance.add_button(lines - 1, xoffset + 1 + 0, '◀', ord('a'))
446
+ instance.add_button(lines - 1, xoffset + 1 + 0, '◀', BUTTON_ACTIONS['left_1'][0])
413
447
  stdscr.addstr(lines - 1, xoffset + 1 + 2, 'ALL', curses.color_pair(2) | (curses.A_REVERSE if a_filter_values[instance.a_filter] is None else 0))
414
448
 
415
449
  instance.add_button(lines - 1, xoffset + 1 + 2, 'ALL', 'AF_0')
@@ -424,37 +458,42 @@ def update_screen(stdscr, instance):
424
458
  instance.add_button(lines - 1, xoffset + 1 + 19, 'CVCS', 'AF_4')
425
459
 
426
460
  stdscr.addstr(lines - 1, xoffset + 1 + 24, '▶')
427
- instance.add_button(lines - 1, xoffset + 1 + 24, '▶', ord('d'))
461
+ instance.add_button(lines - 1, xoffset + 1 + 24, '▶', BUTTON_ACTIONS['right_1'][0])
428
462
  stdscr.addstr(lines - 1, xoffset + 1 + 25, ' ' * (columns - 27 - xoffset))
429
463
 
430
464
  stdscr.addstr(lines - 1, left_width - 8, '[Q:QUIT]', curses.color_pair(2))
431
- instance.add_button(lines - 1, left_width - 8, '[Q:QUIT]', ord('q')) # 53
465
+ instance.add_button(lines - 1, left_width - 8, '[Q:QUIT]', BUTTON_ACTIONS['quit'][0]) # 53
432
466
 
433
467
  stdscr.addstr(lines - 1, left_width - 18 + 19, '[Y:REDRAW]', curses.color_pair(2))
434
- instance.add_button(lines - 1, left_width - 18 + 19, '[Y:REDRAW]', ord('y'))
468
+ instance.add_button(lines - 1, left_width - 18 + 19, '[Y:REDRAW]', BUTTON_ACTIONS['refresh'][0])
435
469
 
470
+ stdscr.addstr(0, left_width + 2, '[I:', curses.color_pair(2))
471
+ stdscr.addstr(0, left_width + 5, 'INFO', curses.color_pair(2) | (curses.A_REVERSE if instance.view_mode == 'info' else 0))
472
+ stdscr.addstr(0, left_width + 9, ']', curses.color_pair(2))
473
+ instance.add_button(0, left_width + 2, '[I:INFO]', BUTTON_ACTIONS['info'][0])
474
+ stdscr.addstr(0, left_width + 10, '─' * (columns - 15 - left_width - 3), curses.color_pair(2))
436
475
  stdscr.addstr(0, columns - 15, '[G:', curses.color_pair(2))
437
476
  stdscr.addstr(0, columns - 12, 'GPU', curses.color_pair(2) | (curses.A_REVERSE if instance.view_mode == 'gpu' else 0))
438
477
  stdscr.addstr(0, columns - 12 + 3, 'RAM', curses.color_pair(2) | (curses.A_REVERSE if instance.view_mode == 'ram' else 0))
439
478
  stdscr.addstr(0, columns - 12 + 6, 'CPU', curses.color_pair(2) | (curses.A_REVERSE if instance.view_mode == 'cpu' else 0))
440
479
  stdscr.addstr(0, columns - 12 + 9, ']', curses.color_pair(2))
441
- instance.add_button(0, columns - 15, '[G:GPURAMCPU]', ord('g'))
480
+ instance.add_button(0, columns - 15, '[G:GPURAMCPU]', BUTTON_ACTIONS['res_view_mode'][0])
442
481
 
443
- stdscr.addstr(lines - 1, xoffset + 25 + 2, '[J:', curses.color_pair(2))
482
+ stdscr.addstr(lines - 1, xoffset + 25 + 2, '[B:', curses.color_pair(2))
444
483
  stdscr.addstr(lines - 1, xoffset + 25 + 2 + 3, 'AGG', curses.color_pair(2) | (curses.A_REVERSE if instance.job_id_type == 'agg' else 0))
445
484
  stdscr.addstr(lines - 1, xoffset + 25 + 2 + 3 + 3, 'TRUE', curses.color_pair(2) | (curses.A_REVERSE if instance.job_id_type == 'true' else 0))
446
485
  stdscr.addstr(lines - 1, xoffset + 25 + 2 + 3 + 3 + 4, ']', curses.color_pair(2))
447
- instance.add_button(lines - 1, xoffset + 25 + 2, '[J:AGGTRUE]', ord('j'))
486
+ instance.add_button(lines - 1, xoffset + 25 + 2, '[B:AGGTRUE]', BUTTON_ACTIONS['job_id_type'][0])
448
487
 
449
488
  stdscr.addstr(lines - 1, xoffset + 37 + 2, '[P:', curses.color_pair(2))
450
489
  stdscr.addstr(lines - 1, xoffset + 37 + 2 + 3, 'PRIORITY', curses.color_pair(2) | (curses.A_REVERSE if instance.show_prio else 0))
451
490
  stdscr.addstr(lines - 1, xoffset + 37 + 2 + 3 + 8, ']', curses.color_pair(2))
452
- instance.add_button(lines - 1, xoffset + 37 + 2, '[P:PRIORITY]', ord('p'))
491
+ instance.add_button(lines - 1, xoffset + 37 + 2, '[P:PRIORITY]', BUTTON_ACTIONS['show_prio'][0])
453
492
 
454
493
  stdscr.addstr(lines - 1, xoffset + 50 + 2, '[T:', curses.color_pair(2))
455
494
  stdscr.addstr(lines - 1, xoffset + 50 + 2 + 3, 'ACCOUNT', curses.color_pair(2) | (curses.A_REVERSE if instance.show_account else 0))
456
495
  stdscr.addstr(lines - 1, xoffset + 50 + 2 + 3 + 7, ']', curses.color_pair(2))
457
- instance.add_button(lines - 1, xoffset + 50 + 2, '[T:ACCOUNT]', ord('t'))
496
+ instance.add_button(lines - 1, xoffset + 50 + 2, '[T:ACCOUNT]', BUTTON_ACTIONS['show_account'][0])
458
497
 
459
498
  # get slurm user partition
460
499
  stdscr.addstr(lines - 1, xoffset + 62 + 2, f'(Avg time {instance.avg_wait_time})', curses.color_pair(2))
@@ -467,7 +506,7 @@ def update_screen(stdscr, instance):
467
506
 
468
507
 
469
508
  def get_char_async(stdscr, instance):
470
- while instance.k != ord('q') and instance.k != 'q':
509
+ while instance.k not in BUTTON_ACTIONS['quit']: # != 'q'
471
510
  handle_keys(stdscr, instance)
472
511
  update_screen(stdscr, instance)
473
512
 
@@ -476,7 +515,7 @@ def get_char_async(stdscr, instance):
476
515
 
477
516
 
478
517
  async def update_screen_info(stdscr, instance):
479
- while instance.k != ord('q') and instance.k != 'q':
518
+ while instance.k not in BUTTON_ACTIONS['quit']:
480
519
  await instance.fetch()
481
520
  instance.log("GOT DATA FROM MASTER")
482
521
  update_screen(stdscr, instance)
@@ -547,6 +586,7 @@ async def main(stdscr):
547
586
  # status
548
587
 
549
588
  instance.a_filter = 0
589
+ instance.right_width = 33
550
590
 
551
591
  stdscr.clear()
552
592
 
@@ -21,7 +21,7 @@ def _joblet_format(instance, job, width=74, jobid_type='agg'):
21
21
  if instance.show_prio:
22
22
  joblet_repr += '(' + _format_to(job.priority, len(str(job.priority))) + ')'
23
23
  joblet_repr += ' '
24
- loffset += 9
24
+ loffset += len(str(job.priority)) + 3
25
25
  if instance.show_account:
26
26
  joblet_repr += _format_to(job.account, 9)
27
27
  joblet_repr += ' '
@@ -115,6 +115,8 @@ def view_viz(infrastructure, jobs, work=True, stylefn=cmdstyle, current_user=Non
115
115
  return view_viz_ram(infrastructure, jobs, work, stylefn, current_user)
116
116
  elif mode == 'cpu':
117
117
  return view_viz_cpu(infrastructure, jobs, work, stylefn, current_user)
118
+ elif mode == 'info':
119
+ return view_viz_info(infrastructure, jobs, work, stylefn, current_user)
118
120
 
119
121
 
120
122
  def view_viz_ram(infrastructure, jobs, work=True, stylefn=cmdstyle, current_user=None):
@@ -354,6 +356,9 @@ def get_cpu_icon(stat):
354
356
  }.get(stat, cpu_avail)
355
357
 
356
358
 
359
+ GPU_MEM = {"K80": 12, "P100": 16, "1080": 11, "TitanXP": 12, "2080": 11, "RTX5000": 16, "RTX6000": 24, "RTXA5000": 24, "A40": 48}
360
+
361
+
357
362
  def view_viz_cpu(infrastructure, jobs, work=True, stylefn=cmdstyle, current_user=None):
358
363
  # this is for hot reload
359
364
  if not work:
@@ -480,6 +485,70 @@ def view_viz_cpu(infrastructure, jobs, work=True, stylefn=cmdstyle, current_user
480
485
  return RetScope.return_string
481
486
 
482
487
 
488
+ def view_viz_info(infrastructure, jobs, work=True, stylefn=cmdstyle, current_user=None):
489
+ # this is for hot reload
490
+ if not work:
491
+ return "UPDATE IN PROGRESS - PLZ W8 M8 B8"
492
+
493
+ # who is the current user?
494
+ if current_user is None:
495
+ import os
496
+ current_user = os.path.basename(os.environ['HOME'])
497
+
498
+ nodes = infrastructure.get_sorted_nodes()
499
+ infrast_down = all([x.status == 'down' for x in nodes])
500
+
501
+ class RetScope:
502
+ return_string = ''
503
+
504
+ def cust_print(thing, style=None):
505
+ RetScope.return_string += (thing if style is None else stylefn(style, thing)) + '\n'
506
+
507
+ if not infrast_down:
508
+ stalled_jobs = sum([j.state == 'S' for j in jobs])
509
+ max_gpu_len = max([len(n.gpu_model) + len(str(n.n_gpus)) if n.gpu_model is not None else 0 for n in nodes])
510
+ max_n_gpu_len = max([len(str(n.n_gpus)) for n in nodes])
511
+ max_cpu_len = max([len(str(n.cpus)) for n in nodes] + [len('CPUs')])
512
+ max_ram_len = max([len(str(n.mem // 1024)) for n in nodes])
513
+ RetScope.return_string += f'{"":{gpu_name_chars}} {"GPUs":{max_gpu_len+1}}\n'
514
+ for n in nodes:
515
+ gpu_model = n.gpu_model if n.gpu_model is not None else 'N/A'
516
+ # gpu_mem = n.gpu_mem if n.gpu_mem is not None else 'N/A'
517
+ mem = n.mem // 1024 if n.mem is not None else 'N/A'
518
+ n_gpus = n.n_gpus if n.n_gpus is not None else 'N/A'
519
+ n_cpus = n.cpus if n.cpus is not None else 'N/A'
520
+ # gpu_mem = GPU_MEM.get(gpu_model, 'N/A')
521
+ gpu_mem = ','.join([str(GPU_MEM.get(x, 'N/A')) for x in gpu_model.split(',')])
522
+ # gpu_str = f'{n_gpus}x{gpu_model:{max_gpu_len-(1*(len(str(n_gpus))))}}' if n_gpus != 0 else f'{"N/A":{max_gpu_len+1}}'
523
+ if n_gpus != 0:
524
+ gpu_str = f'{stylefn("RED", str(n_gpus))}'
525
+ gpu_str += f'x{stylefn("YELLOW", gpu_model)}'
526
+ gpu_str += f'@{stylefn("GREEN", str(gpu_mem))}G'
527
+ else:
528
+ gpu_str = f'{"N/A":{max_gpu_len+1}}'
529
+ RetScope.return_string += f'{_format_to(n.name, gpu_name_chars, "right")} {gpu_str}\n'
530
+ # RetScope.return_string += f'{"":{gpu_name_chars}} {"VRAM":{max_gpu_len+1}} {f"{mem}G"}\n'
531
+
532
+ onmain, waitString = maintenance_status(infrastructure)
533
+ if onmain:
534
+ cust_print(' ◀ ONGOING MAINTENANCE ▶ ', 'BG_MAGENTA')
535
+ elif len(infrastructure.maintenances):
536
+ cust_print(' ◀ MAINTENANCE in %4s ▶ ' % waitString, 'BG_MAGENTA')
537
+ elif len(jobs) == 0:
538
+ cust_print(' ◀ NO JOBS ▶ ', 'BG_GREEN')
539
+ elif stalled_jobs / len(jobs) > 0.5:
540
+ cust_print(' ◀ JOBS ON HOLD ▶ ', 'BG_YELLOW')
541
+ else:
542
+ cust_print('')
543
+
544
+ # print summary
545
+ # cust_print(''.join([' ['+ ram_occ + 'run', paused + 'hld', drain + 'drn', down + 'dwn', '()res]']))
546
+ # cust_print(''.join(['[' + ram_occ + f'{cpu_size}{cpu_unit}', cpu_paused + 'hld', cpu_drain + 'drn', cpu_pendr + 'pnd', cpu_down + 'dwn', '()res]']))
547
+ # gpuc = 'GREEN'
548
+
549
+ return RetScope.return_string
550
+
551
+
483
552
  if __name__ == '__main__':
484
553
  import sys
485
554
 
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes