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