remoteRF-server 1.1.0.dev35__py3-none-any.whl → 1.1.0.dev37__py3-none-any.whl
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/server/commands.py +1 -0
- remoteRF_server/server/device_manager.py +14 -0
- remoteRF_server/server/federation/cli.py +74 -9
- remoteRF_server/server/federation/cluster.py +67 -9
- {remoterf_server-1.1.0.dev35.dist-info → remoterf_server-1.1.0.dev37.dist-info}/METADATA +1 -1
- {remoterf_server-1.1.0.dev35.dist-info → remoterf_server-1.1.0.dev37.dist-info}/RECORD +10 -10
- {remoterf_server-1.1.0.dev35.dist-info → remoterf_server-1.1.0.dev37.dist-info}/WHEEL +0 -0
- {remoterf_server-1.1.0.dev35.dist-info → remoterf_server-1.1.0.dev37.dist-info}/entry_points.txt +0 -0
- {remoterf_server-1.1.0.dev35.dist-info → remoterf_server-1.1.0.dev37.dist-info}/licenses/LICENSE +0 -0
- {remoterf_server-1.1.0.dev35.dist-info → remoterf_server-1.1.0.dev37.dist-info}/top_level.txt +0 -0
|
@@ -149,6 +149,7 @@ def _table() -> list[Command]:
|
|
|
149
149
|
Command(("global leave",), "Stop participating", _global("leave"), "Global"),
|
|
150
150
|
# ---- clusters (sharing devices with other labs) ----
|
|
151
151
|
Command(("cluster list",), "Clusters shared with other labs", _federation("list"), "Clusters"),
|
|
152
|
+
Command(("cluster info",), "Every lab, shared group and device in a cluster", _federation("info"), "Clusters"),
|
|
152
153
|
Command(("cluster create",), "Start a cluster: cluster create <name>", _federation("create"), "Clusters"),
|
|
153
154
|
Command(("cluster invite",), "Invite code for a lab: cluster invite <name>", _federation("invite"), "Clusters"),
|
|
154
155
|
Command(("cluster join",), "Join with a code: cluster join <code>", _federation("join"), "Clusters"),
|
|
@@ -704,6 +704,20 @@ def get_registered_devices() -> Dict[int, Tuple[object, str, str]]:
|
|
|
704
704
|
gid: (st.dev, st.salt, st.hsh) for gid, st in _devices.items()
|
|
705
705
|
}
|
|
706
706
|
|
|
707
|
+
def describe_devices(ids) -> list[dict]:
|
|
708
|
+
"""Name, type and live state for the given ids (unknown ids are skipped)."""
|
|
709
|
+
_sync_host_devices()
|
|
710
|
+
out = []
|
|
711
|
+
with _state_lock:
|
|
712
|
+
for gid in ids:
|
|
713
|
+
st = _devices.get(int(gid))
|
|
714
|
+
if st is None:
|
|
715
|
+
continue
|
|
716
|
+
out.append({"id": int(gid), "name": (st.name or f"device-{gid}").strip(),
|
|
717
|
+
"type": st.dtype or "", "online": _is_connected(st)})
|
|
718
|
+
return out
|
|
719
|
+
|
|
720
|
+
|
|
707
721
|
def get_all_devices_str() -> Dict[int, str]:
|
|
708
722
|
_sync_host_devices()
|
|
709
723
|
|
|
@@ -38,6 +38,10 @@ def _lab(party: dict) -> str:
|
|
|
38
38
|
return f"{party['display_name']} ({short}…)" if party.get("display_name") else party["deployment_id"]
|
|
39
39
|
|
|
40
40
|
|
|
41
|
+
def _n(count: int, noun: str) -> str:
|
|
42
|
+
return f"{count} {noun}{'' if count == 1 else 's'}"
|
|
43
|
+
|
|
44
|
+
|
|
41
45
|
def _day(epoch) -> str:
|
|
42
46
|
import datetime
|
|
43
47
|
|
|
@@ -64,6 +68,7 @@ def cmd_clusters() -> int:
|
|
|
64
68
|
for index, item in enumerate(items):
|
|
65
69
|
if index:
|
|
66
70
|
rows.append("")
|
|
71
|
+
owner_row = None
|
|
67
72
|
if item["role"] == "owner":
|
|
68
73
|
invites = item["open_invites"]
|
|
69
74
|
rows.append(f"{item['name']} (yours)" + (f" {invites} open invite{'s' if invites != 1 else ''}" if invites else ""))
|
|
@@ -72,20 +77,25 @@ def cmd_clusters() -> int:
|
|
|
72
77
|
if not item["members"] and not invites:
|
|
73
78
|
rows.append(f" nobody yet: `cluster invite {item['name']}`")
|
|
74
79
|
else:
|
|
75
|
-
owner = {"display_name": item.get("peer_name"), "deployment_id": item["peer"]}
|
|
80
|
+
owner_row = owner = {"display_name": item.get("peer_name"), "deployment_id": item["peer"]}
|
|
76
81
|
rows.append(f"{item['name']} {item['state']}")
|
|
77
82
|
labs = [(owner, "admin", "", None), (me, "member", "", None)] + [
|
|
78
83
|
(p, "member", p["state"] or "joining", None) for p in item.get("peers", [])]
|
|
79
84
|
for lab, role, lab_state, since in labs:
|
|
80
|
-
you =
|
|
81
|
-
line = f" {_lab(lab)} {role}
|
|
85
|
+
you = lab["deployment_id"] == me["deployment_id"]
|
|
86
|
+
line = f" {_lab(lab)} {role}" + (" (you)" if you else "")
|
|
82
87
|
if lab_state and lab_state != "active":
|
|
83
88
|
line += f" {lab_state}"
|
|
84
89
|
if since:
|
|
85
90
|
line += f" since {_day(since)}"
|
|
86
91
|
rows.append(line)
|
|
87
|
-
|
|
88
|
-
|
|
92
|
+
if you:
|
|
93
|
+
rows.append(" sharing: " + (", ".join(
|
|
94
|
+
f"{g['group_name']} ({_n(len(g['devices']), 'device')})" for g in item["groups"]) or "nothing"))
|
|
95
|
+
else:
|
|
96
|
+
sharing = lab.get("sharing") if lab is not owner_row else item.get("peer_sharing")
|
|
97
|
+
if sharing:
|
|
98
|
+
rows.append(f" sharing {_n(sharing['groups'], 'group')}, {_n(sharing['devices'], 'device')}")
|
|
89
99
|
rows.append(f" {item['students']} of your users enrolled, {item['visitors']} visiting")
|
|
90
100
|
print()
|
|
91
101
|
print_panel("Clusters", rows, literal=True)
|
|
@@ -93,6 +103,58 @@ def cmd_clusters() -> int:
|
|
|
93
103
|
return 0
|
|
94
104
|
|
|
95
105
|
|
|
106
|
+
def cmd_cluster_info(name: str) -> int:
|
|
107
|
+
"""Every lab in the cluster, each shared group with its limits, each device."""
|
|
108
|
+
from ...common.utils.panel import print_panel
|
|
109
|
+
from ..device_manager import describe_devices
|
|
110
|
+
from ..user_group_cli import _fmt_duration
|
|
111
|
+
from . import cluster
|
|
112
|
+
|
|
113
|
+
config = load_config()
|
|
114
|
+
state = load_state(Path(config["identity_dir"]))
|
|
115
|
+
item = {i["name"]: i for i in cluster.list_clusters()}.get(name)
|
|
116
|
+
if item is None:
|
|
117
|
+
raise cluster.ClusterError(f"no cluster named {name}")
|
|
118
|
+
me = {"display_name": config["display_name"], "deployment_id": state.deployment_id}
|
|
119
|
+
mine = {"groups": len(item["groups"]), "detail": [
|
|
120
|
+
{"name": g["group_name"], "max_reservation_time_sec": g["max_reservation_time_sec"],
|
|
121
|
+
"max_reservations": g["max_reservations"], "lifetime_sec": g["lifetime_sec"],
|
|
122
|
+
"devices": describe_devices(g["devices"])}
|
|
123
|
+
for g in item["groups"]]}
|
|
124
|
+
if item["role"] == "owner":
|
|
125
|
+
labs = [(me, "admin", mine)] + [(m, "member", m.get("sharing")) for m in item["members"]]
|
|
126
|
+
else:
|
|
127
|
+
owner = {"display_name": item.get("peer_name"), "deployment_id": item["peer"]}
|
|
128
|
+
labs = [(owner, "admin", item.get("peer_sharing")), (me, "member", mine)] + [
|
|
129
|
+
(p, "member", p.get("sharing")) for p in item.get("peers", [])]
|
|
130
|
+
rows = []
|
|
131
|
+
for index, (lab, role, sharing) in enumerate(labs):
|
|
132
|
+
if index:
|
|
133
|
+
rows.append("")
|
|
134
|
+
rows.append(f"{_lab(lab)} {role}" + (" (you)" if lab["deployment_id"] == me["deployment_id"] else ""))
|
|
135
|
+
if sharing is None:
|
|
136
|
+
rows.append(" (no details from this lab yet)")
|
|
137
|
+
continue
|
|
138
|
+
if not sharing.get("detail"):
|
|
139
|
+
rows.append(" shares nothing")
|
|
140
|
+
for group in sharing["detail"]:
|
|
141
|
+
limits = " · ".join([
|
|
142
|
+
_n(len(group["devices"]), "device"),
|
|
143
|
+
f"{_fmt_duration(group['max_reservation_time_sec'])} max per reservation",
|
|
144
|
+
f"{_n(group['max_reservations'], 'reservation')} at once",
|
|
145
|
+
"no expiry" if group["lifetime_sec"] <= 0 else f"expires after {_fmt_duration(group['lifetime_sec'])}",
|
|
146
|
+
])
|
|
147
|
+
rows.append(f" {group['name']} — {limits}")
|
|
148
|
+
width = max([len(d["name"]) for d in group["devices"]] + [4])
|
|
149
|
+
for device in group["devices"]:
|
|
150
|
+
rows.append(f" {device['id']:>6} {device['name']:<{width}} {device['type'] or '?':<12} "
|
|
151
|
+
+ ("online" if device["online"] else "offline"))
|
|
152
|
+
print()
|
|
153
|
+
print_panel(f"Cluster {name}", rows, literal=True)
|
|
154
|
+
print()
|
|
155
|
+
return 0
|
|
156
|
+
|
|
157
|
+
|
|
96
158
|
def _ask_for(verb: str) -> str:
|
|
97
159
|
"""Prompt for the missing cluster name (or invite code), offering the obvious default."""
|
|
98
160
|
from . import cluster
|
|
@@ -100,7 +162,7 @@ def _ask_for(verb: str) -> str:
|
|
|
100
162
|
if verb == "join":
|
|
101
163
|
return _input("Invite code from the other lab: ")
|
|
102
164
|
names = [name for name, item in sorted(cluster.load().items()) if item.get("role") == "owner"]
|
|
103
|
-
if verb in ("remove", "groups", "codes"):
|
|
165
|
+
if verb in ("remove", "groups", "codes", "info"):
|
|
104
166
|
names = sorted(cluster.load())
|
|
105
167
|
if verb == "accept":
|
|
106
168
|
names = [name for name, item in sorted(cluster.load().items()) if item.get("state") == "pending"]
|
|
@@ -108,6 +170,7 @@ def _ask_for(verb: str) -> str:
|
|
|
108
170
|
"invite": "Which cluster to invite a lab into",
|
|
109
171
|
"accept": "Which cluster's pending join to accept",
|
|
110
172
|
"groups": "Which cluster to share groups with",
|
|
173
|
+
"info": "Which cluster to show",
|
|
111
174
|
"codes": "Which cluster to make student codes for",
|
|
112
175
|
"remove": "Which cluster to leave (revokes the contract)"}[verb]
|
|
113
176
|
if verb != "create" and names:
|
|
@@ -163,7 +226,7 @@ def _input(prompt: str) -> str:
|
|
|
163
226
|
|
|
164
227
|
|
|
165
228
|
_USAGE = ("Usage: cluster list | cluster create|invite|remove <name> | cluster join <code> | "
|
|
166
|
-
"cluster groups [<name>] | cluster codes [<name>] [count]")
|
|
229
|
+
"cluster info [<name>] | cluster groups [<name>] | cluster codes [<name>] [count]")
|
|
167
230
|
|
|
168
231
|
|
|
169
232
|
def cmd_cluster(argv: list[str]) -> int:
|
|
@@ -179,7 +242,7 @@ def cmd_cluster(argv: list[str]) -> int:
|
|
|
179
242
|
words = [_ask_for(verb), words[0]] # `cluster codes 3`: the count came first
|
|
180
243
|
if not words[0]:
|
|
181
244
|
return 2
|
|
182
|
-
if not words and verb in ("create", "invite", "accept", "join", "remove", "groups", "codes"):
|
|
245
|
+
if not words and verb in ("create", "invite", "accept", "join", "remove", "groups", "codes", "info"):
|
|
183
246
|
# Leaving the argument out asks for it (docs/deployment-groups.md §8).
|
|
184
247
|
answer = _ask_for(verb)
|
|
185
248
|
if not answer:
|
|
@@ -205,6 +268,8 @@ def cmd_cluster(argv: list[str]) -> int:
|
|
|
205
268
|
_print(f" Accepted. {arg} activates once both servers confirm (about a minute).", Sty.GRAY)
|
|
206
269
|
elif verb == "remove" and arg:
|
|
207
270
|
_print(f" {arg}: {cluster.remove(arg)}.", Sty.GRAY)
|
|
271
|
+
elif verb == "info" and arg:
|
|
272
|
+
return cmd_cluster_info(arg)
|
|
208
273
|
elif verb == "groups" and len(words) == 1:
|
|
209
274
|
cluster._find(cluster.load(), arg)
|
|
210
275
|
return _share_groups(arg)
|
|
@@ -233,7 +298,7 @@ def main(argv) -> int:
|
|
|
233
298
|
# `cluster <verb> …` from the table; `global cluster …` is the old spelling.
|
|
234
299
|
if argv and argv[0] in ("cluster", "clusters"):
|
|
235
300
|
argv = argv[1:] or ["list"]
|
|
236
|
-
if argv and argv[0] in ("list", "create", "invite", "join", "accept", "remove", "groups", "codes"):
|
|
301
|
+
if argv and argv[0] in ("list", "create", "invite", "join", "accept", "remove", "groups", "codes", "info"):
|
|
237
302
|
return cmd_cluster(argv)
|
|
238
303
|
print(_USAGE, file=sys.stderr)
|
|
239
304
|
return 2
|
|
@@ -404,7 +404,8 @@ def list_clusters() -> list[dict]:
|
|
|
404
404
|
except ContractError:
|
|
405
405
|
pass
|
|
406
406
|
members.append({"deployment_id": peer, "display_name": member.get("display_name"),
|
|
407
|
-
"state": state, "joined_at": member.get("joined_at")
|
|
407
|
+
"state": state, "joined_at": member.get("joined_at"),
|
|
408
|
+
"sharing": member.get("sharing")})
|
|
408
409
|
item.update(members=members, open_invites=len(cluster["invites"]))
|
|
409
410
|
else:
|
|
410
411
|
state = cluster.get("state")
|
|
@@ -414,9 +415,11 @@ def list_clusters() -> list[dict]:
|
|
|
414
415
|
state = store.show(cluster["contract_id"], 1)["state"]
|
|
415
416
|
except ContractError:
|
|
416
417
|
pass
|
|
417
|
-
peers = [{"deployment_id": peer, "display_name": party.get("display_name"), "state": party.get("state")
|
|
418
|
+
peers = [{"deployment_id": peer, "display_name": party.get("display_name"), "state": party.get("state"),
|
|
419
|
+
"sharing": party.get("sharing")}
|
|
418
420
|
for peer, party in sorted(cluster.get("peers", {}).items())]
|
|
419
|
-
item.update(peer=cluster["peer"], peer_name=cluster.get("peer_name"), state=state, peers=peers
|
|
421
|
+
item.update(peer=cluster["peer"], peer_name=cluster.get("peer_name"), state=state, peers=peers,
|
|
422
|
+
peer_sharing=cluster.get("sharing"))
|
|
420
423
|
item.update(groups=groups(name), **_account_counts(name))
|
|
421
424
|
out.append(item)
|
|
422
425
|
return out
|
|
@@ -445,12 +448,34 @@ def groups(name: str) -> list[dict]:
|
|
|
445
448
|
out = []
|
|
446
449
|
with _usergroups() as conn:
|
|
447
450
|
for group_id in cluster.get("groups", []):
|
|
448
|
-
row = conn.execute(
|
|
451
|
+
row = conn.execute(
|
|
452
|
+
"SELECT group_name,devices_whitelist,max_reservation_time_sec,max_reservations,lifetime_sec "
|
|
453
|
+
"FROM UserGroups WHERE group_id=?", (group_id,)).fetchone()
|
|
449
454
|
if row is not None:
|
|
450
|
-
|
|
455
|
+
try:
|
|
456
|
+
devices = {int(d) for d in json.loads(row[1] or "[]")}
|
|
457
|
+
except (ValueError, TypeError):
|
|
458
|
+
devices = set()
|
|
459
|
+
out.append({"group_id": group_id, "group_name": row[0], "devices": sorted(devices),
|
|
460
|
+
"max_reservation_time_sec": int(row[2]), "max_reservations": int(row[3]),
|
|
461
|
+
"lifetime_sec": int(row[4])})
|
|
451
462
|
return out
|
|
452
463
|
|
|
453
464
|
|
|
465
|
+
def _sharing(name: str) -> dict:
|
|
466
|
+
"""What the other labs in the cluster get told about what we share:
|
|
467
|
+
counts, plus each shared group's limits and devices (`cluster info`)."""
|
|
468
|
+
from ..device_manager import describe_devices
|
|
469
|
+
|
|
470
|
+
shared = groups(name)
|
|
471
|
+
detail = [{
|
|
472
|
+
"name": g["group_name"], "max_reservation_time_sec": g["max_reservation_time_sec"],
|
|
473
|
+
"max_reservations": g["max_reservations"], "lifetime_sec": g["lifetime_sec"],
|
|
474
|
+
"devices": describe_devices(g["devices"]),
|
|
475
|
+
} for g in shared]
|
|
476
|
+
return {"groups": len(shared), "devices": len({d for g in shared for d in g["devices"]}), "detail": detail}
|
|
477
|
+
|
|
478
|
+
|
|
454
479
|
def _account_counts(name: str) -> dict:
|
|
455
480
|
with _usergroups() as conn:
|
|
456
481
|
rows = dict(conn.execute(
|
|
@@ -687,6 +712,33 @@ def _publish_roster(name: str, cluster: dict) -> None:
|
|
|
687
712
|
cluster["roster"] = active
|
|
688
713
|
|
|
689
714
|
|
|
715
|
+
def _publish_status(name: str, cluster: dict) -> None:
|
|
716
|
+
"""Any lab: post our counts for this cluster when they change."""
|
|
717
|
+
sharing = _sharing(name)
|
|
718
|
+
if sharing == cluster.get("status"):
|
|
719
|
+
return
|
|
720
|
+
key, _, me = g._signing_material()
|
|
721
|
+
owner = me if cluster["role"] == "owner" else cluster["peer"]
|
|
722
|
+
status = {"cluster_name": name, "owner": owner, "deployment_id": me, "issued_at": int(time.time()), **sharing}
|
|
723
|
+
signature = base64.b64encode(key.sign(canonical_json(status).encode())).decode("ascii")
|
|
724
|
+
g.call("POST", "/v2/broker/clusters/roster", {"protocol_version": "2", "status": status, "signature": signature})
|
|
725
|
+
cluster["status"] = sharing
|
|
726
|
+
|
|
727
|
+
|
|
728
|
+
def _take_statuses(name: str, cluster: dict, statuses: list[dict]) -> None:
|
|
729
|
+
"""Attach each partner's verified counts to its row."""
|
|
730
|
+
owner = g._signing_material()[2] if cluster["role"] == "owner" else cluster["peer"]
|
|
731
|
+
parties = dict(_parties(cluster))
|
|
732
|
+
for item in statuses:
|
|
733
|
+
status = item.get("status") or {}
|
|
734
|
+
peer = status.get("deployment_id")
|
|
735
|
+
if status.get("owner") != owner or status.get("cluster_name") != name or peer not in parties:
|
|
736
|
+
continue
|
|
737
|
+
if _roster_signed_by(peer, status, item.get("signature", "")):
|
|
738
|
+
parties[peer]["sharing"] = {"groups": status["groups"], "devices": status["devices"],
|
|
739
|
+
"detail": status.get("detail", [])}
|
|
740
|
+
|
|
741
|
+
|
|
690
742
|
def _roster_signed_by(owner: str, roster: dict, signature: str) -> bool:
|
|
691
743
|
from cryptography.exceptions import InvalidSignature
|
|
692
744
|
from cryptography.hazmat.primitives.asymmetric.ed25519 import Ed25519PublicKey
|
|
@@ -774,7 +826,7 @@ def tick(notify=None, home=None) -> None:
|
|
|
774
826
|
return
|
|
775
827
|
directory = None
|
|
776
828
|
broker_invites = None
|
|
777
|
-
|
|
829
|
+
board = None # rosters and status notes, one GET for all clusters
|
|
778
830
|
for name, cluster in clusters.items():
|
|
779
831
|
if cluster["role"] == "owner":
|
|
780
832
|
if cluster["invites"]:
|
|
@@ -797,9 +849,15 @@ def tick(notify=None, home=None) -> None:
|
|
|
797
849
|
if directory is None:
|
|
798
850
|
directory = g.call("GET", "/v2/broker/contracts")["contracts"]
|
|
799
851
|
_advance(cluster, cluster["peer"], directory, label=name, notify=notify, is_member=True)
|
|
800
|
-
|
|
801
|
-
|
|
802
|
-
|
|
852
|
+
if board is None:
|
|
853
|
+
board = g.call("GET", "/v2/broker/clusters/roster")
|
|
854
|
+
if cluster["role"] != "owner":
|
|
855
|
+
_mesh(name, cluster, board["rosters"], directory, notify)
|
|
856
|
+
try:
|
|
857
|
+
_publish_status(name, cluster)
|
|
858
|
+
except g.GlobalError as exc:
|
|
859
|
+
notify(f"[cluster] {name}: could not publish what we share: {exc}")
|
|
860
|
+
_take_statuses(name, cluster, board.get("statuses", []))
|
|
803
861
|
save(clusters)
|
|
804
862
|
if home is not None:
|
|
805
863
|
enroll_students(home, notify)
|
|
@@ -52,10 +52,10 @@ remoteRF_server/host/host_tunnel_server.py,sha256=sz06xOtFbaDZSy1UQfw6faKhJmuOy5
|
|
|
52
52
|
remoteRF_server/server/__init__.py,sha256=-1h_xQjiE66oiWRO-vnLGJ4smpgUNhFuV-ZreW9_tD4,667
|
|
53
53
|
remoteRF_server/server/acc_perms.py,sha256=2loCvstVu8uvsb2uCv2Sk7iNQ5GyO_sBIkPQFzqOaRI,13334
|
|
54
54
|
remoteRF_server/server/cert_provider.py,sha256=CRDrZL4DSf-eL6wzPJzwucrzPLOr2PvNRq5V--oZtr0,6272
|
|
55
|
-
remoteRF_server/server/commands.py,sha256=
|
|
55
|
+
remoteRF_server/server/commands.py,sha256=6HcvEpznOblV57NMuOJTl2VY2js1wbuLV9mwxGDqMeg,17399
|
|
56
56
|
remoteRF_server/server/console.py,sha256=J91LfwPVmBgielOxfE740NOYduekggyZkqi-47y_A4w,4472
|
|
57
57
|
remoteRF_server/server/control.py,sha256=Uq1Fk0zIoHHX_EXfz3OuNle8urvO_UhAiB5Lk5cs2e4,5177
|
|
58
|
-
remoteRF_server/server/device_manager.py,sha256=
|
|
58
|
+
remoteRF_server/server/device_manager.py,sha256=PPuCwlPSmjfVpCqy_hCaOr9WvFgE8mqtLMhWOAgXNDA,39281
|
|
59
59
|
remoteRF_server/server/direct_path.py,sha256=CQs0uPTANJq6Bn-TzsV0APmD3hYKH8xYvczXZZk1OXk,15229
|
|
60
60
|
remoteRF_server/server/dynamic_v2.py,sha256=AMhQEFhNAvCNcF-PdfCCQQLmeoaR7s1Os8et1KFGn_8,70814
|
|
61
61
|
remoteRF_server/server/global_onboarding.py,sha256=boLAKXCKvcsIY3TDiFwduMaHdDMB9Cr104MyWJYZ-BA,39891
|
|
@@ -70,8 +70,8 @@ remoteRF_server/server/deployment/__init__.py,sha256=YV86uafd8XiGD7sLaqNfO5xxmt5
|
|
|
70
70
|
remoteRF_server/server/deployment/capabilities.py,sha256=20RxmGG03VoQQZnV3jGhc-3i9B5hOl5fCopnsErFjPk,872
|
|
71
71
|
remoteRF_server/server/federation/__init__.py,sha256=auqdGiGnEGIKQiXX2jFVt8iWmfyZ-V3w4dUmtWDKAZE,735
|
|
72
72
|
remoteRF_server/server/federation/admin.py,sha256=t3F17FIytekzclPICmaFJEA4-F5OuWcDx3rgmFga17A,9187
|
|
73
|
-
remoteRF_server/server/federation/cli.py,sha256=
|
|
74
|
-
remoteRF_server/server/federation/cluster.py,sha256=
|
|
73
|
+
remoteRF_server/server/federation/cli.py,sha256=rMA81mk656tP29cPntTIlLE95JTUeJ-RdNZSTU_eJuI,13995
|
|
74
|
+
remoteRF_server/server/federation/cluster.py,sha256=bQpT7jq-2d87ae6AkmsKktozBsULSy3QrIbAVgBLMQg,36238
|
|
75
75
|
remoteRF_server/server/federation/contracts.py,sha256=NYh7lb_9etJHFfzXwevjC4HfSBUypLxg5xGeBgydLF4,29305
|
|
76
76
|
remoteRF_server/server/federation/destination.py,sha256=XawmGCiVbL_MfNNn8xwV9_bYxUDW1L7efCpL11K2JAc,35909
|
|
77
77
|
remoteRF_server/server/federation/devices.py,sha256=n5MvnD0UZyl4sFpiekG9ProanWXTYizYq6YchCflLgY,18061
|
|
@@ -90,9 +90,9 @@ remoteRF_server/tools/gen_certs.py,sha256=Vbyu8JjRu__UYPVmyCRXt5SOw0Za1HV7g1md3G
|
|
|
90
90
|
remoteRF_server/tools/gist_status.py,sha256=SFv2iQgQrIKT9gxKrU-_Dr7S0p0rE4wQM6J3OC6FcoM,4649
|
|
91
91
|
remoteRF_server/tools/gist_status_testing.py,sha256=IYw7UkPjAUpKDTtIlnYGb5g6SJkwz4TRGUfJJ6f4LhQ,2690
|
|
92
92
|
remoteRF_server/tools/introspect_uhd.py,sha256=vUpPb0rM9tsHPxWadS3EfMDE-gMd-TvNV0TjrZOQqBY,4531
|
|
93
|
-
remoterf_server-1.1.0.
|
|
94
|
-
remoterf_server-1.1.0.
|
|
95
|
-
remoterf_server-1.1.0.
|
|
96
|
-
remoterf_server-1.1.0.
|
|
97
|
-
remoterf_server-1.1.0.
|
|
98
|
-
remoterf_server-1.1.0.
|
|
93
|
+
remoterf_server-1.1.0.dev37.dist-info/licenses/LICENSE,sha256=OXLcl0T2SZ8Pmy2_dmlvKuetivmyPd5m1q-Gyd-zaYY,35149
|
|
94
|
+
remoterf_server-1.1.0.dev37.dist-info/METADATA,sha256=L_odnm8vq5_QVx26laNnT9KsfhijKBkDPfgubrd_hig,42069
|
|
95
|
+
remoterf_server-1.1.0.dev37.dist-info/WHEEL,sha256=YVMoNqKzERt-wjUZwJ33xBGAwnFl-4cqbYkTtWa4itE,91
|
|
96
|
+
remoterf_server-1.1.0.dev37.dist-info/entry_points.txt,sha256=_bbKk65PfYi-n6zUjqOiOwcalf_Ws1TCGATIe1TukxE,136
|
|
97
|
+
remoterf_server-1.1.0.dev37.dist-info/top_level.txt,sha256=NuYWe8dSx3qdbxshWBwOgx68EHmJ9I8Zvxam4ARsQ_I,30
|
|
98
|
+
remoterf_server-1.1.0.dev37.dist-info/RECORD,,
|
|
File without changes
|
{remoterf_server-1.1.0.dev35.dist-info → remoterf_server-1.1.0.dev37.dist-info}/entry_points.txt
RENAMED
|
File without changes
|
{remoterf_server-1.1.0.dev35.dist-info → remoterf_server-1.1.0.dev37.dist-info}/licenses/LICENSE
RENAMED
|
File without changes
|
{remoterf_server-1.1.0.dev35.dist-info → remoterf_server-1.1.0.dev37.dist-info}/top_level.txt
RENAMED
|
File without changes
|