vagrant-openstack-provider 0.5.2 → 0.6.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (59) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +34 -0
  3. data/lib/vagrant-openstack-provider.rb +2 -31
  4. data/lib/vagrant-openstack-provider/action.rb +21 -7
  5. data/lib/vagrant-openstack-provider/action/abstract_action.rb +22 -0
  6. data/lib/vagrant-openstack-provider/action/connect_openstack.rb +19 -40
  7. data/lib/vagrant-openstack-provider/action/create_server.rb +10 -6
  8. data/lib/vagrant-openstack-provider/action/create_stack.rb +67 -0
  9. data/lib/vagrant-openstack-provider/action/delete_server.rb +28 -3
  10. data/lib/vagrant-openstack-provider/action/delete_stack.rb +72 -0
  11. data/lib/vagrant-openstack-provider/action/message.rb +4 -2
  12. data/lib/vagrant-openstack-provider/action/read_ssh_info.rb +4 -2
  13. data/lib/vagrant-openstack-provider/action/read_state.rb +9 -4
  14. data/lib/vagrant-openstack-provider/action/resume.rb +4 -2
  15. data/lib/vagrant-openstack-provider/action/start_server.rb +4 -2
  16. data/lib/vagrant-openstack-provider/action/stop_server.rb +4 -2
  17. data/lib/vagrant-openstack-provider/action/suspend.rb +4 -2
  18. data/lib/vagrant-openstack-provider/action/sync_folders.rb +17 -13
  19. data/lib/vagrant-openstack-provider/action/wait_accessible.rb +5 -2
  20. data/lib/vagrant-openstack-provider/action/wait_active.rb +5 -3
  21. data/lib/vagrant-openstack-provider/action/wait_stop.rb +5 -3
  22. data/lib/vagrant-openstack-provider/catalog/openstack_catalog.rb +66 -0
  23. data/lib/vagrant-openstack-provider/client/domain.rb +41 -1
  24. data/lib/vagrant-openstack-provider/client/glance.rb +63 -0
  25. data/lib/vagrant-openstack-provider/client/heat.rb +50 -0
  26. data/lib/vagrant-openstack-provider/client/http_utils.rb +18 -0
  27. data/lib/vagrant-openstack-provider/client/neutron.rb +9 -15
  28. data/lib/vagrant-openstack-provider/client/nova.rb +3 -3
  29. data/lib/vagrant-openstack-provider/client/openstack.rb +10 -0
  30. data/lib/vagrant-openstack-provider/command/abstract_command.rb +7 -0
  31. data/lib/vagrant-openstack-provider/command/image_list.rb +12 -2
  32. data/lib/vagrant-openstack-provider/command/main.rb +1 -0
  33. data/lib/vagrant-openstack-provider/command/network_list.rb +3 -3
  34. data/lib/vagrant-openstack-provider/command/subnet_list.rb +25 -0
  35. data/lib/vagrant-openstack-provider/config.rb +78 -7
  36. data/lib/vagrant-openstack-provider/config_resolver.rb +36 -5
  37. data/lib/vagrant-openstack-provider/errors.rb +30 -2
  38. data/lib/vagrant-openstack-provider/logging.rb +39 -0
  39. data/lib/vagrant-openstack-provider/version.rb +1 -1
  40. data/locales/en.yml +107 -4
  41. data/spec/vagrant-openstack-provider/action/connect_openstack_spec.rb +255 -8
  42. data/spec/vagrant-openstack-provider/action/create_server_spec.rb +6 -1
  43. data/spec/vagrant-openstack-provider/action/create_stack_spec.rb +97 -0
  44. data/spec/vagrant-openstack-provider/action/delete_server_spec.rb +34 -6
  45. data/spec/vagrant-openstack-provider/action/delete_stack_spec.rb +64 -0
  46. data/spec/vagrant-openstack-provider/action/read_state_spec.rb +13 -1
  47. data/spec/vagrant-openstack-provider/action/sync_folders_spec.rb +1 -0
  48. data/spec/vagrant-openstack-provider/action/wait_active_spec.rb +1 -1
  49. data/spec/vagrant-openstack-provider/action/wait_stop_spec.rb +1 -1
  50. data/spec/vagrant-openstack-provider/client/glance_spec.rb +128 -0
  51. data/spec/vagrant-openstack-provider/client/heat_spec.rb +124 -0
  52. data/spec/vagrant-openstack-provider/client/neutron_spec.rb +33 -1
  53. data/spec/vagrant-openstack-provider/client/nova_spec.rb +2 -2
  54. data/spec/vagrant-openstack-provider/command/image_list_spec.rb +75 -23
  55. data/spec/vagrant-openstack-provider/command/subnet_list_spec.rb +46 -0
  56. data/spec/vagrant-openstack-provider/config_resolver_spec.rb +85 -19
  57. data/spec/vagrant-openstack-provider/config_spec.rb +177 -1
  58. data/spec/vagrant-openstack-provider/spec_helper.rb +3 -0
  59. metadata +20 -2
@@ -40,11 +40,13 @@ module VagrantPlugins
40
40
  config = env[:machine].provider_config
41
41
  nova = env[:openstack_client].nova
42
42
  return config.floating_ip if config.floating_ip
43
- fail Errors::UnableToResolveFloatingIP unless config.floating_ip_pool
44
- nova.get_all_floating_ips(env).each do |single|
45
- return single.ip if single.pool == config.floating_ip_pool && single.instance_id.nil?
46
- end unless config.floating_ip_pool_always_allocate
47
- nova.allocate_floating_ip(env, config.floating_ip_pool).ip
43
+ fail Errors::UnableToResolveFloatingIP if config.floating_ip_pool.nil? || config.floating_ip_pool.empty?
44
+ @logger.debug 'Searching for available ips'
45
+ free_ip = search_free_ip(config, nova, env)
46
+ return free_ip unless free_ip.nil?
47
+ @logger.debug 'Allocate new ip anyway'
48
+ allocated_ip = allocate_ip(config, nova, env)
49
+ return allocated_ip unless allocated_ip.nil?
48
50
  end
49
51
 
50
52
  def resolve_keypair(env)
@@ -133,6 +135,35 @@ module VagrantPlugins
133
135
 
134
136
  private
135
137
 
138
+ def search_free_ip(config, nova, env)
139
+ @logger.debug 'Retrieving all allocated floating ips on tenant'
140
+ all_floating_ips = nova.get_all_floating_ips(env)
141
+ all_floating_ips.each do |floating_ip|
142
+ log_attach = floating_ip.instance_id ? "attached to #{floating_ip.instance_id}" : 'not attached'
143
+ @logger.debug "#{floating_ip.ip} #{log_attach}" if config.floating_ip_pool.include? floating_ip.pool
144
+ return floating_ip.ip if (config.floating_ip_pool.include? floating_ip.pool) && floating_ip.instance_id.nil?
145
+ end unless config.floating_ip_pool_always_allocate
146
+ @logger.debug 'No free ip found'
147
+ nil
148
+ end
149
+
150
+ def allocate_ip(config, nova, env)
151
+ allocation_error = nil
152
+ config.floating_ip_pool.each do |floating_ip_pool|
153
+ begin
154
+ @logger.debug "Allocating ip in pool #{floating_ip_pool}"
155
+ return nova.allocate_floating_ip(env, floating_ip_pool).ip
156
+ rescue Errors::VagrantOpenstackError => e
157
+ @logger.warn "Error allocating ip in pool #{floating_ip_pool} : #{e}"
158
+ allocation_error = e
159
+ next if e.extra_data[:code] == 404
160
+ raise allocation_error
161
+ end
162
+ end
163
+ @logger.warn 'Impossible to allocate a new IP'
164
+ fail allocation_error
165
+ end
166
+
136
167
  def generate_keypair(env)
137
168
  key = SSHKey.generate
138
169
  nova = env[:openstack_client].nova
@@ -8,6 +8,10 @@ module VagrantPlugins
8
8
  error_key(:default)
9
9
  end
10
10
 
11
+ class Timeout < VagrantOpenstackError
12
+ error_key(:timeout)
13
+ end
14
+
11
15
  class AuthenticationRequired < VagrantOpenstackError
12
16
  error_key(:authentication_required)
13
17
  end
@@ -20,8 +24,8 @@ module VagrantPlugins
20
24
  error_key(:bad_authentication_endpoint)
21
25
  end
22
26
 
23
- class MultipleApiVersion < VagrantOpenstackError
24
- error_key(:multiple_api_version)
27
+ class NoMatchingApiVersion < VagrantOpenstackError
28
+ error_key(:no_matching_api_version)
25
29
  end
26
30
 
27
31
  class CreateBadState < VagrantOpenstackError
@@ -132,6 +136,10 @@ module VagrantPlugins
132
136
  error_key(:instance_not_found)
133
137
  end
134
138
 
139
+ class StackNotFound < VagrantOpenstackError
140
+ error_key(:stack_not_found)
141
+ end
142
+
135
143
  class NetworkServiceUnavailable < VagrantOpenstackError
136
144
  error_key(:nerwork_service_unavailable)
137
145
  end
@@ -139,6 +147,26 @@ module VagrantPlugins
139
147
  class VolumeServiceUnavailable < VagrantOpenstackError
140
148
  error_key(:volume_service_unavailable)
141
149
  end
150
+
151
+ class FloatingIPAlreadyAssigned < VagrantOpenstackError
152
+ error_key(:floating_ip_already_assigned)
153
+ end
154
+
155
+ class FloatingIPNotAvailable < VagrantOpenstackError
156
+ error_key(:floating_ip_not_available)
157
+ end
158
+
159
+ class ServerStatusError < VagrantOpenstackError
160
+ error_key(:server_status_error)
161
+ end
162
+
163
+ class StackStatusError < VagrantOpenstackError
164
+ error_key(:stack_status_error)
165
+ end
166
+
167
+ class MissingNovaEndpoint < VagrantOpenstackError
168
+ error_key(:missing_nova_endpoint)
169
+ end
142
170
  end
143
171
  end
144
172
  end
@@ -0,0 +1,39 @@
1
+ module VagrantPlugins
2
+ module Openstack
3
+ module Logging
4
+ # This initializes the logging so that our logs are outputted at
5
+ # the same level as Vagrant core logs.
6
+ def self.init
7
+ # Initialize logging
8
+ level = nil
9
+ begin
10
+ level = Log4r.const_get(ENV['VAGRANT_LOG'].upcase)
11
+ rescue NameError
12
+ # This means that the logging constant wasn't found,
13
+ # which is fine. We just keep `level` as `nil`. But
14
+ # we tell the user.
15
+ begin
16
+ level = Log4r.const_get(ENV['VAGRANT_OPENSTACK_LOG'].upcase)
17
+ rescue NameError
18
+ level = nil
19
+ end
20
+ end
21
+
22
+ # Some constants, such as "true" resolve to booleans, so the
23
+ # above error checking doesn't catch it. This will check to make
24
+ # sure that the log level is an integer, as Log4r requires.
25
+ level = nil unless level.is_a?(Integer)
26
+
27
+ # Set the logging level
28
+ # logs as long as we have a valid level.
29
+ if level
30
+ logger = Log4r::Logger.new('vagrant_openstack')
31
+ out = Log4r::Outputter.stdout
32
+ out.formatter = Log4r::PatternFormatter.new(pattern: '%d | %5l | %m', date_pattern: '%Y-%m-%d %H:%M')
33
+ logger.outputters = out
34
+ logger.level = level
35
+ end
36
+ end
37
+ end
38
+ end
39
+ end
@@ -1,5 +1,5 @@
1
1
  module VagrantPlugins
2
2
  module Openstack
3
- VERSION = '0.5.2'
3
+ VERSION = '0.6.0'
4
4
  end
5
5
  end
data/locales/en.yml CHANGED
@@ -1,9 +1,32 @@
1
1
  en:
2
2
  vagrant_openstack:
3
+ global_error: |-
4
+ An unknow error happened in Vagrant OpenStack provider
5
+
6
+ To easily debug what happened, we recommend to set the environment
7
+ variable VAGRANT_OPENSTACK_LOG to debug
8
+
9
+ $ export VAGRANT_OPENSTACK_LOG=debug
10
+
11
+ If doing this does not help fixing your issue, there may be a bug
12
+ in the provider. Please submit an issue on Github at
13
+ https://github.com/ggiamarchi/vagrant-openstack-provider
14
+ with the stracktrace and the logs.
15
+
16
+ We are looking for feedback, so feel free to ask questions or
17
+ describe features you would like to see in this provider.
3
18
  already_created: |-
4
19
  The server is already created.
20
+ create_stack: |-
21
+ Creating Heat Stack.
22
+ delete_stack: |-
23
+ Deleting Heat Stack.
5
24
  already_suspended: |-
6
- The server is already suspended
25
+ The server is already suspended.
26
+ ongoing_task: |-
27
+ The server is currently running a task and cannot execute your request.
28
+ not_suspended: |-
29
+ The server is not currently "suspended" and cannot be resumed.
7
30
  deleting_server: |-
8
31
  Deleting server...
9
32
  finding_flavor: |-
@@ -40,6 +63,12 @@ en:
40
63
  Waiting for the server to stop...
41
64
  waiting_start: |-
42
65
  Waiting for the server to start...
66
+ waiting_deleted: |-
67
+ Waiting for the server to be deleted...
68
+ waiting_for_stack: |-
69
+ Waiting for Heat Stack to be created...
70
+ waiting_for_stack_deleted: |-
71
+ Waiting for Heat Stack to be created...
43
72
  warn_networks: |-
44
73
  Warning! The Openstack provider doesn't support any of the Vagrant
45
74
  high-level network configurations (`config.vm.network`). They
@@ -48,6 +77,12 @@ en:
48
77
  As Neutron endpoint is not available, the identifier '%{network}' is assumed to be an id (not a name).
49
78
  warn_volume_identifier_is_assumed_to_be_an_id: |-
50
79
  As Cinder endpoint is not available, the identifier '%{volume}' is assumed to be an id (not a name).
80
+ ssh_disabled_provisioning: |-
81
+ Provisioning will not be performed because provider config ssh_disabled is set to true
82
+ ssh_disabled_sync_folders: |-
83
+ Folders will not be synced because provider config ssh_disabled is set to true
84
+ disabled_sync_folders: |-
85
+ Sync folders are disabled in the provider configuration
51
86
 
52
87
  config:
53
88
  password_required: |-
@@ -56,6 +91,13 @@ en:
56
91
  A username is required.
57
92
  invalid_uri: |-
58
93
  The value for %{key} is not a valid URI: %{uri}
94
+ invalid_stack: |-
95
+ One of the stacks in the stacks provider configuration is invalid
96
+ The configuration option should have the following format:
97
+ os.stacks = [{
98
+ name: 'mystack',
99
+ template: '/path/to/heat_template.yml',
100
+ }]
59
101
  metadata_must_be_hash: |-
60
102
  Metadata must be a hash.
61
103
  keypair_name_required: |-
@@ -78,8 +120,14 @@ en:
78
120
  errors:
79
121
  default: |-
80
122
  %{message}
123
+ timeout: |-
124
+ Timeout occurred
81
125
  authentication_required: |-
82
126
  Authentication token is missing or no longer valid.
127
+ floating_ip_already_assigned: |-
128
+ Floating IP %{floating_ip} already assigned to another server
129
+ floating_ip_not_available: |-
130
+ Floating IP %{floating_ip} not available for this tenant
83
131
  authentication_failed: |-
84
132
  Authentication failed.
85
133
  bad_authentication_endpoint: |-
@@ -89,11 +137,11 @@ en:
89
137
  state: '%{state}', instead of properly booting. Run `vagrant status`
90
138
  to find out what can be done about this state, or `vagrant destroy`
91
139
  if you want to start over.
92
- multiple_api_version: |-
93
- Multiple version found for %{api_name} API
140
+ no_matching_api_version: |-
141
+ No matching version found for %{api_name} API
94
142
 
95
143
  %{version_list}
96
- You must specify the desired network API url by setting
144
+ You must specify the desired %{api_name} API url by setting
97
145
  the provider's property '%{url_property}'.
98
146
  no_matching_flavor: |-
99
147
  No matching flavor was found! Please check your flavor setting
@@ -158,12 +206,21 @@ en:
158
206
  Dashboard instead of using vagrant commands.
159
207
  We recommend using the command `vagrant openstack reset` to reset
160
208
  vagrant to a clear state
209
+ stack_not_found: |-
210
+ Heat stack not found
161
211
  nerwork_service_unavailable: |-
162
212
  Neutron service endpoint is not available, thus there is not way to retrieve
163
213
  network id from its name. You have to provide only ids in your Vagrantfile.
164
214
  volume_service_unavailable: |-
165
215
  Cinder service endpoint is not available, thus there is not way to retrieve
166
216
  volume id from its name. You have to provide only ids in your Vagrantfile.
217
+ server_status_error: |-
218
+ Server '%{server}' is in error status.
219
+ stack_status_error: |-
220
+ Heat Stack '%{stack}' is in error status.
221
+ missing_nova_endpoint: |-
222
+ Nova endpoint must either be present in your keystone service catalog or be
223
+ specified using the provider's attribute 'openstack_compute_url'
167
224
 
168
225
  states:
169
226
  short_active: |-
@@ -239,6 +296,50 @@ en:
239
296
  not created
240
297
  long_not_created: |-
241
298
  The server is not created. Run `vagrant up` to create it.
299
+ short_suspending: |-
300
+ suspending
301
+ long_suspending: |-
302
+ The server is active and currently suspending, either by request or necessity.
303
+ short_resuming: |-
304
+ resuming
305
+ long_resuming: |-
306
+ The server is suspended and currently resuming by request.
307
+ short_spawning: |-
308
+ spawning
309
+ long_spawning: |-
310
+ The server is not created yet but is currently spawning by request.
311
+ short_scheduling: |-
312
+ scheduling
313
+ long_scheduling: |-
314
+ The server is building and currently scheduling by necessity.
315
+ short_deleting: |-
316
+ deleting
317
+ long_deleting: |-
318
+ The server is created and currently deleting by request.
319
+ short_rebooting: |-
320
+ rebooting
321
+ long_rebooting: |-
322
+ The server is currently rebooting, either by request or necessity.
323
+ short_reboot_pending: |-
324
+ reboot_pending
325
+ long_reboot_pending: |-
326
+ The server reboot is pending by necessity.
327
+ short_reboot_started: |-
328
+ reboot_started
329
+ long_reboot_started: |-
330
+ The server is starting to reboot, either by request or necessity.
331
+ short_rebooting_hard: |-
332
+ rebooting_hard
333
+ long_rebooting_hard: |-
334
+ The server is currently hard rebooting, either by request.
335
+ short_reboot_pending_hard: |-
336
+ reboot_pending_hard
337
+ long_reboot_pending_hard: |-
338
+ The server hard reboot is pending by necessity.
339
+ short_reboot_started_hard: |-
340
+ reboot_started_hard
341
+ long_reboot_started_hard: |-
342
+ The server is starting to hard reboot by request.
242
343
 
243
344
  client:
244
345
  looking_for_available_endpoints: |-
@@ -261,6 +362,8 @@ en:
261
362
  List available flavors
262
363
  network_list_synopsis : |-
263
364
  List private networks in project
365
+ subnet_list_synopsis : |-
366
+ List subnets for available networks
264
367
  flaotingip_list_synopsis : |-
265
368
  List floating IP and floating IP pools
266
369
  volume_list_synopsis : |-
@@ -17,9 +17,11 @@ describe VagrantPlugins::Openstack::Action::ConnectOpenstack do
17
17
  config.stub(:openstack_compute_url) { nil }
18
18
  config.stub(:openstack_network_url) { nil }
19
19
  config.stub(:openstack_volume_url) { nil }
20
+ config.stub(:openstack_image_url) { nil }
20
21
  config.stub(:tenant_name) { 'testTenant' }
21
22
  config.stub(:username) { 'username' }
22
23
  config.stub(:password) { 'password' }
24
+ config.stub(:region) { nil }
23
25
  end
24
26
  end
25
27
 
@@ -42,6 +44,82 @@ describe VagrantPlugins::Openstack::Action::ConnectOpenstack do
42
44
  end
43
45
  end
44
46
 
47
+ let(:neutron_france) do
48
+ double.tap do |neutron|
49
+ neutron.stub(:get_api_version_list).with(anything) do
50
+ [
51
+ {
52
+ 'status' => 'CURRENT',
53
+ 'id' => 'v2.0',
54
+ 'links' => [
55
+ {
56
+ 'href' => 'http://france.neutron/v2.0',
57
+ 'rel' => 'self'
58
+ }
59
+ ]
60
+ }
61
+ ]
62
+ end
63
+ end
64
+ end
65
+
66
+ let(:glance) do
67
+ double.tap do |glance|
68
+ glance.stub(:get_api_version_list).with(anything) do
69
+ [
70
+ {
71
+ 'status' => 'CURRENT',
72
+ 'id' => 'v2.1',
73
+ 'links' => [
74
+ {
75
+ 'href' => 'http://glance/v2.0',
76
+ 'rel' => 'self'
77
+ }
78
+ ]
79
+ }
80
+ ]
81
+ end
82
+ end
83
+ end
84
+
85
+ let(:glance_v1) do
86
+ double.tap do |glance|
87
+ glance.stub(:get_api_version_list).with(anything) do
88
+ [
89
+ {
90
+ 'status' => 'CURRENT',
91
+ 'id' => 'v1.0',
92
+ 'links' => [
93
+ {
94
+ 'href' => 'http://glance/v1',
95
+ 'rel' => 'self'
96
+ }
97
+ ]
98
+ }
99
+ ]
100
+ end
101
+ end
102
+ end
103
+
104
+ let(:glance_france) do
105
+ double.tap do |glance|
106
+ glance.stub(:get_api_version_list).with(anything) do
107
+ [
108
+ {
109
+ 'status' => 'CURRENT',
110
+ 'id' => 'v2.1',
111
+ 'links' => [
112
+ {
113
+ 'href' => 'http://france.glance/v2.0',
114
+ 'rel' => 'self'
115
+ }
116
+ ]
117
+ }
118
+ ]
119
+ end
120
+ end
121
+ end
122
+
45
123
  let(:env) do
46
124
  Hash.new.tap do |env|
47
125
  env[:ui] = double
@@ -51,6 +129,7 @@ describe VagrantPlugins::Openstack::Action::ConnectOpenstack do
51
129
  env[:machine].stub(:provider_config) { config }
52
130
  env[:openstack_client] = double('openstack_client')
53
131
  env[:openstack_client].stub(:neutron) { neutron }
132
+ env[:openstack_client].stub(:glance) { glance }
54
133
  end
55
134
  end
56
135
 
@@ -86,6 +165,26 @@ describe VagrantPlugins::Openstack::Action::ConnectOpenstack do
86
165
  ],
87
166
  'type' => 'network',
88
167
  'name' => 'neutron'
168
+ },
169
+ {
170
+ 'endpoints' => [
171
+ {
172
+ 'publicURL' => 'http://cinder/v2/projectId',
173
+ 'id' => '2'
174
+ }
175
+ ],
176
+ 'type' => 'volume',
177
+ 'name' => 'cinder'
178
+ },
179
+ {
180
+ 'endpoints' => [
181
+ {
182
+ 'publicURL' => 'http://glance',
183
+ 'id' => '2'
184
+ }
185
+ ],
186
+ 'type' => 'image',
187
+ 'name' => 'glance'
89
188
  }
90
189
  ]
91
190
 
@@ -93,18 +192,103 @@ describe VagrantPlugins::Openstack::Action::ConnectOpenstack do
93
192
  keystone.stub(:authenticate).with(anything) { catalog }
94
193
  env[:openstack_client].stub(:keystone) { keystone }
95
194
  end
96
- env[:openstack_client].stub(:neutron) { neutron }
195
+ env[:openstack_client].stub(:neutron) { neutron }
196
+ env[:openstack_client].stub(:glance) { glance }
97
197
 
98
198
  @action.call(env)
99
199
 
100
200
  expect(env[:openstack_client].session.endpoints)
101
- .to eq(compute: 'http://nova/v2/projectId', network: 'http://neutron/v2.0')
201
+ .to eq(compute: 'http://nova/v2/projectId',
202
+ network: 'http://neutron/v2.0',
203
+ volume: 'http://cinder/v2/projectId',
204
+ image: 'http://glance/v2.0')
205
+ end
206
+ end
207
+
208
+ context 'with multiple regions' do
209
+ it 'read service catalog and stores endpoints URL for desired regions in session' do
210
+ catalog = [
211
+ {
212
+ 'endpoints' => [
213
+ {
214
+ 'publicURL' => 'http://france.nova/v2/projectId',
215
+ 'id' => '1',
216
+ 'region' => 'france'
217
+ },
218
+ {
219
+ 'publicURL' => 'http://us.nova/v2/projectId',
220
+ 'id' => '4',
221
+ 'region' => 'us'
222
+ }
223
+ ],
224
+ 'type' => 'compute',
225
+ 'name' => 'nova'
226
+ },
227
+ {
228
+ 'endpoints' => [
229
+ {
230
+ 'publicURL' => 'http://france.neutron',
231
+ 'id' => '2',
232
+ 'region' => 'france'
233
+ },
234
+ {
235
+ 'publicURL' => 'http://us.neutron',
236
+ 'id' => '5',
237
+ 'region' => 'us'
238
+ }
239
+ ],
240
+ 'type' => 'network',
241
+ 'name' => 'neutron'
242
+ },
243
+ {
244
+ 'endpoints' => [
245
+ {
246
+ 'publicURL' => 'http://france.glance',
247
+ 'id' => '3',
248
+ 'region' => 'france'
249
+ },
250
+ {
251
+ 'publicURL' => 'http://us.glance',
252
+ 'id' => '6',
253
+ 'region' => 'us'
254
+ }
255
+ ],
256
+ 'type' => 'image',
257
+ 'name' => 'glance'
258
+ }
259
+ ]
260
+
261
+ double.tap do |keystone|
262
+ keystone.stub(:authenticate).with(anything) { catalog }
263
+ env[:openstack_client].stub(:keystone) { keystone }
264
+ end
265
+
266
+ env[:openstack_client].stub(:neutron) { neutron_france }
267
+ env[:openstack_client].stub(:glance) { glance_france }
268
+ config.stub(:region) { 'france' }
269
+
270
+ @action.call(env)
271
+
272
+ expect(env[:openstack_client].session.endpoints)
273
+ .to eq(compute: 'http://france.nova/v2/projectId',
274
+ network: 'http://france.neutron/v2.0',
275
+ image: 'http://france.glance/v2.0')
102
276
  end
103
277
  end
104
278
 
105
279
  context 'with multiple endpoints for a service' do
106
280
  it 'takes the first one' do
107
281
  catalog = [
282
+ {
283
+ 'endpoints' => [
284
+ {
285
+ 'publicURL' => 'http://nova',
286
+ 'id' => '1'
287
+ }
288
+ ],
289
+ 'type' => 'compute',
290
+ 'name' => 'nova'
291
+ },
108
292
  {
109
293
  'endpoints' => [
110
294
  {
@@ -129,11 +313,74 @@ describe VagrantPlugins::Openstack::Action::ConnectOpenstack do
129
313
 
130
314
  @action.call(env)
131
315
 
132
- expect(env[:openstack_client].session.endpoints).to eq(network: 'http://neutron/v2.0')
316
+ expect(env[:openstack_client].session.endpoints).to eq(compute: 'http://nova', network: 'http://neutron/v2.0')
317
+ end
318
+ end
319
+
320
+ context 'with glance v1 only' do
321
+ it 'read service catalog and stores endpoints URL in session', :focus do
322
+ catalog = [
323
+ {
324
+ 'endpoints' => [
325
+ {
326
+ 'publicURL' => 'http://nova/v2/projectId',
327
+ 'id' => '1'
328
+ }
329
+ ],
330
+ 'type' => 'compute',
331
+ 'name' => 'nova'
332
+ },
333
+ {
334
+ 'endpoints' => [
335
+ {
336
+ 'publicURL' => 'http://glance',
337
+ 'id' => '2'
338
+ }
339
+ ],
340
+ 'type' => 'image',
341
+ 'name' => 'glance'
342
+ }
343
+ ]
344
+
345
+ double.tap do |keystone|
346
+ keystone.stub(:authenticate).with(anything) { catalog }
347
+ env[:openstack_client].stub(:keystone) { keystone }
348
+ end
349
+ env[:openstack_client].stub(:glance) { glance_v1 }
350
+
351
+ @action.call(env)
352
+
353
+ expect(env[:openstack_client].session.endpoints)
354
+ .to eq(compute: 'http://nova/v2/projectId',
355
+ image: 'http://glance/v1')
356
+ end
357
+ end
358
+
359
+ context 'with nova endpoint missing' do
360
+ it 'raise an error' do
361
+ catalog = [
362
+ {
363
+ 'endpoints' => [
364
+ {
365
+ 'publicURL' => 'http://keystone',
366
+ 'id' => '1'
367
+ }
368
+ ],
369
+ 'type' => 'identity',
370
+ 'name' => 'keystone'
371
+ }
372
+ ]
373
+
374
+ double.tap do |keystone|
375
+ keystone.stub(:authenticate).with(anything) { catalog }
376
+ env[:openstack_client].stub(:keystone) { keystone }
377
+ end
378
+
379
+ expect { @action.call(env) }.to raise_error Errors::MissingNovaEndpoint
133
380
  end
134
381
  end
135
382
 
136
- context 'with multiple versions for network service' do
383
+ context 'with no matching versions for network service' do
137
384
 
138
385
  let(:neutron) do
139
386
  double.tap do |neutron|
@@ -141,10 +388,10 @@ describe VagrantPlugins::Openstack::Action::ConnectOpenstack do
141
388
  [
142
389
  {
143
390
  'status' => 'CURRENT',
144
- 'id' => 'v2.0',
391
+ 'id' => 'v1.1',
145
392
  'links' => [
146
393
  {
147
- 'href' => 'http://neutron/v2.0',
394
+ 'href' => 'http://neutron/v1.1',
148
395
  'rel' => 'self'
149
396
  }
150
397
  ]
@@ -164,7 +411,7 @@ describe VagrantPlugins::Openstack::Action::ConnectOpenstack do
164
411
  end
165
412
  end
166
413
 
167
- it 'raise a MultipleApiVersion error' do
414
+ it 'raise an error' do
168
415
  catalog = [
169
416
  {
170
417
  'endpoints' => [
@@ -184,7 +431,7 @@ describe VagrantPlugins::Openstack::Action::ConnectOpenstack do
184
431
  end
185
432
  env[:openstack_client].stub(:neutron) { neutron }
186
433
 
187
- expect { @action.call(env) }.to raise_error(Errors::MultipleApiVersion)
434
+ expect { @action.call(env) }.to raise_error(Errors::NoMatchingApiVersion)
188
435
  end
189
436
  end
190
437