diff --git a/labgrid/remote/client.py b/labgrid/remote/client.py index 4d2eb0bfa..da4a0f189 100755 --- a/labgrid/remote/client.py +++ b/labgrid/remote/client.py @@ -32,6 +32,11 @@ # TODO: drop if Python >= 3.11 guaranteed from exceptiongroup import ExceptionGroup # pylint: disable=redefined-builtin +from labgrid.remote.grpc.interceptor.client import ( + IdentityClientStreamStreamInterceptor, + IdentityClientUnaryUnaryInterceptor, +) + from .common import ( ResourceEntry, ResourceMatch, @@ -120,9 +125,19 @@ def __attrs_post_init__(self): ("grpc.http2.max_pings_without_data", 0), # no limit ] + identity = { + "username": self.getuser(), + "hostname": self.gethostname(), + "user_agent": f"labgrid-client {labgrid_version()}", + } + interceptors = [ + IdentityClientUnaryUnaryInterceptor(**identity), + IdentityClientStreamStreamInterceptor(**identity), + ] self.channel = grpc.aio.insecure_channel( target=self.address, options=channel_options, + interceptors=interceptors, ) self.stub = labgrid_coordinator_pb2_grpc.CoordinatorStub(self.channel) @@ -139,10 +154,6 @@ async def start(self): self.pump_task = self.loop.create_task(self.message_pump()) msg = labgrid_coordinator_pb2.ClientInMessage() - msg.startup.version = labgrid_version() - msg.startup.name = f"{self.gethostname()}/{self.getuser()}" - self.out_queue.put_nowait(msg) - msg = labgrid_coordinator_pb2.ClientInMessage() msg.subscribe.all_places = True self.out_queue.put_nowait(msg) msg = labgrid_coordinator_pb2.ClientInMessage() @@ -475,11 +486,11 @@ def _check_allowed(self, place): host, user = place.acquired.split("/") if user != self.getuser(): raise UserError( - f"place {place.name} is not acquired by your user, acquired by {user}. To work simultaneously, {user} can execute labgrid-client -p {place.name} allow {self.gethostname()}/{self.getuser()}" + f"place {place.name} is not acquired by your user, acquired by {user}. To work simultaneously, {user} can execute labgrid-client -p {place.name} share {self.gethostname()}/{self.getuser()}" ) if host != self.gethostname(): raise UserError( - f"place {place.name} is not acquired on this computer, acquired on {host}. To allow this host, use labgrid-client -p {place.name} allow {self.gethostname()}/{self.getuser()} on the other host" + f"place {place.name} is not acquired on this computer, acquired on {host}. To share with this host, use labgrid-client -p {place.name} share {self.gethostname()}/{self.getuser()} on the other host" ) def get_place(self, place=None): @@ -555,15 +566,15 @@ async def print_place(self): print(f"Matching resource '{name}' ({exporter}/{group_name}/{resource.cls}/{resource_name}):") # pylint: disable=line-too-long print(indent(pformat(resource.asdict()), prefix=" ")) - async def add_place(self): - """Add a place to the coordinator""" + async def create_place(self): + """Create a place on the coordinator""" name = self.args.place if not name: raise UserError("missing place name. Set with -p or via env var LG_PLACE") - request = labgrid_coordinator_pb2.AddPlaceRequest(name=name) + request = labgrid_coordinator_pb2.CreatePlaceRequest(name=name) try: - await self.stub.AddPlace(request) + await self.stub.CreatePlace(request) await self.sync_with_coordinator() except grpc.aio.AioRpcError as e: raise ServerError(e.details()) @@ -727,17 +738,17 @@ async def _acquire_place(self, place): place = self.get_place(place) if place.acquired: host, user = place.acquired.split("/") - allowhelp = f"'labgrid-client -p {place.name} allow {self.gethostname()}/{self.getuser()}' on {host}." + sharehelp = f"'labgrid-client -p {place.name} share {self.gethostname()}/{self.getuser()}' on {host}." if self.getuser() == user: if self.gethostname() == host: raise UserError(f"You have already acquired place {place.name}.") else: raise UserError( - f"You have already acquired place {place.name} on {host}. To work simultaneously, execute {allowhelp}" + f"You have already acquired place {place.name} on {host}. To work simultaneously, execute {sharehelp}" ) else: raise UserError( - f"Place {place.name} is already acquired by {place.acquired}. To work simultaneously, {user} can execute {allowhelp}" + f"Place {place.name} is already acquired by {place.acquired}. To work simultaneously, {user} can execute {sharehelp}" ) if not self.args.allow_unmatched: self.check_matches(place) @@ -821,20 +832,35 @@ async def release_from(self): print(f"{self.args.acquired} has released place {place.name}") - async def allow(self): - """Allow another use access to a previously acquired place""" + async def share(self): + """Share access to a previously acquired place with another user""" place = self.get_acquired_place() if "/" not in self.args.user: raise UserError(f"user {self.args.user} must be in / format") - request = labgrid_coordinator_pb2.AllowPlaceRequest(placename=place.name, user=self.args.user) + request = labgrid_coordinator_pb2.SharePlaceRequest(name=place.name, user=self.args.user) try: - await self.stub.AllowPlace(request) + await self.stub.SharePlace(request) await self.sync_with_coordinator() except grpc.aio.AioRpcError as e: raise ServerError(e.details()) - print(f"allowed {self.args.user} for place {place.name}") + print(f"shared place {place.name} with {self.args.user}") + + async def unshare(self): + """Remove another user's access to a previously acquired place""" + place = self.get_acquired_place() + if "/" not in self.args.user: + raise UserError(f"user {self.args.user} must be in / format") + request = labgrid_coordinator_pb2.UnsharePlaceRequest(name=place.name, user=self.args.user) + + try: + await self.stub.UnsharePlace(request) + await self.sync_with_coordinator() + except grpc.aio.AioRpcError as e: + raise ServerError(e.details()) + + print(f"unshared place {place.name} with {self.args.user}") def get_target_resources(self, place): self._check_allowed(place) @@ -1593,10 +1619,12 @@ async def cancel_reservation(self): async def _wait_reservation(self, token: str, verbose=True): while True: - request = labgrid_coordinator_pb2.PollReservationRequest(token=token) + request = labgrid_coordinator_pb2.RefreshReservationRequest(reservation_id=token) try: - response: labgrid_coordinator_pb2.PollReservationResponse = await self.stub.PollReservation(request) + response: labgrid_coordinator_pb2.RefreshReservationResponse = await self.stub.RefreshReservation( + request + ) except grpc.aio.AioRpcError as e: raise ServerError(e.details()) @@ -1917,9 +1945,9 @@ def get_parser(auto_doc_mode=False) -> "argparse.ArgumentParser | AutoProgramArg subparser = subparsers.add_parser( "create", - help="add a new place with the name specified via --place or the LG_PLACE environment variable", + help="create a new place with the name specified via --place or the LG_PLACE environment variable", ) - subparser.set_defaults(func=ClientSession.add_place) + subparser.set_defaults(func=ClientSession.create_place) subparser = subparsers.add_parser("delete", help="delete an existing place") subparser.set_defaults(func=ClientSession.del_place) @@ -1974,9 +2002,13 @@ def get_parser(auto_doc_mode=False) -> "argparse.ArgumentParser | AutoProgramArg subparser.add_argument("acquired", metavar="HOST/USER", help="User and host to match against when releasing") subparser.set_defaults(func=ClientSession.release_from) - subparser = subparsers.add_parser("allow", help="allow another user to access a place") + subparser = subparsers.add_parser("share", aliases=("allow",), help="share a place with another user") + subparser.add_argument("user", help="/") + subparser.set_defaults(func=ClientSession.share) + + subparser = subparsers.add_parser("unshare", help="remove another user's access to a place") subparser.add_argument("user", help="/") - subparser.set_defaults(func=ClientSession.allow) + subparser.set_defaults(func=ClientSession.unshare) subparser = subparsers.add_parser("env", help="generate a labgrid environment file for a place") subparser.set_defaults(func=ClientSession.print_env) diff --git a/labgrid/remote/common.py b/labgrid/remote/common.py index 14c8a2d74..3998733f3 100644 --- a/labgrid/remote/common.py +++ b/labgrid/remote/common.py @@ -7,6 +7,8 @@ import logging from datetime import datetime from fnmatch import fnmatchcase +from typing import Optional +import warnings import attr @@ -481,6 +483,21 @@ def from_pb2(cls, pb2: labgrid_coordinator_pb2.Reservation): ) +def get_metadata_single_value_by_key(metadata, key: str) -> Optional[str]: + """Look up a single value by key in a metadata sequence of (key, value) pairs.""" + values = [v for k, v in metadata or () if k == key] + + if not values: + return None + + if len(values) > 1: + warnings.warn( + "Multiple metadata KV pairs with the same key. The value of the first matching KV pair will be returned." + ) + + return values[0] + + async def queue_as_aiter(q): try: while True: diff --git a/labgrid/remote/coordinator.py b/labgrid/remote/coordinator.py index ea60f4933..e61cb941c 100644 --- a/labgrid/remote/coordinator.py +++ b/labgrid/remote/coordinator.py @@ -1,5 +1,6 @@ #!/usr/bin/env python3 import argparse +import contextvars import logging import asyncio import traceback @@ -10,11 +11,16 @@ import copy import random import signal +from typing import Optional import attr +import cel import grpc from grpc_reflection.v1alpha import reflection +from labgrid.remote.grpc.interceptor.server import IdentityServerInterceptor +from labgrid.remote.identity import ClientIdentity, infer_peer_identity + from .common import ( ResourceEntry, ResourceMatch, @@ -30,6 +36,10 @@ from .generated import labgrid_coordinator_pb2_grpc from ..util import atomic_replace, labgrid_version, yaml, Timeout +client_identity_context: contextvars.ContextVar[Optional[ClientIdentity]] = contextvars.ContextVar( + "client_identity", default=None +) + @contextmanager def warn_if_slow(prefix, *, level=logging.WARNING, limit=0.1): @@ -317,9 +327,17 @@ async def ClientStream(self, request_iterator, context): assert peer not in self.clients out_msg_queue = asyncio.Queue() + identity = client_identity_context.get() + if identity: + logging.debug("client identity provided in gRPC metadata") + logging.debug(identity) + self.clients[peer] = ClientSession(self, peer, identity.id, out_msg_queue, identity.user_agent) + async def request_task(): name = None version = None + if peer in self.clients: + session = self.clients[peer] try: async for in_msg in request_iterator: in_msg: labgrid_coordinator_pb2.ClientInMessage @@ -330,6 +348,9 @@ async def request_task(): out_msg.sync.id = in_msg.sync.id out_msg_queue.put_nowait(out_msg) elif kind == "startup": + if peer in self.clients: + logging.debug("already setup, probably because identity was provided in metadata") + continue version = in_msg.startup.version name = in_msg.startup.name session = self.clients[peer] = ClientSession(self, peer, name, out_msg_queue, version) @@ -409,9 +430,23 @@ async def ExporterStream(self, request_iterator, context): out_msg.hello.version = labgrid_version() yield out_msg + identity = client_identity_context.get() + if identity: + logging.debug("exporter identity provided in gRPC metadata") + logging.debug(identity) + if existing := self.get_exporter_by_name(identity.id): + await context.abort( + grpc.StatusCode.ALREADY_EXISTS, + f"startup failed: exporter with name '{identity.id}' is already connected from {existing.peer}", + ) + self.exporters[peer] = ExporterSession(self, peer, identity.id, command_queue, identity.user_agent) + startup_done.set() + async def request_task(): name = None version = None + if peer in self.exporters: + session = self.exporters[peer] try: async for in_msg in request_iterator: in_msg: labgrid_coordinator_pb2.ExporterInMessage @@ -422,6 +457,9 @@ async def request_task(): cmd.complete(in_msg.response) logging.debug("Command %s is done", cmd) elif kind == "startup": + if peer in self.exporters: + logging.debug("already setup, probably because identity was provided in metadata") + continue version = in_msg.startup.version name = in_msg.startup.name if existing := self.get_exporter_by_name(name): @@ -499,8 +537,11 @@ async def request_task(): except KeyError: logging.info("Never received startup from peer %s that disconnected", peer) - @locked - async def AddPlace(self, request, context): + async def create_place( + self, + request: labgrid_coordinator_pb2.CreatePlaceRequest | labgrid_coordinator_pb2.AddPlaceRequest, + context, + ) -> Place: name = request.name if not name or not isinstance(name, str): await context.abort(grpc.StatusCode.INVALID_ARGUMENT, "name was not a string") @@ -511,8 +552,19 @@ async def AddPlace(self, request, context): self.places[name] = place self._publish_place(place) self.save_later() + return place + + @locked + async def AddPlace(self, request, context): + await self.create_place(request, context) return labgrid_coordinator_pb2.AddPlaceResponse() + @locked + async def CreatePlace(self, request, context): + logging.debug("CreatePlace name=%s", request.name) + place = await self.create_place(request, context) + return labgrid_coordinator_pb2.CreatePlaceResponse(place=place.as_pb2()) + @locked async def DeletePlace(self, request, context): name = request.name @@ -852,7 +904,7 @@ async def AcquirePlace(self, request, context): peer = context.peer() name = request.placename try: - username = self.clients[peer].name + username = infer_peer_identity(self.clients, context, client_identity_context) except KeyError: await context.abort(grpc.StatusCode.FAILED_PRECONDITION, f"Peer {peer} does not have a valid session") print(request) @@ -916,13 +968,10 @@ async def ReleasePlace(self, request, context): print(f"{place.name}: place released") return labgrid_coordinator_pb2.ReleasePlaceResponse() - @locked - async def AllowPlace(self, request, context): - placename = request.placename - user = request.user + async def share_place(self, placename, user, context): peer = context.peer() try: - username = self.clients[peer].name + username = infer_peer_identity(self.clients, context, client_identity_context) except KeyError: await context.abort(grpc.StatusCode.FAILED_PRECONDITION, f"Peer {peer} does not have a valid session") try: @@ -939,11 +988,71 @@ async def AllowPlace(self, request, context): place.touch() self._publish_place(place) self.save_later() + + @locked + async def AllowPlace(self, request, context): + await self.share_place(request.placename, request.user, context) return labgrid_coordinator_pb2.AllowPlaceResponse() + @locked + async def SharePlace(self, request, context): + name = request.name + logging.debug("SharePlace name=%s user=%s", name, request.user) + if not name or not isinstance(name, str): + await context.abort(grpc.StatusCode.INVALID_ARGUMENT, "name was not a string") + + await self.share_place(name, request.user, context) + return labgrid_coordinator_pb2.SharePlaceResponse() + + @locked + async def UnsharePlace(self, request, context): + logging.debug("UnsharePlace name=%s user=%s", request.name, request.user) + + placename = request.name + user = request.user + peer = context.peer() + try: + username = infer_peer_identity(self.clients, context, client_identity_context) + except KeyError: + await context.abort(grpc.StatusCode.FAILED_PRECONDITION, f"Peer {peer} does not have a valid session") + try: + place = self.places[placename] + except KeyError: + await context.abort(grpc.StatusCode.INVALID_ARGUMENT, f"Place {placename} does not exist") + if not place.acquired: + await context.abort(grpc.StatusCode.FAILED_PRECONDITION, f"Place {placename} is not acquired") + if not place.acquired == username: + await context.abort( + grpc.StatusCode.FAILED_PRECONDITION, f"Place {placename} is not acquired by {username}" + ) + try: + place.allowed.remove(user) + place.touch() + self._publish_place(place) + self.save_later() + except KeyError: + await context.abort(grpc.StatusCode.FAILED_PRECONDITION, f"Place {placename} is not shared with {user}") + + return labgrid_coordinator_pb2.UnsharePlaceResponse() + def _get_places(self): return {k: v.asdict() for k, v in self.places.items()} + @locked + async def GetPlace(self, request, context): + name = request.name + logging.debug("GetPlace name=%s", name) + if not name or not isinstance(name, str): + await context.abort(grpc.StatusCode.INVALID_ARGUMENT, "name was not a string") + try: + place = self.places[name] + return labgrid_coordinator_pb2.GetPlaceResponse(place=place.as_pb2()) + except KeyError: + await context.abort(grpc.StatusCode.INVALID_ARGUMENT, f"Place {name} does not exist") + except Exception: + logging.exception("error during get place") + await context.abort(grpc.StatusCode.INTERNAL, "internal error") + @locked async def GetPlaces(self, unused_request, unused_context): logging.debug("GetPlaces") @@ -952,6 +1061,112 @@ async def GetPlaces(self, unused_request, unused_context): except Exception: logging.exception("error during get places") + @locked + async def ListPlaces(self, request, context): + logging.debug("ListPlaces") + + if request.page_size != 0 or request.page_token != "": + await context.abort(grpc.StatusCode.UNIMPLEMENTED, "ListPlaces does not yet support pagination") + + filter_program = None + if request.filter: + try: + filter_program = cel.compile(request.filter) # pylint: disable=no-member + except ValueError as exc: + await context.abort(grpc.StatusCode.INVALID_ARGUMENT, f"Invalid filter: {exc}") + + places = [] + for place in self.places.values(): + if filter_program is not None: + filter_context = place.asdict() + filter_context["name"] = place.name + + try: + filter_result = filter_program.execute(filter_context) + if not isinstance(filter_result, bool): + await context.abort( + grpc.StatusCode.INVALID_ARGUMENT, + "Filter must evaluate to a boolean", + ) + + if not filter_result: + continue + except (RuntimeError, TypeError) as exc: + await context.abort(grpc.StatusCode.INVALID_ARGUMENT, f"Invalid filter: {exc}") + + places.append(place.as_pb2()) + + return labgrid_coordinator_pb2.ListPlacesResponse(places=places) + + @locked + async def ListResources(self, request, context): + logging.debug("ListResources") + + if request.page_size != 0 or request.page_token != "": + await context.abort(grpc.StatusCode.UNIMPLEMENTED, "ListResources does not yet support pagination") + + filter_program = None + if request.filter: + try: + filter_program = cel.compile(request.filter) # pylint: disable=no-member + except ValueError as exc: + await context.abort(grpc.StatusCode.INVALID_ARGUMENT, f"Invalid filter: {exc}") + + resources = [] + for _, session in sorted(self.exporters.items()): + for _, group in sorted(session.groups.items()): + for _, resource in sorted(group.items()): + if filter_program is not None: + filter_context = resource.asdict() + filter_context["path"] = { + "exporter_name": resource.path[0], + "group_name": resource.path[1], + "resource_name": resource.path[3], + } + + try: + filter_result = filter_program.execute(filter_context) + if not isinstance(filter_result, bool): + await context.abort( + grpc.StatusCode.INVALID_ARGUMENT, + "Filter must evaluate to a boolean", + ) + + if not filter_result: + continue + except (RuntimeError, TypeError) as exc: + await context.abort(grpc.StatusCode.INVALID_ARGUMENT, f"Invalid filter: {exc}") + + serialised = resource.as_pb2() + serialised.path.exporter_name = resource.path[0] + serialised.path.group_name = resource.path[1] + serialised.path.resource_name = resource.path[3] + resources.append(serialised) + return labgrid_coordinator_pb2.ListResourcesResponse(resources=resources) + + @locked + async def ListPlaceResources(self, request, context): + name = request.name + logging.debug("ListPlaceResources name=%s", name) + if not name or not isinstance(name, str): + await context.abort(grpc.StatusCode.INVALID_ARGUMENT, "name was not a string") + try: + place = self.places[name] + except KeyError: + await context.abort(grpc.StatusCode.INVALID_ARGUMENT, f"Place {name} does not exist") + resources = [] + for _, session in sorted(self.exporters.items()): + for _, group in sorted(session.groups.items()): + for _, resource in sorted(group.items()): + if not place.hasmatch(resource.path): + continue + serialised = resource.as_pb2() + serialised.path.exporter_name = resource.path[0] + serialised.path.group_name = resource.path[1] + serialised.path.resource_name = resource.path[3] + resources.append(serialised) + return labgrid_coordinator_pb2.ListPlaceResourcesResponse(resources=resources) + def schedule_reservations(self): # The primary information is stored in the reservations and the places # only have a copy for convenience. @@ -1076,7 +1291,10 @@ async def CreateReservation(self, request: labgrid_coordinator_pb2.CreateReserva await context.abort(grpc.StatusCode.INVALID_ARGUMENT, f"Value {v} is invalid") fltr[k] = v - owner = self.clients[peer].name + try: + owner = infer_peer_identity(self.clients, context, client_identity_context) + except KeyError: + await context.abort(grpc.StatusCode.FAILED_PRECONDITION, f"Peer {peer} does not have a valid session") res = Reservation(owner=owner, prio=request.prio, filters=fltrs) self.reservations[res.token] = res self.schedule_reservations() @@ -1093,22 +1311,31 @@ async def CancelReservation(self, request: labgrid_coordinator_pb2.CancelReserva self.schedule_reservations() return labgrid_coordinator_pb2.CancelReservationResponse() - @locked - async def PollReservation(self, request: labgrid_coordinator_pb2.PollReservationRequest, context): - token = request.token + async def refresh_reservation(self, reservation_id: str, context) -> Reservation: try: - res = self.reservations[token] + res = self.reservations[reservation_id] except KeyError: - await context.abort(grpc.StatusCode.FAILED_PRECONDITION, f"Reservation {token} does not exist") - res.refresh() + await context.abort(grpc.StatusCode.FAILED_PRECONDITION, f"Reservation {reservation_id} does not exist") + if res.state in (ReservationState.waiting, ReservationState.allocated): + res.refresh() + return res + + @locked + async def PollReservation(self, request: labgrid_coordinator_pb2.PollReservationRequest, context): + res = await self.refresh_reservation(request.token, context) return labgrid_coordinator_pb2.PollReservationResponse(reservation=res.as_pb2()) + @locked + async def RefreshReservation(self, request: labgrid_coordinator_pb2.RefreshReservationRequest, context): + logging.debug("RefreshReservation reservation_id=%s", request.reservation_id) + res = await self.refresh_reservation(request.reservation_id, context) + return labgrid_coordinator_pb2.RefreshReservationResponse(reservation=res.as_pb2()) + @locked async def GetReservations(self, request: labgrid_coordinator_pb2.GetReservationsRequest, context): reservations = [x.as_pb2() for x in self.reservations.values()] return labgrid_coordinator_pb2.GetReservationsResponse(reservations=reservations) - async def serve(listen, cleanup) -> None: asyncio.current_task().set_name("coordinator-serve") # It seems since https://github.com/grpc/grpc/pull/34647, the @@ -1126,6 +1353,7 @@ async def serve(listen, cleanup) -> None: ] server = grpc.aio.server( options=channel_options, + interceptors=[IdentityServerInterceptor(client_identity_context)], ) coordinator = Coordinator() labgrid_coordinator_pb2_grpc.add_CoordinatorServicer_to_server(coordinator, server) diff --git a/labgrid/remote/exporter.py b/labgrid/remote/exporter.py index 68c4fa708..cb8579d2a 100755 --- a/labgrid/remote/exporter.py +++ b/labgrid/remote/exporter.py @@ -20,6 +20,11 @@ import attr import grpc +from labgrid.remote.grpc.interceptor.client import ( + IdentityClientStreamStreamInterceptor, + IdentityClientUnaryUnaryInterceptor, +) + from .config import ResourceConfig from .common import ResourceEntry, queue_as_aiter from .generated import labgrid_coordinator_pb2, labgrid_coordinator_pb2_grpc @@ -831,9 +836,14 @@ def __init__(self, config) -> None: if urlsplit(f"//{config['coordinator']}").port is None: config["coordinator"] += ":20408" + identity = (None, self.name, f"labgrid-exporter {labgrid_version()}") self.channel = grpc.aio.insecure_channel( target=config["coordinator"], options=channel_options, + interceptors=[ + IdentityClientUnaryUnaryInterceptor(*identity), + IdentityClientStreamStreamInterceptor(*identity), + ], ) self.stub = labgrid_coordinator_pb2_grpc.CoordinatorStub(self.channel) self.out_queue = asyncio.Queue() @@ -845,7 +855,6 @@ def __init__(self, config) -> None: async def run(self) -> None: self.pump_task = self.loop.create_task(self.message_pump()) - self.send_started() config_template_env = { "env": os.environ, @@ -892,12 +901,6 @@ async def run(self) -> None: except asyncio.CancelledError: return - def send_started(self): - msg = labgrid_coordinator_pb2.ExporterInMessage() - msg.startup.version = labgrid_version() - msg.startup.name = self.name - self.out_queue.put_nowait(msg) - async def message_pump(self): got_message = False try: diff --git a/labgrid/remote/generated/labgrid_coordinator_pb2.py b/labgrid/remote/generated/labgrid_coordinator_pb2.py index 37652bff7..d3d5fecac 100644 --- a/labgrid/remote/generated/labgrid_coordinator_pb2.py +++ b/labgrid/remote/generated/labgrid_coordinator_pb2.py @@ -1,12 +1,22 @@ # -*- coding: utf-8 -*- # Generated by the protocol buffer compiler. DO NOT EDIT! +# NO CHECKED-IN PROTOBUF GENCODE # source: labgrid-coordinator.proto -# Protobuf Python Version: 4.25.1 +# Protobuf Python Version: 6.31.1 """Generated protocol buffer code.""" from google.protobuf import descriptor as _descriptor from google.protobuf import descriptor_pool as _descriptor_pool +from google.protobuf import runtime_version as _runtime_version from google.protobuf import symbol_database as _symbol_database from google.protobuf.internal import builder as _builder +_runtime_version.ValidateProtobufRuntimeVersion( + _runtime_version.Domain.PUBLIC, + 6, + 31, + 1, + '', + 'labgrid-coordinator.proto' +) # @@protoc_insertion_point(imports) _sym_db = _symbol_database.Default() @@ -14,145 +24,207 @@ -DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x19labgrid-coordinator.proto\x12\x07labgrid\"\x8a\x01\n\x0f\x43lientInMessage\x12\x1d\n\x04sync\x18\x01 \x01(\x0b\x32\r.labgrid.SyncH\x00\x12\'\n\x07startup\x18\x02 \x01(\x0b\x32\x14.labgrid.StartupDoneH\x00\x12\'\n\tsubscribe\x18\x03 \x01(\x0b\x32\x12.labgrid.SubscribeH\x00\x42\x06\n\x04kind\"\x12\n\x04Sync\x12\n\n\x02id\x18\x01 \x01(\x04\",\n\x0bStartupDone\x12\x0f\n\x07version\x18\x01 \x01(\t\x12\x0c\n\x04name\x18\x02 \x01(\t\"r\n\tSubscribe\x12\x1b\n\x0eis_unsubscribe\x18\x01 \x01(\x08H\x01\x88\x01\x01\x12\x14\n\nall_places\x18\x02 \x01(\x08H\x00\x12\x17\n\rall_resources\x18\x03 \x01(\x08H\x00\x42\x06\n\x04kindB\x11\n\x0f_is_unsubscribe\"g\n\x10\x43lientOutMessage\x12 \n\x04sync\x18\x01 \x01(\x0b\x32\r.labgrid.SyncH\x00\x88\x01\x01\x12(\n\x07updates\x18\x02 \x03(\x0b\x32\x17.labgrid.UpdateResponseB\x07\n\x05_sync\"\xa5\x01\n\x0eUpdateResponse\x12%\n\x08resource\x18\x01 \x01(\x0b\x32\x11.labgrid.ResourceH\x00\x12.\n\x0c\x64\x65l_resource\x18\x02 \x01(\x0b\x32\x16.labgrid.Resource.PathH\x00\x12\x1f\n\x05place\x18\x03 \x01(\x0b\x32\x0e.labgrid.PlaceH\x00\x12\x13\n\tdel_place\x18\x04 \x01(\tH\x00\x42\x06\n\x04kind\"\x9a\x01\n\x11\x45xporterInMessage\x12%\n\x08resource\x18\x01 \x01(\x0b\x32\x11.labgrid.ResourceH\x00\x12\'\n\x07startup\x18\x02 \x01(\x0b\x32\x14.labgrid.StartupDoneH\x00\x12-\n\x08response\x18\x03 \x01(\x0b\x32\x19.labgrid.ExporterResponseH\x00\x42\x06\n\x04kind\"\x9e\x03\n\x08Resource\x12$\n\x04path\x18\x01 \x01(\x0b\x32\x16.labgrid.Resource.Path\x12\x0b\n\x03\x63ls\x18\x02 \x01(\t\x12-\n\x06params\x18\x03 \x03(\x0b\x32\x1d.labgrid.Resource.ParamsEntry\x12+\n\x05\x65xtra\x18\x04 \x03(\x0b\x32\x1c.labgrid.Resource.ExtraEntry\x12\x10\n\x08\x61\x63quired\x18\x05 \x01(\t\x12\r\n\x05\x61vail\x18\x06 \x01(\x08\x1a_\n\x04Path\x12\x1a\n\rexporter_name\x18\x01 \x01(\tH\x00\x88\x01\x01\x12\x12\n\ngroup_name\x18\x02 \x01(\t\x12\x15\n\rresource_name\x18\x03 \x01(\tB\x10\n\x0e_exporter_name\x1a@\n\x0bParamsEntry\x12\x0b\n\x03key\x18\x01 \x01(\t\x12 \n\x05value\x18\x02 \x01(\x0b\x32\x11.labgrid.MapValue:\x02\x38\x01\x1a?\n\nExtraEntry\x12\x0b\n\x03key\x18\x01 \x01(\t\x12 \n\x05value\x18\x02 \x01(\x0b\x32\x11.labgrid.MapValue:\x02\x38\x01\"\x82\x01\n\x08MapValue\x12\x14\n\nbool_value\x18\x01 \x01(\x08H\x00\x12\x13\n\tint_value\x18\x02 \x01(\x03H\x00\x12\x14\n\nuint_value\x18\x03 \x01(\x04H\x00\x12\x15\n\x0b\x66loat_value\x18\x04 \x01(\x01H\x00\x12\x16\n\x0cstring_value\x18\x05 \x01(\tH\x00\x42\x06\n\x04kind\"C\n\x10\x45xporterResponse\x12\x0f\n\x07success\x18\x01 \x01(\x08\x12\x13\n\x06reason\x18\x02 \x01(\tH\x00\x88\x01\x01\x42\t\n\x07_reason\"\x18\n\x05Hello\x12\x0f\n\x07version\x18\x01 \x01(\t\"\x82\x01\n\x12\x45xporterOutMessage\x12\x1f\n\x05hello\x18\x01 \x01(\x0b\x32\x0e.labgrid.HelloH\x00\x12\x43\n\x14set_acquired_request\x18\x02 \x01(\x0b\x32#.labgrid.ExporterSetAcquiredRequestH\x00\x42\x06\n\x04kind\"o\n\x1a\x45xporterSetAcquiredRequest\x12\x12\n\ngroup_name\x18\x01 \x01(\t\x12\x15\n\rresource_name\x18\x02 \x01(\t\x12\x17\n\nplace_name\x18\x03 \x01(\tH\x00\x88\x01\x01\x42\r\n\x0b_place_name\"\x1f\n\x0f\x41\x64\x64PlaceRequest\x12\x0c\n\x04name\x18\x01 \x01(\t\"\x12\n\x10\x41\x64\x64PlaceResponse\"\"\n\x12\x44\x65letePlaceRequest\x12\x0c\n\x04name\x18\x01 \x01(\t\"\x15\n\x13\x44\x65letePlaceResponse\"\x12\n\x10GetPlacesRequest\"3\n\x11GetPlacesResponse\x12\x1e\n\x06places\x18\x01 \x03(\x0b\x32\x0e.labgrid.Place\"\xd2\x02\n\x05Place\x12\x0c\n\x04name\x18\x01 \x01(\t\x12\x0f\n\x07\x61liases\x18\x02 \x03(\t\x12\x0f\n\x07\x63omment\x18\x03 \x01(\t\x12&\n\x04tags\x18\x04 \x03(\x0b\x32\x18.labgrid.Place.TagsEntry\x12\'\n\x07matches\x18\x05 \x03(\x0b\x32\x16.labgrid.ResourceMatch\x12\x15\n\x08\x61\x63quired\x18\x06 \x01(\tH\x00\x88\x01\x01\x12\x1a\n\x12\x61\x63quired_resources\x18\x07 \x03(\t\x12\x0f\n\x07\x61llowed\x18\x08 \x03(\t\x12\x0f\n\x07\x63reated\x18\t \x01(\x01\x12\x0f\n\x07\x63hanged\x18\n \x01(\x01\x12\x18\n\x0breservation\x18\x0b \x01(\tH\x01\x88\x01\x01\x1a+\n\tTagsEntry\x12\x0b\n\x03key\x18\x01 \x01(\t\x12\r\n\x05value\x18\x02 \x01(\t:\x02\x38\x01\x42\x0b\n\t_acquiredB\x0e\n\x0c_reservation\"y\n\rResourceMatch\x12\x10\n\x08\x65xporter\x18\x01 \x01(\t\x12\r\n\x05group\x18\x02 \x01(\t\x12\x0b\n\x03\x63ls\x18\x03 \x01(\t\x12\x11\n\x04name\x18\x04 \x01(\tH\x00\x88\x01\x01\x12\x13\n\x06rename\x18\x05 \x01(\tH\x01\x88\x01\x01\x42\x07\n\x05_nameB\t\n\x07_rename\"8\n\x14\x41\x64\x64PlaceAliasRequest\x12\x11\n\tplacename\x18\x01 \x01(\t\x12\r\n\x05\x61lias\x18\x02 \x01(\t\"\x17\n\x15\x41\x64\x64PlaceAliasResponse\";\n\x17\x44\x65letePlaceAliasRequest\x12\x11\n\tplacename\x18\x01 \x01(\t\x12\r\n\x05\x61lias\x18\x02 \x01(\t\"\x1a\n\x18\x44\x65letePlaceAliasResponse\"\x8b\x01\n\x13SetPlaceTagsRequest\x12\x11\n\tplacename\x18\x01 \x01(\t\x12\x34\n\x04tags\x18\x02 \x03(\x0b\x32&.labgrid.SetPlaceTagsRequest.TagsEntry\x1a+\n\tTagsEntry\x12\x0b\n\x03key\x18\x01 \x01(\t\x12\r\n\x05value\x18\x02 \x01(\t:\x02\x38\x01\"\x16\n\x14SetPlaceTagsResponse\"<\n\x16SetPlaceCommentRequest\x12\x11\n\tplacename\x18\x01 \x01(\t\x12\x0f\n\x07\x63omment\x18\x02 \x01(\t\"\x19\n\x17SetPlaceCommentResponse\"Z\n\x14\x41\x64\x64PlaceMatchRequest\x12\x11\n\tplacename\x18\x01 \x01(\t\x12\x0f\n\x07pattern\x18\x02 \x01(\t\x12\x13\n\x06rename\x18\x03 \x01(\tH\x00\x88\x01\x01\x42\t\n\x07_rename\"\x17\n\x15\x41\x64\x64PlaceMatchResponse\"]\n\x17\x44\x65letePlaceMatchRequest\x12\x11\n\tplacename\x18\x01 \x01(\t\x12\x0f\n\x07pattern\x18\x02 \x01(\t\x12\x13\n\x06rename\x18\x03 \x01(\tH\x00\x88\x01\x01\x42\t\n\x07_rename\"\x1a\n\x18\x44\x65letePlaceMatchResponse\"(\n\x13\x41\x63quirePlaceRequest\x12\x11\n\tplacename\x18\x01 \x01(\t\"\x16\n\x14\x41\x63quirePlaceResponse\"L\n\x13ReleasePlaceRequest\x12\x11\n\tplacename\x18\x01 \x01(\t\x12\x15\n\x08\x66romuser\x18\x02 \x01(\tH\x00\x88\x01\x01\x42\x0b\n\t_fromuser\"\x16\n\x14ReleasePlaceResponse\"4\n\x11\x41llowPlaceRequest\x12\x11\n\tplacename\x18\x01 \x01(\t\x12\x0c\n\x04user\x18\x02 \x01(\t\"\x14\n\x12\x41llowPlaceResponse\"\xb6\x01\n\x18\x43reateReservationRequest\x12?\n\x07\x66ilters\x18\x01 \x03(\x0b\x32..labgrid.CreateReservationRequest.FiltersEntry\x12\x0c\n\x04prio\x18\x02 \x01(\x01\x1aK\n\x0c\x46iltersEntry\x12\x0b\n\x03key\x18\x01 \x01(\t\x12*\n\x05value\x18\x02 \x01(\x0b\x32\x1b.labgrid.Reservation.Filter:\x02\x38\x01\"F\n\x19\x43reateReservationResponse\x12)\n\x0breservation\x18\x01 \x01(\x0b\x32\x14.labgrid.Reservation\"\xcd\x03\n\x0bReservation\x12\r\n\x05owner\x18\x01 \x01(\t\x12\r\n\x05token\x18\x02 \x01(\t\x12\r\n\x05state\x18\x03 \x01(\x05\x12\x0c\n\x04prio\x18\x04 \x01(\x01\x12\x32\n\x07\x66ilters\x18\x05 \x03(\x0b\x32!.labgrid.Reservation.FiltersEntry\x12:\n\x0b\x61llocations\x18\x06 \x03(\x0b\x32%.labgrid.Reservation.AllocationsEntry\x12\x0f\n\x07\x63reated\x18\x07 \x01(\x01\x12\x0f\n\x07timeout\x18\x08 \x01(\x01\x1ap\n\x06\x46ilter\x12\x37\n\x06\x66ilter\x18\x01 \x03(\x0b\x32\'.labgrid.Reservation.Filter.FilterEntry\x1a-\n\x0b\x46ilterEntry\x12\x0b\n\x03key\x18\x01 \x01(\t\x12\r\n\x05value\x18\x02 \x01(\t:\x02\x38\x01\x1aK\n\x0c\x46iltersEntry\x12\x0b\n\x03key\x18\x01 \x01(\t\x12*\n\x05value\x18\x02 \x01(\x0b\x32\x1b.labgrid.Reservation.Filter:\x02\x38\x01\x1a\x32\n\x10\x41llocationsEntry\x12\x0b\n\x03key\x18\x01 \x01(\t\x12\r\n\x05value\x18\x02 \x01(\t:\x02\x38\x01\")\n\x18\x43\x61ncelReservationRequest\x12\r\n\x05token\x18\x01 \x01(\t\"\x1b\n\x19\x43\x61ncelReservationResponse\"\'\n\x16PollReservationRequest\x12\r\n\x05token\x18\x01 \x01(\t\"D\n\x17PollReservationResponse\x12)\n\x0breservation\x18\x01 \x01(\x0b\x32\x14.labgrid.Reservation\"E\n\x17GetReservationsResponse\x12*\n\x0creservations\x18\x01 \x03(\x0b\x32\x14.labgrid.Reservation\"\x18\n\x16GetReservationsRequest2\xd2\x0b\n\x0b\x43oordinator\x12I\n\x0c\x43lientStream\x12\x18.labgrid.ClientInMessage\x1a\x19.labgrid.ClientOutMessage\"\x00(\x01\x30\x01\x12O\n\x0e\x45xporterStream\x12\x1a.labgrid.ExporterInMessage\x1a\x1b.labgrid.ExporterOutMessage\"\x00(\x01\x30\x01\x12\x41\n\x08\x41\x64\x64Place\x12\x18.labgrid.AddPlaceRequest\x1a\x19.labgrid.AddPlaceResponse\"\x00\x12J\n\x0b\x44\x65letePlace\x12\x1b.labgrid.DeletePlaceRequest\x1a\x1c.labgrid.DeletePlaceResponse\"\x00\x12\x44\n\tGetPlaces\x12\x19.labgrid.GetPlacesRequest\x1a\x1a.labgrid.GetPlacesResponse\"\x00\x12P\n\rAddPlaceAlias\x12\x1d.labgrid.AddPlaceAliasRequest\x1a\x1e.labgrid.AddPlaceAliasResponse\"\x00\x12Y\n\x10\x44\x65letePlaceAlias\x12 .labgrid.DeletePlaceAliasRequest\x1a!.labgrid.DeletePlaceAliasResponse\"\x00\x12M\n\x0cSetPlaceTags\x12\x1c.labgrid.SetPlaceTagsRequest\x1a\x1d.labgrid.SetPlaceTagsResponse\"\x00\x12V\n\x0fSetPlaceComment\x12\x1f.labgrid.SetPlaceCommentRequest\x1a .labgrid.SetPlaceCommentResponse\"\x00\x12P\n\rAddPlaceMatch\x12\x1d.labgrid.AddPlaceMatchRequest\x1a\x1e.labgrid.AddPlaceMatchResponse\"\x00\x12Y\n\x10\x44\x65letePlaceMatch\x12 .labgrid.DeletePlaceMatchRequest\x1a!.labgrid.DeletePlaceMatchResponse\"\x00\x12M\n\x0c\x41\x63quirePlace\x12\x1c.labgrid.AcquirePlaceRequest\x1a\x1d.labgrid.AcquirePlaceResponse\"\x00\x12M\n\x0cReleasePlace\x12\x1c.labgrid.ReleasePlaceRequest\x1a\x1d.labgrid.ReleasePlaceResponse\"\x00\x12G\n\nAllowPlace\x12\x1a.labgrid.AllowPlaceRequest\x1a\x1b.labgrid.AllowPlaceResponse\"\x00\x12\\\n\x11\x43reateReservation\x12!.labgrid.CreateReservationRequest\x1a\".labgrid.CreateReservationResponse\"\x00\x12\\\n\x11\x43\x61ncelReservation\x12!.labgrid.CancelReservationRequest\x1a\".labgrid.CancelReservationResponse\"\x00\x12V\n\x0fPollReservation\x12\x1f.labgrid.PollReservationRequest\x1a .labgrid.PollReservationResponse\"\x00\x12V\n\x0fGetReservations\x12\x1f.labgrid.GetReservationsRequest\x1a .labgrid.GetReservationsResponse\"\x00\x62\x06proto3') +DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x19labgrid-coordinator.proto\x12\x07labgrid\"\x8e\x01\n\x0f\x43lientInMessage\x12\x1d\n\x04sync\x18\x01 \x01(\x0b\x32\r.labgrid.SyncH\x00\x12+\n\x07startup\x18\x02 \x01(\x0b\x32\x14.labgrid.StartupDoneB\x02\x18\x01H\x00\x12\'\n\tsubscribe\x18\x03 \x01(\x0b\x32\x12.labgrid.SubscribeH\x00\x42\x06\n\x04kind\"\x12\n\x04Sync\x12\n\n\x02id\x18\x01 \x01(\x04\"0\n\x0bStartupDone\x12\x0f\n\x07version\x18\x01 \x01(\t\x12\x0c\n\x04name\x18\x02 \x01(\t:\x02\x18\x01\"r\n\tSubscribe\x12\x1b\n\x0eis_unsubscribe\x18\x01 \x01(\x08H\x01\x88\x01\x01\x12\x14\n\nall_places\x18\x02 \x01(\x08H\x00\x12\x17\n\rall_resources\x18\x03 \x01(\x08H\x00\x42\x06\n\x04kindB\x11\n\x0f_is_unsubscribe\"g\n\x10\x43lientOutMessage\x12 \n\x04sync\x18\x01 \x01(\x0b\x32\r.labgrid.SyncH\x00\x88\x01\x01\x12(\n\x07updates\x18\x02 \x03(\x0b\x32\x17.labgrid.UpdateResponseB\x07\n\x05_sync\"\xa5\x01\n\x0eUpdateResponse\x12%\n\x08resource\x18\x01 \x01(\x0b\x32\x11.labgrid.ResourceH\x00\x12.\n\x0c\x64\x65l_resource\x18\x02 \x01(\x0b\x32\x16.labgrid.Resource.PathH\x00\x12\x1f\n\x05place\x18\x03 \x01(\x0b\x32\x0e.labgrid.PlaceH\x00\x12\x13\n\tdel_place\x18\x04 \x01(\tH\x00\x42\x06\n\x04kind\"\x9e\x01\n\x11\x45xporterInMessage\x12%\n\x08resource\x18\x01 \x01(\x0b\x32\x11.labgrid.ResourceH\x00\x12+\n\x07startup\x18\x02 \x01(\x0b\x32\x14.labgrid.StartupDoneB\x02\x18\x01H\x00\x12-\n\x08response\x18\x03 \x01(\x0b\x32\x19.labgrid.ExporterResponseH\x00\x42\x06\n\x04kind\"\x9e\x03\n\x08Resource\x12$\n\x04path\x18\x01 \x01(\x0b\x32\x16.labgrid.Resource.Path\x12\x0b\n\x03\x63ls\x18\x02 \x01(\t\x12-\n\x06params\x18\x03 \x03(\x0b\x32\x1d.labgrid.Resource.ParamsEntry\x12+\n\x05\x65xtra\x18\x04 \x03(\x0b\x32\x1c.labgrid.Resource.ExtraEntry\x12\x10\n\x08\x61\x63quired\x18\x05 \x01(\t\x12\r\n\x05\x61vail\x18\x06 \x01(\x08\x1a_\n\x04Path\x12\x1a\n\rexporter_name\x18\x01 \x01(\tH\x00\x88\x01\x01\x12\x12\n\ngroup_name\x18\x02 \x01(\t\x12\x15\n\rresource_name\x18\x03 \x01(\tB\x10\n\x0e_exporter_name\x1a@\n\x0bParamsEntry\x12\x0b\n\x03key\x18\x01 \x01(\t\x12 \n\x05value\x18\x02 \x01(\x0b\x32\x11.labgrid.MapValue:\x02\x38\x01\x1a?\n\nExtraEntry\x12\x0b\n\x03key\x18\x01 \x01(\t\x12 \n\x05value\x18\x02 \x01(\x0b\x32\x11.labgrid.MapValue:\x02\x38\x01\"\x82\x01\n\x08MapValue\x12\x14\n\nbool_value\x18\x01 \x01(\x08H\x00\x12\x13\n\tint_value\x18\x02 \x01(\x03H\x00\x12\x14\n\nuint_value\x18\x03 \x01(\x04H\x00\x12\x15\n\x0b\x66loat_value\x18\x04 \x01(\x01H\x00\x12\x16\n\x0cstring_value\x18\x05 \x01(\tH\x00\x42\x06\n\x04kind\"C\n\x10\x45xporterResponse\x12\x0f\n\x07success\x18\x01 \x01(\x08\x12\x13\n\x06reason\x18\x02 \x01(\tH\x00\x88\x01\x01\x42\t\n\x07_reason\"\x18\n\x05Hello\x12\x0f\n\x07version\x18\x01 \x01(\t\"\x82\x01\n\x12\x45xporterOutMessage\x12\x1f\n\x05hello\x18\x01 \x01(\x0b\x32\x0e.labgrid.HelloH\x00\x12\x43\n\x14set_acquired_request\x18\x02 \x01(\x0b\x32#.labgrid.ExporterSetAcquiredRequestH\x00\x42\x06\n\x04kind\"o\n\x1a\x45xporterSetAcquiredRequest\x12\x12\n\ngroup_name\x18\x01 \x01(\t\x12\x15\n\rresource_name\x18\x02 \x01(\t\x12\x17\n\nplace_name\x18\x03 \x01(\tH\x00\x88\x01\x01\x42\r\n\x0b_place_name\"#\n\x0f\x41\x64\x64PlaceRequest\x12\x0c\n\x04name\x18\x01 \x01(\t:\x02\x18\x01\"\x16\n\x10\x41\x64\x64PlaceResponse:\x02\x18\x01\"\"\n\x12\x43reatePlaceRequest\x12\x0c\n\x04name\x18\x01 \x01(\t\"4\n\x13\x43reatePlaceResponse\x12\x1d\n\x05place\x18\x01 \x01(\x0b\x32\x0e.labgrid.Place\"\"\n\x12\x44\x65letePlaceRequest\x12\x0c\n\x04name\x18\x01 \x01(\t\"\x15\n\x13\x44\x65letePlaceResponse\"\x1f\n\x0fGetPlaceRequest\x12\x0c\n\x04name\x18\x01 \x01(\t\"1\n\x10GetPlaceResponse\x12\x1d\n\x05place\x18\x01 \x01(\x0b\x32\x0e.labgrid.Place\"\x16\n\x10GetPlacesRequest:\x02\x18\x01\"7\n\x11GetPlacesResponse\x12\x1e\n\x06places\x18\x01 \x03(\x0b\x32\x0e.labgrid.Place:\x02\x18\x01\"Z\n\x11ListPlacesRequest\x12\x13\n\x06\x66ilter\x18\x01 \x01(\tH\x00\x88\x01\x01\x12\x11\n\tpage_size\x18\x02 \x01(\x05\x12\x12\n\npage_token\x18\x03 \x01(\tB\t\n\x07_filter\"M\n\x12ListPlacesResponse\x12\x1e\n\x06places\x18\x01 \x03(\x0b\x32\x0e.labgrid.Place\x12\x17\n\x0fnext_page_token\x18\x02 \x01(\t\"]\n\x14ListResourcesRequest\x12\x13\n\x06\x66ilter\x18\x01 \x01(\tH\x00\x88\x01\x01\x12\x11\n\tpage_size\x18\x02 \x01(\x05\x12\x12\n\npage_token\x18\x03 \x01(\tB\t\n\x07_filter\"V\n\x15ListResourcesResponse\x12$\n\tresources\x18\x01 \x03(\x0b\x32\x11.labgrid.Resource\x12\x17\n\x0fnext_page_token\x18\x02 \x01(\t\")\n\x19ListPlaceResourcesRequest\x12\x0c\n\x04name\x18\x01 \x01(\t\"B\n\x1aListPlaceResourcesResponse\x12$\n\tresources\x18\x01 \x03(\x0b\x32\x11.labgrid.Resource\"\xd2\x02\n\x05Place\x12\x0c\n\x04name\x18\x01 \x01(\t\x12\x0f\n\x07\x61liases\x18\x02 \x03(\t\x12\x0f\n\x07\x63omment\x18\x03 \x01(\t\x12&\n\x04tags\x18\x04 \x03(\x0b\x32\x18.labgrid.Place.TagsEntry\x12\'\n\x07matches\x18\x05 \x03(\x0b\x32\x16.labgrid.ResourceMatch\x12\x15\n\x08\x61\x63quired\x18\x06 \x01(\tH\x00\x88\x01\x01\x12\x1a\n\x12\x61\x63quired_resources\x18\x07 \x03(\t\x12\x0f\n\x07\x61llowed\x18\x08 \x03(\t\x12\x0f\n\x07\x63reated\x18\t \x01(\x01\x12\x0f\n\x07\x63hanged\x18\n \x01(\x01\x12\x18\n\x0breservation\x18\x0b \x01(\tH\x01\x88\x01\x01\x1a+\n\tTagsEntry\x12\x0b\n\x03key\x18\x01 \x01(\t\x12\r\n\x05value\x18\x02 \x01(\t:\x02\x38\x01\x42\x0b\n\t_acquiredB\x0e\n\x0c_reservation\"y\n\rResourceMatch\x12\x10\n\x08\x65xporter\x18\x01 \x01(\t\x12\r\n\x05group\x18\x02 \x01(\t\x12\x0b\n\x03\x63ls\x18\x03 \x01(\t\x12\x11\n\x04name\x18\x04 \x01(\tH\x00\x88\x01\x01\x12\x13\n\x06rename\x18\x05 \x01(\tH\x01\x88\x01\x01\x42\x07\n\x05_nameB\t\n\x07_rename\"8\n\x14\x41\x64\x64PlaceAliasRequest\x12\x11\n\tplacename\x18\x01 \x01(\t\x12\r\n\x05\x61lias\x18\x02 \x01(\t\"\x17\n\x15\x41\x64\x64PlaceAliasResponse\";\n\x17\x44\x65letePlaceAliasRequest\x12\x11\n\tplacename\x18\x01 \x01(\t\x12\r\n\x05\x61lias\x18\x02 \x01(\t\"\x1a\n\x18\x44\x65letePlaceAliasResponse\"\x8b\x01\n\x13SetPlaceTagsRequest\x12\x11\n\tplacename\x18\x01 \x01(\t\x12\x34\n\x04tags\x18\x02 \x03(\x0b\x32&.labgrid.SetPlaceTagsRequest.TagsEntry\x1a+\n\tTagsEntry\x12\x0b\n\x03key\x18\x01 \x01(\t\x12\r\n\x05value\x18\x02 \x01(\t:\x02\x38\x01\"\x16\n\x14SetPlaceTagsResponse\"<\n\x16SetPlaceCommentRequest\x12\x11\n\tplacename\x18\x01 \x01(\t\x12\x0f\n\x07\x63omment\x18\x02 \x01(\t\"\x19\n\x17SetPlaceCommentResponse\"Z\n\x14\x41\x64\x64PlaceMatchRequest\x12\x11\n\tplacename\x18\x01 \x01(\t\x12\x0f\n\x07pattern\x18\x02 \x01(\t\x12\x13\n\x06rename\x18\x03 \x01(\tH\x00\x88\x01\x01\x42\t\n\x07_rename\"\x17\n\x15\x41\x64\x64PlaceMatchResponse\"]\n\x17\x44\x65letePlaceMatchRequest\x12\x11\n\tplacename\x18\x01 \x01(\t\x12\x0f\n\x07pattern\x18\x02 \x01(\t\x12\x13\n\x06rename\x18\x03 \x01(\tH\x00\x88\x01\x01\x42\t\n\x07_rename\"\x1a\n\x18\x44\x65letePlaceMatchResponse\"(\n\x13\x41\x63quirePlaceRequest\x12\x11\n\tplacename\x18\x01 \x01(\t\"\x16\n\x14\x41\x63quirePlaceResponse\"L\n\x13ReleasePlaceRequest\x12\x11\n\tplacename\x18\x01 \x01(\t\x12\x15\n\x08\x66romuser\x18\x02 \x01(\tH\x00\x88\x01\x01\x42\x0b\n\t_fromuser\"\x16\n\x14ReleasePlaceResponse\"8\n\x11\x41llowPlaceRequest\x12\x11\n\tplacename\x18\x01 \x01(\t\x12\x0c\n\x04user\x18\x02 \x01(\t:\x02\x18\x01\"\x18\n\x12\x41llowPlaceResponse:\x02\x18\x01\"/\n\x11SharePlaceRequest\x12\x0c\n\x04name\x18\x01 \x01(\t\x12\x0c\n\x04user\x18\x02 \x01(\t\"\x14\n\x12SharePlaceResponse\"1\n\x13UnsharePlaceRequest\x12\x0c\n\x04name\x18\x01 \x01(\t\x12\x0c\n\x04user\x18\x02 \x01(\t\"\x16\n\x14UnsharePlaceResponse\"\xb6\x01\n\x18\x43reateReservationRequest\x12?\n\x07\x66ilters\x18\x01 \x03(\x0b\x32..labgrid.CreateReservationRequest.FiltersEntry\x12\x0c\n\x04prio\x18\x02 \x01(\x01\x1aK\n\x0c\x46iltersEntry\x12\x0b\n\x03key\x18\x01 \x01(\t\x12*\n\x05value\x18\x02 \x01(\x0b\x32\x1b.labgrid.Reservation.Filter:\x02\x38\x01\"F\n\x19\x43reateReservationResponse\x12)\n\x0breservation\x18\x01 \x01(\x0b\x32\x14.labgrid.Reservation\"\xcd\x03\n\x0bReservation\x12\r\n\x05owner\x18\x01 \x01(\t\x12\r\n\x05token\x18\x02 \x01(\t\x12\r\n\x05state\x18\x03 \x01(\x05\x12\x0c\n\x04prio\x18\x04 \x01(\x01\x12\x32\n\x07\x66ilters\x18\x05 \x03(\x0b\x32!.labgrid.Reservation.FiltersEntry\x12:\n\x0b\x61llocations\x18\x06 \x03(\x0b\x32%.labgrid.Reservation.AllocationsEntry\x12\x0f\n\x07\x63reated\x18\x07 \x01(\x01\x12\x0f\n\x07timeout\x18\x08 \x01(\x01\x1ap\n\x06\x46ilter\x12\x37\n\x06\x66ilter\x18\x01 \x03(\x0b\x32\'.labgrid.Reservation.Filter.FilterEntry\x1a-\n\x0b\x46ilterEntry\x12\x0b\n\x03key\x18\x01 \x01(\t\x12\r\n\x05value\x18\x02 \x01(\t:\x02\x38\x01\x1aK\n\x0c\x46iltersEntry\x12\x0b\n\x03key\x18\x01 \x01(\t\x12*\n\x05value\x18\x02 \x01(\x0b\x32\x1b.labgrid.Reservation.Filter:\x02\x38\x01\x1a\x32\n\x10\x41llocationsEntry\x12\x0b\n\x03key\x18\x01 \x01(\t\x12\r\n\x05value\x18\x02 \x01(\t:\x02\x38\x01\")\n\x18\x43\x61ncelReservationRequest\x12\r\n\x05token\x18\x01 \x01(\t\"\x1b\n\x19\x43\x61ncelReservationResponse\"+\n\x16PollReservationRequest\x12\r\n\x05token\x18\x01 \x01(\t:\x02\x18\x01\"H\n\x17PollReservationResponse\x12)\n\x0breservation\x18\x01 \x01(\x0b\x32\x14.labgrid.Reservation:\x02\x18\x01\"3\n\x19RefreshReservationRequest\x12\x16\n\x0ereservation_id\x18\x01 \x01(\t\"G\n\x1aRefreshReservationResponse\x12)\n\x0breservation\x18\x01 \x01(\x0b\x32\x14.labgrid.Reservation\"E\n\x17GetReservationsResponse\x12*\n\x0creservations\x18\x01 \x03(\x0b\x32\x14.labgrid.Reservation\"\x18\n\x16GetReservationsRequest2\xe2\x10\n\x0b\x43oordinator\x12I\n\x0c\x43lientStream\x12\x18.labgrid.ClientInMessage\x1a\x19.labgrid.ClientOutMessage\"\x00(\x01\x30\x01\x12O\n\x0e\x45xporterStream\x12\x1a.labgrid.ExporterInMessage\x1a\x1b.labgrid.ExporterOutMessage\"\x00(\x01\x30\x01\x12\x44\n\x08\x41\x64\x64Place\x12\x18.labgrid.AddPlaceRequest\x1a\x19.labgrid.AddPlaceResponse\"\x03\x88\x02\x01\x12J\n\x0b\x43reatePlace\x12\x1b.labgrid.CreatePlaceRequest\x1a\x1c.labgrid.CreatePlaceResponse\"\x00\x12J\n\x0b\x44\x65letePlace\x12\x1b.labgrid.DeletePlaceRequest\x1a\x1c.labgrid.DeletePlaceResponse\"\x00\x12\x41\n\x08GetPlace\x12\x18.labgrid.GetPlaceRequest\x1a\x19.labgrid.GetPlaceResponse\"\x00\x12G\n\tGetPlaces\x12\x19.labgrid.GetPlacesRequest\x1a\x1a.labgrid.GetPlacesResponse\"\x03\x88\x02\x01\x12G\n\nListPlaces\x12\x1a.labgrid.ListPlacesRequest\x1a\x1b.labgrid.ListPlacesResponse\"\x00\x12P\n\rListResources\x12\x1d.labgrid.ListResourcesRequest\x1a\x1e.labgrid.ListResourcesResponse\"\x00\x12_\n\x12ListPlaceResources\x12\".labgrid.ListPlaceResourcesRequest\x1a#.labgrid.ListPlaceResourcesResponse\"\x00\x12P\n\rAddPlaceAlias\x12\x1d.labgrid.AddPlaceAliasRequest\x1a\x1e.labgrid.AddPlaceAliasResponse\"\x00\x12Y\n\x10\x44\x65letePlaceAlias\x12 .labgrid.DeletePlaceAliasRequest\x1a!.labgrid.DeletePlaceAliasResponse\"\x00\x12M\n\x0cSetPlaceTags\x12\x1c.labgrid.SetPlaceTagsRequest\x1a\x1d.labgrid.SetPlaceTagsResponse\"\x00\x12V\n\x0fSetPlaceComment\x12\x1f.labgrid.SetPlaceCommentRequest\x1a .labgrid.SetPlaceCommentResponse\"\x00\x12P\n\rAddPlaceMatch\x12\x1d.labgrid.AddPlaceMatchRequest\x1a\x1e.labgrid.AddPlaceMatchResponse\"\x00\x12Y\n\x10\x44\x65letePlaceMatch\x12 .labgrid.DeletePlaceMatchRequest\x1a!.labgrid.DeletePlaceMatchResponse\"\x00\x12M\n\x0c\x41\x63quirePlace\x12\x1c.labgrid.AcquirePlaceRequest\x1a\x1d.labgrid.AcquirePlaceResponse\"\x00\x12M\n\x0cReleasePlace\x12\x1c.labgrid.ReleasePlaceRequest\x1a\x1d.labgrid.ReleasePlaceResponse\"\x00\x12J\n\nAllowPlace\x12\x1a.labgrid.AllowPlaceRequest\x1a\x1b.labgrid.AllowPlaceResponse\"\x03\x88\x02\x01\x12G\n\nSharePlace\x12\x1a.labgrid.SharePlaceRequest\x1a\x1b.labgrid.SharePlaceResponse\"\x00\x12M\n\x0cUnsharePlace\x12\x1c.labgrid.UnsharePlaceRequest\x1a\x1d.labgrid.UnsharePlaceResponse\"\x00\x12\\\n\x11\x43reateReservation\x12!.labgrid.CreateReservationRequest\x1a\".labgrid.CreateReservationResponse\"\x00\x12\\\n\x11\x43\x61ncelReservation\x12!.labgrid.CancelReservationRequest\x1a\".labgrid.CancelReservationResponse\"\x00\x12Y\n\x0fPollReservation\x12\x1f.labgrid.PollReservationRequest\x1a .labgrid.PollReservationResponse\"\x03\x88\x02\x01\x12_\n\x12RefreshReservation\x12\".labgrid.RefreshReservationRequest\x1a#.labgrid.RefreshReservationResponse\"\x00\x12V\n\x0fGetReservations\x12\x1f.labgrid.GetReservationsRequest\x1a .labgrid.GetReservationsResponse\"\x00\x62\x06proto3') _globals = globals() _builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals) _builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'labgrid_coordinator_pb2', _globals) -if _descriptor._USE_C_DESCRIPTORS == False: - DESCRIPTOR._options = None - _globals['_RESOURCE_PARAMSENTRY']._options = None +if not _descriptor._USE_C_DESCRIPTORS: + DESCRIPTOR._loaded_options = None + _globals['_CLIENTINMESSAGE'].fields_by_name['startup']._loaded_options = None + _globals['_CLIENTINMESSAGE'].fields_by_name['startup']._serialized_options = b'\030\001' + _globals['_STARTUPDONE']._loaded_options = None + _globals['_STARTUPDONE']._serialized_options = b'\030\001' + _globals['_EXPORTERINMESSAGE'].fields_by_name['startup']._loaded_options = None + _globals['_EXPORTERINMESSAGE'].fields_by_name['startup']._serialized_options = b'\030\001' + _globals['_RESOURCE_PARAMSENTRY']._loaded_options = None _globals['_RESOURCE_PARAMSENTRY']._serialized_options = b'8\001' - _globals['_RESOURCE_EXTRAENTRY']._options = None + _globals['_RESOURCE_EXTRAENTRY']._loaded_options = None _globals['_RESOURCE_EXTRAENTRY']._serialized_options = b'8\001' - _globals['_PLACE_TAGSENTRY']._options = None + _globals['_ADDPLACEREQUEST']._loaded_options = None + _globals['_ADDPLACEREQUEST']._serialized_options = b'\030\001' + _globals['_ADDPLACERESPONSE']._loaded_options = None + _globals['_ADDPLACERESPONSE']._serialized_options = b'\030\001' + _globals['_GETPLACESREQUEST']._loaded_options = None + _globals['_GETPLACESREQUEST']._serialized_options = b'\030\001' + _globals['_GETPLACESRESPONSE']._loaded_options = None + _globals['_GETPLACESRESPONSE']._serialized_options = b'\030\001' + _globals['_PLACE_TAGSENTRY']._loaded_options = None _globals['_PLACE_TAGSENTRY']._serialized_options = b'8\001' - _globals['_SETPLACETAGSREQUEST_TAGSENTRY']._options = None + _globals['_SETPLACETAGSREQUEST_TAGSENTRY']._loaded_options = None _globals['_SETPLACETAGSREQUEST_TAGSENTRY']._serialized_options = b'8\001' - _globals['_CREATERESERVATIONREQUEST_FILTERSENTRY']._options = None + _globals['_ALLOWPLACEREQUEST']._loaded_options = None + _globals['_ALLOWPLACEREQUEST']._serialized_options = b'\030\001' + _globals['_ALLOWPLACERESPONSE']._loaded_options = None + _globals['_ALLOWPLACERESPONSE']._serialized_options = b'\030\001' + _globals['_CREATERESERVATIONREQUEST_FILTERSENTRY']._loaded_options = None _globals['_CREATERESERVATIONREQUEST_FILTERSENTRY']._serialized_options = b'8\001' - _globals['_RESERVATION_FILTER_FILTERENTRY']._options = None + _globals['_RESERVATION_FILTER_FILTERENTRY']._loaded_options = None _globals['_RESERVATION_FILTER_FILTERENTRY']._serialized_options = b'8\001' - _globals['_RESERVATION_FILTERSENTRY']._options = None + _globals['_RESERVATION_FILTERSENTRY']._loaded_options = None _globals['_RESERVATION_FILTERSENTRY']._serialized_options = b'8\001' - _globals['_RESERVATION_ALLOCATIONSENTRY']._options = None + _globals['_RESERVATION_ALLOCATIONSENTRY']._loaded_options = None _globals['_RESERVATION_ALLOCATIONSENTRY']._serialized_options = b'8\001' + _globals['_POLLRESERVATIONREQUEST']._loaded_options = None + _globals['_POLLRESERVATIONREQUEST']._serialized_options = b'\030\001' + _globals['_POLLRESERVATIONRESPONSE']._loaded_options = None + _globals['_POLLRESERVATIONRESPONSE']._serialized_options = b'\030\001' + _globals['_COORDINATOR'].methods_by_name['AddPlace']._loaded_options = None + _globals['_COORDINATOR'].methods_by_name['AddPlace']._serialized_options = b'\210\002\001' + _globals['_COORDINATOR'].methods_by_name['GetPlaces']._loaded_options = None + _globals['_COORDINATOR'].methods_by_name['GetPlaces']._serialized_options = b'\210\002\001' + _globals['_COORDINATOR'].methods_by_name['AllowPlace']._loaded_options = None + _globals['_COORDINATOR'].methods_by_name['AllowPlace']._serialized_options = b'\210\002\001' + _globals['_COORDINATOR'].methods_by_name['PollReservation']._loaded_options = None + _globals['_COORDINATOR'].methods_by_name['PollReservation']._serialized_options = b'\210\002\001' _globals['_CLIENTINMESSAGE']._serialized_start=39 - _globals['_CLIENTINMESSAGE']._serialized_end=177 - _globals['_SYNC']._serialized_start=179 - _globals['_SYNC']._serialized_end=197 - _globals['_STARTUPDONE']._serialized_start=199 - _globals['_STARTUPDONE']._serialized_end=243 - _globals['_SUBSCRIBE']._serialized_start=245 - _globals['_SUBSCRIBE']._serialized_end=359 - _globals['_CLIENTOUTMESSAGE']._serialized_start=361 - _globals['_CLIENTOUTMESSAGE']._serialized_end=464 - _globals['_UPDATERESPONSE']._serialized_start=467 - _globals['_UPDATERESPONSE']._serialized_end=632 - _globals['_EXPORTERINMESSAGE']._serialized_start=635 - _globals['_EXPORTERINMESSAGE']._serialized_end=789 - _globals['_RESOURCE']._serialized_start=792 - _globals['_RESOURCE']._serialized_end=1206 - _globals['_RESOURCE_PATH']._serialized_start=980 - _globals['_RESOURCE_PATH']._serialized_end=1075 - _globals['_RESOURCE_PARAMSENTRY']._serialized_start=1077 - _globals['_RESOURCE_PARAMSENTRY']._serialized_end=1141 - _globals['_RESOURCE_EXTRAENTRY']._serialized_start=1143 - _globals['_RESOURCE_EXTRAENTRY']._serialized_end=1206 - _globals['_MAPVALUE']._serialized_start=1209 - _globals['_MAPVALUE']._serialized_end=1339 - _globals['_EXPORTERRESPONSE']._serialized_start=1341 - _globals['_EXPORTERRESPONSE']._serialized_end=1408 - _globals['_HELLO']._serialized_start=1410 - _globals['_HELLO']._serialized_end=1434 - _globals['_EXPORTEROUTMESSAGE']._serialized_start=1437 - _globals['_EXPORTEROUTMESSAGE']._serialized_end=1567 - _globals['_EXPORTERSETACQUIREDREQUEST']._serialized_start=1569 - _globals['_EXPORTERSETACQUIREDREQUEST']._serialized_end=1680 - _globals['_ADDPLACEREQUEST']._serialized_start=1682 - _globals['_ADDPLACEREQUEST']._serialized_end=1713 - _globals['_ADDPLACERESPONSE']._serialized_start=1715 - _globals['_ADDPLACERESPONSE']._serialized_end=1733 - _globals['_DELETEPLACEREQUEST']._serialized_start=1735 - _globals['_DELETEPLACEREQUEST']._serialized_end=1769 - _globals['_DELETEPLACERESPONSE']._serialized_start=1771 - _globals['_DELETEPLACERESPONSE']._serialized_end=1792 - _globals['_GETPLACESREQUEST']._serialized_start=1794 - _globals['_GETPLACESREQUEST']._serialized_end=1812 - _globals['_GETPLACESRESPONSE']._serialized_start=1814 - _globals['_GETPLACESRESPONSE']._serialized_end=1865 - _globals['_PLACE']._serialized_start=1868 - _globals['_PLACE']._serialized_end=2206 - _globals['_PLACE_TAGSENTRY']._serialized_start=2134 - _globals['_PLACE_TAGSENTRY']._serialized_end=2177 - _globals['_RESOURCEMATCH']._serialized_start=2208 - _globals['_RESOURCEMATCH']._serialized_end=2329 - _globals['_ADDPLACEALIASREQUEST']._serialized_start=2331 - _globals['_ADDPLACEALIASREQUEST']._serialized_end=2387 - _globals['_ADDPLACEALIASRESPONSE']._serialized_start=2389 - _globals['_ADDPLACEALIASRESPONSE']._serialized_end=2412 - _globals['_DELETEPLACEALIASREQUEST']._serialized_start=2414 - _globals['_DELETEPLACEALIASREQUEST']._serialized_end=2473 - _globals['_DELETEPLACEALIASRESPONSE']._serialized_start=2475 - _globals['_DELETEPLACEALIASRESPONSE']._serialized_end=2501 - _globals['_SETPLACETAGSREQUEST']._serialized_start=2504 - _globals['_SETPLACETAGSREQUEST']._serialized_end=2643 - _globals['_SETPLACETAGSREQUEST_TAGSENTRY']._serialized_start=2134 - _globals['_SETPLACETAGSREQUEST_TAGSENTRY']._serialized_end=2177 - _globals['_SETPLACETAGSRESPONSE']._serialized_start=2645 - _globals['_SETPLACETAGSRESPONSE']._serialized_end=2667 - _globals['_SETPLACECOMMENTREQUEST']._serialized_start=2669 - _globals['_SETPLACECOMMENTREQUEST']._serialized_end=2729 - _globals['_SETPLACECOMMENTRESPONSE']._serialized_start=2731 - _globals['_SETPLACECOMMENTRESPONSE']._serialized_end=2756 - _globals['_ADDPLACEMATCHREQUEST']._serialized_start=2758 - _globals['_ADDPLACEMATCHREQUEST']._serialized_end=2848 - _globals['_ADDPLACEMATCHRESPONSE']._serialized_start=2850 - _globals['_ADDPLACEMATCHRESPONSE']._serialized_end=2873 - _globals['_DELETEPLACEMATCHREQUEST']._serialized_start=2875 - _globals['_DELETEPLACEMATCHREQUEST']._serialized_end=2968 - _globals['_DELETEPLACEMATCHRESPONSE']._serialized_start=2970 - _globals['_DELETEPLACEMATCHRESPONSE']._serialized_end=2996 - _globals['_ACQUIREPLACEREQUEST']._serialized_start=2998 - _globals['_ACQUIREPLACEREQUEST']._serialized_end=3038 - _globals['_ACQUIREPLACERESPONSE']._serialized_start=3040 - _globals['_ACQUIREPLACERESPONSE']._serialized_end=3062 - _globals['_RELEASEPLACEREQUEST']._serialized_start=3064 - _globals['_RELEASEPLACEREQUEST']._serialized_end=3140 - _globals['_RELEASEPLACERESPONSE']._serialized_start=3142 - _globals['_RELEASEPLACERESPONSE']._serialized_end=3164 - _globals['_ALLOWPLACEREQUEST']._serialized_start=3166 - _globals['_ALLOWPLACEREQUEST']._serialized_end=3218 - _globals['_ALLOWPLACERESPONSE']._serialized_start=3220 - _globals['_ALLOWPLACERESPONSE']._serialized_end=3240 - _globals['_CREATERESERVATIONREQUEST']._serialized_start=3243 - _globals['_CREATERESERVATIONREQUEST']._serialized_end=3425 - _globals['_CREATERESERVATIONREQUEST_FILTERSENTRY']._serialized_start=3350 - _globals['_CREATERESERVATIONREQUEST_FILTERSENTRY']._serialized_end=3425 - _globals['_CREATERESERVATIONRESPONSE']._serialized_start=3427 - _globals['_CREATERESERVATIONRESPONSE']._serialized_end=3497 - _globals['_RESERVATION']._serialized_start=3500 - _globals['_RESERVATION']._serialized_end=3961 - _globals['_RESERVATION_FILTER']._serialized_start=3720 - _globals['_RESERVATION_FILTER']._serialized_end=3832 - _globals['_RESERVATION_FILTER_FILTERENTRY']._serialized_start=3787 - _globals['_RESERVATION_FILTER_FILTERENTRY']._serialized_end=3832 - _globals['_RESERVATION_FILTERSENTRY']._serialized_start=3350 - _globals['_RESERVATION_FILTERSENTRY']._serialized_end=3425 - _globals['_RESERVATION_ALLOCATIONSENTRY']._serialized_start=3911 - _globals['_RESERVATION_ALLOCATIONSENTRY']._serialized_end=3961 - _globals['_CANCELRESERVATIONREQUEST']._serialized_start=3963 - _globals['_CANCELRESERVATIONREQUEST']._serialized_end=4004 - _globals['_CANCELRESERVATIONRESPONSE']._serialized_start=4006 - _globals['_CANCELRESERVATIONRESPONSE']._serialized_end=4033 - _globals['_POLLRESERVATIONREQUEST']._serialized_start=4035 - _globals['_POLLRESERVATIONREQUEST']._serialized_end=4074 - _globals['_POLLRESERVATIONRESPONSE']._serialized_start=4076 - _globals['_POLLRESERVATIONRESPONSE']._serialized_end=4144 - _globals['_GETRESERVATIONSRESPONSE']._serialized_start=4146 - _globals['_GETRESERVATIONSRESPONSE']._serialized_end=4215 - _globals['_GETRESERVATIONSREQUEST']._serialized_start=4217 - _globals['_GETRESERVATIONSREQUEST']._serialized_end=4241 - _globals['_COORDINATOR']._serialized_start=4244 - _globals['_COORDINATOR']._serialized_end=5734 + _globals['_CLIENTINMESSAGE']._serialized_end=181 + _globals['_SYNC']._serialized_start=183 + _globals['_SYNC']._serialized_end=201 + _globals['_STARTUPDONE']._serialized_start=203 + _globals['_STARTUPDONE']._serialized_end=251 + _globals['_SUBSCRIBE']._serialized_start=253 + _globals['_SUBSCRIBE']._serialized_end=367 + _globals['_CLIENTOUTMESSAGE']._serialized_start=369 + _globals['_CLIENTOUTMESSAGE']._serialized_end=472 + _globals['_UPDATERESPONSE']._serialized_start=475 + _globals['_UPDATERESPONSE']._serialized_end=640 + _globals['_EXPORTERINMESSAGE']._serialized_start=643 + _globals['_EXPORTERINMESSAGE']._serialized_end=801 + _globals['_RESOURCE']._serialized_start=804 + _globals['_RESOURCE']._serialized_end=1218 + _globals['_RESOURCE_PATH']._serialized_start=992 + _globals['_RESOURCE_PATH']._serialized_end=1087 + _globals['_RESOURCE_PARAMSENTRY']._serialized_start=1089 + _globals['_RESOURCE_PARAMSENTRY']._serialized_end=1153 + _globals['_RESOURCE_EXTRAENTRY']._serialized_start=1155 + _globals['_RESOURCE_EXTRAENTRY']._serialized_end=1218 + _globals['_MAPVALUE']._serialized_start=1221 + _globals['_MAPVALUE']._serialized_end=1351 + _globals['_EXPORTERRESPONSE']._serialized_start=1353 + _globals['_EXPORTERRESPONSE']._serialized_end=1420 + _globals['_HELLO']._serialized_start=1422 + _globals['_HELLO']._serialized_end=1446 + _globals['_EXPORTEROUTMESSAGE']._serialized_start=1449 + _globals['_EXPORTEROUTMESSAGE']._serialized_end=1579 + _globals['_EXPORTERSETACQUIREDREQUEST']._serialized_start=1581 + _globals['_EXPORTERSETACQUIREDREQUEST']._serialized_end=1692 + _globals['_ADDPLACEREQUEST']._serialized_start=1694 + _globals['_ADDPLACEREQUEST']._serialized_end=1729 + _globals['_ADDPLACERESPONSE']._serialized_start=1731 + _globals['_ADDPLACERESPONSE']._serialized_end=1753 + _globals['_CREATEPLACEREQUEST']._serialized_start=1755 + _globals['_CREATEPLACEREQUEST']._serialized_end=1789 + _globals['_CREATEPLACERESPONSE']._serialized_start=1791 + _globals['_CREATEPLACERESPONSE']._serialized_end=1843 + _globals['_DELETEPLACEREQUEST']._serialized_start=1845 + _globals['_DELETEPLACEREQUEST']._serialized_end=1879 + _globals['_DELETEPLACERESPONSE']._serialized_start=1881 + _globals['_DELETEPLACERESPONSE']._serialized_end=1902 + _globals['_GETPLACEREQUEST']._serialized_start=1904 + _globals['_GETPLACEREQUEST']._serialized_end=1935 + _globals['_GETPLACERESPONSE']._serialized_start=1937 + _globals['_GETPLACERESPONSE']._serialized_end=1986 + _globals['_GETPLACESREQUEST']._serialized_start=1988 + _globals['_GETPLACESREQUEST']._serialized_end=2010 + _globals['_GETPLACESRESPONSE']._serialized_start=2012 + _globals['_GETPLACESRESPONSE']._serialized_end=2067 + _globals['_LISTPLACESREQUEST']._serialized_start=2069 + _globals['_LISTPLACESREQUEST']._serialized_end=2159 + _globals['_LISTPLACESRESPONSE']._serialized_start=2161 + _globals['_LISTPLACESRESPONSE']._serialized_end=2238 + _globals['_LISTRESOURCESREQUEST']._serialized_start=2240 + _globals['_LISTRESOURCESREQUEST']._serialized_end=2333 + _globals['_LISTRESOURCESRESPONSE']._serialized_start=2335 + _globals['_LISTRESOURCESRESPONSE']._serialized_end=2421 + _globals['_LISTPLACERESOURCESREQUEST']._serialized_start=2423 + _globals['_LISTPLACERESOURCESREQUEST']._serialized_end=2464 + _globals['_LISTPLACERESOURCESRESPONSE']._serialized_start=2466 + _globals['_LISTPLACERESOURCESRESPONSE']._serialized_end=2532 + _globals['_PLACE']._serialized_start=2535 + _globals['_PLACE']._serialized_end=2873 + _globals['_PLACE_TAGSENTRY']._serialized_start=2801 + _globals['_PLACE_TAGSENTRY']._serialized_end=2844 + _globals['_RESOURCEMATCH']._serialized_start=2875 + _globals['_RESOURCEMATCH']._serialized_end=2996 + _globals['_ADDPLACEALIASREQUEST']._serialized_start=2998 + _globals['_ADDPLACEALIASREQUEST']._serialized_end=3054 + _globals['_ADDPLACEALIASRESPONSE']._serialized_start=3056 + _globals['_ADDPLACEALIASRESPONSE']._serialized_end=3079 + _globals['_DELETEPLACEALIASREQUEST']._serialized_start=3081 + _globals['_DELETEPLACEALIASREQUEST']._serialized_end=3140 + _globals['_DELETEPLACEALIASRESPONSE']._serialized_start=3142 + _globals['_DELETEPLACEALIASRESPONSE']._serialized_end=3168 + _globals['_SETPLACETAGSREQUEST']._serialized_start=3171 + _globals['_SETPLACETAGSREQUEST']._serialized_end=3310 + _globals['_SETPLACETAGSREQUEST_TAGSENTRY']._serialized_start=2801 + _globals['_SETPLACETAGSREQUEST_TAGSENTRY']._serialized_end=2844 + _globals['_SETPLACETAGSRESPONSE']._serialized_start=3312 + _globals['_SETPLACETAGSRESPONSE']._serialized_end=3334 + _globals['_SETPLACECOMMENTREQUEST']._serialized_start=3336 + _globals['_SETPLACECOMMENTREQUEST']._serialized_end=3396 + _globals['_SETPLACECOMMENTRESPONSE']._serialized_start=3398 + _globals['_SETPLACECOMMENTRESPONSE']._serialized_end=3423 + _globals['_ADDPLACEMATCHREQUEST']._serialized_start=3425 + _globals['_ADDPLACEMATCHREQUEST']._serialized_end=3515 + _globals['_ADDPLACEMATCHRESPONSE']._serialized_start=3517 + _globals['_ADDPLACEMATCHRESPONSE']._serialized_end=3540 + _globals['_DELETEPLACEMATCHREQUEST']._serialized_start=3542 + _globals['_DELETEPLACEMATCHREQUEST']._serialized_end=3635 + _globals['_DELETEPLACEMATCHRESPONSE']._serialized_start=3637 + _globals['_DELETEPLACEMATCHRESPONSE']._serialized_end=3663 + _globals['_ACQUIREPLACEREQUEST']._serialized_start=3665 + _globals['_ACQUIREPLACEREQUEST']._serialized_end=3705 + _globals['_ACQUIREPLACERESPONSE']._serialized_start=3707 + _globals['_ACQUIREPLACERESPONSE']._serialized_end=3729 + _globals['_RELEASEPLACEREQUEST']._serialized_start=3731 + _globals['_RELEASEPLACEREQUEST']._serialized_end=3807 + _globals['_RELEASEPLACERESPONSE']._serialized_start=3809 + _globals['_RELEASEPLACERESPONSE']._serialized_end=3831 + _globals['_ALLOWPLACEREQUEST']._serialized_start=3833 + _globals['_ALLOWPLACEREQUEST']._serialized_end=3889 + _globals['_ALLOWPLACERESPONSE']._serialized_start=3891 + _globals['_ALLOWPLACERESPONSE']._serialized_end=3915 + _globals['_SHAREPLACEREQUEST']._serialized_start=3917 + _globals['_SHAREPLACEREQUEST']._serialized_end=3964 + _globals['_SHAREPLACERESPONSE']._serialized_start=3966 + _globals['_SHAREPLACERESPONSE']._serialized_end=3986 + _globals['_UNSHAREPLACEREQUEST']._serialized_start=3988 + _globals['_UNSHAREPLACEREQUEST']._serialized_end=4037 + _globals['_UNSHAREPLACERESPONSE']._serialized_start=4039 + _globals['_UNSHAREPLACERESPONSE']._serialized_end=4061 + _globals['_CREATERESERVATIONREQUEST']._serialized_start=4064 + _globals['_CREATERESERVATIONREQUEST']._serialized_end=4246 + _globals['_CREATERESERVATIONREQUEST_FILTERSENTRY']._serialized_start=4171 + _globals['_CREATERESERVATIONREQUEST_FILTERSENTRY']._serialized_end=4246 + _globals['_CREATERESERVATIONRESPONSE']._serialized_start=4248 + _globals['_CREATERESERVATIONRESPONSE']._serialized_end=4318 + _globals['_RESERVATION']._serialized_start=4321 + _globals['_RESERVATION']._serialized_end=4782 + _globals['_RESERVATION_FILTER']._serialized_start=4541 + _globals['_RESERVATION_FILTER']._serialized_end=4653 + _globals['_RESERVATION_FILTER_FILTERENTRY']._serialized_start=4608 + _globals['_RESERVATION_FILTER_FILTERENTRY']._serialized_end=4653 + _globals['_RESERVATION_FILTERSENTRY']._serialized_start=4171 + _globals['_RESERVATION_FILTERSENTRY']._serialized_end=4246 + _globals['_RESERVATION_ALLOCATIONSENTRY']._serialized_start=4732 + _globals['_RESERVATION_ALLOCATIONSENTRY']._serialized_end=4782 + _globals['_CANCELRESERVATIONREQUEST']._serialized_start=4784 + _globals['_CANCELRESERVATIONREQUEST']._serialized_end=4825 + _globals['_CANCELRESERVATIONRESPONSE']._serialized_start=4827 + _globals['_CANCELRESERVATIONRESPONSE']._serialized_end=4854 + _globals['_POLLRESERVATIONREQUEST']._serialized_start=4856 + _globals['_POLLRESERVATIONREQUEST']._serialized_end=4899 + _globals['_POLLRESERVATIONRESPONSE']._serialized_start=4901 + _globals['_POLLRESERVATIONRESPONSE']._serialized_end=4973 + _globals['_REFRESHRESERVATIONREQUEST']._serialized_start=4975 + _globals['_REFRESHRESERVATIONREQUEST']._serialized_end=5026 + _globals['_REFRESHRESERVATIONRESPONSE']._serialized_start=5028 + _globals['_REFRESHRESERVATIONRESPONSE']._serialized_end=5099 + _globals['_GETRESERVATIONSRESPONSE']._serialized_start=5101 + _globals['_GETRESERVATIONSRESPONSE']._serialized_end=5170 + _globals['_GETRESERVATIONSREQUEST']._serialized_start=5172 + _globals['_GETRESERVATIONSREQUEST']._serialized_end=5196 + _globals['_COORDINATOR']._serialized_start=5199 + _globals['_COORDINATOR']._serialized_end=7345 # @@protoc_insertion_point(module_scope) diff --git a/labgrid/remote/generated/labgrid_coordinator_pb2.pyi b/labgrid/remote/generated/labgrid_coordinator_pb2.pyi index 366f4e438..6e13f4949 100644 --- a/labgrid/remote/generated/labgrid_coordinator_pb2.pyi +++ b/labgrid/remote/generated/labgrid_coordinator_pb2.pyi @@ -1,7 +1,8 @@ from google.protobuf.internal import containers as _containers from google.protobuf import descriptor as _descriptor from google.protobuf import message as _message -from typing import ClassVar as _ClassVar, Iterable as _Iterable, Mapping as _Mapping, Optional as _Optional, Union as _Union +from collections.abc import Iterable as _Iterable, Mapping as _Mapping +from typing import ClassVar as _ClassVar, Optional as _Optional, Union as _Union DESCRIPTOR: _descriptor.FileDescriptor @@ -164,6 +165,18 @@ class AddPlaceResponse(_message.Message): __slots__ = () def __init__(self) -> None: ... +class CreatePlaceRequest(_message.Message): + __slots__ = ("name",) + NAME_FIELD_NUMBER: _ClassVar[int] + name: str + def __init__(self, name: _Optional[str] = ...) -> None: ... + +class CreatePlaceResponse(_message.Message): + __slots__ = ("place",) + PLACE_FIELD_NUMBER: _ClassVar[int] + place: Place + def __init__(self, place: _Optional[_Union[Place, _Mapping]] = ...) -> None: ... + class DeletePlaceRequest(_message.Message): __slots__ = ("name",) NAME_FIELD_NUMBER: _ClassVar[int] @@ -174,6 +187,18 @@ class DeletePlaceResponse(_message.Message): __slots__ = () def __init__(self) -> None: ... +class GetPlaceRequest(_message.Message): + __slots__ = ("name",) + NAME_FIELD_NUMBER: _ClassVar[int] + name: str + def __init__(self, name: _Optional[str] = ...) -> None: ... + +class GetPlaceResponse(_message.Message): + __slots__ = ("place",) + PLACE_FIELD_NUMBER: _ClassVar[int] + place: Place + def __init__(self, place: _Optional[_Union[Place, _Mapping]] = ...) -> None: ... + class GetPlacesRequest(_message.Message): __slots__ = () def __init__(self) -> None: ... @@ -184,6 +209,54 @@ class GetPlacesResponse(_message.Message): places: _containers.RepeatedCompositeFieldContainer[Place] def __init__(self, places: _Optional[_Iterable[_Union[Place, _Mapping]]] = ...) -> None: ... +class ListPlacesRequest(_message.Message): + __slots__ = ("filter", "page_size", "page_token") + FILTER_FIELD_NUMBER: _ClassVar[int] + PAGE_SIZE_FIELD_NUMBER: _ClassVar[int] + PAGE_TOKEN_FIELD_NUMBER: _ClassVar[int] + filter: str + page_size: int + page_token: str + def __init__(self, filter: _Optional[str] = ..., page_size: _Optional[int] = ..., page_token: _Optional[str] = ...) -> None: ... + +class ListPlacesResponse(_message.Message): + __slots__ = ("places", "next_page_token") + PLACES_FIELD_NUMBER: _ClassVar[int] + NEXT_PAGE_TOKEN_FIELD_NUMBER: _ClassVar[int] + places: _containers.RepeatedCompositeFieldContainer[Place] + next_page_token: str + def __init__(self, places: _Optional[_Iterable[_Union[Place, _Mapping]]] = ..., next_page_token: _Optional[str] = ...) -> None: ... + +class ListResourcesRequest(_message.Message): + __slots__ = ("filter", "page_size", "page_token") + FILTER_FIELD_NUMBER: _ClassVar[int] + PAGE_SIZE_FIELD_NUMBER: _ClassVar[int] + PAGE_TOKEN_FIELD_NUMBER: _ClassVar[int] + filter: str + page_size: int + page_token: str + def __init__(self, filter: _Optional[str] = ..., page_size: _Optional[int] = ..., page_token: _Optional[str] = ...) -> None: ... + +class ListResourcesResponse(_message.Message): + __slots__ = ("resources", "next_page_token") + RESOURCES_FIELD_NUMBER: _ClassVar[int] + NEXT_PAGE_TOKEN_FIELD_NUMBER: _ClassVar[int] + resources: _containers.RepeatedCompositeFieldContainer[Resource] + next_page_token: str + def __init__(self, resources: _Optional[_Iterable[_Union[Resource, _Mapping]]] = ..., next_page_token: _Optional[str] = ...) -> None: ... + +class ListPlaceResourcesRequest(_message.Message): + __slots__ = ("name",) + NAME_FIELD_NUMBER: _ClassVar[int] + name: str + def __init__(self, name: _Optional[str] = ...) -> None: ... + +class ListPlaceResourcesResponse(_message.Message): + __slots__ = ("resources",) + RESOURCES_FIELD_NUMBER: _ClassVar[int] + resources: _containers.RepeatedCompositeFieldContainer[Resource] + def __init__(self, resources: _Optional[_Iterable[_Union[Resource, _Mapping]]] = ...) -> None: ... + class Place(_message.Message): __slots__ = ("name", "aliases", "comment", "tags", "matches", "acquired", "acquired_resources", "allowed", "created", "changed", "reservation") class TagsEntry(_message.Message): @@ -348,6 +421,30 @@ class AllowPlaceResponse(_message.Message): __slots__ = () def __init__(self) -> None: ... +class SharePlaceRequest(_message.Message): + __slots__ = ("name", "user") + NAME_FIELD_NUMBER: _ClassVar[int] + USER_FIELD_NUMBER: _ClassVar[int] + name: str + user: str + def __init__(self, name: _Optional[str] = ..., user: _Optional[str] = ...) -> None: ... + +class SharePlaceResponse(_message.Message): + __slots__ = () + def __init__(self) -> None: ... + +class UnsharePlaceRequest(_message.Message): + __slots__ = ("name", "user") + NAME_FIELD_NUMBER: _ClassVar[int] + USER_FIELD_NUMBER: _ClassVar[int] + name: str + user: str + def __init__(self, name: _Optional[str] = ..., user: _Optional[str] = ...) -> None: ... + +class UnsharePlaceResponse(_message.Message): + __slots__ = () + def __init__(self) -> None: ... + class CreateReservationRequest(_message.Message): __slots__ = ("filters", "prio") class FiltersEntry(_message.Message): @@ -437,6 +534,18 @@ class PollReservationResponse(_message.Message): reservation: Reservation def __init__(self, reservation: _Optional[_Union[Reservation, _Mapping]] = ...) -> None: ... +class RefreshReservationRequest(_message.Message): + __slots__ = ("reservation_id",) + RESERVATION_ID_FIELD_NUMBER: _ClassVar[int] + reservation_id: str + def __init__(self, reservation_id: _Optional[str] = ...) -> None: ... + +class RefreshReservationResponse(_message.Message): + __slots__ = ("reservation",) + RESERVATION_FIELD_NUMBER: _ClassVar[int] + reservation: Reservation + def __init__(self, reservation: _Optional[_Union[Reservation, _Mapping]] = ...) -> None: ... + class GetReservationsResponse(_message.Message): __slots__ = ("reservations",) RESERVATIONS_FIELD_NUMBER: _ClassVar[int] diff --git a/labgrid/remote/generated/labgrid_coordinator_pb2_grpc.py b/labgrid/remote/generated/labgrid_coordinator_pb2_grpc.py index debfb24f2..a173a00ce 100644 --- a/labgrid/remote/generated/labgrid_coordinator_pb2_grpc.py +++ b/labgrid/remote/generated/labgrid_coordinator_pb2_grpc.py @@ -1,9 +1,29 @@ # Generated by the gRPC Python protocol compiler plugin. DO NOT EDIT! """Client and server classes corresponding to protobuf-defined services.""" import grpc +import warnings from . import labgrid_coordinator_pb2 as labgrid__coordinator__pb2 +GRPC_GENERATED_VERSION = '1.76.0' +GRPC_VERSION = grpc.__version__ +_version_not_supported = False + +try: + from grpc._utilities import first_version_is_lower + _version_not_supported = first_version_is_lower(GRPC_VERSION, GRPC_GENERATED_VERSION) +except ImportError: + _version_not_supported = True + +if _version_not_supported: + raise RuntimeError( + f'The grpc package installed is at version {GRPC_VERSION},' + + ' but the generated code in labgrid_coordinator_pb2_grpc.py depends on' + + f' grpcio>={GRPC_GENERATED_VERSION}.' + + f' Please upgrade your grpc module to grpcio>={GRPC_GENERATED_VERSION}' + + f' or downgrade your generated code using grpcio-tools<={GRPC_VERSION}.' + ) + class CoordinatorStub(object): """Missing associated documentation comment in .proto file.""" @@ -18,92 +38,132 @@ def __init__(self, channel): '/labgrid.Coordinator/ClientStream', request_serializer=labgrid__coordinator__pb2.ClientInMessage.SerializeToString, response_deserializer=labgrid__coordinator__pb2.ClientOutMessage.FromString, - ) + _registered_method=True) self.ExporterStream = channel.stream_stream( '/labgrid.Coordinator/ExporterStream', request_serializer=labgrid__coordinator__pb2.ExporterInMessage.SerializeToString, response_deserializer=labgrid__coordinator__pb2.ExporterOutMessage.FromString, - ) + _registered_method=True) self.AddPlace = channel.unary_unary( '/labgrid.Coordinator/AddPlace', request_serializer=labgrid__coordinator__pb2.AddPlaceRequest.SerializeToString, response_deserializer=labgrid__coordinator__pb2.AddPlaceResponse.FromString, - ) + _registered_method=True) + self.CreatePlace = channel.unary_unary( + '/labgrid.Coordinator/CreatePlace', + request_serializer=labgrid__coordinator__pb2.CreatePlaceRequest.SerializeToString, + response_deserializer=labgrid__coordinator__pb2.CreatePlaceResponse.FromString, + _registered_method=True) self.DeletePlace = channel.unary_unary( '/labgrid.Coordinator/DeletePlace', request_serializer=labgrid__coordinator__pb2.DeletePlaceRequest.SerializeToString, response_deserializer=labgrid__coordinator__pb2.DeletePlaceResponse.FromString, - ) + _registered_method=True) + self.GetPlace = channel.unary_unary( + '/labgrid.Coordinator/GetPlace', + request_serializer=labgrid__coordinator__pb2.GetPlaceRequest.SerializeToString, + response_deserializer=labgrid__coordinator__pb2.GetPlaceResponse.FromString, + _registered_method=True) self.GetPlaces = channel.unary_unary( '/labgrid.Coordinator/GetPlaces', request_serializer=labgrid__coordinator__pb2.GetPlacesRequest.SerializeToString, response_deserializer=labgrid__coordinator__pb2.GetPlacesResponse.FromString, - ) + _registered_method=True) + self.ListPlaces = channel.unary_unary( + '/labgrid.Coordinator/ListPlaces', + request_serializer=labgrid__coordinator__pb2.ListPlacesRequest.SerializeToString, + response_deserializer=labgrid__coordinator__pb2.ListPlacesResponse.FromString, + _registered_method=True) + self.ListResources = channel.unary_unary( + '/labgrid.Coordinator/ListResources', + request_serializer=labgrid__coordinator__pb2.ListResourcesRequest.SerializeToString, + response_deserializer=labgrid__coordinator__pb2.ListResourcesResponse.FromString, + _registered_method=True) + self.ListPlaceResources = channel.unary_unary( + '/labgrid.Coordinator/ListPlaceResources', + request_serializer=labgrid__coordinator__pb2.ListPlaceResourcesRequest.SerializeToString, + response_deserializer=labgrid__coordinator__pb2.ListPlaceResourcesResponse.FromString, + _registered_method=True) self.AddPlaceAlias = channel.unary_unary( '/labgrid.Coordinator/AddPlaceAlias', request_serializer=labgrid__coordinator__pb2.AddPlaceAliasRequest.SerializeToString, response_deserializer=labgrid__coordinator__pb2.AddPlaceAliasResponse.FromString, - ) + _registered_method=True) self.DeletePlaceAlias = channel.unary_unary( '/labgrid.Coordinator/DeletePlaceAlias', request_serializer=labgrid__coordinator__pb2.DeletePlaceAliasRequest.SerializeToString, response_deserializer=labgrid__coordinator__pb2.DeletePlaceAliasResponse.FromString, - ) + _registered_method=True) self.SetPlaceTags = channel.unary_unary( '/labgrid.Coordinator/SetPlaceTags', request_serializer=labgrid__coordinator__pb2.SetPlaceTagsRequest.SerializeToString, response_deserializer=labgrid__coordinator__pb2.SetPlaceTagsResponse.FromString, - ) + _registered_method=True) self.SetPlaceComment = channel.unary_unary( '/labgrid.Coordinator/SetPlaceComment', request_serializer=labgrid__coordinator__pb2.SetPlaceCommentRequest.SerializeToString, response_deserializer=labgrid__coordinator__pb2.SetPlaceCommentResponse.FromString, - ) + _registered_method=True) self.AddPlaceMatch = channel.unary_unary( '/labgrid.Coordinator/AddPlaceMatch', request_serializer=labgrid__coordinator__pb2.AddPlaceMatchRequest.SerializeToString, response_deserializer=labgrid__coordinator__pb2.AddPlaceMatchResponse.FromString, - ) + _registered_method=True) self.DeletePlaceMatch = channel.unary_unary( '/labgrid.Coordinator/DeletePlaceMatch', request_serializer=labgrid__coordinator__pb2.DeletePlaceMatchRequest.SerializeToString, response_deserializer=labgrid__coordinator__pb2.DeletePlaceMatchResponse.FromString, - ) + _registered_method=True) self.AcquirePlace = channel.unary_unary( '/labgrid.Coordinator/AcquirePlace', request_serializer=labgrid__coordinator__pb2.AcquirePlaceRequest.SerializeToString, response_deserializer=labgrid__coordinator__pb2.AcquirePlaceResponse.FromString, - ) + _registered_method=True) self.ReleasePlace = channel.unary_unary( '/labgrid.Coordinator/ReleasePlace', request_serializer=labgrid__coordinator__pb2.ReleasePlaceRequest.SerializeToString, response_deserializer=labgrid__coordinator__pb2.ReleasePlaceResponse.FromString, - ) + _registered_method=True) self.AllowPlace = channel.unary_unary( '/labgrid.Coordinator/AllowPlace', request_serializer=labgrid__coordinator__pb2.AllowPlaceRequest.SerializeToString, response_deserializer=labgrid__coordinator__pb2.AllowPlaceResponse.FromString, - ) + _registered_method=True) + self.SharePlace = channel.unary_unary( + '/labgrid.Coordinator/SharePlace', + request_serializer=labgrid__coordinator__pb2.SharePlaceRequest.SerializeToString, + response_deserializer=labgrid__coordinator__pb2.SharePlaceResponse.FromString, + _registered_method=True) + self.UnsharePlace = channel.unary_unary( + '/labgrid.Coordinator/UnsharePlace', + request_serializer=labgrid__coordinator__pb2.UnsharePlaceRequest.SerializeToString, + response_deserializer=labgrid__coordinator__pb2.UnsharePlaceResponse.FromString, + _registered_method=True) self.CreateReservation = channel.unary_unary( '/labgrid.Coordinator/CreateReservation', request_serializer=labgrid__coordinator__pb2.CreateReservationRequest.SerializeToString, response_deserializer=labgrid__coordinator__pb2.CreateReservationResponse.FromString, - ) + _registered_method=True) self.CancelReservation = channel.unary_unary( '/labgrid.Coordinator/CancelReservation', request_serializer=labgrid__coordinator__pb2.CancelReservationRequest.SerializeToString, response_deserializer=labgrid__coordinator__pb2.CancelReservationResponse.FromString, - ) + _registered_method=True) self.PollReservation = channel.unary_unary( '/labgrid.Coordinator/PollReservation', request_serializer=labgrid__coordinator__pb2.PollReservationRequest.SerializeToString, response_deserializer=labgrid__coordinator__pb2.PollReservationResponse.FromString, - ) + _registered_method=True) + self.RefreshReservation = channel.unary_unary( + '/labgrid.Coordinator/RefreshReservation', + request_serializer=labgrid__coordinator__pb2.RefreshReservationRequest.SerializeToString, + response_deserializer=labgrid__coordinator__pb2.RefreshReservationResponse.FromString, + _registered_method=True) self.GetReservations = channel.unary_unary( '/labgrid.Coordinator/GetReservations', request_serializer=labgrid__coordinator__pb2.GetReservationsRequest.SerializeToString, response_deserializer=labgrid__coordinator__pb2.GetReservationsResponse.FromString, - ) + _registered_method=True) class CoordinatorServicer(object): @@ -127,18 +187,48 @@ def AddPlace(self, request, context): context.set_details('Method not implemented!') raise NotImplementedError('Method not implemented!') + def CreatePlace(self, request, context): + """Missing associated documentation comment in .proto file.""" + context.set_code(grpc.StatusCode.UNIMPLEMENTED) + context.set_details('Method not implemented!') + raise NotImplementedError('Method not implemented!') + def DeletePlace(self, request, context): """Missing associated documentation comment in .proto file.""" context.set_code(grpc.StatusCode.UNIMPLEMENTED) context.set_details('Method not implemented!') raise NotImplementedError('Method not implemented!') + def GetPlace(self, request, context): + """Missing associated documentation comment in .proto file.""" + context.set_code(grpc.StatusCode.UNIMPLEMENTED) + context.set_details('Method not implemented!') + raise NotImplementedError('Method not implemented!') + def GetPlaces(self, request, context): """Missing associated documentation comment in .proto file.""" context.set_code(grpc.StatusCode.UNIMPLEMENTED) context.set_details('Method not implemented!') raise NotImplementedError('Method not implemented!') + def ListPlaces(self, request, context): + """Missing associated documentation comment in .proto file.""" + context.set_code(grpc.StatusCode.UNIMPLEMENTED) + context.set_details('Method not implemented!') + raise NotImplementedError('Method not implemented!') + + def ListResources(self, request, context): + """Missing associated documentation comment in .proto file.""" + context.set_code(grpc.StatusCode.UNIMPLEMENTED) + context.set_details('Method not implemented!') + raise NotImplementedError('Method not implemented!') + + def ListPlaceResources(self, request, context): + """Missing associated documentation comment in .proto file.""" + context.set_code(grpc.StatusCode.UNIMPLEMENTED) + context.set_details('Method not implemented!') + raise NotImplementedError('Method not implemented!') + def AddPlaceAlias(self, request, context): """Missing associated documentation comment in .proto file.""" context.set_code(grpc.StatusCode.UNIMPLEMENTED) @@ -193,6 +283,18 @@ def AllowPlace(self, request, context): context.set_details('Method not implemented!') raise NotImplementedError('Method not implemented!') + def SharePlace(self, request, context): + """Missing associated documentation comment in .proto file.""" + context.set_code(grpc.StatusCode.UNIMPLEMENTED) + context.set_details('Method not implemented!') + raise NotImplementedError('Method not implemented!') + + def UnsharePlace(self, request, context): + """Missing associated documentation comment in .proto file.""" + context.set_code(grpc.StatusCode.UNIMPLEMENTED) + context.set_details('Method not implemented!') + raise NotImplementedError('Method not implemented!') + def CreateReservation(self, request, context): """Missing associated documentation comment in .proto file.""" context.set_code(grpc.StatusCode.UNIMPLEMENTED) @@ -211,6 +313,12 @@ def PollReservation(self, request, context): context.set_details('Method not implemented!') raise NotImplementedError('Method not implemented!') + def RefreshReservation(self, request, context): + """Missing associated documentation comment in .proto file.""" + context.set_code(grpc.StatusCode.UNIMPLEMENTED) + context.set_details('Method not implemented!') + raise NotImplementedError('Method not implemented!') + def GetReservations(self, request, context): """Missing associated documentation comment in .proto file.""" context.set_code(grpc.StatusCode.UNIMPLEMENTED) @@ -235,16 +343,41 @@ def add_CoordinatorServicer_to_server(servicer, server): request_deserializer=labgrid__coordinator__pb2.AddPlaceRequest.FromString, response_serializer=labgrid__coordinator__pb2.AddPlaceResponse.SerializeToString, ), + 'CreatePlace': grpc.unary_unary_rpc_method_handler( + servicer.CreatePlace, + request_deserializer=labgrid__coordinator__pb2.CreatePlaceRequest.FromString, + response_serializer=labgrid__coordinator__pb2.CreatePlaceResponse.SerializeToString, + ), 'DeletePlace': grpc.unary_unary_rpc_method_handler( servicer.DeletePlace, request_deserializer=labgrid__coordinator__pb2.DeletePlaceRequest.FromString, response_serializer=labgrid__coordinator__pb2.DeletePlaceResponse.SerializeToString, ), + 'GetPlace': grpc.unary_unary_rpc_method_handler( + servicer.GetPlace, + request_deserializer=labgrid__coordinator__pb2.GetPlaceRequest.FromString, + response_serializer=labgrid__coordinator__pb2.GetPlaceResponse.SerializeToString, + ), 'GetPlaces': grpc.unary_unary_rpc_method_handler( servicer.GetPlaces, request_deserializer=labgrid__coordinator__pb2.GetPlacesRequest.FromString, response_serializer=labgrid__coordinator__pb2.GetPlacesResponse.SerializeToString, ), + 'ListPlaces': grpc.unary_unary_rpc_method_handler( + servicer.ListPlaces, + request_deserializer=labgrid__coordinator__pb2.ListPlacesRequest.FromString, + response_serializer=labgrid__coordinator__pb2.ListPlacesResponse.SerializeToString, + ), + 'ListResources': grpc.unary_unary_rpc_method_handler( + servicer.ListResources, + request_deserializer=labgrid__coordinator__pb2.ListResourcesRequest.FromString, + response_serializer=labgrid__coordinator__pb2.ListResourcesResponse.SerializeToString, + ), + 'ListPlaceResources': grpc.unary_unary_rpc_method_handler( + servicer.ListPlaceResources, + request_deserializer=labgrid__coordinator__pb2.ListPlaceResourcesRequest.FromString, + response_serializer=labgrid__coordinator__pb2.ListPlaceResourcesResponse.SerializeToString, + ), 'AddPlaceAlias': grpc.unary_unary_rpc_method_handler( servicer.AddPlaceAlias, request_deserializer=labgrid__coordinator__pb2.AddPlaceAliasRequest.FromString, @@ -290,6 +423,16 @@ def add_CoordinatorServicer_to_server(servicer, server): request_deserializer=labgrid__coordinator__pb2.AllowPlaceRequest.FromString, response_serializer=labgrid__coordinator__pb2.AllowPlaceResponse.SerializeToString, ), + 'SharePlace': grpc.unary_unary_rpc_method_handler( + servicer.SharePlace, + request_deserializer=labgrid__coordinator__pb2.SharePlaceRequest.FromString, + response_serializer=labgrid__coordinator__pb2.SharePlaceResponse.SerializeToString, + ), + 'UnsharePlace': grpc.unary_unary_rpc_method_handler( + servicer.UnsharePlace, + request_deserializer=labgrid__coordinator__pb2.UnsharePlaceRequest.FromString, + response_serializer=labgrid__coordinator__pb2.UnsharePlaceResponse.SerializeToString, + ), 'CreateReservation': grpc.unary_unary_rpc_method_handler( servicer.CreateReservation, request_deserializer=labgrid__coordinator__pb2.CreateReservationRequest.FromString, @@ -305,6 +448,11 @@ def add_CoordinatorServicer_to_server(servicer, server): request_deserializer=labgrid__coordinator__pb2.PollReservationRequest.FromString, response_serializer=labgrid__coordinator__pb2.PollReservationResponse.SerializeToString, ), + 'RefreshReservation': grpc.unary_unary_rpc_method_handler( + servicer.RefreshReservation, + request_deserializer=labgrid__coordinator__pb2.RefreshReservationRequest.FromString, + response_serializer=labgrid__coordinator__pb2.RefreshReservationResponse.SerializeToString, + ), 'GetReservations': grpc.unary_unary_rpc_method_handler( servicer.GetReservations, request_deserializer=labgrid__coordinator__pb2.GetReservationsRequest.FromString, @@ -314,6 +462,7 @@ def add_CoordinatorServicer_to_server(servicer, server): generic_handler = grpc.method_handlers_generic_handler( 'labgrid.Coordinator', rpc_method_handlers) server.add_generic_rpc_handlers((generic_handler,)) + server.add_registered_method_handlers('labgrid.Coordinator', rpc_method_handlers) # This class is part of an EXPERIMENTAL API. @@ -331,11 +480,21 @@ def ClientStream(request_iterator, wait_for_ready=None, timeout=None, metadata=None): - return grpc.experimental.stream_stream(request_iterator, target, '/labgrid.Coordinator/ClientStream', + return grpc.experimental.stream_stream( + request_iterator, + target, + '/labgrid.Coordinator/ClientStream', labgrid__coordinator__pb2.ClientInMessage.SerializeToString, labgrid__coordinator__pb2.ClientOutMessage.FromString, - options, channel_credentials, - insecure, call_credentials, compression, wait_for_ready, timeout, metadata) + options, + channel_credentials, + insecure, + call_credentials, + compression, + wait_for_ready, + timeout, + metadata, + _registered_method=True) @staticmethod def ExporterStream(request_iterator, @@ -348,11 +507,21 @@ def ExporterStream(request_iterator, wait_for_ready=None, timeout=None, metadata=None): - return grpc.experimental.stream_stream(request_iterator, target, '/labgrid.Coordinator/ExporterStream', + return grpc.experimental.stream_stream( + request_iterator, + target, + '/labgrid.Coordinator/ExporterStream', labgrid__coordinator__pb2.ExporterInMessage.SerializeToString, labgrid__coordinator__pb2.ExporterOutMessage.FromString, - options, channel_credentials, - insecure, call_credentials, compression, wait_for_ready, timeout, metadata) + options, + channel_credentials, + insecure, + call_credentials, + compression, + wait_for_ready, + timeout, + metadata, + _registered_method=True) @staticmethod def AddPlace(request, @@ -365,11 +534,48 @@ def AddPlace(request, wait_for_ready=None, timeout=None, metadata=None): - return grpc.experimental.unary_unary(request, target, '/labgrid.Coordinator/AddPlace', + return grpc.experimental.unary_unary( + request, + target, + '/labgrid.Coordinator/AddPlace', labgrid__coordinator__pb2.AddPlaceRequest.SerializeToString, labgrid__coordinator__pb2.AddPlaceResponse.FromString, - options, channel_credentials, - insecure, call_credentials, compression, wait_for_ready, timeout, metadata) + options, + channel_credentials, + insecure, + call_credentials, + compression, + wait_for_ready, + timeout, + metadata, + _registered_method=True) + + @staticmethod + def CreatePlace(request, + target, + options=(), + channel_credentials=None, + call_credentials=None, + insecure=False, + compression=None, + wait_for_ready=None, + timeout=None, + metadata=None): + return grpc.experimental.unary_unary( + request, + target, + '/labgrid.Coordinator/CreatePlace', + labgrid__coordinator__pb2.CreatePlaceRequest.SerializeToString, + labgrid__coordinator__pb2.CreatePlaceResponse.FromString, + options, + channel_credentials, + insecure, + call_credentials, + compression, + wait_for_ready, + timeout, + metadata, + _registered_method=True) @staticmethod def DeletePlace(request, @@ -382,11 +588,48 @@ def DeletePlace(request, wait_for_ready=None, timeout=None, metadata=None): - return grpc.experimental.unary_unary(request, target, '/labgrid.Coordinator/DeletePlace', + return grpc.experimental.unary_unary( + request, + target, + '/labgrid.Coordinator/DeletePlace', labgrid__coordinator__pb2.DeletePlaceRequest.SerializeToString, labgrid__coordinator__pb2.DeletePlaceResponse.FromString, - options, channel_credentials, - insecure, call_credentials, compression, wait_for_ready, timeout, metadata) + options, + channel_credentials, + insecure, + call_credentials, + compression, + wait_for_ready, + timeout, + metadata, + _registered_method=True) + + @staticmethod + def GetPlace(request, + target, + options=(), + channel_credentials=None, + call_credentials=None, + insecure=False, + compression=None, + wait_for_ready=None, + timeout=None, + metadata=None): + return grpc.experimental.unary_unary( + request, + target, + '/labgrid.Coordinator/GetPlace', + labgrid__coordinator__pb2.GetPlaceRequest.SerializeToString, + labgrid__coordinator__pb2.GetPlaceResponse.FromString, + options, + channel_credentials, + insecure, + call_credentials, + compression, + wait_for_ready, + timeout, + metadata, + _registered_method=True) @staticmethod def GetPlaces(request, @@ -399,11 +642,102 @@ def GetPlaces(request, wait_for_ready=None, timeout=None, metadata=None): - return grpc.experimental.unary_unary(request, target, '/labgrid.Coordinator/GetPlaces', + return grpc.experimental.unary_unary( + request, + target, + '/labgrid.Coordinator/GetPlaces', labgrid__coordinator__pb2.GetPlacesRequest.SerializeToString, labgrid__coordinator__pb2.GetPlacesResponse.FromString, - options, channel_credentials, - insecure, call_credentials, compression, wait_for_ready, timeout, metadata) + options, + channel_credentials, + insecure, + call_credentials, + compression, + wait_for_ready, + timeout, + metadata, + _registered_method=True) + + @staticmethod + def ListPlaces(request, + target, + options=(), + channel_credentials=None, + call_credentials=None, + insecure=False, + compression=None, + wait_for_ready=None, + timeout=None, + metadata=None): + return grpc.experimental.unary_unary( + request, + target, + '/labgrid.Coordinator/ListPlaces', + labgrid__coordinator__pb2.ListPlacesRequest.SerializeToString, + labgrid__coordinator__pb2.ListPlacesResponse.FromString, + options, + channel_credentials, + insecure, + call_credentials, + compression, + wait_for_ready, + timeout, + metadata, + _registered_method=True) + + @staticmethod + def ListResources(request, + target, + options=(), + channel_credentials=None, + call_credentials=None, + insecure=False, + compression=None, + wait_for_ready=None, + timeout=None, + metadata=None): + return grpc.experimental.unary_unary( + request, + target, + '/labgrid.Coordinator/ListResources', + labgrid__coordinator__pb2.ListResourcesRequest.SerializeToString, + labgrid__coordinator__pb2.ListResourcesResponse.FromString, + options, + channel_credentials, + insecure, + call_credentials, + compression, + wait_for_ready, + timeout, + metadata, + _registered_method=True) + + @staticmethod + def ListPlaceResources(request, + target, + options=(), + channel_credentials=None, + call_credentials=None, + insecure=False, + compression=None, + wait_for_ready=None, + timeout=None, + metadata=None): + return grpc.experimental.unary_unary( + request, + target, + '/labgrid.Coordinator/ListPlaceResources', + labgrid__coordinator__pb2.ListPlaceResourcesRequest.SerializeToString, + labgrid__coordinator__pb2.ListPlaceResourcesResponse.FromString, + options, + channel_credentials, + insecure, + call_credentials, + compression, + wait_for_ready, + timeout, + metadata, + _registered_method=True) @staticmethod def AddPlaceAlias(request, @@ -416,11 +750,21 @@ def AddPlaceAlias(request, wait_for_ready=None, timeout=None, metadata=None): - return grpc.experimental.unary_unary(request, target, '/labgrid.Coordinator/AddPlaceAlias', + return grpc.experimental.unary_unary( + request, + target, + '/labgrid.Coordinator/AddPlaceAlias', labgrid__coordinator__pb2.AddPlaceAliasRequest.SerializeToString, labgrid__coordinator__pb2.AddPlaceAliasResponse.FromString, - options, channel_credentials, - insecure, call_credentials, compression, wait_for_ready, timeout, metadata) + options, + channel_credentials, + insecure, + call_credentials, + compression, + wait_for_ready, + timeout, + metadata, + _registered_method=True) @staticmethod def DeletePlaceAlias(request, @@ -433,11 +777,21 @@ def DeletePlaceAlias(request, wait_for_ready=None, timeout=None, metadata=None): - return grpc.experimental.unary_unary(request, target, '/labgrid.Coordinator/DeletePlaceAlias', + return grpc.experimental.unary_unary( + request, + target, + '/labgrid.Coordinator/DeletePlaceAlias', labgrid__coordinator__pb2.DeletePlaceAliasRequest.SerializeToString, labgrid__coordinator__pb2.DeletePlaceAliasResponse.FromString, - options, channel_credentials, - insecure, call_credentials, compression, wait_for_ready, timeout, metadata) + options, + channel_credentials, + insecure, + call_credentials, + compression, + wait_for_ready, + timeout, + metadata, + _registered_method=True) @staticmethod def SetPlaceTags(request, @@ -450,11 +804,21 @@ def SetPlaceTags(request, wait_for_ready=None, timeout=None, metadata=None): - return grpc.experimental.unary_unary(request, target, '/labgrid.Coordinator/SetPlaceTags', + return grpc.experimental.unary_unary( + request, + target, + '/labgrid.Coordinator/SetPlaceTags', labgrid__coordinator__pb2.SetPlaceTagsRequest.SerializeToString, labgrid__coordinator__pb2.SetPlaceTagsResponse.FromString, - options, channel_credentials, - insecure, call_credentials, compression, wait_for_ready, timeout, metadata) + options, + channel_credentials, + insecure, + call_credentials, + compression, + wait_for_ready, + timeout, + metadata, + _registered_method=True) @staticmethod def SetPlaceComment(request, @@ -467,11 +831,21 @@ def SetPlaceComment(request, wait_for_ready=None, timeout=None, metadata=None): - return grpc.experimental.unary_unary(request, target, '/labgrid.Coordinator/SetPlaceComment', + return grpc.experimental.unary_unary( + request, + target, + '/labgrid.Coordinator/SetPlaceComment', labgrid__coordinator__pb2.SetPlaceCommentRequest.SerializeToString, labgrid__coordinator__pb2.SetPlaceCommentResponse.FromString, - options, channel_credentials, - insecure, call_credentials, compression, wait_for_ready, timeout, metadata) + options, + channel_credentials, + insecure, + call_credentials, + compression, + wait_for_ready, + timeout, + metadata, + _registered_method=True) @staticmethod def AddPlaceMatch(request, @@ -484,11 +858,21 @@ def AddPlaceMatch(request, wait_for_ready=None, timeout=None, metadata=None): - return grpc.experimental.unary_unary(request, target, '/labgrid.Coordinator/AddPlaceMatch', + return grpc.experimental.unary_unary( + request, + target, + '/labgrid.Coordinator/AddPlaceMatch', labgrid__coordinator__pb2.AddPlaceMatchRequest.SerializeToString, labgrid__coordinator__pb2.AddPlaceMatchResponse.FromString, - options, channel_credentials, - insecure, call_credentials, compression, wait_for_ready, timeout, metadata) + options, + channel_credentials, + insecure, + call_credentials, + compression, + wait_for_ready, + timeout, + metadata, + _registered_method=True) @staticmethod def DeletePlaceMatch(request, @@ -501,11 +885,21 @@ def DeletePlaceMatch(request, wait_for_ready=None, timeout=None, metadata=None): - return grpc.experimental.unary_unary(request, target, '/labgrid.Coordinator/DeletePlaceMatch', + return grpc.experimental.unary_unary( + request, + target, + '/labgrid.Coordinator/DeletePlaceMatch', labgrid__coordinator__pb2.DeletePlaceMatchRequest.SerializeToString, labgrid__coordinator__pb2.DeletePlaceMatchResponse.FromString, - options, channel_credentials, - insecure, call_credentials, compression, wait_for_ready, timeout, metadata) + options, + channel_credentials, + insecure, + call_credentials, + compression, + wait_for_ready, + timeout, + metadata, + _registered_method=True) @staticmethod def AcquirePlace(request, @@ -518,11 +912,21 @@ def AcquirePlace(request, wait_for_ready=None, timeout=None, metadata=None): - return grpc.experimental.unary_unary(request, target, '/labgrid.Coordinator/AcquirePlace', + return grpc.experimental.unary_unary( + request, + target, + '/labgrid.Coordinator/AcquirePlace', labgrid__coordinator__pb2.AcquirePlaceRequest.SerializeToString, labgrid__coordinator__pb2.AcquirePlaceResponse.FromString, - options, channel_credentials, - insecure, call_credentials, compression, wait_for_ready, timeout, metadata) + options, + channel_credentials, + insecure, + call_credentials, + compression, + wait_for_ready, + timeout, + metadata, + _registered_method=True) @staticmethod def ReleasePlace(request, @@ -535,11 +939,21 @@ def ReleasePlace(request, wait_for_ready=None, timeout=None, metadata=None): - return grpc.experimental.unary_unary(request, target, '/labgrid.Coordinator/ReleasePlace', + return grpc.experimental.unary_unary( + request, + target, + '/labgrid.Coordinator/ReleasePlace', labgrid__coordinator__pb2.ReleasePlaceRequest.SerializeToString, labgrid__coordinator__pb2.ReleasePlaceResponse.FromString, - options, channel_credentials, - insecure, call_credentials, compression, wait_for_ready, timeout, metadata) + options, + channel_credentials, + insecure, + call_credentials, + compression, + wait_for_ready, + timeout, + metadata, + _registered_method=True) @staticmethod def AllowPlace(request, @@ -552,11 +966,75 @@ def AllowPlace(request, wait_for_ready=None, timeout=None, metadata=None): - return grpc.experimental.unary_unary(request, target, '/labgrid.Coordinator/AllowPlace', + return grpc.experimental.unary_unary( + request, + target, + '/labgrid.Coordinator/AllowPlace', labgrid__coordinator__pb2.AllowPlaceRequest.SerializeToString, labgrid__coordinator__pb2.AllowPlaceResponse.FromString, - options, channel_credentials, - insecure, call_credentials, compression, wait_for_ready, timeout, metadata) + options, + channel_credentials, + insecure, + call_credentials, + compression, + wait_for_ready, + timeout, + metadata, + _registered_method=True) + + @staticmethod + def SharePlace(request, + target, + options=(), + channel_credentials=None, + call_credentials=None, + insecure=False, + compression=None, + wait_for_ready=None, + timeout=None, + metadata=None): + return grpc.experimental.unary_unary( + request, + target, + '/labgrid.Coordinator/SharePlace', + labgrid__coordinator__pb2.SharePlaceRequest.SerializeToString, + labgrid__coordinator__pb2.SharePlaceResponse.FromString, + options, + channel_credentials, + insecure, + call_credentials, + compression, + wait_for_ready, + timeout, + metadata, + _registered_method=True) + + @staticmethod + def UnsharePlace(request, + target, + options=(), + channel_credentials=None, + call_credentials=None, + insecure=False, + compression=None, + wait_for_ready=None, + timeout=None, + metadata=None): + return grpc.experimental.unary_unary( + request, + target, + '/labgrid.Coordinator/UnsharePlace', + labgrid__coordinator__pb2.UnsharePlaceRequest.SerializeToString, + labgrid__coordinator__pb2.UnsharePlaceResponse.FromString, + options, + channel_credentials, + insecure, + call_credentials, + compression, + wait_for_ready, + timeout, + metadata, + _registered_method=True) @staticmethod def CreateReservation(request, @@ -569,11 +1047,21 @@ def CreateReservation(request, wait_for_ready=None, timeout=None, metadata=None): - return grpc.experimental.unary_unary(request, target, '/labgrid.Coordinator/CreateReservation', + return grpc.experimental.unary_unary( + request, + target, + '/labgrid.Coordinator/CreateReservation', labgrid__coordinator__pb2.CreateReservationRequest.SerializeToString, labgrid__coordinator__pb2.CreateReservationResponse.FromString, - options, channel_credentials, - insecure, call_credentials, compression, wait_for_ready, timeout, metadata) + options, + channel_credentials, + insecure, + call_credentials, + compression, + wait_for_ready, + timeout, + metadata, + _registered_method=True) @staticmethod def CancelReservation(request, @@ -586,11 +1074,21 @@ def CancelReservation(request, wait_for_ready=None, timeout=None, metadata=None): - return grpc.experimental.unary_unary(request, target, '/labgrid.Coordinator/CancelReservation', + return grpc.experimental.unary_unary( + request, + target, + '/labgrid.Coordinator/CancelReservation', labgrid__coordinator__pb2.CancelReservationRequest.SerializeToString, labgrid__coordinator__pb2.CancelReservationResponse.FromString, - options, channel_credentials, - insecure, call_credentials, compression, wait_for_ready, timeout, metadata) + options, + channel_credentials, + insecure, + call_credentials, + compression, + wait_for_ready, + timeout, + metadata, + _registered_method=True) @staticmethod def PollReservation(request, @@ -603,11 +1101,48 @@ def PollReservation(request, wait_for_ready=None, timeout=None, metadata=None): - return grpc.experimental.unary_unary(request, target, '/labgrid.Coordinator/PollReservation', + return grpc.experimental.unary_unary( + request, + target, + '/labgrid.Coordinator/PollReservation', labgrid__coordinator__pb2.PollReservationRequest.SerializeToString, labgrid__coordinator__pb2.PollReservationResponse.FromString, - options, channel_credentials, - insecure, call_credentials, compression, wait_for_ready, timeout, metadata) + options, + channel_credentials, + insecure, + call_credentials, + compression, + wait_for_ready, + timeout, + metadata, + _registered_method=True) + + @staticmethod + def RefreshReservation(request, + target, + options=(), + channel_credentials=None, + call_credentials=None, + insecure=False, + compression=None, + wait_for_ready=None, + timeout=None, + metadata=None): + return grpc.experimental.unary_unary( + request, + target, + '/labgrid.Coordinator/RefreshReservation', + labgrid__coordinator__pb2.RefreshReservationRequest.SerializeToString, + labgrid__coordinator__pb2.RefreshReservationResponse.FromString, + options, + channel_credentials, + insecure, + call_credentials, + compression, + wait_for_ready, + timeout, + metadata, + _registered_method=True) @staticmethod def GetReservations(request, @@ -620,8 +1155,18 @@ def GetReservations(request, wait_for_ready=None, timeout=None, metadata=None): - return grpc.experimental.unary_unary(request, target, '/labgrid.Coordinator/GetReservations', + return grpc.experimental.unary_unary( + request, + target, + '/labgrid.Coordinator/GetReservations', labgrid__coordinator__pb2.GetReservationsRequest.SerializeToString, labgrid__coordinator__pb2.GetReservationsResponse.FromString, - options, channel_credentials, - insecure, call_credentials, compression, wait_for_ready, timeout, metadata) + options, + channel_credentials, + insecure, + call_credentials, + compression, + wait_for_ready, + timeout, + metadata, + _registered_method=True) diff --git a/labgrid/remote/grpc/__init__.py b/labgrid/remote/grpc/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/labgrid/remote/grpc/interceptor/__init__.py b/labgrid/remote/grpc/interceptor/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/labgrid/remote/grpc/interceptor/client.py b/labgrid/remote/grpc/interceptor/client.py new file mode 100644 index 000000000..478c6dd63 --- /dev/null +++ b/labgrid/remote/grpc/interceptor/client.py @@ -0,0 +1,32 @@ +from typing import Optional + +from grpc.aio import ClientCallDetails, ClientInterceptor, StreamStreamClientInterceptor, UnaryUnaryClientInterceptor + +from labgrid.remote.identity import HOSTNAME_KEY, USER_AGENT_KEY, USERNAME_KEY + + +class BaseIdentityClientInterceptor(ClientInterceptor): + def __init__(self, username: Optional[str], hostname: str, user_agent: Optional[str]): + super().__init__() + self.username = username + self.hostname = hostname + self.user_agent = user_agent + + def _inject(self, client_call_details: ClientCallDetails): + if self.username: + client_call_details.metadata.add(USERNAME_KEY, self.username) + client_call_details.metadata.add(HOSTNAME_KEY, self.hostname) + if self.user_agent: + client_call_details.metadata.add(USER_AGENT_KEY, self.user_agent) + + +class IdentityClientUnaryUnaryInterceptor(UnaryUnaryClientInterceptor, BaseIdentityClientInterceptor): + async def intercept_unary_unary(self, continuation, client_call_details, request): + self._inject(client_call_details) + return await continuation(client_call_details, request) + + +class IdentityClientStreamStreamInterceptor(StreamStreamClientInterceptor, BaseIdentityClientInterceptor): + async def intercept_stream_stream(self, continuation, client_call_details, request_iterator): + self._inject(client_call_details) + return await continuation(client_call_details, request_iterator) diff --git a/labgrid/remote/grpc/interceptor/server.py b/labgrid/remote/grpc/interceptor/server.py new file mode 100644 index 000000000..3727fced6 --- /dev/null +++ b/labgrid/remote/grpc/interceptor/server.py @@ -0,0 +1,33 @@ +import contextvars +import logging +from asyncio import iscoroutine + +from grpc.aio import ServerInterceptor + +from labgrid.remote.identity import ClientIdentity, NoIdentityPresent + + +class IdentityServerInterceptor(ServerInterceptor): + def __init__(self, client_identity_contextvar: contextvars.ContextVar): + super().__init__() + self.client_identity_contextvar = client_identity_contextvar + + async def intercept_service(self, continuation, handler_call_details): + # continuation may return a handler + # OR an awaitable depending on grpcio build + maybe_handler = continuation(handler_call_details) + handler = await maybe_handler if iscoroutine(maybe_handler) else maybe_handler + if handler is None: + return None + + metadata = handler_call_details.invocation_metadata + logging.debug(metadata) + + try: + client_identity = ClientIdentity.from_metadata(metadata) + logging.debug(client_identity) + self.client_identity_contextvar.set(client_identity) + except NoIdentityPresent: + pass + + return handler diff --git a/labgrid/remote/identity.py b/labgrid/remote/identity.py new file mode 100644 index 000000000..442fc755d --- /dev/null +++ b/labgrid/remote/identity.py @@ -0,0 +1,65 @@ +import contextvars +import logging +from typing import Optional + +from labgrid.remote.common import get_metadata_single_value_by_key + +USERNAME_KEY = "x-lg-username" +HOSTNAME_KEY = "x-lg-hostname" +USER_AGENT_KEY = "x-lg-user-agent" + + +class NoIdentityPresent(Exception): + """Raised when metadata-based identity information is missing from the request.""" + + +class ClientIdentity: + """Represents the identity of a connected client, derived from gRPC metadata.""" + + def __init__(self, identity_id: str, user_agent: Optional[str]): + self.id = identity_id + self.user_agent = user_agent + + def __str__(self): + return f"ClientIdentity(id={self.id}, user_agent={self.user_agent})" + + @classmethod + def from_metadata(cls, metadata: tuple): + """Construct a ClientIdentity from gRPC request metadata. + + Args: + metadata: A sequence of (key, value) pairs from the gRPC context. + + Returns: + A ClientIdentity with id set to ``hostname/username`` (or just + ``hostname`` if no username is present) and (optional) user_agent. + + Raises: + NoIdentityPresent: If the hostname key is missing from metadata. + """ + username = get_metadata_single_value_by_key(metadata, USERNAME_KEY) + hostname = get_metadata_single_value_by_key(metadata, HOSTNAME_KEY) + user_agent = get_metadata_single_value_by_key(metadata, USER_AGENT_KEY) + + if not hostname: + raise NoIdentityPresent() + + if username: + return cls(f"{hostname}/{username}", user_agent) + + return cls(hostname, user_agent) + + +def infer_peer_identity(clients, context, identity_contextvar: contextvars.ContextVar[Optional[ClientIdentity]]): + logger = logging.getLogger("infer_peer_identity") + + user = identity_contextvar.get() + if user: + logger.debug("identity sourced from metadata") + return user.id + + try: + logger.debug("identity sourced from self.clients") + return clients[context.peer()].name + except KeyError: + raise diff --git a/labgrid/remote/proto/labgrid-coordinator.proto b/labgrid/remote/proto/labgrid-coordinator.proto index e0585f7e1..100897341 100644 --- a/labgrid/remote/proto/labgrid-coordinator.proto +++ b/labgrid/remote/proto/labgrid-coordinator.proto @@ -7,11 +7,25 @@ service Coordinator { rpc ExporterStream(stream ExporterInMessage) returns (stream ExporterOutMessage) {} - rpc AddPlace(AddPlaceRequest) returns (AddPlaceResponse) {} + rpc AddPlace(AddPlaceRequest) returns (AddPlaceResponse) { + option deprecated = true; + } + + rpc CreatePlace(CreatePlaceRequest) returns (CreatePlaceResponse) {} rpc DeletePlace(DeletePlaceRequest) returns (DeletePlaceResponse) {} - rpc GetPlaces(GetPlacesRequest) returns (GetPlacesResponse) {} + rpc GetPlace(GetPlaceRequest) returns (GetPlaceResponse) {} + + rpc GetPlaces(GetPlacesRequest) returns (GetPlacesResponse) { + option deprecated = true; + } + + rpc ListPlaces(ListPlacesRequest) returns (ListPlacesResponse) {} + + rpc ListResources(ListResourcesRequest) returns (ListResourcesResponse) {} + + rpc ListPlaceResources(ListPlaceResourcesRequest) returns (ListPlaceResourcesResponse) {} rpc AddPlaceAlias(AddPlaceAliasRequest) returns (AddPlaceAliasResponse) {} @@ -29,13 +43,23 @@ service Coordinator { rpc ReleasePlace(ReleasePlaceRequest) returns (ReleasePlaceResponse) {} - rpc AllowPlace(AllowPlaceRequest) returns (AllowPlaceResponse) {} + rpc AllowPlace(AllowPlaceRequest) returns (AllowPlaceResponse) { + option deprecated = true; + } + + rpc SharePlace(SharePlaceRequest) returns (SharePlaceResponse) {} + + rpc UnsharePlace(UnsharePlaceRequest) returns (UnsharePlaceResponse) {} rpc CreateReservation(CreateReservationRequest) returns (CreateReservationResponse) {} rpc CancelReservation(CancelReservationRequest) returns (CancelReservationResponse) {} - rpc PollReservation(PollReservationRequest) returns (PollReservationResponse) {} + rpc PollReservation(PollReservationRequest) returns (PollReservationResponse) { + option deprecated = true; + } + + rpc RefreshReservation(RefreshReservationRequest) returns (RefreshReservationResponse) {} rpc GetReservations(GetReservationsRequest) returns (GetReservationsResponse) {} } @@ -43,7 +67,7 @@ service Coordinator { message ClientInMessage { oneof kind { Sync sync = 1; - StartupDone startup = 2; + StartupDone startup = 2 [deprecated = true]; Subscribe subscribe = 3; }; }; @@ -53,6 +77,8 @@ message Sync { }; message StartupDone { + option deprecated = true; + string version = 1; string name = 2; }; @@ -82,7 +108,7 @@ message UpdateResponse { message ExporterInMessage { oneof kind { Resource resource = 1; - StartupDone startup = 2; + StartupDone startup = 2 [deprecated = true]; ExporterResponse response = 3; }; }; @@ -135,10 +161,21 @@ message ExporterSetAcquiredRequest { }; message AddPlaceRequest { + option deprecated = true; + string name = 1; }; message AddPlaceResponse { + option deprecated = true; +}; + +message CreatePlaceRequest { + string name = 1; +}; + +message CreatePlaceResponse { + Place place = 1; }; message DeletePlaceRequest { @@ -148,11 +185,52 @@ message DeletePlaceRequest { message DeletePlaceResponse { }; +message GetPlaceRequest { + string name = 1; +}; + +message GetPlaceResponse { + Place place = 1; +} + message GetPlacesRequest { + option deprecated = true; }; message GetPlacesResponse { + option deprecated = true; + + repeated Place places = 1; +} + +message ListPlacesRequest { + optional string filter = 1; + int32 page_size = 2; + string page_token = 3; +} + +message ListPlacesResponse { repeated Place places = 1; + string next_page_token = 2; +} + +message ListResourcesRequest { + optional string filter = 1; + int32 page_size = 2; + string page_token = 3; +}; + +message ListResourcesResponse { + repeated Resource resources = 1; + string next_page_token = 2; +}; + +message ListPlaceResourcesRequest { + string name = 1; +} + +message ListPlaceResourcesResponse { + repeated Resource resources = 1; } message Place { @@ -243,13 +321,31 @@ message ReleasePlaceResponse { }; message AllowPlaceRequest { + option deprecated = true; + string placename = 1; string user = 2; }; message AllowPlaceResponse { + option deprecated = true; }; +message SharePlaceRequest { + string name = 1; + string user = 2; +}; + +message SharePlaceResponse { +}; + +message UnsharePlaceRequest { + string name = 1; + string user = 2; +}; + +message UnsharePlaceResponse { +}; message CreateReservationRequest { map filters = 1; @@ -282,13 +378,25 @@ message CancelReservationResponse { }; message PollReservationRequest { + option deprecated = true; + string token = 1; }; message PollReservationResponse { + option deprecated = true; + Reservation reservation = 1; }; +message RefreshReservationRequest { + string reservation_id = 1; +} + +message RefreshReservationResponse { + Reservation reservation = 1; +} + message GetReservationsResponse { repeated Reservation reservations = 1; }; diff --git a/man/labgrid-client.1 b/man/labgrid-client.1 index dfc95b2e8..2d8fa5e5c 100644 --- a/man/labgrid-client.1 +++ b/man/labgrid-client.1 @@ -149,22 +149,6 @@ usage: labgrid\-client add\-named\-match PATTERN NAME .TP .B name .UNINDENT -.SS labgrid\-client allow -.sp -allow another user to access a place -.INDENT 0.0 -.INDENT 3.5 -.sp -.EX -usage: labgrid\-client allow user -.EE -.UNINDENT -.UNINDENT -.INDENT 0.0 -.TP -.B user -/ -.UNINDENT .SS labgrid\-client audio .sp start a audio stream @@ -259,7 +243,7 @@ Log output to FILE .UNINDENT .SS labgrid\-client create .sp -add a new place with the name specified via \-\-place or the LG_PLACE environment variable +create a new place with the name specified via \-\-place or the LG_PLACE environment variable .INDENT 0.0 .INDENT 3.5 .sp @@ -786,6 +770,22 @@ usage: labgrid\-client set\-tags KEY=VALUE [KEY=VALUE ...] .B key=value use an empty value for deletion .UNINDENT +.SS labgrid\-client share|allow +.sp +share a place with another user +.INDENT 0.0 +.INDENT 3.5 +.sp +.EX +usage: labgrid\-client share|allow user +.EE +.UNINDENT +.UNINDENT +.INDENT 0.0 +.TP +.B user +/ +.UNINDENT .SS labgrid\-client show .sp show a place and related resources @@ -932,6 +932,22 @@ usage: labgrid\-client tmc screen {show,save} .TP .B action .UNINDENT +.SS labgrid\-client unshare +.sp +remove another user\(aqs access to a place +.INDENT 0.0 +.INDENT 3.5 +.sp +.EX +usage: labgrid\-client unshare user +.EE +.UNINDENT +.UNINDENT +.INDENT 0.0 +.TP +.B user +/ +.UNINDENT .SS labgrid\-client usb\-mux .sp switch USB Muxer diff --git a/pyproject.toml b/pyproject.toml index a046ebe65..f2eee2225 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -46,6 +46,7 @@ dependencies = [ "PyYAML>=6.0.1", "requests>=2.26.0", "xmodem>=0.4.6", + "common-expression-language==0.5.6", ] dynamic = ["version"] # via setuptools_scm @@ -84,6 +85,7 @@ dev = [ # additional dev dependencies "psutil>=5.8.0", + "pytest-asyncio==1.3.0", "pytest-benchmark>=4.0.0", "pytest-cov>=3.0.0", "pytest-dependency>=0.5.1", @@ -120,6 +122,8 @@ packages = [ "labgrid.pytestplugin", "labgrid.remote", "labgrid.remote.generated", + "labgrid.remote.grpc", + "labgrid.remote.grpc.interceptor", "labgrid.resource", "labgrid.strategy", "labgrid.util", diff --git a/tests/conftest.py b/tests/conftest.py index 44f9ca76f..0c3131d9d 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -63,6 +63,7 @@ def __init__(self, cwd): self.cwd = str(cwd) self.spawn = None self.reader = None + self.stop_reader_event = threading.Event() def stop(self): logging.info("stopping %s pid=%s", self.__class__.__name__, self.spawn.pid) @@ -70,18 +71,18 @@ def stop(self): # let coverage write its data: # https://coverage.readthedocs.io/en/latest/subprocess.html#process-termination self.spawn.kill(SIGTERM) + self.stop_reader() if not self.spawn.closed: self.spawn.expect(pexpect.EOF) self.spawn.wait() assert not self.spawn.isalive() self.spawn = None - self.stop_reader() @staticmethod - def keep_reading(spawn): + def keep_reading(spawn, stop_event): "The output from background processes must be read to avoid blocking them." - while spawn.isalive(): + while not stop_event.is_set() and spawn.isalive(): try: data = spawn.read_nonblocking(size=1024, timeout=0.1) if not data: @@ -94,13 +95,15 @@ def keep_reading(spawn): return def start_reader(self): + self.stop_reader_event.clear() self.reader = threading.Thread( target=LabgridComponent.keep_reading, name=f'{self.__class__.__name__}-reader-{self.pid}', - args=(self.spawn,), daemon=True) + args=(self.spawn, self.stop_reader_event), daemon=True) self.reader.start() def stop_reader(self): + self.stop_reader_event.set() self.reader.join() self.reader = None @@ -234,6 +237,11 @@ def exporter(tmpdir, coordinator): NetworkService: address: "192.168.0.1" username: "root" + ClsNotEqualResourceName: + ExampleResource: + cls: NetworkSerialPort + host: 'localhost' + port: 4000 """ ) diff --git a/tests/test_coordinator.py b/tests/test_coordinator.py index 2907baed0..eb37e88da 100644 --- a/tests/test_coordinator.py +++ b/tests/test_coordinator.py @@ -1,9 +1,12 @@ -import pytest +from time import monotonic, sleep import grpc +import pytest import labgrid.remote.generated.labgrid_coordinator_pb2_grpc as labgrid_coordinator_pb2_grpc import labgrid.remote.generated.labgrid_coordinator_pb2 as labgrid_coordinator_pb2 +LIST_PLACE_RESOURCES_PATTERN = "testhost/ClsNotEqualResourceName/NetworkSerialPort/ExampleResource" + @pytest.fixture(scope="function") def channel_stub(): @@ -51,6 +54,37 @@ def test_coordinator_add_place(coordinator, channel_stub): assert res, f"There was an error: {res}" +def test_coordinator_create_place(coordinator, channel_stub): + name = "test" + place = labgrid_coordinator_pb2.CreatePlaceRequest(name=name) + res = channel_stub.CreatePlace(place) + assert res, f"There was an error: {res}" + assert res.place.name == name + + +def test_coordinator_create_place_already_exists(coordinator, channel_stub): + name = "test" + place = labgrid_coordinator_pb2.CreatePlaceRequest(name=name) + res = channel_stub.CreatePlace(place) + assert res, f"There was an error: {res}" + + with pytest.raises(grpc.RpcError) as excinfo: + channel_stub.CreatePlace(place) + + assert excinfo.value.code() == grpc.StatusCode.ALREADY_EXISTS + assert excinfo.value.details() == "Place test already exists" + + +def test_coordinator_create_place_not_provided(coordinator, channel_stub): + place = labgrid_coordinator_pb2.CreatePlaceRequest() + + with pytest.raises(grpc.RpcError) as excinfo: + channel_stub.CreatePlace(place) + + assert excinfo.value.code() == grpc.StatusCode.INVALID_ARGUMENT + assert excinfo.value.details() == "name was not a string" + + def test_coordinator_del_place(coordinator, channel_stub): name = "test" place = labgrid_coordinator_pb2.AddPlaceRequest(name=name) @@ -173,6 +207,61 @@ def test_coordinator_place_allow(coordinator, coordinator_place): assert res res = stub.AllowPlace(labgrid_coordinator_pb2.AllowPlaceRequest(placename="test", user="othertest")) assert res + res = stub.GetPlace(labgrid_coordinator_pb2.GetPlaceRequest(name="test")) + assert "othertest" in res.place.allowed + + +def test_coordinator_place_share(coordinator, coordinator_place): + stub = coordinator_place + res = stub.AcquirePlace(labgrid_coordinator_pb2.AcquirePlaceRequest(placename="test")) + assert res + res = stub.SharePlace(labgrid_coordinator_pb2.SharePlaceRequest(name="test", user="othertest")) + assert res + res = stub.GetPlace(labgrid_coordinator_pb2.GetPlaceRequest(name="test")) + assert "othertest" in res.place.allowed + + +def test_coordinator_place_share_not_acquired(coordinator, coordinator_place): + stub = coordinator_place + with pytest.raises(grpc.RpcError) as excinfo: + stub.SharePlace(labgrid_coordinator_pb2.SharePlaceRequest(name="test", user="othertest")) + + assert excinfo.value.code() == grpc.StatusCode.FAILED_PRECONDITION + assert excinfo.value.details() == "Place test is not acquired" + + +def test_coordinator_place_share_name_not_provided(coordinator, channel_stub): + request = labgrid_coordinator_pb2.SharePlaceRequest(user="test") + + with pytest.raises(grpc.RpcError) as excinfo: + channel_stub.SharePlace(request) + + assert excinfo.value.code() == grpc.StatusCode.INVALID_ARGUMENT + assert excinfo.value.details() == "name was not a string" + + +def test_coordinator_place_unshare(coordinator, coordinator_place): + stub = coordinator_place + res = stub.AcquirePlace(labgrid_coordinator_pb2.AcquirePlaceRequest(placename="test")) + assert res + res = stub.AllowPlace(labgrid_coordinator_pb2.AllowPlaceRequest(placename="test", user="othertest")) + assert res + res = stub.UnsharePlace(labgrid_coordinator_pb2.UnsharePlaceRequest(name="test", user="othertest")) + assert res + + +def test_coordinator_place_unshare_not_acquired(coordinator, coordinator_place): + stub = coordinator_place + with pytest.raises(Exception, match=r".Place test is not acquired.*"): + stub.UnsharePlace(labgrid_coordinator_pb2.UnsharePlaceRequest(name="test", user="othertest")) + + +def test_coordinator_place_unshare_not_shared(coordinator, coordinator_place): + stub = coordinator_place + res = stub.AcquirePlace(labgrid_coordinator_pb2.AcquirePlaceRequest(placename="test")) + assert res + with pytest.raises(Exception, match=r".Place test is not shared with othertest.*"): + stub.UnsharePlace(labgrid_coordinator_pb2.UnsharePlaceRequest(name="test", user="othertest")) def test_coordinator_create_reservation(coordinator, coordinator_place): @@ -191,3 +280,279 @@ def test_coordinator_create_reservation(coordinator, coordinator_place): assert res res: labgrid_coordinator_pb2.CreateReservationResponse assert len(res.reservation.token) > 0 + + +def test_coordinator_get_place(coordinator, channel_stub): + name = "test" + place = labgrid_coordinator_pb2.AddPlaceRequest(name=name) + res = channel_stub.AddPlace(place) + assert res, f"There was an error: {res}" + + request = labgrid_coordinator_pb2.GetPlaceRequest(name=name) + res = channel_stub.GetPlace(request) + + from labgrid.remote.common import Place + + place = Place.from_pb2(res.place) + + assert place.name == name, f"There was an error: {res}" + + +def test_coordinator_get_place_missing(coordinator, channel_stub): + request = labgrid_coordinator_pb2.GetPlaceRequest(name="missing") + + with pytest.raises(grpc.RpcError) as excinfo: + channel_stub.GetPlace(request) + + assert excinfo.value.code() == grpc.StatusCode.INVALID_ARGUMENT + assert excinfo.value.details() == "Place missing does not exist" + + +def test_coordinator_get_place_not_provided(coordinator, channel_stub): + request = labgrid_coordinator_pb2.GetPlaceRequest() + + with pytest.raises(grpc.RpcError) as excinfo: + channel_stub.GetPlace(request) + + assert excinfo.value.code() == grpc.StatusCode.INVALID_ARGUMENT + assert excinfo.value.details() == "name was not a string" + + +def test_coordinator_place_unshare_name_not_provided(coordinator, channel_stub): + request = labgrid_coordinator_pb2.UnsharePlaceRequest(user="test") + + with pytest.raises(grpc.RpcError) as excinfo: + channel_stub.UnsharePlace(request) + + assert excinfo.value.code() == grpc.StatusCode.INVALID_ARGUMENT + assert excinfo.value.details() == "name was not a string" + + +def test_coordinator_poll_reservation(coordinator, coordinator_place): + tags = {"board": "test"} + stub = coordinator_place + res = stub.SetPlaceTags(labgrid_coordinator_pb2.SetPlaceTagsRequest(placename="test", tags=tags)) + assert res + res = stub.CreateReservation( + labgrid_coordinator_pb2.CreateReservationRequest( + filters={ + "main": labgrid_coordinator_pb2.Reservation.Filter(filter=tags), + }, + prio=1.0, + ) + ) + assert res + token = res.reservation.token + res = stub.PollReservation(labgrid_coordinator_pb2.PollReservationRequest(token=token)) + assert res + assert res.reservation.token == token + + +def test_coordinator_poll_reservation_not_found(coordinator, coordinator_place): + stub = coordinator_place + with pytest.raises(grpc.RpcError) as excinfo: + stub.PollReservation(labgrid_coordinator_pb2.PollReservationRequest(token="nonexistent")) + + assert excinfo.value.code() == grpc.StatusCode.FAILED_PRECONDITION + assert excinfo.value.details() == "Reservation nonexistent does not exist" + + +def test_coordinator_refresh_reservation(coordinator, coordinator_place): + tags = {"board": "test"} + stub = coordinator_place + res = stub.SetPlaceTags(labgrid_coordinator_pb2.SetPlaceTagsRequest(placename="test", tags=tags)) + assert res + res = stub.CreateReservation( + labgrid_coordinator_pb2.CreateReservationRequest( + filters={ + "main": labgrid_coordinator_pb2.Reservation.Filter(filter=tags), + }, + prio=1.0, + ) + ) + assert res + token = res.reservation.token + res = stub.RefreshReservation(labgrid_coordinator_pb2.RefreshReservationRequest(reservation_id=token)) + assert res + assert res.reservation.token == token + + +def test_coordinator_refresh_reservation_not_found(coordinator, coordinator_place): + stub = coordinator_place + with pytest.raises(grpc.RpcError) as excinfo: + stub.RefreshReservation(labgrid_coordinator_pb2.RefreshReservationRequest(reservation_id="nonexistent")) + + assert excinfo.value.code() == grpc.StatusCode.FAILED_PRECONDITION + assert excinfo.value.details() == "Reservation nonexistent does not exist" + + +def wait_for_list_place_resources(stub, name, expected_count, timeout=5.0): + deadline = monotonic() + timeout + request = labgrid_coordinator_pb2.ListPlaceResourcesRequest(name=name) + while monotonic() < deadline: + res = stub.ListPlaceResources(request) + if len(res.resources) == expected_count: + return res + sleep(0.1) + return stub.ListPlaceResources(request) + + +def wait_for_list_resources(stub, expected_count, filter_expr=None, timeout=5.0): + deadline = monotonic() + timeout + kwargs = {} + if filter_expr is not None: + kwargs["filter"] = filter_expr + request = labgrid_coordinator_pb2.ListResourcesRequest(**kwargs) + while monotonic() < deadline: + res = stub.ListResources(request) + if len(res.resources) == expected_count: + return res + sleep(0.1) + return stub.ListResources(request) + + +def test_coordinator_list_place_resources(coordinator, coordinator_place, exporter): + stub = coordinator_place + res = stub.AddPlaceMatch( + labgrid_coordinator_pb2.AddPlaceMatchRequest(placename="test", pattern=LIST_PLACE_RESOURCES_PATTERN) + ) + assert res + res = wait_for_list_place_resources(stub, "test", 1) + assert len(res.resources) == 1 + assert res.resources[0].cls == "NetworkSerialPort" + assert res.resources[0].path.exporter_name == "testhost" + assert res.resources[0].path.group_name == "ClsNotEqualResourceName" + assert res.resources[0].path.resource_name == "ExampleResource" + + +def test_coordinator_list_place_resources_no_matches(coordinator, coordinator_place, exporter): + stub = coordinator_place + res = stub.AddPlaceMatch( + labgrid_coordinator_pb2.AddPlaceMatchRequest(placename="test", pattern=LIST_PLACE_RESOURCES_PATTERN) + ) + assert res + res = wait_for_list_place_resources(stub, "test", 1) + assert len(res.resources) == 1 + res = stub.DeletePlaceMatch( + labgrid_coordinator_pb2.DeletePlaceMatchRequest(placename="test", pattern=LIST_PLACE_RESOURCES_PATTERN) + ) + assert res + res = stub.ListPlaceResources(labgrid_coordinator_pb2.ListPlaceResourcesRequest(name="test")) + assert res + assert len(res.resources) == 0 + + +def test_coordinator_list_place_resources_place_does_not_exist(coordinator, coordinator_place): + stub = coordinator_place + with pytest.raises(grpc.RpcError) as excinfo: + stub.ListPlaceResources(labgrid_coordinator_pb2.ListPlaceResourcesRequest(name="test_nonexistant_place")) + + assert excinfo.value.code() == grpc.StatusCode.INVALID_ARGUMENT + assert excinfo.value.details() == "Place test_nonexistant_place does not exist" + + +def test_coordinator_list_place_resources_name_not_provided(coordinator, coordinator_place): + stub = coordinator_place + with pytest.raises(grpc.RpcError) as excinfo: + stub.ListPlaceResources(labgrid_coordinator_pb2.ListPlaceResourcesRequest()) + + assert excinfo.value.code() == grpc.StatusCode.INVALID_ARGUMENT + assert excinfo.value.details() == "name was not a string" + + +def test_coordinator_list_resources(coordinator, coordinator_place, exporter): + stub = coordinator_place + res = wait_for_list_resources(stub, 5) + assert len(res.resources) == 5 + + +def test_coordinator_list_resources_filter_on_resource_name(coordinator, coordinator_place, exporter): + stub = coordinator_place + res = wait_for_list_resources(stub, 1, filter_expr="path.resource_name == 'ExampleResource'") + assert len(res.resources) == 1 + assert res.resources[0].cls == "NetworkSerialPort" + assert res.resources[0].path.exporter_name == "testhost" + assert res.resources[0].path.group_name == "ClsNotEqualResourceName" + assert res.resources[0].path.resource_name == "ExampleResource" + + +def test_coordinator_list_resources_pagination_not_supported(coordinator, channel_stub): + with pytest.raises(grpc.RpcError) as excinfo: + channel_stub.ListResources(labgrid_coordinator_pb2.ListResourcesRequest(page_size=1)) + + assert excinfo.value.code() == grpc.StatusCode.UNIMPLEMENTED + assert excinfo.value.details() == "ListResources does not yet support pagination" + + +def test_coordinator_list_resources_invalid_filter(coordinator, channel_stub): + with pytest.raises(grpc.RpcError) as excinfo: + channel_stub.ListResources(labgrid_coordinator_pb2.ListResourcesRequest(filter="(")) + + assert excinfo.value.code() == grpc.StatusCode.INVALID_ARGUMENT + assert excinfo.value.details().startswith("Invalid filter:") + + +def test_coordinator_list_resources_filter_must_be_boolean(coordinator, channel_stub, exporter): + res = wait_for_list_resources(channel_stub, 5) + assert len(res.resources) == 5 + + with pytest.raises(grpc.RpcError) as excinfo: + channel_stub.ListResources(labgrid_coordinator_pb2.ListResourcesRequest(filter="path.resource_name")) + + assert excinfo.value.code() == grpc.StatusCode.INVALID_ARGUMENT + assert excinfo.value.details() == "Filter must evaluate to a boolean" + + +def test_coordinator_list_places(coordinator, coordinator_place): + stub = coordinator_place + res = stub.ListPlaces(labgrid_coordinator_pb2.ListPlacesRequest()) + assert res + assert len(res.places) == 1 + assert res.places[0].name == "test" + + +def test_coordinator_list_places_pagination_not_supported(coordinator, channel_stub): + with pytest.raises(grpc.RpcError) as excinfo: + channel_stub.ListPlaces(labgrid_coordinator_pb2.ListPlacesRequest(page_size=1)) + + assert excinfo.value.code() == grpc.StatusCode.UNIMPLEMENTED + assert excinfo.value.details() == "ListPlaces does not yet support pagination" + + +def test_coordinator_list_places_filter_on_place_name(coordinator, coordinator_place): + stub = coordinator_place + res = stub.ListPlaces(labgrid_coordinator_pb2.ListPlacesRequest(filter="name == 'test'")) + assert res + assert len(res.places) == 1 + assert res.places[0].name == "test" + + +def test_coordinator_list_places_filter_on_place_name_no_matches(coordinator, coordinator_place): + stub = coordinator_place + res = stub.ListPlaces(labgrid_coordinator_pb2.ListPlacesRequest(filter="name == 'missing'")) + assert res + assert len(res.places) == 0 + + +def test_coordinator_list_places_filter_invalid_syntax(coordinator, coordinator_place): + stub = coordinator_place + with pytest.raises(grpc.RpcError) as excinfo: + stub.ListPlaces(labgrid_coordinator_pb2.ListPlacesRequest(filter="name ==")) + assert excinfo.value.code() == grpc.StatusCode.INVALID_ARGUMENT + assert "Invalid filter" in excinfo.value.details() + + +def test_coordinator_list_places_filter_unknown_field(coordinator, coordinator_place): + stub = coordinator_place + with pytest.raises(grpc.RpcError) as excinfo: + stub.ListPlaces(labgrid_coordinator_pb2.ListPlacesRequest(filter="missing == 'x'")) + assert excinfo.value.code() == grpc.StatusCode.INVALID_ARGUMENT + assert "Invalid filter" in excinfo.value.details() + + +def test_coordinator_list_places_filter_must_evaluate_to_boolean(coordinator, coordinator_place): + stub = coordinator_place + with pytest.raises(grpc.RpcError) as excinfo: + stub.ListPlaces(labgrid_coordinator_pb2.ListPlacesRequest(filter="name")) + assert excinfo.value.code() == grpc.StatusCode.INVALID_ARGUMENT + assert "Filter must evaluate to a boolean" in excinfo.value.details() diff --git a/tests/test_interceptor_client.py b/tests/test_interceptor_client.py new file mode 100644 index 000000000..eb8623618 --- /dev/null +++ b/tests/test_interceptor_client.py @@ -0,0 +1,73 @@ +import pytest + +from labgrid.remote.grpc.interceptor.client import ( + BaseIdentityClientInterceptor, + IdentityClientUnaryUnaryInterceptor, + IdentityClientStreamStreamInterceptor, +) +from labgrid.remote.identity import USERNAME_KEY, HOSTNAME_KEY, USER_AGENT_KEY + + +class DummyMetadata: + def __init__(self): + self.items = [] + + def add(self, key, value): + self.items.append((key, value)) + + +class DummyClientCallDetails: + def __init__(self): + self.metadata = DummyMetadata() + + +def test_base_identity_client_interceptor_injects_all_fields(): + interceptor = BaseIdentityClientInterceptor( + username="test_username", + hostname="test_hostname", + user_agent="test_agent", + ) + + client_call_details = DummyClientCallDetails() + + interceptor._inject(client_call_details) + + assert client_call_details.metadata.items == [ + (USERNAME_KEY, "test_username"), + (HOSTNAME_KEY, "test_hostname"), + (USER_AGENT_KEY, "test_agent"), + ] + + +@pytest.mark.asyncio +@pytest.mark.parametrize( + "impl,method", + [ + (IdentityClientUnaryUnaryInterceptor, "intercept_unary_unary"), + (IdentityClientStreamStreamInterceptor, "intercept_stream_stream"), + ], +) +async def test_client_interceptor_implementations(impl, method): + interceptor = impl("test_username", "test_hostname", "test_agent") + + client_call_details = DummyClientCallDetails() + request_or_iterator = object() + sentinel_response = object() + + received = {} + + async def continuation(ccd, req): + received["ccd"] = ccd + received["req"] = req + return sentinel_response + + interceptor_method = getattr(interceptor, method) + result = await interceptor_method(continuation, client_call_details, request_or_iterator) + + assert result is sentinel_response + assert received["ccd"] is client_call_details + assert client_call_details.metadata.items == [ + (USERNAME_KEY, "test_username"), + (HOSTNAME_KEY, "test_hostname"), + (USER_AGENT_KEY, "test_agent"), + ] diff --git a/tests/test_interceptor_server.py b/tests/test_interceptor_server.py new file mode 100644 index 000000000..0b9741712 --- /dev/null +++ b/tests/test_interceptor_server.py @@ -0,0 +1,33 @@ +import contextvars, pytest +from types import SimpleNamespace +from unittest.mock import Mock +from labgrid.remote.grpc.interceptor.server import IdentityServerInterceptor + + +@pytest.fixture +def cv(): + return contextvars.ContextVar("client_identity", default=None) + + +@pytest.fixture +def interceptor(cv): + return IdentityServerInterceptor(cv) + + +def handler_call_details(metadata): + return SimpleNamespace(invocation_metadata=tuple(metadata)) + + +@pytest.mark.asyncio +async def test_server_interceptor_sets_contextvar(interceptor, cv): + handler = object() + continuation = Mock(return_value=handler) + + metadata = (("x-lg-hostname", "h"), ("x-lg-username", "u"), ("x-lg-user-agent", "ua")) + + ret = await interceptor.intercept_service(continuation, handler_call_details(metadata)) + assert ret is handler + + identity = cv.get() + assert identity.id == "h/u" + assert identity.user_agent == "ua" diff --git a/tests/test_remote.py b/tests/test_remote.py index 76a1da434..76021c63f 100644 --- a/tests/test_remote.py +++ b/tests/test_remote.py @@ -1,4 +1,9 @@ +import warnings + import pexpect +import pytest + +from labgrid.remote.common import get_metadata_single_value_by_key def test_client_help(): @@ -48,3 +53,50 @@ def test_exporter_coordinator_becomes_unreachable(coordinator, exporter): assert exporter.exitstatus == 100 coordinator.resume_tree() + + +def test_get_metadata_single_value_by_key_returns_value_for_existing_key(): + metadata = [("key1", "value1"), ("key2", "value2")] + assert get_metadata_single_value_by_key(metadata, "key1") == "value1" + assert get_metadata_single_value_by_key(metadata, "key2") == "value2" + + +def test_get_metadata_single_value_by_key_returns_none_for_missing_key(): + metadata = [("key1", "value1")] + assert get_metadata_single_value_by_key(metadata, "other") is None + + +def test_get_metadata_single_value_by_key_returns_none_for_empty_metadata(): + assert get_metadata_single_value_by_key((), "key") is None + + +def test_get_metadata_single_value_by_key_returns_none_for_none_metadata(): + assert get_metadata_single_value_by_key(None, "key") is None + + +def test_get_metadata_single_value_by_key_returns_first_value_on_duplicate_keys(): + metadata = [("key", "first"), ("key", "second")] + with pytest.warns(UserWarning, match="Multiple metadata KV pairs"): + result = get_metadata_single_value_by_key(metadata, "key") + assert result == "first" + + +def test_get_metadata_single_value_by_key_no_warning_on_single_match(): + metadata = [("key", "value")] + with warnings.catch_warnings(record=True) as caught: + warnings.simplefilter("always") + get_metadata_single_value_by_key(metadata, "key") + assert len(caught) == 0 + + +def test_get_metadata_single_value_by_key_returns_first_value_for_non_adjacent_duplicates(): + metadata = [("key", "first"), ("other", "value"), ("key", "second")] + with pytest.warns(UserWarning, match="Multiple metadata KV pairs"): + result = get_metadata_single_value_by_key(metadata, "key") + assert result == "first" + + +def test_get_metadata_single_value_by_key_is_case_sensitive(): + metadata = [("Key", "value")] + assert get_metadata_single_value_by_key(metadata, "key") is None + assert get_metadata_single_value_by_key(metadata, "Key") == "value"