nodeocc 1.0.22__tar.gz → 1.0.24__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.22 → nodeocc-1.0.24}/PKG-INFO +2 -2
  2. {nodeocc-1.0.22 → nodeocc-1.0.24}/pyproject.toml +1 -1
  3. {nodeocc-1.0.22 → nodeocc-1.0.24}/src/controller/controller.py +14 -4
  4. {nodeocc-1.0.22 → nodeocc-1.0.24}/src/nodeocc.egg-info/PKG-INFO +2 -2
  5. {nodeocc-1.0.22 → nodeocc-1.0.24}/src/readers/slurmreader.py +1 -1
  6. {nodeocc-1.0.22 → nodeocc-1.0.24}/src/view/curses_multiwindow.py +35 -23
  7. {nodeocc-1.0.22 → nodeocc-1.0.24}/README.md +0 -0
  8. {nodeocc-1.0.22 → nodeocc-1.0.24}/setup.cfg +0 -0
  9. {nodeocc-1.0.22 → nodeocc-1.0.24}/setup.py +0 -0
  10. {nodeocc-1.0.22 → nodeocc-1.0.24}/src/__init__.py +0 -0
  11. {nodeocc-1.0.22 → nodeocc-1.0.24}/src/model/infrastructure.py +0 -0
  12. {nodeocc-1.0.22 → nodeocc-1.0.24}/src/model/job.py +0 -0
  13. {nodeocc-1.0.22 → nodeocc-1.0.24}/src/nodeocc.egg-info/SOURCES.txt +0 -0
  14. {nodeocc-1.0.22 → nodeocc-1.0.24}/src/nodeocc.egg-info/dependency_links.txt +0 -0
  15. {nodeocc-1.0.22 → nodeocc-1.0.24}/src/nodeocc.egg-info/entry_points.txt +0 -0
  16. {nodeocc-1.0.22 → nodeocc-1.0.24}/src/nodeocc.egg-info/requires.txt +0 -0
  17. {nodeocc-1.0.22 → nodeocc-1.0.24}/src/nodeocc.egg-info/top_level.txt +0 -0
  18. {nodeocc-1.0.22 → nodeocc-1.0.24}/src/readers/compute_wait_time.py +0 -0
  19. {nodeocc-1.0.22 → nodeocc-1.0.24}/src/view/__init__.py +0 -0
  20. {nodeocc-1.0.22 → nodeocc-1.0.24}/src/view/slurm_list.py +0 -0
  21. {nodeocc-1.0.22 → nodeocc-1.0.24}/src/view/slurm_viz.py +0 -0
  22. {nodeocc-1.0.22 → nodeocc-1.0.24}/src/view/styles.py +0 -0
  23. {nodeocc-1.0.22 → nodeocc-1.0.24}/src/view/utils.py +0 -0
@@ -1,6 +1,6 @@
1
- Metadata-Version: 2.1
1
+ Metadata-Version: 2.2
2
2
  Name: nodeocc
3
- Version: 1.0.22
3
+ Version: 1.0.24
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.22"
7
+ version = "1.0.24"
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"
@@ -1,3 +1,4 @@
1
+ import random
1
2
  import sys
2
3
  import os
3
4
  from pathlib import Path
@@ -64,11 +65,13 @@ BEGIN_DELIM = "!{$"
64
65
  END_DELIM_ENCODED = "!}$".encode('utf-8')
65
66
  EXTRA_MSG_BEGIN_DELIM = "!{#"
66
67
  MAX_BUF = 65535
67
- MAX_MSG_LEN = 1024 * 1024 * 1024
68
- JOB_LIMIT_PER_MSG = 60
68
+ JOB_LIMIT_PER_MSG = 40
69
+ TIMEOUT_MAX = 3
70
+ n_timeout = 0
69
71
 
70
72
  parser = argparse.ArgumentParser(description='Visualize slurm jobs')
71
73
  parser.add_argument('--debug', action='store_true', help='Enable logging')
74
+ parser.add_argument('--override', action='store_true', help='Enable logging')
72
75
  parser.add_argument('--master_only', action='store_true', help='Disable all prints - only run in background')
73
76
  parser.add_argument('--basepath', type=str, default='/tmp', help='Base path for nodeocc. Must be readable by all users')
74
77
  args = parser.parse_args()
@@ -168,6 +171,7 @@ def decode_msg_slave(data, delim=BEGIN_DELIM):
168
171
 
169
172
 
170
173
  async def get_data_slave(instance):
174
+ global n_timeout
171
175
  inf, jobs, avg_wait_time = None, None, None
172
176
  try:
173
177
  # listen for data from master
@@ -210,12 +214,18 @@ async def get_data_slave(instance):
210
214
  instance.timeme(f"- no data")
211
215
  return None, None, None, None
212
216
 
217
+ n_timeout = 0
218
+
213
219
  except BlockingIOError as e:
214
220
  pass
215
221
 
216
222
  except TimeoutError as e:
217
- instance.log(f"TIMEOUT")
218
- try_open_socket_as_slave(instance)
223
+ n_timeout += 1
224
+ instance.log(f"TIMEOUT {n_timeout}/{TIMEOUT_MAX}")
225
+ if n_timeout > TIMEOUT_MAX:
226
+ try_open_socket_as_slave(instance, force=True)
227
+
228
+ time.sleep(random.randint(100, 10000) / 1000)
219
229
 
220
230
  return inf, jobs, avg_wait_time
221
231
 
@@ -1,6 +1,6 @@
1
- Metadata-Version: 2.1
1
+ Metadata-Version: 2.2
2
2
  Name: nodeocc
3
- Version: 1.0.22
3
+ Version: 1.0.24
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
@@ -36,7 +36,7 @@ def _match_str_wildcard(s, w):
36
36
 
37
37
  def _node_from_sinfo(x):
38
38
  n = Node(_node_preproc(x['NODELIST']), x.n_gpu, x.m_gpu, x.reserved, None, x.CPUS, x.MEMORY) # , feature=x.AVAIL_FEATURES)
39
- if x['STATE'] == 'drng' or 'drain' in x['STATE']:
39
+ if 'drng' in x['STATE'] or 'drain' in x['STATE']:
40
40
  n.status = 'drain'
41
41
  elif x['STATE'].upper() in ('MAINT', 'DOWN', 'DOWN*', 'FAIL', 'FAILING', 'FAIL*') or _match_str_wildcard(x['STATE'].lower(), 'inv*'):
42
42
  n.status = 'down'
@@ -44,7 +44,7 @@ def is_file_readable_byall(file_path):
44
44
  return os.stat(file_path).st_mode & stat.S_IROTH
45
45
 
46
46
 
47
- def try_open_socket_as_slave(instance):
47
+ def try_open_socket_as_slave(instance, force=False):
48
48
  if len(instance.get_port_files()) == 0:
49
49
  raise Exception("No master running")
50
50
 
@@ -57,7 +57,7 @@ def try_open_socket_as_slave(instance):
57
57
  instance.log(f"- MASTER HAS CHANGED PORT: {instance.port} vs {cur_port}")
58
58
 
59
59
  instance.port = cur_port
60
- instance._open_socket_as_slave(instance.port)
60
+ instance._open_socket_as_slave(instance.port, force=force)
61
61
 
62
62
  if instance.try_open_counter > 5:
63
63
  instance.err(f"Could not open socket as slave on port {instance.port}")
@@ -90,7 +90,7 @@ class Singleton:
90
90
  logging.basicConfig(level=logging.DEBUG, format='%(asctime)s - %(message)s', datefmt='%d-%b-%y %H:%M:%S',
91
91
  handlers=[handler])
92
92
 
93
- def clean_port_files(self):
93
+ def clean_port_files(self, force=False):
94
94
  nodename = socket.getfqdn().split('.')[0]
95
95
  bp = os.path.join(self.basepath, nodename)
96
96
  if not os.path.exists(bp):
@@ -100,7 +100,7 @@ class Singleton:
100
100
  # read pid from file
101
101
  pid = Path(os.path.join(bp, f)).read_text()
102
102
  # assert no process is running
103
- if psutil.pid_exists(int(pid)):
103
+ if psutil.pid_exists(int(pid)) and not force:
104
104
  self.log(f"Process {pid} is still running, killing self")
105
105
  return False
106
106
 
@@ -148,8 +148,8 @@ class Singleton:
148
148
 
149
149
  self.is_master = not self.check_existing_master_running()
150
150
 
151
- if self.is_master:
152
- self.port = self.create_socket_as_master()
151
+ if self.is_master or args.override:
152
+ self.port = self.create_socket_as_master(force=args.override)
153
153
  else:
154
154
  try_open_socket_as_slave(self)
155
155
 
@@ -176,28 +176,14 @@ class Singleton:
176
176
  portfiles = [f for f in portfiles if is_file_readable_byall(f) and is_file_writable_byall(f)]
177
177
  return portfiles
178
178
 
179
- def create_socket_as_master(self):
179
+ def update_port_file(self, force=False):
180
180
  nodename = socket.getfqdn().split('.')[0]
181
181
 
182
- # create udp socket for broadcasting
183
- self.sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM, socket.IPPROTO_UDP)
184
-
185
- self.sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
186
-
187
- # Enable broadcasting mode
188
- self.sock.setsockopt(socket.SOL_SOCKET, socket.SO_BROADCAST, 1)
189
-
190
- self.sock.bind(('', 0))
191
-
192
- self.port = self.sock.getsockname()[1]
193
- self.sock.settimeout(2)
194
- self.log(f"Socket created as master on port {self.port}")
195
-
196
182
  # get pid of current process
197
183
  self.pid = os.getpid()
198
184
 
199
185
  # clean up old port files
200
- kill_ok = self.clean_port_files()
186
+ kill_ok = self.clean_port_files(force=force)
201
187
  if not kill_ok:
202
188
  return None
203
189
 
@@ -205,12 +191,19 @@ class Singleton:
205
191
  # create file to store port with 666 permissions to file
206
192
  # file is in folder basepath/nodename/master_{port}.portfile
207
193
  bp = os.path.join(self.basepath, nodename)
194
+ created = False
208
195
  if not os.path.exists(bp):
196
+ created = True
209
197
  os.makedirs(bp, mode=0o777)
198
+
210
199
  filepath = Path(os.path.join(bp, f"master_{self.port}.portfile"))
211
200
  filepath.write_text(str(self.pid))
212
201
  os.chmod(filepath, 0o666)
213
202
 
203
+ # force 777 permissions
204
+ if created:
205
+ os.chmod(bp, 0o777)
206
+
214
207
  self.port_filepath = filepath
215
208
  except Exception as e:
216
209
  self.err(f"Could not create port file: {e}")
@@ -221,9 +214,26 @@ class Singleton:
221
214
  os.system(f"rm {filepath}")
222
215
  return None
223
216
 
217
+ def create_socket_as_master(self, force=False):
218
+ # create udp socket for broadcasting
219
+ self.sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM, socket.IPPROTO_UDP)
220
+
221
+ self.sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
222
+
223
+ # Enable broadcasting mode
224
+ self.sock.setsockopt(socket.SOL_SOCKET, socket.SO_BROADCAST, 1)
225
+
226
+ self.sock.bind(('', 0))
227
+
228
+ self.port = self.sock.getsockname()[1]
229
+ self.sock.settimeout(2)
230
+ self.log(f"Socket created as master on port {self.port}")
231
+
232
+ self.update_port_file(force=force)
233
+
224
234
  return self.port
225
235
 
226
- def _open_socket_as_slave(self, port):
236
+ def _open_socket_as_slave(self, port, force=False):
227
237
  try:
228
238
  # create udp socket for broadcasting
229
239
  self.sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM, socket.IPPROTO_UDP)
@@ -238,6 +248,8 @@ class Singleton:
238
248
  self.sock.settimeout(6.5)
239
249
  self.log(f"Socket opened on port {self.port}")
240
250
 
251
+ self.update_port_file(force=force)
252
+
241
253
  self.try_open_counter = 0
242
254
  return self.port
243
255
  except Exception as e:
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