controller_host is recorded on the mounted compute sub-application
when the controller queries the compute capabilities; the test relied
on another test having made that call first and failed when run on
its own. Set the state directly in the test.
Every node lifecycle call (start/stop) emitted a "starting"/"stopping"
node.updated notification on entry, and for the whole duration of the
call asdict() masked the real status with the transitional one — every
compute event forwarded in that window was re-emitted as a duplicate
transitional status.
Stop-all/start-all on a 13-node project pushed two transitional events
per node before the final state. Clients that do not render these
transitional statuses (the Web UI) showed nodes flickering through an
unexpected suspended-looking state before reaching started/stopped.
Remove the transitional notification and the status masking: clients
see the real statuses again (started/stopped), from the forwarded
compute events plus the controller-side confirmation. The wait option
on the lifecycle routes, the strict stop error handling and
wait_for_status() are unchanged.
Resolve conflicts against the upstream wave (PRs #2952-#2965, incl. the
typed-response/schema hardening and the drop of Python 3.10):
- controller/node.py: keep upstream's _transitioning/_confirm_status
lifecycle guard; the kernel-anchor link wiring (node_started) runs
after the transition completes.
- controller/link.py: keep upstream's add_node rollback (release_ports
on create failure) and delete via release_ports; release_ports now
resolves ports live (same staleness reason as _bind_members) and
discards the node link unconditionally.
- schemas/controller/links.py: extend LinkFilterType with the eight
kernel-datapath filter types and LinkFilterParameter.type with "str"
so the newly typed /available_filters response validates; Link.filters
stays a free-form dict — the vocabulary is capability-gated per link
and validated by the controller (409), not by a strict request schema.
- schemas/controller/topology.py: drop the type: ignore made redundant
by the dict filters field.
- tests: route-level filter validation now expects the controller's 409
(unknown types, out-of-range values); packet_loss correlation is a
valid kernel parameter (positive test added); OpenAPI assertions match
the free-form filters property.
The live suite asserted the datapath over IPv4 only. Give every guest a
ULA IPv6 address next to its v4 one and twin every v4 assertion point,
sharing the impairment window (one filter application, both families
asserted) so the runtime grows by minutes, not double.
- harness: configure_ios(eth_ipv6=), ping() dispatches to `ping ipv6`,
docker_bring_up_ipv6 (the Docker daemon leaves eth0 with
disable_ipv6=1 on v6-less bridge networks, so the interfaces-file
route cannot work), family-aware docker_wait_address, pcap_records
scans every record (a leading DAD/MLD frame must not mask the
sampled ethertype), and pcap_icmp_rtt measures the wire round trip
from capture timestamps;
- scenarios: connectivity/delay/suspend/isolation/churn checks in both
families, captures assert 86dd next to 0800, the libpcap bpf drops
are asserted family-selective (icmp vs icmp6, each killing its own
family and leaving the other alive), the eBPF classifier modes on
both families, marker v6 twins with an echo-specific expression
(bare icmp6 also matches DAD/MLD and would break the exact counts),
the v6 sharkd replay, and the link-local matrix gains the 86dd
all-nodes row; serial (HDLC) legs stay v4-only;
- the dynamips-emulated classic IOS under-reports its `ping ipv6` RTT
by ~40 % of a delayed round trip (pcap-verified: the wire carries the
v6 echo exchange in the same ~230 ms as v4 while the console reports
~130 ms), so the dynamips-family scenarios assert the v6 delay from
the capture's own timestamps — recorded in tests/e2e/README.md.
Full suite: 17 passed in 58m50s.