#!/usr/bin/perl use v5.36; use JSON qw(decode_json); use PVE::Tools; my sub ceph_volume_lvm_osd_info : prototype() () { my $cmd = [ "/usr/sbin/ceph-volume", "lvm", "list", "--format", "json", ]; my $stdout = ''; my $outfunc = sub($line) { $stdout .= "$line\n"; }; my $stderr = ''; my $errfunc = sub($line) { $stderr .= "$line\n"; }; eval { PVE::Tools::run_command( $cmd, timeout => 10, outfunc => $outfunc, errfunc => $errfunc, ); }; if (my $err = $@) { $err = "$err\n" if $err !~ m/\n$/; print STDERR $stderr; *STDERR->flush(); die $err; } my $osd_info = decode_json($stdout); return $osd_info; } my sub lvs : prototype() () { my $cmd = [ "/usr/sbin/lvs", "--noheadings", "--separator", ":", "--options", "lv_name,vg_name,autoactivation", ]; my $all_lvs = {}; my $outfunc = sub($line) { $line = PVE::Tools::trim($line); my ($lv_name, $vg_name, $autoactivation) = split(':', $line, -1); return undef if ($lv_name eq '' || $vg_name eq ''); $all_lvs->{"$vg_name/$lv_name"} = { autoactivation => $autoactivation, }; }; my $stderr = ''; my $errfunc = sub($line) { $stderr .= "$line\n"; }; eval { PVE::Tools::run_command( $cmd, timeout => 10, outfunc => $outfunc, errfunc => $errfunc, ); }; if (my $err = $@) { $err = "$err\n" if $err !~ m/\n$/; print STDERR $stderr; *STDERR->flush(); die $err; } return $all_lvs; } my sub main : prototype() () { my $osd_info = ceph_volume_lvm_osd_info(); my $all_lvs = lvs(); my $re_uuid4 = qr/ \b [0-9a-fA-F]{8} - [0-9a-fA-F]{4} - [0-9a-fA-F]{4} - [0-9a-fA-F]{4} - [0-9a-fA-F]{12} \b /x; # $re_lv_name and $re_vg_name specifically match the LV and VG names we # assign in OSD.pm in order to avoid modifying LVs created through means # other than our API. # Also include osd-block LVs for good measure. my $re_lv_name = qr/^ osd-(db|wal|block) - $re_uuid4 $/nx; my $re_vg_name = qr/^ (ceph) - $re_uuid4 $/nx; my @osd_lvs_no_autoactivation = (); for my $osd (keys $osd_info->%*) { for my $osd_lv ($osd_info->{$osd}->@*) { my ($lv_name, $vg_name) = $osd_lv->@{qw(lv_name vg_name)}; next if $all_lvs->{$osd_lv}->{autoactivation}; next if $lv_name !~ $re_lv_name; next if $vg_name !~ $re_vg_name; my $osd_lv = "$vg_name/$lv_name"; push(@osd_lvs_no_autoactivation, $osd_lv) if !$all_lvs->{$osd_lv}->{autoactivation}; } } my $has_err = 0; # Logical volumes are formatted as "vg_name/lv_name", which is necessary for lvchange for my $lv (@osd_lvs_no_autoactivation) { my $log = ''; my $logfunc = sub($line) { $log .= "$line\n"; }; eval { my $cmd = [ '/usr/sbin/lvchange', '--setautoactivation', 'y', $lv, ]; PVE::Tools::run_command( $cmd, logfunc => $logfunc, timeout => 10, ); }; if (my $err = $@) { $has_err = 1; $err = "$err\n" if $err !~ m/\n$/; print STDERR $log; *STDERR->flush(); warn("Error: Failed to enable autoactivation for Ceph OSD logical volume '$lv'\n"); warn("$err"); next; } } if ($has_err) { warn("Couldn't enable autoactivation for all Ceph OSD DB/WAL/BLOCK logical volumes.\n"); exit 1; } return undef; } main();