]>
Commit | Line | Data |
---|---|---|
4ea09218 | 1 | Ext.define('PVE.CephCreateOsd', { |
9fccc702 | 2 | extend: 'Proxmox.window.Edit', |
4d3e918a | 3 | xtype: 'pveCephCreateOsd', |
4ea09218 DM |
4 | |
5 | subject: 'Ceph OSD', | |
6 | ||
7 | showProgress: true, | |
8 | ||
35085f4a TL |
9 | onlineHelp: 'pve_ceph_osds', |
10 | ||
4f14c9c9 TL |
11 | initComponent: function() { |
12 | let me = this; | |
4ea09218 DM |
13 | |
14 | if (!me.nodename) { | |
15 | throw "no node name specified"; | |
16 | } | |
17 | ||
d5e771ce | 18 | me.isCreate = true; |
c474314e | 19 | |
e7ec42ff AL |
20 | Proxmox.Utils.API2Request({ |
21 | url: `/nodes/${me.nodename}/ceph/crush`, | |
22 | method: 'GET', | |
23 | failure: response => Ext.Msg.alert(gettext('Error'), response.htmlStatus), | |
24 | success: function({ result: { data } }) { | |
25 | let classes = [...new Set( | |
26 | Array.from( | |
27 | data.matchAll(/^device\s[0-9]*\sosd\.[0-9]*\sclass\s(.*)$/gim), | |
28 | m => m[1], | |
29 | ).filter(v => !['hdd', 'ssd', 'nvme'].includes(v)), | |
30 | )].map(v => [v, v]); | |
31 | ||
32 | if (classes.length) { | |
33 | let kvField = me.down('field[name=crush-device-class]'); | |
16c6f8af | 34 | kvField.setComboItems([...kvField.comboItems, ...classes]); |
e7ec42ff AL |
35 | } |
36 | }, | |
37 | }); | |
38 | ||
4ea09218 DM |
39 | Ext.applyIf(me, { |
40 | url: "/nodes/" + me.nodename + "/ceph/osd", | |
9ad28182 DC |
41 | method: 'POST', |
42 | items: [ | |
43 | { | |
4d3e918a | 44 | xtype: 'inputpanel', |
4c94e9de DC |
45 | onGetValues: function(values) { |
46 | Object.keys(values || {}).forEach(function(name) { | |
47 | if (values[name] === '') { | |
48 | delete values[name]; | |
49 | } | |
50 | }); | |
51 | ||
52 | return values; | |
53 | }, | |
4d3e918a DC |
54 | column1: [ |
55 | { | |
d0cfe2ea | 56 | xtype: 'pmxDiskSelector', |
4d3e918a DC |
57 | name: 'dev', |
58 | nodename: me.nodename, | |
59 | diskType: 'unused', | |
76e0823a | 60 | includePartitions: true, |
4d3e918a | 61 | fieldLabel: gettext('Disk'), |
4f14c9c9 TL |
62 | allowBlank: false, |
63 | }, | |
4d3e918a DC |
64 | ], |
65 | column2: [ | |
66 | { | |
d0cfe2ea | 67 | xtype: 'pmxDiskSelector', |
4d3e918a DC |
68 | name: 'db_dev', |
69 | nodename: me.nodename, | |
70 | diskType: 'journal_disks', | |
76e0823a | 71 | includePartitions: true, |
4d3e918a DC |
72 | fieldLabel: gettext('DB Disk'), |
73 | value: '', | |
74 | autoSelect: false, | |
75 | allowBlank: true, | |
76 | emptyText: 'use OSD disk', | |
77 | listeners: { | |
78 | change: function(field, val) { | |
596bb7b1 | 79 | me.down('field[name=db_dev_size]').setDisabled(!val); |
4f14c9c9 TL |
80 | }, |
81 | }, | |
4d3e918a DC |
82 | }, |
83 | { | |
84 | xtype: 'numberfield', | |
596bb7b1 | 85 | name: 'db_dev_size', |
4d3e918a DC |
86 | fieldLabel: gettext('DB size') + ' (GiB)', |
87 | minValue: 1, | |
88 | maxValue: 128*1024, | |
89 | decimalPrecision: 2, | |
90 | allowBlank: true, | |
91 | disabled: true, | |
4f14c9c9 TL |
92 | emptyText: gettext('Automatic'), |
93 | }, | |
4d3e918a DC |
94 | ], |
95 | advancedColumn1: [ | |
6c311f2c DC |
96 | { |
97 | xtype: 'proxmoxcheckbox', | |
98 | name: 'encrypted', | |
4f14c9c9 | 99 | fieldLabel: gettext('Encrypt OSD'), |
6c311f2c | 100 | }, |
c4f39f9a AA |
101 | { |
102 | xtype: 'proxmoxKVComboBox', | |
103 | comboItems: [ | |
104 | ['hdd', 'HDD'], | |
105 | ['ssd', 'SSD'], | |
37dbbda8 | 106 | ['nvme', 'NVMe'], |
c4f39f9a AA |
107 | ], |
108 | name: 'crush-device-class', | |
109 | nodename: me.nodename, | |
110 | fieldLabel: gettext('Device Class'), | |
111 | value: '', | |
112 | autoSelect: false, | |
113 | allowBlank: true, | |
114 | editable: true, | |
115 | emptyText: 'auto detect', | |
4f14c9c9 | 116 | deleteEmpty: !me.isCreate, |
c4f39f9a | 117 | }, |
6c311f2c DC |
118 | ], |
119 | advancedColumn2: [ | |
4d3e918a | 120 | { |
d0cfe2ea | 121 | xtype: 'pmxDiskSelector', |
4d3e918a DC |
122 | name: 'wal_dev', |
123 | nodename: me.nodename, | |
124 | diskType: 'journal_disks', | |
76e0823a | 125 | includePartitions: true, |
4d3e918a DC |
126 | fieldLabel: gettext('WAL Disk'), |
127 | value: '', | |
128 | autoSelect: false, | |
129 | allowBlank: true, | |
130 | emptyText: 'use OSD/DB disk', | |
131 | listeners: { | |
132 | change: function(field, val) { | |
596bb7b1 | 133 | me.down('field[name=wal_dev_size]').setDisabled(!val); |
4f14c9c9 TL |
134 | }, |
135 | }, | |
4d3e918a DC |
136 | }, |
137 | { | |
138 | xtype: 'numberfield', | |
596bb7b1 | 139 | name: 'wal_dev_size', |
4d3e918a DC |
140 | fieldLabel: gettext('WAL size') + ' (GiB)', |
141 | minValue: 0.5, | |
142 | maxValue: 128*1024, | |
143 | decimalPrecision: 2, | |
144 | allowBlank: true, | |
145 | disabled: true, | |
4f14c9c9 TL |
146 | emptyText: gettext('Automatic'), |
147 | }, | |
148 | ], | |
deca45c1 TL |
149 | }, |
150 | { | |
151 | xtype: 'displayfield', | |
152 | padding: '5 0 0 0', | |
f71b7c28 | 153 | userCls: 'pmx-hint', |
deca45c1 TL |
154 | value: 'Note: Ceph is not compatible with disks backed by a hardware ' + |
155 | 'RAID controller. For details see ' + | |
156 | '<a target="_blank" href="' + Proxmox.Utils.get_help_link('chapter_pveceph') + '">the reference documentation</a>.', | |
4f14c9c9 TL |
157 | }, |
158 | ], | |
4d3e918a | 159 | }); |
4ea09218 | 160 | |
4d3e918a | 161 | me.callParent(); |
4f14c9c9 | 162 | }, |
4ea09218 DM |
163 | }); |
164 | ||
165 | Ext.define('PVE.CephRemoveOsd', { | |
9fccc702 | 166 | extend: 'Proxmox.window.Edit', |
4ea09218 DM |
167 | alias: ['widget.pveCephRemoveOsd'], |
168 | ||
169 | isRemove: true, | |
170 | ||
171 | showProgress: true, | |
89cd5a3f DC |
172 | method: 'DELETE', |
173 | items: [ | |
174 | { | |
896c0d50 | 175 | xtype: 'proxmoxcheckbox', |
89cd5a3f DC |
176 | name: 'cleanup', |
177 | checked: true, | |
178 | labelWidth: 130, | |
4f14c9c9 TL |
179 | fieldLabel: gettext('Cleanup Disks'), |
180 | }, | |
d9415cc4 AL |
181 | { |
182 | xtype: 'displayfield', | |
183 | name: 'osd-flag-hint', | |
184 | userCls: 'pmx-hint', | |
185 | value: gettext('Global flags limiting the self healing of Ceph are enabled.'), | |
186 | hidden: true, | |
187 | }, | |
188 | { | |
189 | xtype: 'displayfield', | |
190 | name: 'degraded-objects-hint', | |
191 | userCls: 'pmx-hint', | |
192 | value: gettext('Objects are degraded. Consider waiting until the cluster is healthy.'), | |
193 | hidden: true, | |
194 | }, | |
89cd5a3f | 195 | ], |
4f14c9c9 TL |
196 | initComponent: function() { |
197 | let me = this; | |
4ea09218 DM |
198 | |
199 | if (!me.nodename) { | |
200 | throw "no node name specified"; | |
201 | } | |
202 | if (me.osdid === undefined || me.osdid < 0) { | |
203 | throw "no osdid specified"; | |
204 | } | |
205 | ||
d5e771ce | 206 | me.isCreate = true; |
4ea09218 | 207 | |
fb0e1813 | 208 | me.title = gettext('Destroy') + ': Ceph OSD osd.' + me.osdid.toString(); |
c474314e | 209 | |
4ea09218 | 210 | Ext.applyIf(me, { |
4f14c9c9 | 211 | url: "/nodes/" + me.nodename + "/ceph/osd/" + me.osdid.toString(), |
4ea09218 DM |
212 | }); |
213 | ||
214 | me.callParent(); | |
d9415cc4 AL |
215 | |
216 | if (me.warnings.flags) { | |
217 | me.down('field[name=osd-flag-hint]').setHidden(false); | |
218 | } | |
219 | if (me.warnings.degraded) { | |
220 | me.down('field[name=degraded-objects-hint]').setHidden(false); | |
221 | } | |
4f14c9c9 | 222 | }, |
4ea09218 DM |
223 | }); |
224 | ||
5960a66c DC |
225 | Ext.define('PVE.CephSetFlags', { |
226 | extend: 'Proxmox.window.Edit', | |
227 | xtype: 'pveCephSetFlags', | |
228 | ||
5960a66c DC |
229 | showProgress: true, |
230 | ||
844a81ba | 231 | width: 720, |
844a81ba TL |
232 | layout: 'fit', |
233 | ||
5960a66c DC |
234 | onlineHelp: 'pve_ceph_osds', |
235 | isCreate: true, | |
60456379 TL |
236 | title: Ext.String.format(gettext('Manage {0}'), 'Global OSD Flags'), |
237 | submitText: gettext('Apply'), | |
5960a66c DC |
238 | |
239 | items: [ | |
240 | { | |
241 | xtype: 'inputpanel', | |
242 | onGetValues: function(values) { | |
4f14c9c9 TL |
243 | let me = this; |
244 | let val = {}; | |
245 | me.down('#flaggrid').getStore().each((rec) => { | |
5960a66c DC |
246 | val[rec.data.name] = rec.data.value ? 1 : 0; |
247 | }); | |
248 | ||
249 | return val; | |
250 | }, | |
251 | items: [ | |
252 | { | |
253 | xtype: 'grid', | |
254 | itemId: 'flaggrid', | |
f6556196 TL |
255 | store: { |
256 | listeners: { | |
257 | update: function() { | |
258 | this.commitChanges(); | |
4f14c9c9 TL |
259 | }, |
260 | }, | |
f6556196 | 261 | }, |
5960a66c DC |
262 | |
263 | columns: [ | |
264 | { | |
265 | text: gettext('Enable'), | |
266 | xtype: 'checkcolumn', | |
844a81ba | 267 | width: 75, |
5960a66c DC |
268 | dataIndex: 'value', |
269 | }, | |
270 | { | |
271 | text: 'Name', | |
272 | dataIndex: 'name', | |
273 | }, | |
274 | { | |
275 | text: 'Description', | |
276 | flex: 1, | |
277 | dataIndex: 'description', | |
278 | }, | |
4f14c9c9 | 279 | ], |
60456379 | 280 | }, |
5960a66c DC |
281 | ], |
282 | }, | |
283 | ], | |
284 | ||
4f14c9c9 TL |
285 | initComponent: function() { |
286 | let me = this; | |
5960a66c DC |
287 | |
288 | if (!me.nodename) { | |
289 | throw "no node name specified"; | |
290 | } | |
291 | ||
292 | Ext.applyIf(me, { | |
1225095d | 293 | url: "/cluster/ceph/flags", |
5960a66c DC |
294 | method: 'PUT', |
295 | }); | |
296 | ||
297 | me.callParent(); | |
844a81ba | 298 | |
4f14c9c9 | 299 | let grid = me.down('#flaggrid'); |
5960a66c DC |
300 | me.load({ |
301 | success: function(response, options) { | |
4f14c9c9 | 302 | let data = response.result.data; |
5960a66c | 303 | grid.getStore().setData(data); |
cd86360d TL |
304 | // re-align after store load, else the window is not centered |
305 | me.alignTo(Ext.getBody(), 'c-c'); | |
4f14c9c9 | 306 | }, |
844a81ba | 307 | }); |
4f14c9c9 | 308 | }, |
5960a66c DC |
309 | }); |
310 | ||
4ea09218 DM |
311 | Ext.define('PVE.node.CephOsdTree', { |
312 | extend: 'Ext.tree.Panel', | |
313 | alias: ['widget.pveNodeCephOsdTree'], | |
ba93a9c6 | 314 | onlineHelp: 'chapter_pveceph', |
3982a214 DC |
315 | |
316 | viewModel: { | |
317 | data: { | |
318 | nodename: '', | |
319 | flags: [], | |
320 | maxversion: '0', | |
0b88c18a | 321 | mixedversions: false, |
3982a214 DC |
322 | versions: {}, |
323 | isOsd: false, | |
324 | downOsd: false, | |
325 | upOsd: false, | |
326 | inOsd: false, | |
327 | outOsd: false, | |
328 | osdid: '', | |
329 | osdhost: '', | |
4f14c9c9 | 330 | }, |
3982a214 DC |
331 | }, |
332 | ||
333 | controller: { | |
334 | xclass: 'Ext.app.ViewController', | |
335 | ||
336 | reload: function() { | |
0b9e52dd TL |
337 | let me = this; |
338 | let view = me.getView(); | |
339 | let vm = me.getViewModel(); | |
4f14c9c9 TL |
340 | let nodename = vm.get('nodename'); |
341 | let sm = view.getSelectionModel(); | |
3982a214 DC |
342 | Proxmox.Utils.API2Request({ |
343 | url: "/nodes/" + nodename + "/ceph/osd", | |
0b9e52dd | 344 | waitMsgTarget: view, |
3982a214 DC |
345 | method: 'GET', |
346 | failure: function(response, opts) { | |
0b9e52dd TL |
347 | let msg = response.htmlStatus; |
348 | PVE.Utils.showCephInstallOrMask(view, msg, nodename, win => | |
349 | view.mon(win, 'cephInstallWindowClosed', me.reload), | |
3982a214 DC |
350 | ); |
351 | }, | |
352 | success: function(response, opts) { | |
4f14c9c9 | 353 | let data = response.result.data; |
0b9e52dd | 354 | let selected = view.getSelection(); |
4f14c9c9 | 355 | let name; |
3982a214 DC |
356 | if (selected.length) { |
357 | name = selected[0].data.name; | |
358 | } | |
2ee96d1c FE |
359 | data.versions = data.versions || {}; |
360 | vm.set('versions', data.versions); | |
3982a214 | 361 | // extract max version |
4f14c9c9 TL |
362 | let maxversion = "0"; |
363 | let mixedversions = false; | |
364 | let traverse; | |
0b88c18a DC |
365 | traverse = function(node, fn) { |
366 | fn(node); | |
367 | if (Array.isArray(node.children)) { | |
368 | node.children.forEach(c => { traverse(c, fn); }); | |
3982a214 | 369 | } |
0b88c18a DC |
370 | }; |
371 | traverse(data.root, node => { | |
372 | // compatibility for old api call | |
9b803670 | 373 | if (node.type === 'host' && !node.version) { |
0b88c18a DC |
374 | node.version = data.versions[node.name]; |
375 | } | |
376 | ||
377 | if (node.version === undefined) { | |
378 | return; | |
379 | } | |
380 | ||
d6fb92d7 | 381 | if (PVE.Utils.compare_ceph_versions(node.version, maxversion) !== 0 && maxversion !== "0") { |
0b88c18a DC |
382 | mixedversions = true; |
383 | } | |
384 | ||
385 | if (PVE.Utils.compare_ceph_versions(node.version, maxversion) > 0) { | |
386 | maxversion = node.version; | |
387 | } | |
3982a214 DC |
388 | }); |
389 | vm.set('maxversion', maxversion); | |
0b88c18a | 390 | vm.set('mixedversions', mixedversions); |
3982a214 | 391 | sm.deselectAll(); |
4f14c9c9 TL |
392 | view.setRootNode(data.root); |
393 | view.expandAll(); | |
3982a214 | 394 | if (name) { |
b6b4c816 | 395 | let node = view.getRootNode().findChild('name', name, true); |
3982a214 | 396 | if (node) { |
4f14c9c9 | 397 | view.setSelection([node]); |
3982a214 DC |
398 | } |
399 | } | |
400 | ||
4f14c9c9 | 401 | let flags = data.flags.split(','); |
3982a214 | 402 | vm.set('flags', flags); |
4f14c9c9 | 403 | }, |
3982a214 DC |
404 | }); |
405 | }, | |
406 | ||
407 | osd_cmd: function(comp) { | |
4f14c9c9 TL |
408 | let me = this; |
409 | let vm = this.getViewModel(); | |
410 | let cmd = comp.cmd; | |
411 | let params = comp.params || {}; | |
412 | let osdid = vm.get('osdid'); | |
3982a214 | 413 | |
4f14c9c9 | 414 | let doRequest = function() { |
8fdc9793 AL |
415 | let targetnode = vm.get('osdhost'); |
416 | // cmds not node specific and need to work if the OSD node is down | |
417 | if (['in', 'out'].includes(cmd)) { | |
418 | targetnode = vm.get('nodename'); | |
419 | } | |
3982a214 | 420 | Proxmox.Utils.API2Request({ |
8fdc9793 | 421 | url: `/nodes/${targetnode}/ceph/osd/${osdid}/${cmd}`, |
3982a214 DC |
422 | waitMsgTarget: me.getView(), |
423 | method: 'POST', | |
424 | params: params, | |
425 | success: () => { me.reload(); }, | |
426 | failure: function(response, opts) { | |
427 | Ext.Msg.alert(gettext('Error'), response.htmlStatus); | |
4f14c9c9 | 428 | }, |
3982a214 DC |
429 | }); |
430 | }; | |
431 | ||
432 | if (cmd === 'scrub') { | |
433 | Ext.MessageBox.defaultButton = params.deep === 1 ? 2 : 1; | |
434 | Ext.Msg.show({ | |
435 | title: gettext('Confirm'), | |
436 | icon: params.deep === 1 ? Ext.Msg.WARNING : Ext.Msg.QUESTION, | |
4f14c9c9 TL |
437 | msg: params.deep !== 1 |
438 | ? Ext.String.format(gettext("Scrub OSD.{0}"), osdid) | |
439 | : Ext.String.format(gettext("Deep Scrub OSD.{0}"), osdid) + | |
3982a214 DC |
440 | "<br>Caution: This can reduce performance while it is running.", |
441 | buttons: Ext.Msg.YESNO, | |
442 | callback: function(btn) { | |
443 | if (btn !== 'yes') { | |
444 | return; | |
445 | } | |
446 | doRequest(); | |
4f14c9c9 | 447 | }, |
3982a214 DC |
448 | }); |
449 | } else { | |
450 | doRequest(); | |
451 | } | |
452 | }, | |
453 | ||
454 | create_osd: function() { | |
4f14c9c9 TL |
455 | let me = this; |
456 | let vm = this.getViewModel(); | |
3982a214 DC |
457 | Ext.create('PVE.CephCreateOsd', { |
458 | nodename: vm.get('nodename'), | |
4f14c9c9 | 459 | taskDone: () => { me.reload(); }, |
3982a214 DC |
460 | }).show(); |
461 | }, | |
462 | ||
d9415cc4 | 463 | destroy_osd: async function() { |
4f14c9c9 TL |
464 | let me = this; |
465 | let vm = this.getViewModel(); | |
d9415cc4 AL |
466 | |
467 | let warnings = { | |
468 | flags: false, | |
469 | degraded: false, | |
470 | }; | |
471 | ||
472 | let flagsPromise = Proxmox.Async.api2({ | |
473 | url: `/cluster/ceph/flags`, | |
474 | method: 'GET', | |
475 | }); | |
476 | ||
477 | let statusPromise = Proxmox.Async.api2({ | |
478 | url: `/cluster/ceph/status`, | |
479 | method: 'GET', | |
480 | }); | |
481 | ||
482 | me.getView().mask(gettext('Loading...')); | |
483 | ||
484 | try { | |
485 | let result = await Promise.all([flagsPromise, statusPromise]); | |
486 | ||
487 | let flagsData = result[0].result.data; | |
488 | let statusData = result[1].result.data; | |
489 | ||
490 | let flags = Array.from( | |
491 | flagsData.filter(v => v.value), | |
492 | v => v.name, | |
493 | ).filter(v => ['norebalance', 'norecover', 'noout'].includes(v)); | |
494 | ||
495 | if (flags.length) { | |
496 | warnings.flags = true; | |
497 | } | |
498 | if (Object.keys(statusData.pgmap).includes('degraded_objects')) { | |
499 | warnings.degraded = true; | |
500 | } | |
501 | } catch (error) { | |
502 | Ext.Msg.alert(gettext('Error'), error.htmlStatus); | |
503 | me.getView().unmask(); | |
504 | return; | |
505 | } | |
506 | ||
507 | me.getView().unmask(); | |
3982a214 DC |
508 | Ext.create('PVE.CephRemoveOsd', { |
509 | nodename: vm.get('osdhost'), | |
510 | osdid: vm.get('osdid'), | |
d9415cc4 | 511 | warnings: warnings, |
4f14c9c9 | 512 | taskDone: () => { me.reload(); }, |
d9415cc4 AL |
513 | autoShow: true, |
514 | }); | |
3982a214 DC |
515 | }, |
516 | ||
1c58ffa6 | 517 | set_flags: function() { |
4f14c9c9 TL |
518 | let me = this; |
519 | let vm = this.getViewModel(); | |
1c58ffa6 DC |
520 | Ext.create('PVE.CephSetFlags', { |
521 | nodename: vm.get('nodename'), | |
4f14c9c9 | 522 | taskDone: () => { me.reload(); }, |
1c58ffa6 | 523 | }).show(); |
3982a214 DC |
524 | }, |
525 | ||
526 | service_cmd: function(comp) { | |
4f14c9c9 TL |
527 | let me = this; |
528 | let vm = this.getViewModel(); | |
529 | let cmd = comp.cmd || comp; | |
3982a214 DC |
530 | Proxmox.Utils.API2Request({ |
531 | url: "/nodes/" + vm.get('osdhost') + "/ceph/" + cmd, | |
532 | params: { service: "osd." + vm.get('osdid') }, | |
533 | waitMsgTarget: me.getView(), | |
534 | method: 'POST', | |
535 | success: function(response, options) { | |
4f14c9c9 TL |
536 | let upid = response.result.data; |
537 | let win = Ext.create('Proxmox.window.TaskProgress', { | |
3982a214 | 538 | upid: upid, |
4f14c9c9 | 539 | taskDone: () => { me.reload(); }, |
3982a214 DC |
540 | }); |
541 | win.show(); | |
542 | }, | |
543 | failure: function(response, opts) { | |
544 | Ext.Msg.alert(gettext('Error'), response.htmlStatus); | |
4f14c9c9 | 545 | }, |
3982a214 DC |
546 | }); |
547 | }, | |
548 | ||
549 | set_selection_status: function(tp, selection) { | |
550 | if (selection.length < 1) { | |
551 | return; | |
552 | } | |
4f14c9c9 TL |
553 | let rec = selection[0]; |
554 | let vm = this.getViewModel(); | |
3982a214 | 555 | |
53e3ea84 | 556 | let isOsd = rec.data.host && rec.data.type === 'osd' && rec.data.id >= 0; |
3982a214 DC |
557 | |
558 | vm.set('isOsd', isOsd); | |
559 | vm.set('downOsd', isOsd && rec.data.status === 'down'); | |
560 | vm.set('upOsd', isOsd && rec.data.status !== 'down'); | |
561 | vm.set('inOsd', isOsd && rec.data.in); | |
562 | vm.set('outOsd', isOsd && !rec.data.in); | |
563 | vm.set('osdid', isOsd ? rec.data.id : undefined); | |
564 | vm.set('osdhost', isOsd ? rec.data.host : undefined); | |
3982a214 DC |
565 | }, |
566 | ||
567 | render_status: function(value, metaData, rec) { | |
568 | if (!value) { | |
569 | return value; | |
570 | } | |
4f14c9c9 TL |
571 | let inout = rec.data.in ? 'in' : 'out'; |
572 | let updownicon = value === 'up' ? 'good fa-arrow-circle-up' | |
573 | : 'critical fa-arrow-circle-down'; | |
3982a214 | 574 | |
4f14c9c9 TL |
575 | let inouticon = rec.data.in ? 'good fa-circle' |
576 | : 'warning fa-circle-o'; | |
3982a214 | 577 | |
4f14c9c9 | 578 | let text = value + ' <i class="fa ' + updownicon + '"></i> / ' + |
3982a214 DC |
579 | inout + ' <i class="fa ' + inouticon + '"></i>'; |
580 | ||
581 | return text; | |
582 | }, | |
583 | ||
584 | render_wal: function(value, metaData, rec) { | |
585 | if (!value && | |
586 | rec.data.osdtype === 'bluestore' && | |
587 | rec.data.type === 'osd') { | |
588 | return 'N/A'; | |
589 | } | |
590 | return value; | |
591 | }, | |
592 | ||
593 | render_version: function(value, metadata, rec) { | |
4f14c9c9 TL |
594 | let vm = this.getViewModel(); |
595 | let versions = vm.get('versions'); | |
596 | let icon = ""; | |
597 | let version = value || ""; | |
598 | let maxversion = vm.get('maxversion'); | |
d6fb92d7 | 599 | if (value && PVE.Utils.compare_ceph_versions(value, maxversion) !== 0) { |
30a6ce73 DC |
600 | let host_version = rec.parentNode?.data?.version || versions[rec.data.host] || ""; |
601 | if (rec.data.type === 'host' || PVE.Utils.compare_ceph_versions(host_version, maxversion) !== 0) { | |
0b88c18a DC |
602 | icon = PVE.Utils.get_ceph_icon_html('HEALTH_UPGRADE'); |
603 | } else { | |
604 | icon = PVE.Utils.get_ceph_icon_html('HEALTH_OLD'); | |
605 | } | |
606 | } else if (value && vm.get('mixedversions')) { | |
607 | icon = PVE.Utils.get_ceph_icon_html('HEALTH_OK'); | |
3982a214 DC |
608 | } |
609 | ||
610 | return icon + version; | |
611 | }, | |
612 | ||
613 | render_osd_val: function(value, metaData, rec) { | |
4f14c9c9 | 614 | return rec.data.type === 'osd' ? value : ''; |
3982a214 | 615 | }, |
a3368752 TL |
616 | render_osd_weight: function(value, metaData, rec) { |
617 | if (rec.data.type !== 'osd') { | |
618 | return ''; | |
619 | } | |
620 | return Ext.util.Format.number(value, '0.00###'); | |
621 | }, | |
3982a214 | 622 | |
a9e23b28 TL |
623 | render_osd_latency: function(value, metaData, rec) { |
624 | if (rec.data.type !== 'osd') { | |
625 | return ''; | |
626 | } | |
627 | let commit_ms = rec.data.commit_latency_ms, | |
628 | apply_ms = rec.data.apply_latency_ms; | |
629 | return apply_ms + ' / ' + commit_ms; | |
630 | }, | |
631 | ||
3982a214 | 632 | render_osd_size: function(value, metaData, rec) { |
1bd7bcdb | 633 | return this.render_osd_val(Proxmox.Utils.render_size(value), metaData, rec); |
3982a214 DC |
634 | }, |
635 | ||
636 | control: { | |
637 | '#': { | |
4f14c9c9 TL |
638 | selectionchange: 'set_selection_status', |
639 | }, | |
3982a214 DC |
640 | }, |
641 | ||
642 | init: function(view) { | |
4f14c9c9 TL |
643 | let me = this; |
644 | let vm = this.getViewModel(); | |
3982a214 DC |
645 | |
646 | if (!view.pveSelNode.data.node) { | |
647 | throw "no node name specified"; | |
648 | } | |
649 | ||
650 | vm.set('nodename', view.pveSelNode.data.node); | |
651 | ||
652 | me.callParent(); | |
653 | me.reload(); | |
4f14c9c9 | 654 | }, |
3982a214 DC |
655 | }, |
656 | ||
361aafd0 DC |
657 | stateful: true, |
658 | stateId: 'grid-ceph-osd', | |
3982a214 DC |
659 | rootVisible: false, |
660 | useArrows: true, | |
661 | ||
89cd5a3f DC |
662 | columns: [ |
663 | { | |
664 | xtype: 'treecolumn', | |
665 | text: 'Name', | |
666 | dataIndex: 'name', | |
4f14c9c9 | 667 | width: 150, |
89cd5a3f DC |
668 | }, |
669 | { | |
670 | text: 'Type', | |
671 | dataIndex: 'type', | |
413ada0f | 672 | hidden: true, |
89cd5a3f | 673 | align: 'right', |
4f14c9c9 | 674 | width: 75, |
89cd5a3f | 675 | }, |
33a7e157 DC |
676 | { |
677 | text: gettext("Class"), | |
678 | dataIndex: 'device_class', | |
679 | align: 'right', | |
4f14c9c9 | 680 | width: 75, |
33a7e157 | 681 | }, |
7d406f18 DC |
682 | { |
683 | text: "OSD Type", | |
684 | dataIndex: 'osdtype', | |
685 | align: 'right', | |
4f14c9c9 | 686 | width: 100, |
7d406f18 DC |
687 | }, |
688 | { | |
689 | text: "Bluestore Device", | |
690 | dataIndex: 'blfsdev', | |
691 | align: 'right', | |
a3f146fc | 692 | width: 75, |
4f14c9c9 | 693 | hidden: true, |
7d406f18 DC |
694 | }, |
695 | { | |
696 | text: "DB Device", | |
697 | dataIndex: 'dbdev', | |
698 | align: 'right', | |
a3f146fc | 699 | width: 75, |
4f14c9c9 | 700 | hidden: true, |
7d406f18 DC |
701 | }, |
702 | { | |
703 | text: "WAL Device", | |
704 | dataIndex: 'waldev', | |
705 | align: 'right', | |
3982a214 | 706 | renderer: 'render_wal', |
a3f146fc | 707 | width: 75, |
4f14c9c9 | 708 | hidden: true, |
7d406f18 | 709 | }, |
89cd5a3f DC |
710 | { |
711 | text: 'Status', | |
712 | dataIndex: 'status', | |
713 | align: 'right', | |
3982a214 | 714 | renderer: 'render_status', |
4f14c9c9 | 715 | width: 120, |
89cd5a3f | 716 | }, |
e0297023 DC |
717 | { |
718 | text: gettext('Version'), | |
719 | dataIndex: 'version', | |
a3f146fc | 720 | align: 'right', |
4f14c9c9 | 721 | renderer: 'render_version', |
e0297023 | 722 | }, |
89cd5a3f DC |
723 | { |
724 | text: 'weight', | |
725 | dataIndex: 'crush_weight', | |
726 | align: 'right', | |
a3368752 | 727 | renderer: 'render_osd_weight', |
4f14c9c9 | 728 | width: 90, |
89cd5a3f DC |
729 | }, |
730 | { | |
731 | text: 'reweight', | |
732 | dataIndex: 'reweight', | |
733 | align: 'right', | |
a3368752 | 734 | renderer: 'render_osd_weight', |
4f14c9c9 | 735 | width: 90, |
89cd5a3f DC |
736 | }, |
737 | { | |
cfb10313 TL |
738 | text: gettext('Used') + ' (%)', |
739 | dataIndex: 'percent_used', | |
740 | align: 'right', | |
741 | renderer: function(value, metaData, rec) { | |
742 | if (rec.data.type !== 'osd') { | |
743 | return ''; | |
35c9c3d6 | 744 | } |
cfb10313 TL |
745 | return Ext.util.Format.number(value, '0.00'); |
746 | }, | |
4f14c9c9 | 747 | width: 100, |
cfb10313 TL |
748 | }, |
749 | { | |
750 | text: gettext('Total'), | |
751 | dataIndex: 'total_space', | |
752 | align: 'right', | |
753 | renderer: 'render_osd_size', | |
4f14c9c9 | 754 | width: 100, |
89cd5a3f DC |
755 | }, |
756 | { | |
a9e23b28 TL |
757 | text: 'Apply/Commit<br>Latency (ms)', |
758 | dataIndex: 'apply_latency_ms', | |
759 | align: 'right', | |
760 | renderer: 'render_osd_latency', | |
4f14c9c9 TL |
761 | width: 120, |
762 | }, | |
89cd5a3f | 763 | ], |
4ea09218 | 764 | |
4ea09218 | 765 | |
3982a214 DC |
766 | tbar: { |
767 | items: [ | |
768 | { | |
769 | text: gettext('Reload'), | |
770 | iconCls: 'fa fa-refresh', | |
4f14c9c9 | 771 | handler: 'reload', |
3982a214 | 772 | }, |
a3f146fc | 773 | '-', |
3982a214 DC |
774 | { |
775 | text: gettext('Create') + ': OSD', | |
776 | handler: 'create_osd', | |
777 | }, | |
778 | { | |
60456379 | 779 | text: Ext.String.format(gettext('Manage {0}'), 'Global Flags'), |
1c58ffa6 | 780 | handler: 'set_flags', |
3982a214 DC |
781 | }, |
782 | '->', | |
783 | { | |
784 | xtype: 'tbtext', | |
785 | data: { | |
4f14c9c9 | 786 | osd: undefined, |
4ea09218 | 787 | }, |
3982a214 DC |
788 | bind: { |
789 | data: { | |
4f14c9c9 TL |
790 | osd: "{osdid}", |
791 | }, | |
4ea09218 | 792 | }, |
3982a214 DC |
793 | tpl: [ |
794 | '<tpl if="osd">', | |
795 | 'osd.{osd}:', | |
796 | '<tpl else>', | |
797 | gettext('No OSD selected'), | |
4f14c9c9 TL |
798 | '</tpl>', |
799 | ], | |
6f82e1b6 | 800 | }, |
3982a214 DC |
801 | { |
802 | text: gettext('Start'), | |
803 | iconCls: 'fa fa-play', | |
804 | disabled: true, | |
805 | bind: { | |
4f14c9c9 | 806 | disabled: '{!downOsd}', |
3982a214 DC |
807 | }, |
808 | cmd: 'start', | |
4f14c9c9 | 809 | handler: 'service_cmd', |
3982a214 DC |
810 | }, |
811 | { | |
812 | text: gettext('Stop'), | |
813 | iconCls: 'fa fa-stop', | |
814 | disabled: true, | |
815 | bind: { | |
4f14c9c9 | 816 | disabled: '{!upOsd}', |
3982a214 DC |
817 | }, |
818 | cmd: 'stop', | |
4f14c9c9 | 819 | handler: 'service_cmd', |
3982a214 DC |
820 | }, |
821 | { | |
822 | text: gettext('Restart'), | |
823 | iconCls: 'fa fa-refresh', | |
824 | disabled: true, | |
825 | bind: { | |
4f14c9c9 | 826 | disabled: '{!upOsd}', |
3982a214 DC |
827 | }, |
828 | cmd: 'restart', | |
4f14c9c9 | 829 | handler: 'service_cmd', |
3982a214 DC |
830 | }, |
831 | '-', | |
832 | { | |
833 | text: 'Out', | |
834 | iconCls: 'fa fa-circle-o', | |
835 | disabled: true, | |
836 | bind: { | |
4f14c9c9 | 837 | disabled: '{!inOsd}', |
3982a214 DC |
838 | }, |
839 | cmd: 'out', | |
4f14c9c9 | 840 | handler: 'osd_cmd', |
3982a214 DC |
841 | }, |
842 | { | |
843 | text: 'In', | |
844 | iconCls: 'fa fa-circle', | |
845 | disabled: true, | |
846 | bind: { | |
4f14c9c9 | 847 | disabled: '{!outOsd}', |
3982a214 DC |
848 | }, |
849 | cmd: 'in', | |
4f14c9c9 | 850 | handler: 'osd_cmd', |
3982a214 DC |
851 | }, |
852 | '-', | |
853 | { | |
3b0ae6b2 | 854 | text: gettext('More'), |
3982a214 DC |
855 | iconCls: 'fa fa-bars', |
856 | disabled: true, | |
857 | bind: { | |
4f14c9c9 | 858 | disabled: '{!isOsd}', |
3982a214 DC |
859 | }, |
860 | menu: [ | |
861 | { | |
862 | text: gettext('Scrub'), | |
863 | iconCls: 'fa fa-shower', | |
864 | cmd: 'scrub', | |
4f14c9c9 | 865 | handler: 'osd_cmd', |
3982a214 DC |
866 | }, |
867 | { | |
868 | text: gettext('Deep Scrub'), | |
869 | iconCls: 'fa fa-bath', | |
870 | cmd: 'scrub', | |
871 | params: { | |
872 | deep: 1, | |
873 | }, | |
4f14c9c9 | 874 | handler: 'osd_cmd', |
3982a214 DC |
875 | }, |
876 | { | |
877 | text: gettext('Destroy'), | |
878 | itemId: 'remove', | |
879 | iconCls: 'fa fa-fw fa-trash-o', | |
880 | bind: { | |
4f14c9c9 | 881 | disabled: '{!downOsd}', |
3982a214 | 882 | }, |
4f14c9c9 TL |
883 | handler: 'destroy_osd', |
884 | }, | |
3982a214 | 885 | ], |
4f14c9c9 TL |
886 | }, |
887 | ], | |
3982a214 DC |
888 | }, |
889 | ||
890 | fields: [ | |
4f14c9c9 | 891 | 'name', 'type', 'status', 'host', 'in', 'id', |
3982a214 DC |
892 | { type: 'number', name: 'reweight' }, |
893 | { type: 'number', name: 'percent_used' }, | |
894 | { type: 'integer', name: 'bytes_used' }, | |
895 | { type: 'integer', name: 'total_space' }, | |
896 | { type: 'integer', name: 'apply_latency_ms' }, | |
897 | { type: 'integer', name: 'commit_latency_ms' }, | |
898 | { type: 'string', name: 'device_class' }, | |
899 | { type: 'string', name: 'osdtype' }, | |
900 | { type: 'string', name: 'blfsdev' }, | |
901 | { type: 'string', name: 'dbdev' }, | |
902 | { type: 'string', name: 'waldev' }, | |
4f14c9c9 TL |
903 | { |
904 | type: 'string', name: 'version', calculate: function(data) { | |
3982a214 | 905 | return PVE.Utils.parse_ceph_version(data); |
4f14c9c9 TL |
906 | }, |
907 | }, | |
908 | { | |
909 | type: 'string', name: 'iconCls', calculate: function(data) { | |
910 | let iconMap = { | |
3982a214 DC |
911 | host: 'fa-building', |
912 | osd: 'fa-hdd-o', | |
913 | root: 'fa-server', | |
914 | }; | |
915 | return 'fa x-fa-tree ' + iconMap[data.type]; | |
4f14c9c9 TL |
916 | }, |
917 | }, | |
918 | { type: 'number', name: 'crush_weight' }, | |
3982a214 | 919 | ], |
4ea09218 | 920 | }); |