Remote control of hosts over SSH¶
Customize (hosts etc) all examples to match student VM setup
It is possible to control a local ssh session using subprocess.Popen() if no libraries are available. This is a super primative way to do things, and not recommended if you can avoid it.
Here is an example: 
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
import subprocess import sys HOST="www.example.org" # Ports are handled in ~/.ssh/config since we use OpenSSH COMMAND="uname -a" ssh = subprocess.Popen(["ssh", "%s" % HOST, COMMAND], shell=False, stdout=subprocess.PIPE, stderr=subprocess.PIPE) result = ssh.stdout.readlines() if result == : error = ssh.stderr.readlines() print >>sys.stderr, "ERROR: %s" % error else: print result
Fabric is a library and command-line tool for streamlining the use of SSH for application deployment or systems administration tasks.
It provides a basic suite of operations for executing local or remote shell commands (normally or via sudo) and uploading/downloading files, as well as auxiliary functionality such as prompting the running user for input, or aborting execution.
Typical use involves creating a Python file named fabfile.py, containing one or more functions, then executing them via the fab command-line tool. Below is a small but complete fabfile.py containing a single task:
from fabric.api import run def host_type(): run('uname -s')
Once a task is defined, it may be run on one or more servers, like so:
(sysadmin)$ fab -H applebox,linuxbox host_type [applebox] run: uname -s [applebox] out: Darwin [linuxbox] run: uname -s [linuxbox] out: Linux Done. Disconnecting from localhost... done. Disconnecting from linuxbox... done.
It’s often useful to pass runtime parameters into your tasks, just as you might during regular Python programming. Fabric has basic support for this using a shell-compatible notation: <task name>:<arg>,<kwarg>=<value>,.... It’s contrived, but let’s extend the above example to say hello to you personally: 
def hello(name="world"): print("Hello %s!" % name)
By default, calling fab hello will still behave as it did before; but now we can personalize it:
(sysadmin)$ fab hello:name=Jeff Hello Jeff! Done.
Those already used to programming in Python might have guessed that this invocation behaves exactly the same way:
(sysadmin)$ fab hello:Jeff Hello Jeff! Done.
For the time being, your argument values will always show up in Python as strings and may require a bit of string manipulation for complex types such as lists. Future versions may add a typecasting system to make this easier.
In addition to use via the fab tool, Fabric’s components may be imported into other Python code, providing a Pythonic interface to the SSH protocol suite at a higher level than that provided by e.g. the ssh library (which Fabric itself uses.) 
Consider the case where we want to collect average uptime from a list of hosts:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26
from fabric import tasks env.hosts = ['localhost', 'sunflower.heliotropic.us'] pattern = re.compile(r'up (\d+) days') # No need to decorate this function with @task def uptime(): res = run('uptime') match = pattern.search(res) if match: days = int(match.group(1)) env['uts'].append(days) def main(): env['uts'] =  tasks.execute(uptime) uts_list = env['uts'] if not uts_list: return # Perhaps we should print a notice here? avg = sum(uts_list) / float(len(uts_list)) print '-' * 80 print 'Average uptime: %s days' % avg print '-' * 80 if __name__ == '__main__': main()