remoteRF-server 0.1.63__tar.gz → 0.1.64__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {remoterf_server-0.1.63 → remoterf_server-0.1.64}/PKG-INFO +1 -1
- {remoterf_server-0.1.63 → remoterf_server-0.1.64}/pyproject.toml +1 -1
- {remoterf_server-0.1.63 → remoterf_server-0.1.64}/src/remoteRF_server/host/host_tunnel_server.py +64 -40
- {remoterf_server-0.1.63 → remoterf_server-0.1.64}/src/remoteRF_server.egg-info/PKG-INFO +1 -1
- {remoterf_server-0.1.63 → remoterf_server-0.1.64}/README.md +0 -0
- {remoterf_server-0.1.63 → remoterf_server-0.1.64}/setup.cfg +0 -0
- {remoterf_server-0.1.63 → remoterf_server-0.1.64}/src/remoteRF_server/__init__.py +0 -0
- {remoterf_server-0.1.63 → remoterf_server-0.1.64}/src/remoteRF_server/common/__init__.py +0 -0
- {remoterf_server-0.1.63 → remoterf_server-0.1.64}/src/remoteRF_server/common/grpc/__init__.py +0 -0
- {remoterf_server-0.1.63 → remoterf_server-0.1.64}/src/remoteRF_server/common/grpc/grpc_host_pb2.py +0 -0
- {remoterf_server-0.1.63 → remoterf_server-0.1.64}/src/remoteRF_server/common/grpc/grpc_host_pb2_grpc.py +0 -0
- {remoterf_server-0.1.63 → remoterf_server-0.1.64}/src/remoteRF_server/common/grpc/grpc_pb2.py +0 -0
- {remoterf_server-0.1.63 → remoterf_server-0.1.64}/src/remoteRF_server/common/grpc/grpc_pb2_grpc.py +0 -0
- {remoterf_server-0.1.63 → remoterf_server-0.1.64}/src/remoteRF_server/common/utils/__init__.py +0 -0
- {remoterf_server-0.1.63 → remoterf_server-0.1.64}/src/remoteRF_server/common/utils/ansi_codes.py +0 -0
- {remoterf_server-0.1.63 → remoterf_server-0.1.64}/src/remoteRF_server/common/utils/api_token.py +0 -0
- {remoterf_server-0.1.63 → remoterf_server-0.1.64}/src/remoteRF_server/common/utils/db_connection.py +0 -0
- {remoterf_server-0.1.63 → remoterf_server-0.1.64}/src/remoteRF_server/common/utils/db_location.py +0 -0
- {remoterf_server-0.1.63 → remoterf_server-0.1.64}/src/remoteRF_server/common/utils/list_string.py +0 -0
- {remoterf_server-0.1.63 → remoterf_server-0.1.64}/src/remoteRF_server/common/utils/process_arg.py +0 -0
- {remoterf_server-0.1.63 → remoterf_server-0.1.64}/src/remoteRF_server/drivers/__init__.py +0 -0
- {remoterf_server-0.1.63 → remoterf_server-0.1.64}/src/remoteRF_server/drivers/adalm_pluto/__init__.py +0 -0
- {remoterf_server-0.1.63 → remoterf_server-0.1.64}/src/remoteRF_server/drivers/adalm_pluto/pluto_remote_server.py +0 -0
- {remoterf_server-0.1.63 → remoterf_server-0.1.64}/src/remoteRF_server/host/__init__.py +0 -0
- {remoterf_server-0.1.63 → remoterf_server-0.1.64}/src/remoteRF_server/host/host_directory_store.py +0 -0
- {remoterf_server-0.1.63 → remoterf_server-0.1.64}/src/remoteRF_server/server/__init__.py +0 -0
- {remoterf_server-0.1.63 → remoterf_server-0.1.64}/src/remoteRF_server/server/acc_perms.py +0 -0
- {remoterf_server-0.1.63 → remoterf_server-0.1.64}/src/remoteRF_server/server/cert_provider.py +0 -0
- {remoterf_server-0.1.63 → remoterf_server-0.1.64}/src/remoteRF_server/server/device_manager.py +0 -0
- {remoterf_server-0.1.63 → remoterf_server-0.1.64}/src/remoteRF_server/server/grpc_server.py +0 -0
- {remoterf_server-0.1.63 → remoterf_server-0.1.64}/src/remoteRF_server/server/reservation.py +0 -0
- {remoterf_server-0.1.63 → remoterf_server-0.1.64}/src/remoteRF_server/server/rpc_manager.py +0 -0
- {remoterf_server-0.1.63 → remoterf_server-0.1.64}/src/remoteRF_server/server/user_group_cli.py +0 -0
- {remoterf_server-0.1.63 → remoterf_server-0.1.64}/src/remoteRF_server/server/user_group_handler.py +0 -0
- {remoterf_server-0.1.63 → remoterf_server-0.1.64}/src/remoteRF_server/serverrf_cli.py +0 -0
- {remoterf_server-0.1.63 → remoterf_server-0.1.64}/src/remoteRF_server/tools/__init__.py +0 -0
- {remoterf_server-0.1.63 → remoterf_server-0.1.64}/src/remoteRF_server/tools/gen_certs.py +0 -0
- {remoterf_server-0.1.63 → remoterf_server-0.1.64}/src/remoteRF_server.egg-info/SOURCES.txt +0 -0
- {remoterf_server-0.1.63 → remoterf_server-0.1.64}/src/remoteRF_server.egg-info/dependency_links.txt +0 -0
- {remoterf_server-0.1.63 → remoterf_server-0.1.64}/src/remoteRF_server.egg-info/entry_points.txt +0 -0
- {remoterf_server-0.1.63 → remoterf_server-0.1.64}/src/remoteRF_server.egg-info/requires.txt +0 -0
- {remoterf_server-0.1.63 → remoterf_server-0.1.64}/src/remoteRF_server.egg-info/top_level.txt +0 -0
{remoterf_server-0.1.63 → remoterf_server-0.1.64}/src/remoteRF_server/host/host_tunnel_server.py
RENAMED
|
@@ -34,10 +34,12 @@ from .host_directory_store import (
|
|
|
34
34
|
|
|
35
35
|
import hashlib
|
|
36
36
|
|
|
37
|
+
|
|
37
38
|
def host_key(host_id: str) -> str:
|
|
38
39
|
h = hashlib.sha256((host_id or "").encode("utf-8")).hexdigest()
|
|
39
40
|
return h[:16] # short stable key
|
|
40
41
|
|
|
42
|
+
|
|
41
43
|
def _copy_argument_map(dst_map: Any, src_map: Any) -> None:
|
|
42
44
|
if src_map is None:
|
|
43
45
|
return
|
|
@@ -108,6 +110,7 @@ class HostSession:
|
|
|
108
110
|
fut.set_exception(RuntimeError(f"Host '{self.host_id}' disconnected"))
|
|
109
111
|
self.inflight.clear()
|
|
110
112
|
|
|
113
|
+
|
|
111
114
|
class HostTunnelRegistry:
|
|
112
115
|
"""
|
|
113
116
|
Persistent host/device directory + live sessions.
|
|
@@ -193,16 +196,6 @@ class HostTunnelRegistry:
|
|
|
193
196
|
known_hosts = csv_split(kv_lists.get("KNOWN_HOSTS", ""))
|
|
194
197
|
known_devices = csv_split(kv_lists.get("KNOWN_DEVICES", ""))
|
|
195
198
|
|
|
196
|
-
# also accept hosts referenced by HOST_*_DEVICES keys even if KNOWN_HOSTS drifted
|
|
197
|
-
hosts_from_keys: Set[str] = set()
|
|
198
|
-
for k, v in kv_lists.items():
|
|
199
|
-
if k.startswith("HOST_") and k.endswith("_DEVICES"):
|
|
200
|
-
# We don't know the original host_id from the sanitized key reliably.
|
|
201
|
-
# That's fine; we still load routes primarily from DEVICE_*_HOST in meta.
|
|
202
|
-
# But we DO want to retain the device list; we’ll stitch it via meta host ids.
|
|
203
|
-
# So: just collect devices from these keys.
|
|
204
|
-
pass
|
|
205
|
-
|
|
206
199
|
# Reset directory + status (do not touch live sessions)
|
|
207
200
|
with self._lock:
|
|
208
201
|
self._routes.clear()
|
|
@@ -223,9 +216,6 @@ class HostTunnelRegistry:
|
|
|
223
216
|
if k.startswith("HOST_") and k.endswith("_DEVICES"):
|
|
224
217
|
devices_from_host_lists.update(csv_split(v))
|
|
225
218
|
|
|
226
|
-
devices_from_meta: Set[str] = set()
|
|
227
|
-
# any DEVICE_<X>_HOST implies a known device id (but we can’t reverse sanitize_env_key)
|
|
228
|
-
# so we rely on KNOWN_DEVICES + HOST_*_DEVICES as primary device-id sources.
|
|
229
219
|
# meta augments those devices; it can’t introduce new raw device ids safely.
|
|
230
220
|
all_devices: List[str] = sorted(set(known_devices) | devices_from_host_lists)
|
|
231
221
|
|
|
@@ -247,15 +237,11 @@ class HostTunnelRegistry:
|
|
|
247
237
|
kind = (kv_meta.get(f"DEVICE_{dk}_KIND", "") or "").strip()
|
|
248
238
|
|
|
249
239
|
if host_id:
|
|
250
|
-
# ensure host exists
|
|
251
240
|
if host_id not in self._host_status:
|
|
252
241
|
self._host_status[host_id] = HostStatus(host_id=host_id, online=False, last_seen_ms=0)
|
|
253
242
|
self._host_devices.setdefault(host_id, set()).add(device_id)
|
|
254
|
-
|
|
255
|
-
# route exists even while offline
|
|
256
243
|
self._routes[device_id] = DeviceRoute(host_id=host_id, host_local_id=int(local_id))
|
|
257
244
|
|
|
258
|
-
# keep info visible even when offline
|
|
259
245
|
self._device_infos[device_id] = host_tunnel_pb2.DeviceInfo(
|
|
260
246
|
device_id=str(device_id),
|
|
261
247
|
local_id=int(local_id),
|
|
@@ -277,6 +263,7 @@ class HostTunnelRegistry:
|
|
|
277
263
|
# ---------------------------
|
|
278
264
|
# persistence write helpers
|
|
279
265
|
# ---------------------------
|
|
266
|
+
|
|
280
267
|
def _persist_host_meta(self, host_id: str, *, host_name: str = "", platform: str = "", version: str = "") -> None:
|
|
281
268
|
hk = host_key(host_id)
|
|
282
269
|
self._env_meta.set_kv_if_absent(f"HOST_{hk}_ID", host_id)
|
|
@@ -287,6 +274,18 @@ class HostTunnelRegistry:
|
|
|
287
274
|
if version:
|
|
288
275
|
self._env_meta.upsert_kv(f"HOST_{hk}_VERSION", version)
|
|
289
276
|
|
|
277
|
+
def persist_host_meta_from_hello(self, hello: Any) -> None:
|
|
278
|
+
"""
|
|
279
|
+
Safe helper so HostTunnelServicer can persist from HostHello without needing to know field names.
|
|
280
|
+
"""
|
|
281
|
+
try:
|
|
282
|
+
hid = str(getattr(hello, "host_id", "") or "").strip()
|
|
283
|
+
ver = str(getattr(hello, "version", "") or "").strip()
|
|
284
|
+
if hid:
|
|
285
|
+
self._persist_host_meta(hid, version=ver)
|
|
286
|
+
except Exception:
|
|
287
|
+
pass
|
|
288
|
+
|
|
290
289
|
def _persist_host(self, host_id: str) -> None:
|
|
291
290
|
self._env_lists.append_to_csv_list("KNOWN_HOSTS", host_id)
|
|
292
291
|
|
|
@@ -296,7 +295,6 @@ class HostTunnelRegistry:
|
|
|
296
295
|
self._env_meta.set_kv_if_absent(f"HOST_{hk}_ID", host_id) # reverse map
|
|
297
296
|
self._env_lists.append_to_csv_list(f"HOST_{hk}_DEVICES", device_id)
|
|
298
297
|
|
|
299
|
-
|
|
300
298
|
dk = sanitize_env_key(device_id)
|
|
301
299
|
self._env_meta.upsert_kv(f"DEVICE_{dk}_HOST", host_id)
|
|
302
300
|
self._env_meta.upsert_kv(f"DEVICE_{dk}_LOCAL_ID", str(int(local_id)))
|
|
@@ -367,8 +365,6 @@ class HostTunnelRegistry:
|
|
|
367
365
|
ds.online = True
|
|
368
366
|
ds.last_seen_ms = now
|
|
369
367
|
|
|
370
|
-
# no need to invalidate full cache; active-cache TTL handles this
|
|
371
|
-
# but host status flips can matter; be conservative:
|
|
372
368
|
self._invalidate_caches_locked()
|
|
373
369
|
|
|
374
370
|
def announce_devices(self, host_id: str, devices: list) -> None:
|
|
@@ -399,17 +395,14 @@ class HostTunnelRegistry:
|
|
|
399
395
|
if not device_id:
|
|
400
396
|
continue
|
|
401
397
|
|
|
402
|
-
# local_id is basically not used; keep for completeness
|
|
403
398
|
try:
|
|
404
399
|
local_id = int(getattr(d, "local_id", 0))
|
|
405
400
|
except Exception:
|
|
406
401
|
local_id = 0
|
|
407
402
|
|
|
408
|
-
# route
|
|
409
403
|
self._routes[device_id] = DeviceRoute(host_id=host_id, host_local_id=local_id)
|
|
410
404
|
self._host_devices[host_id].add(device_id)
|
|
411
405
|
|
|
412
|
-
# status
|
|
413
406
|
ds = self._device_status.get(device_id) or DeviceStatus(
|
|
414
407
|
device_id=device_id,
|
|
415
408
|
host_id=host_id,
|
|
@@ -421,13 +414,11 @@ class HostTunnelRegistry:
|
|
|
421
414
|
ds.last_seen_ms = now
|
|
422
415
|
self._device_status[device_id] = ds
|
|
423
416
|
|
|
424
|
-
# info (copy protobuf if possible)
|
|
425
417
|
try:
|
|
426
418
|
tmp = host_tunnel_pb2.DeviceInfo()
|
|
427
419
|
tmp.CopyFrom(d)
|
|
428
420
|
self._device_infos[device_id] = tmp
|
|
429
421
|
except Exception:
|
|
430
|
-
# fallback minimal
|
|
431
422
|
self._device_infos[device_id] = host_tunnel_pb2.DeviceInfo(
|
|
432
423
|
device_id=device_id,
|
|
433
424
|
local_id=int(local_id),
|
|
@@ -445,10 +436,6 @@ class HostTunnelRegistry:
|
|
|
445
436
|
# ---------------------------
|
|
446
437
|
|
|
447
438
|
def is_host_device(self, device_id: str) -> bool:
|
|
448
|
-
"""
|
|
449
|
-
O(1): does the directory route this device_id to a host?
|
|
450
|
-
(Host may be offline.)
|
|
451
|
-
"""
|
|
452
439
|
did = (device_id or "").strip()
|
|
453
440
|
if not did:
|
|
454
441
|
return False
|
|
@@ -456,10 +443,6 @@ class HostTunnelRegistry:
|
|
|
456
443
|
return did in self._routes
|
|
457
444
|
|
|
458
445
|
def is_host_device_active(self, device_id: str) -> bool:
|
|
459
|
-
"""
|
|
460
|
-
O(1) route lookup + cached host-alive check (TTL).
|
|
461
|
-
True iff device routes to a host AND that host's tunnel session is alive.
|
|
462
|
-
"""
|
|
463
446
|
did = (device_id or "").strip()
|
|
464
447
|
if not did:
|
|
465
448
|
return False
|
|
@@ -485,12 +468,6 @@ class HostTunnelRegistry:
|
|
|
485
468
|
# ---------------------------
|
|
486
469
|
|
|
487
470
|
def device_directory_cached(self, *, ttl_ms: Optional[int] = None) -> Dict[str, Tuple[str, Any, bool]]:
|
|
488
|
-
"""
|
|
489
|
-
Returns:
|
|
490
|
-
{ device_id: (host_id, device_info, is_active) }
|
|
491
|
-
|
|
492
|
-
Cached rebuild at most once per ttl_ms.
|
|
493
|
-
"""
|
|
494
471
|
now = now_ms()
|
|
495
472
|
ttl = int(self._dir_cache_ttl_ms if ttl_ms is None else ttl_ms)
|
|
496
473
|
|
|
@@ -647,6 +624,7 @@ class HostTunnelRegistry:
|
|
|
647
624
|
cancel_on_timeout=cancel_on_timeout,
|
|
648
625
|
)
|
|
649
626
|
|
|
627
|
+
|
|
650
628
|
def handle_host_device(
|
|
651
629
|
registry: HostTunnelRegistry,
|
|
652
630
|
*,
|
|
@@ -711,6 +689,23 @@ class HostTunnelServicer(host_tunnel_pb2_grpc.HostTunnelServicer):
|
|
|
711
689
|
self.registry.register_host(host_id, session)
|
|
712
690
|
self.registry.persist_host_meta_from_hello(hello)
|
|
713
691
|
|
|
692
|
+
# Ask host for metadata immediately on join (best-effort)
|
|
693
|
+
try:
|
|
694
|
+
mrid = secrets.token_hex(12)
|
|
695
|
+
session.send(
|
|
696
|
+
host_tunnel_pb2.HostFrame(
|
|
697
|
+
meta_request=host_tunnel_pb2.MetaRequest(
|
|
698
|
+
req_id=mrid,
|
|
699
|
+
include_platform=True,
|
|
700
|
+
include_env=True,
|
|
701
|
+
include_devices=True,
|
|
702
|
+
)
|
|
703
|
+
)
|
|
704
|
+
)
|
|
705
|
+
except Exception:
|
|
706
|
+
pass
|
|
707
|
+
|
|
708
|
+
# Also ping/ack
|
|
714
709
|
try:
|
|
715
710
|
session.send(
|
|
716
711
|
host_tunnel_pb2.HostFrame(
|
|
@@ -753,6 +748,35 @@ class HostTunnelServicer(host_tunnel_pb2_grpc.HostTunnelServicer):
|
|
|
753
748
|
if fut is not None and not fut.done():
|
|
754
749
|
fut.set_result(resp)
|
|
755
750
|
|
|
751
|
+
elif which == "meta_response":
|
|
752
|
+
# Print host metadata (best-effort diagnostics)
|
|
753
|
+
try:
|
|
754
|
+
mr = frame.meta_response
|
|
755
|
+
if not mr.ok:
|
|
756
|
+
print(f"[HOST META] host_id={host_id or '?'} ERROR: {mr.error}")
|
|
757
|
+
continue
|
|
758
|
+
|
|
759
|
+
meta = mr.meta
|
|
760
|
+
print(f"\n[HOST META] host_id={meta.host_id} version={meta.version} unix_ms={meta.unix_ms}")
|
|
761
|
+
|
|
762
|
+
if meta.kv:
|
|
763
|
+
for k in sorted(meta.kv.keys()):
|
|
764
|
+
print(f" {k}={meta.kv[k]}")
|
|
765
|
+
|
|
766
|
+
if meta.devices:
|
|
767
|
+
print(" devices:")
|
|
768
|
+
for d in meta.devices:
|
|
769
|
+
print(
|
|
770
|
+
f" - local_id={d.local_id} "
|
|
771
|
+
f"label={d.label!r} "
|
|
772
|
+
f"device_id={d.device_id!r} "
|
|
773
|
+
f"serial={d.serial!r} "
|
|
774
|
+
f"kind={d.kind!r}"
|
|
775
|
+
)
|
|
776
|
+
print()
|
|
777
|
+
except Exception:
|
|
778
|
+
pass
|
|
779
|
+
|
|
756
780
|
elif which == "heartbeat":
|
|
757
781
|
if session is not None and host_id is not None:
|
|
758
782
|
session.last_heartbeat_ms = int(frame.heartbeat.unix_ms) or now_ms()
|
|
@@ -844,4 +868,4 @@ def start_host_tunnel_server(
|
|
|
844
868
|
|
|
845
869
|
th = threading.Thread(target=run, daemon=True)
|
|
846
870
|
th.start()
|
|
847
|
-
return server, th, reg
|
|
871
|
+
return server, th, reg
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{remoterf_server-0.1.63 → remoterf_server-0.1.64}/src/remoteRF_server/common/grpc/__init__.py
RENAMED
|
File without changes
|
{remoterf_server-0.1.63 → remoterf_server-0.1.64}/src/remoteRF_server/common/grpc/grpc_host_pb2.py
RENAMED
|
File without changes
|
|
File without changes
|
{remoterf_server-0.1.63 → remoterf_server-0.1.64}/src/remoteRF_server/common/grpc/grpc_pb2.py
RENAMED
|
File without changes
|
{remoterf_server-0.1.63 → remoterf_server-0.1.64}/src/remoteRF_server/common/grpc/grpc_pb2_grpc.py
RENAMED
|
File without changes
|
{remoterf_server-0.1.63 → remoterf_server-0.1.64}/src/remoteRF_server/common/utils/__init__.py
RENAMED
|
File without changes
|
{remoterf_server-0.1.63 → remoterf_server-0.1.64}/src/remoteRF_server/common/utils/ansi_codes.py
RENAMED
|
File without changes
|
{remoterf_server-0.1.63 → remoterf_server-0.1.64}/src/remoteRF_server/common/utils/api_token.py
RENAMED
|
File without changes
|
{remoterf_server-0.1.63 → remoterf_server-0.1.64}/src/remoteRF_server/common/utils/db_connection.py
RENAMED
|
File without changes
|
{remoterf_server-0.1.63 → remoterf_server-0.1.64}/src/remoteRF_server/common/utils/db_location.py
RENAMED
|
File without changes
|
{remoterf_server-0.1.63 → remoterf_server-0.1.64}/src/remoteRF_server/common/utils/list_string.py
RENAMED
|
File without changes
|
{remoterf_server-0.1.63 → remoterf_server-0.1.64}/src/remoteRF_server/common/utils/process_arg.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{remoterf_server-0.1.63 → remoterf_server-0.1.64}/src/remoteRF_server/host/host_directory_store.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{remoterf_server-0.1.63 → remoterf_server-0.1.64}/src/remoteRF_server/server/cert_provider.py
RENAMED
|
File without changes
|
{remoterf_server-0.1.63 → remoterf_server-0.1.64}/src/remoteRF_server/server/device_manager.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{remoterf_server-0.1.63 → remoterf_server-0.1.64}/src/remoteRF_server/server/user_group_cli.py
RENAMED
|
File without changes
|
{remoterf_server-0.1.63 → remoterf_server-0.1.64}/src/remoteRF_server/server/user_group_handler.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{remoterf_server-0.1.63 → remoterf_server-0.1.64}/src/remoteRF_server.egg-info/dependency_links.txt
RENAMED
|
File without changes
|
{remoterf_server-0.1.63 → remoterf_server-0.1.64}/src/remoteRF_server.egg-info/entry_points.txt
RENAMED
|
File without changes
|
|
File without changes
|
{remoterf_server-0.1.63 → remoterf_server-0.1.64}/src/remoteRF_server.egg-info/top_level.txt
RENAMED
|
File without changes
|