* [pdm-devel] [PATCH proxmox{, -datacenter-manager} 0/4] generate Vec's for string-lists
@ 2025-10-21 13:50 Hannes Laimer
2025-10-21 13:50 ` [pdm-devel] [PATCH proxmox 1/3] pve-api-types: schema2rust: generate arrays for types with format `-list` Hannes Laimer
` (3 more replies)
0 siblings, 4 replies; 5+ messages in thread
From: Hannes Laimer @ 2025-10-21 13:50 UTC (permalink / raw)
To: pdm-devel
PVE does accept actual arrays[1] as input to fields with a `-list` format.
This updates the generator so it produces a Vec if it is a parameter,
not for return values.
I did not find endpoint that directly share the same format for
parameters and return value. Update endpoint also include a
`delete`-field and those were the only ones that had really similar
input and output formats. So deduplication in the generate _should_ not be
a problem here.
This required some small changes to pdm. Context for those is in the
commit itself.
[1] pve-common 69d9edcc ("section config: implement array support")
proxmox:
Hannes Laimer (3):
pve-api-types: schema2rust: generate arrays for types with format
`-list`
pve-api-types: add regex for both storage- and bridge-pair
pve-api-types: regenerate
pve-api-types/generate.pl | 11 +-
pve-api-types/generator-lib/Schema2Rust.pm | 35 ++-
pve-api-types/src/generated/code.rs | 2 +-
pve-api-types/src/generated/types.rs | 335 +++++++++++----------
pve-api-types/src/types/verifiers.rs | 10 -
5 files changed, 213 insertions(+), 180 deletions(-)
proxmox-datacenter-manager:
Hannes Laimer (1):
server: use types indead of string for migration parameters
server/src/api/pve/lxc.rs | 133 ++++++++----------------------------
server/src/api/pve/qemu.rs | 135 ++++++++-----------------------------
2 files changed, 56 insertions(+), 212 deletions(-)
Summary over all repositories:
7 files changed, 269 insertions(+), 392 deletions(-)
--
Generated by git-murpp 0.8.1
_______________________________________________
pdm-devel mailing list
pdm-devel@lists.proxmox.com
https://lists.proxmox.com/cgi-bin/mailman/listinfo/pdm-devel
^ permalink raw reply [flat|nested] 5+ messages in thread
* [pdm-devel] [PATCH proxmox 1/3] pve-api-types: schema2rust: generate arrays for types with format `-list`
2025-10-21 13:50 [pdm-devel] [PATCH proxmox{, -datacenter-manager} 0/4] generate Vec's for string-lists Hannes Laimer
@ 2025-10-21 13:50 ` Hannes Laimer
2025-10-21 13:50 ` [pdm-devel] [PATCH proxmox 2/3] pve-api-types: add regex for both storage- and bridge-pair Hannes Laimer
` (2 subsequent siblings)
3 siblings, 0 replies; 5+ messages in thread
From: Hannes Laimer @ 2025-10-21 13:50 UTC (permalink / raw)
To: pdm-devel
Since [1] the PVE API does take in actual arrays for parameters with a
format that ends in `-list`. With this we generate `Vec<String>` for
those, return types are not affected, so they will still just generate a
`String`.
[1] pve-common 69d9edcc ("section config: implement array support")
Signed-off-by: Hannes Laimer <h.laimer@proxmox.com>
---
pve-api-types/generator-lib/Schema2Rust.pm | 35 ++++++++++++++++++++--
1 file changed, 33 insertions(+), 2 deletions(-)
diff --git a/pve-api-types/generator-lib/Schema2Rust.pm b/pve-api-types/generator-lib/Schema2Rust.pm
index c5ac4aad..73ca4150 100644
--- a/pve-api-types/generator-lib/Schema2Rust.pm
+++ b/pve-api-types/generator-lib/Schema2Rust.pm
@@ -39,6 +39,7 @@ my $dedup_enum = {};
my $dedup_array_types = {};
our $__err_path = '';
+our $__list_format_as_array = undef;
my sub to_doc_comment : prototype($);
my sub strip_doc_comment : prototype($);
@@ -504,11 +505,12 @@ my sub print_method_without_body : prototype($$$$$) {
my $arg = $fields->{$name};
my $rust_name = $arg->{rust_name};
-
if ($arg->{type} eq 'Option<bool>') {
print {$out} " .maybe_bool_arg(\"$name\", p_$rust_name)\n";
} elsif ($arg->{is_string_list}) {
print {$out} " .maybe_list_arg(\"$name\", p_$rust_name)\n";
+ } elsif ($arg->{type} =~ /^Option<Vec<.*>>$/) {
+ print {$out} " .maybe_list_arg(\"$name\", &p_$rust_name)\n";
} elsif ($arg->{optional}) {
print {$out} " .maybe_arg(\"$name\", &p_$rust_name)\n";
} else {
@@ -534,7 +536,7 @@ my sub print_method_without_body : prototype($$$$$) {
if ($arg->{type} eq 'Option<bool>') {
print {$out} " .maybe_bool_arg(\"$name\", $rust_name)\n";
- } elsif ($arg->{is_string_list}) {
+ } elsif ($arg->{is_string_list} || $arg->{type} =~ /^Option<Vec<.*>>$/) {
print {$out} " .maybe_list_arg(\"$name\", &$rust_name)\n";
} elsif ($arg->{optional}) {
print {$out} " .maybe_arg(\"$name\", &$rust_name)\n";
@@ -809,6 +811,22 @@ my sub type_of : prototype($) {
my ($schema) = @_;
my $kind = $schema->{type};
+
+ # If type is 'string' but format ends with `-list`, treat as array
+ # but only for inputs to endpoints.
+ #
+ # We want a typed Vec<String>, and since [1] the PVE API does accept
+ # actual arrays for parameters with a format ending in `-list`.
+ # This is only for inputs though, so we can only have Vec's for
+ # inputs, returns are still string lists.
+ #
+ # [1] pve-common 69d9edcc ("section config: implement array support")
+ if ($kind eq 'string' && defined($schema->{format}) && $schema->{format} =~ /-list$/) {
+ if (defined($__list_format_as_array) && $__list_format_as_array) {
+ return 'array';
+ }
+ }
+
return $kind if $kind;
if (exists $schema->{properties}) {
@@ -1132,6 +1150,15 @@ my sub array_type : prototype($$$) {
optional => undef,
description => '',
};
+ if (!$schema->{items} && $schema->{format} =~ /-list$/) {
+ my $format_name = $schema->{format};
+ $format_name =~ s/-list$//;
+ $schema->{items} = {
+ description => "List item of type $format_name.",
+ format => $format_name,
+ type => 'string',
+ };
+ }
my $items = delete $schema->{items} or die "missing 'items' in array schema\n";
my $description = $items->{description};
@@ -1578,6 +1605,8 @@ my sub url_parameters : prototype($) {
my sub method_parameters : prototype($$$$$) {
my ($def, $api_url, $param_name, $api_method, $rust_method_name) = @_;
+ local $__list_format_as_array = 1;
+
my $url_params = url_parameters($api_url);
my $parameters = $api_method->{parameters} // {};
@@ -1676,6 +1705,8 @@ my sub method_parameters : prototype($$$$$) {
my sub method_return_type : prototype($$$$) {
my ($def, $method, $return_name, $extra) = @_;
+ local $__list_format_as_array = 0;
+
if (defined(my $returns = $extra->{'output-type'})) {
$def->{output_type} = $returns;
return;
--
2.47.3
_______________________________________________
pdm-devel mailing list
pdm-devel@lists.proxmox.com
https://lists.proxmox.com/cgi-bin/mailman/listinfo/pdm-devel
^ permalink raw reply [flat|nested] 5+ messages in thread
* [pdm-devel] [PATCH proxmox 2/3] pve-api-types: add regex for both storage- and bridge-pair
2025-10-21 13:50 [pdm-devel] [PATCH proxmox{, -datacenter-manager} 0/4] generate Vec's for string-lists Hannes Laimer
2025-10-21 13:50 ` [pdm-devel] [PATCH proxmox 1/3] pve-api-types: schema2rust: generate arrays for types with format `-list` Hannes Laimer
@ 2025-10-21 13:50 ` Hannes Laimer
2025-10-21 13:50 ` [pdm-devel] [PATCH proxmox 3/3] pve-api-types: regenerate Hannes Laimer
2025-10-21 13:50 ` [pdm-devel] [PATCH proxmox-datacenter-manager 1/1] server: use types indead of string for migration parameters Hannes Laimer
3 siblings, 0 replies; 5+ messages in thread
From: Hannes Laimer @ 2025-10-21 13:50 UTC (permalink / raw)
To: pdm-devel
These are used for target storage and bridge mappings in remote
migrations. For the list items we don't really need a verifier function
so remove them and add regexes instead.
What this is not doing is check for uniqueness of mappings in the list,
but that is not really possible. Since we can't currently have verifiers
on fields that are not strings.
Signed-off-by: Hannes Laimer <h.laimer@proxmox.com>
---
pve-api-types/generate.pl | 11 +++++++----
pve-api-types/src/types/verifiers.rs | 10 ----------
2 files changed, 7 insertions(+), 14 deletions(-)
diff --git a/pve-api-types/generate.pl b/pve-api-types/generate.pl
index 75729b3c..9ea9aa4f 100644
--- a/pve-api-types/generate.pl
+++ b/pve-api-types/generate.pl
@@ -31,6 +31,9 @@ Schema2Rust::init_api($pve_api->{root}, \&lookup_format);
# From JSONSchema.pm, but we can't use perl-re directly, particularly `qr//`...
my $CONFIGID_RE = '^(?i:[a-z][a-z0-9_-]+)$';
+my $STORAGEID_RE = '(?i:[a-z][a-z0-9\-_.]*[a-z0-9])';
+my $BRIDGEID_RE = '[-_.\w\d]+';
+
# Disable `#[api]` generation for now, it's incomplete/untested.
#$Schema2Rust::API = 0;
@@ -41,7 +44,7 @@ Schema2Rust::register_format('ipv4mask' => { code => 'verifiers::verify_ipv4_mas
Schema2Rust::register_format('mac-addr' => { regex => '^(?i)[a-f0-9][02468ace](?::[a-f0-9]{2}){5}$' });
## Schema2Rust::register_format('pve-acme-alias' => { code => 'verify_pve_acme_alias' });
## Schema2Rust::register_format('pve-acme-domain' => { code => 'verify_pve_acme_domain' });
-Schema2Rust::register_format('pve-bridge-id' => { regex => '^[-_.\w\d]+$' });
+Schema2Rust::register_format('pve-bridge-id' => { regex => '^'.$BRIDGEID_RE.'$' });
Schema2Rust::register_format('pve-configid' => { regex => $CONFIGID_RE });
## Schema2Rust::register_format('pve-groupid' => { code => 'verify_pve_groupid' });
Schema2Rust::register_format('pve-userid' => { code => 'verify_pve_userid' });
@@ -59,7 +62,7 @@ Schema2Rust::register_format('pve-qm-bootdev' => { unchecked => 1 });
Schema2Rust::register_format('pve-qm-bootdisk' => { regex => '^(ide|sata|scsi|virtio|efidisk|tpmstate)\d+$' });
Schema2Rust::register_format('pve-qm-usb-device' => { unchecked => 1 });
Schema2Rust::register_format('pve-startup-order' => { unchecked => 1 });
-Schema2Rust::register_format('pve-storage-id' => { regex => '^(?i:[a-z][a-z0-9\-_.]*[a-z0-9])$' });
+Schema2Rust::register_format('pve-storage-id' => { regex => '^'.$STORAGEID_RE.'$' });
Schema2Rust::register_format('pve-storage-content' => { type => 'StorageContent' });
Schema2Rust::register_format('pve-tag' => { regex => '^(?i)[a-z0-9_][a-z0-9_\-+.]*$' });
Schema2Rust::register_format('pve-volume-id' => { code => 'verifiers::verify_volume_id' });
@@ -75,8 +78,8 @@ Schema2Rust::register_format('lxc-ip-with-ll-iface' => { code => 'verifiers::ver
Schema2Rust::register_format('pve-ct-timezone' => { regex => '^.*/.*$' });
Schema2Rust::register_format('pve-lxc-dev-string' => { code => 'verifiers::verify_pve_lxc_dev_string' });
##
-Schema2Rust::register_format('storage-pair' => { code => 'verifiers::verify_storage_pair' });
-Schema2Rust::register_format('bridge-pair' => { code => 'verifiers::verify_bridge_pair' });
+Schema2Rust::register_format('storage-pair' => { regex => '^'.$STORAGEID_RE.':'.$STORAGEID_RE.'|'.$STORAGEID_RE.'|1$' });
+Schema2Rust::register_format('bridge-pair' => { regex => '^'.$BRIDGEID_RE.':'.$BRIDGEID_RE.'|'.$BRIDGEID_RE.'|1$' });
Schema2Rust::register_format('pve-task-status-type' => { regex => '^(?i:ok|error|warning|unknown)$' });
diff --git a/pve-api-types/src/types/verifiers.rs b/pve-api-types/src/types/verifiers.rs
index caefba1a..092893b3 100644
--- a/pve-api-types/src/types/verifiers.rs
+++ b/pve-api-types/src/types/verifiers.rs
@@ -194,16 +194,6 @@ pub fn verify_ip_with_ll_iface(s: &str) -> Result<(), Error> {
verify_ip(s)
}
-pub fn verify_storage_pair(_s: &str) -> Result<(), Error> {
- // FIXME: Implement this!
- Ok(())
-}
-
-pub fn verify_bridge_pair(_s: &str) -> Result<(), Error> {
- // FIXME: Implement this!
- Ok(())
-}
-
pub fn verify_pve_lxc_dev_string(s: &str) -> Result<(), Error> {
if !s.starts_with("/dev") || s.ends_with("/..") || s.contains("/..") {
bail!("not a valid device string");
--
2.47.3
_______________________________________________
pdm-devel mailing list
pdm-devel@lists.proxmox.com
https://lists.proxmox.com/cgi-bin/mailman/listinfo/pdm-devel
^ permalink raw reply [flat|nested] 5+ messages in thread
* [pdm-devel] [PATCH proxmox 3/3] pve-api-types: regenerate
2025-10-21 13:50 [pdm-devel] [PATCH proxmox{, -datacenter-manager} 0/4] generate Vec's for string-lists Hannes Laimer
2025-10-21 13:50 ` [pdm-devel] [PATCH proxmox 1/3] pve-api-types: schema2rust: generate arrays for types with format `-list` Hannes Laimer
2025-10-21 13:50 ` [pdm-devel] [PATCH proxmox 2/3] pve-api-types: add regex for both storage- and bridge-pair Hannes Laimer
@ 2025-10-21 13:50 ` Hannes Laimer
2025-10-21 13:50 ` [pdm-devel] [PATCH proxmox-datacenter-manager 1/1] server: use types indead of string for migration parameters Hannes Laimer
3 siblings, 0 replies; 5+ messages in thread
From: Hannes Laimer @ 2025-10-21 13:50 UTC (permalink / raw)
To: pdm-devel
Signed-off-by: Hannes Laimer <h.laimer@proxmox.com>
---
pve-api-types/src/generated/code.rs | 2 +-
pve-api-types/src/generated/types.rs | 335 ++++++++++++++-------------
2 files changed, 173 insertions(+), 164 deletions(-)
diff --git a/pve-api-types/src/generated/code.rs b/pve-api-types/src/generated/code.rs
index 8b3c6696..788e4bf5 100644
--- a/pve-api-types/src/generated/code.rs
+++ b/pve-api-types/src/generated/code.rs
@@ -900,7 +900,7 @@ where
.maybe_arg("since", &p_since)
.maybe_arg("source", &p_source)
.maybe_arg("start", &p_start)
- .maybe_arg("statusfilter", &p_statusfilter)
+ .maybe_list_arg("statusfilter", &p_statusfilter)
.maybe_arg("typefilter", &p_typefilter)
.maybe_arg("until", &p_until)
.maybe_arg("userfilter", &p_userfilter)
diff --git a/pve-api-types/src/generated/types.rs b/pve-api-types/src/generated/types.rs
index 98cb012b..66177998 100644
--- a/pve-api-types/src/generated/types.rs
+++ b/pve-api-types/src/generated/types.rs
@@ -1027,9 +1027,13 @@ fn test_regex_compilation_5() {
type: String,
},
"isis-ifaces": {
- format: &ApiStringFormat::Pattern(&CREATE_CONTROLLER_ISIS_IFACES_RE),
+ items: {
+ description: "List item of type pve-iface.",
+ format: &ApiStringFormat::Pattern(&CREATE_CONTROLLER_ISIS_IFACES_RE),
+ type: String,
+ },
optional: true,
- type: String,
+ type: Array,
},
"isis-net": {
format: &ApiStringFormat::Pattern(&CREATE_CONTROLLER_ISIS_NET_RE),
@@ -1050,9 +1054,13 @@ fn test_regex_compilation_5() {
type: String,
},
peers: {
- format: &ApiStringFormat::VerifyFn(verifiers::verify_ip),
+ items: {
+ description: "List item of type ip.",
+ format: &ApiStringFormat::VerifyFn(verifiers::verify_ip),
+ type: String,
+ },
optional: true,
- type: String,
+ type: Array,
},
type: {
type: ListControllersType,
@@ -1099,7 +1107,7 @@ pub struct CreateController {
/// Comma-separated list of interfaces where IS-IS should be active.
#[serde(default, skip_serializing_if = "Option::is_none")]
#[serde(rename = "isis-ifaces")]
- pub isis_ifaces: Option<String>,
+ pub isis_ifaces: Option<Vec<String>>,
/// Network Entity title for this node in the IS-IS network.
#[serde(default, skip_serializing_if = "Option::is_none")]
@@ -1121,7 +1129,7 @@ pub struct CreateController {
/// peers address list.
#[serde(default, skip_serializing_if = "Option::is_none")]
- pub peers: Option<String>,
+ pub peers: Option<Vec<String>>,
#[serde(rename = "type")]
pub ty: ListControllersType,
@@ -1381,9 +1389,13 @@ fn test_regex_compilation_6() {
type: Integer,
},
exitnodes: {
- format: &ApiStringFormat::Pattern(&CREATE_ZONE_EXITNODES_RE),
+ items: {
+ description: "List item of type pve-node.",
+ format: &ApiStringFormat::Pattern(&CREATE_ZONE_EXITNODES_RE),
+ type: String,
+ },
optional: true,
- type: String,
+ type: Array,
},
"exitnodes-local-routing": {
default: false,
@@ -1417,23 +1429,35 @@ fn test_regex_compilation_6() {
type: Integer,
},
nodes: {
- format: &ApiStringFormat::Pattern(&CREATE_ZONE_NODES_RE),
+ items: {
+ description: "List item of type pve-node.",
+ format: &ApiStringFormat::Pattern(&CREATE_ZONE_NODES_RE),
+ type: String,
+ },
optional: true,
- type: String,
+ type: Array,
},
peers: {
- format: &ApiStringFormat::VerifyFn(verifiers::verify_ip),
+ items: {
+ description: "List item of type ip.",
+ format: &ApiStringFormat::VerifyFn(verifiers::verify_ip),
+ type: String,
+ },
optional: true,
- type: String,
+ type: Array,
},
reversedns: {
optional: true,
type: String,
},
"rt-import": {
- format: &ApiStringFormat::VerifyFn(verifiers::verify_sdn_bgp_rt),
+ items: {
+ description: "List item of type pve-sdn-bgp-rt.",
+ format: &ApiStringFormat::VerifyFn(verifiers::verify_sdn_bgp_rt),
+ type: String,
+ },
optional: true,
- type: String,
+ type: Array,
},
tag: {
minimum: 0,
@@ -1515,7 +1539,7 @@ pub struct CreateZone {
/// List of cluster node names.
#[serde(default, skip_serializing_if = "Option::is_none")]
- pub exitnodes: Option<String>,
+ pub exitnodes: Option<Vec<String>>,
/// Allow exitnodes to connect to EVPN guests.
#[serde(deserialize_with = "proxmox_serde::perl::deserialize_bool")]
@@ -1552,12 +1576,12 @@ pub struct CreateZone {
/// List of cluster node names.
#[serde(default, skip_serializing_if = "Option::is_none")]
- pub nodes: Option<String>,
+ pub nodes: Option<Vec<String>>,
/// Comma-separated list of peers, that are part of the VXLAN zone. Usually
/// the IPs of the nodes.
#[serde(default, skip_serializing_if = "Option::is_none")]
- pub peers: Option<String>,
+ pub peers: Option<Vec<String>>,
/// reverse dns api server
#[serde(default, skip_serializing_if = "Option::is_none")]
@@ -1566,7 +1590,7 @@ pub struct CreateZone {
/// List of Route Targets that should be imported into the VRF of the zone.
#[serde(default, skip_serializing_if = "Option::is_none")]
#[serde(rename = "rt-import")]
- pub rt_import: Option<String>,
+ pub rt_import: Option<Vec<String>>,
/// Service-VLAN Tag (outer VLAN)
#[serde(deserialize_with = "proxmox_serde::perl::deserialize_u64")]
@@ -1610,101 +1634,6 @@ pub enum IsRunning {
serde_plain::derive_display_from_serialize!(IsRunning);
serde_plain::derive_fromstr_from_deserialize!(IsRunning);
-const LIST_STORAGES_CONTENT: Schema =
- proxmox_schema::ArraySchema::new("list", &StorageContent::API_SCHEMA).schema();
-
-mod list_storages_content {
- use serde::{Deserialize, Deserializer, Serialize, Serializer};
-
- #[doc(hidden)]
- pub trait Ser: Sized {
- fn ser<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error>;
- fn de<'de, D>(deserializer: D) -> Result<Self, D::Error>
- where
- D: Deserializer<'de>;
- }
-
- impl<T: Serialize + for<'a> Deserialize<'a>> Ser for Vec<T> {
- fn ser<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
- where
- S: Serializer,
- {
- super::stringlist::serialize(&self[..], serializer, &super::LIST_STORAGES_CONTENT)
- }
-
- fn de<'de, D>(deserializer: D) -> Result<Self, D::Error>
- where
- D: Deserializer<'de>,
- {
- super::stringlist::deserialize(deserializer, &super::LIST_STORAGES_CONTENT)
- }
- }
-
- impl<T: Ser> Ser for Option<T> {
- fn ser<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
- where
- S: Serializer,
- {
- match self {
- None => serializer.serialize_none(),
- Some(inner) => inner.ser(serializer),
- }
- }
-
- fn de<'de, D>(deserializer: D) -> Result<Self, D::Error>
- where
- D: Deserializer<'de>,
- {
- use std::fmt;
- use std::marker::PhantomData;
-
- struct V<T: Ser>(PhantomData<T>);
-
- impl<'de, T: Ser> serde::de::Visitor<'de> for V<T> {
- type Value = Option<T>;
-
- fn expecting(&self, f: &mut fmt::Formatter) -> fmt::Result {
- f.write_str("an optional string")
- }
-
- fn visit_none<E: serde::de::Error>(self) -> Result<Self::Value, E> {
- Ok(None)
- }
-
- fn visit_some<D>(self, deserializer: D) -> Result<Self::Value, D::Error>
- where
- D: Deserializer<'de>,
- {
- T::de(deserializer).map(Some)
- }
-
- fn visit_str<E: serde::de::Error>(self, value: &str) -> Result<Self::Value, E> {
- use serde::de::IntoDeserializer;
- T::de(value.into_deserializer()).map(Some)
- }
- }
-
- deserializer.deserialize_option(V::<T>(PhantomData))
- }
- }
-
- pub fn serialize<T, S>(this: &T, serializer: S) -> Result<S::Ok, S::Error>
- where
- S: serde::Serializer,
- T: Ser,
- {
- this.ser(serializer)
- }
-
- pub fn deserialize<'de, T, D>(deserializer: D) -> Result<T, D::Error>
- where
- D: serde::Deserializer<'de>,
- T: Ser,
- {
- T::de(deserializer)
- }
-}
-
#[api]
/// Only list sdn controllers of specific type
#[derive(Clone, Copy, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize)]
@@ -1864,9 +1793,13 @@ fn test_regex_compilation_7() {
type: Integer,
},
statusfilter: {
- format: &ApiStringFormat::Pattern(&LIST_TASKS_STATUSFILTER_RE),
+ items: {
+ description: "List item of type pve-task-status-type.",
+ format: &ApiStringFormat::Pattern(&LIST_TASKS_STATUSFILTER_RE),
+ type: String,
+ },
optional: true,
- type: String,
+ type: Array,
},
typefilter: {
optional: true,
@@ -1916,7 +1849,7 @@ pub struct ListTasks {
/// List of Task States that should be returned.
#[serde(default, skip_serializing_if = "Option::is_none")]
- pub statusfilter: Option<String>,
+ pub statusfilter: Option<Vec<String>>,
/// Only list tasks of this type (e.g., vzstart, vzdump).
#[serde(default, skip_serializing_if = "Option::is_none")]
@@ -3450,6 +3383,7 @@ pub struct LxcStatus {
const_regex! {
MIGRATE_LXC_TARGET_RE = r##"^(?i:[a-z0-9](?i:[a-z0-9\-]*[a-z0-9])?)$"##;
+MIGRATE_LXC_TARGET_STORAGE_RE = r##"^(?i:[a-z][a-z0-9\-_.]*[a-z0-9]):(?i:[a-z][a-z0-9\-_.]*[a-z0-9])|(?i:[a-z][a-z0-9\-_.]*[a-z0-9])|1$"##;
}
@@ -3457,6 +3391,7 @@ MIGRATE_LXC_TARGET_RE = r##"^(?i:[a-z0-9](?i:[a-z0-9\-]*[a-z0-9])?)$"##;
fn test_regex_compilation_12() {
use regex::Regex;
let _: &Regex = &MIGRATE_LXC_TARGET_RE;
+ let _: &Regex = &MIGRATE_LXC_TARGET_STORAGE_RE;
}
#[api(
properties: {
@@ -3477,9 +3412,13 @@ fn test_regex_compilation_12() {
type: String,
},
"target-storage": {
- format: &ApiStringFormat::VerifyFn(verifiers::verify_storage_pair),
+ items: {
+ description: "List item of type storage-pair.",
+ format: &ApiStringFormat::Pattern(&MIGRATE_LXC_TARGET_STORAGE_RE),
+ type: String,
+ },
optional: true,
- type: String,
+ type: Array,
},
timeout: {
default: 180,
@@ -3514,7 +3453,7 @@ pub struct MigrateLxc {
/// '1' will map each source storage to itself.
#[serde(default, skip_serializing_if = "Option::is_none")]
#[serde(rename = "target-storage")]
- pub target_storage: Option<String>,
+ pub target_storage: Option<Vec<String>>,
/// Timeout in seconds for shutdown for restart migration
#[serde(deserialize_with = "proxmox_serde::perl::deserialize_i64")]
@@ -3525,6 +3464,7 @@ pub struct MigrateLxc {
const_regex! {
MIGRATE_QEMU_TARGET_RE = r##"^(?i:[a-z0-9](?i:[a-z0-9\-]*[a-z0-9])?)$"##;
+MIGRATE_QEMU_TARGETSTORAGE_RE = r##"^(?i:[a-z][a-z0-9\-_.]*[a-z0-9]):(?i:[a-z][a-z0-9\-_.]*[a-z0-9])|(?i:[a-z][a-z0-9\-_.]*[a-z0-9])|1$"##;
}
@@ -3532,6 +3472,7 @@ MIGRATE_QEMU_TARGET_RE = r##"^(?i:[a-z0-9](?i:[a-z0-9\-]*[a-z0-9])?)$"##;
fn test_regex_compilation_13() {
use regex::Regex;
let _: &Regex = &MIGRATE_QEMU_TARGET_RE;
+ let _: &Regex = &MIGRATE_QEMU_TARGETSTORAGE_RE;
}
#[api(
properties: {
@@ -3562,9 +3503,13 @@ fn test_regex_compilation_13() {
type: String,
},
targetstorage: {
- format: &ApiStringFormat::VerifyFn(verifiers::verify_storage_pair),
+ items: {
+ description: "List item of type storage-pair.",
+ format: &ApiStringFormat::Pattern(&MIGRATE_QEMU_TARGETSTORAGE_RE),
+ type: String,
+ },
optional: true,
- type: String,
+ type: Array,
},
"with-conntrack-state": {
default: false,
@@ -3609,7 +3554,7 @@ pub struct MigrateQemu {
/// ID maps all source storages to that storage. Providing the special value
/// '1' will map each source storage to itself.
#[serde(default, skip_serializing_if = "Option::is_none")]
- pub targetstorage: Option<String>,
+ pub targetstorage: Option<Vec<String>>,
/// Whether to migrate conntrack entries for running VMs.
#[serde(deserialize_with = "proxmox_serde::perl::deserialize_bool")]
@@ -9762,6 +9707,19 @@ pub struct ReloadSdn {
pub release_lock: Option<bool>,
}
+const_regex! {
+
+REMOTE_MIGRATE_LXC_TARGET_BRIDGE_RE = r##"^[-_.\w\d]+:[-_.\w\d]+|[-_.\w\d]+|1$"##;
+REMOTE_MIGRATE_LXC_TARGET_STORAGE_RE = r##"^(?i:[a-z][a-z0-9\-_.]*[a-z0-9]):(?i:[a-z][a-z0-9\-_.]*[a-z0-9])|(?i:[a-z][a-z0-9\-_.]*[a-z0-9])|1$"##;
+
+}
+
+#[test]
+fn test_regex_compilation_26() {
+ use regex::Regex;
+ let _: &Regex = &REMOTE_MIGRATE_LXC_TARGET_BRIDGE_RE;
+ let _: &Regex = &REMOTE_MIGRATE_LXC_TARGET_STORAGE_RE;
+}
#[api(
properties: {
bwlimit: {
@@ -9781,16 +9739,24 @@ pub struct ReloadSdn {
optional: true,
},
"target-bridge": {
- format: &ApiStringFormat::VerifyFn(verifiers::verify_bridge_pair),
- type: String,
+ items: {
+ description: "List item of type bridge-pair.",
+ format: &ApiStringFormat::Pattern(&REMOTE_MIGRATE_LXC_TARGET_BRIDGE_RE),
+ type: String,
+ },
+ type: Array,
},
"target-endpoint": {
format: &ApiStringFormat::PropertyString(&ProxmoxRemote::API_SCHEMA),
type: String,
},
"target-storage": {
- format: &ApiStringFormat::VerifyFn(verifiers::verify_storage_pair),
- type: String,
+ items: {
+ description: "List item of type storage-pair.",
+ format: &ApiStringFormat::Pattern(&REMOTE_MIGRATE_LXC_TARGET_STORAGE_RE),
+ type: String,
+ },
+ type: Array,
},
"target-vmid": {
maximum: 999999999,
@@ -9834,7 +9800,7 @@ pub struct RemoteMigrateLxc {
/// maps all source bridges to that bridge. Providing the special value '1'
/// will map each source bridge to itself.
#[serde(rename = "target-bridge")]
- pub target_bridge: String,
+ pub target_bridge: Vec<String>,
/// Remote target endpoint
#[serde(rename = "target-endpoint")]
@@ -9844,7 +9810,7 @@ pub struct RemoteMigrateLxc {
/// ID maps all source storages to that storage. Providing the special value
/// '1' will map each source storage to itself.
#[serde(rename = "target-storage")]
- pub target_storage: String,
+ pub target_storage: Vec<String>,
/// The (unique) ID of the VM.
#[serde(deserialize_with = "proxmox_serde::perl::deserialize_u32")]
@@ -9858,6 +9824,19 @@ pub struct RemoteMigrateLxc {
pub timeout: Option<i64>,
}
+const_regex! {
+
+REMOTE_MIGRATE_QEMU_TARGET_BRIDGE_RE = r##"^[-_.\w\d]+:[-_.\w\d]+|[-_.\w\d]+|1$"##;
+REMOTE_MIGRATE_QEMU_TARGET_STORAGE_RE = r##"^(?i:[a-z][a-z0-9\-_.]*[a-z0-9]):(?i:[a-z][a-z0-9\-_.]*[a-z0-9])|(?i:[a-z][a-z0-9\-_.]*[a-z0-9])|1$"##;
+
+}
+
+#[test]
+fn test_regex_compilation_27() {
+ use regex::Regex;
+ let _: &Regex = &REMOTE_MIGRATE_QEMU_TARGET_BRIDGE_RE;
+ let _: &Regex = &REMOTE_MIGRATE_QEMU_TARGET_STORAGE_RE;
+}
#[api(
properties: {
bwlimit: {
@@ -9874,16 +9853,24 @@ pub struct RemoteMigrateLxc {
optional: true,
},
"target-bridge": {
- format: &ApiStringFormat::VerifyFn(verifiers::verify_bridge_pair),
- type: String,
+ items: {
+ description: "List item of type bridge-pair.",
+ format: &ApiStringFormat::Pattern(&REMOTE_MIGRATE_QEMU_TARGET_BRIDGE_RE),
+ type: String,
+ },
+ type: Array,
},
"target-endpoint": {
format: &ApiStringFormat::PropertyString(&ProxmoxRemote::API_SCHEMA),
type: String,
},
"target-storage": {
- format: &ApiStringFormat::VerifyFn(verifiers::verify_storage_pair),
- type: String,
+ items: {
+ description: "List item of type storage-pair.",
+ format: &ApiStringFormat::Pattern(&REMOTE_MIGRATE_QEMU_TARGET_STORAGE_RE),
+ type: String,
+ },
+ type: Array,
},
"target-vmid": {
maximum: 999999999,
@@ -9917,7 +9904,7 @@ pub struct RemoteMigrateQemu {
/// maps all source bridges to that bridge. Providing the special value '1'
/// will map each source bridge to itself.
#[serde(rename = "target-bridge")]
- pub target_bridge: String,
+ pub target_bridge: Vec<String>,
/// Remote target endpoint
#[serde(rename = "target-endpoint")]
@@ -9927,7 +9914,7 @@ pub struct RemoteMigrateQemu {
/// ID maps all source storages to that storage. Providing the special value
/// '1' will map each source storage to itself.
#[serde(rename = "target-storage")]
- pub target_storage: String,
+ pub target_storage: Vec<String>,
/// The (unique) ID of the VM.
#[serde(deserialize_with = "proxmox_serde::perl::deserialize_u32")]
@@ -10162,7 +10149,7 @@ SDN_CONTROLLER_ISIS_NET_RE = r##"^[a-fA-F0-9]{2}(\.[a-fA-F0-9]{4}){3,9}\.[a-fA-F
}
#[test]
-fn test_regex_compilation_26() {
+fn test_regex_compilation_28() {
use regex::Regex;
let _: &Regex = &SDN_CONTROLLER_ISIS_IFACES_RE;
let _: &Regex = &SDN_CONTROLLER_ISIS_NET_RE;
@@ -10315,7 +10302,7 @@ SDN_CONTROLLER_PENDING_ISIS_NET_RE = r##"^[a-fA-F0-9]{2}(\.[a-fA-F0-9]{4}){3,9}\
}
#[test]
-fn test_regex_compilation_27() {
+fn test_regex_compilation_29() {
use regex::Regex;
let _: &Regex = &SDN_CONTROLLER_PENDING_ISIS_IFACES_RE;
let _: &Regex = &SDN_CONTROLLER_PENDING_ISIS_NET_RE;
@@ -10610,7 +10597,7 @@ SDN_ZONE_EXITNODES_PRIMARY_RE = r##"^(?i:[a-z0-9](?i:[a-z0-9\-]*[a-z0-9])?)$"##;
}
#[test]
-fn test_regex_compilation_28() {
+fn test_regex_compilation_30() {
use regex::Regex;
let _: &Regex = &SDN_ZONE_EXITNODES_RE;
let _: &Regex = &SDN_ZONE_EXITNODES_PRIMARY_RE;
@@ -10884,7 +10871,7 @@ SDN_ZONE_PENDING_EXITNODES_PRIMARY_RE = r##"^(?i:[a-z0-9](?i:[a-z0-9\-]*[a-z0-9]
}
#[test]
-fn test_regex_compilation_29() {
+fn test_regex_compilation_31() {
use regex::Regex;
let _: &Regex = &SDN_ZONE_PENDING_EXITNODES_RE;
let _: &Regex = &SDN_ZONE_PENDING_EXITNODES_PRIMARY_RE;
@@ -11209,13 +11196,15 @@ pub struct StartLxc {
const_regex! {
START_QEMU_MIGRATEDFROM_RE = r##"^(?i:[a-z0-9](?i:[a-z0-9\-]*[a-z0-9])?)$"##;
+START_QEMU_TARGETSTORAGE_RE = r##"^(?i:[a-z][a-z0-9\-_.]*[a-z0-9]):(?i:[a-z][a-z0-9\-_.]*[a-z0-9])|(?i:[a-z][a-z0-9\-_.]*[a-z0-9])|1$"##;
}
#[test]
-fn test_regex_compilation_30() {
+fn test_regex_compilation_32() {
use regex::Regex;
let _: &Regex = &START_QEMU_MIGRATEDFROM_RE;
+ let _: &Regex = &START_QEMU_TARGETSTORAGE_RE;
}
#[api(
properties: {
@@ -11256,9 +11245,13 @@ fn test_regex_compilation_30() {
type: String,
},
targetstorage: {
- format: &ApiStringFormat::VerifyFn(verifiers::verify_storage_pair),
+ items: {
+ description: "List item of type storage-pair.",
+ format: &ApiStringFormat::Pattern(&START_QEMU_TARGETSTORAGE_RE),
+ type: String,
+ },
optional: true,
- type: String,
+ type: Array,
},
timeout: {
default: 30,
@@ -11316,7 +11309,7 @@ pub struct StartQemu {
/// ID maps all source storages to that storage. Providing the special value
/// '1' will map each source storage to itself.
#[serde(default, skip_serializing_if = "Option::is_none")]
- pub targetstorage: Option<String>,
+ pub targetstorage: Option<Vec<String>>,
/// Wait maximal timeout seconds.
#[serde(deserialize_with = "proxmox_serde::perl::deserialize_u64")]
@@ -11379,7 +11372,7 @@ STOP_QEMU_MIGRATEDFROM_RE = r##"^(?i:[a-z0-9](?i:[a-z0-9\-]*[a-z0-9])?)$"##;
}
#[test]
-fn test_regex_compilation_31() {
+fn test_regex_compilation_33() {
use regex::Regex;
let _: &Regex = &STOP_QEMU_MIGRATEDFROM_RE;
}
@@ -11478,7 +11471,7 @@ STORAGE_INFO_STORAGE_RE = r##"^(?i:[a-z][a-z0-9\-_.]*[a-z0-9])$"##;
}
#[test]
-fn test_regex_compilation_32() {
+fn test_regex_compilation_34() {
use regex::Regex;
let _: &Regex = &STORAGE_INFO_STORAGE_RE;
}
@@ -11756,7 +11749,7 @@ UPDATE_QEMU_CONFIG_VMSTATESTORAGE_RE = r##"^(?i:[a-z][a-z0-9\-_.]*[a-z0-9])$"##;
}
#[test]
-fn test_regex_compilation_33() {
+fn test_regex_compilation_35() {
use regex::Regex;
let _: &Regex = &UPDATE_QEMU_CONFIG_AFFINITY_RE;
let _: &Regex = &UPDATE_QEMU_CONFIG_BOOTDISK_RE;
@@ -11882,9 +11875,13 @@ fn test_regex_compilation_33() {
type: Integer,
},
delete: {
- format: &ApiStringFormat::Pattern(&UPDATE_QEMU_CONFIG_DELETE_RE),
+ items: {
+ description: "List item of type pve-configid.",
+ format: &ApiStringFormat::Pattern(&UPDATE_QEMU_CONFIG_DELETE_RE),
+ type: String,
+ },
optional: true,
- type: String,
+ type: Array,
},
description: {
max_length: 8192,
@@ -11989,9 +11986,13 @@ fn test_regex_compilation_33() {
type: String,
},
nameserver: {
- format: &ApiStringFormat::VerifyFn(verifiers::verify_address),
+ items: {
+ description: "List item of type address.",
+ format: &ApiStringFormat::VerifyFn(verifiers::verify_address),
+ type: String,
+ },
optional: true,
- type: String,
+ type: Array,
},
net: {
type: QemuConfigNetArray,
@@ -12023,9 +12024,13 @@ fn test_regex_compilation_33() {
optional: true,
},
revert: {
- format: &ApiStringFormat::Pattern(&UPDATE_QEMU_CONFIG_REVERT_RE),
+ items: {
+ description: "List item of type pve-configid.",
+ format: &ApiStringFormat::Pattern(&UPDATE_QEMU_CONFIG_REVERT_RE),
+ type: String,
+ },
optional: true,
- type: String,
+ type: Array,
},
rng0: {
format: &ApiStringFormat::PropertyString(&PveQmRng::API_SCHEMA),
@@ -12104,9 +12109,13 @@ fn test_regex_compilation_33() {
optional: true,
},
tags: {
- format: &ApiStringFormat::Pattern(&UPDATE_QEMU_CONFIG_TAGS_RE),
+ items: {
+ description: "List item of type pve-tag.",
+ format: &ApiStringFormat::Pattern(&UPDATE_QEMU_CONFIG_TAGS_RE),
+ type: String,
+ },
optional: true,
- type: String,
+ type: Array,
},
tdf: {
default: false,
@@ -12274,7 +12283,7 @@ pub struct UpdateQemuConfig {
/// A list of settings you want to delete.
#[serde(default, skip_serializing_if = "Option::is_none")]
- pub delete: Option<String>,
+ pub delete: Option<Vec<String>>,
/// Description for the VM. Shown in the web-interface VM's summary. This is
/// saved as comment inside the configuration file.
@@ -12413,7 +12422,7 @@ pub struct UpdateQemuConfig {
/// automatically use the setting from the host if neither searchdomain nor
/// nameserver are set.
#[serde(default, skip_serializing_if = "Option::is_none")]
- pub nameserver: Option<String>,
+ pub nameserver: Option<Vec<String>>,
/// Specify network devices.
#[serde(flatten)]
@@ -12453,7 +12462,7 @@ pub struct UpdateQemuConfig {
/// Revert a pending change.
#[serde(default, skip_serializing_if = "Option::is_none")]
- pub revert: Option<String>,
+ pub revert: Option<Vec<String>>,
/// Configure a VirtIO-based Random Number Generator.
#[serde(default, skip_serializing_if = "Option::is_none")]
@@ -12538,7 +12547,7 @@ pub struct UpdateQemuConfig {
/// Tags of the VM. This is only meta information.
#[serde(default, skip_serializing_if = "Option::is_none")]
- pub tags: Option<String>,
+ pub tags: Option<Vec<String>>,
/// Enable/disable time drift fix.
#[serde(deserialize_with = "proxmox_serde::perl::deserialize_bool")]
@@ -12649,7 +12658,7 @@ UPDATE_QEMU_CONFIG_EFIDISK0_SIZE_RE = r##"^(\d+(\.\d+)?)([KMGT])?$"##;
}
#[test]
-fn test_regex_compilation_34() {
+fn test_regex_compilation_36() {
use regex::Regex;
let _: &Regex = &UPDATE_QEMU_CONFIG_EFIDISK0_SIZE_RE;
}
@@ -12726,7 +12735,7 @@ UPDATE_QEMU_CONFIG_IDE_SIZE_RE = r##"^(\d+(\.\d+)?)([KMGT])?$"##;
}
#[test]
-fn test_regex_compilation_35() {
+fn test_regex_compilation_37() {
use regex::Regex;
let _: &Regex = &UPDATE_QEMU_CONFIG_IDE_MODEL_RE;
let _: &Regex = &UPDATE_QEMU_CONFIG_IDE_SERIAL_RE;
@@ -13082,7 +13091,7 @@ UPDATE_QEMU_CONFIG_SATA_SIZE_RE = r##"^(\d+(\.\d+)?)([KMGT])?$"##;
}
#[test]
-fn test_regex_compilation_36() {
+fn test_regex_compilation_38() {
use regex::Regex;
let _: &Regex = &UPDATE_QEMU_CONFIG_SATA_SERIAL_RE;
let _: &Regex = &UPDATE_QEMU_CONFIG_SATA_SIZE_RE;
@@ -13427,7 +13436,7 @@ UPDATE_QEMU_CONFIG_SCSI_SIZE_RE = r##"^(\d+(\.\d+)?)([KMGT])?$"##;
}
#[test]
-fn test_regex_compilation_37() {
+fn test_regex_compilation_39() {
use regex::Regex;
let _: &Regex = &UPDATE_QEMU_CONFIG_SCSI_SERIAL_RE;
let _: &Regex = &UPDATE_QEMU_CONFIG_SCSI_SIZE_RE;
@@ -13827,7 +13836,7 @@ UPDATE_QEMU_CONFIG_TPMSTATE0_SIZE_RE = r##"^(\d+(\.\d+)?)([KMGT])?$"##;
}
#[test]
-fn test_regex_compilation_38() {
+fn test_regex_compilation_40() {
use regex::Regex;
let _: &Regex = &UPDATE_QEMU_CONFIG_TPMSTATE0_SIZE_RE;
}
@@ -13883,7 +13892,7 @@ UPDATE_QEMU_CONFIG_VIRTIO_SIZE_RE = r##"^(\d+(\.\d+)?)([KMGT])?$"##;
}
#[test]
-fn test_regex_compilation_39() {
+fn test_regex_compilation_41() {
use regex::Regex;
let _: &Regex = &UPDATE_QEMU_CONFIG_VIRTIO_SERIAL_RE;
let _: &Regex = &UPDATE_QEMU_CONFIG_VIRTIO_SIZE_RE;
--
2.47.3
_______________________________________________
pdm-devel mailing list
pdm-devel@lists.proxmox.com
https://lists.proxmox.com/cgi-bin/mailman/listinfo/pdm-devel
^ permalink raw reply [flat|nested] 5+ messages in thread
* [pdm-devel] [PATCH proxmox-datacenter-manager 1/1] server: use types indead of string for migration parameters
2025-10-21 13:50 [pdm-devel] [PATCH proxmox{, -datacenter-manager} 0/4] generate Vec's for string-lists Hannes Laimer
` (2 preceding siblings ...)
2025-10-21 13:50 ` [pdm-devel] [PATCH proxmox 3/3] pve-api-types: regenerate Hannes Laimer
@ 2025-10-21 13:50 ` Hannes Laimer
3 siblings, 0 replies; 5+ messages in thread
From: Hannes Laimer @ 2025-10-21 13:50 UTC (permalink / raw)
To: pdm-devel
This is specifically about target-storage and target-bridge, these were
strings, but are now actaul Vec's with mappings as items. Since
pve-api-types now generates Vec's instead of Strings for list parameters
we can directly use the type.
Signed-off-by: Hannes Laimer <h.laimer@proxmox.com>
---
server/src/api/pve/lxc.rs | 133 ++++++++----------------------------
server/src/api/pve/qemu.rs | 135 ++++++++-----------------------------
2 files changed, 56 insertions(+), 212 deletions(-)
diff --git a/server/src/api/pve/lxc.rs b/server/src/api/pve/lxc.rs
index 83f9f4a..61db8ff 100644
--- a/server/src/api/pve/lxc.rs
+++ b/server/src/api/pve/lxc.rs
@@ -288,29 +288,10 @@ pub async fn lxc_shutdown(
schema: NODE_SCHEMA,
optional: true,
},
- target: { schema: NODE_SCHEMA },
vmid: { schema: VMID_SCHEMA },
- online: {
- type: bool,
- description: "Attempt an online migration if the container is running.",
- optional: true,
- },
- restart: {
- type: bool,
- description: "Perform a restart-migration if the container is running.",
- optional: true,
- },
- "target-storage": {
- description: "Mapping of source storages to target storages.",
- optional: true,
- },
- bwlimit: {
- description: "Override I/O bandwidth limit (in KiB/s).",
- optional: true,
- },
- timeout: {
- description: "Shutdown timeout for restart-migrations.",
- optional: true,
+ migrate: {
+ type: pve_api_types::MigrateLxc,
+ flatten: true,
},
},
},
@@ -327,35 +308,23 @@ pub async fn lxc_migrate(
remote: String,
node: Option<String>,
vmid: u32,
- bwlimit: Option<u64>,
- restart: Option<bool>,
- online: Option<bool>,
- target: String,
- target_storage: Option<String>,
- timeout: Option<i64>,
+ migrate: pve_api_types::MigrateLxc,
) -> Result<RemoteUpid, Error> {
- let bwlimit = bwlimit.map(|n| n as f64);
-
- log::info!("in-cluster migration requested for remote {remote:?} ct {vmid} to node {target:?}");
+ log::info!(
+ "in-cluster migration requested for remote {remote:?} ct {vmid} to node {:?}",
+ migrate.target
+ );
let (remotes, _) = pdm_config::remotes::config()?;
let pve = connect_to_remote(&remotes, &remote)?;
let node = find_node_for_vm(node, vmid, pve.as_ref()).await?;
- if node == target {
+ if node == migrate.target {
bail!("refusing migration to the same node");
}
- let params = pve_api_types::MigrateLxc {
- bwlimit,
- online,
- restart,
- target,
- target_storage,
- timeout,
- };
- let upid = pve.migrate_lxc(&node, vmid, params).await?;
+ let upid = pve.migrate_lxc(&node, vmid, migrate).await?;
new_remote_upid(remote, upid).await
}
@@ -370,44 +339,10 @@ pub async fn lxc_migrate(
optional: true,
},
vmid: { schema: VMID_SCHEMA },
- "target-vmid": {
- optional: true,
- schema: VMID_SCHEMA,
- },
- delete: {
- description: "Delete the original VM and related data after successful migration.",
- optional: true,
- default: false,
- },
- online: {
- type: bool,
- description: "Perform an online migration if the vm is running.",
- optional: true,
- default: false,
- },
- "target-storage": {
- description: "Mapping of source storages to target storages.",
- },
- "target-bridge": {
- description: "Mapping of source bridges to remote bridges.",
- },
- bwlimit: {
- description: "Override I/O bandwidth limit (in KiB/s).",
- optional: true,
- },
- restart: {
- description: "Perform a restart-migration.",
- optional: true,
- },
- timeout: {
- description: "Add a shutdown timeout for the restart-migration.",
- optional: true,
- },
// TODO better to change remote migration to proxy to node?
- "target-endpoint": {
- type: String,
- optional: true,
- description: "The target endpoint to use for the connection.",
+ remote_migrate: {
+ type: pve_api_types::RemoteMigrateLxc,
+ flatten: true,
},
},
},
@@ -425,15 +360,7 @@ pub async fn lxc_remote_migrate(
target: String, // this is the destination remote name
node: Option<String>,
vmid: u32,
- target_vmid: Option<u32>,
- delete: bool,
- online: bool,
- target_storage: String,
- target_bridge: String,
- bwlimit: Option<u64>,
- restart: Option<bool>,
- timeout: Option<i64>,
- target_endpoint: Option<String>,
+ remote_migrate: pve_api_types::RemoteMigrateLxc,
rpcenv: &mut dyn RpcEnvironment,
) -> Result<RemoteUpid, Error> {
let user_info = CachedUserInfo::new()?;
@@ -447,7 +374,7 @@ pub async fn lxc_remote_migrate(
"resource",
&target,
"guest",
- &target_vmid.unwrap_or(vmid).to_string(),
+ &remote_migrate.target_vmid.unwrap_or(vmid).to_string(),
],
);
if target_privs & PRIV_RESOURCE_MIGRATE == 0 {
@@ -456,7 +383,7 @@ pub async fn lxc_remote_migrate(
"missing PRIV_RESOURCE_MIGRATE on target remote+vmid"
);
}
- if delete {
+ if remote_migrate.delete.unwrap_or_default() {
check_guest_delete_perms(rpcenv, &remote, vmid)?;
}
@@ -477,14 +404,17 @@ pub async fn lxc_remote_migrate(
// FIXME: For now we'll only try with the first node but we should probably try others, too, in
// case some are offline?
+ // TODO: target_endpoint optional? if single node i guess
let target_node = target
.nodes
.iter()
- .find(|endpoint| match target_endpoint.as_deref() {
- Some(target) => target == endpoint.hostname,
- None => true,
- })
- .ok_or_else(|| match target_endpoint {
+ .find(
+ |endpoint| match Some(remote_migrate.target_endpoint.clone()).as_deref() {
+ Some(target) => target == endpoint.hostname,
+ None => true,
+ },
+ )
+ .ok_or_else(|| match Some(remote_migrate.target_endpoint.clone()) {
Some(endpoint) => format_err!("{endpoint} not configured for target cluster"),
None => format_err!("no nodes configured for target cluster"),
})?;
@@ -504,19 +434,10 @@ pub async fn lxc_remote_migrate(
}
log::info!("forwarding remote migration requested");
- let params = pve_api_types::RemoteMigrateLxc {
- target_bridge,
- target_storage,
- delete: Some(delete),
- online: Some(online),
- target_vmid,
- target_endpoint,
- bwlimit: bwlimit.map(|limit| limit as f64),
- restart,
- timeout,
- };
log::info!("migrating vm {vmid} of node {node:?}");
- let upid = source_conn.remote_migrate_lxc(&node, vmid, params).await?;
+ let upid = source_conn
+ .remote_migrate_lxc(&node, vmid, remote_migrate)
+ .await?;
new_remote_upid(source, upid).await
}
diff --git a/server/src/api/pve/qemu.rs b/server/src/api/pve/qemu.rs
index 54ede11..6158bef 100644
--- a/server/src/api/pve/qemu.rs
+++ b/server/src/api/pve/qemu.rs
@@ -10,11 +10,11 @@ use proxmox_sortable_macro::sortable;
use pdm_api_types::remotes::REMOTE_ID_SCHEMA;
use pdm_api_types::{
- Authid, ConfigurationState, RemoteUpid, CIDR_FORMAT, NODE_SCHEMA, PRIV_RESOURCE_AUDIT,
- PRIV_RESOURCE_MANAGE, PRIV_RESOURCE_MIGRATE, SNAPSHOT_NAME_SCHEMA, VMID_SCHEMA,
+ Authid, ConfigurationState, RemoteUpid, NODE_SCHEMA, PRIV_RESOURCE_AUDIT, PRIV_RESOURCE_MANAGE,
+ PRIV_RESOURCE_MIGRATE, SNAPSHOT_NAME_SCHEMA, VMID_SCHEMA,
};
-use pve_api_types::{QemuMigratePreconditions, StartQemuMigrationType};
+use pve_api_types::QemuMigratePreconditions;
use crate::api::pve::get_remote;
@@ -297,37 +297,9 @@ pub async fn qemu_shutdown(
},
target: { schema: NODE_SCHEMA },
vmid: { schema: VMID_SCHEMA },
- online: {
- type: bool,
- description: "Perform an online migration if the vm is running.",
- optional: true,
- },
- "target-storage": {
- description: "Mapping of source storages to target storages.",
- optional: true,
- },
- bwlimit: {
- description: "Override I/O bandwidth limit (in KiB/s).",
- optional: true,
- },
- "migration-network": {
- description: "CIDR of the (sub) network that is used for migration.",
- type: String,
- format: &CIDR_FORMAT,
- optional: true,
- },
- "migration-type": {
- type: StartQemuMigrationType,
- optional: true,
- },
- force: {
- description: "Allow to migrate VMs with local devices.",
- optional: true,
- default: false,
- },
- "with-local-disks": {
- description: "Enable live storage migration for local disks.",
- optional: true,
+ migrate: {
+ type: pve_api_types::MigrateQemu,
+ flatten: true,
},
},
},
@@ -344,38 +316,23 @@ pub async fn qemu_migrate(
remote: String,
node: Option<String>,
vmid: u32,
- bwlimit: Option<u64>,
- force: Option<bool>,
- migration_network: Option<String>,
- migration_type: Option<StartQemuMigrationType>,
- online: Option<bool>,
- target: String,
- target_storage: Option<String>,
- with_local_disks: Option<bool>,
+ migrate: pve_api_types::MigrateQemu,
) -> Result<RemoteUpid, Error> {
- log::info!("in-cluster migration requested for remote {remote:?} vm {vmid} to node {target:?}");
+ log::info!(
+ "in-cluster migration requested for remote {remote:?} vm {vmid} to node {:?}",
+ migrate.target
+ );
let (remotes, _) = pdm_config::remotes::config()?;
let pve = connect_to_remote(&remotes, &remote)?;
let node = find_node_for_vm(node, vmid, pve.as_ref()).await?;
- if node == target {
+ if node == migrate.target {
bail!("refusing migration to the same node");
}
- let params = pve_api_types::MigrateQemu {
- bwlimit,
- force,
- migration_network,
- migration_type,
- online,
- target,
- targetstorage: target_storage,
- with_local_disks,
- with_conntrack_state: None,
- };
- let upid = pve.migrate_qemu(&node, vmid, params).await?;
+ let upid = pve.migrate_qemu(&node, vmid, migrate).await?;
new_remote_upid(remote, upid).await
}
@@ -431,32 +388,9 @@ async fn qemu_migrate_preconditions(
optional: true,
schema: VMID_SCHEMA,
},
- delete: {
- description: "Delete the original VM and related data after successful migration.",
- optional: true,
- default: false,
- },
- online: {
- type: bool,
- description: "Perform an online migration if the vm is running.",
- optional: true,
- default: false,
- },
- "target-storage": {
- description: "Mapping of source storages to target storages.",
- },
- "target-bridge": {
- description: "Mapping of source bridges to remote bridges.",
- },
- bwlimit: {
- description: "Override I/O bandwidth limit (in KiB/s).",
- optional: true,
- },
- // TODO better to change remote migration to proxy to node?
- "target-endpoint": {
- type: String,
- optional: true,
- description: "The target endpoint to use for the connection.",
+ remote_migrate: {
+ type: pve_api_types::RemoteMigrateQemu,
+ flatten: true,
},
},
},
@@ -474,13 +408,7 @@ pub async fn qemu_remote_migrate(
target: String, // this is the destination remote name
node: Option<String>,
vmid: u32,
- target_vmid: Option<u32>,
- delete: bool,
- online: bool,
- target_storage: String,
- target_bridge: String,
- bwlimit: Option<u64>,
- target_endpoint: Option<String>,
+ remote_migrate: pve_api_types::RemoteMigrateQemu,
rpcenv: &mut dyn RpcEnvironment,
) -> Result<RemoteUpid, Error> {
let user_info = CachedUserInfo::new()?;
@@ -494,7 +422,7 @@ pub async fn qemu_remote_migrate(
"resource",
&target,
"guest",
- &target_vmid.unwrap_or(vmid).to_string(),
+ &remote_migrate.target_vmid.unwrap_or(vmid).to_string(),
],
);
if target_privs & PRIV_RESOURCE_MIGRATE == 0 {
@@ -504,7 +432,7 @@ pub async fn qemu_remote_migrate(
);
}
- if delete {
+ if remote_migrate.delete.unwrap_or_default() {
check_guest_delete_perms(rpcenv, &remote, vmid)?;
}
@@ -528,11 +456,13 @@ pub async fn qemu_remote_migrate(
let target_node = target
.nodes
.iter()
- .find(|endpoint| match target_endpoint.as_deref() {
- Some(target) => target == endpoint.hostname,
- None => true,
- })
- .ok_or_else(|| match target_endpoint {
+ .find(
+ |endpoint| match Some(remote_migrate.target_endpoint.clone()).as_deref() {
+ Some(target) => target == endpoint.hostname,
+ None => true,
+ },
+ )
+ .ok_or_else(|| match Some(remote_migrate.target_endpoint.clone()) {
Some(endpoint) => format_err!("{endpoint} not configured for target cluster"),
None => format_err!("no nodes configured for target cluster"),
})?;
@@ -552,17 +482,10 @@ pub async fn qemu_remote_migrate(
}
log::info!("forwarding remote migration requested");
- let params = pve_api_types::RemoteMigrateQemu {
- target_bridge,
- target_storage,
- delete: Some(delete),
- online: Some(online),
- target_vmid,
- target_endpoint,
- bwlimit,
- };
log::info!("migrating vm {vmid} of node {node:?}");
- let upid = source_conn.remote_migrate_qemu(&node, vmid, params).await?;
+ let upid = source_conn
+ .remote_migrate_qemu(&node, vmid, remote_migrate)
+ .await?;
new_remote_upid(source, upid).await
}
--
2.47.3
_______________________________________________
pdm-devel mailing list
pdm-devel@lists.proxmox.com
https://lists.proxmox.com/cgi-bin/mailman/listinfo/pdm-devel
^ permalink raw reply [flat|nested] 5+ messages in thread
end of thread, other threads:[~2025-10-21 13:50 UTC | newest]
Thread overview: 5+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2025-10-21 13:50 [pdm-devel] [PATCH proxmox{, -datacenter-manager} 0/4] generate Vec's for string-lists Hannes Laimer
2025-10-21 13:50 ` [pdm-devel] [PATCH proxmox 1/3] pve-api-types: schema2rust: generate arrays for types with format `-list` Hannes Laimer
2025-10-21 13:50 ` [pdm-devel] [PATCH proxmox 2/3] pve-api-types: add regex for both storage- and bridge-pair Hannes Laimer
2025-10-21 13:50 ` [pdm-devel] [PATCH proxmox 3/3] pve-api-types: regenerate Hannes Laimer
2025-10-21 13:50 ` [pdm-devel] [PATCH proxmox-datacenter-manager 1/1] server: use types indead of string for migration parameters Hannes Laimer
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox