1 В избранное 0 Ответвления 0

OSCHINA-MIRROR/openeuler-stratovirt

Присоединиться к Gitlife
Откройте для себя и примите участие в публичных проектах с открытым исходным кодом с участием более 10 миллионов разработчиков. Приватные репозитории также полностью бесплатны :)
Присоединиться бесплатно
В этом репозитории не указан файл с открытой лицензией (LICENSE). При использовании обратитесь к конкретному описанию проекта и его зависимостям в коде.
Клонировать/Скачать
network.rs 27 КБ
Копировать Редактировать Исходные данные Просмотреть построчно История
MJY-HUST Отправлено 3 лет назад 8a3011b
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833
// Copyright (c) 2020 Huawei Technologies Co.,Ltd. All rights reserved.
//
// StratoVirt is licensed under Mulan PSL v2.
// You can use this software according to the terms and conditions of the Mulan
// PSL v2.
// You may obtain a copy of Mulan PSL v2 at:
// http://license.coscl.org.cn/MulanPSL2
// THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY
// KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO
// NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
// See the Mulan PSL v2 for more details.
use anyhow::{anyhow, bail, Result};
use serde::{Deserialize, Serialize};
use super::{error::ConfigError, pci_args_check};
use crate::config::get_chardev_socket_path;
use crate::config::{
CmdParser, ConfigCheck, ExBool, VmConfig, MAX_PATH_LENGTH, MAX_STRING_LENGTH, MAX_VIRTIO_QUEUE,
};
use crate::qmp::{qmp_schema, QmpChannel};
const MAC_ADDRESS_LENGTH: usize = 17;
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct NetDevcfg {
pub id: String,
pub tap_fds: Option<Vec<i32>>,
pub vhost_type: Option<String>,
pub vhost_fds: Option<Vec<i32>>,
pub ifname: String,
pub queues: u16,
pub chardev: Option<String>,
}
impl Default for NetDevcfg {
fn default() -> Self {
NetDevcfg {
id: "".to_string(),
tap_fds: None,
vhost_type: None,
vhost_fds: None,
ifname: "".to_string(),
queues: 2,
chardev: None,
}
}
}
impl ConfigCheck for NetDevcfg {
fn check(&self) -> Result<()> {
if self.id.len() > MAX_STRING_LENGTH {
return Err(anyhow!(ConfigError::StringLengthTooLong(
"id".to_string(),
MAX_STRING_LENGTH
)));
}
if self.ifname.len() > MAX_STRING_LENGTH {
return Err(anyhow!(ConfigError::StringLengthTooLong(
self.ifname.clone(),
MAX_STRING_LENGTH
)));
}
if let Some(vhost_type) = self.vhost_type.as_ref() {
if vhost_type != "vhost-kernel" && vhost_type != "vhost-user" {
return Err(anyhow!(ConfigError::UnknownVhostType));
}
}
if !is_netdev_queues_valid(self.queues) {
return Err(anyhow!(ConfigError::IllegalValue(
"number queues of net device".to_string(),
1,
true,
MAX_VIRTIO_QUEUE as u64 / 2,
true,
)));
}
Ok(())
}
}
/// Config struct for network
/// Contains network device config, such as `host_dev_name`, `mac`...
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(deny_unknown_fields)]
pub struct NetworkInterfaceConfig {
pub id: String,
pub host_dev_name: String,
pub mac: Option<String>,
pub tap_fds: Option<Vec<i32>>,
pub vhost_type: Option<String>,
pub vhost_fds: Option<Vec<i32>>,
pub iothread: Option<String>,
pub queues: u16,
pub mq: bool,
pub socket_path: Option<String>,
}
impl NetworkInterfaceConfig {
pub fn set_mac(&mut self, mac_addr: String) {
self.mac = Some(mac_addr);
}
}
impl Default for NetworkInterfaceConfig {
fn default() -> Self {
NetworkInterfaceConfig {
id: "".to_string(),
host_dev_name: "".to_string(),
mac: None,
tap_fds: None,
vhost_type: None,
vhost_fds: None,
iothread: None,
queues: 2,
mq: false,
socket_path: None,
}
}
}
impl ConfigCheck for NetworkInterfaceConfig {
fn check(&self) -> Result<()> {
if self.id.len() > MAX_STRING_LENGTH {
return Err(anyhow!(ConfigError::StringLengthTooLong(
"id".to_string(),
MAX_STRING_LENGTH
)));
}
if self.host_dev_name.len() > MAX_STRING_LENGTH {
return Err(anyhow!(ConfigError::StringLengthTooLong(
self.host_dev_name.clone(),
MAX_STRING_LENGTH,
)));
}
if self.mac.is_some() && !check_mac_address(self.mac.as_ref().unwrap()) {
return Err(anyhow!(ConfigError::MacFormatError));
}
if self.iothread.is_some() && self.iothread.as_ref().unwrap().len() > MAX_STRING_LENGTH {
return Err(anyhow!(ConfigError::StringLengthTooLong(
"iothread name".to_string(),
MAX_STRING_LENGTH,
)));
}
if self.socket_path.is_some() && self.socket_path.as_ref().unwrap().len() > MAX_PATH_LENGTH
{
return Err(anyhow!(ConfigError::StringLengthTooLong(
"socket path".to_string(),
MAX_PATH_LENGTH
)));
}
Ok(())
}
}
fn parse_fds(cmd_parser: &CmdParser, name: &str) -> Result<Option<Vec<i32>>> {
if let Some(fds) = cmd_parser.get_value::<String>(name)? {
let mut raw_fds = Vec::new();
for fd in fds.split(':').collect::<Vec<&str>>().iter() {
raw_fds.push(
(*fd)
.parse::<i32>()
.map_err(|_| anyhow!("Failed to parse fds"))?,
);
}
Ok(Some(raw_fds))
} else {
Ok(None)
}
}
fn parse_netdev(cmd_parser: CmdParser) -> Result<NetDevcfg> {
let mut net = NetDevcfg::default();
let netdev_type = if let Some(netdev_type) = cmd_parser.get_value::<String>("")? {
netdev_type
} else {
"".to_string()
};
if netdev_type.ne("tap") && netdev_type.ne("vhost-user") {
bail!("Unsupported netdev type: {:?}", &netdev_type);
}
if let Some(net_id) = cmd_parser.get_value::<String>("id")? {
net.id = net_id;
} else {
return Err(anyhow!(ConfigError::FieldIsMissing("id", "netdev")));
}
if let Some(ifname) = cmd_parser.get_value::<String>("ifname")? {
net.ifname = ifname;
}
if let Some(queue_pairs) = cmd_parser.get_value::<u16>("queues")? {
let queues = queue_pairs * 2;
if !is_netdev_queues_valid(queues) {
return Err(anyhow!(ConfigError::IllegalValue(
"number queues of net device".to_string(),
1,
true,
MAX_VIRTIO_QUEUE as u64 / 2,
true,
)));
}
net.queues = queues;
}
if let Some(tap_fd) = parse_fds(&cmd_parser, "fd")? {
net.tap_fds = Some(tap_fd);
} else if let Some(tap_fds) = parse_fds(&cmd_parser, "fds")? {
net.tap_fds = Some(tap_fds);
}
if let Some(fds) = &net.tap_fds {
let fds_num = (fds.len() * 2) as u16;
if fds_num > net.queues {
net.queues = fds_num;
}
}
if let Some(vhost) = cmd_parser.get_value::<ExBool>("vhost")? {
if vhost.into() {
net.vhost_type = Some(String::from("vhost-kernel"));
}
} else if netdev_type.eq("vhost-user") {
net.vhost_type = Some(String::from("vhost-user"));
}
if let Some(chardev) = cmd_parser.get_value::<String>("chardev")? {
net.chardev = Some(chardev);
}
if let Some(vhost_fd) = parse_fds(&cmd_parser, "vhostfd")? {
net.vhost_fds = Some(vhost_fd);
} else if let Some(vhost_fds) = parse_fds(&cmd_parser, "vhostfds")? {
net.vhost_fds = Some(vhost_fds);
}
if let Some(fds) = &net.vhost_fds {
let fds_num = (fds.len() * 2) as u16;
if fds_num > net.queues {
net.queues = fds_num;
}
}
if net.vhost_fds.is_some() && net.vhost_type.is_none() {
bail!("Argument \'vhostfd\' is not needed for virtio-net device");
}
if net.tap_fds.is_none() && net.ifname.eq("") && netdev_type.ne("vhost-user") {
bail!("Tap device is missing, use \'ifname\' or \'fd\' to configure a tap device");
}
net.check()?;
Ok(net)
}
pub fn parse_net(vm_config: &mut VmConfig, net_config: &str) -> Result<NetworkInterfaceConfig> {
let mut cmd_parser = CmdParser::new("virtio-net");
cmd_parser
.push("")
.push("id")
.push("netdev")
.push("mq")
.push("vectors")
.push("bus")
.push("addr")
.push("multifunction")
.push("mac")
.push("iothread");
cmd_parser.parse(net_config)?;
pci_args_check(&cmd_parser)?;
let mut netdevinterfacecfg = NetworkInterfaceConfig::default();
let netdev = if let Some(devname) = cmd_parser.get_value::<String>("netdev")? {
devname
} else {
return Err(anyhow!(ConfigError::FieldIsMissing("netdev", "net")));
};
let netid = if let Some(id) = cmd_parser.get_value::<String>("id")? {
id
} else {
"".to_string()
};
if let Some(mq) = cmd_parser.get_value::<ExBool>("mq")? {
netdevinterfacecfg.mq = mq.inner;
}
netdevinterfacecfg.iothread = cmd_parser.get_value::<String>("iothread")?;
netdevinterfacecfg.mac = cmd_parser.get_value::<String>("mac")?;
if let Some(netcfg) = &vm_config.netdevs.remove(&netdev) {
netdevinterfacecfg.id = netid;
netdevinterfacecfg.host_dev_name = netcfg.ifname.clone();
netdevinterfacecfg.tap_fds = netcfg.tap_fds.clone();
netdevinterfacecfg.vhost_fds = netcfg.vhost_fds.clone();
netdevinterfacecfg.vhost_type = netcfg.vhost_type.clone();
netdevinterfacecfg.queues = netcfg.queues;
if let Some(chardev) = &netcfg.chardev {
netdevinterfacecfg.socket_path = Some(get_chardev_socket_path(chardev, vm_config)?);
}
} else {
bail!("Netdev: {:?} not found for net device", &netdev);
}
netdevinterfacecfg.check()?;
Ok(netdevinterfacecfg)
}
pub fn get_netdev_config(args: Box<qmp_schema::NetDevAddArgument>) -> Result<NetDevcfg> {
let mut config = NetDevcfg {
id: args.id,
tap_fds: None,
vhost_type: None,
vhost_fds: None,
ifname: String::new(),
queues: args.queues.unwrap_or(1) * 2,
chardev: args.chardev,
};
if let Some(fds) = args.fds {
let netdev_fd = if fds.contains(':') {
let col: Vec<_> = fds.split(':').collect();
String::from(col[col.len() - 1])
} else {
String::from(&fds)
};
if let Some(fd_num) = QmpChannel::get_fd(&netdev_fd) {
config.tap_fds = Some(vec![fd_num]);
} else {
// try to convert string to RawFd
let fd_num = match netdev_fd.parse::<i32>() {
Ok(fd) => fd,
_ => {
bail!("Failed to parse fd: {}", netdev_fd);
}
};
config.tap_fds = Some(vec![fd_num]);
}
} else if let Some(if_name) = args.if_name {
config.ifname = if_name;
}
let netdev_type = if let Some(net_type) = args.net_type {
net_type
} else {
"".to_string()
};
if let Some(vhost) = args.vhost {
match vhost.parse::<ExBool>() {
Ok(vhost) => {
if vhost.into() {
if netdev_type.ne("vhost-user") {
config.vhost_type = Some(String::from("vhost-kernel"));
} else {
bail!("vhost-user netdev does not support \"vhost\" option");
}
}
}
Err(_) => {
bail!("Failed to get vhost type: {}", vhost);
}
};
} else if netdev_type.eq("vhost-user") {
config.vhost_type = Some(netdev_type.clone());
}
if let Some(vhostfd) = args.vhostfds {
match vhostfd.parse::<i32>() {
Ok(fd) => config.vhost_fds = Some(vec![fd]),
Err(_e) => {
bail!("Failed to get vhost fd: {}", vhostfd);
}
};
}
if config.vhost_fds.is_some() && config.vhost_type.is_none() {
bail!("Argument \'vhostfd\' is not needed for virtio-net device");
}
if config.tap_fds.is_none() && config.ifname.eq("") && netdev_type.ne("vhost-user") {
bail!("Tap device is missing, use \'ifname\' or \'fd\' to configure a tap device");
}
Ok(config)
}
impl VmConfig {
pub fn add_netdev(&mut self, netdev_config: &str) -> Result<()> {
let mut cmd_parser = CmdParser::new("netdev");
cmd_parser
.push("")
.push("id")
.push("fd")
.push("fds")
.push("vhost")
.push("ifname")
.push("vhostfd")
.push("vhostfds")
.push("queues")
.push("chardev");
cmd_parser.parse(netdev_config)?;
let drive_cfg = parse_netdev(cmd_parser)?;
self.add_netdev_with_config(drive_cfg)
}
pub fn add_netdev_with_config(&mut self, conf: NetDevcfg) -> Result<()> {
let netdev_id = conf.id.clone();
if self.netdevs.get(&netdev_id).is_none() {
self.netdevs.insert(netdev_id, conf);
} else {
bail!("Netdev {:?} has been added", netdev_id);
}
Ok(())
}
pub fn del_netdev_by_id(&mut self, id: &str) -> Result<()> {
if self.netdevs.get(id).is_some() {
self.netdevs.remove(id);
} else {
bail!("Netdev {} not found", id);
}
Ok(())
}
}
fn check_mac_address(mac: &str) -> bool {
if mac.len() != MAC_ADDRESS_LENGTH {
return false;
}
let mac_vec: Vec<&str> = mac.split(':').collect();
if mac_vec.len() != 6 {
return false;
}
let bit_list = [
'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f', 'A', 'B',
'C', 'D', 'E', 'F',
];
for mac_bit in mac_vec {
if mac_bit.len() != 2 {
return false;
}
let mut mac_bit_char = mac_bit.chars();
if !bit_list.contains(&mac_bit_char.next().unwrap())
|| !bit_list.contains(&mac_bit_char.next().unwrap())
{
return false;
}
}
true
}
fn is_netdev_queues_valid(queues: u16) -> bool {
queues >= 1 && queues <= MAX_VIRTIO_QUEUE as u16
}
#[cfg(test)]
mod tests {
use crate::config::get_pci_bdf;
use super::*;
#[test]
fn test_network_config_cmdline_parser() {
let mut vm_config = VmConfig::default();
assert!(vm_config.add_netdev("tap,id=eth0,ifname=tap0").is_ok());
let net_cfg_res = parse_net(
&mut vm_config,
"virtio-net-device,id=net0,netdev=eth0,iothread=iothread0",
);
assert!(net_cfg_res.is_ok());
let network_configs = net_cfg_res.unwrap();
assert_eq!(network_configs.id, "net0");
assert_eq!(network_configs.host_dev_name, "tap0");
assert_eq!(network_configs.iothread, Some("iothread0".to_string()));
assert!(network_configs.mac.is_none());
assert!(network_configs.tap_fds.is_none());
assert!(network_configs.vhost_type.is_none());
assert!(network_configs.vhost_fds.is_none());
let mut vm_config = VmConfig::default();
assert!(vm_config
.add_netdev("tap,id=eth1,ifname=tap1,vhost=on,vhostfd=4")
.is_ok());
let net_cfg_res = parse_net(
&mut vm_config,
"virtio-net-device,id=net1,netdev=eth1,mac=12:34:56:78:9A:BC",
);
assert!(net_cfg_res.is_ok());
let network_configs = net_cfg_res.unwrap();
assert_eq!(network_configs.id, "net1");
assert_eq!(network_configs.host_dev_name, "tap1");
assert_eq!(network_configs.mac, Some(String::from("12:34:56:78:9A:BC")));
assert!(network_configs.tap_fds.is_none());
assert_eq!(
network_configs.vhost_type,
Some(String::from("vhost-kernel"))
);
assert_eq!(network_configs.vhost_fds, Some(vec![4]));
let mut vm_config = VmConfig::default();
assert!(vm_config.add_netdev("tap,id=eth1,fd=35").is_ok());
let net_cfg_res = parse_net(&mut vm_config, "virtio-net-device,id=net1,netdev=eth1");
assert!(net_cfg_res.is_ok());
let network_configs = net_cfg_res.unwrap();
assert_eq!(network_configs.id, "net1");
assert_eq!(network_configs.host_dev_name, "");
assert_eq!(network_configs.tap_fds, Some(vec![35]));
let mut vm_config = VmConfig::default();
assert!(vm_config
.add_netdev("tap,id=eth1,ifname=tap1,vhost=on,vhostfd=4")
.is_ok());
let net_cfg_res = parse_net(
&mut vm_config,
"virtio-net-device,id=net1,netdev=eth2,mac=12:34:56:78:9A:BC",
);
assert!(net_cfg_res.is_err());
let mut vm_config = VmConfig::default();
assert!(vm_config.add_netdev("tap,id=eth1,fd=35").is_ok());
let net_cfg_res = parse_net(&mut vm_config, "virtio-net-device,id=net1,netdev=eth3");
assert!(net_cfg_res.is_err());
// multi queue testcases
let mut vm_config = VmConfig::default();
assert!(vm_config
.add_netdev("tap,id=eth0,ifname=tap0,queues=4")
.is_ok());
let net_cfg_res = parse_net(
&mut vm_config,
"virtio-net-device,id=net0,netdev=eth0,iothread=iothread0,mq=on,vectors=6",
);
assert!(net_cfg_res.is_ok());
let network_configs = net_cfg_res.unwrap();
assert_eq!(network_configs.queues, 8);
assert_eq!(network_configs.mq, true);
let mut vm_config = VmConfig::default();
assert!(vm_config
.add_netdev("tap,id=eth0,fds=34:35:36:37:38")
.is_ok());
let net_cfg_res = parse_net(
&mut vm_config,
"virtio-net-device,id=net0,netdev=eth0,iothread=iothread0,mq=off,vectors=12",
);
assert!(net_cfg_res.is_ok());
let network_configs = net_cfg_res.unwrap();
assert_eq!(network_configs.queues, 10);
assert_eq!(network_configs.tap_fds, Some(vec![34, 35, 36, 37, 38]));
assert_eq!(network_configs.mq, false);
let mut vm_config = VmConfig::default();
assert!(vm_config
.add_netdev("tap,id=eth0,fds=34:35:36:37:38,vhost=on,vhostfds=39:40:41:42:43")
.is_ok());
let net_cfg_res = parse_net(
&mut vm_config,
"virtio-net-device,id=net0,netdev=eth0,iothread=iothread0,mq=off,vectors=12",
);
assert!(net_cfg_res.is_ok());
let network_configs = net_cfg_res.unwrap();
assert_eq!(network_configs.queues, 10);
assert_eq!(network_configs.vhost_fds, Some(vec![39, 40, 41, 42, 43]));
assert_eq!(network_configs.mq, false);
}
#[test]
fn test_pci_network_config_cmdline_parser() {
let mut vm_config = VmConfig::default();
assert!(vm_config
.add_netdev("tap,id=eth1,ifname=tap1,vhost=on,vhostfd=4")
.is_ok());
let net_cfg =
"virtio-net-pci,id=net1,netdev=eth1,bus=pcie.0,addr=0x1.0x2,mac=12:34:56:78:9A:BC";
let net_cfg_res = parse_net(&mut vm_config, net_cfg);
assert!(net_cfg_res.is_ok());
let network_configs = net_cfg_res.unwrap();
assert_eq!(network_configs.id, "net1");
assert_eq!(network_configs.host_dev_name, "tap1");
assert_eq!(network_configs.mac, Some(String::from("12:34:56:78:9A:BC")));
assert!(network_configs.tap_fds.is_none());
assert_eq!(
network_configs.vhost_type,
Some(String::from("vhost-kernel"))
);
assert_eq!(network_configs.vhost_fds.unwrap()[0], 4);
let pci_bdf = get_pci_bdf(net_cfg);
assert!(pci_bdf.is_ok());
let pci = pci_bdf.unwrap();
assert_eq!(pci.bus, "pcie.0".to_string());
assert_eq!(pci.addr, (1, 2));
let net_cfg_res = parse_net(&mut vm_config, net_cfg);
assert!(net_cfg_res.is_err());
let mut vm_config = VmConfig::default();
assert!(vm_config
.add_netdev("tap,id=eth1,ifname=tap1,vhost=on,vhostfd=4")
.is_ok());
let net_cfg =
"virtio-net-pci,id=net1,netdev=eth1,bus=pcie.0,addr=0x1.0x2,mac=12:34:56:78:9A:BC,multifunction=on";
assert!(parse_net(&mut vm_config, net_cfg).is_ok());
// For vhost-user net
assert!(vm_config.add_netdev("vhost-user,id=netdevid").is_ok());
let net_cfg =
"virtio-net-pci,id=netid,netdev=netdevid,bus=pcie.0,addr=0x2.0x0,mac=12:34:56:78:9A:BC";
let net_cfg_res = parse_net(&mut vm_config, net_cfg);
assert!(net_cfg_res.is_ok());
let network_configs = net_cfg_res.unwrap();
assert_eq!(network_configs.id, "netid");
assert_eq!(network_configs.vhost_type, Some("vhost-user".to_string()));
assert_eq!(network_configs.mac, Some("12:34:56:78:9A:BC".to_string()));
assert!(vm_config
.add_netdev("vhost-user,id=netdevid2,chardev=chardevid2")
.is_ok());
let net_cfg =
"virtio-net-pci,id=netid2,netdev=netdevid2,bus=pcie.0,addr=0x2.0x0,mac=12:34:56:78:9A:BC";
let net_cfg_res = parse_net(&mut vm_config, net_cfg);
assert!(net_cfg_res.is_err());
}
#[test]
fn test_netdev_config_check() {
let mut netdev_conf = NetDevcfg::default();
for _ in 0..MAX_STRING_LENGTH {
netdev_conf.id += "A";
}
assert!(netdev_conf.check().is_ok());
// Overflow
netdev_conf.id += "A";
assert!(netdev_conf.check().is_err());
let mut netdev_conf = NetDevcfg::default();
for _ in 0..MAX_STRING_LENGTH {
netdev_conf.ifname += "A";
}
assert!(netdev_conf.check().is_ok());
// Overflow
netdev_conf.ifname += "A";
assert!(netdev_conf.check().is_err());
let mut netdev_conf = NetDevcfg::default();
netdev_conf.vhost_type = None;
assert!(netdev_conf.check().is_ok());
netdev_conf.vhost_type = Some(String::from("vhost-kernel"));
assert!(netdev_conf.check().is_ok());
netdev_conf.vhost_type = Some(String::from("vhost-"));
assert!(netdev_conf.check().is_err());
}
#[test]
fn test_add_netdev_with_different_queues() {
let mut vm_config = VmConfig::default();
let set_queues = |q: u16| {
format!(
"vhost-user,id=netdevid{num},chardev=chardevid,queues={num}",
num = q.to_string()
)
};
assert!(vm_config.add_netdev(&set_queues(0)).is_err());
assert!(vm_config.add_netdev(&set_queues(1)).is_ok());
assert!(vm_config
.add_netdev(&set_queues(MAX_VIRTIO_QUEUE as u16 / 2))
.is_ok());
assert!(vm_config
.add_netdev(&set_queues(MAX_VIRTIO_QUEUE as u16 / 2 + 1))
.is_err());
}
#[test]
fn test_add_netdev_with_config() {
let mut vm_config = VmConfig::default();
let netdev_list = ["netdev-0", "netdev-1", "netdev-2"];
for id in netdev_list.iter() {
let mut net_conf = NetDevcfg::default();
net_conf.id = String::from(*id);
assert!(vm_config.add_netdev_with_config(net_conf).is_ok());
let netdev = vm_config.netdevs.get(*id).unwrap();
assert_eq!(*id, netdev.id);
}
let mut net_conf = NetDevcfg::default();
net_conf.id = String::from("netdev-0");
assert!(vm_config.add_netdev_with_config(net_conf).is_err());
}
#[test]
fn test_del_netdev_by_id() {
let mut vm_config = VmConfig::default();
assert!(vm_config.del_netdev_by_id("netdev-0").is_err());
let netdev_list = ["netdev-0", "netdev-1", "netdev-2"];
for id in netdev_list.iter() {
let mut net_conf = NetDevcfg::default();
net_conf.id = String::from(*id);
assert!(vm_config.add_netdev_with_config(net_conf).is_ok());
let netdev = vm_config.netdevs.get(*id).unwrap();
assert_eq!(*id, netdev.id);
}
for id in netdev_list.iter() {
let mut net_conf = NetDevcfg::default();
net_conf.id = String::from(*id);
assert!(vm_config.netdevs.get(*id).is_some());
assert!(vm_config.del_netdev_by_id(*id).is_ok());
assert!(vm_config.netdevs.get(*id).is_none());
}
}
fn create_netdev_add(
id: String,
if_name: Option<String>,
fds: Option<String>,
vhost: Option<String>,
vhostfds: Option<String>,
) -> Box<qmp_schema::NetDevAddArgument> {
Box::new(qmp_schema::NetDevAddArgument {
id,
if_name,
fds,
dnssearch: None,
net_type: None,
vhost,
vhostfds,
ifname: None,
downscript: None,
script: None,
queues: None,
chardev: None,
})
}
#[test]
fn test_get_netdev_config() {
// Invalid vhost
let netdev_add = create_netdev_add(
String::from("netdev"),
None,
None,
Some(String::from("1")),
None,
);
let net_cfg = get_netdev_config(netdev_add);
assert!(net_cfg.is_err());
// Invalid vhost fd
let netdev_add = create_netdev_add(
String::from("netdev"),
None,
None,
None,
Some(String::from("999999999999999999999")),
);
let net_cfg = get_netdev_config(netdev_add);
assert!(net_cfg.is_err());
// No need to config vhost fd
let netdev_add = create_netdev_add(
String::from("netdev"),
None,
None,
None,
Some(String::from("55")),
);
let net_cfg = get_netdev_config(netdev_add);
assert!(net_cfg.is_err());
// No ifname or fd
let netdev_add = create_netdev_add(
String::from("netdev"),
None,
None,
Some(String::from("on")),
Some(String::from("55")),
);
let net_cfg = get_netdev_config(netdev_add);
assert!(net_cfg.is_err());
let netdev_add = create_netdev_add(
String::from("netdev"),
Some(String::from("tap0")),
None,
None,
None,
);
let net_cfg = get_netdev_config(netdev_add);
assert!(net_cfg.is_ok());
assert_eq!(net_cfg.unwrap().ifname, "tap0");
let netdev_add = create_netdev_add(
String::from("netdev"),
Some(String::from("tap0")),
None,
Some(String::from("on")),
None,
);
let net_cfg = get_netdev_config(netdev_add);
assert!(net_cfg.is_ok());
assert_eq!(net_cfg.unwrap().vhost_type.unwrap(), "vhost-kernel");
let netdev_add = create_netdev_add(
String::from("netdev"),
Some(String::from("tap0")),
None,
Some(String::from("on")),
Some(String::from("12")),
);
let net_cfg = get_netdev_config(netdev_add);
assert!(net_cfg.is_ok());
let net_cfg = net_cfg.unwrap();
assert_eq!(net_cfg.vhost_type.unwrap(), "vhost-kernel");
assert_eq!(net_cfg.vhost_fds.unwrap()[0], 12);
}
}

Опубликовать ( 0 )

Вы можете оставить комментарий после Вход в систему

1
https://gitlife.ru/oschina-mirror/openeuler-stratovirt.git
git@gitlife.ru:oschina-mirror/openeuler-stratovirt.git
oschina-mirror
openeuler-stratovirt
openeuler-stratovirt
v2.2.0-rc6