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