]>
git.proxmox.com Git - proxmox-backup.git/blob - www/tape/window/TapeRestore.js
1 Ext
.define('PBS.TapeManagement.TapeRestoreWindow', {
2 extend
: 'Ext.window.Window',
3 alias
: 'widget.pbsTapeRestoreWindow',
4 mixins
: ['Proxmox.Mixin.CBind'],
6 title
: gettext('Restore Media-Set'),
11 url
: '/api2/extjs/tape/restore',
21 cbindData: function(config
) {
23 if (me
.prefilter
!== undefined) {
24 me
.title
= gettext('Restore Snapshot(s)');
35 singleDatastore
: true,
38 singleSelectorLabel
: get =>
39 get('singleDatastore') ? gettext('Target Datastore') : gettext('Default Datastore'),
40 singleSelectorEmptyText
: get => get('singleDatastore') ? '' : Proxmox
.Utils
.NoneText
,
45 xclass
: 'Ext.app.ViewController',
47 panelIsValid: function(panel
) {
48 return panel
.query('[isFormField]').every(field
=> field
.isValid());
51 changeMediaSet: function(field
, value
) {
53 let vm
= me
.getViewModel();
54 vm
.set('uuid', value
);
58 checkValidity: function() {
61 let tabpanel
= me
.lookup('tabpanel');
63 return; // can get triggered early, when the tabpanel is not yet available
65 let items
= tabpanel
.items
;
67 let indexOfActiveTab
= items
.indexOf(tabpanel
.getActiveTab());
68 let indexOfLastValidTab
= 0;
70 let checkValidity
= true;
71 items
.each((panel
) => {
73 panel
.setDisabled(false);
74 indexOfLastValidTab
= items
.indexOf(panel
);
75 if (!me
.panelIsValid(panel
)) {
76 checkValidity
= false;
79 panel
.setDisabled(true);
85 if (indexOfLastValidTab
< indexOfActiveTab
) {
86 tabpanel
.setActiveTab(indexOfLastValidTab
);
88 me
.setButtonState(tabpanel
.getActiveTab());
92 setButtonState: function(panel
) {
94 let isValid
= me
.panelIsValid(panel
);
95 let nextButton
= me
.lookup('nextButton');
96 let finishButton
= me
.lookup('finishButton');
97 nextButton
.setDisabled(!isValid
);
98 finishButton
.setDisabled(!isValid
);
101 changeButtonVisibility: function(tabpanel
, newItem
) {
103 let items
= tabpanel
.items
;
105 let backButton
= me
.lookup('backButton');
106 let nextButton
= me
.lookup('nextButton');
107 let finishButton
= me
.lookup('finishButton');
109 let isLast
= items
.last() === newItem
;
110 let isFirst
= items
.first() === newItem
;
112 backButton
.setVisible(!isFirst
);
113 nextButton
.setVisible(!isLast
);
114 finishButton
.setVisible(isLast
);
116 me
.setButtonState(newItem
);
119 previousTab: function() {
121 let tabpanel
= me
.lookup('tabpanel');
122 let index
= tabpanel
.items
.indexOf(tabpanel
.getActiveTab());
123 tabpanel
.setActiveTab(index
- 1);
126 nextTab: function() {
128 let tabpanel
= me
.lookup('tabpanel');
129 let index
= tabpanel
.items
.indexOf(tabpanel
.getActiveTab());
130 tabpanel
.setActiveTab(index
+ 1);
133 getValues: function() {
138 let tabpanel
= me
.lookup('tabpanel');
142 Proxmox
.Utils
.assemble_field_data(values
, panel
.getValues()));
149 let view
= me
.getView();
151 let values
= me
.getValues();
153 let method
= view
.method
;
155 Proxmox
.Utils
.API2Request({
160 failure: function(response
, options
) {
161 Ext
.Msg
.alert(gettext('Error'), response
.htmlStatus
);
163 success: function(response
, options
) {
164 // keep around so we can trigger our close events when background action completes
167 Ext
.create('Proxmox.window.TaskViewer', {
169 upid
: response
.result
.data
,
171 destroy
: () => view
.close(),
178 updateDatastores: function(grid
, values
) {
180 if (values
=== 'all') {
184 values
.forEach((snapshotOrDatastore
) => {
185 let datastore
= snapshotOrDatastore
;
186 if (snapshotOrDatastore
.indexOf(':') !== -1) {
187 let snapshot
= snapshotOrDatastore
;
188 let match
= snapshot
.split(':');
189 datastore
= match
[0];
190 } datastores
[datastore
] = true;
193 me
.setDataStores(Object
.keys(datastores
));
196 setDataStores: function(datastores
, initial
) {
199 // save all datastores on the first setting, and restore them if we selected all
201 me
.datastores
= datastores
;
202 } else if (datastores
.length
=== 0) {
203 datastores
= me
.datastores
;
206 const singleDatastore
= !datastores
|| datastores
.length
<= 1;
207 me
.getViewModel().set('singleDatastore', singleDatastore
);
209 let grid
= me
.lookup('mappingGrid');
210 if (!singleDatastore
&& grid
) {
211 grid
.setDataStores(datastores
);
215 updateSnapshots: function() {
217 let view
= me
.getView();
218 let grid
= me
.lookup('snapshotGrid');
219 let vm
= me
.getViewModel();
220 let uuid
= vm
.get('uuid');
222 Proxmox
.Utils
.API2Request({
224 url
: `/tape/media/content?media-set=${uuid}`,
225 success: function(response
, opt
) {
227 for (const content
of response
.result
.data
) {
228 datastores
[content
.store
] = true;
230 me
.setDataStores(Object
.keys(datastores
), true);
231 if (response
.result
.data
.length
> 0) {
232 grid
.setDisabled(false);
233 grid
.setData(response
.result
.data
);
234 grid
.getSelectionModel().selectAll();
235 // we've shown a big list, center the window again
239 failure: function() {
240 // ignore failing api call, maybe catalog is missing
241 me
.setDataStores([], true);
246 init: function(view
) {
248 let vm
= me
.getViewModel();
250 vm
.set('uuid', view
.uuid
);
255 change
: 'checkValidity',
256 validitychange
: 'checkValidity',
259 tabchange
: 'changeButtonVisibility',
266 text
: gettext('Back'),
267 reference
: 'backButton',
268 handler
: 'previousTab',
272 text
: gettext('Next'),
273 reference
: 'nextButton',
277 text
: gettext('Restore'),
278 reference
: 'finishButton',
287 reference
: 'tabpanel',
292 title
: gettext('Snapshot Selection'),
294 onGetValues: function(values
) {
297 if (values
!== "all" &&
298 Ext
.isString(values
.snapshots
) &&
300 values
.snapshots
.indexOf(':') !== -1
302 values
.snapshots
= values
.snapshots
.split(',');
304 delete values
.snapshots
;
312 xtype
: 'pbsMediaSetSelector',
313 fieldLabel
: gettext('Media-Set'),
316 emptyText
: gettext('Select Media-Set to restore'),
325 change
: 'changeMediaSet',
329 xtype
: 'displayfield',
330 fieldLabel
: gettext('Media-Set'),
341 xtype
: 'displayfield',
342 fieldLabel
: gettext('Media-Set UUID'),
355 xtype
: 'pbsTapeSnapshotGrid',
356 reference
: 'snapshotGrid',
359 disabled
: true, // will be shown/enabled on successful load
361 change
: 'updateDatastores',
364 prefilter
: '{prefilter}',
370 title
: gettext('Target'),
372 onGetValues: function(values
) {
375 if (values
.store
.toString() !== "") {
376 datastores
.push(values
.store
);
380 if (values
.mapping
.toString() !== "") {
381 datastores
.push(values
.mapping
);
383 delete values
.mapping
;
385 values
.store
= datastores
.join(',');
391 xtype
: 'pmxUserSelector',
393 fieldLabel
: gettext('Notify User'),
394 emptyText
: gettext('Current User'),
398 renderer
: Ext
.String
.htmlEncode
,
401 xtype
: 'pbsAuthidSelector',
403 fieldLabel
: gettext('Owner'),
404 emptyText
: gettext('Current Auth ID'),
408 renderer
: Ext
.String
.htmlEncode
,
414 xtype
: 'pbsDriveSelector',
416 fieldLabel
: gettext('Drive'),
420 xtype
: 'pbsDataStoreSelector',
424 fieldLabel
: '{singleSelectorLabel}',
425 emptyText
: '{singleSelectorEmptyText}',
426 allowBlank
: '{!singleDatastore}',
429 change: function(field
, value
) {
430 this.up('window').lookup('mappingGrid').setNeedStores(!value
);
438 xtype
: 'displayfield',
439 fieldLabel
: gettext('Datastore Mapping'),
442 hidden
: '{singleDatastore}',
446 xtype
: 'pbsDataStoreMappingField',
448 reference
: 'mappingGrid',
450 defaultBindProperty
: 'value',
452 hidden
: '{singleDatastore}',
462 afterrender
: 'updateSnapshots',
466 Ext
.define('PBS.TapeManagement.DataStoreMappingGrid', {
467 extend
: 'Ext.grid.Panel',
468 alias
: 'widget.pbsDataStoreMappingField',
469 mixins
: ['Ext.form.field.Field'],
473 getValue: function() {
476 me
.getStore().each(rec
=> {
477 let { source
, target
} = rec
.data
;
478 if (target
&& target
!== "") {
479 datastores
.push(`${source}=${target}`);
483 return datastores
.join(',');
488 needStores
: false, // this determines if we need at least one valid mapping
491 emptyMeans
: get => get('needStores') ? Proxmox
.Utils
.NoneText
: Proxmox
.Utils
.defaultText
,
495 setNeedStores: function(needStores
) {
497 me
.getViewModel().set('needStores', needStores
);
502 setValue: function(value
) {
504 me
.setDataStores(value
);
508 getErrors: function(value
) {
512 if (me
.getViewModel().get('needStores')) {
514 me
.getStore().each(rec
=> {
515 if (rec
.data
.target
) {
521 let el
= me
.getActionEl();
523 me
.addCls(['x-form-trigger-wrap-default', 'x-form-trigger-wrap-invalid']);
524 let errorMsg
= gettext("Need at least one mapping");
526 el
.dom
.setAttribute('data-errorqtip', errorMsg
);
531 me
.removeCls(['x-form-trigger-wrap-default', 'x-form-trigger-wrap-invalid']);
533 el
.dom
.setAttribute('data-errorqtip', "");
538 setDataStores: function(datastores
) {
542 for (const datastore
of datastores
) {
549 me
.getStore().setData(data
);
560 text
: gettext('Source Datastore'),
565 text
: gettext('Target Datastore'),
566 xtype
: 'widgetcolumn',
570 xtype
: 'pbsDataStoreSelector',
574 emptyText
: '{emptyMeans}',
577 change: function(selector
, value
) {
579 let rec
= me
.getWidgetRecord();
583 rec
.set('target', value
);
584 me
.up('grid').checkChange();
592 Ext
.define('PBS.TapeManagement.SnapshotGrid', {
593 extend
: 'Ext.grid.Panel',
594 alias
: 'widget.pbsTapeSnapshotGrid',
595 mixins
: ['Ext.form.field.Field'],
597 getValue: function() {
601 let storeCounts
= {};
603 me
.getSelection().forEach((rec
) => {
604 let id
= rec
.get('id');
605 let store
= rec
.data
.store
;
606 let snap
= rec
.data
.snapshot
;
607 // only add if not filtered
608 if (me
.store
.findExact('id', id
) !== -1) {
609 snapshots
.push(`${store}:${snap}`);
610 if (storeCounts
[store
] === undefined) {
611 storeCounts
[store
] = 0;
613 storeCounts
[store
]++;
617 // getSource returns null if data is not filtered
618 let originalData
= me
.store
.getData().getSource() || me
.store
.getData();
620 if (snapshots
.length
=== originalData
.length
) {
624 let wholeStores
= [];
625 let wholeStoresSelected
= true;
626 for (const [store
, count
] of Object
.entries(storeCounts
)) {
627 if (me
.storeCounts
[store
] === count
) {
628 wholeStores
.push(store
);
630 wholeStoresSelected
= false;
635 if (wholeStoresSelected
) {
642 setValue: function(value
) {
648 getErrors: function(value
) {
650 if (me
.getSelection().length
< 1) {
651 me
.addCls(['x-form-trigger-wrap-default', 'x-form-trigger-wrap-invalid']);
652 let errorMsg
= gettext("Need at least one snapshot");
653 let el
= me
.getActionEl();
655 el
.dom
.setAttribute('data-errorqtip', errorMsg
);
660 me
.removeCls(['x-form-trigger-wrap-default', 'x-form-trigger-wrap-invalid']);
661 let el
= me
.getActionEl();
663 el
.dom
.setAttribute('data-errorqtip', "");
668 setData: function(records
) {
670 let storeCounts
= {};
671 records
.forEach((rec
) => {
672 let store
= rec
.store
;
673 if (storeCounts
[store
] === undefined) {
674 storeCounts
[store
] = 0;
676 storeCounts
[store
]++;
678 me
.storeCounts
= storeCounts
;
679 me
.getStore().setData(records
);
683 plugins
: 'gridfilters',
686 emptyText
: gettext('No Snapshots'),
690 selModel
: 'checkboxmodel',
692 sorters
: ['store', 'snapshot'],
698 selectionchange: function() {
699 // to trigger validity and error checks
711 text
: gettext('Source Datastore'),
719 text
: gettext('Snapshot'),
720 dataIndex
: 'snapshot',
728 initComponent: function() {
731 if (me
.prefilter
!== undefined) {
732 if (me
.prefilter
.store
!== undefined) {
733 me
.store
.filters
.add(
735 id
: 'x-gridfilter-store',
738 value
: [me
.prefilter
.store
],
743 if (me
.prefilter
.snapshot
!== undefined) {
744 me
.store
.filters
.add(
746 id
: 'x-gridfilter-snapshot',
747 property
: 'snapshot',
748 value
: me
.prefilter
.snapshot
,
754 me
.mon(me
.store
, 'filterchange', () => me
.checkChange());
758 Ext
.define('PBS.TapeManagement.MediaSetSelector', {
759 extend
: 'Proxmox.form.ComboGrid',
760 alias
: 'widget.pbsMediaSetSelector',
763 displayField
: 'media-set-name',
764 valueField
: 'media-set-uuid',
770 url
: '/api2/json/tape/media/media-sets',
773 idProperty
: 'media-set-uuid',
774 sorters
: ['pool', 'media-set-ctime'],
781 text
: gettext('Pool'),
786 text
: gettext('Name'),
787 dataIndex
: 'media-set-name',
791 text
: gettext('Media-Set UUID'),
792 dataIndex
: 'media-set-uuid',