--- /dev/null
+require 'puppet'
+require 'puppet/feature/aviator'
+require 'puppet/util/inifile'
+
+class Puppet::Provider::Aviator < Puppet::Provider
+
+ def session
+ @session ||= authenticate(resource[:auth], resource[:log_file])
+ end
+
+ def self.session
+ @session ||= authenticate(nil, nil)
+ end
+
+ def request(service, request, &block)
+ self.class.make_request(service, request, session_data, &block)
+ end
+
+ def self.request(service, request, &block)
+ self.make_request(service, request, session_data, &block)
+ end
+
+ # needed for tests
+ def session_data
+ @session_data
+ end
+
+ def self.session_data
+ @session_data
+ end
+
+ def session_data=(data)
+ @session_data=data
+ end
+
+ def self.session_data=(data)
+ @session_data=data
+ end
+
+ private
+
+ # Attempt to find credentials in this order:
+ # 1. username,password,tenant,host set in type parameters
+ # 2. openrc file path set in type parameters
+ # 3. service token and host set in type parameters
+ # 4. username,password,tenant,host set in environment variables
+ # 5. service token and host set in keystone.conf (backwards compatible version)
+ def authenticate(auth_params, log_file)
+ auth_params ||= {}
+ if password_credentials_set?(auth_params)
+ @session = get_authenticated_session(auth_params, log_file)
+
+ elsif openrc_set?(auth_params)
+ credentials = get_credentials_from_openrc(auth_params['openrc'])
+ @session = get_authenticated_session(credentials, log_file)
+
+ elsif service_credentials_set?(auth_params)
+ session_hash = get_unauthenticated_session(auth_params, log_file)
+ @session_data = session_hash[:data]
+ @session = session_hash[:session]
+
+ elsif env_vars_set?
+ credentials = get_credentials_from_env
+ @session = get_authenticated_session(credentials, log_file)
+
+ else # Last effort: try to get the token from keystone.conf
+ session_hash = self.class.try_auth_with_token(keystone_file, log_file)
+ @session_data = session_hash[:data]
+ @session = session_hash[:session]
+ end
+ end
+
+ def self.authenticate(auth_params, log_file)
+ auth_params = {} unless auth_params
+ if env_vars_set?
+ credentials = get_credentials_from_env
+ @session = get_authenticated_session(credentials, log_file)
+
+ else # Last effort: try to get the token from keystone.conf
+ session_hash = try_auth_with_token(keystone_file, log_file)
+ @session_data = session_hash[:data]
+ @session = session_hash[:session]
+ end
+ end
+
+
+ def self.try_auth_with_token(conf_file, log_file)
+ service_token = get_admin_token_from_keystone_file(conf_file)
+ auth_url = get_auth_url_from_keystone_file(conf_file)
+ session_hash = {}
+ if service_token
+ credentials = {
+ 'service_token' => service_token,
+ 'host_uri' => auth_url,
+ }
+ session_hash = get_unauthenticated_session(credentials, log_file)
+ else # All authentication efforts failed
+ raise(Puppet::Error, 'No credentials provided.')
+ end
+ end
+
+
+ def self.make_request(service, request, session_data, &block)
+ response = nil
+ if service && service.default_session_data
+ response = service.request(request, :endpoint_type => 'admin') do |params|
+ yield(params) if block
+ end
+ elsif session_data
+ response = service.request(request, :endpoint_type => 'admin',
+ :session_data => session_data) do |params|
+ yield(params) if block
+ end
+ else
+ raise(Puppet::Error, 'Cannot make a request with no session data.')
+ end
+ if response.body.hash['error']
+ raise(Puppet::Error, "Error making request: #{response.body.hash['error']['code']} #{response.body.hash['error']['title']}")
+ end
+ response
+ end
+
+
+ def password_credentials_set?(auth_params)
+ auth_params['username'] && auth_params['password'] && auth_params['tenant_name'] && auth_params['host_uri']
+ end
+
+
+ def openrc_set?(auth_params)
+ auth_params['openrc']
+ end
+
+
+ def service_credentials_set?(auth_params)
+ auth_params['service_token'] && auth_params['host_uri']
+ end
+
+
+ def self.env_vars_set?
+ ENV['OS_USERNAME'] && ENV['OS_PASSWORD'] && ENV['OS_TENANT_NAME'] && ENV['OS_AUTH_URL']
+ end
+
+
+ def env_vars_set?
+ self.class.env_vars_set?
+ end
+
+
+ def get_credentials_from_openrc(file)
+ creds = {}
+ begin
+ File.open(file).readlines.delete_if{|l| l=~ /^#/}.each do |line|
+ key, value = line.split('=')
+ key = key.split(' ').last
+ value = value.chomp.gsub(/'/, '')
+ creds[key] = value
+ end
+ return creds
+ rescue Exception => error
+ return {}
+ end
+ end
+
+
+ def self.get_credentials_from_env
+ ENV.to_hash.dup.delete_if { |key, _| ! (key =~ /^OS/) } # Ruby 1.8.7
+ end
+
+ def get_credentials_from_env
+ self.class.get_credentials_from_env
+ end
+
+
+ def self.keystone_file
+ keystone_file = Puppet::Util::IniConfig::File.new
+ keystone_file.read('/etc/keystone/keystone.conf')
+ keystone_file
+ end
+
+ def keystone_file
+ return @keystone_file if @keystone_file
+ @keystone_file = Puppet::Util::IniConfig::File.new
+ @keystone_file.read('/etc/keystone/keystone.conf')
+ @keystone_file
+ end
+
+
+ def self.get_admin_token_from_keystone_file(conf_file)
+ if conf_file and conf_file['DEFAULT'] and conf_file['DEFAULT']['admin_token']
+ return "#{conf_file['DEFAULT']['admin_token'].strip}"
+ else
+ return nil
+ end
+ end
+
+ def get_admin_token_from_keystone_file
+ conf_file = keystone_file
+ self.class.get_admin_token_from_keystone_file(conf_file)
+ end
+
+
+ def self.get_auth_url_from_keystone_file(conf_file)
+ if conf_file
+ if conf_file['DEFAULT']
+ if conf_file['DEFAULT']['admin_endpoint']
+ auth_url = conf_file['DEFAULT']['admin_endpoint'].strip
+ return versioned_endpoint(auth_url)
+ end
+
+ if conf_file['DEFAULT']['admin_port']
+ admin_port = conf_file['DEFAULT']['admin_port'].strip
+ else
+ admin_port = '35357'
+ end
+
+ if conf_file['DEFAULT']['admin_bind_host']
+ host = conf_file['DEFAULT']['admin_bind_host'].strip
+ if host == "0.0.0.0"
+ host = "127.0.0.1"
+ end
+ else
+ host = "127.0.0.1"
+ end
+ end
+
+ if conf_file['ssl'] && conf_file['ssl']['enable'] && conf_file['ssl']['enable'].strip.downcase == 'true'
+ protocol = 'https'
+ else
+ protocol = 'http'
+ end
+ end
+
+ "#{protocol}://#{host}:#{admin_port}/v2.0/"
+ end
+
+ def get_auth_url_from_keystone_file
+ self.class.get_auth_url_from_keystone_file(keystone_file)
+ end
+
+
+ def self.make_configuration(credentials)
+ host_uri = versioned_endpoint(credentials['host_uri'] || credentials['OS_AUTH_URL'], credentials['api_version'])
+ {
+ :provider => 'openstack',
+ :auth_service => {
+ :name => 'identity',
+ :host_uri => host_uri,
+ :request => 'create_token',
+ :validator => 'list_tenants',
+ },
+ :auth_credentials => {
+ :username => credentials['username'] || credentials['OS_USERNAME'],
+ :password => credentials['password'] || credentials['OS_PASSWORD'],
+ :tenant_name => credentials['tenant_name'] || credentials['OS_TENANT_NAME']
+ }
+ }
+ end
+
+
+ def self.get_authenticated_session(credentials, log_file)
+ configuration = make_configuration(credentials)
+ session = ::Aviator::Session.new(:config => configuration, :log_file => log_file)
+ session.authenticate
+ session
+ end
+
+ def get_authenticated_session(credentials, log_file)
+ self.class.get_authenticated_session(credentials, log_file)
+ end
+
+
+ def self.get_unauthenticated_session(credentials, log_file)
+ configuration = {
+ :provider => 'openstack',
+ }
+ session_data = {
+ :base_url => credentials['host_uri'],
+ :service_token => credentials['service_token']
+ }
+ session = ::Aviator::Session.new(:config => configuration, :log_file => log_file)
+ { :session => session, :data => session_data }
+ end
+
+ def get_unauthenticated_session(credentials, log_file)
+ self.class.get_unauthenticated_session(credentials, log_file)
+ end
+
+
+ def self.versioned_endpoint(endpoint, version = 'v2.0')
+ version = 'v2.0' if version.nil?
+ if endpoint =~ /\/#{version}\/?$/ || endpoint =~ /\/v2.0\/?$/ || endpoint =~ /\/v3\/?$/
+ endpoint
+ else
+ "#{endpoint.chomp('/')}/#{version}"
+ end
+ end
+end