nodeocc 1.0.7__tar.gz → 1.0.9__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.7 → nodeocc-1.0.9}/PKG-INFO +1 -1
  2. {nodeocc-1.0.7 → nodeocc-1.0.9}/pyproject.toml +1 -1
  3. {nodeocc-1.0.7 → nodeocc-1.0.9}/src/controller/controller.py +1 -1
  4. {nodeocc-1.0.7 → nodeocc-1.0.9}/src/model/infrastructure.py +1 -0
  5. {nodeocc-1.0.7 → nodeocc-1.0.9}/src/nodeocc.egg-info/PKG-INFO +1 -1
  6. {nodeocc-1.0.7 → nodeocc-1.0.9}/src/readers/slurmreader.py +12 -8
  7. {nodeocc-1.0.7 → nodeocc-1.0.9}/src/view/__init__.py +6 -2
  8. {nodeocc-1.0.7 → nodeocc-1.0.9}/src/view/slurm_viz.py +36 -20
  9. {nodeocc-1.0.7 → nodeocc-1.0.9}/src/view/utils.py +6 -2
  10. {nodeocc-1.0.7 → nodeocc-1.0.9}/README.md +0 -0
  11. {nodeocc-1.0.7 → nodeocc-1.0.9}/setup.cfg +0 -0
  12. {nodeocc-1.0.7 → nodeocc-1.0.9}/setup.py +0 -0
  13. {nodeocc-1.0.7 → nodeocc-1.0.9}/src/__init__.py +0 -0
  14. {nodeocc-1.0.7 → nodeocc-1.0.9}/src/model/job.py +0 -0
  15. {nodeocc-1.0.7 → nodeocc-1.0.9}/src/nodeocc.egg-info/SOURCES.txt +0 -0
  16. {nodeocc-1.0.7 → nodeocc-1.0.9}/src/nodeocc.egg-info/dependency_links.txt +0 -0
  17. {nodeocc-1.0.7 → nodeocc-1.0.9}/src/nodeocc.egg-info/entry_points.txt +0 -0
  18. {nodeocc-1.0.7 → nodeocc-1.0.9}/src/nodeocc.egg-info/requires.txt +0 -0
  19. {nodeocc-1.0.7 → nodeocc-1.0.9}/src/nodeocc.egg-info/top_level.txt +0 -0
  20. {nodeocc-1.0.7 → nodeocc-1.0.9}/src/readers/compute_wait_time.py +0 -0
  21. {nodeocc-1.0.7 → nodeocc-1.0.9}/src/view/curses_multiwindow.py +0 -0
  22. {nodeocc-1.0.7 → nodeocc-1.0.9}/src/view/slurm_list.py +0 -0
  23. {nodeocc-1.0.7 → nodeocc-1.0.9}/src/view/styles.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: nodeocc
3
- Version: 1.0.7
3
+ Version: 1.0.9
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.7"
7
+ version = "1.0.9"
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"
@@ -66,7 +66,7 @@ END_DELIM_ENCODED = "!}$".encode('utf-8')
66
66
  EXTRA_MSG_BEGIN_DELIM = "!{#"
67
67
  MAX_BUF = 65535
68
68
  MAX_MSG_LEN = 1024 * 1024 * 1024
69
- JOB_LIMIT_PER_MSG = 100
69
+ JOB_LIMIT_PER_MSG = 60
70
70
 
71
71
  parser = argparse.ArgumentParser(description='Visualize slurm jobs')
72
72
  parser.add_argument('--debug', action='store_true', help='Enable logging')
@@ -10,6 +10,7 @@ class Node:
10
10
  def __init__(self, name, n_gpus, gpu_model, reserved, status, cpus, mem) -> None:
11
11
  self.name = name
12
12
  self.n_gpus = n_gpus
13
+ # self.feature = feature
13
14
  self.gpu_model = gpu_model
14
15
  self.reserved = reserved
15
16
  assert reserved in ('no', 'yes', 'pending')
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: nodeocc
3
- Version: 1.0.7
3
+ Version: 1.0.9
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
@@ -31,7 +31,7 @@ def _split_column(df, column):
31
31
 
32
32
 
33
33
  def _node_from_sinfo(x):
34
- n = Node(_node_preproc(x['NODELIST']), x.n_gpu, x.m_gpu, x.reserved, None, x.CPUS, x.MEMORY)
34
+ n = Node(_node_preproc(x['NODELIST']), x.n_gpu, x.m_gpu, x.reserved, None, x.CPUS, x.MEMORY)#, feature=x.AVAIL_FEATURES)
35
35
  if x['STATE'] == 'drng' or 'drain' in x['STATE']:
36
36
  n.status = 'drain'
37
37
  elif x['STATE'].upper() in ('MAINT', 'DOWN', 'DOWN*', 'FAIL', 'FAILING', 'FAIL*'):
@@ -53,18 +53,20 @@ def _parse_gpu_models(line):
53
53
  return ','.join([x.split(':')[1] for x in line])
54
54
 
55
55
 
56
- def _read_nodes(reservations, pending_res):
57
- sinfo_df = pd.read_csv(StringIO(os.popen(r'sinfo -N -o "%N;%G;%t;%m;%c"').read()), sep=';')
56
+ def _read_nodes(reservations=None, pending_res=None, return_raw=False):
57
+ sinfo_df = pd.read_csv(StringIO(os.popen(r'sinfo -N -o "%N;%G;%t;%m;%c;%f"').read()), sep=';')
58
58
  sinfo_df = _split_column(sinfo_df, 'NODELIST').drop_duplicates()
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]))
59
+ sinfo_df.loc[:,'n_gpu'] = sinfo_df['GRES'].apply(lambda x: 0 if x.split('(')[0].split(':')[0] in ('(null)', '') else int(x.split('(')[0].split(':')[-1]))
60
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)
61
+ sinfo_df.loc[:,'m_gpu'] = sinfo_df['GRES'].str.split(';').apply(_parse_gpu_models)
62
62
  sinfo_df['reserved'] = 'no'
63
63
  if reservations is not None:
64
64
  sinfo_df.loc[sinfo_df['NODELIST'].isin(reservations.Nodes.unique()), 'reserved'] = 'yes'
65
65
  if pending_res is not None:
66
66
  sinfo_df.loc[sinfo_df['NODELIST'].isin(pending_res.Nodes.unique()), 'reserved'] = 'pending'
67
-
67
+ if return_raw:
68
+ return sinfo_df
69
+
68
70
  return sinfo_df.apply(_node_from_sinfo, axis=1).tolist()
69
71
 
70
72
 
@@ -83,8 +85,10 @@ def _read_maintenances():
83
85
  if not len(reservations):
84
86
  return [], None, None
85
87
  reservations = pd.DataFrame.from_records(reservations)
86
- reservations['StartTime'] = str(pd.to_datetime(reservations['StartTime']))
87
- reservations['EndTime'] = str(pd.to_datetime(reservations['EndTime']))
88
+ reservations['StartTime'] = reservations['StartTime'].apply(lambda x:str(pd.to_datetime(x)))
89
+ reservations['EndTime'] = reservations['EndTime'].apply(lambda x:str(pd.to_datetime(x)))
90
+ # reservations['StartTime'] = str(pd.to_datetime(reservations['StartTime']))
91
+ # reservations['EndTime'] = str(pd.to_datetime(reservations['EndTime']))
88
92
  reservations = _split_column(reservations, 'Nodes')
89
93
  maint_flag = reservations['Flags'].str.contains('MAINT') & \
90
94
  reservations['Flags'].str.contains('SPEC_NODES') & \
@@ -1,12 +1,16 @@
1
1
  import curses
2
2
  from . import slurm_list, slurm_viz, styles
3
3
 
4
+
4
5
  def update_views(stdscr, instance, filter):
5
6
  if instance.jobs is None or instance.inf is None:
6
7
  return
7
8
 
8
- instance.rens = slurm_list.view_list(instance, instance.jobs, filter, stylefn=styles.crsstyler, width=instance.left_width if hasattr(instance, 'left_width') else 72, jit=instance.job_id_type if hasattr(instance, 'job_id_type') else 'agg')
9
+ instance.rens = slurm_list.view_list(
10
+ instance, instance.jobs, filter, stylefn=styles.crsstyler, width=instance.left_width if hasattr(
11
+ instance, 'left_width') else 72, jit=instance.job_id_type if hasattr(
12
+ instance, 'job_id_type') else 'agg')
9
13
  instance.timeme(f"- print job list")
10
14
 
11
15
  instance.nocc = slurm_viz.view_viz(instance.inf, instance.jobs, stylefn=styles.crsstyler, mode=instance.view_mode if hasattr(instance, 'view_mode') else 'gpu')
12
- instance.timeme(f"- view nodeocc")
16
+ instance.timeme(f"- view nodeocc")
@@ -1,6 +1,7 @@
1
1
  import random
2
2
 
3
3
  import pandas as pd
4
+ import numpy as np
4
5
 
5
6
  from view.styles import _format_to, cmdstyle
6
7
  from view.utils import is_cvcs_viz, is_student_viz, maintenance_status, to_font
@@ -99,6 +100,8 @@ cpu_pendr = '⧖'
99
100
  cpu_paused = '▚'
100
101
  cpu_down = '⨯'
101
102
 
103
+ GLOBAL_NODES = None
104
+
102
105
 
103
106
  def get_ram_block(megs):
104
107
  return int(round(megs / 1024 / mem_size))
@@ -356,9 +359,6 @@ def get_cpu_icon(stat):
356
359
  }.get(stat, cpu_avail)
357
360
 
358
361
 
359
- GPU_MEM = {"K80": 12, "P100": 16, "1080": 11, "TitanXP": 12, "2080": 11, "RTX5000": 16, "RTX6000": 24, "RTXA5000": 24, "A40": 48}
360
-
361
-
362
362
  def view_viz_cpu(infrastructure, jobs, work=True, stylefn=cmdstyle, current_user=None):
363
363
  # this is for hot reload
364
364
  if not work:
@@ -490,13 +490,22 @@ def view_viz_info(infrastructure, jobs, work=True, stylefn=cmdstyle, current_use
490
490
  if not work:
491
491
  return "UPDATE IN PROGRESS - PLZ W8 M8 B8"
492
492
 
493
+ global GLOBAL_NODES
494
+ if GLOBAL_NODES is None:
495
+ from readers.slurmreader import _read_nodes, _node_preproc
496
+ GLOBAL_NODES = _read_nodes(return_raw=True)
497
+ GLOBAL_NODES.loc[:, 'nodename'] = GLOBAL_NODES.NODELIST.apply(_node_preproc)
498
+ prior = ['n/a', None, 'A40', 'RTXA5000', 'RTX6000', 'RTX5000', '2080', 'V100', '1080', 'P100', 'K80']
499
+ reord_idx = sorted(zip(GLOBAL_NODES['m_gpu'], GLOBAL_NODES.index), key=lambda g: prior.index(g[0].split(',')[0]) if g[0].split(',')[0] in prior else -1)
500
+ GLOBAL_NODES = GLOBAL_NODES.loc[np.array([i for x, i in reord_idx])].reset_index(drop=True)
501
+
493
502
  # who is the current user?
494
503
  if current_user is None:
495
504
  import os
496
505
  current_user = os.path.basename(os.environ['HOME'])
497
506
 
498
- nodes = infrastructure.get_sorted_nodes()
499
- infrast_down = all([x.status == 'down' for x in nodes])
507
+ # nodes = infrastructure.get_sorted_nodes()
508
+ infrast_down = all([x.status == 'down' for x in infrastructure.get_sorted_nodes()])
500
509
 
501
510
  class RetScope:
502
511
  return_string = ''
@@ -506,27 +515,34 @@ def view_viz_info(infrastructure, jobs, work=True, stylefn=cmdstyle, current_use
506
515
 
507
516
  if not infrast_down:
508
517
  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'
518
+ # 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])
519
+ # max_n_gpu_len = max([len(str(n.n_gpus)) for n in nodes])
520
+ # max_cpu_len = max([len(str(n.cpus)) for n in nodes] + [len('CPUs')])
521
+ # max_ram_len = max([len(str(n.mem // 1024)) for n in nodes])
522
+ feat_str = stylefn("GREEN", "FEATURES")
523
+ RetScope.return_string += f'{"":{gpu_name_chars}} GPU {feat_str}\n'
524
+ for i, n in GLOBAL_NODES.iterrows():
525
+ gpu_model = n.m_gpu # if n.m_gpu is not None else 'N/A'
516
526
  # 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'
527
+ mem = n.MEMORY // 1024 if n.MEMORY is not None else 'N/A'
528
+ n_gpus = n.n_gpu if n.n_gpu is not None else 'N/A'
529
+ n_cpus = n.CPUS # if n.CPUS is not None else 'N/A'
520
530
  # 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(',')])
531
+
532
+ gpu_feature = n.AVAIL_FEATURES # if n.feature is not None else '(null)'
533
+
534
+ # gpu_mem = ','.join([str(GPU_MEM.get(x, 'N/A')) for x in gpu_model.split(',')])
522
535
  # 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
536
  if n_gpus != 0:
524
537
  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'
538
+ if gpu_feature != '(null)':
539
+ gpu_str += f'x{stylefn("GREEN", gpu_feature)}'
540
+ else:
541
+ gpu_str += f'@({stylefn("YELLOW", gpu_model)})'
527
542
  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'
543
+ gpu_str = "N/A" # f'{"N/A":{max_gpu_len+1}}'
544
+
545
+ RetScope.return_string += f'{_format_to(n.nodename, gpu_name_chars, "right")} {gpu_str}\n'
530
546
  # RetScope.return_string += f'{"":{gpu_name_chars}} {"VRAM":{max_gpu_len+1}} {f"{mem}G"}\n'
531
547
 
532
548
  onmain, waitString = maintenance_status(infrastructure)
@@ -22,8 +22,12 @@ def to_datetime(time_str):
22
22
  return np.datetime64(re.match(r'\d+\s+(.*)\n', str(time_str)).group(1))
23
23
 
24
24
 
25
- def to_datetime(time_str):
26
- return np.datetime64(re.match(r'\d+\s+(.*)\n', str(time_str)).group(1))
25
+ def to_datetime(time_str):
26
+ try:
27
+ dt = np.datetime64(re.match(r'\d+\s+(.*)\n', str(time_str)).group(1))
28
+ except:
29
+ dt = np.datetime64(str(time_str))
30
+ return dt
27
31
 
28
32
 
29
33
  def maintenance_status(infrastructure):
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes