Skip to content
This repository has been archived by the owner on Jul 25, 2022. It is now read-only.

Add CLI for stratagem reports #2323

Merged
merged 1 commit into from
Oct 13, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 1 addition & 12 deletions iml-api/src/graphql.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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::{
Expand Down Expand Up @@ -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<Utc>,
/// The size of the report in bytes
size: i32,
}

pub(crate) struct QueryRoot;

#[juniper::graphql_object(Context = Context)]
Expand Down
1 change: 1 addition & 0 deletions iml-graphql-queries/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
// license that can be found in the LICENSE file.

pub mod snapshot;
pub mod stratagem;

use std::fmt;

Expand Down
64 changes: 64 additions & 0 deletions iml-graphql-queries/src/stratagem.rs
Original file line number Diff line number Diff line change
@@ -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<Vars> {
Query {
query: QUERY.to_string(),
variables: None,
}
}

#[derive(Debug, Clone, serde::Deserialize)]
pub struct Resp {
#[serde(rename(deserialize = "stratagemReports"))]
pub stratagem_reports: Vec<StratagemReport>,
}
}

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<Vars> {
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,
}
}
12 changes: 11 additions & 1 deletion iml-manager-cli/src/display_utils.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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};
Expand Down Expand Up @@ -186,6 +186,16 @@ impl IntoTable for Vec<SnapshotRetention> {
}
}

impl IntoTable for Vec<StratagemReport> {
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()]),
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit, it would be nice to display this as a human readable string (number-formatters). This file could be pretty large

)
}
}

impl IntoTable for Vec<Host> {
fn into_table(self) -> Table {
generate_table(
Expand Down
187 changes: 114 additions & 73 deletions iml-manager-cli/src/stratagem.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,14 +3,15 @@
// 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,
RunStratagemValidationError,
},
};
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};
Expand All @@ -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<ReportCommand>,
},
}

#[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<String>,
},
}

#[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))]
Expand All @@ -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))]
Expand Down Expand Up @@ -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<stratagem_queries::list_reports::Resp> =
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<stratagem_queries::delete_report::Resp> =
graphql(query).await?;
}
Ok(())
}
}
}

async fn interval_cli(cmd: IntervalCommand) -> Result<(), ImlManagerCliError> {
match cmd {
IntervalCommand::List { display_type } => {
let r: ApiList<StratagemConfiguration> = 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) => {
Expand Down Expand Up @@ -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<StratagemConfiguration> = 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(())
Expand Down
Loading