Commit 2538e091 authored by Sébastien Luttringer's avatar Sébastien Luttringer
Browse files

Implement support of SPICE

parent 8d77d9f0
Loading
Loading
Loading
Loading
+131 −0
Original line number Diff line number Diff line
@@ -22,11 +22,15 @@ CloudControl Console related commands
from cloudcontrol.cli.command import TqlCommand
from cloudcontrol.cli.exception import *
from cloudcontrol.cli.printer import FakeTtySocket
from cloudcontrol.cli.tunnel import Forward
from sjrpc.core.exceptions import *
import fcntl
import os
import signal
import struct
import subprocess
import termios
import threading


class Command_shell(TqlCommand):
@@ -113,3 +117,130 @@ class Command_console(TqlCommand):

    def remote_functions(self):
        return set(("console",))


class Viewer(threading.Thread):
    '''
    This object handle the launching of a viewer.
    It handle reading exit code and close the dedicated forward
    '''

    def __init__(self, viewer, cli, host_id, dest_ip, dest_port, graphic):
        threading.Thread.__init__(self)
        self.daemon = True
        self.viewer = viewer
        self.graphic = graphic
        self.cli = cli
        self.rpc = cli.rpc
        self.host_id = host_id
        self.dest_ip = dest_ip
        self.dest_port = dest_port

    def run(self):
        '''Thread entry point'''
        # create forward
        fwd = Forward(self.rpc, self.host_id,
                      "127.0.0.1", 0,
                      self.dest_ip, self.dest_port)
        self.cli.forwards[fwd.id] = fwd
        fwd.start()
        onull = open(os.devnull, "w+")
        if self.graphic == "vnc":
            cmd = "%s '%s::%s'" % (self.viewer, fwd.source_ip,
                fwd.source_port)
        elif self.graphic == "spice":
            cmd = "%s -h '%s' -p '%s'" %  (self.viewer, fwd.source_ip,
                fwd.source_port)
        else:
            raise CmdError("Invalid graphic type: %s" % self.graphic)
        # run vncviewer
        proc = subprocess.Popen(cmd, preexec_fn=os.setpgrp,
                                shell=True, close_fds=True,
                                stdin=onull, stdout=onull, stderr=onull)
        proc.wait()
        fwd.stop()
        del self.cli.forwards[fwd.id]


class Command_vnc(TqlCommand):
    '''Start a graphic vnc console'''

    def __init__(self, cli, argv0):
        TqlCommand.__init__(self, cli, argv0)
        self.remove_option("--direct")
        self.remove_option("--quiet")
        self.add_option("--viewer", default="vncviewer",
                        help="define a custom vncviewer binary")
        self.tql_filter = "&r=vm&status=running"

    def __call__(self, argv):
        # args parse
        self.parse_args(argv)
        if len(self.args) != 1:
            raise cmdBadArgument()
        # list vm and get parent and vncport tag
        tql = self.args[0]
        tql += "&vncport"
        tql += "&p"
        ans = self.rpccall("list", tql, _status=False, _direct=True)
        if len(ans["objects"]) == 0:
            raise cmdError("No selected object")
        for vm in ans["objects"]:
            host_id = vm["p"]
            try:
                dest_port = int(vm["vncport"])
            except ValueError:
                raise cmdError('Invalid value for tag vncport')
            # check if we have the right to forward on this hypervisor
            ans2 = self.rpc.call("list", "id:%s" % host_id, method="forward")
            if len(ans2["objects"]) == 0:
                self.printer.warn("You cannot forward on host %s" % host_id)
                continue
            # launch a vncviewer
            Viewer(self.options.viewer, self.cli,
                      host_id, "127.0.0.1", dest_port, "vnc").start()

    def remote_functions(self):
        return set(("forward",))


class Command_spice(TqlCommand):
    '''Start a graphic spice console'''

    def __init__(self, cli, argv0):
        TqlCommand.__init__(self, cli, argv0)
        self.remove_option("--direct")
        self.remove_option("--quiet")
        self.add_option("--viewer", default="spicec",
                        help="define a custom spicec binary")
        self.tql_filter = "&r=vm&status=running"

    def __call__(self, argv):
        # args parse
        self.parse_args(argv)
        if len(self.args) != 1:
            raise cmdBadArgument()
        # list vm and get parent and spiceport tag
        tql = self.args[0]
        tql += "&spiceport"
        tql += "&p"
        ans = self.rpccall("list", tql, _status=False, _direct=True)
        if len(ans["objects"]) == 0:
            raise cmdError("No selected object")
        for vm in ans["objects"]:
            host_id = vm["p"]
            try:
                dest_port = int(vm["spiceport"])
            except ValueError:
                raise cmdError('Invalid value for tag spiceport')
            # check if we have the right to forward on this hypervisor
            ans2 = self.rpc.call("list", "id:%s" % host_id, method="forward")
            if len(ans2["objects"]) == 0:
                self.printer.warn("You cannot forward on host %s" % host_id)
                continue
            # launch a viewver
            Viewer(self.options.viewer, self.cli,
                      host_id, "127.0.0.1", dest_port, "spice").start()

    def remote_functions(self):
        return set(("forward",))
 No newline at end of file

cloudcontrol/cli/commands/vnc.py

deleted100644 → 0
+0 −104
Original line number Diff line number Diff line
#coding=utf8

# This file is part of CloudControl.
#
# CloudControl is free software: you can redistribute it and/or modify
# it under the terms of the GNU Lesser General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# CloudControl is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public License
# along with CloudControl.  If not, see <http://www.gnu.org/licenses/>.

'''
CloudControl vnc command
'''

from cloudcontrol.cli.command import TqlCommand
from cloudcontrol.cli.exception import *
from cloudcontrol.cli.tunnel import Forward
import os
import subprocess
import threading

class VNCViewer(threading.Thread):
    '''
    This object handle the launching of a vnc viewer.
    It handle reading exit code and close the dedicated forward
    '''

    def __init__(self, viewer, cli, host_id, dest_ip, dest_port):
        threading.Thread.__init__(self)
        self.daemon = True
        self.viewer = viewer
        self.cli = cli
        self.rpc = cli.rpc
        self.host_id = host_id
        self.dest_ip = dest_ip
        self.dest_port = dest_port

    def run(self):
        '''Thread entry point'''
        # create forward
        fwd = Forward(self.rpc, self.host_id,
                      "127.0.0.1", 0,
                      self.dest_ip, self.dest_port)
        self.cli.forwards[fwd.id] = fwd
        fwd.start()
        # run vncviewer
        onull = open(os.devnull, "w+")
        proc = subprocess.Popen("%s '%s::%s'" % (self.viewer,
                                                 fwd.source_ip,
                                                 fwd.source_port),
                                preexec_fn=os.setpgrp,
                                shell=True, close_fds=True,
                                stdin=onull, stdout=onull, stderr=onull)
        proc.wait()
        fwd.stop()
        del self.cli.forwards[fwd.id]

class Command_vnc(TqlCommand):
    '''Start a vnc on a vm'''

    def __init__(self, cli, argv0):
        TqlCommand.__init__(self, cli, argv0)
        self.remove_option("--direct")
        self.remove_option("--quiet")
        self.add_option("--viewer", default="vncviewer",
                        help="define a custom vncviewer binary")
        self.tql_filter = "&r=vm&status=running"

    def __call__(self, argv):
        # args parse
        self.parse_args(argv)
        if len(self.args) != 1:
            raise cmdBadArgument()
        # list vm and get parent and vncport tag
        tql = self.args[0]
        tql += "&vncport"
        tql += "&p"
        ans = self.rpccall("list", tql, _status=False, _direct=True)
        if len(ans["objects"]) == 0:
            raise cmdError("No selected object")
        for vm in ans["objects"]:
            host_id = vm["p"]
            try:
                dest_port = int(vm["vncport"])
            except ValueError:
                raise cmdError('Invalid value for tag vncport')
            # check if we have the right to forward on this hypervisor
            ans2 = self.rpc.call("list", "id:%s" % host_id, method="forward")
            if len(ans2["objects"]) == 0:
                self.printer.warn("You cannot forward on host %s" % host_id)
                continue
            # launch a vncviewer
            VNCViewer(self.options.viewer, self.cli,
                      host_id, "127.0.0.1", dest_port).start()

    def remote_functions(self):
        return set(("forward",))