]> git.proxmox.com Git - pve-manager.git/blob - www/manager6/ceph/StatusDetail.js
fix missing mons array in ceph status output
[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 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 ||
98 !record.data.monmap.mons) {
99 // only continue if we have all the data
100 return;
101 }
102
103 // update pgs sorted
104 var pgs_by_state = record.data.pgmap.pgs_by_state || [];
105 pgs_by_state.sort(function(a,b){
106 return (a.state_name < b.state_name)?-1:(a.state_name === b.state_name)?0:1;
107 });
108 me.getComponent('pgs').update({monitors: pgs_by_state});
109
110 // update osds counts
111 // caution: this code is not the nicest,
112 // but since the status call only gives us
113 // the total, up and in value,
114 // we parse the health summary and look for the
115 // x/y in osds are down message
116 // to get the rest of the numbers
117 //
118 // the alternative would be to make a second api call,
119 // as soon as not all osds are up, but those are costly
120
121 var total_osds = record.data.osdmap.osdmap.num_osds || 0;
122 var in_osds = record.data.osdmap.osdmap.num_in_osds || 0;
123 var up_osds = record.data.osdmap.osdmap.num_up_osds || 0;
124 var out_osds = total_osds - in_osds;
125 var down_osds = total_osds - up_osds;
126 var downin_osds = 0;
127 var downinregex = /(\d+)\/(\d+) in osds are down/;
128 Ext.Array.some(record.data.health.summary, function(item) {
129 var found = item.summary.match(downinregex);
130
131 if (found !== null) {
132 // sanity check, test if the message is
133 // consistent with the direct value
134 // for in osds
135 if (found[2] == in_osds) {
136 downin_osds = parseInt(found[1],10);
137 return true;
138 }
139 }
140
141 return false;
142 });
143
144 var downout_osds = down_osds - downin_osds;
145 var upin_osds = in_osds - downin_osds;
146 var upout_osds = up_osds - upin_osds;
147 var osds = {
148 total: total_osds,
149 upin: upin_osds,
150 upout: upout_osds,
151 downin: downin_osds,
152 downout: downout_osds
153 };
154 me.getComponent('osds').update(osds);
155
156 // update the monitors
157 var mons = record.data.monmap.mons.sort(function(a,b) {
158 return (a.name < b.name)?-1:(a.name > b.name)?1:0;
159 });
160
161 var monTimes = record.data.health.timechecks.mons || [];
162 var timechecks = {};
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 for (i = 0; i < mons.length; i++) {
170 var monitor = monContainer.getComponent('mon.' + mons[i].name);
171 if (!monitor) {
172 // since mons are already sorted, and
173 // we always have a sorted list
174 // we can add it at the mons+1 position (because of the title)
175 monitor = monContainer.insert(i+1, {
176 xtype: 'pveCephMonitorWidget',
177 itemId: 'mon.' + mons[i].name
178 });
179 }
180 monitor.updateMonitor(timechecks[mons[i].name], mons[i], record.data.quorum_names);
181 }
182 me.suspendLayout = false;
183 me.updateLayout();
184 }
185 });
186
187 Ext.define('PVE.ceph.MonitorWidget', {
188 extend: 'Ext.Component',
189 alias: 'widget.pveCephMonitorWidget',
190
191 userCls: 'monitor inline-block',
192 data: {
193 name: '0',
194 health: 'HEALTH_ERR',
195 iconCls: PVE.Utils.get_health_icon(),
196 addr: ''
197 },
198
199 tpl: [
200 '{name}: ',
201 '<i class="fa fa-fw {iconCls}"></i>'
202 ],
203
204 // expects 3 variables which are
205 // timestate: the status from timechecks.mons
206 // data: the monmap.mons data
207 // quorum_names: the quorum_names array
208 updateMonitor: function(timestate, data, quorum_names) {
209 var me = this;
210 var state = 'HEALTH_ERR';
211
212 // if the monitor is part of the quorum
213 // and has a timestate, get the timestate,
214 // otherwise the state is ERR
215 if (timestate && quorum_names &&
216 quorum_names.indexOf(data.name) !== -1) {
217 state = timestate;
218 }
219
220 me.update(Ext.apply(me.data, {
221 health: state,
222 addr: data.addr,
223 name: data.name,
224 iconCls: PVE.Utils.get_health_icon(PVE.Utils.map_ceph_health[state])
225 }));
226 },
227
228 listeners: {
229 mouseenter: {
230 element: 'el',
231 fn: function(events, element) {
232 var me = this.component;
233 if (!me) {
234 return;
235 }
236 if (!me.tooltip) {
237 me.tooltip = Ext.create('Ext.tip.ToolTip', {
238 target: me.el,
239 trackMouse: true,
240 renderTo: Ext.getBody(),
241 html: gettext('Monitor') + ': ' + me.data.name + '<br />' +
242 gettext('Address') + ': ' + me.data.addr + '<br />' +
243 gettext('Health') + ': ' + me.data.health
244 });
245 }
246 me.tooltip.show();
247 }
248 },
249 mouseleave: {
250 element: 'el',
251 fn: function(events, element) {
252 var me = this.component;
253 if (me.tooltip) {
254 me.tooltip.hide();
255 }
256 }
257 }
258 }
259 });