src/bin/proxmox-backup-client.rs: implement repository completions
This commit is contained in:
		@ -21,6 +21,7 @@ use serde_json::{json, Value};
 | 
				
			|||||||
use hyper::Body;
 | 
					use hyper::Body;
 | 
				
			||||||
use std::sync::Arc;
 | 
					use std::sync::Arc;
 | 
				
			||||||
use regex::Regex;
 | 
					use regex::Regex;
 | 
				
			||||||
 | 
					use xdg::BaseDirectories;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
use lazy_static::lazy_static;
 | 
					use lazy_static::lazy_static;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -28,6 +29,81 @@ lazy_static! {
 | 
				
			|||||||
    static ref BACKUPSPEC_REGEX: Regex = Regex::new(r"^([a-zA-Z0-9_-]+\.(?:catar|raw)):(.+)$").unwrap();
 | 
					    static ref BACKUPSPEC_REGEX: Regex = Regex::new(r"^([a-zA-Z0-9_-]+\.(?:catar|raw)):(.+)$").unwrap();
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					fn record_repository(repo: &BackupRepository) {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    let base = match BaseDirectories::with_prefix("proxmox-backup") {
 | 
				
			||||||
 | 
					        Ok(v) => v,
 | 
				
			||||||
 | 
					        _ => return,
 | 
				
			||||||
 | 
					    };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // usually $HOME/.cache/proxmox-backup/repo-list
 | 
				
			||||||
 | 
					    let path = match base.place_cache_file("repo-list") {
 | 
				
			||||||
 | 
					        Ok(v) => v,
 | 
				
			||||||
 | 
					        _ => return,
 | 
				
			||||||
 | 
					    };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    let mut data = tools::file_get_json(&path).unwrap_or(json!({}));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    let repo = repo.to_string();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    data[&repo] = json!{ data[&repo].as_i64().unwrap_or(0) + 1 };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    let mut map = serde_json::map::Map::new();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    loop {
 | 
				
			||||||
 | 
					        let mut max_used = 0;
 | 
				
			||||||
 | 
					        let mut max_repo = None;
 | 
				
			||||||
 | 
					        for (repo, count) in data.as_object().unwrap() {
 | 
				
			||||||
 | 
					            if map.contains_key(repo) { continue; }
 | 
				
			||||||
 | 
					            if let Some(count) = count.as_i64() {
 | 
				
			||||||
 | 
					                if count > max_used {
 | 
				
			||||||
 | 
					                    max_used = count;
 | 
				
			||||||
 | 
					                    max_repo = Some(repo);
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        if let Some(repo) = max_repo {
 | 
				
			||||||
 | 
					            map.insert(repo.to_owned(), json!(max_used));
 | 
				
			||||||
 | 
					        } else {
 | 
				
			||||||
 | 
					            break;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        if map.len() > 10 { // store max. 10 repos
 | 
				
			||||||
 | 
					            break;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    let new_data = json!(map);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    let _ = tools::file_set_contents(path, new_data.to_string().as_bytes(), None);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					fn complete_repository(arg: &str, param: &HashMap<String, String>) -> Vec<String> {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    let mut result = vec![];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    let base = match BaseDirectories::with_prefix("proxmox-backup") {
 | 
				
			||||||
 | 
					        Ok(v) => v,
 | 
				
			||||||
 | 
					        _ => return result,
 | 
				
			||||||
 | 
					    };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // usually $HOME/.cache/proxmox-backup/repo-list
 | 
				
			||||||
 | 
					    let path = match base.place_cache_file("repo-list") {
 | 
				
			||||||
 | 
					        Ok(v) => v,
 | 
				
			||||||
 | 
					        _ => return result,
 | 
				
			||||||
 | 
					    };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    let data = tools::file_get_json(&path).unwrap_or(json!({}));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if let Some(map) = data.as_object() {
 | 
				
			||||||
 | 
					        for (repo, count) in map {
 | 
				
			||||||
 | 
					            result.push(repo.to_owned());
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    result
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
fn backup_directory<P: AsRef<Path>>(
 | 
					fn backup_directory<P: AsRef<Path>>(
 | 
				
			||||||
    client: &mut HttpClient,
 | 
					    client: &mut HttpClient,
 | 
				
			||||||
    repo: &BackupRepository,
 | 
					    repo: &BackupRepository,
 | 
				
			||||||
@ -53,7 +129,7 @@ fn backup_directory<P: AsRef<Path>>(
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
    let query = tools::json_object_to_query(param)?;
 | 
					    let query = tools::json_object_to_query(param)?;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    let path = format!("api2/json/admin/datastore/{}/catar?{}", repo.store, query);
 | 
					    let path = format!("api2/json/admin/datastore/{}/catar?{}", repo.store(), query);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    let stream = CaTarBackupStream::open(dir_path.as_ref(), all_file_systems, verbose)?;
 | 
					    let stream = CaTarBackupStream::open(dir_path.as_ref(), all_file_systems, verbose)?;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -116,12 +192,14 @@ fn list_backups(
 | 
				
			|||||||
    let repo_url = tools::required_string_param(¶m, "repository")?;
 | 
					    let repo_url = tools::required_string_param(¶m, "repository")?;
 | 
				
			||||||
    let repo = BackupRepository::parse(repo_url)?;
 | 
					    let repo = BackupRepository::parse(repo_url)?;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    let mut client = HttpClient::new(&repo.host, &repo.user);
 | 
					    let mut client = HttpClient::new(repo.host(), repo.user());
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    let path = format!("api2/json/admin/datastore/{}/backups", repo.store);
 | 
					    let path = format!("api2/json/admin/datastore/{}/backups", repo.store());
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    let result = client.get(&path)?;
 | 
					    let result = client.get(&path)?;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    record_repository(&repo);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    // fixme: implement and use output formatter instead ..
 | 
					    // fixme: implement and use output formatter instead ..
 | 
				
			||||||
    let list = result["data"].as_array().unwrap();
 | 
					    let list = result["data"].as_array().unwrap();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -155,12 +233,14 @@ fn list_backup_groups(
 | 
				
			|||||||
    let repo_url = tools::required_string_param(¶m, "repository")?;
 | 
					    let repo_url = tools::required_string_param(¶m, "repository")?;
 | 
				
			||||||
    let repo = BackupRepository::parse(repo_url)?;
 | 
					    let repo = BackupRepository::parse(repo_url)?;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    let mut client = HttpClient::new(&repo.host, &repo.user);
 | 
					    let mut client = HttpClient::new(repo.host(), repo.user());
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    let path = format!("api2/json/admin/datastore/{}/groups", repo.store);
 | 
					    let path = format!("api2/json/admin/datastore/{}/groups", repo.store());
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    let mut result = client.get(&path)?;
 | 
					    let mut result = client.get(&path)?;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    record_repository(&repo);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    // fixme: implement and use output formatter instead ..
 | 
					    // fixme: implement and use output formatter instead ..
 | 
				
			||||||
    let list = result["data"].as_array_mut().unwrap();
 | 
					    let list = result["data"].as_array_mut().unwrap();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -218,13 +298,15 @@ fn list_snapshots(
 | 
				
			|||||||
        "backup-id": group.backup_id(),
 | 
					        "backup-id": group.backup_id(),
 | 
				
			||||||
    }))?;
 | 
					    }))?;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    let mut client = HttpClient::new(&repo.host, &repo.user);
 | 
					    let mut client = HttpClient::new(repo.host(), repo.user());
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    let path = format!("api2/json/admin/datastore/{}/snapshots?{}", repo.store, query);
 | 
					    let path = format!("api2/json/admin/datastore/{}/snapshots?{}", repo.store(), query);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    // fixme: params
 | 
					    // fixme: params
 | 
				
			||||||
    let result = client.get(&path)?;
 | 
					    let result = client.get(&path)?;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    record_repository(&repo);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    // fixme: implement and use output formatter instead ..
 | 
					    // fixme: implement and use output formatter instead ..
 | 
				
			||||||
    let list = result["data"].as_array().unwrap();
 | 
					    let list = result["data"].as_array().unwrap();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -265,12 +347,14 @@ fn forget_snapshots(
 | 
				
			|||||||
        "backup-time": snapshot.backup_time().timestamp(),
 | 
					        "backup-time": snapshot.backup_time().timestamp(),
 | 
				
			||||||
    }))?;
 | 
					    }))?;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    let mut client = HttpClient::new(&repo.host, &repo.user);
 | 
					    let mut client = HttpClient::new(repo.host(), repo.user());
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    let path = format!("api2/json/admin/datastore/{}/snapshots?{}", repo.store, query);
 | 
					    let path = format!("api2/json/admin/datastore/{}/snapshots?{}", repo.store(), query);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    let result = client.delete(&path)?;
 | 
					    let result = client.delete(&path)?;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    record_repository(&repo);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    Ok(result)
 | 
					    Ok(result)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -283,12 +367,14 @@ fn start_garbage_collection(
 | 
				
			|||||||
    let repo_url = tools::required_string_param(¶m, "repository")?;
 | 
					    let repo_url = tools::required_string_param(¶m, "repository")?;
 | 
				
			||||||
    let repo = BackupRepository::parse(repo_url)?;
 | 
					    let repo = BackupRepository::parse(repo_url)?;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    let mut client = HttpClient::new(&repo.host, &repo.user);
 | 
					    let mut client = HttpClient::new(repo.host(), repo.user());
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    let path = format!("api2/json/admin/datastore/{}/gc", repo.store);
 | 
					    let path = format!("api2/json/admin/datastore/{}/gc", repo.store());
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    let result = client.post(&path)?;
 | 
					    let result = client.post(&path)?;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    record_repository(&repo);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    Ok(result)
 | 
					    Ok(result)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -356,10 +442,12 @@ fn create_backup(
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
    let backup_time = Local.timestamp(Local::now().timestamp(), 0);
 | 
					    let backup_time = Local.timestamp(Local::now().timestamp(), 0);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    let mut client = HttpClient::new(&repo.host, &repo.user);
 | 
					    let mut client = HttpClient::new(repo.host(), repo.user());
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    client.login()?; // login before starting backup
 | 
					    client.login()?; // login before starting backup
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    record_repository(&repo);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    println!("Starting backup");
 | 
					    println!("Starting backup");
 | 
				
			||||||
    println!("Client name: {}", tools::nodename());
 | 
					    println!("Client name: {}", tools::nodename());
 | 
				
			||||||
    println!("Start Time: {}", backup_time.to_rfc3339());
 | 
					    println!("Start Time: {}", backup_time.to_rfc3339());
 | 
				
			||||||
@ -379,7 +467,7 @@ fn create_backup(
 | 
				
			|||||||
    Ok(Value::Null)
 | 
					    Ok(Value::Null)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
pub fn complete_backup_source(arg: &str, param: &HashMap<String, String>) -> Vec<String> {
 | 
					fn complete_backup_source(arg: &str, param: &HashMap<String, String>) -> Vec<String> {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    let mut result = vec![];
 | 
					    let mut result = vec![];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -411,7 +499,11 @@ fn restore(
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
    let archive_name = tools::required_string_param(¶m, "archive-name")?;
 | 
					    let archive_name = tools::required_string_param(¶m, "archive-name")?;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    let mut client = HttpClient::new(&repo.host, &repo.user);
 | 
					    let mut client = HttpClient::new(repo.host(), repo.user());
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    client.login()?; // login before starting
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    record_repository(&repo);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    let path = tools::required_string_param(¶m, "snapshot")?;
 | 
					    let path = tools::required_string_param(¶m, "snapshot")?;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -425,7 +517,7 @@ fn restore(
 | 
				
			|||||||
            "backup-id": group.backup_id(),
 | 
					            "backup-id": group.backup_id(),
 | 
				
			||||||
        }))?;
 | 
					        }))?;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        let path = format!("api2/json/admin/datastore/{}/snapshots?{}", repo.store, subquery);
 | 
					        let path = format!("api2/json/admin/datastore/{}/snapshots?{}", repo.store(), subquery);
 | 
				
			||||||
        let result = client.get(&path)?;
 | 
					        let result = client.get(&path)?;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        let list = result["data"].as_array().unwrap();
 | 
					        let list = result["data"].as_array().unwrap();
 | 
				
			||||||
@ -453,7 +545,7 @@ fn restore(
 | 
				
			|||||||
    let target = tools::required_string_param(¶m, "target")?;
 | 
					    let target = tools::required_string_param(¶m, "target")?;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    if archive_name.ends_with(".catar") {
 | 
					    if archive_name.ends_with(".catar") {
 | 
				
			||||||
        let path = format!("api2/json/admin/datastore/{}/catar?{}", repo.store, query);
 | 
					        let path = format!("api2/json/admin/datastore/{}/catar?{}", repo.store(), query);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        println!("DOWNLOAD FILE {} to {}", path, target);
 | 
					        println!("DOWNLOAD FILE {} to {}", path, target);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -476,14 +568,16 @@ fn prune(
 | 
				
			|||||||
    let repo_url = tools::required_string_param(¶m, "repository")?;
 | 
					    let repo_url = tools::required_string_param(¶m, "repository")?;
 | 
				
			||||||
    let repo = BackupRepository::parse(repo_url)?;
 | 
					    let repo = BackupRepository::parse(repo_url)?;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    let mut client = HttpClient::new(&repo.host, &repo.user);
 | 
					    let mut client = HttpClient::new(repo.host(), repo.user());
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    let path = format!("api2/json/admin/datastore/{}/prune", repo.store);
 | 
					    let path = format!("api2/json/admin/datastore/{}/prune", repo.store());
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    param.as_object_mut().unwrap().remove("repository");
 | 
					    param.as_object_mut().unwrap().remove("repository");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    let result = client.post_json(&path, param)?;
 | 
					    let result = client.post_json(&path, param)?;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    record_repository(&repo);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    Ok(result)
 | 
					    Ok(result)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -529,6 +623,7 @@ fn main() {
 | 
				
			|||||||
                )
 | 
					                )
 | 
				
			||||||
        ))
 | 
					        ))
 | 
				
			||||||
        .arg_param(vec!["repository", "backupspec"])
 | 
					        .arg_param(vec!["repository", "backupspec"])
 | 
				
			||||||
 | 
					        .completion_cb("repository", complete_repository)
 | 
				
			||||||
        .completion_cb("backupspec", complete_backup_source);
 | 
					        .completion_cb("backupspec", complete_backup_source);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    let list_cmd_def = CliCommand::new(
 | 
					    let list_cmd_def = CliCommand::new(
 | 
				
			||||||
@ -537,7 +632,8 @@ fn main() {
 | 
				
			|||||||
            ObjectSchema::new("List backup groups.")
 | 
					            ObjectSchema::new("List backup groups.")
 | 
				
			||||||
                .required("repository", repo_url_schema.clone())
 | 
					                .required("repository", repo_url_schema.clone())
 | 
				
			||||||
        ))
 | 
					        ))
 | 
				
			||||||
        .arg_param(vec!["repository"]);
 | 
					        .arg_param(vec!["repository"])
 | 
				
			||||||
 | 
					        .completion_cb("repository", complete_repository);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    let snapshots_cmd_def = CliCommand::new(
 | 
					    let snapshots_cmd_def = CliCommand::new(
 | 
				
			||||||
        ApiMethod::new(
 | 
					        ApiMethod::new(
 | 
				
			||||||
@ -546,7 +642,8 @@ fn main() {
 | 
				
			|||||||
                .required("repository", repo_url_schema.clone())
 | 
					                .required("repository", repo_url_schema.clone())
 | 
				
			||||||
                .required("group", StringSchema::new("Backup group."))
 | 
					                .required("group", StringSchema::new("Backup group."))
 | 
				
			||||||
        ))
 | 
					        ))
 | 
				
			||||||
        .arg_param(vec!["repository", "group"]);
 | 
					        .arg_param(vec!["repository", "group"])
 | 
				
			||||||
 | 
					        .completion_cb("repository", complete_repository);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    let forget_cmd_def = CliCommand::new(
 | 
					    let forget_cmd_def = CliCommand::new(
 | 
				
			||||||
        ApiMethod::new(
 | 
					        ApiMethod::new(
 | 
				
			||||||
@ -555,7 +652,8 @@ fn main() {
 | 
				
			|||||||
                .required("repository", repo_url_schema.clone())
 | 
					                .required("repository", repo_url_schema.clone())
 | 
				
			||||||
                .required("snapshot", StringSchema::new("Snapshot path."))
 | 
					                .required("snapshot", StringSchema::new("Snapshot path."))
 | 
				
			||||||
        ))
 | 
					        ))
 | 
				
			||||||
        .arg_param(vec!["repository", "snapshot"]);
 | 
					        .arg_param(vec!["repository", "snapshot"])
 | 
				
			||||||
 | 
					        .completion_cb("repository", complete_repository);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    let garbage_collect_cmd_def = CliCommand::new(
 | 
					    let garbage_collect_cmd_def = CliCommand::new(
 | 
				
			||||||
        ApiMethod::new(
 | 
					        ApiMethod::new(
 | 
				
			||||||
@ -563,7 +661,8 @@ fn main() {
 | 
				
			|||||||
            ObjectSchema::new("Start garbage collection for a specific repository.")
 | 
					            ObjectSchema::new("Start garbage collection for a specific repository.")
 | 
				
			||||||
                .required("repository", repo_url_schema.clone())
 | 
					                .required("repository", repo_url_schema.clone())
 | 
				
			||||||
        ))
 | 
					        ))
 | 
				
			||||||
        .arg_param(vec!["repository"]);
 | 
					        .arg_param(vec!["repository"])
 | 
				
			||||||
 | 
					        .completion_cb("repository", complete_repository);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    let restore_cmd_def = CliCommand::new(
 | 
					    let restore_cmd_def = CliCommand::new(
 | 
				
			||||||
        ApiMethod::new(
 | 
					        ApiMethod::new(
 | 
				
			||||||
@ -574,7 +673,8 @@ fn main() {
 | 
				
			|||||||
                .required("archive-name", StringSchema::new("Backup archive name."))
 | 
					                .required("archive-name", StringSchema::new("Backup archive name."))
 | 
				
			||||||
                .required("target", StringSchema::new("Target directory path."))
 | 
					                .required("target", StringSchema::new("Target directory path."))
 | 
				
			||||||
        ))
 | 
					        ))
 | 
				
			||||||
        .arg_param(vec!["repository", "snapshot", "archive-name", "target"]);
 | 
					        .arg_param(vec!["repository", "snapshot", "archive-name", "target"])
 | 
				
			||||||
 | 
					        .completion_cb("repository", complete_repository);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    let prune_cmd_def = CliCommand::new(
 | 
					    let prune_cmd_def = CliCommand::new(
 | 
				
			||||||
        ApiMethod::new(
 | 
					        ApiMethod::new(
 | 
				
			||||||
@ -584,7 +684,8 @@ fn main() {
 | 
				
			|||||||
                    .required("repository", repo_url_schema.clone())
 | 
					                    .required("repository", repo_url_schema.clone())
 | 
				
			||||||
            )
 | 
					            )
 | 
				
			||||||
        ))
 | 
					        ))
 | 
				
			||||||
        .arg_param(vec!["repository"]);
 | 
					        .arg_param(vec!["repository"])
 | 
				
			||||||
 | 
					        .completion_cb("repository", complete_repository);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    let cmd_def = CliCommandMap::new()
 | 
					    let cmd_def = CliCommandMap::new()
 | 
				
			||||||
        .insert("backup".to_owned(), backup_cmd_def.into())
 | 
					        .insert("backup".to_owned(), backup_cmd_def.into())
 | 
				
			||||||
 | 
				
			|||||||
@ -611,12 +611,12 @@ fn main() {
 | 
				
			|||||||
    );
 | 
					    );
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    let no_cert_validation = true; // FIXME
 | 
					    let no_cert_validation = true; // FIXME
 | 
				
			||||||
    let domain = repo.host;
 | 
					    let domain = repo.host().to_owned();
 | 
				
			||||||
    let port = 8007;
 | 
					    let port = 8007;
 | 
				
			||||||
    let address = format!("{}:{}", domain, port);
 | 
					    let address = format!("{}:{}", domain, port);
 | 
				
			||||||
    let urlbase = format!("https://{}/api2/json", address);
 | 
					    let urlbase = format!("https://{}/api2/json", address);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    let user = repo.user.to_string();
 | 
					    let user = repo.user().to_string();
 | 
				
			||||||
    let pass = match proxmox_backup::tools::tty::read_password("Password: ")
 | 
					    let pass = match proxmox_backup::tools::tty::read_password("Password: ")
 | 
				
			||||||
        .and_then(|x| String::from_utf8(x).map_err(Error::from))
 | 
					        .and_then(|x| String::from_utf8(x).map_err(Error::from))
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
@ -626,7 +626,7 @@ fn main() {
 | 
				
			|||||||
            exit(1);
 | 
					            exit(1);
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
    };
 | 
					    };
 | 
				
			||||||
    let store = repo.store;
 | 
					    let store = repo.store().to_owned();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    let stream = File::open(filename.clone())
 | 
					    let stream = File::open(filename.clone())
 | 
				
			||||||
        .map_err(Error::from)
 | 
					        .map_err(Error::from)
 | 
				
			||||||
 | 
				
			|||||||
@ -22,11 +22,11 @@ lazy_static! {
 | 
				
			|||||||
#[derive(Debug)]
 | 
					#[derive(Debug)]
 | 
				
			||||||
pub struct BackupRepository {
 | 
					pub struct BackupRepository {
 | 
				
			||||||
    /// The user name used for Authentication
 | 
					    /// The user name used for Authentication
 | 
				
			||||||
    pub user: String,
 | 
					    user: Option<String>,
 | 
				
			||||||
    /// The host name or IP address
 | 
					    /// The host name or IP address
 | 
				
			||||||
    pub host: String,
 | 
					    host: Option<String>,
 | 
				
			||||||
    /// The name of the datastore
 | 
					    /// The name of the datastore
 | 
				
			||||||
    pub store: String,
 | 
					    store: String,
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
impl BackupRepository {
 | 
					impl BackupRepository {
 | 
				
			||||||
@ -41,10 +41,42 @@ impl BackupRepository {
 | 
				
			|||||||
        let cap = BACKUP_REPO_URL_REGEX.captures(url)
 | 
					        let cap = BACKUP_REPO_URL_REGEX.captures(url)
 | 
				
			||||||
            .ok_or_else(|| format_err!("unable to parse repository url '{}'", url))?;
 | 
					            .ok_or_else(|| format_err!("unable to parse repository url '{}'", url))?;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        Ok(BackupRepository {
 | 
					        Ok(BackupRepository {
 | 
				
			||||||
            user: cap.get(1).map_or("root@pam", |m| m.as_str()).to_owned(),
 | 
					            //user: cap.get(1).map_or("root@pam", |m| m.as_str()).to_owned(),
 | 
				
			||||||
            host: cap.get(2).map_or("localhost", |m| m.as_str()).to_owned(),
 | 
					            //host: cap.get(2).map_or("localhost", |m| m.as_str()).to_owned(),
 | 
				
			||||||
 | 
					            user: cap.get(1).map(|m| m.as_str().to_owned()),
 | 
				
			||||||
 | 
					            host: cap.get(2).map(|m| m.as_str().to_owned()),
 | 
				
			||||||
            store: cap[3].to_owned(),
 | 
					            store: cap[3].to_owned(),
 | 
				
			||||||
        })
 | 
					        })
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    pub fn user(&self) -> &str {
 | 
				
			||||||
 | 
					        if let Some(ref user) = self.user {
 | 
				
			||||||
 | 
					            return user;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        "root@pam"
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    pub fn host(&self) -> &str {
 | 
				
			||||||
 | 
					        if let Some(ref host) = self.host {
 | 
				
			||||||
 | 
					            return host;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        "localhost"
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    pub fn store(&self) -> &str {
 | 
				
			||||||
 | 
					        &self.store
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    pub fn to_string(&self) -> String  {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if let Some(ref user) = self.user {
 | 
				
			||||||
 | 
					            return format!("{}@{}:{}", user, self.host(), self.store);
 | 
				
			||||||
 | 
					        } else if let Some(ref host) = self.host {
 | 
				
			||||||
 | 
					            return format!("{}:{}", host, self.store);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        self.store.to_owned()
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
				
			|||||||
		Reference in New Issue
	
	Block a user