remoteRF-server 1.0.3__tar.gz → 1.0.5__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-1.0.3 → remoterf_server-1.0.5}/PKG-INFO +5 -31
- {remoterf_server-1.0.3 → remoterf_server-1.0.5}/README.md +4 -30
- {remoterf_server-1.0.3 → remoterf_server-1.0.5}/pyproject.toml +1 -1
- {remoterf_server-1.0.3 → remoterf_server-1.0.5}/src/remoteRF_server/common/idl/schema.py +2 -2
- remoterf_server-1.0.5/src/remoteRF_server/drivers/usrp/policy.py +41 -0
- {remoterf_server-1.0.3 → remoterf_server-1.0.5}/src/remoteRF_server/drivers/usrp/uhd_manifest.py +8 -25
- {remoterf_server-1.0.3 → remoterf_server-1.0.5}/src/remoteRF_server/drivers/usrp/usrp_schema.py +1 -2
- {remoterf_server-1.0.3 → remoterf_server-1.0.5}/src/remoteRF_server/server/device_manager.py +6 -1
- {remoterf_server-1.0.3 → remoterf_server-1.0.5}/src/remoteRF_server/server/dynamic_v2.py +4 -34
- {remoterf_server-1.0.3 → remoterf_server-1.0.5}/src/remoteRF_server/server/grpc_server.py +13 -5
- {remoterf_server-1.0.3 → remoterf_server-1.0.5}/src/remoteRF_server/serverrf_cli.py +14 -18
- {remoterf_server-1.0.3 → remoterf_server-1.0.5}/src/remoteRF_server.egg-info/PKG-INFO +5 -31
- {remoterf_server-1.0.3 → remoterf_server-1.0.5}/tests/test_cert_provider.py +24 -0
- {remoterf_server-1.0.3 → remoterf_server-1.0.5}/tests/test_dynamic_v2.py +108 -79
- {remoterf_server-1.0.3 → remoterf_server-1.0.5}/tests/test_pluto_compat.py +15 -0
- remoterf_server-1.0.3/src/remoteRF_server/drivers/usrp/policy.py +0 -363
- {remoterf_server-1.0.3 → remoterf_server-1.0.5}/setup.cfg +0 -0
- {remoterf_server-1.0.3 → remoterf_server-1.0.5}/src/remoteRF_server/__init__.py +0 -0
- {remoterf_server-1.0.3 → remoterf_server-1.0.5}/src/remoteRF_server/common/__init__.py +0 -0
- {remoterf_server-1.0.3 → remoterf_server-1.0.5}/src/remoteRF_server/common/grpc/__init__.py +0 -0
- {remoterf_server-1.0.3 → remoterf_server-1.0.5}/src/remoteRF_server/common/grpc/grpc_host_pb2.py +0 -0
- {remoterf_server-1.0.3 → remoterf_server-1.0.5}/src/remoteRF_server/common/grpc/grpc_host_pb2_grpc.py +0 -0
- {remoterf_server-1.0.3 → remoterf_server-1.0.5}/src/remoteRF_server/common/grpc/grpc_pb2.py +0 -0
- {remoterf_server-1.0.3 → remoterf_server-1.0.5}/src/remoteRF_server/common/grpc/grpc_pb2_grpc.py +0 -0
- {remoterf_server-1.0.3 → remoterf_server-1.0.5}/src/remoteRF_server/common/grpc/v2_codec.py +0 -0
- {remoterf_server-1.0.3 → remoterf_server-1.0.5}/src/remoteRF_server/common/idl/__init__.py +0 -0
- {remoterf_server-1.0.3 → remoterf_server-1.0.5}/src/remoteRF_server/common/idl/device_schema.py +0 -0
- {remoterf_server-1.0.3 → remoterf_server-1.0.5}/src/remoteRF_server/common/idl/pluto_mimo_schema.py +0 -0
- {remoterf_server-1.0.3 → remoterf_server-1.0.5}/src/remoteRF_server/common/idl/pluto_schema.py +0 -0
- {remoterf_server-1.0.3 → remoterf_server-1.0.5}/src/remoteRF_server/common/idl/v2.py +0 -0
- {remoterf_server-1.0.3 → remoterf_server-1.0.5}/src/remoteRF_server/common/utils/__init__.py +0 -0
- {remoterf_server-1.0.3 → remoterf_server-1.0.5}/src/remoteRF_server/common/utils/ansi_codes.py +0 -0
- {remoterf_server-1.0.3 → remoterf_server-1.0.5}/src/remoteRF_server/common/utils/api_token.py +0 -0
- {remoterf_server-1.0.3 → remoterf_server-1.0.5}/src/remoteRF_server/common/utils/db_connection.py +0 -0
- {remoterf_server-1.0.3 → remoterf_server-1.0.5}/src/remoteRF_server/common/utils/db_location.py +0 -0
- {remoterf_server-1.0.3 → remoterf_server-1.0.5}/src/remoteRF_server/common/utils/list_string.py +0 -0
- {remoterf_server-1.0.3 → remoterf_server-1.0.5}/src/remoteRF_server/common/utils/process_arg.py +0 -0
- {remoterf_server-1.0.3 → remoterf_server-1.0.5}/src/remoteRF_server/drivers/__init__.py +0 -0
- {remoterf_server-1.0.3 → remoterf_server-1.0.5}/src/remoteRF_server/drivers/adalm_pluto/__init__.py +0 -0
- {remoterf_server-1.0.3 → remoterf_server-1.0.5}/src/remoteRF_server/drivers/adalm_pluto/pluto_mimo_schema.py +0 -0
- {remoterf_server-1.0.3 → remoterf_server-1.0.5}/src/remoteRF_server/drivers/adalm_pluto/pluto_remote_server.py +0 -0
- {remoterf_server-1.0.3 → remoterf_server-1.0.5}/src/remoteRF_server/drivers/adalm_pluto/pluto_schema.py +0 -0
- {remoterf_server-1.0.3 → remoterf_server-1.0.5}/src/remoteRF_server/drivers/usrp/__init__.py +0 -0
- {remoterf_server-1.0.3 → remoterf_server-1.0.5}/src/remoteRF_server/host/__init__.py +0 -0
- {remoterf_server-1.0.3 → remoterf_server-1.0.5}/src/remoteRF_server/host/host_auth_token.py +0 -0
- {remoterf_server-1.0.3 → remoterf_server-1.0.5}/src/remoteRF_server/host/host_directory_store.py +0 -0
- {remoterf_server-1.0.3 → remoterf_server-1.0.5}/src/remoteRF_server/host/host_tunnel_server.py +0 -0
- {remoterf_server-1.0.3 → remoterf_server-1.0.5}/src/remoteRF_server/server/__init__.py +0 -0
- {remoterf_server-1.0.3 → remoterf_server-1.0.5}/src/remoteRF_server/server/acc_perms.py +0 -0
- {remoterf_server-1.0.3 → remoterf_server-1.0.5}/src/remoteRF_server/server/cert_provider.py +0 -0
- {remoterf_server-1.0.3 → remoterf_server-1.0.5}/src/remoteRF_server/server/reservation.py +0 -0
- {remoterf_server-1.0.3 → remoterf_server-1.0.5}/src/remoteRF_server/server/rpc_manager.py +0 -0
- {remoterf_server-1.0.3 → remoterf_server-1.0.5}/src/remoteRF_server/server/user_group_cli.py +0 -0
- {remoterf_server-1.0.3 → remoterf_server-1.0.5}/src/remoteRF_server/server/user_group_handler.py +0 -0
- {remoterf_server-1.0.3 → remoterf_server-1.0.5}/src/remoteRF_server/server/v2_errors.py +0 -0
- {remoterf_server-1.0.3 → remoterf_server-1.0.5}/src/remoteRF_server/tools/__init__.py +0 -0
- {remoterf_server-1.0.3 → remoterf_server-1.0.5}/src/remoteRF_server/tools/gen_certs.py +0 -0
- {remoterf_server-1.0.3 → remoterf_server-1.0.5}/src/remoteRF_server/tools/gist_status.py +0 -0
- {remoterf_server-1.0.3 → remoterf_server-1.0.5}/src/remoteRF_server/tools/gist_status_testing.py +0 -0
- {remoterf_server-1.0.3 → remoterf_server-1.0.5}/src/remoteRF_server/tools/introspect_uhd.py +0 -0
- {remoterf_server-1.0.3 → remoterf_server-1.0.5}/src/remoteRF_server.egg-info/SOURCES.txt +0 -0
- {remoterf_server-1.0.3 → remoterf_server-1.0.5}/src/remoteRF_server.egg-info/dependency_links.txt +0 -0
- {remoterf_server-1.0.3 → remoterf_server-1.0.5}/src/remoteRF_server.egg-info/entry_points.txt +0 -0
- {remoterf_server-1.0.3 → remoterf_server-1.0.5}/src/remoteRF_server.egg-info/requires.txt +0 -0
- {remoterf_server-1.0.3 → remoterf_server-1.0.5}/src/remoteRF_server.egg-info/top_level.txt +0 -0
- {remoterf_server-1.0.3 → remoterf_server-1.0.5}/tests/test_default_usrp_schema.py +0 -0
- {remoterf_server-1.0.3 → remoterf_server-1.0.5}/tests/test_idl_schema.py +0 -0
- {remoterf_server-1.0.3 → remoterf_server-1.0.5}/tests/test_process_arg.py +0 -0
- {remoterf_server-1.0.3 → remoterf_server-1.0.5}/tests/test_reservation_timestamps.py +0 -0
- {remoterf_server-1.0.3 → remoterf_server-1.0.5}/tests/test_rpc_manager_calln.py +0 -0
- {remoterf_server-1.0.3 → remoterf_server-1.0.5}/tests/test_usrp_client_server_hardware.py +0 -0
- {remoterf_server-1.0.3 → remoterf_server-1.0.5}/tests/test_usrp_client_server_localhost.py +0 -0
- {remoterf_server-1.0.3 → remoterf_server-1.0.5}/tests/test_usrp_remoterf_rx_hardware.py +0 -0
- {remoterf_server-1.0.3 → remoterf_server-1.0.5}/tests/test_usrp_uhd410_hardware.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: remoteRF-server
|
|
3
|
-
Version: 1.0.
|
|
3
|
+
Version: 1.0.5
|
|
4
4
|
Summary: RemoteRF server-side control package
|
|
5
5
|
Requires-Python: >=3.10
|
|
6
6
|
Description-Content-Type: text/markdown
|
|
@@ -329,36 +329,10 @@ devices:
|
|
|
329
329
|
serial: 31A2B3C
|
|
330
330
|
```
|
|
331
331
|
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
```yaml
|
|
338
|
-
defaults:
|
|
339
|
-
tx_enabled: false
|
|
340
|
-
low_level_write_enabled: false
|
|
341
|
-
|
|
342
|
-
devices:
|
|
343
|
-
"31A2B3C":
|
|
344
|
-
tx_enabled: true
|
|
345
|
-
tx_frequency_ranges_hz:
|
|
346
|
-
- [902000000, 928000000]
|
|
347
|
-
max_tx_gain_db: 20
|
|
348
|
-
max_tx_normalized_gain: 0.5
|
|
349
|
-
max_tx_power_dbm: -10
|
|
350
|
-
max_tx_bandwidth_hz: 2000000
|
|
351
|
-
max_tx_sample_rate_sps: 2000000
|
|
352
|
-
allowed_tx_antennas: [TX/RX]
|
|
353
|
-
max_burst_seconds: 1.0
|
|
354
|
-
max_duty_cycle: 0.1
|
|
355
|
-
duty_cycle_window_seconds: 60
|
|
356
|
-
low_level_write_enabled: false
|
|
357
|
-
low_level_write_methods: []
|
|
358
|
-
gpio_write_masks: {}
|
|
359
|
-
property_write_paths: []
|
|
360
|
-
register_write_addresses: []
|
|
361
|
-
```
|
|
332
|
+
All USRP operations exposed by the Dynamic v2 schema are available without a
|
|
333
|
+
separate policy file. This includes transmit controls, transmit streaming,
|
|
334
|
+
GPIO/register methods, and property-tree writes. `usrp_policy.yml` is not read
|
|
335
|
+
or required by the current server.
|
|
362
336
|
|
|
363
337
|
On every qualified target, generate the introspection record and enforce the
|
|
364
338
|
pinned API gate:
|
|
@@ -314,36 +314,10 @@ devices:
|
|
|
314
314
|
serial: 31A2B3C
|
|
315
315
|
```
|
|
316
316
|
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
```yaml
|
|
323
|
-
defaults:
|
|
324
|
-
tx_enabled: false
|
|
325
|
-
low_level_write_enabled: false
|
|
326
|
-
|
|
327
|
-
devices:
|
|
328
|
-
"31A2B3C":
|
|
329
|
-
tx_enabled: true
|
|
330
|
-
tx_frequency_ranges_hz:
|
|
331
|
-
- [902000000, 928000000]
|
|
332
|
-
max_tx_gain_db: 20
|
|
333
|
-
max_tx_normalized_gain: 0.5
|
|
334
|
-
max_tx_power_dbm: -10
|
|
335
|
-
max_tx_bandwidth_hz: 2000000
|
|
336
|
-
max_tx_sample_rate_sps: 2000000
|
|
337
|
-
allowed_tx_antennas: [TX/RX]
|
|
338
|
-
max_burst_seconds: 1.0
|
|
339
|
-
max_duty_cycle: 0.1
|
|
340
|
-
duty_cycle_window_seconds: 60
|
|
341
|
-
low_level_write_enabled: false
|
|
342
|
-
low_level_write_methods: []
|
|
343
|
-
gpio_write_masks: {}
|
|
344
|
-
property_write_paths: []
|
|
345
|
-
register_write_addresses: []
|
|
346
|
-
```
|
|
317
|
+
All USRP operations exposed by the Dynamic v2 schema are available without a
|
|
318
|
+
separate policy file. This includes transmit controls, transmit streaming,
|
|
319
|
+
GPIO/register methods, and property-tree writes. `usrp_policy.yml` is not read
|
|
320
|
+
or required by the current server.
|
|
347
321
|
|
|
348
322
|
On every qualified target, generate the introspection record and enforce the
|
|
349
323
|
pinned API gate:
|
|
@@ -6,7 +6,7 @@ Admin writes one .py per device TYPE, drops it in ~/.config/remoterf/drivers/,
|
|
|
6
6
|
and the server auto-discovers it on startup via load_drivers().
|
|
7
7
|
|
|
8
8
|
~/.config/remoterf/
|
|
9
|
-
devices.
|
|
9
|
+
devices.yml ← "I have 3 devices at these serials"
|
|
10
10
|
drivers/
|
|
11
11
|
pluto_schema.py ← "here's what a pluto IS"
|
|
12
12
|
hackrf_schema.py
|
|
@@ -531,7 +531,7 @@ class DeviceSchema:
|
|
|
531
531
|
Base for all device schemas. Admin subclasses this once per device type.
|
|
532
532
|
|
|
533
533
|
Class attributes (admin sets these):
|
|
534
|
-
device_type str — must match
|
|
534
|
+
device_type str — must match device_type in devices.yml
|
|
535
535
|
driver_version str — semver
|
|
536
536
|
|
|
537
537
|
Admin implements:
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
"""Compatibility shim for unrestricted USRP access.
|
|
2
|
+
|
|
3
|
+
USRP policy enforcement is currently disabled. ``USRPPolicy`` remains importable
|
|
4
|
+
so older integrations do not fail at import time, but every supported control,
|
|
5
|
+
streaming, property-tree, GPIO, and register operation passes through without
|
|
6
|
+
policy checks.
|
|
7
|
+
"""
|
|
8
|
+
from __future__ import annotations
|
|
9
|
+
|
|
10
|
+
from pathlib import Path
|
|
11
|
+
from typing import Any, Mapping
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
class USRPPolicy:
|
|
15
|
+
"""No-op policy retained for protocol and integration compatibility."""
|
|
16
|
+
|
|
17
|
+
def __init__(self, config: Mapping[str, Any] | None = None, *, source: str = ""):
|
|
18
|
+
self.config = dict(config or {})
|
|
19
|
+
self.source = source
|
|
20
|
+
|
|
21
|
+
@classmethod
|
|
22
|
+
def load(cls, *, gid: int, serial: str = "", path: Path | None = None) -> "USRPPolicy":
|
|
23
|
+
del gid, serial, path
|
|
24
|
+
return cls()
|
|
25
|
+
|
|
26
|
+
@property
|
|
27
|
+
def tx_enabled(self) -> bool:
|
|
28
|
+
return True
|
|
29
|
+
|
|
30
|
+
@property
|
|
31
|
+
def restrictions(self) -> dict[str, Any]:
|
|
32
|
+
return {}
|
|
33
|
+
|
|
34
|
+
def check_control(self, method: str, args: Mapping[str, Any]) -> None:
|
|
35
|
+
del method, args
|
|
36
|
+
|
|
37
|
+
def check_tx_duration(self, duration: float) -> None:
|
|
38
|
+
del duration
|
|
39
|
+
|
|
40
|
+
def check_handle_call(self, type_id: str, method: str, args: Mapping[str, Any]) -> None:
|
|
41
|
+
del type_id, method, args
|
{remoterf_server-1.0.3 → remoterf_server-1.0.5}/src/remoteRF_server/drivers/usrp/uhd_manifest.py
RENAMED
|
@@ -135,11 +135,6 @@ HANDLE_METHOD_RETURN_TYPES = {
|
|
|
135
135
|
("uhd.property.dboard_iface", "get_desired"): "uhd.usrp.dboard_iface",
|
|
136
136
|
}
|
|
137
137
|
|
|
138
|
-
LOW_LEVEL_WRITES = {
|
|
139
|
-
"set_gpio_attr", "set_gpio_src", "set_user_register",
|
|
140
|
-
"set_rx_filter", "set_tx_filter",
|
|
141
|
-
}
|
|
142
|
-
|
|
143
138
|
USRP2901_UNSUPPORTED_HANDLE_TYPES = {
|
|
144
139
|
"uhd.usrp.radio_control",
|
|
145
140
|
"uhd.usrp.mboard_controller",
|
|
@@ -1305,7 +1300,7 @@ def _objects() -> list[dict]:
|
|
|
1305
1300
|
"uhd.property_tree",
|
|
1306
1301
|
kind="remote_handle",
|
|
1307
1302
|
mutability="remote_handle",
|
|
1308
|
-
ownership={"scope": "session", "default_access": "
|
|
1303
|
+
ownership={"scope": "session", "default_access": "read_write"},
|
|
1309
1304
|
),
|
|
1310
1305
|
*[
|
|
1311
1306
|
object_type(
|
|
@@ -1315,7 +1310,7 @@ def _objects() -> list[dict]:
|
|
|
1315
1310
|
ownership={
|
|
1316
1311
|
"scope": "session",
|
|
1317
1312
|
"parent": "uhd.property_tree",
|
|
1318
|
-
"default_access": "
|
|
1313
|
+
"default_access": "read_write",
|
|
1319
1314
|
},
|
|
1320
1315
|
)
|
|
1321
1316
|
for suffix in (
|
|
@@ -1331,25 +1326,25 @@ def _objects() -> list[dict]:
|
|
|
1331
1326
|
"uhd.usrp.radio_control",
|
|
1332
1327
|
kind="remote_handle",
|
|
1333
1328
|
mutability="remote_handle",
|
|
1334
|
-
ownership={"scope": "session", "default_access": "
|
|
1329
|
+
ownership={"scope": "session", "default_access": "read_write"},
|
|
1335
1330
|
),
|
|
1336
1331
|
object_type(
|
|
1337
1332
|
"uhd.usrp.mboard_controller",
|
|
1338
1333
|
kind="remote_handle",
|
|
1339
1334
|
mutability="remote_handle",
|
|
1340
|
-
ownership={"scope": "session", "default_access": "
|
|
1335
|
+
ownership={"scope": "session", "default_access": "read_write"},
|
|
1341
1336
|
),
|
|
1342
1337
|
object_type(
|
|
1343
1338
|
"uhd.usrp.dboard_iface",
|
|
1344
1339
|
kind="remote_handle",
|
|
1345
1340
|
mutability="remote_handle",
|
|
1346
|
-
ownership={"scope": "session", "default_access": "
|
|
1341
|
+
ownership={"scope": "session", "default_access": "read_write"},
|
|
1347
1342
|
),
|
|
1348
1343
|
object_type(
|
|
1349
1344
|
"uhd.usrp.user_settings_iface",
|
|
1350
1345
|
kind="remote_handle",
|
|
1351
1346
|
mutability="remote_handle",
|
|
1352
|
-
ownership={"scope": "session", "default_access": "
|
|
1347
|
+
ownership={"scope": "session", "default_access": "read_write"},
|
|
1353
1348
|
),
|
|
1354
1349
|
]
|
|
1355
1350
|
|
|
@@ -1415,7 +1410,6 @@ def _streamer_methods() -> list[dict]:
|
|
|
1415
1410
|
)
|
|
1416
1411
|
],
|
|
1417
1412
|
execution="sample_stream",
|
|
1418
|
-
policy="tx_samples",
|
|
1419
1413
|
),
|
|
1420
1414
|
method(
|
|
1421
1415
|
"recv_async_msg", "uhd.usrp.TXStreamer",
|
|
@@ -1514,7 +1508,6 @@ def _property_tree_methods() -> list[dict]:
|
|
|
1514
1508
|
)
|
|
1515
1509
|
],
|
|
1516
1510
|
execution="server_handle",
|
|
1517
|
-
policy="low_level_write",
|
|
1518
1511
|
)
|
|
1519
1512
|
)
|
|
1520
1513
|
return methods
|
|
@@ -1531,13 +1524,13 @@ CAPABILITY_FIELDS = (
|
|
|
1531
1524
|
"tx_bandwidth_ranges_per_channel", "master_clock_rate_range", "time_sources",
|
|
1532
1525
|
"clock_sources", "gpio_banks", "sensors", "supported_cpu_formats",
|
|
1533
1526
|
"supported_otw_formats", "maximum_remote_payload_bytes",
|
|
1534
|
-
"supported_streaming_protocol_versions",
|
|
1527
|
+
"supported_streaming_protocol_versions",
|
|
1535
1528
|
"remote_resource_limits", "advanced_interfaces",
|
|
1536
1529
|
)
|
|
1537
1530
|
|
|
1538
1531
|
ERRORS = (
|
|
1539
1532
|
"RemoteRFTransportError", "RemoteRFSessionExpiredError",
|
|
1540
|
-
"RemoteRFReservationError",
|
|
1533
|
+
"RemoteRFReservationError",
|
|
1541
1534
|
"RemoteRFStaleHandleError", "RemoteRFProtocolError",
|
|
1542
1535
|
"RemoteRFOverloadError",
|
|
1543
1536
|
"RemoteRFServerOverloadedError", "RemoteRFNativeUHDError",
|
|
@@ -1558,15 +1551,6 @@ def build_usrp_schema_v2(driver_version: str = "0.0.1") -> dict:
|
|
|
1558
1551
|
coverage: dict[str, str] = {}
|
|
1559
1552
|
for name in MULTIUSRP_METHODS:
|
|
1560
1553
|
execution = "server_optimized" if name in {"recv_num_samps", "send_waveform"} else "server_control"
|
|
1561
|
-
policy = None
|
|
1562
|
-
if (
|
|
1563
|
-
name.startswith("set_tx_")
|
|
1564
|
-
or name == "set_normalized_tx_gain"
|
|
1565
|
-
or name == "send_waveform"
|
|
1566
|
-
):
|
|
1567
|
-
policy = "tx_control"
|
|
1568
|
-
elif name in LOW_LEVEL_WRITES:
|
|
1569
|
-
policy = "low_level_write"
|
|
1570
1554
|
availability = {"runtime_member": name}
|
|
1571
1555
|
if name in OPTIONAL_METHODS:
|
|
1572
1556
|
availability["optional"] = True
|
|
@@ -1577,7 +1561,6 @@ def build_usrp_schema_v2(driver_version: str = "0.0.1") -> dict:
|
|
|
1577
1561
|
signatures.get(name, _fallback_overload()),
|
|
1578
1562
|
execution=execution,
|
|
1579
1563
|
availability=availability,
|
|
1580
|
-
policy=policy,
|
|
1581
1564
|
)
|
|
1582
1565
|
)
|
|
1583
1566
|
coverage[f"uhd.usrp.MultiUSRP.{name}"] = (
|
{remoterf_server-1.0.3 → remoterf_server-1.0.5}/src/remoteRF_server/drivers/usrp/usrp_schema.py
RENAMED
|
@@ -518,7 +518,7 @@ class UsrpSchema(DeviceSchema):
|
|
|
518
518
|
return str(value)
|
|
519
519
|
return ""
|
|
520
520
|
|
|
521
|
-
def get_capabilities_v2(self
|
|
521
|
+
def get_capabilities_v2(self):
|
|
522
522
|
device = self.device
|
|
523
523
|
if device is None:
|
|
524
524
|
raise RuntimeError("USRP session is not open")
|
|
@@ -615,7 +615,6 @@ class UsrpSchema(DeviceSchema):
|
|
|
615
615
|
},
|
|
616
616
|
"maximum_remote_payload_bytes": 4 * 1024 * 1024,
|
|
617
617
|
"supported_streaming_protocol_versions": ["1.0"],
|
|
618
|
-
"policy_restrictions": dict(policy_restrictions or {}),
|
|
619
618
|
}
|
|
620
619
|
|
|
621
620
|
def _streamers(self):
|
{remoterf_server-1.0.3 → remoterf_server-1.0.5}/src/remoteRF_server/server/device_manager.py
RENAMED
|
@@ -314,6 +314,11 @@ def _load_device_records() -> Dict[int, Dict[str, str]]:
|
|
|
314
314
|
def _connect_from_record(rec: Dict[str, Any]):
|
|
315
315
|
"""Returns (schema_instance_or_None, ident, dtype)."""
|
|
316
316
|
dtype = (rec.get("TYPE") or "").strip().lower()
|
|
317
|
+
# Older serverrf builds serialized the --pluto shorthand as "pluto",
|
|
318
|
+
# while the packaged schema has always registered as "adalm_pluto".
|
|
319
|
+
# Keep those inventories usable while all new writes use the canonical type.
|
|
320
|
+
if dtype == "pluto":
|
|
321
|
+
dtype = "adalm_pluto"
|
|
317
322
|
ident = (rec.get("IDENT") or "").strip()
|
|
318
323
|
init_kwargs = rec.get("INIT") or {}
|
|
319
324
|
|
|
@@ -873,7 +878,7 @@ def acquire_device(api_token: str) -> Iterator[Tuple[int, object]]:
|
|
|
873
878
|
|
|
874
879
|
def reload_devices() -> None:
|
|
875
880
|
"""
|
|
876
|
-
Reload devices.
|
|
881
|
+
Reload devices.yml and reconnect local devices.
|
|
877
882
|
Preserves existing salt/hash for devices that remain (same gid),
|
|
878
883
|
and preserves host virtual devices + their auth state.
|
|
879
884
|
"""
|
|
@@ -23,6 +23,8 @@ from ..common.idl.v2 import (
|
|
|
23
23
|
SCHEMA_VERSION,
|
|
24
24
|
STREAMING_PROTOCOL_VERSION,
|
|
25
25
|
)
|
|
26
|
+
# Retained as a module attribute for integrations that patched the former
|
|
27
|
+
# policy loader. Runtime access is no longer gated by it.
|
|
26
28
|
from ..drivers.usrp.policy import USRPPolicy
|
|
27
29
|
from ..drivers.usrp.uhd_manifest import (
|
|
28
30
|
HANDLE_METHOD_RETURN_TYPES,
|
|
@@ -39,7 +41,6 @@ from .device_manager import (
|
|
|
39
41
|
from .v2_errors import (
|
|
40
42
|
RemoteRFNativeUHDError,
|
|
41
43
|
RemoteRFOverloadError,
|
|
42
|
-
RemoteRFPolicyError,
|
|
43
44
|
RemoteRFProtocolError,
|
|
44
45
|
RemoteRFReservationError,
|
|
45
46
|
RemoteRFServerOverloadedError,
|
|
@@ -466,7 +467,6 @@ class DeviceSession:
|
|
|
466
467
|
gid: int
|
|
467
468
|
schema: Any
|
|
468
469
|
schema_v2: dict
|
|
469
|
-
policy: USRPPolicy
|
|
470
470
|
root_handle: str
|
|
471
471
|
capabilities: dict
|
|
472
472
|
handles: dict[str, HandleEntry] = field(default_factory=dict)
|
|
@@ -569,10 +569,7 @@ class SessionManager:
|
|
|
569
569
|
"the USRP is already open through a legacy v1 session"
|
|
570
570
|
)
|
|
571
571
|
live_schema = create()
|
|
572
|
-
|
|
573
|
-
capabilities = live_schema.get_capabilities_v2(
|
|
574
|
-
policy_restrictions=policy.restrictions
|
|
575
|
-
)
|
|
572
|
+
capabilities = live_schema.get_capabilities_v2()
|
|
576
573
|
capabilities["remote_resource_limits"] = {
|
|
577
574
|
"maximum_sessions": _MAX_SESSIONS,
|
|
578
575
|
"maximum_handles_per_session": _MAX_HANDLES_PER_SESSION,
|
|
@@ -601,7 +598,6 @@ class SessionManager:
|
|
|
601
598
|
gid=gid,
|
|
602
599
|
schema=live_schema,
|
|
603
600
|
schema_v2=schema_v2,
|
|
604
|
-
policy=policy,
|
|
605
601
|
root_handle=root_handle,
|
|
606
602
|
capabilities=capabilities,
|
|
607
603
|
)
|
|
@@ -865,10 +861,6 @@ class SessionManager:
|
|
|
865
861
|
args,
|
|
866
862
|
positional_args,
|
|
867
863
|
)
|
|
868
|
-
policy_args = dict(args)
|
|
869
|
-
if positional_args:
|
|
870
|
-
policy_args["__args__"] = positional_args
|
|
871
|
-
session.policy.check_control(method_name, policy_args)
|
|
872
864
|
native_args = {
|
|
873
865
|
key: self._resolve_handle_argument(session, value)
|
|
874
866
|
for key, value in args.items()
|
|
@@ -884,7 +876,7 @@ class SessionManager:
|
|
|
884
876
|
native_args,
|
|
885
877
|
positional_args=native_positional,
|
|
886
878
|
)
|
|
887
|
-
except
|
|
879
|
+
except RemoteRFProtocolError:
|
|
888
880
|
raise
|
|
889
881
|
except Exception as exc:
|
|
890
882
|
raise RemoteRFNativeUHDError(
|
|
@@ -953,15 +945,6 @@ class SessionManager:
|
|
|
953
945
|
except queue.Empty:
|
|
954
946
|
return (False if mutate_metadata else None), mutations
|
|
955
947
|
|
|
956
|
-
if not entry.type_id.endswith(("RXStreamer", "TXStreamer")):
|
|
957
|
-
policy_args = dict(args)
|
|
958
|
-
if positional_args:
|
|
959
|
-
policy_args["__args__"] = positional_args
|
|
960
|
-
session.policy.check_handle_call(
|
|
961
|
-
entry.type_id,
|
|
962
|
-
method_name,
|
|
963
|
-
policy_args,
|
|
964
|
-
)
|
|
965
948
|
target = getattr(entry.value, method_name, None)
|
|
966
949
|
if not callable(target):
|
|
967
950
|
raise RemoteRFProtocolError(
|
|
@@ -1413,19 +1396,6 @@ class SampleDataV1Servicer(grpc_pb2_grpc.SampleDataV1Servicer):
|
|
|
1413
1396
|
):
|
|
1414
1397
|
if not entry.type_id.endswith("TXStreamer"):
|
|
1415
1398
|
raise RemoteRFStaleHandleError("TX data used a non-TX handle")
|
|
1416
|
-
channels = entry.stream_channels or [0]
|
|
1417
|
-
rates = []
|
|
1418
|
-
get_rate = getattr(session.schema.device, "get_tx_rate", None)
|
|
1419
|
-
if callable(get_rate):
|
|
1420
|
-
for channel in channels:
|
|
1421
|
-
rates.append(float(get_rate(channel)))
|
|
1422
|
-
if not rates or min(rates) <= 0:
|
|
1423
|
-
raise RemoteRFPolicyError(
|
|
1424
|
-
"TX sample rate is unavailable for duty-cycle enforcement"
|
|
1425
|
-
)
|
|
1426
|
-
session.policy.check_tx_duration(
|
|
1427
|
-
float(request.sample_count) / min(rates)
|
|
1428
|
-
)
|
|
1429
1399
|
if len(request.payload) != byte_count:
|
|
1430
1400
|
raise RemoteRFProtocolError(
|
|
1431
1401
|
"TX payload byte length does not match dtype/shape"
|
|
@@ -214,6 +214,18 @@ def _load_server_credentials(cert_dir: Path):
|
|
|
214
214
|
)
|
|
215
215
|
|
|
216
216
|
|
|
217
|
+
def _print_tls_credentials_error(cert_dir: Path, error: Exception) -> None:
|
|
218
|
+
messages = (
|
|
219
|
+
"TLS certs/keys not found or unreadable.",
|
|
220
|
+
f"Expected in: {cert_dir}",
|
|
221
|
+
f"Fix: run serverrf --gen-certs {local_ip} --force",
|
|
222
|
+
"Or set REMOTERF_CERT_DIR to your cert folder.",
|
|
223
|
+
f"Details: {error}",
|
|
224
|
+
)
|
|
225
|
+
for message in messages:
|
|
226
|
+
printf(message, Sty.MAGENTA)
|
|
227
|
+
|
|
228
|
+
|
|
217
229
|
def start_server_runtime(
|
|
218
230
|
*,
|
|
219
231
|
bind_host: str | None = None,
|
|
@@ -258,11 +270,7 @@ def start_server_runtime(
|
|
|
258
270
|
try:
|
|
259
271
|
server_credentials = _load_server_credentials(cert_dir)
|
|
260
272
|
except Exception as e:
|
|
261
|
-
|
|
262
|
-
printf("Expected in: ", Sty.DEFAULT, f"{cert_dir}", Sty.MAGENTA)
|
|
263
|
-
printf("Fix: run ", Sty.DEFAULT, "RRRFcerts --ip ", Sty.GREEN, f"{local_ip}", Sty.MAGENTA, " --force", Sty.DEFAULT)
|
|
264
|
-
printf("Or set REMOTERF_CERT_DIR to your cert folder.", Sty.GRAY)
|
|
265
|
-
print(f"\nDetails: {e}\n", file=sys.stderr)
|
|
273
|
+
_print_tls_credentials_error(cert_dir, e)
|
|
266
274
|
raise SystemExit(2)
|
|
267
275
|
|
|
268
276
|
if include_host_tunnel:
|
|
@@ -135,7 +135,7 @@ def _server_env_path() -> Path:
|
|
|
135
135
|
|
|
136
136
|
|
|
137
137
|
def _devices_env_path() -> Path:
|
|
138
|
-
#
|
|
138
|
+
# Read-only migration source for pre-YAML installations.
|
|
139
139
|
return _cfg_dir() / "devices.env"
|
|
140
140
|
|
|
141
141
|
|
|
@@ -156,11 +156,11 @@ def _gen_certs_path() -> Path:
|
|
|
156
156
|
return (_repo_root() / "tools" / "gen_certs.py").resolve()
|
|
157
157
|
|
|
158
158
|
def _gist_env_path() -> Path:
|
|
159
|
-
# Stored alongside server.env/devices.
|
|
159
|
+
# Stored alongside server.env/devices.yml under ~/.config/remoterf by default
|
|
160
160
|
return _cfg_dir() / "gist.env"
|
|
161
161
|
|
|
162
162
|
def _tos_ack_path() -> Path:
|
|
163
|
-
# Stored alongside server.env/devices.
|
|
163
|
+
# Stored alongside server.env/devices.yml under ~/.config/remoterf by default
|
|
164
164
|
return _cfg_dir() / "tos.env"
|
|
165
165
|
|
|
166
166
|
|
|
@@ -262,6 +262,7 @@ def print_help() -> None:
|
|
|
262
262
|
"\n"
|
|
263
263
|
"Devices:\n"
|
|
264
264
|
" serverrf -d | --device [options]\n"
|
|
265
|
+
" Inventory: ~/.config/remoterf/devices.yml (canonical YAML)\n"
|
|
265
266
|
"\n"
|
|
266
267
|
"Device options:\n"
|
|
267
268
|
" --add --pluto <id:name:iio_serial> Add an ADALM-Pluto\n"
|
|
@@ -811,7 +812,11 @@ def _read_device_records() -> Dict[int, Dict[str, Any]]:
|
|
|
811
812
|
recs[gid] = {
|
|
812
813
|
"TYPE": dtype,
|
|
813
814
|
"NAME": str(item.get("name") or f"device-{gid}").strip(),
|
|
814
|
-
"IDENT_KIND":
|
|
815
|
+
"IDENT_KIND": (
|
|
816
|
+
"iio_serial"
|
|
817
|
+
if dtype in {"pluto", "adalm_pluto"}
|
|
818
|
+
else "serial"
|
|
819
|
+
),
|
|
815
820
|
"IDENT": ident,
|
|
816
821
|
"INIT": dict(init),
|
|
817
822
|
}
|
|
@@ -833,7 +838,6 @@ def _read_device_records() -> Dict[int, Dict[str, Any]]:
|
|
|
833
838
|
|
|
834
839
|
def _write_device_records(recs: Dict[int, Dict[str, Any]]) -> None:
|
|
835
840
|
devices: List[Dict[str, Any]] = []
|
|
836
|
-
out: Dict[str, str] = {}
|
|
837
841
|
for gid in sorted(recs.keys()):
|
|
838
842
|
fields = recs[gid]
|
|
839
843
|
dtype = str(fields.get("TYPE") or "").strip().lower()
|
|
@@ -852,22 +856,14 @@ def _write_device_records(recs: Dict[int, Dict[str, Any]]) -> None:
|
|
|
852
856
|
"init": init,
|
|
853
857
|
}
|
|
854
858
|
)
|
|
855
|
-
order = ["TYPE", "NAME", "IDENT_KIND", "IDENT"]
|
|
856
|
-
for f in order:
|
|
857
|
-
if f in fields and fields[f] != "":
|
|
858
|
-
out[f"DEVICE_{gid}_{f}"] = str(fields[f])
|
|
859
|
-
for f in sorted(fields.keys()):
|
|
860
|
-
if f in order or f == "INIT":
|
|
861
|
-
continue
|
|
862
|
-
if fields[f] != "":
|
|
863
|
-
out[f"DEVICE_{gid}_{f}"] = str(fields[f])
|
|
864
859
|
_cfg_dir().mkdir(parents=True, exist_ok=True)
|
|
865
860
|
_devices_yaml_path().write_text(
|
|
866
861
|
yaml.safe_dump({"devices": devices}, sort_keys=False),
|
|
867
862
|
encoding="utf-8",
|
|
868
863
|
)
|
|
869
|
-
#
|
|
870
|
-
|
|
864
|
+
# Once a legacy inventory has been migrated successfully, remove it so
|
|
865
|
+
# there is exactly one authoritative device manifest.
|
|
866
|
+
_devices_env_path().unlink(missing_ok=True)
|
|
871
867
|
|
|
872
868
|
|
|
873
869
|
def _device_add_pluto(spec: str) -> int:
|
|
@@ -894,14 +890,14 @@ def _device_add_pluto(spec: str) -> int:
|
|
|
894
890
|
return 2
|
|
895
891
|
|
|
896
892
|
recs[gid] = {
|
|
897
|
-
"TYPE": "
|
|
893
|
+
"TYPE": "adalm_pluto",
|
|
898
894
|
"NAME": name,
|
|
899
895
|
"IDENT_KIND": "iio_serial",
|
|
900
896
|
"IDENT": iio_serial,
|
|
901
897
|
}
|
|
902
898
|
_write_device_records(recs)
|
|
903
899
|
|
|
904
|
-
print(f"Added device: {gid} type=
|
|
900
|
+
print(f"Added device: {gid} type=adalm_pluto name={name} iio_serial={iio_serial}")
|
|
905
901
|
return 0
|
|
906
902
|
|
|
907
903
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: remoteRF-server
|
|
3
|
-
Version: 1.0.
|
|
3
|
+
Version: 1.0.5
|
|
4
4
|
Summary: RemoteRF server-side control package
|
|
5
5
|
Requires-Python: >=3.10
|
|
6
6
|
Description-Content-Type: text/markdown
|
|
@@ -329,36 +329,10 @@ devices:
|
|
|
329
329
|
serial: 31A2B3C
|
|
330
330
|
```
|
|
331
331
|
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
```yaml
|
|
338
|
-
defaults:
|
|
339
|
-
tx_enabled: false
|
|
340
|
-
low_level_write_enabled: false
|
|
341
|
-
|
|
342
|
-
devices:
|
|
343
|
-
"31A2B3C":
|
|
344
|
-
tx_enabled: true
|
|
345
|
-
tx_frequency_ranges_hz:
|
|
346
|
-
- [902000000, 928000000]
|
|
347
|
-
max_tx_gain_db: 20
|
|
348
|
-
max_tx_normalized_gain: 0.5
|
|
349
|
-
max_tx_power_dbm: -10
|
|
350
|
-
max_tx_bandwidth_hz: 2000000
|
|
351
|
-
max_tx_sample_rate_sps: 2000000
|
|
352
|
-
allowed_tx_antennas: [TX/RX]
|
|
353
|
-
max_burst_seconds: 1.0
|
|
354
|
-
max_duty_cycle: 0.1
|
|
355
|
-
duty_cycle_window_seconds: 60
|
|
356
|
-
low_level_write_enabled: false
|
|
357
|
-
low_level_write_methods: []
|
|
358
|
-
gpio_write_masks: {}
|
|
359
|
-
property_write_paths: []
|
|
360
|
-
register_write_addresses: []
|
|
361
|
-
```
|
|
332
|
+
All USRP operations exposed by the Dynamic v2 schema are available without a
|
|
333
|
+
separate policy file. This includes transmit controls, transmit streaming,
|
|
334
|
+
GPIO/register methods, and property-tree writes. `usrp_policy.yml` is not read
|
|
335
|
+
or required by the current server.
|
|
362
336
|
|
|
363
337
|
On every qualified target, generate the introspection record and enforce the
|
|
364
338
|
pinned API gate:
|
|
@@ -59,6 +59,30 @@ class CertificateProviderTests(unittest.TestCase):
|
|
|
59
59
|
self.assertEqual(served, ca_pem)
|
|
60
60
|
self.assertNotEqual(served, leaf_pem)
|
|
61
61
|
|
|
62
|
+
def test_missing_tls_guidance_is_visible_and_uses_serverrf(self):
|
|
63
|
+
from remoteRF_server.server import grpc_server
|
|
64
|
+
|
|
65
|
+
cert_dir = Path("/tmp/remoterf-test-certs")
|
|
66
|
+
error = FileNotFoundError("missing server.key")
|
|
67
|
+
with patch.object(grpc_server, "printf") as mock_printf:
|
|
68
|
+
grpc_server._print_tls_credentials_error(cert_dir, error)
|
|
69
|
+
|
|
70
|
+
rendered = [call.args for call in mock_printf.call_args_list]
|
|
71
|
+
self.assertEqual(
|
|
72
|
+
[message for message, _style in rendered],
|
|
73
|
+
[
|
|
74
|
+
"TLS certs/keys not found or unreadable.",
|
|
75
|
+
f"Expected in: {cert_dir}",
|
|
76
|
+
f"Fix: run serverrf --gen-certs {grpc_server.local_ip} --force",
|
|
77
|
+
"Or set REMOTERF_CERT_DIR to your cert folder.",
|
|
78
|
+
"Details: missing server.key",
|
|
79
|
+
],
|
|
80
|
+
)
|
|
81
|
+
self.assertTrue(
|
|
82
|
+
all(style is grpc_server.Sty.MAGENTA for _message, style in rendered)
|
|
83
|
+
)
|
|
84
|
+
self.assertNotIn("RRRFcerts", "\n".join(message for message, _style in rendered))
|
|
85
|
+
|
|
62
86
|
|
|
63
87
|
if __name__ == "__main__":
|
|
64
88
|
unittest.main()
|