Skip to content

Commit da2eaca

Browse files
author
Dan
committed
Updated documentation. Added advanced usage documentation
1 parent 32997bf commit da2eaca

File tree

5 files changed

+164
-9
lines changed

5 files changed

+164
-9
lines changed

doc/advanced.rst

Lines changed: 151 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,151 @@
1+
.. contents::
2+
3+
4+
***************
5+
Advanced Usage
6+
***************
7+
8+
There are several more advanced use cases of `ParallelSSH`, such as tunneling (aka proxying) via an intermediate SSH server and per-host configuration and command substitution among others.
9+
10+
SSH Agent forwarding
11+
*********************
12+
13+
SSH agent forwarding, what ``ssh -A`` does on the command line, is supported and enabled by default. Creating an object as ``ParallelSSHClient(hosts, forward_ssh_agent=False)`` will disable this behaviour.
14+
15+
Programmatic Private Keys
16+
**************************
17+
18+
By default, `ParallelSSH` will use all keys in an available SSH agent and all available keys under the user's SSH directory (`~/.ssh`).
19+
20+
A private key can also be provided programmatically.
21+
22+
.. code-block:: python
23+
24+
from pssh.utils import load_private_key
25+
from pssh import ParallelSSHClient
26+
27+
client = ParallelSSHClient(hosts, pkey=load_private_key('my_key'))
28+
29+
Where `my_key` is a private key file in current working directory.
30+
31+
The helper function :py:func:`pssh.utils.load_private_key` will attempt to load all available key types and raises :mod:`pssh.exceptions.SSHException` if it cannot load the key file.
32+
33+
Using an available SSH agent can also be disabled programmatically.
34+
35+
.. code-block:: python
36+
37+
client = ParallelSSHClient(hosts, pkey=load_private_key('my_key'),
38+
allow_agent=False)
39+
40+
Programmatic SSH Agent
41+
***********************
42+
43+
It is also possible to programmatically provide an SSH agent for the client to use, instead of a system provided one. This is useful in cases where different hosts in the host list need different private keys and a system SSH agent is not available.
44+
45+
.. code-block:: python
46+
47+
from pssh.agent import SSHAgent
48+
from pssh.utils import load_private_key
49+
from pssh import ParallelSSHClient
50+
51+
agent = SSHAgent()
52+
agent.add_key(load_private_key('my_private_key_filename'))
53+
agent.add_key(load_private_key('my_other_private_key_filename'))
54+
hosts = ['my_host', 'my_other_host']
55+
56+
client = ParallelSSHClient(hosts, agent=agent)
57+
client.run_command(<..>)
58+
59+
Supplying an agent object programmatically implies that a system SSH agent will *not* be used if available.
60+
61+
62+
Tunneling
63+
**********
64+
65+
This is used in cases where the client does not have direct access to the target host and has to authenticate via an intermediary, also called a bastion host, commonly used for additional security as only the bastion host needs to have access to the target host.
66+
67+
ParallelSSHClient ------> SSH Proxy server --------> SSH target host
68+
69+
Proxy server can be configured as follows in the simplest case::
70+
71+
hosts = [<..>]
72+
client = ParallelSSHClient(hosts, proxy_host=bastion)
73+
74+
Configuration for the proxy host's user name, port, password and private key can also be provided, separete from target host user name.
75+
76+
.. code-block:: python
77+
78+
from pssh.utils import load_private_key
79+
80+
hosts = [<..>]
81+
client = ParallelSSHClient(hosts, user='target_host_user',
82+
proxy_host=bastion, proxy_user='my_proxy_user',
83+
proxy_port=2222,
84+
proxy_pkey=load_private_key('proxy.key'))
85+
86+
Where `proxy.key` is a filename containing private key to use for proxy host authentication.
87+
88+
Per-Host Configuration
89+
***********************
90+
91+
Sometimes, different hosts require different configuration like user names and passwords, ports and private keys. Capability is provided to supply per host configuration for such cases.
92+
93+
.. code-block:: python
94+
95+
from pssh.utils import load_private_key
96+
97+
host_config = {'host1' : {'user': 'user1', 'password': 'pass',
98+
'port': 2222,
99+
'private_key': load_private_key(
100+
'my_key.pem')},
101+
'host2' : {'user': 'user2', 'password': 'pass',
102+
'port': 2223,
103+
'private_key': load_private_key(
104+
open('my_other_key.pem'))},
105+
}
106+
hosts = host_config.keys()
107+
108+
client = ParallelSSHClient(hosts, host_config=host_config)
109+
client.run_command('uname')
110+
<..>
111+
112+
In the above example, `host1` will use user name `user1` and private key from `my_key.pem` and `host2` will use user name `user2` and private key from `my_other_key.pem`.
113+
114+
Per-Host Command substitution
115+
******************************
116+
117+
For cases where different commands should be run each host, or the same command with different arguments, functionality exists to provide per-host command arguments for substitution.
118+
119+
The `host_args` keyword parameter to `run_command` can be used to provide arguments to use to format the command string.
120+
121+
Number of `host_args` items should be at least as many as number of hosts.
122+
123+
Any Python string format specification characters may be used in command string.
124+
125+
126+
In the following example, first host in hosts list will use cmd `host1_cmd` second host `host2_cmd` and so on
127+
128+
.. code-block:: python
129+
130+
output = client.run_command('%s', host_args=('host1_cmd',
131+
'host2_cmd',
132+
'host3_cmd',))
133+
134+
Command can also have multiple arguments to be substituted.
135+
136+
.. code-block:: python
137+
138+
output = client.run_command('%s %s',
139+
host_args=(('host1_cmd1', 'host1_cmd2'),
140+
('host2_cmd1', 'host2_cmd2'),
141+
('host3_cmd1', 'host3_cmd2'),))
142+
143+
A list of dictionaries can also be used as `host_args` for named argument substitution.
144+
145+
In the following example, first host in host list will use cmd `host-index-0`, second host `host-index-1` and so on.
146+
147+
.. code-block:: python
148+
149+
host_args=[{'cmd': 'host-index-%s' % (i,))
150+
for i in range(len(client.hosts))]
151+
output = client.run_command('%(cmd)s', host_args=host_args)

doc/front_page.rst

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,3 +28,4 @@ User Guide
2828
introduction
2929
installation
3030
quickstart
31+
advanced

doc/index.rst

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,6 @@ Parallel-SSH's documentation
77
============================
88

99
.. toctree::
10-
:hidden:
1110

1211
front_page
1312

doc/quickstart.rst

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
.. contents::
2+
13
***********
24
Quickstart
35
***********
@@ -11,7 +13,7 @@ The most basic usage of `parallel-ssh` is, unsurprisingly, to run a command on m
1113

1214
Examples here will be using `print` as a function, for which a future import is needed for Python `2.7`.
1315

14-
Make a list of the hosts to run on::
16+
Make a list or other iterable of the hosts to run on::
1517

1618
from __future__ import print_function
1719

@@ -29,9 +31,9 @@ Now one or more commands can be run via the client::
2931

3032
output = client.run_command('whoami')
3133

32-
At this point the remote command has started executing in parallel.
34+
Once the call to `run_command` returns, the command has started executing in parallel.
3335

34-
Output is keyed by host and contains a host output object. From that, SSH output is available
36+
Output is keyed by host and contains a host output object. From that, SSH output is available.
3537

3638
Authentication
3739
----------------
@@ -96,7 +98,7 @@ Of course, iterating over all hosts can also be done the same way::
9698
Exit codes
9799
-------------
98100

99-
Exit codes are available on the host output object.
101+
Exit codes are available on the host output object - see :pyclass:`pssh.output.HostOutput`.
100102

101103
First, ensure that all commands have finished and exit codes gathered by joining on the output object, then iterate over all hosts::
102104

@@ -107,11 +109,13 @@ First, ensure that all commands have finished and exit codes gathered by joining
107109
Host Logger
108110
------------
109111

110-
There is a built in host logger that can be enabled to log output from remote hosts. The helper function ``pssh.utils.enable_host_logger`` will enable host logging to standard output, for example ::
112+
There is a built in host logger that can be enabled to automatically log output from remote hosts. This requires the `consume_output` flag to be enabled on `run_command`.
113+
114+
The helper function ``pssh.utils.enable_host_logger`` will enable host logging to standard output, for example ::
111115

112116
from pssh.utils import enable_host_logger
113117
enable_host_logger()
114-
client.join(client.run_command('uname'))
118+
client.join(client.run_command('uname'), consume_output=True)
115119
116120
[localhost] Linux
117121

pssh/pssh_client.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -538,11 +538,11 @@ def run_command(self, command, sudo=False, user=None, stop_on_errors=True,
538538
539539
# List of dict
540540
#
541-
# Fist host in host list will use cmd 'host-index-0',
541+
# First host in host list will use cmd 'host-index-0',
542542
# second host 'host-index-1' and so on
543543
output = client.run_command(
544544
'%(cmd)s', host_args=[{'cmd': 'host-index-%s' % (i,))
545-
for i in range(len(client.hosts))]
545+
for i in range(len(client.hosts))])
546546
547547
:Expression as host list:
548548

0 commit comments

Comments
 (0)