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