tape: add basic restore api/command
This commit is contained in:
parent
b4772d1c43
commit
b9b4b31284
@ -13,6 +13,7 @@ pub const SUBDIRS: SubdirMap = &[
|
||||
("changer", &changer::ROUTER),
|
||||
("drive", &drive::ROUTER),
|
||||
("media", &media::ROUTER),
|
||||
("restore", &restore::ROUTER),
|
||||
];
|
||||
|
||||
pub const ROUTER: Router = Router::new()
|
||||
|
@ -3,8 +3,16 @@ use std::ffi::OsStr;
|
||||
use std::convert::TryFrom;
|
||||
|
||||
use anyhow::{bail, format_err, Error};
|
||||
use serde_json::Value;
|
||||
|
||||
use proxmox::{
|
||||
api::{
|
||||
api,
|
||||
RpcEnvironment,
|
||||
RpcEnvironmentType,
|
||||
Router,
|
||||
section_config::SectionConfigData,
|
||||
},
|
||||
tools::{
|
||||
Uuid,
|
||||
io::ReadExt,
|
||||
@ -13,12 +21,20 @@ use proxmox::{
|
||||
CreateOptions,
|
||||
},
|
||||
},
|
||||
api::section_config::SectionConfigData,
|
||||
};
|
||||
|
||||
use crate::{
|
||||
tools::compute_file_csum,
|
||||
api2::types::Authid,
|
||||
api2::types::{
|
||||
DATASTORE_SCHEMA,
|
||||
UPID_SCHEMA,
|
||||
Authid,
|
||||
MediaPoolConfig,
|
||||
},
|
||||
config::{
|
||||
self,
|
||||
drive::check_drive_exists,
|
||||
},
|
||||
backup::{
|
||||
archive_type,
|
||||
MANIFEST_BLOB_NAME,
|
||||
@ -40,6 +56,8 @@ use crate::{
|
||||
MediaCatalog,
|
||||
ChunkArchiveDecoder,
|
||||
TapeDriver,
|
||||
MediaPool,
|
||||
Inventory,
|
||||
request_and_load_media,
|
||||
file_formats::{
|
||||
PROXMOX_BACKUP_MEDIA_LABEL_MAGIC_1_0,
|
||||
@ -52,6 +70,112 @@ use crate::{
|
||||
},
|
||||
};
|
||||
|
||||
pub const ROUTER: Router = Router::new()
|
||||
.post(&API_METHOD_RESTORE);
|
||||
|
||||
|
||||
#[api(
|
||||
input: {
|
||||
properties: {
|
||||
store: {
|
||||
schema: DATASTORE_SCHEMA,
|
||||
},
|
||||
"media-set": {
|
||||
description: "Media set UUID.",
|
||||
type: String,
|
||||
},
|
||||
},
|
||||
},
|
||||
returns: {
|
||||
schema: UPID_SCHEMA,
|
||||
},
|
||||
)]
|
||||
/// Restore data from media-set
|
||||
pub fn restore(
|
||||
store: String,
|
||||
media_set: String,
|
||||
rpcenv: &mut dyn RpcEnvironment,
|
||||
) -> Result<Value, Error> {
|
||||
|
||||
let datastore = DataStore::lookup_datastore(&store)?;
|
||||
|
||||
let auth_id: Authid = rpcenv.get_auth_id().unwrap().parse()?;
|
||||
|
||||
let status_path = Path::new(TAPE_STATUS_DIR);
|
||||
let inventory = Inventory::load(status_path)?;
|
||||
|
||||
let media_set_uuid = media_set.parse()?;
|
||||
|
||||
let pool = inventory.lookup_media_set_pool(&media_set_uuid)?;
|
||||
|
||||
let (config, _digest) = config::media_pool::config()?;
|
||||
let pool_config: MediaPoolConfig = config.lookup("pool", &pool)?;
|
||||
|
||||
let (drive_config, _digest) = config::drive::config()?;
|
||||
// early check before starting worker
|
||||
check_drive_exists(&drive_config, &pool_config.drive)?;
|
||||
|
||||
let to_stdout = if rpcenv.env_type() == RpcEnvironmentType::CLI { true } else { false };
|
||||
|
||||
let upid_str = WorkerTask::new_thread(
|
||||
"tape-restore",
|
||||
Some(store.clone()),
|
||||
auth_id.clone(),
|
||||
to_stdout,
|
||||
move |worker| {
|
||||
|
||||
let _lock = MediaPool::lock(status_path, &pool)?;
|
||||
|
||||
let members = inventory.compute_media_set_members(&media_set_uuid)?;
|
||||
|
||||
let media_list = members.media_list().clone();
|
||||
|
||||
let mut media_id_list = Vec::new();
|
||||
|
||||
for (seq_nr, media_uuid) in media_list.iter().enumerate() {
|
||||
match media_uuid {
|
||||
None => {
|
||||
bail!("media set {} is incomplete (missing member {}).", media_set_uuid, seq_nr);
|
||||
}
|
||||
Some(media_uuid) => {
|
||||
media_id_list.push(inventory.lookup_media(media_uuid).unwrap());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let drive = &pool_config.drive;
|
||||
|
||||
worker.log(format!("Restore mediaset '{}'", media_set));
|
||||
worker.log(format!("Pool: {}", pool));
|
||||
worker.log(format!("Datastore: {}", store));
|
||||
worker.log(format!("Drive: {}", drive));
|
||||
worker.log(format!(
|
||||
"Required media list: {}",
|
||||
media_id_list.iter()
|
||||
.map(|media_id| media_id.label.changer_id.as_str())
|
||||
.collect::<Vec<&str>>()
|
||||
.join(";")
|
||||
));
|
||||
|
||||
for media_id in media_id_list.iter() {
|
||||
request_and_restore_media(
|
||||
&worker,
|
||||
media_id,
|
||||
&drive_config,
|
||||
drive,
|
||||
&datastore,
|
||||
&auth_id,
|
||||
)?;
|
||||
}
|
||||
|
||||
worker.log(format!("Restore mediaset '{}' done", media_set));
|
||||
Ok(())
|
||||
}
|
||||
)?;
|
||||
|
||||
Ok(upid_str.into())
|
||||
}
|
||||
|
||||
/// Request and restore complete media without using existing catalog (create catalog instead)
|
||||
pub fn request_and_restore_media(
|
||||
worker: &WorkerTask,
|
||||
|
@ -43,6 +43,7 @@ use proxmox_backup::{
|
||||
tape::{
|
||||
open_drive,
|
||||
complete_media_changer_id,
|
||||
complete_media_set_uuid,
|
||||
file_formats::{
|
||||
PROXMOX_BACKUP_CONTENT_HEADER_MAGIC_1_0,
|
||||
PROXMOX_BACKUP_CONTENT_NAME,
|
||||
@ -631,6 +632,36 @@ async fn backup(
|
||||
|
||||
Ok(())
|
||||
}
|
||||
#[api(
|
||||
input: {
|
||||
properties: {
|
||||
store: {
|
||||
schema: DATASTORE_SCHEMA,
|
||||
},
|
||||
"media-set": {
|
||||
description: "Media set UUID.",
|
||||
type: String,
|
||||
},
|
||||
},
|
||||
},
|
||||
)]
|
||||
/// Restore data from media-set
|
||||
async fn restore(
|
||||
param: Value,
|
||||
rpcenv: &mut dyn RpcEnvironment,
|
||||
) -> Result<(), Error> {
|
||||
|
||||
let info = &api2::tape::restore::API_METHOD_RESTORE;
|
||||
|
||||
let result = match info.handler {
|
||||
ApiHandler::Sync(handler) => (handler)(param, info, rpcenv)?,
|
||||
_ => unreachable!(),
|
||||
};
|
||||
|
||||
wait_for_local_worker(result.as_str().unwrap()).await?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[api(
|
||||
input: {
|
||||
@ -688,6 +719,13 @@ fn main() {
|
||||
.completion_cb("store", complete_datastore_name)
|
||||
.completion_cb("pool", complete_pool_name)
|
||||
)
|
||||
.insert(
|
||||
"restore",
|
||||
CliCommand::new(&API_METHOD_RESTORE)
|
||||
.arg_param(&["media-set", "store"])
|
||||
.completion_cb("store", complete_datastore_name)
|
||||
.completion_cb("media-set", complete_media_set_uuid)
|
||||
)
|
||||
.insert(
|
||||
"barcode-label",
|
||||
CliCommand::new(&API_METHOD_BARCODE_LABEL_MEDIA)
|
||||
|
Loading…
Reference in New Issue
Block a user