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