]> git.proxmox.com Git - pve-manager.git/blob - www/manager6/grid/BackupView.js
api: add proxmox-firewall to versions pkg list
[pve-manager.git] / www / manager6 / grid / BackupView.js
1 Ext.define('PVE.grid.BackupView', {
2 extend: 'Ext.grid.GridPanel',
3
4 alias: ['widget.pveBackupView'],
5
6 onlineHelp: 'chapter_vzdump',
7
8 stateful: true,
9 stateId: 'grid-guest-backup',
10
11 initComponent: function() {
12 var me = this;
13
14 var nodename = me.pveSelNode.data.node;
15 if (!nodename) {
16 throw "no node name specified";
17 }
18
19 var vmid = me.pveSelNode.data.vmid;
20 if (!vmid) {
21 throw "no VM ID specified";
22 }
23
24 var vmtype = me.pveSelNode.data.type;
25 if (!vmtype) {
26 throw "no VM type specified";
27 }
28
29 var vmtypeFilter;
30 if (vmtype === 'lxc' || vmtype === 'openvz') {
31 vmtypeFilter = function(item) {
32 return PVE.Utils.volume_is_lxc_backup(item.data.volid, item.data.format);
33 };
34 } else if (vmtype === 'qemu') {
35 vmtypeFilter = function(item) {
36 return PVE.Utils.volume_is_qemu_backup(item.data.volid, item.data.format);
37 };
38 } else {
39 throw "unsupported VM type '" + vmtype + "'";
40 }
41
42 var searchFilter = {
43 property: 'volid',
44 value: '',
45 anyMatch: true,
46 caseSensitive: false,
47 };
48
49 var vmidFilter = {
50 property: 'vmid',
51 value: vmid,
52 exactMatch: true,
53 };
54
55 me.store = Ext.create('Ext.data.Store', {
56 model: 'pve-storage-content',
57 sorters: [
58 {
59 property: 'vmid',
60 direction: 'ASC',
61 },
62 {
63 property: 'vdate',
64 direction: 'DESC',
65 },
66 ],
67 filters: [
68 vmtypeFilter,
69 searchFilter,
70 vmidFilter,
71 ],
72 });
73
74 let updateFilter = function() {
75 me.store.filter([
76 vmtypeFilter,
77 searchFilter,
78 vmidFilter,
79 ]);
80 };
81
82 var reload = Ext.Function.createBuffered(function() {
83 if (me.store) {
84 me.store.load();
85 }
86 }, 100);
87
88 let isPBS = false;
89 var setStorage = function(storage) {
90 var url = '/api2/json/nodes/' + nodename + '/storage/' + storage + '/content';
91 url += '?content=backup';
92
93 me.store.setProxy({
94 type: 'proxmox',
95 url: url,
96 });
97
98 Proxmox.Utils.monStoreErrors(me.view, me.store, true);
99
100 reload();
101 };
102
103 let file_restore_btn;
104
105 var storagesel = Ext.create('PVE.form.StorageSelector', {
106 nodename: nodename,
107 fieldLabel: gettext('Storage'),
108 labelAlign: 'right',
109 storageContent: 'backup',
110 allowBlank: false,
111 listeners: {
112 change: function(f, value) {
113 let storage = f.getStore().findRecord('storage', value, 0, false, true, true);
114 if (storage) {
115 isPBS = storage.data.type === 'pbs';
116 me.getColumns().forEach((column) => {
117 let id = column.dataIndex;
118 if (id === 'verification' || id === 'encrypted') {
119 column.setHidden(!isPBS);
120 }
121 });
122 } else {
123 isPBS = false;
124 }
125 setStorage(value);
126 if (file_restore_btn) {
127 file_restore_btn.setHidden(!isPBS);
128 }
129 },
130 },
131 });
132
133 var storagefilter = Ext.create('Ext.form.field.Text', {
134 fieldLabel: gettext('Search'),
135 labelWidth: 50,
136 labelAlign: 'right',
137 enableKeyEvents: true,
138 value: searchFilter.value,
139 listeners: {
140 buffer: 500,
141 keyup: function(field) {
142 me.store.clearFilter(true);
143 searchFilter.value = field.getValue();
144 updateFilter();
145 },
146 },
147 });
148
149 var vmidfilterCB = Ext.create('Ext.form.field.Checkbox', {
150 boxLabel: gettext('Filter VMID'),
151 value: '1',
152 listeners: {
153 change: function(cb, value) {
154 vmidFilter.value = value ? vmid : '';
155 vmidFilter.exactMatch = !!value;
156 updateFilter();
157 },
158 },
159 });
160
161 var sm = Ext.create('Ext.selection.RowModel', {});
162
163 var backup_btn = Ext.create('Ext.button.Button', {
164 text: gettext('Backup now'),
165 handler: function() {
166 var win = Ext.create('PVE.window.Backup', {
167 nodename: nodename,
168 vmid: vmid,
169 vmtype: vmtype,
170 storage: storagesel.getValue(),
171 listeners: {
172 close: function() {
173 reload();
174 },
175 },
176 });
177 win.show();
178 },
179 });
180
181 var restore_btn = Ext.create('Proxmox.button.Button', {
182 text: gettext('Restore'),
183 disabled: true,
184 selModel: sm,
185 enableFn: function(rec) {
186 return !!rec;
187 },
188 handler: function(b, e, rec) {
189 let win = Ext.create('PVE.window.Restore', {
190 nodename: nodename,
191 vmid: vmid,
192 volid: rec.data.volid,
193 volidText: PVE.Utils.render_storage_content(rec.data.volid, {}, rec),
194 vmtype: vmtype,
195 isPBS: isPBS,
196 });
197 win.show();
198 win.on('destroy', reload);
199 },
200 });
201
202 let delete_btn = Ext.create('Proxmox.button.StdRemoveButton', {
203 selModel: sm,
204 dangerous: true,
205 delay: 5,
206 enableFn: rec => !rec?.data?.protected,
207 confirmMsg: ({ data }) => {
208 let msg = Ext.String.format(
209 gettext('Are you sure you want to remove entry {0}'), `'${data.volid}'`);
210 return msg + " " + gettext('This will permanently erase all data.');
211 },
212 getUrl: ({ data }) => `/nodes/${nodename}/storage/${storagesel.getValue()}/content/${data.volid}`,
213 callback: () => reload(),
214 });
215
216 let config_btn = Ext.create('Proxmox.button.Button', {
217 text: gettext('Show Configuration'),
218 disabled: true,
219 selModel: sm,
220 enableFn: rec => !!rec,
221 handler: function(b, e, rec) {
222 let storage = storagesel.getValue();
223 if (!storage) {
224 return;
225 }
226 Ext.create('PVE.window.BackupConfig', {
227 volume: rec.data.volid,
228 pveSelNode: me.pveSelNode,
229 autoShow: true,
230 });
231 },
232 });
233
234 // declared above so that the storage selector can change this buttons hidden state
235 file_restore_btn = Ext.create('Proxmox.button.Button', {
236 text: gettext('File Restore'),
237 disabled: true,
238 selModel: sm,
239 enableFn: rec => !!rec && isPBS,
240 hidden: !isPBS,
241 handler: function(b, e, rec) {
242 let storage = storagesel.getValue();
243 let isVMArchive = PVE.Utils.volume_is_qemu_backup(rec.data.volid, rec.data.format);
244 Ext.create('Proxmox.window.FileBrowser', {
245 title: gettext('File Restore') + " - " + rec.data.text,
246 listURL: `/api2/json/nodes/localhost/storage/${storage}/file-restore/list`,
247 downloadURL: `/api2/json/nodes/localhost/storage/${storage}/file-restore/download`,
248 extraParams: {
249 volume: rec.data.volid,
250 },
251 archive: isVMArchive ? 'all' : undefined,
252 autoShow: true,
253 });
254 },
255 });
256
257 Ext.apply(me, {
258 selModel: sm,
259 tbar: {
260 overflowHandler: 'scroller',
261 items: [
262 backup_btn,
263 '-',
264 restore_btn,
265 file_restore_btn,
266 config_btn,
267 {
268 xtype: 'proxmoxButton',
269 text: gettext('Edit Notes'),
270 disabled: true,
271 handler: function() {
272 let volid = sm.getSelection()[0].data.volid;
273 var storage = storagesel.getValue();
274 Ext.create('Proxmox.window.Edit', {
275 autoLoad: true,
276 width: 600,
277 height: 400,
278 resizable: true,
279 title: gettext('Notes'),
280 url: `/api2/extjs/nodes/${nodename}/storage/${storage}/content/${volid}`,
281 layout: 'fit',
282 items: [
283 {
284 xtype: 'textarea',
285 layout: 'fit',
286 name: 'notes',
287 height: '100%',
288 },
289 ],
290 listeners: {
291 destroy: () => reload(),
292 },
293 }).show();
294 },
295 },
296 {
297 xtype: 'proxmoxButton',
298 text: gettext('Change Protection'),
299 disabled: true,
300 handler: function(button, event, record) {
301 let volid = record.data.volid, storage = storagesel.getValue();
302 let url = `/api2/extjs/nodes/${nodename}/storage/${storage}/content/${volid}`;
303 let newProtection = record.data.protected ? 0 : 1;
304 Proxmox.Utils.API2Request({
305 url: url,
306 method: 'PUT',
307 waitMsgTarget: me,
308 params: {
309 'protected': newProtection,
310 },
311 failure: (response) => Ext.Msg.alert('Error', response.htmlStatus),
312 success: (response) => {
313 reload();
314 // propagate to remove button, fake for event as reload is to slow
315 record.data.protected = newProtection; // TODO: check if writing is OK!
316 sm.fireEvent('selectionchange', sm, [record]);
317 },
318 });
319 },
320 },
321 '-',
322 delete_btn,
323 '->',
324 storagesel,
325 '-',
326 vmidfilterCB,
327 storagefilter,
328 ],
329 },
330 columns: [
331 {
332 header: gettext('Name'),
333 flex: 2,
334 sortable: true,
335 renderer: PVE.Utils.render_storage_content,
336 dataIndex: 'volid',
337 },
338 {
339 header: gettext('Notes'),
340 dataIndex: 'notes',
341 flex: 1,
342 renderer: Ext.htmlEncode,
343 },
344 {
345 header: `<i class="fa fa-shield"></i>`,
346 tooltip: gettext('Protected'),
347 width: 30,
348 renderer: v => v ? `<i data-qtip="${gettext('Protected')}" class="fa fa-shield"></i>` : '',
349 sorter: (a, b) => (b.data.protected || 0) - (a.data.protected || 0),
350 dataIndex: 'protected',
351 },
352 {
353 header: gettext('Date'),
354 width: 150,
355 dataIndex: 'vdate',
356 },
357 {
358 header: gettext('Format'),
359 width: 100,
360 dataIndex: 'format',
361 },
362 {
363 header: gettext('Size'),
364 width: 100,
365 renderer: Proxmox.Utils.format_size,
366 dataIndex: 'size',
367 },
368 {
369 header: gettext('VMID'),
370 dataIndex: 'vmid',
371 hidden: true,
372 },
373 {
374 header: gettext('Encrypted'),
375 dataIndex: 'encrypted',
376 renderer: PVE.Utils.render_backup_encryption,
377 },
378 {
379 header: gettext('Verify State'),
380 dataIndex: 'verification',
381 renderer: PVE.Utils.render_backup_verification,
382 },
383 ],
384 });
385
386 me.callParent();
387 },
388 });