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:
parent
d0093f48b3
commit
da823569a7
|
|
@ -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"
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
;;
|
||||
|
|
|
|||
|
|
@ -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 \
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
},
|
||||
});
|
||||
Loading…
Reference in New Issue