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

Tim Marx t.marx at proxmox.com
Wed Mar 6 12:29:40 CET 2019


Signed-off-by: Tim Marx <t.marx at proxmox.com>
---

changes v7:
* no back button
* display error if error isn't expected while install
* fix store error on close
* fix code style errors
* fix typos
* use load event
* changed store to updateStore
* moved reusable code to utils
* changed regex

 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 at 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: '<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 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.</p>'+
+		'<p>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 <a href="http://docs.ceph.com/docs/master/">ceph.com</a>.</p>',
+		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: '<h3>Installation successful!</h3>'+
+		'<p>The basic installation and configuration is completed, depending on your setup some of the following steps are required to start using Ceph:</p>'+
+		'<ul><li>Creating Ceph Monitors</li><li>Creating Ceph OSDs</li><li>Creating Ceph Pools</li></ul>'+
+		'<p>To learn more click on 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);
+			}
+
+			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) + "<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 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: '<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();
+		});
+
+	    }
+	}
+    ]
+});
-- 
2.11.0




More information about the pve-devel mailing list