1 Ext
.define('PVE.node.CephStatus', {
2 extend
: 'Ext.panel.Panel',
3 alias
: 'widget.pveNodeCephStatus',
5 onlineHelp
: 'chapter_pveceph',
20 title
: gettext('Health'),
22 plugins
: 'responsive',
48 itemId
: 'overallhealth',
49 xtype
: 'pveHealthWidget',
50 title
: gettext('Status'),
53 itemId
: 'versioninfo',
54 xtype
: 'displayfield',
55 fieldLabel
: gettext('Ceph Version'),
59 'data-qtip': gettext('The newest version installed in the Cluster.'),
63 'text-align': 'center',
72 stateId
: 'ceph-status-warnings',
74 // we load the store manually, to show an emptyText specify an empty intermediate store
79 emptyText
: gettext('No Warnings/Errors'),
82 dataIndex
: 'severity',
83 header
: gettext('Severity'),
86 renderer: function(value
) {
87 let health
= PVE
.Utils
.map_ceph_health
[value
];
88 let icon
= PVE
.Utils
.get_health_icon(health
);
89 return `<i class="fa fa-fw ${icon}'"></i>`;
92 sorterFn: function(a
, b
) {
93 let health
= ['HEALTH_ERR', 'HEALTH_WARN', 'HEALTH_OK'];
94 return health
.indexOf(b
.data
.severity
) - health
.indexOf(a
.data
.severity
);
100 header
: gettext('Summary'),
104 xtype
: 'actioncolumn',
107 tooltip
: gettext('Detail'),
110 iconCls
: 'x-fa fa-info-circle',
111 handler: function(grid
, rowindex
, colindex
, item
, e
, record
) {
112 var win
= Ext
.create('Ext.window.Window', {
113 title
: gettext('Detail'),
126 '<span>' + Ext
.htmlEncode(record
.data
.summary
) + '</span>',
127 '<pre>' + Ext
.htmlEncode(record
.data
.detail
) + '</pre>',
141 xtype
: 'pveCephStatusDetail',
142 itemId
: 'statusdetail',
143 plugins
: 'responsive',
154 title
: gettext('Status'),
157 title
: gettext('Services'),
158 xtype
: 'pveCephServices',
160 plugins
: 'responsive',
178 title
: gettext('Performance'),
191 xtype
: 'proxmoxGauge',
193 title
: gettext('Usage'),
206 itemId
: 'recoverychart',
207 xtype
: 'pveRunningChart',
208 title
: gettext('Recovery') +'/ '+ gettext('Rebalance'),
209 renderer
: PVE
.Utils
.render_bandwidth
,
213 xtype
: 'progressbar',
214 itemId
: 'recoveryprogress',
230 xtype
: 'pveRunningChart',
231 title
: gettext('Reads'),
232 renderer
: PVE
.Utils
.render_bandwidth
,
236 xtype
: 'pveRunningChart',
237 title
: gettext('Writes'),
238 renderer
: PVE
.Utils
.render_bandwidth
,
242 xtype
: 'pveRunningChart',
243 title
: 'IOPS: ' + gettext('Reads'),
244 renderer
: Ext
.util
.Format
.numberRenderer('0,000'),
248 xtype
: 'pveRunningChart',
249 title
: 'IOPS: ' + gettext('Writes'),
250 renderer
: Ext
.util
.Format
.numberRenderer('0,000'),
258 generateCheckData: function(health
) {
260 let checks
= health
.checks
|| {};
262 Object
.keys(checks
).sort().forEach(key
=> {
263 let check
= checks
[key
];
266 summary
: check
.summary
.message
,
267 detail
: check
.detail
.reduce((acc
, v
) => `${acc}\n${v.message}`, ''),
268 severity
: check
.severity
,
274 updateAll: function(store
, records
, success
) {
275 if (!success
|| records
.length
=== 0) {
280 var rec
= records
[0];
281 me
.status
= rec
.data
;
284 me
.down('#overallhealth').updateHealth(PVE
.Utils
.render_ceph_health(rec
.data
.health
|| {}));
285 // add errors to gridstore
286 me
.down('#warnings').getStore().loadRawData(me
.generateCheckData(rec
.data
.health
|| {}), false);
289 me
.getComponent('services').updateAll(me
.metadata
|| {}, rec
.data
);
291 // update detailstatus panel
292 me
.getComponent('statusdetail').updateAll(me
.metadata
|| {}, rec
.data
);
294 // add performance data
295 let pgmap
= rec
.data
.pgmap
;
296 let used
= pgmap
.bytes_used
;
297 let total
= pgmap
.bytes_total
;
299 var text
= Ext
.String
.format(gettext('{0} of {1}'),
300 Proxmox
.Utils
.render_size(used
),
301 Proxmox
.Utils
.render_size(total
),
304 // update the usage widget
305 me
.down('#space').updateValue(used
/total
, text
);
307 let readiops
= pgmap
.read_op_per_sec
;
308 let writeiops
= pgmap
.write_op_per_sec
;
309 let reads
= pgmap
.read_bytes_sec
|| 0;
310 let writes
= pgmap
.write_bytes_sec
|| 0;
313 me
.reads
.addDataPoint(reads
);
314 me
.writes
.addDataPoint(writes
);
315 me
.readiops
.addDataPoint(readiops
);
316 me
.writeiops
.addDataPoint(writeiops
);
318 let degraded
= pgmap
.degraded_objects
|| 0;
319 let misplaced
= pgmap
.misplaced_objects
|| 0;
320 let unfound
= pgmap
.unfound_objects
|| 0;
321 let unhealthy
= degraded
+ unfound
+ misplaced
;
323 if (pgmap
.recovering_objects_per_sec
!== undefined || unhealthy
> 0) {
324 let toRecover
= pgmap
.misplaced_total
|| pgmap
.unfound_total
|| pgmap
.degraded_total
|| 0;
325 if (toRecover
=== 0) {
326 return; // FIXME: unexpected return and leaves things possible visible when it shouldn't?
328 let recovered
= toRecover
- unhealthy
|| 0;
329 let speed
= pgmap
.recovering_bytes_per_sec
|| 0;
331 let recoveryRatio
= recovered
/ total
;
332 let txt
= `${(recoveryRatio * 100).toFixed(2)}%`;
334 let obj_per_sec
= speed
/ (4 * 1024 * 1024); // 4 MiB per Object
335 let duration
= Proxmox
.Utils
.format_duration_human(unhealthy
/obj_per_sec
);
336 let speedTxt
= PVE
.Utils
.render_bandwidth(speed
);
337 txt
+= ` (${speedTxt} - ${duration} left)`;
340 me
.down('#recovery').setVisible(true);
341 me
.down('#recoveryprogress').updateValue(recoveryRatio
);
342 me
.down('#recoveryprogress').updateText(txt
);
343 me
.down('#recoverychart').addDataPoint(speed
);
345 me
.down('#recovery').setVisible(false);
346 me
.down('#recoverychart').addDataPoint(0);
350 initComponent: function() {
353 var nodename
= me
.pveSelNode
.data
.node
;
356 var baseurl
= '/api2/json' + (nodename
? '/nodes/' + nodename
: '/cluster') + '/ceph';
357 me
.store
= Ext
.create('Proxmox.data.UpdateStore', {
358 storeid
: 'ceph-status-' + (nodename
|| 'cluster'),
362 url
: baseurl
+ '/status',
366 me
.metadatastore
= Ext
.create('Proxmox.data.UpdateStore', {
367 storeid
: 'ceph-metadata-' + (nodename
|| 'cluster'),
371 url
: '/api2/json/cluster/ceph/metadata',
375 // save references for the updatefunction
376 me
.iops
= me
.down('#iops');
377 me
.readiops
= me
.down('#readiops');
378 me
.writeiops
= me
.down('#writeiops');
379 me
.reads
= me
.down('#reads');
380 me
.writes
= me
.down('#writes');
382 // manages the "install ceph?" overlay
383 PVE
.Utils
.monitor_ceph_installed(me
, me
.store
, nodename
);
385 me
.mon(me
.store
, 'load', me
.updateAll
, me
);
386 me
.mon(me
.metadatastore
, 'load', function(store
, records
, success
) {
387 if (!success
|| records
.length
< 1) {
390 me
.metadata
= records
[0].data
;
393 me
.getComponent('services').updateAll(me
.metadata
, me
.status
|| {});
395 // update detailstatus panel
396 me
.getComponent('statusdetail').updateAll(me
.metadata
, me
.status
|| {});
399 let maxversiontext
= "";
400 for (const [_nodename
, data
] of Object
.entries(me
.metadata
.node
)) {
401 let version
= data
.version
.parts
;
402 if (PVE
.Utils
.compare_ceph_versions(version
, maxversion
) > 0) {
403 maxversion
= version
;
404 maxversiontext
= data
.version
.str
;
407 me
.down('#versioninfo').setValue(maxversiontext
);
410 me
.on('destroy', me
.store
.stopUpdate
);
411 me
.on('destroy', me
.metadatastore
.stopUpdate
);
412 me
.store
.startUpdate();
413 me
.metadatastore
.startUpdate();