
Put back exists? method in keystone_user in line with the usual openstacklib mechanism. This is done by adding the possibility for request call to pass regexp messages that shouldn't be retried. Now we can safely call fetch_user without worrying about having the call retried by opentacklib. Fetch_project has the same behavior, so I added it to the mix. It may be a performance killer somewhere. Change-Id: I368cf6a06d21d018337af3e6d09cdabee839a563 Closes-Bug: 1597357
282 lines
7.7 KiB
Ruby
282 lines
7.7 KiB
Ruby
require 'puppet/util/inifile'
|
|
require 'puppet/provider/openstack'
|
|
require 'puppet/provider/openstack/auth'
|
|
require 'puppet/provider/openstack/credentials'
|
|
require File.join(File.dirname(__FILE__), '..','..', 'puppet/provider/keystone/util')
|
|
|
|
class Puppet::Provider::Keystone < Puppet::Provider::Openstack
|
|
|
|
extend Puppet::Provider::Openstack::Auth
|
|
|
|
INI_FILENAME = '/etc/keystone/keystone.conf'
|
|
DEFAULT_DOMAIN = 'Default'
|
|
|
|
@@default_domain_id = nil
|
|
|
|
def self.admin_endpoint
|
|
@admin_endpoint ||= get_admin_endpoint
|
|
end
|
|
|
|
def self.admin_token
|
|
@admin_token ||= get_admin_token
|
|
end
|
|
|
|
def self.clean_host(host)
|
|
host ||= '127.0.0.1'
|
|
case host
|
|
when '0.0.0.0'
|
|
return '127.0.0.1'
|
|
when '::0'
|
|
return '[::1]'
|
|
else
|
|
# if ipv6, make sure ip address has brackets - LP#1541512
|
|
if host.include?(':') and !host.include?(']')
|
|
return "[" + host + "]"
|
|
else
|
|
return host
|
|
end
|
|
end
|
|
end
|
|
|
|
def self.default_domain_from_ini_file
|
|
default_domain_from_conf = Puppet::Resource.indirection
|
|
.find('Keystone_config/identity/default_domain_id')
|
|
if default_domain_from_conf[:ensure] == :present
|
|
# get from ini file
|
|
default_domain_from_conf[:value]
|
|
else
|
|
nil
|
|
end
|
|
rescue
|
|
nil
|
|
end
|
|
|
|
def self.default_domain_id
|
|
if @@default_domain_id
|
|
# cached
|
|
@@default_domain_id
|
|
else
|
|
@@default_domain_id = default_domain_from_ini_file
|
|
end
|
|
@@default_domain_id = @@default_domain_id.nil? ? 'default' : @@default_domain_id
|
|
end
|
|
|
|
def self.default_domain_changed
|
|
default_domain_id != 'default'
|
|
end
|
|
|
|
def self.default_domain_deprecation_message
|
|
'Support for a resource without the domain ' \
|
|
'set is deprecated in Liberty cycle. ' \
|
|
'It will be dropped in the M-cycle. ' \
|
|
"Currently using '#{default_domain}' as default domain name " \
|
|
"while the default domain id is '#{default_domain_id}'."
|
|
end
|
|
|
|
def self.default_domain
|
|
DEFAULT_DOMAIN
|
|
end
|
|
|
|
def self.resource_to_name(domain, name, check_for_default = true)
|
|
raise Puppet::Error, "Domain cannot be nil for project '#{name}'. " \
|
|
'Please report a bug.' if domain.nil?
|
|
join_str = '::'
|
|
name_display = [name]
|
|
unless check_for_default && domain == default_domain
|
|
name_display << domain
|
|
end
|
|
name_display.join(join_str)
|
|
end
|
|
|
|
def self.name_to_resource(name)
|
|
uniq = name.split('::')
|
|
if uniq.count == 1
|
|
uniq.insert(0, default_domain)
|
|
else
|
|
uniq.reverse!
|
|
end
|
|
uniq
|
|
end
|
|
|
|
# Prefix with default domain if missing from the name.
|
|
def self.make_full_name(name)
|
|
resource_to_name(*name_to_resource(name), false)
|
|
end
|
|
|
|
def self.user_id_from_name_and_domain_name(name, domain_name)
|
|
@users_name ||= {}
|
|
id_str = "#{name}_#{domain_name}"
|
|
unless @users_name.keys.include?(id_str)
|
|
user = fetch_user(name, domain_name)
|
|
err("Could not find user with name [#{name}] and domain [#{domain_name}]") unless user
|
|
@users_name[id_str] = user[:id]
|
|
end
|
|
@users_name[id_str]
|
|
end
|
|
|
|
def self.project_id_from_name_and_domain_name(name, domain_name)
|
|
@projects_name ||= {}
|
|
id_str = "#{name}_#{domain_name}"
|
|
unless @projects_name.keys.include?(id_str)
|
|
project = fetch_project(name, domain_name)
|
|
err("Could not find project with name [#{name}] and domain [#{domain_name}]") unless project
|
|
@projects_name[id_str] = project[:id]
|
|
end
|
|
@projects_name[id_str]
|
|
end
|
|
|
|
def self.domain_name_from_id(id)
|
|
unless @domain_hash
|
|
list = request('domain', 'list')
|
|
@domain_hash = Hash[list.collect{|domain| [domain[:id], domain[:name]]}]
|
|
end
|
|
unless @domain_hash.include?(id)
|
|
name = request('domain', 'show', id)[:name]
|
|
err("Could not find domain with id [#{id}]") unless name
|
|
@domain_hash[id] = name
|
|
end
|
|
@domain_hash[id]
|
|
end
|
|
|
|
def self.domain_id_from_name(name)
|
|
unless @domain_hash_name
|
|
list = request('domain', 'list')
|
|
@domain_hash_name = Hash[list.collect{|domain| [domain[:name], domain[:id]]}]
|
|
end
|
|
unless @domain_hash_name.include?(name)
|
|
id = request('domain', 'show', name)[:id]
|
|
err("Could not find domain with name [#{name}]") unless id
|
|
@domain_hash_name[name] = id
|
|
end
|
|
@domain_hash_name[name]
|
|
end
|
|
|
|
def self.fetch_project(name, domain)
|
|
domain ||= default_domain
|
|
request('project', 'show',
|
|
[name, '--domain', domain],
|
|
{:no_retry_exception_msgs => /No project with a name or ID/})
|
|
rescue Puppet::ExecutionFailure => e
|
|
raise e unless e.message =~ /No project with a name or ID/
|
|
end
|
|
|
|
def self.fetch_user(name, domain)
|
|
domain ||= default_domain
|
|
request('user', 'show',
|
|
[name, '--domain', domain],
|
|
{:no_retry_exception_msgs => /No user with a name or ID/})
|
|
rescue Puppet::ExecutionFailure => e
|
|
raise e unless e.message =~ /No user with a name or ID/
|
|
end
|
|
|
|
def self.get_admin_endpoint
|
|
endpoint = nil
|
|
if keystone_file
|
|
if url = get_section('DEFAULT', 'admin_endpoint')
|
|
endpoint = url.chomp('/')
|
|
else
|
|
admin_port = get_section('DEFAULT', 'admin_port') || '35357'
|
|
host = clean_host(get_section('DEFAULT', 'admin_bind_host'))
|
|
protocol = ssl? ? 'https' : 'http'
|
|
endpoint = "#{protocol}://#{host}:#{admin_port}"
|
|
end
|
|
end
|
|
return endpoint
|
|
end
|
|
|
|
def self.get_admin_token
|
|
get_section('DEFAULT', 'admin_token')
|
|
end
|
|
|
|
def self.get_auth_url
|
|
auth_url = nil
|
|
if ENV['OS_AUTH_URL']
|
|
auth_url = ENV['OS_AUTH_URL'].dup
|
|
elsif auth_url = get_os_vars_from_rcfile(rc_filename)['OS_AUTH_URL']
|
|
else
|
|
auth_url = admin_endpoint
|
|
end
|
|
return auth_url
|
|
end
|
|
|
|
def self.get_section(group, name)
|
|
if keystone_file && keystone_file[group] && keystone_file[group][name]
|
|
return keystone_file[group][name].strip
|
|
end
|
|
return nil
|
|
end
|
|
|
|
def self.get_service_url
|
|
service_url = nil
|
|
if ENV['OS_URL']
|
|
service_url = ENV['OS_URL'].dup
|
|
elsif admin_endpoint
|
|
service_url = admin_endpoint
|
|
service_url << "/v#{@credentials.version}"
|
|
end
|
|
return service_url
|
|
end
|
|
|
|
def self.ini_filename
|
|
INI_FILENAME
|
|
end
|
|
|
|
def self.keystone_file
|
|
return @keystone_file if @keystone_file
|
|
if File.exists?(ini_filename)
|
|
@keystone_file = Puppet::Util::IniConfig::File.new
|
|
@keystone_file.read(ini_filename)
|
|
@keystone_file
|
|
end
|
|
end
|
|
|
|
def self.request(service, action, properties=nil, options={})
|
|
super
|
|
rescue Puppet::Error::OpenstackAuthInputError, Puppet::Error::OpenstackUnauthorizedError => error
|
|
request_by_service_token(service, action, error, properties, options=options)
|
|
end
|
|
|
|
def self.request_by_service_token(service, action, error, properties=nil, options={})
|
|
properties ||= []
|
|
@credentials.token = admin_token
|
|
@credentials.url = service_url
|
|
raise error unless @credentials.service_token_set?
|
|
Puppet::Provider::Openstack.request(service, action, properties, @credentials, options)
|
|
end
|
|
|
|
def self.service_url
|
|
@service_url ||= get_service_url
|
|
end
|
|
|
|
def self.set_domain_for_name(name, domain_name)
|
|
if domain_name.nil? || domain_name.empty?
|
|
raise(Puppet::Error, "Missing domain name for resource #{name}")
|
|
end
|
|
domain_id = self.domain_id_from_name(domain_name)
|
|
case domain_id
|
|
when default_domain_id
|
|
name
|
|
when nil
|
|
name
|
|
else
|
|
name << "::#{domain_name}"
|
|
end
|
|
end
|
|
|
|
def self.ssl?
|
|
if keystone_file && keystone_file['ssl'] && keystone_file['ssl']['enable'] && keystone_file['ssl']['enable'].strip.downcase == 'true'
|
|
return true
|
|
end
|
|
return false
|
|
end
|
|
|
|
# Helper functions to use on the pre-validated enabled field
|
|
def bool_to_sym(bool)
|
|
bool == true ? :true : :false
|
|
end
|
|
|
|
def sym_to_bool(sym)
|
|
sym == :true ? true : false
|
|
end
|
|
end
|