Ubuntu systemd-networkd: VLAN ifname heuristics

Add a regular expression match to verify that VLAN
interfaces are named like VLAN interfaces, enabling
non-VLAN interfaces to exist on a network where the
VLAN ID is defined.

Co-Authored-By: Mark Goddard <mark@stackhpc.com>
Story: 2009270
Task: 43511
Change-Id: If09c4840bbe51168a8b94be0db85e6610c21aa82
This commit is contained in:
Stig Telfer 2021-10-03 21:58:52 +01:00 committed by Mark Goddard
parent b8abf706d4
commit 1b647acaf6
4 changed files with 71 additions and 3 deletions

View File

@ -488,8 +488,9 @@ def _add_to_result(result, prefix, device, config):
# This should not happen.
if key in result:
raise errors.AnsibleFilterError(
"Programming error: duplicate interface configuration for %s"
% device)
"Programming error: duplicate interface configuration for %s: "
"have %s"
% (device, result))
result[key] = config
@ -511,7 +512,8 @@ def networkd_netdevs(context, names, inventory_hostname=None):
result = {}
# VLANs.
for name in networks.net_select_vlans(context, names, inventory_hostname):
for name in networks.net_select_vlan_interfaces(context, names,
inventory_hostname):
device = networks.get_and_validate_interface(context, name,
inventory_hostname)
netdev = _vlan_netdev(context, name, inventory_hostname)

View File

@ -535,6 +535,14 @@ def net_is_vlan(context, name, inventory_hostname=None):
return net_vlan(context, name) is not None
@jinja2.contextfilter
def net_is_vlan_interface(context, name, inventory_hostname=None):
device = get_and_validate_interface(context, name, inventory_hostname)
# Use a heuristic to match conventional VLAN names, ending with a
# period and a numerical extension to an interface name
return re.match(r"^[a-zA-Z0-9_\-]+\.[1-9][\d]{0,3}$", device)
@jinja2.contextfilter
def net_select_ethers(context, names, inventory_hostname=None):
return [name for name in names
@ -559,6 +567,12 @@ def net_select_vlans(context, names, inventory_hostname=None):
if net_is_vlan(context, name, inventory_hostname)]
@jinja2.contextfilter
def net_select_vlan_interfaces(context, names, inventory_hostname=None):
return [name for name in names
if net_is_vlan_interface(context, name, inventory_hostname)]
@jinja2.contextfilter
def net_reject_vlans(context, names, inventory_hostname=None):
return [name for name in names

View File

@ -74,6 +74,19 @@ class TestNetworkdNetDevs(BaseNetworkdTest):
devs = networkd.networkd_netdevs(self.context, [])
self.assertEqual({}, devs)
def test_eth(self):
devs = networkd.networkd_netdevs(self.context, ["net1"])
expected = {}
self.assertEqual(expected, devs)
def test_eth_untagged_vlan(self):
# An untagged interface on a network with a VLAN defined should not
# create a VLAN subinterface.
self._update_context({"net1_vlan": 42})
devs = networkd.networkd_netdevs(self.context, ["net1"])
expected = {}
self.assertEqual(expected, devs)
def test_vlan(self):
devs = networkd.networkd_netdevs(self.context, ["net2"])
expected = {
@ -154,6 +167,23 @@ class TestNetworkdNetDevs(BaseNetworkdTest):
self.assertRaises(errors.AnsibleFilterError,
networkd.networkd_netdevs, self.context, ["net3"])
def test_bridge_untagged_vlan(self):
# A bridge on a network with a VLAN defined should not create a VLAN
# subinterface.
self._update_context({"net3_vlan": 42})
devs = networkd.networkd_netdevs(self.context, ["net3"])
expected = {
"50-kayobe-br0": [
{
"NetDev": [
{"Name": "br0"},
{"Kind": "bridge"},
]
},
]
}
self.assertEqual(expected, devs)
def test_bond(self):
devs = networkd.networkd_netdevs(self.context, ["net4"])
expected = {
@ -207,6 +237,23 @@ class TestNetworkdNetDevs(BaseNetworkdTest):
self.assertRaises(errors.AnsibleFilterError,
networkd.networkd_netdevs, self.context, ["net4"])
def test_bond_untagged_vlan(self):
# A bridge on a network with a VLAN defined should not create a VLAN
# subinterface.
self._update_context({"net4_vlan": 42})
devs = networkd.networkd_netdevs(self.context, ["net4"])
expected = {
"50-kayobe-bond0": [
{
"NetDev": [
{"Name": "bond0"},
{"Kind": "bond"},
]
},
]
}
self.assertEqual(expected, devs)
def test_veth(self):
self._update_context({"external_net_names": ["net3"]})
devs = networkd.networkd_netdevs(self.context, ["net3"])

View File

@ -0,0 +1,5 @@
---
fixes:
- |
Fixes an issue with ``systemd-networkd`` configuration for VLAN interfaces
when the interface is untagged.