tape: implement fast catalog restore
This commit is contained in:
parent
c6f55139f8
commit
074503f288
@ -48,7 +48,10 @@ use crate::{
|
|||||||
MamAttribute,
|
MamAttribute,
|
||||||
LinuxDriveAndMediaStatus,
|
LinuxDriveAndMediaStatus,
|
||||||
},
|
},
|
||||||
tape::restore::restore_media,
|
tape::restore::{
|
||||||
|
fast_catalog_restore,
|
||||||
|
restore_media,
|
||||||
|
},
|
||||||
},
|
},
|
||||||
server::WorkerTask,
|
server::WorkerTask,
|
||||||
tape::{
|
tape::{
|
||||||
@ -1279,7 +1282,7 @@ pub fn catalog_media(
|
|||||||
|
|
||||||
let mut inventory = Inventory::new(status_path);
|
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 => {
|
None => {
|
||||||
worker.log("media is empty");
|
worker.log("media is empty");
|
||||||
let _lock = lock_unassigned_media_pool(status_path)?;
|
let _lock = lock_unassigned_media_pool(status_path)?;
|
||||||
@ -1307,7 +1310,7 @@ pub fn catalog_media(
|
|||||||
|
|
||||||
inventory.store(media_id.clone(), false)?;
|
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)");
|
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)?;
|
restore_media(&worker, &mut drive, &media_id, None, verbose)?;
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
use std::path::Path;
|
use std::path::Path;
|
||||||
use std::ffi::OsStr;
|
use std::ffi::OsStr;
|
||||||
use std::convert::TryFrom;
|
use std::convert::TryFrom;
|
||||||
|
use std::io::{Seek, SeekFrom};
|
||||||
|
|
||||||
use anyhow::{bail, format_err, Error};
|
use anyhow::{bail, format_err, Error};
|
||||||
use serde_json::Value;
|
use serde_json::Value;
|
||||||
@ -26,6 +27,7 @@ use proxmox::{
|
|||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
task_log,
|
task_log,
|
||||||
|
task_warn,
|
||||||
task::TaskState,
|
task::TaskState,
|
||||||
tools::compute_file_csum,
|
tools::compute_file_csum,
|
||||||
api2::types::{
|
api2::types::{
|
||||||
@ -65,6 +67,7 @@ use crate::{
|
|||||||
TAPE_STATUS_DIR,
|
TAPE_STATUS_DIR,
|
||||||
TapeRead,
|
TapeRead,
|
||||||
MediaId,
|
MediaId,
|
||||||
|
MediaSet,
|
||||||
MediaCatalog,
|
MediaCatalog,
|
||||||
Inventory,
|
Inventory,
|
||||||
lock_media_set,
|
lock_media_set,
|
||||||
@ -76,10 +79,12 @@ use crate::{
|
|||||||
PROXMOX_BACKUP_CONTENT_HEADER_MAGIC_1_0,
|
PROXMOX_BACKUP_CONTENT_HEADER_MAGIC_1_0,
|
||||||
PROXMOX_BACKUP_CHUNK_ARCHIVE_MAGIC_1_0,
|
PROXMOX_BACKUP_CHUNK_ARCHIVE_MAGIC_1_0,
|
||||||
PROXMOX_BACKUP_CHUNK_ARCHIVE_MAGIC_1_1,
|
PROXMOX_BACKUP_CHUNK_ARCHIVE_MAGIC_1_1,
|
||||||
|
PROXMOX_BACKUP_CATALOG_ARCHIVE_MAGIC_1_0,
|
||||||
MediaContentHeader,
|
MediaContentHeader,
|
||||||
ChunkArchiveHeader,
|
ChunkArchiveHeader,
|
||||||
ChunkArchiveDecoder,
|
ChunkArchiveDecoder,
|
||||||
SnapshotArchiveHeader,
|
SnapshotArchiveHeader,
|
||||||
|
CatalogArchiveHeader,
|
||||||
},
|
},
|
||||||
drive::{
|
drive::{
|
||||||
TapeDriver,
|
TapeDriver,
|
||||||
@ -661,3 +666,137 @@ fn try_restore_snapshot_archive<R: pxar::decoder::SeqRead>(
|
|||||||
|
|
||||||
Ok(())
|
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