3 require 'puppet/provider/neutron'
6 describe Puppet::Provider::Neutron do
12 let :credential_hash do
14 'auth_host' => '192.168.56.210',
15 'auth_port' => '35357',
16 'auth_protocol' => 'https',
17 'admin_tenant_name' => 'admin_tenant',
18 'admin_user' => 'admin',
19 'admin_password' => 'password',
24 'https://192.168.56.210:35357/v2.0/'
27 let :credential_error do
28 /Neutron types will not work/
32 /Neutron or Keystone API is not avalaible/
39 describe 'when determining credentials' do
41 it 'should fail if config is empty' do
43 klass.expects(:neutron_conf).returns(conf)
45 klass.neutron_credentials
46 end.to raise_error(Puppet::Error, credential_error)
49 it 'should fail if config does not have keystone_authtoken section.' do
50 conf = {'foo' => 'bar'}
51 klass.expects(:neutron_conf).returns(conf)
53 klass.neutron_credentials
54 end.to raise_error(Puppet::Error, credential_error)
57 it 'should fail if config does not contain all auth params' do
58 conf = {'keystone_authtoken' => {'invalid_value' => 'foo'}}
59 klass.expects(:neutron_conf).returns(conf)
61 klass.neutron_credentials
62 end.to raise_error(Puppet::Error, credential_error)
65 it 'should use specified host/port/protocol in the auth endpoint' do
66 conf = {'keystone_authtoken' => credential_hash}
67 klass.expects(:neutron_conf).returns(conf)
68 klass.get_auth_endpoint.should == auth_endpoint
71 it 'should find region_name if specified' do
73 'keystone_authtoken' => credential_hash,
74 'DEFAULT' => { 'nova_region_name' => 'REGION_NAME' }
76 klass.expects(:neutron_conf).returns(conf)
77 klass.neutron_credentials['nova_region_name'] == 'REGION_NAME'
82 describe 'when invoking the neutron cli' do
84 it 'should set auth credentials in the environment' do
86 :OS_AUTH_URL => auth_endpoint,
87 :OS_USERNAME => credential_hash['admin_user'],
88 :OS_TENANT_NAME => credential_hash['admin_tenant_name'],
89 :OS_PASSWORD => credential_hash['admin_password'],
91 klass.expects(:get_neutron_credentials).with().returns(credential_hash)
92 klass.expects(:withenv).with(authenv)
93 klass.auth_neutron('test_retries')
96 it 'should set region in the environment if needed' do
98 :OS_AUTH_URL => auth_endpoint,
99 :OS_USERNAME => credential_hash['admin_user'],
100 :OS_TENANT_NAME => credential_hash['admin_tenant_name'],
101 :OS_PASSWORD => credential_hash['admin_password'],
102 :OS_REGION_NAME => 'REGION_NAME',
105 cred_hash = credential_hash.merge({'nova_region_name' => 'REGION_NAME'})
106 klass.expects(:get_neutron_credentials).with().returns(cred_hash)
107 klass.expects(:withenv).with(authenv)
108 klass.auth_neutron('test_retries')
111 ['[Errno 111] Connection refused',
112 '400-{\'message\': \'\'}',
114 '503 Service Unavailable',
115 '504 Gateway Time-out',
116 'Maximum attempts reached',
117 'Unauthorized: bad credentials',
118 'Max retries exceeded'].reverse.each do |valid_message|
119 it "should retry when neutron cli returns with error #{valid_message}" do
120 klass.expects(:get_neutron_credentials).with().returns({})
121 klass.expects(:sleep).with(2).returns(nil)
122 klass.expects(:neutron).twice.with(['test_retries']).raises(
123 Puppet::ExecutionFailure, valid_message).then.returns('')
124 klass.auth_neutron('test_retries')
130 describe 'when listing neutron resources' do
132 it 'should exclude the column header' do
138 klass.expects(:auth_neutron).returns(output)
139 result = klass.list_neutron_resources('foo')
140 result.should eql(['net1', 'net2'])
143 it 'should return empty list when there are no neutron resources' do
146 klass.stubs(:auth_neutron).returns(output)
147 result = klass.list_neutron_resources('foo')
148 result.should eql([])
151 it 'should fail if resources list is nil' do
152 klass.stubs(:auth_neutron).returns(nil)
154 klass.list_neutron_resources('foo')
155 end.to raise_error(Puppet::Error, exec_error)
160 describe 'when retrieving attributes for neutron resources' do
162 it 'should parse single-valued attributes into a key-value pair' do
163 klass.expects(:auth_neutron).returns('admin_state_up="True"')
164 result = klass.get_neutron_resource_attrs('foo', 'id')
165 result.should eql({"admin_state_up" => 'True'})
168 it 'should parse multi-valued attributes into a key-list pair' do
174 klass.expects(:auth_neutron).returns(output)
175 result = klass.get_neutron_resource_attrs('foo', 'id')
176 result.should eql({"subnets" => ['subnet1', 'subnet2', 'subnet3']})
181 describe 'when listing router ports' do
187 it 'should handle an empty port list' do
188 klass.expects(:auth_neutron).with('router-port-list',
191 result = klass.list_router_ports(router)
192 result.should eql([])
195 it 'should handle several ports' do
197 "id","name","mac_address","fixed_ips"
198 "1345e576-a21f-4c2e-b24a-b245639852ab","","fa:16:3e:e3:e6:38","{""subnet_id"": ""839a1d2d-2c6e-44fb-9a2b-9b011dce8c2f"", ""ip_address"": ""10.0.0.1""}"
199 "de0dc526-02b2-467c-9832-2c3dc69ac2b4","","fa:16:3e:f6:b5:72","{""subnet_id"": ""e4db0abd-276a-4f69-92ea-8b9e4eacfd43"", ""ip_address"": ""172.24.4.226""}"
203 "{\"subnet_id\": \"839a1d2d-2c6e-44fb-9a2b-9b011dce8c2f\", \
204 \"ip_address\": \"10.0.0.1\"}",
206 "subnet_id"=>"839a1d2d-2c6e-44fb-9a2b-9b011dce8c2f",
207 "id"=>"1345e576-a21f-4c2e-b24a-b245639852ab",
208 "mac_address"=>"fa:16:3e:e3:e6:38"},
210 "{\"subnet_id\": \"e4db0abd-276a-4f69-92ea-8b9e4eacfd43\", \
211 \"ip_address\": \"172.24.4.226\"}",
213 "subnet_id"=>"e4db0abd-276a-4f69-92ea-8b9e4eacfd43",
214 "id"=>"de0dc526-02b2-467c-9832-2c3dc69ac2b4",
215 "mac_address"=>"fa:16:3e:f6:b5:72"}]
216 klass.expects(:auth_neutron).
217 with('router-port-list', '--format=csv', router).
219 result = klass.list_router_ports(router)
220 result.should eql(expected)
225 describe 'when parsing creation output' do
227 it 'should parse valid output into a hash' do
229 Created a new network:
230 admin_state_up="True"
231 id="5f9cbed2-d31c-4e9c-be92-87229acb3f69"
233 tenant_id="3056a91768d948d399f1d79051a7f221"
236 'admin_state_up' => 'True',
237 'id' => '5f9cbed2-d31c-4e9c-be92-87229acb3f69',
239 'tenant_id' => '3056a91768d948d399f1d79051a7f221',
241 klass.parse_creation_output(data).should == expected