SRIOV: Support referring interface using PF name and VF ID
Kernel function `dev_valid_name()` indicate `:` is not allowed character for interface name. Hence we introduce special interface name format when referring a SRIOV VF interface without knowing its real interface name. sriov:<pf_name>:<vf_id> You may use this SRIOV VF naming schema for: * Top interface name * Bond port name * Linux bridge port name * OVS bridge port name * OVS bond port name Both Unit test cases and integration test cases are included. Integration test cases are tested on Mellanox MT27710(mlx5). Signed-off-by: Gris Ge <fge@redhat.com>
This commit is contained in:
parent
4f737f3d8d
commit
13cec24117
@ -50,6 +50,29 @@ impl BondInterface {
|
||||
.map(|ports| ports.remove(index));
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn change_port_name(
|
||||
&mut self,
|
||||
origin_name: &str,
|
||||
new_name: String,
|
||||
) {
|
||||
if let Some(index) = self
|
||||
.bond
|
||||
.as_ref()
|
||||
.and_then(|bond_conf| bond_conf.port.as_ref())
|
||||
.and_then(|ports| {
|
||||
ports.iter().position(|port_name| port_name == origin_name)
|
||||
})
|
||||
{
|
||||
if let Some(ports) = self
|
||||
.bond
|
||||
.as_mut()
|
||||
.and_then(|bond_conf| bond_conf.port.as_mut())
|
||||
{
|
||||
ports[index] = new_name;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl BondConfig {
|
||||
|
@ -236,4 +236,18 @@ impl Interface {
|
||||
Self::Unknown(_) | Self::Dummy(_) | Self::OvsInterface(_) => (),
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn change_port_name(
|
||||
&mut self,
|
||||
org_port_name: &str,
|
||||
new_port_name: String,
|
||||
) {
|
||||
if let Interface::LinuxBridge(iface) = self {
|
||||
iface.change_port_name(org_port_name, new_port_name);
|
||||
} else if let Interface::OvsBridge(iface) = self {
|
||||
iface.change_port_name(org_port_name, new_port_name);
|
||||
} else if let Interface::Bond(iface) = self {
|
||||
iface.change_port_name(org_port_name, new_port_name);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -121,6 +121,7 @@ impl Interfaces {
|
||||
&ignored_user_ifaces,
|
||||
);
|
||||
cur_clone.remove_unknown_type_port();
|
||||
self_clone.resolve_sriov_reference(pre_apply_current)?;
|
||||
|
||||
for iface in self_clone.to_vec() {
|
||||
if iface.is_absent() || (iface.is_virtual() && iface.is_down()) {
|
||||
|
@ -126,6 +126,25 @@ impl LinuxBridgeInterface {
|
||||
}
|
||||
false
|
||||
}
|
||||
|
||||
pub(crate) fn change_port_name(
|
||||
&mut self,
|
||||
origin_name: &str,
|
||||
new_name: String,
|
||||
) {
|
||||
if let Some(port_conf) = self
|
||||
.bridge
|
||||
.as_mut()
|
||||
.and_then(|br_conf| br_conf.port.as_mut())
|
||||
.and_then(|port_confs| {
|
||||
port_confs
|
||||
.iter_mut()
|
||||
.find(|port_conf| port_conf.name == origin_name)
|
||||
})
|
||||
{
|
||||
port_conf.name = new_name;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl LinuxBridgeConfig {
|
||||
|
@ -126,6 +126,10 @@ impl NetworkState {
|
||||
.interfaces
|
||||
.resolve_unknown_ifaces(&cur_net_state.interfaces)?;
|
||||
|
||||
desire_state_to_apply
|
||||
.interfaces
|
||||
.resolve_sriov_reference(&cur_net_state.interfaces)?;
|
||||
|
||||
let (add_net_state, chg_net_state, del_net_state) =
|
||||
desire_state_to_apply.gen_state_for_apply(&cur_net_state)?;
|
||||
|
||||
@ -160,7 +164,7 @@ impl NetworkState {
|
||||
// state instead of
|
||||
// current,
|
||||
&cur_net_state,
|
||||
self,
|
||||
&desire_state_to_apply,
|
||||
&checkpoint,
|
||||
self.memory_only,
|
||||
)?;
|
||||
|
@ -153,6 +153,63 @@ impl OvsBridgeInterface {
|
||||
br_ports.retain(|p| p.name.as_str() != port_name)
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn change_port_name(
|
||||
&mut self,
|
||||
origin_name: &str,
|
||||
new_name: String,
|
||||
) {
|
||||
if let Some(index) = self
|
||||
.bridge
|
||||
.as_ref()
|
||||
.and_then(|br_conf| br_conf.ports.as_ref())
|
||||
.and_then(|ports| {
|
||||
ports.iter().position(|port| port.name == origin_name)
|
||||
})
|
||||
{
|
||||
if let Some(ports) = self
|
||||
.bridge
|
||||
.as_mut()
|
||||
.and_then(|br_conf| br_conf.ports.as_mut())
|
||||
{
|
||||
ports[index].name = new_name;
|
||||
}
|
||||
} else if let Some(index) = self
|
||||
.bridge
|
||||
.as_ref()
|
||||
.and_then(|br_conf| br_conf.ports.as_ref())
|
||||
.and_then(|ports| {
|
||||
ports.iter().position(|port_conf| {
|
||||
port_conf
|
||||
.bond
|
||||
.as_ref()
|
||||
.and_then(|bond_conf| bond_conf.ports.as_ref())
|
||||
.map(|bond_port_confs| {
|
||||
bond_port_confs
|
||||
.iter()
|
||||
.any(|bond_conf| bond_conf.name == origin_name)
|
||||
})
|
||||
.unwrap_or_default()
|
||||
})
|
||||
})
|
||||
{
|
||||
if let Some(bond_port_confs) = self
|
||||
.bridge
|
||||
.as_mut()
|
||||
.and_then(|br_conf| br_conf.ports.as_mut())
|
||||
.and_then(|ports| ports.get_mut(index))
|
||||
.and_then(|port_conf| port_conf.bond.as_mut())
|
||||
.and_then(|bond_conf| bond_conf.ports.as_mut())
|
||||
{
|
||||
for bond_port_conf in bond_port_confs {
|
||||
if bond_port_conf.name == origin_name {
|
||||
bond_port_conf.name = new_name;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl OvsBridgeBondConfig {
|
||||
|
@ -4,6 +4,9 @@ use crate::{
|
||||
ErrorKind, Interface, InterfaceType, Interfaces, NmstateError, SrIovConfig,
|
||||
};
|
||||
|
||||
const SRIOV_VF_NAMING_SEPERATOR: char = ':';
|
||||
const SRIOV_VF_NAMING_PREFIX: &str = "sriov:";
|
||||
|
||||
impl SrIovConfig {
|
||||
pub(crate) fn update(&mut self, other: Option<&SrIovConfig>) {
|
||||
if let Some(other) = other {
|
||||
@ -91,3 +94,182 @@ impl SrIovConfig {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl Interfaces {
|
||||
pub(crate) fn resolve_sriov_reference(
|
||||
&mut self,
|
||||
current: &Self,
|
||||
) -> Result<(), NmstateError> {
|
||||
self.resolve_sriov_reference_iface_name(current)?;
|
||||
self.resolve_sriov_reference_port_name(current)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn resolve_sriov_reference_iface_name(
|
||||
&mut self,
|
||||
current: &Self,
|
||||
) -> Result<(), NmstateError> {
|
||||
let mut changed_iface_names: Vec<String> = Vec::new();
|
||||
for iface in self
|
||||
.kernel_ifaces
|
||||
.values_mut()
|
||||
.filter(|i| i.iface_type() == InterfaceType::Ethernet)
|
||||
{
|
||||
if let Some((pf_name, vf_id)) = parse_sriov_vf_naming(iface.name())?
|
||||
{
|
||||
if let Some(vf_iface_name) =
|
||||
get_sriov_vf_iface_name(current, pf_name, vf_id)
|
||||
{
|
||||
changed_iface_names.push(iface.name().to_string());
|
||||
log::info!(
|
||||
"SR-IOV VF {} resolved to interface name {}",
|
||||
iface.name(),
|
||||
vf_iface_name
|
||||
);
|
||||
iface.base_iface_mut().name = vf_iface_name;
|
||||
} else {
|
||||
let e = NmstateError::new(
|
||||
ErrorKind::InvalidArgument,
|
||||
format!(
|
||||
"Failed to find SR-IOV VF interface name for {}",
|
||||
iface.name()
|
||||
),
|
||||
);
|
||||
log::error!("{}", e);
|
||||
return Err(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
for changed_iface_name in changed_iface_names {
|
||||
if let Some(iface) = self.kernel_ifaces.remove(&changed_iface_name)
|
||||
{
|
||||
if self.kernel_ifaces.get(iface.name()).is_some() {
|
||||
let e = NmstateError::new(
|
||||
ErrorKind::InvalidArgument,
|
||||
format!(
|
||||
"SR-IOV VF name {} has been resolved as interface \
|
||||
{}, but it is already defined in desire state",
|
||||
changed_iface_name,
|
||||
iface.name()
|
||||
),
|
||||
);
|
||||
log::error!("{}", e);
|
||||
return Err(e);
|
||||
}
|
||||
self.kernel_ifaces.insert(iface.name().to_string(), iface);
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn resolve_sriov_reference_port_name(
|
||||
&mut self,
|
||||
current: &Self,
|
||||
) -> Result<(), NmstateError> {
|
||||
// pending_changes:
|
||||
// Vec<(ctrl_name, ctrl_iface_type, origin_name, new_name)>
|
||||
let mut pending_changes = Vec::new();
|
||||
for iface in self
|
||||
.kernel_ifaces
|
||||
.values()
|
||||
.chain(self.user_ifaces.values())
|
||||
.filter(|i| i.is_controller())
|
||||
{
|
||||
let ports = match iface.ports() {
|
||||
Some(p) => p,
|
||||
None => continue,
|
||||
};
|
||||
for port in ports {
|
||||
if let Some((pf_name, vf_id)) = parse_sriov_vf_naming(port)? {
|
||||
if let Some(vf_iface_name) =
|
||||
get_sriov_vf_iface_name(current, pf_name, vf_id)
|
||||
{
|
||||
log::info!(
|
||||
"SR-IOV VF {} resolved to interface name {}",
|
||||
port,
|
||||
vf_iface_name
|
||||
);
|
||||
pending_changes.push((
|
||||
iface.name().to_string(),
|
||||
iface.iface_type(),
|
||||
port.to_string(),
|
||||
vf_iface_name.to_string(),
|
||||
));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
for (ctrl, ctrl_iface_type, origin_name, new_name) in pending_changes {
|
||||
if let Some(iface) = self.get_iface_mut(&ctrl, ctrl_iface_type) {
|
||||
iface.change_port_name(origin_name.as_str(), new_name);
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
fn parse_sriov_vf_naming(
|
||||
iface_name: &str,
|
||||
) -> Result<Option<(&str, u32)>, NmstateError> {
|
||||
if iface_name.starts_with(SRIOV_VF_NAMING_PREFIX) {
|
||||
let names: Vec<&str> =
|
||||
iface_name.split(SRIOV_VF_NAMING_SEPERATOR).collect();
|
||||
if names.len() == 3 {
|
||||
match names[2].parse::<u32>() {
|
||||
Ok(vf_id) => Ok(Some((names[1], vf_id))),
|
||||
Err(e) => {
|
||||
let e = NmstateError::new(
|
||||
ErrorKind::InvalidArgument,
|
||||
format!(
|
||||
"Invalid SR-IOV VF ID in {}, correct format \
|
||||
is 'sriov:<pf_name>:<vf_id>', error: {}",
|
||||
iface_name, e
|
||||
),
|
||||
);
|
||||
log::error!("{}", e);
|
||||
Err(e)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
let e = NmstateError::new(
|
||||
ErrorKind::InvalidArgument,
|
||||
format!(
|
||||
"Invalid SR-IOV VF name {}, correct format is \
|
||||
'sriov:<pf_name>:<vf_id>'",
|
||||
iface_name,
|
||||
),
|
||||
);
|
||||
log::error!("{}", e);
|
||||
Err(e)
|
||||
}
|
||||
} else {
|
||||
Ok(None)
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn get_sriov_vf_iface_name(
|
||||
current: &Interfaces,
|
||||
pf_name: &str,
|
||||
vf_id: u32,
|
||||
) -> Option<String> {
|
||||
if let Some(Interface::Ethernet(pf_iface)) =
|
||||
current.get_iface(pf_name, InterfaceType::Ethernet)
|
||||
{
|
||||
if let Some(vfs) = pf_iface
|
||||
.ethernet
|
||||
.as_ref()
|
||||
.and_then(|e| e.sr_iov.as_ref())
|
||||
.and_then(|s| s.vfs.as_ref())
|
||||
{
|
||||
for vf in vfs {
|
||||
if vf.id == vf_id {
|
||||
if !vf.iface_name.is_empty() {
|
||||
return Some(vf.iface_name.clone());
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
None
|
||||
}
|
||||
|
@ -1,6 +1,7 @@
|
||||
use crate::{
|
||||
unit_tests::testlib::new_eth_iface, EthernetConfig, Interface, Interfaces,
|
||||
SrIovConfig, SrIovVfConfig,
|
||||
unit_tests::testlib::new_eth_iface, BridgePortVlanMode, ErrorKind,
|
||||
EthernetConfig, Interface, InterfaceType, Interfaces, SrIovConfig,
|
||||
SrIovVfConfig,
|
||||
};
|
||||
|
||||
#[test]
|
||||
@ -75,3 +76,371 @@ fn test_ignore_sriov_if_not_desired() {
|
||||
|
||||
desired.verify(&Interfaces::new(), ¤t).unwrap();
|
||||
}
|
||||
|
||||
fn gen_sriov_current_ifaces() -> Interfaces {
|
||||
let mut current = serde_yaml::from_str::<Interfaces>(
|
||||
r#"---
|
||||
- name: eth1
|
||||
type: ethernet
|
||||
state: up
|
||||
ethernet:
|
||||
sr-iov:
|
||||
total-vfs: 2
|
||||
vfs:
|
||||
- id: 0
|
||||
- id: 1
|
||||
- name: eth1v0
|
||||
type: ethernet
|
||||
state: up
|
||||
- name: eth1v1
|
||||
type: ethernet
|
||||
state: up
|
||||
"#,
|
||||
)
|
||||
.unwrap();
|
||||
let iface = current.kernel_ifaces.get_mut("eth1").unwrap();
|
||||
if let Interface::Ethernet(eth_iface) = iface {
|
||||
eth_iface
|
||||
.ethernet
|
||||
.as_mut()
|
||||
.unwrap()
|
||||
.sr_iov
|
||||
.as_mut()
|
||||
.unwrap()
|
||||
.vfs
|
||||
.as_mut()
|
||||
.unwrap()[0]
|
||||
.iface_name = "eth1v0".to_string();
|
||||
eth_iface
|
||||
.ethernet
|
||||
.as_mut()
|
||||
.unwrap()
|
||||
.sr_iov
|
||||
.as_mut()
|
||||
.unwrap()
|
||||
.vfs
|
||||
.as_mut()
|
||||
.unwrap()[1]
|
||||
.iface_name = "eth1v1".to_string();
|
||||
}
|
||||
current
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_resolve_sriov_name() {
|
||||
let current = gen_sriov_current_ifaces();
|
||||
let mut desired = serde_yaml::from_str::<Interfaces>(
|
||||
r#"---
|
||||
- name: sriov:eth1:0
|
||||
type: ethernet
|
||||
state: up
|
||||
mtu: 1280
|
||||
- name: sriov:eth1:1
|
||||
type: ethernet
|
||||
state: up
|
||||
mtu: 1281
|
||||
"#,
|
||||
)
|
||||
.unwrap();
|
||||
desired.resolve_sriov_reference(¤t).unwrap();
|
||||
let vf0_iface = desired
|
||||
.get_iface("eth1v0", InterfaceType::Ethernet)
|
||||
.unwrap();
|
||||
let vf1_iface = desired
|
||||
.get_iface("eth1v1", InterfaceType::Ethernet)
|
||||
.unwrap();
|
||||
assert_eq!(vf0_iface.base_iface().mtu, Some(1280));
|
||||
assert_eq!(vf1_iface.base_iface().mtu, Some(1281));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_resolve_sriov_name_duplicate() {
|
||||
let current = gen_sriov_current_ifaces();
|
||||
let mut desired = serde_yaml::from_str::<Interfaces>(
|
||||
r#"---
|
||||
- name: sriov:eth1:0
|
||||
type: ethernet
|
||||
state: up
|
||||
mtu: 1280
|
||||
- name: eth1v0
|
||||
type: ethernet
|
||||
state: up
|
||||
mtu: 1281
|
||||
"#,
|
||||
)
|
||||
.unwrap();
|
||||
let result = desired.resolve_sriov_reference(¤t);
|
||||
assert!(result.is_err());
|
||||
if let Err(e) = result {
|
||||
assert_eq!(e.kind(), ErrorKind::InvalidArgument);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_verify_sriov_name() {
|
||||
let current = gen_sriov_current_ifaces();
|
||||
let desired = serde_yaml::from_str::<Interfaces>(
|
||||
r#"---
|
||||
- name: sriov:eth1:0
|
||||
type: ethernet
|
||||
state: up
|
||||
"#,
|
||||
)
|
||||
.unwrap();
|
||||
desired.verify(¤t, ¤t).unwrap();
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_resolve_sriov_port_name_linux_bridge() {
|
||||
let current = gen_sriov_current_ifaces();
|
||||
let mut desired = serde_yaml::from_str::<Interfaces>(
|
||||
r#"---
|
||||
- name: br0
|
||||
type: linux-bridge
|
||||
state: up
|
||||
bridge:
|
||||
port:
|
||||
- name: sriov:eth1:1
|
||||
vlan:
|
||||
mode: access
|
||||
tag: 305
|
||||
"#,
|
||||
)
|
||||
.unwrap();
|
||||
desired.resolve_sriov_reference(¤t).unwrap();
|
||||
if let Interface::LinuxBridge(br_iface) = desired
|
||||
.get_iface("br0", InterfaceType::LinuxBridge)
|
||||
.unwrap()
|
||||
{
|
||||
let port_confs =
|
||||
br_iface.bridge.as_ref().unwrap().port.as_ref().unwrap();
|
||||
assert_eq!(port_confs.len(), 1);
|
||||
assert_eq!(port_confs[0].name, "eth1v1".to_string());
|
||||
assert_eq!(
|
||||
port_confs[0].vlan.as_ref().unwrap().mode.unwrap(),
|
||||
BridgePortVlanMode::Access
|
||||
);
|
||||
assert_eq!(port_confs[0].vlan.as_ref().unwrap().tag.unwrap(), 305);
|
||||
} else {
|
||||
panic!("Failed to find expected bridge interface br0");
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_resolve_sriov_port_name_bond() {
|
||||
let current = gen_sriov_current_ifaces();
|
||||
let mut desired = serde_yaml::from_str::<Interfaces>(
|
||||
r#"---
|
||||
- name: bond0
|
||||
type: bond
|
||||
state: up
|
||||
link-aggregation:
|
||||
mode: balance-rr
|
||||
port:
|
||||
- sriov:eth1:1
|
||||
- sriov:eth1:0
|
||||
"#,
|
||||
)
|
||||
.unwrap();
|
||||
desired.resolve_sriov_reference(¤t).unwrap();
|
||||
let bond_iface = desired.get_iface("bond0", InterfaceType::Bond).unwrap();
|
||||
let ports = bond_iface.ports().unwrap();
|
||||
assert_eq!(ports.len(), 2);
|
||||
assert_eq!(ports, vec!["eth1v1", "eth1v0"]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_resolve_sriov_port_name_ovs_bridge() {
|
||||
let current = gen_sriov_current_ifaces();
|
||||
let mut desired = serde_yaml::from_str::<Interfaces>(
|
||||
r#"---
|
||||
- name: ovs-br0
|
||||
type: ovs-bridge
|
||||
state: up
|
||||
bridge:
|
||||
port:
|
||||
- name: sriov:eth1:0
|
||||
- name: sriov:eth1:1
|
||||
"#,
|
||||
)
|
||||
.unwrap();
|
||||
desired.resolve_sriov_reference(¤t).unwrap();
|
||||
let br_iface = desired
|
||||
.get_iface("ovs-br0", InterfaceType::OvsBridge)
|
||||
.unwrap();
|
||||
let ports = br_iface.ports().unwrap();
|
||||
assert_eq!(ports.len(), 2);
|
||||
assert_eq!(ports, vec!["eth1v0", "eth1v1"]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_resolve_sriov_port_name_ovs_bond() {
|
||||
let current = gen_sriov_current_ifaces();
|
||||
let mut desired = serde_yaml::from_str::<Interfaces>(
|
||||
r#"---
|
||||
- name: ovs-br0
|
||||
type: ovs-bridge
|
||||
state: up
|
||||
bridge:
|
||||
port:
|
||||
- name: bond1
|
||||
link-aggregation:
|
||||
mode: balance-slb
|
||||
port:
|
||||
- name: eth2
|
||||
- name: sriov:eth1:1
|
||||
"#,
|
||||
)
|
||||
.unwrap();
|
||||
desired.resolve_sriov_reference(¤t).unwrap();
|
||||
let br_iface = desired
|
||||
.get_iface("ovs-br0", InterfaceType::OvsBridge)
|
||||
.unwrap();
|
||||
let ports = br_iface.ports().unwrap();
|
||||
assert_eq!(ports.len(), 2);
|
||||
assert_eq!(ports, vec!["eth2", "eth1v1"]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_verify_sriov_port_name_linux_bridge() {
|
||||
let pre_apply_current = gen_sriov_current_ifaces();
|
||||
let desired = serde_yaml::from_str::<Interfaces>(
|
||||
r#"---
|
||||
- name: br0
|
||||
type: linux-bridge
|
||||
state: up
|
||||
bridge:
|
||||
port:
|
||||
- name: sriov:eth1:1
|
||||
vlan:
|
||||
mode: access
|
||||
tag: 305
|
||||
"#,
|
||||
)
|
||||
.unwrap();
|
||||
let mut current = gen_sriov_current_ifaces();
|
||||
current.push(
|
||||
serde_yaml::from_str::<Interface>(
|
||||
r#"---
|
||||
name: br0
|
||||
type: linux-bridge
|
||||
state: up
|
||||
bridge:
|
||||
port:
|
||||
- name: eth1v1
|
||||
vlan:
|
||||
mode: access
|
||||
tag: 305
|
||||
"#,
|
||||
)
|
||||
.unwrap(),
|
||||
);
|
||||
|
||||
desired.verify(&pre_apply_current, ¤t).unwrap();
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_verify_sriov_port_name_bond() {
|
||||
let pre_apply_current = gen_sriov_current_ifaces();
|
||||
let desired = serde_yaml::from_str::<Interfaces>(
|
||||
r#"---
|
||||
- name: bond0
|
||||
type: bond
|
||||
state: up
|
||||
link-aggregation:
|
||||
mode: balance-rr
|
||||
port:
|
||||
- sriov:eth1:1
|
||||
"#,
|
||||
)
|
||||
.unwrap();
|
||||
let mut current = gen_sriov_current_ifaces();
|
||||
current.push(
|
||||
serde_yaml::from_str::<Interface>(
|
||||
r#"---
|
||||
name: bond0
|
||||
type: bond
|
||||
state: up
|
||||
link-aggregation:
|
||||
mode: balance-rr
|
||||
port:
|
||||
- eth1v1
|
||||
"#,
|
||||
)
|
||||
.unwrap(),
|
||||
);
|
||||
|
||||
desired.verify(&pre_apply_current, ¤t).unwrap();
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_verify_sriov_port_name_ovs_bridge() {
|
||||
let pre_apply_current = gen_sriov_current_ifaces();
|
||||
let desired = serde_yaml::from_str::<Interfaces>(
|
||||
r#"---
|
||||
- name: ovs-br0
|
||||
type: ovs-bridge
|
||||
state: up
|
||||
bridge:
|
||||
port:
|
||||
- name: sriov:eth1:1
|
||||
"#,
|
||||
)
|
||||
.unwrap();
|
||||
let mut current = gen_sriov_current_ifaces();
|
||||
current.push(
|
||||
serde_yaml::from_str::<Interface>(
|
||||
r#"---
|
||||
name: ovs-br0
|
||||
type: ovs-bridge
|
||||
state: up
|
||||
bridge:
|
||||
port:
|
||||
- name: eth1v1
|
||||
"#,
|
||||
)
|
||||
.unwrap(),
|
||||
);
|
||||
desired.verify(&pre_apply_current, ¤t).unwrap();
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_verify_sriov_port_name_ovs_bond() {
|
||||
let pre_apply_current = gen_sriov_current_ifaces();
|
||||
let desired = serde_yaml::from_str::<Interfaces>(
|
||||
r#"---
|
||||
- name: ovs-br0
|
||||
type: ovs-bridge
|
||||
state: up
|
||||
bridge:
|
||||
port:
|
||||
- name: bond1
|
||||
link-aggregation:
|
||||
mode: balance-slb
|
||||
port:
|
||||
- name: eth2
|
||||
- name: sriov:eth1:1
|
||||
"#,
|
||||
)
|
||||
.unwrap();
|
||||
let mut current = gen_sriov_current_ifaces();
|
||||
current.push(
|
||||
serde_yaml::from_str::<Interface>(
|
||||
r#"---
|
||||
name: ovs-br0
|
||||
type: ovs-bridge
|
||||
state: up
|
||||
bridge:
|
||||
port:
|
||||
- name: bond1
|
||||
link-aggregation:
|
||||
mode: balance-slb
|
||||
port:
|
||||
- name: eth1v1
|
||||
- name: eth2
|
||||
"#,
|
||||
)
|
||||
.unwrap(),
|
||||
);
|
||||
desired.verify(&pre_apply_current, ¤t).unwrap();
|
||||
}
|
||||
|
@ -1,39 +1,35 @@
|
||||
#
|
||||
# Copyright (c) 2019-2020 Red Hat, Inc.
|
||||
#
|
||||
# This file is part of nmstate
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU Lesser General Public License as published by
|
||||
# the Free Software Foundation, either version 2.1 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU Lesser General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU Lesser General Public License
|
||||
# along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
#
|
||||
# SPDX-License-Identifier: LGPL-2.1-or-later
|
||||
|
||||
import copy
|
||||
import os
|
||||
|
||||
import pytest
|
||||
|
||||
import libnmstate
|
||||
from libnmstate.schema import Bond
|
||||
from libnmstate.schema import Ethernet
|
||||
from libnmstate.schema import Interface
|
||||
from libnmstate.schema import InterfaceState
|
||||
from libnmstate.schema import InterfaceType
|
||||
from libnmstate.schema import LinuxBridge
|
||||
from libnmstate.schema import OVSBridge
|
||||
|
||||
from .testlib import assertlib
|
||||
from .testlib import statelib
|
||||
|
||||
from .testlib.bondlib import bond_interface
|
||||
from .testlib.bridgelib import add_port_to_bridge
|
||||
from .testlib.bridgelib import create_bridge_subtree_state
|
||||
from .testlib.bridgelib import linux_bridge
|
||||
from .testlib.ovslib import ovs_bridge
|
||||
from .testlib.ovslib import ovs_bridge_bond
|
||||
|
||||
MAC1 = "00:11:22:33:44:55"
|
||||
MAC2 = "00:11:22:33:44:66"
|
||||
MAC3 = "00:11:22:33:44:FF"
|
||||
MAC_MIX_CASE = "00:11:22:33:44:Ff"
|
||||
TEST_BOND = "test-bond0"
|
||||
TEST_BRIDGE = "test-br0"
|
||||
TEST_OVS_IFACE = "test-ovs0"
|
||||
|
||||
VF0_CONF = {
|
||||
Ethernet.SRIOV.VFS.ID: 0,
|
||||
@ -54,6 +50,12 @@ def _test_nic_name():
|
||||
return os.environ.get("TEST_REAL_NIC")
|
||||
|
||||
|
||||
def find_vf_iface_name(pf_name, vf_id):
|
||||
return os.listdir(
|
||||
f"/sys/class/net/{pf_name}/device/virtfn{vf_id}/net"
|
||||
).pop()
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def disable_sriov():
|
||||
pf_name = _test_nic_name()
|
||||
@ -121,189 +123,288 @@ def sriov_iface_vf(disable_sriov):
|
||||
not os.environ.get("TEST_REAL_NIC"),
|
||||
reason="Need to define TEST_REAL_NIC for SR-IOV test",
|
||||
)
|
||||
def test_sriov_with_no_vfs_config(sriov_interface):
|
||||
assertlib.assert_state_match(sriov_interface)
|
||||
class TestSrIov:
|
||||
def test_sriov_with_no_vfs_config(self, sriov_interface):
|
||||
assertlib.assert_state_match(sriov_interface)
|
||||
|
||||
|
||||
@pytest.mark.skipif(
|
||||
not os.environ.get("TEST_REAL_NIC"),
|
||||
reason="Need to define TEST_REAL_NIC for SR-IOV test",
|
||||
)
|
||||
def test_sriov_increase_vfs(sriov_interface):
|
||||
desired_state = sriov_interface
|
||||
eth_config = desired_state[Interface.KEY][0][Ethernet.CONFIG_SUBTREE]
|
||||
eth_config[Ethernet.SRIOV_SUBTREE][Ethernet.SRIOV.TOTAL_VFS] = 5
|
||||
libnmstate.apply(desired_state)
|
||||
assertlib.assert_state_match(desired_state)
|
||||
|
||||
|
||||
@pytest.mark.skipif(
|
||||
not os.environ.get("TEST_REAL_NIC"),
|
||||
reason="Need to define TEST_REAL_NIC for SR-IOV test",
|
||||
)
|
||||
def test_sriov_decrease_vfs(sriov_interface):
|
||||
desired_state = sriov_interface
|
||||
eth_config = desired_state[Interface.KEY][0][Ethernet.CONFIG_SUBTREE]
|
||||
eth_config[Ethernet.SRIOV_SUBTREE][Ethernet.SRIOV.TOTAL_VFS] = 1
|
||||
eth_config[Ethernet.SRIOV_SUBTREE][Ethernet.SRIOV.VFS_SUBTREE] = [VF0_CONF]
|
||||
libnmstate.apply(desired_state)
|
||||
assertlib.assert_state_match(desired_state)
|
||||
|
||||
|
||||
@pytest.mark.skipif(
|
||||
not os.environ.get("TEST_REAL_NIC"),
|
||||
reason="Need to define TEST_REAL_NIC for SR-IOV test",
|
||||
)
|
||||
def test_sriov_create_vf_config(sriov_iface_vf):
|
||||
assertlib.assert_state_match(sriov_iface_vf)
|
||||
|
||||
|
||||
@pytest.mark.skipif(
|
||||
not os.environ.get("TEST_REAL_NIC"),
|
||||
reason="Need to define TEST_REAL_NIC for SR-IOV test",
|
||||
)
|
||||
def test_sriov_edit_vf_config(sriov_iface_vf):
|
||||
desired_state = sriov_iface_vf
|
||||
eth_config = desired_state[Interface.KEY][0][Ethernet.CONFIG_SUBTREE]
|
||||
vf0 = eth_config[Ethernet.SRIOV_SUBTREE][Ethernet.SRIOV.VFS_SUBTREE][0]
|
||||
vf0[Ethernet.SRIOV.VFS.TRUST] = True
|
||||
vf0[Ethernet.SRIOV.VFS.MAC_ADDRESS] = MAC3
|
||||
libnmstate.apply(desired_state)
|
||||
assertlib.assert_state_match(desired_state)
|
||||
|
||||
|
||||
@pytest.mark.skipif(
|
||||
not os.environ.get("TEST_REAL_NIC"),
|
||||
reason="Need to define TEST_REAL_NIC for SR-IOV test",
|
||||
)
|
||||
@pytest.mark.xfail(
|
||||
raises=libnmstate.error.NmstateVerificationError,
|
||||
reason="https://github.com/nmstate/nmstate/issues/1454",
|
||||
strict=True,
|
||||
)
|
||||
def test_sriov_remove_vf_config(sriov_iface_vf):
|
||||
desired_state = sriov_iface_vf
|
||||
eth_config = desired_state[Interface.KEY][0][Ethernet.CONFIG_SUBTREE]
|
||||
eth_config[Ethernet.SRIOV_SUBTREE][Ethernet.SRIOV.VFS_SUBTREE] = []
|
||||
libnmstate.apply(desired_state)
|
||||
assertlib.assert_state_match(desired_state)
|
||||
|
||||
|
||||
@pytest.mark.skipif(
|
||||
not os.environ.get("TEST_REAL_NIC"),
|
||||
reason="Need to define TEST_REAL_NIC for SR-IOV test",
|
||||
)
|
||||
def test_sriov_vf_mac_mixed_case(sriov_iface_vf):
|
||||
desired_state = sriov_iface_vf
|
||||
eth_config = desired_state[Interface.KEY][0][Ethernet.CONFIG_SUBTREE]
|
||||
vf0 = eth_config[Ethernet.SRIOV_SUBTREE][Ethernet.SRIOV.VFS_SUBTREE][0]
|
||||
vf0[Ethernet.SRIOV.VFS.MAC_ADDRESS] = MAC_MIX_CASE
|
||||
libnmstate.apply(desired_state)
|
||||
|
||||
vf0[Ethernet.SRIOV.VFS.MAC_ADDRESS] = MAC_MIX_CASE.upper()
|
||||
assertlib.assert_state_match(desired_state)
|
||||
|
||||
|
||||
@pytest.mark.skipif(
|
||||
not os.environ.get("TEST_REAL_NIC"),
|
||||
reason="Need to define TEST_REAL_NIC for SR-IOV test",
|
||||
)
|
||||
def test_wait_sriov_vf_been_created():
|
||||
pf_name = _test_nic_name()
|
||||
desired_state = {
|
||||
Interface.KEY: [
|
||||
{
|
||||
Interface.NAME: pf_name,
|
||||
Ethernet.CONFIG_SUBTREE: {
|
||||
Ethernet.SRIOV_SUBTREE: {Ethernet.SRIOV.TOTAL_VFS: 2}
|
||||
},
|
||||
}
|
||||
]
|
||||
}
|
||||
try:
|
||||
def test_sriov_increase_vfs(self, sriov_interface):
|
||||
desired_state = sriov_interface
|
||||
eth_config = desired_state[Interface.KEY][0][Ethernet.CONFIG_SUBTREE]
|
||||
eth_config[Ethernet.SRIOV_SUBTREE][Ethernet.SRIOV.TOTAL_VFS] = 5
|
||||
libnmstate.apply(desired_state)
|
||||
assertlib.assert_state_match(desired_state)
|
||||
current_state = statelib.show_only((f"{pf_name}v0", f"{pf_name}v1"))
|
||||
assert len(current_state[Interface.KEY]) == 2
|
||||
|
||||
finally:
|
||||
desired_state[Interface.KEY][0][
|
||||
Interface.STATE
|
||||
] = InterfaceState.ABSENT
|
||||
libnmstate.apply(desired_state)
|
||||
|
||||
|
||||
@pytest.mark.skipif(
|
||||
not os.environ.get("TEST_REAL_NIC"),
|
||||
reason="Need to define TEST_REAL_NIC for SR-IOV test",
|
||||
)
|
||||
def test_wait_sriov_vf_been_deleted_when_total_vfs_decrease():
|
||||
pf_name = _test_nic_name()
|
||||
desired_state = {
|
||||
Interface.KEY: [
|
||||
{
|
||||
Interface.NAME: pf_name,
|
||||
Ethernet.CONFIG_SUBTREE: {
|
||||
Ethernet.SRIOV_SUBTREE: {Ethernet.SRIOV.TOTAL_VFS: 2}
|
||||
},
|
||||
}
|
||||
def test_sriov_decrease_vfs(self, sriov_interface):
|
||||
desired_state = sriov_interface
|
||||
eth_config = desired_state[Interface.KEY][0][Ethernet.CONFIG_SUBTREE]
|
||||
eth_config[Ethernet.SRIOV_SUBTREE][Ethernet.SRIOV.TOTAL_VFS] = 1
|
||||
eth_config[Ethernet.SRIOV_SUBTREE][Ethernet.SRIOV.VFS_SUBTREE] = [
|
||||
VF0_CONF
|
||||
]
|
||||
}
|
||||
try:
|
||||
libnmstate.apply(desired_state)
|
||||
assertlib.assert_state_match(desired_state)
|
||||
current_state = statelib.show_only((f"{pf_name}v0", f"{pf_name}v1"))
|
||||
assert len(current_state[Interface.KEY]) == 2
|
||||
|
||||
desired_state[Interface.KEY][0][Ethernet.CONFIG_SUBTREE][
|
||||
Ethernet.SRIOV_SUBTREE
|
||||
][Ethernet.SRIOV.TOTAL_VFS] = 1
|
||||
def test_sriov_create_vf_config(self, sriov_iface_vf):
|
||||
assertlib.assert_state_match(sriov_iface_vf)
|
||||
|
||||
def test_sriov_edit_vf_config(self, sriov_iface_vf):
|
||||
desired_state = sriov_iface_vf
|
||||
eth_config = desired_state[Interface.KEY][0][Ethernet.CONFIG_SUBTREE]
|
||||
vf0 = eth_config[Ethernet.SRIOV_SUBTREE][Ethernet.SRIOV.VFS_SUBTREE][0]
|
||||
vf0[Ethernet.SRIOV.VFS.TRUST] = True
|
||||
vf0[Ethernet.SRIOV.VFS.MAC_ADDRESS] = MAC3
|
||||
libnmstate.apply(desired_state)
|
||||
assertlib.assert_state_match(desired_state)
|
||||
current_state = statelib.show_only((f"{pf_name}v0", f"{pf_name}v1"))
|
||||
assert len(current_state[Interface.KEY]) == 1
|
||||
|
||||
finally:
|
||||
desired_state[Interface.KEY][0][
|
||||
Interface.STATE
|
||||
] = InterfaceState.ABSENT
|
||||
@pytest.mark.xfail(
|
||||
raises=libnmstate.error.NmstateVerificationError,
|
||||
reason="https://github.com/nmstate/nmstate/issues/1454",
|
||||
strict=True,
|
||||
)
|
||||
def test_sriov_remove_vf_config(self, sriov_iface_vf):
|
||||
desired_state = sriov_iface_vf
|
||||
eth_config = desired_state[Interface.KEY][0][Ethernet.CONFIG_SUBTREE]
|
||||
eth_config[Ethernet.SRIOV_SUBTREE][Ethernet.SRIOV.VFS_SUBTREE] = []
|
||||
libnmstate.apply(desired_state)
|
||||
assertlib.assert_state_match(desired_state)
|
||||
|
||||
def test_sriov_vf_mac_mixed_case(self, sriov_iface_vf):
|
||||
desired_state = sriov_iface_vf
|
||||
eth_config = desired_state[Interface.KEY][0][Ethernet.CONFIG_SUBTREE]
|
||||
vf0 = eth_config[Ethernet.SRIOV_SUBTREE][Ethernet.SRIOV.VFS_SUBTREE][0]
|
||||
vf0[Ethernet.SRIOV.VFS.MAC_ADDRESS] = MAC_MIX_CASE
|
||||
libnmstate.apply(desired_state)
|
||||
|
||||
vf0[Ethernet.SRIOV.VFS.MAC_ADDRESS] = MAC_MIX_CASE.upper()
|
||||
assertlib.assert_state_match(desired_state)
|
||||
|
||||
@pytest.mark.skipif(
|
||||
not os.environ.get("TEST_REAL_NIC"),
|
||||
reason="Need to define TEST_REAL_NIC for SR-IOV test",
|
||||
)
|
||||
def test_sriov_vf_vlan_id_and_qos():
|
||||
pf_name = _test_nic_name()
|
||||
desired_state = {
|
||||
Interface.KEY: [
|
||||
{
|
||||
Interface.NAME: pf_name,
|
||||
Ethernet.CONFIG_SUBTREE: {
|
||||
Ethernet.SRIOV_SUBTREE: {
|
||||
Ethernet.SRIOV.TOTAL_VFS: 2,
|
||||
Ethernet.SRIOV.VFS_SUBTREE: [
|
||||
{
|
||||
Ethernet.SRIOV.VFS.ID: 0,
|
||||
Ethernet.SRIOV.VFS.VLAN_ID: 100,
|
||||
Ethernet.SRIOV.VFS.QOS: 5,
|
||||
},
|
||||
{
|
||||
Ethernet.SRIOV.VFS.ID: 1,
|
||||
Ethernet.SRIOV.VFS.VLAN_ID: 102,
|
||||
Ethernet.SRIOV.VFS.QOS: 6,
|
||||
},
|
||||
],
|
||||
}
|
||||
def test_wait_sriov_vf_been_created(self):
|
||||
pf_name = _test_nic_name()
|
||||
desired_state = {
|
||||
Interface.KEY: [
|
||||
{
|
||||
Interface.NAME: pf_name,
|
||||
Ethernet.CONFIG_SUBTREE: {
|
||||
Ethernet.SRIOV_SUBTREE: {Ethernet.SRIOV.TOTAL_VFS: 2}
|
||||
},
|
||||
}
|
||||
]
|
||||
}
|
||||
try:
|
||||
libnmstate.apply(desired_state)
|
||||
assertlib.assert_state_match(desired_state)
|
||||
current_state = statelib.show_only(
|
||||
(f"{pf_name}v0", f"{pf_name}v1")
|
||||
)
|
||||
assert len(current_state[Interface.KEY]) == 2
|
||||
|
||||
finally:
|
||||
desired_state[Interface.KEY][0][
|
||||
Interface.STATE
|
||||
] = InterfaceState.ABSENT
|
||||
libnmstate.apply(desired_state)
|
||||
|
||||
def test_wait_sriov_vf_been_deleted_when_total_vfs_decrease(self):
|
||||
pf_name = _test_nic_name()
|
||||
desired_state = {
|
||||
Interface.KEY: [
|
||||
{
|
||||
Interface.NAME: pf_name,
|
||||
Ethernet.CONFIG_SUBTREE: {
|
||||
Ethernet.SRIOV_SUBTREE: {Ethernet.SRIOV.TOTAL_VFS: 2}
|
||||
},
|
||||
}
|
||||
]
|
||||
}
|
||||
try:
|
||||
libnmstate.apply(desired_state)
|
||||
assertlib.assert_state_match(desired_state)
|
||||
current_state = statelib.show_only(
|
||||
(f"{pf_name}v0", f"{pf_name}v1")
|
||||
)
|
||||
assert len(current_state[Interface.KEY]) == 2
|
||||
|
||||
desired_state[Interface.KEY][0][Ethernet.CONFIG_SUBTREE][
|
||||
Ethernet.SRIOV_SUBTREE
|
||||
][Ethernet.SRIOV.TOTAL_VFS] = 1
|
||||
libnmstate.apply(desired_state)
|
||||
assertlib.assert_state_match(desired_state)
|
||||
current_state = statelib.show_only(
|
||||
(f"{pf_name}v0", f"{pf_name}v1")
|
||||
)
|
||||
assert len(current_state[Interface.KEY]) == 1
|
||||
|
||||
finally:
|
||||
desired_state[Interface.KEY][0][
|
||||
Interface.STATE
|
||||
] = InterfaceState.ABSENT
|
||||
libnmstate.apply(desired_state)
|
||||
|
||||
def test_sriov_vf_vlan_id_and_qos(self):
|
||||
pf_name = _test_nic_name()
|
||||
desired_state = {
|
||||
Interface.KEY: [
|
||||
{
|
||||
Interface.NAME: pf_name,
|
||||
Ethernet.CONFIG_SUBTREE: {
|
||||
Ethernet.SRIOV_SUBTREE: {
|
||||
Ethernet.SRIOV.TOTAL_VFS: 2,
|
||||
Ethernet.SRIOV.VFS_SUBTREE: [
|
||||
{
|
||||
Ethernet.SRIOV.VFS.ID: 0,
|
||||
Ethernet.SRIOV.VFS.VLAN_ID: 100,
|
||||
Ethernet.SRIOV.VFS.QOS: 5,
|
||||
},
|
||||
{
|
||||
Ethernet.SRIOV.VFS.ID: 1,
|
||||
Ethernet.SRIOV.VFS.VLAN_ID: 102,
|
||||
Ethernet.SRIOV.VFS.QOS: 6,
|
||||
},
|
||||
],
|
||||
}
|
||||
},
|
||||
}
|
||||
]
|
||||
}
|
||||
try:
|
||||
libnmstate.apply(desired_state)
|
||||
finally:
|
||||
desired_state[Interface.KEY][0][
|
||||
Interface.STATE
|
||||
] = InterfaceState.ABSENT
|
||||
libnmstate.apply(desired_state)
|
||||
|
||||
def test_refer_vf_using_pf_name_and_vf_id(self, sriov_interface):
|
||||
pf_name = _test_nic_name()
|
||||
desired_state = {
|
||||
Interface.KEY: [
|
||||
{
|
||||
Interface.NAME: f"sriov:{pf_name}:0",
|
||||
Interface.TYPE: InterfaceType.ETHERNET,
|
||||
Interface.MTU: 1280,
|
||||
},
|
||||
}
|
||||
]
|
||||
}
|
||||
try:
|
||||
libnmstate.apply(desired_state)
|
||||
finally:
|
||||
desired_state[Interface.KEY][0][
|
||||
Interface.STATE
|
||||
] = InterfaceState.ABSENT
|
||||
libnmstate.apply(desired_state)
|
||||
{
|
||||
Interface.NAME: f"sriov:{pf_name}:1",
|
||||
Interface.TYPE: InterfaceType.ETHERNET,
|
||||
Interface.MTU: 1281,
|
||||
},
|
||||
]
|
||||
}
|
||||
expected_state = copy.deepcopy(desired_state)
|
||||
expected_state[Interface.KEY][0][Interface.NAME] = find_vf_iface_name(
|
||||
pf_name, 0
|
||||
)
|
||||
expected_state[Interface.KEY][1][Interface.NAME] = find_vf_iface_name(
|
||||
pf_name, 1
|
||||
)
|
||||
try:
|
||||
libnmstate.apply(desired_state)
|
||||
assertlib.assert_state_match(expected_state)
|
||||
finally:
|
||||
libnmstate.apply(
|
||||
{
|
||||
Interface.KEY: [
|
||||
{
|
||||
Interface.NAME: expected_state[Interface.KEY][0][
|
||||
Interface.NAME
|
||||
],
|
||||
Interface.STATE: InterfaceState.ABSENT,
|
||||
},
|
||||
{
|
||||
Interface.NAME: expected_state[Interface.KEY][0][
|
||||
Interface.NAME
|
||||
],
|
||||
Interface.STATE: InterfaceState.ABSENT,
|
||||
},
|
||||
]
|
||||
}
|
||||
)
|
||||
|
||||
def test_refer_vf_using_pf_name_and_vf_id_bond(self, sriov_interface):
|
||||
pf_name = _test_nic_name()
|
||||
with bond_interface(
|
||||
TEST_BOND, [f"sriov:{pf_name}:0", f"sriov:{pf_name}:1"]
|
||||
) as expected_state:
|
||||
expected_state[Interface.KEY][0][Bond.CONFIG_SUBTREE][
|
||||
Bond.PORT
|
||||
] = [
|
||||
find_vf_iface_name(pf_name, 0),
|
||||
find_vf_iface_name(pf_name, 1),
|
||||
]
|
||||
assertlib.assert_state_match(expected_state)
|
||||
|
||||
def test_refer_vf_using_pf_name_and_vf_id_linux_bridge(
|
||||
self, sriov_interface
|
||||
):
|
||||
pf_name = _test_nic_name()
|
||||
bridge_state = create_bridge_subtree_state()
|
||||
add_port_to_bridge(bridge_state, f"sriov:{pf_name}:0")
|
||||
add_port_to_bridge(bridge_state, f"sriov:{pf_name}:1")
|
||||
# Disable STP to avoid topology changes and the consequence link change
|
||||
options_subtree = bridge_state[LinuxBridge.OPTIONS_SUBTREE]
|
||||
options_subtree[LinuxBridge.STP_SUBTREE][
|
||||
LinuxBridge.STP.ENABLED
|
||||
] = False
|
||||
with linux_bridge(TEST_BRIDGE, bridge_state) as expected_state:
|
||||
expected_state[Interface.KEY][0][LinuxBridge.CONFIG_SUBTREE][
|
||||
LinuxBridge.PORT_SUBTREE
|
||||
] = [
|
||||
{
|
||||
LinuxBridge.Port.NAME: port_name,
|
||||
}
|
||||
for port_name in (
|
||||
find_vf_iface_name(pf_name, 0),
|
||||
find_vf_iface_name(pf_name, 1),
|
||||
)
|
||||
]
|
||||
assertlib.assert_state_match(expected_state)
|
||||
|
||||
def test_refer_vf_using_pf_name_and_vf_id_ovs_bridge(
|
||||
self, sriov_interface
|
||||
):
|
||||
pf_name = _test_nic_name()
|
||||
with ovs_bridge(
|
||||
TEST_BRIDGE,
|
||||
[f"sriov:{pf_name}:0", f"sriov:{pf_name}:1"],
|
||||
TEST_OVS_IFACE,
|
||||
) as expected_state:
|
||||
expected_state[Interface.KEY][0][OVSBridge.CONFIG_SUBTREE][
|
||||
OVSBridge.PORT_SUBTREE
|
||||
] = [
|
||||
{
|
||||
OVSBridge.Port.NAME: port_name,
|
||||
}
|
||||
for port_name in (
|
||||
find_vf_iface_name(pf_name, 0),
|
||||
find_vf_iface_name(pf_name, 1),
|
||||
TEST_OVS_IFACE,
|
||||
)
|
||||
]
|
||||
assertlib.assert_state_match(expected_state)
|
||||
|
||||
def test_refer_vf_using_pf_name_and_vf_id_ovs_bond(self, sriov_interface):
|
||||
pf_name = _test_nic_name()
|
||||
with ovs_bridge_bond(
|
||||
TEST_BRIDGE,
|
||||
{TEST_BOND: [f"sriov:{pf_name}:0", f"sriov:{pf_name}:1"]},
|
||||
TEST_OVS_IFACE,
|
||||
) as expected_state:
|
||||
ports = [
|
||||
{OVSBridge.Port.LinkAggregation.Port.NAME: port_name}
|
||||
for port_name in (
|
||||
find_vf_iface_name(pf_name, 0),
|
||||
find_vf_iface_name(pf_name, 1),
|
||||
)
|
||||
]
|
||||
expected_state[Interface.KEY][0][OVSBridge.CONFIG_SUBTREE][
|
||||
OVSBridge.PORT_SUBTREE
|
||||
] = [
|
||||
{
|
||||
OVSBridge.Port.NAME: TEST_BOND,
|
||||
OVSBridge.Port.LINK_AGGREGATION_SUBTREE: {
|
||||
OVSBridge.Port.LinkAggregation.PORT_SUBTREE: ports,
|
||||
},
|
||||
},
|
||||
{OVSBridge.Port.NAME: TEST_OVS_IFACE},
|
||||
]
|
||||
assertlib.assert_state_match(expected_state)
|
||||
|
@ -155,3 +155,25 @@ def _set_ifaces_state(ifaces, state):
|
||||
for iface in ifaces:
|
||||
iface[Interface.STATE] = state
|
||||
return ifaces
|
||||
|
||||
|
||||
@contextmanager
|
||||
def ovs_bridge(br_name, sys_port_names, internal_port_name):
|
||||
bridge = Bridge(br_name)
|
||||
bridge.add_internal_port(internal_port_name)
|
||||
for sys_port_name in sys_port_names:
|
||||
bridge.add_system_port(sys_port_name)
|
||||
with bridge.create():
|
||||
yield bridge.state
|
||||
|
||||
|
||||
@contextmanager
|
||||
# The bond_ports should be in the format of
|
||||
# dict(bond_port_name, bond_sys_port_names)
|
||||
def ovs_bridge_bond(br_name, bond_ports, internal_port_name):
|
||||
bridge = Bridge(br_name)
|
||||
bridge.add_internal_port(internal_port_name)
|
||||
for bond_port_name, bond_sys_port_names in bond_ports.items():
|
||||
bridge.add_link_aggregation_port(bond_port_name, bond_sys_port_names)
|
||||
with bridge.create():
|
||||
yield bridge.state
|
||||
|
Loading…
x
Reference in New Issue
Block a user