Signed-off-by: Stefan Priebe <s.pri...@profihost.ag> --- www/manager/Workspace.js | 12 + www/manager/button/ConsoleButton.js | 717 ++++++++++++++++++++++++++++++++++- 2 files changed, 728 insertions(+), 1 deletion(-)
diff --git a/www/manager/Workspace.js b/www/manager/Workspace.js index e1f4405..a890e99 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 6dd45d8..5e763f0 100644 --- a/www/manager/button/ConsoleButton.js +++ b/www/manager/button/ConsoleButton.js @@ -1,3 +1,706 @@ +PVE_vnc_console_event = function(appletid, action, err) { + //console.log("TESTINIT param1 " + appletid + " action " + action); + + if (action === "error") { + var compid = appletid.replace("-vncapp", ""); + var comp = Ext.getCmp(compid); + + if (!comp || !comp.vmid || !comp.toplevel) { + return; + } + + // try to detect migrated VM + PVE.Utils.API2Request({ + url: '/cluster/resources', + method: 'GET', + success: function(response) { + var list = response.result.data; + Ext.Array.each(list, function(item) { + if (item.type === 'qemu' && item.vmid == comp.vmid) { + if (item.node !== comp.nodename) { + //console.log("MOVED VM to node " + item.node); + comp.nodename = item.node; + comp.url = "/nodes/" + comp.nodename + "/" + item.type + "/" + comp.vmid + "/vncproxy"; + //console.log("NEW URL " + comp.url); + comp.reloadApplet(); + } + return false; // break + } + }); + } + }); + } + + return; + /* + var el = Ext.get(appletid); + if (!el) + return; + + if (action === "close") { + // el.remove(); + } else if (action === "error") { + // console.log("TESTERROR: " + err); + // var compid = appletid.replace("-vncapp", ""); + // var comp = Ext.getCmp(compid); + } + + //Ext.get('mytestid').remove(); + */ + +}; + +Ext.define('PVE.VNCConsole', { + extend: 'Ext.panel.Panel', + alias: ['widget.pveVNCConsole'], + + initComponent : function() { + var me = this; + + if (!me.url) { + throw "no url specified"; + } + + var myid = me.id + "-vncapp"; + + me.appletID = myid; + + var box = Ext.create('Ext.Component', { + border: false, + html: "" + }); + + var resize_window = function() { + //console.log("resize"); + + var applet = Ext.getDom(myid); + //console.log("resize " + myid + " " + applet); + + // try again when dom element is available + if (!(applet && Ext.isFunction(applet.getPreferredSize))) { + return Ext.Function.defer(resize_window, 1000); + } + + var tbar = me.getDockedItems("[dock=top]")[0]; + var tbh = tbar ? tbar.getHeight() : 0; + var ps = applet.getPreferredSize(); + var aw = ps.width; + var ah = ps.height; + + 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"; + } + + Ext.fly(applet).setSize(aw, ah + tbh); + + 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 resize_box = function() { + var applet = Ext.getDom(myid); + + if ((applet && Ext.isFunction(applet.getPreferredSize))) { + var ps = applet.getPreferredSize(); + Ext.fly(applet).setSize(ps.width, ps.height); + } + + Ext.Function.defer(resize_box, 1000); + }; + + var start_vnc_viewer = function(param) { + var cert = param.cert; + cert = cert.replace(/\n/g, "|"); + + box.update({ + id: myid, + border: false, + tag: 'applet', + code: 'com.tigervnc.vncviewer.VncViewer', + archive: '/vncterm/VncViewer.jar', + // NOTE: set size to '100%' - else resize does not work + width: "100%", + height: "100%", + cn: [ + {tag: 'param', name: 'id', value: myid}, + {tag: 'param', name: 'PORT', value: param.port}, + {tag: 'param', name: 'PASSWORD', value: param.ticket}, + {tag: 'param', name: 'USERNAME', value: param.user}, + {tag: 'param', name: 'Show Controls', value: 'No'}, + {tag: 'param', name: 'Offer Relogin', value: 'No'}, + {tag: 'param', name: 'PVECert', value: cert} + ] + }); + if (me.toplevel) { + Ext.Function.defer(resize_window, 1000); + } else { + Ext.Function.defer(resize_box, 1000); + } + }; + + Ext.apply(me, { + layout: 'fit', + border: false, + autoScroll: me.toplevel ? false : true, + items: box, + reloadApplet: 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_vnc_viewer(response.result.data); + } + }); + } + }); + + me.callParent(); + + if (me.toplevel) { + me.on("render", function() { me.reloadApplet();}); + } else { + me.on("show", function() { me.reloadApplet();}); + me.on("hide", function() { box.update(""); }); + } + } +}); + +Ext.define('PVE.KVMConsole', { + extend: 'PVE.VNCConsole', + alias: ['widget.pveKVMConsole'], + + 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_applet) { + 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_applet) { + Ext.Function.defer(me.reloadApplet, 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('Refresh'), + handler: function() { + var applet = Ext.getDom(me.appletID); + applet.sendRefreshRequest(); + } + }, + { + text: gettext('Reload'), + handler: function () { + me.reloadApplet(); + } + } + ]; + + Ext.apply(me, { + tbar: tbar, + url: "/nodes/" + me.nodename + "/qemu/" + me.vmid + "/vncproxy" + }); + + 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(); + } +}); + +Ext.define('PVE.OpenVZConsole', { + extend: 'PVE.VNCConsole', + alias: ['widget.pveOpenVZConsole'], + + 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_applet) { + PVE.Utils.API2Request({ + params: params, + url: '/nodes/' + me.nodename + '/openvz/' + me.vmid + "/status/" + cmd, + waitMsgTarget: me, + method: 'POST', + failure: function(response, opts) { + Ext.Msg.alert('Error', response.htmlStatus); + }, + success: function() { + if (reload_applet) { + Ext.Function.defer(me.reloadApplet, 1000, me); + } + } + }); + }; + + var tbar = [ + { + text: gettext('Start'), + handler: function() { + vm_command("start"); + } + }, + { + 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('Stop'), + handler: function() { + var msg = Ext.String.format(gettext("Do you really want to stop VM {0}?"), me.vmid); + Ext.Msg.confirm(gettext('Confirm'), msg, function(btn) { + if (btn !== 'yes') { + return; + } + vm_command("stop"); + }); + } + }, + // Note: no migrate here, because we can't display migrate log + '->', + { + text: gettext('Refresh'), + handler: function() { + var applet = Ext.getDom(me.appletID); + applet.sendRefreshRequest(); + } + }, + { + text: gettext('Reload'), + handler: function () { + me.reloadApplet(); + } + } + ]; + + Ext.apply(me, { + tbar: tbar, + url: "/nodes/" + me.nodename + "/openvz/" + me.vmid + "/vncproxy" + }); + + me.callParent(); + } +}); + +Ext.define('PVE.Shell', { + extend: 'PVE.VNCConsole', + alias: ['widget.pveShell'], + + ugradeSystem: false, // set to true to run "apt-get dist-upgrade" + + initComponent : function() { + var me = this; + + if (!me.nodename) { + throw "no node name specified"; + } + + var tbar = [ + '->', + { + text: gettext('Refresh'), + handler: function() { + var applet = Ext.getDom(me.appletID); + applet.sendRefreshRequest(); + } + } + ]; + + if (!me.ugradeSystem) { + // we dont want to restart the upgrade script + tbar.push([ + { + text: gettext('Reload'), + handler: function () { me.reloadApplet(); } + }]); + } + + tbar.push([ + { + text: gettext('Shell'), + handler: function() { + PVE.Utils.openConsoleWindow('shell', undefined, me.nodename); + } + } + ]); + + + Ext.apply(me, { + tbar: tbar, + url: "/nodes/" + me.nodename + "/vncshell" + }); + + if (me.ugradeSystem) { + me.params = { upgrade: 1 }; + } + + me.callParent(); + } +}); Ext.define('PVE.button.ConsoleButton', { extend: 'Ext.button.Split', alias: 'widget.pveConsoleButton', @@ -71,6 +774,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 +806,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 +824,7 @@ Ext.define('PVE.button.ConsoleButton', { } }, menu: new Ext.menu.Menu({ - items: [ vncMenu, me.spiceMenu ] + items: [ novncMenu, vncMenu, me.spiceMenu ] }) }); -- 1.7.10.4 _______________________________________________ pve-devel mailing list pve-devel@pve.proxmox.com http://pve.proxmox.com/cgi-bin/mailman/listinfo/pve-devel