Skip to content

Commit

Permalink
remote: add session feature in labgrid-client
Browse files Browse the repository at this point in the history
Adds a --session option to the labgrid-client monitor, reserve and acquire commands.
Enables the user to setup a monitor session. Reservations and place acquisitions which are registered to the same monitor sessions will be released if the monitor session ends.

Signed-off-by: Luke Hackwell <[email protected]>
  • Loading branch information
luke-hackwell committed Jan 6, 2025
1 parent b67bfc1 commit 718edd8
Show file tree
Hide file tree
Showing 9 changed files with 602 additions and 144 deletions.
17 changes: 17 additions & 0 deletions doc/usage.rst
Original file line number Diff line number Diff line change
Expand Up @@ -872,3 +872,20 @@ like this:
$ labgrid-client -p example allow sirius/john
To remove the allow it is currently necessary to unlock and lock the place.

Using places within a session
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
This workflow enables places and reservations to be released when a "session" is killed.
This is useful when running a session within a CI job, so that if the CI job is killed,
its places and reservations are freed.

Use the ``labgrid-client monitor --session <session-name>`` command to start a session.
The ``--session`` option can be used to tie reservations and place acquisitions to a session.
If the ``monitor`` process is killed then all reservations and place acquisitions will be freed.

Example:
.. code-block:: bash
$ labgrid-client monitor --session=my-session &
$ labgrid-client reserve --session=my-session tag=my-tag
$ labgrid-client -p +<token> acquire --session=my-session
11 changes: 9 additions & 2 deletions labgrid/remote/client.py
Original file line number Diff line number Diff line change
Expand Up @@ -120,6 +120,7 @@ async def start(self):
msg = labgrid_coordinator_pb2.ClientInMessage()
msg.startup.version = labgrid_version()
msg.startup.name = f"{self.gethostname()}/{self.getuser()}"
msg.startup.session = self.args.monitor_session if hasattr(self.args, "monitor_session") else ""
self.out_queue.put_nowait(msg)
msg = labgrid_coordinator_pb2.ClientInMessage()
msg.subscribe.all_places = True
Expand Down Expand Up @@ -669,7 +670,7 @@ async def acquire(self):
if not self.args.allow_unmatched:
self.check_matches(place)

request = labgrid_coordinator_pb2.AcquirePlaceRequest(placename=place.name)
request = labgrid_coordinator_pb2.AcquirePlaceRequest(placename=place.name, session=self.args.session)

try:
await self.stub.AcquirePlace(request)
Expand Down Expand Up @@ -1416,6 +1417,7 @@ def write_image(self):

async def create_reservation(self):
prio = self.args.prio
session = self.args.session

fltr = {}
for pair in self.args.filters:
Expand All @@ -1433,7 +1435,7 @@ async def create_reservation(self):
"main": labgrid_coordinator_pb2.Reservation.Filter(filter=fltr),
}

request = labgrid_coordinator_pb2.CreateReservationRequest(filters=fltrs, prio=prio)
request = labgrid_coordinator_pb2.CreateReservationRequest(filters=fltrs, prio=prio, session=session)

try:
response: labgrid_coordinator_pb2.CreateReservationResponse = await self.stub.CreateReservation(request)
Expand Down Expand Up @@ -1712,6 +1714,9 @@ def main():
subparser.set_defaults(func=ClientSession.complete)

subparser = subparsers.add_parser("monitor", help="monitor events from the coordinator")
subparser.add_argument(
"--session", default="", dest="monitor_session", help="Create a session to book places and reservations under"
)
subparser.set_defaults(func=ClientSession.do_monitor)

subparser = subparsers.add_parser("resources", aliases=("r",), help="list available resources")
Expand Down Expand Up @@ -1778,6 +1783,7 @@ def main():
subparser.add_argument(
"--allow-unmatched", action="store_true", help="allow missing resources for matches when locking the place"
)
subparser.add_argument("--session", default="", help="Acquire a place within a given session")
subparser.set_defaults(func=ClientSession.acquire)

subparser = subparsers.add_parser("release", aliases=("unlock",), help="release a place")
Expand Down Expand Up @@ -2001,6 +2007,7 @@ def main():
"--prio", type=float, default=0.0, help="priority relative to other reservations (default 0)"
)
subparser.add_argument("filters", metavar="KEY=VALUE", nargs="+", help="required tags")
subparser.add_argument("--session", default="", help="Make a reservation within a given session")
subparser.set_defaults(func=ClientSession.create_reservation)

subparser = subparsers.add_parser("cancel-reservation", help="cancel a reservation")
Expand Down
12 changes: 12 additions & 0 deletions labgrid/remote/common.py
Original file line number Diff line number Diff line change
Expand Up @@ -230,6 +230,7 @@ class Place:
created = attr.ib(default=attr.Factory(time.time))
changed = attr.ib(default=attr.Factory(time.time))
reservation = attr.ib(default=None)
session = attr.ib(default="")

def asdict(self):
# in the coordinator, we have resource objects, otherwise just a path
Expand All @@ -251,6 +252,7 @@ def asdict(self):
"created": self.created,
"changed": self.changed,
"reservation": self.reservation,
"session": self.session,
}

def update_from_pb2(self, place_pb2):
Expand Down Expand Up @@ -298,6 +300,8 @@ def show(self, level=0):
print(indent + f"changed: {datetime.fromtimestamp(self.changed)}")
if self.reservation:
print(indent + f"reservation: {self.reservation}")
if self.session:
print(indent + f"session: {self.session}")

def getmatch(self, resource_path):
"""Return the ResourceMatch object for the given resource path or None if not found.
Expand Down Expand Up @@ -350,6 +354,7 @@ def as_pb2(self):
place.created = self.created
if self.reservation:
place.reservation = self.reservation
place.session = self.session
for key, value in self.tags.items():
place.tags[key] = value
return place
Expand Down Expand Up @@ -377,6 +382,7 @@ def from_pb2(cls, pb2):
created=pb2.created,
changed=pb2.changed,
reservation=pb2.reservation if pb2.HasField("reservation") else None,
session=pb2.session,
)


Expand Down Expand Up @@ -406,6 +412,7 @@ class Reservation:
allocations = attr.ib(default=attr.Factory(dict), validator=attr.validators.instance_of(dict))
created = attr.ib(default=attr.Factory(time.time))
timeout = attr.ib(default=attr.Factory(lambda: time.time() + 60))
session = attr.ib(default="")

def asdict(self):
return {
Expand All @@ -416,6 +423,7 @@ def asdict(self):
"allocations": self.allocations,
"created": self.created,
"timeout": self.timeout,
"session": self.session,
}

def refresh(self, delta=60):
Expand All @@ -441,6 +449,8 @@ def show(self, level=0):
print(indent + f" {name}: {', '.join(allocation)}")
print(indent + f"created: {datetime.fromtimestamp(self.created)}")
print(indent + f"timeout: {datetime.fromtimestamp(self.timeout)}")
if self.session:
print(indent + f"session: {self.session}")

def as_pb2(self):
res = labgrid_coordinator_pb2.Reservation()
Expand All @@ -459,6 +469,7 @@ def as_pb2(self):
res.allocations.update({"main": allocation[0]})
res.created = self.created
res.timeout = self.timeout
res.session = self.session
return res

@classmethod
Expand All @@ -478,6 +489,7 @@ def from_pb2(cls, pb2: labgrid_coordinator_pb2.Reservation):
allocations=allocations,
created=pb2.created,
timeout=pb2.timeout,
session=pb2.session,
)


Expand Down
Loading

0 comments on commit 718edd8

Please sign in to comment.