]> git.proxmox.com Git - pmg-gui.git/blame - js/PBSConfig.js
add PBSConfig tab to Backup menu
[pmg-gui.git] / js / PBSConfig.js
CommitLineData
d944805a
SI
1Ext.define('Proxmox.form.PBSEncryptionCheckbox', {
2 extend: 'Ext.form.field.Checkbox',
3 xtype: 'pbsEncryptionCheckbox',
4
5 inputValue: true,
6
7 viewModel: {
8 data: {
9 value: null,
10 originalValue: null,
11 },
12 formulas: {
13 blabel: (get) => {
14 let v = get('value');
15 let original = get('originalValue');
16 if (!get('isCreate') && original) {
17 if (!v) {
18 return gettext('Warning: Existing encryption key will be deleted!');
19 }
20 return gettext('Active');
21 } else {
22 return gettext('Auto-generate a client encryption key, saved privately in /etc/pmg');
23 }
24 },
25 },
26 },
27
28 bind: {
29 value: '{value}',
30 boxLabel: '{blabel}',
31 },
32 resetOriginalValue: function() {
33 let me = this;
34 let vm = me.getViewModel();
35 vm.set('originalValue', me.value);
36
37 me.callParent(arguments);
38 },
39
40 getSubmitData: function() {
41 let me = this;
42 let val = me.getSubmitValue();
43 if (!me.isCreate) {
44 if (val === null) {
45 return { 'delete': 'encryption-key' };
46 } else if (val && !!val !== !!me.originalValue) {
47 return { 'encryption-key': 'autogen' };
48 }
49 } else if (val) {
50 return { 'encryption-key': 'autogen' };
51 }
52 return null;
53 },
54
55 initComponent: function() {
56 let me = this;
57 me.callParent();
58
59 let vm = me.getViewModel();
60 vm.set('isCreate', me.isCreate);
61 },
62});
63
64Ext.define('PMG.PBSInputPanel', {
65 extend: 'Ext.tab.Panel',
66 xtype: 'pmgPBSInputPanel',
67
68 bodyPadding: 10,
69 remoteId: undefined,
70
71 initComponent: function() {
72 let me = this;
73
74 me.items = [
75 {
76 title: gettext('Backup Server'),
77 xtype: 'inputpanel',
78 reference: 'remoteeditpanel',
79 onGetValues: function(values) {
80 values.disable = values.enable ? 0 : 1;
81 delete values.enable;
82
83 return values;
84 },
85
86 column1: [
87 {
88 xtype: me.isCreate ? 'textfield' : 'displayfield',
89 name: 'remote',
90 value: me.isCreate ? null : undefined,
91 fieldLabel: gettext('ID'),
92 allowBlank: false,
93 },
94 {
95 xtype: 'proxmoxtextfield',
96 name: 'server',
97 value: me.isCreate ? null : undefined,
98 vtype: 'DnsOrIp',
99 fieldLabel: gettext('Server'),
100 allowBlank: false,
101 },
102 {
103 xtype: 'proxmoxtextfield',
104 name: 'datastore',
105 value: me.isCreate ? null : undefined,
106 fieldLabel: 'Datastore',
107 allowBlank: false,
108 },
109 ],
110 column2: [
111 {
112 xtype: 'proxmoxtextfield',
113 name: 'username',
114 value: me.isCreate ? null : undefined,
115 emptyText: gettext('Example') + ': admin@pbs',
116 fieldLabel: gettext('Username'),
117 regex: /\S+@\w+/,
118 regexText: gettext('Example') + ': admin@pbs',
119 allowBlank: false,
120 },
121 {
122 xtype: 'proxmoxtextfield',
123 inputType: 'password',
124 name: 'password',
125 value: me.isCreate ? null : undefined,
126 emptyText: me.isCreate ? gettext('None') : '********',
127 fieldLabel: gettext('Password'),
128 allowBlank: true,
129 },
130 {
131 xtype: 'proxmoxcheckbox',
132 name: 'enable',
133 checked: true,
134 uncheckedValue: 0,
135 fieldLabel: gettext('Enable'),
136 },
137 ],
138 columnB: [
139 {
140 xtype: 'proxmoxtextfield',
141 name: 'fingerprint',
142 value: me.isCreate ? null : undefined,
143 fieldLabel: gettext('Fingerprint'),
144 emptyText: gettext('Server certificate SHA-256 fingerprint, required for self-signed certificates'),
145 regex: /[A-Fa-f0-9]{2}(:[A-Fa-f0-9]{2}){31}/,
146 regexText: gettext('Example') + ': AB:CD:EF:...',
147 allowBlank: true,
148 },
149 {
150 xtype: 'pbsEncryptionCheckbox',
151 name: 'encryption-key',
152 isCreate: me.isCreate,
153 fieldLabel: gettext('Encryption Key'),
154 },
155 {
156 xtype: 'displayfield',
157 userCls: 'pmx-hint',
158 value: `Proxmox Backup Server is currently in beta.`,
159 },
160 ],
161 },
162 {
163 title: gettext('Prune Options'),
164 xtype: 'inputpanel',
165 reference: 'prunepanel',
166 column1: [
167 {
168 xtype: 'proxmoxintegerfield',
169 fieldLabel: gettext('Keep Last'),
170 name: 'keep-last',
171 cbind: {
172 deleteEmpty: '{!isCreate}',
173 },
174 minValue: 1,
175 allowBlank: true,
176 },
177 {
178 xtype: 'proxmoxintegerfield',
179 fieldLabel: gettext('Keep Daily'),
180 name: 'keep-daily',
181 cbind: {
182 deleteEmpty: '{!isCreate}',
183 },
184 minValue: 1,
185 allowBlank: true,
186 },
187 {
188 xtype: 'proxmoxintegerfield',
189 fieldLabel: gettext('Keep Monthly'),
190 name: 'keep-monthly',
191 cbind: {
192 deleteEmpty: '{!isCreate}',
193 },
194 minValue: 1,
195 allowBlank: true,
196 },
197 ],
198 column2: [
199 {
200 xtype: 'proxmoxintegerfield',
201 fieldLabel: gettext('Keep Hourly'),
202 name: 'keep-hourly',
203 cbind: {
204 deleteEmpty: '{!isCreate}',
205 },
206 minValue: 1,
207 allowBlank: true,
208 },
209 {
210 xtype: 'proxmoxintegerfield',
211 fieldLabel: gettext('Keep Weekly'),
212 name: 'keep-weekly',
213 cbind: {
214 deleteEmpty: '{!isCreate}',
215 },
216 minValue: 1,
217 allowBlank: true,
218 },
219 {
220 xtype: 'proxmoxintegerfield',
221 fieldLabel: gettext('Keep Yearly'),
222 name: 'keep-yearly',
223 cbind: {
224 deleteEmpty: '{!isCreate}',
225 },
226 minValue: 1,
227 allowBlank: true,
228 },
229 ],
230 },
231 ];
232
233 me.callParent();
234 },
235
236});
237
238Ext.define('PMG.PBSEdit', {
239 extend: 'Proxmox.window.Edit',
240 xtype: 'pmgPBSEdit',
241
242 subject: 'Proxmox Backup Server',
243 isAdd: true,
244
245 bodyPadding: 0,
246
247 initComponent: function() {
248 let me = this;
249
250 me.isCreate = !me.remoteId;
251
252 if (me.isCreate) {
253 me.url = '/api2/extjs/config/pbs';
254 me.method = 'POST';
255 } else {
256 me.url = '/api2/extjs/config/pbs/' + me.remoteId;
257 me.method = 'PUT';
258 }
259
260 let ipanel = Ext.create('PMG.PBSInputPanel', {
261 isCreate: me.isCreate,
262 remoteId: me.remoteId,
263 });
264
265 me.items = [ipanel];
266
267 me.fieldDefaults = {
268 labelWidth: 150,
269 };
270
271 me.callParent();
272
273 if (!me.isCreate) {
274 me.load({
275 success: function(response, options) {
276 let values = response.result.data;
277
278 values.enable = values.disable ? 0 : 1;
279 me.down('inputpanel[reference=remoteeditpanel]').setValues(values);
280 me.down('inputpanel[reference=prunepanel]').setValues(values);
281 },
282 });
283 }
284 },
285});
286
287Ext.define('PMG.PBSScheduleEdit', {
288 extend: 'Proxmox.window.Edit',
289 xtype: 'pmgPBSScheduleEdit',
290
291 isAdd: true,
292 method: 'POST',
293 subject: gettext('Scheduled Backup'),
294 autoLoad: true,
295 items: [
296 {
297 xtype: 'proxmoxKVComboBox',
298 name: 'schedule',
299 fieldLabel: gettext('Schedule'),
300 comboItems: [
301 ['daily', 'daily'],
302 ['hourly', 'hourly'],
303 ['weekly', 'weekly'],
304 ['monthly', 'monthly'],
305 ],
306 editable: true,
307 emptyText: 'Systemd Calender Event',
308 },
309 {
310 xtype: 'proxmoxKVComboBox',
311 name: 'delay',
312 fieldLabel: gettext('Random Delay'),
313 comboItems: [
314 ['0s', 'no delay'],
315 ['15 minutes', '15 Minutes'],
316 ['6 hours', '6 hours'],
317 ],
318 editable: true,
319 emptyText: 'Systemd TimeSpan',
320 },
321 ],
322 initComponent: function() {
323 let me = this;
324
325 me.url = '/nodes/' + Proxmox.NodeName + '/pbs/' + me.remote + '/timer';
326 me.callParent();
327 },
328});
329
330Ext.define('PMG.PBSConfig', {
331 extend: 'Ext.panel.Panel',
332 xtype: 'pmgPBSConfig',
333
334 controller: {
335 xclass: 'Ext.app.ViewController',
336
337 callRestore: function(grid, record) {
338 let name = this.getViewModel().get('name');
339 Ext.create('PMG.RestoreWindow', {
340 name: name,
341 backup_time: record.data.time,
342 }).show();
343 },
344
345 restoreSnapshot: function(button) {
346 let me = this;
347 let view = me.lookup('pbsremotegrid');
348 let record = view.getSelection()[0];
349 me.callRestore(view, record);
350 },
351
352 runBackup: function(button) {
353 let me = this;
354 let view = me.lookup('pbsremotegrid');
355 let name = me.getViewModel().get('name');
356 Proxmox.Utils.API2Request({
357 url: "/nodes/" + Proxmox.NodeName + "/pbs/" + name + "/backup",
358 method: 'POST',
359 waitMsgTarget: view,
360 failure: function(response, opts) {
361 Ext.Msg.alert(gettext('Error'), response.htmlStatus);
362 },
363 success: function(response, opts) {
364 let upid = response.result.data;
365
366 let win = Ext.create('Proxmox.window.TaskViewer', {
367 upid: upid,
368 });
369 win.show();
370 me.mon(win, 'close', function() { view.getStore().load(); });
371 },
372 });
373 },
374
375 reload: function(grid) {
376 let me = this;
377 let selection = grid.getSelection();
378 me.showInfo(grid, selection);
379 },
380
381 showInfo: function(grid, selected) {
382 let me = this;
383 let viewModel = me.getViewModel();
384 if (selected[0]) {
385 let name = selected[0].data.remote;
386 viewModel.set('selected', true);
387 viewModel.set('name', name);
388
389 // set grid stores and load them
390 let remstore = me.lookup('pbsremotegrid').getStore();
391 remstore.getProxy().setUrl('/api2/json/nodes/' + Proxmox.NodeName + '/pbs/' + name + '/snapshots');
392 remstore.load();
393 } else {
394 viewModel.set('selected', false);
395 }
396 },
397 reloadSnapshots: function() {
398 let me = this;
399 let grid = me.lookup('grid');
400 let selection = grid.getSelection();
401 me.showInfo(grid, selection);
402 },
403 init: function(view) {
404 let me = this;
405 me.lookup('grid').relayEvents(view, ['activate']);
406 let pbsremotegrid = me.lookup('pbsremotegrid');
407
408 Proxmox.Utils.monStoreErrors(pbsremotegrid, pbsremotegrid.getStore(), true);
409 },
410
411 control: {
412 'grid[reference=grid]': {
413 selectionchange: 'showInfo',
414 load: 'reload',
415 },
416 'grid[reference=pbsremotegrid]': {
417 itemdblclick: 'restoreSnapshot',
418 },
419 },
420 },
421
422 viewModel: {
423 data: {
424 name: '',
425 selected: false,
426 },
427 },
428
429 layout: 'border',
430
431 items: [
432 {
433 region: 'center',
434 reference: 'grid',
435 xtype: 'pmgPBSConfigGrid',
436 border: false,
437 },
438 {
439 xtype: 'grid',
440 region: 'south',
441 reference: 'pbsremotegrid',
442 hidden: true,
443 height: '70%',
444 border: false,
445 split: true,
446 emptyText: gettext('No backups on remote'),
447 tbar: [
448 {
449 xtype: 'proxmoxButton',
450 text: gettext('Backup'),
451 handler: 'runBackup',
452 selModel: false,
453 },
454 {
455 xtype: 'proxmoxButton',
456 text: gettext('Restore'),
457 handler: 'restoreSnapshot',
458 disabled: true,
459 },
460 {
461 xtype: 'proxmoxStdRemoveButton',
462 text: gettext('Forget Snapshot'),
463 disabled: true,
464 getUrl: function(rec) {
465 let me = this;
466 let remote = me.lookupViewModel().get('name');
467 return '/nodes/' + Proxmox.NodeName + '/pbs/' + remote +'/snapshots/'+ rec.data.time;
468 },
469 confirmMsg: function(rec) {
470 let me = this;
471 let time = rec.data.time;
472 return Ext.String.format(gettext('Are you sure you want to forget snapshot {0}'), `'${time}'`);
473 },
474 callback: 'reloadSnapshots',
475 },
476 ],
477 store: {
478 fields: ['time', 'size', 'ctime', 'encrypted'],
479 proxy: { type: 'proxmox' },
480 sorters: [
481 {
482 property: 'time',
483 direction: 'DESC',
484 },
485 ],
486 },
487 bind: {
488 title: Ext.String.format(gettext("Backup snapshots on '{0}'"), '{name}'),
489 hidden: '{!selected}',
490 },
491 columns: [
492 {
493 text: 'Time',
494 dataIndex: 'time',
495 flex: 1,
496 },
497 {
498 text: 'Size',
499 dataIndex: 'size',
500 flex: 1,
501 },
502 {
503 text: 'Encrypted',
504 dataIndex: 'encrypted',
505 renderer: Proxmox.Utils.format_boolean,
506 flex: 1,
507 },
508 ],
509 },
510 ],
511
512});
513
514Ext.define('pmg-pbs-config', {
515 extend: 'Ext.data.Model',
516 fields: ['remote', 'server', 'datastore', 'username', 'disabled'],
517 proxy: {
518 type: 'proxmox',
519 url: '/api2/json/config/pbs',
520 },
521 idProperty: 'remote',
522});
523
524Ext.define('PMG.PBSConfigGrid', {
525 extend: 'Ext.grid.GridPanel',
526 xtype: 'pmgPBSConfigGrid',
527
528 controller: {
529 xclass: 'Ext.app.ViewController',
530
531 run_editor: function() {
532 let me = this;
533 let view = me.getView();
534 let rec = view.getSelection()[0];
535 if (!rec) {
536 return;
537 }
538
539 let win = Ext.createWidget('pmgPBSEdit', {
540 remoteId: rec.data.remote,
541 });
542 win.on('destroy', me.reload, me);
543 win.load();
544 win.show();
545 },
546
547 newRemote: function() {
548 let me = this;
549 let win = Ext.createWidget('pmgPBSEdit', {});
550 win.on('destroy', me.reload, me);
551 win.show();
552 },
553
554
555 reload: function() {
556 let me = this;
557 let view = me.getView();
558 view.getStore().load();
559 view.fireEvent('load', view);
560 },
561
562 createSchedule: function() {
563 let me = this;
564 let view = me.getView();
565 let rec = view.getSelection()[0];
566 let remotename = rec.data.remote;
567 let win = Ext.createWidget('pmgPBSScheduleEdit', {
568 remote: remotename,
569 });
570 win.on('destroy', me.reload, me);
571 win.show();
572 },
573
574 init: function(view) {
575 let me = this;
576 Proxmox.Utils.monStoreErrors(view, view.getStore(), true);
577 },
578
579 },
580
581 store: {
582 model: 'pmg-pbs-config',
583 sorters: [{
584 property: 'remote',
585 order: 'DESC',
586 }],
587 },
588
589 tbar: [
590 {
591 xtype: 'proxmoxButton',
592 text: gettext('Edit'),
593 disabled: true,
594 handler: 'run_editor',
595 },
596 {
597 text: gettext('Create'),
598 handler: 'newRemote',
599 },
600 {
601 xtype: 'proxmoxStdRemoveButton',
602 baseurl: '/config/pbs',
603 callback: 'reload',
604 },
605 {
606 xtype: 'proxmoxButton',
607 text: gettext('Schedule'),
608 enableFn: function(rec) {
609 return !rec.data.disable;
610 },
611 disabled: true,
612 handler: 'createSchedule',
613 },
614 {
615 xtype: 'proxmoxStdRemoveButton',
616 baseurl: '/nodes/' + Proxmox.NodeName + '/pbs/',
617 callback: 'reload',
618 text: gettext('Remove Schedule'),
619 confirmMsg: function(rec) {
620 let me = this;
621 let name = rec.getId();
622 return Ext.String.format(gettext('Are you sure you want to remove the schedule for {0}'), `'${name}'`);
623 },
624 getUrl: function(rec) {
625 let me = this;
626 return me.baseurl + '/' + rec.getId() + '/timer';
627 },
628 },
629 ],
630
631 listeners: {
632 itemdblclick: 'run_editor',
633 activate: 'reload',
634 },
635
636 columns: [
637 {
638 header: gettext('Backup Server name'),
639 sortable: true,
640 dataIndex: 'remote',
641 flex: 2,
642 },
643 {
644 header: gettext('Server'),
645 sortable: true,
646 dataIndex: 'server',
647 flex: 2,
648 },
649 {
650 header: gettext('Datastore'),
651 sortable: true,
652 dataIndex: 'datastore',
653 flex: 1,
654 },
655 {
656 header: gettext('User ID'),
657 sortable: true,
658 dataIndex: 'username',
659 flex: 1,
660 },
661 {
662 header: gettext('Encryption'),
663 width: 80,
664 sortable: true,
665 dataIndex: 'encryption-key',
666 renderer: Proxmox.Utils.format_boolean,
667 },
668 {
669 header: gettext('Enabled'),
670 width: 80,
671 sortable: true,
672 dataIndex: 'disable',
673 renderer: Proxmox.Utils.format_neg_boolean,
674 },
675 ],
676
677});
678