From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: from gate001.proxmox.com (gate001.proxmox.com [IPv6:2a0f:8001:1:32::40]) by lore.proxmox.com (Postfix) with ESMTPS id 737981FF0A8 for ; Sat, 03 Oct 2026 14:49:45 +0200 (CEST) Received: from gate001.proxmox.com (localhost.localdomain [127.0.0.1]) by gate001.proxmox.com (Proxmox) with ESMTP id CA06F21700; Sat, 03 Oct 2026 14:49:35 +0200 (CEST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=dualfroz.com; s=dkim; t=1791031768; h=from:subject:date:message-id:to:mime-version: content-transfer-encoding:in-reply-to:references; bh=/M4LX+HeUwzpJPKOpF19Umk2dj2AUES5PWgzLynMf0o=; b=qynQnpx6MHWb2sOF1b4tg1bEVWrpMrJiiNcpMDCxqb9OOSqq4memNz7L4wsj6z1mN6mZrL s/uUSrkXRhskUIPCNd5raTM3plvPC5LuTAKB0/H8Z9TdZPprWNonWtXYlLV8NgmjXAlnOI QNVBfVu8P5p9tzpu+rpGMG94YYE5qFjHLK3ZDRTa2M0kRCdKk5hPEIHCaefj37oXyUtglZ t+SEC5/kHrBQNsml3TLEXcS7Kq4tQr/zemj1nU2/dTKB4IyFx264PXpUhv0wsVADuisw/y Pw/GgbRqUJ5B3oNAW5fIIxuIpKxs6OD2+VyXj9AifAn5MEjNM4iO5KVHM1zOnw== From: Michal Fox To: pve-devel@lists.proxmox.com Subject: [PATCH storage 1/3] test: add tests for zpool status parsing of the ZFS disk API Date: Sat, 3 Oct 2026 12:49:15 +0000 Message-ID: <20261003124917.7-2-me@dualfroz.com> X-Mailer: git-send-email 2.47.3 In-Reply-To: <20261003124917.7-1-me@dualfroz.com> References: <20261003124917.7-1-me@dualfroz.com> MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Last-TLS-Session-Version: TLSv1.3 X-SPAM-LEVEL: Spam detection results: 0 AWL 0.285 Adjusted score from AWL reputation of From: address DKIM_SIGNED 0.1 Message has a DKIM or DK signature, not necessarily valid DKIM_VALID -0.1 Message has at least one valid DKIM or DK signature DKIM_VALID_AU -0.1 Message has a valid DKIM or DK signature from author's domain DKIM_VALID_EF -0.1 Message has a valid DKIM or DK signature from envelope-from domain DMARC_PASS -0.1 DMARC pass policy SPF_HELO_NONE 0.001 SPF: HELO does not publish an SPF Record SPF_PASS -0.001 SPF: sender matches SPF record Message-ID-Hash: QARH72EXZK2RDFM7TDFY2HRMFH2WDRAD X-Message-ID-Hash: QARH72EXZK2RDFM7TDFY2HRMFH2WDRAD X-MailFrom: me@dualfroz.com X-Mailman-Rule-Misses: dmarc-mitigation; no-senders; approved; loop; banned-address; emergency; member-moderation; nonmember-moderation; administrivia; implicit-dest; max-recipients; max-size; news-moderation; no-subject; digests; suspicious-header X-Mailman-Version: 3.3.10 Precedence: list List-Id: Proxmox VE development discussion List-Help: List-Owner: List-Post: List-Subscribe: List-Unsubscribe: The detail call of the ZFS disk API parses the output of 'zpool status' into the vdev tree of a pool. Add a test for it, which mocks the zpool command and checks the result for a healthy pool with log, cache and spare devices, so that fixes for the parser can extend it with their cases. Signed-off-by: Michal Fox --- The test goes through the API handler, which checks that /sbin/zpool exists, so it relies on zfsutils-linux from the Build-Depends. src/test/run_disk_tests.pl | 2 +- src/test/zfs_pool_detail_test.pm | 165 +++++++++++++++++++++++++++++++ 2 files changed, 166 insertions(+), 1 deletion(-) create mode 100644 src/test/zfs_pool_detail_test.pm diff --git a/src/test/run_disk_tests.pl b/src/test/run_disk_tests.pl index 5a6af07..b4fd09b 100755 --- a/src/test/run_disk_tests.pl +++ b/src/test/run_disk_tests.pl @@ -6,7 +6,7 @@ use warnings; use TAP::Harness; my $harness = TAP::Harness->new({ verbosity => -2 }); -my $res = $harness->runtests("disklist_test.pm"); +my $res = $harness->runtests("disklist_test.pm", "zfs_pool_detail_test.pm"); exit -1 if !$res || $res->{failed} || $res->{parse_errors}; diff --git a/src/test/zfs_pool_detail_test.pm b/src/test/zfs_pool_detail_test.pm new file mode 100644 index 0000000..a062656 --- /dev/null +++ b/src/test/zfs_pool_detail_test.pm @@ -0,0 +1,165 @@ +package PVE::API2::Disks::ZFS::TestPoolDetail; + +use strict; +use warnings; + +use lib qw(..); + +use PVE::API2::Disks::ZFS; +use Test::More; +use Test::MockModule; + +my $zpool_status_output; + +my $zfs_api_module = Test::MockModule->new('PVE::API2::Disks::ZFS'); +$zfs_api_module->mock( + run_command => sub { + my ($cmd, %param) = @_; + + my $cmdline = join(' ', @$cmd); + die "unexpected run_command call: '$cmdline'\n" + if $cmdline ne '/sbin/zpool status -P tank'; + + $param{outfunc}->($_) for split(/\n/, $zpool_status_output); + + return 0; + }, +); + +# each test is comprised of the following keys: +# description => what the test is about +# status => output of 'zpool status' for the pool 'tank' +# expected => pool details as returned by the API +my $tests = [ + { + description => 'healthy pool with log, cache and spare', + status => <<"EOF", + pool: tank + state: ONLINE + scan: scrub repaired 0B in 00:00:01 with 0 errors on Sun Sep 13 00:24:02 2026 +config: + +\tNAME STATE READ WRITE CKSUM +\ttank ONLINE 0 0 0 +\t mirror-0 ONLINE 0 0 0 +\t /dev/disk/by-id/ata-DISK_A-part1 ONLINE 0 0 0 +\t /dev/disk/by-id/ata-DISK_B-part1 ONLINE 0 0 0 +\tlogs +\t /dev/disk/by-id/nvme-LOG-part1 ONLINE 0 0 0 +\tcache +\t /dev/disk/by-id/nvme-CACHE-part1 ONLINE 0 0 0 +\tspares +\t /dev/disk/by-id/ata-DISK_C-part1 AVAIL + +errors: No known data errors +EOF + expected => { + name => 'tank', + state => 'ONLINE', + scan => 'scrub repaired 0B in 00:00:01 with 0 errors on Sun Sep 13 00:24:02 2026', + errors => 'No known data errors', + leaf => 0, + children => [ + { + name => 'tank', + state => 'ONLINE', + read => 0, + write => 0, + cksum => 0, + msg => '', + leaf => 0, + children => [ + { + name => 'mirror-0', + state => 'ONLINE', + read => 0, + write => 0, + cksum => 0, + msg => '', + leaf => 0, + children => [ + { + name => '/dev/disk/by-id/ata-DISK_A-part1', + state => 'ONLINE', + read => 0, + write => 0, + cksum => 0, + msg => '', + leaf => 1, + }, + { + name => '/dev/disk/by-id/ata-DISK_B-part1', + state => 'ONLINE', + read => 0, + write => 0, + cksum => 0, + msg => '', + leaf => 1, + }, + ], + }, + ], + }, + { + name => 'logs', + msg => '', + leaf => 0, + children => [ + { + name => '/dev/disk/by-id/nvme-LOG-part1', + state => 'ONLINE', + read => 0, + write => 0, + cksum => 0, + msg => '', + leaf => 1, + }, + ], + }, + { + name => 'cache', + msg => '', + leaf => 0, + children => [ + { + name => '/dev/disk/by-id/nvme-CACHE-part1', + state => 'ONLINE', + read => 0, + write => 0, + cksum => 0, + msg => '', + leaf => 1, + }, + ], + }, + { + name => 'spares', + msg => '', + leaf => 0, + children => [ + { + name => '/dev/disk/by-id/ata-DISK_C-part1', + state => 'AVAIL', + msg => '', + leaf => 1, + }, + ], + }, + ], + }, + }, +]; + +plan tests => scalar @$tests; + +for my $test (@$tests) { + $zpool_status_output = $test->{status}; + + my $pool = eval { PVE::API2::Disks::ZFS->detail({ node => 'localhost', name => 'tank' }) }; + diag("unexpected error: $@") if $@; + is_deeply($pool, $test->{expected}, $test->{description}); +} + +done_testing(); + +1; -- 2.43.0