diff --git a/cloudcontrol/cli/commands/console.py b/cloudcontrol/cli/commands/console.py index 926f1794bd58caf7cebf3e818745b8a3b98d4729..93ff6af790698949baac39336d1009d91c313332 100644 --- a/cloudcontrol/cli/commands/console.py +++ b/cloudcontrol/cli/commands/console.py @@ -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 diff --git a/cloudcontrol/cli/commands/vnc.py b/cloudcontrol/cli/commands/vnc.py deleted file mode 100644 index 9ec7c5d0b03535044ee3b35fa010697a4e6234ba..0000000000000000000000000000000000000000 --- a/cloudcontrol/cli/commands/vnc.py +++ /dev/null @@ -1,104 +0,0 @@ -#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 . - -''' -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",))