When testing new stuff for Booko, I sometimes create a new slicehost and build myself a test box. In the past I've done this manually, which really isn't very difficult, but it turns out that  Slicehost have an API to allow you to automate this stuff.  With the API you can create, destroy, rebuild and reboot your VPS which is pretty cool.  It also lets you manipulate all your DNS settings. Check out the  API for complete details. 

Here's a script I put together to build a new dev host for me and set it up so it's ready to use.  It performs the following steps:

  1. Check the Domain name I've selected for the host is managed by Slicehost.
  2. Create a new 256MB VPS with Ubuntu 8.04 installed.
  3. Create the domain names for the host (including on the internal interface if required)
  4. Wait for host to build and for Networking and SSH to startup.
  5. Add my SSH key to the root user's account for passwordless login.
  6. Update and upgrade the host.
  7. Install Puppet  

I plan on having a puppet server setup soon which will take over the rest of the setup, so I've installed that. I'll probably use the Net::SSH stuff to get the host added to puppet (signing certificates and such.)

#!/usr/bin/env ruby

require 'rubygems'  
require 'activeresource'  
require 'net/ssh'  
require 'net/scp'



fqdn = host\_name + "." + domain\_name  
origin = domain\_name + "."

# Required definitions to access the Slicehost stuff  
class Slice self.site = SITE  

# Address class is required for Slice class  
class Address

class Zone self.site = SITE  

class Record self.site = SITE  

# Create or update a DNS record  
def create\_host\_record(zone\_id, host\_name, ip\_address, ttl = DEFAULT\_TTL)  
host\_record = Record.find(:first, :params => { :name => host\_name, :zone\_id => zone\_id } )  
unless host\_record.nil?  
host\_record.data = ip\_address  
host\_record.record\_type = "A"  
host\_record = Record.new(:ttl => ttl, :record\_type => 'A', :zone\_id => zone\_id, :name => host\_name, :data => ip\_address)  

puts "Getting Zone data for \"#{domain\_name}\"."  
dom = Zone.find(:first, :params => { :origin => origin } )

raise "Domain \"#{domain\_name}\" not found. Won't be able to create host record." if dom.nil?

puts "Domain exists. Creating slice."

slice = Slice.new(:image\_id => 10, :flavor\_id => 1, :name => host\_name)  

puts "Slice created. Creating DNS records while it builds."

create\_host\_record(dom.id, host\_name, slice.addresses[0])  
create\_host\_record(dom.id, int\_host\_name, slice.addresses[1]) unless int\_host\_name.nil?

puts "DNS created. Waiting for host to build and become active."

while slice.progress != 100 && slice.status != "active"  
puts "Host is #{slice.progress}% complete - host status: #{slice.status}"  
sleep 10  

puts "Host built. Waiting for host to startup."

Net::SSH.start(slice.ip\_address, 'root', {:auth\_methods => ["password"], :password => slice.root\_password}) do |ssh|  
puts "Connected to new host. Bootstrapping."

puts "Creating .ssh directory and uploading public key."  
ssh.exec "/bin/mkdir -p /root/.ssh/"  
ssh.scp.upload!("/Users/dkam/.ssh/id\_dsa\_omena.pub", "/root/.ssh/authorized\_keys")  
puts "Done!"

puts "Updating apt and upgrading system."  
ssh.exec!("/usr/bin/aptitude update")  
ssh.exec!("/usr/bin/aptitude dist-upgrade -y")  
puts "Host has been upgraded and updated."

 puts "Installing puppet."  
ssh.exec!("/usr/bin/aptitude install puppet -y")  
puts "Puppet Installed."  
rescue Errno::ENETUNREACH  
puts "Host network not up. Waiting 10 seconds, then retrying"  
sleep 10  
rescue Errno::ECONNREFUSED  
puts "SSH not ready. Waiting 10 seconds, then retrying"  
sleep 10  

puts "Done! Enjoy your new host #{fqdn}. You can now ssh root@#{slice.ip\_address}"