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