|
Packit Service |
0535c1 |
#
|
|
Packit Service |
0535c1 |
# Copyright (c) 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 |
0535c1 |
from libnmstate.error import NmstateValueError
|
|
Packit Service |
0535c1 |
from libnmstate.schema import LinuxBridge
|
|
Packit Service |
0535c1 |
|
|
Packit Service |
0535c1 |
from .bridge import BridgeIface
|
|
Packit Service |
0535c1 |
|
|
Packit Service |
0535c1 |
# The aging_time, forward_delay, hello_time, max_age options are multipled by
|
|
Packit Service |
0535c1 |
# 100(USER_HZ) when apply to kernel(via NM), so they are not impacted by this
|
|
Packit Service |
0535c1 |
# integer round up/down issue.
|
|
Packit Service |
0535c1 |
INTEGER_ROUNDED_OPTIONS = [
|
|
Packit Service |
0535c1 |
LinuxBridge.Options.MULTICAST_LAST_MEMBER_INTERVAL,
|
|
Packit Service |
0535c1 |
LinuxBridge.Options.MULTICAST_MEMBERSHIP_INTERVAL,
|
|
Packit Service |
0535c1 |
LinuxBridge.Options.MULTICAST_QUERIER_INTERVAL,
|
|
Packit Service |
0535c1 |
LinuxBridge.Options.MULTICAST_QUERY_RESPONSE_INTERVAL,
|
|
Packit Service |
0535c1 |
LinuxBridge.Options.MULTICAST_STARTUP_QUERY_INTERVAL,
|
|
Packit Service |
0535c1 |
]
|
|
Packit Service |
0535c1 |
|
|
Packit Service |
0535c1 |
|
|
Packit Service |
0535c1 |
class LinuxBridgeIface(BridgeIface):
|
|
Packit Service |
0535c1 |
@property
|
|
Packit Service |
0535c1 |
def _options(self):
|
|
Packit Service |
0535c1 |
return self.raw.get(LinuxBridge.CONFIG_SUBTREE, {}).get(
|
|
Packit Service |
0535c1 |
LinuxBridge.OPTIONS_SUBTREE, {}
|
|
Packit Service |
0535c1 |
)
|
|
Packit Service |
0535c1 |
|
|
Packit Service |
0535c1 |
def pre_edit_validation_and_cleanup(self):
|
|
Packit Service |
0535c1 |
self._validate()
|
|
Packit Service |
0535c1 |
self._fix_vlan_filtering_mode()
|
|
Packit Service |
0535c1 |
super().pre_edit_validation_and_cleanup()
|
|
Packit Service |
0535c1 |
|
|
Packit Service |
0535c1 |
@property
|
|
Packit Service |
0535c1 |
def slaves(self):
|
|
Packit Service |
0535c1 |
return [p[LinuxBridge.Port.NAME] for p in self.port_configs]
|
|
Packit Service |
0535c1 |
|
|
Packit Service |
0535c1 |
def _validate(self):
|
|
Packit Service |
0535c1 |
self._validate_vlan_filtering_trunk_tags()
|
|
Packit Service |
0535c1 |
self._validate_vlan_filtering_tag()
|
|
Packit Service |
0535c1 |
self._validate_vlan_filtering_enable_native()
|
|
Packit Service |
0535c1 |
|
|
Packit Service |
0535c1 |
def _validate_vlan_filtering_trunk_tags(self):
|
|
Packit Service |
0535c1 |
for port_config in self.original_dict.get(
|
|
Packit Service |
0535c1 |
LinuxBridge.CONFIG_SUBTREE, {}
|
|
Packit Service |
0535c1 |
).get(LinuxBridge.PORT_SUBTREE, []):
|
|
Packit Service |
0535c1 |
port_vlan_state = port_config.get(
|
|
Packit Service |
0535c1 |
LinuxBridge.Port.VLAN_SUBTREE, {}
|
|
Packit Service |
0535c1 |
)
|
|
Packit Service |
0535c1 |
vlan_mode = port_vlan_state.get(LinuxBridge.Port.Vlan.MODE)
|
|
Packit Service |
0535c1 |
trunk_tags = port_vlan_state.get(
|
|
Packit Service |
0535c1 |
LinuxBridge.Port.Vlan.TRUNK_TAGS, []
|
|
Packit Service |
0535c1 |
)
|
|
Packit Service |
0535c1 |
|
|
Packit Service |
0535c1 |
if vlan_mode == LinuxBridge.Port.Vlan.Mode.ACCESS:
|
|
Packit Service |
0535c1 |
if trunk_tags:
|
|
Packit Service |
0535c1 |
raise NmstateValueError(
|
|
Packit Service |
0535c1 |
"Access port cannot have trunk tags"
|
|
Packit Service |
0535c1 |
)
|
|
Packit Service |
0535c1 |
elif port_vlan_state:
|
|
Packit Service |
0535c1 |
if not trunk_tags:
|
|
Packit Service |
0535c1 |
raise NmstateValueError(
|
|
Packit Service |
0535c1 |
"A trunk port needs to specify trunk tags"
|
|
Packit Service |
0535c1 |
)
|
|
Packit Service |
0535c1 |
for trunk_tag in trunk_tags:
|
|
Packit Service |
0535c1 |
_assert_vlan_filtering_trunk_tag(trunk_tag)
|
|
Packit Service |
0535c1 |
|
|
Packit Service |
0535c1 |
def _validate_vlan_filtering_tag(self):
|
|
Packit Service |
0535c1 |
"""
|
|
Packit Service |
0535c1 |
The "tag" is valid in access mode or tunk mode with
|
|
Packit Service |
0535c1 |
"enable-native:True".
|
|
Packit Service |
0535c1 |
"""
|
|
Packit Service |
0535c1 |
for port_config in self.original_dict.get(
|
|
Packit Service |
0535c1 |
LinuxBridge.CONFIG_SUBTREE, {}
|
|
Packit Service |
0535c1 |
).get(LinuxBridge.PORT_SUBTREE, []):
|
|
Packit Service |
0535c1 |
vlan_config = _get_port_vlan_config(port_config)
|
|
Packit Service |
0535c1 |
if (
|
|
Packit Service |
0535c1 |
vlan_config.get(LinuxBridge.Port.Vlan.TAG)
|
|
Packit Service |
0535c1 |
and _vlan_is_trunk_mode(vlan_config)
|
|
Packit Service |
0535c1 |
and not _vlan_is_enable_native(vlan_config)
|
|
Packit Service |
0535c1 |
):
|
|
Packit Service |
0535c1 |
raise NmstateValueError(
|
|
Packit Service |
0535c1 |
"Tag cannot be use in trunk mode without enable-native"
|
|
Packit Service |
0535c1 |
)
|
|
Packit Service |
0535c1 |
|
|
Packit Service |
0535c1 |
def _validate_vlan_filtering_enable_native(self):
|
|
Packit Service |
0535c1 |
for port_config in self.original_dict.get(
|
|
Packit Service |
0535c1 |
LinuxBridge.CONFIG_SUBTREE, {}
|
|
Packit Service |
0535c1 |
).get(LinuxBridge.PORT_SUBTREE, []):
|
|
Packit Service |
0535c1 |
vlan_config = _get_port_vlan_config(port_config)
|
|
Packit Service |
0535c1 |
if _vlan_is_access_mode(vlan_config) and _vlan_is_enable_native(
|
|
Packit Service |
0535c1 |
vlan_config
|
|
Packit Service |
0535c1 |
):
|
|
Packit Service |
0535c1 |
raise NmstateValueError(
|
|
Packit Service |
0535c1 |
"enable-native cannot be set in access mode"
|
|
Packit Service |
0535c1 |
)
|
|
Packit Service |
0535c1 |
|
|
Packit Service |
0535c1 |
def _fix_vlan_filtering_mode(self):
|
|
Packit Service |
0535c1 |
for port_config in self.port_configs:
|
|
Packit Service |
0535c1 |
_vlan_config_clean_up(_get_port_vlan_config(port_config))
|
|
Packit Service |
0535c1 |
|
|
Packit Service |
0535c1 |
def gen_metadata(self, ifaces):
|
|
Packit Service |
0535c1 |
super().gen_metadata(ifaces)
|
|
Packit Service |
0535c1 |
if not self.is_absent:
|
|
Packit Service |
0535c1 |
for port_config in self.port_configs:
|
|
Packit Service |
0535c1 |
ifaces[port_config[LinuxBridge.Port.NAME]].update(
|
|
Packit Service |
0535c1 |
{BridgeIface.BRPORT_OPTIONS_METADATA: port_config}
|
|
Packit Service |
0535c1 |
)
|
|
Packit Service |
0535c1 |
|
|
Packit Service |
0535c1 |
def remove_slave(self, slave_name):
|
|
Packit Service |
0535c1 |
if self._bridge_config:
|
|
Packit Service |
0535c1 |
self.raw[LinuxBridge.CONFIG_SUBTREE][LinuxBridge.PORT_SUBTREE] = [
|
|
Packit Service |
0535c1 |
port_config
|
|
Packit Service |
0535c1 |
for port_config in self.port_configs
|
|
Packit Service |
0535c1 |
if port_config[LinuxBridge.Port.NAME] != slave_name
|
|
Packit Service |
0535c1 |
]
|
|
Packit Service |
0535c1 |
self.sort_slaves()
|
|
Packit Service |
0535c1 |
|
|
Packit Service |
0535c1 |
@staticmethod
|
|
Packit Service |
0535c1 |
def is_integer_rounded(iface_state, current_iface_state):
|
|
Packit Service |
0535c1 |
for key, value in iface_state._options.items():
|
|
Packit Service |
0535c1 |
if key in INTEGER_ROUNDED_OPTIONS:
|
|
Packit Service |
0535c1 |
try:
|
|
Packit Service |
0535c1 |
value = int(value)
|
|
Packit Service |
0535c1 |
cur_value = int(current_iface_state._options.get(key))
|
|
Packit Service |
0535c1 |
except (TypeError, ValueError):
|
|
Packit Service |
0535c1 |
continue
|
|
Packit Service |
0535c1 |
# With 250 HZ and 100 USER_HZ, every 8,000,000 will have 1
|
|
Packit Service |
0535c1 |
# deviation, caused by:
|
|
Packit Service |
0535c1 |
# * kernel set the value using clock_t_to_jiffies():
|
|
Packit Service |
0535c1 |
# jiffies = int(clock * 100 / 250)
|
|
Packit Service |
0535c1 |
# * kernel showing the value using jiffies_to_clock_t():
|
|
Packit Service |
0535c1 |
# clock = int(int(jiffies * ( (10 ** 9 + 250/2) / 250)
|
|
Packit Service |
0535c1 |
# / 10 ** 9 * 100)
|
|
Packit Service |
0535c1 |
#
|
|
Packit Service |
0535c1 |
# The number 8,000,000 is found by exhaustion.
|
|
Packit Service |
0535c1 |
# There is no good way to detect kernel HZ in user space. Hence
|
|
Packit Service |
0535c1 |
# we check whether certain value is rounded.
|
|
Packit Service |
0535c1 |
if cur_value != value:
|
|
Packit Service |
0535c1 |
if value >= 8 * (10 ** 6):
|
|
Packit Service |
0535c1 |
if abs(value - cur_value) <= int(
|
|
Packit Service |
0535c1 |
value / 8 * (10 ** 6)
|
|
Packit Service |
0535c1 |
):
|
|
Packit Service |
0535c1 |
return key, value, cur_value
|
|
Packit Service |
0535c1 |
else:
|
|
Packit Service |
0535c1 |
if abs(value - cur_value) == 1:
|
|
Packit Service |
0535c1 |
return key, value, cur_value
|
|
Packit Service |
0535c1 |
|
|
Packit Service |
0535c1 |
return None, None, None
|
|
Packit Service |
0535c1 |
|
|
Packit Service |
0535c1 |
|
|
Packit Service |
0535c1 |
def _assert_vlan_filtering_trunk_tag(trunk_tag_state):
|
|
Packit Service |
0535c1 |
vlan_id = trunk_tag_state.get(LinuxBridge.Port.Vlan.TrunkTags.ID)
|
|
Packit Service |
0535c1 |
vlan_id_range = trunk_tag_state.get(
|
|
Packit Service |
0535c1 |
LinuxBridge.Port.Vlan.TrunkTags.ID_RANGE
|
|
Packit Service |
0535c1 |
)
|
|
Packit Service |
0535c1 |
|
|
Packit Service |
0535c1 |
if vlan_id and vlan_id_range:
|
|
Packit Service |
0535c1 |
raise NmstateValueError(
|
|
Packit Service |
0535c1 |
"Trunk port cannot be configured by both id and range: {}".format(
|
|
Packit Service |
0535c1 |
trunk_tag_state
|
|
Packit Service |
0535c1 |
)
|
|
Packit Service |
0535c1 |
)
|
|
Packit Service |
0535c1 |
elif vlan_id_range:
|
|
Packit Service |
0535c1 |
if not (
|
|
Packit Service |
0535c1 |
{
|
|
Packit Service |
0535c1 |
LinuxBridge.Port.Vlan.TrunkTags.MIN_RANGE,
|
|
Packit Service |
0535c1 |
LinuxBridge.Port.Vlan.TrunkTags.MAX_RANGE,
|
|
Packit Service |
0535c1 |
}
|
|
Packit Service |
0535c1 |
<= set(vlan_id_range)
|
|
Packit Service |
0535c1 |
):
|
|
Packit Service |
0535c1 |
raise NmstateValueError(
|
|
Packit Service |
0535c1 |
"Trunk port range requires min / max keys: {}".format(
|
|
Packit Service |
0535c1 |
vlan_id_range
|
|
Packit Service |
0535c1 |
)
|
|
Packit Service |
0535c1 |
)
|
|
Packit Service |
0535c1 |
|
|
Packit Service |
0535c1 |
|
|
Packit Service |
0535c1 |
def _get_port_vlan_config(port_config):
|
|
Packit Service |
0535c1 |
return port_config.get(LinuxBridge.Port.VLAN_SUBTREE, {})
|
|
Packit Service |
0535c1 |
|
|
Packit Service |
0535c1 |
|
|
Packit Service |
0535c1 |
# TODO: Group them into class _LinuxBridgePort
|
|
Packit Service |
0535c1 |
def _vlan_is_access_mode(vlan_config):
|
|
Packit Service |
0535c1 |
return (
|
|
Packit Service |
0535c1 |
vlan_config.get(LinuxBridge.Port.Vlan.MODE)
|
|
Packit Service |
0535c1 |
== LinuxBridge.Port.Vlan.Mode.ACCESS
|
|
Packit Service |
0535c1 |
)
|
|
Packit Service |
0535c1 |
|
|
Packit Service |
0535c1 |
|
|
Packit Service |
0535c1 |
def _vlan_is_trunk_mode(vlan_config):
|
|
Packit Service |
0535c1 |
return (
|
|
Packit Service |
0535c1 |
vlan_config.get(LinuxBridge.Port.Vlan.MODE)
|
|
Packit Service |
0535c1 |
== LinuxBridge.Port.Vlan.Mode.TRUNK
|
|
Packit Service |
0535c1 |
)
|
|
Packit Service |
0535c1 |
|
|
Packit Service |
0535c1 |
|
|
Packit Service |
0535c1 |
def _vlan_is_enable_native(vlan_config):
|
|
Packit Service |
0535c1 |
return vlan_config.get(LinuxBridge.Port.Vlan.ENABLE_NATIVE) is True
|
|
Packit Service |
0535c1 |
|
|
Packit Service |
0535c1 |
|
|
Packit Service |
0535c1 |
def _vlan_config_clean_up(vlan_config):
|
|
Packit Service |
0535c1 |
_vlan_remove_enable_native_if_access_mode(vlan_config)
|
|
Packit Service |
0535c1 |
_vlan_remove_tag_if_trunk_mode_without_enable_native(vlan_config)
|
|
Packit Service |
0535c1 |
_vlan_remove_trunk_tag_if_access_mode(vlan_config)
|
|
Packit Service |
0535c1 |
|
|
Packit Service |
0535c1 |
|
|
Packit Service |
0535c1 |
def _vlan_remove_enable_native_if_access_mode(vlan_config):
|
|
Packit Service |
0535c1 |
if _vlan_is_access_mode(vlan_config):
|
|
Packit Service |
0535c1 |
vlan_config.pop(LinuxBridge.Port.Vlan.ENABLE_NATIVE, None)
|
|
Packit Service |
0535c1 |
|
|
Packit Service |
0535c1 |
|
|
Packit Service |
0535c1 |
def _vlan_remove_tag_if_trunk_mode_without_enable_native(vlan_config):
|
|
Packit Service |
0535c1 |
if _vlan_is_trunk_mode(vlan_config) and not _vlan_is_enable_native(
|
|
Packit Service |
0535c1 |
vlan_config
|
|
Packit Service |
0535c1 |
):
|
|
Packit Service |
0535c1 |
vlan_config.pop(LinuxBridge.Port.Vlan.TAG, None)
|
|
Packit Service |
0535c1 |
|
|
Packit Service |
0535c1 |
|
|
Packit Service |
0535c1 |
def _vlan_remove_trunk_tag_if_access_mode(vlan_config):
|
|
Packit Service |
0535c1 |
if _vlan_is_access_mode(vlan_config):
|
|
Packit Service |
0535c1 |
vlan_config.pop(LinuxBridge.Port.Vlan.TRUNK_TAGS, None)
|