puppet-keystone/lib/puppet/provider/keystone_endpoint/openstack.rb

296 lines
8.4 KiB
Ruby

require File.join(File.dirname(__FILE__), '..','..','..', 'puppet/provider/keystone')
Puppet::Type.type(:keystone_endpoint).provide(
:openstack,
:parent => Puppet::Provider::Keystone
) do
desc "Provider to manage keystone endpoints."
include PuppetX::Keystone::CompositeNamevar::Helpers
attr_accessor :property_hash, :property_flush
@endpoints = nil
@services = nil
@credentials = Puppet::Provider::Openstack::CredentialsV3.new
@do_not_manage = false
def initialize(value={})
super(value)
@property_flush = {}
end
def self.do_not_manage
@do_not_manage
end
def self.do_not_manage=(value)
@do_not_manage = value
end
def create
if self.class.do_not_manage
fail("Not managing Keystone_endpoint[#{@resource[:name]}] due to earlier Keystone API failures.")
end
name = resource[:name]
region = resource[:region]
type = resource[:type]
type = self.class.type_from_service(name) unless set?(:type)
@property_hash[:type] = type
ids = []
s_id = service_id
created = false
[:admin_url, :internal_url, :public_url].each do |scope|
if resource[scope]
created = true
ids << endpoint_create(s_id, region, scope.to_s.sub(/_url$/, ''),
resource[scope])[:id]
end
end
if created
@property_hash[:id] = ids.join(',')
@property_hash[:ensure] = :present
else
warning('Specifying a keystone_endpoint without an ' \
'admin_url/public_url/internal_url ' \
"won't create the endpoint at all, despite what Puppet is saying.")
@property_hash[:ensure] = :absent
end
end
def destroy
if self.class.do_not_manage
fail("Not managing Keystone_endpoint[#{@resource[:name]}] due to earlier Keystone API failures.")
end
ids = @property_hash[:id].split(',')
ids.each do |id|
self.class.system_request('endpoint', 'delete', id)
end
@property_hash.clear
end
def exists?
@property_hash[:ensure] == :present
end
mk_resource_methods
def public_url=(value)
if self.class.do_not_manage
fail("Not managing Keystone_endpoint[#{@resource[:name]}] due to earlier Keystone API failures.")
end
@property_flush[:public_url] = value
end
def internal_url=(value)
if self.class.do_not_manage
fail("Not managing Keystone_endpoint[#{@resource[:name]}] due to earlier Keystone API failures.")
end
@property_flush[:internal_url] = value
end
def admin_url=(value)
if self.class.do_not_manage
fail("Not managing Keystone_endpoint[#{@resource[:name]}] due to earlier Keystone API failures.")
end
@property_flush[:admin_url] = value
end
def region=(_)
fail(Puppet::Error, "Updating the endpoint's region is not currently supported.")
end
def self.instances
names = []
list = []
endpoints.each do |current|
name = transform_name(current[:region], current[:service_name], current[:service_type])
unless names.include?(name)
names << name
endpoint = { :name => name, current[:interface].to_sym => current }
endpoints.each do |ep_osc|
if (ep_osc[:id] != current[:id]) &&
(ep_osc[:service_name] == current[:service_name]) &&
(ep_osc[:service_type] == current[:service_type]) &&
(ep_osc[:region] == current[:region])
endpoint.merge!(ep_osc[:interface].to_sym => ep_osc)
end
end
list << endpoint
end
end
list.collect do |endpoint|
new(
:name => endpoint[:name],
:ensure => :present,
:id => make_id(endpoint),
:region => get_region(endpoint),
:admin_url => get_url(endpoint, :admin),
:internal_url => get_url(endpoint, :internal),
:public_url => get_url(endpoint, :public)
)
end
end
def self.prefetch(resources)
prefetch_composite(resources) do |sorted_namevars|
name = sorted_namevars[0]
region = sorted_namevars[1]
type = sorted_namevars[2]
transform_name(region, name, type)
end
end
def flush
if property_flush && property_hash[:id]
scopes = [:admin_url, :internal_url, :public_url]
ids = Hash[scopes.zip(property_hash[:id].split(','))]
scopes.each do |scope|
if property_flush[scope]
if ids[scope].nil? || ids[scope].empty?
ids[scope] = endpoint_create(service_id, resource[:region],
scope.to_s.sub(/_url$/, ''),
property_flush[scope])[:id]
else
self.class.system_request('endpoint',
'set',
[ids[scope], "--url=#{resource[scope]}"])
end
end
end
@property_hash = resource.to_hash
@property_hash[:id] = scopes.map { |s| ids[s] }.join(',')
@property_hash[:ensure] = :present
end
end
private
def endpoint_create(name, region, interface, url)
properties = [name, interface, url, '--region', region]
self.class.system_request('endpoint', 'create', properties)
end
private
def self.endpoints
return @endpoints unless @endpoints.nil?
prev_do_not_manage = self.do_not_manage
self.do_not_manage = true
@endpoints = system_request('endpoint', 'list')
self.do_not_manage = prev_do_not_manage
@endpoints
end
def self.endpoints=(value)
@endpoints = value
end
def self.services
return @services unless @services.nil?
prev_do_not_manage = self.do_not_manage
self.do_not_manage = true
@services = system_request('service', 'list')
self.do_not_manage = prev_do_not_manage
@services
end
def self.services=(value)
@services = value
end
def self.endpoint_from_region_name(region, name)
endpoints.find_all { |e| e[:region] == region && e[:service_name] == name }
.map { |e| e[:service_type] }.uniq
end
def self.type_from_service(name)
types = services.find_all { |s| s[:name] == name }.map { |e| e[:type] }.uniq
if types.count == 1
types[0]
else
# We don't fail here as it can happen during a ensure => absent.
PuppetX::Keystone::CompositeNamevar::Unset
end
end
def self.service_type(services, region, name)
nbr_of_services = services.count
err_msg = ["endpoint matching #{region}/#{name}:"]
type = nil
case
when nbr_of_services == 1
type = services[0]
when nbr_of_services > 1
err_msg += [endpoint_from_region_name(region, name).join(' ')]
when nbr_of_services < 1
# Then we try to get the type by service name.
type = type_from_service(name)
end
if !type.nil?
type
else
fail(Puppet::Error, 'Cannot get the correct endpoint type: ' \
"#{err_msg.join(' ')}")
end
end
def self.transform_name(region, name, type)
if type == PuppetX::Keystone::CompositeNamevar::Unset
type = service_type(endpoint_from_region_name(region, name), region, name)
end
if type == PuppetX::Keystone::CompositeNamevar::Unset
Puppet.debug("Could not find the type for endpoint #{region}/#{name}")
"#{region}/#{name}"
else
"#{region}/#{name}::#{type}"
end
end
def self.make_id(endpoint)
id_str = ''
id_sep = ''
[:admin, :internal, :public].each do |type|
id_str += "#{id_sep}#{endpoint[type][:id]}" if endpoint[type]
id_sep = ','
end
id_str
end
def self.get_region(endpoint)
type = [:admin, :internal, :public].detect { |t| endpoint.key? t }
type ? endpoint[type][:region] : ''
end
def self.get_url(endpoint, type, default='')
endpoint[type][:url] rescue default
end
def service_id
# Reset the cache.
self.class.services = nil
name = resource[:name]
type = resource[:type]
services = self.class.services.find_all { |s| s[:name] == name }
service = services.find { |s| s[:type] == type }
service_id = ''
if service.nil? && services.count == 1
# For backward comptatibility, match the service by name only.
service_id = services[0][:id]
else
# Math the service by id.
service_id = service[:id] if service
end
if service_id.nil? || service_id.empty?
title = self.class.transform_name(resource[:region], resource[:name], resource[:type])
fail(Puppet::Error, "Cannot find service associated with #{title}")
end
service_id
end
end