From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: from gate001.proxmox.com (gate001.proxmox.com [45.144.208.40]) by lore.proxmox.com (Postfix) with ESMTPS id 0D78C1FF0C0 for ; Wed, 04 Nov 2026 10:30:08 +0100 (CET) Received: from gate001.proxmox.com (localhost.localdomain [127.0.0.1]) by gate001.proxmox.com (Proxmox) with ESMTP id 9A4D22143A; Wed, 04 Nov 2026 10:29:57 +0100 (CET) From: Thomas Ellmenreich To: pve-devel@lists.proxmox.com Subject: [PATCH pve-container 1/2] mountpoint: allow changing mountpoint properties at runtime Date: Wed, 4 Nov 2026 10:29:38 +0100 Message-ID: <20261104092939.123574-2-t.ellmenreich@proxmox.com> X-Mailer: git-send-email 2.47.3 In-Reply-To: <20261104092939.123574-1-t.ellmenreich@proxmox.com> References: <20261104092939.123574-1-t.ellmenreich@proxmox.com> MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Bm-Milter-Handled: 55990f41-d878-4baa-be0a-ee34c49e34d2 X-Bm-Transport-Timestamp: 1793784593244 X-SPAM-LEVEL: Spam detection results: 0 AWL 0.360 Adjusted score from AWL reputation of From: address DMARC_MISSING 0.1 Missing DMARC policy KAM_DMARC_STATUS 0.01 Test Rule for DKIM or SPF Failure with Strict Alignment (newer systems) RCVD_IN_DNSWL_MED -2.3 Sender listed at https://www.dnswl.org/, medium trust SPF_HELO_NONE 0.001 SPF: HELO does not publish an SPF Record SPF_PASS -0.001 SPF: sender matches SPF record Message-ID-Hash: IFBEL3PWALBUOZJBYDKFH4XG2DURH4FF X-Message-ID-Hash: IFBEL3PWALBUOZJBYDKFH4XG2DURH4FF X-MailFrom: t.ellmenreich@proxmox.com X-Mailman-Rule-Misses: dmarc-mitigation; no-senders; approved; loop; banned-address; emergency; member-moderation; nonmember-moderation; administrivia; implicit-dest; max-recipients; max-size; news-moderation; no-subject; digests; suspicious-header CC: Thomas Ellmenreich X-Mailman-Version: 3.3.10 Precedence: list List-Id: Proxmox VE development discussion List-Help: List-Owner: List-Post: List-Subscribe: List-Unsubscribe: Previously, setting the backup or replicate flags of a running container would first place them in a pending state before applying them at a later time. As the flags do not affect the actual mount point, they are now applied directly. However, in situations where multiple settings are changed at once and 'backup' or 'replicate' happen to be among them, the flags are not applied directly but instead marked as pending alongside the other changes. Suggested-by: Filip Schauer Signed-off-by: Thomas Ellmenreich --- src/PVE/LXC/Config.pm | 37 ++++++++++++++++++--- src/test/Makefile | 6 +++- src/test/config_tests.pl | 72 ++++++++++++++++++++++++++++++++++++++++ 3 files changed, 109 insertions(+), 6 deletions(-) create mode 100755 src/test/config_tests.pl diff --git a/src/PVE/LXC/Config.pm b/src/PVE/LXC/Config.pm index 9823d00..60318b4 100644 --- a/src/PVE/LXC/Config.pm +++ b/src/PVE/LXC/Config.pm @@ -1738,10 +1738,6 @@ sub vmconfig_hotplug_pending { $hotplug_memory->($conf->{pending}->{memory}, $conf->{pending}->{swap}); } } elsif ($opt =~ m/^mp(\d+)$/) { - if (exists($conf->{$opt})) { - die "skip\n"; # don't try to hotplug over existing mp - } - $class->apply_pending_mountpoint($vmid, $conf, $opt, $storecfg, 1); # apply_pending_mountpoint modifies the value if it creates a new disk $value = $conf->{pending}->{$opt}; @@ -1876,11 +1872,43 @@ my $rescan_volume = sub { warn "Could not rescan volume size - $@\n" if $@; }; +my @FAST_PLUG_SAFE_KEYS = qw(backup replicate); + +# Checks whether the changed properties can be applied at runtime. It only +# returns true if all changes can be applied in this way. If even one +# property cannot be applied, returns false to ensure changes are applied +# as a unit. +sub __mountpoint_config_fast_plug_safe { + my ($class, $key, $new, $old) = @_; + + my %new = $new->%*; + my %old = $old->%*; + delete @new{@FAST_PLUG_SAFE_KEYS}; + delete @old{@FAST_PLUG_SAFE_KEYS}; + + my $new_str = $class->print_volume($key, \%new); + my $old_str = $class->print_volume($key, \%old); + + return $new_str eq $old_str; +} + sub apply_pending_mountpoint { my ($class, $vmid, $conf, $opt, $storecfg, $running) = @_; my $mp = $class->parse_volume($opt, $conf->{pending}->{$opt}); my $old = $conf->{$opt}; + + if (defined($old) && $running) { + if ($class->__mountpoint_config_fast_plug_safe( + $opt, + $mp, + $class->parse_volume($opt, $old), + )) { + return; + } + die "skip\n"; # don't try to hotplug over existing mp + } + if ($mp->{type} eq 'volume' && $mp->{volume} =~ $PVE::LXC::NEW_DISK_RE) { my $original_value = $conf->{pending}->{$opt}; my $vollist = PVE::LXC::create_disks( @@ -1901,7 +1929,6 @@ sub apply_pending_mountpoint { } } } else { - die "skip\n" if $running && defined($old); # TODO: "changing" mount points? $rescan_volume->($storecfg, $mp) if $mp->{type} eq 'volume'; if ($running) { PVE::LXC::mountpoint_hotplug($vmid, $conf, $opt, $mp, $storecfg); diff --git a/src/test/Makefile b/src/test/Makefile index 91ae6ff..a3728b5 100644 --- a/src/test/Makefile +++ b/src/test/Makefile @@ -2,7 +2,7 @@ RUN_USERNS := lxc-usernsexec -m "u:0:`id -u`:1" -m "g:0:`id -g`:1" -- all: test -test: test_setup test_snapshot test_bindmount test_idmap +test: test_setup test_snapshot test_bindmount test_idmap test_config test_setup: run_setup_tests.pl if test -e /run/lock/sbuild; then \ @@ -24,5 +24,9 @@ test_bindmount: bindmount_test.pl test_idmap: run_idmap_tests.pl ./run_idmap_tests.pl +test_config: config_tests.pl + ./config_tests.pl + clean: rm -rf tmprootfs + diff --git a/src/test/config_tests.pl b/src/test/config_tests.pl new file mode 100755 index 0000000..43935a9 --- /dev/null +++ b/src/test/config_tests.pl @@ -0,0 +1,72 @@ +#!/usr/bin/perl + +use strict; +use warnings; + +use lib qw(..); + +use Test::More; + +use PVE::LXC::Config; + +my sub create_conf { + my ($common, $new, $old) = @_; + + my $new_str = defined($new) ? $common . ',' . $new : $common; + my $old_str = defined($old) ? $common . ',' . $old : $common; + return ( + PVE::LXC::Config->parse_volume('mp0', $new_str), + PVE::LXC::Config->parse_volume('mp0', $old_str), + ); +} + +subtest 'same config can be applied' => sub { + my ($new, $old) = create_conf("local:100/vm-100-disk-1.raw,mp=/mnt,backup=1"); + my $result = PVE::LXC::Config->__mountpoint_config_fast_plug_safe("mp0", $new, $old); + + ok($result, "can be hotplugged"); +}; + +subtest 'config with only backup difference can be applied' => sub { + my ($new, $old) = + create_conf("local:100/vm-100-disk-1.raw,mp=/mnt", "backup=1", "backup=0"); + my $result = PVE::LXC::Config->__mountpoint_config_fast_plug_safe("mp0", $new, $old); + + ok($result, "can be hotplugged"); +}; + +subtest 'fast plug option also ignored if previously not present' => sub { + my ($new, $old) = create_conf("local:100/vm-100-disk-1.raw,mp=/mnt", "replicate=1", undef); + my $result = PVE::LXC::Config->__mountpoint_config_fast_plug_safe("mp0", $new, $old); + + ok($result, "can be hotplugged"); +}; + +subtest 'difference in mountpoint cannot be applied' => sub { + my ($new, $old) = + create_conf("local:100/vm-100-disk-1.raw,replicate=1", "mp=/mnt/one", "mp=/mnt/two"); + my $result = PVE::LXC::Config->__mountpoint_config_fast_plug_safe("mp0", $new, $old); + + ok(!$result, "cannot be hotplugged"); +}; + +subtest 'changing fast pluggable and non fast pluggable cannot be applied' => sub { + my ($new, $old) = + create_conf("local:100/vm-100-disk-1.raw", "mp=/mnt/one", "mp=/mnt/two,replicate=1"); + my $result = PVE::LXC::Config->__mountpoint_config_fast_plug_safe("mp0", $new, $old); + + ok(!$result, "cannot be hotplugged"); +}; + +subtest 'idmap array does not throw off comparison' => sub { + my ($new, $old) = create_conf( + "local:100/vm-100-disk-1.raw,mp=/mnt,idmap=u:0:100000:65536;g:0:100000:65536", + "backup=1", + undef, + ); + my $result = PVE::LXC::Config->__mountpoint_config_fast_plug_safe("mp0", $new, $old); + + ok($result, "can be hotplugged"); +}; + +done_testing(); -- 2.47.3