Satori/SSHModuleProposal

SSH Module Proposal

 * Implement an SSH wrapper module to enable logging on to servers so we can do data plane discovery.
 * This wiki page defines the full specification for the ssh-module blueprint.

Interface
from satori import ssh if proxy_ip: proxy = ssh.connect(proxy_ip, port=22, username=proxy_user, password=proxy_pass) else: proxy = None connection = ssh.connect("10.1.1.20", username="root", password="Password", key=None, proxy=proxy) output = connection.remote_execute("sudo echo hello", with_exit_code=True) >>> {'stdout': 'hello', 'stderr': '', 'exit_code': 0}

Requirements
Support the following logins:
 * password/username
 * private key/username
 * private key file/username
 * for all the above logins, the module should support using a proxy (a.k.a. bastion)

Primary class methods:


 * remote_execute: to execute a remote command and return stderr and stdout
 * test_connection: to test that a connection can be made

Additionally:
 * Implement an instance property, , that will return the remote host's platform info using (python >=2.4)'s platform module

Implementation

 * Use paramiko and extend its SSHClient class. Wrap paramiko to make it simpler to use and understand the code from other parts of satori.
 * Accept a password string, private key string, or path to private key file for auth. Paramiko will automatically check in the standard places for ssh keys if nothing else is provided.
 * Manage authentication mechanisms, retry authenticating, and prefer SSH keys.
 * Implement/override a  method to do this.
 * Provide a method,, for doing just that.
 * Manage connecting and disconnecting when  is called. (Lazy load the auth object)
 * Attempt to handle password prompts for non-passwordless sudoers
 * Attempt to handle hiccups with pty/tty rules by retrying the command with a pty channel if the system responds with "sudo requires a tty" or similar
 * Support ssh proxy connections, and create an implementation that provides the same behavior whether connecting through a proxy or connecting to the remote host directly.
 * Implement an instance property, , that will return the remote host's platform info using (python >=2.4)'s platform module

For the platform requirements:
 * Remote system requires python>=2.4
 * architecture, distro, version
 * e.g. Ubuntu 12.04 x86_64 would return

# function signature def connect(host, credentials, port=22, timeout=None, proxy=None): """Connect to remote host over SSH and return a client connection.     credentials can have:          username (required)          password          private_key          private_key_file      proxy can have:          host (required if proxy supplied)          username (required if proxy supplied)          port          timeout          password          private_key          private_key_file      """

Notes:

We considered the following to stay within pylint's max 5 argument rule, but we decided to break the rule for practicality...

# Simplest use case from satori import ssh connection = ssh.connect("10.1.1.20", username="root", password="Password", key=None) output = connection.remote_execute("sudo echo hello", with_exit_code=True, timeout=None) >>> {'stdout': 'hello', 'stderr': '', 'exit_code': 0}

# More options from satori import ssh client = ssh.Client("10.1.1.20", port=2222, timeout=1000, StrictHostKeyChecking=True) client.set_credentials(username="root", password="Password") if proxy_ip: proxy = ssh.Client(proxy_ip, port=22) proxy.set_credentials(username=proxy_user, password=proxy_pass) else: proxy = None connection = client.connect(proxy=proxy) output = connection.remote_execute("sudo echo hello", with_exit_code=True, timeout=None) >>> {'stdout': 'hello', 'stderr': '', 'exit_code': 0}