use std::collections::HashSet;
use std::net;
use std::rc::Rc;
use std::time::Duration;
use chrono::{DateTime, FixedOffset};
use fallible_iterator::{FallibleIterator, IntoFallibleIterator};
use super::super::common::{
DeletionWaiter, IntoVerified, NetworkRef, PortRef, Refresh, ResourceIterator, ResourceQuery,
RouterRef, SubnetRef,
};
use super::super::session::Session;
use super::super::utils::Query;
use super::super::{Error, ErrorKind, Result, Sort};
use super::{api, protocol, Network, Port};
#[derive(Clone, Debug)]
pub struct FloatingIp {
session: Rc<Session>,
inner: protocol::FloatingIp,
dirty: HashSet<&'static str>,
}
#[derive(Clone, Debug)]
pub struct FloatingIpQuery {
session: Rc<Session>,
query: Query,
can_paginate: bool,
floating_network: Option<NetworkRef>,
port: Option<PortRef>,
}
#[derive(Clone, Debug)]
pub struct NewFloatingIp {
session: Rc<Session>,
inner: protocol::FloatingIp,
floating_network: NetworkRef,
port: Option<PortRef>,
subnet: Option<SubnetRef>,
}
impl FloatingIp {
pub(crate) fn new(session: Rc<Session>, inner: protocol::FloatingIp) -> FloatingIp {
FloatingIp {
session,
inner,
dirty: HashSet::new(),
}
}
pub(crate) fn load<Id: AsRef<str>>(session: Rc<Session>, id: Id) -> Result<FloatingIp> {
let inner = api::get_floating_ip(&session, id)?;
Ok(FloatingIp::new(session, inner))
}
transparent_property! {
#[doc = "Creation data and time (if available)."]
created_at: Option<DateTime<FixedOffset>>
}
transparent_property! {
#[doc = "Floating IP 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 floating IP (if available)."]
dns_domain: ref Option<String>
}
transparent_property! {
#[doc = "DNS domain for the floating IP (if available)."]
dns_name: ref Option<String>
}
transparent_property! {
#[doc = "IP address of the port associated with the IP (if any)."]
fixed_ip_address: Option<net::IpAddr>
}
update_field! {
#[doc = "Update which fixed IP address is associated with the floating IP."]
set_fixed_ip_address, with_fixed_ip_address ->fixed_ip_address: optional net::IpAddr
}
transparent_property! {
#[doc = "Floating IP address"]
floating_ip_address: net::IpAddr
}
transparent_property! {
#[doc = "ID of the network this floating IP belongs to."]
floating_network_id: ref String
}
pub fn floating_network(&self) -> Result<Network> {
Network::load(self.session.clone(), &self.inner.floating_network_id)
}
transparent_property! {
#[doc = "Unique ID."]
id: ref String
}
pub fn is_associated(&self) -> bool {
self.inner.port_id.is_some()
}
transparent_property! {
#[doc = "List of port forwardings (if any)."]
port_forwardings: ref Vec<protocol::PortForwarding>
}
transparent_property! {
#[doc = "ID of the port this IP is attached to (if any)."]
port_id: ref Option<String>
}
transparent_property! {
#[doc = "ID of the router of this floating IP."]
router_id: ref Option<String>
}
pub fn port(&self) -> Result<Port> {
match self.inner.port_id {
Some(ref port_id) => Port::load(self.session.clone(), &port_id),
None => Err(Error::new(
ErrorKind::ResourceNotFound,
"Floating IP is not associated",
)),
}
}
transparent_property! {
#[doc = "Status of the floating IP."]
status: protocol::FloatingIpStatus
}
transparent_property! {
#[doc = "Last update data and time (if available)."]
updated_at: Option<DateTime<FixedOffset>>
}
pub fn associate<P>(&mut self, port: P, fixed_ip_address: Option<net::IpAddr>) -> Result<()>
where
P: Into<PortRef>,
{
let new_port = port.into().into_verified(&self.session)?.into();
self.update_port(new_port, fixed_ip_address)
}
pub fn dissociate(&mut self) -> Result<()> {
self.update_port(serde_json::Value::Null, None)
}
pub fn delete(self) -> Result<DeletionWaiter<FloatingIp>> {
api::delete_floating_ip(&self.session, &self.inner.id)?;
Ok(DeletionWaiter::new(
self,
Duration::new(60, 0),
Duration::new(1, 0),
))
}
pub fn save(&mut self) -> Result<()> {
let mut update = protocol::FloatingIpUpdate::default();
save_option_fields! {
self -> update: description fixed_ip_address
};
self.inner = api::update_floating_ip(&self.session, self.id(), update)?;
self.dirty.clear();
Ok(())
}
fn update_port(
&mut self,
value: serde_json::Value,
fixed_ip_address: Option<net::IpAddr>,
) -> Result<()> {
let update = protocol::FloatingIpUpdate {
description: None,
fixed_ip_address,
port_id: Some(value),
};
let mut inner = api::update_floating_ip(&self.session, self.id(), update)?;
let desc_changed = self.dirty.contains("description");
self.dirty.clear();
if desc_changed {
inner.description = self.inner.description.take();
let _ = self.dirty.insert("description");
}
self.inner = inner;
Ok(())
}
}
impl Refresh for FloatingIp {
fn refresh(&mut self) -> Result<()> {
self.inner = api::get_floating_ip(&self.session, &self.inner.id)?;
Ok(())
}
}
impl FloatingIpQuery {
pub(crate) fn new(session: Rc<Session>) -> FloatingIpQuery {
FloatingIpQuery {
session,
query: Query::new(),
can_paginate: true,
floating_network: None,
port: None,
}
}
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::FloatingIpSortKey>) -> Self {
let (field, direction) = sort.into();
self.query.push_str("sort_key", field);
self.query.push("sort_dir", direction);
self
}
query_filter! {
#[doc = "Filter by description."]
set_description, with_description -> description
}
query_filter! {
#[doc = "Filter by fixed IP address."]
set_fixed_ip_address, with_fixed_ip_address -> fixed_ip_address: net::IpAddr
}
query_filter! {
#[doc = "Filter by floating IP address."]
set_floating_ip_address, with_floating_ip_address -> floating_ip_address: net::IpAddr
}
pub fn set_floating_network<N: Into<NetworkRef>>(&mut self, value: N) {
self.floating_network = Some(value.into());
}
pub fn with_floating_network<N: Into<NetworkRef>>(mut self, value: N) -> Self {
self.set_floating_network(value);
self
}
pub fn set_port<N: Into<PortRef>>(&mut self, value: N) {
self.port = Some(value.into());
}
pub fn with_port<N: Into<PortRef>>(mut self, value: N) -> Self {
self.set_port(value);
self
}
pub fn set_router<N: Into<RouterRef>>(&mut self, value: N) {
self.query.push_str("router_id", value.into());
}
pub fn with_router<N: Into<RouterRef>>(mut self, value: N) -> Self {
self.set_router(value);
self
}
query_filter! {
#[doc = "Filter by status."]
set_status, with_status -> status: protocol::FloatingIpStatus
}
pub fn into_iter(self) -> ResourceIterator<FloatingIpQuery> {
debug!("Fetching floating_ips with {:?}", self.query);
ResourceIterator::new(self)
}
pub fn all(self) -> Result<Vec<FloatingIp>> {
self.into_iter().collect()
}
pub fn one(mut self) -> Result<FloatingIp> {
debug!("Fetching one floating IP with {:?}", self.query);
if self.can_paginate {
self.query.push("limit", 2);
}
self.into_iter().one()
}
}
impl ResourceQuery for FloatingIpQuery {
type Item = FloatingIp;
const DEFAULT_LIMIT: usize = 50;
fn can_paginate(&self) -> Result<bool> {
Ok(self.can_paginate)
}
fn extract_marker(&self, resource: &Self::Item) -> String {
resource.id().clone()
}
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_floating_ips(&self.session, &query)?
.into_iter()
.map(|item| FloatingIp::new(self.session.clone(), item))
.collect())
}
fn validate(&mut self) -> Result<()> {
if let Some(floating_network) = self.floating_network.take() {
let verified = floating_network.into_verified(&self.session)?;
self.query.push_str("floating_network_id", verified);
}
if let Some(port) = self.port.take() {
let verified = port.into_verified(&self.session)?;
self.query.push_str("port_id", verified);
}
Ok(())
}
}
impl NewFloatingIp {
pub(crate) fn new(session: Rc<Session>, floating_network: NetworkRef) -> NewFloatingIp {
NewFloatingIp {
session,
inner: protocol::FloatingIp {
created_at: None,
description: None,
dns_domain: None,
dns_name: None,
fixed_ip_address: None,
floating_ip_address: net::IpAddr::V4(net::Ipv4Addr::new(0, 0, 0, 0)),
floating_network_id: String::new(),
id: String::new(),
port_id: None,
port_forwardings: Vec::new(),
router_id: None,
status: protocol::FloatingIpStatus::Active,
subnet_id: None,
updated_at: None,
},
floating_network,
port: None,
subnet: None,
}
}
pub fn create(mut self) -> Result<FloatingIp> {
self.inner.floating_network_id = self.floating_network.into_verified(&self.session)?.into();
if let Some(port) = self.port {
self.inner.port_id = Some(port.into_verified(&self.session)?.into());
}
if let Some(subnet) = self.subnet {
self.inner.subnet_id = Some(subnet.into_verified(&self.session)?.into());
}
let floating_ip = api::create_floating_ip(&self.session, self.inner)?;
Ok(FloatingIp::new(self.session, floating_ip))
}
creation_inner_field! {
#[doc = "Set description of the floating IP."]
set_description, with_description -> description: optional String
}
creation_inner_field! {
#[doc = "Set DNS domain for the floating IP."]
set_dns_domain, with_dns_domain -> dns_domain: optional String
}
creation_inner_field! {
#[doc = "Set DNS name for the floating IP."]
set_dns_name, with_dns_name -> dns_name: optional String
}
creation_inner_field! {
#[doc = "Set the requested fixed IP address (required if the port has several)."]
set_fixed_ip_address, with_fixed_ip_address -> fixed_ip_address: optional net::IpAddr
}
creation_inner_field! {
#[doc = "Set the requested floating IP address."]
set_floating_ip_address, with_floating_ip_address -> floating_ip_address: net::IpAddr
}
pub fn set_port<P>(&mut self, port: P)
where
P: Into<PortRef>,
{
self.port = Some(port.into());
}
pub fn with_port<P>(mut self, port: P) -> NewFloatingIp
where
P: Into<PortRef>,
{
self.set_port(port);
self
}
pub fn set_subnet<P>(&mut self, subnet: P)
where
P: Into<SubnetRef>,
{
self.subnet = Some(subnet.into());
}
pub fn with_subnet<P>(mut self, subnet: P) -> NewFloatingIp
where
P: Into<SubnetRef>,
{
self.set_subnet(subnet);
self
}
}
impl IntoFallibleIterator for FloatingIpQuery {
type Item = FloatingIp;
type Error = Error;
type IntoFallibleIter = ResourceIterator<FloatingIpQuery>;
fn into_fallible_iter(self) -> Self::IntoFallibleIter {
self.into_iter()
}
}