Skip to content

Add netplan provider for network.managed (fixes #62219)#69615

Open
ggiesen wants to merge 14 commits into
saltstack:3006.xfrom
ggiesen:feature-netplan-ip-provider
Open

Add netplan provider for network.managed (fixes #62219)#69615
ggiesen wants to merge 14 commits into
saltstack:3006.xfrom
ggiesen:feature-netplan-ip-provider

Conversation

@ggiesen

@ggiesen ggiesen commented Jul 1, 2026

Copy link
Copy Markdown
Contributor

What does this PR do?

Adds a netplan provider for network.managed so it manages netplan YAML on netplan-based systems instead of writing /etc/network/interfaces, which netplan ignores.

On Ubuntu 18.04+ (and Debian where netplan is the active renderer), the legacy debian_ip provider writes /etc/network/interfaces (ifupdown). netplan does not read that file, so network.managed silently has no effect -- issue #62219.

New module salt/modules/netplan_ip.py:

  • Claims the ip virtual only when netplan is active (the netplan command + /etc/netplan present); debian_ip now defers in that case, so exactly one provider owns ip on any box.
  • Generates per-interface netplan v2 YAML at /etc/netplan/90-salt-<iface>.yaml (prefix 90 so salt-managed config wins over cloud-init's 50-cloud-init.yaml) and applies with netplan generate + netplan apply.
  • Supports eth, bond, vlan, and bridge (addresses, dhcp4/dhcp6, gateway as a default route, nameservers, mtu; bond slaves/params, vlan id/link, bridge ports/params), plus routes and up/down. Referenced member NICs (bond slaves, bridge ports, vlan parent) are declared so netplan generate resolves them.
  • ifupdown-only options with no netplan equivalent (ethtool offload, up/down hook scripts) raise an informative error instead of being silently dropped.

What issues does this PR fix or reference?

Fixes #62219.

Previous Behavior

network.managed on Ubuntu 18.04+ wrote /etc/network/interfaces; netplan ignored it, so interfaces were never configured as declared.

New Behavior

network.managed writes and applies /etc/netplan/90-salt-<iface>.yaml; the interface comes up via the active renderer (networkd/NetworkManager) and persists across reboot. debian_ip continues to handle non-netplan Debian unchanged.

Testing

Unit tests for netplan_ip (all interface types, provider selection, apply, routes) and new baseline tests pinning debian_ip.__virtual__ selection. All green; black/isort/pylint clean.

Beyond unit tests, validated behaviorally on real VMs (VMware Workstation via Vagrant), since config application and reboot-persistence can't be exercised in a container:

OS Scenario Provider selected Apply Survived reboot
Ubuntu 24.04 eth netplan_ip pass pass
Ubuntu 24.04 vlan + bond + bridge netplan_ip pass pass
Ubuntu 22.04 eth netplan_ip pass pass
Debian 13 (no netplan) eth debian_ip pass pass
Debian 13 (+ netplan.io) eth netplan_ip pass pass

The interface types create a real 802.1Q vlan, an active-backup bond over two slaves, and a bridge with a port -- each up with its address and persistent across reboot. Provider selection verified in all three modes (netplan Ubuntu, non-netplan Debian, Debian with netplan installed).

Branch

Targeted at 3006.x since #62219 affects the LTS line. Happy to rebase onto master (or another branch) if the team considers a new provider a feature that doesn't belong in 3006.x -- just say which branch you'd prefer.

Merge requirements satisfied?

[NOTICE] Bug fixes or features added to Salt require tests.

  • Docs (module reference stub)
  • Changelog
  • Tests written/updated

Commits signed with GPG?

No

ggiesen added 5 commits June 30, 2026 15:23
Baseline regression tests before adding netplan-aware provider selection.
debian_ip.__virtual__ had zero test coverage; these pin that it claims the
'ip' provider on the Debian os_family and declines elsewhere, so the
upcoming change (deferring to a netplan provider when netplan is active)
can't silently alter which systems debian_ip manages.
On netplan systems (Ubuntu 18.04+ and Debian where netplan is the active
renderer), network.managed wrote /etc/network/interfaces via debian_ip,
which netplan ignores -- so interface config never took effect.

Add salt/modules/netplan_ip.py: a new 'ip' provider that generates
per-interface netplan v2 YAML under /etc/netplan/90-salt-<iface>.yaml and
applies it via 'netplan generate'/'netplan apply'. It claims the 'ip'
virtual when the netplan command and /etc/netplan are present; debian_ip
now defers in that case so exactly one provider owns 'ip'.

This first increment covers ethernet interfaces (static/dhcp4/dhcp6,
addresses, gateway as a default route, nameservers, mtu), routes, and
apply/up/down. ifupdown-only options (ethtool, up/down hooks) raise an
informative error rather than being silently dropped. bonds/vlans/bridges
are not yet implemented (the state's bond path is gated on ip.get_bond,
which this provider does not yet define, so it is safely skipped).

Targets 3006.x; can be rebased forward if maintainers prefer a later line.

Refs saltstack#62219
Extend build_interface beyond ethernet:
- bond: slaves -> interfaces, bond options (mode, miimon, lacp_rate,
  xmit_hash_policy, up/down delay, arp_interval, primary) -> parameters.
  Handled inline in the interface YAML (netplan has no separate modprobe
  file), so get_bond is intentionally not defined and the state's bond
  path is skipped.
- vlan: id + link from vlan_id/parent settings, or parsed from a dotted
  iface name (eth0.100 -> id 100, link eth0).
- bridge: ports -> interfaces, bridge options (stp, forward-delay,
  ageing-time, max-age, hello-time, priority) -> parameters.

Adds unit tests for each type. Refs saltstack#62219
netplan rejects a config that references an interface it can't resolve
("Error in network definition: bond0: interface 'eth3' is not defined"),
so a bond/bridge/vlan file that named its slaves/ports/parent but never
declared them failed 'netplan generate' and the interface never came up.

Emit each member (bond slaves, bridge ports, vlan parent) as a bare
ethernets entry in the same document; setdefault leaves any
separately-managed definition of the same NIC intact on merge.

Caught by behavioral testing on a real Ubuntu VM (unit tests checked the
dict shape but not netplan's acceptance); re-validated end-to-end --
vlan/bond/bridge now generate, apply, come up, and survive reboot.

Refs saltstack#62219
@ggiesen ggiesen requested a review from a team as a code owner July 1, 2026 00:33
The placeholder '3006.x' failed the salt-rewrite (fix_docstrings)
pre-commit hook, which rewrites it to a concrete version. Set it to
3006.27 (the next 3006 release, which also ships Python 3.11 per saltstack#69526)
rather than salt-rewrite's default '3006.0', which would wrongly imply
the provider has existed since the first 3006 release.
@dwoz dwoz added the test:full Run the full test suite label Jul 1, 2026
ggiesen and others added 6 commits July 2, 2026 02:20
3006.27 was released 2026-07-01 without this provider, so the next
available 3006.x release is 3006.28.
…tion

The direct test calls netplan_ip.build_interface at the exact call shape
network.managed uses (name, iface_type, enabled, **kwargs) with the test
flag the state always injects from __opts__, asserting a test=True dry run
returns the rendered lines without writing under /etc/netplan while
test=False writes the file. The inverse test guards against overcorrecting
the debian_ip gate: a netplan binary alone (netplan.io installed as a
dependency, no /etc/netplan) must not stop debian_ip from claiming the ip
provider on ifupdown systems.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

test:full Run the full test suite

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants