Skip to content

Commit

Permalink
Add block_on_close argument
Browse files Browse the repository at this point in the history
  • Loading branch information
fernandezcuesta committed May 6, 2019
1 parent b2e0cc3 commit fa85471
Show file tree
Hide file tree
Showing 6 changed files with 70 additions and 19 deletions.
2 changes: 1 addition & 1 deletion LICENSE
@@ -1,4 +1,4 @@
Copyright (c) 2014-2016 Pahaz Blinov
Copyright (c) 2014-2019 Pahaz Blinov

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
Expand Down
50 changes: 42 additions & 8 deletions README.rst
Expand Up @@ -102,7 +102,7 @@ Code corresponding to **Fig1** above follows, given remote server's address is
``pahaz.urfuclub.ru``, password authentication and randomly assigned local bind
port.

.. code-block:: py
.. code-block:: python
from sshtunnel import SSHTunnelForwarder
Expand All @@ -127,12 +127,12 @@ Example of a port forwarding to a private server not directly reachable,
assuming password protected pkey authentication, remote server's SSH service is
listening on port 443 and that port is open in the firewall (**Fig2**):

.. code-block:: py
.. code-block:: python
import paramiko
from sshtunnel import SSHTunnelForwarder
import sshtunnel
with SSHTunnelForwarder(
with sshtunnel.open_tunnel(
(REMOTE_SERVER_IP, 443),
ssh_username="",
ssh_pkey="/var/ssh/rsa_key",
Expand All @@ -154,12 +154,12 @@ Example 3

Example of a port forwarding for the Vagrant MySQL local port:

.. code-block:: py
.. code-block:: python
from sshtunnel import SSHTunnelForwarder
from sshtunnel import open_tunnel
from time import sleep
with SSHTunnelForwarder(
with open_tunnel(
('localhost', 2222),
ssh_username="vagrant",
ssh_password="vagrant",
Expand All @@ -179,6 +179,40 @@ Or simply using the CLI:
(bash)$ python -m sshtunnel -U vagrant -P vagrant -L :3306 -R 127.0.0.1:3306 -p 2222 localhost
Example 4
---------

Opening an SSH session jumping over two tunnels:

.. code-block:: python
import sshtunnel
from paramiko import SSHClient
with sshtunnel.open_tunnel(
ssh_address_or_host=('GW1_ip', 20022),
remote_bind_address=('GW2_ip', 22),
block_on_close=False
) as tunnel1:
print('Connection to tunnel1 (GW1_ip:GW1_port) OK...')
with sshtunnel.open_tunnel(
ssh_address_or_host=('localhost', tunnel1.local_bind_port),
remote_bind_address=('target_ip', 22),
ssh_username='GW2_user',
ssh_password='GW2_pwd',
block_on_close=False
) as tunnel2:
print('Connection to tunnel2 (GW2_ip:GW2_port) OK...')
with SSHClient() as ssh:
ssh.connect('localhost',
port=tunnel2.local_bind_port,
username='target_user',
password='target_pwd',
)
ssh.exec_command(...)
CLI usage
=========

Expand All @@ -192,7 +226,7 @@ CLI usage
ssh_address

Pure python ssh tunnel utils
Version 0.1.4
Version 0.1.5

positional arguments:
ssh_address SSH server IP address (GW for SSH tunnels)
Expand Down
3 changes: 3 additions & 0 deletions changelog.rst
Expand Up @@ -16,6 +16,9 @@ CONTRIBUTORS
CHANGELOG
=========

- v.0.1.5 (`JM Fernández`_)
+ Introduce `block_on_close` attribute

- v.0.1.4 (`Niels Zeilemaker`_)
+ Allow loading pkeys from `~/.ssh`

Expand Down
3 changes: 3 additions & 0 deletions setup.py
Expand Up @@ -119,6 +119,9 @@ def run_tests(self):
# dependencies). You can install these using the following syntax,
# for example:
# $ pip install -e .[dev,test]
tests_require=[
'tox>=1.8.1',
],
extras_require={
'dev': ['check-manifest'],
'test': [
Expand Down
22 changes: 15 additions & 7 deletions sshtunnel.py
Expand Up @@ -36,13 +36,13 @@
input_ = input


__version__ = '0.1.4'
__version__ = '0.1.5'
__author__ = 'pahaz'


DEFAULT_LOGLEVEL = logging.ERROR #: default level if no logger passed (ERROR)
TUNNEL_TIMEOUT = 1.0 #: Timeout (seconds) for tunnel connection
DAEMON = False
_DAEMON = False #: Use daemon threads in connections
TRACE_LEVEL = 1
_CONNECTION_COUNTER = 1
_LOCK = threading.Lock()
Expand Down Expand Up @@ -415,7 +415,7 @@ class _ThreadingForwardServer(socketserver.ThreadingMixIn, _ForwardServer):
Allow concurrent connections to each tunnel
"""
# If True, cleanly stop threads created by ThreadingMixIn when quitting
daemon_threads = DAEMON
daemon_threads = _DAEMON


class _UnixStreamForwardServer(UnixStreamServer):
Expand Down Expand Up @@ -459,7 +459,7 @@ class _ThreadingUnixStreamForwardServer(socketserver.ThreadingMixIn,
Allow concurrent connections to each tunnel
"""
# If True, cleanly stop threads created by ThreadingMixIn when quitting
daemon_threads = DAEMON
daemon_threads = _DAEMON


class SSHTunnelForwarder(object):
Expand Down Expand Up @@ -614,7 +614,6 @@ class SSHTunnelForwarder(object):
host_pkey_directories (list):
Look for pkeys in folders on this list, for example ['~/.ssh'].
An empty list disables this feature
Default: ``None``
Expand Down Expand Up @@ -720,8 +719,8 @@ class SSHTunnelForwarder(object):
"""
skip_tunnel_checkup = True
daemon_forward_servers = DAEMON #: flag tunnel threads in daemon mode
daemon_transport = DAEMON #: flag SSH transport thread in daemon mode
daemon_forward_servers = _DAEMON #: flag tunnel threads in daemon mode
daemon_transport = _DAEMON #: flag SSH transport thread in daemon mode

def local_is_up(self, target):
"""
Expand Down Expand Up @@ -1580,6 +1579,12 @@ def open_tunnel(*args, **kwargs):
.. versionadded:: 0.1.0
block_on_close (boolean):
Wait until all connections are done during close by changing the
value of :attr:`~SSHTunnelForwarder.block_on_close`
Default: True
.. note::
A value of ``debug_level`` set to 1 == ``TRACE`` enables tracing mode
.. note::
Expand Down Expand Up @@ -1617,13 +1622,16 @@ def do_something(port):

ssh_port = kwargs.pop('ssh_port', None)
skip_tunnel_checkup = kwargs.pop('skip_tunnel_checkup', True)
block_on_close = kwargs.pop('block_on_close', DAEMON)
if not args:
if isinstance(ssh_address_or_host, tuple):
args = (ssh_address_or_host, )
else:
args = ((ssh_address_or_host, ssh_port), )
forwarder = SSHTunnelForwarder(*args, **kwargs)
forwarder.skip_tunnel_checkup = skip_tunnel_checkup
forwarder.daemon_forward_servers = not block_on_close
forwarder.daemon_transport = not block_on_close
return forwarder


Expand Down
9 changes: 6 additions & 3 deletions tox.ini
Expand Up @@ -19,14 +19,15 @@ basepython =
py34: python3.4
py35: python3.5
py36: python3.6
py37: python3.7
deps =
mock
pytest
pytest-cov
pytest-xdist
commands =
python -V
py.test --showlocals --cov sshtunnel --durations=10 -n4 tests
py.test --showlocals --cov sshtunnel --durations=10 -n4 tests -W ignore::DeprecationWarning

[testenv:docs]
basepython = python
Expand All @@ -47,10 +48,12 @@ deps =
mccabe
pygments
readme
twine
commands =
check-manifest --ignore "tox.ini,tests*,*.yml"
python setup.py check -m -r -s
flake8 .
python setup.py sdist
twine check dist/*
flake8 --ignore=W504 .
bashtest README.rst

[flake8]
Expand Down

0 comments on commit fa85471

Please sign in to comment.