[pve-devel] [PATCH manager 07/15] gui: ceph: add Services.js
Dominik Csapak
d.csapak at proxmox.com
Mon May 27 14:13:58 CEST 2019
Used for a new Panel in ceph dashboard, shows the ceph services
managed by us (mon,mgr,mds)
some code is copied from 'ceph/StatusDetail.js' and is not necessary there
anymore
Signed-off-by: Dominik Csapak <d.csapak at proxmox.com>
---
www/manager6/Makefile | 1 +
www/manager6/Utils.js | 15 ++
www/manager6/ceph/Services.js | 374 ++++++++++++++++++++++++++++++++++++++++++
3 files changed, 390 insertions(+)
create mode 100644 www/manager6/ceph/Services.js
diff --git a/www/manager6/Makefile b/www/manager6/Makefile
index 853fcb4f..ac43053e 100644
--- a/www/manager6/Makefile
+++ b/www/manager6/Makefile
@@ -103,6 +103,7 @@ JSSRC= \
ceph/Crush.js \
ceph/Status.js \
ceph/StatusDetail.js \
+ ceph/Services.js \
ceph/Config.js \
ceph/Log.js \
ceph/CephInstallWizard.js \
diff --git a/www/manager6/Utils.js b/www/manager6/Utils.js
index a159445b..b3a28400 100644
--- a/www/manager6/Utils.js
+++ b/www/manager6/Utils.js
@@ -102,6 +102,21 @@ Ext.define('PVE.Utils', { utilities: {
return icon;
},
+ parse_ceph_version: function(service) {
+ if (service.ceph_version_short) {
+ return service.ceph_version_short;
+ }
+
+ if (service.ceph_version) {
+ var match = service.ceph_version.match(/version (\d+\.\d+\.\d+)/);
+ if (match) {
+ return match[1];
+ }
+ }
+
+ return undefined;
+ },
+
get_ceph_icon_html: function(health, fw) {
var state = PVE.Utils.map_ceph_health[health];
var cls = PVE.Utils.get_health_icon(state);
diff --git a/www/manager6/ceph/Services.js b/www/manager6/ceph/Services.js
new file mode 100644
index 00000000..d049a307
--- /dev/null
+++ b/www/manager6/ceph/Services.js
@@ -0,0 +1,374 @@
+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;
+
+ var healthstates = {
+ 'HEALTH_UNKNOWN': 0,
+ 'HEALTH_ERR': 1,
+ 'HEALTH_WARN': 2,
+ 'HEALTH_OLD': 3,
+ 'HEALTH_OK': 4
+ };
+ var healthmap = [
+ 'HEALTH_UNKNOWN',
+ 'HEALTH_ERR',
+ 'HEALTH_WARN',
+ 'HEALTH_OLD',
+ 'HEALTH_OK'
+ ];
+ var reduceFn = function(first, second) {
+ return first + '\n' + second.message;
+ };
+ var services = ['mon','mgr','mds'];
+ var maxversion = "00.0.00";
+ Object.values(metadata.version || {}).forEach(function(version) {
+ if (version > maxversion) {
+ maxversion = version;
+ }
+ });
+ var i;
+ var quorummap = (status && status.quorum_names) ? status.quorum_names : [];
+ var monmessages = {};
+ var mgrmessages = {};
+ var mdsmessages = {};
+ if (status) {
+ if (status.health) {
+ Ext.Object.each(status.health.checks, function(key, value, obj) {
+ if (!Ext.String.startsWith(key, "MON_")) {
+ return;
+ }
+
+ var match = value.detail[0].message.match(/mon.([a-zA-Z0-9\-\.]+)/);
+ if (!match) {
+ return;
+ }
+ var monid = match[1];
+
+ if (!monmessages[monid]) {
+ monmessages[monid] = {
+ worstSeverity: healthstates.HEALTH_OK,
+ messages: []
+ };
+ }
+
+
+ monmessages[monid].messages.push(
+ PVE.Utils.get_ceph_icon_html(value.severity, true) +
+ Ext.Array.reduce(value.detail, reduceFn, '')
+ );
+ 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;
+ });
+ }
+ }
+
+ var 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 (i = 0; i < services.length; i++) {
+ var type = services[i];
+ var ids = Object.keys(metadata[type] || {});
+ me[type] = {};
+
+ var j;
+ for (j = 0; j < ids.length; j++) {
+ var id = ids[j];
+ var tmp = id.split('@');
+ var name = tmp[0];
+ var host = tmp[1];
+ var 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') + ": " + result.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 (result.health > healthstates.HEALTH_OLD) {
+ result.health = healthstates.HEALTH_OLD;
+ }
+ result.messages.push(
+ PVE.Utils.get_ceph_icon_html('HEALTH_OLD', true) +
+ gettext('Not Current Version, please upgrade')
+ );
+ }
+ }
+
+ 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;
+
+ var i;
+ list.sort(function(a,b) {
+ return a.id > b.id ? 1 : a.id < b.id ? -1 : 0;
+ });
+ var ids = {};
+ if (me.ids) {
+ me.ids.forEach(function(id) {
+ ids[id] = true;
+ });
+ }
+ for (i = 0; i < list.length; i++) {
+ var service = me.getComponent(list[i].id);
+ if (!service) {
+ // since services are already sorted, and
+ // we always have a sorted list
+ // we can add it at the service+1 position (because of the title)
+ service = me.insert(i+1, {
+ xtype: 'pveCephServiceWidget',
+ itemId: list[i].id
+ });
+ if (!me.ids) {
+ me.ids = [];
+ }
+ me.ids.push(list[i].id);
+ } else {
+ delete ids[list[i].id];
+ }
+ service.updateService(list[i].title, list[i].text, list[i].health);
+ }
+
+ Object.keys(ids).forEach(function(id) {
+ me.remove(id);
+ });
+ me.suspendLayout = false;
+ me.updateLayout();
+ },
+
+ initComponent: function() {
+ var me = this;
+ me.callParent();
+ me.getComponent('title').update({
+ title: me.title
+ });
+ }
+});
+
+/*jslint confusion: true*/
+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() {
+ var me = this;
+ if (me.tooltip) {
+ me.tooltip.destroy();
+ delete me.tooltip;
+ }
+ },
+ 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,
+ dismissDelay: 0,
+ renderTo: Ext.getBody(),
+ html: me.data.text
+ });
+ }
+ me.tooltip.show();
+ }
+ },
+ mouseleave: {
+ element: 'el',
+ fn: function(events, element) {
+ var me = this.component;
+ if (me.tooltip) {
+ me.tooltip.destroy();
+ delete me.tooltip;
+ }
+ }
+ }
+ }
+});
--
2.11.0
More information about the pve-devel
mailing list