5 # <b>Request Options</b>
7 # The following options may be used in combination with each other when calling
8 # an OpenStack request class
10 # :api_version => :v2::
11 # Forces Aviator to use the request class for the v2 API. For any other
12 # version, replace :v2 with the desired one. Note that this may throw an
13 # error if no such request class for the given api version exists. If you
14 # want to globally specify the API version to use for a specific service,
15 # declare it in your config file under the correct environment. For example:
23 # Note that the <tt>:api_version</tt> option overrides whatever is declared in the
26 # :endpoint_type => (:public|:admin)::
27 # This allows you to be specific about the endpoint type in cases where two
28 # request classes under admin and public endpoints of the same service share
29 # the same name. This is true, for example, for the :list_tenants request of
30 # the identity service's v2 API. Its public endpoint will return only the tenants
31 # the user is a member of whereas the admin endpoint will return all tenants
34 # :session_data => Hash::
35 # Under normal situations, you wouldn't need to use this as it is automatically populated
36 # by the Session object provided it is authenticated. The specific use case when you'd
37 # need to set thsi optin is when you want to use Aviator to seed your OpenStack installation.
38 # In such a scenario, you would need to use a service token since no usernames and tenants
39 # would exist yet in the environment. To use a service token with Aviator, you will need to
40 # write something similar to the following example:
42 # openstack = Aviator::Session.new(:config => { :provider => 'openstack'})
44 # session_data = {:base_url => 'http://example.com',
45 # :service_token => 'service-token-created-at-openstack-install-time'}
47 # openstack.request :identity, :create_tenant, :api_version => :v2, :session_data => session_data) do |params|
48 # params.name = 'Tenant A'
49 # params.description = 'First Tenant!'
50 # params.enabled = true
53 # Notice how the above code skips authentication. This is because the service token is
54 # pre-validated and ready for use with any request. Also note how we're providing a <tt>:base_url</tt>
55 # member in our session data. This is necessary since we normally get the service endpoints from
56 # Keystone when we authenticate. Now since we are not authenticating against Keystone, we don't have
57 # that catalogue to begin with. Thus the need to hardcode it in the request.
61 class MultipleServiceApisError < StandardError
62 def initialize(service, entries, request_name)
63 types = entries.map{|e| e[:type] }.join("\n - ")
65 Multiple entries for the #{ service } service were found in the api catalog:
69 I'm unable to guess which one it is you want to use. To fix this problem, you'll need to
72 1) Indicate in the config file the api version you want to use:
80 2) Indicate the api version when you call the request:
82 session.#{ service }_service.request :#{ request_name }, :api_version => :v2 { ... }
84 If you combine the two methods, method #2 will override method #1
94 def find_request(service, name, session_data, options)
95 service = service.to_s
96 endpoint_type = options[:endpoint_type]
97 endpoint_types = if endpoint_type
98 [endpoint_type.to_s.camelize]
103 namespace = Aviator.const_get('Openstack') \
104 .const_get(service.camelize) \
105 .const_get('Requests')
107 if options[:api_version]
108 m = options[:api_version].to_s.match(/(v\d+)\.?\d*/)
109 version = m[1].to_s.camelize unless m.nil?
112 version ||= infer_version(session_data, name, service)
115 version = version.to_s.camelize
118 return nil unless version && namespace.const_defined?(version)
120 namespace = namespace.const_get(version, name)
122 endpoint_types.each do |endpoint_type|
123 name = name.to_s.camelize
125 next unless namespace.const_defined?(endpoint_type)
126 next unless namespace.const_get(endpoint_type).const_defined?(name)
128 return namespace.const_get(endpoint_type).const_get(name)
136 Pathname.new(__FILE__).join('..').expand_path
140 def request_file_paths(service)
141 Dir.glob(Pathname.new(__FILE__).join(
154 def infer_version(session_data, request_name, service)
155 if session_data.has_key?(:auth_service) && session_data[:auth_service][:api_version]
156 session_data[:auth_service][:api_version].to_sym
158 elsif session_data.has_key?(:auth_service) && session_data[:auth_service][:host_uri]
159 m = session_data[:auth_service][:host_uri].match(/(v\d+)\.?\d*/)
160 return m[1].to_sym unless m.nil?
162 elsif session_data.has_key? :base_url
163 m = session_data[:base_url].match(/(v\d+)\.?\d*/)
164 return m[1].to_sym unless m.nil?
166 elsif session_data.has_key?(:body) && session_data[:body].has_key?(:access)
167 service_specs = session_data[:body][:access][:serviceCatalog].select{|s| s[:type].match("#{ service }(v\d+)?") }
168 raise MultipleServiceApisError.new(service, service_specs, request_name) unless service_specs.length <= 1
169 raise Aviator::Service::MissingServiceEndpointError.new(service.to_s, request_name) unless service_specs.length > 0
170 version = service_specs[0][:endpoints][0][:publicURL].match(/(v\d+)\.?\d*/)
171 version ? version[1].to_sym : :v1
173 elsif session_data.has_key?(:headers) && session_data[:headers].has_key?("x-subject-token")
174 service_specs = session_data[:body][:token][:catalog].select{|s| s[:type].match("#{ service }(v\d+)?") }
175 raise MultipleServiceApisError.new(service, service_specs, request_name) unless service_specs.length <= 1
176 raise Aviator::Service::MissingServiceEndpointError.new(service.to_s, request_name) unless service_specs.length > 0
177 version = service_specs[0][:endpoints][0][:url].match(/(v\d+)\.?\d*/)
178 version ? version[1].to_sym : :v1
184 end # module Provider
186 end # module Openstack