Loading cccli/command.py +0 −43 Original line number Diff line number Diff line Loading @@ -414,46 +414,3 @@ class TqlCommand(RemoteCommand): args2[self.options.tql_index] = "id=%s"%obj["id"] # now we can make call self._unsecure_rpccall(args2, **kwargs) class ConsoleCommand(TqlCommand): def __init__(self, cli, argv0): TqlCommand.__init__(self, cli, argv0) def start_console(self, stdin=sys.stdin, stdout=sys.stdout, stderr=sys.stderr): self.stdin = stdin.fileno() self.stdout = stdout.fileno() self.stderr = stderr.fileno() if self.stdin == sys.stdin.fileno(): sys.stdin.flush() if self.stdout == sys.stdout.fileno(): sys.stdout.flush() if self.stderr == sys.stderr.fileno(): sys.stderr.flush() self._old_settings = termios.tcgetattr(self.stdout) tty.setraw(self.stdin) self.running = True def stop_console(self): # Reset terminal attributes. termios.tcsetattr(sys.stdout, termios.TCSADRAIN, self._old_settings) self.running = False def recv(self, size): return os.read(self.stdin, 1024) def send(self, data): return os.write(self.stdout, data) def fileno(self): return self.stdin def setblocking(self, blocking): # Do NOT be in non-blocking mode : non-blocking mode # causes rshell to get EAGAIN errors when having to # output loads of data, causing the TTY to break. return def close(self): # Reset terminal attributes. termios.tcsetattr(sys.stdout, termios.TCSADRAIN, self._old_settings) cccli/commands/console.py +123 −49 Original line number Diff line number Diff line Loading @@ -2,32 +2,71 @@ #coding=utf8 ''' Connection to a remote shell CloudControl Console related commands ''' import os import sys import tty from cccli.command import TqlCommand from cccli.exception import * from cccli.printer import Printer, color from sjrpc.core import AsyncWatcher from sjrpc.core.exceptions import * from sjrpc.core.protocols import TunnelProtocol import fcntl import os import signal import struct import sys import termios import threading import time import tty from cccli.exception import * from sjrpc.core.exceptions import * from cccli.printer import Printer, color from cccli.command import ConsoleCommand class FakeTtySocket(object): from sjrpc.core.protocols import TunnelProtocol from sjrpc.core import AsyncWatcher '''Give a tty a socket interface This object cannot handle sigwinch directly because close can be called by remote RPC and by the way, called outside the main thread and fail. ''' def __init__(self): if not os.isatty(0): raise cmdError("stdin is not tty") # flush stdin sys.stdin.flush() # save termios settings self._tc_attr = termios.tcgetattr(0) # set tty in raw mode tty.setraw(0) def recv(self, size): return os.read(0, 1024) def send(self, data): return os.write(0, data) def fileno(self): return 0 def setblocking(self, blocking): # Do NOT be in non-blocking mode : non-blocking mode # causes rshell to get EAGAIN errors when having to # output loads of data, causing the TTY to break. return def close(self): # reset tty original state termios.tcsetattr(0, termios.TCSADRAIN, self._tc_attr) class Command_console(ConsoleCommand): '''Start a remote shell on the provided host''' class Command_rshell(TqlCommand): '''Start a shell on the provided host''' def __init__(self, cli, argv0): ConsoleCommand.__init__(self, cli, argv0) #self.tql_filter = "&con" TqlCommand.__init__(self, cli, argv0) self.remove_option("--direct") self.remove_option("--quiet") self.tql_filter = "&con" def __call__(self, argv): # args parse Loading @@ -35,42 +74,77 @@ class Command_console(ConsoleCommand): if len(self.args) != 1: raise cmdBadArgument() # rpccall self.rpccall("console", self.args[0], _callback=self._cb_open_shell, _status=True, _direct=True) def _cb_open_shell(self, response): if not 'objects' in response: return response = response['objects'][0] if not 'status' in response: return if response['status'] == 'success': label = response['output'] ans = self.rpccall("rshell", self.args[0], _status=False, _direct=True) # alias first object obj = ans['objects'][0] # check response is successful if obj['status'] != 'success': raise cmdError("%s: %s" % (obj["status"], obj["message"])) # create a callbeck function for sigwinch def _cb_sig_resize(*args): '''Callback for terminal resized''' size = struct.pack('HHHH', 0, 0, 0, 0) size = fcntl.ioctl(0, termios.TIOCGWINSZ, size) size = struct.unpack('HHHH', size) try: size = 0 self.start_console(sys.stdin, sys.stdout, sys.stderr) self.rpc.call('rshell_resize', obj["output"], *size) except RpcError as e: # we dont care of failure of this self.printer.debug("Terminal resize failed: %s" % e) # Connect to remote end using sjrpc tun = self.rpc.create_tunnel(endpoint=self) # Save current terminal size size = struct.pack('HHHH', 0, 0, 0, 0) size = fcntl.ioctl(sys.stdout.fileno(), termios.TIOCGWINSZ, size) while self.running: time.sleep(1) except Exception: # Cleanup self.stop_console() tun.shutdown() tun.close() # Restore terminal size fcntl.ioctl(sys.stdout.fileno(), termios.TIOCSWINSZ, size) raise else: # Cleanup self.stop_console() tun.shutdown() tun = self.rpc.create_tunnel(obj["output"], endpoint=FakeTtySocket()) try: signal.signal(signal.SIGWINCH, _cb_sig_resize) # we need an async watcher to be able to handle sigwinch watcher = AsyncWatcher() watcher.register(self.rpc.rpc, "rshell_wait", obj["output"]) watcher.wait() finally: # restore original signal signal.signal(signal.SIGWINCH, signal.SIG_DFL) # shutdown is empty, useless #tun.shutdown() # close tunnel if sjrpc doesn't do it tun.close() # Restore terminal size fcntl.ioctl(sys.stdout.fileno(), termios.TIOCSWINSZ, size) def remote_functions(self): return set(("rshell",)) class Command_console(TqlCommand): '''Start a vm console using libvirt''' def __init__(self, cli, argv0): TqlCommand.__init__(self, cli, argv0) self.remove_option("--direct") self.remove_option("--quiet") self.tql_filter = "&r=vm&status=running" def __call__(self, argv): # args parse self.parse_args(argv) if len(self.args) != 1: raise cmdBadArgument() # rpccall ans = self.rpccall("console", self.args[0], _status=False, _direct=True) # alias first object obj = ans['objects'][0] # check everything is ok if obj['status'] != 'success': raise cmdError("%s: %s" % (obj["status"], obj["message"])) # Connect to remote end using sjrpc ev = threading.Event() def cb_on_close(tun): tun.cb_default_on_close(tun) ev.set() try: tun = self.rpc.create_tunnel(obj["output"], endpoint=FakeTtySocket(), on_close=cb_on_close) ev.wait() finally: # see tun.close in previous command tun.close() def remote_functions(self): return set(("console",)) cccli/commands/rshell.pydeleted 100644 → 0 +0 −95 Original line number Diff line number Diff line #!/usr/bin/env python #coding=utf8 ''' Connection to a remote shell ''' import os import sys import tty import fcntl import signal import struct import termios from cccli.exception import * from sjrpc.core.exceptions import * from cccli.printer import Printer, color from cccli.command import ConsoleCommand from sjrpc.core.protocols import TunnelProtocol from sjrpc.core import AsyncWatcher class Command_rshell(ConsoleCommand): '''Start a remote shell on the provided host''' def __init__(self, cli, argv0): ConsoleCommand.__init__(self, cli, argv0) self.tql_filter = "&con" def __call__(self, argv): # args parse self.parse_args(argv) if len(self.args) != 1: raise cmdBadArgument() # rpccall self.rpccall("rshell", self.args[0], _callback=self._cb_open_shell, _status=True, _direct=True) def _cb_open_shell(self, response): tun = None if not 'objects' in response: return response = response['objects'][0] if not 'status' in response: return if response['status'] == 'success': label = response['output'] try: # Create terminal self.start_console(sys.stdin, sys.stdout, sys.stderr) # Connect to remote end using sjrpc tun = self.rpc.create_tunnel(endpoint=self) # Terminal resizing event handler def sig_resize(signum, frame): size = struct.pack('HHHH', 0, 0, 0, 0) size = fcntl.ioctl(sys.stdout.fileno(), termios.TIOCGWINSZ, size) size = struct.unpack('HHHH', size) self.rpc.call('rshell_resize', label, *size) signal.signal(signal.SIGWINCH, sig_resize) # Init size for term size = struct.pack('HHHH', 0, 0, 0, 0) size = fcntl.ioctl(sys.stdout.fileno(), termios.TIOCGWINSZ, size) size = struct.unpack('HHHH', size) self.rpc.call('rshell_resize', label, *size) # Setup and run event loop watcher = AsyncWatcher() watcher.register(self.rpc.rpc, 'rshell_wait', label) while self.running: returned = watcher.wait(1) if returned: if 'return' in returned[0]: rcode = returned[0]['return'] break elif 'error' in returned[0]: raise RpcError(Exception(returned[0]['error']['message']), Exception(returned[0]['error']['message'])) # Clear signal handlers signal.signal(signal.SIGWINCH, signal.SIG_IGN) except Exception: # Cleanup self.stop_console() if tun: tun.shutdown() tun.close() raise else: # Cleanup self.stop_console() if tun: tun.shutdown() tun.close() def remote_functions(self): return set(("rshell",)) Loading
cccli/command.py +0 −43 Original line number Diff line number Diff line Loading @@ -414,46 +414,3 @@ class TqlCommand(RemoteCommand): args2[self.options.tql_index] = "id=%s"%obj["id"] # now we can make call self._unsecure_rpccall(args2, **kwargs) class ConsoleCommand(TqlCommand): def __init__(self, cli, argv0): TqlCommand.__init__(self, cli, argv0) def start_console(self, stdin=sys.stdin, stdout=sys.stdout, stderr=sys.stderr): self.stdin = stdin.fileno() self.stdout = stdout.fileno() self.stderr = stderr.fileno() if self.stdin == sys.stdin.fileno(): sys.stdin.flush() if self.stdout == sys.stdout.fileno(): sys.stdout.flush() if self.stderr == sys.stderr.fileno(): sys.stderr.flush() self._old_settings = termios.tcgetattr(self.stdout) tty.setraw(self.stdin) self.running = True def stop_console(self): # Reset terminal attributes. termios.tcsetattr(sys.stdout, termios.TCSADRAIN, self._old_settings) self.running = False def recv(self, size): return os.read(self.stdin, 1024) def send(self, data): return os.write(self.stdout, data) def fileno(self): return self.stdin def setblocking(self, blocking): # Do NOT be in non-blocking mode : non-blocking mode # causes rshell to get EAGAIN errors when having to # output loads of data, causing the TTY to break. return def close(self): # Reset terminal attributes. termios.tcsetattr(sys.stdout, termios.TCSADRAIN, self._old_settings)
cccli/commands/console.py +123 −49 Original line number Diff line number Diff line Loading @@ -2,32 +2,71 @@ #coding=utf8 ''' Connection to a remote shell CloudControl Console related commands ''' import os import sys import tty from cccli.command import TqlCommand from cccli.exception import * from cccli.printer import Printer, color from sjrpc.core import AsyncWatcher from sjrpc.core.exceptions import * from sjrpc.core.protocols import TunnelProtocol import fcntl import os import signal import struct import sys import termios import threading import time import tty from cccli.exception import * from sjrpc.core.exceptions import * from cccli.printer import Printer, color from cccli.command import ConsoleCommand class FakeTtySocket(object): from sjrpc.core.protocols import TunnelProtocol from sjrpc.core import AsyncWatcher '''Give a tty a socket interface This object cannot handle sigwinch directly because close can be called by remote RPC and by the way, called outside the main thread and fail. ''' def __init__(self): if not os.isatty(0): raise cmdError("stdin is not tty") # flush stdin sys.stdin.flush() # save termios settings self._tc_attr = termios.tcgetattr(0) # set tty in raw mode tty.setraw(0) def recv(self, size): return os.read(0, 1024) def send(self, data): return os.write(0, data) def fileno(self): return 0 def setblocking(self, blocking): # Do NOT be in non-blocking mode : non-blocking mode # causes rshell to get EAGAIN errors when having to # output loads of data, causing the TTY to break. return def close(self): # reset tty original state termios.tcsetattr(0, termios.TCSADRAIN, self._tc_attr) class Command_console(ConsoleCommand): '''Start a remote shell on the provided host''' class Command_rshell(TqlCommand): '''Start a shell on the provided host''' def __init__(self, cli, argv0): ConsoleCommand.__init__(self, cli, argv0) #self.tql_filter = "&con" TqlCommand.__init__(self, cli, argv0) self.remove_option("--direct") self.remove_option("--quiet") self.tql_filter = "&con" def __call__(self, argv): # args parse Loading @@ -35,42 +74,77 @@ class Command_console(ConsoleCommand): if len(self.args) != 1: raise cmdBadArgument() # rpccall self.rpccall("console", self.args[0], _callback=self._cb_open_shell, _status=True, _direct=True) def _cb_open_shell(self, response): if not 'objects' in response: return response = response['objects'][0] if not 'status' in response: return if response['status'] == 'success': label = response['output'] ans = self.rpccall("rshell", self.args[0], _status=False, _direct=True) # alias first object obj = ans['objects'][0] # check response is successful if obj['status'] != 'success': raise cmdError("%s: %s" % (obj["status"], obj["message"])) # create a callbeck function for sigwinch def _cb_sig_resize(*args): '''Callback for terminal resized''' size = struct.pack('HHHH', 0, 0, 0, 0) size = fcntl.ioctl(0, termios.TIOCGWINSZ, size) size = struct.unpack('HHHH', size) try: size = 0 self.start_console(sys.stdin, sys.stdout, sys.stderr) self.rpc.call('rshell_resize', obj["output"], *size) except RpcError as e: # we dont care of failure of this self.printer.debug("Terminal resize failed: %s" % e) # Connect to remote end using sjrpc tun = self.rpc.create_tunnel(endpoint=self) # Save current terminal size size = struct.pack('HHHH', 0, 0, 0, 0) size = fcntl.ioctl(sys.stdout.fileno(), termios.TIOCGWINSZ, size) while self.running: time.sleep(1) except Exception: # Cleanup self.stop_console() tun.shutdown() tun.close() # Restore terminal size fcntl.ioctl(sys.stdout.fileno(), termios.TIOCSWINSZ, size) raise else: # Cleanup self.stop_console() tun.shutdown() tun = self.rpc.create_tunnel(obj["output"], endpoint=FakeTtySocket()) try: signal.signal(signal.SIGWINCH, _cb_sig_resize) # we need an async watcher to be able to handle sigwinch watcher = AsyncWatcher() watcher.register(self.rpc.rpc, "rshell_wait", obj["output"]) watcher.wait() finally: # restore original signal signal.signal(signal.SIGWINCH, signal.SIG_DFL) # shutdown is empty, useless #tun.shutdown() # close tunnel if sjrpc doesn't do it tun.close() # Restore terminal size fcntl.ioctl(sys.stdout.fileno(), termios.TIOCSWINSZ, size) def remote_functions(self): return set(("rshell",)) class Command_console(TqlCommand): '''Start a vm console using libvirt''' def __init__(self, cli, argv0): TqlCommand.__init__(self, cli, argv0) self.remove_option("--direct") self.remove_option("--quiet") self.tql_filter = "&r=vm&status=running" def __call__(self, argv): # args parse self.parse_args(argv) if len(self.args) != 1: raise cmdBadArgument() # rpccall ans = self.rpccall("console", self.args[0], _status=False, _direct=True) # alias first object obj = ans['objects'][0] # check everything is ok if obj['status'] != 'success': raise cmdError("%s: %s" % (obj["status"], obj["message"])) # Connect to remote end using sjrpc ev = threading.Event() def cb_on_close(tun): tun.cb_default_on_close(tun) ev.set() try: tun = self.rpc.create_tunnel(obj["output"], endpoint=FakeTtySocket(), on_close=cb_on_close) ev.wait() finally: # see tun.close in previous command tun.close() def remote_functions(self): return set(("console",))
cccli/commands/rshell.pydeleted 100644 → 0 +0 −95 Original line number Diff line number Diff line #!/usr/bin/env python #coding=utf8 ''' Connection to a remote shell ''' import os import sys import tty import fcntl import signal import struct import termios from cccli.exception import * from sjrpc.core.exceptions import * from cccli.printer import Printer, color from cccli.command import ConsoleCommand from sjrpc.core.protocols import TunnelProtocol from sjrpc.core import AsyncWatcher class Command_rshell(ConsoleCommand): '''Start a remote shell on the provided host''' def __init__(self, cli, argv0): ConsoleCommand.__init__(self, cli, argv0) self.tql_filter = "&con" def __call__(self, argv): # args parse self.parse_args(argv) if len(self.args) != 1: raise cmdBadArgument() # rpccall self.rpccall("rshell", self.args[0], _callback=self._cb_open_shell, _status=True, _direct=True) def _cb_open_shell(self, response): tun = None if not 'objects' in response: return response = response['objects'][0] if not 'status' in response: return if response['status'] == 'success': label = response['output'] try: # Create terminal self.start_console(sys.stdin, sys.stdout, sys.stderr) # Connect to remote end using sjrpc tun = self.rpc.create_tunnel(endpoint=self) # Terminal resizing event handler def sig_resize(signum, frame): size = struct.pack('HHHH', 0, 0, 0, 0) size = fcntl.ioctl(sys.stdout.fileno(), termios.TIOCGWINSZ, size) size = struct.unpack('HHHH', size) self.rpc.call('rshell_resize', label, *size) signal.signal(signal.SIGWINCH, sig_resize) # Init size for term size = struct.pack('HHHH', 0, 0, 0, 0) size = fcntl.ioctl(sys.stdout.fileno(), termios.TIOCGWINSZ, size) size = struct.unpack('HHHH', size) self.rpc.call('rshell_resize', label, *size) # Setup and run event loop watcher = AsyncWatcher() watcher.register(self.rpc.rpc, 'rshell_wait', label) while self.running: returned = watcher.wait(1) if returned: if 'return' in returned[0]: rcode = returned[0]['return'] break elif 'error' in returned[0]: raise RpcError(Exception(returned[0]['error']['message']), Exception(returned[0]['error']['message'])) # Clear signal handlers signal.signal(signal.SIGWINCH, signal.SIG_IGN) except Exception: # Cleanup self.stop_console() if tun: tun.shutdown() tun.close() raise else: # Cleanup self.stop_console() if tun: tun.shutdown() tun.close() def remote_functions(self): return set(("rshell",))