diff --git a/PVE/API2/Cluster/BackupInfo.pm b/PVE/API2/Cluster/BackupInfo.pm index 4ef59ac..1ac5966 100644 --- a/PVE/API2/Cluster/BackupInfo.pm +++ b/PVE/API2/Cluster/BackupInfo.pm @@ -25,7 +25,12 @@ sub get_included_vmids { my $all_vmids = {}; for my $job ($legacy_jobs->@*, grep { $_->{type} eq 'vzdump' } values $jobs->{ids}->%*) { - my $job_included_guests = PVE::VZDump::get_included_guests($job); + # a job referencing e.g. a deleted pool backs up nothing, but must not break the overview + my $job_included_guests = eval { PVE::VZDump::get_included_guests($job) }; + if (my $err = $@) { + warn "ignoring backup job with invalid guest selection - $err"; + next; + } $all_vmids->{$_} = 1 for map { $_->@* } values %{$job_included_guests}; } diff --git a/test/Makefile b/test/Makefile index 2dfd20a..adc1f17 100644 --- a/test/Makefile +++ b/test/Makefile @@ -18,7 +18,7 @@ replication%.t: replication_test%.pl ./$< .PHONY: test-vzdump -test-vzdump: test-vzdump-guest-included test-vzdump-new +test-vzdump: test-vzdump-guest-included test-vzdump-new test-vzdump-backup-info .PHONY: test-vzdump-guest-included test-vzdump-guest-included: @@ -28,6 +28,10 @@ test-vzdump-guest-included: test-vzdump-new: ./vzdump_new_test.pl +.PHONY: test-vzdump-backup-info +test-vzdump-backup-info: + ./backup_info_test.pl + .PHONY: test-osd test-osd: ./OSD_test.pl diff --git a/test/backup_info_test.pl b/test/backup_info_test.pl new file mode 100644 index 0000000..54aece2 --- /dev/null +++ b/test/backup_info_test.pl @@ -0,0 +1,71 @@ +#!/usr/bin/perl + +use strict; +use warnings; + +use lib '..'; + +use Test::More; +use Test::MockModule; + +use PVE::API2::Cluster::BackupInfo; + +my $vmlist = { + ids => { + 100 => { type => 'qemu', node => 'node1' }, + 101 => { type => 'qemu', node => 'node1' }, + 102 => { type => 'lxc', node => 'node1' }, + }, +}; + +my $pools = { + testpool => [100], +}; + +my $jobs_cfg = { + ids => { + 'backup-pool' => { type => 'vzdump', pool => 'testpool' }, + 'backup-vmid' => { type => 'vzdump', vmid => '101' }, + }, +}; + +my $cluster_module = Test::MockModule->new('PVE::Cluster'); +$cluster_module->mock(get_vmlist => sub { return $vmlist }); + +my $api2tools_module = Test::MockModule->new('PVE::API2Tools'); +$api2tools_module->mock( + get_resource_pool_guest_members => sub { + my ($pool) = @_; + die "pool '$pool' does not exist\n" if !$pools->{$pool}; + return $pools->{$pool}; + }, +); + +my $backup_info_module = Test::MockModule->new('PVE::API2::Cluster::BackupInfo'); +$backup_info_module->mock( + cfs_read_file => sub { + my ($filename) = @_; + return { jobs => [] } if $filename eq 'vzdump.cron'; + return $jobs_cfg if $filename eq 'jobs.cfg'; + die "unexpected file '$filename'\n"; + }, +); + +is_deeply( + PVE::API2::Cluster::BackupInfo::get_included_vmids(), + { 100 => 1, 101 => 1 }, + 'guests of all backup jobs are included', +); + +$jobs_cfg->{ids}->{'backup-deleted-pool'} = { type => 'vzdump', pool => 'deletedpool' }; + +my @warnings; +my $included = eval { + local $SIG{__WARN__} = sub { push @warnings, $_[0] }; + PVE::API2::Cluster::BackupInfo::get_included_vmids(); +}; +is($@, '', 'job referencing a deleted pool does not make the lookup fail'); +is_deeply($included, { 100 => 1, 101 => 1 }, 'guests of the other jobs are still included'); +like($warnings[0] // '', qr/pool 'deletedpool' does not exist/, 'broken job is warned about'); + +done_testing();