vmconfig_hotplug_pending is responsible for checking if a key/value pair in the
pending section can be hotplugged, if yes; perform a generic replace,
or perform specific actions for hotplugging the special cases.

vmconfig_apply_pending is only supposed to be called when ct isn't live.

Signed-off-by: Oguz Bektas <o.bek...@proxmox.com>
---
 src/PVE/LXC.pm        |  14 +--
 src/PVE/LXC/Config.pm | 199 ++++++++++++++++++++++++++++++++++++++++--
 2 files changed, 203 insertions(+), 10 deletions(-)

diff --git a/src/PVE/LXC.pm b/src/PVE/LXC.pm
index 65c41f5..f91e27d 100644
--- a/src/PVE/LXC.pm
+++ b/src/PVE/LXC.pm
@@ -1632,7 +1632,7 @@ sub alloc_disk {
 
 our $NEW_DISK_RE = qr/^([^:\s]+):(\d+(\.\d+)?)$/;
 sub create_disks {
-    my ($storecfg, $vmid, $settings, $conf) = @_;
+    my ($storecfg, $vmid, $settings, $conf, $pending) = @_;
 
     my $vollist = [];
 
@@ -1659,10 +1659,14 @@ sub create_disks {
                push @$vollist, $volid;
                $mountpoint->{volume} = $volid;
                $mountpoint->{size} = $size_kb * 1024;
-               $conf->{$ms} = 
PVE::LXC::Config->print_ct_mountpoint($mountpoint, $ms eq 'rootfs');
+               if ($pending) {
+                   $conf->{pending}->{$ms} = 
PVE::LXC::Config->print_ct_mountpoint($mountpoint, $ms eq 'rootfs');
+               } else {
+                   $conf->{$ms} = 
PVE::LXC::Config->print_ct_mountpoint($mountpoint, $ms eq 'rootfs');
+               }
            } else {
-                # use specified/existing volid/dir/device
-                $conf->{$ms} = 
PVE::LXC::Config->print_ct_mountpoint($mountpoint, $ms eq 'rootfs');
+               # use specified/existing volid/dir/device
+               $conf->{$ms} = 
PVE::LXC::Config->print_ct_mountpoint($mountpoint, $ms eq 'rootfs');
            }
        });
 
@@ -1676,7 +1680,7 @@ sub create_disks {
     # free allocated images on error
     if (my $err = $@) {
        destroy_disks($storecfg, $vollist);
-        die $err;
+       die $err;
     }
     return $vollist;
 }
diff --git a/src/PVE/LXC/Config.pm b/src/PVE/LXC/Config.pm
index 14c26bc..10dfc75 100644
--- a/src/PVE/LXC/Config.pm
+++ b/src/PVE/LXC/Config.pm
@@ -1177,6 +1177,194 @@ sub option_exists {
 }
 # END JSON config code
 
+my $LXC_FASTPLUG_OPTIONS= {
+    'description' => 1,
+    'onboot' => 1,
+    'startup' => 1,
+    'protection' => 1,
+    'hostname' => 1,
+    'hookscript' => 1,
+    'cores' => 1,
+    'tags' => 1,
+};
+
+sub vmconfig_hotplug_pending {
+    my ($class, $vmid, $conf, $storecfg, $selection, $errors) = @_;
+
+    my $pid = PVE::LXC::find_lxc_pid($vmid);
+    my $rootdir = "/proc/$pid/root";
+
+    my $add_error = sub {
+       my ($opt, $msg) = @_;
+       $errors->{$opt} = "hotplug problem - $msg";
+    };
+
+    my $changes;
+    foreach my $opt (keys %{$conf->{pending}}) { # add/change
+       next if $selection && !$selection->{$opt};
+       if ($LXC_FASTPLUG_OPTIONS->{$opt}) {
+           $conf->{$opt} = delete $conf->{pending}->{$opt};
+           $changes = 1;
+       }
+    }
+
+    if ($changes) {
+       $class->write_config($vmid, $conf);
+    }
+
+    # There's no separate swap size to configure, there's memory and "total"
+    # memory (iow. memory+swap). This means we have to change them together.
+    my $hotplug_memory_done;
+    my $hotplug_memory = sub {
+       my ($wanted_memory, $wanted_swap) = @_;
+       my $old_memory = ($conf->{memory} || $confdesc->{memory}->{default});
+       my $old_swap = ($conf->{swap} || $confdesc->{swap}->{default});
+
+       $wanted_memory //= $old_memory;
+       $wanted_swap //= $old_swap;
+
+       my $total = $wanted_memory + $wanted_swap;
+       my $old_total = $old_memory + $old_swap;
+
+       if ($total > $old_total) {
+           PVE::LXC::write_cgroup_value("memory", $vmid,
+                                        "memory.memsw.limit_in_bytes",
+                                        int($total*1024*1024));
+           PVE::LXC::write_cgroup_value("memory", $vmid,
+                                        "memory.limit_in_bytes",
+                                        int($wanted_memory*1024*1024));
+       } else {
+           PVE::LXC::write_cgroup_value("memory", $vmid,
+                                        "memory.limit_in_bytes",
+                                        int($wanted_memory*1024*1024));
+           PVE::LXC::write_cgroup_value("memory", $vmid,
+                                        "memory.memsw.limit_in_bytes",
+                                        int($total*1024*1024));
+       }
+       $hotplug_memory_done = 1;
+    };
+
+    my $pending_delete_hash = 
$class->parse_pending_delete($conf->{pending}->{delete});
+    # FIXME: $force deletion is not implemented for CTs
+    while (my ($opt, undef) = each %$pending_delete_hash) {
+       next if $selection && !$selection->{$opt};
+       eval {
+           if ($LXC_FASTPLUG_OPTIONS->{$opt}) {
+               # pass
+           } elsif ($opt =~ m/^unused(\d+)$/) {
+               PVE::LXC::delete_mountpoint_volume($storecfg, $vmid, 
$conf->{$opt})
+                   if !$class->is_volume_in_use($conf, $conf->{$opt}, 1, 1);
+           } elsif ($opt eq 'swap') {
+               $hotplug_memory->(undef, 0);
+           } elsif ($opt eq 'cpulimit') {
+               PVE::LXC::write_cgroup_value("cpu", $vmid, "cpu.cfs_period_us", 
-1);
+               PVE::LXC::write_cgroup_value("cpu", $vmid, "cpu.cfs_quota_us", 
-1);
+           } elsif ($opt eq 'cpuunits') {
+               PVE::LXC::write_cgroup_value("cpu", $vmid, "cpu.shared", 
$confdesc->{cpuunits}->{default});
+           } elsif ($opt =~ m/^net(\d)$/) {
+               my $netid = $1;
+               PVE::Network::veth_delete("veth${vmid}i$netid");
+           } else {
+               die "skip\n"; # skip non-hotpluggable opts
+           }
+       };
+       if (my $err = $@) {
+           $add_error->($opt, $err) if $err ne "skip\n";
+       } else {
+           delete $conf->{$opt};
+           $class->remove_from_pending_delete($conf, $opt);
+           $class->write_config($vmid, $conf);
+       }
+    }
+
+    foreach my $opt (keys %{$conf->{pending}}) {
+       next if $opt eq 'delete'; # just to be sure
+       next if $selection && !$selection->{$opt};
+       my $value = $conf->{pending}->{$opt};
+       eval {
+           if ($opt eq 'cpulimit') {
+               PVE::LXC::write_cgroup_value("cpu", $vmid, "cpu.cfs_period_us", 
100000);
+               PVE::LXC::write_cgroup_value("cpu", $vmid, "cpu.cfs_quota_us", 
int(100000*$value));
+           } elsif ($opt eq 'cpuunits') {
+               PVE::LXC::write_cgroup_value("cpu", $vmid, "cpu.shares", 
$value);
+           } elsif ($opt =~ m/^net(\d+)$/) {
+               my $netid = $1;
+               my $net = $class->parse_lxc_network($value);
+               PVE::LXC::update_net($vmid, $conf, $opt, $net, $netid, 
$rootdir);
+           } elsif ($opt eq 'memory' || $opt eq 'swap') {
+               if (!$hotplug_memory_done) { # don't call twice if both opts 
are passed
+                   $hotplug_memory->($conf->{pending}->{memory}, 
$conf->{pending}->{swap});
+               }
+           } else {
+               die "skip\n"; # skip non-hotpluggable
+           }
+       };
+       if (my $err = $@) {
+           $add_error->($opt, $err) if $err ne "skip\n";
+           $conf = $class->load_config($vmid);
+       } else {
+           $conf->{$opt} = $value;
+           delete $conf->{pending}->{$opt};
+           $class->write_config($vmid, $conf);
+       }
+    }
+}
+
+sub vmconfig_apply_pending {
+    my ($class, $vmid, $conf, $storecfg) = @_;
+
+    my $rescan_volume = sub {
+       my ($mp) = @_;
+       eval {
+           $mp->{size} = PVE::Storage::volume_size_info($storecfg, 
$mp->{volume}, 5)
+               if !defined($mp->{size});
+       };
+       warn "Could not rescan volume size - $@\n" if $@;
+    };
+
+    my $pending_delete_hash = 
$class->parse_pending_delete($conf->{pending}->{delete});
+    # FIXME: $force deletion is not implemented for CTs
+    while (my ($opt, undef) = each %$pending_delete_hash) {
+       $class->cleanup_pending($conf);
+       if ($opt =~ m/^mp(\d+)$/) {
+           my $mp = $class->parse_ct_mountpoint($conf->{$opt});
+           if ($mp->{type} eq 'volume') {
+               $class->add_unused_volume($conf, $mp->{volume});
+           }
+       } elsif ($opt =~ m/^unused(\d+)$/) {
+           PVE::LXC::delete_mountpoint_volume($storecfg, $vmid, $conf->{$opt})
+               if !$class->is_volume_in_use($conf, $conf->{$opt}, 1, 1);
+       }
+       delete $conf->{$opt};
+       $class->remove_from_pending_delete($conf, $opt);
+    }
+
+    foreach my $opt (keys %{$conf->{pending}}) { # add/change
+       if ($opt =~ m/^mp(\d+)$/) {
+           my $mp = $class->parse_ct_mountpoint($conf->{pending}->{$opt});
+           my $old = $conf->{$opt};
+           if ($mp->{type} eq 'volume') {
+               if ($mp->{volume} =~ $PVE::LXC::NEW_DISK_RE) {
+                   PVE::LXC::create_disks($storecfg, $vmid, { $opt => 
$conf->{pending}->{$opt} }, $conf, 1);
+               } else {
+                   $rescan_volume->($mp);
+                   $conf->{pending}->{$opt} = $class->print_ct_mountpoint($mp);
+               }
+           }
+           if (defined($old)) {
+               my $mp = $class->parse_ct_mountpoint($old);
+               if ($mp->{type} eq 'volume') {
+                   $class->add_unused_volume($conf, $mp->{volume});
+               }
+           }
+       }
+       $class->cleanup_pending($conf);
+       $conf->{$opt} = delete $conf->{pending}->{$opt};
+    }
+
+    $class->write_config($vmid, $conf);
+}
+
 sub classify_mountpoint {
     my ($class, $vol) = @_;
     if ($vol =~ m!^/!) {
@@ -1186,7 +1374,7 @@ sub classify_mountpoint {
     return 'volume';
 }
 
-my $is_volume_in_use = sub {
+my $__is_volume_in_use = sub {
     my ($class, $config, $volid) = @_;
     my $used = 0;
 
@@ -1204,17 +1392,18 @@ sub is_volume_in_use_by_snapshots {
 
     if (my $snapshots = $config->{snapshots}) {
        foreach my $snap (keys %$snapshots) {
-           return 1 if $is_volume_in_use->($class, $snapshots->{$snap}, 
$volid);
+           return 1 if $__is_volume_in_use->($class, $snapshots->{$snap}, 
$volid);
        }
     }
 
     return 0;
-};
+}
 
 sub is_volume_in_use {
-    my ($class, $config, $volid, $include_snapshots) = @_;
-    return 1 if $is_volume_in_use->($class, $config, $volid);
+    my ($class, $config, $volid, $include_snapshots, $include_pending) = @_;
+    return 1 if $__is_volume_in_use->($class, $config, $volid);
     return 1 if $include_snapshots && 
$class->is_volume_in_use_by_snapshots($config, $volid);
+    return 1 if $include_pending && $__is_volume_in_use->($class, 
$config->{pending}, $volid);
     return 0;
 }
 
-- 
2.20.1

_______________________________________________
pve-devel mailing list
pve-devel@pve.proxmox.com
https://pve.proxmox.com/cgi-bin/mailman/listinfo/pve-devel

Reply via email to