--- /dev/null
+module Aviator
+module Openstack
+
+ #
+ # <b>Request Options</b>
+ #
+ # The following options may be used in combination with each other when calling
+ # an OpenStack request class
+ #
+ # :api_version => :v2::
+ # Forces Aviator to use the request class for the v2 API. For any other
+ # version, replace :v2 with the desired one. Note that this may throw an
+ # error if no such request class for the given api version exists. If you
+ # want to globally specify the API version to use for a specific service,
+ # declare it in your config file under the correct environment. For example:
+ #
+ # production:
+ # provider: openstack
+ # ...
+ # compute_service:
+ # api_version: v2
+ #
+ # Note that the <tt>:api_version</tt> option overrides whatever is declared in the
+ # configuration.
+ #
+ # :endpoint_type => (:public|:admin)::
+ # This allows you to be specific about the endpoint type in cases where two
+ # request classes under admin and public endpoints of the same service share
+ # the same name. This is true, for example, for the :list_tenants request of
+ # the identity service's v2 API. Its public endpoint will return only the tenants
+ # the user is a member of whereas the admin endpoint will return all tenants
+ # in the system.
+ #
+ # :session_data => Hash::
+ # Under normal situations, you wouldn't need to use this as it is automatically populated
+ # by the Session object provided it is authenticated. The specific use case when you'd
+ # need to set thsi optin is when you want to use Aviator to seed your OpenStack installation.
+ # In such a scenario, you would need to use a service token since no usernames and tenants
+ # would exist yet in the environment. To use a service token with Aviator, you will need to
+ # write something similar to the following example:
+ #
+ # openstack = Aviator::Session.new(:config => { :provider => 'openstack'})
+ #
+ # session_data = {:base_url => 'http://example.com',
+ # :service_token => 'service-token-created-at-openstack-install-time'}
+ #
+ # openstack.request :identity, :create_tenant, :api_version => :v2, :session_data => session_data) do |params|
+ # params.name = 'Tenant A'
+ # params.description = 'First Tenant!'
+ # params.enabled = true
+ # end
+ #
+ # Notice how the above code skips authentication. This is because the service token is
+ # pre-validated and ready for use with any request. Also note how we're providing a <tt>:base_url</tt>
+ # member in our session data. This is necessary since we normally get the service endpoints from
+ # Keystone when we authenticate. Now since we are not authenticating against Keystone, we don't have
+ # that catalogue to begin with. Thus the need to hardcode it in the request.
+ #
+ module Provider
+
+ class MultipleServiceApisError < StandardError
+ def initialize(service, entries, request_name)
+ types = entries.map{|e| e[:type] }.join("\n - ")
+ msg = <<EOF
+Multiple entries for the #{ service } service were found in the api catalog:
+
+ - #{ types }
+
+I'm unable to guess which one it is you want to use. To fix this problem, you'll need to
+do one of two things:
+
+ 1) Indicate in the config file the api version you want to use:
+
+ production:
+ provider: openstack
+ ...
+ #{ service }_service:
+ api_version: v2
+
+ 2) Indicate the api version when you call the request:
+
+ session.#{ service }_service.request :#{ request_name }, :api_version => :v2 { ... }
+
+If you combine the two methods, method #2 will override method #1
+
+EOF
+ super(msg)
+ end
+ end
+
+ class << self
+
+ #:nodoc:
+ def find_request(service, name, session_data, options)
+ service = service.to_s
+ endpoint_type = options[:endpoint_type]
+ endpoint_types = if endpoint_type
+ [endpoint_type.to_s.camelize]
+ else
+ ['Public', 'Admin']
+ end
+
+ namespace = Aviator.const_get('Openstack') \
+ .const_get(service.camelize) \
+ .const_get('Requests')
+
+ if options[:api_version]
+ m = options[:api_version].to_s.match(/(v\d+)\.?\d*/)
+ version = m[1].to_s.camelize unless m.nil?
+ end
+
+ version ||= infer_version(session_data, name, service)
+
+ unless version.nil?
+ version = version.to_s.camelize
+ end
+
+ return nil unless version && namespace.const_defined?(version)
+
+ namespace = namespace.const_get(version, name)
+
+ endpoint_types.each do |endpoint_type|
+ name = name.to_s.camelize
+
+ next unless namespace.const_defined?(endpoint_type)
+ next unless namespace.const_get(endpoint_type).const_defined?(name)
+
+ return namespace.const_get(endpoint_type).const_get(name)
+ end
+
+ nil
+ end
+
+
+ def root_dir
+ Pathname.new(__FILE__).join('..').expand_path
+ end
+
+
+ def request_file_paths(service)
+ Dir.glob(Pathname.new(__FILE__).join(
+ '..',
+ service.to_s,
+ 'requests',
+ '**',
+ '*.rb'
+ ).expand_path
+ )
+ end
+
+
+ private
+
+ def infer_version(session_data, request_name, service)
+ if session_data.has_key?(:auth_service) && session_data[:auth_service][:api_version]
+ session_data[:auth_service][:api_version].to_sym
+
+ elsif session_data.has_key?(:auth_service) && session_data[:auth_service][:host_uri]
+ m = session_data[:auth_service][:host_uri].match(/(v\d+)\.?\d*/)
+ return m[1].to_sym unless m.nil?
+
+ elsif session_data.has_key? :base_url
+ m = session_data[:base_url].match(/(v\d+)\.?\d*/)
+ return m[1].to_sym unless m.nil?
+
+ elsif session_data.has_key?(:body) && session_data[:body].has_key?(:access)
+ service_specs = session_data[:body][:access][:serviceCatalog].select{|s| s[:type].match("#{ service }(v\d+)?") }
+ raise MultipleServiceApisError.new(service, service_specs, request_name) unless service_specs.length <= 1
+ raise Aviator::Service::MissingServiceEndpointError.new(service.to_s, request_name) unless service_specs.length > 0
+ version = service_specs[0][:endpoints][0][:publicURL].match(/(v\d+)\.?\d*/)
+ version ? version[1].to_sym : :v1
+
+ elsif session_data.has_key?(:headers) && session_data[:headers].has_key?("x-subject-token")
+ service_specs = session_data[:body][:token][:catalog].select{|s| s[:type].match("#{ service }(v\d+)?") }
+ raise MultipleServiceApisError.new(service, service_specs, request_name) unless service_specs.length <= 1
+ raise Aviator::Service::MissingServiceEndpointError.new(service.to_s, request_name) unless service_specs.length > 0
+ version = service_specs[0][:endpoints][0][:url].match(/(v\d+)\.?\d*/)
+ version ? version[1].to_sym : :v1
+ end
+ end
+
+ end # class << self
+
+ end # module Provider
+
+end # module Openstack
+end # module Aviator