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