[pve-devel] [PATCH manager 6/7] add new ceph dashboard
Caspar Smit
casparsmit at supernas.eu
Thu Jan 12 13:45:42 CET 2017
Hi,
First of all, thanks for the nice new CEPH dashboard.
One thing i was wondering about is this, the performance gauges only show
"Client IO" stats. If the cluster is recovering and/or backfilling these
stats are not shown. Maybe it's possible to show recovery/backfilling
performance stats in a seperate gauge? (like in ceph-dash).
Hope you like the idea.
Kind regards,
Caspar Smit
2016-11-22 12:32 GMT+01:00 Dominik Csapak <d.csapak at proxmox.com>:
> this patch changes the ceph dashboard
>
> now we show the information in a more graphical way, namely:
>
> the overall status is displayed by a big icon (+health)
> the warnings/errors are in a list (with severity)
>
> we show more detailed information about monitors, osds, and pgs
>
> we show the usage of the cluster as a gauge graph, and
> the reads, writes and iops as running charts (the last 5 minutes)
>
> Signed-off-by: Dominik Csapak <d.csapak at proxmox.com>
> ---
> www/manager6/Utils.js | 8 ++
> www/manager6/ceph/Status.js | 338 +++++++++++++++++++++++++++++-
> --------------
> 2 files changed, 231 insertions(+), 115 deletions(-)
>
> diff --git a/www/manager6/Utils.js b/www/manager6/Utils.js
> index a32697a..7f261a2 100644
> --- a/www/manager6/Utils.js
> +++ b/www/manager6/Utils.js
> @@ -893,6 +893,14 @@ Ext.define('PVE.Utils', { utilities: {
> return PVE.Utils.format_size(value);
> },
>
> + render_bandwidth: function(value) {
> + if (!Ext.isNumeric(value)) {
> + return '';
> + }
> +
> + return PVE.Utils.format_size(value) + '/s';
> + },
> +
> render_timestamp: function(value, metaData, record, rowIndex,
> colIndex, store) {
> var servertime = new Date(value * 1000);
> return Ext.Date.format(servertime, 'Y-m-d H:i:s');
> diff --git a/www/manager6/ceph/Status.js b/www/manager6/ceph/Status.js
> index ab97a9f..f04fca0 100644
> --- a/www/manager6/ceph/Status.js
> +++ b/www/manager6/ceph/Status.js
> @@ -1,128 +1,236 @@
> Ext.define('PVE.node.CephStatus', {
> - extend: 'PVE.grid.ObjectGrid',
> - alias: ['widget.pveNodeCephStatus'],
> + extend: 'Ext.panel.Panel',
> + alias: 'widget.pveNodeCephStatus',
> +
> onlineHelp: 'chapter_pveceph',
> - cwidth1: 150,
> - interval: 3000,
> +
> + scrollable: true,
> +
> + bodyPadding: '10 0 0 0',
> +
> + defaults: {
> + width: 762,
> + userCls: 'inline-block',
> + padding: '0 0 10 10'
> + },
> +
> + items: [
> + {
> + xtype: 'panel',
> + title: gettext('Health'),
> + bodyPadding: '0 10 10 10',
> + minHeight: 210,
> + layout: {
> + type: 'hbox',
> + align: 'top'
> + },
> + items: [
> + {
> + flex: 1,
> + itemId: 'overallhealth',
> + xtype: 'pveHealthWidget',
> + title: gettext('Status')
> + },
> + {
> + flex: 2,
> + itemId: 'warnings',
> + stateful: true,
> + stateId: 'ceph-status-warnings',
> + padding: '15 0 0 0',
> + xtype: 'grid',
> + minHeight: 100,
> + // since we load the store manually,
> + // to show the emptytext, we have to
> + // specify an empty store
> + store: { data:[] },
> + emptyText: gettext('No Warnings/Errors'),
> + columns: [
> + {
> + dataIndex: 'severity',
> + header: gettext('Severity'),
> + align: 'center',
> + width: 70,
> + renderer: function(value) {
> + var health = PVE.Utils.map_ceph_health[
> value];
> + var classes = PVE.Utils.get_health_icon(
> health);
> +
> + return '<i class="fa fa-fw ' + classes +
> '"></i>';
> + },
> + sorter: {
> + sorterFn: function(a,b) {
> + var healthArr = ['HEALTH_ERR',
> 'HEALTH_WARN', 'HEALTH_OK'];
> + return healthArr.indexOf(b.data.severity)
> - healthArr.indexOf(a.data.severity);
> + }
> + }
> + },
> + {
> + dataIndex: 'summary',
> + header: gettext('Summary'),
> + flex: 1
> + }
> + ]
> + }
> + ]
> + },
> + {
> + xtype: 'pveCephStatusDetail',
> + itemId: 'statusdetail',
> + title: gettext('Status')
> + },
> + {
> + xtype: 'panel',
> + title: gettext('Performance'),
> + bodyPadding: '0 10 10 10',
> + layout: {
> + type: 'hbox',
> + align: 'center'
> + },
> + items: [
> + {
> + flex: 1,
> + xtype: 'pveGauge',
> + itemId: 'space',
> + title: gettext('Usage')
> + },
> + {
> + flex: 2,
> + xtype: 'container',
> + defaults: {
> + padding: '0 0 0 30',
> + height: 100
> + },
> + items: [
> + {
> + itemId: 'reads',
> + xtype: 'pveRunningChart',
> + title: gettext('Reads'),
> + renderer: PVE.Utils.render_bandwidth
> + },
> + {
> + itemId: 'writes',
> + xtype: 'pveRunningChart',
> + title: gettext('Writes'),
> + renderer: PVE.Utils.render_bandwidth
> + },
> + {
> + itemId: 'iops',
> + xtype: 'pveRunningChart',
> + hidden: true,
> + title: gettext('IOPS'),
> + renderer: Ext.util.Format.
> numberRenderer('0,000')
> + },
> + {
> + itemId: 'readiops',
> + xtype: 'pveRunningChart',
> + hidden: true,
> + title: gettext('Read IOPS'),
> + renderer: Ext.util.Format.
> numberRenderer('0,000')
> + },
> + {
> + itemId: 'writeiops',
> + xtype: 'pveRunningChart',
> + hidden: true,
> + title: gettext('Write IOPS'),
> + renderer: Ext.util.Format.
> numberRenderer('0,000')
> + }
> + ]
> + }
> + ]
> + }
> + ],
> +
> + updateAll: function(store, records, success) {
> + if (!success || records.length === 0) {
> + return;
> + }
> +
> + var me = this;
> + var rec = records[0];
> +
> + // add health panel
> + me.down('#overallhealth').updateHealth(PVE.Utils.render_
> ceph_health(rec));
> + // add errors to gridstore
> + me.down('#warnings').getStore().loadRawData(rec.data.health.summary,
> false);
> +
> + // update detailstatus panel
> + me.getComponent('statusdetail').updateAll(rec);
> +
> + // add performance data
> + var used = rec.data.pgmap.bytes_used;
> + var total = rec.data.pgmap.bytes_total;
> +
> + var text = Ext.String.format(gettext('{0} of {1}'),
> + PVE.Utils.render_size(used),
> + PVE.Utils.render_size(total)
> + );
> +
> + // update the usage widget
> + me.down('#space').updateValue(used/total, text);
> +
> + // TODO: logic for jewel (iops splitted in read/write)
> +
> + var iops = rec.data.pgmap.op_per_sec;
> + var readiops = rec.data.pgmap.read_op_per_sec;
> + var writeiops = rec.data.pgmap.write_op_per_sec0;
> + var reads = rec.data.pgmap.read_bytes_sec || 0;
> + var writes = rec.data.pgmap.write_bytes_sec || 0;
> +
> + if (iops !== undefined && me.version !== 'hammer') {
> + me.change_version('hammer');
> + } else if((readiops !== undefined || writeiops !== undefined) &&
> me.version !== 'jewel') {
> + me.change_version('jewel');
> + }
> + // update the graphs
> + me.reads.addDataPoint(reads);
> + me.writes.addDataPoint(writes);
> + me.iops.addDataPoint(iops);
> + me.readiops.addDataPoint(readiops);
> + me.writeiops.addDataPoint(writeiops);
> + },
> +
> + change_version: function(version) {
> + var me = this;
> + me.version = version;
> + me.sp.set('ceph-version', version);
> + me.iops.setVisible(version === 'hammer');
> + me.readiops.setVisible(version === 'jewel');
> + me.writeiops.setVisible(version === 'jewel');
> + },
> +
> initComponent: function() {
> - /*jslint confusion: true */
> - var me = this;
> + var me = this;
>
> var nodename = me.pveSelNode.data.node;
> if (!nodename) {
> throw "no node name specified";
> }
>
> - var renderquorum = function(value) {
> - if (!value || value.length < 0) {
> - return 'No';
> - }
> -
> - return 'Yes {' + value.join(' ') + '}';
> - };
> -
> - var rendermonmap = function(d) {
> - if (!d) {
> - return '';
> - }
> -
> - var txt = 'e' + d.epoch + ': ' + d.mons.length + " mons at ";
> -
> - Ext.Array.each(d.mons, function(d) {
> - txt += d.name + '=' + d.addr + ',';
> - });
> -
> - return txt;
> - };
> -
> - var renderosdmap = function(value) {
> - if (!value || !value.osdmap) {
> - return '';
> - }
> -
> - var d = value.osdmap;
> -
> - var txt = 'e' + d.epoch + ': ';
> -
> - txt += d.num_osds + ' osds: ' + d.num_up_osds + ' up, ' +
> - d.num_in_osds + " in";
> -
> - return txt;
> - };
> -
> - var renderhealth = function(value) {
> - if (!value || !value.overall_status) {
> - return '';
> - }
> -
> - var txt = value.overall_status;
> -
> - Ext.Array.each(value.summary, function(d) {
> - txt += " " + d.summary + ';';
> - });
> -
> - return txt;
> - };
> -
> - var renderpgmap = function(d) {
> - if (!d) {
> - return '';
> - }
> -
> - var txt = 'v' + d.version + ': ';
> -
> - txt += d.num_pgs + " pgs:";
> -
> - Ext.Array.each(d.pgs_by_state, function(s) {
> - txt += " " + s.count + " " + s.state_name;
> - });
> - txt += '; ';
> -
> - txt += PVE.Utils.format_size(d.data_bytes) + " data, ";
> - txt += PVE.Utils.format_size(d.bytes_used) + " used, ";
> - txt += PVE.Utils.format_size(d.bytes_avail) + " avail";
> -
> - return txt;
> - };
> -
> - Ext.applyIf(me, {
> - url: "/api2/json/nodes/" + nodename + "/ceph/status",
> - rows: {
> - health: {
> - header: 'health',
> - renderer: renderhealth,
> - required: true
> - },
> - quorum_names: {
> - header: 'quorum',
> - renderer: renderquorum,
> - required: true
> - },
> - fsid: {
> - header: 'cluster',
> - required: true
> - },
> - monmap: {
> - header: 'monmap',
> - renderer: rendermonmap,
> - required: true
> - },
> - osdmap: {
> - header: 'osdmap',
> - renderer: renderosdmap,
> - required: true
> - },
> - pgmap: {
> - header: 'pgmap',
> - renderer: renderpgmap,
> - required: true
> - }
> - }
> - });
> -
> me.callParent();
> + me.store = Ext.create('PVE.data.UpdateStore', {
> + storeid: 'ceph-status-' + nodename,
> + interval: 5000,
> + proxy: {
> + type: 'pve',
> + url: '/api2/json/nodes/' + nodename + '/ceph/status'
> + }
> + });
>
> - me.on('activate', me.rstore.startUpdate);
> - me.on('destroy', me.rstore.stopUpdate);
> + // save references for the updatefunction
> + me.iops = me.down('#iops');
> + me.readiops = me.down('#readiops');
> + me.writeiops = me.down('#writeiops');
> + me.reads = me.down('#reads');
> + me.writes = me.down('#writes');
> +
> + // get ceph version
> + me.sp = Ext.state.Manager.getProvider();
> + me.version = me.sp.get('ceph-version');
> + me.change_version(me.version);
> +
> + PVE.Utils.monStoreErrors(me,me.store);
> + me.mon(me.store, 'load', me.updateAll, me);
> + me.on('destroy', me.store.stopUpdate);
> + me.store.startUpdate();
> }
> +
> });
> --
> 2.1.4
>
>
> _______________________________________________
> pve-devel mailing list
> pve-devel at pve.proxmox.com
> http://pve.proxmox.com/cgi-bin/mailman/listinfo/pve-devel
>
More information about the pve-devel
mailing list