]> git.proxmox.com Git - pve-manager.git/commitdiff
added basic ability to install ceph via gui
authorTim Marx <t.marx@proxmox.com>
Wed, 6 Mar 2019 11:29:40 +0000 (12:29 +0100)
committerThomas Lamprecht <t.lamprecht@proxmox.com>
Fri, 8 Mar 2019 11:33:31 +0000 (12:33 +0100)
Signed-off-by: Tim Marx <t.marx@proxmox.com>
Reviewed-by: Dominik Csapak <d.csapak@proxmox.com>
14 files changed:
www/css/ext6-pve.css
www/manager6/Makefile
www/manager6/Utils.js
www/manager6/ceph/CephInstallWizard.js [new file with mode: 0644]
www/manager6/ceph/Config.js
www/manager6/ceph/Crush.js
www/manager6/ceph/FS.js
www/manager6/ceph/Log.js [new file with mode: 0644]
www/manager6/ceph/Monitor.js
www/manager6/ceph/OSD.js
www/manager6/ceph/Pool.js
www/manager6/ceph/Status.js
www/manager6/node/Config.js
www/manager6/window/CephInstall.js [new file with mode: 0644]

index 174511ac0665d4346ede5de375a971372a1c28ec..7ac35603a68ca66b998e06fa12fdb7adaf2bbc2c 100644 (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;
+}
\ No newline at end of file
index e75f0de6763286722f004eba7c4577b0ac24cad4..db5ced2dbfda8da394bf0c8a592e6c04b8e1843b 100644 (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                                 \
index 326a2527f7f0616f1ab55fbe23e814e60d246040..b5897b979bb4b5535a462f7260af45a1c6c38482 100644 (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;
+       }
     }
 },
 
diff --git a/www/manager6/ceph/CephInstallWizard.js b/www/manager6/ceph/CephInstallWizard.js
new file mode 100644 (file)
index 0000000..a184f50
--- /dev/null
@@ -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
index 0412468470e79efb894c4ce4be95049c99080517..4636469011307da12428293a488c840b827e8653 100644 (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;
index ebd46c835d6fedc5e9ef6e35212426721cee8aee..e9a7fb4a246c24f9f5ec4e5d267ffcd36a9efada 100644 (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;
index a1c34d7358416f7b9d9f8b1a36acf079f78e69eb..5947baadc5dc3a530d19fd496934857831a1c6e1 100644 (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);
                },
diff --git a/www/manager6/ceph/Log.js b/www/manager6/ceph/Log.js
new file mode 100644 (file)
index 0000000..2cfb845
--- /dev/null
@@ -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
index a3a18a83ced2128cb6720114daab5bbde6bbff47..638fa9f10ff0f27934976de768501118bfdb0137 100644 (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() {
index 144fab7e55837f130c708c077af53f16acba18b7..cb3d5f0d17102932ed394b36b52163053c23dbfb 100644 (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();
index 27eba024e15d689dbdd69f83035a469a4abc1c6f..522445f8695f18c616d1f5ce6b8955fefd6556b1 100644 (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'),
index 78fa1cf83db10ccdc02b1d288058d9d34e66f50b..ffb407d729b1811f192ed490912551aca741816e 100644 (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();
index f9a626705fcfe2c9343c26033ee5f2b3ff260be7..831d2e023bae5e930745eaa858e4997a13833133 100644 (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
                });
        }
 
diff --git a/www/manager6/window/CephInstall.js b/www/manager6/window/CephInstall.js
new file mode 100644 (file)
index 0000000..cd38dd0
--- /dev/null
@@ -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();
+               });
+
+           }
+       }
+    ]
+});