[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