[pve-devel] [PATCH xtermjs 4/4] implement reconnect logic
Dominik Csapak
d.csapak at proxmox.com
Tue Apr 10 14:17:00 CEST 2018
this improves the closed gracefully/error logic
(detects an error if we disconnect immediately after connecting)
and implements a reconnect for vms/containers
if we have a disconnect, we check /cluster/resources
(and /nodes/<node>/lxc/<id>/status/current) if we are migrated
and reconnect the console window or reload the window
Signed-off-by: Dominik Csapak <d.csapak at proxmox.com>
---
src/www/main.js | 138 ++++++++++++++++++++++++++++++++++++++++++++++++++++----
1 file changed, 129 insertions(+), 9 deletions(-)
diff --git a/src/www/main.js b/src/www/main.js
index 461b6e6..6fec72f 100644
--- a/src/www/main.js
+++ b/src/www/main.js
@@ -6,6 +6,7 @@ var states = {
connected: 3,
disconnecting: 4,
disconnected: 5,
+ reconnecting: 6,
};
var term,
@@ -15,7 +16,13 @@ var term,
ticket,
resize,
ping,
- state = states.start;
+ state = states.start,
+ starttime = new Date();
+
+var type = getQueryParameter('console');
+var vmid = getQueryParameter('vmid');
+var vmname = getQueryParameter('vmname');
+var nodename = getQueryParameter('node');
function updateState(newState, msg) {
var timeout, severity, message;
@@ -33,18 +40,30 @@ function updateState(newState, msg) {
timeout = 0;
severity = severities.warning;
break;
+ case states.reconnecting:
+ message = "Reconnecting...";
+ timeout = 0;
+ severity = severities.warning;
+ break;
case states.disconnected:
switch (state) {
case states.start:
case states.connecting:
+ case states.reconnecting:
message = "Connection failed";
timeout = 0;
severity = severities.error;
break;
case states.connected:
case states.disconnecting:
- message = "Connection closed";
- timeout = 0;
+ var time_since_started = new Date() - starttime;
+ timeout = 5000;
+ if (time_since_started > 5*1000) {
+ message = "Connection closed";
+ } else {
+ message = "Connection failed";
+ severity = severities.error;
+ }
break;
case states.disconnected:
// no state change
@@ -81,10 +100,6 @@ function createTerminal() {
protocol = (location.protocol === 'https:') ? 'wss://' : 'ws://';
var params = {};
- var type = getQueryParameter('console');
- var vmid = getQueryParameter('vmid');
- var vmname = getQueryParameter('vmname');
- var nodename = getQueryParameter('node');
var url = '/nodes/' + nodename;
switch (type) {
case 'kvm':
@@ -110,8 +125,8 @@ function createTerminal() {
socket = new WebSocket(socketURL, 'binary');
socket.binaryType = 'arraybuffer';
socket.onopen = runTerminal;
- socket.onclose = stopTerminal;
- socket.onerror = errorTerminal;
+ socket.onclose = tryReconnect;
+ socket.onerror = tryReconnect;
window.onbeforeunload = stopTerminal;
updateState(states.connecting);
},
@@ -160,6 +175,111 @@ function runTerminal() {
setTimeout(function() {term.fit();}, 250);
}
+function getLxcStatus(callback) {
+ API2Request({
+ method: 'GET',
+ url: '/nodes/' + nodename + '/lxc/' + vmid + '/status/current',
+ success: function(result) {
+ if (typeof callback === 'function') {
+ callback(true, result);
+ }
+ },
+ failure: function(msg) {
+ if (typeof callback === 'function') {
+ callback(false, msg);
+ }
+ }
+ });
+}
+
+function checkMigration() {
+ var apitype = type;
+ if (apitype === 'kvm') {
+ apitype = 'qemu';
+ }
+ API2Request({
+ method: 'GET',
+ params: {
+ type: 'vm'
+ },
+ url: '/cluster/resources',
+ success: function(result) {
+ // if not yet migrated , wait and try again
+ // if not migrating and stopped, cancel
+ // if started, connect
+ result.data.forEach(function(entity) {
+ if (entity.id === (apitype + '/' + vmid)) {
+ var started = entity.status === 'running';
+ var migrated = entity.node !== nodename;
+ if (migrated) {
+ if (started) {
+ // goto different node
+ location.href = '?console=' + type +
+ '&xtermjs=1&vmid=' + vmid + '&vmname=' +
+ vmname + '&node=' + entity.node;
+ } else {
+ // wait again
+ updateState(states.reconnecting, 'waiting for migration to finish...');
+ setTimeout(checkMigration, 5000);
+ }
+ } else {
+ if (type === 'lxc') {
+ // we have to check the status of the
+ // container to know if it has the
+ // migration lock
+ getLxcStatus(function(success, result) {
+ if (success) {
+ if (result.data.lock === 'migrate') {
+ // still waiting
+ updateState(states.reconnecting, 'waiting for migration to finish...');
+ setTimeout(checkMigration, 5000);
+ } else {
+ stopTerminal();
+ }
+ } else {
+ // probably the status call failed because
+ // the ct is already somewhere else, so retry
+ setTimeout(checkMigration, 1000);
+ }
+ });
+ } else if (started) {
+ // this happens if we have old data in
+ // /cluster/resources, or the connection
+ // disconnected, so simply try to reload here
+ location.reload();
+ } else if (type === 'kvm') {
+ // it seems the guest simply stopped
+ stopTerminal();
+ }
+ }
+
+ return;
+ }
+ });
+ },
+ failure: function(msg) {
+ errorTerminal({msg: msg});
+ }
+ });
+}
+
+function tryReconnect() {
+ var time_since_started = new Date() - starttime;
+ var type = getQueryParameter('console');
+ if (time_since_started < 5*1000) { // 5 seconds
+ stopTerminal({});
+ return;
+ } else if (type === 'shell') {
+ updateState(states.reconnecting, 'trying to reconnect...');
+ setTimeout(function() {
+ location.reload();
+ }, 1000);
+ }
+
+ updateState(states.disconnecting, 'Detecting migration...');
+ setTimeout(checkMigration, 5000);
+}
+
function stopTerminal(event) {
term.off('resize');
term.off('data');
--
2.11.0
More information about the pve-devel
mailing list