4 use crate::tools
::wrapped_reader_stream
::*;
5 use crate::backup
::datastore
::*;
6 use crate::backup
::dynamic_index
::*;
7 //use crate::server::rest::*;
8 use crate::api
::schema
::*;
9 use crate::api
::router
::*;
11 use chrono
::{Utc, TimeZone}
;
13 use serde_json
::Value
;
16 use std
::path
::PathBuf
;
20 use hyper
::http
::request
::Parts
;
22 pub struct UploadCaTar
{
24 index
: DynamicIndexWriter
,
28 impl Future
for UploadCaTar
{
30 type Error
= failure
::Error
;
32 fn poll(&mut self) -> Poll
<(), failure
::Error
> {
34 match try_ready
!(self.stream
.poll()) {
36 self.count
+= chunk
.len();
37 if let Err(err
) = self.index
.write(&chunk
) {
38 bail
!("writing chunk failed - {}", err
);
43 return Ok(Async
::Ready(()))
54 _info
: &ApiAsyncMethod
,
55 _rpcenv
: &mut RpcEnvironment
,
56 ) -> Result
<BoxFut
, Error
> {
58 let store
= tools
::required_string_param(¶m
, "store")?
;
59 let archive_name
= tools
::required_string_param(¶m
, "archive_name")?
;
61 let backup_type
= tools
::required_string_param(¶m
, "type")?
;
62 let backup_id
= tools
::required_string_param(¶m
, "id")?
;
63 let backup_time
= tools
::required_integer_param(¶m
, "time")?
;
65 println
!("Upload {}.catar to {} ({}/{}/{}/{}.didx)", archive_name
, store
,
66 backup_type
, backup_id
, backup_time
, archive_name
);
68 let content_type
= parts
.headers
.get(http
::header
::CONTENT_TYPE
)
69 .ok_or(format_err
!("missing content-type header"))?
;
71 if content_type
!= "application/x-proxmox-backup-catar" {
72 bail
!("got wrong content-type for catar archive upload");
75 let chunk_size
= 4*1024*1024;
77 let datastore
= DataStore
::lookup_datastore(store
)?
;
79 let mut path
= datastore
.create_backup_dir(backup_type
, backup_id
, backup_time
)?
;
81 let mut full_archive_name
= PathBuf
::from(archive_name
);
82 full_archive_name
.set_extension("didx");
84 path
.push(full_archive_name
);
86 let index
= datastore
.create_dynamic_writer(path
, chunk_size
)?
;
88 let upload
= UploadCaTar { stream: req_body, index, count: 0}
;
90 let resp
= upload
.and_then(|_
| {
92 let response
= http
::Response
::builder()
94 .body(hyper
::Body
::empty())
103 pub fn api_method_upload_catar() -> ApiAsyncMethod
{
106 ObjectSchema
::new("Upload .catar backup file.")
107 .required("store", StringSchema
::new("Datastore name."))
108 .required("archive_name", StringSchema
::new("Backup archive name."))
109 .required("type", StringSchema
::new("Backup type.")
110 .format(Arc
::new(ApiStringFormat
::Enum(vec
!["ct".into(), "host".into()]))))
111 .required("id", StringSchema
::new("Backup ID."))
112 .required("time", IntegerSchema
::new("Backup time (Unix epoch.)")
113 .minimum(1547797308))
122 _info
: &ApiAsyncMethod
,
123 _rpcenv
: &mut RpcEnvironment
,
124 ) -> Result
<BoxFut
, Error
> {
126 let store
= tools
::required_string_param(¶m
, "store")?
;
127 let archive_name
= tools
::required_string_param(¶m
, "archive_name")?
;
129 let backup_type
= tools
::required_string_param(¶m
, "type")?
;
130 let backup_id
= tools
::required_string_param(¶m
, "id")?
;
131 let backup_time
= tools
::required_integer_param(¶m
, "time")?
;
132 let backup_time
= Utc
.timestamp(backup_time
, 0);
134 println
!("Download {}.catar from {} ({}/{}/{}/{}.didx)", archive_name
, store
,
135 backup_type
, backup_id
, backup_time
, archive_name
);
137 let datastore
= DataStore
::lookup_datastore(store
)?
;
139 let mut path
= datastore
.get_backup_dir(backup_type
, backup_id
, backup_time
);
141 let mut full_archive_name
= PathBuf
::from(archive_name
);
142 full_archive_name
.set_extension("didx");
144 path
.push(full_archive_name
);
146 let index
= datastore
.open_dynamic_reader(path
)?
;
147 let reader
= BufferedDynamicReader
::new(index
);
148 let stream
= WrappedReaderStream
::new(reader
);
150 // fixme: set size, content type?
151 let response
= http
::Response
::builder()
153 .body(Body
::wrap_stream(stream
))?
;
155 Ok(Box
::new(future
::ok(response
)))
158 pub fn api_method_download_catar() -> ApiAsyncMethod
{
161 ObjectSchema
::new("Download .catar backup file.")
162 .required("store", StringSchema
::new("Datastore name."))
163 .required("archive_name", StringSchema
::new("Backup archive name."))
164 .required("type", StringSchema
::new("Backup type.")
165 .format(Arc
::new(ApiStringFormat
::Enum(vec
!["ct".into(), "host".into()]))))
166 .required("id", StringSchema
::new("Backup ID."))
167 .required("time", IntegerSchema
::new("Backup time (Unix epoch.)")
168 .minimum(1547797308))