memcached (openstack) is no longer in use
[mirror/dsa-puppet.git] / 3rdparty / modules / neutron / spec / unit / provider / neutron_spec.rb
1 require 'puppet'
2 require 'spec_helper'
3 require 'puppet/provider/neutron'
4 require 'tempfile'
5
6 describe Puppet::Provider::Neutron do
7
8   def klass
9     described_class
10   end
11
12   let :credential_hash do
13     {
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',
20     }
21   end
22
23   let :auth_endpoint do
24     'https://192.168.56.210:35357/v2.0/'
25   end
26
27   let :credential_error do
28     /Neutron types will not work/
29   end
30
31   let :exec_error do
32     /Neutron or Keystone API is not avalaible/
33   end
34
35   after :each do
36     klass.reset
37   end
38
39   describe 'when determining credentials' do
40
41     it 'should fail if config is empty' do
42       conf = {}
43       klass.expects(:neutron_conf).returns(conf)
44       expect do
45         klass.neutron_credentials
46       end.to raise_error(Puppet::Error, credential_error)
47     end
48
49     it 'should fail if config does not have keystone_authtoken section.' do
50       conf = {'foo' => 'bar'}
51       klass.expects(:neutron_conf).returns(conf)
52       expect do
53         klass.neutron_credentials
54       end.to raise_error(Puppet::Error, credential_error)
55     end
56
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)
60       expect do
61        klass.neutron_credentials
62       end.to raise_error(Puppet::Error, credential_error)
63     end
64
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
69     end
70
71     it 'should find region_name if specified' do
72       conf = {
73         'keystone_authtoken' => credential_hash,
74         'DEFAULT' => { 'nova_region_name' => 'REGION_NAME' }
75       }
76       klass.expects(:neutron_conf).returns(conf)
77       klass.neutron_credentials['nova_region_name'] == 'REGION_NAME'
78     end
79
80   end
81
82   describe 'when invoking the neutron cli' do
83
84     it 'should set auth credentials in the environment' do
85       authenv = {
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'],
90       }
91       klass.expects(:get_neutron_credentials).with().returns(credential_hash)
92       klass.expects(:withenv).with(authenv)
93       klass.auth_neutron('test_retries')
94     end
95
96     it 'should set region in the environment if needed' do
97       authenv = {
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',
103       }
104
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')
109     end
110
111     ['[Errno 111] Connection refused',
112      '400-{\'message\': \'\'}',
113      '(HTTP 400)',
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')
125       end
126     end
127
128   end
129
130   describe 'when listing neutron resources' do
131
132     it 'should exclude the column header' do
133       output = <<-EOT
134         id
135         net1
136         net2
137       EOT
138       klass.expects(:auth_neutron).returns(output)
139       result = klass.list_neutron_resources('foo')
140       result.should eql(['net1', 'net2'])
141     end
142
143     it 'should return empty list when there are no neutron resources' do
144       output = <<-EOT
145       EOT
146       klass.stubs(:auth_neutron).returns(output)
147       result = klass.list_neutron_resources('foo')
148       result.should eql([])
149     end
150
151     it 'should fail if resources list is nil' do
152       klass.stubs(:auth_neutron).returns(nil)
153       expect do
154         klass.list_neutron_resources('foo')
155       end.to raise_error(Puppet::Error, exec_error)
156     end
157
158   end
159
160   describe 'when retrieving attributes for neutron resources' do
161
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'})
166     end
167
168     it 'should parse multi-valued attributes into a key-list pair' do
169       output = <<-EOT
170 subnets="subnet1
171 subnet2
172 subnet3"
173       EOT
174       klass.expects(:auth_neutron).returns(output)
175       result = klass.get_neutron_resource_attrs('foo', 'id')
176       result.should eql({"subnets" => ['subnet1', 'subnet2', 'subnet3']})
177     end
178
179   end
180
181   describe 'when listing router ports' do
182
183     let :router do
184       'router1'
185     end
186
187     it 'should handle an empty port list' do
188       klass.expects(:auth_neutron).with('router-port-list',
189                                         '--format=csv',
190                                         router)
191       result = klass.list_router_ports(router)
192       result.should eql([])
193     end
194
195     it 'should handle several ports' do
196       output = <<-EOT
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""}"
200       EOT
201       expected =
202        [{ "fixed_ips"=>
203           "{\"subnet_id\": \"839a1d2d-2c6e-44fb-9a2b-9b011dce8c2f\", \
204 \"ip_address\": \"10.0.0.1\"}",
205           "name"=>"",
206           "subnet_id"=>"839a1d2d-2c6e-44fb-9a2b-9b011dce8c2f",
207           "id"=>"1345e576-a21f-4c2e-b24a-b245639852ab",
208           "mac_address"=>"fa:16:3e:e3:e6:38"},
209         {"fixed_ips"=>
210           "{\"subnet_id\": \"e4db0abd-276a-4f69-92ea-8b9e4eacfd43\", \
211 \"ip_address\": \"172.24.4.226\"}",
212           "name"=>"",
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).
218         returns(output)
219       result = klass.list_router_ports(router)
220       result.should eql(expected)
221     end
222
223   end
224
225   describe 'when parsing creation output' do
226
227     it 'should parse valid output into a hash' do
228       data = <<-EOT
229 Created a new network:
230 admin_state_up="True"
231 id="5f9cbed2-d31c-4e9c-be92-87229acb3f69"
232 name="foo"
233 tenant_id="3056a91768d948d399f1d79051a7f221"
234       EOT
235       expected = {
236         'admin_state_up' => 'True',
237         'id'             => '5f9cbed2-d31c-4e9c-be92-87229acb3f69',
238         'name'           => 'foo',
239         'tenant_id'      => '3056a91768d948d399f1d79051a7f221',
240       }
241       klass.parse_creation_output(data).should == expected
242     end
243
244   end
245
246 end