remoteRF-server-testing 0.0.11__tar.gz → 0.0.13__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.11 → remoterf_server_testing-0.0.13}/PKG-INFO +1 -1
- {remoterf_server_testing-0.0.11 → remoterf_server_testing-0.0.13}/pyproject.toml +1 -1
- remoterf_server_testing-0.0.13/src/remoteRF_server/drivers/adalm_pluto/pluto_schema.py +228 -0
- {remoterf_server_testing-0.0.11 → remoterf_server_testing-0.0.13}/src/remoteRF_server/server/device_manager.py +54 -3
- {remoterf_server_testing-0.0.11 → remoterf_server_testing-0.0.13}/src/remoteRF_server/serverrf_cli.py +83 -17
- {remoterf_server_testing-0.0.11 → remoterf_server_testing-0.0.13}/src/remoteRF_server/tools/gist_status.py +1 -1
- {remoterf_server_testing-0.0.11 → remoterf_server_testing-0.0.13}/src/remoteRF_server_testing.egg-info/PKG-INFO +1 -1
- {remoterf_server_testing-0.0.11 → remoterf_server_testing-0.0.13}/src/remoteRF_server_testing.egg-info/SOURCES.txt +1 -0
- {remoterf_server_testing-0.0.11 → remoterf_server_testing-0.0.13}/README.md +0 -0
- {remoterf_server_testing-0.0.11 → remoterf_server_testing-0.0.13}/setup.cfg +0 -0
- {remoterf_server_testing-0.0.11 → remoterf_server_testing-0.0.13}/src/remoteRF_server/__init__.py +0 -0
- {remoterf_server_testing-0.0.11 → remoterf_server_testing-0.0.13}/src/remoteRF_server/common/__init__.py +0 -0
- {remoterf_server_testing-0.0.11 → remoterf_server_testing-0.0.13}/src/remoteRF_server/common/grpc/__init__.py +0 -0
- {remoterf_server_testing-0.0.11 → remoterf_server_testing-0.0.13}/src/remoteRF_server/common/grpc/grpc_host_pb2.py +0 -0
- {remoterf_server_testing-0.0.11 → remoterf_server_testing-0.0.13}/src/remoteRF_server/common/grpc/grpc_host_pb2_grpc.py +0 -0
- {remoterf_server_testing-0.0.11 → remoterf_server_testing-0.0.13}/src/remoteRF_server/common/grpc/grpc_pb2.py +0 -0
- {remoterf_server_testing-0.0.11 → remoterf_server_testing-0.0.13}/src/remoteRF_server/common/grpc/grpc_pb2_grpc.py +0 -0
- {remoterf_server_testing-0.0.11 → remoterf_server_testing-0.0.13}/src/remoteRF_server/common/idl/__init__.py +0 -0
- {remoterf_server_testing-0.0.11 → remoterf_server_testing-0.0.13}/src/remoteRF_server/common/idl/device_schema.py +0 -0
- {remoterf_server_testing-0.0.11 → remoterf_server_testing-0.0.13}/src/remoteRF_server/common/idl/pluto_schema.py +0 -0
- {remoterf_server_testing-0.0.11 → remoterf_server_testing-0.0.13}/src/remoteRF_server/common/idl/schema.py +0 -0
- {remoterf_server_testing-0.0.11 → remoterf_server_testing-0.0.13}/src/remoteRF_server/common/utils/__init__.py +0 -0
- {remoterf_server_testing-0.0.11 → remoterf_server_testing-0.0.13}/src/remoteRF_server/common/utils/ansi_codes.py +0 -0
- {remoterf_server_testing-0.0.11 → remoterf_server_testing-0.0.13}/src/remoteRF_server/common/utils/api_token.py +0 -0
- {remoterf_server_testing-0.0.11 → remoterf_server_testing-0.0.13}/src/remoteRF_server/common/utils/db_connection.py +0 -0
- {remoterf_server_testing-0.0.11 → remoterf_server_testing-0.0.13}/src/remoteRF_server/common/utils/db_location.py +0 -0
- {remoterf_server_testing-0.0.11 → remoterf_server_testing-0.0.13}/src/remoteRF_server/common/utils/list_string.py +0 -0
- {remoterf_server_testing-0.0.11 → remoterf_server_testing-0.0.13}/src/remoteRF_server/common/utils/process_arg.py +0 -0
- {remoterf_server_testing-0.0.11 → remoterf_server_testing-0.0.13}/src/remoteRF_server/drivers/__init__.py +0 -0
- {remoterf_server_testing-0.0.11 → remoterf_server_testing-0.0.13}/src/remoteRF_server/drivers/adalm_pluto/__init__.py +0 -0
- {remoterf_server_testing-0.0.11 → remoterf_server_testing-0.0.13}/src/remoteRF_server/drivers/adalm_pluto/pluto_remote_server.py +0 -0
- {remoterf_server_testing-0.0.11 → remoterf_server_testing-0.0.13}/src/remoteRF_server/host/__init__.py +0 -0
- {remoterf_server_testing-0.0.11 → remoterf_server_testing-0.0.13}/src/remoteRF_server/host/host_auth_token.py +0 -0
- {remoterf_server_testing-0.0.11 → remoterf_server_testing-0.0.13}/src/remoteRF_server/host/host_directory_store.py +0 -0
- {remoterf_server_testing-0.0.11 → remoterf_server_testing-0.0.13}/src/remoteRF_server/host/host_tunnel_server.py +0 -0
- {remoterf_server_testing-0.0.11 → remoterf_server_testing-0.0.13}/src/remoteRF_server/server/__init__.py +0 -0
- {remoterf_server_testing-0.0.11 → remoterf_server_testing-0.0.13}/src/remoteRF_server/server/acc_perms.py +0 -0
- {remoterf_server_testing-0.0.11 → remoterf_server_testing-0.0.13}/src/remoteRF_server/server/cert_provider.py +0 -0
- {remoterf_server_testing-0.0.11 → remoterf_server_testing-0.0.13}/src/remoteRF_server/server/grpc_server.py +0 -0
- {remoterf_server_testing-0.0.11 → remoterf_server_testing-0.0.13}/src/remoteRF_server/server/reservation.py +0 -0
- {remoterf_server_testing-0.0.11 → remoterf_server_testing-0.0.13}/src/remoteRF_server/server/rpc_manager.py +0 -0
- {remoterf_server_testing-0.0.11 → remoterf_server_testing-0.0.13}/src/remoteRF_server/server/user_group_cli.py +0 -0
- {remoterf_server_testing-0.0.11 → remoterf_server_testing-0.0.13}/src/remoteRF_server/server/user_group_handler.py +0 -0
- {remoterf_server_testing-0.0.11 → remoterf_server_testing-0.0.13}/src/remoteRF_server/tools/__init__.py +0 -0
- {remoterf_server_testing-0.0.11 → remoterf_server_testing-0.0.13}/src/remoteRF_server/tools/gen_certs.py +0 -0
- {remoterf_server_testing-0.0.11 → remoterf_server_testing-0.0.13}/src/remoteRF_server/tools/gist_status_testing.py +0 -0
- {remoterf_server_testing-0.0.11 → remoterf_server_testing-0.0.13}/src/remoteRF_server_testing.egg-info/dependency_links.txt +0 -0
- {remoterf_server_testing-0.0.11 → remoterf_server_testing-0.0.13}/src/remoteRF_server_testing.egg-info/entry_points.txt +0 -0
- {remoterf_server_testing-0.0.11 → remoterf_server_testing-0.0.13}/src/remoteRF_server_testing.egg-info/requires.txt +0 -0
- {remoterf_server_testing-0.0.11 → remoterf_server_testing-0.0.13}/src/remoteRF_server_testing.egg-info/top_level.txt +0 -0
|
@@ -0,0 +1,228 @@
|
|
|
1
|
+
# SAMPLE schema for ADI Pluto SDR.
|
|
2
|
+
# Place into -> ~/.config/remoterf/drivers and restart server to utilize!
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
from remoteRF_server.common.idl import DeviceSchema, idl_register, idl_expose
|
|
6
|
+
|
|
7
|
+
import subprocess
|
|
8
|
+
import re
|
|
9
|
+
import adi
|
|
10
|
+
|
|
11
|
+
# == PLUTO Connection Logic ==
|
|
12
|
+
|
|
13
|
+
def connect_pluto(*, serial: str):
|
|
14
|
+
serial = (serial or "").strip()
|
|
15
|
+
if not serial:
|
|
16
|
+
print("A Pluto serial must be provided")
|
|
17
|
+
return None
|
|
18
|
+
|
|
19
|
+
try:
|
|
20
|
+
out = subprocess.check_output(
|
|
21
|
+
["iio_info", "-s"],
|
|
22
|
+
text=True,
|
|
23
|
+
stderr=subprocess.STDOUT
|
|
24
|
+
)
|
|
25
|
+
|
|
26
|
+
usb = None
|
|
27
|
+
for line in out.splitlines():
|
|
28
|
+
if (f"serial={serial}" in line) or (f"hw_serial={serial}" in line):
|
|
29
|
+
m = re.search(r"\[usb:([^\]]+)\]", line)
|
|
30
|
+
if m:
|
|
31
|
+
usb = m.group(1).strip()
|
|
32
|
+
break
|
|
33
|
+
|
|
34
|
+
if not usb:
|
|
35
|
+
print(f"No device found with serial {serial}")
|
|
36
|
+
return None
|
|
37
|
+
|
|
38
|
+
dev = adi.Pluto(f"usb:{usb}")
|
|
39
|
+
print(f"Connected to Pluto serial={serial} via usb:{usb}")
|
|
40
|
+
return dev
|
|
41
|
+
|
|
42
|
+
except Exception as e:
|
|
43
|
+
print(f"Failed to connect to Pluto serial={serial}: {e}")
|
|
44
|
+
return None
|
|
45
|
+
|
|
46
|
+
# ========================= PLUTO Schema =========================
|
|
47
|
+
|
|
48
|
+
@idl_register("pluto")
|
|
49
|
+
class PlutoSchema(DeviceSchema):
|
|
50
|
+
|
|
51
|
+
device_type = "pluto"
|
|
52
|
+
driver_version = "0.0.1"
|
|
53
|
+
|
|
54
|
+
@staticmethod
|
|
55
|
+
def make_device(**kwargs):
|
|
56
|
+
serial = kwargs.get("serial")
|
|
57
|
+
return connect_pluto(serial=serial)
|
|
58
|
+
|
|
59
|
+
# region ad9364
|
|
60
|
+
|
|
61
|
+
@idl_expose(kind="get")
|
|
62
|
+
def get_filter(self):
|
|
63
|
+
return self.device.filter
|
|
64
|
+
|
|
65
|
+
@idl_expose(kind="set")
|
|
66
|
+
def set_filter(self, value):
|
|
67
|
+
self.device.filter = value
|
|
68
|
+
|
|
69
|
+
@idl_expose(kind="get")
|
|
70
|
+
def get_loopback(self):
|
|
71
|
+
return self.device.loopback
|
|
72
|
+
|
|
73
|
+
@idl_expose(kind="set")
|
|
74
|
+
def set_loopback(self, value):
|
|
75
|
+
self.device.loopback = value
|
|
76
|
+
|
|
77
|
+
@idl_expose(kind="get")
|
|
78
|
+
def get_gain_control_mode_chan0(self):
|
|
79
|
+
return self.device.gain_control_mode_chan0
|
|
80
|
+
|
|
81
|
+
@idl_expose(kind="set")
|
|
82
|
+
def set_gain_control_mode_chan0(self, value):
|
|
83
|
+
self.device.gain_control_mode_chan0 = value
|
|
84
|
+
|
|
85
|
+
@idl_expose(kind="get")
|
|
86
|
+
def get_hardwaregain_chan0(self):
|
|
87
|
+
"""RX hardware gain (dB). pyadi-iio attribute: hardwaregain_chan0"""
|
|
88
|
+
return self.device.hardwaregain_chan0
|
|
89
|
+
|
|
90
|
+
@idl_expose(kind="set")
|
|
91
|
+
def set_hardwaregain_chan0(self, value):
|
|
92
|
+
"""RX hardware gain (dB). pyadi-iio attribute: hardwaregain_chan0"""
|
|
93
|
+
self.device.hardwaregain_chan0 = value
|
|
94
|
+
|
|
95
|
+
@idl_expose(kind="get")
|
|
96
|
+
def get_tx_hardwaregain_chan0(self):
|
|
97
|
+
return self.device.tx_hardwaregain_chan0
|
|
98
|
+
|
|
99
|
+
@idl_expose(kind="set")
|
|
100
|
+
def set_tx_hardwaregain_chan0(self, value):
|
|
101
|
+
self.device.tx_hardwaregain_chan0 = value
|
|
102
|
+
|
|
103
|
+
@idl_expose(kind="get")
|
|
104
|
+
def get_rx_rf_bandwidth(self):
|
|
105
|
+
return self.device.rx_rf_bandwidth
|
|
106
|
+
|
|
107
|
+
@idl_expose(kind="set")
|
|
108
|
+
def set_rx_rf_bandwidth(self, value):
|
|
109
|
+
self.device.rx_rf_bandwidth = value
|
|
110
|
+
|
|
111
|
+
@idl_expose(kind="get")
|
|
112
|
+
def get_tx_rf_bandwidth(self):
|
|
113
|
+
return self.device.tx_rf_bandwidth
|
|
114
|
+
|
|
115
|
+
@idl_expose(kind="set")
|
|
116
|
+
def set_tx_rf_bandwidth(self, value):
|
|
117
|
+
self.device.tx_rf_bandwidth = value
|
|
118
|
+
|
|
119
|
+
@idl_expose(kind="get")
|
|
120
|
+
def get_sample_rate(self):
|
|
121
|
+
return self.device.sample_rate
|
|
122
|
+
|
|
123
|
+
@idl_expose(kind="set")
|
|
124
|
+
def set_sample_rate(self, value):
|
|
125
|
+
self.device.sample_rate = value
|
|
126
|
+
|
|
127
|
+
@idl_expose(kind="get")
|
|
128
|
+
def get_rx_lo(self):
|
|
129
|
+
return self.device.rx_lo
|
|
130
|
+
|
|
131
|
+
@idl_expose(kind="set")
|
|
132
|
+
def set_rx_lo(self, value):
|
|
133
|
+
self.device.rx_lo = value
|
|
134
|
+
|
|
135
|
+
@idl_expose(kind="get")
|
|
136
|
+
def get_tx_lo(self):
|
|
137
|
+
return self.device.tx_lo
|
|
138
|
+
|
|
139
|
+
@idl_expose(kind="set")
|
|
140
|
+
def set_tx_lo(self, value):
|
|
141
|
+
self.device.tx_lo = value
|
|
142
|
+
|
|
143
|
+
@idl_expose(kind="get")
|
|
144
|
+
def get_tx_cyclic_buffer(self):
|
|
145
|
+
return self.device.tx_cyclic_buffer
|
|
146
|
+
|
|
147
|
+
@idl_expose(kind="set")
|
|
148
|
+
def set_tx_cyclic_buffer(self, value):
|
|
149
|
+
self.device.tx_cyclic_buffer = value
|
|
150
|
+
|
|
151
|
+
# region RX/TX
|
|
152
|
+
|
|
153
|
+
@idl_expose(kind="call")
|
|
154
|
+
def call_rx(self):
|
|
155
|
+
return self.device.rx()
|
|
156
|
+
|
|
157
|
+
@idl_expose(kind="get")
|
|
158
|
+
def get_rx_buffer_size(self):
|
|
159
|
+
return self.device.rx_buffer_size
|
|
160
|
+
|
|
161
|
+
@idl_expose(kind="set")
|
|
162
|
+
def set_rx_buffer_size(self, value):
|
|
163
|
+
self.device.rx_buffer_size = value
|
|
164
|
+
|
|
165
|
+
@idl_expose(kind="call")
|
|
166
|
+
def call_rx_destroy_buffer(self):
|
|
167
|
+
self.device.rx_destroy_buffer()
|
|
168
|
+
|
|
169
|
+
@idl_expose(kind="call")
|
|
170
|
+
def call_tx(self, value):
|
|
171
|
+
self.device.tx(value)
|
|
172
|
+
|
|
173
|
+
@idl_expose(kind="call")
|
|
174
|
+
def call_tx_destroy_buffer(self):
|
|
175
|
+
self.device.tx_destroy_buffer()
|
|
176
|
+
|
|
177
|
+
@idl_expose(kind="call")
|
|
178
|
+
def call_disable_dds(self):
|
|
179
|
+
"""Disable the internal DDS tone generator."""
|
|
180
|
+
self.device.disable_dds()
|
|
181
|
+
|
|
182
|
+
# endregion
|
|
183
|
+
|
|
184
|
+
# region ip / repr
|
|
185
|
+
|
|
186
|
+
@idl_expose(kind="call")
|
|
187
|
+
def call_ip(self):
|
|
188
|
+
"""Return a connection identifier for this device (ping / verify)."""
|
|
189
|
+
try:
|
|
190
|
+
return str(self.device.uri)
|
|
191
|
+
except AttributeError:
|
|
192
|
+
return str(self.device._uri_auto)
|
|
193
|
+
|
|
194
|
+
@idl_expose(kind="get")
|
|
195
|
+
def get_repr(self):
|
|
196
|
+
"""Return repr() of the underlying device object."""
|
|
197
|
+
return repr(self.device)
|
|
198
|
+
|
|
199
|
+
# endregion
|
|
200
|
+
|
|
201
|
+
# region _dec_int_fpga_filter
|
|
202
|
+
|
|
203
|
+
@idl_expose(kind="get")
|
|
204
|
+
def get_rx_dec8_filter_en(self):
|
|
205
|
+
"""rx_dec8_filter_en: Enable decimate-by-8 filter in FPGA"""
|
|
206
|
+
return bool(self.device.rx_dec8_filter_en)
|
|
207
|
+
|
|
208
|
+
@idl_expose(kind="set")
|
|
209
|
+
def set_rx_dec8_filter_en(self, value):
|
|
210
|
+
"""rx_dec8_filter_en: Enable decimate-by-8 filter in FPGA"""
|
|
211
|
+
self.device.rx_dec8_filter_en = value
|
|
212
|
+
|
|
213
|
+
@idl_expose(kind="get")
|
|
214
|
+
def get_tx_int8_filter_en(self):
|
|
215
|
+
"""tx_int8_filter_en: Enable interpolate-by-8 filter in FPGA"""
|
|
216
|
+
return bool(self.device.tx_int8_filter_en)
|
|
217
|
+
|
|
218
|
+
@idl_expose(kind="set")
|
|
219
|
+
def set_tx_int8_filter_en(self, value):
|
|
220
|
+
"""tx_int8_filter_en: Enable interpolate-by-8 filter in FPGA"""
|
|
221
|
+
self.device.tx_int8_filter_en = value
|
|
222
|
+
|
|
223
|
+
@idl_expose(kind="get")
|
|
224
|
+
def get_rates(self):
|
|
225
|
+
"""Get decimation/interpolation rates from the FPGA filter block."""
|
|
226
|
+
return self.device._get_rates(self.device._rxadc, False)
|
|
227
|
+
|
|
228
|
+
# endregion
|
|
@@ -4,6 +4,7 @@ from __future__ import annotations
|
|
|
4
4
|
|
|
5
5
|
import os
|
|
6
6
|
import re
|
|
7
|
+
import shutil
|
|
7
8
|
import time
|
|
8
9
|
import yaml
|
|
9
10
|
import threading
|
|
@@ -12,7 +13,7 @@ from pathlib import Path
|
|
|
12
13
|
from typing import Dict, Tuple, Optional, Any, List, Iterator
|
|
13
14
|
from contextlib import contextmanager
|
|
14
15
|
|
|
15
|
-
from ..common.utils import validate_token, get_remoterf_root
|
|
16
|
+
from ..common.utils import validate_token, get_remoterf_root, get_drivers_dir
|
|
16
17
|
from ..common.idl.schema import get_driver_class, load_drivers
|
|
17
18
|
from ..host import host_tunnel_server as hts
|
|
18
19
|
|
|
@@ -245,10 +246,19 @@ def _get_master_token() -> str:
|
|
|
245
246
|
return ""
|
|
246
247
|
|
|
247
248
|
def _devices_yaml_path() -> Path:
|
|
248
|
-
|
|
249
|
+
cfg_dir = _cfg_dir()
|
|
250
|
+
p1 = cfg_dir / "devices.yml"
|
|
249
251
|
if p1.exists():
|
|
250
252
|
return p1
|
|
251
|
-
|
|
253
|
+
p2 = cfg_dir / "devices.yaml"
|
|
254
|
+
if p2.exists():
|
|
255
|
+
return p2
|
|
256
|
+
try:
|
|
257
|
+
cfg_dir.mkdir(parents=True, exist_ok=True)
|
|
258
|
+
p1.touch(exist_ok=True)
|
|
259
|
+
except OSError as e:
|
|
260
|
+
print(f"Warning: could not create empty devices config {p1}: {e}")
|
|
261
|
+
return p1
|
|
252
262
|
|
|
253
263
|
def _load_device_records() -> Dict[int, Dict[str, str]]:
|
|
254
264
|
path = _devices_yaml_path()
|
|
@@ -376,7 +386,48 @@ def _init_from_env() -> None:
|
|
|
376
386
|
|
|
377
387
|
# Load built-in drivers, then any user-supplied drivers from the config dir.
|
|
378
388
|
# Importing the module triggers @idl_register, which adds the class to the registry.
|
|
389
|
+
def _packaged_pluto_schema_path() -> Path:
|
|
390
|
+
return Path(__file__).resolve().parents[1] / "drivers" / "adalm_pluto" / "pluto_schema.py"
|
|
391
|
+
|
|
392
|
+
def _driver_registers_pluto(path: Path) -> bool:
|
|
393
|
+
try:
|
|
394
|
+
text = path.read_text(encoding="utf-8")
|
|
395
|
+
except OSError:
|
|
396
|
+
return False
|
|
397
|
+
return re.search(r"(?:idl_register|register_driver)\(\s*['\"]pluto['\"]\s*\)", text) is not None
|
|
398
|
+
|
|
399
|
+
def _seed_default_pluto_driver() -> None:
|
|
400
|
+
drivers_dir = get_drivers_dir()
|
|
401
|
+
target = drivers_dir / "pluto_schema.py"
|
|
402
|
+
if target.exists() or target.is_symlink():
|
|
403
|
+
return
|
|
404
|
+
|
|
405
|
+
try:
|
|
406
|
+
drivers_dir.mkdir(parents=True, exist_ok=True)
|
|
407
|
+
except OSError as e:
|
|
408
|
+
print(f"Warning: could not create drivers directory {drivers_dir}: {e}")
|
|
409
|
+
return
|
|
410
|
+
|
|
411
|
+
for driver_file in drivers_dir.glob("*.py"):
|
|
412
|
+
if driver_file.name.startswith("_"):
|
|
413
|
+
continue
|
|
414
|
+
if _driver_registers_pluto(driver_file):
|
|
415
|
+
return
|
|
416
|
+
|
|
417
|
+
source = _packaged_pluto_schema_path()
|
|
418
|
+
if not source.exists():
|
|
419
|
+
print(f"Warning: default Pluto schema source is missing: {source}")
|
|
420
|
+
return
|
|
421
|
+
|
|
422
|
+
try:
|
|
423
|
+
shutil.copy2(source, target)
|
|
424
|
+
print(f"Created default Pluto driver config at {target}")
|
|
425
|
+
except OSError as e:
|
|
426
|
+
print(f"Warning: could not create default Pluto driver config {target}: {e}")
|
|
427
|
+
|
|
379
428
|
def _load_all_drivers() -> None:
|
|
429
|
+
_seed_default_pluto_driver()
|
|
430
|
+
|
|
380
431
|
try:
|
|
381
432
|
from ..common.idl import pluto_schema as _ # noqa: F401
|
|
382
433
|
except Exception as e:
|
|
@@ -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"
|
|
@@ -139,12 +194,12 @@ def print_help() -> None:
|
|
|
139
194
|
"Devices:\n"
|
|
140
195
|
" serverrf -d | --device [options]\n"
|
|
141
196
|
"\n"
|
|
142
|
-
"Device options:\n"
|
|
143
|
-
" --add --pluto <id:name:iio_serial> Add device (fails if gid OR serial already used)\n"
|
|
144
|
-
" --remove <id> Remove device\n"
|
|
145
|
-
" --edit-name <id> <name> Override device NAME for existing device\n"
|
|
146
|
-
" --show | -s
|
|
147
|
-
" -w | --wipe [-y]
|
|
197
|
+
# "Device options:\n"
|
|
198
|
+
# " --add --pluto <id:name:iio_serial> Add device (fails if gid OR serial already used)\n"
|
|
199
|
+
# " --remove <id> Remove device\n"
|
|
200
|
+
# " --edit-name <id> <name> Override device NAME for existing device\n"
|
|
201
|
+
# " --show | -s Show all devices\n"
|
|
202
|
+
# " -w | --wipe [-y] Wipe ONLY device config (devices.env)\n"
|
|
148
203
|
|
|
149
204
|
"\n"
|
|
150
205
|
"Cert options (for --gen-certs):\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
|
-
print(f"
|
|
592
|
+
print(f" MAIN_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
|
|
|
@@ -766,8 +828,12 @@ def _wipe_devices_only(*, yes: bool) -> int:
|
|
|
766
828
|
def main() -> int:
|
|
767
829
|
argv = list(sys.argv[1:])
|
|
768
830
|
|
|
831
|
+
if len(argv) == 0:
|
|
832
|
+
print("invalid args, expect serverrf <args>, run serverrf --help for more info", file=sys.stderr)
|
|
833
|
+
return 2
|
|
834
|
+
|
|
769
835
|
# help
|
|
770
|
-
if
|
|
836
|
+
if argv[0] in ("--help", "-h", "-help", "--h"):
|
|
771
837
|
print_help()
|
|
772
838
|
return 0
|
|
773
839
|
|
|
@@ -1256,7 +1322,7 @@ def main() -> int:
|
|
|
1256
1322
|
return 2
|
|
1257
1323
|
|
|
1258
1324
|
print("Host token created and stored at ~/.config/remoterf/db/hosts_auth.env")
|
|
1259
|
-
print(f'
|
|
1325
|
+
print(f'On the host you want to setup, run the following:\n hostrf --config --host {host_token_create_id} "{token}"')
|
|
1260
1326
|
return 0
|
|
1261
1327
|
|
|
1262
1328
|
# ----------------
|
|
@@ -111,7 +111,7 @@ def start_status_publisher() -> None:
|
|
|
111
111
|
return
|
|
112
112
|
|
|
113
113
|
if _load_gist_env() is None:
|
|
114
|
-
print("[gist_status] off (not configured; see README gist docs)", file=sys.stderr)
|
|
114
|
+
# print("[gist_status] off (not configured; see README gist docs)", file=sys.stderr)
|
|
115
115
|
return
|
|
116
116
|
|
|
117
117
|
t = threading.Thread(
|
|
@@ -22,6 +22,7 @@ src/remoteRF_server/common/utils/process_arg.py
|
|
|
22
22
|
src/remoteRF_server/drivers/__init__.py
|
|
23
23
|
src/remoteRF_server/drivers/adalm_pluto/__init__.py
|
|
24
24
|
src/remoteRF_server/drivers/adalm_pluto/pluto_remote_server.py
|
|
25
|
+
src/remoteRF_server/drivers/adalm_pluto/pluto_schema.py
|
|
25
26
|
src/remoteRF_server/host/__init__.py
|
|
26
27
|
src/remoteRF_server/host/host_auth_token.py
|
|
27
28
|
src/remoteRF_server/host/host_directory_store.py
|
|
File without changes
|
|
File without changes
|
{remoterf_server_testing-0.0.11 → remoterf_server_testing-0.0.13}/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
|