I need to be able to locally run a script that will connect to various servers and run commands on them. What is the best way to accomplish this?
Personally, I would use Capistrano. It's friendly and written in Ruby and they already did all of the heavy lifting for you.
Capistrano is a utility and framework for executing commands in parallel on multiple remote machines, via SSH.
A quickie bash 'for' loop might be easiest, perhaps something like:
for h in host1 host2 host3 do echo $h ssh user@$h "ps -ef" done
Of course, cfengine/puppet/chef/capistrano are better configuration management options. If you wanted to interactively send commands to the various shells, clusterm (http://sourceforge.net/projects/clusterm/) is a solid choice too.
I have been pretty happy with a shell script called
dssh.sh that utilizes
ssh to communicate with many machines simultaneously. It can execute the same command across lots of machines simultaneously and wait for them all to exit before returning. To download and learn more about it, the best reference I have found is the BASH Cures Cancer blog.
I was having the exact same question a year ago, and after asking some people I started with Fabric. It was recommended to me over Puppet and Chef for it's simplicity.
You can easily mix local commands and remote commands in the same function. Also, being able to use Python instead of Bash is a notable upgrade that will help if you need to do lots of helper code besides the local/remote commands. Even if sometimes you need a trick to make simple things work, I still recommend Fabric.
Here's a nice introduction to Fabric: http://www.slideshare.net/panopticdev/fabric-a-capistrano-alternative The author claims that Fabric is better than Capistrano for simple systems.
Here I'll give you a small real-life example: transferring latest git repo code (stored in code_dir) to a remote machine and compiling:
# Use: fab -H <username>@<test_computer_ip> compile_on_test_computer @task def compile_on_test_computer(): # create a remote dir for incoming updates run("mkdir -p %s/update" % env.remoteDir) # create a local dir for outgoing updates lrun("mkdir -p %s/update" % env.localDir) # archive latest code, be it committed or not with lcd("%s/code_dir" % env.localDir): lrun("uploadStash=`git stash create` && git archive -o ../update/code_outgoing.zip $uploadStash || git archive -o ../update/code_outgoing.zip HEAD") # transfer source code to test computer, unzip and compile put("%s/update/code_outgoing.zip" % (env.localDir), "%s/update/code_incoming.zip" % (env.remoteDir), mirror_local_mode=True) with cd("%s" % env.remoteDir): run("unzip -uo update/code_incoming.zip -d code_dir") run("cd code_dir && ./compile_and_install_all.sh")