public inbox for pve-devel@lists.proxmox.com
 help / color / mirror / Atom feed
* [pve-devel] [PATCH v2 qemu-server 0/6] cloudinit pending behaviour change
@ 2021-04-01  0:21 Alexandre Derumier
  2021-04-01  0:21 ` [pve-devel] [PATCH v2 qemu-server 1/6] cloudinit: add cloudinit section for current generated config Alexandre Derumier
                   ` (5 more replies)
  0 siblings, 6 replies; 7+ messages in thread
From: Alexandre Derumier @ 2021-04-01  0:21 UTC (permalink / raw)
  To: pve-devel

Hi,

This is an attempt to cleanup current behaviour of cloudinit online changes.

Currently, we setup cloudinit options as pending, until we generate the config drive.

This is not 100% true, because some option like vm name, nic mac address can be changed,
without going to pending, so user can't known if it need to regenerated it.

Also, some can are very difficult to handle, if you hotplug a nic but it's failing,so pending,
then you defined an ipconfig, and then you revert hotplug.
or if you delete a nic, the ipconfig is no more displayed in the gui.


So, instead of setting cloudinit values in pending,
this patch serie copy the current cloudinit config in a new section [special:cloudinit],
when the config drive is generated.
This is only an hint, to allow to display diff between the generated cloudinit
drive, and the current vm config.

A new specific cloudinit config api is added too, merging ipaddrX && netX mac
in same field, and displaying the diff between current and generated config.

Changelog V1:

- use [special:cloudinit] instead [CLOUDINIT] for section
- delete config section on drive removal
- config api: move code to new PVE::QemuServer::Cloudinit::get_pending_config
- config api: add "qm cloudinit pending" cli
- add update api to regenerate drive with 1 api call
- add cloudinit hotplug option

Changelog v2:

- fix trailing whitespace in first patch
- revert previous "cloudinit" check in snapshot name (":" character is already forbidden)


Alexandre Derumier (6):
  cloudinit: add cloudinit section for current generated config.
  generate cloudinit drive on offline plug
  cloudinit: make cloudnit options fastplug
  api2: add cloudinit config api
  api2: add cloudinit_update
  add cloudinit hotplug

 PVE/API2/Qemu.pm            | 117 ++++++++++++++++++++++++++++++++++++
 PVE/CLI/qm.pm               |   2 +
 PVE/QemuServer.pm           | 102 +++++++++++++++++++++----------
 PVE/QemuServer/Cloudinit.pm | 101 +++++++++++++++++++++++++++++++
 4 files changed, 289 insertions(+), 33 deletions(-)

-- 
2.20.1




^ permalink raw reply	[flat|nested] 7+ messages in thread

* [pve-devel] [PATCH v2 qemu-server 1/6] cloudinit: add cloudinit section for current generated config.
  2021-04-01  0:21 [pve-devel] [PATCH v2 qemu-server 0/6] cloudinit pending behaviour change Alexandre Derumier
@ 2021-04-01  0:21 ` Alexandre Derumier
  2021-04-01  0:21 ` [pve-devel] [PATCH v2 qemu-server 2/6] generate cloudinit drive on offline plug Alexandre Derumier
                   ` (4 subsequent siblings)
  5 siblings, 0 replies; 7+ messages in thread
From: Alexandre Derumier @ 2021-04-01  0:21 UTC (permalink / raw)
  To: pve-devel

Instead using vm pending options for pending cloudinit generated config,

write current generated cloudinit config in a new [special:cloudinit] SECTION.

Currently, some options like vm name, nic mac address can be hotplugged,
so they are not way to know if the cloud-init disk is already updated.
---
 PVE/QemuServer.pm           | 21 +++++++++++++++++----
 PVE/QemuServer/Cloudinit.pm | 31 +++++++++++++++++++++++++++++++
 2 files changed, 48 insertions(+), 4 deletions(-)

diff --git a/PVE/QemuServer.pm b/PVE/QemuServer.pm
index 57cfe62..9a7add5 100644
--- a/PVE/QemuServer.pm
+++ b/PVE/QemuServer.pm
@@ -1875,6 +1875,7 @@ sub vmconfig_register_unused_drive {
     if (drive_is_cloudinit($drive)) {
 	eval { PVE::Storage::vdisk_free($storecfg, $drive->{file}) };
 	warn $@ if $@;
+	delete $conf->{cloudinit};
     } elsif (!drive_is_cdrom($drive)) {
 	my $volid = $drive->{file};
 	if (vm_is_volid_owner($storecfg, $vmid, $volid)) {
@@ -2135,6 +2136,7 @@ sub parse_vm_config {
 	digest => Digest::SHA::sha1_hex($raw),
 	snapshots => {},
 	pending => {},
+	cloudinit => {},
     };
 
     $filename =~ m|/qemu-server/(\d+)\.conf$|
@@ -2159,6 +2161,11 @@ sub parse_vm_config {
 	    $descr = undef;
 	    $conf = $res->{$section} = {};
 	    next;
+	} elsif ($line =~ m/^\[special:cloudinit\]\s*$/i) {
+	    $section = 'cloudinit';
+	    $descr = undef;
+	    $conf = $res->{$section} = {};
+	    next;
 
 	} elsif ($line =~ m/^\[([a-z][a-z0-9_\-]+)\]\s*$/i) {
 	    $section = $1;
@@ -2219,7 +2226,6 @@ sub parse_vm_config {
 	    warn "vm $vmid - unable to parse config: $line\n";
 	}
     }
-
     if (defined($descr)) {
 	$descr =~ s/\s+$//;
 	$conf->{description} = $descr;
@@ -2256,7 +2262,7 @@ sub write_vm_config {
 
 	foreach my $key (keys %$cref) {
 	    next if $key eq 'digest' || $key eq 'description' || $key eq 'snapshots' ||
-		$key eq 'snapstate' || $key eq 'pending';
+		$key eq 'snapstate' || $key eq 'pending' || $key eq 'cloudinit';
 	    my $value = $cref->{$key};
 	    if ($key eq 'delete') {
 		die "propertry 'delete' is only allowed in [PENDING]\n"
@@ -2280,6 +2286,8 @@ sub write_vm_config {
 
     &$cleanup_config($conf->{pending}, 1);
 
+    &$cleanup_config($conf->{cloudinit}, 1);
+
     foreach my $snapname (keys %{$conf->{snapshots}}) {
 	die "internal error: snapshot name '$snapname' is forbidden" if lc($snapname) eq 'pending';
 	&$cleanup_config($conf->{snapshots}->{$snapname}, undef, $snapname);
@@ -2310,7 +2318,7 @@ sub write_vm_config {
 	}
 
 	foreach my $key (sort keys %$conf) {
-	    next if $key =~ /^(digest|description|pending|snapshots)$/;
+	    next if $key =~ /^(digest|description|pending|cloudinit|snapshots)$/;
 	    $raw .= "$key: $conf->{$key}\n";
 	}
 	return $raw;
@@ -2323,6 +2331,11 @@ sub write_vm_config {
 	$raw .= &$generate_raw_config($conf->{pending}, 1);
     }
 
+    if (scalar(keys %{$conf->{cloudinit}})){
+	$raw .= "\n[special:cloudinit]\n";
+	$raw .= &$generate_raw_config($conf->{cloudinit}, 1);
+    }
+
     foreach my $snapname (sort keys %{$conf->{snapshots}}) {
 	$raw .= "\n[$snapname]\n";
 	$raw .= &$generate_raw_config($conf->{snapshots}->{$snapname});
@@ -4702,9 +4715,9 @@ sub vmconfig_apply_pending {
 	    $conf->{$opt} = delete $conf->{pending}->{$opt};
 	}
     }
-
     # write all changes at once to avoid unnecessary i/o
     PVE::QemuConfig->write_config($vmid, $conf);
+
 }
 
 sub vmconfig_update_net {
diff --git a/PVE/QemuServer/Cloudinit.pm b/PVE/QemuServer/Cloudinit.pm
index c464bf3..8da18b4 100644
--- a/PVE/QemuServer/Cloudinit.pm
+++ b/PVE/QemuServer/Cloudinit.pm
@@ -570,6 +570,37 @@ sub generate_cloudinitconfig {
 
 	$generator->($conf, $vmid, $drive, $volname, $storeid);
     });
+
+    my $cloudinitconf = delete $conf->{cloudinit};
+    $cloudinitconf = {};
+
+    my @cloudinit_opts = keys %{PVE::QemuServer::cloudinit_config_properties()};
+    push @cloudinit_opts, 'name';
+
+    foreach my $opt (@cloudinit_opts) {
+
+	if ($opt =~ m/^ipconfig(\d+)/) {
+	    my $netid = "net$1";
+	    next if !defined($conf->{$netid});
+	    $conf->{cloudinit}->{$netid} = $conf->{$netid};
+	}
+
+	$conf->{cloudinit}->{$opt} = $conf->{$opt} if $conf->{$opt};
+    }
+
+    $conf->{cloudinit}->{name} = "VM$vmid" if !$conf->{cloudinit}->{name};
+
+    foreach my $opt (keys %{$conf}) {
+	if (PVE::QemuServer::is_valid_drivename($opt)) {
+	    my $drive = PVE::QemuServer::parse_drive($opt, $conf->{$opt});
+	    if (PVE::QemuServer::drive_is_cloudinit($drive)) {
+		$conf->{cloudinit}->{$opt} = $conf->{$opt};
+	    }
+	}
+    }
+
+    PVE::QemuConfig->write_config($vmid, $conf);
+
 }
 
 sub dump_cloudinit_config {
-- 
2.20.1




^ permalink raw reply	[flat|nested] 7+ messages in thread

* [pve-devel] [PATCH v2 qemu-server 2/6] generate cloudinit drive on offline plug
  2021-04-01  0:21 [pve-devel] [PATCH v2 qemu-server 0/6] cloudinit pending behaviour change Alexandre Derumier
  2021-04-01  0:21 ` [pve-devel] [PATCH v2 qemu-server 1/6] cloudinit: add cloudinit section for current generated config Alexandre Derumier
@ 2021-04-01  0:21 ` Alexandre Derumier
  2021-04-01  0:21 ` [pve-devel] [PATCH v2 qemu-server 3/6] cloudinit: make cloudnit options fastplug Alexandre Derumier
                   ` (3 subsequent siblings)
  5 siblings, 0 replies; 7+ messages in thread
From: Alexandre Derumier @ 2021-04-01  0:21 UTC (permalink / raw)
  To: pve-devel

Currently when only generate it at vm start
---
 PVE/QemuServer.pm | 10 ++++++++++
 1 file changed, 10 insertions(+)

diff --git a/PVE/QemuServer.pm b/PVE/QemuServer.pm
index 9a7add5..5092b49 100644
--- a/PVE/QemuServer.pm
+++ b/PVE/QemuServer.pm
@@ -4702,6 +4702,8 @@ sub vmconfig_apply_pending {
 
     PVE::QemuConfig->cleanup_pending($conf);
 
+    my $generate_cloudnit = undef;
+
     foreach my $opt (keys %{$conf->{pending}}) { # add/change
 	next if $opt eq 'delete'; # just to be sure
 	eval {
@@ -4709,15 +4711,23 @@ sub vmconfig_apply_pending {
 		vmconfig_register_unused_drive($storecfg, $vmid, $conf, parse_drive($opt, $conf->{$opt}))
 	    }
 	};
+
+	if (is_valid_drivename($opt)) {
+	    my $drive = parse_drive($opt, $conf->{pending}->{$opt});
+	    $generate_cloudnit = 1 if drive_is_cloudinit($drive);
+	}
+
 	if (my $err = $@) {
 	    $add_apply_error->($opt, $err);
 	} else {
 	    $conf->{$opt} = delete $conf->{pending}->{$opt};
 	}
     }
+
     # write all changes at once to avoid unnecessary i/o
     PVE::QemuConfig->write_config($vmid, $conf);
 
+    PVE::QemuServer::Cloudinit::generate_cloudinitconfig($conf, $vmid) if $generate_cloudnit;
 }
 
 sub vmconfig_update_net {
-- 
2.20.1




^ permalink raw reply	[flat|nested] 7+ messages in thread

* [pve-devel] [PATCH v2 qemu-server 3/6] cloudinit: make cloudnit options fastplug
  2021-04-01  0:21 [pve-devel] [PATCH v2 qemu-server 0/6] cloudinit pending behaviour change Alexandre Derumier
  2021-04-01  0:21 ` [pve-devel] [PATCH v2 qemu-server 1/6] cloudinit: add cloudinit section for current generated config Alexandre Derumier
  2021-04-01  0:21 ` [pve-devel] [PATCH v2 qemu-server 2/6] generate cloudinit drive on offline plug Alexandre Derumier
@ 2021-04-01  0:21 ` Alexandre Derumier
  2021-04-01  0:21 ` [pve-devel] [PATCH v2 qemu-server 4/6] api2: add cloudinit config api Alexandre Derumier
                   ` (2 subsequent siblings)
  5 siblings, 0 replies; 7+ messages in thread
From: Alexandre Derumier @ 2021-04-01  0:21 UTC (permalink / raw)
  To: pve-devel

---
 PVE/QemuServer.pm | 30 +++---------------------------
 1 file changed, 3 insertions(+), 27 deletions(-)

diff --git a/PVE/QemuServer.pm b/PVE/QemuServer.pm
index 5092b49..900e227 100644
--- a/PVE/QemuServer.pm
+++ b/PVE/QemuServer.pm
@@ -4456,9 +4456,10 @@ sub vmconfig_hotplug_pending {
 	$errors->{$opt} = "hotplug problem - $msg";
     };
 
+    my @cloudinit_opts = keys %$confdesc_cloudinit;
     my $changes = 0;
     foreach my $opt (keys %{$conf->{pending}}) { # add/change
-	if ($fast_plug_option->{$opt}) {
+	if ($fast_plug_option->{$opt} || grep { $_ eq $opt } @cloudinit_opts) {
 	    $conf->{$opt} = $conf->{pending}->{$opt};
 	    delete $conf->{pending}->{$opt};
 	    $changes = 1;
@@ -4531,31 +4532,6 @@ sub vmconfig_hotplug_pending {
 	}
     }
 
-    my ($apply_pending_cloudinit, $apply_pending_cloudinit_done);
-    $apply_pending_cloudinit = sub {
-	return if $apply_pending_cloudinit_done; # once is enough
-	$apply_pending_cloudinit_done = 1; # once is enough
-
-	my ($key, $value) = @_;
-
-	my @cloudinit_opts = keys %$confdesc_cloudinit;
-	foreach my $opt (keys %{$conf->{pending}}) {
-	    next if !grep { $_ eq $opt } @cloudinit_opts;
-	    $conf->{$opt} = delete $conf->{pending}->{$opt};
-	}
-
-	my $pending_delete_hash = PVE::QemuConfig->parse_pending_delete($conf->{pending}->{delete});
-	foreach my $opt (sort keys %$pending_delete_hash) {
-	    next if !grep { $_ eq $opt } @cloudinit_opts;
-	    PVE::QemuConfig->remove_from_pending_delete($conf, $opt);
-	    delete $conf->{$opt};
-	}
-
-	my $new_conf = { %$conf };
-	$new_conf->{$key} = $value;
-	PVE::QemuServer::Cloudinit::generate_cloudinitconfig($new_conf, $vmid);
-    };
-
     foreach my $opt (keys %{$conf->{pending}}) {
 	next if $selection && !$selection->{$opt};
 	my $value = $conf->{pending}->{$opt};
@@ -4602,7 +4578,7 @@ sub vmconfig_hotplug_pending {
 		# some changes can be done without hotplug
 		my $drive = parse_drive($opt, $value);
 		if (drive_is_cloudinit($drive)) {
-		    &$apply_pending_cloudinit($opt, $value);
+		    PVE::QemuServer::Cloudinit::generate_cloudinitconfig($conf, $vmid);
 		}
 		vmconfig_update_disk($storecfg, $conf, $hotplug_features->{disk},
 				     $vmid, $opt, $value, $arch, $machine_type);
-- 
2.20.1




^ permalink raw reply	[flat|nested] 7+ messages in thread

* [pve-devel] [PATCH v2 qemu-server 4/6] api2: add cloudinit config api
  2021-04-01  0:21 [pve-devel] [PATCH v2 qemu-server 0/6] cloudinit pending behaviour change Alexandre Derumier
                   ` (2 preceding siblings ...)
  2021-04-01  0:21 ` [pve-devel] [PATCH v2 qemu-server 3/6] cloudinit: make cloudnit options fastplug Alexandre Derumier
@ 2021-04-01  0:21 ` Alexandre Derumier
  2021-04-01  0:21 ` [pve-devel] [PATCH v2 qemu-server 5/6] api2: add cloudinit_update Alexandre Derumier
  2021-04-01  0:21 ` [pve-devel] [PATCH v2 qemu-server 6/6] add cloudinit hotplug Alexandre Derumier
  5 siblings, 0 replies; 7+ messages in thread
From: Alexandre Derumier @ 2021-04-01  0:21 UTC (permalink / raw)
  To: pve-devel

---
 PVE/API2/Qemu.pm            | 73 +++++++++++++++++++++++++++++++++++++
 PVE/CLI/qm.pm               |  1 +
 PVE/QemuServer/Cloudinit.pm | 70 +++++++++++++++++++++++++++++++++++
 3 files changed, 144 insertions(+)

diff --git a/PVE/API2/Qemu.pm b/PVE/API2/Qemu.pm
index ea74c69..b6122fe 100644
--- a/PVE/API2/Qemu.pm
+++ b/PVE/API2/Qemu.pm
@@ -21,6 +21,7 @@ use PVE::ReplicationConfig;
 use PVE::GuestHelpers;
 use PVE::QemuConfig;
 use PVE::QemuServer;
+use PVE::QemuServer::Cloudinit;
 use PVE::QemuServer::Drive;
 use PVE::QemuServer::CPUConfig;
 use PVE::QemuServer::Monitor qw(mon_cmd);
@@ -1039,6 +1040,78 @@ __PACKAGE__->register_method({
 	return PVE::GuestHelpers::config_with_pending_array($conf, $pending_delete_hash);
    }});
 
+__PACKAGE__->register_method({
+    name => 'cloudinit_pending',
+    path => '{vmid}/cloudinit',
+    method => 'GET',
+    proxyto => 'node',
+    description => "Get the cloudinit configuration with both current and pending values.",
+    permissions => {
+	check => ['perm', '/vms/{vmid}', [ 'VM.Audit' ]],
+    },
+    parameters => {
+	additionalProperties => 0,
+	properties => {
+	    node => get_standard_option('pve-node'),
+	    vmid => get_standard_option('pve-vmid', { completion => \&PVE::QemuServer::complete_vmid }),
+	},
+    },
+    returns => {
+	type => "array",
+	items => {
+	    type => "object",
+	    properties => {
+		key => {
+		    description => "Configuration option name.",
+		    type => 'string',
+		},
+		value => {
+		    description => "Current value.",
+		    type => 'string',
+		    optional => 1,
+		},
+		pending => {
+		    description => "Pending value.",
+		    type => 'string',
+		    optional => 1,
+		},
+		delete => {
+		    description => "Indicates a pending delete request if present and not 0. " .
+		                   "The value 2 indicates a force-delete request.",
+		    type => 'integer',
+		    minimum => 0,
+		    maximum => 2,
+		    optional => 1,
+		},
+	    },
+	},
+    },
+    code => sub {
+	my ($param) = @_;
+
+	my $vmid = $param->{vmid};
+	my $conf = PVE::QemuConfig->load_config($vmid);
+
+	if( defined($conf->{cipassword}) && 
+	    defined($conf->{cloudinit}->{cipassword}) && 
+	    $conf->{cipassword} ne $conf->{cloudinit}->{cipassword}) {
+	    $conf->{cipassword} = '********** ';
+	} elsif (defined($conf->{cipassword})) {
+	    $conf->{cipassword} = '**********';
+	}
+
+	$conf->{cloudinit}->{cipassword} = '**********' if defined($conf->{cloudinit}->{cipassword});
+
+	my $res = [];
+	my $pending = PVE::QemuServer::Cloudinit::get_pending_config($conf, $vmid);
+
+	foreach my $opt (keys %{$pending}) {
+	    push @$res, $pending->{$opt};
+	}
+
+	return $res;
+   }});
+
 # POST/PUT {vmid}/config implementation
 #
 # The original API used PUT (idempotent) an we assumed that all operations
diff --git a/PVE/CLI/qm.pm b/PVE/CLI/qm.pm
index f8972bd..e24b832 100755
--- a/PVE/CLI/qm.pm
+++ b/PVE/CLI/qm.pm
@@ -996,6 +996,7 @@ our $cmddef = {
 		my $data = shift;
 		print "$data\n";
 	    }],
+	pending => [ "PVE::API2::Qemu", 'cloudinit_pending', ['vmid'], { node => $nodename }, \&PVE::GuestHelpers::format_pending ]
     },
 
 };
diff --git a/PVE/QemuServer/Cloudinit.pm b/PVE/QemuServer/Cloudinit.pm
index 8da18b4..99aba5c 100644
--- a/PVE/QemuServer/Cloudinit.pm
+++ b/PVE/QemuServer/Cloudinit.pm
@@ -628,4 +628,74 @@ sub dump_cloudinit_config {
     }
 }
 
+sub get_pending_config {
+    my ($conf, $vmid) = @_;
+
+    my $newconf = { %{$conf} };
+    my $cloudinit_current = $newconf->{cloudinit};
+    my @cloudinit_opts = keys %{PVE::QemuServer::cloudinit_config_properties()};
+    push @cloudinit_opts, 'name';
+
+    #add cloud-init drive 
+    my $drives = {};
+    PVE::QemuConfig->foreach_volume($newconf, sub {
+	my ($ds, $drive) = @_;
+	$drives->{$ds} = 1 if PVE::QemuServer::drive_is_cloudinit($drive);
+    });
+
+    PVE::QemuConfig->foreach_volume($cloudinit_current, sub {
+	my ($ds, $drive) = @_;
+	$drives->{$ds} = 1 if PVE::QemuServer::drive_is_cloudinit($drive);
+    });
+    foreach my $ds (keys %{$drives}) {
+	push @cloudinit_opts, $ds;
+    }
+
+    $newconf->{name} = "VM$vmid" if !$newconf->{name};
+
+    my $print_net_addr = sub {
+	my ($conf, $opt, $netid) = @_;
+
+	if (defined($conf->{$netid})) {
+
+	    my $net = PVE::QemuServer::parse_net($conf->{$netid});
+	    if (defined($conf->{$opt})) {
+		$conf->{$opt} .= ",macaddr=".$net->{macaddr} if $net->{macaddr};
+	    } else {
+		$conf->{$opt} = "";
+	    }
+	}
+    };
+
+    my $res = {};
+    foreach my $opt (@cloudinit_opts) {
+
+	#add macaddr to ipconfig
+	if ($opt =~ m/^ipconfig(\d+)/) {
+	    my $netid = "net$1";
+	    next if !defined($newconf->{$netid}) && !defined($cloudinit_current->{$netid}) && !defined($newconf->{$opt}) && !defined($cloudinit_current->{$opt} );
+
+	    &$print_net_addr($newconf, $opt, $netid);
+	    &$print_net_addr($cloudinit_current, $opt, $netid);
+	}
+
+	my $item = {
+	    key => $opt,
+	};
+	if ($cloudinit_current->{$opt}) {
+	    $item->{value} = $cloudinit_current->{$opt};
+	    if ($newconf->{$opt}) {
+		$item->{pending} = $newconf->{$opt} if $newconf->{$opt} ne $cloudinit_current->{$opt};
+	    } else {
+		$item->{delete} = 1;
+	    }
+	} else {
+	    $item->{pending} = $newconf->{$opt} if $newconf->{$opt}
+	}
+
+	$res->{$opt} = $item;
+   }
+   return $res;
+}
+
 1;
-- 
2.20.1




^ permalink raw reply	[flat|nested] 7+ messages in thread

* [pve-devel] [PATCH v2 qemu-server 5/6] api2: add cloudinit_update
  2021-04-01  0:21 [pve-devel] [PATCH v2 qemu-server 0/6] cloudinit pending behaviour change Alexandre Derumier
                   ` (3 preceding siblings ...)
  2021-04-01  0:21 ` [pve-devel] [PATCH v2 qemu-server 4/6] api2: add cloudinit config api Alexandre Derumier
@ 2021-04-01  0:21 ` Alexandre Derumier
  2021-04-01  0:21 ` [pve-devel] [PATCH v2 qemu-server 6/6] add cloudinit hotplug Alexandre Derumier
  5 siblings, 0 replies; 7+ messages in thread
From: Alexandre Derumier @ 2021-04-01  0:21 UTC (permalink / raw)
  To: pve-devel

This allow to regenerate the config drive with 1 api call.

This also avoid to delete drive first, and recreate it again.

As it's a readonly drive, we can simply live update it,
and eject/replace it with qemu monitor
---
 PVE/API2/Qemu.pm  | 44 ++++++++++++++++++++++++++++++++++++++++++++
 PVE/CLI/qm.pm     |  3 ++-
 PVE/QemuServer.pm | 28 ++++++++++++++++++++++++++++
 3 files changed, 74 insertions(+), 1 deletion(-)

diff --git a/PVE/API2/Qemu.pm b/PVE/API2/Qemu.pm
index b6122fe..de2c378 100644
--- a/PVE/API2/Qemu.pm
+++ b/PVE/API2/Qemu.pm
@@ -1112,6 +1112,50 @@ __PACKAGE__->register_method({
 	return $res;
    }});
 
+__PACKAGE__->register_method({
+    name => 'cloudinit_update',
+    path => '{vmid}/cloudinit',
+    method => 'PUT',
+    protected => 1,
+    proxyto => 'node',
+    description => "Regenerate and change cloudinit config drive.",
+    permissions => {
+	check => ['perm', '/vms/{vmid}', 'VM.Config.Cloudinit', any => 1],
+    },
+    parameters => {
+	additionalProperties => 0,
+	properties => {
+	    node => get_standard_option('pve-node'),
+	    vmid => get_standard_option('pve-vmid'),
+	},
+    },
+    returns => { type => 'null' },
+    code => sub {
+	my ($param) = @_;
+
+	my $rpcenv = PVE::RPCEnvironment::get();
+
+	my $authuser = $rpcenv->get_user();
+
+	my $vmid = extract_param($param, 'vmid');
+
+	my $updatefn =  sub {
+
+	    my $conf = PVE::QemuConfig->load_config($vmid);
+
+	    PVE::QemuConfig->check_lock($conf);
+
+	    my $storecfg = PVE::Storage::config();
+
+	    PVE::QemuServer::vmconfig_update_cloudinit_drive($storecfg, $conf, $vmid);
+
+	};
+
+	PVE::QemuConfig->lock_config($vmid, $updatefn);
+
+	return;
+    }});
+
 # POST/PUT {vmid}/config implementation
 #
 # The original API used PUT (idempotent) an we assumed that all operations
diff --git a/PVE/CLI/qm.pm b/PVE/CLI/qm.pm
index e24b832..8d594c6 100755
--- a/PVE/CLI/qm.pm
+++ b/PVE/CLI/qm.pm
@@ -996,7 +996,8 @@ our $cmddef = {
 		my $data = shift;
 		print "$data\n";
 	    }],
-	pending => [ "PVE::API2::Qemu", 'cloudinit_pending', ['vmid'], { node => $nodename }, \&PVE::GuestHelpers::format_pending ]
+	pending => [ "PVE::API2::Qemu", 'cloudinit_pending', ['vmid'], { node => $nodename }, \&PVE::GuestHelpers::format_pending ],
+	update => [ "PVE::API2::Qemu", 'cloudinit_update', ['vmid'], { node => $nodename }],
     },
 
 };
diff --git a/PVE/QemuServer.pm b/PVE/QemuServer.pm
index 900e227..59f90c9 100644
--- a/PVE/QemuServer.pm
+++ b/PVE/QemuServer.pm
@@ -4867,6 +4867,34 @@ sub vmconfig_update_disk {
     vm_deviceplug($storecfg, $conf, $vmid, $opt, $drive, $arch, $machine_type);
 }
 
+sub vmconfig_update_cloudinit_drive {
+    my ($storecfg, $conf, $vmid) = @_;
+
+    my $cloudinit_ds = undef;
+    my $cloudinit_drive = undef;
+
+    PVE::QemuConfig->foreach_volume($conf, sub {
+	my ($ds, $drive) = @_;
+	if (PVE::QemuServer::drive_is_cloudinit($drive)) {
+	    $cloudinit_ds = $ds;
+	    $cloudinit_drive = $drive;
+	}
+    });
+
+    return if !$cloudinit_drive;
+
+    PVE::QemuServer::Cloudinit::generate_cloudinitconfig($conf, $vmid);
+    my $running = PVE::QemuServer::check_running($vmid);
+
+    if ($running) {
+	my $path = PVE::Storage::path($storecfg, $cloudinit_drive->{file});
+	if ($path) {
+	    mon_cmd($vmid, "eject", force => JSON::true, id => "$cloudinit_ds");
+	    mon_cmd($vmid, "blockdev-change-medium", id => "$cloudinit_ds", filename => "$path");
+	}
+    }
+}
+
 # called in locked context by incoming migration
 sub vm_migrate_get_nbd_disks {
     my ($storecfg, $conf, $replicated_volumes) = @_;
-- 
2.20.1




^ permalink raw reply	[flat|nested] 7+ messages in thread

* [pve-devel] [PATCH v2 qemu-server 6/6] add cloudinit hotplug
  2021-04-01  0:21 [pve-devel] [PATCH v2 qemu-server 0/6] cloudinit pending behaviour change Alexandre Derumier
                   ` (4 preceding siblings ...)
  2021-04-01  0:21 ` [pve-devel] [PATCH v2 qemu-server 5/6] api2: add cloudinit_update Alexandre Derumier
@ 2021-04-01  0:21 ` Alexandre Derumier
  5 siblings, 0 replies; 7+ messages in thread
From: Alexandre Derumier @ 2021-04-01  0:21 UTC (permalink / raw)
  To: pve-devel

This allow to regenerate config drive if pending values exist
when we change vm options.
---
 PVE/QemuServer.pm | 15 ++++++++++++---
 1 file changed, 12 insertions(+), 3 deletions(-)

diff --git a/PVE/QemuServer.pm b/PVE/QemuServer.pm
index 59f90c9..eaad21a 100644
--- a/PVE/QemuServer.pm
+++ b/PVE/QemuServer.pm
@@ -287,7 +287,7 @@ my $confdesc = {
     hotplug => {
         optional => 1,
         type => 'string', format => 'pve-hotplug-features',
-        description => "Selectively enable hotplug features. This is a comma separated list of hotplug features: 'network', 'disk', 'cpu', 'memory' and 'usb'. Use '0' to disable hotplug completely. Value '1' is an alias for the default 'network,disk,usb'.",
+        description => "Selectively enable hotplug features. This is a comma separated list of hotplug features: 'network', 'disk', 'cpu', 'memory', 'usb' and 'cloudinit'. Use '0' to disable hotplug completely. Value '1' is an alias for the default 'network,disk,usb'.",
         default => 'network,disk,usb',
     },
     reboot => {
@@ -1298,7 +1298,7 @@ sub parse_hotplug_features {
     $data = $confdesc->{hotplug}->{default} if $data eq '1';
 
     foreach my $feature (PVE::Tools::split_list($data)) {
-	if ($feature =~ m/^(network|disk|cpu|memory|usb)$/) {
+	if ($feature =~ m/^(network|disk|cpu|memory|usb|cloudinit)$/) {
 	    $res->{$1} = 1;
 	} else {
 	    die "invalid hotplug feature '$feature'\n";
@@ -4601,8 +4601,17 @@ sub vmconfig_hotplug_pending {
 	    delete $conf->{pending}->{$opt};
 	}
     }
-
     PVE::QemuConfig->write_config($vmid, $conf);
+
+    if($hotplug_features->{cloudinit}) {
+	my $pending = PVE::QemuServer::Cloudinit::get_pending_config($conf, $vmid);
+	my $regenerate = undef;
+	foreach my $opt (keys %{$pending}) {
+	    my $item = $pending->{$opt};
+	    $regenerate = 1 if defined($item->{delete}) or defined($item->{pending});
+	}
+	PVE::QemuServer::vmconfig_update_cloudinit_drive($storecfg, $conf, $vmid) if $regenerate;
+    }
 }
 
 sub try_deallocate_drive {
-- 
2.20.1




^ permalink raw reply	[flat|nested] 7+ messages in thread

end of thread, other threads:[~2021-04-01  0:22 UTC | newest]

Thread overview: 7+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2021-04-01  0:21 [pve-devel] [PATCH v2 qemu-server 0/6] cloudinit pending behaviour change Alexandre Derumier
2021-04-01  0:21 ` [pve-devel] [PATCH v2 qemu-server 1/6] cloudinit: add cloudinit section for current generated config Alexandre Derumier
2021-04-01  0:21 ` [pve-devel] [PATCH v2 qemu-server 2/6] generate cloudinit drive on offline plug Alexandre Derumier
2021-04-01  0:21 ` [pve-devel] [PATCH v2 qemu-server 3/6] cloudinit: make cloudnit options fastplug Alexandre Derumier
2021-04-01  0:21 ` [pve-devel] [PATCH v2 qemu-server 4/6] api2: add cloudinit config api Alexandre Derumier
2021-04-01  0:21 ` [pve-devel] [PATCH v2 qemu-server 5/6] api2: add cloudinit_update Alexandre Derumier
2021-04-01  0:21 ` [pve-devel] [PATCH v2 qemu-server 6/6] add cloudinit hotplug Alexandre Derumier

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox
Service provided by Proxmox Server Solutions GmbH | Privacy | Legal