]> git.proxmox.com Git - pve-manager.git/blame - www/manager6/ceph/OSD.js
ui: osd: mon: mds: warn if stop/destroy actions are 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;
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});