src/api2/admin/datastore/backup.rs: implement finish_backup
This commit is contained in:
		@ -89,11 +89,11 @@ fn upgrade_to_backup_protocol(
 | 
			
		||||
 | 
			
		||||
        env.log(format!("starting new backup on datastore '{}': {:?}", store, path));
 | 
			
		||||
 | 
			
		||||
        let service = BackupService::new(env, worker.clone());
 | 
			
		||||
        let service = BackupService::new(env.clone(), worker.clone());
 | 
			
		||||
 | 
			
		||||
        let abort_future = worker.abort_future();
 | 
			
		||||
 | 
			
		||||
        let worker2 = worker.clone();
 | 
			
		||||
        let env2 = env.clone();
 | 
			
		||||
 | 
			
		||||
        req_body
 | 
			
		||||
            .on_upgrade()
 | 
			
		||||
@ -108,12 +108,20 @@ fn upgrade_to_backup_protocol(
 | 
			
		||||
                    .map_err(Error::from)
 | 
			
		||||
             })
 | 
			
		||||
            .select(abort_future.map_err(|_| {}).then(move |_| { bail!("task aborted"); }))
 | 
			
		||||
            .map_err(|(err, _)| err)
 | 
			
		||||
            .and_then(move |(_result, _)| {
 | 
			
		||||
                worker2.log("backup finished sucessfully");
 | 
			
		||||
                env.ensure_finished()?;
 | 
			
		||||
                env.log("backup finished sucessfully");
 | 
			
		||||
                Ok(())
 | 
			
		||||
            })
 | 
			
		||||
            .map_err(|(err, _)| {
 | 
			
		||||
                err
 | 
			
		||||
            .then(move |result| {
 | 
			
		||||
                if let Err(err) = result {
 | 
			
		||||
                    env2.log(format!("backup failed: {}", err));
 | 
			
		||||
                    env2.log("removing failed backup");
 | 
			
		||||
                    env2.remove_backup()?;
 | 
			
		||||
                    return Err(err);
 | 
			
		||||
                }
 | 
			
		||||
                Ok(())
 | 
			
		||||
            })
 | 
			
		||||
    })?;
 | 
			
		||||
 | 
			
		||||
@ -157,6 +165,15 @@ fn backup_api() -> Router {
 | 
			
		||||
            "dynamic_close", Router::new()
 | 
			
		||||
                .post(api_method_close_dynamic_index())
 | 
			
		||||
        )
 | 
			
		||||
        .subdir(
 | 
			
		||||
            "finish", Router::new()
 | 
			
		||||
                .get(
 | 
			
		||||
                    ApiMethod::new(
 | 
			
		||||
                        finish_backup,
 | 
			
		||||
                        ObjectSchema::new("Mark backup as finished.")
 | 
			
		||||
                    )
 | 
			
		||||
                )
 | 
			
		||||
        )
 | 
			
		||||
        .subdir("test1", test1)
 | 
			
		||||
        .subdir("test2", test2)
 | 
			
		||||
        .list_subdirs();
 | 
			
		||||
@ -191,7 +208,6 @@ fn create_dynamic_index(
 | 
			
		||||
) -> Result<Value, Error> {
 | 
			
		||||
 | 
			
		||||
    let env: &BackupEnvironment = rpcenv.as_ref();
 | 
			
		||||
    env.log("Inside create_dynamic_index");
 | 
			
		||||
 | 
			
		||||
    let mut archive_name = tools::required_string_param(¶m, "archive-name")?.to_owned();
 | 
			
		||||
 | 
			
		||||
@ -207,11 +223,10 @@ fn create_dynamic_index(
 | 
			
		||||
    let chunk_size = 4096*1024; // todo: ??
 | 
			
		||||
 | 
			
		||||
    let index = env.datastore.create_dynamic_writer(&path, chunk_size)?;
 | 
			
		||||
    let wid = env.register_dynamic_writer(index);
 | 
			
		||||
    let wid = env.register_dynamic_writer(index)?;
 | 
			
		||||
 | 
			
		||||
    env.log(format!("created new dynamic index {} ({:?})", wid, path));
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    Ok(json!(wid))
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -244,6 +259,18 @@ fn close_dynamic_index (
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
fn finish_backup (
 | 
			
		||||
    _param: Value,
 | 
			
		||||
    _info: &ApiMethod,
 | 
			
		||||
    rpcenv: &mut RpcEnvironment,
 | 
			
		||||
) -> Result<Value, Error> {
 | 
			
		||||
 | 
			
		||||
    let env: &BackupEnvironment = rpcenv.as_ref();
 | 
			
		||||
 | 
			
		||||
    env.finish_backup()?;
 | 
			
		||||
 | 
			
		||||
    Ok(Value::Null)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
fn test1_get (
 | 
			
		||||
    _param: Value,
 | 
			
		||||
@ -268,7 +295,6 @@ fn dynamic_chunk_index(
 | 
			
		||||
) -> Result<BoxFut, Error> {
 | 
			
		||||
 | 
			
		||||
    let env: &BackupEnvironment = rpcenv.as_ref();
 | 
			
		||||
    env.log("Inside dynamic_chunk_index");
 | 
			
		||||
 | 
			
		||||
    let mut archive_name = tools::required_string_param(¶m, "archive-name")?.to_owned();
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -11,10 +11,29 @@ use crate::server::formatter::*;
 | 
			
		||||
use hyper::{Body, Response};
 | 
			
		||||
 | 
			
		||||
struct SharedBackupState {
 | 
			
		||||
    finished: bool,
 | 
			
		||||
    uid_counter: usize,
 | 
			
		||||
    dynamic_writers: HashMap<usize, (u64 /* offset */, DynamicIndexWriter)>,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl SharedBackupState {
 | 
			
		||||
 | 
			
		||||
    // Raise error if finished flag is set
 | 
			
		||||
    fn ensure_unfinished(&self) -> Result<(), Error> {
 | 
			
		||||
        if self.finished {
 | 
			
		||||
            bail!("backup already marked as finished.");
 | 
			
		||||
        }
 | 
			
		||||
        Ok(())
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // Get an unique integer ID
 | 
			
		||||
    pub fn next_uid(&mut self) -> usize {
 | 
			
		||||
        self.uid_counter += 1;
 | 
			
		||||
        self.uid_counter
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
/// `RpcEnvironmet` implementation for backup service
 | 
			
		||||
#[derive(Clone)]
 | 
			
		||||
pub struct BackupEnvironment {
 | 
			
		||||
@ -39,6 +58,7 @@ impl BackupEnvironment {
 | 
			
		||||
    ) -> Self {
 | 
			
		||||
 | 
			
		||||
        let state = SharedBackupState {
 | 
			
		||||
            finished: false,
 | 
			
		||||
            uid_counter: 0,
 | 
			
		||||
            dynamic_writers: HashMap::new(),
 | 
			
		||||
        };
 | 
			
		||||
@ -56,27 +76,25 @@ impl BackupEnvironment {
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// Get an unique integer ID
 | 
			
		||||
    pub fn next_uid(&self) -> usize {
 | 
			
		||||
        let mut state = self.state.lock().unwrap();
 | 
			
		||||
        state.uid_counter += 1;
 | 
			
		||||
        state.uid_counter
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// Store the writer with an unique ID
 | 
			
		||||
    pub fn register_dynamic_writer(&self, writer: DynamicIndexWriter) -> usize {
 | 
			
		||||
       let mut state = self.state.lock().unwrap();
 | 
			
		||||
        state.uid_counter += 1;
 | 
			
		||||
        let uid = state.uid_counter;
 | 
			
		||||
    pub fn register_dynamic_writer(&self, writer: DynamicIndexWriter) -> Result<usize, Error> {
 | 
			
		||||
        let mut state = self.state.lock().unwrap();
 | 
			
		||||
 | 
			
		||||
        state.ensure_unfinished()?;
 | 
			
		||||
 | 
			
		||||
        let uid = state.next_uid();
 | 
			
		||||
 | 
			
		||||
        state.dynamic_writers.insert(uid, (0, writer));
 | 
			
		||||
        uid
 | 
			
		||||
 | 
			
		||||
        Ok(uid)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// Append chunk to dynamic writer
 | 
			
		||||
    pub fn dynamic_writer_append_chunk(&self, wid: usize, size: u64, digest: &[u8; 32]) -> Result<(), Error> {
 | 
			
		||||
        let mut state = self.state.lock().unwrap();
 | 
			
		||||
 | 
			
		||||
        state.ensure_unfinished()?;
 | 
			
		||||
 | 
			
		||||
        let mut data = match state.dynamic_writers.get_mut(&wid) {
 | 
			
		||||
            Some(data) => data,
 | 
			
		||||
            None => bail!("dynamic writer '{}' not registered", wid),
 | 
			
		||||
@ -93,6 +111,8 @@ impl BackupEnvironment {
 | 
			
		||||
    pub fn dynamic_writer_close(&self, wid: usize) -> Result<(), Error> {
 | 
			
		||||
        let mut state = self.state.lock().unwrap();
 | 
			
		||||
 | 
			
		||||
        state.ensure_unfinished()?;
 | 
			
		||||
 | 
			
		||||
        let mut data = match state.dynamic_writers.remove(&wid) {
 | 
			
		||||
            Some(data) => data,
 | 
			
		||||
            None => bail!("dynamic writer '{}' not registered", wid),
 | 
			
		||||
@ -103,6 +123,22 @@ impl BackupEnvironment {
 | 
			
		||||
        Ok(())
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// Mark backup as finished
 | 
			
		||||
    pub fn finish_backup(&self) -> Result<(), Error> {
 | 
			
		||||
        let mut state = self.state.lock().unwrap();
 | 
			
		||||
        // test if all writer are correctly closed
 | 
			
		||||
 | 
			
		||||
        state.ensure_unfinished()?;
 | 
			
		||||
 | 
			
		||||
        state.finished = true;
 | 
			
		||||
 | 
			
		||||
        if state.dynamic_writers.len() != 0 {
 | 
			
		||||
            bail!("found open index writer - unable to finish backup");
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        Ok(())
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    pub fn log<S: AsRef<str>>(&self, msg: S) {
 | 
			
		||||
        self.worker.log(msg);
 | 
			
		||||
    }
 | 
			
		||||
@ -113,6 +149,25 @@ impl BackupEnvironment {
 | 
			
		||||
            Err(err) => (self.formatter.format_error)(err),
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// Raise error if finished flag is not set
 | 
			
		||||
    pub fn ensure_finished(&self) -> Result<(), Error> {
 | 
			
		||||
        let state = self.state.lock().unwrap();
 | 
			
		||||
        if !state.finished {
 | 
			
		||||
            bail!("backup ended but finished flag is not set.");
 | 
			
		||||
        }
 | 
			
		||||
        Ok(())
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// Remove complete backup
 | 
			
		||||
    pub fn remove_backup(&self) -> Result<(), Error> {
 | 
			
		||||
        let mut state = self.state.lock().unwrap();
 | 
			
		||||
        state.finished = true;
 | 
			
		||||
 | 
			
		||||
        self.datastore.remove_backup_dir(&self.backup_dir)?;
 | 
			
		||||
 | 
			
		||||
        Ok(())
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl RpcEnvironment for BackupEnvironment {
 | 
			
		||||
 | 
			
		||||
		Reference in New Issue
	
	Block a user