rosotacom-dev 2.5.dev61__tar.gz → 2.5.dev62__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.
- {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev62}/PKG-INFO +1 -1
- {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev62}/src/rosotacom/cli.py +219 -3
- {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev62}/src/rosotacom/cli_benchmark.py +1 -0
- {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev62}/src/rosotacom_dev.egg-info/PKG-INFO +1 -1
- rosotacom_dev-2.5.dev62/src/rosotacom_dev.egg-info/scm_version.json +8 -0
- {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev62}/tests/contract/test_packaged_resources.py +33 -0
- {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev62}/tests/unit/test_cli.py +13 -0
- {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev62}/tests/unit/test_concurrency.py +147 -0
- rosotacom_dev-2.5.dev61/src/rosotacom_dev.egg-info/scm_version.json +0 -8
- {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev62}/.devcontainer/devcontainer.json.template +0 -0
- {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev62}/.devcontainer/re_generate_devcontainer_json.py +0 -0
- {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev62}/.github/CODEOWNERS +0 -0
- {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev62}/.github/ISSUE_TEMPLATE/documentation-audit.md +0 -0
- {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev62}/.github/ISSUE_TEMPLATE/implementation-cleanup.md +0 -0
- {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev62}/.github/ISSUE_TEMPLATE/quality-workflow.md +0 -0
- {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev62}/.github/ISSUE_TEMPLATE/redesign.md +0 -0
- {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev62}/.github/ISSUE_TEMPLATE/release-process.md +0 -0
- {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev62}/.github/ISSUE_TEMPLATE/repository-diagnosis.md +0 -0
- {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev62}/.github/ISSUE_TEMPLATE/test-ci-audit.md +0 -0
- {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev62}/.github/ISSUE_TEMPLATE/work-item.md +0 -0
- {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev62}/.github/PULL_REQUEST_TEMPLATE.md +0 -0
- {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev62}/.github/actions/setup-just/action.yml +0 -0
- {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev62}/.github/dependabot.yml +0 -0
- {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev62}/.github/workflows/benchmark-calibrate.yml +0 -0
- {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev62}/.github/workflows/benchmark-gate.yml +0 -0
- {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev62}/.github/workflows/dev-release.yml +0 -0
- {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev62}/.github/workflows/image-scan.yml +0 -0
- {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev62}/.github/workflows/nightly-e2e.yml +0 -0
- {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev62}/.github/workflows/pr-lightweight.yml +0 -0
- {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev62}/.github/workflows/pr-merge-gate.yml +0 -0
- {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev62}/.github/workflows/release.yml +0 -0
- {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev62}/.pre-commit-config.yaml +0 -0
- {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev62}/AGENTS.md +0 -0
- {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev62}/CONCURRENCY.md +0 -0
- {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev62}/CONTRIBUTING.md +0 -0
- {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev62}/DEVELOPMENT_PRINCIPLES.md +0 -0
- {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev62}/LICENSE.txt +0 -0
- {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev62}/MANIFEST.in +0 -0
- {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev62}/README.md +0 -0
- {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev62}/budgets.jsonl +0 -0
- {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev62}/calibration/README.md +0 -0
- {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev62}/calibration/capacity-size-tight-cyclone.json +0 -0
- {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev62}/calibration/probe-loss-gop-tight-cyclone.json +0 -0
- {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev62}/calibration/probe-loss-nominal-cyclone.json +0 -0
- {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev62}/calibration/probe-loss-tight-cyclone.json +0 -0
- {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev62}/calibration/probe-loss-tight-fastdds.json +0 -0
- {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev62}/calibration/probe-loss-tight-zenoh.json +0 -0
- {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev62}/codecov.yml +0 -0
- {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev62}/deployment-configuration.md +0 -0
- {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev62}/docs/benchmark-ab.md +0 -0
- {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev62}/docs/benchmark-bag-as-load.md +0 -0
- {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev62}/docs/ci.md +0 -0
- {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev62}/docs/ffmpeg-keyframes.md +0 -0
- {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev62}/docs/findings/README.md +0 -0
- {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev62}/docs/findings/a-transport-element-doubles-every-fastdds-sample.md +0 -0
- {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev62}/docs/findings/cyclone-spdp-discovery-bursts.md +0 -0
- {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev62}/docs/findings/delay-alone-no-loss.md +0 -0
- {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev62}/docs/findings/fastdds-datagram-cap-drops-large-samples.md +0 -0
- {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev62}/docs/findings/graph-derived-types-block-a-non-graph-transport.md +0 -0
- {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev62}/docs/findings/head-of-line-irregularity.md +0 -0
- {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev62}/docs/findings/jitter-loss-bandwidth-latency.md +0 -0
- {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev62}/docs/findings/keyframe-loss-kills-the-next-deltas.md +0 -0
- {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev62}/docs/findings/lifespan-is-enforced-by-one-reader-and-not-the-other.md +0 -0
- {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev62}/docs/findings/lighter-message-loss.md +0 -0
- {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev62}/docs/findings/oversubscription-queues-not-losses.md +0 -0
- {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev62}/docs/findings/reorder-becomes-reader-loss.md +0 -0
- {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev62}/docs/findings/repro/decode_chain_repro.py +0 -0
- {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev62}/docs/findings/repro/reorder_drop_repro.py +0 -0
- {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev62}/docs/findings/sender-rows-make-a-dead-link-look-loss-free.md +0 -0
- {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev62}/docs/findings/zero-loss-boundaries-18kb-20hz.md +0 -0
- {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev62}/docs/forensics-report.md +0 -0
- {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev62}/docs/geo-link-map.md +0 -0
- {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev62}/docs/github-repository-settings.md +0 -0
- {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev62}/docs/link-bandwidth.md +0 -0
- {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev62}/docs/link-trace.md +0 -0
- {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev62}/docs/owner-runbook.md +0 -0
- {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev62}/docs/performance-bands.md +0 -0
- {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev62}/docs/playbooks/README.md +0 -0
- {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev62}/docs/quality-model.md +0 -0
- {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev62}/docs/release-notes/TEMPLATE.md +0 -0
- {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev62}/docs/release-notes/v2.1.md +0 -0
- {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev62}/docs/release-notes/v2.2.md +0 -0
- {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev62}/docs/release-notes/v2.3.md +0 -0
- {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev62}/docs/release-notes/v2.4.md +0 -0
- {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev62}/docs/release.md +0 -0
- {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev62}/docs/rfcs/0001-expectation-driven-test-suite.md +0 -0
- {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev62}/docs/rfcs/0002-expectation-concepts.md +0 -0
- {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev62}/docs/rfcs/0003-metric-backbone.md +0 -0
- {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev62}/docs/rfcs/0004-network-profiles.md +0 -0
- {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev62}/docs/rfcs/0005-benchmark-genres-and-ci.md +0 -0
- {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev62}/docs/rfcs/0006-dynamic-qos.md +0 -0
- {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev62}/docs/rfcs/0007-regression-gate.md +0 -0
- {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev62}/docs/rfcs/README.md +0 -0
- {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev62}/docs/testing.md +0 -0
- {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev62}/docs/work-items.md +0 -0
- {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev62}/install.sh +0 -0
- {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev62}/justfile +0 -0
- {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev62}/media/control_center.png +0 -0
- {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev62}/media/echo_test.png +0 -0
- {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev62}/media/forensics-report-example.png +0 -0
- {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev62}/media/host.png +0 -0
- {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev62}/media/vehicle.png +0 -0
- {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev62}/pyproject.toml +0 -0
- {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev62}/requirements.txt +0 -0
- {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev62}/rosotacom.yaml +0 -0
- {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev62}/session-configuration.md +0 -0
- {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev62}/setup.cfg +0 -0
- {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev62}/src/rosotacom/__init__.py +0 -0
- {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev62}/src/rosotacom/__main__.py +0 -0
- {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev62}/src/rosotacom/anonymize.py +0 -0
- {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev62}/src/rosotacom/bag_ground_truth.py +0 -0
- {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev62}/src/rosotacom/benched_set.py +0 -0
- {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev62}/src/rosotacom/benchmark.py +0 -0
- {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev62}/src/rosotacom/benchmark_delta.py +0 -0
- {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev62}/src/rosotacom/bundle_check.py +0 -0
- {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev62}/src/rosotacom/deployment.py +0 -0
- {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev62}/src/rosotacom/ffmpeg_packet.py +0 -0
- {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev62}/src/rosotacom/forensics.py +0 -0
- {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev62}/src/rosotacom/geomap.py +0 -0
- {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev62}/src/rosotacom/image_cache.py +0 -0
- {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev62}/src/rosotacom/netem_dist.py +0 -0
- {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev62}/src/rosotacom/network_preflight.py +0 -0
- {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev62}/src/rosotacom/network_profiles.py +0 -0
- {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev62}/src/rosotacom/network_shaper.py +0 -0
- {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev62}/src/rosotacom/plots.py +0 -0
- {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev62}/src/rosotacom/py.typed +0 -0
- {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev62}/src/rosotacom/resources/benched-set.yaml +0 -0
- {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev62}/src/rosotacom/resources/examples/.gitignore +0 -0
- {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev62}/src/rosotacom/resources/examples/README.md +0 -0
- {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev62}/src/rosotacom/resources/examples/__init__.py +0 -0
- {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev62}/src/rosotacom/resources/examples/bags/8_drop_reference/metadata.yaml +0 -0
- {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev62}/src/rosotacom/resources/examples/deployment.example.yaml +0 -0
- {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev62}/src/rosotacom/resources/examples/dist/field-20260817.dist +0 -0
- {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev62}/src/rosotacom/resources/examples/profiles.yaml +0 -0
- {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev62}/src/rosotacom/resources/examples/ros2docker.json +0 -0
- {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev62}/src/rosotacom/resources/examples/ros2docker.lyrical.json +0 -0
- {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev62}/src/rosotacom/resources/examples/rosotacom.yaml +0 -0
- {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev62}/src/rosotacom/resources/examples/scenarios/2_native_chatter/scenario-definition.yaml +0 -0
- {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev62}/src/rosotacom/resources/examples/scripts/1_heartbeat/run_machine_a.sh +0 -0
- {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev62}/src/rosotacom/resources/examples/scripts/1_heartbeat/run_machine_b.sh +0 -0
- {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev62}/src/rosotacom/resources/examples/scripts/2_native_chatter/machine_a/external.lyrical.ros2docker.json +0 -0
- {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev62}/src/rosotacom/resources/examples/scripts/2_native_chatter/machine_a/external.ros2docker.json +0 -0
- {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev62}/src/rosotacom/resources/examples/scripts/2_native_chatter/machine_a/run_communication.sh +0 -0
- {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev62}/src/rosotacom/resources/examples/scripts/2_native_chatter/machine_a/run_external.py +0 -0
- {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev62}/src/rosotacom/resources/examples/scripts/2_native_chatter/machine_a/wait_for_echo_topic.sh +0 -0
- {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev62}/src/rosotacom/resources/examples/scripts/2_native_chatter/machine_b/external.lyrical.ros2docker.json +0 -0
- {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev62}/src/rosotacom/resources/examples/scripts/2_native_chatter/machine_b/external.ros2docker.json +0 -0
- {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev62}/src/rosotacom/resources/examples/scripts/2_native_chatter/machine_b/run_communication.sh +0 -0
- {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev62}/src/rosotacom/resources/examples/scripts/2_native_chatter/machine_b/run_external.py +0 -0
- {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev62}/src/rosotacom/resources/examples/scripts/3_comp_occ_grid/machine_a/external.ros2docker.json +0 -0
- {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev62}/src/rosotacom/resources/examples/scripts/3_comp_occ_grid/machine_a/external_session.yaml +0 -0
- {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev62}/src/rosotacom/resources/examples/scripts/3_comp_occ_grid/machine_a/foxglove.json +0 -0
- {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev62}/src/rosotacom/resources/examples/scripts/3_comp_occ_grid/machine_a/run_communication.sh +0 -0
- {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev62}/src/rosotacom/resources/examples/scripts/3_comp_occ_grid/machine_a/run_external.py +0 -0
- {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev62}/src/rosotacom/resources/examples/scripts/3_comp_occ_grid/machine_b/costmap_1second/costmap_1second_0.mcap +0 -0
- {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev62}/src/rosotacom/resources/examples/scripts/3_comp_occ_grid/machine_b/costmap_1second/metadata.yaml +0 -0
- {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev62}/src/rosotacom/resources/examples/scripts/3_comp_occ_grid/machine_b/external.ros2docker.json +0 -0
- {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev62}/src/rosotacom/resources/examples/scripts/3_comp_occ_grid/machine_b/run_communication.sh +0 -0
- {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev62}/src/rosotacom/resources/examples/scripts/3_comp_occ_grid/machine_b/run_external.py +0 -0
- {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev62}/src/rosotacom/resources/examples/scripts/4_comp_occ_grid_zen/machine_a/external.ros2docker.json +0 -0
- {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev62}/src/rosotacom/resources/examples/scripts/4_comp_occ_grid_zen/machine_a/external_session.yaml +0 -0
- {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev62}/src/rosotacom/resources/examples/scripts/4_comp_occ_grid_zen/machine_a/foxglove.json +0 -0
- {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev62}/src/rosotacom/resources/examples/scripts/4_comp_occ_grid_zen/machine_a/run_communication.sh +0 -0
- {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev62}/src/rosotacom/resources/examples/scripts/4_comp_occ_grid_zen/machine_a/run_external.py +0 -0
- {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev62}/src/rosotacom/resources/examples/scripts/4_comp_occ_grid_zen/machine_b/costmap_1second/costmap_1second_0.mcap +0 -0
- {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev62}/src/rosotacom/resources/examples/scripts/4_comp_occ_grid_zen/machine_b/costmap_1second/metadata.yaml +0 -0
- {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev62}/src/rosotacom/resources/examples/scripts/4_comp_occ_grid_zen/machine_b/external.ros2docker.json +0 -0
- {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev62}/src/rosotacom/resources/examples/scripts/4_comp_occ_grid_zen/machine_b/run_communication.sh +0 -0
- {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev62}/src/rosotacom/resources/examples/scripts/4_comp_occ_grid_zen/machine_b/run_external.py +0 -0
- {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev62}/src/rosotacom/resources/examples/scripts/5_sized_payload/machine_a/external.ros2docker.json +0 -0
- {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev62}/src/rosotacom/resources/examples/scripts/5_sized_payload/machine_a/external_session.yaml +0 -0
- {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev62}/src/rosotacom/resources/examples/scripts/5_sized_payload/machine_a/run_communication.sh +0 -0
- {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev62}/src/rosotacom/resources/examples/scripts/5_sized_payload/machine_a/run_external.py +0 -0
- {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev62}/src/rosotacom/resources/examples/scripts/5_sized_payload/machine_b/external.ros2docker.json +0 -0
- {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev62}/src/rosotacom/resources/examples/scripts/5_sized_payload/machine_b/external_session.yaml +0 -0
- {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev62}/src/rosotacom/resources/examples/scripts/5_sized_payload/machine_b/run_communication.sh +0 -0
- {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev62}/src/rosotacom/resources/examples/scripts/5_sized_payload/machine_b/run_external.py +0 -0
- {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev62}/src/rosotacom/resources/examples/scripts/6_sized_payload_zen/machine_a/external.ros2docker.json +0 -0
- {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev62}/src/rosotacom/resources/examples/scripts/6_sized_payload_zen/machine_a/external_session.yaml +0 -0
- {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev62}/src/rosotacom/resources/examples/scripts/6_sized_payload_zen/machine_a/run_communication.sh +0 -0
- {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev62}/src/rosotacom/resources/examples/scripts/6_sized_payload_zen/machine_a/run_external.py +0 -0
- {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev62}/src/rosotacom/resources/examples/scripts/6_sized_payload_zen/machine_b/external.ros2docker.json +0 -0
- {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev62}/src/rosotacom/resources/examples/scripts/6_sized_payload_zen/machine_b/external_session.yaml +0 -0
- {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev62}/src/rosotacom/resources/examples/scripts/6_sized_payload_zen/machine_b/run_communication.sh +0 -0
- {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev62}/src/rosotacom/resources/examples/scripts/6_sized_payload_zen/machine_b/run_external.py +0 -0
- {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev62}/src/rosotacom/resources/examples/sessions/10_restamp/session-definition.yaml +0 -0
- {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev62}/src/rosotacom/resources/examples/sessions/11_trickle/session-definition.yaml +0 -0
- {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev62}/src/rosotacom/resources/examples/sessions/12_content_integrity/session-definition.yaml +0 -0
- {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev62}/src/rosotacom/resources/examples/sessions/13_link_latency/session-definition.yaml +0 -0
- {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev62}/src/rosotacom/resources/examples/sessions/14_remote_assist_anonymized/session-definition.yaml +0 -0
- {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev62}/src/rosotacom/resources/examples/sessions/15_remote_assist_anonymized_costmap/session-definition.yaml +0 -0
- {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev62}/src/rosotacom/resources/examples/sessions/16_remote_assist_anonymized_camera/session-definition.yaml +0 -0
- {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev62}/src/rosotacom/resources/examples/sessions/17_synthetic_camera_quality/session-definition.yaml +0 -0
- {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev62}/src/rosotacom/resources/examples/sessions/1_heartbeat/session-definition.yaml +0 -0
- {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev62}/src/rosotacom/resources/examples/sessions/1_heartbeat_status/session-definition.yaml +0 -0
- {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev62}/src/rosotacom/resources/examples/sessions/2_native_chatter/session-definition.yaml +0 -0
- {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev62}/src/rosotacom/resources/examples/sessions/3_comp_occ_grid/session-definition.yaml +0 -0
- {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev62}/src/rosotacom/resources/examples/sessions/4_comp_occ_grid_zen/session-definition.yaml +0 -0
- {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev62}/src/rosotacom/resources/examples/sessions/5_sized_payload/session-definition.yaml +0 -0
- {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev62}/src/rosotacom/resources/examples/sessions/6_sized_payload_zen/session-definition.yaml +0 -0
- {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev62}/src/rosotacom/resources/examples/sessions/7_latched_static/session-definition.yaml +0 -0
- {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev62}/src/rosotacom/resources/examples/sessions/8_drop/session-definition.yaml +0 -0
- {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev62}/src/rosotacom/resources/examples/sessions/9_throttle/session-definition.yaml +0 -0
- {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev62}/src/rosotacom/resources/examples/sessions/bench_1_1_capacity/session-definition.yaml +0 -0
- {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev62}/src/rosotacom/resources/examples/sessions/bench_1_2_load_sweep/session-definition.yaml +0 -0
- {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev62}/src/rosotacom/resources/examples/sessions/bench_1_3_ramp/session-definition.yaml +0 -0
- {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev62}/src/rosotacom/resources/examples/sessions/bench_1_4_recovery/session-definition.yaml +0 -0
- {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev62}/src/rosotacom/resources/examples/sessions/bench_1_5_camera/session-definition.yaml +0 -0
- {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev62}/src/rosotacom/resources/ros2docker.json.example +0 -0
- {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev62}/src/rosotacom/resources/ros2docker.lyrical.json.example +0 -0
- {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev62}/src/rosotacom/resources/ws/.vscode/c_cpp_properties.json +0 -0
- {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev62}/src/rosotacom/resources/ws/.vscode/settings.json +0 -0
- {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev62}/src/rosotacom/resources/ws/ota_configs/archive/fastdds_easy_mode.xml.template +0 -0
- {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev62}/src/rosotacom/resources/ws/ota_configs/archive/fastdds_initial_peer_list.xml.template +0 -0
- {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev62}/src/rosotacom/resources/ws/ota_configs/archive/fastdds_v2.xml.template +0 -0
- {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev62}/src/rosotacom/resources/ws/ota_configs/archive/fastdds_v3.xml.template +0 -0
- {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev62}/src/rosotacom/resources/ws/ota_configs/archive/fastdds_v4.xml.template +0 -0
- {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev62}/src/rosotacom/resources/ws/ota_configs/archive/fastdds_v5.xml.template +0 -0
- {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev62}/src/rosotacom/resources/ws/ota_configs/create_zenoh_json5.py +0 -0
- {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev62}/src/rosotacom/resources/ws/ota_configs/cyclonedds_local_participants.xml.template +0 -0
- {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev62}/src/rosotacom/resources/ws/ota_configs/cyclonedds_minimal.xml.template +0 -0
- {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev62}/src/rosotacom/resources/ws/ota_configs/cyclonedds_tuned.xml.template +0 -0
- {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev62}/src/rosotacom/resources/ws/ota_configs/fastdds_easy_mode.xml.template +0 -0
- {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev62}/src/rosotacom/resources/ws/ota_configs/fastdds_local.xml.template +0 -0
- {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev62}/src/rosotacom/resources/ws/ota_configs/fastdds_tuned.xml.template +0 -0
- {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev62}/src/rosotacom/resources/ws/ota_configs/fastdds_unicast.xml.template +0 -0
- {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev62}/src/rosotacom/resources/ws/ota_configs/get_ota_xml.py +0 -0
- {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev62}/src/rosotacom/resources/ws/ota_configs/utils.py +0 -0
- {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev62}/src/rosotacom/resources/ws/ota_configs/zenoh.json5.template +0 -0
- {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev62}/src/rosotacom/resources/ws/ros2src/.gitkeep +0 -0
- {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev62}/src/rosotacom/resources/ws/ros2src/com_cpp/CMakeLists.txt +0 -0
- {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev62}/src/rosotacom/resources/ws/ros2src/com_cpp/COLCON_IGNORE +0 -0
- {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev62}/src/rosotacom/resources/ws/ros2src/com_cpp/LICENSE.txt +0 -0
- {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev62}/src/rosotacom/resources/ws/ros2src/com_cpp/package.xml +0 -0
- {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev62}/src/rosotacom/resources/ws/ros2src/com_cpp/src/hz_monitor.cpp +0 -0
- {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev62}/src/rosotacom/resources/ws/ros2src/com_cpp/src/universal_compressor.cpp +0 -0
- {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev62}/src/rosotacom/resources/ws/ros2src/com_cpp/src/universal_decompressor.cpp +0 -0
- {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev62}/src/rosotacom/resources/ws/ros2src/com_msgs/CMakeLists.txt +0 -0
- {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev62}/src/rosotacom/resources/ws/ros2src/com_msgs/LICENSE.txt +0 -0
- {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev62}/src/rosotacom/resources/ws/ros2src/com_msgs/msg/CompressedData.msg +0 -0
- {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev62}/src/rosotacom/resources/ws/ros2src/com_msgs/msg/EchoHeartbeat.msg +0 -0
- {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev62}/src/rosotacom/resources/ws/ros2src/com_msgs/msg/OtaStamped.msg +0 -0
- {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev62}/src/rosotacom/resources/ws/ros2src/com_msgs/msg/SizedPayload.msg +0 -0
- {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev62}/src/rosotacom/resources/ws/ros2src/com_msgs/package.xml +0 -0
- {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev62}/src/rosotacom/resources/ws/ros2src/com_py/LICENSE.txt +0 -0
- {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev62}/src/rosotacom/resources/ws/ros2src/com_py/com_py/__init__.py +0 -0
- {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev62}/src/rosotacom/resources/ws/ros2src/com_py/com_py/base_bridge_relay.py +0 -0
- {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev62}/src/rosotacom/resources/ws/ros2src/com_py/com_py/bridge_in.py +0 -0
- {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev62}/src/rosotacom/resources/ws/ros2src/com_py/com_py/bridge_out.py +0 -0
- {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev62}/src/rosotacom/resources/ws/ros2src/com_py/com_py/ffmpeg_flags.py +0 -0
- {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev62}/src/rosotacom/resources/ws/ros2src/com_py/com_py/heartbeat_echo.py +0 -0
- {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev62}/src/rosotacom/resources/ws/ros2src/com_py/com_py/hz_monitor.py +0 -0
- {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev62}/src/rosotacom/resources/ws/ros2src/com_py/com_py/image_bandwidth_reducer.py +0 -0
- {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev62}/src/rosotacom/resources/ws/ros2src/com_py/com_py/image_pixel_capper.py +0 -0
- {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev62}/src/rosotacom/resources/ws/ros2src/com_py/com_py/lanelet_saver.py +0 -0
- {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev62}/src/rosotacom/resources/ws/ros2src/com_py/com_py/latch_relay.py +0 -0
- {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev62}/src/rosotacom/resources/ws/ros2src/com_py/com_py/legacy/costmap_remapper.py +0 -0
- {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev62}/src/rosotacom/resources/ws/ros2src/com_py/com_py/legacy/marker_remapper.py +0 -0
- {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev62}/src/rosotacom/resources/ws/ros2src/com_py/com_py/legacy/speed_test.py +0 -0
- {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev62}/src/rosotacom/resources/ws/ros2src/com_py/com_py/legacy/tf_remapper.py +0 -0
- {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev62}/src/rosotacom/resources/ws/ros2src/com_py/com_py/legacy/traffic_monitor.py +0 -0
- {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev62}/src/rosotacom/resources/ws/ros2src/com_py/com_py/legacy/wifi_network_analysis_1.py +0 -0
- {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev62}/src/rosotacom/resources/ws/ros2src/com_py/com_py/link_bytes.py +0 -0
- {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev62}/src/rosotacom/resources/ws/ros2src/com_py/com_py/link_trace.py +0 -0
- {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev62}/src/rosotacom/resources/ws/ros2src/com_py/com_py/local_global_frame_bridge.py +0 -0
- {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev62}/src/rosotacom/resources/ws/ros2src/com_py/com_py/localization_map_split_reduce_saver.py +0 -0
- {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev62}/src/rosotacom/resources/ws/ros2src/com_py/com_py/msg_transform.py +0 -0
- {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev62}/src/rosotacom/resources/ws/ros2src/com_py/com_py/pair_management.py +0 -0
- {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev62}/src/rosotacom/resources/ws/ros2src/com_py/com_py/playout_pacer.py +0 -0
- {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev62}/src/rosotacom/resources/ws/ros2src/com_py/com_py/playout_pacer_core.py +0 -0
- {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev62}/src/rosotacom/resources/ws/ros2src/com_py/com_py/pub_sub_pair.py +0 -0
- {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev62}/src/rosotacom/resources/ws/ros2src/com_py/com_py/qos.py +0 -0
- {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev62}/src/rosotacom/resources/ws/ros2src/com_py/com_py/relay_in.py +0 -0
- {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev62}/src/rosotacom/resources/ws/ros2src/com_py/com_py/relay_out.py +0 -0
- {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev62}/src/rosotacom/resources/ws/ros2src/com_py/com_py/restamp.py +0 -0
- {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev62}/src/rosotacom/resources/ws/ros2src/com_py/com_py/sized_publisher.py +0 -0
- {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev62}/src/rosotacom/resources/ws/ros2src/com_py/com_py/stage_latency.py +0 -0
- {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev62}/src/rosotacom/resources/ws/ros2src/com_py/com_py/stage_latency_core.py +0 -0
- {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev62}/src/rosotacom/resources/ws/ros2src/com_py/com_py/status_overview.py +0 -0
- {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev62}/src/rosotacom/resources/ws/ros2src/com_py/com_py/status_overview_core.py +0 -0
- {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev62}/src/rosotacom/resources/ws/ros2src/com_py/com_py/tf_map_to_cart_saver.py +0 -0
- {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev62}/src/rosotacom/resources/ws/ros2src/com_py/com_py/topic_monitor.py +0 -0
- {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev62}/src/rosotacom/resources/ws/ros2src/com_py/com_py/topic_resolution.py +0 -0
- {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev62}/src/rosotacom/resources/ws/ros2src/com_py/com_py/trickle.py +0 -0
- {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev62}/src/rosotacom/resources/ws/ros2src/com_py/com_py/universal_compressor.py +0 -0
- {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev62}/src/rosotacom/resources/ws/ros2src/com_py/com_py/universal_decompressor.py +0 -0
- {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev62}/src/rosotacom/resources/ws/ros2src/com_py/com_py/universal_ota_unwrapper.py +0 -0
- {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev62}/src/rosotacom/resources/ws/ros2src/com_py/com_py/universal_ota_wrapper.py +0 -0
- {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev62}/src/rosotacom/resources/ws/ros2src/com_py/package.xml +0 -0
- {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev62}/src/rosotacom/resources/ws/ros2src/com_py/resource/com_py +0 -0
- {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev62}/src/rosotacom/resources/ws/ros2src/com_py/setup.cfg +0 -0
- {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev62}/src/rosotacom/resources/ws/ros2src/com_py/setup.py +0 -0
- {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev62}/src/rosotacom/resources/ws/ros2src/com_py/test/test_copyright.py +0 -0
- {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev62}/src/rosotacom/resources/ws/ros2src/com_py/test/test_flake8.py +0 -0
- {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev62}/src/rosotacom/resources/ws/ros2src/com_py/test/test_pep257.py +0 -0
- {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev62}/src/rosotacom/resources/ws/session/content/base/session_plugin_base.yaml +0 -0
- {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev62}/src/rosotacom/resources/ws/session/content/network/up_connectivity.py +0 -0
- {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev62}/src/rosotacom/resources/ws/session/content/network/wifi_network_analysis_2.py +0 -0
- {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev62}/src/rosotacom/resources/ws/session/creation/anonymize_bag.py +0 -0
- {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev62}/src/rosotacom/resources/ws/session/creation/catmux_log_setup.sh +0 -0
- {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev62}/src/rosotacom/resources/ws/session/creation/create_session_yaml.py +0 -0
- {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev62}/src/rosotacom/resources/ws/session/creation/generate_session_files.py +0 -0
- {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev62}/src/rosotacom/resources/ws/session/creation/run_session.py +0 -0
- {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev62}/src/rosotacom/resources/ws/session/creation/run_session_in_external_terminal.py +0 -0
- {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev62}/src/rosotacom/resources/ws/session/creation/strip_ansi.py +0 -0
- {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev62}/src/rosotacom/status_eval.py +0 -0
- {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev62}/src/rosotacom/stream_stats.py +0 -0
- {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev62}/src/rosotacom/trace_profiles.py +0 -0
- {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev62}/src/rosotacom/transit.py +0 -0
- {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev62}/src/rosotacom/video_quality.py +0 -0
- {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev62}/src/rosotacom_dev.egg-info/SOURCES.txt +0 -0
- {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev62}/src/rosotacom_dev.egg-info/dependency_links.txt +0 -0
- {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev62}/src/rosotacom_dev.egg-info/entry_points.txt +0 -0
- {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev62}/src/rosotacom_dev.egg-info/requires.txt +0 -0
- {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev62}/src/rosotacom_dev.egg-info/scm_file_list.json +0 -0
- {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev62}/src/rosotacom_dev.egg-info/top_level.txt +0 -0
- {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev62}/terminology.md +0 -0
- {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev62}/tests/contract/test_benched_set_registry.py +0 -0
- {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev62}/tests/contract/test_findings.py +0 -0
- {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev62}/tests/contract/test_generated_topics_have_a_producer.py +0 -0
- {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev62}/tests/contract/test_lyrical_runtime_compat.py +0 -0
- {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev62}/tests/contract/test_markdown_links.py +0 -0
- {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev62}/tests/contract/test_ota_source_bundle.py +0 -0
- {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev62}/tests/contract/test_package_metadata.py +0 -0
- {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev62}/tests/contract/test_profile_covers_generated_commands.py +0 -0
- {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev62}/tests/contract/test_pytest_policy.py +0 -0
- {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev62}/tests/contract/test_readme_examples.py +0 -0
- {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev62}/tests/contract/test_security_maintenance_config.py +0 -0
- {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev62}/tests/contract/test_session_configuration_doc.py +0 -0
- {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev62}/tests/contract/test_session_template_substitution.py +0 -0
- {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev62}/tests/contract/test_workflow_contracts.py +0 -0
- {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev62}/tests/e2e/conftest.py +0 -0
- {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev62}/tests/e2e/test_anonymize_e2e.py +0 -0
- {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev62}/tests/e2e/test_benchmark_ab.py +0 -0
- {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev62}/tests/e2e/test_benchmark_capacity.py +0 -0
- {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev62}/tests/e2e/test_benchmark_replay.py +0 -0
- {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev62}/tests/e2e/test_parallel_smoke.py +0 -0
- {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev62}/tests/e2e/test_smoke.py +0 -0
- {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev62}/tests/e2e/test_timeline_stepping.py +0 -0
- {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev62}/tests/e2e/test_video_quality_e2e.py +0 -0
- {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev62}/tests/sessions/generate_rmw_matrix.py +0 -0
- {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev62}/tests/sessions/rmw_matrix/1_heartbeat_cyclone-local_zenoh-ros2dds-ota/session-definition.yaml +0 -0
- {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev62}/tests/sessions/rmw_matrix/1_heartbeat_cyclone-ota/session-definition.yaml +0 -0
- {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev62}/tests/sessions/rmw_matrix/1_heartbeat_cyclone-ota-tuned/session-definition.yaml +0 -0
- {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev62}/tests/sessions/rmw_matrix/1_heartbeat_fastdds/session-definition.yaml +0 -0
- {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev62}/tests/sessions/rmw_matrix/1_heartbeat_fastdds-default/session-definition.yaml +0 -0
- {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev62}/tests/sessions/rmw_matrix/1_heartbeat_zen-endpoints/session-definition.yaml +0 -0
- {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev62}/tests/unit/test_anonymize.py +0 -0
- {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev62}/tests/unit/test_bag_ground_truth.py +0 -0
- {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev62}/tests/unit/test_benched_set.py +0 -0
- {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev62}/tests/unit/test_benchmark.py +0 -0
- {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev62}/tests/unit/test_benchmark_delta.py +0 -0
- {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev62}/tests/unit/test_bundle_check.py +0 -0
- {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev62}/tests/unit/test_catmux_pane_logging.py +0 -0
- {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev62}/tests/unit/test_cli_benchmark.py +0 -0
- {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev62}/tests/unit/test_container_shaping.py +0 -0
- {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev62}/tests/unit/test_deployment.py +0 -0
- {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev62}/tests/unit/test_ffmpeg_flags.py +0 -0
- {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev62}/tests/unit/test_ffmpeg_packet.py +0 -0
- {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev62}/tests/unit/test_field_profiles.py +0 -0
- {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev62}/tests/unit/test_forensics.py +0 -0
- {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev62}/tests/unit/test_geomap.py +0 -0
- {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev62}/tests/unit/test_image_cache.py +0 -0
- {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev62}/tests/unit/test_link_bytes.py +0 -0
- {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev62}/tests/unit/test_link_trace.py +0 -0
- {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev62}/tests/unit/test_netem_dist.py +0 -0
- {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev62}/tests/unit/test_network_preflight.py +0 -0
- {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev62}/tests/unit/test_network_profiles.py +0 -0
- {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev62}/tests/unit/test_network_shaper.py +0 -0
- {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev62}/tests/unit/test_ota_link_configs.py +0 -0
- {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev62}/tests/unit/test_ota_unwrapper.py +0 -0
- {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev62}/tests/unit/test_ota_wrapper.py +0 -0
- {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev62}/tests/unit/test_ota_xml.py +0 -0
- {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev62}/tests/unit/test_playout_pacer.py +0 -0
- {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev62}/tests/unit/test_plots.py +0 -0
- {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev62}/tests/unit/test_pub_sub_pair_types.py +0 -0
- {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev62}/tests/unit/test_qos_fallback_logging.py +0 -0
- {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev62}/tests/unit/test_relay_optional_parameters.py +0 -0
- {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev62}/tests/unit/test_ros_node_entity_ownership.py +0 -0
- {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev62}/tests/unit/test_session_pipeline.py +0 -0
- {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev62}/tests/unit/test_stage_latency.py +0 -0
- {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev62}/tests/unit/test_status_eval.py +0 -0
- {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev62}/tests/unit/test_status_overview.py +0 -0
- {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev62}/tests/unit/test_status_overview_observer.py +0 -0
- {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev62}/tests/unit/test_stream_stats.py +0 -0
- {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev62}/tests/unit/test_trace_profiles.py +0 -0
- {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev62}/tests/unit/test_transit.py +0 -0
- {rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev62}/tests/unit/test_video_quality.py +0 -0
|
@@ -275,6 +275,13 @@ class OtaSmokePeer:
|
|
|
275
275
|
#: list. Naming the transport lets the narrow thing be substituted without
|
|
276
276
|
#: this file having to know what narrow means.
|
|
277
277
|
exec_command: str | None = None
|
|
278
|
+
#: CPUs this peer's session containers are confined to, e.g. "0-3".
|
|
279
|
+
#:
|
|
280
|
+
#: The reason it is per peer: a reservation is a property of one machine's
|
|
281
|
+
#: contention, not of the run. A bench pair is usually one busy workstation
|
|
282
|
+
#: shared with somebody else and one idle machine, and pinning the idle one
|
|
283
|
+
#: would only take capacity away from the measurement.
|
|
284
|
+
cpuset: str | None = None
|
|
278
285
|
|
|
279
286
|
|
|
280
287
|
@dataclass(frozen=True)
|
|
@@ -1189,6 +1196,11 @@ def _parse_peer_exec_overrides(overrides: list[str] | None) -> dict[str, str]:
|
|
|
1189
1196
|
return parse_assignments(overrides, option="--peer-exec")
|
|
1190
1197
|
|
|
1191
1198
|
|
|
1199
|
+
def _parse_peer_cpuset_overrides(overrides: list[str] | None) -> dict[str, str]:
|
|
1200
|
+
parsed = parse_assignments(overrides, option="--peer-cpuset")
|
|
1201
|
+
return {name: validate_cpuset(value) for name, value in parsed.items()}
|
|
1202
|
+
|
|
1203
|
+
|
|
1192
1204
|
def _deployment_config(runtime: RuntimeConfig) -> DeploymentConfig | None:
|
|
1193
1205
|
return load_deployment(runtime.deployment)
|
|
1194
1206
|
|
|
@@ -1749,8 +1761,13 @@ def _ota_plan_from_bindings(
|
|
|
1749
1761
|
checkouts: Mapping[str, str] | None = None,
|
|
1750
1762
|
project: str | None = None,
|
|
1751
1763
|
exec_commands: Mapping[str, str] | None = None,
|
|
1764
|
+
cpusets: Mapping[str, str] | None = None,
|
|
1752
1765
|
) -> OtaSmokePlan:
|
|
1753
1766
|
_ota_validate_install_mode(install_mode)
|
|
1767
|
+
if cpusets:
|
|
1768
|
+
unknown = sorted(set(cpusets) - set(bindings))
|
|
1769
|
+
if unknown:
|
|
1770
|
+
raise RuntimeError(f"--peer-cpuset names a peer the session does not have: {', '.join(unknown)}")
|
|
1754
1771
|
if exec_commands:
|
|
1755
1772
|
unknown = sorted(set(exec_commands) - set(bindings))
|
|
1756
1773
|
if unknown:
|
|
@@ -1811,6 +1828,7 @@ def _ota_plan_from_bindings(
|
|
|
1811
1828
|
address=binding.address,
|
|
1812
1829
|
checkout=(checkouts or {}).get(name) if install_mode == "checkout" else None,
|
|
1813
1830
|
exec_command=(exec_commands or {}).get(name),
|
|
1831
|
+
cpuset=(cpusets or {}).get(name),
|
|
1814
1832
|
)
|
|
1815
1833
|
for name, binding in bindings.items()
|
|
1816
1834
|
}
|
|
@@ -1839,6 +1857,7 @@ def _ota_write_state(instance: SessionInstance, plan: OtaSmokePlan) -> OtaSmokeP
|
|
|
1839
1857
|
"address": peer.address,
|
|
1840
1858
|
"ssh": peer.ssh,
|
|
1841
1859
|
"checkout": peer.checkout,
|
|
1860
|
+
"cpuset": peer.cpuset,
|
|
1842
1861
|
"exec": peer.exec_command,
|
|
1843
1862
|
}
|
|
1844
1863
|
for name, peer in sorted(plan.peers.items())
|
|
@@ -1865,6 +1884,7 @@ def _ota_load_state(path_arg: str) -> OtaSmokePlan:
|
|
|
1865
1884
|
address=str(peer_raw["address"]),
|
|
1866
1885
|
ssh=str(peer_raw["ssh"]) if peer_raw.get("ssh") else None,
|
|
1867
1886
|
checkout=str(peer_raw["checkout"]) if peer_raw.get("checkout") else None,
|
|
1887
|
+
cpuset=str(peer_raw["cpuset"]) if peer_raw.get("cpuset") else None,
|
|
1868
1888
|
exec_command=str(peer_raw["exec"]) if peer_raw.get("exec") else None,
|
|
1869
1889
|
)
|
|
1870
1890
|
install_pin = raw.get("install_pin")
|
|
@@ -2199,6 +2219,7 @@ def _ota_start_parts(
|
|
|
2199
2219
|
mode: str,
|
|
2200
2220
|
force: bool = True,
|
|
2201
2221
|
link_trace_parts: list[str] | None = None,
|
|
2222
|
+
cpuset: str | None = None,
|
|
2202
2223
|
) -> list[str]:
|
|
2203
2224
|
if target.target_type == "scenario":
|
|
2204
2225
|
parts = ["scenario", "start", target.name, "--identity", identity, "--mode", mode, "--instance-id", instance_id]
|
|
@@ -2215,6 +2236,8 @@ def _ota_start_parts(
|
|
|
2215
2236
|
]
|
|
2216
2237
|
parts.append("--force" if force else "--no-force")
|
|
2217
2238
|
parts.extend(link_trace_parts or [])
|
|
2239
|
+
if cpuset:
|
|
2240
|
+
parts.extend(["--cpuset", cpuset])
|
|
2218
2241
|
for override in peer_args:
|
|
2219
2242
|
parts.extend(["--peer-address", override])
|
|
2220
2243
|
return parts
|
|
@@ -2229,6 +2252,7 @@ def _ota_communication_start_parts(
|
|
|
2229
2252
|
mode: str,
|
|
2230
2253
|
force: bool = True,
|
|
2231
2254
|
link_trace_parts: list[str] | None = None,
|
|
2255
|
+
cpuset: str | None = None,
|
|
2232
2256
|
) -> list[str]:
|
|
2233
2257
|
parts = [
|
|
2234
2258
|
"start",
|
|
@@ -2243,6 +2267,8 @@ def _ota_communication_start_parts(
|
|
|
2243
2267
|
]
|
|
2244
2268
|
parts.append("--force" if force else "--no-force")
|
|
2245
2269
|
parts.extend(link_trace_parts or [])
|
|
2270
|
+
if cpuset:
|
|
2271
|
+
parts.extend(["--cpuset", cpuset])
|
|
2246
2272
|
for override in peer_args:
|
|
2247
2273
|
parts.extend(["--peer-address", override])
|
|
2248
2274
|
return parts
|
|
@@ -2658,6 +2684,7 @@ def _resolve_ota_smoke_context(
|
|
|
2658
2684
|
checkouts=checkouts,
|
|
2659
2685
|
project=project,
|
|
2660
2686
|
exec_commands=exec_commands,
|
|
2687
|
+
cpusets=_parse_peer_cpuset_overrides(getattr(args, "peer_cpuset", None)),
|
|
2661
2688
|
)
|
|
2662
2689
|
plan_peers = set(plan.peers)
|
|
2663
2690
|
target_peers = set(_peer_keys_from_cfg(target.cfg))
|
|
@@ -2877,10 +2904,121 @@ def _ota_conflict_check(plan: OtaSmokePlan, *, dry_run: bool) -> None:
|
|
|
2877
2904
|
_LOAD_WARN_PER_CPU = 0.7
|
|
2878
2905
|
_LOAD_REFUSE_PER_CPU = 2.0
|
|
2879
2906
|
|
|
2907
|
+
# Thresholds for a peer whose containers are pinned to a CPU set. These are a
|
|
2908
|
+
# different quantity from the ones above and deliberately not the same numbers:
|
|
2909
|
+
# load-per-CPU counts runnable threads and is unbounded, while this is the busy
|
|
2910
|
+
# fraction of the pinned CPUs and cannot exceed 1.0. What it answers is the only
|
|
2911
|
+
# question a reservation raises — is anybody else on the cores this run was
|
|
2912
|
+
# given — so it is read before the run puts its own work there.
|
|
2913
|
+
_CPUSET_WARN_BUSY = 0.5
|
|
2914
|
+
_CPUSET_REFUSE_BUSY = 0.85
|
|
2915
|
+
|
|
2916
|
+
|
|
2917
|
+
#: Two snapshots of the per-CPU jiffy counters, a second apart. A busy fraction
|
|
2918
|
+
#: needs a delta -- the absolute counters describe the machine since boot, which
|
|
2919
|
+
#: says nothing about who is on those cores now.
|
|
2920
|
+
_CPU_BUSY_PROBE = "grep '^cpu[0-9]' /proc/stat; sleep 1; echo ---; grep '^cpu[0-9]' /proc/stat"
|
|
2921
|
+
|
|
2922
|
+
|
|
2923
|
+
def _parse_cpu_busy(stdout: str, cpus: Sequence[int]) -> dict[int, float] | None:
|
|
2924
|
+
"""Busy fraction per CPU between the two /proc/stat snapshots.
|
|
2925
|
+
|
|
2926
|
+
Busy is everything but idle and iowait: a core waiting on disk is not
|
|
2927
|
+
competing for the runqueue this measurement cares about, and counting it
|
|
2928
|
+
would refuse runs on a machine that is merely writing a bag.
|
|
2929
|
+
"""
|
|
2930
|
+
before, sep, after = stdout.partition("---")
|
|
2931
|
+
if not sep:
|
|
2932
|
+
return None
|
|
2933
|
+
|
|
2934
|
+
def snapshot(text: str) -> dict[int, tuple[int, int]]:
|
|
2935
|
+
out: dict[int, tuple[int, int]] = {}
|
|
2936
|
+
for line in text.splitlines():
|
|
2937
|
+
fields = line.split()
|
|
2938
|
+
if len(fields) < 6 or not fields[0].startswith("cpu") or not fields[0][3:].isdigit():
|
|
2939
|
+
continue
|
|
2940
|
+
try:
|
|
2941
|
+
values = [int(x) for x in fields[1:]]
|
|
2942
|
+
except ValueError:
|
|
2943
|
+
continue
|
|
2944
|
+
total = sum(values)
|
|
2945
|
+
idle = values[3] + values[4] # idle + iowait
|
|
2946
|
+
out[int(fields[0][3:])] = (total, idle)
|
|
2947
|
+
return out
|
|
2948
|
+
|
|
2949
|
+
first, second = snapshot(before), snapshot(after)
|
|
2950
|
+
busy: dict[int, float] = {}
|
|
2951
|
+
for cpu in cpus:
|
|
2952
|
+
if cpu not in first or cpu not in second:
|
|
2953
|
+
return None
|
|
2954
|
+
total_delta = second[cpu][0] - first[cpu][0]
|
|
2955
|
+
idle_delta = second[cpu][1] - first[cpu][1]
|
|
2956
|
+
if total_delta <= 0:
|
|
2957
|
+
return None
|
|
2958
|
+
busy[cpu] = max(0.0, min(1.0, 1.0 - idle_delta / total_delta))
|
|
2959
|
+
return busy or None
|
|
2960
|
+
|
|
2961
|
+
|
|
2962
|
+
def _ota_cpuset_load_check(peer: OtaSmokePeer, *, dry_run: bool) -> bool:
|
|
2963
|
+
"""Judge the CPUs this peer is pinned to, not the machine around them.
|
|
2964
|
+
|
|
2965
|
+
A reservation makes a machine measurable without making it quiet: the other
|
|
2966
|
+
account keeps every thread it had, they just queue on fewer cores, so
|
|
2967
|
+
/proc/loadavg reads higher than before and says nothing about whether the
|
|
2968
|
+
reserved cores are free. Reading those cores directly is the only way the
|
|
2969
|
+
preflight can tell a partitioned workstation from a saturated one.
|
|
2970
|
+
|
|
2971
|
+
Returns False when the probe could not produce a number, so the caller can
|
|
2972
|
+
fall back to the machine-wide check rather than silently skip it.
|
|
2973
|
+
"""
|
|
2974
|
+
if not peer.cpuset:
|
|
2975
|
+
return False
|
|
2976
|
+
cpus = parse_cpuset_list(peer.cpuset)
|
|
2977
|
+
result = _ota_run(
|
|
2978
|
+
peer,
|
|
2979
|
+
_CPU_BUSY_PROBE,
|
|
2980
|
+
label=f"{peer.name}: load on CPUs {peer.cpuset}",
|
|
2981
|
+
dry_run=dry_run,
|
|
2982
|
+
batch=True,
|
|
2983
|
+
check=False,
|
|
2984
|
+
)
|
|
2985
|
+
if dry_run:
|
|
2986
|
+
return True
|
|
2987
|
+
if result.returncode != 0:
|
|
2988
|
+
return False
|
|
2989
|
+
busy = _parse_cpu_busy(result.stdout or "", cpus)
|
|
2990
|
+
if busy is None:
|
|
2991
|
+
return False
|
|
2992
|
+
|
|
2993
|
+
worst_cpu = max(busy, key=lambda cpu: busy[cpu])
|
|
2994
|
+
mean = sum(busy.values()) / len(busy)
|
|
2995
|
+
detail = " ".join(f"cpu{cpu}={busy[cpu] * 100:.0f}%" for cpu in sorted(busy))
|
|
2996
|
+
summary = (
|
|
2997
|
+
f"CPUs {peer.cpuset} are {mean * 100:.0f}% busy on average "
|
|
2998
|
+
f"(worst cpu{worst_cpu} at {busy[worst_cpu] * 100:.0f}%) — {detail}"
|
|
2999
|
+
)
|
|
3000
|
+
if mean >= _CPUSET_REFUSE_BUSY:
|
|
3001
|
+
raise RuntimeError(
|
|
3002
|
+
f"Peer {peer.name} is pinned to CPUs {peer.cpuset}, and they are not free: {summary}.\n"
|
|
3003
|
+
"The reservation is either not in force or somebody else is inside it. Check it on that "
|
|
3004
|
+
"machine, pin to different CPUs, or — if this load is yours and expected — pass "
|
|
3005
|
+
"--skip-conflict-check."
|
|
3006
|
+
)
|
|
3007
|
+
if mean >= _CPUSET_WARN_BUSY:
|
|
3008
|
+
print(f"! {peer.name}: {summary} — the run has less of its reservation than it looks")
|
|
3009
|
+
else:
|
|
3010
|
+
print(f"+ {peer.name}: {summary}")
|
|
3011
|
+
return True
|
|
3012
|
+
|
|
2880
3013
|
|
|
2881
3014
|
def _ota_load_check(plan: OtaSmokePlan, *, dry_run: bool) -> None:
|
|
2882
3015
|
"""Refuse to measure on a peer that is busy with someone else's work."""
|
|
2883
3016
|
for peer in plan.peers.values():
|
|
3017
|
+
# A pinned peer is judged on its own CPUs. The machine-wide figure is the
|
|
3018
|
+
# wrong question there: it is high by construction on exactly the shared
|
|
3019
|
+
# workstation the pinning exists for.
|
|
3020
|
+
if _ota_cpuset_load_check(peer, dry_run=dry_run):
|
|
3021
|
+
continue
|
|
2884
3022
|
result = _ota_run(
|
|
2885
3023
|
peer,
|
|
2886
3024
|
"cut -d' ' -f1-3 /proc/loadavg; nproc",
|
|
@@ -2910,7 +3048,9 @@ def _ota_load_check(plan: OtaSmokePlan, *, dry_run: bool) -> None:
|
|
|
2910
3048
|
f"Peer {peer.name} is carrying unrelated load: {summary}.\n"
|
|
2911
3049
|
"A measurement taken here would describe the contention, not the link.\n"
|
|
2912
3050
|
"Wait for the machine to go quiet, measure on another pair, or — if this "
|
|
2913
|
-
"load is yours and expected — pass --skip-conflict-check
|
|
3051
|
+
"load is yours and expected — pass --skip-conflict-check.\n"
|
|
3052
|
+
"If those CPUs were reserved away from another account, pin this peer to them "
|
|
3053
|
+
"with --peer-cpuset and the check will read them instead of the whole machine."
|
|
2914
3054
|
)
|
|
2915
3055
|
if ratio >= _LOAD_WARN_PER_CPU:
|
|
2916
3056
|
print(f"! {peer.name}: {summary} — comparisons survive if runs are interleaved, absolute latencies do not")
|
|
@@ -3692,7 +3832,15 @@ def _ota_start_peers(
|
|
|
3692
3832
|
peer = plan.peers[peer_name]
|
|
3693
3833
|
command = _ota_rosotacom_command(
|
|
3694
3834
|
plan,
|
|
3695
|
-
_ota_start_parts(
|
|
3835
|
+
_ota_start_parts(
|
|
3836
|
+
target,
|
|
3837
|
+
peer_name,
|
|
3838
|
+
instance_id,
|
|
3839
|
+
peer_args,
|
|
3840
|
+
mode=mode,
|
|
3841
|
+
link_trace_parts=link_trace_parts,
|
|
3842
|
+
cpuset=peer.cpuset,
|
|
3843
|
+
),
|
|
3696
3844
|
peer,
|
|
3697
3845
|
)
|
|
3698
3846
|
_ota_run(peer, command, label=f"{peer_name}: start {target.name}", dry_run=dry_run)
|
|
@@ -4089,6 +4237,7 @@ def _ota_create_tmux(
|
|
|
4089
4237
|
peer_args,
|
|
4090
4238
|
mode="attach",
|
|
4091
4239
|
link_trace_parts=link_trace_parts,
|
|
4240
|
+
cpuset=first_peer.cpuset,
|
|
4092
4241
|
),
|
|
4093
4242
|
first_peer,
|
|
4094
4243
|
)
|
|
@@ -4169,6 +4318,7 @@ def _ota_create_tmux(
|
|
|
4169
4318
|
peer_args,
|
|
4170
4319
|
mode="attach",
|
|
4171
4320
|
link_trace_parts=link_trace_parts,
|
|
4321
|
+
cpuset=peer.cpuset,
|
|
4172
4322
|
),
|
|
4173
4323
|
peer,
|
|
4174
4324
|
)
|
|
@@ -5235,6 +5385,43 @@ def _local_ipc_run_args(network_isolated: bool) -> list[str]:
|
|
|
5235
5385
|
return [] if network_isolated else ["--ipc", "host"]
|
|
5236
5386
|
|
|
5237
5387
|
|
|
5388
|
+
_CPUSET_RE = re.compile(r"^\d+(-\d+)?(,\d+(-\d+)?)*$")
|
|
5389
|
+
|
|
5390
|
+
|
|
5391
|
+
def validate_cpuset(cpuset: str) -> str:
|
|
5392
|
+
"""Accept a docker `--cpuset-cpus` list, and nothing else.
|
|
5393
|
+
|
|
5394
|
+
This string is handed to `docker run` and travels to a peer over ssh, so it
|
|
5395
|
+
is validated rather than trusted: only decimal CPU numbers and ranges, the
|
|
5396
|
+
exact grammar docker documents. Anything else — a shell metacharacter, a
|
|
5397
|
+
negative number, an empty element — is a caller error and says so here
|
|
5398
|
+
instead of at the far end of an ssh pipe.
|
|
5399
|
+
"""
|
|
5400
|
+
cleaned = cpuset.strip()
|
|
5401
|
+
if not _CPUSET_RE.match(cleaned):
|
|
5402
|
+
raise RuntimeError(
|
|
5403
|
+
f"--cpuset {cpuset!r} is not a CPU list: expected decimal numbers and ranges like '0-3' or '0,2,4-7'."
|
|
5404
|
+
)
|
|
5405
|
+
for part in cleaned.split(","):
|
|
5406
|
+
if "-" in part:
|
|
5407
|
+
low, high = (int(x) for x in part.split("-", 1))
|
|
5408
|
+
if high < low:
|
|
5409
|
+
raise RuntimeError(f"--cpuset {cpuset!r}: range {part!r} counts backwards.")
|
|
5410
|
+
return cleaned
|
|
5411
|
+
|
|
5412
|
+
|
|
5413
|
+
def parse_cpuset_list(cpuset: str) -> list[int]:
|
|
5414
|
+
"""Expand a validated CPU list into the individual CPU numbers."""
|
|
5415
|
+
cpus: list[int] = []
|
|
5416
|
+
for part in validate_cpuset(cpuset).split(","):
|
|
5417
|
+
if "-" in part:
|
|
5418
|
+
low, high = (int(x) for x in part.split("-", 1))
|
|
5419
|
+
cpus.extend(range(low, high + 1))
|
|
5420
|
+
else:
|
|
5421
|
+
cpus.append(int(part))
|
|
5422
|
+
return sorted(set(cpus))
|
|
5423
|
+
|
|
5424
|
+
|
|
5238
5425
|
def _base_extra_run_args(
|
|
5239
5426
|
runtime: RuntimeConfig,
|
|
5240
5427
|
session: ResolvedSession,
|
|
@@ -5242,8 +5429,15 @@ def _base_extra_run_args(
|
|
|
5242
5429
|
instance: SessionInstance,
|
|
5243
5430
|
*,
|
|
5244
5431
|
network_isolated: bool = False,
|
|
5432
|
+
cpuset: str | None = None,
|
|
5245
5433
|
) -> list[str]:
|
|
5246
5434
|
args: list[str] = _local_ipc_run_args(network_isolated)
|
|
5435
|
+
if cpuset:
|
|
5436
|
+
# Confine every container of this session to the given CPUs. On a shared
|
|
5437
|
+
# workstation the point is not to slow this session down: it is to put it
|
|
5438
|
+
# on the cores that were reserved away from whoever else is on the
|
|
5439
|
+
# machine, so a latency measurement stops competing for a runqueue.
|
|
5440
|
+
args.extend(["--cpuset-cpus", validate_cpuset(cpuset)])
|
|
5247
5441
|
ros2docker_cfg = load_config(runtime.ros2docker_config)
|
|
5248
5442
|
|
|
5249
5443
|
if not ros2docker_cfg.get("mount_ws"):
|
|
@@ -5427,7 +5621,9 @@ def start_session(args: argparse.Namespace) -> str:
|
|
|
5427
5621
|
" (or pass --force to replace it, --skip-conflict-check to ignore it).",
|
|
5428
5622
|
)
|
|
5429
5623
|
image_name = _scoped_image_name(runtime)
|
|
5430
|
-
extra_run_args = _base_extra_run_args(
|
|
5624
|
+
extra_run_args = _base_extra_run_args(
|
|
5625
|
+
runtime, session, cfg, instance, network_isolated=network_isolated, cpuset=getattr(args, "cpuset", None)
|
|
5626
|
+
)
|
|
5431
5627
|
run_override: dict[str, object] = {}
|
|
5432
5628
|
network_name = getattr(args, "network_name", None)
|
|
5433
5629
|
if network_name:
|
|
@@ -8912,6 +9108,18 @@ def _add_ota_install_args(parser: argparse.ArgumentParser) -> None:
|
|
|
8912
9108
|
"Required for every peer in that mode; the same repository is at a different path on each machine."
|
|
8913
9109
|
),
|
|
8914
9110
|
)
|
|
9111
|
+
parser.add_argument(
|
|
9112
|
+
"--peer-cpuset",
|
|
9113
|
+
action="append",
|
|
9114
|
+
default=[],
|
|
9115
|
+
metavar="PEER=CPUS",
|
|
9116
|
+
help=(
|
|
9117
|
+
"Confine this peer's session containers to these CPUs, e.g. 'a=0-3'. For a shared "
|
|
9118
|
+
"workstation whose CPUs were reserved away from another account: the run then measures "
|
|
9119
|
+
"on cores nobody else can take, and the load preflight judges those cores instead of "
|
|
9120
|
+
"the whole machine."
|
|
9121
|
+
),
|
|
9122
|
+
)
|
|
8915
9123
|
parser.add_argument(
|
|
8916
9124
|
"--peer-exec",
|
|
8917
9125
|
action="append",
|
|
@@ -8980,6 +9188,14 @@ def _add_start_args(parser: argparse.ArgumentParser) -> None:
|
|
|
8980
9188
|
help="Require three bounded ICMP probes to the peer to succeed before starting.",
|
|
8981
9189
|
)
|
|
8982
9190
|
parser.add_argument("--instance-id", help="Join or create a named runtime session instance.")
|
|
9191
|
+
parser.add_argument(
|
|
9192
|
+
"--cpuset",
|
|
9193
|
+
help=(
|
|
9194
|
+
"Confine this session's containers to these CPUs, e.g. '0-3'. Use on a shared "
|
|
9195
|
+
"workstation where those CPUs were reserved away from another account: the "
|
|
9196
|
+
"session then measures on cores nobody else can take."
|
|
9197
|
+
),
|
|
9198
|
+
)
|
|
8983
9199
|
parser.add_argument(
|
|
8984
9200
|
"--skip-conflict-check",
|
|
8985
9201
|
action="store_true",
|
|
@@ -5214,6 +5214,7 @@ def _make_live_run_point(args: argparse.Namespace, session_name: str) -> RunPoin
|
|
|
5214
5214
|
peer_address=getattr(args, "peer_address", None),
|
|
5215
5215
|
peer_ssh=getattr(args, "peer_ssh", None),
|
|
5216
5216
|
peer_checkout=getattr(args, "peer_checkout", None),
|
|
5217
|
+
peer_cpuset=getattr(args, "peer_cpuset", None),
|
|
5217
5218
|
peer_exec=getattr(args, "peer_exec", None),
|
|
5218
5219
|
install_mode=getattr(args, "install_mode", None),
|
|
5219
5220
|
install_pin=getattr(args, "install_pin", None),
|
{rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev62}/tests/contract/test_packaged_resources.py
RENAMED
|
@@ -537,3 +537,36 @@ def test_every_default_ota_profile_template_is_packaged() -> None:
|
|
|
537
537
|
defaults = list(gsf._DDS_OTA_DEFAULT_CONFIG.values()) + list(gsf._DDS_LOCAL_DEFAULT_CONFIG.values())
|
|
538
538
|
missing = [name for name in defaults if name and not (ota_configs / f"{name}.template").exists()]
|
|
539
539
|
assert not missing, f"default profile templates absent from the package: {missing}"
|
|
540
|
+
|
|
541
|
+
|
|
542
|
+
def test_benchmark_forwards_every_shared_ota_peer_option() -> None:
|
|
543
|
+
"""A hand-copied field list drops new options silently, and did.
|
|
544
|
+
|
|
545
|
+
`cli_benchmark` builds an `argparse.Namespace` for the OTA layer by naming
|
|
546
|
+
each field it forwards. `--peer-cpuset` was added to the shared OTA argument
|
|
547
|
+
group, reached the parser, and was accepted on the command line — and then
|
|
548
|
+
never arrived, because this list did not mention it. Every unit test passed
|
|
549
|
+
and the feature was inert on the machine it was built for.
|
|
550
|
+
|
|
551
|
+
So the list is checked against the parser rather than maintained by hand:
|
|
552
|
+
whatever `_add_ota_install_args` defines has to appear here too.
|
|
553
|
+
"""
|
|
554
|
+
import argparse
|
|
555
|
+
import inspect
|
|
556
|
+
|
|
557
|
+
import rosotacom.cli as cli
|
|
558
|
+
import rosotacom.cli_benchmark as cli_benchmark
|
|
559
|
+
|
|
560
|
+
parser = argparse.ArgumentParser()
|
|
561
|
+
cli._add_ota_install_args(parser)
|
|
562
|
+
peer_dests = {
|
|
563
|
+
action.dest for action in parser._actions if action.dest.startswith("peer_") and action.dest != "help"
|
|
564
|
+
}
|
|
565
|
+
assert peer_dests, "the shared OTA argument group defines no --peer-* options"
|
|
566
|
+
|
|
567
|
+
forwarded = inspect.getsource(cli_benchmark)
|
|
568
|
+
missing = sorted(dest for dest in peer_dests if f"{dest}=getattr(args" not in forwarded)
|
|
569
|
+
assert not missing, (
|
|
570
|
+
f"cli_benchmark does not forward {', '.join(missing)} into the OTA namespace, "
|
|
571
|
+
"so the option is accepted on the command line and then ignored"
|
|
572
|
+
)
|
|
@@ -2258,6 +2258,19 @@ def test_resolve_session_and_base_extra_run_args(tmp_path: Path, monkeypatch: py
|
|
|
2258
2258
|
assert resolved.container_dir == "/session/definitions/1_heartbeat"
|
|
2259
2259
|
assert f"{rosotacom.WS_DIR.resolve()}:/ws" in args
|
|
2260
2260
|
assert f"{runtime.session_configs_dir[0]}:/session/definitions:ro" in args
|
|
2261
|
+
assert "--cpuset-cpus" not in args
|
|
2262
|
+
|
|
2263
|
+
# A cpuset confines every container of the session. Without this the CPUs a
|
|
2264
|
+
# shared workstation reserved are merely idle, not used by the run.
|
|
2265
|
+
pinned = rosotacom._base_extra_run_args(runtime, resolved, {"peers": {"a": {}, "b": {}}}, instance, cpuset=" 0-3 ")
|
|
2266
|
+
assert pinned[pinned.index("--cpuset-cpus") + 1] == "0-3"
|
|
2267
|
+
|
|
2268
|
+
# It is validated here too, not only where it is parsed: this is the last
|
|
2269
|
+
# place before the string becomes part of a docker command line.
|
|
2270
|
+
with pytest.raises(RuntimeError):
|
|
2271
|
+
rosotacom._base_extra_run_args(
|
|
2272
|
+
runtime, resolved, {"peers": {"a": {}, "b": {}}}, instance, cpuset="0-3; rm -rf /"
|
|
2273
|
+
)
|
|
2261
2274
|
assert f"{runtime.session_instances_dir}:/session/instances" in args
|
|
2262
2275
|
assert "Configured sessions:" in rosotacom._format_available_sessions(runtime)
|
|
2263
2276
|
# A peer shares the host IPC namespace, so it can exchange local-domain
|
|
@@ -796,3 +796,150 @@ def test_the_load_check_does_not_retry(
|
|
|
796
796
|
rosotacom._ota_load_check(_load_plan(tmp_path), dry_run=False)
|
|
797
797
|
|
|
798
798
|
assert len(calls) == 1
|
|
799
|
+
|
|
800
|
+
|
|
801
|
+
def test_validate_cpuset_accepts_a_cpu_list_and_nothing_else() -> None:
|
|
802
|
+
assert rosotacom.validate_cpuset("0-3") == "0-3"
|
|
803
|
+
assert rosotacom.validate_cpuset(" 0,2,4-7 ") == "0,2,4-7"
|
|
804
|
+
assert rosotacom.parse_cpuset_list("0-3,8") == [0, 1, 2, 3, 8]
|
|
805
|
+
|
|
806
|
+
# The string reaches `docker run` and travels over ssh, so it is validated
|
|
807
|
+
# rather than trusted.
|
|
808
|
+
for bad in ("0-3; rm -rf /", "$(id)", "0..3", "-1", "", "0-", "a-b"):
|
|
809
|
+
with pytest.raises(RuntimeError):
|
|
810
|
+
rosotacom.validate_cpuset(bad)
|
|
811
|
+
with pytest.raises(RuntimeError) as excinfo:
|
|
812
|
+
rosotacom.validate_cpuset("7-2")
|
|
813
|
+
assert "backwards" in str(excinfo.value)
|
|
814
|
+
|
|
815
|
+
|
|
816
|
+
def test_cpu_busy_is_read_from_two_snapshots() -> None:
|
|
817
|
+
"""A busy fraction needs the delta; the absolute counters describe boot onward."""
|
|
818
|
+
stdout = (
|
|
819
|
+
"cpu0 100 0 100 800 0 0 0 0 0 0\n"
|
|
820
|
+
"cpu1 100 0 100 800 0 0 0 0 0 0\n"
|
|
821
|
+
"---\n"
|
|
822
|
+
# cpu0 gains 100 busy and 900 idle -> 10% busy.
|
|
823
|
+
"cpu0 150 0 150 1700 0 0 0 0 0 0\n"
|
|
824
|
+
# cpu1 gains 900 busy and 100 idle -> 90% busy.
|
|
825
|
+
"cpu1 550 0 550 900 0 0 0 0 0 0\n"
|
|
826
|
+
)
|
|
827
|
+
busy = rosotacom._parse_cpu_busy(stdout, [0, 1])
|
|
828
|
+
assert busy is not None
|
|
829
|
+
assert busy[0] == pytest.approx(0.10, abs=0.01)
|
|
830
|
+
assert busy[1] == pytest.approx(0.90, abs=0.01)
|
|
831
|
+
|
|
832
|
+
# iowait does not count as competition for the runqueue: a peer writing a
|
|
833
|
+
# bag must not read as a peer that cannot be measured on.
|
|
834
|
+
io = "cpu0 0 0 0 100 0 0 0 0 0 0\n---\ncpu0 0 0 0 100 900 0 0 0 0 0\n"
|
|
835
|
+
busy_io = rosotacom._parse_cpu_busy(io, [0])
|
|
836
|
+
assert busy_io is not None
|
|
837
|
+
assert busy_io[0] == pytest.approx(0.0, abs=0.01)
|
|
838
|
+
|
|
839
|
+
assert rosotacom._parse_cpu_busy("no separator here", [0]) is None
|
|
840
|
+
assert rosotacom._parse_cpu_busy(stdout, [9]) is None # a CPU the peer does not have
|
|
841
|
+
|
|
842
|
+
|
|
843
|
+
def _pinned_peer(cpuset: str = "0-3") -> rosotacom.OtaSmokePeer:
|
|
844
|
+
return rosotacom.OtaSmokePeer("a", "robot-a", "10.0.0.10", cpuset=cpuset)
|
|
845
|
+
|
|
846
|
+
|
|
847
|
+
def _busy_probe(fraction: float) -> str:
|
|
848
|
+
"""Two snapshots in which every CPU is `fraction` busy."""
|
|
849
|
+
lines = [f"cpu{cpu} 0 0 0 0 0 0 0 0 0 0" for cpu in range(4)]
|
|
850
|
+
after = [f"cpu{cpu} {int(1000 * fraction)} 0 0 {int(1000 * (1 - fraction))} 0 0 0 0 0 0" for cpu in range(4)]
|
|
851
|
+
return "\n".join(lines) + "\n---\n" + "\n".join(after) + "\n"
|
|
852
|
+
|
|
853
|
+
|
|
854
|
+
def test_cpuset_check_refuses_when_the_reserved_cpus_are_not_free(
|
|
855
|
+
monkeypatch: pytest.MonkeyPatch,
|
|
856
|
+
) -> None:
|
|
857
|
+
"""A reservation that is not in force must not read as a quiet machine."""
|
|
858
|
+
monkeypatch.setattr(
|
|
859
|
+
rosotacom,
|
|
860
|
+
"_ota_run",
|
|
861
|
+
lambda peer, script, **kw: subprocess.CompletedProcess([script], 0, _busy_probe(0.95), ""),
|
|
862
|
+
)
|
|
863
|
+
with pytest.raises(RuntimeError) as excinfo:
|
|
864
|
+
rosotacom._ota_cpuset_load_check(_pinned_peer(), dry_run=False)
|
|
865
|
+
message = str(excinfo.value)
|
|
866
|
+
assert "not free" in message
|
|
867
|
+
assert "0-3" in message
|
|
868
|
+
|
|
869
|
+
|
|
870
|
+
def test_cpuset_check_passes_on_reserved_cpus_that_are_idle(
|
|
871
|
+
monkeypatch: pytest.MonkeyPatch,
|
|
872
|
+
capsys: pytest.CaptureFixture[str],
|
|
873
|
+
) -> None:
|
|
874
|
+
monkeypatch.setattr(
|
|
875
|
+
rosotacom,
|
|
876
|
+
"_ota_run",
|
|
877
|
+
lambda peer, script, **kw: subprocess.CompletedProcess([script], 0, _busy_probe(0.02), ""),
|
|
878
|
+
)
|
|
879
|
+
assert rosotacom._ota_cpuset_load_check(_pinned_peer(), dry_run=False) is True
|
|
880
|
+
assert "CPUs 0-3 are 2% busy" in capsys.readouterr().out
|
|
881
|
+
|
|
882
|
+
|
|
883
|
+
def test_a_pinned_peer_is_not_judged_by_the_machine_wide_load(
|
|
884
|
+
tmp_path: Path,
|
|
885
|
+
monkeypatch: pytest.MonkeyPatch,
|
|
886
|
+
) -> None:
|
|
887
|
+
"""The whole point: a reserved workstation reads high and is still measurable.
|
|
888
|
+
|
|
889
|
+
Measured on majestic on 2026-08-19: 68-75 on 32 CPUs with four cores reserved
|
|
890
|
+
away from the account carrying that load. loadavg cannot see the reservation
|
|
891
|
+
— the other account's threads all stayed runnable, they just queue on fewer
|
|
892
|
+
cores — so judging the machine refuses a run the reservation made possible.
|
|
893
|
+
"""
|
|
894
|
+
scripts: list[str] = []
|
|
895
|
+
|
|
896
|
+
def fake_ota_run(peer: object, script: str, **kwargs: object) -> subprocess.CompletedProcess[str]:
|
|
897
|
+
scripts.append(script)
|
|
898
|
+
if "/proc/stat" in script:
|
|
899
|
+
return subprocess.CompletedProcess([script], 0, _busy_probe(0.01), "")
|
|
900
|
+
return subprocess.CompletedProcess([script], 0, "75.68 68.49 67.20\n32\n", "")
|
|
901
|
+
|
|
902
|
+
monkeypatch.setattr(rosotacom, "_ota_run", fake_ota_run)
|
|
903
|
+
plan = rosotacom.OtaSmokePlan(
|
|
904
|
+
state_path=tmp_path / "ota-deployment.yaml",
|
|
905
|
+
workdir="/tmp/rosotacom_ota",
|
|
906
|
+
rosotacom="rosotacom",
|
|
907
|
+
project="rosotacom.yaml",
|
|
908
|
+
peers={"a": _pinned_peer()},
|
|
909
|
+
)
|
|
910
|
+
|
|
911
|
+
rosotacom._ota_load_check(plan, dry_run=False) # must not raise
|
|
912
|
+
|
|
913
|
+
assert any("/proc/stat" in s for s in scripts)
|
|
914
|
+
assert not any("loadavg" in s for s in scripts), "a pinned peer must not fall back to loadavg"
|
|
915
|
+
|
|
916
|
+
|
|
917
|
+
def test_an_unpinned_peer_still_uses_the_machine_wide_load(
|
|
918
|
+
tmp_path: Path,
|
|
919
|
+
monkeypatch: pytest.MonkeyPatch,
|
|
920
|
+
) -> None:
|
|
921
|
+
monkeypatch.setattr(
|
|
922
|
+
rosotacom,
|
|
923
|
+
"_ota_run",
|
|
924
|
+
_fake_ota_run_factory({"/proc/loadavg": "78.24 81.86 81.28\n32\n"}),
|
|
925
|
+
)
|
|
926
|
+
with pytest.raises(RuntimeError) as excinfo:
|
|
927
|
+
rosotacom._ota_load_check(_load_plan(tmp_path), dry_run=False)
|
|
928
|
+
assert "--peer-cpuset" in str(excinfo.value), "the refusal should name the way out"
|
|
929
|
+
|
|
930
|
+
|
|
931
|
+
def test_a_pinned_peer_confines_its_containers(tmp_path: Path) -> None:
|
|
932
|
+
"""The reservation only buys anything if the containers actually land on it."""
|
|
933
|
+
target = rosotacom.InteractiveSmokeTarget(
|
|
934
|
+
name="s",
|
|
935
|
+
target_type="session",
|
|
936
|
+
session=rosotacom.ResolvedSession(host_dir=tmp_path, container_dir="/s", source="test"),
|
|
937
|
+
cfg={},
|
|
938
|
+
)
|
|
939
|
+
parts = rosotacom._ota_start_parts(target, "a", "id-1", [], mode="detached", cpuset="0-3")
|
|
940
|
+
assert "--cpuset" in parts
|
|
941
|
+
assert parts[parts.index("--cpuset") + 1] == "0-3"
|
|
942
|
+
|
|
943
|
+
# And without one, nothing is added: the flag must not appear on a peer that
|
|
944
|
+
# was never pinned, or every unpinned run changes shape too.
|
|
945
|
+
assert "--cpuset" not in rosotacom._ota_start_parts(target, "a", "id-1", [], mode="detached")
|
{rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev62}/.devcontainer/devcontainer.json.template
RENAMED
|
File without changes
|
{rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev62}/.devcontainer/re_generate_devcontainer_json.py
RENAMED
|
File without changes
|
|
File without changes
|
{rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev62}/.github/ISSUE_TEMPLATE/documentation-audit.md
RENAMED
|
File without changes
|
{rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev62}/.github/ISSUE_TEMPLATE/implementation-cleanup.md
RENAMED
|
File without changes
|
{rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev62}/.github/ISSUE_TEMPLATE/quality-workflow.md
RENAMED
|
File without changes
|
|
File without changes
|
{rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev62}/.github/ISSUE_TEMPLATE/release-process.md
RENAMED
|
File without changes
|
{rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev62}/.github/ISSUE_TEMPLATE/repository-diagnosis.md
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{rosotacom_dev-2.5.dev61 → rosotacom_dev-2.5.dev62}/.github/workflows/benchmark-calibrate.yml
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|