Harden the `tipc` two-host smoke test
Turn the physical-host sketch into an operator runbook covering cluster identity, interface and bearer setup, link validation, failure/rejoin testing, diagnostic capture and cleanup. Explain the cluster-domain-socket analogy and identify a future `pyroute2` TIPC codec as the path from manual `tipc(8)` commands to the same netlink management stack planned for WireGuard. Authorize `host_a_srv` by its stable import name so direct script execution does not expose only `__main__` while host B requests the callable's actual `NamespacePath`. Prompt-IO: ai/prompt-io/opencode/20260819T003327Z_53516b09_prompt_io.md (this patch was generated in some part by `opencode` using `gpt-5.6-sol` (`openai`))wkt/tipc_backend_378
parent
caf9c3d6d3
commit
ff2b20b7d1
|
|
@ -0,0 +1,37 @@
|
||||||
|
---
|
||||||
|
model: openai/gpt-5.6-sol
|
||||||
|
service: opencode
|
||||||
|
session: a998acff-af66-4969-a65e-f77cbf5ef8dd
|
||||||
|
timestamp: 2026-08-19T00:33:27Z
|
||||||
|
git_ref: wkt/tipc_backend_378
|
||||||
|
scope: code
|
||||||
|
substantive: true
|
||||||
|
raw_file: 20260819T003327Z_53516b09_prompt_io.raw.md
|
||||||
|
---
|
||||||
|
|
||||||
|
## Prompt
|
||||||
|
|
||||||
|
Develop a practical bootstrap plan for testing TIPC across two
|
||||||
|
physical motherboards.
|
||||||
|
|
||||||
|
## Response summary
|
||||||
|
|
||||||
|
Expanded the two-host example into a reproducible smoke-test runbook
|
||||||
|
and made host A authorize the stable module name imported by host B.
|
||||||
|
Documented the cluster-domain-socket analogy and the prospective
|
||||||
|
`pyroute2` management path.
|
||||||
|
|
||||||
|
## Files changed
|
||||||
|
|
||||||
|
- `examples/multihost/tipc_cluster/README.md` - document setup,
|
||||||
|
validation, resilience testing, capture and cleanup.
|
||||||
|
- `examples/multihost/tipc_cluster/host_a_srv.py` - authorize the
|
||||||
|
`host_a_srv` RPC module under direct script execution.
|
||||||
|
|
||||||
|
## Human edits
|
||||||
|
|
||||||
|
The human selected two separate physical motherboards as the target
|
||||||
|
environment and requested that the resulting local work be packaged
|
||||||
|
as its own commit boundary. The human also proposed “Cluster Domain
|
||||||
|
Sockets” as clearer terminology and reusing the planned `pyroute2`
|
||||||
|
dependency for TIPC management.
|
||||||
|
|
@ -0,0 +1,27 @@
|
||||||
|
---
|
||||||
|
model: openai/gpt-5.6-sol
|
||||||
|
service: opencode
|
||||||
|
timestamp: 2026-08-19T00:33:27Z
|
||||||
|
git_ref: wkt/tipc_backend_378
|
||||||
|
diff_cmd: git diff HEAD~1..HEAD
|
||||||
|
---
|
||||||
|
|
||||||
|
## Prompt
|
||||||
|
|
||||||
|
The user asked for a bootstrap plan to try TIPC with two real,
|
||||||
|
physically separate Linux motherboards.
|
||||||
|
|
||||||
|
## Response
|
||||||
|
|
||||||
|
The generated changes turn the existing sketch into an operator
|
||||||
|
runbook and correct the RPC module authorization used by direct
|
||||||
|
script execution:
|
||||||
|
|
||||||
|
> `git diff HEAD~1..HEAD -- examples/multihost/tipc_cluster/README.md examples/multihost/tipc_cluster/host_a_srv.py`
|
||||||
|
|
||||||
|
The runbook covers matching revisions, cluster identity, interface
|
||||||
|
selection, Ethernet bearer setup, link and name-table validation,
|
||||||
|
RPC, failure/rejoin testing, diagnostics, cleanup and a future
|
||||||
|
network-namespace fixture. It also records “Cluster Domain Sockets”
|
||||||
|
as explanatory terminology and identifies a future `pyroute2` TIPC
|
||||||
|
generic-netlink codec as the path away from manual `tipc(8)` calls.
|
||||||
|
|
@ -2,6 +2,10 @@
|
||||||
|
|
||||||
TIPC is a linux-kernel cluster IPC protocol whose service names
|
TIPC is a linux-kernel cluster IPC protocol whose service names
|
||||||
live in a **cluster-wide name table maintained by the kernel**.
|
live in a **cluster-wide name table maintained by the kernel**.
|
||||||
|
It is also described as **Cluster Domain Sockets**: the Unix-domain
|
||||||
|
socket model extended from one kernel to a cluster. That name is a
|
||||||
|
useful explanation for new users, while the code keeps `tipc` as its
|
||||||
|
protocol key to match Linux's `AF_TIPC`, kernel module and tooling.
|
||||||
For `tractor` that means:
|
For `tractor` that means:
|
||||||
|
|
||||||
- an actor's IPC address is a service name `(stype, instance)`,
|
- an actor's IPC address is a service name `(stype, instance)`,
|
||||||
|
|
@ -85,30 +89,55 @@ Everything above is single-node (`modprobe` is enough). To span
|
||||||
hosts you need a **bearer** on both, which is the one thing that
|
hosts you need a **bearer** on both, which is the one thing that
|
||||||
can't be CI'd.
|
can't be CI'd.
|
||||||
|
|
||||||
|
For the first physical test, use two wired Linux hosts on the same
|
||||||
|
L2 segment. Prefer a direct cable or uncomplicated switch; avoid
|
||||||
|
Wi-Fi, guest VLANs and port isolation until the basic link works.
|
||||||
|
Use the same checkout and Python environment on both hosts:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
# on BOTH hosts
|
# on BOTH hosts
|
||||||
|
git rev-parse HEAD # must match on A and B
|
||||||
|
uv sync --all-extras --dev
|
||||||
sudo modprobe tipc
|
sudo modprobe tipc
|
||||||
|
|
||||||
# over ethernet (L2) — simplest when the hosts share a segment
|
# choose the real wired iface; do not assume `eth0`
|
||||||
sudo tipc bearer enable media eth device eth0
|
ip -br link
|
||||||
|
IFACE=enp3s0
|
||||||
|
|
||||||
|
# inspect existing cluster identity before changing anything
|
||||||
|
tipc node get address # must differ between hosts
|
||||||
|
tipc node get netid # must match between hosts
|
||||||
|
|
||||||
|
# use one private test netid on BOTH hosts, before enabling bearers
|
||||||
|
sudo tipc node set netid 37801
|
||||||
|
|
||||||
|
# ethernet is simplest when the hosts share an L2 segment
|
||||||
|
sudo tipc bearer enable media eth device "$IFACE"
|
||||||
|
|
||||||
# ..or over UDP when L2 isn't available — and MANDATORY over a
|
# ..or over UDP when L2 isn't available — and MANDATORY over a
|
||||||
# `wg` mesh, see below
|
# `wg` mesh, see below
|
||||||
sudo tipc bearer enable media udp name uc localip 10.0.11.1
|
sudo tipc bearer enable media udp name uc localip 10.0.11.1
|
||||||
|
|
||||||
# verify BEFORE running anything: this must list the peer
|
# verify BEFORE running anything: this must list the peer
|
||||||
|
tipc bearer list
|
||||||
tipc link list
|
tipc link list
|
||||||
tipc node list
|
tipc node list
|
||||||
```
|
```
|
||||||
|
|
||||||
Then:
|
If the link does not appear, first verify carrier, a common TIPC
|
||||||
|
network ID, distinct node addresses, a common VLAN and compatible
|
||||||
|
MTUs. Ethernet TIPC uses EtherType traffic rather than IP routing,
|
||||||
|
so a successful `ping` alone does not prove the bearer can work.
|
||||||
|
|
||||||
|
Run each command from `examples/multihost/tipc_cluster/`:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
# host A
|
# host A
|
||||||
python host_a_srv.py
|
uv run python host_a_srv.py
|
||||||
|
|
||||||
# host B
|
# host B
|
||||||
python host_b_client.py
|
watch -n 0.5 tipc nametable show # optional second terminal
|
||||||
|
uv run python host_b_client.py
|
||||||
```
|
```
|
||||||
|
|
||||||
Note what's absent from both scripts: any IP, hostname or port.
|
Note what's absent from both scripts: any IP, hostname or port.
|
||||||
|
|
@ -116,6 +145,51 @@ Both sides name the *same service*, and the kernel routes it.
|
||||||
Move `host_a_srv.py` to a third node and host B's dial keeps
|
Move `host_a_srv.py` to a third node and host B's dial keeps
|
||||||
working, unchanged.
|
working, unchanged.
|
||||||
|
|
||||||
|
For a first resilience pass, use a local console or separate
|
||||||
|
management link so the test does not cut off your own SSH session:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# host B: record the healthy baseline
|
||||||
|
tipc link list
|
||||||
|
tipc link statistics show
|
||||||
|
|
||||||
|
# either host: withdraw and recreate the bearer
|
||||||
|
sudo tipc bearer disable media eth device "$IFACE"
|
||||||
|
tipc link list
|
||||||
|
sudo tipc bearer enable media eth device "$IFACE"
|
||||||
|
tipc link list
|
||||||
|
|
||||||
|
# prove name withdrawal/republication and RPC recovery
|
||||||
|
tipc nametable show
|
||||||
|
uv run python host_b_client.py
|
||||||
|
```
|
||||||
|
|
||||||
|
Capture `uname -a`, both node addresses, `tipc bearer list`,
|
||||||
|
`tipc link list`, `tipc link statistics show`, the name table and
|
||||||
|
both Python transcripts. Those artifacts distinguish an actor bug
|
||||||
|
from bearer discovery, cluster identity or switch configuration.
|
||||||
|
|
||||||
|
Clean up a disposable Ethernet test on both hosts with:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
sudo tipc bearer disable media eth device "$IFACE"
|
||||||
|
tipc link list
|
||||||
|
```
|
||||||
|
|
||||||
|
Restore any pre-existing network ID only after all bearers are
|
||||||
|
disabled. The first useful automation target is a two-node network
|
||||||
|
namespace fixture that asserts link-up, remote publication, RPC,
|
||||||
|
withdrawal and republication in that order; physical hardware then
|
||||||
|
remains the validation layer for real NIC and switch behaviour.
|
||||||
|
|
||||||
|
The commands above use iproute2's `tipc` frontend, which speaks the
|
||||||
|
kernel's `TIPCv2` generic-netlink family. The planned `pyroute2`
|
||||||
|
dependency already manages WireGuard, interfaces and namespaces and
|
||||||
|
provides generic-netlink primitives, but it does not currently ship a
|
||||||
|
TIPC message codec. Adding one upstream would let `tractor` replace
|
||||||
|
these manual commands with one Python netlink stack instead of
|
||||||
|
shelling out; until then, `tipc(8)` remains the canonical frontend.
|
||||||
|
|
||||||
### over a `wg` mesh
|
### over a `wg` mesh
|
||||||
|
|
||||||
TIPC over WireGuard is the intended reference multihost
|
TIPC over WireGuard is the intended reference multihost
|
||||||
|
|
@ -231,12 +305,10 @@ self-skip when the module is absent.
|
||||||
## CI
|
## CI
|
||||||
|
|
||||||
Single-host TIPC *is* CI-able — the module ships with the
|
Single-host TIPC *is* CI-able — the module ships with the
|
||||||
standard Ubuntu kernel package, so a `sudo modprobe tipc` step
|
standard Ubuntu kernel package. CI loads it with `sudo modprobe
|
||||||
plus a `--tpt-proto tipc` matrix entry should work. That's not
|
tipc` and runs the suite with `--tpt-proto tipc` as a blocking
|
||||||
wired up yet; verify in a throwaway workflow first, and fall
|
matrix leg. Cross-node bearer testing stays manual — this README
|
||||||
back to a container job with `--cap-add NET_ADMIN` if the
|
is that smoke test.
|
||||||
runners refuse. Cross-node (bearer) testing stays manual — this
|
|
||||||
README is that smoke test.
|
|
||||||
|
|
||||||
## normative refs
|
## normative refs
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -57,7 +57,10 @@ async def main() -> None:
|
||||||
) as an:
|
) as an:
|
||||||
await an.start_actor(
|
await an.start_actor(
|
||||||
'host_a',
|
'host_a',
|
||||||
enable_modules=[__name__],
|
# Host B imports this same module name to construct the
|
||||||
|
# RPC `NamespacePath`; direct script execution would
|
||||||
|
# otherwise expose it as `__main__` on host A.
|
||||||
|
enable_modules=['host_a_srv'],
|
||||||
)
|
)
|
||||||
print(
|
print(
|
||||||
'host_a up — `tipc nametable show` on EITHER host\n'
|
'host_a up — `tipc nametable show` on EITHER host\n'
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue