
Currently every project and user resource generates a call to fetch_domain, which is an unhashed query to the openstack domain show command. This is very wasteful and does not scale well. This patch takes the code that hashes domains by id and generates an inverse, a hash of domains by name. This code is used in place of fetch_domain, which is removed. Since the only thing used in this hash is the id, which should be immutable, we should not have issues with the other fields changing (like description and enabled). This should provide a performance boost to users who have a lot of users and projects, especially if everything is in the default domain. Co-Authored-By: Denis Egorenko <degorenko@mirantis.com> Change-Id: I99d8fe272aedc0cbc6ad48561ea50a7b7d6cdb1e
251 lines
6.5 KiB
Ruby
251 lines
6.5 KiB
Ruby
require 'puppet/util/inifile'
|
|
require 'puppet/provider/openstack'
|
|
require 'puppet/provider/openstack/auth'
|
|
require 'puppet/provider/openstack/credentials'
|
|
require '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
|
|
return host
|
|
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.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])
|
|
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])
|
|
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)
|
|
super
|
|
rescue Puppet::Error::OpenstackAuthInputError, Puppet::Error::OpenstackUnauthorizedError => error
|
|
request_by_service_token(service, action, error, properties)
|
|
end
|
|
|
|
def self.request_by_service_token(service, action, error, properties=nil)
|
|
properties ||= []
|
|
@credentials.token = admin_token
|
|
@credentials.url = service_url
|
|
raise error unless @credentials.service_token_set?
|
|
Puppet::Provider::Openstack.request(service, action, properties, @credentials)
|
|
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
|