all lists on lists.proxmox.com
 help / color / mirror / Atom feed
From: Thomas Lamprecht <t.lamprecht@proxmox.com>
To: pve-devel@lists.proxmox.com
Subject: [PATCH storage 02/13] api: disks: add read-only multipath status endpoint
Date: Fri, 26 Jun 2026 14:07:32 +0200	[thread overview]
Message-ID: <20260626121000.2095591-3-t.lamprecht@proxmox.com> (raw)
In-Reply-To: <20260626121000.2095591-1-t.lamprecht@proxmox.com>

Expose multipath map state under /nodes/{node}/disks/multipath so the
web UI and operators can see map health, per-path transport and which
LVM volume group consumes a map, rather than parsing 'multipath -ll' by
hand.

Signed-off-by: Thomas Lamprecht <t.lamprecht@proxmox.com>
---
 src/PVE/API2/Disks.pm           |   7 ++
 src/PVE/API2/Disks/Makefile     |   1 +
 src/PVE/API2/Disks/Multipath.pm | 206 ++++++++++++++++++++++++++++++++
 3 files changed, 214 insertions(+)
 create mode 100644 src/PVE/API2/Disks/Multipath.pm

diff --git a/src/PVE/API2/Disks.pm b/src/PVE/API2/Disks.pm
index e707a9e..984d890 100644
--- a/src/PVE/API2/Disks.pm
+++ b/src/PVE/API2/Disks.pm
@@ -14,6 +14,7 @@ use PVE::Tools qw(run_command);
 use PVE::API2::Disks::Directory;
 use PVE::API2::Disks::LVM;
 use PVE::API2::Disks::LVMThin;
+use PVE::API2::Disks::Multipath;
 use PVE::API2::Disks::ZFS;
 
 use PVE::RESTHandler;
@@ -34,6 +35,11 @@ __PACKAGE__->register_method({
     path => 'directory',
 });
 
+__PACKAGE__->register_method({
+    subclass => "PVE::API2::Disks::Multipath",
+    path => 'multipath',
+});
+
 __PACKAGE__->register_method({
     subclass => "PVE::API2::Disks::ZFS",
     path => 'zfs',
@@ -70,6 +76,7 @@ __PACKAGE__->register_method({
             { name => 'lvm' },
             { name => 'lvmthin' },
             { name => 'directory' },
+            { name => 'multipath' },
             { name => 'wipedisk' },
             { name => 'zfs' },
         ];
diff --git a/src/PVE/API2/Disks/Makefile b/src/PVE/API2/Disks/Makefile
index 9152aed..f6f8449 100644
--- a/src/PVE/API2/Disks/Makefile
+++ b/src/PVE/API2/Disks/Makefile
@@ -1,6 +1,7 @@
 
 SOURCES= LVM.pm\
 	 LVMThin.pm\
+	 Multipath.pm\
 	 ZFS.pm\
 	 Directory.pm
 
diff --git a/src/PVE/API2/Disks/Multipath.pm b/src/PVE/API2/Disks/Multipath.pm
new file mode 100644
index 0000000..5cf2d17
--- /dev/null
+++ b/src/PVE/API2/Disks/Multipath.pm
@@ -0,0 +1,206 @@
+package PVE::API2::Disks::Multipath;
+
+use strict;
+use warnings;
+
+use PVE::JSONSchema qw(get_standard_option);
+use PVE::Multipath;
+use PVE::Storage::LVMPlugin;
+
+use PVE::RESTHandler;
+
+use base qw(PVE::RESTHandler);
+
+my $path_groups_schema = {
+    type => 'array',
+    description => 'The path groups of the map, in priority order.',
+    items => {
+        type => 'object',
+        additionalProperties => 1,
+        properties => {
+            group => { type => 'integer' },
+            'dm-state' => { type => 'string', optional => 1 },
+            priority => { type => 'integer' },
+            paths => {
+                type => 'array',
+                items => {
+                    type => 'object',
+                    additionalProperties => 1,
+                    properties => {
+                        dev => {
+                            type => 'string',
+                            description => 'The underlying block device of the path.',
+                        },
+                        'dm-state' => {
+                            type => 'string',
+                            description => "Path state as seen by device-mapper "
+                                . "('active' or 'failed').",
+                            optional => 1,
+                        },
+                        'dev-state' => {
+                            type => 'string',
+                            description => "Path state as seen by the kernel block layer.",
+                            optional => 1,
+                        },
+                        'check-state' => {
+                            type => 'string',
+                            description => 'Result of the path checker.',
+                            optional => 1,
+                        },
+                        priority => { type => 'integer', optional => 1 },
+                        transport => {
+                            type => 'string',
+                            description => 'Transport of this path (iscsi, fc, sas).',
+                            optional => 1,
+                        },
+                    },
+                },
+            },
+        },
+    },
+};
+
+# Annotates each map with the LVM volume group sitting on it, if any; the map's PV shows up under
+# its /dev/mapper/<name> path in the VG -> PV listing.
+my sub annotate_lvm_usage {
+    my ($maps) = @_;
+
+    return if !scalar(@$maps);
+
+    my $pv_to_vg = {};
+    eval {
+        my $vgs = PVE::Storage::LVMPlugin::lvm_vgs(1);
+        for my $vgname (keys %$vgs) {
+            for my $pv ($vgs->{$vgname}->{pvs}->@*) {
+                $pv_to_vg->{ $pv->{name} } = $vgname;
+            }
+        }
+    };
+    warn $@ if $@;
+
+    for my $map (@$maps) {
+        next if !defined($map->{name});
+        my $vg = $pv_to_vg->{"/dev/mapper/$map->{name}"};
+        $map->{'used-by'} = "LVM VG '$vg'" if defined($vg);
+    }
+}
+
+__PACKAGE__->register_method({
+    name => 'index',
+    path => '',
+    method => 'GET',
+    proxyto => 'node',
+    protected => 1,
+    permissions => {
+        check => ['perm', '/', ['Sys.Audit']],
+    },
+    description => "List and report the health of device-mapper multipath maps.",
+    parameters => {
+        additionalProperties => 0,
+        properties => {
+            node => get_standard_option('pve-node'),
+        },
+    },
+    returns => {
+        type => 'object',
+        properties => {
+            supported => {
+                type => 'boolean',
+                description => "Whether multipath-tools is installed on the node.",
+            },
+            running => {
+                type => 'boolean',
+                description => "Whether the multipathd daemon is reachable.",
+            },
+            maps => {
+                type => 'array',
+                items => {
+                    type => 'object',
+                    additionalProperties => 1,
+                    properties => {
+                        wwid => {
+                            type => 'string',
+                            description => 'The WWID, the stable identity of the LUN.',
+                            optional => 1,
+                        },
+                        name => {
+                            type => 'string',
+                            description => 'The (node-local) multipath map name.',
+                            optional => 1,
+                        },
+                        path => {
+                            type => 'string',
+                            description => 'Stable WWID-based device path of the map.',
+                            optional => 1,
+                        },
+                        sysfs => {
+                            type => 'string',
+                            description => "The 'dm-N' kernel device name.",
+                            optional => 1,
+                        },
+                        size => {
+                            type => 'integer',
+                            description => 'Size of the map in bytes.',
+                            optional => 1,
+                        },
+                        health => {
+                            type => 'string',
+                            description => "Aggregated map health: 'optimal' (all paths "
+                                . "active), 'degraded' (some paths failed) or 'failed' "
+                                . "(no active path).",
+                            enum => ['optimal', 'degraded', 'failed'],
+                        },
+                        'dm-state' => { type => 'string', optional => 1 },
+                        'paths-total' => {
+                            type => 'integer',
+                            description => 'Total number of paths.',
+                        },
+                        'paths-active' => {
+                            type => 'integer',
+                            description => 'Number of currently active paths.',
+                        },
+                        transport => {
+                            type => 'string',
+                            description =>
+                                "Transport shared by all of the map's paths, if uniform.",
+                            optional => 1,
+                        },
+                        used => {
+                            type => 'boolean',
+                            description => 'Whether something sits on the map, such as an LVM'
+                                . ' physical volume.',
+                            optional => 1,
+                        },
+                        'used-by' => {
+                            type => 'string',
+                            description => 'What consumes the map, if known, such as an LVM'
+                                . ' volume group.',
+                            optional => 1,
+                        },
+                        'path-groups' => $path_groups_schema,
+                    },
+                },
+            },
+        },
+    },
+    code => sub {
+        my ($param) = @_;
+
+        my $supported = PVE::Multipath::is_supported();
+        my $running = $supported ? PVE::Multipath::is_running() : 0;
+
+        my $maps = [];
+        if ($running) {
+            $maps = PVE::Multipath::get_maps();
+            annotate_lvm_usage($maps);
+        }
+
+        return {
+            supported => $supported ? 1 : 0,
+            running => $running ? 1 : 0,
+            maps => $maps,
+        };
+    },
+});
+
+1;
-- 
2.47.3





  parent reply	other threads:[~2026-06-26 12:11 UTC|newest]

Thread overview: 16+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-06-26 12:07 [PATCH storage,cluster,manager 0/13] multipath: cluster-wide config, storage and health overview Thomas Lamprecht
2026-06-26 12:07 ` [PATCH storage 01/13] multipath: add helper library and managed configuration Thomas Lamprecht
2026-06-26 14:43   ` Maximiliano Sandoval
2026-06-26 12:07 ` Thomas Lamprecht [this message]
2026-06-26 12:07 ` [PATCH storage 03/13] api: multipath: add cluster-wide configuration endpoints Thomas Lamprecht
2026-06-26 12:07 ` [PATCH storage 04/13] multipath: add storage plugin for multipath LUNs Thomas Lamprecht
2026-06-26 12:07 ` [PATCH storage 05/13] lvm: allow a multipath storage as the base device Thomas Lamprecht
2026-06-26 12:07 ` [PATCH storage 06/13] multipath: broadcast per-node map health to the cluster KV store Thomas Lamprecht
2026-06-26 12:07 ` [PATCH storage 07/13] api: multipath: add cluster-wide health status endpoint Thomas Lamprecht
2026-06-26 12:07 ` [PATCH cluster 08/13] pmxcfs: track cluster-wide multipath configuration Thomas Lamprecht
2026-06-26 12:07 ` [PATCH manager 09/13] pvestatd: apply the cluster-wide multipath config on each node Thomas Lamprecht
2026-06-26 12:07 ` [PATCH manager 10/13] api: cluster: mount the multipath configuration endpoint Thomas Lamprecht
2026-06-26 12:07 ` [PATCH manager 11/13] pvestatd: broadcast multipath map health to the cluster Thomas Lamprecht
2026-06-26 12:07 ` [PATCH manager 12/13] ui: dc: add multipath health matrix and config editor Thomas Lamprecht
2026-06-26 14:05   ` Maximiliano Sandoval
2026-06-26 12:07 ` [PATCH manager 13/13] ui: node: show multipath maps and their paths under Disks Thomas Lamprecht

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20260626121000.2095591-3-t.lamprecht@proxmox.com \
    --to=t.lamprecht@proxmox.com \
    --cc=pve-devel@lists.proxmox.com \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.
Service provided by Proxmox Server Solutions GmbH | Privacy | Legal