Import plugin's code

Change-Id: I8d7f3d88401eadfb627795c4dc72ec1b8a1d9769
This commit is contained in:
Ivan Zinoviev 2017-07-18 20:29:21 +03:00
parent 4d6486d5ca
commit 5fff5a4964
256 changed files with 13246 additions and 0 deletions

201
LICENSE Normal file
View File

@ -0,0 +1,201 @@
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.
END OF TERMS AND CONDITIONS
APPENDIX: How to apply the Apache License to your work.
To apply the Apache License to your work, attach the following
boilerplate notice, with the fields enclosed by brackets "[]"
replaced with your own identifying information. (Don't include
the brackets!) The text should be enclosed in the appropriate
comment syntax for the file format. We also recommend that a
file or class name and description of purpose be included on the
same "printed page" as the copyright notice for easier
identification within third-party archives.
Copyright [yyyy] [name of copyright owner]
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.

View File

@ -0,0 +1,52 @@
# Copyright 2017 Sberbank
#
# 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.
notice('fuel-plugin-tempest: tempest_install.pp')
$plugin_hash = hiera_hash('tempest', {})
if has_key($plugin_hash, 'image_name') {
$image_name = $plugin_hash['image_name']
} else {
$image_name = 'TestVM'
}
$service_endpoint = hiera('service_endpoint')
$auth_url_v2 = "http://${service_endpoint}:35357/v2.0"
$auth_url_v3 = "http://${service_endpoint}:35357/v3"
class { 'tempest':
package_ensure => 'latest',
tempest_workspace => '/var/lib/tempest/default',
tempest_config_file => '/var/lib/tempest/default/etc/tempest.conf',
lock_path => '/var/lib/tempest/default/tempest_lock',
install_from_source => false,
git_clone => false,
image_name => $image_name,
image_name_alt => $image_name,
configure_networks => false,
identity_uri => $auth_url_v2,
identity_uri_v3 => $auth_url_v3,
auth_version => 'v3',
region_name => hiera('region', 'RegionOne'),
manage_tests_packages => false,
admin_username => 'admin',
admin_password => 'admin',
admin_project_name => 'admin',
admin_role => 'admin',
admin_domain_name => 'Default',
img_dir => undef,
img_file => undef,
min_compute_nodes => 3,
}

View File

@ -0,0 +1,35 @@
# Copyright 2017 Sberbank
#
# 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.
notice('fuel-plugin-tempest: tempest_run.pp')
$plugin_hash = hiera_hash('tempest', {})
$run_tests = $plugin_hash['run_tests']
if $run_tests {
$run_script = $plugin_hash['run_script']
file { '/tmp/tempest-run.sh':
ensure => file,
owner => 'root',
mode => '0744',
content => inline_template("#!/bin/bash -eux\n$run_script"),
}->
exec { 'run_tempest_tests':
command => 'bash /tmp/tempest-run.sh',
path => '/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin',
user => 'root',
logoutput => 'on_failure',
}
}

View File

@ -0,0 +1,15 @@
pkg/
Gemfile.lock
vendor/
spec/fixtures/
.vagrant/
.bundle/
coverage/
.idea/
*.swp
*.iml
/openstack/
# Files created by releasenotes build
releasenotes/build
.tox

View File

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

View File

@ -0,0 +1,78 @@
## 8.0.0 and beyond
From 8.0.0 release and beyond, release notes are published on
[docs.openstack.org](http://docs.openstack.org/releasenotes/puppet-openstacklib/).
##2015-11-24 - 7.0.0
###Summary
This is a backwards-compatible major release for OpenStack Liberty.
####Features
- fallback to default rcfile
- prepare $::os_package_type
- add a proxy inifile provider
- allow the use of an ensure_absent_val param
- create is_service_default function
- create os_service_default fact
- allow to path custom fragment to vhost
- pass necessary options to Apache when using WSGI
####Bugfixes
- fix fact for puppet facter 2.0.1+
####Maintenance
- initial msync run for all Puppet OpenStack modules
- enable acceptance tests for openstack_config
- remove class_parameter_defaults puppet-lint check
##2015-10-10 - 6.1.0
###Summary
This is a maintenance release in the Kilo series.
####Maintenance
- acceptance: checkout stable/kilo puppet modules
##2015-07-08 - 6.0.0
###Summary
This is a backwards-incompatible major release for OpenStack Kilo.
####Backwards-incompatible changes
- MySQL: change default MySQL collate to utf8_general_ci
####Features
- Puppet 4.x support
- Add db::postgresql to openstacklib
- Implement openstacklib::wsgi::apache
- Move openstackclient parent provider to openstacklib
- Keystone V3 API support
- Restructures authentication for resource providers
####Bugfixes
- Properly handle policy values containing spaces
####Maintenance
- Bump mysql version to 3.x
- Acceptance tests with Beaker
##2015-06-17 - 5.1.0
###Summary
This is a feature and bugfix release in the Juno series.
####Features
- Adding augeas insertion check
####Bugfixes
- MySQL: change default MySQL collate to utf8_general_ci
####Maintenance
- Update .gitreview file for project rename
- spec: pin rspec-puppet to 1.0.1
##2014-11-25 - 5.0.0
###Summary
Initial release for Juno.

View File

@ -0,0 +1,21 @@
source ENV['GEM_SOURCE'] || "https://rubygems.org"
group :development, :test, :system_tests do
gem 'puppet-openstack_spec_helper',
:git => 'https://git.openstack.org/openstack/puppet-openstack_spec_helper',
:require => false
end
if facterversion = ENV['FACTER_GEM_VERSION']
gem 'facter', facterversion, :require => false
else
gem 'facter', :require => false
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,13 @@
Copyright 2012 OpenStack Foundation
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.

View File

@ -0,0 +1,333 @@
Team and repository tags
========================
[![Team and repository tags](http://governance.openstack.org/badges/puppet-openstacklib.svg)](http://governance.openstack.org/reference/tags/index.html)
<!-- Change things from this point on -->
openstacklib
============
#### Table of Contents
1. [Overview - What is the openstacklib module?](#overview)
2. [Module Description - What does the module do?](#module-description)
3. [Setup - The basics of getting started with openstacklib](#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 openstacklib 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 not part
of the core software. The module itself is used to expose common functionality
between Openstack modules as a library that can be utilized to avoid code
duplication.
Module Description
------------------
The openstacklib module is a library module for other Openstack modules to
utilize. A thorough description will be added later.
This module is tested in combination with other modules needed to build and
leverage an entire Openstack software stack.
Setup
-----
### Installing openstacklib
puppet module install openstack/openstacklib
Usage
-----
### Classes and Defined Types
#### Defined type: openstacklib::db::mysql
The db::mysql resource is a library resource that can be used by nova, cinder,
ceilometer, etc., to create a mysql database with configurable privileges for
a user connecting from defined hosts.
Typically this resource will be declared with a notify parameter to configure
the sync command to execute when the database resource is changed.
For example, in heat::db::mysql you might declare:
```
::openstacklib::db::mysql { 'heat':
password_hash => mysql_password($password),
dbname => $dbname,
user => $user,
host => $host,
charset => $charset,
collate => $collate,
allowed_hosts => $allowed_hosts,
notify => Exec['heat-dbsync'],
}
```
Some modules should ensure that the database is created before the service is
set up. For example, in keystone::db::mysql you would have:
```
::openstacklib::db::mysql { 'keystone':
password_hash => mysql_password($password),
dbname => $dbname,
user => $user,
host => $host,
charset => $charset,
collate => $collate,
allowed_hosts => $allowed_hosts,
notify => Exec['keystone-manage db_sync'],
before => Service['keystone'],
}
```
** Parameters for openstacklib::db::mysql: **
#####`password_hash`
Password hash to use for the database user for this service;
string; required
#####`dbname`
The name of the database
string; optional; default to the $title of the resource, i.e. 'nova'
#####`user`
The database user to create;
string; optional; default to the $title of the resource, i.e. 'nova'
#####`host`
The IP address or hostname of the user in mysql_grant;
string; optional; default to '127.0.0.1'
#####`charset`
The charset to use for the database;
string; optional; default to 'utf8'
#####`collate`
The collate to use for the database;
string; optional; default to 'utf8_general_ci'
#####`allowed_hosts`
Additional hosts that are allowed to access this database;
array or string; optional; default to undef
#####`privileges`
Privileges given to the database user;
string or array of strings; optional; default to 'ALL'
#### Defined type: openstacklib::db::postgresql
The db::postgresql resource is a library resource that can be used by nova,
cinder, ceilometer, etc., to create a postgresql database and a user with
configurable privileges.
Typically this resource will be declared with a notify parameter to configure
the sync command to execute when the database resource is changed.
For example, in heat::db::postgresql you might declare:
```
::openstacklib::db::postgresql { $dbname:
password_hash => postgresql_password($user, $password),
dbname => $dbname,
user => $user,
notify => Exec['heat-dbsync'],
}
```
Some modules should ensure that the database is created before the service is
set up. For example, in keystone::db::postgresql you would have:
```
::openstacklib::db::postgresql { $dbname:
password_hash => postgresql_password($user, $password),
dbname => $dbname,
user => $user,
notify => Exec['keystone-manage db_sync'],
before => Service['keystone'],
}
```
** Parameters for openstacklib::db::postgresql: **
#####`password_hash`
Password hash to use for the database user for this service;
string; required
#####`dbname`
The name of the database
string; optional; default to the $title of the resource, i.e. 'nova'
#####`user`
The database user to create;
string; optional; default to the $title of the resource, i.e. 'nova'
#####`encoding`
The encoding use for the database;
string; optional; default to undef
#####`privileges`
Privileges given to the database user;
string or array of strings; optional; default to 'ALL'
#### Defined type: openstacklib::service_validation
The service_validation resource is a library resource that can be used by nova, cinder,
ceilometer, etc., to validate that a resource is actually up and running.
For example, in nova::api you might declare:
```
::openstacklib::service_validation { 'nova-api':
command => 'nova list',
}
```
This defined resource creates an exec-anchor pair where the anchor depends upon
the successful exec run.
** Parameters for openstacklib::service_validation: **
#####`command`
Command to run for validating the service;
string; required
#####`service_name`
The name of the service to validate;
string; optional; default to the $title of the resource, i.e. 'nova-api'
#####`path`
The path of the command to validate the service;
string; optional; default to '/usr/bin:/bin:/usr/sbin:/sbin'
#####`provider`
The provider to use for the exec command;
string; optional; default to 'shell'
#####`tries`
Number of times to retry validation;
string; optional; default to '10'
#####`try_sleep`
Number of seconds between validation attempts;
string; optional; default to '2'
#### Defined provider for openstack_config: ini_setting
It provides an interface to any INI configuration file as they are
used in Openstack modules.
You use it like this:
```
Puppet::Type.type(:<module>_config).provide(
:openstackconfig,
:parent => Puppet::Type.type(:openstack_config).provider(:ini_setting)
) do
```
It has the standard features of the upstream puppetlabs' `inifile`
module as it's a direct children of it. Furthermore it can transform
a value with some function of you're choice, enabling you to get value
that get filled at run-time like an `uuid`.
For an example of how that's working you can have a look at this
[review](https://review.openstack.org/#/c/347468/)
#### Defined provider for openstack_config: ruby
This one has the same basic features as the ini_setting one but the
ability to transformation the value. It offers another feature,
though. It can parse array. What it enables one to do is to parse
this correctly:
```
[DEFAULT]
conf1 = value1
conf1 = value2
```
On the opposite side if you put that:
```
module_config { 'DEFAULT/conf1' : value => ['value1', 'value2'] }
```
in your manifest, it will properly be written as the example above.
To use this provider you use this:
```
Puppet::Type.type(:<module>_config).provide(
:openstackconfig,
:parent => Puppet::Type.type(:openstack_config).provider(:ruby)
) do
```
and define you type with ```:array_matching => :all```. An example of
such provider is ```nova_config```. Have a look for inspiration.
Implementation
--------------
### openstacklib
openstacklib is a combination of Puppet manifest and ruby code to delivery
configuration and extra functionality through types and providers.
Limitations
-----------
The python-migrate system package for RHEL 6 and below is out of date and may
fail to correctly migrate postgresql databases. While this module does not
handle database migrations, it is common to set up refresh relationships
between openstacklib::db::postgresql resource and the database sync exec
resource. Relying on this behavior may cause errors.
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.
* http://docs.openstack.org/developer/puppet-openstack-guide/
Contributors
------------
* https://github.com/stackforge/puppet-openstacklib/graphs/contributors
Versioning
----------
This module has been given version 5 to track the puppet-openstack modules. The
versioning for the puppet-openstack modules are as follows:
```
Puppet Module :: OpenStack Version :: OpenStack Codename
2.0.0 -> 2013.1.0 -> Grizzly
3.0.0 -> 2013.2.0 -> Havana
4.0.0 -> 2014.1.0 -> Icehouse
5.0.0 -> 2014.2.0 -> Juno
6.0.0 -> 2015.1.0 -> Kilo
7.0.0 -> 2015.2.0 -> Liberty
```

View File

@ -0,0 +1 @@
require 'puppet-openstack_spec_helper/rake_tasks'

View File

@ -0,0 +1,11 @@
Exec { logoutput => 'on_failure' }
include ::openstacklib::defaults
if $::osfamily == 'RedHat' {
# Virtual package name, present in @base.
package { 'perl(Net::HTTP)':
ensure => present,
tag => 'openstack',
}
}

View File

@ -0,0 +1 @@
os_service_default=<SERVICE DEFAULT>

View File

@ -0,0 +1,38 @@
#
# We need this to be able to make decision of what style of package we are
# working with: Debian style package (for example, it uses a nova-consoleproxy
# package and not nova-novncproxy, or it has a openstack-dashboard-apache,
# etc.), or just the Ubuntu style package.
#
# This is needed, because in some cases, we are using the Debian style packages
# but running under Ubuntu. For example, that's the case when running with MOS
# over Ubuntu. For this case, a manual override is provided, in the form of a
# /etc/facter/facts.d/os_package_type.txt containing:
# os_package_type=debian
#
# In all other cases, we can consider that we're using vanilia (ie: unmodified)
# distribution packages, and we can set $::os_package_type depending on the
# value of $::operatingsystem.
#
# Having the below snipets helps simplifying checks within individual project
# manifests, so that we can just reuse $::os_package_type directly without
# having to also check if it contains a value, then check for the content of
# $::operatingsystem (ie: what's below factors the check once and for all).
Facter.add('os_package_type') do
setcode do
case Facter.value(:osfamily)
when 'Debian'
if Facter.value(:operatingsystem) == 'Debian' then
'debian'
else
'ubuntu'
end
when 'RedHat'
'rpm'
when 'Solaris'
'solaris'
else
'unknown'
end
end
end

View File

@ -0,0 +1,14 @@
#
# This adds the os_service_default fact for people with facter < 2.0.1
# For people with facter >= 2.0.1, the facts.d/os_service_default.txt should
# provide this information
#
require 'puppet/util/package'
if Puppet::Util::Package.versioncmp(Facter.value(:facterversion), '2.0.1') < 0
Facter.add('os_service_default') do
setcode do
'<SERVICE DEFAULT>'
end
end
end

View File

@ -0,0 +1,50 @@
#
# We've found that using $::processorcount for workers/threads can lead to
# unexpected memory or process counts for people deploying on baremetal or
# if they have large number of cpus. This fact allows us to tweak the formula
# used to determine number of workers in a single place but use it across all
# modules.
#
# The value for os_workers is max between '(<# processors> / 4)' and '2' with
# a cap of 8.
#
# This fact can be overloaded by an external fact from /etc/factor/facts.d if
# a user would like to provide their own default value.
#
Facter.add(:os_workers_small) do
has_weight 100
setcode do
processors = Facter.value('processorcount')
[ [ (processors.to_i / 4), 2 ].max, 8 ].min
end
end
#
# The value above for os_workers performs 3x worse in many cases compared to
# the prevuous default of $::processorcount.
#
# Based on performance data [1], the following calculation is within 1-2%.
#
# The value for os_workers is max between '(<# processors> / 2)' and '2' with
# a cap of 12.
#
# [1] http://elk.browbeatproject.org:80/goto/a23307fd511e314b975dedca6f65425d
#
Facter.add(:os_workers) do
has_weight 100
setcode do
processors = Facter.value('processorcount')
[ [ (processors.to_i / 2), 2 ].max, 12 ].min
end
end
#
# For cases where services are not co-located together (ie monolithic).
#
Facter.add(:os_workers_large) do
has_weight 100
setcode do
processors = Facter.value('processorcount')
[ (processors.to_i / 2) ]
end
end

View File

@ -0,0 +1,27 @@
#
# is_service_default.rb
#
# This function can be used to check if a variable is set to the default value
# of '<SERVICE DEFAULT>'
#
# For reference:
# http://lists.openstack.org/pipermail/openstack-dev/2015-July/069823.html
# https://github.com/openstack/puppet-openstacklib/commit/3b85306d042292713d0fd89fa508e0a0fbf99671
#
module Puppet::Parser::Functions
newfunction(:is_service_default, :type => :rvalue, :doc => <<-EOS
Returns true if the variable passed to this function is '<SERVICE DEFAULT>'
EOS
) do |arguments|
raise(Puppet::ParseError, "is_service_default(): Wrong number of arguments" +
"given (#{arguments.size} for 1)") if arguments.size != 1
value = arguments[0]
unless value.is_a?(String)
return false
end
return (value == '<SERVICE DEFAULT>')
end
end

View File

@ -0,0 +1,37 @@
require 'ipaddr'
module Puppet::Parser::Functions
newfunction(:normalize_ip_for_uri,
:type => :rvalue,
:doc => <<-EOD
Add brackets if the argument is an IPv6 address.
Returns the argument untouched otherwise.
CAUTION: this code "fails" when the user is passing
an IPv6 address with the port in it without the
brackets: 2001::1:8080, to specify address 2001::1
and port 8080. This code will change it to
[2001::1:8080] as it's a valid ip address. This
shouldn't be an issue in most cases.
If an array is given, each member will be normalized to
a valid IPv6 address with brackets when needed.
EOD
) do |args|
result = []
args = args[0] if args[0].kind_of?(Array)
args.each do |ip|
begin
if IPAddr.new(ip).ipv6?
unless ip.match(/\[.+\]/)
Puppet.debug("IP #{ip} is changed to [#{ip}]")
ip = "[#{ip}]"
end
end
rescue ArgumentError
# ignore it
end
result << ip
end
return result[0] if args.size == 1
result
end
end

View File

@ -0,0 +1,81 @@
require 'puppet/parser/functions'
Puppet::Parser::Functions.newfunction(:os_database_connection,
:type => :rvalue,
:doc => <<-EOS
This function builds a os_database_connection string from various parameters.
EOS
) do |arguments|
require 'uri'
if (arguments.size != 1) then
raise(Puppet::ParseError, "os_database_connection(): Wrong number of arguments " +
"given (#{arguments.size} for 1)")
end
v = arguments[0]
klass = v.class
unless klass == Hash
raise(Puppet::ParseError, "os_database_connection(): Requires an hash, got #{klass}")
end
v.keys.each do |key|
klass = (key == 'extra') ? Hash : String
unless (v[key].class == klass) or (v[key] == :undef)
raise(Puppet::ParseError, "os_database_connection(): #{key} should be a #{klass}")
end
end
parts = {}
unless v.include?('dialect')
raise(Puppet::ParseError, 'os_database_connection(): dialect is required')
end
if v.include?('host')
parts[:host] = v['host']
end
unless v.include?('database')
raise(Puppet::ParseError, 'os_database_connection(): database is required')
end
if v.include?('port')
if v.include?('host')
parts[:port] = v['port'].to_i
else
raise(Puppet::ParseError, 'os_database_connection(): host is required with port')
end
end
if v.include?('username') and (v['username'] != :undef) and (v['username'].to_s != '')
parts[:userinfo] = URI.escape(v['username'])
if v.include?('password') and (v['password'] != :undef) and (v['password'].to_s != '')
parts[:userinfo] += ":#{URI.escape(v['password'])}"
end
end
# support previous charset option on the function. Setting charset will
# override charset if passed in via the extra parameters
if v.include?('charset')
if v.include?('extra')
v['extra'].merge!({ 'charset' => v['charset'] })
else
v['extra'] = { 'charset' => v['charset'] }
end
end
parts[:query] = v['extra'].map{ |k,v| "#{k}=#{v}" }.join('&') if v.include?('extra')
parts[:scheme] = v['dialect']
if v.include?('host')
parts[:path] = "/#{v['database']}"
else
parts[:path] = "///#{v['database']}"
end
URI::Generic.build(parts).to_s
end

View File

@ -0,0 +1,157 @@
require 'puppet/parser/functions'
Puppet::Parser::Functions.newfunction(:os_transport_url,
:type => :rvalue,
:arity => 1,
:doc => <<-EOS
This function builds a os_transport_url string from a hash of parameters.
Valid hash parameteres:
* transport - (string) type of transport, 'rabbit' or 'amqp'
* host - (string) single host
* hosts - (array) array of hosts to use
* port - (string | integer) port to connect to
* username - (string) connection username
* password - (string) connection password
* virtual_host - (string) virtual host to connect to
* ssl - (string) is the connection ssl or not ('1' or '0'). overrides the ssl
key in the query parameter
* query - (hash) hash of key,value pairs used to create a query string for
the transport_url.
Only 'transport' and either 'host' or 'hosts' are required keys for the
parameters hash.
The url format that will be generated:
transport://user:pass@host:port[,userN:passN@hostN:portN]/virtual_host?query
NOTE: ipv6 addresses will automatically be bracketed for the URI using the
normalize_ip_for_uri function.
Single Host Example:
os_transport_url({
'transport' => 'rabbit',
'host' => '1.1.1.1',
'port' => '5672',
'username' => 'username',
'password' => 'password',
'virtual_host' => 'virtual_host',
'ssl' => '1',
'query' => { 'key' => 'value' },
})
Generates:
rabbit://username:password@1.1.1.1:5672/virtual_host?key=value&ssl=1
Multiple Hosts Example:
os_transport_url({
'transport' => 'rabbit',
'hosts' => [ '1.1.1.1', '2.2.2.2' ],
'port' => '5672',
'username' => 'username',
'password' => 'password',
'virtual_host' => 'virtual_host',
'query' => { 'key' => 'value' },
})
Generates:
rabbit://username:password@1.1.1.1:5672,username:password@2.2.2.2:5672/virtual_host?key=value
EOS
) do |arguments|
require 'uri'
v = arguments[0]
klass = v.class
unless klass == Hash
raise(Puppet::ParseError, "os_transport_url(): Requires an hash, got #{klass}")
end
# type checking for the parameter hash
v.keys.each do |key|
v[key] = v[key].to_s if key == 'port'
klass = (key == 'hosts') ? Array : String
klass = (key == 'query') ? Hash : klass
unless (v[key].class == klass) or (v[key] == :undef)
raise(Puppet::ParseError, "os_transport_url(): #{key} should be a #{klass}")
end
end
# defaults
parts = {
:transport => 'rabbit',
:hostinfo => 'localhost',
:path => '/',
}
unless v.include?('transport')
raise(Puppet::ParseError, 'os_transport_url(): transport is required')
end
unless v.include?('host') or v.include?('hosts')
raise(Puppet::ParseError, 'os_transport_url(): host or hosts is required')
end
if v.include?('host') and v.include?('hosts')
raise(Puppet::ParseError, 'os_transport_url(): cannot use both host and hosts.')
end
parts[:transport] = v['transport']
if v.include?('username') and (v['username'] != :undef) and (v['username'].to_s != '')
parts[:userinfo] = URI.escape(v['username'])
if v.include?('password') and (v['password'] != :undef) and (v['password'].to_s != '')
parts[:userinfo] += ":#{URI.escape(v['password'])}"
end
end
if v.include?('host')
host = function_normalize_ip_for_uri([v['host']])
host += ":#{v['port'].to_s}" if v.include?('port')
if parts.include?(:userinfo)
parts[:hostinfo] = "#{parts[:userinfo]}@#{host}"
else
parts[:hostinfo] = "#{host}"
end
end
if v.include?('hosts')
hosts = function_normalize_ip_for_uri([v['hosts']])
# normalize_ip_for_uri may return a string, so check that we still have an
# array
hosts = [hosts] if hosts.kind_of?(String)
hosts = hosts.map{ |h| "#{h}:#{v['port'].to_s}" } if v.include?('port')
if parts.include?(:userinfo)
parts[:hostinfo] = hosts.map { |h| "#{parts[:userinfo]}@#{h}" }.join(',')
else
parts[:hostinfo] = hosts.join(',')
end
end
parts[:path] = "/#{v['virtual_host']}" if v.include?('virtual_host')
# support previous ssl option on the function. Setting ssl will
# override ssl if passed in via the query parameters
if v.include?('ssl')
# ssl can be passed in as a query paramter but should be 0/1. See
# http://docs.celeryproject.org/projects/kombu/en/latest/userguide/connections.html#urls
# so we rely on the stdlib str2bool and bool2num to ensure it's in the
# format
ssl_val = function_bool2num([function_str2bool([v['ssl']])])
if v.include?('query')
v['query'].merge!({ 'ssl' => ssl_val })
else
v['query'] = { 'ssl' => ssl_val }
end
end
parts[:query] = v['query'].map{ |k,val| "#{k}=#{val}" }.join('&') if v.include?('query')
url_parts = []
url_parts << parts[:transport]
url_parts << '://'
url_parts << parts[:hostinfo]
url_parts << parts[:path]
url_parts << '?' << parts[:query] if parts.include?(:query)
url_parts.join()
end

View File

@ -0,0 +1,153 @@
require 'csv'
require 'puppet'
require 'timeout'
class Puppet::Error::OpenstackAuthInputError < Puppet::Error
end
class Puppet::Error::OpenstackUnauthorizedError < Puppet::Error
end
class Puppet::Provider::Openstack < Puppet::Provider
initvars # so commands will work
commands :openstack_command => 'openstack'
@@no_retry_actions = %w(create remove delete)
@@command_timeout = 40
# Fails on the 5th retry for a max of 212s (~3.5min) before total
# failure.
@@request_timeout = 170
@@retry_sleep = 3
class << self
[:no_retry_actions, :request_timeout, :retry_sleep].each do |m|
define_method m do
self.class_variable_get("@@#{m}")
end
define_method :"#{m}=" do |value|
self.class_variable_set("@@#{m}", value)
end
end
end
# timeout the openstack command
# after this number of seconds
# retry the command until the request_timeout,
# unless it's a no_retry_actions call
def self.command_timeout(action=nil)
# give no_retry actions the full time limit to finish
return self.request_timeout() if no_retry_actions.include? action
self.class_variable_get("@@command_timeout")
end
# with command_timeout
def self.openstack(*args)
begin
action = args[1]
Timeout.timeout(command_timeout(action)) do
openstack_command *args
end
rescue Timeout::Error
raise Puppet::ExecutionFailure, "Command: 'openstack #{args.inspect}' has been running for more than #{command_timeout(action)} seconds"
end
end
# get the current timestamp
def self.current_time
Time.now.to_i
end
def self.request_without_retry(&block)
previous_timeout = self.request_timeout
rc = nil
if block_given?
self.request_timeout = 0
rc = yield
end
ensure
self.request_timeout = previous_timeout
rc
end
# Returns an array of hashes, where the keys are the downcased CSV headers
# with underscores instead of spaces
#
# @param options [Hash] Other options
# @options :no_retry_exception_msgs [Array<Regexp>,Regexp] exception without retries
def self.request(service, action, properties, credentials=nil, options={})
env = credentials ? credentials.to_env : {}
no_retry = options[:no_retry_exception_msgs]
Puppet::Util.withenv(env) do
rv = nil
end_time = current_time + request_timeout
start_time = current_time
retry_count = 0
loop do
begin
if action == 'list'
# shell output is:
# ID,Name,Description,Enabled
response = openstack(service, action, '--quiet', '--format', 'csv', properties)
response = parse_csv(response)
keys = response.delete_at(0)
rv = response.collect do |line|
hash = {}
keys.each_index do |index|
key = keys[index].downcase.gsub(/ /, '_').to_sym
hash[key] = line[index]
end
hash
end
elsif action == 'show' or action == 'create'
rv = {}
# shell output is:
# name="value1"
# id="value2"
# description="value3"
openstack(service, action, '--format', 'shell', properties).split("\n").each do |line|
# key is everything before the first "="
key, val = line.split('=', 2)
next unless val # Ignore warnings
# value is everything after the first "=", with leading and trailing double quotes stripped
val = val.gsub(/\A"|"\Z/, '')
rv[key.downcase.to_sym] = val
end
else
rv = openstack(service, action, properties)
end
break
rescue Puppet::ExecutionFailure => exception
raise Puppet::Error::OpenstackUnauthorizedError, 'Could not authenticate' if exception.message =~ /HTTP 40[13]/
if current_time > end_time
error_message = exception.message
error_message += " (tried #{retry_count}, for a total of #{end_time - start_time } seconds)"
raise(Puppet::ExecutionFailure, error_message)
end
raise exception if no_retry_actions.include? action
if no_retry
no_retry = [no_retry] unless no_retry.is_a?(Array)
no_retry.each do |nr|
raise exception if exception.message.match(nr)
end
end
debug "Non-fatal error: '#{exception.message}'. Retrying for #{end_time - current_time} more seconds"
sleep retry_sleep
retry_count += 1
retry
end
end
return rv
end
end
private
def self.parse_csv(text)
# Ignore warnings - assume legitimate output starts with a double quoted
# string. Errors will be caught and raised prior to this
text = text.split("\n").drop_while { |line| line !~ /^\".*\"/ }.join("\n")
return CSV.parse(text + "\n")
end
end

View File

@ -0,0 +1,51 @@
#require 'puppet/provider/openstack/credentials'
require File.join(File.dirname(__FILE__), '..','..','..', 'puppet/provider/openstack/credentials')
module Puppet::Provider::Openstack::Auth
RCFILENAME = "#{ENV['HOME']}/openrc"
def get_os_vars_from_env
env = {}
ENV.each { |k,v| env.merge!(k => v) if k =~ /^OS_/ }
return env
end
def get_os_vars_from_rcfile(filename)
env = {}
rcfile = [filename, '/root/openrc'].detect { |f| File.exists? f }
unless rcfile.nil?
File.open(rcfile).readlines.delete_if{|l| l=~ /^#|^$/ }.each do |line|
key, value = line.split('=')
key = key.split(' ').last
value = value.chomp.gsub(/'/, '')
env.merge!(key => value) if key =~ /OS_/
end
end
return env
end
def rc_filename
RCFILENAME
end
def request(service, action, properties=nil, options={})
properties ||= []
set_credentials(@credentials, get_os_vars_from_env)
unless @credentials.set?
@credentials.unset
set_credentials(@credentials, get_os_vars_from_rcfile(rc_filename))
end
unless @credentials.set?
raise(Puppet::Error::OpenstackAuthInputError, 'Insufficient credentials to authenticate')
end
super(service, action, properties, @credentials, options)
end
def set_credentials(creds, env)
env.each do |key, val|
var = key.sub(/^OS_/,'').downcase
creds.set(var, val)
end
end
end

View File

@ -0,0 +1,95 @@
require 'puppet'
require 'puppet/provider/openstack'
class Puppet::Provider::Openstack::Credentials
KEYS = [
:auth_url, :password, :project_name, :username,
:token, :url,
:identity_api_version,
:region_name,
:interface
]
KEYS.each { |var| attr_accessor var }
def self.defined?(name)
KEYS.include?(name.to_sym)
end
def set(key, val)
if self.class.defined?(key.to_sym)
self.instance_variable_set("@#{key}".to_sym, val)
end
end
def set?
return true if user_password_set? || service_token_set?
end
def service_token_set?
return true if @token && @url
end
def to_env
env = {}
self.instance_variables.each do |var|
name = var.to_s.sub(/^@/,'OS_').upcase
env.merge!(name => self.instance_variable_get(var))
end
env
end
def user_password_set?
return true if @username && @password && @project_name && @auth_url
end
def unset
KEYS.each do |key|
if key != :identity_api_version &&
self.instance_variable_defined?("@#{key}")
set(key, '')
end
end
end
def version
self.class.to_s.sub(/.*V/,'').sub('_','.')
end
end
class Puppet::Provider::Openstack::CredentialsV2_0 < Puppet::Provider::Openstack::Credentials
end
class Puppet::Provider::Openstack::CredentialsV3 < Puppet::Provider::Openstack::Credentials
KEYS = [
:cacert,
:cert,
:default_domain,
:domain_id,
:domain_name,
:key,
:project_domain_id,
:project_domain_name,
:project_id,
:trust_id,
:user_domain_id,
:user_domain_name,
:user_id
]
KEYS.each { |var| attr_accessor var }
def self.defined?(name)
KEYS.include?(name.to_sym) || super
end
def user_password_set?
return true if (@username || @user_id) && @password && (@project_name || @project_id) && @auth_url
end
def initialize
set(:identity_api_version, version)
end
end

View File

@ -0,0 +1,73 @@
Puppet::Type.type(:openstack_config).provide(
:ini_setting,
:parent => Puppet::Type.type(:ini_setting).provider(:ruby)
) do
def exists?
if resource[:value] == ensure_absent_val
resource[:ensure] = :absent
end
super
end
def create
resource[:value] = transform(:to, resource[:value])
super
end
def section
resource[:name].split('/', 2).first
end
def setting
resource[:name].split('/', 2).last
end
def value=(value)
new_value = transform(:to, value)
ini_file.set_value(section, setting, new_value)
ini_file.save
end
def value
value = ini_file.get_value(section, setting)
new_value = transform(:from, value)
@property_hash[:value] = new_value
new_value
end
def ensure_absent_val
resource[:ensure_absent_val]
end
def transform_to
return nil unless resource.to_hash.has_key? :transform_to
resource[:transform_to]
end
def transform_to=(value)
@property_hash[:transform_to] = value
end
def separator
'='
end
def file_path
self.class.file_path
end
def transform(direction, value)
new_value = value
if !transform_to.nil? && !transform_to.empty?
transformation_function = "#{direction}_#{transform_to}".to_sym
if self.respond_to?(transformation_function)
new_value = send(transformation_function, value)
else
error("Cannot find transformation #{transformation_function} for #{value}")
end
end
new_value
end
end

View File

@ -0,0 +1,94 @@
require File.expand_path('../../../util/openstackconfig', __FILE__)
Puppet::Type.type(:openstack_config).provide(:ruby) do
def self.instances
if self.respond_to?(:file_path)
config = Puppet::Util::OpenStackConfig.new(file_path)
resources = []
config.section_names.each do |section_name|
config.get_settings(section_name).each do |setting, value|
resources.push(
new(
:name => namevar(section_name, setting),
:value => value,
:ensure => :present
)
)
end
end
resources
else
raise(Puppet::Error,
'OpenStackConfig only support collecting instances when a file path ' +
'is hard coded'
)
end
end
def self.namevar(section_name, setting)
"#{section_name}/#{setting}"
end
def exists?
if resource[:value] == ensure_absent_val
resource[:ensure] = :absent
end
!config.get_value(section, setting).nil?
end
def create
config.set_value(section, setting, resource[:value])
config.save
@config = nil
end
def destroy
config.remove_setting(section, setting)
config.save
@config = nil
end
def value=(value)
config.set_value(section, setting, resource[:value])
config.save
end
def value
val = config.get_value(section, setting)
if !val.kind_of?(Array)
[val]
else
val
end
end
def section
resource[:name].split('/', 2).first
end
def setting
resource[:name].split('/', 2).last
end
def ensure_absent_val
# :array_matching => :all values comes in form of array even when they
# are passed as single string
if resource[:value].kind_of?(Array) and not resource[:ensure_absent_val].kind_of?(Array)
[resource[:ensure_absent_val]]
else
resource[:ensure_absent_val]
end
end
def file_path
self.class.file_path
end
private
def config
@config ||= Puppet::Util::OpenStackConfig.new(file_path)
end
end

View File

@ -0,0 +1,91 @@
Puppet::Type.type(:policy_rcd).provide(:policy_rcd) do
desc 'Provider for managing policy-rc.d for Ubuntu'
mk_resource_methods
def check_os
Facter.value(:osfamily) == 'Debian'
end
def check_policy_rcd
return File.exist? policy_rcd
end
def file_lines
@file_lines ||= File.open(policy_rcd).readlines
end
def policy_rcd
'/usr/sbin/policy-rc.d'
end
def service
@resource[:service]
end
def set_code
@resource[:set_code]
end
def self.write_to_file(file, content, truncate=false)
File.truncate(file, 0) if truncate
policy = File.open(file, 'a+')
policy.puts(content)
policy.close
File.chmod(0744, file)
end
def exists?
# we won't do anything if os family is not debian
return true unless check_os
if check_policy_rcd
file_lines.each do |line|
unless line =~ /"#{@resource[:service]}"/
next
end
return true
end
end
false
end
def create
unless check_policy_rcd
header = "#!/bin/bash\n# THIS FILE MANAGED BY PUPPET\n"
else
header = ""
end
content = "#{header}[[ \"$1\" == \"#{@resource[:service]}\" ]] && exit #{@resource[:set_code]}\n"
self.class.write_to_file(policy_rcd, content)
end
def destroy
if check_policy_rcd
file_lines.delete_if { |l| l =~ /"#{@resource[:service]}"/ }
self.class.write_to_file(policy_rcd, file_lines, true)
end
end
def flush
if @resource[:ensure] == :present and ! file_lines.nil?
new_line = nil
outdated_line = nil
file_lines.each do |line|
unless line =~ /"#{@resource[:service]}"/
next
end
code = line.match(/exit\s(\d+)/)[1]
if code != @resource[:set_code]
new_line = "[[ \"$1\" == \"#{@resource[:service]}\" ]] && exit #{@resource[:set_code]}\n"
outdated_line = line
end
end
unless new_line.nil?
file_lines.delete(outdated_line)
file_lines.push(new_line)
self.class.write_to_file(policy_rcd, file_lines, true)
end
end
end
end

View File

@ -0,0 +1,3 @@
Puppet::Type.newtype(:openstack_config) do
end

View File

@ -0,0 +1,21 @@
Puppet::Type.newtype(:policy_rcd) do
ensurable
newparam(:name, :namevar => true) do
newvalues(/\S+/)
end
newproperty(:service) do
defaultto { @resource[:name] }
newvalues(/\S+/)
end
newproperty(:set_code) do
defaultto('101')
validate do |value|
# validate codes according to https://people.debian.org/~hmh/invokerc.d-policyrc.d-specification.txt
allowed_codes = [ '0', '1', '100', '101', '102', '103', '104', '105', '106' ]
raise ArgumentError, 'Unknown exit status code is set' unless allowed_codes.include?(value)
end
end
end

View File

@ -0,0 +1,117 @@
#
# Author: Martin Magr <mmagr@redhat.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.
#
# Forked from https://github.com/puppetlabs/puppetlabs-inifile .
require File.expand_path('../openstackconfig/section', __FILE__)
module Puppet
module Util
class OpenStackConfig
@@SECTION_REGEX = /^\s*\[(.*)\]\s*$/
def initialize(path)
@path = path
@order = []
@sections = {}
parse_file
end
attr_reader :path
def section_names
@sections.keys
end
def get_settings(section_name)
@sections[section_name].settings
end
def get_value(section_name, setting_name)
if @sections.has_key?(section_name)
@sections[section_name].settings[setting_name]
end
end
def set_value(section_name, setting_name, value)
unless @sections.has_key?(section_name)
add_section(section_name)
end
if @sections[section_name].settings.has_key?(setting_name)
@sections[section_name].update_setting(setting_name, value)
else
@sections[section_name].add_setting(setting_name, value)
end
end
def remove_setting(section_name, setting_name, value=nil)
@sections[section_name].remove_setting(setting_name, value)
end
def save
File.open(@path, 'w') do |fh|
@order.each do |section_name|
if section_name.length > 0
fh.puts("[#{section_name}]")
end
unless @sections[section_name].lines.empty?
@sections[section_name].lines.each do |line|
fh.puts(line)
end
end
end
end
end
private
# This is mostly here because it makes testing easier
# --we don't have to try to stub any methods on File.
def self.readlines(path)
# If this type is ever used with very large files, we should
# write this in a different way, using a temp
# file; for now assuming that this type is only used on
# small-ish config files that can fit into memory without
# too much trouble.
File.file?(path) ? File.readlines(path) : []
end
def parse_file
# We always create a "global" section at the beginning of the file,
# for anything that appears before the first named section.
lines = []
current_section = ''
OpenStackConfig.readlines(@path).each do |line|
if match = @@SECTION_REGEX.match(line)
add_section(current_section, lines)
# start new section parsing
lines = []
current_section = match[1]
else
lines.push(line)
end
end
add_section(current_section, lines)
end
def add_section(section_name, lines=nil)
@order.push(section_name)
@sections[section_name] = Section.new(section_name, lines)
end
end
end
end

View File

@ -0,0 +1,178 @@
#
# Author: Martin Magr <mmagr@redhat.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.
#
# Forked from https://github.com/puppetlabs/puppetlabs-inifile .
module Puppet
module Util
class OpenStackConfig
class Section
@@SETTING_REGEX = /^(\s*)([^#;\s]|[^#;\s].*?[^\s=])(\s*=[ \t]*)(.*)\s*$/
@@COMMENTED_SETTING_REGEX = /^(\s*)[#;]+(\s*)(.*?[^\s=])(\s*=[ \t]*)(.*)\s*$/
def initialize(name, lines=nil)
@name = name
@lines = lines.nil? ? [] : lines
# parse lines
@indentation = nil
@settings = {}
@lines.each do |line|
if match = @@SETTING_REGEX.match(line)
indent = match[1].length
@indentation = [indent, @indentation || indent].min
if @settings.include?(match[2])
if not @settings[match[2]].kind_of?(Array)
@settings[match[2]] = [@settings[match[2]]]
end
@settings[match[2]].push(match[4])
else
@settings[match[2]] = match[4]
end
end
end
end
attr_reader :name, :indentation
def settings
Marshal.load(Marshal.dump(@settings))
end
def lines
@lines.clone
end
def is_global?
@name == ''
end
def is_new_section?
@lines.empty?
end
def setting_names
@settings.keys
end
def add_setting(setting_name, value)
@settings[setting_name] = value
add_lines(setting_name, value)
end
def update_setting(setting_name, value)
old_value = @settings[setting_name]
@settings[setting_name] = value
if value.kind_of?(Array) or old_value.kind_of?(Array)
# ---- update lines for multi setting ----
old_value = old_value.kind_of?(Array) ? old_value : [old_value]
new_value = value.kind_of?(Array) ? value : [value]
if useless = old_value - new_value
remove_lines(setting_name, useless)
end
if missing = new_value - old_value
add_lines(setting_name, missing)
end
else
# ---- update lines for single setting ----
@lines.each_with_index do |line, index|
if match = @@SETTING_REGEX.match(line)
if (match[2] == setting_name)
@lines[index] = "#{match[1]}#{match[2]}#{match[3]}#{value}\n"
end
end
end
end
end
def remove_setting(setting_name, value=nil)
if value.nil? or @settings[setting_name] == value
@settings.delete(setting_name)
else
value.each do |val|
@settings[setting_name].delete(val)
end
end
remove_lines(setting_name, value)
end
private
def find_commented_setting(setting_name)
@lines.each_with_index do |line, index|
if match = @@COMMENTED_SETTING_REGEX.match(line)
if match[3] == setting_name
return index
end
end
end
nil
end
def find_last_setting(setting_name)
result = nil
@lines.each_with_index do |line, index|
if match = @@SETTING_REGEX.match(line)
if match[2] == setting_name
result = index
end
end
end
result
end
def remove_lines(setting_name, value=nil)
if value.kind_of?(Array)
val_arr = value
else
val_arr = [value]
end
val_arr.each do |val|
@lines.each_with_index do |line, index|
if (match = @@SETTING_REGEX.match(line))
if match[2] == setting_name
if val.nil? or val_arr.include?(match[4])
@lines.delete_at(index)
break
end
end
end
end
end
end
def add_lines(setting_name, value)
indent_str = ' ' * (indentation || 0)
if current = find_last_setting(setting_name)
offset = current
elsif comment = find_commented_setting(setting_name)
offset = comment + 1
else
offset = @lines.length
end
if value.kind_of?(Array)
value.each do |val|
@lines.insert(offset, "#{indent_str}#{setting_name}=#{val}\n")
offset += 1
end
else
@lines.insert(offset, "#{indent_str}#{setting_name}=#{value}\n")
end
end
end
end
end
end

View File

@ -0,0 +1,91 @@
# == Definition: openstacklib::db::mysql
#
# This resource configures a mysql database for an OpenStack service
#
# == Parameters:
#
# [*password_hash*]
# Password hash to use for the database user for this service;
# string; required
#
# [*dbname*]
# The name of the database
# string; optional; default to the $title of the resource, i.e. 'nova'
#
# [*user*]
# The database user to create;
# string; optional; default to the $title of the resource, i.e. 'nova'
#
# [*host*]
# The IP address or hostname of the user in mysql_grant;
# string; optional; default to '127.0.0.1'
#
# [*charset*]
# The charset to use for the database;
# string; optional; default to 'utf8'
#
# [*collate*]
# The collate to use for the database;
# string; optional; default to 'utf8_general_ci'
#
# [*allowed_hosts*]
# Additional hosts that are allowed to access this database;
# array or string; optional; default to undef
#
# [*privileges*]
# Privileges given to the database user;
# string or array of strings; optional; default to 'ALL'
#
# [*create_user*]
# Flag to allow for the skipping of the user as part of the database setup.
# Set to false to skip the user creation.
# Defaults to true.
#
# [*create_grant*]
# Flag to allow for the skipping of the user grants as part of the database
# setup. Set to false to skip the user creation.
# Defaults to true.
#
# [*tls_options*]
# The TLS options that the user will have
# Defaults to ['NONE']
#
define openstacklib::db::mysql (
$password_hash,
$dbname = $title,
$user = $title,
$host = '127.0.0.1',
$charset = 'utf8',
$collate = 'utf8_general_ci',
$allowed_hosts = [],
$privileges = 'ALL',
$create_user = true,
$create_grant = true,
$tls_options = ['NONE'],
) {
include ::mysql::server
include ::mysql::client
mysql_database { $dbname:
ensure => present,
charset => $charset,
collate => $collate,
require => [ Class['mysql::server'], Class['mysql::client'] ],
}
if $create_user or $create_grant {
$allowed_hosts_list = unique(concat(any2array($allowed_hosts), [$host]))
$real_allowed_hosts = prefix($allowed_hosts_list, "${dbname}_")
openstacklib::db::mysql::host_access { $real_allowed_hosts:
user => $user,
password_hash => $password_hash,
database => $dbname,
privileges => $privileges,
create_user => $create_user,
create_grant => $create_grant,
tls_options => $tls_options,
}
}
}

View File

@ -0,0 +1,63 @@
# Allow a user to access the database for the service
#
# == Namevar
# String with the form dbname_host. The host part of the string is the host
# to allow
#
# == Parameters
# [*user*]
# username to allow
#
# [*password_hash*]
# user password hash
#
# [*database*]
# the database name
#
# [*privileges*]
# the privileges to grant to this user
#
# [*create_user*]
# Flag to allow for the skipping of the user as part of the database setup.
# Set to false to skip the user creation.
# Defaults to true.
#
# [*create_grant*]
# Flag to allow for the skipping of the user grants as part of the database
# setup. Set to false to skip the user creation.
# Defaults to true.
#
# [*tls_options*]
# The TLS options that the user will have
# Defaults to ['NONE']
#
define openstacklib::db::mysql::host_access (
$user,
$password_hash,
$database,
$privileges,
$create_user = true,
$create_grant = true,
$tls_options = ['NONE'],
) {
validate_re($title, '_', 'Title must be $dbname_$host')
$host = inline_template('<%= @title.split("_").last.downcase %>')
if $create_user {
mysql_user { "${user}@${host}":
password_hash => $password_hash,
tls_options => $tls_options,
require => Mysql_database[$database],
}
}
if $create_grant {
mysql_grant { "${user}@${host}/${database}.*":
privileges => $privileges,
table => "${database}.*",
require => Mysql_user["${user}@${host}"],
user => "${user}@${host}",
}
}
}

View File

@ -0,0 +1,47 @@
# == Definition: openstacklib::db::postgresql
#
# This resource configures a postgresql database for an OpenStack service
#
# == Parameters:
#
# [*password_hash*]
# Password hash to use for the database user for this service;
# string; required
#
# [*dbname*]
# The name of the database
# string; optional; default to the $title of the resource, i.e. 'nova'
#
# [*user*]
# The database user to create;
# string; optional; default to the $title of the resource, i.e. 'nova'
#
# [*encoding*]
# The charset to use for the database;
# string; optional; default to undef
#
# [*privileges*]
# Privileges given to the database user;
# string or array of strings; optional; default to 'ALL'
define openstacklib::db::postgresql (
$password_hash,
$dbname = $title,
$user = $title,
$encoding = undef,
$privileges = 'ALL',
){
if ((($::operatingsystem == 'RedHat' or $::operatingsystem == 'CentOS') and (versioncmp($::operatingsystemmajrelease, '6') <= 0))
or ($::operatingsystem == 'Fedora' and (versioncmp($::operatingsystemmajrelease, '14') <= 0))) {
warning("The system packages handling the postgresql infrastructure for OpenStack \
are out of date and should not be relied on for database migrations.")
}
postgresql::server::db { $dbname:
user => $user,
password => $password_hash,
encoding => $encoding,
grant => $privileges,
}
}

View File

@ -0,0 +1,14 @@
# == Class: openstacklib::defaults
#
# Default configuration for all openstack-puppet module.
#
# This file is loaded in the params.pp of each class.
#
class openstacklib::defaults {
# Ensure all package resources have virtual package enable.
if versioncmp($::puppetversion, '4.0.0') < 0 and versioncmp($::puppetversion, '3.6.1') >= 0 {
Package<| tag == 'openstack' |> {
allow_virtual => true,
}
}
}

View File

@ -0,0 +1,105 @@
#
# Copyright (C) 2014 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.
#
# == Definition: openstacklib::messaging::rabbitmq
#
# This resource creates RabbitMQ resources for an OpenStack service.
#
# == Parameters:
#
# [*userid*]
# (optional) The username to use when connecting to Rabbit
# Defaults to 'guest'
#
# [*password*]
# (optional) The password to use when connecting to Rabbit
# Defaults to 'guest'
#
# [*virtual_host*]
# (optional) The virtual host to use when connecting to Rabbit
# Defaults to '/'
#
# [*is_admin*]
# (optional) If the user should be admin or not
# Defaults to false
#
# [*configure_permission*]
# (optional) Define configure permission
# Defaults to '.*'
#
# [*write_permission*]
# (optional) Define write permission
# Defaults to '.*'
#
# [*read_permission*]
# (optional) Define read permission
# Defaults to '.*'
#
# [*manage_user*]
# (optional) Manage or not the user
# Defaults to true
#
# [*manage_user_permissions*]
# (optional) Manage or not user permissions
# Defaults to true
#
# [*manage_vhost*]
# (optional) Manage or not the vhost
# Defaults to true
#
define openstacklib::messaging::rabbitmq(
$userid = 'guest',
$password = 'guest',
$virtual_host = '/',
$is_admin = false,
$configure_permission = '.*',
$write_permission = '.*',
$read_permission = '.*',
$manage_user = true,
$manage_user_permissions = true,
$manage_vhost = true,
) {
if $manage_user {
if $userid == 'guest' {
$is_admin_real = false
} else {
$is_admin_real = $is_admin
}
ensure_resource('rabbitmq_user', $userid, {
'admin' => $is_admin_real,
'password' => $password,
'provider' => 'rabbitmqctl',
})
}
if $manage_user_permissions {
ensure_resource('rabbitmq_user_permissions', "${userid}@${virtual_host}", {
'configure_permission' => $configure_permission,
'write_permission' => $write_permission,
'read_permission' => $read_permission,
'provider' => 'rabbitmqctl',
})
}
if $manage_vhost {
ensure_resource('rabbitmq_vhost', $virtual_host, {
'provider' => 'rabbitmqctl',
})
}
}

View File

@ -0,0 +1,15 @@
# == Class: openstacklib::openstackclient
#
# Installs the openstackclient
#
# == Parameters
#
# [*package_ensure*]
# Ensure state of the openstackclient package.
# Optional. Defaults to 'present'.
#
class openstacklib::openstackclient(
$package_ensure = 'present',
){
ensure_packages('python-openstackclient', {'ensure' => $package_ensure, tag => 'openstack'})
}

View File

@ -0,0 +1,19 @@
# == Class: openstacklib::policies
#
# This resource is an helper to call the policy definition
#
# == Parameters:
#
# [*policies*]
# Hash of policies one would like to set to specific values
# hash; optional
#
class openstacklib::policy (
$policies = {},
) {
validate_hash($policies)
create_resources('openstacklib::policy::base', $policies)
}

View File

@ -0,0 +1,45 @@
# == Definition: openstacklib::policy::base
#
# This resource configures the policy.json file for an OpenStack service
#
# == Parameters:
#
# [*file_path*]
# Path to the policy.json file
# string; required
#
# [*key*]
# The key to replace the value for
# string; required; the key to replace the value for
#
# [*value*]
# The value to set
# string; optional; the value to set
#
define openstacklib::policy::base (
$file_path,
$key,
$value = '',
) {
# Add entry if it doesn't exists
augeas { "${file_path}-${key}-${value}-add":
lens => 'Json.lns',
incl => $file_path,
changes => [
"set dict/entry[last()+1] \"${key}\"",
"set dict/entry[last()]/string \"${value}\"",
],
onlyif => "match dict/entry[*][.=\"${key}\"] size == 0",
}
# Requires that the entry is added before this call or it will fail.
augeas { "${file_path}-${key}-${value}" :
lens => 'Json.lns',
incl => $file_path,
changes => "set dict/entry[*][.=\"${key}\"]/string \"${value}\"",
require => Augeas["${file_path}-${key}-${value}-add"],
}
}

View File

@ -0,0 +1,43 @@
#
# Copyright (C) 2016 Matthew J. Black
#
# Author: Matthew J. Black <mjblack@gmail.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: openstacklib::policyrcd
#
# [*services*]
# (required) The services that should be in the policy-rc.d shell script
# that should not autostart on install.
#
class openstacklib::policyrcd(
$services
) {
validate_array($services)
if $::osfamily == 'Debian' {
# We put this out there so openstack services wont auto start
# when installed.
file { '/usr/sbin/policy-rc.d':
ensure => present,
content => template('openstacklib/policy-rc.d.erb'),
mode => '0755',
owner => root,
group => root,
}
File['/usr/sbin/policy-rc.d'] -> Package<| tag == 'openstack' |>
}
}

View File

@ -0,0 +1,99 @@
#
# Copyright (C) 2014 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.
#
# == Definition: openstacklib::service_validation
#
# This resource does service validation for an OpenStack service.
#
# == Parameters:
#
# [*command*]
# Command to run for validating the service;
# string; required
#
# [*service_name*]
# The name of the service to validate;
# string; optional; default to the $title of the resource, i.e. 'nova-api'
#
# [*path*]
# The path of the command to validate the service;
# string; optional; default to '/usr/bin:/bin:/usr/sbin:/sbin'
#
# [*provider*]
# The provider to use for the exec command;
# string; optional; default to 'shell'
#
# [*refreshonly*]
# If the service validation should only occur on a refresh/notification;
# boolean; optional; default to false
#
# [*timeout*]
# The maximum time the command should take;
# string; optional; default to '60'
#
# [*tries*]
# Number of times to retry validation;
# string; optional; default to '10'
#
# [*try_sleep*]
# Number of seconds between validation attempts;
# string; optional; default to '2'
#
# [*onlyif*]
# Run the exec if all conditions in the array return true.
# string or array; optional; default to 'undef'
#
# [*unless*]
# Run the exec if all conditions in the array return false.
# string or array; optional; default to 'undef'
#
define openstacklib::service_validation(
$command,
$service_name = $name,
$path = '/usr/bin:/bin:/usr/sbin:/sbin',
$provider = shell,
$refreshonly = false,
$timeout = '60',
$tries = '10',
$try_sleep = '2',
$onlyif = undef,
$unless = undef,
) {
if $onlyif and $unless {
fail ('Only one parameter should be declared: onlyif or unless')
}
exec { "execute ${service_name} validation":
command => $command,
path => $path,
provider => $provider,
refreshonly => $refreshonly,
timeout => $timeout,
tries => $tries,
try_sleep => $try_sleep,
onlyif => $onlyif,
unless => $unless,
logoutput => 'on_failure',
}
anchor { "create ${service_name} anchor":
require => Exec["execute ${service_name} validation"],
}
}

View File

@ -0,0 +1,248 @@
#
# Copyright (C) 2014 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: openstacklib::wsgi::apache
#
# Serve a service with apache mod_wsgi
# When using this class you should disable your service.
#
# == Parameters
#
# [*service_name*]
# (optional) Name of the service to run.
# Example: nova-api
# Defaults to $name
#
# [*servername*]
# (optional) The servername for the virtualhost.
# Defaults to $::fqdn
#
# [*bind_host*]
# (optional) The host/ip address Apache will listen on.
# Defaults to undef (listen on all ip addresses).
#
# [*bind_port*]
# (optional) The port to listen.
# Defaults to undef
#
# [*group*]
# (optional) Group with permissions on the script
# Defaults to undef
#
# [*path*]
# (optional) The prefix for the endpoint.
# Defaults to '/'
#
# [*priority*]
# (optional) The priority for the vhost.
# Defaults to '10'
#
# [*ssl*]
# (optional) Use ssl ? (boolean)
# Defaults to false
#
# [*ssl_cert*]
# (optional) Path to SSL certificate
# Default to apache::vhost 'ssl_*' defaults.
#
# [*ssl_key*]
# (optional) Path to SSL key
# Default to apache::vhost 'ssl_*' defaults.
#
# [*ssl_chain*]
# (optional) SSL chain
# Default to apache::vhost 'ssl_*' defaults.
#
# [*ssl_ca*]
# (optional) Path to SSL certificate authority
# Default to apache::vhost 'ssl_*' defaults.
#
# [*ssl_crl_path*]
# (optional) Path to SSL certificate revocation list
# Default to apache::vhost 'ssl_*' defaults.
#
# [*ssl_crl*]
# (optional) SSL certificate revocation list name
# Default to apache::vhost 'ssl_*' defaults.
#
# [*ssl_certs_dir*]
# (optional) Path to SSL certificate directory
# Default to apache::vhost 'ssl_*' defaults.
#
# [*threads*]
# (optional) The number of threads for the vhost.
# Defaults to $::os_workers
#
# [*user*]
# (optional) User with permissions on the script
# Defaults to undef
#
# [*workers*]
# (optional) The number of workers for the vhost.
# Defaults to '1'
#
# [*wsgi_daemon_process*]
# (optional) Name of the WSGI daemon process.
# Defaults to $name
#
# [*wsgi_process_display_name*]
# (optional) Name of the WSGI process display-name.
# Defaults to $name
#
# [*wsgi_process_group*]
# (optional) Name of the WSGI process group.
# Defaults to $name
#
# [*wsgi_script_dir*]
# (optional) The directory path of the WSGI script.
# Defaults to undef
#
# [*wsgi_script_file*]
# (optional) The file path of the WSGI script.
# Defaults to undef
#
# [*wsgi_script_source*]
# (optional) The source of the WSGI script.
# Defaults to undef
#
# [*wsgi_application_group*]
# (optional) The application group of the WSGI script.
# Defaults to '%{GLOBAL}'
#
# [*wsgi_pass_authorization*]
# (optional) Whether HTTP authorisation headers are passed through to a WSGI
# script when the equivalent HTTP request headers are present.
# Defaults to undef
#
# [*wsgi_chunked_request*]
# (optional) Makes the vhost allow chunked requests which is useful for
# handling TE (Transfer-Encoding), chunked or gzip. This sets the
# WSGIChunkedRequest option in the vhost.
# Defaults to undef
#
# [*vhost_custom_fragment*]
# (optional) Passes a string of custom configuration
# directives to be placed at the end of the vhost configuration.
# Defaults to undef.
#
# [*allow_encoded_slashes*]
# (optional) If set, uses apache's AllowEncodedSlashes option in the vhost.
# This option is passed to puppetlabs-apache, which accepts only 4
# options: undef, "on", "off" or "nodecode". This is thus validated in the
# underlying vhost resource.
# Defaults to undef.
#
define openstacklib::wsgi::apache (
$service_name = $name,
$bind_host = undef,
$bind_port = undef,
$group = undef,
$path = '/',
$priority = '10',
$servername = $::fqdn,
$ssl = false,
$ssl_ca = undef,
$ssl_cert = undef,
$ssl_certs_dir = undef,
$ssl_chain = undef,
$ssl_crl = undef,
$ssl_crl_path = undef,
$ssl_key = undef,
$threads = $::os_workers,
$user = undef,
$workers = 1,
$wsgi_daemon_process = $name,
$wsgi_process_display_name = $name,
$wsgi_process_group = $name,
$wsgi_script_dir = undef,
$wsgi_script_file = undef,
$wsgi_script_source = undef,
$wsgi_application_group = '%{GLOBAL}',
$wsgi_pass_authorization = undef,
$wsgi_chunked_request = undef,
$vhost_custom_fragment = undef,
$allow_encoded_slashes = undef,
) {
include ::apache
include ::apache::mod::wsgi
if $ssl {
include ::apache::mod::ssl
}
# Ensure there's no trailing '/' except if this is also the only character
$path_real = regsubst($path, '(^/.*)/$', '\1')
if !defined(File[$wsgi_script_dir]) {
file { $wsgi_script_dir:
ensure => directory,
owner => $user,
group => $group,
require => Package['httpd'],
}
}
file { $service_name:
ensure => file,
path => "${wsgi_script_dir}/${wsgi_script_file}",
source => $wsgi_script_source,
owner => $user,
group => $group,
mode => '0644',
require => File[$wsgi_script_dir],
}
$wsgi_daemon_process_options = {
user => $user,
group => $group,
processes => $workers,
threads => $threads,
display-name => $wsgi_process_display_name,
}
$wsgi_script_aliases = hash([$path_real,"${wsgi_script_dir}/${wsgi_script_file}"])
::apache::vhost { $service_name:
ensure => 'present',
servername => $servername,
ip => $bind_host,
port => $bind_port,
docroot => $wsgi_script_dir,
docroot_owner => $user,
docroot_group => $group,
priority => $priority,
setenvif => ['X-Forwarded-Proto https HTTPS=1'],
ssl => $ssl,
ssl_cert => $ssl_cert,
ssl_key => $ssl_key,
ssl_chain => $ssl_chain,
ssl_ca => $ssl_ca,
ssl_crl_path => $ssl_crl_path,
ssl_crl => $ssl_crl,
ssl_certs_dir => $ssl_certs_dir,
wsgi_daemon_process => $wsgi_daemon_process,
wsgi_daemon_process_options => $wsgi_daemon_process_options,
wsgi_process_group => $wsgi_process_group,
wsgi_script_aliases => $wsgi_script_aliases,
wsgi_application_group => $wsgi_application_group,
wsgi_pass_authorization => $wsgi_pass_authorization,
wsgi_chunked_request => $wsgi_chunked_request,
custom_fragment => $vhost_custom_fragment,
allow_encoded_slashes => $allow_encoded_slashes,
require => File[$service_name],
}
}

View File

@ -0,0 +1,73 @@
{
"author": "Puppet Labs and OpenStack Contributors",
"dependencies": [
{
"name": "puppetlabs/apache",
"version_requirement": ">=1.0.0 <2.0.0"
},
{
"name": "puppetlabs/inifile",
"version_requirement": ">=1.0.0 <2.0.0"
},
{
"name": "puppetlabs/mysql",
"version_requirement": ">=3.10.0 <4.0.0"
},
{
"name": "puppetlabs/stdlib",
"version_requirement": ">=4.0.0 <5.0.0"
},
{
"name": "puppetlabs/rabbitmq",
"version_requirement": ">=2.0.2 <6.0.0"
},
{
"name": "puppetlabs/postgresql",
"version_requirement": ">=3.3.0 <5.0.0"
}
],
"description": "Puppet module library to expose common functionality between OpenStack modules.",
"issues_url": "https://bugs.launchpad.net/puppet-openstacklib",
"license": "Apache-2.0",
"name": "openstack-openstacklib",
"operatingsystem_support": [
{
"operatingsystem": "Debian",
"operatingsystemrelease": [
"8"
]
},
{
"operatingsystem": "Fedora",
"operatingsystemrelease": [
"24"
]
},
{
"operatingsystem": "RedHat",
"operatingsystemrelease": [
"7"
]
},
{
"operatingsystem": "Ubuntu",
"operatingsystemrelease": [
"16.04"
]
}
],
"project_page": "https://launchpad.net/puppet-openstacklib",
"requirements": [
{
"name": "pe",
"version_requirement": "4.x"
},
{
"name": "puppet",
"version_requirement": "4.x"
}
],
"source": "git://github.com/openstack/puppet-openstacklib.git",
"summary": "Puppet OpenStack Libraries",
"version": "11.0.0"
}

View File

@ -0,0 +1,6 @@
---
features:
- For the users that result from the usage of the mysql resource, it is now
possible to specify the TLS options. This is useful if one wants to force
the user to only connect using TLS, or if one wants to force the usage of
client certificates for this specific user.

View File

@ -0,0 +1,4 @@
---
fixes:
- Bugfix 1664561. Allow to use Integers and Strings
for the port parameter.

View File

@ -0,0 +1,3 @@
---
fixes:
- Catch HTTP 403 responses (not authorized requests) in openstack provider.

View File

@ -0,0 +1,12 @@
---
features:
- Add a manifest which is loaded by all puppet modules in
manifests/param.pp. This is described in
`bug 1599113
<https://bugs.launchpad.net/puppet-openstacklib/+bug/1599113>`__
fixes:
- The first feature would help fixing RDO package using virtual
package which is a recurring problem. Example of such problem is
`bug 1599113
<https://bugs.launchpad.net/puppet-keystone/+bug/1597753>`__

View File

@ -0,0 +1,5 @@
---
features:
- Add the possibility to exclude some exception from retry
mechanism. It helps to fix `bug 1597357
<https://bugs.launchpad.net/puppet-keystone/+bug/1597357>`__

View File

@ -0,0 +1,5 @@
---
features:
- Add the ability to transform values in config files. This allows
operators to configure input directly at assignment by specifying
a 'transform_to' attribute.

View File

@ -0,0 +1,4 @@
---
features:
- Allow to give an array of IP addresses to normalize_ip_for_uri and normalize
each IP in the list.

View File

@ -0,0 +1,6 @@
---
features:
- Utility to handle IPv6 address brackets with normalize_ip_for_uri
is a function that help us to add brackets to IPv6 addresses
when missing. The function moved to puppetlabs-stdlib but is not in latest release
yet. Once it's done, we'll probably drop this function so our modules will use stdlib.

View File

@ -0,0 +1,11 @@
---
features:
- Add a class that takes an array of services that can be
configured to not autostart on package install. The most
notable example is keystone. The policy-rc.d file is
generated to return the correct exit code to prevent the
services from autostarting on package install. This change
is only meant for debian based systems.
issues:
- ubuntu cloud archive keystone package is auto-starting the
eventlet process on package install.

View File

@ -0,0 +1,4 @@
---
features:
- Add an "extra" hash parameter to os_database_connection that allows to extend
the database uri configuration.

View File

@ -0,0 +1,9 @@
---
features:
- Add the ability to skip the mysql user and/or grant creation as part of the
openstack::db::mysql resource.
fixes:
- openstack::db::mysql could not be used to create multiple databases
with the same user/password for access due to a duplicate mysql_user
resource declaration. Now the user and/or grant creation process can be
skipped if they already exist.

View File

@ -0,0 +1,4 @@
---
fixes:
- bug 1651215 The transport parameter was not being used so
transport was fixed value 'rabbit'

View File

@ -0,0 +1,4 @@
---
features:
- os_transport_url puppet parser function can be used to generate valid
transport_url URIs from a hash of connection parameters.

View File

@ -0,0 +1,12 @@
---
features:
- Moved existing $::os_workers to $::os_workers_small
- Updated $::os_workers to have a value between '2' and '12'.
The value of this fact is the larger value between '2'
and the number of processors divided by '2' but will not
exceed '12'.
- Created fact $::os_workers_large to have a value of number
of processors divided by '2'
fixes:
- bug 1650424 The current calculation for os_workers negatively
impacts api response times

View File

@ -0,0 +1,6 @@
---
features:
- Created a new fact called $::os_workers to be used as the defaults for any
of the openstack service worker configurations. This fact will have a value
between '2' and '8'. The value of this fact is the larger value between '2'
and the number of processors divided by '4' but will not exceed '8'.

View File

@ -0,0 +1,5 @@
---
other:
- Parameters that control the number of spawned child processes for
distributing processing have had their default value changed from
::processorcount to ::os_workers.

View File

@ -0,0 +1,3 @@
---
features:
- Added policy_rcd provider for managing policy-rc.d for Debian family.

View File

@ -0,0 +1,7 @@
---
fixes:
- Add retries to the openstack command.
Increase command timeout to 20s and request timeout to 60s
and sleep 3s between retries. Do not retry non-idempotent actions.
This is a more robust implementation that will prevent failures in case
of Keystone API failures during a deployment.

View File

@ -0,0 +1,4 @@
---
fixes:
- Add support for multiple regions in base provider code used by other puppet
modules.

View File

@ -0,0 +1,4 @@
---
features:
- Release notes are no longer maintained by hand, we now use the reno tool to
manage them.

View File

@ -0,0 +1,262 @@
# -*- coding: utf-8 -*-
# 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.
#
# If extensions (or modules to document with autodoc) are in another directory,
# add these directories to sys.path here. If the directory is relative to the
# documentation root, use os.path.abspath to make it absolute, like shown here.
#sys.path.insert(0, os.path.abspath('.'))
# -- General configuration ------------------------------------------------
# If your documentation needs a minimal Sphinx version, state it here.
#needs_sphinx = '1.0'
# Add any Sphinx extension module names here, as strings. They can be
# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom
# ones.
extensions = [
'oslosphinx',
'reno.sphinxext',
]
# Add any paths that contain templates here, relative to this directory.
templates_path = ['_templates']
# The suffix of source filenames.
source_suffix = '.rst'
# The encoding of source files.
#source_encoding = 'utf-8-sig'
# The master toctree document.
master_doc = 'index'
# General information about the project.
project = u'puppet-openstacklib Release Notes'
copyright = u'2017, Puppet OpenStack Developers'
# The version info for the project you're documenting, acts as replacement for
# |version| and |release|, also used in various other places throughout the
# built documents.
#
# The short X.Y version.
version = '11.0.0'
# The full version, including alpha/beta/rc tags.
release = '11.0.0'
# The language for content autogenerated by Sphinx. Refer to documentation
# for a list of supported languages.
#language = None
# There are two options for replacing |today|: either, you set today to some
# non-false value, then it is used:
#today = ''
# Else, today_fmt is used as the format for a strftime call.
#today_fmt = '%B %d, %Y'
# List of patterns, relative to source directory, that match files and
# directories to ignore when looking for source files.
exclude_patterns = []
# The reST default role (used for this markup: `text`) to use for all
# documents.
#default_role = None
# If true, '()' will be appended to :func: etc. cross-reference text.
#add_function_parentheses = True
# If true, the current module name will be prepended to all description
# unit titles (such as .. function::).
#add_module_names = True
# If true, sectionauthor and moduleauthor directives will be shown in the
# output. They are ignored by default.
#show_authors = False
# The name of the Pygments (syntax highlighting) style to use.
pygments_style = 'sphinx'
# A list of ignored prefixes for module index sorting.
#modindex_common_prefix = []
# If true, keep warnings as "system message" paragraphs in the built documents.
#keep_warnings = False
# -- Options for HTML output ----------------------------------------------
# The theme to use for HTML and HTML Help pages. See the documentation for
# a list of builtin themes.
html_theme = 'default'
# Theme options are theme-specific and customize the look and feel of a theme
# further. For a list of options available for each theme, see the
# documentation.
#html_theme_options = {}
# Add any paths that contain custom themes here, relative to this directory.
#html_theme_path = []
# The name for this set of Sphinx documents. If None, it defaults to
# "<project> v<release> documentation".
#html_title = None
# A shorter title for the navigation bar. Default is the same as html_title.
#html_short_title = None
# The name of an image file (relative to this directory) to place at the top
# of the sidebar.
#html_logo = None
# The name of an image file (within the static path) to use as favicon of the
# docs. This file should be a Windows icon file (.ico) being 16x16 or 32x32
# pixels large.
#html_favicon = None
# Add any paths that contain custom static files (such as style sheets) here,
# relative to this directory. They are copied after the builtin static files,
# so a file named "default.css" will overwrite the builtin "default.css".
html_static_path = ['_static']
# Add any extra paths that contain custom files (such as robots.txt or
# .htaccess) here, relative to this directory. These files are copied
# directly to the root of the documentation.
#html_extra_path = []
# If not '', a 'Last updated on:' timestamp is inserted at every page bottom,
# using the given strftime format.
#html_last_updated_fmt = '%b %d, %Y'
# If true, SmartyPants will be used to convert quotes and dashes to
# typographically correct entities.
#html_use_smartypants = True
# Custom sidebar templates, maps document names to template names.
#html_sidebars = {}
# Additional templates that should be rendered to pages, maps page names to
# template names.
#html_additional_pages = {}
# If false, no module index is generated.
#html_domain_indices = True
# If false, no index is generated.
#html_use_index = True
# If true, the index is split into individual pages for each letter.
#html_split_index = False
# If true, links to the reST sources are added to the pages.
#html_show_sourcelink = True
# If true, "Created using Sphinx" is shown in the HTML footer. Default is True.
#html_show_sphinx = True
# If true, "(C) Copyright ..." is shown in the HTML footer. Default is True.
#html_show_copyright = True
# If true, an OpenSearch description file will be output, and all pages will
# contain a <link> tag referring to it. The value of this option must be the
# base URL from which the finished HTML is served.
#html_use_opensearch = ''
# This is the file name suffix for HTML files (e.g. ".xhtml").
#html_file_suffix = None
# Output file base name for HTML help builder.
htmlhelp_basename = 'puppet-openstacklibReleaseNotesdoc'
# -- Options for LaTeX output ---------------------------------------------
latex_elements = {
# The paper size ('letterpaper' or 'a4paper').
#'papersize': 'letterpaper',
# The font size ('10pt', '11pt' or '12pt').
#'pointsize': '10pt',
# Additional stuff for the LaTeX preamble.
#'preamble': '',
}
# Grouping the document tree into LaTeX files. List of tuples
# (source start file, target name, title,
# author, documentclass [howto, manual, or own class]).
latex_documents = [
('index', 'puppet-openstacklibReleaseNotes.tex', u'puppet-openstacklib Release Notes Documentation',
u'2017, Puppet OpenStack Developers', 'manual'),
]
# The name of an image file (relative to this directory) to place at the top of
# the title page.
#latex_logo = None
# For "manual" documents, if this is true, then toplevel headings are parts,
# not chapters.
#latex_use_parts = False
# If true, show page references after internal links.
#latex_show_pagerefs = False
# If true, show URL addresses after external links.
#latex_show_urls = False
# Documents to append as an appendix to all manuals.
#latex_appendices = []
# If false, no module index is generated.
#latex_domain_indices = True
# -- Options for manual page output ---------------------------------------
# One entry per manual page. List of tuples
# (source start file, name, description, authors, manual section).
man_pages = [
('index', 'puppet-openstacklibreleasenotes', u'puppet-openstacklib Release Notes Documentation',
[u'2017, Puppet OpenStack Developers'], 1)
]
# If true, show URL addresses after external links.
#man_show_urls = False
# -- Options for Texinfo output -------------------------------------------
# Grouping the document tree into Texinfo files. List of tuples
# (source start file, target name, title, author,
# dir menu entry, description, category)
texinfo_documents = [
('index', 'puppet-openstacklibReleaseNotes', u'puppet-openstacklib Release Notes Documentation',
u'2017, Puppet OpenStack Developers', 'puppet-openstacklibReleaseNotes', 'One line description of project.',
'Miscellaneous'),
]
# Documents to append as an appendix to all manuals.
#texinfo_appendices = []
# If false, no module index is generated.
#texinfo_domain_indices = True
# How to display URL addresses: 'footnote', 'no', or 'inline'.
#texinfo_show_urls = 'footnote'
# If true, do not generate a @detailmenu in the "Top" node's menu.
#texinfo_no_detailmenu = False
# -- Options for Internationalization output ------------------------------
locale_dirs = ['locale/']

View File

@ -0,0 +1,21 @@
=============================================
Welcome to puppet-openstacklib Release Notes!
=============================================
Contents
========
.. toctree::
:maxdepth: 2
unreleased
ocata
newton
mitaka
Indices and tables
==================
* :ref:`genindex`
* :ref:`search`

View File

@ -0,0 +1,6 @@
============================
Mitaka Series Release Notes
============================
.. release-notes::
:branch: origin/stable/mitaka

View File

@ -0,0 +1,6 @@
===================================
Newton Series Release Notes
===================================
.. release-notes::
:branch: origin/stable/newton

View File

@ -0,0 +1,6 @@
===================================
Ocata Series Release Notes
===================================
.. release-notes::
:branch: origin/stable/ocata

View File

@ -0,0 +1,5 @@
==============================
Current Series Release Notes
==============================
.. release-notes::

View File

@ -0,0 +1,13 @@
[metadata]
name = puppet-openstacklib
summary = Puppet module for OpenStack Openstacklib
description-file =
README.md
author = Puppet Labs and OpenStack Contributors
author-email = openstack-dev@lists.openstack.org
home-page = http://docs.openstack.org/developer/puppet-openstack-guide
classifier =
Intended Audience :: Developers
Intended Audience :: System Administrators
License :: OSI Approved :: Apache Software License
Operating System :: POSIX :: Linux

View File

@ -0,0 +1,22 @@
#!/usr/bin/env python
# Copyright (c) 2013 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.
# THIS FILE IS MANAGED BY THE GLOBAL REQUIREMENTS REPO - DO NOT EDIT
import setuptools
setuptools.setup(
setup_requires=['pbr'],
pbr=True)

View File

@ -0,0 +1,7 @@
require 'spec_helper_acceptance'
describe 'Defaults manifest' do
context 'virtual_package' do
it_behaves_like 'puppet_apply_success_from_example', 'virtual_packages'
end
end

View File

@ -0,0 +1,35 @@
require 'spec_helper_acceptance'
describe 'openstacklib mysql' do
context 'default parameters' do
it 'should work with no errors' do
pp= <<-EOS
Exec { logoutput => 'on_failure' }
class { '::mysql::server': }
::openstacklib::db::mysql { 'beaker':
password_hash => mysql_password('keystone'),
allowed_hosts => '127.0.0.1',
}
EOS
# Run it twice and test for idempotency
apply_manifest(pp, :catch_failures => true)
apply_manifest(pp, :catch_changes => true)
end
describe port(3306) do
it { is_expected.to be_listening.with('tcp') }
end
describe 'test database listing' do
it 'should list beaker database' do
expect(shell("mysql -e 'show databases;'|grep -q beaker").exit_code).to be_zero
end
end
end
end

View File

@ -0,0 +1,11 @@
HOSTS:
centos-server-70-x64:
roles:
- master
platform: el-7-x86_64
box: puppetlabs/centos-7.0-64-nocm
box_url: https://vagrantcloud.com/puppetlabs/centos-7.0-64-nocm
hypervisor: vagrant
CONFIG:
log_level: debug
type: foss

View File

@ -0,0 +1,10 @@
HOSTS:
ubuntu-server-14.04-amd64:
roles:
- master
platform: ubuntu-14.04-amd64
box: puppetlabs/ubuntu-14.04-64-nocm
box_url: https://vagrantcloud.com/puppetlabs/ubuntu-14.04-64-nocm
hypervisor: vagrant
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,10 @@
HOSTS:
ubuntu-16.04-amd64:
roles:
- master
platform: ubuntu-16.04-amd64
hypervisor: none
ip: 127.0.0.1
CONFIG:
type: foss
set_env: false

View File

@ -0,0 +1,11 @@
HOSTS:
ubuntu-server-14.04-amd64:
roles:
- master
platform: ubuntu-14.04-amd64
box: puppetlabs/ubuntu-14.04-64-nocm
box_url: https://vagrantcloud.com/puppetlabs/ubuntu-14.04-64-nocm
hypervisor: vagrant
CONFIG:
log_level: debug
type: foss

View File

@ -0,0 +1,57 @@
require 'spec_helper_acceptance'
describe 'basic config provider resource' do
context 'default parameters' do
it 'should work with no errors' do
pp= <<-EOS
Exec { logoutput => 'on_failure' }
# We create the file manually here because we only want to test
# the logic of the provider hence installing the whole stack would
# result in some overhead that is already tested in puppet-keystone
File <||> -> Keystone_config <||>
file { '/etc/keystone' :
ensure => directory,
}
file { '/etc/keystone/keystone.conf' :
ensure => file,
}
keystone_config { 'DEFAULT/thisshouldexist' :
value => 'foo',
}
keystone_config { 'DEFAULT/thisshouldnotexist' :
value => '<SERVICE DEFAULT>',
}
keystone_config { 'DEFAULT/thisshouldexist2' :
value => '<SERVICE DEFAULT>',
ensure_absent_val => 'toto',
}
keystone_config { 'DEFAULT/thisshouldnotexist2' :
value => 'toto',
ensure_absent_val => 'toto',
}
EOS
# Run it twice and test for idempotency
apply_manifest(pp, :catch_failures => true)
apply_manifest(pp, :catch_changes => true)
end
describe file('/etc/keystone/keystone.conf') do
it { should exist }
it { should contain('thisshouldexist=foo') }
it { should contain('thisshouldexist2=<SERVICE DEFAULT>') }
its(:content) { should_not match /thisshouldnotexist/ }
end
end
end

View File

@ -0,0 +1,55 @@
require 'spec_helper_acceptance'
describe 'openstacklib class' do
context 'default parameters' do
it 'should work with no errors' do
pp= <<-EOS
Exec { logoutput => 'on_failure' }
if $::osfamily == 'RedHat' {
class { '::openstack_extras::repo::redhat::redhat':
release => 'kilo',
manage_epel => false,
}
} else {
include ::apt
}
class { '::rabbitmq':
delete_guest_user => true,
package_provider => $::package_provider
}
# openstacklib resources
include ::openstacklib::openstackclient
::openstacklib::messaging::rabbitmq { 'beaker':
userid => 'beaker',
is_admin => true,
}
EOS
# Run it twice and test for idempotency
apply_manifest(pp, :catch_failures => true)
apply_manifest(pp, :catch_changes => true)
end
describe 'test rabbitmq resources' do
it 'should list rabbitmq beaker resources' do
shell('rabbitmqctl list_users') do |r|
expect(r.stdout).to match(/^beaker/)
expect(r.stdout).not_to match(/^guest/)
expect(r.exit_code).to eq(0)
end
shell('rabbitmqctl list_permissions') do |r|
expect(r.stdout).to match(/^beaker\t\.\*\t\.\*\t\.\*$/)
expect(r.exit_code).to eq(0)
end
end
end
end
end

View File

@ -0,0 +1,33 @@
require 'spec_helper'
describe 'openstacklib::defaults', type: :class do
on_supported_os.each do |os, facts|
let(:pre_condition) do
<<-eof
package { 'my_virt_package' :
ensure => present,
tag => 'openstack'
}
eof
end
context "Puppet < 4.0.0" do
context "on #{os}" do
let(:facts) { facts.merge(:puppetversion => '3.8.0') }
it { is_expected.to compile.with_all_deps }
it { is_expected.to contain_class('openstacklib::defaults') }
it { is_expected.to contain_package('my_virt_package')
.with(:allow_virtual => true)}
end
end
context "Puppet >= 4.0.0" do
context "on #{os}" do
let(:facts) { facts.merge(:puppetversion => '4.0.0') }
it { is_expected.to compile.with_all_deps }
it { is_expected.to contain_class('openstacklib::defaults') }
it { is_expected.to contain_package('my_virt_package')
.without(:allow_virtual)}
end
end
end
end

View File

@ -0,0 +1,41 @@
require 'spec_helper'
describe 'openstacklib::policy' do
shared_examples_for 'openstacklib::policy' do
context 'with basic configuration' do
let :params do
{
:policies => {
'foo' => {
'file_path' => '/etc/nova/policy.json',
'key' => 'context_is_admin',
'value' => 'foo:bar'
}
}
}
end
it 'configures the proper policy' do
is_expected.to contain_openstacklib__policy__base('foo').with(
:file_path => '/etc/nova/policy.json',
:key => 'context_is_admin',
:value => 'foo:bar'
)
end
end
end
on_supported_os({
:supported_os => OSDefaults.get_supported_os
}).each do |os,facts|
context "on #{os}" do
let (:facts) do
facts.merge!(OSDefaults.get_facts())
end
it_configures 'openstacklib::policy'
end
end
end

View File

@ -0,0 +1,102 @@
#
# Copyright (C) 2016 Matthew J. Black
#
# Author: Matthew J. Black <mjblack@gmail.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 openstacklib::policyrcd
#
require 'spec_helper'
describe 'openstacklib::policyrcd' do
shared_examples_for 'openstacklib::policyrcd on Debian platforms' do
context "with single service" do
let :params do
{ :services => ['keystone'] }
end
let(:contents) {
<<-eof
#!/bin/bash
if [ "$1" == "keystone" ]
then
exit 101
fi
eof
}
it 'creates policy-rc.d file' do
is_expected.to contain_file('/usr/sbin/policy-rc.d').with_content(contents)
end
end
context "with multiple services" do
let :params do
{ :services => ['keystone', 'nova'] }
end
let(:contents) {
<<-eof
#!/bin/bash
if [ "$1" == "keystone" ]
then
exit 101
fi
if [ "$1" == "nova" ]
then
exit 101
fi
eof
}
it 'creates policy-rc.d file' do
is_expected.to contain_file('/usr/sbin/policy-rc.d').with_content(contents)
end
end
end
shared_examples_for 'openstacklib::policyrcd on RedHat platforms' do
describe "with single service" do
let :params do
{ :services => ['keystone'] }
end
it 'does not create policy-rc.d file' do
is_expected.to_not contain_file('/usr/sbin/policy-rc.d')
end
end
end
on_supported_os({
:supported_os => OSDefaults.get_supported_os
}).each do |os,facts|
context "on #{os}" do
let (:facts) do
facts.merge!(OSDefaults.get_facts())
end
it_configures "openstacklib::policyrcd on #{facts[:osfamily]} platforms"
end
end
end

View File

@ -0,0 +1,102 @@
require 'spec_helper'
describe 'openstacklib::db::mysql::host_access' do
let :pre_condition do
"include mysql::server\n" +
"openstacklib::db::mysql { 'nova':\n" +
" password_hash => 'AA1420F182E88B9E5F874F6FBE7459291E8F4601'}"
end
shared_examples 'openstacklib::db::mysql::host_access examples' do
context 'with required parameters' do
let (:title) { 'nova_10.0.0.1' }
let :params do
{ :user => 'foobar',
:password_hash => 'AA1420F182E88B9E5F874F6FBE7459291E8F4601',
:database => 'nova',
:privileges => 'ALL' }
end
it { is_expected.to contain_mysql_user("#{params[:user]}@10.0.0.1").with(
:password_hash => params[:password_hash],
:tls_options => ['NONE']
)}
it { is_expected.to contain_mysql_grant("#{params[:user]}@10.0.0.1/#{params[:database]}.*").with(
:user => "#{params[:user]}@10.0.0.1",
:privileges => 'ALL',
:table => "#{params[:database]}.*"
)}
end
context 'with skipping user creation' do
let (:title) { 'nova_10.0.0.1' }
let :params do
{ :user => 'foobar',
:password_hash => 'AA1420F182E88B9E5F874F6FBE7459291E8F4601',
:database => 'nova',
:privileges => 'ALL',
:create_user => false,
}
end
it { is_expected.to_not contain_mysql_user("#{params[:user]}@10.0.0.1") }
it { is_expected.to contain_mysql_grant("#{params[:user]}@10.0.0.1/#{params[:database]}.*").with(
:user => "#{params[:user]}@10.0.0.1",
:privileges => 'ALL',
:table => "#{params[:database]}.*"
)}
end
context 'with skipping grant creation' do
let (:title) { 'nova_10.0.0.1' }
let :params do
{ :user => 'foobar',
:password_hash => 'AA1420F182E88B9E5F874F6FBE7459291E8F4601',
:database => 'nova',
:privileges => 'ALL',
:create_grant => false,
}
end
it { is_expected.to contain_mysql_user("#{params[:user]}@10.0.0.1").with(
:password_hash => params[:password_hash]
)}
it { is_expected.to_not contain_mysql_grant("#{params[:user]}@10.0.0.1/#{params[:database]}.*") }
end
context 'with skipping user and grant creation' do
let (:title) { 'nova_10.0.0.1' }
let :params do
{ :user => 'foobar',
:password_hash => 'AA1420F182E88B9E5F874F6FBE7459291E8F4601',
:database => 'nova',
:privileges => 'ALL',
:create_user => false,
:create_grant => false,
}
end
it { is_expected.to_not contain_mysql_user("#{params[:user]}@10.0.0.1") }
it { is_expected.to_not contain_mysql_grant("#{params[:user]}@10.0.0.1/#{params[:database]}.*") }
end
end
on_supported_os({
:supported_os => OSDefaults.get_supported_os
}).each do |os,facts|
context "on #{os}" do
let (:facts) do
facts.merge!(OSDefaults.get_facts())
end
it_configures 'openstacklib::db::mysql::host_access examples'
end
end
end

View File

@ -0,0 +1,229 @@
require 'spec_helper'
describe 'openstacklib::db::mysql' do
let :pre_condition do
'include mysql::server'
end
let (:title) { 'nova' }
let :required_params do
{ :password_hash => 'AA1420F182E88B9E5F874F6FBE7459291E8F4601' }
end
shared_examples 'openstacklib::db::mysql examples' do
context 'with only required parameters' do
let :params do
required_params
end
it { is_expected.to contain_mysql_database(title).with(
:charset => 'utf8',
:collate => 'utf8_general_ci'
)}
it { is_expected.to contain_openstacklib__db__mysql__host_access("#{title}_127.0.0.1").with(
:user => title,
:database => title,
:privileges => 'ALL',
:tls_options => ['NONE'],
)}
end
context 'with overriding dbname parameter' do
let :params do
{ :dbname => 'foobar' }.merge(required_params)
end
it { is_expected.to contain_mysql_database(params[:dbname]).with(
:charset => 'utf8',
:collate => 'utf8_general_ci'
)}
it { is_expected.to contain_openstacklib__db__mysql__host_access("#{params[:dbname]}_127.0.0.1").with(
:user => title,
:database => params[:dbname],
:privileges => 'ALL',
:create_user => true,
:create_grant => true,
:tls_options => ['NONE'],
)}
end
context 'with overriding user parameter' do
let :params do
{ :user => 'foobar' }.merge(required_params)
end
it { is_expected.to contain_mysql_database(title).with(
:charset => 'utf8',
:collate => 'utf8_general_ci'
)}
it { is_expected.to contain_openstacklib__db__mysql__host_access("#{title}_127.0.0.1").with(
:user => params[:user],
:database => title,
:privileges => 'ALL',
:create_user => true,
:create_grant => true,
:tls_options => ['NONE'],
)}
end
context 'when overriding charset parameter' do
let :params do
{ :charset => 'latin1' }.merge(required_params)
end
it { is_expected.to contain_mysql_database(title).with_charset(params[:charset]) }
end
context 'when omitting the required parameter password_hash' do
let :params do
required_params.delete(:password_hash)
end
it { expect { is_expected.to raise_error(Puppet::Error) } }
end
context 'when notifying other resources' do
let :pre_condition do
'exec {"nova-db-sync":}'
end
let :params do
{ :notify => 'Exec[nova-db-sync]'}.merge(required_params)
end
it { is_expected.to contain_exec('nova-db-sync').that_subscribes_to("Openstacklib::Db::Mysql[#{title}]") }
end
context 'when required for other openstack services' do
let :pre_condition do
'service {"keystone":}'
end
let :title do
'keystone'
end
let :params do
{ :before => 'Service[keystone]'}.merge(required_params)
end
it { is_expected.to contain_service('keystone').that_requires("Openstacklib::Db::Mysql[keystone]") }
end
context "overriding allowed_hosts parameter with array value" do
let :params do
{ :allowed_hosts => ['127.0.0.1','%'] }.merge(required_params)
end
it {is_expected.to contain_openstacklib__db__mysql__host_access("#{title}_127.0.0.1").with(
:user => title,
:password_hash => params[:password_hash],
:database => title
)}
it {is_expected.to contain_openstacklib__db__mysql__host_access("#{title}_%").with(
:user => title,
:password_hash => params[:password_hash],
:database => title
)}
end
context "overriding allowed_hosts parameter with string value" do
let :params do
{ :allowed_hosts => '192.168.1.1' }.merge(required_params)
end
it {is_expected.to contain_openstacklib__db__mysql__host_access("#{title}_192.168.1.1").with(
:user => title,
:password_hash => params[:password_hash],
:database => title
)}
end
context "overriding allowed_hosts parameter equals to host param " do
let :params do
{ :allowed_hosts => '127.0.0.1' }.merge(required_params)
end
it {is_expected.to contain_openstacklib__db__mysql__host_access("#{title}_127.0.0.1").with(
:user => title,
:password_hash => params[:password_hash],
:database => title
)}
end
context 'with skipping user creation' do
let :params do
{ :create_user => false }.merge(required_params)
end
it { is_expected.to contain_mysql_database(title).with(
:charset => 'utf8',
:collate => 'utf8_general_ci'
)}
it { is_expected.to contain_openstacklib__db__mysql__host_access("#{title}_127.0.0.1").with(
:user => title,
:database => title,
:privileges => 'ALL',
:create_user => false,
:create_grant => true,
)}
end
context 'with skipping grant creation' do
let :params do
{ :create_grant => false }.merge(required_params)
end
it { is_expected.to contain_mysql_database(title).with(
:charset => 'utf8',
:collate => 'utf8_general_ci'
)}
it { is_expected.to contain_openstacklib__db__mysql__host_access("#{title}_127.0.0.1").with(
:user => title,
:database => title,
:privileges => 'ALL',
:create_user => true,
:create_grant => false,
)}
end
context 'with skipping user and grant creation' do
let :params do
{ :create_user => false,
:create_grant => false }.merge(required_params)
end
it { is_expected.to contain_mysql_database(title).with(
:charset => 'utf8',
:collate => 'utf8_general_ci'
)}
it { is_expected.to_not contain_openstacklib__db__mysql__host_access("#{title}_127.0.0.1") }
end
context "overriding tls_options" do
let :params do
{ :tls_options => ['SSL'] }.merge(required_params)
end
it {is_expected.to contain_openstacklib__db__mysql__host_access("#{title}_127.0.0.1").with(
:user => title,
:password_hash => params[:password_hash],
:database => title,
:tls_options => ['SSL'],
)}
end
end
on_supported_os({
:supported_os => OSDefaults.get_supported_os
}).each do |os,facts|
context "on #{os}" do
let (:facts) do
facts.merge!(OSDefaults.get_facts())
end
it_configures 'openstacklib::db::mysql examples'
end
end
end

View File

@ -0,0 +1,81 @@
require 'spec_helper'
describe 'openstacklib::db::postgresql' do
let (:title) { 'nova' }
let :required_params do
{ :password_hash => 'AA1420F182E88B9E5F874F6FBE7459291E8F4601' }
end
let (:pre_condition) do
"include ::postgresql::server"
end
shared_examples 'openstacklib::db::postgresql examples' do
context 'with only required parameters' do
let :params do
required_params
end
it { is_expected.to contain_postgresql__server__db(title).with(
:user => title,
:password => params[:password_hash]
)}
end
context 'when overriding encoding' do
let :params do
{ :encoding => 'latin1' }.merge(required_params)
end
it { is_expected.to contain_postgresql__server__db(title).with_encoding(params[:encoding]) }
end
context 'when omitting the required parameter password_hash' do
let :params do
required_params.delete(:password_hash)
end
it { expect { is_expected.to raise_error(Puppet::Error) } }
end
context 'when notifying other resources' do
let :pre_condition do
"include ::postgresql::server
exec { 'nova-db-sync': }"
end
let :params do
{ :notify => 'Exec[nova-db-sync]'}.merge(required_params)
end
it {is_expected.to contain_exec('nova-db-sync').that_subscribes_to("Openstacklib::Db::Postgresql[#{title}]") }
end
context 'when required for other openstack services' do
let :pre_condition do
"include ::postgresql::server
service {'keystone':}"
end
let :title do
'keystone'
end
let :params do
{ :before => 'Service[keystone]'}.merge(required_params)
end
it { is_expected.to contain_service('keystone').that_requires("Openstacklib::Db::Postgresql[keystone]") }
end
end
on_supported_os({
:supported_os => OSDefaults.get_supported_os
}).each do |os,facts|
context "on #{os}" do
let (:facts) do
facts.merge!(OSDefaults.get_facts())
end
it_configures 'openstacklib::db::postgresql examples'
end
end
end

View File

@ -0,0 +1,98 @@
#
# Copyright (C) 2014 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.
require 'spec_helper'
describe 'openstacklib::messaging::rabbitmq' do
let (:title) { 'nova' }
shared_examples 'openstacklib::messaging::rabbitmq examples' do
let :params do
{}
end
context 'with default parameters' do
it { is_expected.to contain_rabbitmq_user('guest').with(
:admin => false,
:password => 'guest',
:provider => 'rabbitmqctl',
)}
it { is_expected.to contain_rabbitmq_user_permissions('guest@/').with(
:configure_permission => '.*',
:write_permission => '.*',
:read_permission => '.*',
:provider => 'rabbitmqctl',
)}
it { is_expected.to contain_rabbitmq_vhost('/').with(
:provider => 'rabbitmqctl',
)}
end
context 'with custom parameters' do
before :each do
params.merge!(
:userid => 'nova',
:password => 'secrete',
:virtual_host => '/nova',
:is_admin => true,
:configure_permission => '.nova',
:write_permission => '.nova',
:read_permission => '.nova'
)
end
it { is_expected.to contain_rabbitmq_user('nova').with(
:admin => true,
:password => 'secrete',
:provider => 'rabbitmqctl',
)}
it { is_expected.to contain_rabbitmq_user_permissions('nova@/nova').with(
:configure_permission => '.nova',
:write_permission => '.nova',
:read_permission => '.nova',
:provider => 'rabbitmqctl',
)}
it { is_expected.to contain_rabbitmq_vhost('/nova').with(
:provider => 'rabbitmqctl',
)}
end
context 'when disabling vhost management' do
before :each do
params.merge!( :manage_vhost => false )
end
it { is_expected.not_to contain_rabbitmq_vhost('/') }
end
end
on_supported_os({
:supported_os => OSDefaults.get_supported_os
}).each do |os,facts|
context "on #{os}" do
let (:facts) do
facts.merge!(OSDefaults.get_facts())
end
it_configures 'openstacklib::messaging::rabbitmq examples'
end
end
end

View File

@ -0,0 +1,54 @@
require 'spec_helper'
describe 'openstacklib::policy::base' do
shared_examples_for 'openstacklib::policy' do
context 'with some basic parameters' do
let :title do
'nova-contest_is_admin'
end
let :params do
{:file_path => '/etc/nova/policy.json',
:key => 'context_is_admin or owner',
:value => 'foo:bar'}
end
it 'configures (modifies) the proper policy' do
is_expected.to contain_augeas('/etc/nova/policy.json-context_is_admin or owner-foo:bar').with(
'lens' => 'Json.lns',
'incl' => '/etc/nova/policy.json',
'changes' => 'set dict/entry[*][.="context_is_admin or owner"]/string "foo:bar"',
'require' => 'Augeas[/etc/nova/policy.json-context_is_admin or owner-foo:bar-add]'
)
end
it 'configures (adds) the proper policy' do
is_expected.to contain_augeas('/etc/nova/policy.json-context_is_admin or owner-foo:bar-add').with(
'lens' => 'Json.lns',
'incl' => '/etc/nova/policy.json',
'changes' => [
'set dict/entry[last()+1] "context_is_admin or owner"',
'set dict/entry[last()]/string "foo:bar"'
],
'onlyif' => 'match dict/entry[*][.="context_is_admin or owner"] size == 0'
)
end
end
end
on_supported_os({
:supported_os => OSDefaults.get_supported_os
}).each do |os,facts|
context "on #{os}" do
let (:facts) do
facts.merge!(OSDefaults.get_facts())
end
it_configures 'openstacklib::policy'
end
end
end

View File

@ -0,0 +1,117 @@
#
# Copyright (C) 2014 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.
require 'spec_helper'
describe 'openstacklib::service_validation' do
let (:title) { 'nova-api' }
let :required_params do
{ :command => 'nova list' }
end
shared_examples 'openstacklib::service_validation examples' do
context 'with only required parameters' do
let :params do
required_params
end
it { is_expected.to contain_exec("execute #{title} validation").with(
:path => '/usr/bin:/bin:/usr/sbin:/sbin',
:provider => 'shell',
:command => 'nova list',
:refreshonly => false,
:timeout => '60',
:tries => '10',
:try_sleep => '2',
:logoutput => 'on_failure',
)}
it { is_expected.to contain_anchor("create #{title} anchor").with(
:require => "Exec[execute #{title} validation]",
)}
end
context 'with unless parameter' do
let :params do
required_params.merge!({ :unless => 'pwd' })
end
it { is_expected.to contain_exec("execute #{title} validation").with(
:path => '/usr/bin:/bin:/usr/sbin:/sbin',
:provider => 'shell',
:command => 'nova list',
:refreshonly => false,
:timeout => '60',
:tries => '10',
:try_sleep => '2',
:unless => 'pwd',
)}
it { is_expected.to contain_anchor("create #{title} anchor").with(
:require => "Exec[execute #{title} validation]",
)}
end
context 'with onlyif parameter' do
let :params do
required_params.merge!({:onlyif => 'pwd' })
end
it { is_expected.to contain_exec("execute #{title} validation").with(
:path => '/usr/bin:/bin:/usr/sbin:/sbin',
:provider => 'shell',
:command => 'nova list',
:refreshonly => false,
:timeout => '60',
:tries => '10',
:try_sleep => '2',
:onlyif => 'pwd',
)}
it { is_expected.to contain_anchor("create #{title} anchor").with(
:require => "Exec[execute #{title} validation]",
)}
end
context 'when omitting a required parameter command' do
let :params do
required_params.delete(:command)
end
it { expect { is_expected.to raise_error(Puppet::Error) } }
end
end
on_supported_os({
:supported_os => OSDefaults.get_supported_os
}).each do |os,facts|
context "on #{os}" do
let (:facts) do
facts.merge!(OSDefaults.get_facts())
end
it_configures 'openstacklib::service_validation examples'
end
end
end

View File

@ -0,0 +1,161 @@
#
# Copyright (C) 2014 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.
require 'spec_helper'
describe 'openstacklib::wsgi::apache' do
let (:title) { 'keystone_wsgi' }
let :global_facts do
{
:os_workers => 8,
:concat_basedir => '/var/lib/puppet/concat',
:fqdn => 'some.host.tld'
}
end
let :params do
{
:bind_port => 5000,
:group => 'keystone',
:ssl => true,
:user => 'keystone',
:wsgi_script_dir => '/var/www/cgi-bin/keystone',
:wsgi_script_file => 'main',
:wsgi_script_source => '/usr/share/keystone/keystone.wsgi'
}
end
shared_examples_for 'apache serving a service with mod_wsgi' do
it { is_expected.to contain_service('httpd').with_name(platform_params[:httpd_service_name]) }
it { is_expected.to contain_class('apache') }
it { is_expected.to contain_class('apache::mod::wsgi') }
describe 'with default parameters' do
it { is_expected.to contain_file('/var/www/cgi-bin/keystone').with(
'ensure' => 'directory',
'owner' => 'keystone',
'group' => 'keystone',
'require' => 'Package[httpd]'
)}
it { is_expected.to contain_file('keystone_wsgi').with(
'ensure' => 'file',
'path' => '/var/www/cgi-bin/keystone/main',
'source' => '/usr/share/keystone/keystone.wsgi',
'owner' => 'keystone',
'group' => 'keystone',
'mode' => '0644',
)}
it { is_expected.to contain_apache__vhost('keystone_wsgi').with(
'servername' => 'some.host.tld',
'ip' => nil,
'port' => '5000',
'docroot' => '/var/www/cgi-bin/keystone',
'docroot_owner' => 'keystone',
'docroot_group' => 'keystone',
'ssl' => 'true',
'wsgi_daemon_process' => 'keystone_wsgi',
'wsgi_process_group' => 'keystone_wsgi',
'wsgi_script_aliases' => { '/' => "/var/www/cgi-bin/keystone/main" },
'wsgi_daemon_process_options' => {
'user' => 'keystone',
'group' => 'keystone',
'processes' => 1,
'threads' => global_facts[:os_workers],
'display-name' => 'keystone_wsgi',
},
'wsgi_application_group' => '%{GLOBAL}',
'require' => 'File[keystone_wsgi]',
'setenvif' => ['X-Forwarded-Proto https HTTPS=1']
)}
it { is_expected.to contain_concat("#{platform_params[:httpd_ports_file]}") }
end
describe 'when overriding parameters' do
let :params do
{
:wsgi_script_dir => '/var/www/cgi-bin/keystone',
:wsgi_script_file => 'main',
:wsgi_script_source => '/usr/share/keystone/keystone.wsgi',
:wsgi_pass_authorization => 'On',
:wsgi_chunked_request => 'On',
:servername => 'dummy.host',
:bind_host => '10.42.51.1',
:bind_port => 4142,
:user => 'keystone',
:group => 'keystone',
:ssl => false,
:workers => 37,
:vhost_custom_fragment => 'LimitRequestFieldSize 81900',
:allow_encoded_slashes => 'on',
}
end
it { is_expected.to contain_apache__vhost('keystone_wsgi').with(
'servername' => 'dummy.host',
'ip' => '10.42.51.1',
'port' => '4142',
'docroot' => "/var/www/cgi-bin/keystone",
'ssl' => 'false',
'wsgi_daemon_process' => 'keystone_wsgi',
'wsgi_daemon_process_options' => {
'user' => 'keystone',
'group' => 'keystone',
'processes' => '37',
'threads' => '8',
'display-name' => 'keystone_wsgi',
},
'wsgi_process_group' => 'keystone_wsgi',
'wsgi_script_aliases' => { '/' => "/var/www/cgi-bin/keystone/main" },
'wsgi_application_group' => '%{GLOBAL}',
'wsgi_pass_authorization' => 'On',
'wsgi_chunked_request' => 'On',
'require' => 'File[keystone_wsgi]',
'custom_fragment' => 'LimitRequestFieldSize 81900',
'allow_encoded_slashes' => 'on',
)}
end
end
on_supported_os({
:supported_os => OSDefaults.get_supported_os
}).each do |os,facts|
context "on #{os}" do
let (:facts) do
facts.merge!(OSDefaults.get_facts(global_facts))
end
let(:platform_params) do
case facts[:osfamily]
when 'Debian'
{ :httpd_service_name => 'apache2',
:httpd_ports_file => '/etc/apache2/ports.conf', }
when 'RedHat'
{ :httpd_service_name => 'httpd',
:httpd_ports_file => '/etc/httpd/conf/ports.conf', }
end
end
it_configures 'apache serving a service with mod_wsgi'
end
end
end

View File

@ -0,0 +1,40 @@
require 'spec_helper'
describe 'is_service_default' do
it 'refuses without at least one argument' do
is_expected.to run.with_params().\
and_raise_error(Puppet::ParseError, /Wrong number of arguments/)
end
it 'refuses too many arguments' do
is_expected.to run.with_params('foo', 'bar').\
and_raise_error(Puppet::ParseError, /Wrong number of arguments/)
end
context 'is_service_default' do
it 'with <SERVICE DEFAULT>' do
is_expected.to run.with_params('<SERVICE DEFAULT>').and_return(true)
end
it 'with string != <SERVICE DEFAULT>' do
is_expected.to run.with_params('a value').and_return(false)
end
it 'with array' do
is_expected.to run.with_params([1,2,3]).and_return(false)
end
it 'with hash' do
is_expected.to run.with_params({'foo' => 'bar'}).and_return(false)
end
it 'with integer' do
is_expected.to run.with_params(1234).and_return(false)
end
it 'with boolean' do
is_expected.to run.with_params(false).and_return(false)
end
end
end

View File

@ -0,0 +1,15 @@
require 'spec_helper'
describe 'normalize_ip_for_uri' do
it { should run.with_params(false).and_return(false)}
it { should run.with_params('not_an_ip').and_return('not_an_ip')}
it { should run.with_params('127.0.0.1').and_return('127.0.0.1')}
it { should run.with_params('::1').and_return('[::1]')}
it { should run.with_params('[2001::01]').and_return('[2001::01]')}
# You're not forced to pass an array, a list of argument will do.
it { should run.with_params('::1','::2').and_return(['[::1]','[::2]'])}
it { should run.with_params(['::1','::2']).and_return(['[::1]','[::2]'])}
it { should run.with_params(['::1','[::2]','::3']).and_return(['[::1]','[::2]','[::3]'])}
it { should run.with_params(['192.168.0.1','[::2]']).and_return(['192.168.0.1','[::2]'])}
it { should run.with_params(['192.168.0.1','::2']).and_return(['192.168.0.1','[::2]'])}
end

View File

@ -0,0 +1,157 @@
require 'spec_helper'
describe 'os_database_connection' do
it 'refuses String' do
is_expected.to run.with_params('foo').\
and_raise_error(Puppet::ParseError, /Requires an hash/)
end
it 'refuses Array' do
is_expected.to run.with_params(['foo']).\
and_raise_error(Puppet::ParseError, /Requires an hash/)
end
it 'refuses without at least one argument' do
is_expected.to run.with_params().\
and_raise_error(Puppet::ParseError, /Wrong number of arguments/)
end
it 'refuses too many arguments' do
is_expected.to run.with_params('foo', 'bar').\
and_raise_error(Puppet::ParseError, /Wrong number of arguments/)
end
it 'refuses extra params passed as String' do
is_expected.to run.with_params({
'dialect' => 'sqlite',
'database' => '/var/lib/keystone/keystone.db',
'host' => '127.0.0.1',
'port' => '3306',
'extra' => 'charset=utf-8'
}).and_raise_error(Puppet::ParseError, /extra should be a Hash/)
end
it 'fails if port is provided with missing host' do
is_expected.to run.with_params({
'dialect' => 'sqlite',
'database' => '/var/lib/keystone/keystone.db',
'port' => '3306',
'extra' => { 'charset' => 'utf-8' }
}).and_raise_error(Puppet::ParseError, /host is required with port/)
end
context 'creates the correct connection URI' do
it 'with all parameters' do
is_expected.to run.with_params({
'dialect' => 'mysql',
'host' => '127.0.0.1',
'port' => '3306',
'database' => 'test',
'username' => 'guest',
'password' => 's3cr3t',
'extra' => { 'charset' => 'utf-8', 'read_timeout' => '60' }
}).and_return('mysql://guest:s3cr3t@127.0.0.1:3306/test?charset=utf-8&read_timeout=60')
end
it 'with all parameters and charset set' do
is_expected.to run.with_params({
'dialect' => 'mysql',
'host' => '127.0.0.1',
'port' => '3306',
'database' => 'test',
'username' => 'guest',
'password' => 's3cr3t',
'charset' => 'utf-8',
'extra' => { 'charset' => 'latin1', 'read_timeout' => '60' }
}).and_return('mysql://guest:s3cr3t@127.0.0.1:3306/test?charset=utf-8&read_timeout=60')
end
it 'without port' do
is_expected.to run.with_params({
'dialect' => 'mysql',
'host' => '127.0.0.1',
'database' => 'test',
'username' => 'guest',
'password' => 's3cr3t',
'extra' => { 'charset' => 'utf-8' }
}).and_return('mysql://guest:s3cr3t@127.0.0.1/test?charset=utf-8')
end
it 'without host and port' do
is_expected.to run.with_params({
'dialect' => 'sqlite',
'database' => '/var/lib/keystone/keystone.db',
'extra' => { 'charset' => 'utf-8' }
}).and_return('sqlite:////var/lib/keystone/keystone.db?charset=utf-8')
end
it 'without username and password' do
is_expected.to run.with_params({
'dialect' => 'mysql',
'host' => '127.0.0.1',
'port' => '3306',
'database' => 'test',
'extra' => { 'charset' => 'utf-8' }
}).and_return('mysql://127.0.0.1:3306/test?charset=utf-8')
end
it 'with username set to undef' do
is_expected.to run.with_params({
'dialect' => 'mysql',
'host' => '127.0.0.1',
'port' => '3306',
'database' => 'test',
'username' => :undef,
'extra' => { 'charset' => 'utf-8' }
}).and_return('mysql://127.0.0.1:3306/test?charset=utf-8')
end
it 'with username set to an empty string' do
is_expected.to run.with_params({
'dialect' => 'mysql',
'host' => '127.0.0.1',
'port' => '3306',
'database' => 'test',
'username' => '',
'extra' => { 'charset' => 'utf-8' }
}).and_return('mysql://127.0.0.1:3306/test?charset=utf-8')
end
it 'without password' do
is_expected.to run.with_params({
'dialect' => 'mysql',
'host' => '127.0.0.1',
'port' => '3306',
'database' => 'test',
'username' => 'guest',
'extra' => { 'charset' => 'utf-8' }
}).and_return('mysql://guest@127.0.0.1:3306/test?charset=utf-8')
end
it 'with password set to undef' do
is_expected.to run.with_params({
'dialect' => 'mysql',
'host' => '127.0.0.1',
'port' => '3306',
'database' => 'test',
'username' => 'guest',
'password' => :undef,
'extra' => { 'charset' => 'utf-8' }
}).and_return('mysql://guest@127.0.0.1:3306/test?charset=utf-8')
end
it 'with password set to an empty string' do
is_expected.to run.with_params({
'dialect' => 'mysql',
'host' => '127.0.0.1',
'port' => '3306',
'database' => 'test',
'username' => 'guest',
'password' => '',
'extra' => { 'charset' => 'utf-8' }
}).and_return('mysql://guest@127.0.0.1:3306/test?charset=utf-8')
end
end
end

View File

@ -0,0 +1,324 @@
require 'spec_helper'
describe 'os_transport_url' do
it 'refuses String' do
is_expected.to run.with_params('foo').\
and_raise_error(Puppet::ParseError, /Requires an hash/)
end
it 'refuses Array' do
is_expected.to run.with_params(['foo']).\
and_raise_error(Puppet::ParseError, /Requires an hash/)
end
it 'refuses without at least one argument' do
is_expected.to run.with_params().\
and_raise_error(ArgumentError, /Wrong number of arguments/)
end
it 'refuses too many arguments' do
is_expected.to run.with_params('foo', 'bar').\
and_raise_error(ArgumentError, /Wrong number of arguments/)
end
it 'refuses hosts params passed as String' do
is_expected.to run.with_params({
'transport'=> 'rabbit',
'hosts' => '127.0.0.1',
}).and_raise_error(Puppet::ParseError, /hosts should be a Array/)
end
it 'fails if missing host' do
is_expected.to run.with_params({
'transport'=> 'rabbit',
}).and_raise_error(Puppet::ParseError, /host or hosts is required/)
end
context 'creates the correct transport URI' do
it 'with a single host array for hosts' do
is_expected.to run.with_params({
'transport' => 'rabbit',
'hosts' => [ '127.0.0.1' ],
'port' => '5672',
'username' => 'guest',
'password' => 's3cr3t',
'virtual_host' => 'virt',
'ssl' => '1',
'query' => { 'read_timeout' => '60' },
}).and_return('rabbit://guest:s3cr3t@127.0.0.1:5672/virt?read_timeout=60&ssl=1')
end
it 'with a single host array for hosts and integer port' do
is_expected.to run.with_params({
'transport' => 'rabbit',
'hosts' => [ '127.0.0.1' ],
'port' => 5672,
'username' => 'guest',
'password' => 's3cr3t',
'virtual_host' => 'virt',
'ssl' => '1',
'query' => { 'read_timeout' => '60' },
}).and_return('rabbit://guest:s3cr3t@127.0.0.1:5672/virt?read_timeout=60&ssl=1')
end
it 'with all params for a single host' do
is_expected.to run.with_params({
'transport' => 'rabbit',
'host' => '127.0.0.1',
'port' => '5672',
'username' => 'guest',
'password' => 's3cr3t',
'virtual_host' => 'virt',
'ssl' => '1',
'query' => { 'read_timeout' => '60' },
}).and_return('rabbit://guest:s3cr3t@127.0.0.1:5672/virt?read_timeout=60&ssl=1')
end
it 'with all params for a single host and integer port' do
is_expected.to run.with_params({
'transport' => 'rabbit',
'host' => '127.0.0.1',
'port' => 5672,
'username' => 'guest',
'password' => 's3cr3t',
'virtual_host' => 'virt',
'ssl' => '1',
'query' => { 'read_timeout' => '60' },
}).and_return('rabbit://guest:s3cr3t@127.0.0.1:5672/virt?read_timeout=60&ssl=1')
end
it 'with only required params for a single host' do
is_expected.to run.with_params({
'transport' => 'rabbit',
'host' => '127.0.0.1',
}).and_return('rabbit://127.0.0.1/')
end
it 'with a single ipv6 address' do
is_expected.to run.with_params({
'transport' => 'rabbit',
'host' => 'fe80::ca5b:76ff:fe4b:be3b',
'port' => '5672',
}).and_return('rabbit://[fe80::ca5b:76ff:fe4b:be3b]:5672/')
end
it 'with a single ipv6 address and integer port' do
is_expected.to run.with_params({
'transport' => 'rabbit',
'host' => 'fe80::ca5b:76ff:fe4b:be3b',
'port' => 5672,
}).and_return('rabbit://[fe80::ca5b:76ff:fe4b:be3b]:5672/')
end
it 'with all params with multiple hosts' do
is_expected.to run.with_params({
'transport' => 'rabbit',
'hosts' => ['1.1.1.1', '2.2.2.2'],
'port' => '5672',
'username' => 'guest',
'password' => 's3cr3t',
'virtual_host' => 'virt',
'query' => { 'read_timeout' => '60' },
}).and_return('rabbit://guest:s3cr3t@1.1.1.1:5672,guest:s3cr3t@2.2.2.2:5672/virt?read_timeout=60')
end
it 'with all params with multiple hosts and integer port' do
is_expected.to run.with_params({
'transport' => 'rabbit',
'hosts' => ['1.1.1.1', '2.2.2.2'],
'port' => 5672,
'username' => 'guest',
'password' => 's3cr3t',
'virtual_host' => 'virt',
'query' => { 'read_timeout' => '60' },
}).and_return('rabbit://guest:s3cr3t@1.1.1.1:5672,guest:s3cr3t@2.2.2.2:5672/virt?read_timeout=60')
end
it 'with only required params for multiple hosts' do
is_expected.to run.with_params({
'transport' => 'rabbit',
'hosts' => [ '1.1.1.1', '2.2.2.2' ],
'port' => '5672',
'username' => 'guest',
'password' => 's3cr3t',
}).and_return('rabbit://guest:s3cr3t@1.1.1.1:5672,guest:s3cr3t@2.2.2.2:5672/')
end
it 'with multiple ipv6 hosts' do
is_expected.to run.with_params({
'transport' => 'rabbit',
'hosts' => [ 'fe80::ca5b:76ff:fe4b:be3b', 'fe80::ca5b:76ff:fe4b:be3c' ],
'port' => '5672',
'username' => 'guest',
'password' => 's3cr3t',
}).and_return('rabbit://guest:s3cr3t@[fe80::ca5b:76ff:fe4b:be3b]:5672,guest:s3cr3t@[fe80::ca5b:76ff:fe4b:be3c]:5672/')
end
it 'with a mix of ipv4 and ipv6 hosts' do
is_expected.to run.with_params({
'transport' => 'rabbit',
'hosts' => [ 'fe80::ca5b:76ff:fe4b:be3b', '1.1.1.1' ],
'port' => '5672',
'username' => 'guest',
'password' => 's3cr3t',
}).and_return('rabbit://guest:s3cr3t@[fe80::ca5b:76ff:fe4b:be3b]:5672,guest:s3cr3t@1.1.1.1:5672/')
end
it 'without port' do
is_expected.to run.with_params({
'transport' => 'rabbit',
'host' => '127.0.0.1',
'username' => 'guest',
'password' => 's3cr3t',
'virtual_host' => 'virt',
'query' => { 'read_timeout' => '60' },
}).and_return('rabbit://guest:s3cr3t@127.0.0.1/virt?read_timeout=60')
end
it 'without port and query' do
is_expected.to run.with_params({
'transport' => 'rabbit',
'host' => '127.0.0.1',
'username' => 'guest',
'password' => 's3cr3t',
'virtual_host' => 'virt',
}).and_return('rabbit://guest:s3cr3t@127.0.0.1/virt')
end
it 'without username and password' do
is_expected.to run.with_params({
'transport' => 'rabbit',
'host' => '127.0.0.1',
'port' => '5672',
'virtual_host' => 'virt',
'query' => { 'read_timeout' => '60' },
}).and_return('rabbit://127.0.0.1:5672/virt?read_timeout=60')
end
it 'with username set to undef' do
is_expected.to run.with_params({
'transport' => 'rabbit',
'host' => '127.0.0.1',
'port' => '5672',
'username' => :undef,
'virtual_host' => 'virt',
'query' => { 'read_timeout' => '60' },
}).and_return('rabbit://127.0.0.1:5672/virt?read_timeout=60')
end
it 'with username set to an empty string' do
is_expected.to run.with_params({
'transport' => 'rabbit',
'host' => '127.0.0.1',
'port' => '5672',
'username' => '',
'virtual_host' => 'virt',
'query' => { 'read_timeout' => '60' },
}).and_return('rabbit://127.0.0.1:5672/virt?read_timeout=60')
end
it 'without password' do
is_expected.to run.with_params({
'transport' => 'rabbit',
'host' => '127.0.0.1',
'port' => '5672',
'username' => 'guest',
'virtual_host' => 'virt',
'query' => { 'read_timeout' => '60' },
}).and_return('rabbit://guest@127.0.0.1:5672/virt?read_timeout=60')
end
it 'with password set to undef' do
is_expected.to run.with_params({
'transport' => 'rabbit',
'host' => '127.0.0.1',
'port' => '5672',
'username' => 'guest',
'password' => :undef,
'virtual_host' => 'virt',
'query' => { 'read_timeout' => '60' },
}).and_return('rabbit://guest@127.0.0.1:5672/virt?read_timeout=60')
end
it 'with password set to an empty string' do
is_expected.to run.with_params({
'transport' => 'rabbit',
'host' => '127.0.0.1',
'port' => '5672',
'username' => 'guest',
'password' => '',
'virtual_host' => 'virt',
'query' => { 'read_timeout' => '60' },
}).and_return('rabbit://guest@127.0.0.1:5672/virt?read_timeout=60')
end
it 'with ssl overrides ssl in quert hash' do
is_expected.to run.with_params({
'transport' => 'rabbit',
'host' => '127.0.0.1',
'port' => '5672',
'username' => 'guest',
'password' => '',
'virtual_host' => 'virt',
'ssl' => '1',
'query' => { 'read_timeout' => '60' , 'ssl' => '0'},
}).and_return('rabbit://guest@127.0.0.1:5672/virt?read_timeout=60&ssl=1')
end
it 'with ssl as boolean string' do
is_expected.to run.with_params({
'transport' => 'rabbit',
'host' => '127.0.0.1',
'port' => '5672',
'username' => 'guest',
'password' => '',
'virtual_host' => 'virt',
'ssl' => 'true',
'query' => { 'read_timeout' => '60' },
}).and_return('rabbit://guest@127.0.0.1:5672/virt?read_timeout=60&ssl=1')
is_expected.to run.with_params({
'transport' => 'rabbit',
'host' => '127.0.0.1',
'port' => '5672',
'username' => 'guest',
'password' => '',
'virtual_host' => 'virt',
'ssl' => 'false',
'query' => { 'read_timeout' => '60' },
}).and_return('rabbit://guest@127.0.0.1:5672/virt?read_timeout=60&ssl=0')
is_expected.to run.with_params({
'transport' => 'rabbit',
'host' => '127.0.0.1',
'port' => '5672',
'username' => 'guest',
'password' => '',
'virtual_host' => 'virt',
'ssl' => 'True',
'query' => { 'read_timeout' => '60' },
}).and_return('rabbit://guest@127.0.0.1:5672/virt?read_timeout=60&ssl=1')
is_expected.to run.with_params({
'transport' => 'rabbit',
'host' => '127.0.0.1',
'port' => '5672',
'username' => 'guest',
'password' => '',
'virtual_host' => 'virt',
'ssl' => 'False',
'query' => { 'read_timeout' => '60' },
}).and_return('rabbit://guest@127.0.0.1:5672/virt?read_timeout=60&ssl=0')
end
it 'with alternative transport and single host array for hosts' do
is_expected.to run.with_params({
'transport' => 'amqp',
'hosts' => [ '127.0.0.1' ],
'port' => '5672',
'username' => 'guest',
'password' => 's3cr3t',
}).and_return('amqp://guest:s3cr3t@127.0.0.1:5672/')
end
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,10 @@
require 'puppetlabs_spec_helper/module_spec_helper'
require 'shared_examples'
require 'puppet-openstack_spec_helper/facts'
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
at_exit { RSpec::Puppet::Coverage.report! }

View File

@ -0,0 +1 @@
require 'puppet-openstack_spec_helper/beaker_spec_helper'

View File

@ -0,0 +1,241 @@
require 'puppet'
require 'spec_helper'
require 'puppet/provider/openstack'
require 'puppet/provider/openstack/auth'
require 'tempfile'
class Puppet::Provider::Openstack::AuthTester < Puppet::Provider::Openstack
extend Puppet::Provider::Openstack::Auth
end
klass = Puppet::Provider::Openstack::AuthTester
describe Puppet::Provider::Openstack::Auth do
let(:type) do
Puppet::Type.newtype(:test_resource) do
newparam(:name, :namevar => true)
newparam(:log_file)
end
end
let(:resource_attrs) do
{
:name => 'stubresource'
}
end
let(:provider) do
klass.new(type.new(resource_attrs))
end
before(:each) do
ENV['OS_USERNAME'] = nil
ENV['OS_PASSWORD'] = nil
ENV['OS_PROJECT_NAME'] = nil
ENV['OS_AUTH_URL'] = nil
ENV['OS_TOKEN'] = nil
ENV['OS_URL'] = nil
end
describe '#set_credentials' do
it 'adds keys to the object' do
credentials = Puppet::Provider::Openstack::CredentialsV2_0.new
set = { 'OS_USERNAME' => 'user',
'OS_PASSWORD' => 'secret',
'OS_PROJECT_NAME' => 'tenant',
'OS_AUTH_URL' => 'http://127.0.0.1:5000',
'OS_TOKEN' => 'token',
'OS_URL' => 'http://127.0.0.1:35357',
'OS_IDENTITY_API_VERSION' => '2.0',
'OS_NOT_VALID' => 'notvalid'
}
klass.set_credentials(credentials, set)
expect(credentials.to_env).to eq(
"OS_AUTH_URL" => "http://127.0.0.1:5000",
"OS_IDENTITY_API_VERSION" => '2.0',
"OS_PASSWORD" => "secret",
"OS_PROJECT_NAME" => "tenant",
"OS_TOKEN" => "token",
"OS_URL" => "http://127.0.0.1:35357",
"OS_USERNAME" => "user")
end
end
describe '#rc_filename' do
it 'returns RCFILENAME' do
expect(klass.rc_filename).to eq("#{ENV['HOME']}/openrc")
end
end
describe '#get_os_from_env' do
context 'with Openstack environment variables set' do
it 'provides a hash' do
ENV['OS_AUTH_URL'] = 'http://127.0.0.1:5000'
ENV['OS_PASSWORD'] = 'abc123'
ENV['OS_PROJECT_NAME'] = 'test'
ENV['OS_USERNAME'] = 'test'
response = klass.get_os_vars_from_env
expect(response).to eq({
"OS_AUTH_URL" => "http://127.0.0.1:5000",
"OS_PASSWORD" => "abc123",
"OS_PROJECT_NAME" => "test",
"OS_USERNAME" => "test"})
end
end
end
describe '#get_os_vars_from_rcfile' do
context 'with a valid RC file' do
it 'provides a hash' do
mock = "export OS_USERNAME='test'\nexport OS_PASSWORD='abc123'\nexport OS_PROJECT_NAME='test'\nexport OS_AUTH_URL='http://127.0.0.1:5000'"
filename = 'file'
File.expects(:exists?).with('file').returns(true)
File.expects(:open).with('file').returns(StringIO.new(mock))
response = klass.get_os_vars_from_rcfile(filename)
expect(response).to eq({
"OS_AUTH_URL" => "http://127.0.0.1:5000",
"OS_PASSWORD" => "abc123",
"OS_PROJECT_NAME" => "test",
"OS_USERNAME" => "test"})
end
end
context 'with an empty file' do
it 'provides an empty hash' do
filename = 'file'
File.expects(:exists?).with(filename).returns(true)
File.expects(:open).with(filename).returns(StringIO.new(""))
response = klass.get_os_vars_from_rcfile(filename)
expect(response).to eq({})
end
end
context 'with a nonexistent file' do
it 'should get default rcfile when no environment or openrc file' do
ENV.clear
mock = "export OS_USERNAME='user'\nexport OS_PASSWORD='secret'\nexport OS_PROJECT_NAME='project'\nexport OS_AUTH_URL='http://127.0.0.1:5000'"
filename = '/root/openrc'
File.expects(:exists?).with("#{ENV['HOME']}/openrc").returns(false)
File.expects(:exists?).with(filename).returns(true)
File.expects(:open).with(filename).returns(StringIO.new(mock))
expect(klass.get_os_vars_from_rcfile("#{ENV['HOME']}/openrc")).to eq({
'OS_USERNAME' => 'user',
'OS_PASSWORD' => 'secret',
'OS_PROJECT_NAME' => 'project',
'OS_AUTH_URL' => 'http://127.0.0.1:5000'
})
end
end
end
before(:each) do
class Puppet::Provider::Openstack::AuthTester
@credentials = Puppet::Provider::Openstack::CredentialsV2_0.new
end
end
describe '#request' do
context 'with no valid credentials' do
it 'fails to authenticate' do
expect { klass.request('project', 'list', ['--long']) }.to raise_error(Puppet::Error::OpenstackAuthInputError, "Insufficient credentials to authenticate")
expect(klass.instance_variable_get(:@credentials).to_env).to eq({})
end
end
context 'with user credentials in env' do
it 'is successful' do
klass.expects(:get_os_vars_from_env)
.returns({ 'OS_USERNAME' => 'test',
'OS_PASSWORD' => 'abc123',
'OS_PROJECT_NAME' => 'test',
'OS_AUTH_URL' => 'http://127.0.0.1:5000',
'OS_NOT_VALID' => 'notvalid' })
klass.expects(:openstack)
.with('project', 'list', '--quiet', '--format', 'csv', ['--long'])
.returns('"ID","Name","Description","Enabled"
"1cb05cfed7c24279be884ba4f6520262","test","Test tenant",True
')
response = klass.request('project', 'list', ['--long'])
expect(response.first[:description]).to eq("Test tenant")
expect(klass.instance_variable_get(:@credentials).to_env).to eq({
'OS_USERNAME' => 'test',
'OS_PASSWORD' => 'abc123',
'OS_PROJECT_NAME' => 'test',
'OS_AUTH_URL' => 'http://127.0.0.1:5000'
})
end
end
context 'with service token credentials in env' do
it 'is successful' do
klass.expects(:get_os_vars_from_env)
.returns({ 'OS_TOKEN' => 'test',
'OS_URL' => 'http://127.0.0.1:5000',
'OS_NOT_VALID' => 'notvalid' })
klass.expects(:openstack)
.with('project', 'list', '--quiet', '--format', 'csv', ['--long'])
.returns('"ID","Name","Description","Enabled"
"1cb05cfed7c24279be884ba4f6520262","test","Test tenant",True
')
response = klass.request('project', 'list', ['--long'])
expect(response.first[:description]).to eq("Test tenant")
expect(klass.instance_variable_get(:@credentials).to_env).to eq({
'OS_TOKEN' => 'test',
'OS_URL' => 'http://127.0.0.1:5000',
})
end
end
context 'with a RC file containing user credentials' do
it 'is successful' do
# return incomplete creds from env
klass.expects(:get_os_vars_from_env)
.returns({ 'OS_USERNAME' => 'incompleteusername',
'OS_AUTH_URL' => 'incompleteauthurl' })
mock = "export OS_USERNAME='test'\nexport OS_PASSWORD='abc123'\nexport OS_PROJECT_NAME='test'\nexport OS_AUTH_URL='http://127.0.0.1:5000'\nexport OS_NOT_VALID='notvalid'"
File.expects(:exists?).with("#{ENV['HOME']}/openrc").returns(true)
File.expects(:open).with("#{ENV['HOME']}/openrc").returns(StringIO.new(mock))
klass.expects(:openstack)
.with('project', 'list', '--quiet', '--format', 'csv', ['--long'])
.returns('"ID","Name","Description","Enabled"
"1cb05cfed7c24279be884ba4f6520262","test","Test tenant",True
')
response = provider.class.request('project', 'list', ['--long'])
expect(response.first[:description]).to eq("Test tenant")
expect(klass.instance_variable_get(:@credentials).to_env).to eq({
'OS_USERNAME' => 'test',
'OS_PASSWORD' => 'abc123',
'OS_PROJECT_NAME' => 'test',
'OS_AUTH_URL' => 'http://127.0.0.1:5000'
})
end
end
context 'with a RC file containing service token credentials' do
it 'is successful' do
# return incomplete creds from env
klass.expects(:get_os_vars_from_env)
.returns({ 'OS_TOKEN' => 'incomplete' })
mock = "export OS_TOKEN='test'\nexport OS_URL='abc123'\nexport OS_NOT_VALID='notvalid'\n"
File.expects(:exists?).with("#{ENV['HOME']}/openrc").returns(true)
File.expects(:open).with("#{ENV['HOME']}/openrc").returns(StringIO.new(mock))
klass.expects(:openstack)
.with('project', 'list', '--quiet', '--format', 'csv', ['--long'])
.returns('"ID","Name","Description","Enabled"
"1cb05cfed7c24279be884ba4f6520262","test","Test tenant",True
')
response = klass.request('project', 'list', ['--long'])
expect(response.first[:description]).to eq("Test tenant")
expect(klass.instance_variable_get(:@credentials).to_env).to eq({
'OS_TOKEN' => 'test',
'OS_URL' => 'abc123',
})
end
end
end
end

View File

@ -0,0 +1,160 @@
require 'puppet'
require 'spec_helper'
require 'puppet/provider/openstack'
require 'puppet/provider/openstack/credentials'
describe Puppet::Provider::Openstack::Credentials do
let(:creds) do
creds = Puppet::Provider::Openstack::CredentialsV2_0.new
end
describe "#set with valid value" do
it 'works with valid value' do
expect(creds.class.defined?('auth_url')).to be_truthy
creds.set('auth_url', 'http://localhost:5000/v2.0')
expect(creds.auth_url).to eq('http://localhost:5000/v2.0')
end
end
describe "#set with invalid value" do
it 'works with invalid value' do
expect(creds.class.defined?('foo')).to be_falsey
creds.set('foo', 'junk')
expect(creds.respond_to?(:foo)).to be_falsey
expect(creds.instance_variable_defined?(:@foo)).to be_falsey
expect { creds.foo }.to raise_error(NoMethodError, /undefined method/)
end
end
describe '#service_token_set?' do
context "with service credentials" do
it 'is successful' do
creds.token = 'token'
creds.url = 'url'
expect(creds.service_token_set?).to be_truthy
expect(creds.user_password_set?).to be_falsey
end
it 'fails' do
creds.token = 'token'
expect(creds.service_token_set?).to be_falsey
expect(creds.user_password_set?).to be_falsey
end
end
end
describe '#password_set?' do
context "with user credentials" do
it 'is successful' do
creds.auth_url = 'auth_url'
creds.password = 'password'
creds.project_name = 'project_name'
creds.username = 'username'
expect(creds.user_password_set?).to be_truthy
expect(creds.service_token_set?).to be_falsey
end
it 'fails' do
creds.auth_url = 'auth_url'
creds.password = 'password'
creds.project_name = 'project_name'
expect(creds.user_password_set?).to be_falsey
expect(creds.service_token_set?).to be_falsey
end
end
end
describe '#set?' do
context "without any credential" do
it 'fails' do
expect(creds.set?).to be_falsey
end
end
end
describe '#version' do
it 'is version 2' do
expect(creds.version).to eq('2.0')
end
end
describe '#unset' do
context "with all instance variables set" do
it 'resets all but the identity_api_version' do
creds.auth_url = 'auth_url'
creds.password = 'password'
creds.project_name = 'project_name'
creds.username = 'username'
creds.token = 'token'
creds.url = 'url'
creds.identity_api_version = 'identity_api_version'
creds.unset
expect(creds.auth_url).to eq('')
expect(creds.password).to eq('')
expect(creds.project_name).to eq('')
expect(creds.username).to eq('')
expect(creds.token).to eq('')
expect(creds.url).to eq('')
expect(creds.identity_api_version).to eq('identity_api_version')
newcreds = Puppet::Provider::Openstack::CredentialsV3.new
expect(newcreds.identity_api_version).to eq('3')
end
end
end
describe '#to_env' do
context "with an exhaustive data set" do
it 'successfully returns content' do
creds.auth_url = 'auth_url'
creds.password = 'password'
creds.project_name = 'project_name'
creds.username = 'username'
creds.token = 'token'
creds.url = 'url'
creds.identity_api_version = 'identity_api_version'
creds.region_name = 'Region1'
expect(creds.to_env).to eq({
'OS_USERNAME' => 'username',
'OS_PASSWORD' => 'password',
'OS_PROJECT_NAME' => 'project_name',
'OS_AUTH_URL' => 'auth_url',
'OS_TOKEN' => 'token',
'OS_URL' => 'url',
'OS_IDENTITY_API_VERSION' => 'identity_api_version',
'OS_REGION_NAME' => 'Region1',
})
end
end
end
describe 'using v3' do
let(:creds) do
creds = Puppet::Provider::Openstack::CredentialsV3.new
end
describe 'with v3' do
it 'uses v3 identity api' do
creds.identity_api_version == '3'
end
end
describe '#password_set? with username and project_name' do
it 'is successful' do
creds.auth_url = 'auth_url'
creds.password = 'password'
creds.project_name = 'project_name'
creds.username = 'username'
expect(creds.user_password_set?).to be_truthy
end
end
describe '#password_set? with user_id and project_id' do
it 'is successful' do
creds.auth_url = 'auth_url'
creds.password = 'password'
creds.project_id = 'projid'
creds.user_id = 'userid'
expect(creds.user_password_set?).to be_truthy
end
end
end
end

Some files were not shown because too many files have changed in this diff Show More