use std::collections::HashMap;
use async_trait::async_trait;
use chrono::{DateTime, FixedOffset};
use futures::{Stream, TryStreamExt};
use osauth::Query;
use super::{api, infos::*, protocol, types::*};
use crate::{
common::{ResourceIterator, ResourceQuery},
session::Session,
Refresh, Result, Sort,
};
#[derive(Debug, Clone)]
pub struct Node {
session: Session,
inner: protocol::Node,
}
#[derive(Debug, Clone)]
pub struct NodeSummary {
session: Session,
inner: protocol::NodeSummary,
}
#[derive(Clone, Debug)]
pub struct NodeQuery {
session: Session,
query: Query<NodeFilter>,
can_paginate: bool,
}
#[derive(Clone, Debug)]
pub struct DetailedNodeQuery {
inner: NodeQuery,
}
#[async_trait]
impl Refresh for Node {
async fn refresh(&mut self) -> Result<()> {
self.inner = api::get_node(&self.session, &self.inner.id).await?;
Ok(())
}
}
impl Node {
pub(crate) async fn load<Id: AsRef<str>>(session: Session, id_or_name: Id) -> Result<Node> {
api::get_node(&session, id_or_name)
.await
.map(|inner| Node { session, inner })
}
transparent_property! {
allocation_id: ref Option<String>
}
transparent_property! {
automated_clean: Option<bool>
}
transparent_property! {
bios_interface: ref String
}
transparent_property! {
boot_interface: ref String
}
transparent_property! {
chassis_id: ref Option<String>
}
transparent_property! {
clean_step: ref Option<CleanStep>
}
transparent_property! {
conductor_group: ref String
}
transparent_property! {
conductor_name: ref Option<String>
}
transparent_property! {
console_enabled: bool
}
transparent_property! {
console_interface: ref String
}
transparent_property! {
created_at: DateTime<FixedOffset>
}
transparent_property! {
deploy_interface: ref String
}
transparent_property! {
deploy_step: ref Option<DeployStep>
}
transparent_property! {
description: ref Option<String>
}
transparent_property! {
driver: ref String
}
transparent_property! {
driver_info: ref DriverInfo
}
transparent_property! {
extra: ref HashMap<String, serde_json::Value>
}
transparent_property! {
fault: Option<Fault>
}
transparent_property! {
id: ref String
}
transparent_property! {
inspect_interface: ref String
}
transparent_property! {
inspection_finished_at: Option<DateTime<FixedOffset>>
}
transparent_property! {
inspection_started_at: Option<DateTime<FixedOffset>>
}
transparent_property! {
instance_id: ref Option<String>
}
transparent_property! {
instance_info: ref InstanceInfo
}
transparent_property! {
last_error: ref Option<String>
}
transparent_property! {
lessee: ref Option<String>
}
transparent_property! {
maintenance: bool
}
transparent_property! {
maintenance_reason: ref Option<String>
}
transparent_property! {
management_interface: ref String
}
transparent_property! {
name: ref Option<String>
}
transparent_property! {
network_interface: ref String
}
transparent_property! {
owner: ref Option<String>
}
transparent_property! {
power_interface: ref String
}
transparent_property! {
power_state: Option<PowerState>
}
transparent_property! {
properties: ref Properties
}
transparent_property! {
protected: bool
}
transparent_property! {
protected_reason: ref Option<String>
}
transparent_property! {
provision_state: ProvisionState
}
transparent_property! {
provision_updated_at: Option<DateTime<FixedOffset>>
}
transparent_property! {
raid_interface: ref String
}
transparent_property! {
rescue_interface: ref String
}
transparent_property! {
reservation: ref Option<String>
}
transparent_property! {
resource_class: ref Option<String>
}
transparent_property! {
retired: bool
}
transparent_property! {
retired_reason: ref Option<String>
}
transparent_property! {
shard: ref Option<String>
}
transparent_property! {
storage_interface: ref String
}
transparent_property! {
target_power_state: Option<TargetPowerState>
}
transparent_property! {
target_provision_state: Option<TargetProvisionState>
}
transparent_property! {
traits: ref Vec<String>
}
transparent_property! {
updated_at: Option<DateTime<FixedOffset>>
}
transparent_property! {
vendor_interface: ref String
}
}
impl NodeSummary {
transparent_property! {
id: ref String
}
transparent_property! {
instance_id: ref Option<String>
}
transparent_property! {
maintenance: bool
}
transparent_property! {
name: ref Option<String>
}
transparent_property! {
power_state: Option<PowerState>
}
transparent_property! {
provision_state: ProvisionState
}
}
impl NodeQuery {
pub(crate) fn new(session: Session) -> Self {
Self {
session,
query: Query::default(),
can_paginate: true,
}
}
pub fn set(&mut self, filter: NodeFilter) {
if let NodeFilter::Marker(..) | NodeFilter::Limit(..) = filter {
self.can_paginate = false;
}
self.query.push(filter)
}
#[inline]
pub fn with(mut self, filter: NodeFilter) -> Self {
self.set(filter);
self
}
pub fn sort_by(mut self, sort: Sort<NodeSortKey>) -> Self {
let (field, direction) = sort.unwrap();
self.query.push(NodeFilter::SortKey(field));
self.query.push(NodeFilter::SortDir(direction));
self
}
#[inline]
pub fn detailed(self) -> DetailedNodeQuery {
DetailedNodeQuery { inner: self }
}
#[inline]
pub fn into_stream(self) -> impl Stream<Item = Result<NodeSummary>> {
debug!("Fetching nodes with {:?}", self.query);
ResourceIterator::new(self).into_stream()
}
#[inline]
pub async fn all(self) -> Result<Vec<NodeSummary>> {
self.into_stream().try_collect().await
}
pub async fn one(mut self) -> Result<NodeSummary> {
debug!("Fetching one node with {:?}", self.query);
if self.can_paginate {
self.query.push(NodeFilter::Limit(2))
}
ResourceIterator::new(self).one().await
}
fn with_marker_and_limit(
&self,
limit: Option<usize>,
marker: Option<String>,
) -> Query<NodeFilter> {
let mut result = self.query.clone();
if let Some(limit) = limit {
result.push(NodeFilter::Limit(limit));
}
if let Some(marker) = marker {
result.push(NodeFilter::Marker(marker));
}
result
}
}
#[async_trait]
impl ResourceQuery for NodeQuery {
type Item = NodeSummary;
const DEFAULT_LIMIT: usize = 100;
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.with_marker_and_limit(limit, marker);
Ok(api::list_nodes(&self.session, &query)
.await?
.into_iter()
.map(|srv| NodeSummary {
session: self.session.clone(),
inner: srv,
})
.collect())
}
}
impl DetailedNodeQuery {
pub fn set(&mut self, filter: NodeFilter) {
self.inner.set(filter);
}
#[inline]
pub fn with(mut self, filter: NodeFilter) -> Self {
self.inner.set(filter);
self
}
pub fn sort_by(self, sort: Sort<NodeSortKey>) -> Self {
Self {
inner: self.inner.sort_by(sort),
}
}
#[inline]
pub fn into_stream(self) -> impl Stream<Item = Result<Node>> {
debug!("Fetching nodes with {:?}", self.inner.query);
ResourceIterator::new(self).into_stream()
}
#[inline]
pub async fn all(self) -> Result<Vec<Node>> {
self.into_stream().try_collect().await
}
pub async fn one(mut self) -> Result<Node> {
debug!("Fetching one node with {:?}", self.inner.query);
if self.inner.can_paginate {
self.inner.query.push(NodeFilter::Limit(2))
}
ResourceIterator::new(self).one().await
}
fn with_marker_and_limit(
&self,
limit: Option<usize>,
marker: Option<String>,
) -> Query<NodeFilter> {
let mut result = self.inner.query.clone();
if let Some(limit) = limit {
result.push(NodeFilter::Limit(limit));
}
if let Some(marker) = marker {
result.push(NodeFilter::Marker(marker));
}
result
}
}
#[async_trait]
impl ResourceQuery for DetailedNodeQuery {
type Item = Node;
const DEFAULT_LIMIT: usize = 100;
async fn can_paginate(&self) -> Result<bool> {
Ok(self.inner.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.with_marker_and_limit(limit, marker);
Ok(api::list_nodes_detailed(&self.inner.session, &query)
.await?
.into_iter()
.map(|srv| Node {
session: self.inner.session.clone(),
inner: srv,
})
.collect())
}
}
impl NodeSummary {
pub async fn details(&self) -> Result<Node> {
Node::load(self.session.clone(), &self.inner.id).await
}
}