]>
git.proxmox.com Git - pve-manager.git/blob - PVE/CLI/pve5to6.pm
1 package PVE
::CLI
::pve5to6
;
16 use PVE
::RPCEnvironment
;
24 use base
qw(PVE::CLIHandler);
26 my $nodename = PVE
::INotify
::nodename
();
28 sub setup_environment
{
29 PVE
::RPCEnvironment-
>setup_default_cli_env();
32 my $min_pve_rel = '5.4';
33 my $min_pve_pkgrel = 2;
43 my ($level, $line) = @_;
45 $counters->{$level}++ if defined($level) && defined($counters->{$level});
47 print uc($level), ': ' if defined($level);
53 $log_line->('pass', @_);
58 $log_line->('info', @_);
61 $log_line->('skip', @_);
64 print color
('yellow');
65 $log_line->('warn', @_);
70 $log_line->('fail', @_);
77 my $versions = eval { PVE
::API2
::APT-
>versions({ node
=> $nodename }); };
79 if (!defined($versions)) {
80 my $msg = "unable to retrieve package version information";
86 my $pkgs = [ grep { $_->{Package
} eq $pkg } @$versions ];
87 if (!defined $pkgs || $pkgs == 0) {
88 log_fail
("unable to determine installed $pkg version.");
95 sub check_pve_packages
{
96 print "CHECKING VERSION INFORMATION FOR PVE PACKAGES\n";
98 print "\nChecking for package updates..\n";
99 my $updates = eval { PVE
::API2
::APT-
>list_updates({ node
=> $nodename }); };
100 if (!defined($updates)) {
101 log_warn
("$@") if $@;
102 log_fail
("unable to retrieve list of package updates!");
103 } elsif (@$updates > 0) {
104 my $pkgs = join(', ', map { $_->{Package
} } @$updates);
105 log_warn
("updates for the following packages are available: $pkgs");
107 log_pass
("all packages uptodate");
110 print "\nChecking proxmox-ve package version..\n";
111 if (defined(my $proxmox_ve = $get_pkg->('proxmox-ve'))) {
112 my $min_pve_ver = "$min_pve_rel-$min_pve_pkgrel";
114 if ($proxmox_ve->{OldVersion
} =~ m/^$min_pve_rel-(\d+)/ && $1 >= $min_pve_pkgrel) {
115 log_pass
("proxmox-ve package has version >= $min_pve_ver");
117 log_fail
("proxmox-ve package is too old, please upgrade to >= $min_pve_ver!");
122 sub check_storage_health
{
123 print "\nCHECKING CONFIGURED STORAGES\n\n";
124 my $cfg = PVE
::Storage
::config
();
128 my $info = PVE
::Storage
::storage_info
($cfg);
130 foreach my $storeid (keys %$info) {
131 my $d = $info->{$storeid};
134 log_pass
("storage '$storeid' enabled and active.");
136 log_warn
("storage '$storeid' enabled but not active!");
139 log_skip
("storage '$storeid' disabled.");
144 sub check_cluster_corosync
{
145 print "\nCHECKING CLUSTER HEALTH/SETTINGS\n\n";
147 if (!PVE
::Corosync
::check_conf_exists
(1)) {
148 log_skip
("standalone node.");
152 if (PVE
::Cluster
::check_cfs_quorum
(1)) {
153 log_pass
("Cluster is quorate.");
155 log_fail
("Cluster lost quorum!");
158 my $conf = PVE
::Cluster
::cfs_read_file
('corosync.conf');
159 my $conf_nodelist = PVE
::Corosync
::nodelist
($conf);
161 if (!defined($conf_nodelist)) {
162 log_fail
("unable to retrieve nodelist from corosync.conf");
163 } elsif (grep { $conf_nodelist->{$_}->{quorum_votes
} != 1 } keys %$conf_nodelist) {
164 log_warn
("non-default quorum_votes distribution detected!");
167 my $cfs_nodelist = PVE
::Cluster
::get_clinfo
()->{nodelist
};
168 my $offline_nodes = grep { $cfs_nodelist->{$_}->{online
} != 1 } keys %$cfs_nodelist;
169 if ($offline_nodes > 0) {
170 log_fail
("$offline_nodes nodes are offline!");
173 my $conf_nodelist_count = scalar(keys %$conf_nodelist);
174 my $cfs_nodelist_count = scalar(keys %$cfs_nodelist);
175 log_warn
("cluster consists of less than three nodes!")
176 if $conf_nodelist_count < 3;
178 log_fail
("corosync.conf ($conf_nodelist_count) and pmxcfs ($cfs_nodelist_count) don't agree about size of nodelist.")
179 if $conf_nodelist_count != $cfs_nodelist_count;
181 foreach my $cs_node (keys %$conf_nodelist) {
182 my $entry = $conf_nodelist->{$cs_node};
183 log_fail
("No name entry for node '$cs_node' in corosync.conf.")
184 if !defined($entry->{name
});
185 log_fail
("No nodeid configured for node '$cs_node' in corosync.conf.")
186 if !defined($entry->{nodeid
});
188 my $verify_ring_ip = sub {
190 my $ring = $entry->{$key};
191 if (defined($ring) && !PVE
::JSONSchema
::pve_verify_ip
($ring, 1)) {
192 log_fail
("$key '$ring' of node '$cs_node' is not an IP address, consider replacing it with the currently resolved IP address.");
195 $verify_ring_ip->('ring0_addr');
196 $verify_ring_ip->('ring1_addr');
199 my $totem = $conf->{main
}->{totem
};
201 my $transport = $totem->{transport
};
202 if (defined($transport)) {
203 log_fail
("Corosync transport expliclitly set to '$transport' instead of implicit default!");
206 if ((!defined($totem->{secauth
}) || $totem->{secauth
} ne 'on') && (!defined($totem->{crypto_cipher
}) || $totem->{crypto_cipher
} eq 'none')) {
207 log_fail
("Corosync authentication/encryption is not explicitly enabled (secauth / crypto_cipher / crypto_hash)!");
210 if (defined($totem->{crypto_cipher
}) && $totem->{crypto_cipher
} eq '3des') {
211 log_fail
("Corosync encryption cipher set to '3des', no longer supported in Corosync 3.x!");
214 my $prefix_info = sub { my $line = shift; log_info
("$line"); };
217 log_info
("Printing detailed cluster status..");
218 PVE
::Tools
::run_command
(['corosync-quorumtool', '-siH'], outfunc
=> $prefix_info, errfunc
=> $prefix_info);
221 print "\nCHECKING INSTALLED COROSYNC VERSION\n\n";
222 if (defined(my $corosync = $get_pkg->('corosync'))) {
223 if ($corosync->{OldVersion
} =~ m/^2\./) {
224 log_fail
("corosync 2.x installed, cluster-wide upgrade to 3.x needed!");
225 } elsif ($corosync->{OldVersion
} =~ m/^3\./) {
226 log_pass
("corosync 3.x installed.");
228 log_fail
("unexpected corosync version installed: $corosync->{OldVersion}!");
234 print "\nCHECKING HYPER-CONVERGED CEPH STATUS\n\n";
236 if (PVE
::Ceph
::Tools
::check_ceph_inited
(1)) {
237 log_info
("hyper-converged ceph setup detected!");
239 log_skip
("no hyper-converged ceph setup detected!");
243 log_info
("getting Ceph status/health information..");
244 my $ceph_status = eval { PVE
::API2
::Ceph-
>status({ node
=> $nodename }); };
245 my $osd_flags = eval { PVE
::API2
::Ceph-
>get_flags({ node
=> $nodename }); };
247 $noout = $osd_flags =~ m/noout/ if $osd_flags;
249 if (!$ceph_status || !$ceph_status->{health
}) {
250 log_fail
("unable to determine Ceph status!");
252 my $ceph_health = $ceph_status->{health
}->{status
};
254 log_fail
("unable to determine Ceph health!");
255 } elsif ($ceph_health eq 'HEALTH_OK') {
256 log_pass
("Ceph health reported as 'HEALTH_OK'.");
257 } elsif ($ceph_health eq 'HEALTH_WARN' && $noout && (keys %{$ceph_status->{health
}->{checks
}} == 1)) {
258 log_pass
("Ceph health reported as 'HEALTH_WARN' with a single failing check and 'noout' flag set.");
260 log_warn
("Ceph health reported as '$ceph_health'");
264 log_info
("getting Ceph OSD flags..");
267 log_fail
("unable to get Ceph OSD flags!");
269 if ($osd_flags =~ m/recovery_deletes/ && $osd_flags =~ m/purged_snapdirs/) {
270 log_pass
("all PGs have been scrubbed at least once while running Ceph Luminous.");
272 log_fail
("missing 'recovery_deletes' and/or 'purged_snapdirs' flag, scrub of all PGs required before upgrading to Nautilus!");
275 log_pass
("noout flag set to prevent rebalancing during cluster-wide upgrades.");
277 log_warn
("noout flag not set - recommended to prevent rebalancing during upgrades.");
282 log_info
("getting Ceph daemon versions..");
283 my $ceph_versions = eval { PVE
::Ceph
::Tools
::get_cluster_versions
(undef, 1); };
284 if (!$ceph_versions) {
285 log_fail
("unable to determine Ceph daemon versions!");
288 { 'key' => 'mon', 'name' => 'monitor' },
289 { 'key' => 'mgr', 'name' => 'manager' },
290 { 'key' => 'mds', 'name' => 'MDS' },
291 { 'key' => 'osd', 'name' => 'OSD' },
294 foreach my $service (@$services) {
295 my $name = $service->{name
};
296 if (my $service_versions = $ceph_versions->{$service->{key
}}) {
297 if (keys %$service_versions == 0) {
298 log_skip
("no running instances detected for daemon type $name.");
299 } elsif (keys %$service_versions == 1) {
300 log_pass
("single running version detected for daemon type $name.");
302 log_warn
("multiple running versions detected for daemon type $name!");
305 log_skip
("unable to determine versions of running Ceph $name instances.");
309 my $overall_versions = $ceph_versions->{overall
};
310 if (!$overall_versions) {
311 log_warn
("unable to determine overall Ceph daemon versions!");
312 } elsif (keys %$overall_versions == 1) {
313 log_pass
("single running overall version detected for all Ceph daemon types.");
315 log_warn
("overall version mismatch detected, check 'ceph versions' output for details!");
321 print "\nMISCELLANEOUS CHECKS\n\n";
322 my $ssh_config = eval { PVE
::Tools
::file_get_contents
('/root/.ssh/config') };
323 if (defined($ssh_config)) {
324 log_fail
("Unsupported SSH Cipher configured for root in /root/.ssh/config: $1")
325 if $ssh_config =~ /^Ciphers .*(blowfish|arcfour|3des).*$/m;
327 log_skip
("No SSH config file found.");
330 my $root_free = PVE
::Tools
::df
('/', 10);
331 log_warn
("Less than 2G free space on root file system.")
332 if defined($root_free) && $root_free->{avail
} < 2*1024*1024*1024;
334 my $running_guests = 0;
335 my $vms = eval { PVE
::API2
::Qemu-
>vmlist({ node
=> $nodename }) };
336 log_warn
("Failed to retrieve information about this node's VMs - $@") if $@;
337 $running_guests += grep { $_->{status
} eq 'running' } @$vms
339 my $cts = eval { PVE
::API2
::LXC-
>vmlist({ node
=> $nodename }) };
340 log_warn
("Failed to retrieve information about this node's CTs - $@") if $@;
341 $running_guests += grep { $_->{status
} eq 'running' } @$cts
343 log_warn
("$running_guests running guests detected - consider migrating/stopping them.")
344 if $running_guests > 0;
347 __PACKAGE__-
>register_method ({
351 description
=> 'Check (pre-/post-)upgrade conditions.',
353 additionalProperties
=> 0,
357 returns
=> { type
=> 'null' },
361 check_pve_packages
();
362 check_cluster_corosync
();
364 check_storage_health
();
367 print "\n\nSUMMARY:\n";
368 print colored
("PASSED: $counters->{pass}\n", 'green');
369 print "SKIPPED: $counters->{skip}\n";
370 print colored
("WARNINGS: $counters->{warn}\n", 'yellow');
371 print colored
("FAILURES: $counters->{fail}\n", 'red');
373 print colored
("\nATTENTION: Please check the output for detailed information!\n", 'red')
374 if ($counters->{warn} > 0 || $counters->{fail
} > 0);
380 checklist
=> [ __PACKAGE__
, 'checklist', [], {}],