5
5
starts/stops/polls controllers, engines, etc.
6
6
"""
7
7
import asyncio
8
+ import atexit
8
9
import inspect
9
10
import logging
10
11
import os
@@ -304,7 +305,7 @@ async def start_engines(self, n=None, engine_set_id=None):
304
305
n = cpu_count ()
305
306
self .log .info (f"Starting { n or '' } engines with { self .engine_launcher_class } " )
306
307
r = engine_set .start (n )
307
- engine_set .on_stop (self ._engines_stopped )
308
+ engine_set .on_stop (partial ( self ._engines_stopped , engine_set_id ) )
308
309
if inspect .isawaitable (r ):
309
310
await r
310
311
return engine_set_id
@@ -347,8 +348,12 @@ async def stop_engine(self, engine_id):
347
348
"""
348
349
raise NotImplementedError ("How do we find an engine by id?" )
349
350
350
- async def restart_engine_set (self , engine_set_id ):
351
+ async def restart_engines (self , engine_set_id = None ):
351
352
"""Restart an engine set"""
353
+ if engine_set_id is None :
354
+ for engine_set_id in list (self ._engine_sets ):
355
+ await self .restart_engines (engine_set_id )
356
+ return
352
357
engine_set = self ._engine_sets [engine_set_id ]
353
358
n = engine_set .n
354
359
await self .stop_engines (engine_set_id )
@@ -362,19 +367,28 @@ async def restart_engine(self, engine_id):
362
367
"""
363
368
raise NotImplementedError ("How do we find an engine by id?" )
364
369
365
- async def signal_engine (self , engine_id , signum ):
370
+ async def signal_engine (self , signum , engine_id ):
366
371
"""Signal one engine
367
372
368
373
*May* signal all engines in a set,
369
374
depending on EngineSet features (e.g. mpiexec)
370
375
"""
371
376
raise NotImplementedError ("How do we find an engine by id?" )
372
377
373
- async def signal_engines (self , engine_set_id , signum ):
374
- """Signal all engines in a set"""
378
+ async def signal_engines (self , signum , engine_set_id = None ):
379
+ """Signal all engines in a set
380
+
381
+ If no engine set is specified, signal all engine sets.
382
+ """
383
+ if engine_set_id is None :
384
+ for engine_set_id in list (self ._engine_sets ):
385
+ await self .signal_engines (signum , engine_set_id )
386
+ return
375
387
self .log .info (f"Sending signal { signum } to engine(s) { engine_set_id } " )
376
388
engine_set = self ._engine_sets [engine_set_id ]
377
- engine_set .signal (signum )
389
+ r = engine_set .signal (signum )
390
+ if inspect .isawaitable (r ):
391
+ await r
378
392
379
393
async def stop_controller (self ):
380
394
"""Stop the controller"""
@@ -443,19 +457,37 @@ def __exit__(self, *args):
443
457
class ClusterManager (LoggingConfigurable ):
444
458
"""A manager of clusters
445
459
446
- Wraps Cluster, adding"""
460
+ Wraps Cluster, adding lookup/list by cluster id
461
+ """
447
462
448
463
_clusters = Dict (help = "My cluster objects" )
449
464
450
- def load_clusters (self ):
465
+ def load_clusters (self , serialized_state ):
451
466
"""Load serialized cluster state"""
452
- raise NotImplementedError ()
467
+ raise NotImplementedError ("Serializing clusters not implemented" )
453
468
454
469
def list_clusters (self ):
455
470
"""List current clusters"""
471
+ # TODO: what should we return?
472
+ # just cluster ids or the full dict?
473
+ # just cluster ids for now
474
+ return sorted (self ._clusters )
456
475
457
- def new_cluster (self , cluster_cls ):
476
+ def new_cluster (self , cluster_cls , ** kwargs ):
458
477
"""Create a new cluster"""
478
+ cluster = Cluster (parent = self )
479
+ if cluster .cluster_id in self ._clusters :
480
+ raise KeyError (f"Cluster { cluster .cluster_id } already exists!" )
481
+ self ._clusters [cluster ]
482
+
483
+ def get_cluster (self , cluster_id ):
484
+ """Get a Cluster object by id"""
485
+ return self ._clusters [cluster_id ]
486
+
487
+ def remove_cluster (self , cluster_id ):
488
+ """Delete a cluster by id"""
489
+ # TODO: check running?
490
+ del self ._clusters [cluster_id ]
459
491
460
492
def _cluster_method (self , method_name , cluster_id , * args , ** kwargs ):
461
493
"""Wrapper around single-cluster methods
0 commit comments