tape: implement fast catalog restore
This commit is contained in:
parent
c6f55139f8
commit
074503f288
@ -48,7 +48,10 @@ use crate::{
|
||||
MamAttribute,
|
||||
LinuxDriveAndMediaStatus,
|
||||
},
|
||||
tape::restore::restore_media,
|
||||
tape::restore::{
|
||||
fast_catalog_restore,
|
||||
restore_media,
|
||||
},
|
||||
},
|
||||
server::WorkerTask,
|
||||
tape::{
|
||||
@ -1279,7 +1282,7 @@ pub fn catalog_media(
|
||||
|
||||
let mut inventory = Inventory::new(status_path);
|
||||
|
||||
let _media_set_lock = match media_id.media_set_label {
|
||||
let (_media_set_lock, media_set_uuid) = match media_id.media_set_label {
|
||||
None => {
|
||||
worker.log("media is empty");
|
||||
let _lock = lock_unassigned_media_pool(status_path)?;
|
||||
@ -1307,7 +1310,7 @@ pub fn catalog_media(
|
||||
|
||||
inventory.store(media_id.clone(), false)?;
|
||||
|
||||
media_set_lock
|
||||
(media_set_lock, &set.uuid)
|
||||
}
|
||||
};
|
||||
|
||||
@ -1315,7 +1318,14 @@ pub fn catalog_media(
|
||||
bail!("media catalog exists (please use --force to overwrite)");
|
||||
}
|
||||
|
||||
// fixme: implement fast catalog restore
|
||||
let media_set = inventory.compute_media_set_members(media_set_uuid)?;
|
||||
|
||||
if fast_catalog_restore(&worker, &mut drive, &media_set, &media_id.label.uuid)? {
|
||||
return Ok(())
|
||||
}
|
||||
|
||||
task_log!(worker, "no catalog found - scaning entire media now");
|
||||
|
||||
restore_media(&worker, &mut drive, &media_id, None, verbose)?;
|
||||
|
||||
Ok(())
|
||||
|
@ -1,6 +1,7 @@
|
||||
use std::path::Path;
|
||||
use std::ffi::OsStr;
|
||||
use std::convert::TryFrom;
|
||||
use std::io::{Seek, SeekFrom};
|
||||
|
||||
use anyhow::{bail, format_err, Error};
|
||||
use serde_json::Value;
|
||||
@ -26,6 +27,7 @@ use proxmox::{
|
||||
|
||||
use crate::{
|
||||
task_log,
|
||||
task_warn,
|
||||
task::TaskState,
|
||||
tools::compute_file_csum,
|
||||
api2::types::{
|
||||
@ -65,6 +67,7 @@ use crate::{
|
||||
TAPE_STATUS_DIR,
|
||||
TapeRead,
|
||||
MediaId,
|
||||
MediaSet,
|
||||
MediaCatalog,
|
||||
Inventory,
|
||||
lock_media_set,
|
||||
@ -76,10 +79,12 @@ use crate::{
|
||||
PROXMOX_BACKUP_CONTENT_HEADER_MAGIC_1_0,
|
||||
PROXMOX_BACKUP_CHUNK_ARCHIVE_MAGIC_1_0,
|
||||
PROXMOX_BACKUP_CHUNK_ARCHIVE_MAGIC_1_1,
|
||||
PROXMOX_BACKUP_CATALOG_ARCHIVE_MAGIC_1_0,
|
||||
MediaContentHeader,
|
||||
ChunkArchiveHeader,
|
||||
ChunkArchiveDecoder,
|
||||
SnapshotArchiveHeader,
|
||||
CatalogArchiveHeader,
|
||||
},
|
||||
drive::{
|
||||
TapeDriver,
|
||||
@ -661,3 +666,137 @@ fn try_restore_snapshot_archive<R: pxar::decoder::SeqRead>(
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Try to restore media catalogs (form catalog_archives)
|
||||
pub fn fast_catalog_restore(
|
||||
worker: &WorkerTask,
|
||||
drive: &mut Box<dyn TapeDriver>,
|
||||
media_set: &MediaSet,
|
||||
uuid: &Uuid, // current media Uuid
|
||||
) -> Result<bool, Error> {
|
||||
|
||||
let status_path = Path::new(TAPE_STATUS_DIR);
|
||||
|
||||
let current_file_number = drive.current_file_number()?;
|
||||
if current_file_number != 2 {
|
||||
bail!("fast_catalog_restore: wrong media position - internal error");
|
||||
}
|
||||
|
||||
let mut found_catalog = false;
|
||||
|
||||
let mut moved_to_eom = false;
|
||||
|
||||
loop {
|
||||
let current_file_number = drive.current_file_number()?;
|
||||
|
||||
{ // limit reader scope
|
||||
let mut reader = match drive.read_next_file()? {
|
||||
None => {
|
||||
task_log!(worker, "detected EOT after {} files", current_file_number);
|
||||
break;
|
||||
}
|
||||
Some(reader) => reader,
|
||||
};
|
||||
|
||||
let header: MediaContentHeader = unsafe { reader.read_le_value()? };
|
||||
if header.magic != PROXMOX_BACKUP_CONTENT_HEADER_MAGIC_1_0 {
|
||||
bail!("missing MediaContentHeader");
|
||||
}
|
||||
|
||||
if header.content_magic == PROXMOX_BACKUP_CATALOG_ARCHIVE_MAGIC_1_0 {
|
||||
task_log!(worker, "found catalog at pos {}", current_file_number);
|
||||
|
||||
let header_data = reader.read_exact_allocated(header.size as usize)?;
|
||||
|
||||
let archive_header: CatalogArchiveHeader = serde_json::from_slice(&header_data)
|
||||
.map_err(|err| format_err!("unable to parse catalog archive header - {}", err))?;
|
||||
|
||||
if &archive_header.media_set_uuid != media_set.uuid() {
|
||||
task_log!(worker, "skipping unrelated catalog at pos {}", current_file_number);
|
||||
reader.skip_to_end()?; // read all data
|
||||
continue;
|
||||
}
|
||||
|
||||
let catalog_uuid = &archive_header.uuid;
|
||||
|
||||
let wanted = media_set
|
||||
.media_list()
|
||||
.iter()
|
||||
.find(|e| {
|
||||
match e {
|
||||
None => false,
|
||||
Some(uuid) => uuid == catalog_uuid,
|
||||
}
|
||||
})
|
||||
.is_some();
|
||||
|
||||
if !wanted {
|
||||
task_log!(worker, "skip catalog because media '{}' not inventarized", catalog_uuid);
|
||||
reader.skip_to_end()?; // read all data
|
||||
continue;
|
||||
}
|
||||
|
||||
if catalog_uuid == uuid {
|
||||
// always restore and overwrite catalog
|
||||
} else {
|
||||
// only restore if catalog does not exist
|
||||
if MediaCatalog::exists(status_path, catalog_uuid) {
|
||||
task_log!(worker, "catalog for media '{}' already exists", catalog_uuid);
|
||||
reader.skip_to_end()?; // read all data
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
let mut file = MediaCatalog::create_temporary_database_file(status_path, catalog_uuid)?;
|
||||
|
||||
std::io::copy(&mut reader, &mut file)?;
|
||||
|
||||
file.seek(SeekFrom::Start(0))?;
|
||||
|
||||
match MediaCatalog::parse_catalog_header(&mut file)? {
|
||||
(true, Some(media_uuid), Some(media_set_uuid)) => {
|
||||
if &media_uuid != catalog_uuid {
|
||||
task_log!(worker, "catalog uuid missmatch at pos {}", current_file_number);
|
||||
continue;
|
||||
}
|
||||
if media_set_uuid != archive_header.media_set_uuid {
|
||||
task_log!(worker, "catalog media_set missmatch at pos {}", current_file_number);
|
||||
continue;
|
||||
}
|
||||
|
||||
MediaCatalog::finish_temporary_database(status_path, &media_uuid, true)?;
|
||||
|
||||
if catalog_uuid == uuid {
|
||||
task_log!(worker, "successfully restored catalog");
|
||||
found_catalog = true
|
||||
} else {
|
||||
task_log!(worker, "successfully restored related catalog {}", media_uuid);
|
||||
}
|
||||
}
|
||||
_ => {
|
||||
task_warn!(worker, "got incomplete catalog header - skip file");
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
if moved_to_eom {
|
||||
break; // already done - stop
|
||||
}
|
||||
moved_to_eom = true;
|
||||
|
||||
task_log!(worker, "searching for catalog at EOT (moving to EOT)");
|
||||
drive.move_to_last_file()?;
|
||||
|
||||
let new_file_number = drive.current_file_number()?;
|
||||
|
||||
if new_file_number < (current_file_number + 1) {
|
||||
break; // no new content - stop
|
||||
}
|
||||
}
|
||||
|
||||
Ok(found_catalog)
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user