diff --git a/cmd/zfs_object_agent/Cargo.lock b/cmd/zfs_object_agent/Cargo.lock index 761b07a4441e..ffb77e5bcae0 100644 --- a/cmd/zfs_object_agent/Cargo.lock +++ b/cmd/zfs_object_agent/Cargo.lock @@ -313,8 +313,8 @@ version = "0.20.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "51e3973b165dc0f435831a9e426de67e894de532754ff7a3f307c03ee5dec7dc" dependencies = [ - "clap", - "heck", + "clap 2.34.0", + "heck 0.3.3", "indexmap", "log", "proc-macro2", @@ -361,12 +361,42 @@ dependencies = [ "ansi_term", "atty", "bitflags", - "strsim", - "textwrap", + "strsim 0.8.0", + "textwrap 0.11.0", "unicode-width", "vec_map", ] +[[package]] +name = "clap" +version = "3.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d8c93436c21e4698bacadf42917db28b23017027a4deccb35dbe47a7e7840123" +dependencies = [ + "atty", + "bitflags", + "clap_derive", + "indexmap", + "lazy_static", + "os_str_bytes", + "strsim 0.10.0", + "termcolor", + "textwrap 0.15.0", +] + +[[package]] +name = "clap_derive" +version = "3.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "da95d038ede1a964ce99f49cbe27a7fb538d1da595e4b4f70b8c8f338d17bf16" +dependencies = [ + "heck 0.4.0", + "proc-macro-error", + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "config" version = "0.12.0" @@ -930,6 +960,12 @@ dependencies = [ "unicode-segmentation", ] +[[package]] +name = "heck" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2540771e65fc8cb83cd6e8a237f70c319bd5c29f78ed1084ba5d50eeac86f7f9" + [[package]] name = "hermit-abi" version = "0.1.19" @@ -1271,7 +1307,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1e7a3760cebfa88dc786e9a75654e01297b28a0ab28cd94e686a42f96823b9ff" dependencies = [ "aspect-weave", - "heck", + "heck 0.3.3", "indexmap", "proc-macro2", "quote", @@ -1528,6 +1564,15 @@ dependencies = [ "hashbrown 0.9.1", ] +[[package]] +name = "os_str_bytes" +version = "6.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e22443d1643a904602595ba1cd8f7d896afe56d26712531c5ff73a15b2fbf64" +dependencies = [ + "memchr", +] + [[package]] name = "parking_lot" version = "0.11.2" @@ -2283,6 +2328,12 @@ version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8ea5119cdb4c55b55d432abb513a0429384878c15dde60cc77b1c99de1a95a6a" +[[package]] +name = "strsim" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623" + [[package]] name = "subtle" version = "2.4.1" @@ -2340,6 +2391,15 @@ dependencies = [ "winapi 0.3.9", ] +[[package]] +name = "termcolor" +version = "1.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bab24d30b911b2376f3a13cc2cd443142f0c81dda04c118693e35b3835757755" +dependencies = [ + "winapi-util", +] + [[package]] name = "termion" version = "1.5.6" @@ -2374,6 +2434,12 @@ dependencies = [ "unicode-width", ] +[[package]] +name = "textwrap" +version = "0.15.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b1141d4d61095b28419e22cb0bbf02755f5e54e0526f97f1e3d1d160e60885fb" + [[package]] name = "thiserror" version = "1.0.30" @@ -2739,6 +2805,15 @@ version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" +[[package]] +name = "winapi-util" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "70ec6ce85bb158151cae5e5c87f95a8e97d2c0c4b001223f33a334e3ce5de178" +dependencies = [ + "winapi 0.3.9", +] + [[package]] name = "winapi-x86_64-pc-windows-gnu" version = "0.4.0" @@ -2810,7 +2885,7 @@ dependencies = [ "anyhow", "async-trait", "chrono", - "clap", + "clap 2.34.0", "exitcode", "log", "more-asserts", @@ -2829,7 +2904,7 @@ name = "zcachedb" version = "0.1.0" dependencies = [ "anyhow", - "clap", + "clap 2.34.0", "exitcode", "git-version", "libc", @@ -2933,7 +3008,7 @@ name = "zfs_object_agent" version = "0.1.0" dependencies = [ "chrono", - "clap", + "clap 2.34.0", "git-version", "lazy_static", "log", @@ -2947,7 +3022,7 @@ dependencies = [ name = "zfs_object_perf" version = "0.1.0" dependencies = [ - "clap", + "clap 2.34.0", "futures", "git-version", "log", @@ -2986,7 +3061,7 @@ version = "0.1.0" dependencies = [ "anyhow", "chrono", - "clap", + "clap 3.1.6", "futures", "git-version", "itertools", diff --git a/cmd/zfs_object_agent/client/Cargo.toml b/cmd/zfs_object_agent/client/Cargo.toml index 0977b2a37fbd..1897206b58d9 100644 --- a/cmd/zfs_object_agent/client/Cargo.toml +++ b/cmd/zfs_object_agent/client/Cargo.toml @@ -10,7 +10,7 @@ publish = false [dependencies] anyhow = "1.0" chrono = "0.4.19" -clap = "2" +clap = { version = "3.1.6", features = ["derive"] } futures = "0.3.13" git-version = "0.3.5" itertools = "0.10.3" diff --git a/cmd/zfs_object_agent/client/src/main.rs b/cmd/zfs_object_agent/client/src/main.rs index 1030c52bc35b..1c1d33b8e9d7 100644 --- a/cmd/zfs_object_agent/client/src/main.rs +++ b/cmd/zfs_object_agent/client/src/main.rs @@ -12,8 +12,8 @@ use ::util::writeln_stderr; use ::util::writeln_stdout; use chrono::prelude::*; use chrono::DateTime; -use clap::Arg; -use clap::SubCommand; +use clap::Parser; +use clap::Subcommand; use client::Client; use futures::stream::StreamExt; use git_version::git_version; @@ -397,6 +397,42 @@ async fn do_destroy_old_pools( Ok(()) } +async fn do_dump_object( + object_access: Arc, + pool_guid: u64, + object: u64, + verbose: usize, +) { + match DataObject::get( + &object_access, + PoolGuid(pool_guid), + ObjectId::new(BlockId(object)), + ObjectAccessOpType::ReadsGet, + true, + ) + .await + { + Ok(obj) => { + writeln_stdout!("Data object header: {}", obj); + for (k, v) in obj.blocks.iter().sorted() { + if verbose < 2 { + writeln_stdout!( + "Block id: {}, Length: {}, Contents: {:?}...", + k, + v.len(), + v.slice(0..8) + ); + } else { + writeln_stdout!("Block id: {}, Length: {}, Contents: {:?}", k, v.len(), v); + } + } + } + Err(e) => { + writeln_stderr!("Failed to access DataObject: {:?}", e); + } + }; +} + fn get_object_access( endpoint: &str, region: &str, @@ -419,227 +455,97 @@ fn get_object_access( } } +#[derive(Parser)] +//#[clap(long_about = None)] +#[clap(version=GIT_VERSION)] +#[clap(about = "ZFS Object Agent test")] +#[clap(propagate_version = true)] +struct Cli { + #[clap(short, long, help = "S3 endpoint", default_value = ENDPOINT)] + endpoint: String, + #[clap(short, long, help = "S3 region", default_value = REGION)] + region: String, + #[clap(short, long, help = "S3 bucket", default_value = BUCKET_NAME)] + bucket: String, + #[clap(short, long, help = "credentials profile", default_value = "default")] + profile: String, + #[clap(short = 'i', long, requires = "aws-secret-access-key")] + aws_access_key_id: Option, + #[clap(short = 's', long, requires = "aws-access-key-id")] + aws_secret_access_key: Option, + #[clap(short, long, parse(from_occurrences))] + verbose: usize, + #[clap(subcommand)] + command: Commands, +} + +#[derive(Subcommand)] +enum Commands { + S3Rusoto, + Create, + Write, + Read, + Free, + Btree, + Nvpair, + RusotoRole, + ListPools, + ListPoolObjects, + DestroyOldPools { + #[clap(short = 'd', long)] + days: u64, + }, + DumpObject { + #[clap(short, long)] + pool_guid: u64, + #[clap(short, long)] + object: u64, + }, +} + #[tokio::main] -async fn main() { - let matches = clap::App::new("zoa_test") - .about("ZFS object agent test") - .version(GIT_VERSION) - .arg( - Arg::with_name("endpoint") - .short("e") - .long("endpoint") - .help("S3 endpoint") - .takes_value(true) - .default_value(ENDPOINT), - ) - .arg( - Arg::with_name("region") - .short("r") - .long("region") - .help("S3 region") - .takes_value(true) - .default_value(REGION), - ) - .arg( - Arg::with_name("bucket") - .short("b") - .long("bucket") - .help("S3 bucket") - .takes_value(true) - .default_value(BUCKET_NAME), - ) - .arg( - Arg::with_name("profile") - .short("p") - .long("profile") - .help("credentials profile") - .takes_value(true) - .default_value("default"), - ) - .arg( - Arg::with_name("aws_access_key_id") - .short("i") - .long("aws_access_key_id") - .takes_value(true) - .help("AWS access key id"), - ) - .arg( - Arg::with_name("aws_secret_access_key") - .short("s") - .long("aws_secret_access_key") - .takes_value(true) - .help("AWS secret access key"), - ) - .arg( - Arg::with_name("verbosity") - .short("v") - .long("verbose") - .help("verbosity") - .multiple(true), - ) - .subcommand(SubCommand::with_name("s3").about("s3 test")) - .subcommand(SubCommand::with_name("s3_rusoto").about("s3 rusoto test")) - .subcommand(SubCommand::with_name("create").about("create test ")) - .subcommand(SubCommand::with_name("write").about("write test")) - .subcommand(SubCommand::with_name("read").about("read test")) - .subcommand(SubCommand::with_name("free").about("free test")) - .subcommand(SubCommand::with_name("btree").about("btree test")) - .subcommand(SubCommand::with_name("nvpair").about("nvpair test")) - .subcommand(SubCommand::with_name("rusoto_role").about("rusoto credentials test")) - .subcommand(SubCommand::with_name("list_pools").about("list pools")) - .subcommand(SubCommand::with_name("list_pool_objects").about("list pool objects")) - .subcommand( - SubCommand::with_name("destroy_old_pools") - .about("destroy old pools") - .arg( - Arg::with_name("number-of-days") - .short("d") - .long("number-of-days") - .required(true) - .takes_value(true) - .help("number of days"), - ), - ) - .subcommand( - SubCommand::with_name("dump_object") - .about("dump data object") - .arg( - Arg::with_name("pool_guid") - .short("p") - .long("pool_guid") - .required(true) - .takes_value(true) - .help("guid of pool to retrieve object from"), - ) - .arg( - Arg::with_name("object_id") - .short("o") - .long("object_id") - .required(true) - .takes_value(true) - .help("id of object to retreive"), - ), - ) - .get_matches(); - - // Command line parameters - let endpoint = matches.value_of("endpoint").unwrap(); - let region_str = matches.value_of("region").unwrap(); - let bucket_name = matches.value_of("bucket").unwrap(); - let profile = matches.value_of("profile").unwrap(); - let aws_access_key_id = matches.value_of("aws_access_key_id"); - let aws_secret_access_key = matches.value_of("aws_secret_access_key"); - let verbosity = matches.occurrences_of("verbosity"); - - if aws_access_key_id.is_some() != aws_secret_access_key.is_some() { - matches.usage(); - panic!("Error: Both aws_access_key_id and aws_secret_access_key should be specified."); - } +async fn main() -> Result<(), Box> { + let cli = Cli::parse(); - if verbosity > 0 || matches.subcommand_name().is_none() { + if cli.verbose > 0 { println!( "endpoint: {}, region: {}, bucket: {} profile: {} access_id: {:?}, secret_key: {:?}", - endpoint, region_str, bucket_name, profile, aws_access_key_id, aws_secret_access_key + cli.endpoint, + cli.region, + cli.bucket, + cli.profile, + cli.aws_access_key_id, + cli.aws_secret_access_key ); } let object_access = get_object_access( - endpoint, - region_str, - bucket_name, - profile, - aws_access_key_id, - aws_secret_access_key, + &cli.endpoint, + &cli.region, + &cli.bucket, + &cli.profile, + cli.aws_access_key_id.as_deref(), + cli.aws_secret_access_key.as_deref(), ); - match matches.subcommand() { - ("s3_rusoto", Some(_matches)) => { - do_s3_rusoto().await.unwrap(); - } - ("create", Some(_matches)) => { - do_create().await.unwrap(); - } - ("write", Some(_matches)) => { - do_write().await.unwrap(); - } - ("read", Some(_matches)) => { - do_read().await.unwrap(); - } - ("free", Some(_matches)) => { - do_free().await.unwrap(); - } - ("btree", Some(_matches)) => { - do_btree(); - } - ("nvpair", Some(_matches)) => { - do_nvpair(); - } - ("rusoto_role", Some(_matches)) => { - do_rusoto_role().await.unwrap(); - } - ("list_pools", Some(_matches)) => { - do_list_pools(&object_access, false).await.unwrap(); - } - ("list_pool_objects", Some(_matches)) => { - do_list_pools(&object_access, true).await.unwrap(); - } - ("destroy_old_pools", Some(destroy_matches)) => { - let age_str = destroy_matches.value_of("number-of-days").unwrap(); - let min_age = Duration::from_secs(age_str.parse::().unwrap() * 60 * 60 * 24); - - do_destroy_old_pools(&object_access, min_age).await.unwrap(); + match cli.command { + Commands::S3Rusoto => do_s3_rusoto().await?, + Commands::Create => do_create().await?, + Commands::Write => do_write().await?, + Commands::Read => do_read().await?, + Commands::Free => do_free().await?, + Commands::Btree => do_btree(), + Commands::Nvpair => do_nvpair(), + Commands::RusotoRole => do_rusoto_role().await?, + Commands::ListPools => do_list_pools(&object_access, false).await?, + Commands::ListPoolObjects => do_list_pools(&object_access, true).await?, + Commands::DestroyOldPools { days } => { + let min_age = Duration::from_secs(days * 60 * 60 * 24); + do_destroy_old_pools(&object_access, min_age).await?; } - ("dump_object", Some(dump_matches)) => { - let pool_guid = match str::parse::(dump_matches.value_of("pool_guid").unwrap()) { - Ok(value) => PoolGuid(value), - Err(_e) => { - writeln_stderr!("Failed to parse pool guid"); - return; - } - }; - let object = match str::parse::(dump_matches.value_of("object_id").unwrap()) { - Ok(value) => ObjectId::new(BlockId(value)), - Err(_e) => { - writeln_stderr!("Failed to parse object id"); - return; - } - }; - match DataObject::get( - &object_access, - pool_guid, - object, - ObjectAccessOpType::ReadsGet, - true, - ) - .await - { - Ok(obj) => { - writeln_stdout!("Data object header: {}", obj); - for (k, v) in obj.blocks.iter().sorted() { - if verbosity < 2 { - writeln_stdout!( - "Block id: {}, Length: {}, Contents: {:?}...", - k, - v.len(), - v.slice(0..8) - ); - } else { - writeln_stdout!( - "Block id: {}, Length: {}, Contents: {:?}", - k, - v.len(), - v - ); - } - } - } - Err(e) => { - writeln_stderr!("Failed to access DataObject: {:?}", e); - } - }; + Commands::DumpObject { pool_guid, object } => { + do_dump_object(object_access, pool_guid, object, cli.verbose).await } - _ => { - matches.usage(); - } - }; + } + Ok(()) }