Add initial support for systemd-networkd link configuration
Added initial support for systemd-networkd link configuration, now you can configure and rename the name of a network interface if you know the MAC address of the interface. Also added unit tests and fixed issues in the test_overcloud_host_configure.py: * Added unit test for networkd. * Fixed pep8 issues. * Removed unused import. * Fixed 'not in' issue in assert. Change-Id: I8321183dbc747ef521aa0d2660ebeef8b0342c6a Signed-off-by: Maksim Malchuk <maksim.malchuk@gmail.com>
This commit is contained in:
parent
28d78297af
commit
2c22526f70
@ -41,3 +41,9 @@
|
||||
systemd_networkd_cleanup: true
|
||||
systemd_networkd_cleanup_patterns:
|
||||
- "{{ networkd_prefix }}*"
|
||||
|
||||
- name: Ensure udev is triggered on links changes
|
||||
become: true
|
||||
command: "udevadm trigger --verbose --subsystem-match=net --action=add"
|
||||
changed_when: false
|
||||
when: network_interfaces | networkd_links | length
|
||||
|
@ -429,6 +429,34 @@ To configure a network called ``example`` with an Ethernet interface on
|
||||
|
||||
example_interface: eth0
|
||||
|
||||
Advanced: Configuring (Renaming) Ethernet Interfaces System Name
|
||||
----------------------------------------------------------------
|
||||
|
||||
The name of the Ethernet interface may be explicitly configured by binding
|
||||
known MAC address of the specific interface to its name by setting the
|
||||
``macaddress`` attribute for a network.
|
||||
|
||||
.. warning::
|
||||
|
||||
Supported only on Ubuntu/Debian operating systems.
|
||||
|
||||
To configure a network called ``example`` with known MAC address
|
||||
``aa:bb:cc:dd:ee:ff`` and rename it from a system name (might be ``eth0``,
|
||||
``ens3``, or any other name) to the ``lan0`` (new name):
|
||||
|
||||
.. code-block:: yaml
|
||||
:caption: ``inventory/group_vars/<group>/network-interfaces``
|
||||
|
||||
example_interface: lan0
|
||||
example_macaddress: "aa:bb:cc:dd:ee:ff"
|
||||
|
||||
.. warning::
|
||||
|
||||
The network interface must be down before changing its name. See
|
||||
`issue <https://github.com/systemd/systemd/issues/26601>`__ in the systemd
|
||||
project. So the configured node reboot might be required right after the
|
||||
``seed host configure`` or ``overcloud host configure`` Kayobe commands.
|
||||
|
||||
.. _configuring-bridge-interfaces:
|
||||
|
||||
Configuring Bridge Interfaces
|
||||
|
@ -479,6 +479,35 @@ def _veth_peer_network(context, veth, inventory_hostname):
|
||||
return _filter_options(config)
|
||||
|
||||
|
||||
def _ether_link(context, name, inventory_hostname):
|
||||
"""Return a networkd link configuration for a ether.
|
||||
|
||||
:param context: a Jinja2 Context object.
|
||||
:param name: name of the network.
|
||||
:param inventory_hostname: Ansible inventory hostname.
|
||||
"""
|
||||
config = []
|
||||
|
||||
device = networks.net_interface(context, name, inventory_hostname)
|
||||
macaddress = networks.net_macaddress(context, name, inventory_hostname)
|
||||
|
||||
if macaddress is not None:
|
||||
config = [
|
||||
{
|
||||
'Match': [
|
||||
{'PermanentMACAddress': macaddress},
|
||||
],
|
||||
},
|
||||
{
|
||||
'Link': [
|
||||
{'Name': device},
|
||||
],
|
||||
}
|
||||
]
|
||||
|
||||
return _filter_options(config)
|
||||
|
||||
|
||||
def _add_to_result(result, prefix, device, config):
|
||||
"""Add configuration for an interface to a filter result.
|
||||
|
||||
@ -561,8 +590,20 @@ def networkd_links(context, names, inventory_hostname=None):
|
||||
:param inventory_hostname: Ansible inventory hostname.
|
||||
:returns: a dict representation of networkd link configuration.
|
||||
"""
|
||||
# NOTE(mgoddard): We do not currently support link configuration.
|
||||
return {}
|
||||
# Prefix for configuration file names.
|
||||
prefix = utils.get_hostvar(context, "networkd_prefix", inventory_hostname)
|
||||
|
||||
result = {}
|
||||
|
||||
# only ethers
|
||||
for name in networks.net_select_ethers(context, names, inventory_hostname):
|
||||
device = networks.get_and_validate_interface(context, name,
|
||||
inventory_hostname)
|
||||
ether_link = _ether_link(context, name, inventory_hostname)
|
||||
if ether_link:
|
||||
_add_to_result(result, prefix, device, ether_link)
|
||||
|
||||
return result
|
||||
|
||||
|
||||
@jinja2.pass_context
|
||||
|
@ -274,6 +274,11 @@ def net_mtu(context, name, inventory_hostname=None):
|
||||
return mtu
|
||||
|
||||
|
||||
@jinja2.pass_context
|
||||
def net_macaddress(context, name, inventory_hostname=None):
|
||||
return net_attr(context, name, 'macaddress', inventory_hostname)
|
||||
|
||||
|
||||
@jinja2.pass_context
|
||||
def net_bridge_stp(context, name, inventory_hostname=None):
|
||||
"""Return the Spanning Tree Protocol (STP) state for a bridge.
|
||||
@ -394,6 +399,7 @@ def net_interface_obj(context, name, inventory_hostname=None, names=None):
|
||||
netmask = None
|
||||
vlan = net_vlan(context, name, inventory_hostname)
|
||||
mtu = net_mtu(context, name, inventory_hostname)
|
||||
macaddress = net_macaddress(context, name, inventory_hostname)
|
||||
|
||||
# NOTE(priteau): do not pass MTU for VLAN interfaces on bridges when it is
|
||||
# identical to the parent bridge, to work around a NetworkManager bug.
|
||||
@ -433,6 +439,7 @@ def net_interface_obj(context, name, inventory_hostname=None, names=None):
|
||||
'gateway': gateway,
|
||||
'vlan': vlan,
|
||||
'mtu': mtu,
|
||||
'macaddress': macaddress,
|
||||
'route': routes,
|
||||
'rules': rules,
|
||||
'bootproto': bootproto or 'static',
|
||||
@ -789,6 +796,7 @@ def get_filters():
|
||||
'net_neutron_gateway': net_neutron_gateway,
|
||||
'net_vlan': net_vlan,
|
||||
'net_mtu': net_mtu,
|
||||
'net_macaddress': net_macaddress,
|
||||
'net_routes': net_routes,
|
||||
'net_rules': net_rules,
|
||||
'net_physical_network': net_physical_network,
|
||||
|
@ -33,6 +33,7 @@ class BaseNetworkdTest(unittest.TestCase):
|
||||
"net1_interface": "eth0",
|
||||
"net1_cidr": "1.2.3.0/24",
|
||||
"net1_ips": {"test-host": "1.2.3.4"},
|
||||
"net1_macaddress": "aa:bb:cc:dd:ee:ff",
|
||||
# net2: VLAN on eth0.2 with VLAN 2 on interface eth0.
|
||||
"net2_interface": "eth0.2",
|
||||
"net2_vlan": 2,
|
||||
@ -351,9 +352,27 @@ class TestNetworkdNetDevs(BaseNetworkdTest):
|
||||
class TestNetworkdLinks(BaseNetworkdTest):
|
||||
|
||||
def test_empty(self):
|
||||
links = networkd.networkd_links(self.context, ['net1'])
|
||||
links = networkd.networkd_links(self.context, ['net2'])
|
||||
self.assertEqual({}, links)
|
||||
|
||||
def test_link_name(self):
|
||||
links = networkd.networkd_links(self.context, ['net1'])
|
||||
expected = {
|
||||
"50-kayobe-eth0": [
|
||||
{
|
||||
"Match": [
|
||||
{"PermanentMACAddress": "aa:bb:cc:dd:ee:ff"}
|
||||
]
|
||||
},
|
||||
{
|
||||
"Link": [
|
||||
{"Name": "eth0"},
|
||||
]
|
||||
},
|
||||
]
|
||||
}
|
||||
self.assertEqual(expected, links)
|
||||
|
||||
|
||||
class TestNetworkdNetworks(BaseNetworkdTest):
|
||||
|
||||
|
@ -5,7 +5,6 @@
|
||||
|
||||
import ipaddress
|
||||
import os
|
||||
import time
|
||||
|
||||
import distro
|
||||
import pytest
|
||||
@ -54,17 +53,18 @@ def test_network_bridge(host):
|
||||
interface = host.interface('br0')
|
||||
assert interface.exists
|
||||
assert '192.168.36.1' in interface.addresses
|
||||
stp_status = host.file('/sys/class/net/br0/bridge/stp_state').content_string.strip()
|
||||
state_file = "/sys/class/net/br0/bridge/stp_state"
|
||||
stp_status = host.file(state_file).content_string.strip()
|
||||
assert '0' == stp_status
|
||||
ports = ['dummy3', 'dummy4']
|
||||
sys_ports = host.check_output('ls -1 /sys/class/net/br0/brif')
|
||||
assert sys_ports == "\n".join(ports)
|
||||
for port in ports:
|
||||
interface = host.interface(port)
|
||||
assert interface.exists
|
||||
v4_addresses = [a for a in interface.addresses
|
||||
if ipaddress.ip_address(a).version == '4']
|
||||
assert not v4_addresses
|
||||
interface = host.interface(port)
|
||||
assert interface.exists
|
||||
v4_addresses = [a for a in interface.addresses
|
||||
if ipaddress.ip_address(a).version == '4']
|
||||
assert not v4_addresses
|
||||
|
||||
|
||||
def test_network_bridge_vlan(host):
|
||||
@ -84,9 +84,9 @@ def test_network_bond(host):
|
||||
slaves = set(['dummy5', 'dummy6'])
|
||||
assert sys_slaves == slaves
|
||||
for slave in slaves:
|
||||
interface = host.interface(slave)
|
||||
assert interface.exists
|
||||
assert not interface.addresses
|
||||
interface = host.interface(slave)
|
||||
assert interface.exists
|
||||
assert not interface.addresses
|
||||
|
||||
|
||||
def test_network_bond_vlan(host):
|
||||
@ -99,8 +99,9 @@ def test_network_bond_vlan(host):
|
||||
def test_network_bridge_no_ip(host):
|
||||
interface = host.interface('br1')
|
||||
assert interface.exists
|
||||
assert not '192.168.40.1' in interface.addresses
|
||||
stp_status = host.file('/sys/class/net/br1/bridge/stp_state').content_string.strip()
|
||||
assert '192.168.40.1' not in interface.addresses
|
||||
state_file = "/sys/class/net/br1/bridge/stp_state"
|
||||
stp_status = host.file(state_file).content_string.strip()
|
||||
assert '1' == stp_status
|
||||
|
||||
|
||||
@ -113,13 +114,13 @@ def test_network_systemd_vlan(host):
|
||||
|
||||
|
||||
def test_additional_user_account(host):
|
||||
user = host.user("kayobe-test-user")
|
||||
assert user.name == "kayobe-test-user"
|
||||
assert user.group == "kayobe-test-user"
|
||||
assert set(user.groups) == {"kayobe-test-user", "stack"}
|
||||
assert user.gecos == "Kayobe test user"
|
||||
with host.sudo():
|
||||
assert user.password == 'kayobe-test-user-password'
|
||||
user = host.user("kayobe-test-user")
|
||||
assert user.name == "kayobe-test-user"
|
||||
assert user.group == "kayobe-test-user"
|
||||
assert set(user.groups) == {"kayobe-test-user", "stack"}
|
||||
assert user.gecos == "Kayobe test user"
|
||||
with host.sudo():
|
||||
assert user.password == 'kayobe-test-user-password'
|
||||
|
||||
|
||||
def test_software_RAID(host):
|
||||
@ -229,7 +230,8 @@ def test_apt_auth(host):
|
||||
|
||||
|
||||
@pytest.mark.parametrize('repo', ["appstream", "baseos", "extras", "epel"])
|
||||
@pytest.mark.skipif(not _is_dnf_mirror(), reason="DNF OpenDev mirror only for CentOS 8")
|
||||
@pytest.mark.skipif(not _is_dnf_mirror(),
|
||||
reason="DNF OpenDev mirror only for CentOS 8")
|
||||
def test_dnf_local_package_mirrors(host, repo):
|
||||
# Depends on SITE_MIRROR_FQDN environment variable.
|
||||
assert os.getenv('SITE_MIRROR_FQDN')
|
||||
@ -256,7 +258,7 @@ def test_dnf_automatic(host):
|
||||
|
||||
|
||||
@pytest.mark.skipif(not _is_dnf(),
|
||||
reason="tuned profile setting only supported on CentOS/Rocky")
|
||||
reason="tuned profiles only supported on CentOS/Rocky")
|
||||
def test_tuned_profile_is_active(host):
|
||||
tuned_output = host.check_output("tuned-adm active")
|
||||
assert "throughput-performance" in tuned_output
|
||||
|
@ -0,0 +1,6 @@
|
||||
---
|
||||
features:
|
||||
- |
|
||||
Added initial support for systemd-networkd link configuration, now you can
|
||||
configure and rename the name of a network interface if you know the MAC
|
||||
address of the interface.
|
Loading…
Reference in New Issue
Block a user