]>
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; | |
6242a600 AL |
530 | |
531 | let doRequest = function() { | |
532 | Proxmox.Utils.API2Request({ | |
533 | url: `/nodes/${vm.get('osdhost')}/ceph/${cmd}`, | |
534 | params: { service: "osd." + vm.get('osdid') }, | |
535 | waitMsgTarget: me.getView(), | |
536 | method: 'POST', | |
537 | success: function(response, options) { | |
538 | let upid = response.result.data; | |
539 | let win = Ext.create('Proxmox.window.TaskProgress', { | |
540 | upid: upid, | |
541 | taskDone: () => { me.reload(); }, | |
542 | }); | |
543 | win.show(); | |
544 | }, | |
545 | failure: function(response, opts) { | |
546 | Ext.Msg.alert(gettext('Error'), response.htmlStatus); | |
547 | }, | |
548 | }); | |
549 | }; | |
550 | ||
551 | if (cmd === "stop") { | |
552 | Proxmox.Utils.API2Request({ | |
553 | url: `/nodes/${vm.get('osdhost')}/ceph/cmd-safety`, | |
554 | params: { | |
555 | service: 'osd', | |
556 | id: vm.get('osdid'), | |
557 | action: 'stop', | |
558 | }, | |
559 | waitMsgTarget: me.getView(), | |
560 | method: 'GET', | |
561 | success: function({ result: { data } }) { | |
562 | if (!data.safe) { | |
563 | Ext.Msg.show({ | |
564 | title: gettext('Warning'), | |
565 | message: data.status, | |
566 | icon: Ext.Msg.WARNING, | |
567 | buttons: Ext.Msg.OKCANCEL, | |
568 | buttonText: { ok: gettext('Stop OSD') }, | |
569 | fn: function(selection) { | |
570 | if (selection === 'ok') { | |
571 | doRequest(); | |
572 | } | |
573 | }, | |
574 | }); | |
575 | } else { | |
576 | doRequest(); | |
577 | } | |
578 | }, | |
579 | failure: response => Ext.Msg.alert(gettext('Error'), response.htmlStatus), | |
580 | }); | |
581 | } else { | |
582 | doRequest(); | |
583 | } | |
3982a214 DC |
584 | }, |
585 | ||
586 | set_selection_status: function(tp, selection) { | |
587 | if (selection.length < 1) { | |
588 | return; | |
589 | } | |
4f14c9c9 TL |
590 | let rec = selection[0]; |
591 | let vm = this.getViewModel(); | |
3982a214 | 592 | |
53e3ea84 | 593 | let isOsd = rec.data.host && rec.data.type === 'osd' && rec.data.id >= 0; |
3982a214 DC |
594 | |
595 | vm.set('isOsd', isOsd); | |
596 | vm.set('downOsd', isOsd && rec.data.status === 'down'); | |
597 | vm.set('upOsd', isOsd && rec.data.status !== 'down'); | |
598 | vm.set('inOsd', isOsd && rec.data.in); | |
599 | vm.set('outOsd', isOsd && !rec.data.in); | |
600 | vm.set('osdid', isOsd ? rec.data.id : undefined); | |
601 | vm.set('osdhost', isOsd ? rec.data.host : undefined); | |
3982a214 DC |
602 | }, |
603 | ||
604 | render_status: function(value, metaData, rec) { | |
605 | if (!value) { | |
606 | return value; | |
607 | } | |
4f14c9c9 TL |
608 | let inout = rec.data.in ? 'in' : 'out'; |
609 | let updownicon = value === 'up' ? 'good fa-arrow-circle-up' | |
610 | : 'critical fa-arrow-circle-down'; | |
3982a214 | 611 | |
4f14c9c9 TL |
612 | let inouticon = rec.data.in ? 'good fa-circle' |
613 | : 'warning fa-circle-o'; | |
3982a214 | 614 | |
4f14c9c9 | 615 | let text = value + ' <i class="fa ' + updownicon + '"></i> / ' + |
3982a214 DC |
616 | inout + ' <i class="fa ' + inouticon + '"></i>'; |
617 | ||
618 | return text; | |
619 | }, | |
620 | ||
621 | render_wal: function(value, metaData, rec) { | |
622 | if (!value && | |
623 | rec.data.osdtype === 'bluestore' && | |
624 | rec.data.type === 'osd') { | |
625 | return 'N/A'; | |
626 | } | |
627 | return value; | |
628 | }, | |
629 | ||
630 | render_version: function(value, metadata, rec) { | |
4f14c9c9 TL |
631 | let vm = this.getViewModel(); |
632 | let versions = vm.get('versions'); | |
633 | let icon = ""; | |
634 | let version = value || ""; | |
635 | let maxversion = vm.get('maxversion'); | |
d6fb92d7 | 636 | if (value && PVE.Utils.compare_ceph_versions(value, maxversion) !== 0) { |
30a6ce73 DC |
637 | let host_version = rec.parentNode?.data?.version || versions[rec.data.host] || ""; |
638 | if (rec.data.type === 'host' || PVE.Utils.compare_ceph_versions(host_version, maxversion) !== 0) { | |
0b88c18a DC |
639 | icon = PVE.Utils.get_ceph_icon_html('HEALTH_UPGRADE'); |
640 | } else { | |
641 | icon = PVE.Utils.get_ceph_icon_html('HEALTH_OLD'); | |
642 | } | |
643 | } else if (value && vm.get('mixedversions')) { | |
644 | icon = PVE.Utils.get_ceph_icon_html('HEALTH_OK'); | |
3982a214 DC |
645 | } |
646 | ||
647 | return icon + version; | |
648 | }, | |
649 | ||
650 | render_osd_val: function(value, metaData, rec) { | |
4f14c9c9 | 651 | return rec.data.type === 'osd' ? value : ''; |
3982a214 | 652 | }, |
a3368752 TL |
653 | render_osd_weight: function(value, metaData, rec) { |
654 | if (rec.data.type !== 'osd') { | |
655 | return ''; | |
656 | } | |
657 | return Ext.util.Format.number(value, '0.00###'); | |
658 | }, | |
3982a214 | 659 | |
a9e23b28 TL |
660 | render_osd_latency: function(value, metaData, rec) { |
661 | if (rec.data.type !== 'osd') { | |
662 | return ''; | |
663 | } | |
664 | let commit_ms = rec.data.commit_latency_ms, | |
665 | apply_ms = rec.data.apply_latency_ms; | |
666 | return apply_ms + ' / ' + commit_ms; | |
667 | }, | |
668 | ||
3982a214 | 669 | render_osd_size: function(value, metaData, rec) { |
1bd7bcdb | 670 | return this.render_osd_val(Proxmox.Utils.render_size(value), metaData, rec); |
3982a214 DC |
671 | }, |
672 | ||
673 | control: { | |
674 | '#': { | |
4f14c9c9 TL |
675 | selectionchange: 'set_selection_status', |
676 | }, | |
3982a214 DC |
677 | }, |
678 | ||
679 | init: function(view) { | |
4f14c9c9 TL |
680 | let me = this; |
681 | let vm = this.getViewModel(); | |
3982a214 DC |
682 | |
683 | if (!view.pveSelNode.data.node) { | |
684 | throw "no node name specified"; | |
685 | } | |
686 | ||
687 | vm.set('nodename', view.pveSelNode.data.node); | |
688 | ||
689 | me.callParent(); | |
690 | me.reload(); | |
4f14c9c9 | 691 | }, |
3982a214 DC |
692 | }, |
693 | ||
361aafd0 DC |
694 | stateful: true, |
695 | stateId: 'grid-ceph-osd', | |
3982a214 DC |
696 | rootVisible: false, |
697 | useArrows: true, | |
698 | ||
89cd5a3f DC |
699 | columns: [ |
700 | { | |
701 | xtype: 'treecolumn', | |
702 | text: 'Name', | |
703 | dataIndex: 'name', | |
4f14c9c9 | 704 | width: 150, |
89cd5a3f DC |
705 | }, |
706 | { | |
707 | text: 'Type', | |
708 | dataIndex: 'type', | |
413ada0f | 709 | hidden: true, |
89cd5a3f | 710 | align: 'right', |
4f14c9c9 | 711 | width: 75, |
89cd5a3f | 712 | }, |
33a7e157 DC |
713 | { |
714 | text: gettext("Class"), | |
715 | dataIndex: 'device_class', | |
716 | align: 'right', | |
4f14c9c9 | 717 | width: 75, |
33a7e157 | 718 | }, |
7d406f18 DC |
719 | { |
720 | text: "OSD Type", | |
721 | dataIndex: 'osdtype', | |
722 | align: 'right', | |
4f14c9c9 | 723 | width: 100, |
7d406f18 DC |
724 | }, |
725 | { | |
726 | text: "Bluestore Device", | |
727 | dataIndex: 'blfsdev', | |
728 | align: 'right', | |
a3f146fc | 729 | width: 75, |
4f14c9c9 | 730 | hidden: true, |
7d406f18 DC |
731 | }, |
732 | { | |
733 | text: "DB Device", | |
734 | dataIndex: 'dbdev', | |
735 | align: 'right', | |
a3f146fc | 736 | width: 75, |
4f14c9c9 | 737 | hidden: true, |
7d406f18 DC |
738 | }, |
739 | { | |
740 | text: "WAL Device", | |
741 | dataIndex: 'waldev', | |
742 | align: 'right', | |
3982a214 | 743 | renderer: 'render_wal', |
a3f146fc | 744 | width: 75, |
4f14c9c9 | 745 | hidden: true, |
7d406f18 | 746 | }, |
89cd5a3f DC |
747 | { |
748 | text: 'Status', | |
749 | dataIndex: 'status', | |
750 | align: 'right', | |
3982a214 | 751 | renderer: 'render_status', |
4f14c9c9 | 752 | width: 120, |
89cd5a3f | 753 | }, |
e0297023 DC |
754 | { |
755 | text: gettext('Version'), | |
756 | dataIndex: 'version', | |
a3f146fc | 757 | align: 'right', |
4f14c9c9 | 758 | renderer: 'render_version', |
e0297023 | 759 | }, |
89cd5a3f DC |
760 | { |
761 | text: 'weight', | |
762 | dataIndex: 'crush_weight', | |
763 | align: 'right', | |
a3368752 | 764 | renderer: 'render_osd_weight', |
4f14c9c9 | 765 | width: 90, |
89cd5a3f DC |
766 | }, |
767 | { | |
768 | text: 'reweight', | |
769 | dataIndex: 'reweight', | |
770 | align: 'right', | |
a3368752 | 771 | renderer: 'render_osd_weight', |
4f14c9c9 | 772 | width: 90, |
89cd5a3f DC |
773 | }, |
774 | { | |
cfb10313 TL |
775 | text: gettext('Used') + ' (%)', |
776 | dataIndex: 'percent_used', | |
777 | align: 'right', | |
778 | renderer: function(value, metaData, rec) { | |
779 | if (rec.data.type !== 'osd') { | |
780 | return ''; | |
35c9c3d6 | 781 | } |
cfb10313 TL |
782 | return Ext.util.Format.number(value, '0.00'); |
783 | }, | |
4f14c9c9 | 784 | width: 100, |
cfb10313 TL |
785 | }, |
786 | { | |
787 | text: gettext('Total'), | |
788 | dataIndex: 'total_space', | |
789 | align: 'right', | |
790 | renderer: 'render_osd_size', | |
4f14c9c9 | 791 | width: 100, |
89cd5a3f DC |
792 | }, |
793 | { | |
a9e23b28 TL |
794 | text: 'Apply/Commit<br>Latency (ms)', |
795 | dataIndex: 'apply_latency_ms', | |
796 | align: 'right', | |
797 | renderer: 'render_osd_latency', | |
4f14c9c9 TL |
798 | width: 120, |
799 | }, | |
89cd5a3f | 800 | ], |
4ea09218 | 801 | |
4ea09218 | 802 | |
3982a214 DC |
803 | tbar: { |
804 | items: [ | |
805 | { | |
806 | text: gettext('Reload'), | |
807 | iconCls: 'fa fa-refresh', | |
4f14c9c9 | 808 | handler: 'reload', |
3982a214 | 809 | }, |
a3f146fc | 810 | '-', |
3982a214 DC |
811 | { |
812 | text: gettext('Create') + ': OSD', | |
813 | handler: 'create_osd', | |
814 | }, | |
815 | { | |
60456379 | 816 | text: Ext.String.format(gettext('Manage {0}'), 'Global Flags'), |
1c58ffa6 | 817 | handler: 'set_flags', |
3982a214 DC |
818 | }, |
819 | '->', | |
820 | { | |
821 | xtype: 'tbtext', | |
822 | data: { | |
4f14c9c9 | 823 | osd: undefined, |
4ea09218 | 824 | }, |
3982a214 DC |
825 | bind: { |
826 | data: { | |
4f14c9c9 TL |
827 | osd: "{osdid}", |
828 | }, | |
4ea09218 | 829 | }, |
3982a214 DC |
830 | tpl: [ |
831 | '<tpl if="osd">', | |
832 | 'osd.{osd}:', | |
833 | '<tpl else>', | |
834 | gettext('No OSD selected'), | |
4f14c9c9 TL |
835 | '</tpl>', |
836 | ], | |
6f82e1b6 | 837 | }, |
3982a214 DC |
838 | { |
839 | text: gettext('Start'), | |
840 | iconCls: 'fa fa-play', | |
841 | disabled: true, | |
842 | bind: { | |
4f14c9c9 | 843 | disabled: '{!downOsd}', |
3982a214 DC |
844 | }, |
845 | cmd: 'start', | |
4f14c9c9 | 846 | handler: 'service_cmd', |
3982a214 DC |
847 | }, |
848 | { | |
849 | text: gettext('Stop'), | |
850 | iconCls: 'fa fa-stop', | |
851 | disabled: true, | |
852 | bind: { | |
4f14c9c9 | 853 | disabled: '{!upOsd}', |
3982a214 DC |
854 | }, |
855 | cmd: 'stop', | |
4f14c9c9 | 856 | handler: 'service_cmd', |
3982a214 DC |
857 | }, |
858 | { | |
859 | text: gettext('Restart'), | |
860 | iconCls: 'fa fa-refresh', | |
861 | disabled: true, | |
862 | bind: { | |
4f14c9c9 | 863 | disabled: '{!upOsd}', |
3982a214 DC |
864 | }, |
865 | cmd: 'restart', | |
4f14c9c9 | 866 | handler: 'service_cmd', |
3982a214 DC |
867 | }, |
868 | '-', | |
869 | { | |
870 | text: 'Out', | |
871 | iconCls: 'fa fa-circle-o', | |
872 | disabled: true, | |
873 | bind: { | |
4f14c9c9 | 874 | disabled: '{!inOsd}', |
3982a214 DC |
875 | }, |
876 | cmd: 'out', | |
4f14c9c9 | 877 | handler: 'osd_cmd', |
3982a214 DC |
878 | }, |
879 | { | |
880 | text: 'In', | |
881 | iconCls: 'fa fa-circle', | |
882 | disabled: true, | |
883 | bind: { | |
4f14c9c9 | 884 | disabled: '{!outOsd}', |
3982a214 DC |
885 | }, |
886 | cmd: 'in', | |
4f14c9c9 | 887 | handler: 'osd_cmd', |
3982a214 DC |
888 | }, |
889 | '-', | |
890 | { | |
3b0ae6b2 | 891 | text: gettext('More'), |
3982a214 DC |
892 | iconCls: 'fa fa-bars', |
893 | disabled: true, | |
894 | bind: { | |
4f14c9c9 | 895 | disabled: '{!isOsd}', |
3982a214 DC |
896 | }, |
897 | menu: [ | |
898 | { | |
899 | text: gettext('Scrub'), | |
900 | iconCls: 'fa fa-shower', | |
901 | cmd: 'scrub', | |
4f14c9c9 | 902 | handler: 'osd_cmd', |
3982a214 DC |
903 | }, |
904 | { | |
905 | text: gettext('Deep Scrub'), | |
906 | iconCls: 'fa fa-bath', | |
907 | cmd: 'scrub', | |
908 | params: { | |
909 | deep: 1, | |
910 | }, | |
4f14c9c9 | 911 | handler: 'osd_cmd', |
3982a214 DC |
912 | }, |
913 | { | |
914 | text: gettext('Destroy'), | |
915 | itemId: 'remove', | |
916 | iconCls: 'fa fa-fw fa-trash-o', | |
917 | bind: { | |
4f14c9c9 | 918 | disabled: '{!downOsd}', |
3982a214 | 919 | }, |
4f14c9c9 TL |
920 | handler: 'destroy_osd', |
921 | }, | |
3982a214 | 922 | ], |
4f14c9c9 TL |
923 | }, |
924 | ], | |
3982a214 DC |
925 | }, |
926 | ||
927 | fields: [ | |
4f14c9c9 | 928 | 'name', 'type', 'status', 'host', 'in', 'id', |
3982a214 DC |
929 | { type: 'number', name: 'reweight' }, |
930 | { type: 'number', name: 'percent_used' }, | |
931 | { type: 'integer', name: 'bytes_used' }, | |
932 | { type: 'integer', name: 'total_space' }, | |
933 | { type: 'integer', name: 'apply_latency_ms' }, | |
934 | { type: 'integer', name: 'commit_latency_ms' }, | |
935 | { type: 'string', name: 'device_class' }, | |
936 | { type: 'string', name: 'osdtype' }, | |
937 | { type: 'string', name: 'blfsdev' }, | |
938 | { type: 'string', name: 'dbdev' }, | |
939 | { type: 'string', name: 'waldev' }, | |
4f14c9c9 TL |
940 | { |
941 | type: 'string', name: 'version', calculate: function(data) { | |
3982a214 | 942 | return PVE.Utils.parse_ceph_version(data); |
4f14c9c9 TL |
943 | }, |
944 | }, | |
945 | { | |
946 | type: 'string', name: 'iconCls', calculate: function(data) { | |
947 | let iconMap = { | |
3982a214 DC |
948 | host: 'fa-building', |
949 | osd: 'fa-hdd-o', | |
950 | root: 'fa-server', | |
951 | }; | |
952 | return 'fa x-fa-tree ' + iconMap[data.type]; | |
4f14c9c9 TL |
953 | }, |
954 | }, | |
955 | { type: 'number', name: 'crush_weight' }, | |
3982a214 | 956 | ], |
4ea09218 | 957 | }); |