]> git.proxmox.com Git - pve-manager.git/blobdiff - www/manager6/ceph/Pool.js
update shipped appliance info index
[pve-manager.git] / www / manager6 / ceph / Pool.js
index 9cd79177016156d949f7df7894fd021e79e85ac9..c61d4f71baea88aa4e8f646b2aba7021411591a8 100644 (file)
@@ -7,6 +7,52 @@ Ext.define('PVE.CephPoolInputPanel', {
     onlineHelp: 'pve_ceph_pools',
 
     subject: 'Ceph Pool',
+
+    defaultSize: undefined,
+    defaultMinSize: undefined,
+
+    controller: {
+       xclass: 'Ext.app.ViewController',
+
+       init: function(view) {
+           let vm = this.getViewModel();
+           vm.set('size', Number(view.defaultSize));
+           vm.set('minSize', Number(view.defaultMinSize));
+       },
+       sizeChange: function(field, val) {
+           let vm = this.getViewModel();
+           let minSize = Math.round(val / 2);
+           if (minSize > 1) {
+               vm.set('minSize', minSize);
+           }
+           vm.set('size', val); // bind does not work in a pmxDisplayEditField, update manually
+       },
+    },
+
+    viewModel: {
+       data: {
+           minSize: null,
+           size: null,
+       },
+       formulas: {
+           minSizeLabel: (get) => {
+               if (get('showMinSizeOneWarning') || get('showMinSizeHalfWarning')) {
+                   return `${gettext('Min. Size')} <i class="fa fa-exclamation-triangle warning"></i>`;
+               }
+               return gettext('Min. Size');
+           },
+           showMinSizeOneWarning: (get) => get('minSize') === 1,
+           showMinSizeHalfWarning: (get) => {
+               let minSize = get('minSize');
+               let size = get('size');
+               if (minSize === 1) {
+                   return false;
+               }
+               return minSize < (size / 2) && minSize !== size;
+           },
+       },
+    },
+
     column1: [
        {
            xtype: 'pmxDisplayEditField',
@@ -14,25 +60,27 @@ Ext.define('PVE.CephPoolInputPanel', {
            cbind: {
                editable: '{isCreate}',
                value: '{pool_name}',
-               disabled: '{!isCreate}',
            },
            name: 'name',
            allowBlank: false,
        },
        {
-           xtype: 'proxmoxintegerfield',
+           xtype: 'pmxDisplayEditField',
+           cbind: {
+               editable: '{!isErasure}',
+           },
            fieldLabel: gettext('Size'),
            name: 'size',
-           value: 3,
-           minValue: 2,
-           maxValue: 7,
-           allowBlank: false,
-           listeners: {
-               change: function(field, val) {
-                   let size = Math.round(val / 2);
-                   if (size > 1) {
-                       field.up('inputpanel').down('field[name=min_size]').setValue(size);
-                   }
+           editConfig: {
+               xtype: 'proxmoxintegerfield',
+               cbind: {
+                   value: (get) => get('defaultSize'),
+               },
+               minValue: 2,
+               maxValue: 7,
+               allowBlank: false,
+               listeners: {
+                   change: 'sizeChange',
                },
            },
        },
@@ -47,7 +95,7 @@ Ext.define('PVE.CephPoolInputPanel', {
                ['on', 'on'],
                ['off', 'off'],
            ],
-           value: 'warn',
+           value: 'on', // FIXME: check ceph version and only default to on on octopus and newer
            allowBlank: false,
            autoSelect: false,
            labelWidth: 140,
@@ -70,42 +118,55 @@ Ext.define('PVE.CephPoolInputPanel', {
     advancedColumn1: [
        {
            xtype: 'proxmoxintegerfield',
-           fieldLabel: gettext('Min. Size'),
+           bind: {
+               fieldLabel: '{minSizeLabel}',
+               value: '{minSize}',
+           },
            name: 'min_size',
-           value: 2,
            cbind: {
-               minValue: (get) => get('isCreate') ? 2 : 1,
-           },
-           maxValue: 7,
-           allowBlank: false,
-           listeners: {
-               change: function(field, val) {
-                   let warn = true;
-                   let warn_text = gettext('Min. Size');
-
-                   if (val < 2) {
-                       warn = false;
-                       warn_text = gettext('Min. Size') + ' <i class="fa fa-exclamation-triangle warning"></i>';
+               value: (get) => get('defaultMinSize'),
+               minValue: (get) => {
+                   if (Number(get('defaultMinSize')) === 1) {
+                       return 1;
+                   } else {
+                       return get('isCreate') ? 2 : 1;
                    }
-
-                   field.up().down('field[name=min_size-warning]').setHidden(warn);
-                   field.setFieldLabel(warn_text);
                },
            },
+           maxValue: 7,
+           allowBlank: false,
        },
        {
            xtype: 'displayfield',
-           name: 'min_size-warning',
+           bind: {
+               hidden: '{!showMinSizeHalfWarning}',
+           },
+           hidden: true,
            userCls: 'pmx-hint',
-           value: 'A pool with min_size=1 could lead to data loss, incomplete PGs or unfound objects.',
+           value: gettext('min_size < size/2 can lead to data loss, incomplete PGs or unfound objects.'),
+       },
+       {
+           xtype: 'displayfield',
+           bind: {
+               hidden: '{!showMinSizeOneWarning}',
+           },
            hidden: true,
+           userCls: 'pmx-hint',
+           value: gettext('a min_size of 1 is not recommended and can lead to data loss'),
        },
        {
-           xtype: 'pveCephRuleSelector',
+           xtype: 'pmxDisplayEditField',
+           cbind: {
+               editable: '{!isErasure}',
+               nodename: '{nodename}',
+               isCreate: '{isCreate}',
+           },
            fieldLabel: 'Crush Rule', // do not localize
-           cbind: { nodename: '{nodename}' },
            name: 'crush_rule',
-           allowBlank: false,
+           editConfig: {
+               xtype: 'pveCephRuleSelector',
+               allowBlank: false,
+           },
        },
        {
            xtype: 'proxmoxintegerfield',
@@ -121,34 +182,41 @@ Ext.define('PVE.CephPoolInputPanel', {
     advancedColumn2: [
        {
            xtype: 'numberfield',
-           fieldLabel: gettext('Target Size Ratio'),
+           fieldLabel: gettext('Target Ratio'),
            name: 'target_size_ratio',
-           labelWidth: 140,
            minValue: 0,
            decimalPrecision: 3,
            allowBlank: true,
            emptyText: '0.0',
+           autoEl: {
+               tag: 'div',
+               'data-qtip': gettext('The ratio of storage amount this pool will consume compared to other pools with ratios. Used for auto-scaling.'),
+           },
        },
        {
            xtype: 'pveSizeField',
-           fieldLabel: gettext('Target Size') + ' (GiB)',
            name: 'target_size',
-           labelWidth: 140,
+           fieldLabel: gettext('Target Size'),
            unit: 'GiB',
            minValue: 0,
            allowBlank: true,
+           allowZero: true,
            emptyText: '0',
+           emptyValue: 0,
+           autoEl: {
+               tag: 'div',
+               'data-qtip': gettext('The amount of data eventually stored in this pool. Used for auto-scaling.'),
+           },
        },
        {
            xtype: 'displayfield',
            userCls: 'pmx-hint',
-           value: 'Target Size Ratio takes precedence.',
+           value: Ext.String.format(gettext('{0} takes precedence.'), gettext('Target Ratio')), // FIXME: tooltip?
        },
        {
            xtype: 'proxmoxintegerfield',
            fieldLabel: 'Min. # of PGs',
            name: 'pg_num_min',
-           labelWidth: 140,
            minValue: 0,
            allowBlank: true,
            emptyText: '0',
@@ -166,22 +234,24 @@ Ext.define('PVE.CephPoolInputPanel', {
     },
 });
 
-Ext.define('PVE.CephPoolEdit', {
+Ext.define('PVE.Ceph.PoolEdit', {
     extend: 'Proxmox.window.Edit',
     alias: 'widget.pveCephPoolEdit',
-    xtype: 'pveCephPoolEdit',
     mixins: ['Proxmox.Mixin.CBind'],
 
     cbindData: {
        pool_name: '',
        isCreate: (cfg) => !cfg.pool_name,
+       defaultSize: undefined,
+       defaultMinSize: undefined,
     },
 
     cbind: {
        autoLoad: get => !get('isCreate'),
        url: get => get('isCreate')
-           ? `/nodes/${get('nodename')}/ceph/pools`
-           : `/nodes/${get('nodename')}/ceph/pools/${get('pool_name')}`,
+           ? `/nodes/${get('nodename')}/ceph/pool`
+           : `/nodes/${get('nodename')}/ceph/pool/${get('pool_name')}`,
+       loadUrl: get => `/nodes/${get('nodename')}/ceph/pool/${get('pool_name')}/status`,
        method: get => get('isCreate') ? 'POST' : 'PUT',
     },
 
@@ -194,12 +264,15 @@ Ext.define('PVE.CephPoolEdit', {
        cbind: {
            nodename: '{nodename}',
            pool_name: '{pool_name}',
+           isErasure: '{isErasure}',
            isCreate: '{isCreate}',
+           defaultSize: '{defaultSize}',
+           defaultMinSize: '{defaultMinSize}',
        },
     }],
 });
 
-Ext.define('PVE.node.CephPoolList', {
+Ext.define('PVE.node.Ceph.PoolList', {
     extend: 'Ext.grid.GridPanel',
     alias: 'widget.pveNodeCephPoolList',
 
@@ -212,6 +285,14 @@ Ext.define('PVE.node.CephPoolList', {
     features: [{ ftype: 'summary' }],
 
     columns: [
+       {
+           text: gettext('Pool #'),
+           minWidth: 70,
+           flex: 1,
+           align: 'right',
+           sortable: true,
+           dataIndex: 'pool',
+       },
        {
            text: gettext('Name'),
            minWidth: 120,
@@ -219,27 +300,32 @@ Ext.define('PVE.node.CephPoolList', {
            sortable: true,
            dataIndex: 'pool_name',
        },
+       {
+           text: gettext('Type'),
+           minWidth: 100,
+           flex: 1,
+           dataIndex: 'type',
+           hidden: true,
+       },
        {
            text: gettext('Size') + '/min',
            minWidth: 100,
            flex: 1,
            align: 'right',
-           renderer: function(v, meta, rec) {
-               return v + '/' + rec.data.min_size;
-           },
+           renderer: (v, meta, rec) => `${v}/${rec.data.min_size}`,
            dataIndex: 'size',
        },
        {
            text: '# of Placement Groups',
            flex: 1,
-           minWidth: 150,
+           minWidth: 100,
            align: 'right',
            dataIndex: 'pg_num',
        },
        {
            text: gettext('Optimal # of PGs'),
            flex: 1,
-           minWidth: 140,
+           minWidth: 100,
            align: 'right',
            dataIndex: 'pg_num_final',
            renderer: function(value, metaData) {
@@ -253,15 +339,15 @@ Ext.define('PVE.node.CephPoolList', {
        {
            text: gettext('Min. # of PGs'),
            flex: 1,
-           minWidth: 140,
+           minWidth: 100,
            align: 'right',
            dataIndex: 'pg_num_min',
            hidden: true,
        },
        {
-           text: gettext('Target Size Ratio'),
+           text: gettext('Target Ratio'),
            flex: 1,
-           minWidth: 140,
+           minWidth: 100,
            align: 'right',
            dataIndex: 'target_size_ratio',
            renderer: Ext.util.Format.numberRenderer('0.0000'),
@@ -270,12 +356,12 @@ Ext.define('PVE.node.CephPoolList', {
        {
            text: gettext('Target Size'),
            flex: 1,
-           minWidth: 140,
+           minWidth: 100,
            align: 'right',
            dataIndex: 'target_size',
            hidden: true,
            renderer: function(v, metaData, rec) {
-               let value = PVE.Utils.render_size(v);
+               let value = Proxmox.Utils.render_size(v);
                if (rec.data.target_size_ratio > 0) {
                    value = '<i class="fa fa-info-circle faded"></i> ' + value;
                    metaData.tdAttr = 'data-qtip="Target Size Ratio takes precedence over Target Size."';
@@ -286,7 +372,7 @@ Ext.define('PVE.node.CephPoolList', {
        {
            text: gettext('Autoscale Mode'),
            flex: 1,
-           minWidth: 140,
+           minWidth: 100,
            align: 'right',
            dataIndex: 'pg_autoscale_mode',
        },
@@ -295,24 +381,22 @@ Ext.define('PVE.node.CephPoolList', {
            flex: 1,
            align: 'right',
            minWidth: 150,
-           renderer: function(v, meta, rec) {
-               return v + ' (' + rec.data.crush_rule + ')';
-           },
+           renderer: (v, meta, rec) => `${v} (${rec.data.crush_rule})`,
            dataIndex: 'crush_rule_name',
        },
        {
            text: gettext('Used') + ' (%)',
            flex: 1,
-           minWidth: 180,
+           minWidth: 150,
            sortable: true,
            align: 'right',
            dataIndex: 'bytes_used',
            summaryType: 'sum',
-           summaryRenderer: PVE.Utils.render_size,
+           summaryRenderer: Proxmox.Utils.render_size,
            renderer: function(v, meta, rec) {
                let percentage = Ext.util.Format.percent(rec.data.percent_used, '0.00');
-               let used = PVE.Utils.render_size(v);
-               return used + ' (' + percentage + ')';
+               let used = Proxmox.Utils.render_size(v);
+               return `${used} (${percentage})`;
            },
        },
     ],
@@ -331,33 +415,25 @@ Ext.define('PVE.node.CephPoolList', {
            storeid: 'ceph-pool-list' + nodename,
            model: 'ceph-pool-list',
            proxy: {
-                type: 'proxmox',
-                url: "/api2/json/nodes/" + nodename + "/ceph/pools",
+               type: 'proxmox',
+               url: `/api2/json/nodes/${nodename}/ceph/pool`,
            },
        });
        let store = Ext.create('Proxmox.data.DiffStore', { rstore: rstore });
 
-       PVE.Utils.handleStoreErrorOrMask(
-           me,
-           rstore,
-           /not (installed|initialized)/i,
-           (_, error) => {
-               rstore.stopUpdate();
-               PVE.Utils.showCephInstallOrMask(me, error.statusText, nodename, win => {
-                   me.mon(win, 'cephInstallWindowClosed', () => rstore.startUpdate());
-               });
-           },
-       );
+       // manages the "install ceph?" overlay
+       PVE.Utils.monitor_ceph_installed(me, rstore, nodename);
 
        var run_editor = function() {
            let rec = sm.getSelection()[0];
            if (!rec || !rec.data.pool_name) {
                return;
            }
-           Ext.create('PVE.CephPoolEdit', {
+           Ext.create('PVE.Ceph.PoolEdit', {
                title: gettext('Edit') + ': Ceph Pool',
                nodename: nodename,
                pool_name: rec.data.pool_name,
+               isErasure: rec.data.type === 'erasure',
                autoShow: true,
                listeners: {
                    destroy: () => rstore.load(),
@@ -372,13 +448,37 @@ Ext.define('PVE.node.CephPoolList', {
                {
                    text: gettext('Create'),
                    handler: function() {
-                       Ext.create('PVE.CephPoolEdit', {
-                           title: gettext('Create') + ': Ceph Pool',
-                           isCreate: true,
-                           nodename: nodename,
-                           autoShow: true,
-                           listeners: {
-                               destroy: () => rstore.load(),
+                       let keys = [
+                           'global:osd-pool-default-min-size',
+                           'global:osd-pool-default-size',
+                       ];
+                       let params = {
+                           'config-keys': keys.join(';'),
+                       };
+
+                       Proxmox.Utils.API2Request({
+                           url: '/nodes/localhost/ceph/cfg/value',
+                           method: 'GET',
+                           params,
+                           waitMsgTarget: me.getView(),
+                           failure: response => Ext.Msg.alert(gettext('Error'), response.htmlStatus),
+                           success: function({ result: { data } }) {
+                               let global = data.global;
+                               let defaultSize = global?.['osd-pool-default-size'] ?? 3;
+                               let defaultMinSize = global?.['osd-pool-default-min-size'] ?? 2;
+
+                               Ext.create('PVE.Ceph.PoolEdit', {
+                                   title: gettext('Create') + ': Ceph Pool',
+                                   isCreate: true,
+                                   isErasure: false,
+                                   defaultSize,
+                                   defaultMinSize,
+                                   nodename: nodename,
+                                   autoShow: true,
+                                   listeners: {
+                                       destroy: () => rstore.load(),
+                                   },
+                               });
                            },
                        });
                    },
@@ -401,9 +501,9 @@ Ext.define('PVE.node.CephPoolList', {
                            return;
                        }
                        let poolName = rec.data.pool_name;
-                       Ext.create('PVE.window.SafeDestroy', {
+                       Ext.create('Proxmox.window.SafeDestroy', {
                            showProgress: true,
-                           url: `/nodes/${nodename}/ceph/pools/${poolName}`,
+                           url: `/nodes/${nodename}/ceph/pool/${poolName}`,
                            params: {
                                remove_storages: 1,
                            },
@@ -411,6 +511,7 @@ Ext.define('PVE.node.CephPoolList', {
                                type: 'CephPool',
                                id: poolName,
                            },
+                           taskName: 'cephdestroypool',
                            autoShow: true,
                            listeners: {
                                destroy: () => rstore.load(),
@@ -461,34 +562,38 @@ Ext.define('PVE.form.CephRuleSelector', {
     queryMode: 'local',
 
     initComponent: function() {
-       var me = this;
+       let me = this;
 
        if (!me.nodename) {
            throw "no nodename given";
        }
 
-       var store = Ext.create('Ext.data.Store', {
-           fields: ['name'],
-           sorters: 'name',
-           proxy: {
-               type: 'proxmox',
-               url: `/api2/json/nodes/${me.nodename}/ceph/rules`,
-           },
-       });
+       me.originalAllowBlank = me.allowBlank;
+       me.allowBlank = true;
 
        Ext.apply(me, {
-           store: store,
-       });
-
-       me.callParent();
+           store: {
+               fields: ['name'],
+               sorters: 'name',
+               proxy: {
+                   type: 'proxmox',
+                   url: `/api2/json/nodes/${me.nodename}/ceph/rules`,
+               },
+               autoLoad: {
+                   callback: (records, op, success) => {
+                       if (me.isCreate && success && records.length > 0) {
+                           me.select(records[0]);
+                       }
 
-       store.load({
-           callback: function(rec, op, success) {
-               if (success && rec.length > 0) {
-                   me.select(rec[0]);
-               }
+                       me.allowBlank = me.originalAllowBlank;
+                       delete me.originalAllowBlank;
+                       me.validate();
+                   },
+               },
            },
        });
+
+       me.callParent();
     },
 
 });