src/config/datastore.rs: add prune settings
This commit is contained in:
		| @ -4,11 +4,11 @@ use anyhow::{bail, Error}; | ||||
| use serde_json::Value; | ||||
| use ::serde::{Deserialize, Serialize}; | ||||
|  | ||||
| use proxmox::api::{api, ApiMethod, Router, RpcEnvironment, Permission}; | ||||
| use proxmox::api::{api, Router, RpcEnvironment, Permission}; | ||||
|  | ||||
| use crate::api2::types::*; | ||||
| use crate::backup::*; | ||||
| use crate::config::datastore; | ||||
| use crate::config::datastore::{self, DataStoreConfig, DIR_NAME_SCHEMA}; | ||||
| use crate::config::acl::{PRIV_DATASTORE_AUDIT, PRIV_DATASTORE_MODIFY}; | ||||
|  | ||||
| #[api( | ||||
| @ -18,25 +18,7 @@ use crate::config::acl::{PRIV_DATASTORE_AUDIT, PRIV_DATASTORE_MODIFY}; | ||||
|     returns: { | ||||
|         description: "List the configured datastores (with config digest).", | ||||
|         type: Array, | ||||
|         items: { | ||||
|             description: "Datastore configuration.", | ||||
|             properties: { | ||||
|                 name: { | ||||
|                     schema: DATASTORE_SCHEMA, | ||||
|                 }, | ||||
|                 path: { | ||||
|                     schema: datastore::DIR_NAME_SCHEMA, | ||||
|                 }, | ||||
|                 "gc-schedule": { | ||||
|                     optional: true, | ||||
|                     schema: GC_SCHEDULE_SCHEMA, | ||||
|                 }, | ||||
|                 comment: { | ||||
|                     optional: true, | ||||
|                     schema: SINGLE_LINE_COMMENT_SCHEMA, | ||||
|                 }, | ||||
|             }, | ||||
|         }, | ||||
|         items: { type: datastore::DataStoreConfig }, | ||||
|     }, | ||||
|     access: { | ||||
|         permission: &Permission::Privilege(&["datastore"], PRIV_DATASTORE_AUDIT, false), | ||||
| @ -45,15 +27,23 @@ use crate::config::acl::{PRIV_DATASTORE_AUDIT, PRIV_DATASTORE_MODIFY}; | ||||
| /// List all datastores | ||||
| pub fn list_datastores( | ||||
|     _param: Value, | ||||
|     _info: &ApiMethod, | ||||
|     _rpcenv: &mut dyn RpcEnvironment, | ||||
| ) -> Result<Value, Error> { | ||||
|     mut rpcenv: &mut dyn RpcEnvironment, | ||||
| ) -> Result<Vec<DataStoreConfig>, Error> { | ||||
|  | ||||
|     let (config, digest) = datastore::config()?; | ||||
|  | ||||
|     Ok(config.convert_to_array("name", Some(&digest), &[])) | ||||
|     let list = config.convert_to_typed_array("datastore")?; | ||||
|  | ||||
|     rpcenv["digest"] = proxmox::tools::digest_to_hex(&digest).into(); | ||||
|  | ||||
|     Ok(list) | ||||
| } | ||||
|  | ||||
|  | ||||
| // fixme: impl. const fn get_object_schema(datastore::DataStoreConfig::API_SCHEMA), | ||||
| // but this need support for match inside const fn | ||||
| // see: https://github.com/rust-lang/rust/issues/49146 | ||||
|  | ||||
| #[api( | ||||
|     protected: true, | ||||
|     input: { | ||||
| @ -61,6 +51,9 @@ pub fn list_datastores( | ||||
|             name: { | ||||
|                 schema: DATASTORE_SCHEMA, | ||||
|             }, | ||||
|             path: { | ||||
|                 schema: DIR_NAME_SCHEMA, | ||||
|             }, | ||||
|             comment: { | ||||
|                 optional: true, | ||||
|                 schema: SINGLE_LINE_COMMENT_SCHEMA, | ||||
| @ -69,8 +62,33 @@ pub fn list_datastores( | ||||
|                 optional: true, | ||||
|                 schema: GC_SCHEDULE_SCHEMA, | ||||
|             }, | ||||
|             path: { | ||||
|                 schema: datastore::DIR_NAME_SCHEMA, | ||||
|             "prune-schedule": { | ||||
|                 optional: true, | ||||
|                 schema: PRUNE_SCHEDULE_SCHEMA, | ||||
|             }, | ||||
|             "keep-last": { | ||||
|                 optional: true, | ||||
|                 schema: PRUNE_SCHEMA_KEEP_LAST, | ||||
|             }, | ||||
|             "keep-hourly": { | ||||
|                 optional: true, | ||||
|                 schema: PRUNE_SCHEMA_KEEP_HOURLY, | ||||
|             }, | ||||
|             "keep-daily": { | ||||
|                 optional: true, | ||||
|                 schema: PRUNE_SCHEMA_KEEP_DAILY, | ||||
|             }, | ||||
|             "keep-weekly": { | ||||
|                 optional: true, | ||||
|                 schema: PRUNE_SCHEMA_KEEP_WEEKLY, | ||||
|             }, | ||||
|             "keep-monthly": { | ||||
|                 optional: true, | ||||
|                 schema: PRUNE_SCHEMA_KEEP_MONTHLY, | ||||
|             }, | ||||
|             "keep-yearly": { | ||||
|                 optional: true, | ||||
|                 schema: PRUNE_SCHEMA_KEEP_YEARLY, | ||||
|             }, | ||||
|         }, | ||||
|     }, | ||||
| @ -79,7 +97,7 @@ pub fn list_datastores( | ||||
|     }, | ||||
| )] | ||||
| /// Create new datastore config. | ||||
| pub fn create_datastore(name: String, param: Value) -> Result<(), Error> { | ||||
| pub fn create_datastore(param: Value) -> Result<(), Error> { | ||||
|  | ||||
|     let _lock = crate::tools::open_file_locked(datastore::DATASTORE_CFG_LOCKFILE, std::time::Duration::new(10, 0))?; | ||||
|  | ||||
| @ -87,16 +105,16 @@ pub fn create_datastore(name: String, param: Value) -> Result<(), Error> { | ||||
|  | ||||
|     let (mut config, _digest) = datastore::config()?; | ||||
|  | ||||
|     if let Some(_) = config.sections.get(&name) { | ||||
|         bail!("datastore '{}' already exists.", name); | ||||
|     if let Some(_) = config.sections.get(&datastore.name) { | ||||
|         bail!("datastore '{}' already exists.", datastore.name); | ||||
|     } | ||||
|  | ||||
|     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)?; | ||||
|     let _store = ChunkStore::create(&datastore.name, path, backup_user.uid, backup_user.gid)?; | ||||
|  | ||||
|     config.set_data(&name, "datastore", &datastore)?; | ||||
|     config.set_data(&datastore.name, "datastore", &datastore)?; | ||||
|  | ||||
|     datastore::save_config(&config)?; | ||||
|  | ||||
| @ -120,12 +138,16 @@ pub fn create_datastore(name: String, param: Value) -> Result<(), Error> { | ||||
|     }, | ||||
| )] | ||||
| /// Read a datastore configuration. | ||||
| pub fn read_datastore(name: String) -> Result<Value, Error> { | ||||
| pub fn read_datastore( | ||||
|     name: String, | ||||
|     mut rpcenv: &mut dyn RpcEnvironment, | ||||
| ) -> Result<DataStoreConfig, Error> { | ||||
|     let (config, digest) = datastore::config()?; | ||||
|     let mut data = config.lookup_json("datastore", &name)?; | ||||
|     data.as_object_mut().unwrap() | ||||
|         .insert("digest".into(), proxmox::tools::digest_to_hex(&digest).into()); | ||||
|     Ok(data) | ||||
|  | ||||
|     let store_config = config.lookup("datastore", &name)?; | ||||
|     rpcenv["digest"] = proxmox::tools::digest_to_hex(&digest).into(); | ||||
|  | ||||
|     Ok(store_config) | ||||
| } | ||||
|  | ||||
| #[api()] | ||||
| @ -138,6 +160,20 @@ pub enum DeletableProperty { | ||||
|     comment, | ||||
|     /// Delete the garbage collection schedule. | ||||
|     gc_schedule, | ||||
|     /// Delete the prune job schedule. | ||||
|     prune_schedule, | ||||
|     /// Delete the keep-last property | ||||
|     keep_last, | ||||
|     /// Delete the keep-hourly property | ||||
|     keep_hourly, | ||||
|     /// Delete the keep-daily property | ||||
|     keep_daily, | ||||
|     /// Delete the keep-weekly property | ||||
|     keep_weekly, | ||||
|     /// Delete the keep-monthly property | ||||
|     keep_monthly, | ||||
|     /// Delete the keep-yearly property | ||||
|     keep_yearly, | ||||
| } | ||||
|  | ||||
| #[api( | ||||
| @ -155,6 +191,34 @@ pub enum DeletableProperty { | ||||
|                 optional: true, | ||||
|                 schema: GC_SCHEDULE_SCHEMA, | ||||
|             }, | ||||
|             "prune-schedule": { | ||||
|                 optional: true, | ||||
|                 schema: PRUNE_SCHEDULE_SCHEMA, | ||||
|             }, | ||||
|             "keep-last": { | ||||
|                 optional: true, | ||||
|                 schema: PRUNE_SCHEMA_KEEP_LAST, | ||||
|             }, | ||||
|             "keep-hourly": { | ||||
|                 optional: true, | ||||
|                 schema: PRUNE_SCHEMA_KEEP_HOURLY, | ||||
|             }, | ||||
|             "keep-daily": { | ||||
|                 optional: true, | ||||
|                 schema: PRUNE_SCHEMA_KEEP_DAILY, | ||||
|             }, | ||||
|             "keep-weekly": { | ||||
|                 optional: true, | ||||
|                 schema: PRUNE_SCHEMA_KEEP_WEEKLY, | ||||
|             }, | ||||
|             "keep-monthly": { | ||||
|                 optional: true, | ||||
|                 schema: PRUNE_SCHEMA_KEEP_MONTHLY, | ||||
|             }, | ||||
|             "keep-yearly": { | ||||
|                 optional: true, | ||||
|                 schema: PRUNE_SCHEMA_KEEP_YEARLY, | ||||
|             }, | ||||
|             delete: { | ||||
|                 description: "List of properties to delete.", | ||||
|                 type: Array, | ||||
| @ -178,6 +242,13 @@ pub fn update_datastore( | ||||
|     name: String, | ||||
|     comment: Option<String>, | ||||
|     gc_schedule: Option<String>, | ||||
|     prune_schedule: Option<String>, | ||||
|     keep_last: Option<i64>, | ||||
|     keep_hourly: Option<i64>, | ||||
|     keep_daily: Option<i64>, | ||||
|     keep_weekly: Option<i64>, | ||||
|     keep_monthly: Option<i64>, | ||||
|     keep_yearly: Option<i64>, | ||||
|     delete: Option<Vec<DeletableProperty>>, | ||||
|     digest: Option<String>, | ||||
| ) -> Result<(), Error> { | ||||
| @ -199,6 +270,13 @@ pub fn update_datastore( | ||||
|             match delete_prop { | ||||
|                 DeletableProperty::comment => { data.comment = None; }, | ||||
|                 DeletableProperty::gc_schedule => { data.gc_schedule = None; }, | ||||
|                 DeletableProperty::prune_schedule => { data.prune_schedule = None; }, | ||||
|                 DeletableProperty::keep_last => { data.keep_last = None; }, | ||||
|                 DeletableProperty::keep_hourly => { data.keep_hourly = None; }, | ||||
|                 DeletableProperty::keep_daily => { data.keep_daily = None; }, | ||||
|                 DeletableProperty::keep_weekly => { data.keep_weekly = None; }, | ||||
|                 DeletableProperty::keep_monthly => { data.keep_monthly = None; }, | ||||
|                 DeletableProperty::keep_yearly => { data.keep_yearly = None; }, | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| @ -213,6 +291,14 @@ pub fn update_datastore( | ||||
|     } | ||||
|  | ||||
|     if gc_schedule.is_some() { data.gc_schedule = gc_schedule; } | ||||
|     if prune_schedule.is_some() { data.prune_schedule = prune_schedule; } | ||||
|  | ||||
|     if keep_last.is_some() { data.keep_last = keep_last; } | ||||
|     if keep_hourly.is_some() { data.keep_hourly = keep_hourly; } | ||||
|     if keep_daily.is_some() { data.keep_daily = keep_daily; } | ||||
|     if keep_weekly.is_some() { data.keep_weekly = keep_weekly; } | ||||
|     if keep_monthly.is_some() { data.keep_monthly = keep_monthly; } | ||||
|     if keep_yearly.is_some() { data.keep_yearly = keep_yearly; } | ||||
|  | ||||
|     config.set_data(&name, "datastore", &data)?; | ||||
|  | ||||
|  | ||||
| @ -292,6 +292,11 @@ pub const GC_SCHEDULE_SCHEMA: Schema = StringSchema::new( | ||||
|     .format(&ApiStringFormat::VerifyFn(crate::tools::systemd::time::verify_calendar_event)) | ||||
|     .schema(); | ||||
|  | ||||
| pub const PRUNE_SCHEDULE_SCHEMA: Schema = StringSchema::new( | ||||
|     "Run prune job at specified schedule.") | ||||
|     .format(&ApiStringFormat::VerifyFn(crate::tools::systemd::time::verify_calendar_event)) | ||||
|     .schema(); | ||||
|  | ||||
| pub const REMOTE_ID_SCHEMA: Schema = StringSchema::new("Remote ID.") | ||||
|     .format(&PROXMOX_SAFE_ID_FORMAT) | ||||
|     .min_length(3) | ||||
|  | ||||
| @ -26,16 +26,47 @@ pub const DIR_NAME_SCHEMA: Schema = StringSchema::new("Directory name").schema() | ||||
|  | ||||
| #[api( | ||||
|     properties: { | ||||
|         name: { | ||||
|             schema: DATASTORE_SCHEMA, | ||||
|         }, | ||||
|         path: { | ||||
|             schema: DIR_NAME_SCHEMA, | ||||
|         }, | ||||
|         comment: { | ||||
|             optional: true, | ||||
|             schema: SINGLE_LINE_COMMENT_SCHEMA, | ||||
|         }, | ||||
|         "gc-schedule": { | ||||
|             schema: GC_SCHEDULE_SCHEMA, | ||||
|             optional: true, | ||||
|             schema: GC_SCHEDULE_SCHEMA, | ||||
|         }, | ||||
|         path: { | ||||
|             schema: DIR_NAME_SCHEMA, | ||||
|         "prune-schedule": { | ||||
|             optional: true, | ||||
|             schema: PRUNE_SCHEDULE_SCHEMA, | ||||
|         }, | ||||
|         "keep-last": { | ||||
|             optional: true, | ||||
|             schema: PRUNE_SCHEMA_KEEP_LAST, | ||||
|         }, | ||||
|         "keep-hourly": { | ||||
|             optional: true, | ||||
|             schema: PRUNE_SCHEMA_KEEP_HOURLY, | ||||
|         }, | ||||
|         "keep-daily": { | ||||
|             optional: true, | ||||
|             schema: PRUNE_SCHEMA_KEEP_DAILY, | ||||
|         }, | ||||
|         "keep-weekly": { | ||||
|             optional: true, | ||||
|             schema: PRUNE_SCHEMA_KEEP_WEEKLY, | ||||
|         }, | ||||
|         "keep-monthly": { | ||||
|             optional: true, | ||||
|             schema: PRUNE_SCHEMA_KEEP_MONTHLY, | ||||
|         }, | ||||
|         "keep-yearly": { | ||||
|             optional: true, | ||||
|             schema: PRUNE_SCHEMA_KEEP_YEARLY, | ||||
|         }, | ||||
|     } | ||||
| )] | ||||
| @ -43,12 +74,27 @@ pub const DIR_NAME_SCHEMA: Schema = StringSchema::new("Directory name").schema() | ||||
| #[derive(Serialize,Deserialize)] | ||||
| /// Datastore configuration properties. | ||||
| pub struct DataStoreConfig { | ||||
|     pub name: String, | ||||
|     #[serde(skip_serializing_if="Option::is_none")] | ||||
|     pub comment: Option<String>, | ||||
|     pub path: String, | ||||
|     #[serde(skip_serializing_if="Option::is_none")] | ||||
|     pub gc_schedule: Option<String>, | ||||
|  } | ||||
|     #[serde(skip_serializing_if="Option::is_none")] | ||||
|     pub prune_schedule: Option<String>, | ||||
|     #[serde(skip_serializing_if="Option::is_none")] | ||||
|     pub keep_last: Option<i64>, | ||||
|     #[serde(skip_serializing_if="Option::is_none")] | ||||
|     pub keep_hourly: Option<i64>, | ||||
|     #[serde(skip_serializing_if="Option::is_none")] | ||||
|     pub keep_daily: Option<i64>, | ||||
|     #[serde(skip_serializing_if="Option::is_none")] | ||||
|     pub keep_weekly: Option<i64>, | ||||
|     #[serde(skip_serializing_if="Option::is_none")] | ||||
|     pub keep_monthly: Option<i64>, | ||||
|     #[serde(skip_serializing_if="Option::is_none")] | ||||
|     pub keep_yearly: Option<i64>, | ||||
| } | ||||
|  | ||||
| fn init() -> SectionConfig { | ||||
|     let obj_schema = match DataStoreConfig::API_SCHEMA { | ||||
| @ -56,7 +102,7 @@ fn init() -> SectionConfig { | ||||
|         _ => unreachable!(), | ||||
|     }; | ||||
|  | ||||
|     let plugin = SectionConfigPlugin::new("datastore".to_string(), None, obj_schema); | ||||
|     let plugin = SectionConfigPlugin::new("datastore".to_string(), Some(String::from("name")), obj_schema); | ||||
|     let mut config = SectionConfig::new(&DATASTORE_SCHEMA); | ||||
|     config.register_plugin(plugin); | ||||
|  | ||||
|  | ||||
		Reference in New Issue
	
	Block a user