|
|
"""Utilities for connecting to kernels
|
|
|
|
|
|
Authors:
|
|
|
|
|
|
* Min Ragan-Kelley
|
|
|
|
|
|
"""
|
|
|
|
|
|
#-----------------------------------------------------------------------------
|
|
|
# Copyright (C) 2011 The IPython Development Team
|
|
|
#
|
|
|
# Distributed under the terms of the BSD License. The full license is in
|
|
|
# the file COPYING, distributed as part of this software.
|
|
|
#-----------------------------------------------------------------------------
|
|
|
|
|
|
#-----------------------------------------------------------------------------
|
|
|
# Imports
|
|
|
#-----------------------------------------------------------------------------
|
|
|
|
|
|
import json
|
|
|
import sys
|
|
|
from getpass import getpass
|
|
|
from subprocess import Popen, PIPE
|
|
|
|
|
|
# external imports
|
|
|
from IPython.external.ssh import tunnel
|
|
|
|
|
|
# IPython imports
|
|
|
from IPython.utils.path import filefind
|
|
|
from IPython.utils.py3compat import str_to_bytes
|
|
|
|
|
|
|
|
|
#-----------------------------------------------------------------------------
|
|
|
# Functions
|
|
|
#-----------------------------------------------------------------------------
|
|
|
|
|
|
def get_connection_file(app=None):
|
|
|
"""Return the path to the connection file of an app
|
|
|
|
|
|
Parameters
|
|
|
----------
|
|
|
app : KernelApp instance [optional]
|
|
|
If unspecified, the currently running app will be used
|
|
|
"""
|
|
|
if app is None:
|
|
|
from IPython.zmq.kernelapp import KernelApp
|
|
|
if not KernelApp.initialized():
|
|
|
raise RuntimeError("app not specified, and not in a running Kernel")
|
|
|
|
|
|
app = KernelApp.instance()
|
|
|
return filefind(app.connection_file, ['.', app.profile_dir.security_dir])
|
|
|
|
|
|
def get_connection_info(unpack=False):
|
|
|
"""Return the connection information for the current Kernel.
|
|
|
|
|
|
Parameters
|
|
|
----------
|
|
|
unpack : bool [default: False]
|
|
|
if True, return the unpacked dict, otherwise just the string contents
|
|
|
of the file.
|
|
|
|
|
|
Returns
|
|
|
-------
|
|
|
The connection dictionary of the current kernel, as string or dict,
|
|
|
depending on `unpack`.
|
|
|
"""
|
|
|
cf = get_connection_file()
|
|
|
with open(cf) as f:
|
|
|
info = f.read()
|
|
|
|
|
|
if unpack:
|
|
|
info = json.loads(info)
|
|
|
# ensure key is bytes:
|
|
|
info['key'] = str_to_bytes(info.get('key', ''))
|
|
|
return info
|
|
|
|
|
|
def connect_qtconsole(argv=None):
|
|
|
"""Connect a qtconsole to the current kernel.
|
|
|
|
|
|
This is useful for connecting a second qtconsole to a kernel, or to a
|
|
|
local notebook.
|
|
|
|
|
|
Parameters
|
|
|
----------
|
|
|
argv : list [optional]
|
|
|
Any extra args to be passed to the console.
|
|
|
|
|
|
Returns
|
|
|
-------
|
|
|
subprocess.Popen instance running the qtconsole frontend
|
|
|
"""
|
|
|
argv = [] if argv is None else argv
|
|
|
|
|
|
# get connection file from current kernel
|
|
|
cf = get_connection_file()
|
|
|
|
|
|
cmd = ';'.join([
|
|
|
"from IPython.frontend.qt.console import qtconsoleapp",
|
|
|
"qtconsoleapp.main()"
|
|
|
])
|
|
|
|
|
|
return Popen([sys.executable, '-c', cmd, '--existing', cf] + argv, stdout=PIPE, stderr=PIPE)
|
|
|
|
|
|
def tunnel_to_kernel(connection_info, sshserver, sshkey=None):
|
|
|
"""tunnel connections to a kernel via ssh
|
|
|
|
|
|
This will open four SSH tunnels from localhost on this machine to the
|
|
|
ports associated with the kernel. They can be either direct
|
|
|
localhost-localhost tunnels, or if an intermediate server is necessary,
|
|
|
the kernel must be listening on a public IP.
|
|
|
|
|
|
Parameters
|
|
|
----------
|
|
|
connection_info : dict or str (path)
|
|
|
Either a connection dict, or the path to a JSON connection file
|
|
|
sshserver : str
|
|
|
The ssh sever to use to tunnel to the kernel. Can be a full
|
|
|
`user@server:port` string. ssh config aliases are respected.
|
|
|
sshkey : str [optional]
|
|
|
Path to file containing ssh key to use for authentication.
|
|
|
Only necessary if your ssh config does not already associate
|
|
|
a keyfile with the host.
|
|
|
|
|
|
Returns
|
|
|
-------
|
|
|
|
|
|
(shell, iopub, stdin, hb) : ints
|
|
|
The four ports on localhost that have been forwarded to the kernel.
|
|
|
"""
|
|
|
if isinstance(connection_info, basestring):
|
|
|
# it's a path, unpack it
|
|
|
with open(connection_info) as f:
|
|
|
connection_info = json.loads(f.read())
|
|
|
|
|
|
cf = connection_info
|
|
|
|
|
|
lports = tunnel.select_random_ports(4)
|
|
|
rports = cf['shell_port'], cf['iopub_port'], cf['stdin_port'], cf['hb_port']
|
|
|
|
|
|
remote_ip = cf['ip']
|
|
|
|
|
|
if tunnel.try_passwordless_ssh(sshserver, sshkey):
|
|
|
password=False
|
|
|
else:
|
|
|
password = getpass("SSH Password for %s: "%sshserver)
|
|
|
|
|
|
for lp,rp in zip(lports, rports):
|
|
|
tunnel.ssh_tunnel(lp, rp, sshserver, remote_ip, sshkey, password)
|
|
|
|
|
|
return tuple(lports)
|
|
|
|
|
|
|
|
|
|