How to get disk usage of mp0, mp1, etc (not just rootfs)?

I'd like to find a way that a token with PVEAudit on / can get an aggregate report of used vs available disk usage across a variety of mount points (mp0, mp1, etc), similar to how its done for rootfs.

pct gives us this directly on the Proxmox VE node:

Code:
pct df 1104037                                                                        |
MP     Volume                                    Size   Used Avail Use% Path
rootfs slc1-tank1-acmeinc:subvol-1142037-disk-0  8.0G 181.6M  7.8G  2.2 /
mp0    slc1-tank1-acmeinc:subvol-1142037-disk-1 50.1G  19.2M 50.1G  0.0 /mnt/storage

This is nice because it seems to provide the correct abstraction for the various storage backends:
- lv
- zfs
- ceph
- smb

But I don't see that exposed via API.

I'm thinking of using an ssh authorized_keys wrapper script that allows pct df and some other tooling but, if there's a better way to do this with available APIs, I'd prefer to do that.
 
I use Codex 5.4 to author this script according to my scripting rules and styles and corrections.

It's doing some things in a non-optimal way from how I'd have done them by hand, but it's good enough for now to get me testing the use case.

Code:
This is a restricted command wrapper.

Supported commands:
  cat /proc/pressure/cpu [/proc/pressure/memory] [/proc/pressure/io]
  pct df <vmid>
  ssh <host> <supported-command...>
  smartctl --scan-open
  smartctl -i -H -j <device>
  zfs get -Hp -o value used <dataset>
  zpool status -p
  pvecm status

  1. install manually on Proxmox nodes as /root/bin/pmx-pve-audit
  2. reference it from ~/.ssh/authorized_keys via a forced-command entry: no-agent-forwarding,no-port-forwarding,no-pty,no-user-rc,no-X11-forwarding,command="/root/bin/pmx-pve-audit" ssh-ed25519 <key> [comment]
  3. run it like:
    ssh root@pve1 pmx-pve-audit pct df
    ssh root@pve1 pct df
    ssh root@pve1 ssh root@pve2 pct df

Bash:
#!/bin/sh
#
# pmx-pve-audit
#
# Restricted read-only command wrapper for Proxmox-node audit access.
#
# Intended deployment:
# - install manually on Proxmox nodes as `/root/bin/pmx-pve-audit`
# - reference it from `~/.ssh/authorized_keys` via a forced-command entry
#     no-agent-forwarding,no-port-forwarding,no-pty,no-user-rc,no-X11-forwarding,command="/root/bin/pmx-pve-audit" ssh-ed25519 <key> [comment]
# - run it either as:
#     pmx-pve-audit <command> [args]
#   or, over SSH forced-command, as:
#     <command> [args]
#
# This wrapper intentionally accepts only a narrow allowlist of exact command
# forms. Unsupported commands or unsupported argument shapes fail closed and
# print the supported forms.

set -eu
set -f

PATH='/usr/sbin:/usr/bin:/sbin:/bin'
g_validate_only=0

fn_usage() {
        cat <<'EOF'
This is a restricted command wrapper.

Supported commands:
  cat /proc/pressure/cpu [/proc/pressure/memory] [/proc/pressure/io]
  pct df <vmid>
  ssh <host> <supported-command...>
  smartctl --scan-open
  smartctl -i -H -j <device>
  zfs get -Hp -o value used <dataset>
  zpool status -p
  pvecm status
EOF
}

fn_version() {
        printf 'pmx-pve-audit v1\n'
        exit 0
}

fn_is_pressure_path() {
        case "$1" in
        /proc/pressure/cpu | /proc/pressure/memory | /proc/pressure/io)
                return 0
                ;;
        *)
                return 1
                ;;
        esac
}

fn_validate_vmid() {
        printf '%s\n' "$1" | grep -Eq '^[0-9]+$'
}

fn_validate_device() {
        case "$1" in
        /dev/*)
                ;;
        *)
                return 1
                ;;
        esac
        case "$1" in
        *' '* | *'..'* | *'//'*)
                return 1
                ;;
        esac
        printf '%s\n' "$1" | grep -Eq '^[A-Za-z0-9_./:-]+$'
}

fn_validate_dataset() {
        case "$1" in
        '' | /* | *' '* | *'..'* | *'//'*)
                return 1
                ;;
        esac
        case "$1" in
        */*)
                ;;
        *)
                return 1
                ;;
        esac
        printf '%s\n' "$1" | grep -Eq '^[A-Za-z0-9_./:-]+$'
}

fn_validate_host() {
        case "$1" in
        '' | -* | *'@'* | *' '* | *'..'* | *'//'*)
                return 1
                ;;
        esac
        printf '%s\n' "$1" | grep -Eq '^[A-Za-z0-9.-]+$'
}

fn_require_cluster_host() {
        if ! command -v pvesh >/dev/null 2>&1; then
                return 1
        fi

        if pvesh get /nodes --output-format json 2>/dev/null |
                grep -o '"node":"[^"]*"' |
                sed 's/"node":"//; s/"$//' |
                grep -Fx "$1" >/dev/null 2>&1; then
                return 0
        fi
        return 1
}

fn_shell_quote() {
        printf "'%s'" "$(printf '%s' "$1" | sed "s/'/'\"'\"'/g")"
}

fn_build_remote_command() {
        out=""
        for b_arg in "$@"; do
                if test -z "$out"; then
                        out=$(fn_shell_quote "$b_arg")
                else
                        out="$out $(fn_shell_quote "$b_arg")"
                fi
        done
        printf '%s\n' "$out"
}

fn_finish_validate() {
        if test "$g_validate_only" = "1"; then
                return 0
        fi
        return 1
}

# AGENT: Keep this function, fn_is_pressure_path, and the exact "cat ..." line
# in fn_usage in sync. If the allowed pressure-file list changes, update all
# three places together so agents get exact feedback from the wrapper.
fn_cat() {
        if test $# -lt 1; then
                fn_usage >&2
                exit 1
        fi
        for b_arg in "$@"; do
                if ! fn_is_pressure_path "$b_arg"; then
                        printf 'unsupported cat path: %s\n' "$b_arg" >&2
                        fn_usage >&2
                        exit 1
                fi
        done
        if fn_finish_validate; then
                return 0
        fi
        exec cat "$@"
}

fn_pct() {
        if test $# -ne 2; then
                fn_usage >&2
                exit 1
        fi
        if test "$1" != "df"; then
                printf 'unsupported pct subcommand: %s\n' "$1" >&2
                fn_usage >&2
                exit 1
        fi
        if ! fn_validate_vmid "$2"; then
                printf 'invalid vmid: %s\n' "$2" >&2
                fn_usage >&2
                exit 1
        fi
        if fn_finish_validate; then
                return 0
        fi
        exec pct df "$2"
}

fn_smartctl() {
        case "${1:-}" in
        --scan-open)
                if test $# -ne 1; then
                        fn_usage >&2
                        exit 1
                fi
                exec smartctl --scan-open
                ;;
        -i)
                if test $# -ne 4; then
                        fn_usage >&2
                        exit 1
                fi
                if test "$2" != "-H"; then
                        fn_usage >&2
                        exit 1
                fi
                if test "$3" != "-j"; then
                        fn_usage >&2
                        exit 1
                fi
                if ! fn_validate_device "$4"; then
                        printf 'invalid device path: %s\n' "$4" >&2
                        fn_usage >&2
                        exit 1
                fi
                if fn_finish_validate; then
                        return 0
                fi
                exec smartctl -i -H -j "$4"
                ;;
        *)
                printf 'unsupported smartctl invocation\n' >&2
                fn_usage >&2
                exit 1
                ;;
        esac
}

fn_zfs() {
        if test $# -ne 6; then
                fn_usage >&2
                exit 1
        fi
        if test "$1" != "get"; then
                fn_usage >&2
                exit 1
        fi
        if test "$2" != "-Hp"; then
                fn_usage >&2
                exit 1
        fi
        if test "$3" != "-o"; then
                fn_usage >&2
                exit 1
        fi
        if test "$4" != "value"; then
                fn_usage >&2
                exit 1
        fi
        if test "$5" != "used"; then
                fn_usage >&2
                exit 1
        fi
        if ! fn_validate_dataset "$6"; then
                printf 'invalid dataset: %s\n' "$6" >&2
                fn_usage >&2
                exit 1
        fi
        if fn_finish_validate; then
                return 0
        fi
        exec /sbin/zfs get -Hp -o value used "$6"
}

fn_zpool() {
        if test $# -ne 2; then
                fn_usage >&2
                exit 1
        fi
        if test "$1" != "status"; then
                fn_usage >&2
                exit 1
        fi
        if test "$2" != "-p"; then
                fn_usage >&2
                exit 1
        fi
        if fn_finish_validate; then
                return 0
        fi
        exec /sbin/zpool status -p
}

fn_pvecm() {
        if test $# -ne 1; then
                fn_usage >&2
                exit 1
        fi
        if test "$1" != "status"; then
                fn_usage >&2
                exit 1
        fi
        if fn_finish_validate; then
                return 0
        fi
        exec pvecm status
}

fn_dispatch() {
        case "${1:-}" in
        cat)
                shift
                fn_cat "$@"
                ;;
        pct)
                shift
                fn_pct "$@"
                ;;
        smartctl)
                shift
                fn_smartctl "$@"
                ;;
        zfs)
                shift
                fn_zfs "$@"
                ;;
        zpool)
                shift
                fn_zpool "$@"
                ;;
        pvecm)
                shift
                fn_pvecm "$@"
                ;;
        help | -help | --help)
                if test "$g_validate_only" = "1"; then
                        return 0
                fi
                fn_usage
                exit 0
                ;;
        version | -V | -version | --version)
                if test "$g_validate_only" = "1"; then
                        return 0
                fi
                fn_version
                ;;
        *)
                printf 'unsupported command: %s\n' "${1:-}" >&2
                fn_usage >&2
                exit 1
                ;;
        esac
}

fn_execute_args() {
        remote_host=""

        if test "${1:-}" = "pmx-pve-audit"; then
                shift
        fi

        if test $# -eq 0; then
                fn_usage
                exit 0
        fi

        if test "${1:-}" = "ssh"; then
                shift
                if test $# -lt 2; then
                        fn_usage >&2
                        exit 1
                fi
                remote_host=$1
                shift

                if ! fn_validate_host "$remote_host"; then
                        printf 'invalid ssh host: %s\n' "$remote_host" >&2
                        fn_usage >&2
                        exit 1
                fi
                if ! fn_require_cluster_host "$remote_host"; then
                        printf 'unknown cluster host: %s\n' "$remote_host" >&2
                        fn_usage >&2
                        exit 1
                fi
        fi

        b_prev_validate_only=$g_validate_only
        g_validate_only=1
        fn_dispatch "$@"
        g_validate_only=$b_prev_validate_only

        if test -n "$remote_host"; then
                remote_command=$(fn_build_remote_command "$@")
                exec ssh \
                        -o BatchMode=yes \
                        "$remote_host" \
                        "$remote_command"
        fi

        fn_dispatch "$@"
}

if test $# -gt 0; then
        fn_execute_args "$@"
fi

b_original=${SSH_ORIGINAL_COMMAND:-}
if test -z "$b_original"; then
        fn_usage >&2
        exit 1
fi

# shellcheck disable=SC2086
set -- $b_original
fn_execute_args "$@"
 
Last edited: