]> git.proxmox.com Git - pve-manager.git/blobdiff - www/manager6/window/Restore.js
ui: vzdump: remove left-overs from target/policy based notifications
[pve-manager.git] / www / manager6 / window / Restore.js
index 51f6a8537bfe3fe7f7c3b4851be24204b373134c..36aecc39230b10c4a3b5fc78005a7ce7dca52440 100644 (file)
@@ -2,69 +2,213 @@ Ext.define('PVE.window.Restore', {
     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 += `. ${Ext.String.format(
+                   gettext('This will permanently erase current {0} data.'),
+                   view.vmtype === 'lxc' ? 'CT' : 'VM',
+               )}`;
+               if (view.vmtype === 'lxc') {
+                   confirmMsg += `<br>${gettext('Mount point volumes are also erased.')}`;
+               }
+               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',
+           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'),
+           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,
-               loadNextFreeID: 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',
-               fieldLabel: gettext('Read Limit'),
+               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.")
-               }
+                   'data-qtip': gettext("Use '0' to disable all bandwidth limits."),
+               },
            },
            {
                xtype: 'fieldcontainer',
@@ -73,135 +217,171 @@ Ext.define('PVE.window.Restore', {
                    xtype: 'proxmoxcheckbox',
                    name: 'unique',
                    fieldLabel: gettext('Unique'),
-                   hidden: !!me.vmid,
                    flex: 1,
                    autoEl: {
                        tag: 'div',
-                       'data-qtip': gettext('Autogenerate unique properties, e.g., MAC addresses')
+                       'data-qtip': gettext('Autogenerate unique properties, e.g., MAC addresses'),
                    },
-                   checked: false
+                   checked: false,
                },
                {
                    xtype: 'proxmoxcheckbox',
                    name: 'start',
+                   reference: 'start',
                    flex: 1,
                    fieldLabel: gettext('Start after restore'),
                    labelWidth: 105,
-                   checked: false
+                   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: 'proxmoxcheckbox',
-               name: 'unprivileged',
-               value: true,
-               fieldLabel: gettext('Unprivileged container')
+               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: 'fieldset',
+           title: `${gettext('Override Settings')}:`,
+           layout: 'hbox',
+           defaults: {
+               border: false,
+               layout: 'anchor',
+               flex: 1,
            },
-           items: items
-       });
-
-       var form = me.formPanel.getForm();
-
-       var doRestore = function(url, params) {
-           Proxmox.Utils.API2Request({
-               url: url,
-               params: params,
-               method: 'POST',
-               waitMsgTarget: me,
-               failure: function (response, opts) {
-                   Ext.Msg.alert(gettext('Error'), response.htmlStatus);
+           items: [
+               {
+                   padding: '0 10 0 0',
+                   items: [{
+                       xtype: 'textfield',
+                       fieldLabel: me.vmtype === 'lxc' ? gettext('Hostname') : gettext('Name'),
+                       name: 'name',
+                       vtype: 'DnsName',
+                       reference: 'nameField',
+                       allowBlank: true,
+                   }, {
+                       xtype: 'proxmoxintegerfield',
+                       fieldLabel: gettext('Cores'),
+                       name: 'cores',
+                       reference: 'coresField',
+                       minValue: 1,
+                       maxValue: 128,
+                       allowBlank: true,
+                   }],
                },
-               success: function(response, options) {
-                   var upid = response.result.data;
-
-                   var win = Ext.create('Proxmox.window.TaskViewer', {
-                       upid: upid
-                   });
-                   win.show();
-                   me.close();
-               }
-           });
-       };
-
-       var submitBtn = Ext.create('Ext.Button', {
-           text: gettext('Restore'),
-           handler: function(){
-               var values = form.getValues();
-
-               var params = {
-                   vmid: me.vmid || values.vmid,
-                   force: me.vmid ? 1 : 0
-               };
-               if (values.unique) { params.unique = 1; }
-               if (values.start) { params.start = 1; }
-               if (values.storage) { params.storage = values.storage; }
-
-               if (values.bwlimit !== undefined) {
-                   params.bwlimit = values.bwlimit;
-               }
-
-               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 = Proxmox.Utils.format_task_description('vzrestore', params.vmid);
-               } else if (me.vmtype === 'qemu') {
-                   url = '/nodes/' + me.nodename + '/qemu';
-                   params.archive = me.volid;
-                   msg = Proxmox.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);
+               {
+                   padding: '0 0 0 10',
+                   items: [
+                   {
+                       xtype: 'pveMemoryField',
+                       fieldLabel: gettext('Memory'),
+                       name: 'memory',
+                       reference: 'memoryField',
+                       value: '',
+                       allowBlank: true,
+                   },
+                   {
+                       xtype: 'proxmoxintegerfield',
+                       fieldLabel: gettext('Sockets'),
+                       name: 'sockets',
+                       reference: 'socketsField',
+                       minValue: 1,
+                       maxValue: 4,
+                       allowBlank: true,
+                       hidden: me.vmtype !== 'qemu',
+                       disabled: me.vmtype !== 'qemu',
+                   }],
+               },
+           ],
        });
 
-       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();
-    }
+    },
 });