ovs: Support interface level other_config
(api break)
The Rust API changed: The `OvsBridgeBondConfig.other_config: Option<HashMap>` changed to `OvsBridgeBondConfig.ovs_db: Option<OvsDbIfaceConfig>` The YAML API changed: The `other_config` under ovs bond should be stored under `ovs-db` section, please check follow up example for detail. Considering the interface level `other_config` never works in previous release, this is API change is acceptable. Example on `other_config` of OVS bridge: ```yml interfaces: - name: br0 type: ovs-bridge state: up ovs-db: other_config: in-band-queue: '12' bridge: port: - name: eth1 - name: ovs0 ``` Example on `other_config` of OVS Bond: ```yml - name: br0 type: ovs-bridge state: up bridge: port: - name: bond1 link-aggregation: mode: balance-slb ovs-db: other_config: bond-miimon-interval: "100" port: - name: eth2 - name: eth1 ``` Example on `other_config` of OVS interface: ```yml - name: eth1 type: ethernet state: up ovs-db: other_config: emc-insert-inv-prob: 90 ``` We hide interface level `ovs-db` section if it is empty. This help us supporting older NetworkManager where `other_config` is not supported yet. User will get error when they apply above yaml on old NetworkManager: NmstateError: DependencyError: Please upgrade NetworkManager for specified interface type: Connection(InvalidSetting):ovs-other-config: unknown setting name Integration test case included. Signed-off-by: Gris Ge <fge@redhat.com>
This commit is contained in:
parent
e2b3ea72b9
commit
8ec213b8cb
@ -661,6 +661,9 @@ impl Interface {
|
||||
if let Interface::LinuxBridge(iface) = self {
|
||||
iface.sanitize_for_verify()
|
||||
}
|
||||
if let Interface::OvsBridge(iface) = self {
|
||||
iface.sanitize_for_verify()
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn parent(&self) -> Option<&str> {
|
||||
|
@ -260,6 +260,10 @@ impl BaseInterface {
|
||||
if let Some(ipv6_conf) = self.ipv6.as_mut() {
|
||||
ipv6_conf.sanitize_for_verify();
|
||||
}
|
||||
// ovsdb None equal to empty
|
||||
if self.ovsdb.is_none() {
|
||||
self.ovsdb = Some(OvsDbIfaceConfig::empty());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1,13 +1,12 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use std::collections::HashMap;
|
||||
use std::convert::TryFrom;
|
||||
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
use crate::{
|
||||
BaseInterface, BridgePortVlanConfig, ErrorKind, Interface, InterfaceType,
|
||||
MergedInterface, NmstateError,
|
||||
MergedInterface, NmstateError, OvsDbIfaceConfig,
|
||||
};
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
|
||||
@ -138,6 +137,20 @@ impl OvsBridgeInterface {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub(crate) fn sanitize_for_verify(&mut self) {
|
||||
if let Some(port_confs) = self
|
||||
.bridge
|
||||
.as_mut()
|
||||
.and_then(|br_conf| br_conf.ports.as_mut())
|
||||
{
|
||||
for port_conf in port_confs {
|
||||
if let Some(bond_conf) = port_conf.bond.as_mut() {
|
||||
bond_conf.sanitize_for_verify();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Only support remove non-bonding port or the bond itself as bond require
|
||||
// two ports, removal any of them will trigger error.
|
||||
pub(crate) fn remove_port(&mut self, port_name: &str) {
|
||||
@ -499,11 +512,13 @@ pub struct OvsBridgeBondConfig {
|
||||
)]
|
||||
/// Deserialize and serialize from/to `bond-updelay`.
|
||||
pub bond_updelay: Option<u32>,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
#[serde(skip_serializing_if = "Option::is_none", rename = "ovs-db")]
|
||||
/// OpenvSwitch specific `other_config` for OVS bond. Please refer to
|
||||
/// manpage `ovs-vswitchd.conf.db(5)` for more detail.
|
||||
/// Set to None for remove specific entry.
|
||||
pub other_config: Option<HashMap<String, Option<String>>>,
|
||||
/// When setting to None, nmstate will try to preserve current
|
||||
/// `other_config`, otherwise, nmstate will override all `other_config`
|
||||
/// for specified OVS bond.
|
||||
pub ovsdb: Option<OvsDbIfaceConfig>,
|
||||
}
|
||||
|
||||
impl OvsBridgeBondConfig {
|
||||
@ -526,6 +541,13 @@ impl OvsBridgeBondConfig {
|
||||
bond_ports.sort_unstable_by_key(|p| p.name.clone())
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn sanitize_for_verify(&mut self) {
|
||||
// None ovsbd equal to empty
|
||||
if self.ovsdb.is_none() {
|
||||
self.ovsdb = Some(OvsDbIfaceConfig::empty());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, Default)]
|
||||
|
@ -19,7 +19,8 @@ use super::super::{
|
||||
connection::mac_vlan::NmSettingMacVlan,
|
||||
connection::ovs::{
|
||||
NmSettingOvsBridge, NmSettingOvsDpdk, NmSettingOvsExtIds,
|
||||
NmSettingOvsIface, NmSettingOvsPatch, NmSettingOvsPort,
|
||||
NmSettingOvsIface, NmSettingOvsOtherConfig, NmSettingOvsPatch,
|
||||
NmSettingOvsPort,
|
||||
},
|
||||
connection::sriov::NmSettingSriov,
|
||||
connection::user::NmSettingUser,
|
||||
@ -86,6 +87,7 @@ pub struct NmConnection {
|
||||
pub ovs_port: Option<NmSettingOvsPort>,
|
||||
pub ovs_iface: Option<NmSettingOvsIface>,
|
||||
pub ovs_ext_ids: Option<NmSettingOvsExtIds>,
|
||||
pub ovs_other_config: Option<NmSettingOvsOtherConfig>,
|
||||
pub ovs_patch: Option<NmSettingOvsPatch>,
|
||||
pub ovs_dpdk: Option<NmSettingOvsDpdk>,
|
||||
pub wired: Option<NmSettingWired>,
|
||||
@ -151,6 +153,11 @@ impl TryFrom<NmConnectionDbusOwnedValue> for NmConnection {
|
||||
"ovs-external-ids",
|
||||
NmSettingOvsExtIds::try_from
|
||||
)?,
|
||||
ovs_other_config: _from_map!(
|
||||
v,
|
||||
"ovs-other-config",
|
||||
NmSettingOvsOtherConfig::try_from
|
||||
)?,
|
||||
ovs_patch: _from_map!(v, "ovs-patch", NmSettingOvsPatch::try_from)?,
|
||||
ovs_dpdk: _from_map!(v, "ovs-dpdk", NmSettingOvsDpdk::try_from)?,
|
||||
wired: _from_map!(v, "802-3-ethernet", NmSettingWired::try_from)?,
|
||||
@ -229,6 +236,9 @@ impl NmConnection {
|
||||
if let Some(ovs_ext_ids) = &self.ovs_ext_ids {
|
||||
ret.insert("ovs-external-ids", ovs_ext_ids.to_value()?);
|
||||
}
|
||||
if let Some(ovs_other_config) = &self.ovs_other_config {
|
||||
ret.insert("ovs-other-config", ovs_other_config.to_value()?);
|
||||
}
|
||||
if let Some(ovs_patch_set) = &self.ovs_patch {
|
||||
ret.insert("ovs-patch", ovs_patch_set.to_value()?);
|
||||
}
|
||||
|
@ -52,7 +52,8 @@ pub use self::loopback::NmSettingLoopback;
|
||||
pub use self::mac_vlan::NmSettingMacVlan;
|
||||
pub use self::ovs::{
|
||||
NmSettingOvsBridge, NmSettingOvsDpdk, NmSettingOvsExtIds,
|
||||
NmSettingOvsIface, NmSettingOvsPatch, NmSettingOvsPort,
|
||||
NmSettingOvsIface, NmSettingOvsOtherConfig, NmSettingOvsPatch,
|
||||
NmSettingOvsPort,
|
||||
};
|
||||
pub use self::route::NmIpRoute;
|
||||
pub use self::route_rule::{NmIpRouteRule, NmIpRouteRuleAction};
|
||||
|
@ -210,6 +210,45 @@ impl ToDbusValue for NmSettingOvsExtIds {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Default, Deserialize)]
|
||||
#[serde(try_from = "DbusDictionary")]
|
||||
#[non_exhaustive]
|
||||
pub struct NmSettingOvsOtherConfig {
|
||||
pub data: Option<HashMap<String, String>>,
|
||||
_other: HashMap<String, zvariant::OwnedValue>,
|
||||
}
|
||||
|
||||
impl TryFrom<DbusDictionary> for NmSettingOvsOtherConfig {
|
||||
type Error = NmError;
|
||||
fn try_from(mut v: DbusDictionary) -> Result<Self, Self::Error> {
|
||||
Ok(Self {
|
||||
data: _from_map!(v, "data", <HashMap<String, String>>::try_from)?,
|
||||
_other: v,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl ToDbusValue for NmSettingOvsOtherConfig {
|
||||
fn to_value(&self) -> Result<HashMap<&str, zvariant::Value>, NmError> {
|
||||
let mut ret = HashMap::new();
|
||||
if let Some(v) = &self.data {
|
||||
let mut dict_value = zvariant::Dict::new(
|
||||
zvariant::Signature::from_str_unchecked("s"),
|
||||
zvariant::Signature::from_str_unchecked("s"),
|
||||
);
|
||||
for (k, v) in v.iter() {
|
||||
dict_value
|
||||
.append(zvariant::Value::new(k), zvariant::Value::new(v))?;
|
||||
}
|
||||
ret.insert("data", zvariant::Value::Dict(dict_value));
|
||||
}
|
||||
ret.extend(self._other.iter().map(|(key, value)| {
|
||||
(key.as_str(), zvariant::Value::from(value.clone()))
|
||||
}));
|
||||
Ok(ret)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Default, Deserialize)]
|
||||
#[serde(try_from = "DbusDictionary")]
|
||||
#[non_exhaustive]
|
||||
|
@ -82,6 +82,9 @@ impl NmConnection {
|
||||
if let Some(ovs_eids) = &self.ovs_ext_ids {
|
||||
sections.push(("ovs-external-ids", ovs_eids.to_keyfile()?));
|
||||
}
|
||||
if let Some(ovs_other_cfgs) = &self.ovs_other_config {
|
||||
sections.push(("ovs-other-config", ovs_other_cfgs.to_keyfile()?));
|
||||
}
|
||||
|
||||
keyfile_sections_to_string(§ions)
|
||||
}
|
||||
|
@ -4,7 +4,8 @@ use std::collections::HashMap;
|
||||
|
||||
use super::super::{
|
||||
NmError, NmSettingOvsBridge, NmSettingOvsDpdk, NmSettingOvsExtIds,
|
||||
NmSettingOvsIface, NmSettingOvsPatch, NmSettingOvsPort, ToKeyfile,
|
||||
NmSettingOvsIface, NmSettingOvsOtherConfig, NmSettingOvsPatch,
|
||||
NmSettingOvsPort, ToKeyfile,
|
||||
};
|
||||
|
||||
impl ToKeyfile for NmSettingOvsBridge {}
|
||||
@ -24,3 +25,15 @@ impl ToKeyfile for NmSettingOvsExtIds {
|
||||
Ok(ret)
|
||||
}
|
||||
}
|
||||
|
||||
impl ToKeyfile for NmSettingOvsOtherConfig {
|
||||
fn to_keyfile(&self) -> Result<HashMap<String, zvariant::Value>, NmError> {
|
||||
let mut ret = HashMap::new();
|
||||
if let Some(data) = self.data.as_ref() {
|
||||
for (k, v) in data {
|
||||
ret.insert(format!("data.{k}"), zvariant::Value::new(v));
|
||||
}
|
||||
}
|
||||
Ok(ret)
|
||||
}
|
||||
}
|
||||
|
@ -29,10 +29,10 @@ pub use self::connection::{
|
||||
NmSettingBridgeVlanRange, NmSettingConnection, NmSettingEthtool,
|
||||
NmSettingInfiniBand, NmSettingIp, NmSettingIpMethod, NmSettingLoopback,
|
||||
NmSettingMacVlan, NmSettingOvsBridge, NmSettingOvsDpdk, NmSettingOvsExtIds,
|
||||
NmSettingOvsIface, NmSettingOvsPatch, NmSettingOvsPort, NmSettingSriov,
|
||||
NmSettingSriovVf, NmSettingSriovVfVlan, NmSettingUser, NmSettingVeth,
|
||||
NmSettingVlan, NmSettingVrf, NmSettingVxlan, NmSettingWired,
|
||||
NmSettingsConnectionFlag, NmVlanProtocol,
|
||||
NmSettingOvsIface, NmSettingOvsOtherConfig, NmSettingOvsPatch,
|
||||
NmSettingOvsPort, NmSettingSriov, NmSettingSriovVf, NmSettingSriovVfVlan,
|
||||
NmSettingUser, NmSettingVeth, NmSettingVlan, NmSettingVrf, NmSettingVxlan,
|
||||
NmSettingWired, NmSettingsConnectionFlag, NmVlanProtocol,
|
||||
};
|
||||
#[cfg(feature = "query_apply")]
|
||||
pub use self::device::{NmDevice, NmDeviceState, NmDeviceStateReason};
|
||||
|
@ -14,8 +14,8 @@ use super::{
|
||||
loopback::gen_nm_loopback_setting,
|
||||
mptcp::apply_mptcp_conf,
|
||||
ovs::{
|
||||
create_ovs_port_nm_conn, gen_nm_ovs_br_setting,
|
||||
gen_nm_ovs_ext_ids_setting, gen_nm_ovs_iface_setting,
|
||||
create_ovs_port_nm_conn, gen_nm_iface_ovs_db_setting,
|
||||
gen_nm_ovs_br_setting, gen_nm_ovs_iface_setting,
|
||||
},
|
||||
sriov::gen_nm_sriov_setting,
|
||||
user::gen_nm_user_setting,
|
||||
@ -124,7 +124,7 @@ pub(crate) fn iface_to_nm_connections(
|
||||
{
|
||||
gen_nm_wired_setting(iface, &mut nm_conn);
|
||||
}
|
||||
gen_nm_ovs_ext_ids_setting(iface, &mut nm_conn);
|
||||
gen_nm_iface_ovs_db_setting(iface, &mut nm_conn);
|
||||
gen_nm_802_1x_setting(iface, &mut nm_conn);
|
||||
gen_nm_user_setting(iface, &mut nm_conn);
|
||||
gen_ethtool_setting(iface, &mut nm_conn)?;
|
||||
|
@ -5,14 +5,14 @@ use std::iter::FromIterator;
|
||||
|
||||
use super::super::nm_dbus::{
|
||||
NmConnection, NmRange, NmSettingOvsDpdk, NmSettingOvsExtIds,
|
||||
NmSettingOvsIface, NmSettingOvsPatch,
|
||||
NmSettingOvsIface, NmSettingOvsOtherConfig, NmSettingOvsPatch,
|
||||
};
|
||||
use super::super::settings::connection::gen_nm_conn_setting;
|
||||
|
||||
use crate::{
|
||||
BaseInterface, BridgePortTunkTag, Interface, InterfaceType, NmstateError,
|
||||
OvsBridgeBondMode, OvsBridgeInterface, OvsBridgePortConfig, OvsInterface,
|
||||
UnknownInterface,
|
||||
OvsBridgeBondMode, OvsBridgeInterface, OvsBridgePortConfig,
|
||||
OvsDbIfaceConfig, OvsInterface, UnknownInterface,
|
||||
};
|
||||
|
||||
pub(crate) fn create_ovs_port_nm_conn(
|
||||
@ -58,6 +58,10 @@ pub(crate) fn create_ovs_port_nm_conn(
|
||||
if let Some(bond_updelay) = bond_conf.bond_updelay {
|
||||
nm_ovs_port_set.up_delay = Some(bond_updelay);
|
||||
}
|
||||
|
||||
if let Some(ovsdb_conf) = bond_conf.ovsdb.as_ref() {
|
||||
apply_iface_ovsdb_conf(ovsdb_conf, &mut nm_conn);
|
||||
}
|
||||
}
|
||||
if let Some(vlan_conf) = port_conf.vlan.as_ref() {
|
||||
if let Some(tag) = vlan_conf.tag {
|
||||
@ -168,21 +172,42 @@ pub(crate) fn gen_nm_ovs_iface_setting(
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn gen_nm_ovs_ext_ids_setting(
|
||||
fn apply_iface_ovsdb_conf(conf: &OvsDbIfaceConfig, nm_conn: &mut NmConnection) {
|
||||
let external_ids = conf.get_external_ids();
|
||||
let other_config = conf.get_other_config();
|
||||
|
||||
if !(external_ids.is_empty() && nm_conn.ovs_ext_ids.is_none()) {
|
||||
let mut nm_setting = NmSettingOvsExtIds::default();
|
||||
nm_setting.data = Some(HashMap::from_iter(
|
||||
external_ids
|
||||
.iter()
|
||||
.map(|(k, v)| (k.to_string(), v.to_string())),
|
||||
));
|
||||
nm_conn.ovs_ext_ids = Some(nm_setting);
|
||||
}
|
||||
|
||||
// Do not create new setting for empty other_config unless pre-exist.
|
||||
if !(other_config.is_empty() && nm_conn.ovs_other_config.is_none()) {
|
||||
let mut nm_setting = NmSettingOvsOtherConfig::default();
|
||||
nm_setting.data = Some(HashMap::from_iter(
|
||||
other_config
|
||||
.iter()
|
||||
.map(|(k, v)| (k.to_string(), v.to_string())),
|
||||
));
|
||||
nm_conn.ovs_other_config = Some(nm_setting);
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn gen_nm_iface_ovs_db_setting(
|
||||
iface: &Interface,
|
||||
nm_conn: &mut NmConnection,
|
||||
) {
|
||||
if iface.iface_type() != InterfaceType::OvsBridge
|
||||
&& iface.base_iface().controller_type != Some(InterfaceType::OvsBridge)
|
||||
{
|
||||
nm_conn.ovs_other_config = None;
|
||||
nm_conn.ovs_ext_ids = None;
|
||||
} else if let Some(conf) = iface.base_iface().ovsdb.as_ref() {
|
||||
let mut nm_setting = NmSettingOvsExtIds::default();
|
||||
nm_setting.data = Some(HashMap::from_iter(
|
||||
conf.get_external_ids()
|
||||
.iter()
|
||||
.map(|(k, v)| (k.to_string(), v.to_string())),
|
||||
));
|
||||
nm_conn.ovs_ext_ids = Some(nm_setting);
|
||||
apply_iface_ovsdb_conf(conf, nm_conn);
|
||||
}
|
||||
}
|
||||
|
@ -56,10 +56,21 @@ pub struct OvsDbIfaceConfig {
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub external_ids: Option<HashMap<String, Option<String>>>,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
/// OpenvSwitch specific `other_config`. Please refer to
|
||||
/// manpage `ovs-vswitchd.conf.db(5)` for more detail.
|
||||
/// When setting to None, nmstate will try to preserve current
|
||||
/// `other_config`, otherwise, nmstate will override all `other_config`
|
||||
/// for specified interface.
|
||||
pub other_config: Option<HashMap<String, Option<String>>>,
|
||||
}
|
||||
|
||||
impl OvsDbIfaceConfig {
|
||||
pub(crate) fn empty() -> Self {
|
||||
Self {
|
||||
external_ids: Some(HashMap::new()),
|
||||
other_config: Some(HashMap::new()),
|
||||
}
|
||||
}
|
||||
pub(crate) fn get_external_ids(&self) -> HashMap<&str, &str> {
|
||||
let mut ret = HashMap::new();
|
||||
if let Some(eids) = self.external_ids.as_ref() {
|
||||
@ -71,6 +82,18 @@ impl OvsDbIfaceConfig {
|
||||
}
|
||||
ret
|
||||
}
|
||||
|
||||
pub(crate) fn get_other_config(&self) -> HashMap<&str, &str> {
|
||||
let mut ret = HashMap::new();
|
||||
if let Some(cfgs) = self.other_config.as_ref() {
|
||||
for (k, v) in cfgs {
|
||||
if let Some(v) = v {
|
||||
ret.insert(k.as_str(), v.as_str());
|
||||
}
|
||||
}
|
||||
}
|
||||
ret
|
||||
}
|
||||
}
|
||||
|
||||
impl<'de> Deserialize<'de> for OvsDbIfaceConfig {
|
||||
|
@ -180,6 +180,13 @@ fn parse_ovs_bond_conf(
|
||||
if v == 0 { None } else { Some(v as u32) };
|
||||
}
|
||||
}
|
||||
let external_ids = HashMap::from_iter(
|
||||
ovsdb_port
|
||||
.external_ids
|
||||
.clone()
|
||||
.drain()
|
||||
.map(|(k, v)| (k, Some(v))),
|
||||
);
|
||||
|
||||
let other_config = HashMap::from_iter(
|
||||
ovsdb_port
|
||||
@ -188,7 +195,12 @@ fn parse_ovs_bond_conf(
|
||||
.drain()
|
||||
.map(|(k, v)| (k, Some(v))),
|
||||
);
|
||||
bond_conf.other_config = Some(other_config);
|
||||
if !external_ids.is_empty() || !other_config.is_empty() {
|
||||
bond_conf.ovsdb = Some(OvsDbIfaceConfig {
|
||||
external_ids: Some(external_ids),
|
||||
other_config: Some(other_config),
|
||||
});
|
||||
}
|
||||
|
||||
bond_conf.ports = Some(bond_port_confs);
|
||||
bond_conf
|
||||
@ -381,10 +393,12 @@ fn ovsdb_iface_to_nmstate(
|
||||
.drain()
|
||||
.map(|(k, v)| (k, Some(v))),
|
||||
);
|
||||
iface.base_iface_mut().ovsdb = Some(OvsDbIfaceConfig {
|
||||
external_ids: Some(external_ids),
|
||||
other_config: Some(other_config),
|
||||
});
|
||||
if !external_ids.is_empty() || !other_config.is_empty() {
|
||||
iface.base_iface_mut().ovsdb = Some(OvsDbIfaceConfig {
|
||||
external_ids: Some(external_ids),
|
||||
other_config: Some(other_config),
|
||||
});
|
||||
}
|
||||
Some(iface)
|
||||
}
|
||||
|
||||
|
@ -330,6 +330,7 @@ class OVSBridge(Bridge, OvsDB):
|
||||
class LinkAggregation:
|
||||
MODE = "mode"
|
||||
PORT_SUBTREE = "port"
|
||||
OVS_DB_SUBTREE = "ovs-db"
|
||||
|
||||
class Port:
|
||||
NAME = "name"
|
||||
|
@ -67,7 +67,6 @@ OVS_BOND_YAML_STATE = f"""
|
||||
- name: {BOND1}
|
||||
link-aggregation:
|
||||
mode: active-backup
|
||||
other_config: {EMPTY_MAP}
|
||||
port:
|
||||
- name: {ETH1}
|
||||
- name: {ETH2}
|
||||
@ -564,8 +563,7 @@ def test_ovsdb_remove_external_ids(ovs_bridge_with_custom_external_ids):
|
||||
}
|
||||
)
|
||||
iface_info = statelib.show_only((PORT1,))[Interface.KEY][0]
|
||||
external_ids = iface_info[OvsDB.OVS_DB_SUBTREE][OvsDB.EXTERNAL_IDS]
|
||||
assert len(external_ids) == 0
|
||||
assert OvsDB.OVS_DB_SUBTREE not in iface_info
|
||||
|
||||
|
||||
def test_ovsdb_override_external_ids(ovs_bridge_with_custom_external_ids):
|
||||
@ -1645,3 +1643,162 @@ def test_ovs_detach_2_ports_from_4_ports_ovs_bond(
|
||||
)
|
||||
libnmstate.apply(desired_state)
|
||||
assertlib.assert_state_match(desired_state)
|
||||
|
||||
|
||||
# OpenStack use case
|
||||
@pytest.mark.tier1
|
||||
@pytest.mark.skipif(
|
||||
nm_minor_version() < 41,
|
||||
reason="OVS interface level other_config is not supported in NM 1.40-",
|
||||
)
|
||||
def test_ovs_bond_other_config_and_remove(
|
||||
cleanup_ovs_bridge, eth1_up, eth2_up
|
||||
):
|
||||
desired_state = yaml.load(
|
||||
"""---
|
||||
interfaces:
|
||||
- name: br0
|
||||
type: ovs-bridge
|
||||
state: up
|
||||
bridge:
|
||||
port:
|
||||
- name: ovs0
|
||||
- name: bond0
|
||||
link-aggregation:
|
||||
mode: balance-slb
|
||||
port:
|
||||
- name: eth1
|
||||
- name: eth2
|
||||
ovs-db:
|
||||
external_ids:
|
||||
test_str: foo1
|
||||
test_num: 100
|
||||
other_config:
|
||||
bond-miimon-interval: 100
|
||||
""",
|
||||
Loader=yaml.SafeLoader,
|
||||
)
|
||||
libnmstate.apply(desired_state)
|
||||
assertlib.assert_state_match(desired_state)
|
||||
|
||||
desired_state = yaml.load(
|
||||
"""---
|
||||
interfaces:
|
||||
- name: br0
|
||||
type: ovs-bridge
|
||||
state: up
|
||||
bridge:
|
||||
port:
|
||||
- name: ovs0
|
||||
- name: bond0
|
||||
link-aggregation:
|
||||
mode: balance-slb
|
||||
port:
|
||||
- name: eth1
|
||||
- name: eth2
|
||||
ovs-db: {}
|
||||
""",
|
||||
Loader=yaml.SafeLoader,
|
||||
)
|
||||
libnmstate.apply(desired_state)
|
||||
assertlib.assert_state_match(desired_state)
|
||||
|
||||
|
||||
# OpenStack use case
|
||||
@pytest.mark.tier1
|
||||
@pytest.mark.skipif(
|
||||
nm_minor_version() < 41,
|
||||
reason="OVS interface level other_config is not supported in NM 1.40-",
|
||||
)
|
||||
def test_ovs_bridge_other_config_and_remove(
|
||||
cleanup_ovs_bridge, eth1_up, eth2_up
|
||||
):
|
||||
desired_state = yaml.load(
|
||||
"""---
|
||||
interfaces:
|
||||
- name: br0
|
||||
type: ovs-bridge
|
||||
state: up
|
||||
ovs-db:
|
||||
other_config:
|
||||
in-band-queue: 12
|
||||
bridge:
|
||||
port:
|
||||
- name: ovs0
|
||||
- name: bond0
|
||||
link-aggregation:
|
||||
mode: balance-slb
|
||||
port:
|
||||
- name: eth1
|
||||
- name: eth2
|
||||
""",
|
||||
Loader=yaml.SafeLoader,
|
||||
)
|
||||
libnmstate.apply(desired_state)
|
||||
assertlib.assert_state_match(desired_state)
|
||||
desired_state = yaml.load(
|
||||
"""---
|
||||
interfaces:
|
||||
- name: br0
|
||||
type: ovs-bridge
|
||||
state: up
|
||||
ovs-db: {}
|
||||
bridge:
|
||||
port:
|
||||
- name: ovs0
|
||||
- name: bond0
|
||||
link-aggregation:
|
||||
mode: balance-slb
|
||||
port:
|
||||
- name: eth1
|
||||
- name: eth2
|
||||
""",
|
||||
Loader=yaml.SafeLoader,
|
||||
)
|
||||
libnmstate.apply(desired_state)
|
||||
assertlib.assert_state_match(desired_state)
|
||||
|
||||
|
||||
# OpenStack use case
|
||||
@pytest.mark.tier1
|
||||
@pytest.mark.skipif(
|
||||
nm_minor_version() < 41,
|
||||
reason="OVS interface level other_config is not supported in NM 1.40-",
|
||||
)
|
||||
def test_ovs_sys_iface_other_config_and_remove(
|
||||
cleanup_ovs_bridge, eth1_up, eth2_up
|
||||
):
|
||||
desired_state = yaml.load(
|
||||
"""---
|
||||
interfaces:
|
||||
- name: eth1
|
||||
type: ethernet
|
||||
state: up
|
||||
ovs-db:
|
||||
other_config:
|
||||
emc-insert-inv-prob: 90
|
||||
- name: br0
|
||||
type: ovs-bridge
|
||||
state: up
|
||||
bridge:
|
||||
port:
|
||||
- name: ovs0
|
||||
- name: eth1
|
||||
""",
|
||||
Loader=yaml.SafeLoader,
|
||||
)
|
||||
libnmstate.apply(desired_state)
|
||||
assertlib.assert_state_match(desired_state)
|
||||
|
||||
desired_state = yaml.load(
|
||||
"""---
|
||||
interfaces:
|
||||
- name: eth1
|
||||
type: ethernet
|
||||
state: up
|
||||
ovs-db: {}
|
||||
""",
|
||||
Loader=yaml.SafeLoader,
|
||||
)
|
||||
libnmstate.apply(desired_state)
|
||||
assertlib.assert_state_match(desired_state)
|
||||
|
@ -29,6 +29,7 @@ from libnmstate.schema import InfiniBand
|
||||
from libnmstate.schema import Interface
|
||||
from libnmstate.schema import InterfaceType
|
||||
from libnmstate.schema import LinuxBridge as LB
|
||||
from libnmstate.schema import OVSBridge
|
||||
from libnmstate.schema import OvsDB
|
||||
|
||||
from . import statelib
|
||||
@ -115,7 +116,7 @@ def _prepare_state_for_verify(desired_state_data):
|
||||
full_desired_state.remove_absent_entries()
|
||||
full_desired_state.normalize()
|
||||
_fix_bond_state(current_state)
|
||||
_fix_ovsdb_external_ids(full_desired_state)
|
||||
_sanitize_ovsdb(full_desired_state)
|
||||
_remove_iface_state_for_verify(full_desired_state)
|
||||
_remove_iface_state_for_verify(current_state)
|
||||
_expand_vlan_filter_range(current_state)
|
||||
@ -174,13 +175,34 @@ def _fix_bond_state(current_state):
|
||||
bond_options["arp_ip_target"] = ""
|
||||
|
||||
|
||||
def _fix_ovsdb_external_ids(state):
|
||||
def _stringlize_ovsdb_conf(ovsdb_conf):
|
||||
for prop_name in [OvsDB.EXTERNAL_IDS, OvsDB.OTHER_CONFIG]:
|
||||
settings = ovsdb_conf.get(prop_name, {})
|
||||
for key, value in settings.items():
|
||||
settings[key] = str(value)
|
||||
|
||||
|
||||
def _sanitize_ovsdb(state):
|
||||
for iface_state in state.state[Interface.KEY]:
|
||||
external_ids = iface_state.get(OvsDB.OVS_DB_SUBTREE, {}).get(
|
||||
OvsDB.EXTERNAL_IDS, {}
|
||||
)
|
||||
for key, value in external_ids.items():
|
||||
external_ids[key] = str(value)
|
||||
ovsdb_conf = iface_state.get(OvsDB.OVS_DB_SUBTREE, {})
|
||||
_stringlize_ovsdb_conf(ovsdb_conf)
|
||||
if len(ovsdb_conf) == 0:
|
||||
iface_state.pop(OvsDB.OVS_DB_SUBTREE, None)
|
||||
|
||||
# Convert OVS bond ovs-db value to string
|
||||
for iface_state in state.state[Interface.KEY]:
|
||||
for port_conf in iface_state.get(OVSBridge.CONFIG_SUBTREE, {}).get(
|
||||
OVSBridge.PORT_SUBTREE, []
|
||||
):
|
||||
bond_conf = port_conf.get(
|
||||
OVSBridge.Port.LINK_AGGREGATION_SUBTREE, {}
|
||||
)
|
||||
ovsdb_conf = bond_conf.get(
|
||||
OVSBridge.Port.LinkAggregation.OVS_DB_SUBTREE, {}
|
||||
)
|
||||
_stringlize_ovsdb_conf(ovsdb_conf)
|
||||
if len(ovsdb_conf) == 0:
|
||||
bond_conf.pop(OvsDB.OVS_DB_SUBTREE, None)
|
||||
|
||||
|
||||
def _remove_iface_state_for_verify(state):
|
||||
|
Loading…
x
Reference in New Issue
Block a user