]> git.proxmox.com Git - pve-manager.git/blobdiff - PVE/CLI/pve6to7.pm
pve6to7: dont guard noout check on Ceph version
[pve-manager.git] / PVE / CLI / pve6to7.pm
index 899ef6c659ec1fb8e5846517e2b6350d2f36b364..629d69351434fa345b3e2d34bb088e6f0abbbe36 100644 (file)
@@ -8,6 +8,7 @@ use PVE::API2::Ceph;
 use PVE::API2::LXC;
 use PVE::API2::Qemu;
 use PVE::API2::Certificates;
+use PVE::API2::Cluster::Ceph;
 
 use PVE::AccessControl;
 use PVE::Ceph::Tools;
@@ -23,6 +24,9 @@ use PVE::Tools qw(run_command split_list);
 use PVE::QemuConfig;
 use PVE::QemuServer;
 use PVE::VZDump::Common;
+use PVE::LXC;
+use PVE::LXC::Config;
+use PVE::LXC::Setup;
 
 use Term::ANSIColor;
 
@@ -204,7 +208,7 @@ sub check_storage_health {
 
     my $info = PVE::Storage::storage_info($cfg);
 
-    foreach my $storeid (keys %$info) {
+    foreach my $storeid (sort keys %$info) {
        my $d = $info->{$storeid};
        if ($d->{enabled}) {
            if ($d->{type} eq 'sheepdog') {
@@ -389,9 +393,12 @@ sub check_ceph {
 
     log_info("getting Ceph status/health information..");
     my $ceph_status = eval { PVE::API2::Ceph->status({ node => $nodename }); };
-    my $osd_flags = eval { PVE::API2::Ceph->get_flags({ node => $nodename }); };
+    my $noout = eval { PVE::API2::Cluster::Ceph->get_flag({ flag => "noout" }); };
+    if ($@) {
+       log_fail("failed to get 'noout' flag status - $@");
+    }
+
     my $noout_wanted = 1;
-    my $noout = $osd_flags && $osd_flags =~ m/noout/;
 
     if (!$ceph_status || !$ceph_status->{health}) {
        log_fail("unable to determine Ceph status!");
@@ -409,19 +416,6 @@ sub check_ceph {
        }
     }
 
-    log_info("getting Ceph OSD flags..");
-    eval {
-       if (!$osd_flags) {
-           log_fail("unable to get Ceph OSD flags!");
-       } else {
-           if ($osd_flags =~ m/recovery_deletes/ && $osd_flags =~ m/purged_snapdirs/) {
-               log_pass("all PGs have been scrubbed at least once while running Ceph Luminous."); # FIXME: remove?
-           } else {
-               log_fail("missing 'recovery_deletes' and/or 'purged_snapdirs' flag, scrub of all PGs required before upgrading to Nautilus!");
-           }
-       }
-    };
-
     # TODO: check OSD min-required version, if to low it breaks stuff!
 
     log_info("getting Ceph daemon versions..");
@@ -456,9 +450,7 @@ sub check_ceph {
            log_warn("unable to determine overall Ceph daemon versions!");
        } elsif (keys %$overall_versions == 1) {
            log_pass("single running overall version detected for all Ceph daemon types.");
-           if ((keys %$overall_versions)[0] =~ /^ceph version 15\./) {
-               $noout_wanted = 0;
-           }
+           $noout_wanted = 0; # off post-upgrade, on pre-upgrade
        } else {
            log_warn("overall version mismatch detected, check 'ceph versions' output for details!");
        }
@@ -482,8 +474,6 @@ sub check_ceph {
        my $global_monhost = $global->{mon_host} // $global->{"mon host"} // $global->{"mon-host"};
        if (!defined($global_monhost)) {
            log_warn("No 'mon_host' entry found in ceph config.\n  It's recommended to add mon_host with all monitor addresses (without ports) to the global section.");
-       } else {
-           log_pass("Found 'mon_host' entry.");
        }
 
        my $ipv6 = $global->{ms_bind_ipv6} // $global->{"ms bind ipv6"} // $global->{"ms-bind-ipv6"};
@@ -491,17 +481,11 @@ sub check_ceph {
            my $ipv4 = $global->{ms_bind_ipv4} // $global->{"ms bind ipv4"} // $global->{"ms-bind-ipv4"};
            if ($ipv6 eq 'true' && (!defined($ipv4) || $ipv4 ne 'false')) {
                log_warn("'ms_bind_ipv6' is enabled but 'ms_bind_ipv4' is not disabled.\n  Make sure to disable 'ms_bind_ipv4' for ipv6 only clusters, or add an ipv4 network to public/cluster network.");
-           } else {
-               log_pass("'ms_bind_ipv6' is enabled and 'ms_bind_ipv4' disabled");
            }
-       } else {
-           log_pass("'ms_bind_ipv6' not enabled");
        }
 
        if (defined($global->{keyring})) {
            log_warn("[global] config section contains 'keyring' option, which will prevent services from starting with Nautilus.\n Move 'keyring' option to [client] section instead.");
-       } else {
-           log_pass("no 'keyring' option in [global] section found.");
        }
 
     } else {
@@ -510,12 +494,8 @@ sub check_ceph {
 
     my $local_ceph_ver = PVE::Ceph::Tools::get_local_version(1);
     if (defined($local_ceph_ver)) {
-       if ($local_ceph_ver == 14) {
-           my $ceph_volume_osds = PVE::Ceph::Tools::ceph_volume_list();
-           my $scanned_osds = PVE::Tools::dir_glob_regex('/etc/ceph/osd', '^.*\.json$');
-           if (-e '/var/lib/ceph/osd/' && !defined($scanned_osds) && !(keys %$ceph_volume_osds)) {
-               log_warn("local Ceph version is Nautilus, local OSDs detected, but no conversion from ceph-disk to ceph-volume done (yet).");
-           }
+       if ($local_ceph_ver <= 14) {
+           log_fail("local Ceph version too low, at least Octopus required..");
        }
     } else {
        log_fail("unable to determine local Ceph version.");
@@ -705,7 +685,7 @@ sub check_description_lengths {
        push @$affected_guests, "VM $vmid" if defined($desc) && length($desc) > 8 * 1024;
     }
     if (scalar($affected_guests->@*) > 0) {
-       log_warn("Node config description of the following nodes too long for new limit of 64 KiB:\n"
+       log_warn("Guest config description of the following virtual-guests too long for new limit of 64 KiB:\n"
            ."    * " . join("\n    * ", $affected_guests->@*));
     } else {
        log_pass("All guest config descriptions fit in the new limit of 8 KiB");
@@ -724,7 +704,7 @@ sub check_storage_content {
     my $potentially_affected = {};
     my $referenced_volids = {};
 
-    for my $storeid (keys $storage_cfg->{ids}->%*) {
+    for my $storeid (sort keys $storage_cfg->{ids}->%*) {
        my $scfg = $storage_cfg->{ids}->{$storeid};
 
        next if !PVE::Storage::storage_check_enabled($storage_cfg, $storeid, undef, 1);
@@ -893,6 +873,128 @@ sub check_storage_content {
     }
 }
 
+sub check_containers_cgroup_compat {
+
+    my $kernel_cli = PVE::Tools::file_get_contents('/proc/cmdline');
+    if ($kernel_cli =~ /systemd.unified_cgroup_hierarchy=0/){
+       log_skip("System explicitly configured for legacy hybrid cgroup hierarchy.");
+       return;
+    }
+
+    my $supports_cgroupv2 = sub {
+       my ($conf, $rootdir, $ctid) = @_;
+
+       my $get_systemd_version = sub {
+           my ($self) = @_;
+
+           my $sd_lib_dir = -d "/lib/systemd" ? "/lib/systemd" : "/usr/lib/systemd";
+           my $libsd = PVE::Tools::dir_glob_regex($sd_lib_dir, "libsystemd-shared-.+\.so");
+           if (defined($libsd) && $libsd =~ /libsystemd-shared-(\d+)\.so/) {
+               return $1;
+           }
+
+           return undef;
+       };
+
+       my  $unified_cgroupv2_support = sub {
+           my ($self) = @_;
+
+           # https://www.freedesktop.org/software/systemd/man/systemd.html
+           # systemd is installed as symlink to /sbin/init
+           my $systemd = CORE::readlink('/sbin/init');
+
+           # assume non-systemd init will run with unified cgroupv2
+           if (!defined($systemd) || $systemd !~ m@/systemd$@) {
+               return 1;
+           }
+
+           # systemd version 232 (e.g. debian stretch) supports the unified hierarchy
+           my $sdver = $get_systemd_version->();
+           if (!defined($sdver) || $sdver < 232) {
+               return 0;
+           }
+
+           return 1;
+       };
+
+       my $ostype = $conf->{ostype};
+       if (!defined($ostype)) {
+           log_warn("Found CT ($ctid) without 'ostype' set!");
+       } elsif ($ostype eq 'devuan' || $ostype eq 'alpine') {
+           return 1; # no systemd, no cgroup problems
+       }
+
+       my $lxc_setup = PVE::LXC::Setup->new($conf, $rootdir);
+       return $lxc_setup->protected_call($unified_cgroupv2_support);
+    };
+
+    my $log_problem = sub {
+       my ($ctid) = @_;
+       log_warn("Found at least one CT ($ctid) which does not support running in a unified cgroup v2" .
+           " layout.\n    Either upgrade the Container distro or set systemd.unified_cgroup_hierarchy=0 " .
+           "in the Proxmox VE hosts' kernel cmdline! Skipping further CT compat checks."
+       );
+    };
+
+    my $cts = eval { PVE::API2::LXC->vmlist({ node => $nodename }) };
+    if ($@) {
+       log_warn("Failed to retrieve information about this node's CTs - $@");
+       return;
+    }
+
+    if (!defined($cts) || !scalar(@$cts)) {
+       log_skip("No containers on node detected.");
+       return;
+    }
+
+    my @running_cts = sort { $a <=> $b } grep { $_->{status} eq 'running' } @$cts;
+    my @offline_cts = sort { $a <=> $b } grep { $_->{status} ne 'running' } @$cts;
+
+    for my $ct (@running_cts) {
+       my $ctid = $ct->{vmid};
+       my $pid = eval { PVE::LXC::find_lxc_pid($ctid) };
+       if (my $err = $@) {
+           log_warn("Failed to get PID for running CT $ctid - $err");
+           next;
+       }
+       my $rootdir = "/proc/$pid/root";
+       my $conf = PVE::LXC::Config->load_config($ctid);
+
+       my $ret = eval { $supports_cgroupv2->($conf, $rootdir, $ctid) };
+       if (my $err = $@) {
+           log_warn("Failed to get cgroup support status for CT $ctid - $err");
+           next;
+       }
+       if (!$ret) {
+           $log_problem->($ctid);
+           return;
+       }
+    }
+
+    my $storage_cfg = PVE::Storage::config();
+    for my $ct (@offline_cts) {
+       my $ctid = $ct->{vmid};
+       my ($conf, $rootdir, $ret);
+       eval {
+           $conf = PVE::LXC::Config->load_config($ctid);
+           $rootdir = PVE::LXC::mount_all($ctid, $storage_cfg, $conf);
+           $ret = $supports_cgroupv2->($conf, $rootdir, $ctid);
+       };
+       if (my $err = $@) {
+           log_warn("Failed to load config and mount CT $ctid - $err");
+           eval { PVE::LXC::umount_all($ctid, $storage_cfg, $conf) };
+           next;
+       }
+       if (!$ret) {
+           $log_problem->($ctid);
+           eval { PVE::LXC::umount_all($ctid, $storage_cfg, $conf) };
+           last;
+       }
+
+       eval { PVE::LXC::umount_all($ctid, $storage_cfg, $conf) };
+    }
+};
+
 sub check_misc {
     print_header("MISCELLANEOUS CHECKS");
     my $ssh_config = eval { PVE::Tools::file_get_contents('/root/.ssh/config') };
@@ -998,6 +1100,12 @@ __PACKAGE__->register_method ({
     parameters => {
        additionalProperties => 0,
        properties => {
+           full => {
+               description => 'perform additional, expensive checks.',
+               type => 'boolean',
+               optional => 1,
+               default => 0,
+           },
        },
     },
     returns => { type => 'null' },
@@ -1010,6 +1118,12 @@ __PACKAGE__->register_method ({
        check_storage_health();
        check_misc();
 
+       if ($param->{full}) {
+           check_containers_cgroup_compat();
+       } else {
+           log_skip("NOTE: Expensive checks, like CT cgroupv2 compat, not performed without '--full' parameter");
+       }
+
        print_header("SUMMARY");
 
        my $total = 0;
@@ -1032,7 +1146,4 @@ __PACKAGE__->register_method ({
 
 our $cmddef = [ __PACKAGE__, 'checklist', [], {}];
 
-# for now drop all unknown params and just check
-@ARGV = ();
-
 1;