]>
git.proxmox.com Git - pve-manager.git/blob - www/manager6/ceph/OSD.js
1 Ext
.define('PVE.CephCreateOsd', {
2 extend
: 'Proxmox.window.Edit',
3 xtype
: 'pveCephCreateOsd',
9 onlineHelp
: 'pve_ceph_osds',
11 initComponent: function() {
15 throw "no node name specified";
20 Proxmox
.Utils
.API2Request({
21 url
: `/nodes/${me.nodename}/ceph/crush`,
23 failure
: response
=> Ext
.Msg
.alert(gettext('Error'), response
.htmlStatus
),
24 success: function({ result
: { data
} }) {
25 let classes
= [...new Set(
27 data
.matchAll(/^device\s[0-9]*\sosd\.[0-9]*\sclass\s(.*)$/gim),
29 ).filter(v
=> !['hdd', 'ssd', 'nvme'].includes(v
)),
33 let kvField
= me
.down('field[name=crush-device-class]');
34 kvField
.setComboItems([...kvField
.comboItems
, ...classes
]);
40 url
: "/nodes/" + me
.nodename
+ "/ceph/osd",
45 onGetValues: function(values
) {
46 Object
.keys(values
|| {}).forEach(function(name
) {
47 if (values
[name
] === '') {
56 xtype
: 'pmxDiskSelector',
58 nodename
: me
.nodename
,
60 includePartitions
: true,
61 fieldLabel
: gettext('Disk'),
67 xtype
: 'pmxDiskSelector',
69 nodename
: me
.nodename
,
70 diskType
: 'journal_disks',
71 includePartitions
: true,
72 fieldLabel
: gettext('DB Disk'),
76 emptyText
: 'use OSD disk',
78 change: function(field
, val
) {
79 me
.down('field[name=db_dev_size]').setDisabled(!val
);
86 fieldLabel
: gettext('DB size') + ' (GiB)',
92 emptyText
: gettext('Automatic'),
97 xtype
: 'proxmoxcheckbox',
99 fieldLabel
: gettext('Encrypt OSD'),
102 xtype
: 'proxmoxKVComboBox',
108 name
: 'crush-device-class',
109 nodename
: me
.nodename
,
110 fieldLabel
: gettext('Device Class'),
115 emptyText
: 'auto detect',
116 deleteEmpty
: !me
.isCreate
,
121 xtype
: 'pmxDiskSelector',
123 nodename
: me
.nodename
,
124 diskType
: 'journal_disks',
125 includePartitions
: true,
126 fieldLabel
: gettext('WAL Disk'),
130 emptyText
: 'use OSD/DB disk',
132 change: function(field
, val
) {
133 me
.down('field[name=wal_dev_size]').setDisabled(!val
);
138 xtype
: 'numberfield',
139 name
: 'wal_dev_size',
140 fieldLabel
: gettext('WAL size') + ' (GiB)',
146 emptyText
: gettext('Automatic'),
151 xtype
: 'displayfield',
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>.',
165 Ext
.define('PVE.CephRemoveOsd', {
166 extend
: 'Proxmox.window.Edit',
167 alias
: ['widget.pveCephRemoveOsd'],
175 xtype
: 'proxmoxcheckbox',
179 fieldLabel
: gettext('Cleanup Disks'),
182 xtype
: 'displayfield',
183 name
: 'osd-flag-hint',
185 value
: gettext('Global flags limiting the self healing of Ceph are enabled.'),
189 xtype
: 'displayfield',
190 name
: 'degraded-objects-hint',
192 value
: gettext('Objects are degraded. Consider waiting until the cluster is healthy.'),
196 initComponent: function() {
200 throw "no node name specified";
202 if (me
.osdid
=== undefined || me
.osdid
< 0) {
203 throw "no osdid specified";
208 me
.title
= gettext('Destroy') + ': Ceph OSD osd.' + me
.osdid
.toString();
211 url
: "/nodes/" + me
.nodename
+ "/ceph/osd/" + me
.osdid
.toString(),
216 if (me
.warnings
.flags
) {
217 me
.down('field[name=osd-flag-hint]').setHidden(false);
219 if (me
.warnings
.degraded
) {
220 me
.down('field[name=degraded-objects-hint]').setHidden(false);
225 Ext
.define('PVE.CephSetFlags', {
226 extend
: 'Proxmox.window.Edit',
227 xtype
: 'pveCephSetFlags',
234 onlineHelp
: 'pve_ceph_osds',
236 title
: Ext
.String
.format(gettext('Manage {0}'), 'Global OSD Flags'),
237 submitText
: gettext('Apply'),
242 onGetValues: function(values
) {
245 me
.down('#flaggrid').getStore().each((rec
) => {
246 val
[rec
.data
.name
] = rec
.data
.value
? 1 : 0;
258 this.commitChanges();
265 text
: gettext('Enable'),
266 xtype
: 'checkcolumn',
277 dataIndex
: 'description',
285 initComponent: function() {
289 throw "no node name specified";
293 url
: "/cluster/ceph/flags",
299 let grid
= me
.down('#flaggrid');
301 success: function(response
, options
) {
302 let data
= response
.result
.data
;
303 grid
.getStore().setData(data
);
304 // re-align after store load, else the window is not centered
305 me
.alignTo(Ext
.getBody(), 'c-c');
311 Ext
.define('PVE.node.CephOsdTree', {
312 extend
: 'Ext.tree.Panel',
313 alias
: ['widget.pveNodeCephOsdTree'],
314 onlineHelp
: 'chapter_pveceph',
321 mixedversions
: false,
334 xclass
: 'Ext.app.ViewController',
338 let view
= me
.getView();
339 let vm
= me
.getViewModel();
340 let nodename
= vm
.get('nodename');
341 let sm
= view
.getSelectionModel();
342 Proxmox
.Utils
.API2Request({
343 url
: "/nodes/" + nodename
+ "/ceph/osd",
346 failure: function(response
, opts
) {
347 let msg
= response
.htmlStatus
;
348 PVE
.Utils
.showCephInstallOrMask(view
, msg
, nodename
, win
=>
349 view
.mon(win
, 'cephInstallWindowClosed', me
.reload
),
352 success: function(response
, opts
) {
353 let data
= response
.result
.data
;
354 let selected
= view
.getSelection();
356 if (selected
.length
) {
357 name
= selected
[0].data
.name
;
359 data
.versions
= data
.versions
|| {};
360 vm
.set('versions', data
.versions
);
361 // extract max version
362 let maxversion
= "0";
363 let mixedversions
= false;
365 traverse = function(node
, fn
) {
367 if (Array
.isArray(node
.children
)) {
368 node
.children
.forEach(c
=> { traverse(c
, fn
); });
371 traverse(data
.root
, node
=> {
372 // compatibility for old api call
373 if (node
.type
=== 'host' && !node
.version
) {
374 node
.version
= data
.versions
[node
.name
];
377 if (node
.version
=== undefined) {
381 if (PVE
.Utils
.compare_ceph_versions(node
.version
, maxversion
) !== 0 && maxversion
!== "0") {
382 mixedversions
= true;
385 if (PVE
.Utils
.compare_ceph_versions(node
.version
, maxversion
) > 0) {
386 maxversion
= node
.version
;
389 vm
.set('maxversion', maxversion
);
390 vm
.set('mixedversions', mixedversions
);
392 view
.setRootNode(data
.root
);
395 let node
= view
.getRootNode().findChild('name', name
, true);
397 view
.setSelection([node
]);
401 let flags
= data
.flags
.split(',');
402 vm
.set('flags', flags
);
407 osd_cmd: function(comp
) {
409 let vm
= this.getViewModel();
411 let params
= comp
.params
|| {};
412 let osdid
= vm
.get('osdid');
414 let doRequest = function() {
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');
420 Proxmox
.Utils
.API2Request({
421 url
: `/nodes/${targetnode}/ceph/osd/${osdid}/${cmd}`,
422 waitMsgTarget
: me
.getView(),
425 success
: () => { me
.reload(); },
426 failure: function(response
, opts
) {
427 Ext
.Msg
.alert(gettext('Error'), response
.htmlStatus
);
432 if (cmd
=== 'scrub') {
433 Ext
.MessageBox
.defaultButton
= params
.deep
=== 1 ? 2 : 1;
435 title
: gettext('Confirm'),
436 icon
: params
.deep
=== 1 ? Ext
.Msg
.WARNING
: Ext
.Msg
.QUESTION
,
437 msg
: params
.deep
!== 1
438 ? Ext
.String
.format(gettext("Scrub OSD.{0}"), osdid
)
439 : Ext
.String
.format(gettext("Deep Scrub OSD.{0}"), osdid
) +
440 "<br>Caution: This can reduce performance while it is running.",
441 buttons
: Ext
.Msg
.YESNO
,
442 callback: function(btn
) {
454 create_osd: function() {
456 let vm
= this.getViewModel();
457 Ext
.create('PVE.CephCreateOsd', {
458 nodename
: vm
.get('nodename'),
459 taskDone
: () => { me
.reload(); },
463 destroy_osd
: async
function() {
465 let vm
= this.getViewModel();
472 let flagsPromise
= Proxmox
.Async
.api2({
473 url
: `/cluster/ceph/flags`,
477 let statusPromise
= Proxmox
.Async
.api2({
478 url
: `/cluster/ceph/status`,
482 me
.getView().mask(gettext('Loading...'));
485 let result
= await Promise
.all([flagsPromise
, statusPromise
]);
487 let flagsData
= result
[0].result
.data
;
488 let statusData
= result
[1].result
.data
;
490 let flags
= Array
.from(
491 flagsData
.filter(v
=> v
.value
),
493 ).filter(v
=> ['norebalance', 'norecover', 'noout'].includes(v
));
496 warnings
.flags
= true;
498 if (Object
.keys(statusData
.pgmap
).includes('degraded_objects')) {
499 warnings
.degraded
= true;
502 Ext
.Msg
.alert(gettext('Error'), error
.htmlStatus
);
503 me
.getView().unmask();
507 me
.getView().unmask();
508 Ext
.create('PVE.CephRemoveOsd', {
509 nodename
: vm
.get('osdhost'),
510 osdid
: vm
.get('osdid'),
512 taskDone
: () => { me
.reload(); },
517 set_flags: function() {
519 let vm
= this.getViewModel();
520 Ext
.create('PVE.CephSetFlags', {
521 nodename
: vm
.get('nodename'),
522 taskDone
: () => { me
.reload(); },
526 service_cmd: function(comp
) {
528 let vm
= this.getViewModel();
529 let cmd
= comp
.cmd
|| comp
;
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(),
537 success: function(response
, options
) {
538 let upid
= response
.result
.data
;
539 let win
= Ext
.create('Proxmox.window.TaskProgress', {
541 taskDone
: () => { me
.reload(); },
545 failure: function(response
, opts
) {
546 Ext
.Msg
.alert(gettext('Error'), response
.htmlStatus
);
551 if (cmd
=== "stop") {
552 Proxmox
.Utils
.API2Request({
553 url
: `/nodes/${vm.get('osdhost')}/ceph/cmd-safety`,
559 waitMsgTarget
: me
.getView(),
561 success: function({ result
: { data
} }) {
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') {
579 failure
: response
=> Ext
.Msg
.alert(gettext('Error'), response
.htmlStatus
),
586 set_selection_status: function(tp
, selection
) {
587 if (selection
.length
< 1) {
590 let rec
= selection
[0];
591 let vm
= this.getViewModel();
593 let isOsd
= rec
.data
.host
&& rec
.data
.type
=== 'osd' && rec
.data
.id
>= 0;
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);
604 render_status: function(value
, metaData
, rec
) {
608 let inout
= rec
.data
.in ? 'in' : 'out';
609 let updownicon
= value
=== 'up' ? 'good fa-arrow-circle-up'
610 : 'critical fa-arrow-circle-down';
612 let inouticon
= rec
.data
.in ? 'good fa-circle'
613 : 'warning fa-circle-o';
615 let text
= value
+ ' <i class="fa ' + updownicon
+ '"></i> / ' +
616 inout
+ ' <i class="fa ' + inouticon
+ '"></i>';
621 render_wal: function(value
, metaData
, rec
) {
623 rec
.data
.osdtype
=== 'bluestore' &&
624 rec
.data
.type
=== 'osd') {
630 render_version: function(value
, metadata
, rec
) {
631 let vm
= this.getViewModel();
632 let versions
= vm
.get('versions');
634 let version
= value
|| "";
635 let maxversion
= vm
.get('maxversion');
636 if (value
&& PVE
.Utils
.compare_ceph_versions(value
, maxversion
) !== 0) {
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) {
639 icon
= PVE
.Utils
.get_ceph_icon_html('HEALTH_UPGRADE');
641 icon
= PVE
.Utils
.get_ceph_icon_html('HEALTH_OLD');
643 } else if (value
&& vm
.get('mixedversions')) {
644 icon
= PVE
.Utils
.get_ceph_icon_html('HEALTH_OK');
647 return icon
+ version
;
650 render_osd_val: function(value
, metaData
, rec
) {
651 return rec
.data
.type
=== 'osd' ? value
: '';
653 render_osd_weight: function(value
, metaData
, rec
) {
654 if (rec
.data
.type
!== 'osd') {
657 return Ext
.util
.Format
.number(value
, '0.00###');
660 render_osd_latency: function(value
, metaData
, rec
) {
661 if (rec
.data
.type
!== 'osd') {
664 let commit_ms
= rec
.data
.commit_latency_ms
,
665 apply_ms
= rec
.data
.apply_latency_ms
;
666 return apply_ms
+ ' / ' + commit_ms
;
669 render_osd_size: function(value
, metaData
, rec
) {
670 return this.render_osd_val(Proxmox
.Utils
.render_size(value
), metaData
, rec
);
675 selectionchange
: 'set_selection_status',
679 init: function(view
) {
681 let vm
= this.getViewModel();
683 if (!view
.pveSelNode
.data
.node
) {
684 throw "no node name specified";
687 vm
.set('nodename', view
.pveSelNode
.data
.node
);
695 stateId
: 'grid-ceph-osd',
714 text
: gettext("Class"),
715 dataIndex
: 'device_class',
721 dataIndex
: 'osdtype',
726 text
: "Bluestore Device",
727 dataIndex
: 'blfsdev',
743 renderer
: 'render_wal',
751 renderer
: 'render_status',
755 text
: gettext('Version'),
756 dataIndex
: 'version',
758 renderer
: 'render_version',
762 dataIndex
: 'crush_weight',
764 renderer
: 'render_osd_weight',
769 dataIndex
: 'reweight',
771 renderer
: 'render_osd_weight',
775 text
: gettext('Used') + ' (%)',
776 dataIndex
: 'percent_used',
778 renderer: function(value
, metaData
, rec
) {
779 if (rec
.data
.type
!== 'osd') {
782 return Ext
.util
.Format
.number(value
, '0.00');
787 text
: gettext('Total'),
788 dataIndex
: 'total_space',
790 renderer
: 'render_osd_size',
794 text
: 'Apply/Commit<br>Latency (ms)',
795 dataIndex
: 'apply_latency_ms',
797 renderer
: 'render_osd_latency',
806 text
: gettext('Reload'),
807 iconCls
: 'fa fa-refresh',
812 text
: gettext('Create') + ': OSD',
813 handler
: 'create_osd',
816 text
: Ext
.String
.format(gettext('Manage {0}'), 'Global Flags'),
817 handler
: 'set_flags',
834 gettext('No OSD selected'),
839 text
: gettext('Start'),
840 iconCls
: 'fa fa-play',
843 disabled
: '{!downOsd}',
846 handler
: 'service_cmd',
849 text
: gettext('Stop'),
850 iconCls
: 'fa fa-stop',
853 disabled
: '{!upOsd}',
856 handler
: 'service_cmd',
859 text
: gettext('Restart'),
860 iconCls
: 'fa fa-refresh',
863 disabled
: '{!upOsd}',
866 handler
: 'service_cmd',
871 iconCls
: 'fa fa-circle-o',
874 disabled
: '{!inOsd}',
881 iconCls
: 'fa fa-circle',
884 disabled
: '{!outOsd}',
891 text
: gettext('More'),
892 iconCls
: 'fa fa-bars',
895 disabled
: '{!isOsd}',
899 text
: gettext('Scrub'),
900 iconCls
: 'fa fa-shower',
905 text
: gettext('Deep Scrub'),
906 iconCls
: 'fa fa-bath',
914 text
: gettext('Destroy'),
916 iconCls
: 'fa fa-fw fa-trash-o',
918 disabled
: '{!downOsd}',
920 handler
: 'destroy_osd',
928 'name', 'type', 'status', 'host', 'in', 'id',
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' },
941 type
: 'string', name
: 'version', calculate: function(data
) {
942 return PVE
.Utils
.parse_ceph_version(data
);
946 type
: 'string', name
: 'iconCls', calculate: function(data
) {
952 return 'fa x-fa-tree ' + iconMap
[data
.type
];
955 { type
: 'number', name
: 'crush_weight' },