Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions changelog/46618.fixed.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Allow the Debian ip module to accept rh_ip-style ipv6addr/ipv6addrs (and bare addr/addrs) as aliases for the address/addresses interface settings.
10 changes: 10 additions & 0 deletions salt/modules/debian_ip.py
Original file line number Diff line number Diff line change
Expand Up @@ -397,13 +397,19 @@ def __space_delimited_list(value):
"hwaddr": "hwaddress", # TODO: this limits bootp functionality
"ipaddr": "address",
"ipaddrs": "addresses",
# Aliases so rh_ip-style names resolve to the Debian attributes. This
# lets ``ipv6addr``/``ipv6addrs`` (stripped to ``addr``/``addrs``) and the
# bare ``addr``/``addrs`` map to the same address stanzas as ``ipaddr``.
"addr": "address",
"addrs": "addresses",
}


DEBIAN_ATTR_TO_SALT_ATTR_MAP = {v: k for (k, v) in SALT_ATTR_TO_DEBIAN_ATTR_MAP.items()}

# TODO
DEBIAN_ATTR_TO_SALT_ATTR_MAP["address"] = "address"
DEBIAN_ATTR_TO_SALT_ATTR_MAP["addresses"] = "addresses"
DEBIAN_ATTR_TO_SALT_ATTR_MAP["hwaddress"] = "hwaddress"

IPV4_VALID_PROTO = ["bootp", "dhcp", "static", "manual", "loopback", "ppp"]
Expand Down Expand Up @@ -1654,6 +1660,10 @@ def build_interface(iface, iface_type, enabled, **settings):
"""
Build an interface script for a network interface.

The IPv6 address may be supplied either as ``ipv6ipaddr``/``ipv6ipaddrs``
or, for consistency with the Red Hat module, as ``ipv6addr``/``ipv6addrs``.
Both spellings map to the same Debian ``address``/``addresses`` stanzas.

CLI Example:

.. code-block:: bash
Expand Down
113 changes: 113 additions & 0 deletions tests/pytests/unit/modules/test_debian_ip.py
Original file line number Diff line number Diff line change
Expand Up @@ -1123,6 +1123,119 @@ def test_build_interface(test_interfaces):
)


def test_build_interface_ipv6addr_alias():
"""
The rh_ip-style ``ipv6addr``/``ipv6addrs`` names should resolve to the
same Debian ``inet6`` address stanzas as ``ipv6ipaddr``/``ipv6ipaddrs``.

See https://github.com/saltstack/salt/issues/46618
"""
common = {
"ipv6proto": "static",
"enable_ipv6": True,
"noifupdown": True,
}
with tempfile.NamedTemporaryFile(mode="r", delete=True) as tfile:
with patch("salt.modules.debian_ip._DEB_NETWORK_FILE", str(tfile.name)):
canonical = debian_ip.build_interface(
iface="eth0",
iface_type="eth",
enabled=True,
interface_file=tfile.name,
ipv6ipaddr="2001:db8:dead:beef::5/64",
ipv6ipaddrs=["2001:db8:dead:beef::7/64"],
**common,
)
aliased = debian_ip.build_interface(
iface="eth0",
iface_type="eth",
enabled=True,
interface_file=tfile.name,
ipv6addr="2001:db8:dead:beef::5/64",
ipv6addrs=["2001:db8:dead:beef::7/64"],
**common,
)

assert " address 2001:db8:dead:beef::5/64\n" in aliased
assert " address 2001:db8:dead:beef::7/64\n" in aliased
assert aliased == canonical


def test_build_interface_ipv6addr_alias_overcorrection_46618():
"""
Guard against overcorrection in the issue #46618 fix, which aliased the
rh_ip-style ``addr``/``addrs`` settings names onto the Debian
``address``/``addresses`` stanzas.

Two things must NOT start happening because of the alias:

* on a dual-family interface the aliased ``ipv6addr`` must be confined
to the ``inet6`` stanza; the ``inet`` (IPv4) stanza must render
byte-identical to the same interface built without any IPv6 address
* a MAC-valued bare ``addr`` (the legacy shape that ``network.managed``
remaps to ``hwaddr`` before calling ``ip.build_interface``) must
still be ignored when passed straight to the module, not rendered as
a bogus ``address`` stanza

Both assertions hold with and without the source fix applied.
"""

def inet_stanza(lines):
# Collect only the "iface <name> inet ..." (IPv4) stanza lines.
block = []
capture = False
for line in lines:
if line.startswith("iface "):
capture = " inet " in line
if capture:
block.append(line)
return block

common = {
"proto": "static",
"ipaddr": "192.168.4.9",
"netmask": "255.255.255.0",
"ipv6proto": "static",
"enable_ipv6": True,
"noifupdown": True,
}
with tempfile.NamedTemporaryFile(mode="r", delete=True) as tfile:
with patch("salt.modules.debian_ip._DEB_NETWORK_FILE", str(tfile.name)):
baseline = debian_ip.build_interface(
iface="eth9",
iface_type="eth",
enabled=True,
interface_file=tfile.name,
**common,
)
aliased = debian_ip.build_interface(
iface="eth9",
iface_type="eth",
enabled=True,
interface_file=tfile.name,
ipv6addr="2001:db8:dead:beef::5/64",
**common,
)
mac_as_addr = debian_ip.build_interface(
iface="eth9",
iface_type="eth",
enabled=True,
interface_file=tfile.name,
proto="manual",
addr="00:11:22:33:44:55",
noifupdown=True,
)

# The IPv4 stanza must be untouched by the aliased IPv6 address.
assert inet_stanza(aliased) == inet_stanza(baseline)
assert not any("2001:db8:dead:beef::5/64" in line for line in inet_stanza(aliased))

# A MAC in bare ``addr`` fails address validation for both families and
# must be dropped entirely, exactly as before the fix.
assert not any(line.strip().startswith("address ") for line in mac_as_addr)
assert not any("00:11:22:33:44:55" in line for line in mac_as_addr)


# 'up' function tests: 1


Expand Down
Loading