Add db::mysql and db::mysql::host_access to openstacklib

The openstacklib::db::mysql resource is a library resource that can be used by
nova, cinder, ceilometer, etc., rather than replicating equivalent
functionality across all of these modules.

This resource reimplements most of the functionality of the puppetlabs
mysql::db resource. The primary purpose of writing this code from scratch
rather than using the mysql::db resource is to allow the use of a password
hash rather than a plaintext password as a parameter. Other differences from
the mysql::db implementation are:

* It does not have an ensure parameter, we will assume the db should be present
* It does not accept and execute arbitrary SQL because the db sync exec manages
  the state of the db
* It does not use ensure_resource because the database and user should only be
  created from within this resource and creating them elsewhere should be an
  error

Implements: blueprint commmon-openstack-database-resource

Change-Id: I76bd93d1579179932d1f48cea4bb80a2576a7fba
This commit is contained in:
Colleen Murphy 2014-07-07 14:03:47 -07:00
parent e72605f500
commit dd25406b9e
6 changed files with 444 additions and 2 deletions

View File

@ -1,3 +1,6 @@
fixtures:
repositories:
mysql: git://github.com/puppetlabs/puppetlabs-mysql.git
stdlib: git://github.com/puppetlabs/puppetlabs-stdlib.git
symlinks:
'openstacklib': "#{source_dir}"

View File

@ -6,3 +6,6 @@ license 'Apache License 2.0'
summary 'Puppet Labs OpenStackLib Module'
description 'Puppet module library to expose common functionality between OpenStack modules'
project_page 'https://launchpad.net/puppet-openstacklib'
dependency 'puppetlabs/mysql', '>=2.2.0 <3.0.0'
dependency 'puppetlabs/stdlib', '>=3.2.0'

View File

@ -39,9 +39,85 @@ Setup
example% puppet module install puppetlabs/openstacklib
### Beginning with openstacklib
Usage
-----
Instructions for beginning with openstacklib will be added later.
### 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_unicode_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'
Implementation
--------------

90
manifests/db/mysql.pp Normal file
View File

@ -0,0 +1,90 @@
# == 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_unicode_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'
define openstacklib::db::mysql (
$password_hash,
$dbname = $title,
$user = $title,
$host = '127.0.0.1',
$charset = 'utf8',
$collate = 'utf8_unicode_ci',
$allowed_hosts = undef,
$privileges = 'ALL',
) {
include ::mysql::client
mysql_database { $dbname:
ensure => present,
charset => $charset,
collate => $collate,
require => [ Class['mysql::server'], Class['mysql::client'] ],
}
mysql_user { "${user}@${host}":
ensure => present,
password_hash => $password_hash,
require => Class['mysql::server'],
}
mysql_grant { "${user}@${host}/${dbname}.*":
privileges => $privileges,
user => "${user}@${host}",
table => "${dbname}.*",
require => [Mysql_database[$dbname], Mysql_user["${user}@${host}"], Class['mysql::server'] ],
}
# Check allowed_hosts to avoid duplicate resource declarations
if is_array($allowed_hosts) and delete($allowed_hosts,$host) != [] {
$real_allowed_hosts = delete($allowed_hosts,$host)
$unique_real_allowed_hosts = prefix($real_allowed_hosts, "${dbname}_")
} elsif is_string($allowed_hosts) and ($allowed_hosts != $host) {
$real_allowed_hosts = $allowed_hosts
$unique_real_allowed_hosts = "${dbname}_${real_allowed_hosts}"
}
if $real_allowed_hosts {
openstacklib::db::mysql::host_access { $unique_real_allowed_hosts:
user => $user,
password_hash => $password_hash,
database => $dbname,
privileges => $privileges,
}
}
}

View File

@ -0,0 +1,41 @@
# 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
#
define openstacklib::db::mysql::host_access (
$user,
$password_hash,
$database,
$privileges,
) {
validate_re($title, '_', 'Title must be $dbname_$host')
$host = inline_template('<%= @title.split("_").last %>')
mysql_user { "${user}@${host}":
password_hash => $password_hash,
require => Mysql_database[$database],
}
mysql_grant { "${user}@${host}/${database}.*":
privileges => $privileges,
table => "${database}.*",
require => Mysql_user["${user}@${host}"],
user => "${user}@${host}",
}
}

View File

@ -0,0 +1,229 @@
require 'spec_helper'
describe 'openstacklib::db::mysql' do
let :pre_condition do
'include mysql::server'
end
password_hash = 'AA1420F182E88B9E5F874F6FBE7459291E8F4601'
let :required_params do
{ :password_hash => password_hash }
end
title = 'nova'
let (:title) { title }
context 'on a Debian osfamily' do
let :facts do
{ :osfamily => "Debian" }
end
context 'with only required parameters' do
let :params do
required_params
end
it { should contain_mysql_database(title).with(
:charset => 'utf8',
:collate => 'utf8_unicode_ci'
)}
it { should contain_mysql_user("#{title}@127.0.0.1").with(
:password_hash => password_hash
)}
it { should contain_mysql_grant("#{title}@127.0.0.1/#{title}.*").with(
:user => "#{title}@127.0.0.1",
:privileges => 'ALL',
:table => "#{title}.*"
)}
end
context 'when overriding charset' do
let :params do
{ :charset => 'latin1' }.merge(required_params)
end
it { should 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 { should 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 { should 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 { should contain_service('keystone').that_requires("Openstacklib::Db::Mysql[keystone]") }
end
context "overriding allowed_hosts param to array" do
let :params do
{ :allowed_hosts => ['127.0.0.1','%'] }.merge(required_params)
end
it {should_not contain_openstacklib__db__mysql__host_access("#{title}_127.0.0.1").with(
:user => title,
:password_hash => password_hash,
:database => title
)}
it {should contain_openstacklib__db__mysql__host_access("#{title}_%").with(
:user => title,
:password_hash => password_hash,
:database => title
)}
end
context "overriding allowed_hosts param to string" do
let :params do
{
:password_hash => password_hash,
:allowed_hosts => '192.168.1.1'
}
end
it {should contain_openstacklib__db__mysql__host_access("#{title}_192.168.1.1").with(
:user => title,
:password_hash => password_hash,
:database => title
)}
end
context "overriding allowed_hosts param equals to host param " do
let :params do
{
:password_hash => password_hash,
:allowed_hosts => '127.0.0.1'
}
end
it {should_not contain_openstacklib__db__mysql__host_access("#{title}_127.0.0.1").with(
:user => title,
:password_hash => password_hash,
:database => title
)}
end
end
context 'on a RedHat osfamily' do
let :facts do
{ :osfamily => 'RedHat' }
end
context 'with only required parameters' do
let :params do
required_params
end
it { should contain_mysql_database(title).with(
:charset => 'utf8',
:collate => 'utf8_unicode_ci'
)}
it { should contain_mysql_user("#{title}@127.0.0.1").with(
:password_hash => password_hash
)}
it { should contain_mysql_grant("#{title}@127.0.0.1/#{title}.*").with(
:user => "#{title}@127.0.0.1",
:privileges => 'ALL',
:table => "#{title}.*"
)}
end
context 'when overriding charset' do
let :params do
{ :charset => 'latin1' }.merge(required_params)
end
it { should contain_mysql_database(title).with_charset(params[:charset]) }
end
context 'when omitting the required parameter password' do
let :params do
required_params.delete(:password)
end
it { expect { should raise_error(Puppet::Error) } }
end
context 'when notifying other resources' do
let(:pre_condition) { 'exec {"nova-db-sync":}' }
let(:params) { { :notify => 'Exec[nova-db-sync]'}.merge(required_params) }
it { should contain_exec('nova-db-sync').that_subscribes_to("Openstacklib::Db::Mysql[#{title}]") }
end
context 'when required for other openstack services' do
let(:pre_condition) { 'service {"keystone":}' }
let(:title) { 'keystone' }
let(:params) { { :before => 'Service[keystone]'}.merge(required_params) }
it { should contain_service('keystone').that_requires("Openstacklib::Db::Mysql[keystone]") }
end
context "overriding allowed_hosts param to array" do
let :params do
{ :allowed_hosts => ['127.0.0.1','%'] }.merge(required_params)
end
it {should_not contain_openstacklib__db__mysql__host_access("#{title}_127.0.0.1").with(
:user => title,
:password_hash => password_hash,
:database => title
)}
it {should contain_openstacklib__db__mysql__host_access("#{title}_%").with(
:user => title,
:password_hash => password_hash,
:database => title
)}
end
context "overriding allowed_hosts param to string" do
let :params do
{
:password_hash => password_hash,
:allowed_hosts => '192.168.1.1'
}
end
it {should contain_openstacklib__db__mysql__host_access("#{title}_192.168.1.1").with(
:user => title,
:password_hash => password_hash,
:database => title
)}
end
context "overriding allowed_hosts param equals to host param " do
let :params do
{
:password_hash => password_hash,
:allowed_hosts => '127.0.0.1'
}
end
it {should_not contain_openstacklib__db__mysql__host_access("#{title}_127.0.0.1").with(
:user => title,
:password_hash => password_hash,
:database => title
)}
end
end
end