remoteRF-server-testing 0.0.10__tar.gz → 0.0.12__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_testing-0.0.10 → remoterf_server_testing-0.0.12}/PKG-INFO +1 -1
- {remoterf_server_testing-0.0.10 → remoterf_server_testing-0.0.12}/pyproject.toml +1 -1
- {remoterf_server_testing-0.0.10 → remoterf_server_testing-0.0.12}/src/remoteRF_server/server/device_manager.py +33 -15
- {remoterf_server_testing-0.0.10 → remoterf_server_testing-0.0.12}/src/remoteRF_server/serverrf_cli.py +77 -15
- {remoterf_server_testing-0.0.10 → remoterf_server_testing-0.0.12}/src/remoteRF_server_testing.egg-info/PKG-INFO +1 -1
- {remoterf_server_testing-0.0.10 → remoterf_server_testing-0.0.12}/README.md +0 -0
- {remoterf_server_testing-0.0.10 → remoterf_server_testing-0.0.12}/setup.cfg +0 -0
- {remoterf_server_testing-0.0.10 → remoterf_server_testing-0.0.12}/src/remoteRF_server/__init__.py +0 -0
- {remoterf_server_testing-0.0.10 → remoterf_server_testing-0.0.12}/src/remoteRF_server/common/__init__.py +0 -0
- {remoterf_server_testing-0.0.10 → remoterf_server_testing-0.0.12}/src/remoteRF_server/common/grpc/__init__.py +0 -0
- {remoterf_server_testing-0.0.10 → remoterf_server_testing-0.0.12}/src/remoteRF_server/common/grpc/grpc_host_pb2.py +0 -0
- {remoterf_server_testing-0.0.10 → remoterf_server_testing-0.0.12}/src/remoteRF_server/common/grpc/grpc_host_pb2_grpc.py +0 -0
- {remoterf_server_testing-0.0.10 → remoterf_server_testing-0.0.12}/src/remoteRF_server/common/grpc/grpc_pb2.py +0 -0
- {remoterf_server_testing-0.0.10 → remoterf_server_testing-0.0.12}/src/remoteRF_server/common/grpc/grpc_pb2_grpc.py +0 -0
- {remoterf_server_testing-0.0.10 → remoterf_server_testing-0.0.12}/src/remoteRF_server/common/idl/__init__.py +0 -0
- {remoterf_server_testing-0.0.10 → remoterf_server_testing-0.0.12}/src/remoteRF_server/common/idl/device_schema.py +0 -0
- {remoterf_server_testing-0.0.10 → remoterf_server_testing-0.0.12}/src/remoteRF_server/common/idl/pluto_schema.py +0 -0
- {remoterf_server_testing-0.0.10 → remoterf_server_testing-0.0.12}/src/remoteRF_server/common/idl/schema.py +0 -0
- {remoterf_server_testing-0.0.10 → remoterf_server_testing-0.0.12}/src/remoteRF_server/common/utils/__init__.py +0 -0
- {remoterf_server_testing-0.0.10 → remoterf_server_testing-0.0.12}/src/remoteRF_server/common/utils/ansi_codes.py +0 -0
- {remoterf_server_testing-0.0.10 → remoterf_server_testing-0.0.12}/src/remoteRF_server/common/utils/api_token.py +0 -0
- {remoterf_server_testing-0.0.10 → remoterf_server_testing-0.0.12}/src/remoteRF_server/common/utils/db_connection.py +0 -0
- {remoterf_server_testing-0.0.10 → remoterf_server_testing-0.0.12}/src/remoteRF_server/common/utils/db_location.py +0 -0
- {remoterf_server_testing-0.0.10 → remoterf_server_testing-0.0.12}/src/remoteRF_server/common/utils/list_string.py +0 -0
- {remoterf_server_testing-0.0.10 → remoterf_server_testing-0.0.12}/src/remoteRF_server/common/utils/process_arg.py +0 -0
- {remoterf_server_testing-0.0.10 → remoterf_server_testing-0.0.12}/src/remoteRF_server/drivers/__init__.py +0 -0
- {remoterf_server_testing-0.0.10 → remoterf_server_testing-0.0.12}/src/remoteRF_server/drivers/adalm_pluto/__init__.py +0 -0
- {remoterf_server_testing-0.0.10 → remoterf_server_testing-0.0.12}/src/remoteRF_server/drivers/adalm_pluto/pluto_remote_server.py +0 -0
- {remoterf_server_testing-0.0.10 → remoterf_server_testing-0.0.12}/src/remoteRF_server/host/__init__.py +0 -0
- {remoterf_server_testing-0.0.10 → remoterf_server_testing-0.0.12}/src/remoteRF_server/host/host_auth_token.py +0 -0
- {remoterf_server_testing-0.0.10 → remoterf_server_testing-0.0.12}/src/remoteRF_server/host/host_directory_store.py +0 -0
- {remoterf_server_testing-0.0.10 → remoterf_server_testing-0.0.12}/src/remoteRF_server/host/host_tunnel_server.py +0 -0
- {remoterf_server_testing-0.0.10 → remoterf_server_testing-0.0.12}/src/remoteRF_server/server/__init__.py +0 -0
- {remoterf_server_testing-0.0.10 → remoterf_server_testing-0.0.12}/src/remoteRF_server/server/acc_perms.py +0 -0
- {remoterf_server_testing-0.0.10 → remoterf_server_testing-0.0.12}/src/remoteRF_server/server/cert_provider.py +0 -0
- {remoterf_server_testing-0.0.10 → remoterf_server_testing-0.0.12}/src/remoteRF_server/server/grpc_server.py +0 -0
- {remoterf_server_testing-0.0.10 → remoterf_server_testing-0.0.12}/src/remoteRF_server/server/reservation.py +0 -0
- {remoterf_server_testing-0.0.10 → remoterf_server_testing-0.0.12}/src/remoteRF_server/server/rpc_manager.py +0 -0
- {remoterf_server_testing-0.0.10 → remoterf_server_testing-0.0.12}/src/remoteRF_server/server/user_group_cli.py +0 -0
- {remoterf_server_testing-0.0.10 → remoterf_server_testing-0.0.12}/src/remoteRF_server/server/user_group_handler.py +0 -0
- {remoterf_server_testing-0.0.10 → remoterf_server_testing-0.0.12}/src/remoteRF_server/tools/__init__.py +0 -0
- {remoterf_server_testing-0.0.10 → remoterf_server_testing-0.0.12}/src/remoteRF_server/tools/gen_certs.py +0 -0
- {remoterf_server_testing-0.0.10 → remoterf_server_testing-0.0.12}/src/remoteRF_server/tools/gist_status.py +0 -0
- {remoterf_server_testing-0.0.10 → remoterf_server_testing-0.0.12}/src/remoteRF_server/tools/gist_status_testing.py +0 -0
- {remoterf_server_testing-0.0.10 → remoterf_server_testing-0.0.12}/src/remoteRF_server_testing.egg-info/SOURCES.txt +0 -0
- {remoterf_server_testing-0.0.10 → remoterf_server_testing-0.0.12}/src/remoteRF_server_testing.egg-info/dependency_links.txt +0 -0
- {remoterf_server_testing-0.0.10 → remoterf_server_testing-0.0.12}/src/remoteRF_server_testing.egg-info/entry_points.txt +0 -0
- {remoterf_server_testing-0.0.10 → remoterf_server_testing-0.0.12}/src/remoteRF_server_testing.egg-info/requires.txt +0 -0
- {remoterf_server_testing-0.0.10 → remoterf_server_testing-0.0.12}/src/remoteRF_server_testing.egg-info/top_level.txt +0 -0
|
@@ -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
|
|
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(
|
|
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 ==
|
|
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 ==
|
|
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 ==
|
|
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 ==
|
|
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
|
|
@@ -5,6 +5,8 @@ import os
|
|
|
5
5
|
import subprocess
|
|
6
6
|
import shutil
|
|
7
7
|
import re
|
|
8
|
+
import ssl
|
|
9
|
+
import ipaddress
|
|
8
10
|
from pathlib import Path
|
|
9
11
|
from typing import Optional, Dict, List, Tuple
|
|
10
12
|
from datetime import datetime, timezone
|
|
@@ -53,6 +55,59 @@ def _fmt_remaining(na_utc: datetime) -> str:
|
|
|
53
55
|
return f"{days}d {hours}h {mins}m"
|
|
54
56
|
|
|
55
57
|
|
|
58
|
+
_STATIC_IP_ENV_KEYS: Tuple[str, ...] = ("STATIC_IP", "SERVER_IP")
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
def _is_ip_address(value: str) -> bool:
|
|
62
|
+
try:
|
|
63
|
+
ipaddress.ip_address((value or "").strip())
|
|
64
|
+
return True
|
|
65
|
+
except ValueError:
|
|
66
|
+
return False
|
|
67
|
+
|
|
68
|
+
|
|
69
|
+
def _x509_static_ip(cert_path: Path) -> str:
|
|
70
|
+
if not cert_path.exists():
|
|
71
|
+
raise FileNotFoundError(f"server cert not found: {cert_path}")
|
|
72
|
+
|
|
73
|
+
try:
|
|
74
|
+
cert = ssl._ssl._test_decode_cert(str(cert_path))
|
|
75
|
+
except FileNotFoundError:
|
|
76
|
+
raise
|
|
77
|
+
except Exception as e:
|
|
78
|
+
raise ValueError(f"could not parse server cert {cert_path}: {e}") from e
|
|
79
|
+
|
|
80
|
+
for name, value in cert.get("subjectAltName", []):
|
|
81
|
+
if name.lower() == "ip address":
|
|
82
|
+
ip = (value or "").strip()
|
|
83
|
+
if ip:
|
|
84
|
+
return ip
|
|
85
|
+
|
|
86
|
+
for group in cert.get("subject", []):
|
|
87
|
+
for name, value in group:
|
|
88
|
+
if name.lower() != "commonname":
|
|
89
|
+
continue
|
|
90
|
+
cn = (value or "").strip()
|
|
91
|
+
if _is_ip_address(cn):
|
|
92
|
+
return cn
|
|
93
|
+
|
|
94
|
+
raise ValueError(f"server cert does not contain an IP SAN or IP common name: {cert_path}")
|
|
95
|
+
|
|
96
|
+
|
|
97
|
+
def _resolve_static_ip(kv: Dict[str, str]) -> Tuple[Optional[str], str, Optional[str]]:
|
|
98
|
+
env_path = _server_env_path()
|
|
99
|
+
for key in _STATIC_IP_ENV_KEYS:
|
|
100
|
+
value = (kv.get(key, "") or "").strip()
|
|
101
|
+
if value:
|
|
102
|
+
return value, f"{env_path} ({key})", None
|
|
103
|
+
|
|
104
|
+
cert_path = _certs_dir() / "server.crt"
|
|
105
|
+
try:
|
|
106
|
+
return _x509_static_ip(cert_path), str(cert_path), None
|
|
107
|
+
except (FileNotFoundError, ValueError) as e:
|
|
108
|
+
return None, str(cert_path), str(e)
|
|
109
|
+
|
|
110
|
+
|
|
56
111
|
# -----------------------------
|
|
57
112
|
# Repo-local server config locations
|
|
58
113
|
# -----------------------------
|
|
@@ -126,7 +181,7 @@ def print_help() -> None:
|
|
|
126
181
|
"Port options:\n"
|
|
127
182
|
" --main-port <int> Set GRPC_PORT\n"
|
|
128
183
|
" --cert-port <int> Set CERT_PORT\n"
|
|
129
|
-
" --show | -s Show current ports\n"
|
|
184
|
+
" --show | -s Show current ports and static IP\n"
|
|
130
185
|
" -w | --wipe [-y] Wipe ONLY port config (server.env)\n"
|
|
131
186
|
"\n"
|
|
132
187
|
"Hosts:\n"
|
|
@@ -186,13 +241,13 @@ def print_help() -> None:
|
|
|
186
241
|
" serverrf --host --show\n"
|
|
187
242
|
" serverrf --host --delete lab-host-01\n"
|
|
188
243
|
" 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"
|
|
244
|
+
# "\n"
|
|
245
|
+
# " serverrf --device --add --pluto 0:pluto_aaa:123123\n"
|
|
246
|
+
# " serverrf --device --edit-name 0 \"New Pluto Name\"\n"
|
|
247
|
+
# " serverrf --device --remove 0\n"
|
|
248
|
+
# " serverrf --device --show\n"
|
|
249
|
+
# " serverrf --device --wipe -y\n"
|
|
250
|
+
# "\n"
|
|
196
251
|
" serverrf -s\n"
|
|
197
252
|
" serverrf --serve\n"
|
|
198
253
|
"\n"
|
|
@@ -525,20 +580,27 @@ def _gen_certs(
|
|
|
525
580
|
|
|
526
581
|
def _config_show_ports() -> int:
|
|
527
582
|
p = _server_env_path()
|
|
528
|
-
if not p.exists():
|
|
529
|
-
print(f"No port config found (missing {p}).")
|
|
530
|
-
return 0
|
|
531
|
-
|
|
532
583
|
kv = _read_env_kv(p)
|
|
533
|
-
if not kv:
|
|
534
|
-
print("Port config is empty.")
|
|
535
|
-
return 0
|
|
536
584
|
|
|
537
585
|
print(f"Port config: {p}")
|
|
586
|
+
if not p.exists():
|
|
587
|
+
print(" (missing)")
|
|
588
|
+
elif not kv:
|
|
589
|
+
print(" (empty)")
|
|
590
|
+
|
|
538
591
|
if "GRPC_PORT" in kv:
|
|
539
592
|
print(f" GRPC_PORT={kv['GRPC_PORT']}")
|
|
540
593
|
if "CERT_PORT" in kv:
|
|
541
594
|
print(f" CERT_PORT={kv['CERT_PORT']}")
|
|
595
|
+
|
|
596
|
+
static_ip, source, err = _resolve_static_ip(kv)
|
|
597
|
+
if static_ip:
|
|
598
|
+
print(f" STATIC_IP={static_ip}")
|
|
599
|
+
if source != f"{p} (STATIC_IP)":
|
|
600
|
+
print(f" STATIC_IP_SOURCE={source}")
|
|
601
|
+
else:
|
|
602
|
+
print(" STATIC_IP=(not set)")
|
|
603
|
+
print(f" STATIC_IP_ERROR={err or f'could not resolve from {source}'}")
|
|
542
604
|
return 0
|
|
543
605
|
|
|
544
606
|
|
|
File without changes
|
|
File without changes
|
{remoterf_server_testing-0.0.10 → remoterf_server_testing-0.0.12}/src/remoteRF_server/__init__.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|