move datastore config to pbs_config workspace

This commit is contained in:
Dietmar Maurer 2021-09-10 08:40:58 +02:00
parent ba3d7e19fb
commit e7d4be9d85
24 changed files with 311 additions and 342 deletions

View File

@ -3,14 +3,16 @@ use serde::{Deserialize, Serialize};
use proxmox::api::api; use proxmox::api::api;
use proxmox::api::schema::{ use proxmox::api::schema::{
ApiStringFormat, ApiType, ArraySchema, EnumEntry, IntegerSchema, ReturnType, Schema, ApiStringFormat, ApiType, ArraySchema, EnumEntry, IntegerSchema, ReturnType, Schema,
StringSchema, StringSchema, Updater,
}; };
use proxmox::const_regex; use proxmox::const_regex;
use crate::{ use crate::{
PROXMOX_SAFE_ID_FORMAT, SHA256_HEX_REGEX, SINGLE_LINE_COMMENT_SCHEMA, CryptMode, UPID, PROXMOX_SAFE_ID_FORMAT, SHA256_HEX_REGEX, SINGLE_LINE_COMMENT_SCHEMA, CryptMode, UPID,
Fingerprint, Authid, Fingerprint, Userid, Authid,
GC_SCHEDULE_SCHEMA, DATASTORE_NOTIFY_STRING_SCHEMA, PRUNE_SCHEDULE_SCHEMA,
}; };
const_regex!{ const_regex!{
@ -31,6 +33,11 @@ const_regex!{
pub const CHUNK_DIGEST_FORMAT: ApiStringFormat = ApiStringFormat::Pattern(&SHA256_HEX_REGEX); pub const CHUNK_DIGEST_FORMAT: ApiStringFormat = ApiStringFormat::Pattern(&SHA256_HEX_REGEX);
pub const DIR_NAME_SCHEMA: Schema = StringSchema::new("Directory name")
.min_length(1)
.max_length(4096)
.schema();
pub const BACKUP_ARCHIVE_NAME_SCHEMA: Schema = StringSchema::new("Backup archive name.") pub const BACKUP_ARCHIVE_NAME_SCHEMA: Schema = StringSchema::new("Backup archive name.")
.format(&PROXMOX_SAFE_ID_FORMAT) .format(&PROXMOX_SAFE_ID_FORMAT)
.schema(); .schema();
@ -84,6 +91,130 @@ pub const DATASTORE_MAP_LIST_SCHEMA: Schema = StringSchema::new(
.format(&ApiStringFormat::PropertyString(&DATASTORE_MAP_ARRAY_SCHEMA)) .format(&ApiStringFormat::PropertyString(&DATASTORE_MAP_ARRAY_SCHEMA))
.schema(); .schema();
pub const PRUNE_SCHEMA_KEEP_DAILY: Schema = IntegerSchema::new("Number of daily backups to keep.")
.minimum(1)
.schema();
pub const PRUNE_SCHEMA_KEEP_HOURLY: Schema =
IntegerSchema::new("Number of hourly backups to keep.")
.minimum(1)
.schema();
pub const PRUNE_SCHEMA_KEEP_LAST: Schema = IntegerSchema::new("Number of backups to keep.")
.minimum(1)
.schema();
pub const PRUNE_SCHEMA_KEEP_MONTHLY: Schema =
IntegerSchema::new("Number of monthly backups to keep.")
.minimum(1)
.schema();
pub const PRUNE_SCHEMA_KEEP_WEEKLY: Schema =
IntegerSchema::new("Number of weekly backups to keep.")
.minimum(1)
.schema();
pub const PRUNE_SCHEMA_KEEP_YEARLY: Schema =
IntegerSchema::new("Number of yearly backups to keep.")
.minimum(1)
.schema();
#[api(
properties: {
name: {
schema: DATASTORE_SCHEMA,
},
path: {
schema: DIR_NAME_SCHEMA,
},
"notify-user": {
optional: true,
type: Userid,
},
"notify": {
optional: true,
schema: DATASTORE_NOTIFY_STRING_SCHEMA,
},
comment: {
optional: true,
schema: SINGLE_LINE_COMMENT_SCHEMA,
},
"gc-schedule": {
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,
},
"verify-new": {
description: "If enabled, all new backups will be verified right after completion.",
optional: true,
type: bool,
},
}
)]
#[derive(Serialize,Deserialize,Updater)]
#[serde(rename_all="kebab-case")]
/// Datastore configuration properties.
pub struct DataStoreConfig {
#[updater(skip)]
pub name: String,
#[updater(skip)]
pub path: String,
#[serde(skip_serializing_if="Option::is_none")]
pub comment: Option<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<u64>,
#[serde(skip_serializing_if="Option::is_none")]
pub keep_hourly: Option<u64>,
#[serde(skip_serializing_if="Option::is_none")]
pub keep_daily: Option<u64>,
#[serde(skip_serializing_if="Option::is_none")]
pub keep_weekly: Option<u64>,
#[serde(skip_serializing_if="Option::is_none")]
pub keep_monthly: Option<u64>,
#[serde(skip_serializing_if="Option::is_none")]
pub keep_yearly: Option<u64>,
/// If enabled, all backups will be verified right after completion.
#[serde(skip_serializing_if="Option::is_none")]
pub verify_new: Option<bool>,
/// Send job email notification to this user
#[serde(skip_serializing_if="Option::is_none")]
pub notify_user: Option<Userid>,
/// Send notification only for job errors
#[serde(skip_serializing_if="Option::is_none")]
pub notify: Option<String>,
}
#[api( #[api(
properties: { properties: {
store: { store: {

View File

@ -5,8 +5,7 @@ use anyhow::bail;
use proxmox::api::api; use proxmox::api::api;
use proxmox::api::schema::{ use proxmox::api::schema::{
ApiStringFormat, ApiType, ArraySchema, IntegerSchema, ReturnType, Schema, ApiStringFormat, ApiType, ArraySchema, ReturnType, Schema, StringSchema,
StringSchema,
}; };
use proxmox::const_regex; use proxmox::const_regex;
use proxmox::{IPRE, IPRE_BRACKET, IPV4OCTET, IPV4RE, IPV6H16, IPV6LS32, IPV6RE}; use proxmox::{IPRE, IPRE_BRACKET, IPV4OCTET, IPV4RE, IPV6H16, IPV6LS32, IPV6RE};
@ -246,34 +245,6 @@ pub const CERT_FINGERPRINT_SHA256_SCHEMA: Schema =
.format(&FINGERPRINT_SHA256_FORMAT) .format(&FINGERPRINT_SHA256_FORMAT)
.schema(); .schema();
pub const PRUNE_SCHEMA_KEEP_DAILY: Schema = IntegerSchema::new("Number of daily backups to keep.")
.minimum(1)
.schema();
pub const PRUNE_SCHEMA_KEEP_HOURLY: Schema =
IntegerSchema::new("Number of hourly backups to keep.")
.minimum(1)
.schema();
pub const PRUNE_SCHEMA_KEEP_LAST: Schema = IntegerSchema::new("Number of backups to keep.")
.minimum(1)
.schema();
pub const PRUNE_SCHEMA_KEEP_MONTHLY: Schema =
IntegerSchema::new("Number of monthly backups to keep.")
.minimum(1)
.schema();
pub const PRUNE_SCHEMA_KEEP_WEEKLY: Schema =
IntegerSchema::new("Number of weekly backups to keep.")
.minimum(1)
.schema();
pub const PRUNE_SCHEMA_KEEP_YEARLY: Schema =
IntegerSchema::new("Number of yearly backups to keep.")
.minimum(1)
.schema();
pub const PROXMOX_SAFE_ID_FORMAT: ApiStringFormat = pub const PROXMOX_SAFE_ID_FORMAT: ApiStringFormat =
ApiStringFormat::Pattern(&PROXMOX_SAFE_ID_REGEX); ApiStringFormat::Pattern(&PROXMOX_SAFE_ID_REGEX);

100
pbs-config/src/datastore.rs Normal file
View File

@ -0,0 +1,100 @@
use anyhow::{Error};
use lazy_static::lazy_static;
use std::collections::HashMap;
use proxmox::api::{
schema::{ApiType, Schema},
section_config::{
SectionConfig,
SectionConfigData,
SectionConfigPlugin,
}
};
use pbs_api_types::{DataStoreConfig, DATASTORE_SCHEMA};
use crate::{open_backup_lockfile, replace_backup_config, BackupLockGuard};
lazy_static! {
pub static ref CONFIG: SectionConfig = init();
}
fn init() -> SectionConfig {
let obj_schema = match DataStoreConfig::API_SCHEMA {
Schema::Object(ref obj_schema) => obj_schema,
_ => unreachable!(),
};
let plugin = SectionConfigPlugin::new("datastore".to_string(), Some(String::from("name")), obj_schema);
let mut config = SectionConfig::new(&DATASTORE_SCHEMA);
config.register_plugin(plugin);
config
}
pub const DATASTORE_CFG_FILENAME: &str = "/etc/proxmox-backup/datastore.cfg";
pub const DATASTORE_CFG_LOCKFILE: &str = "/etc/proxmox-backup/.datastore.lck";
/// Get exclusive lock
pub fn lock_config() -> Result<BackupLockGuard, Error> {
open_backup_lockfile(DATASTORE_CFG_LOCKFILE, None, true)
}
pub fn config() -> Result<(SectionConfigData, [u8;32]), Error> {
let content = proxmox::tools::fs::file_read_optional_string(DATASTORE_CFG_FILENAME)?
.unwrap_or_else(|| "".to_string());
let digest = openssl::sha::sha256(content.as_bytes());
let data = CONFIG.parse(DATASTORE_CFG_FILENAME, &content)?;
Ok((data, digest))
}
pub fn save_config(config: &SectionConfigData) -> Result<(), Error> {
let raw = CONFIG.write(DATASTORE_CFG_FILENAME, &config)?;
replace_backup_config(DATASTORE_CFG_FILENAME, raw.as_bytes())
}
// shell completion helper
pub fn complete_datastore_name(_arg: &str, _param: &HashMap<String, String>) -> Vec<String> {
match config() {
Ok((data, _digest)) => data.sections.iter().map(|(id, _)| id.to_string()).collect(),
Err(_) => return vec![],
}
}
pub fn complete_acl_path(_arg: &str, _param: &HashMap<String, String>) -> Vec<String> {
let mut list = Vec::new();
list.push(String::from("/"));
list.push(String::from("/datastore"));
list.push(String::from("/datastore/"));
if let Ok((data, _digest)) = config() {
for id in data.sections.keys() {
list.push(format!("/datastore/{}", id));
}
}
list.push(String::from("/remote"));
list.push(String::from("/remote/"));
list.push(String::from("/tape"));
list.push(String::from("/tape/"));
list.push(String::from("/tape/drive"));
list.push(String::from("/tape/drive/"));
list.push(String::from("/tape/changer"));
list.push(String::from("/tape/changer/"));
list.push(String::from("/tape/pool"));
list.push(String::from("/tape/pool/"));
list.push(String::from("/tape/job"));
list.push(String::from("/tape/job/"));
list
}
pub fn complete_calendar_event(_arg: &str, _param: &HashMap<String, String>) -> Vec<String> {
// just give some hints about possible values
["minutely", "hourly", "daily", "mon..fri", "0:0"]
.iter().map(|s| String::from(*s)).collect()
}

View File

@ -1,6 +1,7 @@
pub mod acl; pub mod acl;
mod cached_user_info; mod cached_user_info;
pub use cached_user_info::CachedUserInfo; pub use cached_user_info::CachedUserInfo;
pub mod datastore;
pub mod domains; pub mod domains;
pub mod drive; pub mod drive;
pub mod key_config; pub mod key_config;

View File

@ -50,6 +50,7 @@ use pbs_datastore::prune::{compute_prune_info, PruneOptions};
use pbs_tools::blocking::WrappedReaderStream; use pbs_tools::blocking::WrappedReaderStream;
use pbs_tools::stream::{AsyncReaderStream, AsyncChannelWriter}; use pbs_tools::stream::{AsyncReaderStream, AsyncChannelWriter};
use pbs_tools::json::{required_integer_param, required_string_param}; use pbs_tools::json::{required_integer_param, required_string_param};
use pbs_config::CachedUserInfo;
use crate::api2::types::{DataStoreStatus, RRDMode, RRDTimeFrameResolution}; use crate::api2::types::{DataStoreStatus, RRDMode, RRDTimeFrameResolution};
use crate::api2::node::rrd::create_value_from_rrd; use crate::api2::node::rrd::create_value_from_rrd;
@ -57,8 +58,6 @@ use crate::backup::{
check_backup_owner, verify_all_backups, verify_backup_group, verify_backup_dir, verify_filter, check_backup_owner, verify_all_backups, verify_backup_group, verify_backup_dir, verify_filter,
DataStore, LocalChunkReader, DataStore, LocalChunkReader,
}; };
use crate::config::datastore;
use pbs_config::CachedUserInfo;
use crate::server::{jobstate::Job, WorkerTask}; use crate::server::{jobstate::Job, WorkerTask};
@ -1050,7 +1049,7 @@ pub fn get_datastore_list(
rpcenv: &mut dyn RpcEnvironment, rpcenv: &mut dyn RpcEnvironment,
) -> Result<Vec<DataStoreListItem>, Error> { ) -> Result<Vec<DataStoreListItem>, Error> {
let (config, _digest) = datastore::config()?; let (config, _digest) = pbs_config::datastore::config()?;
let auth_id: Authid = rpcenv.get_auth_id().unwrap().parse()?; let auth_id: Authid = rpcenv.get_auth_id().unwrap().parse()?;
let user_info = CachedUserInfo::new()?; let user_info = CachedUserInfo::new()?;

View File

@ -15,6 +15,7 @@ use pbs_api_types::{
Authid, DatastoreNotify, Authid, DatastoreNotify,
DATASTORE_SCHEMA, PROXMOX_CONFIG_DIGEST_SCHEMA, DATASTORE_SCHEMA, PROXMOX_CONFIG_DIGEST_SCHEMA,
PRIV_DATASTORE_ALLOCATE, PRIV_DATASTORE_AUDIT, PRIV_DATASTORE_MODIFY, PRIV_DATASTORE_ALLOCATE, PRIV_DATASTORE_AUDIT, PRIV_DATASTORE_MODIFY,
DataStoreConfig, DataStoreConfigUpdater,
}; };
use crate::api2::config::sync::delete_sync_job; use crate::api2::config::sync::delete_sync_job;
@ -25,7 +26,7 @@ use crate::api2::admin::{
verify::list_verification_jobs, verify::list_verification_jobs,
}; };
use pbs_config::CachedUserInfo; use pbs_config::CachedUserInfo;
use crate::config::datastore::{self, DataStoreConfig, DataStoreConfigUpdater};
use crate::server::{jobstate, WorkerTask}; use crate::server::{jobstate, WorkerTask};
#[api( #[api(
@ -47,7 +48,7 @@ pub fn list_datastores(
mut rpcenv: &mut dyn RpcEnvironment, mut rpcenv: &mut dyn RpcEnvironment,
) -> Result<Vec<DataStoreConfig>, Error> { ) -> Result<Vec<DataStoreConfig>, Error> {
let (config, digest) = datastore::config()?; let (config, digest) = pbs_config::datastore::config()?;
let auth_id: Authid = rpcenv.get_auth_id().unwrap().parse()?; let auth_id: Authid = rpcenv.get_auth_id().unwrap().parse()?;
let user_info = CachedUserInfo::new()?; let user_info = CachedUserInfo::new()?;
@ -76,7 +77,7 @@ pub(crate) fn do_create_datastore(
config.set_data(&datastore.name, "datastore", &datastore)?; config.set_data(&datastore.name, "datastore", &datastore)?;
datastore::save_config(&config)?; pbs_config::datastore::save_config(&config)?;
jobstate::create_state_file("prune", &datastore.name)?; jobstate::create_state_file("prune", &datastore.name)?;
jobstate::create_state_file("garbage_collection", &datastore.name)?; jobstate::create_state_file("garbage_collection", &datastore.name)?;
@ -104,9 +105,9 @@ pub fn create_datastore(
rpcenv: &mut dyn RpcEnvironment, rpcenv: &mut dyn RpcEnvironment,
) -> Result<String, Error> { ) -> Result<String, Error> {
let lock = datastore::lock_config()?; let lock = pbs_config::datastore::lock_config()?;
let (section_config, _digest) = datastore::config()?; let (section_config, _digest) = pbs_config::datastore::config()?;
if section_config.sections.get(&config.name).is_some() { if section_config.sections.get(&config.name).is_some() {
bail!("datastore '{}' already exists.", config.name); bail!("datastore '{}' already exists.", config.name);
@ -141,7 +142,7 @@ pub fn read_datastore(
name: String, name: String,
mut rpcenv: &mut dyn RpcEnvironment, mut rpcenv: &mut dyn RpcEnvironment,
) -> Result<DataStoreConfig, Error> { ) -> Result<DataStoreConfig, Error> {
let (config, digest) = datastore::config()?; let (config, digest) = pbs_config::datastore::config()?;
let store_config = config.lookup("datastore", &name)?; let store_config = config.lookup("datastore", &name)?;
rpcenv["digest"] = proxmox::tools::digest_to_hex(&digest).into(); rpcenv["digest"] = proxmox::tools::digest_to_hex(&digest).into();
@ -218,10 +219,10 @@ pub fn update_datastore(
digest: Option<String>, digest: Option<String>,
) -> Result<(), Error> { ) -> Result<(), Error> {
let _lock = datastore::lock_config()?; let _lock = pbs_config::datastore::lock_config()?;
// pass/compare digest // pass/compare digest
let (mut config, expected_digest) = datastore::config()?; let (mut config, expected_digest) = pbs_config::datastore::config()?;
if let Some(ref digest) = digest { if let Some(ref digest) = digest {
let digest = proxmox::tools::hex_to_digest(digest)?; let digest = proxmox::tools::hex_to_digest(digest)?;
@ -292,7 +293,7 @@ pub fn update_datastore(
config.set_data(&name, "datastore", &data)?; config.set_data(&name, "datastore", &data)?;
datastore::save_config(&config)?; pbs_config::datastore::save_config(&config)?;
// we want to reset the statefiles, to avoid an immediate action in some cases // we want to reset the statefiles, to avoid an immediate action in some cases
// (e.g. going from monthly to weekly in the second week of the month) // (e.g. going from monthly to weekly in the second week of the month)
@ -338,9 +339,9 @@ pub async fn delete_datastore(
rpcenv: &mut dyn RpcEnvironment, rpcenv: &mut dyn RpcEnvironment,
) -> Result<(), Error> { ) -> Result<(), Error> {
let _lock = datastore::lock_config()?; let _lock = pbs_config::datastore::lock_config()?;
let (mut config, expected_digest) = datastore::config()?; let (mut config, expected_digest) = pbs_config::datastore::config()?;
if let Some(ref digest) = digest { if let Some(ref digest) = digest {
let digest = proxmox::tools::hex_to_digest(digest)?; let digest = proxmox::tools::hex_to_digest(digest)?;
@ -366,7 +367,7 @@ pub async fn delete_datastore(
} }
} }
datastore::save_config(&config)?; pbs_config::datastore::save_config(&config)?;
// ignore errors // ignore errors
let _ = jobstate::remove_state_file("prune", &name); let _ = jobstate::remove_state_file("prune", &name);

View File

@ -7,8 +7,8 @@ use proxmox::api::section_config::SectionConfigData;
use proxmox::api::router::Router; use proxmox::api::router::Router;
use pbs_api_types::{ use pbs_api_types::{
Authid, NODE_SCHEMA, BLOCKDEVICE_NAME_SCHEMA, DATASTORE_SCHEMA, UPID_SCHEMA, Authid, DataStoreConfig, NODE_SCHEMA, BLOCKDEVICE_NAME_SCHEMA,
PRIV_SYS_AUDIT, PRIV_SYS_MODIFY, DATASTORE_SCHEMA, UPID_SCHEMA, PRIV_SYS_AUDIT, PRIV_SYS_MODIFY,
}; };
use crate::tools::disks::{ use crate::tools::disks::{
@ -19,9 +19,6 @@ use crate::tools::systemd::{self, types::*};
use crate::server::WorkerTask; use crate::server::WorkerTask;
use crate::config::datastore::{self, DataStoreConfig};
use pbs_config::open_backup_lockfile;
#[api( #[api(
properties: { properties: {
"filesystem": { "filesystem": {
@ -183,11 +180,11 @@ pub fn create_datastore_disk(
pbs_systemd::start_unit(&mount_unit_name)?; pbs_systemd::start_unit(&mount_unit_name)?;
if add_datastore { if add_datastore {
let lock = open_backup_lockfile(datastore::DATASTORE_CFG_LOCKFILE, None, true)?; let lock = pbs_config::datastore::lock_config()?;
let datastore: DataStoreConfig = let datastore: DataStoreConfig =
serde_json::from_value(json!({ "name": name, "path": mount_point }))?; serde_json::from_value(json!({ "name": name, "path": mount_point }))?;
let (config, _digest) = datastore::config()?; let (config, _digest) = pbs_config::datastore::config()?;
if config.sections.get(&datastore.name).is_some() { if config.sections.get(&datastore.name).is_some() {
bail!("datastore '{}' already exists.", datastore.name); bail!("datastore '{}' already exists.", datastore.name);
@ -223,7 +220,7 @@ pub fn delete_datastore_disk(name: String) -> Result<(), Error> {
let path = format!("/mnt/datastore/{}", name); let path = format!("/mnt/datastore/{}", name);
// path of datastore cannot be changed // path of datastore cannot be changed
let (config, _) = crate::config::datastore::config()?; let (config, _) = pbs_config::datastore::config()?;
let datastores: Vec<DataStoreConfig> = config.convert_to_typed_array("datastore")?; let datastores: Vec<DataStoreConfig> = config.convert_to_typed_array("datastore")?;
let conflicting_datastore: Option<DataStoreConfig> = datastores.into_iter() let conflicting_datastore: Option<DataStoreConfig> = datastores.into_iter()
.find(|ds| ds.path == path); .find(|ds| ds.path == path);

View File

@ -8,7 +8,7 @@ use proxmox::api::{
use proxmox::api::router::Router; use proxmox::api::router::Router;
use pbs_api_types::{ use pbs_api_types::{
Authid, ZpoolListItem, ZfsRaidLevel, ZfsCompressionType, Authid, ZpoolListItem, ZfsRaidLevel, ZfsCompressionType, DataStoreConfig,
NODE_SCHEMA, ZPOOL_NAME_SCHEMA, DATASTORE_SCHEMA, DISK_ARRAY_SCHEMA, NODE_SCHEMA, ZPOOL_NAME_SCHEMA, DATASTORE_SCHEMA, DISK_ARRAY_SCHEMA,
DISK_LIST_SCHEMA, ZFS_ASHIFT_SCHEMA, UPID_SCHEMA, DISK_LIST_SCHEMA, ZFS_ASHIFT_SCHEMA, UPID_SCHEMA,
PRIV_SYS_AUDIT, PRIV_SYS_MODIFY, PRIV_SYS_AUDIT, PRIV_SYS_MODIFY,
@ -18,7 +18,6 @@ use crate::tools::disks::{
zpool_list, zpool_status, parse_zpool_status_config_tree, vdev_list_to_tree, zpool_list, zpool_status, parse_zpool_status_config_tree, vdev_list_to_tree,
DiskUsageType, DiskUsageType,
}; };
use crate::config::datastore::{self, DataStoreConfig};
use crate::server::WorkerTask; use crate::server::WorkerTask;
@ -285,11 +284,11 @@ pub fn create_zpool(
} }
if add_datastore { if add_datastore {
let lock = datastore::lock_config()?; let lock = pbs_config::datastore::lock_config()?;
let datastore: DataStoreConfig = let datastore: DataStoreConfig =
serde_json::from_value(json!({ "name": name, "path": mount_point }))?; serde_json::from_value(json!({ "name": name, "path": mount_point }))?;
let (config, _digest) = datastore::config()?; let (config, _digest) = pbs_config::datastore::config()?;
if config.sections.get(&datastore.name).is_some() { if config.sections.get(&datastore.name).is_some() {
bail!("datastore '{}' already exists.", datastore.name); bail!("datastore '{}' already exists.", datastore.name);

View File

@ -20,7 +20,6 @@ use pbs_api_types::{
}; };
use crate::backup::DataStore; use crate::backup::DataStore;
use crate::config::datastore;
use crate::tools::statistics::{linear_regression}; use crate::tools::statistics::{linear_regression};
use pbs_config::CachedUserInfo; use pbs_config::CachedUserInfo;
@ -83,7 +82,7 @@ pub fn datastore_status(
rpcenv: &mut dyn RpcEnvironment, rpcenv: &mut dyn RpcEnvironment,
) -> Result<Value, Error> { ) -> Result<Value, Error> {
let (config, _digest) = datastore::config()?; let (config, _digest) = pbs_config::datastore::config()?;
let auth_id: Authid = rpcenv.get_auth_id().unwrap().parse()?; let auth_id: Authid = rpcenv.get_auth_id().unwrap().parse()?;
let user_info = CachedUserInfo::new()?; let user_info = CachedUserInfo::new()?;

View File

@ -11,8 +11,7 @@ use lazy_static::lazy_static;
use proxmox::tools::fs::{replace_file, file_read_optional_string, CreateOptions}; use proxmox::tools::fs::{replace_file, file_read_optional_string, CreateOptions};
use pbs_api_types::upid::UPID; use pbs_api_types::{UPID, DataStoreConfig, Authid, GarbageCollectionStatus};
use pbs_api_types::{Authid, GarbageCollectionStatus};
use pbs_datastore::{task_log, task_warn}; use pbs_datastore::{task_log, task_warn};
use pbs_datastore::DataBlob; use pbs_datastore::DataBlob;
use pbs_datastore::backup_info::{BackupGroup, BackupDir}; use pbs_datastore::backup_info::{BackupGroup, BackupDir};
@ -29,7 +28,6 @@ use pbs_datastore::task::TaskState;
use pbs_tools::format::HumanByte; use pbs_tools::format::HumanByte;
use pbs_tools::fs::{lock_dir_noblock, DirLockGuard}; use pbs_tools::fs::{lock_dir_noblock, DirLockGuard};
use crate::config::datastore::{self, DataStoreConfig};
use crate::tools; use crate::tools;
use pbs_config::{open_backup_lockfile, BackupLockGuard}; use pbs_config::{open_backup_lockfile, BackupLockGuard};
@ -67,8 +65,8 @@ impl DataStore {
pub fn lookup_datastore(name: &str) -> Result<Arc<DataStore>, Error> { pub fn lookup_datastore(name: &str) -> Result<Arc<DataStore>, Error> {
let (config, _digest) = datastore::config()?; let (config, _digest) = pbs_config::datastore::config()?;
let config: datastore::DataStoreConfig = config.lookup("datastore", name)?; let config: DataStoreConfig = config.lookup("datastore", name)?;
let path = PathBuf::from(&config.path); let path = PathBuf::from(&config.path);
let mut map = DATASTORE_MAP.lock().unwrap(); let mut map = DATASTORE_MAP.lock().unwrap();
@ -92,7 +90,7 @@ impl DataStore {
/// removes all datastores that are not configured anymore /// removes all datastores that are not configured anymore
pub fn remove_unused_datastores() -> Result<(), Error>{ pub fn remove_unused_datastores() -> Result<(), Error>{
let (config, _digest) = datastore::config()?; let (config, _digest) = pbs_config::datastore::config()?;
let mut map = DATASTORE_MAP.lock().unwrap(); let mut map = DATASTORE_MAP.lock().unwrap();
// removes all elements that are not in the config // removes all elements that are not in the config

View File

@ -27,7 +27,7 @@ use proxmox::{
use pbs_api_types::PRIVILEGES; use pbs_api_types::PRIVILEGES;
use proxmox_backup::{api2, config}; use proxmox_backup::api2;
fn get_args() -> (String, Vec<String>) { fn get_args() -> (String, Vec<String>) {
@ -50,7 +50,7 @@ fn main() -> Result<(), Error> {
for arg in args.iter() { for arg in args.iter() {
let text = match arg.as_ref() { let text = match arg.as_ref() {
"apidata.js" => generate_api_tree(), "apidata.js" => generate_api_tree(),
"datastore.cfg" => dump_section_config(&config::datastore::CONFIG), "datastore.cfg" => dump_section_config(&pbs_config::datastore::CONFIG),
"tape.cfg" => dump_section_config(&pbs_config::drive::CONFIG), "tape.cfg" => dump_section_config(&pbs_config::drive::CONFIG),
"tape-job.cfg" => dump_section_config(&pbs_config::tape_job::CONFIG), "tape-job.cfg" => dump_section_config(&pbs_config::tape_job::CONFIG),
"user.cfg" => dump_section_config(&pbs_config::user::CONFIG), "user.cfg" => dump_section_config(&pbs_config::user::CONFIG),

View File

@ -89,12 +89,12 @@ fn garbage_collection_commands() -> CommandLineInterface {
.insert("status", .insert("status",
CliCommand::new(&API_METHOD_GARBAGE_COLLECTION_STATUS) CliCommand::new(&API_METHOD_GARBAGE_COLLECTION_STATUS)
.arg_param(&["store"]) .arg_param(&["store"])
.completion_cb("store", config::datastore::complete_datastore_name) .completion_cb("store", pbs_config::datastore::complete_datastore_name)
) )
.insert("start", .insert("start",
CliCommand::new(&API_METHOD_START_GARBAGE_COLLECTION) CliCommand::new(&API_METHOD_START_GARBAGE_COLLECTION)
.arg_param(&["store"]) .arg_param(&["store"])
.completion_cb("store", config::datastore::complete_datastore_name) .completion_cb("store", pbs_config::datastore::complete_datastore_name)
); );
cmd_def.into() cmd_def.into()
@ -379,7 +379,7 @@ fn main() {
"pull", "pull",
CliCommand::new(&API_METHOD_PULL_DATASTORE) CliCommand::new(&API_METHOD_PULL_DATASTORE)
.arg_param(&["remote", "remote-store", "local-store"]) .arg_param(&["remote", "remote-store", "local-store"])
.completion_cb("local-store", config::datastore::complete_datastore_name) .completion_cb("local-store", pbs_config::datastore::complete_datastore_name)
.completion_cb("remote", pbs_config::remote::complete_remote_name) .completion_cb("remote", pbs_config::remote::complete_remote_name)
.completion_cb("remote-store", complete_remote_datastore_name) .completion_cb("remote-store", complete_remote_datastore_name)
) )
@ -387,7 +387,7 @@ fn main() {
"verify", "verify",
CliCommand::new(&API_METHOD_VERIFY) CliCommand::new(&API_METHOD_VERIFY)
.arg_param(&["store"]) .arg_param(&["store"])
.completion_cb("store", config::datastore::complete_datastore_name) .completion_cb("store", pbs_config::datastore::complete_datastore_name)
) )
.insert("report", .insert("report",
CliCommand::new(&API_METHOD_REPORT) CliCommand::new(&API_METHOD_REPORT)

View File

@ -32,7 +32,9 @@ use pbs_buildcfg::configdir;
use pbs_systemd::time::{compute_next_event, parse_calendar_event}; use pbs_systemd::time::{compute_next_event, parse_calendar_event};
use pbs_tools::logrotate::LogRotate; use pbs_tools::logrotate::LogRotate;
use pbs_api_types::{Authid, TapeBackupJobConfig, VerificationJobConfig, SyncJobConfig}; use pbs_api_types::{Authid, TapeBackupJobConfig, VerificationJobConfig, SyncJobConfig, DataStoreConfig};
use pbs_datastore::prune::PruneOptions;
use proxmox_backup::server; use proxmox_backup::server;
use proxmox_backup::auth_helpers::*; use proxmox_backup::auth_helpers::*;
use proxmox_backup::tools::{ use proxmox_backup::tools::{
@ -45,6 +47,7 @@ use proxmox_backup::tools::{
}, },
}; };
use proxmox_backup::api2::pull::do_sync_job; use proxmox_backup::api2::pull::do_sync_job;
use proxmox_backup::api2::tape::backup::do_tape_backup_job; use proxmox_backup::api2::tape::backup::do_tape_backup_job;
use proxmox_backup::server::do_verification_job; use proxmox_backup::server::do_verification_job;
@ -374,14 +377,7 @@ async fn schedule_tasks() -> Result<(), Error> {
async fn schedule_datastore_garbage_collection() { async fn schedule_datastore_garbage_collection() {
use proxmox_backup::config::{ let config = match pbs_config::datastore::config() {
datastore::{
self,
DataStoreConfig,
},
};
let config = match datastore::config() {
Err(err) => { Err(err) => {
eprintln!("unable to read datastore config - {}", err); eprintln!("unable to read datastore config - {}", err);
return; return;
@ -459,15 +455,7 @@ async fn schedule_datastore_garbage_collection() {
async fn schedule_datastore_prune() { async fn schedule_datastore_prune() {
use pbs_datastore::prune::PruneOptions; let config = match pbs_config::datastore::config() {
use proxmox_backup::{
config::datastore::{
self,
DataStoreConfig,
},
};
let config = match datastore::config() {
Err(err) => { Err(err) => {
eprintln!("unable to read datastore config - {}", err); eprintln!("unable to read datastore config - {}", err);
return; return;
@ -765,8 +753,6 @@ fn rrd_update_derive(name: &str, value: f64, save: bool) {
async fn generate_host_stats(save: bool) { async fn generate_host_stats(save: bool) {
use proxmox::sys::linux::procfs::{ use proxmox::sys::linux::procfs::{
read_meminfo, read_proc_stat, read_proc_net_dev, read_loadavg}; read_meminfo, read_proc_stat, read_proc_net_dev, read_loadavg};
use proxmox_backup::config::datastore;
pbs_runtime::block_in_place(move || { pbs_runtime::block_in_place(move || {
@ -823,9 +809,9 @@ async fn generate_host_stats(save: bool) {
gather_disk_stats(disk_manager.clone(), Path::new("/"), "host", save); gather_disk_stats(disk_manager.clone(), Path::new("/"), "host", save);
match datastore::config() { match pbs_config::datastore::config() {
Ok((config, _)) => { Ok((config, _)) => {
let datastore_list: Vec<datastore::DataStoreConfig> = let datastore_list: Vec<DataStoreConfig> =
config.convert_to_typed_array("datastore").unwrap_or_default(); config.convert_to_typed_array("datastore").unwrap_or_default();
for config in datastore_list { for config in datastore_list {

View File

@ -23,6 +23,7 @@ use pbs_tools::format::{
use pbs_config::drive::complete_drive_name; use pbs_config::drive::complete_drive_name;
use pbs_config::media_pool::complete_pool_name; use pbs_config::media_pool::complete_pool_name;
use pbs_config::datastore::complete_datastore_name;
use proxmox_backup::{ use proxmox_backup::{
api2::{ api2::{
@ -38,9 +39,6 @@ use proxmox_backup::{
TAPE_RESTORE_SNAPSHOT_SCHEMA, TAPE_RESTORE_SNAPSHOT_SCHEMA,
}, },
}, },
config::{
datastore::complete_datastore_name,
},
tape::{ tape::{
BlockReadError, BlockReadError,
drive::{ drive::{

View File

@ -3,7 +3,6 @@ use serde_json::Value;
use proxmox::api::{api, cli::*, RpcEnvironment, ApiHandler}; use proxmox::api::{api, cli::*, RpcEnvironment, ApiHandler};
use proxmox_backup::config;
use proxmox_backup::api2; use proxmox_backup::api2;
#[api( #[api(
@ -61,7 +60,7 @@ pub fn acl_commands() -> CommandLineInterface {
CliCommand::new(&api2::access::acl::API_METHOD_UPDATE_ACL) CliCommand::new(&api2::access::acl::API_METHOD_UPDATE_ACL)
.arg_param(&["path", "role"]) .arg_param(&["path", "role"])
.completion_cb("auth-id", pbs_config::user::complete_authid) .completion_cb("auth-id", pbs_config::user::complete_authid)
.completion_cb("path", config::datastore::complete_acl_path) .completion_cb("path", pbs_config::datastore::complete_acl_path)
); );

View File

@ -4,10 +4,9 @@ use serde_json::Value;
use proxmox::api::{api, cli::*, RpcEnvironment, ApiHandler}; use proxmox::api::{api, cli::*, RpcEnvironment, ApiHandler};
use pbs_client::{connect_to_localhost, view_task_result}; use pbs_client::{connect_to_localhost, view_task_result};
use pbs_api_types::{DataStoreConfig, DATASTORE_SCHEMA};
use proxmox_backup::config; use proxmox_backup::api2;
use proxmox_backup::api2::{self, types::* };
use proxmox_backup::config::datastore::DataStoreConfig;
#[api( #[api(
input: { input: {
@ -106,7 +105,7 @@ pub fn datastore_commands() -> CommandLineInterface {
.insert("show", .insert("show",
CliCommand::new(&API_METHOD_SHOW_DATASTORE) CliCommand::new(&API_METHOD_SHOW_DATASTORE)
.arg_param(&["name"]) .arg_param(&["name"])
.completion_cb("name", config::datastore::complete_datastore_name) .completion_cb("name", pbs_config::datastore::complete_datastore_name)
) )
.insert("create", .insert("create",
CliCommand::new(&API_METHOD_CREATE_DATASTORE) CliCommand::new(&API_METHOD_CREATE_DATASTORE)
@ -115,14 +114,14 @@ pub fn datastore_commands() -> CommandLineInterface {
.insert("update", .insert("update",
CliCommand::new(&api2::config::datastore::API_METHOD_UPDATE_DATASTORE) CliCommand::new(&api2::config::datastore::API_METHOD_UPDATE_DATASTORE)
.arg_param(&["name"]) .arg_param(&["name"])
.completion_cb("name", config::datastore::complete_datastore_name) .completion_cb("name", pbs_config::datastore::complete_datastore_name)
.completion_cb("gc-schedule", config::datastore::complete_calendar_event) .completion_cb("gc-schedule", pbs_config::datastore::complete_calendar_event)
.completion_cb("prune-schedule", config::datastore::complete_calendar_event) .completion_cb("prune-schedule", pbs_config::datastore::complete_calendar_event)
) )
.insert("remove", .insert("remove",
CliCommand::new(&api2::config::datastore::API_METHOD_DELETE_DATASTORE) CliCommand::new(&api2::config::datastore::API_METHOD_DELETE_DATASTORE)
.arg_param(&["name"]) .arg_param(&["name"])
.completion_cb("name", config::datastore::complete_datastore_name) .completion_cb("name", pbs_config::datastore::complete_datastore_name)
); );
cmd_def.into() cmd_def.into()

View File

@ -3,8 +3,9 @@ use serde_json::Value;
use proxmox::api::{api, cli::*, RpcEnvironment, ApiHandler}; use proxmox::api::{api, cli::*, RpcEnvironment, ApiHandler};
use proxmox_backup::config; use pbs_api_types::JOB_ID_SCHEMA;
use proxmox_backup::api2::{self, types::* };
use proxmox_backup::api2;
#[api( #[api(
input: { input: {
@ -83,8 +84,8 @@ pub fn sync_job_commands() -> CommandLineInterface {
CliCommand::new(&api2::config::sync::API_METHOD_CREATE_SYNC_JOB) CliCommand::new(&api2::config::sync::API_METHOD_CREATE_SYNC_JOB)
.arg_param(&["id"]) .arg_param(&["id"])
.completion_cb("id", pbs_config::sync::complete_sync_job_id) .completion_cb("id", pbs_config::sync::complete_sync_job_id)
.completion_cb("schedule", config::datastore::complete_calendar_event) .completion_cb("schedule", pbs_config::datastore::complete_calendar_event)
.completion_cb("store", config::datastore::complete_datastore_name) .completion_cb("store", pbs_config::datastore::complete_datastore_name)
.completion_cb("remote", pbs_config::remote::complete_remote_name) .completion_cb("remote", pbs_config::remote::complete_remote_name)
.completion_cb("remote-store", crate::complete_remote_datastore_name) .completion_cb("remote-store", crate::complete_remote_datastore_name)
) )
@ -92,8 +93,8 @@ pub fn sync_job_commands() -> CommandLineInterface {
CliCommand::new(&api2::config::sync::API_METHOD_UPDATE_SYNC_JOB) CliCommand::new(&api2::config::sync::API_METHOD_UPDATE_SYNC_JOB)
.arg_param(&["id"]) .arg_param(&["id"])
.completion_cb("id", pbs_config::sync::complete_sync_job_id) .completion_cb("id", pbs_config::sync::complete_sync_job_id)
.completion_cb("schedule", config::datastore::complete_calendar_event) .completion_cb("schedule", pbs_config::datastore::complete_calendar_event)
.completion_cb("store", config::datastore::complete_datastore_name) .completion_cb("store", pbs_config::datastore::complete_datastore_name)
.completion_cb("remote-store", crate::complete_remote_datastore_name) .completion_cb("remote-store", crate::complete_remote_datastore_name)
) )
.insert("remove", .insert("remove",

View File

@ -5,9 +5,9 @@ use std::collections::HashMap;
use proxmox::api::{api, cli::*, RpcEnvironment, ApiHandler}; use proxmox::api::{api, cli::*, RpcEnvironment, ApiHandler};
use proxmox_backup::config; use pbs_api_types::{ACL_PATH_SCHEMA, Authid, Userid};
use proxmox_backup::api2; use proxmox_backup::api2;
use proxmox_backup::api2::types::{ACL_PATH_SCHEMA, Authid, Userid};
fn render_expire(value: &Value, _record: &Value) -> Result<String, Error> { fn render_expire(value: &Value, _record: &Value) -> Result<String, Error> {
let never = String::from("never"); let never = String::from("never");
@ -213,7 +213,7 @@ pub fn user_commands() -> CommandLineInterface {
CliCommand::new(&&API_METHOD_LIST_PERMISSIONS) CliCommand::new(&&API_METHOD_LIST_PERMISSIONS)
.arg_param(&["auth-id"]) .arg_param(&["auth-id"])
.completion_cb("auth-id", pbs_config::user::complete_authid) .completion_cb("auth-id", pbs_config::user::complete_authid)
.completion_cb("path", config::datastore::complete_acl_path) .completion_cb("path", pbs_config::datastore::complete_acl_path)
); );
cmd_def.into() cmd_def.into()

View File

@ -3,8 +3,9 @@ use serde_json::Value;
use proxmox::api::{api, cli::*, RpcEnvironment, ApiHandler}; use proxmox::api::{api, cli::*, RpcEnvironment, ApiHandler};
use proxmox_backup::config; use pbs_api_types::JOB_ID_SCHEMA;
use proxmox_backup::api2::{self, types::* };
use proxmox_backup::api2;
#[api( #[api(
input: { input: {
@ -83,15 +84,15 @@ pub fn verify_job_commands() -> CommandLineInterface {
CliCommand::new(&api2::config::verify::API_METHOD_CREATE_VERIFICATION_JOB) CliCommand::new(&api2::config::verify::API_METHOD_CREATE_VERIFICATION_JOB)
.arg_param(&["id"]) .arg_param(&["id"])
.completion_cb("id", pbs_config::verify::complete_verification_job_id) .completion_cb("id", pbs_config::verify::complete_verification_job_id)
.completion_cb("schedule", config::datastore::complete_calendar_event) .completion_cb("schedule", pbs_config::datastore::complete_calendar_event)
.completion_cb("store", config::datastore::complete_datastore_name) .completion_cb("store", pbs_config::datastore::complete_datastore_name)
) )
.insert("update", .insert("update",
CliCommand::new(&api2::config::verify::API_METHOD_UPDATE_VERIFICATION_JOB) CliCommand::new(&api2::config::verify::API_METHOD_UPDATE_VERIFICATION_JOB)
.arg_param(&["id"]) .arg_param(&["id"])
.completion_cb("id", pbs_config::verify::complete_verification_job_id) .completion_cb("id", pbs_config::verify::complete_verification_job_id)
.completion_cb("schedule", config::datastore::complete_calendar_event) .completion_cb("schedule", pbs_config::datastore::complete_calendar_event)
.completion_cb("store", config::datastore::complete_datastore_name) .completion_cb("store", pbs_config::datastore::complete_datastore_name)
.completion_cb("remote-store", crate::complete_remote_datastore_name) .completion_cb("remote-store", crate::complete_remote_datastore_name)
) )
.insert("remove", .insert("remove",

View File

@ -6,10 +6,7 @@ use proxmox::api::{api, cli::*, RpcEnvironment, ApiHandler};
use pbs_api_types::JOB_ID_SCHEMA; use pbs_api_types::JOB_ID_SCHEMA;
use pbs_client::{connect_to_localhost, view_task_result}; use pbs_client::{connect_to_localhost, view_task_result};
use proxmox_backup::{ use proxmox_backup::api2;
config,
api2,
};
#[api( #[api(
input: { input: {
@ -121,8 +118,8 @@ pub fn backup_job_commands() -> CommandLineInterface {
CliCommand::new(&api2::config::tape_backup_job::API_METHOD_CREATE_TAPE_BACKUP_JOB) CliCommand::new(&api2::config::tape_backup_job::API_METHOD_CREATE_TAPE_BACKUP_JOB)
.arg_param(&["id"]) .arg_param(&["id"])
.completion_cb("id", pbs_config::tape_job::complete_tape_job_id) .completion_cb("id", pbs_config::tape_job::complete_tape_job_id)
.completion_cb("schedule", config::datastore::complete_calendar_event) .completion_cb("schedule", pbs_config::datastore::complete_calendar_event)
.completion_cb("store", config::datastore::complete_datastore_name) .completion_cb("store", pbs_config::datastore::complete_datastore_name)
.completion_cb("pool", pbs_config::media_pool::complete_pool_name) .completion_cb("pool", pbs_config::media_pool::complete_pool_name)
.completion_cb("drive", crate::complete_drive_name) .completion_cb("drive", crate::complete_drive_name)
) )
@ -130,8 +127,8 @@ pub fn backup_job_commands() -> CommandLineInterface {
CliCommand::new(&api2::config::tape_backup_job::API_METHOD_UPDATE_TAPE_BACKUP_JOB) CliCommand::new(&api2::config::tape_backup_job::API_METHOD_UPDATE_TAPE_BACKUP_JOB)
.arg_param(&["id"]) .arg_param(&["id"])
.completion_cb("id", pbs_config::tape_job::complete_tape_job_id) .completion_cb("id", pbs_config::tape_job::complete_tape_job_id)
.completion_cb("schedule", config::datastore::complete_calendar_event) .completion_cb("schedule", pbs_config::datastore::complete_calendar_event)
.completion_cb("store", config::datastore::complete_datastore_name) .completion_cb("store", pbs_config::datastore::complete_datastore_name)
.completion_cb("pool", pbs_config::media_pool::complete_pool_name) .completion_cb("pool", pbs_config::media_pool::complete_pool_name)
.completion_cb("drive", crate::complete_drive_name) .completion_cb("drive", crate::complete_drive_name)
) )

View File

@ -1,201 +0,0 @@
use anyhow::{Error};
use lazy_static::lazy_static;
use std::collections::HashMap;
use serde::{Serialize, Deserialize};
use proxmox::api::{
api,
schema::{ApiType, Schema, StringSchema, Updater},
section_config::{
SectionConfig,
SectionConfigData,
SectionConfigPlugin,
}
};
use pbs_config::{open_backup_lockfile, BackupLockGuard};
use crate::api2::types::*;
lazy_static! {
pub static ref CONFIG: SectionConfig = init();
}
// fixme: define better schemas
pub const DIR_NAME_SCHEMA: Schema = StringSchema::new("Directory name").schema();
#[api(
properties: {
name: {
schema: DATASTORE_SCHEMA,
},
path: {
schema: DIR_NAME_SCHEMA,
},
"notify-user": {
optional: true,
type: Userid,
},
"notify": {
optional: true,
schema: DATASTORE_NOTIFY_STRING_SCHEMA,
},
comment: {
optional: true,
schema: SINGLE_LINE_COMMENT_SCHEMA,
},
"gc-schedule": {
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,
},
"verify-new": {
description: "If enabled, all new backups will be verified right after completion.",
optional: true,
type: bool,
},
}
)]
#[derive(Serialize,Deserialize,Updater)]
#[serde(rename_all="kebab-case")]
/// Datastore configuration properties.
pub struct DataStoreConfig {
#[updater(skip)]
pub name: String,
#[updater(skip)]
pub path: String,
#[serde(skip_serializing_if="Option::is_none")]
pub comment: Option<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<u64>,
#[serde(skip_serializing_if="Option::is_none")]
pub keep_hourly: Option<u64>,
#[serde(skip_serializing_if="Option::is_none")]
pub keep_daily: Option<u64>,
#[serde(skip_serializing_if="Option::is_none")]
pub keep_weekly: Option<u64>,
#[serde(skip_serializing_if="Option::is_none")]
pub keep_monthly: Option<u64>,
#[serde(skip_serializing_if="Option::is_none")]
pub keep_yearly: Option<u64>,
/// If enabled, all backups will be verified right after completion.
#[serde(skip_serializing_if="Option::is_none")]
pub verify_new: Option<bool>,
/// Send job email notification to this user
#[serde(skip_serializing_if="Option::is_none")]
pub notify_user: Option<Userid>,
/// Send notification only for job errors
#[serde(skip_serializing_if="Option::is_none")]
pub notify: Option<String>,
}
fn init() -> SectionConfig {
let obj_schema = match DataStoreConfig::API_SCHEMA {
Schema::Object(ref obj_schema) => obj_schema,
_ => unreachable!(),
};
let plugin = SectionConfigPlugin::new("datastore".to_string(), Some(String::from("name")), obj_schema);
let mut config = SectionConfig::new(&DATASTORE_SCHEMA);
config.register_plugin(plugin);
config
}
pub const DATASTORE_CFG_FILENAME: &str = "/etc/proxmox-backup/datastore.cfg";
pub const DATASTORE_CFG_LOCKFILE: &str = "/etc/proxmox-backup/.datastore.lck";
/// Get exclusive lock
pub fn lock_config() -> Result<BackupLockGuard, Error> {
open_backup_lockfile(DATASTORE_CFG_LOCKFILE, None, true)
}
pub fn config() -> Result<(SectionConfigData, [u8;32]), Error> {
let content = proxmox::tools::fs::file_read_optional_string(DATASTORE_CFG_FILENAME)?
.unwrap_or_else(|| "".to_string());
let digest = openssl::sha::sha256(content.as_bytes());
let data = CONFIG.parse(DATASTORE_CFG_FILENAME, &content)?;
Ok((data, digest))
}
pub fn save_config(config: &SectionConfigData) -> Result<(), Error> {
let raw = CONFIG.write(DATASTORE_CFG_FILENAME, &config)?;
pbs_config::replace_backup_config(DATASTORE_CFG_FILENAME, raw.as_bytes())
}
// shell completion helper
pub fn complete_datastore_name(_arg: &str, _param: &HashMap<String, String>) -> Vec<String> {
match config() {
Ok((data, _digest)) => data.sections.iter().map(|(id, _)| id.to_string()).collect(),
Err(_) => return vec![],
}
}
pub fn complete_acl_path(_arg: &str, _param: &HashMap<String, String>) -> Vec<String> {
let mut list = Vec::new();
list.push(String::from("/"));
list.push(String::from("/datastore"));
list.push(String::from("/datastore/"));
if let Ok((data, _digest)) = config() {
for id in data.sections.keys() {
list.push(format!("/datastore/{}", id));
}
}
list.push(String::from("/remote"));
list.push(String::from("/remote/"));
list.push(String::from("/tape"));
list.push(String::from("/tape/"));
list.push(String::from("/tape/drive"));
list.push(String::from("/tape/drive/"));
list.push(String::from("/tape/changer"));
list.push(String::from("/tape/changer/"));
list.push(String::from("/tape/pool"));
list.push(String::from("/tape/pool/"));
list.push(String::from("/tape/job"));
list.push(String::from("/tape/job/"));
list
}
pub fn complete_calendar_event(_arg: &str, _param: &HashMap<String, String>) -> Vec<String> {
// just give some hints about possible values
["minutely", "hourly", "daily", "mon..fri", "0:0"]
.iter().map(|s| String::from(*s)).collect()
}

View File

@ -15,7 +15,6 @@ use proxmox::try_block;
use pbs_buildcfg::{self, configdir}; use pbs_buildcfg::{self, configdir};
pub mod acme; pub mod acme;
pub mod datastore;
pub mod node; pub mod node;
pub mod tfa; pub mod tfa;

View File

@ -11,11 +11,7 @@ use pbs_tools::format::HumanByte;
use pbs_api_types::{ use pbs_api_types::{
User, TapeBackupJobSetup, SyncJobConfig, VerificationJobConfig, User, TapeBackupJobSetup, SyncJobConfig, VerificationJobConfig,
APTUpdateInfo, GarbageCollectionStatus, APTUpdateInfo, GarbageCollectionStatus,
Userid, Notify, DatastoreNotify, Userid, Notify, DatastoreNotify, DataStoreConfig,
};
use crate::{
config::datastore::DataStoreConfig,
}; };
const GC_OK_TEMPLATE: &str = r###" const GC_OK_TEMPLATE: &str = r###"
@ -566,7 +562,7 @@ pub fn lookup_datastore_notify_settings(
let notify = DatastoreNotify { gc: None, verify: None, sync: None }; let notify = DatastoreNotify { gc: None, verify: None, sync: None };
let (config, _digest) = match crate::config::datastore::config() { let (config, _digest) = match pbs_config::datastore::config() {
Ok(result) => result, Ok(result) => result,
Err(_) => return (email, notify), Err(_) => return (email, notify),
}; };

View File

@ -1,8 +1,6 @@
use std::path::Path; use std::path::Path;
use std::process::Command; use std::process::Command;
use crate::config::datastore;
fn files() -> Vec<&'static str> { fn files() -> Vec<&'static str> {
vec![ vec![
"/etc/hostname", "/etc/hostname",
@ -35,7 +33,7 @@ type FunctionMapping = (&'static str, fn() -> String);
fn function_calls() -> Vec<FunctionMapping> { fn function_calls() -> Vec<FunctionMapping> {
vec![ vec![
("Datastores", || { ("Datastores", || {
let config = match datastore::config() { let config = match pbs_config::datastore::config() {
Ok((config, _digest)) => config, Ok((config, _digest)) => config,
_ => return String::from("could not read datastore config"), _ => return String::from("could not read datastore config"),
}; };