]>
Commit | Line | Data |
---|---|---|
7bd89462 DM |
1 | Ext.define('PVE.grid.BackupView', { |
2 | extend: 'Ext.grid.GridPanel', | |
3 | ||
4 | alias: ['widget.pveBackupView'], | |
5 | ||
ba93a9c6 | 6 | onlineHelp: 'chapter_vzdump', |
7bd89462 | 7 | |
a61607a8 DC |
8 | stateful: true, |
9 | stateId: 'grid-guest-backup', | |
10 | ||
8058410f | 11 | initComponent: function() { |
7bd89462 DM |
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 | ||
2685b382 | 29 | var vmtypeFilter; |
3b8f599b | 30 | if (vmtype === 'lxc' || vmtype === 'openvz') { |
2685b382 | 31 | vmtypeFilter = function(item) { |
3b8f599b | 32 | return PVE.Utils.volume_is_lxc_backup(item.data.volid, item.data.format); |
7bd89462 DM |
33 | }; |
34 | } else if (vmtype === 'qemu') { | |
2685b382 | 35 | vmtypeFilter = function(item) { |
3b8f599b | 36 | return PVE.Utils.volume_is_qemu_backup(item.data.volid, item.data.format); |
7bd89462 DM |
37 | }; |
38 | } else { | |
39 | throw "unsupported VM type '" + vmtype + "'"; | |
40 | } | |
41 | ||
ce76a18a EK |
42 | var searchFilter = { |
43 | property: 'volid', | |
201b89db | 44 | value: '', |
ce76a18a | 45 | anyMatch: true, |
f6710aac | 46 | caseSensitive: false, |
ce76a18a EK |
47 | }; |
48 | ||
201b89db DC |
49 | var vmidFilter = { |
50 | property: 'vmid', | |
51 | value: vmid, | |
496eb1cc | 52 | exactMatch: true, |
201b89db DC |
53 | }; |
54 | ||
7bd89462 DM |
55 | me.store = Ext.create('Ext.data.Store', { |
56 | model: 'pve-storage-content', | |
9082d8d0 FE |
57 | sorters: [ |
58 | { | |
59 | property: 'vmid', | |
60 | direction: 'ASC', | |
61 | }, | |
62 | { | |
63 | property: 'vdate', | |
64 | direction: 'DESC', | |
65 | }, | |
66 | ], | |
ce76a18a | 67 | filters: [ |
2685b382 | 68 | vmtypeFilter, |
201b89db DC |
69 | searchFilter, |
70 | vmidFilter, | |
f6710aac | 71 | ], |
7bd89462 DM |
72 | }); |
73 | ||
201b89db DC |
74 | let updateFilter = function() { |
75 | me.store.filter([ | |
76 | vmtypeFilter, | |
77 | searchFilter, | |
78 | vmidFilter, | |
79 | ]); | |
80 | }; | |
81 | ||
7bd89462 | 82 | var reload = Ext.Function.createBuffered(function() { |
8dfd00cd | 83 | if (me.store) { |
7bd89462 DM |
84 | me.store.load(); |
85 | } | |
86 | }, 100); | |
87 | ||
878bbf0b | 88 | let isPBS = false; |
7bd89462 DM |
89 | var setStorage = function(storage) { |
90 | var url = '/api2/json/nodes/' + nodename + '/storage/' + storage + '/content'; | |
91 | url += '?content=backup'; | |
92 | ||
93 | me.store.setProxy({ | |
56a353b9 | 94 | type: 'proxmox', |
f6710aac | 95 | url: url, |
7bd89462 DM |
96 | }); |
97 | ||
98301c08 FE |
98 | Proxmox.Utils.monStoreErrors(me.view, me.store, true); |
99 | ||
7bd89462 DM |
100 | reload(); |
101 | }; | |
102 | ||
c72ff9a9 TL |
103 | let file_restore_btn; |
104 | ||
7bd89462 DM |
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) { | |
8267aa63 | 113 | let storage = f.getStore().findRecord('storage', value, 0, false, true, true); |
957a53bd | 114 | if (storage) { |
878bbf0b | 115 | isPBS = storage.data.type === 'pbs'; |
957a53bd | 116 | me.getColumns().forEach((column) => { |
3003a59d TL |
117 | let id = column.dataIndex; |
118 | if (id === 'verification' || id === 'encrypted') { | |
814c5942 | 119 | column.setHidden(!isPBS); |
957a53bd DC |
120 | } |
121 | }); | |
878bbf0b SR |
122 | } else { |
123 | isPBS = false; | |
957a53bd | 124 | } |
7bd89462 | 125 | setStorage(value); |
c72ff9a9 TL |
126 | if (file_restore_btn) { |
127 | file_restore_btn.setHidden(!isPBS); | |
128 | } | |
f6710aac TL |
129 | }, |
130 | }, | |
7bd89462 DM |
131 | }); |
132 | ||
133 | var storagefilter = Ext.create('Ext.form.field.Text', { | |
134 | fieldLabel: gettext('Search'), | |
135 | labelWidth: 50, | |
136 | labelAlign: 'right', | |
137 | enableKeyEvents: true, | |
ce76a18a | 138 | value: searchFilter.value, |
7bd89462 DM |
139 | listeners: { |
140 | buffer: 500, | |
141 | keyup: function(field) { | |
142 | me.store.clearFilter(true); | |
ce76a18a | 143 | searchFilter.value = field.getValue(); |
201b89db | 144 | updateFilter(); |
f6710aac TL |
145 | }, |
146 | }, | |
7bd89462 DM |
147 | }); |
148 | ||
201b89db | 149 | var vmidfilterCB = Ext.create('Ext.form.field.Checkbox', { |
a7c90b40 | 150 | boxLabel: gettext('Filter VMID'), |
201b89db DC |
151 | value: '1', |
152 | listeners: { | |
153 | change: function(cb, value) { | |
814c5942 | 154 | vmidFilter.value = value ? vmid : ''; |
ac27dd32 | 155 | vmidFilter.exactMatch = !!value; |
201b89db DC |
156 | updateFilter(); |
157 | }, | |
158 | }, | |
159 | }); | |
160 | ||
7bd89462 DM |
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() { | |
2a4971d8 | 166 | var win = Ext.create('PVE.window.Backup', { |
7bd89462 DM |
167 | nodename: nodename, |
168 | vmid: vmid, | |
169 | vmtype: vmtype, | |
e83e60bf | 170 | storage: storagesel.getValue(), |
8058410f | 171 | listeners: { |
e83e60bf EK |
172 | close: function() { |
173 | reload(); | |
f6710aac TL |
174 | }, |
175 | }, | |
7bd89462 DM |
176 | }); |
177 | win.show(); | |
f6710aac | 178 | }, |
7bd89462 DM |
179 | }); |
180 | ||
5720fafa | 181 | var restore_btn = Ext.create('Proxmox.button.Button', { |
7bd89462 DM |
182 | text: gettext('Restore'), |
183 | disabled: true, | |
184 | selModel: sm, | |
185 | enableFn: function(rec) { | |
186 | return !!rec; | |
187 | }, | |
188 | handler: function(b, e, rec) { | |
814c5942 | 189 | let win = Ext.create('PVE.window.Restore', { |
7bd89462 DM |
190 | nodename: nodename, |
191 | vmid: vmid, | |
192 | volid: rec.data.volid, | |
193 | volidText: PVE.Utils.render_storage_content(rec.data.volid, {}, rec), | |
f6710aac | 194 | vmtype: vmtype, |
878bbf0b | 195 | isPBS: isPBS, |
7bd89462 DM |
196 | }); |
197 | win.show(); | |
198 | win.on('destroy', reload); | |
f6710aac | 199 | }, |
7bd89462 DM |
200 | }); |
201 | ||
9bfa063e | 202 | let delete_btn = Ext.create('Proxmox.button.StdRemoveButton', { |
7bd89462 | 203 | selModel: sm, |
3b1ca3ff | 204 | dangerous: true, |
4d23cdef | 205 | delay: 5, |
2b289547 | 206 | enableFn: rec => !rec?.data?.protected, |
9bfa063e TL |
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.'); | |
f6710aac | 211 | }, |
9bfa063e TL |
212 | getUrl: ({ data }) => `/nodes/${nodename}/storage/${storagesel.getValue()}/content/${data.volid}`, |
213 | callback: () => reload(), | |
7bd89462 DM |
214 | }); |
215 | ||
9bfa063e | 216 | let config_btn = Ext.create('Proxmox.button.Button', { |
79f305de DC |
217 | text: gettext('Show Configuration'), |
218 | disabled: true, | |
219 | selModel: sm, | |
9bfa063e | 220 | enableFn: rec => !!rec, |
79f305de | 221 | handler: function(b, e, rec) { |
9bfa063e | 222 | let storage = storagesel.getValue(); |
79f305de DC |
223 | if (!storage) { |
224 | return; | |
225 | } | |
9bfa063e | 226 | Ext.create('PVE.window.BackupConfig', { |
6da4aea8 | 227 | volume: rec.data.volid, |
f6710aac | 228 | pveSelNode: me.pveSelNode, |
9bfa063e | 229 | autoShow: true, |
79f305de | 230 | }); |
f6710aac | 231 | }, |
79f305de DC |
232 | }); |
233 | ||
c72ff9a9 TL |
234 | // declared above so that the storage selector can change this buttons hidden state |
235 | file_restore_btn = Ext.create('Proxmox.button.Button', { | |
84ae9c8a SR |
236 | text: gettext('File Restore'), |
237 | disabled: true, | |
238 | selModel: sm, | |
9bfa063e | 239 | enableFn: rec => !!rec && isPBS, |
c72ff9a9 | 240 | hidden: !isPBS, |
84ae9c8a | 241 | handler: function(b, e, rec) { |
9bfa063e | 242 | let storage = storagesel.getValue(); |
6e339db1 | 243 | let isVMArchive = PVE.Utils.volume_is_qemu_backup(rec.data.volid, rec.data.format); |
84ae9c8a SR |
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: { | |
4c02fcfd | 249 | volume: rec.data.volid, |
84ae9c8a | 250 | }, |
6e339db1 TL |
251 | archive: isVMArchive ? 'all' : undefined, |
252 | autoShow: true, | |
253 | }); | |
84ae9c8a SR |
254 | }, |
255 | }); | |
256 | ||
7bd89462 | 257 | Ext.apply(me, { |
7bd89462 | 258 | selModel: sm, |
201b89db DC |
259 | tbar: { |
260 | overflowHandler: 'scroller', | |
a6d960f1 DC |
261 | items: [ |
262 | backup_btn, | |
fbbb2a14 | 263 | '-', |
6e339db1 | 264 | restore_btn, |
84ae9c8a | 265 | file_restore_btn, |
6e339db1 | 266 | config_btn, |
a6d960f1 DC |
267 | { |
268 | xtype: 'proxmoxButton', | |
269 | text: gettext('Edit Notes'), | |
270 | disabled: true, | |
a6d960f1 DC |
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, | |
fbbb2a14 | 279 | title: gettext('Notes'), |
a6d960f1 DC |
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: { | |
fbbb2a14 | 291 | destroy: () => reload(), |
a6d960f1 DC |
292 | }, |
293 | }).show(); | |
294 | }, | |
295 | }, | |
b4818ea7 FE |
296 | { |
297 | xtype: 'proxmoxButton', | |
298 | text: gettext('Change Protection'), | |
299 | disabled: true, | |
300 | handler: function(button, event, record) { | |
9bfa063e TL |
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; | |
b4818ea7 FE |
304 | Proxmox.Utils.API2Request({ |
305 | url: url, | |
306 | method: 'PUT', | |
307 | waitMsgTarget: me, | |
9bfa063e TL |
308 | params: { |
309 | 'protected': newProtection, | |
310 | }, | |
b4818ea7 | 311 | failure: (response) => Ext.Msg.alert('Error', response.htmlStatus), |
2b289547 TL |
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 | }, | |
b4818ea7 FE |
318 | }); |
319 | }, | |
320 | }, | |
6e339db1 TL |
321 | '-', |
322 | delete_btn, | |
a6d960f1 DC |
323 | '->', |
324 | storagesel, | |
325 | '-', | |
326 | vmidfilterCB, | |
f6710aac | 327 | storagefilter, |
a6d960f1 | 328 | ], |
201b89db | 329 | }, |
7bd89462 DM |
330 | columns: [ |
331 | { | |
332 | header: gettext('Name'), | |
425e05d7 | 333 | flex: 2, |
7bd89462 DM |
334 | sortable: true, |
335 | renderer: PVE.Utils.render_storage_content, | |
f6710aac | 336 | dataIndex: 'volid', |
7bd89462 | 337 | }, |
425e05d7 | 338 | { |
ef402242 DC |
339 | header: gettext('Notes'), |
340 | dataIndex: 'notes', | |
425e05d7 TL |
341 | flex: 1, |
342 | renderer: Ext.htmlEncode, | |
343 | }, | |
61cf2658 | 344 | { |
528c24b5 TL |
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>` : '', | |
bc54c473 | 349 | sorter: (a, b) => (b.data.protected || 0) - (a.data.protected || 0), |
61cf2658 FE |
350 | dataIndex: 'protected', |
351 | }, | |
afd3b071 TL |
352 | { |
353 | header: gettext('Date'), | |
354 | width: 150, | |
f6710aac | 355 | dataIndex: 'vdate', |
afd3b071 | 356 | }, |
7bd89462 DM |
357 | { |
358 | header: gettext('Format'), | |
359 | width: 100, | |
f6710aac | 360 | dataIndex: 'format', |
7bd89462 DM |
361 | }, |
362 | { | |
363 | header: gettext('Size'), | |
364 | width: 100, | |
e7ade592 | 365 | renderer: Proxmox.Utils.format_size, |
f6710aac | 366 | dataIndex: 'size', |
201b89db DC |
367 | }, |
368 | { | |
369 | header: gettext('VMID'), | |
370 | dataIndex: 'vmid', | |
371 | hidden: true, | |
372 | }, | |
3003a59d TL |
373 | { |
374 | header: gettext('Encrypted'), | |
375 | dataIndex: 'encrypted', | |
376 | renderer: PVE.Utils.render_backup_encryption, | |
377 | }, | |
957a53bd DC |
378 | { |
379 | header: gettext('Verify State'), | |
380 | dataIndex: 'verification', | |
770d614f | 381 | renderer: PVE.Utils.render_backup_verification, |
f6710aac TL |
382 | }, |
383 | ], | |
7bd89462 DM |
384 | }); |
385 | ||
386 | me.callParent(); | |
f6710aac | 387 | }, |
7bd89462 | 388 | }); |