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)
 |