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
|
||||
Gemfile.lock
|
||||
.vendor
|
||||
.bundle/
|
||||
vendor/
|
||||
|
3
Gemfile
3
Gemfile
@ -1,9 +1,12 @@
|
||||
source 'https://rubygems.org'
|
||||
|
||||
gem 'json'
|
||||
|
||||
group :development, :test do
|
||||
gem 'puppetlabs_spec_helper', :require => false
|
||||
gem 'puppet-lint', '~> 0.3.2'
|
||||
gem 'rake', '10.1.1'
|
||||
gem 'webmock'
|
||||
end
|
||||
|
||||
if puppetversion = ENV['PUPPET_GEM_VERSION']
|
||||
|
@ -17,6 +17,12 @@ class { 'neutron::server':
|
||||
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
|
||||
class { 'neutron::agents::dhcp': }
|
||||
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
|
||||
end
|
||||
end
|
||||
|
||||
def create
|
||||
provider.create
|
||||
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 'shared_examples'
|
||||
require 'webmock/rspec'
|
||||
require 'json'
|
||||
|
||||
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')
|
||||
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