pve-manager/www/manager6/ceph/StatusDetail.js
Dominik Csapak f3f73cde3b check general health for ceph monitors, not only time
we did not check the healthservices array for the monitor widget,
this patch does that, and takes the worst health state

Signed-off-by: Dominik Csapak <d.csapak@proxmox.com>
2017-01-10 13:03:04 +01:00

279 lines
6.7 KiB
JavaScript

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 ||
!record.data.health.health ||
!record.data.health.health.health_services ||
!record.data.health.health.health_services[0]) {
// 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 monHealth = record.data.health.health.health_services[0].mons || [];
var timechecks = {};
var healthchecks = {};
var monContainer = me.getComponent('monitors');
var i;
for (i = 0; i < mons.length && i < monTimes.length; i++) {
timechecks[monTimes[i].name] = monTimes[i].health;
}
if (mons.length === 1) {
timechecks[mons[0].name] = "HEALTH_OK";
}
for (i = 0; i < mons.length && i < monHealth.length; i++) {
healthchecks[monHealth[i].name] = monHealth[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, healthchecks[mons[i].name]);
}
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, health) {
var me = this;
var state = 'HEALTH_ERR';
var healthstates = {
'HEALTH_OK': 3,
'HEALTH_WARN': 2,
'HEALTH_ERR': 1
};
// if the monitor is part of the quorum
// and has a timestate, get the timestate,
// otherwise the state is ERR
if (timestate && health && quorum_names &&
quorum_names.indexOf(data.name) !== -1) {
state = (healthstates[health] < healthstates[timestate])?
health : 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();
}
}
}
}
});