]> git.proxmox.com Git - pve-manager.git/blob - www/manager6/ceph/Services.js
gui: ceph: add HEALTH_UPGRADE state
[pve-manager.git] / www / manager6 / ceph / Services.js
1 Ext.define('PVE.ceph.Services', {
2 extend: 'Ext.panel.Panel',
3 alias: 'widget.pveCephServices',
4
5 layout: {
6 type: 'hbox',
7 align: 'stretch'
8 },
9
10 bodyPadding: '0 5 20',
11 defaults: {
12 xtype: 'box',
13 style: {
14 'text-align':'center'
15 }
16 },
17
18 items: [
19 {
20 flex: 1,
21 xtype: 'pveCephServiceList',
22 itemId: 'mons',
23 title: gettext('Monitors')
24 },
25 {
26 flex: 1,
27 xtype: 'pveCephServiceList',
28 itemId: 'mgrs',
29 title: gettext('Managers')
30 },
31 {
32 flex: 1,
33 xtype: 'pveCephServiceList',
34 itemId: 'mdss',
35 title: gettext('Meta Data Servers')
36 }
37 ],
38
39 updateAll: function(metadata, status) {
40 var me = this;
41
42 var healthstates = {
43 'HEALTH_UNKNOWN': 0,
44 'HEALTH_ERR': 1,
45 'HEALTH_WARN': 2,
46 'HEALTH_UPGRADE': 3,
47 'HEALTH_OLD': 4,
48 'HEALTH_OK': 5
49 };
50 var healthmap = [
51 'HEALTH_UNKNOWN',
52 'HEALTH_ERR',
53 'HEALTH_WARN',
54 'HEALTH_UPGRADE',
55 'HEALTH_OLD',
56 'HEALTH_OK'
57 ];
58 var reduceFn = function(first, second) {
59 return first + '\n' + second.message;
60 };
61 var services = ['mon','mgr','mds'];
62 var maxversion = "00.0.00";
63 Object.values(metadata.version || {}).forEach(function(version) {
64 if (PVE.Utils.compare_ceph_versions(version, maxversion) > 0) {
65 maxversion = version;
66 }
67 });
68 var i;
69 var quorummap = (status && status.quorum_names) ? status.quorum_names : [];
70 var monmessages = {};
71 var mgrmessages = {};
72 var mdsmessages = {};
73 if (status) {
74 if (status.health) {
75 Ext.Object.each(status.health.checks, function(key, value, obj) {
76 if (!Ext.String.startsWith(key, "MON_")) {
77 return;
78 }
79
80 var i;
81 for (i = 0; i < value.detail.length; i++) {
82 var match = value.detail[i].message.match(/mon.([a-zA-Z0-9\-\.]+)/);
83 if (!match) {
84 continue;
85 }
86 var monid = match[1];
87
88 if (!monmessages[monid]) {
89 monmessages[monid] = {
90 worstSeverity: healthstates.HEALTH_OK,
91 messages: []
92 };
93 }
94
95
96 monmessages[monid].messages.push(
97 PVE.Utils.get_ceph_icon_html(value.severity, true) +
98 Ext.Array.reduce(value.detail, reduceFn, '')
99 );
100 if (healthstates[value.severity] < monmessages[monid].worstSeverity) {
101 monmessages[monid].worstSeverity = healthstates[value.severity];
102 }
103 }
104 });
105 }
106
107 if (status.mgrmap) {
108 mgrmessages[status.mgrmap.active_name] = "active";
109 status.mgrmap.standbys.forEach(function(mgr) {
110 mgrmessages[mgr.name] = "standby";
111 });
112 }
113
114 if (status.fsmap) {
115 status.fsmap.by_rank.forEach(function(mds) {
116 mdsmessages[mds.name] = 'rank: ' + mds.rank + "; " + mds.status;
117 });
118 }
119 }
120
121 var checks = {
122 mon: function(mon) {
123 if (quorummap.indexOf(mon.name) !== -1) {
124 mon.health = healthstates.HEALTH_OK;
125 } else {
126 mon.health = healthstates.HEALTH_ERR;
127 }
128 if (monmessages[mon.name]) {
129 if (monmessages[mon.name].worstSeverity < mon.health) {
130 mon.health = monmessages[mon.name].worstSeverity;
131 }
132 Array.prototype.push.apply(mon.messages, monmessages[mon.name].messages);
133 }
134 return mon;
135 },
136 mgr: function(mgr) {
137 if (mgrmessages[mgr.name] === 'active') {
138 mgr.title = '<b>' + mgr.title + '</b>';
139 mgr.statuses.push(gettext('Status') + ': <b>active</b>');
140 } else if (mgrmessages[mgr.name] === 'standby') {
141 mgr.statuses.push(gettext('Status') + ': standby');
142 } else if (mgr.health > healthstates.HEALTH_WARN) {
143 mgr.health = healthstates.HEALTH_WARN;
144 }
145
146 return mgr;
147 },
148 mds: function(mds) {
149 if (mdsmessages[mds.name]) {
150 mds.title = '<b>' + mds.title + '</b>';
151 mds.statuses.push(gettext('Status') + ': <b>' + mdsmessages[mds.name]+"</b>");
152 } else if (mds.addr !== Proxmox.Utils.unknownText) {
153 mds.statuses.push(gettext('Status') + ': standby');
154 }
155
156 return mds;
157 }
158 };
159
160 for (i = 0; i < services.length; i++) {
161 var type = services[i];
162 var ids = Object.keys(metadata[type] || {});
163 me[type] = {};
164
165 var j;
166 for (j = 0; j < ids.length; j++) {
167 var id = ids[j];
168 var tmp = id.split('@');
169 var name = tmp[0];
170 var host = tmp[1];
171 var result = {
172 id: id,
173 health: healthstates.HEALTH_OK,
174 statuses: [],
175 messages: [],
176 name: name,
177 title: metadata[type][id].name || name,
178 host: host,
179 version: PVE.Utils.parse_ceph_version(metadata[type][id]),
180 service: metadata[type][id].service,
181 addr: metadata[type][id].addr || metadata[type][id].addrs || Proxmox.Utils.unknownText
182 };
183
184 result.statuses = [
185 gettext('Host') + ": " + result.host,
186 gettext('Address') + ": " + result.addr
187 ];
188
189 if (checks[type]) {
190 result = checks[type](result);
191 }
192
193 if (result.service && !result.version) {
194 result.messages.push(
195 PVE.Utils.get_ceph_icon_html('HEALTH_UNKNOWN', true) +
196 gettext('Stopped')
197 );
198 result.health = healthstates.HEALTH_UNKNOWN;
199 }
200
201 if (!result.version && result.addr === Proxmox.Utils.unknownText) {
202 result.health = healthstates.HEALTH_UNKNOWN;
203 }
204
205 if (result.version) {
206 result.statuses.push(gettext('Version') + ": " + result.version);
207
208 if (result.version != maxversion) {
209 if (result.health > healthstates.HEALTH_OLD) {
210 result.health = healthstates.HEALTH_OLD;
211 }
212 result.messages.push(
213 PVE.Utils.get_ceph_icon_html('HEALTH_OLD', true) +
214 gettext('Not Current Version, please upgrade')
215 );
216 }
217 }
218
219 result.statuses.push(''); // empty line
220 result.text = result.statuses.concat(result.messages).join('<br>');
221
222 result.health = healthmap[result.health];
223
224 me[type][id] = result;
225 }
226 }
227
228 me.getComponent('mons').updateAll(Object.values(me.mon));
229 me.getComponent('mgrs').updateAll(Object.values(me.mgr));
230 me.getComponent('mdss').updateAll(Object.values(me.mds));
231 }
232 });
233
234 Ext.define('PVE.ceph.ServiceList', {
235 extend: 'Ext.container.Container',
236 xtype: 'pveCephServiceList',
237
238 style: {
239 'text-align':'center'
240 },
241 defaults: {
242 xtype: 'box',
243 style: {
244 'text-align':'center'
245 }
246 },
247
248 items: [
249 {
250 itemId: 'title',
251 data: {
252 title: ''
253 },
254 tpl: '<h3>{title}</h3>'
255 }
256 ],
257
258 updateAll: function(list) {
259 var me = this;
260 me.suspendLayout = true;
261
262 var i;
263 list.sort(function(a,b) {
264 return a.id > b.id ? 1 : a.id < b.id ? -1 : 0;
265 });
266 var ids = {};
267 if (me.ids) {
268 me.ids.forEach(function(id) {
269 ids[id] = true;
270 });
271 }
272 for (i = 0; i < list.length; i++) {
273 var service = me.getComponent(list[i].id);
274 if (!service) {
275 // since services are already sorted, and
276 // we always have a sorted list
277 // we can add it at the service+1 position (because of the title)
278 service = me.insert(i+1, {
279 xtype: 'pveCephServiceWidget',
280 itemId: list[i].id
281 });
282 if (!me.ids) {
283 me.ids = [];
284 }
285 me.ids.push(list[i].id);
286 } else {
287 delete ids[list[i].id];
288 }
289 service.updateService(list[i].title, list[i].text, list[i].health);
290 }
291
292 Object.keys(ids).forEach(function(id) {
293 me.remove(id);
294 });
295 me.suspendLayout = false;
296 me.updateLayout();
297 },
298
299 initComponent: function() {
300 var me = this;
301 me.callParent();
302 me.getComponent('title').update({
303 title: me.title
304 });
305 }
306 });
307
308 /*jslint confusion: true*/
309 Ext.define('PVE.ceph.ServiceWidget', {
310 extend: 'Ext.Component',
311 alias: 'widget.pveCephServiceWidget',
312
313 userCls: 'monitor inline-block',
314 data: {
315 title: '0',
316 health: 'HEALTH_ERR',
317 text: '',
318 iconCls: PVE.Utils.get_health_icon()
319 },
320
321 tpl: [
322 '{title}: ',
323 '<i class="fa fa-fw {iconCls}"></i>'
324 ],
325
326 updateService: function(title, text, health) {
327 var me = this;
328
329 me.update(Ext.apply(me.data, {
330 health: health,
331 text: text,
332 title: title,
333 iconCls: PVE.Utils.get_health_icon(PVE.Utils.map_ceph_health[health])
334 }));
335
336 if (me.tooltip) {
337 me.tooltip.setHtml(text);
338 }
339 },
340
341 listeners: {
342 destroy: function() {
343 var me = this;
344 if (me.tooltip) {
345 me.tooltip.destroy();
346 delete me.tooltip;
347 }
348 },
349 mouseenter: {
350 element: 'el',
351 fn: function(events, element) {
352 var me = this.component;
353 if (!me) {
354 return;
355 }
356 if (!me.tooltip) {
357 me.tooltip = Ext.create('Ext.tip.ToolTip', {
358 target: me.el,
359 trackMouse: true,
360 dismissDelay: 0,
361 renderTo: Ext.getBody(),
362 html: me.data.text
363 });
364 }
365 me.tooltip.show();
366 }
367 },
368 mouseleave: {
369 element: 'el',
370 fn: function(events, element) {
371 var me = this.component;
372 if (me.tooltip) {
373 me.tooltip.destroy();
374 delete me.tooltip;
375 }
376 }
377 }
378 }
379 });