]> git.proxmox.com Git - pve-manager.git/blame - www/manager6/ceph/Status.js
api tools: fix usage of stat by-name interface
[pve-manager.git] / www / manager6 / ceph / Status.js
CommitLineData
bd39c945 1Ext.define('PVE.node.CephStatus', {
946730cd
DC
2 extend: 'Ext.panel.Panel',
3 alias: 'widget.pveNodeCephStatus',
4
ba93a9c6 5 onlineHelp: 'chapter_pveceph',
bd39c945 6
946730cd 7 scrollable: true,
9f7cbaf3 8 bodyPadding: 5,
9f7cbaf3 9 layout: {
f6710aac 10 type: 'column',
9f7cbaf3 11 },
946730cd
DC
12
13 defaults: {
f6710aac 14 padding: 5,
946730cd
DC
15 },
16
17 items: [
18 {
19 xtype: 'panel',
20 title: gettext('Health'),
9f7cbaf3
DC
21 bodyPadding: 10,
22 plugins: 'responsive',
23 responsiveConfig: {
35279245 24 'width < 1600': {
27b91275 25 minHeight: 230,
f6710aac 26 columnWidth: 1,
9f7cbaf3 27 },
35279245 28 'width >= 1600': {
27b91275 29 minHeight: 500,
f6710aac
TL
30 columnWidth: 0.5,
31 },
9f7cbaf3 32 },
946730cd
DC
33 layout: {
34 type: 'hbox',
f6710aac 35 align: 'stretch',
946730cd
DC
36 },
37 items: [
38 {
949a6609
DC
39 xtype: 'container',
40 layout: {
41 type: 'vbox',
42 align: 'stretch',
43 },
946730cd 44 flex: 1,
949a6609
DC
45 items: [
46 {
616d54de 47
949a6609 48 xtype: 'pveHealthWidget',
616d54de
TL
49 itemId: 'overallhealth',
50 flex: 1,
f6710aac 51 title: gettext('Status'),
949a6609
DC
52 },
53 {
0beb2578 54 xtype: 'displayfield',
616d54de 55 itemId: 'versioninfo',
0beb2578
TL
56 fieldLabel: gettext('Ceph Version'),
57 value: "",
58 autoEl: {
59 tag: 'div',
60 'data-qtip': gettext('The newest version installed in the Cluster.'),
949a6609
DC
61 },
62 padding: '10 0 0 0',
63 style: {
64 'text-align': 'center',
65 },
f6710aac 66 },
0beb2578 67 ],
946730cd
DC
68 },
69 {
616d54de 70 xtype: 'grid',
946730cd 71 itemId: 'warnings',
616d54de 72 flex: 2,
946730cd
DC
73 stateful: true,
74 stateId: 'ceph-status-warnings',
10b00f3a 75 // we load the store manually, to show an emptyText specify an empty intermediate store
23f14fd9 76 store: {
ca0267fd 77 trackRemoved: false,
23f14fd9
TL
78 data: [],
79 },
b8febbcc
TL
80 updateHealth: function(health) {
81 let checks = health.checks || {};
82
83 let checkRecords = Object.keys(checks).sort().map(key => {
84 let check = checks[key];
85 return {
86 id: key,
87 summary: check.summary.message,
88 detail: check.detail.reduce((acc, v) => `${acc}\n${v.message}`, ''),
89 severity: check.severity,
90 };
91 });
92
93 this.getStore().loadRawData(checkRecords, false);
94 },
946730cd
DC
95 emptyText: gettext('No Warnings/Errors'),
96 columns: [
97 {
98 dataIndex: 'severity',
99 header: gettext('Severity'),
100 align: 'center',
101 width: 70,
102 renderer: function(value) {
10b00f3a
TL
103 let health = PVE.Utils.map_ceph_health[value];
104 let icon = PVE.Utils.get_health_icon(health);
5f2e6c2e 105 return `<i class="fa fa-fw ${icon}"></i>`;
946730cd
DC
106 },
107 sorter: {
f6710aac 108 sorterFn: function(a, b) {
10b00f3a
TL
109 let health = ['HEALTH_ERR', 'HEALTH_WARN', 'HEALTH_OK'];
110 return health.indexOf(b.data.severity) - health.indexOf(a.data.severity);
f6710aac
TL
111 },
112 },
946730cd
DC
113 },
114 {
115 dataIndex: 'summary',
116 header: gettext('Summary'),
f6710aac 117 flex: 1,
e932cd5f
DC
118 },
119 {
120 xtype: 'actioncolumn',
121 width: 40,
122 align: 'center',
123 tooltip: gettext('Detail'),
124 items: [
125 {
126 iconCls: 'x-fa fa-info-circle',
127 handler: function(grid, rowindex, colindex, item, e, record) {
128 var win = Ext.create('Ext.window.Window', {
129 title: gettext('Detail'),
130 resizable: true,
bf6e58d2 131 modal: true,
e932cd5f
DC
132 width: 650,
133 height: 400,
134 layout: {
f6710aac 135 type: 'fit',
e932cd5f
DC
136 },
137 items: [{
138 scrollable: true,
9f7cbaf3 139 padding: 10,
e932cd5f 140 xtype: 'box',
b01e7f7a
DC
141 html: [
142 '<span>' + Ext.htmlEncode(record.data.summary) + '</span>',
f6710aac
TL
143 '<pre>' + Ext.htmlEncode(record.data.detail) + '</pre>',
144 ],
145 }],
e932cd5f
DC
146 });
147 win.show();
f6710aac
TL
148 },
149 },
150 ],
151 },
152 ],
153 },
154 ],
946730cd
DC
155 },
156 {
157 xtype: 'pveCephStatusDetail',
158 itemId: 'statusdetail',
9f7cbaf3
DC
159 plugins: 'responsive',
160 responsiveConfig: {
35279245 161 'width < 1600': {
27b91275 162 columnWidth: 1,
f6710aac 163 minHeight: 250,
9f7cbaf3 164 },
35279245 165 'width >= 1600': {
27b91275 166 columnWidth: 0.5,
f6710aac
TL
167 minHeight: 300,
168 },
9f7cbaf3 169 },
f6710aac 170 title: gettext('Status'),
946730cd 171 },
4ad4262d 172 {
4ad4262d 173 xtype: 'pveCephServices',
616d54de 174 title: gettext('Services'),
4ad4262d
DC
175 itemId: 'services',
176 plugins: 'responsive',
177 layout: {
178 type: 'hbox',
f6710aac 179 align: 'stretch',
4ad4262d
DC
180 },
181 responsiveConfig: {
35279245 182 'width < 1600': {
4ad4262d 183 columnWidth: 1,
f6710aac 184 minHeight: 200,
4ad4262d 185 },
35279245 186 'width >= 1600': {
4ad4262d 187 columnWidth: 0.5,
f6710aac
TL
188 minHeight: 200,
189 },
190 },
4ad4262d 191 },
946730cd
DC
192 {
193 xtype: 'panel',
194 title: gettext('Performance'),
9f7cbaf3
DC
195 columnWidth: 1,
196 bodyPadding: 5,
946730cd
DC
197 layout: {
198 type: 'hbox',
f6710aac 199 align: 'center',
946730cd
DC
200 },
201 items: [
202 {
62281115 203 xtype: 'container',
616d54de 204 flex: 1,
62281115
DC
205 items: [
206 {
207 xtype: 'proxmoxGauge',
208 itemId: 'space',
f6710aac 209 title: gettext('Usage'),
62281115
DC
210 },
211 {
212 flex: 1,
213 border: false,
214 },
215 {
216 xtype: 'container',
217 itemId: 'recovery',
218 hidden: true,
219 padding: 25,
220 items: [
221 {
62281115 222 xtype: 'pveRunningChart',
616d54de 223 itemId: 'recoverychart',
e767e1ec 224 title: gettext('Recovery') +'/ '+ gettext('Rebalance'),
62281115
DC
225 renderer: PVE.Utils.render_bandwidth,
226 height: 100,
227 },
228 {
229 xtype: 'progressbar',
230 itemId: 'recoveryprogress',
231 },
f6710aac 232 ],
62281115 233 },
f6710aac 234 ],
946730cd
DC
235 },
236 {
946730cd 237 xtype: 'container',
616d54de 238 flex: 2,
946730cd 239 defaults: {
9f7cbaf3 240 padding: 0,
f6710aac 241 height: 100,
946730cd
DC
242 },
243 items: [
244 {
946730cd 245 xtype: 'pveRunningChart',
616d54de 246 itemId: 'reads',
946730cd 247 title: gettext('Reads'),
f6710aac 248 renderer: PVE.Utils.render_bandwidth,
946730cd
DC
249 },
250 {
946730cd 251 xtype: 'pveRunningChart',
616d54de 252 itemId: 'writes',
946730cd 253 title: gettext('Writes'),
f6710aac 254 renderer: PVE.Utils.render_bandwidth,
946730cd 255 },
946730cd 256 {
946730cd 257 xtype: 'pveRunningChart',
616d54de 258 itemId: 'readiops',
8f8ec25d 259 title: 'IOPS: ' + gettext('Reads'),
f6710aac 260 renderer: Ext.util.Format.numberRenderer('0,000'),
946730cd
DC
261 },
262 {
946730cd 263 xtype: 'pveRunningChart',
616d54de 264 itemId: 'writeiops',
8f8ec25d 265 title: 'IOPS: ' + gettext('Writes'),
f6710aac 266 renderer: Ext.util.Format.numberRenderer('0,000'),
df503ff9 267 },
f6710aac
TL
268 ],
269 },
270 ],
271 },
946730cd 272 ],
bd39c945 273
946730cd
DC
274 updateAll: function(store, records, success) {
275 if (!success || records.length === 0) {
276 return;
277 }
bd39c945 278
946730cd
DC
279 var me = this;
280 var rec = records[0];
0bf3c581 281 me.status = rec.data;
bd39c945 282
946730cd 283 // add health panel
dfe6d184 284 me.down('#overallhealth').updateHealth(PVE.Utils.render_ceph_health(rec.data.health || {}));
b8febbcc 285 me.down('#warnings').updateHealth(rec.data.health || {}); // add errors to gridstore
bd39c945 286
4ad4262d
DC
287 me.getComponent('services').updateAll(me.metadata || {}, rec.data);
288
0bf3c581 289 me.getComponent('statusdetail').updateAll(me.metadata || {}, rec.data);
bd39c945 290
946730cd 291 // add performance data
df503ff9
TL
292 let pgmap = rec.data.pgmap;
293 let used = pgmap.bytes_used;
294 let total = pgmap.bytes_total;
bd39c945 295
946730cd 296 var text = Ext.String.format(gettext('{0} of {1}'),
1bd7bcdb
DC
297 Proxmox.Utils.render_size(used),
298 Proxmox.Utils.render_size(total),
946730cd 299 );
bd39c945 300
946730cd
DC
301 // update the usage widget
302 me.down('#space').updateValue(used/total, text);
bd39c945 303
df503ff9
TL
304 let readiops = pgmap.read_op_per_sec;
305 let writeiops = pgmap.write_op_per_sec;
306 let reads = pgmap.read_bytes_sec || 0;
307 let writes = pgmap.write_bytes_sec || 0;
bd39c945 308
946730cd
DC
309 // update the graphs
310 me.reads.addDataPoint(reads);
311 me.writes.addDataPoint(writes);
946730cd
DC
312 me.readiops.addDataPoint(readiops);
313 me.writeiops.addDataPoint(writeiops);
62281115
DC
314
315 let degraded = pgmap.degraded_objects || 0;
316 let misplaced = pgmap.misplaced_objects || 0;
317 let unfound = pgmap.unfound_objects || 0;
318 let unhealthy = degraded + unfound + misplaced;
319 // update recovery
320 if (pgmap.recovering_objects_per_sec !== undefined || unhealthy > 0) {
cb2013db
DC
321 let toRecoverObjects = pgmap.misplaced_total || pgmap.unfound_total || pgmap.degraded_total || 0;
322 if (toRecoverObjects === 0) {
10b00f3a
TL
323 return; // FIXME: unexpected return and leaves things possible visible when it shouldn't?
324 }
cb2013db 325 let recovered = toRecoverObjects - unhealthy || 0;
62281115 326 let speed = pgmap.recovering_bytes_per_sec || 0;
62281115 327
cb2013db 328 let recoveryRatio = recovered / toRecoverObjects;
10b00f3a 329 let txt = `${(recoveryRatio * 100).toFixed(2)}%`;
62281115 330 if (speed > 0) {
10b00f3a
TL
331 let obj_per_sec = speed / (4 * 1024 * 1024); // 4 MiB per Object
332 let duration = Proxmox.Utils.format_duration_human(unhealthy/obj_per_sec);
333 let speedTxt = PVE.Utils.render_bandwidth(speed);
62281115
DC
334 txt += ` (${speedTxt} - ${duration} left)`;
335 }
336
337 me.down('#recovery').setVisible(true);
10b00f3a 338 me.down('#recoveryprogress').updateValue(recoveryRatio);
62281115
DC
339 me.down('#recoveryprogress').updateText(txt);
340 me.down('#recoverychart').addDataPoint(speed);
341 } else {
342 me.down('#recovery').setVisible(false);
343 me.down('#recoverychart').addDataPoint(0);
344 }
946730cd
DC
345 },
346
946730cd
DC
347 initComponent: function() {
348 var me = this;
bd39c945 349
946730cd 350 var nodename = me.pveSelNode.data.node;
bd39c945 351
946730cd 352 me.callParent();
d2193664 353 var baseurl = '/api2/json' + (nodename ? '/nodes/' + nodename : '/cluster') + '/ceph';
0c7c0d6b 354 me.store = Ext.create('Proxmox.data.UpdateStore', {
2365c5c1 355 storeid: 'ceph-status-' + (nodename || 'cluster'),
946730cd
DC
356 interval: 5000,
357 proxy: {
56a353b9 358 type: 'proxmox',
f6710aac
TL
359 url: baseurl + '/status',
360 },
bd39c945
DM
361 });
362
7f58689d
DC
363 me.metadatastore = Ext.create('Proxmox.data.UpdateStore', {
364 storeid: 'ceph-metadata-' + (nodename || 'cluster'),
365 interval: 15*1000,
366 proxy: {
367 type: 'proxmox',
f6710aac
TL
368 url: '/api2/json/cluster/ceph/metadata',
369 },
7f58689d
DC
370 });
371
946730cd
DC
372 // save references for the updatefunction
373 me.iops = me.down('#iops');
374 me.readiops = me.down('#readiops');
375 me.writeiops = me.down('#writeiops');
376 me.reads = me.down('#reads');
377 me.writes = me.down('#writes');
378
13786fb0
TL
379 // manages the "install ceph?" overlay
380 PVE.Utils.monitor_ceph_installed(me, me.store, nodename);
4616a55b 381
946730cd 382 me.mon(me.store, 'load', me.updateAll, me);
7f58689d
DC
383 me.mon(me.metadatastore, 'load', function(store, records, success) {
384 if (!success || records.length < 1) {
385 return;
386 }
10b00f3a 387 me.metadata = records[0].data;
7f58689d 388
4ad4262d 389 // update services
10b00f3a 390 me.getComponent('services').updateAll(me.metadata, me.status || {});
4ad4262d 391
7f58689d 392 // update detailstatus panel
10b00f3a 393 me.getComponent('statusdetail').updateAll(me.metadata, me.status || {});
7f58689d 394
949a6609
DC
395 let maxversion = [];
396 let maxversiontext = "";
10b00f3a 397 for (const [_nodename, data] of Object.entries(me.metadata.node)) {
949a6609
DC
398 let version = data.version.parts;
399 if (PVE.Utils.compare_ceph_versions(version, maxversion) > 0) {
400 maxversion = version;
949a6609
DC
401 maxversiontext = data.version.str;
402 }
403 }
0beb2578 404 me.down('#versioninfo').setValue(maxversiontext);
7f58689d
DC
405 }, me);
406
946730cd 407 me.on('destroy', me.store.stopUpdate);
4ad4262d 408 me.on('destroy', me.metadatastore.stopUpdate);
946730cd 409 me.store.startUpdate();
4ad4262d 410 me.metadatastore.startUpdate();
f6710aac 411 },
946730cd 412
bd39c945 413});