nodeocc 1.0.17__tar.gz → 1.0.19__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.
- {nodeocc-1.0.17 → nodeocc-1.0.19}/PKG-INFO +1 -1
- {nodeocc-1.0.17 → nodeocc-1.0.19}/pyproject.toml +1 -1
- {nodeocc-1.0.17 → nodeocc-1.0.19}/src/controller/controller.py +1 -2
- {nodeocc-1.0.17 → nodeocc-1.0.19}/src/model/infrastructure.py +6 -1
- {nodeocc-1.0.17 → nodeocc-1.0.19}/src/nodeocc.egg-info/PKG-INFO +1 -1
- {nodeocc-1.0.17 → nodeocc-1.0.19}/src/view/__init__.py +0 -1
- {nodeocc-1.0.17 → nodeocc-1.0.19}/src/view/curses_multiwindow.py +13 -7
- {nodeocc-1.0.17 → nodeocc-1.0.19}/src/view/utils.py +7 -5
- {nodeocc-1.0.17 → nodeocc-1.0.19}/README.md +0 -0
- {nodeocc-1.0.17 → nodeocc-1.0.19}/setup.cfg +0 -0
- {nodeocc-1.0.17 → nodeocc-1.0.19}/setup.py +0 -0
- {nodeocc-1.0.17 → nodeocc-1.0.19}/src/__init__.py +0 -0
- {nodeocc-1.0.17 → nodeocc-1.0.19}/src/model/job.py +0 -0
- {nodeocc-1.0.17 → nodeocc-1.0.19}/src/nodeocc.egg-info/SOURCES.txt +0 -0
- {nodeocc-1.0.17 → nodeocc-1.0.19}/src/nodeocc.egg-info/dependency_links.txt +0 -0
- {nodeocc-1.0.17 → nodeocc-1.0.19}/src/nodeocc.egg-info/entry_points.txt +0 -0
- {nodeocc-1.0.17 → nodeocc-1.0.19}/src/nodeocc.egg-info/requires.txt +0 -0
- {nodeocc-1.0.17 → nodeocc-1.0.19}/src/nodeocc.egg-info/top_level.txt +0 -0
- {nodeocc-1.0.17 → nodeocc-1.0.19}/src/readers/compute_wait_time.py +0 -0
- {nodeocc-1.0.17 → nodeocc-1.0.19}/src/readers/slurmreader.py +0 -0
- {nodeocc-1.0.17 → nodeocc-1.0.19}/src/view/slurm_list.py +0 -0
- {nodeocc-1.0.17 → nodeocc-1.0.19}/src/view/slurm_viz.py +0 -0
- {nodeocc-1.0.17 → nodeocc-1.0.19}/src/view/styles.py +0 -0
|
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "nodeocc"
|
|
7
|
-
version = "1.0.
|
|
7
|
+
version = "1.0.19"
|
|
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"
|
|
@@ -136,7 +136,6 @@ def update_data_master(instance: Singleton):
|
|
|
136
136
|
queue_jobs = sorted(queue_jobs, key=lambda x: x['priority'], reverse=True)
|
|
137
137
|
maxlen = min(JOB_LIMIT_PER_MSG, len(running_jobs) + len(queue_jobs))
|
|
138
138
|
|
|
139
|
-
|
|
140
139
|
queue_jobs, extra_queue_jobs = queue_jobs[:maxlen - len(running_jobs)], queue_jobs[maxlen - len(running_jobs):]
|
|
141
140
|
cur_timestamp = time.time()
|
|
142
141
|
msg = json.dumps({'inf': inf.to_nested_dict(), 'jobs': running_jobs + queue_jobs, 'ts': str(cur_timestamp)})
|
|
@@ -195,7 +194,7 @@ async def get_data_slave(instance):
|
|
|
195
194
|
# check if timestamp is the same
|
|
196
195
|
if float(new_ts['ts']) != orig_timestamp:
|
|
197
196
|
instance.timeme(f"Timestamp mismatch, skipping")
|
|
198
|
-
return inf, jobs, avg_wait_time
|
|
197
|
+
return inf, jobs, avg_wait_time # return what we have
|
|
199
198
|
|
|
200
199
|
msg_store[msg_idx] += new_jobs
|
|
201
200
|
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
from typing import List
|
|
2
2
|
|
|
3
|
+
from view.utils import to_datetime
|
|
4
|
+
|
|
3
5
|
|
|
4
6
|
class Node:
|
|
5
7
|
"""
|
|
@@ -34,7 +36,7 @@ class Maintenance:
|
|
|
34
36
|
self.end_time = end_time
|
|
35
37
|
|
|
36
38
|
def __repr__(self):
|
|
37
|
-
return f'RESERVATION {self.start_time}
|
|
39
|
+
return f'RESERVATION - From: {self.start_time} To: {self.end_time} on {",".join(self.nodes)}'
|
|
38
40
|
|
|
39
41
|
@staticmethod
|
|
40
42
|
def from_dict(d):
|
|
@@ -94,3 +96,6 @@ class Infrastructure:
|
|
|
94
96
|
def get_sorted_nodes(self):
|
|
95
97
|
|
|
96
98
|
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)
|
|
99
|
+
|
|
100
|
+
def get_next_maintenance(self):
|
|
101
|
+
return sorted(self.maintenances, key=lambda x: to_datetime(x.start_time))[0] if len(self.maintenances) else None
|
|
@@ -92,16 +92,17 @@ class Singleton:
|
|
|
92
92
|
|
|
93
93
|
def clean_port_files(self):
|
|
94
94
|
nodename = socket.getfqdn().split('.')[0]
|
|
95
|
-
|
|
96
|
-
|
|
95
|
+
bp = os.path.join(self.basepath, nodename)
|
|
96
|
+
for f in os.listdir(bp):
|
|
97
|
+
if 'master_' in f and f.endswith('.portfile') and is_file_writable_byall(os.path.join(bp, f)):
|
|
97
98
|
# read pid from file
|
|
98
|
-
pid = Path(os.path.join(
|
|
99
|
+
pid = Path(os.path.join(bp, f)).read_text()
|
|
99
100
|
# assert no process is running
|
|
100
101
|
if psutil.pid_exists(int(pid)):
|
|
101
102
|
self.log(f"Process {pid} is still running, killing self")
|
|
102
103
|
return False
|
|
103
104
|
|
|
104
|
-
os.system(f"rm {os.path.join(
|
|
105
|
+
os.system(f"rm {os.path.join(bp, f)}")
|
|
105
106
|
return True
|
|
106
107
|
|
|
107
108
|
def __init__(self, args=None):
|
|
@@ -165,8 +166,9 @@ class Singleton:
|
|
|
165
166
|
Check if a port file exists in the basepath and the file has 666 permissions
|
|
166
167
|
"""
|
|
167
168
|
nodename = socket.getfqdn().split('.')[0]
|
|
168
|
-
|
|
169
|
-
|
|
169
|
+
bp = os.path.join(self.basepath, nodename)
|
|
170
|
+
portfiles = [os.path.join(bp, f) for f in os.listdir(bp)
|
|
171
|
+
if f.endswith('.portfile') and 'master_' in f]
|
|
170
172
|
portfiles = [f for f in portfiles if is_file_readable_byall(f) and is_file_writable_byall(f)]
|
|
171
173
|
return portfiles
|
|
172
174
|
|
|
@@ -197,7 +199,11 @@ class Singleton:
|
|
|
197
199
|
|
|
198
200
|
try:
|
|
199
201
|
# create file to store port with 666 permissions to file
|
|
200
|
-
|
|
202
|
+
# file is in folder basepath/nodename/master_{port}.portfile
|
|
203
|
+
bp = os.path.join(self.basepath, nodename)
|
|
204
|
+
if not os.path.exists(bp):
|
|
205
|
+
os.makedirs(bp, mode=0o777)
|
|
206
|
+
filepath = Path(os.path.join(bp, f"master_{self.port}.portfile"))
|
|
201
207
|
filepath.write_text(str(self.pid))
|
|
202
208
|
os.chmod(filepath, 0o666)
|
|
203
209
|
|
|
@@ -1,6 +1,8 @@
|
|
|
1
|
-
import numpy as np
|
|
2
1
|
import re
|
|
3
2
|
|
|
3
|
+
import numpy as np
|
|
4
|
+
|
|
5
|
+
|
|
4
6
|
|
|
5
7
|
def is_student_viz(user):
|
|
6
8
|
return user.user_group in ('studenti', 'tesisti') and not is_cvcs_viz(user)
|
|
@@ -22,7 +24,7 @@ def to_datetime(time_str):
|
|
|
22
24
|
return np.datetime64(re.match(r'\d+\s+(.*)\n', str(time_str)).group(1))
|
|
23
25
|
|
|
24
26
|
|
|
25
|
-
def to_datetime(time_str):
|
|
27
|
+
def to_datetime(time_str):
|
|
26
28
|
try:
|
|
27
29
|
dt = np.datetime64(re.match(r'\d+\s+(.*)\n', str(time_str)).group(1))
|
|
28
30
|
except:
|
|
@@ -35,9 +37,9 @@ def maintenance_status(infrastructure):
|
|
|
35
37
|
waitString = ''
|
|
36
38
|
if len(infrastructure.maintenances):
|
|
37
39
|
# time format: '0 2023-12-04 14:00:00\nName: StartTime, dtype: datetime64[ns]'
|
|
38
|
-
next_maintenance =
|
|
39
|
-
start_time = to_datetime(next_maintenance
|
|
40
|
-
end_time = to_datetime(next_maintenance
|
|
40
|
+
next_maintenance = infrastructure.get_next_maintenance()
|
|
41
|
+
start_time = to_datetime(next_maintenance.start_time)
|
|
42
|
+
end_time = to_datetime(next_maintenance.end_time)
|
|
41
43
|
time_to_maintenance = (start_time - np.datetime64('now')).astype(float)
|
|
42
44
|
# TODO: fix timezone
|
|
43
45
|
if time_to_maintenance < 0 and (end_time - np.datetime64('now')) > 0:
|
|
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
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|