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