test: Fix bridge MAC address issue on Fedora 31.

With systemd version 242, Fedora 31 will use persistent MAC address for
all virtual network interface (including linux bridges). Therefore the
bridge will not use the MAC address of the first added port anymore.

If you want to let kernel handle the MAC address selection system wide,
please change `/usr/lib/systemd/network/99-default.link` to:

```
 [Link]
 NamePolicy=kernel database onboard slot path
 MACAddressPolicy=none
```

This cause these test cases fail on Fedora 31:
    * test_linux_bridge_uses_the_port_mac
    * test_dhcp_on_bridge0

For `test_dhcp_on_bridge0` test, we explicitly set bridge to use the MAC
address of the dhcpcli NIC.

For `test_linux_bridge_uses_the_port_mac`, we change it into two test
cases:
    * test_linux_bridge_uses_specified_mac_address

      Explicitly set MAC address and expected to pass on all systems.

    * test_linux_bridge_uses_the_port_mac_implicitly

      Do not specify the MAC address of the bridge. Expected to pass on
      CentOS/RHEL 8 but xfail on Fedora 31+.

From nmstate point of view, explicitly setting MAC address in desired
state is the preferred way when MAC address of virtual interface matters.

Signed-off-by: Gris Ge <fge@redhat.com>
This commit is contained in:
Gris Ge 2019-11-19 15:59:32 +08:00
parent ca6d291218
commit 8cdaaa71c8
4 changed files with 77 additions and 2 deletions

View File

@ -39,6 +39,7 @@ from .testlib import cmd as libcmd
from .testlib import bondlib
from .testlib import ifacelib
from .testlib import statelib
from .testlib.ifacelib import get_mac_address
from .testlib.bridgelib import add_port_to_bridge
from .testlib.bridgelib import create_bridge_subtree_state
from .testlib.bridgelib import linux_bridge
@ -496,6 +497,7 @@ def test_dhcp_on_bridge0(dhcpcli_up_with_dynamic_ip):
Interface.IPV6: create_ipv6_state(
enabled=True, dhcp=True, autoconf=True
),
Interface.MAC: get_mac_address(DHCP_CLI_NIC),
}
bridge_name = TEST_BRIDGE_NIC
with linux_bridge(bridge_name, bridge_state, bridge_iface_state) as state:

View File

@ -16,6 +16,7 @@
# You should have received a copy of the GNU Lesser General Public License
# along with this program. If not, see <https://www.gnu.org/licenses/>.
#
from contextlib import contextmanager
from copy import deepcopy
import time
@ -34,10 +35,12 @@ from .testlib import assertlib
from .testlib.bondlib import bond_interface
from .testlib.bridgelib import add_port_to_bridge
from .testlib.bridgelib import linux_bridge
from .testlib.ifacelib import get_mac_address
from .testlib.iproutelib import ip_monitor_assert_stable_link_up
from .testlib.statelib import show_only
from .testlib.assertlib import assert_mac_address
from .testlib.vlan import vlan_interface
from .testlib.env import is_fedora
TEST_BRIDGE0 = 'linux-br0'
@ -64,6 +67,18 @@ stp-priority: 32
@pytest.fixture
def bridge0_with_port0(port0_up):
with _bridge0_with_port0(port0_up) as state:
yield state
@pytest.fixture
def bridge0_with_port0_with_explicit_port_mac(port0_up):
with _bridge0_with_port0(port0_up, use_port_mac=True) as state:
yield state
@contextmanager
def _bridge0_with_port0(port0_up, use_port_mac=False):
bridge_name = TEST_BRIDGE0
port_name = port0_up[Interface.KEY][0][Interface.NAME]
bridge_state = _create_bridge_subtree_config((port_name,))
@ -71,7 +86,14 @@ def bridge0_with_port0(port0_up):
options_subtree = bridge_state[LinuxBridge.OPTIONS_SUBTREE]
options_subtree[LinuxBridge.STP_SUBTREE][LinuxBridge.STP_ENABLED] = False
with linux_bridge(bridge_name, bridge_state) as desired_state:
extra_iface_state = None
if use_port_mac:
extra_iface_state = {Interface.MAC: get_mac_address(port_name)}
with linux_bridge(
bridge_name, bridge_state, extra_iface_state
) as desired_state:
# Need to set twice so the wired setting will be explicitly set,
# allowing reapply to succeed.
# https://bugzilla.redhat.com/1703960
@ -189,7 +211,29 @@ def test_add_port_to_existing_bridge(bridge0_with_port0, port1_up):
assertlib.assert_state(desired_state)
def test_linux_bridge_uses_the_port_mac(port0_up, bridge0_with_port0):
@pytest.mark.xfail(
is_fedora(),
reason=(
'On Fedora 31+, users need to explicitly configure the port MAC '
'due to changes to the default systemd config.'
),
raises=AssertionError,
strict=True,
)
def test_linux_bridge_uses_the_port_mac_implicitly(
port0_up, bridge0_with_port0
):
print(bridge0_with_port0)
port0_name = port0_up[Interface.KEY][0][Interface.NAME]
current_state = show_only((TEST_BRIDGE0, port0_name))
assert_mac_address(
current_state, port0_up[Interface.KEY][0][Interface.MAC]
)
def test_linux_bridge_uses_specified_mac_address(
port0_up, bridge0_with_port0_with_explicit_port_mac
):
port0_name = port0_up[Interface.KEY][0][Interface.NAME]
current_state = show_only((TEST_BRIDGE0, port0_name))
assert_mac_address(

View File

@ -0,0 +1,24 @@
#
# Copyright (c) 2019 Red Hat, Inc.
#
# This file is part of nmstate
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Lesser General Public License as published by
# the Free Software Foundation, either version 2.1 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public License
# along with this program. If not, see <https://www.gnu.org/licenses/>.
#
import os
def is_fedora():
return os.path.exists('/etc/fedora-release')

View File

@ -54,3 +54,8 @@ def _set_eth_admin_state(ifname, state):
]
}
libnmstate.apply(desired_state)
def get_mac_address(ifname):
state = statelib.show_only((ifname,))
return state[schema.Interface.KEY][0].get(schema.Interface.MAC)