Neutron Nova Interactions Support
Recent versions of Neutron are able to provide active notifications to Nova about changes to port status and data. This change adds the neutron::server::notifications class to allow Puppet to configure this feature when setting up a Neutron server. neutron.conf requires a tenant UUID for communicating with Nova. This changes adds adds a custom type and provider to translate an admin tenant name to the required UUID. Co-Authored-By: emilienm <emilien.macchi@enovance.com> Change-Id: I57af7cb47a12adabed9b1ee65e1c352b3e5fc2de Closes-bug: 1306694
This commit is contained in:
parent
71bd36760d
commit
f9b3d184a0
2
.gitignore
vendored
2
.gitignore
vendored
@ -3,3 +3,5 @@ spec/fixtures/modules/*
|
|||||||
spec/fixtures/manifests/site.pp
|
spec/fixtures/manifests/site.pp
|
||||||
Gemfile.lock
|
Gemfile.lock
|
||||||
.vendor
|
.vendor
|
||||||
|
.bundle/
|
||||||
|
vendor/
|
||||||
|
3
Gemfile
3
Gemfile
@ -1,9 +1,12 @@
|
|||||||
source 'https://rubygems.org'
|
source 'https://rubygems.org'
|
||||||
|
|
||||||
|
gem 'json'
|
||||||
|
|
||||||
group :development, :test do
|
group :development, :test do
|
||||||
gem 'puppetlabs_spec_helper', :require => false
|
gem 'puppetlabs_spec_helper', :require => false
|
||||||
gem 'puppet-lint', '~> 0.3.2'
|
gem 'puppet-lint', '~> 0.3.2'
|
||||||
gem 'rake', '10.1.1'
|
gem 'rake', '10.1.1'
|
||||||
|
gem 'webmock'
|
||||||
end
|
end
|
||||||
|
|
||||||
if puppetversion = ENV['PUPPET_GEM_VERSION']
|
if puppetversion = ENV['PUPPET_GEM_VERSION']
|
||||||
|
@ -17,6 +17,12 @@ class { 'neutron::server':
|
|||||||
connection => 'mysql://neutron:password@192.168.1.1/neutron',
|
connection => 'mysql://neutron:password@192.168.1.1/neutron',
|
||||||
}
|
}
|
||||||
|
|
||||||
|
# Configure nova notifications system
|
||||||
|
class { 'neutron::server::notifications':
|
||||||
|
nova_admin_tenant_name => 'admin',
|
||||||
|
nova_admin_password => 'secrete',
|
||||||
|
}
|
||||||
|
|
||||||
# Various agents
|
# Various agents
|
||||||
class { 'neutron::agents::dhcp': }
|
class { 'neutron::agents::dhcp': }
|
||||||
class { 'neutron::agents::l3': }
|
class { 'neutron::agents::l3': }
|
||||||
|
183
lib/puppet/provider/nova_admin_tenant_id_setter/ini_setting.rb
Normal file
183
lib/puppet/provider/nova_admin_tenant_id_setter/ini_setting.rb
Normal file
@ -0,0 +1,183 @@
|
|||||||
|
## NB: This must work with Ruby 1.8!
|
||||||
|
|
||||||
|
# This providers permits the nova_admin_tenant_id paramter in neutron.conf
|
||||||
|
# to be set by providing a nova_admin_tenant_name to the Puppet module and
|
||||||
|
# using the Keystone REST API to translate the name into the corresponding
|
||||||
|
# UUID.
|
||||||
|
#
|
||||||
|
# This requires that tenant names be unique. If there are multiple matches
|
||||||
|
# for a given tenant name, this provider will raise an exception.
|
||||||
|
|
||||||
|
require 'rubygems'
|
||||||
|
require 'net/http'
|
||||||
|
require 'json'
|
||||||
|
|
||||||
|
class KeystoneError < Puppet::Error
|
||||||
|
end
|
||||||
|
|
||||||
|
class KeystoneConnectionError < KeystoneError
|
||||||
|
end
|
||||||
|
|
||||||
|
class KeystoneAPIError < KeystoneError
|
||||||
|
end
|
||||||
|
|
||||||
|
# Provides common request handling semantics to the other methods in
|
||||||
|
# this module.
|
||||||
|
#
|
||||||
|
# +req+::
|
||||||
|
# An HTTPRequest object
|
||||||
|
# +url+::
|
||||||
|
# A parsed URL (returned from URI.parse)
|
||||||
|
def handle_request(req, url)
|
||||||
|
begin
|
||||||
|
res = Net::HTTP.start(url.host, url.port) {|http|
|
||||||
|
http.request(req)
|
||||||
|
}
|
||||||
|
|
||||||
|
if res.code != '200'
|
||||||
|
raise KeystoneAPIError, "Received error response from Keystone server at #{url}: #{res.message}"
|
||||||
|
end
|
||||||
|
rescue Errno::ECONNREFUSED => detail
|
||||||
|
raise KeystoneConnectionError, "Failed to connect to Keystone server at #{url}: #{detail}"
|
||||||
|
rescue SocketError => detail
|
||||||
|
raise KeystoneConnectionError, "Failed to connect to Keystone server at #{url}: #{detail}"
|
||||||
|
end
|
||||||
|
|
||||||
|
res
|
||||||
|
end
|
||||||
|
|
||||||
|
# Authenticates to a Keystone server and obtains an authentication token.
|
||||||
|
# It returns a 2-element +[token, authinfo]+, where +token+ is a token
|
||||||
|
# suitable for passing to openstack apis in the +X-Auth-Token+ header, and
|
||||||
|
# +authinfo+ is the complete response from Keystone, including the service
|
||||||
|
# catalog (if available).
|
||||||
|
#
|
||||||
|
# +auth_url+::
|
||||||
|
# Keystone endpoint URL. This function assumes API version
|
||||||
|
# 2.0 and an administrative endpoint, so this will typically look like
|
||||||
|
# +http://somehost:35357/v2.0+.
|
||||||
|
#
|
||||||
|
# +username+::
|
||||||
|
# Username for authentication.
|
||||||
|
#
|
||||||
|
# +password+::
|
||||||
|
# Password for authentication
|
||||||
|
#
|
||||||
|
# +tenantID+::
|
||||||
|
# Tenant UUID
|
||||||
|
#
|
||||||
|
# +tenantName+::
|
||||||
|
# Tenant name
|
||||||
|
#
|
||||||
|
def keystone_v2_authenticate(auth_url,
|
||||||
|
username,
|
||||||
|
password,
|
||||||
|
tenantId=nil,
|
||||||
|
tenantName=nil)
|
||||||
|
|
||||||
|
post_args = {
|
||||||
|
'auth' => {
|
||||||
|
'passwordCredentials' => {
|
||||||
|
'username' => username,
|
||||||
|
'password' => password
|
||||||
|
},
|
||||||
|
}}
|
||||||
|
|
||||||
|
if tenantId
|
||||||
|
post_args['auth']['tenantId'] = tenantId
|
||||||
|
end
|
||||||
|
|
||||||
|
if tenantName
|
||||||
|
post_args['auth']['tenantName'] = tenantName
|
||||||
|
end
|
||||||
|
|
||||||
|
url = URI.parse("#{auth_url}/tokens")
|
||||||
|
req = Net::HTTP::Post.new url.path
|
||||||
|
req['content-type'] = 'application/json'
|
||||||
|
req.body = post_args.to_json
|
||||||
|
|
||||||
|
res = handle_request(req, url)
|
||||||
|
data = JSON.parse res.body
|
||||||
|
return data['access']['token']['id'], data
|
||||||
|
end
|
||||||
|
|
||||||
|
# Queries a Keystone server to a list of all tenants.
|
||||||
|
#
|
||||||
|
# +auth_url+::
|
||||||
|
# Keystone endpoint. See the notes for +auth_url+ in
|
||||||
|
# +keystone_v2_authenticate+.
|
||||||
|
#
|
||||||
|
# +token+::
|
||||||
|
# A Keystone token that will be passed in requests as the value of the
|
||||||
|
# +X-Auth-Token+ header.
|
||||||
|
#
|
||||||
|
def keystone_v2_tenants(auth_url,
|
||||||
|
token)
|
||||||
|
|
||||||
|
url = URI.parse("#{auth_url}/tenants")
|
||||||
|
req = Net::HTTP::Get.new url.path
|
||||||
|
req['content-type'] = 'application/json'
|
||||||
|
req['x-auth-token'] = token
|
||||||
|
|
||||||
|
res = handle_request(req, url)
|
||||||
|
data = JSON.parse res.body
|
||||||
|
data['tenants']
|
||||||
|
end
|
||||||
|
|
||||||
|
Puppet::Type.type(:nova_admin_tenant_id_setter).provide(:ruby) do
|
||||||
|
def authenticate
|
||||||
|
token, authinfo = keystone_v2_authenticate(
|
||||||
|
@resource[:auth_url],
|
||||||
|
@resource[:auth_username],
|
||||||
|
@resource[:auth_password],
|
||||||
|
nil,
|
||||||
|
@resource[:auth_tenant_name])
|
||||||
|
|
||||||
|
return token
|
||||||
|
end
|
||||||
|
|
||||||
|
def find_tenant_by_name (token)
|
||||||
|
tenants = keystone_v2_tenants(
|
||||||
|
@resource[:auth_url],
|
||||||
|
token)
|
||||||
|
|
||||||
|
tenants.select{|tenant| tenant['name'] == @resource[:tenant_name]}
|
||||||
|
end
|
||||||
|
|
||||||
|
def exists?
|
||||||
|
false
|
||||||
|
end
|
||||||
|
|
||||||
|
def create
|
||||||
|
config
|
||||||
|
end
|
||||||
|
|
||||||
|
# This looks for the tenant specified by the 'tenant_name' parameter to
|
||||||
|
# the resource and returns the corresponding UUID if there is a single
|
||||||
|
# match.
|
||||||
|
#
|
||||||
|
# Raises a KeystoneAPIError if:
|
||||||
|
#
|
||||||
|
# - There are multiple matches, or
|
||||||
|
# - There are zero matches
|
||||||
|
def get_tenant_id
|
||||||
|
token = authenticate
|
||||||
|
tenants = find_tenant_by_name(token)
|
||||||
|
|
||||||
|
if tenants.length == 1
|
||||||
|
return tenants[0]['id']
|
||||||
|
elsif tenants.length > 1
|
||||||
|
raise KeystoneAPIError, 'Found multiple matches for tenant name'
|
||||||
|
else
|
||||||
|
raise KeystoneAPIError, 'Unable to find matching tenant'
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def config
|
||||||
|
Puppet::Type.type(:neutron_config).new(
|
||||||
|
{:name => 'DEFAULT/nova_admin_tenant_id', :value => "#{get_tenant_id}"}
|
||||||
|
).create
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
||||||
|
|
@ -15,4 +15,9 @@ Puppet::Type.newtype(:neutron_config) do
|
|||||||
value
|
value
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def create
|
||||||
|
provider.create
|
||||||
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
|
32
lib/puppet/type/nova_admin_tenant_id_setter.rb
Normal file
32
lib/puppet/type/nova_admin_tenant_id_setter.rb
Normal file
@ -0,0 +1,32 @@
|
|||||||
|
Puppet::Type.newtype(:nova_admin_tenant_id_setter) do
|
||||||
|
|
||||||
|
ensurable
|
||||||
|
|
||||||
|
newparam(:name, :namevar => true) do
|
||||||
|
desc 'The name of the setting to update'
|
||||||
|
end
|
||||||
|
|
||||||
|
newparam(:tenant_name) do
|
||||||
|
desc 'The nova admin tenant name'
|
||||||
|
end
|
||||||
|
|
||||||
|
newparam(:auth_url) do
|
||||||
|
desc 'The Keystone endpoint URL'
|
||||||
|
defaultto 'http://localhost:35357/v2.0'
|
||||||
|
end
|
||||||
|
|
||||||
|
newparam(:auth_username) do
|
||||||
|
desc 'Username with which to authenticate'
|
||||||
|
defaultto 'admin'
|
||||||
|
end
|
||||||
|
|
||||||
|
newparam(:auth_password) do
|
||||||
|
desc 'Password with which to authenticate'
|
||||||
|
end
|
||||||
|
|
||||||
|
newparam(:auth_tenant_name) do
|
||||||
|
desc 'Tenant name with which to authenticate'
|
||||||
|
defaultto 'admin'
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
112
manifests/server/notifications.pp
Normal file
112
manifests/server/notifications.pp
Normal file
@ -0,0 +1,112 @@
|
|||||||
|
# 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: neutron::server::notifications
|
||||||
|
#
|
||||||
|
# Configure Notification System Options
|
||||||
|
#
|
||||||
|
# === Parameters
|
||||||
|
#
|
||||||
|
# [*notify_nova_on_port_status_changes*]
|
||||||
|
# (optional) Send notification to nova when port status is active.
|
||||||
|
# Defaults to true
|
||||||
|
#
|
||||||
|
# [*notify_nova_on_port_data_changes*]
|
||||||
|
# (optional) Send notifications to nova when port data (fixed_ips/floatingips)
|
||||||
|
# change so nova can update its cache.
|
||||||
|
# Defaults to true
|
||||||
|
#
|
||||||
|
# [*send_events_interval*]
|
||||||
|
# (optional) Number of seconds between sending events to nova if there are
|
||||||
|
# any events to send.
|
||||||
|
# Defaults to '2'
|
||||||
|
#
|
||||||
|
# [*nova_url*]
|
||||||
|
# (optional) URL for connection to nova (Only supports one nova region
|
||||||
|
# currently).
|
||||||
|
# Defaults to 'http://127.0.0.1:8774/v2'
|
||||||
|
#
|
||||||
|
# [*nova_admin_auth_url*]
|
||||||
|
# (optional) Authorization URL for connection to nova in admin context.
|
||||||
|
# Defaults to 'http://127.0.0.1:35357/v2.0'
|
||||||
|
#
|
||||||
|
# [*nova_admin_username*]
|
||||||
|
# (optional) Username for connection to nova in admin context
|
||||||
|
# Defaults to 'nova'
|
||||||
|
#
|
||||||
|
# [*nova_admin_tenant_name*]
|
||||||
|
# (optional) The name of the admin nova tenant
|
||||||
|
# Defaults to 'services'
|
||||||
|
#
|
||||||
|
# [*nova_admin_tenant_id*]
|
||||||
|
# (optional) The UUID of the admin nova tenant. If provided this takes
|
||||||
|
# precedence over nova_admin_tenant_name.
|
||||||
|
#
|
||||||
|
# [*nova_admin_password*]
|
||||||
|
# (required) Password for connection to nova in admin context.
|
||||||
|
#
|
||||||
|
# [*nova_region_name*]
|
||||||
|
# (optional) Name of nova region to use. Useful if keystone manages more than
|
||||||
|
# one region.
|
||||||
|
# Defaults to 'RegionOne'
|
||||||
|
#
|
||||||
|
|
||||||
|
class neutron::server::notifications (
|
||||||
|
$notify_nova_on_port_status_changes = true,
|
||||||
|
$notify_nova_on_port_data_changes = true,
|
||||||
|
$send_events_interval = '2',
|
||||||
|
$nova_url = 'http://127.0.0.1:8774/v2',
|
||||||
|
$nova_admin_auth_url = 'http://127.0.0.1:35357/v2.0',
|
||||||
|
$nova_admin_username = 'nova',
|
||||||
|
$nova_admin_tenant_name = 'services',
|
||||||
|
$nova_admin_tenant_id = undef,
|
||||||
|
$nova_admin_password = false,
|
||||||
|
$nova_region_name = 'RegionOne',
|
||||||
|
) {
|
||||||
|
|
||||||
|
# Depend on the specified keystone_user resource, if it exists.
|
||||||
|
Keystone_user <| title == 'nova' |> -> Class[neutron::server::notifications]
|
||||||
|
|
||||||
|
if ! $nova_admin_password {
|
||||||
|
fail('nova_admin_password must be set.')
|
||||||
|
}
|
||||||
|
|
||||||
|
if ! ( $nova_admin_tenant_id or $nova_admin_tenant_name ) {
|
||||||
|
fail('You must provide either nova_admin_tenant_name or nova_admin_tenant_id.')
|
||||||
|
}
|
||||||
|
|
||||||
|
neutron_config {
|
||||||
|
'DEFAULT/notify_nova_on_port_status_changes': value => $notify_nova_on_port_status_changes;
|
||||||
|
'DEFAULT/notify_nova_on_port_data_changes': value => $notify_nova_on_port_data_changes;
|
||||||
|
'DEFAULT/send_events_interval': value => $send_events_interval;
|
||||||
|
'DEFAULT/nova_url': value => $nova_url;
|
||||||
|
'DEFAULT/nova_admin_auth_url': value => $nova_admin_auth_url;
|
||||||
|
'DEFAULT/nova_admin_username': value => $nova_admin_username;
|
||||||
|
'DEFAULT/nova_admin_password': value => $nova_admin_password;
|
||||||
|
'DEFAULT/nova_region_name': value => $nova_region_name;
|
||||||
|
}
|
||||||
|
|
||||||
|
if $nova_admin_tenant_id {
|
||||||
|
neutron_config {
|
||||||
|
'DEFAULT/nova_admin_tenant_id': value => $nova_admin_tenant_id;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
nova_admin_tenant_id_setter {'nova_admin_tenant_id':
|
||||||
|
ensure => present,
|
||||||
|
tenant_name => $nova_admin_tenant_name,
|
||||||
|
auth_url => $nova_admin_auth_url,
|
||||||
|
auth_username => $nova_admin_username,
|
||||||
|
auth_password => $nova_admin_password,
|
||||||
|
auth_tenant_name => $nova_admin_tenant_name,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
150
spec/classes/neutron_server_notifications_spec.rb
Normal file
150
spec/classes/neutron_server_notifications_spec.rb
Normal file
@ -0,0 +1,150 @@
|
|||||||
|
# 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 neutron::server::notifications class
|
||||||
|
#
|
||||||
|
|
||||||
|
require 'spec_helper'
|
||||||
|
|
||||||
|
describe 'neutron::server::notifications' do
|
||||||
|
let :pre_condition do
|
||||||
|
'define keystone_user ($name) {}'
|
||||||
|
end
|
||||||
|
|
||||||
|
let :default_params do
|
||||||
|
{
|
||||||
|
:notify_nova_on_port_status_changes => true,
|
||||||
|
:notify_nova_on_port_data_changes => true,
|
||||||
|
:send_events_interval => '2',
|
||||||
|
:nova_url => 'http://127.0.0.1:8774/v2',
|
||||||
|
:nova_admin_auth_url => 'http://127.0.0.1:35357/v2.0',
|
||||||
|
:nova_admin_username => 'nova',
|
||||||
|
:nova_admin_tenant_name => 'services',
|
||||||
|
:nova_region_name => 'RegionOne'
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
|
let :params do
|
||||||
|
{
|
||||||
|
:nova_admin_password => 'secrete',
|
||||||
|
:nova_admin_tenant_id => 'UUID'
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
|
shared_examples_for 'neutron server notifications' do
|
||||||
|
let :p do
|
||||||
|
default_params.merge(params)
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'configure neutron.conf' do
|
||||||
|
should contain_neutron_config('DEFAULT/notify_nova_on_port_status_changes').with_value(true)
|
||||||
|
should contain_neutron_config('DEFAULT/notify_nova_on_port_data_changes').with_value(true)
|
||||||
|
should contain_neutron_config('DEFAULT/send_events_interval').with_value('2')
|
||||||
|
should contain_neutron_config('DEFAULT/nova_url').with_value('http://127.0.0.1:8774/v2')
|
||||||
|
should contain_neutron_config('DEFAULT/nova_admin_auth_url').with_value('http://127.0.0.1:35357/v2.0')
|
||||||
|
should contain_neutron_config('DEFAULT/nova_admin_username').with_value('nova')
|
||||||
|
should contain_neutron_config('DEFAULT/nova_admin_password').with_value('secrete')
|
||||||
|
should contain_neutron_config('DEFAULT/nova_region_name').with_value('RegionOne')
|
||||||
|
should contain_neutron_config('DEFAULT/nova_admin_tenant_id').with_value('UUID')
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'when overriding parameters' do
|
||||||
|
before :each do
|
||||||
|
params.merge!(
|
||||||
|
:notify_nova_on_port_status_changes => false,
|
||||||
|
:notify_nova_on_port_data_changes => false,
|
||||||
|
:send_events_interval => '10',
|
||||||
|
:nova_url => 'http://nova:8774/v3',
|
||||||
|
:nova_admin_auth_url => 'http://keystone:35357/v2.0',
|
||||||
|
:nova_admin_username => 'joe',
|
||||||
|
:nova_region_name => 'MyRegion',
|
||||||
|
:nova_admin_tenant_id => 'UUID2'
|
||||||
|
)
|
||||||
|
end
|
||||||
|
it 'should configure neutron server with overrided parameters' do
|
||||||
|
should contain_neutron_config('DEFAULT/notify_nova_on_port_status_changes').with_value(false)
|
||||||
|
should contain_neutron_config('DEFAULT/notify_nova_on_port_data_changes').with_value(false)
|
||||||
|
should contain_neutron_config('DEFAULT/send_events_interval').with_value('10')
|
||||||
|
should contain_neutron_config('DEFAULT/nova_url').with_value('http://nova:8774/v3')
|
||||||
|
should contain_neutron_config('DEFAULT/nova_admin_auth_url').with_value('http://keystone:35357/v2.0')
|
||||||
|
should contain_neutron_config('DEFAULT/nova_admin_username').with_value('joe')
|
||||||
|
should contain_neutron_config('DEFAULT/nova_admin_password').with_value('secrete')
|
||||||
|
should contain_neutron_config('DEFAULT/nova_region_name').with_value('MyRegion')
|
||||||
|
should contain_neutron_config('DEFAULT/nova_admin_tenant_id').with_value('UUID2')
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'when no nova_admin_password is specified' do
|
||||||
|
before :each do
|
||||||
|
params.merge!(:nova_admin_password => '')
|
||||||
|
end
|
||||||
|
it 'should fail to configure neutron server' do
|
||||||
|
expect { subject }.to raise_error(Puppet::Error, /nova_admin_password must be set./)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'when no nova_admin_tenant_id and nova_admin_tenant_name specified' do
|
||||||
|
before :each do
|
||||||
|
params.merge!(
|
||||||
|
:nova_admin_tenant_id => '',
|
||||||
|
:nova_admin_tenant_name => ''
|
||||||
|
)
|
||||||
|
end
|
||||||
|
it 'should fail to configure neutron server' do
|
||||||
|
expect { subject }.to raise_error(Puppet::Error, /You must provide either nova_admin_tenant_name or nova_admin_tenant_id./)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'when providing a tenant name' do
|
||||||
|
before :each do
|
||||||
|
params.merge!(
|
||||||
|
:nova_admin_tenant_id => '',
|
||||||
|
:nova_admin_tenant_name => 'services'
|
||||||
|
)
|
||||||
|
end
|
||||||
|
it 'should configure nova admin tenant id' do
|
||||||
|
should contain_nova_admin_tenant_id_setter('nova_admin_tenant_id').with(
|
||||||
|
:ensure => 'present',
|
||||||
|
:tenant_name => 'services',
|
||||||
|
:auth_url => 'http://127.0.0.1:35357/v2.0',
|
||||||
|
:auth_password => 'secrete',
|
||||||
|
:auth_tenant_name => 'services'
|
||||||
|
)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'on Debian platforms' do
|
||||||
|
let :facts do
|
||||||
|
{ :osfamily => 'Debian' }
|
||||||
|
end
|
||||||
|
|
||||||
|
let :platform_params do
|
||||||
|
{}
|
||||||
|
end
|
||||||
|
|
||||||
|
it_configures 'neutron server notifications'
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'on RedHat platforms' do
|
||||||
|
let :facts do
|
||||||
|
{ :osfamily => 'RedHat' }
|
||||||
|
end
|
||||||
|
|
||||||
|
let :platform_params do
|
||||||
|
{}
|
||||||
|
end
|
||||||
|
|
||||||
|
it_configures 'neutron server notifications'
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
@ -1,7 +1,13 @@
|
|||||||
require 'puppetlabs_spec_helper/module_spec_helper'
|
require 'puppetlabs_spec_helper/module_spec_helper'
|
||||||
require 'shared_examples'
|
require 'shared_examples'
|
||||||
|
require 'webmock/rspec'
|
||||||
|
require 'json'
|
||||||
|
|
||||||
|
fixture_path = File.expand_path(File.join(__FILE__, '..', 'fixtures'))
|
||||||
|
|
||||||
RSpec.configure do |c|
|
RSpec.configure do |c|
|
||||||
c.alias_it_should_behave_like_to :it_configures, 'configures'
|
c.alias_it_should_behave_like_to :it_configures, 'configures'
|
||||||
c.alias_it_should_behave_like_to :it_raises, 'raises'
|
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')
|
||||||
end
|
end
|
||||||
|
177
spec/unit/provider/nova_admin_tenant_id_setter/neutron_spec.rb
Normal file
177
spec/unit/provider/nova_admin_tenant_id_setter/neutron_spec.rb
Normal file
@ -0,0 +1,177 @@
|
|||||||
|
require 'spec_helper'
|
||||||
|
require 'puppet'
|
||||||
|
require 'puppet/type/nova_admin_tenant_id_setter'
|
||||||
|
|
||||||
|
provider_class = Puppet::Type.type(:nova_admin_tenant_id_setter).provider(:ruby)
|
||||||
|
|
||||||
|
# used to simulate an authentication response from Keystone
|
||||||
|
# (POST v2.0/tokens)
|
||||||
|
auth_response = {
|
||||||
|
'access' => {
|
||||||
|
'token' => {
|
||||||
|
'id' => 'TOKEN',
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
# used to simulate a response to GET v2.0/tenants
|
||||||
|
tenants_response = {
|
||||||
|
'tenants' => [
|
||||||
|
{
|
||||||
|
'name' => 'services',
|
||||||
|
'id' => 'UUID_SERVICES'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
'name' => 'multiple_matches_tenant',
|
||||||
|
'id' => 'UUID1'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
'name' => 'multiple_matches_tenant',
|
||||||
|
'id' => 'UUID2'
|
||||||
|
},
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
|
# Stub for ini_setting resource
|
||||||
|
Puppet::Type.newtype(:ini_setting) do
|
||||||
|
end
|
||||||
|
|
||||||
|
# Stub for ini_setting provider
|
||||||
|
Puppet::Type.newtype(:ini_setting).provide(:ruby) do
|
||||||
|
def create
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
describe 'Puppet::Type.type(:nova_admin_tenant_id_setter)' do
|
||||||
|
let :params do
|
||||||
|
{
|
||||||
|
:name => 'nova_admin_tenant_id',
|
||||||
|
:tenant_name => 'services',
|
||||||
|
:auth_username => 'nova',
|
||||||
|
:auth_password => 'secret',
|
||||||
|
:auth_tenant_name => 'admin',
|
||||||
|
:auth_url => 'http://127.0.0.1:35357/v2.0',
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'should have a non-nil provider' do
|
||||||
|
expect(provider_class).not_to be_nil
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'when url is correct' do
|
||||||
|
before :each do
|
||||||
|
stub_request(:post, "http://127.0.0.1:35357/v2.0/tokens").
|
||||||
|
to_return(:status => 200,
|
||||||
|
:body => auth_response.to_json,
|
||||||
|
:headers => {})
|
||||||
|
stub_request(:get, "http://127.0.0.1:35357/v2.0/tenants").
|
||||||
|
with(:headers => {'X-Auth-Token'=>'TOKEN'}).
|
||||||
|
to_return(:status => 200,
|
||||||
|
:body => tenants_response.to_json,
|
||||||
|
:headers => {})
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'should create a resource' do
|
||||||
|
resource = Puppet::Type::Nova_admin_tenant_id_setter.new(params)
|
||||||
|
provider = provider_class.new(resource)
|
||||||
|
expect(provider.exists?).to be_false
|
||||||
|
expect(provider.create).to be_nil
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
# What happens if we ask for a tenant that does not exist?
|
||||||
|
context 'when tenant cannot be found' do
|
||||||
|
before :each do
|
||||||
|
stub_request(:post, "http://127.0.0.1:35357/v2.0/tokens").
|
||||||
|
to_return(:status => 200,
|
||||||
|
:body => auth_response.to_json,
|
||||||
|
:headers => {})
|
||||||
|
stub_request(:get, "http://127.0.0.1:35357/v2.0/tenants").
|
||||||
|
with(:headers => {'X-Auth-Token'=>'TOKEN'}).
|
||||||
|
to_return(:status => 200,
|
||||||
|
:body => tenants_response.to_json,
|
||||||
|
:headers => {})
|
||||||
|
|
||||||
|
params.merge!(:tenant_name => 'bad_tenant_name')
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'should receive an api error' do
|
||||||
|
resource = Puppet::Type::Nova_admin_tenant_id_setter.new(params)
|
||||||
|
provider = provider_class.new(resource)
|
||||||
|
expect(provider.exists?).to be_false
|
||||||
|
expect { provider.create }.to raise_error KeystoneAPIError, /Unable to find matching tenant/
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
# What happens if we ask for a tenant name that results in multiple
|
||||||
|
# matches?
|
||||||
|
context 'when there are multiple matching tenants' do
|
||||||
|
before :each do
|
||||||
|
stub_request(:post, "http://127.0.0.1:35357/v2.0/tokens").
|
||||||
|
to_return(:status => 200,
|
||||||
|
:body => auth_response.to_json,
|
||||||
|
:headers => {})
|
||||||
|
stub_request(:get, "http://127.0.0.1:35357/v2.0/tenants").
|
||||||
|
with(:headers => {'X-Auth-Token'=>'TOKEN'}).
|
||||||
|
to_return(:status => 200,
|
||||||
|
:body => tenants_response.to_json,
|
||||||
|
:headers => {})
|
||||||
|
|
||||||
|
params.merge!(:tenant_name => 'multiple_matches_tenant')
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'should receive an api error' do
|
||||||
|
resource = Puppet::Type::Nova_admin_tenant_id_setter.new(params)
|
||||||
|
provider = provider_class.new(resource)
|
||||||
|
expect(provider.exists?).to be_false
|
||||||
|
expect { provider.create }.to raise_error KeystoneAPIError, /Found multiple matches for tenant name/
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
# What happens if we pass a bad password?
|
||||||
|
context 'when password is incorrect' do
|
||||||
|
before :each do
|
||||||
|
stub_request(:post, "http://127.0.0.1:35357/v2.0/tokens").
|
||||||
|
to_return(:status => 401,
|
||||||
|
:body => auth_response.to_json,
|
||||||
|
:headers => {})
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'should receive an authentication error' do
|
||||||
|
resource = Puppet::Type::Nova_admin_tenant_id_setter.new(params)
|
||||||
|
provider = provider_class.new(resource)
|
||||||
|
expect(provider.exists?).to be_false
|
||||||
|
expect { provider.create }.to raise_error KeystoneAPIError
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
# What happens if the server is not listening?
|
||||||
|
context 'when keystone server is unavailable' do
|
||||||
|
before :each do
|
||||||
|
stub_request(:post, "http://127.0.0.1:35357/v2.0/tokens").to_raise Errno::ECONNREFUSED
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'should receive a connection error' do
|
||||||
|
resource = Puppet::Type::Nova_admin_tenant_id_setter.new(params)
|
||||||
|
provider = provider_class.new(resource)
|
||||||
|
expect(provider.exists?).to be_false
|
||||||
|
expect { provider.create }.to raise_error KeystoneConnectionError
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
# What happens if we mistype the hostname?
|
||||||
|
context 'when keystone server is unknown' do
|
||||||
|
before :each do
|
||||||
|
stub_request(:post, "http://127.0.0.1:35357/v2.0/tokens").to_raise SocketError, 'getaddrinfo: Name or service not known'
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'should receive a connection error' do
|
||||||
|
resource = Puppet::Type::Nova_admin_tenant_id_setter.new(params)
|
||||||
|
provider = provider_class.new(resource)
|
||||||
|
expect(provider.exists?).to be_false
|
||||||
|
expect { provider.create }.to raise_error KeystoneConnectionError
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
||||||
|
|
Loading…
Reference in New Issue
Block a user