bridge/bond: User permanent MAC address for Interface.COPY_MAC_FROM

Use permanent MAC address when possible for `Interface.COPY_MAC_FROM`.

Integration test case added by require real NIC to test using. Example:

    sudo env TEST_REAL_NIC="enp7s0" pytest -k \
        test_create_linux_bridge_with_copy_mac_from_permanent_mac

Tested on e100e in RHEL 8.4 VM.

Signed-off-by: Gris Ge <fge@redhat.com>
This commit is contained in:
Gris Ge 2021-06-07 12:32:36 +08:00 committed by Fernando Fernández Mancera
parent 1ea97bfddc
commit e87349169d
7 changed files with 120 additions and 4 deletions

View File

@ -130,6 +130,7 @@ class BaseIface:
ROUTE_RULES_METADATA = "_route_rules"
RULE_CHANGED_METADATA = "_changed"
ROUTE_CHANGED_METADATA = "_changed"
PERMANENT_MAC_ADDRESS_METADATA = "_permanent_mac_address"
def __init__(self, info, save_to_disk=True):
self._origin_info = deepcopy(info)
@ -215,6 +216,10 @@ class BaseIface:
def original_dict(self):
return self._origin_info
@property
def permanent_mac_address(self):
return self._info.get(BaseIface.PERMANENT_MAC_ADDRESS_METADATA)
def ip_state(self, family):
return IPState(family, self._info.get(family, {}))

View File

@ -194,8 +194,10 @@ class Ifaces:
f"{Interface.COPY_MAC_FROM} property "
f"as the port {iface.copy_mac_from} does not exists yet"
)
iface.apply_copy_mac_from(port_iface.mac)
if port_iface.permanent_mac_address:
iface.apply_copy_mac_from(port_iface.permanent_mac_address)
else:
iface.apply_copy_mac_from(port_iface.mac)
def _create_virtual_port(self):
"""

View File

@ -20,6 +20,7 @@
import logging
from operator import attrgetter
from libnmstate.ifaces import BaseIface
from libnmstate.schema import Interface
from libnmstate.schema import InterfaceIP
from libnmstate.schema import InterfaceIPv6
@ -91,6 +92,10 @@ class NisporPluginBaseIface:
Interface.MAC: self.mac,
Interface.ACCEPT_ALL_MAC_ADDRESSES: self.accept_all_mac_addresses,
}
if self._np_iface.permanent_mac_address:
iface_info[
BaseIface.PERMANENT_MAC_ADDRESS_METADATA
] = self._np_iface.permanent_mac_address
if self.mtu:
iface_info[Interface.MTU] = self.mtu
ip_info = self._ip_info(config_only)

View File

@ -103,7 +103,7 @@ def _logging_setup():
def _ethx_init():
""" Remove any existing definitions on the ethX interfaces. """
"""Remove any existing definitions on the ethX interfaces."""
ifacelib.ifaces_init("eth1", "eth2")

View File

@ -18,10 +18,12 @@
#
from contextlib import contextmanager
from copy import deepcopy
import os
import time
import pytest
import yaml
import json
import libnmstate
from libnmstate.error import NmstateKernelIntegerRoundedError
@ -56,6 +58,8 @@ from .testlib.vlan import vlan_interface
TEST_BRIDGE0 = "linux-br0"
TEST_TAP0 = "tap0"
ETH1 = "eth1"
# RFC 7042 reserved EUI-48 MAC range for document
TEST_MAC_ADDRESS = "00:00:5E:00:53:01"
BRIDGE_OPTIONS_YAML = """
options:
@ -813,6 +817,60 @@ def test_create_linux_bridge_with_copy_mac_from(eth1_up, eth2_up):
assert_mac_address(current_state, eth2_mac)
def _get_permanent_mac(iface_name):
np_iface = json.loads(exec_cmd(f"npc {iface_name} --json".split())[1])
return np_iface[0].get("permanent_mac_address")
@pytest.fixture
def real_nic_with_mac_differ_from_permanent():
test_nic = os.environ.get("TEST_REAL_NIC")
permanent_mac = _get_permanent_mac(test_nic)
assert permanent_mac
libnmstate.apply(
{
Interface.KEY: [
{
Interface.NAME: test_nic,
Interface.MAC: TEST_MAC_ADDRESS,
}
]
}
)
yield permanent_mac
libnmstate.apply(
{
Interface.KEY: [
{
Interface.NAME: test_nic,
Interface.STATE: InterfaceState.ABSENT,
}
]
}
)
@pytest.mark.skipif(
not os.environ.get("TEST_REAL_NIC"),
reason="Need to define TEST_REAL_NIC for SR-IOV test",
)
def test_create_linux_bridge_with_copy_mac_from_permanent_mac(
real_nic_with_mac_differ_from_permanent,
):
permanent_mac = real_nic_with_mac_differ_from_permanent
test_nic = os.environ.get("TEST_REAL_NIC")
# Create bridge use this nic
bridge_state = _create_bridge_subtree_config([test_nic])
with linux_bridge(
TEST_BRIDGE0,
bridge_state,
extra_iface_state={Interface.COPY_MAC_FROM: test_nic},
):
current_state = show_only((TEST_BRIDGE0,))
assert_mac_address(current_state, permanent_mac)
@pytest.mark.tier1
@pytest.mark.skipif(
nm_major_minor_version() < 1.31,

View File

@ -31,6 +31,7 @@ from libnmstate.schema import InterfaceState
from libnmstate.schema import InterfaceType
from libnmstate.ifaces.ifaces import Ifaces
from libnmstate.ifaces.ifaces import BaseIface
from libnmstate.ifaces.ifaces import OvsBridgeIface
from libnmstate.state import state_match
@ -39,6 +40,7 @@ from .testlib.bridgelib import PORT1_IFACE_NAME
from .testlib.bridgelib import PORT2_IFACE_NAME
from .testlib.bridgelib import gen_bridge_iface_info
from .testlib.constants import MAC_ADDRESS1
from .testlib.constants import MAC_ADDRESS2
from .testlib.ifacelib import gen_foo_iface_info_static_ip
from .testlib.ifacelib import gen_foo_iface_info
from .testlib.ovslib import OVS_BRIDGE_IFACE_NAME
@ -454,6 +456,49 @@ class TestIfaces:
ifaces = Ifaces(des_iface_infos, cur_iface_infos)
ifaces.verify(cur_iface_infos)
def test_copy_mac_from_permanent_mac_address(self):
cur_iface_infos = [gen_foo_iface_info(), gen_foo_iface_info()]
cur_iface_infos[0][Interface.NAME] = PORT1_IFACE_NAME
cur_iface_infos[1][Interface.NAME] = PORT2_IFACE_NAME
cur_iface_infos[1][Interface.MAC] = MAC_ADDRESS1
cur_iface_infos[1][
BaseIface.PERMANENT_MAC_ADDRESS_METADATA
] = MAC_ADDRESS2
cur_iface_infos.append(gen_bridge_iface_info())
des_iface_infos = [gen_foo_iface_info(), gen_foo_iface_info()]
des_iface_infos[0][Interface.NAME] = PORT1_IFACE_NAME
des_iface_infos[1][Interface.NAME] = PORT2_IFACE_NAME
des_bridge_info = gen_bridge_iface_info()
des_bridge_info[Interface.NAME] = LINUX_BRIDGE_IFACE_NAME
des_bridge_info[Interface.COPY_MAC_FROM] = PORT2_IFACE_NAME
des_iface_infos.append(des_bridge_info)
ifaces = Ifaces(des_iface_infos, cur_iface_infos)
assert (
ifaces.all_kernel_ifaces[LINUX_BRIDGE_IFACE_NAME].mac
== MAC_ADDRESS2
)
def test_copy_mac_from(self):
cur_iface_infos = [gen_foo_iface_info(), gen_foo_iface_info()]
cur_iface_infos[0][Interface.NAME] = PORT1_IFACE_NAME
cur_iface_infos[1][Interface.NAME] = PORT2_IFACE_NAME
cur_iface_infos[1][Interface.MAC] = MAC_ADDRESS1
cur_iface_infos.append(gen_bridge_iface_info())
des_iface_infos = [gen_foo_iface_info(), gen_foo_iface_info()]
des_iface_infos[0][Interface.NAME] = PORT1_IFACE_NAME
des_iface_infos[1][Interface.NAME] = PORT2_IFACE_NAME
des_bridge_info = gen_bridge_iface_info()
des_bridge_info[Interface.NAME] = LINUX_BRIDGE_IFACE_NAME
des_bridge_info[Interface.COPY_MAC_FROM] = PORT2_IFACE_NAME
des_iface_infos.append(des_bridge_info)
ifaces = Ifaces(des_iface_infos, cur_iface_infos)
assert (
ifaces.all_kernel_ifaces[LINUX_BRIDGE_IFACE_NAME].mac
== MAC_ADDRESS1
)
class TestIfacesSriov:
def test_vf_been_auto_include_as_desired(self):

View File

@ -21,6 +21,7 @@ from libnmstate.schema import InterfaceIP
FOO_IFACE_NAME = "foo"
MAC_ADDRESS1 = "01:23:45:67:89:AB"
MAC_ADDRESS2 = "01:23:45:67:89:AC"
IPV6_ADDRESS1 = "2001:db8:1::1"
IPV6_ADDRESS1_FULL = "2001:db8:1:0:0:0:0:1"