feat: add UI panel for truenas-multipath storage type (#278)

Without a registered JS panel the truenas-multipath type was invisible in
the PVE Datacenter → Storage → Add dropdown. Adds truenas-multipath.js
with all base TrueNAS fields plus the required truenas_portals field, and
wires it into the multipath package install/remove scripts and CI build.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
Kevin Adams 2026-06-08 22:23:01 -04:00
parent d0093f48b3
commit da823569a7
4 changed files with 153 additions and 0 deletions

View File

@ -224,6 +224,8 @@ jobs:
cp perl5/PVE/Storage/Custom/TrueNASMultipath.pm \
"${STAGING}/usr/share/truenas-proxmox-multipath/TrueNASMultipath.pm"
cp ui/truenas-multipath.js \
"${STAGING}/usr/share/truenas-proxmox-multipath/truenas-multipath.js"
cp packaging/DEBIAN-multipath/multipath.conf.example \
"${STAGING}/usr/share/truenas-proxmox-multipath/multipath.conf.example"

View File

@ -4,6 +4,9 @@ set -e
INSTALL_DIR="/usr/share/truenas-proxmox-multipath"
PLUGIN_DST="/usr/share/perl5/PVE/Storage/Custom/TrueNASMultipath.pm"
JS_DST="/usr/share/pve-manager/js/truenas-multipath.js"
TPL="/usr/share/pve-manager/index.html.tpl"
SCRIPT_TAG=' <script type="text/javascript" src="/pve2/js/truenas-multipath.js?ver=[% version %]"></script>'
MULTIPATH_CONF="/etc/multipath.conf"
LOG_FILE="/var/log/truenas-proxmox-install.log"
@ -17,6 +20,19 @@ install_plugin() {
cp "${INSTALL_DIR}/TrueNASMultipath.pm" "$PLUGIN_DST"
}
install_ui() {
log "Installing ${JS_DST}"
cp "${INSTALL_DIR}/truenas-multipath.js" "$JS_DST"
if grep -qF 'truenas-multipath.js' "$TPL" 2>/dev/null; then
log "index.html.tpl already has truenas-multipath.js — skipping"
return 0
fi
log "Adding <script> tag to ${TPL}"
sed -i "/pvemanagerlib\.js/a\\${SCRIPT_TAG}" "$TPL"
}
BLOCK_BEGIN="# BEGIN truenas-proxmox-multipath"
BLOCK_END="# END truenas-proxmox-multipath"
@ -73,6 +89,7 @@ case "$1" in
configure)
log "Configuring truenas-proxmox-multipath (previous version: ${2:-none})"
install_plugin
install_ui
install_multipath_conf
restart_pve_services
;;

View File

@ -3,6 +3,8 @@
set -e
PLUGIN_DST="/usr/share/perl5/PVE/Storage/Custom/TrueNASMultipath.pm"
JS_DST="/usr/share/pve-manager/js/truenas-multipath.js"
TPL="/usr/share/pve-manager/index.html.tpl"
MULTIPATH_CONF="/etc/multipath.conf"
BLOCK_BEGIN="# BEGIN truenas-proxmox-multipath"
BLOCK_END="# END truenas-proxmox-multipath"
@ -19,6 +21,16 @@ case "$1" in
rm -f "$PLUGIN_DST"
fi
if [ -f "$JS_DST" ]; then
log "Removing ${JS_DST}"
rm -f "$JS_DST"
fi
if [ -f "$TPL" ] && grep -qF 'truenas-multipath.js' "$TPL" 2>/dev/null; then
log "Removing <script> tag from ${TPL}"
sed -i '/truenas-multipath\.js/d' "$TPL"
fi
if [ -f "$MULTIPATH_CONF" ] && grep -qF "$BLOCK_BEGIN" "$MULTIPATH_CONF"; then
log "Removing TrueNAS stanza from ${MULTIPATH_CONF}"
perl -i -0777 -pe \

122
ui/truenas-multipath.js Normal file
View File

@ -0,0 +1,122 @@
// TrueNAS Multipath Storage Plugin — Proxmox VE UI
//
// Registers the 'truenas-multipath' storage type in PVE's UI schema and defines
// the configuration panel shown when adding or editing TrueNAS Multipath storage.
//
// Only loaded on nodes where truenas-proxmox-multipath is installed, so the type
// does not appear in the Add dropdown on nodes without the package.
PVE.Utils.storageSchema['truenas-multipath'] = {
name: 'TrueNAS Multipath (ZFS/iSCSI)',
ipanel: 'TrueNASMultipathInputPanel',
faIcon: 'database',
backups: false,
};
Ext.define('PVE.storage.TrueNASMultipathInputPanel', {
extend: 'PVE.panel.StorageBase',
initComponent: function () {
let me = this;
// Column 1 — identity and credentials (left side)
me.column1 = [
{
xtype: me.isCreate ? 'proxmoxtextfield' : 'displayfield',
fieldLabel: gettext('TrueNAS Host'),
name: 'truenas_host',
allowBlank: false,
autoComplete: false,
inputAttrTpl: 'autocomplete="url"',
emptyText: gettext('hostname or IP address'),
},
{
xtype: 'proxmoxtextfield',
fieldLabel: gettext('API Key'),
name: 'truenas_api_key',
inputType: 'password',
allowBlank: !me.isCreate,
autoComplete: false,
inputAttrTpl: 'autocomplete="new-password"',
emptyText: me.isCreate
? gettext('Paste API key from TrueNAS UI')
: gettext('unchanged — paste new key to change'),
triggers: {
reveal: {
cls: 'truenas-reveal-trigger',
tooltip: gettext('Show / hide API key'),
handler: function (field) {
let dom = field.inputEl.dom;
dom.type = dom.type === 'password' ? 'text' : 'password';
},
},
},
listeners: {
afterrender: function (field) {
if (!me.isCreate) {
field.submitValue = false;
}
},
change: function (field, value) {
if (!me.isCreate) {
field.submitValue = (value && value.length > 0);
}
},
},
},
{
xtype: me.isCreate ? 'proxmoxtextfield' : 'displayfield',
fieldLabel: gettext('Pool / Dataset Path'),
name: 'truenas_pool',
allowBlank: false,
autoComplete: false,
emptyText: gettext('ZFS path where volumes live (e.g. tank or tank/proxmox/vdisks)'),
},
{
xtype: 'proxmoxtextfield',
fieldLabel: gettext('Sub-dataset'),
name: 'truenas_dataset',
allowBlank: true,
autoComplete: false,
emptyText: gettext('Leave blank — extra sub-path below Pool if needed'),
deleteEmpty: !me.isCreate,
},
];
// Column 2 — connection and multipath options (right side)
// Note: Nodes selector and Enable checkbox are prepended by StorageBase
me.column2 = [
{
xtype: 'proxmoxcheckbox',
fieldLabel: gettext('Shared'),
name: 'shared',
checked: true,
uncheckedValue: 0,
},
{
xtype: 'proxmoxcheckbox',
fieldLabel: gettext('Use SSL'),
name: 'truenas_ssl',
checked: true,
uncheckedValue: 0,
},
{
xtype: 'proxmoxcheckbox',
fieldLabel: gettext('Verify SSL Certificate'),
name: 'truenas_ssl_verify',
checked: false,
uncheckedValue: 0,
},
{
xtype: 'proxmoxtextfield',
fieldLabel: gettext('Portal IPs'),
name: 'truenas_portals',
allowBlank: false,
autoComplete: false,
emptyText: gettext('Comma-separated portal IPs (e.g. 172.31.x.x,192.168.x.x)'),
},
];
me.callParent();
},
});