moved tape_job.rs to pbs_config workspace

This commit is contained in:
Dietmar Maurer 2021-09-07 12:32:01 +02:00
parent 5839c469c1
commit e3619d4101
22 changed files with 578 additions and 607 deletions

393
pbs-api-types/src/jobs.rs Normal file
View File

@ -0,0 +1,393 @@
use serde::{Deserialize, Serialize};
use proxmox::const_regex;
use proxmox::api::{api, schema::*};
use crate::{
Userid, Authid, REMOTE_ID_SCHEMA, DRIVE_NAME_SCHEMA, MEDIA_POOL_NAME_SCHEMA,
SINGLE_LINE_COMMENT_SCHEMA, PROXMOX_SAFE_ID_FORMAT, DATASTORE_SCHEMA,
};
const_regex!{
/// Regex for verification jobs 'DATASTORE:ACTUAL_JOB_ID'
pub VERIFICATION_JOB_WORKER_ID_REGEX = concat!(r"^(", PROXMOX_SAFE_ID_REGEX_STR!(), r"):");
/// Regex for sync jobs 'REMOTE:REMOTE_DATASTORE:LOCAL_DATASTORE:ACTUAL_JOB_ID'
pub SYNC_JOB_WORKER_ID_REGEX = concat!(r"^(", PROXMOX_SAFE_ID_REGEX_STR!(), r"):(", PROXMOX_SAFE_ID_REGEX_STR!(), r"):(", PROXMOX_SAFE_ID_REGEX_STR!(), r"):");
}
pub const UPID_SCHEMA: Schema = StringSchema::new("Unique Process/Task ID.")
.max_length(256)
.schema();
pub const JOB_ID_SCHEMA: Schema = StringSchema::new("Job ID.")
.format(&PROXMOX_SAFE_ID_FORMAT)
.min_length(3)
.max_length(32)
.schema();
pub const SYNC_SCHEDULE_SCHEMA: Schema = StringSchema::new(
"Run sync job at specified schedule.")
.format(&ApiStringFormat::VerifyFn(pbs_systemd::time::verify_calendar_event))
.type_text("<calendar-event>")
.schema();
pub const GC_SCHEDULE_SCHEMA: Schema = StringSchema::new(
"Run garbage collection job at specified schedule.")
.format(&ApiStringFormat::VerifyFn(pbs_systemd::time::verify_calendar_event))
.type_text("<calendar-event>")
.schema();
pub const PRUNE_SCHEDULE_SCHEMA: Schema = StringSchema::new(
"Run prune job at specified schedule.")
.format(&ApiStringFormat::VerifyFn(pbs_systemd::time::verify_calendar_event))
.type_text("<calendar-event>")
.schema();
pub const VERIFICATION_SCHEDULE_SCHEMA: Schema = StringSchema::new(
"Run verify job at specified schedule.")
.format(&ApiStringFormat::VerifyFn(pbs_systemd::time::verify_calendar_event))
.type_text("<calendar-event>")
.schema();
pub const REMOVE_VANISHED_BACKUPS_SCHEMA: Schema = BooleanSchema::new(
"Delete vanished backups. This remove the local copy if the remote backup was deleted.")
.default(true)
.schema();
#[api(
properties: {
"next-run": {
description: "Estimated time of the next run (UNIX epoch).",
optional: true,
type: Integer,
},
"last-run-state": {
description: "Result of the last run.",
optional: true,
type: String,
},
"last-run-upid": {
description: "Task UPID of the last run.",
optional: true,
type: String,
},
"last-run-endtime": {
description: "Endtime of the last run.",
optional: true,
type: Integer,
},
}
)]
#[derive(Serialize,Deserialize,Default)]
#[serde(rename_all="kebab-case")]
/// Job Scheduling Status
pub struct JobScheduleStatus {
#[serde(skip_serializing_if="Option::is_none")]
pub next_run: Option<i64>,
#[serde(skip_serializing_if="Option::is_none")]
pub last_run_state: Option<String>,
#[serde(skip_serializing_if="Option::is_none")]
pub last_run_upid: Option<String>,
#[serde(skip_serializing_if="Option::is_none")]
pub last_run_endtime: Option<i64>,
}
#[api()]
#[derive(Debug, Copy, Clone, PartialEq, Serialize, Deserialize)]
#[serde(rename_all = "lowercase")]
/// When do we send notifications
pub enum Notify {
/// Never send notification
Never,
/// Send notifications for failed and successful jobs
Always,
/// Send notifications for failed jobs only
Error,
}
#[api(
properties: {
gc: {
type: Notify,
optional: true,
},
verify: {
type: Notify,
optional: true,
},
sync: {
type: Notify,
optional: true,
},
},
)]
#[derive(Debug, Serialize, Deserialize)]
/// Datastore notify settings
pub struct DatastoreNotify {
/// Garbage collection settings
pub gc: Option<Notify>,
/// Verify job setting
pub verify: Option<Notify>,
/// Sync job setting
pub sync: Option<Notify>,
}
pub const DATASTORE_NOTIFY_STRING_SCHEMA: Schema = StringSchema::new(
"Datastore notification setting")
.format(&ApiStringFormat::PropertyString(&DatastoreNotify::API_SCHEMA))
.schema();
pub const IGNORE_VERIFIED_BACKUPS_SCHEMA: Schema = BooleanSchema::new(
"Do not verify backups that are already verified if their verification is not outdated.")
.default(true)
.schema();
pub const VERIFICATION_OUTDATED_AFTER_SCHEMA: Schema = IntegerSchema::new(
"Days after that a verification becomes outdated")
.minimum(1)
.schema();
#[api(
properties: {
id: {
schema: JOB_ID_SCHEMA,
},
store: {
schema: DATASTORE_SCHEMA,
},
"ignore-verified": {
optional: true,
schema: IGNORE_VERIFIED_BACKUPS_SCHEMA,
},
"outdated-after": {
optional: true,
schema: VERIFICATION_OUTDATED_AFTER_SCHEMA,
},
comment: {
optional: true,
schema: SINGLE_LINE_COMMENT_SCHEMA,
},
schedule: {
optional: true,
schema: VERIFICATION_SCHEDULE_SCHEMA,
},
}
)]
#[derive(Serialize,Deserialize)]
#[serde(rename_all="kebab-case")]
/// Verification Job
pub struct VerificationJobConfig {
/// unique ID to address this job
pub id: String,
/// the datastore ID this verificaiton job affects
pub store: String,
#[serde(skip_serializing_if="Option::is_none")]
/// if not set to false, check the age of the last snapshot verification to filter
/// out recent ones, depending on 'outdated_after' configuration.
pub ignore_verified: Option<bool>,
#[serde(skip_serializing_if="Option::is_none")]
/// Reverify snapshots after X days, never if 0. Ignored if 'ignore_verified' is false.
pub outdated_after: Option<i64>,
#[serde(skip_serializing_if="Option::is_none")]
pub comment: Option<String>,
#[serde(skip_serializing_if="Option::is_none")]
/// when to schedule this job in calendar event notation
pub schedule: Option<String>,
}
#[api(
properties: {
config: {
type: VerificationJobConfig,
},
status: {
type: JobScheduleStatus,
},
},
)]
#[derive(Serialize,Deserialize)]
#[serde(rename_all="kebab-case")]
/// Status of Verification Job
pub struct VerificationJobStatus {
#[serde(flatten)]
pub config: VerificationJobConfig,
#[serde(flatten)]
pub status: JobScheduleStatus,
}
#[api(
properties: {
store: {
schema: DATASTORE_SCHEMA,
},
pool: {
schema: MEDIA_POOL_NAME_SCHEMA,
},
drive: {
schema: DRIVE_NAME_SCHEMA,
},
"eject-media": {
description: "Eject media upon job completion.",
type: bool,
optional: true,
},
"export-media-set": {
description: "Export media set upon job completion.",
type: bool,
optional: true,
},
"latest-only": {
description: "Backup latest snapshots only.",
type: bool,
optional: true,
},
"notify-user": {
optional: true,
type: Userid,
},
}
)]
#[derive(Serialize,Deserialize,Clone)]
#[serde(rename_all="kebab-case")]
/// Tape Backup Job Setup
pub struct TapeBackupJobSetup {
pub store: String,
pub pool: String,
pub drive: String,
#[serde(skip_serializing_if="Option::is_none")]
pub eject_media: Option<bool>,
#[serde(skip_serializing_if="Option::is_none")]
pub export_media_set: Option<bool>,
#[serde(skip_serializing_if="Option::is_none")]
pub latest_only: Option<bool>,
/// Send job email notification to this user
#[serde(skip_serializing_if="Option::is_none")]
pub notify_user: Option<Userid>,
}
#[api(
properties: {
id: {
schema: JOB_ID_SCHEMA,
},
setup: {
type: TapeBackupJobSetup,
},
comment: {
optional: true,
schema: SINGLE_LINE_COMMENT_SCHEMA,
},
schedule: {
optional: true,
schema: SYNC_SCHEDULE_SCHEMA,
},
}
)]
#[derive(Serialize,Deserialize,Clone)]
#[serde(rename_all="kebab-case")]
/// Tape Backup Job
pub struct TapeBackupJobConfig {
pub id: String,
#[serde(flatten)]
pub setup: TapeBackupJobSetup,
#[serde(skip_serializing_if="Option::is_none")]
pub comment: Option<String>,
#[serde(skip_serializing_if="Option::is_none")]
pub schedule: Option<String>,
}
#[api(
properties: {
config: {
type: TapeBackupJobConfig,
},
status: {
type: JobScheduleStatus,
},
},
)]
#[derive(Serialize,Deserialize)]
#[serde(rename_all="kebab-case")]
/// Status of Tape Backup Job
pub struct TapeBackupJobStatus {
#[serde(flatten)]
pub config: TapeBackupJobConfig,
#[serde(flatten)]
pub status: JobScheduleStatus,
/// Next tape used (best guess)
#[serde(skip_serializing_if="Option::is_none")]
pub next_media_label: Option<String>,
}
#[api(
properties: {
id: {
schema: JOB_ID_SCHEMA,
},
store: {
schema: DATASTORE_SCHEMA,
},
"owner": {
type: Authid,
optional: true,
},
remote: {
schema: REMOTE_ID_SCHEMA,
},
"remote-store": {
schema: DATASTORE_SCHEMA,
},
"remove-vanished": {
schema: REMOVE_VANISHED_BACKUPS_SCHEMA,
optional: true,
},
comment: {
optional: true,
schema: SINGLE_LINE_COMMENT_SCHEMA,
},
schedule: {
optional: true,
schema: SYNC_SCHEDULE_SCHEMA,
},
}
)]
#[derive(Serialize,Deserialize,Clone)]
#[serde(rename_all="kebab-case")]
/// Sync Job
pub struct SyncJobConfig {
pub id: String,
pub store: String,
#[serde(skip_serializing_if="Option::is_none")]
pub owner: Option<Authid>,
pub remote: String,
pub remote_store: String,
#[serde(skip_serializing_if="Option::is_none")]
pub remove_vanished: Option<bool>,
#[serde(skip_serializing_if="Option::is_none")]
pub comment: Option<String>,
#[serde(skip_serializing_if="Option::is_none")]
pub schedule: Option<String>,
}
#[api(
properties: {
config: {
type: SyncJobConfig,
},
status: {
type: JobScheduleStatus,
},
},
)]
#[derive(Serialize,Deserialize)]
#[serde(rename_all="kebab-case")]
/// Status of Sync Job
pub struct SyncJobStatus {
#[serde(flatten)]
pub config: SyncJobConfig,
#[serde(flatten)]
pub status: JobScheduleStatus,
}

View File

@ -34,6 +34,9 @@ macro_rules! SNAPSHOT_PATH_REGEX_STR {
);
}
mod jobs;
pub use jobs::*;
mod key_derivation;
pub use key_derivation::{Kdf, KeyInfo};
@ -667,3 +670,33 @@ pub const NODE_TASKS_LIST_TASKS_RETURN_TYPE: ReturnType = ReturnType {
&TaskListItem::API_SCHEMA,
).schema(),
};
#[api()]
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(rename_all = "PascalCase")]
/// Describes a package for which an update is available.
pub struct APTUpdateInfo {
/// Package name
pub package: String,
/// Package title
pub title: String,
/// Package architecture
pub arch: String,
/// Human readable package description
pub description: String,
/// New version to be updated to
pub version: String,
/// Old version currently installed
pub old_version: String,
/// Package origin
pub origin: String,
/// Package priority in human-readable form
pub priority: String,
/// Package section
pub section: String,
/// URL under which the package's changelog can be retrieved
pub change_log_url: String,
/// Custom extra field for additional package information
#[serde(skip_serializing_if="Option::is_none")]
pub extra_info: Option<String>,
}

View File

@ -4,6 +4,7 @@ pub mod key_config;
pub mod media_pool;
pub mod remote;
pub mod tape_encryption_keys;
pub mod tape_job;
use anyhow::{format_err, Error};

View File

@ -0,0 +1,66 @@
use anyhow::{Error};
use lazy_static::lazy_static;
use std::collections::HashMap;
use proxmox::api::{
schema::{Schema, ApiType},
section_config::{
SectionConfig,
SectionConfigData,
SectionConfigPlugin,
}
};
use pbs_api_types::{TapeBackupJobConfig, JOB_ID_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 TapeBackupJobConfig::API_SCHEMA {
Schema::AllOf(ref allof_schema) => allof_schema,
_ => unreachable!(),
};
let plugin = SectionConfigPlugin::new("backup".to_string(), Some(String::from("id")), obj_schema);
let mut config = SectionConfig::new(&JOB_ID_SCHEMA);
config.register_plugin(plugin);
config
}
pub const TAPE_JOB_CFG_FILENAME: &str = "/etc/proxmox-backup/tape-job.cfg";
pub const TAPE_JOB_CFG_LOCKFILE: &str = "/etc/proxmox-backup/.tape-job.lck";
/// Get exclusive lock
pub fn lock() -> Result<BackupLockGuard, Error> {
open_backup_lockfile( TAPE_JOB_CFG_LOCKFILE, None, true)
}
pub fn config() -> Result<(SectionConfigData, [u8;32]), Error> {
let content = proxmox::tools::fs::file_read_optional_string(TAPE_JOB_CFG_FILENAME)?
.unwrap_or_else(|| "".to_string());
let digest = openssl::sha::sha256(content.as_bytes());
let data = CONFIG.parse(TAPE_JOB_CFG_FILENAME, &content)?;
Ok((data, digest))
}
pub fn save_config(config: &SectionConfigData) -> Result<(), Error> {
let raw = CONFIG.write(TAPE_JOB_CFG_FILENAME, &config)?;
replace_backup_config(TAPE_JOB_CFG_FILENAME, raw.as_bytes())
}
// shell completion helper
/// List all tape job IDs
pub fn complete_tape_job_id(_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![],
}
}

View File

@ -7,13 +7,10 @@ use proxmox::api::{api, ApiMethod, Permission, Router, RpcEnvironment};
use proxmox::api::router::SubdirMap;
use proxmox::{list_subdirs_api_method, sortable};
use pbs_api_types::{DATASTORE_SCHEMA, JOB_ID_SCHEMA, Authid, SyncJobConfig, SyncJobStatus};
use crate::{
api2::{
types::{
DATASTORE_SCHEMA,
JOB_ID_SCHEMA,
Authid,
},
pull::do_sync_job,
config::sync::{
check_sync_job_modify_access,
@ -22,11 +19,7 @@ use crate::{
},
config::{
cached_user_info::CachedUserInfo,
sync::{
self,
SyncJobStatus,
SyncJobConfig,
},
sync,
},
server::{
jobstate::{

View File

@ -7,11 +7,11 @@ use proxmox::api::router::SubdirMap;
use proxmox::{list_subdirs_api_method, sortable};
use proxmox::api::{api, ApiMethod, Permission, Router, RpcEnvironment};
use pbs_api_types::{VerificationJobConfig, VerificationJobStatus, JOB_ID_SCHEMA, Authid};
use crate::{
api2::types::{
DATASTORE_SCHEMA,
JOB_ID_SCHEMA,
Authid,
},
server::{
do_verification_job,
@ -22,16 +22,12 @@ use crate::{
},
},
config::{
verify,
acl::{
PRIV_DATASTORE_AUDIT,
PRIV_DATASTORE_VERIFY,
},
cached_user_info::CachedUserInfo,
verify::{
self,
VerificationJobConfig,
VerificationJobStatus,
},
},
};
@ -48,7 +44,7 @@ use crate::{
returns: {
description: "List configured jobs and their status (filtered by access)",
type: Array,
items: { type: verify::VerificationJobStatus },
items: { type: VerificationJobStatus },
},
access: {
permission: &Permission::Anybody,

View File

@ -8,11 +8,12 @@ use proxmox::http_err;
use pbs_client::{HttpClient, HttpClientOptions};
use pbs_api_types::{
REMOTE_ID_SCHEMA, REMOTE_PASSWORD_SCHEMA, Remote, RemoteConfig, RemoteConfigUpdater,
Authid, PROXMOX_CONFIG_DIGEST_SCHEMA, DataStoreListItem,
Authid, PROXMOX_CONFIG_DIGEST_SCHEMA, DataStoreListItem, SyncJobConfig,
};
use crate::config::cached_user_info::CachedUserInfo;
use crate::config::acl::{PRIV_REMOTE_AUDIT, PRIV_REMOTE_MODIFY};
use crate::config::sync;
#[api(
input: {
@ -247,8 +248,6 @@ pub fn update_remote(
/// Remove a remote from the configuration file.
pub fn delete_remote(name: String, digest: Option<String>) -> Result<(), Error> {
use crate::config::sync::{self, SyncJobConfig};
let (sync_jobs, _) = sync::config()?;
let job_list: Vec<SyncJobConfig> = sync_jobs.convert_to_typed_array("sync")?;

View File

@ -4,7 +4,11 @@ use ::serde::{Deserialize, Serialize};
use proxmox::api::{api, Permission, Router, RpcEnvironment};
use crate::api2::types::*;
use pbs_api_types::{
Authid, SyncJobConfig,
SINGLE_LINE_COMMENT_SCHEMA, JOB_ID_SCHEMA, REMOTE_ID_SCHEMA, DATASTORE_SCHEMA,
REMOVE_VANISHED_BACKUPS_SCHEMA, SYNC_SCHEDULE_SCHEMA, PROXMOX_CONFIG_DIGEST_SCHEMA,
};
use crate::config::acl::{
PRIV_DATASTORE_AUDIT,
@ -15,8 +19,7 @@ use crate::config::acl::{
PRIV_REMOTE_READ,
};
use crate::config::cached_user_info::CachedUserInfo;
use crate::config::sync::{self, SyncJobConfig};
use crate::config::{sync, cached_user_info::CachedUserInfo};
use pbs_config::open_backup_lockfile;
pub fn check_sync_job_read_access(
@ -77,7 +80,7 @@ pub fn check_sync_job_modify_access(
returns: {
description: "List configured jobs.",
type: Array,
items: { type: sync::SyncJobConfig },
items: { type: SyncJobConfig },
},
access: {
description: "Limited to sync job entries where user has Datastore.Audit on target datastore, and Remote.Audit on source remote.",
@ -154,7 +157,7 @@ pub fn create_sync_job(
let _lock = open_backup_lockfile(sync::SYNC_CFG_LOCKFILE, None, true)?;
let sync_job: sync::SyncJobConfig = serde_json::from_value(param)?;
let sync_job: SyncJobConfig = serde_json::from_value(param)?;
if !check_sync_job_modify_access(&user_info, &auth_id, &sync_job) {
bail!("permission check failed");
}
@ -182,7 +185,7 @@ pub fn create_sync_job(
},
},
},
returns: { type: sync::SyncJobConfig },
returns: { type: SyncJobConfig },
access: {
description: "Limited to sync job entries where user has Datastore.Audit on target datastore, and Remote.Audit on source remote.",
permission: &Permission::Anybody,
@ -306,7 +309,7 @@ pub fn update_sync_job(
crate::tools::detect_modified_configuration_file(&digest, &expected_digest)?;
}
let mut data: sync::SyncJobConfig = config.lookup("sync", &id)?;
let mut data: SyncJobConfig = config.lookup("sync", &id)?;
if let Some(delete) = delete {
for delete_prop in delete {

View File

@ -3,31 +3,27 @@ use serde_json::Value;
use ::serde::{Deserialize, Serialize};
use proxmox::api::{api, Router, RpcEnvironment, Permission};
use pbs_config::open_backup_lockfile;
use pbs_api_types::{
Authid,
Userid,
TapeBackupJobConfig,
JOB_ID_SCHEMA,
DATASTORE_SCHEMA,
DRIVE_NAME_SCHEMA,
PROXMOX_CONFIG_DIGEST_SCHEMA,
SINGLE_LINE_COMMENT_SCHEMA,
MEDIA_POOL_NAME_SCHEMA,
SYNC_SCHEDULE_SCHEMA,
};
use crate::{
api2::types::{
Authid,
Userid,
JOB_ID_SCHEMA,
DATASTORE_SCHEMA,
DRIVE_NAME_SCHEMA,
PROXMOX_CONFIG_DIGEST_SCHEMA,
SINGLE_LINE_COMMENT_SCHEMA,
MEDIA_POOL_NAME_SCHEMA,
SYNC_SCHEDULE_SCHEMA,
},
config::{
self,
cached_user_info::CachedUserInfo,
acl::{
PRIV_TAPE_AUDIT,
PRIV_TAPE_MODIFY,
},
tape_job::{
TAPE_JOB_CFG_LOCKFILE,
TapeBackupJobConfig,
}
},
};
@ -53,7 +49,7 @@ pub fn list_tape_backup_jobs(
let auth_id: Authid = rpcenv.get_auth_id().unwrap().parse()?;
let user_info = CachedUserInfo::new()?;
let (config, digest) = config::tape_job::config()?;
let (config, digest) = pbs_config::tape_job::config()?;
let list = config.convert_to_typed_array::<TapeBackupJobConfig>("backup")?;
@ -89,9 +85,9 @@ pub fn create_tape_backup_job(
job: TapeBackupJobConfig,
_rpcenv: &mut dyn RpcEnvironment,
) -> Result<(), Error> {
let _lock = open_backup_lockfile(TAPE_JOB_CFG_LOCKFILE, None, true)?;
let _lock = pbs_config::tape_job::lock()?;
let (mut config, _digest) = config::tape_job::config()?;
let (mut config, _digest) = pbs_config::tape_job::config()?;
if config.sections.get(&job.id).is_some() {
bail!("job '{}' already exists.", job.id);
@ -99,7 +95,7 @@ pub fn create_tape_backup_job(
config.set_data(&job.id, "backup", &job)?;
config::tape_job::save_config(&config)?;
pbs_config::tape_job::save_config(&config)?;
crate::server::jobstate::create_state_file("tape-backup-job", &job.id)?;
@ -125,7 +121,7 @@ pub fn read_tape_backup_job(
mut rpcenv: &mut dyn RpcEnvironment,
) -> Result<TapeBackupJobConfig, Error> {
let (config, digest) = config::tape_job::config()?;
let (config, digest) = pbs_config::tape_job::config()?;
let job = config.lookup("backup", &id)?;
@ -232,9 +228,9 @@ pub fn update_tape_backup_job(
delete: Option<Vec<DeletableProperty>>,
digest: Option<String>,
) -> Result<(), Error> {
let _lock = open_backup_lockfile(TAPE_JOB_CFG_LOCKFILE, None, true)?;
let _lock = pbs_config::tape_job::lock()?;
let (mut config, expected_digest) = config::tape_job::config()?;
let (mut config, expected_digest) = pbs_config::tape_job::config()?;
let mut data: TapeBackupJobConfig = config.lookup("backup", &id)?;
@ -279,7 +275,7 @@ pub fn update_tape_backup_job(
config.set_data(&id, "backup", &data)?;
config::tape_job::save_config(&config)?;
pbs_config::tape_job::save_config(&config)?;
if schedule_changed {
crate::server::jobstate::update_job_last_run_time("tape-backup-job", &id)?;
@ -311,9 +307,9 @@ pub fn delete_tape_backup_job(
digest: Option<String>,
_rpcenv: &mut dyn RpcEnvironment,
) -> Result<(), Error> {
let _lock = open_backup_lockfile(TAPE_JOB_CFG_LOCKFILE, None, true)?;
let _lock = pbs_config::tape_job::lock()?;
let (mut config, expected_digest) = config::tape_job::config()?;
let (mut config, expected_digest) = pbs_config::tape_job::config()?;
if let Some(ref digest) = digest {
let digest = proxmox::tools::hex_to_digest(digest)?;
@ -327,7 +323,7 @@ pub fn delete_tape_backup_job(
Err(_) => { bail!("job '{}' does not exist.", id) },
};
config::tape_job::save_config(&config)?;
pbs_config::tape_job::save_config(&config)?;
crate::server::jobstate::remove_state_file("tape-backup-job", &id)?;

View File

@ -4,7 +4,12 @@ use ::serde::{Deserialize, Serialize};
use proxmox::api::{api, Permission, Router, RpcEnvironment};
use crate::api2::types::*;
use pbs_api_types::{
Authid, VerificationJobConfig,
SINGLE_LINE_COMMENT_SCHEMA, JOB_ID_SCHEMA, IGNORE_VERIFIED_BACKUPS_SCHEMA,
VERIFICATION_OUTDATED_AFTER_SCHEMA, VERIFICATION_SCHEDULE_SCHEMA,
DATASTORE_SCHEMA, PROXMOX_CONFIG_DIGEST_SCHEMA,
};
use crate::config::acl::{
PRIV_DATASTORE_AUDIT,
@ -12,7 +17,7 @@ use crate::config::acl::{
};
use crate::config::cached_user_info::CachedUserInfo;
use crate::config::verify::{self, VerificationJobConfig};
use crate::config::verify;
use pbs_config::open_backup_lockfile;
#[api(
@ -22,7 +27,7 @@ use pbs_config::open_backup_lockfile;
returns: {
description: "List configured jobs.",
type: Array,
items: { type: verify::VerificationJobConfig },
items: { type: VerificationJobConfig },
},
access: {
permission: &Permission::Anybody,
@ -97,7 +102,7 @@ pub fn create_verification_job(
let auth_id: Authid = rpcenv.get_auth_id().unwrap().parse()?;
let user_info = CachedUserInfo::new()?;
let verification_job: verify::VerificationJobConfig = serde_json::from_value(param)?;
let verification_job: VerificationJobConfig = serde_json::from_value(param)?;
user_info.check_privs(&auth_id, &["datastore", &verification_job.store], PRIV_DATASTORE_VERIFY, false)?;
@ -126,7 +131,7 @@ pub fn create_verification_job(
},
},
},
returns: { type: verify::VerificationJobConfig },
returns: { type: VerificationJobConfig },
access: {
permission: &Permission::Anybody,
description: "Requires Datastore.Audit or Datastore.Verify on job's datastore.",
@ -142,7 +147,7 @@ pub fn read_verification_job(
let (config, digest) = verify::config()?;
let verification_job: verify::VerificationJobConfig = config.lookup("verification", &id)?;
let verification_job: VerificationJobConfig = config.lookup("verification", &id)?;
let required_privs = PRIV_DATASTORE_AUDIT | PRIV_DATASTORE_VERIFY;
user_info.check_privs(&auth_id, &["datastore", &verification_job.store], required_privs, true)?;
@ -239,7 +244,7 @@ pub fn update_verification_job(
crate::tools::detect_modified_configuration_file(&digest, &expected_digest)?;
}
let mut data: verify::VerificationJobConfig = config.lookup("verification", &id)?;
let mut data: VerificationJobConfig = config.lookup("verification", &id)?;
// check existing store
user_info.check_privs(&auth_id, &["datastore", &data.store], PRIV_DATASTORE_VERIFY, true)?;
@ -318,7 +323,7 @@ pub fn delete_verification_job(
let (mut config, expected_digest) = verify::config()?;
let job: verify::VerificationJobConfig = config.lookup("verification", &id)?;
let job: VerificationJobConfig = config.lookup("verification", &id)?;
user_info.check_privs(&auth_id, &["datastore", &job.store], PRIV_DATASTORE_VERIFY, true)?;
if let Some(ref digest) = digest {

View File

@ -14,10 +14,10 @@ use pbs_api_types::{
use crate::server::{WorkerTask, jobstate::Job, pull::pull_store};
use crate::backup::DataStore;
use crate::api2::types::REMOVE_VANISHED_BACKUPS_SCHEMA;
use pbs_api_types::{SyncJobConfig, REMOVE_VANISHED_BACKUPS_SCHEMA};
use crate::config::{
sync::SyncJobConfig,
acl::{PRIV_DATASTORE_BACKUP, PRIV_DATASTORE_PRUNE, PRIV_REMOTE_READ},
cached_user_info::CachedUserInfo,
};

View File

@ -15,25 +15,23 @@ use proxmox::{
},
};
use pbs_api_types::{Authid, Userid};
use pbs_api_types::{
Authid, Userid, TapeBackupJobConfig, TapeBackupJobSetup, TapeBackupJobStatus, MediaPoolConfig,
UPID_SCHEMA, JOB_ID_SCHEMA,
};
use pbs_datastore::{task_log, task_warn, StoreProgress};
use pbs_datastore::backup_info::{BackupDir, BackupInfo};
use pbs_datastore::task::TaskState;
use crate::{
config::{
self,
cached_user_info::CachedUserInfo,
acl::{
PRIV_DATASTORE_READ,
PRIV_TAPE_AUDIT,
PRIV_TAPE_WRITE,
},
tape_job::{
TapeBackupJobConfig,
TapeBackupJobSetup,
TapeBackupJobStatus,
},
},
server::{
lookup_user_email,
@ -45,11 +43,6 @@ use crate::{
},
},
backup::DataStore,
api2::types::{
UPID_SCHEMA,
JOB_ID_SCHEMA,
MediaPoolConfig,
},
server::WorkerTask,
tape::{
TAPE_STATUS_DIR,
@ -121,7 +114,7 @@ pub fn list_tape_backup_jobs(
let auth_id: Authid = rpcenv.get_auth_id().unwrap().parse()?;
let user_info = CachedUserInfo::new()?;
let (job_config, digest) = config::tape_job::config()?;
let (job_config, digest) = pbs_config::tape_job::config()?;
let (pool_config, _pool_digest) = pbs_config::media_pool::config()?;
let (drive_config, _digest) = pbs_config::drive::config()?;
@ -310,7 +303,7 @@ pub fn run_tape_backup_job(
) -> Result<String, Error> {
let auth_id: Authid = rpcenv.get_auth_id().unwrap().parse()?;
let (config, _digest) = config::tape_job::config()?;
let (config, _digest) = pbs_config::tape_job::config()?;
let backup_job: TapeBackupJobConfig = config.lookup("backup", &id)?;
check_backup_permission(

View File

@ -27,11 +27,6 @@ pub const FILENAME_FORMAT: ApiStringFormat = ApiStringFormat::VerifyFn(|name| {
const_regex!{
pub SYSTEMD_DATETIME_REGEX = r"^\d{4}-\d{2}-\d{2}( \d{2}:\d{2}(:\d{2})?)?$"; // fixme: define in common_regex ?
/// Regex for verification jobs 'DATASTORE:ACTUAL_JOB_ID'
pub VERIFICATION_JOB_WORKER_ID_REGEX = concat!(r"^(", PROXMOX_SAFE_ID_REGEX_STR!(), r"):");
/// Regex for sync jobs 'REMOTE:REMOTE_DATASTORE:LOCAL_DATASTORE:ACTUAL_JOB_ID'
pub SYNC_JOB_WORKER_ID_REGEX = concat!(r"^(", PROXMOX_SAFE_ID_REGEX_STR!(), r"):(", PROXMOX_SAFE_ID_REGEX_STR!(), r"):(", PROXMOX_SAFE_ID_REGEX_STR!(), r"):");
pub ACL_PATH_REGEX = concat!(r"^(?:/|", r"(?:/", PROXMOX_SAFE_ID_REGEX_STR!(), ")+", r")$");
pub SUBSCRIPTION_KEY_REGEX = concat!(r"^pbs(?:[cbsp])-[0-9a-f]{10}$");
@ -202,10 +197,6 @@ pub struct AclListItem {
pub roleid: String,
}
pub const UPID_SCHEMA: Schema = StringSchema::new("Unique Process/Task ID.")
.max_length(256)
.schema();
pub const DATASTORE_MAP_SCHEMA: Schema = StringSchema::new("Datastore mapping.")
.format(&DATASTORE_MAP_FORMAT)
.min_length(3)
@ -225,50 +216,6 @@ pub const DATASTORE_MAP_LIST_SCHEMA: Schema = StringSchema::new(
.format(&ApiStringFormat::PropertyString(&DATASTORE_MAP_ARRAY_SCHEMA))
.schema();
pub const SYNC_SCHEDULE_SCHEMA: Schema = StringSchema::new(
"Run sync job at specified schedule.")
.format(&ApiStringFormat::VerifyFn(pbs_systemd::time::verify_calendar_event))
.type_text("<calendar-event>")
.schema();
pub const GC_SCHEDULE_SCHEMA: Schema = StringSchema::new(
"Run garbage collection job at specified schedule.")
.format(&ApiStringFormat::VerifyFn(pbs_systemd::time::verify_calendar_event))
.type_text("<calendar-event>")
.schema();
pub const PRUNE_SCHEDULE_SCHEMA: Schema = StringSchema::new(
"Run prune job at specified schedule.")
.format(&ApiStringFormat::VerifyFn(pbs_systemd::time::verify_calendar_event))
.type_text("<calendar-event>")
.schema();
pub const VERIFICATION_SCHEDULE_SCHEMA: Schema = StringSchema::new(
"Run verify job at specified schedule.")
.format(&ApiStringFormat::VerifyFn(pbs_systemd::time::verify_calendar_event))
.type_text("<calendar-event>")
.schema();
pub const JOB_ID_SCHEMA: Schema = StringSchema::new("Job ID.")
.format(&PROXMOX_SAFE_ID_FORMAT)
.min_length(3)
.max_length(32)
.schema();
pub const REMOVE_VANISHED_BACKUPS_SCHEMA: Schema = BooleanSchema::new(
"Delete vanished backups. This remove the local copy if the remote backup was deleted.")
.default(true)
.schema();
pub const IGNORE_VERIFIED_BACKUPS_SCHEMA: Schema = BooleanSchema::new(
"Do not verify backups that are already verified if their verification is not outdated.")
.default(true)
.schema();
pub const VERIFICATION_OUTDATED_AFTER_SCHEMA: Schema = IntegerSchema::new(
"Days after that a verification becomes outdated")
.minimum(1)
.schema();
pub const HOSTNAME_SCHEMA: Schema = StringSchema::new("Hostname (as defined in RFC1123).")
.format(&HOSTNAME_FORMAT)
@ -690,120 +637,6 @@ pub enum RRDTimeFrameResolution {
Year = 60*10080,
}
#[api()]
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(rename_all = "PascalCase")]
/// Describes a package for which an update is available.
pub struct APTUpdateInfo {
/// Package name
pub package: String,
/// Package title
pub title: String,
/// Package architecture
pub arch: String,
/// Human readable package description
pub description: String,
/// New version to be updated to
pub version: String,
/// Old version currently installed
pub old_version: String,
/// Package origin
pub origin: String,
/// Package priority in human-readable form
pub priority: String,
/// Package section
pub section: String,
/// URL under which the package's changelog can be retrieved
pub change_log_url: String,
/// Custom extra field for additional package information
#[serde(skip_serializing_if="Option::is_none")]
pub extra_info: Option<String>,
}
#[api()]
#[derive(Debug, Copy, Clone, PartialEq, Serialize, Deserialize)]
#[serde(rename_all = "lowercase")]
/// When do we send notifications
pub enum Notify {
/// Never send notification
Never,
/// Send notifications for failed and successful jobs
Always,
/// Send notifications for failed jobs only
Error,
}
#[api(
properties: {
gc: {
type: Notify,
optional: true,
},
verify: {
type: Notify,
optional: true,
},
sync: {
type: Notify,
optional: true,
},
},
)]
#[derive(Debug, Serialize, Deserialize)]
/// Datastore notify settings
pub struct DatastoreNotify {
/// Garbage collection settings
pub gc: Option<Notify>,
/// Verify job setting
pub verify: Option<Notify>,
/// Sync job setting
pub sync: Option<Notify>,
}
pub const DATASTORE_NOTIFY_STRING_SCHEMA: Schema = StringSchema::new(
"Datastore notification setting")
.format(&ApiStringFormat::PropertyString(&DatastoreNotify::API_SCHEMA))
.schema();
#[api(
properties: {
"next-run": {
description: "Estimated time of the next run (UNIX epoch).",
optional: true,
type: Integer,
},
"last-run-state": {
description: "Result of the last run.",
optional: true,
type: String,
},
"last-run-upid": {
description: "Task UPID of the last run.",
optional: true,
type: String,
},
"last-run-endtime": {
description: "Endtime of the last run.",
optional: true,
type: Integer,
},
}
)]
#[derive(Serialize,Deserialize,Default)]
#[serde(rename_all="kebab-case")]
/// Job Scheduling Status
pub struct JobScheduleStatus {
#[serde(skip_serializing_if="Option::is_none")]
pub next_run: Option<i64>,
#[serde(skip_serializing_if="Option::is_none")]
pub last_run_state: Option<String>,
#[serde(skip_serializing_if="Option::is_none")]
pub last_run_upid: Option<String>,
#[serde(skip_serializing_if="Option::is_none")]
pub last_run_endtime: Option<i64>,
}
#[api]
#[derive(Serialize, Deserialize, Default)]
#[serde(rename_all = "kebab-case")]

View File

@ -56,7 +56,7 @@ fn main() -> Result<(), Error> {
"apidata.js" => generate_api_tree(),
"datastore.cfg" => dump_section_config(&config::datastore::CONFIG),
"tape.cfg" => dump_section_config(&pbs_config::drive::CONFIG),
"tape-job.cfg" => dump_section_config(&config::tape_job::CONFIG),
"tape-job.cfg" => dump_section_config(&pbs_config::tape_job::CONFIG),
"user.cfg" => dump_section_config(&config::user::CONFIG),
"remote.cfg" => dump_section_config(&pbs_config::remote::CONFIG),
"sync.cfg" => dump_section_config(&config::sync::CONFIG),

View File

@ -32,7 +32,7 @@ use pbs_buildcfg::configdir;
use pbs_systemd::time::{compute_next_event, parse_calendar_event};
use pbs_tools::logrotate::LogRotate;
use proxmox_backup::api2::types::Authid;
use pbs_api_types::{Authid, TapeBackupJobConfig, VerificationJobConfig, SyncJobConfig};
use proxmox_backup::server;
use proxmox_backup::auth_helpers::*;
use proxmox_backup::tools::{
@ -520,12 +520,8 @@ async fn schedule_datastore_prune() {
async fn schedule_datastore_sync_jobs() {
use proxmox_backup::config::sync::{
self,
SyncJobConfig,
};
let config = match sync::config() {
let config = match proxmox_backup::config::sync::config() {
Err(err) => {
eprintln!("unable to read sync job config - {}", err);
return;
@ -564,12 +560,7 @@ async fn schedule_datastore_sync_jobs() {
async fn schedule_datastore_verify_jobs() {
use proxmox_backup::config::verify::{
self,
VerificationJobConfig,
};
let config = match verify::config() {
let config = match proxmox_backup::config::verify::config() {
Err(err) => {
eprintln!("unable to read verification job config - {}", err);
return;
@ -605,12 +596,7 @@ async fn schedule_datastore_verify_jobs() {
async fn schedule_tape_backup_jobs() {
use proxmox_backup::config::tape_job::{
self,
TapeBackupJobConfig,
};
let config = match tape_job::config() {
let config = match pbs_config::tape_job::config() {
Err(err) => {
eprintln!("unable to read tape job config - {}", err);
return;

View File

@ -3,14 +3,12 @@ use serde_json::Value;
use proxmox::api::{api, cli::*, RpcEnvironment, ApiHandler};
use pbs_api_types::JOB_ID_SCHEMA;
use pbs_client::{connect_to_localhost, view_task_result};
use proxmox_backup::{
config,
api2::{
self,
types::*,
},
api2,
};
#[api(
@ -112,17 +110,17 @@ pub fn backup_job_commands() -> CommandLineInterface {
.insert("show",
CliCommand::new(&API_METHOD_SHOW_TAPE_BACKUP_JOB)
.arg_param(&["id"])
.completion_cb("id", config::tape_job::complete_tape_job_id)
.completion_cb("id", pbs_config::tape_job::complete_tape_job_id)
)
.insert("run",
CliCommand::new(&API_METHOD_RUN_TAPE_BACKUP_JOB)
.arg_param(&["id"])
.completion_cb("id", config::tape_job::complete_tape_job_id)
.completion_cb("id", pbs_config::tape_job::complete_tape_job_id)
)
.insert("create",
CliCommand::new(&api2::config::tape_backup_job::API_METHOD_CREATE_TAPE_BACKUP_JOB)
.arg_param(&["id"])
.completion_cb("id", 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("store", config::datastore::complete_datastore_name)
.completion_cb("pool", pbs_config::media_pool::complete_pool_name)
@ -131,7 +129,7 @@ pub fn backup_job_commands() -> CommandLineInterface {
.insert("update",
CliCommand::new(&api2::config::tape_backup_job::API_METHOD_UPDATE_TAPE_BACKUP_JOB)
.arg_param(&["id"])
.completion_cb("id", 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("store", config::datastore::complete_datastore_name)
.completion_cb("pool", pbs_config::media_pool::complete_pool_name)
@ -140,7 +138,7 @@ pub fn backup_job_commands() -> CommandLineInterface {
.insert("remove",
CliCommand::new(&api2::config::tape_backup_job::API_METHOD_DELETE_TAPE_BACKUP_JOB)
.arg_param(&["id"])
.completion_cb("id", config::tape_job::complete_tape_job_id)
.completion_cb("id", pbs_config::tape_job::complete_tape_job_id)
);
cmd_def.into()

View File

@ -25,7 +25,6 @@ pub mod tfa;
pub mod token_shadow;
pub mod user;
pub mod verify;
pub mod tape_job;
/// Check configuration directory permissions
///

View File

@ -1,10 +1,8 @@
use anyhow::{Error};
use lazy_static::lazy_static;
use std::collections::HashMap;
use serde::{Serialize, Deserialize};
use proxmox::api::{
api,
schema::*,
section_config::{
SectionConfig,
@ -13,82 +11,12 @@ use proxmox::api::{
}
};
use crate::api2::types::*;
use pbs_api_types::{JOB_ID_SCHEMA, SyncJobConfig};
lazy_static! {
pub static ref CONFIG: SectionConfig = init();
}
#[api(
properties: {
id: {
schema: JOB_ID_SCHEMA,
},
store: {
schema: DATASTORE_SCHEMA,
},
"owner": {
type: Authid,
optional: true,
},
remote: {
schema: REMOTE_ID_SCHEMA,
},
"remote-store": {
schema: DATASTORE_SCHEMA,
},
"remove-vanished": {
schema: REMOVE_VANISHED_BACKUPS_SCHEMA,
optional: true,
},
comment: {
optional: true,
schema: SINGLE_LINE_COMMENT_SCHEMA,
},
schedule: {
optional: true,
schema: SYNC_SCHEDULE_SCHEMA,
},
}
)]
#[derive(Serialize,Deserialize,Clone)]
#[serde(rename_all="kebab-case")]
/// Sync Job
pub struct SyncJobConfig {
pub id: String,
pub store: String,
#[serde(skip_serializing_if="Option::is_none")]
pub owner: Option<Authid>,
pub remote: String,
pub remote_store: String,
#[serde(skip_serializing_if="Option::is_none")]
pub remove_vanished: Option<bool>,
#[serde(skip_serializing_if="Option::is_none")]
pub comment: Option<String>,
#[serde(skip_serializing_if="Option::is_none")]
pub schedule: Option<String>,
}
#[api(
properties: {
config: {
type: SyncJobConfig,
},
status: {
type: JobScheduleStatus,
},
},
)]
#[derive(Serialize,Deserialize)]
#[serde(rename_all="kebab-case")]
/// Status of Sync Job
pub struct SyncJobStatus {
#[serde(flatten)]
pub config: SyncJobConfig,
#[serde(flatten)]
pub status: JobScheduleStatus,
}
fn init() -> SectionConfig {
let obj_schema = match SyncJobConfig::API_SCHEMA {

View File

@ -1,174 +0,0 @@
use anyhow::{Error};
use lazy_static::lazy_static;
use std::collections::HashMap;
use serde::{Serialize, Deserialize};
use proxmox::api::{
api,
schema::*,
section_config::{
SectionConfig,
SectionConfigData,
SectionConfigPlugin,
}
};
use crate::api2::types::{
Userid,
JOB_ID_SCHEMA,
DATASTORE_SCHEMA,
DRIVE_NAME_SCHEMA,
MEDIA_POOL_NAME_SCHEMA,
SINGLE_LINE_COMMENT_SCHEMA,
SYNC_SCHEDULE_SCHEMA,
JobScheduleStatus,
};
lazy_static! {
pub static ref CONFIG: SectionConfig = init();
}
#[api(
properties: {
store: {
schema: DATASTORE_SCHEMA,
},
pool: {
schema: MEDIA_POOL_NAME_SCHEMA,
},
drive: {
schema: DRIVE_NAME_SCHEMA,
},
"eject-media": {
description: "Eject media upon job completion.",
type: bool,
optional: true,
},
"export-media-set": {
description: "Export media set upon job completion.",
type: bool,
optional: true,
},
"latest-only": {
description: "Backup latest snapshots only.",
type: bool,
optional: true,
},
"notify-user": {
optional: true,
type: Userid,
},
}
)]
#[derive(Serialize,Deserialize,Clone)]
#[serde(rename_all="kebab-case")]
/// Tape Backup Job Setup
pub struct TapeBackupJobSetup {
pub store: String,
pub pool: String,
pub drive: String,
#[serde(skip_serializing_if="Option::is_none")]
pub eject_media: Option<bool>,
#[serde(skip_serializing_if="Option::is_none")]
pub export_media_set: Option<bool>,
#[serde(skip_serializing_if="Option::is_none")]
pub latest_only: Option<bool>,
/// Send job email notification to this user
#[serde(skip_serializing_if="Option::is_none")]
pub notify_user: Option<Userid>,
}
#[api(
properties: {
id: {
schema: JOB_ID_SCHEMA,
},
setup: {
type: TapeBackupJobSetup,
},
comment: {
optional: true,
schema: SINGLE_LINE_COMMENT_SCHEMA,
},
schedule: {
optional: true,
schema: SYNC_SCHEDULE_SCHEMA,
},
}
)]
#[derive(Serialize,Deserialize,Clone)]
#[serde(rename_all="kebab-case")]
/// Tape Backup Job
pub struct TapeBackupJobConfig {
pub id: String,
#[serde(flatten)]
pub setup: TapeBackupJobSetup,
#[serde(skip_serializing_if="Option::is_none")]
pub comment: Option<String>,
#[serde(skip_serializing_if="Option::is_none")]
pub schedule: Option<String>,
}
#[api(
properties: {
config: {
type: TapeBackupJobConfig,
},
status: {
type: JobScheduleStatus,
},
},
)]
#[derive(Serialize,Deserialize)]
#[serde(rename_all="kebab-case")]
/// Status of Tape Backup Job
pub struct TapeBackupJobStatus {
#[serde(flatten)]
pub config: TapeBackupJobConfig,
#[serde(flatten)]
pub status: JobScheduleStatus,
/// Next tape used (best guess)
#[serde(skip_serializing_if="Option::is_none")]
pub next_media_label: Option<String>,
}
fn init() -> SectionConfig {
let obj_schema = match TapeBackupJobConfig::API_SCHEMA {
Schema::AllOf(ref allof_schema) => allof_schema,
_ => unreachable!(),
};
let plugin = SectionConfigPlugin::new("backup".to_string(), Some(String::from("id")), obj_schema);
let mut config = SectionConfig::new(&JOB_ID_SCHEMA);
config.register_plugin(plugin);
config
}
pub const TAPE_JOB_CFG_FILENAME: &str = "/etc/proxmox-backup/tape-job.cfg";
pub const TAPE_JOB_CFG_LOCKFILE: &str = "/etc/proxmox-backup/.tape-job.lck";
pub fn config() -> Result<(SectionConfigData, [u8;32]), Error> {
let content = proxmox::tools::fs::file_read_optional_string(TAPE_JOB_CFG_FILENAME)?
.unwrap_or_else(|| "".to_string());
let digest = openssl::sha::sha256(content.as_bytes());
let data = CONFIG.parse(TAPE_JOB_CFG_FILENAME, &content)?;
Ok((data, digest))
}
pub fn save_config(config: &SectionConfigData) -> Result<(), Error> {
let raw = CONFIG.write(TAPE_JOB_CFG_FILENAME, &config)?;
pbs_config::replace_backup_config(TAPE_JOB_CFG_FILENAME, raw.as_bytes())
}
// shell completion helper
/// List all tape job IDs
pub fn complete_tape_job_id(_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![],
}
}

View File

@ -1,10 +1,8 @@
use anyhow::{Error};
use lazy_static::lazy_static;
use std::collections::HashMap;
use serde::{Serialize, Deserialize};
use proxmox::api::{
api,
schema::*,
section_config::{
SectionConfig,
@ -13,81 +11,12 @@ use proxmox::api::{
}
};
use crate::api2::types::*;
use pbs_api_types::{JOB_ID_SCHEMA, VerificationJobConfig};
lazy_static! {
pub static ref CONFIG: SectionConfig = init();
}
#[api(
properties: {
id: {
schema: JOB_ID_SCHEMA,
},
store: {
schema: DATASTORE_SCHEMA,
},
"ignore-verified": {
optional: true,
schema: IGNORE_VERIFIED_BACKUPS_SCHEMA,
},
"outdated-after": {
optional: true,
schema: VERIFICATION_OUTDATED_AFTER_SCHEMA,
},
comment: {
optional: true,
schema: SINGLE_LINE_COMMENT_SCHEMA,
},
schedule: {
optional: true,
schema: VERIFICATION_SCHEDULE_SCHEMA,
},
}
)]
#[derive(Serialize,Deserialize)]
#[serde(rename_all="kebab-case")]
/// Verification Job
pub struct VerificationJobConfig {
/// unique ID to address this job
pub id: String,
/// the datastore ID this verificaiton job affects
pub store: String,
#[serde(skip_serializing_if="Option::is_none")]
/// if not set to false, check the age of the last snapshot verification to filter
/// out recent ones, depending on 'outdated_after' configuration.
pub ignore_verified: Option<bool>,
#[serde(skip_serializing_if="Option::is_none")]
/// Reverify snapshots after X days, never if 0. Ignored if 'ignore_verified' is false.
pub outdated_after: Option<i64>,
#[serde(skip_serializing_if="Option::is_none")]
pub comment: Option<String>,
#[serde(skip_serializing_if="Option::is_none")]
/// when to schedule this job in calendar event notation
pub schedule: Option<String>,
}
#[api(
properties: {
config: {
type: VerificationJobConfig,
},
status: {
type: JobScheduleStatus,
},
},
)]
#[derive(Serialize,Deserialize)]
#[serde(rename_all="kebab-case")]
/// Status of Verification Job
pub struct VerificationJobStatus {
#[serde(flatten)]
pub config: VerificationJobConfig,
#[serde(flatten)]
pub status: JobScheduleStatus,
}
fn init() -> SectionConfig {
let obj_schema = match VerificationJobConfig::API_SCHEMA {
Schema::Object(ref obj_schema) => obj_schema,

View File

@ -8,19 +8,14 @@ use proxmox::api::schema::{ApiType, parse_property_string};
use proxmox::try_block;
use pbs_tools::format::HumanByte;
use pbs_api_types::{
TapeBackupJobSetup, SyncJobConfig, VerificationJobConfig,
APTUpdateInfo, GarbageCollectionStatus,
Userid, Notify, DatastoreNotify,
};
use crate::{
config::datastore::DataStoreConfig,
config::verify::VerificationJobConfig,
config::sync::SyncJobConfig,
config::tape_job::TapeBackupJobSetup,
api2::types::{
APTUpdateInfo,
GarbageCollectionStatus,
Userid,
Notify,
DatastoreNotify,
},
};
const GC_OK_TEMPLATE: &str = r###"

View File

@ -1,12 +1,11 @@
use anyhow::{format_err, Error};
use pbs_datastore::task_log;
use pbs_api_types::{Authid, VerificationJobConfig};
use crate::{
server::WorkerTask,
api2::types::*,
server::jobstate::Job,
config::verify::VerificationJobConfig,
backup::{
DataStore,
verify_filter,