]>
Commit | Line | Data |
---|---|---|
5f8b5df0 DM |
1 | Ext.define('PVE.noVncConsole', { |
2 | extend: 'Ext.panel.Panel', | |
3 | alias: 'widget.pveNoVncConsole', | |
4 | ||
5 | nodename: undefined, | |
6 | ||
7 | vmid: undefined, | |
8 | ||
9 | consoleType: undefined, // lxc or kvm | |
10 | ||
11 | initComponent : function() { | |
12 | var me = this; | |
13 | ||
14 | if (!me.nodename) { | |
15 | throw "no node name specified"; | |
16 | } | |
17 | ||
18 | if (!me.consoleType) { | |
19 | throw "no console type specified"; | |
20 | } | |
21 | ||
22 | if (!me.vmid && me.consoleType !== 'shell') { | |
23 | throw "no VM ID specified"; | |
24 | } | |
25 | ||
26 | // always use same iframe, to avoid running several noVnc clients | |
27 | // at same time (to avoid performance problems) | |
28 | var box = Ext.create('widget.uxiframe', { id: "vncconsole" }); | |
29 | ||
30 | Ext.apply(me, { | |
31 | layout: { type: 'fit' }, | |
32 | border: false, | |
33 | items: box, | |
34 | listeners: { | |
35 | show: function() { | |
36 | var url = '/?console=' + me.consoleType + '&novnc=1&node=' + me.nodename + '&resize=scale'; | |
37 | if (me.vmid) { | |
38 | url += '&vmid='+ me.vmid; | |
39 | } | |
40 | box.load(url); | |
41 | } | |
42 | } | |
43 | }); | |
44 | ||
45 | me.callParent(); | |
46 | } | |
47 | }); | |
48 | ||
49 | PVE_vnc_console_event = function(appletid, action, err) { | |
50 | //console.log("TESTINIT param1 " + appletid + " action " + action); | |
51 | ||
52 | if (action === "error") { | |
53 | var compid = appletid.replace("-vncapp", ""); | |
54 | var comp = Ext.getCmp(compid); | |
55 | ||
56 | if (!comp || !comp.vmid || !comp.toplevel) { | |
57 | return; | |
58 | } | |
59 | ||
60 | comp.detectMigratedVM(); | |
61 | } | |
62 | ||
63 | return; | |
64 | }; | |
65 | ||
66 | Ext.define('PVE.VNCConsole', { | |
67 | extend: 'Ext.panel.Panel', | |
68 | alias: ['widget.pveVNCConsole'], | |
69 | ||
70 | novnc: false, | |
71 | ||
72 | last_novnc_state: undefined, | |
73 | last_novnc_msg: undefined, | |
74 | ||
75 | detectMigratedVM: function() { | |
76 | var me = this; | |
77 | ||
78 | if (!me.vmid) { | |
79 | return; | |
80 | } | |
81 | ||
82 | // try to detect migrated VM | |
83 | PVE.Utils.API2Request({ | |
84 | url: '/cluster/resources', | |
85 | method: 'GET', | |
86 | success: function(response) { | |
87 | var list = response.result.data; | |
88 | Ext.Array.each(list, function(item) { | |
89 | if (item.type === 'qemu' && item.vmid == me.vmid) { | |
90 | if (item.node !== me.nodename) { | |
91 | me.nodename = item.node; | |
92 | me.url = "/nodes/" + me.nodename + "/" + item.type + "/" + me.vmid + "/vncproxy"; | |
93 | me.wsurl = "/nodes/" + me.nodename + "/" + item.type + "/" + me.vmid + "/vncwebsocket"; | |
94 | me.reloadApplet(); | |
95 | } | |
96 | return false; // break | |
97 | } | |
98 | }); | |
99 | } | |
100 | }); | |
101 | }, | |
102 | ||
103 | initComponent : function() { | |
104 | var me = this; | |
105 | ||
106 | if (!me.url) { | |
107 | throw "no url specified"; | |
108 | } | |
109 | ||
110 | var myid = me.id + "-vncapp"; | |
111 | ||
112 | me.appletID = myid; | |
113 | ||
114 | var box; | |
115 | ||
116 | if (me.novnc) { | |
117 | if (!me.wsurl) { | |
118 | throw "no web socket url specified"; | |
119 | } | |
120 | box = Ext.create('widget.uxiframe', { id: myid }); | |
121 | } else { | |
122 | box = Ext.create('Ext.Component', { border: false, html: "" }); | |
123 | } | |
124 | ||
125 | var resize_window = function() { | |
126 | //console.log("resize"); | |
127 | ||
128 | var aw; | |
129 | var ah; | |
130 | var applet; | |
131 | ||
132 | if (me.novnc) { | |
133 | var novnciframe = box.getFrame(); | |
134 | // noVNC_canvas | |
135 | var innerDoc = novnciframe.contentDocument || novnciframe.contentWindow.document; | |
136 | aw = innerDoc.getElementById('noVNC_canvas').width; | |
137 | ah = innerDoc.getElementById('noVNC_canvas').height + 8; | |
138 | ||
139 | var novnc_state = innerDoc.getElementById('noVNC_status_state').innerHTML; | |
140 | var novnc_msg = innerDoc.getElementById('noVNC_status_msg').innerHTML; | |
141 | ||
142 | if (novnc_state !== me.last_novnc_state || novnc_msg !== me.last_novnc_msg) { | |
143 | me.last_novnc_state = novnc_state; | |
144 | me.last_novnc_msg = novnc_msg; | |
145 | ||
146 | if (novnc_state !== 'normal') { | |
147 | PVE.Utils.setErrorMask(box, novnc_msg || 'unknown'); | |
148 | } else { | |
149 | PVE.Utils.setErrorMask(box); // clear mask | |
150 | } | |
151 | ||
152 | if (novnc_state === 'disconnected') { | |
153 | me.detectMigratedVM(); | |
154 | } | |
155 | } | |
156 | ||
157 | } else { | |
158 | applet = Ext.getDom(myid); | |
159 | ||
160 | // try again when dom element is available | |
161 | if (!(applet && Ext.isFunction(applet.getPreferredSize))) { | |
162 | return Ext.Function.defer(resize_window, 1000); | |
163 | } | |
164 | ||
165 | var ps = applet.getPreferredSize(); | |
166 | aw = ps.width; | |
167 | ah = ps.height; | |
168 | } | |
169 | ||
170 | if (aw < 640) { aw = 640; } | |
171 | if (ah < 400) { ah = 400; } | |
172 | ||
173 | var tbar = me.getDockedItems("[dock=top]")[0]; | |
174 | var tbh = tbar ? tbar.getHeight() : 0; | |
175 | ||
176 | var oh; | |
177 | var ow; | |
178 | ||
179 | //console.log("size0 " + aw + " " + ah + " tbh " + tbh); | |
180 | ||
181 | if (window.innerHeight) { | |
182 | oh = window.innerHeight; | |
183 | ow = window.innerWidth; | |
184 | } else if (document.documentElement && | |
185 | document.documentElement.clientHeight) { | |
186 | oh = document.documentElement.clientHeight; | |
187 | ow = document.documentElement.clientWidth; | |
188 | } else if (document.body) { | |
189 | oh = document.body.clientHeight; | |
190 | ow = document.body.clientWidth; | |
191 | } else { | |
192 | throw "can't get window size"; | |
193 | } | |
194 | ||
195 | if (!me.novnc) { | |
196 | Ext.fly(applet).setSize(aw, ah + tbh); | |
197 | } | |
198 | ||
199 | var offsetw = aw - ow; | |
200 | var offseth = ah + tbh - oh; | |
201 | ||
202 | if (offsetw !== 0 || offseth !== 0) { | |
203 | //console.log("try resize by " + offsetw + " " + offseth); | |
204 | try { window.resizeBy(offsetw, offseth); } catch (e) {} | |
205 | } | |
206 | ||
207 | Ext.Function.defer(resize_window, 1000); | |
208 | }; | |
209 | ||
210 | var resize_box = function() { | |
211 | if (me.novnc) { | |
212 | throw "implement me"; | |
213 | } else { | |
214 | var applet = Ext.getDom(myid); | |
215 | ||
216 | if ((applet && Ext.isFunction(applet.getPreferredSize))) { | |
217 | var ps = applet.getPreferredSize(); | |
218 | Ext.fly(applet).setSize(ps.width, ps.height); | |
219 | } | |
220 | } | |
221 | ||
222 | Ext.Function.defer(resize_box, 1000); | |
223 | }; | |
224 | ||
225 | var start_vnc_viewer = function(param) { | |
226 | ||
227 | if (me.novnc) { | |
228 | ||
229 | var pveparams = Ext.urlEncode({ | |
230 | port: param.port, | |
231 | vncticket: param.ticket | |
232 | }); | |
233 | ||
234 | var urlparams = Ext.urlEncode({ | |
235 | encrypt: 1, | |
236 | path: "api2/json" + me.wsurl + "?" + pveparams, | |
237 | password: param.ticket | |
238 | }); | |
239 | box.load('/novnc/vnc_pve.html?' + urlparams); | |
240 | ||
241 | } else { | |
242 | ||
243 | var cert = param.cert; | |
244 | cert = cert.replace(/\n/g, "|"); | |
245 | ||
246 | box.update({ | |
247 | id: myid, | |
248 | border: false, | |
249 | tag: 'applet', | |
250 | code: 'com.tigervnc.vncviewer.VncViewer', | |
251 | archive: '/vncterm/VncViewer.jar', | |
252 | // NOTE: set size to '100%' - else resize does not work | |
253 | width: "100%", | |
254 | height: "100%", | |
255 | cn: [ | |
256 | {tag: 'param', name: 'id', value: myid}, | |
257 | {tag: 'param', name: 'PORT', value: param.port}, | |
258 | {tag: 'param', name: 'PASSWORD', value: param.ticket}, | |
259 | {tag: 'param', name: 'USERNAME', value: param.user}, | |
260 | {tag: 'param', name: 'Show Controls', value: 'No'}, | |
261 | {tag: 'param', name: 'Offer Relogin', value: 'No'}, | |
262 | {tag: 'param', name: 'PVECert', value: cert} | |
263 | ] | |
264 | }); | |
265 | } | |
266 | ||
267 | if (me.toplevel) { | |
268 | Ext.Function.defer(resize_window, 1000); | |
269 | } else { | |
270 | Ext.Function.defer(resize_box, 1000); | |
271 | } | |
272 | }; | |
273 | ||
274 | Ext.apply(me, { | |
275 | layout: 'fit', | |
276 | border: false, | |
277 | autoScroll: me.toplevel ? false : true, | |
278 | items: box, | |
279 | reloadApplet: function() { | |
280 | var params = Ext.apply({}, me.params); | |
281 | if (me.novnc) { | |
282 | params.websocket = 1; | |
283 | } | |
284 | PVE.Utils.API2Request({ | |
285 | url: me.url, | |
286 | params: params, | |
287 | method: me.method || 'POST', | |
288 | failure: function(response, opts) { | |
289 | box.update(gettext('Error') + ' ' + response.htmlStatus); | |
290 | }, | |
291 | success: function(response, opts) { | |
292 | start_vnc_viewer(response.result.data); | |
293 | } | |
294 | }); | |
295 | } | |
296 | }); | |
297 | ||
298 | me.callParent(); | |
299 | ||
300 | if (me.toplevel) { | |
301 | me.on("render", me.reloadApplet); | |
302 | } else { | |
303 | me.on("show", me.reloadApplet); | |
304 | me.on("hide", function() { box.update(""); }); | |
305 | } | |
306 | } | |
307 | }); | |
308 | ||
309 | Ext.define('PVE.KVMConsole', { | |
310 | extend: 'PVE.VNCConsole', | |
311 | alias: ['widget.pveKVMConsole'], | |
312 | ||
313 | initComponent : function() { | |
314 | var me = this; | |
315 | ||
316 | if (!me.nodename) { | |
317 | throw "no node name specified"; | |
318 | } | |
319 | ||
320 | if (!me.vmid) { | |
321 | throw "no VM ID specified"; | |
322 | } | |
323 | ||
324 | var baseUrl = "/nodes/" + me.nodename + "/qemu/" + me.vmid; | |
325 | ||
326 | var vm_command = function(cmd, params, reload_applet) { | |
327 | PVE.Utils.API2Request({ | |
328 | params: params, | |
329 | url: baseUrl + "/status/" + cmd, | |
330 | method: 'POST', | |
331 | waitMsgTarget: me, | |
332 | failure: function(response, opts) { | |
333 | Ext.Msg.alert('Error', response.htmlStatus); | |
334 | }, | |
335 | success: function() { | |
336 | if (reload_applet) { | |
337 | Ext.Function.defer(me.reloadApplet, 1000, me); | |
338 | } | |
339 | } | |
340 | }); | |
341 | }; | |
342 | ||
343 | var tbar = [ | |
344 | { | |
345 | text: gettext('Start'), | |
346 | handler: function() { | |
347 | vm_command("start", {}, 1); | |
348 | } | |
349 | }, | |
350 | { | |
351 | text: gettext('Shutdown'), | |
352 | handler: function() { | |
353 | var msg = Ext.String.format(gettext("Do you really want to shutdown VM {0}?"), me.vmid); | |
354 | Ext.Msg.confirm(gettext('Confirm'), msg, function(btn) { | |
355 | if (btn !== 'yes') { | |
356 | return; | |
357 | } | |
358 | vm_command('shutdown'); | |
359 | }); | |
360 | } | |
361 | }, | |
362 | { | |
363 | text: gettext('Stop'), | |
364 | handler: function() { | |
365 | var msg = Ext.String.format(gettext("Do you really want to stop VM {0}?"), me.vmid); | |
366 | Ext.Msg.confirm(gettext('Confirm'), msg, function(btn) { | |
367 | if (btn !== 'yes') { | |
368 | return; | |
369 | } | |
370 | vm_command("stop"); | |
371 | }); | |
372 | } | |
373 | }, | |
374 | { | |
375 | xtype: 'pveQemuSendKeyMenu', | |
376 | nodename: me.nodename, | |
377 | vmid: me.vmid | |
378 | }, | |
379 | { | |
380 | text: gettext('Reset'), | |
381 | handler: function() { | |
382 | var msg = Ext.String.format(gettext("Do you really want to reset VM {0}?"), me.vmid); | |
383 | Ext.Msg.confirm(gettext('Confirm'), msg, function(btn) { | |
384 | if (btn !== 'yes') { | |
385 | return; | |
386 | } | |
387 | vm_command("reset"); | |
388 | }); | |
389 | } | |
390 | }, | |
391 | { | |
392 | text: gettext('Suspend'), | |
393 | handler: function() { | |
394 | var msg = Ext.String.format(gettext("Do you really want to suspend VM {0}?"), me.vmid); | |
395 | Ext.Msg.confirm(gettext('Confirm'), msg, function(btn) { | |
396 | if (btn !== 'yes') { | |
397 | return; | |
398 | } | |
399 | vm_command("suspend"); | |
400 | }); | |
401 | } | |
402 | }, | |
403 | { | |
404 | text: gettext('Resume'), | |
405 | handler: function() { | |
406 | vm_command("resume"); | |
407 | } | |
408 | }, | |
409 | // Note: no migrate here, because we can't display migrate log | |
410 | { | |
411 | text: gettext('Console'), | |
412 | handler: function() { | |
413 | PVE.Utils.openVNCViewer('kvm', me.vmid, me.nodename, me.vmname, me.novnc); | |
414 | } | |
415 | }, | |
416 | '->', | |
417 | { | |
418 | text: gettext('Refresh'), | |
419 | hidden: me.novnc ? true : false, | |
420 | handler: function() { | |
421 | var applet = Ext.getDom(me.appletID); | |
422 | applet.sendRefreshRequest(); | |
423 | } | |
424 | }, | |
425 | { | |
426 | text: gettext('Reload'), | |
427 | handler: function () { | |
428 | me.reloadApplet(); | |
429 | } | |
430 | } | |
431 | ]; | |
432 | ||
433 | ||
434 | Ext.apply(me, { | |
435 | tbar: tbar, | |
436 | url: baseUrl + "/vncproxy", | |
437 | wsurl: baseUrl + "/vncwebsocket" | |
438 | }); | |
439 | ||
440 | me.callParent(); | |
441 | } | |
442 | }); | |
443 | ||
444 | Ext.define('PVE.LxcConsole', { | |
445 | extend: 'PVE.VNCConsole', | |
446 | alias: ['widget.pveLxcConsole'], | |
447 | ||
448 | initComponent : function() { | |
449 | var me = this; | |
450 | ||
451 | if (!me.nodename) { | |
452 | throw "no node name specified"; | |
453 | } | |
454 | ||
455 | if (!me.vmid) { | |
456 | throw "no VM ID specified"; | |
457 | } | |
458 | ||
459 | var baseUrl = "/nodes/" + me.nodename + "/lxc/" + me.vmid; | |
460 | ||
461 | var vm_command = function(cmd, params, reload_applet) { | |
462 | PVE.Utils.API2Request({ | |
463 | params: params, | |
464 | url: baseUrl + "/status/" + cmd, | |
465 | waitMsgTarget: me, | |
466 | method: 'POST', | |
467 | failure: function(response, opts) { | |
468 | Ext.Msg.alert('Error', response.htmlStatus); | |
469 | }, | |
470 | success: function() { | |
471 | if (reload_applet) { | |
472 | Ext.Function.defer(me.reloadApplet, 1000, me); | |
473 | } | |
474 | } | |
475 | }); | |
476 | }; | |
477 | ||
478 | var tbar = [ | |
479 | { | |
480 | text: gettext('Start'), | |
481 | handler: function() { | |
482 | vm_command("start"); | |
483 | } | |
484 | }, | |
485 | { | |
486 | text: gettext('Shutdown'), | |
487 | handler: function() { | |
488 | var msg = Ext.String.format(gettext("Do you really want to shutdown VM {0}?"), me.vmid); | |
489 | Ext.Msg.confirm(gettext('Confirm'), msg, function(btn) { | |
490 | if (btn !== 'yes') { | |
491 | return; | |
492 | } | |
493 | vm_command("shutdown"); | |
494 | }); | |
495 | } | |
496 | }, | |
497 | { | |
498 | text: gettext('Stop'), | |
499 | handler: function() { | |
500 | var msg = Ext.String.format(gettext("Do you really want to stop VM {0}?"), me.vmid); | |
501 | Ext.Msg.confirm(gettext('Confirm'), msg, function(btn) { | |
502 | if (btn !== 'yes') { | |
503 | return; | |
504 | } | |
505 | vm_command("stop"); | |
506 | }); | |
507 | } | |
508 | }, | |
509 | // Note: no migrate here, because we can't display migrate log | |
510 | '->', | |
511 | { | |
512 | text: gettext('Refresh'), | |
513 | hidden: me.novnc ? true : false, | |
514 | handler: function() { | |
515 | var applet = Ext.getDom(me.appletID); | |
516 | applet.sendRefreshRequest(); | |
517 | } | |
518 | }, | |
519 | { | |
520 | text: gettext('Reload'), | |
521 | handler: function () { | |
522 | me.reloadApplet(); | |
523 | } | |
524 | } | |
525 | ]; | |
526 | ||
527 | Ext.apply(me, { | |
528 | tbar: tbar, | |
529 | url: baseUrl + "/vncproxy", | |
530 | wsurl: baseUrl + "/vncwebsocket" | |
531 | }); | |
532 | ||
533 | me.callParent(); | |
534 | } | |
535 | }); | |
536 | ||
537 | Ext.define('PVE.Shell', { | |
538 | extend: 'PVE.VNCConsole', | |
539 | alias: ['widget.pveShell'], | |
540 | ||
541 | ugradeSystem: false, // set to true to run "apt-get dist-upgrade" | |
542 | ||
543 | initComponent : function() { | |
544 | var me = this; | |
545 | ||
546 | if (!me.nodename) { | |
547 | throw "no node name specified"; | |
548 | } | |
549 | ||
550 | var tbar = [ '->' ]; | |
551 | ||
552 | if (!me.novnc) { | |
553 | tbar.push({ | |
554 | text: gettext('Refresh'), | |
555 | handler: function() { | |
556 | var applet = Ext.getDom(me.appletID); | |
557 | applet.sendRefreshRequest(); | |
558 | } | |
559 | }); | |
560 | } | |
561 | ||
562 | if (!me.ugradeSystem) { | |
563 | // we dont want to restart the upgrade script | |
564 | tbar.push({ | |
565 | text: gettext('Reload'), | |
566 | handler: function () { me.reloadApplet(); } | |
567 | }); | |
568 | } | |
569 | ||
570 | tbar.push({ | |
571 | text: gettext('Shell'), | |
572 | handler: function() { | |
573 | PVE.Utils.openVNCViewer('shell', undefined, me.nodename, undefined, me.novnc); | |
574 | } | |
575 | }); | |
576 | ||
577 | var baseUrl = "/nodes/" + me.nodename; | |
578 | ||
579 | Ext.apply(me, { | |
580 | tbar: tbar, | |
581 | url: baseUrl + "/vncshell", | |
582 | wsurl: baseUrl + "/vncwebsocket" | |
583 | }); | |
584 | ||
585 | if (me.ugradeSystem) { | |
586 | me.params = { upgrade: 1 }; | |
587 | } | |
588 | ||
589 | me.callParent(); | |
590 | } | |
591 | }); |