client/remote: add support to specify port number
this adds the ability to add port numbers in the backup repo spec as well as remotes, so that user that are behind a NAT/Firewall/Reverse proxy can still use it also adds some explanation and examples to the docs to make it clearer for h2 client i left the localhost:8007 part, since it is not configurable where we bind to Signed-off-by: Dominik Csapak <d.csapak@proxmox.com>
This commit is contained in:
parent
729d41fe6a
commit
ba20987ae7
|
@ -732,11 +732,14 @@ Repository Locations
|
||||||
The client uses the following notation to specify a datastore repository
|
The client uses the following notation to specify a datastore repository
|
||||||
on the backup server.
|
on the backup server.
|
||||||
|
|
||||||
[[username@]server:]datastore
|
[[username@]server[:port]:]datastore
|
||||||
|
|
||||||
The default value for ``username`` ist ``root@pam``. If no server is specified,
|
The default value for ``username`` ist ``root@pam``. If no server is specified,
|
||||||
the default is the local host (``localhost``).
|
the default is the local host (``localhost``).
|
||||||
|
|
||||||
|
You can specify a port if your backup server is only reachable on a different
|
||||||
|
port (e.g. with NAT and port forwarding).
|
||||||
|
|
||||||
Note that if the server is an IPv6 address, you have to write it with
|
Note that if the server is an IPv6 address, you have to write it with
|
||||||
square brackets (e.g. [fe80::01]).
|
square brackets (e.g. [fe80::01]).
|
||||||
|
|
||||||
|
@ -744,6 +747,18 @@ You can pass the repository with the ``--repository`` command
|
||||||
line option, or by setting the ``PBS_REPOSITORY`` environment
|
line option, or by setting the ``PBS_REPOSITORY`` environment
|
||||||
variable.
|
variable.
|
||||||
|
|
||||||
|
Here some examples of valid repositories and the real values
|
||||||
|
|
||||||
|
================================ ============ ================== ===========
|
||||||
|
Example User Host:Port Datastore
|
||||||
|
================================ ============ ================== ===========
|
||||||
|
mydatastore ``root@pam`` localhost:8007 mydatastore
|
||||||
|
myhostname:mydatastore ``root@pam`` myhostname:8007 mydatastore
|
||||||
|
user@pbs@myhostname:mydatastore ``user@pbs`` myhostname:8007 mydatastore
|
||||||
|
192.168.55.55:1234:mydatastore ``root@pam`` 192.168.55.55:1234 mydatastore
|
||||||
|
[ff80::51]:mydatastore ``root@pam`` [ff80::51]:8007 mydatastore
|
||||||
|
[ff80::51]:1234:mydatastore ``root@pam`` [ff80::51]:1234 mydatastore
|
||||||
|
================================ ============ ================== ===========
|
||||||
|
|
||||||
Environment Variables
|
Environment Variables
|
||||||
~~~~~~~~~~~~~~~~~~~~~
|
~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
|
|
@ -60,6 +60,12 @@ pub fn list_remotes(
|
||||||
host: {
|
host: {
|
||||||
schema: DNS_NAME_OR_IP_SCHEMA,
|
schema: DNS_NAME_OR_IP_SCHEMA,
|
||||||
},
|
},
|
||||||
|
port: {
|
||||||
|
description: "The (optional) port.",
|
||||||
|
type: u16,
|
||||||
|
optional: true,
|
||||||
|
default: 8007,
|
||||||
|
},
|
||||||
userid: {
|
userid: {
|
||||||
type: Userid,
|
type: Userid,
|
||||||
},
|
},
|
||||||
|
@ -136,6 +142,8 @@ pub enum DeletableProperty {
|
||||||
comment,
|
comment,
|
||||||
/// Delete the fingerprint property.
|
/// Delete the fingerprint property.
|
||||||
fingerprint,
|
fingerprint,
|
||||||
|
/// Delete the port property.
|
||||||
|
port,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[api(
|
#[api(
|
||||||
|
@ -153,6 +161,11 @@ pub enum DeletableProperty {
|
||||||
optional: true,
|
optional: true,
|
||||||
schema: DNS_NAME_OR_IP_SCHEMA,
|
schema: DNS_NAME_OR_IP_SCHEMA,
|
||||||
},
|
},
|
||||||
|
port: {
|
||||||
|
description: "The (optional) port.",
|
||||||
|
type: u16,
|
||||||
|
optional: true,
|
||||||
|
},
|
||||||
userid: {
|
userid: {
|
||||||
optional: true,
|
optional: true,
|
||||||
type: Userid,
|
type: Userid,
|
||||||
|
@ -188,6 +201,7 @@ pub fn update_remote(
|
||||||
name: String,
|
name: String,
|
||||||
comment: Option<String>,
|
comment: Option<String>,
|
||||||
host: Option<String>,
|
host: Option<String>,
|
||||||
|
port: Option<u16>,
|
||||||
userid: Option<Userid>,
|
userid: Option<Userid>,
|
||||||
password: Option<String>,
|
password: Option<String>,
|
||||||
fingerprint: Option<String>,
|
fingerprint: Option<String>,
|
||||||
|
@ -211,6 +225,7 @@ pub fn update_remote(
|
||||||
match delete_prop {
|
match delete_prop {
|
||||||
DeletableProperty::comment => { data.comment = None; },
|
DeletableProperty::comment => { data.comment = None; },
|
||||||
DeletableProperty::fingerprint => { data.fingerprint = None; },
|
DeletableProperty::fingerprint => { data.fingerprint = None; },
|
||||||
|
DeletableProperty::port => { data.port = None; },
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -224,6 +239,7 @@ pub fn update_remote(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if let Some(host) = host { data.host = host; }
|
if let Some(host) = host { data.host = host; }
|
||||||
|
if port.is_some() { data.port = port; }
|
||||||
if let Some(userid) = userid { data.userid = userid; }
|
if let Some(userid) = userid { data.userid = userid; }
|
||||||
if let Some(password) = password { data.password = password; }
|
if let Some(password) = password { data.password = password; }
|
||||||
|
|
||||||
|
|
|
@ -55,12 +55,12 @@ pub async fn get_pull_parameters(
|
||||||
.password(Some(remote.password.clone()))
|
.password(Some(remote.password.clone()))
|
||||||
.fingerprint(remote.fingerprint.clone());
|
.fingerprint(remote.fingerprint.clone());
|
||||||
|
|
||||||
let client = HttpClient::new(&remote.host, &remote.userid, options)?;
|
let client = HttpClient::new(&remote.host, remote.port.unwrap_or(8007), &remote.userid, options)?;
|
||||||
let _auth_info = client.login() // make sure we can auth
|
let _auth_info = client.login() // make sure we can auth
|
||||||
.await
|
.await
|
||||||
.map_err(|err| format_err!("remote connection to '{}' failed - {}", remote.host, err))?;
|
.map_err(|err| format_err!("remote connection to '{}' failed - {}", remote.host, err))?;
|
||||||
|
|
||||||
let src_repo = BackupRepository::new(Some(remote.userid), Some(remote.host), remote_store.to_string());
|
let src_repo = BackupRepository::new(Some(remote.userid), Some(remote.host), remote.port, remote_store.to_string());
|
||||||
|
|
||||||
Ok((client, src_repo, tgt_store))
|
Ok((client, src_repo, tgt_store))
|
||||||
}
|
}
|
||||||
|
|
|
@ -65,7 +65,7 @@ const_regex!{
|
||||||
|
|
||||||
pub DNS_NAME_OR_IP_REGEX = concat!(r"^", DNS_NAME!(), "|", IPRE!(), r"$");
|
pub DNS_NAME_OR_IP_REGEX = concat!(r"^", DNS_NAME!(), "|", IPRE!(), r"$");
|
||||||
|
|
||||||
pub BACKUP_REPO_URL_REGEX = concat!(r"^^(?:(?:(", USER_ID_REGEX_STR!(), ")@)?(", DNS_NAME!(), "|", IPRE_BRACKET!() ,"):)?(", PROXMOX_SAFE_ID_REGEX_STR!(), r")$");
|
pub BACKUP_REPO_URL_REGEX = concat!(r"^^(?:(?:(", USER_ID_REGEX_STR!(), ")@)?(", DNS_NAME!(), "|", IPRE_BRACKET!() ,"):)?(?:([0-9]{1,5}):)?(", PROXMOX_SAFE_ID_REGEX_STR!(), r")$");
|
||||||
|
|
||||||
pub CERT_FINGERPRINT_SHA256_REGEX = r"^(?:[0-9a-fA-F][0-9a-fA-F])(?::[0-9a-fA-F][0-9a-fA-F]){31}$";
|
pub CERT_FINGERPRINT_SHA256_REGEX = r"^(?:[0-9a-fA-F][0-9a-fA-F])(?::[0-9a-fA-F][0-9a-fA-F]){31}$";
|
||||||
|
|
||||||
|
|
|
@ -192,7 +192,7 @@ pub fn complete_repository(_arg: &str, _param: &HashMap<String, String>) -> Vec<
|
||||||
result
|
result
|
||||||
}
|
}
|
||||||
|
|
||||||
fn connect(server: &str, userid: &Userid) -> Result<HttpClient, Error> {
|
fn connect(server: &str, port: u16, userid: &Userid) -> Result<HttpClient, Error> {
|
||||||
|
|
||||||
let fingerprint = std::env::var(ENV_VAR_PBS_FINGERPRINT).ok();
|
let fingerprint = std::env::var(ENV_VAR_PBS_FINGERPRINT).ok();
|
||||||
|
|
||||||
|
@ -211,7 +211,7 @@ fn connect(server: &str, userid: &Userid) -> Result<HttpClient, Error> {
|
||||||
.fingerprint_cache(true)
|
.fingerprint_cache(true)
|
||||||
.ticket_cache(true);
|
.ticket_cache(true);
|
||||||
|
|
||||||
HttpClient::new(server, userid, options)
|
HttpClient::new(server, port, userid, options)
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn view_task_result(
|
async fn view_task_result(
|
||||||
|
@ -365,7 +365,7 @@ async fn list_backup_groups(param: Value) -> Result<Value, Error> {
|
||||||
|
|
||||||
let repo = extract_repository_from_value(¶m)?;
|
let repo = extract_repository_from_value(¶m)?;
|
||||||
|
|
||||||
let client = connect(repo.host(), repo.user())?;
|
let client = connect(repo.host(), repo.port(), repo.user())?;
|
||||||
|
|
||||||
let path = format!("api2/json/admin/datastore/{}/groups", repo.store());
|
let path = format!("api2/json/admin/datastore/{}/groups", repo.store());
|
||||||
|
|
||||||
|
@ -438,7 +438,7 @@ async fn list_snapshots(param: Value) -> Result<Value, Error> {
|
||||||
|
|
||||||
let output_format = get_output_format(¶m);
|
let output_format = get_output_format(¶m);
|
||||||
|
|
||||||
let client = connect(repo.host(), repo.user())?;
|
let client = connect(repo.host(), repo.port(), repo.user())?;
|
||||||
|
|
||||||
let group: Option<BackupGroup> = if let Some(path) = param["group"].as_str() {
|
let group: Option<BackupGroup> = if let Some(path) = param["group"].as_str() {
|
||||||
Some(path.parse()?)
|
Some(path.parse()?)
|
||||||
|
@ -503,7 +503,7 @@ async fn forget_snapshots(param: Value) -> Result<Value, Error> {
|
||||||
let path = tools::required_string_param(¶m, "snapshot")?;
|
let path = tools::required_string_param(¶m, "snapshot")?;
|
||||||
let snapshot: BackupDir = path.parse()?;
|
let snapshot: BackupDir = path.parse()?;
|
||||||
|
|
||||||
let mut client = connect(repo.host(), repo.user())?;
|
let mut client = connect(repo.host(), repo.port(), repo.user())?;
|
||||||
|
|
||||||
let path = format!("api2/json/admin/datastore/{}/snapshots", repo.store());
|
let path = format!("api2/json/admin/datastore/{}/snapshots", repo.store());
|
||||||
|
|
||||||
|
@ -533,7 +533,7 @@ async fn api_login(param: Value) -> Result<Value, Error> {
|
||||||
|
|
||||||
let repo = extract_repository_from_value(¶m)?;
|
let repo = extract_repository_from_value(¶m)?;
|
||||||
|
|
||||||
let client = connect(repo.host(), repo.user())?;
|
let client = connect(repo.host(), repo.port(), repo.user())?;
|
||||||
client.login().await?;
|
client.login().await?;
|
||||||
|
|
||||||
record_repository(&repo);
|
record_repository(&repo);
|
||||||
|
@ -590,7 +590,7 @@ async fn api_version(param: Value) -> Result<(), Error> {
|
||||||
|
|
||||||
let repo = extract_repository_from_value(¶m);
|
let repo = extract_repository_from_value(¶m);
|
||||||
if let Ok(repo) = repo {
|
if let Ok(repo) = repo {
|
||||||
let client = connect(repo.host(), repo.user())?;
|
let client = connect(repo.host(), repo.port(), repo.user())?;
|
||||||
|
|
||||||
match client.get("api2/json/version", None).await {
|
match client.get("api2/json/version", None).await {
|
||||||
Ok(mut result) => version_info["server"] = result["data"].take(),
|
Ok(mut result) => version_info["server"] = result["data"].take(),
|
||||||
|
@ -640,7 +640,7 @@ async fn list_snapshot_files(param: Value) -> Result<Value, Error> {
|
||||||
|
|
||||||
let output_format = get_output_format(¶m);
|
let output_format = get_output_format(¶m);
|
||||||
|
|
||||||
let client = connect(repo.host(), repo.user())?;
|
let client = connect(repo.host(), repo.port(), repo.user())?;
|
||||||
|
|
||||||
let path = format!("api2/json/admin/datastore/{}/files", repo.store());
|
let path = format!("api2/json/admin/datastore/{}/files", repo.store());
|
||||||
|
|
||||||
|
@ -684,7 +684,7 @@ async fn start_garbage_collection(param: Value) -> Result<Value, Error> {
|
||||||
|
|
||||||
let output_format = get_output_format(¶m);
|
let output_format = get_output_format(¶m);
|
||||||
|
|
||||||
let mut client = connect(repo.host(), repo.user())?;
|
let mut client = connect(repo.host(), repo.port(), repo.user())?;
|
||||||
|
|
||||||
let path = format!("api2/json/admin/datastore/{}/gc", repo.store());
|
let path = format!("api2/json/admin/datastore/{}/gc", repo.store());
|
||||||
|
|
||||||
|
@ -996,7 +996,7 @@ async fn create_backup(
|
||||||
|
|
||||||
let backup_time = backup_time_opt.unwrap_or_else(|| epoch_i64());
|
let backup_time = backup_time_opt.unwrap_or_else(|| epoch_i64());
|
||||||
|
|
||||||
let client = connect(repo.host(), repo.user())?;
|
let client = connect(repo.host(), repo.port(), repo.user())?;
|
||||||
record_repository(&repo);
|
record_repository(&repo);
|
||||||
|
|
||||||
println!("Starting backup: {}/{}/{}", backup_type, backup_id, BackupDir::backup_time_to_string(backup_time)?);
|
println!("Starting backup: {}/{}/{}", backup_type, backup_id, BackupDir::backup_time_to_string(backup_time)?);
|
||||||
|
@ -1299,7 +1299,7 @@ async fn restore(param: Value) -> Result<Value, Error> {
|
||||||
|
|
||||||
let archive_name = tools::required_string_param(¶m, "archive-name")?;
|
let archive_name = tools::required_string_param(¶m, "archive-name")?;
|
||||||
|
|
||||||
let client = connect(repo.host(), repo.user())?;
|
let client = connect(repo.host(), repo.port(), repo.user())?;
|
||||||
|
|
||||||
record_repository(&repo);
|
record_repository(&repo);
|
||||||
|
|
||||||
|
@ -1472,7 +1472,7 @@ async fn upload_log(param: Value) -> Result<Value, Error> {
|
||||||
let snapshot = tools::required_string_param(¶m, "snapshot")?;
|
let snapshot = tools::required_string_param(¶m, "snapshot")?;
|
||||||
let snapshot: BackupDir = snapshot.parse()?;
|
let snapshot: BackupDir = snapshot.parse()?;
|
||||||
|
|
||||||
let mut client = connect(repo.host(), repo.user())?;
|
let mut client = connect(repo.host(), repo.port(), repo.user())?;
|
||||||
|
|
||||||
let (keydata, crypt_mode) = keyfile_parameters(¶m)?;
|
let (keydata, crypt_mode) = keyfile_parameters(¶m)?;
|
||||||
|
|
||||||
|
@ -1543,7 +1543,7 @@ fn prune<'a>(
|
||||||
async fn prune_async(mut param: Value) -> Result<Value, Error> {
|
async fn prune_async(mut param: Value) -> Result<Value, Error> {
|
||||||
let repo = extract_repository_from_value(¶m)?;
|
let repo = extract_repository_from_value(¶m)?;
|
||||||
|
|
||||||
let mut client = connect(repo.host(), repo.user())?;
|
let mut client = connect(repo.host(), repo.port(), repo.user())?;
|
||||||
|
|
||||||
let path = format!("api2/json/admin/datastore/{}/prune", repo.store());
|
let path = format!("api2/json/admin/datastore/{}/prune", repo.store());
|
||||||
|
|
||||||
|
@ -1626,7 +1626,7 @@ async fn status(param: Value) -> Result<Value, Error> {
|
||||||
|
|
||||||
let output_format = get_output_format(¶m);
|
let output_format = get_output_format(¶m);
|
||||||
|
|
||||||
let client = connect(repo.host(), repo.user())?;
|
let client = connect(repo.host(), repo.port(), repo.user())?;
|
||||||
|
|
||||||
let path = format!("api2/json/admin/datastore/{}/status", repo.store());
|
let path = format!("api2/json/admin/datastore/{}/status", repo.store());
|
||||||
|
|
||||||
|
@ -1671,7 +1671,7 @@ async fn try_get(repo: &BackupRepository, url: &str) -> Value {
|
||||||
.fingerprint_cache(true)
|
.fingerprint_cache(true)
|
||||||
.ticket_cache(true);
|
.ticket_cache(true);
|
||||||
|
|
||||||
let client = match HttpClient::new(repo.host(), repo.user(), options) {
|
let client = match HttpClient::new(repo.host(), repo.port(), repo.user(), options) {
|
||||||
Ok(v) => v,
|
Ok(v) => v,
|
||||||
_ => return Value::Null,
|
_ => return Value::Null,
|
||||||
};
|
};
|
||||||
|
|
|
@ -62,10 +62,10 @@ fn connect() -> Result<HttpClient, Error> {
|
||||||
let ticket = Ticket::new("PBS", Userid::root_userid())?
|
let ticket = Ticket::new("PBS", Userid::root_userid())?
|
||||||
.sign(private_auth_key(), None)?;
|
.sign(private_auth_key(), None)?;
|
||||||
options = options.password(Some(ticket));
|
options = options.password(Some(ticket));
|
||||||
HttpClient::new("localhost", Userid::root_userid(), options)?
|
HttpClient::new("localhost", 8007, Userid::root_userid(), options)?
|
||||||
} else {
|
} else {
|
||||||
options = options.ticket_cache(true).interactive(true);
|
options = options.ticket_cache(true).interactive(true);
|
||||||
HttpClient::new("localhost", Userid::root_userid(), options)?
|
HttpClient::new("localhost", 8007, Userid::root_userid(), options)?
|
||||||
};
|
};
|
||||||
|
|
||||||
Ok(client)
|
Ok(client)
|
||||||
|
@ -410,6 +410,7 @@ pub fn complete_remote_datastore_name(_arg: &str, param: &HashMap<String, String
|
||||||
|
|
||||||
let client = HttpClient::new(
|
let client = HttpClient::new(
|
||||||
&remote.host,
|
&remote.host,
|
||||||
|
remote.port.unwrap_or(8007),
|
||||||
&remote.userid,
|
&remote.userid,
|
||||||
options,
|
options,
|
||||||
)?;
|
)?;
|
||||||
|
|
|
@ -225,7 +225,7 @@ async fn test_upload_speed(
|
||||||
|
|
||||||
let backup_time = proxmox::tools::time::epoch_i64();
|
let backup_time = proxmox::tools::time::epoch_i64();
|
||||||
|
|
||||||
let client = connect(repo.host(), repo.user())?;
|
let client = connect(repo.host(), repo.port(), repo.user())?;
|
||||||
record_repository(&repo);
|
record_repository(&repo);
|
||||||
|
|
||||||
if verbose { eprintln!("Connecting to backup server"); }
|
if verbose { eprintln!("Connecting to backup server"); }
|
||||||
|
|
|
@ -79,7 +79,7 @@ async fn dump_catalog(param: Value) -> Result<Value, Error> {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
let client = connect(repo.host(), repo.user())?;
|
let client = connect(repo.host(), repo.port(), repo.user())?;
|
||||||
|
|
||||||
let client = BackupReader::start(
|
let client = BackupReader::start(
|
||||||
client,
|
client,
|
||||||
|
@ -153,7 +153,7 @@ async fn dump_catalog(param: Value) -> Result<Value, Error> {
|
||||||
/// Shell to interactively inspect and restore snapshots.
|
/// Shell to interactively inspect and restore snapshots.
|
||||||
async fn catalog_shell(param: Value) -> Result<(), Error> {
|
async fn catalog_shell(param: Value) -> Result<(), Error> {
|
||||||
let repo = extract_repository_from_value(¶m)?;
|
let repo = extract_repository_from_value(¶m)?;
|
||||||
let client = connect(repo.host(), repo.user())?;
|
let client = connect(repo.host(), repo.port(), repo.user())?;
|
||||||
let path = tools::required_string_param(¶m, "snapshot")?;
|
let path = tools::required_string_param(¶m, "snapshot")?;
|
||||||
let archive_name = tools::required_string_param(¶m, "archive-name")?;
|
let archive_name = tools::required_string_param(¶m, "archive-name")?;
|
||||||
|
|
||||||
|
|
|
@ -101,7 +101,7 @@ async fn mount_do(param: Value, pipe: Option<RawFd>) -> Result<Value, Error> {
|
||||||
let repo = extract_repository_from_value(¶m)?;
|
let repo = extract_repository_from_value(¶m)?;
|
||||||
let archive_name = tools::required_string_param(¶m, "archive-name")?;
|
let archive_name = tools::required_string_param(¶m, "archive-name")?;
|
||||||
let target = tools::required_string_param(¶m, "target")?;
|
let target = tools::required_string_param(¶m, "target")?;
|
||||||
let client = connect(repo.host(), repo.user())?;
|
let client = connect(repo.host(), repo.port(), repo.user())?;
|
||||||
|
|
||||||
record_repository(&repo);
|
record_repository(&repo);
|
||||||
|
|
||||||
|
|
|
@ -48,7 +48,7 @@ async fn task_list(param: Value) -> Result<Value, Error> {
|
||||||
let output_format = get_output_format(¶m);
|
let output_format = get_output_format(¶m);
|
||||||
|
|
||||||
let repo = extract_repository_from_value(¶m)?;
|
let repo = extract_repository_from_value(¶m)?;
|
||||||
let client = connect(repo.host(), repo.user())?;
|
let client = connect(repo.host(), repo.port(), repo.user())?;
|
||||||
|
|
||||||
let limit = param["limit"].as_u64().unwrap_or(50) as usize;
|
let limit = param["limit"].as_u64().unwrap_or(50) as usize;
|
||||||
let running = !param["all"].as_bool().unwrap_or(false);
|
let running = !param["all"].as_bool().unwrap_or(false);
|
||||||
|
@ -96,7 +96,7 @@ async fn task_log(param: Value) -> Result<Value, Error> {
|
||||||
let repo = extract_repository_from_value(¶m)?;
|
let repo = extract_repository_from_value(¶m)?;
|
||||||
let upid = tools::required_string_param(¶m, "upid")?;
|
let upid = tools::required_string_param(¶m, "upid")?;
|
||||||
|
|
||||||
let client = connect(repo.host(), repo.user())?;
|
let client = connect(repo.host(), repo.port(), repo.user())?;
|
||||||
|
|
||||||
display_task_log(client, upid, true).await?;
|
display_task_log(client, upid, true).await?;
|
||||||
|
|
||||||
|
@ -122,7 +122,7 @@ async fn task_stop(param: Value) -> Result<Value, Error> {
|
||||||
let repo = extract_repository_from_value(¶m)?;
|
let repo = extract_repository_from_value(¶m)?;
|
||||||
let upid_str = tools::required_string_param(¶m, "upid")?;
|
let upid_str = tools::required_string_param(¶m, "upid")?;
|
||||||
|
|
||||||
let mut client = connect(repo.host(), repo.user())?;
|
let mut client = connect(repo.host(), repo.port(), repo.user())?;
|
||||||
|
|
||||||
let path = format!("api2/json/nodes/localhost/tasks/{}", upid_str);
|
let path = format!("api2/json/nodes/localhost/tasks/{}", upid_str);
|
||||||
let _ = client.delete(&path, None).await?;
|
let _ = client.delete(&path, None).await?;
|
||||||
|
|
|
@ -54,7 +54,7 @@ impl BackupReader {
|
||||||
"store": datastore,
|
"store": datastore,
|
||||||
"debug": debug,
|
"debug": debug,
|
||||||
});
|
});
|
||||||
let req = HttpClient::request_builder(client.server(), "GET", "/api2/json/reader", Some(param)).unwrap();
|
let req = HttpClient::request_builder(client.server(), client.port(), "GET", "/api2/json/reader", Some(param)).unwrap();
|
||||||
|
|
||||||
let (h2, abort) = client.start_h2_connection(req, String::from(PROXMOX_BACKUP_READER_PROTOCOL_ID_V1!())).await?;
|
let (h2, abort) = client.start_h2_connection(req, String::from(PROXMOX_BACKUP_READER_PROTOCOL_ID_V1!())).await?;
|
||||||
|
|
||||||
|
|
|
@ -19,14 +19,16 @@ pub struct BackupRepository {
|
||||||
user: Option<Userid>,
|
user: Option<Userid>,
|
||||||
/// The host name or IP address
|
/// The host name or IP address
|
||||||
host: Option<String>,
|
host: Option<String>,
|
||||||
|
/// The port
|
||||||
|
port: Option<u16>,
|
||||||
/// The name of the datastore
|
/// The name of the datastore
|
||||||
store: String,
|
store: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl BackupRepository {
|
impl BackupRepository {
|
||||||
|
|
||||||
pub fn new(user: Option<Userid>, host: Option<String>, store: String) -> Self {
|
pub fn new(user: Option<Userid>, host: Option<String>, port: Option<u16>, store: String) -> Self {
|
||||||
Self { user, host, store }
|
Self { user, host, port, store }
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn user(&self) -> &Userid {
|
pub fn user(&self) -> &Userid {
|
||||||
|
@ -43,6 +45,13 @@ impl BackupRepository {
|
||||||
"localhost"
|
"localhost"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn port(&self) -> u16 {
|
||||||
|
if let Some(port) = self.port {
|
||||||
|
return port;
|
||||||
|
}
|
||||||
|
8007
|
||||||
|
}
|
||||||
|
|
||||||
pub fn store(&self) -> &str {
|
pub fn store(&self) -> &str {
|
||||||
&self.store
|
&self.store
|
||||||
}
|
}
|
||||||
|
@ -50,12 +59,11 @@ impl BackupRepository {
|
||||||
|
|
||||||
impl fmt::Display for BackupRepository {
|
impl fmt::Display for BackupRepository {
|
||||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||||
if let Some(ref user) = self.user {
|
match (&self.user, &self.host, self.port) {
|
||||||
write!(f, "{}@{}:{}", user, self.host(), self.store)
|
(Some(user), _, _) => write!(f, "{}@{}:{}:{}", user, self.host(), self.port(), self.store),
|
||||||
} else if let Some(ref host) = self.host {
|
(None, Some(host), None) => write!(f, "{}:{}", host, self.store),
|
||||||
write!(f, "{}:{}", host, self.store)
|
(None, _, Some(port)) => write!(f, "{}:{}:{}", self.host(), port, self.store),
|
||||||
} else {
|
(None, None, None) => write!(f, "{}", self.store),
|
||||||
write!(f, "{}", self.store)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -76,7 +84,8 @@ impl std::str::FromStr for BackupRepository {
|
||||||
Ok(Self {
|
Ok(Self {
|
||||||
user: cap.get(1).map(|m| Userid::try_from(m.as_str().to_owned())).transpose()?,
|
user: cap.get(1).map(|m| Userid::try_from(m.as_str().to_owned())).transpose()?,
|
||||||
host: cap.get(2).map(|m| m.as_str().to_owned()),
|
host: cap.get(2).map(|m| m.as_str().to_owned()),
|
||||||
store: cap[3].to_owned(),
|
port: cap.get(3).map(|m| m.as_str().parse::<u16>()).transpose()?,
|
||||||
|
store: cap[4].to_owned(),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -65,7 +65,7 @@ impl BackupWriter {
|
||||||
});
|
});
|
||||||
|
|
||||||
let req = HttpClient::request_builder(
|
let req = HttpClient::request_builder(
|
||||||
client.server(), "GET", "/api2/json/backup", Some(param)).unwrap();
|
client.server(), client.port(), "GET", "/api2/json/backup", Some(param)).unwrap();
|
||||||
|
|
||||||
let (h2, abort) = client.start_h2_connection(req, String::from(PROXMOX_BACKUP_PROTOCOL_ID_V1!())).await?;
|
let (h2, abort) = client.start_h2_connection(req, String::from(PROXMOX_BACKUP_PROTOCOL_ID_V1!())).await?;
|
||||||
|
|
||||||
|
|
|
@ -99,6 +99,7 @@ impl HttpClientOptions {
|
||||||
pub struct HttpClient {
|
pub struct HttpClient {
|
||||||
client: Client<HttpsConnector>,
|
client: Client<HttpsConnector>,
|
||||||
server: String,
|
server: String,
|
||||||
|
port: u16,
|
||||||
fingerprint: Arc<Mutex<Option<String>>>,
|
fingerprint: Arc<Mutex<Option<String>>>,
|
||||||
first_auth: BroadcastFuture<()>,
|
first_auth: BroadcastFuture<()>,
|
||||||
auth: Arc<RwLock<AuthInfo>>,
|
auth: Arc<RwLock<AuthInfo>>,
|
||||||
|
@ -250,6 +251,7 @@ fn load_ticket_info(prefix: &str, server: &str, userid: &Userid) -> Option<(Stri
|
||||||
impl HttpClient {
|
impl HttpClient {
|
||||||
pub fn new(
|
pub fn new(
|
||||||
server: &str,
|
server: &str,
|
||||||
|
port: u16,
|
||||||
userid: &Userid,
|
userid: &Userid,
|
||||||
mut options: HttpClientOptions,
|
mut options: HttpClientOptions,
|
||||||
) -> Result<Self, Error> {
|
) -> Result<Self, Error> {
|
||||||
|
@ -338,7 +340,7 @@ impl HttpClient {
|
||||||
let authinfo = auth2.read().unwrap().clone();
|
let authinfo = auth2.read().unwrap().clone();
|
||||||
(authinfo.userid, authinfo.ticket)
|
(authinfo.userid, authinfo.ticket)
|
||||||
};
|
};
|
||||||
match Self::credentials(client2.clone(), server2.clone(), userid, ticket).await {
|
match Self::credentials(client2.clone(), server2.clone(), port, userid, ticket).await {
|
||||||
Ok(auth) => {
|
Ok(auth) => {
|
||||||
if use_ticket_cache & &prefix2.is_some() {
|
if use_ticket_cache & &prefix2.is_some() {
|
||||||
let _ = store_ticket_info(prefix2.as_ref().unwrap(), &server2, &auth.userid.to_string(), &auth.ticket, &auth.token);
|
let _ = store_ticket_info(prefix2.as_ref().unwrap(), &server2, &auth.userid.to_string(), &auth.ticket, &auth.token);
|
||||||
|
@ -358,6 +360,7 @@ impl HttpClient {
|
||||||
let login_future = Self::credentials(
|
let login_future = Self::credentials(
|
||||||
client.clone(),
|
client.clone(),
|
||||||
server.to_owned(),
|
server.to_owned(),
|
||||||
|
port,
|
||||||
userid.to_owned(),
|
userid.to_owned(),
|
||||||
password.to_owned(),
|
password.to_owned(),
|
||||||
).map_ok({
|
).map_ok({
|
||||||
|
@ -377,6 +380,7 @@ impl HttpClient {
|
||||||
Ok(Self {
|
Ok(Self {
|
||||||
client,
|
client,
|
||||||
server: String::from(server),
|
server: String::from(server),
|
||||||
|
port,
|
||||||
fingerprint: verified_fingerprint,
|
fingerprint: verified_fingerprint,
|
||||||
auth,
|
auth,
|
||||||
ticket_abort,
|
ticket_abort,
|
||||||
|
@ -486,7 +490,7 @@ impl HttpClient {
|
||||||
path: &str,
|
path: &str,
|
||||||
data: Option<Value>,
|
data: Option<Value>,
|
||||||
) -> Result<Value, Error> {
|
) -> Result<Value, Error> {
|
||||||
let req = Self::request_builder(&self.server, "GET", path, data).unwrap();
|
let req = Self::request_builder(&self.server, self.port, "GET", path, data)?;
|
||||||
self.request(req).await
|
self.request(req).await
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -495,7 +499,7 @@ impl HttpClient {
|
||||||
path: &str,
|
path: &str,
|
||||||
data: Option<Value>,
|
data: Option<Value>,
|
||||||
) -> Result<Value, Error> {
|
) -> Result<Value, Error> {
|
||||||
let req = Self::request_builder(&self.server, "DELETE", path, data).unwrap();
|
let req = Self::request_builder(&self.server, self.port, "DELETE", path, data)?;
|
||||||
self.request(req).await
|
self.request(req).await
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -504,7 +508,7 @@ impl HttpClient {
|
||||||
path: &str,
|
path: &str,
|
||||||
data: Option<Value>,
|
data: Option<Value>,
|
||||||
) -> Result<Value, Error> {
|
) -> Result<Value, Error> {
|
||||||
let req = Self::request_builder(&self.server, "POST", path, data).unwrap();
|
let req = Self::request_builder(&self.server, self.port, "POST", path, data)?;
|
||||||
self.request(req).await
|
self.request(req).await
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -513,7 +517,7 @@ impl HttpClient {
|
||||||
path: &str,
|
path: &str,
|
||||||
output: &mut (dyn Write + Send),
|
output: &mut (dyn Write + Send),
|
||||||
) -> Result<(), Error> {
|
) -> Result<(), Error> {
|
||||||
let mut req = Self::request_builder(&self.server, "GET", path, None).unwrap();
|
let mut req = Self::request_builder(&self.server, self.port, "GET", path, None)?;
|
||||||
|
|
||||||
let client = self.client.clone();
|
let client = self.client.clone();
|
||||||
|
|
||||||
|
@ -549,7 +553,7 @@ impl HttpClient {
|
||||||
) -> Result<Value, Error> {
|
) -> Result<Value, Error> {
|
||||||
|
|
||||||
let path = path.trim_matches('/');
|
let path = path.trim_matches('/');
|
||||||
let mut url = format!("https://{}:8007/{}", &self.server, path);
|
let mut url = format!("https://{}:{}/{}", &self.server, self.port, path);
|
||||||
|
|
||||||
if let Some(data) = data {
|
if let Some(data) = data {
|
||||||
let query = tools::json_object_to_query(data).unwrap();
|
let query = tools::json_object_to_query(data).unwrap();
|
||||||
|
@ -624,11 +628,12 @@ impl HttpClient {
|
||||||
async fn credentials(
|
async fn credentials(
|
||||||
client: Client<HttpsConnector>,
|
client: Client<HttpsConnector>,
|
||||||
server: String,
|
server: String,
|
||||||
|
port: u16,
|
||||||
username: Userid,
|
username: Userid,
|
||||||
password: String,
|
password: String,
|
||||||
) -> Result<AuthInfo, Error> {
|
) -> Result<AuthInfo, Error> {
|
||||||
let data = json!({ "username": username, "password": password });
|
let data = json!({ "username": username, "password": password });
|
||||||
let req = Self::request_builder(&server, "POST", "/api2/json/access/ticket", Some(data)).unwrap();
|
let req = Self::request_builder(&server, port, "POST", "/api2/json/access/ticket", Some(data))?;
|
||||||
let cred = Self::api_request(client, req).await?;
|
let cred = Self::api_request(client, req).await?;
|
||||||
let auth = AuthInfo {
|
let auth = AuthInfo {
|
||||||
userid: cred["data"]["username"].as_str().unwrap().parse()?,
|
userid: cred["data"]["username"].as_str().unwrap().parse()?,
|
||||||
|
@ -672,9 +677,13 @@ impl HttpClient {
|
||||||
&self.server
|
&self.server
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn request_builder(server: &str, method: &str, path: &str, data: Option<Value>) -> Result<Request<Body>, Error> {
|
pub fn port(&self) -> u16 {
|
||||||
|
self.port
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn request_builder(server: &str, port: u16, method: &str, path: &str, data: Option<Value>) -> Result<Request<Body>, Error> {
|
||||||
let path = path.trim_matches('/');
|
let path = path.trim_matches('/');
|
||||||
let url: Uri = format!("https://{}:8007/{}", server, path).parse()?;
|
let url: Uri = format!("https://{}:{}/{}", server, port, path).parse()?;
|
||||||
|
|
||||||
if let Some(data) = data {
|
if let Some(data) = data {
|
||||||
if method == "POST" {
|
if method == "POST" {
|
||||||
|
@ -687,7 +696,7 @@ impl HttpClient {
|
||||||
return Ok(request);
|
return Ok(request);
|
||||||
} else {
|
} else {
|
||||||
let query = tools::json_object_to_query(data)?;
|
let query = tools::json_object_to_query(data)?;
|
||||||
let url: Uri = format!("https://{}:8007/{}?{}", server, path, query).parse()?;
|
let url: Uri = format!("https://{}:{}/{}?{}", server, port, path, query).parse()?;
|
||||||
let request = Request::builder()
|
let request = Request::builder()
|
||||||
.method(method)
|
.method(method)
|
||||||
.uri(url)
|
.uri(url)
|
||||||
|
|
|
@ -439,7 +439,7 @@ pub async fn pull_group(
|
||||||
.password(Some(auth_info.ticket.clone()))
|
.password(Some(auth_info.ticket.clone()))
|
||||||
.fingerprint(fingerprint.clone());
|
.fingerprint(fingerprint.clone());
|
||||||
|
|
||||||
let new_client = HttpClient::new(src_repo.host(), src_repo.user(), options)?;
|
let new_client = HttpClient::new(src_repo.host(), src_repo.port(), src_repo.user(), options)?;
|
||||||
|
|
||||||
let reader = BackupReader::start(
|
let reader = BackupReader::start(
|
||||||
new_client,
|
new_client,
|
||||||
|
|
|
@ -39,6 +39,11 @@ pub const REMOTE_PASSWORD_SCHEMA: Schema = StringSchema::new("Password or auth t
|
||||||
host: {
|
host: {
|
||||||
schema: DNS_NAME_OR_IP_SCHEMA,
|
schema: DNS_NAME_OR_IP_SCHEMA,
|
||||||
},
|
},
|
||||||
|
port: {
|
||||||
|
optional: true,
|
||||||
|
description: "The (optional) port",
|
||||||
|
type: u16,
|
||||||
|
},
|
||||||
userid: {
|
userid: {
|
||||||
type: Userid,
|
type: Userid,
|
||||||
},
|
},
|
||||||
|
@ -58,6 +63,8 @@ pub struct Remote {
|
||||||
#[serde(skip_serializing_if="Option::is_none")]
|
#[serde(skip_serializing_if="Option::is_none")]
|
||||||
pub comment: Option<String>,
|
pub comment: Option<String>,
|
||||||
pub host: String,
|
pub host: String,
|
||||||
|
#[serde(skip_serializing_if="Option::is_none")]
|
||||||
|
pub port: Option<u16>,
|
||||||
pub userid: Userid,
|
pub userid: Userid,
|
||||||
#[serde(skip_serializing_if="String::is_empty")]
|
#[serde(skip_serializing_if="String::is_empty")]
|
||||||
#[serde(with = "proxmox::tools::serde::string_as_base64")]
|
#[serde(with = "proxmox::tools::serde::string_as_base64")]
|
||||||
|
|
Loading…
Reference in New Issue