A lightweight Python library for managing and executing commands across multiple remote servers over SSH in parallel.
SSHManager provides a simple and intuitive interface for automating tasks on one or many remote machines simultaneously. It's designed to be easy to integrate into any project, offering abstractions for single hosts, clusters of hosts, and even groups of clusters.
- 🚀 Parallel Execution: Run commands and file operations on multiple hosts at once.
- 🎛️ Cluster Abstraction: Group hosts into a
SSHClusterto manage them as a single unit. - 🛰️ Single-Host Control: Operate on individual servers with the
SSHConnectionclass when needed. - 💻 Remote Command Execution: Execute any shell command and get structured results per host.
- 📁 File & Directory Transfers: Recursively upload/download files and entire directories (
put,get,put_dir,get_dir). - 🔑 Flexible Authentication: Supports both password and SSH private key authentication.
- 📝 Strongly Typed: Built with
dataclassmodels for clear and predictable interfaces. - 🪵 Simple Logging: Uses the standard Python
loggingmodule, which is easily configurable by the user.
SSHManager relies on the paramiko library as its SSH backend.
-
Install the dependency:
pip install paramiko
-
Add the library to your project: This library is not on PyPI. To use it, simply copy the
sshmanagerdirectory into your project's source tree.
- HostInfo: A
dataclassthat holds all the connection details for a single host (hostname, port, username, credentials, etc.). - Result: A
dataclassthat captures the outcome of an operation (command execution or file transfer), including stdout, stderr, exit code, and success status. - SSHConnection: The low-level class for managing a connection and operations on a single host.
- SSHCluster: The primary interface for parallel operations. It manages a pool of
SSHConnectionobjects for a list of hosts. - ClusterManager: An optional high-level utility to manage multiple named clusters for more complex workflows.
Here are some common examples to get you started.
First, define your target machines using HostInfo. You can mix and match password and key-based authentication.
from sshmanager.cluster import SSHCluster
from sshmanager.types import HostInfo
# Define hosts with different authentication methods
hosts = [
HostInfo(hostname="1.2.3.4", username="root", password="your_secure_password"),
HostInfo(hostname="server.example.com", username="admin", key_filename="~/.ssh/id_rsa"),
HostInfo(hostname="1.2.3.6", username="user", port=2222, password="another_password"),
]
# Create a cluster instance
cluster = SSHCluster(hosts)Execute a command across all hosts in the cluster. The results are returned as a dictionary mapping each host's IP/hostname to its Result object.
# Run a command on all hosts
results = cluster.run("uname -a")
# Process the results
print("--- System Information ---")
for host, res in results.items():
if res.success:
print(f"✅ {host}: {res.stdout.strip()}")
else:
print(f"❌ {host}: Failed with error: {res.stderr.strip()}")Transfer files to and from all remote machines.
# Upload a single local file to a remote destination on all hosts
cluster.put("local_script.sh", "/usr/local/bin/run.sh")
# Download a single file from all hosts to a local directory
# The filename will be prefixed with the host's IP to avoid collisions
cluster.get("/var/log/app.log", "logs/")You can also transfer entire directory trees.
# Upload a local directory to a remote location
cluster.put_dir("local_configs/", "/etc/app_configs/")
# Download a remote directory from all hosts
cluster.get_dir("/var/www/html", "backup/www/")For tasks that don't require parallelism, you can work with a single host.
from sshmanager.connection import SSHConnection
from sshmanager.types import HostInfo
host_info = HostInfo(hostname="1.2.3.4", username="root", password="your_secure_password")
with SSHConnection(host_info) as conn:
res = conn.exec("hostname && uptime")
if res.success:
print(res.stdout)Defines the parameters for a connection.
hostname(str): The server hostname or IP address.port(int, optional): The SSH port. Defaults to22.username(str, optional): The user to connect as. Defaults to the current user.password(str, optional): The user's password for authentication.key_filename(str, optional): Path to the private SSH key for authentication.label(str, optional): A custom name or label for the host.
Captures the result of an operation on a single host.
success(bool):Trueif the operation completed successfully,Falseotherwise.stdout(str): The standard output from a command.stderr(str): The standard error from a command.exit_code(int): The exit code of the command (e.g.,0for success).elapsed(float): The time taken for the operation in seconds.error(Exception): The exception object if one was raised during the operation.
The library uses the root logger named "sshmanager". You can easily configure its verbosity and output destination using Python's standard logging module.
For example, to enable detailed DEBUG level logging:
import logging
# Configure logging to show all debug messages from the library
logging.basicConfig(
level=logging.DEBUG,
format='%(asctime)s - %(name)s - %(levelname)s - %(message)s'
)
# Your SSHManager code here...To see only informational messages and errors, use logging.INFO.
Contributions are welcome! If you find a bug or have a feature request, please open an issue. If you'd like to contribute code, please open a pull request.