Skip to content

Commit

Permalink
Merge pull request #125 from stackhpc/upstream/wallaby-2023-04-19
Browse files Browse the repository at this point in the history
Synchronise wallaby with upstream
  • Loading branch information
markgoddard authored Apr 20, 2023
2 parents 718b391 + cad9830 commit dad23f4
Show file tree
Hide file tree
Showing 9 changed files with 165 additions and 17 deletions.
18 changes: 16 additions & 2 deletions doc/source/configuration/reference/network.rst
Original file line number Diff line number Diff line change
Expand Up @@ -343,6 +343,9 @@ The following attributes are supported:

``interface``
The name of the network interface attached to the network.
``parent``
The name of the parent interface, when configuring a VLAN interface using
``systemd-networkd`` syntax.
``bootproto``
Boot protocol for the interface. Valid values are ``static`` and ``dhcp``.
The default is ``static``. When set to ``dhcp``, an external DHCP server
Expand Down Expand Up @@ -469,8 +472,9 @@ Configuring VLAN Interfaces
---------------------------

A VLAN interface may be configured by setting the ``interface`` attribute of a
network to the name of the VLAN interface. The interface name must be of the
form ``<parent interface>.<VLAN ID>``.
network to the name of the VLAN interface. The interface name must normally be
of the form ``<parent interface>.<VLAN ID>`` to ensure compatibility with all
supported host operating systems.

To configure a network called ``example`` with a VLAN interface with a parent
interface of ``eth2`` for VLAN ``123``:
Expand All @@ -487,6 +491,16 @@ To keep the configuration DRY, reference the network's ``vlan`` attribute:
example_interface: "eth2.{{ example_vlan }}"
Alternatively, when using Ubuntu as a host operating system, VLAN interfaces
can be named arbitrarily using syntax supported by ``systemd-networkd``. In
this case, a ``parent`` attribute must specify the underlying interface:

.. code-block:: yaml
:caption: ``inventory/group_vars/<group>/network-interfaces``
example_interface: "myvlan{{ example_vlan }}"
example_parent: "eth2"
Ethernet interfaces, bridges, and bond master interfaces may all be parents to
a VLAN interface.

Expand Down
6 changes: 5 additions & 1 deletion kayobe/plugins/action/kolla_ansible_host_vars.py
Original file line number Diff line number Diff line change
Expand Up @@ -123,7 +123,11 @@ def _get_external_interface(self, net_name, required):
# tagged interface may be shared between these networks.
vlan = self._templar.template("{{ '%s' | net_vlan }}" %
net_name)
if vlan and iface.endswith(".%s" % vlan):
parent = self._templar.template("{{ '%s' | net_parent }}" %
net_name)
if vlan and parent:
iface = parent
elif vlan and iface.endswith(".%s" % vlan):
iface = iface.replace(".%s" % vlan, "")
return iface
elif required:
Expand Down
3 changes: 2 additions & 1 deletion kayobe/plugins/filter/networkd.py
Original file line number Diff line number Diff line change
Expand Up @@ -610,7 +610,8 @@ def networkd_networks(context, names, inventory_hostname=None):
inventory_hostname)
vlan = networks.net_vlan(context, name, inventory_hostname)
mtu = networks.net_mtu(context, name, inventory_hostname)
parent = networks.get_vlan_parent(device, vlan)
parent = networks.get_vlan_parent(
context, name, device, vlan, inventory_hostname)
vlan_interfaces = interface_to_vlans.setdefault(parent, [])
vlan_interfaces.append({"device": device, "mtu": mtu})

Expand Down
38 changes: 30 additions & 8 deletions kayobe/plugins/filter/networks.py
Original file line number Diff line number Diff line change
Expand Up @@ -106,7 +106,8 @@ def get_ovs_veths(context, names, inventory_hostname):
# tagged interface may be shared between these networks.
vlan = net_vlan(context, name, inventory_hostname)
if vlan:
parent_or_device = get_vlan_parent(device, vlan)
parent_or_device = get_vlan_parent(
context, name, device, vlan, inventory_hostname)
else:
parent_or_device = device
if parent_or_device in bridge_interfaces:
Expand All @@ -131,14 +132,21 @@ def get_ovs_veths(context, names, inventory_hostname):
]


def get_vlan_parent(device, vlan):
def get_vlan_parent(context, name, device, vlan, inventory_hostname):
"""Return the parent interface of a VLAN subinterface.
:param context: a Jinja2 Context object.
:param name: name of the network.
:param device: VLAN interface name.
:param vlan: VLAN ID.
:param inventory_hostname: Ansible inventory hostname.
:returns: parent interface name.
:raises: ansible.errors.AnsibleFilterError
"""
return re.sub(r'\.{}$'.format(vlan), '', device)
parent = net_parent(context, name, inventory_hostname)
if not parent:
parent = re.sub(r'\.{}$'.format(vlan), '', device)
return parent


@jinja2.contextfilter
Expand Down Expand Up @@ -190,6 +198,11 @@ def net_mask(context, name, inventory_hostname=None):
return str(netaddr.IPNetwork(cidr).netmask) if cidr is not None else None


@jinja2.contextfilter
def net_parent(context, name, inventory_hostname=None):
return net_attr(context, name, 'parent', inventory_hostname)


@jinja2.contextfilter
def net_prefix(context, name, inventory_hostname=None):
cidr = net_cidr(context, name, inventory_hostname)
Expand Down Expand Up @@ -542,10 +555,15 @@ def net_is_vlan(context, name, inventory_hostname=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)
parent = net_parent(context, name, inventory_hostname)
vlan = net_vlan(context, name, inventory_hostname)
if parent and vlan:
return True
else:
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
Expand Down Expand Up @@ -597,7 +615,10 @@ def net_configdrive_network_device(context, name, inventory_hostname=None):
bootproto = net_bootproto(context, name, inventory_hostname)
mtu = net_mtu(context, name, inventory_hostname)
vlan = net_vlan(context, name, inventory_hostname)
if vlan and '.' in device:
parent = net_parent(context, name, inventory_hostname)
if vlan and parent:
backend = parent
elif vlan and '.' in device:
backend = [device.split('.')[0]]
else:
backend = None
Expand Down Expand Up @@ -675,6 +696,7 @@ def get_filters():
'net_fqdn': _make_attr_filter('fqdn'),
'net_ip': net_ip,
'net_interface': net_interface,
'net_parent': net_parent,
'net_no_ip': net_no_ip,
'net_cidr': net_cidr,
'net_mask': net_mask,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,11 @@ def _net_interface(context, name):
return context.get(name + '_interface')


@jinja2.contextfilter
def _net_parent(context, name):
return context.get(name + '_parent')


@jinja2.contextfilter
def _net_vlan(context, name):
return context.get(name + '_vlan')
Expand All @@ -42,6 +47,7 @@ def __init__(self, variables):
self.variables = variables
self.env = jinja2.Environment()
self.env.filters['net_interface'] = _net_interface
self.env.filters['net_parent'] = _net_parent
self.env.filters['net_vlan'] = _net_vlan
self.env.filters['net_select_bridges'] = _net_select_bridges

Expand Down
82 changes: 77 additions & 5 deletions kayobe/tests/unit/plugins/filter/test_networkd.py
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,15 @@ class BaseNetworkdTest(unittest.TestCase):
# net4: bond on bond0 with members eth0 and eth1.
"net4_interface": "bond0",
"net4_bond_slaves": ["eth0", "eth1"],
# net5: VLAN on vlan.5 with VLAN 5 on interface eth0.
"net5_interface": "vlan.5",
"net5_parent": "eth0",
"net5_vlan": 5,
# net6: VLAN on vlan6 with VLAN 6 on interface eth0.
"net6_interface": "vlan6",
"net6_parent": "eth0",
"net6_vlan": 6,
# NOTE(priteau): net7 is used in test_veth_on_vlan
# Prefix for networkd config file names.
"networkd_prefix": "50-kayobe-",
# Veth pair patch link prefix and suffix.
Expand Down Expand Up @@ -132,6 +141,52 @@ def test_vlan_no_interface(self):
self.assertRaises(errors.AnsibleFilterError,
networkd.networkd_netdevs, self.context, ["net2"])

def test_vlan_with_parent(self):
devs = networkd.networkd_netdevs(self.context,
["net1", "net2", "net5", "net6"])
expected = {
"50-kayobe-eth0.2": [
{
"NetDev": [
{"Name": "eth0.2"},
{"Kind": "vlan"},
]
},
{
"VLAN": [
{"Id": 2},
]
},
],
"50-kayobe-vlan.5": [
{
"NetDev": [
{"Name": "vlan.5"},
{"Kind": "vlan"},
]
},
{
"VLAN": [
{"Id": 5},
]
},
],
"50-kayobe-vlan6": [
{
"NetDev": [
{"Name": "vlan6"},
{"Kind": "vlan"},
]
},
{
"VLAN": [
{"Id": 6},
]
},
]
}
self.assertEqual(expected, devs)

def test_bridge(self):
devs = networkd.networkd_netdevs(self.context, ["net3"])
expected = {
Expand Down Expand Up @@ -437,7 +492,8 @@ def test_vlan(self):
self.assertEqual(expected, nets)

def test_vlan_with_parent(self):
nets = networkd.networkd_networks(self.context, ["net1", "net2"])
nets = networkd.networkd_networks(self.context,
["net1", "net2", "net5", "net6"])
expected = {
"50-kayobe-eth0": [
{
Expand All @@ -449,6 +505,8 @@ def test_vlan_with_parent(self):
"Network": [
{"Address": "1.2.3.4/24"},
{"VLAN": "eth0.2"},
{"VLAN": "vlan.5"},
{"VLAN": "vlan6"},
]
},
],
Expand All @@ -458,6 +516,20 @@ def test_vlan_with_parent(self):
{"Name": "eth0.2"}
]
},
],
"50-kayobe-vlan.5": [
{
"Match": [
{"Name": "vlan.5"}
]
},
],
"50-kayobe-vlan6": [
{
"Match": [
{"Name": "vlan6"}
]
},
]
}
self.assertEqual(expected, nets)
Expand Down Expand Up @@ -946,11 +1018,11 @@ def test_veth_on_vlan(self):
# needs patching to OVS. The parent interface is a bridge, and the veth
# pair should be plugged into it.
self._update_context({
"provision_wl_net_name": "net5",
"provision_wl_net_name": "net7",
"net3_bridge_ports": [],
"net5_interface": "br0.42",
"net5_vlan": 42})
nets = networkd.networkd_networks(self.context, ["net3", "net5"])
"net7_interface": "br0.42",
"net7_vlan": 42})
nets = networkd.networkd_networks(self.context, ["net3", "net7"])
expected = {
"50-kayobe-br0": [
{
Expand Down
12 changes: 12 additions & 0 deletions playbooks/kayobe-overcloud-host-configure-base/overrides.yml.j2
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,9 @@ controller_extra_network_interfaces:
- test_net_bond
- test_net_bond_vlan
- test_net_bridge_noip
{% if ansible_os_family == "Debian" %}
- test_net_systemd_vlan
{% endif %}

# Custom IP routing tables.
network_route_tables:
Expand Down Expand Up @@ -74,6 +77,15 @@ test_net_bridge_noip_interface: br1
test_net_bridge_noip_bridge_ports: [dummy7]
test_net_bridge_noip_no_ip: true

{% if ansible_os_family == "Debian" %}
# vlan45: VLAN interface of bond0 using systemd-networkd style
test_net_systemd_vlan_cidr: 192.168.41.0/24
test_net_systemd_vlan_interface: "vlan{% raw %}{{ test_net_systemd_vlan_vlan }}{% endraw %}"
test_net_systemd_vlan_parent: "{% raw %}{{ test_net_bond_interface }}{% endraw %}"
test_net_systemd_vlan_vlan: 45
test_net_systemd_vlan_zone: public
{% endif %}

# Define a software RAID device consisting of two loopback devices.
controller_mdadm_arrays:
- name: md0
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,9 @@ def _is_dnf():
info = distro.linux_distribution()
return info[0].startswith('CentOS') and info[1].startswith('8')

def _is_apt():
info = distro.linux_distribution()
return info[0].startswith('Ubuntu')

def test_network_ethernet(host):
interface = host.interface('dummy2')
Expand Down Expand Up @@ -92,6 +95,14 @@ def test_network_bridge_no_ip(host):
assert not '192.168.40.1' in interface.addresses


@pytest.mark.skipif(not _is_apt(),
reason="systemd-networkd VLANs only supported on Ubuntu")
def test_network_systemd_vlan(host):
interface = host.interface('vlan45')
assert interface.exists
assert '192.168.41.1' in interface.addresses


def test_additional_user_account(host):
user = host.user("kayobe-test-user")
assert user.name == "kayobe-test-user"
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
---
features:
- |
Adds support for configuring arbitrarily named VLAN interfaces using
``systemd-networkd``. See `story 2010266
<https://storyboard.openstack.org/#!/story/2010266>`__ for details.

0 comments on commit dad23f4

Please sign in to comment.