Fix Ironic modules so services properly run in Juno

Initially, the module was not creating an ironic.conf file and now
it does.  The configurations that are being put into the ironic.conf
file come straight out of the documentation.

The package 'python-pbr' is required and was not being installed
elsewhere so it is installed by the init module.

The ironic-conductor service requires the execution of 'ironic-dbsync'
before it can be active.

In the params module, the 'RedHat' packages and services were named
incorrectly or were otherwise missing.

Change-Id: I73cecc590038fa7d9518453c448bfc243ddedff1
This commit is contained in:
Ryan Hallisey 2014-11-12 15:09:31 -05:00
parent a34629f956
commit 556446b82f
9 changed files with 332 additions and 48 deletions

View File

@ -31,7 +31,7 @@ $glance_api_servers = 'glance:9292'
$deploy_kernel = 'glance://deploy_kernel_uuid'
$deploy_ramdisk = 'glance://deploy_ramdisk_uuid'
node db {
node 'db' {
class { 'mysql::server':
config_hash => {

View File

@ -0,0 +1,182 @@
## NB: This must work with Ruby 1.8!
# This provider permits the swift_account paramter in ironic.conf
# to be set by providing a ironic_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'
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
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = url.scheme == 'https'
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(:ironic_admin_tenant_id_setter).provide(:ruby) do
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?
false
end
def create
config
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(:ironic_config).new(
{:name => 'glance/swift_account', :value => "AUTH_#{get_tenant_id}"}
).create
end
end

View File

@ -0,0 +1,31 @@
Puppet::Type.newtype(:ironic_admin_tenant_id_setter) do
ensurable
newparam(:name, :namevar => true) do
desc 'The name of the setting to update'
end
newparam(:tenant_name) do
desc 'The ironic admin tenant name'
end
newparam(:auth_url) do
desc 'The Keystone endpoint URL'
defaultto 'http://localhost:35357/v2.0'
end
newparam(:auth_username) do
desc 'Username with which to authenticate'
defaultto 'admin'
end
newparam(:auth_password) do
desc 'Password with which to authenticate'
end
newparam(:auth_tenant_name) do
desc 'Tenant name with which to authenticate'
defaultto 'admin'
end
end

View File

@ -39,4 +39,9 @@ Puppet::Type.newtype(:ironic_config) do
defaultto false
end
def create
provider.create
end
end

View File

@ -68,9 +68,20 @@
# (optional) The name of the user to create in keystone for use by the ironic services
# Defaults to 'ironic'
#
# [*neutron_url*]
# (optional) The Neutron URL to be used for requests from ironic
# Defaults to false
#
# [*admin_password*]
# (required) The password to set for the ironic admin user in keystone
#
# [*enabled_drivers*]
# The back-end drivers for Ironic
# Defaults to agent_ssh
#
# [*swift_temp_url_key*]
# (required) The temporaty url key for Ironic to access Swift
#
class ironic::api (
$package_ensure = 'present',
@ -79,14 +90,17 @@ class ironic::api (
$port = '6385',
$max_limit = '1000',
$auth_host = '127.0.0.1',
$auth_port = 35357,
$auth_port = '35357',
$auth_protocol = 'http',
$auth_uri = false,
$auth_admin_prefix = false,
$auth_version = false,
$admin_tenant_name = 'services',
$admin_user = 'ironic',
$neutron_url = false,
$enabled_drivers = 'agent_ssh',
$admin_password,
$swift_temp_url_key,
) {
include ironic::params
@ -95,13 +109,6 @@ class ironic::api (
Ironic_config<||> ~> Service['ironic-api']
Class['ironic::policy'] ~> Service['ironic-api']
# Configure ironic.conf
ironic_config {
'api/host_ip': value => $host_ip;
'api/port': value => $port;
'api/max_limit': value => $max_limit;
}
# Install package
if $::ironic::params::api_package {
Package['ironic-api'] -> Class['ironic::policy']
@ -127,6 +134,40 @@ class ironic::api (
hasstatus => true,
}
# Configures 'glance/swift_account'
ironic_admin_tenant_id_setter {'swift_account':
ensure => present,
tenant_name => $admin_tenant_name,
auth_url => "${auth_protocol}://${auth_host}:35357/v2.0",
auth_username => $admin_user,
auth_password => $admin_password,
auth_tenant_name => $admin_tenant_name,
}
# Configure ironic.conf
ironic_config {
'api/host_ip': value => $host_ip;
'api/port': value => $port;
'api/max_limit': value => $max_limit;
'DEFAULT/enabled_drivers': value => $enabled_drivers;
'glance/swift_temp_url_key': value => $swift_temp_url_key, secret => true;
'glance/swift_account': value => $swift_account;
}
# Post temp_url_key to swift
exec { 'swift-temp-url-key-post':
command => "swift post -m temp-url-key:${swift_temp_url_key}",
path => ['/bin', '/usr/bin'],
subscribe => File['/etc/ironic/ironic.conf'],
refreshonly => true,
}
if $neutron_url {
ironic_config { 'neutron/url': value => $neutron_url; }
} else {
ironic_config { 'neutron/url': value => "${auth_protocol}://${auth_host}:9696/"; }
}
if $auth_uri {
ironic_config { 'keystone_authtoken/auth_uri': value => $auth_uri; }
} else {
@ -159,5 +200,4 @@ class ironic::api (
}
}
}

View File

@ -156,25 +156,32 @@ class ironic (
include ironic::params
Package['ironic'] -> Ironic_config<||>
Package['ironic-common'] -> Ironic_config<||>
File {
require => Package['ironic'],
file { '/etc/ironic':
ensure => directory,
require => Package['ironic-common'],
owner => 'root',
group => 'ironic',
mode => '0750',
}
file { '/etc/ironic/ironic.conf':
require => Package['ironic-common'],
owner => 'root',
group => 'ironic',
mode => '0640',
}
file { '/etc/ironic':
ensure => directory,
mode => '0750',
package { 'python-pbr':
ensure => $package_ensure,
name => $::ironic::params::pbr_package,
}
file { '/etc/ironic/ironic.conf': }
package { 'ironic':
ensure => $package_ensure,
name => $::ironic::params::package_name,
package { 'ironic-common':
ensure => $package_ensure,
name => $::ironic::params::common_package_name,
require => Package['python-pbr'],
}
validate_re($database_connection, '(sqlite|mysql|postgresql):\/\/(\S+:\S+@\S+\/\S+)?')
@ -182,7 +189,6 @@ class ironic (
case $database_connection {
/mysql:\/\/\S+:\S+@\S+\/\S+/: {
$database_backend_package = false
require 'mysql::python'
}
/postgresql:\/\/\S+:\S+@\S+\/\S+/: {
$database_backend_package = 'python-psycopg2'
@ -226,6 +232,16 @@ class ironic (
'glance/glance_api_insecure': value => $glance_api_insecure;
}
Ironic_config['database/connection'] ~> Exec['ironic-dbsync']
exec { 'ironic-dbsync':
command => $::ironic::params::dbsync_command,
path => '/usr/bin',
user => 'ironic',
refreshonly => true,
logoutput => on_failure,
}
if $rpc_backend == 'ironic.openstack.common.rpc.impl_kombu' {
if ! $rabbit_password {
fail('When rpc_backend is rabbitmq, you must set rabbit password')

View File

@ -20,22 +20,27 @@
class ironic::params {
$dbsync_command =
'ironic-dbsync --config-file /etc/ironic/ironic.conf'
case $::osfamily {
'RedHat': {
$package_name = 'openstack-ironic'
$api_package = false
$conductor_package = false
$api_service = 'ironic-api'
$conductor_service = 'ironic-conductor'
$client_package = 'python-ironicclient'
$common_package_name = 'openstack-ironic-common'
$api_package = 'openstack-ironic-api'
$api_service = 'openstack-ironic-api'
$conductor_package = 'openstack-ironic-conductor'
$conductor_service = 'openstack-ironic-conductor'
$client_package = 'python-ironicclient'
$pbr_package = 'python-pbr'
}
'Debian': {
$package_name = 'ironic-common'
$api_service = 'ironic-api'
$conductor_service = 'ironic-conductor'
$api_package = 'ironic-api'
$conductor_package = 'ironic-conductor'
$client_package = 'python-ironicclient'
$common_package_name = 'ironic-common'
$api_service = 'ironic-api'
$api_package = 'ironic-api'
$conductor_service = 'ironic-conductor'
$conductor_package = 'ironic-conductor'
$client_package = 'python-ironicclient'
$pbr_package = 'python-pbr'
}
default: {
fail("Unsupported osfamily ${::osfamily}")

View File

@ -23,17 +23,19 @@ require 'spec_helper'
describe 'ironic::api' do
let :default_params do
{ :package_ensure => 'present',
:enabled => true,
:port => '6385',
:max_limit => '1000',
:host_ip => '0.0.0.0',
:admin_user => 'ironic',
{ :package_ensure => 'present',
:enabled => true,
:port => '6385',
:max_limit => '1000',
:host_ip => '0.0.0.0',
:admin_user => 'ironic',
:enabled_drivers => 'agent_ssh',
}
end
let :params do
{ :admin_password => 'thepassword' }
{ :admin_password => 'thepassword',
:swift_temp_url_key => 'thepassword'}
end
shared_examples_for 'ironic api' do
@ -42,7 +44,6 @@ describe 'ironic::api' do
end
it { should contain_class('ironic::params') }
it { should contain_class('ironic::policy') }
it 'installs ironic api package' do
if platform_params.has_key?(:api_package)
@ -66,6 +67,9 @@ describe 'ironic::api' do
should contain_ironic_config('keystone_authtoken/admin_password').with_value(p[:admin_password])
should contain_ironic_config('keystone_authtoken/admin_user').with_value(p[:admin_user])
should contain_ironic_config('keystone_authtoken/auth_uri').with_value('http://127.0.0.1:5000/')
should contain_ironic_config('glance/swift_temp_url_key').with_value(p[:swift_temp_url_key])
should contain_ironic_config('DEFAULT/enabled_drivers').with_value(p[:enabled_drivers])
should contain_ironic_config('neutron/url').with_value('http://127.0.0.1:9696/')
end
context 'when overriding parameters' do
@ -74,8 +78,8 @@ describe 'ironic::api' do
:port => '3430',
:host_ip => '127.0.0.1',
:max_limit => '10',
:auth_protocol => 'https',
:auth_host => '1.2.3.4'
:auth_protocol => 'https',
:auth_host => '1.2.3.4'
)
end
it 'should replace default parameter with new value' do

View File

@ -102,7 +102,8 @@ describe 'ironic' do
:owner => 'root',
:group => 'ironic',
:mode => '0750',
:require => 'Package[ironic]'
:require => 'Package[python-pbr]',
:require => 'Package[ironic-common]'
)
end
@ -111,12 +112,12 @@ describe 'ironic' do
:owner => 'root',
:group => 'ironic',
:mode => '0640',
:require => 'Package[ironic]'
:require => 'Package[ironic-common]'
)
end
it 'installs ironic package' do
should contain_package('ironic').with(
should contain_package('ironic-common').with(
:ensure => 'present',
:name => platform_params[:common_package_name]
)
@ -233,7 +234,7 @@ describe 'ironic' do
end
let :platform_params do
{ :common_package_name => 'openstack-ironic' }
{ :common_package_name => 'openstack-ironic-common' }
end
it_configures 'ironic'