Skip to content

Commit cbae844

Browse files
myakovelukas-bednar
authored andcommitted
Support ssh proxy command
1 parent dca6c35 commit cbae844

File tree

4 files changed

+61
-8
lines changed

4 files changed

+61
-8
lines changed

README.rst

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,14 @@ Using SSH key with disabled algorithms on paramiko SSHClient connect (Used when
4646
4747
h.executor(user).run_cmd(['echo', 'Use pkey and disabled algorithms for old openSSH connection'])
4848
49+
Using with SSH ProxyCommand
50+
.. code:: python
51+
52+
proxy_command = 'some proxy command'
53+
h = Host(hostname="hostname")
54+
host.executor_factory = ssh.RemoteExecutorFactory(sock=proxy_command)
55+
h.executor(user).run_cmd(['echo', 'Use SSH with ProxyCommand'])
56+
4957
Features
5058
--------
5159

rrmngmnt/host.py

Lines changed: 17 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
import warnings
1111

1212
import netaddr
13+
1314
from rrmngmnt import errors
1415
from rrmngmnt import power_manager
1516
from rrmngmnt import ssh
@@ -56,15 +57,24 @@ def process(self, msg, kwargs):
5657
kwargs,
5758
)
5859

59-
def __init__(self, ip, service_provider=None):
60+
def __init__(self, ip=None, service_provider=None, hostname=None):
6061
"""
6162
Args:
6263
ip (str): IP address of machine or resolvable FQDN
6364
service_provider (Service): system service handler
65+
hostname (str): hostname of machine (Used with ProxyCommand)
6466
"""
6567
super(Host, self).__init__()
66-
if not netaddr.valid_ipv4(ip) and not netaddr.valid_ipv6(ip):
67-
ip = fqdn2ip(ip)
68+
if hostname:
69+
# When using ProxyCommand host is not IP and does not have fqdn.
70+
ip = hostname
71+
else:
72+
if not netaddr.valid_ipv4(ip) and not netaddr.valid_ipv6(ip):
73+
ip = fqdn2ip(ip)
74+
75+
if not ip:
76+
raise ValueError("ip or hostname is required")
77+
6878
self.ip = ip
6979
self.users = list()
7080
self._executor_user = None
@@ -233,7 +243,10 @@ def executor(self, user=None, pkey=False, sudo=False):
233243
ef = copy.copy(self.executor_factory)
234244
ef.use_pkey = pkey
235245
return ef.build(self, user, self.sudo)
236-
return self.executor_factory.build(self, user, sudo=self.sudo)
246+
247+
return self.executor_factory.build(
248+
self, user, sudo=self.sudo
249+
)
237250

238251
def run_command(
239252
self, command, input_=None, tcp_timeout=None, io_timeout=None,

rrmngmnt/ssh.py

Lines changed: 14 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -98,7 +98,8 @@ def open(self):
9898
timeout=self._timeout,
9999
pkey=self.pkey,
100100
port=self._executor.port,
101-
disabled_algorithms=self._executor.disabled_algorithms
101+
disabled_algorithms=self._executor.disabled_algorithms,
102+
sock=self._executor.sock,
102103
)
103104
except (socket.gaierror, socket.herror) as ex:
104105
args = list(ex.args)
@@ -222,21 +223,25 @@ def __init__(self,
222223
use_pkey=False,
223224
port=22,
224225
sudo=False,
225-
disabled_algorithms=None):
226+
disabled_algorithms=None,
227+
sock=None,
228+
):
226229
"""
227230
Args:
228231
use_pkey (bool): Use ssh private key in the connection
229232
user (instance of User): User
230233
address (str): Ip / hostname
231234
port (int): Port to connect
232235
sudo (bool): Use sudo to execute command.
236+
sock (ProxyCommand): Proxy command to use.
233237
"""
234238
super(RemoteExecutor, self).__init__(user)
235239
self.address = address
236240
self.use_pkey = use_pkey
237241
self.port = port
238242
self.sudo = sudo
239243
self.disabled_algorithms = disabled_algorithms
244+
self.sock = sock
240245
if use_pkey:
241246
warnings.warn(
242247
"Parameter 'use_pkey' is deprecated and will be removed in "
@@ -335,10 +340,13 @@ def wait_for_connectivity_state(
335340

336341

337342
class RemoteExecutorFactory(ExecutorFactory):
338-
def __init__(self, use_pkey=False, port=22, disabled_algorithms=None):
343+
def __init__(
344+
self, use_pkey=False, port=22, disabled_algorithms=None, sock=None
345+
):
339346
self.use_pkey = use_pkey
340347
self.port = port
341348
self.disabled_algorithms = disabled_algorithms
349+
self.sock = sock
342350
if use_pkey:
343351
warnings.warn(
344352
"Parameter 'use_pkey' is deprecated and will be removed in "
@@ -352,4 +360,6 @@ def build(self, host, user, sudo=False):
352360
use_pkey=self.use_pkey,
353361
port=self.port,
354362
sudo=sudo,
355-
disabled_algorithms=self.disabled_algorithms)
363+
disabled_algorithms=self.disabled_algorithms,
364+
sock=paramiko.ProxyCommand(self.sock) if self.sock else self.sock,
365+
)

tests/test_executor.py

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
from rrmngmnt import Host, User
2+
3+
from tests.common import FakeExecutorFactory
4+
5+
6+
def get_host(hostname='test-host'):
7+
return Host(hostname=hostname)
8+
9+
10+
def test_executor_with_proxy_command():
11+
data = {
12+
'which systemctl': (0, '/usr/bin/systemctl', ''),
13+
}
14+
host = get_host()
15+
sock = 'my proxy command --stdio=true test-host 22'
16+
host_user = User(name="user", password="user")
17+
host.executor_user = host_user
18+
host.executor_factory = FakeExecutorFactory(
19+
cmd_to_data=data, files_content=None
20+
)
21+
host.executor_factory.sock = sock
22+
host.executor().run_cmd(['which', 'systemctl'])

0 commit comments

Comments
 (0)