]> git.proxmox.com Git - pve-xtermjs.git/blob - src/www/main.js
reload on container reboot
[pve-xtermjs.git] / src / www / main.js
1 console.log('xtermjs: starting');
2
3 var states = {
4 start: 1,
5 connecting: 2,
6 connected: 3,
7 disconnecting: 4,
8 disconnected: 5,
9 reconnecting: 6,
10 };
11
12 var term,
13 protocol,
14 socketURL,
15 socket,
16 ticket,
17 resize,
18 ping,
19 state = states.start,
20 starttime = new Date();
21
22 var type = getQueryParameter('console');
23 var vmid = getQueryParameter('vmid');
24 var vmname = getQueryParameter('vmname');
25 var nodename = getQueryParameter('node');
26
27 function updateState(newState, msg) {
28 var timeout, severity, message;
29 switch (newState) {
30 case states.connecting:
31 message = "Connecting...";
32 timeout = 0;
33 severity = severities.warning;
34 break;
35 case states.connected:
36 message = "Connected";
37 break;
38 case states.disconnecting:
39 message = "Disconnecting...";
40 timeout = 0;
41 severity = severities.warning;
42 break;
43 case states.reconnecting:
44 message = "Reconnecting...";
45 timeout = 0;
46 severity = severities.warning;
47 break;
48 case states.disconnected:
49 switch (state) {
50 case states.start:
51 case states.connecting:
52 case states.reconnecting:
53 message = "Connection failed";
54 timeout = 0;
55 severity = severities.error;
56 break;
57 case states.connected:
58 case states.disconnecting:
59 var time_since_started = new Date() - starttime;
60 timeout = 5000;
61 if (time_since_started > 5*1000) {
62 message = "Connection closed";
63 } else {
64 message = "Connection failed";
65 severity = severities.error;
66 }
67 break;
68 case states.disconnected:
69 // no state change
70 break;
71 default:
72 throw "unknown state";
73 }
74 break;
75 default:
76 throw "unknown state";
77 }
78 if (msg) {
79 message += " (" + msg + ")";
80 }
81 state = newState;
82 showMsg(message, timeout, severity);
83 }
84
85 var terminalContainer = document.getElementById('terminal-container');
86 document.getElementById('status_bar').addEventListener('click', hideMsg);
87 Terminal.applyAddon(fit);
88
89 createTerminal();
90
91 function createTerminal() {
92 term = new Terminal(getTerminalSettings());
93
94 term.on('resize', function (size) {
95 if (state === states.connected) {
96 socket.send("1:" + size.cols + ":" + size.rows + ":");
97 }
98 });
99
100 protocol = (location.protocol === 'https:') ? 'wss://' : 'ws://';
101
102 var params = {};
103 var url = '/nodes/' + nodename;
104 switch (type) {
105 case 'kvm':
106 url += '/qemu/' + vmid;
107 break;
108 case 'lxc':
109 url += '/lxc/' + vmid;
110 break;
111 case 'upgrade':
112 params.upgrade = 1;
113 break;
114 }
115 API2Request({
116 method: 'POST',
117 params: params,
118 url: url + '/termproxy',
119 success: function(result) {
120 var port = encodeURIComponent(result.data.port);
121 ticket = result.data.ticket;
122 socketURL = protocol + location.hostname + ((location.port) ? (':' + location.port) : '') + '/api2/json' + url + '/vncwebsocket?port=' + port + '&vncticket=' + encodeURIComponent(ticket);
123
124 term.open(terminalContainer, true);
125 socket = new WebSocket(socketURL, 'binary');
126 socket.binaryType = 'arraybuffer';
127 socket.onopen = runTerminal;
128 socket.onclose = tryReconnect;
129 socket.onerror = tryReconnect;
130 window.onbeforeunload = stopTerminal;
131 updateState(states.connecting);
132 },
133 failure: function(msg) {
134 updateState(states.disconnected,msg);
135 }
136 });
137
138 }
139
140 function runTerminal() {
141 socket.onmessage = function(event) {
142 var answer = Utf8ArrayToStr(event.data);
143 if (state === states.connected) {
144 term.write(answer);
145 } else if(state === states.connecting) {
146 if (answer.slice(0,2) === "OK") {
147 updateState(states.connected);
148 term.write(answer.slice(2));
149 } else {
150 socket.close();
151 }
152 }
153 };
154
155 term.on('data', function(data) {
156 if (state === states.connected) {
157 socket.send("0:" + unescape(encodeURIComponent(data)).length.toString() + ":" + data);
158 }
159 });
160
161 ping = setInterval(function() {
162 socket.send("2");
163 }, 30*1000);
164
165 window.addEventListener('resize', function() {
166 clearTimeout(resize);
167 resize = setTimeout(function() {
168 // done resizing
169 term.fit();
170 }, 250);
171 });
172
173 socket.send(PVE.UserName + ':' + ticket + "\n");
174
175 // initial focus and resize
176 setTimeout(function() {
177 term.focus();
178 term.fit();
179 }, 250);
180 }
181
182 function getLxcStatus(callback) {
183 API2Request({
184 method: 'GET',
185 url: '/nodes/' + nodename + '/lxc/' + vmid + '/status/current',
186 success: function(result) {
187 if (typeof callback === 'function') {
188 callback(true, result);
189 }
190 },
191 failure: function(msg) {
192 if (typeof callback === 'function') {
193 callback(false, msg);
194 }
195 }
196 });
197 }
198
199 function checkMigration() {
200 var apitype = type;
201 if (apitype === 'kvm') {
202 apitype = 'qemu';
203 }
204 API2Request({
205 method: 'GET',
206 params: {
207 type: 'vm'
208 },
209 url: '/cluster/resources',
210 success: function(result) {
211 // if not yet migrated , wait and try again
212 // if not migrating and stopped, cancel
213 // if started, connect
214 result.data.forEach(function(entity) {
215 if (entity.id === (apitype + '/' + vmid)) {
216 var started = entity.status === 'running';
217 var migrated = entity.node !== nodename;
218 if (migrated) {
219 if (started) {
220 // goto different node
221 location.href = '?console=' + type +
222 '&xtermjs=1&vmid=' + vmid + '&vmname=' +
223 vmname + '&node=' + entity.node;
224 } else {
225 // wait again
226 updateState(states.reconnecting, 'waiting for migration to finish...');
227 setTimeout(checkMigration, 5000);
228 }
229 } else {
230 if (type === 'lxc') {
231 // we have to check the status of the
232 // container to know if it has the
233 // migration lock
234 getLxcStatus(function(success, result) {
235 if (success) {
236 if (result.data.lock === 'migrate') {
237 // still waiting
238 updateState(states.reconnecting, 'waiting for migration to finish...');
239 setTimeout(checkMigration, 5000);
240 } else if (started) {
241 // container was rebooted
242 location.reload();
243 } else {
244 stopTerminal();
245 }
246 } else {
247 // probably the status call failed because
248 // the ct is already somewhere else, so retry
249 setTimeout(checkMigration, 1000);
250 }
251 });
252 } else if (started) {
253 // this happens if we have old data in
254 // /cluster/resources, or the connection
255 // disconnected, so simply try to reload here
256 location.reload();
257 } else if (type === 'kvm') {
258 // it seems the guest simply stopped
259 stopTerminal();
260 }
261 }
262
263 return;
264 }
265 });
266 },
267 failure: function(msg) {
268 errorTerminal({msg: msg});
269 }
270 });
271 }
272
273 function tryReconnect() {
274 var time_since_started = new Date() - starttime;
275 var type = getQueryParameter('console');
276 if (time_since_started < 5*1000) { // 5 seconds
277 stopTerminal();
278 return;
279 } else if (type === 'shell') {
280 updateState(states.reconnecting, 'trying to reconnect...');
281 setTimeout(function() {
282 location.reload();
283 }, 1000);
284 }
285
286 updateState(states.disconnecting, 'Detecting migration...');
287 setTimeout(checkMigration, 5000);
288 }
289
290 function stopTerminal(event) {
291 event = event || {};
292 term.off('resize');
293 term.off('data');
294 clearInterval(ping);
295 socket.close();
296 updateState(states.disconnected, event.msg + event.code);
297 }
298
299 function errorTerminal(event) {
300 even = event || {};
301 term.off('resize');
302 term.off('data');
303 clearInterval(ping);
304 socket.close();
305 term.destroy();
306 updateState(states.disconnected, event.msg + event.code);
307 }