]>
Commit | Line | Data |
---|---|---|
ad74d86c | 1 | Ext.define('PVE.dc.BackupEdit', { |
9fccc702 | 2 | extend: 'Proxmox.window.Edit', |
ad74d86c DM |
3 | alias: ['widget.pveDcBackupEdit'], |
4 | ||
fc03fcb8 TL |
5 | defaultFocus: undefined, |
6 | ||
ad74d86c | 7 | initComponent : function() { |
ad74d86c DM |
8 | var me = this; |
9 | ||
d5e771ce | 10 | me.isCreate = !me.jobid; |
ad74d86c DM |
11 | |
12 | var url; | |
13 | var method; | |
14 | ||
d5e771ce | 15 | if (me.isCreate) { |
ad74d86c DM |
16 | url = '/api2/extjs/cluster/backup'; |
17 | method = 'POST'; | |
18 | } else { | |
19 | url = '/api2/extjs/cluster/backup/' + me.jobid; | |
20 | method = 'PUT'; | |
21 | } | |
22 | ||
23 | var vmidField = Ext.create('Ext.form.field.Hidden', { | |
24 | name: 'vmid' | |
25 | }); | |
26 | ||
d5e771ce EK |
27 | /*jslint confusion: true*/ |
28 | // 'value' can be assigned a string or an array | |
09cacce7 TL |
29 | var selModeField = Ext.create('Proxmox.form.KVComboBox', { |
30 | xtype: 'proxmoxKVComboBox', | |
a26c8bf7 | 31 | comboItems: [ |
ad74d86c DM |
32 | ['include', gettext('Include selected VMs')], |
33 | ['all', gettext('All')], | |
34 | ['exclude', gettext('Exclude selected VMs')] | |
35 | ], | |
36 | fieldLabel: gettext('Selection mode'), | |
37 | name: 'selMode', | |
38 | value: '' | |
39 | }); | |
40 | ||
ad74d86c DM |
41 | var sm = Ext.create('Ext.selection.CheckboxModel', { |
42 | mode: 'SIMPLE', | |
43 | listeners: { | |
44 | selectionchange: function(model, selected) { | |
be8b69aa TL |
45 | var sel = []; |
46 | Ext.Array.each(selected, function(record) { | |
47 | sel.push(record.data.vmid); | |
48 | }); | |
49 | ||
50 | // to avoid endless recursion suspend the vmidField change | |
51 | // event temporary as it calls us again | |
52 | vmidField.suspendEvent('change'); | |
53 | vmidField.setValue(sel); | |
54 | vmidField.resumeEvent('change'); | |
ad74d86c DM |
55 | } |
56 | } | |
57 | }); | |
58 | ||
59 | var storagesel = Ext.create('PVE.form.StorageSelector', { | |
60 | fieldLabel: gettext('Storage'), | |
61 | nodename: 'localhost', | |
62 | storageContent: 'backup', | |
63 | allowBlank: false, | |
64 | name: 'storage' | |
65 | }); | |
66 | ||
67 | var store = new Ext.data.Store({ | |
68 | model: 'PVEResources', | |
69 | sorters: { | |
70 | property: 'vmid', | |
71 | order: 'ASC' | |
72 | } | |
73 | }); | |
74 | ||
75 | var vmgrid = Ext.createWidget('grid', { | |
76 | store: store, | |
77 | border: true, | |
78 | height: 300, | |
79 | selModel: sm, | |
80 | disabled: true, | |
81 | columns: [ | |
82 | { | |
83 | header: 'ID', | |
84 | dataIndex: 'vmid', | |
85 | width: 60 | |
86 | }, | |
87 | { | |
88 | header: gettext('Node'), | |
89 | dataIndex: 'node' | |
90 | }, | |
91 | { | |
92 | header: gettext('Status'), | |
93 | dataIndex: 'uptime', | |
94 | renderer: function(value) { | |
95 | if (value) { | |
e7ade592 | 96 | return Proxmox.Utils.runningText; |
ad74d86c | 97 | } else { |
e7ade592 | 98 | return Proxmox.Utils.stoppedText; |
ad74d86c DM |
99 | } |
100 | } | |
101 | }, | |
102 | { | |
103 | header: gettext('Name'), | |
104 | dataIndex: 'name', | |
105 | flex: 1 | |
106 | }, | |
107 | { | |
108 | header: gettext('Type'), | |
109 | dataIndex: 'type' | |
110 | } | |
111 | ] | |
112 | }); | |
113 | ||
114 | var nodesel = Ext.create('PVE.form.NodeSelector', { | |
115 | name: 'node', | |
116 | fieldLabel: gettext('Node'), | |
117 | allowBlank: true, | |
118 | editable: true, | |
119 | autoSelect: false, | |
120 | emptyText: '-- ' + gettext('All') + ' --', | |
121 | listeners: { | |
122 | change: function(f, value) { | |
123 | storagesel.setNodename(value || 'localhost'); | |
124 | var mode = selModeField.getValue(); | |
125 | store.clearFilter(); | |
126 | store.filterBy(function(rec) { | |
127 | return (!value || rec.get('node') === value); | |
128 | }); | |
129 | if (mode === 'all') { | |
130 | sm.selectAll(true); | |
131 | } | |
132 | } | |
133 | } | |
134 | }); | |
135 | ||
136 | var column1 = [ | |
137 | nodesel, | |
138 | storagesel, | |
139 | { | |
140 | xtype: 'pveDayOfWeekSelector', | |
141 | name: 'dow', | |
142 | fieldLabel: gettext('Day of week'), | |
143 | multiSelect: true, | |
144 | value: ['sat'], | |
145 | allowBlank: false | |
146 | }, | |
147 | { | |
148 | xtype: 'timefield', | |
149 | fieldLabel: gettext('Start Time'), | |
150 | name: 'starttime', | |
151 | format: 'H:i', | |
152 | value: '00:00', | |
153 | allowBlank: false | |
154 | }, | |
155 | selModeField | |
156 | ]; | |
157 | ||
158 | var column2 = [ | |
159 | { | |
160 | xtype: 'textfield', | |
161 | fieldLabel: gettext('Send email to'), | |
162 | name: 'mailto' | |
163 | }, | |
164 | { | |
165 | xtype: 'pveEmailNotificationSelector', | |
166 | fieldLabel: gettext('Email notification'), | |
167 | name: 'mailnotification', | |
d5e771ce EK |
168 | deleteEmpty: me.isCreate ? false : true, |
169 | value: me.isCreate ? 'always' : '' | |
ad74d86c DM |
170 | }, |
171 | { | |
172 | xtype: 'pveCompressionSelector', | |
173 | fieldLabel: gettext('Compression'), | |
174 | name: 'compress', | |
d5e771ce | 175 | deleteEmpty: me.isCreate ? false : true, |
be37a97e | 176 | value: 'lzo' |
ad74d86c DM |
177 | }, |
178 | { | |
179 | xtype: 'pveBackupModeSelector', | |
180 | fieldLabel: gettext('Mode'), | |
181 | value: 'snapshot', | |
182 | name: 'mode' | |
183 | }, | |
cfdc7ada | 184 | { |
896c0d50 | 185 | xtype: 'proxmoxcheckbox', |
cfdc7ada EK |
186 | fieldLabel: gettext('Enable'), |
187 | name: 'enabled', | |
188 | uncheckedValue: 0, | |
189 | defaultValue: 1, | |
190 | checked: true | |
191 | }, | |
ad74d86c DM |
192 | vmidField |
193 | ]; | |
d5e771ce | 194 | /*jslint confusion: false*/ |
ad74d86c DM |
195 | |
196 | var ipanel = Ext.create('PVE.panel.InputPanel', { | |
0de33b54 | 197 | onlineHelp: 'chapter_vzdump', |
ad74d86c DM |
198 | column1: column1, |
199 | column2: column2, | |
200 | onGetValues: function(values) { | |
201 | if (!values.node) { | |
d5e771ce | 202 | if (!me.isCreate) { |
e7ade592 | 203 | Proxmox.Utils.assemble_field_data(values, { 'delete': 'node' }); |
ad74d86c DM |
204 | } |
205 | delete values.node; | |
206 | } | |
207 | ||
208 | var selMode = values.selMode; | |
209 | delete values.selMode; | |
210 | ||
211 | if (selMode === 'all') { | |
212 | values.all = 1; | |
213 | values.exclude = ''; | |
214 | delete values.vmid; | |
215 | } else if (selMode === 'exclude') { | |
216 | values.all = 1; | |
217 | values.exclude = values.vmid; | |
218 | delete values.vmid; | |
219 | } | |
220 | return values; | |
221 | } | |
222 | }); | |
223 | ||
224 | var update_vmid_selection = function(list, mode) { | |
ad74d86c DM |
225 | if (mode !== 'all') { |
226 | sm.deselectAll(true); | |
227 | if (list) { | |
228 | Ext.Array.each(list.split(','), function(vmid) { | |
229 | var rec = store.findRecord('vmid', vmid); | |
230 | if (rec) { | |
231 | sm.select(rec, true); | |
232 | } | |
233 | }); | |
234 | } | |
235 | } | |
ad74d86c DM |
236 | }; |
237 | ||
238 | vmidField.on('change', function(f, value) { | |
239 | var mode = selModeField.getValue(); | |
240 | update_vmid_selection(value, mode); | |
241 | }); | |
242 | ||
243 | selModeField.on('change', function(f, value, oldValue) { | |
244 | if (value === 'all') { | |
245 | sm.selectAll(true); | |
246 | vmgrid.setDisabled(true); | |
247 | } else { | |
248 | vmgrid.setDisabled(false); | |
249 | } | |
250 | if (oldValue === 'all') { | |
251 | sm.deselectAll(true); | |
252 | vmidField.setValue(''); | |
253 | } | |
254 | var list = vmidField.getValue(); | |
255 | update_vmid_selection(list, value); | |
256 | }); | |
257 | ||
258 | var reload = function() { | |
259 | store.load({ | |
260 | params: { type: 'vm' }, | |
261 | callback: function() { | |
262 | var node = nodesel.getValue(); | |
263 | store.clearFilter(); | |
264 | store.filterBy(function(rec) { | |
163bf378 | 265 | return (!node || node.length === 0 || rec.get('node') === node); |
ad74d86c DM |
266 | }); |
267 | var list = vmidField.getValue(); | |
268 | var mode = selModeField.getValue(); | |
269 | if (mode === 'all') { | |
270 | sm.selectAll(true); | |
271 | } else { | |
272 | update_vmid_selection(list, mode); | |
273 | } | |
274 | } | |
275 | }); | |
276 | }; | |
277 | ||
278 | Ext.applyIf(me, { | |
279 | subject: gettext("Backup Job"), | |
280 | url: url, | |
281 | method: method, | |
282 | items: [ ipanel, vmgrid ] | |
283 | }); | |
284 | ||
285 | me.callParent(); | |
286 | ||
d5e771ce | 287 | if (me.isCreate) { |
ad74d86c DM |
288 | selModeField.setValue('include'); |
289 | } else { | |
290 | me.load({ | |
291 | success: function(response, options) { | |
292 | var data = response.result.data; | |
293 | ||
294 | data.dow = data.dow.split(','); | |
295 | ||
296 | if (data.all || data.exclude) { | |
297 | if (data.exclude) { | |
298 | data.vmid = data.exclude; | |
299 | data.selMode = 'exclude'; | |
300 | } else { | |
301 | data.vmid = ''; | |
302 | data.selMode = 'all'; | |
303 | } | |
304 | } else { | |
305 | data.selMode = 'include'; | |
306 | } | |
307 | ||
308 | me.setValues(data); | |
309 | } | |
310 | }); | |
311 | } | |
312 | ||
313 | reload(); | |
314 | } | |
315 | }); | |
316 | ||
317 | ||
318 | Ext.define('PVE.dc.BackupView', { | |
319 | extend: 'Ext.grid.GridPanel', | |
320 | ||
321 | alias: ['widget.pveDcBackupView'], | |
322 | ||
ba93a9c6 DC |
323 | onlineHelp: 'chapter_vzdump', |
324 | ||
ad74d86c DM |
325 | allText: '-- ' + gettext('All') + ' --', |
326 | allExceptText: gettext('All except {0}'), | |
327 | ||
328 | initComponent : function() { | |
329 | var me = this; | |
330 | ||
331 | var store = new Ext.data.Store({ | |
332 | model: 'pve-cluster-backup', | |
333 | proxy: { | |
334 | type: 'pve', | |
335 | url: "/api2/json/cluster/backup" | |
336 | } | |
337 | }); | |
338 | ||
339 | var reload = function() { | |
340 | store.load(); | |
341 | }; | |
342 | ||
343 | var sm = Ext.create('Ext.selection.RowModel', {}); | |
344 | ||
345 | var run_editor = function() { | |
346 | var rec = sm.getSelection()[0]; | |
347 | if (!rec) { | |
348 | return; | |
349 | } | |
350 | ||
351 | var win = Ext.create('PVE.dc.BackupEdit',{ | |
352 | jobid: rec.data.id | |
353 | }); | |
354 | win.on('destroy', reload); | |
355 | win.show(); | |
356 | }; | |
357 | ||
5720fafa | 358 | var edit_btn = new Proxmox.button.Button({ |
ad74d86c DM |
359 | text: gettext('Edit'), |
360 | disabled: true, | |
361 | selModel: sm, | |
362 | handler: run_editor | |
363 | }); | |
364 | ||
3b1ca3ff | 365 | var remove_btn = Ext.create('Proxmox.button.StdRemoveButton', { |
ad74d86c | 366 | selModel: sm, |
3b1ca3ff DC |
367 | baseurl: '/cluster/backup', |
368 | callback: function() { | |
369 | reload(); | |
ad74d86c DM |
370 | } |
371 | }); | |
372 | ||
e7ade592 | 373 | Proxmox.Utils.monStoreErrors(me, store); |
ad74d86c DM |
374 | |
375 | Ext.apply(me, { | |
376 | store: store, | |
377 | selModel: sm, | |
c4bb9405 DC |
378 | stateful: true, |
379 | stateId: 'grid-dc-backup', | |
ad74d86c DM |
380 | viewConfig: { |
381 | trackOver: false | |
382 | }, | |
383 | tbar: [ | |
384 | { | |
385 | text: gettext('Add'), | |
386 | handler: function() { | |
387 | var win = Ext.create('PVE.dc.BackupEdit',{}); | |
388 | win.on('destroy', reload); | |
389 | win.show(); | |
390 | } | |
391 | }, | |
392 | remove_btn, | |
393 | edit_btn | |
394 | ], | |
395 | columns: [ | |
cfdc7ada EK |
396 | { |
397 | header: gettext('Enabled'), | |
ae9e2161 | 398 | width: 80, |
cfdc7ada | 399 | dataIndex: 'enabled', |
370ed4c8 | 400 | xtype: 'checkcolumn', |
cfdc7ada | 401 | sortable: true, |
370ed4c8 DC |
402 | disabled: true, |
403 | disabledCls: 'x-item-enabled', | |
22f2f9d6 | 404 | stopSelection: false |
cfdc7ada | 405 | }, |
ad74d86c DM |
406 | { |
407 | header: gettext('Node'), | |
408 | width: 100, | |
409 | sortable: true, | |
410 | dataIndex: 'node', | |
411 | renderer: function(value) { | |
412 | if (value) { | |
413 | return value; | |
414 | } | |
415 | return me.allText; | |
416 | } | |
417 | }, | |
418 | { | |
419 | header: gettext('Day of week'), | |
420 | width: 200, | |
421 | sortable: false, | |
2bdf9dd3 DC |
422 | dataIndex: 'dow', |
423 | renderer: function(val) { | |
424 | var dows = ['sun', 'mon', 'tue', 'wed', 'thu', 'fri', 'sat']; | |
425 | var selected = []; | |
af3838c5 | 426 | var cur = -1; |
2bdf9dd3 | 427 | val.split(',').forEach(function(day){ |
af3838c5 | 428 | cur++; |
68f8c765 | 429 | var dow = (dows.indexOf(day)+6)%7; |
af3838c5 DC |
430 | if (cur === dow) { |
431 | if (selected.length === 0 || selected[selected.length-1] === 0) { | |
432 | selected.push(1); | |
433 | } else { | |
434 | selected[selected.length-1]++; | |
435 | } | |
436 | } else { | |
437 | while (cur < dow) { | |
438 | cur++; | |
439 | selected.push(0); | |
440 | } | |
441 | selected.push(1); | |
442 | } | |
2bdf9dd3 | 443 | }); |
af3838c5 DC |
444 | |
445 | cur = -1; | |
446 | var days = []; | |
447 | selected.forEach(function(item) { | |
448 | cur++; | |
449 | if (item > 2) { | |
450 | days.push(Ext.Date.dayNames[(cur+1)] + '-' + Ext.Date.dayNames[(cur+item)%7]); | |
451 | cur += item-1; | |
452 | } else if (item == 2) { | |
453 | days.push(Ext.Date.dayNames[cur+1]); | |
454 | days.push(Ext.Date.dayNames[(cur+2)%7]); | |
455 | cur++; | |
456 | } else if (item == 1) { | |
457 | days.push(Ext.Date.dayNames[(cur+1)%7]); | |
458 | } | |
a764c5f7 | 459 | }); |
af3838c5 | 460 | return days.join(', '); |
2bdf9dd3 | 461 | } |
ad74d86c DM |
462 | }, |
463 | { | |
464 | header: gettext('Start Time'), | |
465 | width: 60, | |
466 | sortable: true, | |
467 | dataIndex: 'starttime' | |
468 | }, | |
469 | { | |
470 | header: gettext('Storage'), | |
471 | width: 100, | |
472 | sortable: true, | |
473 | dataIndex: 'storage' | |
474 | }, | |
475 | { | |
476 | header: gettext('Selection'), | |
477 | flex: 1, | |
478 | sortable: false, | |
479 | dataIndex: 'vmid', | |
480 | renderer: function(value, metaData, record) { | |
481 | /*jslint confusion: true */ | |
482 | if (record.data.all) { | |
483 | if (record.data.exclude) { | |
484 | return Ext.String.format(me.allExceptText, record.data.exclude); | |
485 | } | |
486 | return me.allText; | |
487 | } | |
488 | if (record.data.vmid) { | |
489 | return record.data.vmid; | |
490 | } | |
491 | ||
492 | return "-"; | |
493 | } | |
494 | } | |
495 | ], | |
496 | listeners: { | |
c0b3df6e | 497 | activate: reload, |
ad74d86c DM |
498 | itemdblclick: run_editor |
499 | } | |
500 | }); | |
501 | ||
502 | me.callParent(); | |
503 | } | |
504 | }, function() { | |
505 | ||
506 | Ext.define('pve-cluster-backup', { | |
507 | extend: 'Ext.data.Model', | |
508 | fields: [ | |
509 | 'id', 'starttime', 'dow', | |
510 | 'storage', 'node', 'vmid', 'exclude', | |
511 | 'mailto', | |
cfdc7ada | 512 | { name: 'enabled', type: 'boolean' }, |
ad74d86c DM |
513 | { name: 'all', type: 'boolean' }, |
514 | { name: 'snapshot', type: 'boolean' }, | |
515 | { name: 'stop', type: 'boolean' }, | |
516 | { name: 'suspend', type: 'boolean' }, | |
517 | { name: 'compress', type: 'boolean' } | |
518 | ] | |
519 | }); | |
cfdc7ada | 520 | }); |