[lxc-devel] [pylxd/master] WIP: container.execute_interactive()
lingxiaoyang on Github
lxc-bot at linuxcontainers.org
Thu Jul 20 19:42:20 UTC 2017
A non-text attachment was scrubbed...
Name: not available
Type: text/x-mailbox
Size: 2339 bytes
Desc: not available
URL: <http://lists.linuxcontainers.org/pipermail/lxc-devel/attachments/20170720/a74cd6d2/attachment.bin>
-------------- next part --------------
From 315aab47df2cd7bf3bec1a4e957c324d580cc832 Mon Sep 17 00:00:00 2001
From: Ling-Xiao Yang <ling-xiao.yang at savoirfairelinux.com>
Date: Wed, 19 Jul 2017 17:55:28 -0400
Subject: [PATCH] WIP: interactive exec.
Caveats:
1. bash color... it's weird because if I `vi` a file the colors are all correct.
2. Ctrl+D... for some reason I have to type another key to let the remote close.
---
pylxd/models/container.py | 100 ++++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 100 insertions(+)
diff --git a/pylxd/models/container.py b/pylxd/models/container.py
index 415ea37..adbe2a6 100644
--- a/pylxd/models/container.py
+++ b/pylxd/models/container.py
@@ -12,12 +12,21 @@
# License for the specific language governing permissions and limitations
# under the License.
import collections
+import fcntl
+import json
+import platform
+import termios
import time
+import tty
+import signal
+import struct
+import sys
import six
from six.moves.urllib import parse
try:
from ws4py.client import WebSocketBaseClient
+ from ws4py.client.threadedclient import WebSocketClient
from ws4py.manager import WebSocketManager
_ws4py_installed = True
except ImportError: # pragma: no cover
@@ -250,6 +259,65 @@ def execute(self, commands, environment={}):
return _ContainerExecuteResult(
operation.metadata['return'], stdout.data, stderr.data)
+ def execute_interactive(self, commands, environment={}):
+ if not _ws4py_installed:
+ raise ValueError(
+ 'This feature requires the optional ws4py library.')
+ if isinstance(commands, six.string_types):
+ raise TypeError("First argument must be a list.")
+ rows, cols = _pty_size()
+ response = self.api['exec'].post(json={
+ 'command': commands,
+ 'environment': environment,
+ 'wait-for-websocket': True,
+ 'interactive': True,
+ 'width': cols,
+ 'height': rows,
+ })
+
+ fds = response.json()['metadata']['metadata']['fds']
+ operation_id = response.json()['operation'].split('/')[-1]
+ parsed = parse.urlparse(
+ self.client.api.operations[operation_id].websocket._api_endpoint)
+
+ pts = _InteractiveWebsocket(self.client.websocket_url)
+ pts.resource = '{}?secret={}'.format(parsed.path, fds['0'])
+ pts.connect()
+
+ ctl = WebSocketClient(self.client.websocket_url)
+ ctl.resource = '{}?secret={}'.format(parsed.path, fds['control'])
+ ctl.connect()
+
+ oldtty = termios.tcgetattr(sys.stdin)
+ old_handler = signal.getsignal(signal.SIGWINCH)
+
+ def on_term_resize(signum, frame):
+ rows, cols = _pty_size()
+ # Refs:
+ # https://github.com/lxc/lxd/blob/master/lxd/container_exec.go#L190
+ # https://github.com/lxc/lxd/blob/master/shared/api/container_exec.go
+ ctl.send(json.dumps({
+ 'command': 'window-resize',
+ 'args': {
+ 'width': str(cols),
+ 'height': str(rows)
+ }
+ }))
+ signal.signal(signal.SIGWINCH, on_term_resize)
+
+ try:
+ tty.setraw(sys.stdin.fileno())
+ tty.setcbreak(sys.stdin.fileno())
+ pts.run_forever()
+ except Exception as e:
+ raise
+ finally:
+ termios.tcsetattr(sys.stdin, termios.TCSADRAIN, oldtty)
+ signal.signal(signal.SIGWINCH, old_handler)
+
+ operation = self.client.operations.get(operation_id)
+ return _ContainerExecuteResult(operation.metadata['return'], '', '')
+
def migrate(self, new_client, wait=False):
"""Migrate a container.
@@ -356,6 +424,38 @@ def handshake_ok(self):
self.close()
+class _InteractiveWebsocket(WebSocketClient): # pragma: no cover
+ def received_message(self, message):
+ if message.encoding:
+ m = message.data.decode(message.encoding)
+ else:
+ m = message.data.decode('utf-8')
+ sys.stdout.write(m)
+ sys.stdout.flush()
+
+ def run_forever(self):
+ while not self.terminated:
+ if sys.stdin.isatty():
+ x = sys.stdin.buffer.read(1)
+ self.send(x, binary=True)
+ # The timeout should let cursor move fluidly in vim
+ self._th.join(timeout=0.01)
+
+
+def _pty_size():
+ """ From wssh.client._pty_size """
+ rows, cols = 24, 80
+ # Can't do much for Windows
+ if platform.system() == 'Windows':
+ return rows, cols
+ fmt = 'HH'
+ buffer = struct.pack(fmt, 0, 0)
+ result = fcntl.ioctl(sys.stdout.fileno(), termios.TIOCGWINSZ,
+ buffer)
+ rows, cols = struct.unpack(fmt, result)
+ return rows, cols
+
+
class Snapshot(model.Model):
"""A container snapshot."""
More information about the lxc-devel
mailing list