diff --git a/www/manager/VNCConsole.js b/www/manager/VNCConsole.js index fba07ffe..e8c869d4 100644 --- a/www/manager/VNCConsole.js +++ b/www/manager/VNCConsole.js @@ -468,3 +468,236 @@ Ext.define('PVE.Shell', { me.callParent(); } }); + +Ext.define('PVE.noVNCConsole', { + extend: 'Ext.panel.Panel', + alias: ['widget.pvenoVNCConsole'], + + initComponent : function() { + var me = this; + + if (!me.url) { + throw "no url specified"; + } + + var myid = me.id + "-vncapp"; + + var box = Ext.create('widget.uxiframe', { + id: myid + }); + + var resize_window = function() { + //console.log("resize"); + + var novnciframe = box.getFrame(); + // console.log("resize " + myid + " " + novnciframe); + // noVNC_canvas + var tbar = me.getDockedItems("[dock=top]")[0]; + var tbh = tbar ? tbar.getHeight() : 0; + + // does not work as ExtJS modifies our iframe id from below + var innerDoc = novnciframe.contentDocument || novnciframe.contentWindow.document; + + var aw = innerDoc.getElementById('noVNC_canvas').width + 8; + var ah = innerDoc.getElementById('noVNC_canvas').height + 8; + + if (aw < 640) { aw = 640; } + if (ah < 400) { ah = 400; } + + var oh; + var ow; + + //console.log("size0 " + aw + " " + ah + " tbh " + tbh); + + if (window.innerHeight) { + oh = window.innerHeight; + ow = window.innerWidth; + } else if (document.documentElement && + document.documentElement.clientHeight) { + oh = document.documentElement.clientHeight; + ow = document.documentElement.clientWidth; + } else if (document.body) { + oh = document.body.clientHeight; + ow = document.body.clientWidth; + } else { + throw "can't get window size"; + } + + var offsetw = aw - ow; + var offseth = ah + tbh - oh; + + if (offsetw !== 0 || offseth !== 0) { + //console.log("try resize by " + offsetw + " " + offseth); + try { window.resizeBy(offsetw, offseth); } catch (e) {} + } + + Ext.Function.defer(resize_window, 1000); + }; + + var start_novnc_viewer = function(param) { + + var urlparams = Ext.urlEncode({ + encrypt: 1, + port: param.port, + password: param.ticket + }); + box.load('/novnc/vnc_pve.html?' + urlparams); + if (me.toplevel) { + Ext.Function.defer(resize_window, 1000); + } + }; + + Ext.apply(me, { + layout: 'fit', + border: false, + autoScroll: me.toplevel ? false : true, + items: box, + reloadnoVNC: function() { + PVE.Utils.API2Request({ + url: me.url, + params: me.params, + method: me.method || 'POST', + failure: function(response, opts) { + box.update(gettext('Error') + ' ' + response.htmlStatus); + }, + success: function(response, opts) { + start_novnc_viewer(response.result.data); + } + }); + } + }); + + me.callParent(); + + if (me.toplevel) { + me.on("render", function() { me.reloadnoVNC();}); + } else { + me.on("show", function() { me.reloadnoVNC();}); + me.on("hide", function() { box.update(""); }); + } + } +}); + +Ext.define('PVE.novncConsole', { + extend: 'PVE.noVNCConsole', + alias: ['widget.pvenovncConsole'], + + initComponent : function() { + var me = this; + + if (!me.nodename) { + throw "no node name specified"; + } + + if (!me.vmid) { + throw "no VM ID specified"; + } + + var vm_command = function(cmd, params, reload) { + PVE.Utils.API2Request({ + params: params, + url: '/nodes/' + me.nodename + '/qemu/' + me.vmid + "/status/" + cmd, + method: 'POST', + waitMsgTarget: me, + failure: function(response, opts) { + Ext.Msg.alert('Error', response.htmlStatus); + }, + success: function() { + if (reload) { + Ext.Function.defer(me.reloadnoVNC, 1000, me); + } + } + }); + }; + + var tbar = [ + { + text: gettext('Start'), + handler: function() { + vm_command("start", {}, 1); + } + }, + { + text: gettext('Shutdown'), + handler: function() { + var msg = Ext.String.format(gettext("Do you really want to shutdown VM {0}?"), me.vmid); + Ext.Msg.confirm(gettext('Confirm'), msg, function(btn) { + if (btn !== 'yes') { + return; + } + vm_command('shutdown'); + }); + } + }, + { + text: gettext('Kill VM'), + handler: function() { + var msg = Ext.String.format(gettext("Do you really want to KILL VM {0}? This can cause data loss!"), me.vmid); + Ext.Msg.confirm(gettext('Confirm'), msg, function(btn) { + if (btn !== 'yes') { + return; + } + vm_command("stop"); + }); + } + }, + { + xtype: 'pveQemuSendKeyMenu', + nodename: me.nodename, + vmid: me.vmid + }, + { + text: gettext('Reset'), + handler: function() { + var msg = Ext.String.format(gettext("Do you really want to reset VM {0}?"), me.vmid); + Ext.Msg.confirm(gettext('Confirm'), msg, function(btn) { + if (btn !== 'yes') { + return; + } + vm_command("reset"); + }); + } + }, + { + text: gettext('Suspend'), + handler: function() { + var msg = Ext.String.format(gettext("Do you really want to suspend VM {0}?"), me.vmid); + Ext.Msg.confirm(gettext('Confirm'), msg, function(btn) { + if (btn !== 'yes') { + return; + } + vm_command("suspend"); + }); + } + }, + { + text: gettext('Resume'), + handler: function() { + vm_command("resume"); + } + }, + // Note: no migrate here, because we can't display migrate log + { + text: gettext('Console'), + handler: function() { + PVE.Utils.openConsoleWindow('kvm', me.vmid, me.nodename, me.vmname); + } + }, + '->', + { + text: gettext('Reload'), + handler: function () { + me.reloadnoVNC(); + } + } + ]; + + Ext.apply(me, { + tbar: tbar, + url: "/nodes/" + me.nodename + "/qemu/" + me.vmid + "/vncproxy", + params: { unsecure: 1, websocket: 1 } + }); + + me.callParent(); + } +}); diff --git a/www/manager/Workspace.js b/www/manager/Workspace.js index 3a99c80a..5efb509c 100644 --- a/www/manager/Workspace.js +++ b/www/manager/Workspace.js @@ -129,6 +129,18 @@ Ext.define('PVE.ConsoleWorkspace', { vmname: param.vmname, toplevel: true }; + } else if (consoleType === 'novnc') { + me.title = "VM " + param.vmid; + if (param.vmname) { + me.title += " ('" + param.vmname + "')"; + } + content = { + xtype: 'pvenovncConsole', + vmid: param.vmid, + nodename: param.node, + vmname: param.vmname, + toplevel: true + }; } else if (consoleType === 'openvz') { me.title = "CT " + param.vmid; if (param.vmname) { diff --git a/www/manager/button/ConsoleButton.js b/www/manager/button/ConsoleButton.js index 6dd45d8f..23cfd749 100644 --- a/www/manager/button/ConsoleButton.js +++ b/www/manager/button/ConsoleButton.js @@ -71,6 +71,12 @@ Ext.define('PVE.button.ConsoleButton', { } }; + var create_novnc_console = function() { + if (me.consoleType === 'kvm') { + PVE.Utils.openConsoleWindow('novnc', me.vmid, me.nodename, me.consoleName); + } + }; + var create_vnc_console = function() { if (me.consoleType === 'kvm') { PVE.Utils.openConsoleWindow('kvm', me.vmid, me.nodename, me.consoleName); @@ -97,6 +103,12 @@ Ext.define('PVE.button.ConsoleButton', { handler: create_vnc_console }); + var novncMenu = Ext.create('Ext.menu.Item', { + text: 'noVNC', + iconCls: 'pve-itype-icon-novnc', + handler: create_novnc_console + }); + Ext.applyIf(me, { text: gettext('Console') }); Ext.apply(me, { @@ -109,7 +121,7 @@ Ext.define('PVE.button.ConsoleButton', { } }, menu: new Ext.menu.Menu({ - items: [ vncMenu, me.spiceMenu ] + items: [ novncMenu, vncMenu, me.spiceMenu ] }) });