]> git.proxmox.com Git - proxmox-backup.git/blob - src/api3/admin/datastore/upload_catar.rs
backup/datastore.rs: try to create useful directory layout
[proxmox-backup.git] / src / api3 / admin / datastore / upload_catar.rs
1 use failure::*;
2
3 use crate::tools;
4 use crate::backup::datastore::*;
5 use crate::backup::archive_index::*;
6 //use crate::server::rest::*;
7 use crate::api::schema::*;
8 use crate::api::router::*;
9
10 use serde_json::Value;
11 use std::io::Write;
12 use futures::*;
13 use std::path::PathBuf;
14 use std::sync::Arc;
15
16 use hyper::Body;
17 use hyper::http::request::Parts;
18
19 pub struct UploadCaTar {
20 stream: Body,
21 index: ArchiveIndexWriter,
22 count: usize,
23 }
24
25 impl Future for UploadCaTar {
26 type Item = ();
27 type Error = failure::Error;
28
29 fn poll(&mut self) -> Poll<(), failure::Error> {
30 loop {
31 match try_ready!(self.stream.poll()) {
32 Some(chunk) => {
33 self.count += chunk.len();
34 if let Err(err) = self.index.write(&chunk) {
35 bail!("writing chunk failed - {}", err);
36 }
37 }
38 None => {
39 self.index.close()?;
40 return Ok(Async::Ready(()))
41 }
42 }
43 }
44 }
45 }
46
47 fn upload_catar(parts: Parts, req_body: Body, param: Value, _info: &ApiUploadMethod) -> Result<BoxFut, Error> {
48
49 let store = tools::required_string_param(&param, "store")?;
50 let archive_name = tools::required_string_param(&param, "archive_name")?;
51
52 let backup_type = tools::required_string_param(&param, "type")?;
53 let backup_id = tools::required_string_param(&param, "id")?;
54 let backup_time = tools::required_integer_param(&param, "time")?;
55
56 println!("Upload {}.catar to {} ({}/{}/{}/{}.aidx)", archive_name, store,
57 backup_type, backup_id, backup_time, archive_name);
58
59 let content_type = parts.headers.get(http::header::CONTENT_TYPE)
60 .ok_or(format_err!("missing content-type header"))?;
61
62 if content_type != "application/x-proxmox-backup-catar" {
63 bail!("got wrong content-type for catar archive upload");
64 }
65
66 let chunk_size = 4*1024*1024;
67
68 let datastore = DataStore::lookup_datastore(store)?;
69
70 let mut path = datastore.create_backup_dir(backup_type, backup_id, backup_time)?;
71
72 let mut full_archive_name = PathBuf::from(archive_name);
73 full_archive_name.set_extension("aidx");
74
75 path.push(full_archive_name);
76
77 let index = datastore.create_archive_writer(path, chunk_size).unwrap();
78
79 let upload = UploadCaTar { stream: req_body, index, count: 0};
80
81 let resp = upload.and_then(|_| {
82
83 let response = http::Response::builder()
84 .status(200)
85 .body(hyper::Body::empty())
86 .unwrap();
87
88 Ok(response)
89 });
90
91 Ok(Box::new(resp))
92 }
93
94 pub fn api_method_upload_catar() -> ApiUploadMethod {
95 ApiUploadMethod::new(
96 upload_catar,
97 ObjectSchema::new("Upload .catar backup file.")
98 .required("store", StringSchema::new("Datastore name."))
99 .required("archive_name", StringSchema::new("Backup archive name."))
100 .required("type", StringSchema::new("Backup type.")
101 .format(Arc::new(ApiStringFormat::Enum(vec!["ct".into(), "host".into()]))))
102 .required("id", StringSchema::new("Backup ID."))
103 .required("time", IntegerSchema::new("Backup time (Unix epoch.)")
104 .minimum(1547797308))
105
106 )
107 }