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