Skip to content

Commit a9b59e4

Browse files
remote/client: make acquire/release operate on all RemotePlaces in env
If an environment config with multiple RemotePlaces is given via `-c`/`--config`, only the first RemotePlace is used on acquire/lock and release/unlock. Having multiple RemotePlaces is meant for multi place testing, so all RemotePlaces need to be acquired/released. This is implemented in a best effort way: If an error occurs, the remaining places are still tried to be acquired/released. A combined error message is shown if the operation failed on multiple places. When called with `-d`, tracebacks of all errors are shown. If any of the operations failed, the process exits with 1. CI pipelines should use this pattern: ```shell export LG_ENV=env.yaml labgrid-client unlock || true labgrid-client lock ``` Signed-off-by: Bastian Krause <bst@pengutronix.de>
1 parent 8f94eb6 commit a9b59e4

2 files changed

Lines changed: 84 additions & 2 deletions

File tree

labgrid/remote/client.py

Lines changed: 41 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -489,6 +489,17 @@ def get_place(self, place=None):
489489
raise UserError(f"pattern {pattern} matches multiple places ({', '.join(places)})")
490490
return self.places[places[0]]
491491

492+
def get_place_names_from_env(self):
493+
"""Returns a list of RemotePlace names found in the environment config."""
494+
places = []
495+
for role_config in self.env.config.get_targets().values():
496+
resources, _ = target_factory.normalize_config(role_config)
497+
remote_places = resources.get("RemotePlace", [])
498+
for place in remote_places:
499+
places.append(place)
500+
501+
return places
502+
492503
def get_idle_place(self, place=None):
493504
place = self.get_place(place)
494505
if place.acquired:
@@ -692,8 +703,22 @@ def check_matches(self, place):
692703
raise UserError(f"Match {match} has no matching remote resource")
693704

694705
async def acquire(self):
706+
errors = []
707+
places = self.get_place_names_from_env() if self.env else [self.args.place]
708+
for place in places:
709+
try:
710+
await self._acquire_place(place)
711+
except Error as e:
712+
errors.append(e)
713+
714+
if errors:
715+
if len(errors) == 1:
716+
raise errors[0]
717+
raise ErrorGroup("Multiple errors occurred during acquire", errors)
718+
719+
async def _acquire_place(self, place):
695720
"""Acquire a place, marking it unavailable for other clients"""
696-
place = self.get_place()
721+
place = self.get_place(place)
697722
if place.acquired:
698723
host, user = place.acquired.split("/")
699724
allowhelp = f"'labgrid-client -p {place.name} allow {self.gethostname()}/{self.getuser()}' on {host}."
@@ -738,8 +763,22 @@ async def acquire(self):
738763
raise ServerError(e.details())
739764

740765
async def release(self):
766+
errors = []
767+
places = self.get_place_names_from_env() if self.env else [self.args.place]
768+
for place in places:
769+
try:
770+
await self._release_place(place)
771+
except Error as e:
772+
errors.append(e)
773+
774+
if errors:
775+
if len(errors) == 1:
776+
raise errors[0]
777+
raise ErrorGroup("Multiple errors occurred during release", errors)
778+
779+
async def _release_place(self, place):
741780
"""Release a previously acquired place"""
742-
place = self.get_place()
781+
place = self.get_place(place)
743782
if not place.acquired:
744783
raise UserError(f"place {place.name} is not acquired")
745784
_, user = place.acquired.split("/")

tests/test_client.py

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -162,6 +162,49 @@ def test_place_acquire(place):
162162
spawn.close()
163163
assert spawn.exitstatus == 0, spawn.before.strip()
164164

165+
def test_place_acquire_multiple(create_place, tmpdir):
166+
# create multiple places
167+
place_names = ['test1', 'test2']
168+
for place_name in place_names:
169+
create_place(place_name)
170+
171+
# create env config with multiple RemotePlaces
172+
p = tmpdir.join('config.yaml')
173+
p.write('targets:')
174+
for place_name in place_names:
175+
p.write(
176+
f"""
177+
{place_name}:
178+
resources:
179+
RemotePlace:
180+
name: {place_name}
181+
""",
182+
mode='a',
183+
)
184+
185+
# acquire all places in env config
186+
with pexpect.spawn(f'python -m labgrid.remote.client -c {p} acquire') as spawn:
187+
spawn.expect(pexpect.EOF)
188+
assert spawn.exitstatus == 0, spawn.before.strip()
189+
190+
# check 'who'
191+
with pexpect.spawn('python -m labgrid.remote.client who') as spawn:
192+
spawn.expect(pexpect.EOF)
193+
for place_name in place_names:
194+
assert place_name.encode('utf-8') in spawn.before
195+
196+
assert spawn.exitstatus == 0, spawn.before.strip()
197+
198+
# release all places in env config
199+
with pexpect.spawn(f'python -m labgrid.remote.client -c {p} release') as spawn:
200+
spawn.expect(pexpect.EOF)
201+
assert spawn.exitstatus == 0, spawn.before.strip()
202+
203+
# check 'who' again
204+
with pexpect.spawn('python -m labgrid.remote.client who') as spawn:
205+
spawn.expect('User.*Host.*Place.*Changed\r\n')
206+
assert not spawn.before, spawn.before
207+
165208
def test_place_acquire_enforce(place):
166209
with pexpect.spawn('python -m labgrid.remote.client -p test add-match does/not/exist') as spawn:
167210
spawn.expect(pexpect.EOF)

0 commit comments

Comments
 (0)