remoteRF-server 0.1.95__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.95 → remoterf_server-0.1.96}/PKG-INFO +2 -2
- {remoterf_server-0.1.95 → remoterf_server-0.1.96}/README.md +1 -1
- {remoterf_server-0.1.95 → remoterf_server-0.1.96}/pyproject.toml +1 -1
- {remoterf_server-0.1.95 → remoterf_server-0.1.96}/src/remoteRF_server/server/device_manager.py +292 -133
- {remoterf_server-0.1.95 → remoterf_server-0.1.96}/src/remoteRF_server/server/grpc_server.py +4 -4
- {remoterf_server-0.1.95 → remoterf_server-0.1.96}/src/remoteRF_server.egg-info/PKG-INFO +2 -2
- {remoterf_server-0.1.95 → remoterf_server-0.1.96}/setup.cfg +0 -0
- {remoterf_server-0.1.95 → remoterf_server-0.1.96}/src/remoteRF_server/__init__.py +0 -0
- {remoterf_server-0.1.95 → remoterf_server-0.1.96}/src/remoteRF_server/common/__init__.py +0 -0
- {remoterf_server-0.1.95 → remoterf_server-0.1.96}/src/remoteRF_server/common/grpc/__init__.py +0 -0
- {remoterf_server-0.1.95 → remoterf_server-0.1.96}/src/remoteRF_server/common/grpc/grpc_host_pb2.py +0 -0
- {remoterf_server-0.1.95 → remoterf_server-0.1.96}/src/remoteRF_server/common/grpc/grpc_host_pb2_grpc.py +0 -0
- {remoterf_server-0.1.95 → remoterf_server-0.1.96}/src/remoteRF_server/common/grpc/grpc_pb2.py +0 -0
- {remoterf_server-0.1.95 → remoterf_server-0.1.96}/src/remoteRF_server/common/grpc/grpc_pb2_grpc.py +0 -0
- {remoterf_server-0.1.95 → remoterf_server-0.1.96}/src/remoteRF_server/common/utils/__init__.py +0 -0
- {remoterf_server-0.1.95 → remoterf_server-0.1.96}/src/remoteRF_server/common/utils/ansi_codes.py +0 -0
- {remoterf_server-0.1.95 → remoterf_server-0.1.96}/src/remoteRF_server/common/utils/api_token.py +0 -0
- {remoterf_server-0.1.95 → remoterf_server-0.1.96}/src/remoteRF_server/common/utils/db_connection.py +0 -0
- {remoterf_server-0.1.95 → remoterf_server-0.1.96}/src/remoteRF_server/common/utils/db_location.py +0 -0
- {remoterf_server-0.1.95 → remoterf_server-0.1.96}/src/remoteRF_server/common/utils/list_string.py +0 -0
- {remoterf_server-0.1.95 → remoterf_server-0.1.96}/src/remoteRF_server/common/utils/process_arg.py +0 -0
- {remoterf_server-0.1.95 → remoterf_server-0.1.96}/src/remoteRF_server/drivers/__init__.py +0 -0
- {remoterf_server-0.1.95 → remoterf_server-0.1.96}/src/remoteRF_server/drivers/adalm_pluto/__init__.py +0 -0
- {remoterf_server-0.1.95 → remoterf_server-0.1.96}/src/remoteRF_server/drivers/adalm_pluto/pluto_remote_server.py +0 -0
- {remoterf_server-0.1.95 → remoterf_server-0.1.96}/src/remoteRF_server/host/__init__.py +0 -0
- {remoterf_server-0.1.95 → remoterf_server-0.1.96}/src/remoteRF_server/host/host_directory_store.py +0 -0
- {remoterf_server-0.1.95 → remoterf_server-0.1.96}/src/remoteRF_server/host/host_tunnel_server.py +0 -0
- {remoterf_server-0.1.95 → remoterf_server-0.1.96}/src/remoteRF_server/server/__init__.py +0 -0
- {remoterf_server-0.1.95 → remoterf_server-0.1.96}/src/remoteRF_server/server/acc_perms.py +0 -0
- {remoterf_server-0.1.95 → remoterf_server-0.1.96}/src/remoteRF_server/server/cert_provider.py +0 -0
- {remoterf_server-0.1.95 → remoterf_server-0.1.96}/src/remoteRF_server/server/reservation.py +0 -0
- {remoterf_server-0.1.95 → remoterf_server-0.1.96}/src/remoteRF_server/server/rpc_manager.py +0 -0
- {remoterf_server-0.1.95 → remoterf_server-0.1.96}/src/remoteRF_server/server/user_group_cli.py +0 -0
- {remoterf_server-0.1.95 → remoterf_server-0.1.96}/src/remoteRF_server/server/user_group_handler.py +0 -0
- {remoterf_server-0.1.95 → remoterf_server-0.1.96}/src/remoteRF_server/serverrf_cli.py +0 -0
- {remoterf_server-0.1.95 → remoterf_server-0.1.96}/src/remoteRF_server/tools/__init__.py +0 -0
- {remoterf_server-0.1.95 → remoterf_server-0.1.96}/src/remoteRF_server/tools/gen_certs.py +0 -0
- {remoterf_server-0.1.95 → remoterf_server-0.1.96}/src/remoteRF_server.egg-info/SOURCES.txt +0 -0
- {remoterf_server-0.1.95 → remoterf_server-0.1.96}/src/remoteRF_server.egg-info/dependency_links.txt +0 -0
- {remoterf_server-0.1.95 → remoterf_server-0.1.96}/src/remoteRF_server.egg-info/entry_points.txt +0 -0
- {remoterf_server-0.1.95 → remoterf_server-0.1.96}/src/remoteRF_server.egg-info/requires.txt +0 -0
- {remoterf_server-0.1.95 → 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.95 → 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,89 +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
|
-
|
|
126
|
-
f"({status})"
|
|
127
|
-
)
|
|
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})"
|
|
128
243
|
|
|
129
244
|
return out
|
|
130
245
|
|
|
131
|
-
|
|
246
|
+
|
|
247
|
+
# ============================================================
|
|
132
248
|
# Config paths (server-side)
|
|
133
|
-
#
|
|
249
|
+
# ============================================================
|
|
134
250
|
|
|
135
251
|
def _xdg_config_home() -> Path:
|
|
136
252
|
return Path(os.getenv("XDG_CONFIG_HOME", Path.home() / ".config"))
|
|
137
253
|
|
|
254
|
+
|
|
138
255
|
def _cfg_dir() -> Path:
|
|
139
256
|
# default: ~/.config/remoterf
|
|
140
257
|
return Path(os.getenv("REMOTERF_CONFIG_DIR", _xdg_config_home() / "remoterf"))
|
|
141
258
|
|
|
259
|
+
|
|
142
260
|
def _devices_env_path() -> Path:
|
|
143
261
|
return _cfg_dir() / "devices.env"
|
|
144
262
|
|
|
145
|
-
|
|
263
|
+
|
|
264
|
+
# ============================================================
|
|
146
265
|
# Pluto helpers
|
|
147
|
-
#
|
|
266
|
+
# ============================================================
|
|
148
267
|
|
|
149
268
|
def connect_pluto(*, ip: str = "", usb: str = ""):
|
|
150
269
|
try:
|
|
@@ -158,7 +277,8 @@ def connect_pluto(*, ip: str = "", usb: str = ""):
|
|
|
158
277
|
except Exception as e:
|
|
159
278
|
print(f"Pluto {ip}: {e}")
|
|
160
279
|
return None
|
|
161
|
-
|
|
280
|
+
|
|
281
|
+
|
|
162
282
|
def get_usb_port_from_serial(serial: str) -> str | None:
|
|
163
283
|
serial = (serial or "").strip()
|
|
164
284
|
if not serial:
|
|
@@ -170,7 +290,6 @@ def get_usb_port_from_serial(serial: str) -> str | None:
|
|
|
170
290
|
print(f"Error running iio_info: {e}")
|
|
171
291
|
return None
|
|
172
292
|
|
|
173
|
-
# match either serial= or hw_serial= and extract usb context anywhere on the line
|
|
174
293
|
for line in out.splitlines():
|
|
175
294
|
if (f"serial={serial}" in line) or (f"hw_serial={serial}" in line):
|
|
176
295
|
m = re.search(r"\[usb:([^\]]+)\]", line)
|
|
@@ -181,19 +300,21 @@ def get_usb_port_from_serial(serial: str) -> str | None:
|
|
|
181
300
|
return None
|
|
182
301
|
|
|
183
302
|
|
|
184
|
-
#
|
|
303
|
+
# ============================================================
|
|
185
304
|
# Parse devices.env
|
|
186
|
-
#
|
|
305
|
+
# ============================================================
|
|
187
306
|
|
|
188
307
|
_ENV_LINE = re.compile(r"^\s*([A-Za-z_][A-Za-z0-9_]*)\s*=\s*(.*?)\s*$")
|
|
189
308
|
_DEVICE_KEY_RE = re.compile(r"^DEVICE_(\d+)_(.+)$", re.IGNORECASE)
|
|
190
309
|
|
|
310
|
+
|
|
191
311
|
def _strip_quotes(v: str) -> str:
|
|
192
312
|
v = (v or "").strip()
|
|
193
313
|
if len(v) >= 2 and ((v[0] == v[-1] == '"') or (v[0] == v[-1] == "'")):
|
|
194
314
|
return v[1:-1]
|
|
195
315
|
return v
|
|
196
316
|
|
|
317
|
+
|
|
197
318
|
def _read_env_file(path: Path) -> Dict[str, str]:
|
|
198
319
|
out: Dict[str, str] = {}
|
|
199
320
|
if not path.exists():
|
|
@@ -215,6 +336,7 @@ def _read_env_file(path: Path) -> Dict[str, str]:
|
|
|
215
336
|
out[k] = v
|
|
216
337
|
return out
|
|
217
338
|
|
|
339
|
+
|
|
218
340
|
def _load_device_records() -> Dict[int, Dict[str, str]]:
|
|
219
341
|
"""
|
|
220
342
|
Returns:
|
|
@@ -253,6 +375,7 @@ def _load_device_records() -> Dict[int, Dict[str, str]]:
|
|
|
253
375
|
|
|
254
376
|
return recs
|
|
255
377
|
|
|
378
|
+
|
|
256
379
|
def _connect_from_record(rec: Dict[str, str]):
|
|
257
380
|
"""
|
|
258
381
|
Returns (device_obj, ident, dtype).
|
|
@@ -285,9 +408,9 @@ def _connect_from_record(rec: Dict[str, str]):
|
|
|
285
408
|
return (None, ident, dtype)
|
|
286
409
|
|
|
287
410
|
|
|
288
|
-
#
|
|
411
|
+
# ============================================================
|
|
289
412
|
# Thread-safe runtime state
|
|
290
|
-
#
|
|
413
|
+
# ============================================================
|
|
291
414
|
|
|
292
415
|
# legacy maps (kept for your existing server calls/UI)
|
|
293
416
|
devices_info: Dict[int, str] = {}
|
|
@@ -318,6 +441,8 @@ def _init_from_env() -> None:
|
|
|
318
441
|
|
|
319
442
|
tmp_devices[int(gid)] = _DeviceState(
|
|
320
443
|
dev=dev,
|
|
444
|
+
origin="local",
|
|
445
|
+
online=(dev is not None),
|
|
321
446
|
salt="",
|
|
322
447
|
hsh="",
|
|
323
448
|
name=name,
|
|
@@ -337,13 +462,14 @@ def _init_from_env() -> None:
|
|
|
337
462
|
device_serialization.clear()
|
|
338
463
|
device_serialization.update(tmp_ser)
|
|
339
464
|
|
|
465
|
+
|
|
340
466
|
# initialize on import
|
|
341
467
|
_init_from_env()
|
|
342
468
|
|
|
343
469
|
|
|
344
|
-
#
|
|
470
|
+
# ============================================================
|
|
345
471
|
# Legacy reservation helpers (master token parsing)
|
|
346
|
-
#
|
|
472
|
+
# ============================================================
|
|
347
473
|
|
|
348
474
|
def parse_mastertoken(token: str):
|
|
349
475
|
"""
|
|
@@ -366,9 +492,9 @@ def parse_mastertoken(token: str):
|
|
|
366
492
|
return (device_id, force)
|
|
367
493
|
|
|
368
494
|
|
|
369
|
-
#
|
|
495
|
+
# ============================================================
|
|
370
496
|
# Transmitter stubs (legacy)
|
|
371
|
-
#
|
|
497
|
+
# ============================================================
|
|
372
498
|
|
|
373
499
|
_transmitter = None
|
|
374
500
|
|
|
@@ -382,57 +508,52 @@ def get_transmitter_state() -> bool:
|
|
|
382
508
|
return False
|
|
383
509
|
|
|
384
510
|
|
|
385
|
-
#
|
|
511
|
+
# ============================================================
|
|
386
512
|
# Legacy API (state-safe)
|
|
387
|
-
#
|
|
513
|
+
# ============================================================
|
|
388
514
|
|
|
389
515
|
def get_all_devices() -> Dict[int, Tuple[object, str, str]]:
|
|
390
|
-
|
|
516
|
+
_sync_host_devices()
|
|
517
|
+
|
|
391
518
|
with _state_lock:
|
|
392
519
|
out: Dict[int, Tuple[object, str, str]] = {}
|
|
393
520
|
for gid, st in _devices.items():
|
|
394
|
-
if st
|
|
395
|
-
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
|
|
396
523
|
return out
|
|
397
524
|
|
|
525
|
+
|
|
398
526
|
def get_all_devices_str() -> Dict[int, str]:
|
|
399
|
-
|
|
400
|
-
out = _local_devices_str_snapshot()
|
|
527
|
+
_sync_host_devices()
|
|
401
528
|
|
|
402
|
-
|
|
529
|
+
out = _local_devices_str_snapshot()
|
|
403
530
|
host_out = _host_devices_str_snapshot()
|
|
404
531
|
|
|
405
|
-
#
|
|
406
|
-
# try:
|
|
407
|
-
# from ..host import host_tunnel_server as hts # adjust if your canonical import differs
|
|
408
|
-
# reg = hts.get_tunnel_registry(create=False) if hasattr(hts, "get_tunnel_registry") else None
|
|
409
|
-
# print("\n[device_manager] host_out dump")
|
|
410
|
-
# print(f" hts module: {getattr(hts, '__name__', '<?>')}")
|
|
411
|
-
# print(f" registry id: {id(reg) if reg is not None else None}")
|
|
412
|
-
# print(f" host_out count: {len(host_out)}")
|
|
413
|
-
|
|
414
|
-
# if not host_out:
|
|
415
|
-
# print(" (empty)")
|
|
416
|
-
# else:
|
|
417
|
-
# for gid, s in sorted(host_out.items(), key=lambda kv: int(kv[0])):
|
|
418
|
-
# print(f" {gid}: {s}")
|
|
419
|
-
# print()
|
|
420
|
-
# except Exception as e:
|
|
421
|
-
# print(f"[device_manager] host_out dump failed: {e}")
|
|
422
|
-
|
|
423
|
-
# merge with collision handling
|
|
532
|
+
# merge with collision handling (shouldn't happen; keeps UI stable if it does)
|
|
424
533
|
for gid, s in host_out.items():
|
|
425
534
|
if gid in out:
|
|
426
|
-
|
|
427
|
-
alt = -int(gid) - 1 # deterministic "shadow key"
|
|
535
|
+
alt = -int(gid) - 1
|
|
428
536
|
out[alt] = s + " [gid-collision]"
|
|
429
537
|
else:
|
|
430
538
|
out[gid] = s
|
|
431
539
|
|
|
432
540
|
return out
|
|
433
541
|
|
|
542
|
+
|
|
434
543
|
def set_device(device_id: int, salt: str, hash: str):
|
|
435
|
-
#
|
|
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
|
+
|
|
436
557
|
with _state_lock:
|
|
437
558
|
st = _devices.get(int(device_id))
|
|
438
559
|
if not st:
|
|
@@ -440,39 +561,48 @@ def set_device(device_id: int, salt: str, hash: str):
|
|
|
440
561
|
st.salt = str(salt or "")
|
|
441
562
|
st.hsh = str(hash or "")
|
|
442
563
|
|
|
564
|
+
|
|
443
565
|
def device_exists(device_id: int) -> bool:
|
|
444
566
|
did = int(device_id)
|
|
445
567
|
|
|
446
|
-
|
|
568
|
+
_sync_host_devices()
|
|
569
|
+
|
|
570
|
+
# local: connected only (unchanged semantics)
|
|
447
571
|
with _state_lock:
|
|
448
572
|
st = _devices.get(did)
|
|
449
|
-
if st and st.dev is not None:
|
|
573
|
+
if st and st.origin == "local" and st.dev is not None:
|
|
450
574
|
return True
|
|
451
575
|
|
|
452
|
-
# host
|
|
576
|
+
# host: existence as known by registry (unchanged)
|
|
453
577
|
reg = _get_host_registry()
|
|
454
578
|
if reg is None:
|
|
455
579
|
return False
|
|
456
580
|
return bool(reg.is_host_device(str(did)))
|
|
457
581
|
|
|
582
|
+
|
|
458
583
|
def device_is_available(device_id: int) -> bool:
|
|
584
|
+
_sync_host_devices()
|
|
585
|
+
|
|
459
586
|
with _state_lock:
|
|
460
587
|
st = _devices.get(int(device_id))
|
|
461
|
-
if not st or st
|
|
588
|
+
if not st or not _is_connected(st):
|
|
462
589
|
return False
|
|
463
590
|
return (st.salt == "") and (st.hsh == "")
|
|
464
591
|
|
|
592
|
+
|
|
465
593
|
def get_device_by_id(device_id: int):
|
|
466
|
-
|
|
594
|
+
_sync_host_devices()
|
|
595
|
+
|
|
467
596
|
with _state_lock:
|
|
468
597
|
st = _devices.get(int(device_id))
|
|
469
598
|
if not st:
|
|
470
599
|
return None
|
|
471
600
|
return (st.dev, st.salt, st.hsh)
|
|
472
601
|
|
|
602
|
+
|
|
473
603
|
def get_device(*, api_token: str):
|
|
474
604
|
"""
|
|
475
|
-
Legacy behavior:
|
|
605
|
+
Legacy behavior preserved:
|
|
476
606
|
- master_token returns first available device object
|
|
477
607
|
- master_token_<id>[_force] selects a specific device
|
|
478
608
|
- otherwise validate against per-device salt/hash (set via reservations)
|
|
@@ -483,17 +613,24 @@ def get_device(*, api_token: str):
|
|
|
483
613
|
if not api_token:
|
|
484
614
|
return None
|
|
485
615
|
|
|
486
|
-
|
|
616
|
+
_sync_host_devices()
|
|
617
|
+
|
|
487
618
|
with _state_lock:
|
|
488
|
-
snapshot: List[Tuple[int, object, str, str]] = [
|
|
489
|
-
(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)
|
|
490
621
|
for gid, st in _devices.items()
|
|
491
|
-
if st
|
|
622
|
+
if _is_connected(st)
|
|
492
623
|
]
|
|
493
624
|
|
|
625
|
+
# Preserve "master prefers local" behavior
|
|
494
626
|
if api_token == master_token:
|
|
495
|
-
|
|
496
|
-
|
|
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 == "":
|
|
497
634
|
return dev
|
|
498
635
|
return None
|
|
499
636
|
|
|
@@ -502,13 +639,13 @@ def get_device(*, api_token: str):
|
|
|
502
639
|
device_id, force = parsed
|
|
503
640
|
with _state_lock:
|
|
504
641
|
st = _devices.get(int(device_id))
|
|
505
|
-
if not st or st
|
|
642
|
+
if not st or not _is_connected(st):
|
|
506
643
|
return None
|
|
507
644
|
if force:
|
|
508
645
|
return st.dev
|
|
509
646
|
return st.dev if (st.salt == "" and st.hsh == "") else None
|
|
510
647
|
|
|
511
|
-
for _, dev, salt, hsh in snapshot:
|
|
648
|
+
for _, dev, salt, hsh, _origin in snapshot:
|
|
512
649
|
if dev is None:
|
|
513
650
|
continue
|
|
514
651
|
if salt and hsh and validate_token(salt, hsh, api_token):
|
|
@@ -517,47 +654,55 @@ def get_device(*, api_token: str):
|
|
|
517
654
|
return None
|
|
518
655
|
|
|
519
656
|
|
|
520
|
-
#
|
|
521
|
-
#
|
|
522
|
-
#
|
|
657
|
+
# ============================================================
|
|
658
|
+
# Per-device I/O locking (true concurrency support)
|
|
659
|
+
# ============================================================
|
|
523
660
|
|
|
524
661
|
@contextmanager
|
|
525
662
|
def acquire_device(api_token: str) -> Iterator[Tuple[int, object]]:
|
|
526
663
|
"""
|
|
527
664
|
Thread-safe way to use a device:
|
|
528
665
|
with acquire_device(token) as (gid, dev):
|
|
529
|
-
# safe: only one thread at a time per device
|
|
530
666
|
...
|
|
531
667
|
|
|
532
668
|
- Selects the device using the SAME logic as get_device()
|
|
533
669
|
- Then acquires that device's io_lock
|
|
534
670
|
- Yields (gid, dev)
|
|
671
|
+
|
|
672
|
+
NOTE:
|
|
673
|
+
dev can be an adi device (local) OR a VirtualDevice (host).
|
|
535
674
|
"""
|
|
536
675
|
if not api_token:
|
|
537
676
|
raise RuntimeError("missing api_token")
|
|
538
677
|
|
|
539
|
-
|
|
678
|
+
_sync_host_devices()
|
|
679
|
+
|
|
540
680
|
gid: Optional[int] = None
|
|
541
681
|
|
|
542
682
|
if api_token == master_token:
|
|
543
683
|
with _state_lock:
|
|
684
|
+
# prefer local first
|
|
544
685
|
for k, st in _devices.items():
|
|
545
|
-
if st.
|
|
686
|
+
if st.origin == "local" and _is_connected(st) and st.salt == "" and st.hsh == "":
|
|
546
687
|
gid = k
|
|
547
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
|
|
548
694
|
else:
|
|
549
695
|
parsed = parse_mastertoken(api_token)
|
|
550
696
|
if parsed:
|
|
551
697
|
cand, force = parsed
|
|
552
698
|
with _state_lock:
|
|
553
699
|
st = _devices.get(int(cand))
|
|
554
|
-
if st and st
|
|
700
|
+
if st and _is_connected(st):
|
|
555
701
|
if force or (st.salt == "" and st.hsh == ""):
|
|
556
702
|
gid = int(cand)
|
|
557
703
|
else:
|
|
558
|
-
# validate token against per-device salt/hash
|
|
559
704
|
with _state_lock:
|
|
560
|
-
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)]
|
|
561
706
|
for k, dev, salt, hsh in snapshot:
|
|
562
707
|
if salt and hsh and validate_token(salt, hsh, api_token):
|
|
563
708
|
gid = k
|
|
@@ -566,10 +711,9 @@ def acquire_device(api_token: str) -> Iterator[Tuple[int, object]]:
|
|
|
566
711
|
if gid is None:
|
|
567
712
|
raise RuntimeError("no device available / invalid token")
|
|
568
713
|
|
|
569
|
-
# lock the selected device for I/O
|
|
570
714
|
with _state_lock:
|
|
571
715
|
st = _devices.get(int(gid))
|
|
572
|
-
if not st or st.dev is None:
|
|
716
|
+
if not st or not _is_connected(st) or st.dev is None:
|
|
573
717
|
raise RuntimeError("device disappeared / not connected")
|
|
574
718
|
lock = st.io_lock
|
|
575
719
|
dev = st.dev
|
|
@@ -581,22 +725,25 @@ def acquire_device(api_token: str) -> Iterator[Tuple[int, object]]:
|
|
|
581
725
|
lock.release()
|
|
582
726
|
|
|
583
727
|
|
|
584
|
-
#
|
|
728
|
+
# ============================================================
|
|
585
729
|
# Optional: reload device definitions (atomic swap)
|
|
586
|
-
#
|
|
730
|
+
# ============================================================
|
|
587
731
|
|
|
588
732
|
def reload_devices() -> None:
|
|
589
733
|
"""
|
|
590
|
-
Reload devices.env and reconnect devices.
|
|
591
|
-
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.
|
|
592
737
|
"""
|
|
593
738
|
records = _load_device_records()
|
|
594
739
|
|
|
595
|
-
#
|
|
740
|
+
_sync_host_devices() # ensure host entries exist before snapshot
|
|
741
|
+
|
|
596
742
|
with _state_lock:
|
|
597
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"}
|
|
598
745
|
|
|
599
|
-
|
|
746
|
+
tmp_local: Dict[int, _DeviceState] = {}
|
|
600
747
|
tmp_info: Dict[int, str] = {}
|
|
601
748
|
tmp_ser: Dict[int, str] = {}
|
|
602
749
|
|
|
@@ -611,8 +758,10 @@ def reload_devices() -> None:
|
|
|
611
758
|
tmp_ser[int(gid)] = ident
|
|
612
759
|
|
|
613
760
|
salt, hsh = prev_auth.get(int(gid), ("", ""))
|
|
614
|
-
|
|
761
|
+
tmp_local[int(gid)] = _DeviceState(
|
|
615
762
|
dev=dev,
|
|
763
|
+
origin="local",
|
|
764
|
+
online=(dev is not None),
|
|
616
765
|
salt=salt,
|
|
617
766
|
hsh=hsh,
|
|
618
767
|
name=name,
|
|
@@ -623,7 +772,14 @@ def reload_devices() -> None:
|
|
|
623
772
|
|
|
624
773
|
with _state_lock:
|
|
625
774
|
_devices.clear()
|
|
626
|
-
|
|
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)
|
|
627
783
|
|
|
628
784
|
devices_info.clear()
|
|
629
785
|
devices_info.update(tmp_info)
|
|
@@ -631,7 +787,10 @@ def reload_devices() -> None:
|
|
|
631
787
|
device_serialization.clear()
|
|
632
788
|
device_serialization.update(tmp_ser)
|
|
633
789
|
|
|
790
|
+
# refresh host online/offline status after reload (best-effort)
|
|
791
|
+
_sync_host_devices(force=True)
|
|
792
|
+
|
|
634
793
|
|
|
635
794
|
def set_pluto(ip: str = "192.168.2.1"):
|
|
636
795
|
# kept for compatibility (no-op)
|
|
637
|
-
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.95 → remoterf_server-0.1.96}/src/remoteRF_server/common/grpc/__init__.py
RENAMED
|
File without changes
|
{remoterf_server-0.1.95 → 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.95 → remoterf_server-0.1.96}/src/remoteRF_server/common/grpc/grpc_pb2.py
RENAMED
|
File without changes
|
{remoterf_server-0.1.95 → remoterf_server-0.1.96}/src/remoteRF_server/common/grpc/grpc_pb2_grpc.py
RENAMED
|
File without changes
|
{remoterf_server-0.1.95 → remoterf_server-0.1.96}/src/remoteRF_server/common/utils/__init__.py
RENAMED
|
File without changes
|
{remoterf_server-0.1.95 → remoterf_server-0.1.96}/src/remoteRF_server/common/utils/ansi_codes.py
RENAMED
|
File without changes
|
{remoterf_server-0.1.95 → remoterf_server-0.1.96}/src/remoteRF_server/common/utils/api_token.py
RENAMED
|
File without changes
|
{remoterf_server-0.1.95 → remoterf_server-0.1.96}/src/remoteRF_server/common/utils/db_connection.py
RENAMED
|
File without changes
|
{remoterf_server-0.1.95 → remoterf_server-0.1.96}/src/remoteRF_server/common/utils/db_location.py
RENAMED
|
File without changes
|
{remoterf_server-0.1.95 → remoterf_server-0.1.96}/src/remoteRF_server/common/utils/list_string.py
RENAMED
|
File without changes
|
{remoterf_server-0.1.95 → 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.95 → remoterf_server-0.1.96}/src/remoteRF_server/host/host_directory_store.py
RENAMED
|
File without changes
|
{remoterf_server-0.1.95 → 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.95 → 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.95 → remoterf_server-0.1.96}/src/remoteRF_server/server/user_group_cli.py
RENAMED
|
File without changes
|
{remoterf_server-0.1.95 → 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.95 → remoterf_server-0.1.96}/src/remoteRF_server.egg-info/dependency_links.txt
RENAMED
|
File without changes
|
{remoterf_server-0.1.95 → remoterf_server-0.1.96}/src/remoteRF_server.egg-info/entry_points.txt
RENAMED
|
File without changes
|
|
File without changes
|
{remoterf_server-0.1.95 → remoterf_server-0.1.96}/src/remoteRF_server.egg-info/top_level.txt
RENAMED
|
File without changes
|