]> git.proxmox.com Git - proxmox-backup.git/blob - www/Dashboard.js
ui: code cleanup
[proxmox-backup.git] / www / Dashboard.js
1 Ext.define('PBS.Dashboard', {
2 extend: 'Ext.panel.Panel',
3 xtype: 'pbsDashboard',
4
5 controller: {
6 xclass: 'Ext.app.ViewController',
7
8 openDashboardOptions: function() {
9 var me = this;
10 var viewModel = me.getViewModel();
11 Ext.create('Ext.window.Window', {
12 modal: true,
13 width: 300,
14 title: gettext('Dashboard Options'),
15 layout: {
16 type: 'auto',
17 },
18 items: [{
19 xtype: 'form',
20 bodyPadding: '10 10 10 10',
21 defaultButton: 'savebutton',
22 items: [{
23 xtype: 'proxmoxintegerfield',
24 itemId: 'days',
25 labelWidth: 100,
26 anchor: '100%',
27 allowBlank: false,
28 minValue: 1,
29 maxValue: 60,
30 value: viewModel.get('days'),
31 fieldLabel: gettext('Days to show'),
32 }],
33 buttons: [{
34 text: gettext('Save'),
35 reference: 'savebutton',
36 formBind: true,
37 handler: function() {
38 var win = this.up('window');
39 var days = win.down('#days').getValue();
40 me.setDays(days, true);
41 win.close();
42 },
43 }],
44 }],
45 }).show();
46 },
47
48 setDays: function(days, setState) {
49 var me = this;
50 var viewModel = me.getViewModel();
51 viewModel.set('days', days);
52 viewModel.notify();
53
54 viewModel.getStore('tasks').reload();
55
56 if (setState) {
57 var sp = Ext.state.Manager.getProvider();
58 sp.set('dashboard-days', days);
59 }
60 },
61
62
63 updateSubscription: function(store, records, success) {
64 if (!success) { return; }
65 let me = this;
66 let subStatus = records[0].data.status === 'Active' ? 2 : 0; // 2 = all good, 1 = different leves, 0 = none
67 me.lookup('subscription').setSubStatus(subStatus);
68 },
69
70 updateUsageStats: function(store, records, success) {
71 if (!success) {
72 return;
73 }
74 if (records === undefined || records.length < 1) {
75 return;
76 }
77 let me = this;
78 let viewmodel = me.getViewModel();
79
80 let res = records[0].data;
81 viewmodel.set('fingerprint', res.info.fingerprint || Proxmox.Utils.unknownText);
82
83 let cpu = res.cpu,
84 mem = res.memory,
85 root = res.root;
86
87 var cpuPanel = me.lookup('cpu');
88 cpuPanel.updateValue(cpu);
89
90 var memPanel = me.lookup('mem');
91 memPanel.updateValue(mem.used / mem.total);
92
93 var hdPanel = me.lookup('root');
94 hdPanel.updateValue(root.used / root.total);
95 },
96
97 showFingerPrint: function() {
98 let me = this;
99 let vm = me.getViewModel();
100 let fingerprint = vm.get('fingerprint');
101 Ext.create('Ext.window.Window', {
102 modal: true,
103 width: 600,
104 title: gettext('Fingerprint'),
105 layout: 'form',
106 bodyPadding: '10 0',
107 items: [
108 {
109 xtype: 'textfield',
110 inputId: 'fingerprintField',
111 value: fingerprint,
112 editable: false,
113 },
114 ],
115 buttons: [
116 {
117 xtype: 'button',
118 iconCls: 'fa fa-clipboard',
119 handler: function(b) {
120 var el = document.getElementById('fingerprintField');
121 el.select();
122 document.execCommand("copy");
123 },
124 text: gettext('Copy'),
125 },
126 {
127 text: gettext('Ok'),
128 handler: function() {
129 this.up('window').close();
130 },
131 },
132 ],
133 }).show();
134 },
135
136 updateTasks: function(store, records, success) {
137 if (!success) return;
138 let me = this;
139 let viewModel = me.getViewModel();
140
141 records.sort((a, b) => a.data.duration - b.data.duration);
142 let top10 = records.slice(-10);
143 me.lookup('longesttasks').updateTasks(top10);
144
145 let data = {
146 backup: { error: 0, warning: 0, ok: 0 },
147 prune: { error: 0, warning: 0, ok: 0 },
148 garbage_collection: { error: 0, warning: 0, ok: 0 },
149 sync: { error: 0, warning: 0, ok: 0 },
150 verify: { error: 0, warning: 0, ok: 0 },
151 };
152
153 records.forEach(record => {
154 let task = record.data;
155 let type = task.worker_type;
156 if (type === 'syncjob') {
157 type = 'sync';
158 }
159
160 if (type.startsWith('verify')) {
161 type = 'verify';
162 }
163
164 if (data[type] && task.status) {
165 let parsed = Proxmox.Utils.parse_task_status(task.status);
166 data[type][parsed]++;
167 }
168 });
169
170 me.lookup('tasksummary').updateTasks(data, viewModel.get('sinceEpoch'));
171 },
172
173 init: function(view) {
174 var me = this;
175 var sp = Ext.state.Manager.getProvider();
176 var days = sp.get('dashboard-days') || 30;
177 me.setDays(days, false);
178 },
179 },
180
181 viewModel: {
182 data: {
183 fingerprint: "",
184 days: 30,
185 },
186
187 formulas: {
188 disableFPButton: (get) => get('fingerprint') === "",
189 sinceEpoch: (get) => (Date.now()/1000 - get('days') * 24*3600).toFixed(0),
190 },
191
192 stores: {
193 usage: {
194 storeid: 'dash-usage',
195 type: 'update',
196 interval: 3000,
197 autoStart: true,
198 autoLoad: true,
199 autoDestroy: true,
200 proxy: {
201 type: 'proxmox',
202 url: '/api2/json/nodes/localhost/status',
203 },
204 listeners: {
205 load: 'updateUsageStats',
206 },
207 },
208 subscription: {
209 storeid: 'dash-subscription',
210 type: 'update',
211 interval: 10000,
212 autoStart: true,
213 autoLoad: true,
214 autoDestroy: true,
215 proxy: {
216 type: 'proxmox',
217 url: '/api2/json/nodes/localhost/subscription',
218 },
219 listeners: {
220 load: 'updateSubscription',
221 },
222 },
223 tasks: {
224 storeid: 'dash-tasks',
225 type: 'update',
226 interval: 15000,
227 autoStart: true,
228 autoLoad: true,
229 autoDestroy: true,
230 model: 'proxmox-tasks',
231 proxy: {
232 type: 'proxmox',
233 url: '/api2/json/status/tasks',
234 extraParams: {
235 since: '{sinceEpoch}',
236 },
237 },
238 listeners: {
239 load: 'updateTasks',
240 },
241 },
242 },
243 },
244
245 title: gettext('Dashboard'),
246
247 layout: {
248 type: 'column',
249 },
250
251 bodyPadding: '20 0 0 20',
252
253 defaults: {
254 columnWidth: 0.49,
255 xtype: 'panel',
256 margin: '0 20 20 0',
257 },
258
259 tools: [
260 {
261 type: 'gear',
262 tooltip: gettext('Edit dashboard settings'),
263 handler: 'openDashboardOptions',
264 },
265 ],
266
267 scrollable: true,
268
269 items: [
270 {
271 height: 250,
272 iconCls: 'fa fa-tasks',
273 title: gettext('Server Resources'),
274 bodyPadding: '0 20 0 20',
275 tools: [
276 {
277 xtype: 'button',
278 text: gettext('Show Fingerprint'),
279 handler: 'showFingerPrint',
280 bind: {
281 disabled: '{disableFPButton}',
282 },
283 },
284 ],
285 layout: {
286 type: 'hbox',
287 align: 'center',
288 },
289 defaults: {
290 xtype: 'proxmoxGauge',
291 spriteFontSize: '20px',
292 flex: 1,
293 },
294 items: [
295 {
296 title: gettext('CPU'),
297 reference: 'cpu',
298 },
299 {
300 title: gettext('Memory'),
301 reference: 'mem',
302 },
303 {
304 title: gettext('Root Disk'),
305 reference: 'root',
306 },
307 ],
308 },
309 {
310 xtype: 'pbsDatastoresStatistics',
311 height: 250,
312 },
313 {
314 xtype: 'pbsLongestTasks',
315 bind: {
316 title: gettext('Longest Tasks') + ' (' +
317 Ext.String.format(gettext('{0} days'), '{days}') + ')',
318 },
319 reference: 'longesttasks',
320 height: 250,
321 },
322 {
323 xtype: 'pbsRunningTasks',
324 height: 250,
325 },
326 {
327 bind: {
328 title: gettext('Task Summary') + ' (' +
329 Ext.String.format(gettext('{0} days'), '{days}') + ')',
330 },
331 xtype: 'pbsTaskSummary',
332 reference: 'tasksummary',
333 },
334 {
335 iconCls: 'fa fa-ticket',
336 title: 'Subscription',
337 height: 166,
338 reference: 'subscription',
339 xtype: 'pbsSubscriptionInfo',
340 },
341 ],
342 });
343
344 Ext.define('PBS.dashboard.SubscriptionInfo', {
345 extend: 'Ext.panel.Panel',
346 xtype: 'pbsSubscriptionInfo',
347
348 style: {
349 cursor: 'pointer',
350 },
351
352 layout: {
353 type: 'hbox',
354 align: 'middle',
355 },
356
357 items: [
358 {
359 xtype: 'box',
360 itemId: 'icon',
361 data: {
362 icon: 'question-circle',
363 },
364 width: 100,
365 tpl: '<center><i class="fa fa-3x fa-{icon}"></i></center>',
366 },
367 {
368 flex: 1,
369 xtype: 'box',
370 data: {
371 message: gettext('Unknown'),
372 },
373 itemId: 'message',
374 tpl: '<center>{message}</center>',
375 },
376 ],
377
378 setSubStatus: function(status) {
379 var me = this;
380 let icon = '';
381 let message = '';
382
383 switch (status) {
384 case 2:
385 icon = 'check good';
386 message = gettext('Your subscription status is valid.');
387 break;
388 case 1:
389 icon = 'exclamation-triangle warning';
390 message = gettext('Warning: Your subscription levels are not the same.');
391 break;
392 case 0:
393 icon = 'times-circle critical';
394 message = gettext('This node does not have a subscription.');
395 break;
396 default:
397 throw 'invalid subscription status';
398 }
399 me.getComponent('icon').update({ icon });
400 me.getComponent('message').update({ message });
401 },
402
403 listeners: {
404 click: {
405 element: 'body',
406 fn: function() {
407 var mainview = this.component.up('mainview');
408 mainview.getController().redirectTo('pbsSubscription');
409 },
410 },
411 },
412 });