MidoNet services manifests

Provide TripleO overcloud manifests to deploy MidoNet and the cluster
services that needs to run.

Change-Id: I24f852e74fc4652d4609e1a71897e813448055fe
This commit is contained in:
Jaume Devesa
2015-11-10 21:13:43 +01:00
parent 37b58bef9e
commit 95c87ea79b
14 changed files with 767 additions and 0 deletions

View File

@@ -2,5 +2,26 @@ fixtures:
repositories:
'firewall': 'git://github.com/puppetlabs/puppetlabs-firewall.git'
'stdlib': 'git://github.com/puppetlabs/puppetlabs-stdlib.git'
'midonet':
repo: 'git://github.com/midonet/puppet-midonet.git'
ref: 'v2015.06.7'
'tomcat':
repo: 'git://github.com/puppetlabs/puppetlabs-tomcat.git'
ref: '1.3.2'
'inifile':
repo: 'git://github.com/puppetlabs/puppetlabs-inifile.git'
ref: '1.4.2'
'cassandra':
repo: 'git://github.com/midonet/puppet-cassandra.git'
ref: 'v1.1.1'
'zookeeper':
repo: 'git://github.com/deric/puppet-zookeeper.git'
ref: 'v0.3.9'
'datacat':
repo: 'git://github.com/richardc/puppet-datacat'
ref: '0.6.2'
'java':
repo: 'git://github.com/puppetlabs/puppetlabs-java'
ref: '1.4.2'
symlinks:
"tripleo": "#{source_dir}"

View File

@@ -0,0 +1,14 @@
# Custom function to extract the index from a list.
# The list are a list of hostname, and the index is the n'th
# position of the host in list
module Puppet::Parser::Functions
newfunction(:extract_id, :type => :rvalue) do |argv|
hosts = argv[0]
if hosts.class != Array
hosts = [hosts]
end
hostname = argv[1]
hash = Hash[hosts.map.with_index.to_a]
return hash[hostname].to_i + 1
end
end

View File

@@ -0,0 +1,24 @@
# Custom function to convert a list of ips to a map
# like {'ip' => xxx.xxx.xxx.xxx }. This function is needed
# because a not-so-good design of the puppet-midonet module
# and we hope to deprecate it soon.
module Puppet::Parser::Functions
newfunction(:list_to_zookeeper_hash, :type => :rvalue, :doc => <<-EOS
This function returns Zookeper configuration list of hash
EOS
) do |argv|
zk_list = argv[0]
if zk_list.class != Array
zk_list = [zk_list]
end
result = Array.new
zk_list.each do |zk_ip|
zk_map = Hash.new
zk_map['ip'] = zk_ip
zk_map['port'] = 2181
result.push(zk_map)
end
return result
end
end

View File

@@ -0,0 +1,53 @@
# TODO(devvesa): Remove the validation function once puppetlabs-stdlib maintainers
# accept the pull request: https://github.com/puppetlabs/puppetlabs-stdlib/pull/546
# This project should not maintain it.
module Puppet::Parser::Functions
newfunction(:validate_ip_address, :doc => <<-ENDHEREDOC
Validate that all values passed are valid IP addresses,
regardless they are IPv4 or IPv6
Fail compilation if any value fails this check.
The following values will pass:
$my_ip = "1.2.3.4"
validate_ip_address($my_ip)
validate_bool("8.8.8.8", "172.16.0.1", $my_ip)
$my_ip = "3ffe:505:2"
validate_ip_address(1)
validate_ip_address($my_ip)
validate_bool("fe80::baf6:b1ff:fe19:7507", $my_ip)
The following values will fail, causing compilation to abort:
$some_array = [ 1, true, false, "garbage string", "3ffe:505:2" ]
validate_ip_address($some_array)
ENDHEREDOC
) do |args|
require "ipaddr"
rescuable_exceptions = [ ArgumentError ]
if defined?(IPAddr::InvalidAddressError)
rescuable_exceptions << IPAddr::InvalidAddressError
end
unless args.length > 0 then
raise Puppet::ParseError, ("validate_ip_address(): wrong number of arguments (#{args.length}; must be > 0)")
end
args.each do |arg|
unless arg.is_a?(String)
raise Puppet::ParseError, "#{arg.inspect} is not a string."
end
begin
unless IPAddr.new(arg).ipv4? or IPAddr.new(arg).ipv6?
raise Puppet::ParseError, "#{arg.inspect} is not a valid IP address."
end
rescue *rescuable_exceptions
raise Puppet::ParseError, "#{arg.inspect} is not a valid IP address."
end
end
end
end

View File

@@ -0,0 +1,69 @@
#
# Copyright (C) 2015 Midokura SARL
#
# 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: tripleo::cluster::cassandra
#
# Deploys a cassandra service that belongs to a cluster. Uses puppet-cassandra
#
# == Parameters:
#
# [*cassandra_servers*]
# (required) All the IP addresses of the cassandra cluster.
# Array of strings value.
#
# [*cassandra_ip*]
# (required) IP address of the current host.
# String value
#
# [*storage_port*]
# (optional) Inter-node cluster communication port.
# Defaults to 7000.
#
# [*ssl_storage_port*]
# (optional) SSL Inter-node cluster communication port.
# Defaults to 7001.
#
# [*client_port*]
# (optional) Cassandra client port.
# Defaults to 9042.
#
# [*client_port_thrift*]
# (optional) Cassandra client port thrift.
# Defaults to 9160.
#
class tripleo::cluster::cassandra(
$cassandra_servers,
$cassandra_ip,
$storage_port = '7000',
$ssl_storage_port = '7001',
$client_port = '9042',
$client_port_thrift = '9160'
)
{
validate_array($cassandra_servers)
validate_ipv4_address($cassandra_ip)
class {'::cassandra::run':
seeds => $cassandra_servers,
seed_address => $cassandra_ip,
conf_dir => '/etc/cassandra/default.conf',
pid_dir => '/var/run/cassandra',
service_path => '/sbin',
storage_port => $storage_port,
ssl_storage_port => $ssl_storage_port,
client_port => $client_port,
client_port_thrift => $client_port_thrift
}
}

View File

@@ -0,0 +1,65 @@
#
# Copyright (C) 2015 Midokura SARL
#
# 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: tripleo::cluster::zookeeper
#
# Deploys a zookeeper service that belongs to a cluster. Uses deric-zookeeper
#
# == Parameters:
#
# [*zookeeper_server_ips*]
# (required) List of IP addresses of the zookeeper cluster.
# Arrays of strings value.
#
# [*zookeeper_client_ip*]
# (required) IP address of the host where zookeeper will listen IP addresses.
# String (IPv4) value.
#
# [*zookeeper_hostnames*]
# (required) List of hostnames of the zookeeper cluster. The hostname of the
# node will be used to define the ID of the zookeeper configuration
# Array of strings value.
#
class tripleo::cluster::zookeeper(
$zookeeper_server_ips,
$zookeeper_client_ip,
$zookeeper_hostnames
)
{
validate_array($zookeeper_server_ips)
validate_ipv4_address($zookeeper_client_ip)
validate_array($zookeeper_hostnames)
# TODO(devvesa) Zookeeper package should provide these paths,
# remove this lines as soon as it will.
file {['/usr/lib', '/usr/lib/zookeeper', '/usr/lib/zookeeper/bin/']:
ensure => directory
}
file {'/usr/lib/zookeeper/bin/zkEnv.sh':
ensure => link,
target => '/usr/libexec/zkEnv.sh'
}
class {'::zookeeper':
servers => $zookeeper_server_ips,
client_ip => $zookeeper_client_ip,
id => extract_id($zookeeper_hostnames, $::hostname),
cfg_dir => '/etc/zookeeper/conf',
}
File['/usr/lib/zookeeper/bin/zkEnv.sh'] -> Class['::zookeeper']
}

View File

@@ -0,0 +1,61 @@
#
# Copyright (C) 2015 Midokura SARL
#
# 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: tripleo::network::midonet::agent
#
# Configure the midonet agent
#
# == Parameters:
#
# [*zookeeper_servers*]
# (required) List of IPs of the zookeeper server cluster. It will configure
# the connection using the 2181 port.
# Array of strings value.
#
# [*cassandra_seeds*]
# (required) List of IPs of the cassandra cluster.
# Array of strings value.
#
class tripleo::network::midonet::agent (
$zookeeper_servers,
$cassandra_seeds
) {
validate_array($zookeeper_servers)
validate_array($cassandra_seeds)
# FIXME: This statement should be controlled by hiera on heat templates
# project
# Make sure openvswitch service is not running
service {'openvswitch':
ensure => stopped,
enable => false
}
exec {'delete datapaths':
command => '/usr/bin/mm-dpctl --delete-dp ovs-system',
path => '/usr/bin:/usr/sbin:/bin',
onlyif => '/usr/bin/mm-dpctl --show-dp ovs-system'
}
# Configure and run the agent
class {'::midonet::midonet_agent':
zk_servers => list_to_zookeeper_hash($zookeeper_servers),
cassandra_seeds => $cassandra_seeds
}
Service['openvswitch'] -> Class['::midonet::midonet_agent::run']
Exec['delete datapaths'] -> Class['::midonet::midonet_agent::run']
}

View File

@@ -0,0 +1,117 @@
#
# Copyright (C) 2015 Midokura SARL
#
# 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: tripleo::network::midonet::api
#
# Configure the MidoNet API
#
# == Parameters:
#
# [*zookeeper_servers*]
# (required) List IPs of the zookeeper server cluster. Zookeeper is the
# backend database where MidoNet stores the virtual network topology.
# Array of strings value.
#
# [*vip*]
# (required) Public Virtual IP where the API will be exposed.
# String (IPv4) value.
#
# [*keystone_ip*]
# (required) MidoNet API is registered as an OpenStack service. Provide the
# keystone ip address.
# String (IPv4) value.
#
# [*keystone_admin_token*]
# (required) MidoNet API is registered as an OpenStack service. It needs the
# keystone admin token to perform some admin calls.
# String value.
#
# [*bind_address*]
# (required) MidoNet API uses a Tomcat instance to offer the REST service. The
# ip address where to bind the tomcat service.
# String (IPv4) value.
#
# [*admin_password*]
# (required) OpenStack admin user password.
# String value.
#
# [*keystone_port*]
# (optional) MidoNet API is registered as an OpenStack service. Provide
# the keystone port.
# Defaults to 35357
#
# [*keystone_tenant_name*]
# (optional) Tenant of the keystone service.
# Defaults to 'admin'
#
# [*admin_user_name*]
# (optional) OpenStack admin user name.
# Defaults to 'admin'
#
# [*admin_tenant_name*]
# (optional). OpenStack admin tenant name.
# Defaults to 'admin'
#
class tripleo::network::midonet::api(
$zookeeper_servers,
$vip,
$keystone_ip,
$keystone_admin_token,
$bind_address,
$admin_password,
$keystone_port = 35357,
$keystone_tenant_name = 'admin',
$admin_user_name = 'admin',
$admin_tenant_name = 'admin'
)
{
validate_array($zookeeper_servers)
validate_ip_address($vip)
validate_ip_address($keystone_ip)
validate_ip_address($bind_address)
# Run Tomcat and MidoNet API
class {'::tomcat':
install_from_source => false
} ->
package {'midonet-api':
ensure => present
} ->
class {'::midonet::midonet_api::run':
zk_servers => list_to_zookeeper_hash($zookeeper_servers),
keystone_auth => true,
tomcat_package => 'tomcat',
vtep => false,
api_ip => $vip,
api_port => '8081',
keystone_host => $keystone_ip,
keystone_port => $keystone_port,
keystone_admin_token => $keystone_admin_token,
keystone_tenant_name => $keystone_tenant_name,
catalina_base => '/usr/share/tomcat',
bind_address => $bind_address
}
# Configure the CLI
class {'::midonet::midonet_cli':
api_endpoint => "http://${vip}:8081/midonet-api",
username => $admin_user_name,
password => $admin_password,
tenant_name => $admin_tenant_name
}
}

View File

@@ -0,0 +1,45 @@
#
# Copyright (C) 2015 Midokura SARL
#
# 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 the cassandra service
require 'spec_helper'
describe 'tripleo::cluster::cassandra' do
shared_examples_for 'cassandra cluster service' do
let :params do
{
:cassandra_servers => ['192.168.2.2', '192.168.2.3'],
:cassandra_ip => '192.168.2.2'
}
end
it 'should configure cassandra' do
is_expected.to contain_class('cassandra::run').with(
:seeds => ['192.168.2.2', '192.168.2.3'],
:seed_address => '192.168.2.2',
:storage_port => '7000',
:ssl_storage_port => '7001',
:client_port => '9042',
:client_port_thrift => '9160'
)
end
end
it_configures 'cassandra cluster service'
end

View File

@@ -0,0 +1,115 @@
#
# Copyright (C) 2015 Midokura SARL
#
# 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 the zookeeper service
require 'spec_helper'
describe 'tripleo::cluster::zookeeper' do
let :default_params do
{
:zookeeper_server_ips => ['23.43.2.34', '23.43.2.35', '24.43.2.36'],
:zookeeper_hostnames => ['host1.midonet', 'host2.midonet', 'host3.midonet']
}
end
context 'on host1' do
let :facts do
{
:hostname => 'host1.midonet',
:osfamily => 'RedHat',
:operatingsystemmajrelease => 7,
}
end
let :params do
{
:zookeeper_client_ip => '23.43.2.34'
}
end
before do
params.merge!(default_params)
end
it 'should call zookeeper using id==1' do
is_expected.to contain_class('zookeeper').with(
:servers => ['23.43.2.34', '23.43.2.35', '24.43.2.36'],
:client_ip => '23.43.2.34',
:id => 1
)
end
end
context 'on host2' do
let :facts do
{
:hostname => 'host2.midonet',
:osfamily => 'RedHat',
:operatingsystemmajrelease => 7,
}
end
let :params do
{
:zookeeper_client_ip => '23.43.2.35'
}
end
before do
params.merge!(default_params)
end
it 'should call zookeeper using id==1' do
is_expected.to contain_class('zookeeper').with(
:servers => ['23.43.2.34', '23.43.2.35', '24.43.2.36'],
:client_ip => '23.43.2.35',
:id => 2
)
end
end
context 'on host3' do
let :facts do
{
:hostname => 'host3.midonet',
:osfamily => 'RedHat',
:operatingsystemmajrelease => 7,
}
end
let :params do
{
:zookeeper_client_ip => '23.43.2.36'
}
end
before do
params.merge!(default_params)
end
it 'should call zookeeper using id==1' do
is_expected.to contain_class('zookeeper').with(
:servers => ['23.43.2.34', '23.43.2.35', '24.43.2.36'],
:client_ip => '23.43.2.36',
:id => 3
)
end
end
end

View File

@@ -0,0 +1,61 @@
#
# Copyright (C) 2015 Midokura SARL
#
# 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 the midonet agent
require 'spec_helper'
describe 'tripleo::network::midonet::agent' do
let :facts do
{
:hostname => 'host2.midonet',
:osfamily => 'RedHat',
:operatingsystem => 'CentOS',
:operatingsystemmajrelease => 7,
}
end
shared_examples_for 'midonet agent test' do
let :params do
{
:zookeeper_servers => ['192.168.2.2', '192.168.2.3'],
:cassandra_seeds => ['192.168.2.2', '192.168.2.3']
}
end
it 'should stop openvswitch' do
is_expected.to contain_service('openvswitch').with(
:ensure => 'stopped',
:enable => false
)
end
it 'should run the agent with a list of maps' do
is_expected.to contain_class('midonet::midonet_agent').with(
:zk_servers => [{'ip' => '192.168.2.2',
'port' => 2181},
{'ip' => '192.168.2.3',
'port' => 2181}],
:cassandra_seeds => ['192.168.2.2','192.168.2.3']
)
end
end
it_configures 'midonet agent test'
end

View File

@@ -0,0 +1,72 @@
#
# Copyright (C) 2015 Midokura SARL
#
# 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 the midonet api
require 'spec_helper'
describe 'tripleo::network::midonet::api' do
let :facts do
{
:augeasversion => '1.0.0'
}
end
shared_examples_for 'midonet api test' do
let :params do
{
:zookeeper_servers => ['192.168.2.1', '192.168.2.2'],
:vip => '192.23.0.2',
:keystone_ip => '192.23.0.2',
:keystone_admin_token => 'admin_token',
:admin_password => 'admin_password',
:bind_address => '192.23.0.65'
}
end
it 'should call api configuration' do
is_expected.to contain_class('midonet::midonet_api::run').with(
:zk_servers => [{'ip' => '192.168.2.1', 'port' => 2181},
{'ip' => '192.168.2.2', 'port' => 2181}],
:keystone_auth => true,
:tomcat_package => 'tomcat',
:vtep => false,
:api_ip => '192.23.0.2',
:api_port => '8081',
:keystone_host => '192.23.0.2',
:keystone_port => 35357,
:keystone_admin_token => 'admin_token',
:keystone_tenant_name => 'admin',
:catalina_base => '/usr/share/tomcat',
:bind_address => '192.23.0.65'
)
end
it 'should install the cli' do
is_expected.to contain_class('midonet::midonet_cli').with(
:api_endpoint => 'http://192.23.0.2:8081/midonet-api',
:username => 'admin',
:password => 'admin_password',
:tenant_name => 'admin'
)
end
end
it_configures 'midonet api test'
end

View File

@@ -0,0 +1,46 @@
require 'spec_helper'
describe 'validate_ip_address' do
describe 'signature validation' do
it { is_expected.not_to eq(nil) }
it { is_expected.to run.with_params().and_raise_error(Puppet::ParseError, /wrong number of arguments/i) }
end
describe 'valid inputs' do
it { is_expected.to run.with_params('0.0.0.0') }
it { is_expected.to run.with_params('8.8.8.8') }
it { is_expected.to run.with_params('127.0.0.1') }
it { is_expected.to run.with_params('10.10.10.10') }
it { is_expected.to run.with_params('194.232.104.150') }
it { is_expected.to run.with_params('244.24.24.24') }
it { is_expected.to run.with_params('255.255.255.255') }
it { is_expected.to run.with_params('1.2.3.4', '5.6.7.8') }
it { is_expected.to run.with_params('3ffe:0505:0002::') }
it { is_expected.to run.with_params('3ffe:0505:0002::', '3ffe:0505:0002::2') }
it { is_expected.to run.with_params('::1/64') }
it { is_expected.to run.with_params('fe80::a00:27ff:fe94:44d6/64') }
context 'with netmasks' do
it { is_expected.to run.with_params('8.8.8.8/0') }
it { is_expected.to run.with_params('8.8.8.8/16') }
it { is_expected.to run.with_params('8.8.8.8/32') }
it { is_expected.to run.with_params('8.8.8.8/255.255.0.0') }
end
end
describe 'invalid inputs' do
it { is_expected.to run.with_params({}).and_raise_error(Puppet::ParseError, /is not a string/) }
it { is_expected.to run.with_params(1).and_raise_error(Puppet::ParseError, /is not a string/) }
it { is_expected.to run.with_params(true).and_raise_error(Puppet::ParseError, /is not a string/) }
it { is_expected.to run.with_params('one').and_raise_error(Puppet::ParseError, /is not a valid IP/) }
it { is_expected.to run.with_params('0.0.0').and_raise_error(Puppet::ParseError, /is not a valid IP/) }
it { is_expected.to run.with_params('0.0.0.256').and_raise_error(Puppet::ParseError, /is not a valid IP/) }
it { is_expected.to run.with_params('0.0.0.0.0').and_raise_error(Puppet::ParseError, /is not a valid IP/) }
it { is_expected.to run.with_params('1.2.3.4', {}).and_raise_error(Puppet::ParseError, /is not a string/) }
it { is_expected.to run.with_params('1.2.3.4', 1).and_raise_error(Puppet::ParseError, /is not a string/) }
it { is_expected.to run.with_params('1.2.3.4', true).and_raise_error(Puppet::ParseError, /is not a string/) }
it { is_expected.to run.with_params('1.2.3.4', 'one').and_raise_error(Puppet::ParseError, /is not a valid IP/) }
it { is_expected.to run.with_params('::1', {}).and_raise_error(Puppet::ParseError, /is not a string/) }
it { is_expected.to run.with_params('::1', true).and_raise_error(Puppet::ParseError, /is not a string/) }
it { is_expected.to run.with_params('::1', 'one').and_raise_error(Puppet::ParseError, /is not a valid IP/) }
end
end

View File

@@ -2,9 +2,13 @@ require 'puppetlabs_spec_helper/module_spec_helper'
require 'shared_examples'
require 'webmock/rspec'
fixture_path = File.expand_path(File.join(__FILE__, '..', 'fixtures'))
RSpec.configure do |c|
c.alias_it_should_behave_like_to :it_configures, 'configures'
c.alias_it_should_behave_like_to :it_raises, 'raises'
c.module_path = File.join(fixture_path, 'modules')
c.manifest_dir = File.join(fixture_path, 'manifests')
c.default_facts = {
:kernel => 'Linux',