Skip to content

Commit

Permalink
You can send command over telnet to docker
Browse files Browse the repository at this point in the history
  • Loading branch information
julien-duponchelle committed Dec 15, 2015
1 parent 5071c74 commit b57428c
Show file tree
Hide file tree
Showing 4 changed files with 66 additions and 12 deletions.
20 changes: 19 additions & 1 deletion gns3server/modules/docker/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,8 @@ def http_query(self, method, path, data={}, params={}):
data = json.dumps(data)
url = "http://docker/" + path
response = yield from aiohttp.request(
method, url,
method,
url,
connector=self._connector,
params=params,
data=data,
Expand All @@ -90,6 +91,23 @@ def http_query(self, method, path, data={}, params={}):
raise DockerError("Docker has returned an error: {}".format(body))
return response

@asyncio.coroutine
def websocket_query(self, path, params={}):
"""
Open a websocket connection
:param path: Endpoint in API
:param params: Parameters added as a query arg
:returns: Websocket
"""

url = "http://docker/" + path
connection = yield from aiohttp.ws_connect(url,
connector=self._connector,
origin="http://docker",
autoping=True)
return connection

@asyncio.coroutine
def list_images(self):
"""Gets Docker image list.
Expand Down
42 changes: 38 additions & 4 deletions gns3server/modules/docker/docker_vm.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,8 @@
import shutil
import psutil
import shlex
import aiohttp


from ...ubridge.hypervisor import Hypervisor
from .docker_error import DockerError
Expand Down Expand Up @@ -141,14 +143,46 @@ def start(self):
@asyncio.coroutine
def _start_console(self):
"""
Stream the console via telnet
Start streaming the console via telnet
"""
stream = yield from self.manager.http_query("POST", "containers/{}/attach".format(self._cid), params={"stream": 1, "logs": 1, "stdout": 1, "stderr": 1})
class InputStream:
def __init__(self):
self._data = b""

def write(self, data):
self._data += data

@asyncio.coroutine
def drain(self):
self.ws.send_bytes(self._data)
self._data = b""

telnet = AsyncioTelnetServer(reader=stream.content)
output_stream = asyncio.StreamReader()
input_stream = InputStream()

telnet = AsyncioTelnetServer(reader=output_stream, writer=input_stream)
yield from asyncio.start_server(telnet.run, self._manager.port_manager.console_host, self._console)

ws = yield from self.manager.websocket_query("containers/{}/attach/ws?stream=1&stdin=1&stdout=1&stderr=1".format(self._cid))
input_stream.ws = ws

asyncio.async(self._read_console_output(ws, output_stream))
# TODO: kill telnet server when exit

@asyncio.coroutine
def _read_console_output(self, ws, out):
"""
Read websocket and forward it to the telnet
:params ws: Websocket connection
:param out: Output stream
"""
while True:
msg = yield from ws.receive()
if msg.tp == aiohttp.MsgType.text:
out.feed_data(msg.data.encode())
else:
break

def is_running(self):
"""Checks if the container is running.
Expand Down Expand Up @@ -371,4 +405,4 @@ def adapters(self, adapters):
@asyncio.coroutine
def _get_namespace(self):
result = yield from self.manager.query("GET", "containers/{}/json".format(self._cid))
return int(result['State']['Pid']) #+ 1 #  Namespace 0 doesn't work
return int(result['State']['Pid'])
12 changes: 8 additions & 4 deletions gns3server/utils/asyncio/telnet_server.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,8 +30,10 @@ def __init__(self, reader=None, writer=None):

@asyncio.coroutine
def run(self, network_reader, network_writer):
network_read = asyncio.async(network_reader.readline())
reader_read = asyncio.async(self._reader.read(1))
READ_SIZE = 100

network_read = asyncio.async(network_reader.read(READ_SIZE))
reader_read = asyncio.async(self._reader.read(READ_SIZE))

while True:
done, pending = yield from asyncio.wait(
Expand All @@ -43,12 +45,14 @@ def run(self, network_reader, network_writer):
for coro in done:
data = coro.result()
if coro == network_read:
network_read = asyncio.async(network_reader.readline())
network_read = asyncio.async(network_reader.read(READ_SIZE))
if self._writer:
# Strip that so we don't get a double prompt.
data = data.replace(b"\r\n", b"\n")
self._writer.write(data)
yield from self._writer.drain()
elif coro == reader_read:
reader_read = asyncio.async(self._reader.readline())
reader_read = asyncio.async(self._reader.read(READ_SIZE))
network_writer.write(data)
yield from network_writer.drain()
# TODO: manage EOF
Expand Down
4 changes: 1 addition & 3 deletions requirements.txt
Original file line number Diff line number Diff line change
@@ -1,6 +1,4 @@
jsonschema>=2.4.0
aiohttp==0.17.4
aiohttp==0.19.0
Jinja2>=2.7.3
raven>=5.2.0
docker-py==1.4.0
psutil>=3.0.0

0 comments on commit b57428c

Please sign in to comment.