nodeocc 1.0.1__tar.gz → 1.0.3__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.1 → nodeocc-1.0.3}/PKG-INFO +2 -2
  2. {nodeocc-1.0.1 → nodeocc-1.0.3}/pyproject.toml +2 -2
  3. {nodeocc-1.0.1 → nodeocc-1.0.3}/src/controller/controller.py +20 -10
  4. {nodeocc-1.0.1 → nodeocc-1.0.3}/src/model/infrastructure.py +1 -1
  5. {nodeocc-1.0.1 → nodeocc-1.0.3}/src/nodeocc.egg-info/PKG-INFO +2 -2
  6. {nodeocc-1.0.1 → nodeocc-1.0.3}/src/readers/slurmreader.py +43 -25
  7. {nodeocc-1.0.1 → nodeocc-1.0.3}/src/view/curses_multiwindow.py +3 -3
  8. {nodeocc-1.0.1 → nodeocc-1.0.3}/README.md +0 -0
  9. {nodeocc-1.0.1 → nodeocc-1.0.3}/setup.cfg +0 -0
  10. {nodeocc-1.0.1 → nodeocc-1.0.3}/setup.py +0 -0
  11. {nodeocc-1.0.1 → nodeocc-1.0.3}/src/__init__.py +0 -0
  12. {nodeocc-1.0.1 → nodeocc-1.0.3}/src/model/job.py +0 -0
  13. {nodeocc-1.0.1 → nodeocc-1.0.3}/src/nodeocc.egg-info/SOURCES.txt +0 -0
  14. {nodeocc-1.0.1 → nodeocc-1.0.3}/src/nodeocc.egg-info/dependency_links.txt +0 -0
  15. {nodeocc-1.0.1 → nodeocc-1.0.3}/src/nodeocc.egg-info/entry_points.txt +0 -0
  16. {nodeocc-1.0.1 → nodeocc-1.0.3}/src/nodeocc.egg-info/requires.txt +0 -0
  17. {nodeocc-1.0.1 → nodeocc-1.0.3}/src/nodeocc.egg-info/top_level.txt +0 -0
  18. {nodeocc-1.0.1 → nodeocc-1.0.3}/src/readers/compute_wait_time.py +0 -0
  19. {nodeocc-1.0.1 → nodeocc-1.0.3}/src/view/__init__.py +0 -0
  20. {nodeocc-1.0.1 → nodeocc-1.0.3}/src/view/slurm_list.py +0 -0
  21. {nodeocc-1.0.1 → nodeocc-1.0.3}/src/view/slurm_viz.py +0 -0
  22. {nodeocc-1.0.1 → nodeocc-1.0.3}/src/view/styles.py +0 -0
  23. {nodeocc-1.0.1 → nodeocc-1.0.3}/src/view/utils.py +0 -0
@@ -1,11 +1,11 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: nodeocc
3
- Version: 1.0.1
3
+ Version: 1.0.3
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
7
7
  License: MIT
8
- Project-URL: Repository, https://github.com/mbosc/slurm_nodeocc
8
+ Project-URL: Repository, https://github.com/apanariello4/slurm_nodeocc
9
9
  Keywords: Slurm,GPU,TUI,CLI,HPC,cluster,allocations,nodeocc
10
10
  Classifier: Development Status :: 4 - Beta
11
11
  Classifier: Environment :: Console :: Curses
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "nodeocc"
7
- version = "1.0.1"
7
+ version = "1.0.3"
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"
@@ -30,7 +30,7 @@ classifiers = [
30
30
  dependencies = ["pandas", "numpy"]
31
31
 
32
32
  [project.urls]
33
- Repository = "https://github.com/mbosc/slurm_nodeocc"
33
+ Repository = "https://github.com/apanariello4/slurm_nodeocc"
34
34
 
35
35
  [project.scripts]
36
36
  nodeocc = "controller.controller:_main"
@@ -29,28 +29,35 @@ import importlib.metadata
29
29
 
30
30
  program_name = 'nodeocc'
31
31
  version_number = importlib.metadata.version(program_name)
32
+ updated = False
32
33
 
33
- # get newest version from pip
34
+ BBLUE = '\033[1;34m'
35
+ END = '\033[0m'
34
36
  try:
35
37
  url = "https://pypi.python.org/pypi/nodeocc/json"
36
38
  import requests
37
39
  newest_version = json.loads(requests.get(url).text)['info']['version']
40
+ print(BBLUE, end='')
38
41
  if newest_version.split('.')[0] > version_number.split('.')[0]:
39
42
  print(f"New major version available: {newest_version} (current: {version_number})")
40
- for _ in range(5):
41
- print('.', end='')
42
- time.sleep(0.5)
43
+ print(f"Please update with 'pip install {program_name} --upgrade'")
44
+ time.sleep(7)
43
45
  elif newest_version.split('.')[1] > version_number.split('.')[1]:
44
46
  print(f"New minor version available: {newest_version} (current: {version_number})")
45
- for _ in range(3):
46
- print('.', end='')
47
- time.sleep(0.5)
47
+ print(f"Please update with 'pip install {program_name} --upgrade'")
48
+ time.sleep(7)
48
49
  elif newest_version.split('.')[2] > version_number.split('.')[2]:
49
50
  print(f"New patch version available: {newest_version} (current: {version_number})")
50
- time.sleep(0.5)
51
+ print(f"Please update with 'pip install {program_name} --upgrade'")
52
+ time.sleep(7)
53
+ else:
54
+ print(f"Up to date: {version_number}")
55
+ updated = True
56
+ time.sleep(3)
57
+ print(END, end='')
51
58
  except Exception as e:
52
- print(f"Could not get newest version from pip, check if {program_name} is up to date")
53
- time.sleep(1)
59
+ print(f"{BBLUE}Could not get newest version from pip, check if {program_name} is up to date{END}")
60
+ time.sleep(5)
54
61
 
55
62
 
56
63
  BEGIN_DELIM = "!{$"
@@ -246,6 +253,9 @@ def _main():
246
253
  # configure singleton
247
254
 
248
255
  Singleton.getInstance().signature = f"{program_name} v{version_number}"
256
+ Singleton.getInstance().version = version_number
257
+ Singleton.getInstance().updated = updated
258
+ Singleton.getInstance().newest_version = newest_version
249
259
  Singleton.getInstance().fetch_fn = get_all
250
260
 
251
261
  wrapper(display_main)
@@ -67,7 +67,7 @@ class Infrastructure:
67
67
  self.cpu_limit_grp = cpu_limit_grp
68
68
  self.cpu_limit_stu = cpu_limit_stu
69
69
  self.cpu_limit_stugrp = cpu_limit_stugrp
70
- self.prior = ['RTX6000', '2080', 'V100', 'RTXA5000', 'RTX5000', '1080', 'P100', 'n/a', None, 'K80']
70
+ self.prior = ['n/a', None, 'A40', 'RTXA5000', 'RTX6000', 'RTX5000', '2080', 'V100', '1080', 'P100', 'K80']
71
71
 
72
72
  @staticmethod
73
73
  def from_dict(d):
@@ -1,11 +1,11 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: nodeocc
3
- Version: 1.0.1
3
+ Version: 1.0.3
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
7
7
  License: MIT
8
- Project-URL: Repository, https://github.com/mbosc/slurm_nodeocc
8
+ Project-URL: Repository, https://github.com/apanariello4/slurm_nodeocc
9
9
  Keywords: Slurm,GPU,TUI,CLI,HPC,cluster,allocations,nodeocc
10
10
  Classifier: Development Status :: 4 - Beta
11
11
  Classifier: Environment :: Console :: Curses
@@ -1,6 +1,5 @@
1
- import json
2
1
  import os
3
- import time
2
+ import re
4
3
  from io import StringIO
5
4
 
6
5
  import pandas as pd
@@ -152,27 +151,27 @@ def _load_joblet_meta(line):
152
151
 
153
152
 
154
153
  def _gpus_per_joblet(line):
155
- if pd.isna(line['gpus_per_job']) and pd.isna(line['gpus_per_node']) and pd.isna(line['gpus_per_task']):
154
+ if pd.isna(line['ALL_JOB_NODES']):
156
155
  return 0
157
- elif not pd.isna(line['gpus_per_node']):
158
- return line['gpus_per_node'] * (line['NODES'] if pd.isna(line['NODELIST']) else 1)
156
+ if line['ALL_JOB_NODES'].count(',') == 0:
157
+ return line['TRES_ALLOC'].split('gpu=')[-1].split(',')[0] if 'gpu=' in line['TRES_ALLOC'] else 0
159
158
  else:
160
- if line['ST'] == 'PD':
161
- return line['gpus_per_job'] if not pd.isna(line['gpus_per_job']) else line['gpus_per_task']
162
- gpus_id = line['joblet_meta_str']
163
- gpus_id = sum([x.split('IDX:')[-1].split(')')[0].split(',') for x in gpus_id if 'gpu' in x], [])
164
- gpus_n = len(gpus_id) + sum([len(range(int(x.split('-')[1].split()[0]) - int(x.split('-')[0]))) for x in gpus_id if '-' in x])
165
- return gpus_n
159
+ node = line['NODELIST']
160
+ node_idx = line['ALL_JOB_NODES'].split(',').index(node)
161
+ return int(re.findall(r'gpu:(\d+)', line['TRES_PER_NODE'])[node_idx]) if 'gpu:' in line['TRES_PER_NODE'] else 0
166
162
 
167
163
 
168
164
  def _cpus_per_joblet(line):
169
- if line['ST'] == 'PD':
170
- return line['MIN_CPUS']
165
+ if pd.isna(line['ALL_JOB_NODES']):
166
+ return 0
167
+ if line['ALL_JOB_NODES'].count(',') == 0:
168
+ return int(str(line['TRES_ALLOC']).split('cpu=')[-1].split(',')[0])
171
169
  else:
172
- cpus_id = line['joblet_meta_str']
173
- cpus = sum([x.split('CPU_IDs=')[-1].split()[0].split(',') for x in cpus_id], [])
174
- cpus_n = len(cpus) + sum([len(range(int(x.split('-')[1].split()[0]) - int(x.split('-')[0]))) for x in cpus if '-' in x])
175
- return cpus_n
170
+ nodes = line['NODES']
171
+ tot_gpu = int(str(line['TRES_ALLOC']).split('gpu=')[-1].split(',')[0]) if 'gpu=' in str(line['TRES_ALLOC']) else 0
172
+ tot_cpus = int(str(line['TRES_ALLOC']).split('cpu=')[-1].split(',')[0])
173
+ n_gpus = line['joblet_gpus']
174
+ return int(tot_cpus) // int(nodes) if tot_gpu == 0 else int(tot_cpus) // tot_gpu * n_gpus
176
175
 
177
176
 
178
177
  memunits = {
@@ -193,12 +192,26 @@ def parse_mem(thing):
193
192
 
194
193
 
195
194
  def _mem_per_joblet(line):
196
- if line['ST'] == 'PD':
197
- return parse_mem(line['MIN_MEMORY'])
198
- else:
199
- mem_id = line['joblet_meta_str']
200
- mem = sum([int(x.split('Mem=')[-1].split()[0]) for x in mem_id])
195
+ if pd.isna(line['ALL_JOB_NODES']):
196
+ return 0
197
+ try:
198
+ mem = line['TRES_ALLOC'].split('mem=')[-1].split(',')[0]
199
+ if 'G' in mem:
200
+ mem = int(mem.split('G')[0]) * 1024
201
+ elif 'M' in mem:
202
+ mem = int(mem.split('M')[0])
203
+ else:
204
+ mem = int(mem)
205
+ except BaseException:
206
+ return 0
207
+
208
+ if line['ALL_JOB_NODES'].count(',') == 0:
201
209
  return mem
210
+ else:
211
+ nodes = line['NODES']
212
+ tot_gpu = int(str(line['TRES_ALLOC']).split('gpu=')[-1].split(',')[0]) if 'gpu=' in str(line['TRES_ALLOC']) else 0
213
+ n_gpus = line['joblet_gpus']
214
+ return int(mem) // int(nodes) if tot_gpu == 0 else int(mem) // tot_gpu * n_gpus
202
215
 
203
216
 
204
217
  def _true_jobid(line):
@@ -257,15 +270,20 @@ def read_jobs():
257
270
  instance.timeme(f"\t- squeue and dataframe creation")
258
271
 
259
272
  squeue_df['JOBID'] = squeue_df['JOBID'].apply(str)
273
+ squeue_df['ALL_JOB_NODES'] = squeue_df['NODELIST']
260
274
  squeue_df = _split_column(squeue_df, 'NODELIST')
275
+
261
276
  squeue_df['gpus_per_node'] = squeue_df['TRES_PER_NODE'].apply(lambda x: try_parse_gpu(x) if isinstance(x, str) else x)
262
277
  squeue_df['gpus_per_job'] = squeue_df['TRES_PER_JOB'].apply(lambda x: try_parse_gpu(x) if isinstance(x, str) else x)
263
278
  squeue_df['gpus_per_task'] = squeue_df['TRES_PER_TASK'].apply(lambda x: try_parse_gpu(x) if isinstance(x, str) else x)
264
279
 
265
280
  try:
266
- squeue_df['joblet_mem'] = squeue_df['TRES_ALLOC'].apply(get_mem_joblet)
267
- squeue_df['joblet_gpus'] = squeue_df['TRES_ALLOC'].apply(lambda l: str(l).split('gpu=')[-1].split(',')[0] if 'gpu=' in str(l) else 0)
268
- squeue_df['joblet_cpus'] = squeue_df['TRES_ALLOC'].apply(lambda l: int(str(l).split('cpu=')[-1].split(',')[0]))
281
+ # squeue_df['joblet_gpus'] = squeue_df['TRES_ALLOC'].apply(lambda l: str(l).split('gpu=')[-1].split(',')[0] if 'gpu=' in str(l) else 0)
282
+ squeue_df['joblet_gpus'] = squeue_df.apply(_gpus_per_joblet, axis=1)
283
+ # squeue_df['joblet_cpus'] = squeue_df['TRES_ALLOC'].apply(lambda l: int(str(l).split('cpu=')[-1].split(',')[0]))
284
+ squeue_df['joblet_cpus'] = squeue_df.apply(_cpus_per_joblet, axis=1)
285
+ # squeue_df['joblet_mem'] = squeue_df['TRES_ALLOC'].apply(get_mem_joblet)
286
+ squeue_df['joblet_mem'] = squeue_df.apply(_mem_per_joblet, axis=1)
269
287
  except Exception as e:
270
288
  instance.err(f"Exception PD: {e}")
271
289
 
@@ -89,7 +89,7 @@ class Singleton:
89
89
  self.basepath = self.args.basepath
90
90
 
91
91
  if self.args.daemon_only:
92
- handler = RotatingFileHandler(os.path.join(self.basepath, '.master_log_n.txt'), maxBytes=5 * 1024 * 1024, backupCount=2, mode='w')
92
+ handler = RotatingFileHandler(os.path.join(self.basepath, '.master_log.txt'), maxBytes=5 * 1024 * 1024, backupCount=2, mode='w')
93
93
  logging.basicConfig(level=logging.DEBUG, format='%(asctime)s - %(message)s', datefmt='%d-%b-%y %H:%M:%S',
94
94
  handlers=[handler])
95
95
  # set logging file
@@ -459,8 +459,8 @@ def update_screen(stdscr, instance):
459
459
  # get slurm user partition
460
460
  stdscr.addstr(lines - 1, xoffset + 62 + 2, f'(Avg time {instance.avg_wait_time})', curses.color_pair(2))
461
461
 
462
- signature = instance.signature
463
- stdscr.addstr(lines - 1, columns - 2 - len(signature), signature)
462
+ signature = instance.signature if instance.updated else f'{instance.version} -> {instance.newest_version}'
463
+ stdscr.addstr(lines - 1, columns - 2 - len(signature), signature, curses.color_pair(2) if instance.updated else curses.color_pair(3))
464
464
 
465
465
  stdscr.refresh()
466
466
  curses.doupdate()
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes