mirror of
				https://github.com/retspen/webvirtcloud
				synced 2025-07-31 12:41:08 +00:00 
			
		
		
		
	
		
			
				
	
	
		
			219 lines
		
	
	
	
		
			5 KiB
		
	
	
	
		
			Python
		
	
	
		
			Executable file
		
	
	
	
	
			
		
		
	
	
			219 lines
		
	
	
	
		
			5 KiB
		
	
	
	
		
			Python
		
	
	
		
			Executable file
		
	
	
	
	
| #!/usr/bin/env python3
 | |
| import os
 | |
| import sys
 | |
| import logging
 | |
| import django
 | |
| 
 | |
| import signal
 | |
| signal.signal(signal.SIGCHLD, signal.SIG_IGN)
 | |
| 
 | |
| DIR_PATH = os.path.dirname(os.path.abspath(__file__))
 | |
| ROOT_PATH = os.path.abspath(os.path.join(DIR_PATH, "..", ""))
 | |
| os.environ.setdefault("DJANGO_SETTINGS_MODULE", "webvirtcloud.settings")
 | |
| CERT = DIR_PATH + "/cert.pem"
 | |
| 
 | |
| if ROOT_PATH not in sys.path:
 | |
|     sys.path.append(ROOT_PATH)
 | |
| 
 | |
| django.setup()
 | |
| 
 | |
| import re
 | |
| import tempfile
 | |
| import io
 | |
| import socket
 | |
| import socketio
 | |
| import pty
 | |
| import select
 | |
| import subprocess
 | |
| import struct
 | |
| import fcntl
 | |
| import termios
 | |
| import signal
 | |
| import eventlet
 | |
| import atexit
 | |
| import tty
 | |
| import termios
 | |
| import libvirt
 | |
| 
 | |
| # from six.moves import http_cookies as Cookie
 | |
| from http import cookies as Cookie
 | |
| from webvirtcloud.settings import SOCKETIO_PORT, SOCKETIO_HOST
 | |
| from vrtManager.connection import CONN_SSH, CONN_SOCKET
 | |
| from optparse import OptionParser
 | |
| 
 | |
| parser = OptionParser()
 | |
| 
 | |
| parser.add_option(
 | |
|     "-v",
 | |
|     "--verbose",
 | |
|     dest="verbose",
 | |
|     action="store_true",
 | |
|     help="Verbose mode",
 | |
|     default=False,
 | |
| )
 | |
| 
 | |
| parser.add_option(
 | |
|     "-d", "--debug", dest="debug", action="store_true", help="Debug mode", default=False
 | |
| )
 | |
| 
 | |
| parser.add_option(
 | |
|     "-H",
 | |
|     "--host",
 | |
|     dest="host",
 | |
|     action="store",
 | |
|     help="Listen host",
 | |
|     default=SOCKETIO_HOST,
 | |
| )
 | |
| 
 | |
| parser.add_option(
 | |
|     "-p",
 | |
|     "--port",
 | |
|     dest="port",
 | |
|     action="store",
 | |
|     help="Listen port",
 | |
|     default=SOCKETIO_PORT or 6081,
 | |
| )
 | |
| 
 | |
| (options, args) = parser.parse_args()
 | |
| 
 | |
| FORMAT = "%(asctime)s - %(name)s - %(levelname)s : %(message)s"
 | |
| if options.debug:
 | |
|     logging.basicConfig(level=logging.DEBUG, format=FORMAT)
 | |
|     options.verbose = True
 | |
| elif options.verbose:
 | |
|     logging.basicConfig(level=logging.INFO, format=FORMAT)
 | |
| else:
 | |
|     logging.basicConfig(level=logging.WARNING, format=FORMAT)
 | |
| 
 | |
| async_mode = "eventlet"
 | |
| sio = socketio.Server(async_mode=async_mode, cors_allowed_origins=[])
 | |
| 
 | |
| fd = None
 | |
| child_pid = None
 | |
| 
 | |
| 
 | |
| def get_connection_infos(token):
 | |
|     from instances.models import Instance
 | |
|     from vrtManager.instance import wvmInstance
 | |
| 
 | |
|     try:
 | |
|         temptoken = token.split("-", 1)
 | |
|         host = int(temptoken[0])
 | |
|         uuid = temptoken[1]
 | |
|         instance = Instance.objects.get(compute_id=host, uuid=uuid)
 | |
|         conn = wvmInstance(
 | |
|             instance.compute.hostname,
 | |
|             instance.compute.login,
 | |
|             instance.compute.password,
 | |
|             instance.compute.type,
 | |
|             instance.name,
 | |
|         )
 | |
|     except Exception as e:
 | |
|         logging.error(
 | |
|             "Fail to retrieve console connection infos for token %s : %s" % (token, e)
 | |
|         )
 | |
|         raise
 | |
|     return (instance, conn)
 | |
| 
 | |
| 
 | |
| def set_winsize(fd, row, col, xpix=0, ypix=0):
 | |
|     winsize = struct.pack("HHHH", row, col, xpix, ypix)
 | |
|     fcntl.ioctl(fd, termios.TIOCSWINSZ, winsize)
 | |
| 
 | |
| 
 | |
| def read_and_forward_pty_output():
 | |
|     global fd
 | |
|     max_read_bytes = 1024 * 20
 | |
|     while True:
 | |
|         sio.sleep(0.01)
 | |
|         if fd:
 | |
|             timeout_sec = 0
 | |
|             (data_ready, _, _) = select.select([fd], [], [], timeout_sec)
 | |
|             if data_ready:
 | |
|                 output = os.read(fd, max_read_bytes).decode()
 | |
|                 sio.emit("pty_output", {"output": output})
 | |
|         else:
 | |
|             return
 | |
| 
 | |
| 
 | |
| @sio.event
 | |
| def resize(sid, message):
 | |
|     global fd
 | |
|     if fd:
 | |
|         set_winsize(fd, message["rows"], message["cols"])
 | |
| 
 | |
| 
 | |
| @sio.event
 | |
| def pty_input(sid, message):
 | |
|     global fd
 | |
|     if fd:
 | |
|         os.write(fd, message["input"].encode())
 | |
| 
 | |
| 
 | |
| @sio.event
 | |
| def disconnect_request(sid):
 | |
|     sio.disconnect(sid)
 | |
| 
 | |
| 
 | |
| @sio.event
 | |
| def connect(sid, environ):
 | |
|     global fd
 | |
|     global child_pid
 | |
| 
 | |
|     hcookie = environ.get("HTTP_COOKIE")
 | |
|     if hcookie:
 | |
|         cookie = Cookie.SimpleCookie()
 | |
|         for hcookie_part in hcookie.split(";"):
 | |
|             hcookie_part = hcookie_part.lstrip()
 | |
|             try:
 | |
|                 cookie.load(hcookie_part)
 | |
|             except Cookie.CookieError:
 | |
|                 logging.warn("Found malformed cookie")
 | |
|             else:
 | |
|                 if "token" in cookie:
 | |
|                     token = cookie["token"].value
 | |
| 
 | |
|     if child_pid:
 | |
|         # already started child process, don't start another
 | |
|         # write a new line so that when a client refresh the shell prompt is printed
 | |
|         os.write(fd, str.encode("\n"))
 | |
|         return
 | |
| 
 | |
|     # create child process attached to a pty we can read from and write to
 | |
|     (child_pid, fd) = pty.fork()
 | |
| 
 | |
|     if child_pid == 0:
 | |
|         (instance, conn) = get_connection_infos(token)
 | |
|         uuid = conn.get_uuid()
 | |
|         uri = conn.wvm.getURI()
 | |
| 
 | |
|         subprocess.run(["conf/daemon/consolecallback", uri, uuid])
 | |
|     else:
 | |
|         # this is the parent process fork.
 | |
|         sio.start_background_task(target=read_and_forward_pty_output)
 | |
| 
 | |
| 
 | |
| @sio.event
 | |
| def disconnect(sid):
 | |
| 
 | |
|     global fd
 | |
|     global child_pid
 | |
| 
 | |
|     # kill pty process
 | |
|     try:
 | |
|         os.kill(child_pid, signal.SIGKILL)
 | |
|         os.wait()
 | |
|     except ProcessLookupError:
 | |
|         pass
 | |
|     except ChildProcessError:
 | |
|         pass
 | |
| 
 | |
|     # reset the variables
 | |
|     fd = None
 | |
|     child_pid = None
 | |
| 
 | |
| 
 | |
| app = socketio.WSGIApp(sio)
 | |
| import eventlet
 | |
| 
 | |
| eventlet.wsgi.server(eventlet.listen((options.host, int(options.port))), app)
 |