Class: Rightscale::Rackspace::Interface

Inherits:
Object
  • Object
show all
Defined in:
lib/rackspace.rb,
lib/rackspace_base.rb

Overview

Rightscale::Rackspace::Interface

Examples:

# Create a handle
rackspace = Rightscale::Rackspace::Interface::new('uw1...cct', '99b0...047d', :verbose_errors => true )

# list images and flavors
rackspace.list_images(:detaile => true)
rackspace.list_flavors(:detaile => true)

# launch a new server
image_id = 8
flavor_id = 4
new_server = rackspace.create_server('my-awesome-server', image_id, flavor_id,
                                     :metadata => {
                                       'metakey1' => 'metavalue1',
                                       'metakey2' => 'metavalue2',
                                       'metakey3' => 'metavalue3' },
                                     :personalities => {
                                       '/home/file1.txt' => 'ohoho!',
                                       '/home/file2.txt' => 'ahaha!',
                                       '/home/file3.rb' => 'puts "Olalah!"'}

puts "New server password is: #{new_server['server']['adminPass']}"

# change the server password
rackspace.update_server(new_server['server']['id'], 'my_new_password')

# "upgrade" the server
new_flavor_id = 6
rackspace.resize_server(new_server['server']['id'], new_flavor_id)
rackspace.confirm_resized_server(new_server['server']['id'])

# make an image from the server
new_image = rackspace.create_image(new_server['server']['id'], 'my-awesome-image-0001')

# make a hard reboot (power off)
rackspace.reboot_server(new_server['server']['id'], :hard)

# delete the server
rackspace.delete_server(new_server['server']['id'])

RightRackspace gem caching usage (only list_xxx calls). This caching does not hit if any additional URL variables are set through :vars hash.

# Create a handle
rackspace = Rightscale::Rackspace::Interface::new('uw1...cct', '99b0...047d', :caching => true)

# make a call. this fills the internal cache with the response and
# a current timestamp
old_list = rackspace.list_servers(:detail => true)

# sleep a bit
sleep 5

# make another call
begin
  new_list = rackspace.list_servers(:detail => true)
rescue Rightscale::Rackspace::NoChange
  puts e.message #=> "Cached: '/servers/detail' has not changed since Thu, 09 Jul 2009 10:53:35 GMT."
  # extract the list of servers from internal cache
  new_list = rackspace.cache['/servers/detail'][:data]
  # the lists must be equal
  puts old_list == new_list
end

# non detailed list of servers
rackspace.list_servers
begin
  flavors = rackspace.list_servers
rescue Rightscale::Rackspace::NoChange
  flavors = rackspace.cache['/servers'][:data]
ens

# this does not hit internal cache doe to the :vars.
new_list = rackspace.list_servers(:detail => true, :vars => {'format' => 'json'})

Rackspace service caching usage:

# Create a handle
rackspace = Rightscale::Rackspace::Interface::new('uw1...cct', '99b0...047d')

# 'if-modified-since' HTTP request header usage:
last_request_time = Time.now - 3600
begin
 rackspace.list_servers(:detail => true, :headers => { 'if-modified-since' => last_request_time })
rescue Rightscale::Rackspace::NoChange => e
  # e.message can return one of the messages below:
  # - "Cached: '/servers/detail' has not changed since Thu, 09 Jul 2009 10:55:41 GMT."
  # - "NotModified: '/servers/detail' has not changed since Thu, 09 Jul 2009 10:55:41 GMT."
  # The first comes when we have cached response from Rackspace and it can be retreived as
  # rackspace.cache['/servers/detail'][:data]. The second one appears when the local
  # cache has no stored records for the request.
  puts e.message
end

# 'changes-since' URL variable usage:
begin
  new_servers = rackspace.list_servers(:detail => true, :vars => { 'changes-since' => last_request_time })
  # show the changes at servers since last_request_time
  puts new_servers.inspect
rescue Rightscale::Rackspace::NoChange => e
  puts e.message #=>
    "NotModified: '/flavors?changes-since=1247137435&limit=1000&offset=0' has not changed since the requested time."
end

Callbacks:

# On response calback
on_response = Proc.new do |handle|
  puts ">> response headers: #{handle.last_response.to_hash.inspect}"
end

# On error calback
on_error = Proc.new do |handle, error_message|
  puts ">> Error: #{error_message}"
end

# Create a handle
rackspace = Rightscale::Rackspace::Interface::new('uw1...cct', '99b0...047d',
  :on_response => on_response,
  :on_error    => on_error)

Constant Summary collapse

DEFAULT_AUTH_ENDPOINT =
"https://auth.api.rackspacecloud.com/v1.0"
DEFAULT_LIMIT =
1000
@@rackspace_problems =
[]
@@bench =
Rightscale::Rackspace::BenchmarkingBlock.new
@@params =
{}
@@caching =
false

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(username = nil, auth_key = nil, params = {}) ⇒ Interface

Create new Rackspace interface handle.

Params:

:logger            - a logger object
:caching           - enabled/disables RightRackspace caching on level (only for list_xxx calls)
:verbose_errors    - verbose error messages
:auth_endpoint     - an auth endpoint URL ()
:service_endpoint  - a service endpoint URL
callbacks:
:on_request(self, request_hash) - every time before the request
:on_response(self)              - every time the response comes
:on_error(self, error_message)  - every time the response is not 2xx | 304
:on_success(self)               - once after the successfull response
:on_login(self)                 - before login
:on_login_success(self)         - when login is successfull
:on_login_failure(self)         - when login fails
:on_failure(self)               - once after the unsuccessfull response

Handle creation:

# Just pass your username and youe key
rackspace = Rightscale::Rackspace::Interface::new('uw1...cct', '99b0...047d')

# The username and the key are in ENV vars: RACKSPACE_USERNAME & RACKSPACE_AUTH_KEY
rackspace = Rightscale::Rackspace::Interface::new

# Specify the auth endpoint and the service endpoint explicitly. Make the error
# messages as verbose as possible.
rackspace = Rightscale::Rackspace::Interface::new('uw1...cct', '99b0...047d',
  :auth_endpoint  => 'https://api.mosso.com/auth',
  :service_point  => 'https://servers.api.rackspacecloud.com/v1.0/413609',
  :verbose_errors => true )

# Fix params after the handle creation:
rackspace = Rightscale::Rackspace::Interface::new('uw1...cct', '99b0...047d')
rackspace.params[:verbose_errors] = true

Calbacks handling:

# On response calback
on_response = Proc.new do |handle|
  puts ">> response headers: #{handle.last_response.to_hash.inspect}"
end

# On error calback
on_error = Proc.new do |handle, error_message|
  puts ">> Error: #{error_message}"
end

# Create a handle
rackspace = Rightscale::Rackspace::Interface::new('uw1...cct', '99b0...047d',
  :on_response => on_response,
  :on_error    => on_error)


145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
# File 'lib/rackspace_base.rb', line 145

def initialize(username=nil, auth_key=nil, params={})
  @params = params
  # Auth data
  @username  = username || ENV['RACKSPACE_USERNAME']
  @auth_key  = auth_key || ENV['RACKSPACE_AUTH_KEY']
  @logged_in = false
  # Auth host
  @auth_headers  = {} # a set of headers is returned on authentification coplete
  @auth_endpoint = ENV['RACKSPACE_AUTH_ENDPOINT'] || params[:auth_endpoint] || DEFAULT_AUTH_ENDPOINT
  @auth_endpoint_data = endpoint_to_host_data(@auth_endpoint)
  # Logger
  @logger = @params[:logger] || (defined?(RAILS_DEFAULT_LOGGER) && RAILS_DEFAULT_LOGGER) || Logger.new(STDOUT)
  # Request and response
  @last_request = nil
  @last_response = nil
  # cache
  @cache = {}
end

Instance Attribute Details

#auth_endpointObject

Returns the value of attribute auth_endpoint.



73
74
75
# File 'lib/rackspace_base.rb', line 73

def auth_endpoint
  @auth_endpoint
end

#auth_headersObject (readonly)

Returns the value of attribute auth_headers.



71
72
73
# File 'lib/rackspace_base.rb', line 71

def auth_headers
  @auth_headers
end

#auth_keyObject

Returns the value of attribute auth_key.



69
70
71
# File 'lib/rackspace_base.rb', line 69

def auth_key
  @auth_key
end

#auth_tokenObject (readonly)

Returns the value of attribute auth_token.



72
73
74
# File 'lib/rackspace_base.rb', line 72

def auth_token
  @auth_token
end

#cacheObject (readonly)

Returns the value of attribute cache.



79
80
81
# File 'lib/rackspace_base.rb', line 79

def cache
  @cache
end

#last_errorObject

Returns the value of attribute last_error.



77
78
79
# File 'lib/rackspace_base.rb', line 77

def last_error
  @last_error
end

#last_requestObject

Returns the value of attribute last_request.



75
76
77
# File 'lib/rackspace_base.rb', line 75

def last_request
  @last_request
end

#last_responseObject

Returns the value of attribute last_response.



76
77
78
# File 'lib/rackspace_base.rb', line 76

def last_response
  @last_response
end

#logged_inObject (readonly)

Returns the value of attribute logged_in.



70
71
72
# File 'lib/rackspace_base.rb', line 70

def logged_in
  @logged_in
end

#loggerObject (readonly)

Returns the value of attribute logger.



78
79
80
# File 'lib/rackspace_base.rb', line 78

def logger
  @logger
end

#service_endpointObject

Returns the value of attribute service_endpoint.



74
75
76
# File 'lib/rackspace_base.rb', line 74

def service_endpoint
  @service_endpoint
end

#usernameObject

Returns the value of attribute username.



68
69
70
# File 'lib/rackspace_base.rb', line 68

def username
  @username
end

Class Method Details

.benchObject



49
50
51
# File 'lib/rackspace_base.rb', line 49

def self.bench
  @@bench
end

.paramsObject



54
55
56
# File 'lib/rackspace_base.rb', line 54

def self.params
  @@params
end

.rackspace_problemsObject

A Regexps of errors say the Rackspace has issues and a request is likely to be repeated



44
45
46
# File 'lib/rackspace_base.rb', line 44

def self.rackspace_problems
  @@rackspace_problems
end

Instance Method Details

#api(verb, path = '', options = {}) ⇒ Object

Call Rackspace. Caching is not used.



354
355
356
357
358
359
# File 'lib/rackspace_base.rb', line 354

def api(verb, path='', options={}) # :nodoc:
   unless @logged_in
  options[:headers] ||= {}
  options[:headers]['x-auth-token'] = @auth_token
  request_info(generate_request(verb, path, options))
end

#api_or_cache(verb, path, options = {}) ⇒ Object

Call Rackspace. Use cache if possible opts:

:incrementally - use incrementally list to get the whole list of items
otherwise it will get max DEFAULT_LIMIT items (PS API call must support pagination)


366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
# File 'lib/rackspace_base.rb', line 366

def api_or_cache(verb, path, options={}) # :nodoc:
  use_caching  = merged_params[:caching] && options[:vars].blank?
  cache_record = use_caching && @cache[path]
  # Create a proc object to avoid a code duplication
  proc = Proc.new do
    if options[:incrementally]
         incrementally_list_resources(verb, path, nil, nil, options)
    else api(verb, path, options)
    end
  end
  # The cache is not used or record is not found
  unless cache_record
    response = proc.call
    if use_caching
      last_modified_at = @last_response.to_hash['last-modified'].first
      update_cache(path, last_modified_at, response)
    end
    response
  else
    # Record found - ask Rackspace whether it changed or not since last update
    options = options.dup
    options[:headers] ||= {}
    options[:headers]['if-modified-since'] = cache_record[:last_modified_at]
    proc.call
  end
end

#authenticate(opts = {}) ⇒ Object

Authenticate a user. Params: soft is used for auto-authentication when auth_token expires. Soft auth do not overrides @last_request and @last_response attributes (are needed for a proper error handling) on success.



294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
# File 'lib/rackspace_base.rb', line 294

def authenticate(opts={}) # :nodoc:
  @logged_in    = false
  @auth_headers = {}
  opts = opts.dup
  opts[:endpoint_data] = @auth_endpoint_data
  (opts[:headers] ||= {}).merge!({ 'x-auth-user' => @username, 'x-auth-key'  => @auth_key })
  request_data  = generate_request( :get, '', opts )
  on_event(:on_login)
  internal_request_info(request_data)
  unless @last_response.is_a?(Net::HTTPSuccess)
    @error_handler = nil
    @last_error = HttpErrorHandler::extract_error_description(@last_response, merged_params[:verbose_errors])
    on_event(:on_error, @last_error)
    on_event(:on_login_failure)
    on_event(:on_failure)
    raise Error.new(@last_error)
  end
  # Store all auth response headers
  @auth_headers = @last_response.to_hash
  @auth_token   = @auth_headers['x-auth-token'].first
  # Service endpoint
  @service_endpoint      = merged_params[:service_endpoint] || @auth_headers['x-server-management-url'].first
  @service_endpoint_data = endpoint_to_host_data(@service_endpoint)
  @logged_in = true
  on_event(:on_login_success)
  true
end

#cached_path(path) ⇒ Object

simple_path(‘/v1.0/123456/images/detail?var1=var2’) #=> ‘/images/detail’



281
282
283
# File 'lib/rackspace_base.rb', line 281

def cached_path(path) # :nodoc:
  simple_path(path)[/([^?]*)/] && $1
end

#confirm_resized_server(server_id, opts = {}) ⇒ Object

Confirm a server resize action.

rackspace.confirm_resized_server(2290) #=> true


556
557
558
559
# File 'lib/rackspace.rb', line 556

def confirm_resized_server(server_id, opts={})
  body = { 'confirmResize' => nil }
  api(:post, "/servers/#{server_id}/action", opts.merge(:body => body.to_json))
end

#create_image(server_id, name, opts = {}) ⇒ Object

Create a new image for the given server ID. Once complete, a new image will be available that can be used to rebuild or create servers. Specifying the same image name as an existing custom image replaces the image

# create an image
new_image = rackspace.create_image(62844, 'my-awesome-image-0001') #=>
  {"image"=>{"name"=>"my-awesome-image-0001", "serverId"=>62844, "id"=>3226}}

# sleep a bit
sleep 10

# get the new image status
rackspace.get_image(new_image['image']['id']) #=>
  {"image"=>
    {"name"=>"my-awesome-image-0001",
     "progress"=>78,
     "id"=>3226,
     "updated"=>"2009-07-07T01:20:16-05:00",
     "status"=>"SAVING",
     "created"=>"2009-07-07T01:16:51-05:00"}}

# sleep more to make the new image active
sleep 60

# list all the images
rackspace.list_images(:detail => true) #=>
  {"images"=>
    [{"name"=>"CentOS 5.2", "id"=>2, "status"=>"ACTIVE"},
     {"name"=>"Gentoo 2008.0",
      "id"=>3,
      "updated"=>"2007-10-24T12:52:03-05:00",
      "status"=>"ACTIVE"},
     {"name"=>"Debian 5.0 (lenny)", "id"=>4, "status"=>"ACTIVE"},
     {"name"=>"Fedora 10 (Cambridge)", "id"=>5, "status"=>"ACTIVE"},
     {"name"=>"CentOS 5.3", "id"=>7, "status"=>"ACTIVE"},
     {"name"=>"Ubuntu 9.04 (jaunty)", "id"=>8, "status"=>"ACTIVE"},
     {"name"=>"Arch 2009.02", "id"=>9, "status"=>"ACTIVE"},
     {"name"=>"Ubuntu 8.04.2 LTS (hardy)", "id"=>10, "status"=>"ACTIVE"},
     {"name"=>"Ubuntu 8.10 (intrepid)", "id"=>11, "status"=>"ACTIVE"},
     {"name"=>"Red Hat EL 5.3", "id"=>12, "status"=>"ACTIVE"},
     {"name"=>"Fedora 11 (Leonidas)", "id"=>13, "status"=>"ACTIVE"},
     {"name"=>"my-awesome-image-0001",
      "serverId"=>62844,
      "progress"=>100,
      "id"=>3226,
      "updated"=>"2009-07-07T01:20:48-05:00",
      "status"=>"ACTIVE",
      "created"=>"2009-07-07T01:17:16-05:00"}]}


321
322
323
324
325
# File 'lib/rackspace.rb', line 321

def create_image(server_id, name, opts={})
  body = { 'image' => { 'name'     => name,
                        'serverId' => server_id } }
  api(:post, "/images",  opts.merge(:body => body.to_json))
end

#create_server(server_data, opts = {}) ⇒ Object

Launch a new server.

+Server_data+ is a hash of params params:
 Mandatory: :name, :image_id, :flavor_id
 Optional:  :metadata, :personalities

rackspace.create_server(
  :name      => 'my-awesome-server',
  :image_id  => 8,
  :flavor_id => 4,
  :metadata  => { 'KD1' => 'XXXX1', 'KD2' => 'XXXX2'},
  :personalities => { '/home/1.txt' => 'woo-hoo',
                      '/home/2.rb'  => 'puts"Olalah!' }) #=>
  {"server"=>
    {"name"=>"my-awesome-server",
     "addresses"=>{"public"=>["174.143.56.6"], "private"=>["10.176.1.235"]},
     "progress"=>0,
     "imageId"=>8,
     "metadata"=>{"KD1"=>"XXXX1", "KD2"=>"XXXX2"},
     "adminPass"=>"my-awesome-server85lzHZ",
     "id"=>2290,
     "flavorId"=>4,
     "hostId"=>"19956ee1c79a57e481b652ddf818a569",
     "status"=>"BUILD"}}


441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
# File 'lib/rackspace.rb', line 441

def create_server(server_data, opts={} )
  personality = server_data[:personalities].to_a.dup
  personality.map! { |file, contents| { 'path'=> file, 'contents' => Base64.encode64(contents).chomp } }
  body = {
    'server' => {
      'name'     => server_data[:name],
      'imageId'  => server_data[:image_id],
      'flavorId' => server_data[:flavor_id],
    }
  }
  #body['server']['adminPass']   = server_data[:password] if     server_data[:password]
  body['server']['sharedIpGroupId']   = server_data[:shared_ip_group_id] if server_data[:shared_ip_group_id]
  body['server']['metadata']    = server_data[:metadata] unless server_data[:metadata].blank?
  body['server']['personality'] = personality            unless personality.blank?
  api(:post, "/servers", opts.merge(:body => body.to_json))
end

#create_shared_ip_group(name, server_id = nil, opts = {}) ⇒ Object

Create a new shared IP group.

rackspace.create_shared_ip_group('my_awesome_group', 2290) #=>
 {"sharedIpGroup"=>{"name"=>"my_awesome_group", "id"=>42}}


719
720
721
722
723
# File 'lib/rackspace.rb', line 719

def create_shared_ip_group(name, server_id=nil, opts={})
  body = { 'sharedIpGroup' => { 'name' => name } }
  body['sharedIpGroup']['server'] = server_id unless server_id.blank?
  api(:post, "/shared_ip_groups", opts.merge(:body => body.to_json))
end

#delete_backup_schedule(server_id, opts = {}) ⇒ Object

Deletes the backup schedule for the specified server.

h.delete_backup_schedule(62844) #=> true

P.S. the changes may appear in some seconds



686
687
688
# File 'lib/rackspace.rb', line 686

def delete_backup_schedule(server_id, opts={})
  api(:delete, "/servers/#{server_id}/backup_schedule", opts)
end

#delete_server(server_id, opts = {}) ⇒ Object

Delete a server. Returns true on success.

rackspace.delete_server(2284) #=> true


640
641
642
# File 'lib/rackspace.rb', line 640

def delete_server(server_id, opts={})
  api(:delete, "/servers/#{server_id}", opts)
end

#delete_shared_ip_group(shared_ip_group_id, opts = {}) ⇒ Object

Delete an IP group.

rackspace.delete_shared_ip_group(42) #=> true


738
739
740
# File 'lib/rackspace.rb', line 738

def delete_shared_ip_group(shared_ip_group_id, opts={})
  api(:delete, "/shared_ip_groups/#{shared_ip_group_id}", opts)
end

#detailed_path(path, options) ⇒ Object

detailed_path(‘/images’, true) #=> ‘/images/detail’



286
287
288
# File 'lib/rackspace_base.rb', line 286

def detailed_path(path, options) # :nodoc:
  "#{path}#{options[:detail] ? '/detail' : ''}"
end

#endpoint_to_host_data(endpoint) ⇒ Object

Parses an endpoint and returns a hash of data



82
83
84
85
86
87
88
89
# File 'lib/rackspace_base.rb', line 82

def endpoint_to_host_data(endpoint)# :nodoc:
  service = URI.parse(endpoint).path
  service.chop! if service[/\/$/]  # remove a trailing '/'
  { :server   => URI.parse(endpoint).host,
    :service  => service,
    :protocol => URI.parse(endpoint).scheme,
    :port     => URI.parse(endpoint).port }
end

#generate_request(verb, path = '', opts = {}) ⇒ Object

Generate a request.

opts:

:body            - String
:endpoint_data   - Hash
:no_service_path - bool
:headers           - a hash or an array of HTTP headers
:vars              - a hash or an array of URL variables


173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
# File 'lib/rackspace_base.rb', line 173

def generate_request(verb, path='', opts={}) #:nodoc:
  # Form a valid http verb: 'Get', 'Post', 'Put', 'Delete'
  verb = verb.to_s.capitalize
  raise "Unsupported HTTP verb #{verb.inspect}!" unless verb[/^(Get|Post|Put|Delete)$/]
  # Select an endpoint
  endpoint_data = (opts[:endpoint_data] || @service_endpoint_data).dup
  # Fix a path
  path = "/#{path}" if !path.blank? && !path[/^\//]
  # Request variables
  request_params = opts[:vars].to_a.map do |key, value|
    key = key.to_s.downcase
    # Make sure we do not pass a Time object instead of integer for 'changes-since'
    value = value.to_i if key == 'changes-since'
    "#{URI.escape(key)}=#{URI.escape(value.to_s)}"
  end.join('&')
  # Build a request final path
  service = opts[:no_service_path] ? '' : endpoint_data[:service]
  request_path  = "#{service}#{path}"
  request_path  = '/' if request_path.blank?
  request_path += "?#{request_params}" unless request_params.blank?
  # Create a request
  request = eval("Net::HTTP::#{verb}").new(request_path)
  request.body = opts[:body] if opts[:body]
  # Set headers
  opts[:headers].to_a.each do |key, value|
    key = key.to_s.downcase
    # make sure 'if-modified-since' is always httpdated
    if key == 'if-modified-since'
      value = Time.at(value)     if value.is_a?(Fixnum)
      value = value.utc.httpdate if value.is_a?(Time)
    end
    request[key] = value.to_s
  end
  request['content-type'] ||= 'application/json'
  request['accept'] = 'application/json'
  # prepare output hash
  endpoint_data.merge(:request => request)
end

#get_backup_schedule(server_id, opts = {}) ⇒ Object

Get the backup schedule for the specified server.

rackspace.get_backup_schedule(62844) #=>
  {"backupSchedule"=>{"weekly"=>"DISABLED", "enabled"=>false, "daily"=>"DISABLED"}}


653
654
655
# File 'lib/rackspace.rb', line 653

def get_backup_schedule(server_id, opts={})
  api(:get, "/servers/#{server_id}/backup_schedule", opts)
end

#get_flavor(flavor_id, opts = {}) ⇒ Object

Get flavor data.

rackspace.get_flavor(5) #=>
  {"flavor"=>{"name"=>"4GB slice", "id"=>5, "ram"=>4096, "disk"=>160}}


375
376
377
# File 'lib/rackspace.rb', line 375

def get_flavor(flavor_id, opts={})
  api(:get, "/flavors/#{flavor_id}", opts)
end

#get_image(image_id, opts = {}) ⇒ Object

Get image data.

rackspace.get_image(5) #=>
  {"image"=>{"name"=>"Fedora 10 (Cambridge)", "id"=>5, "status"=>"ACTIVE"}}


267
268
269
# File 'lib/rackspace.rb', line 267

def get_image(image_id, opts={})
  api(:get, "/images/#{image_id}", opts)
end

#get_server(server_id, opts = {}) ⇒ Object

Get a server data.

rackspace.get_server(2290)
  {"server"=>
    {"name"=>"my-awesome-server",
     "addresses"=>{"public"=>["174.143.56.6"], "private"=>["10.176.1.235"]},
     "progress"=>100,
     "imageId"=>8,
     "metadata"=>{"KD1"=>"XXXX1", "KD2"=>"XXXX2"},
     "id"=>2290,
     "flavorId"=>4,
     "hostId"=>"19956ee1c79a57e481b652ddf818a569",
     "status"=>"ACTIVE"}}


471
472
473
# File 'lib/rackspace.rb', line 471

def get_server(server_id, opts={})
  api(:get, "/servers/#{server_id}", opts)
end

#get_shared_ip_group(shared_ip_group_id, opts = {}) ⇒ Object

Get shared IP group data.

rackspace.list_shared_ip_groups #=>
 {"sharedIpGroups"=>[{"name"=>"my_awesome_group", "id"=>42, "servers"=>[2290]}]}


730
731
732
# File 'lib/rackspace.rb', line 730

def get_shared_ip_group(shared_ip_group_id, opts={})
  api(:get, "/shared_ip_groups/#{shared_ip_group_id}", opts)
end

#incrementally_list_flavors(offset = nil, limit = nil, opts = {}, &block) ⇒ Object

Incrementally list flavors.

rackspace.incrementally_list_flavors(0,3) do |response|
  puts response.inspect
  true
end


366
367
368
# File 'lib/rackspace.rb', line 366

def incrementally_list_flavors(offset=nil, limit=nil, opts={}, &block)
  incrementally_list_resources(:get, detailed_path("/flavors", opts), offset, limit, opts, &block)
end

#incrementally_list_images(offset = nil, limit = nil, opts = {}, &block) ⇒ Object

Incrementally list images.

# list images by 3
rackspace.incrementally_list_images(0, 3, :detail=>true) do |response|
  puts response.inspect
  true
end


258
259
260
# File 'lib/rackspace.rb', line 258

def incrementally_list_images(offset=nil, limit=nil, opts={}, &block)
  incrementally_list_resources(:get, detailed_path("/images", opts), offset, limit, opts, &block)
end

#incrementally_list_resources(verb, path, offset = nil, limit = nil, opts = {}, &block) ⇒ Object

Incrementally lists something.



323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
# File 'lib/rackspace_base.rb', line 323

def incrementally_list_resources(verb, path, offset=nil, limit=nil, opts={}, &block) # :nodoc:
  opts = opts.dup
  opts[:vars] ||= {}
  opts[:vars]['offset'] = offset || 0
  opts[:vars]['limit']  = limit  || DEFAULT_LIMIT
  # Get a resource name by path:
  #   '/images'                  -> 'images'
  #   '/shared_ip_groups/detail' -> 'sharedIpGroups'
  resource_name = ''
  (path[%r{^/([^/]*)}] && $1).split('_').each_with_index do |w, i|
    resource_name += (i==0 ? w.downcase : w.capitalize)
  end
  result = { resource_name => []}
  loop do
#          begin
      response = api(verb, path, opts)
      result[resource_name] += response[resource_name]
#          rescue Rightscale::Rackspace::Error => e
#            raise e unless e.message[/itemNotFound/]
#            response = nil
#          end
    break if  response.blank? ||
             (response[resource_name].blank?) ||
             (block && !block.call(response)) ||
             (response[resource_name].size < opts[:vars]['limit'])
    opts[:vars]['offset'] += opts[:vars]['limit']
  end
  result
end

#incrementally_list_servers(offset = nil, limit = nil, opts = {}, &block) ⇒ Object

Incrementally list servers.

# list servers by 3
rackspace.incrementally_list_servers(0, 3) do |response|
  puts response.inspect
  true
end


413
414
415
# File 'lib/rackspace.rb', line 413

def incrementally_list_servers(offset=nil, limit=nil, opts={}, &block)
  incrementally_list_resources(:get, detailed_path("/servers", opts), offset, limit, opts, &block)
end

#incrementally_list_shared_ip_groups(offset = nil, limit = nil, opts = {}, &block) ⇒ Object

Incrementally list IP groups.

# list groups by 5
rackspace.incrementally_list_shared_ip_groups(0, 5) do |x|
  puts x.inspect
  true
end


710
711
712
# File 'lib/rackspace.rb', line 710

def incrementally_list_shared_ip_groups(offset=nil, limit=nil, opts={}, &block)
  incrementally_list_resources(:get, detailed_path("/shared_ip_groups", opts), offset, limit, opts, &block)
end

#internal_request_info(request_hash) ⇒ Object

Just requests a remote end



213
214
215
216
217
218
219
# File 'lib/rackspace_base.rb', line 213

def internal_request_info(request_hash) #:nodoc:
  on_event(:on_request, request_hash)
  @connection  ||= Rightscale::HttpConnection.new(:exception => Error, :logger => @logger)
  @last_request  = request_hash[:request]
  @@bench.service.add!{ @last_response = @connection.request(request_hash) }
  on_event(:on_response)
end

#list_addresses(server_id, address_type = :all, opts = {}) ⇒ Object

Get server addresses.

# get all addresses
rackspace.list_addresses(62844) #=>
  {"addresses"=>{"public"=>["174.143.246.228"], "private"=>["10.176.134.157"]}}

# get public addresses
rackspace.list_addresses(62844, :public) #=>
  {"public"=>["174.143.246.228"]}

# get private addresses
rackspace.list_addresses(62844, :private) #=>
  {"private"=>["10.176.134.157"]}

RightRackspace caching: no



589
590
591
592
593
594
595
596
# File 'lib/rackspace.rb', line 589

def list_addresses(server_id, address_type=:all, opts={})
  path = "/servers/#{server_id}/ips"
  case address_type.to_s
  when 'public'  then path += "/public"
  when 'private' then path += "/private"
  end
  api(:get, path, opts.merge(:incrementally => PAGINATION_ENABLED))
end

#list_api_versions(opts = {}) ⇒ Object

List all API versions supported by a Service Endpoint.

rackspace.list_api_versions #=> {"versions"=>[{"id"=>"v1.0", "status"=>"BETA"}]}

RightRackspace caching: yes, key: '/'


172
173
174
# File 'lib/rackspace.rb', line 172

def list_api_versions(opts={})
  api_or_cache(:get, "/",  opts.merge(:no_service_path => true))
end

#list_flavors(opts = {}) ⇒ Object

List flavors. Options: :detail => false|true.

# Get list of flavors.
rackspace.list_flavors #=>
  {"flavors"=>
    [{"name"=>"256 slice", "id"=>1},
     {"name"=>"512 slice", "id"=>2},
     {"name"=>"1GB slice", "id"=>3},
     ...}]}

# Get the detailed flavors description.
rackspace.list_flavors(:detail => true) #=>
  {"flavors"=>
    [{"name"=>"256 slice", "id"=>1, "ram"=>256, "disk"=>10},
     {"name"=>"512 slice", "id"=>2, "ram"=>512, "disk"=>20},
     {"name"=>"1GB slice", "id"=>3, "ram"=>1024, "disk"=>40},
     ...}]}

# Get the most recent changes or Rightscale::Rackspace::NoChange.
# (no RightRackspace gem caching)
rackspace.list_flavors(:detail => true, :vars => {'changes-since'=>Time.now-3600}) #=>

RightRackspace caching: yes, keys: ‘/flavors’, ‘/flavors/detail’



355
356
357
# File 'lib/rackspace.rb', line 355

def list_flavors(opts={})
  api_or_cache(:get, detailed_path("/flavors", opts), opts.merge(:incrementally => PAGINATION_ENABLED))
end

#list_images(opts = {}) ⇒ Object

List images. Options: :detail => false|true.

# Get images list.
rackspace.list_images #=>
  {"images"=>
    [{"name"=>"CentOS 5.2", "id"=>2},
     {"name"=>"Gentoo 2008.0", "id"=>3},
     {"name"=>"Debian 5.0 (lenny)", "id"=>4},
      ...}]}

# Get the detailed images description.
rackspace.list_images(:detail => true) #=>
  {"images"=>
    [{"name"=>"CentOS 5.2", "id"=>2, "status"=>"ACTIVE"},
     {"name"=>"Gentoo 2008.0",
      "id"=>3,
      "updated"=>"2007-10-24T12:52:03-05:00",
      "status"=>"ACTIVE"},
     {"name"=>"Debian 5.0 (lenny)", "id"=>4, "status"=>"ACTIVE"},
     {"name"=>"Fedora 10 (Cambridge)", "id"=>5, "status"=>"ACTIVE"},
     {"name"=>"CentOS 5.3", "id"=>7, "status"=>"ACTIVE"},
     {"name"=>"Ubuntu 9.04 (jaunty)", "id"=>8, "status"=>"ACTIVE"},
     {"name"=>"Arch 2009.02", "id"=>9, "status"=>"ACTIVE"},
     {"name"=>"Ubuntu 8.04.2 LTS (hardy)", "id"=>10, "status"=>"ACTIVE"},
     {"name"=>"Ubuntu 8.10 (intrepid)", "id"=>11, "status"=>"ACTIVE"},
     {"name"=>"Red Hat EL 5.3", "id"=>12, "status"=>"ACTIVE"},
     {"name"=>"Fedora 11 (Leonidas)", "id"=>13, "status"=>"ACTIVE"},
     {"name"=>"my-awesome-image-0001",
      "serverId"=>62844,
      "progress"=>100,
      "id"=>3226,
      "updated"=>"2009-07-07T01:20:48-05:00",
      "status"=>"ACTIVE",
      "created"=>"2009-07-07T01:17:16-05:00"}]}

RightRackspace caching: yes, keys: ‘/images’, ‘/images/detail’



246
247
248
# File 'lib/rackspace.rb', line 246

def list_images(opts={})
  api_or_cache(:get, detailed_path("/images", opts), opts.merge(:incrementally => PAGINATION_ENABLED))
end

#list_limits(opts = {}) ⇒ Object

Determine rate limits.

rackspace.list_limits #=> 
  {"limits"=>
    {"absolute"=>
      {"maxNumServers"=>25, "maxIPGroups"=>50, "maxIPGroupMembers"=>25},
     "rate"=>
      [{"regex"=>".*",
        "verb"=>"PUT",
        "URI"=>"*",
        "remaining"=>10,
        "unit"=>"MINUTE",
        "value"=>10,
        "resetTime"=>1246604596},
       {"regex"=>"^/servers",
        "verb"=>"POST",
        "URI"=>"/servers*",
        "remaining"=>1000,
        "unit"=>"DAY",
        "value"=>1000,
        "resetTime"=>1246604596}, ...]}}


199
200
201
202
203
# File 'lib/rackspace.rb', line 199

def list_limits(opts={})
#     # RightRackspace caching: yes, key: '/limits'
#        api_or_cache(:get, "/limits",  opts)
  api(:get, "/limits",  opts)
end

#list_servers(opts = {}) ⇒ Object

List servers. Options: :detail => false|true.

rackspace.list_servers #=>
  {"servers"=>[{"name"=>"my-super-awesome-server-", "id"=>62844}]}

rackspace.list_servers(:detail => true) #=>
  {"servers"=>
    [{"name"=>"my-super-awesome-server-",
      "addresses"=>
       {"public"=>["174.143.246.228"], "private"=>["10.176.134.157"]},
      "progress"=>100,
      "imageId"=>8,
      "metadata"=>{"data1"=>"Ohoho!", "data2"=>"Ehehe!"},
      "id"=>62844,
      "flavorId"=>3,
      "hostId"=>"fabfc1cebef6f1d7e4b075138dbd6b46",
      "status"=>"ACTIVE"}]


401
402
403
# File 'lib/rackspace.rb', line 401

def list_servers(opts={})
  api_or_cache(:get, detailed_path("/servers", opts), opts.merge(:incrementally => PAGINATION_ENABLED))
end

#list_shared_ip_groups(opts = {}) ⇒ Object

List shared IP groups. Options: :detail => false|true.

RightRackspace caching: yes, keys: ‘/shared_ip_groups’, ‘/shared_ip_groups/detail’



698
699
700
# File 'lib/rackspace.rb', line 698

def list_shared_ip_groups(opts={})
  api_or_cache(:get, detailed_path("/shared_ip_groups", opts), opts.merge(:incrementally => PAGINATION_ENABLED))
end

#login(opts = {}) ⇒ Object

The login is executed automatically when one calls any othe API call. The only use case for this method is when one need to pass any custom headers or URL vars during a login process.

rackspace. #=> true


162
163
164
# File 'lib/rackspace.rb', line 162

def (opts={})
  authenticate(opts)
end

#merged_paramsObject

:nodoc:



62
63
64
# File 'lib/rackspace_base.rb', line 62

def merged_params #:nodoc:
  @@params.merge(@params)
end

#on_event(event, *params) ⇒ Object

Events (callbacks) for logging and debugging features. These callbacks do not catch low level connection errors that are handled by RightHttpConnection but only HTTP errors.



402
403
404
# File 'lib/rackspace_base.rb', line 402

def on_event(event, *params) #:nodoc:
  self.merged_params[event].call(self, *params) if self.merged_params[event].kind_of?(Proc)
end

#paramsObject



58
59
60
# File 'lib/rackspace_base.rb', line 58

def params
  @params
end

#reboot_server(server_id, type = :soft, opts = {}) ⇒ Object

Reboot a server.

# Soft reboot
rackspace.reboot_server(2290) #=> true

# Hard reboot (power off)
rackspace.reboot_server(2290, :hard) #=> true


500
501
502
503
# File 'lib/rackspace.rb', line 500

def reboot_server(server_id, type = :soft, opts={})
  body = { 'reboot' => { 'type' => type.to_s.upcase } }
  api(:post, "/servers/#{server_id}/action", opts.merge(:body => body.to_json))
end

#rebuild_server(server_id, image_id, opts = {}) ⇒ Object

The rebuild function removes all data on the server and replaces it with the specified image. Server id and IP addresses will remain the same.

# rebuild a server
rackspace.rebuild_server(62844, 3226) #=> true

# watch for the progress
rackspace.get_server(62844) #=>
  {"server"=>
    {"name"=>"my-super-awesome-server-",
     "addresses"=>{"public"=>["174.143.246.228"], "private"=>["10.176.134.157"]},
     "progress"=>65,
     "imageId"=>3226,
     "metadata"=>{"data1"=>"Ohoho!", "data2"=>"Ehehe!"},
     "id"=>62844,
     "flavorId"=>3,
     "hostId"=>"fabfc1cebef6f1d7e4b075138dbd6b46",
     "status"=>"REBUILD"}}


524
525
526
527
# File 'lib/rackspace.rb', line 524

def rebuild_server(server_id, image_id, opts={})
  body = { 'rebuild' => { 'imageId' => image_id } }
  api(:post, "/servers/#{server_id}/action", opts.merge(:body => body.to_json))
end

#request_info(request_hash) ⇒ Object

Request a remote end and process any errors is found



222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
# File 'lib/rackspace_base.rb', line 222

def request_info(request_hash) #:nodoc:
  internal_request_info(request_hash)
  result = nil
  # check response for success...
  case @last_response.code
  when /^2..|304/   # SUCCESS
    @error_handler = nil
    on_event(:on_success)
    
    # Cache hit: Cached
    case @last_response.code
    when '304'  # 'changes-since' param
      raise NoChange.new("NotModified: '#{simple_path(@last_request.path)}' has not changed since the requested time.")
    when '203'  # 'if-modified-since' header
      # TODO: Mhhh... It seems Rackspace updates 'last-modified' header every 60 seconds or something even if nothing has changed
      if @last_response.body.blank?
        cached_path    = cached_path(@last_request.path)
        last_modified  = @last_response['last-modified'].first
        message_header = merged_params[:caching] &&
                         @cache[cached_path] &&
                         @cache[cached_path][:last_modified_at] == last_modified ? 'Cached' : 'NotModified'
        # 203 + an empty response body means we asked whether the value did not change and it hit
        raise NoChange.new("#{message_header}: '#{cached_path}' has not changed since #{last_modified}.")
      end
    end

    # Parse a response body. If the body is empty the return +true+
    @@bench.parser.add! do
      result = if @last_response.body.blank? then true
               else
                 case Array(@last_response['content-type']).first
                 when 'application/json' then JSON::parse(@last_response.body)
                 else @last_response.body
                 end
               end
    end
  else # ERROR
    @last_error = HttpErrorHandler::extract_error_description(@last_response, merged_params[:verbose_errors])
    on_event(:on_error, @last_error)
    @error_handler ||= HttpErrorHandler.new(self, :errors_list => self.class.rackspace_problems)
    result           = @error_handler.check(request_hash)
    @error_handler   = nil
    if result.nil?
      on_event(:on_failure)
      raise Error.new(@last_error)
    end
  end
  result
rescue
  @error_handler = nil
  raise
end

#resize_server(server_id, flavor_id, opts = {}) ⇒ Object

The resize function converts an existing server to a different flavor, in essence, scaling the server up or down. The original server is saved for a period of time to allow rollback if there is a problem. All resizes should be tested and explicitly confirmed, at which time the original server is removed. All resizes are automatically confirmed after 24 hours if they are not explicitly confirmed or reverted.

rackspace.resize_server(2290, 3) #=> true
rackspace.get_server(2290) #=>
  {"server"=>
    {"name"=>"my-awesome-server",
     "addresses"=>{"public"=>["174.143.56.6"], "private"=>["10.176.1.235"]},
     "progress"=>0,
     "imageId"=>8,
     "metadata"=>{"KD1"=>"XXXX1", "KD2"=>"XXXX2"},
     "id"=>2290,
     "flavorId"=>4,
     "hostId"=>"19956ee1c79a57e481b652ddf818a569",
     "status"=>"QUEUE_RESIZE"}}


547
548
549
550
# File 'lib/rackspace.rb', line 547

def resize_server(server_id, flavor_id, opts={})
  body = { 'resize' => { 'flavorId' => flavor_id } }
  api(:post, "/servers/#{server_id}/action", opts.merge(:body => body.to_json))
end

#revert_resized_server(server_id, opts = {}) ⇒ Object

Revert a server resize action.

rackspace.revert_resized_server(2290) #=> true


565
566
567
568
# File 'lib/rackspace.rb', line 565

def revert_resized_server(server_id, opts={})
  body = { 'revertResize' => nil }
  api(:post, "/servers/#{server_id}/action", opts.merge(:body => body.to_json))
end

#share_ip_address(server_id, shared_ip_group_id, address, configure_server = true, opts = {}) ⇒ Object

Share an IP from an existing server in the specified shared IP group to another specified server in the same group.

rackspace.share_ip_address(2296, 42, "174.143.56.6") #=> true

rackspace.get_server(2290) #=>
  {"server"=>
    {"name"=>"my-awesome-server",
     "addresses"=>
      {"public"=>["174.143.56.6", "174.143.56.13"], "private"=>["10.176.1.235"]},
     "progress"=>100,
     "imageId"=>8,
     "metadata"=>{"KD1"=>"XXXX1", "KD2"=>"XXXX2"},
     "id"=>2290,
     "flavorId"=>3,
     "hostId"=>"1d5fa1271f57354d9e2861e848568eb3",
     "status"=>"SHARE_IP_NO_CONFIG"}}


616
617
618
619
620
621
622
623
624
# File 'lib/rackspace.rb', line 616

def share_ip_address(server_id, shared_ip_group_id, address, configure_server=true, opts={})
  body = { 
    'shareIp' => {
      'sharedIpGroupId' => shared_ip_group_id,
      'configureServer' => configure_server
    }
  }
  api(:put, "/servers/#{server_id}/ips/public/#{address}",  opts.merge(:body => body.to_json))
end

#simple_path(path) ⇒ Object

simple_path(‘/v1.0/123456/images/detail?var1=var2’) #=> ‘/images/detail?var1=var2’



276
277
278
# File 'lib/rackspace_base.rb', line 276

def simple_path(path) # :nodoc:
  (path[/^#{@service_endpoint_data[:service]}(.*)/] && $1) || path
end

#unshare_ip_address(server_id, address, opts = {}) ⇒ Object

Remove a shared IP address from the specified server

rackspace.unshare_ip_address(2296, "174.143.56.6") #=> true


630
631
632
633
# File 'lib/rackspace.rb', line 630

def unshare_ip_address(server_id, address, opts={})
  body = { 'unshareIp' => { 'addr' => address } }
  api(:delete, "/servers/#{server_id}/ips/public/#{address}",  opts.merge(:body => body.to_json))
end

#update_backup_schedule(server_id, schedule_data = {}, opts = {}) ⇒ Object

Create a new backup schedule or updates an existing backup schedule for the specified server. Schedule_data is a hash: :enabled, :daily, :weekly

# set just a daily backup
rackspace.update_backup_schedule(62844, {:enabled => true, :daily => "H_0400_0600"}) #=> true

# set both the weekly and the daily schedules
h.update_backup_schedule(62844, {:enabled => true, :daily => "H_0400_0600", :weekly => 'MONDAY'}) #=> true

# disable (delete) the schedule
h.update_backup_schedule(62844, {:enabled => false}) #=> true

P.S. the changes may appear in some seconds



671
672
673
674
675
676
677
678
# File 'lib/rackspace.rb', line 671

def update_backup_schedule(server_id, schedule_data={}, opts={})
  body = { 'backupSchedule' => { 'enabled' => schedule_data[:enabled] ? true : false } }
  daily  = schedule_data[:daily].blank?  ? 'DISABLED' : schedule_data[:daily].to_s.upcase
  weekly = schedule_data[:weekly].blank? ? 'DISABLED' : schedule_data[:weekly].to_s.upcase
  body['backupSchedule']['daily']  = daily
  body['backupSchedule']['weekly'] = weekly
  api(:post, "/servers/#{server_id}/backup_schedule", opts.merge(:body => body.to_json))
end

#update_cache(path, last_modified_at, data) ⇒ Object

:nodoc:



393
394
395
396
397
# File 'lib/rackspace_base.rb', line 393

def update_cache(path, last_modified_at, data) #:nodoc:
  @cache[path] ||= {}
  @cache[path][:last_modified_at] = last_modified_at
  @cache[path][:data] = data
end

#update_server(server_id, server_data, opts = {}) ⇒ Object

Change server name and/or password. Server_data: :name, :password

rackspace.update_server(2290, :password => '12345' ) #=> true
rackspace.update_server(2290, :name => 'my-super-awesome-server', :password => '67890' ) #=> true

P.S. the changes will appers in some seconds.

P.P.S. changes server status: ‘ACTIVE’ -> ‘PASSWORD’.



485
486
487
488
489
490
# File 'lib/rackspace.rb', line 485

def update_server(server_id, server_data, opts={})
  body = { 'server' => {} }
  body['server']['name']      = server_data[:name]     if server_data[:name]
  body['server']['adminPass'] = server_data[:password] if server_data[:password]
  api(:put, "/servers/#{server_id}", opts.merge(:body => body.to_json))
end