@@ -25,6 +25,75 @@ function getTransportInfo(transport) {
2525
2626// Hook into the storage Summary panel to add multipath status
2727( function ( ) {
28+ function ensureMultipathStyles ( ) {
29+ if ( Ext . get ( 'pve-multipath-styles' ) ) return ;
30+
31+ Ext . DomHelper . append ( Ext . getHead ( ) , {
32+ tag : 'style' ,
33+ id : 'pve-multipath-styles' ,
34+ html : [
35+ '.pve-mpath {' ,
36+ ' --pve-mpath-surface: var(--pwt-panel-background, #f8f8f8);' ,
37+ ' --pve-mpath-border: var(--pwt-chart-grid-stroke, #e0e0e0);' ,
38+ ' --pve-mpath-header-bg: var(--pwt-gauge-back, #e8e8e8);' ,
39+ ' --pve-mpath-summary-bg: var(--pwt-gauge-back, #f0f0f0);' ,
40+ ' --pve-mpath-muted: var(--pwt-text-color, #666);' ,
41+ ' --pve-mpath-accent: var(--pwt-chart-primary, #337ab7);' ,
42+ ' --pve-mpath-code-bg: #2d2d2d;' ,
43+ ' --pve-mpath-code-fg: #f8f8f2;' ,
44+ '}' ,
45+ '.pve-mpath .pve-mpath-device {' ,
46+ ' margin-bottom: 15px;' ,
47+ ' padding: 10px;' ,
48+ ' background: var(--pve-mpath-surface);' ,
49+ ' border: 1px solid var(--pve-mpath-border);' ,
50+ ' border-radius: 4px;' ,
51+ '}' ,
52+ '.pve-mpath .pve-mpath-muted {' ,
53+ ' color: var(--pve-mpath-muted);' ,
54+ '}' ,
55+ '.pve-mpath .pve-mpath-accent {' ,
56+ ' color: var(--pve-mpath-accent);' ,
57+ '}' ,
58+ '.pve-mpath .pve-mpath-summary {' ,
59+ ' margin-top: 10px;' ,
60+ ' padding: 8px;' ,
61+ ' background: var(--pve-mpath-summary-bg);' ,
62+ ' border-radius: 4px;' ,
63+ '}' ,
64+ '.pve-mpath .pve-mpath-empty {' ,
65+ ' margin-top: 15px;' ,
66+ ' padding: 10px;' ,
67+ ' background: var(--pve-mpath-surface);' ,
68+ ' border: 1px solid var(--pve-mpath-border);' ,
69+ ' border-radius: 3px;' ,
70+ '}' ,
71+ '.pve-mpath .pve-mpath-command {' ,
72+ ' display: block;' ,
73+ ' margin-top: 8px;' ,
74+ ' padding: 8px;' ,
75+ ' background: var(--pve-mpath-code-bg);' ,
76+ ' color: var(--pve-mpath-code-fg);' ,
77+ ' border-radius: 3px;' ,
78+ '}' ,
79+ '.pve-mpath .pve-mpath-table {' ,
80+ ' width: 100%;' ,
81+ ' border-collapse: collapse;' ,
82+ ' margin-top: 8px;' ,
83+ '}' ,
84+ '.pve-mpath .pve-mpath-table th,' ,
85+ '.pve-mpath .pve-mpath-table td {' ,
86+ ' padding: 6px;' ,
87+ ' border: 1px solid var(--pve-mpath-border);' ,
88+ ' text-align: left;' ,
89+ '}' ,
90+ '.pve-mpath .pve-mpath-table thead tr {' ,
91+ ' background: var(--pve-mpath-header-bg);' ,
92+ '}' ,
93+ ] . join ( '\n' ) ,
94+ } ) ;
95+ }
96+
2897 // Store the original initComponent
2998 let originalInit = PVE . storage . Summary . prototype . initComponent ;
3099
@@ -65,6 +134,8 @@ function getTransportInfo(transport) {
65134 PVE . storage . Summary . prototype . addMultipathStatusPanel = function ( storageConfig , storeid , nodename ) {
66135 let me = this ;
67136
137+ ensureMultipathStyles ( ) ;
138+
68139 // Do not add if already exists
69140 if ( me . down ( '#multipathStatus' ) ) return ;
70141
@@ -124,7 +195,7 @@ function getTransportInfo(transport) {
124195 } ;
125196
126197 PVE . storage . Summary . prototype . renderMultipathStatus = function ( panel , storageConfig , statusData , pathData ) {
127- let html = '<table class="pve-infotable" style="width: 100%;">' ;
198+ let html = '<div class="pve-mpath">< table class="pve-infotable" style="width: 100%;">' ;
128199
129200 // Determine storage type display
130201 let hasMultipath = statusData . multipathStatus &&
@@ -195,7 +266,7 @@ function getTransportInfo(transport) {
195266 }
196267 } ) ;
197268
198- html += '<div style="margin-top: 10px; padding: 8px; background: #f0f0f0; border-radius: 4px; ">' ;
269+ html += '<div class="pve-mpath-summary ">' ;
199270 if ( activePaths === totalPaths ) {
200271 html += '<span style="color: #5cb85c;"><i class="fa fa-check-circle"></i> ' +
201272 activePaths + '/' + totalPaths + ' ' + gettext ( 'paths active' ) + '</span>' ;
@@ -206,18 +277,19 @@ function getTransportInfo(transport) {
206277 html += '</div>' ;
207278 html += '</div>' ;
208279 } else {
209- html += '<div style="margin-top: 15px; padding: 10px; background: #f8f8f8; border: 1px solid #e0e0e0; border-radius: 3px; ">' ;
210- html += '<div style="color: #999; "><i class="fa fa-info-circle"></i> ' +
280+ html += '<div class="pve-mpath-empty ">' ;
281+ html += '<div class="pve-mpath-muted "><i class="fa fa-info-circle"></i> ' +
211282 gettext ( 'Path details not available. Run on the node:' ) + '</div>' ;
212- html += '<code style="display: block; margin-top: 8px; padding: 8px; background: #2d2d2d; color: #f8f8f2; border-radius: 3px; ">multipath -ll</code>' ;
283+ html += '<code class="pve-mpath-command ">multipath -ll</code>' ;
213284 html += '</div>' ;
214285 }
215286
287+ html += '</div>' ;
216288 panel . update ( html ) ;
217289 } ;
218290
219291 PVE . storage . Summary . prototype . renderMultipathDevice = function ( device , storageType ) {
220- let html = '<div style="margin-bottom: 15px; padding: 10px; background: #f8f8f8; border: 1px solid #e0e0e0; border-radius: 4px; ">' ;
292+ let html = '<div class="pve-mpath-device ">' ;
221293
222294 // Detect NVMe native multipath based on storage type or device properties
223295 // LVM on NVMe will have nqn/subsystem set, or paths starting with 'nvme'
@@ -246,15 +318,15 @@ function getTransportInfo(transport) {
246318 html += '<i class="fa fa-hdd-o"></i> /dev/mapper/' + Ext . htmlEncode ( device . name ) ;
247319 }
248320 if ( device . size ) {
249- html += ' <span style="color: #666; font-weight: normal;">(' + Ext . htmlEncode ( device . size ) + ')</span>' ;
321+ html += ' <span class="pve-mpath-muted" style=" font-weight: normal;">(' + Ext . htmlEncode ( device . size ) + ')</span>' ;
250322 }
251323 if ( device . pv_path ) {
252- html += ' <span style="color: #337ab7; font-weight: normal;">→ PV: ' + Ext . htmlEncode ( device . pv_path ) + '</span>' ;
324+ html += ' <span class="pve-mpath-accent" style=" font-weight: normal;">→ PV: ' + Ext . htmlEncode ( device . pv_path ) + '</span>' ;
253325 }
254326 html += '</div>' ;
255327
256328 // Device details - different for NVMe vs SCSI
257- html += '<div style="font-size: 0.9em; color: #666 ; margin-bottom: 8px;">' ;
329+ html += '<div class="pve-mpath-muted" style="font-size: 0.9em; margin-bottom: 8px;">' ;
258330 if ( isNvmeNative ) {
259331 html += 'NQN: ' + Ext . htmlEncode ( device . nqn || device . wwid || 'unknown' ) ;
260332 if ( device . subsystem ) {
@@ -274,18 +346,18 @@ function getTransportInfo(transport) {
274346
275347 // Path table - adjust columns based on multipath type
276348 if ( device . paths && device . paths . length > 0 ) {
277- html += '<table style="width: 100%; border-collapse: collapse; margin-top: 8px; ">' ;
278- html += '<thead><tr style="background: #e8e8e8;" >' ;
279- html += '<th style="padding: 6px; border: 1px solid #ddd; text-align: left;" >' + gettext ( 'Device' ) + '</th>' ;
349+ html += '<table class="pve-mpath-table ">' ;
350+ html += '<thead><tr>' ;
351+ html += '<th>' + gettext ( 'Device' ) + '</th>' ;
280352 if ( isNvmeNative ) {
281- html += '<th style="padding: 6px; border: 1px solid #ddd; text-align: left;" >' + gettext ( 'Controller' ) + '</th>' ;
353+ html += '<th>' + gettext ( 'Controller' ) + '</th>' ;
282354 } else {
283- html += '<th style="padding: 6px; border: 1px solid #ddd; text-align: left;" >HCTL</th>' ;
355+ html += '<th>HCTL</th>' ;
284356 }
285- html += '<th style="padding: 6px; border: 1px solid #ddd; text-align: left;" >' + gettext ( 'Transport' ) + '</th>' ;
286- html += '<th style="padding: 6px; border: 1px solid #ddd; text-align: left;" >' + gettext ( 'Portal/Address' ) + '</th>' ;
287- html += '<th style="padding: 6px; border: 1px solid #ddd; text-align: left;" >' + gettext ( 'Interface' ) + '</th>' ;
288- html += '<th style="padding: 6px; border: 1px solid #ddd; text-align: left;" >' + gettext ( 'State' ) + '</th>' ;
357+ html += '<th>' + gettext ( 'Transport' ) + '</th>' ;
358+ html += '<th>' + gettext ( 'Portal/Address' ) + '</th>' ;
359+ html += '<th>' + gettext ( 'Interface' ) + '</th>' ;
360+ html += '<th>' + gettext ( 'State' ) + '</th>' ;
289361 html += '</tr></thead><tbody>' ;
290362
291363 device . paths . forEach ( function ( path ) {
@@ -301,19 +373,19 @@ function getTransportInfo(transport) {
301373 let transportInfo = getTransportInfo ( path . transport || 'unknown' ) ;
302374
303375 html += '<tr>' ;
304- html += '<td style="padding: 6px; border: 1px solid #ddd;" ><code>/dev/' + Ext . htmlEncode ( path . device ) + '</code></td>' ;
376+ html += '<td><code>/dev/' + Ext . htmlEncode ( path . device ) + '</code></td>' ;
305377 if ( isNvmeNative ) {
306378 // Show controller name for NVMe (e.g., nvme0, nvme1)
307- html += '<td style="padding: 6px; border: 1px solid #ddd;" >' + Ext . htmlEncode ( path . controller || '-' ) + '</td>' ;
379+ html += '<td>' + Ext . htmlEncode ( path . controller || '-' ) + '</td>' ;
308380 } else {
309- html += '<td style="padding: 6px; border: 1px solid #ddd;" >' + Ext . htmlEncode ( path . hctl || '-' ) + '</td>' ;
381+ html += '<td>' + Ext . htmlEncode ( path . hctl || '-' ) + '</td>' ;
310382 }
311- html += '<td style="padding: 6px; border: 1px solid #ddd; color: ' + transportInfo . color + ';">' ;
383+ html += '<td style="color: ' + transportInfo . color + ';">' ;
312384 html += '<i class="fa ' + transportInfo . icon + '"></i> ' + transportInfo . label ;
313385 html += '</td>' ;
314- html += '<td style="padding: 6px; border: 1px solid #ddd;" >' + Ext . htmlEncode ( path . portal || '-' ) + '</td>' ;
315- html += '<td style="padding: 6px; border: 1px solid #ddd;" >' + Ext . htmlEncode ( path . hostIface || '-' ) + '</td>' ;
316- html += '<td style="padding: 6px; border: 1px solid #ddd; color: ' + stateColor + ';">' ;
386+ html += '<td>' + Ext . htmlEncode ( path . portal || '-' ) + '</td>' ;
387+ html += '<td>' + Ext . htmlEncode ( path . hostIface || '-' ) + '</td>' ;
388+ html += '<td style="color: ' + stateColor + ';">' ;
317389 html += '<i class="fa ' + stateIcon + '"></i> ' + Ext . htmlEncode ( path . state ) ;
318390 html += '</td>' ;
319391 html += '</tr>' ;
0 commit comments