]> git.proxmox.com Git - pve-manager.git/commitdiff
gui: ceph: add Services.js
authorDominik Csapak <d.csapak@proxmox.com>
Mon, 27 May 2019 12:13:58 +0000 (14:13 +0200)
committerThomas Lamprecht <t.lamprecht@proxmox.com>
Mon, 27 May 2019 14:17:29 +0000 (16:17 +0200)
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@proxmox.com>
www/manager6/Makefile
www/manager6/Utils.js
www/manager6/ceph/Services.js [new file with mode: 0644]

index 853fcb4fb3fd2ea028ceb99e08cca0e78295981e..ac43053e001bff2acaa42841cf51e4eada963b65 100644 (file)
@@ -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                               \
index a159445bc7e8bc384b3cc955449285fb38bc2d8f..b3a2840062ba76f82409854a300c7c19d71a06ff 100644 (file)
@@ -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 (file)
index 0000000..d049a30
--- /dev/null
@@ -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;
+               }
+           }
+       }
+    }
+});