]> git.proxmox.com Git - proxmox-backup.git/blame - www/tape/BackupOverview.js
ui: tapeRestore: make window non-resizable
[proxmox-backup.git] / www / tape / BackupOverview.js
CommitLineData
07d6c096
DC
1Ext.define('PBS.TapeManagement.BackupOverview', {
2 extend: 'Ext.tree.Panel',
3 alias: 'widget.pbsBackupOverview',
4
5 controller: {
6 xclass: 'Ext.app.ViewController',
7
8 backup: function() {
9 let me = this;
10 Ext.create('PBS.TapeManagement.TapeBackupWindow', {
11 listeners: {
12 destroy: function() {
13 me.reload();
14 },
15 },
104ae609
TL
16 autoShow: true,
17 });
07d6c096
DC
18 },
19
afb790db
DC
20 restore: function() {
21 Ext.create('PBS.TapeManagement.TapeRestoreWindow', {
22 autoShow: true,
23 });
24 },
25
104ae609 26 restoreBackups: function(view, rI, cI, item, e, rec) {
68b10226 27 let me = this;
68b10226 28
4923a76f 29 let mediaset = rec.data.is_media_set ? rec.data.text : rec.data['media-set'];
631e5509 30 Ext.create('PBS.TapeManagement.TapeRestoreWindow', {
f36c6593 31 autoShow: true,
104ae609
TL
32 uuid: rec.data['media-set-uuid'],
33 prefilter: rec.data.prefilter,
631e5509 34 mediaset,
f36c6593 35 });
631e5509 36 },
07d6c096 37
631e5509 38 loadContent: async function() {
69ebbec4 39 let me = this;
3006d70e 40 let content_response = await Proxmox.Async.api2({
159100b9 41 url: '/api2/extjs/tape/media/list?update-status=false',
631e5509
DC
42 });
43 let data = {};
44
45 for (const entry of content_response.result.data) {
46 let pool = entry.pool;
3ddbab61
DC
47 if (pool === undefined) {
48 continue; // pools not belonging to a pool cannot contain data
49 }
631e5509 50 let media_set = entry['media-set-name'];
32b88d92
DC
51 if (media_set === undefined) {
52 continue; // tape does not belong to media-set (yet))
53 }
631e5509
DC
54 if (data[pool] === undefined) {
55 data[pool] = {};
56 }
07d6c096 57
69ebbec4
DC
58 if (data[pool][media_set] === undefined) {
59 data[pool][media_set] = entry;
60 data[pool][media_set].text = media_set;
f9b824ac 61 data[pool][media_set].restore = true;
69ebbec4
DC
62 data[pool][media_set].tapes = 1;
63 data[pool][media_set]['seq-nr'] = undefined;
64 data[pool][media_set].is_media_set = true;
f9b824ac 65 data[pool][media_set].typeText = 'media-set';
69ebbec4
DC
66 } else {
67 data[pool][media_set].tapes++;
631e5509 68 }
631e5509 69 }
07d6c096 70
631e5509
DC
71 let list = [];
72
69ebbec4
DC
73 for (const [pool, media_sets] of Object.entries(data)) {
74 let pool_entry = Ext.create('Ext.data.TreeModel', {
631e5509 75 text: pool,
2914e99f 76 iconCls: 'fa fa-object-group',
927ebc70 77 expanded: true,
631e5509 78 leaf: false,
69ebbec4
DC
79 });
80
81 let children = [];
82
83 for (const media_set of Object.values(media_sets)) {
84 let entry = Ext.create('Ext.data.TreeModel', media_set);
85 entry.on('beforeexpand', (node) => me.beforeExpand(node));
86 children.push(entry);
07d6c096 87 }
69ebbec4
DC
88
89 pool_entry.set('children', children);
631e5509
DC
90 list.push(pool_entry);
91 }
07d6c096 92
631e5509
DC
93 return list;
94 },
95
96 reload: async function() {
97 let me = this;
98 let view = me.getView();
99
100 Proxmox.Utils.setErrorMask(view, true);
101
102 try {
103 let list = await me.loadContent();
07d6c096
DC
104
105 view.setRootNode({
106 expanded: true,
107 children: list,
108 });
109
110 Proxmox.Utils.setErrorMask(view, false);
111 } catch (error) {
112 Proxmox.Utils.setErrorMask(view, error.toString());
113 }
114 },
69ebbec4
DC
115
116 loadMediaSet: async function(node) {
117 let me = this;
118 let view = me.getView();
119
120 Proxmox.Utils.setErrorMask(view, true);
00a5072a
DC
121 const media_set_uuid = node.data['media-set-uuid'];
122 const media_set = node.data.text;
69ebbec4
DC
123
124 try {
3006d70e 125 let list = await Proxmox.Async.api2({
69ebbec4
DC
126 method: 'GET',
127 url: `/api2/extjs/tape/media/content`,
128 params: {
00a5072a 129 'media-set': media_set_uuid,
69ebbec4
DC
130 },
131 });
132
0af8c26b
DC
133 list.result.data.sort(function(a, b) {
134 let storeRes = a.store.localeCompare(b.store);
135 if (storeRes === 0) {
136 return a.snapshot.localeCompare(b.snapshot);
137 } else {
138 return storeRes;
139 }
140 });
69ebbec4 141
0af8c26b 142 let stores = {};
dfde34e6 143
69ebbec4
DC
144 for (let entry of list.result.data) {
145 entry.text = entry.snapshot;
4db4b970 146 entry.restore = true;
69ebbec4
DC
147 entry.leaf = true;
148 entry.children = [];
00a5072a 149 entry['media-set'] = media_set;
4923a76f
DC
150 entry.prefilter = {
151 store: entry.store,
152 snapshot: entry.snapshot,
153 };
69ebbec4
DC
154 let iconCls = PBS.Utils.get_type_icon_cls(entry.snapshot);
155 if (iconCls !== '') {
156 entry.iconCls = `fa ${iconCls}`;
157 }
dfde34e6 158
0af8c26b 159 let store = entry.store;
dfde34e6 160 let tape = entry['label-text'];
0af8c26b
DC
161 if (stores[store] === undefined) {
162 stores[store] = {
163 text: store,
164 'media-set-uuid': entry['media-set-uuid'],
165 iconCls: 'fa fa-database',
f9b824ac 166 typeText: 'datastore',
e0168997
DC
167 restore: true,
168 'media-set': media_set,
169 prefilter: {
170 store,
171 },
0af8c26b
DC
172 tapes: {},
173 };
174 }
175
176 if (stores[store].tapes[tape] === undefined) {
177 stores[store].tapes[tape] = {
dfde34e6
DC
178 text: tape,
179 'media-set-uuid': entry['media-set-uuid'],
180 'seq-nr': entry['seq-nr'],
181 iconCls: 'pbs-icon-tape',
182 expanded: true,
183 children: [],
184 };
185 }
d938c933
DC
186 let [type, group, _id] = PBS.Utils.parse_snapshot_id(entry.snapshot);
187
0af8c26b 188 let children = stores[store].tapes[tape].children;
d938c933
DC
189 let text = `${type}/${group}`;
190 if (children.length < 1 || children[children.length - 1].text !== text) {
191 children.push({
192 text,
193 'media-set-uuid': entry['media-set-uuid'],
194 leaf: false,
47e5cbdb
DC
195 restore: true,
196 prefilter: {
197 store,
198 snapshot: `${type}/${group}/`,
199 },
200 'media-set': media_set,
d938c933 201 iconCls: `fa ${iconCls}`,
f9b824ac 202 typeText: `group`,
d938c933
DC
203 children: [],
204 });
205 }
206 children[children.length - 1].children.push(entry);
dfde34e6
DC
207 }
208
0af8c26b 209 let storeList = Object.values(stores);
657c47db 210 let storeNameList = Object.keys(stores);
0af8c26b
DC
211 let expand = storeList.length === 1;
212 for (const store of storeList) {
213 store.children = Object.values(store.tapes);
214 store.expanded = expand;
215 delete store.tapes;
216 node.appendChild(store);
69ebbec4
DC
217 }
218
219 if (list.result.data.length === 0) {
220 node.set('leaf', true);
221 }
222
223 node.set('loaded', true);
657c47db 224 node.set('datastores', storeNameList);
69ebbec4
DC
225 Proxmox.Utils.setErrorMask(view, false);
226 node.expand();
3006d70e 227 } catch (response) {
00286540 228 Proxmox.Utils.setErrorMask(view, false);
3006d70e 229 Ext.Msg.alert('Error', response.result.message.toString());
69ebbec4
DC
230 }
231 },
232
233 beforeExpand: function(node, e) {
234 let me = this;
235 if (node.isLoaded()) {
236 return true;
237 }
238
239 me.loadMediaSet(node);
240
241 return false;
242 },
07d6c096
DC
243 },
244
245 listeners: {
246 activate: 'reload',
247 },
248
249 store: {
07d6c096 250 data: [],
631e5509 251 sorters: function(a, b) {
69ebbec4
DC
252 if (a.data.is_media_set && b.data.is_media_set) {
253 return a.data['media-set-ctime'] - b.data['media-set-ctime'];
631e5509
DC
254 } else {
255 return a.data.text.localeCompare(b.data.text);
256 }
257 },
07d6c096
DC
258 },
259
260 rootVisible: false,
261
262 tbar: [
263 {
264 text: gettext('Reload'),
07875ce1 265 iconCls: 'fa fa-refresh',
07d6c096
DC
266 handler: 'reload',
267 },
268 '-',
269 {
270 text: gettext('New Backup'),
01a99f56 271 iconCls: 'fa fa-floppy-o',
07d6c096
DC
272 handler: 'backup',
273 },
01a99f56 274 '-',
afb790db 275 {
e21a15ab 276 text: gettext('Restore'),
01a99f56 277 iconCls: 'fa fa-undo',
afb790db
DC
278 handler: 'restore',
279 },
07d6c096
DC
280 ],
281
282 columns: [
283 {
284 xtype: 'treecolumn',
f44254b4 285 text: gettext('Pool/Media-Set/Snapshot'),
631e5509
DC
286 dataIndex: 'text',
287 sortable: false,
07d6c096
DC
288 flex: 3,
289 },
4db4b970 290 {
1336be16 291 header: gettext('Restore'),
4db4b970 292 xtype: 'actioncolumn',
f9b824ac 293 dataIndex: 'text',
4db4b970
DC
294 items: [
295 {
104ae609 296 handler: 'restoreBackups',
f9b824ac
TL
297 getTip: (v, m, rec) => {
298 let typeText = rec.get('typeText');
299 if (typeText) {
300 v = `${typeText} '${v}'`;
301 }
302 return Ext.String.format(gettext("Open restore wizard for {0}"), v);
303 },
4db4b970 304 getClass: (v, m, rec) => rec.data.restore ? 'fa fa-fw fa-undo' : 'pmx-hidden',
4e0faf5e 305 isActionDisabled: (v, r, c, i, rec) => !rec.data.restore,
4db4b970
DC
306 },
307 ],
308 },
69ebbec4 309 {
1576c7a0 310 text: gettext('Tapes'),
69ebbec4
DC
311 dataIndex: 'tapes',
312 sortable: false,
69ebbec4
DC
313 },
314 {
1576c7a0 315 text: gettext('Seq. Nr.'),
69ebbec4
DC
316 dataIndex: 'seq-nr',
317 sortable: false,
69ebbec4 318 },
07d6c096 319 {
f44254b4 320 text: gettext('Media-Set UUID'),
69ebbec4 321 dataIndex: 'media-set-uuid',
1576c7a0 322 hidden: true,
631e5509 323 sortable: false,
1576c7a0 324 width: 280,
07d6c096
DC
325 },
326 ],
327});
328