diff --git a/www/manager6/Makefile b/www/manager6/Makefile index 8c5a2275d..e8785f190 100644 --- a/www/manager6/Makefile +++ b/www/manager6/Makefile @@ -96,6 +96,7 @@ JSSRC= \ panel/IPSet.js \ panel/ConfigPanel.js \ grid/BackupView.js \ + ceph/ServiceList.js \ ceph/FS.js \ ceph/Pool.js \ ceph/OSD.js \ diff --git a/www/manager6/ceph/Monitor.js b/www/manager6/ceph/Monitor.js index 638fa9f10..eb35e91ca 100644 --- a/www/manager6/ceph/Monitor.js +++ b/www/manager6/ceph/Monitor.js @@ -1,234 +1,46 @@ -Ext.define('PVE.CephCreateMon', { - extend: 'Proxmox.window.Edit', - alias: ['widget.pveCephCreateMon'], +Ext.define('PVE.node.CephMonMgrList', { + extend: 'Ext.container.Container', + xtype: 'pveNodeCephMonMgr', - subject: 'Ceph Monitor/Manager', - onlineHelp: 'pve_ceph_monitors', - - showProgress: true, - - setNode: function(nodename) { - var me = this; - - me.nodename = nodename; - me.url = "/nodes/" + nodename + "/ceph/mon"; - }, - - initComponent : function() { - - var me = this; - - if (!me.nodename) { - throw "no node name specified"; - } - - me.setNode(me.nodename); - - me.isCreate = true; - - Ext.applyIf(me, { - method: 'POST', - items: [ - { - xtype: 'pveNodeSelector', - submitValue: false, - fieldLabel: gettext('Host'), - selectCurNode: true, - allowBlank: false, - listeners: { - change: function(f, value) { - me.setNode(value); - } - } - } - ] - }); - - me.callParent(); - } -}); - -Ext.define('PVE.node.CephMonList', { - extend: 'Ext.grid.GridPanel', - alias: ['widget.pveNodeCephMonList'], + mixins: ['Proxmox.Mixin.CBind' ], onlineHelp: 'chapter_pveceph', - stateful: true, - stateId: 'grid-ceph-monitor', + defaults: { + border: false, + onlineHelp: 'chapter_pveceph', + flex: 1 + }, - initComponent: function() { - var me = this; + layout: { + type: 'vbox', + align: 'stretch' + }, - var nodename = me.pveSelNode.data.node; - if (!nodename) { - throw "no node name specified"; - } - - var sm = Ext.create('Ext.selection.RowModel', {}); - - var rstore = Ext.create('Proxmox.data.UpdateStore', { - interval: 3000, - storeid: 'ceph-mon-list' + nodename, - model: 'ceph-mon-list', - proxy: { - type: 'proxmox', - url: "/api2/json/nodes/" + nodename + "/ceph/mon" - } - }); - - var store = Ext.create('Proxmox.data.DiffStore', { - rstore: rstore, - sorters: [{ property: 'name'}] - }); - - - var service_cmd = function(cmd) { - var rec = sm.getSelection()[0]; - if (!rec.data.host) { - Ext.Msg.alert(gettext('Error'), "entry has no host"); - return; - } - Proxmox.Utils.API2Request({ - url: "/nodes/" + rec.data.host + "/ceph/" + cmd, - method: 'POST', - params: { service: "mon." + rec.data.name }, - success: function(response, options) { - var upid = response.result.data; - var win = Ext.create('Proxmox.window.TaskProgress', { upid: upid }); - win.show(); - }, - failure: function(response, opts) { - Ext.Msg.alert(gettext('Error'), response.htmlStatus); - } - }); - }; - - var start_btn = new Proxmox.button.Button({ - text: gettext('Start'), - selModel: sm, - disabled: true, - handler: function(){ - service_cmd("start"); - } - }); - - var stop_btn = new Proxmox.button.Button({ - text: gettext('Stop'), - selModel: sm, - disabled: true, - handler: function(){ - service_cmd("stop"); - } - }); - - var restart_btn = new Proxmox.button.Button({ - text: gettext('Restart'), - selModel: sm, - disabled: true, - handler: function(){ - service_cmd("restart"); - } - }); - - var create_btn = new Ext.Button({ - text: gettext('Create'), - handler: function(){ - var win = Ext.create('PVE.CephCreateMon', { - nodename: nodename - }); - win.show(); - } - }); - - var remove_btn = new Proxmox.button.Button({ - text: gettext('Remove'), - selModel: sm, - disabled: true, - handler: function() { - var rec = sm.getSelection()[0]; - - if (!rec.data.host) { - Ext.Msg.alert(gettext('Error'), "entry has no host"); - return; - } - - Proxmox.Utils.API2Request({ - url: "/nodes/" + rec.data.host + "/ceph/mon/" + - rec.data.name, - method: 'DELETE', - success: function(response, options) { - var upid = response.result.data; - var win = Ext.create('Proxmox.window.TaskProgress', { upid: upid }); - win.show(); - }, - failure: function(response, opts) { - Ext.Msg.alert(gettext('Error'), response.htmlStatus); - } - }); - } - }); - - Ext.apply(me, { - store: store, - selModel: sm, - tbar: [ start_btn, stop_btn, restart_btn, '-', create_btn, remove_btn ], - columns: [ - { - header: gettext('Name'), - width: 100, - sortable: true, - renderer: function(v) { return "mon." + v; }, - dataIndex: 'name' - }, - { - header: gettext('Host'), - width: 100, - sortable: true, - renderer: function(v) { - return v || 'unknown'; - }, - dataIndex: 'host' - }, + items: [ + { + xtype: 'pveNodeCephServiceList', + cbind: { pveSelNode: '{pveSelNode}' }, + type: 'mon', + additionalColumns: [ { header: gettext('Quorum'), width: 70, - sortable: false, + sortable: true, renderer: Proxmox.Utils.format_boolean, dataIndex: 'quorum' - }, - { - header: gettext('Address'), - flex: 1, - sortable: true, - dataIndex: 'addr' } ], - listeners: { - activate: rstore.startUpdate, - destroy: rstore.stopUpdate - } - }); - - 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() { - - Ext.define('ceph-mon-list', { - extend: 'Ext.data.Model', - fields: [ 'addr', 'name', 'rank', 'host', 'quorum' ], - idProperty: 'name' - }); + stateId: 'grid-ceph-monitor', + showCephInstallMask: true, + title: gettext('Monitor') + }, + { + xtype: 'pveNodeCephServiceList', + type: 'mgr', + stateId: 'grid-ceph-manager', + cbind: { pveSelNode: '{pveSelNode}' }, + title: gettext('Manager') + } + ] }); diff --git a/www/manager6/ceph/ServiceList.js b/www/manager6/ceph/ServiceList.js new file mode 100644 index 000000000..16b41fcca --- /dev/null +++ b/www/manager6/ceph/ServiceList.js @@ -0,0 +1,324 @@ +Ext.define('PVE.CephCreateService', { + extend: 'Proxmox.window.Edit', + xtype: 'pveCephCreateService', + + showProgress: true, + + setNode: function(nodename) { + var me = this; + + me.nodename = nodename; + me.url = "/nodes/" + nodename + "/ceph/" + me.type; + }, + + method: 'POST', + isCreate: true, + + items: [ + { + xtype: 'pveNodeSelector', + submitValue: false, + fieldLabel: gettext('Host'), + selectCurNode: true, + allowBlank: false, + listeners: { + change: function(f, value) { + var me = this.up('pveCephCreateService'); + me.setNode(value); + } + } + } + ], + + initComponent : function() { + var me = this; + + if (!me.nodename) { + throw "no node name specified"; + } + + if (!me.type) { + throw "no type specified"; + } + + me.setNode(me.nodename); + + me.callParent(); + } +}); + +Ext.define('PVE.node.CephServiceList', { + extend: 'Ext.grid.GridPanel', + xtype: 'pveNodeCephServiceList', + + onlineHelp: 'chapter_pveceph', + emptyText: Ext.String.format(gettext('No {0} configured.'), 'MDS'), + + stateful: true, + + // will be called when the store loads + storeLoadCallback: Ext.emptyFn, + + // if set to true, does shows the ceph install mask if needed + showCephInstallMask: false, + + controller: { + xclass: 'Ext.app.ViewController', + + init: function(view) { + if (view.pveSelNode) { + view.nodename = view.pveSelNode.data.node; + } + if (!view.nodename) { + throw "no node name specified"; + } + + if (!view.type) { + throw "no type specified"; + } + + view.rstore = Ext.create('Proxmox.data.UpdateStore', { + autoLoad: true, + autoStart: true, + interval: 3000, + storeid: 'ceph-' + view.type + '-list' + view.nodename, + model: 'ceph-service-list', + proxy: { + type: 'proxmox', + url: "/api2/json/nodes/" + view.nodename + "/ceph/" + view.type + } + }); + + view.setStore(Ext.create('Proxmox.data.DiffStore', { + rstore: view.rstore, + sorters: [{ property: 'name' }] + })); + + if (view.storeLoadCallback) { + view.rstore.on('load', view.storeLoadCallback, this); + } + view.on('destroy', view.rstore.stopUpdate); + + if (view.showCephInstallMask) { + var regex = new RegExp("not (installed|initialized)", "i"); + PVE.Utils.handleStoreErrorOrMask(view, view.rstore, regex, function(me, error) { + view.rstore.stopUpdate(); + PVE.Utils.showCephInstallOrMask(view.ownerCt, error.statusText, view.nodename, + function(win){ + me.mon(win, 'cephInstallWindowClosed', function(){ + view.rstore.startUpdate(); + }); + } + ); + }); + } + }, + + service_cmd: function(rec, cmd) { + var view = this.getView(); + if (!rec.data.host) { + Ext.Msg.alert(gettext('Error'), "entry has no host"); + return; + } + Proxmox.Utils.API2Request({ + url: "/nodes/" + rec.data.host + "/ceph/" + cmd, + method: 'POST', + params: { service: view.type + '.' + rec.data.name }, + success: function(response, options) { + var upid = response.result.data; + var win = Ext.create('Proxmox.window.TaskProgress', { + upid: upid, + taskDone: function() { + view.rstore.load(); + } + }); + win.show(); + }, + failure: function(response, opts) { + Ext.Msg.alert(gettext('Error'), response.htmlStatus); + } + }); + }, + onChangeService: function(btn) { + var me = this; + var view = this.getView(); + var cmd = btn.action; + var rec = view.getSelection()[0]; + me.service_cmd(rec, cmd); + }, + + showSyslog: function() { + var view = this.getView(); + var rec = view.getSelection()[0]; + var servicename = 'ceph-' + view.type + '@' + rec.data.name; + var url = "/api2/extjs/nodes/" + rec.data.host + "/syslog?service=" + encodeURIComponent(servicename); + var win = Ext.create('Ext.window.Window', { + title: gettext('Syslog') + ': ' + servicename, + modal: true, + items: [{ + xtype: 'proxmoxLogView', + width: 800, + height: 400, + url: url, + log_select_timespan: 1 + }] + }); + win.show(); + }, + + onCreate: function() { + var view = this.getView(); + var win = Ext.create('PVE.CephCreateService', { + autoShow: true, + nodename: view.nodename, + subject: view.getTitle(), + type: view.type, + taskDone: function() { + view.rstore.load(); + } + }); + } + }, + + tbar: [ + { + xtype: 'proxmoxButton', + text: gettext('Start'), + iconCls: 'fa fa-play', + action: 'start', + disabled: true, + enableFn: function(rec) { + return rec.data.state === 'stopped' || + rec.data.state === 'unknown'; + }, + handler: 'onChangeService' + }, + { + xtype: 'proxmoxButton', + text: gettext('Stop'), + iconCls: 'fa fa-stop', + action: 'stop', + enableFn: function(rec) { + return rec.data.state !== 'stopped'; + }, + disabled: true, + handler: 'onChangeService' + }, + { + xtype: 'proxmoxButton', + text: gettext('Restart'), + iconCls: 'fa fa-refresh', + action: 'restart', + disabled: true, + enableFn: function(rec) { + return rec.data.state !== 'stopped'; + }, + handler: 'onChangeService' + }, + '-', + { + text: gettext('Create'), + reference: 'createButton', + handler: 'onCreate' + }, + { + text: gettext('Destroy'), + xtype: 'proxmoxStdRemoveButton', + getUrl: function(rec) { + var view = this.up('grid'); + if (!rec.data.host) { + Ext.Msg.alert(gettext('Error'), "entry has no host"); + return; + } + return "/nodes/" + rec.data.host + "/ceph/" + view.type + "/" + rec.data.name; + }, + callback: function(options, success, response) { + var view = this.up('grid'); + if (!success) { + Ext.Msg.alert(gettext('Error'), response.htmlStatus); + return; + } + var upid = response.result.data; + var win = Ext.create('Proxmox.window.TaskProgress', { + upid: upid, + taskDone: function() { + view.rstore.load(); + } + }); + win.show(); + } + }, + '-', + { + xtype: 'proxmoxButton', + text: gettext('Syslog'), + disabled: true, + handler: 'showSyslog' + } + ], + + columns: [ + { + header: gettext('Name'), + width: 100, + sortable: true, + renderer: function(v) { + return this.type + '.' + v; + }, + dataIndex: 'name' + }, + { + header: gettext('Host'), + width: 100, + sortable: true, + renderer: function(v) { + return v || Proxmox.Utils.unknownText; + }, + dataIndex: 'host' + }, + { + header: gettext('Status'), + width: 70, + sortable: false, + dataIndex: 'state' + }, + { + header: gettext('Address'), + flex: 1, + sortable: true, + renderer: function(v) { + return v || Proxmox.Utils.unknownText; + }, + dataIndex: 'addr' + }, + { + header: gettext('Version'), + flex: 1, + sortable: true, + dataIndex: 'version' + } + ], + + initComponent: function() { + var me = this; + + if (me.additionalColumns) { + me.columns = me.columns.concat(me.additionalColumns); + } + + me.callParent(); + } + +}, function() { + + Ext.define('ceph-service-list', { + extend: 'Ext.data.Model', + fields: [ 'addr', 'name', 'rank', 'host', 'quorum', 'state', + 'ceph_version', 'ceph_version_short', + { type: 'string', name: 'version', calculate: function(data) { + return PVE.Utils.parse_ceph_version(data); + } } + ], + idProperty: 'name' + }); +}); diff --git a/www/manager6/node/Config.js b/www/manager6/node/Config.js index 700ca6116..054ced643 100644 --- a/www/manager6/node/Config.js +++ b/www/manager6/node/Config.js @@ -326,7 +326,7 @@ Ext.define('PVE.node.Config', { itemId: 'ceph-config' }, { - xtype: 'pveNodeCephMonList', + xtype: 'pveNodeCephMonMgr', title: gettext('Monitor'), iconCls: 'fa fa-tv', groups: ['ceph'],