use std::env;
use std::future;
use std::net;
use std::sync::Once;
use futures::TryStreamExt;
use openstack::waiter::Waiter;
use openstack::Refresh;
static INIT: Once = Once::new();
async fn set_up() -> openstack::Cloud {
INIT.call_once(|| {
env_logger::init();
});
openstack::Cloud::from_env()
.await
.expect("Failed to create an identity provider from the environment")
}
#[tokio::test]
async fn test_port_create_update_delete() {
let os = set_up().await;
let network_id = env::var("RUST_OPENSTACK_NETWORK").expect("Missing RUST_OPENSTACK_NETWORK");
let mut port = os
.new_port(network_id.clone())
.with_name("rust-openstack-integration")
.create()
.await
.expect("Could not create port");
assert_eq!(port.name().as_ref().unwrap(), "rust-openstack-integration");
assert!(port.device_id().is_none());
assert!(port.device_owner().is_none());
assert!(port.dns_name().is_none());
assert!(port.dns_domain().is_none());
assert!(!port.fixed_ips().is_empty());
assert!(!port.is_dirty());
assert_eq!(port.status(), openstack::network::NetworkStatus::Down);
port.set_name("rust-openstack-integration-2");
port.extra_dhcp_opts_mut()
.push(openstack::network::PortExtraDhcpOption::new(
"bootfile-name",
"pxelinux.0",
));
assert!(port.is_dirty());
port.save().await.expect("Cannot update port");
assert_eq!(
port.name().as_ref().unwrap(),
"rust-openstack-integration-2"
);
assert_eq!(1, port.extra_dhcp_opts().len());
assert!(!port.is_dirty());
port.refresh().await.expect("Cannot refresh port");
assert_eq!(
port.name().as_ref().unwrap(),
"rust-openstack-integration-2"
);
assert!(!port.is_dirty());
port.set_name("rust-openstack-integration-3");
port.refresh().await.expect("Cannot refresh port");
assert_eq!(
port.name().as_ref().unwrap(),
"rust-openstack-integration-2"
);
assert!(!port.is_dirty());
let mut port_found = os
.find_ports()
.with_network(network_id)
.with_name("rust-openstack-integration-2")
.one()
.await
.expect("Cannot find port by network");
assert_eq!(
port_found.name().as_ref().unwrap(),
"rust-openstack-integration-2"
);
assert!(!port_found.is_dirty());
port.delete()
.await
.expect("Cannot request port deletion")
.wait()
.await
.expect("Port was not deleted");
os.get_port("rust-openstack-integration-2")
.await
.err()
.expect("Port is still present");
port_found
.refresh()
.await
.err()
.expect("Refresh succeeds on deleted port");
}
#[tokio::test]
async fn test_network_create_delete_simple() {
let os = set_up().await;
let network = os
.new_network()
.create()
.await
.expect("Could not create network");
assert!(network.admin_state_up());
assert!(network.dns_domain().is_none());
assert_eq!(network.external(), Some(false));
assert!(!network.shared());
assert!(network.name().is_none());
assert_eq!(network.status(), openstack::network::NetworkStatus::Active);
let cidr = ipnet::Ipv4Net::new(net::Ipv4Addr::new(192, 168, 1, 0), 24)
.unwrap()
.into();
let mut subnet = os
.new_subnet(network.clone(), cidr)
.create()
.await
.expect("Could not create subnet");
assert_eq!(subnet.cidr(), cidr);
assert!(subnet.dhcp_enabled());
assert!(subnet.dns_nameservers().is_empty());
assert_eq!(subnet.ip_version(), openstack::network::IpVersion::V4);
assert!(subnet.name().is_none());
subnet.refresh().await.expect("Cannot refresh subnet");
subnet
.delete()
.await
.expect("Cannot request subnet deletion")
.wait()
.await
.expect("Subnet was not deleted");
network
.delete()
.await
.expect("Cannot request network deletion")
.wait()
.await
.expect("Network was not deleted");
}
#[tokio::test]
async fn test_subnet_update() {
let os = set_up().await;
let network = os
.new_network()
.create()
.await
.expect("Could not create network");
let cidr = ipnet::Ipv4Net::new(net::Ipv4Addr::new(192, 168, 1, 0), 24)
.unwrap()
.into();
let mut subnet = os
.new_subnet(network.clone(), cidr)
.create()
.await
.expect("Could not create subnet");
assert!(!subnet.is_dirty());
subnet.dns_nameservers_mut().push("1.2.3.4".to_string());
assert!(subnet.is_dirty());
subnet.set_name("unused name");
subnet.set_description("unused description");
subnet.refresh().await.expect("Cannot refresh subnet");
assert!(!subnet.is_dirty());
assert!(subnet.dns_nameservers().is_empty());
assert!(subnet.name().is_none());
subnet.dns_nameservers_mut().push("8.8.8.8".to_string());
subnet.set_dhcp_enabled(false);
subnet.set_name("rust-openstack-integration-new");
subnet.set_description("some description");
assert_eq!("8.8.8.8", subnet.dns_nameservers()[0]);
assert!(!subnet.dhcp_enabled());
assert_eq!(
subnet.name().as_ref().unwrap(),
"rust-openstack-integration-new"
);
subnet.save().await.expect("Could not save subnet");
assert!(!subnet.is_dirty());
assert_eq!("8.8.8.8", subnet.dns_nameservers()[0]);
assert!(!subnet.dhcp_enabled());
assert_eq!(
subnet.name().as_ref().unwrap(),
"rust-openstack-integration-new"
);
subnet.refresh().await.expect("Cannot refresh subnet");
assert_eq!("8.8.8.8", subnet.dns_nameservers()[0]);
assert!(!subnet.dhcp_enabled());
assert_eq!(
subnet.name().as_ref().unwrap(),
"rust-openstack-integration-new"
);
subnet
.delete()
.await
.expect("Cannot request subnet deletion")
.wait()
.await
.expect("Subnet was not deleted");
network
.delete()
.await
.expect("Cannot request network deletion")
.wait()
.await
.expect("Network was not deleted");
}
#[tokio::test]
async fn test_network_update() {
let os = set_up().await;
let mut network = os
.new_network()
.with_admin_state_up(false)
.with_name("rust-openstack-integration-new")
.with_mtu(1400)
.with_description("New network for testing")
.create()
.await
.expect("Could not create network");
assert!(!network.is_dirty());
network.set_admin_state_up(true);
network.set_name("rust-openstack-integration-new2");
network.set_mtu(1420);
assert!(network.is_dirty());
assert!(network.admin_state_up());
assert_eq!(network.mtu(), Some(1420));
assert_eq!(
network.name().as_ref().unwrap(),
"rust-openstack-integration-new2"
);
network.save().await.expect("Could not save network");
assert!(!network.is_dirty());
assert!(network.admin_state_up());
assert_eq!(network.mtu(), Some(1420));
assert_eq!(
network.name().as_ref().unwrap(),
"rust-openstack-integration-new2"
);
network.set_name("rust-openstack-integration-new3");
network.set_mtu(42);
assert!(network.is_dirty());
network.refresh().await.expect("Could not refresh network");
assert!(!network.is_dirty());
assert!(network.admin_state_up());
assert_eq!(network.mtu(), Some(1420));
assert_eq!(
network.name().as_ref().unwrap(),
"rust-openstack-integration-new2"
);
network
.delete()
.await
.expect("Cannot request network deletion")
.wait()
.await
.expect("Network was not deleted");
}
#[tokio::test]
async fn test_network_create_delete_with_fields() {
let os = set_up().await;
let network = os
.new_network()
.with_admin_state_up(false)
.with_name("rust-openstack-integration-new")
.with_mtu(1400)
.with_description("New network for testing")
.create()
.await
.expect("Could not create network");
assert!(!network.admin_state_up());
assert!(network.dns_domain().is_none());
assert_eq!(network.external(), Some(false));
assert!(!network.shared());
assert_eq!(
network.name().as_ref().unwrap(),
"rust-openstack-integration-new"
);
let cidr = ipnet::Ipv4Net::new(net::Ipv4Addr::new(192, 168, 1, 0), 24)
.unwrap()
.into();
let subnet = os
.new_subnet("rust-openstack-integration-new", cidr)
.with_name("rust-openstack-integration-new")
.with_dhcp_enabled(false)
.with_dns_nameserver("8.8.8.8")
.create()
.await
.expect("Could not create subnet");
assert_eq!(subnet.cidr(), cidr);
assert!(!subnet.dhcp_enabled());
assert_eq!(subnet.dns_nameservers(), &vec!["8.8.8.8".to_string()]);
assert_eq!(subnet.ip_version(), openstack::network::IpVersion::V4);
assert_eq!(
subnet.name().as_ref().unwrap(),
"rust-openstack-integration-new"
);
let subnets = os
.find_subnets()
.with_network("rust-openstack-integration-new")
.all()
.await
.expect("Cannot find subnets by network name");
assert_eq!(subnets.len(), 1);
assert_eq!(subnets[0].id(), subnet.id());
assert_eq!(subnets[0].name(), subnet.name());
subnet
.delete()
.await
.expect("Cannot request subnet deletion")
.wait()
.await
.expect("Subnet was not deleted");
network
.delete()
.await
.expect("Cannot request network deletion")
.wait()
.await
.expect("Network was not deleted");
}
#[tokio::test]
async fn test_floating_ip_create_delete() {
let os = set_up().await;
let floating_network_id = env::var("RUST_OPENSTACK_FLOATING_NETWORK")
.expect("Missing RUST_OPENSTACK_FLOATING_NETWORK");
let mut floating_ip = os
.new_floating_ip(floating_network_id)
.create()
.await
.expect("Cannot create a floating IP");
assert!(!floating_ip.is_associated());
floating_ip.port().await.err().unwrap();
let net = floating_ip
.floating_network()
.await
.expect("Cannot find floating network");
let floating_ip_found = {
let stream = os
.find_floating_ips()
.with_floating_network(net.name().clone().expect("Floating network has no name"))
.into_stream();
futures::pin_mut!(stream);
stream
.try_filter(|ip| future::ready(ip.id() == floating_ip.id()))
.try_next()
.await
}
.expect("Failed to list floating IPs")
.expect("Floating IP was not found");
assert_eq!(floating_ip_found.id(), floating_ip.id());
floating_ip
.refresh()
.await
.expect("Cannot refresh a floating IP");
floating_ip
.delete()
.await
.expect("Cannot request floating IP deletion")
.wait()
.await
.expect("Floating IP was not deleted");
}
#[tokio::test]
async fn test_router_create_delete_simple() {
let os = set_up().await;
let router = os
.new_router()
.create()
.await
.expect("Could not create router.");
assert!(router.admin_state_up());
assert!(router.availability_zone_hints().is_empty());
assert!(router.availability_zones().is_empty());
assert!(router.created_at().is_some());
assert!(router.conntrack_helpers().is_empty());
assert!(router.description().is_none());
assert!(router.distributed().is_none());
assert!(router.external_gateway().is_none());
assert!(router.flavor_id().is_none());
assert!(router.ha().is_none());
assert!(router.name().is_none());
assert!(router.revision_number().is_some());
assert_eq!(*router.routes(), Some(vec![]));
assert!(router.service_type_id().is_none());
assert_eq!(router.status(), openstack::network::RouterStatus::Active);
let network = os
.new_network()
.create()
.await
.expect("Could not create network");
let cidr = ipnet::Ipv4Net::new(net::Ipv4Addr::new(192, 168, 1, 0), 24)
.unwrap()
.into();
let mut subnet = os
.new_subnet(network.clone(), cidr)
.create()
.await
.expect("Could not create subnet");
subnet.refresh().await.expect("Cannot refresh subnet");
let _ = router.delete();
subnet
.delete()
.await
.expect("Cannot request subnet deletion")
.wait()
.await
.expect("Subnet was not deleted");
network
.delete()
.await
.expect("Cannot request network deletion")
.wait()
.await
.expect("Network was not deleted");
}
#[tokio::test]
async fn test_router_create_update_delete_with_fields() {
let os = set_up().await;
let mut router = os
.new_router()
.with_admin_state_up(false)
.with_availability_zone_hints(vec![String::from("nova")])
.with_description("rust openstack integration")
.with_name("rust-openstack-integration")
.create()
.await
.expect("Could not create router.");
assert!(!router.admin_state_up());
assert!(router.availability_zone_hints().is_empty());
assert!(router.availability_zones().is_empty());
assert!(router.created_at().is_some());
assert!(router.conntrack_helpers().is_empty());
assert_eq!(
router.description(),
&Some(String::from("rust openstack integration"))
);
assert!(router.distributed().is_none());
assert!(router.external_gateway().is_none());
assert!(router.flavor_id().is_none());
assert!(router.ha().is_none());
assert_eq!(
router.name().as_ref().unwrap(),
"rust-openstack-integration"
);
assert!(router.project_id().is_some());
assert!(router.revision_number().is_some());
assert_eq!(*router.routes(), Some(vec![]));
assert!(router.service_type_id().is_none());
assert_eq!(router.status(), openstack::network::RouterStatus::Active);
let network = os
.new_network()
.with_admin_state_up(false)
.with_name("rust-openstack-integration-new")
.with_description("New network for testing")
.create()
.await
.expect("Could not create network");
let cidr = ipnet::Ipv4Net::new(net::Ipv4Addr::new(192, 168, 1, 0), 24)
.unwrap()
.into();
let subnet = os
.new_subnet("rust-openstack-integration-new", cidr)
.with_name("rust-openstack-integration-new")
.with_dhcp_enabled(false)
.with_dns_nameserver("8.8.8.8")
.create()
.await
.expect("Could not create subnet");
let ports = os.find_ports().with_device_id(router.id()).all().await;
assert_eq!(ports.unwrap().len(), 0);
let _ = router.add_router_interface(Some(subnet.id()), None).await;
let ports = os.find_ports().with_device_id(router.id()).all().await;
assert_eq!(ports.unwrap().len(), 1);
let _ = router
.remove_router_interface(Some(subnet.id()), None)
.await;
let ports = os.find_ports().with_device_id(router.id()).all().await;
assert_eq!(ports.unwrap().len(), 0);
let port = os.new_port(network.id().as_ref()).create().await.unwrap();
let _ = router.add_router_interface(None, Some(port.id())).await;
let ports = os.find_ports().with_device_id(router.id()).all().await;
assert_eq!(ports.unwrap().len(), 1);
let _ = router.remove_router_interface(None, Some(port.id())).await;
let ports = os.find_ports().with_device_id(router.id()).all().await;
assert_eq!(ports.unwrap().len(), 0);
let routers = os
.find_routers()
.with_name("rust-openstack-integration")
.all()
.await
.expect("Cannot find routers by router name.");
assert_eq!(routers.len(), 1);
assert_eq!(routers[0].id(), router.id());
assert_eq!(routers[0].name(), router.name());
subnet
.delete()
.await
.expect("Cannot request subnet deletion")
.wait()
.await
.expect("Subnet was not deleted");
network
.delete()
.await
.expect("Cannot request network deletion")
.wait()
.await
.expect("Network was not deleted");
router
.delete()
.await
.expect("Cannot request router deletetion.")
.wait()
.await
.expect("Router was not deleted.");
}
#[tokio::test]
async fn test_router_update() {
let os = set_up().await;
let mut router = os
.new_router()
.with_admin_state_up(false)
.with_name("rust-openstack-integration-new")
.with_description("New router for testing")
.create()
.await
.expect("Could not create router");
assert!(!router.is_dirty());
router.set_admin_state_up(true);
router.set_name("rust-openstack-integration-new2");
router.set_description("Updated router for testing.");
assert!(router.is_dirty());
assert!(router.admin_state_up());
assert_eq!(
router.name().as_ref().unwrap(),
"rust-openstack-integration-new2"
);
assert_eq!(
router.description().as_ref().unwrap(),
"Updated router for testing."
);
router.save().await.expect("Could not save router");
assert!(!router.is_dirty());
assert!(router.admin_state_up());
assert_eq!(
router.name().as_ref().unwrap(),
"rust-openstack-integration-new2"
);
assert_eq!(
router.description().as_ref().unwrap(),
"Updated router for testing."
);
router.set_name("rust-openstack-integration-new3");
router.set_description("This will be reverted by a refresh.");
assert!(router.is_dirty());
router.refresh().await.expect("Could not refresh router.");
assert!(!router.is_dirty());
assert!(router.admin_state_up());
assert_eq!(
router.name().as_ref().unwrap(),
"rust-openstack-integration-new2"
);
assert_eq!(
router.description().as_ref().unwrap(),
"Updated router for testing."
);
router
.delete()
.await
.expect("Cannot request router deletion.")
.wait()
.await
.expect("Router was not deleted.");
}