[pve-devel] [PATCH 7/8] add novnc console to workspace
Stefan Priebe
s.priebe at profihost.ag
Sun Jun 1 22:49:24 CEST 2014
Signed-off-by: Stefan Priebe <s.priebe at 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
More information about the pve-devel
mailing list