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:
parent
a34629f956
commit
556446b82f
|
@ -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 => {
|
||||
|
|
|
@ -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
|
||||
|
|
@ -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
|
|
@ -39,4 +39,9 @@ Puppet::Type.newtype(:ironic_config) do
|
|||
|
||||
defaultto false
|
||||
end
|
||||
|
||||
def create
|
||||
provider.create
|
||||
end
|
||||
|
||||
end
|
||||
|
|
|
@ -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 (
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
|
|
@ -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')
|
||||
|
|
|
@ -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}")
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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'
|
||||
|
|
Loading…
Reference in New Issue