[pve-devel] [PATCH manager v2 5/5] ui: add CephFS integration

Thomas Lamprecht t.lamprecht at proxmox.com
Thu Nov 22 20:34:22 CET 2018


create/destroy MDS and create CephFS (if none is configured yet).
Can be improved, e.g., start/stop/restart for MDS this should be enough for a
starter, though.

Basic code and ui layout is based off my dc/Cluster view. We may want to split
the two grids out in separate defines, it could be a bit much to have all
inline.

Signed-off-by: Thomas Lamprecht <t.lamprecht at proxmox.com>
---

new in v2

 www/manager6/Makefile       |   1 +
 www/manager6/ceph/FS.js     | 385 ++++++++++++++++++++++++++++++++++++
 www/manager6/node/Config.js |   8 +
 3 files changed, 394 insertions(+)
 create mode 100644 www/manager6/ceph/FS.js

diff --git a/www/manager6/Makefile b/www/manager6/Makefile
index d005d714..e75f0de6 100644
--- a/www/manager6/Makefile
+++ b/www/manager6/Makefile
@@ -93,6 +93,7 @@ JSSRC= 				                 	\
 	panel/IPSet.js					\
 	panel/ConfigPanel.js				\
 	grid/BackupView.js				\
+	ceph/FS.js					\
 	ceph/Pool.js					\
 	ceph/OSD.js					\
 	ceph/Monitor.js					\
diff --git a/www/manager6/ceph/FS.js b/www/manager6/ceph/FS.js
new file mode 100644
index 00000000..f2743a4d
--- /dev/null
+++ b/www/manager6/ceph/FS.js
@@ -0,0 +1,385 @@
+/*jslint confusion: true */
+Ext.define('PVE.CephCreateFS', {
+    extend: 'Proxmox.window.Edit',
+    alias: 'widget.pveCephCreateFS',
+
+    showTaskViewer: true,
+    //onlineHelp: 'pve_ceph_fs',
+
+    subject: 'Ceph FS',
+    isCreate: true,
+    method: 'POST',
+
+    setFSName: function(fsName) {
+        var me = this;
+
+	if (fsName === '') {
+	    fsName = 'cephfs';
+	}
+
+        me.url = "/nodes/" + me.nodename + "/ceph/fs/" + fsName;
+    },
+
+    items: [
+	{
+	    xtype: 'textfield',
+	    fieldLabel: gettext('Name'),
+	    name: 'name',
+	    value: 'cephfs',
+	    listeners: {
+	        change: function(f, value) {
+		    this.up('pveCephCreateFS').setFSName(value);
+	        }
+	    },
+	    submitValue: false, // already encoded in apicall URL
+	    emptyText: 'cephfs'
+	},
+	{
+	    xtype: 'proxmoxintegerfield',
+	    fieldLabel: 'pg_num',
+	    name: 'pg_num',
+	    value: 64,
+	    emptyText: 64,
+	    minValue: 8,
+	    maxValue: 32768,
+	    allowBlank: false
+	},
+	{
+	    xtype: 'proxmoxcheckbox',
+	    fieldLabel: gettext('Add Storage'),
+	    value: true,
+	    name: 'add_storage'
+	}
+    ],
+
+    initComponent : function() {
+        var me = this;
+
+	if (!me.nodename) {
+	    throw "no node name specified";
+	}
+
+        Ext.apply(me, {
+	    url: "/nodes/" + me.nodename + "/ceph/fs/cephfs",
+	    defaults: {
+		nodename: me.nodename
+	    }
+        });
+
+        me.callParent();
+    }
+});
+
+Ext.define('PVE.CephCreateMDS', {
+    extend: 'Proxmox.window.Edit',
+    alias: 'widget.pveCephCreateMDS',
+
+    showProgress: true,
+    //onlineHelp: 'pve_ceph_mds',
+
+    subject: 'Ceph MDS',
+    isCreate: true,
+    method: 'POST',
+
+    setNode: function(nodename) {
+        var me = this;
+
+	me.nodename = nodename;
+        me.url = "/nodes/" + nodename + "/ceph/mds/" + nodename;
+    },
+
+    items: [
+	{
+	    xtype: 'pveNodeSelector',
+	    fieldLabel: gettext('Node'),
+	    selectCurNode: true,
+	    submitValue: false,
+	    allowBlank: false,
+	    listeners: {
+		change: function(f, value) {
+		    this.up('pveCephCreateMDS').setNode(value);
+		}
+	    }
+	}
+    ],
+
+    initComponent : function() {
+        var me = this;
+
+	if (!me.nodename) {
+	    throw "no node name specified";
+	}
+
+        Ext.apply(me, {
+	    url: "/nodes/" + me.nodename + "/ceph/mds/" + me.nodename
+        });
+
+        me.callParent();
+    }
+});
+
+Ext.define('PVE.NodeCephFSPanel', {
+    extend: 'Ext.panel.Panel',
+    xtype: 'pveNodeCephFSPanel',
+    mixins: ['Proxmox.Mixin.CBind'],
+
+    title: gettext('Cluster Administration'),
+    onlineHelp: 'chapter_pvecm',
+
+    border: false,
+    defaults: {
+	border: false,
+	cbind: {
+	    nodename: '{nodename}'
+	}
+    },
+
+    viewModel: {
+	parent: null,
+	data: {
+	    cephfsConfigured: false,
+	    mdscount: 0
+	}
+    },
+
+    /*initComponent: function() {
+        var me = this;
+        Ext.apply(me, {
+	    defaults: {
+		nodename: me.nodename,
+		border: false
+	    }
+	});
+
+        me.callParent();
+    },*/
+
+    items: [
+	{
+	    xtype: 'grid',
+	    title: gettext('CephFS'),
+	    controller: {
+		xclass: 'Ext.app.ViewController',
+
+		init: function(view) {
+		    view.rstore = Ext.create('Proxmox.data.UpdateStore', {
+			autoLoad: true,
+			xtype: 'update',
+			interval: 5 * 1000,
+			autoStart: true,
+			storeid: 'pve-ceph-fs',
+			model: 'pve-ceph-fs'
+		    });
+		    view.setStore(Ext.create('Proxmox.data.DiffStore', {
+			rstore: view.rstore,
+			sorters: {
+			    property: 'name',
+			    order: 'DESC'
+			}
+		    }));
+		    Proxmox.Utils.monStoreErrors(view, view.rstore);
+		    view.rstore.on('load', this.onLoad, this);
+		    view.on('destroy', view.rstore.stopUpdate);
+		},
+
+		onCreate: function() {
+		    var view = this.getView();
+		    view.rstore.stopUpdate();
+		    var win = Ext.create('PVE.CephCreateFS', {
+			autoShow: true,
+			nodename: view.nodename,
+			listeners: {
+			    destroy: function() {
+				view.rstore.startUpdate();
+			    }
+			}
+		    });
+		},
+
+		onLoad: function(store, records, success) {
+		    var vm = this.getViewModel();
+		    if (!(success && records && records.length > 0)) {
+			vm.set('cephfsConfigured', false);
+			return;
+		    }
+		    vm.set('cephfsConfigured', true);
+		}
+	    },
+	    tbar: [
+		{
+		    text: gettext('Create CephFS'),
+		    reference: 'createButton',
+		    handler: 'onCreate',
+		    bind: {
+			// only one CephFS per Ceph cluster makes sense for now
+			disabled: '{cephfsConfigured}'
+		    }
+		}
+	    ],
+	    columns: [
+		{
+		    header: gettext('Name'),
+		    flex: 1,
+		    dataIndex: 'name'
+		},
+		{
+		    header: 'Data Pool',
+		    flex: 1,
+		    dataIndex: 'data_pool'
+		},
+		{
+		    header: 'Metadata Pool',
+		    flex: 1,
+		    dataIndex: 'metadata_pool'
+		}
+	    ],
+	    cbind: {
+		nodename: '{nodename}'
+	    }
+	},
+	{
+	    xtype: 'grid',
+	    title: gettext('Metadata Servers'),
+	    viewModel: {
+		data: {
+		    rowSelected: false
+		}
+	    },
+	    controller: {
+		xclass: 'Ext.app.ViewController',
+
+		init: function(view) {
+		    view.rstore = Ext.create('Proxmox.data.UpdateStore', {
+			autoLoad: true,
+			xtype: 'update',
+			interval: 3 * 1000,
+			autoStart: true,
+			storeid: 'pve-ceph-mds',
+			model: 'pve-ceph-mds'
+		    });
+		    view.setStore(Ext.create('Proxmox.data.DiffStore', {
+			rstore: view.rstore,
+			sorters: {
+			    property: 'id',
+			    order: 'DESC'
+			}
+		    }));
+		    Proxmox.Utils.monStoreErrors(view, view.rstore);
+		    view.rstore.on('load', this.onLoad, this);
+		    view.on('destroy', view.rstore.stopUpdate);
+
+		    var vm = this.getViewModel();
+		    view.mon(view.selModel, "selectionchange", function() {
+			var rec = view.selModel.getSelection()[0];
+
+			vm.set('rowSelected', !!rec);
+		    });
+		},
+
+		onCreateMDS: function() {
+		    var view = this.getView();
+		    view.rstore.stopUpdate();
+		    var win = Ext.create('PVE.CephCreateMDS', {
+			autoShow: true,
+			nodename: view.nodename,
+			listeners: {
+			    destroy: function() {
+				view.rstore.startUpdate();
+			    }
+			}
+		    });
+		},
+
+		onDestroyMDS: function() {
+		    var view = this.getView();
+		    var rec = view.selModel.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/mds/" + 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);
+			}
+		    });
+		},
+
+		onLoad: function(store, records, success) {
+		    var vm = this.getViewModel();
+		    if (!success || !records) {
+			vm.set('mdscount', 0);
+			return;
+		    }
+		    vm.set('mdscount', records.length);
+		}
+	    },
+	    tbar: [
+		{
+		    text: gettext('Create MDS'),
+		    reference: 'createButton',
+		    handler: 'onCreateMDS'
+		},
+		{
+		    text: gettext('Destroy MDS'),
+		    bind: {
+			disabled: '{!rowSelected}'
+		    },
+		    handler: 'onDestroyMDS'
+		}
+	    ],
+	    columns: [
+		{
+		    header: gettext('Name'),
+		    flex: 1,
+		    dataIndex: 'name'
+		},
+		{
+		    header: gettext('Host'),
+		    flex: 1,
+		    dataIndex: 'host'
+		},
+		{
+		    header: gettext('Address'),
+		    flex: 1,
+		    dataIndex: 'addr'
+		},
+		{
+		    header: gettext('State'),
+		    flex: 1,
+		    dataIndex: 'state'
+		}
+	    ],
+	    cbind: {
+		nodename: '{nodename}'
+	    }
+	}
+    ]
+}, function() {
+    Ext.define('pve-ceph-mds', {
+	extend: 'Ext.data.Model',
+	fields: [ 'name', 'host', 'addr', 'state' ],
+	proxy: {
+	    type: 'proxmox',
+	    url: "/api2/json/nodes/localhost/ceph/mds"
+	},
+	idProperty: 'name'
+    });
+    Ext.define('pve-ceph-fs', {
+	extend: 'Ext.data.Model',
+	fields: [ 'name', 'data_pool', 'metadata_pool' ],
+	proxy: {
+	    type: 'proxmox',
+	    url: "/api2/json/nodes/localhost/ceph/fs"
+	},
+	idProperty: 'name'
+    });
+});
diff --git a/www/manager6/node/Config.js b/www/manager6/node/Config.js
index 8b2b802a..f9a62670 100644
--- a/www/manager6/node/Config.js
+++ b/www/manager6/node/Config.js
@@ -340,6 +340,14 @@ Ext.define('PVE.node.Config', {
 		    groups: ['ceph'],
 		    itemId: 'ceph-osdtree'
 		},
+		{
+		    xtype: 'pveNodeCephFSPanel',
+		    title: 'CephFS',
+		    iconCls: 'fa fa-folder',
+		    groups: ['ceph'],
+		    nodename: nodename,
+		    itemId: 'ceph-cephfspanel'
+		},
 		{
 		    xtype: 'pveNodeCephPoolList',
 		    title: 'Pools',
-- 
2.19.1





More information about the pve-devel mailing list