v1.1.37 MIT License

jt-pve-storage-purestorage

Pure Storage FlashArray iSCSI/FC Storage Plugin for Proxmox VE Pure Storage FlashArray iSCSI/FC Proxmox VE 儲存外掛程式

Enterprise SAN storage integration for Proxmox VE using the Pure Storage REST API (v1.x and v2.x). Supports iSCSI and Fibre Channel with multipath, snapshots, instant clones, ActiveCluster pods, live migration, and automatic device lifecycle management. 透過 Pure Storage REST API(v1.x 與 v2.x)將企業級 SAN 儲存整合至 Proxmox VE。支援 iSCSI 及 Fibre Channel,具備 multipath、快照、即時複製、ActiveCluster Pod、即時遷移,以及自動裝置生命週期管理。

Disclaimer 免責聲明

WARNING: This is a third-party project. Use at your own risk. 警告:此為第三方專案,使用風險自負。
  • This plugin is provided "AS IS" without warranty of any kind. 本 plugin 以「現況」提供,不附帶任何形式的保證。
  • iSCSI protocol has been tested in real customer environments but not yet at large scale. iSCSI 協定已經過環境測試,但尚未大規模驗證。
  • FC (Fibre Channel) protocol is implemented but has not been fully verified in production. FC (Fibre Channel) 協定已實作但尚未在正式環境完整驗證。
  • Always test thoroughly in a non-production environment before deployment. 部署前請務必在非正式環境中徹底測試。
  • Back up your data regularly and have a recovery plan in place. 請定期備份資料,並準備好復原計畫。

Features 功能特性

Direct integration with Pure Storage FlashArray, leveraging native array capabilities through the REST API. 直接整合 Pure Storage FlashArray,透過 REST API 充分運用原生陣列功能。

1 VM Disk = 1 Pure Volume 1 VM Disk = 1 Pure Volume
Direct mapping with no intermediate layer. Each disk is an independent Pure Storage volume. 直接對應、無中間層。每個磁碟都是獨立的 Pure Storage volume。
Snapshot Create/Delete/Rollback 快照建立/刪除/倒回
Native Pure Storage snapshots. Instant, space-efficient, with host-side buffer flush for crash consistency. 原生 Pure Storage 快照。即時、空間效率佳,並以 host 端 buffer flush 確保 crash 一致性。
Template & Linked Clone 範本與 Linked Clone
Instant clones via Pure Storage volume copy-on-write. Zero data movement, immediately writable. 透過 Pure Storage volume copy-on-write 即時複製。完全沒有資料搬移、立即可寫。
Full Clone from Snapshot 從快照完整複製
Clone any VM from a specific snapshot to a fully independent VM, with automatic temp clone cleanup. 從特定快照複製任何 VM 為完全獨立的 VM,含自動暫時 clone 清理。
Real-time Capacity Reporting 即時容量報告
Storage usage reported directly from Pure Storage REST API, including data reduction ratio. 儲存使用量直接從 Pure Storage REST API 回報,含 data reduction 比率。
Multipath I/O for HA Multipath I/O 高可用性
Automatic multipath configuration with Pure-friendly defaults: queue-length, ALUA, fast failover. 自動 multipath 設定,採 Pure 推薦預設:queue-length、ALUA、快速容錯切換。
Cluster-aware Live Migration 叢集感知即時遷移
Volumes connected to all node hosts on the array for seamless live migration between Proxmox VE nodes. Volume 連接至陣列上所有節點 host,實現 Proxmox VE 節點間的無縫即時遷移。
ActiveCluster Pod Support ActiveCluster Pod 支援
Stretched-cluster volumes via Pure Storage ActiveCluster pods. Synchronous replication across two arrays. 透過 Pure Storage ActiveCluster pod 提供延伸叢集 volume,跨兩座陣列同步複寫。
Always-on Dedup & Compression 永遠啟用的去重與壓縮
Pure Storage applies inline data reduction transparently. No tuning required, no thin/thick choice. Pure Storage 自動套用 inline data reduction,無需調整,沒有 thin/thick 選擇。
iSCSI and FC SAN Support iSCSI 及 FC SAN 支援
Choose transport protocol per storage. Both share the same multipath and WWID identification (Pure prefix 3624a9370). 可依儲存選擇傳輸協定。兩者共用相同的 multipath 與 WWID 識別(Pure 前綴 3624a9370)。
LXC Container Support LXC 容器支援
Container rootfs on Pure Storage volume. Configure with --content images,rootdir. 容器 rootfs 可放在 Pure Storage volume 上。以 --content images,rootdir 設定。
EFI, Cloud-init, TPM, vmstate EFI、Cloud-init、TPM、vmstate
Full support for EFI disk, Cloud-init ISO, TPM 2.0 state, and VM RAM snapshot (vmstate). 完整支援 EFI 磁碟、Cloud-init ISO、TPM 2.0 state、以及 VM 記憶體快照 (vmstate)。

Requirements 系統需求

Proxmox VE

PVE VersionStorage API Compatibility 相容性
PVE 9.1+13 Supported 支援

Pure Storage FlashArray

  • Purity//FA 6.x or later (REST API 1.16+ or 2.0+ supported) Purity//FA 6.x 或更新版本(支援 REST API 1.16+ 或 2.0+)
  • REST API enabled (default on FlashArray) 已啟用 REST API(FlashArray 預設啟用)
  • Network connectivity from Proxmox VE management network to the array management IP (HTTPS/443) Proxmox VE 管理網路可連線至陣列管理 IP(HTTPS/443)
  • iSCSI or FC ports configured and reachable from Proxmox VE nodes 已設定 iSCSI 或 FC port,且 Proxmox VE 節點可連線
  • User account with API token, or username + password with permissions for volume, host, snapshot, and pod operations 有 API token 或 帳號密碼的使用者,需有 volume、host、snapshot、pod 操作權限
  • For ActiveCluster: a configured pod with the source array as a member 使用 ActiveCluster 時:已設定的 pod,且來源陣列為成員

Proxmox VE Node Dependencies Proxmox VE 節點相依套件

Package 套件 Purpose 用途 Required 必要
open-iscsi iSCSI initiator (iscsiadm) iSCSI initiator (iscsiadm) Yes (for iSCSI) 是(iSCSI 模式)
multipath-tools Multipath I/O daemon (multipathd) Multipath I/O 背景服務 (multipathd) Yes 是
sg3-utils SCSI utilities (sg_inq) SCSI 工具 (sg_inq) Yes 是
psmisc Process utilities (fuser) for device-in-use detection 行程工具 (fuser),偵測裝置使用中 Yes 是
libwww-perl HTTP client for REST API HTTP 客戶端,供 REST API 使用 Yes 是
libjson-perl JSON encoding/decoding JSON 編碼/解碼 Yes 是
liburi-perl URI handling URI 處理 Yes 是
lsscsi List SCSI devices (troubleshooting) 列出 SCSI 裝置(疑難排解用) Recommended 建議

Installation 安裝

First-Time Installation 首次安裝

Important: Install dependencies BEFORE installing the plugin package to avoid dependency resolution issues. 重要:請在安裝 plugin 套件前先安裝相依套件,以避免相依性解析問題。
1
Update apt cache 更新 apt 快取
apt update
2
Install ALL dependencies 安裝所有相依套件
apt install -y open-iscsi multipath-tools sg3-utils psmisc \
    libwww-perl libjson-perl liburi-perl lsscsi
3
Enable required services 啟用必要服務
systemctl enable --now iscsid
systemctl enable --now multipathd
4
Install the plugin package 安裝 plugin 套件
dpkg -i jt-pve-storage-purestorage_1.1.37-1_all.deb

The plugin automatically writes /etc/multipath/conf.d/pure-storage.conf with Pure-recommended settings, detects dangerous existing multipath.conf settings, and reloads Proxmox VE services (pvedaemon, pvestatd, pveproxy) via SIGHUP. Plugin 會自動寫入 /etc/multipath/conf.d/pure-storage.conf(採 Pure 推薦設定)、偵測 multipath.conf 危險設定,並透過 SIGHUP 重新載入 Proxmox VE 服務(pvedaemon、pvestatd、pveproxy)。

Fix Broken State 修復安裝失敗狀態

If you ran dpkg -i before installing dependencies and got errors: 如果在安裝相依套件前就執行 dpkg -i 而出現錯誤:

apt update
apt --fix-broken install -y
dpkg -l | grep jt-pve-storage-purestorage

Cluster Installation 叢集安裝

CRITICAL: In a Proxmox VE cluster, this plugin MUST be installed on ALL nodes. Nodes without the plugin will show Parameter verification failed. (400). 重要:在 Proxmox VE 叢集中,此 plugin 必須在所有節點上安裝。未安裝的節點會顯示 Parameter verification failed. (400)。

Repeat the 4 steps above on EACH node. Install the plugin on ALL nodes first, then add the storage configuration (only once, on any node). 在每個節點上重複上述 4 個步驟。先在所有節點安裝 plugin,再新增儲存設定(只需在任一節點操作一次)。

Upgrade SOP 升級標準作業程序

When upgrading, follow these steps on every cluster node in sequence (one node at a time): 升級時,請依序在每個叢集節點上執行以下步驟(一次一個節點):

Pre-1.1.1 to 1.1.x note: If you are upgrading from a 1.0.x or pre-1.1.1 release, your /etc/multipath/conf.d/pure-storage.conf does NOT carry a version marker and will NOT be auto-upgraded. You must align it manually with the new device block (or delete it so the next pvesm status recreates it). See the README "READ FIRST" section. 從 1.1.1 之前升級的注意事項:從 1.0.x 或 1.1.1 之前版本升級時,您的 /etc/multipath/conf.d/pure-storage.conf 沒有版本標記、不會自動升級。請手動對齊新的 device block,或刪除該檔讓下次 pvesm status 重新建立。詳見 README「READ FIRST」段落。
1
Pre-upgrade backup 升級前備份
# Backup multipath configs
cp /etc/multipath.conf /etc/multipath.conf.bak.$(date +%Y%m%d-%H%M%S)
cp /etc/multipath/conf.d/pure-storage.conf \
    /etc/multipath/conf.d/pure-storage.conf.bak.$(date +%Y%m%d-%H%M%S) 2>/dev/null
# Note current version
dpkg -l jt-pve-storage-purestorage | tail -1
2
Stop or migrate VMs (recommended) 停止或遷移 VM(建議)

For safest upgrade, migrate or stop VMs that have disks on this storage. Live VMs will continue to work during upgrade, but a clean state simplifies recovery if anything goes wrong. 最安全的做法是先將使用此儲存的 VM 遷移或停止。升級期間執行中的 VM 仍可繼續運作,但乾淨的狀態有助於出問題時的復原。

3
Install new package 安裝新套件
dpkg -i jt-pve-storage-purestorage_1.1.37-1_all.deb
4
Review postinst warnings and verify 檢視 postinst 警告並驗證
# If postinst warned about dangerous multipath.conf settings, edit:
nano /etc/multipath.conf
# Apply: no_path_retry queue -> 30, remove queue_if_no_path, dev_loss_tmo infinity -> 60
systemctl restart multipathd  # restart, NOT reload

# Verify
dpkg -l jt-pve-storage-purestorage | grep ii
pvesm status | grep purestorage
multipath -ll
5
Repeat on next node 在下一個節點重複

Move to the next cluster node and repeat from Step 1. Do not upgrade multiple nodes simultaneously. 移至下一個叢集節點,從步驟 1 重複。請勿同時升級多個節點。

Quick Start 快速入門

Add Storage (iSCSI, API token) 新增儲存(iSCSI、API token)

pvesm add purestorage pure1 \
    --pure-portal 192.168.1.100 \
    --pure-api-token 'xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx' \
    --pure-protocol iscsi \
    --content images,rootdir \
    --shared 1

Add Storage (FC / Fibre Channel) 新增儲存(FC / Fibre Channel)

pvesm add purestorage pure-fc \
    --pure-portal 192.168.1.100 \
    --pure-api-token 'xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx' \
    --pure-protocol fc \
    --content images \
    --shared 1

Add Storage (ActiveCluster Pod) 新增儲存(ActiveCluster Pod)

pvesm add purestorage pure-stretched \
    --pure-portal 192.168.1.100 \
    --pure-api-token 'xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx' \
    --pure-protocol iscsi \
    --pure-pod prod-pod \
    --content images,rootdir \
    --shared 1

Verify 驗證

pvesm status
# Name      Type         Status  Total   Used  Available
# pure1     purestorage  active  ...     ...   ...
Note: This is a custom/third-party storage plugin. It is NOT listed in the Web UI "Add Storage" dropdown -- storage must be added via CLI (pvesm add). After adding, the storage will appear in the Web UI storage list and support all VM operations normally. 注意:這是第三方自訂儲存 plugin,不會出現在 Web UI 的「新增儲存」下拉選單中 -- 必須透過 CLI (pvesm add) 新增。新增後,儲存會出現在 Web UI 的儲存清單中,並正常支援所有 VM 操作。

Configuration Reference 設定選項參考

Required Options 必要選項

Use the array's virtual management IP. A FlashArray has three management addresses: one per controller, plus a virtual IP (vir0) bound to whichever controller currently holds the management primary role. pure-portal must point at the virtual IP. Pointed at ct0.eth0 or ct1.eth0, the plugin loses the REST API the moment that controller fails over and the storage goes inactive — running guests keep running, but every operation that needs the array's API stops. This is the same requirement Pure places on its other integrations: the OpenStack Cinder driver documentation states that "the Management VIP address is required to properly configure the FlashArray driver", and the vSphere Plugin refuses to install with a "No virtual IP configured" error. Check with purenetwork list on the array. 請使用陣列的虛擬管理 IP。FlashArray 有三個管理位址:兩顆控制器各一個,加上一個綁定在當下管理主控控制器上的虛擬 IP(vir0)。pure-portal 必須指向虛擬 IP。若指向 ct0.eth0 或 ct1.eth0,該控制器一旦 failover,外掛立刻失去 REST API,storage 轉為 inactive——執行中的 guest 照常運作,但所有需要陣列 API 的操作都會停止。這與 Pure 對自家其他整合的要求一致:OpenStack Cinder 驅動文件寫明「the Management VIP address is required to properly configure the FlashArray driver」,vSphere Plugin 則會以「No virtual IP configured」錯誤拒絕安裝。可用陣列上的 purenetwork list 確認。
Option 選項 Description 說明 Example 範例
pure-portal The array's virtual management IP (vir0), or a hostname resolving to it — not a controller's own IP (ct0.eth0 / ct1.eth0). Fixed property: it cannot be changed with pvesm set. 陣列的虛擬管理 IP(vir0),或指向它的主機名稱——不是單一控制器自己的 IP(ct0.eth0/ct1.eth0)。此為 fixed 參數,無法用 pvesm set 修改。 192.168.1.100
pure-api-token
OR或
API token (preferred). One of token or username/password is required. API token(建議優先使用)。token 或 帳密擇一即可。 xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx
pure-username +
pure-password
Username and password (alternative to API token) 帳號密碼(API token 的替代方案) pureuser / secret

Optional Options 可選選項

Option 選項 Default 預設值 Description 說明
pure-protocoliscsi SAN protocol: iscsi or fc (Fibre Channel) SAN 協定:iscsi 或 fc (Fibre Channel)
pure-ssl-verify0 Verify SSL certificates (0 = disable for self-signed) 驗證 SSL 憑證(0 = 停用,適用自簽憑證)
pure-host-modeper-node Host mode: per-node (recommended) or shared Host 模式:per-node(建議)或 shared
pure-cluster-namepve Cluster name for host naming. Use different values for multiple storages on the same array. Host 命名用的叢集名稱。同一陣列上有多個儲存時請使用不同值。
pure-pod (none) (無) Pod name. Setting this scopes the plugin to the pod: capacity reporting follows the pod's quota_limit, and Volume names are namespaced as pod::pve-.... See Capacity and Permission Isolation. Pod 名稱。設定後外掛會綁定該 pod:容量回報以 pod 的 quota_limit 為準,Volume 名稱進入 pod::pve-... 命名空間。請見容量與權限隔離。
pure-device-timeout60 Device discovery timeout in seconds 裝置探索逾時秒數
pure-portal-probe-timeout2 TCP pre-check timeout per iSCSI portal before iscsiadm discovery/login. Skips unreachable portals fast instead of stalling 30s+60s each. Set to 0 to disable. Raise on high-latency storage networks. iscsiadm 之前對每個 iSCSI portal 的 TCP 預探測逾時秒數。可快速跳過無法連通的 portal,避免每個各吃 30 秒(discovery)+ 60 秒(login)的逾時。設為 0 停用。儲存網路延遲偏高時可調大。

Proxmox VE Standard Storage Options Proxmox VE 標準儲存選項

These are standard Proxmox VE storage options that apply to all storage types, including this plugin. 這些是 Proxmox VE 標準儲存選項,適用於所有儲存類型,包含本外掛程式。

Option 選項 Default 預設值 Description 說明
contentimages Content types this storage can hold. Use images for VM disks only, or images,rootdir to also support LXC containers. 此儲存可存放的內容類型。使用 images 僅支援 VM 磁碟,或使用 images,rootdir 同時支援 LXC 容器。
shared0 Mark storage as shared across cluster nodes. Must set to 1 for this plugin -- required for live migration. 標記儲存為叢集節點間共享。本外掛必須設為 1 -- 即時遷移所需。
nodes (all) (全部) Restrict storage to specific nodes. Comma-separated list. Example: --nodes pve1,pve2,pve3. 限制儲存僅供特定節點使用。逗號分隔。範例:--nodes pve1,pve2,pve3。
disable0 Disable this storage. Set to 1 to temporarily deactivate without removing the configuration. 停用此儲存。設為 1 可暫時停用而不刪除設定。

Capacity and Permission Isolation: Pod and Realm 容量與權限隔離:Pod 與 Realm

The plugin connects to the array via REST API and creates Volumes on demand. By default it uses the entire array's capacity and an array-wide API token. To bound what the plugin can touch -- both for capacity caps and for API blast-radius isolation -- Pure offers two mechanisms the plugin honours. 外掛透過 REST API 連接陣列,並依需求動態建立 Volume。預設情況下,外掛使用整台陣列的容量,且 API Token 為陣列層級權限。若要限制外掛可動用的範圍——無論是出於容量上限或 API 影響範圍隔離考量——Pure 提供兩種外掛能識別的機制。

Volume names always use the pve-<storeid>- prefix, so existing array Volumes that do NOT match this prefix are never read or written by the plugin even without any of the isolation options below. Volume 名稱永遠採用 pve-<storeid>- 前綴,因此既有陣列上不符合此前綴的 Volume,即使未啟用以下任何隔離選項,也不會被外掛讀取或寫入。

Option A -- Pod with quota (any Purity version) 方案 A——Pod 加配額(任何 Purity 版本)

A Pod is a namespace on the array. Set a quota_limit on the Pod and point the plugin at it; the plugin then reports and enforces the Pod's quota instead of the array total. Volume names get a pod:: prefix so they cannot collide with anything outside the Pod. Pod 是陣列上的命名空間。在 Pod 設定 quota_limit 並讓外掛指向此 Pod,外掛便會以 Pod 配額取代陣列總容量進行回報與管理。Volume 名稱會自動加上 pod:: 前綴,與 Pod 外的物件不會撞名。

On Pure: Storage > Pods > Create (e.g. pve-pod), then edit the Pod and set Quota Limit (e.g. 2T). Pure 端:Storage > Pods > Create(名稱例如 pve-pod),編輯該 Pod 並設定 Quota Limit(例如 2T)。

On Proxmox VE: Proxmox VE 端:

pvesm add purestorage pure1 \
    --pure-portal 192.168.1.100 \
    --pure-api-token 'xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx' \
    --pure-protocol iscsi \
    --pure-pod pve-pod \
    --content images,rootdir \
    --shared 1

Single-array (local) Pods do not require an ActiveCluster license; ActiveCluster is only needed for stretched (cross-array) Pods. 單陣列 local Pod 不需要 ActiveCluster 授權;ActiveCluster 僅用於跨陣列的 stretched Pod。

Option B -- Realm with quota (Purity 6.6+) 方案 B——Realm 加配額(Purity 6.6+)

A Realm is a tenant-level isolation boundary on the array. Volumes, snapshots, hosts, and the API capacity view are all scoped to the realm. An API token created for a user inside the realm has no visibility into anything outside. Realm 是陣列上的租戶層級隔離邊界。Volume、Snapshot、Host、甚至 API 看到的容量視圖都被限定在 realm 內。在 realm 內建立的使用者所產生的 API Token,完全看不到 realm 之外的任何東西。

On Pure: Pure 端:

  1. Settings > Access > Realms > Create -- name e.g. pve-realm, set Quota Limit (e.g. 2T) Settings > Access > Realms > Create——名稱例如 pve-realm,設定 Quota Limit(例如 2T)
  2. Settings > Access > Users > Create -- username pve-plugin, realm pve-realm, role storage_admin Settings > Access > Users > Create——使用者 pve-plugin、Realm pve-realm、Role storage_admin
  3. Open pve-plugin > API Tokens > Create 點 pve-plugin > API Tokens > Create

On Proxmox VE: just use the realm-scoped token. No plugin option to set -- the scoping is enforced by the token itself. Proxmox VE 端:直接使用此 realm-scoped Token,外掛沒有任何選項要設——範圍由 Token 本身強制執行。

pvesm add purestorage pure1 \
    --pure-portal 192.168.1.100 \
    --pure-api-token '<realm-scoped-token>' \
    --pure-protocol iscsi \
    --content images,rootdir \
    --shared 1

Choosing between them 兩者比較

  比較項目 Pod Realm
Capacity limit 容量限制 Yes (quota_limit on Pod) 有(Pod 上的 quota_limit) Yes (quota_limit on Realm) 有(Realm 上的 quota_limit)
Naming isolation 命名隔離 Yes (pod:: prefix) 有(pod:: 前綴) Yes (cross-realm names independent) 有(不同 realm 之間名稱獨立)
Permission isolation 權限隔離 No -- token still sees other Pods 無——Token 仍看得到其他 Pod Yes -- token sees only its realm 有——Token 只看得到自己的 realm
Plugin option needed 外掛需設定的選項 --pure-pod <name> None (token enforces) 無(Token 本身就限定了)
Volume name format Volume 名稱格式 pve-pod::pve-... pve-...
Purity version Purity 版本需求 Any 任意版本 6.6+ 6.6+

Realm is the cleaner choice when available -- it isolates capacity AND permissions in a single mechanism. On older Purity, Pod is the only built-in mechanism the plugin recognises for capacity reporting; the Pod's quota_limit still gives you a hard capacity cap that the plugin honours, even though API tokens remain array-wide. 若版本支援,Realm 是比較乾淨的選擇——它在單一機制中同時隔離容量與權限。在較舊的 Purity 上,Pod 是唯一外掛能識別的容量機制;Pod 的 quota_limit 仍會給你硬上限,雖然 API Token 本身仍是陣列層級的權限。

Example storage.cfg (iSCSI, API token) 範例 storage.cfg (iSCSI、API token)

purestorage: pure1 pure-portal 192.168.1.100 pure-api-token xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx pure-protocol iscsi pure-host-mode per-node pure-cluster-name pve content images,rootdir shared 1

Example storage.cfg (FC SAN) 範例 storage.cfg (FC SAN)

purestorage: pure-fc pure-portal 192.168.1.100 pure-api-token xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx pure-protocol fc pure-host-mode per-node pure-cluster-name pve content images,rootdir shared 1

Example storage.cfg (ActiveCluster Pod) 範例 storage.cfg (ActiveCluster Pod)

purestorage: pure-stretched pure-portal 192.168.1.100 pure-api-token xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx pure-protocol iscsi pure-pod prod-pod pure-host-mode per-node content images,rootdir shared 1
Note: For FC, pure-portal is still required for Pure REST API access. The FC data path uses the FC fabric, not the management IP. 注意:FC 模式下仍需 pure-portal 用於 Pure REST API 存取。FC 資料路徑使用 FC fabric,不是管理 IP。

Architecture 系統架構

1:1 Architecture Model 1:1 架構模型

This plugin uses direct volume provisioning: every VM disk is allocated as its own dedicated Pure Storage volume, sized exactly to the requested capacity. There is no pre-allocated "big LUN" or storage pool that gets carved into smaller pieces on the host side, and no LVM-thin or FlexVol-style intermediate container. When Proxmox VE asks for a 50 GiB disk, the plugin calls volume_create on the array with size = 50 GiB. When Proxmox VE asks for a snapshot, the plugin calls snapshot_create on that volume. The semantics map 1:1, so Pure's data services (deduplication, compression, snapshot, ActiveCluster, async replication) all apply at the natural unit of one VM disk. 本外掛採 direct volume provisioning(直接 Volume 供應):每一顆 VM 磁碟在 Pure FlashArray 上對應一個獨立的 Volume,大小即為當下要求的容量。陣列上沒有任何「預先建立的大 LUN」或儲存集區在主機端被切片,也沒有 LVM-thin 或 FlexVol 那種中間容器層。當 Proxmox VE 要求 50 GiB 磁碟時,外掛對陣列呼叫 volume_create 建立一顆 50 GiB 的 Volume;要求快照時,外掛對該 Volume 呼叫 snapshot_create。語意 1:1 對齊,Pure 的資料服務(重複資料刪除、壓縮、快照、ActiveCluster、非同步複寫)全部以「一顆 VM 磁碟」為自然單位作用。

Proxmox VE Cluster Pure Storage FlashArray +------------------+ +-------------------+ | PVE Node 1 | | FlashArray | | +----------+ | iSCSI/FC | +-------------+ | | | VM 100 |<--+----------------->| | Volume | | | | scsi0 | | multipath | | pve-...-100 | | | +----------+ | | | -disk0 | | +------------------+ | +-------------+ | | | | | live migration | Hosts: | v | - pve-pve-pve1 | +------------------+ | - pve-pve-pve2 | | PVE Node 2 | | | | +----------+ | iSCSI/FC | (Optional) | | | VM 100 |<--+----------------->| Pod: prod-pod | | | scsi0 | | multipath | for stretched | | +----------+ | | ActiveCluster | +------------------+ +-------------------+

Direct Volume Provisioning vs Pool-Based SAN 直接 Volume 供應 vs 儲存集區式 SAN

The pool-based model puts an LVM (or LVM-thin) layer between the array and Proxmox VE. Snapshots, clones, and replication happen at the LVM layer — the array sees a single giant LUN that is permanently "in use", and its data services cannot reason about individual VM disks. This plugin removes that intermediate layer entirely. 儲存集區式模型在陣列與 Proxmox VE 之間插入一層 LVM(或 LVM-thin)。快照、複製、複寫都發生在 LVM 層 — 陣列只看到一顆永遠「使用中」的大 LUN,無法針對單一 VM 磁碟做 dedup ratio、replication policy、snapshot retention。本外掛完全移除這層中介。

Traditional pool-based SAN model ================================= +----------------------------+ | 1x big LUN on array | admin pre-allocates one | (e.g. 10 TB) | giant LUN +----------------------------+ | v +----------------------------+ | LVM / LVM-thin on host | | +------+ +------+ +-----+ | | | LV | | LV | | ... | | each VM disk is an LV | | vm | | vm | | | | inside the host's LVM | | -100 | | -100 | | | | | | -d0 | | -d1 | | | | | +------+ +------+ +-----+ | +----------------------------+ Snapshots / clones / replication happen at the LVM layer. Array sees ONE LUN that is always "in use". This plugin (direct volume provisioning) ======================================== +-----------------------------+ | Pure FlashArray | | | | Volume pve-...-100-d0 | each VM disk is its own | Volume pve-...-100-d1 | volume on the array, sized | Volume pve-...-101-d0 | exactly to the request | Volume pve-...-102-d0 | | ... | +-----------------------------+ No host-side LVM. Every volume has its own WWID, snapshot chain, and host connections. Snapshots / clones / replication happen on the array, natively.

Layer Stack 層級堆疊

How a single Proxmox VE volname becomes a usable block device on the host: 一個 Proxmox VE volname 如何在主機端變成可用的區塊裝置:

Proxmox VE level vm-100-disk-0 (PVE volname) | v Plugin encoding pve-pure1-100-disk0 (Naming.pm) | v Pure FlashArray volume 50 GiB, WWID 624a9370... (REST API) | v Host connections connected to every (per-node mode) "pve-{cluster}-*" host | v SCSI / FC transport iSCSI sessions OR (multiple paths FC fabric per host) | v Linux SCSI paths /dev/sdc /dev/sdd ... (one per portal / fabric link) | v multipath aggregation /dev/mapper/3624a9370... (DM-multipath, = /dev/dm-N ALUA, queue-length 0) | v QEMU block device passed to qemu (raw, no FS layer between)

What This Means in Practice 對使用者的意義

  • Snapshots are array-native — volume_snapshot calls snapshot_create on Pure; rollback uses volume_overwrite. No LVM copy-on-write cost. 快照即陣列原生 — volume_snapshot 呼叫 Pure 的 snapshot_create;倒回使用 volume_overwrite。沒有 LVM copy-on-write 成本。
  • Linked clones use array clone — instant; the new volume shares blocks with the source until written. Proxmox VE's "Full Clone" is a Proxmox VE design choice (block-by-block copy via qemu-img), not a plugin limit. 連結複製即陣列複製 — 瞬間完成;新 volume 與來源共享資料區塊直到被寫入。Proxmox VE 的「完整複製」是 Proxmox VE 設計選擇(透過 qemu-img 逐區塊複製),不是外掛限制。
  • Online resize is one API call — volume_resize on the array, then per-path SCSI rescan + multipath_resize_map on the host. No filesystem-level resize on a parent pool. 線上擴充僅一次 API 呼叫 — 對陣列下 volume_resize,主機端再進行每路徑 SCSI rescan 與 multipath_resize_map。沒有上層儲存集區的檔案系統擴充步驟。
  • Live migration is implicit — every volume is connected to every node's host (per-node mode), so the same multipath device exists on every cluster node simultaneously. Proxmox VE just changes which node has the device open; no storage migration step is needed. Live Migration 隱含完成 — 在 per-node 模式下,每個 volume 都連到每個節點的 host,相同的 multipath 裝置同時存在於叢集所有節點。Proxmox VE 只是切換哪個節點開啟它,無需儲存遷移步驟。
  • Per-VM array data services — dedup ratio, replication policy, snapshot retention, QoS, and ActiveCluster Pod membership can all be configured per VM disk, because each disk is its own object on the array. 逐 VM 的陣列資料服務 — dedup 比、複寫政策、快照保留、QoS 與 ActiveCluster Pod 成員資格都可以針對單一 VM 磁碟設定,因為每顆磁碟在陣列上都是獨立物件。

Object Mapping 物件對應

Proxmox VE Object Proxmox VE 物件 Pure Storage Object Pure Storage 物件 Naming Pattern 命名規則
Storage-- User defined (e.g., pure1) 使用者定義(例如 pure1)
VM DiskVolumepve-{storage}-{vmid}-disk{id}
SnapshotVolume Snapshot{volume}.pve-snap-{snapname}
Proxmox VE NodeHostpve-{cluster}-{node}
Cloud-initVolumepve-{storage}-{vmid}-cloudinit
VM Config BackupVolume (1MB ext4)pve-{storage}-{vmid}-vmconf-{snapname}
Stretched VolumePod-prefixed Volume{pod}::{volume}

Module Architecture 模組架構

PureStoragePlugin.pm
Main plugin -- Proxmox VE Storage Plugin API implementation. Volumes, snapshots, clones, WWID tracking, cluster orphan cleanup. 主 plugin -- Proxmox VE Storage Plugin API 實作。Volume、快照、複製、WWID 追蹤、叢集殘留清理。
API.pm
Pure Storage REST API client. Dual support for API 1.x (1.16+) and API 2.x (2.0+). Volumes, hosts, snapshots, capacity, pods. Pure Storage REST API 客戶端。雙版本支援 API 1.x (1.16+) 與 API 2.x (2.0+)。Volume、host、快照、容量、pod。
ISCSI.pm
iSCSI management. Target discovery, login, session handling, device wait. All glob calls protected with alarm timeout. iSCSI 管理。Target 探索、登入、session 處理、裝置等待。所有 glob 呼叫都有 alarm timeout 保護。
Multipath.pm
Multipath and SCSI device management. SCSI host scan filtered to /sys/class/iscsi_host/ only (HPE smartpqi safety). kpartx partition handling. Multipath 與 SCSI 裝置管理。SCSI host scan 僅針對 /sys/class/iscsi_host/(HPE smartpqi 安全)。kpartx partition 處理。
FC.pm
Fibre Channel HBA management. WWPN discovery via /sys/class/fc_host/, LIP rescan with sysfs timeout. Fibre Channel HBA 管理。透過 /sys/class/fc_host/ 取得 WWPN,含 sysfs timeout 的 LIP rescan。
Naming.pm
Proxmox VE to Pure Storage naming conventions. Volume names, snapshot names, host names, pod prefixes. Proxmox VE 至 Pure Storage 命名規則。Volume 名稱、快照名稱、host 名稱、pod 前綴。
bin/pve-pure-config-get
CLI tool for retrieving VM configuration backups stored on the array. Useful for disaster recovery scenarios. 用於擷取陣列上 VM 設定備份的 CLI 工具,適用於災難復原情境。

VM Config Backup VM 設定備份

When you create a snapshot, the plugin automatically backs up the VM configuration file to Pure Storage. This means you can recover not only the disk data but also the VM settings as they were at that point in time. 建立快照時,Plugin 會自動將 VM 設定檔備份到 Pure Storage。這讓您不僅可以還原磁碟資料,也可以還原該時間點的 VM 設定。

How It Works 運作方式

  • Automatic — the config is saved every time a snapshot is created. No extra setting required. 自動 — 每次建立快照都會儲存設定,不需任何額外設定。
  • Per-snapshot — each snapshot gets its own independent config backup, so different points in time can have different configs. 獨立於每個快照 — 每個快照都有自己獨立的設定備份,不同時間點可以有不同的設定。
  • Storage format — a 1MB ext4 volume named pve-{storage}-{vmid}-vmconf-{snapname} containing {vmid}.conf and a metadata.txt with timestamp and source info. 儲存格式 — 1MB 的 ext4 volume,命名為 pve-{storage}-{vmid}-vmconf-{snapname},內含 {vmid}.conf 與標記時間戳與來源資訊的 metadata.txt。
  • Hidden from UI — config volumes do not appear in the Proxmox VE disk listing. They are storage-side artifacts only. 不出現在 UI — 設定 volume 不會出現在 Proxmox VE 的磁碟清單中,純粹是儲存端的物件。

Automatic Cleanup 自動清理

  • When a snapshot is deleted, its corresponding config volume is also deleted. 刪除快照時,對應的設定 volume 也會一併刪除。
  • When the last disk of a VM is removed, all its config volumes are deleted along with it. 當 VM 的最後一顆磁碟被刪除時,該 VM 所有的設定 volume 也會被一併刪除。

Retrieving a Config Backup 取出設定備份

The package ships with a CLI tool pve-pure-config-get for retrieving config backups. 套件附帶 CLI 工具 pve-pure-config-get,用於取出設定備份。

pve-pure-config-get -s <storage> -v <vmid> [-n <snap>] [-o <output_dir>] [-l] [-r]
Option 選項 Description 說明
-s, --storage <name> Pure Storage storage ID (required) Pure Storage 的 storage ID(必要)
-v, --vmid <id> VM ID to retrieve config for (required) 要取出設定的 VM ID(必要)
-n, --snap <name> Specific snapshot to retrieve (skips interactive selection) 指定要取出的快照名稱(跳過互動選擇)
-o, --output <dir> Output directory (default: /tmp) 輸出目錄(預設:/tmp)
-l, --list List available snapshots, do not retrieve 列出可用的快照,但不取出
-r, --restore Disaster recovery mode (see below) 災難復原模式(見下方)
-h, --help Show help message 顯示說明訊息

Examples 範例

# List available config backups for VM 100
pve-pure-config-get -s pure1 -v 100 -l

# Interactive retrieval (will prompt for snapshot selection)
pve-pure-config-get -s pure1 -v 100

# Retrieve a specific snapshot's config directly
pve-pure-config-get -s pure1 -v 100 -n snap1

# Retrieve to a custom directory
pve-pure-config-get -s pure1 -v 100 -n snap1 -o /root/configs

Output files are written as: 輸出檔案為:

/tmp/vm-{vmid}-{snapname}-{vmid}.conf      # VM config
/tmp/vm-{vmid}-{snapname}-metadata.txt     # Backup metadata

To restore the config back into Proxmox VE, copy the .conf file to /etc/pve/qemu-server/{vmid}.conf (or /etc/pve/lxc/{vmid}.conf for LXC). 要將設定還原到 Proxmox VE,將 .conf 檔複製到 /etc/pve/qemu-server/{vmid}.conf(LXC 則是 /etc/pve/lxc/{vmid}.conf)。

Disaster Recovery (-r mode) 災難復原(-r 模式)

The --restore flag enables full VM recovery from destroyed volumes that have not yet been eradicated by Pure Storage. This is the lifeline when a VM has been accidentally deleted: while Pure's destroyed-volume window is still open (default 24 hours), both the config backup and the disk volumes can be brought back. --restore 旗標啟用從 已刪除但尚未 eradicate 的 volume 完整復原 VM。當 VM 被誤刪、但 Pure 的 destroyed-volume 視窗(預設 24 小時)還沒關閉時,這就是救命稻草:設定備份與磁碟 volume 都可以救回。

  • Searches both active and destroyed volumes; shows status as [active] or [DESTROYED]. 同時搜尋 active 與 destroyed 的 volume,狀態以 [active] 或 [DESTROYED] 顯示。
  • Automatically recovers destroyed config and disk volumes from Pure Storage. 自動從 Pure Storage 救回已刪除的設定與磁碟 volume。
  • Places the config file directly into the correct Proxmox VE location (/etc/pve/qemu-server/ or /etc/pve/lxc/). 直接將設定檔放置到 Proxmox VE 正確位置(/etc/pve/qemu-server/ 或 /etc/pve/lxc/)。
  • Connects the recovered disk volumes to this host so the VM can start. 將救回的磁碟 volume 重新連到此主機,VM 即可啟動。
  • Safety check: refuses to overwrite an existing VM config (use a different VM ID or delete the existing one first). 安全檢查:拒絕覆寫既有的 VM 設定(請改用其他 VM ID,或先刪除既有設定)。
# List available backups including destroyed volumes
pve-pure-config-get -s pure1 -v 100 -r -l

# Full VM restore from destroyed volumes
pve-pure-config-get -s pure1 -v 100 -n snap1 -r
Note: Pure Storage's default destroyed-volume retention is 24 hours before automatic eradication. After eradication the data cannot be recovered by this tool. Adjust the array's eradication timer if you need a longer recovery window. 注意:Pure Storage 預設保留 destroyed volume 24 小時後自動 eradicate;eradicate 之後此工具無法救回。如需更長的救援視窗,請調整陣列上的 eradication 時間。

Multipath Safety Rules Multipath 安全規則

READ THIS BEFORE INSTALLING. These rules prevent Proxmox VE node hangs and accidental disconnection of other storage. 安裝前請務必閱讀。這些規則防止 Proxmox VE 節點當機及意外斷開其他儲存。

Rule 1: NEVER use multipath -F (capital F) 規則 1:絕對不要使用 multipath -F(大寫 F)

multipath -F flushes ALL unused multipath maps system-wide. If you have other storage (manual iSCSI LVM, other vendors, etc.) and there is no active I/O on it at the moment, it will be disconnected. The plugin's multipath_flush() helper refuses to run without a specific device argument. Always use targeted flushing: multipath -F 會清除系統上所有未使用的 multipath map。如果您有其他儲存(手動 iSCSI LVM、其他廠商等)且當時沒有 active I/O,該儲存會被斷開。Plugin 的 multipath_flush() 沒有指定裝置時會拒絕執行。請使用指定目標的清除:

# Identify stale WWIDs (look for "failed faulty" in all paths)
multipath -ll

# Flush ONE specific stale WWID (lowercase f)
multipath -f 3624a93701234567890abcdef0123456

Rule 2: After editing multipath.conf, use restart not reload 規則 2:編輯 multipath.conf 後,使用 restart 而非 reload

# CORRECT - applies new settings AND flushes stale state
systemctl restart multipathd

# WRONG - only re-reads config, leaves stale maps in place
systemctl reload multipathd

Rule 3: Check your multipath.conf settings 規則 3:檢查您的 multipath.conf 設定

If your config contains any of these, the entire Proxmox VE node can hang when a Pure volume is deleted or becomes unavailable: 如果您的設定包含以下任何一項,當 Pure volume 被刪除或變得不可用時,整個 Proxmox VE 節點可能會當機:

Setting 設定 Risk 風險 Fix 修正
no_path_retry queue I/O queues forever I/O 永久排隊 Change to no_path_retry 30 改為 no_path_retry 30
queue_if_no_path Same as above 同上 Remove from features line 從 features 行移除
dev_loss_tmo infinity Stale devices never removed 殘留裝置永遠不會被移除 Change to dev_loss_tmo 60 改為 dev_loss_tmo 60

Rule 4: lvm.conf global_filter on Proxmox VE 7→9 upgraded nodes 規則 4:從 Proxmox VE 7 升級至 9 的節點需設定 lvm.conf global_filter

Proxmox VE 9.x ships lvm.conf without a global_filter. On nodes upgraded from Proxmox VE 7, the host LVM scanner can auto-activate VGs found INSIDE VM disks (guest OS LVM), which then become "holders" of the multipath device and block volume deletion. The postinst detects this and prints the exact filter to add: Proxmox VE 9.x 出廠的 lvm.conf 沒有 global_filter。從 Proxmox VE 7 升級的節點,主機 LVM 掃描器可能 auto-activate VM 磁碟內部的 VG(客體 OS 的 LVM),這些 VG 會成為 multipath 裝置的 "holder" 並擋住 volume 刪除。Postinst 會偵測並印出要加入的 filter:

# /etc/lvm/lvm.conf
global_filter = [ "r|/dev/mapper/3624a9370.*|", "a|.*|" ]

Rule 5: v1.1.0+ handles cleanup automatically 規則 5:v1.1.0+ 自動處理殘留清理

You do not need to manually clean stale Pure devices after upgrading to v1.1.0+. The plugin tracks every WWID it creates in /var/lib/pve-storage-purestorage/<storeid>-wwids.json, auto-imports cluster-wide WWIDs from the array on every status() poll, and runs background cleanup via a double-fork worker. It only touches WWIDs from the Pure prefix (3624a9370) and never affects other storage. 升級至 v1.1.0+ 後不需要手動清理 Pure 殘留裝置。Plugin 會在 /var/lib/pve-storage-purestorage/<storeid>-wwids.json 追蹤每個建立的 WWID,每次 status() 輪詢時自動從陣列匯入叢集範圍 WWID,並透過 double-fork worker 在背景清理。只會處理 Pure 前綴 (3624a9370) 的 WWID,不會影響其他儲存。

Supported Features Matrix 功能支援表

Feature 功能 Status 狀態 Notes 備註
Disk create/delete 磁碟建立/刪除 Supported 支援 Pure volume; soft delete with eradicate skipped (24h recovery window) Pure volume;軟刪除不立即 eradicate(24 小時可救回)
Disk resize 磁碟調整大小 Supported 支援 Online resize with per-device rescan + multipath_resize_map 支援線上調整,含 per-device rescan 與 multipath_resize_map
Snapshots 快照 Supported 支援 Pure volume snapshots
Snapshot rollback 快照倒回 Supported 支援 VM stopped; flushbufs to invalidate cache VM 須停止;含 flushbufs 清除快取
Live migration 即時遷移 Supported 支援 Volume connected to all node hosts Volume 連接至所有節點 host
Inline dedup & compression 行內去重與壓縮 Always on 永遠啟用 Pure Storage native, transparent Pure Storage 原生、透明
Multipath I/O Supported 支援 Auto-config in /etc/multipath/conf.d/pure-storage.conf 自動設定於 /etc/multipath/conf.d/pure-storage.conf
Template Supported 支援 Convert VM to template 將 VM 轉為範本
Linked Clone Supported 支援 Via Pure copy-on-write (instant, space-efficient) 透過 Pure copy-on-write(即時、節省空間)
Full Clone Supported 支援 Via Proxmox VE built-in copy 透過 Proxmox VE 內建複製
Full Clone from Snapshot 從快照完整複製 Supported 支援 Via temporary Pure clone 透過暫時 Pure clone
Backup (vzdump) Supported 支援 Via snapshot 透過快照
RAM Snapshot (vmstate) Supported 支援 Stored as separate Pure volume 存為獨立 Pure volume
LXC Container (rootdir) Supported 支援 Add rootdir to --content --content 中加入 rootdir
EFI Disk Supported 支援 --
Cloud-init Disk Supported 支援 --
TPM State Supported 支援 --
ActiveCluster Pod Supported 支援 Set --pure-pod to enable stretched volumes 設定 --pure-pod 以啟用延伸 volume
API 1.x & 2.x Supported 支援 Auto-detected at first connection 首次連線時自動偵測

Troubleshooting 疑難排解

Common issues and their solutions. For a comprehensive guide, see docs/TROUBLESHOOTING.md in the repository. 常見問題及解決方案。完整指南請參閱 repository 中的 docs/TROUBLESHOOTING_zh-TW.md。

Quick Diagnostic Commands 快速診斷指令

# Check storage status
pvesm status

# Check Proxmox VE daemon logs
journalctl -xeu pvedaemon --since "10 minutes ago"

# Check iSCSI sessions
iscsiadm -m session

# Check multipath devices
multipathd show maps

# Inspect WWID tracking state
cat /var/lib/pve-storage-purestorage/*-wwids.json

# Check Pure API connectivity
curl -k -H "api-token: YOUR-TOKEN" https://PURE_IP/api/2.21/arrays
Storage Not Active 儲存未啟用

Symptoms: Storage shows "inactive" in pvesm status. Cannot create VMs on storage. 症狀:在 pvesm status 中顯示 "inactive"。無法在該儲存上建立 VM。

Common Causes: 常見原因:

  • Invalid API token or credentials -- test with: curl -k -H "api-token: TOKEN" https://192.168.1.100/api/2.21/arraysAPI token 或帳密無效 -- 測試指令:curl -k -H "api-token: TOKEN" https://192.168.1.100/api/2.21/arrays
  • Network connectivity -- check: ping <pure-portal> and nc -zv <pure-portal> 443網路連線問題 -- 檢查:ping <pure-portal> 及 nc -zv <pure-portal> 443
  • SSL certificate issues -- temporarily disable: pvesm set <storage-id> --pure-ssl-verify 0SSL 憑證問題 -- 暫時停用:pvesm set <storage-id> --pure-ssl-verify 0
  • REST API disabled on the array (rare; default is enabled)陣列上停用了 REST API(少見;預設啟用)
Device Not Found After Create 建立後找不到裝置

Symptoms: Disk created successfully on the array but device not appearing in /dev/. 症狀:磁碟在陣列上建立成功但裝置未出現在 /dev/ 中。

Solutions: 解決方案:

# Rescan iSCSI sessions
iscsiadm -m session --rescan

# Reload multipath
multipathd reconfigure
multipath -v2

# Verify host connection on the array
# Pure UI: Storage > Hosts > pve-{cluster}-{node} > Connected Volumes
Host Connection Issues Host 連線問題

Symptoms: Volume created and mapped but node cannot see the device. iscsiadm -m session shows active sessions but lsscsi shows no PURE devices. 症狀:Volume 已建立並對應但節點看不到裝置。iscsiadm -m session 顯示 active session 但 lsscsi 沒有 PURE 裝置。

Verify the node's iSCSI initiator IQN (from /etc/iscsi/initiatorname.iscsi) is listed under the correct Pure host (pve-{cluster}-{node}). Check on the array UI: Storage > Hosts > select your node > Host Ports. 確認節點的 iSCSI initiator IQN(來自 /etc/iscsi/initiatorname.iscsi)已列在正確的 Pure host (pve-{cluster}-{node}) 下。在陣列 UI 檢查:Storage > Hosts > 選擇您的節點 > Host Ports。

Hung Kernel Tasks (vgs blocked, D-state processes) Kernel 卡住(vgs 阻塞、D-state 行程)

Symptoms: vgs or other commands hang. ps aux shows processes in D state. Proxmox VE operations time out. 症狀:vgs 或其他指令卡住。ps aux 顯示 D state 的行程。Proxmox VE 操作逾時。

Root cause: Usually a stale multipath device with queue_if_no_path or no_path_retry queue. Any process that touches the device enters uninterruptible sleep (D-state). 根本原因:通常是設定了 queue_if_no_path 或 no_path_retry queue 的殘留 multipath 裝置。任何碰觸該裝置的行程都會進入不可中斷睡眠(D-state)。

Manual cleanup for stale devices with queue_if_no_path: 有 queue_if_no_path 的殘留裝置手動清理:

# 1. Disable queueing
multipathd disablequeueing map <wwid>
dmsetup message <wwid> 0 fail_if_no_path

# 2. Flush the specific device
multipath -f <wwid>

# 3. If step 2 fails, force remove
dmsetup remove --force --retry <wwid>
Cannot Delete Volume (LVM holders / "device is still in use") 無法刪除 Volume(LVM holders / 「裝置仍在使用中」)

Symptoms: Cannot delete volume: device is still in use (has holders). Common on Proxmox VE nodes upgraded from 7 to 8 to 9. 症狀:Cannot delete volume: device is still in use (has holders)。常見於從 Proxmox VE 7 升級至 8 再到 9 的節點。

Root cause: The host's LVM scanner auto-activated VGs found INSIDE VM disks (guest OS LVM). This happens when /etc/lvm/lvm.conf has no global_filter to exclude Pure multipath devices. Note: kpartx partition holders are NOT this case (v1.1.7 fixed that separately). 根本原因:主機的 LVM 掃描器自動 activate 了 VM 磁碟裡面的 VG(客體 OS 的 LVM)。這在 /etc/lvm/lvm.conf 沒有 global_filter 排除 Pure multipath 裝置時會發生。注意:kpartx partition holders 不是這個情境(v1.1.7 已另外修復)。

Fix: 修復:

# Deactivate the guest VG on the host
vgchange -an <guest-vg-name>

# Long-term fix: add global_filter to /etc/lvm/lvm.conf
# global_filter = [ "r|/dev/mapper/3624a9370.*|", "a|.*|" ]
Common Error Messages 常見錯誤訊息
Error 錯誤 Cause / Fix 原因 / 修復
unknown storage type 'purestorage' Plugin not loaded. Reinstall and restart pvedaemon. Plugin 未載入。重新安裝並重啟 pvedaemon。
device is still in use (has holders) Either guest LVM auto-activated (see above) or pre-1.1.7 kpartx partition issue (upgrade required). 客體 LVM 自動 activate(見上方),或是 1.1.7 之前的 kpartx 問題(需升級)。
Cannot grow device files Kernel did not see new volume size. Fixed in v1.1.2+ (per-device rescan). Kernel 未偵測到新的 volume 大小。v1.1.2+ 已修復(per-device rescan)。
trying to acquire lock... got timeout D-state child blocking kernel lock. See "Hung Kernel Tasks" above. Upgrade to v1.1.5+. D-state child 佔住 kernel lock。見上方「Kernel 卡住」。請升級至 v1.1.5+。
sysfs write ... timed out Writing to non-iSCSI SCSI host (HPE smartpqi, Dell PERC). Fixed in v1.1.5+ by filtering to /sys/class/iscsi_host/ only. 對非 iSCSI SCSI host 寫入(HPE smartpqi、Dell PERC)。v1.1.5+ 已修復,僅針對 /sys/class/iscsi_host/。
Volume already exists on Pure Storage Disk-id collision (TOCTOU race) -- v1.1.x retries with bounded loop. If persistent, check the array UI for the volume name. Disk-id 碰撞(TOCTOU race)-- v1.1.x 已加入有界重試。若持續發生,請於陣列 UI 檢查該 volume 名稱。

Changelog 變更紀錄

Version history. For full details see CHANGELOG.md. 版本紀錄。完整內容請參閱 CHANGELOG_zh-TW.md。

v1.1.37 Fix
2026-08-15
Four reported defects, and 1.1.36 users should upgrade: the argument guard added in 1.1.35 refused %n %w, the format string this plugin itself passes to multipathd, and the first caller to reach it is the in-use check free_image() depends on — so deleting a volume and migrating a disk both failed. A scan of every literal argument found a second one refused since 1.1.35 and silent. Also: a realm-scoped pod was split on the wrong separator, the host object name carried no realm prefix, and an OVMF efidisk0 (528 KiB) fell below the array's 1 MiB minimum. The behaviour tests now live in t/ and run from make test. 四個回報的缺陷,1.1.36 的使用者請升級:1.1.35 加入的參數守門拒絕了 %n %w——那是本外掛自己傳給 multipathd 的格式字串,而第一個走到它的正是 free_image() 依賴的使用中檢查,因此刪除 Volume 與遷移磁碟都會失敗。掃過全樹所有字面參數後又找到第二個,自 1.1.35 起被拒絕且一直無聲。另外:realm 範圍的 pod 被切錯分隔符、host 物件名稱沒帶 realm 前綴、OVMF 的 efidisk0(528 KiB)低於陣列的 1 MiB 下限。行為測試現在放在 t/ 並由 make test 執行。
v1.1.36 Fix
2026-08-12
Every process left a REST session behind on the array: a session is a resource on the array, not a local object, and nothing here called a logout. pvestatd rebuilds its client whenever the local cache expires, a pvedaemon worker runs one task and exits, and every pvesm, qm or pct invocation is its own process — each logged in once and walked away. Sessions this process created are now released from an END block, stamped with the creating pid so a forked worker does not end its parent's session, and a client seeded with another client's token never releases it. Also stops repeating the full transport error on every poll of a storage already recorded as down. 每一個行程都在陣列上留下一個 REST 工作階段:工作階段是陣列上的資源而非本機物件,而我們從來沒有呼叫過 logout。pvestatd 每當本機快取過期就重建 client、pvedaemon 的 worker 執行完一個工作就結束、每次 pvesm、qm、pct 呼叫都是獨立行程——每一個都登入一次然後走掉。現在本行程建立的工作階段會在 END 區塊釋放,並記下建立它的 pid,讓 fork 出來的 worker 不會結束父行程的工作階段;以其他 client 的 token 建立的用戶端也永遠不會去釋放它。另外,已被記錄為離線的 storage 不再於每次輪詢重複輸出完整的傳輸錯誤。
v1.1.35 Fix
2026-08-09
The web interface runs a different Perl from your shell: pvedaemon and pveproxy are #!/usr/bin/perl -T, and under taint mode a value read from a file, a socket or another command's output cannot reach exec — PVE::Tools::run_command does not untaint. Every device path here comes from a WWID the array returned or from multipathd output, so an operation driven from the web interface could fail where the identical one from pvesm worked. Arguments are now untainted against an allowlist and anything outside it is refused rather than stripped. Also fixes /sbin/udevadm, which does not exist on Debian 13: all four udev refresh calls had been failing silently since they were written. 網頁介面執行的 Perl 與你的 shell 不同:pvedaemon 與 pveproxy 是 #!/usr/bin/perl -T,在 taint 模式下,來自檔案、socket 或其他指令輸出的值無法傳給 exec——而 PVE::Tools::run_command 不會 untaint。本外掛的每一個裝置路徑都來自陣列回傳的 WWID 或 multipathd 的輸出,因此從網頁介面驅動的操作可能失敗,而同一個操作從 pvesm 卻正常。現在參數會以允許清單 untaint,清單以外一律拒絕而非清洗。同時修正 /sbin/udevadm——該路徑在 Debian 13 上不存在,四個 udev 刷新呼叫自寫下以來一直無聲失敗。
v1.1.34 Fix
2026-08-07
Six exported functions with no callers removed, one hiding a bare system(): set_initiator_name() ran system('systemctl', 'restart', 'iscsid') — no timeout, restarting a daemon whose stop phase can block while iSCSI sessions are active. The rule against bare system() was already enforced by a static check, but that check was scoped to bin/ and had never looked at the library. It now covers lib/, and a new check fails when a function is exported without a caller. Documentation rules are also static checks now rather than hand-applied. 移除六個沒有呼叫者的匯出函式,其中一個藏著裸 system():set_initiator_name() 執行 system('systemctl', 'restart', 'iscsid')——沒有逾時,而且在 iSCSI 工作階段仍存活時,該服務的停止階段可能卡住。禁止裸 system() 的規則本來就有靜態檢查把關,但那個檢查只掃 bin/,從來沒有看過函式庫。現在已涵蓋 lib/,並新增「匯出但無呼叫者」的檢查。文件規則也改由靜態檢查涵蓋,不再靠人工。
v1.1.33 Fix
2026-08-07
Snapshot access failed on most storage names: reading from a snapshot creates a clone whose name was the volume name plus 36 characters, and nothing checked the total against the 63 Pure allows. The array rejects an over-long name rather than truncating it, so backing up from a snapshot, qemu-img convert out of one and container backup all failed — with an error that never mentioned the storage id that caused it. The old suffix left room for a storage id of ten characters; purestorage is eleven. The marker is now -tsa-, raising the workable storage id to 23, and the length is asserted where the name is built. Both reapers still recognise the old marker, so existing clones are collected rather than leaked. 多數 storage 命名下的快照存取都會失敗:從快照讀取會建立一個複製,其名稱是 Volume 名稱再加 36 個字元,而沒有任何地方檢查總長是否超過 Pure 允許的 63 字元。陣列對過長名稱是直接拒絕而非截斷,因此從快照備份、對快照執行 qemu-img convert、容器備份全部失敗——而錯誤訊息完全不會提到造成問題的 storage id。舊字尾只留給 storage id 十個字元的空間,而 purestorage 是十一個。標記現在改為 -tsa-,可用長度提高到 23,並且在組出名稱的地方就檢查長度。兩個回收器仍認得舊標記,因此既有的複製會被回收而不會洩漏。
v1.1.32 Fix
2026-08-06
A node could destroy another node's snapshot clone in shared host mode: reading from a snapshot creates a temp clone connected only to the node doing the reading, and the reapers use that to decide ownership. With pure-host-mode = shared every node reports the same Pure host name, so the check answered “it is mine” for a clone created anywhere in the cluster — and the creating node's own in-use check protects only itself. Where ownership cannot be established the reapers now wait 24 hours instead of 60 seconds; per-node mode is unchanged. Also warns when a snapshot-access clone name exceeds the array's 63-character limit, which a storage id longer than about 13 characters causes. shared host 模式下,某節點可能銷毀另一節點的快照複製:從快照讀取會建立暫存複製,且只連線到執行讀取的那個節點,回收器就是靠這一點判斷歸屬。但在 pure-host-mode = shared 之下每個節點回報的 Pure host 名稱都相同,因此對叢集中任何節點建立的複製,檢查的答案都是「是我的」——而建立者自己的使用中檢查只保護得了自己。現在在無法確立歸屬時,回收器改為等待 24 小時而非 60 秒;per-node 模式維持不變。另外,當快照存取用的複製名稱超過陣列的 63 字元上限時會發出警告,storage id 超過約 13 個字元就會發生。
v1.1.31 Fix
2026-08-06
Two storages could own the same Pure volumes: volume names are built from the storage id with characters Pure cannot use removed and - mapped to _, so pure-prod, pure_prod and pure-p.rod all produce the same names. That prefix is the only thing scoping ownership, so two colliding storages on one array shared a namespace — each listed the other's disks, and deleting through one could destroy a volume the other's guests were running on. Adding a colliding storage is now refused; updating one warns. Also warns when two node names, which are truncated to 20 characters, produce the same Pure host object. 兩個 storage 可能擁有同一批 Pure Volume:Volume 名稱由 storage id 推導,過程中會移除 Pure 不接受的字元並把 - 換成 _,因此 pure-prod、pure_prod、pure-p.rod 會產生完全相同的名稱。而這個前綴是唯一界定歸屬的依據,所以同一台陣列上兩個互相碰撞的 storage 會共用一個命名空間——彼此都會列出對方的磁碟,透過其中一個刪除可能銷毀另一個的 guest 正在使用的 Volume。現在新增會碰撞的 storage 會被拒絕,更新則發出警告。另外,當兩個節點名稱(會被截斷成 20 字元)產生相同的 Pure host 物件時也會警告。
v1.1.30 Fix
2026-08-06
Container snapshots are now filesystem-consistent: Proxmox VE cgroup-freezes an LXC container's processes before taking a snapshot, but freezes the filesystem only for storages that ask for it through volume_snapshot_needs_fsfreeze(). This plugin did not implement it, so dirty pages held by the host kernel were not on the array when the array took its copy — running container snapshots, including vzdump backups in snapshot mode, were crash-consistent rather than filesystem-consistent. The pre-snapshot sync was also being skipped whenever the device was in use, which is exactly when there is something to flush. QEMU guests were never affected: their quiescing runs through the guest agent. 容器快照現在具備檔案系統一致性:Proxmox VE 在取得快照前會用 cgroup 凍結 LXC 容器的行程,但只有在 storage 透過 volume_snapshot_needs_fsfreeze() 要求時才會凍結檔案系統。本外掛先前沒有實作,因此主機核心持有的 dirty page 在陣列取得快照時還沒寫到陣列上——執行中容器的快照,包含 snapshot 模式的 vzdump 備份,都只是 crash-consistent 而非檔案系統一致。快照前的 sync 也會在裝置使用中時被跳過,而那正是有東西需要寫出的時候。QEMU guest 從來不受影響:它們的一致性處理由 guest agent 負責。
v1.1.29 Fix
2026-08-06
Storage API version is now negotiated, not hardcoded: APIVER lives in libpve-storage-perl, which versions independently of pve-manager and moved 13 to 14 to 15 within the 9.1 point releases, so no fixed claim is right on every node. Claiming below the running APIVER made Proxmox VE print Plugin ... is implementing an older storage API on every pvesm, qm and pct call; claiming above it would have made older libraries refuse to load the plugin, removing every purestorage storage from the node. The plugin now claims min(APIVER, 15), floored at 9. No functional change — api() is only a load-time gate. Also refuses volume_resize with a snapshot name instead of silently resizing the parent volume. Storage API 版本改為協商,不再寫死:APIVER 位於 libpve-storage-perl,其版本與 pve-manager 各自獨立,且在 9.1 的小版本之間就從 13 變到 14 再到 15,因此沒有任何固定值在每個節點上都正確。宣告值低於執行中的 APIVER 時,Proxmox VE 會在每一次 pvesm、qm、pct 呼叫時印出 Plugin ... is implementing an older storage API;宣告值高於它,較舊的函式庫則會拒絕載入外掛,該節點上所有 purestorage storage 都會消失。現在改為宣告 min(APIVER, 15),下限 9。功能沒有變化——api() 只是載入時的關卡。另外,volume_resize 收到快照名稱時改為明確拒絕,不再默默去調整母 Volume 的大小。
v1.1.28 Fix
2026-08-06
Credential migration: upgrade every node first (MEDIUM): installing the package is safe on its own and needs no coordination — it does not touch storage configuration, so an un-migrated storage keeps authenticating from the value still in storage.cfg on old and new nodes alike. The hazard is the migration command: /etc/pve is replicated so the secret file reaches every node immediately, but a node still running a pre-1.1.25 plugin does not know to read it, and the cleartext copy it was reading has just been removed. The plugin now warns about this from on_update_hook_full() — at the moment the operator takes the risk, not at install time when the decision is not yet in front of them. Both READMEs gained the same ordering requirement. 憑證遷移:請先升級所有節點(中):安裝套件本身是安全的,不需要任何協調——它不會碰 storage 設定,因此尚未遷移的 storage 在新舊節點上都仍從 storage.cfg 裡的值通過認證。風險在於遷移指令:/etc/pve 會同步,機密檔立刻出現在每個節點,但仍在執行 1.1.25 之前版本的節點不知道要去讀它,而它原本在讀的明文副本剛剛被移除了。外掛現在會從 on_update_hook_full() 發出警告——在操作者實際承擔風險的當下,而不是在安裝時那個決定還沒擺在面前的時候。兩份 README 也補上了同樣的順序要求。
v1.1.27 Docs
2026-08-06
`pure-portal` must be the array's virtual management IP (DOC): a FlashArray is assigned three management addresses — one per controller plus a virtual IP (vir0) bound to whichever controller holds the management primary role. Pointed at a controller's own address (ct0.eth0 / ct1.eth0), the plugin loses the REST API the moment that controller fails over and the storage goes inactive after about 30 seconds. Running guests are unaffected — status() failing touches no device, so multipath maps stay mapped and I/O continues — but every operation needing the array's API stops. Same requirement Pure places on its other integrations. Documented in both READMEs with the remove-and-re-add procedure, since pure-portal is a fixed property. No behaviour change. `pure-portal` 必須是陣列的虛擬管理 IP(文件):FlashArray 會配置三個管理位址——兩顆控制器各一個,加上一個綁定在管理主控控制器上的虛擬 IP(vir0)。若指向控制器自己的位址(ct0.eth0/ct1.eth0),該控制器一旦 failover,外掛立刻失去 REST API,storage 約 30 秒後轉為 inactive。執行中的 guest 不受影響——status() 失敗時不碰任何裝置,multipath 對應維持不變、I/O 繼續——但所有需要陣列 API 的操作都會停止。這與 Pure 對自家其他整合的要求一致。已寫入兩份 README 並附移除重建步驟,因為 pure-portal 是 fixed 參數。無行為變更。
v1.1.26 Fix
2026-07-27
Roughly half the steady-state REST calls were avoidable (MEDIUM): counting what one pvestatd poll costs per node per storage found four unnecessary calls. The pod object was fetched twice per poll (get_managed_capacity() and then pod_get_quota_limit() again); every new client re-ran REST version detection, which the forking background reaper triggers constantly; every forked reaper pass logged in again, though a Pure x-auth-token is a bearer token that a new client can be seeded with; and activate_storage() re-fetched the iSCSI port list and re-verified the host object on every poll though neither changes in normal operation. Measured over three consecutive polls with a counting API stub: 15 calls before, 8 after — for a five-node cluster with two Pure storages, from about nine REST calls per second at complete idle to under four. 穩態下約有一半的 REST 呼叫可以省掉(中):實際計算一次 pvestatd 輪詢在每個節點、每個 storage 的成本,找出四個不必要的呼叫。同一顆 pod 每次輪詢被抓兩次(get_managed_capacity() 之後 pod_get_quota_limit() 又抓一次);每個新用戶端都重新偵測 REST 版本,而會 fork 的背景回收讓這件事不斷發生;每次 fork 出來的回收都重新登入一次,儘管 Pure 的 x-auth-token 是可以帶給新用戶端的 bearer token;activate_storage() 每次輪詢都重新取得 iSCSI 埠清單並重新驗證 Host 物件,而兩者在正常運作下都不會變。以附計數器的 API stub 連續三次輪詢實測:修正前 15 次、修正後 8 次——以五節點、兩個 Pure storage 的叢集換算,等於完全閒置時從每秒約九次 REST 降到四次以下。
v1.1.25 Security
2026-07-27
The API token is no longer stored in cleartext (MEDIUM): PVE 9 lets a plugin declare which of its properties are sensitive via plugindata()->{'sensitive-properties'}, so the config API can keep them out of /etc/pve/storage.cfg. We never declared it, so the list fell back to PVE's hardcoded set — which covers neither pure-api-token nor pure-password. Both sat in storage.cfg in cleartext and GET /storage/<id> returned them to anyone holding Datastore.Allocate on that storage, which is not root; a Pure API token is normally array-wide. Credentials now live in /etc/pve/priv/storage/<id>.pure-token (mode 0600, root-only directory), the same place the built-in PBS and CIFS plugins keep theirs. Upgrading needs no action — the plugin falls back to the value still in the config. To remove the cleartext copy, run pvesm set <id> --pure-api-token <token> once, which writes the secret file and drops the old line in one command. Do not use --delete pure-api-token: PVE reports that as an explicit removal, so the secret file goes too. API token 不再以明文儲存(中):PVE 9 允許外掛透過 plugindata()->{'sensitive-properties'} 宣告哪些設定屬於機密,設定 API 便會把它們排除在 /etc/pve/storage.cfg 之外。我們一直沒有宣告,清單因此回退到 PVE 硬編的那一組——兩個都不涵蓋 pure-api-token 與 pure-password。兩者都以明文留在 storage.cfg,而 GET /storage/<id> 會回傳給任何在該 storage 上持有 Datastore.Allocate 的人,那不需要 root;Pure 的 API token 通常是整台陣列的權限。憑證現在存放於 /etc/pve/priv/storage/<id>.pure-token(權限 0600,目錄只有 root 可讀),與內建 PBS、CIFS 外掛相同。升級不需要任何動作——外掛會回退使用仍在設定檔裡的值。若要移除明文副本,執行一次 pvesm set <id> --pure-api-token <token>,該指令會寫入機密檔並移除舊行。請不要用 --delete pure-api-token:PVE 會把它當成明確移除,連機密檔也會一併刪掉。
v1.1.24 Critical
2026-07-27
Linked clones could gain a phantom "unused" disk pointing at the live volume (CRITICAL): clone_image() returns base-102-disk-0/vm-104-disk-0 and the guest config stores that, but list_images() reported the bare vm-104-disk-0. qm rescan compares the two by string, saw the volume as unreferenced and called add_unused_volume() — so the guest gained an unusedN entry pointing at the same Pure volume its scsi0 was running on, and removing that "unused disk" in the GUI destroys the live disk. Now derived from Pure's source field, accepting only a .pve-base snapshot source. Container backups leaked a Pure volume per mountpoint, indefinitely (HIGH): VZDump::LXC creates a snapshot-access clone through our path() and never calls deactivate_volume at all, so nothing released it; the one-hour reaper was the only backstop and it never ran on an API 2.x array before v1.1.22. Cleanup now hooks volume_snapshot_delete(). Resizing a disk did not reach the local device (HIGH): the refresh was gated on $running, so resizing a stopped VM — or resizing on one node and starting the guest on another — left the multipath map at the old size and the guest saw the old disk, silently. $vollist is now matched exactly (MEDIUM) instead of by prefix, which returned vm-10-disk-10 when asked for vm-10-disk-1. Linked clone 可能多出指向使用中 Volume 的幽靈「未使用」磁碟(嚴重):clone_image() 回傳 base-102-disk-0/vm-104-disk-0,guest 設定存的也是這個,但 list_images() 回報的是單純的 vm-104-disk-0。qm rescan 以字串比對兩者,判定該 Volume 未被引用而呼叫 add_unused_volume()——guest 因此多出一個 unusedN,指向它 scsi0 正在使用的同一顆 Pure Volume,在 GUI 移除該「未使用磁碟」就會銷毀使用中的磁碟。現在改由 Pure 的 source 欄位推導,且只接受 .pve-base 快照來源。容器備份每次洩漏一顆 Pure Volume 且無限累積(高):VZDump::LXC 透過我們的 path() 建立快照存取用的複製,卻完全不會呼叫 deactivate_volume,因此無人釋放;唯一後備是一小時的回收器,而它在 API 2.x 陣列上直到 v1.1.22 之前從未執行過。清理改由 volume_snapshot_delete() 觸發。調整磁碟大小不會傳達到本機裝置(高):刷新被 $running 包住,因此在 VM 停機時調整、或在某節點調整而在另一節點啟動,multipath map 都會停留在舊容量,guest 無聲地看到舊磁碟。$vollist 改為精確比對(中),前綴比對在查詢 vm-10-disk-1 時會一併回傳 vm-10-disk-10。
v1.1.23 Critical
2026-07-27
The in-use guard now fails CLOSED (CRITICAL): is_device_in_use() collapsed "I could not determine the answer" into "not in use" — a /proc/mounts read that timed out, or a fuser call killed by its own 5s watchdog, fell through to return 0. For a raw Pure LUN attached to a running VM there is no mount and no real holder (kpartx partitions are deliberately ignored), so fuser is the only positive signal that a guest has the device open — and a timed-out fuser turned "a VM is using this disk" into "nothing is using this disk", exactly when the node was unhealthy enough for that watchdog to fire. New device_usage_state() returns in-use / idle / unknown with a reason; unknown now counts as in use. A failed WWID lookup no longer disables the guard (CRITICAL): free_image(), volume_snapshot_rollback() and create_base() wrapped the lookup in eval and skipped the whole safety check when it failed, then destroyed or overwrote anyway. Rollback was the worst case — volume_overwrite() replaces the volume outright and has no eradication-delay recovery window. No automated path eradicates any more (HIGH): the temp-clone reapers called volume_delete() without skip_eradicate, i.e. permanent deletion with no recovery window from a background process; all 15 delete call sites are now recoverable. The reaper also re-matches candidates against the exact name path() generates and requires two independent age sources to agree. alloc_image() checks before replacing a state/cloudinit volume (HIGH) — it was the only destructive path with no in-use check at all. Two mirrored volume_list() calls that cancelled each other out (HIGH): named arguments passed to a positional-only function meant config-backup cleanup never ran and the "last disk?" test was always true; fixing either side alone would have destroyed config backups on the first disk deletion of a multi-disk VM, so both are fixed together. Fibre Channel: no more LIP on every rescan (HIGH): a Loop Initialization Primitive is a link reset that forces every device behind the HBA port to re-login, including other vendors' LUNs — and it was issued unconditionally from loops running every 2-3 seconds. It is not needed to find a newly mapped LUN; the SCSI host scan does that. Now opt-in and unused by default. FC.pm also reads sysfs with a timeout instead of a bare open(). The disaster-recovery tool cannot hang (HIGH): pve-pure-config-get used bare system('mount')/system('umount') with no timeout, which never returns on a multipath device whose paths are gone — the exact state in which the tool is run. It also now picks the newest destroyed generation when a disk has been deleted and recreated several times (lexical sort picked the oldest, and the newer ones then aborted the restore on a name conflict). The temp-clone reaper respects ownership (HIGH): a temp clone is connected only to its creating node, but every node reaps — so a snapshot-source operation running over an hour on node A had its device disconnected and destroyed by node B. 使用中檢查改為 fail-closed(嚴重):is_device_in_use() 把「我無法判斷」歸類成「沒有在使用」——/proc/mounts 讀取逾時,或 fuser 被自己的 5 秒看門狗殺掉,都會落到 return 0。對一顆交給執行中 VM 使用的 raw Pure LUN 而言,沒有掛載點也沒有真正的 holder(kpartx 分割被刻意忽略),因此 fuser 是唯一會回報 guest 正持有該裝置的訊號——而 fuser 逾時就把「有 VM 正在使用這顆磁碟」變成「沒有任何東西在用」,恰好發生在節點不健康到足以觸發該看門狗的時候。新增的 device_usage_state() 回傳 in-use/idle/unknown 並附理由,unknown 一律視為使用中。WWID 查詢失敗不再讓保護失效(嚴重):free_image()、volume_snapshot_rollback() 與 create_base() 原本把查詢包在 eval 裡,失敗就跳過整段安全檢查,然後照樣銷毀或覆寫。rollback 最嚴重——volume_overwrite() 直接取代整顆 Volume,且沒有 eradication delay 的復原窗口。不再有任何自動路徑執行 eradicate(高):暫存複製回收原本呼叫 volume_delete() 未帶 skip_eradicate,等於由背景程序執行永久且無復原窗口的刪除;現在 15 個刪除呼叫點全部可復原。回收器也改為以 path() 產生的精確名稱二次比對,並要求兩個獨立的年齡來源都同意才刪除。alloc_image() 取代 state/cloudinit Volume 前會先檢查(高)——這是唯一完全沒有使用中檢查的破壞性路徑。兩個互相抵消的 volume_list() 呼叫(高):對只吃位置參數的函式傳具名參數,導致 config 備份清理從未執行,而「最後一顆磁碟?」判斷永遠為真;只修好其中一邊,多磁碟 VM 刪除第一顆磁碟時就會銷毀 config 備份,因此兩者一併修正。Fibre Channel:不再每次 rescan 都發 LIP(高):LIP 是鏈路重置,會強迫該 HBA port 後面所有裝置重新登入,包含其他廠商的 LUN——而它原本是在每 2 至 3 秒執行一次的迴圈裡無條件發送。探索新對應的 LUN 根本不需要它,SCSI host 掃描就夠了。現在改為選用且預設不使用。FC.pm 也改用有逾時保護的 sysfs 讀取,不再用裸 open()。災難復原工具不會再卡死(高):pve-pure-config-get 原本用裸 system('mount')/system('umount') 且無逾時,對路徑全斷的 multipath 裝置永遠不返回——而那正是這支工具被執行時的狀態。它同時改為在磁碟被刪除重建多次時挑選最新的一代(字典序排序原本會挑到最舊的,較新的幾代接著因撞名而中止還原)。暫存複製回收器尊重歸屬(高):暫存複製只連線到建立它的節點,但每個節點都在回收——因此在節點 A 上執行超過一小時的快照來源操作,會被節點 B 斷線並銷毀。
v1.1.22 High
2026-07-26
No more full SAN rescan on every pvestatd poll (CRITICAL): Proxmox VE calls activate_storage() from storage_info() on every pvestatd poll (~10s), sequentially for every storage — and activate_storage() unconditionally ran an iSCSI session rescan, a SCSI host scan, a host-wide multipathd reconfigure and udevadm trigger --subsystem-match=block + udevadm settle. That is a full multipath rebuild and a re-trigger of every block device on the node, six times a minute, on every node. It also competed directly with device discovery: a VM start or backup waiting on a new LUN was racing a reconfigure that kept tearing the map table down and rebuilding it. Rescans now run immediately when the node logs in to a new iSCSI portal, and otherwise at most once per new option pure-rescan-interval (default 300s); multipathd reconfigure is throttled process-wide to once per 30s on all discovery paths. Unkillable hang in the timeout handler (HIGH): _run_cmd ran kill(TERM) then a blocking waitpid; a D-state child — the exact case these timeouts exist for — cannot be killed, so the handler itself hung. Now escalates TERM to KILL and reaps only with WNOHANG. Device discovery reworked (HIGH, #13): wait_for_multipath_device() probed only at the end of a loop body that could consume the whole timeout, giving the caller a single look at the device right after the most disruptive step. It now probes first, probes after every step, polls cheaply between steps and is deadline-aware throughout — and the failure message now reports what multipathd actually saw, the matching /dev/disk/by-id links and per-session iSCSI state, captured at failure time. REST 2.x timestamps are milliseconds (MEDIUM): snapshot dates in the Web UI were ~53000 years out, and the orphaned temp-clone reaper could never fire. Pod capacity reporting (MEDIUM, #10): used is clamped to the pod quota, a pod at or over quota logs what that does and does not mean plus the array's raw figures, and new option pure-pod-usage-metric selects provisioned (default) / virtual / physical. Also: API 2.x host lookup no longer passes a wildcard to names (which silently returned nothing), N+1 REST lookup removed from deactivate_storage, and the API client cache key no longer collapses two storages on one array into a single client; and postinst no longer reports another vendor's device-scoped multipath settings as a Pure hazard. 不再於每次 pvestatd 輪詢執行完整 SAN 重新掃描(嚴重):Proxmox VE 每次 pvestatd 輪詢(約 10 秒)都會由 storage_info() 對每個 storage 循序呼叫 activate_storage(),而 activate_storage() 無條件執行 iSCSI session 重新掃描、SCSI host 掃描、全主機 multipathd reconfigure 以及 udevadm trigger --subsystem-match=block 與 udevadm settle。等於每個節點每分鐘做六次完整 multipath 重建與全系統區塊裝置重新觸發,而且會直接與裝置探索競爭:正在等待新 LUN 的 VM 啟動或備份,等於在跟一個不斷拆除並重建對應表的 reconfigure 賽跑。現在只有登入新的 iSCSI portal 時會立即重新掃描,其餘最多每 pure-rescan-interval(新選項,預設 300 秒)一次;multipathd reconfigure 在所有探索路徑上以行程範圍限流為每 30 秒一次。逾時處理器內部無法終結的卡死(高):_run_cmd 原本執行 kill(TERM) 後接會阻塞的 waitpid;處於 D state 的子行程——正是這些逾時存在的理由——無法被終結,因此處理器本身卡死。現在改為 TERM 升級 KILL,並只以 WNOHANG 回收。重寫裝置探索(高,#13):wait_for_multipath_device() 原本只在可能耗盡整個逾時的迴圈本體最後才檢查,等於只讓呼叫端在最具破壞性的步驟之後看一次裝置。現在改為先檢查、每個步驟後再檢查、步驟之間輕量輪詢,且全程檢查截止時間——失敗訊息也會回報 multipathd 當下實際看到的內容、相符的 /dev/disk/by-id 連結,以及在失敗當下擷取的逐 session iSCSI 狀態。REST 2.x 時間戳記為毫秒(中):Web UI 的快照日期偏離約 53000 年,殘留暫存複製回收也永遠不會觸發。Pod 容量回報(中,#10):used 會被限制在 pod 配額之內,達到或超過配額的 pod 會記錄這代表什麼、不代表什麼,以及陣列回傳的原始數值;新增選項 pure-pod-usage-metric 可選 provisioned(預設)/virtual/physical。另外:API 2.x 的主機查詢不再把萬用字元傳給 names(先前會靜默回傳空值)、移除 deactivate_storage 的 N+1 REST 查詢,API 用戶端快取鍵不再把同一陣列上的兩個 storage 併成同一個用戶端,postinst 也不再把其他廠商 device 範圍的 multipath 設定回報為 Pure 的風險。
v1.1.21 High
2026-06-16
pvestatd isolation from a slow array (HIGH): activate_storage() and the foreground of status() now use a short-timeout, single-attempt REST client (new option pure-status-timeout, default 5s) instead of the resilient data-path client (15s x 2 retries). PVE polls storages sequentially every ~10s, so a slow or degraded array previously backed up the whole pvestatd cycle and starved sibling storages on the same node into inactive; the next poll is the retry. HTTP keep-alive (HIGH): the REST client now reuses one TCP+TLS connection (keep_alive => 1) instead of a fresh handshake per call, cutting the steady-state load on the array's management gateway. Bounded iSCSI activate loop (MEDIUM): new option pure-activate-deadline (default 30s) caps the cumulative discover/login time — once spent and at least one path is up, remaining portals defer to a later activation (never while zero paths are up, never mid-login); the session list is snapshotted once before the loop. temp-clone reaper N+1 removed (LOW) and postinst upgrade warning (LOW): after an upgrade postinst now tells the operator to systemctl restart pvestatd on every node (SIGHUP reload does not reload Perl modules on many PVE versions). Ported from sibling-pattern fixes in the related NetApp plugin. pvestatd 與緩慢陣列隔離(高):activate_storage() 與 status() 前景改用短逾時、單次嘗試的 REST 用戶端(新增選項 pure-status-timeout,預設 5 秒),取代耐用的資料路徑用戶端(15 秒 × 2 次重試)。PVE 每約 10 秒循序輪詢各 storage,因此緩慢或劣化的陣列先前會拖累整個 pvestatd 週期,把同節點其他 storage 拖成 inactive;下次輪詢即為重試。HTTP keep-alive(高):REST 用戶端現在重複使用同一條 TCP+TLS 連線(keep_alive => 1),不再每次呼叫重開交握,降低對陣列管理閘道的穩態負載。限制 iSCSI 啟用迴圈(中):新增選項 pure-activate-deadline(預設 30 秒)限制探索/登入的累積時間——用盡且至少一條路徑已建立後,其餘 portal 延後至後續啟用(尚無路徑時不套用、不中斷進行中的登入);session 清單於迴圈前一次性快照。移除暫存複製回收的 N+1(低)與 postinst 升級警告(低):升級後 postinst 會提示在每個節點執行 systemctl restart pvestatd(許多 PVE 版本上 SIGHUP reload 不會重新載入 Perl 模組)。本次修正自相關 NetApp 外掛的同型樣式移植而來。
v1.1.20 Fix
2026-05-29
Proxmox VE 9.2 compatibility — override `get_identity()` (MEDIUM): PVE 9.2 added get_identity() to the base PVE::Storage::Plugin, whose default implementation dies with "get_identity not implemented for this plugin". It is invoked via the new GET /nodes/<node>/storage/<storage>/identity endpoint (primarily for Proxmox Backup Server instance matching; the Web UI may poll it for any storage), so on PVE 9.2 the base die would surface as a Web UI error. The plugin now returns a deterministic purestorage:<portal>:<pod> — the management portal plus the optional ActiveCluster pod, which together pin the storage to one array. Signature verified against the pve-storage source (my ($class, $scfg, $storeid)). No APIVERSION change required (still 13, within PVE 9.2's accepted 9..14). Proxmox VE 9.2 相容性——覆寫 `get_identity()`(中):PVE 9.2 在 base PVE::Storage::Plugin 新增了 get_identity(),預設實作會 die 並回報「get_identity not implemented for this plugin」。它透過新的 GET /nodes/<node>/storage/<storage>/identity 端點被呼叫(主要供 Proxmox Backup Server 比對實例;Web UI 也可能對任一 storage 輪詢),因此在 PVE 9.2 上 base 的 die 會在 Web UI 浮現為錯誤。外掛現在回傳具決定性的 purestorage:<portal>:<pod>——管理 portal 加上選用的 ActiveCluster pod,兩者一起把 storage 綁定到單一陣列。簽章已對照 pve-storage 原始碼驗證(my ($class, $scfg, $storeid))。不需變更 APIVERSION(仍為 13,落在 PVE 9.2 接受的 9..14 範圍內)。
v1.1.19 High
2026-05-29
Listing past 1000 volumes (HIGH): volume_list(), volume_list_destroyed() and snapshot_list() now follow the API 2.x continuation_token across all pages. Pure REST 2.x caps each collection GET at a server page (~1000 items); the old code read only the first page, so a storage with more than one page of volumes/snapshots silently truncated — list_images() hid disks from the web UI and the orphan reaper missed tail volumes. New API::_get_v2_collection() helper. Orphan reaper hardening (HIGH): Phase 1 now derives each WWID from the serial already in the volume_list() response instead of one extra REST call per volume on every poll; background cleanup is serialised with a per-storeid flock; and a device is only reaped after a 600s grace period AND 3 consecutive absent passes, so a freshly-added, in-use LUN can never be torn down on a single bad array snapshot (cross-project fix from a NetApp incident). Phase 3 no longer mis-flags a second purestorage storage's live device as orphaned. Monitoring (MEDIUM): status() outage detection (ERROR after 3 failed polls, INFO on recovery) and capacity health warnings at 90%/95%; single-controller iSCSI redundancy advisory; postinst in-flight-operation grace before service reload. 超過 1000 顆 Volume 的列舉(高):volume_list()、volume_list_destroyed() 與 snapshot_list() 現在會依 API 2.x 的 continuation_token 逐頁抓取。Pure REST 2.x 每次集合 GET 都有單頁上限(約 1000 筆);舊程式只讀第一頁,因此 Volume/Snapshot 超過一頁的 storage 會被靜默截斷——list_images() 讓 Web UI 看不到部分磁碟,殘留清理也漏看後段 Volume。新增 API::_get_v2_collection() 協助函式。殘留清理強化(高):Phase 1 改用 volume_list() 回應已帶的 serial 推導 WWID,不再每次輪詢對每顆 Volume 多打一次 REST;背景清理以 per-storeid flock 序列化;裝置必須通過 600 秒寬限期「且」連續 3 次缺席才會被清,因此剛加入、使用中的 LUN 絕不會因單次錯誤的陣列快照被拆除(源自 NetApp 事故的跨專案修正)。Phase 3 也不再把另一個 purestorage storage 的使用中裝置誤判為殘留。監控(中):status() 停機偵測(連 3 次失敗記 ERROR、恢復記 INFO)與 90%/95% 容量健康警告;單一控制器 iSCSI 冗餘提醒;postinst 在重新載入服務前對進行中操作給予寬限。
v1.1.18 Fix
2026-05-14
Snapshot tombstone rename (MEDIUM, #11): Pure's destroyed-pending state reserves a snapshot's suffix for the array's eradication delay (default 24h), so recreating a snapshot with the same name within that window failed with Snapshot 't1' already exists for volume. snapshot_delete() now pre-renames the snapshot to <orig-suffix>-pve-tomb-<unix-ts>-<pid> before destroy — mirrors the volume-side fix from v1.1.15. Uses Pure's PATCH /volume-snapshots rename API (new suffix in body, not full name — verified against FA 2.x spec). Length overflow, idempotent re-destroy, rollback on destroy failure all handled. API 1.x falls through (lacks REST snapshot rename). Config-backup timeout (LOW, #12): new storage option pure-config-backup-timeout (5..60, default 15s) for the auxiliary 1 MB config-backup volume's device wait. Previously used pure-device-timeout (60s) and stalled every snapshot for the full timeout on degraded multipath. Non-fatal warning text expanded to spell out the skip is non-critical. Postinst sanity check (LOW, #9): postinst now refuses to complete configure when required binaries (multipathd, multipath, kpartx, iscsiadm, sg_inq, blockdev) are missing, with a clear "use apt install ./*.deb or apt --fix-broken install" error. Catches the "I installed with dpkg -i and got internal errors later" failure mode. README install instructions now lead with apt install ./*.deb. Snapshot tombstone rename(中,#11):Pure destroyed-pending 狀態保留 snapshot suffix 直到 eradication delay(預設 24 小時),這段期間建同名 snapshot 會失敗回 Snapshot 't1' already exists for volume。snapshot_delete() 現在 destroy 前先把 snapshot rename 為 <orig-suffix>-pve-tomb-<unix-ts>-<pid>——對應 v1.1.15 在 Volume 端的修法。使用 Pure 的 PATCH /volume-snapshots rename API(body 是新 suffix、不是完整新名——已對照 FA 2.x spec 驗證)。長度超限、idempotent re-destroy、destroy 失敗時 rollback rename 全部處理。API 1.x fall through(REST 不支援 snapshot rename)。Config-backup timeout(低,#12):新增 storage 參數 pure-config-backup-timeout(5..60,預設 15 秒),專門給 1 MB 輔助 config-backup Volume 等 device 用。原本用 pure-device-timeout(60 秒),multipath 降級時每次 snapshot 都卡完整 60 秒。非致命 warning 文字改寫,明說 skip 是非必要。Postinst 檢查(低,#9):postinst 偵測到必要 binary(multipathd、multipath、kpartx、iscsiadm、sg_inq、blockdev)缺少時拒絕完成 configure,明確指示用 apt install ./*.deb 或 apt --fix-broken install。攔住「用 dpkg -i 裝完之後跑出內部錯誤」這類失敗。README 安裝指令改為以 apt install ./*.deb 為首選。
v1.1.17 Fix
2026-05-13
Pod capacity `used` aligned with Pure quota enforcement (MEDIUM): get_managed_capacity() for pod-backed storage now reports used based on space.total_provisioned (sum of all volume sizes within the pod) instead of space.virtual (host-written bytes). Pure pod quota is enforced against total_provisioned at allocation time, so this is the metric that matches what the array will actually refuse — and the headline "Size" indicator Pure UI shows on the pod detail page. A pod with quota 2 TB containing a 2 TB thin volume with zero writes correctly reports as full (the next allocation would be rejected by the array). Fallback chain unchanged (virtual → total_physical → total_used) for older Purity. Per-volume size in PVE disk list is unaffected — that has always used the volume's own provisioned field. Tracked in #7. Pod 容量 `used` 對齊 Pure 配額強制邏輯(中):get_managed_capacity() 對 pod-backed storage 的 used 改採 space.total_provisioned(pod 內所有 Volume size 加總),不再以 space.virtual(host 寫入 bytes)為主。Pure pod quota 在 allocate 階段對 total_provisioned 強制,這是陣列實際會拒絕分派的依據,也是 Pure UI 對 pod 顯眼的「Size」指標。2 TB 配額 pod 裡有 2 TB thin volume、即使零寫入,PVE 也會正確顯示為滿(下次 allocate 真的會被陣列拒絕)。Fallback chain 不變(virtual → total_physical → total_used)相容舊版 Purity。PVE disk 列表上每顆 Volume 顯示的大小不變——那邊一直讀 Volume 自己的 provisioned 欄位。Tracked in #7。
v1.1.16 High
2026-05-13
pve-pure-config-get tombstone-awareness (HIGH): v1.1.15 introduced tombstone rename in volume_delete, but the disaster-recovery tool pve-pure-config-get --restore wasn't updated to match. After a v1.1.15 destroy, the destroyed volumes had tombstone names, which (a) showed up as ugly snapnames in the restore picker because decode_config_volume_name's greedy capture absorbed the suffix, and more seriously (b) recovered disk volumes lived under their tombstone names while the restored VM config referenced original names → PVE looked up disks by their expected names, didn't find them, and the restored VM failed to start with "volume does not exist." Fixed in 1.1.16: strip the tombstone suffix before decode for display, rename each recovered volume BACK to its original name immediately after volume_recover, abort with a clear error if rename-back conflicts with an already-existing live volume. Also picked up the inline storeid_to_pure_prefix duplication that #6 missed in this tool. make test now also syntax-checks bin/pve-pure-config-get. pve-pure-config-get 跟上 tombstone(高):v1.1.15 在 volume_delete 加了 tombstone rename,但災難復原工具 pve-pure-config-get --restore 沒同步更新。v1.1.15 之後 destroy 的 Volume 都帶 tombstone 名,導致(a)restore picker 顯示的 snapname 把 tombstone 字尾整段吃進去、難以辨認,更嚴重的(b)recover 回來的 disk Volume 在 Pure 上仍叫 tombstone 名但還原好的 VM config 用的是原名 → PVE 找 disk 找不到 → 還原好的 VM 開不起來,「volume does not exist」。1.1.16 修法:decode 顯示前先剝 tombstone 字尾、`volume_recover` 之後立即 rename 回原名、rename-back 與既有 alive Volume 衝突時 abort 並印明確錯誤。也順便補齊 #6 在這個工具裡漏網的 inline storeid_to_pure_prefix 重複。make test 現在也對 bin/pve-pure-config-get 做語法檢查。
v1.1.15 Fix
2026-05-13
Pre-rename tombstone on volume destroy (MEDIUM): Pure's destroyed-pending state reserves a volume's name for the array's eradication delay (default 24h). Without a rename, recreating a same-named disk in PVE within that window failed. volume_delete() now pre-renames the volume to <orig>-pve-tomb-<unix-ts>-<pid> before destroying it — the original name is freed as soon as the rename succeeds; the tombstoned volume still goes into destroyed-pending (still recoverable from Pure UI) under the suffixed name. Edge cases handled: pod-prefixed volumes (keep pod:: prefix), name-length overflow (skip rename, warn), already-tombstoned names (no double-rename), concurrent destroys from different PVE nodes (PID suffix), WWID preservation (no tracking-JSON update needed). On destroy failure after a successful rename, the name is rolled back to the original to keep PVE retries natural. PVE snapshot rollback (volume_overwrite) is unaffected — that path overwrites contents in-place, no destroy involved. Reported by @pulipulichen (#8). Volume destroy 前先 rename tombstone(中):Pure 的 destroyed-pending 狀態會保留 Volume 名稱直到陣列的 eradication delay(預設 24h)。沒有 rename 的話,這段期間 PVE 內重建同名 disk 會失敗。volume_delete() 現在在 destroy 前先把 Volume rename 為 <orig>-pve-tomb-<unix-ts>-<pid>——rename 成功後原名立即釋放,tombstone Volume 仍進入 destroyed-pending(仍可從 Pure UI recover),只是用 suffix 後的名稱。處理的邊界情境:pod 內 Volume 保留 pod:: 前綴、加 suffix 後超過 63 char 跳過 rename 並 warn、已有 tombstone 標記的名稱不會二度 rename、跨 PVE 節點併發 destroy 同個 Volume(PID suffix 防撞名)、WWID 在 rename 後不變所以追蹤檔不用更新。Rename 成功但 destroy 失敗時,會 rollback rename 回原名讓 PVE 重試走得通。PVE snapshot 倒回(volume_overwrite)不受影響——那條路是原地覆寫,沒有 destroy。@pulipulichen 回報(#8)。
v1.1.14 High
2026-05-13
VM-with-memory snapshot wedge on degraded multipath (HIGH): on a host with broken paths (e.g. 4-portal Pure with 2 paths down), VM snapshot WITH memory caused pvedaemon / pvestatd to progressively die and the entire PVE web UI to show ? for every storage. CT snapshot and VM-without-memory snapshot were unaffected because they don't trigger host-side new-volume activation. Root cause: rescan_sessions() used iscsiadm -m session --rescan which rescans every active session in one shot, queueing SCSI commands on the dead sessions that wait for kernel-level timeouts (30s+ per dead path); iscsiadm parent gets killed at our 60s wrapper but leaves D-state children behind. Each pvestatd poll re-fires the same rescan and stacks more D-state children until management dies. Fixed by enumerating sessions via /sys/class/iscsi_session/, reading each session's state, and only issuing per-session rescan (10s timeout each) on LOGGED_IN sessions. Reported by @pulipulichen (#5) with the critical "CT works, VM-with-memory doesn't" reproducer.
Storage ID with `.` empty disk list (MEDIUM): encode_volume_name stripped dots via sanitize_for_pure on write, but list_images and 6 sibling pattern-building sites only did s/-/_/g — leaving the dot in the filter pattern, which never matched volumes stored without it. Fixed by adding Naming::storeid_to_pure_prefix helper that performs the full transform symmetrically and replacing all 7+3 inline duplications. Reported by @pulipulichen (#6).
multipath 部分斷線下、VM 含記憶體快照拖垮管理層(高):有 path 斷掉的節點上(例如 4 portal Pure、2 條 path 斷),VM 含記憶體快照會讓 pvedaemon/pvestatd 漸進失能,PVE Web UI 對所有 storage 顯示 ?。CT 快照與不含記憶體的 VM 快照不會中招——它們不會觸發 host 端的新 Volume activation。根因:rescan_sessions() 用 iscsiadm -m session --rescan 一次 rescan 所有 session,包括死的;死 session 上 queue 的 SCSI command 等 kernel timeout(每條 30 秒以上);iscsiadm 父行程在 60 秒被殺,留下 D-state 子行程;每次 pvestatd 輪詢又 fire 一次、累積到管理層死。修法:改用 /sys/class/iscsi_session/ 列舉 session、讀 state、只對 LOGGED_IN session 個別 rescan(每個 10 秒 timeout)。@pulipulichen 回報(#5),關鍵線索是「CT 快照沒事、VM 含記憶體快照才出事」。
storage ID 含 `.` 時 disk 列表空白(中):寫入路徑 encode_volume_name 透過 sanitize_for_pure 去點,但 list_images 與 6 處 sibling pattern 建構只做 s/-/_/g——點還在、永遠 match 不到。修法:新 helper Naming::storeid_to_pure_prefix 做對稱的完整 transform,所有 7+3 處 inline 重複改呼叫它。@pulipulichen 回報(#6)。
v1.1.13 High
2026-05-11
Snapshot rollback fix + pod capacity display alignment + CI build workflow. Snapshot rollback (HIGH): volume_overwrite() wrongly used PATCH /volumes with a source body field — per the FA 2.x spec, PATCH /volumes is the rename/modify endpoint and does not accept source; Pure returned No attribute specified. with HTTP 200 so the PVE task layer reported success while the volume contents were never restored. Switched to POST /volumes?names=X&overwrite=true with source in body (the spec-defined object-copy path). Reported by @tgdfama1 (#1) and @pulipulichen (#2). Pod capacity (MEDIUM): get_managed_capacity() fallback chain now prefers space.virtual (matching Pure UI's pod usage display) over total_provisioned — earlier code reported 100% full the instant a thin volume of the quota's size was created, mismatching the Pure GUI. Reported by @pulipulichen (#3). CI: new manually-triggered .github/workflows/build-deb.yml that runs make test + dpkg-buildpackage and uploads the .deb as a 30-day artifact. Contributed by @pulipulichen (#4). Snapshot 倒回修復 + Pod 容量顯示對齊 + CI build workflow。Snapshot 倒回(高):volume_overwrite() 之前錯用 PATCH /volumes 並把 source 放 body——對照 FA 2.x spec,PATCH /volumes 是 rename / modify 端點不接受 source;Pure 回 No attribute specified. 但 HTTP 200 帶空 body,PVE task 層判定成功實際倒回沒發生。改為 POST /volumes?names=X&overwrite=true、source 放 body(spec 定義給 object-copy 用的路徑)。@tgdfama1(#1)與 @pulipulichen(#2)獨立回報。Pod 容量(中):get_managed_capacity() fallback chain 改為優先取 space.virtual(對齊 Pure UI pod 用量顯示),取代之前優先取 total_provisioned——前者讓配額大小的 thin Volume 一建好就顯示 100% full,與 Pure GUI 落差大。@pulipulichen 回報(#3)。CI:新增手動觸發的 .github/workflows/build-deb.yml,跑 make test + dpkg-buildpackage、上傳 .deb 為 30 天 artifact。@pulipulichen 貢獻(#4)。
v1.1.12 Fix
2026-05-08
Stop misreading file-services quota policies as pod block quotas. Field follow-up to v1.1.10/v1.1.11 revealed that Pure's Storage > Policies panel — including the quota policy that the v1.1.10 walk had been reading — is FlashArray Files / managed-directory only, even when the GUI lets you scope a policy to a Pod. Attaching ANY such policy to a Pod makes Pure reject every block volume create with the misleading Pod contains file systems or policies. (context: <podname>). pod_get_quota_limit() simplified to read ONLY Pod.quota_limit — the genuine block-level pod quota field, set via purepod --quota-limit CLI / PATCH /pods REST / 6.6+ GUI. README updated to spell out the three correct setting paths and warn explicitly that Storage > Policies must NOT be used for pod block quotas, with the destroy + re-set recipe so operators can self-recover. v1.1.11's with_default_protection=false kept as defensive change. 不再把 file-services 性質的 quota Policy 誤讀為 Pod block 配額。接續 v1.1.10/v1.1.11 現場排查發現 Pure Storage > Policies 整個面板(含 v1.1.10 在讀的 quota policy)都是 FlashArray Files/managed-directory 限定,即使 GUI 讓你在建 policy 時指定 Pod 也一樣。任何此類 policy 一旦掛到 Pod 上,Pure 就會把所有 block volume create 拒絕並回傳誤導性的 Pod contains file systems or policies. (context: <podname>)。pod_get_quota_limit() 簡化為**只讀 Pod.quota_limit**——這才是真正的 block-level Pod 配額欄位,由 purepod --quota-limit CLI/PATCH /pods REST/6.6+ GUI 設定。README 更新明確列出三種正確設定路徑並警告**不要**用 Storage > Policies 設 Pod block 配額,附上「砍 policy + 改設 Pod.quota_limit」的恢復步驟。v1.1.11 的 with_default_protection=false 作為防禦性修改保留。
v1.1.11 High
2026-05-08
Pod Volume Create With Policy Attached Fix. Direct follow-up to v1.1.10: as soon as the operator attached a quota policy to the pod (so v1.1.10 could read the cap), every VM disk creation failed with Pure Storage API: Pod contains file systems or policies. (context: pvepod). Per the FA 2.26 spec, POST /volumes defaults with_default_protection=true, which makes Pure try to apply the pod's container default protection — and Pure rejects that step when the pod has any policy attached. Fixed by passing with_default_protection=false on volume_create and volume_clone when the volume name carries a pod:: prefix. The plugin manages PVE snapshots itself, so it never relied on Pure's default-protection mechanism. Non-pod volumes are left alone to preserve any user-configured array-level default_protections. Pod 內掛 Policy 後 Volume Create 失敗修復。直接接續 v1.1.10:操作者一旦把 quota policy 掛到 pod 上(v1.1.10 才能讀到 cap),任何 VM 磁碟建立都會失敗並回傳 Pure Storage API: Pod contains file systems or policies. (context: pvepod)。對照 FA 2.26 spec,POST /volumes 預設 with_default_protection=true,會讓 Pure 嘗試把 pod 的 container default protection 套到新 Volume——但 pod 一旦有任何 policy 附掛,Pure 就會拒絕該步驟。修法是在 Volume 名稱帶 pod:: 前綴時,於 volume_create 與 volume_clone 加上 with_default_protection=false。外掛端 PVE snapshot 由自身管理,本來就不依賴 Pure default-protection 機制。非 pod Volume 保持原行為,不影響使用者於 array-level 設定的 default_protections。
v1.1.10 Fix
2026-05-08
Pod Quota Capacity Reporting Fix. When the storage was created with --pure-pod <name>, PVE always reported the FULL FlashArray capacity instead of the pod's quota. Pure FlashArray API 2.x exposes pod quotas through two mechanisms: (a) the Pod object's own quota_limit field set via purepod --quota-limit CLI, and (b) Policy objects of policy_type=quota that reference the pod through their pod field, with rules in /policies/quota/rules. The GUI uses (b) and does NOT propagate the cap back to (a), so the old code always saw quota_limit = 0 on the pod and fell through to array capacity. New helper pod_get_quota_limit() reads both paths and takes the smallest positive limit. Pod used switched from total_used to total_provisioned (the metric Pure quotas actually count against, per the FA 2.26 spec). Verified against the FA 2.26 OpenAPI spec — /policies/quota/members is for managed directories, NOT pods. Pod 配額容量回報修復。Storage 以 --pure-pod <name> 建立時,PVE 一律顯示整個 FlashArray 全容量,而非該 Pod 的配額。Pure FlashArray API 2.x 對 Pod 配額有兩種設定路徑:(a) Pod 物件本身的 quota_limit 欄位,由 purepod --quota-limit CLI 設定;(b) policy_type=quota 的 Policy 物件透過自身的 pod 欄位指到 Pod,quota_limit 則由 /policies/quota/rules 攜帶。GUI 走的是 (b) 而且不會把 cap 寫回 (a),所以舊版程式只讀 Pod 物件永遠看到 quota_limit = 0,於是 fall through 到全陣列容量。新 helper pod_get_quota_limit() 兩條路徑都查並取最小的正值。Pod used 改用 total_provisioned(依 FA 2.26 spec,Pure 配額實際計量的指標)。已對照 FA 2.26 OpenAPI spec 驗證——/policies/quota/members 是給 managed directory 用的,不是 Pod。
v1.1.9 Critical
2026-05-05
Unreachable iSCSI Portal Stall Fix. activate_storage() now TCP-probes every iSCSI portal returned by iscsi_get_ports() before invoking iscsiadm, skipping unreachable portals in seconds instead of stalling 30s (discovery) + 60s (login) per dead LIF. Field reproducer: 4-LIF Pure with asymmetric cabling that only reached one controller wedged pvesm add and every pvestatd poll, leaving the web UI Status panel stuck on "Loading...". New tunable pure-portal-probe-timeout (default 2s, set 0 to disable). Hard-fails when zero portals are reachable, with an actionable message pointing at --nodes binding. 無法連通的 iSCSI Portal 卡死修復。activate_storage() 在呼叫 iscsiadm 前先對 iscsi_get_ports() 回傳的每個 portal 做 TCP 預探測,幾秒內跳過不通的 portal,不再讓每個失效 LIF 各吃 30 秒(discovery)+ 60 秒(login)的 timeout。實際案例:4 LIF Pure 線路不對稱,只連得到其中一個控制器,導致 pvesm add 與每輪 pvestatd 全部卡死,Web UI Status 永遠停在「Loading...」。新增可調 pure-portal-probe-timeout(預設 2 秒,設為 0 可停用)。零 portal 可連通時直接 fail-fast,錯誤訊息提示 --nodes 綁定。
v1.1.8 Fix
2026-04-26
Sibling-pattern audit from author's related NetApp plugin v0.2.9. Fixed _cleanup_orphaned_devices() unconditionally untracking WWIDs after a failed cleanup (mirrors the conditional-untrack pattern in free_image()). Wrapped glob("/dev/disk/by-id/...") calls in alarm(5) to bound the lookup if the underlying block layer hangs. 來自本作者相關專案 NetApp v0.2.9 的 sibling-pattern 稽核。修復 _cleanup_orphaned_devices() 在清理失敗後仍無條件 untrack WWID(鏡像 free_image() 的 conditional-untrack 模式)。glob("/dev/disk/by-id/...") 加上 alarm(5) 保護,避免底層 block layer 卡住時無上限等待。
v1.1.7 Critical
2026-04-11
kpartx Partition Holder Fix. Fixed is_device_in_use() blocking ALL volume deletions for VMs with GPT/MBR partition tables (i.e. every production VM). Distinguishes bare kpartx partitions from real holders (LVM, dm-crypt). Added kpartx -d in cleanup before multipath -f. kpartx Partition Holder 修復。修復 is_device_in_use() 擋住所有有 GPT/MBR partition table 的 VM disk 刪除(即所有正式環境 VM)。區分 bare kpartx partition 與真正的 holder(LVM、dm-crypt)。新增 kpartx -d 在 multipath -f 之前清理。
v1.1.6 Fix
2026-04-10
Postinst Service Reload + lvm.conf Detection. Reloads pvedaemon, pvestatd, pveproxy via SIGHUP (not restart, to avoid stop-phase hang on D-state children). Detects missing lvm.conf global_filter on Proxmox VE 7→9 upgraded nodes and prints the exact filter to add. Postinst 服務 Reload + lvm.conf 偵測。透過 SIGHUP reload pvedaemon、pvestatd、pveproxy(不用 restart,避免 stop 階段在 D-state child 上卡住)。偵測 Proxmox VE 7→9 升級節點上缺少的 lvm.conf global_filter 並印出正確 filter。
v1.1.5 Critical
2026-04-10
Non-iSCSI SCSI Host Scan Fix (HPE ProLiant production incident). Fixed rescan_scsi_hosts() writing to /sys/class/scsi_host/ hosts that include hardware controllers (smartpqi, PERC, megaraid_sas), causing 600+ second D-state cascades. Now sources host list from /sys/class/iscsi_host/ only. Added translate_pure_error() for operator-friendly limit-error messages. 非 iSCSI SCSI Host 掃描修復(HPE ProLiant 正式環境事件)。修復 rescan_scsi_hosts() 對 /sys/class/scsi_host/ 中的硬體控制器(smartpqi、PERC、megaraid_sas)寫入,導致 600+ 秒 D-state 連鎖。改從 /sys/class/iscsi_host/ 取得 host 清單。新增 translate_pure_error() 提供 operator 友善的 limit-error 訊息。
v1.1.4 Fix
2026-04-09
API 1.x Disconnect Normalisation. Fixed volume_get_connections returning different shapes for API 1.x vs 2.x; 1.x's $conn->{name} was the volume name, not host name -- making disconnect a silent no-op and leaving ghost LUNs after partial connect failures. API 1.x 斷線正規化。修復 volume_get_connections 對 API 1.x 與 2.x 回傳不同結構;1.x 的 $conn->{name} 是 volume 名稱不是 host 名稱,導致 disconnect 變成 no-op,partial connect 失敗後留下 ghost LUN。
Older versions (v1.1.3 -- v1.0.x) 更早版本(v1.1.3 -- v1.0.x)
v1.1.3 Fix
2026-04-09
Partial Connect Cleanup. Cleanup paths now call _disconnect_from_all_hosts() before volume_delete() -- partial successful connects no longer leave orphaned host connections that become ghost LUNs. Partial Connect 清理。Cleanup 路徑會先 _disconnect_from_all_hosts() 再 volume_delete() -- 部分成功的連線不再留下變成 ghost LUN 的殘留 host connection。
v1.1.2 Critical
2026-04-09
Data Loss Protection. Fixed is_device_in_use(/dev/mapper/<wwid>) always returning false because basename of a symlink doesn't exist under /sys/block/ -- was silently destroying LVM-on-Pure. Per-device rescan after resize. blockdev --flushbufs after rollback to invalidate stale page cache. 資料保護。修復 is_device_in_use(/dev/mapper/<wwid>) 永遠回傳 false,因為 symlink 的 basename 在 /sys/block/ 不存在 -- 導致 LVM-on-Pure 被悄悄破壞。Resize 後改用 per-device rescan。Rollback 後加 blockdev --flushbufs 清除過期 page cache。
v1.1.1 Fix
2026-04-09
Multipath Config Auto-upgrade. Added version marker (# pure-multipath-config-version: N) to plugin-managed pure-storage.conf so future upgrades can automatically rewrite outdated device blocks. Multipath 設定自動升級。在 plugin 管理的 pure-storage.conf 加入版本標記(# pure-multipath-config-version: N),未來升級可自動覆寫過期 device block。
v1.1.0 Feature
2026-04-09
Cluster Orphan Device Cleanup. Per-storage WWID tracking file. Auto-import of cluster-wide WWIDs from the array on every status() poll. Background cleanup via double-fork worker. Per-WWID warning cooldown (1 hour). 60s extended timeout for volume_delete. 叢集殘留裝置清理。Per-storage 的 WWID tracking 檔。每次 status() 輪詢自動從陣列匯入叢集範圍 WWID。透過 double-fork worker 在背景清理。Per-WWID 警告冷卻(1 小時)。volume_delete 延長至 60 秒 timeout。
v1.0.49 Fix
2026-02-27
Late 1.0.x stabilisation. Final round of small bug fixes before the 1.1.0 architecture jump. 1.0.x 末期穩定化。1.1.0 架構升級前最後一輪小型修復。
v1.0.48 Fix
2026-02-12
FC SAN bug fixes and dependency cleanups. FC SAN bug 修復與相依套件整理。
v1.0.0 -- v1.0.47
2025 -- early 2026
Initial release through to mid-1.0.x maturation. iSCSI / FC dual-protocol, snapshot, clone, template, online resize, RAM snapshot, LXC, EFI / Cloud-init / TPM support, multipath auto-config. See CHANGELOG.md for the full per-version detail. 初始 release 至 1.0.x 中期成熟。iSCSI / FC 雙協定、快照、複製、範本、線上 resize、RAM snapshot、LXC、EFI / Cloud-init / TPM 支援、multipath 自動設定。完整逐版細節請見 CHANGELOG.md。

Acknowledgments 致謝

Special thanks to: 特別感謝:

  • Pure Storage — for excellent storage technology and comprehensive REST API documentation that made dual 1.x/2.x support straightforward. Pure Storage 原廠 — 提供優秀的儲存技術與完善的 REST API,讓 1.x/2.x 雙版本支援得以順利實作。
  • MetaAge (邁達特) — for providing test equipment and environment for development and testing. MetaAge 邁達特(代理商) — 協助提供測試設備與環境進行開發測試。
  • Proxmox VE community — for the well-documented Storage Plugin API. Proxmox VE 社群 — 提供文件完善的 Storage Plugin API。

References 參考資料