@@ -205,7 +205,11 @@ async def wait(self):
205205 self .expired = True
206206
207207
208- class ExporterError (Exception ):
208+ class CoordinatorError (Exception ):
209+ pass
210+
211+
212+ class ExporterError (CoordinatorError ):
209213 pass
210214
211215
@@ -650,9 +654,12 @@ async def _acquire_resource(self, place, resource):
650654 request .place_name = place .name
651655 cmd = ExporterCommand (request )
652656 self .get_exporter_by_name (resource .path [0 ]).queue .put_nowait (cmd )
653- await cmd .wait ()
657+ try :
658+ await cmd .wait ()
659+ except asyncio .TimeoutError as e :
660+ raise ExporterError ("timed out waiting for exporter while acquiring resource" ) from e
654661 if not cmd .response .success :
655- raise ExporterError (f"failed to acquire { resource } ( { cmd . response . reason } ) " )
662+ raise ExporterError (cmd . response . reason or "exporter returned failure without a reason" )
656663 if resource .acquired != place .name :
657664 logging .warning ("resource %s not acquired by this place after acquire request" , resource )
658665
@@ -663,33 +670,38 @@ async def _acquire_resources(self, place, resources):
663670 # all resources need to be free
664671 for resource in resources :
665672 if resource .acquired :
666- return False
673+ raise CoordinatorError ( f" { resource } is already acquired by { resource . acquired } " )
667674
668675 for otherplace in self .places .values ():
669676 for oldres in otherplace .acquired_resources :
670677 if resource .path == oldres .path :
671678 logging .info (
672679 "Conflicting orphaned resource %s for acquire request for place %s" , oldres , place .name
673680 )
674- return False
681+ raise CoordinatorError (
682+ f"conflicting orphaned resource { oldres } for acquire request for place { place .name } "
683+ )
675684
676685 # acquire resources
677686 acquired = []
678687 try :
679688 for resource in resources :
680689 await self ._acquire_resource (place , resource )
681690 acquired .append (resource )
682- except Exception :
691+ except Exception as e :
683692 logging .exception ("failed to acquire %s" , resource )
684693 # cleanup
685- await self ._release_resources (place , acquired )
686- return False
694+ try :
695+ await self ._release_resources (place , acquired )
696+ except CoordinatorError :
697+ logging .exception ("failed to release acquired resources during acquire cleanup" )
698+ if isinstance (e , CoordinatorError ):
699+ raise
700+ raise CoordinatorError (f"failed to acquire { resource } " ) from e
687701
688702 for resource in resources :
689703 place .acquired_resources .append (resource )
690704
691- return True
692-
693705 async def _release_resources (self , place , resources , callback = True ):
694706 assert self .lock .locked ()
695707
@@ -701,6 +713,7 @@ async def _release_resources(self, place, resources, callback=True):
701713 except ValueError :
702714 pass
703715
716+ failure = None
704717 for resource in resources :
705718 if resource .orphaned :
706719 continue
@@ -714,18 +727,27 @@ async def _release_resources(self, place, resources, callback=True):
714727 # request.place_name is left unset to indicate release
715728 cmd = ExporterCommand (request )
716729 self .get_exporter_by_name (resource .path [0 ]).queue .put_nowait (cmd )
717- await cmd .wait ()
730+ try :
731+ await cmd .wait ()
732+ except asyncio .TimeoutError as e :
733+ raise ExporterError ("timed out waiting for exporter while releasing resource" ) from e
718734 if not cmd .response .success :
719- raise ExporterError (f"failed to release { resource } ( { cmd . response . reason } ) " )
735+ raise ExporterError (cmd . response . reason or "exporter returned failure without a reason" )
720736 if resource .acquired :
721737 logging .warning ("resource %s still acquired after release request" , resource )
722- except (ExporterError , TimeoutError ):
738+ except ExporterError as e :
739+ if failure is None :
740+ failure = e
723741 logging .exception ("failed to release %s" , resource )
724742 # at leaset try to notify the clients
725743 try :
726744 self ._publish_resource (resource )
727745 except :
728746 logging .exception ("failed to publish released resource %s" , resource )
747+ if failure :
748+ if isinstance (failure , ExporterError ):
749+ raise failure
750+ raise CoordinatorError (f"failed to release resources for place { place .name } " ) from failure
729751
730752 async def _synchronize_resources (self ):
731753 assert self .lock .locked ()
@@ -877,10 +899,18 @@ async def AcquirePlace(self, request, context):
877899 if not place .hasmatch (resource .path ):
878900 continue
879901 resources .append (resource )
880- if not await self ._acquire_resources (place , resources ):
902+ try :
903+ await self ._acquire_resources (place , resources )
904+ except CoordinatorError as e :
881905 # revert earlier change
882906 place .acquired = None
883- await context .abort (grpc .StatusCode .FAILED_PRECONDITION , f"Failed to acquire resources for place { name } " )
907+ message = f"Failed to acquire resources for place { name } "
908+ if str (e ):
909+ message += f": { e } "
910+ await context .abort (
911+ grpc .StatusCode .FAILED_PRECONDITION ,
912+ message ,
913+ )
884914 place .touch ()
885915 self ._publish_place (place )
886916 self .save_later ()
@@ -904,7 +934,13 @@ async def ReleasePlace(self, request, context):
904934 if fromuser and place .acquired != fromuser :
905935 return labgrid_coordinator_pb2 .ReleasePlaceResponse ()
906936
907- await self ._release_resources (place , place .acquired_resources )
937+ try :
938+ await self ._release_resources (place , place .acquired_resources )
939+ except CoordinatorError as e :
940+ message = f"Failed to release resources for place { name } "
941+ if str (e ):
942+ message += f": { e } "
943+ await context .abort (grpc .StatusCode .FAILED_PRECONDITION , message )
908944
909945 place .acquired = None
910946 place .allowed = set ()
0 commit comments