From 4616a55b373fc87e2ad889640502ad2b114ba811 Mon Sep 17 00:00:00 2001 From: Tim Marx Date: Wed, 6 Mar 2019 12:29:40 +0100 Subject: [PATCH] added basic ability to install ceph via gui Signed-off-by: Tim Marx Reviewed-by: Dominik Csapak --- www/css/ext6-pve.css | 5 + www/manager6/Makefile | 3 + www/manager6/Utils.js | 46 +++++ www/manager6/ceph/CephInstallWizard.js | 267 +++++++++++++++++++++++++ www/manager6/ceph/Config.js | 9 + www/manager6/ceph/Crush.js | 8 + www/manager6/ceph/FS.js | 24 ++- www/manager6/ceph/Log.js | 60 ++++++ www/manager6/ceph/Monitor.js | 13 +- www/manager6/ceph/OSD.js | 9 +- www/manager6/ceph/Pool.js | 12 +- www/manager6/ceph/Status.js | 13 +- www/manager6/node/Config.js | 5 +- www/manager6/window/CephInstall.js | 66 ++++++ 14 files changed, 532 insertions(+), 8 deletions(-) create mode 100644 www/manager6/ceph/CephInstallWizard.js create mode 100644 www/manager6/ceph/Log.js create mode 100644 www/manager6/window/CephInstall.js diff --git a/www/css/ext6-pve.css b/www/css/ext6-pve.css index 174511ac..7ac35603 100644 --- a/www/css/ext6-pve.css +++ b/www/css/ext6-pve.css @@ -582,3 +582,8 @@ table.osds td:first-of-type { right: 0px; background-color: #555; } + +.install-mask { + background-color: rgb(245, 245, 245); + color: #000; +} \ No newline at end of file diff --git a/www/manager6/Makefile b/www/manager6/Makefile index e75f0de6..db5ced2d 100644 --- a/www/manager6/Makefile +++ b/www/manager6/Makefile @@ -83,6 +83,7 @@ JSSRC= \ window/BackupConfig.js \ window/Settings.js \ window/StartupEdit.js \ + window/CephInstall.js \ panel/NotesView.js \ grid/ResourceGrid.js \ grid/PoolMembers.js \ @@ -101,6 +102,8 @@ JSSRC= \ ceph/Status.js \ ceph/StatusDetail.js \ ceph/Config.js \ + ceph/Log.js \ + ceph/CephInstallWizard.js \ node/Disks.js \ node/LVM.js \ node/LVMThin.js \ diff --git a/www/manager6/Utils.js b/www/manager6/Utils.js index 326a2527..b5897b97 100644 --- a/www/manager6/Utils.js +++ b/www/manager6/Utils.js @@ -1102,6 +1102,52 @@ Ext.define('PVE.Utils', { utilities: { } } } + }, + + handleStoreErrorOrMask: function(me, store, regex, callback) { + + me.mon(store, 'load', function (proxy, response, success, operation) { + + if (success) { + Proxmox.Utils.setErrorMask(me, false); + return; + } + var msg; + + if (operation.error.statusText) { + if (operation.error.statusText.match(regex)) { + callback(me, operation.error); + return; + } else { + msg = operation.error.statusText + ' (' + operation.error.status + ')'; + } + } else { + msg = gettext('Connection error'); + } + Proxmox.Utils.setErrorMask(me, msg); + }); + }, + + showCephInstallOrMask: function(container, msg, nodename, callback){ + var regex = new RegExp("not (installed|initialized)", "i"); + if (msg.match(regex)) { + if (Proxmox.UserName === 'root@pam') { + container.el.mask(); + if (!container.down('pveCephInstallWindow')){ + var win = Ext.create('PVE.ceph.Install', { + nodename: nodename + }); + container.add(win); + win.show(); + callback(win); + } + } else { + container.mask(Ext.String.format(gettext('{0} not installed.') + gettext(' Log in as root to install.'), 'Ceph'), ['pve-static-mask']); + } + return true; + } else { + return false; + } } }, diff --git a/www/manager6/ceph/CephInstallWizard.js b/www/manager6/ceph/CephInstallWizard.js new file mode 100644 index 00000000..a184f50b --- /dev/null +++ b/www/manager6/ceph/CephInstallWizard.js @@ -0,0 +1,267 @@ +Ext.define('PVE.ceph.CephInstallWizard', { + extend: 'PVE.window.Wizard', + alias: 'widget.pveCephInstallWizard', + mixins: ['Proxmox.Mixin.CBind'], + resizable: false, + nodename: undefined, + viewModel: { + data: { + nodename: '' + } + }, + cbindData: { + nodename: undefined + }, + title: gettext('Installation'), + items: [ + { + title: gettext('Info'), + xtype: 'panel', + border: false, + bodyBorder: false, + onlineHelp: 'chapter_pveceph', + html: '

Ceph?

'+ + '

"Ceph is a unified, distributed storage system designed for excellent performance, reliability and scalability."

'+ + '

Ceph is currently not installed on this node, click on the next button below to start the installation.'+ + ' This wizard will guide you through the necessary steps, after the initial installation you will be offered to create a initial configuration.'+ + ' The configuration step is only needed once per cluster and will be skipped if a config is already present.

'+ + '

Please take a look at our documentation, by clicking the help button below, before starting the installation, if you want to gain deeper knowledge about Ceph visit ceph.com.

', + listeners: { + activate: function() { + // notify owning container that it should display a help button + if (this.onlineHelp) { + Ext.GlobalEvents.fireEvent('proxmoxShowHelp', this.onlineHelp); + } + this.up('pveCephInstallWizard').down('#back').hide(true); + }, + deactivate: function() { + if (this.onlineHelp) { + Ext.GlobalEvents.fireEvent('proxmoxHideHelp', this.onlineHelp); + } + } + } + }, + { + title: gettext('Installation'), + xtype: 'panel', + layout: 'fit', + cbind:{ + nodename: '{nodename}' + }, + listeners: { + afterrender: function() { + var me = this; + me.down('pveNoVncConsole').fireEvent('activate'); + }, + activate: function() { + var me = this; + var nodename = me.nodename; + me.updateStore = Ext.create('Proxmox.data.UpdateStore', { + storeid: 'ceph-status-' + nodename, + interval: 1000, + proxy: { + type: 'proxmox', + url: '/api2/json/nodes/' + nodename + '/ceph/status' + }, + listeners: { + load: function(rec, response, success, operation) { + var wizard = me.up('#wizcontent'); + var tabs = wizard.items; + var lastTab = tabs.items[tabs.length-1]; + if (success) { + me.updateStore.stopUpdate(); + lastTab.enable(); + wizard.setActiveTab(lastTab); + } else if (operation.error.statusText.match("not initialized", "i")) { + me.updateStore.stopUpdate(); + me.down('textfield').setValue('success'); + } else if (operation.error.statusText.match("rados_connect failed", "i")) { + me.updateStore.stopUpdate(); + lastTab.enable(); + wizard.setActiveTab(lastTab); + } else if (!operation.error.statusText.match("not installed", "i")) { + Proxmox.Utils.setErrorMask(me, operation.error.statusText); + } + } + } + }); + me.updateStore.startUpdate(); + }, + destroy: function() { + var me = this; + if (me.updateStore) { + me.updateStore.stopUpdate(); + } + } + }, + items: [ + { + itemId: 'jsconsole', + consoleType: 'cmd', + xtermjs: true, + xtype: 'pveNoVncConsole', + cbind:{ + nodename: '{nodename}' + }, + cmd: 'ceph_install' + }, + { + xtype: 'textfield', + name: 'installSuccess', + value: '', + allowBlank: false, + submitValue: false, + hidden: true + } + ] + }, + { + xtype: 'inputpanel', + title: gettext('Configuration'), + onlineHelp: 'chapter_pveceph', + cbind: { + nodename: '{nodename}' + }, + listeners: { + activate: function() { + this.up('pveCephInstallWizard').down('#submit').setText(gettext('Next')); + }, + deactivate: function() { + this.up('pveCephInstallWizard').down('#submit').setText(gettext('Finish')); + } + }, + column1: [ + { + xtype: 'displayfield', + name: 'nodename', + fieldLabel: gettext('Node'), + cbind: { + value: '{nodename}' + }, + padding: 5 + }, + { + xtype: 'textfield', + name: 'network', + vtype: 'IPCIDRAddress', + value: '', + fieldLabel: 'Network IPv4/CIDR', + allowBlank: false + }, + { + xtype: 'textfield', + name: 'cluster-network', + vtype: 'IPCIDRAddress', + fieldLabel: 'Cluster-Network IPv4/CIDR', + allowBlank: true, + emptyText: gettext('default') + } + ], + advancedColumn1: [ + { + xtype: 'numberfield', + name: 'size', + fieldLabel: gettext('Number of replicas'), + value: '', + maxValue: 7, + minValue: 1, + allowBlank: true, + emptyText: gettext('default') + }, + { + xtype: 'numberfield', + name: 'min_size', + fieldLabel: gettext('Minimum replicas'), + value: '', + maxValue: 7, + minValue: 1, + allowBlank: true, + emptyText: gettext('default') + }, + { + xtype: 'numberfield', + name: 'pg_bits', + fieldLabel: 'Placement group bits', + value: '', + maxValue: 14, + minValue: 6, + allowBlank: true, + emptyText: gettext('default') + } + ], + onGetValues: function(values) { + ['cluster-network', 'size', 'min_size', 'pg_bits'].forEach(function(field) { + if (!values[field]) { + delete values[field]; + } + }); + return values; + }, + onSubmit: function() { + var me = this; + var wizard = me.up('window'); + var kv = wizard.getValues(); + delete kv['delete']; + var nodename = me.nodename; + delete kv.nodename; + Proxmox.Utils.API2Request({ + url: '/nodes/'+nodename+'/ceph/init', + waitMsgTarget: wizard, + method: 'POST', + params: kv, + success: function() { + var tp = me.up('#wizcontent'); + var atab = tp.getActiveTab(); + + var next = tp.items.indexOf(atab) + 1; + var ntab = tp.items.getAt(next); + if (ntab) { + ntab.enable(); + tp.setActiveTab(ntab); + } + }, + failure: function(response, opts) { + Ext.Msg.alert(gettext('Error'), response.htmlStatus); + } + }); + } + }, + { + title: gettext('Success'), + xtype: 'panel', + border: false, + bodyBorder: false, + onlineHelp: 'pve_ceph_install', + html: '

Installation successful!

'+ + '

The basic installation and configuration is completed, depending on your setup some of the following steps are required to start using Ceph:

'+ + '
  • Creating Ceph Monitors
  • Creating Ceph OSDs
  • Creating Ceph Pools
'+ + '

To learn more click on the help button below.

', + listeners: { + activate: function() { + // notify owning container that it should display a help button + if (this.onlineHelp) { + Ext.GlobalEvents.fireEvent('proxmoxShowHelp', this.onlineHelp); + } + + var tp = this.up('#wizcontent'); + var idx = tp.items.indexOf(this)-1; + for(;idx >= 0;idx--) { + var nc = tp.items.getAt(idx); + if (nc) { + nc.disable(); + } + } + }, + deactivate: function() { + if (this.onlineHelp) { + Ext.GlobalEvents.fireEvent('proxmoxHideHelp', this.onlineHelp); + } + } + }, + onSubmit: function() { + var wizard = this.up('pveCephInstallWizard'); + wizard.close(); + } + } + ] + }); \ No newline at end of file diff --git a/www/manager6/ceph/Config.js b/www/manager6/ceph/Config.js index 04124684..46364690 100644 --- a/www/manager6/ceph/Config.js +++ b/www/manager6/ceph/Config.js @@ -14,6 +14,15 @@ Ext.define('PVE.node.CephConfig', { waitMsgTarget: me, failure: function(response, opts) { me.update(gettext('Error') + " " + response.htmlStatus); + var msg = response.htmlStatus; + PVE.Utils.showCephInstallOrMask(me.ownerCt, msg, me.pveSelNode.data.node, + function(win){ + me.mon(win, 'cephInstallWindowClosed', function(){ + me.load(); + }); + } + ); + }, success: function(response, opts) { var data = response.result.data; diff --git a/www/manager6/ceph/Crush.js b/www/manager6/ceph/Crush.js index ebd46c83..e9a7fb4a 100644 --- a/www/manager6/ceph/Crush.js +++ b/www/manager6/ceph/Crush.js @@ -15,6 +15,14 @@ Ext.define('PVE.node.CephCrushMap', { waitMsgTarget: me, failure: function(response, opts) { me.update(gettext('Error') + " " + response.htmlStatus); + var msg = response.htmlStatus; + PVE.Utils.showCephInstallOrMask(me.ownerCt, msg, me.pveSelNode.data.node, + function(win){ + me.mon(win, 'cephInstallWindowClosed', function(){ + me.load(); + }); + } + ); }, success: function(response, opts) { var data = response.result.data; diff --git a/www/manager6/ceph/FS.js b/www/manager6/ceph/FS.js index a1c34d73..5947baad 100644 --- a/www/manager6/ceph/FS.js +++ b/www/manager6/ceph/FS.js @@ -161,7 +161,17 @@ Ext.define('PVE.NodeCephFSPanel', { order: 'DESC' } })); - Proxmox.Utils.monStoreErrors(view, view.rstore); + var regex = new RegExp("not (installed|initialized)", "i"); + PVE.Utils.handleStoreErrorOrMask(view, view.rstore, regex, function(me, error){ + me.rstore.stopUpdate(); + PVE.Utils.showCephInstallOrMask(me.ownerCt, error.statusText, view.nodename, + function(win){ + me.mon(win, 'cephInstallWindowClosed', function(){ + me.rstore.startUpdate(); + }); + } + ); + }); view.rstore.on('load', this.onLoad, this); view.on('destroy', view.rstore.stopUpdate); }, @@ -244,7 +254,17 @@ Ext.define('PVE.NodeCephFSPanel', { order: 'DESC' } })); - Proxmox.Utils.monStoreErrors(view, view.rstore); + var regex = new RegExp("not (installed|initialized)", "i"); + PVE.Utils.handleStoreErrorOrMask(view, view.rstore, regex, function(me, error){ + me.rstore.stopUpdate(); + PVE.Utils.showCephInstallOrMask(me.ownerCt, error.statusText, view.nodename, + function(win){ + me.mon(win, 'cephInstallWindowClosed', function(){ + me.rstore.startUpdate(); + }); + } + ); + }); view.rstore.on('load', this.onLoad, this); view.on('destroy', view.rstore.stopUpdate); }, diff --git a/www/manager6/ceph/Log.js b/www/manager6/ceph/Log.js new file mode 100644 index 00000000..2cfb8452 --- /dev/null +++ b/www/manager6/ceph/Log.js @@ -0,0 +1,60 @@ +Ext.define('PVE.ceph.Log', { + extend: 'Proxmox.panel.LogView', + xtype: 'cephLogView', + nodename: undefined, + doAttemptLoad: function(start) { + var me = this; + + var req_params = { + start: start, + limit: me.pageSize + }; + + if (me.log_select_timespan) { + // always show log until the end of the selected day + req_params.until = Ext.Date.format(me.until_date, 'Y-m-d') + ' 23:59:59'; + req_params.since = Ext.Date.format(me.since_date, 'Y-m-d'); + } + + Proxmox.Utils.API2Request({ + url: me.url, + params: req_params, + method: 'GET', + success: function(response) { + Proxmox.Utils.setErrorMask(me, false); + var list = response.result.data; + var total = response.result.total; + var first = 0, last = 0; + var text = ''; + Ext.Array.each(list, function(item) { + if (!first|| item.n < first) { + first = item.n; + } + if (!last || item.n > last) { + last = item.n; + } + text = text + Ext.htmlEncode(item.t) + "
"; + }); + + if (first && last && total) { + me.updateView(first -1 , last -1, total, text); + } else { + me.updateView(0, 0, 0, ''); + } + }, + failure: function(response) { + var msg = response.htmlStatus; + var windowShow = PVE.Utils.showCephInstallOrMask(me, msg, me.nodename, + function(win){ + me.mon(win, 'cephInstallWindowClosed', function(){ + me.doAttemptLoad(0); + }); + } + ); + if (!windowShow) { + Proxmox.Utils.setErrorMask(me, msg); + } + } + }); + } +}); \ No newline at end of file diff --git a/www/manager6/ceph/Monitor.js b/www/manager6/ceph/Monitor.js index a3a18a83..638fa9f1 100644 --- a/www/manager6/ceph/Monitor.js +++ b/www/manager6/ceph/Monitor.js @@ -82,7 +82,6 @@ Ext.define('PVE.node.CephMonList', { sorters: [{ property: 'name'}] }); - Proxmox.Utils.monStoreErrors(me, rstore); var service_cmd = function(cmd) { var rec = sm.getSelection()[0]; @@ -211,6 +210,18 @@ Ext.define('PVE.node.CephMonList', { } }); + var regex = new RegExp("not (installed|initialized)", "i"); + PVE.Utils.handleStoreErrorOrMask(me, rstore, regex, function(me, error){ + me.store.rstore.stopUpdate(); + PVE.Utils.showCephInstallOrMask(me, error.statusText, nodename, + function(win){ + me.mon(win, 'cephInstallWindowClosed', function(){ + me.store.rstore.startUpdate(); + }); + } + ); + }); + me.callParent(); } }, function() { diff --git a/www/manager6/ceph/OSD.js b/www/manager6/ceph/OSD.js index 144fab7e..cb3d5f0d 100644 --- a/www/manager6/ceph/OSD.js +++ b/www/manager6/ceph/OSD.js @@ -281,7 +281,14 @@ Ext.define('PVE.node.CephOsdTree', { waitMsgTarget: me, method: 'GET', failure: function(response, opts) { - Proxmox.Utils.setErrorMask(me, response.htmlStatus); + var msg = response.htmlStatus; + PVE.Utils.showCephInstallOrMask(me, msg, me.pveSelNode.data.node, + function(win){ + me.mon(win, 'cephInstallWindowClosed', function(){ + reload(); + }); + } + ); }, success: function(response, opts) { sm.deselectAll(); diff --git a/www/manager6/ceph/Pool.js b/www/manager6/ceph/Pool.js index 27eba024..522445f8 100644 --- a/www/manager6/ceph/Pool.js +++ b/www/manager6/ceph/Pool.js @@ -164,7 +164,17 @@ Ext.define('PVE.node.CephPoolList', { var store = Ext.create('Proxmox.data.DiffStore', { rstore: rstore }); - Proxmox.Utils.monStoreErrors(me, rstore); + var regex = new RegExp("not (installed|initialized)", "i"); + PVE.Utils.handleStoreErrorOrMask(me, rstore, regex, function(me, error){ + me.store.rstore.stopUpdate(); + PVE.Utils.showCephInstallOrMask(me, error.statusText, nodename, + function(win){ + me.mon(win, 'cephInstallWindowClosed', function(){ + me.store.rstore.startUpdate(); + }); + } + ); + }); var create_btn = new Ext.Button({ text: gettext('Create'), diff --git a/www/manager6/ceph/Status.js b/www/manager6/ceph/Status.js index 78fa1cf8..ffb407d7 100644 --- a/www/manager6/ceph/Status.js +++ b/www/manager6/ceph/Status.js @@ -308,7 +308,18 @@ Ext.define('PVE.node.CephStatus', { me.version = me.sp.get('ceph-version'); me.change_version(me.version); - Proxmox.Utils.monStoreErrors(me,me.store); + var regex = new RegExp("not (installed|initialized)", "i"); + PVE.Utils.handleStoreErrorOrMask(me, me.store, regex, function(me, error){ + me.store.stopUpdate(); + PVE.Utils.showCephInstallOrMask(me, error.statusText, nodename, + function(win){ + me.mon(win, 'cephInstallWindowClosed', function(){ + me.store.startUpdate(); + }); + } + ); + }); + me.mon(me.store, 'load', me.updateAll, me); me.on('destroy', me.store.stopUpdate); me.store.startUpdate(); diff --git a/www/manager6/node/Config.js b/www/manager6/node/Config.js index f9a62670..831d2e02 100644 --- a/www/manager6/node/Config.js +++ b/www/manager6/node/Config.js @@ -375,8 +375,9 @@ Ext.define('PVE.node.Config', { iconCls: 'fa fa-list', groups: ['ceph'], onlineHelp: 'chapter_pveceph', - xtype: 'proxmoxLogView', - url: "/api2/extjs/nodes/" + nodename + "/ceph/log" + xtype: 'cephLogView', + url: "/api2/extjs/nodes/" + nodename + "/ceph/log", + nodename: nodename }); } diff --git a/www/manager6/window/CephInstall.js b/www/manager6/window/CephInstall.js new file mode 100644 index 00000000..cd38dd0b --- /dev/null +++ b/www/manager6/window/CephInstall.js @@ -0,0 +1,66 @@ +/*jslint confusion: true*/ +Ext.define('PVE.ceph.Install', { + extend: 'Ext.window.Window', + xtype: 'pveCephInstallWindow', + mixins: ['Proxmox.Mixin.CBind'], + + width: 220, + header: false, + resizable: false, + draggable: false, + modal: true, + nodename: undefined, + shadow: false, + border: false, + bodyBorder: false, + closable: false, + cls: 'install-mask', + bodyCls: 'install-mask', + layout: { + align: 'stretch', + pack: 'center', + type: 'vbox' + }, + viewModel: { + parent: null, + data: { + cephVersion: 'luminous' + }, + formulas: { + buttonText: function (get){ + return gettext('Install Ceph-') + get('cephVersion'); + } + } + }, + items: [ + { + html: '

' + Ext.String.format(gettext('{0} is not installed on this node.'), 'Ceph') + '
' + + gettext('Would you like to install it now?') + '

', + border: false, + padding: 5, + bodyCls: 'install-mask' + + }, + { + xtype: 'button', + bind: { + text: '{buttonText}' + }, + cbind: { + nodename: '{nodename}' + }, + handler: function() { + var me = this.up('pveCephInstallWindow'); + var win = Ext.create('PVE.ceph.CephInstallWizard',{ + nodename: me.nodename + }); + win.show(); + me.mon(win,'beforeClose', function(){ + me.fireEvent("cephInstallWindowClosed"); + me.close(); + }); + + } + } + ] +}); -- 2.39.2