1
0
Fork 0
mirror of https://github.com/retspen/webvirtcloud synced 2024-11-01 03:54:15 +00:00
webvirtcloud/console/socketiod

220 lines
5 KiB
Text
Raw Normal View History

#!/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__))
2022-11-02 05:54:35 +00:00
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
2022-11-02 05:54:35 +00:00
# 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()
2022-11-02 05:54:35 +00:00
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
2022-11-02 05:54:35 +00:00
def get_connection_infos(token):
from instances.models import Instance
from vrtManager.instance import wvmInstance
try:
2022-11-02 05:54:35 +00:00
temptoken = token.split("-", 1)
host = int(temptoken[0])
uuid = temptoken[1]
instance = Instance.objects.get(compute_id=host, uuid=uuid)
2022-11-02 05:54:35 +00:00
conn = wvmInstance(
instance.compute.hostname,
instance.compute.login,
instance.compute.password,
instance.compute.type,
instance.name,
)
except Exception as e:
logging.error(
2022-11-02 05:54:35 +00:00
"Fail to retrieve console connection infos for token %s : %s" % (token, e)
)
raise
return (instance, conn)
2022-11-02 05:54:35 +00:00
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
2022-11-02 05:54:35 +00:00
@sio.event
2022-11-02 05:54:35 +00:00
def resize(sid, message):
global fd
if fd:
set_winsize(fd, message["rows"], message["cols"])
2022-11-02 05:54:35 +00:00
@sio.event
def pty_input(sid, message):
global fd
if fd:
os.write(fd, message["input"].encode())
2022-11-02 05:54:35 +00:00
@sio.event
def disconnect_request(sid):
sio.disconnect(sid)
2022-11-02 05:54:35 +00:00
@sio.event
def connect(sid, environ):
global fd
global child_pid
2022-11-02 05:54:35 +00:00
hcookie = environ.get("HTTP_COOKIE")
if hcookie:
cookie = Cookie.SimpleCookie()
2022-11-02 05:54:35 +00:00
for hcookie_part in hcookie.split(";"):
hcookie_part = hcookie_part.lstrip()
try:
cookie.load(hcookie_part)
except Cookie.CookieError:
2022-11-02 05:54:35 +00:00
logging.warn("Found malformed cookie")
else:
2022-11-02 05:54:35 +00:00
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)
2022-11-02 05:54:35 +00:00
uuid = conn.get_uuid()
uri = conn.wvm.getURI()
2022-11-02 05:54:35 +00:00
subprocess.run(["conf/daemon/consolecallback", uri, uuid])
else:
# this is the parent process fork.
sio.start_background_task(target=read_and_forward_pty_output)
2022-11-02 05:54:35 +00:00
@sio.event
def disconnect(sid):
global fd
global child_pid
# kill pty process
2023-09-26 06:23:20 +00:00
try:
os.kill(child_pid, signal.SIGKILL)
os.wait()
except ProcessLookupError:
pass
except ChildProcessError:
pass
# reset the variables
fd = None
child_pid = None
2022-11-02 05:54:35 +00:00
app = socketio.WSGIApp(sio)
import eventlet
2022-11-02 05:54:35 +00:00
eventlet.wsgi.server(eventlet.listen((options.host, int(options.port))), app)