class Ravello::Client
A client for the Ravello
API. The client is a thin layer on top of the RESTful API. The client manages a single HTTPS connection, and implements login and retry functionality.
Some general comments on how the RESTful API is mapped:
-
The calls are named “<verb>_<noun>”, for example, “get_keypair”.
-
There is no client-side object model. The return value from any API call is simply the parsed JSON response.
-
Resources are returned as a Hash, containing the key/value pairs of the resource. It is common that resources contain nested hashes.
-
Lists of resources are returned as an Array of zero or more hashes. It is common that resources in an Array only contain a subset of all available attributes.
-
Resources are identifed by a numeric ID.
-
Methods that retrieve a single resource take this ID parameter as a parameter. As a special case, it is also allowed to pass in a hash with an “id” key. These methods return a Hash, or nil if the resource was not found.
-
Methods that retrieve multiple resources can be passed a filter. The filter consists of hash style arguments. Only resources that have the specified key/value pairs will included in the response.
-
Methods that create a resource return a represenation of the created resources. It is common that this represenation contains (a lot) more attributes than you passed in. The extra attributes are defaults filled in by the system.
-
API errors are HTTP response codes in the 4xx or 5xx range. All API errors are turned into exceptions, except for the 404 (Not Found) code for a “get” call. In this case, nil is returned.
-
In case a certain API call is not yet mapped as a method, a fallback
request
method is available that allows you to invoke arbitrary requests.
Attributes
The number of times to retry an API call before giving up. Only idempotent API calls are retried.
The default network timeout.
The parsed URL used in the current connection.
A hash containing user information. Available when the client is logged in.
Public Class Methods
Create a new API client. The following options are supported: @param :username [String] The API user name. @param :password [String] The API password. @param :timeout [Integer] The network timeout. @param :retries [Integer] Times to retry a failed operation.
# File lib/ravello-sdk.rb, line 156 def initialize(options={}) @username = options[:username] @password = options[:password] @timeout = options.fetch(:timeout, 60) @retries = options.fetch(:retries, 3) @logger = Logger.new(STDERR) @logger.level = if $DEBUG then Logger::DEBUG \ elsif $VERBOSE then Logger::INFO else Logger::WARN end @redirects = 3 @autologin = true @cookies = nil @user_info = nil @connection = nil set_url(options.fetch(:url, DEFAULT_URL)) end
Public Instance Methods
Close the connection to the API.
# File lib/ravello-sdk.rb, line 209 def close @connection.finish if !@connection.nil? @connection = nil @cookies = nil end
Connect to the API. @param url [String] The URL to connect to. If not provided, use the
default service end point.
# File lib/ravello-sdk.rb, line 185 def connect(url=nil) url = url unless url.nil? do_connect end
@return [Boolean] Whether the client is connected.
# File lib/ravello-sdk.rb, line 173 def connected? !@connection.nil? end
Create a new application. @param app [Hash] The application. @return [Hash] The application that was created.
# File lib/ravello-sdk.rb, line 268 def create_application(app) request(:POST, '/applications', app) end
Create a new blueprint. @param req [Hash] The blueprint creation request. @return [Hash] The blueprint that was created.
# File lib/ravello-sdk.rb, line 404 def create_blueprint(req) request(:POST, '/blueprints', req) end
Create a new keypair. @param keypair [Hash] The keypair to created. @return [Hash] The keypair that was created.
# File lib/ravello-sdk.rb, line 476 def create_keypair(keypair) request(:POST, '/keypairs', keypair) end
Delete an application @param id [Numeric,Hash] The ID of the application. This parameter may
also be a Hash with a numeric "id" key.
@return nil
# File lib/ravello-sdk.rb, line 283 def delete_application(id) if id.is_a? Hash then id = id['id'] end request(:DELETE, "/applications/#{id}") end
Delete a blueprint. @param id [Integer, Hash] The ID of the blueprint to delete. This
parameter may also be a Hash, in which case it must have an "id" key.
@return nil
# File lib/ravello-sdk.rb, line 412 def delete_blueprint(id) if id.is_a? Hash then id = id['id'] end request(:DELETE, "/blueprints/#{id}") end
Delete an image. @param id [Numeric, Hash] The Id of the image to delete. This may also be
a Hash, in which case it must have an "id" key.
@return nil
# File lib/ravello-sdk.rb, line 448 def delete_image(id) if id.is_a? Hash then id = id['id'] end request(:DELETE, "/images/#{id}") end
Delete a keypair. @param id The ID of the keypair to delete. This parameter may also be a
Hash, in which case it must have an "id" key.
@return nil
# File lib/ravello-sdk.rb, line 491 def delete_keypair(id) if id.is_a? Hash then id = id['id'] end request(:DELETE, "/keypairs/#{id}") end
Generate a new keyapair. @return [Hash] The newly generated keypair.
# File lib/ravello-sdk.rb, line 498 def generate_keypair request(:POST, '/keypairs/generate') end
Get an application by its ID. @param id [Integer, Hash] The numeric ID of the application. This may
also be a hash, in which case it must have a numeric "id" key.
@return [Hash, nil] The application, or nil if the application does not
exist.
# File lib/ravello-sdk.rb, line 250 def get_application(id) if id.is_a? Hash then id = id['id'] end request(:GET, "/applications/#{id}") end
Get a list of all applications. @param filter [Hash] A hash containing key/value pairs the application
must have.
@return [Array<Hash>] A list of applications.
# File lib/ravello-sdk.rb, line 259 def get_applications(filter={}) result = request(:GET, '/applications') result.select! do |app| match_filter(app, filter) end if !filter.empty? result end
Get a blueprint. @param id [Integer, Hash] The ID of the blueprint. This may also be a
Hash, in which case it must have an "id" key.
@return [Hash, nil] The blueprint as a Hash, or nil in case the blueprint
does not exist.
# File lib/ravello-sdk.rb, line 386 def get_blueprint(id) if id.is_a? Hash then id = id['id'] end request(:GET, "/blueprints/#{id}") end
Get a list of all blueprints. @param filter [Hash] An optional filter. Only blueprints that have
key/value pairs as specified in the filter will be returned.
@return [Array<Hash>] A list of blueprints.
# File lib/ravello-sdk.rb, line 395 def get_blueprints(filter={}) result = request(:GET, '/blueprints') result.select! do |bp| match_filter(bp, filter) end if !filter.empty? result end
Get an image. @param id [Integer, Hash] The ID of the image. This may also be a
Hash, in which case it must have an "id" key.
@return [Hash, nil] The image as a Hash, or nil in case the image
does not exist.
# File lib/ravello-sdk.rb, line 422 def get_image(id) if id.is_a? Hash then id = id['id'] end request(:GET, "/images/#{id}") end
Get a list of all images. @param filter [Hash] An optional filter. Only images that have
key/value pairs as specified in the filter will be returned.
@return [Array<Hash>] A list of images.
# File lib/ravello-sdk.rb, line 431 def get_images(filter={}) result = request(:GET, '/images') result.select! do |img| match_filter(img, filter) end if !filter.empty? result end
Get a keypair. @param id [Integer, Hash] The ID of the keypair. This parameter may also
be a Hash, in which case it must have an "id" key.
@return [Hash, nil] The keypair as a Hash, or nil in case the keypair
does not exist.
# File lib/ravello-sdk.rb, line 458 def get_keypair(id) if id.is_a? Hash then id = id['id'] end request(:GET, "/keypairs/#{id}") end
Get a list of all keypairs. @param filter [Hash] An optional filter. Only keypairs that have
key/value pairs as specified in the filter will be returned.
@return [Array<Hash>] A list of keypairs.
# File lib/ravello-sdk.rb, line 467 def get_keypairs(filter={}) result = request(:GET, '/keypairs') result.select! do |kp| match_filter(kp, filter) end if !filter.empty? result end
# File lib/ravello-sdk.rb, line 502 def inspect "#<RavelloClient:#{object_id}>" end
@return [Boolean] Whether the client is logged in.
# File lib/ravello-sdk.rb, line 178 def logged_in? !@cookies.nil? end
Authenticate to the API. Authentication is required for most operations. @param username The API username. If not provided, use the username
specified in the constructor.
@param password The API password If not provided, use the password
specified in the constructor.
# File lib/ravello-sdk.rb, line 195 def login(username=nil, password=nil) raise "already logged in" if logged_in? @username = username unless username.nil? @password = password unless password.nil? raise "username and password must be set" unless have_credentials? do_login end
Logout. The connection is maintained.
# File lib/ravello-sdk.rb, line 204 def logout do_logout if logged_in? end
Publish an application @param id [Numeric, Hash] The ID of the application to publish. This
parameter may also be a Hash with a numeric "id" key.
@param req [Hash] The publish request. @return [Hash] The publish response.
# File lib/ravello-sdk.rb, line 293 def publish_application(id, req={}) if id.is_a? Hash then id = id['id'] end request(:POST, "/applications/#{id}/publish", req) end
Publish application updates. @param id [Numeric, Hash] The ID of the application to publish updates
for. This parameter may also be a Hash with a numeric "id" key.
@return [Hash] The publish updates response.
# File lib/ravello-sdk.rb, line 329 def publish_application_updates(id) if id.is_a? Hash then id = id['id'] end request(:POST, "/applications/#{id}/publishUpdates") end
Issue a call to the API. This is a low-level method that you should only use in case the call you want is not mapped as a method. @param method [Symbol] The HTTP method, e.g. :GET or :POST. @param path [String] The path relative to the API root. @param entity [Hash] A JSON serializable hash that is specific to
the method and path.
@return [Hash,Array<Hash>] The parsed JSON response.
# File lib/ravello-sdk.rb, line 222 def request(method, path, entity=nil) body = JSON.generate(entity) unless entity.nil? response = make_request(method, path, body) response.entity end
Restart an application. @param id [Numeric, Hash] The ID of the application to restart. This
parameter may also be a Hash with a numeric "id" key.
@return [Hash] The restart response.
# File lib/ravello-sdk.rb, line 320 def restart_application(id) if id.is_a? Hash then id = id['id'] end request(:POST, "/applications/#{id}/restart") end
Restart a virtual machine @param appid [Numeric, Hash] The ID of the application that contains the
VM. This parameter may also be a Hash with an "id" key.
@param vmid [Numeric, Hash] The ID of the VM to restart. This parameter
may also be a Hash with an "id" key.
@return [Hash] The restart VM response.
# File lib/ravello-sdk.rb, line 375 def restart_vm(appid, vmid) if appid.is_a? Hash then appid = appid['id'] end if vmid.is_a? Hash then vmid = vmid['id'] end request(:POST, "/applications/#{appid}/vms/#{vmid}/restart") end
Set the application expiration time. @param id [Numeric, Hash] The ID of the application to set the
expiration time of. This parameter may also be a Hash with a numeric "id" key.
@param req [Hash] The set expiration time request. @return [Hash] The set expiration time response.
# File lib/ravello-sdk.rb, line 340 def set_application_expiration(id, req) if id.is_a? Hash then id = id['id'] end request(:POST, "/applications/#{id}/setExpiration", req) end
Start an application. @param id [Numeric, Hash] The ID of the application to start. This
parameter may also be a Hash with a numeric "id" key.
@return [Hash] The start response.
# File lib/ravello-sdk.rb, line 302 def start_application(id) if id.is_a? Hash then id = id['id'] end request(:POST, "/applications/#{id}/start") end
Start a virtual machine @param appid [Numeric, Hash] The ID of the application that contains the
VM. This parameter may also be a Hash with an "id" key.
@param vmid [Numeric, Hash] The ID of the VM to start. This parameter
may also be a Hash with an "id" key.
@return [Hash] The start VM response.
# File lib/ravello-sdk.rb, line 351 def start_vm(appid, vmid) if appid.is_a? Hash then appid = appid['id'] end if vmid.is_a? Hash then vmid = vmid['id'] end request(:POST, "/applications/#{appid}/vms/#{vmid}/start") end
Stop an application. @param id [Numeric, Hash] The ID of the application to stop. This
parameter may also be a Hash with a numeric "id" key.
@return [Hash] The stop response.
# File lib/ravello-sdk.rb, line 311 def stop_application(id) if id.is_a? Hash then id = id['id'] end request(:POST, "/applications/#{id}/stop") end
Stop a virtual machine @param appid [Numeric, Hash] The ID of the application that contains the
VM. This parameter may also be a Hash with an "id" key.
@param vmid [Numeric, Hash] The ID of the VM to stop. This parameter
may also be a Hash with an "id" key.
@return [Hash] The stop VM response.
# File lib/ravello-sdk.rb, line 363 def stop_vm(appid, vmid) if appid.is_a? Hash then appid = appid['id'] end if vmid.is_a? Hash then vmid = vmid['id'] end request(:POST, "/applications/#{appid}/vms/#{vmid}/stop") end
Update an existing application @param app [Hash] The application to update. @return [Hash] The updated application.
# File lib/ravello-sdk.rb, line 275 def update_application(app) request(:PUT, "/applications/#{app['id']}", app) end
Update an image. @param image [Hash] The image to update. @return [Hash] The updated image.
# File lib/ravello-sdk.rb, line 440 def update_image(image) request(:PUT, "/images/#{image['id']}", image) end
Update a keypair. @param keypair [Hash] The keypair to update. @return [Hash] The updated keypair.
# File lib/ravello-sdk.rb, line 483 def update_keypair(keypair) request(:PUT, "/keypairs/#{keypair['id']}", keypair) end
Wait until a condition on the object evaluates to true. @param obj [Hash] The object to wait for. It must be a hash returned by
this client that has a "href" attribute.
@param timeout [Numeric] The number of seconds to wait. @param block [Block] A block that evaluates the condition. The block must
return true if the condition holds, false otherwise.
# File lib/ravello-sdk.rb, line 234 def wait_for(obj, timeout, &block) end_time = Time.now + timeout raise 'object requires a "href" key' if !obj.key? 'href' href = obj['href'] while end_time >= Time.now do break if yield request(:GET, href) sleep 5 end raise 'timeout' if end_time < Time.now end
Private Instance Methods
# File lib/ravello-sdk.rb, line 519 def do_connect() @connection = Net::HTTP.new(url.host, url.port) @connection.use_ssl = true @connection.set_debug_output $stderr if $DEBUG @connection.start @logger.debug("connected to #{url.host}:#{url.port}") end
# File lib/ravello-sdk.rb, line 527 def do_login() raise "should have credentials" if not have_credentials? @logger.debug('performing a username/password login') @autologin = false auth = Base64.encode64("#{@username}:#{@password}").gsub(/\s/, '') headers = [['Authorization', "Basic #{auth}"]] response = make_request(:POST, '/login', nil, headers) cookies = response.get_fields('Set-Cookie') @cookies = cookies.map{ |v| v.split(';')[0]}.join(', ') @autologin = true @user_info = response.entity end
# File lib/ravello-sdk.rb, line 540 def do_logout make_request(:POST, '/logout') @cookies = nil end
# File lib/ravello-sdk.rb, line 574 def do_request(request) retries = redirects = 0 while retries < @retries and redirects < @redirects do do_connect if !connected? do_login if @autologin && have_credentials? && !logged_in? begin @logger.info("request: #{request.method} #{request.path}") response = @connection.request(request) @logger.info("response: #{response.code} #{response.message}") case response.code.to_i when 200..299 body = response.read_body ctype = response.content_type entity = JSON.parse(body) if ctype == 'application/json' if entity.is_a?(Hash) && !entity['id'].nil? then if response.key? 'Content-Location' href = URI.parse(response['Content-Location']).path elsif response.key? 'Location' href = URI.parse(response['Location']).path elsif request.method == 'POST' # missing location header with e.g. /pubkeys href = "#{request.uri.path}/#{entity['id']}" else href = request.uri.path end entity['href'] = href.sub(/^#{@url.path}/, '') end response.entity = entity when 300..399 location = response['Location'] raise "no location for #{response.code} response" if location.nil? url = URI.parse(location) raise "will not redirect to #{url.to_s}" if url.route_from(@url).host request.url = url response.entity = nil redirects += 1 next when 404 response.entity = nil break else message = "#{response.code} #{response.message.dump} " \ "[#{response.fetch('ERROR-CODE', 'unknown')}: " \ "#{response.fetch('ERROR-MESSAGE', 'unknown')}]" raise response.error_type.new(message, response) end rescue Timeout::Error => e @logger.debug("timeout: #{e.message}") close retries += 1 next if idempotent request raise "not retrying #{request.method.to_s} request" end break end raise "maximum number of retries reached" if retries == @retries raise "maximum number of redirects reached" if redirects == @redirects response end
# File lib/ravello-sdk.rb, line 515 def have_credentials? !@username.nil? && !@password.nil? end
# File lib/ravello-sdk.rb, line 570 def idempotent(request) [:GET, :HEAD, :PUT].include? request.method end
# File lib/ravello-sdk.rb, line 545 def make_request(method, path, body=nil, headers=nil) uri = @url.merge(@url.path + path) case method when :GET request = Net::HTTP::Get.new uri when :POST request = Net::HTTP::Post.new uri when :DELETE request = Net::HTTP::Delete.new uri when :PUT request = Net::HTTP::Put.new uri when :PATCH request = New::HTTP::Patch.new uri else raise ArgumentError, "unknown method: #{method.to_s}" end request['Accept'] = 'application/json' request['Cookie'] = @cookies unless @cookies.nil? request['Accept-Encoding'] = 'identity' if $DEBUG headers.each { |kv| request.add_field(kv[0], kv[1]) } unless headers.nil? request.body = body request.content_type = 'application/json' do_request(request) end
# File lib/ravello-sdk.rb, line 634 def match_filter(obj, filter) filter.each_pair do |fkey,fvalue| obval = obj[fkey.to_s] if obval.nil? return false elsif fvalue.is_a? Hash return false if !obval.is_a? Hash || !match_filter(obval, fvalue) elsif fvalue != obval return false end end true end
# File lib/ravello-sdk.rb, line 508 def set_url(url) raise 'cannot change URL when connected' if connected? parsed = URI.parse(url.chomp '/') raise ArgumentError, 'https is required' if parsed.scheme != "https" @url = parsed end