]> git.proxmox.com Git - pve-manager.git/blame - www/manager6/ceph/OSD.js
ui: osd: warn if removal could be problematic
[pve-manager.git] / www / manager6 / ceph / OSD.js
CommitLineData
4ea09218 1Ext.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
165Ext.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
225Ext.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
311Ext.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});