You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@deltacloud.apache.org by lu...@apache.org on 2010/07/09 01:48:44 UTC
svn commit: r962352 - in /incubator/deltacloud/trunk/client: ./ lib/
lib/dcloud/
Author: lutter
Date: Thu Jul 8 23:48:43 2010
New Revision: 962352
URL: http://svn.apache.org/viewvc?rev=962352&view=rev
Log:
Replaced old client code with new client code.
Removed:
incubator/deltacloud/trunk/client/credentials.yml
incubator/deltacloud/trunk/client/lib/dcloud/base_model.rb
incubator/deltacloud/trunk/client/lib/dcloud/hardware_profile.rb
incubator/deltacloud/trunk/client/lib/dcloud/image.rb
incubator/deltacloud/trunk/client/lib/dcloud/instance.rb
incubator/deltacloud/trunk/client/lib/dcloud/realm.rb
incubator/deltacloud/trunk/client/lib/dcloud/state.rb
incubator/deltacloud/trunk/client/lib/dcloud/storage_snapshot.rb
incubator/deltacloud/trunk/client/lib/dcloud/storage_volume.rb
incubator/deltacloud/trunk/client/lib/dcloud/transition.rb
Modified:
incubator/deltacloud/trunk/client/lib/deltacloud.rb
Modified: incubator/deltacloud/trunk/client/lib/deltacloud.rb
URL: http://svn.apache.org/viewvc/incubator/deltacloud/trunk/client/lib/deltacloud.rb?rev=962352&r1=962351&r2=962352&view=diff
==============================================================================
--- incubator/deltacloud/trunk/client/lib/deltacloud.rb (original)
+++ incubator/deltacloud/trunk/client/lib/deltacloud.rb Thu Jul 8 23:48:43 2010
@@ -15,404 +15,494 @@
# License along with this library; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+require 'nokogiri'
require 'rest_client'
-require 'rexml/document'
-require 'logger'
-require 'dcloud/hardware_profile'
-require 'dcloud/realm'
-require 'dcloud/image'
-require 'dcloud/instance'
-require 'dcloud/storage_volume'
-require 'dcloud/storage_snapshot'
-require 'dcloud/state'
-require 'dcloud/transition'
require 'base64'
+require 'logger'
-class DeltaCloud
-
- attr_accessor :logger
- attr_reader :api_uri
- attr_reader :entry_points
- attr_reader :driver_name
- attr_reader :last_request_xml
- attr_reader :features
-
- def self.driver_name(url)
- DeltaCloud.new( nil, nil, url) do |client|
- return client.driver_name
- end
- end
-
- def initialize(name, password, api_uri, opts={}, &block)
- @logger = Logger.new( STDERR )
- @name = name
- @password = password
- @api_uri = URI.parse( api_uri )
- @entry_points = {}
- @verbose = opts[:verbose]
- @features = {}
- discover_entry_points
- connect( &block )
- self
- end
-
+module DeltaCloud
- def connect(&block)
- @http = RestClient::Resource.new( api_uri.to_s , :accept => 'application/xml' )
- discover_entry_points
- block.call( self ) if block
- self
+ # Get a new API client instance
+ #
+ # @param [String, user_name] API user name
+ # @param [String, password] API password
+ # @param [String, user_name] API URL (eg. http://localhost:3001/api)
+ # @return [DeltaCloud::API]
+ def self.new(user_name, password, api_url, &block)
+ API.new(user_name, password, api_url, &block)
end
- def api_host
- @api_uri.host
+ # Return a API driver for specified URL
+ #
+ # @param [String, url] API URL (eg. http://localhost:3001/api)
+ def self.driver_name(url)
+ API.new(nil, nil, url).driver_name
end
- def api_port
- @api_uri.port
- end
+ class API
+ attr_accessor :logger
+ attr_reader :api_uri, :driver_name, :api_version, :features, :entry_points
+ attr_reader :classes
+
+ def initialize(user_name, password, api_url, opts={}, &block)
+ @logger = opts[:verbose] ? Logger.new(STDERR) : []
+ @username, @password = user_name, password
+ @api_uri = URI.parse(api_url)
+ @features, @entry_points = {}, {}
+ @classes = []
+ @verbose = opts[:verbose] || false
+ discover_entry_points
+ yield self if block_given?
+ end
+
+ def connect(&block)
+ yield self
+ end
+
+ # Return API hostname
+ def api_host; @api_uri.host ; end
+
+ # Return API port
+ def api_port; @api_uri.port ; end
+
+ # Return API path
+ def api_path; @api_uri.path ; end
+
+ # Define methods based on 'rel' attribute in entry point
+ # Two methods are declared: 'images' and 'image'
+ def declare_entry_points_methods(entry_points)
+ logger = @logger
+ API.instance_eval do
+ entry_points.keys.select {|k| [:instance_states].include?(k)==false }.each do |model|
+ define_method model do |*args|
+ request(:get, "/#{model}", args.first) do |response|
+ # Define a new class based on model name
+ c = Kernel.define_class("#{model.to_s.classify}")
+ # Create collection from index operation
+ base_object_collection(c, model, response)
+ end
+ end
+ logger << "[API] Added method #{model}\n"
+ define_method :"#{model.to_s.singularize}" do |*args|
+ request(:get, "/#{model}/#{args[0]}") do |response|
+ # Define a new class based on model name
+ c = Kernel.define_class("#{model.to_s.classify}")
+ # Build class for returned object
+ base_object(c, model, response)
+ end
+ end
+ logger << "[API] Added method #{model.to_s.singularize}\n"
+ define_method :"fetch_#{model.to_s.singularize}" do |url|
+ id = url.grep(/\/#{model}\/(.*)$/)
+ self.send(model.to_s.singularize.to_sym, $1)
+ end
+ end
+ end
+ end
- def api_path
- @api_uri.path
- end
+ def base_object_collection(c, model, response)
+ collection = []
+ Nokogiri::XML(response).xpath("#{model}/#{model.to_s.singularize}").each do |item|
+ c.instance_eval do
+ attr_accessor :id
+ attr_accessor :uri
+ end
+ collection << xml_to_class(c, item)
+ end
+ return collection
+ end
- def feature?(collection, name)
- @features.has_key?(collection) && @features[collection].include?(name)
- end
+ # Add default attributes [id and href] to class
+ def base_object(c, model, response)
+ obj = nil
+ Nokogiri::XML(response).xpath("#{model.to_s.singularize}").each do |item|
+ c.instance_eval do
+ attr_accessor :id
+ attr_accessor :uri
+ end
+ obj = xml_to_class(c, item)
+ end
+ return obj
+ end
- def hardware_profiles(opts={})
- hardware_profiles = []
- request(entry_points[:hardware_profiles], :get, opts) do |response|
- doc = REXML::Document.new( response )
- doc.get_elements( 'hardware-profiles/hardware-profile' ).each do |hwp|
- uri = hwp.attributes['href']
- hardware_profiles << DCloud::HardwareProfile.new( self, uri, hwp )
+ # Convert XML response to defined Ruby Class
+ def xml_to_class(c, item)
+ obj = c.new
+ # Set default attributes
+ obj.id = item['id']
+ api = self
+ c.instance_eval do
+ define_method :client do
+ api
+ end
end
+ obj.uri = item['href']
+ logger = @logger
+ logger << "[DC] Creating class #{obj.class.name}\n"
+ obj.instance_eval do
+ # Declare methods for all attributes in object
+ item.xpath('./*').each do |attribute|
+ # If attribute is a link to another object then
+ # create a method which request this object from API
+ if api.entry_points.keys.include?(:"#{attribute.name}s")
+ c.instance_eval do
+ define_method :"#{attribute.name.sanitize}" do
+ client.send(:"#{attribute.name}", attribute['id'] )
+ end
+ logger << "[DC] Added #{attribute.name} to class #{obj.class.name}\n"
+ end
+ else
+ # Define methods for other attributes
+ c.instance_eval do
+ case attribute.name
+ # When response cointains 'link' block, declare
+ # methods to call links inside. This is used for instance
+ # to dynamicaly create .stop!, .start! methods
+ when "actions":
+ actions = []
+ attribute.xpath('link').each do |link|
+ actions << [link['rel'], link[:href]]
+ define_method :"#{link['rel'].sanitize}!" do
+ client.request(:"#{link['method']}", link['href'], {}, {})
+ client.send(:"#{item.name}", item['id'])
+ end
+ end
+ define_method :actions do
+ actions.collect { |a| a.first }
+ end
+ define_method :actions_urls do
+ urls = {}
+ actions.each { |a| urls[a.first] = a.last }
+ urls
+ end
+ # Property attribute is handled differently
+ when "property":
+ define_method :"#{attribute['name'].sanitize}" do
+ if attribute['value'] =~ /^(\d+)$/
+ DeltaCloud::HWP::FloatProperty.new(attribute, attribute['name'])
+ else
+ DeltaCloud::HWP::Property.new(attribute, attribute['name'])
+ end
+ end
+ # Public and private addresses are returned as Array
+ when "public_addresses", "private_addresses":
+ define_method :"#{attribute.name.sanitize}" do
+ attribute.xpath('address').collect { |address| address.text }
+ end
+ # Value for other attributes are just returned using
+ # method with same name as attribute (eg. .owner_id, .state)
+ else
+ define_method :"#{attribute.name.sanitize}" do
+ attribute.text.convert
+ end
+ logger << "[DC] Added method #{attribute.name} to #{obj.class.name}\n"
+ end
+ end
+ end
+ end
+ end
+ add_class_record(obj)
+ return obj
end
- hardware_profiles
- end
- def hardware_profile(id)
- request( entry_points[:hardware_profiles], :get, {:id=>id } ) do |response|
- doc = REXML::Document.new( response )
- doc.get_elements( '/hardware-profile' ).each do |hwp|
- uri = hwp.attributes['href']
- return DCloud::HardwareProfile.new( self, uri, hwp )
+ # Get /api and parse entry points
+ def discover_entry_points
+ return if discovered?
+ request(:get, @api_uri.to_s) do |response|
+ api_xml = Nokogiri::XML(response)
+ @driver_name = api_xml.xpath('/api').first['driver']
+ @api_version = api_xml.xpath('/api').first['version']
+ logger << "[API] Version #{@api_version}\n"
+ logger << "[API] Driver #{@driver_name}\n"
+ api_xml.css("api > link").each do |entry_point|
+ rel, href = entry_point['rel'].to_sym, entry_point['href']
+ @entry_points.store(rel, href)
+ logger << "[API] Entry point '#{rel}' added\n"
+ entry_point.css("feature").each do |feature|
+ @features[rel] ||= []
+ @features[rel] << feature['name'].to_sym
+ logger << "[API] Feature #{feature['name']} added to #{rel}\n"
+ end
+ end
end
+ declare_entry_points_methods(@entry_points)
end
- end
- def fetch_hardware_profile(uri)
- xml = fetch_resource( :hardware_profile, uri )
- return DCloud::HardwareProfile.new( self, uri, xml ) if xml
- nil
- end
+ # Create a new instance, using image +image_id+. Possible optiosn are
+ #
+ # name - a user-defined name for the instance
+ # realm - a specific realm for placement of the instance
+ # hardware_profile - either a string giving the name of the
+ # hardware profile or a hash. The hash must have an
+ # entry +id+, giving the id of the hardware profile,
+ # and may contain additional names of properties,
+ # e.g. 'storage', to override entries in the
+ # hardware profile
+ def create_instance(image_id, opts={}, &block)
+ name = opts[:name]
+ realm_id = opts[:realm]
+ user_data = opts[:user_data]
+
+ params = {}
+ ( params[:realm_id] = realm_id ) if realm_id
+ ( params[:name] = name ) if name
+ ( params[:user_data] = user_data ) if user_data
+
+ if opts[:hardware_profile].is_a?(String)
+ params[:hwp_id] = opts[:hardware_profile]
+ elsif opts[:hardware_profile].is_a?(Hash)
+ opts[:hardware_profile].each do |k,v|
+ params[:"hwp_#{k}"] = v
+ end
+ end
+
+ params[:image_id] = image_id
+ instance = nil
- def fetch_resource(type, uri)
- request( uri ) do |response|
- doc = REXML::Document.new( response )
- if ( doc.root && ( doc.root.name == type.to_s.gsub( /_/, '-' ) ) )
- return doc.root
+ request(:post, entry_points[:instances], {}, params) do |response|
+ c = Kernel.define_class("Instance")
+ instance = base_object(c, :instance, response)
+ yield instance if block_given?
+ end
+
+ return instance
+ end
+
+ # Basic request method
+ #
+ def request(*args, &block)
+ conf = {
+ :method => (args[0] || 'get').to_sym,
+ :path => (args[1]=~/^http/) ? args[1] : "#{api_uri.to_s}#{args[1]}",
+ :query_args => args[2] || {},
+ :form_data => args[3] || {}
+ }
+ if conf[:query_args] != {}
+ conf[:path] += '?' + URI.escape(conf[:query_args].collect{ |key, value| "#{key}=#{value}" }.join('&')).to_s
+ end
+ logger << "[#{conf[:method].to_s.upcase}] #{conf[:path]}\n"
+ if conf[:method].eql?(:post)
+ RestClient.send(:post, conf[:path], conf[:form_data], default_headers) do |response|
+ yield response.body if block_given?
+ end
+ else
+ RestClient.send(conf[:method], conf[:path], default_headers) do |response|
+ yield response.body if block_given?
+ end
end
end
- nil
- end
- def fetch_documentation(collection, operation=nil)
- response = @http["docs/#{collection}#{operation ? "/#{operation}" : ''}"].get(:accept => "application/xml")
- doc = REXML::Document.new( response.to_s )
- if operation.nil?
- docs = {
- :name => doc.get_elements('docs/collection').first.attributes['name'],
- :description => doc.get_elements('docs/collection/description').first.text,
- :operations => []
- }
- doc.get_elements('docs/collection/operations/operation').each do |operation|
- p = {}
- p[:name] = operation.attributes['name']
- p[:description] = operation.get_elements('description').first.text
- p[:parameters] = []
- operation.get_elements('parameter').each do |param|
- p[:parameters] << param.attributes['name']
- end
- docs[:operations] << p
- end
- else
- docs = {
- :name => doc.get_elements('docs/operation').attributes['name'],
- :description => doc.get_elements('docs/operation/description').first.text,
- :parameters => []
- }
- doc.get_elements('docs/operation/parameter').each do |param|
- docs[:parameters] << param.attributes['name']
+ # Check if specified collection have wanted feature
+ def feature?(collection, name)
+ @feature.has_key?(collection) && @feature[collection].include?(name)
+ end
+
+ # List available instance states and transitions between them
+ def instance_states
+ states = []
+ request(:get, entry_points[:instance_states]) do |response|
+ Nokogiri::XML(response).xpath('states/state').each do |state_el|
+ state = DeltaCloud::InstanceState::State.new(state_el['name'])
+ state_el.xpath('transition').each do |transition_el|
+ state.transitions << DeltaCloud::InstanceState::Transition.new(
+ transition_el['to'],
+ transition_el['action']
+ )
+ end
+ states << state
+ end
end
+ states
end
- docs
- end
- def instance_states
- states = []
- request( entry_points[:instance_states] ) do |response|
- doc = REXML::Document.new( response )
- doc.get_elements( 'states/state' ).each do |state_elem|
- state = DCloud::State.new( state_elem.attributes['name'] )
- state_elem.get_elements( 'transition' ).each do |transition_elem|
- state.transitions << DCloud::Transition.new(
- transition_elem.attributes['to'],
- transition_elem.attributes['action']
- )
+ # Select instance state specified by name
+ def instance_state(name)
+ instance_states.select { |s| s.name.to_s.eql?(name.to_s) }.first
+ end
+
+ # Skip parsing /api when we already got entry points
+ def discovered?
+ true if @entry_points!={}
+ end
+
+ def documentation(collection, operation=nil)
+ data = {}
+ request(:get, "/docs/#{collection}") do |body|
+ document = Nokogiri::XML(body)
+ if operation
+ data[:description] = document.xpath('/docs/collection/operations/operation[@name = "'+operation+'"]/description').first
+ return false unless data[:description]
+ data[:params] = []
+ (document/"/docs/collection/operations/operation[@name='#{operation}']/parameter").each do |param|
+ data[:params] << {
+ :name => param['name'],
+ :required => param['type'] == 'optional',
+ :type => (param/'class').text
+ }
+ end
+ else
+ data[:description] = (document/'/docs/collection/description').text
end
- states << state
end
+ return Documentation.new(data)
end
- states
- end
- def instance_state(name)
- found = instance_states.find{|e| e.name.to_s == name.to_s}
- found
- end
+ private
- def realms(opts={})
- realms = []
- request( entry_points[:realms], :get, opts ) do |response|
- doc = REXML::Document.new( response )
- doc.get_elements( 'realms/realm' ).each do |realm|
- uri = realm.attributes['href']
- realms << DCloud::Realm.new( self, uri, realm )
- end
+ def default_headers
+ {
+ :authorization => "Basic "+Base64.encode64("#{@username}:#{@password}"),
+ :accept => "application/xml"
+ }
end
- realms
- end
- def realm(id)
- request( entry_points[:realms], :get, {:id=>id } ) do |response|
- doc = REXML::Document.new( response )
- doc.get_elements( 'realm' ).each do |realm|
- uri = realm.attributes['href']
- return DCloud::Realm.new( self, uri, realm )
- end
+ def add_class_record(obj)
+ return if self.classes.include?(obj.class)
+ self.classes << obj.class
end
- nil
- end
- def fetch_realm(uri)
- xml = fetch_resource( :realm, uri )
- return DCloud::Realm.new( self, uri, xml ) if xml
- nil
end
- def images(opts={})
- images = []
- request_path = entry_points[:images]
- request( request_path, :get, opts ) do |response|
- doc = REXML::Document.new( response )
- doc.get_elements( 'images/image' ).each do |image|
- uri = image.attributes['href']
- images << DCloud::Image.new( self, uri, image )
- end
- end
- images
- end
+ class Documentation
+ attr_reader :description
+ attr_reader :params
- def image(id)
- request( entry_points[:images], :get, {:id=>id } ) do |response|
- doc = REXML::Document.new( response )
- doc.get_elements( 'image' ).each do |instance|
- uri = instance.attributes['href']
- return DCloud::Image.new( self, uri, instance )
- end
+ def initialize(opts={})
+ @description = opts[:description]
+ @params = parse_parameters(opts[:params]) if opts[:params]
+ self
end
- nil
- end
- def instances(opts={})
- instances = []
- request( entry_points[:instances], :get, opts ) do |response|
- doc = REXML::Document.new( response )
- doc.get_elements( 'instances/instance' ).each do |instance|
- uri = instance.attributes['href']
- instances << DCloud::Instance.new( self, uri, instance )
+ class OperationParameter
+ attr_reader :name
+ attr_reader :type
+ attr_reader :required
+ attr_reader :description
+
+ def initialize(data)
+ @name, @type, @required, @description = data[:name], data[:type], data[:required], data[:description]
end
- end
- instances
- end
- def instance(id)
- request( entry_points[:instances], :get, {:id=>id } ) do |response|
- doc = REXML::Document.new( response )
- doc.get_elements( 'instance' ).each do |instance|
- uri = instance.attributes['href']
- return DCloud::Instance.new( self, uri, instance )
+ def to_comment
+ " # @param [#{@type}, #{@name}] #{@description}"
end
+
end
- nil
- end
- def post_instance(uri)
- request( uri, :post ) do |response|
- return true
+ private
+
+ def parse_parameters(params)
+ params.collect { |p| OperationParameter.new(p) }
end
- return false
- end
- def fetch_instance(uri)
- xml = fetch_resource( :instance, uri )
- return DCloud::Instance.new( self, uri, xml ) if xml
- nil
end
- # Create a new instance, using image +image_id+. Possible optiosn are
- #
- # name - a user-defined name for the instance
- # realm - a specific realm for placement of the instance
- # hardware_profile - either a string giving the name of the
- # hardware profile or a hash. The hash must have an
- # entry +id+, giving the id of the hardware profile,
- # and may contain additional names of properties,
- # e.g. 'storage', to override entries in the
- # hardware profile
- def create_instance(image_id, opts={})
- name = opts[:name]
- realm_id = opts[:realm]
+ module InstanceState
- params = {}
- ( params[:realm_id] = realm_id ) if realm_id
- ( params[:name] = name ) if name
+ class State
+ attr_reader :name
+ attr_reader :transitions
- if opts[:hardware_profile].is_a?(String)
- params[:hwp_id] = opts[:hardware_profile]
- elsif opts[:hardware_profile].is_a?(Hash)
- opts[:hardware_profile].each do |k,v|
- params[:"hwp_#{k}"] = v
+ def initialize(name)
+ @name, @transitions = name, []
end
end
- params[:image_id] = image_id
- request( entry_points[:instances], :post, {}, params ) do |response|
- doc = REXML::Document.new( response )
- instance = doc.root
- uri = instance.attributes['href']
- return DCloud::Instance.new( self, uri, instance )
- end
- end
+ class Transition
+ attr_reader :to
+ attr_reader :action
- def storage_volumes(opts={})
- storage_volumes = []
- request( entry_points[:storage_volumes] ) do |response|
- doc = REXML::Document.new( response )
- doc.get_elements( 'storage-volumes/storage-volume' ).each do |instance|
- uri = instance.attributes['href']
- storage_volumes << DCloud::StorageVolume.new( self, uri, instance )
+ def initialize(to, action)
+ @to = to
+ @action = action
end
- end
- storage_volumes
- end
- def storage_volume(id)
- request( entry_points[:storage_volumes], :get, {:id=>id } ) do |response|
- doc = REXML::Document.new( response )
- doc.get_elements( 'storage-volume' ).each do |storage_volume|
- uri = storage_volume.attributes['href']
- return DCloud::StorageVolume.new( self, uri, storage_volume )
+ def auto?
+ @action.nil?
end
end
- nil
end
- def fetch_storage_volume(uri)
- xml = fetch_resource( :storage_volume, uri )
- return DCloud::StorageVolume.new( self, uri, xml ) if xml
- nil
- end
+ module HWP
+
+ class Property
+ attr_reader :name, :unit, :value, :kind
- def storage_snapshots(opts={})
- storage_snapshots = []
- request( entry_points[:storage_snapshots] ) do |response|
- doc = REXML::Document.new( response )
- doc.get_elements( 'storage-snapshots/storage-snapshot' ).each do |instance|
- uri = instance.attributes['href']
- storage_snapshots << DCloud::StorageSnapshot.new( self, uri, instance )
+ def initialize(xml, name)
+ @kind, @value, @unit = xml['kind'].to_sym, xml['value'], xml['unit']
+ declare_ranges(xml)
+ self
end
+
+ def present?
+ ! @value.nil?
+ end
+
+ private
+
+ def declare_ranges(xml)
+ case xml['kind']
+ when 'range':
+ self.class.instance_eval do
+ attr_reader :range
+ end
+ @range = { :from => xml.xpath('range').first['first'], :to => xml.xpath('range').first['last'] }
+ when 'enum':
+ self.class.instance_eval do
+ attr_reader :options
+ end
+ @options = xml.xpath('enum/entry').collect { |e| e['value'] }
+ end
+ end
+
end
- storage_snapshots
- end
- def storage_snapshot(id)
- request( entry_points[:storage_snapshots], :get, {:id=>id } ) do |response|
- doc = REXML::Document.new( response )
- doc.get_elements( 'storage-snapshot' ).each do |storage_snapshot|
- uri = storage_snapshot.attributes['href']
- return DCloud::StorageSnapshot.new( self, uri, storage_snapshot )
+ # FloatProperty is like Property but return value is Float instead of String.
+ class FloatProperty < Property
+ def initialize(xml, name)
+ super(xml, name)
+ @value = @value.to_f if @value
end
end
- nil
end
- def fetch_storage_snapshot(uri)
- xml = fetch_resource( :storage_snapshot, uri )
- return DCloud::StorageSnapshot.new( self, uri, xml ) if xml
- nil
+end
+
+class String
+
+ # Create a class name from string
+ def classify
+ self.singularize.camelize
end
- def fetch_image(uri)
- xml = fetch_resource( :image, uri )
- return DCloud::Image.new( self, uri, xml ) if xml
- nil
+ # Camelize converts strings to UpperCamelCase
+ def camelize
+ self.to_s.gsub(/\/(.?)/) { "::#{$1.upcase}" }.gsub(/(?:^|_)(.)/) { $1.upcase }
end
- private
+ # Strip 's' character from end of string
+ def singularize
+ self.gsub(/s$/, '')
+ end
- attr_reader :http
+ # Convert string to float if string value seems like Float
+ def convert
+ return self.to_f if self.strip =~ /^([\d\.]+$)/
+ self
+ end
- def discover_entry_points
- return if @discovered
- request(api_uri.to_s) do |response|
- doc = REXML::Document.new( response )
- @driver_name = doc.root.attributes['driver']
- doc.get_elements( 'api/link' ).each do |link|
- rel = link.attributes['rel'].to_sym
- uri = link.attributes['href']
- @entry_points[rel] = uri
- @features[rel] ||= []
- link.get_elements('feature').each do |feature|
- @features[rel] << feature.attributes['name'].to_sym
- end
- end
- end
- @discovered = true
+ # Simply converts whitespaces and - symbols to '_' which is safe for Ruby
+ def sanitize
+ self.gsub(/(\W+)/, '_')
end
- def request(path='', method=:get, query_args={}, form_data={}, &block)
- if ( path =~ /^http/ )
- request_path = path
- else
- request_path = "#{api_uri.to_s}#{path}"
- end
- if query_args[:id]
- request_path += "/#{query_args[:id]}"
- query_args.delete(:id)
- end
- query_string = URI.escape(query_args.collect{|k,v| "#{k}=#{v}"}.join('&'))
- request_path += "?#{query_string}" unless query_string==''
- headers = {
- :authorization => "Basic "+Base64.encode64("#{@name}:#{@password}"),
- :accept => "application/xml"
- }
+end
- logger << "Request [#{method.to_s.upcase}] #{request_path}]\n" if @verbose
+module Kernel
- if method.eql?(:get)
- RestClient.send(method, request_path, headers) do |response|
- @last_request_xml = response
- yield response.to_s
- end
- else
- RestClient.send(method, request_path, form_data, headers) do |response|
- @last_request_xml = response
- yield response.to_s
- end
- end
+ # Get defined class or declare a new one, when class was not declared before
+ def define_class(name)
+ DeltaCloud.const_get(name) rescue DeltaCloud.const_set(name, Class.new)
end
end