|
| 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) |
0 commit comments