[pve-devel] [PATCH v6 manager 6/6] added basic ability to install ceph via gui

Dominik Csapak d.csapak at proxmox.com
Thu Feb 28 11:46:34 CET 2019


high level comments:

closing the wizard right after opening it, produces an error in the 
console (stopUpdate on undefined, or something similar)

after initializing i get the error: rados_connect failed - No such file 
or directory (500)

maybe a 'mon create' in the wizard could also be done? (optionally)

also when installing, the next button got activated early, but
i did not click on it, is there any better way to detect
when it is finished?

also when we already have ceph installed from the beginning, does it
make sense to show the install tab at all?

some more comments inline

On 2/27/19 3:01 PM, Tim Marx wrote:
> Signed-off-by: Tim Marx <t.marx at proxmox.com>
> ---
>   www/css/ext6-pve.css                   |   5 +
>   www/manager6/Makefile                  |   3 +
>   www/manager6/Utils.js                  |  31 ++++
>   www/manager6/ceph/CephInstallWizard.js | 259 +++++++++++++++++++++++++++++++++
>   www/manager6/ceph/Config.js            |  16 ++
>   www/manager6/ceph/Crush.js             |  16 ++
>   www/manager6/ceph/FS.js                |  42 +++++-
>   www/manager6/ceph/Log.js               |  70 +++++++++
>   www/manager6/ceph/Monitor.js           |  19 ++-
>   www/manager6/ceph/OSD.js               |  19 ++-
>   www/manager6/ceph/Pool.js              |  18 ++-
>   www/manager6/ceph/Status.js            |  21 ++-
>   www/manager6/node/Config.js            |   5 +-
>   www/manager6/window/CephInstall.js     |  66 +++++++++
>   14 files changed, 582 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 b9fa25b7..83ff8a3e 100644
> --- a/www/manager6/Utils.js
> +++ b/www/manager6/Utils.js
> @@ -1091,6 +1091,37 @@ Ext.define('PVE.Utils', { utilities: {
>   		return;
>   	    }
>   	}
> +    },
> +
> +    handleStoreErrorOrMask: function(me, store, regex, callback) {
> +
> +	if (!(store.proxy instanceof Proxmox.RestProxy)) {
> +	    throw "Proxy must implement afterload event!";
> +	}
> +
> +	me.mon(store.proxy, 'afterload', function (proxy, request, success) {
> +
> +	    if (success) {
> +		Proxmox.Utils.setErrorMask(me, false);
> +		return;
> +	    }
> +	    var msg;
> +	    /*jslint nomen: true */
> +	    var operation = request._operation;
> +	    var error = operation.getError();
> +
> +	    if (error.statusText) {
> +		if (error.statusText.match(regex)) {
> +		    callback(me, error);
> +		    return;
> +		} else {
> +		    msg = error.statusText + ' (' + error.status + ')';
> +		}
> +	    } else {
> +		msg = gettext('Connection error');
> +	    }
> +	    Proxmox.Utils.setErrorMask(me, msg);
> +	});
>       }

like discussed off-list, i think it would be better to use the
stores 'load' event, since we get the operation object directly
as parameter (saves us the access of private properties and the jslint 
override)

>   },
>   
> diff --git a/www/manager6/ceph/CephInstallWizard.js b/www/manager6/ceph/CephInstallWizard.js
> new file mode 100644
> index 00000000..a6789a88
> --- /dev/null
> +++ b/www/manager6/ceph/CephInstallWizard.js
> @@ -0,0 +1,259 @@
> +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: '<h3>Ceph?</h3>'+
> +		'<blockquote cite="https://ceph.com/"><p>"<b>Ceph</b> is a unified, distributed storage system designed for excellent performance, reliability and scalability."</p></blockquote>'+
> +		'<p><b>Ceph</b> is currently <b>not installed</b> on this node, click on the next button below to start the installation.'+
> +		' This wizard will guide you through the nessecery steps, after the inital installation you will be offered to create a inital configuration.'+
> +		' The configuration step is only needed once per cluster and will be skipped if a config is already present.</p>'+
> +		'<p>If you want to learn more visit <a href="http://docs.ceph.com/docs/master/">ceph.com</a> or click the help button below.</p>',
> +		listeners: {
> +		    activate: function() {
> +			// notify owning container that it should display a help button
> +			if (this.onlineHelp) {
> +			    Ext.GlobalEvents.fireEvent('proxmoxShowHelp', this.onlineHelp);
> +			}
> +		    },
> +		    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.store = Ext.create('Proxmox.data.UpdateStore', {

i would not use the 'store' property, since extjs may use that. e.g. 
destroy it on the destroy event (i think)
maybe 'updateStore' just to be safe

> +				storeid: 'ceph-status-' + nodename,
> +				interval: 1000,
> +				proxy: {
> +				    type: 'proxmox',
> +				    url: '/api2/json/nodes/' + nodename + '/ceph/status',
> +				    listeners: {
> +					exception: function(proxy, response, options){
> +						
> +					    if(response.statusText.match("not initialized", "i")){

nit: space after if and between ) and {

> +						me.store.stopUpdate();
> +						me.down('textfield').setValue('success');
> +					    } else if (!response.statusText.match("not installed", "i")) {

are you sure this code path is correct?
we have a failing api call but are neither in 'not initialized' nor 'not 
installed' state... what do we do then?

> +						me.store.stopUpdate();
> +						var wizard = me.up('#wizcontent');
> +						var tabs = wizard.items;
> +						var lastTab = tabs.items[tabs.length-1];
> +						lastTab.enable();
> +						wizard.setActiveTab(lastTab);
> +					    }
> +					}
> +				    }

any particular reason why the use of this listener?
wouldn't it be possible to use the load event with !success and the 
operation argument (the 4th that you did not specify)

> +				},
> +				listeners: {
> +				    load: function(rec, response, success){
> +					if(success){

nit: spaces around ()

> +					    me.store.stopUpdate();
> +					    var wizard = me.up('#wizcontent');
> +					    var tabs = wizard.items;
> +					    var lastTab = tabs.items[tabs.length-1];
> +					    lastTab.enable();
> +					    wizard.setActiveTab(lastTab);
> +					}
> +				    }
> +				}
> +			});
> +			me.store.startUpdate();
> +		    },
> +		    destroy: function(){
> +			var me = this;
> +			me.store.stopUpdate();
> +		    }

i guess we want the 'deactivate' event, not destroy? or both?
also check if the store exists, since it does not have to (exit on first 
panel for example)

> +		},
> +		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')
> +		    }

i would prefer if we use the same gettexts as with the pools
(while i like yours better, so maybe change the fieldlabels for pools? 
can ofc be done later on)

also it is not really clear that this are the defaults we are setting,
maybe 'Default number of replicas' ? etc.

> +		],
> +		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: 'chapter_pveceph',
> +		html: '<h3>Installation successfull!</h3>',
> +		listeners: {
> +		    activate: function() {
> +			// notify owning container that it should display a help button
> +			if (this.onlineHelp) {
> +			    Ext.GlobalEvents.fireEvent('proxmoxShowHelp', this.onlineHelp);
> +			}

maybe deactivate all old panels/backbutton here? else i can get back and 
submit again

> +		    },
> +		    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..447a9682 100644
> --- a/www/manager6/ceph/Config.js
> +++ b/www/manager6/ceph/Config.js
> @@ -14,6 +14,22 @@ Ext.define('PVE.node.CephConfig', {
>   	    waitMsgTarget: me,
>   	    failure: function(response, opts) {
>   		me.update(gettext('Error') + " " + response.htmlStatus);
> +		var msg = response.htmlStatus;
> +		var regex = new RegExp("(not installed|not initialized)", "i");

a better regex: 'not (installed|initialized)'

> +		if (msg.match(regex)) {
> +		    if (Proxmox.UserName === 'root at pam') {
> +			me.ownerCt.el.mask();
> +			if (!me.ownerCt.down('pveCephInstallWindow')){
> +			    var win = Ext.create('PVE.ceph.Install', {
> +				nodename: me.pveSelNode.data.node
> +			    });
> +			    me.ownerCt.add(win);
> +			    win.show();
> +			}
> +		    } else {
> +			me.ownerCt.mask(Ext.String.format(gettext('{0} not installed.') + gettext(' Log in as root to install.'), 'Ceph'), ['pve-static-mask']);
> +		    }
> +		}
>   	    },
>   	    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..7cdfdf6d 100644
> --- a/www/manager6/ceph/Crush.js
> +++ b/www/manager6/ceph/Crush.js
> @@ -15,6 +15,22 @@ Ext.define('PVE.node.CephCrushMap', {
>   	    waitMsgTarget: me,
>   	    failure: function(response, opts) {
>   		me.update(gettext('Error') + " " + response.htmlStatus);
> +		var msg = response.htmlStatus;
> +		var regex = new RegExp("(not installed|not initialized)", "i");
> +		if (msg.match(regex)) {
> +		    if (Proxmox.UserName === 'root at pam') {
> +			me.ownerCt.el.mask();
> +			if (!me.ownerCt.down('pveCephInstallWindow')){
> +			    var win = Ext.create('PVE.ceph.Install', {
> +				nodename: me.pveSelNode.data.node
> +			    });
> +			    me.ownerCt.add(win);
> +			    win.show();
> +			}
> +		    } else {
> +			me.ownerCt.mask(Ext.String.format(gettext('{0} not installed.') + gettext(' Log in as root to install.'), 'Ceph'), ['pve-static-mask']);
> +		    }
> +		}

this code looks very much like it could be factored out

>   	    },
>   	    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..bff1837b 100644
> --- a/www/manager6/ceph/FS.js
> +++ b/www/manager6/ceph/FS.js
> @@ -161,7 +161,26 @@ Ext.define('PVE.NodeCephFSPanel', {
>   			    order: 'DESC'
>   			}
>   		    }));
> -		    Proxmox.Utils.monStoreErrors(view, view.rstore);
> +		    var regex = new RegExp("(not installed|not initialized)", "i");
> +		    PVE.Utils.handleStoreErrorOrMask(view, view.rstore, regex, function(me, error){
> +			me.rstore.stopUpdate();
> +			if (Proxmox.UserName === 'root at pam') {
> +			    me.ownerCt.el.mask();
> +
> +			    if (!me.ownerCt.down('pveCephInstallWindow')){
> +				var win = Ext.create('PVE.ceph.Install', {
> +				    nodename: view.nodename
> +				});
> +				me.ownerCt.add(win);
> +				me.ownerCt.mon(win, 'cephInstallWindowClosed', function(){
> +				    me.rstore.startUpdate();
> +				});
> +				win.show();
> +			    }
> +			} else {
> +			    me.ownerCt.mask(Ext.String.format(gettext('{0} not installed.') + gettext(' Log in as root to install.'), 'Ceph'), ['pve-static-mask']);
> +			}
> +		    });
>   		    view.rstore.on('load', this.onLoad, this);
>   		    view.on('destroy', view.rstore.stopUpdate);
>   		},
> @@ -244,7 +263,26 @@ Ext.define('PVE.NodeCephFSPanel', {
>   			    order: 'DESC'
>   			}
>   		    }));
> -		    Proxmox.Utils.monStoreErrors(view, view.rstore);
> +		    var regex = new RegExp("(not installed|not initialized)", "i");
> +		    PVE.Utils.handleStoreErrorOrMask(view, view.rstore, regex, function(me, error){
> +			me.rstore.stopUpdate();
> +			if (Proxmox.UserName === 'root at pam') {
> +			    me.ownerCt.el.mask();
> +	
> +			    if (!me.ownerCt.down('pveCephInstallWindow')){
> +				var win = Ext.create('PVE.ceph.Install', {
> +				    nodename: view.nodename
> +				});
> +				me.ownerCt.add(win);
> +				me.ownerCt.mon(win, 'cephInstallWindowClosed', function(){
> +				    me.rstore.startUpdate();
> +				});
> +				win.show();
> +			    }
> +			} else {
> +			    me.ownerCt.mask(Ext.String.format(gettext('{0} not installed.') + gettext(' Log in as root to install.'), 'Ceph'), ['pve-static-mask']);
> +			}
> +		    });

same here, those two codepaths look very much alike

>   		    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..25178923
> --- /dev/null
> +++ b/www/manager6/ceph/Log.js
> @@ -0,0 +1,70 @@
> +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) + "<br>";
> +		});
> +
> +		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 regex = new RegExp("(not installed|not initialized)", "i");
> +		if (msg.match(regex)) {
> +		    if (Proxmox.UserName === 'root at pam') {
> +			me.el.mask();
> +
> +			if (!me.down('pveCephInstallWindow')){
> +			    var win = Ext.create('PVE.ceph.Install', {
> +				nodename: me.nodename
> +			    });
> +			    me.add(win);
> +			    win.show();
> +			}
> +
> +		    } else {
> +			me.mask(Ext.String.format(gettext('{0} not installed.') + gettext(' Log in as root to install.'), 'Ceph'), ['pve-static-mask']);
> +		    }
> +
> +		} else {
> +		    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..476ed8cc 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,24 @@ Ext.define('PVE.node.CephMonList', {
>   	    }
>   	});
>   
> +	var regex = new RegExp("(not installed|not initialized)", "i");
> +	PVE.Utils.handleStoreErrorOrMask(me, rstore, regex, function(me, error){
> +	    me.store.rstore.stopUpdate();
> +	    if (Proxmox.UserName === 'root at pam') {
> +		me.el.mask();
> +		var win = Ext.create('PVE.ceph.Install', {
> +		    nodename: nodename
> +		});
> +		me.add(win);
> +		me.mon(win, 'cephInstallWindowClosed', function(){
> +		    me.store.rstore.startUpdate();
> +		});
> +		win.show();
> +	    } else {
> +		me.el.mask(Ext.String.format(gettext('{0} not installed.') + gettext(' Log in as root to install.'), 'Ceph'), ['pve-static-mask']);
> +	    }
> +	});
> +
>   	me.callParent();
>       }
>   }, function() {
> diff --git a/www/manager6/ceph/OSD.js b/www/manager6/ceph/OSD.js
> index 144fab7e..f635cbd6 100644
> --- a/www/manager6/ceph/OSD.js
> +++ b/www/manager6/ceph/OSD.js
> @@ -281,7 +281,24 @@ Ext.define('PVE.node.CephOsdTree', {
>   		waitMsgTarget: me,
>   		method: 'GET',
>   		failure: function(response, opts) {
> -		    Proxmox.Utils.setErrorMask(me, response.htmlStatus);
> +		    var msg = response.htmlStatus;
> +		    var regex = new RegExp("(not installed|not initialized)", "i");
> +		    if (msg.match(regex)) {
> +			if (Proxmox.UserName === 'root at pam') {
> +			    me.el.mask();
> +			    if (!me.down('pveCephInstallWindow')){
> +				var win = Ext.create('PVE.ceph.Install', {
> +				    nodename: me.pveSelNode.data.node
> +				});
> +				me.add(win);
> +				win.show();
> +			    }
> +			} else {
> +			    me.mask(Ext.String.format(gettext('{0} not installed.') + gettext(' Log in as root to install.'), 'Ceph'), ['pve-static-mask']);
> +			}
> +		    } else {
> +			Proxmox.Utils.setErrorMask(me, msg);
> +		    }
>   		},
>   		success: function(response, opts) {
>   		    sm.deselectAll();
> diff --git a/www/manager6/ceph/Pool.js b/www/manager6/ceph/Pool.js
> index 27eba024..c7dd2248 100644
> --- a/www/manager6/ceph/Pool.js
> +++ b/www/manager6/ceph/Pool.js
> @@ -164,7 +164,23 @@ 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|not initialized)", "i");
> +	PVE.Utils.handleStoreErrorOrMask(me, rstore, regex, function(me, error){
> +	    me.store.rstore.stopUpdate();
> +	    if (Proxmox.UserName === 'root at pam') {
> +		me.el.mask();
> +		var win = Ext.create('PVE.ceph.Install', {
> +		    nodename: nodename
> +		});
> +		me.add(win);
> +		me.mon(win, 'cephInstallWindowClosed', function(){
> +		    me.store.rstore.startUpdate();
> +		});
> +		win.show();
> +	    } else {
> +		me.el.mask(Ext.String.format(gettext('{0} not installed.') + gettext(' Log in as root to install.'), 'Ceph'), ['pve-static-mask']);
> +	    }
> +	});
>   
>   	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..ea4e27e7 100644
> --- a/www/manager6/ceph/Status.js
> +++ b/www/manager6/ceph/Status.js
> @@ -308,7 +308,26 @@ 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|not initialized)", "i");
> +	PVE.Utils.handleStoreErrorOrMask(me, me.store, regex, function(me, error){
> +	    me.store.stopUpdate();
> +	    if (Proxmox.UserName === 'root at pam') {
> +		me.el.mask();
> +		if (!me.down('PVE.ceph.Install')){
> +		    var win = Ext.create('PVE.ceph.Install', {
> +			nodename: nodename
> +		    });
> +		    me.add(win);
> +		    me.mon(win, 'cephInstallWindowClosed', function(){
> +			me.store.startUpdate();
> +		    });
> +		    win.show();
> +		}
> +	    } else {
> +		me.el.mask(Ext.String.format(gettext('{0} not installed.') + gettext(' Log in as root to install.'), 'Ceph'), ['pve-static-mask']);
> +	    }
> +	});

most of these look very much alike and should be refactored imo

> +
>   	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..20ecdd16
> --- /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',

i think for the padding issue, a
padding: 5
or
bodyPadding: 5
here could help

> +    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: '<p class="install-mask">' + Ext.String.format(gettext('{0} is not installed on this node.'), 'Ceph') + '<br>' +
> +	    gettext('Would you like to install it now?') + '</p>',
> +	    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();
> +		});
> +
> +	    }
> +	}
> +    ]
> +});
> 





More information about the pve-devel mailing list