diff --git a/src/api2/config/datastore.rs b/src/api2/config/datastore.rs index dbbe33ae..13048a6c 100644 --- a/src/api2/config/datastore.rs +++ b/src/api2/config/datastore.rs @@ -3,19 +3,26 @@ use std::path::PathBuf; use failure::*; use serde_json::Value; -use proxmox::api::{ApiHandler, ApiMethod, Router, RpcEnvironment}; -use proxmox::api::schema::*; +use proxmox::api::{api, ApiMethod, Router, RpcEnvironment}; use crate::api2::types::*; use crate::backup::*; use crate::config::datastore; -pub const GET: ApiMethod = ApiMethod::new( - &ApiHandler::Sync(&get_datastore_list), - &ObjectSchema::new("Directory index.", &[]) -); - -fn get_datastore_list( +#[api( + input: { + properties: {}, + }, + returns: { + description: "List the configured datastores.", + type: Array, + items: { + type: datastore::DataStoreConfig, + }, + }, +)] +/// List all datastores +pub fn list_datastores( _param: Value, _info: &ApiMethod, _rpcenv: &mut dyn RpcEnvironment, @@ -26,90 +33,83 @@ fn get_datastore_list( Ok(config.convert_to_array("name")) } -pub const POST: ApiMethod = ApiMethod::new( - &ApiHandler::Sync(&create_datastore), - &ObjectSchema::new( - "Create new datastore.", - &[ - ("comment", true, &StringSchema::new("Comment for this Datastore").schema()), - ("name", false, &DATASTORE_SCHEMA), - ("path", false, &StringSchema::new("Directory path. The directory path is created if it does not already exist.").schema()), - ], - ) -).protected(true); - -fn create_datastore( - param: Value, - _info: &ApiMethod, - _rpcenv: &mut dyn RpcEnvironment, -) -> Result { +#[api( + protected: true, + input: { + properties: { + name: { + schema: DATASTORE_SCHEMA, + }, + comment: { + optional: true, + schema: datastore::COMMENT_SCHEMA, + }, + path: { + schema: datastore::DIR_NAME_SCHEMA, + }, + }, + }, +)] +/// Create new datastore config. +pub fn create_datastore(name: String, param: Value) -> Result<(), Error> { // fixme: locking ? + let datastore: datastore::DataStoreConfig = serde_json::from_value(param.clone())?; + let mut config = datastore::config()?; - let name = param["name"].as_str().unwrap(); - - if let Some(_) = config.sections.get(name) { + if let Some(_) = config.sections.get(&name) { bail!("datastore '{}' already exists.", name); } - let path: PathBuf = param["path"].as_str().unwrap().into(); - let backup_user = crate::backup::backup_user()?; - let _store = ChunkStore::create(name, path, backup_user.uid, backup_user.gid)?; - - let mut datastore = serde_json::Map::new(); - datastore.insert("path".to_string(), param["path"].clone()); - if let Some(comment) = param.get("comment") { - if comment.as_str().unwrap().find(|c: char| c.is_control()).is_some() { + if let Some(ref comment) = datastore.comment { + if comment.find(|c: char| c.is_control()).is_some() { bail!("comment must not contain control characters!"); } - - datastore.insert("comment".to_string(), comment.clone()); } - config.set_data(name, "datastore", Value::Object(datastore)); + let path: PathBuf = datastore.path.clone().into(); + + let backup_user = crate::backup::backup_user()?; + let _store = ChunkStore::create(&name, path, backup_user.uid, backup_user.gid)?; + + config.set_data(&name, "datastore", serde_json::to_value(datastore)?); datastore::save_config(&config)?; - Ok(Value::Null) + Ok(()) } -pub const DELETE: ApiMethod = ApiMethod::new( - &ApiHandler::Sync(&delete_datastore), - &ObjectSchema::new( - "Remove a datastore configuration.", - &[ - ("name", false, &DATASTORE_SCHEMA), - ], - ) -).protected(true); - -fn delete_datastore( - param: Value, - _info: &ApiMethod, - _rpcenv: &mut dyn RpcEnvironment, -) -> Result { - println!("This is a test {}", param); +#[api( + protected: true, + input: { + properties: { + name: { + schema: DATASTORE_SCHEMA, + }, + }, + }, +)] +/// Remove a datastore configuration. +pub fn delete_datastore(name: String) -> Result<(), Error> { // fixme: locking ? // fixme: check digest ? let mut config = datastore::config()?; - let name = param["name"].as_str().unwrap(); - - match config.sections.get(name) { - Some(_) => { config.sections.remove(name); }, + match config.sections.get(&name) { + Some(_) => { config.sections.remove(&name); }, None => bail!("datastore '{}' does not exist.", name), } datastore::save_config(&config)?; - Ok(Value::Null) + Ok(()) } pub const ROUTER: Router = Router::new() - .get(&GET) - .post(&POST) - .delete(&DELETE); + .get(&API_METHOD_LIST_DATASTORES) + .post(&API_METHOD_CREATE_DATASTORE) + .delete(&API_METHOD_DELETE_DATASTORE); diff --git a/src/api2/config/remotes.rs b/src/api2/config/remotes.rs index 45493304..7e822789 100644 --- a/src/api2/config/remotes.rs +++ b/src/api2/config/remotes.rs @@ -30,6 +30,7 @@ pub fn list_remotes( } #[api( + protected: true, input: { properties: { name: { @@ -72,6 +73,7 @@ pub fn create_remote(name: String, param: Value) -> Result<(), Error> { } #[api( + protected: true, input: { properties: { name: { diff --git a/src/api2/types.rs b/src/api2/types.rs index ae4cadb4..e89e951f 100644 --- a/src/api2/types.rs +++ b/src/api2/types.rs @@ -113,6 +113,7 @@ pub const UPID_SCHEMA: Schema = StringSchema::new("Unique Process/Task ID.") pub const DATASTORE_SCHEMA: Schema = StringSchema::new("Datastore name.") .format(&PROXMOX_SAFE_ID_FORMAT) + .min_length(3) .max_length(32) .schema(); diff --git a/src/bin/proxmox-backup-manager.rs b/src/bin/proxmox-backup-manager.rs index 52233702..6eeb30d5 100644 --- a/src/bin/proxmox-backup-manager.rs +++ b/src/bin/proxmox-backup-manager.rs @@ -44,18 +44,40 @@ fn connect() -> Result { Ok(client) } +fn remotes_commands() -> CommandLineInterface { + + use proxmox_backup::api2; + + let cmd_def = CliCommandMap::new() + .insert("list", CliCommand::new(&api2::config::remotes::API_METHOD_LIST_REMOTES)) + .insert( + "create", + // fixme: howto handle password parameter? + CliCommand::new(&api2::config::remotes::API_METHOD_CREATE_REMOTE) + .arg_param(&["name"]) + ) + .insert( + "remove", + CliCommand::new(&api2::config::remotes::API_METHOD_DELETE_REMOTE) + .arg_param(&["name"]) + .completion_cb("name", config::remotes::complete_remote_name) + ); + + cmd_def.into() +} + fn datastore_commands() -> CommandLineInterface { use proxmox_backup::api2; let cmd_def = CliCommandMap::new() - .insert("list", CliCommand::new(&api2::config::datastore::GET)) + .insert("list", CliCommand::new(&api2::config::datastore::API_METHOD_LIST_DATASTORES)) .insert("create", - CliCommand::new(&api2::config::datastore::POST) + CliCommand::new(&api2::config::datastore::API_METHOD_CREATE_DATASTORE) .arg_param(&["name", "path"]) ) .insert("remove", - CliCommand::new(&api2::config::datastore::DELETE) + CliCommand::new(&api2::config::datastore::API_METHOD_DELETE_DATASTORE) .arg_param(&["name"]) .completion_cb("name", config::datastore::complete_datastore_name) ); @@ -360,8 +382,7 @@ fn cert_mgmt_cli() -> CommandLineInterface { schema: DATASTORE_SCHEMA, }, remote: { - description: "Remote name.", // fixme: remote ID schema - type: String, + schema: crate::config::remotes::REMOTE_ID_SCHEMA, }, "remote-store": { schema: DATASTORE_SCHEMA, @@ -407,6 +428,7 @@ fn main() { let cmd_def = CliCommandMap::new() .insert("datastore", datastore_commands()) + .insert("remotes", remotes_commands()) .insert("garbage-collection", garbage_collection_commands()) .insert("cert", cert_mgmt_cli()) .insert("task", task_mgmt_cli()) diff --git a/src/config/datastore.rs b/src/config/datastore.rs index e6c5c7a2..e27f81db 100644 --- a/src/config/datastore.rs +++ b/src/config/datastore.rs @@ -6,6 +6,7 @@ use serde::{Serialize, Deserialize}; use proxmox::api::{api, schema::*}; use proxmox::tools::{fs::replace_file, fs::CreateOptions}; +use crate::api2::types::*; use crate::section_config::{SectionConfig, SectionConfigData, SectionConfigPlugin}; lazy_static! { @@ -16,9 +17,6 @@ lazy_static! { pub const DIR_NAME_SCHEMA: Schema = StringSchema::new("Directory name").schema(); pub const COMMENT_SCHEMA: Schema = StringSchema::new("Datastore comment").schema(); -pub const DATASTORE_ID_SCHEMA: Schema = StringSchema::new("DataStore ID schema.") - .min_length(3) - .schema(); #[api( properties: { @@ -45,7 +43,7 @@ fn init() -> SectionConfig { }; let plugin = SectionConfigPlugin::new("datastore".to_string(), obj_schema); - let mut config = SectionConfig::new(&DATASTORE_ID_SCHEMA); + let mut config = SectionConfig::new(&DATASTORE_SCHEMA); config.register_plugin(plugin); config