remoteRF-server 0.1.94__tar.gz → 0.1.96__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-0.1.94 → remoterf_server-0.1.96}/PKG-INFO +2 -2
- {remoterf_server-0.1.94 → remoterf_server-0.1.96}/README.md +1 -1
- {remoterf_server-0.1.94 → remoterf_server-0.1.96}/pyproject.toml +1 -1
- {remoterf_server-0.1.94 → remoterf_server-0.1.96}/src/remoteRF_server/server/device_manager.py +292 -131
- {remoterf_server-0.1.94 → remoterf_server-0.1.96}/src/remoteRF_server/server/grpc_server.py +4 -4
- {remoterf_server-0.1.94 → remoterf_server-0.1.96}/src/remoteRF_server.egg-info/PKG-INFO +2 -2
- {remoterf_server-0.1.94 → remoterf_server-0.1.96}/setup.cfg +0 -0
- {remoterf_server-0.1.94 → remoterf_server-0.1.96}/src/remoteRF_server/__init__.py +0 -0
- {remoterf_server-0.1.94 → remoterf_server-0.1.96}/src/remoteRF_server/common/__init__.py +0 -0
- {remoterf_server-0.1.94 → remoterf_server-0.1.96}/src/remoteRF_server/common/grpc/__init__.py +0 -0
- {remoterf_server-0.1.94 → remoterf_server-0.1.96}/src/remoteRF_server/common/grpc/grpc_host_pb2.py +0 -0
- {remoterf_server-0.1.94 → remoterf_server-0.1.96}/src/remoteRF_server/common/grpc/grpc_host_pb2_grpc.py +0 -0
- {remoterf_server-0.1.94 → remoterf_server-0.1.96}/src/remoteRF_server/common/grpc/grpc_pb2.py +0 -0
- {remoterf_server-0.1.94 → remoterf_server-0.1.96}/src/remoteRF_server/common/grpc/grpc_pb2_grpc.py +0 -0
- {remoterf_server-0.1.94 → remoterf_server-0.1.96}/src/remoteRF_server/common/utils/__init__.py +0 -0
- {remoterf_server-0.1.94 → remoterf_server-0.1.96}/src/remoteRF_server/common/utils/ansi_codes.py +0 -0
- {remoterf_server-0.1.94 → remoterf_server-0.1.96}/src/remoteRF_server/common/utils/api_token.py +0 -0
- {remoterf_server-0.1.94 → remoterf_server-0.1.96}/src/remoteRF_server/common/utils/db_connection.py +0 -0
- {remoterf_server-0.1.94 → remoterf_server-0.1.96}/src/remoteRF_server/common/utils/db_location.py +0 -0
- {remoterf_server-0.1.94 → remoterf_server-0.1.96}/src/remoteRF_server/common/utils/list_string.py +0 -0
- {remoterf_server-0.1.94 → remoterf_server-0.1.96}/src/remoteRF_server/common/utils/process_arg.py +0 -0
- {remoterf_server-0.1.94 → remoterf_server-0.1.96}/src/remoteRF_server/drivers/__init__.py +0 -0
- {remoterf_server-0.1.94 → remoterf_server-0.1.96}/src/remoteRF_server/drivers/adalm_pluto/__init__.py +0 -0
- {remoterf_server-0.1.94 → remoterf_server-0.1.96}/src/remoteRF_server/drivers/adalm_pluto/pluto_remote_server.py +0 -0
- {remoterf_server-0.1.94 → remoterf_server-0.1.96}/src/remoteRF_server/host/__init__.py +0 -0
- {remoterf_server-0.1.94 → remoterf_server-0.1.96}/src/remoteRF_server/host/host_directory_store.py +0 -0
- {remoterf_server-0.1.94 → remoterf_server-0.1.96}/src/remoteRF_server/host/host_tunnel_server.py +0 -0
- {remoterf_server-0.1.94 → remoterf_server-0.1.96}/src/remoteRF_server/server/__init__.py +0 -0
- {remoterf_server-0.1.94 → remoterf_server-0.1.96}/src/remoteRF_server/server/acc_perms.py +0 -0
- {remoterf_server-0.1.94 → remoterf_server-0.1.96}/src/remoteRF_server/server/cert_provider.py +0 -0
- {remoterf_server-0.1.94 → remoterf_server-0.1.96}/src/remoteRF_server/server/reservation.py +0 -0
- {remoterf_server-0.1.94 → remoterf_server-0.1.96}/src/remoteRF_server/server/rpc_manager.py +0 -0
- {remoterf_server-0.1.94 → remoterf_server-0.1.96}/src/remoteRF_server/server/user_group_cli.py +0 -0
- {remoterf_server-0.1.94 → remoterf_server-0.1.96}/src/remoteRF_server/server/user_group_handler.py +0 -0
- {remoterf_server-0.1.94 → remoterf_server-0.1.96}/src/remoteRF_server/serverrf_cli.py +0 -0
- {remoterf_server-0.1.94 → remoterf_server-0.1.96}/src/remoteRF_server/tools/__init__.py +0 -0
- {remoterf_server-0.1.94 → remoterf_server-0.1.96}/src/remoteRF_server/tools/gen_certs.py +0 -0
- {remoterf_server-0.1.94 → remoterf_server-0.1.96}/src/remoteRF_server.egg-info/SOURCES.txt +0 -0
- {remoterf_server-0.1.94 → remoterf_server-0.1.96}/src/remoteRF_server.egg-info/dependency_links.txt +0 -0
- {remoterf_server-0.1.94 → remoterf_server-0.1.96}/src/remoteRF_server.egg-info/entry_points.txt +0 -0
- {remoterf_server-0.1.94 → remoterf_server-0.1.96}/src/remoteRF_server.egg-info/requires.txt +0 -0
- {remoterf_server-0.1.94 → remoterf_server-0.1.96}/src/remoteRF_server.egg-info/top_level.txt +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: remoteRF-server
|
|
3
|
-
Version: 0.1.
|
|
3
|
+
Version: 0.1.96
|
|
4
4
|
Description-Content-Type: text/markdown
|
|
5
5
|
Requires-Dist: grpcio
|
|
6
6
|
Requires-Dist: protobuf
|
|
@@ -350,7 +350,7 @@ todo:
|
|
|
350
350
|
|
|
351
351
|
<!-- Host ID (NEEDS TO BE UNIQUE AND TELL THE HOST THAT) (or if device also does it as well.) -->
|
|
352
352
|
|
|
353
|
-
ServerRF display meta data as part of devices list / reservation
|
|
353
|
+
<!-- ServerRF display meta data as part of devices list / reservation -->
|
|
354
354
|
|
|
355
355
|
+ online/activity status (appended into device name) + (plus host side warning if the device id is NOT globally unique!)
|
|
356
356
|
|
|
@@ -339,7 +339,7 @@ todo:
|
|
|
339
339
|
|
|
340
340
|
<!-- Host ID (NEEDS TO BE UNIQUE AND TELL THE HOST THAT) (or if device also does it as well.) -->
|
|
341
341
|
|
|
342
|
-
ServerRF display meta data as part of devices list / reservation
|
|
342
|
+
<!-- ServerRF display meta data as part of devices list / reservation -->
|
|
343
343
|
|
|
344
344
|
+ online/activity status (appended into device name) + (plus host side warning if the device id is NOT globally unique!)
|
|
345
345
|
|
{remoterf_server-0.1.94 → remoterf_server-0.1.96}/src/remoteRF_server/server/device_manager.py
RENAMED
|
@@ -2,47 +2,188 @@
|
|
|
2
2
|
|
|
3
3
|
from __future__ import annotations
|
|
4
4
|
|
|
5
|
-
import hashlib
|
|
6
|
-
from typing import Dict, Tuple, Optional, Any
|
|
7
|
-
|
|
8
5
|
import os
|
|
9
6
|
import re
|
|
7
|
+
import time
|
|
8
|
+
import adi
|
|
10
9
|
import subprocess
|
|
11
10
|
import threading
|
|
12
|
-
from dataclasses import dataclass
|
|
11
|
+
from dataclasses import dataclass, field
|
|
13
12
|
from pathlib import Path
|
|
14
|
-
from typing import Dict, Optional,
|
|
13
|
+
from typing import Dict, Tuple, Optional, Any, List, Iterator
|
|
15
14
|
from contextlib import contextmanager
|
|
16
15
|
|
|
17
|
-
import adi
|
|
18
|
-
|
|
19
16
|
from ..common.utils import validate_token # your salt/hash validator
|
|
20
17
|
from ..host import host_tunnel_server as hts # gives access to hts.TUNNEL_REGISTRY
|
|
21
18
|
|
|
22
|
-
import time # add near imports
|
|
23
|
-
|
|
24
19
|
_state_lock = threading.RLock()
|
|
25
20
|
|
|
21
|
+
|
|
22
|
+
# ============================================================
|
|
23
|
+
# Virtual device (server-side proxy for host-managed hardware)
|
|
24
|
+
# ============================================================
|
|
25
|
+
|
|
26
|
+
@dataclass(frozen=True)
|
|
27
|
+
class VirtualDevice:
|
|
28
|
+
"""
|
|
29
|
+
Server-side proxy for a host-managed device.
|
|
30
|
+
|
|
31
|
+
IMPORTANT:
|
|
32
|
+
- This is NOT an adi device.
|
|
33
|
+
- It only carries routing metadata so your pipeline can forward later.
|
|
34
|
+
"""
|
|
35
|
+
gid: int
|
|
36
|
+
host_id: str
|
|
37
|
+
device_id: str # whatever key the host directory uses (stringified)
|
|
38
|
+
label: str = ""
|
|
39
|
+
serial: str = ""
|
|
40
|
+
kind: str = ""
|
|
41
|
+
|
|
42
|
+
def __repr__(self) -> str:
|
|
43
|
+
return f"<VirtualDevice gid={self.gid} host={self.host_id} device_id={self.device_id}>"
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
def is_virtual(dev: object) -> bool:
|
|
47
|
+
return isinstance(dev, VirtualDevice)
|
|
48
|
+
|
|
49
|
+
|
|
26
50
|
@dataclass
|
|
27
51
|
class _DeviceState:
|
|
28
|
-
dev: object
|
|
52
|
+
dev: object | None
|
|
53
|
+
origin: str # "local" or "host"
|
|
54
|
+
online: bool # for host devices; for local can mirror (dev is not None)
|
|
29
55
|
salt: str
|
|
30
56
|
hsh: str
|
|
31
57
|
name: str
|
|
32
58
|
ident: str
|
|
33
59
|
dtype: str
|
|
34
|
-
io_lock: threading.RLock
|
|
60
|
+
io_lock: threading.RLock = field(default_factory=threading.RLock)
|
|
61
|
+
|
|
35
62
|
|
|
36
63
|
# gid -> state
|
|
37
64
|
_devices: Dict[int, _DeviceState] = {}
|
|
38
65
|
|
|
39
|
-
|
|
66
|
+
|
|
67
|
+
def _is_connected(st: _DeviceState) -> bool:
|
|
68
|
+
if st.origin == "local":
|
|
69
|
+
return st.dev is not None
|
|
70
|
+
# host
|
|
71
|
+
return bool(st.online)
|
|
72
|
+
|
|
73
|
+
|
|
74
|
+
# ============================================================
|
|
40
75
|
# Host device pulling/grabbing
|
|
41
|
-
#
|
|
76
|
+
# ============================================================
|
|
42
77
|
|
|
43
78
|
def _get_host_registry():
|
|
44
79
|
return hts.get_tunnel_registry()
|
|
45
80
|
|
|
81
|
+
|
|
82
|
+
_host_sync_lock = threading.RLock()
|
|
83
|
+
_host_sync_last_ms = 0
|
|
84
|
+
_HOST_SYNC_TTL_MS = 500 # tune later
|
|
85
|
+
|
|
86
|
+
|
|
87
|
+
def _sync_host_devices(*, force: bool = False) -> None:
|
|
88
|
+
"""
|
|
89
|
+
Pull host directory and upsert host devices into _devices using their GID.
|
|
90
|
+
|
|
91
|
+
- Does NOT touch local devices (origin="local")
|
|
92
|
+
- Preserves salt/hsh/io_lock for existing entries
|
|
93
|
+
- Marks missing host devices offline (keeps their proxy for reservations)
|
|
94
|
+
- Updates devices_info/device_serialization for host devices (best-effort)
|
|
95
|
+
"""
|
|
96
|
+
global _host_sync_last_ms
|
|
97
|
+
|
|
98
|
+
now_ms = int(time.time() * 1000)
|
|
99
|
+
with _host_sync_lock:
|
|
100
|
+
if not force and (now_ms - _host_sync_last_ms) < _HOST_SYNC_TTL_MS:
|
|
101
|
+
return
|
|
102
|
+
_host_sync_last_ms = now_ms
|
|
103
|
+
|
|
104
|
+
reg = _get_host_registry()
|
|
105
|
+
if reg is None:
|
|
106
|
+
return
|
|
107
|
+
|
|
108
|
+
# Do registry calls without holding _state_lock
|
|
109
|
+
snap = reg.device_directory_cached(ttl_ms=0) # device_id -> (host_id, info_obj, is_active)
|
|
110
|
+
|
|
111
|
+
updates: Dict[int, Dict[str, object]] = {}
|
|
112
|
+
for device_id, (host_id, info_obj, is_active) in snap.items():
|
|
113
|
+
# You said device_id is assumed to be the GID
|
|
114
|
+
try:
|
|
115
|
+
gid = int(str(device_id))
|
|
116
|
+
except Exception:
|
|
117
|
+
continue
|
|
118
|
+
|
|
119
|
+
label = str(getattr(info_obj, "label", "") or "").strip()
|
|
120
|
+
serial = str(getattr(info_obj, "serial", "") or "").strip()
|
|
121
|
+
kind = str(getattr(info_obj, "kind", "") or "").strip()
|
|
122
|
+
|
|
123
|
+
updates[gid] = {
|
|
124
|
+
"host_id": str(host_id),
|
|
125
|
+
"device_id": str(device_id),
|
|
126
|
+
"label": label,
|
|
127
|
+
"serial": serial,
|
|
128
|
+
"kind": kind,
|
|
129
|
+
"online": bool(is_active),
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
with _state_lock:
|
|
133
|
+
# Upsert / refresh host entries
|
|
134
|
+
for gid, u in updates.items():
|
|
135
|
+
prev = _devices.get(int(gid))
|
|
136
|
+
|
|
137
|
+
# Collision policy: do not overwrite local devices
|
|
138
|
+
if prev is not None and prev.origin == "local":
|
|
139
|
+
# collision shouldn't happen; keep local stable
|
|
140
|
+
continue
|
|
141
|
+
|
|
142
|
+
salt = prev.salt if prev else ""
|
|
143
|
+
hsh = prev.hsh if prev else ""
|
|
144
|
+
lock = prev.io_lock if prev else threading.RLock()
|
|
145
|
+
|
|
146
|
+
label = str(u["label"] or "")
|
|
147
|
+
serial = str(u["serial"] or "")
|
|
148
|
+
kind = str(u["kind"] or "")
|
|
149
|
+
host_id = str(u["host_id"] or "")
|
|
150
|
+
device_id = str(u["device_id"] or "")
|
|
151
|
+
|
|
152
|
+
name = label or (prev.name if prev else f"host-device-{gid}")
|
|
153
|
+
dtype = (kind or (prev.dtype if prev else "")).strip().lower()
|
|
154
|
+
ident = serial or (prev.ident if prev else device_id)
|
|
155
|
+
|
|
156
|
+
_devices[int(gid)] = _DeviceState(
|
|
157
|
+
dev=VirtualDevice(
|
|
158
|
+
gid=int(gid),
|
|
159
|
+
host_id=host_id,
|
|
160
|
+
device_id=device_id,
|
|
161
|
+
label=label,
|
|
162
|
+
serial=serial,
|
|
163
|
+
kind=kind,
|
|
164
|
+
),
|
|
165
|
+
origin="host",
|
|
166
|
+
online=bool(u["online"]),
|
|
167
|
+
salt=salt,
|
|
168
|
+
hsh=hsh,
|
|
169
|
+
name=name,
|
|
170
|
+
ident=ident,
|
|
171
|
+
dtype=dtype,
|
|
172
|
+
io_lock=lock,
|
|
173
|
+
)
|
|
174
|
+
|
|
175
|
+
# Keep legacy maps coherent for host devices too (best-effort)
|
|
176
|
+
devices_info[int(gid)] = name
|
|
177
|
+
if ident:
|
|
178
|
+
device_serialization[int(gid)] = ident
|
|
179
|
+
|
|
180
|
+
# Mark host entries not present in current snap as offline (but keep proxy + auth)
|
|
181
|
+
present = set(updates.keys())
|
|
182
|
+
for gid, st in _devices.items():
|
|
183
|
+
if st.origin == "host" and gid not in present:
|
|
184
|
+
st.online = False
|
|
185
|
+
|
|
186
|
+
|
|
46
187
|
def _local_devices_str_snapshot() -> Dict[int, str]:
|
|
47
188
|
"""
|
|
48
189
|
Local devices from devices.env (whether connected or not).
|
|
@@ -50,8 +191,11 @@ def _local_devices_str_snapshot() -> Dict[int, str]:
|
|
|
50
191
|
out: Dict[int, str] = {}
|
|
51
192
|
with _state_lock:
|
|
52
193
|
for gid, st in _devices.items():
|
|
194
|
+
if st.origin != "local":
|
|
195
|
+
continue
|
|
196
|
+
|
|
53
197
|
name = (st.name or f"device-{gid}").strip()
|
|
54
|
-
status = "online" if (st
|
|
198
|
+
status = "online" if _is_connected(st) else "offline"
|
|
55
199
|
|
|
56
200
|
dtype = (st.dtype or "").strip()
|
|
57
201
|
ident = (st.ident or "").strip()
|
|
@@ -62,87 +206,64 @@ def _local_devices_str_snapshot() -> Dict[int, str]:
|
|
|
62
206
|
if ident:
|
|
63
207
|
meta_parts.append(f"ident={ident}")
|
|
64
208
|
|
|
65
|
-
# optional: show reservation state
|
|
66
209
|
if st.salt or st.hsh:
|
|
67
210
|
status = status + ", reserved"
|
|
68
211
|
|
|
69
212
|
out[int(gid)] = f"{name} [{', '.join(meta_parts)}] ({status})"
|
|
70
213
|
return out
|
|
71
214
|
|
|
215
|
+
|
|
72
216
|
def _host_devices_str_snapshot() -> Dict[int, str]:
|
|
217
|
+
_sync_host_devices(force=True)
|
|
218
|
+
|
|
73
219
|
reg = _get_host_registry()
|
|
74
220
|
if reg is None:
|
|
75
221
|
return {}
|
|
76
222
|
|
|
77
|
-
snap = reg.device_directory_cached(ttl_ms=0)
|
|
78
|
-
|
|
79
|
-
try:
|
|
80
|
-
status_map = reg.list_devices() # device_id -> DeviceStatus
|
|
81
|
-
except Exception:
|
|
82
|
-
status_map = {}
|
|
83
|
-
|
|
84
|
-
now_ms = int(time.time() * 1000)
|
|
223
|
+
snap = reg.device_directory_cached(ttl_ms=0)
|
|
85
224
|
|
|
86
225
|
out: Dict[int, str] = {}
|
|
87
226
|
for device_id, (host_id, info_obj, is_active) in snap.items():
|
|
88
|
-
# Prefer numeric device_id (old world), otherwise fall back to local_id (compat/UI key).
|
|
89
227
|
try:
|
|
90
228
|
gid = int(str(device_id))
|
|
91
229
|
except Exception:
|
|
92
|
-
|
|
93
|
-
gid = int(getattr(info_obj, "local_id", 0) or 0)
|
|
94
|
-
except Exception:
|
|
95
|
-
continue
|
|
96
|
-
if gid <= 0:
|
|
97
|
-
continue
|
|
230
|
+
continue
|
|
98
231
|
|
|
99
|
-
local_id = int(getattr(info_obj, "local_id", 0) or 0)
|
|
100
232
|
label = str(getattr(info_obj, "label", "") or "").strip()
|
|
101
|
-
serial = str(getattr(info_obj, "serial", "") or "").strip()
|
|
102
|
-
kind = str(getattr(info_obj, "kind", "") or "").strip()
|
|
103
|
-
|
|
104
233
|
status = "online" if bool(is_active) else "offline"
|
|
105
234
|
name = label or f"host-device-{gid}"
|
|
106
235
|
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
last_seen = f", seen={age_s}s ago"
|
|
115
|
-
except Exception:
|
|
116
|
-
pass
|
|
117
|
-
|
|
118
|
-
out[gid] = (
|
|
119
|
-
f"{name} "
|
|
120
|
-
f"[host={host_id}, local_id={local_id}"
|
|
121
|
-
f"{', kind=' + kind if kind else ''}"
|
|
122
|
-
f"{', serial=' + serial if serial else ''}"
|
|
123
|
-
f"{last_seen}"
|
|
124
|
-
f"] ({status})"
|
|
125
|
-
)
|
|
236
|
+
# reservation flag from _devices
|
|
237
|
+
with _state_lock:
|
|
238
|
+
st = _devices.get(int(gid))
|
|
239
|
+
if st and (st.salt or st.hsh):
|
|
240
|
+
status = status + ", reserved"
|
|
241
|
+
|
|
242
|
+
out[gid] = f"{name} ({status})"
|
|
126
243
|
|
|
127
244
|
return out
|
|
128
245
|
|
|
129
|
-
|
|
246
|
+
|
|
247
|
+
# ============================================================
|
|
130
248
|
# Config paths (server-side)
|
|
131
|
-
#
|
|
249
|
+
# ============================================================
|
|
132
250
|
|
|
133
251
|
def _xdg_config_home() -> Path:
|
|
134
252
|
return Path(os.getenv("XDG_CONFIG_HOME", Path.home() / ".config"))
|
|
135
253
|
|
|
254
|
+
|
|
136
255
|
def _cfg_dir() -> Path:
|
|
137
256
|
# default: ~/.config/remoterf
|
|
138
257
|
return Path(os.getenv("REMOTERF_CONFIG_DIR", _xdg_config_home() / "remoterf"))
|
|
139
258
|
|
|
259
|
+
|
|
140
260
|
def _devices_env_path() -> Path:
|
|
141
261
|
return _cfg_dir() / "devices.env"
|
|
142
262
|
|
|
143
|
-
|
|
263
|
+
|
|
264
|
+
# ============================================================
|
|
144
265
|
# Pluto helpers
|
|
145
|
-
#
|
|
266
|
+
# ============================================================
|
|
146
267
|
|
|
147
268
|
def connect_pluto(*, ip: str = "", usb: str = ""):
|
|
148
269
|
try:
|
|
@@ -156,7 +277,8 @@ def connect_pluto(*, ip: str = "", usb: str = ""):
|
|
|
156
277
|
except Exception as e:
|
|
157
278
|
print(f"Pluto {ip}: {e}")
|
|
158
279
|
return None
|
|
159
|
-
|
|
280
|
+
|
|
281
|
+
|
|
160
282
|
def get_usb_port_from_serial(serial: str) -> str | None:
|
|
161
283
|
serial = (serial or "").strip()
|
|
162
284
|
if not serial:
|
|
@@ -168,7 +290,6 @@ def get_usb_port_from_serial(serial: str) -> str | None:
|
|
|
168
290
|
print(f"Error running iio_info: {e}")
|
|
169
291
|
return None
|
|
170
292
|
|
|
171
|
-
# match either serial= or hw_serial= and extract usb context anywhere on the line
|
|
172
293
|
for line in out.splitlines():
|
|
173
294
|
if (f"serial={serial}" in line) or (f"hw_serial={serial}" in line):
|
|
174
295
|
m = re.search(r"\[usb:([^\]]+)\]", line)
|
|
@@ -179,19 +300,21 @@ def get_usb_port_from_serial(serial: str) -> str | None:
|
|
|
179
300
|
return None
|
|
180
301
|
|
|
181
302
|
|
|
182
|
-
#
|
|
303
|
+
# ============================================================
|
|
183
304
|
# Parse devices.env
|
|
184
|
-
#
|
|
305
|
+
# ============================================================
|
|
185
306
|
|
|
186
307
|
_ENV_LINE = re.compile(r"^\s*([A-Za-z_][A-Za-z0-9_]*)\s*=\s*(.*?)\s*$")
|
|
187
308
|
_DEVICE_KEY_RE = re.compile(r"^DEVICE_(\d+)_(.+)$", re.IGNORECASE)
|
|
188
309
|
|
|
310
|
+
|
|
189
311
|
def _strip_quotes(v: str) -> str:
|
|
190
312
|
v = (v or "").strip()
|
|
191
313
|
if len(v) >= 2 and ((v[0] == v[-1] == '"') or (v[0] == v[-1] == "'")):
|
|
192
314
|
return v[1:-1]
|
|
193
315
|
return v
|
|
194
316
|
|
|
317
|
+
|
|
195
318
|
def _read_env_file(path: Path) -> Dict[str, str]:
|
|
196
319
|
out: Dict[str, str] = {}
|
|
197
320
|
if not path.exists():
|
|
@@ -213,6 +336,7 @@ def _read_env_file(path: Path) -> Dict[str, str]:
|
|
|
213
336
|
out[k] = v
|
|
214
337
|
return out
|
|
215
338
|
|
|
339
|
+
|
|
216
340
|
def _load_device_records() -> Dict[int, Dict[str, str]]:
|
|
217
341
|
"""
|
|
218
342
|
Returns:
|
|
@@ -251,6 +375,7 @@ def _load_device_records() -> Dict[int, Dict[str, str]]:
|
|
|
251
375
|
|
|
252
376
|
return recs
|
|
253
377
|
|
|
378
|
+
|
|
254
379
|
def _connect_from_record(rec: Dict[str, str]):
|
|
255
380
|
"""
|
|
256
381
|
Returns (device_obj, ident, dtype).
|
|
@@ -283,9 +408,9 @@ def _connect_from_record(rec: Dict[str, str]):
|
|
|
283
408
|
return (None, ident, dtype)
|
|
284
409
|
|
|
285
410
|
|
|
286
|
-
#
|
|
411
|
+
# ============================================================
|
|
287
412
|
# Thread-safe runtime state
|
|
288
|
-
#
|
|
413
|
+
# ============================================================
|
|
289
414
|
|
|
290
415
|
# legacy maps (kept for your existing server calls/UI)
|
|
291
416
|
devices_info: Dict[int, str] = {}
|
|
@@ -316,6 +441,8 @@ def _init_from_env() -> None:
|
|
|
316
441
|
|
|
317
442
|
tmp_devices[int(gid)] = _DeviceState(
|
|
318
443
|
dev=dev,
|
|
444
|
+
origin="local",
|
|
445
|
+
online=(dev is not None),
|
|
319
446
|
salt="",
|
|
320
447
|
hsh="",
|
|
321
448
|
name=name,
|
|
@@ -335,13 +462,14 @@ def _init_from_env() -> None:
|
|
|
335
462
|
device_serialization.clear()
|
|
336
463
|
device_serialization.update(tmp_ser)
|
|
337
464
|
|
|
465
|
+
|
|
338
466
|
# initialize on import
|
|
339
467
|
_init_from_env()
|
|
340
468
|
|
|
341
469
|
|
|
342
|
-
#
|
|
470
|
+
# ============================================================
|
|
343
471
|
# Legacy reservation helpers (master token parsing)
|
|
344
|
-
#
|
|
472
|
+
# ============================================================
|
|
345
473
|
|
|
346
474
|
def parse_mastertoken(token: str):
|
|
347
475
|
"""
|
|
@@ -364,9 +492,9 @@ def parse_mastertoken(token: str):
|
|
|
364
492
|
return (device_id, force)
|
|
365
493
|
|
|
366
494
|
|
|
367
|
-
#
|
|
495
|
+
# ============================================================
|
|
368
496
|
# Transmitter stubs (legacy)
|
|
369
|
-
#
|
|
497
|
+
# ============================================================
|
|
370
498
|
|
|
371
499
|
_transmitter = None
|
|
372
500
|
|
|
@@ -380,57 +508,52 @@ def get_transmitter_state() -> bool:
|
|
|
380
508
|
return False
|
|
381
509
|
|
|
382
510
|
|
|
383
|
-
#
|
|
511
|
+
# ============================================================
|
|
384
512
|
# Legacy API (state-safe)
|
|
385
|
-
#
|
|
513
|
+
# ============================================================
|
|
386
514
|
|
|
387
515
|
def get_all_devices() -> Dict[int, Tuple[object, str, str]]:
|
|
388
|
-
|
|
516
|
+
_sync_host_devices()
|
|
517
|
+
|
|
389
518
|
with _state_lock:
|
|
390
519
|
out: Dict[int, Tuple[object, str, str]] = {}
|
|
391
520
|
for gid, st in _devices.items():
|
|
392
|
-
if st
|
|
393
|
-
out[gid] = (st.dev, st.salt, st.hsh)
|
|
521
|
+
if _is_connected(st):
|
|
522
|
+
out[gid] = (st.dev, st.salt, st.hsh) # dev may be VirtualDevice for host
|
|
394
523
|
return out
|
|
395
524
|
|
|
525
|
+
|
|
396
526
|
def get_all_devices_str() -> Dict[int, str]:
|
|
397
|
-
|
|
398
|
-
out = _local_devices_str_snapshot()
|
|
527
|
+
_sync_host_devices()
|
|
399
528
|
|
|
400
|
-
|
|
529
|
+
out = _local_devices_str_snapshot()
|
|
401
530
|
host_out = _host_devices_str_snapshot()
|
|
402
531
|
|
|
403
|
-
#
|
|
404
|
-
# try:
|
|
405
|
-
# from ..host import host_tunnel_server as hts # adjust if your canonical import differs
|
|
406
|
-
# reg = hts.get_tunnel_registry(create=False) if hasattr(hts, "get_tunnel_registry") else None
|
|
407
|
-
# print("\n[device_manager] host_out dump")
|
|
408
|
-
# print(f" hts module: {getattr(hts, '__name__', '<?>')}")
|
|
409
|
-
# print(f" registry id: {id(reg) if reg is not None else None}")
|
|
410
|
-
# print(f" host_out count: {len(host_out)}")
|
|
411
|
-
|
|
412
|
-
# if not host_out:
|
|
413
|
-
# print(" (empty)")
|
|
414
|
-
# else:
|
|
415
|
-
# for gid, s in sorted(host_out.items(), key=lambda kv: int(kv[0])):
|
|
416
|
-
# print(f" {gid}: {s}")
|
|
417
|
-
# print()
|
|
418
|
-
# except Exception as e:
|
|
419
|
-
# print(f"[device_manager] host_out dump failed: {e}")
|
|
420
|
-
|
|
421
|
-
# merge with collision handling
|
|
532
|
+
# merge with collision handling (shouldn't happen; keeps UI stable if it does)
|
|
422
533
|
for gid, s in host_out.items():
|
|
423
534
|
if gid in out:
|
|
424
|
-
|
|
425
|
-
alt = -int(gid) - 1 # deterministic "shadow key"
|
|
535
|
+
alt = -int(gid) - 1
|
|
426
536
|
out[alt] = s + " [gid-collision]"
|
|
427
537
|
else:
|
|
428
538
|
out[gid] = s
|
|
429
539
|
|
|
430
540
|
return out
|
|
431
541
|
|
|
542
|
+
|
|
432
543
|
def set_device(device_id: int, salt: str, hash: str):
|
|
433
|
-
#
|
|
544
|
+
# ensure host devices are upserted so reservations can apply to them too
|
|
545
|
+
_sync_host_devices()
|
|
546
|
+
|
|
547
|
+
with _state_lock:
|
|
548
|
+
st = _devices.get(int(device_id))
|
|
549
|
+
|
|
550
|
+
if not st:
|
|
551
|
+
_sync_host_devices(force=True)
|
|
552
|
+
with _state_lock:
|
|
553
|
+
st = _devices.get(int(device_id))
|
|
554
|
+
if not st:
|
|
555
|
+
return
|
|
556
|
+
|
|
434
557
|
with _state_lock:
|
|
435
558
|
st = _devices.get(int(device_id))
|
|
436
559
|
if not st:
|
|
@@ -438,39 +561,48 @@ def set_device(device_id: int, salt: str, hash: str):
|
|
|
438
561
|
st.salt = str(salt or "")
|
|
439
562
|
st.hsh = str(hash or "")
|
|
440
563
|
|
|
564
|
+
|
|
441
565
|
def device_exists(device_id: int) -> bool:
|
|
442
566
|
did = int(device_id)
|
|
443
567
|
|
|
444
|
-
|
|
568
|
+
_sync_host_devices()
|
|
569
|
+
|
|
570
|
+
# local: connected only (unchanged semantics)
|
|
445
571
|
with _state_lock:
|
|
446
572
|
st = _devices.get(did)
|
|
447
|
-
if st and st.dev is not None:
|
|
573
|
+
if st and st.origin == "local" and st.dev is not None:
|
|
448
574
|
return True
|
|
449
575
|
|
|
450
|
-
# host
|
|
576
|
+
# host: existence as known by registry (unchanged)
|
|
451
577
|
reg = _get_host_registry()
|
|
452
578
|
if reg is None:
|
|
453
579
|
return False
|
|
454
580
|
return bool(reg.is_host_device(str(did)))
|
|
455
581
|
|
|
582
|
+
|
|
456
583
|
def device_is_available(device_id: int) -> bool:
|
|
584
|
+
_sync_host_devices()
|
|
585
|
+
|
|
457
586
|
with _state_lock:
|
|
458
587
|
st = _devices.get(int(device_id))
|
|
459
|
-
if not st or st
|
|
588
|
+
if not st or not _is_connected(st):
|
|
460
589
|
return False
|
|
461
590
|
return (st.salt == "") and (st.hsh == "")
|
|
462
591
|
|
|
592
|
+
|
|
463
593
|
def get_device_by_id(device_id: int):
|
|
464
|
-
|
|
594
|
+
_sync_host_devices()
|
|
595
|
+
|
|
465
596
|
with _state_lock:
|
|
466
597
|
st = _devices.get(int(device_id))
|
|
467
598
|
if not st:
|
|
468
599
|
return None
|
|
469
600
|
return (st.dev, st.salt, st.hsh)
|
|
470
601
|
|
|
602
|
+
|
|
471
603
|
def get_device(*, api_token: str):
|
|
472
604
|
"""
|
|
473
|
-
Legacy behavior:
|
|
605
|
+
Legacy behavior preserved:
|
|
474
606
|
- master_token returns first available device object
|
|
475
607
|
- master_token_<id>[_force] selects a specific device
|
|
476
608
|
- otherwise validate against per-device salt/hash (set via reservations)
|
|
@@ -481,17 +613,24 @@ def get_device(*, api_token: str):
|
|
|
481
613
|
if not api_token:
|
|
482
614
|
return None
|
|
483
615
|
|
|
484
|
-
|
|
616
|
+
_sync_host_devices()
|
|
617
|
+
|
|
485
618
|
with _state_lock:
|
|
486
|
-
snapshot: List[Tuple[int, object, str, str]] = [
|
|
487
|
-
(gid, st.dev, st.salt, st.hsh)
|
|
619
|
+
snapshot: List[Tuple[int, object, str, str, str]] = [
|
|
620
|
+
(gid, st.dev, st.salt, st.hsh, st.origin)
|
|
488
621
|
for gid, st in _devices.items()
|
|
489
|
-
if st
|
|
622
|
+
if _is_connected(st)
|
|
490
623
|
]
|
|
491
624
|
|
|
625
|
+
# Preserve "master prefers local" behavior
|
|
492
626
|
if api_token == master_token:
|
|
493
|
-
|
|
494
|
-
|
|
627
|
+
# local first
|
|
628
|
+
for _, dev, salt, hsh, origin in snapshot:
|
|
629
|
+
if origin == "local" and dev is not None and salt == "" and hsh == "":
|
|
630
|
+
return dev
|
|
631
|
+
# then host
|
|
632
|
+
for _, dev, salt, hsh, origin in snapshot:
|
|
633
|
+
if origin == "host" and dev is not None and salt == "" and hsh == "":
|
|
495
634
|
return dev
|
|
496
635
|
return None
|
|
497
636
|
|
|
@@ -500,13 +639,13 @@ def get_device(*, api_token: str):
|
|
|
500
639
|
device_id, force = parsed
|
|
501
640
|
with _state_lock:
|
|
502
641
|
st = _devices.get(int(device_id))
|
|
503
|
-
if not st or st
|
|
642
|
+
if not st or not _is_connected(st):
|
|
504
643
|
return None
|
|
505
644
|
if force:
|
|
506
645
|
return st.dev
|
|
507
646
|
return st.dev if (st.salt == "" and st.hsh == "") else None
|
|
508
647
|
|
|
509
|
-
for _, dev, salt, hsh in snapshot:
|
|
648
|
+
for _, dev, salt, hsh, _origin in snapshot:
|
|
510
649
|
if dev is None:
|
|
511
650
|
continue
|
|
512
651
|
if salt and hsh and validate_token(salt, hsh, api_token):
|
|
@@ -515,47 +654,55 @@ def get_device(*, api_token: str):
|
|
|
515
654
|
return None
|
|
516
655
|
|
|
517
656
|
|
|
518
|
-
#
|
|
519
|
-
#
|
|
520
|
-
#
|
|
657
|
+
# ============================================================
|
|
658
|
+
# Per-device I/O locking (true concurrency support)
|
|
659
|
+
# ============================================================
|
|
521
660
|
|
|
522
661
|
@contextmanager
|
|
523
662
|
def acquire_device(api_token: str) -> Iterator[Tuple[int, object]]:
|
|
524
663
|
"""
|
|
525
664
|
Thread-safe way to use a device:
|
|
526
665
|
with acquire_device(token) as (gid, dev):
|
|
527
|
-
# safe: only one thread at a time per device
|
|
528
666
|
...
|
|
529
667
|
|
|
530
668
|
- Selects the device using the SAME logic as get_device()
|
|
531
669
|
- Then acquires that device's io_lock
|
|
532
670
|
- Yields (gid, dev)
|
|
671
|
+
|
|
672
|
+
NOTE:
|
|
673
|
+
dev can be an adi device (local) OR a VirtualDevice (host).
|
|
533
674
|
"""
|
|
534
675
|
if not api_token:
|
|
535
676
|
raise RuntimeError("missing api_token")
|
|
536
677
|
|
|
537
|
-
|
|
678
|
+
_sync_host_devices()
|
|
679
|
+
|
|
538
680
|
gid: Optional[int] = None
|
|
539
681
|
|
|
540
682
|
if api_token == master_token:
|
|
541
683
|
with _state_lock:
|
|
684
|
+
# prefer local first
|
|
542
685
|
for k, st in _devices.items():
|
|
543
|
-
if st.
|
|
686
|
+
if st.origin == "local" and _is_connected(st) and st.salt == "" and st.hsh == "":
|
|
544
687
|
gid = k
|
|
545
688
|
break
|
|
689
|
+
if gid is None:
|
|
690
|
+
for k, st in _devices.items():
|
|
691
|
+
if st.origin == "host" and _is_connected(st) and st.salt == "" and st.hsh == "":
|
|
692
|
+
gid = k
|
|
693
|
+
break
|
|
546
694
|
else:
|
|
547
695
|
parsed = parse_mastertoken(api_token)
|
|
548
696
|
if parsed:
|
|
549
697
|
cand, force = parsed
|
|
550
698
|
with _state_lock:
|
|
551
699
|
st = _devices.get(int(cand))
|
|
552
|
-
if st and st
|
|
700
|
+
if st and _is_connected(st):
|
|
553
701
|
if force or (st.salt == "" and st.hsh == ""):
|
|
554
702
|
gid = int(cand)
|
|
555
703
|
else:
|
|
556
|
-
# validate token against per-device salt/hash
|
|
557
704
|
with _state_lock:
|
|
558
|
-
snapshot = [(k, st.dev, st.salt, st.hsh) for k, st in _devices.items() if st
|
|
705
|
+
snapshot = [(k, st.dev, st.salt, st.hsh) for k, st in _devices.items() if _is_connected(st)]
|
|
559
706
|
for k, dev, salt, hsh in snapshot:
|
|
560
707
|
if salt and hsh and validate_token(salt, hsh, api_token):
|
|
561
708
|
gid = k
|
|
@@ -564,10 +711,9 @@ def acquire_device(api_token: str) -> Iterator[Tuple[int, object]]:
|
|
|
564
711
|
if gid is None:
|
|
565
712
|
raise RuntimeError("no device available / invalid token")
|
|
566
713
|
|
|
567
|
-
# lock the selected device for I/O
|
|
568
714
|
with _state_lock:
|
|
569
715
|
st = _devices.get(int(gid))
|
|
570
|
-
if not st or st.dev is None:
|
|
716
|
+
if not st or not _is_connected(st) or st.dev is None:
|
|
571
717
|
raise RuntimeError("device disappeared / not connected")
|
|
572
718
|
lock = st.io_lock
|
|
573
719
|
dev = st.dev
|
|
@@ -579,22 +725,25 @@ def acquire_device(api_token: str) -> Iterator[Tuple[int, object]]:
|
|
|
579
725
|
lock.release()
|
|
580
726
|
|
|
581
727
|
|
|
582
|
-
#
|
|
728
|
+
# ============================================================
|
|
583
729
|
# Optional: reload device definitions (atomic swap)
|
|
584
|
-
#
|
|
730
|
+
# ============================================================
|
|
585
731
|
|
|
586
732
|
def reload_devices() -> None:
|
|
587
733
|
"""
|
|
588
|
-
Reload devices.env and reconnect devices.
|
|
589
|
-
Preserves existing salt/hash for devices that remain (same gid)
|
|
734
|
+
Reload devices.env and reconnect local devices.
|
|
735
|
+
Preserves existing salt/hash for devices that remain (same gid),
|
|
736
|
+
and preserves host virtual devices + their auth state.
|
|
590
737
|
"""
|
|
591
738
|
records = _load_device_records()
|
|
592
739
|
|
|
593
|
-
#
|
|
740
|
+
_sync_host_devices() # ensure host entries exist before snapshot
|
|
741
|
+
|
|
594
742
|
with _state_lock:
|
|
595
743
|
prev_auth = {gid: (st.salt, st.hsh) for gid, st in _devices.items()}
|
|
744
|
+
prev_host = {gid: st for gid, st in _devices.items() if st.origin == "host"}
|
|
596
745
|
|
|
597
|
-
|
|
746
|
+
tmp_local: Dict[int, _DeviceState] = {}
|
|
598
747
|
tmp_info: Dict[int, str] = {}
|
|
599
748
|
tmp_ser: Dict[int, str] = {}
|
|
600
749
|
|
|
@@ -609,8 +758,10 @@ def reload_devices() -> None:
|
|
|
609
758
|
tmp_ser[int(gid)] = ident
|
|
610
759
|
|
|
611
760
|
salt, hsh = prev_auth.get(int(gid), ("", ""))
|
|
612
|
-
|
|
761
|
+
tmp_local[int(gid)] = _DeviceState(
|
|
613
762
|
dev=dev,
|
|
763
|
+
origin="local",
|
|
764
|
+
online=(dev is not None),
|
|
614
765
|
salt=salt,
|
|
615
766
|
hsh=hsh,
|
|
616
767
|
name=name,
|
|
@@ -621,7 +772,14 @@ def reload_devices() -> None:
|
|
|
621
772
|
|
|
622
773
|
with _state_lock:
|
|
623
774
|
_devices.clear()
|
|
624
|
-
|
|
775
|
+
|
|
776
|
+
# restore host entries (but do not overwrite locals if collision)
|
|
777
|
+
for gid, st in prev_host.items():
|
|
778
|
+
if gid not in tmp_local:
|
|
779
|
+
_devices[gid] = st
|
|
780
|
+
|
|
781
|
+
# add locals
|
|
782
|
+
_devices.update(tmp_local)
|
|
625
783
|
|
|
626
784
|
devices_info.clear()
|
|
627
785
|
devices_info.update(tmp_info)
|
|
@@ -629,7 +787,10 @@ def reload_devices() -> None:
|
|
|
629
787
|
device_serialization.clear()
|
|
630
788
|
device_serialization.update(tmp_ser)
|
|
631
789
|
|
|
790
|
+
# refresh host online/offline status after reload (best-effort)
|
|
791
|
+
_sync_host_devices(force=True)
|
|
792
|
+
|
|
632
793
|
|
|
633
794
|
def set_pluto(ip: str = "192.168.2.1"):
|
|
634
795
|
# kept for compatibility (no-op)
|
|
635
|
-
pass
|
|
796
|
+
pass
|
|
@@ -391,10 +391,10 @@ def _cmd_hosts_status() -> None:
|
|
|
391
391
|
reg = hts.get_tunnel_registry()
|
|
392
392
|
assert reg is not None
|
|
393
393
|
|
|
394
|
-
print("\n[hosts status] registry debug")
|
|
395
|
-
print(f" hts module: {hts.__name__}")
|
|
396
|
-
reg = hts.get_tunnel_registry(create=False)
|
|
397
|
-
print(f" registry id: {id(reg) if reg else None}\n")
|
|
394
|
+
# print("\n[hosts status] registry debug")
|
|
395
|
+
# print(f" hts module: {hts.__name__}")
|
|
396
|
+
# reg = hts.get_tunnel_registry(create=False)
|
|
397
|
+
# print(f" registry id: {id(reg) if reg else None}\n")
|
|
398
398
|
|
|
399
399
|
hosts = reg.list_hosts() # host_id -> HostStatus
|
|
400
400
|
devices = reg.list_devices() # device_id -> DeviceStatus
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: remoteRF-server
|
|
3
|
-
Version: 0.1.
|
|
3
|
+
Version: 0.1.96
|
|
4
4
|
Description-Content-Type: text/markdown
|
|
5
5
|
Requires-Dist: grpcio
|
|
6
6
|
Requires-Dist: protobuf
|
|
@@ -350,7 +350,7 @@ todo:
|
|
|
350
350
|
|
|
351
351
|
<!-- Host ID (NEEDS TO BE UNIQUE AND TELL THE HOST THAT) (or if device also does it as well.) -->
|
|
352
352
|
|
|
353
|
-
ServerRF display meta data as part of devices list / reservation
|
|
353
|
+
<!-- ServerRF display meta data as part of devices list / reservation -->
|
|
354
354
|
|
|
355
355
|
+ online/activity status (appended into device name) + (plus host side warning if the device id is NOT globally unique!)
|
|
356
356
|
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{remoterf_server-0.1.94 → remoterf_server-0.1.96}/src/remoteRF_server/common/grpc/__init__.py
RENAMED
|
File without changes
|
{remoterf_server-0.1.94 → remoterf_server-0.1.96}/src/remoteRF_server/common/grpc/grpc_host_pb2.py
RENAMED
|
File without changes
|
|
File without changes
|
{remoterf_server-0.1.94 → remoterf_server-0.1.96}/src/remoteRF_server/common/grpc/grpc_pb2.py
RENAMED
|
File without changes
|
{remoterf_server-0.1.94 → remoterf_server-0.1.96}/src/remoteRF_server/common/grpc/grpc_pb2_grpc.py
RENAMED
|
File without changes
|
{remoterf_server-0.1.94 → remoterf_server-0.1.96}/src/remoteRF_server/common/utils/__init__.py
RENAMED
|
File without changes
|
{remoterf_server-0.1.94 → remoterf_server-0.1.96}/src/remoteRF_server/common/utils/ansi_codes.py
RENAMED
|
File without changes
|
{remoterf_server-0.1.94 → remoterf_server-0.1.96}/src/remoteRF_server/common/utils/api_token.py
RENAMED
|
File without changes
|
{remoterf_server-0.1.94 → remoterf_server-0.1.96}/src/remoteRF_server/common/utils/db_connection.py
RENAMED
|
File without changes
|
{remoterf_server-0.1.94 → remoterf_server-0.1.96}/src/remoteRF_server/common/utils/db_location.py
RENAMED
|
File without changes
|
{remoterf_server-0.1.94 → remoterf_server-0.1.96}/src/remoteRF_server/common/utils/list_string.py
RENAMED
|
File without changes
|
{remoterf_server-0.1.94 → remoterf_server-0.1.96}/src/remoteRF_server/common/utils/process_arg.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{remoterf_server-0.1.94 → remoterf_server-0.1.96}/src/remoteRF_server/host/host_directory_store.py
RENAMED
|
File without changes
|
{remoterf_server-0.1.94 → remoterf_server-0.1.96}/src/remoteRF_server/host/host_tunnel_server.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{remoterf_server-0.1.94 → remoterf_server-0.1.96}/src/remoteRF_server/server/cert_provider.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{remoterf_server-0.1.94 → remoterf_server-0.1.96}/src/remoteRF_server/server/user_group_cli.py
RENAMED
|
File without changes
|
{remoterf_server-0.1.94 → remoterf_server-0.1.96}/src/remoteRF_server/server/user_group_handler.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{remoterf_server-0.1.94 → remoterf_server-0.1.96}/src/remoteRF_server.egg-info/dependency_links.txt
RENAMED
|
File without changes
|
{remoterf_server-0.1.94 → remoterf_server-0.1.96}/src/remoteRF_server.egg-info/entry_points.txt
RENAMED
|
File without changes
|
|
File without changes
|
{remoterf_server-0.1.94 → remoterf_server-0.1.96}/src/remoteRF_server.egg-info/top_level.txt
RENAMED
|
File without changes
|