]> git.proxmox.com Git - pve-manager.git/blame - www/manager6/dc/Backup.js
ui: backup job: disable zstd thread count field when zstd isn't used
[pve-manager.git] / www / manager6 / dc / Backup.js
CommitLineData
ad74d86c 1Ext.define('PVE.dc.BackupEdit', {
9fccc702 2 extend: 'Proxmox.window.Edit',
ad74d86c
DM
3 alias: ['widget.pveDcBackupEdit'],
4
d232c409
DC
5 mixins: ['Proxmox.Mixin.CBind'],
6
fc03fcb8
TL
7 defaultFocus: undefined,
8
d232c409
DC
9 subject: gettext("Backup Job"),
10 bodyPadding: 0,
ad74d86c 11
d232c409
DC
12 url: '/api2/extjs/cluster/backup',
13 method: 'POST',
14 isCreate: true,
ad74d86c 15
d232c409
DC
16 cbindData: function() {
17 let me = this;
18 if (me.jobid) {
19 me.isCreate = false;
20 me.method = 'PUT';
21 me.url += `/${me.jobid}`;
12809bba 22 }
d232c409
DC
23 return {};
24 },
12809bba 25
d232c409
DC
26 controller: {
27 xclass: 'Ext.app.ViewController',
28
29 onGetValues: function(values) {
30 let me = this;
31 let isCreate = me.getView().isCreate;
32 if (!values.node) {
33 if (!isCreate) {
34 Proxmox.Utils.assemble_field_data(values, { 'delete': 'node' });
35 }
36 delete values.node;
37 }
38
75601945
LW
39 // Get rid of new-old parameters for notification settings.
40 // These should only be set for those selected few who ran
41 // pve-manager from pvetest.
f2aa317a
DC
42 if (!isCreate) {
43 Proxmox.Utils.assemble_field_data(values, { 'delete': 'notification-policy' });
44 Proxmox.Utils.assemble_field_data(values, { 'delete': 'notification-target' });
45 }
2c4780cc 46
d232c409
DC
47 if (!values.id && isCreate) {
48 values.id = 'backup-' + Ext.data.identifier.Uuid.Global.generate().slice(0, 13);
49 }
50
51 let selMode = values.selMode;
52 delete values.selMode;
53
54 if (selMode === 'all') {
55 values.all = 1;
56 values.exclude = '';
57 delete values.vmid;
58 } else if (selMode === 'exclude') {
59 values.all = 1;
60 values.exclude = values.vmid;
61 delete values.vmid;
62 } else if (selMode === 'pool') {
63 delete values.vmid;
64 }
ad74d86c 65
d232c409
DC
66 if (selMode !== 'pool') {
67 delete values.pool;
68 }
69 return values;
70 },
71
72 nodeChange: function(f, value) {
73 let me = this;
74 me.lookup('storageSelector').setNodename(value);
75 let vmgrid = me.lookup('vmgrid');
76 let store = vmgrid.getStore();
77
78 store.clearFilter();
79 store.filterBy(function(rec) {
80 return !value || rec.get('node') === value;
81 });
ad74d86c 82
d232c409
DC
83 let mode = me.lookup('modeSelector').getValue();
84 if (mode === 'all') {
85 vmgrid.selModel.selectAll(true);
86 }
87 if (mode === 'pool') {
88 me.selectPoolMembers();
89 }
90 },
91
92 storageChange: function(f, v) {
93 let me = this;
94 let rec = f.getStore().findRecord('storage', v, 0, false, true, true);
95 let compressionSelector = me.lookup('compressionSelector');
96
97 if (rec?.data?.type === 'pbs') {
98 compressionSelector.setValue('zstd');
99 compressionSelector.setDisabled(true);
100 } else if (!compressionSelector.getEditable()) {
101 compressionSelector.setDisabled(false);
102 }
103 },
104
105 selectPoolMembers: function() {
106 let me = this;
25f7446c
DC
107 let mode = me.lookup('modeSelector').getValue();
108
109 if (mode !== 'pool') {
110 return;
111 }
112
d232c409
DC
113 let vmgrid = me.lookup('vmgrid');
114 let poolid = me.lookup('poolSelector').getValue();
115
116 vmgrid.getSelectionModel().deselectAll(true);
ab648869
TM
117 if (!poolid) {
118 return;
119 }
e440f8a4 120 vmgrid.getStore().filter([
ab648869
TM
121 {
122 id: 'poolFilter',
123 property: 'pool',
f6710aac
TL
124 value: poolid,
125 },
ab648869 126 ]);
e440f8a4 127 vmgrid.selModel.selectAll(true);
d232c409 128 },
ad74d86c 129
d232c409
DC
130 modeChange: function(f, value, oldValue) {
131 let me = this;
132 let vmgrid = me.lookup('vmgrid');
133 vmgrid.getStore().removeFilter('poolFilter');
ab648869 134
d232c409
DC
135 if (oldValue === 'all' && value !== 'all') {
136 vmgrid.getSelectionModel().deselectAll(true);
ab648869
TM
137 }
138
ad74d86c 139 if (value === 'all') {
d232c409 140 vmgrid.getSelectionModel().selectAll(true);
ad74d86c 141 }
ab648869
TM
142
143 if (value === 'pool') {
d232c409
DC
144 me.selectPoolMembers();
145 }
146 },
147
9f2b84be
FE
148 compressionChange: function(f, value, oldValue) {
149 this.getView().lookup('backupAdvanced').updateCompression(value, f.isDisabled());
150 },
151
152 compressionDisable: function(f) {
153 this.getView().lookup('backupAdvanced').updateCompression(f.getValue(), true);
154 },
155
156 compressionEnable: function(f) {
157 this.getView().lookup('backupAdvanced').updateCompression(f.getValue(), false);
158 },
159
d232c409
DC
160 init: function(view) {
161 let me = this;
162 if (view.isCreate) {
163 me.lookup('modeSelector').setValue('include');
ab648869 164 } else {
d232c409
DC
165 view.load({
166 success: function(response, _options) {
167 let data = response.result.data;
168
75601945
LW
169 // Migrate 'new'-old notification-policy back to
170 // old-old mailnotification. Only should affect
171 // users who used pve-manager from pvetest.
172 // This was a remnant of notifications before the
173 // overhaul.
174 let policy = data['notification-policy'];
175 if (policy === 'always' || policy === 'failure') {
176 data.mailnotification = policy;
2c4780cc
LW
177 }
178
d232c409
DC
179 if (data.exclude) {
180 data.vmid = data.exclude;
181 data.selMode = 'exclude';
182 } else if (data.all) {
183 data.vmid = '';
184 data.selMode = 'all';
185 } else if (data.pool) {
186 data.selMode = 'pool';
187 data.selPool = data.pool;
188 } else {
189 data.selMode = 'include';
190 }
191
192 me.getViewModel().set('selMode', data.selMode);
193
194 if (data['prune-backups']) {
195 Object.assign(data, data['prune-backups']);
196 delete data['prune-backups'];
197 } else if (data.maxfiles !== undefined) {
198 if (data.maxfiles > 0) {
199 data['keep-last'] = data.maxfiles;
200 } else {
201 data['keep-all'] = 1;
202 }
203 delete data.maxfiles;
204 }
205
206 if (data['notes-template']) {
207 data['notes-template'] =
208 PVE.Utils.unEscapeNotesTemplate(data['notes-template']);
209 }
210
bb4741c7
FE
211 if (data.performance) {
212 Object.assign(data, data.performance);
213 delete data.performance;
214 }
215
d232c409
DC
216 view.setValues(data);
217 },
218 });
ad74d86c 219 }
d232c409
DC
220 },
221 },
60e049c2 222
d232c409
DC
223 viewModel: {
224 data: {
225 selMode: 'include',
66b20862 226 notificationMode: '__default__',
d232c409
DC
227 },
228
229 formulas: {
230 poolMode: (get) => get('selMode') === 'pool',
231 disableVMSelection: (get) => get('selMode') !== 'include' && get('selMode') !== 'exclude',
66b20862
LW
232 showMailtoFields: (get) =>
233 ['auto', 'legacy-sendmail', '__default__'].includes(get('notificationMode')),
d232c409
DC
234 },
235 },
236
237 items: [
238 {
239 xtype: 'tabpanel',
240 region: 'center',
241 layout: 'fit',
242 bodyPadding: 10,
759c752c
FE
243 items: [
244 {
d232c409
DC
245 xtype: 'container',
246 title: gettext('General'),
759c752c 247 region: 'center',
d232c409
DC
248 layout: {
249 type: 'vbox',
250 align: 'stretch',
251 },
759c752c 252 items: [
34721757
FE
253 {
254 xtype: 'inputpanel',
d232c409
DC
255 onlineHelp: 'chapter_vzdump',
256 column1: [
257 {
258 xtype: 'pveNodeSelector',
259 name: 'node',
260 fieldLabel: gettext('Node'),
261 allowBlank: true,
262 editable: true,
263 autoSelect: false,
264 emptyText: '-- ' + gettext('All') + ' --',
265 listeners: {
266 change: 'nodeChange',
267 },
268 },
269 {
270 xtype: 'pveStorageSelector',
271 reference: 'storageSelector',
272 fieldLabel: gettext('Storage'),
273 clusterView: true,
274 storageContent: 'backup',
275 allowBlank: false,
276 name: 'storage',
277 listeners: {
278 change: 'storageChange',
279 },
280 },
34721757 281 {
d232c409
DC
282 xtype: 'pveCalendarEvent',
283 fieldLabel: gettext('Schedule'),
284 allowBlank: false,
285 name: 'schedule',
34721757
FE
286 },
287 {
d232c409
DC
288 xtype: 'proxmoxKVComboBox',
289 reference: 'modeSelector',
290 comboItems: [
291 ['include', gettext('Include selected VMs')],
292 ['all', gettext('All')],
293 ['exclude', gettext('Exclude selected VMs')],
294 ['pool', gettext('Pool based')],
295 ],
296 fieldLabel: gettext('Selection mode'),
297 name: 'selMode',
298 value: '',
299 bind: {
300 value: '{selMode}',
301 },
302 listeners: {
303 change: 'modeChange',
304 },
305 },
306 {
307 xtype: 'pvePoolSelector',
308 reference: 'poolSelector',
309 fieldLabel: gettext('Pool to backup'),
310 hidden: true,
311 allowBlank: false,
312 name: 'pool',
313 listeners: {
314 change: 'selectPoolMembers',
315 },
316 bind: {
317 hidden: '{!poolMode}',
318 disabled: '{!poolMode}',
34721757 319 },
34721757
FE
320 },
321 ],
d232c409 322 column2: [
66b20862
LW
323 {
324 xtype: 'proxmoxKVComboBox',
325 comboItems: [
326 [
327 '__default__',
328 Ext.String.format(
329 gettext('{0} (Auto)'), Proxmox.Utils.defaultText,
330 ),
331 ],
332 ['auto', gettext('Auto')],
333 ['legacy-sendmail', gettext('Email (legacy)')],
334 ['notification-system', gettext('Notification system')],
335 ],
336 fieldLabel: gettext('Notification mode'),
337 name: 'notification-mode',
338 cbind: {
339 deleteEmpty: '{!isCreate}',
340 },
341 bind: {
342 value: '{notificationMode}',
343 },
344 },
d232c409
DC
345 {
346 xtype: 'pveEmailNotificationSelector',
fb5b8179 347 fieldLabel: gettext('Send email'),
75601945 348 name: 'mailnotification',
d232c409
DC
349 cbind: {
350 value: (get) => get('isCreate') ? 'always' : '',
351 deleteEmpty: '{!isCreate}',
352 },
66b20862
LW
353 bind: {
354 disabled: '{!showMailtoFields}',
355 },
d232c409 356 },
2c4780cc
LW
357 {
358 xtype: 'textfield',
359 fieldLabel: gettext('Send email to'),
360 name: 'mailto',
66b20862
LW
361 bind: {
362 disabled: '{!showMailtoFields}',
363 },
2c4780cc 364 },
d232c409 365 {
5265a2d1 366 xtype: 'pveBackupCompressionSelector',
d232c409
DC
367 reference: 'compressionSelector',
368 fieldLabel: gettext('Compression'),
369 name: 'compress',
370 cbind: {
371 deleteEmpty: '{!isCreate}',
372 },
373 value: 'zstd',
9f2b84be
FE
374 listeners: {
375 change: 'compressionChange',
376 disable: 'compressionDisable',
377 enable: 'compressionEnable',
378 },
d232c409
DC
379 },
380 {
381 xtype: 'pveBackupModeSelector',
382 fieldLabel: gettext('Mode'),
383 value: 'snapshot',
384 name: 'mode',
385 },
386 {
387 xtype: 'proxmoxcheckbox',
388 fieldLabel: gettext('Enable'),
389 name: 'enabled',
390 uncheckedValue: 0,
391 defaultValue: 1,
392 checked: true,
393 },
394 ],
395 columnB: [
396 {
397 xtype: 'proxmoxtextfield',
398 name: 'comment',
399 fieldLabel: gettext('Job Comment'),
400 cbind: {
401 deleteEmpty: '{!isCreate}',
402 },
403 autoEl: {
404 tag: 'div',
405 'data-qtip': gettext('Description of the job'),
406 },
407 },
408 {
409 xtype: 'vmselector',
410 reference: 'vmgrid',
411 height: 300,
412 name: 'vmid',
413 disabled: true,
414 allowBlank: false,
415 columnSelection: ['vmid', 'node', 'status', 'name', 'type'],
416 bind: {
417 disabled: '{disableVMSelection}',
418 },
419 },
420 ],
421 advancedColumn1: [
422 {
423 xtype: 'proxmoxcheckbox',
424 fieldLabel: gettext('Repeat missed'),
425 name: 'repeat-missed',
426 uncheckedValue: 0,
427 defaultValue: 0,
428 cbind: {
429 deleteDefaultValue: '{!isCreate}',
430 },
431 },
432 ],
433 onGetValues: function(values) {
434 return this.up('window').getController().onGetValues(values);
435 },
34721757 436 },
759c752c
FE
437 ],
438 },
d232c409
DC
439 {
440 xtype: 'pveBackupJobPrunePanel',
441 title: gettext('Retention'),
442 cbind: {
443 isCreate: '{isCreate}',
444 },
445 keepAllDefaultForCreate: false,
446 showPBSHint: false,
447 fallbackHintHtml: gettext('Without any keep option, the storage\'s configuration or node\'s vzdump.conf is used as fallback'),
448 },
449 {
450 xtype: 'inputpanel',
451 title: gettext('Note Template'),
452 region: 'center',
453 layout: {
454 type: 'vbox',
455 align: 'stretch',
456 },
457 onGetValues: function(values) {
458 if (values['notes-template']) {
459 values['notes-template'] =
460 PVE.Utils.escapeNotesTemplate(values['notes-template']);
759c752c 461 }
d232c409
DC
462 return values;
463 },
464 items: [
465 {
466 xtype: 'textarea',
467 name: 'notes-template',
468 fieldLabel: gettext('Backup Notes'),
469 height: 100,
470 maxLength: 512,
471 cbind: {
472 deleteEmpty: '{!isCreate}',
473 value: (get) => get('isCreate') ? '{{guestname}}' : undefined,
474 },
475 },
476 {
477 xtype: 'box',
478 style: {
479 margin: '8px 0px',
480 'line-height': '1.5em',
481 },
482 html: gettext('The notes are added to each backup created by this job.')
483 + '<br>'
484 + Ext.String.format(
485 gettext('Possible template variables are: {0}'),
486 PVE.Utils.notesTemplateVars.map(v => `<code>{{${v}}}</code>`).join(', '),
487 ),
488 },
489 ],
12809bba 490 },
bb4741c7
FE
491 {
492 xtype: 'pveBackupAdvancedOptionsPanel',
9f2b84be 493 reference: 'backupAdvanced',
bb4741c7
FE
494 title: gettext('Advanced'),
495 cbind: {
496 isCreate: '{isCreate}',
497 },
498 },
d232c409
DC
499 ],
500 },
501 ],
ad74d86c
DM
502});
503
ad74d86c
DM
504Ext.define('PVE.dc.BackupView', {
505 extend: 'Ext.grid.GridPanel',
506
507 alias: ['widget.pveDcBackupView'],
508
ba93a9c6
DC
509 onlineHelp: 'chapter_vzdump',
510
ad74d86c 511 allText: '-- ' + gettext('All') + ' --',
ad74d86c 512
8058410f 513 initComponent: function() {
12809bba 514 let me = this;
ad74d86c 515
12809bba 516 let store = new Ext.data.Store({
ad74d86c
DM
517 model: 'pve-cluster-backup',
518 proxy: {
12809bba 519 type: 'proxmox',
f6710aac
TL
520 url: "/api2/json/cluster/backup",
521 },
ad74d86c
DM
522 });
523
12809bba 524 let not_backed_store = new Ext.data.Store({
7d2fac4a 525 sorters: 'vmid',
8058410f 526 proxy: {
7d2fac4a 527 type: 'proxmox',
1a2e0e23 528 url: 'api2/json/cluster/backup-info/not-backed-up',
7d2fac4a
AL
529 },
530 });
531
315fecea 532 let noBackupJobInfoButton;
12809bba 533 let reload = function() {
ad74d86c 534 store.load();
7d2fac4a 535 not_backed_store.load({
315fecea 536 callback: records => noBackupJobInfoButton.setVisible(records.length > 0),
7d2fac4a 537 });
ad74d86c
DM
538 };
539
12809bba 540 let sm = Ext.create('Ext.selection.RowModel', {});
ad74d86c 541
12809bba
TL
542 let run_editor = function() {
543 let rec = sm.getSelection()[0];
ad74d86c
DM
544 if (!rec) {
545 return;
546 }
547
70480c37
TL
548 Ext.create('PVE.dc.BackupEdit', {
549 autoShow: true,
f6710aac 550 jobid: rec.data.id,
70480c37
TL
551 listeners: {
552 destroy: () => reload(),
553 },
43b2494b 554 });
ad74d86c
DM
555 };
556
12809bba 557 let run_detail = function() {
8058410f 558 let record = sm.getSelection()[0];
01ad47af
AL
559 if (!record) {
560 return;
561 }
386c9ce5 562 Ext.create('Ext.window.Window', {
01ad47af 563 modal: true,
386c9ce5 564 width: 800,
a4240b80 565 height: Ext.getBody().getViewSize().height > 1000 ? 800 : 600, // factor out as common infra?
01ad47af
AL
566 resizable: true,
567 layout: 'fit',
568 title: gettext('Backup Details'),
12809bba
TL
569 items: [
570 {
571 xtype: 'panel',
572 region: 'center',
573 layout: {
574 type: 'vbox',
575 align: 'stretch',
576 },
577 items: [
578 {
579 xtype: 'pveBackupInfo',
580 flex: 0,
581 layout: 'fit',
582 record: record.data,
583 },
584 {
585 xtype: 'pveBackupDiskTree',
586 title: gettext('Included disks'),
587 flex: 1,
588 jobid: record.data.id,
589 },
590 ],
01ad47af 591 },
12809bba 592 ],
01ad47af
AL
593 }).show();
594 };
595
12809bba 596 let run_backup_now = function(job) {
389d3cf1
SR
597 job = Ext.clone(job);
598
a0fecb88 599 let jobNode = job.node;
389d3cf1
SR
600 // Remove properties related to scheduling
601 delete job.enabled;
602 delete job.starttime;
603 delete job.dow;
604 delete job.id;
20d15804
DC
605 delete job.schedule;
606 delete job.type;
389d3cf1 607 delete job.node;
1a8bd901 608 delete job.comment;
31de6834 609 delete job['next-run'];
9cd2e638 610 delete job['repeat-missed'];
389d3cf1
SR
611 job.all = job.all === true ? 1 : 0;
612
93880785
FE
613 ['performance', 'prune-backups'].forEach(key => {
614 if (job[key]) {
615 job[key] = PVE.Parser.printPropertyString(job[key]);
616 }
617 });
3e70f3c5 618
a0fecb88
TL
619 let allNodes = PVE.data.ResourceStore.getNodes();
620 let nodes = allNodes.filter(node => node.status === 'online').map(node => node.node);
621 let errors = [];
622
623 if (jobNode !== undefined) {
624 if (!nodes.includes(jobNode)) {
625 Ext.Msg.alert('Error', "Node '"+ jobNode +"' from backup job isn't online!");
626 return;
627 }
8058410f 628 nodes = [jobNode];
a0fecb88
TL
629 } else {
630 let unkownNodes = allNodes.filter(node => node.status !== 'online');
12809bba 631 if (unkownNodes.length > 0) {errors.push(unkownNodes.map(node => node.node + ": " + gettext("Node is offline")));}
a0fecb88
TL
632 }
633 let jobTotalCount = nodes.length, jobsStarted = 0;
389d3cf1
SR
634
635 Ext.Msg.show({
636 title: gettext('Please wait...'),
637 closable: false,
a0fecb88
TL
638 progress: true,
639 progressText: '0/' + jobTotalCount,
389d3cf1 640 });
389d3cf1 641
8058410f 642 let postRequest = function() {
a0fecb88
TL
643 jobsStarted++;
644 Ext.Msg.updateProgress(jobsStarted / jobTotalCount, jobsStarted + '/' + jobTotalCount);
389d3cf1 645
12809bba 646 if (jobsStarted === jobTotalCount) {
389d3cf1 647 Ext.Msg.hide();
a0fecb88
TL
648 if (errors.length > 0) {
649 Ext.Msg.alert('Error', 'Some errors have been encountered:<br />' + errors.join('<br />'));
389d3cf1
SR
650 }
651 }
a0fecb88
TL
652 };
653
654 nodes.forEach(node => Proxmox.Utils.API2Request({
655 url: '/nodes/' + node + '/vzdump',
656 method: 'POST',
657 params: job,
8058410f 658 failure: function(response, opts) {
a0fecb88
TL
659 errors.push(node + ': ' + response.htmlStatus);
660 postRequest();
661 },
f6710aac 662 success: postRequest,
a0fecb88
TL
663 }));
664 };
389d3cf1 665
5720fafa 666 var edit_btn = new Proxmox.button.Button({
ad74d86c
DM
667 text: gettext('Edit'),
668 disabled: true,
669 selModel: sm,
f6710aac 670 handler: run_editor,
ad74d86c
DM
671 });
672
389d3cf1
SR
673 var run_btn = new Proxmox.button.Button({
674 text: gettext('Run now'),
675 disabled: true,
676 selModel: sm,
677 handler: function() {
678 var rec = sm.getSelection()[0];
679 if (!rec) {
680 return;
681 }
682
683 Ext.Msg.show({
684 title: gettext('Confirm'),
685 icon: Ext.Msg.QUESTION,
686 msg: gettext('Start the selected backup job now?'),
687 buttons: Ext.Msg.YESNO,
688 callback: function(btn) {
689 if (btn !== 'yes') {
690 return;
691 }
692 run_backup_now(rec.data);
f6710aac 693 },
389d3cf1 694 });
f6710aac 695 },
389d3cf1
SR
696 });
697
3b1ca3ff 698 var remove_btn = Ext.create('Proxmox.button.StdRemoveButton', {
ad74d86c 699 selModel: sm,
3b1ca3ff
DC
700 baseurl: '/cluster/backup',
701 callback: function() {
702 reload();
f6710aac 703 },
ad74d86c
DM
704 });
705
01ad47af 706 var detail_btn = new Proxmox.button.Button({
393b74ff 707 text: gettext('Job Detail'),
01ad47af
AL
708 disabled: true,
709 tooltip: gettext('Show job details and which guests and volumes are affected by the backup job'),
710 selModel: sm,
711 handler: run_detail,
712 });
713
12809bba 714 noBackupJobInfoButton = new Proxmox.button.Button({
315fecea
TL
715 text: `${gettext('Show')}: ${gettext('Guests Without Backup Job')}`,
716 tooltip: gettext('Some guests are not covered by any backup job.'),
717 iconCls: 'fa fa-fw fa-exclamation-circle',
7d2fac4a 718 hidden: true,
38e79438
TL
719 handler: () => {
720 Ext.create('Ext.window.Window', {
721 autoShow: true,
722 modal: true,
723 width: 600,
724 height: 500,
725 resizable: true,
726 layout: 'fit',
727 title: gettext('Guests Without Backup Job'),
728 items: [
729 {
730 xtype: 'panel',
731 region: 'center',
732 layout: {
733 type: 'vbox',
734 align: 'stretch',
735 },
736 items: [
737 {
738 xtype: 'pveBackedGuests',
739 flex: 1,
740 layout: 'fit',
741 store: not_backed_store,
742 },
743 ],
744 },
745 ],
746 });
747 },
7d2fac4a
AL
748 });
749
e7ade592 750 Proxmox.Utils.monStoreErrors(me, store);
ad74d86c
DM
751
752 Ext.apply(me, {
753 store: store,
754 selModel: sm,
c4bb9405
DC
755 stateful: true,
756 stateId: 'grid-dc-backup',
ad74d86c 757 viewConfig: {
f6710aac 758 trackOver: false,
ad74d86c 759 },
cc911f63
DC
760 dockedItems: [{
761 xtype: 'toolbar',
762 overflowHandler: 'scroller',
763 dock: 'top',
764 items: [
765 {
766 text: gettext('Add'),
767 handler: function() {
768 var win = Ext.create('PVE.dc.BackupEdit', {});
769 win.on('destroy', reload);
770 win.show();
771 },
f6710aac 772 },
cc911f63
DC
773 '-',
774 remove_btn,
775 edit_btn,
776 detail_btn,
777 '-',
778 run_btn,
779 '->',
780 noBackupJobInfoButton,
781 '-',
782 {
783 xtype: 'proxmoxButton',
784 selModel: null,
785 text: gettext('Schedule Simulator'),
786 handler: () => {
787 let record = sm.getSelection()[0];
788 let schedule;
789 if (record) {
790 schedule = record.data.schedule;
791 }
792 Ext.create('PVE.window.ScheduleSimulator', {
793 autoShow: true,
794 schedule,
795 });
796 },
4d6215bc 797 },
cc911f63
DC
798 ],
799 }],
ad74d86c 800 columns: [
cfdc7ada
EK
801 {
802 header: gettext('Enabled'),
ae9e2161 803 width: 80,
cfdc7ada 804 dataIndex: 'enabled',
b98ffc0d 805 align: 'center',
bacb4173 806 renderer: Proxmox.Utils.renderEnabledIcon,
cfdc7ada
EK
807 sortable: true,
808 },
20d15804
DC
809 {
810 header: gettext('ID'),
811 dataIndex: 'id',
d9e05d34 812 hidden: true,
20d15804 813 },
ad74d86c
DM
814 {
815 header: gettext('Node'),
816 width: 100,
817 sortable: true,
818 dataIndex: 'node',
819 renderer: function(value) {
820 if (value) {
821 return value;
822 }
823 return me.allText;
f6710aac 824 },
ad74d86c
DM
825 },
826 {
20d15804
DC
827 header: gettext('Schedule'),
828 width: 150,
829 dataIndex: 'schedule',
ad74d86c 830 },
8aeb7146
TL
831 {
832 text: gettext('Next Run'),
833 dataIndex: 'next-run',
834 width: 150,
835 renderer: PVE.Utils.render_next_event,
836 },
ad74d86c
DM
837 {
838 header: gettext('Storage'),
839 width: 100,
840 sortable: true,
f6710aac 841 dataIndex: 'storage',
ad74d86c
DM
842 },
843 {
d9e05d34
DC
844 header: gettext('Comment'),
845 dataIndex: 'comment',
846 renderer: Ext.htmlEncode,
611ef475 847 sorter: (a, b) => (a.data.comment || '').localeCompare(b.data.comment || ''),
ad74d86c 848 flex: 1,
d9e05d34
DC
849 },
850 {
fcb267e3
TL
851 header: gettext('Retention'),
852 dataIndex: 'prune-backups',
853 renderer: v => v ? PVE.Parser.printPropertyString(v) : gettext('Fallback from storage config'),
d9e05d34 854 flex: 2,
fcb267e3
TL
855 },
856 {
857 header: gettext('Selection'),
858 flex: 4,
ad74d86c
DM
859 sortable: false,
860 dataIndex: 'vmid',
f6710aac
TL
861 renderer: PVE.Utils.render_backup_selection,
862 },
ad74d86c
DM
863 ],
864 listeners: {
c0b3df6e 865 activate: reload,
f6710aac
TL
866 itemdblclick: run_editor,
867 },
ad74d86c 868 });
60e049c2 869
ad74d86c 870 me.callParent();
f6710aac 871 },
ad74d86c 872}, function() {
ad74d86c
DM
873 Ext.define('pve-cluster-backup', {
874 extend: 'Ext.data.Model',
60e049c2 875 fields: [
12809bba
TL
876 'id',
877 'compress',
878 'dow',
879 'exclude',
880 'mailto',
881 'mode',
882 'node',
883 'pool',
c6801352 884 'prune-backups',
12809bba
TL
885 'starttime',
886 'storage',
887 'vmid',
cfdc7ada 888 { name: 'enabled', type: 'boolean' },
f6710aac
TL
889 { name: 'all', type: 'boolean' },
890 ],
ad74d86c 891 });
cfdc7ada 892});