Skip to content

Commit 2b9e725

Browse files
author
Dan
committed
Added per host configuration to parallel ssh client. Added ssh agent class at pssh.agent.SSHAgent
1 parent 0f66a26 commit 2b9e725

File tree

2 files changed

+63
-9
lines changed

2 files changed

+63
-9
lines changed

pssh/agent.py

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
class SSHAgent(paramiko.agent.AgentSSH):
2+
""":mod:`paramiko.agent.AgentSSH` compatible class for programmatically
3+
supplying an SSH agent"""
4+
5+
def __init__(self):
6+
self._conn = None
7+
self.keys = []
8+
9+
def add_key(self, key):
10+
"""Add key to agent.
11+
:param key: Key to add
12+
:type key: :mod:`paramiko.pkey.PKey`
13+
"""
14+
self.keys.append(key)
15+
16+
def _connect(self, conn):
17+
pass
18+
19+
def _close(self):
20+
self._keys = []
21+
22+
def get_keys(self):
23+
return tuple(self.keys)

pssh/pssh_client.py

Lines changed: 40 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,6 @@
3434
from .ssh_client import SSHClient
3535

3636

37-
host_logger = logging.getLogger('pssh.host_logger')
3837
logger = logging.getLogger('pssh')
3938

4039

@@ -51,7 +50,7 @@ def __init__(self, hosts,
5150
user=None, password=None, port=None, pkey=None,
5251
forward_ssh_agent=True, num_retries=DEFAULT_RETRIES, timeout=120,
5352
pool_size=10, proxy_host=None, proxy_port=22,
54-
agent=None):
53+
agent=None, host_config=None):
5554
"""
5655
:param hosts: Hosts to connect to
5756
:type hosts: list(str)
@@ -89,6 +88,12 @@ def __init__(self, hosts,
8988
:param proxy_port: (Optional) SSH port to use to login to proxy host if \
9089
set. Defaults to 22.
9190
:type proxy_port: int
91+
:param agent: (Optional) SSH agent object to programmatically supply an \
92+
agent to override system SSH agent with
93+
:type agent: :mod:`pssh.agent.SSHAgent`
94+
:param host_config: (Optional) Per-host configuration for cases where \
95+
not all hosts use the same configuration values.
96+
:type host_config: dict
9297
9398
**Example Usage**
9499
@@ -192,6 +197,18 @@ def __init__(self, hosts,
192197
>>> for cmd in ['uname', 'whoami']:
193198
... client.run_command(cmd)
194199
200+
**Per-Host configuration**
201+
202+
203+
>>> host_config = { 'host1' : {'user': 'user1', 'password': 'pass',
204+
... 'port': 2222, 'private_key': 'my_key.pem'},
205+
... 'host2' : {'user': 'user2', 'password': 'pass',
206+
... 'port': 2223, 'private_key': 'my_other_key.pem'},
207+
... }
208+
>>> hosts = host_config.keys()
209+
>>> client = ParallelSSHClient(hosts, host_config=host_config)
210+
>>> client.run_command('uname')
211+
195212
.. note ::
196213
197214
**Connection persistence**
@@ -226,6 +243,7 @@ def __init__(self, hosts,
226243
# To hold host clients
227244
self.host_clients = {}
228245
self.agent = agent
246+
self.host_config = host_config if host_config else {}
229247

230248
def run_command(self, *args, **kwargs):
231249
"""Run command on all hosts in parallel, honoring self.pool_size,
@@ -423,21 +441,29 @@ def exec_command(self, *args, **kwargs):
423441
future releases - use self.run_command instead", DeprecationWarning)
424442
return [self.pool.spawn(self._exec_command, host, *args, **kwargs)
425443
for host in self.hosts]
444+
445+
def _get_host_config_values(self, host):
446+
_user = self.host_config.get(host, {}).get('user', self.user)
447+
_port = self.host_config.get(host, {}).get('port', self.port)
448+
_password = self.host_config.get(host, {}).get('password', self.password)
449+
_pkey = self.host_config.get(host, {}).get('private_key', self.pkey)
450+
return _user, _port, _password, _pkey
426451

427452
def _exec_command(self, host, *args, **kwargs):
428453
"""Make SSHClient, run command on host"""
429454
if not host in self.host_clients or not self.host_clients[host]:
430-
self.host_clients[host] = SSHClient(host, user=self.user,
431-
password=self.password,
432-
port=self.port, pkey=self.pkey,
455+
_user, _port, _password, _pkey = self._get_host_config_values(host)
456+
self.host_clients[host] = SSHClient(host, user=_user,
457+
password=_password,
458+
port=_port, pkey=_pkey,
433459
forward_ssh_agent=self.forward_ssh_agent,
434460
num_retries=self.num_retries,
435461
timeout=self.timeout,
436462
proxy_host=self.proxy_host,
437463
proxy_port=self.proxy_port,
438464
agent=self.agent)
439465
return self.host_clients[host].exec_command(*args, **kwargs)
440-
466+
441467
def get_output(self, cmd, output):
442468
"""Get output from command.
443469
@@ -618,9 +644,14 @@ def copy_file(self, local_file, remote_file, recurse=False):
618644
def _copy_file(self, host, local_file, remote_file, recurse=False):
619645
"""Make sftp client, copy file"""
620646
if not host in self.host_clients or not self.host_clients[host]:
647+
_user, _port, _password, _pkey = self._get_host_config_values(host)
621648
self.host_clients[host] = SSHClient(
622-
host, user=self.user, password=self.password,
623-
port=self.port, pkey=self.pkey,
624-
forward_ssh_agent=self.forward_ssh_agent)
649+
host, user=_user, password=_password, port=_port, pkey=_pkey,
650+
forward_ssh_agent=self.forward_ssh_agent,
651+
num_retries=self.num_retries,
652+
timeout=self.timeout,
653+
proxy_host=self.proxy_host,
654+
proxy_port=self.proxy_port,
655+
agent=self.agent)
625656
return self.host_clients[host].copy_file(local_file, remote_file,
626657
recurse=recurse)

0 commit comments

Comments
 (0)