Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Synchronise xena with upstream #121

Merged
merged 4 commits into from
Apr 17, 2023
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
2 changes: 1 addition & 1 deletion ansible/roles/kolla-openstack/templates/ironic.conf.j2
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ provisioning_network = {{ kolla_ironic_provisioning_network }}

[pxe]
{% if kolla_ironic_pxe_append_params %}
pxe_append_params = {{ kolla_ironic_pxe_append_params | join(' ') }}
kernel_append_params = {{ kolla_ironic_pxe_append_params | join(' ') }}
{% endif %}

{% raw %}
Expand Down
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 @@ -473,8 +476,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 @@ -491,6 +495,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 @@ -612,7 +612,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.pass_context
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.pass_context
def net_parent(context, name, inventory_hostname=None):
return net_attr(context, name, 'parent', inventory_hostname)


@jinja2.pass_context
def net_prefix(context, name, inventory_hostname=None):
cidr = net_cidr(context, name, inventory_hostname)
Expand Down Expand Up @@ -545,10 +558,15 @@ def net_is_vlan(context, name, inventory_hostname=None):

@jinja2.pass_context
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.pass_context
Expand Down Expand Up @@ -600,7 +618,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 @@ -678,6 +699,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.pass_context
def _net_parent(context, name):
return context.get(name + '_parent')


@jinja2.pass_context
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 @@ -475,7 +530,8 @@ def test_vlan_multiple(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 @@ -487,6 +543,8 @@ def test_vlan_with_parent(self):
"Network": [
{"Address": "1.2.3.4/24"},
{"VLAN": "eth0.2"},
{"VLAN": "vlan.5"},
{"VLAN": "vlan6"},
]
},
],
Expand All @@ -496,6 +554,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 @@ -984,11 +1056,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 @@ -79,6 +82,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 @@ -101,6 +101,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.