Created
November 3, 2008 06:24
-
-
Save code/21814 to your computer and use it in GitHub Desktop.
Quick and dirty api for dreamhost uisng mechanize. Add / remove domains, users, and databases.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# File: dreamhost_adapter.rb | |
# Author: Luke Hubbard | |
# Gist: http://gist.github.com/gists/21814 | |
# License: MIT | |
require 'rubygems' | |
require 'mechanize' | |
require 'logger' | |
# Need to monkey patch the CookieJar so it accepts SSL cookies! | |
module WWW | |
class Mechanize | |
class CookieJar | |
def add(uri, cookie) | |
# Move this line up, added split | |
normal_domain = cookie.domain.downcase.split(":").first | |
return unless uri.host =~ /#{normal_domain}$/i | |
unless @jar.has_key?(normal_domain) | |
@jar[normal_domain] = Hash.new | |
end | |
@jar[normal_domain][cookie.name] = cookie | |
cleanup() | |
cookie | |
end | |
end | |
end | |
end | |
# Ugly hack stop random SSL errors | |
module WWW | |
class Mechanize | |
class Chain | |
class SSLResolver | |
def handle(ctx, params) | |
uri = params[:uri] | |
http_obj = params[:connection] | |
if uri.scheme == 'https' && ! http_obj.started? and !http_obj.use_ssl # Added last bit | |
http_obj.use_ssl = true | |
http_obj.verify_mode = OpenSSL::SSL::VERIFY_NONE | |
if @ca_file | |
http_obj.ca_file = @ca_file | |
http_obj.verify_mode = OpenSSL::SSL::VERIFY_PEER | |
http_obj.verify_callback = @verify_callback if @verify_callback | |
end | |
if @cert && @key | |
http_obj.cert = OpenSSL::X509::Certificate.new(::File.read(@cert)) | |
http_obj.key = OpenSSL::PKey::RSA.new(::File.read(@key), @pass) | |
end | |
end | |
super | |
end | |
end | |
end | |
end | |
end | |
class DreamhostAdapter < WWW::Mechanize | |
PANEL_URL = 'https://panel.dreamhost.com:443/index.cgi' | |
LOGIN_URL = PANEL_URL | |
LOGOUT_URL = PANEL_URL + '?Nscmd=Nlogout' | |
ADD_DOMAIN_URL = PANEL_URL + '?tree=domain.manage¤t_step=Index&next_step=ShowAddhttp&domain=' | |
DEL_DOMAIN_URL = PANEL_URL + '?tree=domain.manage¤t_step=Index&next_step=ShowDestroyDomain&domain=' | |
ADD_USER_URL = PANEL_URL + '?tab=users&subtab=users¤t_step=Index&next_step=Add' | |
LIST_USER_URL = PANEL_URL + '?tree=users.users' | |
MYSQL_DB_URL = PANEL_URL + '?tree=goodies.mysql&' | |
attr_accessor :email, :password, :logged_in | |
def initialize(email, password) | |
super() | |
#self.cookie_jar = WWW::Mechanize::CookieJar.new | |
self.email = email | |
self.password = password | |
self.user_agent_alias = 'Mac Mozilla' | |
self.log = Logger.new(STDOUT, :level=>:debug) | |
login or raise "Login failed" unless self.logged_in | |
end | |
# :domain=>"blah.codegent.org" | |
# :user=>"codegent" | |
# :relpath=>"www/apps/blah/current/public" | |
# :mod_security_on=>true | |
# :passenger=>true | |
# :fastcgi=>false | |
# :www_redir=>"both" "www" "notwww" | |
# :google_apps=>false | |
# :gmail=>:false | |
def add_domain!(options) | |
options_required!(options, :domain, :relpath) | |
get(ADD_DOMAIN_URL) | |
form = page.forms.first | |
form.domain = options[:domain] | |
form.relpath = options[:relpath] | |
unless options[:user].nil? | |
user_options = form.fields.name('usid').options | |
# look for a matching user, note we dont require the (on server) part. | |
option = user_options.detect{|it| it.text =~ /^#{options[:user]} \(on [a-z0-9]+\)$/i } | |
if option.nil? | |
user_options.last.select | |
form.newuser_username = options[:user] | |
else | |
option.select | |
end | |
end | |
populate_checkboxes_from_options(form.checkboxes, options) | |
populate_radiobuttons_from_options(form.radiobuttons, options) | |
submit(form, form.buttons.first) | |
check_for_success | |
end | |
def delete_domain!(options) | |
options_required!(options, :domain) | |
get(DEL_DOMAIN_URL+options[:domain]) | |
form = page.forms.first | |
form.checkboxes.name('confirm').check | |
submit(form) | |
check_for_success | |
end | |
# :type=>'shell' | |
# :shell=>'bash' | |
# :user=>'slice201' | |
# :lockdown_homedir=>true | |
# :password=>"icanhazslice" | |
# :disable_ftp=>true | |
# :sa_chk=>true enable cpu check | |
# :notify_disk=>true | |
# :quote_chk=>true limit disk | |
# :hard_quota=>"1000" | |
def add_user!(options) | |
options_required!(options, :type, :user, :password) | |
options[:gecos] ||= " " | |
get(ADD_USER_URL) | |
form = page.forms.first | |
form.user = options[:user] | |
form.pw1 = options[:password] | |
form.pw2 = options[:password] | |
form.gecos = options[:gecos] | |
unless options[:shell].nil? | |
shell_options = form.fields.name('shell').options | |
# look for a matching shell, note we dont require path prefix. | |
option = shell_options.detect{|it| it.text =~ /\/#{options[:shell]}/i } | |
option.select unless option.nil? | |
end | |
populate_checkboxes_from_options(form.checkboxes, options) | |
populate_radiobuttons_from_options(form.radiobuttons, options) | |
submit(form) | |
check_for_success | |
end | |
def delete_user!(options) | |
options_required!(options, :user) | |
get(LIST_USER_URL) | |
link = page.search('td a').select{|it| it.inner_text.strip =~ /Delete/ }.reject{|it| it[:href] =~ /withDomain/ }.detect{|it| it[:href] =~ /user=([a-z0-9_-]+)\&/ and $1.eql?(options[:user]) } | |
return false unless link | |
click link | |
form = page.forms.first | |
form.checkboxes.name('confirm').check | |
submit(form) | |
check_for_success | |
end | |
def add_mysql!(options) | |
# Needs to use new user for each db, and default hostname | |
options_required!(options, :dbname, :user, :password) | |
get(MYSQL_DB_URL) | |
form = page.forms.last | |
form.fields.name('old_username').options.last.select | |
form.dbname = options[:dbname] | |
form.username = options[:user] | |
form.pw1 = options[:password] | |
form.pw2 = options[:password] | |
submit(form) | |
check_for_success | |
end | |
def add_mysql!(options) | |
# Needs to use new user for each db, and default hostname | |
options_required!(options, :dbname, :user, :password) | |
get(MYSQL_DB_URL) | |
form = page.forms.last | |
user_options = form.fields.name('old_username').options | |
option = user_options.detect{|it| it.value.eql?(options[:user]) } | |
if option.nil? | |
user_options.last.select | |
form.username = options[:user] | |
else | |
option.select | |
end | |
form.dbname = options[:dbname] | |
form.pw1 = options[:password] | |
form.pw2 = options[:password] | |
#raise form.inspect | |
submit(form) | |
check_for_success | |
end | |
def options_required!(options, *required) | |
required.each { |option| raise "Missing required option: #{option}" unless options.has_key? option } | |
end | |
def populate_checkboxes_from_options(checkboxes, options) | |
checkboxes.each do |checkbox| | |
checked = options[checkbox.name.to_sym] | |
if checked == true | |
checkbox.check | |
elsif checkbox == false | |
checkbox.uncheck | |
end | |
end | |
end | |
def populate_radiobuttons_from_options(radiobuttons, options) | |
radiobuttons.each do |radio| | |
next if (value = options[radio.name.to_sym]).nil? | |
radio.check if radio.value.eql?(value) | |
end | |
end | |
def check_for_success | |
match = page.search('.successbox_head') | |
raise "Didn't return success" unless match.size > 0 | |
match.text.strip.eql?("Success!") | |
rescue | |
log.error "Unexpected response: #{$!}" | |
log.debug page.inspect | |
log.debug page.body.inspect | |
false | |
end | |
def click_link(pattern) | |
pattern = pattern.is_a?(Regexp) ? pattern : /#{pattern}/i | |
click page.links.text(pattern).first | |
end | |
private | |
def login(email=@email, password=@password) | |
tries = 0 | |
@logged_in = begin | |
get(LOGIN_URL) | |
# Fill out the login form | |
form = page.forms.first | |
form.username = email | |
form.password = password | |
submit(form, form.buttons.first) | |
# Check we have a logout link | |
!page.links.text(/Logout/).first.nil? | |
# If something random goes wrong | |
rescue | |
# Try 3 times before giving up | |
tries += 1 | |
retry unless tries > 3 | |
raise $! | |
end | |
end | |
end |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment