Files
puppet-openstacklib/lib/puppet/provider/openstack.rb
Gilles Dubreuil 67d1c38703 Restructures authentication for resource providers
Workflow to find credentials details from:
  1. The environment variables (ENV['OS_*'])
  2. If not enough, credentials available from the environment RC file
     to be used. Default RC file must be in current user homedir: ~/openrc

  - Adds module Puppet::Provider::Openstack::Auth providing class methods for
  providers instead of relying on superclass inheritance.
  As discussed [1], this purposefully removes the possibility to pass authenti-
  cation details as parameters to Puppet resources instances.

  - Wraps credentials information with object:
    Puppet::Provider::Openstack::Credentials

  - The credentials information is used in a withenv block

  Review for puppet-keystone needs to be merged at same time:
  https://review.openstack.org/#/c/181299/

  [1] http://lists.openstack.org/pipermail/openstack-dev/2015-May/063352.html

Change-Id: If628f4ad95f3aac3392475d4ea6857fb858f8755
Implements: blueprint auth-restructure
2015-05-28 09:44:54 +10:00

85 lines
2.9 KiB
Ruby

require 'csv'
require 'puppet'
class Puppet::Error::OpenstackAuthInputError < Puppet::Error
end
class Puppet::Error::OpenstackUnauthorizedError < Puppet::Error
end
class Puppet::Provider::Openstack < Puppet::Provider
initvars # so commands will work
commands :openstack => 'openstack'
# Returns an array of hashes, where the keys are the downcased CSV headers
# with underscores instead of spaces
def self.request(service, action, properties, credentials=nil)
env = credentials ? credentials.to_env : {}
Puppet::Util.withenv(env) do
rv = nil
timeout = 10
end_time = Time.now.to_i + timeout
loop do
begin
if(action == 'list')
response = openstack(service, action, '--quiet', '--format', 'csv', properties)
response = parse_csv(response)
keys = response.delete_at(0) # ID,Name,Description,Enabled
rv = response.collect do |line|
hash = {}
keys.each_index do |index|
key = keys[index].downcase.gsub(/ /, '_').to_sym
hash[key] = line[index]
end
hash
end
elsif(action == 'show' || action == 'create')
rv = {}
# shell output is name="value"\nid="value2"\ndescription="value3" etc.
openstack(service, action, '--format', 'shell', properties).split("\n").each do |line|
# key is everything before the first "="
key, val = line.split("=", 2)
next unless val # Ignore warnings
# value is everything after the first "=", with leading and trailing double quotes stripped
val = val.gsub(/\A"|"\Z/, '')
rv[key.downcase.to_sym] = val
end
else
rv = openstack(service, action, properties)
end
break
rescue Puppet::ExecutionFailure => e
if e.message =~ /HTTP 401/
raise(Puppet::Error::OpenstackUnauthorizedError, 'Could not authenticate.')
elsif e.message =~ /Unable to establish connection/
current_time = Time.now.to_i
if current_time > end_time
break
else
wait = end_time - current_time
Puppet::debug("Non-fatal error: \"#{e.message}\"; retrying for #{wait} more seconds.")
if wait > timeout - 2 # Only notice the first time
notice("#{service} service is unavailable. Will retry for up to #{wait} seconds.")
end
end
sleep(2)
else
raise e
end
end
end
return rv
end
end
private
def self.parse_csv(text)
# Ignore warnings - assume legitimate output starts with a double quoted
# string. Errors will be caught and raised prior to this
text = text.split("\n").drop_while { |line| line !~ /^\".*\"/ }.join("\n")
return CSV.parse(text + "\n")
end
end