]> git.proxmox.com Git - pve-manager.git/blobdiff - www/manager6/window/Restore.js
ui: restore: allow override of some settings
[pve-manager.git] / www / manager6 / window / Restore.js
index d32fae002a8e83777b84a60a7f4fa62b99ffe83d..2d78eb5697cdcc1ff1793ca4a14fd2158758337a 100644 (file)
 Ext.define('PVE.window.Restore', {
-    extend: 'Ext.window.Window', // fixme: PVE.window.Edit?
+    extend: 'Ext.window.Window', // fixme: Proxmox.window.Edit?
 
     resizable: false,
+    width: 500,
+    modal: true,
+    layout: 'auto',
+    border: false,
 
-    initComponent : function() {
-       var me = this;
+    controller: {
+       xclass: 'Ext.app.ViewController',
+       control: {
+           '#liveRestore': {
+               change: function(el, newVal) {
+                   let liveWarning = this.lookupReference('liveWarning');
+                   liveWarning.setHidden(!newVal);
+                   let start = this.lookupReference('start');
+                   start.setDisabled(newVal);
+               },
+           },
+           'form': {
+               validitychange: function(f, valid) {
+                   this.lookupReference('doRestoreBtn').setDisabled(!valid);
+               },
+           },
+       },
+
+       doRestore: function() {
+           let me = this;
+           let view = me.getView();
+
+           let values = view.down('form').getForm().getValues();
+
+           let params = {
+               vmid: view.vmid || values.vmid,
+               force: view.vmid ? 1 : 0,
+           };
+           if (values.unique) {
+               params.unique = 1;
+           }
+           if (values.start && !values['live-restore']) {
+               params.start = 1;
+           }
+           if (values['live-restore']) {
+               params['live-restore'] = 1;
+           }
+           if (values.storage) {
+               params.storage = values.storage;
+           }
+
+           ['bwlimit', 'cores', 'name', 'memory', 'sockets'].forEach(opt => {
+               if ((values[opt] ?? '') !== '') {
+                   params[opt] = values[opt];
+               }
+           });
+
+           if (params.name && view.vmtype === 'lxc') {
+               params.hostname = params.name;
+               delete params.name;
+           }
+
+           let confirmMsg;
+           if (view.vmtype === 'lxc') {
+               params.ostemplate = view.volid;
+               params.restore = 1;
+               if (values.unprivileged !== 'keep') {
+                   params.unprivileged = values.unprivileged;
+               }
+               confirmMsg = Proxmox.Utils.format_task_description('vzrestore', params.vmid);
+           } else if (view.vmtype === 'qemu') {
+               params.archive = view.volid;
+               confirmMsg = Proxmox.Utils.format_task_description('qmrestore', params.vmid);
+           } else {
+               throw 'unknown VM type';
+           }
+
+           let executeRestore = () => {
+               Proxmox.Utils.API2Request({
+                   url: `/nodes/${view.nodename}/${view.vmtype}`,
+                   params: params,
+                   method: 'POST',
+                   waitMsgTarget: view,
+                   failure: response => Ext.Msg.alert(gettext('Error'), response.htmlStatus),
+                   success: function(response, options) {
+                       Ext.create('Proxmox.window.TaskViewer', {
+                           autoShow: true,
+                           upid: response.result.data,
+                       });
+                       view.close();
+                   },
+               });
+           };
+
+           if (view.vmid) {
+               confirmMsg += '. ' + gettext('This will permanently erase current VM data.');
+               Ext.Msg.confirm(gettext('Confirm'), confirmMsg, function(btn) {
+                   if (btn === 'yes') {
+                       executeRestore();
+                   }
+               });
+           } else {
+               executeRestore();
+           }
+       },
+
+       afterRender: function() {
+           let view = this.getView();
+
+           Proxmox.Utils.API2Request({
+               url: `/nodes/${view.nodename}/vzdump/extractconfig`,
+               method: 'GET',
+               waitMsgTarget: view,
+               params: {
+                   volume: view.volid,
+               },
+               failure: response => Ext.Msg.alert('Error', response.htmlStatus),
+               success: function(response, options) {
+                   let allStoragesAvailable = true;
+
+                   response.result.data.split('\n').forEach(line => {
+                       let [_, key, value] = line.match(/^([^:]+):\s*(\S+)\s*$/) ?? [];
+
+                       if (!key) {
+                           return;
+                       }
+
+                       if (key === '#qmdump#map') {
+                           let match = value.match(/^(\S+):(\S+):(\S*):(\S*):$/) ?? [];
+                           // if a /dev/XYZ disk was backed up, ther is no storage hint
+                           allStoragesAvailable &&= !!match[3] && !!PVE.data.ResourceStore.getById(
+                               `storage/${view.nodename}/${match[3]}`);
+                       } else if (key === 'name' || key === 'hostname') {
+                           view.lookupReference('nameField').setEmptyText(value);
+                       } else if (key === 'memory' || key === 'cores' || key === 'sockets') {
+                           view.lookupReference(`${key}Field`).setEmptyText(value);
+                       }
+                   });
+
+                   if (!allStoragesAvailable) {
+                       let storagesel = view.down('pveStorageSelector[name=storage]');
+                       storagesel.allowBlank = false;
+                       storagesel.setEmptyText('');
+                   }
+               },
+           });
+       },
+    },
+
+    initComponent: function() {
+       let me = this;
 
        if (!me.nodename) {
            throw "no node name specified";
        }
-
        if (!me.volid) {
            throw "no volume ID specified";
        }
-
        if (!me.vmtype) {
            throw "no vmtype specified";
        }
 
-       var storagesel = Ext.create('PVE.form.StorageSelector', {
+       let storagesel = Ext.create('PVE.form.StorageSelector', {
            nodename: me.nodename,
            name: 'storage',
            value: '',
            fieldLabel: gettext('Storage'),
-           storageContent: (me.vmtype === 'lxc') ? 'rootdir' : 'images',
-           allowBlank: true
+           storageContent: me.vmtype === 'lxc' ? 'rootdir' : 'images',
+           // when restoring a container without specifying a storage, the backend defaults
+           // to 'local', which is unintuitive and 'rootdir' might not even be allowed on it
+           allowBlank: me.vmtype !== 'lxc',
+           emptyText: me.vmtype === 'lxc' ? '' : gettext('From backup configuration'),
+           autoSelect: me.vmtype === 'lxc',
        });
 
-       var IDfield;
-       if (me.vmid) {
-           IDfield = Ext.create('Ext.form.field.Display', {
-               name: 'vmid',
-               value: me.vmid,
-               fieldLabel: (me.vmtype === 'lxc') ? 'CT' : 'VM'
-           });
-       } else {
-           IDfield = Ext.create('PVE.form.GuestIDSelector', {
-               name: 'vmid',
-               guestType: me.vmtype,
-               loadNextGuestID: true,
-               validateExists: false
-           });
-       }
-
-       var items = [
+       let items = [
            {
                xtype: 'displayfield',
                value: me.volidText || me.volid,
-               fieldLabel: gettext('Source')
+               fieldLabel: gettext('Source'),
            },
            storagesel,
-           IDfield
+           {
+               xtype: 'pmxDisplayEditField',
+               name: 'vmid',
+               fieldLabel: me.vmtype === 'lxc' ? 'CT' : 'VM',
+               value: me.vmid,
+               editable: !me.vmid,
+               editConfig: {
+                   xtype: 'pveGuestIDSelector',
+                   guestType: me.vmtype,
+                   loadNextFreeID: true,
+                   validateExists: false,
+               },
+           },
+           {
+               xtype: 'pveBandwidthField',
+               name: 'bwlimit',
+               backendUnit: 'KiB',
+               allowZero: true,
+               fieldLabel: gettext('Bandwidth Limit'),
+               emptyText: gettext('Defaults to target storage restore limit'),
+               autoEl: {
+                   tag: 'div',
+                   'data-qtip': gettext("Use '0' to disable all bandwidth limits."),
+               },
+           },
+           {
+               xtype: 'fieldcontainer',
+               layout: 'hbox',
+               items: [{
+                   xtype: 'proxmoxcheckbox',
+                   name: 'unique',
+                   fieldLabel: gettext('Unique'),
+                   flex: 1,
+                   autoEl: {
+                       tag: 'div',
+                       'data-qtip': gettext('Autogenerate unique properties, e.g., MAC addresses'),
+                   },
+                   checked: false,
+               },
+               {
+                   xtype: 'proxmoxcheckbox',
+                   name: 'start',
+                   reference: 'start',
+                   flex: 1,
+                   fieldLabel: gettext('Start after restore'),
+                   labelWidth: 105,
+                   checked: false,
+               }],
+           },
        ];
 
        if (me.vmtype === 'lxc') {
+           items.push(
+               {
+                   xtype: 'radiogroup',
+                   fieldLabel: gettext('Privilege Level'),
+                   reference: 'noVNCScalingGroup',
+                   height: '15px', // renders faster with value assigned
+                   layout: {
+                       type: 'hbox',
+                       algin: 'stretch',
+                   },
+                   autoEl: {
+                       tag: 'div',
+                       'data-qtip':
+                           gettext('Choose if you want to keep or override the privilege level of the restored Container.'),
+                   },
+                   items: [
+                       {
+                           xtype: 'radiofield',
+                           name: 'unprivileged',
+                           inputValue: 'keep',
+                           boxLabel: gettext('From Backup'),
+                           flex: 1,
+                           checked: true,
+                       },
+                       {
+                           xtype: 'radiofield',
+                           name: 'unprivileged',
+                           inputValue: '1',
+                           boxLabel: gettext('Unprivileged'),
+                           flex: 1,
+                       },
+                       {
+                           xtype: 'radiofield',
+                           name: 'unprivileged',
+                           inputValue: '0',
+                           boxLabel: gettext('Privileged'),
+                           flex: 1,
+                           //margin: '0 0 0 10',
+                       },
+                   ],
+               },
+           );
+       } else if (me.vmtype === 'qemu') {
            items.push({
-               xtype: 'pvecheckbox',
-               name: 'unprivileged',
-               value: '',
-               fieldLabel: gettext('Unprivileged container')
+               xtype: 'proxmoxcheckbox',
+               name: 'live-restore',
+               itemId: 'liveRestore',
+               flex: 1,
+               fieldLabel: gettext('Live restore'),
+               checked: false,
+               hidden: !me.isPBS,
+           },
+           {
+               xtype: 'displayfield',
+               reference: 'liveWarning',
+               // TODO: Remove once more tested/stable?
+               value: gettext('Note: If anything goes wrong during the live-restore, new data written by the VM may be lost.'),
+               userCls: 'pmx-hint',
+               hidden: true,
            });
        }
 
-       me.formPanel = Ext.create('Ext.form.Panel', {
-           bodyPadding: 10,
-           border: false,
-           fieldDefaults: {
-               labelWidth: 100,
-               anchor: '100%'
+       items.push(
+           {
+               xtype: 'displayfield',
+               value: `${gettext('Override Settings')}:`,
            },
-           items: items
-       });
-
-       var form = me.formPanel.getForm();
+           {
+               xtype: 'textfield',
+               fieldLabel: gettext('Name'),
+               name: 'name',
+               reference: 'nameField',
+               allowBlank: true,
+           },
+           {
+               xtype: 'pveMemoryField',
+               fieldLabel: gettext('Memory'),
+               name: 'memory',
+               reference: 'memoryField',
+               value: '',
+               allowBlank: true,
+           },
+           {
+               xtype: 'proxmoxintegerfield',
+               fieldLabel: gettext('Cores'),
+               name: 'cores',
+               reference: 'coresField',
+               minValue: 1,
+               maxValue: 128,
+               allowBlank: true,
+           },
+       );
 
-       var doRestore = function(url, params) {
-           PVE.Utils.API2Request({
-               url: url,
-               params: params,
-               method: 'POST',
-               waitMsgTarget: me,
-               failure: function (response, opts) {
-                   Ext.Msg.alert(gettext('Error'), response.htmlStatus);
-               },
-               success: function(response, options) {
-                   var upid = response.result.data;
-                   
-                   var win = Ext.create('PVE.window.TaskViewer', { 
-                       upid: upid
-                   });
-                   win.show();
-                   me.close();
-               }
+       if (me.vmtype === 'qemu') {
+           items.push({
+               xtype: 'proxmoxintegerfield',
+               fieldLabel: gettext('Sockets'),
+               name: 'sockets',
+               reference: 'socketsField',
+               minValue: 1,
+               maxValue: 4,
+               allowBlank: true,
            });
-       };
-
-       var submitBtn = Ext.create('Ext.Button', {
-           text: gettext('Restore'),
-           handler: function(){
-               var storage = storagesel.getValue();
-               var values = form.getValues();
-
-               var params = {
-                   storage: storage,
-                   vmid: me.vmid || values.vmid,
-                   force: me.vmid ? 1 : 0
-               };
-
-               var url;
-               var msg;
-               if (me.vmtype === 'lxc') {
-                   url = '/nodes/' + me.nodename + '/lxc';
-                   params.ostemplate = me.volid;
-                   params.restore = 1;
-                   if (values.unprivileged) { params.unprivileged = 1; }
-                   msg = PVE.Utils.format_task_description('vzrestore', params.vmid);
-               } else if (me.vmtype === 'qemu') {
-                   url = '/nodes/' + me.nodename + '/qemu';
-                   params.archive = me.volid;
-                   msg = PVE.Utils.format_task_description('qmrestore', params.vmid);
-               } else {
-                   throw 'unknown VM type';
-               }
-
-               if (me.vmid) {
-                   msg += '. ' + gettext('This will permanently erase current VM data.');
-                   Ext.Msg.confirm(gettext('Confirm'), msg, function(btn) {
-                       if (btn !== 'yes') {
-                           return;
-                       }
-                       doRestore(url, params);
-                   });
-               } else {
-                   doRestore(url, params);
-               }
-           }
-       });
-
-       form.on('validitychange', function(f, valid) {
-           submitBtn.setDisabled(!valid);
-       });
-
-       var title =  gettext('Restore') + ": " + (
-           (me.vmtype === 'lxc') ? 'CT' : 'VM');
+       }
 
+       let title = gettext('Restore') + ": " + (me.vmtype === 'lxc' ? 'CT' : 'VM');
        if (me.vmid) {
-           title += " " + me.vmid;
+           title = `${gettext('Overwrite')} ${title} ${me.vmid}`;
        }
 
        Ext.apply(me, {
            title: title,
-           width: 500,
-           modal: true,
-           layout: 'auto',
-           border: false,
-           items: [ me.formPanel ],
-           buttons: [ submitBtn ]
+           items: [
+               {
+                   xtype: 'form',
+                   bodyPadding: 10,
+                   border: false,
+                   fieldDefaults: {
+                       labelWidth: 100,
+                       anchor: '100%',
+                   },
+                   items: items,
+               },
+           ],
+           buttons: [
+               {
+                   text: gettext('Restore'),
+                   reference: 'doRestoreBtn',
+                   handler: 'doRestore',
+               },
+           ],
        });
 
        me.callParent();
-    }
+    },
 });