]> git.proxmox.com Git - pve-manager.git/blob - www/manager6/window/Restore.js
ui: guest import: avoid one indentation level in deeply nested widget tree
[pve-manager.git] / www / manager6 / window / Restore.js
1 Ext.define('PVE.window.Restore', {
2 extend: 'Ext.window.Window', // fixme: Proxmox.window.Edit?
3
4 resizable: false,
5 width: 500,
6 modal: true,
7 layout: 'auto',
8 border: false,
9
10 controller: {
11 xclass: 'Ext.app.ViewController',
12 control: {
13 '#liveRestore': {
14 change: function(el, newVal) {
15 let liveWarning = this.lookupReference('liveWarning');
16 liveWarning.setHidden(!newVal);
17 let start = this.lookupReference('start');
18 start.setDisabled(newVal);
19 },
20 },
21 'form': {
22 validitychange: function(f, valid) {
23 this.lookupReference('doRestoreBtn').setDisabled(!valid);
24 },
25 },
26 },
27
28 doRestore: function() {
29 let me = this;
30 let view = me.getView();
31
32 let values = view.down('form').getForm().getValues();
33
34 let params = {
35 vmid: view.vmid || values.vmid,
36 force: view.vmid ? 1 : 0,
37 };
38 if (values.unique) {
39 params.unique = 1;
40 }
41 if (values.start && !values['live-restore']) {
42 params.start = 1;
43 }
44 if (values['live-restore']) {
45 params['live-restore'] = 1;
46 }
47 if (values.storage) {
48 params.storage = values.storage;
49 }
50
51 ['bwlimit', 'cores', 'name', 'memory', 'sockets'].forEach(opt => {
52 if ((values[opt] ?? '') !== '') {
53 params[opt] = values[opt];
54 }
55 });
56
57 if (params.name && view.vmtype === 'lxc') {
58 params.hostname = params.name;
59 delete params.name;
60 }
61
62 let confirmMsg;
63 if (view.vmtype === 'lxc') {
64 params.ostemplate = view.volid;
65 params.restore = 1;
66 if (values.unprivileged !== 'keep') {
67 params.unprivileged = values.unprivileged;
68 }
69 confirmMsg = Proxmox.Utils.format_task_description('vzrestore', params.vmid);
70 } else if (view.vmtype === 'qemu') {
71 params.archive = view.volid;
72 confirmMsg = Proxmox.Utils.format_task_description('qmrestore', params.vmid);
73 } else {
74 throw 'unknown VM type';
75 }
76
77 let executeRestore = () => {
78 Proxmox.Utils.API2Request({
79 url: `/nodes/${view.nodename}/${view.vmtype}`,
80 params: params,
81 method: 'POST',
82 waitMsgTarget: view,
83 failure: response => Ext.Msg.alert(gettext('Error'), response.htmlStatus),
84 success: function(response, options) {
85 Ext.create('Proxmox.window.TaskViewer', {
86 autoShow: true,
87 upid: response.result.data,
88 });
89 view.close();
90 },
91 });
92 };
93
94 if (view.vmid) {
95 confirmMsg += `. ${Ext.String.format(
96 gettext('This will permanently erase current {0} data.'),
97 view.vmtype === 'lxc' ? 'CT' : 'VM',
98 )}`;
99 if (view.vmtype === 'lxc') {
100 confirmMsg += `<br>${gettext('Mount point volumes are also erased.')}`;
101 }
102 Ext.Msg.confirm(gettext('Confirm'), confirmMsg, function(btn) {
103 if (btn === 'yes') {
104 executeRestore();
105 }
106 });
107 } else {
108 executeRestore();
109 }
110 },
111
112 afterRender: function() {
113 let view = this.getView();
114
115 Proxmox.Utils.API2Request({
116 url: `/nodes/${view.nodename}/vzdump/extractconfig`,
117 method: 'GET',
118 waitMsgTarget: view,
119 params: {
120 volume: view.volid,
121 },
122 failure: response => Ext.Msg.alert('Error', response.htmlStatus),
123 success: function(response, options) {
124 let allStoragesAvailable = true;
125
126 response.result.data.split('\n').forEach(line => {
127 let [_, key, value] = line.match(/^([^:]+):\s*(\S+)\s*$/) ?? [];
128
129 if (!key) {
130 return;
131 }
132
133 if (key === '#qmdump#map') {
134 let match = value.match(/^(\S+):(\S+):(\S*):(\S*):$/) ?? [];
135 // if a /dev/XYZ disk was backed up, ther is no storage hint
136 allStoragesAvailable &&= !!match[3] && !!PVE.data.ResourceStore.getById(
137 `storage/${view.nodename}/${match[3]}`);
138 } else if (key === 'name' || key === 'hostname') {
139 view.lookupReference('nameField').setEmptyText(value);
140 } else if (key === 'memory' || key === 'cores' || key === 'sockets') {
141 view.lookupReference(`${key}Field`).setEmptyText(value);
142 }
143 });
144
145 if (!allStoragesAvailable) {
146 let storagesel = view.down('pveStorageSelector[name=storage]');
147 storagesel.allowBlank = false;
148 storagesel.setEmptyText('');
149 }
150 },
151 });
152 },
153 },
154
155 initComponent: function() {
156 let me = this;
157
158 if (!me.nodename) {
159 throw "no node name specified";
160 }
161 if (!me.volid) {
162 throw "no volume ID specified";
163 }
164 if (!me.vmtype) {
165 throw "no vmtype specified";
166 }
167
168 let storagesel = Ext.create('PVE.form.StorageSelector', {
169 nodename: me.nodename,
170 name: 'storage',
171 value: '',
172 fieldLabel: gettext('Storage'),
173 storageContent: me.vmtype === 'lxc' ? 'rootdir' : 'images',
174 // when restoring a container without specifying a storage, the backend defaults
175 // to 'local', which is unintuitive and 'rootdir' might not even be allowed on it
176 allowBlank: me.vmtype !== 'lxc',
177 emptyText: me.vmtype === 'lxc' ? '' : gettext('From backup configuration'),
178 autoSelect: me.vmtype === 'lxc',
179 });
180
181 let items = [
182 {
183 xtype: 'displayfield',
184 value: me.volidText || me.volid,
185 fieldLabel: gettext('Source'),
186 },
187 storagesel,
188 {
189 xtype: 'pmxDisplayEditField',
190 name: 'vmid',
191 fieldLabel: me.vmtype === 'lxc' ? 'CT' : 'VM',
192 value: me.vmid,
193 editable: !me.vmid,
194 editConfig: {
195 xtype: 'pveGuestIDSelector',
196 guestType: me.vmtype,
197 loadNextFreeID: true,
198 validateExists: false,
199 },
200 },
201 {
202 xtype: 'pveBandwidthField',
203 name: 'bwlimit',
204 backendUnit: 'KiB',
205 allowZero: true,
206 fieldLabel: gettext('Bandwidth Limit'),
207 emptyText: gettext('Defaults to target storage restore limit'),
208 autoEl: {
209 tag: 'div',
210 'data-qtip': gettext("Use '0' to disable all bandwidth limits."),
211 },
212 },
213 {
214 xtype: 'fieldcontainer',
215 layout: 'hbox',
216 items: [{
217 xtype: 'proxmoxcheckbox',
218 name: 'unique',
219 fieldLabel: gettext('Unique'),
220 flex: 1,
221 autoEl: {
222 tag: 'div',
223 'data-qtip': gettext('Autogenerate unique properties, e.g., MAC addresses'),
224 },
225 checked: false,
226 },
227 {
228 xtype: 'proxmoxcheckbox',
229 name: 'start',
230 reference: 'start',
231 flex: 1,
232 fieldLabel: gettext('Start after restore'),
233 labelWidth: 105,
234 checked: false,
235 }],
236 },
237 ];
238
239 if (me.vmtype === 'lxc') {
240 items.push(
241 {
242 xtype: 'radiogroup',
243 fieldLabel: gettext('Privilege Level'),
244 reference: 'noVNCScalingGroup',
245 height: '15px', // renders faster with value assigned
246 layout: {
247 type: 'hbox',
248 algin: 'stretch',
249 },
250 autoEl: {
251 tag: 'div',
252 'data-qtip':
253 gettext('Choose if you want to keep or override the privilege level of the restored Container.'),
254 },
255 items: [
256 {
257 xtype: 'radiofield',
258 name: 'unprivileged',
259 inputValue: 'keep',
260 boxLabel: gettext('From Backup'),
261 flex: 1,
262 checked: true,
263 },
264 {
265 xtype: 'radiofield',
266 name: 'unprivileged',
267 inputValue: '1',
268 boxLabel: gettext('Unprivileged'),
269 flex: 1,
270 },
271 {
272 xtype: 'radiofield',
273 name: 'unprivileged',
274 inputValue: '0',
275 boxLabel: gettext('Privileged'),
276 flex: 1,
277 //margin: '0 0 0 10',
278 },
279 ],
280 },
281 );
282 } else if (me.vmtype === 'qemu') {
283 items.push({
284 xtype: 'proxmoxcheckbox',
285 name: 'live-restore',
286 itemId: 'liveRestore',
287 flex: 1,
288 fieldLabel: gettext('Live restore'),
289 checked: false,
290 hidden: !me.isPBS,
291 },
292 {
293 xtype: 'displayfield',
294 reference: 'liveWarning',
295 // TODO: Remove once more tested/stable?
296 value: gettext('Note: If anything goes wrong during the live-restore, new data written by the VM may be lost.'),
297 userCls: 'pmx-hint',
298 hidden: true,
299 });
300 }
301
302 items.push({
303 xtype: 'fieldset',
304 title: `${gettext('Override Settings')}:`,
305 layout: 'hbox',
306 defaults: {
307 border: false,
308 layout: 'anchor',
309 flex: 1,
310 },
311 items: [
312 {
313 padding: '0 10 0 0',
314 items: [{
315 xtype: 'textfield',
316 fieldLabel: me.vmtype === 'lxc' ? gettext('Hostname') : gettext('Name'),
317 name: 'name',
318 vtype: 'DnsName',
319 reference: 'nameField',
320 allowBlank: true,
321 }, {
322 xtype: 'proxmoxintegerfield',
323 fieldLabel: gettext('Cores'),
324 name: 'cores',
325 reference: 'coresField',
326 minValue: 1,
327 maxValue: 128,
328 allowBlank: true,
329 }],
330 },
331 {
332 padding: '0 0 0 10',
333 items: [
334 {
335 xtype: 'pveMemoryField',
336 fieldLabel: gettext('Memory'),
337 name: 'memory',
338 reference: 'memoryField',
339 value: '',
340 allowBlank: true,
341 },
342 {
343 xtype: 'proxmoxintegerfield',
344 fieldLabel: gettext('Sockets'),
345 name: 'sockets',
346 reference: 'socketsField',
347 minValue: 1,
348 maxValue: 4,
349 allowBlank: true,
350 hidden: me.vmtype !== 'qemu',
351 disabled: me.vmtype !== 'qemu',
352 }],
353 },
354 ],
355 });
356
357 let title = gettext('Restore') + ": " + (me.vmtype === 'lxc' ? 'CT' : 'VM');
358 if (me.vmid) {
359 title = `${gettext('Overwrite')} ${title} ${me.vmid}`;
360 }
361
362 Ext.apply(me, {
363 title: title,
364 items: [
365 {
366 xtype: 'form',
367 bodyPadding: 10,
368 border: false,
369 fieldDefaults: {
370 labelWidth: 100,
371 anchor: '100%',
372 },
373 items: items,
374 },
375 ],
376 buttons: [
377 {
378 text: gettext('Restore'),
379 reference: 'doRestoreBtn',
380 handler: 'doRestore',
381 },
382 ],
383 });
384
385 me.callParent();
386 },
387 });