+++ /dev/null
-## NB: This must work with Ruby 1.8!
-
-# This providers permits the nova_admin_tenant_id paramter in neutron.conf
-# to be set by providing a nova_admin_tenant_name to the Puppet module and
-# using the Keystone REST API to translate the name into the corresponding
-# UUID.
-#
-# This requires that tenant names be unique. If there are multiple matches
-# for a given tenant name, this provider will raise an exception.
-
-require 'rubygems'
-require 'net/http'
-require 'net/https'
-require 'json'
-require 'puppet/util/inifile'
-
-class KeystoneError < Puppet::Error
-end
-
-class KeystoneConnectionError < KeystoneError
-end
-
-class KeystoneAPIError < KeystoneError
-end
-
-# Provides common request handling semantics to the other methods in
-# this module.
-#
-# +req+::
-# An HTTPRequest object
-# +url+::
-# A parsed URL (returned from URI.parse)
-def handle_request(req, url)
- begin
- # There is issue with ipv6 where address has to be in brackets, this causes the
- # underlying ruby TCPSocket to fail. Net::HTTP.new will fail without brackets on
- # joining the ipv6 address with :port or passing brackets to TCPSocket. It was
- # found that if we use Net::HTTP.start with url.hostname the incriminated code
- # won't be hit.
- use_ssl = url.scheme == "https" ? true : false
- http = Net::HTTP.start(url.hostname, url.port, {:use_ssl => use_ssl})
- res = http.request(req)
-
- if res.code != '200'
- raise KeystoneAPIError, "Received error response from Keystone server at #{url}: #{res.message}"
- end
- rescue Errno::ECONNREFUSED => detail
- raise KeystoneConnectionError, "Failed to connect to Keystone server at #{url}: #{detail}"
- rescue SocketError => detail
- raise KeystoneConnectionError, "Failed to connect to Keystone server at #{url}: #{detail}"
- end
-
- res
-end
-
-# Authenticates to a Keystone server and obtains an authentication token.
-# It returns a 2-element +[token, authinfo]+, where +token+ is a token
-# suitable for passing to openstack apis in the +X-Auth-Token+ header, and
-# +authinfo+ is the complete response from Keystone, including the service
-# catalog (if available).
-#
-# +auth_url+::
-# Keystone endpoint URL. This function assumes API version
-# 2.0 and an administrative endpoint, so this will typically look like
-# +http://somehost:35357/v2.0+.
-#
-# +username+::
-# Username for authentication.
-#
-# +password+::
-# Password for authentication
-#
-# +tenantID+::
-# Tenant UUID
-#
-# +tenantName+::
-# Tenant name
-#
-def keystone_v2_authenticate(auth_url,
- username,
- password,
- tenantId=nil,
- tenantName=nil)
-
- post_args = {
- 'auth' => {
- 'passwordCredentials' => {
- 'username' => username,
- 'password' => password
- },
- }}
-
- if tenantId
- post_args['auth']['tenantId'] = tenantId
- end
-
- if tenantName
- post_args['auth']['tenantName'] = tenantName
- end
-
- url = URI.parse("#{auth_url}/tokens")
- req = Net::HTTP::Post.new url.path
- req['content-type'] = 'application/json'
- req.body = post_args.to_json
-
- res = handle_request(req, url)
- data = JSON.parse res.body
- return data['access']['token']['id']
-end
-
-# Queries a Keystone server to a list of all tenants.
-#
-# +auth_url+::
-# Keystone endpoint. See the notes for +auth_url+ in
-# +keystone_v2_authenticate+.
-#
-# +token+::
-# A Keystone token that will be passed in requests as the value of the
-# +X-Auth-Token+ header.
-#
-def keystone_v2_tenants(auth_url,
- token)
-
- url = URI.parse("#{auth_url}/tenants")
- req = Net::HTTP::Get.new url.path
- req['content-type'] = 'application/json'
- req['x-auth-token'] = token
-
- res = handle_request(req, url)
- data = JSON.parse res.body
- data['tenants']
-end
-
-Puppet::Type.type(:nova_admin_tenant_id_setter).provide(:ruby) do
- @tenant_id = nil
-
- def authenticate
- keystone_v2_authenticate(
- @resource[:auth_url],
- @resource[:auth_username],
- @resource[:auth_password],
- nil,
- @resource[:auth_tenant_name])
- end
-
- def find_tenant_by_name (token)
- tenants = keystone_v2_tenants(
- @resource[:auth_url],
- token)
-
- tenants.select{|tenant| tenant['name'] == @resource[:tenant_name]}
- end
-
- def exists?
- ini_file = Puppet::Util::IniConfig::File.new
- ini_file.read("/etc/neutron/neutron.conf")
- ini_file['DEFAULT'] && ini_file['DEFAULT']['nova_admin_tenant_id'] && ini_file['DEFAULT']['nova_admin_tenant_id'] == tenant_id
- end
-
- def create
- config
- end
-
- def tenant_id
- @tenant_id ||= get_tenant_id
- end
-
- # This looks for the tenant specified by the 'tenant_name' parameter to
- # the resource and returns the corresponding UUID if there is a single
- # match.
- #
- # Raises a KeystoneAPIError if:
- #
- # - There are multiple matches, or
- # - There are zero matches
- def get_tenant_id
- token = authenticate
- tenants = find_tenant_by_name(token)
-
- if tenants.length == 1
- return tenants[0]['id']
- elsif tenants.length > 1
- raise KeystoneAPIError, 'Found multiple matches for tenant name'
- else
- raise KeystoneAPIError, 'Unable to find matching tenant'
- end
- end
-
- def config
- Puppet::Type.type(:neutron_config).new(
- {:name => 'DEFAULT/nova_admin_tenant_id', :value => "#{tenant_id}"}
- ).create
- end
-
-end
-