all lists on lists.proxmox.com
 help / color / mirror / Atom feed
From: Dietmar Maurer <dietmar@proxmox.com>
To: pve-devel@lists.proxmox.com
Subject: [RFC pve-storage 02/27] diskmanage: add helper to list multipath devices
Date: Fri, 31 Jul 2026 12:21:31 +0200	[thread overview]
Message-ID: <20260731102156.3947857-3-dietmar@proxmox.com> (raw)
In-Reply-To: <20260731102156.3947857-1-dietmar@proxmox.com>

Multipath mapped devices are deliberately not part of get_disks, whose
callers (node disk list, local storage creation, ceph OSDs) manage
local disks only. The storage wizard however needs to offer SAN LUNs,
which on production FC/SAS setups are usually multipath devices.

Aggregate vendor, model and transport from the member paths and reuse
the usage detection of get_disks, so callers can tell whether a LUN is
unused, already an LVM physical volume or otherwise occupied.

Signed-off-by: Dietmar Maurer <dietmar@proxmox.com>
---
 src/PVE/Diskmanage.pm                         | 79 ++++++++++++++++++-
 src/test/disk_tests/multipath/disklist        |  9 +++
 .../multipath/disklist_expected.json          | 74 +++++++++++++++++
 src/test/disk_tests/multipath/dm-0/dm/name    |  1 +
 src/test/disk_tests/multipath/dm-0/dm/uuid    |  1 +
 src/test/disk_tests/multipath/dm-0/size       |  1 +
 src/test/disk_tests/multipath/dm-0_slaves     |  4 +
 src/test/disk_tests/multipath/dm-1/dm/name    |  1 +
 src/test/disk_tests/multipath/dm-1/dm/uuid    |  1 +
 src/test/disk_tests/multipath/dm-1/size       |  1 +
 src/test/disk_tests/multipath/dm-1_slaves     |  4 +
 src/test/disk_tests/multipath/dm-2/dm/name    |  1 +
 src/test/disk_tests/multipath/dm-2/dm/uuid    |  1 +
 src/test/disk_tests/multipath/dm-2/size       |  1 +
 src/test/disk_tests/multipath/dm-2_slaves     |  1 +
 src/test/disk_tests/multipath/dm-3/dm/name    |  1 +
 src/test/disk_tests/multipath/dm-3/dm/uuid    |  1 +
 src/test/disk_tests/multipath/dm-3/size       |  1 +
 src/test/disk_tests/multipath/dm-3_slaves     |  1 +
 src/test/disk_tests/multipath/dm-4/dm/name    |  1 +
 src/test/disk_tests/multipath/dm-4/dm/uuid    |  1 +
 src/test/disk_tests/multipath/dm-4/size       |  1 +
 src/test/disk_tests/multipath/dm-4_slaves     |  1 +
 src/test/disk_tests/multipath/lsblk           | 63 +++++++++++++++
 src/test/disk_tests/multipath/lvs             |  0
 src/test/disk_tests/multipath/mounts          |  1 +
 .../multipath/multipath_expected.json         | 59 ++++++++++++++
 src/test/disk_tests/multipath/partlist        |  0
 src/test/disk_tests/multipath/pvs             |  1 +
 .../disk_tests/multipath/sda/device/model     |  1 +
 .../disk_tests/multipath/sda/device/vendor    |  1 +
 .../disk_tests/multipath/sda/holders/holder   |  1 +
 .../disk_tests/multipath/sda/queue/rotational |  1 +
 src/test/disk_tests/multipath/sda/size        |  1 +
 src/test/disk_tests/multipath/sda_udevadm     | 18 +++++
 .../disk_tests/multipath/sdb/device/model     |  1 +
 .../disk_tests/multipath/sdb/device/vendor    |  1 +
 .../disk_tests/multipath/sdb/holders/holder   |  1 +
 .../disk_tests/multipath/sdb/queue/rotational |  1 +
 src/test/disk_tests/multipath/sdb/size        |  1 +
 src/test/disk_tests/multipath/sdb_udevadm     | 18 +++++
 .../disk_tests/multipath/sdc/device/model     |  1 +
 .../disk_tests/multipath/sdc/device/vendor    |  1 +
 .../disk_tests/multipath/sdc/holders/holder   |  1 +
 .../disk_tests/multipath/sdc/queue/rotational |  1 +
 src/test/disk_tests/multipath/sdc/size        |  1 +
 src/test/disk_tests/multipath/sdc_udevadm     | 18 +++++
 .../disk_tests/multipath/sdd/device/model     |  1 +
 .../disk_tests/multipath/sdd/device/vendor    |  1 +
 .../disk_tests/multipath/sdd/holders/holder   |  1 +
 .../disk_tests/multipath/sdd/queue/rotational |  1 +
 src/test/disk_tests/multipath/sdd/size        |  1 +
 src/test/disk_tests/multipath/sdd_udevadm     | 18 +++++
 src/test/disk_tests/multipath/zpool           |  1 +
 src/test/disklist_test.pm                     | 49 +++++++++++-
 55 files changed, 452 insertions(+), 2 deletions(-)
 create mode 100644 src/test/disk_tests/multipath/disklist
 create mode 100644 src/test/disk_tests/multipath/disklist_expected.json
 create mode 100644 src/test/disk_tests/multipath/dm-0/dm/name
 create mode 100644 src/test/disk_tests/multipath/dm-0/dm/uuid
 create mode 100644 src/test/disk_tests/multipath/dm-0/size
 create mode 100644 src/test/disk_tests/multipath/dm-0_slaves
 create mode 100644 src/test/disk_tests/multipath/dm-1/dm/name
 create mode 100644 src/test/disk_tests/multipath/dm-1/dm/uuid
 create mode 100644 src/test/disk_tests/multipath/dm-1/size
 create mode 100644 src/test/disk_tests/multipath/dm-1_slaves
 create mode 100644 src/test/disk_tests/multipath/dm-2/dm/name
 create mode 100644 src/test/disk_tests/multipath/dm-2/dm/uuid
 create mode 100644 src/test/disk_tests/multipath/dm-2/size
 create mode 100644 src/test/disk_tests/multipath/dm-2_slaves
 create mode 100644 src/test/disk_tests/multipath/dm-3/dm/name
 create mode 100644 src/test/disk_tests/multipath/dm-3/dm/uuid
 create mode 100644 src/test/disk_tests/multipath/dm-3/size
 create mode 100644 src/test/disk_tests/multipath/dm-3_slaves
 create mode 100644 src/test/disk_tests/multipath/dm-4/dm/name
 create mode 100644 src/test/disk_tests/multipath/dm-4/dm/uuid
 create mode 100644 src/test/disk_tests/multipath/dm-4/size
 create mode 100644 src/test/disk_tests/multipath/dm-4_slaves
 create mode 100644 src/test/disk_tests/multipath/lsblk
 create mode 100644 src/test/disk_tests/multipath/lvs
 create mode 100644 src/test/disk_tests/multipath/mounts
 create mode 100644 src/test/disk_tests/multipath/multipath_expected.json
 create mode 100644 src/test/disk_tests/multipath/partlist
 create mode 100644 src/test/disk_tests/multipath/pvs
 create mode 100644 src/test/disk_tests/multipath/sda/device/model
 create mode 100644 src/test/disk_tests/multipath/sda/device/vendor
 create mode 100644 src/test/disk_tests/multipath/sda/holders/holder
 create mode 100644 src/test/disk_tests/multipath/sda/queue/rotational
 create mode 100644 src/test/disk_tests/multipath/sda/size
 create mode 100644 src/test/disk_tests/multipath/sda_udevadm
 create mode 100644 src/test/disk_tests/multipath/sdb/device/model
 create mode 100644 src/test/disk_tests/multipath/sdb/device/vendor
 create mode 100644 src/test/disk_tests/multipath/sdb/holders/holder
 create mode 100644 src/test/disk_tests/multipath/sdb/queue/rotational
 create mode 100644 src/test/disk_tests/multipath/sdb/size
 create mode 100644 src/test/disk_tests/multipath/sdb_udevadm
 create mode 100644 src/test/disk_tests/multipath/sdc/device/model
 create mode 100644 src/test/disk_tests/multipath/sdc/device/vendor
 create mode 100644 src/test/disk_tests/multipath/sdc/holders/holder
 create mode 100644 src/test/disk_tests/multipath/sdc/queue/rotational
 create mode 100644 src/test/disk_tests/multipath/sdc/size
 create mode 100644 src/test/disk_tests/multipath/sdc_udevadm
 create mode 100644 src/test/disk_tests/multipath/sdd/device/model
 create mode 100644 src/test/disk_tests/multipath/sdd/device/vendor
 create mode 100644 src/test/disk_tests/multipath/sdd/holders/holder
 create mode 100644 src/test/disk_tests/multipath/sdd/queue/rotational
 create mode 100644 src/test/disk_tests/multipath/sdd/size
 create mode 100644 src/test/disk_tests/multipath/sdd_udevadm
 create mode 100644 src/test/disk_tests/multipath/zpool

diff --git a/src/PVE/Diskmanage.pm b/src/PVE/Diskmanage.pm
index c04408e..ed5181f 100644
--- a/src/PVE/Diskmanage.pm
+++ b/src/PVE/Diskmanage.pm
@@ -174,7 +174,7 @@ sub get_smart_data {
 }
 
 sub get_lsblk_info {
-    my $cmd = [$LSBLK, '--json', '-o', 'path,parttype,fstype,tran'];
+    my $cmd = [$LSBLK, '--json', '-o', 'path,parttype,fstype,tran,pttype'];
     my $output = "";
     eval {
         run_command($cmd, outfunc => sub { $output .= "$_[0]\n"; });
@@ -192,6 +192,7 @@ sub get_lsblk_info {
                 parttype => $_->{parttype},
                 fstype => $_->{fstype},
                 tran => $_->{tran},
+                pttype => $_->{pttype},
             }
         } @{$list}
     };
@@ -771,6 +772,82 @@ sub get_disks {
     return $disklist;
 }
 
+# Lists multipath mapped devices with information aggregated from their member paths, keyed by
+# their kernel device name (dm-X). These are deliberately not returned by get_disks, as its
+# callers manage local disks only.
+sub get_multipath_disks {
+    my $mounted = mounted_blockdevs();
+    my $lsblk_info = get_lsblk_info();
+    my $zfshash = get_zfs_devices($lsblk_info);
+    my $lvmhash = get_lvm_devices($lsblk_info);
+
+    my $disklist = {};
+
+    dir_glob_foreach(
+        '/sys/block',
+        'dm-\d+',
+        sub {
+            my ($dev) = @_;
+            my $sysdir = "/sys/block/$dev";
+
+            my $uuid = file_read_firstline("$sysdir/dm/uuid") // return;
+            return if $uuid !~ m/^mpath-(\S+)$/;
+            my $wwid = $1;
+
+            my $name = file_read_firstline("$sysdir/dm/name") // return;
+            my $devpath = "/dev/mapper/$name";
+            my $resolved_devpath = abs_path($devpath);
+            my @devpaths = ($devpath);
+            push @devpaths, $resolved_devpath
+                if defined($resolved_devpath) && $resolved_devpath ne $devpath;
+
+            my $size = get_sysdir_size($sysdir) or return;
+
+            my $slaves = [];
+            dir_glob_foreach("$sysdir/slaves", '[^.].*', sub { push @$slaves, $_[0]; });
+            @$slaves = sort @$slaves;
+
+            my ($vendor, $model, $transport) = ('unknown', 'unknown', undef);
+            if (my $first = $slaves->[0]) {
+                if (my $sysdata = get_sysdir_info("/sys/block/$first")) {
+                    $vendor = $sysdata->{vendor};
+                    $model = $sysdata->{model};
+                }
+                $transport = $lsblk_info->{"/dev/$first"}->{tran};
+            }
+
+            my $used;
+            $used = 'LVM' if grep { $lvmhash->{$_} } @devpaths;
+            $used //= 'ZFS' if grep { $zfshash->{$_} } @devpaths;
+            my $info = {};
+            for my $path (@devpaths) {
+                $info = $lsblk_info->{$path} // next;
+                last;
+            }
+            $used //= "$info->{fstype}" if defined($info->{fstype});
+            $used //= 'mounted' if grep { $mounted->{$_} } @devpaths;
+            $used //= 'partitions'
+                if defined($info->{pttype}) && $info->{pttype} ne '';
+            # partition mappings and other stacked devices show up as holders
+            $used //= 'Device Mapper' if !dir_is_empty("$sysdir/holders");
+
+            $disklist->{$dev} = {
+                devpath => $devpath,
+                size => $size,
+                vendor => $vendor,
+                model => $model,
+                wwn => $wwid,
+                paths => scalar($slaves->@*),
+                slaves => [map { "/dev/$_" } $slaves->@*],
+            };
+            $disklist->{$dev}->{transport} = $transport if defined($transport);
+            $disklist->{$dev}->{used} = $used if $used;
+        },
+    );
+
+    return $disklist;
+}
+
 sub get_partnum {
     my ($part_path) = @_;
 
diff --git a/src/test/disk_tests/multipath/disklist b/src/test/disk_tests/multipath/disklist
new file mode 100644
index 0000000..9293589
--- /dev/null
+++ b/src/test/disk_tests/multipath/disklist
@@ -0,0 +1,9 @@
+sda
+sdb
+sdc
+sdd
+dm-0
+dm-1
+dm-2
+dm-3
+dm-4
diff --git a/src/test/disk_tests/multipath/disklist_expected.json b/src/test/disk_tests/multipath/disklist_expected.json
new file mode 100644
index 0000000..a8e74fa
--- /dev/null
+++ b/src/test/disk_tests/multipath/disklist_expected.json
@@ -0,0 +1,74 @@
+{
+    "sda": {
+        "devpath": "/dev/sda",
+        "size": 10737418240,
+        "vendor": "SANVEND",
+        "model": "SANMODEL",
+        "serial": "SSERa",
+        "wwn": "0x600140500000000a",
+        "gpt": 0,
+        "rpm": -1,
+        "type": "unknown",
+        "health": "UNKNOWN",
+        "wearout": "N/A",
+        "osdid": -1,
+        "osdid-list": null,
+        "by_id_link": "/dev/disk/by-id/scsi-36001405000000000000000000000000a",
+        "transport": "fc",
+        "used": "Device Mapper"
+    },
+    "sdb": {
+        "devpath": "/dev/sdb",
+        "size": 10737418240,
+        "vendor": "SANVEND",
+        "model": "SANMODEL",
+        "serial": "SSERb",
+        "wwn": "0x600140500000000b",
+        "gpt": 0,
+        "rpm": -1,
+        "type": "unknown",
+        "health": "UNKNOWN",
+        "wearout": "N/A",
+        "osdid": -1,
+        "osdid-list": null,
+        "by_id_link": "/dev/disk/by-id/scsi-36001405000000000000000000000000b",
+        "transport": "fc",
+        "used": "Device Mapper"
+    },
+    "sdc": {
+        "devpath": "/dev/sdc",
+        "size": 10737418240,
+        "vendor": "SANVEND",
+        "model": "SANMODEL",
+        "serial": "SSERc",
+        "wwn": "0x600140500000000c",
+        "gpt": 0,
+        "rpm": -1,
+        "type": "unknown",
+        "health": "UNKNOWN",
+        "wearout": "N/A",
+        "osdid": -1,
+        "osdid-list": null,
+        "by_id_link": "/dev/disk/by-id/scsi-36001405000000000000000000000000c",
+        "transport": "fc",
+        "used": "Device Mapper"
+    },
+    "sdd": {
+        "devpath": "/dev/sdd",
+        "size": 10737418240,
+        "vendor": "SANVEND",
+        "model": "SANMODEL",
+        "serial": "SSERd",
+        "wwn": "0x600140500000000d",
+        "gpt": 0,
+        "rpm": -1,
+        "type": "unknown",
+        "health": "UNKNOWN",
+        "wearout": "N/A",
+        "osdid": -1,
+        "osdid-list": null,
+        "by_id_link": "/dev/disk/by-id/scsi-36001405000000000000000000000000d",
+        "transport": "fc",
+        "used": "Device Mapper"
+    }
+}
diff --git a/src/test/disk_tests/multipath/dm-0/dm/name b/src/test/disk_tests/multipath/dm-0/dm/name
new file mode 100644
index 0000000..0ed057a
--- /dev/null
+++ b/src/test/disk_tests/multipath/dm-0/dm/name
@@ -0,0 +1 @@
+mpatha
\ No newline at end of file
diff --git a/src/test/disk_tests/multipath/dm-0/dm/uuid b/src/test/disk_tests/multipath/dm-0/dm/uuid
new file mode 100644
index 0000000..094955a
--- /dev/null
+++ b/src/test/disk_tests/multipath/dm-0/dm/uuid
@@ -0,0 +1 @@
+mpath-3600140500000000000000000000000001
\ No newline at end of file
diff --git a/src/test/disk_tests/multipath/dm-0/size b/src/test/disk_tests/multipath/dm-0/size
new file mode 100644
index 0000000..8280959
--- /dev/null
+++ b/src/test/disk_tests/multipath/dm-0/size
@@ -0,0 +1 @@
+20971520
\ No newline at end of file
diff --git a/src/test/disk_tests/multipath/dm-0_slaves b/src/test/disk_tests/multipath/dm-0_slaves
new file mode 100644
index 0000000..1637315
--- /dev/null
+++ b/src/test/disk_tests/multipath/dm-0_slaves
@@ -0,0 +1,4 @@
+.
+..
+sda
+sdb
diff --git a/src/test/disk_tests/multipath/dm-1/dm/name b/src/test/disk_tests/multipath/dm-1/dm/name
new file mode 100644
index 0000000..f70ac3d
--- /dev/null
+++ b/src/test/disk_tests/multipath/dm-1/dm/name
@@ -0,0 +1 @@
+mpathb
\ No newline at end of file
diff --git a/src/test/disk_tests/multipath/dm-1/dm/uuid b/src/test/disk_tests/multipath/dm-1/dm/uuid
new file mode 100644
index 0000000..7e8567b
--- /dev/null
+++ b/src/test/disk_tests/multipath/dm-1/dm/uuid
@@ -0,0 +1 @@
+mpath-3600140500000000000000000000000002
\ No newline at end of file
diff --git a/src/test/disk_tests/multipath/dm-1/size b/src/test/disk_tests/multipath/dm-1/size
new file mode 100644
index 0000000..8280959
--- /dev/null
+++ b/src/test/disk_tests/multipath/dm-1/size
@@ -0,0 +1 @@
+20971520
\ No newline at end of file
diff --git a/src/test/disk_tests/multipath/dm-1_slaves b/src/test/disk_tests/multipath/dm-1_slaves
new file mode 100644
index 0000000..cde08b3
--- /dev/null
+++ b/src/test/disk_tests/multipath/dm-1_slaves
@@ -0,0 +1,4 @@
+.
+..
+sdc
+sdd
diff --git a/src/test/disk_tests/multipath/dm-2/dm/name b/src/test/disk_tests/multipath/dm-2/dm/name
new file mode 100644
index 0000000..4770de4
--- /dev/null
+++ b/src/test/disk_tests/multipath/dm-2/dm/name
@@ -0,0 +1 @@
+mpathc
diff --git a/src/test/disk_tests/multipath/dm-2/dm/uuid b/src/test/disk_tests/multipath/dm-2/dm/uuid
new file mode 100644
index 0000000..2d607ea
--- /dev/null
+++ b/src/test/disk_tests/multipath/dm-2/dm/uuid
@@ -0,0 +1 @@
+mpath-3600140500000000000000000000000003
diff --git a/src/test/disk_tests/multipath/dm-2/size b/src/test/disk_tests/multipath/dm-2/size
new file mode 100644
index 0000000..14faeec
--- /dev/null
+++ b/src/test/disk_tests/multipath/dm-2/size
@@ -0,0 +1 @@
+20971520
diff --git a/src/test/disk_tests/multipath/dm-2_slaves b/src/test/disk_tests/multipath/dm-2_slaves
new file mode 100644
index 0000000..8b13789
--- /dev/null
+++ b/src/test/disk_tests/multipath/dm-2_slaves
@@ -0,0 +1 @@
+
diff --git a/src/test/disk_tests/multipath/dm-3/dm/name b/src/test/disk_tests/multipath/dm-3/dm/name
new file mode 100644
index 0000000..a98ef69
--- /dev/null
+++ b/src/test/disk_tests/multipath/dm-3/dm/name
@@ -0,0 +1 @@
+mpathd
diff --git a/src/test/disk_tests/multipath/dm-3/dm/uuid b/src/test/disk_tests/multipath/dm-3/dm/uuid
new file mode 100644
index 0000000..75f25b8
--- /dev/null
+++ b/src/test/disk_tests/multipath/dm-3/dm/uuid
@@ -0,0 +1 @@
+mpath-3600140500000000000000000000000004
diff --git a/src/test/disk_tests/multipath/dm-3/size b/src/test/disk_tests/multipath/dm-3/size
new file mode 100644
index 0000000..14faeec
--- /dev/null
+++ b/src/test/disk_tests/multipath/dm-3/size
@@ -0,0 +1 @@
+20971520
diff --git a/src/test/disk_tests/multipath/dm-3_slaves b/src/test/disk_tests/multipath/dm-3_slaves
new file mode 100644
index 0000000..8b13789
--- /dev/null
+++ b/src/test/disk_tests/multipath/dm-3_slaves
@@ -0,0 +1 @@
+
diff --git a/src/test/disk_tests/multipath/dm-4/dm/name b/src/test/disk_tests/multipath/dm-4/dm/name
new file mode 100644
index 0000000..e084e8f
--- /dev/null
+++ b/src/test/disk_tests/multipath/dm-4/dm/name
@@ -0,0 +1 @@
+mpathe
diff --git a/src/test/disk_tests/multipath/dm-4/dm/uuid b/src/test/disk_tests/multipath/dm-4/dm/uuid
new file mode 100644
index 0000000..0ca7875
--- /dev/null
+++ b/src/test/disk_tests/multipath/dm-4/dm/uuid
@@ -0,0 +1 @@
+mpath-3600140500000000000000000000000005
diff --git a/src/test/disk_tests/multipath/dm-4/size b/src/test/disk_tests/multipath/dm-4/size
new file mode 100644
index 0000000..14faeec
--- /dev/null
+++ b/src/test/disk_tests/multipath/dm-4/size
@@ -0,0 +1 @@
+20971520
diff --git a/src/test/disk_tests/multipath/dm-4_slaves b/src/test/disk_tests/multipath/dm-4_slaves
new file mode 100644
index 0000000..8b13789
--- /dev/null
+++ b/src/test/disk_tests/multipath/dm-4_slaves
@@ -0,0 +1 @@
+
diff --git a/src/test/disk_tests/multipath/lsblk b/src/test/disk_tests/multipath/lsblk
new file mode 100644
index 0000000..91f7457
--- /dev/null
+++ b/src/test/disk_tests/multipath/lsblk
@@ -0,0 +1,63 @@
+{
+   "blockdevices": [
+      {
+         "path": "/dev/sda",
+         "parttype": null,
+         "fstype": null,
+         "tran": "fc"
+      },
+      {
+         "path": "/dev/sdb",
+         "parttype": null,
+         "fstype": null,
+         "tran": "fc"
+      },
+      {
+         "path": "/dev/sdc",
+         "parttype": null,
+         "fstype": null,
+         "tran": "fc"
+      },
+      {
+         "path": "/dev/sdd",
+         "parttype": null,
+         "fstype": null,
+         "tran": "fc"
+      },
+      {
+         "path": "/dev/mapper/mpatha",
+         "parttype": null,
+         "fstype": null,
+         "tran": null,
+         "pttype": null
+      },
+      {
+         "path": "/dev/mapper/mpathb",
+         "parttype": null,
+         "fstype": null,
+         "tran": null,
+         "pttype": null
+      },
+      {
+         "path": "/dev/mapper/mpathc",
+         "parttype": null,
+         "fstype": null,
+         "tran": null,
+         "pttype": "gpt"
+      },
+      {
+         "path": "/dev/dm-3",
+         "parttype": null,
+         "fstype": null,
+         "tran": null,
+         "pttype": null
+      },
+      {
+         "path": "/dev/dm-4",
+         "parttype": null,
+         "fstype": null,
+         "tran": null,
+         "pttype": null
+      }
+   ]
+}
diff --git a/src/test/disk_tests/multipath/lvs b/src/test/disk_tests/multipath/lvs
new file mode 100644
index 0000000..e69de29
diff --git a/src/test/disk_tests/multipath/mounts b/src/test/disk_tests/multipath/mounts
new file mode 100644
index 0000000..06ada27
--- /dev/null
+++ b/src/test/disk_tests/multipath/mounts
@@ -0,0 +1 @@
+/dev/mapper/mpathe /mnt
diff --git a/src/test/disk_tests/multipath/multipath_expected.json b/src/test/disk_tests/multipath/multipath_expected.json
new file mode 100644
index 0000000..62e6e1c
--- /dev/null
+++ b/src/test/disk_tests/multipath/multipath_expected.json
@@ -0,0 +1,59 @@
+{
+    "dm-0": {
+        "devpath": "/dev/mapper/mpatha",
+        "size": 10737418240,
+        "vendor": "SANVEND",
+        "model": "SANMODEL",
+        "wwn": "3600140500000000000000000000000001",
+        "paths": 2,
+        "slaves": [
+            "/dev/sda",
+            "/dev/sdb"
+        ],
+        "transport": "fc"
+    },
+    "dm-1": {
+        "devpath": "/dev/mapper/mpathb",
+        "size": 10737418240,
+        "vendor": "SANVEND",
+        "model": "SANMODEL",
+        "wwn": "3600140500000000000000000000000002",
+        "paths": 2,
+        "slaves": [
+            "/dev/sdc",
+            "/dev/sdd"
+        ],
+        "transport": "fc",
+        "used": "LVM"
+    },
+    "dm-2": {
+        "devpath": "/dev/mapper/mpathc",
+        "size": 10737418240,
+        "vendor": "unknown",
+        "model": "unknown",
+        "wwn": "3600140500000000000000000000000003",
+        "paths": 0,
+        "slaves": [],
+        "used": "partitions"
+    },
+    "dm-3": {
+        "devpath": "/dev/mapper/mpathd",
+        "size": 10737418240,
+        "vendor": "unknown",
+        "model": "unknown",
+        "wwn": "3600140500000000000000000000000004",
+        "paths": 0,
+        "slaves": [],
+        "used": "ZFS"
+    },
+    "dm-4": {
+        "devpath": "/dev/mapper/mpathe",
+        "size": 10737418240,
+        "vendor": "unknown",
+        "model": "unknown",
+        "wwn": "3600140500000000000000000000000005",
+        "paths": 0,
+        "slaves": [],
+        "used": "mounted"
+    }
+}
diff --git a/src/test/disk_tests/multipath/partlist b/src/test/disk_tests/multipath/partlist
new file mode 100644
index 0000000..e69de29
diff --git a/src/test/disk_tests/multipath/pvs b/src/test/disk_tests/multipath/pvs
new file mode 100644
index 0000000..9c2f9b9
--- /dev/null
+++ b/src/test/disk_tests/multipath/pvs
@@ -0,0 +1 @@
+  /dev/dm-1
diff --git a/src/test/disk_tests/multipath/sda/device/model b/src/test/disk_tests/multipath/sda/device/model
new file mode 100644
index 0000000..2f50f3f
--- /dev/null
+++ b/src/test/disk_tests/multipath/sda/device/model
@@ -0,0 +1 @@
+SANMODEL
\ No newline at end of file
diff --git a/src/test/disk_tests/multipath/sda/device/vendor b/src/test/disk_tests/multipath/sda/device/vendor
new file mode 100644
index 0000000..ead8944
--- /dev/null
+++ b/src/test/disk_tests/multipath/sda/device/vendor
@@ -0,0 +1 @@
+SANVEND
\ No newline at end of file
diff --git a/src/test/disk_tests/multipath/sda/holders/holder b/src/test/disk_tests/multipath/sda/holders/holder
new file mode 100644
index 0000000..9255a92
--- /dev/null
+++ b/src/test/disk_tests/multipath/sda/holders/holder
@@ -0,0 +1 @@
+dm-holder
\ No newline at end of file
diff --git a/src/test/disk_tests/multipath/sda/queue/rotational b/src/test/disk_tests/multipath/sda/queue/rotational
new file mode 100644
index 0000000..56a6051
--- /dev/null
+++ b/src/test/disk_tests/multipath/sda/queue/rotational
@@ -0,0 +1 @@
+1
\ No newline at end of file
diff --git a/src/test/disk_tests/multipath/sda/size b/src/test/disk_tests/multipath/sda/size
new file mode 100644
index 0000000..8280959
--- /dev/null
+++ b/src/test/disk_tests/multipath/sda/size
@@ -0,0 +1 @@
+20971520
\ No newline at end of file
diff --git a/src/test/disk_tests/multipath/sda_udevadm b/src/test/disk_tests/multipath/sda_udevadm
new file mode 100644
index 0000000..7bd58b4
--- /dev/null
+++ b/src/test/disk_tests/multipath/sda_udevadm
@@ -0,0 +1,18 @@
+P: /devices/pci0000:00/0000:00:03.0/0000:02:00.0/host4/rport-4:0-0/target4:0:0/4:0:0:0/block/sda
+N: sda
+S: disk/by-id/scsi-36001405000000000000000000000000a
+E: DEVLINKS=/dev/disk/by-id/scsi-36001405000000000000000000000000a
+E: DEVNAME=/dev/sda
+E: DEVPATH=/devices/pci0000:00/0000:00:03.0/0000:02:00.0/host4/rport-4:0-0/target4:0:0/4:0:0:0/block/sda
+E: DEVTYPE=disk
+E: ID_BUS=scsi
+E: ID_MODEL=SANMODEL
+E: ID_SCSI=1
+E: ID_SERIAL=SANSERIALa
+E: ID_SERIAL_SHORT=SSERa
+E: ID_TYPE=disk
+E: ID_VENDOR=SANVEND
+E: ID_WWN=0x600140500000000a
+E: MAJOR=8
+E: MINOR=0
+E: SUBSYSTEM=block
diff --git a/src/test/disk_tests/multipath/sdb/device/model b/src/test/disk_tests/multipath/sdb/device/model
new file mode 100644
index 0000000..2f50f3f
--- /dev/null
+++ b/src/test/disk_tests/multipath/sdb/device/model
@@ -0,0 +1 @@
+SANMODEL
\ No newline at end of file
diff --git a/src/test/disk_tests/multipath/sdb/device/vendor b/src/test/disk_tests/multipath/sdb/device/vendor
new file mode 100644
index 0000000..ead8944
--- /dev/null
+++ b/src/test/disk_tests/multipath/sdb/device/vendor
@@ -0,0 +1 @@
+SANVEND
\ No newline at end of file
diff --git a/src/test/disk_tests/multipath/sdb/holders/holder b/src/test/disk_tests/multipath/sdb/holders/holder
new file mode 100644
index 0000000..9255a92
--- /dev/null
+++ b/src/test/disk_tests/multipath/sdb/holders/holder
@@ -0,0 +1 @@
+dm-holder
\ No newline at end of file
diff --git a/src/test/disk_tests/multipath/sdb/queue/rotational b/src/test/disk_tests/multipath/sdb/queue/rotational
new file mode 100644
index 0000000..56a6051
--- /dev/null
+++ b/src/test/disk_tests/multipath/sdb/queue/rotational
@@ -0,0 +1 @@
+1
\ No newline at end of file
diff --git a/src/test/disk_tests/multipath/sdb/size b/src/test/disk_tests/multipath/sdb/size
new file mode 100644
index 0000000..8280959
--- /dev/null
+++ b/src/test/disk_tests/multipath/sdb/size
@@ -0,0 +1 @@
+20971520
\ No newline at end of file
diff --git a/src/test/disk_tests/multipath/sdb_udevadm b/src/test/disk_tests/multipath/sdb_udevadm
new file mode 100644
index 0000000..9a04c63
--- /dev/null
+++ b/src/test/disk_tests/multipath/sdb_udevadm
@@ -0,0 +1,18 @@
+P: /devices/pci0000:00/0000:00:03.0/0000:02:00.0/host4/rport-4:0-0/target4:0:0/4:0:0:0/block/sdb
+N: sdb
+S: disk/by-id/scsi-36001405000000000000000000000000b
+E: DEVLINKS=/dev/disk/by-id/scsi-36001405000000000000000000000000b
+E: DEVNAME=/dev/sdb
+E: DEVPATH=/devices/pci0000:00/0000:00:03.0/0000:02:00.0/host4/rport-4:0-0/target4:0:0/4:0:0:0/block/sdb
+E: DEVTYPE=disk
+E: ID_BUS=scsi
+E: ID_MODEL=SANMODEL
+E: ID_SCSI=1
+E: ID_SERIAL=SANSERIALb
+E: ID_SERIAL_SHORT=SSERb
+E: ID_TYPE=disk
+E: ID_VENDOR=SANVEND
+E: ID_WWN=0x600140500000000b
+E: MAJOR=8
+E: MINOR=0
+E: SUBSYSTEM=block
diff --git a/src/test/disk_tests/multipath/sdc/device/model b/src/test/disk_tests/multipath/sdc/device/model
new file mode 100644
index 0000000..2f50f3f
--- /dev/null
+++ b/src/test/disk_tests/multipath/sdc/device/model
@@ -0,0 +1 @@
+SANMODEL
\ No newline at end of file
diff --git a/src/test/disk_tests/multipath/sdc/device/vendor b/src/test/disk_tests/multipath/sdc/device/vendor
new file mode 100644
index 0000000..ead8944
--- /dev/null
+++ b/src/test/disk_tests/multipath/sdc/device/vendor
@@ -0,0 +1 @@
+SANVEND
\ No newline at end of file
diff --git a/src/test/disk_tests/multipath/sdc/holders/holder b/src/test/disk_tests/multipath/sdc/holders/holder
new file mode 100644
index 0000000..9255a92
--- /dev/null
+++ b/src/test/disk_tests/multipath/sdc/holders/holder
@@ -0,0 +1 @@
+dm-holder
\ No newline at end of file
diff --git a/src/test/disk_tests/multipath/sdc/queue/rotational b/src/test/disk_tests/multipath/sdc/queue/rotational
new file mode 100644
index 0000000..56a6051
--- /dev/null
+++ b/src/test/disk_tests/multipath/sdc/queue/rotational
@@ -0,0 +1 @@
+1
\ No newline at end of file
diff --git a/src/test/disk_tests/multipath/sdc/size b/src/test/disk_tests/multipath/sdc/size
new file mode 100644
index 0000000..8280959
--- /dev/null
+++ b/src/test/disk_tests/multipath/sdc/size
@@ -0,0 +1 @@
+20971520
\ No newline at end of file
diff --git a/src/test/disk_tests/multipath/sdc_udevadm b/src/test/disk_tests/multipath/sdc_udevadm
new file mode 100644
index 0000000..acff253
--- /dev/null
+++ b/src/test/disk_tests/multipath/sdc_udevadm
@@ -0,0 +1,18 @@
+P: /devices/pci0000:00/0000:00:03.0/0000:02:00.0/host4/rport-4:0-0/target4:0:0/4:0:0:0/block/sdc
+N: sdc
+S: disk/by-id/scsi-36001405000000000000000000000000c
+E: DEVLINKS=/dev/disk/by-id/scsi-36001405000000000000000000000000c
+E: DEVNAME=/dev/sdc
+E: DEVPATH=/devices/pci0000:00/0000:00:03.0/0000:02:00.0/host4/rport-4:0-0/target4:0:0/4:0:0:0/block/sdc
+E: DEVTYPE=disk
+E: ID_BUS=scsi
+E: ID_MODEL=SANMODEL
+E: ID_SCSI=1
+E: ID_SERIAL=SANSERIALc
+E: ID_SERIAL_SHORT=SSERc
+E: ID_TYPE=disk
+E: ID_VENDOR=SANVEND
+E: ID_WWN=0x600140500000000c
+E: MAJOR=8
+E: MINOR=0
+E: SUBSYSTEM=block
diff --git a/src/test/disk_tests/multipath/sdd/device/model b/src/test/disk_tests/multipath/sdd/device/model
new file mode 100644
index 0000000..2f50f3f
--- /dev/null
+++ b/src/test/disk_tests/multipath/sdd/device/model
@@ -0,0 +1 @@
+SANMODEL
\ No newline at end of file
diff --git a/src/test/disk_tests/multipath/sdd/device/vendor b/src/test/disk_tests/multipath/sdd/device/vendor
new file mode 100644
index 0000000..ead8944
--- /dev/null
+++ b/src/test/disk_tests/multipath/sdd/device/vendor
@@ -0,0 +1 @@
+SANVEND
\ No newline at end of file
diff --git a/src/test/disk_tests/multipath/sdd/holders/holder b/src/test/disk_tests/multipath/sdd/holders/holder
new file mode 100644
index 0000000..9255a92
--- /dev/null
+++ b/src/test/disk_tests/multipath/sdd/holders/holder
@@ -0,0 +1 @@
+dm-holder
\ No newline at end of file
diff --git a/src/test/disk_tests/multipath/sdd/queue/rotational b/src/test/disk_tests/multipath/sdd/queue/rotational
new file mode 100644
index 0000000..56a6051
--- /dev/null
+++ b/src/test/disk_tests/multipath/sdd/queue/rotational
@@ -0,0 +1 @@
+1
\ No newline at end of file
diff --git a/src/test/disk_tests/multipath/sdd/size b/src/test/disk_tests/multipath/sdd/size
new file mode 100644
index 0000000..8280959
--- /dev/null
+++ b/src/test/disk_tests/multipath/sdd/size
@@ -0,0 +1 @@
+20971520
\ No newline at end of file
diff --git a/src/test/disk_tests/multipath/sdd_udevadm b/src/test/disk_tests/multipath/sdd_udevadm
new file mode 100644
index 0000000..813c5f1
--- /dev/null
+++ b/src/test/disk_tests/multipath/sdd_udevadm
@@ -0,0 +1,18 @@
+P: /devices/pci0000:00/0000:00:03.0/0000:02:00.0/host4/rport-4:0-0/target4:0:0/4:0:0:0/block/sdd
+N: sdd
+S: disk/by-id/scsi-36001405000000000000000000000000d
+E: DEVLINKS=/dev/disk/by-id/scsi-36001405000000000000000000000000d
+E: DEVNAME=/dev/sdd
+E: DEVPATH=/devices/pci0000:00/0000:00:03.0/0000:02:00.0/host4/rport-4:0-0/target4:0:0/4:0:0:0/block/sdd
+E: DEVTYPE=disk
+E: ID_BUS=scsi
+E: ID_MODEL=SANMODEL
+E: ID_SCSI=1
+E: ID_SERIAL=SANSERIALd
+E: ID_SERIAL_SHORT=SSERd
+E: ID_TYPE=disk
+E: ID_VENDOR=SANVEND
+E: ID_WWN=0x600140500000000d
+E: MAJOR=8
+E: MINOR=0
+E: SUBSYSTEM=block
diff --git a/src/test/disk_tests/multipath/zpool b/src/test/disk_tests/multipath/zpool
new file mode 100644
index 0000000..e723f7e
--- /dev/null
+++ b/src/test/disk_tests/multipath/zpool
@@ -0,0 +1 @@
+	/dev/dm-3	-
diff --git a/src/test/disklist_test.pm b/src/test/disklist_test.pm
index a0dc01f..9e35430 100644
--- a/src/test/disklist_test.pm
+++ b/src/test/disklist_test.pm
@@ -18,6 +18,14 @@ my $testcount = 0; # testcount for TAP::Harness
 my $diskmanage_module; # mockmodule for PVE::Diskmanage
 my $print = 0;
 
+my $multipath_aliases = {
+    '/dev/mapper/mpatha' => '/dev/dm-0',
+    '/dev/mapper/mpathb' => '/dev/dm-1',
+    '/dev/mapper/mpathc' => '/dev/dm-2',
+    '/dev/mapper/mpathd' => '/dev/dm-3',
+    '/dev/mapper/mpathe' => '/dev/dm-4',
+};
+
 sub mocked_run_command {
     my ($cmd, %param) = @_;
 
@@ -101,6 +109,16 @@ sub mocked_is_iscsi {
     return 0;
 }
 
+sub mocked_file_read_firstline {
+    my ($path) = @_;
+
+    my $originalsub = $diskmanage_module->original('file_read_firstline');
+
+    $path =~ s|^/sys/block|disk_tests/$testcasedir|;
+
+    return &$originalsub($path);
+}
+
 sub mocked_dir_glob_foreach {
     my ($dir, $regex, $sub) = @_;
 
@@ -109,6 +127,8 @@ sub mocked_dir_glob_foreach {
     # read lines in from file
     if ($dir =~ m{^/sys/block$}) {
         @$lines = split(/\n/, read_test_file('disklist'));
+    } elsif ($dir =~ m{^/sys/block/([^/]+)/slaves$}) {
+        @$lines = split(/\n/, read_test_file("${1}_slaves"));
     } elsif ($dir =~ m{^/sys/block/([^/]+)}) {
         @$lines = split(/\n/, read_test_file('partlist'));
     }
@@ -213,6 +233,15 @@ sub test_disk_list {
         $testcount++;
         is_deeply($disks, $expected_disk_list, 'disk list should be the same');
 
+        if (-f "disk_tests/$testcasedir/multipath_expected.json") {
+            my $multipath = PVE::Diskmanage::get_multipath_disks();
+            my $expected_multipath = decode_json(read_test_file('multipath_expected.json'));
+
+            print Dumper($multipath) if $print;
+            $testcount++;
+            is_deeply($multipath, $expected_multipath, 'multipath list should be the same');
+        }
+
         done_testing($testcount);
     };
 }
@@ -239,8 +268,15 @@ $diskmanage_module->mock('assert_blockdev' => sub { return 1; });
 print("\tMocked assert_blockdev\n");
 $diskmanage_module->mock(
     'dir_is_empty' => sub {
-        # all partitions have a holder dir
         my $val = shift;
+        # let test cases provide the dir (e.g. holders) with content
+        my $mapped = $val;
+        $mapped =~ s|^/sys/block|disk_tests/$testcasedir|;
+        if (-d $mapped) {
+            my $originalsub = $diskmanage_module->original('dir_is_empty');
+            return &$originalsub($mapped);
+        }
+        # all partitions have a holder dir
         if ($val =~ m|^/sys/block/.+/.+/|) {
             return 0;
         }
@@ -248,6 +284,17 @@ $diskmanage_module->mock(
     },
 );
 print("\tMocked dir_is_empty\n");
+$diskmanage_module->mock('file_read_firstline' => \&mocked_file_read_firstline);
+print("\tMocked file_read_firstline\n");
+$diskmanage_module->mock(
+    'abs_path' => sub {
+        my ($path) = @_;
+        return $multipath_aliases->{$path}
+            if defined($multipath_aliases->{$path});
+        return &{ $diskmanage_module->original('abs_path') }($path);
+    },
+);
+print("\tMocked abs_path\n");
 $diskmanage_module->mock('check_bin' => sub { return 1; });
 print("\tMocked check_bin\n");
 my $tools_module = Test::MockModule->new('PVE::ProcFSTools', no_auto => 1);
-- 
2.47.3




  parent reply	other threads:[~2026-07-31 10:23 UTC|newest]

Thread overview: 28+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-07-31 10:21 [RFC pve-storage/proxmox-widget-toolkit/pve-manager 00/27] add guided remote storage setup and SAN visibility Dietmar Maurer
2026-07-31 10:21 ` [RFC pve-storage 01/27] diskmanage: collect disk transport type from lsblk Dietmar Maurer
2026-07-31 10:21 ` Dietmar Maurer [this message]
2026-07-31 10:21 ` [RFC pve-storage 03/27] diskmanage: qualify NVMe over fabrics transport Dietmar Maurer
2026-07-31 10:21 ` [RFC pve-storage 04/27] disks: list: add include-remote parameter Dietmar Maurer
2026-07-31 10:21 ` [RFC pve-storage 05/27] diskmanage: include iSCSI session devices in disk enumeration Dietmar Maurer
2026-07-31 10:21 ` [RFC pve-storage 06/27] diskmanage: link multipath member disks to their map device Dietmar Maurer
2026-07-31 10:21 ` [RFC pve-storage 07/27] iscsi: factor out session device map from device list Dietmar Maurer
2026-07-31 10:21 ` [RFC pve-storage 08/27] api: scan: add san-luns method listing SAN LUN candidates Dietmar Maurer
2026-07-31 10:21 ` [RFC pve-storage 09/27] disks: lvm: allow creating volume groups on multipath devices Dietmar Maurer
2026-07-31 10:21 ` [RFC pve-storage 10/27] diskmanage: add helper querying multipath path state Dietmar Maurer
2026-07-31 10:21 ` [RFC pve-storage 11/27] diskmanage: add helper querying NVMe native " Dietmar Maurer
2026-07-31 10:21 ` [RFC pve-storage 12/27] api: scan: san-luns: report " Dietmar Maurer
2026-07-31 10:21 ` [RFC pve-storage 13/27] iscsi plugin: list sessions of all transports and capture transport Dietmar Maurer
2026-07-31 10:21 ` [RFC pve-storage 14/27] api: add node-level iSCSI initiator target and session API Dietmar Maurer
2026-07-31 10:21 ` [RFC proxmox-widget-toolkit 15/27] disk selectors: allow opting into remote devices Dietmar Maurer
2026-07-31 10:21 ` [RFC pve-manager 16/27] ui: storage: allow switching the scan node of the NFS/CIFS scan combos Dietmar Maurer
2026-07-31 10:21 ` [RFC pve-manager 17/27] ui: storage: add guided remote storage wizard with NFS support Dietmar Maurer
2026-07-31 10:21 ` [RFC pve-manager 18/27] ui: storage wizard: add SMB/CIFS support Dietmar Maurer
2026-07-31 10:21 ` [RFC pve-manager 19/27] ui: storage wizard: add iSCSI support Dietmar Maurer
2026-07-31 10:21 ` [RFC pve-manager 20/27] ui: storage wizard: add FC-attached SAN (shared LVM) support Dietmar Maurer
2026-07-31 10:21 ` [RFC pve-manager 21/27] ui: storage wizard: add ZFS over iSCSI support Dietmar Maurer
2026-07-31 10:21 ` [RFC pve-manager 22/27] ui: dc: storage: add remote storage wizard entry to the add menu Dietmar Maurer
2026-07-31 10:21 ` [RFC pve-manager 23/27] ui: node: add SAN LUNs panel Dietmar Maurer
2026-07-31 10:21 ` [RFC pve-manager 24/27] ui: san luns: show multipath path state Dietmar Maurer
2026-07-31 10:21 ` [RFC pve-manager 25/27] api: nodes: add iSCSI initiator API endpoint Dietmar Maurer
2026-07-31 10:21 ` [RFC pve-manager 26/27] pvenode: add iscsi commands Dietmar Maurer
2026-07-31 10:21 ` [RFC pve-manager 27/27] ui: san luns: show iSCSI targets and sessions Dietmar Maurer

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=20260731102156.3947857-3-dietmar@proxmox.com \
    --to=dietmar@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