]>
Commit | Line | Data |
---|---|---|
195c7c8c DC |
1 | Ext.define('PVE.ceph.StatusDetail', { |
2 | extend: 'Ext.panel.Panel', | |
3 | alias: 'widget.pveCephStatusDetail', | |
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 | flex: 1, | |
20 | itemId: 'monitors', | |
21 | xtype: 'container', | |
22 | items: [ | |
23 | { | |
24 | xtype: 'box', | |
25 | width: '100%', | |
26 | html: '<h3>' + gettext('Monitors') + '</h3>' | |
27 | } | |
28 | ] | |
29 | },{ | |
30 | flex: 1, | |
31 | itemId: 'osds', | |
32 | data: { | |
33 | total: 0, | |
34 | upin: 0, | |
35 | upout: 0, | |
36 | downin: 0, | |
37 | downout: 0 | |
38 | }, | |
39 | tpl: [ | |
40 | '<h3>' + gettext('OSDs') + '</h3>', | |
41 | '<table class="osds">', | |
42 | '<tr><td></td>', | |
43 | '<td><i class="fa fa-fw good fa-circle"></i>', | |
44 | gettext('In'), | |
45 | '</td>', | |
46 | '<td><i class="fa fa-fw warning fa-circle-o"></i>', | |
47 | gettext('Out'), | |
48 | '</td>', | |
49 | '</tr>', | |
50 | '<tr>', | |
51 | '<td><i class="fa fa-fw good fa-arrow-circle-up"></i>', | |
52 | gettext('Up'), | |
53 | '</td>', | |
54 | '<td>{upin}</td>', | |
55 | '<td>{upout}</td>', | |
56 | '</tr>', | |
57 | '<tr>', | |
58 | '<td><i class="fa fa-fw critical fa-arrow-circle-down"></i>', | |
59 | gettext('Down'), | |
60 | '</td>', | |
61 | '<td>{downin}</td>', | |
62 | '<td>{downout}</td>', | |
63 | '</tr>', | |
64 | '</table>', | |
65 | '<br /><div>', | |
66 | gettext('Total'), | |
67 | ': {total}', | |
68 | '</div>' | |
69 | ] | |
70 | }, | |
71 | { | |
72 | flex: 1.6, | |
73 | itemId: 'pgs', | |
74 | padding: '0 10', | |
75 | data: { | |
76 | monitors: [] | |
77 | }, | |
78 | tpl: [ | |
79 | '<h3>' + gettext('PGs') + '</h3>', | |
80 | '<tpl for="monitors">', | |
81 | '<div class="left-aligned">{state_name}:</div>', | |
82 | '<div class="right-aligned">{count}</div><br />', | |
83 | '<div style="clear:both"></div>', | |
84 | '</tpl>' | |
85 | ] | |
86 | }], | |
87 | ||
88 | updateAll: function(record) { | |
89 | var me = this; | |
90 | me.suspendLayout = true; | |
91 | ||
92 | if (!record.data.pgmap || | |
93 | !record.data.osdmap || | |
94 | !record.data.osdmap.osdmap || | |
95 | !record.data.health || | |
96 | !record.data.health.timechecks || | |
97 | !record.data.monmap || | |
f3f73cde DC |
98 | !record.data.monmap.mons || |
99 | !record.data.health.health || | |
100 | !record.data.health.health.health_services || | |
101 | !record.data.health.health.health_services[0]) { | |
195c7c8c DC |
102 | // only continue if we have all the data |
103 | return; | |
104 | } | |
105 | ||
106 | // update pgs sorted | |
107 | var pgs_by_state = record.data.pgmap.pgs_by_state || []; | |
108 | pgs_by_state.sort(function(a,b){ | |
109 | return (a.state_name < b.state_name)?-1:(a.state_name === b.state_name)?0:1; | |
110 | }); | |
111 | me.getComponent('pgs').update({monitors: pgs_by_state}); | |
112 | ||
113 | // update osds counts | |
114 | // caution: this code is not the nicest, | |
115 | // but since the status call only gives us | |
116 | // the total, up and in value, | |
117 | // we parse the health summary and look for the | |
118 | // x/y in osds are down message | |
119 | // to get the rest of the numbers | |
120 | // | |
121 | // the alternative would be to make a second api call, | |
122 | // as soon as not all osds are up, but those are costly | |
123 | ||
124 | var total_osds = record.data.osdmap.osdmap.num_osds || 0; | |
125 | var in_osds = record.data.osdmap.osdmap.num_in_osds || 0; | |
126 | var up_osds = record.data.osdmap.osdmap.num_up_osds || 0; | |
127 | var out_osds = total_osds - in_osds; | |
128 | var down_osds = total_osds - up_osds; | |
129 | var downin_osds = 0; | |
130 | var downinregex = /(\d+)\/(\d+) in osds are down/; | |
131 | Ext.Array.some(record.data.health.summary, function(item) { | |
132 | var found = item.summary.match(downinregex); | |
133 | ||
134 | if (found !== null) { | |
135 | // sanity check, test if the message is | |
136 | // consistent with the direct value | |
137 | // for in osds | |
138 | if (found[2] == in_osds) { | |
139 | downin_osds = parseInt(found[1],10); | |
140 | return true; | |
141 | } | |
142 | } | |
143 | ||
144 | return false; | |
145 | }); | |
146 | ||
147 | var downout_osds = down_osds - downin_osds; | |
148 | var upin_osds = in_osds - downin_osds; | |
149 | var upout_osds = up_osds - upin_osds; | |
150 | var osds = { | |
151 | total: total_osds, | |
152 | upin: upin_osds, | |
153 | upout: upout_osds, | |
154 | downin: downin_osds, | |
155 | downout: downout_osds | |
156 | }; | |
157 | me.getComponent('osds').update(osds); | |
158 | ||
159 | // update the monitors | |
160 | var mons = record.data.monmap.mons.sort(function(a,b) { | |
161 | return (a.name < b.name)?-1:(a.name > b.name)?1:0; | |
162 | }); | |
163 | ||
5c38487d | 164 | var monTimes = record.data.health.timechecks.mons || []; |
f3f73cde | 165 | var monHealth = record.data.health.health.health_services[0].mons || []; |
195c7c8c | 166 | var timechecks = {}; |
f3f73cde | 167 | var healthchecks = {}; |
195c7c8c DC |
168 | var monContainer = me.getComponent('monitors'); |
169 | var i; | |
170 | for (i = 0; i < mons.length && i < monTimes.length; i++) { | |
171 | timechecks[monTimes[i].name] = monTimes[i].health; | |
172 | } | |
173 | ||
bd8950b0 DC |
174 | if (mons.length === 1) { |
175 | timechecks[mons[0].name] = "HEALTH_OK"; | |
176 | } | |
177 | ||
f3f73cde DC |
178 | for (i = 0; i < mons.length && i < monHealth.length; i++) { |
179 | healthchecks[monHealth[i].name] = monHealth[i].health; | |
180 | } | |
181 | ||
195c7c8c DC |
182 | for (i = 0; i < mons.length; i++) { |
183 | var monitor = monContainer.getComponent('mon.' + mons[i].name); | |
184 | if (!monitor) { | |
185 | // since mons are already sorted, and | |
186 | // we always have a sorted list | |
187 | // we can add it at the mons+1 position (because of the title) | |
188 | monitor = monContainer.insert(i+1, { | |
189 | xtype: 'pveCephMonitorWidget', | |
190 | itemId: 'mon.' + mons[i].name | |
191 | }); | |
192 | } | |
f3f73cde | 193 | monitor.updateMonitor(timechecks[mons[i].name], mons[i], record.data.quorum_names, healthchecks[mons[i].name]); |
195c7c8c DC |
194 | } |
195 | me.suspendLayout = false; | |
196 | me.updateLayout(); | |
197 | } | |
198 | }); | |
199 | ||
200 | Ext.define('PVE.ceph.MonitorWidget', { | |
201 | extend: 'Ext.Component', | |
202 | alias: 'widget.pveCephMonitorWidget', | |
203 | ||
204 | userCls: 'monitor inline-block', | |
205 | data: { | |
206 | name: '0', | |
207 | health: 'HEALTH_ERR', | |
208 | iconCls: PVE.Utils.get_health_icon(), | |
209 | addr: '' | |
210 | }, | |
211 | ||
212 | tpl: [ | |
213 | '{name}: ', | |
214 | '<i class="fa fa-fw {iconCls}"></i>' | |
215 | ], | |
216 | ||
217 | // expects 3 variables which are | |
218 | // timestate: the status from timechecks.mons | |
219 | // data: the monmap.mons data | |
220 | // quorum_names: the quorum_names array | |
f3f73cde | 221 | updateMonitor: function(timestate, data, quorum_names, health) { |
195c7c8c DC |
222 | var me = this; |
223 | var state = 'HEALTH_ERR'; | |
f3f73cde DC |
224 | var healthstates = { |
225 | 'HEALTH_OK': 3, | |
226 | 'HEALTH_WARN': 2, | |
227 | 'HEALTH_ERR': 1 | |
228 | }; | |
195c7c8c DC |
229 | |
230 | // if the monitor is part of the quorum | |
231 | // and has a timestate, get the timestate, | |
232 | // otherwise the state is ERR | |
f3f73cde | 233 | if (timestate && health && quorum_names && |
195c7c8c | 234 | quorum_names.indexOf(data.name) !== -1) { |
f3f73cde DC |
235 | state = (healthstates[health] < healthstates[timestate])? |
236 | health : timestate; | |
195c7c8c DC |
237 | } |
238 | ||
239 | me.update(Ext.apply(me.data, { | |
240 | health: state, | |
241 | addr: data.addr, | |
242 | name: data.name, | |
243 | iconCls: PVE.Utils.get_health_icon(PVE.Utils.map_ceph_health[state]) | |
244 | })); | |
245 | }, | |
246 | ||
247 | listeners: { | |
248 | mouseenter: { | |
249 | element: 'el', | |
250 | fn: function(events, element) { | |
251 | var me = this.component; | |
252 | if (!me) { | |
253 | return; | |
254 | } | |
255 | if (!me.tooltip) { | |
256 | me.tooltip = Ext.create('Ext.tip.ToolTip', { | |
257 | target: me.el, | |
258 | trackMouse: true, | |
259 | renderTo: Ext.getBody(), | |
260 | html: gettext('Monitor') + ': ' + me.data.name + '<br />' + | |
261 | gettext('Address') + ': ' + me.data.addr + '<br />' + | |
262 | gettext('Health') + ': ' + me.data.health | |
263 | }); | |
264 | } | |
265 | me.tooltip.show(); | |
266 | } | |
267 | }, | |
268 | mouseleave: { | |
269 | element: 'el', | |
270 | fn: function(events, element) { | |
271 | var me = this.component; | |
272 | if (me.tooltip) { | |
273 | me.tooltip.hide(); | |
274 | } | |
275 | } | |
276 | } | |
277 | } | |
278 | }); |