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