|
Packit Service |
0535c1 |
#
|
|
Packit Service |
0535c1 |
# Copyright (c) 2018-2020 Red Hat, Inc.
|
|
Packit Service |
0535c1 |
#
|
|
Packit Service |
0535c1 |
# This file is part of nmstate
|
|
Packit Service |
0535c1 |
#
|
|
Packit Service |
0535c1 |
# This program is free software: you can redistribute it and/or modify
|
|
Packit Service |
0535c1 |
# it under the terms of the GNU Lesser General Public License as published by
|
|
Packit Service |
0535c1 |
# the Free Software Foundation, either version 2.1 of the License, or
|
|
Packit Service |
0535c1 |
# (at your option) any later version.
|
|
Packit Service |
0535c1 |
#
|
|
Packit Service |
0535c1 |
# This program is distributed in the hope that it will be useful,
|
|
Packit Service |
0535c1 |
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
Packit Service |
0535c1 |
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
Packit Service |
0535c1 |
# GNU Lesser General Public License for more details.
|
|
Packit Service |
0535c1 |
#
|
|
Packit Service |
0535c1 |
# You should have received a copy of the GNU Lesser General Public License
|
|
Packit Service |
0535c1 |
# along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|
Packit Service |
0535c1 |
#
|
|
Packit Service |
0535c1 |
|
|
Packit Service |
d5ca4e |
from distutils.version import StrictVersion
|
|
Packit Service |
0535c1 |
import logging
|
|
Packit Service |
0535c1 |
import itertools
|
|
Packit Service |
0535c1 |
|
|
Packit Service |
d5ca4e |
from libnmstate.error import NmstateNotSupportedError
|
|
Packit Service |
0535c1 |
from libnmstate.error import NmstateValueError
|
|
Packit Service |
0535c1 |
from libnmstate.schema import Interface
|
|
Packit Service |
0535c1 |
from libnmstate.schema import InterfaceState
|
|
Packit Service |
0535c1 |
from libnmstate.schema import InterfaceType
|
|
Packit Service |
0535c1 |
from libnmstate.schema import LinuxBridge as LB
|
|
Packit Service |
0535c1 |
from libnmstate.schema import OVSBridge as OvsB
|
|
Packit Service |
0535c1 |
from libnmstate.schema import OVSInterface
|
|
Packit Service |
0535c1 |
from libnmstate.schema import Team
|
|
Packit Service |
0535c1 |
from libnmstate.ifaces.bond import BondIface
|
|
Packit Service |
0535c1 |
from libnmstate.ifaces.bridge import BridgeIface
|
|
Packit Service |
0535c1 |
|
|
Packit Service |
0535c1 |
from . import bond
|
|
Packit Service |
0535c1 |
from . import bridge
|
|
Packit Service |
0535c1 |
from . import connection
|
|
Packit Service |
0535c1 |
from . import device
|
|
Packit Service |
0535c1 |
from . import ipv4
|
|
Packit Service |
0535c1 |
from . import ipv6
|
|
Packit Service |
0535c1 |
from . import lldp
|
|
Packit Service |
0535c1 |
from . import ovs
|
|
Packit Service |
0535c1 |
from . import sriov
|
|
Packit Service |
0535c1 |
from . import team
|
|
Packit Service |
0535c1 |
from . import translator
|
|
Packit Service |
0535c1 |
from . import user
|
|
Packit Service |
0535c1 |
from . import vlan
|
|
Packit Service |
0535c1 |
from . import vxlan
|
|
Packit Service |
0535c1 |
from . import wired
|
|
Packit Service |
0535c1 |
from .common import NM
|
|
Packit Service |
0535c1 |
from .dns import get_dns_config_iface_names
|
|
Packit Service |
0535c1 |
|
|
Packit Service |
0535c1 |
|
|
Packit Service |
0535c1 |
MAXIMUM_INTERFACE_LENGTH = 15
|
|
Packit Service |
0535c1 |
|
|
Packit Service |
0535c1 |
MASTER_METADATA = "_master"
|
|
Packit Service |
0535c1 |
MASTER_TYPE_METADATA = "_master_type"
|
|
Packit Service |
0535c1 |
MASTER_IFACE_TYPES = (
|
|
Packit Service |
0535c1 |
InterfaceType.OVS_BRIDGE,
|
|
Packit Service |
0535c1 |
bond.BOND_TYPE,
|
|
Packit Service |
0535c1 |
LB.TYPE,
|
|
Packit Service |
0535c1 |
Team.TYPE,
|
|
Packit Service |
0535c1 |
)
|
|
Packit Service |
0535c1 |
|
|
Packit Service |
0535c1 |
|
|
Packit Service |
0535c1 |
def apply_changes(context, net_state, save_to_disk):
|
|
Packit Service |
0535c1 |
con_profiles = []
|
|
Packit Service |
0535c1 |
|
|
Packit Service |
d5ca4e |
if (
|
|
Packit Service |
d5ca4e |
not save_to_disk
|
|
Packit Service |
d5ca4e |
and _has_ovs_interface_desired_or_changed(net_state)
|
|
Packit Service |
d5ca4e |
and StrictVersion(context.client.get_version())
|
|
Packit Service |
d5ca4e |
< StrictVersion("1.28.0")
|
|
Packit Service |
d5ca4e |
):
|
|
Packit Service |
d5ca4e |
raise NmstateNotSupportedError(
|
|
Packit Service |
d5ca4e |
f"NetworkManager version {context.client.get_version()} does not "
|
|
Packit Service |
d5ca4e |
f"support 'save_to_disk=False' against OpenvSwitch interface"
|
|
Packit Service |
d5ca4e |
)
|
|
Packit Service |
d5ca4e |
|
|
Packit Service |
0535c1 |
_preapply_dns_fix(context, net_state)
|
|
Packit Service |
bd40f4 |
_mark_nm_external_subordinate_changed(context, net_state)
|
|
Packit Service |
0535c1 |
|
|
Packit Service |
0535c1 |
ifaces_desired_state = net_state.ifaces.state_to_edit
|
|
Packit Service |
0535c1 |
ifaces_desired_state.extend(
|
|
Packit Service |
0535c1 |
_create_proxy_ifaces_desired_state(ifaces_desired_state)
|
|
Packit Service |
0535c1 |
)
|
|
Packit Service |
0535c1 |
|
|
Packit Service |
0535c1 |
for iface_desired_state in filter(
|
|
Packit Service |
0535c1 |
lambda s: s.get(Interface.STATE) != InterfaceState.ABSENT,
|
|
Packit Service |
0535c1 |
ifaces_desired_state,
|
|
Packit Service |
0535c1 |
):
|
|
Packit Service |
0535c1 |
ifname = iface_desired_state[Interface.NAME]
|
|
Packit Service |
0535c1 |
nmdev = context.get_nm_dev(ifname)
|
|
Packit Service |
0535c1 |
cur_con_profile = None
|
|
Packit Service |
0535c1 |
if nmdev:
|
|
Packit Service |
0535c1 |
cur_con_profile = connection.ConnectionProfile(context)
|
|
Packit Service |
0535c1 |
cur_con_profile.import_by_device(nmdev)
|
|
Packit Service |
0535c1 |
else:
|
|
Packit Service |
0535c1 |
# Profile for virtual interface will remove interface when down
|
|
Packit Service |
0535c1 |
# hence search on existing NM.RemoteConnections
|
|
Packit Service |
0535c1 |
con_profile = context.client.get_connection_by_id(ifname)
|
|
Packit Service |
0535c1 |
if con_profile and con_profile.get_interface_name() == ifname:
|
|
Packit Service |
0535c1 |
cur_con_profile = connection.ConnectionProfile(
|
|
Packit Service |
0535c1 |
context, profile=con_profile
|
|
Packit Service |
0535c1 |
)
|
|
Packit Service |
0535c1 |
original_desired_iface_state = {}
|
|
Packit Service |
0535c1 |
if net_state.ifaces.get(ifname):
|
|
Packit Service |
0535c1 |
iface = net_state.ifaces[ifname]
|
|
Packit Service |
0535c1 |
if iface.is_desired:
|
|
Packit Service |
0535c1 |
original_desired_iface_state = iface.original_dict
|
|
Packit Service |
0535c1 |
if (
|
|
Packit Service |
0535c1 |
set(original_desired_iface_state.keys())
|
|
Packit Service |
0535c1 |
<= set([Interface.STATE, Interface.NAME, Interface.TYPE])
|
|
Packit Service |
0535c1 |
and cur_con_profile
|
|
Packit Service |
0535c1 |
and cur_con_profile.profile
|
|
Packit Service |
0535c1 |
and not net_state.ifaces[ifname].is_changed
|
|
Packit Service |
0535c1 |
):
|
|
Packit Service |
0535c1 |
# Don't create new profile if original desire does not ask
|
|
Packit Service |
0535c1 |
# anything besides state:up and not been marked as changed.
|
|
Packit Service |
0535c1 |
# We don't need to do this once we support querying on-disk
|
|
Packit Service |
0535c1 |
# configure
|
|
Packit Service |
0535c1 |
con_profiles.append(cur_con_profile)
|
|
Packit Service |
0535c1 |
continue
|
|
Packit Service |
0535c1 |
new_con_profile = _build_connection_profile(
|
|
Packit Service |
0535c1 |
context,
|
|
Packit Service |
0535c1 |
iface_desired_state,
|
|
Packit Service |
0535c1 |
cur_con_profile,
|
|
Packit Service |
0535c1 |
original_desired_iface_state,
|
|
Packit Service |
0535c1 |
)
|
|
Packit Service |
0535c1 |
if not new_con_profile.devname:
|
|
Packit Service |
0535c1 |
set_conn = new_con_profile.profile.get_setting_connection()
|
|
Packit Service |
0535c1 |
set_conn.props.interface_name = iface_desired_state[Interface.NAME]
|
|
Packit Service |
0535c1 |
if cur_con_profile and cur_con_profile.profile:
|
|
Packit Service |
0535c1 |
cur_con_profile.update(new_con_profile, save_to_disk)
|
|
Packit Service |
0535c1 |
con_profiles.append(new_con_profile)
|
|
Packit Service |
0535c1 |
else:
|
|
Packit Service |
0535c1 |
# Missing connection, attempting to create a new one.
|
|
Packit Service |
0535c1 |
connection.delete_iface_inactive_connections(context, ifname)
|
|
Packit Service |
0535c1 |
new_con_profile.add(save_to_disk)
|
|
Packit Service |
0535c1 |
con_profiles.append(new_con_profile)
|
|
Packit Service |
0535c1 |
context.wait_all_finish()
|
|
Packit Service |
0535c1 |
|
|
Packit Service |
0535c1 |
_set_ifaces_admin_state(context, ifaces_desired_state, con_profiles)
|
|
Packit Service |
0535c1 |
context.wait_all_finish()
|
|
Packit Service |
0535c1 |
|
|
Packit Service |
0535c1 |
|
|
Packit Service |
0535c1 |
def _set_ifaces_admin_state(context, ifaces_desired_state, con_profiles):
|
|
Packit Service |
0535c1 |
"""
|
|
Packit Service |
0535c1 |
Control interface admin state by activating, deactivating and deleting
|
|
Packit Service |
0535c1 |
devices connection profiles.
|
|
Packit Service |
0535c1 |
|
|
Packit Service |
0535c1 |
The `absent` state results in deactivating the device and deleting
|
|
Packit Service |
0535c1 |
the connection profile.
|
|
Packit Service |
0535c1 |
|
|
Packit Service |
0535c1 |
For new virtual devices, the `up` state is handled by activating the
|
|
Packit Service |
0535c1 |
new connection profile. For existing devices, the device is activated,
|
|
Packit Service |
0535c1 |
leaving it to choose the correct profile.
|
|
Packit Service |
0535c1 |
|
|
Packit Service |
0535c1 |
In order to activate correctly the interfaces, the order is significant:
|
|
Packit Service |
0535c1 |
- Master-less master interfaces.
|
|
Packit Service |
0535c1 |
- New interfaces (virtual interfaces, but not OVS ones).
|
|
Packit Service |
0535c1 |
- Master interfaces.
|
|
Packit Service |
0535c1 |
- OVS ports.
|
|
Packit Service |
0535c1 |
- OVS internal.
|
|
Packit Service |
0535c1 |
- All the rest.
|
|
Packit Service |
0535c1 |
"""
|
|
Packit Service |
0535c1 |
con_profiles_by_devname = _index_profiles_by_devname(con_profiles)
|
|
Packit Service |
0535c1 |
new_ifaces = _get_new_ifaces(context, con_profiles)
|
|
Packit Service |
0535c1 |
new_ifaces_to_activate = set()
|
|
Packit Service |
0535c1 |
new_vlan_ifaces_to_activate = set()
|
|
Packit Service |
0535c1 |
new_vxlan_ifaces_to_activate = set()
|
|
Packit Service |
0535c1 |
new_ovs_interface_to_activate = set()
|
|
Packit Service |
0535c1 |
new_ovs_port_to_activate = set()
|
|
Packit Service |
0535c1 |
new_master_not_enslaved_ifaces = set()
|
|
Packit Service |
0535c1 |
master_ifaces_to_edit = set()
|
|
Packit Service |
0535c1 |
ifaces_to_edit = set()
|
|
Packit Service |
0535c1 |
devs_to_deactivate = {}
|
|
Packit Service |
0535c1 |
devs_to_delete_profile = {}
|
|
Packit Service |
0535c1 |
devs_to_delete = {}
|
|
Packit Service |
0535c1 |
devs_to_deactivate_beforehand = []
|
|
Packit Service |
0535c1 |
profiles_to_delete = []
|
|
Packit Service |
0535c1 |
devs_to_activate_beforehand = []
|
|
Packit Service |
0535c1 |
|
|
Packit Service |
0535c1 |
current_profiles = context.client.get_connections()
|
|
Packit Service |
0535c1 |
|
|
Packit Service |
0535c1 |
for iface_desired_state in ifaces_desired_state:
|
|
Packit Service |
0535c1 |
ifname = iface_desired_state[Interface.NAME]
|
|
Packit Service |
0535c1 |
nmdev = context.get_nm_dev(ifname)
|
|
Packit Service |
0535c1 |
if not nmdev:
|
|
Packit Service |
0535c1 |
if (
|
|
Packit Service |
0535c1 |
ifname in new_ifaces
|
|
Packit Service |
0535c1 |
and iface_desired_state[Interface.STATE] == InterfaceState.UP
|
|
Packit Service |
0535c1 |
):
|
|
Packit Service |
0535c1 |
if _is_master_iface(
|
|
Packit Service |
0535c1 |
iface_desired_state
|
|
Packit Service |
0535c1 |
) and not _is_slave_iface(iface_desired_state):
|
|
Packit Service |
0535c1 |
new_master_not_enslaved_ifaces.add(ifname)
|
|
Packit Service |
0535c1 |
elif (
|
|
Packit Service |
0535c1 |
iface_desired_state[Interface.TYPE]
|
|
Packit Service |
0535c1 |
== InterfaceType.OVS_INTERFACE
|
|
Packit Service |
0535c1 |
):
|
|
Packit Service |
0535c1 |
new_ovs_interface_to_activate.add(ifname)
|
|
Packit Service |
0535c1 |
elif (
|
|
Packit Service |
0535c1 |
iface_desired_state[Interface.TYPE]
|
|
Packit Service |
0535c1 |
== InterfaceType.OVS_PORT
|
|
Packit Service |
0535c1 |
):
|
|
Packit Service |
0535c1 |
new_ovs_port_to_activate.add(ifname)
|
|
Packit Service |
0535c1 |
elif iface_desired_state[Interface.TYPE] == InterfaceType.VLAN:
|
|
Packit Service |
0535c1 |
new_vlan_ifaces_to_activate.add(ifname)
|
|
Packit Service |
0535c1 |
elif (
|
|
Packit Service |
0535c1 |
iface_desired_state[Interface.TYPE] == InterfaceType.VXLAN
|
|
Packit Service |
0535c1 |
):
|
|
Packit Service |
0535c1 |
new_vxlan_ifaces_to_activate.add(ifname)
|
|
Packit Service |
0535c1 |
else:
|
|
Packit Service |
0535c1 |
new_ifaces_to_activate.add(ifname)
|
|
Packit Service |
0535c1 |
elif iface_desired_state[Interface.STATE] == InterfaceState.ABSENT:
|
|
Packit Service |
0535c1 |
# Delete the down profiles
|
|
Packit Service |
0535c1 |
iface_name = iface_desired_state[Interface.NAME]
|
|
Packit Service |
0535c1 |
for current_profile in current_profiles:
|
|
Packit Service |
0535c1 |
if current_profile.get_interface_name() == iface_name:
|
|
Packit Service |
0535c1 |
profile = connection.ConnectionProfile(
|
|
Packit Service |
0535c1 |
context, current_profile
|
|
Packit Service |
0535c1 |
)
|
|
Packit Service |
0535c1 |
profiles_to_delete.append(profile)
|
|
Packit Service |
0535c1 |
|
|
Packit Service |
0535c1 |
else:
|
|
Packit Service |
0535c1 |
if not nmdev.get_managed():
|
|
Packit Service |
0535c1 |
nmdev.set_managed(True)
|
|
Packit Service |
0535c1 |
if iface_desired_state[Interface.STATE] == InterfaceState.DOWN:
|
|
Packit Service |
0535c1 |
devs_to_activate_beforehand.append(nmdev)
|
|
Packit Service |
0535c1 |
if iface_desired_state[Interface.STATE] == InterfaceState.UP:
|
|
Packit Service |
0535c1 |
if (
|
|
Packit Service |
0535c1 |
iface_desired_state.get(Interface.TYPE)
|
|
Packit Service |
0535c1 |
== InterfaceType.BOND
|
|
Packit Service |
0535c1 |
):
|
|
Packit Service |
0535c1 |
iface = BondIface(iface_desired_state)
|
|
Packit Service |
0535c1 |
if iface.is_bond_mode_changed:
|
|
Packit Service |
0535c1 |
# NetworkManager leaves leftover in sysfs for bond
|
|
Packit Service |
0535c1 |
# options when changing bond mode, bug:
|
|
Packit Service |
0535c1 |
# https://bugzilla.redhat.com/show_bug.cgi?id=1819137
|
|
Packit Service |
0535c1 |
# Workaround: delete the bond interface from kernel and
|
|
Packit Service |
0535c1 |
# create again via full deactivation beforehand.
|
|
Packit Service |
0535c1 |
logging.debug(
|
|
Packit Service |
0535c1 |
f"Bond interface {ifname} is changing bond mode, "
|
|
Packit Service |
0535c1 |
"will do full deactivation before applying changes"
|
|
Packit Service |
0535c1 |
)
|
|
Packit Service |
0535c1 |
devs_to_deactivate_beforehand.append(nmdev)
|
|
Packit Service |
0535c1 |
|
|
Packit Service |
0535c1 |
if _is_master_iface(iface_desired_state):
|
|
Packit Service |
0535c1 |
master_ifaces_to_edit.add(
|
|
Packit Service |
0535c1 |
(nmdev, con_profiles_by_devname[ifname].profile)
|
|
Packit Service |
0535c1 |
)
|
|
Packit Service |
0535c1 |
else:
|
|
Packit Service |
0535c1 |
ifaces_to_edit.add(
|
|
Packit Service |
0535c1 |
(nmdev, con_profiles_by_devname[ifname].profile)
|
|
Packit Service |
0535c1 |
)
|
|
Packit Service |
0535c1 |
elif iface_desired_state[Interface.STATE] in (
|
|
Packit Service |
0535c1 |
InterfaceState.DOWN,
|
|
Packit Service |
0535c1 |
InterfaceState.ABSENT,
|
|
Packit Service |
0535c1 |
):
|
|
Packit Service |
0535c1 |
nmdevs = _get_affected_devices(context, iface_desired_state)
|
|
Packit Service |
0535c1 |
is_absent = (
|
|
Packit Service |
0535c1 |
iface_desired_state[Interface.STATE]
|
|
Packit Service |
0535c1 |
== InterfaceState.ABSENT
|
|
Packit Service |
0535c1 |
)
|
|
Packit Service |
0535c1 |
for affected_nmdev in nmdevs:
|
|
Packit Service |
0535c1 |
devs_to_deactivate[
|
|
Packit Service |
0535c1 |
affected_nmdev.get_iface()
|
|
Packit Service |
0535c1 |
] = affected_nmdev
|
|
Packit Service |
0535c1 |
if is_absent:
|
|
Packit Service |
0535c1 |
devs_to_delete_profile[
|
|
Packit Service |
0535c1 |
affected_nmdev.get_iface()
|
|
Packit Service |
0535c1 |
] = affected_nmdev
|
|
Packit Service |
0535c1 |
if (
|
|
Packit Service |
0535c1 |
is_absent
|
|
Packit Service |
0535c1 |
and nmdev.is_software()
|
|
Packit Service |
0535c1 |
and nmdev.get_device_type() != NM.DeviceType.VETH
|
|
Packit Service |
0535c1 |
):
|
|
Packit Service |
0535c1 |
devs_to_delete[nmdev.get_iface()] = nmdev
|
|
Packit Service |
0535c1 |
else:
|
|
Packit Service |
0535c1 |
raise NmstateValueError(
|
|
Packit Service |
0535c1 |
"Invalid state {} for interface {}".format(
|
|
Packit Service |
0535c1 |
iface_desired_state[Interface.STATE],
|
|
Packit Service |
0535c1 |
iface_desired_state[Interface.NAME],
|
|
Packit Service |
0535c1 |
)
|
|
Packit Service |
0535c1 |
)
|
|
Packit Service |
0535c1 |
|
|
Packit Service |
0535c1 |
for nmdev in devs_to_activate_beforehand:
|
|
Packit Service |
0535c1 |
profile = connection.ConnectionProfile(context)
|
|
Packit Service |
0535c1 |
profile.con_id = nmdev.get_iface()
|
|
Packit Service |
0535c1 |
profile.activate()
|
|
Packit Service |
0535c1 |
context.wait_all_finish()
|
|
Packit Service |
0535c1 |
|
|
Packit Service |
0535c1 |
for dev in devs_to_deactivate_beforehand:
|
|
Packit Service |
0535c1 |
device.deactivate(context, dev)
|
|
Packit Service |
0535c1 |
|
|
Packit Service |
0535c1 |
# Do not remove devices that are marked for editing.
|
|
Packit Service |
0535c1 |
for dev, _ in itertools.chain(master_ifaces_to_edit, ifaces_to_edit):
|
|
Packit Service |
0535c1 |
devs_to_deactivate.pop(dev.get_iface(), None)
|
|
Packit Service |
0535c1 |
devs_to_delete_profile.pop(dev.get_iface(), None)
|
|
Packit Service |
0535c1 |
devs_to_delete.pop(dev.get_iface(), None)
|
|
Packit Service |
0535c1 |
|
|
Packit Service |
0535c1 |
for profile in profiles_to_delete:
|
|
Packit Service |
0535c1 |
profile.delete()
|
|
Packit Service |
0535c1 |
context.wait_all_finish()
|
|
Packit Service |
0535c1 |
|
|
Packit Service |
0535c1 |
for ifname in new_master_not_enslaved_ifaces:
|
|
Packit Service |
0535c1 |
device.activate(context, dev=None, connection_id=ifname)
|
|
Packit Service |
0535c1 |
context.wait_all_finish()
|
|
Packit Service |
0535c1 |
|
|
Packit Service |
0535c1 |
for ifname in new_ifaces_to_activate:
|
|
Packit Service |
0535c1 |
device.activate(context, dev=None, connection_id=ifname)
|
|
Packit Service |
0535c1 |
context.wait_all_finish()
|
|
Packit Service |
0535c1 |
|
|
Packit Service |
0535c1 |
for dev, con_profile in master_ifaces_to_edit:
|
|
Packit Service |
0535c1 |
device.modify(context, dev, con_profile)
|
|
Packit Service |
0535c1 |
context.wait_all_finish()
|
|
Packit Service |
0535c1 |
|
|
Packit Service |
0535c1 |
for ifname in new_ovs_port_to_activate:
|
|
Packit Service |
0535c1 |
device.activate(context, dev=None, connection_id=ifname)
|
|
Packit Service |
0535c1 |
context.wait_all_finish()
|
|
Packit Service |
0535c1 |
|
|
Packit Service |
0535c1 |
for ifname in new_ovs_interface_to_activate:
|
|
Packit Service |
0535c1 |
device.activate(context, dev=None, connection_id=ifname)
|
|
Packit Service |
0535c1 |
context.wait_all_finish()
|
|
Packit Service |
0535c1 |
|
|
Packit Service |
0535c1 |
for dev, con_profile in ifaces_to_edit:
|
|
Packit Service |
0535c1 |
device.modify(context, dev, con_profile)
|
|
Packit Service |
0535c1 |
context.wait_all_finish()
|
|
Packit Service |
0535c1 |
|
|
Packit Service |
0535c1 |
for ifname in new_vlan_ifaces_to_activate:
|
|
Packit Service |
0535c1 |
device.activate(context, dev=None, connection_id=ifname)
|
|
Packit Service |
0535c1 |
context.wait_all_finish()
|
|
Packit Service |
0535c1 |
|
|
Packit Service |
0535c1 |
for ifname in new_vxlan_ifaces_to_activate:
|
|
Packit Service |
0535c1 |
device.activate(context, dev=None, connection_id=ifname)
|
|
Packit Service |
0535c1 |
context.wait_all_finish()
|
|
Packit Service |
0535c1 |
|
|
Packit Service |
0535c1 |
for dev in devs_to_deactivate.values():
|
|
Packit Service |
0535c1 |
device.deactivate(context, dev)
|
|
Packit Service |
0535c1 |
context.wait_all_finish()
|
|
Packit Service |
0535c1 |
|
|
Packit Service |
0535c1 |
for dev in devs_to_delete_profile.values():
|
|
Packit Service |
0535c1 |
device.delete(context, dev)
|
|
Packit Service |
0535c1 |
context.wait_all_finish()
|
|
Packit Service |
0535c1 |
|
|
Packit Service |
0535c1 |
for dev in devs_to_delete.values():
|
|
Packit Service |
0535c1 |
device.delete_device(context, dev)
|
|
Packit Service |
0535c1 |
context.wait_all_finish()
|
|
Packit Service |
0535c1 |
|
|
Packit Service |
0535c1 |
|
|
Packit Service |
0535c1 |
def _index_profiles_by_devname(con_profiles):
|
|
Packit Service |
0535c1 |
return {con_profile.devname: con_profile for con_profile in con_profiles}
|
|
Packit Service |
0535c1 |
|
|
Packit Service |
0535c1 |
|
|
Packit Service |
0535c1 |
def _get_new_ifaces(context, con_profiles):
|
|
Packit Service |
0535c1 |
ifaces_without_device = set()
|
|
Packit Service |
0535c1 |
for con_profile in con_profiles:
|
|
Packit Service |
0535c1 |
ifname = con_profile.devname
|
|
Packit Service |
0535c1 |
nmdev = context.get_nm_dev(ifname)
|
|
Packit Service |
0535c1 |
if not nmdev:
|
|
Packit Service |
0535c1 |
# When the profile id is different from the iface name, use the
|
|
Packit Service |
0535c1 |
# profile id.
|
|
Packit Service |
0535c1 |
if ifname != con_profile.con_id:
|
|
Packit Service |
0535c1 |
ifname = con_profile.con_id
|
|
Packit Service |
0535c1 |
ifaces_without_device.add(ifname)
|
|
Packit Service |
0535c1 |
return ifaces_without_device
|
|
Packit Service |
0535c1 |
|
|
Packit Service |
0535c1 |
|
|
Packit Service |
0535c1 |
def _is_master_iface(iface_state):
|
|
Packit Service |
0535c1 |
return iface_state[Interface.TYPE] in MASTER_IFACE_TYPES
|
|
Packit Service |
0535c1 |
|
|
Packit Service |
0535c1 |
|
|
Packit Service |
0535c1 |
def _is_slave_iface(iface_state):
|
|
Packit Service |
0535c1 |
return iface_state.get(MASTER_METADATA)
|
|
Packit Service |
0535c1 |
|
|
Packit Service |
0535c1 |
|
|
Packit Service |
0535c1 |
def _get_affected_devices(context, iface_state):
|
|
Packit Service |
0535c1 |
nmdev = context.get_nm_dev(iface_state[Interface.NAME])
|
|
Packit Service |
0535c1 |
devs = []
|
|
Packit Service |
0535c1 |
if nmdev:
|
|
Packit Service |
0535c1 |
devs += [nmdev]
|
|
Packit Service |
0535c1 |
iface_type = iface_state[Interface.TYPE]
|
|
Packit Service |
0535c1 |
if iface_type == InterfaceType.OVS_BRIDGE:
|
|
Packit Service |
0535c1 |
port_slaves = ovs.get_slaves(nmdev)
|
|
Packit Service |
0535c1 |
iface_slaves = [
|
|
Packit Service |
0535c1 |
iface for port in port_slaves for iface in ovs.get_slaves(port)
|
|
Packit Service |
0535c1 |
]
|
|
Packit Service |
0535c1 |
devs += port_slaves + iface_slaves
|
|
Packit Service |
0535c1 |
elif iface_type == LB.TYPE:
|
|
Packit Service |
0535c1 |
devs += bridge.get_slaves(nmdev)
|
|
Packit Service |
0535c1 |
elif iface_type == bond.BOND_TYPE:
|
|
Packit Service |
0535c1 |
devs += bond.get_slaves(nmdev)
|
|
Packit Service |
0535c1 |
|
|
Packit Service |
0535c1 |
ovs_port_dev = ovs.get_port_by_slave(nmdev)
|
|
Packit Service |
0535c1 |
if ovs_port_dev:
|
|
Packit Service |
0535c1 |
devs.append(ovs_port_dev)
|
|
Packit Service |
0535c1 |
return devs
|
|
Packit Service |
0535c1 |
|
|
Packit Service |
0535c1 |
|
|
Packit Service |
0535c1 |
def _create_proxy_ifaces_desired_state(ifaces_desired_state):
|
|
Packit Service |
0535c1 |
"""
|
|
Packit Service |
0535c1 |
Prepare the state of the "proxy" interfaces. These are interfaces that
|
|
Packit Service |
0535c1 |
exist as NM entities/profiles, but are invisible to the API.
|
|
Packit Service |
0535c1 |
These proxy interfaces state is created as a side effect of other ifaces
|
|
Packit Service |
0535c1 |
definition.
|
|
Packit Service |
0535c1 |
Note: This function modifies the ifaces_desired_state content in addition
|
|
Packit Service |
0535c1 |
to returning a new set of states for the proxy interfaces.
|
|
Packit Service |
0535c1 |
|
|
Packit Service |
0535c1 |
In OVS case, the port profile is the proxy, it is not part of the public
|
|
Packit Service |
0535c1 |
state of the system, but internal to the NM provider.
|
|
Packit Service |
0535c1 |
"""
|
|
Packit Service |
0535c1 |
new_ifaces_desired_state = []
|
|
Packit Service |
0535c1 |
new_ifaces_names = set()
|
|
Packit Service |
0535c1 |
for iface_desired_state in ifaces_desired_state:
|
|
Packit Service |
0535c1 |
master_type = iface_desired_state.get(MASTER_TYPE_METADATA)
|
|
Packit Service |
0535c1 |
if master_type != InterfaceType.OVS_BRIDGE:
|
|
Packit Service |
0535c1 |
continue
|
|
Packit Service |
0535c1 |
port_opts_metadata = iface_desired_state.get(
|
|
Packit Service |
0535c1 |
BridgeIface.BRPORT_OPTIONS_METADATA
|
|
Packit Service |
0535c1 |
)
|
|
Packit Service |
0535c1 |
if port_opts_metadata is None:
|
|
Packit Service |
0535c1 |
continue
|
|
Packit Service |
0535c1 |
port_iface_desired_state = _create_ovs_port_iface_desired_state(
|
|
Packit Service |
0535c1 |
iface_desired_state, port_opts_metadata
|
|
Packit Service |
0535c1 |
)
|
|
Packit Service |
0535c1 |
port_iface_name = port_iface_desired_state[Interface.NAME]
|
|
Packit Service |
0535c1 |
if port_iface_name not in new_ifaces_names:
|
|
Packit Service |
0535c1 |
new_ifaces_names.add(port_iface_name)
|
|
Packit Service |
0535c1 |
new_ifaces_desired_state.append(port_iface_desired_state)
|
|
Packit Service |
0535c1 |
# The "visible" slave/interface needs to point to the port profile
|
|
Packit Service |
0535c1 |
iface_desired_state[MASTER_METADATA] = port_iface_desired_state[
|
|
Packit Service |
0535c1 |
Interface.NAME
|
|
Packit Service |
0535c1 |
]
|
|
Packit Service |
0535c1 |
iface_desired_state[MASTER_TYPE_METADATA] = InterfaceType.OVS_PORT
|
|
Packit Service |
0535c1 |
return new_ifaces_desired_state
|
|
Packit Service |
0535c1 |
|
|
Packit Service |
0535c1 |
|
|
Packit Service |
0535c1 |
def _create_ovs_port_iface_desired_state(iface_desired_state, port_options):
|
|
Packit Service |
0535c1 |
iface_name = iface_desired_state[Interface.NAME]
|
|
Packit Service |
0535c1 |
if _is_ovs_lag_port(port_options):
|
|
Packit Service |
0535c1 |
port_name = port_options[OvsB.Port.NAME]
|
|
Packit Service |
0535c1 |
else:
|
|
Packit Service |
0535c1 |
port_name = ovs.PORT_PROFILE_PREFIX + iface_name
|
|
Packit Service |
0535c1 |
return {
|
|
Packit Service |
0535c1 |
Interface.NAME: port_name,
|
|
Packit Service |
0535c1 |
Interface.TYPE: InterfaceType.OVS_PORT,
|
|
Packit Service |
0535c1 |
Interface.STATE: iface_desired_state[Interface.STATE],
|
|
Packit Service |
0535c1 |
OvsB.OPTIONS_SUBTREE: port_options,
|
|
Packit Service |
0535c1 |
MASTER_METADATA: iface_desired_state[MASTER_METADATA],
|
|
Packit Service |
0535c1 |
MASTER_TYPE_METADATA: iface_desired_state[MASTER_TYPE_METADATA],
|
|
Packit Service |
0535c1 |
}
|
|
Packit Service |
0535c1 |
|
|
Packit Service |
0535c1 |
|
|
Packit Service |
0535c1 |
def _is_ovs_lag_port(port_state):
|
|
Packit Service |
0535c1 |
return port_state.get(OvsB.Port.LINK_AGGREGATION_SUBTREE) is not None
|
|
Packit Service |
0535c1 |
|
|
Packit Service |
0535c1 |
|
|
Packit Service |
0535c1 |
def _build_connection_profile(
|
|
Packit Service |
0535c1 |
context,
|
|
Packit Service |
0535c1 |
iface_desired_state,
|
|
Packit Service |
0535c1 |
base_con_profile,
|
|
Packit Service |
0535c1 |
original_desired_iface_state,
|
|
Packit Service |
0535c1 |
):
|
|
Packit Service |
0535c1 |
iface_type = translator.Api2Nm.get_iface_type(
|
|
Packit Service |
0535c1 |
iface_desired_state[Interface.TYPE]
|
|
Packit Service |
0535c1 |
)
|
|
Packit Service |
0535c1 |
|
|
Packit Service |
0535c1 |
base_profile = base_con_profile.profile if base_con_profile else None
|
|
Packit Service |
0535c1 |
|
|
Packit Service |
0535c1 |
settings = [
|
|
Packit Service |
0535c1 |
ipv4.create_setting(
|
|
Packit Service |
0535c1 |
iface_desired_state.get(Interface.IPV4), base_profile
|
|
Packit Service |
0535c1 |
),
|
|
Packit Service |
0535c1 |
ipv6.create_setting(
|
|
Packit Service |
0535c1 |
iface_desired_state.get(Interface.IPV6), base_profile
|
|
Packit Service |
0535c1 |
),
|
|
Packit Service |
0535c1 |
]
|
|
Packit Service |
0535c1 |
|
|
Packit Service |
0535c1 |
con_setting = connection.ConnectionSetting()
|
|
Packit Service |
0535c1 |
iface_name = iface_desired_state[Interface.NAME]
|
|
Packit Service |
0535c1 |
if base_profile:
|
|
Packit Service |
0535c1 |
con_setting.import_by_profile(base_con_profile)
|
|
Packit Service |
0535c1 |
con_setting.set_profile_name(iface_name)
|
|
Packit Service |
0535c1 |
else:
|
|
Packit Service |
0535c1 |
con_setting.create(
|
|
Packit Service |
0535c1 |
con_name=iface_name, iface_name=iface_name, iface_type=iface_type,
|
|
Packit Service |
0535c1 |
)
|
|
Packit Service |
0535c1 |
lldp.apply_lldp_setting(con_setting, iface_desired_state)
|
|
Packit Service |
0535c1 |
|
|
Packit Service |
0535c1 |
master = iface_desired_state.get(MASTER_METADATA)
|
|
Packit Service |
0535c1 |
_translate_master_type(iface_desired_state)
|
|
Packit Service |
0535c1 |
master_type = iface_desired_state.get(MASTER_TYPE_METADATA)
|
|
Packit Service |
0535c1 |
con_setting.set_master(master, master_type)
|
|
Packit Service |
0535c1 |
settings.append(con_setting.setting)
|
|
Packit Service |
0535c1 |
|
|
Packit Service |
0535c1 |
# Only apply wired/ethernet configuration based on original desire
|
|
Packit Service |
0535c1 |
# state rather than the merged one.
|
|
Packit Service |
0535c1 |
wired_setting = wired.create_setting(
|
|
Packit Service |
0535c1 |
original_desired_iface_state, base_profile
|
|
Packit Service |
0535c1 |
)
|
|
Packit Service |
0535c1 |
if wired_setting:
|
|
Packit Service |
0535c1 |
settings.append(wired_setting)
|
|
Packit Service |
0535c1 |
|
|
Packit Service |
0535c1 |
user_setting = user.create_setting(iface_desired_state, base_profile)
|
|
Packit Service |
0535c1 |
if user_setting:
|
|
Packit Service |
0535c1 |
settings.append(user_setting)
|
|
Packit Service |
0535c1 |
|
|
Packit Service |
0535c1 |
bond_opts = translator.Api2Nm.get_bond_options(iface_desired_state)
|
|
Packit Service |
0535c1 |
if bond_opts:
|
|
Packit Service |
0535c1 |
settings.append(bond.create_setting(bond_opts, wired_setting))
|
|
Packit Service |
0535c1 |
elif iface_type == bridge.BRIDGE_TYPE:
|
|
Packit Service |
0535c1 |
bridge_config = iface_desired_state.get(bridge.BRIDGE_TYPE, {})
|
|
Packit Service |
0535c1 |
bridge_options = bridge_config.get(LB.OPTIONS_SUBTREE)
|
|
Packit Service |
0535c1 |
bridge_ports = bridge_config.get(LB.PORT_SUBTREE)
|
|
Packit Service |
0535c1 |
|
|
Packit Service |
0535c1 |
if bridge_options or bridge_ports:
|
|
Packit Service |
0535c1 |
linux_bridge_setting = bridge.create_setting(
|
|
Packit Service |
0535c1 |
iface_desired_state,
|
|
Packit Service |
0535c1 |
base_profile,
|
|
Packit Service |
0535c1 |
original_desired_iface_state,
|
|
Packit Service |
0535c1 |
)
|
|
Packit Service |
0535c1 |
settings.append(linux_bridge_setting)
|
|
Packit Service |
0535c1 |
elif iface_type == InterfaceType.OVS_BRIDGE:
|
|
Packit Service |
0535c1 |
ovs_bridge_state = iface_desired_state.get(OvsB.CONFIG_SUBTREE, {})
|
|
Packit Service |
0535c1 |
ovs_bridge_options = ovs_bridge_state.get(OvsB.OPTIONS_SUBTREE)
|
|
Packit Service |
0535c1 |
if ovs_bridge_options:
|
|
Packit Service |
0535c1 |
settings.append(ovs.create_bridge_setting(ovs_bridge_options))
|
|
Packit Service |
0535c1 |
elif iface_type == InterfaceType.OVS_PORT:
|
|
Packit Service |
0535c1 |
ovs_port_options = iface_desired_state.get(OvsB.OPTIONS_SUBTREE)
|
|
Packit Service |
0535c1 |
settings.append(ovs.create_port_setting(ovs_port_options))
|
|
Packit Service |
0535c1 |
elif iface_type == InterfaceType.OVS_INTERFACE:
|
|
Packit Service |
0535c1 |
patch_state = iface_desired_state.get(
|
|
Packit Service |
0535c1 |
OVSInterface.PATCH_CONFIG_SUBTREE
|
|
Packit Service |
0535c1 |
)
|
|
Packit Service |
0535c1 |
settings.extend(ovs.create_interface_setting(patch_state))
|
|
Packit Service |
0535c1 |
|
|
Packit Service |
0535c1 |
bridge_port_options = iface_desired_state.get(
|
|
Packit Service |
0535c1 |
BridgeIface.BRPORT_OPTIONS_METADATA
|
|
Packit Service |
0535c1 |
)
|
|
Packit Service |
0535c1 |
if bridge_port_options and master_type == bridge.BRIDGE_TYPE:
|
|
Packit Service |
0535c1 |
settings.append(
|
|
Packit Service |
0535c1 |
bridge.create_port_setting(bridge_port_options, base_profile)
|
|
Packit Service |
0535c1 |
)
|
|
Packit Service |
0535c1 |
|
|
Packit Service |
0535c1 |
vlan_setting = vlan.create_setting(iface_desired_state, base_profile)
|
|
Packit Service |
0535c1 |
if vlan_setting:
|
|
Packit Service |
0535c1 |
settings.append(vlan_setting)
|
|
Packit Service |
0535c1 |
|
|
Packit Service |
0535c1 |
vxlan_setting = vxlan.create_setting(iface_desired_state, base_profile)
|
|
Packit Service |
0535c1 |
if vxlan_setting:
|
|
Packit Service |
0535c1 |
settings.append(vxlan_setting)
|
|
Packit Service |
0535c1 |
|
|
Packit Service |
0535c1 |
sriov_setting = sriov.create_setting(
|
|
Packit Service |
0535c1 |
context, iface_desired_state, base_con_profile
|
|
Packit Service |
0535c1 |
)
|
|
Packit Service |
0535c1 |
if sriov_setting:
|
|
Packit Service |
0535c1 |
settings.append(sriov_setting)
|
|
Packit Service |
0535c1 |
|
|
Packit Service |
0535c1 |
team_setting = team.create_setting(iface_desired_state, base_con_profile)
|
|
Packit Service |
0535c1 |
if team_setting:
|
|
Packit Service |
0535c1 |
settings.append(team_setting)
|
|
Packit Service |
0535c1 |
|
|
Packit Service |
0535c1 |
new_profile = connection.ConnectionProfile(context)
|
|
Packit Service |
0535c1 |
new_profile.create(settings)
|
|
Packit Service |
0535c1 |
return new_profile
|
|
Packit Service |
0535c1 |
|
|
Packit Service |
0535c1 |
|
|
Packit Service |
0535c1 |
def _translate_master_type(iface_desired_state):
|
|
Packit Service |
0535c1 |
"""
|
|
Packit Service |
0535c1 |
Translates the master type metadata names to their equivalent
|
|
Packit Service |
0535c1 |
NM type names.
|
|
Packit Service |
0535c1 |
"""
|
|
Packit Service |
0535c1 |
master_type = iface_desired_state.get(MASTER_TYPE_METADATA)
|
|
Packit Service |
0535c1 |
if master_type == LB.TYPE:
|
|
Packit Service |
0535c1 |
iface_desired_state[MASTER_TYPE_METADATA] = bridge.BRIDGE_TYPE
|
|
Packit Service |
0535c1 |
|
|
Packit Service |
0535c1 |
|
|
Packit Service |
0535c1 |
def _preapply_dns_fix(context, net_state):
|
|
Packit Service |
0535c1 |
"""
|
|
Packit Service |
0535c1 |
* When DNS configuration does not changed and old interface hold DNS
|
|
Packit Service |
0535c1 |
configuration is not included in `ifaces_desired_state`, preserve
|
|
Packit Service |
0535c1 |
the old DNS configure by removing DNS metadata from
|
|
Packit Service |
0535c1 |
`ifaces_desired_state`.
|
|
Packit Service |
0535c1 |
* When DNS configuration changed, include old interface which is holding
|
|
Packit Service |
0535c1 |
DNS configuration, so it's DNS configure could be removed.
|
|
Packit Service |
0535c1 |
"""
|
|
Packit Service |
0535c1 |
cur_dns_iface_names = get_dns_config_iface_names(
|
|
Packit Service |
0535c1 |
ipv4.acs_and_ip_profiles(context.client),
|
|
Packit Service |
0535c1 |
ipv6.acs_and_ip_profiles(context.client),
|
|
Packit Service |
0535c1 |
)
|
|
Packit Service |
0535c1 |
|
|
Packit Service |
0535c1 |
# Whether to mark interface as changed which is used for holding old DNS
|
|
Packit Service |
0535c1 |
# configurations
|
|
Packit Service |
0535c1 |
remove_existing_dns_config = False
|
|
Packit Service |
0535c1 |
# Whether to preserve old DNS config by DNS metadata to be removed from
|
|
Packit Service |
0535c1 |
# desired state
|
|
Packit Service |
0535c1 |
preserve_old_dns_config = False
|
|
Packit Service |
0535c1 |
if net_state.dns.config == net_state.dns.current_config:
|
|
Packit Service |
0535c1 |
for cur_dns_iface_name in cur_dns_iface_names:
|
|
Packit Service |
0535c1 |
iface = net_state.ifaces[cur_dns_iface_name]
|
|
Packit Service |
0535c1 |
if iface.is_changed or iface.is_desired:
|
|
Packit Service |
0535c1 |
remove_existing_dns_config = True
|
|
Packit Service |
0535c1 |
if not remove_existing_dns_config:
|
|
Packit Service |
0535c1 |
preserve_old_dns_config = True
|
|
Packit Service |
0535c1 |
else:
|
|
Packit Service |
0535c1 |
remove_existing_dns_config = True
|
|
Packit Service |
0535c1 |
|
|
Packit Service |
0535c1 |
if remove_existing_dns_config:
|
|
Packit Service |
0535c1 |
for cur_dns_iface_name in cur_dns_iface_names:
|
|
Packit Service |
0535c1 |
iface = net_state.ifaces[cur_dns_iface_name]
|
|
Packit Service |
0535c1 |
iface.mark_as_changed()
|
|
Packit Service |
0535c1 |
|
|
Packit Service |
0535c1 |
if preserve_old_dns_config:
|
|
Packit Service |
0535c1 |
for iface in net_state.ifaces.values():
|
|
Packit Service |
0535c1 |
if iface.is_changed or iface.is_desired:
|
|
Packit Service |
0535c1 |
iface.remove_dns_metadata()
|
|
Packit Service |
d5ca4e |
|
|
Packit Service |
d5ca4e |
|
|
Packit Service |
d5ca4e |
def _has_ovs_interface_desired_or_changed(net_state):
|
|
Packit Service |
d5ca4e |
for iface in net_state.ifaces.values():
|
|
Packit Service |
d5ca4e |
if iface.type in (
|
|
Packit Service |
d5ca4e |
InterfaceType.OVS_BRIDGE,
|
|
Packit Service |
d5ca4e |
InterfaceType.OVS_INTERFACE,
|
|
Packit Service |
d5ca4e |
InterfaceType.OVS_PORT,
|
|
Packit Service |
d5ca4e |
) and (iface.is_desired or iface.is_changed):
|
|
Packit Service |
d5ca4e |
return True
|
|
Packit Service |
bd40f4 |
|
|
Packit Service |
bd40f4 |
|
|
Packit Service |
bd40f4 |
def _mark_nm_external_subordinate_changed(context, net_state):
|
|
Packit Service |
bd40f4 |
"""
|
|
Packit Service |
bd40f4 |
When certain main interface contains subordinates is marked as
|
|
Packit Service |
bd40f4 |
connected(externally), it means its profile is memory only and will lost
|
|
Packit Service |
bd40f4 |
on next deactivation.
|
|
Packit Service |
bd40f4 |
For this case, we should mark the subordinate as changed.
|
|
Packit Service |
bd40f4 |
that subordinate should be marked as changed for NM to take over.
|
|
Packit Service |
bd40f4 |
"""
|
|
Packit Service |
bd40f4 |
for iface in net_state.ifaces.values():
|
|
Packit Service |
bd40f4 |
if iface.type in MASTER_IFACE_TYPES:
|
|
Packit Service |
bd40f4 |
for subordinate in iface.slaves:
|
|
Packit Service |
bd40f4 |
nmdev = context.get_nm_dev(subordinate)
|
|
Packit Service |
bd40f4 |
if nmdev:
|
|
Packit Service |
bd40f4 |
nm_ac = nmdev.get_active_connection()
|
|
Packit Service |
bd40f4 |
if (
|
|
Packit Service |
bd40f4 |
nm_ac
|
|
Packit Service |
bd40f4 |
and NM.ActivationStateFlags.EXTERNAL
|
|
Packit Service |
bd40f4 |
& nm_ac.get_state_flags()
|
|
Packit Service |
bd40f4 |
):
|
|
Packit Service |
bd40f4 |
subordinate_iface = net_state.ifaces[subordinate]
|
|
Packit Service |
bd40f4 |
subordinate_iface.mark_as_changed()
|