remoteRF-server-testing 0.0.8__tar.gz → 0.0.11__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 (49) hide show
  1. {remoterf_server_testing-0.0.8 → remoterf_server_testing-0.0.11}/PKG-INFO +1 -1
  2. {remoterf_server_testing-0.0.8 → remoterf_server_testing-0.0.11}/pyproject.toml +1 -1
  3. {remoterf_server_testing-0.0.8 → remoterf_server_testing-0.0.11}/src/remoteRF_server/server/device_manager.py +33 -15
  4. {remoterf_server_testing-0.0.8 → remoterf_server_testing-0.0.11}/src/remoteRF_server/server/rpc_manager.py +80 -37
  5. {remoterf_server_testing-0.0.8 → remoterf_server_testing-0.0.11}/src/remoteRF_server/serverrf_cli.py +7 -7
  6. {remoterf_server_testing-0.0.8 → remoterf_server_testing-0.0.11}/src/remoteRF_server_testing.egg-info/PKG-INFO +1 -1
  7. {remoterf_server_testing-0.0.8 → remoterf_server_testing-0.0.11}/README.md +0 -0
  8. {remoterf_server_testing-0.0.8 → remoterf_server_testing-0.0.11}/setup.cfg +0 -0
  9. {remoterf_server_testing-0.0.8 → remoterf_server_testing-0.0.11}/src/remoteRF_server/__init__.py +0 -0
  10. {remoterf_server_testing-0.0.8 → remoterf_server_testing-0.0.11}/src/remoteRF_server/common/__init__.py +0 -0
  11. {remoterf_server_testing-0.0.8 → remoterf_server_testing-0.0.11}/src/remoteRF_server/common/grpc/__init__.py +0 -0
  12. {remoterf_server_testing-0.0.8 → remoterf_server_testing-0.0.11}/src/remoteRF_server/common/grpc/grpc_host_pb2.py +0 -0
  13. {remoterf_server_testing-0.0.8 → remoterf_server_testing-0.0.11}/src/remoteRF_server/common/grpc/grpc_host_pb2_grpc.py +0 -0
  14. {remoterf_server_testing-0.0.8 → remoterf_server_testing-0.0.11}/src/remoteRF_server/common/grpc/grpc_pb2.py +0 -0
  15. {remoterf_server_testing-0.0.8 → remoterf_server_testing-0.0.11}/src/remoteRF_server/common/grpc/grpc_pb2_grpc.py +0 -0
  16. {remoterf_server_testing-0.0.8 → remoterf_server_testing-0.0.11}/src/remoteRF_server/common/idl/__init__.py +0 -0
  17. {remoterf_server_testing-0.0.8 → remoterf_server_testing-0.0.11}/src/remoteRF_server/common/idl/device_schema.py +0 -0
  18. {remoterf_server_testing-0.0.8 → remoterf_server_testing-0.0.11}/src/remoteRF_server/common/idl/pluto_schema.py +0 -0
  19. {remoterf_server_testing-0.0.8 → remoterf_server_testing-0.0.11}/src/remoteRF_server/common/idl/schema.py +0 -0
  20. {remoterf_server_testing-0.0.8 → remoterf_server_testing-0.0.11}/src/remoteRF_server/common/utils/__init__.py +0 -0
  21. {remoterf_server_testing-0.0.8 → remoterf_server_testing-0.0.11}/src/remoteRF_server/common/utils/ansi_codes.py +0 -0
  22. {remoterf_server_testing-0.0.8 → remoterf_server_testing-0.0.11}/src/remoteRF_server/common/utils/api_token.py +0 -0
  23. {remoterf_server_testing-0.0.8 → remoterf_server_testing-0.0.11}/src/remoteRF_server/common/utils/db_connection.py +0 -0
  24. {remoterf_server_testing-0.0.8 → remoterf_server_testing-0.0.11}/src/remoteRF_server/common/utils/db_location.py +0 -0
  25. {remoterf_server_testing-0.0.8 → remoterf_server_testing-0.0.11}/src/remoteRF_server/common/utils/list_string.py +0 -0
  26. {remoterf_server_testing-0.0.8 → remoterf_server_testing-0.0.11}/src/remoteRF_server/common/utils/process_arg.py +0 -0
  27. {remoterf_server_testing-0.0.8 → remoterf_server_testing-0.0.11}/src/remoteRF_server/drivers/__init__.py +0 -0
  28. {remoterf_server_testing-0.0.8 → remoterf_server_testing-0.0.11}/src/remoteRF_server/drivers/adalm_pluto/__init__.py +0 -0
  29. {remoterf_server_testing-0.0.8 → remoterf_server_testing-0.0.11}/src/remoteRF_server/drivers/adalm_pluto/pluto_remote_server.py +0 -0
  30. {remoterf_server_testing-0.0.8 → remoterf_server_testing-0.0.11}/src/remoteRF_server/host/__init__.py +0 -0
  31. {remoterf_server_testing-0.0.8 → remoterf_server_testing-0.0.11}/src/remoteRF_server/host/host_auth_token.py +0 -0
  32. {remoterf_server_testing-0.0.8 → remoterf_server_testing-0.0.11}/src/remoteRF_server/host/host_directory_store.py +0 -0
  33. {remoterf_server_testing-0.0.8 → remoterf_server_testing-0.0.11}/src/remoteRF_server/host/host_tunnel_server.py +0 -0
  34. {remoterf_server_testing-0.0.8 → remoterf_server_testing-0.0.11}/src/remoteRF_server/server/__init__.py +0 -0
  35. {remoterf_server_testing-0.0.8 → remoterf_server_testing-0.0.11}/src/remoteRF_server/server/acc_perms.py +0 -0
  36. {remoterf_server_testing-0.0.8 → remoterf_server_testing-0.0.11}/src/remoteRF_server/server/cert_provider.py +0 -0
  37. {remoterf_server_testing-0.0.8 → remoterf_server_testing-0.0.11}/src/remoteRF_server/server/grpc_server.py +0 -0
  38. {remoterf_server_testing-0.0.8 → remoterf_server_testing-0.0.11}/src/remoteRF_server/server/reservation.py +0 -0
  39. {remoterf_server_testing-0.0.8 → remoterf_server_testing-0.0.11}/src/remoteRF_server/server/user_group_cli.py +0 -0
  40. {remoterf_server_testing-0.0.8 → remoterf_server_testing-0.0.11}/src/remoteRF_server/server/user_group_handler.py +0 -0
  41. {remoterf_server_testing-0.0.8 → remoterf_server_testing-0.0.11}/src/remoteRF_server/tools/__init__.py +0 -0
  42. {remoterf_server_testing-0.0.8 → remoterf_server_testing-0.0.11}/src/remoteRF_server/tools/gen_certs.py +0 -0
  43. {remoterf_server_testing-0.0.8 → remoterf_server_testing-0.0.11}/src/remoteRF_server/tools/gist_status.py +0 -0
  44. {remoterf_server_testing-0.0.8 → remoterf_server_testing-0.0.11}/src/remoteRF_server/tools/gist_status_testing.py +0 -0
  45. {remoterf_server_testing-0.0.8 → remoterf_server_testing-0.0.11}/src/remoteRF_server_testing.egg-info/SOURCES.txt +0 -0
  46. {remoterf_server_testing-0.0.8 → remoterf_server_testing-0.0.11}/src/remoteRF_server_testing.egg-info/dependency_links.txt +0 -0
  47. {remoterf_server_testing-0.0.8 → remoterf_server_testing-0.0.11}/src/remoteRF_server_testing.egg-info/entry_points.txt +0 -0
  48. {remoterf_server_testing-0.0.8 → remoterf_server_testing-0.0.11}/src/remoteRF_server_testing.egg-info/requires.txt +0 -0
  49. {remoterf_server_testing-0.0.8 → remoterf_server_testing-0.0.11}/src/remoteRF_server_testing.egg-info/top_level.txt +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: remoteRF-server-testing
3
- Version: 0.0.8
3
+ Version: 0.0.11
4
4
  Summary: RemoteRF server-side control package
5
5
  Requires-Python: >=3.8
6
6
  Description-Content-Type: text/markdown
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "remoteRF-server-testing"
7
- version = "0.0.8"
7
+ version = "0.0.11"
8
8
  description = "RemoteRF server-side control package"
9
9
  requires-python = ">=3.8"
10
10
  dependencies = [
@@ -229,6 +229,21 @@ def _host_devices_str_snapshot() -> Dict[int, str]:
229
229
  def _cfg_dir() -> Path:
230
230
  return Path(os.getenv("REMOTERF_CONFIG_DIR", get_remoterf_root()))
231
231
 
232
+ def _super_token_path() -> Path:
233
+ path = _cfg_dir() / "super_token.txt"
234
+ path.parent.mkdir(parents=True, exist_ok=True)
235
+ path.touch(exist_ok=True)
236
+ return path
237
+
238
+ def _get_master_token() -> str:
239
+ """Read the configured super token; blank file means the override is disabled."""
240
+ path = _super_token_path()
241
+ try:
242
+ return path.read_text(encoding="utf-8").strip()
243
+ except OSError as e:
244
+ print(f"Warning: could not read super token file {path}: {e}")
245
+ return ""
246
+
232
247
  def _devices_yaml_path() -> Path:
233
248
  p1 = _cfg_dir() / "devices.yml"
234
249
  if p1.exists():
@@ -316,9 +331,6 @@ def _connect_from_record(rec: Dict[str, Any]):
316
331
  devices_info: Dict[int, str] = {}
317
332
  device_serialization: Dict[int, str] = {}
318
333
 
319
- # master token (overrideable)
320
- master_token = os.getenv("REMOTERF_MASTER_TOKEN", "SuperCoolTokenForIan")
321
-
322
334
  def _init_from_env() -> None:
323
335
  records = _load_device_records()
324
336
 
@@ -378,13 +390,15 @@ def _load_all_drivers() -> None:
378
390
 
379
391
  _load_all_drivers()
380
392
  _init_from_env()
393
+ _super_token_path()
381
394
 
382
395
  # Legacy reservation helpers (master token parsing)
383
396
 
384
- def parse_mastertoken(token: str):
385
- if not token:
397
+ def parse_mastertoken(token: str, configured_token: Optional[str] = None):
398
+ configured_token = _get_master_token() if configured_token is None else configured_token
399
+ if not token or not configured_token:
386
400
  return None
387
- prefix = re.escape(master_token)
401
+ prefix = re.escape(configured_token)
388
402
  pattern = re.compile(rf"^{prefix}[_-](\d+)(?:_force)?$")
389
403
  m = pattern.match(token)
390
404
  if not m:
@@ -528,15 +542,16 @@ def get_schema_by_token(api_token: str):
528
542
 
529
543
  _sync_host_devices()
530
544
  gid: Optional[int] = None
545
+ configured_token = _get_master_token()
531
546
 
532
- if api_token == master_token:
547
+ if configured_token and api_token == configured_token:
533
548
  with _state_lock:
534
549
  for k, st in _devices.items():
535
550
  if st.origin == "local" and _is_connected(st) and st.salt == "" and st.hsh == "":
536
551
  gid = k
537
552
  break
538
553
  else:
539
- parsed = parse_mastertoken(api_token)
554
+ parsed = parse_mastertoken(api_token, configured_token=configured_token)
540
555
  if parsed:
541
556
  cand, _ = parsed
542
557
  with _state_lock:
@@ -577,9 +592,10 @@ def resolve_gid_for_token(api_token: str) -> Optional[int]:
577
592
  return None
578
593
 
579
594
  _sync_host_devices()
595
+ configured_token = _get_master_token()
580
596
 
581
597
  # Parsed master-token: resolves any device directly by gid (local or host).
582
- parsed = parse_mastertoken(api_token)
598
+ parsed = parse_mastertoken(api_token, configured_token=configured_token)
583
599
  if parsed:
584
600
  cand, _ = parsed
585
601
  with _state_lock:
@@ -589,7 +605,7 @@ def resolve_gid_for_token(api_token: str) -> Optional[int]:
589
605
  return None
590
606
 
591
607
  # Raw master token — prefer local, fall back to any connected device.
592
- if api_token == master_token:
608
+ if configured_token and api_token == configured_token:
593
609
  with _state_lock:
594
610
  # local first
595
611
  for k, st in _devices.items():
@@ -630,6 +646,7 @@ def get_device(*, api_token: str):
630
646
  return None
631
647
 
632
648
  _sync_host_devices()
649
+ configured_token = _get_master_token()
633
650
 
634
651
  with _state_lock:
635
652
  snapshot: List[Tuple[int, object, str, str, str]] = [
@@ -639,7 +656,7 @@ def get_device(*, api_token: str):
639
656
  ]
640
657
 
641
658
  # Preserve "master prefers local" behavior
642
- if api_token == master_token:
659
+ if configured_token and api_token == configured_token:
643
660
  # local first
644
661
  for _, dev, salt, hsh, origin in snapshot:
645
662
  if origin == "local" and dev is not None and salt == "" and hsh == "":
@@ -650,7 +667,7 @@ def get_device(*, api_token: str):
650
667
  return dev
651
668
  return None
652
669
 
653
- parsed = parse_mastertoken(api_token)
670
+ parsed = parse_mastertoken(api_token, configured_token=configured_token)
654
671
  if parsed:
655
672
  device_id, force = parsed
656
673
  with _state_lock:
@@ -679,8 +696,9 @@ def acquire_device(api_token: str) -> Iterator[Tuple[int, object]]:
679
696
  _sync_host_devices()
680
697
 
681
698
  gid: Optional[int] = None
699
+ configured_token = _get_master_token()
682
700
 
683
- if api_token == master_token:
701
+ if configured_token and api_token == configured_token:
684
702
  with _state_lock:
685
703
  # prefer local first
686
704
  for k, st in _devices.items():
@@ -693,7 +711,7 @@ def acquire_device(api_token: str) -> Iterator[Tuple[int, object]]:
693
711
  gid = k
694
712
  break
695
713
  else:
696
- parsed = parse_mastertoken(api_token)
714
+ parsed = parse_mastertoken(api_token, configured_token=configured_token)
697
715
  if parsed:
698
716
  cand, force = parsed
699
717
  with _state_lock:
@@ -790,4 +808,4 @@ def reload_devices() -> None:
790
808
 
791
809
  def set_pluto(ip: str = "192.168.2.1"):
792
810
  # kept for compatibility (no-op)
793
- pass
811
+ pass
@@ -52,7 +52,8 @@
52
52
 
53
53
  from .reservation import reservation_handler
54
54
  from ..common.utils import *
55
- from .device_manager import acquire_device, VirtualDevice, get_device_schema, find_device_id_by_name, get_schema_by_token, resolve_gid_for_token
55
+ from ..common.grpc import grpc_pb2 as generic_pb2
56
+ from .device_manager import acquire_device, VirtualDevice, get_device_schema, find_device_id_by_name, get_device_by_id, resolve_gid_for_token
56
57
  from ..host import host_tunnel_server as hts
57
58
 
58
59
 
@@ -181,6 +182,71 @@ class RpcManager:
181
182
 
182
183
  raise ValueError(f"Unknown verb {verb!r} in {function_name!r}")
183
184
 
185
+ def _resolve_idl_target(self, args: dict) -> tuple[int | None, str | None]:
186
+ if 'token' in args:
187
+ api_token = str(unmap_arg(args['token']))
188
+ gid = resolve_gid_for_token(api_token)
189
+ if gid is None:
190
+ return None, 'No schema available for this token (invalid token or device offline)'
191
+ return gid, None
192
+
193
+ if 'device_id' in args:
194
+ try:
195
+ return int(unmap_arg(args['device_id'])), None
196
+ except (ValueError, TypeError):
197
+ return None, 'device_id must be an integer'
198
+
199
+ if 'device_name' in args:
200
+ name = str(unmap_arg(args['device_name']))
201
+ gid = find_device_id_by_name(name)
202
+ if gid is None:
203
+ return None, f'No device found with name {name!r}'
204
+ return gid, None
205
+
206
+ return None, 'IDL:get_drivers requires token, device_id, or device_name'
207
+
208
+ def _fetch_host_idl(self, *, gid: int, dev: VirtualDevice, args: dict) -> dict:
209
+ registry = hts.get_tunnel_registry(create=False)
210
+ if registry is None:
211
+ return {'error': map_arg('Host tunnel registry unavailable')}
212
+
213
+ req = generic_pb2.GenericRPCRequest(function_name="IDL:get_drivers")
214
+ for key, value in dict(args).items():
215
+ if str(key) == "token":
216
+ continue
217
+ req.args[str(key)].CopyFrom(value)
218
+ req.args["device_id"].CopyFrom(map_arg(int(gid)))
219
+ req.args["g"].CopyFrom(map_arg(int(gid)))
220
+
221
+ try:
222
+ resp = hts.handle_host_device_request(
223
+ registry,
224
+ host_id=dev.host_id,
225
+ device_id=dev.device_id,
226
+ request=req,
227
+ timeout_sec=10.0,
228
+ )
229
+ except Exception as e:
230
+ return {'error': map_arg(str(e))}
231
+
232
+ results = dict(resp.results)
233
+ if 'error' in results:
234
+ return results
235
+
236
+ err = results.get('Error')
237
+ if err is not None:
238
+ return {'error': map_arg(str(unmap_arg(err)))}
239
+
240
+ ok = results.get('Ok')
241
+ if ok is not None:
242
+ try:
243
+ if not bool(unmap_arg(ok)):
244
+ return {'error': map_arg('Host schema fetch failed')}
245
+ except Exception:
246
+ return {'error': map_arg('Host schema fetch failed')}
247
+
248
+ return results
249
+
184
250
  def _handle_idl(self, sub: str, args: dict) -> dict:
185
251
  """
186
252
  IDL:get_drivers → schema + driver metadata for a device.
@@ -196,42 +262,19 @@ class RpcManager:
196
262
  device_type str e.g. "pluto"
197
263
  """
198
264
  if sub == "get_drivers":
199
- # Resolve device by token (preferred), device_id, or device_name.
200
- schema = None
201
-
202
- if 'token' in args:
203
- api_token = str(unmap_arg(args['token']))
204
- schema = get_schema_by_token(api_token)
205
- if schema is None:
206
- # get_schema_by_token returns None for host (VirtualDevice) entries
207
- # because they carry no salt/hsh. Fall through via gid resolution
208
- # which uses get_device_schema and handles host devices correctly.
209
- gid = resolve_gid_for_token(api_token)
210
- if gid is not None:
211
- schema = get_device_schema(gid)
212
- if schema is None:
213
- return {'error': map_arg('No schema available for this token (invalid token or device offline)')}
214
-
215
- elif 'device_id' in args:
216
- try:
217
- gid = int(unmap_arg(args['device_id']))
218
- except (ValueError, TypeError):
219
- return {'error': map_arg('device_id must be an integer')}
220
- schema = get_device_schema(gid)
221
- if schema is None:
222
- return {'error': map_arg(f'No schema available for device {gid} (offline or host device)')}
223
-
224
- elif 'device_name' in args:
225
- name = str(unmap_arg(args['device_name']))
226
- gid = find_device_id_by_name(name)
227
- if gid is None:
228
- return {'error': map_arg(f'No device found with name {name!r}')}
229
- schema = get_device_schema(gid)
230
- if schema is None:
231
- return {'error': map_arg(f'No schema available for device {name!r} (offline or host device)')}
232
-
233
- else:
234
- return {'error': map_arg('IDL:get_drivers requires token, device_id, or device_name')}
265
+ gid, err = self._resolve_idl_target(args)
266
+ if err is not None or gid is None:
267
+ return {'error': map_arg(err or 'Unable to resolve device')}
268
+
269
+ resolved = get_device_by_id(gid)
270
+ dev = resolved[0] if resolved is not None else None
271
+
272
+ if isinstance(dev, VirtualDevice):
273
+ return self._fetch_host_idl(gid=gid, dev=dev, args=args)
274
+
275
+ schema = get_device_schema(gid)
276
+ if schema is None:
277
+ return {'error': map_arg(f'No schema available for device {gid} (offline)')}
235
278
 
236
279
  return {
237
280
  'schema': map_arg(schema.get_idl_json()),
@@ -186,13 +186,13 @@ def print_help() -> None:
186
186
  " serverrf --host --show\n"
187
187
  " serverrf --host --delete lab-host-01\n"
188
188
  " serverrf --host --wipe -y\n"
189
- "\n"
190
- " serverrf --device --add --pluto 0:pluto_aaa:123123\n"
191
- " serverrf --device --edit-name 0 \"New Pluto Name\"\n"
192
- " serverrf --device --remove 0\n"
193
- " serverrf --device --show\n"
194
- " serverrf --device --wipe -y\n"
195
- "\n"
189
+ # "\n"
190
+ # " serverrf --device --add --pluto 0:pluto_aaa:123123\n"
191
+ # " serverrf --device --edit-name 0 \"New Pluto Name\"\n"
192
+ # " serverrf --device --remove 0\n"
193
+ # " serverrf --device --show\n"
194
+ # " serverrf --device --wipe -y\n"
195
+ # "\n"
196
196
  " serverrf -s\n"
197
197
  " serverrf --serve\n"
198
198
  "\n"
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: remoteRF-server-testing
3
- Version: 0.0.8
3
+ Version: 0.0.11
4
4
  Summary: RemoteRF server-side control package
5
5
  Requires-Python: >=3.8
6
6
  Description-Content-Type: text/markdown