vxlan: add support to modify the VXLAN ID

NetworkManager does not allow to modify the vxlan id of an existing
VXLAN. 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-11 15:10:10 +02:00 committed by Fernando Fernández Mancera
parent a6734598ae
commit 4831c2d456
4 changed files with 67 additions and 11 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 VxlanIface(BaseIface):
def __init__(self, info, save_to_disk=True):
super().__init__(info, save_to_disk)
self._vxlan_id_changed = False
@property
def parent(self):
return self._vxlan_config.get(VXLAN.BASE_IFACE)
@ -36,6 +40,14 @@ class VxlanIface(BaseIface):
def _vxlan_config(self):
return self.raw.get(VXLAN.CONFIG_SUBTREE, {})
@property
def vxlan_id(self):
return self._vxlan_config.get(VXLAN.ID)
@property
def is_vxlan_id_changed(self):
return self._vxlan_id_changed
@property
def is_virtual(self):
return True
@ -44,6 +56,16 @@ class VxlanIface(BaseIface):
def can_have_ip_as_port(self):
return False
def _mark_vxlan_id_changed(self, ifaces):
if self.is_up:
cur_iface = ifaces.get_cur_iface(self.name, self.type)
if cur_iface and self.vxlan_id != cur_iface.vxlan_id:
self._vxlan_id_changed = True
def gen_metadata(self, ifaces):
self._mark_vxlan_id_changed(ifaces)
super().gen_metadata(ifaces)
def pre_edit_validation_and_cleanup(self):
if self.is_up:
self._validate_mandatory_properties()

View File

@ -243,14 +243,19 @@ 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
if self._iface.is_up and (
(
self._iface.type == InterfaceType.VLAN
and self._iface.is_vlan_id_changed
)
or (
self._iface.type == InterfaceType.VXLAN
and self._iface.is_vxlan_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.
# NetworkManager does not allow to modify the vlan/vxlan id of an
# existing VLAN/VXLAN. In order to support it, Nmstate is removing
# the interface and creating it again.
self._add_action(NmProfile.ACTION_DEACTIVATE_FIRST)
if (

View File

@ -1,5 +1,5 @@
#
# Copyright (c) 2019-2020 Red Hat, Inc.
# Copyright (c) 2019-2021 Red Hat, Inc.
#
# This file is part of nmstate
#
@ -25,6 +25,7 @@ import libnmstate
from libnmstate.error import NmstateVerificationError
from libnmstate.schema import Interface
from libnmstate.schema import VXLAN
from .testlib import assertlib
from .testlib.bondlib import bond_interface
@ -139,3 +140,18 @@ def test_show_vxlan_with_no_remote(eth1_up):
finally:
libnmstate.apply(vxlans_absent([vxlan]))
assertlib.assert_absent(vxlan.name)
@pytest.mark.tier1
def test_add_vxlan_and_modify_vxlan_id(eth1_up):
ifname = eth1_up[Interface.KEY][0][Interface.NAME]
with vxlan_interfaces(
VxlanState(id=VXLAN1_ID, base_if=ifname, remote="192.168.100.1")
) as desired_state:
assertlib.assert_state(desired_state)
desired_state[Interface.KEY][0][VXLAN.CONFIG_SUBTREE][VXLAN.ID] = 200
libnmstate.apply(desired_state)
assertlib.assert_state(desired_state)
vxlan1_ifname = desired_state[Interface.KEY][0][Interface.NAME]
assertlib.assert_absent(vxlan1_ifname)

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
#
@ -32,6 +32,7 @@ from ..testlib.constants import IPV4_ADDRESS1
from ..testlib.ifacelib import gen_foo_iface_info
BASE_IFACE_NAME = "base1"
VXLAN_ID101 = 101
class TestVxlanIface:
@ -45,7 +46,7 @@ class TestVxlanIface:
def _gen_iface_info(self):
iface_info = gen_foo_iface_info(iface_type=InterfaceType.VXLAN)
iface_info[VXLAN.CONFIG_SUBTREE] = {
VXLAN.ID: 101,
VXLAN.ID: VXLAN_ID101,
VXLAN.BASE_IFACE: BASE_IFACE_NAME,
VXLAN.REMOTE: IPV4_ADDRESS1,
}
@ -63,6 +64,18 @@ class TestVxlanIface:
def test_can_have_ip_as_port(self):
assert not VxlanIface(self._gen_iface_info()).can_have_ip_as_port
def test_vxlan_id(self):
assert VxlanIface(self._gen_iface_info()).vxlan_id == VXLAN_ID101
def test_is_vxlan_id_changed(self):
vxlan101 = VxlanIface(self._gen_iface_info())
assert not vxlan101.is_vxlan_id_changed
vxlan200_info = self._gen_iface_info()
vxlan200_info[VXLAN.CONFIG_SUBTREE][VXLAN.ID] = 200
vxlan101.gen_metadata(Ifaces([], [vxlan200_info]))
assert vxlan101.is_vxlan_id_changed
@pytest.mark.parametrize(
"required_field", [VXLAN.ID, VXLAN.REMOTE, VXLAN.BASE_IFACE]
)