Add new providers

Adds the new base code for talking directly to
OpenStack APIs for the provider resources.

Change-Id: I4c989ddec2d3f14e91d7f4596bf9a0baf809fc57
This commit is contained in:
Tobias Urdin 2019-11-30 16:57:48 +01:00 committed by Tobias Urdin
parent 3d397fc10c
commit ad0718dfd2
6 changed files with 1070 additions and 0 deletions

View File

@ -0,0 +1,95 @@
# Copyright (c) 2019 Binero AB
#
# Author: Tobias Urdin <tobias.urdin@binero.se>
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
require 'puppet'
require 'puppet/resource_api/simple_provider'
class Puppet::ResourceApi::OpenStackProvider < Puppet::ResourceApi::SimpleProvider
# @method set
# Override set so that we can update the context.
# @param context
# The context.
# @param changes
# The changes.
def set(context, changes)
# Name it openstack_changes so that it never collides with any Puppet naming
# if they introduce anything in the future that is named "changes".
class << context
attr_accessor :openstack_changes
# @method get_openstack_change
# Get the change by name from our openstack_changes.
# @param name
# The name of the resource.
# @return hash
# Returns the hash with the :is and :should keys set that
# conains all the data for the resource. All data returned
# in those keys are symbols.
def get_openstack_change(name)
if @openstack_changes.key?(name.to_s)
@openstack_changes[name.to_s]
else
nil
end
end
# @method get_openstack_is_id
# Get the resource ID from the "is" data.
# @param name
# The name of the resource.
# @return string or nil
# The string with the ID or nil.
def get_openstack_is_id(name)
change = get_openstack_change(name)
if change && change.key?(:is) && change[:is].key?(:id)
change[:is][:id]
else
nil
end
end
end
context.openstack_changes = changes
super
end
# @method handle_deprecations
# Handle the custom deprecations field in the type defintion.
# @param context
# The Puppet::ResourceApi::BaseContext context.
def handle_deprecations(context)
if context.type.definition.key?(:deprecations)
deprecations = context.type.definition[:deprecations]
if !deprecations.is_a?(Array)
raise Puppet::ResourceError 'Deprecations must be an array of strings'
end
deprecations.each do |d|
context.warning("The '#{d.to_s}' parameter is deprecated")
end
end
end
# @method get
# The get function for the provider.
# @param context
# The Puppet::ResourceApi::BaseContext context.
def get(context)
handle_deprecations(context)
end
end

View File

@ -0,0 +1,169 @@
# Copyright (c) 2019 Binero AB
#
# Author: Tobias Urdin <tobias.urdin@binero.se>
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
require 'puppet'
require 'puppet_x'
require 'json'
require 'puppet_x/openstack/http_base'
module PuppetX::OpenStack
# Client base class that is used to provide the base
# functionality for all other clients.
class ClientBase < HttpBase
@creds = nil
@@cached_token = nil
@@cached_catalog = nil
# @method initialize
# Initialize the class.
# @param credentials
# The credentials to use.
# @param get_token_on_instance
# Boolean if we should try to get a token on instantiation.
def initialize(credentials, get_token_on_instance: true)
@creds = credentials
# Initialize HttpBase with the auth_url as base url.
# We need to do this first so that we can issue a token.
super(@creds.auth_url)
# Get a token on initialize if it was requested.
token if get_token_on_instance
# If we have a service type lets lookup the endpoint and
# update the base url. This will generate a warning and
# set the base_url to nil if we don't create a token on
# instantiation (see get_token_on_instance) which means
# the client class that uses this base is responsible for
# setting the base url upon initialization.
if @creds.service_type
@base_url = endpoint(@creds.service_type)
end
end
# @method token
# Authenticate for a token and cache it.
# @return string
# An authenticated token.
def token
return @@cached_token if @@cached_token
req = TokenRequest.new(@creds)
body = req.body
response = request('POST', '/v3/auth/tokens', body.to_json)
if response.code.to_i != 201
raise Puppet::ResourceError, "Failed to authenticate, got code #{response.code.to_i} from Keystone"
end
response_body = JSON.parse(response.body)
if response_body['token'] and response_body['token']['catalog']
@@cached_catalog = response_body['token']['catalog']
end
@@cached_token = response['X-Subject-Token']
return @@cached_token
end
# @method revoke
# Revoke the cached token.
# @return boolean
# The boolean is true on success otherwise false.
def revoke
return false if not @@cached_token
headers = {
'X-Auth-Token': @@cached_token,
'X-Subject-Token': @@cached_token,
}
response = request('DELETE', '/v3/auth/tokens', nil, headers)
return response.code.to_i == 204
end
# @method endpoint
# Lookup an endpoint in the cached catalog.
# Note that the authenticated token must have included
# a catalog otherwise it will not be available.
# @param service_type
# The service type to find endpoint for.
# @param region_name
# The region that the service type should be in.
# This parameter can be nil and it will use the region
# name from the credentials. Fallback to RegionOne.
# @param interface
# The interface to use for the endpoint.
# This parameter can be nil and it will use the interface
# from the credentials. Fallback to public interface.
# @return string or nil
# The endpoint found in the catalog or nil.
# @raises Puppet::ResourceError
# When no region name is specified to this function or in
# the credentials.
def endpoint(service_type, region_name=nil, interface=nil)
return nil if not @@cached_catalog
if region_name
lookup_region = region_name
elsif @creds.region_name
lookup_region = @creds.region_name
else
Puppet.debug("Failing back to RegionOne as region when looking up service #{service_type}")
lookup_region = 'RegionOne'
end
if interface
lookup_interface = interface
elsif @creds.interface
lookup_interface = @creds.interface
else
Puppet.debug("Failing back to public interface when looking up service #{service_type} in region #{lookup_region}")
lookup_interface = 'public'
end
@@cached_catalog.each do |service|
next if service['type'] != service_type
service['endpoints'].each do |ep|
if ep['region'] == lookup_region and ep['interface'] == lookup_interface
Puppet.debug("Found endpoint #{ep['url']} for service #{service_type} in region #{lookup_region}")
return ep['url']
end
end
end
Puppet.warning("Did not find a endpoint for service #{service_type} in region #{lookup_region} will return nil")
nil
end
# @method auth_header
# Get hash with headers for authenticated requests.
# @return hash
# A hash with the headers.
# @raises Puppet::ResourceError
# When a token has not been authenticated yet.
def auth_header
if not @@cached_token
raise Puppet::ResourceError, "Cannot generate authenticated headers because no token has been created yet"
end
{ 'X-Auth-Token': @@cached_token }
end
end
end

View File

@ -0,0 +1,232 @@
# Copyright (c) 2019 Binero AB
#
# Author: Tobias Urdin <tobias.urdin@binero.se>
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
require 'puppet'
require 'puppet_x'
require 'puppet/util/inifile'
require 'uri'
module PuppetX::OpenStack
# This class stores credentials that can be reused by all other
# classes that need access to a wide variety of them.
class Credentials
# The credentials that are available, note that these need
# to be the same as named in the keystone_authtoken section.
KEYS = [
:auth_url,
:scope,
:user_id,
:username,
:password,
:project_id,
:project_name,
:domain_id,
:domain_name,
:user_domain_id,
:user_domain_name,
:project_domain_id,
:project_domain_name,
:region_name,
:interface,
:service_type,
]
KEYS.each { |var| attr_accessor var }
# @method initialize
# Initialize the credentials class.
def initialize
# Default to project scope that can then be changed
# if something else is required.
@scope = 'project'
end
# @method from_keystone_authtoken
# Reads the credentials from the keystone_authtoken section in a config file.
# @param file The configuration file to read the section from.
def from_keystone_authtoken(file)
begin
conf = read_config(file)
rescue => e
raise Puppet::ResourceError, "Failed to read config #{file}: #{e.message}"
end
return if not conf or not conf['keystone_authtoken']
KEYS.each do |k|
if not conf['keystone_authtoken'][k.to_s].nil?
self.instance_variable_set("@#{k.to_s}", conf['keystone_authtoken'][k.to_s])
end
end
# If we dont get a auth_url from the config lets fallback to the
# www_authenticate_uri option if it exists and try that.
if not @auth_url and conf['keystone_authtoken']['www_authenticate_uri']
@auth_url = conf['keystone_authtoken']['www_authenticate_uri']
end
if validate_auth_url? && sanitize_auth_url?
Puppet.debug("Credentials auth URL is valid but was sanitized to: #{@auth_url}")
end
end
# @method validate
# Validate that the credentials that are stored right now can be used
# and that the required credentials for the specified scope is available.
# @raises Puppet::ResourceError
# When a credentials is invalid.
def validate
if not @auth_url
raise Puppet::ResourceError, 'Credentials must have a auth URL'
end
if not validate_auth_url?
raise Puppet::ResourceError, "The auth URL in credentials is an invalid HTTP URL: #{@auth_url}"
end
if not @user_id and not @username
raise Puppet::ResourceError, 'Credentials must have a user ID or username'
end
if not @password
raise Puppet::ResourceError, 'Credentials must have a password'
end
if not ['unscoped', 'system', 'domain', 'project'].include? @scope
raise Puppet::ResourceError, "Credentials contains an invalid scope: #{@scope}"
end
if not @user_domain_id and not @user_domain_name
raise Puppet::ResourceError, 'Credentials must have a user domain ID or user domain name'
end
if @scope == 'domain' and (not @domain_id and not @domain_name)
raise Puppet::ResourceError, 'Credentials with domain scope must have a domain ID or domain name'
end
if @scope == 'project' and (not @project_id and not @project_name)
raise Puppet::ResourceError, 'Credentials with project scope must have project ID or project name'
end
if @scope == 'project' and (not @project_domain_id and not @project_domain_name)
raise Puppet::ResourceError, 'Credentials with project scope must have a project domain ID or project domain name'
end
if @interface and not ['internal', 'admin', 'public'].include? @interface
raise Puppet::ResourceError, "Credentials interface must be internal, admin or public, got: #{@interface}"
end
# TODO: Validate service_type here.
# If the service_type is not set we will talk to the auth_url i.e keystone.
end
# @method user
# Get the user ID or name.
def user
get_id_or_name(@user_id, @username)
end
# @method user_domain
# Get the user domain ID or name.
# @return hash
# With the ID or name set.
def user_domain
get_id_or_name(@user_domain_id, @user_domain_name)
end
# @method domain
# Get the domain ID or name.
# @return hash
# With the ID or name set.
def domain
get_id_or_name(@domain_id, @domain_name)
end
# @method project
# Get the project ID or name.
# @return hash
# With the ID or name set.
def project
get_id_or_name(@project_id, @project_name)
end
# @method project_domain
# Get the project domain ID or name.
# @return hash
# With the ID or name set.
def project_domain
get_id_or_name(@project_domain_id, @project_domain_name)
end
private
# @method validate_auth_url?
# Validate the auth_url is a valid HTTP URL.
# @return boolean
# That is true on success otherwise false.
def validate_auth_url?
url = URI.parse(@auth_url)
url.is_a?(URI::HTTP) && !url.host.nil?
rescue URI::InvalidURIError
false
end
# @method sanitize_auth_url?
# Sanitize the auth_url by removing the path.
# @return boolean
# Returns true if the auth_url was sanitized otherwise false.
def sanitize_auth_url?
changed = false
url = URI.parse(@auth_url)
if url.path != '' or url.query != nil
url.path = ''
url.query = nil
changed = true
end
@auth_url = url.to_s
changed
end
# @method get_id_or_name
# Get the ID or name depending on which one of them
# is not nil.
# @return hash
# With the ID or name set.
def get_id_or_name(id, name)
if not id and not name
return nil
end
result = {}
if id
result[:id] = id
else
result[:name] = name
end
result
end
# @method read_config
# Open config file, read it and save data.
# @return hash
# The read contents from the config file.
def read_config(file)
return @config if @config
@config = Puppet::Util::IniConfig::File.new
@config.read(file)
@config
end
end
end

View File

@ -0,0 +1,206 @@
# Copyright (c) 2019 Binero AB
#
# Author: Tobias Urdin <tobias.urdin@binero.se>
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
require 'puppet'
require 'puppet_x'
require 'net/http'
module PuppetX::OpenStack
class HttpBase
@base_url = nil
@default_headers = nil
@ca_file = nil
# @method initialize
# Initialize the class.
# @param base_url
# The base url to use.
# @param default_headers
# A hash with default headers to use.
def initialize(base_url, default_headers={})
@base_url = base_url
@default_headers = default_headers
# This is not optimal but we must lookup where we should
# load our trusted CAs from. The best way would be to use
# the system path provided by OpenSSL::X509::DEFAULT_CERT_DIR
# but since Puppet runs it's own Ruby stack that points
# to /opt/puppetlabs/puppet/ssl/certs which is not where
# all trusted or self-signed CAs would be placed. What we
# do instead is we lookup the default trusted CA files that
# the operating systems provide.
ca_files = ['/etc/pki/tls/cert.pem', '/etc/ssl/certs/ca-certificates.crt']
@ca_file = ca_files.find { |f|
File.file?(f)
}
if not @ca_files
Puppet.warning("Could not find any of the CA files: #{ca_files.inspect}")
end
end
protected
# @method parse_url
# Parse the base URL and fix the path that will be used for the requests.
# Protected function that can only used by this class and subclasses that
# inherits this class.
# @param path
# The path to add to the base URL.
# @param query
# Optional parameter if the query parameters that should be added to
# the URL.
# @return string
# The generated full URL with the specified path and query.
def parse_url(path, query=nil)
url = URI.parse(@base_url)
path.prepend('/') unless path[0] == '/'
url.path += path
url.query = query if query
url
end
# @method default_headers
# Get all default headers.
# @param array
# An array with all default headers.
def default_headers
@default_headers
end
# @method default_headers=
# Set all default headers.
# @param value
# An array with all default headers.
def default_headers=(value)
@default_headers = value
end
# @method default_header=
# Set one default header.
# @param key
# The header to set.
# @param value
# The value to set for this header.
def default_header(key, value)
@default_headers[key] = value
end
# @method request
# Perform a HTTP request.
# @param type
# The HTTP request type.
# @param path
# The path to perform the request against.
# @param data
# The data to send with the request.
# @param headers
# Hash with headers to add to the request.
# @param retry_count
# The amount of times to retry on exception.
# @param backoff
# Number of seconds to wait between each retry.
# @return Net::HTTPResponse
# A response object.
# @raises Puppet::ResourceError
# If invalid type parameter.
# @raises Puppet::ResourceError
# If request failed and retries are done.
def request(type,
path,
data=nil,
headers=nil,
query=nil,
retry_count=10,
backoff=5)
url = parse_url(path, query)
Puppet.debug("#{type.upcase} request against #{url}")
case type.upcase
when 'GET'
request = Net::HTTP::Get.new(url.request_uri)
when 'POST'
request = Net::HTTP::Post.new(url.request_uri)
when 'PUT'
request = Net::HTTP::Put.new(url.request_uri)
when 'PATCH'
request = Net::HTTP::Patch.new(url.request_uri)
when 'DELETE'
request = Net::HTTP::Delete.new(url.request_uri)
else
raise Puppet::ResourceError, "request called with unsupported HTTP request type: #{type}"
end
request.content_type = 'application/json'
request.body = data if data
all_headers = default_headers
all_headers = all_headers.merge!(headers) if headers && headers.is_a?(Hash)
Puppet.debug("Headers that is set: #{all_headers.keys}")
all_headers.each do |key, value|
if not value.is_a?(String)
val = value.to_s
else
val = value
end
request[key] = val
end
# Always override the user-agent
request['User-Agent'] = 'puppet-openstack-client/1.0.0'
retry_counter = retry_count
begin
use_ssl = (url.scheme == 'https')
if use_ssl
if @ca_file
Puppet.debug("Using CA file #{@ca_file} when doing request")
else
Puppet.warning('HTTPS request without any CA file, request cannot be verified!')
end
end
response = Net::HTTP.start(url.hostname, url.port,
:use_ssl => use_ssl,
:ca_file => @ca_file) do |http|
http.request(request)
end
rescue => e
if retry_counter > 0
Puppet.debug("Failed #{type.upcase} request to #{url} retrying #{retry_counter} more times: #{e.message}")
retry_counter -= 1
if backoff > 0
Puppet.debug("Backing off for #{backoff} seconds until next try against #{url}")
Kernel.sleep backoff
end
retry
end
raise Puppet::ResourceError, "#{type.upcase} HTTP request to #{url.to_s} failed: #{e.message}"
end
response
end
end
end

View File

@ -0,0 +1,267 @@
# Copyright (c) 2019 Binero AB
#
# Author: Tobias Urdin <tobias.urdin@binero.se>
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
require 'puppet'
require 'puppet_x'
require 'json'
DEFAULT_FILTER_KEYS = ['ensure']
module PuppetX::OpenStack
class Resource
# The resource data.
@resource_data = {}
# Can be used to add more keys that will be filtered out.
@filter_keys = nil
# Can be used to transform keys while keeping the value.
@transform_keys = nil
# Can be used to override values everywhere, no matter what.
@overrides = nil
# @method initialize
# Initialize the resource.
# @param resource_data
# Resource data to initialize with or nil
# to do it later.
# @param merge
# Boolean if we should merge or set the data.
def initialize(resource_data=nil, merge=true)
@resource_data = {}
return if not resource_data
initialize_resource_data(resource_data, merge)
end
# @method []
# Get a key from the resource data.
# @param key
# The key to get.
# @return value or nil
# The value of the key or nil
def [](key)
get(key)
end
# @method get
# Get a key from the resource data.
# @param key
# The key to get.
# @return value or nil
# The value of the key or nil
def get(key)
if @resource_data.key?(key)
@resource_data[key]
else
nil
end
rescue
nil
end
# @method []=
# Set a value in the resource data.
# @param key
# The key to set.
# @param value
# The value to set.
def []=(key, value)
set(key, value)
end
# @method set
# Set a value in the resource data.
# @param key
# The key to set.
# @param value
# The value to set.
def set(key, value)
@resource_data[key] = value
end
# @method key?
# Check if a key exists in the resource data.
# @param key
# The key to check.
# @return boolean
# True if the key exists otherwise false.
def key?(key)
@resource_data.key?(key)
end
# @method delete
# Delete a key from the resource data.
# @param key
# The key to delete.
def delete(key)
@resource_data.delete(key) if key?(key)
end
# @method from_type
# Initialize resource data from Puppet type data,
# filter out and transform keys.
# @param resource_data
# The resource data.
# @param merge
# Boolean if we should merge or set the data.
def from_type(resource_data, merge=true)
resource_data = filter_resource_data(resource_data)
resource_data = transform_resource_data(resource_data)
resource_data = override_resource_data(resource_data)
initialize_resource_data(resource_data, merge)
end
# @method to_json
# Generate JSON from the saved resource data.
# @param parent
# String to enclose the data inside a a parent key.
# @return string
# String with the generated JSON data.
def to_json(parent=nil)
if not @resource_data
raise Puppet::ResourceError, "Resource has no data cannot generate JSON"
end
if parent
generate_data = { parent.to_s => @resource_data }
else
@resource_data
end
JSON.generate(generate_data)
rescue
@resource_data
end
# @method to_hash
# Get the resource data as a hash.
# @return hash
# Hash with all the resource data.
def to_hash
if not @resource_data
raise Puppet::ResourceError, "Resource has no data cannot return hash"
end
result = @resource_data
result = transform_resource_data(result, true)
result = override_resource_data(result)
result.transform_keys { |key| key.to_sym rescue key }
end
# @method to_type
# Get the resource data and format to type.
# @param context
# The Puppet::ResourceApi::BaseContext context.
# @return hash
# Hash with all the resource data.
def to_type(context)
h = to_hash
h[:ensure] = 'present'
rejected_keys = context.type.check_schema_keys(h)
h.reject { |k, v| rejected_keys.include?(k) }
end
private
# @method initialize_resource_data
# Initialize the resource data.
# @param resource_data
# The resource data.
# @param merge
# Boolean if we should merge the data otherwise set it.
def initialize_resource_data(resource_data, merge)
if merge
@resource_data.merge!(resource_data)
else
@resource_data = resource_data
end
@resource_data.each do |key, val|
define_singleton_method("#{key}=") { |new| @resource_data[key] = new }
define_singleton_method(key) { @resource_data[key] }
end
end
# @method filter_resource_data
# Filter resource data.
# @param resource_data
# The resource data.
# @return hash
# The filtered hash with the resource data.
def filter_resource_data(resource_data)
filters = DEFAULT_FILTER_KEYS
filters += @filter_keys if @filter_keys
Puppet.debug("Filtering out keys: #{filters.inspect}")
resource_data.reject { |k, v|
filters.include?(k.to_s) || filters.include?(k.to_sym)
}
end
# @method transform_resource_data
# Transform resource data.
# @param reverse
# Boolean if we should do a reverse transform.
# @return hash
# The transformed hash with the resource data.
def transform_resource_data(resource_data, reverse=false)
return resource_data if not @transform_keys
if reverse
Puppet.debug("Reverse transforming keys: #{@transform_keys.values.inspect}")
else
Puppet.debug("Transforming keys: #{@transform_keys.keys.inspect}")
end
@transform_keys.map do |left, right|
if reverse
from = right
to = left
else
from = left
to = right
end
resource_data[to.to_sym] = resource_data.delete(from.to_sym) if resource_data.key?(from.to_sym)
resource_data[to.to_sym] = resource_data.delete(from.to_s) if resource_data.key?(from.to_s)
end
resource_data
end
# @method override_resource_data
# Process overrides.
# @param resource_data
# The resource data.
# @return hash
# The processed hash after overrides.
def override_resource_data(resource_data)
return resource_data if not @overrides
@overrides.map do |key, value|
resource_data[key.to_sym] = value
end
resource_data
end
end
end

View File

@ -0,0 +1,101 @@
# Copyright (c) 2019 Binero AB
#
# Author: Tobias Urdin <tobias.urdin@binero.se>
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
require 'puppet_x'
module PuppetX::OpenStack
# Class whose purpose is to process credentials and generate
# a valid token authentication request body that can be sent
# to keystone.
class TokenRequest
@creds = nil
# @method initialize
# Initialize the class.
def initialize(credentials)
@creds = credentials
end
# @method body
# Create our hash that will be the body of the token request.
# @return hash
# A hash that contains all authentication data that ca be
# converted to JSON and sent to keystone.
def body
user_hash = @creds.user
user_hash[:password] = @creds.password
user_hash[:domain] = @creds.user_domain
password_hash = {
:user => user_hash,
}
methods = ['password']
identity_hash = {
:methods => methods,
:password => password_hash,
}
auth_hash = {
:identity => identity_hash,
}
body_hash = {
:auth => auth_hash,
}
scope_hash = scope
body_hash[:auth][:scope] = scope_hash if scope_hash
body_hash
end
private
# @method scope
# Get the hash with the proper scope data
# that will be added to the body.
# @return hash
# Nil or a hash with all data request for the specified scope.
def scope
case @creds.scope
when 'unscoped'
return nil
when 'system'
scope_data = {
:system => {
:all => true,
}
}
when 'domain'
scope_data = {
:domain => @creds.domain,
}
when 'project'
scope_data = {
:project => @creds.project,
}
# When authenticating as a project we need
# to specify the project domain as well.
scope_data[:project][:domain] = @creds.project_domain
end
scope_data
end
end
end