diff --git a/labgrid/remote/client.py b/labgrid/remote/client.py index 4d2eb0bfa..e7707106c 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() @@ -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()) @@ -836,6 +847,21 @@ async def allow(self): print(f"allowed {self.args.user} for place {place.name}") + 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) resources = {} @@ -1917,9 +1943,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) @@ -1978,6 +2004,10 @@ def get_parser(auto_doc_mode=False) -> "argparse.ArgumentParser | AutoProgramArg subparser.add_argument("user", help="/") subparser.set_defaults(func=ClientSession.allow) + subparser = subparsers.add_parser("unshare", help="remove another user's access to a place") + subparser.add_argument("user", help="/") + 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..3fc3657b6 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,15 @@ import copy import random import signal +from typing import Optional import attr 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 +35,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 +326,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 +347,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 +429,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 +456,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 +536,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 +551,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 +903,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) @@ -922,7 +973,7 @@ async def AllowPlace(self, request, context): user = request.user 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: @@ -941,9 +992,55 @@ async def AllowPlace(self, request, context): self.save_later() return labgrid_coordinator_pb2.AllowPlaceResponse() + @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") @@ -1076,7 +1173,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() @@ -1108,7 +1208,6 @@ async def GetReservations(self, request: labgrid_coordinator_pb2.GetReservations 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 +1225,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..ff78465ae 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,169 @@ -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\"\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\"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\"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\xb3\r\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\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\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\x12V\n\x0fPollReservation\x12\x1f.labgrid.PollReservationRequest\x1a .labgrid.PollReservationResponse\"\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['_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['_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['_COORDINATOR'].methods_by_name['AddPlace']._loaded_options = None + _globals['_COORDINATOR'].methods_by_name['AddPlace']._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=2006 + _globals['_GETPLACESRESPONSE']._serialized_start=2008 + _globals['_GETPLACESRESPONSE']._serialized_end=2059 + _globals['_PLACE']._serialized_start=2062 + _globals['_PLACE']._serialized_end=2400 + _globals['_PLACE_TAGSENTRY']._serialized_start=2328 + _globals['_PLACE_TAGSENTRY']._serialized_end=2371 + _globals['_RESOURCEMATCH']._serialized_start=2402 + _globals['_RESOURCEMATCH']._serialized_end=2523 + _globals['_ADDPLACEALIASREQUEST']._serialized_start=2525 + _globals['_ADDPLACEALIASREQUEST']._serialized_end=2581 + _globals['_ADDPLACEALIASRESPONSE']._serialized_start=2583 + _globals['_ADDPLACEALIASRESPONSE']._serialized_end=2606 + _globals['_DELETEPLACEALIASREQUEST']._serialized_start=2608 + _globals['_DELETEPLACEALIASREQUEST']._serialized_end=2667 + _globals['_DELETEPLACEALIASRESPONSE']._serialized_start=2669 + _globals['_DELETEPLACEALIASRESPONSE']._serialized_end=2695 + _globals['_SETPLACETAGSREQUEST']._serialized_start=2698 + _globals['_SETPLACETAGSREQUEST']._serialized_end=2837 + _globals['_SETPLACETAGSREQUEST_TAGSENTRY']._serialized_start=2328 + _globals['_SETPLACETAGSREQUEST_TAGSENTRY']._serialized_end=2371 + _globals['_SETPLACETAGSRESPONSE']._serialized_start=2839 + _globals['_SETPLACETAGSRESPONSE']._serialized_end=2861 + _globals['_SETPLACECOMMENTREQUEST']._serialized_start=2863 + _globals['_SETPLACECOMMENTREQUEST']._serialized_end=2923 + _globals['_SETPLACECOMMENTRESPONSE']._serialized_start=2925 + _globals['_SETPLACECOMMENTRESPONSE']._serialized_end=2950 + _globals['_ADDPLACEMATCHREQUEST']._serialized_start=2952 + _globals['_ADDPLACEMATCHREQUEST']._serialized_end=3042 + _globals['_ADDPLACEMATCHRESPONSE']._serialized_start=3044 + _globals['_ADDPLACEMATCHRESPONSE']._serialized_end=3067 + _globals['_DELETEPLACEMATCHREQUEST']._serialized_start=3069 + _globals['_DELETEPLACEMATCHREQUEST']._serialized_end=3162 + _globals['_DELETEPLACEMATCHRESPONSE']._serialized_start=3164 + _globals['_DELETEPLACEMATCHRESPONSE']._serialized_end=3190 + _globals['_ACQUIREPLACEREQUEST']._serialized_start=3192 + _globals['_ACQUIREPLACEREQUEST']._serialized_end=3232 + _globals['_ACQUIREPLACERESPONSE']._serialized_start=3234 + _globals['_ACQUIREPLACERESPONSE']._serialized_end=3256 + _globals['_RELEASEPLACEREQUEST']._serialized_start=3258 + _globals['_RELEASEPLACEREQUEST']._serialized_end=3334 + _globals['_RELEASEPLACERESPONSE']._serialized_start=3336 + _globals['_RELEASEPLACERESPONSE']._serialized_end=3358 + _globals['_ALLOWPLACEREQUEST']._serialized_start=3360 + _globals['_ALLOWPLACEREQUEST']._serialized_end=3412 + _globals['_ALLOWPLACERESPONSE']._serialized_start=3414 + _globals['_ALLOWPLACERESPONSE']._serialized_end=3434 + _globals['_UNSHAREPLACEREQUEST']._serialized_start=3436 + _globals['_UNSHAREPLACEREQUEST']._serialized_end=3485 + _globals['_UNSHAREPLACERESPONSE']._serialized_start=3487 + _globals['_UNSHAREPLACERESPONSE']._serialized_end=3509 + _globals['_CREATERESERVATIONREQUEST']._serialized_start=3512 + _globals['_CREATERESERVATIONREQUEST']._serialized_end=3694 + _globals['_CREATERESERVATIONREQUEST_FILTERSENTRY']._serialized_start=3619 + _globals['_CREATERESERVATIONREQUEST_FILTERSENTRY']._serialized_end=3694 + _globals['_CREATERESERVATIONRESPONSE']._serialized_start=3696 + _globals['_CREATERESERVATIONRESPONSE']._serialized_end=3766 + _globals['_RESERVATION']._serialized_start=3769 + _globals['_RESERVATION']._serialized_end=4230 + _globals['_RESERVATION_FILTER']._serialized_start=3989 + _globals['_RESERVATION_FILTER']._serialized_end=4101 + _globals['_RESERVATION_FILTER_FILTERENTRY']._serialized_start=4056 + _globals['_RESERVATION_FILTER_FILTERENTRY']._serialized_end=4101 + _globals['_RESERVATION_FILTERSENTRY']._serialized_start=3619 + _globals['_RESERVATION_FILTERSENTRY']._serialized_end=3694 + _globals['_RESERVATION_ALLOCATIONSENTRY']._serialized_start=4180 + _globals['_RESERVATION_ALLOCATIONSENTRY']._serialized_end=4230 + _globals['_CANCELRESERVATIONREQUEST']._serialized_start=4232 + _globals['_CANCELRESERVATIONREQUEST']._serialized_end=4273 + _globals['_CANCELRESERVATIONRESPONSE']._serialized_start=4275 + _globals['_CANCELRESERVATIONRESPONSE']._serialized_end=4302 + _globals['_POLLRESERVATIONREQUEST']._serialized_start=4304 + _globals['_POLLRESERVATIONREQUEST']._serialized_end=4343 + _globals['_POLLRESERVATIONRESPONSE']._serialized_start=4345 + _globals['_POLLRESERVATIONRESPONSE']._serialized_end=4413 + _globals['_GETRESERVATIONSRESPONSE']._serialized_start=4415 + _globals['_GETRESERVATIONSRESPONSE']._serialized_end=4484 + _globals['_GETRESERVATIONSREQUEST']._serialized_start=4486 + _globals['_GETRESERVATIONSREQUEST']._serialized_end=4510 + _globals['_COORDINATOR']._serialized_start=4513 + _globals['_COORDINATOR']._serialized_end=6228 # @@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..3181b8f17 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: ... @@ -348,6 +373,18 @@ class AllowPlaceResponse(_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): diff --git a/labgrid/remote/generated/labgrid_coordinator_pb2_grpc.py b/labgrid/remote/generated/labgrid_coordinator_pb2_grpc.py index debfb24f2..6a0661a9b 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,107 @@ 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.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.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.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,12 +162,24 @@ 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) @@ -193,6 +240,12 @@ def AllowPlace(self, request, context): 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) @@ -235,11 +288,21 @@ 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, @@ -290,6 +353,11 @@ def add_CoordinatorServicer_to_server(servicer, server): request_deserializer=labgrid__coordinator__pb2.AllowPlaceRequest.FromString, response_serializer=labgrid__coordinator__pb2.AllowPlaceResponse.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, @@ -314,6 +382,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 +400,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 +427,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 +454,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 +508,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 +562,21 @@ 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 AddPlaceAlias(request, @@ -416,11 +589,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 +616,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 +643,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 +670,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 +697,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 +724,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 +751,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 +778,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 +805,48 @@ 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 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 +859,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 +886,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 +913,21 @@ 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 GetReservations(request, @@ -620,8 +940,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..6bb603478 100644 --- a/labgrid/remote/proto/labgrid-coordinator.proto +++ b/labgrid/remote/proto/labgrid-coordinator.proto @@ -7,10 +7,16 @@ 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 GetPlace(GetPlaceRequest) returns (GetPlaceResponse) {} + rpc GetPlaces(GetPlacesRequest) returns (GetPlacesResponse) {} rpc AddPlaceAlias(AddPlaceAliasRequest) returns (AddPlaceAliasResponse) {} @@ -31,6 +37,8 @@ service Coordinator { rpc AllowPlace(AllowPlaceRequest) returns (AllowPlaceResponse) {} + rpc UnsharePlace(UnsharePlaceRequest) returns (UnsharePlaceResponse) {} + rpc CreateReservation(CreateReservationRequest) returns (CreateReservationResponse) {} rpc CancelReservation(CancelReservationRequest) returns (CancelReservationResponse) {} @@ -43,7 +51,7 @@ service Coordinator { message ClientInMessage { oneof kind { Sync sync = 1; - StartupDone startup = 2; + StartupDone startup = 2 [deprecated = true]; Subscribe subscribe = 3; }; }; @@ -53,6 +61,8 @@ message Sync { }; message StartupDone { + option deprecated = true; + string version = 1; string name = 2; }; @@ -82,7 +92,7 @@ message UpdateResponse { message ExporterInMessage { oneof kind { Resource resource = 1; - StartupDone startup = 2; + StartupDone startup = 2 [deprecated = true]; ExporterResponse response = 3; }; }; @@ -135,10 +145,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,6 +169,14 @@ message DeletePlaceRequest { message DeletePlaceResponse { }; +message GetPlaceRequest { + string name = 1; +}; + +message GetPlaceResponse { + Place place = 1; +} + message GetPlacesRequest { }; @@ -250,6 +279,13 @@ message AllowPlaceRequest { message AllowPlaceResponse { }; +message UnsharePlaceRequest { + string name = 1; + string user = 2; +}; + +message UnsharePlaceResponse { +}; message CreateReservationRequest { map filters = 1; diff --git a/man/labgrid-client.1 b/man/labgrid-client.1 index dfc95b2e8..ffd6294bb 100644 --- a/man/labgrid-client.1 +++ b/man/labgrid-client.1 @@ -259,7 +259,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 @@ -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..bd964d48e 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -84,6 +84,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 +121,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..ffd7d780f 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 diff --git a/tests/test_coordinator.py b/tests/test_coordinator.py index 2907baed0..574a24e44 100644 --- a/tests/test_coordinator.py +++ b/tests/test_coordinator.py @@ -51,6 +51,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) @@ -175,6 +206,30 @@ def test_coordinator_place_allow(coordinator, coordinator_place): assert res +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): tags = {"board": "test"} stub = coordinator_place @@ -191,3 +246,39 @@ 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" 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"