remoteRF-server 1.7.22__tar.gz → 1.7.23__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.7.22 → remoterf_server-1.7.23}/PKG-INFO +2 -2
- {remoterf_server-1.7.22 → remoterf_server-1.7.23}/pyproject.toml +3 -2
- remoterf_server-1.7.23/src/remoteRF_server/adapters/__init__.py +0 -0
- remoterf_server-1.7.23/src/remoteRF_server/adapters/oai/__init__.py +115 -0
- remoterf_server-1.7.23/src/remoteRF_server/adapters/oai/integration.json +1 -0
- {remoterf_server-1.7.22 → remoterf_server-1.7.23}/src/remoteRF_server/server/device_manager.py +130 -19
- {remoterf_server-1.7.22 → remoterf_server-1.7.23}/src/remoteRF_server/server/diag.py +12 -0
- {remoterf_server-1.7.22 → remoterf_server-1.7.23}/src/remoteRF_server/server/dynamic_v2.py +411 -75
- {remoterf_server-1.7.22 → remoterf_server-1.7.23}/src/remoteRF_server/server/grpc_server.py +138 -66
- remoterf_server-1.7.23/src/remoteRF_server/server/integration_controller.py +367 -0
- {remoterf_server-1.7.22 → remoterf_server-1.7.23}/src/remoteRF_server/server/rpc_manager.py +8 -0
- {remoterf_server-1.7.22 → remoterf_server-1.7.23}/src/remoteRF_server.egg-info/PKG-INFO +2 -2
- {remoterf_server-1.7.22 → remoterf_server-1.7.23}/src/remoteRF_server.egg-info/SOURCES.txt +11 -0
- {remoterf_server-1.7.22 → remoterf_server-1.7.23}/src/remoteRF_server.egg-info/requires.txt +1 -1
- {remoterf_server-1.7.22 → remoterf_server-1.7.23}/tests/test_default_usrp_schema.py +1 -1
- {remoterf_server-1.7.22 → remoterf_server-1.7.23}/tests/test_dynamic_v2.py +7 -3
- remoterf_server-1.7.23/tests/test_integration_controller.py +664 -0
- remoterf_server-1.7.23/tests/test_integration_ownership.py +121 -0
- remoterf_server-1.7.23/tests/test_integration_routes.py +241 -0
- remoterf_server-1.7.23/tests/test_integration_startup.py +117 -0
- remoterf_server-1.7.23/tests/test_native_lifetime.py +330 -0
- remoterf_server-1.7.23/tests/test_oai_adapter.py +138 -0
- remoterf_server-1.7.23/tests/test_server_signals.py +154 -0
- {remoterf_server-1.7.22 → remoterf_server-1.7.23}/tests/test_startup_check.py +31 -7
- {remoterf_server-1.7.22 → remoterf_server-1.7.23}/tests/test_usrp_client_server_hardware.py +4 -4
- {remoterf_server-1.7.22 → remoterf_server-1.7.23}/tests/test_usrp_client_server_localhost.py +6 -13
- {remoterf_server-1.7.22 → remoterf_server-1.7.23}/tests/test_usrp_n210_client_server_hardware.py +4 -4
- {remoterf_server-1.7.22 → remoterf_server-1.7.23}/LICENSE +0 -0
- {remoterf_server-1.7.22 → remoterf_server-1.7.23}/README.md +0 -0
- {remoterf_server-1.7.22 → remoterf_server-1.7.23}/setup.cfg +0 -0
- {remoterf_server-1.7.22 → remoterf_server-1.7.23}/src/remoteRF_server/__init__.py +0 -0
- {remoterf_server-1.7.22 → remoterf_server-1.7.23}/src/remoteRF_server/common/__init__.py +0 -0
- {remoterf_server-1.7.22 → remoterf_server-1.7.23}/src/remoteRF_server/common/grpc/__init__.py +0 -0
- {remoterf_server-1.7.22 → remoterf_server-1.7.23}/src/remoteRF_server/common/grpc/deployment_capabilities.proto +0 -0
- {remoterf_server-1.7.22 → remoterf_server-1.7.23}/src/remoteRF_server/common/grpc/deployment_capabilities_pb2.py +0 -0
- {remoterf_server-1.7.22 → remoterf_server-1.7.23}/src/remoteRF_server/common/grpc/deployment_capabilities_pb2_grpc.py +0 -0
- {remoterf_server-1.7.22 → remoterf_server-1.7.23}/src/remoteRF_server/common/grpc/federation.proto +0 -0
- {remoterf_server-1.7.22 → remoterf_server-1.7.23}/src/remoteRF_server/common/grpc/federation_pb2.py +0 -0
- {remoterf_server-1.7.22 → remoterf_server-1.7.23}/src/remoteRF_server/common/grpc/federation_pb2_grpc.py +0 -0
- {remoterf_server-1.7.22 → remoterf_server-1.7.23}/src/remoteRF_server/common/grpc/grpc_host_pb2.py +0 -0
- {remoterf_server-1.7.22 → remoterf_server-1.7.23}/src/remoteRF_server/common/grpc/grpc_host_pb2_grpc.py +0 -0
- {remoterf_server-1.7.22 → remoterf_server-1.7.23}/src/remoteRF_server/common/grpc/grpc_pb2.py +0 -0
- {remoterf_server-1.7.22 → remoterf_server-1.7.23}/src/remoteRF_server/common/grpc/grpc_pb2_grpc.py +0 -0
- {remoterf_server-1.7.22 → remoterf_server-1.7.23}/src/remoteRF_server/common/grpc/v2_codec.py +0 -0
- {remoterf_server-1.7.22 → remoterf_server-1.7.23}/src/remoteRF_server/common/idl/__init__.py +0 -0
- {remoterf_server-1.7.22 → remoterf_server-1.7.23}/src/remoteRF_server/common/idl/device_schema.py +0 -0
- {remoterf_server-1.7.22 → remoterf_server-1.7.23}/src/remoteRF_server/common/idl/pluto_mimo_schema.py +0 -0
- {remoterf_server-1.7.22 → remoterf_server-1.7.23}/src/remoteRF_server/common/idl/pluto_schema.py +0 -0
- {remoterf_server-1.7.22 → remoterf_server-1.7.23}/src/remoteRF_server/common/idl/schema.py +0 -0
- {remoterf_server-1.7.22 → remoterf_server-1.7.23}/src/remoteRF_server/common/idl/v2.py +0 -0
- {remoterf_server-1.7.22 → remoterf_server-1.7.23}/src/remoteRF_server/common/utils/__init__.py +0 -0
- {remoterf_server-1.7.22 → remoterf_server-1.7.23}/src/remoteRF_server/common/utils/ansi_codes.py +0 -0
- {remoterf_server-1.7.22 → remoterf_server-1.7.23}/src/remoteRF_server/common/utils/api_token.py +0 -0
- {remoterf_server-1.7.22 → remoterf_server-1.7.23}/src/remoteRF_server/common/utils/db_connection.py +0 -0
- {remoterf_server-1.7.22 → remoterf_server-1.7.23}/src/remoteRF_server/common/utils/db_location.py +0 -0
- {remoterf_server-1.7.22 → remoterf_server-1.7.23}/src/remoteRF_server/common/utils/list_string.py +0 -0
- {remoterf_server-1.7.22 → remoterf_server-1.7.23}/src/remoteRF_server/common/utils/panel.py +0 -0
- {remoterf_server-1.7.22 → remoterf_server-1.7.23}/src/remoteRF_server/common/utils/process_arg.py +0 -0
- {remoterf_server-1.7.22 → remoterf_server-1.7.23}/src/remoteRF_server/drivers/__init__.py +0 -0
- {remoterf_server-1.7.22 → remoterf_server-1.7.23}/src/remoteRF_server/drivers/adalm_pluto/__init__.py +0 -0
- {remoterf_server-1.7.22 → remoterf_server-1.7.23}/src/remoteRF_server/drivers/adalm_pluto/pluto_diag.py +0 -0
- {remoterf_server-1.7.22 → remoterf_server-1.7.23}/src/remoteRF_server/drivers/adalm_pluto/pluto_mimo_schema.py +0 -0
- {remoterf_server-1.7.22 → remoterf_server-1.7.23}/src/remoteRF_server/drivers/adalm_pluto/pluto_remote_server.py +0 -0
- {remoterf_server-1.7.22 → remoterf_server-1.7.23}/src/remoteRF_server/drivers/adalm_pluto/pluto_schema.py +0 -0
- {remoterf_server-1.7.22 → remoterf_server-1.7.23}/src/remoteRF_server/drivers/adalm_pluto/poe.py +0 -0
- {remoterf_server-1.7.22 → remoterf_server-1.7.23}/src/remoteRF_server/drivers/bluetooth/__init__.py +0 -0
- {remoterf_server-1.7.22 → remoterf_server-1.7.23}/src/remoteRF_server/drivers/bluetooth/bluetooth_schema.py +0 -0
- {remoterf_server-1.7.22 → remoterf_server-1.7.23}/src/remoteRF_server/drivers/bluetooth_diag.py +0 -0
- {remoterf_server-1.7.22 → remoterf_server-1.7.23}/src/remoteRF_server/drivers/gps/__init__.py +0 -0
- {remoterf_server-1.7.22 → remoterf_server-1.7.23}/src/remoteRF_server/drivers/gps/gps_schema.py +0 -0
- {remoterf_server-1.7.22 → remoterf_server-1.7.23}/src/remoteRF_server/drivers/gps_diag.py +0 -0
- {remoterf_server-1.7.22 → remoterf_server-1.7.23}/src/remoteRF_server/drivers/hackrf/__init__.py +0 -0
- {remoterf_server-1.7.22 → remoterf_server-1.7.23}/src/remoteRF_server/drivers/hackrf/hackrf_schema.py +0 -0
- {remoterf_server-1.7.22 → remoterf_server-1.7.23}/src/remoteRF_server/drivers/krakensdr/__init__.py +0 -0
- {remoterf_server-1.7.22 → remoterf_server-1.7.23}/src/remoteRF_server/drivers/krakensdr/krakensdr_schema.py +0 -0
- {remoterf_server-1.7.22 → remoterf_server-1.7.23}/src/remoteRF_server/drivers/rfbeam_kmd7/__init__.py +0 -0
- {remoterf_server-1.7.22 → remoterf_server-1.7.23}/src/remoteRF_server/drivers/rfbeam_kmd7/rfbeam_kmd7_schema.py +0 -0
- {remoterf_server-1.7.22 → remoterf_server-1.7.23}/src/remoteRF_server/drivers/rtl_sdr/__init__.py +0 -0
- {remoterf_server-1.7.22 → remoterf_server-1.7.23}/src/remoteRF_server/drivers/rtl_sdr/rtl_sdr_diag.py +0 -0
- {remoterf_server-1.7.22 → remoterf_server-1.7.23}/src/remoteRF_server/drivers/rtl_sdr/rtl_sdr_schema.py +0 -0
- {remoterf_server-1.7.22 → remoterf_server-1.7.23}/src/remoteRF_server/drivers/ti_awr6843/__init__.py +0 -0
- {remoterf_server-1.7.22 → remoterf_server-1.7.23}/src/remoteRF_server/drivers/ti_awr6843/ti_awr6843_schema.py +0 -0
- {remoterf_server-1.7.22 → remoterf_server-1.7.23}/src/remoteRF_server/drivers/ti_mmwave/__init__.py +0 -0
- {remoterf_server-1.7.22 → remoterf_server-1.7.23}/src/remoteRF_server/drivers/ti_mmwave/ti_mmwave_schema.py +0 -0
- {remoterf_server-1.7.22 → remoterf_server-1.7.23}/src/remoteRF_server/drivers/usrp/__init__.py +0 -0
- {remoterf_server-1.7.22 → remoterf_server-1.7.23}/src/remoteRF_server/drivers/usrp/policy.py +0 -0
- {remoterf_server-1.7.22 → remoterf_server-1.7.23}/src/remoteRF_server/drivers/usrp/profiles.py +0 -0
- {remoterf_server-1.7.22 → remoterf_server-1.7.23}/src/remoteRF_server/drivers/usrp/uhd_manifest.py +0 -0
- {remoterf_server-1.7.22 → remoterf_server-1.7.23}/src/remoteRF_server/drivers/usrp/usrp_diag.py +0 -0
- {remoterf_server-1.7.22 → remoterf_server-1.7.23}/src/remoteRF_server/drivers/usrp/usrp_schema.py +0 -0
- {remoterf_server-1.7.22 → remoterf_server-1.7.23}/src/remoteRF_server/drivers/usrp_phaser/__init__.py +0 -0
- {remoterf_server-1.7.22 → remoterf_server-1.7.23}/src/remoteRF_server/drivers/usrp_phaser/usrp_phaser_schema.py +0 -0
- {remoterf_server-1.7.22 → remoterf_server-1.7.23}/src/remoteRF_server/drivers/wifi/__init__.py +0 -0
- {remoterf_server-1.7.22 → remoterf_server-1.7.23}/src/remoteRF_server/drivers/wifi/wifi_schema.py +0 -0
- {remoterf_server-1.7.22 → remoterf_server-1.7.23}/src/remoteRF_server/host/__init__.py +0 -0
- {remoterf_server-1.7.22 → remoterf_server-1.7.23}/src/remoteRF_server/host/host_auth_token.py +0 -0
- {remoterf_server-1.7.22 → remoterf_server-1.7.23}/src/remoteRF_server/host/host_directory_store.py +0 -0
- {remoterf_server-1.7.22 → remoterf_server-1.7.23}/src/remoteRF_server/host/host_tunnel_server.py +0 -0
- {remoterf_server-1.7.22 → remoterf_server-1.7.23}/src/remoteRF_server/remote_admin_shell.py +0 -0
- {remoterf_server-1.7.22 → remoterf_server-1.7.23}/src/remoteRF_server/server/__init__.py +0 -0
- {remoterf_server-1.7.22 → remoterf_server-1.7.23}/src/remoteRF_server/server/acc_perms.py +0 -0
- {remoterf_server-1.7.22 → remoterf_server-1.7.23}/src/remoteRF_server/server/cert_provider.py +0 -0
- {remoterf_server-1.7.22 → remoterf_server-1.7.23}/src/remoteRF_server/server/commands.py +0 -0
- {remoterf_server-1.7.22 → remoterf_server-1.7.23}/src/remoteRF_server/server/console.py +0 -0
- {remoterf_server-1.7.22 → remoterf_server-1.7.23}/src/remoteRF_server/server/control.py +0 -0
- {remoterf_server-1.7.22 → remoterf_server-1.7.23}/src/remoteRF_server/server/deployment/__init__.py +0 -0
- {remoterf_server-1.7.22 → remoterf_server-1.7.23}/src/remoteRF_server/server/deployment/capabilities.py +0 -0
- {remoterf_server-1.7.22 → remoterf_server-1.7.23}/src/remoteRF_server/server/direct_path.py +0 -0
- {remoterf_server-1.7.22 → remoterf_server-1.7.23}/src/remoteRF_server/server/dynamic_v2_bridge.py +0 -0
- {remoterf_server-1.7.22 → remoterf_server-1.7.23}/src/remoteRF_server/server/federation/__init__.py +0 -0
- {remoterf_server-1.7.22 → remoterf_server-1.7.23}/src/remoteRF_server/server/federation/admin.py +0 -0
- {remoterf_server-1.7.22 → remoterf_server-1.7.23}/src/remoteRF_server/server/federation/cli.py +0 -0
- {remoterf_server-1.7.22 → remoterf_server-1.7.23}/src/remoteRF_server/server/federation/cluster.py +0 -0
- {remoterf_server-1.7.22 → remoterf_server-1.7.23}/src/remoteRF_server/server/federation/contracts.py +0 -0
- {remoterf_server-1.7.22 → remoterf_server-1.7.23}/src/remoteRF_server/server/federation/destination.py +0 -0
- {remoterf_server-1.7.22 → remoterf_server-1.7.23}/src/remoteRF_server/server/federation/devices.py +0 -0
- {remoterf_server-1.7.22 → remoterf_server-1.7.23}/src/remoteRF_server/server/federation/enrollment.py +0 -0
- {remoterf_server-1.7.22 → remoterf_server-1.7.23}/src/remoteRF_server/server/federation/global_home.py +0 -0
- {remoterf_server-1.7.22 → remoterf_server-1.7.23}/src/remoteRF_server/server/federation/home.py +0 -0
- {remoterf_server-1.7.22 → remoterf_server-1.7.23}/src/remoteRF_server/server/federation/home_devices.py +0 -0
- {remoterf_server-1.7.22 → remoterf_server-1.7.23}/src/remoteRF_server/server/federation/identity.py +0 -0
- {remoterf_server-1.7.22 → remoterf_server-1.7.23}/src/remoteRF_server/server/federation/legacy_state.py +0 -0
- {remoterf_server-1.7.22 → remoterf_server-1.7.23}/src/remoteRF_server/server/federation/links.py +0 -0
- {remoterf_server-1.7.22 → remoterf_server-1.7.23}/src/remoteRF_server/server/federation/migrations.py +0 -0
- {remoterf_server-1.7.22 → remoterf_server-1.7.23}/src/remoteRF_server/server/federation/peers.py +0 -0
- {remoterf_server-1.7.22 → remoterf_server-1.7.23}/src/remoteRF_server/server/federation/permissions.py +0 -0
- {remoterf_server-1.7.22 → remoterf_server-1.7.23}/src/remoteRF_server/server/federation/principals.py +0 -0
- {remoterf_server-1.7.22 → remoterf_server-1.7.23}/src/remoteRF_server/server/federation/runtime.py +0 -0
- {remoterf_server-1.7.22 → remoterf_server-1.7.23}/src/remoteRF_server/server/federation/trust.py +0 -0
- {remoterf_server-1.7.22 → remoterf_server-1.7.23}/src/remoteRF_server/server/global_onboarding.py +0 -0
- {remoterf_server-1.7.22 → remoterf_server-1.7.23}/src/remoteRF_server/server/host_direct.py +0 -0
- {remoterf_server-1.7.22 → remoterf_server-1.7.23}/src/remoteRF_server/server/host_dynamic_v2.py +0 -0
- {remoterf_server-1.7.22 → remoterf_server-1.7.23}/src/remoteRF_server/server/json_output.py +0 -0
- {remoterf_server-1.7.22 → remoterf_server-1.7.23}/src/remoteRF_server/server/remote_admin.py +0 -0
- {remoterf_server-1.7.22 → remoterf_server-1.7.23}/src/remoteRF_server/server/remote_admin_ssh.py +0 -0
- {remoterf_server-1.7.22 → remoterf_server-1.7.23}/src/remoteRF_server/server/reservation.py +0 -0
- {remoterf_server-1.7.22 → remoterf_server-1.7.23}/src/remoteRF_server/server/schema_registry.py +0 -0
- {remoterf_server-1.7.22 → remoterf_server-1.7.23}/src/remoteRF_server/server/startup_check.py +0 -0
- {remoterf_server-1.7.22 → remoterf_server-1.7.23}/src/remoteRF_server/server/transport_counters.py +0 -0
- {remoterf_server-1.7.22 → remoterf_server-1.7.23}/src/remoteRF_server/server/user_group_cli.py +0 -0
- {remoterf_server-1.7.22 → remoterf_server-1.7.23}/src/remoteRF_server/server/user_group_handler.py +0 -0
- {remoterf_server-1.7.22 → remoterf_server-1.7.23}/src/remoteRF_server/server/v2_errors.py +0 -0
- {remoterf_server-1.7.22 → remoterf_server-1.7.23}/src/remoteRF_server/serverrf_cli.py +0 -0
- {remoterf_server-1.7.22 → remoterf_server-1.7.23}/src/remoteRF_server/tools/__init__.py +0 -0
- {remoterf_server-1.7.22 → remoterf_server-1.7.23}/src/remoteRF_server/tools/gen_certs.py +0 -0
- {remoterf_server-1.7.22 → remoterf_server-1.7.23}/src/remoteRF_server/tools/introspect_uhd.py +0 -0
- {remoterf_server-1.7.22 → remoterf_server-1.7.23}/src/remoteRF_server.egg-info/dependency_links.txt +0 -0
- {remoterf_server-1.7.22 → remoterf_server-1.7.23}/src/remoteRF_server.egg-info/entry_points.txt +0 -0
- {remoterf_server-1.7.22 → remoterf_server-1.7.23}/src/remoteRF_server.egg-info/top_level.txt +0 -0
- {remoterf_server-1.7.22 → remoterf_server-1.7.23}/src/rssi/__init__.py +0 -0
- {remoterf_server-1.7.22 → remoterf_server-1.7.23}/src/rssi/cli.py +0 -0
- {remoterf_server-1.7.22 → remoterf_server-1.7.23}/src/rssi/dongle.py +0 -0
- {remoterf_server-1.7.22 → remoterf_server-1.7.23}/src/rssi/safety.py +0 -0
- {remoterf_server-1.7.22 → remoterf_server-1.7.23}/src/rssi/scanner.py +0 -0
- {remoterf_server-1.7.22 → remoterf_server-1.7.23}/tests/test_account_hardening.py +0 -0
- {remoterf_server-1.7.22 → remoterf_server-1.7.23}/tests/test_account_names.py +0 -0
- {remoterf_server-1.7.22 → remoterf_server-1.7.23}/tests/test_account_removal.py +0 -0
- {remoterf_server-1.7.22 → remoterf_server-1.7.23}/tests/test_activity_log.py +0 -0
- {remoterf_server-1.7.22 → remoterf_server-1.7.23}/tests/test_availability.py +0 -0
- {remoterf_server-1.7.22 → remoterf_server-1.7.23}/tests/test_bind_host.py +0 -0
- {remoterf_server-1.7.22 → remoterf_server-1.7.23}/tests/test_cert_provider.py +0 -0
- {remoterf_server-1.7.22 → remoterf_server-1.7.23}/tests/test_client_sessions.py +0 -0
- {remoterf_server-1.7.22 → remoterf_server-1.7.23}/tests/test_cluster.py +0 -0
- {remoterf_server-1.7.22 → remoterf_server-1.7.23}/tests/test_commands.py +0 -0
- {remoterf_server-1.7.22 → remoterf_server-1.7.23}/tests/test_console.py +0 -0
- {remoterf_server-1.7.22 → remoterf_server-1.7.23}/tests/test_contrib_drivers.py +0 -0
- {remoterf_server-1.7.22 → remoterf_server-1.7.23}/tests/test_default_rtl_sdr_schema.py +0 -0
- {remoterf_server-1.7.22 → remoterf_server-1.7.23}/tests/test_default_wifi_schema.py +0 -0
- {remoterf_server-1.7.22 → remoterf_server-1.7.23}/tests/test_deployment_capabilities.py +0 -0
- {remoterf_server-1.7.22 → remoterf_server-1.7.23}/tests/test_detached_device_rpc.py +0 -0
- {remoterf_server-1.7.22 → remoterf_server-1.7.23}/tests/test_device_core_aliases.py +0 -0
- {remoterf_server-1.7.22 → remoterf_server-1.7.23}/tests/test_device_handles.py +0 -0
- {remoterf_server-1.7.22 → remoterf_server-1.7.23}/tests/test_device_init.py +0 -0
- {remoterf_server-1.7.22 → remoterf_server-1.7.23}/tests/test_devices_add_wizard.py +0 -0
- {remoterf_server-1.7.22 → remoterf_server-1.7.23}/tests/test_devices_list.py +0 -0
- {remoterf_server-1.7.22 → remoterf_server-1.7.23}/tests/test_diag.py +0 -0
- {remoterf_server-1.7.22 → remoterf_server-1.7.23}/tests/test_direct_fixed_port.py +0 -0
- {remoterf_server-1.7.22 → remoterf_server-1.7.23}/tests/test_direct_path_server.py +0 -0
- {remoterf_server-1.7.22 → remoterf_server-1.7.23}/tests/test_federated_direct_path.py +0 -0
- {remoterf_server-1.7.22 → remoterf_server-1.7.23}/tests/test_federated_dynamic_v2.py +0 -0
- {remoterf_server-1.7.22 → remoterf_server-1.7.23}/tests/test_federation_edges.py +0 -0
- {remoterf_server-1.7.22 → remoterf_server-1.7.23}/tests/test_first_run.py +0 -0
- {remoterf_server-1.7.22 → remoterf_server-1.7.23}/tests/test_gate_d_contracts.py +0 -0
- {remoterf_server-1.7.22 → remoterf_server-1.7.23}/tests/test_gate_e_federation.py +0 -0
- {remoterf_server-1.7.22 → remoterf_server-1.7.23}/tests/test_gate_e_proto.py +0 -0
- {remoterf_server-1.7.22 → remoterf_server-1.7.23}/tests/test_gate_f_enrollment.py +0 -0
- {remoterf_server-1.7.22 → remoterf_server-1.7.23}/tests/test_gate_g1_logging.py +0 -0
- {remoterf_server-1.7.22 → remoterf_server-1.7.23}/tests/test_gate_g_permissions.py +0 -0
- {remoterf_server-1.7.22 → remoterf_server-1.7.23}/tests/test_gate_h_devices.py +0 -0
- {remoterf_server-1.7.22 → remoterf_server-1.7.23}/tests/test_gate_i_direct_path.py +0 -0
- {remoterf_server-1.7.22 → remoterf_server-1.7.23}/tests/test_gen_certs.py +0 -0
- {remoterf_server-1.7.22 → remoterf_server-1.7.23}/tests/test_global_accounts_migration.py +0 -0
- {remoterf_server-1.7.22 → remoterf_server-1.7.23}/tests/test_global_home.py +0 -0
- {remoterf_server-1.7.22 → remoterf_server-1.7.23}/tests/test_global_onboarding.py +0 -0
- {remoterf_server-1.7.22 → remoterf_server-1.7.23}/tests/test_global_v1_disposal.py +0 -0
- {remoterf_server-1.7.22 → remoterf_server-1.7.23}/tests/test_hackrf_schema.py +0 -0
- {remoterf_server-1.7.22 → remoterf_server-1.7.23}/tests/test_home_device_plane.py +0 -0
- {remoterf_server-1.7.22 → remoterf_server-1.7.23}/tests/test_host_direct.py +0 -0
- {remoterf_server-1.7.22 → remoterf_server-1.7.23}/tests/test_host_directory_store.py +0 -0
- {remoterf_server-1.7.22 → remoterf_server-1.7.23}/tests/test_host_dynamic_v2.py +0 -0
- {remoterf_server-1.7.22 → remoterf_server-1.7.23}/tests/test_idl_schema.py +0 -0
- {remoterf_server-1.7.22 → remoterf_server-1.7.23}/tests/test_json_commands.py +0 -0
- {remoterf_server-1.7.22 → remoterf_server-1.7.23}/tests/test_lab_logins.py +0 -0
- {remoterf_server-1.7.22 → remoterf_server-1.7.23}/tests/test_native_drivers.py +0 -0
- {remoterf_server-1.7.22 → remoterf_server-1.7.23}/tests/test_panel.py +0 -0
- {remoterf_server-1.7.22 → remoterf_server-1.7.23}/tests/test_peer_transport.py +0 -0
- {remoterf_server-1.7.22 → remoterf_server-1.7.23}/tests/test_pluto_compat.py +0 -0
- {remoterf_server-1.7.22 → remoterf_server-1.7.23}/tests/test_pluto_poe.py +0 -0
- {remoterf_server-1.7.22 → remoterf_server-1.7.23}/tests/test_process_arg.py +0 -0
- {remoterf_server-1.7.22 → remoterf_server-1.7.23}/tests/test_proxy_lifecycle.py +0 -0
- {remoterf_server-1.7.22 → remoterf_server-1.7.23}/tests/test_remote_admin.py +0 -0
- {remoterf_server-1.7.22 → remoterf_server-1.7.23}/tests/test_reservation_cancel.py +0 -0
- {remoterf_server-1.7.22 → remoterf_server-1.7.23}/tests/test_reservation_handoff.py +0 -0
- {remoterf_server-1.7.22 → remoterf_server-1.7.23}/tests/test_reservation_policy_b.py +0 -0
- {remoterf_server-1.7.22 → remoterf_server-1.7.23}/tests/test_reservation_timestamps.py +0 -0
- {remoterf_server-1.7.22 → remoterf_server-1.7.23}/tests/test_rpc_manager_batch.py +0 -0
- {remoterf_server-1.7.22 → remoterf_server-1.7.23}/tests/test_rpc_manager_calln.py +0 -0
- {remoterf_server-1.7.22 → remoterf_server-1.7.23}/tests/test_rpc_manager_host_legacy.py +0 -0
- {remoterf_server-1.7.22 → remoterf_server-1.7.23}/tests/test_rpc_manager_rx_n.py +0 -0
- {remoterf_server-1.7.22 → remoterf_server-1.7.23}/tests/test_rtl_sdr_schema.py +0 -0
- {remoterf_server-1.7.22 → remoterf_server-1.7.23}/tests/test_schema_registry.py +0 -0
- {remoterf_server-1.7.22 → remoterf_server-1.7.23}/tests/test_soak_federation.py +0 -0
- {remoterf_server-1.7.22 → remoterf_server-1.7.23}/tests/test_ti_mmwave_hardware.py +0 -0
- {remoterf_server-1.7.22 → remoterf_server-1.7.23}/tests/test_ti_mmwave_schema.py +0 -0
- {remoterf_server-1.7.22 → remoterf_server-1.7.23}/tests/test_transport_counters.py +0 -0
- {remoterf_server-1.7.22 → remoterf_server-1.7.23}/tests/test_user_group_cli.py +0 -0
- {remoterf_server-1.7.22 → remoterf_server-1.7.23}/tests/test_usrp_b205mini_uhd410_hardware.py +0 -0
- {remoterf_server-1.7.22 → remoterf_server-1.7.23}/tests/test_usrp_preload.py +0 -0
- {remoterf_server-1.7.22 → remoterf_server-1.7.23}/tests/test_usrp_profiles.py +0 -0
- {remoterf_server-1.7.22 → remoterf_server-1.7.23}/tests/test_usrp_remoterf_rx_hardware.py +0 -0
- {remoterf_server-1.7.22 → remoterf_server-1.7.23}/tests/test_usrp_uhd410_hardware.py +0 -0
- {remoterf_server-1.7.22 → remoterf_server-1.7.23}/tests/test_wipe.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: remoteRF-server
|
|
3
|
-
Version: 1.7.
|
|
3
|
+
Version: 1.7.23
|
|
4
4
|
Summary: RemoteRF server-side control package
|
|
5
5
|
License: GPL-3.0-or-later
|
|
6
6
|
Classifier: Programming Language :: Python :: 3
|
|
@@ -10,7 +10,7 @@ Requires-Python: >=3.10
|
|
|
10
10
|
Description-Content-Type: text/markdown
|
|
11
11
|
License-File: LICENSE
|
|
12
12
|
Requires-Dist: remoterf-federation-core==0.22.0
|
|
13
|
-
Requires-Dist: remoterf-device-core==0.1.
|
|
13
|
+
Requires-Dist: remoterf-device-core==0.1.7
|
|
14
14
|
Requires-Dist: grpcio<2.0.0,>=1.81.1
|
|
15
15
|
Requires-Dist: protobuf<7.0.0,>=6.33.5
|
|
16
16
|
Requires-Dist: numpy
|
|
@@ -4,13 +4,13 @@ build-backend = "setuptools.build_meta"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "remoteRF-server"
|
|
7
|
-
version = "1.7.
|
|
7
|
+
version = "1.7.23"
|
|
8
8
|
description = "RemoteRF server-side control package"
|
|
9
9
|
license = { text = "GPL-3.0-or-later" }
|
|
10
10
|
requires-python = ">=3.10"
|
|
11
11
|
dependencies = [
|
|
12
12
|
"remoterf-federation-core==0.22.0",
|
|
13
|
-
"remoterf-device-core==0.1.
|
|
13
|
+
"remoterf-device-core==0.1.7",
|
|
14
14
|
"grpcio>=1.81.1,<2.0.0",
|
|
15
15
|
"protobuf>=6.33.5,<7.0.0",
|
|
16
16
|
"numpy",
|
|
@@ -60,3 +60,4 @@ where = ["src"]
|
|
|
60
60
|
|
|
61
61
|
[tool.setuptools.package-data]
|
|
62
62
|
"remoteRF_server.common.grpc" = ["*.proto"]
|
|
63
|
+
"remoteRF_server.adapters.oai" = ["integration.json"]
|
|
File without changes
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
"""gNB-only OAI scaffold; this adapter TRANSMITS (gNB).
|
|
2
|
+
|
|
3
|
+
The operator-supplied executable and configuration are not managed by this
|
|
4
|
+
adapter; the operator installs and reviews them.
|
|
5
|
+
"""
|
|
6
|
+
from __future__ import annotations
|
|
7
|
+
|
|
8
|
+
import os
|
|
9
|
+
from pathlib import Path
|
|
10
|
+
import re
|
|
11
|
+
import time
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
SERIAL = re.compile(r"[A-Za-z0-9]{1,64}")
|
|
15
|
+
EXTRA_ARGS = ()
|
|
16
|
+
READY_MARKER = "ALL RUs ready"
|
|
17
|
+
STARTUP_TIMEOUT = 25.0
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
def serial_selector(value):
|
|
21
|
+
if type(value) is not dict or set(value) != {"serial"} or type(value["serial"]) is not str:
|
|
22
|
+
raise ValueError("one explicit USRP serial is required")
|
|
23
|
+
if not SERIAL.fullmatch(value["serial"]):
|
|
24
|
+
raise ValueError("invalid USRP serial")
|
|
25
|
+
return value["serial"]
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
def _operator_file(name):
|
|
29
|
+
value = os.environ.get(name)
|
|
30
|
+
path = Path(value) if value is not None else Path()
|
|
31
|
+
if value is None or not path.is_absolute() or not path.is_file():
|
|
32
|
+
raise RuntimeError(f"{name} must name an absolute existing regular file")
|
|
33
|
+
return str(path)
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
class OAI:
|
|
37
|
+
def __init__(self, context):
|
|
38
|
+
self._context = context
|
|
39
|
+
self._process_id = None
|
|
40
|
+
self._ready = False
|
|
41
|
+
self._stopped = False
|
|
42
|
+
|
|
43
|
+
def validate(self, profile, configuration):
|
|
44
|
+
if profile != "gnb" or type(configuration) is not dict or configuration:
|
|
45
|
+
raise ValueError("unsupported gNB configuration")
|
|
46
|
+
return {}
|
|
47
|
+
|
|
48
|
+
def _process(self):
|
|
49
|
+
rows = self._context.processes.snapshot()["processes"]
|
|
50
|
+
row = next((row for row in rows if row["id"] == self._process_id), None)
|
|
51
|
+
if row is None or row["returncode"] is not None:
|
|
52
|
+
raise RuntimeError("OAI gNB process is no longer running")
|
|
53
|
+
return row
|
|
54
|
+
|
|
55
|
+
def start(self, profile, configuration):
|
|
56
|
+
self.validate(profile, configuration)
|
|
57
|
+
serial = serial_selector(self._context.selector)
|
|
58
|
+
processes = self._context.processes
|
|
59
|
+
work_dir = Path(self._context.work_dir)
|
|
60
|
+
if processes is None or not work_dir.is_absolute():
|
|
61
|
+
raise RuntimeError("owned process supervision is required")
|
|
62
|
+
if self._process_id is not None or self._stopped:
|
|
63
|
+
raise RuntimeError("OAI gNB has already started or stopped")
|
|
64
|
+
if self._context.cancelled.is_set():
|
|
65
|
+
raise RuntimeError("OAI gNB was cancelled")
|
|
66
|
+
|
|
67
|
+
softmodem = _operator_file("REMOTERF_OAI_SOFTMODEM")
|
|
68
|
+
conf = _operator_file("REMOTERF_OAI_GNB_CONF")
|
|
69
|
+
env = {"LANG": "C.UTF-8", "LC_ALL": "C.UTF-8"}
|
|
70
|
+
if "UHD_IMAGES_DIR" in os.environ:
|
|
71
|
+
env["UHD_IMAGES_DIR"] = os.environ["UHD_IMAGES_DIR"]
|
|
72
|
+
|
|
73
|
+
# EXTRA_ARGS, READY_MARKER, and sdr_addrs require verification against the pinned OAI build.
|
|
74
|
+
argv = [softmodem, "-O", conf, *EXTRA_ARGS,
|
|
75
|
+
"--RUs.[0].sdr_addrs", f"serial={serial}"]
|
|
76
|
+
self._process_id = processes.launch(
|
|
77
|
+
argv, env=env, cwd=work_dir, required_while_running=True)
|
|
78
|
+
deadline = time.monotonic() + STARTUP_TIMEOUT
|
|
79
|
+
while time.monotonic() < deadline:
|
|
80
|
+
if self._context.cancelled.is_set():
|
|
81
|
+
raise RuntimeError("OAI gNB was cancelled")
|
|
82
|
+
self._process()
|
|
83
|
+
tail = processes.log_tail(self._process_id, "stdout")[-65536:]
|
|
84
|
+
if READY_MARKER in tail.decode("utf-8", errors="replace"):
|
|
85
|
+
self._ready = True
|
|
86
|
+
return
|
|
87
|
+
time.sleep(0.1)
|
|
88
|
+
raise RuntimeError("OAI gNB did not reach readiness")
|
|
89
|
+
|
|
90
|
+
def status(self):
|
|
91
|
+
if self._stopped:
|
|
92
|
+
return {"ready": False, "stopped": True}
|
|
93
|
+
if not self._ready:
|
|
94
|
+
return {"ready": False}
|
|
95
|
+
self._process()
|
|
96
|
+
return {"ready": True}
|
|
97
|
+
|
|
98
|
+
def call(self, action, parameters):
|
|
99
|
+
if action != "health" or type(parameters) is not dict or parameters:
|
|
100
|
+
raise ValueError("health accepts no parameters")
|
|
101
|
+
return self.status()
|
|
102
|
+
|
|
103
|
+
def stop(self):
|
|
104
|
+
if self._stopped:
|
|
105
|
+
return
|
|
106
|
+
if self._context.processes is None:
|
|
107
|
+
self._stopped = True
|
|
108
|
+
return
|
|
109
|
+
if not self._context.processes.stop(timeout=10.0):
|
|
110
|
+
raise RuntimeError("OAI gNB cleanup could not be verified")
|
|
111
|
+
self._stopped = True
|
|
112
|
+
|
|
113
|
+
|
|
114
|
+
def create_adapter(context):
|
|
115
|
+
return OAI(context)
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"id":"org.remoterf.oai","version":"0.1.0","contract_version":"0.1","factory":"remoteRF_server.adapters.oai:create_adapter","device_types":["usrp"],"profiles":["gnb"],"actions":["health"]}
|
{remoterf_server-1.7.22 → remoterf_server-1.7.23}/src/remoteRF_server/server/device_manager.py
RENAMED
|
@@ -43,6 +43,9 @@ from ..host import host_tunnel_server as hts
|
|
|
43
43
|
|
|
44
44
|
logger = logging.getLogger(__name__)
|
|
45
45
|
|
|
46
|
+
from remoterf_device_core.hardware_access import AccessTable
|
|
47
|
+
hardware_access = AccessTable()
|
|
48
|
+
|
|
46
49
|
_state_lock = threading.RLock()
|
|
47
50
|
_reservation_end_callbacks: list = []
|
|
48
51
|
# token -> (gid, salt, hsh): see reservation_bridge_token()
|
|
@@ -926,23 +929,56 @@ def _start_host_reservation(device: VirtualDevice) -> None:
|
|
|
926
929
|
|
|
927
930
|
|
|
928
931
|
def _prepare_for_holder(device_id: int, held: Tuple[str, str]) -> None:
|
|
929
|
-
"""
|
|
930
|
-
|
|
932
|
+
"""Prepare only after verified previous release and for the current holder."""
|
|
933
|
+
from remoterf_device_core.integrations import IntegrationError
|
|
931
934
|
with _state_lock:
|
|
932
935
|
st = _devices.get(int(device_id))
|
|
933
936
|
if st is None:
|
|
934
937
|
return
|
|
938
|
+
retry = lambda: _prepare_for_holder(device_id, held)
|
|
935
939
|
with st.io_lock:
|
|
936
|
-
_reset_locked(st) # a back-to-back predecessor's power-down comes first
|
|
937
940
|
with _state_lock:
|
|
938
941
|
if _devices.get(int(device_id)) is not st or (st.salt, st.hsh) != held:
|
|
939
|
-
return
|
|
942
|
+
return
|
|
943
|
+
if _native_cleanup_pending(device_id):
|
|
944
|
+
_after_native_cleanup(device_id, "prepare", retry)
|
|
945
|
+
return
|
|
940
946
|
try:
|
|
941
|
-
|
|
947
|
+
with hardware_access.native(device_id):
|
|
948
|
+
_reset_locked(st)
|
|
949
|
+
st.dev.prepare_for_holder()
|
|
950
|
+
except IntegrationError as exc:
|
|
951
|
+
if exc.code != "device_owned":
|
|
952
|
+
raise
|
|
953
|
+
_after_native_cleanup(device_id, "prepare", retry)
|
|
942
954
|
except Exception as exc:
|
|
943
955
|
print(f"Warning: preparing {st.name or st.ident} for its reservation failed: {exc}")
|
|
944
956
|
|
|
945
957
|
|
|
958
|
+
_deferred_native_hooks = {}
|
|
959
|
+
|
|
960
|
+
|
|
961
|
+
def _after_native_cleanup(gid, kind, action):
|
|
962
|
+
from remoterf_device_core.native_lifetime import submit_cleanup
|
|
963
|
+
key = (int(gid), kind)
|
|
964
|
+
with _state_lock:
|
|
965
|
+
already = key in _deferred_native_hooks
|
|
966
|
+
_deferred_native_hooks[key] = action
|
|
967
|
+
if already:
|
|
968
|
+
return
|
|
969
|
+
def run():
|
|
970
|
+
with _state_lock:
|
|
971
|
+
latest = _deferred_native_hooks.pop(key)
|
|
972
|
+
latest()
|
|
973
|
+
submit_cleanup(lambda: not _native_cleanup_pending(gid), run)
|
|
974
|
+
|
|
975
|
+
|
|
976
|
+
def _native_cleanup_pending(gid):
|
|
977
|
+
# Import lazily: device inventory is loaded before the session runtime.
|
|
978
|
+
from .dynamic_v2 import session_manager
|
|
979
|
+
return session_manager.cleanup_pending(gid) or hardware_access.blocked(gid)
|
|
980
|
+
|
|
981
|
+
|
|
946
982
|
def _reset_locked(st: _DeviceState) -> None:
|
|
947
983
|
"""Under st.io_lock: drop what the last reservation left on the device
|
|
948
984
|
(buffers, streamers, a running transmitter) before anyone else drives it."""
|
|
@@ -958,11 +994,26 @@ def _reset_locked(st: _DeviceState) -> None:
|
|
|
958
994
|
|
|
959
995
|
|
|
960
996
|
def _reset_if_pending(device_id: int) -> None:
|
|
997
|
+
from remoterf_device_core.integrations import IntegrationError
|
|
961
998
|
with _state_lock:
|
|
962
999
|
st = _devices.get(int(device_id))
|
|
963
|
-
if st is
|
|
964
|
-
|
|
965
|
-
|
|
1000
|
+
if st is None:
|
|
1001
|
+
return
|
|
1002
|
+
retry = lambda: _reset_if_pending(device_id)
|
|
1003
|
+
with st.io_lock:
|
|
1004
|
+
with _state_lock:
|
|
1005
|
+
if _devices.get(int(device_id)) is not st:
|
|
1006
|
+
return
|
|
1007
|
+
if _native_cleanup_pending(device_id):
|
|
1008
|
+
_after_native_cleanup(device_id, "reset", retry)
|
|
1009
|
+
return
|
|
1010
|
+
try:
|
|
1011
|
+
with hardware_access.native(device_id):
|
|
1012
|
+
_reset_locked(st)
|
|
1013
|
+
except IntegrationError as exc:
|
|
1014
|
+
if exc.code != "device_owned":
|
|
1015
|
+
raise
|
|
1016
|
+
_after_native_cleanup(device_id, "reset", retry)
|
|
966
1017
|
|
|
967
1018
|
|
|
968
1019
|
def reservation_bridge_token(device_id: int, expect=None) -> str:
|
|
@@ -1013,6 +1064,8 @@ def get_local_device_context(device_id: int) -> dict[str, Any] | None:
|
|
|
1013
1064
|
"ident": st.ident,
|
|
1014
1065
|
"name": st.name,
|
|
1015
1066
|
"reserved": bool(st.salt or st.hsh),
|
|
1067
|
+
"io_lock": st.io_lock,
|
|
1068
|
+
"prepare": lambda: _reset_if_pending(int(device_id)),
|
|
1016
1069
|
}
|
|
1017
1070
|
|
|
1018
1071
|
|
|
@@ -1086,7 +1139,8 @@ def device_is_available(device_id: int) -> bool:
|
|
|
1086
1139
|
st = _devices.get(int(device_id))
|
|
1087
1140
|
if not st or not _is_connected(st):
|
|
1088
1141
|
return False
|
|
1089
|
-
|
|
1142
|
+
available = (st.salt == "") and (st.hsh == "")
|
|
1143
|
+
return available and not hardware_access.blocked(device_id) and not _native_cleanup_pending(device_id)
|
|
1090
1144
|
|
|
1091
1145
|
def get_device_by_id(device_id: int):
|
|
1092
1146
|
_sync_host_devices()
|
|
@@ -1370,8 +1424,11 @@ def acquire_device(api_token: str) -> Iterator[Tuple[int, object]]:
|
|
|
1370
1424
|
|
|
1371
1425
|
lock.acquire()
|
|
1372
1426
|
try:
|
|
1373
|
-
|
|
1374
|
-
|
|
1427
|
+
with hardware_access.native(gid):
|
|
1428
|
+
if _native_cleanup_pending(gid):
|
|
1429
|
+
raise RuntimeError("previous native cleanup is still in progress")
|
|
1430
|
+
_reset_locked(st)
|
|
1431
|
+
yield int(gid), dev
|
|
1375
1432
|
finally:
|
|
1376
1433
|
lock.release()
|
|
1377
1434
|
|
|
@@ -1398,8 +1455,11 @@ def acquire_device_by_gid(gid: int) -> Iterator[Tuple[int, object]]:
|
|
|
1398
1455
|
|
|
1399
1456
|
lock.acquire()
|
|
1400
1457
|
try:
|
|
1401
|
-
|
|
1402
|
-
|
|
1458
|
+
with hardware_access.native(gid):
|
|
1459
|
+
if _native_cleanup_pending(gid):
|
|
1460
|
+
raise RuntimeError("previous native cleanup is still in progress")
|
|
1461
|
+
_reset_locked(st)
|
|
1462
|
+
yield int(gid), dev
|
|
1403
1463
|
finally:
|
|
1404
1464
|
lock.release()
|
|
1405
1465
|
|
|
@@ -1476,6 +1536,19 @@ _reload_lock = threading.Lock()
|
|
|
1476
1536
|
|
|
1477
1537
|
|
|
1478
1538
|
def _reload_devices_locked(on_device=None) -> None:
|
|
1539
|
+
from .dynamic_v2 import session_manager
|
|
1540
|
+
session_manager.integrations.validate_inventory(_load_device_records())
|
|
1541
|
+
with _state_lock:
|
|
1542
|
+
gids = [gid for gid, st in _devices.items() if st.origin == "local" and st.dev is not None]
|
|
1543
|
+
with hardware_access.maintenance(gids):
|
|
1544
|
+
for gid in gids:
|
|
1545
|
+
session_manager.close_gid(gid)
|
|
1546
|
+
if any(session_manager.cleanup_pending(gid) for gid in gids):
|
|
1547
|
+
raise RuntimeError("native cleanup is still in progress; retry reload after it finishes")
|
|
1548
|
+
_reload_devices_unfenced(on_device)
|
|
1549
|
+
|
|
1550
|
+
|
|
1551
|
+
def _reload_devices_unfenced(on_device=None) -> None:
|
|
1479
1552
|
# ponytail: reconnects every local device, not just the changed one;
|
|
1480
1553
|
# diff against the current table if a reload mid-session ever hurts.
|
|
1481
1554
|
records = _load_device_records()
|
|
@@ -1510,7 +1583,10 @@ def _reload_devices_locked(on_device=None) -> None:
|
|
|
1510
1583
|
|
|
1511
1584
|
if on_device is not None:
|
|
1512
1585
|
on_device(int(gid), name)
|
|
1513
|
-
|
|
1586
|
+
if hardware_access.gate(gid).snapshot()['owner'] is not None:
|
|
1587
|
+
dev, ident2, dtype2 = _connect_from_record(rec, connect=False)
|
|
1588
|
+
else:
|
|
1589
|
+
dev, ident2, dtype2 = _connect_from_record(rec)
|
|
1514
1590
|
|
|
1515
1591
|
tmp_info[int(gid)] = name
|
|
1516
1592
|
if ident:
|
|
@@ -1573,14 +1649,49 @@ def _close_schema(schema) -> bool:
|
|
|
1573
1649
|
|
|
1574
1650
|
|
|
1575
1651
|
def close_local_devices() -> int:
|
|
1576
|
-
"""
|
|
1652
|
+
"""Request inventory cleanup, returning the number completed within 0.5s.
|
|
1653
|
+
|
|
1654
|
+
A stuck driver must not prevent attempts for other devices or close an
|
|
1655
|
+
inventory handle while a native call/application still owns the hardware.
|
|
1656
|
+
"""
|
|
1657
|
+
from remoterf_device_core.native_lifetime import submit_cleanup
|
|
1658
|
+
from remoterf_device_core.integrations import IntegrationError
|
|
1577
1659
|
with _state_lock:
|
|
1578
|
-
|
|
1579
|
-
state
|
|
1580
|
-
for state in _devices.
|
|
1660
|
+
states = [
|
|
1661
|
+
(gid, state)
|
|
1662
|
+
for gid, state in _devices.items()
|
|
1581
1663
|
if state.origin == "local" and state.dev is not None
|
|
1582
1664
|
]
|
|
1583
|
-
|
|
1665
|
+
completed = []
|
|
1666
|
+
jobs = []
|
|
1667
|
+
def close(gid, state):
|
|
1668
|
+
try:
|
|
1669
|
+
with state.io_lock:
|
|
1670
|
+
if _native_cleanup_pending(gid):
|
|
1671
|
+
_after_native_cleanup(gid, "shutdown", lambda: close(gid, state))
|
|
1672
|
+
return
|
|
1673
|
+
with hardware_access.gate(gid).maintenance():
|
|
1674
|
+
completed.append(_close_schema(state.dev))
|
|
1675
|
+
except IntegrationError:
|
|
1676
|
+
# Admission changed between readiness and execution. Retain
|
|
1677
|
+
# this schema and retry only after the new owner has drained.
|
|
1678
|
+
_after_native_cleanup(gid, "shutdown", lambda: close(gid, state))
|
|
1679
|
+
|
|
1680
|
+
for gid, state in states:
|
|
1681
|
+
if getattr(state.dev, "device", None) is None:
|
|
1682
|
+
continue
|
|
1683
|
+
def schedule(gid=gid, state=state):
|
|
1684
|
+
return submit_cleanup(lambda: not _native_cleanup_pending(gid)
|
|
1685
|
+
and not hardware_access.gate(gid).snapshot()["active_native"],
|
|
1686
|
+
lambda: close(gid, state))
|
|
1687
|
+
try:
|
|
1688
|
+
jobs.append(schedule())
|
|
1689
|
+
except Exception:
|
|
1690
|
+
logger.exception("could not schedule inventory cleanup for device %s", gid)
|
|
1691
|
+
deadline = time.monotonic() + 0.5
|
|
1692
|
+
for job in jobs:
|
|
1693
|
+
job.done.wait(max(0.0, deadline - time.monotonic()))
|
|
1694
|
+
return sum(completed)
|
|
1584
1695
|
|
|
1585
1696
|
def set_pluto(ip: str = "192.168.2.1"):
|
|
1586
1697
|
# kept for compatibility (no-op)
|
|
@@ -162,6 +162,13 @@ def _run_one(row: dict, init: dict, display: _Display, log_fd: int, left_after:
|
|
|
162
162
|
typical = [_typical(t[1], st.dev) for t in mod.TESTS] + ([restore_s[0]] if teardown else [])
|
|
163
163
|
started_at = time.monotonic()
|
|
164
164
|
ran = False
|
|
165
|
+
access = []
|
|
166
|
+
access_lock = threading.Lock()
|
|
167
|
+
|
|
168
|
+
def release_access():
|
|
169
|
+
with access_lock:
|
|
170
|
+
if access:
|
|
171
|
+
access.pop().__exit__(None, None, None)
|
|
165
172
|
|
|
166
173
|
def step(i: int, name: str, timeout: float, fn, step_ctx: dict) -> bool:
|
|
167
174
|
"""Run one test; True when it timed out."""
|
|
@@ -204,6 +211,9 @@ def _run_one(row: dict, init: dict, display: _Display, log_fd: int, left_after:
|
|
|
204
211
|
if reason:
|
|
205
212
|
row["status"] = f"skipped: {reason}"
|
|
206
213
|
return
|
|
214
|
+
ticket = dm.hardware_access.native(gid)
|
|
215
|
+
ticket.__enter__()
|
|
216
|
+
access.append(ticket)
|
|
207
217
|
ran = True
|
|
208
218
|
dm._reset_locked(st)
|
|
209
219
|
halted = None
|
|
@@ -232,6 +242,7 @@ def _run_one(row: dict, init: dict, display: _Display, log_fd: int, left_after:
|
|
|
232
242
|
|
|
233
243
|
def _release(_f, fs=wedged):
|
|
234
244
|
if all(f.done() for f in fs):
|
|
245
|
+
release_access()
|
|
235
246
|
dm.diag_release(gid)
|
|
236
247
|
|
|
237
248
|
for f in wedged:
|
|
@@ -239,6 +250,7 @@ def _run_one(row: dict, init: dict, display: _Display, log_fd: int, left_after:
|
|
|
239
250
|
row["tests"].append({"name": "release", "status": "fail",
|
|
240
251
|
"detail": "a test is still running; clients stay refused until it ends"})
|
|
241
252
|
else:
|
|
253
|
+
release_access()
|
|
242
254
|
dm.diag_release(gid)
|
|
243
255
|
st.io_lock.release()
|
|
244
256
|
_flush_all()
|