use std::collections::HashSet;
use std::time::Duration;
use async_trait::async_trait;
use chrono::{DateTime, FixedOffset};
use futures::stream::{Stream, TryStreamExt};
use super::super::common::{NetworkRef, Refresh, ResourceIterator, ResourceQuery};
use super::super::session::Session;
use super::super::utils::Query;
use super::super::waiter::DeletionWaiter;
use super::super::{Result, Sort};
use super::{api, protocol};
#[derive(Clone, Debug)]
pub struct NetworkQuery {
session: Session,
query: Query,
can_paginate: bool,
}
#[derive(Clone, Debug)]
pub struct Network {
session: Session,
inner: protocol::Network,
dirty: HashSet<&'static str>,
}
#[derive(Clone, Debug)]
pub struct NewNetwork {
session: Session,
inner: protocol::Network,
}
impl Network {
fn new(session: Session, inner: protocol::Network) -> Network {
Network {
session,
inner,
dirty: HashSet::new(),
}
}
pub(crate) async fn load<Id: AsRef<str>>(session: Session, id: Id) -> Result<Network> {
let inner = api::get_network(&session, id).await?;
Ok(Network::new(session, inner))
}
transparent_property! {
#[doc = "The administrative state of the network."]
admin_state_up: bool
}
update_field! {
#[doc = "Set the administrative state of the network."]
set_admin_state_up, with_admin_state_up -> admin_state_up: bool
}
transparent_property! {
#[doc = "The availability zones for the network (if available)."]
availability_zones: ref Vec<String>
}
transparent_property! {
#[doc = "Creation data and time (if available)."]
created_at: Option<DateTime<FixedOffset>>
}
transparent_property! {
#[doc = "Network description."]
description: ref Option<String>
}
update_field! {
#[doc = "Update the description."]
set_description, with_description -> description: optional String
}
transparent_property! {
#[doc = "DNS domain for the network (if available)."]
dns_domain: ref Option<String>
}
update_field! {
#[doc = "Update the DNS domain."]
set_dns_domain, with_dns_domain -> dns_domain: optional String
}
transparent_property! {
#[doc = "Whether the network is external (if available)."]
external: Option<bool>
}
update_field! {
#[doc = "Configure whether the network is external."]
set_external, with_external -> external: optional bool
}
transparent_property! {
#[doc = "Unique ID."]
id: ref String
}
transparent_property! {
#[doc = "Whether the network is the default pool (if available)."]
is_default: Option<bool>
}
update_field! {
#[doc = "Configure whether the network is the default pool."]
set_default, with_default -> is_default: optional bool
}
transparent_property! {
#[doc = "Whether there is L2 connectivity throughout the Network."]
l2_adjacency: Option<bool>
}
transparent_property! {
#[doc = "Network MTU (if available)."]
mtu: Option<u32>
}
update_field! {
#[doc = "Set the network MTU."]
set_mtu, with_mtu -> mtu: optional u32
}
transparent_property! {
#[doc = "Network name."]
name: ref Option<String>
}
update_field! {
#[doc = "Update the name."]
set_name, with_name -> name: optional String
}
transparent_property! {
#[doc = "Whether port security is enabled by default."]
port_security_enabled: Option<bool>
}
update_field! {
#[doc = "Configure whether port security is enabled by default."]
set_port_security_enabled, with_port_security_enabled
-> port_security_enabled: optional bool
}
transparent_property! {
#[doc = "Whether the network is shared."]
shared: bool
}
update_field! {
#[doc = "Configure whether the network is shared."]
set_shared, with_shared -> shared: bool
}
transparent_property! {
#[doc = "Status of the network."]
status: protocol::NetworkStatus
}
transparent_property! {
#[doc = "Last update data and time (if available)."]
updated_at: Option<DateTime<FixedOffset>>
}
transparent_property! {
#[doc = "VLAN transparency mode of the network."]
vlan_transparent: Option<bool>
}
pub async fn delete(self) -> Result<DeletionWaiter<Network>> {
api::delete_network(&self.session, &self.inner.id).await?;
Ok(DeletionWaiter::new(
self,
Duration::new(60, 0),
Duration::new(1, 0),
))
}
pub fn is_dirty(&self) -> bool {
!self.dirty.is_empty()
}
#[allow(clippy::field_reassign_with_default)]
pub async fn save(&mut self) -> Result<()> {
let mut update = protocol::NetworkUpdate::default();
save_fields! {
self -> update: admin_state_up shared
};
save_option_fields! {
self -> update: description external dns_domain is_default mtu name
port_security_enabled
};
let inner = api::update_network(&self.session, self.id(), update).await?;
self.dirty.clear();
self.inner = inner;
Ok(())
}
}
#[async_trait]
impl Refresh for Network {
async fn refresh(&mut self) -> Result<()> {
self.inner = api::get_network_by_id(&self.session, &self.inner.id).await?;
self.dirty.clear();
Ok(())
}
}
impl NetworkQuery {
pub(crate) fn new(session: Session) -> NetworkQuery {
NetworkQuery {
session,
query: Query::new(),
can_paginate: true,
}
}
pub fn with_marker<T: Into<String>>(mut self, marker: T) -> Self {
self.can_paginate = false;
self.query.push_str("marker", marker);
self
}
pub fn with_limit(mut self, limit: usize) -> Self {
self.can_paginate = false;
self.query.push("limit", limit);
self
}
pub fn sort_by(mut self, sort: Sort<protocol::NetworkSortKey>) -> Self {
let (field, direction) = sort.into();
self.query.push_str("sort_key", field);
self.query.push("sort_dir", direction);
self
}
pub fn with_name<T: Into<String>>(mut self, value: T) -> Self {
self.query.push_str("name", value);
self
}
pub fn into_stream(self) -> impl Stream<Item = Result<Network>> {
debug!("Fetching networks with {:?}", self.query);
ResourceIterator::new(self).into_stream()
}
pub async fn all(self) -> Result<Vec<Network>> {
self.into_stream().try_collect().await
}
pub async fn one(mut self) -> Result<Network> {
debug!("Fetching one network with {:?}", self.query);
if self.can_paginate {
self.query.push("limit", 2);
}
ResourceIterator::new(self).one().await
}
}
#[async_trait]
impl ResourceQuery for NetworkQuery {
type Item = Network;
const DEFAULT_LIMIT: usize = 50;
async fn can_paginate(&self) -> Result<bool> {
Ok(self.can_paginate)
}
fn extract_marker(&self, resource: &Self::Item) -> String {
resource.id().clone()
}
async fn fetch_chunk(
&self,
limit: Option<usize>,
marker: Option<String>,
) -> Result<Vec<Self::Item>> {
let query = self.query.with_marker_and_limit(limit, marker);
Ok(api::list_networks(&self.session, &query)
.await?
.into_iter()
.map(|item| Network::new(self.session.clone(), item))
.collect())
}
}
impl NewNetwork {
pub(crate) fn new(session: Session) -> NewNetwork {
NewNetwork {
session,
inner: protocol::Network::default(),
}
}
pub async fn create(self) -> Result<Network> {
let inner = api::create_network(&self.session, self.inner).await?;
Ok(Network::new(self.session, inner))
}
creation_inner_field! {
#[doc = "Set administrative status for the network."]
set_admin_state_up, with_admin_state_up -> admin_state_up: bool
}
creation_inner_field! {
#[doc = "Configure whether this network is default."]
set_default, with_default -> is_default: optional bool
}
creation_inner_field! {
#[doc = "Set description of the network."]
set_description, with_description -> description: optional String
}
creation_inner_field! {
#[doc = "Set DNS domain for the network."]
set_dns_domain, with_dns_domain -> dns_domain: optional String
}
creation_inner_field! {
#[doc = "Configure whether this network is external."]
set_external, with_external -> external: optional bool
}
creation_inner_field! {
#[doc = "Set MTU for the network."]
set_mtu, with_mtu -> mtu: optional u32
}
creation_inner_field! {
#[doc = "Set a name for the network."]
set_name, with_name -> name: optional String
}
creation_inner_field! {
#[doc = "Configure whether port security is enabled by default."]
set_port_security_enabled, with_port_security_enabled
-> port_security_enabled: optional bool
}
creation_inner_field! {
#[doc = "Configure whether the network is shared across all projects."]
set_shared, with_shared
-> shared: bool
}
creation_inner_field! {
#[doc = "Configure VLAN transparency mode of the network."]
set_vlan_transparent, with_vlan_transparent
-> vlan_transparent: optional bool
}
}
impl From<Network> for NetworkRef {
fn from(value: Network) -> NetworkRef {
NetworkRef::new_verified(value.inner.id)
}
}
#[cfg(feature = "network")]
impl NetworkRef {
pub(crate) async fn into_verified(self, session: &Session) -> Result<NetworkRef> {
Ok(if self.verified {
self
} else {
NetworkRef::new_verified(api::get_network(session, &self.value).await?.id)
})
}
}