support for keystone v3 api - v3 domain utility code
This patch implements these parts of the blueprint: 1) Adds some utility code for domain handling. Puppet code may need to specify resource titles as "name::domain" e.g. "admin::admin_domain". The function split_domain is used to split a name in this format into its ['name', 'domain'] components as an array. Usually providers will not need to use this, they will use the name_and_domain method in the keystone.rb provider. This is resource aware and will try many different ways to get a domain to use for the provider: resource[:domain], then the domain part of 'name::domain' name/title value. If keystone.conf is available, it will use [identity] default_domain_id as the default domain id, and look up the domain name using the 'domain list' operation to create a mapping. If all else fails, the domain name will be 'Default' which is the "default" default domain name used by Keystone. 2) Adds the method domain_name_from_id - the providers and other code will need to map from the domain id to the name, and this method provides that mapping. Implements: blueprint api-v3-support Change-Id: Ifb8171b78904257f8112e1485d5255f0d0f5aca8 Depends-On: Icafc4cb8ed000fd9d3ed6ffde2afe1a1250d90af
This commit is contained in:
parent
dbf0530c8c
commit
a054c503f3
@ -2,6 +2,7 @@ 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
|
||||
|
||||
@ -30,6 +31,31 @@ class Puppet::Provider::Keystone < Puppet::Provider::Openstack
|
||||
@admin_endpoint ||= get_admin_endpoint
|
||||
end
|
||||
|
||||
# use the domain in this order:
|
||||
# 1 - the domain name specified in the resource definition - resource[:domain]
|
||||
# 2 - the domain name part of the resource name/title e.g. user_name::user_domain
|
||||
# if passed in by name_and_domain above
|
||||
# 3 - use the specified default_domain_name
|
||||
# 4 - lookup the default domain
|
||||
# 5 - use 'Default' - the "default" default domain if no other one is configured
|
||||
# Usage: name_and_domain(resource[:name], resource[:domain], default_domain_name)
|
||||
def self.name_and_domain(namedomstr, domain_from_resource=nil, default_domain_name=nil)
|
||||
name, domain = Util.split_domain(namedomstr)
|
||||
ret = [name]
|
||||
if domain_from_resource
|
||||
ret << domain_from_resource
|
||||
elsif domain
|
||||
ret << domain
|
||||
elsif default_domain_name
|
||||
ret << default_domain_name
|
||||
elsif default_domain
|
||||
ret << default_domain
|
||||
else
|
||||
ret << 'Default'
|
||||
end
|
||||
ret
|
||||
end
|
||||
|
||||
def self.admin_token
|
||||
@admin_token ||= get_admin_token
|
||||
end
|
||||
@ -80,8 +106,8 @@ class Puppet::Provider::Keystone < Puppet::Provider::Openstack
|
||||
|
||||
def self.request(service, action, properties=nil)
|
||||
super
|
||||
rescue Puppet::Error::OpenstackAuthInputError => error
|
||||
request_by_service_token(service, action, error, properties)
|
||||
rescue Puppet::Error::OpenstackAuthInputError => error
|
||||
request_by_service_token(service, action, error, properties)
|
||||
end
|
||||
|
||||
def self.request_by_service_token(service, action, error, properties=nil)
|
||||
@ -96,6 +122,31 @@ class Puppet::Provider::Keystone < Puppet::Provider::Openstack
|
||||
INI_FILENAME
|
||||
end
|
||||
|
||||
def self.default_domain
|
||||
domain_hash[default_domain_id]
|
||||
end
|
||||
|
||||
def self.domain_hash
|
||||
return @domain_hash if @domain_hash
|
||||
list = request('domain', 'list')
|
||||
@domain_hash = Hash[list.collect{|domain| [domain[:id], domain[:name]]}]
|
||||
@domain_hash
|
||||
end
|
||||
|
||||
def self.domain_name_from_id(id)
|
||||
domain_hash[id]
|
||||
end
|
||||
|
||||
def self.default_domain_id
|
||||
return @default_domain_id if @default_domain_id
|
||||
if keystone_file and keystone_file['identity'] and keystone_file['identity']['default_domain_id']
|
||||
@default_domain_id = "#{keystone_file['identity']['default_domain_id'].strip}"
|
||||
else
|
||||
@default_domain_id = 'default'
|
||||
end
|
||||
@default_domain_id
|
||||
end
|
||||
|
||||
def self.keystone_file
|
||||
return @keystone_file if @keystone_file
|
||||
if File.exists?(ini_filename)
|
||||
|
25
lib/puppet/provider/keystone/util.rb
Normal file
25
lib/puppet/provider/keystone/util.rb
Normal file
@ -0,0 +1,25 @@
|
||||
module Util
|
||||
# Splits the rightmost part of a string using '::' as delimiter
|
||||
# Returns an array of both parts or nil if either is empty.
|
||||
# An empty rightmost part is ignored and converted as 'string::' => 'string'
|
||||
#
|
||||
# Examples:
|
||||
# "foo" -> ["foo", nil]
|
||||
# "foo::" -> ["foo", nil]
|
||||
# "foo::bar" -> ["foo", "bar"]
|
||||
# "foo::bar::" -> ["foo", "bar"]
|
||||
# "::foo" -> [nil, "foo"]
|
||||
# "::foo::" -> [nil, "foo"]
|
||||
# "foo::bar::baz" -> ["foo::bar", "baz"]
|
||||
# "foo::bar::baz::" -> ["foo::bar", "baz"]
|
||||
#
|
||||
def self.split_domain(str)
|
||||
left, right = nil, nil
|
||||
unless str.nil?
|
||||
left, delimiter, right = str.gsub(/::$/, '').rpartition('::')
|
||||
left, right = right, nil if delimiter.empty?
|
||||
left = nil if left.empty?
|
||||
end
|
||||
return [left, right]
|
||||
end
|
||||
end
|
29
spec/unit/provider/keystone/util_spec.rb
Normal file
29
spec/unit/provider/keystone/util_spec.rb
Normal file
@ -0,0 +1,29 @@
|
||||
require 'puppet'
|
||||
require 'spec_helper'
|
||||
require 'puppet/provider/keystone'
|
||||
require 'puppet/provider/keystone/util'
|
||||
|
||||
describe "split_domain method" do
|
||||
it 'should handle nil and empty strings' do
|
||||
expect(Util.split_domain('')).to eq([nil, nil])
|
||||
expect(Util.split_domain(nil)).to eq([nil, nil])
|
||||
end
|
||||
it 'should return name and no domain' do
|
||||
expect(Util.split_domain('foo')).to eq(['foo', nil])
|
||||
expect(Util.split_domain('foo::')).to eq(['foo', nil])
|
||||
end
|
||||
it 'should return name and domain' do
|
||||
expect(Util.split_domain('foo::bar')).to eq(['foo', 'bar'])
|
||||
expect(Util.split_domain('foo::bar::')).to eq(['foo', 'bar'])
|
||||
expect(Util.split_domain('::foo::bar')).to eq(['::foo', 'bar'])
|
||||
expect(Util.split_domain('::foo::bar::')).to eq(['::foo', 'bar'])
|
||||
expect(Util.split_domain('foo::bar::baz')).to eq(['foo::bar', 'baz'])
|
||||
expect(Util.split_domain('foo::bar::baz::')).to eq(['foo::bar', 'baz'])
|
||||
expect(Util.split_domain('::foo::bar::baz')).to eq(['::foo::bar', 'baz'])
|
||||
expect(Util.split_domain('::foo::bar::baz::')).to eq(['::foo::bar', 'baz'])
|
||||
end
|
||||
it 'should return domain only' do
|
||||
expect(Util.split_domain('::foo')).to eq([nil, 'foo'])
|
||||
expect(Util.split_domain('::foo::')).to eq([nil, 'foo'])
|
||||
end
|
||||
end
|
@ -6,13 +6,16 @@ require 'tempfile'
|
||||
klass = Puppet::Provider::Keystone
|
||||
|
||||
class Puppet::Provider::Keystone
|
||||
@credentials = Puppet::Provider::Openstack::CredentialsV2_0.new
|
||||
@credentials = Puppet::Provider::Openstack::CredentialsV3.new
|
||||
|
||||
def self.reset
|
||||
@admin_endpoint = nil
|
||||
@tenant_hash = nil
|
||||
@admin_token = nil
|
||||
@keystone_file = nil
|
||||
@domain_id_to_name = nil
|
||||
@default_domain_id = nil
|
||||
@domain_hash = nil
|
||||
end
|
||||
end
|
||||
|
||||
@ -57,7 +60,7 @@ describe Puppet::Provider::Keystone do
|
||||
File.expects(:exists?).with("/etc/keystone/keystone.conf").returns(true)
|
||||
Puppet::Util::IniConfig::File.expects(:new).returns(mock)
|
||||
mock.expects(:read).with('/etc/keystone/keystone.conf')
|
||||
expect(klass.get_admin_endpoint).to eq('http://192.168.56.210:35357/v2.0/')
|
||||
expect(klass.get_admin_endpoint).to eq('http://192.168.56.210:35357/v3/')
|
||||
end
|
||||
|
||||
it 'should use localhost in the admin endpoint if bind_host is 0.0.0.0' do
|
||||
@ -65,7 +68,7 @@ describe Puppet::Provider::Keystone do
|
||||
File.expects(:exists?).with("/etc/keystone/keystone.conf").returns(true)
|
||||
Puppet::Util::IniConfig::File.expects(:new).returns(mock)
|
||||
mock.expects(:read).with('/etc/keystone/keystone.conf')
|
||||
expect(klass.get_admin_endpoint).to eq('http://127.0.0.1:35357/v2.0/')
|
||||
expect(klass.get_admin_endpoint).to eq('http://127.0.0.1:35357/v3/')
|
||||
end
|
||||
|
||||
it 'should use [::1] in the admin endpoint if bind_host is ::0' do
|
||||
@ -73,7 +76,7 @@ describe Puppet::Provider::Keystone do
|
||||
File.expects(:exists?).with("/etc/keystone/keystone.conf").returns(true)
|
||||
Puppet::Util::IniConfig::File.expects(:new).returns(mock)
|
||||
mock.expects(:read).with('/etc/keystone/keystone.conf')
|
||||
expect(klass.get_admin_endpoint).to eq('http://[::1]:35357/v2.0/')
|
||||
expect(klass.get_admin_endpoint).to eq('http://[::1]:35357/v3/')
|
||||
end
|
||||
|
||||
it 'should use localhost in the admin endpoint if bind_host is unspecified' do
|
||||
@ -81,7 +84,7 @@ describe Puppet::Provider::Keystone do
|
||||
File.expects(:exists?).with("/etc/keystone/keystone.conf").returns(true)
|
||||
Puppet::Util::IniConfig::File.expects(:new).returns(mock)
|
||||
mock.expects(:read).with('/etc/keystone/keystone.conf')
|
||||
expect(klass.get_admin_endpoint).to eq('http://127.0.0.1:35357/v2.0/')
|
||||
expect(klass.get_admin_endpoint).to eq('http://127.0.0.1:35357/v3/')
|
||||
end
|
||||
|
||||
it 'should use https if ssl is enabled' do
|
||||
@ -89,7 +92,7 @@ describe Puppet::Provider::Keystone do
|
||||
File.expects(:exists?).with("/etc/keystone/keystone.conf").returns(true)
|
||||
Puppet::Util::IniConfig::File.expects(:new).returns(mock)
|
||||
mock.expects(:read).with('/etc/keystone/keystone.conf')
|
||||
expect(klass.get_admin_endpoint).to eq('https://192.168.56.210:35357/v2.0/')
|
||||
expect(klass.get_admin_endpoint).to eq('https://192.168.56.210:35357/v3/')
|
||||
end
|
||||
|
||||
it 'should use http if ssl is disabled' do
|
||||
@ -97,7 +100,7 @@ describe Puppet::Provider::Keystone do
|
||||
File.expects(:exists?).with("/etc/keystone/keystone.conf").returns(true)
|
||||
Puppet::Util::IniConfig::File.expects(:new).returns(mock)
|
||||
mock.expects(:read).with('/etc/keystone/keystone.conf')
|
||||
expect(klass.get_admin_endpoint).to eq('http://192.168.56.210:35357/v2.0/')
|
||||
expect(klass.get_admin_endpoint).to eq('http://192.168.56.210:35357/v3/')
|
||||
end
|
||||
|
||||
it 'should use the defined admin_endpoint if available' do
|
||||
@ -105,7 +108,7 @@ describe Puppet::Provider::Keystone do
|
||||
File.expects(:exists?).with("/etc/keystone/keystone.conf").returns(true)
|
||||
Puppet::Util::IniConfig::File.expects(:new).returns(mock)
|
||||
mock.expects(:read).with('/etc/keystone/keystone.conf')
|
||||
expect(klass.get_admin_endpoint).to eq('https://keystone.example.com/v2.0/')
|
||||
expect(klass.get_admin_endpoint).to eq('https://keystone.example.com/v3/')
|
||||
end
|
||||
|
||||
it 'should handle an admin_endpoint with a trailing slash' do
|
||||
@ -113,9 +116,58 @@ describe Puppet::Provider::Keystone do
|
||||
File.expects(:exists?).with("/etc/keystone/keystone.conf").returns(true)
|
||||
Puppet::Util::IniConfig::File.expects(:new).returns(mock)
|
||||
mock.expects(:read).with('/etc/keystone/keystone.conf')
|
||||
expect(klass.get_admin_endpoint).to eq('https://keystone.example.com/v2.0/')
|
||||
expect(klass.get_admin_endpoint).to eq('https://keystone.example.com/v3/')
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
describe 'when using domains' do
|
||||
it 'name_and_domain should return the resource domain' do
|
||||
expect(klass.name_and_domain('foo::in_name', 'from_resource', 'default')).to eq(['foo', 'from_resource'])
|
||||
end
|
||||
it 'name_and_domain should return the default domain' do
|
||||
expect(klass.name_and_domain('foo', nil, 'default')).to eq(['foo', 'default'])
|
||||
end
|
||||
it 'name_and_domain should return the domain part of the name' do
|
||||
expect(klass.name_and_domain('foo::in_name', nil, 'default')).to eq(['foo', 'in_name'])
|
||||
end
|
||||
it 'should return the default domain name using the default_domain_id from keystone.conf' do
|
||||
ENV['OS_USERNAME'] = 'test'
|
||||
ENV['OS_PASSWORD'] = 'abc123'
|
||||
ENV['OS_PROJECT_NAME'] = 'test'
|
||||
ENV['OS_AUTH_URL'] = 'http://127.0.0.1:35357/v3'
|
||||
mock = {
|
||||
'DEFAULT' => {
|
||||
'admin_endpoint' => 'http://127.0.0.1:35357',
|
||||
'admin_token' => 'admin_token'
|
||||
},
|
||||
'identity' => {'default_domain_id' => 'somename'}
|
||||
}
|
||||
File.expects(:exists?).with('/etc/keystone/keystone.conf').returns(true)
|
||||
Puppet::Util::IniConfig::File.expects(:new).returns(mock)
|
||||
mock.expects(:read).with('/etc/keystone/keystone.conf')
|
||||
klass.expects(:openstack)
|
||||
.with('domain', 'list', '--quiet', '--format', 'csv', [])
|
||||
.returns('"ID","Name","Enabled","Description"
|
||||
"somename","SomeName",True,"default domain"
|
||||
')
|
||||
expect(klass.name_and_domain('foo')).to eq(['foo', 'SomeName'])
|
||||
end
|
||||
it 'should return Default if default_domain_id is not configured' do
|
||||
ENV['OS_USERNAME'] = 'test'
|
||||
ENV['OS_PASSWORD'] = 'abc123'
|
||||
ENV['OS_PROJECT_NAME'] = 'test'
|
||||
ENV['OS_AUTH_URL'] = 'http://127.0.0.1:35357/v3'
|
||||
mock = {}
|
||||
Puppet::Util::IniConfig::File.expects(:new).returns(mock)
|
||||
File.expects(:exists?).with('/etc/keystone/keystone.conf').returns(true)
|
||||
mock.expects(:read).with('/etc/keystone/keystone.conf')
|
||||
klass.expects(:openstack)
|
||||
.with('domain', 'list', '--quiet', '--format', 'csv', [])
|
||||
.returns('"ID","Name","Enabled","Description"
|
||||
"default","Default",True,"default domain"
|
||||
')
|
||||
expect(klass.name_and_domain('foo')).to eq(['foo', 'Default'])
|
||||
end
|
||||
end
|
||||
end
|
||||
|
Loading…
Reference in New Issue
Block a user