pve-manager/www/manager6/ceph/Services.js
Thomas Lamprecht f87b8d2f3f ui: ceph: services: eslint fixes and code cleanup
Signed-off-by: Thomas Lamprecht <t.lamprecht@proxmox.com>
2021-04-24 18:44:57 +02:00

357 lines
8.6 KiB
JavaScript

Ext.define('PVE.ceph.Services', {
extend: 'Ext.panel.Panel',
alias: 'widget.pveCephServices',
layout: {
type: 'hbox',
align: 'stretch',
},
bodyPadding: '0 5 20',
defaults: {
xtype: 'box',
style: {
'text-align': 'center',
},
},
items: [
{
flex: 1,
xtype: 'pveCephServiceList',
itemId: 'mons',
title: gettext('Monitors'),
},
{
flex: 1,
xtype: 'pveCephServiceList',
itemId: 'mgrs',
title: gettext('Managers'),
},
{
flex: 1,
xtype: 'pveCephServiceList',
itemId: 'mdss',
title: gettext('Meta Data Servers'),
},
],
updateAll: function(metadata, status) {
var me = this;
const healthstates = {
'HEALTH_UNKNOWN': 0,
'HEALTH_ERR': 1,
'HEALTH_WARN': 2,
'HEALTH_UPGRADE': 3,
'HEALTH_OLD': 4,
'HEALTH_OK': 5,
};
// order guarantee since es2020, but browsers did so before. Note, integers would break it.
const healthmap = Object.keys(healthstates);
let maxversion = "00.0.00";
Object.values(metadata.version || {}).forEach(function(version) {
if (PVE.Utils.compare_ceph_versions(version, maxversion) > 0) {
maxversion = version;
}
});
var quorummap = status && status.quorum_names ? status.quorum_names : [];
let monmessages = {}, mgrmessages = {}, mdsmessages = {};
if (status) {
if (status.health) {
Ext.Object.each(status.health.checks, function(key, value, _obj) {
if (!Ext.String.startsWith(key, "MON_")) {
return;
}
for (let i = 0; i < value.detail.length; i++) {
let match = value.detail[i].message.match(/mon.([a-zA-Z0-9\-.]+)/);
if (!match) {
continue;
}
let monid = match[1];
if (!monmessages[monid]) {
monmessages[monid] = {
worstSeverity: healthstates.HEALTH_OK,
messages: [],
};
}
let severityIcon = PVE.Utils.get_ceph_icon_html(value.severity, true);
let details = value.detail.reduce((acc, v) => `${acc}\n${v.message}`, '');
monmessages[monid].messages.push(severityIcon + details);
if (healthstates[value.severity] < monmessages[monid].worstSeverity) {
monmessages[monid].worstSeverity = healthstates[value.severity];
}
}
});
}
if (status.mgrmap) {
mgrmessages[status.mgrmap.active_name] = "active";
status.mgrmap.standbys.forEach(function(mgr) {
mgrmessages[mgr.name] = "standby";
});
}
if (status.fsmap) {
status.fsmap.by_rank.forEach(function(mds) {
mdsmessages[mds.name] = 'rank: ' + mds.rank + "; " + mds.status;
});
}
}
let checks = {
mon: function(mon) {
if (quorummap.indexOf(mon.name) !== -1) {
mon.health = healthstates.HEALTH_OK;
} else {
mon.health = healthstates.HEALTH_ERR;
}
if (monmessages[mon.name]) {
if (monmessages[mon.name].worstSeverity < mon.health) {
mon.health = monmessages[mon.name].worstSeverity;
}
Array.prototype.push.apply(mon.messages, monmessages[mon.name].messages);
}
return mon;
},
mgr: function(mgr) {
if (mgrmessages[mgr.name] === 'active') {
mgr.title = '<b>' + mgr.title + '</b>';
mgr.statuses.push(gettext('Status') + ': <b>active</b>');
} else if (mgrmessages[mgr.name] === 'standby') {
mgr.statuses.push(gettext('Status') + ': standby');
} else if (mgr.health > healthstates.HEALTH_WARN) {
mgr.health = healthstates.HEALTH_WARN;
}
return mgr;
},
mds: function(mds) {
if (mdsmessages[mds.name]) {
mds.title = '<b>' + mds.title + '</b>';
mds.statuses.push(gettext('Status') + ': <b>' + mdsmessages[mds.name]+"</b>");
} else if (mds.addr !== Proxmox.Utils.unknownText) {
mds.statuses.push(gettext('Status') + ': standby');
}
return mds;
},
};
for (let type of ['mon', 'mgr', 'mds']) {
var ids = Object.keys(metadata[type] || {});
me[type] = {};
for (let id of ids) {
const [name, host] = id.split('@');
let result = {
id: id,
health: healthstates.HEALTH_OK,
statuses: [],
messages: [],
name: name,
title: metadata[type][id].name || name,
host: host,
version: PVE.Utils.parse_ceph_version(metadata[type][id]),
service: metadata[type][id].service,
addr: metadata[type][id].addr || metadata[type][id].addrs || Proxmox.Utils.unknownText,
};
result.statuses = [
gettext('Host') + ": " + host,
gettext('Address') + ": " + result.addr,
];
if (checks[type]) {
result = checks[type](result);
}
if (result.service && !result.version) {
result.messages.push(
PVE.Utils.get_ceph_icon_html('HEALTH_UNKNOWN', true) +
gettext('Stopped'),
);
result.health = healthstates.HEALTH_UNKNOWN;
}
if (!result.version && result.addr === Proxmox.Utils.unknownText) {
result.health = healthstates.HEALTH_UNKNOWN;
}
if (result.version) {
result.statuses.push(gettext('Version') + ": " + result.version);
if (result.version !== maxversion) {
if (metadata.version[host] === maxversion) {
if (result.health > healthstates.HEALTH_OLD) {
result.health = healthstates.HEALTH_OLD;
}
result.messages.push(
PVE.Utils.get_ceph_icon_html('HEALTH_OLD', true) +
gettext('A newer version was installed but old version still running, please restart'),
);
} else {
if (result.health > healthstates.HEALTH_UPGRADE) {
result.health = healthstates.HEALTH_UPGRADE;
}
result.messages.push(
PVE.Utils.get_ceph_icon_html('HEALTH_UPGRADE', true) +
gettext('Other cluster members use a newer version of this service, please upgrade and restart'),
);
}
}
}
result.statuses.push(''); // empty line
result.text = result.statuses.concat(result.messages).join('<br>');
result.health = healthmap[result.health];
me[type][id] = result;
}
}
me.getComponent('mons').updateAll(Object.values(me.mon));
me.getComponent('mgrs').updateAll(Object.values(me.mgr));
me.getComponent('mdss').updateAll(Object.values(me.mds));
},
});
Ext.define('PVE.ceph.ServiceList', {
extend: 'Ext.container.Container',
xtype: 'pveCephServiceList',
style: {
'text-align': 'center',
},
defaults: {
xtype: 'box',
style: {
'text-align': 'center',
},
},
items: [
{
itemId: 'title',
data: {
title: '',
},
tpl: '<h3>{title}</h3>',
},
],
updateAll: function(list) {
var me = this;
me.suspendLayout = true;
list.sort((a, b) => a.id > b.id ? 1 : a.id < b.id ? -1 : 0);
if (!me.ids) {
me.ids = [];
}
let pendingRemoval = {};
me.ids.forEach(id => { pendingRemoval[id] = true; }); // mark all as to-remove first here
for (let i = 0; i < list.length; i++) {
let service = me.getComponent(list[i].id);
if (!service) {
// services and list are sorted, so just insert at i + 1 (first el. is the title)
service = me.insert(i + 1, {
xtype: 'pveCephServiceWidget',
itemId: list[i].id,
});
me.ids.push(list[i].id);
} else {
delete pendingRemoval[list[i].id]; // drop exisiting from for-removal
}
service.updateService(list[i].title, list[i].text, list[i].health);
}
Object.keys(pendingRemoval).forEach(id => me.remove(id)); // GC
me.suspendLayout = false;
me.updateLayout();
},
initComponent: function() {
var me = this;
me.callParent();
me.getComponent('title').update({
title: me.title,
});
},
});
Ext.define('PVE.ceph.ServiceWidget', {
extend: 'Ext.Component',
alias: 'widget.pveCephServiceWidget',
userCls: 'monitor inline-block',
data: {
title: '0',
health: 'HEALTH_ERR',
text: '',
iconCls: PVE.Utils.get_health_icon(),
},
tpl: [
'{title}: ',
'<i class="fa fa-fw {iconCls}"></i>',
],
updateService: function(title, text, health) {
var me = this;
me.update(Ext.apply(me.data, {
health: health,
text: text,
title: title,
iconCls: PVE.Utils.get_health_icon(PVE.Utils.map_ceph_health[health]),
}));
if (me.tooltip) {
me.tooltip.setHtml(text);
}
},
listeners: {
destroy: function() {
let me = this;
if (me.tooltip) {
me.tooltip.destroy();
delete me.tooltip;
}
},
mouseenter: {
element: 'el',
fn: function(events, element) {
let view = this.component;
if (!view) {
return;
}
if (!view.tooltip || view.data.text !== view.tooltip.html) {
view.tooltip = Ext.create('Ext.tip.ToolTip', {
target: view.el,
trackMouse: true,
dismissDelay: 0,
renderTo: Ext.getBody(),
html: view.data.text,
});
}
view.tooltip.show();
},
},
mouseleave: {
element: 'el',
fn: function(events, element) {
let view = this.component;
if (view.tooltip) {
view.tooltip.destroy();
delete view.tooltip;
}
},
},
},
});