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 04/13] multipath: add storage plugin for multipath LUNs
Date: Fri, 26 Jun 2026 14:07:34 +0200	[thread overview]
Message-ID: <20260626121000.2095591-5-t.lamprecht@proxmox.com> (raw)
In-Reply-To: <20260626121000.2095591-1-t.lamprecht@proxmox.com>

Expose the dm-multipath maps present on a node as a first-class storage
of raw volumes, each keyed by its WWID and reachable at the WWID-stable
path /dev/disk/by-id/dm-uuid-mpath-<wwid>. The maps are assembled by
multipathd from the cluster-wide allow-list, so the storage carries no
configuration of its own and only reflects what is already there.

This is meant primarily as the base of a shared LVM storage, giving a
volume group on a multipath LUN full path redundancy without any manual
device wiring.

Signed-off-by: Thomas Lamprecht <t.lamprecht@proxmox.com>
---
 src/PVE/Storage.pm                 |   2 +
 src/PVE/Storage/Makefile           |   3 +-
 src/PVE/Storage/MultipathPlugin.pm | 186 +++++++++++++++++++++++++++++
 3 files changed, 190 insertions(+), 1 deletion(-)
 create mode 100644 src/PVE/Storage/MultipathPlugin.pm

diff --git a/src/PVE/Storage.pm b/src/PVE/Storage.pm
index 64ea9da..4daeecf 100755
--- a/src/PVE/Storage.pm
+++ b/src/PVE/Storage.pm
@@ -39,6 +39,7 @@ use PVE::Storage::ZFSPlugin;
 use PVE::Storage::PBSPlugin;
 use PVE::Storage::BTRFSPlugin;
 use PVE::Storage::ESXiPlugin;
+use PVE::Storage::MultipathPlugin;
 
 # Storage API version. Increment it on changes in storage API interface.
 use constant APIVER => 15;
@@ -64,6 +65,7 @@ PVE::Storage::ZFSPlugin->register();
 PVE::Storage::PBSPlugin->register();
 PVE::Storage::BTRFSPlugin->register();
 PVE::Storage::ESXiPlugin->register();
+PVE::Storage::MultipathPlugin->register();
 
 # load third-party plugins
 if (-d '/usr/share/perl5/PVE/Storage/Custom') {
diff --git a/src/PVE/Storage/Makefile b/src/PVE/Storage/Makefile
index a67dc25..d76ec25 100644
--- a/src/PVE/Storage/Makefile
+++ b/src/PVE/Storage/Makefile
@@ -14,7 +14,8 @@ SOURCES= \
 	PBSPlugin.pm \
 	BTRFSPlugin.pm \
 	LvmThinPlugin.pm \
-	ESXiPlugin.pm
+	ESXiPlugin.pm \
+	MultipathPlugin.pm
 
 .PHONY: install
 install:
diff --git a/src/PVE/Storage/MultipathPlugin.pm b/src/PVE/Storage/MultipathPlugin.pm
new file mode 100644
index 0000000..91fc489
--- /dev/null
+++ b/src/PVE/Storage/MultipathPlugin.pm
@@ -0,0 +1,186 @@
+package PVE::Storage::MultipathPlugin;
+
+use strict;
+use warnings;
+
+use PVE::Multipath;
+use PVE::Storage::Plugin;
+
+use base qw(PVE::Storage::Plugin);
+
+# A block-LUN provider over device-mapper multipath: it exposes the maps present on the node as raw
+# volumes keyed by their WWID, reachable at the WWID-stable /dev/disk/by-id/dm-uuid-mpath-<wwid>
+# path. The maps are assembled by multipathd from the cluster-wide allow-list, so this storage has
+# no configuration of its own and only reflects what is there. Its main use is as the 'base' of a
+# shared LVM storage.
+
+sub type {
+    return 'multipath';
+}
+
+sub plugindata {
+    return {
+        content => [{ images => 1, none => 1 }, { images => 1 }],
+        format => [{ raw => 1 }, 'raw'],
+        select_existing => 1,
+        'sensitive-properties' => {},
+    };
+}
+
+sub properties {
+    return {};
+}
+
+sub options {
+    return {
+        nodes => { optional => 1 },
+        shared => { optional => 1 },
+        disable => { optional => 1 },
+        content => { optional => 1 },
+        bwlimit => { optional => 1 },
+    };
+}
+
+# the volume name is the LUN's WWID
+sub parse_volname {
+    my ($class, $volname) = @_;
+
+    if ($volname =~ m!^([a-zA-Z0-9._:-]+)$!) {
+        return ('images', $1, undef, undef, undef, undef, 'raw');
+    }
+
+    die "unable to parse multipath volume name '$volname'\n";
+}
+
+sub filesystem_path {
+    my ($class, $scfg, $volname, $snapname) = @_;
+
+    die "snapshot is not possible on multipath storage\n" if defined($snapname);
+
+    my ($vtype, $wwid, $vmid) = $class->parse_volname($volname);
+
+    my $path = "/dev/disk/by-id/dm-uuid-mpath-$wwid";
+
+    return wantarray ? ($path, $vmid, $vtype) : $path;
+}
+
+sub create_base {
+    die "can't create base images in multipath storage\n";
+}
+
+sub clone_image {
+    die "can't clone images in multipath storage\n";
+}
+
+sub alloc_image {
+    die "can't allocate space in multipath storage\n";
+}
+
+sub free_image {
+    die "can't free space in multipath storage\n";
+}
+
+sub list_images {
+    my ($class, $storeid, $scfg, $vmid, $vollist, $cache) = @_;
+
+    my $res = [];
+
+    # multipath LUNs have no owning guest
+    return $res if defined($vmid) && !$vollist;
+
+    $cache->{multipath_maps} //= eval { PVE::Multipath::get_maps() } // [];
+
+    for my $map ($cache->{multipath_maps}->@*) {
+        next if !defined($map->{wwid});
+
+        my $volid = "$storeid:$map->{wwid}";
+        if ($vollist) {
+            next if !grep { $_ eq $volid } @$vollist;
+        }
+
+        push @$res,
+            {
+                volid => $volid,
+                format => 'raw',
+                size => $map->{size} // 0,
+                vmid => 0,
+            };
+    }
+
+    return $res;
+}
+
+sub list_volumes {
+    my ($class, $storeid, $scfg, $vmid, $content_types) = @_;
+
+    my $res = $class->list_images($storeid, $scfg, $vmid);
+    $_->{content} = 'images' for $res->@*;
+
+    return $res;
+}
+
+sub status {
+    my ($class, $storeid, $scfg, $cache) = @_;
+
+    my $active = PVE::Multipath::is_running() ? 1 : 0;
+
+    return (0, 0, 0, $active);
+}
+
+sub activate_storage {
+    my ($class, $storeid, $scfg, $cache) = @_;
+
+    PVE::Multipath::assert_supported();
+    # nothing to log into: multipathd assembles the maps from the allow-list
+}
+
+sub deactivate_storage {
+    my ($class, $storeid, $scfg, $cache) = @_;
+
+    # the maps are shared infrastructure, do not tear them down
+}
+
+sub check_connection {
+    my ($class, $storeid, $scfg) = @_;
+
+    return PVE::Multipath::is_running() ? 1 : 0;
+}
+
+sub activate_volume {
+    my ($class, $storeid, $scfg, $volname, $snapname, $cache) = @_;
+
+    my ($vtype, $wwid) = $class->parse_volname($volname);
+
+    my $map = PVE::Multipath::wait_for_map($wwid);
+    die "multipath map for WWID '$wwid' is not present - is it in the multipath"
+        . " allow-list and are its paths up?\n"
+        if !$map;
+}
+
+sub deactivate_volume {
+    my ($class, $storeid, $scfg, $volname, $snapname, $cache) = @_;
+
+    # do not drop the map, it is shared infrastructure
+}
+
+sub volume_resize {
+    my ($class, $scfg, $storeid, $volname, $size, $running) = @_;
+
+    die "volume resize is not possible on multipath storage\n";
+}
+
+sub volume_has_feature {
+    my ($class, $scfg, $feature, $storeid, $volname, $snapname, $running) = @_;
+
+    return { copy => { current => 1 } }->{$feature}->{current};
+}
+
+sub volume_export_formats {
+    return ();
+}
+
+sub volume_import_formats {
+    return ();
+}
+
+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 ` [PATCH storage 02/13] api: disks: add read-only multipath status endpoint Thomas Lamprecht
2026-06-26 12:07 ` [PATCH storage 03/13] api: multipath: add cluster-wide configuration endpoints Thomas Lamprecht
2026-06-26 12:07 ` Thomas Lamprecht [this message]
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-5-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