@@ -451,32 +451,40 @@ def sync_instances():
451451 action .apply (list (nodes ))
452452
453453
454- def reconfigure_slurm ():
454+ def reconfigure_slurm () -> bool :
455+ """Fetch new config and regenerate Slurm config files.
456+
457+ Returns True if config was updated and a scontrol reconfigure is needed.
458+ On the controller, the reconfigure is deferred until after topology
459+ regeneration to avoid issuing two sequential scontrol reconfigure calls
460+ in the same sync cycle (which can cause a socket timeout).
461+ """
455462 update_msg = "*** slurm configuration was updated ***"
456463
457464 if lookup ().cfg .hybrid :
458465 # terraform handles generating the config.yaml, don't do it here
459- return
466+ return False
460467
461468 upd , cfg_new = util .fetch_config ()
462469 if not upd :
463470 log .debug ("No changes in config detected." )
464- return
471+ return False
465472 log .debug ("Changes in config detected. Reconfiguring Slurm now." )
466473 util .update_config (cfg_new )
467474
468475 if lookup ().is_controller :
469476 conf .get_generator (lookup ()).generate_configs ()
470477
471- log .info ("Restarting slurmctld to make changes take effect." )
478+ # Defer slurmctld restart and scontrol reconfigure:
479+ # Running them now would cause slurmctld to temporarily load new node defs with old topology.
480+ # This causes slurmctld to hang/timeout when update_topology tries to reconfigure it subsequently.
481+ log .info ("Config files regenerated. Deferring scontrol reconfigure until after topology update." )
472482 try :
473- # TODO: consider removing "restart" since "reconfigure" should restart slurmctld as well
474- run ("sudo systemctl restart slurmctld.service" , check = False )
475- util .scontrol_reconfigure (lookup ())
483+ util .run (f"wall '{ update_msg } '" , timeout = 30 )
476484 except Exception :
477- log .exception ("failed to reconfigure slurmctld" )
478- util .run (f"wall '{ update_msg } '" , timeout = 30 )
485+ log .warning ("failed to broadcast wall message" )
479486 log .debug ("Done." )
487+ return True
480488 elif lookup ().instance_role_safe == "compute" :
481489 log .info ("Restarting slurmd to make changes take effect." )
482490 run ("systemctl restart slurmd" )
@@ -487,19 +495,23 @@ def reconfigure_slurm():
487495 run ("systemctl restart sackd" )
488496 util .run (f"wall '{ update_msg } '" , timeout = 30 )
489497 log .debug ("Done." )
498+ return False
490499
491500
492501def _generate_topology (lkp : util .Lookup ) -> Tuple [bool , Any ]:
493502 return conf .get_generator (lkp ).generate_topology_data ()
494503
495- def update_topology (lkp : util .Lookup ) -> None :
504+ def update_topology (lkp : util .Lookup ) -> Tuple [bool , Any ]:
505+ """Regenerate topology files.
506+
507+ Returns (updated, summary) so the caller can decide when to
508+ issue the scontrol reconfigure and persist the summary.
509+ """
496510 updated , summary = _generate_topology (lkp ) # type: ignore[attr-defined]
497511
498512 if updated :
499- log .info ("Topology configuration updated. Reconfiguring Slurm." )
500- util .scontrol_reconfigure (lkp )
501- # Safe summary only after Slurm got reconfigured, so summary reflects Slurm POV
502- summary .dump (lkp )
513+ log .info ("Topology configuration updated." )
514+ return updated , summary
503515
504516
505517def delete_reservation (lkp : util .Lookup , reservation_name : str ) -> None :
@@ -662,11 +674,19 @@ def main():
662674 lkp = lookup ()
663675 if util .should_mount_slurm_bucket () and not lkp .is_controller :
664676 return
677+
678+ # Track if reconfigure_slurm changed cloud.conf so we can trigger
679+ # a deferred scontrol reconfigure inside update_topology
680+ config_changed = False
665681 try :
666- reconfigure_slurm ()
682+ config_changed = reconfigure_slurm ()
667683 except Exception :
668684 log .exception ("failed to reconfigure slurm" )
669685 if lkp .is_controller :
686+ if not util .is_active_controller (lkp ):
687+ log .info ("Local controller is in STANDBY mode. Skipping instance sync, repair, and reconfigure." )
688+ return
689+
670690 try :
671691 process_messages (lkp )
672692 except :
@@ -692,11 +712,30 @@ def main():
692712 except Exception :
693713 log .exception ("failed to sync placement groups" )
694714
715+ topology_changed = False
716+ topology_summary = None
695717 try :
696- update_topology (lkp )
718+ topology_changed , topology_summary = update_topology (lkp )
697719 except Exception :
698720 log .exception ("failed to update topology" )
699721
722+ # Single deferred reconfigure for both config and topology changes.
723+ # Placed here so it runs even if topology generation failed above.
724+ if config_changed or topology_changed :
725+ reasons = []
726+ if config_changed :
727+ reasons .append ("config updated" )
728+ if topology_changed :
729+ reasons .append ("topology changed" )
730+ try :
731+ log .info (f"Reconfiguring Slurm ({ ', ' .join (reasons )} )." )
732+ util .scontrol_reconfigure (lkp )
733+ # Only dump summary after successful reconfigure so it reflects Slurm's view
734+ if topology_changed and topology_summary is not None :
735+ topology_summary .dump (lkp )
736+ except Exception :
737+ log .exception ("failed to reconfigure slurmctld" )
738+
700739 try :
701740 sync_maintenance_reservation (lkp )
702741 except Exception :
0 commit comments