]>
Commit | Line | Data |
---|---|---|
eb506c83 | 1 | //! Sync datastore from remote server |
268687dd | 2 | use std::sync::{Arc}; |
eb506c83 | 3 | |
07ad6470 | 4 | use anyhow::{format_err, Error}; |
de8ec041 DM |
5 | |
6 | use proxmox::api::api; | |
404d78c4 | 7 | use proxmox::api::{ApiMethod, Router, RpcEnvironment, Permission}; |
de8ec041 DM |
8 | |
9 | use crate::server::{WorkerTask}; | |
07ad6470 DM |
10 | use crate::backup::DataStore; |
11 | use crate::client::{HttpClient, HttpClientOptions, BackupRepository, pull::pull_store}; | |
de8ec041 | 12 | use crate::api2::types::*; |
07ad6470 DM |
13 | use crate::config::{ |
14 | remote, | |
15 | acl::{PRIV_DATASTORE_BACKUP, PRIV_DATASTORE_PRUNE, PRIV_REMOTE_READ}, | |
16 | cached_user_info::CachedUserInfo, | |
17 | }; | |
de8ec041 | 18 | |
268687dd DC |
19 | |
20 | pub fn check_pull_privs( | |
21 | username: &str, | |
22 | store: &str, | |
23 | remote: &str, | |
24 | remote_store: &str, | |
25 | delete: bool, | |
26 | ) -> Result<(), Error> { | |
27 | ||
28 | let user_info = CachedUserInfo::new()?; | |
29 | ||
30 | user_info.check_privs(username, &["datastore", store], PRIV_DATASTORE_BACKUP, false)?; | |
31 | user_info.check_privs(username, &["remote", remote, remote_store], PRIV_REMOTE_READ, false)?; | |
32 | ||
33 | if delete { | |
34 | user_info.check_privs(username, &["datastore", store], PRIV_DATASTORE_PRUNE, false)?; | |
35 | } | |
36 | ||
37 | Ok(()) | |
38 | } | |
39 | ||
40 | pub async fn get_pull_parameters( | |
41 | store: &str, | |
42 | remote: &str, | |
43 | remote_store: &str, | |
44 | ) -> Result<(HttpClient, BackupRepository, Arc<DataStore>), Error> { | |
45 | ||
46 | let tgt_store = DataStore::lookup_datastore(store)?; | |
47 | ||
48 | let (remote_config, _digest) = remote::config()?; | |
49 | let remote: remote::Remote = remote_config.lookup("remote", remote)?; | |
50 | ||
51 | let options = HttpClientOptions::new() | |
52 | .password(Some(remote.password.clone())) | |
53 | .fingerprint(remote.fingerprint.clone()); | |
54 | ||
55 | let client = HttpClient::new(&remote.host, &remote.userid, options)?; | |
56 | let _auth_info = client.login() // make sure we can auth | |
57 | .await | |
58 | .map_err(|err| format_err!("remote connection to '{}' failed - {}", remote.host, err))?; | |
59 | ||
60 | let src_repo = BackupRepository::new(Some(remote.userid), Some(remote.host), remote_store.to_string()); | |
61 | ||
62 | Ok((client, src_repo, tgt_store)) | |
63 | } | |
64 | ||
de8ec041 DM |
65 | #[api( |
66 | input: { | |
67 | properties: { | |
68 | store: { | |
69 | schema: DATASTORE_SCHEMA, | |
70 | }, | |
94609e23 DM |
71 | remote: { |
72 | schema: REMOTE_ID_SCHEMA, | |
de8ec041 DM |
73 | }, |
74 | "remote-store": { | |
75 | schema: DATASTORE_SCHEMA, | |
76 | }, | |
b4900286 DM |
77 | "remove-vanished": { |
78 | schema: REMOVE_VANISHED_BACKUPS_SCHEMA, | |
4b4eba0b | 79 | optional: true, |
4b4eba0b | 80 | }, |
de8ec041 DM |
81 | }, |
82 | }, | |
404d78c4 | 83 | access: { |
365f0f72 | 84 | // Note: used parameters are no uri parameters, so we need to test inside function body |
54552dda | 85 | description: r###"The user needs Datastore.Backup privilege on '/datastore/{store}', |
8247db5b | 86 | and needs to own the backup group. Remote.Read is required on '/remote/{remote}/{remote-store}'. |
ec67af9a | 87 | The delete flag additionally requires the Datastore.Prune privilege on '/datastore/{store}'. |
54552dda | 88 | "###, |
365f0f72 | 89 | permission: &Permission::Anybody, |
404d78c4 | 90 | }, |
de8ec041 | 91 | )] |
eb506c83 DM |
92 | /// Sync store from other repository |
93 | async fn pull ( | |
de8ec041 | 94 | store: String, |
94609e23 | 95 | remote: String, |
de8ec041 | 96 | remote_store: String, |
b4900286 | 97 | remove_vanished: Option<bool>, |
de8ec041 DM |
98 | _info: &ApiMethod, |
99 | rpcenv: &mut dyn RpcEnvironment, | |
100 | ) -> Result<String, Error> { | |
101 | ||
102 | let username = rpcenv.get_user().unwrap(); | |
b4900286 | 103 | let delete = remove_vanished.unwrap_or(true); |
4b4eba0b | 104 | |
268687dd | 105 | check_pull_privs(&username, &store, &remote, &remote_store, delete)?; |
de8ec041 | 106 | |
268687dd | 107 | let (client, src_repo, tgt_store) = get_pull_parameters(&store, &remote, &remote_store).await?; |
de8ec041 DM |
108 | |
109 | // fixme: set to_stdout to false? | |
110 | let upid_str = WorkerTask::spawn("sync", Some(store.clone()), &username.clone(), true, move |worker| async move { | |
111 | ||
112 | worker.log(format!("sync datastore '{}' start", store)); | |
113 | ||
54552dda | 114 | pull_store(&worker, &client, &src_repo, tgt_store.clone(), delete, username).await?; |
de8ec041 DM |
115 | |
116 | worker.log(format!("sync datastore '{}' end", store)); | |
117 | ||
118 | Ok(()) | |
119 | })?; | |
120 | ||
121 | Ok(upid_str) | |
122 | } | |
123 | ||
124 | pub const ROUTER: Router = Router::new() | |
eb506c83 | 125 | .post(&API_METHOD_PULL); |