vlan: add support to modify VLAN ID

NetworkManager does not allow to modify the vlan id of an existing VLAN.
In order to support it, Nmstate is removing the interface and creating
it again.

Ref: https://bugzilla.redhat.com/1722352

Signed-off-by: Fernando Fernandez Mancera <ffmancera@riseup.net>
This commit is contained in:
Fernando Fernandez Mancera 2021-05-07 11:29:33 +02:00 committed by Fernando Fernández Mancera
parent 75ecf57ac3
commit a6734598ae
4 changed files with 68 additions and 4 deletions

View File

@ -1,5 +1,5 @@
#
# Copyright (c) 2020 Red Hat, Inc.
# Copyright (c) 2020-2021 Red Hat, Inc.
#
# This file is part of nmstate
#
@ -24,6 +24,10 @@ from .base_iface import BaseIface
class VlanIface(BaseIface):
def __init__(self, info, save_to_disk=True):
super().__init__(info, save_to_disk)
self._vlan_id_changed = False
@property
def parent(self):
return self._vlan_config.get(VLAN.BASE_IFACE)
@ -36,6 +40,14 @@ class VlanIface(BaseIface):
def _vlan_config(self):
return self.raw.get(VLAN.CONFIG_SUBTREE, {})
@property
def vlan_id(self):
return self._vlan_config.get(VLAN.ID)
@property
def is_vlan_id_changed(self):
return self._vlan_id_changed
@property
def is_virtual(self):
return True
@ -56,3 +68,13 @@ class VlanIface(BaseIface):
f"VLAN tunnel {self.name} has missing mandatory "
f"property: {prop}"
)
def _mark_vlan_id_changed(self, ifaces):
if self.is_up:
cur_iface = ifaces.get_cur_iface(self.name, self.type)
if cur_iface and self.vlan_id != cur_iface.vlan_id:
self._vlan_id_changed = True
def gen_metadata(self, ifaces):
self._mark_vlan_id_changed(ifaces)
super().gen_metadata(ifaces)

View File

@ -243,6 +243,16 @@ class NmProfile:
# it again.
self._add_action(NmProfile.ACTION_DEACTIVATE_FIRST)
if (
self._iface.is_up
and self._iface.type == InterfaceType.VLAN
and self._iface.is_vlan_id_changed
):
# NetworkManager does not allow to modify the vlan id of an
# existing VLAN. In order to support it, Nmstate is removing the
# interface and creating it again.
self._add_action(NmProfile.ACTION_DEACTIVATE_FIRST)
if (
self._iface.is_down
and self._nm_dev

View File

@ -1,5 +1,5 @@
#
# Copyright (c) 2018-2019 Red Hat, Inc.
# Copyright (c) 2018-2021 Red Hat, Inc.
#
# This file is part of nmstate
#
@ -206,6 +206,25 @@ def test_add_vlan_with_mismatching_name_and_id(eth1_up):
assertlib.assert_state(desired_state)
@pytest.mark.tier1
@pytest.mark.xfail(
nm_major_minor_version() < 1.31,
reason="Ref: https://bugzilla.redhat.com/1907960",
raises=NmstateVerificationError,
strict=True,
)
def test_add_vlan_and_modify_vlan_id(eth1_up):
with vlan_interface(
VLAN_IFNAME, 101, eth1_up[Interface.KEY][0][Interface.NAME]
) as desired_state:
assertlib.assert_state(desired_state)
desired_state[Interface.KEY][0][VLAN.CONFIG_SUBTREE][VLAN.ID] = 200
libnmstate.apply(desired_state)
assertlib.assert_state(desired_state)
assertlib.assert_absent(VLAN_IFNAME)
@contextmanager
def two_vlans_on_eth1():
desired_state = create_two_vlans_state()

View File

@ -1,5 +1,5 @@
#
# Copyright (c) 2020 Red Hat, Inc.
# Copyright (c) 2020-2021 Red Hat, Inc.
#
# This file is part of nmstate
#
@ -31,6 +31,7 @@ from libnmstate.ifaces.vlan import VlanIface
from ..testlib.ifacelib import gen_foo_iface_info
BASE_IFACE_NAME = "base1"
VLAN_ID101 = 101
class TestVlanIface:
@ -44,7 +45,7 @@ class TestVlanIface:
def _gen_iface_info(self):
iface_info = gen_foo_iface_info(iface_type=InterfaceType.VLAN)
iface_info[VLAN.CONFIG_SUBTREE] = {
VLAN.ID: 101,
VLAN.ID: VLAN_ID101,
VLAN.BASE_IFACE: BASE_IFACE_NAME,
}
return iface_info
@ -61,6 +62,18 @@ class TestVlanIface:
def test_can_have_ip_as_port(self):
assert not VlanIface(self._gen_iface_info()).can_have_ip_as_port
def test_vlan_id(self):
assert VlanIface(self._gen_iface_info()).vlan_id == 101
def test_is_vlan_id_changed(self):
vlan101 = VlanIface(self._gen_iface_info())
assert not vlan101.is_vlan_id_changed
vlan200_info = self._gen_iface_info()
vlan200_info[VLAN.CONFIG_SUBTREE][VLAN.ID] = 200
vlan101.gen_metadata(Ifaces([], [vlan200_info]))
assert vlan101.is_vlan_id_changed
def test_validate_base_iface_missing(self):
iface_info = self._gen_iface_info()
iface_info[VLAN.CONFIG_SUBTREE].pop(VLAN.BASE_IFACE)