[pve-devel] [PATCH manager 5/7] add ceph statudetail panel
Dominik Csapak
d.csapak at proxmox.com
Tue Nov 22 12:32:13 CET 2016
this adds a the component ceph statusdetail,
it displays the monitors (+status)
the osd as a table (in/out,up/down)
and the pg states as a list (+number)
Signed-off-by: Dominik Csapak <d.csapak at proxmox.com>
---
www/css/ext6-pve.css | 44 +++++++
www/manager6/Makefile | 1 +
www/manager6/ceph/StatusDetail.js | 259 ++++++++++++++++++++++++++++++++++++++
3 files changed, 304 insertions(+)
create mode 100644 www/manager6/ceph/StatusDetail.js
diff --git a/www/css/ext6-pve.css b/www/css/ext6-pve.css
index 7bd7e24..5cd09ca 100644
--- a/www/css/ext6-pve.css
+++ b/www/css/ext6-pve.css
@@ -456,3 +456,47 @@ div.right-aligned {
.critical {
color: #FF6C59;
}
+
+/* for the ceph monitor widgets */
+div.monitor {
+ text-align:left;
+ border:#cfcfcf solid 1px;
+ border-radius:2px;
+ margin: 2px;
+ padding: 5px 8px;
+}
+
+/* for auto layout */
+div.inline-block {
+ display: inline-block;
+ vertical-align: top;
+}
+
+/* ceph dashboard osd table styling */
+table.osds {
+ border-collapse: collapse;
+ margin: auto;
+}
+
+table.osds td {
+ padding: 4px;
+ text-align: right;
+ border-right: 1px solid #cfcfcf;
+}
+
+table.osds td:last-of-type {
+ border-right: 0;
+}
+
+table.osds tr {
+ border-bottom: 1px solid #cfcfcf;
+}
+
+table.osds tr:last-of-type {
+ border-bottom: 0;
+}
+
+table.osds td:first-of-type {
+ text-align: left;
+}
+
diff --git a/www/manager6/Makefile b/www/manager6/Makefile
index 1263cc9..b61ad20 100644
--- a/www/manager6/Makefile
+++ b/www/manager6/Makefile
@@ -110,6 +110,7 @@ JSSRC= \
ceph/Monitor.js \
ceph/Crush.js \
ceph/Status.js \
+ ceph/StatusDetail.js \
ceph/Config.js \
node/Disks.js \
node/DNSEdit.js \
diff --git a/www/manager6/ceph/StatusDetail.js b/www/manager6/ceph/StatusDetail.js
new file mode 100644
index 0000000..16e6e08
--- /dev/null
+++ b/www/manager6/ceph/StatusDetail.js
@@ -0,0 +1,259 @@
+Ext.define('PVE.ceph.StatusDetail', {
+ extend: 'Ext.panel.Panel',
+ alias: 'widget.pveCephStatusDetail',
+
+ layout: {
+ type: 'hbox',
+ align: 'stretch'
+ },
+
+ bodyPadding: '0 5 20',
+ defaults: {
+ xtype: 'box',
+ style: {
+ 'text-align':'center'
+ }
+ },
+
+ items: [{
+ flex: 1,
+ itemId: 'monitors',
+ xtype: 'container',
+ items: [
+ {
+ xtype: 'box',
+ width: '100%',
+ html: '<h3>' + gettext('Monitors') + '</h3>'
+ }
+ ]
+ },{
+ flex: 1,
+ itemId: 'osds',
+ data: {
+ total: 0,
+ upin: 0,
+ upout: 0,
+ downin: 0,
+ downout: 0
+ },
+ tpl: [
+ '<h3>' + gettext('OSDs') + '</h3>',
+ '<table class="osds">',
+ '<tr><td></td>',
+ '<td><i class="fa fa-fw good fa-circle"></i>',
+ gettext('In'),
+ '</td>',
+ '<td><i class="fa fa-fw warning fa-circle-o"></i>',
+ gettext('Out'),
+ '</td>',
+ '</tr>',
+ '<tr>',
+ '<td><i class="fa fa-fw good fa-arrow-circle-up"></i>',
+ gettext('Up'),
+ '</td>',
+ '<td>{upin}</td>',
+ '<td>{upout}</td>',
+ '</tr>',
+ '<tr>',
+ '<td><i class="fa fa-fw critical fa-arrow-circle-down"></i>',
+ gettext('Down'),
+ '</td>',
+ '<td>{downin}</td>',
+ '<td>{downout}</td>',
+ '</tr>',
+ '</table>',
+ '<br /><div>',
+ gettext('Total'),
+ ': {total}',
+ '</div>'
+ ]
+ },
+ {
+ flex: 1.6,
+ itemId: 'pgs',
+ padding: '0 10',
+ data: {
+ monitors: []
+ },
+ tpl: [
+ '<h3>' + gettext('PGs') + '</h3>',
+ '<tpl for="monitors">',
+ '<div class="left-aligned">{state_name}:</div>',
+ '<div class="right-aligned">{count}</div><br />',
+ '<div style="clear:both"></div>',
+ '</tpl>'
+ ]
+ }],
+
+ updateAll: function(record) {
+ var me = this;
+ me.suspendLayout = true;
+
+ if (!record.data.pgmap ||
+ !record.data.osdmap ||
+ !record.data.osdmap.osdmap ||
+ !record.data.health ||
+ !record.data.health.timechecks ||
+ !record.data.monmap ||
+ !record.data.monmap.mons) {
+ // only continue if we have all the data
+ return;
+ }
+
+ // update pgs sorted
+ var pgs_by_state = record.data.pgmap.pgs_by_state || [];
+ pgs_by_state.sort(function(a,b){
+ return (a.state_name < b.state_name)?-1:(a.state_name === b.state_name)?0:1;
+ });
+ me.getComponent('pgs').update({monitors: pgs_by_state});
+
+ // update osds counts
+ // caution: this code is not the nicest,
+ // but since the status call only gives us
+ // the total, up and in value,
+ // we parse the health summary and look for the
+ // x/y in osds are down message
+ // to get the rest of the numbers
+ //
+ // the alternative would be to make a second api call,
+ // as soon as not all osds are up, but those are costly
+
+ var total_osds = record.data.osdmap.osdmap.num_osds || 0;
+ var in_osds = record.data.osdmap.osdmap.num_in_osds || 0;
+ var up_osds = record.data.osdmap.osdmap.num_up_osds || 0;
+ var out_osds = total_osds - in_osds;
+ var down_osds = total_osds - up_osds;
+ var downin_osds = 0;
+ var downinregex = /(\d+)\/(\d+) in osds are down/;
+ Ext.Array.some(record.data.health.summary, function(item) {
+ var found = item.summary.match(downinregex);
+
+ if (found !== null) {
+ // sanity check, test if the message is
+ // consistent with the direct value
+ // for in osds
+ if (found[2] == in_osds) {
+ downin_osds = parseInt(found[1],10);
+ return true;
+ }
+ }
+
+ return false;
+ });
+
+ var downout_osds = down_osds - downin_osds;
+ var upin_osds = in_osds - downin_osds;
+ var upout_osds = up_osds - upin_osds;
+ var osds = {
+ total: total_osds,
+ upin: upin_osds,
+ upout: upout_osds,
+ downin: downin_osds,
+ downout: downout_osds
+ };
+ me.getComponent('osds').update(osds);
+
+ // update the monitors
+ var mons = record.data.monmap.mons.sort(function(a,b) {
+ return (a.name < b.name)?-1:(a.name > b.name)?1:0;
+ });
+
+ var monTimes = record.data.health.timechecks.mons;
+ var timechecks = {};
+ var monContainer = me.getComponent('monitors');
+ var i;
+ for (i = 0; i < mons.length && i < monTimes.length; i++) {
+ timechecks[monTimes[i].name] = monTimes[i].health;
+ }
+
+ for (i = 0; i < mons.length; i++) {
+ var monitor = monContainer.getComponent('mon.' + mons[i].name);
+ if (!monitor) {
+ // since mons are already sorted, and
+ // we always have a sorted list
+ // we can add it at the mons+1 position (because of the title)
+ monitor = monContainer.insert(i+1, {
+ xtype: 'pveCephMonitorWidget',
+ itemId: 'mon.' + mons[i].name
+ });
+ }
+ monitor.updateMonitor(timechecks[mons[i].name], mons[i], record.data.quorum_names);
+ }
+ me.suspendLayout = false;
+ me.updateLayout();
+ }
+});
+
+Ext.define('PVE.ceph.MonitorWidget', {
+ extend: 'Ext.Component',
+ alias: 'widget.pveCephMonitorWidget',
+
+ userCls: 'monitor inline-block',
+ data: {
+ name: '0',
+ health: 'HEALTH_ERR',
+ iconCls: PVE.Utils.get_health_icon(),
+ addr: ''
+ },
+
+ tpl: [
+ '{name}: ',
+ '<i class="fa fa-fw {iconCls}"></i>'
+ ],
+
+ // expects 3 variables which are
+ // timestate: the status from timechecks.mons
+ // data: the monmap.mons data
+ // quorum_names: the quorum_names array
+ updateMonitor: function(timestate, data, quorum_names) {
+ var me = this;
+ var state = 'HEALTH_ERR';
+
+ // if the monitor is part of the quorum
+ // and has a timestate, get the timestate,
+ // otherwise the state is ERR
+ if (timestate && quorum_names &&
+ quorum_names.indexOf(data.name) !== -1) {
+ state = timestate;
+ }
+
+ me.update(Ext.apply(me.data, {
+ health: state,
+ addr: data.addr,
+ name: data.name,
+ iconCls: PVE.Utils.get_health_icon(PVE.Utils.map_ceph_health[state])
+ }));
+ },
+
+ listeners: {
+ mouseenter: {
+ element: 'el',
+ fn: function(events, element) {
+ var me = this.component;
+ if (!me) {
+ return;
+ }
+ if (!me.tooltip) {
+ me.tooltip = Ext.create('Ext.tip.ToolTip', {
+ target: me.el,
+ trackMouse: true,
+ renderTo: Ext.getBody(),
+ html: gettext('Monitor') + ': ' + me.data.name + '<br />' +
+ gettext('Address') + ': ' + me.data.addr + '<br />' +
+ gettext('Health') + ': ' + me.data.health
+ });
+ }
+ me.tooltip.show();
+ }
+ },
+ mouseleave: {
+ element: 'el',
+ fn: function(events, element) {
+ var me = this.component;
+ if (me.tooltip) {
+ me.tooltip.hide();
+ }
+ }
+ }
+ }
+});
--
2.1.4
More information about the pve-devel
mailing list