remoteRF-server 0.1.66__tar.gz → 0.1.68__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.
Files changed (42) hide show
  1. {remoterf_server-0.1.66 → remoterf_server-0.1.68}/PKG-INFO +1 -1
  2. {remoterf_server-0.1.66 → remoterf_server-0.1.68}/pyproject.toml +1 -1
  3. {remoterf_server-0.1.66 → remoterf_server-0.1.68}/src/remoteRF_server/host/host_tunnel_server.py +62 -11
  4. {remoterf_server-0.1.66 → remoterf_server-0.1.68}/src/remoteRF_server/server/grpc_server.py +95 -2
  5. {remoterf_server-0.1.66 → remoterf_server-0.1.68}/src/remoteRF_server.egg-info/PKG-INFO +1 -1
  6. {remoterf_server-0.1.66 → remoterf_server-0.1.68}/README.md +0 -0
  7. {remoterf_server-0.1.66 → remoterf_server-0.1.68}/setup.cfg +0 -0
  8. {remoterf_server-0.1.66 → remoterf_server-0.1.68}/src/remoteRF_server/__init__.py +0 -0
  9. {remoterf_server-0.1.66 → remoterf_server-0.1.68}/src/remoteRF_server/common/__init__.py +0 -0
  10. {remoterf_server-0.1.66 → remoterf_server-0.1.68}/src/remoteRF_server/common/grpc/__init__.py +0 -0
  11. {remoterf_server-0.1.66 → remoterf_server-0.1.68}/src/remoteRF_server/common/grpc/grpc_host_pb2.py +0 -0
  12. {remoterf_server-0.1.66 → remoterf_server-0.1.68}/src/remoteRF_server/common/grpc/grpc_host_pb2_grpc.py +0 -0
  13. {remoterf_server-0.1.66 → remoterf_server-0.1.68}/src/remoteRF_server/common/grpc/grpc_pb2.py +0 -0
  14. {remoterf_server-0.1.66 → remoterf_server-0.1.68}/src/remoteRF_server/common/grpc/grpc_pb2_grpc.py +0 -0
  15. {remoterf_server-0.1.66 → remoterf_server-0.1.68}/src/remoteRF_server/common/utils/__init__.py +0 -0
  16. {remoterf_server-0.1.66 → remoterf_server-0.1.68}/src/remoteRF_server/common/utils/ansi_codes.py +0 -0
  17. {remoterf_server-0.1.66 → remoterf_server-0.1.68}/src/remoteRF_server/common/utils/api_token.py +0 -0
  18. {remoterf_server-0.1.66 → remoterf_server-0.1.68}/src/remoteRF_server/common/utils/db_connection.py +0 -0
  19. {remoterf_server-0.1.66 → remoterf_server-0.1.68}/src/remoteRF_server/common/utils/db_location.py +0 -0
  20. {remoterf_server-0.1.66 → remoterf_server-0.1.68}/src/remoteRF_server/common/utils/list_string.py +0 -0
  21. {remoterf_server-0.1.66 → remoterf_server-0.1.68}/src/remoteRF_server/common/utils/process_arg.py +0 -0
  22. {remoterf_server-0.1.66 → remoterf_server-0.1.68}/src/remoteRF_server/drivers/__init__.py +0 -0
  23. {remoterf_server-0.1.66 → remoterf_server-0.1.68}/src/remoteRF_server/drivers/adalm_pluto/__init__.py +0 -0
  24. {remoterf_server-0.1.66 → remoterf_server-0.1.68}/src/remoteRF_server/drivers/adalm_pluto/pluto_remote_server.py +0 -0
  25. {remoterf_server-0.1.66 → remoterf_server-0.1.68}/src/remoteRF_server/host/__init__.py +0 -0
  26. {remoterf_server-0.1.66 → remoterf_server-0.1.68}/src/remoteRF_server/host/host_directory_store.py +0 -0
  27. {remoterf_server-0.1.66 → remoterf_server-0.1.68}/src/remoteRF_server/server/__init__.py +0 -0
  28. {remoterf_server-0.1.66 → remoterf_server-0.1.68}/src/remoteRF_server/server/acc_perms.py +0 -0
  29. {remoterf_server-0.1.66 → remoterf_server-0.1.68}/src/remoteRF_server/server/cert_provider.py +0 -0
  30. {remoterf_server-0.1.66 → remoterf_server-0.1.68}/src/remoteRF_server/server/device_manager.py +0 -0
  31. {remoterf_server-0.1.66 → remoterf_server-0.1.68}/src/remoteRF_server/server/reservation.py +0 -0
  32. {remoterf_server-0.1.66 → remoterf_server-0.1.68}/src/remoteRF_server/server/rpc_manager.py +0 -0
  33. {remoterf_server-0.1.66 → remoterf_server-0.1.68}/src/remoteRF_server/server/user_group_cli.py +0 -0
  34. {remoterf_server-0.1.66 → remoterf_server-0.1.68}/src/remoteRF_server/server/user_group_handler.py +0 -0
  35. {remoterf_server-0.1.66 → remoterf_server-0.1.68}/src/remoteRF_server/serverrf_cli.py +0 -0
  36. {remoterf_server-0.1.66 → remoterf_server-0.1.68}/src/remoteRF_server/tools/__init__.py +0 -0
  37. {remoterf_server-0.1.66 → remoterf_server-0.1.68}/src/remoteRF_server/tools/gen_certs.py +0 -0
  38. {remoterf_server-0.1.66 → remoterf_server-0.1.68}/src/remoteRF_server.egg-info/SOURCES.txt +0 -0
  39. {remoterf_server-0.1.66 → remoterf_server-0.1.68}/src/remoteRF_server.egg-info/dependency_links.txt +0 -0
  40. {remoterf_server-0.1.66 → remoterf_server-0.1.68}/src/remoteRF_server.egg-info/entry_points.txt +0 -0
  41. {remoterf_server-0.1.66 → remoterf_server-0.1.68}/src/remoteRF_server.egg-info/requires.txt +0 -0
  42. {remoterf_server-0.1.66 → remoterf_server-0.1.68}/src/remoteRF_server.egg-info/top_level.txt +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: remoteRF-server
3
- Version: 0.1.66
3
+ Version: 0.1.68
4
4
  Description-Content-Type: text/markdown
5
5
  Requires-Dist: grpcio
6
6
  Requires-Dist: protobuf
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "remoteRF-server"
7
- version = "0.1.66"
7
+ version = "0.1.68"
8
8
  dependencies = [
9
9
  "grpcio",
10
10
  "protobuf",
@@ -18,6 +18,7 @@ from concurrent.futures import Future
18
18
  from concurrent import futures
19
19
 
20
20
  import grpc
21
+ import os
21
22
 
22
23
  # Your existing GenericRPC schema compiled to python.
23
24
  from ..common.grpc import grpc_pb2 as generic_pb2 # type: ignore
@@ -38,9 +39,58 @@ from .host_directory_store import (
38
39
  )
39
40
 
40
41
  # =============================================================================
41
- # Always-on logging (INFO/WARN/ERROR always print; no config/levels)
42
+ # Always-on logging with lightweight toggles (env + runtime)
42
43
  # =============================================================================
43
44
 
45
+ _LOG_LOCK = threading.Lock()
46
+ _LOG_LEVELS: set[str] = set() # {"INFO","WARN","ERROR"}
47
+ _LOG_SUPPRESS: set[str] = set() # {"HEARTBEAT","OUTBOUND","INBOUND"}
48
+
49
+ def _init_log_state_from_env() -> None:
50
+ """
51
+ Env knobs:
52
+ REMOTERF_LOG_LEVELS="INFO,WARN,ERROR" (default: WARN,ERROR)
53
+ REMOTERF_LOG_SUPPRESS="HEARTBEAT" (default: HEARTBEAT)
54
+ """
55
+ global _LOG_LEVELS, _LOG_SUPPRESS
56
+
57
+ levels = os.getenv("REMOTERF_LOG_LEVELS", "").strip()
58
+ suppress = os.getenv("REMOTERF_LOG_SUPPRESS", "").strip()
59
+
60
+ if not levels:
61
+ _LOG_LEVELS = {"WARN", "ERROR"}
62
+ else:
63
+ _LOG_LEVELS = {x.strip().upper() for x in levels.split(",") if x.strip()}
64
+
65
+ if not suppress:
66
+ _LOG_SUPPRESS = {"HEARTBEAT"} # sane default: avoid spam
67
+ else:
68
+ _LOG_SUPPRESS = {x.strip().upper() for x in suppress.split(",") if x.strip()}
69
+
70
+ _init_log_state_from_env()
71
+
72
+ def set_log_levels(levels: str) -> None:
73
+ """Runtime: set levels, e.g. set_log_levels('INFO,WARN,ERROR')"""
74
+ global _LOG_LEVELS
75
+ with _LOG_LOCK:
76
+ _LOG_LEVELS = {x.strip().upper() for x in (levels or "").split(",") if x.strip()}
77
+
78
+ def set_log_suppress(tags: str) -> None:
79
+ """Runtime: set suppressed tags, e.g. set_log_suppress('HEARTBEAT,OUTBOUND')"""
80
+ global _LOG_SUPPRESS
81
+ with _LOG_LOCK:
82
+ _LOG_SUPPRESS = {x.strip().upper() for x in (tags or "").split(",") if x.strip()}
83
+
84
+ def _should_log(level: str, tag: str = "") -> bool:
85
+ lvl = (level or "").upper()
86
+ tg = (tag or "").upper()
87
+ with _LOG_LOCK:
88
+ if lvl not in _LOG_LEVELS:
89
+ return False
90
+ if tg and tg in _LOG_SUPPRESS:
91
+ return False
92
+ return True
93
+
44
94
  def _ts() -> str:
45
95
  t = time.time()
46
96
  sec = int(t)
@@ -48,24 +98,25 @@ def _ts() -> str:
48
98
  lt = time.localtime(sec)
49
99
  return time.strftime("%H:%M:%S", lt) + f".{ms:03d}"
50
100
 
51
- def _log(level: str, msg: str) -> None:
101
+ def _log(level: str, msg: str, *, tag: str = "") -> None:
102
+ if not _should_log(level, tag):
103
+ return
52
104
  tn = threading.current_thread().name
53
105
  print(f"{_ts()} {level:<5} [{tn}] hostrf.host_tunnel_server: {msg}", file=sys.stderr, flush=True)
54
106
 
55
- def info(msg: str) -> None:
56
- _log("INFO", msg)
107
+ def info(msg: str, *, tag: str = "") -> None:
108
+ _log("INFO", msg, tag=tag)
57
109
 
58
- def warn(msg: str) -> None:
59
- _log("WARN", msg)
110
+ def warn(msg: str, *, tag: str = "") -> None:
111
+ _log("WARN", msg, tag=tag)
60
112
 
61
- def error(msg: str) -> None:
62
- _log("ERROR", msg)
113
+ def error(msg: str, *, tag: str = "") -> None:
114
+ _log("ERROR", msg, tag=tag)
63
115
 
64
- def exception(msg: str) -> None:
65
- _log("ERROR", msg)
116
+ def exception(msg: str, *, tag: str = "") -> None:
117
+ _log("ERROR", msg, tag=tag)
66
118
  traceback.print_exc(limit=80, file=sys.stderr)
67
119
 
68
-
69
120
  # =============================================================================
70
121
  # Helpers
71
122
  # =============================================================================
@@ -17,6 +17,8 @@ from datetime import datetime
17
17
  # os.environ.setdefault("GRPC_VERBOSITY", "ERROR")
18
18
  # os.environ.setdefault("GRPC_TRACE", "")
19
19
 
20
+ TUNNEL_REGISTRY: HostTunnelRegistry | None = None
21
+
20
22
  import grpc
21
23
 
22
24
  import traceback
@@ -201,10 +203,13 @@ def serve():
201
203
 
202
204
  # --- attach HostTunnel to the SAME grpc server/port/creds ---
203
205
  directory_env = Path(__file__).resolve().parent.parent / "config" / ".env.host_directory"
204
- registry = HostTunnelRegistry()
206
+
207
+ # --- attach HostTunnel to the SAME grpc server/port/creds ---
208
+ global TUNNEL_REGISTRY
209
+ TUNNEL_REGISTRY = HostTunnelRegistry()
205
210
 
206
211
  host_tunnel_pb2_grpc.add_HostTunnelServicer_to_server(
207
- HostTunnelServicer(registry),
212
+ HostTunnelServicer(TUNNEL_REGISTRY),
208
213
  server
209
214
  )
210
215
 
@@ -366,6 +371,81 @@ def get_devices():
366
371
  for key, value in data.items():
367
372
  printf(f"Device ID:", Sty.DEFAULT, f' {key}', Sty.MAGENTA, f" Device Name: ", Sty.DEFAULT, f"{(value)}", Sty.GRAY)
368
373
 
374
+ def _fmt_ms_ago(ms: int) -> str:
375
+ if not ms:
376
+ return "-"
377
+ now = int(time.time() * 1000)
378
+ dt = max(0, now - int(ms))
379
+ if dt < 1000:
380
+ return f"{dt}ms"
381
+ if dt < 60_000:
382
+ return f"{dt/1000:.1f}s"
383
+ return f"{dt/60_000:.1f}m"
384
+
385
+ def _cmd_hosts_status() -> None:
386
+ global TUNNEL_REGISTRY
387
+ reg = TUNNEL_REGISTRY
388
+ if reg is None:
389
+ printf("Host tunnel registry not initialized yet.", Sty.RED)
390
+ return
391
+
392
+ hosts = reg.list_hosts() # host_id -> HostStatus
393
+ devices = reg.list_devices() # device_id -> DeviceStatus
394
+
395
+ # Build host -> device list
396
+ by_host: dict[str, list[str]] = {}
397
+ for did, ds in devices.items():
398
+ by_host.setdefault(ds.host_id or "-", []).append(did)
399
+
400
+ printf("Hosts (live):", Sty.BOLD)
401
+ if not hosts:
402
+ printf(" (none)", Sty.GRAY)
403
+ return
404
+
405
+ for hid, hs in sorted(hosts.items(), key=lambda kv: kv[0]):
406
+ dlist = sorted(by_host.get(hid, []))
407
+ printf(
408
+ " ", Sty.DEFAULT,
409
+ f"{hid}", Sty.MAGENTA,
410
+ " online=", Sty.DEFAULT, f"{hs.online}", Sty.GREEN if hs.online else Sty.RED,
411
+ " last_seen=", Sty.DEFAULT, f"{_fmt_ms_ago(hs.last_seen_ms)} ago", Sty.BLUE,
412
+ " devices=", Sty.DEFAULT, f"{len(dlist)}", Sty.GRAY
413
+ )
414
+ for did in dlist:
415
+ ds = devices.get(did)
416
+ if ds is None:
417
+ continue
418
+ printf(
419
+ " - ", Sty.DEFAULT,
420
+ f"{did}", Sty.MAGENTA,
421
+ " local_id=", Sty.DEFAULT, f"{ds.host_local_id}", Sty.GRAY,
422
+ " online=", Sty.DEFAULT, f"{ds.online}", Sty.GREEN if ds.online else Sty.RED,
423
+ " last_seen=", Sty.DEFAULT, f"{_fmt_ms_ago(ds.last_seen_ms)} ago", Sty.BLUE
424
+ )
425
+
426
+ def _cmd_devices_status() -> None:
427
+ global TUNNEL_REGISTRY
428
+ reg = TUNNEL_REGISTRY
429
+ if reg is None:
430
+ printf("Host tunnel registry not initialized yet.", Sty.RED)
431
+ return
432
+
433
+ devices = reg.list_devices()
434
+ printf("Devices (live):", Sty.BOLD)
435
+ if not devices:
436
+ printf(" (none)", Sty.GRAY)
437
+ return
438
+
439
+ for did, ds in sorted(devices.items(), key=lambda kv: kv[0]):
440
+ printf(
441
+ " ", Sty.DEFAULT,
442
+ f"{did}", Sty.MAGENTA,
443
+ " host=", Sty.DEFAULT, f"{ds.host_id or '-'}", Sty.GRAY,
444
+ " local_id=", Sty.DEFAULT, f"{ds.host_local_id}", Sty.GRAY,
445
+ " online=", Sty.DEFAULT, f"{ds.online}", Sty.GREEN if ds.online else Sty.RED,
446
+ " last_seen=", Sty.DEFAULT, f"{_fmt_ms_ago(ds.last_seen_ms)} ago", Sty.BLUE
447
+ )
448
+
369
449
  # def help():
370
450
  # printf("/------- HELP -------/", Sty.BOLD)
371
451
  # printf("'exit' ", Sty.MAGENTA, " - Stop server.", Sty.DEFAULT)
@@ -488,6 +568,11 @@ def help() -> None:
488
568
  printf(" codes delete ", Sty.MAGENTA, "- Delete an enrollment code (interactive)", Sty.DEFAULT)
489
569
  printf(" codes csv ", Sty.MAGENTA, "- Export ALL enrollment codes as CSV to Downloads", Sty.DEFAULT)
490
570
  print()
571
+
572
+ printf("Host Tunnel (live):", Sty.BOLD)
573
+ printf(" hosts status ", Sty.MAGENTA, "- Live host online/last_seen/devices", Sty.DEFAULT)
574
+ printf(" devices status ", Sty.MAGENTA, "- Live device online/last_seen/route", Sty.DEFAULT)
575
+ print()
491
576
 
492
577
  printf("Database:", Sty.BOLD)
493
578
  printf(" db purge ", Sty.MAGENTA, "- Remove all database entries", Sty.DEFAULT)
@@ -581,6 +666,14 @@ def server_input(inpu: str) -> bool:
581
666
  "rm code": lambda: _stay(lambda: remove_enrollment_code_local(session)), # old
582
667
  "rm c": lambda: _stay(lambda: remove_enrollment_code_local(session)), # old
583
668
 
669
+ # ---- host tunnel ----
670
+ "hosts status": lambda: _stay(_cmd_hosts_status),
671
+ "host status": lambda: _stay(_cmd_hosts_status),
672
+ "tunnel hosts": lambda: _stay(_cmd_hosts_status),
673
+
674
+ "devices status": lambda: _stay(_cmd_devices_status),
675
+ "tunnel devices": lambda: _stay(_cmd_devices_status),
676
+
584
677
  "codes csv": lambda: _stay(export_enrollment_codes_csv_local),
585
678
  }
586
679
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: remoteRF-server
3
- Version: 0.1.66
3
+ Version: 0.1.68
4
4
  Description-Content-Type: text/markdown
5
5
  Requires-Dist: grpcio
6
6
  Requires-Dist: protobuf