From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: from firstgate.proxmox.com (firstgate.proxmox.com [212.224.123.68]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (2048 bits)) (No client certificate requested) by lists.proxmox.com (Postfix) with ESMTPS id 412006735D for ; Tue, 12 Jan 2021 11:22:34 +0100 (CET) Received: from firstgate.proxmox.com (localhost [127.0.0.1]) by firstgate.proxmox.com (Proxmox) with ESMTP id 031BD22C6E for ; Tue, 12 Jan 2021 11:22:04 +0100 (CET) Received: from proxmox-new.maurer-it.com (proxmox-new.maurer-it.com [212.186.127.180]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (2048 bits)) (No client certificate requested) by firstgate.proxmox.com (Proxmox) with ESMTPS id 5EA8122BB0 for ; Tue, 12 Jan 2021 11:21:59 +0100 (CET) Received: from proxmox-new.maurer-it.com (localhost.localdomain [127.0.0.1]) by proxmox-new.maurer-it.com (Proxmox) with ESMTP id 1F65C44899 for ; Tue, 12 Jan 2021 11:21:59 +0100 (CET) From: Alwin Antreich To: pve-devel@lists.proxmox.com Date: Tue, 12 Jan 2021 11:21:51 +0100 Message-Id: <20210112102153.3215121-9-a.antreich@proxmox.com> X-Mailer: git-send-email 2.29.2 In-Reply-To: <20210112102153.3215121-1-a.antreich@proxmox.com> References: <20210112102153.3215121-1-a.antreich@proxmox.com> MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-SPAM-LEVEL: Spam detection results: 0 AWL 0.017 Adjusted score from AWL reputation of From: address KAM_DMARC_STATUS 0.01 Test Rule for DKIM or SPF Failure with Strict Alignment RCVD_IN_DNSWL_MED -2.3 Sender listed at https://www.dnswl.org/, medium trust SPF_HELO_NONE 0.001 SPF: HELO does not publish an SPF Record SPF_PASS -0.001 SPF: sender matches SPF record Subject: [pve-devel] [PATCH manager v3 08/10] ceph: gui: rework pool input panel X-BeenThere: pve-devel@lists.proxmox.com X-Mailman-Version: 2.1.29 Precedence: list List-Id: Proxmox VE development discussion List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-List-Received-Date: Tue, 12 Jan 2021 10:22:34 -0000 * add the ability to edit an existing pool * allow adjustment of autoscale settings * warn if user specifies min_size 1 * disallow min_size 1 on pool create * calculate min_size replica by size Signed-off-by: Alwin Antreich --- www/manager6/ceph/Pool.js | 249 +++++++++++++++++++++++++++++--------- 1 file changed, 191 insertions(+), 58 deletions(-) diff --git a/www/manager6/ceph/Pool.js b/www/manager6/ceph/Pool.js index 75c95fce..bd395956 100644 --- a/www/manager6/ceph/Pool.js +++ b/www/manager6/ceph/Pool.js @@ -1,17 +1,21 @@ -Ext.define('PVE.CephCreatePool', { - extend: 'Proxmox.window.Edit', - alias: 'widget.pveCephCreatePool', +Ext.define('PVE.CephPoolInputPanel', { + extend: 'Proxmox.panel.InputPanel', + xtype: 'pveCephPoolInputPanel', + mixins: ['Proxmox.Mixin.CBind'], showProgress: true, onlineHelp: 'pve_ceph_pools', subject: 'Ceph Pool', - isCreate: true, - method: 'POST', - items: [ + column1: [ { - xtype: 'textfield', + xtype: 'pmxDisplayEditField', fieldLabel: gettext('Name'), + cbind: { + editable: '{isCreate}', + value: '{pool_name}', + disabled: '{!isCreate}' + }, name: 'name', allowBlank: false }, @@ -20,75 +24,180 @@ Ext.define('PVE.CephCreatePool', { fieldLabel: gettext('Size'), name: 'size', value: 3, - minValue: 1, + minValue: 2, maxValue: 7, - allowBlank: false + 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); + } + }, + }, }, + ], + column2: [ + { + xtype: 'proxmoxKVComboBox', + fieldLabel: 'PG Autoscale Mode', + name: 'pg_autoscale_mode', + comboItems: [ + ['warn', 'warn'], + ['on', 'on'], + ['off', 'off'], + ], + value: 'warn', + allowBlank: false, + autoSelect: false, + labelWidth: 140, + }, + { + xtype: 'proxmoxcheckbox', + fieldLabel: gettext('Add as Storage'), + cbind: { + value: '{isCreate}', + hidden: '{!isCreate}', + }, + name: 'add_storages', + labelWidth: 140, + autoEl: { + tag: 'div', + 'data-qtip': gettext('Add the new pool to the cluster storage configuration.'), + }, + }, + ], + advancedColumn1: [ { xtype: 'proxmoxintegerfield', fieldLabel: gettext('Min. Size'), name: 'min_size', value: 2, - minValue: 1, + cbind: { + minValue: (get) => get('isCreate') ? 2 : 1, + }, maxValue: 7, - allowBlank: false + 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') + ' '; + } + + field.up().down('field[name=min_size-warning]').setHidden(warn); + field.setFieldLabel(warn_text); + } + }, + }, + { + xtype: 'displayfield', + name: 'min_size-warning', + userCls: 'pmx-hint', + value: 'A pool with min_size=1 could lead to data loss, incomplete PGs or unfound objects.', + hidden: true, }, { xtype: 'pveCephRuleSelector', fieldLabel: 'Crush Rule', // do not localize + cbind: { nodename: '{nodename}' }, name: 'crush_rule', allowBlank: false }, - { - xtype: 'proxmoxKVComboBox', - fieldLabel: 'PG Autoscale Mode', // do not localize - name: 'pg_autoscale_mode', - comboItems: [ - ['warn', 'warn'], - ['on', 'on'], - ['off', 'off'], - ], - value: 'warn', - allowBlank: false, - autoSelect: false, - }, { xtype: 'proxmoxintegerfield', - fieldLabel: 'pg_num', + fieldLabel: '# of PGs', name: 'pg_num', value: 128, - minValue: 8, + minValue: 1, maxValue: 32768, + allowBlank: false, + emptyText: 128, + }, + ], + advancedColumn2: [ + { + xtype: 'numberfield', + fieldLabel: gettext('Target Size Ratio'), + name: 'target_size_ratio', + labelWidth: 140, + minValue: 0, + decimalPrecision: 3, allowBlank: true, - emptyText: gettext('Autoscale'), + emptyText: '0.0', }, { - xtype: 'proxmoxcheckbox', - fieldLabel: gettext('Add as Storage'), - value: true, - name: 'add_storages', - autoEl: { - tag: 'div', - 'data-qtip': gettext('Add the new pool to the cluster storage configuration.'), - }, - } + xtype: 'numberfield', + fieldLabel: gettext('Target Size') + ' (GiB)', + name: 'target_size', + labelWidth: 140, + minValue: 0, + allowBlank: true, + emptyText: '0', + }, + { + xtype: 'displayfield', + name: 'min_size-warning', + userCls: 'pmx-hint', + value: 'Target Size Ratio takes precedence.', + }, ], - initComponent : function() { - var me = this; - if (!me.nodename) { - throw "no node name specified"; + onGetValues: function(values) { + Object.keys(values || {}).forEach(function(name) { + if (values[name] === '') { + delete values[name]; + } + }); + + if (values['target_size'] && values['target_size'] !== 0) { + values['target_size'] = values['target_size']*1024*1024*1024; } + return values; + }, - Ext.apply(me, { - url: "/nodes/" + me.nodename + "/ceph/pools", - defaults: { - nodename: me.nodename - } - }); + setValues: function(values) { + if (values['target_size'] && values['target_size'] !== 0) { + values['target_size'] = values['target_size']/1024/1024/1024; + } - me.callParent(); - } + this.callParent([values]); + }, + +}); + +Ext.define('PVE.CephPoolEdit', { + extend: 'Proxmox.window.Edit', + alias: 'widget.pveCephPoolEdit', + xtype: 'pveCephPoolEdit', + mixins: ['Proxmox.Mixin.CBind'], + + cbindData: { + pool_name: '', + isCreate: (cfg) => !cfg.pool_name, + }, + + cbind: { + autoLoad: get => !get('isCreate'), + url: get => get('isCreate') ? + `/nodes/${get('nodename')}/ceph/pools` : + `/nodes/${get('nodename')}/ceph/pools/${get('pool_name')}`, + method: get => get('isCreate') ? 'POST' : 'PUT', + }, + + subject: gettext('Ceph Pool'), + + items: [{ + xtype: 'pveCephPoolInputPanel', + cbind: { + nodename: '{nodename}', + pool_name: '{pool_name}', + isCreate: '{isCreate}', + }, + }], }); Ext.define('PVE.node.CephPoolList', { @@ -221,6 +330,9 @@ Ext.define('PVE.node.CephPoolList', { }); var store = Ext.create('Proxmox.data.DiffStore', { rstore: rstore }); + var reload = function() { + rstore.load(); + }; var regex = new RegExp("not (installed|initialized)", "i"); PVE.Utils.handleStoreErrorOrMask(me, rstore, regex, function(me, error){ @@ -237,14 +349,36 @@ Ext.define('PVE.node.CephPoolList', { var create_btn = new Ext.Button({ text: gettext('Create'), handler: function() { - var win = Ext.create('PVE.CephCreatePool', { - nodename: nodename + var win = Ext.create('PVE.CephPoolEdit', { + title: gettext('Create') + ': Ceph Pool', + isCreate: true, + nodename: nodename, }); win.show(); - win.on('destroy', function() { - rstore.load(); - }); + win.on('destroy', reload); + } + }); + + var run_editor = function() { + var rec = sm.getSelection()[0]; + if (!rec) { + return; } + + var win = Ext.create('PVE.CephPoolEdit', { + title: gettext('Edit') + ': Ceph Pool', + nodename: nodename, + pool_name: rec.data.pool_name, + }); + win.on('destroy', reload); + win.show(); + }; + + var edit_btn = new Proxmox.button.Button({ + text: gettext('Edit'), + disabled: true, + selModel: sm, + handler: run_editor, }); var destroy_btn = Ext.create('Proxmox.button.Button', { @@ -268,19 +402,18 @@ Ext.define('PVE.node.CephPoolList', { }, item: { type: 'CephPool', id: rec.data.pool_name } }).show(); - win.on('destroy', function() { - rstore.load(); - }); + win.on('destroy', reload); } }); Ext.apply(me, { store: store, selModel: sm, - tbar: [ create_btn, destroy_btn ], + tbar: [ create_btn, edit_btn, destroy_btn ], listeners: { activate: () => rstore.startUpdate(), destroy: () => rstore.stopUpdate(), + itemdblclick: run_editor, } }); @@ -302,7 +435,7 @@ Ext.define('PVE.node.CephPoolList', { { name: 'pg_autoscale_mode', type: 'string'}, { name: 'pg_num_final', type: 'integer'}, { name: 'target_size_ratio', type: 'number'}, - { name: 'target_size_bytes', type: 'integer'}, + { name: 'target_size', type: 'integer'}, ], idProperty: 'pool_name' }); -- 2.29.2