1
0
Fork 0
mirror of https://github.com/fog/fog.git synced 2022-11-09 13:51:43 -05:00
fog--fog/lib/fog/openstack/core.rb
Paul Thornthwaite 7ee3535d99 [core] Replace Fog::Connection with stable version
Fog::Connection mixed in XML parsing via the `parser` argument which
wasn't much use for the majority of APIs using JSON.

This adds the deprecation warning and attempts to update providers to
the correct version of Connection that they need.

Either the cleaner `Fog::Core::Connection` or if reliant on the XML
parsing still `Fog::XML::SAXParserConnection`

The SAX parser will be moving to `fog/xml` fairly soon.
2014-02-25 21:42:34 +00:00

250 lines
8.6 KiB
Ruby

require 'fog/core'
require 'fog/json'
module Fog
module OpenStack
extend Fog::Provider
module Errors
class ServiceError < Fog::Errors::Error
attr_reader :response_data
def self.slurp(error)
if error.response.body.empty?
data = nil
message = nil
else
data = Fog::JSON.decode(error.response.body)
message = data['message']
if message.nil? and !data.values.first.nil?
message = data.values.first['message']
end
end
new_error = super(error, message)
new_error.instance_variable_set(:@response_data, data)
new_error
end
end
class ServiceUnavailable < ServiceError; end
class BadRequest < ServiceError
attr_reader :validation_errors
def self.slurp(error)
new_error = super(error)
unless new_error.response_data.nil? or new_error.response_data['badRequest'].nil?
new_error.instance_variable_set(:@validation_errors, new_error.response_data['badRequest']['validationErrors'])
end
new_error
end
end
end
service(:compute , 'Compute')
service(:image, 'Image')
service(:identity, 'Identity')
service(:network, 'Network')
service(:storage, 'Storage')
service(:volume, 'Volume')
service(:metering, 'Metering')
service(:orchestration, 'Orchestration')
def self.authenticate(options, connection_options = {})
case options[:openstack_auth_uri].path
when /v1(\.\d+)?/
authenticate_v1(options, connection_options)
else
authenticate_v2(options, connection_options)
end
end
# legacy v1.0 style auth
def self.authenticate_v1(options, connection_options = {})
uri = options[:openstack_auth_uri]
connection = Fog::Core::Connection.new(uri.to_s, false, connection_options)
@openstack_api_key = options[:openstack_api_key]
@openstack_username = options[:openstack_username]
response = connection.request({
:expects => [200, 204],
:headers => {
'X-Auth-Key' => @openstack_api_key,
'X-Auth-User' => @openstack_username
},
:method => 'GET',
:path => (uri.path and not uri.path.empty?) ? uri.path : 'v1.0'
})
return {
:token => response.headers['X-Auth-Token'],
:server_management_url => response.headers['X-Server-Management-Url'] || response.headers['X-Storage-Url'],
:identity_public_endpoint => response.headers['X-Keystone']
}
end
# Keystone Style Auth
def self.authenticate_v2(options, connection_options = {})
uri = options[:openstack_auth_uri]
tenant_name = options[:openstack_tenant]
service_type = options[:openstack_service_type]
service_name = options[:openstack_service_name]
identity_service_type = options[:openstack_identity_service_type]
endpoint_type = (options[:openstack_endpoint_type] || 'publicURL').to_s
openstack_region = options[:openstack_region]
body = retrieve_tokens_v2(options, connection_options)
service = get_service(body, service_type, service_name)
options[:unscoped_token] = body['access']['token']['id']
unless service
unless tenant_name
response = Fog::Core::Connection.new(
"#{uri.scheme}://#{uri.host}:#{uri.port}/v2.0/tenants", false, connection_options).request({
:expects => [200, 204],
:headers => {'Content-Type' => 'application/json',
'Accept' => 'application/json',
'X-Auth-Token' => body['access']['token']['id']},
:method => 'GET'
})
body = Fog::JSON.decode(response.body)
if body['tenants'].empty?
raise Fog::Errors::NotFound.new('No Tenant Found')
else
options[:openstack_tenant] = body['tenants'].first['name']
end
end
body = retrieve_tokens_v2(options, connection_options)
service = get_service(body, service_type, service_name)
end
service['endpoints'] = service['endpoints'].select do |endpoint|
endpoint['region'] == openstack_region
end if openstack_region
if service['endpoints'].empty?
raise Fog::Errors::NotFound.new("No endpoints available for region '#{openstack_region}'")
end if openstack_region
unless service
available = body['access']['serviceCatalog'].map { |endpoint|
endpoint['type']
}.sort.join ', '
missing = service_type.join ', '
message = "Could not find service #{missing}. Have #{available}"
raise Fog::Errors::NotFound, message
end
if service['endpoints'].count > 1
regions = service["endpoints"].map{ |e| e['region'] }.uniq.join(',')
raise Fog::Errors::NotFound.new("Multiple regions available choose one of these '#{regions}'")
end
identity_service = get_service(body, identity_service_type) if identity_service_type
tenant = body['access']['token']['tenant']
user = body['access']['user']
management_url = service['endpoints'].detect{|s| s[endpoint_type]}[endpoint_type]
identity_url = identity_service['endpoints'].detect{|s| s['publicURL']}['publicURL'] if identity_service
{
:user => user,
:tenant => tenant,
:identity_public_endpoint => identity_url,
:server_management_url => management_url,
:token => body['access']['token']['id'],
:expires => body['access']['token']['expires'],
:current_user_id => body['access']['user']['id'],
:unscoped_token => options[:unscoped_token]
}
end
def self.get_service(body, service_type=[], service_name=nil)
body['access']['serviceCatalog'].detect do |s|
if service_name.nil? or service_name.empty?
service_type.include?(s['type'])
else
service_type.include?(s['type']) and s['name'] == service_name
end
end
end
def self.retrieve_tokens_v2(options, connection_options = {})
api_key = options[:openstack_api_key].to_s
username = options[:openstack_username].to_s
tenant_name = options[:openstack_tenant].to_s
auth_token = options[:openstack_auth_token] || options[:unscoped_token]
uri = options[:openstack_auth_uri]
connection = Fog::Core::Connection.new(uri.to_s, false, connection_options)
request_body = {:auth => Hash.new}
if auth_token
request_body[:auth][:token] = {
:id => auth_token
}
else
request_body[:auth][:passwordCredentials] = {
:username => username,
:password => api_key
}
end
request_body[:auth][:tenantName] = tenant_name if tenant_name
response = connection.request({
:expects => [200, 204],
:headers => {'Content-Type' => 'application/json'},
:body => Fog::JSON.encode(request_body),
:method => 'POST',
:path => (uri.path and not uri.path.empty?) ? uri.path : 'v2.0'
})
Fog::JSON.decode(response.body)
end
def self.get_supported_version(supported_versions, uri, auth_token, connection_options = {})
connection = Fog::Core::Connection.new("#{uri.scheme}://#{uri.host}:#{uri.port}", false, connection_options)
response = connection.request({
:expects => [200, 204, 300],
:headers => {'Content-Type' => 'application/json',
'Accept' => 'application/json',
'X-Auth-Token' => auth_token},
:method => 'GET'
})
body = Fog::JSON.decode(response.body)
version = nil
unless body['versions'].empty?
supported_version = body['versions'].detect do |x|
x["id"].match(supported_versions) &&
(x["status"] == "CURRENT" || x["status"] == "SUPPORTED")
end
version = supported_version["id"] if supported_version
end
if version.nil?
raise Fog::OpenStack::Errors::ServiceUnavailable.new(
"OpenStack service only supports API versions #{supported_versions.inspect}")
end
version
end
# CGI.escape, but without special treatment on spaces
def self.escape(str,extra_exclude_chars = '')
str.gsub(/([^a-zA-Z0-9_.-#{extra_exclude_chars}]+)/) do
'%' + $1.unpack('H2' * $1.bytesize).join('%').upcase
end
end
end
end