]> git.proxmox.com Git - pve-manager.git/blobdiff - www/manager6/qemu/Config.js
api: add proxmox-firewall to versions pkg list
[pve-manager.git] / www / manager6 / qemu / Config.js
index 879ea322da271ffc0a94effb06ea9a9ec4e3e92f..f28ee67bb748b857dd9d021580c4106ccf46a98a 100644 (file)
@@ -3,40 +3,44 @@ Ext.define('PVE.qemu.Config', {
     alias: 'widget.PVE.qemu.Config',
 
     onlineHelp: 'chapter_virtual_machines',
+    userCls: 'proxmox-tags-full',
 
     initComponent: function() {
         var me = this;
+       var vm = me.pveSelNode.data;
 
-       var nodename = me.pveSelNode.data.node;
+       var nodename = vm.node;
        if (!nodename) {
            throw "no node name specified";
        }
 
-       var vmid = me.pveSelNode.data.vmid;
+       var vmid = vm.vmid;
        if (!vmid) {
            throw "no VM ID specified";
        }
 
-       var template = me.pveSelNode.data.template;
+       var template = !!vm.template;
+
+       var running = !!vm.uptime;
 
        var caps = Ext.state.Manager.get('GuiCap');
 
        var base_url = '/nodes/' + nodename + "/qemu/" + vmid;
 
-       me.statusStore = Ext.create('PVE.data.ObjectStore', {
+       me.statusStore = Ext.create('Proxmox.data.ObjectStore', {
            url: '/api2/json' + base_url + '/status/current',
-           interval: 1000
+           interval: 1000,
        });
 
        var vm_command = function(cmd, params) {
-           PVE.Utils.API2Request({
+           Proxmox.Utils.API2Request({
                params: params,
                url: base_url + '/status/' + cmd,
                waitMsgTarget: me,
                method: 'POST',
                failure: function(response, opts) {
                    Ext.Msg.alert('Error', response.htmlStatus);
-               }
+               },
            });
        };
 
@@ -47,129 +51,216 @@ Ext.define('PVE.qemu.Config', {
            handler: function() {
                vm_command('resume');
            },
-           iconCls: 'fa fa-play'
+           iconCls: 'fa fa-play',
        });
 
        var startBtn = Ext.create('Ext.Button', {
            text: gettext('Start'),
-           disabled: !caps.vms['VM.PowerMgmt'],
+           disabled: !caps.vms['VM.PowerMgmt'] || running,
+           hidden: template,
            handler: function() {
                vm_command('start');
            },
-           iconCls: 'fa fa-play'
+           iconCls: 'fa fa-play',
        });
 
        var migrateBtn = Ext.create('Ext.Button', {
            text: gettext('Migrate'),
            disabled: !caps.vms['VM.Migrate'],
+           hidden: PVE.Utils.isStandaloneNode(),
            handler: function() {
                var win = Ext.create('PVE.window.Migrate', {
                    vmtype: 'qemu',
                    nodename: nodename,
-                   vmid: vmid
+                   vmid: vmid,
                });
                win.show();
            },
-           iconCls: 'fa fa-send-o'
+           iconCls: 'fa fa-send-o',
        });
 
-       var cloneBtn = Ext.create('PVE.button.Split', {
-           text: gettext('Clone'),
-           iconCls: 'fa fa-fw fa-clone',
-           hidden: caps.vms['VM.Clone'] ? false : true,
-           handler: function() {
-               PVE.window.Clone.wrap(nodename, vmid, template);
-           },
+       var moreBtn = Ext.create('Proxmox.button.Button', {
+           text: gettext('More'),
            menu: {
-               items: [{
+ items: [
+               {
+                   text: gettext('Clone'),
+                   iconCls: 'fa fa-fw fa-clone',
+                   hidden: !caps.vms['VM.Clone'],
+                   handler: function() {
+                       PVE.window.Clone.wrap(nodename, vmid, template, 'qemu');
+                   },
+               },
+               {
                    text: gettext('Convert to template'),
                    disabled: template,
+                   xtype: 'pveMenuItem',
                    iconCls: 'fa fa-fw fa-file-o',
-                   hidden: caps.vms['VM.Allocate'] ? false : true,
-                   confirmMsg: PVE.Utils.format_task_description('qmtemplate', vmid),
+                   hidden: !caps.vms['VM.Allocate'],
+                   confirmMsg: Proxmox.Utils.format_task_description('qmtemplate', vmid),
                    handler: function() {
-                       PVE.Utils.API2Request({
+                       Proxmox.Utils.API2Request({
                            url: base_url + '/template',
                            waitMsgTarget: me,
                            method: 'POST',
                            failure: function(response, opts) {
                                Ext.Msg.alert('Error', response.htmlStatus);
-                           }
+                           },
                        });
-                   }
-               }]
-           }
+                   },
+               },
+               {
+                   iconCls: 'fa fa-heartbeat ',
+                   hidden: !caps.nodes['Sys.Console'],
+                   text: gettext('Manage HA'),
+                   handler: function() {
+                       var ha = vm.hastate;
+                       Ext.create('PVE.ha.VMResourceEdit', {
+                           vmid: vmid,
+                           isCreate: !ha || ha === 'unmanaged',
+                       }).show();
+                   },
+               },
+               {
+                   text: gettext('Remove'),
+                   itemId: 'removeBtn',
+                   disabled: !caps.vms['VM.Allocate'],
+                   handler: function() {
+                       Ext.create('PVE.window.SafeDestroyGuest', {
+                           url: base_url,
+                           item: { type: 'VM', id: vmid },
+                           taskName: 'qmdestroy',
+                       }).show();
+                   },
+                   iconCls: 'fa fa-trash-o',
+               },
+           ],
+},
        });
 
        var shutdownBtn = Ext.create('PVE.button.Split', {
            text: gettext('Shutdown'),
-           disabled: !caps.vms['VM.PowerMgmt'],
-           confirmMsg: PVE.Utils.format_task_description('qmshutdown', vmid),
+           disabled: !caps.vms['VM.PowerMgmt'] || !running,
+           hidden: template,
+           confirmMsg: Proxmox.Utils.format_task_description('qmshutdown', vmid),
            handler: function() {
                vm_command('shutdown');
            },
            menu: {
                items: [{
+                   text: gettext('Reboot'),
+                   disabled: !caps.vms['VM.PowerMgmt'],
+                   tooltip: Ext.String.format(gettext('Shutdown, apply pending changes and reboot {0}'), 'VM'),
+                   confirmMsg: Proxmox.Utils.format_task_description('qmreboot', vmid),
+                   handler: function() {
+                       vm_command("reboot");
+                   },
+                   iconCls: 'fa fa-refresh',
+               }, {
+                   text: gettext('Pause'),
+                   disabled: !caps.vms['VM.PowerMgmt'],
+                   confirmMsg: Proxmox.Utils.format_task_description('qmpause', vmid),
+                   handler: function() {
+                       vm_command("suspend");
+                   },
+                   iconCls: 'fa fa-pause',
+               }, {
+                   text: gettext('Hibernate'),
+                   disabled: !caps.vms['VM.PowerMgmt'],
+                   confirmMsg: Proxmox.Utils.format_task_description('qmsuspend', vmid),
+                   tooltip: gettext('Suspend to disk'),
+                   handler: function() {
+                       vm_command("suspend", { todisk: 1 });
+                   },
+                   iconCls: 'fa fa-download',
+               }, {
                    text: gettext('Stop'),
                    disabled: !caps.vms['VM.PowerMgmt'],
-                   dangerous: true,
-                   confirmMsg: PVE.Utils.format_task_description('qmstop', vmid),
+                   tooltip: Ext.String.format(gettext('Stop {0} immediately'), 'VM'),
                    handler: function() {
-                       vm_command("stop", { timeout: 30 });
+                       Ext.create('PVE.GuestStop', {
+                           nodename: nodename,
+                           vm: vm,
+                           autoShow: true,
+                       });
                    },
-                   iconCls: 'fa fa-stop'
-               },{
+                   iconCls: 'fa fa-stop',
+               }, {
                    text: gettext('Reset'),
                    disabled: !caps.vms['VM.PowerMgmt'],
-                   confirmMsg: PVE.Utils.format_task_description('qmreset', vmid),
+                   tooltip: Ext.String.format(gettext('Reset {0} immediately'), 'VM'),
+                   confirmMsg: Proxmox.Utils.format_task_description('qmreset', vmid),
                    handler: function() {
                        vm_command("reset");
                    },
-                   iconCls: 'fa fa-bolt'
-               }]
-           },
-           iconCls: 'fa fa-power-off'
-       });
-
-       var removeBtn = Ext.create('PVE.button.Button', {
-           text: gettext('Remove'),
-           disabled: !caps.vms['VM.Allocate'],
-           handler: function() {
-               Ext.create('PVE.window.SafeDestroy', {
-                   url: base_url,
-                   item: { type: 'VM', id: vmid }
-               }).show();
+                   iconCls: 'fa fa-bolt',
+               }],
            },
-           iconCls: 'fa fa-trash-o'
+           iconCls: 'fa fa-power-off',
        });
 
-       var vmname = me.pveSelNode.data.name;
-
        var consoleBtn = Ext.create('PVE.button.ConsoleButton', {
            disabled: !caps.vms['VM.Console'],
+           hidden: template,
            consoleType: 'kvm',
-           consoleName: vmname,
+           // disable spice/xterm for default action until status api call succeeded
+           enableSpice: false,
+           enableXtermjs: false,
+           consoleName: vm.name,
            nodename: nodename,
            vmid: vmid,
-           iconCls: 'fa fa-terminal'
        });
 
-       var descr = vmid + " (" + (vmname ? "'" + vmname + "' " : "'VM " + vmid + "'") + ")";
+       var statusTxt = Ext.create('Ext.toolbar.TextItem', {
+           data: {
+               lock: undefined,
+           },
+           tpl: [
+               '<tpl if="lock">',
+               '<i class="fa fa-lg fa-lock"></i> ({lock})',
+               '</tpl>',
+           ],
+       });
+
+       let tagsContainer = Ext.create('PVE.panel.TagEditContainer', {
+           tags: vm.tags,
+           canEdit: !!caps.vms['VM.Config.Options'],
+           listeners: {
+               change: function(tags) {
+                   Proxmox.Utils.API2Request({
+                       url: base_url + '/config',
+                       method: 'PUT',
+                       params: {
+                           tags,
+                       },
+                       success: function() {
+                           me.statusStore.load();
+                       },
+                       failure: function(response) {
+                           Ext.Msg.alert('Error', response.htmlStatus);
+                           me.statusStore.load();
+                       },
+                   });
+               },
+           },
+       });
+
+       let vm_text = `${vm.vmid} (${vm.name})`;
 
        Ext.apply(me, {
-           title: Ext.String.format(gettext("Virtual Machine {0} on node {1}"), descr, "'" + nodename + "'"),
+           title: Ext.String.format(gettext("Virtual Machine {0} on node '{1}'"), vm_text, nodename),
            hstateid: 'kvmtab',
-           tbar: [ resumeBtn, startBtn, shutdownBtn,
-                   removeBtn, migrateBtn, cloneBtn, consoleBtn],
+           tbarSpacing: false,
+           tbar: [statusTxt, tagsContainer, '->', resumeBtn, startBtn, shutdownBtn, migrateBtn, consoleBtn, moreBtn],
            defaults: { statusStore: me.statusStore },
            items: [
                {
                    title: gettext('Summary'),
-                   xtype: 'pveQemuSummary',
+                   xtype: 'pveGuestSummary',
                    iconCls: 'fa fa-book',
-                   itemId: 'summary'
-               }
-           ]
+                   itemId: 'summary',
+               },
+           ],
        });
 
        if (caps.vms['VM.Console'] && !template) {
@@ -180,7 +271,7 @@ Ext.define('PVE.qemu.Config', {
                xtype: 'pveNoVncConsole',
                vmid: vmid,
                consoleType: 'kvm',
-               nodename: nodename
+               nodename: nodename,
            });
        }
 
@@ -189,21 +280,30 @@ Ext.define('PVE.qemu.Config', {
                title: gettext('Hardware'),
                itemId: 'hardware',
                iconCls: 'fa fa-desktop',
-               xtype: 'PVE.qemu.HardwareView'
+               xtype: 'PVE.qemu.HardwareView',
+           },
+           {
+               title: 'Cloud-Init',
+               itemId: 'cloudinit',
+               iconCls: 'fa fa-cloud',
+               xtype: 'pveCiPanel',
            },
            {
                title: gettext('Options'),
                iconCls: 'fa fa-gear',
                itemId: 'options',
-               xtype: 'PVE.qemu.Options'
+               xtype: 'PVE.qemu.Options',
            },
            {
                title: gettext('Task History'),
                itemId: 'tasks',
-               xtype: 'pveNodeTasks',
-               iconCls: 'fa fa-list',
-               vmidFilter: vmid
-           }
+               xtype: 'proxmoxNodeTasks',
+               iconCls: 'fa fa-list-alt',
+               nodename: nodename,
+               preFilter: {
+                   vmid,
+               },
+           },
        );
 
        if (caps.vms['VM.Monitor'] && !template) {
@@ -211,7 +311,7 @@ Ext.define('PVE.qemu.Config', {
                title: gettext('Monitor'),
                iconCls: 'fa fa-eye',
                itemId: 'monitor',
-               xtype: 'pveQemuMonitor'
+               xtype: 'pveQemuMonitor',
            });
        }
 
@@ -220,26 +320,28 @@ Ext.define('PVE.qemu.Config', {
                title: gettext('Backup'),
                iconCls: 'fa fa-floppy-o',
                xtype: 'pveBackupView',
-               itemId: 'backup'
+               itemId: 'backup',
            },
            {
                title: gettext('Replication'),
                iconCls: 'fa fa-retweet',
                xtype: 'pveReplicaView',
-               itemId: 'replication'
+               itemId: 'replication',
            });
        }
 
-       if (caps.vms['VM.Snapshot'] && !template) {
+       if ((caps.vms['VM.Snapshot'] || caps.vms['VM.Snapshot.Rollback'] ||
+           caps.vms['VM.Audit']) && !template) {
            me.items.push({
                title: gettext('Snapshots'),
                iconCls: 'fa fa-history',
-               xtype: 'pveQemuSnapshotTree',
-               itemId: 'snapshot'
+               type: 'qemu',
+               xtype: 'pveGuestSnapshotTree',
+               itemId: 'snapshot',
            });
        }
 
-       if (caps.vms['VM.Console']) {
+       if (caps.vms['VM.Audit']) {
            me.items.push(
                {
                    xtype: 'pveFirewallRules',
@@ -248,7 +350,7 @@ Ext.define('PVE.qemu.Config', {
                    allow_iface: true,
                    base_url: base_url + '/firewall/rules',
                    list_refs_url: base_url + '/firewall/refs',
-                   itemId: 'firewall'
+                   itemId: 'firewall',
                },
                {
                    xtype: 'pveFirewallOptions',
@@ -258,7 +360,7 @@ Ext.define('PVE.qemu.Config', {
                    title: gettext('Options'),
                    base_url: base_url + '/firewall/options',
                    fwtype: 'vm',
-                   itemId: 'firewall-options'
+                   itemId: 'firewall-options',
                },
                {
                    xtype: 'pveFirewallAliases',
@@ -266,7 +368,7 @@ Ext.define('PVE.qemu.Config', {
                    groups: ['firewall'],
                    iconCls: 'fa fa-external-link',
                    base_url: base_url + '/firewall/aliases',
-                   itemId: 'firewall-aliases'
+                   itemId: 'firewall-aliases',
                },
                {
                    xtype: 'pveIPSet',
@@ -275,17 +377,24 @@ Ext.define('PVE.qemu.Config', {
                    iconCls: 'fa fa-list-ol',
                    base_url: base_url + '/firewall/ipset',
                    list_refs_url: base_url + '/firewall/refs',
-                   itemId: 'firewall-ipset'
+                   itemId: 'firewall-ipset',
                },
-               {
+           );
+       }
+
+       if (caps.vms['VM.Console']) {
+            me.items.push(
+                {
                    title: gettext('Log'),
                    groups: ['firewall'],
                    iconCls: 'fa fa-list',
                    onlineHelp: 'chapter_pve_firewall',
                    itemId: 'firewall-fwlog',
-                   xtype: 'pveLogView',
-                   url: '/api2/extjs' + base_url + '/firewall/log'
-               }
+                   xtype: 'proxmoxLogView',
+                   url: '/api2/extjs' + base_url + '/firewall/log',
+                   log_select_timespan: true,
+                   submitFormat: 'U',
+               },
            );
        }
 
@@ -295,33 +404,47 @@ Ext.define('PVE.qemu.Config', {
                title: gettext('Permissions'),
                iconCls: 'fa fa-unlock',
                itemId: 'permissions',
-               path: '/vms/' + vmid
+               path: '/vms/' + vmid,
            });
        }
 
        me.callParent();
 
+       var prevQMPStatus = 'unknown';
         me.mon(me.statusStore, 'load', function(s, records, success) {
            var status;
            var qmpstatus;
            var spice = false;
+           var xtermjs = false;
+           var lock;
+           var rec;
 
            if (!success) {
-               me.workspace.checkVmMigration(me.pveSelNode);
                status = qmpstatus = 'unknown';
            } else {
-               var rec = s.data.get('status');
+               rec = s.data.get('status');
                status = rec ? rec.data.value : 'unknown';
                rec = s.data.get('qmpstatus');
                qmpstatus = rec ? rec.data.value : 'unknown';
                rec = s.data.get('template');
-               template = rec.data.value || false;
+               template = rec ? rec.data.value : false;
+               rec = s.data.get('lock');
+               lock = rec ? rec.data.value : undefined;
 
-               spice = s.data.get('spice') ? true : false;
+               spice = !!s.data.get('spice');
+               xtermjs = !!s.data.get('serial');
+           }
 
+           rec = s.data.get('tags');
+           tagsContainer.loadTags(rec?.data?.value);
+
+           if (template) {
+               return;
            }
 
-           if (qmpstatus === 'prelaunch' || qmpstatus === 'paused' || qmpstatus === 'suspended') {
+           var resume = ['prelaunch', 'paused', 'suspended'].indexOf(qmpstatus) !== -1;
+
+           if (resume || lock === 'suspended') {
                startBtn.setVisible(false);
                resumeBtn.setVisible(true);
            } else {
@@ -330,11 +453,27 @@ Ext.define('PVE.qemu.Config', {
            }
 
            consoleBtn.setEnableSpice(spice);
+           consoleBtn.setEnableXtermJS(xtermjs);
+
+           statusTxt.update({ lock: lock });
+
+           let guest_running = status === 'running' &&
+               !(qmpstatus === "shutdown" || qmpstatus === "prelaunch");
+           startBtn.setDisabled(!caps.vms['VM.PowerMgmt'] || template || guest_running);
 
-           startBtn.setDisabled(!caps.vms['VM.PowerMgmt'] || status === 'running' || template);
            shutdownBtn.setDisabled(!caps.vms['VM.PowerMgmt'] || status !== 'running');
-           removeBtn.setDisabled(!caps.vms['VM.Allocate'] || status !== 'stopped');
+           me.down('#removeBtn').setDisabled(!caps.vms['VM.Allocate'] || status !== 'stopped');
            consoleBtn.setDisabled(template);
+
+           let wasStopped = ['prelaunch', 'stopped', 'suspended'].indexOf(prevQMPStatus) !== -1;
+           if (wasStopped && qmpstatus === 'running') {
+               let con = me.down('#console');
+               if (con) {
+                   con.reload();
+               }
+           }
+
+           prevQMPStatus = qmpstatus;
        });
 
        me.on('afterrender', function() {
@@ -344,5 +483,5 @@ Ext.define('PVE.qemu.Config', {
        me.on('destroy', function() {
            me.statusStore.stopUpdate();
        });
-   }
+   },
 });