[pve-devel] [PATCH pve-manager stable-8 v3 3/3] pve8to9: detect and (if requested) disable LVM autoactivation

Friedrich Weber f.weber at proxmox.com
Tue Apr 29 13:36:46 CEST 2025


Starting with PVE 9, the LVM and LVM-thin plugins create new LVs with
the `--setautoactivation n` flag to fix #4997 [1]. However, this does
not affect already existing LVs of setups upgrading from PVE 8.

Hence, add a new `updatelvm` subcommand to `pve8to9` that finds guest
volume LVs with autoactivation enabled on LVM and LVM-thin storages,
and disables autoactivation on each of them. This is done while
holding a lock on the storage, to avoid metadata update conflicts for
shared LVM storages.

Also, check for guest volume LVs with autoactivation enabled during
the `checklist` command, and suggest to run `updatelvm` if necessary.
The check is done without a lock, as taking a lock doesn't have
advantages here.

While some users may have disabled autoactivation on the VG level to
work around #4997, consciously do not take this into account:
Disabling autoactivation on LVs too does not hurt, and avoids issues
in case the user decides to re-enable autoactivation on the VG level
in the future.

[1] https://bugzilla.proxmox.com/show_bug.cgi?id=4997

Signed-off-by: Friedrich Weber <f.weber at proxmox.com>
---

Notes:
    open questions:
    
    - if users create new LVs on PVE 8 nodes after running `pve8to9
      updatelvm` but before actually upgrading to PVE 9, these will still
      be created with autoactivation enabled. Is this a case we want to
      consider? If yes, we could suggest running `pve8to updatelvm` (only)
      post-upgrade on all nodes, but users may forget this...
    
    - `updatelvm` will disable autoactivation on all guest volumes,
      regardless of the guest owner. In other words, it will disable
      autoactivation on LVs owned by a different node. Is this a problem?
      My tests suggests it's unproblematic, but may be worth a second
      look. We can change `updatelvm` to only update LVs owned by the
      local node, but then might miss LVs that are migrated between
      `updatelvm` runs on different nodes.
    
    new in v3

 PVE/CLI/pve8to9.pm | 152 ++++++++++++++++++++++++++++++++++++++++++++-
 1 file changed, 151 insertions(+), 1 deletion(-)

diff --git a/PVE/CLI/pve8to9.pm b/PVE/CLI/pve8to9.pm
index fbb96491..972c91ea 100644
--- a/PVE/CLI/pve8to9.pm
+++ b/PVE/CLI/pve8to9.pm
@@ -22,7 +22,7 @@ use PVE::NodeConfig;
 use PVE::RPCEnvironment;
 use PVE::Storage;
 use PVE::Storage::Plugin;
-use PVE::Tools qw(run_command split_list file_get_contents);
+use PVE::Tools qw(run_command split_list file_get_contents trim);
 use PVE::QemuConfig;
 use PVE::QemuServer;
 use PVE::VZDump::Common;
@@ -1372,6 +1372,95 @@ sub check_dkms_modules {
     }
 }
 
+my $query_lvm_lv_autoactivation = sub {
+    my ($vg_name) = @_;
+
+    my $cmd = [
+	'/sbin/lvs',
+	'--separator', ':',
+	'--noheadings',
+	'--unbuffered',
+	'--options', "lv_name,autoactivation",
+	$vg_name,
+    ];
+
+    my $result;
+    eval {
+	run_command($cmd, outfunc => sub {
+	    my $line = shift;
+	    $line = trim($line);
+
+	    my ($name, $autoactivation_flag) = split(':', $line);
+	    return if !$name;
+
+	    $result->{$name} = $autoactivation_flag eq 'enabled';
+	});
+    };
+    die "could not list LVM logical volumes: $@\n" if $@;
+    return $result;
+};
+
+my $foreach_lvm_vg_with_autoactivated_guest_volumes = sub {
+    my ($with_lock, $code) = @_;
+
+    my $cfg = PVE::Storage::config();
+    my $storage_info = PVE::Storage::storage_info($cfg);
+
+    for my $storeid (sort keys %$storage_info) {
+	my $scfg = PVE::Storage::storage_config($cfg, $storeid);
+	my $vgname = $scfg->{vgname};
+	my $type = $scfg->{type};
+	next if $type ne 'lvm' && $type ne 'lvmthin';
+
+	my $info = $storage_info->{$storeid};
+	if (!$info->{enabled} || !$info->{active}) {
+	    log_skip("storage '$storeid' ($type) is disabled or inactive");
+	    next;
+	}
+
+	my $find_autoactivated_lvs = sub {
+	    my $lvs_autoactivation = $query_lvm_lv_autoactivation->($vgname);
+	    my $vollist = PVE::Storage::volume_list($cfg, $storeid);
+
+	    my $autoactivated_guest_lvs = [];
+	    for my $volinfo (@$vollist) {
+		my $volname = (PVE::Storage::parse_volume_id($volinfo->{volid}))[1];
+		push @$autoactivated_guest_lvs, $volname if $lvs_autoactivation->{$volname};
+	    }
+
+	    $code->($storeid, $vgname, $autoactivated_guest_lvs);
+	};
+
+	if ($with_lock) {
+	    my $plugin = PVE::Storage::Plugin->lookup($scfg->{type});
+	    $plugin->cluster_lock_storage($storeid, $scfg->{shared}, undef,
+		$find_autoactivated_lvs);
+	} else {
+	    $find_autoactivated_lvs->();
+	}
+    };
+};
+
+sub check_lvm_autoactivation {
+    log_info("Check for LVM autoactivation settings on 'lvm' and 'lvmthin' storages...");
+
+    my $needs_fix = 0;
+    $foreach_lvm_vg_with_autoactivated_guest_volumes->(0, sub {
+	my ($storeid, $vgname, $autoactivated_lvs) = @_;
+
+	if (scalar(@$autoactivated_lvs) > 0) {
+	    log_warn("storage '$storeid' has guest volumes with autoactivation enabled");
+	    $needs_fix = 1;
+	} else {
+	    log_pass("all guest volumes on storage '$storeid' have autoactivation disabled");
+	}
+    });
+    log_warn("please run 'pve8to9 updatelvm' to disable autoactivation on LVM guest volumes")
+	if $needs_fix;
+
+    return undef;
+}
+
 sub check_misc {
     print_header("MISCELLANEOUS CHECKS");
     my $ssh_config = eval { PVE::Tools::file_get_contents('/root/.ssh/config') };
@@ -1474,6 +1563,7 @@ sub check_misc {
     check_nvidia_vgpu_service();
     check_bootloader();
     check_dkms_modules();
+    check_lvm_autoactivation();
 }
 
 my sub colored_if {
@@ -1538,8 +1628,68 @@ __PACKAGE__->register_method ({
 	return undef;
     }});
 
+__PACKAGE__->register_method ({
+    name => 'updatelvm',
+    path => 'updatelvm',
+    method => 'POST',
+    description => 'disable autoactivation for all LVM guest volumes',
+    parameters => {
+	additionalProperties => 0,
+    },
+    returns => { type => 'null' },
+    code => sub {
+	my ($param) = @_;
+
+	my $did_work = 0;
+	eval {
+	    $foreach_lvm_vg_with_autoactivated_guest_volumes->(1, sub {
+		my ($storeid, $vgname, $autoactivated_lvs) = @_;
+
+		if (scalar(@$autoactivated_lvs) == 0) {
+		    log_pass("all guest volumes on storage '$storeid' have autoactivation disabled");
+		    return;
+		}
+
+		log_info("disabling autoactivation on storage '$storeid'...");
+		die "unexpected empty VG name (storage '$storeid')\n" if !$vgname;
+
+		for my $lvname (@$autoactivated_lvs) {
+		    log_info("disabling autoactivation for $vgname/$lvname"
+			." on storage '$storeid'...");
+		    my $cmd = [
+			'/sbin/lvchange',
+			'--setautoactivation', 'n',
+			"$vgname/$lvname",
+		    ];
+
+		    eval { run_command($cmd); };
+		    my $err = $@;
+		    die "could not disable autoactivation on $vgname/$lvname: $err\n" if $err;
+
+		    $did_work = 1;
+		}
+	    });
+	};
+	my $err = $@;
+	if ($err) {
+	    log_fail("could not disable autoactivation on enabled and active LVM storages");
+	    die $err;
+	}
+
+	if($did_work) {
+	    log_pass("successfully disabled autoactivation on guest volumes on all"
+		." enabled and active LVM storages");
+	} else {
+	    log_pass("all guest volumes on enabled and active LVM storages"
+		." have autoactivation disabled");
+	};
+
+	return undef;
+    }});
+
 our $cmddef = {
     checklist => [ __PACKAGE__, 'checklist', []],
+    updatelvm => [ __PACKAGE__, 'updatelvm', []],
 };
 
 1;
-- 
2.39.5





More information about the pve-devel mailing list