diff --git a/control-plane/plugin/src/lib.rs b/control-plane/plugin/src/lib.rs index 89f559ec1..0d37cec31 100644 --- a/control-plane/plugin/src/lib.rs +++ b/control-plane/plugin/src/lib.rs @@ -148,7 +148,9 @@ impl ExecuteOperation for GetResources { volume::Volume::topology(id, &cli_args.output).await } GetResources::Pools(args) => pool::Pools::list(args, &cli_args.output).await, - GetResources::Pool { id } => pool::Pool::get(id, &cli_args.output).await, + GetResources::Pool(args) => { + pool::Pool::get(&args.pool_id(), args, &cli_args.output).await + } GetResources::Nodes(args) => node::Nodes::list(args, &cli_args.output).await, GetResources::Node(args) => { node::Node::get(&args.node_id(), args, &cli_args.output).await diff --git a/control-plane/plugin/src/resources/mod.rs b/control-plane/plugin/src/resources/mod.rs index 6b2185a60..dc4dad110 100644 --- a/control-plane/plugin/src/resources/mod.rs +++ b/control-plane/plugin/src/resources/mod.rs @@ -1,7 +1,7 @@ use crate::resources::{ blockdevice::BlockDeviceArgs, node::{DrainNodeArgs, GetNodeArgs, GetNodesArgs}, - pool::GetPoolsArgs, + pool::{GetPoolArgs, GetPoolsArgs}, snapshot::VolumeSnapshotArgs, volume::VolumesArgs, }; @@ -51,7 +51,7 @@ pub enum GetResources { /// Get all pools. Pools(GetPoolsArgs), /// Get pool with the given ID. - Pool { id: PoolId }, + Pool(GetPoolArgs), /// Get all nodes. Nodes(GetNodesArgs), /// Get node with the given ID. diff --git a/control-plane/plugin/src/resources/pool.rs b/control-plane/plugin/src/resources/pool.rs index d63e57d70..8111996fc 100644 --- a/control-plane/plugin/src/resources/pool.rs +++ b/control-plane/plugin/src/resources/pool.rs @@ -1,11 +1,11 @@ use crate::{ - operations::{Get, Label, ListWithArgs, PluginResult}, + operations::{Get, GetWithArgs, Label, ListWithArgs, PluginResult}, resources::{ error::{Error, LabelAssignSnafu, OpError, TopologyError}, utils, utils::{ optional_cell, print_table, validate_topology_key, validate_topology_value, CreateRow, - GetHeaderRow, OutputFormat, + CreateRows, GetHeaderRow, OutputFormat, }, NodeId, PoolId, }, @@ -13,7 +13,8 @@ use crate::{ }; use async_trait::async_trait; use openapi::apis::StatusCode; -use prettytable::Row; +use prettytable::{Cell, Row}; +use serde::Serialize; use snafu::ResultExt; use std::collections::HashMap; @@ -68,6 +69,27 @@ impl GetHeaderRow for openapi::models::Pool { } } +#[derive(Debug, Clone, clap::Args)] +/// Arguments used when getting a pool. +pub struct GetPoolArgs { + /// Id of the pool + pool_id: PoolId, + /// Show the labels of the node + #[clap(long, default_value = "false")] + show_labels: bool, +} + +impl GetPoolArgs { + /// Return the pool ID. + pub fn pool_id(&self) -> PoolId { + self.pool_id.clone() + } + /// Return whether to show the labels of the pool. + pub fn show_labels(&self) -> bool { + self.show_labels + } +} + /// Arguments used when getting pools. #[derive(Debug, Clone, clap::Args)] pub struct GetPoolsArgs { @@ -84,6 +106,10 @@ pub struct GetPoolsArgs { /// Pools must satisfy all of the specified label constraints. #[clap(short = 'l', long)] selector: Option, + + /// Show the labels of the node + #[clap(long, default_value = "false")] + show_labels: bool, } impl GetPoolsArgs { @@ -101,6 +127,11 @@ impl GetPoolsArgs { pub fn selector(&self) -> &Option { &self.selector } + + /// Return whether to show the labels of the pool. + pub fn show_labels(&self) -> bool { + self.show_labels + } } #[async_trait(?Send)] @@ -142,17 +173,45 @@ impl ListWithArgs for Pools { #[derive(clap::Args, Debug)] pub struct Pool {} +// #[async_trait(?Send)] +// impl Get for Pool { +// type ID = PoolId; +// async fn get(id: &Self::ID, output: &utils::OutputFormat) -> PluginResult { +// match RestClient::client().pools_api().get_pool(id).await { +// Ok(pool) => { +// // Print table, json or yaml based on output format. +// utils::print_table(output, pool.into_body()); +// } +// Err(e) => { +// return Err(Error::GetPoolError { +// id: id.to_string(), +// source: e, +// }); +// } +// } +// Ok(()) +// } +// } + #[async_trait(?Send)] -impl Get for Pool { +impl GetWithArgs for Pool { type ID = PoolId; - async fn get(id: &Self::ID, output: &utils::OutputFormat) -> PluginResult { + type Args = GetPoolArgs; + async fn get(id: &Self::ID, args: &Self::Args, output: &utils::OutputFormat) -> PluginResult { match RestClient::client().pools_api().get_pool(id).await { - Ok(pool) => { - // Print table, json or yaml based on output format. - utils::print_table(output, pool.into_body()); - } + Ok(pool) => match output { + OutputFormat::Yaml | OutputFormat::Json => { + print_table(output, pool.clone().into_body()); + } + OutputFormat::None => { + print_table( + output, + PoolDisplayLabels::new(pool.into_body(), args.show_labels()), + ); + } + }, Err(e) => { - return Err(Error::GetPoolError { + return Err(Error::GetNodeError { id: id.to_string(), source: e, }); @@ -279,3 +338,79 @@ impl Label for Pool { Ok(()) } } + +/// The PoolDisplayLabels structure is responsible for controlling the display formatting of Pool +/// objects. `#[serde(flatten)]` and `#[serde(skip)]` attributes are used to ensure that when the +/// object is serialised, only the `inner` object is represented. +#[derive(Serialize, Debug)] +pub struct PoolDisplayLabels { + #[serde(flatten)] + pub inner: Vec, + #[serde(skip)] + show_labels: bool, +} + +impl PoolDisplayLabels { + /// Create a new `PoolDisplayLabels` instance. + pub(crate) fn new(pool: openapi::models::Pool, show_labels: bool) -> Self { + let vec: Vec = vec![pool]; + Self { + inner: vec, + show_labels, + } + } + /// Create a new `PoolDisplay` instance from a vector of pools. + pub(crate) fn new_pools(pools: Vec, show_labels: bool) -> Self { + Self { + inner: pools, + show_labels, + } + } + + /// Get a list of pool labels. + pub(crate) fn get_pool_label_list(pool: &openapi::models::Pool) -> Vec { + let mut pools_labels: Vec = vec![]; + + match &pool.spec { + Some(spec) => match &spec.labels { + Some(ds) => { + pools_labels = ds + .iter() + .filter(|(key, _)| *key != &"openebs.io/created-by") + .map(|(key, value)| format!("{}={}", key, value)) + .collect(); + } + None => {} + }, + None => {} + } + pools_labels + } +} + +// Create the header for a `PoolDisplayLabels` object. +impl GetHeaderRow for PoolDisplayLabels { + fn get_header_row(&self) -> Row { + let mut header = (*utils::POOLS_HEADERS).clone(); + if self.show_labels { + header.extend(vec!["LABELS"]); + } + header + } +} + +impl CreateRows for PoolDisplayLabels { + fn create_rows(&self) -> Vec { + let mut rows = vec![]; + for node in self.inner.iter() { + let mut row = node.create_rows(); + if self.show_labels { + let labelstring = PoolDisplayLabels::get_pool_label_list(node).join(", "); + // Add the node labels to each row. + row[0].add_cell(Cell::new(&labelstring)); + } + rows.push(row[0].clone()); + } + rows + } +}