]> git.proxmox.com Git - pve-manager.git/blame - www/manager6/window/Restore.js
ui: restore: group override settings in a fieldset
[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')}:`,
299 layout: 'column',
300 defaults: {
301 width: '45%',
302 margin: '0 0 5 10',
1ad26512 303 },
c7f66bd0
FE
304 items: [
305 {
306 xtype: 'textfield',
307 fieldLabel: gettext('Name'),
308 name: 'name',
309 reference: 'nameField',
310 allowBlank: true,
311 },
312 {
313 xtype: 'pveMemoryField',
314 fieldLabel: gettext('Memory'),
315 name: 'memory',
316 reference: 'memoryField',
317 value: '',
318 allowBlank: true,
319 },
320 {
321 xtype: 'proxmoxintegerfield',
322 fieldLabel: gettext('Cores'),
323 name: 'cores',
324 reference: 'coresField',
325 minValue: 1,
326 maxValue: 128,
327 allowBlank: true,
328 },
329 {
330 xtype: 'proxmoxintegerfield',
331 fieldLabel: gettext('Sockets'),
332 name: 'sockets',
333 reference: 'socketsField',
334 minValue: 1,
335 maxValue: 4,
336 allowBlank: true,
337 hidden: me.vmtype !== 'qemu',
338 disabled: me.vmtype !== 'qemu',
339 },
340 ],
341 });
1ad26512 342
d3de9b55 343 let title = gettext('Restore') + ": " + (me.vmtype === 'lxc' ? 'CT' : 'VM');
185a77e5 344 if (me.vmid) {
70aaf893 345 title = `${gettext('Overwrite')} ${title} ${me.vmid}`;
185a77e5 346 }
d7683c68
DM
347
348 Ext.apply(me, {
349 title: title,
d3de9b55
TL
350 items: [
351 {
352 xtype: 'form',
353 bodyPadding: 10,
354 border: false,
355 fieldDefaults: {
356 labelWidth: 100,
357 anchor: '100%',
358 },
359 items: items,
360 },
361 ],
362 buttons: [
363 {
364 text: gettext('Restore'),
365 reference: 'doRestoreBtn',
366 handler: 'doRestore',
367 },
368 ],
d7683c68
DM
369 });
370
371 me.callParent();
f6710aac 372 },
d7683c68 373});