2021-01-22 14:53:51 +00:00
//! Datastore Syncronization Job Management
2020-10-30 11:36:42 +00:00
use anyhow ::{ bail , format_err , Error } ;
2020-05-29 08:53:39 +00:00
use serde_json ::Value ;
2020-10-30 11:36:42 +00:00
use proxmox ::api ::{ api , ApiMethod , Permission , Router , RpcEnvironment } ;
2020-05-29 08:53:39 +00:00
use proxmox ::api ::router ::SubdirMap ;
use proxmox ::{ list_subdirs_api_method , sortable } ;
use crate ::api2 ::types ::* ;
2020-08-13 08:29:17 +00:00
use crate ::api2 ::pull ::do_sync_job ;
2020-10-30 11:36:42 +00:00
use crate ::api2 ::config ::sync ::{ check_sync_job_modify_access , check_sync_job_read_access } ;
use crate ::config ::cached_user_info ::CachedUserInfo ;
2020-05-29 08:53:39 +00:00
use crate ::config ::sync ::{ self , SyncJobStatus , SyncJobConfig } ;
2020-08-13 08:29:16 +00:00
use crate ::server ::UPID ;
2020-10-28 06:33:05 +00:00
use crate ::server ::jobstate ::{ Job , JobState } ;
2020-05-29 08:53:39 +00:00
use crate ::tools ::systemd ::time ::{
parse_calendar_event , compute_next_event } ;
#[ api(
input : {
2020-10-27 15:20:04 +00:00
properties : {
store : {
schema : DATASTORE_SCHEMA ,
optional : true ,
} ,
} ,
2020-05-29 08:53:39 +00:00
} ,
returns : {
description : " List configured jobs and their status. " ,
type : Array ,
items : { type : sync ::SyncJobStatus } ,
} ,
2020-10-30 11:36:42 +00:00
access : {
description : " Limited to sync jobs where user has Datastore.Audit on target datastore, and Remote.Audit on source remote. " ,
permission : & Permission ::Anybody ,
} ,
2020-05-29 08:53:39 +00:00
) ]
/// List all sync jobs
pub fn list_sync_jobs (
2020-10-27 15:20:04 +00:00
store : Option < String > ,
2020-05-29 08:53:39 +00:00
_param : Value ,
mut rpcenv : & mut dyn RpcEnvironment ,
) -> Result < Vec < SyncJobStatus > , Error > {
2020-10-30 11:36:42 +00:00
let auth_id : Authid = rpcenv . get_auth_id ( ) . unwrap ( ) . parse ( ) ? ;
let user_info = CachedUserInfo ::new ( ) ? ;
2020-05-29 08:53:39 +00:00
let ( config , digest ) = sync ::config ( ) ? ;
2020-10-27 15:20:04 +00:00
let mut list : Vec < SyncJobStatus > = config
. convert_to_typed_array ( " sync " ) ?
. into_iter ( )
. filter ( | job : & SyncJobStatus | {
if let Some ( store ) = & store {
& job . store = = store
} else {
true
}
2020-10-30 11:36:42 +00:00
} )
. filter ( | job : & SyncJobStatus | {
2021-01-15 13:38:27 +00:00
let as_config : SyncJobConfig = job . into ( ) ;
2020-10-30 11:36:42 +00:00
check_sync_job_read_access ( & user_info , & auth_id , & as_config )
2020-10-27 15:20:04 +00:00
} ) . collect ( ) ;
2020-05-29 08:53:39 +00:00
2020-08-13 08:29:16 +00:00
for job in & mut list {
let last_state = JobState ::load ( " syncjob " , & job . id )
. map_err ( | err | format_err! ( " could not open statefile for {}: {} " , & job . id , err ) ) ? ;
let ( upid , endtime , state , starttime ) = match last_state {
JobState ::Created { time } = > ( None , None , None , time ) ,
JobState ::Started { upid } = > {
let parsed_upid : UPID = upid . parse ( ) ? ;
( Some ( upid ) , None , None , parsed_upid . starttime )
} ,
2020-08-13 12:30:17 +00:00
JobState ::Finished { upid , state } = > {
2020-08-13 08:29:16 +00:00
let parsed_upid : UPID = upid . parse ( ) ? ;
2020-08-13 12:30:17 +00:00
( Some ( upid ) , Some ( state . endtime ( ) ) , Some ( state . to_string ( ) ) , parsed_upid . starttime )
2020-08-13 08:29:16 +00:00
} ,
2020-05-29 08:53:39 +00:00
} ;
2020-08-13 08:29:16 +00:00
job . last_run_upid = upid ;
job . last_run_state = state ;
job . last_run_endtime = endtime ;
2021-01-15 14:21:34 +00:00
let last = job . last_run_endtime . unwrap_or ( starttime ) ;
2020-06-03 14:34:07 +00:00
job . next_run = ( | | -> Option < i64 > {
let schedule = job . schedule . as_ref ( ) ? ;
let event = parse_calendar_event ( & schedule ) . ok ( ) ? ;
2020-09-04 12:33:27 +00:00
// ignore errors
2021-01-15 14:21:34 +00:00
compute_next_event ( & event , last , false ) . unwrap_or ( None )
2020-06-03 14:34:07 +00:00
} ) ( ) ;
2020-05-29 08:53:39 +00:00
}
rpcenv [ " digest " ] = proxmox ::tools ::digest_to_hex ( & digest ) . into ( ) ;
Ok ( list )
}
#[ api(
input : {
properties : {
id : {
schema : JOB_ID_SCHEMA ,
}
}
2020-10-30 11:36:42 +00:00
} ,
access : {
description : " User needs Datastore.Backup on target datastore, and Remote.Read on source remote. Additionally, remove_vanished requires Datastore.Prune, and any owner other than the user themselves requires Datastore.Modify " ,
permission : & Permission ::Anybody ,
} ,
2020-05-29 08:53:39 +00:00
) ]
/// Runs the sync jobs manually.
2021-01-22 14:53:51 +00:00
pub fn run_sync_job (
2020-05-29 08:53:39 +00:00
id : String ,
_info : & ApiMethod ,
rpcenv : & mut dyn RpcEnvironment ,
) -> Result < String , Error > {
2020-10-30 11:36:42 +00:00
let auth_id : Authid = rpcenv . get_auth_id ( ) . unwrap ( ) . parse ( ) ? ;
let user_info = CachedUserInfo ::new ( ) ? ;
2020-05-29 08:53:39 +00:00
let ( config , _digest ) = sync ::config ( ) ? ;
let sync_job : SyncJobConfig = config . lookup ( " sync " , & id ) ? ;
2020-10-30 11:36:42 +00:00
if ! check_sync_job_modify_access ( & user_info , & auth_id , & sync_job ) {
bail! ( " permission check failed " ) ;
}
2020-05-29 08:53:39 +00:00
2020-08-13 12:30:19 +00:00
let job = Job ::new ( " syncjob " , & id ) ? ;
2020-08-13 08:29:18 +00:00
2020-10-23 11:33:21 +00:00
let upid_str = do_sync_job ( job , sync_job , & auth_id , None ) ? ;
2020-05-29 08:53:39 +00:00
Ok ( upid_str )
}
#[ sortable ]
const SYNC_INFO_SUBDIRS : SubdirMap = & [
(
" run " ,
& Router ::new ( )
. post ( & API_METHOD_RUN_SYNC_JOB )
) ,
] ;
const SYNC_INFO_ROUTER : Router = Router ::new ( )
. get ( & list_subdirs_api_method! ( SYNC_INFO_SUBDIRS ) )
. subdirs ( SYNC_INFO_SUBDIRS ) ;
pub const ROUTER : Router = Router ::new ( )
. get ( & API_METHOD_LIST_SYNC_JOBS )
. match_all ( " id " , & SYNC_INFO_ROUTER ) ;