4 use anyhow
::{bail, Error}
;
19 drive
::check_drive_exists
,
29 MEDIA_POOL_NAME_SCHEMA
,
41 update_changer_online_status
,
49 schema
: DATASTORE_SCHEMA
,
52 schema
: MEDIA_POOL_NAME_SCHEMA
,
60 /// Backup datastore to tape media pool
64 rpcenv
: &mut dyn RpcEnvironment
,
65 ) -> Result
<Value
, Error
> {
67 let auth_id
: Authid
= rpcenv
.get_auth_id().unwrap().parse()?
;
69 let datastore
= DataStore
::lookup_datastore(&store
)?
;
71 let (config
, _digest
) = config
::media_pool
::config()?
;
72 let pool_config
: MediaPoolConfig
= config
.lookup("pool", &pool
)?
;
74 let (drive_config
, _digest
) = config
::drive
::config()?
;
75 // early check before starting worker
76 check_drive_exists(&drive_config
, &pool_config
.drive
)?
;
78 let to_stdout
= if rpcenv
.env_type() == RpcEnvironmentType
::CLI { true }
else { false }
;
80 let upid_str
= WorkerTask
::new_thread(
86 backup_worker(&worker
, datastore
, &pool_config
)?
;
96 pub const ROUTER
: Router
= Router
::new()
97 .post(&API_METHOD_BACKUP
);
102 datastore
: Arc
<DataStore
>,
103 pool_config
: &MediaPoolConfig
,
104 ) -> Result
<(), Error
> {
106 let status_path
= Path
::new(TAPE_STATUS_DIR
);
108 let _lock
= MediaPool
::lock(status_path
, &pool_config
.name
)?
;
110 worker
.log("update media online status");
111 let has_changer
= update_media_online_status(&pool_config
.drive
)?
;
113 let use_offline_media
= !has_changer
;
115 let pool
= MediaPool
::with_config(status_path
, &pool_config
, use_offline_media
)?
;
117 let mut pool_writer
= PoolWriter
::new(pool
, &pool_config
.drive
)?
;
119 let mut group_list
= BackupInfo
::list_backup_groups(&datastore
.base_path())?
;
121 group_list
.sort_unstable();
123 for group
in group_list
{
124 let mut snapshot_list
= group
.list_backups(&datastore
.base_path())?
;
125 BackupInfo
::sort_list(&mut snapshot_list
, true); // oldest first
127 for info
in snapshot_list
{
128 if pool_writer
.contains_snapshot(&info
.backup_dir
.to_string()) {
131 worker
.log(format
!("backup snapshot {}", info
.backup_dir
));
132 backup_snapshot(worker
, &mut pool_writer
, datastore
.clone(), info
.backup_dir
)?
;
136 pool_writer
.commit()?
;
141 // Try to update the the media online status
142 fn update_media_online_status(drive
: &str) -> Result
<bool
, Error
> {
144 let (config
, _digest
) = config
::drive
::config()?
;
146 let mut has_changer
= false;
148 if let Ok(Some((mut changer
, changer_name
))) = media_changer(&config
, drive
) {
152 let changer_id_list
= changer
.online_media_changer_ids()?
;
154 let status_path
= Path
::new(TAPE_STATUS_DIR
);
155 let mut inventory
= Inventory
::load(status_path
)?
;
157 update_changer_online_status(
168 pub fn backup_snapshot(
170 pool_writer
: &mut PoolWriter
,
171 datastore
: Arc
<DataStore
>,
173 ) -> Result
<(), Error
> {
175 worker
.log(format
!("start backup {}:{}", datastore
.name(), snapshot
));
177 let snapshot_reader
= SnapshotReader
::new(datastore
.clone(), snapshot
.clone())?
;
179 let mut chunk_iter
= snapshot_reader
.chunk_iterator()?
.peekable();
182 // test is we have remaining chunks
183 if chunk_iter
.peek().is_none() {
187 let uuid
= pool_writer
.load_writable_media(worker
)?
;
189 let (leom
, _bytes
) = pool_writer
.append_chunk_archive(&datastore
, &mut chunk_iter
)?
;
192 pool_writer
.set_media_status_full(&uuid
)?
;
196 let uuid
= pool_writer
.load_writable_media(worker
)?
;
198 let (done
, _bytes
) = pool_writer
.append_snapshot_archive(&snapshot_reader
)?
;
201 // does not fit on tape, so we try on next volume
202 pool_writer
.set_media_status_full(&uuid
)?
;
204 pool_writer
.load_writable_media(worker
)?
;
205 let (done
, _bytes
) = pool_writer
.append_snapshot_archive(&snapshot_reader
)?
;
208 bail
!("write_snapshot_archive failed on second media");
212 worker
.log(format
!("end backup {}:{}", datastore
.name(), snapshot
));