]>
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 | ||
7bd89462 DM |
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 | ||
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 EK |
45 | anyMatch: true, |
46 | caseSensitive: false | |
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', | |
57 | sorters: { | |
58 | property: 'volid', | |
59 | order: 'DESC' | |
60 | }, | |
ce76a18a | 61 | filters: [ |
2685b382 | 62 | vmtypeFilter, |
201b89db DC |
63 | searchFilter, |
64 | vmidFilter, | |
ce76a18a | 65 | ] |
7bd89462 DM |
66 | }); |
67 | ||
201b89db DC |
68 | let updateFilter = function() { |
69 | me.store.filter([ | |
70 | vmtypeFilter, | |
71 | searchFilter, | |
72 | vmidFilter, | |
73 | ]); | |
74 | }; | |
75 | ||
7bd89462 | 76 | var reload = Ext.Function.createBuffered(function() { |
8dfd00cd | 77 | if (me.store) { |
7bd89462 DM |
78 | me.store.load(); |
79 | } | |
80 | }, 100); | |
81 | ||
82 | var setStorage = function(storage) { | |
83 | var url = '/api2/json/nodes/' + nodename + '/storage/' + storage + '/content'; | |
84 | url += '?content=backup'; | |
85 | ||
86 | me.store.setProxy({ | |
56a353b9 | 87 | type: 'proxmox', |
7bd89462 DM |
88 | url: url |
89 | }); | |
90 | ||
91 | reload(); | |
92 | }; | |
93 | ||
94 | var storagesel = Ext.create('PVE.form.StorageSelector', { | |
95 | nodename: nodename, | |
96 | fieldLabel: gettext('Storage'), | |
97 | labelAlign: 'right', | |
98 | storageContent: 'backup', | |
99 | allowBlank: false, | |
100 | listeners: { | |
101 | change: function(f, value) { | |
957a53bd DC |
102 | let storage = f.getStore().findRecord('storage', value); |
103 | if (storage) { | |
814c5942 | 104 | let isPBS = storage.data.type === 'pbs'; |
957a53bd DC |
105 | me.getColumns().forEach((column) => { |
106 | if (column.dataIndex === 'verification') { | |
814c5942 | 107 | column.setHidden(!isPBS); |
957a53bd DC |
108 | } |
109 | }); | |
110 | } | |
7bd89462 DM |
111 | setStorage(value); |
112 | } | |
113 | } | |
114 | }); | |
115 | ||
116 | var storagefilter = Ext.create('Ext.form.field.Text', { | |
117 | fieldLabel: gettext('Search'), | |
118 | labelWidth: 50, | |
119 | labelAlign: 'right', | |
120 | enableKeyEvents: true, | |
ce76a18a | 121 | value: searchFilter.value, |
7bd89462 DM |
122 | listeners: { |
123 | buffer: 500, | |
124 | keyup: function(field) { | |
125 | me.store.clearFilter(true); | |
ce76a18a | 126 | searchFilter.value = field.getValue(); |
201b89db | 127 | updateFilter(); |
7bd89462 DM |
128 | } |
129 | } | |
130 | }); | |
131 | ||
201b89db | 132 | var vmidfilterCB = Ext.create('Ext.form.field.Checkbox', { |
a7c90b40 | 133 | boxLabel: gettext('Filter VMID'), |
201b89db DC |
134 | value: '1', |
135 | listeners: { | |
136 | change: function(cb, value) { | |
814c5942 | 137 | vmidFilter.value = value ? vmid : ''; |
ac27dd32 | 138 | vmidFilter.exactMatch = !!value; |
201b89db DC |
139 | updateFilter(); |
140 | }, | |
141 | }, | |
142 | }); | |
143 | ||
7bd89462 DM |
144 | var sm = Ext.create('Ext.selection.RowModel', {}); |
145 | ||
146 | var backup_btn = Ext.create('Ext.button.Button', { | |
147 | text: gettext('Backup now'), | |
148 | handler: function() { | |
149 | var win = Ext.create('PVE.window.Backup', { | |
150 | nodename: nodename, | |
151 | vmid: vmid, | |
152 | vmtype: vmtype, | |
e83e60bf EK |
153 | storage: storagesel.getValue(), |
154 | listeners : { | |
155 | close: function() { | |
156 | reload(); | |
157 | } | |
158 | } | |
7bd89462 DM |
159 | }); |
160 | win.show(); | |
161 | } | |
162 | }); | |
163 | ||
5720fafa | 164 | var restore_btn = Ext.create('Proxmox.button.Button', { |
7bd89462 DM |
165 | text: gettext('Restore'), |
166 | disabled: true, | |
167 | selModel: sm, | |
168 | enableFn: function(rec) { | |
169 | return !!rec; | |
170 | }, | |
171 | handler: function(b, e, rec) { | |
814c5942 | 172 | let win = Ext.create('PVE.window.Restore', { |
7bd89462 DM |
173 | nodename: nodename, |
174 | vmid: vmid, | |
175 | volid: rec.data.volid, | |
176 | volidText: PVE.Utils.render_storage_content(rec.data.volid, {}, rec), | |
177 | vmtype: vmtype | |
178 | }); | |
179 | win.show(); | |
180 | win.on('destroy', reload); | |
181 | } | |
182 | }); | |
183 | ||
3b1ca3ff | 184 | var delete_btn = Ext.create('Proxmox.button.StdRemoveButton', { |
7bd89462 | 185 | selModel: sm, |
3b1ca3ff | 186 | dangerous: true, |
4d23cdef | 187 | delay: 5, |
7bd89462 DM |
188 | confirmMsg: function(rec) { |
189 | var msg = Ext.String.format(gettext('Are you sure you want to remove entry {0}'), | |
190 | "'" + rec.data.volid + "'"); | |
16152937 | 191 | msg += " " + gettext('This will permanently erase all data.'); |
7bd89462 DM |
192 | |
193 | return msg; | |
194 | }, | |
3b1ca3ff | 195 | getUrl: function(rec) { |
7bd89462 | 196 | var storage = storagesel.getValue(); |
3b1ca3ff DC |
197 | return '/nodes/' + nodename + '/storage/' + storage + '/content/' + rec.data.volid; |
198 | }, | |
199 | callback: function() { | |
200 | reload(); | |
7bd89462 DM |
201 | } |
202 | }); | |
203 | ||
5720fafa | 204 | var config_btn = Ext.create('Proxmox.button.Button', { |
79f305de DC |
205 | text: gettext('Show Configuration'), |
206 | disabled: true, | |
207 | selModel: sm, | |
208 | enableFn: function(rec) { | |
209 | return !!rec; | |
210 | }, | |
211 | handler: function(b, e, rec) { | |
212 | var storage = storagesel.getValue(); | |
213 | if (!storage) { | |
214 | return; | |
215 | } | |
216 | ||
6da4aea8 DC |
217 | var win = Ext.create('PVE.window.BackupConfig', { |
218 | volume: rec.data.volid, | |
219 | pveSelNode: me.pveSelNode | |
79f305de DC |
220 | }); |
221 | ||
6da4aea8 | 222 | win.show(); |
79f305de DC |
223 | } |
224 | }); | |
225 | ||
7bd89462 | 226 | Ext.apply(me, { |
7bd89462 | 227 | selModel: sm, |
201b89db DC |
228 | tbar: { |
229 | overflowHandler: 'scroller', | |
a7c90b40 | 230 | items: [ backup_btn, restore_btn, delete_btn,config_btn, '->', storagesel, '-', vmidfilterCB, storagefilter ], |
201b89db | 231 | }, |
7bd89462 DM |
232 | columns: [ |
233 | { | |
234 | header: gettext('Name'), | |
425e05d7 | 235 | flex: 2, |
7bd89462 DM |
236 | sortable: true, |
237 | renderer: PVE.Utils.render_storage_content, | |
238 | dataIndex: 'volid' | |
239 | }, | |
425e05d7 | 240 | { |
ef402242 DC |
241 | header: gettext('Notes'), |
242 | dataIndex: 'notes', | |
425e05d7 TL |
243 | flex: 1, |
244 | renderer: Ext.htmlEncode, | |
245 | }, | |
afd3b071 TL |
246 | { |
247 | header: gettext('Date'), | |
248 | width: 150, | |
249 | dataIndex: 'vdate' | |
250 | }, | |
7bd89462 DM |
251 | { |
252 | header: gettext('Format'), | |
253 | width: 100, | |
254 | dataIndex: 'format' | |
255 | }, | |
256 | { | |
257 | header: gettext('Size'), | |
258 | width: 100, | |
e7ade592 | 259 | renderer: Proxmox.Utils.format_size, |
7bd89462 | 260 | dataIndex: 'size' |
201b89db DC |
261 | }, |
262 | { | |
263 | header: gettext('VMID'), | |
264 | dataIndex: 'vmid', | |
265 | hidden: true, | |
266 | }, | |
957a53bd DC |
267 | { |
268 | header: gettext('Verify State'), | |
269 | dataIndex: 'verification', | |
270 | renderer: function(v) { | |
271 | let i = (cls, txt) => `<i class="fa fa-fw fa-${cls}"></i> ${txt}`; | |
272 | if (v === undefined || v === null) { | |
273 | return i('question-circle-o warning', gettext('None')); | |
274 | } | |
275 | let tip = "" | |
276 | let txt = gettext('Failed'); | |
277 | let iconCls = 'times critical'; | |
278 | if (v.state === 'ok') { | |
279 | txt = gettext('OK'); | |
280 | iconCls = 'check good'; | |
281 | let now = Date.now() / 1000; | |
282 | let task = Proxmox.Utils.parse_task_upid(v.upid); | |
c9a35168 TL |
283 | let verify_time = Proxmox.Utils.render_timestamp(task.starttime); |
284 | tip = `Last verify task started on ${verify_time}`; | |
957a53bd DC |
285 | if (now - v.starttime > 30 * 24 * 60 * 60) { |
286 | tip = `Last verify task over 30 days ago: ${verify_time}`; | |
287 | iconCls = 'check warning'; | |
288 | } | |
289 | } | |
290 | return `<span data-qtip="${tip}"> ${i(iconCls, txt)} </span>`; | |
291 | } | |
292 | } | |
8dfd00cd | 293 | ] |
7bd89462 DM |
294 | }); |
295 | ||
296 | me.callParent(); | |
297 | } | |
298 | }); |