]> git.proxmox.com Git - pve-manager.git/blame - www/manager6/window/Restore.js
ui: restore: fix syntax error introduced in previous commit
[pve-manager.git] / www / manager6 / window / Restore.js
CommitLineData
d7683c68 1Ext.define('PVE.window.Restore', {
9fccc702 2 extend: 'Ext.window.Window', // fixme: Proxmox.window.Edit?
d7683c68
DM
3
4 resizable: false,
d3de9b55
TL
5 width: 500,
6 modal: true,
7 layout: 'auto',
8 border: false,
d7683c68 9
878bbf0b
SR
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 },
d3de9b55
TL
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 }
1ad26512
FE
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;
d3de9b55
TL
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 += '. ' + gettext('This will permanently erase current VM data.');
96 Ext.Msg.confirm(gettext('Confirm'), confirmMsg, function(btn) {
97 if (btn === 'yes') {
98 executeRestore();
99 }
100 });
101 } else {
102 executeRestore();
103 }
878bbf0b 104 },
47520da3
FE
105
106 afterRender: function() {
107 let view = this.getView();
108
109 Proxmox.Utils.API2Request({
110 url: `/nodes/${view.nodename}/vzdump/extractconfig`,
111 method: 'GET',
112 waitMsgTarget: view,
113 params: {
114 volume: view.volid,
115 },
116 failure: response => Ext.Msg.alert('Error', response.htmlStatus),
117 success: function(response, options) {
1ad26512
FE
118 let allStoragesAvailable = true;
119
120 response.result.data.split('\n').forEach(line => {
121 let [_, key, value] = line.match(/^([^:]+):\s*(\S+)\s*$/) ?? [];
122
123 if (!key) {
124 return;
125 }
126
127 if (key === '#qmdump#map') {
128 let match = value.match(/^(\S+):(\S+):(\S*):(\S*):$/) ?? [];
129 // if a /dev/XYZ disk was backed up, ther is no storage hint
130 allStoragesAvailable &&= !!match[3] && !!PVE.data.ResourceStore.getById(
131 `storage/${view.nodename}/${match[3]}`);
132 } else if (key === 'name' || key === 'hostname') {
133 view.lookupReference('nameField').setEmptyText(value);
134 } else if (key === 'memory' || key === 'cores' || key === 'sockets') {
135 view.lookupReference(`${key}Field`).setEmptyText(value);
47520da3 136 }
47520da3
FE
137 });
138
139 if (!allStoragesAvailable) {
140 let storagesel = view.down('pveStorageSelector[name=storage]');
141 storagesel.allowBlank = false;
142 storagesel.setEmptyText('');
143 }
144 },
145 });
146 },
878bbf0b
SR
147 },
148
8058410f 149 initComponent: function() {
d3de9b55 150 let me = this;
d7683c68
DM
151
152 if (!me.nodename) {
153 throw "no node name specified";
154 }
d7683c68
DM
155 if (!me.volid) {
156 throw "no volume ID specified";
157 }
d7683c68
DM
158 if (!me.vmtype) {
159 throw "no vmtype specified";
160 }
161
d3de9b55 162 let storagesel = Ext.create('PVE.form.StorageSelector', {
d7683c68
DM
163 nodename: me.nodename,
164 name: 'storage',
165 value: '',
166 fieldLabel: gettext('Storage'),
53e3ea84 167 storageContent: me.vmtype === 'lxc' ? 'rootdir' : 'images',
6fe863ba
FE
168 // when restoring a container without specifying a storage, the backend defaults
169 // to 'local', which is unintuitive and 'rootdir' might not even be allowed on it
170 allowBlank: me.vmtype !== 'lxc',
53e3ea84 171 emptyText: me.vmtype === 'lxc' ? '' : gettext('From backup configuration'),
6fe863ba 172 autoSelect: me.vmtype === 'lxc',
d7683c68
DM
173 });
174
d3de9b55 175 let items = [
9cf2e84b
DM
176 {
177 xtype: 'displayfield',
178 value: me.volidText || me.volid,
f6710aac 179 fieldLabel: gettext('Source'),
9cf2e84b
DM
180 },
181 storagesel,
ce60366f
TL
182 {
183 xtype: 'pmxDisplayEditField',
184 name: 'vmid',
185 fieldLabel: me.vmtype === 'lxc' ? 'CT' : 'VM',
186 value: me.vmid,
187 editable: !me.vmid,
188 editConfig: {
189 xtype: 'pveGuestIDSelector',
190 guestType: me.vmtype,
191 loadNextFreeID: true,
192 validateExists: false,
193 },
194 },
9fa4ce6d 195 {
a331258a 196 xtype: 'pveBandwidthField',
9fa4ce6d 197 name: 'bwlimit',
0545b2a5 198 backendUnit: 'KiB',
9ae4a024
FE
199 allowZero: true,
200 fieldLabel: gettext('Bandwidth Limit'),
9fa4ce6d
TL
201 emptyText: gettext('Defaults to target storage restore limit'),
202 autoEl: {
203 tag: 'div',
f6710aac
TL
204 'data-qtip': gettext("Use '0' to disable all bandwidth limits."),
205 },
c517e212 206 },
8b030d08 207 {
24c55f41
TL
208 xtype: 'fieldcontainer',
209 layout: 'hbox',
210 items: [{
211 xtype: 'proxmoxcheckbox',
212 name: 'unique',
213 fieldLabel: gettext('Unique'),
24c55f41
TL
214 flex: 1,
215 autoEl: {
216 tag: 'div',
f6710aac 217 'data-qtip': gettext('Autogenerate unique properties, e.g., MAC addresses'),
24c55f41 218 },
f6710aac 219 checked: false,
8b030d08 220 },
24c55f41
TL
221 {
222 xtype: 'proxmoxcheckbox',
223 name: 'start',
878bbf0b 224 reference: 'start',
24c55f41
TL
225 flex: 1,
226 fieldLabel: gettext('Start after restore'),
227 labelWidth: 105,
f6710aac 228 checked: false,
24c55f41
TL
229 }],
230 },
9cf2e84b
DM
231 ];
232
233 if (me.vmtype === 'lxc') {
6da2926c
TL
234 items.push(
235 {
236 xtype: 'radiogroup',
237 fieldLabel: gettext('Privilege Level'),
238 reference: 'noVNCScalingGroup',
239 height: '15px', // renders faster with value assigned
240 layout: {
241 type: 'hbox',
242 algin: 'stretch',
243 },
244 autoEl: {
245 tag: 'div',
246 'data-qtip':
247 gettext('Choose if you want to keep or override the privilege level of the restored Container.'),
248 },
249 items: [
250 {
251 xtype: 'radiofield',
252 name: 'unprivileged',
253 inputValue: 'keep',
254 boxLabel: gettext('From Backup'),
255 flex: 1,
256 checked: true,
257 },
258 {
259 xtype: 'radiofield',
260 name: 'unprivileged',
261 inputValue: '1',
262 boxLabel: gettext('Unprivileged'),
263 flex: 1,
264 },
265 {
266 xtype: 'radiofield',
267 name: 'unprivileged',
268 inputValue: '0',
269 boxLabel: gettext('Privileged'),
270 flex: 1,
271 //margin: '0 0 0 10',
272 },
273 ],
274 },
275 );
878bbf0b
SR
276 } else if (me.vmtype === 'qemu') {
277 items.push({
278 xtype: 'proxmoxcheckbox',
279 name: 'live-restore',
280 itemId: 'liveRestore',
281 flex: 1,
282 fieldLabel: gettext('Live restore'),
283 checked: false,
284 hidden: !me.isPBS,
d3de9b55
TL
285 },
286 {
878bbf0b
SR
287 xtype: 'displayfield',
288 reference: 'liveWarning',
289 // TODO: Remove once more tested/stable?
d290acc8 290 value: gettext('Note: If anything goes wrong during the live-restore, new data written by the VM may be lost.'),
878bbf0b
SR
291 userCls: 'pmx-hint',
292 hidden: true,
293 });
9cf2e84b
DM
294 }
295
c7f66bd0
FE
296 items.push({
297 xtype: 'fieldset',
298 title: `${gettext('Override Settings')}:`,
8ce4c524 299 layout: 'hbox',
c7f66bd0 300 defaults: {
8ce4c524
TL
301 border: false,
302 layout: 'anchor',
303 flex: 1,
1ad26512 304 },
c7f66bd0
FE
305 items: [
306 {
8ce4c524
TL
307 padding: '0 10 0 0',
308 items: [{
309 xtype: 'textfield',
310 fieldLabel: me.vmtype === 'lxc' ? gettext('Hostname') : gettext('Name'),
311 name: 'name',
312 reference: 'nameField',
313 allowBlank: true,
314 }, {
315 xtype: 'proxmoxintegerfield',
316 fieldLabel: gettext('Cores'),
317 name: 'cores',
318 reference: 'coresField',
319 minValue: 1,
320 maxValue: 128,
321 allowBlank: true,
322 }],
c7f66bd0
FE
323 },
324 {
8ce4c524
TL
325 padding: '0 0 0 10',
326 items: [
327 {
328 xtype: 'pveMemoryField',
329 fieldLabel: gettext('Memory'),
330 name: 'memory',
331 reference: 'memoryField',
332 value: '',
333 allowBlank: true,
334 },
335 {
336 xtype: 'proxmoxintegerfield',
337 fieldLabel: gettext('Sockets'),
338 name: 'sockets',
339 reference: 'socketsField',
340 minValue: 1,
341 maxValue: 4,
342 allowBlank: true,
343 hidden: me.vmtype !== 'qemu',
344 disabled: me.vmtype !== 'qemu',
345 }],
c7f66bd0
FE
346 },
347 ],
b61d079a 348 });
1ad26512 349
d3de9b55 350 let title = gettext('Restore') + ": " + (me.vmtype === 'lxc' ? 'CT' : 'VM');
185a77e5 351 if (me.vmid) {
70aaf893 352 title = `${gettext('Overwrite')} ${title} ${me.vmid}`;
185a77e5 353 }
d7683c68
DM
354
355 Ext.apply(me, {
356 title: title,
d3de9b55
TL
357 items: [
358 {
359 xtype: 'form',
360 bodyPadding: 10,
361 border: false,
362 fieldDefaults: {
363 labelWidth: 100,
364 anchor: '100%',
365 },
366 items: items,
367 },
368 ],
369 buttons: [
370 {
371 text: gettext('Restore'),
372 reference: 'doRestoreBtn',
373 handler: 'doRestore',
374 },
375 ],
d7683c68
DM
376 });
377
378 me.callParent();
f6710aac 379 },
d7683c68 380});