rosotacom-dev 2.5.dev72__py3-none-any.whl → 2.5.dev74__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.
- rosotacom/cli.py +27 -5
- rosotacom/cli_benchmark.py +29 -11
- rosotacom/network_profiles.py +38 -1
- {rosotacom_dev-2.5.dev72.dist-info → rosotacom_dev-2.5.dev74.dist-info}/METADATA +1 -1
- {rosotacom_dev-2.5.dev72.dist-info → rosotacom_dev-2.5.dev74.dist-info}/RECORD +9 -9
- {rosotacom_dev-2.5.dev72.dist-info → rosotacom_dev-2.5.dev74.dist-info}/WHEEL +0 -0
- {rosotacom_dev-2.5.dev72.dist-info → rosotacom_dev-2.5.dev74.dist-info}/entry_points.txt +0 -0
- {rosotacom_dev-2.5.dev72.dist-info → rosotacom_dev-2.5.dev74.dist-info}/licenses/LICENSE.txt +0 -0
- {rosotacom_dev-2.5.dev72.dist-info → rosotacom_dev-2.5.dev74.dist-info}/top_level.txt +0 -0
rosotacom/cli.py
CHANGED
|
@@ -794,6 +794,31 @@ def _new_instance_id() -> str:
|
|
|
794
794
|
return hashlib.sha1(seed.encode("utf-8")).hexdigest()[:8]
|
|
795
795
|
|
|
796
796
|
|
|
797
|
+
#: The timestamp every instance directory carries between its session slug and
|
|
798
|
+
#: its id: `<slug>_YYYY-MM-DD_HH-MM-SS_<id>`. Spelled out digit by digit rather
|
|
799
|
+
#: than left as `*`, because a session's name may be a *prefix* of another
|
|
800
|
+
#: session's name and `f"{slug}_*"` then collects the other session's instances
|
|
801
|
+
#: too. `ccng_remote_assist_*` matches every `ccng_remote_assist_kilted_dds_*`,
|
|
802
|
+
#: and because the matches are ordered by name, `[-1]` answers with the wrong
|
|
803
|
+
#: session's run whenever the longer name sorts after a digit — which `k` does.
|
|
804
|
+
#:
|
|
805
|
+
#: That is not hypothetical. On 2026-08-21 it made `rosotacom status
|
|
806
|
+
#: ccng_remote_assist` report a `ccng_remote_assist_kilted_dds` run that had
|
|
807
|
+
#: been shut down 45 minutes earlier, so three two-host runs concluded their
|
|
808
|
+
#: link never came up while it was in fact carrying traffic for eleven minutes.
|
|
809
|
+
#: Nothing in the reading said it came from another session.
|
|
810
|
+
_INSTANCE_STAMP_GLOB = "[0-9][0-9][0-9][0-9]-[0-9][0-9]-[0-9][0-9]_[0-9][0-9]-[0-9][0-9]-[0-9][0-9]"
|
|
811
|
+
|
|
812
|
+
|
|
813
|
+
def _instance_glob(session_slug: str, instance_id: str | None = None) -> str:
|
|
814
|
+
"""The pattern matching this session's instances, and no other session's.
|
|
815
|
+
|
|
816
|
+
With the stamp pinned, the names sort chronologically, so `sorted(...)[-1]`
|
|
817
|
+
is the newest instance rather than merely the last name.
|
|
818
|
+
"""
|
|
819
|
+
return f"*/{session_slug}_{_INSTANCE_STAMP_GLOB}_{instance_id or '*'}"
|
|
820
|
+
|
|
821
|
+
|
|
797
822
|
def _session_instances_root(runtime: RuntimeConfig) -> Path:
|
|
798
823
|
return (runtime.session_instances_dir or (Path.cwd() / "session-instances")).resolve()
|
|
799
824
|
|
|
@@ -818,7 +843,7 @@ def _resolve_session_instance(
|
|
|
818
843
|
resolved_instance_id = _safe_path_token(instance_id or _new_instance_id())
|
|
819
844
|
|
|
820
845
|
if instance_id:
|
|
821
|
-
matches = sorted(root.glob(
|
|
846
|
+
matches = sorted(root.glob(_instance_glob(session_slug, resolved_instance_id)))
|
|
822
847
|
if matches:
|
|
823
848
|
host_dir = matches[-1].resolve()
|
|
824
849
|
else:
|
|
@@ -8781,10 +8806,7 @@ def smoke(args: argparse.Namespace) -> int:
|
|
|
8781
8806
|
def _find_latest_instance_dir(runtime: RuntimeConfig, session: ResolvedSession, instance_id: str | None) -> Path:
|
|
8782
8807
|
root = _session_instances_root(runtime)
|
|
8783
8808
|
session_slug = _session_instance_slug(session, runtime)
|
|
8784
|
-
if instance_id
|
|
8785
|
-
pattern = f"*/{session_slug}_*_{_safe_path_token(instance_id)}"
|
|
8786
|
-
else:
|
|
8787
|
-
pattern = f"*/{session_slug}_*"
|
|
8809
|
+
pattern = _instance_glob(session_slug, _safe_path_token(instance_id) if instance_id else None)
|
|
8788
8810
|
matches = sorted(p for p in root.glob(pattern) if p.is_dir())
|
|
8789
8811
|
if not matches:
|
|
8790
8812
|
raise RuntimeError(
|
rosotacom/cli_benchmark.py
CHANGED
|
@@ -5614,22 +5614,40 @@ def _make_live_run_point(args: argparse.Namespace, session_name: str) -> RunPoin
|
|
|
5614
5614
|
dist_installs = required_dist_installs(profile_obj)
|
|
5615
5615
|
if dist_installs:
|
|
5616
5616
|
# Custom delay-distribution tables must exist inside the
|
|
5617
|
-
# shaping netns before netem references them by name
|
|
5617
|
+
# shaping netns before netem references them by name, and
|
|
5618
|
+
# in the directory this image's `tc` was compiled to read
|
|
5619
|
+
# -- see tc_lib_dir_probe for why that is not a constant.
|
|
5620
|
+
from .network_profiles import tc_lib_dir_probe
|
|
5621
|
+
|
|
5618
5622
|
for dist_container in (a_container, b_container):
|
|
5619
5623
|
if not dist_container:
|
|
5620
5624
|
continue
|
|
5625
|
+
probe = subprocess.run(
|
|
5626
|
+
["docker", "exec", "-u", "root", dist_container, "sh", "-c", tc_lib_dir_probe()],
|
|
5627
|
+
capture_output=True,
|
|
5628
|
+
text=True,
|
|
5629
|
+
check=False,
|
|
5630
|
+
)
|
|
5631
|
+
tc_lib_dir = probe.stdout.strip()
|
|
5632
|
+
if probe.returncode != 0 or not tc_lib_dir.startswith("/"):
|
|
5633
|
+
raise RuntimeError(
|
|
5634
|
+
f"could not locate the tc library directory in {dist_container}: "
|
|
5635
|
+
f"{probe.stderr or probe.stdout}"
|
|
5636
|
+
)
|
|
5621
5637
|
for table_name, file_path in dist_installs:
|
|
5622
|
-
dist_target = f"/
|
|
5623
|
-
|
|
5624
|
-
["docker", "exec", "-u", "root", dist_container, "mkdir", "-p", "/usr/lib/tc"],
|
|
5638
|
+
dist_target = f"{tc_lib_dir}/{table_name}.dist"
|
|
5639
|
+
res = subprocess.run(
|
|
5625
5640
|
["docker", "cp", file_path, f"{dist_container}:{dist_target}"],
|
|
5626
|
-
|
|
5627
|
-
|
|
5628
|
-
|
|
5629
|
-
|
|
5630
|
-
|
|
5631
|
-
|
|
5632
|
-
|
|
5641
|
+
capture_output=True,
|
|
5642
|
+
text=True,
|
|
5643
|
+
check=False,
|
|
5644
|
+
)
|
|
5645
|
+
if res.returncode != 0:
|
|
5646
|
+
raise RuntimeError(
|
|
5647
|
+
f"failed to install netem distribution table {table_name!r} in "
|
|
5648
|
+
f"{dist_container}: {file_path} -> {dist_target}: "
|
|
5649
|
+
f"{res.stderr or res.stdout}"
|
|
5650
|
+
)
|
|
5633
5651
|
print(
|
|
5634
5652
|
f" netem distribution {table_name!r} installed in {dist_container}: {dist_target}"
|
|
5635
5653
|
)
|
rosotacom/network_profiles.py
CHANGED
|
@@ -440,9 +440,46 @@ def _resolve_distribution_files(profile: Profile, base: Path) -> Profile:
|
|
|
440
440
|
return replace(profile, uplink=fix(profile.uplink), downlink=fix(profile.downlink))
|
|
441
441
|
|
|
442
442
|
|
|
443
|
+
#: Where `tc` looks for a delay-distribution table, in the order to try.
|
|
444
|
+
#: iproute2 compiles the directory in (``LIBDIR "/tc"``), so on a multiarch
|
|
445
|
+
#: Debian or Ubuntu build it is ``/usr/lib/<triplet>/tc`` and ``/usr/lib/tc``
|
|
446
|
+
#: does not exist at all. A table written to the latter is then invisible with
|
|
447
|
+
#: no error of its own: netem answers ``No distribution data for <name>`` and
|
|
448
|
+
#: the run dies at arming time, which is what every profile carrying a fitted
|
|
449
|
+
#: table did until this was found. The directory holding iproute2's own tables
|
|
450
|
+
#: is the one it reads, so that is the evidence to look for rather than a
|
|
451
|
+
#: guessed triplet.
|
|
452
|
+
TC_LIB_DIR_CANDIDATES = ("/usr/lib/*-linux-gnu*/tc", "/usr/lib/tc")
|
|
453
|
+
|
|
454
|
+
#: A table iproute2 ships, used as the marker for "this is the directory".
|
|
455
|
+
TC_LIB_DIR_MARKERS = ("normal.dist", "pareto.dist")
|
|
456
|
+
|
|
457
|
+
|
|
458
|
+
def tc_lib_dir_probe(root: str = "") -> str:
|
|
459
|
+
"""A ``sh -c`` program that prints the directory ``tc`` reads tables from.
|
|
460
|
+
|
|
461
|
+
Three rungs, in order: a candidate that already holds one of iproute2's own
|
|
462
|
+
tables (proof, not inference); a candidate that merely exists; and finally
|
|
463
|
+
``/usr/lib/tc``, created, for an image that has neither. `root` exists so
|
|
464
|
+
the rungs are testable without a container.
|
|
465
|
+
"""
|
|
466
|
+
cands = " ".join(f"{root}{c}" for c in TC_LIB_DIR_CANDIDATES)
|
|
467
|
+
marker = " || ".join(f'[ -f "$d/{m}" ]' for m in TC_LIB_DIR_MARKERS)
|
|
468
|
+
fallback = f"{root}/usr/lib/tc"
|
|
469
|
+
return (
|
|
470
|
+
f"for d in {cands}; do "
|
|
471
|
+
f' if {marker}; then printf %s "$d"; exit 0; fi; '
|
|
472
|
+
f"done; "
|
|
473
|
+
f"for d in {cands}; do "
|
|
474
|
+
f' if [ -d "$d" ]; then printf %s "$d"; exit 0; fi; '
|
|
475
|
+
f"done; "
|
|
476
|
+
f"mkdir -p {fallback} && printf %s {fallback}"
|
|
477
|
+
)
|
|
478
|
+
|
|
479
|
+
|
|
443
480
|
def required_dist_installs(profile: Profile) -> list[tuple[str, str]]:
|
|
444
481
|
"""``(table_name, file_path)`` pairs a runner must install as
|
|
445
|
-
|
|
482
|
+
``<tc lib dir>/<table_name>.dist`` inside the shaping netns before arming.
|
|
446
483
|
|
|
447
484
|
Builtin distributions need nothing; duplicates collapse; conflicting files
|
|
448
485
|
for one name are an error (two segments must mean the same table).
|
|
@@ -6,8 +6,8 @@ rosotacom/benched_set.py,sha256=lBA1Rj04dbr2e-lIvYTWoL55_LdUpCAC-y7eIvJZ8lc,2721
|
|
|
6
6
|
rosotacom/benchmark.py,sha256=3QoXJtWUGQPwvDWyQT_fOjx6K9G2pmvtnVurA21G2QY,76612
|
|
7
7
|
rosotacom/benchmark_delta.py,sha256=qXzriBkbXOeJdlLKM4ziov1F-JfwlseYHE48S9hjFsQ,10005
|
|
8
8
|
rosotacom/bundle_check.py,sha256=l_3BSuYk6juXQmsNU6jkxRbH8ayH2GkiW9ONKMQ1oJY,18270
|
|
9
|
-
rosotacom/cli.py,sha256=
|
|
10
|
-
rosotacom/cli_benchmark.py,sha256=
|
|
9
|
+
rosotacom/cli.py,sha256=WOl0PLl-tEu28ahHh9HIpu-hmNlD4WcH2b45HzoqMJg,438036
|
|
10
|
+
rosotacom/cli_benchmark.py,sha256=IhxiKMl3C7k7zXJAselrjTjcjKAsWGlc16Z_36AVuc0,356021
|
|
11
11
|
rosotacom/deployment.py,sha256=XVhB8JjxhAsglSEEPtiVSeV6fCG0rgGmDRulh14oIYQ,7318
|
|
12
12
|
rosotacom/ffmpeg_packet.py,sha256=zB1BHgY1kexr5sETVMkm4bkVnRytj9k2t0uOYiHUZ6M,4267
|
|
13
13
|
rosotacom/forensics.py,sha256=fxpjuRq3pA1wCf2ERai4SlVnNhJBqZbDh6ETRKODNd0,53576
|
|
@@ -15,7 +15,7 @@ rosotacom/geomap.py,sha256=_85kWI-twzS3rf9KxamnFuhlw8Rp_fRk7n7xI-rUMFc,30292
|
|
|
15
15
|
rosotacom/image_cache.py,sha256=71wC291j8rRMgF1D5pt2gTQdplmiIwnUDXk0BZoQNNQ,7185
|
|
16
16
|
rosotacom/netem_dist.py,sha256=OWtc8asx-ow1wCxDoQuuJwJsFLl7F_lthLWsp-nlK-M,4642
|
|
17
17
|
rosotacom/network_preflight.py,sha256=mQsjVnHP3CPDfHyLsOdobtymcYhcvJbmJRbXm9EgGDs,5532
|
|
18
|
-
rosotacom/network_profiles.py,sha256=
|
|
18
|
+
rosotacom/network_profiles.py,sha256=lUGAAXkJSIwpw7CWGoznKme7b6bC7b73lB56PxgMRAA,33258
|
|
19
19
|
rosotacom/network_shaper.py,sha256=kjn9ZtiO1SMm8UHKIjmn92JBu_hE5TJF6vaNF1XKtNU,5463
|
|
20
20
|
rosotacom/plots.py,sha256=YRGRxuGHSHDZmtL0sv1JS7cPVSL9VzjGYuvMxeHAj54,22674
|
|
21
21
|
rosotacom/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
@@ -205,9 +205,9 @@ rosotacom/resources/ws/session/creation/generate_session_files.py,sha256=3Sx4xTS
|
|
|
205
205
|
rosotacom/resources/ws/session/creation/run_session.py,sha256=G2hJhrmLp11gm3JZ6_C-zZcqu_reXlE_wmobB7BiCfU,16849
|
|
206
206
|
rosotacom/resources/ws/session/creation/run_session_in_external_terminal.py,sha256=Ub_5f-ttUUc-rAPrZmg_Op34s8Z4C91hTyEn8fgyt-E,2633
|
|
207
207
|
rosotacom/resources/ws/session/creation/strip_ansi.py,sha256=vNjpemcq4xHr0VG4x0bZqvgwEBEfOgN-b4PzbrM5nfM,851
|
|
208
|
-
rosotacom_dev-2.5.
|
|
209
|
-
rosotacom_dev-2.5.
|
|
210
|
-
rosotacom_dev-2.5.
|
|
211
|
-
rosotacom_dev-2.5.
|
|
212
|
-
rosotacom_dev-2.5.
|
|
213
|
-
rosotacom_dev-2.5.
|
|
208
|
+
rosotacom_dev-2.5.dev74.dist-info/licenses/LICENSE.txt,sha256=FDB3-fao3lqZ94wsJHKAH7U88kAnfvZhZC8Hm5rN__g,1459
|
|
209
|
+
rosotacom_dev-2.5.dev74.dist-info/METADATA,sha256=nI3Vclf3YkUxefYLlVuE65wu80J6WBxdf_FStvBe-kw,58884
|
|
210
|
+
rosotacom_dev-2.5.dev74.dist-info/WHEEL,sha256=YVMoNqKzERt-wjUZwJ33xBGAwnFl-4cqbYkTtWa4itE,91
|
|
211
|
+
rosotacom_dev-2.5.dev74.dist-info/entry_points.txt,sha256=im11ZWqBbgjQUBz2Ik2RNfMu54wlMwgiVpsBKeI7dCI,49
|
|
212
|
+
rosotacom_dev-2.5.dev74.dist-info/top_level.txt,sha256=BZrtQcIVDWn_pQZR3Mlssqy5SaRXHjOa6B-1D9Fy4Ao,10
|
|
213
|
+
rosotacom_dev-2.5.dev74.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
{rosotacom_dev-2.5.dev72.dist-info → rosotacom_dev-2.5.dev74.dist-info}/licenses/LICENSE.txt
RENAMED
|
File without changes
|
|
File without changes
|