[pve-devel] [PATCH manager 14/20] gui: ceph: add ServiceList component and use it

Dominik Csapak d.csapak at proxmox.com
Tue Jun 4 14:47:53 CEST 2019


this is an abstraction for listing Ceph Services with a few improvements:
* start/stop/restart buttons for all services (incl. icons)
* a syslog button to view the syslog of that service
* correct reloading behaviour when creating/destroying

Signed-off-by: Dominik Csapak <d.csapak at proxmox.com>
---
 www/manager6/Makefile            |   1 +
 www/manager6/ceph/Monitor.js     | 252 ++++--------------------------
 www/manager6/ceph/ServiceList.js | 324 +++++++++++++++++++++++++++++++++++++++
 www/manager6/node/Config.js      |   2 +-
 4 files changed, 358 insertions(+), 221 deletions(-)
 create mode 100644 www/manager6/ceph/ServiceList.js

diff --git a/www/manager6/Makefile b/www/manager6/Makefile
index 2f25a83f..c8673533 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 638fa9f1..eb35e91c 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',
-
-    initComponent: function() {
-        var me = this;
-
-	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;
-		}
+    defaults: {
+	border: false,
+	onlineHelp: 'chapter_pveceph',
+	flex: 1
+    },
 
-		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);
-		    }
-		});
-	    }
-	});
+    layout: {
+	type: 'vbox',
+	align: 'stretch'
+    },
 
-	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 00000000..16b41fcc
--- /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 700ca611..054ced64 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'],
-- 
2.11.0





More information about the pve-devel mailing list