-use failure::*;
+use anyhow::{bail, format_err, Error};
use futures::*;
use hyper::header::{HeaderValue, UPGRADE};
use hyper::http::request::Parts;
use hyper::{Body, Response, StatusCode};
use serde_json::{json, Value};
-use proxmox::{sortable, identity};
-use proxmox::api::list_subdirs_api_method;
-use proxmox::api::{ApiFuture, ApiHandler, ApiMethod, Router, RpcEnvironment};
+use proxmox::{sortable, identity, list_subdirs_api_method};
+use proxmox::api::{ApiResponseFuture, ApiHandler, ApiMethod, Router, RpcEnvironment, Permission};
use proxmox::api::router::SubdirMap;
use proxmox::api::schema::*;
-use crate::tools;
-use crate::tools::wrapped_reader_stream::*;
+use crate::tools::{self, WrappedReaderStream};
use crate::server::{WorkerTask, H2Service};
use crate::backup::*;
use crate::api2::types::*;
+use crate::config::acl::PRIV_DATASTORE_CREATE_BACKUP;
+use crate::config::cached_user_info::CachedUserInfo;
mod environment;
use environment::*;
("debug", true, &BooleanSchema::new("Enable verbose debug logging.").schema()),
]),
)
+).access(
+ // Note: parameter 'store' is no uri parameter, so we need to test inside function body
+ Some("The user needs Datastore.CreateBackup privilege on /datastore/{store}."),
+ &Permission::Anybody
);
fn upgrade_to_backup_protocol(
param: Value,
_info: &ApiMethod,
rpcenv: Box<dyn RpcEnvironment>,
-) -> ApiFuture {
+) -> ApiResponseFuture {
async move {
let debug = param["debug"].as_bool().unwrap_or(false);
+ let username = rpcenv.get_user().unwrap();
+
let store = tools::required_string_param(¶m, "store")?.to_owned();
+
+ let user_info = CachedUserInfo::new()?;
+ user_info.check_privs(&username, &["datastore", &store], PRIV_DATASTORE_CREATE_BACKUP, false)?;
+
let datastore = DataStore::lookup_datastore(&store)?;
let backup_type = tools::required_string_param(¶m, "backup-type")?;
let worker_id = format!("{}_{}_{}", store, backup_type, backup_id);
- let username = rpcenv.get_user().unwrap();
let env_type = rpcenv.env_type();
let backup_group = BackupGroup::new(backup_type, backup_id);
let abort_future = worker.abort_future();
let env2 = env.clone();
- let env3 = env.clone();
- let req_fut = req_body
+ let mut req_fut = req_body
.on_upgrade()
.map_err(Error::from)
.and_then(move |conn| {
- env3.debug("protocol upgrade done");
+ env2.debug("protocol upgrade done");
let mut http = hyper::server::conn::Http::new();
http.http2_only(true);
http.serve_connection(conn, service)
.map_err(Error::from)
});
- let abort_future = abort_future
+ let mut abort_future = abort_future
.map(|_| Err(format_err!("task aborted")));
- use futures::future::Either;
- future::select(req_fut, abort_future)
- .map(|res| match res {
- Either::Left((Ok(res), _)) => Ok(res),
- Either::Left((Err(err), _)) => Err(err),
- Either::Right((Ok(res), _)) => Ok(res),
- Either::Right((Err(err), _)) => Err(err),
- })
- .and_then(move |_result| async move {
- env.ensure_finished()?;
- env.log("backup finished sucessfully");
- Ok(())
- })
- .then(move |result| async move {
- if let Err(err) = result {
- match env2.ensure_finished() {
- Ok(()) => {}, // ignore error after finish
- _ => {
- env2.log(format!("backup failed: {}", err));
- env2.log("removing failed backup");
- env2.remove_backup()?;
- return Err(err);
- }
- }
- }
- Ok(())
- })
+ async move {
+ let res = select!{
+ req = req_fut => req,
+ abrt = abort_future => abrt,
+ };
+
+ match (res, env.ensure_finished()) {
+ (Ok(_), Ok(())) => {
+ env.log("backup finished sucessfully");
+ Ok(())
+ },
+ (Err(err), Ok(())) => {
+ // ignore errors after finish
+ env.log(format!("backup had errors but finished: {}", err));
+ Ok(())
+ },
+ (Ok(_), Err(err)) => {
+ env.log(format!("backup ended and finish failed: {}", err));
+ env.log("removing unfinished backup");
+ env.remove_backup()?;
+ Err(err)
+ },
+ (Err(err), Err(_)) => {
+ env.log(format!("backup failed: {}", err));
+ env.log("removing failed backup");
+ env.remove_backup()?;
+ Err(err)
+ },
+ }
+ }
})?;
let response = Response::builder()
param: Value,
_info: &ApiMethod,
rpcenv: Box<dyn RpcEnvironment>,
-) -> ApiFuture {
+) -> ApiResponseFuture {
async move {
let env: &BackupEnvironment = rpcenv.as_ref();
param: Value,
_info: &ApiMethod,
rpcenv: Box<dyn RpcEnvironment>,
-) -> ApiFuture {
+) -> ApiResponseFuture {
async move {
let env: &BackupEnvironment = rpcenv.as_ref();