]> git.proxmox.com Git - proxmox-backup.git/blob - www/Dashboard.js
docs: add documentation for notification system
[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 updateRepositoryStatus: function(store, records, success) {
63 if (!success) { return; }
64 let me = this;
65 let view = me.getView();
66 view.down('#repositoryStatus').setRepositoryInfo(records[0].data['standard-repos']);
67 },
68
69 updateSubscription: function(store, records, success) {
70 if (!success) { return; }
71 let me = this;
72 let view = me.getView();
73 let status = records[0].data.status || 'unknown';
74 // 2 = all good, 1 = different levels, 0 = none
75 let subscriptionActive = status.toLowerCase() === 'active';
76 let subStatus = status.toLowerCase() === 'active' ? 2 : 0;
77 me.lookup('subscription').setSubStatus(subStatus);
78 view.down('#repositoryStatus').setSubscriptionStatus(subscriptionActive);
79 },
80
81 updateTasks: function(store, records, success) {
82 if (!success) return;
83 let me = this;
84 let viewModel = me.getViewModel();
85
86 records.sort((a, b) => a.data.duration - b.data.duration);
87 let top10 = records.slice(-10);
88 me.lookup('longesttasks').updateTasks(top10);
89
90 let data = {
91 backup: { error: 0, warning: 0, ok: 0 },
92 prune: { error: 0, warning: 0, ok: 0 },
93 garbage_collection: { error: 0, warning: 0, ok: 0 },
94 sync: { error: 0, warning: 0, ok: 0 },
95 verify: { error: 0, warning: 0, ok: 0 },
96 'tape-backup': { error: 0, warning: 0, ok: 0 },
97 'tape-restore': { error: 0, warning: 0, ok: 0 },
98 };
99
100 records.forEach(record => {
101 let task = record.data;
102 let type = task.worker_type;
103 if (type === 'syncjob') {
104 type = 'sync';
105 }
106
107 if (type.startsWith('verif')) {
108 type = 'verify';
109 }
110
111 if (type.startsWith('prune')) {
112 type = 'prune';
113 }
114
115 if (type.startsWith('tape-backup')) {
116 type = 'tape-backup';
117 }
118
119 if (data[type] && task.status) {
120 let parsed = Proxmox.Utils.parse_task_status(task.status);
121 data[type][parsed]++;
122 }
123 });
124
125 me.lookup('tasksummary').updateTasks(data, viewModel.get('sinceEpoch'));
126 },
127
128 init: function(view) {
129 var me = this;
130 var sp = Ext.state.Manager.getProvider();
131 var days = sp.get('dashboard-days') || 30;
132 me.setDays(days, false);
133
134 view.mon(sp, 'statechange', function(provider, key, value) {
135 if (key !== 'summarycolumns') {
136 return;
137 }
138 Proxmox.Utils.updateColumns(view);
139 });
140 },
141 },
142
143 viewModel: {
144 data: {
145 days: 30,
146 },
147
148 formulas: {
149 sinceEpoch: (get) => (Date.now()/1000 - get('days') * 24*3600).toFixed(0),
150 },
151
152 stores: {
153 repositories: {
154 storeid: 'dash-repositories',
155 type: 'update',
156 interval: 15000,
157 autoStart: true,
158 autoLoad: true,
159 autoDestroy: true,
160 proxy: {
161 type: 'proxmox',
162 url: '/api2/json/nodes/localhost/apt/repositories',
163 },
164 listeners: {
165 load: 'updateRepositoryStatus',
166 },
167 },
168 subscription: {
169 storeid: 'dash-subscription',
170 type: 'update',
171 interval: 10000,
172 autoStart: true,
173 autoLoad: true,
174 autoDestroy: true,
175 proxy: {
176 type: 'proxmox',
177 url: '/api2/json/nodes/localhost/subscription',
178 },
179 listeners: {
180 load: 'updateSubscription',
181 },
182 },
183 tasks: {
184 storeid: 'dash-tasks',
185 type: 'update',
186 interval: 15000,
187 autoStart: true,
188 autoLoad: true,
189 autoDestroy: true,
190 model: 'proxmox-tasks',
191 proxy: {
192 type: 'proxmox',
193 url: '/api2/json/nodes/localhost/tasks',
194 extraParams: {
195 limit: 0,
196 since: '{sinceEpoch}',
197 },
198 },
199 listeners: {
200 load: 'updateTasks',
201 },
202 },
203 },
204 },
205
206 listeners: {
207 resize: function(panel) {
208 Proxmox.Utils.updateColumns(panel);
209 },
210 },
211
212 title: gettext('Dashboard'),
213
214 layout: {
215 type: 'column',
216 },
217
218 bodyPadding: '20 0 0 20',
219
220 minWidth: 700,
221
222 defaults: {
223 columnWidth: 0.49,
224 xtype: 'panel',
225 margin: '0 20 20 0',
226 },
227
228 tools: [
229 {
230 type: 'gear',
231 tooltip: gettext('Edit dashboard settings'),
232 handler: 'openDashboardOptions',
233 },
234 ],
235
236 scrollable: true,
237
238 items: [
239 {
240 xtype: 'pbsNodeInfoPanel',
241 reference: 'nodeInfo',
242 height: 290,
243 },
244 {
245 xtype: 'pbsDatastoresStatistics',
246 height: 290,
247 },
248 {
249 xtype: 'pbsLongestTasks',
250 bind: {
251 title: gettext('Longest Tasks') + ' (' +
252 Ext.String.format(gettext('{0} days'), '{days}') + ')',
253 },
254 reference: 'longesttasks',
255 height: 290,
256 },
257 {
258 xtype: 'pbsRunningTasks',
259 height: 290,
260 },
261 {
262 bind: {
263 title: gettext('Task Summary') + ' (' +
264 Ext.String.format(gettext('{0} days'), '{days}') + ')',
265 },
266 xtype: 'pbsTaskSummary',
267 height: 250,
268 reference: 'tasksummary',
269 },
270 {
271 iconCls: 'fa fa-ticket',
272 title: 'Subscription',
273 height: 250,
274 reference: 'subscription',
275 xtype: 'pbsSubscriptionInfo',
276 },
277 ],
278 });
279
280 Ext.define('PBS.dashboard.SubscriptionInfo', {
281 extend: 'Ext.panel.Panel',
282 xtype: 'pbsSubscriptionInfo',
283
284 style: {
285 cursor: 'pointer',
286 },
287
288 layout: {
289 type: 'hbox',
290 align: 'middle',
291 },
292
293 items: [
294 {
295 xtype: 'box',
296 itemId: 'icon',
297 data: {
298 icon: 'question-circle',
299 },
300 width: 100,
301 tpl: '<center><i class="fa fa-3x fa-{icon}"></i></center>',
302 },
303 {
304 flex: 1,
305 xtype: 'box',
306 data: {
307 message: gettext('Unknown'),
308 },
309 itemId: 'message',
310 tpl: '<center>{message}</center>',
311 },
312 ],
313
314 setSubStatus: function(status) {
315 var me = this;
316 let icon = '';
317 let message = '';
318
319 switch (status) {
320 case 2:
321 icon = 'check good';
322 message = gettext('Your subscription status is valid.');
323 break;
324 case 1:
325 icon = 'exclamation-triangle warning';
326 message = gettext('Warning: Your subscription levels are not the same.');
327 break;
328 case 0:
329 icon = 'times-circle critical';
330 message = `<h1>${gettext('No valid subscription')}</h1>${PBS.Utils.noSubKeyHtml}`;
331 break;
332 default:
333 throw 'invalid subscription status';
334 }
335 me.getComponent('icon').update({ icon });
336 me.getComponent('message').update({ message });
337 },
338
339 listeners: {
340 click: {
341 element: 'body',
342 fn: function() {
343 var mainview = this.component.up('mainview');
344 mainview.getController().redirectTo('pbsSubscription');
345 },
346 },
347 },
348 });