]> git.proxmox.com Git - qemu-server.git/commitdiff
api2: add cloudinit config api
authorAlexandre Derumier <aderumier@odiso.com>
Wed, 22 Jun 2022 11:52:03 +0000 (13:52 +0200)
committerThomas Lamprecht <t.lamprecht@proxmox.com>
Tue, 8 Nov 2022 16:24:59 +0000 (17:24 +0100)
Signed-off-by: Alexandre Derumier <aderumier@odiso.com>
Reviewed-by: Fiona Ebner <f.ebner@proxmox.com>
PVE/API2/Qemu.pm
PVE/CLI/qm.pm
PVE/QemuServer/Cloudinit.pm

index 625d6ea7ff491843c0672fe5ed42b73bb197a7b9..a09a9e2abfb839a9317c84d8dfef8dafb756a6e4 100644 (file)
@@ -21,6 +21,7 @@ use PVE::ReplicationConfig;
 use PVE::GuestHelpers;
 use PVE::QemuConfig;
 use PVE::QemuServer;
+use PVE::QemuServer::Cloudinit;
 use PVE::QemuServer::CPUConfig;
 use PVE::QemuServer::Drive;
 use PVE::QemuServer::ImportDisk;
@@ -1311,6 +1312,73 @@ __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 = PVE::QemuServer::Cloudinit::get_pending_config($conf, $vmid);
+
+       return $res;
+   }});
+
 # POST/PUT {vmid}/config implementation
 #
 # The original API used PUT (idempotent) an we assumed that all operations
index 1cd365aa5bb31fae887012678d0b8a583ca2e49d..b67451b0e3f99f6158bbaf964611d4313783128d 100755 (executable)
@@ -984,6 +984,7 @@ our $cmddef = {
 
     cloudinit => {
        dump => [ "PVE::API2::Qemu", 'cloudinit_generated_config_dump', ['vmid', 'type'], { %node }, sub { print "$_[0]\n"; }],
+       pending => [ "PVE::API2::Qemu", 'cloudinit_pending', ['vmid'], { %node }, \&PVE::GuestHelpers::format_pending ]
     },
 
 };
index cdaf4e558eb0526d7ab8367383c912ba7036f327..3e93692cf49c25029a8b6feed9d2a47c58e2b74f 100644 (file)
@@ -7,6 +7,7 @@ use File::Path;
 use Digest::SHA;
 use URI::Escape;
 use MIME::Base64 qw(encode_base64);
+use Storable qw(dclone);
 
 use PVE::Tools qw(run_command file_set_contents);
 use PVE::Storage;
@@ -632,4 +633,82 @@ sub dump_cloudinit_config {
     }
 }
 
+sub get_pending_config {
+    my ($conf, $vmid) = @_;
+
+    my $newconf = dclone($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);
+    });
+    for my $ds (keys %{$drives}) {
+       push @cloudinit_opts, $ds;
+    }
+
+    $newconf->{name} = "VM$vmid" if !$newconf->{name};
+    $cloudinit_current->{name} = "VM$vmid" if !$cloudinit_current->{name};
+
+    #only mac-address is used in cloud-init config.
+    #We don't want to display other pending net changes.
+    my $print_cloudinit_net = sub {
+       my ($conf, $opt) = @_;
+
+       if (defined($conf->{$opt})) {
+           my $net = PVE::QemuServer::parse_net($conf->{$opt});
+           $conf->{$opt} = "macaddr=".$net->{macaddr} if $net->{macaddr};
+       }
+    };
+
+    my $cloudinit_options = {};
+    for my $opt (@cloudinit_opts) {
+       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_cloudinit_net($newconf, $netid);
+           &$print_cloudinit_net($cloudinit_current, $netid);
+           $cloudinit_options->{$netid} = 1;
+       }
+       $cloudinit_options->{$opt} = 1;
+    }
+
+    my $res = [];
+
+    for my $opt (keys %{$cloudinit_options}) {
+
+       my $item = {
+           key => $opt,
+       };
+       if ($cloudinit_current->{$opt}) {
+           $item->{value} = $cloudinit_current->{$opt};
+           if (defined($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}
+       }
+
+       push @$res, $item;
+   }
+
+   return $res;
+}
+
 1;