Files
puppet-openstacklib/lib/puppet/provider/aviator.rb
Colleen Murphy 612fa7e121 Implement base aviator provider
This patch adds a dependency on the aimonb/aviator module and adds
functionality to support interactions with the OpenStack services' API
via aviator.

The patch adds a parent provider that is intended for other providers
to inherit from. The parent provider has methods to authenticate to
openstack services, create session objects, and make requests. The
authenticate method can accept credentials as an argument hash or infer
credentials from the environment.

It also adds a stub type parameter that allows types to incorporate
basic parameters they need in order to support using aviator.

Change-Id: I56b0d07ae8f4738037eda486b75a0f6e24fe80e7
Implements: blueprint use-aviator-in-module-resources
2014-09-30 21:01:28 -07:00

298 lines
8.3 KiB
Ruby

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