]> git.proxmox.com Git - pve-manager.git/blob - www/manager6/window/Backup.js
ui: LXC template upload: allow zstd compressed (.tar.zst) images
[pve-manager.git] / www / manager6 / window / Backup.js
1 Ext.define('PVE.window.Backup', {
2 extend: 'Ext.window.Window',
3
4 resizable: false,
5
6 initComponent: function() {
7 var me = this;
8
9 if (!me.nodename) {
10 throw "no node name specified";
11 }
12
13 if (!me.vmid) {
14 throw "no VM ID specified";
15 }
16
17 if (!me.vmtype) {
18 throw "no VM type specified";
19 }
20
21 let compressionSelector = Ext.create('PVE.form.CompressionSelector', {
22 name: 'compress',
23 value: 'zstd',
24 fieldLabel: gettext('Compression'),
25 });
26
27 let modeSelector = Ext.create('PVE.form.BackupModeSelector', {
28 fieldLabel: gettext('Mode'),
29 value: 'snapshot',
30 name: 'mode',
31 });
32
33 let mailtoField = Ext.create('Ext.form.field.Text', {
34 fieldLabel: gettext('Send email to'),
35 name: 'mailto',
36 emptyText: Proxmox.Utils.noneText,
37 });
38
39 const keepNames = [
40 ['keep-last', gettext('Keep Last')],
41 ['keep-hourly', gettext('Keep Hourly')],
42 ['keep-daily', gettext('Keep Daily')],
43 ['keep-weekly', gettext('Keep Weekly')],
44 ['keep-monthly', gettext('Keep Monthly')],
45 ['keep-yearly', gettext('Keep Yearly')],
46 ];
47
48 let pruneSettings = keepNames.map(
49 name => Ext.create('Ext.form.field.Display', {
50 name: name[0],
51 fieldLabel: name[1],
52 hidden: true,
53 }),
54 );
55
56 let removeCheckbox = Ext.create('Proxmox.form.Checkbox', {
57 name: 'remove',
58 checked: false,
59 hidden: true,
60 uncheckedValue: 0,
61 fieldLabel: gettext('Prune'),
62 autoEl: {
63 tag: 'div',
64 'data-qtip': gettext('Prune older backups afterwards'),
65 },
66 handler: function(checkbox, value) {
67 pruneSettings.forEach(field => field.setHidden(!value));
68 me.down('label[name="pruneLabel"]').setHidden(!value);
69 },
70 });
71
72 let initialDefaults = false;
73
74 var storagesel = Ext.create('PVE.form.StorageSelector', {
75 nodename: me.nodename,
76 name: 'storage',
77 fieldLabel: gettext('Storage'),
78 storageContent: 'backup',
79 allowBlank: false,
80 listeners: {
81 change: function(f, v) {
82 if (!initialDefaults) {
83 me.setLoading(false);
84 }
85
86 if (v === null || v === undefined || v === '') {
87 return;
88 }
89
90 let store = f.getStore();
91 let rec = store.findRecord('storage', v, 0, false, true, true);
92
93 if (rec && rec.data && rec.data.type === 'pbs') {
94 compressionSelector.setValue('zstd');
95 compressionSelector.setDisabled(true);
96 } else if (!compressionSelector.getEditable()) {
97 compressionSelector.setDisabled(false);
98 }
99
100 Proxmox.Utils.API2Request({
101 url: `/nodes/${me.nodename}/vzdump/defaults`,
102 method: 'GET',
103 params: {
104 storage: v,
105 },
106 waitMsgTarget: me,
107 success: function(response, opts) {
108 const data = response.result.data;
109
110 if (!initialDefaults && data.mailto !== undefined) {
111 mailtoField.setValue(data.mailto);
112 }
113 if (!initialDefaults && data.mode !== undefined) {
114 modeSelector.setValue(data.mode);
115 }
116 if (!initialDefaults && (data['notes-template'] ?? false)) {
117 me.down('field[name=notes-template]').setValue(
118 PVE.Utils.unEscapeNotesTemplate(data['notes-template']),
119 );
120 }
121
122 initialDefaults = true;
123
124 // always update storage dependent properties
125 if (data['prune-backups'] !== undefined) {
126 const keepParams = PVE.Parser.parsePropertyString(
127 data["prune-backups"],
128 );
129 if (!keepParams['keep-all']) {
130 removeCheckbox.setHidden(false);
131 pruneSettings.forEach(function(field) {
132 const keep = keepParams[field.name];
133 if (keep) {
134 field.setValue(keep);
135 } else {
136 field.reset();
137 }
138 });
139 return;
140 }
141 }
142
143 // no defaults or keep-all=1
144 removeCheckbox.setHidden(true);
145 removeCheckbox.setValue(false);
146 pruneSettings.forEach(field => field.reset());
147 },
148 failure: function(response, opts) {
149 initialDefaults = true;
150
151 removeCheckbox.setHidden(true);
152 removeCheckbox.setValue(false);
153 pruneSettings.forEach(field => field.reset());
154
155 Ext.Msg.alert(gettext('Error'), response.htmlStatus);
156 },
157 });
158 },
159 },
160 });
161
162 let protectedCheckbox = Ext.create('Proxmox.form.Checkbox', {
163 name: 'protected',
164 checked: false,
165 uncheckedValue: 0,
166 fieldLabel: gettext('Protected'),
167 });
168
169 me.formPanel = Ext.create('Proxmox.panel.InputPanel', {
170 bodyPadding: 10,
171 border: false,
172 column1: [
173 storagesel,
174 modeSelector,
175 protectedCheckbox,
176 ],
177 column2: [
178 compressionSelector,
179 mailtoField,
180 removeCheckbox,
181 ],
182 columnB: [
183 {
184 xtype: 'textareafield',
185 name: 'notes-template',
186 fieldLabel: gettext('Notes'),
187 anchor: '100%',
188 value: '{{guestname}}',
189 },
190 {
191 xtype: 'box',
192 style: {
193 margin: '8px 0px',
194 'line-height': '1.5em',
195 },
196 html: Ext.String.format(
197 gettext('Possible template variables are: {0}'),
198 PVE.Utils.notesTemplateVars.map(v => `<code>{{${v}}}</code>`).join(', '),
199 ),
200 },
201 {
202 xtype: 'label',
203 name: 'pruneLabel',
204 text: gettext('Storage Retention Configuration') + ':',
205 hidden: true,
206 },
207 {
208 layout: 'hbox',
209 border: false,
210 defaults: {
211 border: false,
212 layout: 'anchor',
213 flex: 1,
214 },
215 items: [
216 {
217 padding: '0 10 0 0',
218 defaults: {
219 labelWidth: 110,
220 },
221 items: [
222 pruneSettings[0],
223 pruneSettings[2],
224 pruneSettings[4],
225 ],
226 },
227 {
228 padding: '0 0 0 10',
229 defaults: {
230 labelWidth: 110,
231 },
232 items: [
233 pruneSettings[1],
234 pruneSettings[3],
235 pruneSettings[5],
236 ],
237 },
238 ],
239 },
240 ],
241 });
242
243 var submitBtn = Ext.create('Ext.Button', {
244 text: gettext('Backup'),
245 handler: function() {
246 var storage = storagesel.getValue();
247 let values = me.formPanel.getValues();
248 var params = {
249 storage: storage,
250 vmid: me.vmid,
251 mode: values.mode,
252 remove: values.remove,
253 };
254
255 if (values.mailto) {
256 params.mailto = values.mailto;
257 }
258
259 if (values.compress) {
260 params.compress = values.compress;
261 }
262
263 if (values.protected) {
264 params.protected = values.protected;
265 }
266
267 if (values['notes-template']) {
268 params['notes-template'] = PVE.Utils.escapeNotesTemplate(
269 values['notes-template']);
270 }
271
272 Proxmox.Utils.API2Request({
273 url: '/nodes/' + me.nodename + '/vzdump',
274 params: params,
275 method: 'POST',
276 failure: function(response, opts) {
277 Ext.Msg.alert('Error', response.htmlStatus);
278 },
279 success: function(response, options) {
280 // close later so we reload the grid
281 // after the task has completed
282 me.hide();
283
284 var upid = response.result.data;
285
286 var win = Ext.create('Proxmox.window.TaskViewer', {
287 upid: upid,
288 listeners: {
289 close: function() {
290 me.close();
291 },
292 },
293 });
294 win.show();
295 },
296 });
297 },
298 });
299
300 var helpBtn = Ext.create('Proxmox.button.Help', {
301 onlineHelp: 'chapter_vzdump',
302 listenToGlobalEvent: false,
303 hidden: false,
304 });
305
306 var title = gettext('Backup') + " " +
307 (me.vmtype === 'lxc' ? "CT" : "VM") +
308 " " + me.vmid;
309
310 Ext.apply(me, {
311 title: title,
312 modal: true,
313 layout: 'auto',
314 border: false,
315 width: 600,
316 items: [me.formPanel],
317 buttons: [helpBtn, '->', submitBtn],
318 listeners: {
319 afterrender: function() {
320 /// cleared within the storage selector's change listener
321 me.setLoading(gettext('Please wait...'));
322 storagesel.setValue(me.storage);
323 },
324 },
325 });
326
327 me.callParent();
328 },
329 });