diff --git a/iml-api/src/graphql.rs b/iml-api/src/graphql.rs index 8369c17028..e4a52e60ed 100644 --- a/iml-api/src/graphql.rs +++ b/iml-api/src/graphql.rs @@ -18,7 +18,7 @@ use iml_rabbit::Pool; use iml_wire_types::{ graphql_duration::GraphQLDuration, snapshot::{ReserveUnit, Snapshot, SnapshotInterval, SnapshotRetention}, - Command, EndpointName, Job, + Command, EndpointName, Job, StratagemReport, }; use itertools::Itertools; use juniper::{ @@ -141,17 +141,6 @@ impl Deref for SortDir { } } -#[derive(juniper::GraphQLObject)] -/// Information about a stratagem report -struct StratagemReport { - /// The filename of the stratagem report under /var/spool/iml/report - filename: String, - /// When the report was last modified - modify_time: DateTime, - /// The size of the report in bytes - size: i32, -} - pub(crate) struct QueryRoot; #[juniper::graphql_object(Context = Context)] diff --git a/iml-graphql-queries/src/lib.rs b/iml-graphql-queries/src/lib.rs index 3c222c24be..4db6ec5f7c 100644 --- a/iml-graphql-queries/src/lib.rs +++ b/iml-graphql-queries/src/lib.rs @@ -3,6 +3,7 @@ // license that can be found in the LICENSE file. pub mod snapshot; +pub mod stratagem; use std::fmt; diff --git a/iml-graphql-queries/src/stratagem.rs b/iml-graphql-queries/src/stratagem.rs new file mode 100644 index 0000000000..4d74ce6f3e --- /dev/null +++ b/iml-graphql-queries/src/stratagem.rs @@ -0,0 +1,64 @@ +// Copyright (c) 2020 DDN. All rights reserved. +// Use of this source code is governed by a MIT-style +// license that can be found in the LICENSE file. + +pub mod list_reports { + use crate::Query; + use iml_wire_types::StratagemReport; + + pub static QUERY: &str = r#" + query StratagemReports { + stratagemReports { + filename + size + modify_time: modifyTime + } + } + "#; + + #[derive(Debug, serde::Serialize)] + pub struct Vars {} + + pub fn build() -> Query { + Query { + query: QUERY.to_string(), + variables: None, + } + } + + #[derive(Debug, Clone, serde::Deserialize)] + pub struct Resp { + #[serde(rename(deserialize = "stratagemReports"))] + pub stratagem_reports: Vec, + } +} + +pub mod delete_report { + use crate::Query; + + pub static QUERY: &str = r#" + mutation DeleteStratagemReport($filename: String!) { + deleteStratagemReport(filename: $filename) + } + "#; + + #[derive(Debug, serde::Serialize)] + pub struct Vars { + filename: String, + } + + pub fn build(filename: impl ToString) -> Query { + Query { + query: QUERY.to_string(), + variables: Some(Vars { + filename: filename.to_string(), + }), + } + } + + #[derive(Debug, Clone, serde::Deserialize)] + pub struct Resp { + #[serde(rename(deserialize = "deleteStratagemReport"))] + pub delete_stratagem_report: bool, + } +} diff --git a/iml-manager-cli/src/display_utils.rs b/iml-manager-cli/src/display_utils.rs index da5358eb44..e3a8816c41 100644 --- a/iml-manager-cli/src/display_utils.rs +++ b/iml-manager-cli/src/display_utils.rs @@ -7,7 +7,7 @@ use console::style; use futures::{Future, FutureExt}; use iml_wire_types::{ snapshot::{ReserveUnit, Snapshot, SnapshotInterval, SnapshotRetention}, - Command, Filesystem, Host, OstPool, ServerProfile, StratagemConfiguration, + Command, Filesystem, Host, OstPool, ServerProfile, StratagemConfiguration, StratagemReport, }; use indicatif::ProgressBar; use number_formatter::{format_bytes, format_number}; @@ -186,6 +186,16 @@ impl IntoTable for Vec { } } +impl IntoTable for Vec { + fn into_table(self) -> Table { + generate_table( + &["Filename", "Size", "Modify Time"], + self.into_iter() + .map(|r| vec![r.filename, r.size.to_string(), r.modify_time.to_rfc2822()]), + ) + } +} + impl IntoTable for Vec { fn into_table(self) -> Table { generate_table( diff --git a/iml-manager-cli/src/stratagem.rs b/iml-manager-cli/src/stratagem.rs index 551b6240f9..1ccde572aa 100644 --- a/iml-manager-cli/src/stratagem.rs +++ b/iml-manager-cli/src/stratagem.rs @@ -3,7 +3,7 @@ // license that can be found in the LICENSE file. use crate::{ - api_utils::{delete, first, get, post, put, wait_for_cmd_display}, + api_utils::{delete, first, get, graphql, post, wait_for_cmd_display}, display_utils::{wrap_fut, DisplayType, IntoDisplayType as _}, error::{ DurationParseError, ImlManagerCliError, RunStratagemCommandResult, @@ -11,6 +11,7 @@ use crate::{ }, }; use console::Term; +use iml_graphql_queries::stratagem as stratagem_queries; use iml_manager_client::ImlManagerClientError; use iml_wire_types::{ApiList, CmdWrapper, EndpointName, Filesystem, StratagemConfiguration}; use structopt::{clap::arg_enum, StructOpt}; @@ -22,47 +23,62 @@ pub enum StratagemCommand { Scan(StratagemScanData), /// Configure Stratagem scanning interval #[structopt(name = "interval")] - StratagemInterval(StratagemInterval), + Interval(IntervalCommand), /// Kickoff a Stratagem Filesync #[structopt(name = "filesync")] Filesync(StratagemFilesyncData), /// Kickoff a Stratagem Cloudsync #[structopt(name = "cloudsync")] Cloudsync(StratagemCloudsyncData), + /// Work with Stratagem reports + #[structopt(name = "report")] + Report { + #[structopt(subcommand)] + command: Option, + }, } #[derive(Debug, StructOpt)] -pub enum StratagemInterval { +pub enum ReportCommand { + /// List all existing Stratagem reports (default) + #[structopt(name = "list")] + List { + /// Display type: json, yaml, tabular + #[structopt(short = "d", long = "display", default_value = "tabular")] + display_type: DisplayType, + }, + /// Delete Stratagem reports + #[structopt(name = "remove")] + Delete { + /// Report name to delete + #[structopt(required = true, min_values = 1)] + name: Vec, + }, +} + +#[derive(Debug, StructOpt)] +pub enum IntervalCommand { /// List all existing Stratagem intervals #[structopt(name = "list")] List { - /// Set the display type - /// - /// The display type can be one of the following: - /// tabular: display content in a table format - /// json: return data in json format - /// yaml: return data in yaml format + /// Display type: json, yaml, tabular #[structopt(short = "d", long = "display", default_value = "tabular")] display_type: DisplayType, }, - /// Add a new Stratagem interval - #[structopt(name = "add")] - Add(StratagemIntervalConfig), - /// Update an existing Stratagem interval - #[structopt(name = "update")] - Update(StratagemIntervalConfig), + /// Create Stratagem scan interval + #[structopt(name = "create")] + Create(IntervalCommandConfig), /// Remove a Stratagem interval #[structopt(name = "remove")] Remove(StratagemRemoveData), } #[derive(Debug, StructOpt, serde::Serialize)] -pub struct StratagemIntervalConfig { +pub struct IntervalCommandConfig { /// Filesystem to configure - #[structopt(short = "f", long = "filesystem")] filesystem: String, /// Interval to scan - #[structopt(short = "i", long = "interval", parse(try_from_str = parse_duration))] + #[structopt(parse(try_from_str = parse_duration))] interval: u64, /// The report duration #[structopt(short = "r", long = "report", parse(try_from_str = parse_duration))] @@ -75,14 +91,12 @@ pub struct StratagemIntervalConfig { #[derive(Debug, StructOpt, serde::Serialize)] pub struct StratagemRemoveData { /// Filesystem to unconfigure - #[structopt(short = "f", long = "filesystem")] - name: String, + filesystem: String, } #[derive(serde::Serialize, StructOpt, Debug)] pub struct StratagemScanData { /// The name of the filesystem to scan - #[structopt(short = "f", long = "filesystem")] filesystem: String, /// The report duration #[structopt(short = "r", long = "report", parse(try_from_str = parse_duration))] @@ -230,6 +244,78 @@ fn list_stratagem_configurations( term.write_line(&x).unwrap(); } +async fn report_cli(cmd: ReportCommand) -> Result<(), ImlManagerCliError> { + match cmd { + ReportCommand::List { display_type } => { + let query = stratagem_queries::list_reports::build(); + + let resp: iml_graphql_queries::Response = + graphql(query).await?; + let reports = Result::from(resp)?.data.stratagem_reports; + + let x = reports.into_display_type(display_type); + + let term = Term::stdout(); + term.write_line(&x).unwrap(); + + Ok(()) + } + ReportCommand::Delete { name } => { + for n in name { + let query = stratagem_queries::delete_report::build(n); + + let _resp: iml_graphql_queries::Response = + graphql(query).await?; + } + Ok(()) + } + } +} + +async fn interval_cli(cmd: IntervalCommand) -> Result<(), ImlManagerCliError> { + match cmd { + IntervalCommand::List { display_type } => { + let r: ApiList = wrap_fut( + "Finding existing intervals...", + get( + StratagemConfiguration::endpoint_name(), + serde_json::json!({ "limit": 0 }), + ), + ) + .await?; + + if r.objects.is_empty() { + println!("No Stratagem intervals found"); + } else { + list_stratagem_configurations(r.objects, display_type); + } + Ok(()) + } + IntervalCommand::Create(c) => { + let r = post(StratagemConfiguration::endpoint_name(), c).await?; + + let CmdWrapper { command } = handle_cmd_resp(r).await?; + + wait_for_cmd_display(command).await?; + Ok(()) + } + IntervalCommand::Remove(StratagemRemoveData { filesystem }) => { + let x = get_stratagem_config_by_fs_name(&filesystem).await?; + + let r = delete( + &format!("{}/{}", StratagemConfiguration::endpoint_name(), x.id), + Vec::<(String, String)>::new(), + ) + .await?; + + let CmdWrapper { command } = handle_cmd_resp(r).await?; + + wait_for_cmd_display(command).await?; + Ok(()) + } + } +} + pub async fn stratagem_cli(command: StratagemCommand) -> Result<(), ImlManagerCliError> { match command { StratagemCommand::Scan(data) => { @@ -258,58 +344,13 @@ pub async fn stratagem_cli(command: StratagemCommand) -> Result<(), ImlManagerCl wait_for_cmd_display(command).await?; } - StratagemCommand::StratagemInterval(x) => match x { - StratagemInterval::List { display_type } => { - let r: ApiList = wrap_fut( - "Finding existing intervals...", - get( - StratagemConfiguration::endpoint_name(), - serde_json::json!({ "limit": 0 }), - ), - ) - .await?; - - if r.objects.is_empty() { - println!("No Stratagem intervals found"); - return Ok(()); - } - - list_stratagem_configurations(r.objects, display_type); - } - StratagemInterval::Add(c) => { - let r = post(StratagemConfiguration::endpoint_name(), c).await?; - - let CmdWrapper { command } = handle_cmd_resp(r).await?; - - wait_for_cmd_display(command).await?; - } - StratagemInterval::Update(c) => { - let x = get_stratagem_config_by_fs_name(&c.filesystem).await?; - - let r = put( - &format!("{}/{}", StratagemConfiguration::endpoint_name(), x.id), - c, - ) - .await?; - - let CmdWrapper { command } = handle_cmd_resp(r).await?; - - wait_for_cmd_display(command).await?; - } - StratagemInterval::Remove(StratagemRemoveData { name }) => { - let x = get_stratagem_config_by_fs_name(&name).await?; - - let r = delete( - &format!("{}/{}", StratagemConfiguration::endpoint_name(), x.id), - Vec::<(String, String)>::new(), - ) - .await?; - - let CmdWrapper { command } = handle_cmd_resp(r).await?; - - wait_for_cmd_display(command).await?; - } - }, + StratagemCommand::Interval(cmd) => interval_cli(cmd).await?, + StratagemCommand::Report { command } => { + report_cli(command.unwrap_or(ReportCommand::List { + display_type: DisplayType::Tabular, + })) + .await? + } }; Ok(()) diff --git a/iml-wire-types/src/lib.rs b/iml-wire-types/src/lib.rs index d516ffdd5d..e39feaa4c4 100644 --- a/iml-wire-types/src/lib.rs +++ b/iml-wire-types/src/lib.rs @@ -1315,6 +1315,18 @@ impl EndpointName for StratagemConfiguration { } } +#[derive(serde::Serialize, serde::Deserialize, Clone, Debug)] +#[cfg_attr(feature = "graphql", derive(juniper::GraphQLObject))] +/// Information about a stratagem report +pub struct StratagemReport { + /// The filename of the stratagem report + pub filename: String, + /// When the report was last modified + pub modify_time: DateTime, + /// The size of the report in bytes + pub size: i32, +} + /// An `AlertType` record from `api/alert_type`. #[derive(serde::Serialize, serde::Deserialize, Clone, Debug, Eq, PartialEq)] pub struct AlertType {