---
phase: 07-network-node-fault-injection
plan: "03"
subsystem: net
tags: [tc-netem, impairment, healing, fault-injection, cleanup]
dependency_graph:
  requires: ["07-02"]
  provides: ["net_impair", "net_heal", "net_heal_all"]
  affects: ["vmcluster_mcp/net/fault.py", "vmcluster_mcp/net/__init__.py"]
tech_stack:
  added: ["xml.etree.ElementTree for domain XML parsing", "tc netem qdisc management"]
  patterns: ["TDD RED→GREEN", "dispatch by fault_type", "best-effort cluster sweep"]
key_files:
  created: ["tests/test_net_impair.py", "tests/test_net_heal.py"]
  modified: ["vmcluster_mcp/net/fault.py", "vmcluster_mcp/net/__init__.py"]
decisions:
  - "discover_tap_interface takes libvirt_client as a parameter (testability)"
  - "net/__init__.py creates its own LibvirtClient(config.libvirt_uri) for tap discovery (avoids circular import risk, lazy connection)"
  - "net_heal always returns success=True when host state is absent (D-10)"
  - "net_heal_all is best-effort: per-handle exceptions are logged and skipped (D-11)"
  - "rule_identifiers['interface'] = tap_dev for impairment; parallel to ['comment_tag'] for partition"
metrics:
  duration_seconds: 138
  completed_date: "2026-03-24"
  tasks_completed: 2
  files_changed: 4
requirements-completed: [FAULT-02, FAULT-03, FAULT-04]
---

# Phase 07 Plan 03: net_impair, net_heal, net_heal_all — Summary

**One-liner:** tc netem impairment tool with handle-based and cluster-wide cleanup via FaultRegistry dispatch.

## What Was Built

Completed the fault injection/healing cycle for the `vmcluster-mcp` network fault package:

1. **`vmcluster_mcp/net/fault.py`** — extended with tc netem helpers:
   - `discover_tap_interface`: parses libvirt domain XML to locate the host tap device for a given bridge
   - `build_impairment_cmd`: builds `tc qdisc add dev {tap} root netem` commands; D-09 zero-param omission
   - `apply_impairment`: executes the tc command via `_run_host_cmd`, raises on failure
   - `remove_impairment`: issues `tc qdisc del dev {tap} root`, returns True/False (absent is not an error)

2. **`vmcluster_mcp/net/__init__.py`** — three new MCP tools:
   - `net_impair`: validates ≥1 param non-zero, discovers tap, applies tc netem, registers handle with `rule_identifiers={"interface": tap_dev}`
   - `net_heal`: looks up handle in registry, dispatches to `remove_rules_by_comment` (partition) or `remove_impairment` (impairment), removes registry entry; success when state already absent (D-10)
   - `net_heal_all`: best-effort cluster-wide sweep via `list_faults` + per-handle cleanup; per-handle exceptions logged and skipped (D-11); returns removed count

3. **`tests/test_net_impair.py`** (23 tests) — tap discovery, build_impairment_cmd combos, D-09 zero-omission, apply/remove host dispatch, net_impair tool paths

4. **`tests/test_net_heal.py`** (11 tests) — HANDLE_NOT_FOUND, partition/impairment dispatch, success-when-absent, registry removal, net_heal_all zero/multi/best-effort

## Commits

| Hash | Type | Description |
|------|------|-------------|
| f1b6f2a | test | Failing tests for tc netem helpers and net_impair (RED) |
| b5cf5a3 | feat | tc netem helpers + tap discovery + net_impair tool (GREEN) |
| deb6c41 | test | Failing tests for net_heal and net_heal_all (RED) |
| 8829d53 | feat | net_heal and net_heal_all tools (GREEN) |

## Verification

- `python -m pytest tests/test_net_impair.py tests/test_net_heal.py -x -v` → **34 passed**
- `python -m pytest` (full suite) → **345 passed, 0 failed**
- `ruff check vmcluster_mcp/net/fault.py vmcluster_mcp/net/__init__.py` → **All checks passed**

## Deviations from Plan

### Auto-fixed Issues

None — plan executed exactly as written.

### Notes

- Pre-imported `remove_impairment` and `remove_rules_by_comment` in Task 1 were deferred to Task 2 commit to satisfy ruff F401 (unused import) constraint. Both are used in Task 2 by net_heal/net_heal_all.

## Known Stubs

None — all functions fully implemented and tested.

## Self-Check: PASSED

- [x] `vmcluster_mcp/net/fault.py` — exists with `discover_tap_interface`, `build_impairment_cmd`, `apply_impairment`, `remove_impairment`
- [x] `vmcluster_mcp/net/__init__.py` — exports `net_impair`, `net_heal`, `net_heal_all`
- [x] `tests/test_net_impair.py` — 23 tests, 304 lines
- [x] `tests/test_net_heal.py` — 11 tests, 287 lines
- [x] Commits f1b6f2a, b5cf5a3, deb6c41, 8829d53 all in git log
