mirror of
https://github.com/jmcorgan/fips.git
synced 2026-07-30 19:46:15 +00:00
Scope chaos host interface names per scenario and reap orphaned pairs
Ethernet edges get a veth pair created in the host namespace and then moved into the containers, named from the node ids alone. ethernet-mesh and ethernet-only both connect n01 to n04 and always run together, so one tore down the other's live link. Hash the scenario suffix to four hex characters and insert those after the vh prefix; 15 characters is far too few to carry the suffix itself. The names are now run-specific and never regenerated, so the delete-before-create no longer reclaims an interface an earlier run abandoned. ci-cleanup.sh takes over, deriving the names from the suffixes a run's teardown hands it. It is the first thing that script removes that is neither a docker object nor labelled, so an unscoped reap can sever a running bare simulation's links; testing/README.md and the --reap help say so.
This commit is contained in:
@@ -2,7 +2,9 @@
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import hashlib
|
||||
import os
|
||||
import sys
|
||||
|
||||
|
||||
def name_suffix() -> str:
|
||||
@@ -15,3 +17,25 @@ def name_suffix() -> str:
|
||||
bare ``chaos.sh`` run renders exactly the same names as it always has.
|
||||
"""
|
||||
return os.environ.get("FIPS_CI_NAME_SUFFIX", "")
|
||||
|
||||
|
||||
def veth_token(suffix: str) -> str:
|
||||
"""Shorten a name suffix to four hex characters.
|
||||
|
||||
Host interface names have only 15 characters to work with, far fewer
|
||||
than the suffix needs, so concurrent scenarios are told apart by a
|
||||
hash of it instead. Empty for an empty suffix, so a bare run's
|
||||
interface names are unchanged.
|
||||
"""
|
||||
if not suffix:
|
||||
return ""
|
||||
return hashlib.sha1(suffix.encode()).hexdigest()[:4]
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
# Print the token for each suffix given on the command line, one per
|
||||
# line. ci-cleanup.sh reaps host interfaces by token and calls this
|
||||
# rather than re-deriving the hash, so widening the token here cannot
|
||||
# leave the reaper matching the old width.
|
||||
for arg in sys.argv[1:]:
|
||||
print(veth_token(arg))
|
||||
|
||||
@@ -8,7 +8,7 @@ from collections import deque
|
||||
from dataclasses import dataclass, field
|
||||
|
||||
from .keys import derive
|
||||
from .naming import name_suffix
|
||||
from .naming import name_suffix, veth_token
|
||||
from .scenario import TopologyConfig
|
||||
|
||||
|
||||
@@ -33,6 +33,15 @@ class SimTopology:
|
||||
# outside the CI harness, which keeps a bare run's names unchanged.
|
||||
name_suffix: str = ""
|
||||
|
||||
@property
|
||||
def veth_token(self) -> str:
|
||||
"""Short stand-in for the suffix, for names bound by IFNAMSIZ.
|
||||
|
||||
Derived rather than stored so no caller can build a topology whose
|
||||
host names are scoped differently from its container names.
|
||||
"""
|
||||
return veth_token(self.name_suffix)
|
||||
|
||||
def transport_for_edge(self, a: str, b: str) -> str:
|
||||
"""Get the transport type for an edge (defaults to 'udp')."""
|
||||
edge = _make_edge(a, b)
|
||||
@@ -113,6 +122,26 @@ class SimTopology:
|
||||
def container_name(self, node_id: str) -> str:
|
||||
return f"fips-node-{node_id}{self.name_suffix}"
|
||||
|
||||
def veth_host_name(self, node_a: str, node_b: str, end: str) -> str:
|
||||
"""Generate the host-namespace veth name for one end of an edge.
|
||||
|
||||
Format: ``vh{token}{NN}{MM}{end}`` (max 15 chars for IFNAMSIZ).
|
||||
Host interfaces are global, so the token keeps a scenario from
|
||||
deleting a concurrent scenario's pair; it is empty outside the CI
|
||||
harness, yielding the same "vh0104a" this has always produced.
|
||||
|
||||
``node_a`` and ``node_b`` must be in canonical edge order. Unlike
|
||||
``veth_interface_name()`` this is not symmetric: the far end is
|
||||
``end="b"`` on the same ordering, so swapping the arguments names
|
||||
an interface that does not exist.
|
||||
"""
|
||||
nn_local = node_a.replace("n", "")
|
||||
nn_peer = node_b.replace("n", "")
|
||||
name = f"vh{self.veth_token}{nn_local}{nn_peer}{end}"
|
||||
if len(name) > 15:
|
||||
raise ValueError(f"veth host name too long: {name!r} ({len(name)} > 15)")
|
||||
return name
|
||||
|
||||
def directed_outbound(self) -> dict[str, list[str]]:
|
||||
"""Assign each static-config edge to exactly one node for outbound connection.
|
||||
|
||||
|
||||
@@ -4,7 +4,8 @@ Creates veth pairs between Docker containers for Ethernet-transport
|
||||
edges. Each Ethernet edge gets a veth pair with one end moved into
|
||||
each container's network namespace. Naming:
|
||||
|
||||
Host (temporary): vh{NN}{MM}a / vh{NN}{MM}b
|
||||
Host (temporary): vh{token}{NN}{MM}a / vh{token}{NN}{MM}b
|
||||
(via SimTopology.veth_host_name())
|
||||
Container: ve-{local}-{peer} (via veth_interface_name())
|
||||
|
||||
After creation, the container-side MAC addresses are queried and
|
||||
@@ -113,9 +114,7 @@ class VethManager:
|
||||
if a != node_id and b != node_id:
|
||||
continue
|
||||
# Remove existing pair if any (host-side might still exist)
|
||||
nn_a = a.replace("n", "")
|
||||
nn_b = b.replace("n", "")
|
||||
host_a = f"vh{nn_a}{nn_b}a"
|
||||
host_a = self.topology.veth_host_name(a, b, "a")
|
||||
_run_host(["ip", "link", "delete", host_a], image, check=False)
|
||||
# Re-create
|
||||
self._create_veth_pair(a, b, image)
|
||||
@@ -142,14 +141,14 @@ class VethManager:
|
||||
return
|
||||
|
||||
# Generate names
|
||||
nn_a = node_a.replace("n", "")
|
||||
nn_b = node_b.replace("n", "")
|
||||
host_a = f"vh{nn_a}{nn_b}a"
|
||||
host_b = f"vh{nn_a}{nn_b}b"
|
||||
host_a = self.topology.veth_host_name(node_a, node_b, "a")
|
||||
host_b = self.topology.veth_host_name(node_a, node_b, "b")
|
||||
final_a = veth_interface_name(node_a, node_b)
|
||||
final_b = veth_interface_name(node_b, node_a)
|
||||
|
||||
# Clean up any stale pair
|
||||
# Clean up a stale pair left by this scenario. The token makes the
|
||||
# name unique to this run, so a pair orphaned by an earlier run is
|
||||
# no longer reclaimed here — `ci-cleanup.sh` reaps those instead.
|
||||
_run_host(["ip", "link", "delete", host_a], image, check=False)
|
||||
|
||||
# Create veth pair on host
|
||||
|
||||
Reference in New Issue
Block a user