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