Skip to content

zpool provider get_pool_data fails to parse zpool status output on ZFS 2.3.x #94

@Nourdeo

Description

@Nourdeo

Describe the Bug

Hi,
After upgrading ZFS to 2.3.2-2bpo12+2, the zpool provider’s get_pool_data implementation in puppetlabs-zfs_core no longer parses the zpool status output correctly. This breaks ZFS-related resources that rely on get_pool_data ( zpool disk list), while the same code works fine with ZFS 2.2.7-1~bpo12+1

I’ve implemented a small parsing change that works with both ZFS 2.2.7-1bpo12+1 and 2.3.2-2bpo12+2 and would be happy to open a PR if this approach looks acceptable.


Environment

  • OS: Debian 12 (bookworm)
  • Puppet: 7.x
  • Module: puppetlabs-zfs_core (version: 1.6.1)
  • ZFS:
    • Working: 2.2.7-1~bpo12+1
    • Broken: 2.3.2-2~bpo12+2

Problem description

With ZFS 2.3.2-2bpo12+2, the format of zpool status output changed slightly. The current get_pool_data implementation in the zpool provider assumes tab-indented lines and uses this logic:

out = execute("zpool status #{zpool_opts} #{@resource[:pool]}", failonfail: false, combine: false)
zpool_data = out.lines.select { |line| line.index("\t") == 0 }.map { |l| l.strip.split("\s")[0] }
zpool_data.shift
zpool_data

On ZFS 2.3.2-2bpo12+2, the status table uses spaces and a NAME STATE READ WRITE CKSUM header.
On ZFS 2.2.7-1bpo12+1, the same code works correctly with the older zpool status formatting.

Analysis

The core issue is the parsing strategy inside get_pool_data:

It assumes that each row of interest starts with a tab and uses line.index("\t") == 0 to filter lines.

With ZFS 2.3.2-2bpo12+2, the output is still a table but the indentation and formatting changed enough that this condition no longer matches.

Proposed fix

I changed get_pool_data to:

Find the table header line matching NAME STATE READ WRITE CKSUM.

From there, collect only the indented lines (pool and device rows).

Take just the first column as the device/pool name.

def get_pool_data
  # https://docs.oracle.com/cd/E19082-01/817-2271/gbcve/index.html
  # we could also use zpool iostat -v mypool for a (little bit) cleaner output
  zpool_opts = case Facter.value(:kernel)
               # use full device names ("-P") on Linux/ZOL to prevent
               # mismatches between creation and display paths:
               when 'Linux'
                 '-P'
               else
                 ''
               end
  out = execute("zpool status #{zpool_opts} #{@resource[:pool]}", failonfail: false, combine: false)

  lines = out.lines
  # Skip until we reach the NAME/STATE/READ/WRITE/CKSUM header
  lines = lines.drop_while { |l| !(l =~ /^\s*NAME\s+STATE\s+READ\s+WRITE\s+CKSUM/) }

  # Now collect only the indented table rows (NAME, pool name, devices)
  zpool_data = lines
                 .select { |l| l =~ /^\s+/ }              # only indented lines
                 .map    { |l| l.strip.split(/\s+/)[0] }  # first column only

  # First entry is "NAME", remove it so first element is the pool name
  zpool_data.shift
  zpool_data
end

##Backwards compatibility

I’ve tested this against:

ZFS 2.2.7-1bpo12+1: returns the same device list as before (no change in behaviour).

ZFS 22.3.2-2bpo12+2: correctly returns the pool and device list where the previous code returned an empty/incorrect array.

The logic is based purely on the NAME STATE READ WRITE CKSUM header and indentation, which appears stable across both versions.

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't working

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions