added basic ability to install ceph via gui

Signed-off-by: Tim Marx <t.marx@proxmox.com>
Reviewed-by: Dominik Csapak <d.csapak@proxmox.com>
This commit is contained in:
Tim Marx 2019-03-06 12:29:40 +01:00 committed by Thomas Lamprecht
parent b945c7c1be
commit 4616a55b37
14 changed files with 532 additions and 8 deletions

View File

@ -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;
}

View File

@ -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 \

View File

@ -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;
}
}
},

View File

@ -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();
}
}
]
});

View File

@ -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;

View File

@ -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;

View File

@ -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);
},

60
www/manager6/ceph/Log.js Normal file
View File

@ -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);
}
}
});
}
});

View File

@ -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() {

View File

@ -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();

View File

@ -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'),

View File

@ -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();

View File

@ -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
});
}

View File

@ -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();
});
}
}
]
});