Add setters for images and neutron

Change-Id: I2d7555f5d5f5276509fa9752a2922244cc6f0f2d
This commit is contained in:
Andrey Shestakov 2015-08-21 12:20:53 +03:00
parent bd3d860645
commit e09382edd6
60 changed files with 4374 additions and 4 deletions

3
.gitmodules vendored
View File

@ -1,6 +1,3 @@
[submodule "deployment_scripts/puppet/modules/tftp"]
path = deployment_scripts/puppet/modules/tftp
url = https://github.com/puppetlabs/puppetlabs-tftp
[submodule "deployment_scripts/puppet/modules/ironic"]
path = deployment_scripts/puppet/modules/ironic
url = https://github.com/openstack/puppet-ironic

View File

@ -0,0 +1,41 @@
notice('MODULAR: ironic/ironic-conductor-config.pp')
$ironic_hash = hiera_hash('fuel-plugin-ironic', {})
$management_vip = hiera('management_vip')
$keystone_endpoint = hiera('keystone_endpoint', $management_vip)
$neutron_endpoint = hiera('neutron_endpoint', $management_vip)
$ironic_tenant = pick($ironic_hash['tenant'],'services')
$ironic_user = pick($ironic_hash['user'],'ironic')
$ironic_user_password = pick($ironic_hash['password'],'ironic')
include ::ironic::params
ironic_images_setter {'ironic_images':
ensure => present,
auth_url => "http://${keystone_endpoint}:5000/v2.0/",
auth_username => $ironic_user,
auth_password => $ironic_user_password,
auth_tenant_name => $ironic_tenant,
glance_url => "http://${management_vip}:9292/v2.0/",
}
ironic_neutron_setter {'ironic_network':
ensure => present,
auth_url => "http://${keystone_endpoint}:5000/v2.0/",
auth_username => $ironic_user,
auth_password => $ironic_user_password,
auth_tenant_name => $ironic_tenant,
neutron_url => "http://${neutron_endpoint}:9696/v2.0/",
}
service { 'ironic-conductor':
ensure => 'running',
name => $::ironic::params::conductor_service,
enable => true,
hasstatus => true,
tag => 'ironic-service',
}
Ironic_images_setter<||> ~> Service['ironic-conductor']
Ironic_neutron_setter<||> ~> Service['ironic-conductor']

@ -1 +0,0 @@
Subproject commit 69fa70013893a323a7cf62bc57963bd7a86bab04

View File

@ -0,0 +1,14 @@
fixtures:
repositories:
'inifile': 'git://github.com/puppetlabs/puppetlabs-inifile'
'concat':
'repo': 'git://github.com/puppetlabs/puppetlabs-concat.git'
'ref': '1.2.1'
'keystone': 'git://github.com/openstack/puppet-keystone.git'
'mysql': 'git://github.com/puppetlabs/puppetlabs-mysql.git'
'openstacklib': 'git://github.com/openstack/puppet-openstacklib.git'
'postgresql': 'git://github.com/puppetlabs/puppet-postgresql.git'
'stdlib': 'git://github.com/puppetlabs/puppetlabs-stdlib.git'
'vcsrepo': 'git://github.com/puppetlabs/puppetlabs-vcsrepo.git'
symlinks:
'ironic': "#{source_dir}"

View File

@ -0,0 +1,5 @@
*.swp
spec/fixtures/modules/*
spec/fixtures/manifests/site.pp
Gemfile.lock
.vendor

View File

@ -0,0 +1,4 @@
[gerrit]
host=review.openstack.org
port=29418
project=openstack/puppet-ironic.git

View File

@ -0,0 +1,4 @@
##2015-07-08 - 6.0.0
###Summary
- Initial release of the puppet-ironic module

View File

@ -0,0 +1,30 @@
source 'https://rubygems.org'
group :development, :test do
gem 'puppetlabs_spec_helper', :require => false
gem 'rspec-puppet', '~> 2.1.0', :require => false
gem 'metadata-json-lint'
gem 'puppet-lint-absolute_classname-check'
gem 'puppet-lint-absolute_template_path'
gem 'puppet-lint-trailing_newline-check'
# Puppet 4.x related lint checks
gem 'puppet-lint-unquoted_string-check'
gem 'puppet-lint-leading_zero-check'
gem 'puppet-lint-variable_contains_upcase'
gem 'puppet-lint-numericvariable'
gem 'beaker-rspec', :require => false
gem 'beaker-puppet_install_helper', :require => false
gem 'json'
gem 'webmock'
end
if puppetversion = ENV['PUPPET_GEM_VERSION']
gem 'puppet', puppetversion, :require => false
else
gem 'puppet', :require => false
end
# vim:ft=ruby

View File

@ -0,0 +1,176 @@
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.

View File

@ -0,0 +1,105 @@
puppet-ironic
=============
6.0.0 - 2015.1 - Kilo
#### Table of Contents
1. [Overview - What is the ironic module?](#overview)
2. [Module Description - What does the module do?](#module-description)
3. [Setup - The basics of getting started with ironic](#setup)
4. [Implementation - An under-the-hood peek at what the module is doing](#implementation)
5. [Limitations - OS compatibility, etc.](#limitations)
6. [Development - Guide for contributing to the module](#development)
7. [Contributors - Those with commits](#contributors)
Overview
--------
The ironic module is a part of [OpenStack](https://github.com/openstack), an effort by the Openstack infrastructure team to provide continuous integration testing and code review for Openstack and Openstack community projects as part of the core software. The module itself is used to flexibly configure and manage the baremetal service for Openstack.
Module Description
------------------
Setup
-----
**What the ironic module affects:**
* [Ironic](https://wiki.openstack.org/wiki/Ironic), the baremetal service for Openstack.
### Installing Ironic
puppet module install openstack/ironic
### Beginning with ironic
To utilize the ironic module's functionality you will need to declare multiple resources.
The following is a modified excerpt from the [openstack module](httpd://github.com/stackforge/puppet-openstack).
This is not an exhaustive list of all the components needed. We recommend that you consult and understand the
[openstack module](https://github.com/stackforge/puppet-openstack) and the [core openstack](http://docs.openstack.org)
documentation to assist you in understanding the available deployment options.
```puppet
# enable Ironic resources
class { '::ironic':
rabbit_userid => 'ironic',
rabbit_password => 'an_even_bigger_secret',
rabbit_host => '127.0.0.1',
database_connection => 'mysql://ironic:a_big_secret@127.0.0.1/ironic?charset=utf8',
}
class { '::ironic::db::mysql':
password => 'a_big_secret',
}
class { '::ironic::keystone::auth':
password => 'a_big_secret',
}
class { '::ironic::client': }
class { '::ironic::conductor': }
class { '::ironic::api':
admin_password => 'a_big_secret',
}
class { '::ironic::drivers::ipmi': }
```
Examples of usage also can be found in the *examples* directory.
Implementation
--------------
### puppet-ironic
puppet-ironic is a combination of Puppet manifest and ruby code to delivery configuration and extra functionality through types and providers.
Limitations
-----------
Beaker-Rspec
------------
This module has beaker-rspec tests
To run:
``shell
bundle install
bundle exec rspec spec/acceptance
``
Development
-----------
Developer documentation for the entire puppet-openstack project.
* https://wiki.openstack.org/wiki/Puppet-openstack#Developer_documentation
Contributors
------------
* https://github.com/openstack/puppet-ironic/graphs/contributors

View File

@ -0,0 +1,9 @@
require 'puppetlabs_spec_helper/rake_tasks'
require 'puppet-lint/tasks/puppet-lint'
PuppetLint.configuration.fail_on_warnings = true
PuppetLint.configuration.send('disable_80chars')
PuppetLint.configuration.send('disable_class_parameter_defaults')
task(:default).clear
task :default => [:spec, :lint]

View File

@ -0,0 +1,119 @@
#
# Copyright (C) 2013 eNovance SAS <licensing@enovance.com>
#
# Author: Emilien Macchi <emilien.macchi@enovance.com>
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
#
# Deploy Ironic
#
$db_host = 'db'
$db_username = 'ironic'
$db_name = 'ironic'
$db_password = 'password'
$rabbit_user = 'ironic'
$rabbit_password = 'ironic'
$rabbit_vhost = '/'
$rabbit_hosts = ['rabbitmq:5672']
$rabbit_port = '5672'
$glance_api_servers = 'glance:9292'
$deploy_kernel = 'glance://deploy_kernel_uuid'
$deploy_ramdisk = 'glance://deploy_ramdisk_uuid'
$baremetal_json_hosts = '
"ironic-bm-test.bifrost.example": {
"ansible_ssh_host": "1.1.1.1",
"uuid": "11111111-1111-1111-1111-111111111111",
"driver_info": {
"power": {
"ipmi_address": "10.0.0.1",
"ipmi_username": "admin",
"ipmi_password": "pass"
},
},
"nics": [
{
"mac": "ff:ff:ff:ff:ff:ff"
}
],
"driver": "agent_ipmitool",
"ipv4_address": "1.1.1.1",
"properties": {
"cpu_arch": "x86_64",
"ram": null,
"disk_size": null,
"cpus": null
},
"name": "ironic-bm-test.bifrost.example"
}
'
node 'db' {
class { '::mysql::server':
config_hash => {
'bind_address' => '0.0.0.0',
},
}
class { '::mysql::ruby': }
class { '::ironic::db::mysql':
password => $db_password,
dbname => $db_name,
user => $db_username,
host => $clientcert,
allowed_hosts => ['controller'],
}
}
node controller {
class { '::ironic':
db_password => $db_password,
db_name => $db_name,
db_user => $db_username,
db_host => $db_host,
rabbit_password => $rabbit_password,
rabbit_userid => $rabbit_user,
rabbit_virtual_host => $rabbit_vhost,
rabbit_hosts => $rabbit_hosts,
glance_api_servers => $glance_api_servers,
}
class { '::ironic::api': }
class { '::ironic::conductor': }
class { '::ironic::drivers::ipmi': }
class { '::ironic::drivers::pxe':
deploy_kernel => $deploy_kernel,
deploy_ramdisk => $deploy_ramdisk,
}
}
node bifrost-controller {
class { '::ironic::bifrost':
network_interface => 'eth1',
ironic_db_password => 'changeme',
mysql_password => 'changemetoo',
baremetal_json_hosts => $baremetal_json_hosts,
}
}

View File

@ -0,0 +1,150 @@
require 'csv'
require 'puppet/util/inifile'
class Puppet::Provider::Ironic < Puppet::Provider
def self.conf_filename
'/etc/ironic/ironic.conf'
end
def self.withenv(hash, &block)
saved = ENV.to_hash
hash.each do |name, val|
ENV[name.to_s] = val
end
yield
ensure
ENV.clear
saved.each do |name, val|
ENV[name] = val
end
end
def self.ironic_credentials
@ironic_credentials ||= get_ironic_credentials
end
def self.get_ironic_credentials
auth_keys = ['auth_host', 'auth_port', 'auth_protocol',
'admin_tenant_name', 'admin_user', 'admin_password']
conf = ironic_conf
if conf and conf['keystone_authtoken'] and
auth_keys.all?{|k| !conf['keystone_authtoken'][k].nil?}
return Hash[ auth_keys.map \
{ |k| [k, conf['keystone_authtoken'][k].strip] } ]
else
raise(Puppet::Error, "File: #{conf_filename} does not contain all \
required sections. Ironic types will not work if ironic is not \
correctly configured.")
end
end
def ironic_credentials
self.class.ironic_credentials
end
def self.auth_endpoint
@auth_endpoint ||= get_auth_endpoint
end
def self.get_auth_endpoint
q = ironic_credentials
"#{q['auth_protocol']}://#{q['auth_host']}:#{q['auth_port']}/v2.0/"
end
def self.ironic_conf
return @ironic_conf if @ironic_conf
@ironic_conf = Puppet::Util::IniConfig::File.new
@ironic_conf.read(conf_filename)
@ironic_conf
end
def self.auth_ironic(*args)
q = ironic_credentials
authenv = {
:OS_AUTH_URL => self.auth_endpoint,
:OS_USERNAME => q['admin_user'],
:OS_TENANT_NAME => q['admin_tenant_name'],
:OS_PASSWORD => q['admin_password']
}
begin
withenv authenv do
ironic(args)
end
rescue Exception => e
if (e.message =~ /\[Errno 111\] Connection refused/) or
(e.message =~ /\(HTTP 400\)/)
sleep 10
withenv authenv do
ironic(args)
end
else
raise(e)
end
end
end
def auth_ironic(*args)
self.class.auth_ironic(args)
end
def self.reset
@ironic_conf = nil
@ironic_credentials = nil
end
def self.list_ironic_resources(type)
ids = []
list = auth_ironic("#{type}-list", '--format=csv',
'--column=id', '--quote=none')
(list.split("\n")[1..-1] || []).compact.collect do |line|
ids << line.strip
end
return ids
end
def self.get_ironic_resource_attrs(type, id)
attrs = {}
net = auth_ironic("#{type}-show", '--format=shell', id)
last_key = nil
(net.split("\n") || []).compact.collect do |line|
if line.include? '='
k, v = line.split('=', 2)
attrs[k] = v.gsub(/\A"|"\Z/, '')
last_key = k
else
# Handle the case of a list of values
v = line.gsub(/\A"|"\Z/, '')
attrs[last_key] = [attrs[last_key], v]
end
end
return attrs
end
def self.get_tenant_id(catalog, name)
instance_type = 'keystone_tenant'
instance = catalog.resource("#{instance_type.capitalize!}[#{name}]")
if ! instance
instance = Puppet::Type.type(instance_type).instances.find do |i|
i.provider.name == name
end
end
if instance
return instance.provider.id
else
fail("Unable to find #{instance_type} for name #{name}")
end
end
def self.parse_creation_output(data)
hash = {}
data.split("\n").compact.each do |line|
if line.include? '='
hash[line.split('=').first] = line.split('=', 2)[1].gsub(/\A"|"\Z/, '')
end
end
hash
end
end

View File

@ -0,0 +1,27 @@
Puppet::Type.type(:ironic_config).provide(
:ini_setting,
:parent => Puppet::Type.type(:ini_setting).provider(:ruby)
) do
def section
resource[:name].split('/', 2).first
end
def setting
resource[:name].split('/', 2).last
end
def separator
'='
end
def self.file_path
'/etc/ironic/ironic.conf'
end
# added for backwards compatibility with older versions of inifile
def file_path
self.class.file_path
end
end

View File

@ -0,0 +1,150 @@
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
RETRY_COUNT = 10
RETRY_SLEEP = 3
def handle_request(req, url)
begin
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
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
def glance_images(glance_url, token)
url = URI.parse("#{glance_url}/images")
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['images']
end
Puppet::Type.type(:ironic_images_setter).provide(:ruby) do
@ironic_images = nil
def authenticate
keystone_v2_authenticate(
@resource[:auth_url],
@resource[:auth_username],
@resource[:auth_password],
nil,
@resource[:auth_tenant_name])
end
def find_image_by_name(images, name)
found_images = images.select{|image| image['name'] == name}
if found_images.length == 1
return found_images[0]['id']
elsif found_images.length == 0
raise KeystoneAPIError, "Image with name '#{name}' not found."
elsif found_images.length > 1
raise KeystoneAPIError, "Found multiple matches for name: '#{name}'"
end
end
def exists?
ini_file = Puppet::Util::IniConfig::File.new
ini_file.read("/etc/ironic/ironic.conf")
ironic_images.each do |setting, id|
if ! ( ini_file['fuel'] && ini_file['fuel'][setting] && ini_file['fuel'][setting] == id)
return nil
end
end
end
def create
config
end
def ironic_images
@ironic_images ||= get_ironic_images
end
def get_ironic_images
token = authenticate
RETRY_COUNT.times do |n|
begin
all_images = glance_images(@resource[:glance_url], token)
rescue => e
debug "Request failed: '#{e.message}' Retry: '#{n}'"
if n == RETRY_COUNT - 1
raise KeystoneAPIError, 'Unable to get images.'
end
sleep RETRY_SLEEP
next
end
ironic_images = Hash.new
ironic_images['deploy_kernel'] = find_image_by_name(all_images, 'ironic-deploy-linux')
ironic_images['deploy_ramdisk'] = find_image_by_name(all_images, 'ironic-deploy-initramfs')
ironic_images['deploy_squashfs'] = find_image_by_name(all_images, 'ironic-deploy-squashfs')
return ironic_images
end
end
def config
ironic_images.each do |setting, id|
Puppet::Type.type(:ironic_config).new(
{:name => "fuel/#{setting}", :value => id}
).provider.create
end
end
end

View File

@ -0,0 +1,140 @@
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
RETRY_COUNT = 10
RETRY_SLEEP = 3
def handle_request(req, url)
begin
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
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
def neutron_networks(neutron_url, token)
url = URI.parse("#{neutron_url}/networks")
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['networks']
end
Puppet::Type.type(:ironic_neutron_setter).provide(:ruby) do
@neutron_network = nil
def authenticate
keystone_v2_authenticate(
@resource[:auth_url],
@resource[:auth_username],
@resource[:auth_password],
nil,
@resource[:auth_tenant_name])
end
def find_network_by_name(networks, name)
found_networks = networks.select{|net| net['name'] == name}
if found_networks.length == 1
return found_networks[0]['id']
elsif found_networks.length == 0
raise KeystoneAPIError, "Network with name '#{name}' not found."
elsif found_networks.length > 1
raise KeystoneAPIError, "Found multiple matches for name: '#{name}'"
end
end
def exists?
ini_file = Puppet::Util::IniConfig::File.new
ini_file.read("/etc/ironic/ironic.conf")
ini_file['neutron'] && ini_file['neutron']['cleaning_network_uuid'] && ini_file['neutron']['cleaning_network_uuid'] == neutron_network
end
def create
config
end
def neutron_network
@neutron_network ||= get_neutron_network
end
def get_neutron_network
token = authenticate
RETRY_COUNT.times do |n|
begin
all_networks = neutron_networks(@resource[:neutron_url], token)
rescue => e
debug "Request failed: '#{e.message}' Retry: '#{n}'"
if n == RETRY_COUNT - 1
raise KeystoneAPIError, 'Unable to get networks.'
end
sleep RETRY_SLEEP
next
end
return find_network_by_name(all_networks, 'baremetal')
end
end
def config
Puppet::Type.type(:ironic_config).new(
{:name => "neutron/cleaning_network_uuid", :value => neutron_network}
).provider.create
end
end

View File

@ -0,0 +1,47 @@
Puppet::Type.newtype(:ironic_config) do
ensurable
newparam(:name, :namevar => true) do
desc 'Section/setting name to manage from /etc/ironic/ironic.conf'
newvalues(/\S+\/\S+/)
end
newproperty(:value) do
desc 'The value of the setting to be defined.'
munge do |value|
value = value.to_s.strip
value.capitalize! if value =~ /^(true|false)$/i
value
end
def is_to_s( currentvalue )
if resource.secret?
return '[old secret redacted]'
else
return currentvalue
end
end
def should_to_s( newvalue )
if resource.secret?
return '[new secret redacted]'
else
return newvalue
end
end
end
newparam(:secret, :boolean => true) do
desc 'Whether to hide the value from Puppet logs. Defaults to `false`.'
newvalues(:true, :false)
defaultto false
end
autorequire(:package) do
'ironic-common'
end
end

View File

@ -0,0 +1,31 @@
Puppet::Type.newtype(:ironic_images_setter) do
ensurable
newparam(:name, :namevar => true) do
desc 'The name of the setting to update'
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
newparam(:glance_url) do
desc 'Glance endpoint'
end
end

View File

@ -0,0 +1,31 @@
Puppet::Type.newtype(:ironic_neutron_setter) do
ensurable
newparam(:name, :namevar => true) do
desc 'The name of the setting to update'
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
newparam(:neutron_url) do
desc 'Neutron endpoint'
end
end

View File

@ -0,0 +1,182 @@
#
# Copyright (C) 2013 eNovance SAS <licensing@enovance.com>
#
# Author: Emilien Macchi <emilien.macchi@enovance.com>
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
# Configure the API service in Ironic
#
# === Parameters
#
# [*package_ensure*]
# (optional) Control the ensure parameter for the package ressource.
# Defaults to 'present'.
#
# [*enabled*]
# (optional) Define if the service must be enabled or not.
# Defaults to true.
#
# [*host_ip*]
# (optional) The listen IP for the Ironic API server.
# Should be an valid IP address
# Defaults to '0.0.0.0'.
#
# [*port*]
# (optional) The port for the Ironic API server.
# Should be an valid port
# Defaults to '0.0.0.0'.
#
# [*max_limit*]
# (optional) The maximum number of items returned in a single response
# from a collection resource.
# Should be an valid interger
# Defaults to '1000'.
#
# [*auth_host*]
# (optional) The IP of the server running keystone
# Defaults to '127.0.0.1'
#
# [*auth_port*]
# (optional) The port to use when authenticating against Keystone
# Defaults to 35357
#
# [*auth_protocol*]
# (optional) The protocol to use when authenticating against Keystone
# Defaults to 'http'
#
# [*auth_uri*]
# (optional) The uri of a Keystone service to authenticate against
# Defaults to false
#
# [*auth_admin_prefix*]
# (optional) Prefix to prepend at the beginning of the keystone path
# Defaults to false
#
# [*auth_version*]
# (optional) API version of the admin Identity API endpoint
# for example, use 'v3.0' for the keystone version 3.0 api
# Defaults to false
#
# [*admin_tenant_name*]
# (optional) The name of the tenant to create in keystone for use by the ironic services
# Defaults to 'services'
#
# [*admin_user*]
# (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
#
class ironic::api (
$package_ensure = 'present',
$enabled = true,
$host_ip = '0.0.0.0',
$port = '6385',
$max_limit = '1000',
$auth_host = '127.0.0.1',
$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,
$admin_password,
) {
include ::ironic::params
include ::ironic::policy
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']
Package['ironic-api'] -> Service['ironic-api']
package { 'ironic-api':
ensure => $package_ensure,
name => $::ironic::params::api_package,
tag => ['openstack', 'ironic-package'],
}
}
if $enabled {
$ensure = 'running'
} else {
$ensure = 'stopped'
}
# Manage service
service { 'ironic-api':
ensure => $ensure,
name => $::ironic::params::api_service,
enable => $enabled,
hasstatus => true,
tag => 'ironic-service',
}
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 {
ironic_config { 'keystone_authtoken/auth_uri': value => "${auth_protocol}://${auth_host}:5000/"; }
}
if $auth_version {
ironic_config { 'keystone_authtoken/auth_version': value => $auth_version; }
} else {
ironic_config { 'keystone_authtoken/auth_version': ensure => absent; }
}
ironic_config {
'keystone_authtoken/auth_host': value => $auth_host;
'keystone_authtoken/auth_port': value => $auth_port;
'keystone_authtoken/auth_protocol': value => $auth_protocol;
'keystone_authtoken/admin_tenant_name': value => $admin_tenant_name;
'keystone_authtoken/admin_user': value => $admin_user;
'keystone_authtoken/admin_password': value => $admin_password, secret => true;
}
if $auth_admin_prefix {
validate_re($auth_admin_prefix, '^(/.+[^/])?$')
ironic_config {
'keystone_authtoken/auth_admin_prefix': value => $auth_admin_prefix;
}
} else {
ironic_config {
'keystone_authtoken/auth_admin_prefix': ensure => absent;
}
}
}

View File

@ -0,0 +1,202 @@
# Copyright 2015 Hewlett-Packard Development Company, L.P.
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
# == Class: ironic::bifrost
#
# Installs and configures Bifrost
# Bifrost is a set of Ansible playbooks that automates the task of deploying a
# base image onto a set of known hardware using Ironic. It provides modular
# utility for one-off operating system deployment with as few operational requirements
# as reasonably possible.
# Bifrost also allows to install Ironic in a stand-alone fashion. In this kind of setup,
# neither Keystone nor Neutron is installed, and dnsmasq is used to provide PXE booting.
#
# [*ironic_db_password*]
# (required) The Ironic DB password
#
# [*mysql_password*]
# (required) The mysql server password
#
# [*baremetal_json_hosts*]
# (required) Baremetal hosts in JSON format, will be included in baremetal.json
#
# [*git_source_repo*]
# (optional) Git repository location for pulling Bifrost
# Defaults to 'https://git.openstack.org/openstack/bifrost'
#
# [*revision*]
# (optional) The branch or commit to checkout on Bifrost repository
# Defaults to 'master'
#
# [*ensure*]
# (optional) Ensure value for cloning the Bifrost repository.
# This is a pass-thru variable for vcsrepo, acceptable values are
# present/bare/absent/latest
# Typically, you may want to set this value to either present or absent and use
# revision for setting the branch or commit to clone.
# Defaults to 'present'
#
# [*revision*]
# (optional) The branch or commit to checkout on Bifrost repository
# Defaults to 'master'
#
# [*git_dest_repo_folder*]
# (optional) Folder to clone the Bifrost git repository
# Defaults to '/opt/stack/bifrost'
#
# [*ironic_url*]
# (optional) The URL of the Ironic server
# Defaults to '"http://localhost:6385"'
#
# [*network_interface*]
# (optional) The network interface DHCP will serve requests on
# Defaults to '"virbr0"'
#
# [*testing*]
# (optional) If true, Ironic will provision libvirt and VMs instead of baremetal
# Defaults to 'false'
#
# [*testing_user*]
# (optional) VM default user in case testing is enabled
# Defaults to 'ubuntu'
#
# [*http_boot_folder*]
# (optional) gPXE folder location for HTTP PXE boot
# Defaults to '/httpboot'
#
# [*nginx_port*]
# (optional) NGINX HTTP port
# Defaults to 8080
# [*ssh_public_key_path*]
# (optional) SSH public key location, this will be injected in provisioned servers
# Defaults to '"{{ ansible_env.HOME }}/.ssh/id_rsa.pub"'
#
# [*deploy_kernel*]
# (optional) Kernel to PXE boot from
# Defaults to '"{{http_boot_folder}}/coreos_production_pxe.vmlinuz"'
#
# [*deploy_ramdisk*]
# (optional) Ramdisk to load after kernel boot
# Defaults to '"{{http_boot_folder}}/coreos_production_pxe_image-oem.cpio.gz"'
#
# [*deploy_kernel_url*]
# (optional) Kernel URL
# Defaults to '"http://{{ hostvars[inventory_hostname][\'ansible_\' + network_interface][\'ipv4\'][\'address\'] }}:{{nginx_port}}/coreos_production_pxe.vmlinuz"'
#
# [*deploy_ramdisk_url*]
# (optional) Ramdisk URL
# Defaults to '"http://{{ hostvars[inventory_hostname][\'ansible_\' + network_interface][\'ipv4\'][\'address\'] }}:{{nginx_port}}/coreos_production_pxe_image-oem.cpio.gz"'
#
# [*deploy_image_filename*]
# (optional) Deploy image filename
# Defaults to '"deployment_image.qcow2"'
#
# [*deploy_image*]
# (optional) URL for the deployment image
# Defaults to '"{{http_boot_folder}}/{{deploy_image_filename}}"'
#
# [*create_image_via_dib*]
# (optional) Flag to enable/disable image creation with diskimage-builder
# Defaults to 'true'
#
# [*transform_boot_image*]
# (optional) Flag to prepend a partition image with boot sector and partition table
# Defaults to 'false'
#
# [*node_default_network_interface*]
# (optional) Default network interface to configure with configdrive settings
# Defaults to 'eth0'
#
# [*ipv4_subnet_mask*]
# (optional) Subnet mask for configured NIC
# Defaults to '255.255.255.0'
#
# [*ipv4_gateway*]
# (optional) Gateway for configured NIC
# Defaults to '192.168.1.1'
#
# [*ipv4_nameserver*]
# (optional) Nameserver for DNS configuration
# Defaults to '8.8.8.8'
#
# [*network_mtu*]
# (optional) MTU for configured NIC
# Defaults to '1500'
#
# [*dhcp_pool_start*]
# (optional) Dnsmasq DHCP pool start
# Defaults to '192.168.1.200'
#
# [*dhcp_pool_end*]
# (optional) Dnsmasq DHCP pool end
# Defaults to '192.168.1.250'
#
# [*ipmi_bridging*]
# (optional) Flag to enable/disable IPMI bridging
# Defaults to 'no'
class ironic::bifrost (
$ironic_db_password,
$mysql_password,
$baremetal_json_hosts,
$git_source_repo = 'https://git.openstack.org/openstack/bifrost',
$ensure = present,
$revision = 'master',
$git_dest_repo_folder = '/opt/stack/bifrost',
$ironic_url = '"http://localhost:6385/"',
$network_interface = '"virbr0"',
$testing = false,
$testing_user = 'ubuntu',
$http_boot_folder = '/httpboot',
$nginx_port = 8080,
$ssh_public_key_path = '"{{ ansible_env.HOME }}/.ssh/id_rsa.pub"',
$deploy_kernel = '"{{http_boot_folder}}/coreos_production_pxe.vmlinuz"',
$deploy_ramdisk = '"{{http_boot_folder}}/coreos_production_pxe_image-oem.cpio.gz"',
$deploy_kernel_url = '"http://{{ hostvars[inventory_hostname][\'ansible_\' + network_interface][\'ipv4\'][\'address\'] }}:{{nginx_port}}/coreos_production_pxe.vmlinuz"',
$deploy_ramdisk_url = '"http://{{ hostvars[inventory_hostname][\'ansible_\' + network_interface][\'ipv4\'][\'address\'] }}:{{nginx_port}}/coreos_production_pxe_image-oem.cpio.gz"',
$deploy_image_filename = '"deployment_image.qcow2"',
$deploy_image = '"{{http_boot_folder}}/{{deploy_image_filename}}"',
$create_image_via_dib = true,
$transform_boot_image = false,
$node_default_network_interface = 'eth0',
$ipv4_subnet_mask = '255.255.255.0',
$ipv4_gateway = '192.168.1.1',
$ipv4_nameserver = '8.8.8.8',
$network_mtu = '1500',
$dhcp_pool_start = '192.168.1.200',
$dhcp_pool_end = '192.168.1.250',
$ipmi_bridging = 'no',
) {
vcsrepo { $git_dest_repo_folder:
ensure => $ensure,
provider => git,
revision => $revision,
source => $git_source_repo,
}
file { "${git_dest_repo_folder}/playbooks/inventory/group_vars/all":
ensure => present,
content => template('ironic/group_vars_all.erb'),
require => Vcsrepo[$git_dest_repo_folder],
}
file { "${git_dest_repo_folder}/baremetal.json":
ensure => present,
content => template('ironic/baremetal.json.erb'),
require => Vcsrepo[$git_dest_repo_folder],
}
}

View File

@ -0,0 +1,41 @@
#
# Copyright (C) 2013 eNovance SAS <licensing@enovance.com>
#
# Author: Emilien Macchi <emilien.macchi@enovance.com>
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
# ironic::client
#
# Manages the ironic client package on systems
#
# === Parameters:
#
# [*package_ensure*]
# (optional) The state of the package
# Defaults to present
#
class ironic::client (
$package_ensure = present
) {
include ::ironic::params
package { 'python-ironicclient':
ensure => $package_ensure,
name => $::ironic::params::client_package,
tag => 'openstack',
}
}

View File

@ -0,0 +1,83 @@
#
# Copyright (C) 2013 eNovance SAS <licensing@enovance.com>
#
# Author: Emilien Macchi <emilien.macchi@enovance.com>
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
# Configure the conductor service in Ironic
#
# === Parameters
#
# [*package_ensure*]
# (optional) Control the ensure parameter for the package ressource.
# Defaults to 'present'.
#
# [*enabled*]
# (optional) Define if the service must be enabled or not.
# Defaults to true.
#
# [*max_time_interval*]
# (optional) Maximum time, in seconds, since the last check-in of a conductor.
# Should be an interger value
# Defaults to '120'.
#
# [*force_power_state_during_sync*]
# (optional) Should the hardware power state be set to the state recorded in
# the database (True) or should the database be updated based on the hardware
# state (False).
# Defaults to true.
#
class ironic::conductor (
$package_ensure = 'present',
$enabled = true,
$max_time_interval = '120',
$force_power_state_during_sync = true,
) {
include ::ironic::params
Ironic_config<||> ~> Service['ironic-conductor']
# Configure ironic.conf
ironic_config {
'conductor/max_time_interval': value => $max_time_interval;
'conductor/force_power_state_during_sync': value => $force_power_state_during_sync;
}
# Install package
if $::ironic::params::conductor_package {
Package['ironic-conductor'] -> Service['ironic-conductor']
package { 'ironic-conductor':
ensure => $package_ensure,
name => $::ironic::params::conductor_package,
tag => ['openstack', 'ironic-package'],
}
}
if $enabled {
$ensure = 'running'
} else {
$ensure = 'stopped'
}
# Manage service
service { 'ironic-conductor':
ensure => $ensure,
name => $::ironic::params::conductor_service,
enable => $enabled,
hasstatus => true,
tag => 'ironic-service',
}
}

View File

@ -0,0 +1,30 @@
# == Class: ironic::config
#
# This class is used to manage arbitrary Ironic configurations.
#
# === Parameters
#
# [*ironic_config*]
# (optional) Allow configuration of arbitrary Ironic configurations.
# The value is an hash of ironic_config resources. Example:
# { 'DEFAULT/foo' => { value => 'fooValue'},
# 'DEFAULT/bar' => { value => 'barValue'}
# }
# In yaml format, Example:
# ironic_config:
# DEFAULT/foo:
# value: fooValue
# DEFAULT/bar:
# value: barValue
#
# NOTE: The configuration MUST NOT be already handled by this module
# or Puppet catalog compilation will fail with duplicate resources.
#
class ironic::config (
$ironic_config = {},
) {
validate_hash($ironic_config)
create_resources('ironic_config', $ironic_config)
}

View File

@ -0,0 +1,77 @@
#
# Copyright (C) 2013 eNovance SAS <licensing@enovance.com>
#
# Author: Emilien Macchi <emilien.macchi@enovance.com>
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
#
# ironic::db::mysql
#
# [*password*]
# Password to use for the nova user
#
# [*dbname*]
# (optional) The name of the database
# Defaults to 'nova'
#
# [*user*]
# (optional) The mysql user to create
# Defaults to 'nova'
#
# [*host*]
# (optional) The IP address of the mysql server
# Defaults to '127.0.0.1'
#
# [*charset*]
# (optional) The charset to use for the nova database
# Defaults to 'utf8'
#
# [*collate*]
# (optional) The collate to use for the nova database
# Defaults to 'utf8_general_ci'
#
# [*allowed_hosts*]
# (optional) Additional hosts that are allowed to access this DB
# Defaults to undef
#
# [*cluster_id*]
# (optional) Deprecated. Does nothing
class ironic::db::mysql (
$password,
$dbname = 'ironic',
$user = 'ironic',
$host = '127.0.0.1',
$allowed_hosts = undef,
$charset = 'utf8',
$collate = 'utf8_general_ci',
$cluster_id = undef,
) {
if $cluster_id {
warning('The cluster_id parameter is deprecated and has no effect.')
}
::openstacklib::db::mysql { 'ironic':
user => $user,
password_hash => mysql_password($password),
dbname => $dbname,
host => $host,
charset => $charset,
collate => $collate,
allowed_hosts => $allowed_hosts,
}
::Openstacklib::Db::Mysql['ironic'] ~> Exec<| title == 'ironic-dbsync' |>
}

View File

@ -0,0 +1,47 @@
# == Class: ironic::db::postgresql
#
# Class that configures postgresql for ironic
# Requires the Puppetlabs postgresql module.
#
# === Parameters
#
# [*password*]
# (Required) Password to connect to the database.
#
# [*dbname*]
# (Optional) Name of the database.
# Defaults to 'ironic'.
#
# [*user*]
# (Optional) User to connect to the database.
# Defaults to 'ironic'.
#
# [*encoding*]
# (Optional) The charset to use for the database.
# Default to undef.
#
# [*privileges*]
# (Optional) Privileges given to the database user.
# Default to 'ALL'
#
class ironic::db::postgresql(
$password,
$dbname = 'ironic',
$user = 'ironic',
$encoding = undef,
$privileges = 'ALL',
) {
Class['ironic::db::postgresql'] -> Service<| title == 'ironic' |>
::openstacklib::db::postgresql { 'ironic':
password_hash => postgresql_password($user, $password),
dbname => $dbname,
user => $user,
encoding => $encoding,
privileges => $privileges,
}
::Openstacklib::Db::Postgresql['ironic'] ~> Exec<| title == 'ironic-dbsync' |>
}

View File

@ -0,0 +1,26 @@
#
# Class to execute ironic dbsync
#
class ironic::db::sync {
include ::ironic::params
Package<| tag == 'ironic-package' |> ~> Exec['ironic-dbsync']
Exec['ironic-dbsync'] ~> Service <| tag == 'ironic-service' |>
Ironic_config<||> -> Exec['ironic-dbsync']
Ironic_config<| title == 'database/connection' |> ~> Exec['ironic-dbsync']
exec { 'ironic-dbsync':
command => $::ironic::params::dbsync_command,
path => '/usr/bin',
# Ubuntu packaging is running dbsync command as root during ironic-common
# postinstall script so when Puppet tries to run dbsync again, it fails
# because it is run with ironic user.
# This is a temporary patch until it's changed in Packaging
# https://bugs.launchpad.net/cloud-archive/+bug/1450942
user => 'root',
refreshonly => true,
logoutput => on_failure,
}
}

View File

@ -0,0 +1,37 @@
#
# Copyright (C) 2013 eNovance SAS <licensing@enovance.com>
#
# Author: Emilien Macchi <emilien.macchi@enovance.com>
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
# Configure the IPMI driver in Ironic
#
# === Parameters
#
# [*retry_timeout*]
# (optional) Maximum time in seconds to retry IPMI operations.
# Should be an interger value
# Defaults to '10'.
#
class ironic::drivers::ipmi (
$retry_timeout = '10'
) {
# Configure ironic.conf
ironic_config {
'ipmi/retry_timeout': value => $retry_timeout;
}
}

View File

@ -0,0 +1,110 @@
#
# Copyright (C) 2013 eNovance SAS <licensing@enovance.com>
#
# Author: Emilien Macchi <emilien.macchi@enovance.com>
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
# Configure the PXE driver in Ironic
#
# === Parameters
#
# [*deploy_kernel*]
# (optional) Default kernel image ID used in deployment phase.
# Should be an valid id
# Defaults to undef.
#
# [*deploy_ramdisk*]
# (optional) Default kernel image ID used in deployment phase.
# Should be an valid id
# Defaults to undef.
#
# [*pxe_append_params*]
# (optional) Additional append parameters for baremetal PXE boot.
# Should be valid pxe parameters
# Defaults to 'nofb nomodeset vga=normal'.
#
# [*pxe_config_template*]
# (optional) Template file for PXE configuration.
# Should be an valid template file
# Defaults to '$pybasedir/drivers/modules/pxe_config.template'.
#
# [*pxe_deploy_timeout*]
# (optional) Timeout for PXE deployments.
# Should be an valid integer
# Defaults to '0' for unlimited.
#
# [*tftp_server*]
# (optional) IP address of Ironic compute node's tftp server.
# Should be an valid IP address
# Defaults to '$my_ip'.
#
# [*tftp_root*]
# (optional) Ironic compute node's tftp root path.
# Should be an valid path
# Defaults to '/tftpboot'.
#
# [*images_path*]
# (optional) Directory where images are stored on disk.
# Should be an valid directory
# Defaults to '/tftpboot'.
#
# [*tftp_master_path*]
# (optional) Directory where master tftp images are stored on disk.
# Should be an valid directory
# Defaults to '/tftpboot/master_images'.
#
# [*instance_master_path*]
# (optional) Directory where master tftp images are stored on disk.
# Should be an valid directory
# Defaults to '/var/lib/ironic/master_images'.
#
class ironic::drivers::pxe (
$deploy_kernel = undef,
$deploy_ramdisk = undef,
$pxe_append_params = 'nofb nomodeset vga=normal',
$pxe_config_template = '$pybasedir/drivers/modules/pxe_config.template',
$pxe_deploy_timeout = '0',
$tftp_server = '$my_ip',
$tftp_root = '/tftpboot',
$images_path = '/var/lib/ironic/images/',
$tftp_master_path = '/tftpboot/master_images',
$instance_master_path = '/var/lib/ironic/master_images',
) {
# Configure ironic.conf
ironic_config {
'pxe/pxe_append_params': value => $pxe_append_params;
'pxe/pxe_config_template': value => $pxe_config_template;
'pxe/pxe_deploy_timeout': value => $pxe_deploy_timeout;
'pxe/tftp_server': value => $tftp_server;
'pxe/tftp_root': value => $tftp_root;
'pxe/images_path': value => $images_path;
'pxe/tftp_master_path': value => $tftp_master_path;
'pxe/instance_master_path': value => $instance_master_path;
}
if $deploy_kernel {
ironic_config {
'pxe/deploy_kernel': value => $deploy_kernel;
}
}
if $deploy_ramdisk {
ironic_config {
'pxe/deploy_ramdisk': value => $deploy_ramdisk;
}
}
}

View File

@ -0,0 +1,402 @@
#
# Copyright (C) 2013 eNovance SAS <licensing@enovance.com>
#
# Author: Emilien Macchi <emilien.macchi@enovance.com>
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
#
# == Class: ironic
#
# Installs the ironic package and configures /etc/ironic/ironic.conf
#
# === Parameters:
#
# [*enabled*]
# (required) Whether or not to enable the ironic service
# true/false
#
# [*package_ensure*]
# (optional) The state of the package
# Defaults to 'present'
#
# [*verbose*]
# (optional) Verbose logging
# Defaults to False
#
# [*debug*]
# (optional) Print debug messages in the logs
# Defaults to False
#
# [*auth_strategy*]
# (optional) Default protocol to use when connecting to glance
# Defaults to 'keystone'. 'https' is the only other valid option for SSL
#
# [*enabled_drivers*]
# (optional) Array of drivers to load during service
# initialization.
# Defaults to ['pxe_ipmitool'].
#
# [*control_exchange*]
# (optional) What RPC queue/exchange to use
# Defaults to openstack
#
# [*rpc_backend*]
# (optional) what rpc/queuing service to use
# Defaults to impl_kombu (rabbitmq)
#
# [*rabbit_host*]
# (Optional) IP or hostname of the rabbit server.
# Defaults to 'localhost'
#
# [*rabbit_port*]
# (Optional) Port of the rabbit server.
# Defaults to 5672.
#
# [*rabbit_hosts*]
# (Optional) Array of host:port (used with HA queues).
# If defined, will remove rabbit_host & rabbit_port parameters from config
# Defaults to undef.
#
# [*rabbit_user*]
# (Optional) User to connect to the rabbit server.
# Defaults to undef.
# Deprecated, use rabbit_userid instead.
#
# [*rabbit_userid*]
# (Optional) User to connect to the rabbit server.
# Defaults to 'guest'
#
# [*rabbit_password*]
# (Optional) Password to connect to the rabbit_server.
# Defaults to empty.
#
# [*rabbit_virtual_host*]
# (Optional) Virtual_host to use.
# Defaults to '/'
#
# [*rabbit_use_ssl*]
# (optional) Connect over SSL for RabbitMQ
# Defaults to false
#
# [*kombu_ssl_ca_certs*]
# (optional) SSL certification authority file (valid only if SSL enabled).
# Defaults to undef
#
# [*kombu_ssl_certfile*]
# (optional) SSL cert file (valid only if SSL enabled).
# Defaults to undef
#
# [*kombu_ssl_keyfile*]
# (optional) SSL key file (valid only if SSL enabled).
# Defaults to undef
#
# [*kombu_ssl_version*]
# (optional) SSL version to use (valid only if SSL enabled).
# Valid values are TLSv1, SSLv23 and SSLv3. SSLv2 may be
# available on some distributions.
# Defaults to 'TLSv1'
#
# [*amqp_durable_queues*]
# Use durable queues in amqp.
# (Optional) Defaults to false.
#
# [*rabbit_virtual_host*]
# (optional) Various rabbitmq settings
#
# [*rabbit_hosts*]
# (optional) array of rabbitmq servers for HA.
# A single IP address, such as a VIP, can be used for load-balancing
# multiple RabbitMQ Brokers.
# Defaults to false
#
# [*qpid_hostname*]
# [*qpid_port*]
# [*qpid_username*]
# [*qpid_password*]
# [*qpid_heartbeat*]
# [*qpid_protocol*]
# [*qpid_tcp_nodelay*]
# [*qpid_reconnect*]
# [*qpid_reconnect_timeout*]
# [*qpid_reconnect_limit*]
# [*qpid_reconnect_interval*]
# [*qpid_reconnect_interval_min*]
# [*qpid_reconnect_interval_max*]
# (optional) various QPID options
#
# [*use_syslog*]
# (optional) Use syslog for logging
# Defaults to false
#
# [*log_facility*]
# (optional) Syslog facility to receive log lines
# Defaults to LOG_USER
#
# [*database_connection*]
# (optional) Connection url for the ironic database.
# Defaults to: sqlite:////var/lib/ironic/ironic.sqlite
#
# [*database_max_retries*]
# (optional) Database reconnection retry times.
# Defaults to: 10
#
# [*database_idle_timeout*]
# (optional) Timeout before idle db connections are reaped.
# Defaults to: 3600
#
# [*database_reconnect_interval*]
# (optional) Database reconnection interval in seconds.
# Defaults to: 10
#
# [*database_retry_interval*]
# (optional) Database reconnection interval in seconds.
# Defaults to: 10
#
# [*glance_api_servers*]
# (optional) A list of the glance api servers available to ironic.
# Should be an array with [hostname|ip]:port
# Defaults to undef
#
# [*glance_num_retries*]
# (optional) Number retries when downloading an image from glance.
# Defaults to 0
#
# [*glance_api_insecure*]
# (optional) Allow to perform insecure SSL (https) requests to glance.
# Defaults to false
#
# [*sync_db*]
# Enable dbsync
# Defaults to true
#
class ironic (
$enabled = true,
$package_ensure = 'present',
$verbose = false,
$debug = false,
$auth_strategy = 'keystone',
$enabled_drivers = ['pxe_ipmitool'],
$control_exchange = 'openstack',
$rpc_backend = 'ironic.openstack.common.rpc.impl_kombu',
$rabbit_hosts = false,
$rabbit_virtual_host = '/',
$rabbit_host = 'localhost',
$rabbit_port = 5672,
$rabbit_hosts = false,
$rabbit_virtual_host = '/',
$rabbit_userid = 'guest',
$rabbit_password = false,
$rabbit_use_ssl = false,
$kombu_ssl_ca_certs = undef,
$kombu_ssl_certfile = undef,
$kombu_ssl_keyfile = undef,
$kombu_ssl_version = 'TLSv1',
$amqp_durable_queues = false,
$qpid_hostname = 'localhost',
$qpid_port = '5672',
$qpid_username = 'guest',
$qpid_password = 'guest',
$qpid_heartbeat = 60,
$qpid_protocol = 'tcp',
$qpid_tcp_nodelay = true,
$qpid_reconnect = true,
$qpid_reconnect_timeout = 0,
$qpid_reconnect_limit = 0,
$qpid_reconnect_interval_min = 0,
$qpid_reconnect_interval_max = 0,
$qpid_reconnect_interval = 0,
$use_syslog = false,
$log_facility = 'LOG_USER',
$database_connection = 'sqlite:////var/lib/ironic/ovs.sqlite',
$database_max_retries = '10',
$database_idle_timeout = '3600',
$database_reconnect_interval = '10',
$database_retry_interval = '10',
$glance_api_servers = undef,
$glance_num_retries = '0',
$glance_api_insecure = false,
$sync_db = true,
# DEPRECATED PARAMETERS
$rabbit_user = undef,
) {
include ::ironic::params
if $rabbit_user {
warning('The rabbit_user parameter is deprecated. Please use rabbit_userid instead.')
$rabbit_user_real = $rabbit_user
} else {
$rabbit_user_real = $rabbit_userid
}
file { '/etc/ironic':
ensure => directory,
require => Package['ironic-common'],
group => 'ironic',
}
file { '/etc/ironic/ironic.conf':
require => Package['ironic-common'],
group => 'ironic',
}
package { 'ironic-common':
ensure => $package_ensure,
name => $::ironic::params::common_package_name,
tag => ['openstack', 'ironic-package'],
notify => Exec['ironic-dbsync'],
}
validate_re($database_connection, '(sqlite|mysql|postgresql):\/\/(\S+:\S+@\S+\/\S+)?')
validate_array($enabled_drivers)
case $database_connection {
/mysql:\/\/\S+:\S+@\S+\/\S+/: {
$database_backend_package = false
require 'mysql::bindings'
require 'mysql::bindings::python'
}
/postgresql:\/\/\S+:\S+@\S+\/\S+/: {
$database_backend_package = 'python-psycopg2'
}
/sqlite:\/\//: {
$database_backend_package = 'python-pysqlite2'
}
default: {
fail("Invalid database connection: ${database_connection}")
}
}
if $database_backend_package and !defined(Package[$database_backend_package]) {
package { 'ironic-database-backend':
ensure => present,
name => $database_backend_package,
tag => 'openstack',
}
}
if is_array($glance_api_servers) {
ironic_config {
'glance/glance_api_servers': value => join($glance_api_servers, ',');
}
} elsif is_string($glance_api_servers) {
ironic_config {
'glance/glance_api_servers': value => $glance_api_servers;
}
}
ironic_config {
'DEFAULT/verbose': value => $verbose;
'DEFAULT/debug': value => $debug;
'DEFAULT/auth_strategy': value => $auth_strategy;
'DEFAULT/rpc_backend': value => $rpc_backend;
'DEFAULT/enabled_drivers': value => join($enabled_drivers, ',');
'database/connection': value => $database_connection, secret => true;
'database/idle_timeout': value => $database_idle_timeout;
'database/retry_interval': value => $database_retry_interval;
'database/max_retries': value => $database_max_retries;
'glance/glance_num_retries': value => $glance_num_retries;
'glance/glance_api_insecure': value => $glance_api_insecure;
}
if $sync_db {
include ::ironic::db::sync
}
if $rpc_backend == 'ironic.openstack.common.rpc.impl_kombu' {
if ! $rabbit_password {
fail('When rpc_backend is rabbitmq, you must set rabbit password')
}
ironic_config {
'oslo_messaging_rabbit/rabbit_userid': value => $rabbit_user_real;
'oslo_messaging_rabbit/rabbit_password': value => $rabbit_password, secret => true;
'oslo_messaging_rabbit/rabbit_virtual_host': value => $rabbit_virtual_host;
'oslo_messaging_rabbit/rabbit_use_ssl': value => $rabbit_use_ssl;
'DEFAULT/control_exchange': value => $control_exchange;
'DEFAULT/amqp_durable_queues': value => $amqp_durable_queues;
}
if $rabbit_hosts {
ironic_config { 'oslo_messaging_rabbit/rabbit_hosts': value => join($rabbit_hosts, ',') }
ironic_config { 'oslo_messaging_rabbit/rabbit_ha_queues': value => true }
ironic_config { 'oslo_messaging_rabbit/rabbit_host': ensure => absent }
ironic_config { 'oslo_messaging_rabbit/rabbit_port': ensure => absent }
} else {
ironic_config { 'oslo_messaging_rabbit/rabbit_host': value => $rabbit_host }
ironic_config { 'oslo_messaging_rabbit/rabbit_port': value => $rabbit_port }
ironic_config { 'oslo_messaging_rabbit/rabbit_hosts': value => "${rabbit_host}:${rabbit_port}" }
ironic_config { 'oslo_messaging_rabbit/rabbit_ha_queues': value => false }
}
if $rabbit_use_ssl {
ironic_config { 'oslo_messaging_rabbit/kombu_ssl_version': value => $kombu_ssl_version }
if $kombu_ssl_ca_certs {
ironic_config { 'oslo_messaging_rabbit/kombu_ssl_ca_certs': value => $kombu_ssl_ca_certs }
} else {
ironic_config { 'oslo_messaging_rabbit/kombu_ssl_ca_certs': ensure => absent}
}
if $kombu_ssl_certfile {
ironic_config { 'oslo_messaging_rabbit/kombu_ssl_certfile': value => $kombu_ssl_certfile }
} else {
ironic_config { 'oslo_messaging_rabbit/kombu_ssl_certfile': ensure => absent}
}
if $kombu_ssl_keyfile {
ironic_config { 'oslo_messaging_rabbit/kombu_ssl_keyfile': value => $kombu_ssl_keyfile }
} else {
ironic_config { 'oslo_messaging_rabbit/kombu_ssl_keyfile': ensure => absent}
}
} else {
ironic_config {
'oslo_messaging_rabbit/kombu_ssl_ca_certs': ensure => absent;
'oslo_messaging_rabbit/kombu_ssl_certfile': ensure => absent;
'oslo_messaging_rabbit/kombu_ssl_keyfile': ensure => absent;
'oslo_messaging_rabbit/kombu_ssl_version': ensure => absent;
}
}
}
if $rpc_backend == 'ironic.openstack.common.rpc.impl_qpid' {
ironic_config {
'DEFAULT/qpid_hostname': value => $qpid_hostname;
'DEFAULT/qpid_port': value => $qpid_port;
'DEFAULT/qpid_username': value => $qpid_username;
'DEFAULT/qpid_password': value => $qpid_password, secret => true;
'DEFAULT/qpid_heartbeat': value => $qpid_heartbeat;
'DEFAULT/qpid_protocol': value => $qpid_protocol;
'DEFAULT/qpid_tcp_nodelay': value => $qpid_tcp_nodelay;
'DEFAULT/qpid_reconnect': value => $qpid_reconnect;
'DEFAULT/qpid_reconnect_timeout': value => $qpid_reconnect_timeout;
'DEFAULT/qpid_reconnect_limit': value => $qpid_reconnect_limit;
'DEFAULT/qpid_reconnect_interval_min': value => $qpid_reconnect_interval_min;
'DEFAULT/qpid_reconnect_interval_max': value => $qpid_reconnect_interval_max;
'DEFAULT/qpid_reconnect_interval': value => $qpid_reconnect_interval;
}
}
if $use_syslog {
ironic_config {
'DEFAULT/use_syslog': value => true;
'DEFAULT/syslog_log_facility': value => $log_facility;
}
} else {
ironic_config {
'DEFAULT/use_syslog': value => false;
}
}
}

View File

@ -0,0 +1,214 @@
#
# Copyright (C) 2013 eNovance SAS <licensing@enovance.com>
#
# Author: Emilien Macchi <emilien.macchi@enovance.com>
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
#
# ironic::keystone::auth
#
# Configures Ironic user, service and endpoint in Keystone.
#
# === Parameters
#
# [*password*]
# (required) Password for Ironic user.
#
# [*auth_name*]
# Username for Ironic service. Defaults to 'ironic'.
#
# [*email*]
# Email for Ironic user. Defaults to 'ironic@localhost'.
#
# [*tenant*]
# Tenant for Ironic user. Defaults to 'services'.
#
# [*configure_endpoint*]
# Should Ironic endpoint be configured? Defaults to 'true'.
#
# [*configure_user*]
# (Optional) Should the service user be configured?
# Defaults to 'true'.
#
# [*configure_user_role*]
# (Optional) Should the admin role be configured for the service user?
# Defaults to 'true'.
#
# [*service_name*]
# (Optional) Name of the service.
# Defaults to the value of auth_name, but must differ from the value.
#
# [*service_type*]
# Type of service. Defaults to 'baremetal'.
#
# [*service_description*]
# (Optional) Description for keystone service.
# Defaults to 'Ironic Bare Metal Provisioning Service'.
#
# [*region*]
# Region for endpoint. Defaults to 'RegionOne'.
#
# [*public_url*]
# (optional) The endpoint's public url. (Defaults to 'http://127.0.0.1:6385')
# This url should *not* contain any trailing '/'.
#
# [*admin_url*]
# (optional) The endpoint's admin url. (Defaults to 'http://127.0.0.1:6385')
# This url should *not* contain any trailing '/'.
#
# [*internal_url*]
# (optional) The endpoint's internal url. (Defaults to 'http://127.0.0.1:6385')
# This url should *not* contain any trailing '/'.
#
# [*port*]
# (optional) DEPRECATED: Use public_url, internal_url and admin_url instead.
# Default port for endpoints. (Defaults to 6385)
# Setting this parameter overrides public_url, internal_url and admin_url parameters.
#
# [*public_protocol*]
# (optional) DEPRECATED: Use public_url instead.
# Protocol for public endpoint. (Defaults to 'http')
# Setting this parameter overrides public_url parameter.
#
# [*public_port*]
# (optional) DEPRECATED: Use public_url instead.
# Default port for endpoints. (Defaults to $port)
# Setting this parameter overrides public_url parameter.
#
# [*public_address*]
# (optional) DEPRECATED: Use public_url instead.
# Public address for endpoint. (Defaults to '127.0.0.1')
# Setting this parameter overrides public_url parameter.
#
# [*internal_address*]
# (optional) DEPRECATED: Use internal_url instead.
# Internal address for endpoint. (Defaults to '127.0.0.1')
# Setting this parameter overrides internal_url parameter.
#
# [*admin_address*]
# (optional) DEPRECATED: Use admin_url instead.
# Admin address for endpoint. (Defaults to '127.0.0.1')
# Setting this parameter overrides admin_url parameter.
#
# === Deprecation notes
#
# If any value is provided for public_protocol, public_address or port parameters,
# public_url will be completely ignored. The same applies for internal and admin parameters.
#
# === Examples
#
# class { 'ironic::keystone::auth':
# public_url => 'https://10.0.0.10:6385',
# internal_url => 'https://10.0.0.11:6385',
# admin_url => 'https://10.0.0.11:6385',
# }
#
class ironic::keystone::auth (
$password,
$auth_name = 'ironic',
$email = 'ironic@localhost',
$tenant = 'services',
$configure_endpoint = true,
$configure_user = true,
$configure_user_role = true,
$service_name = undef,
$service_type = 'baremetal',
$service_description = 'Ironic Bare Metal Provisioning Service',
$public_protocol = 'http',
$region = 'RegionOne',
$public_url = 'http://127.0.0.1:6385',
$admin_url = 'http://127.0.0.1:6385',
$internal_url = 'http://127.0.0.1:6385',
# DEPRECATED PARAMETERS
$port = undef,
$public_protocol = undef,
$public_address = undef,
$public_port = undef,
$internal_address = undef,
$admin_address = undef,
) {
if $port {
warning('The port parameter is deprecated, use public_url, internal_url and admin_url instead.')
}
if $public_port {
warning('The public_port parameter is deprecated, use public_url instead.')
}
if $public_protocol {
warning('The public_protocol parameter is deprecated, use public_url instead.')
}
if $public_address {
warning('The public_address parameter is deprecated, use public_url instead.')
}
if $internal_address {
warning('The internal_address parameter is deprecated, use internal_url instead.')
}
if $admin_address {
warning('The admin_address parameter is deprecated, use admin_url instead.')
}
if ($public_protocol or $public_address or $port or $public_port) {
$public_url_real = sprintf('%s://%s:%s',
pick($public_protocol, 'http'),
pick($public_address, '127.0.0.1'),
pick($public_port, $port, '6385'))
} else {
$public_url_real = $public_url
}
if ($admin_address or $port) {
$admin_url_real = sprintf('http://%s:%s',
pick($admin_address, '127.0.0.1'),
pick($port, '6385'))
} else {
$admin_url_real = $admin_url
}
if ($internal_address or $port) {
$internal_url_real = sprintf('http://%s:%s',
pick($internal_address, '127.0.0.1'),
pick($port, '6385'))
} else {
$internal_url_real = $internal_url
}
$real_service_name = pick($service_name, $auth_name)
if $configure_user_role {
Keystone_user_role["${auth_name}@${tenant}"] ~> Service <| name == 'ironic-server' |>
}
Keystone_endpoint["${region}/${real_service_name}"] ~> Service <| name == 'ironic-server' |>
keystone::resource::service_identity { $auth_name:
configure_user => $configure_user,
configure_user_role => $configure_user_role,
configure_endpoint => $configure_endpoint,
service_name => $real_service_name,
service_type => $service_type,
service_description => $service_description,
region => $region,
password => $password,
email => $email,
tenant => $tenant,
public_url => $public_url_real,
internal_url => $internal_url_real,
admin_url => $admin_url_real,
}
}

View File

@ -0,0 +1,48 @@
#
# Copyright (C) 2013 eNovance SAS <licensing@enovance.com>
#
# Author: Emilien Macchi <emilien.macchi@enovance.com>
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
#
# ironic::params
#
class ironic::params {
$dbsync_command =
'ironic-dbsync --config-file /etc/ironic/ironic.conf'
case $::osfamily {
'RedHat': {
$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'
}
'Debian': {
$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'
}
default: {
fail("Unsupported osfamily ${::osfamily}")
}
}
}

View File

@ -0,0 +1,29 @@
# == Class: ironic::policy
#
# Configure the ironic policies
#
# === Parameters
#
# [*policies*]
# (optional) Set of policies to configure for ironic
# Example : { 'ironic-context_is_admin' => {'context_is_admin' => 'true'}, 'ironic-default' => {'default' => 'rule:admin_or_owner'} }
# Defaults to empty hash.
#
# [*policy_path*]
# (optional) Path to the ironic policy.json file
# Defaults to /etc/ironic/policy.json
#
class ironic::policy (
$policies = {},
$policy_path = '/etc/ironic/policy.json',
) {
validate_hash($policies)
Openstacklib::Policy::Base {
file_path => $policy_path,
}
create_resources('openstacklib::policy::base', $policies)
}

View File

@ -0,0 +1,40 @@
{
"name": "openstack-ironic",
"version": "6.0.0",
"author": "eNovance and OpenStack Contributors",
"summary": "Puppet module for OpenStack Ironic",
"license": "Apache-2.0",
"source": "git://github.com/openstack/puppet-ironic.git",
"project_page": "https://launchpad.net/puppet-ironic",
"issues_url": "https://bugs.launchpad.net/puppet-ironic",
"requirements": [
{ "name": "pe","version_requirement": "3.x" },
{ "name": "puppet","version_requirement": "3.x" }
],
"operatingsystem_support": [
{
"operatingsystem": "Debian",
"operatingsystemrelease": ["7"]
},
{
"operatingsystem": "Fedora",
"operatingsystemrelease": ["20"]
},
{
"operatingsystem": "RedHat",
"operatingsystemrelease": ["6.5","7"]
},
{
"operatingsystem": "Ubuntu",
"operatingsystemrelease": ["12.04","14.04"]
}
],
"description": "Installs and configures OpenStack Ironic (Bare metal).",
"dependencies": [
{ "name": "puppetlabs/inifile", "version_requirement": ">=1.0.0 <2.0.0" },
{ "name": "openstack/keystone", "version_requirement": ">=6.0.0 <7.0.0" },
{ "name": "puppetlabs/stdlib", "version_requirement": ">=4.0.0 <5.0.0" },
{ "name": "openstack/openstacklib", "version_requirement": ">=6.0.0 <7.0.0" },
{ "name": "puppetlabs/vcsrepo", "version_requirement": ">=1.3.0 <2.0.0"}
]
}

View File

@ -0,0 +1,130 @@
require 'spec_helper_acceptance'
describe 'basic ironic' do
context 'default parameters' do
it 'should work with no errors' do
pp= <<-EOS
Exec { logoutput => 'on_failure' }
# Common resources
case $::osfamily {
'Debian': {
include ::apt
class { '::openstack_extras::repo::debian::ubuntu':
release => 'kilo',
package_require => true,
}
$package_provider = 'apt'
}
'RedHat': {
class { '::openstack_extras::repo::redhat::redhat':
# Kilo is not GA yet, so let's use the testing repo
manage_rdo => false,
repo_hash => {
'rdo-kilo-testing' => {
'baseurl' => 'https://repos.fedorapeople.org/repos/openstack/openstack-kilo/testing/el7/',
# packages are not GA so not signed
'gpgcheck' => '0',
'priority' => 97,
},
},
}
$package_provider = 'yum'
}
default: {
fail("Unsupported osfamily (${::osfamily})")
}
}
class { '::mysql::server': }
class { '::rabbitmq':
delete_guest_user => true,
package_provider => $package_provider,
}
rabbitmq_vhost { '/':
provider => 'rabbitmqctl',
require => Class['rabbitmq'],
}
rabbitmq_user { 'ironic':
admin => true,
password => 'an_even_bigger_secret',
provider => 'rabbitmqctl',
require => Class['rabbitmq'],
}
rabbitmq_user_permissions { 'ironic@/':
configure_permission => '.*',
write_permission => '.*',
read_permission => '.*',
provider => 'rabbitmqctl',
require => Class['rabbitmq'],
}
# Keystone resources, needed by Ironic to run
class { '::keystone::db::mysql':
password => 'keystone',
}
class { '::keystone':
verbose => true,
debug => true,
database_connection => 'mysql://keystone:keystone@127.0.0.1/keystone',
admin_token => 'admin_token',
enabled => true,
}
class { '::keystone::roles::admin':
email => 'test@example.tld',
password => 'a_big_secret',
}
class { '::keystone::endpoint':
public_url => "https://${::fqdn}:5000/",
admin_url => "https://${::fqdn}:35357/",
}
case $::osfamily {
'Debian': {
# Ironic resources
class { '::ironic':
rabbit_userid => 'ironic',
rabbit_password => 'an_even_bigger_secret',
rabbit_host => '127.0.0.1',
database_connection => 'mysql://ironic:a_big_secret@127.0.0.1/ironic?charset=utf8',
}
class { '::ironic::db::mysql':
password => 'a_big_secret',
}
class { '::ironic::keystone::auth':
password => 'a_big_secret',
}
class { '::ironic::client': }
class { '::ironic::conductor': }
class { '::ironic::api':
admin_password => 'a_big_secret',
}
class { '::ironic::drivers::ipmi': }
}
'RedHat': {
warning("Ironic packaging is not ready on ${::osfamily}.")
}
}
EOS
# Run it twice and test for idempotency
apply_manifest(pp, :catch_failures => true)
apply_manifest(pp, :catch_changes => true)
end
if os[:family] == 'Debian'
describe port(6385) do
it { is_expected.to be_listening.with('tcp') }
end
end
end
end

View File

@ -0,0 +1,9 @@
HOSTS:
ubuntu-14.04-amd64:
roles:
- master
platform: ubuntu-14.04-amd64
hypervisor : none
ip: 127.0.0.1
CONFIG:
type: foss

View File

@ -0,0 +1,10 @@
HOSTS:
centos-70-x64:
roles:
- master
platform: el-7-x86_64
hypervisor : none
ip: 127.0.0.1
CONFIG:
type: foss
set_env: false

View File

@ -0,0 +1,10 @@
HOSTS:
ubuntu-14.04-amd64:
roles:
- master
platform: ubuntu-14.04-amd64
hypervisor : none
ip: 127.0.0.1
CONFIG:
type: foss
set_env: false

View File

@ -0,0 +1,120 @@
#
# Copyright (C) 2013 eNovance SAS <licensing@enovance.com>
#
# Author: Emilien Macchi <emilien.macchi@enovance.com>
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
#
# Unit tests for ironic::api class
#
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',
}
end
let :params do
{ :admin_password => 'thepassword' }
end
shared_examples_for 'ironic api' do
let :p do
default_params.merge(params)
end
it { is_expected.to contain_class('ironic::params') }
it { is_expected.to contain_class('ironic::policy') }
it 'installs ironic api package' do
if platform_params.has_key?(:api_package)
is_expected.to contain_package('ironic-api').with(
:name => platform_params[:api_package],
:ensure => p[:package_ensure],
:tag => ['openstack', 'ironic-package'],
)
is_expected.to contain_package('ironic-api').with_before(/Service\[ironic-api\]/)
end
end
it 'ensure ironic api service is running' do
is_expected.to contain_service('ironic-api').with(
'hasstatus' => true,
'tag' => 'ironic-service',
)
end
it 'configures ironic.conf' do
is_expected.to contain_ironic_config('api/port').with_value(p[:port])
is_expected.to contain_ironic_config('api/host_ip').with_value(p[:host_ip])
is_expected.to contain_ironic_config('api/max_limit').with_value(p[:max_limit])
is_expected.to contain_ironic_config('keystone_authtoken/admin_password').with_value(p[:admin_password])
is_expected.to contain_ironic_config('keystone_authtoken/admin_user').with_value(p[:admin_user])
is_expected.to contain_ironic_config('keystone_authtoken/auth_uri').with_value('http://127.0.0.1:5000/')
is_expected.to contain_ironic_config('neutron/url').with_value('http://127.0.0.1:9696/')
end
context 'when overriding parameters' do
before :each do
params.merge!(
:port => '3430',
:host_ip => '127.0.0.1',
:max_limit => '10',
:auth_protocol => 'https',
:auth_host => '1.2.3.4'
)
end
it 'should replace default parameter with new value' do
is_expected.to contain_ironic_config('api/port').with_value(p[:port])
is_expected.to contain_ironic_config('api/host_ip').with_value(p[:host_ip])
is_expected.to contain_ironic_config('api/max_limit').with_value(p[:max_limit])
is_expected.to contain_ironic_config('keystone_authtoken/auth_uri').with_value('https://1.2.3.4:5000/')
end
end
end
context 'on Debian platforms' do
let :facts do
{ :osfamily => 'Debian' }
end
let :platform_params do
{ :api_package => 'ironic-api',
:api_service => 'ironic-api' }
end
it_configures 'ironic api'
end
context 'on RedHat platforms' do
let :facts do
{ :osfamily => 'RedHat' }
end
let :platform_params do
{ :api_service => 'ironic-api' }
end
it_configures 'ironic api'
end
end

View File

@ -0,0 +1,84 @@
# Copyright 2015 Hewlett-Packard Development Company, L.P.
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
#
# Unit tests for ironic::bifrost class
#
require 'spec_helper'
describe 'ironic::bifrost' do
let :default_params do
{ :git_source_repo => 'https://git.openstack.org/openstack/bifrost',
:revision => master,
:git_dest_repo_folder => '/opt/stack/bifrost',
:ironic_url => '"http://localhost:6385/"',
:network_interface => '"virbr0"',
:testing => false,
:testing_user => 'ubuntu',
:http_boot_folder => '/httpboot',
:nginx_port => 8080,
:ssh_public_key_path => '"{{ ansible_env.HOME }}/.ssh/id_rsa.pub"',
:deploy_kernel => '"{{http_boot_folder}}/coreos_production_pxe.vmlinuz"',
:deploy_ramdisk => '"{{http_boot_folder}}/coreos_production_pxe_image-oem.cpio.gz"',
:deploy_kernel_url => '"http://{{ hostvars[inventory_hostname][\'ansible_\' + network_interface][\'ipv4\'][\'address\'] }}:{{nginx_port}}/coreos_production_pxe.vmlinuz"',
:deploy_ramdisk_url => '"http://{{ hostvars[inventory_hostname][\'ansible_\' + network_interface][\'ipv4\'][\'address\'] }}:{{nginx_port}}/coreos_production_pxe_image-oem.cpio.gz"',
:deploy_image_filename => '"deployment_image.qcow2"',
:deploy_image => '"{{http_boot_folder}}/{{deploy_image_filename}}"',
:create_image_via_dib => true,
:transform_boot_image => false,
:node_default_network_interface => 'eth0',
:ipv4_subnet_mask => '255.255.255.0',
:ipv4_gateway => '192.168.1.1',
:ipv4_nameserver => '8.8.8.8',
:network_mtu => '1500',
:dhcp_pool_start => '192.168.1.200',
:dhcp_pool_end => '192.168.1.250',
:ipmi_bridging => 'no',
}
end
let :params do
{ :mysql_password => 'changeme',
:ironic_db_password => 'changeme',
:baremetal_json_hosts => 'test',
}
end
it 'should clone with vcsrepo bifrost repo with master branch' do
should contain_vcsrepo('/opt/stack/bifrost').with(
'ensure' => 'present',
'provider' => 'git',
'revision' => 'master',
'source' => 'https://git.openstack.org/openstack/bifrost',
)
end
it 'should contain file group_vars/all' do
should contain_file('/opt/stack/bifrost/playbooks/inventory/group_vars/all').with(
'ensure' => 'present',
'require' => 'Vcsrepo[/opt/stack/bifrost]',
'content' => /ironic_url/,
)
end
it 'should contain file baremetal.json' do
should contain_file('/opt/stack/bifrost/baremetal.json').with(
'ensure' => 'present',
'require' => 'Vcsrepo[/opt/stack/bifrost]',
'content' => /test/,
)
end
end

View File

@ -0,0 +1,40 @@
#
# Copyright (C) 2013 eNovance SAS <licensing@enovance.com>
#
# Author: Emilien Macchi <emilien.macchi@enovance.com>
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
#
# Unit tests for ironic::client
#
require 'spec_helper'
describe 'ironic::client' do
context 'on Debian platforms' do
let :facts do
{ :osfamily => 'Debian' }
end
it { is_expected.to contain_class('ironic::client') }
end
context 'on RedHat platforms' do
let :facts do
{ :osfamily => 'RedHat' }
end
it { is_expected.to contain_class('ironic::client') }
end
end

View File

@ -0,0 +1,106 @@
#
# Copyright (C) 2013 eNovance SAS <licensing@enovance.com>
#
# Author: Emilien Macchi <emilien.macchi@enovance.com>
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
#
# Unit tests for ironic::conductor class
#
require 'spec_helper'
describe 'ironic::conductor' do
let :default_params do
{ :package_ensure => 'present',
:enabled => true,
:max_time_interval => '120',
:force_power_state_during_sync => true }
end
let :params do
{}
end
shared_examples_for 'ironic conductor' do
let :p do
default_params.merge(params)
end
it { is_expected.to contain_class('ironic::params') }
it 'installs ironic conductor package' do
if platform_params.has_key?(:conductor_package)
is_expected.to contain_package('ironic-conductor').with(
:name => platform_params[:conductor_package],
:ensure => p[:package_ensure],
:tag => ['openstack', 'ironic-package'],
)
is_expected.to contain_package('ironic-conductor').with_before(/Service\[ironic-conductor\]/)
end
end
it 'ensure ironic conductor service is running' do
is_expected.to contain_service('ironic-conductor').with(
'hasstatus' => true,
'tag' => 'ironic-service',
)
end
it 'configures ironic.conf' do
is_expected.to contain_ironic_config('conductor/max_time_interval').with_value(p[:max_time_interval])
is_expected.to contain_ironic_config('conductor/force_power_state_during_sync').with_value(p[:force_power_state_during_sync])
end
context 'when overriding parameters' do
before :each do
params.merge!(
:max_time_interval => '50',
:force_power_state_during_sync => false
)
end
it 'should replace default parameter with new value' do
is_expected.to contain_ironic_config('conductor/max_time_interval').with_value(p[:max_time_interval])
is_expected.to contain_ironic_config('conductor/force_power_state_during_sync').with_value(p[:force_power_state_during_sync])
end
end
end
context 'on Debian platforms' do
let :facts do
{ :osfamily => 'Debian' }
end
let :platform_params do
{ :conductor_package => 'ironic-conductor',
:conductor_service => 'ironic-conductor' }
end
it_configures 'ironic conductor'
end
context 'on RedHat platforms' do
let :facts do
{ :osfamily => 'RedHat' }
end
let :platform_params do
{ :conductor_service => 'ironic-conductor' }
end
it_configures 'ironic conductor'
end
end

View File

@ -0,0 +1,20 @@
require 'spec_helper'
describe 'ironic::config' do
let :params do
{ :ironic_config => {
'DEFAULT/foo' => { 'value' => 'fooValue' },
'DEFAULT/bar' => { 'value' => 'barValue' },
'DEFAULT/baz' => { 'ensure' => 'absent' }
}
}
end
it 'configures arbitrary ironic configurations' do
is_expected.to contain_ironic_config('DEFAULT/foo').with_value('fooValue')
is_expected.to contain_ironic_config('DEFAULT/bar').with_value('barValue')
is_expected.to contain_ironic_config('DEFAULT/baz').with_ensure('absent')
end
end

View File

@ -0,0 +1,89 @@
#
# Copyright (C) 2013 eNovance SAS <licensing@enovance.com>
#
# Author: Emilien Macchi <emilien.macchi@enovance.com>
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
#
# Unit tests for ironic::db::mysql
#
require 'spec_helper'
describe 'ironic::db::mysql' do
let :pre_condition do
'include mysql::server'
end
let :params do
{ :password => 'passw0rd' }
end
let :facts do
{ :osfamily => 'Debian' }
end
context 'on Debian platforms' do
let :facts do
{ :osfamily => 'Debian' }
end
it { is_expected.to contain_openstacklib__db__mysql('ironic').with(
:user => 'ironic',
:password_hash => '*74B1C21ACE0C2D6B0678A5E503D2A60E8F9651A3',
:charset => 'utf8',
:collate => 'utf8_general_ci',
)}
end
context 'on RedHat platforms' do
let :facts do
{ :osfamily => 'RedHat' }
end
it { is_expected.to contain_openstacklib__db__mysql('ironic').with(
:user => 'ironic',
:password_hash => '*74B1C21ACE0C2D6B0678A5E503D2A60E8F9651A3',
:charset => 'utf8',
:collate => 'utf8_general_ci',
)}
end
describe "overriding allowed_hosts param to array" do
let :params do
{
:allowed_hosts => ['127.0.0.1','%']
}
end
end
describe "overriding allowed_hosts param to string" do
let :params do
{
:allowed_hosts => '192.168.1.1'
}
end
end
describe "overriding allowed_hosts param equals to host param " do
let :params do
{
:allowed_hosts => '127.0.0.1'
}
end
end
end

View File

@ -0,0 +1,58 @@
require 'spec_helper'
describe 'ironic::db::postgresql' do
let :req_params do
{ :password => 'pw' }
end
let :pre_condition do
'include postgresql::server'
end
context 'on a RedHat osfamily' do
let :facts do
{
:osfamily => 'RedHat',
:operatingsystemrelease => '7.0',
:concat_basedir => '/var/lib/puppet/concat'
}
end
context 'with only required parameters' do
let :params do
req_params
end
it { is_expected.to contain_postgresql__server__db('ironic').with(
:user => 'ironic',
:password => 'md554bdb85e136b50c40104fd9f73e1294d'
)}
end
end
context 'on a Debian osfamily' do
let :facts do
{
:operatingsystemrelease => '7.8',
:operatingsystem => 'Debian',
:osfamily => 'Debian',
:concat_basedir => '/var/lib/puppet/concat'
}
end
context 'with only required parameters' do
let :params do
req_params
end
it { is_expected.to contain_postgresql__server__db('ironic').with(
:user => 'ironic',
:password => 'md554bdb85e136b50c40104fd9f73e1294d'
)}
end
end
end

View File

@ -0,0 +1,44 @@
require 'spec_helper'
describe 'ironic::db::sync' do
shared_examples_for 'ironic-dbsync' do
it 'runs ironic-manage db_sync' do
is_expected.to contain_exec('ironic-dbsync').with(
:command => 'ironic-dbsync --config-file /etc/ironic/ironic.conf',
:path => '/usr/bin',
:user => 'root',
:refreshonly => 'true',
:logoutput => 'on_failure'
)
end
end
context 'on a RedHat osfamily' do
let :facts do
{
:osfamily => 'RedHat',
:operatingsystemrelease => '7.0',
:concat_basedir => '/var/lib/puppet/concat'
}
end
it_configures 'ironic-dbsync'
end
context 'on a Debian osfamily' do
let :facts do
{
:operatingsystemrelease => '7.8',
:operatingsystem => 'Debian',
:osfamily => 'Debian',
:concat_basedir => '/var/lib/puppet/concat'
}
end
it_configures 'ironic-dbsync'
end
end

View File

@ -0,0 +1,69 @@
#
# Copyright (C) 2013 eNovance SAS <licensing@enovance.com>
#
# Author: Emilien Macchi <emilien.macchi@enovance.com>
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
#
# Unit tests for ironic::drivers::ipmi class
#
require 'spec_helper'
describe 'ironic::drivers::ipmi' do
let :default_params do
{ :retry_timeout => '10' }
end
let :params do
{}
end
shared_examples_for 'ironic ipmi driver' do
let :p do
default_params.merge(params)
end
it 'configures ironic.conf' do
is_expected.to contain_ironic_config('ipmi/retry_timeout').with_value(p[:retry_timeout])
end
context 'when overriding parameters' do
before do
params.merge!(:retry_timeout => '50')
end
it 'should replace default parameter with new value' do
is_expected.to contain_ironic_config('ipmi/retry_timeout').with_value(p[:retry_timeout])
end
end
end
context 'on Debian platforms' do
let :facts do
{ :osfamily => 'Debian' }
end
it_configures 'ironic ipmi driver'
end
context 'on RedHat platforms' do
let :facts do
{ :osfamily => 'RedHat' }
end
it_configures 'ironic ipmi driver'
end
end

View File

@ -0,0 +1,104 @@
#
# Copyright (C) 2013 eNovance SAS <licensing@enovance.com>
#
# Author: Emilien Macchi <emilien.macchi@enovance.com>
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
#
# Unit tests for ironic::drivers::pxe class
#
require 'spec_helper'
describe 'ironic::drivers::pxe' do
let :default_params do
{ :pxe_append_params => 'nofb nomodeset vga=normal',
:pxe_config_template => '$pybasedir/drivers/modules/pxe_config.template',
:pxe_deploy_timeout => '0',
:tftp_server => '$my_ip',
:tftp_root => '/tftpboot',
:images_path => '/var/lib/ironic/images/',
:tftp_master_path => '/tftpboot/master_images',
:instance_master_path => '/var/lib/ironic/master_images' }
end
let :params do
{}
end
shared_examples_for 'ironic pxe driver' do
let :p do
default_params.merge(params)
end
it 'configures ironic.conf' do
is_expected.to contain_ironic_config('pxe/pxe_append_params').with_value(p[:pxe_append_params])
is_expected.to contain_ironic_config('pxe/pxe_config_template').with_value(p[:pxe_config_template])
is_expected.to contain_ironic_config('pxe/pxe_deploy_timeout').with_value(p[:pxe_deploy_timeout])
is_expected.to contain_ironic_config('pxe/tftp_server').with_value(p[:tftp_server])
is_expected.to contain_ironic_config('pxe/tftp_root').with_value(p[:tftp_root])
is_expected.to contain_ironic_config('pxe/images_path').with_value(p[:images_path])
is_expected.to contain_ironic_config('pxe/tftp_master_path').with_value(p[:tftp_master_path])
is_expected.to contain_ironic_config('pxe/instance_master_path').with_value(p[:instance_master_path])
end
context 'when overriding parameters' do
before do
params.merge!(
:deploy_kernel => 'foo',
:deploy_ramdisk => 'bar',
:pxe_append_params => 'foo',
:pxe_config_template => 'bar',
:pxe_deploy_timeout => '40',
:tftp_server => '192.168.0.1',
:tftp_root => '/mnt/ftp',
:images_path => '/mnt/images',
:tftp_master_path => '/mnt/master_images',
:instance_master_path => '/mnt/ironic/master_images'
)
end
it 'should replace default parameter with new value' do
is_expected.to contain_ironic_config('pxe/deploy_kernel').with_value(p[:deploy_kernel])
is_expected.to contain_ironic_config('pxe/deploy_ramdisk').with_value(p[:deploy_ramdisk])
is_expected.to contain_ironic_config('pxe/pxe_append_params').with_value(p[:pxe_append_params])
is_expected.to contain_ironic_config('pxe/pxe_config_template').with_value(p[:pxe_config_template])
is_expected.to contain_ironic_config('pxe/pxe_deploy_timeout').with_value(p[:pxe_deploy_timeout])
is_expected.to contain_ironic_config('pxe/tftp_server').with_value(p[:tftp_server])
is_expected.to contain_ironic_config('pxe/tftp_root').with_value(p[:tftp_root])
is_expected.to contain_ironic_config('pxe/images_path').with_value(p[:images_path])
is_expected.to contain_ironic_config('pxe/tftp_master_path').with_value(p[:tftp_master_path])
is_expected.to contain_ironic_config('pxe/instance_master_path').with_value(p[:instance_master_path])
end
end
end
context 'on Debian platforms' do
let :facts do
{ :osfamily => 'Debian' }
end
it_configures 'ironic pxe driver'
end
context 'on RedHat platforms' do
let :facts do
{ :osfamily => 'RedHat' }
end
it_configures 'ironic pxe driver'
end
end

View File

@ -0,0 +1,315 @@
#
# Copyright (C) 2013 eNovance SAS <licensing@enovance.com>
#
# Author: Emilien Macchi <emilien.macchi@enovance.com>
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
#
# Unit tests for ironic
#
require 'spec_helper'
describe 'ironic' do
let :params do
{ :package_ensure => 'present',
:verbose => false,
:debug => false,
:enabled_drivers => ['pxe_ipmitool'],
:rabbit_host => '127.0.0.1',
:rabbit_port => 5672,
:rabbit_hosts => false,
:rabbit_userid => 'guest',
:rabbit_password => 'guest',
:rabbit_virtual_host => '/',
:database_connection => 'sqlite:////var/lib/ironic/ironic.sqlite',
:database_max_retries => 10,
:database_idle_timeout => 3600,
:database_reconnect_interval => 10,
:database_retry_interval => 10,
:glance_num_retries => 0,
:glance_api_insecure => false
}
end
shared_examples_for 'ironic' do
context 'and if rabbit_host parameter is provided' do
it_configures 'a ironic base installation'
it_configures 'with SSL disabled'
it_configures 'with SSL enabled without kombu'
it_configures 'with SSL enabled with kombu'
end
context 'and if rabbit_hosts parameter is provided' do
before do
params.delete(:rabbit_host)
params.delete(:rabbit_port)
end
context 'with one server' do
before { params.merge!( :rabbit_hosts => ['127.0.0.1:5672'] ) }
it_configures 'a ironic base installation'
it_configures 'rabbit HA with a single virtual host'
end
context 'with multiple servers' do
before { params.merge!( :rabbit_hosts => ['rabbit1:5672', 'rabbit2:5672'] ) }
it_configures 'a ironic base installation'
it_configures 'rabbit HA with multiple hosts'
end
end
context 'with mysql database backend' do
before do
params.merge!(:database_connection => 'mysql://ironic:ironic@localhost/ironic')
end
end
context 'with sqlite database backend' do
before do
params.merge!(:database_connection => 'sqlite:////var/lib/ironic/ironic.sqlite')
end
it { is_expected.to contain_package('ironic-database-backend').with_name('python-pysqlite2')}
end
context 'with postgresql database backend' do
before do
params.merge!(:database_connection => 'postgresql://ironic:ironic@localhost/ironic')
end
it { is_expected.to contain_package('ironic-database-backend').with_name('python-psycopg2')}
end
it_configures 'with syslog disabled'
it_configures 'with syslog enabled'
it_configures 'with syslog enabled and custom settings'
end
shared_examples_for 'a ironic base installation' do
it { is_expected.to contain_class('ironic::params') }
it 'configures ironic configuration folder' do
is_expected.to contain_file('/etc/ironic/').with(
:ensure => 'directory',
:group => 'ironic',
:require => 'Package[ironic-common]'
)
end
it 'configures ironic configuration file' do
is_expected.to contain_file('/etc/ironic/ironic.conf').with(
:group => 'ironic',
:require => 'Package[ironic-common]'
)
end
it 'installs ironic package' do
is_expected.to contain_package('ironic-common').with(
:ensure => 'present',
:name => platform_params[:common_package_name],
:tag => ['openstack', 'ironic-package'],
)
end
it 'configures enabled_drivers' do
is_expected.to contain_ironic_config('DEFAULT/enabled_drivers').with_value( params[:enabled_drivers] )
end
it 'configures credentials for rabbit' do
is_expected.to contain_ironic_config('oslo_messaging_rabbit/rabbit_userid').with_value( params[:rabbit_userid] )
is_expected.to contain_ironic_config('oslo_messaging_rabbit/rabbit_password').with_value( params[:rabbit_password] )
is_expected.to contain_ironic_config('oslo_messaging_rabbit/rabbit_virtual_host').with_value( params[:rabbit_virtual_host] )
is_expected.to contain_ironic_config('oslo_messaging_rabbit/rabbit_password').with_secret( true )
end
it 'should perform default database configuration' do
is_expected.to contain_ironic_config('database/connection').with_value(params[:database_connection])
is_expected.to contain_ironic_config('database/max_retries').with_value(params[:database_max_retries])
is_expected.to contain_ironic_config('database/idle_timeout').with_value(params[:database_idle_timeout])
is_expected.to contain_ironic_config('database/retry_interval').with_value(params[:database_retry_interval])
end
it 'configures glance connection' do
is_expected.to contain_ironic_config('glance/glance_num_retries').with_value(params[:glance_num_retries])
is_expected.to contain_ironic_config('glance/glance_api_insecure').with_value(params[:glance_api_insecure])
end
it 'configures ironic.conf' do
is_expected.to contain_ironic_config('DEFAULT/verbose').with_value( params[:verbose] )
is_expected.to contain_ironic_config('DEFAULT/auth_strategy').with_value('keystone')
is_expected.to contain_ironic_config('DEFAULT/control_exchange').with_value('openstack')
end
end
shared_examples_for 'rabbit HA with a single virtual host' do
it 'in ironic.conf' do
is_expected.to contain_ironic_config('oslo_messaging_rabbit/rabbit_host').with_ensure('absent')
is_expected.to contain_ironic_config('oslo_messaging_rabbit/rabbit_port').with_ensure('absent')
is_expected.to contain_ironic_config('oslo_messaging_rabbit/rabbit_hosts').with_value( params[:rabbit_hosts] )
is_expected.to contain_ironic_config('oslo_messaging_rabbit/rabbit_ha_queues').with_value(true)
end
end
shared_examples_for 'rabbit HA with multiple hosts' do
it 'in ironic.conf' do
is_expected.to contain_ironic_config('oslo_messaging_rabbit/rabbit_host').with_ensure('absent')
is_expected.to contain_ironic_config('oslo_messaging_rabbit/rabbit_port').with_ensure('absent')
is_expected.to contain_ironic_config('oslo_messaging_rabbit/rabbit_hosts').with_value( params[:rabbit_hosts].join(',') )
is_expected.to contain_ironic_config('oslo_messaging_rabbit/rabbit_ha_queues').with_value(true)
end
end
shared_examples_for 'with SSL enabled with kombu' do
before do
params.merge!(
:rabbit_use_ssl => true,
:kombu_ssl_ca_certs => '/path/to/ssl/ca/certs',
:kombu_ssl_certfile => '/path/to/ssl/cert/file',
:kombu_ssl_keyfile => '/path/to/ssl/keyfile',
:kombu_ssl_version => 'TLSv1'
)
end
it do
is_expected.to contain_ironic_config('oslo_messaging_rabbit/rabbit_use_ssl').with_value('true')
is_expected.to contain_ironic_config('oslo_messaging_rabbit/kombu_ssl_ca_certs').with_value('/path/to/ssl/ca/certs')
is_expected.to contain_ironic_config('oslo_messaging_rabbit/kombu_ssl_certfile').with_value('/path/to/ssl/cert/file')
is_expected.to contain_ironic_config('oslo_messaging_rabbit/kombu_ssl_keyfile').with_value('/path/to/ssl/keyfile')
is_expected.to contain_ironic_config('oslo_messaging_rabbit/kombu_ssl_version').with_value('TLSv1')
end
end
shared_examples_for 'with SSL enabled without kombu' do
before do
params.merge!(
:rabbit_use_ssl => true,
)
end
it do
is_expected.to contain_ironic_config('oslo_messaging_rabbit/rabbit_use_ssl').with_value('true')
is_expected.to contain_ironic_config('oslo_messaging_rabbit/kombu_ssl_ca_certs').with_ensure('absent')
is_expected.to contain_ironic_config('oslo_messaging_rabbit/kombu_ssl_certfile').with_ensure('absent')
is_expected.to contain_ironic_config('oslo_messaging_rabbit/kombu_ssl_keyfile').with_ensure('absent')
is_expected.to contain_ironic_config('oslo_messaging_rabbit/kombu_ssl_version').with_value('TLSv1')
end
end
shared_examples_for 'with SSL disabled' do
before do
params.merge!(
:rabbit_use_ssl => false,
:kombu_ssl_ca_certs => 'undef',
:kombu_ssl_certfile => 'undef',
:kombu_ssl_keyfile => 'undef',
:kombu_ssl_version => 'TLSv1'
)
end
it do
is_expected.to contain_ironic_config('oslo_messaging_rabbit/rabbit_use_ssl').with_value('false')
is_expected.to contain_ironic_config('oslo_messaging_rabbit/kombu_ssl_ca_certs').with_ensure('absent')
is_expected.to contain_ironic_config('oslo_messaging_rabbit/kombu_ssl_certfile').with_ensure('absent')
is_expected.to contain_ironic_config('oslo_messaging_rabbit/kombu_ssl_keyfile').with_ensure('absent')
is_expected.to contain_ironic_config('oslo_messaging_rabbit/kombu_ssl_version').with_ensure('absent')
end
end
shared_examples_for 'with amqp_durable_queues disabled' do
it { is_expected.to contain_ironic_config('DEFAULT/amqp_durable_queues').with_value(false) }
end
shared_examples_for 'with amqp_durable_queues enabled' do
before do
params.merge( :amqp_durable_queues => true )
end
it { is_expected.to contain_ironic_config('DEFAULT/amqp_durable_queues').with_value(true) }
end
shared_examples_for 'with syslog disabled' do
it { is_expected.to contain_ironic_config('DEFAULT/use_syslog').with_value(false) }
end
shared_examples_for 'with syslog enabled' do
before do
params.merge!( :use_syslog => true )
end
it do
is_expected.to contain_ironic_config('DEFAULT/use_syslog').with_value(true)
is_expected.to contain_ironic_config('DEFAULT/syslog_log_facility').with_value('LOG_USER')
end
end
shared_examples_for 'with syslog enabled and custom settings' do
before do
params.merge!(
:use_syslog => true,
:log_facility => 'LOG_LOCAL0'
)
end
it do
is_expected.to contain_ironic_config('DEFAULT/use_syslog').with_value(true)
is_expected.to contain_ironic_config('DEFAULT/syslog_log_facility').with_value('LOG_LOCAL0')
end
end
shared_examples_for 'with one glance server' do
before do
params.merge!(:glance_api_servers => '10.0.0.1:9292')
end
it 'should configure one glance server' do
is_expected.to contain_ironic_config('glance/glance_api_servers').with_value(p[:glance_api_servers])
end
end
shared_examples_for 'with two glance servers' do
before do
params.merge!(:glance_api_servers => ['10.0.0.1:9292','10.0.0.2:9292'])
end
it 'should configure one glance server' do
is_expected.to contain_ironic_config('glance/glance_api_servers').with_value(p[:glance_api_servers].join(','))
end
end
context 'on Debian platforms' do
let :facts do
{ :osfamily => 'Debian' }
end
let :platform_params do
{ :common_package_name => 'ironic-common' }
end
it_configures 'ironic'
end
context 'on RedHat platforms' do
let :facts do
{ :osfamily => 'RedHat' }
end
let :platform_params do
{ :common_package_name => 'openstack-ironic-common' }
end
it_configures 'ironic'
end
end

View File

@ -0,0 +1,177 @@
#
# Copyright (C) 2013 eNovance SAS <licensing@enovance.com>
#
# Author: Emilien Macchi <emilien.macchi@enovance.com>
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
#
# Unit tests for ironic::keystone::auth
#
require 'spec_helper'
describe 'ironic::keystone::auth' do
let :facts do
{ :osfamily => 'Debian' }
end
describe 'with default class parameters' do
let :params do
{ :password => 'ironic_password',
:tenant => 'foobar' }
end
it { is_expected.to contain_keystone_user('ironic').with(
:ensure => 'present',
:password => 'ironic_password',
:tenant => 'foobar'
) }
it { is_expected.to contain_keystone_user_role('ironic@foobar').with(
:ensure => 'present',
:roles => ['admin']
)}
it { is_expected.to contain_keystone_service('ironic').with(
:ensure => 'present',
:type => 'baremetal',
:description => 'Ironic Bare Metal Provisioning Service'
) }
it { is_expected.to contain_keystone_endpoint('RegionOne/ironic').with(
:ensure => 'present',
:public_url => "http://127.0.0.1:6385",
:admin_url => "http://127.0.0.1:6385",
:internal_url => "http://127.0.0.1:6385"
) }
end
describe 'when configuring ironic-server' do
let :pre_condition do
"class { 'ironic::server': auth_password => 'test' }"
end
let :params do
{ :password => 'ironic_password',
:tenant => 'foobar' }
end
#FIXME it { should contain_keystone_endpoint('RegionOne/ironic').with_notify('Service[ironic-server]') }
end
describe 'with endpoint parameters' do
let :params do
{ :password => 'ironic_password',
:public_url => 'https://10.0.0.10:6385',
:admin_url => 'https://10.0.0.11:6385',
:internal_url => 'https://10.0.0.11:6385' }
end
it { is_expected.to contain_keystone_endpoint('RegionOne/ironic').with(
:ensure => 'present',
:public_url => 'https://10.0.0.10:6385',
:admin_url => 'https://10.0.0.11:6385',
:internal_url => 'https://10.0.0.11:6385'
) }
end
describe 'with deprecated endpoint parameters' do
let :params do
{ :password => 'ironic_password',
:public_protocol => 'https',
:public_port => '80',
:public_address => '10.10.10.10',
:port => '81',
:internal_address => '10.10.10.11',
:admin_address => '10.10.10.12' }
end
it { is_expected.to contain_keystone_endpoint('RegionOne/ironic').with(
:ensure => 'present',
:public_url => "https://10.10.10.10:80",
:internal_url => "http://10.10.10.11:81",
:admin_url => "http://10.10.10.12:81"
) }
end
describe 'when overriding auth name' do
let :params do
{ :password => 'foo',
:auth_name => 'ironicy' }
end
it { is_expected.to contain_keystone_user('ironicy') }
it { is_expected.to contain_keystone_user_role('ironicy@services') }
it { is_expected.to contain_keystone_service('ironicy') }
it { is_expected.to contain_keystone_endpoint('RegionOne/ironicy') }
end
describe 'when overriding service name' do
let :params do
{
:service_name => 'ironic_service',
:password => 'ironic_password',
}
end
it { is_expected.to contain_keystone_user('ironic') }
it { is_expected.to contain_keystone_user_role('ironic@services') }
it { is_expected.to contain_keystone_service('ironic_service') }
it { is_expected.to contain_keystone_endpoint('RegionOne/ironic_service') }
end
describe 'when disabling user configuration' do
let :params do
{
:password => 'ironic_password',
:configure_user => false
}
end
it { is_expected.not_to contain_keystone_user('ironic') }
it { is_expected.to contain_keystone_user_role('ironic@services') }
it { is_expected.to contain_keystone_service('ironic').with(
:ensure => 'present',
:type => 'baremetal',
:description => 'Ironic Bare Metal Provisioning Service'
) }
end
describe 'when disabling user and user role configuration' do
let :params do
{
:password => 'ironic_password',
:configure_user => false,
:configure_user_role => false
}
end
it { is_expected.not_to contain_keystone_user('ironic') }
it { is_expected.not_to contain_keystone_user_role('ironic@services') }
it { is_expected.to contain_keystone_service('ironic').with(
:ensure => 'present',
:type => 'baremetal',
:description => 'Ironic Bare Metal Provisioning Service'
) }
end
end

View File

@ -0,0 +1,41 @@
require 'spec_helper'
describe 'ironic::policy' do
shared_examples_for 'ironic policies' do
let :params do
{
:policy_path => '/etc/ironic/policy.json',
:policies => {
'context_is_admin' => {
'key' => 'context_is_admin',
'value' => 'foo:bar'
}
}
}
end
it 'set up the policies' do
is_expected.to contain_openstacklib__policy__base('context_is_admin').with({
:key => 'context_is_admin',
:value => 'foo:bar'
})
end
end
context 'on Debian platforms' do
let :facts do
{ :osfamily => 'Debian' }
end
it_configures 'ironic policies'
end
context 'on RedHat platforms' do
let :facts do
{ :osfamily => 'RedHat' }
end
it_configures 'ironic policies'
end
end

View File

@ -0,0 +1,5 @@
shared_examples_for "a Puppet::Error" do |description|
it "with message matching #{description.inspect}" do
expect { is_expected.to have_class_count(1) }.to raise_error(Puppet::Error, description)
end
end

View File

@ -0,0 +1,7 @@
require 'puppetlabs_spec_helper/module_spec_helper'
require 'shared_examples'
RSpec.configure do |c|
c.alias_it_should_behave_like_to :it_configures, 'configures'
c.alias_it_should_behave_like_to :it_raises, 'raises'
end

View File

@ -0,0 +1,56 @@
require 'beaker-rspec'
require 'beaker/puppet_install_helper'
run_puppet_install_helper
RSpec.configure do |c|
# Project root
proj_root = File.expand_path(File.join(File.dirname(__FILE__), '..'))
modname = JSON.parse(open('metadata.json').read)['name'].split('-')[1]
# Readable test descriptions
c.formatter = :documentation
# Configure all nodes in nodeset
c.before :suite do
# Install module and dependencies
hosts.each do |host|
# install git
install_package host, 'git'
zuul_ref = ENV['ZUUL_REF']
zuul_branch = ENV['ZUUL_BRANCH']
zuul_url = ENV['ZUUL_URL']
repo = 'openstack/puppet-openstack-integration'
# Start out with clean moduledir, don't trust r10k to purge it
on host, "rm -rf /etc/puppet/modules/*"
# Install dependent modules via git or zuul
r = on host, "test -e /usr/zuul-env/bin/zuul-cloner", { :acceptable_exit_codes => [0,1] }
if r.exit_code == 0
zuul_clone_cmd = '/usr/zuul-env/bin/zuul-cloner '
zuul_clone_cmd += '--cache-dir /opt/git '
zuul_clone_cmd += "--zuul-ref #{zuul_ref} "
zuul_clone_cmd += "--zuul-branch #{zuul_branch} "
zuul_clone_cmd += "--zuul-url #{zuul_url} "
zuul_clone_cmd += "git://git.openstack.org #{repo}"
on host, zuul_clone_cmd
else
on host, "git clone https://git.openstack.org/#{repo} #{repo}"
end
on host, "ZUUL_REF=#{zuul_ref} ZUUL_BRANCH=#{zuul_branch} ZUUL_URL=#{zuul_url} bash #{repo}/install_modules.sh"
# Install the module being tested
on host, "rm -fr /etc/puppet/modules/#{modname}"
puppet_module_install(:source => proj_root, :module_name => modname)
on host, "rm -fr #{repo}"
# List modules installed to help with debugging
on host, puppet('module','list'), { :acceptable_exit_codes => 0 }
end
end
end

View File

@ -0,0 +1,42 @@
$LOAD_PATH.push(
File.join(
File.dirname(__FILE__),
'..',
'..',
'..',
'fixtures',
'modules',
'inifile',
'lib')
)
require 'spec_helper'
provider_class = Puppet::Type.type(:ironic_config).provider(:ini_setting)
describe provider_class do
it 'should default to the default setting when no other one is specified' do
resource = Puppet::Type::Ironic_config.new(
{
:name => 'DEFAULT/foo',
:value => 'bar'
}
)
provider = provider_class.new(resource)
expect(provider.section).to eq('DEFAULT')
expect(provider.setting).to eq('foo')
end
it 'should allow setting to be set explicitly' do
resource = Puppet::Type::Ironic_config.new(
{
:name => 'dude/foo',
:value => 'bar'
}
)
provider = provider_class.new(resource)
expect(provider.section).to eq('dude')
expect(provider.setting).to eq('foo')
end
end

View File

@ -0,0 +1,111 @@
require 'puppet'
require 'spec_helper'
require 'puppet/provider/ironic'
require 'tempfile'
describe Puppet::Provider::Ironic do
def klass
described_class
end
let :credential_hash do
{
'auth_host' => '192.168.56.210',
'auth_port' => '35357',
'auth_protocol' => 'https',
'admin_tenant_name' => 'admin_tenant',
'admin_user' => 'admin',
'admin_password' => 'password',
}
end
let :auth_endpoint do
'https://192.168.56.210:35357/v2.0/'
end
let :credential_error do
/Ironic types will not work/
end
after :each do
klass.reset
end
describe 'when determining credentials' do
it 'should fail if config is empty' do
conf = {}
klass.expects(:ironic_conf).returns(conf)
expect do
klass.ironic_credentials
end.to raise_error(Puppet::Error, credential_error)
end
it 'should fail if config does not have keystone_authtoken section.' do
conf = {'foo' => 'bar'}
klass.expects(:ironic_conf).returns(conf)
expect do
klass.ironic_credentials
end.to raise_error(Puppet::Error, credential_error)
end
it 'should fail if config does not contain all auth params' do
conf = {'keystone_authtoken' => {'invalid_value' => 'foo'}}
klass.expects(:ironic_conf).returns(conf)
expect do
klass.ironic_credentials
end.to raise_error(Puppet::Error, credential_error)
end
it 'should use specified host/port/protocol in the auth endpoint' do
conf = {'keystone_authtoken' => credential_hash}
klass.expects(:ironic_conf).returns(conf)
expect(klass.get_auth_endpoint).to eq(auth_endpoint)
end
end
describe 'when invoking the ironic cli' do
it 'should set auth credentials in the environment' do
authenv = {
:OS_AUTH_URL => auth_endpoint,
:OS_USERNAME => credential_hash['admin_user'],
:OS_TENANT_NAME => credential_hash['admin_tenant_name'],
:OS_PASSWORD => credential_hash['admin_password'],
}
klass.expects(:get_ironic_credentials).with().returns(credential_hash)
klass.expects(:withenv).with(authenv)
klass.auth_ironic('test_retries')
end
['[Errno 111] Connection refused',
'(HTTP 400)'].reverse.each do |valid_message|
it "should retry when ironic cli returns with error #{valid_message}" do
klass.expects(:get_ironic_credentials).with().returns({})
klass.expects(:sleep).with(10).returns(nil)
klass.expects(:ironic).twice.with(['test_retries']).raises(
Exception, valid_message).then.returns('')
klass.auth_ironic('test_retries')
end
end
end
describe 'when listing ironic resources' do
it 'should exclude the column header' do
output = <<-EOT
id
net1
net2
EOT
klass.expects(:auth_ironic).returns(output)
result = klass.list_ironic_resources('foo')
expect(result).to eql(['net1', 'net2'])
end
end
end

View File

@ -0,0 +1,19 @@
require 'puppet'
require 'puppet/type/ironic_config'
describe 'Puppet::Type.type(:ironic_config)' do
before :each do
@ironic_config = Puppet::Type.type(:ironic_config).new(:name => 'DEFAULT/foo', :value => 'bar')
end
it 'should autorequire the package that install the file' do
catalog = Puppet::Resource::Catalog.new
package = Puppet::Type.type(:package).new(:name => 'ironic-common')
catalog.add_resource package, @ironic_config
dependency = @ironic_config.autorequire
expect(dependency.size).to eq(1)
expect(dependency[0].target).to eq(@ironic_config)
expect(dependency[0].source).to eq(package)
end
end

View File

@ -0,0 +1,3 @@
{
<%= @baremetal_json_hosts %>
}

View File

@ -0,0 +1,44 @@
---
ironic_url: <%= @ironic_url %>
network_interface: <%= @network_interface %>
# ironic_db_password ironic user password for rabbit
ironic_db_password: <%= @ironic_db_password %>
# mysql_password: mysql root user password
mysql_password: <%= @mysql_password %>
# If testing is true, then the environment is setup for using libvirt
# virtual machines for the hardware instead of real hardware.
# testing: true
#
# Normally this user should be ubuntu, however if cirros is used,
# a user may wish to define a specific user for testing VM
# connectivity during atest sequence
testing: <%= @testing %>
testing_user: <%= @testing_user %>
http_boot_folder: <%= @http_boot_folder %>
nginx_port: <%= @nginx_port %>
ssh_public_key_path: <%= @ssh_public_key_path %>
deploy_kernel: <%= @deploy_kernel %>
deploy_ramdisk: <%= @deploy_ramdisk %>
deploy_kernel_url: <%= @deploy_kernel_url %>
deploy_ramdisk_url: <%= @deploy_ramdisk_url %>
# When using disk image builder based image generation, which is the
# default at this time, the deploy_image_filename must end with .qcow2
# due to the image creation process.
deploy_image_filename: <%= @deploy_image_filename %>
deploy_image: <%= @deploy_image %>
# Setting to utilize diskimage-builder to create a bootable image.
create_image_via_dib: <%= @create_image_via_dib %>
# Transform boot image is intended for use with the Ubuntu trusty image. It makes the image bootable by installing Grub.
# Setting to prepend a partition image with a boot sector and partition table.
transform_boot_image: <%= @transform_boot_image %>
node_default_network_interface: <%= @node_default_network_interface %>
# ipv4_subnet_mask is intended for the static ipv4 address assignments.
ipv4_subnet_mask: <%= @ipv4_subnet_mask %>
ipv4_gateway: <%= @ipv4_gateway %>
ipv4_nameserver: <%= @ipv4_nameserver %>
network_mtu: <%= @network_mtu %>
dhcp_pool_start: <%= @dhcp_pool_start %>
dhcp_pool_end: <%= @dhcp_pool_end %>
# ipmi_bridging: Default undefined. Valid values: "no", "single", and "dual"
# See http://docs.openstack.org/developer/ironic/_modules/ironic/drivers/modules/ipmitool.html
ipmi_bridging: <%= @ipmi_bridging %>

View File

@ -61,6 +61,16 @@
interval: 20
timeout: 180
- id: ironic-conductor-config
role: ['ironic']
type: puppet
required_for: [post_deployment_end]
requires: [ironic-upload-images]
parameters:
puppet_manifest: puppet/manifests/ironic-conductor-config.pp
puppet_modules: puppet/modules:/etc/puppet/modules
timeout: 3600
- id: ironic-swift-key
role: ['primary-controller']
type: shell