]> git.proxmox.com Git - pve-manager.git/blob - www/manager6/ceph/StatusDetail.js
rename 'monitors' to 'states' in pg list
[pve-manager.git] / www / manager6 / ceph / StatusDetail.js
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 states: []
77 },
78 tpl: [
79 '<h3>' + gettext('PGs') + '</h3>',
80 '<tpl for="states">',
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 ||
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]) {
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({states: 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+) osds down/;
131 Ext.Array.some(record.data.health.summary, function(item) {
132 var found = item.summary.match(downinregex);
133
134 if (found !== null) {
135 downin_osds = parseInt(found[1],10);
136 return true;
137 }
138
139 return false;
140 });
141
142 var downout_osds = down_osds - downin_osds;
143 var upin_osds = in_osds - downin_osds;
144 var upout_osds = up_osds - upin_osds;
145 var osds = {
146 total: total_osds,
147 upin: upin_osds,
148 upout: upout_osds,
149 downin: downin_osds,
150 downout: downout_osds
151 };
152 me.getComponent('osds').update(osds);
153
154 // update the monitors
155 var mons = record.data.monmap.mons.sort(function(a,b) {
156 return (a.name < b.name)?-1:(a.name > b.name)?1:0;
157 });
158
159 var monTimes = record.data.health.timechecks.mons || [];
160 var monHealth = record.data.health.health.health_services[0].mons || [];
161 var timechecks = {};
162 var healthchecks = {};
163 var monContainer = me.getComponent('monitors');
164 var i;
165 for (i = 0; i < mons.length && i < monTimes.length; i++) {
166 timechecks[monTimes[i].name] = monTimes[i].health;
167 }
168
169 if (mons.length === 1) {
170 timechecks[mons[0].name] = "HEALTH_OK";
171 }
172
173 for (i = 0; i < mons.length && i < monHealth.length; i++) {
174 healthchecks[monHealth[i].name] = monHealth[i].health;
175 }
176
177 for (i = 0; i < mons.length; i++) {
178 var monitor = monContainer.getComponent('mon.' + mons[i].name);
179 if (!monitor) {
180 // since mons are already sorted, and
181 // we always have a sorted list
182 // we can add it at the mons+1 position (because of the title)
183 monitor = monContainer.insert(i+1, {
184 xtype: 'pveCephMonitorWidget',
185 itemId: 'mon.' + mons[i].name
186 });
187 }
188 monitor.updateMonitor(timechecks[mons[i].name], mons[i], record.data.quorum_names, healthchecks[mons[i].name]);
189 }
190 me.suspendLayout = false;
191 me.updateLayout();
192 }
193 });
194
195 Ext.define('PVE.ceph.MonitorWidget', {
196 extend: 'Ext.Component',
197 alias: 'widget.pveCephMonitorWidget',
198
199 userCls: 'monitor inline-block',
200 data: {
201 name: '0',
202 health: 'HEALTH_ERR',
203 iconCls: PVE.Utils.get_health_icon(),
204 addr: ''
205 },
206
207 tpl: [
208 '{name}: ',
209 '<i class="fa fa-fw {iconCls}"></i>'
210 ],
211
212 // expects 3 variables which are
213 // timestate: the status from timechecks.mons
214 // data: the monmap.mons data
215 // quorum_names: the quorum_names array
216 updateMonitor: function(timestate, data, quorum_names, health) {
217 var me = this;
218 var state = 'HEALTH_ERR';
219 var healthstates = {
220 'HEALTH_OK': 3,
221 'HEALTH_WARN': 2,
222 'HEALTH_ERR': 1
223 };
224
225 // if the monitor is part of the quorum
226 // and has a timestate, get the timestate,
227 // otherwise the state is ERR
228 if (timestate && health && quorum_names &&
229 quorum_names.indexOf(data.name) !== -1) {
230 state = (healthstates[health] < healthstates[timestate])?
231 health : timestate;
232 }
233
234 me.update(Ext.apply(me.data, {
235 health: state,
236 addr: data.addr,
237 name: data.name,
238 iconCls: PVE.Utils.get_health_icon(PVE.Utils.map_ceph_health[state])
239 }));
240 },
241
242 listeners: {
243 mouseenter: {
244 element: 'el',
245 fn: function(events, element) {
246 var me = this.component;
247 if (!me) {
248 return;
249 }
250 if (!me.tooltip) {
251 me.tooltip = Ext.create('Ext.tip.ToolTip', {
252 target: me.el,
253 trackMouse: true,
254 renderTo: Ext.getBody(),
255 html: gettext('Monitor') + ': ' + me.data.name + '<br />' +
256 gettext('Address') + ': ' + me.data.addr + '<br />' +
257 gettext('Health') + ': ' + me.data.health
258 });
259 }
260 me.tooltip.show();
261 }
262 },
263 mouseleave: {
264 element: 'el',
265 fn: function(events, element) {
266 var me = this.component;
267 if (me.tooltip) {
268 me.tooltip.hide();
269 }
270 }
271 }
272 }
273 });