Skip to content
This repository
Browse code

Merge pull request #101 from cloudfoundry/openstack_fixes

Openstack fixes
  • Loading branch information...
commit 140ab42022c973c864b75e5d52452d924b16065b 2 parents 1fd7038 + f12939a
Amit Gupta Amit-PivotalLabs authored
47 bat/README.md
Source Rendered
@@ -72,6 +72,49 @@ properties:
72 72 security_groups:
73 73 - bat
74 74 ```
  75 +
  76 +On OpenStack with DHCP:
  77 +```yaml
  78 +---
  79 +cpi: openstack
  80 +properties:
  81 + static_ip: 54.235.115.62 # floating IP to use for the bat-release jobs
  82 + uuid: 25569986-a7ed-4529-ba84-8a03e2c6c78f # BAT_DIRECTOR UUID
  83 + pool_size: 1
  84 + stemcell:
  85 + name: bosh-stemcell
  86 + version: latest
  87 + instances: 1
  88 + key_name: bosh # OpenStack key name
  89 + mbus: nats://nats:0b450ada9f830085e2cdeff6@10.42.49.80:4222 # Not used now, but don't remove
  90 +```
  91 +
  92 +On OpenStack with manual networking (requires Quantum):
  93 +```yaml
  94 +---
  95 +cpi: openstack
  96 +properties:
  97 + static_ip: 54.235.115.62 # floating IP to use for the bat-release jobs
  98 + uuid: 25569986-a7ed-4529-ba84-8a03e2c6c78f # BAT_DIRECTOR UUID
  99 + pool_size: 1
  100 + stemcell:
  101 + name: bosh-stemcell
  102 + version: latest
  103 + instances: 1
  104 + key_name: bosh # OpenStack key name
  105 + mbus: nats://nats:0b450ada9f830085e2cdeff6@10.42.49.80:4222
  106 + network:
  107 + cidr: 10.0.1.0/24
  108 + reserved:
  109 + - 10.0.1.2 - 10.0.1.9
  110 + static:
  111 + - 10.0.1.10 - 10.0.1.30
  112 + gateway: 10.0.1.1
  113 + net_id: 4ef0b0ec-58c9-4478-8382-2099da773fdd #
  114 + security_groups:
  115 + - default
  116 +```
  117 +
75 118 ## EC2 Networking Config
76 119
77 120 ### On EC2 with AWS-provided DHCP networking
@@ -81,6 +124,10 @@ Add TCP port `4567` to the **default** security group.
81 124 Create a **bat** security group in the same VPC the BAT_DIRECTOR is running in. Allow inbound access to TCP ports
82 125 `22` and `4567` to the bat security group.
83 126
  127 +## OpenStack Networking Config
  128 +
  129 +Add TCP ports `22` and `4567` to the **default** security group.
  130 +
84 131 ## Running BAT
85 132
86 133 When all of the above is ready, running `bundle exec rake bat:env` will verify environment variables are set correctly.
45 bat/templates/openstack.yml.erb
@@ -7,11 +7,14 @@ release:
7 7 version: <%= properties.release || "latest" %>
8 8
9 9 compilation:
10   - workers: 1
  10 + workers: 2
11 11 network: default
12 12 reuse_compilation_vms: true
13 13 cloud_properties:
14 14 instance_type: m1.small
  15 + <% if properties.key_name %>
  16 + key_name: <%= properties.key_name %>
  17 + <% end %>
15 18
16 19 update:
17 20 canaries: <%= properties.canaries || 1 %>
@@ -21,22 +24,49 @@ update:
21 24 max_errors: 1
22 25
23 26 networks:
  27 +
  28 +- name: static
  29 + type: vip
  30 + cloud_properties: {}
  31 +
24 32 - name: default
  33 +<% if p('network.net_id', false) %>
  34 + type: manual
  35 + subnets:
  36 + - range: <%= properties.network.cidr %>
  37 + reserved:
  38 + <% properties.network.reserved.each do |range| %>
  39 + - <%= range %>
  40 + <% end %>
  41 + static:
  42 + <% properties.network.static.each do |range| %>
  43 + - <%= range %>
  44 + <% end %>
  45 + gateway: <%= properties.network.gateway %>
  46 + dns:
  47 + <% if_p('dns_nameserver') do |dns_nameserver| %>
  48 + - <%= dns_nameserver %>
  49 + <% end %>
  50 + <% if_p('network.dns') do |dns| %>
  51 + - <%= dns %>
  52 + <% end %>
  53 + cloud_properties:
  54 + security_groups: <%= p('network.security_groups') %>
  55 + net_id: <%= p('network.net_id') %>
  56 +<% else %>
25 57 type: dynamic
26 58 <% if_p('dns_nameserver') do |dns_nameserver| %>
27 59 dns:
28 60 - <%= dns_nameserver %>
29 61 <% end %>
30   - <% if properties.security_group %>
  62 + <% if properties.security_groups %>
31 63 cloud_properties:
32 64 security_groups:
33   - - <%= properties.security_group %>
  65 + - <%= properties.security_groups %>
34 66 <% else %>
35 67 cloud_properties: {}
36 68 <% end %>
37   -- name: static
38   - type: vip
39   - cloud_properties: {}
  69 +<% end %>
40 70
41 71 resource_pools:
42 72 - name: common
@@ -47,6 +77,9 @@ resource_pools:
47 77 version: <%= properties.stemcell.version %>
48 78 cloud_properties:
49 79 instance_type: m1.small
  80 + <% if properties.key_name %>
  81 + key_name: <%= properties.key_name %>
  82 + <% end %>
50 83 <% if properties.password %>
51 84 env:
52 85 bosh:
1  bosh_openstack_cpi/lib/cloud/openstack.rb
@@ -20,7 +20,6 @@ module OpenStackCloud; end
20 20 require "cloud"
21 21 require "cloud/openstack/helpers"
22 22 require "cloud/openstack/cloud"
23   -require "cloud/openstack/connection"
24 23 require "cloud/openstack/registry_client"
25 24 require "cloud/openstack/tag_manager"
26 25 require "cloud/openstack/version"
88 bosh_openstack_cpi/lib/cloud/openstack/cloud.rb
@@ -42,7 +42,7 @@ def initialize(options)
42 42 :openstack_region => @openstack_properties["region"],
43 43 :openstack_endpoint_type => @openstack_properties["endpoint_type"]
44 44 }
45   - @openstack = Connection.new(:compute, openstack_params)
  45 + @openstack = Fog::Compute.new(openstack_params)
46 46
47 47 glance_params = {
48 48 :provider => "OpenStack",
@@ -53,7 +53,7 @@ def initialize(options)
53 53 :openstack_region => @openstack_properties["region"],
54 54 :openstack_endpoint_type => @openstack_properties["endpoint_type"]
55 55 }
56   - @glance = Connection.new(:image, glance_params)
  56 + @glance = Fog::Image.new(glance_params)
57 57
58 58 registry_endpoint = @registry_properties["endpoint"]
59 59 registry_user = @registry_properties["user"]
@@ -175,15 +175,15 @@ def create_stemcell(image_path, cloud_properties)
175 175 def delete_stemcell(stemcell_id)
176 176 with_thread_name("delete_stemcell(#{stemcell_id})") do
177 177 @logger.info("Deleting stemcell `#{stemcell_id}'...")
178   - image = @glance.images.find_by_id(stemcell_id)
  178 + image = with_openstack { @glance.images.find_by_id(stemcell_id) }
179 179 if image
180 180 kernel_id = image.properties["kernel_id"]
181 181 if kernel_id
182   - kernel = @glance.images.find_by_id(kernel_id)
  182 + kernel = with_openstack { @glance.images.find_by_id(kernel_id) }
183 183 if kernel && kernel.properties["stemcell"]
184 184 if kernel.properties["stemcell"] == image.name
185 185 @logger.info("Deleting kernel `#{kernel_id}'...")
186   - kernel.destroy
  186 + with_openstack { kernel.destroy }
187 187 @logger.info("Kernel `#{kernel_id}' is now deleted")
188 188 end
189 189 end
@@ -191,17 +191,17 @@ def delete_stemcell(stemcell_id)
191 191
192 192 ramdisk_id = image.properties["ramdisk_id"]
193 193 if ramdisk_id
194   - ramdisk = @glance.images.find_by_id(ramdisk_id)
  194 + ramdisk = with_openstack { @glance.images.find_by_id(ramdisk_id) }
195 195 if ramdisk && ramdisk.properties["stemcell"]
196 196 if ramdisk.properties["stemcell"] == image.name
197 197 @logger.info("Deleting ramdisk `#{ramdisk_id}'...")
198   - ramdisk.destroy
  198 + with_openstack { ramdisk.destroy }
199 199 @logger.info("Ramdisk `#{ramdisk_id}' is now deleted")
200 200 end
201 201 end
202 202 end
203 203
204   - image.destroy
  204 + with_openstack { image.destroy }
205 205 @logger.info("Stemcell `#{stemcell_id}' is now deleted")
206 206 else
207 207 @logger.info("Stemcell `#{stemcell_id}' not found. Skipping.")
@@ -240,11 +240,11 @@ def create_vm(agent_id, stemcell_id, resource_pool,
240 240 nics = network_configurator.nics
241 241 @logger.debug("Using NICs: `#{nics.join(', ')}'")
242 242
243   - image = @openstack.images.find { |i| i.id == stemcell_id }
  243 + image = with_openstack { @openstack.images.find { |i| i.id == stemcell_id } }
244 244 cloud_error("Image `#{stemcell_id}' not found") if image.nil?
245 245 @logger.debug("Using image: `#{stemcell_id}'")
246 246
247   - flavor = @openstack.flavors.find { |f| f.name == resource_pool["instance_type"] }
  247 + flavor = with_openstack { @openstack.flavors.find { |f| f.name == resource_pool["instance_type"] } }
248 248 cloud_error("Flavor `#{resource_pool["instance_type"]}' not found") if flavor.nil?
249 249 @logger.debug("Using flavor: `#{resource_pool["instance_type"]}'")
250 250
@@ -262,7 +262,7 @@ def create_vm(agent_id, stemcell_id, resource_pool,
262 262 server_params[:availability_zone] = availability_zone if availability_zone
263 263
264 264 @logger.debug("Using boot parms: `#{server_params.inspect}'")
265   - server = @openstack.servers.create(server_params)
  265 + server = with_openstack { @openstack.servers.create(server_params) }
266 266
267 267 @logger.info("Creating new server `#{server.id}'...")
268 268 wait_resource(server, :active, :state)
@@ -286,9 +286,9 @@ def create_vm(agent_id, stemcell_id, resource_pool,
286 286 def delete_vm(server_id)
287 287 with_thread_name("delete_vm(#{server_id})") do
288 288 @logger.info("Deleting server `#{server_id}'...")
289   - server = @openstack.servers.get(server_id)
  289 + server = with_openstack { @openstack.servers.get(server_id) }
290 290 if server
291   - server.destroy
  291 + with_openstack { server.destroy }
292 292 wait_resource(server, :terminated, :state, true)
293 293
294 294 @logger.info("Deleting settings for server `#{server.id}'...")
@@ -306,7 +306,7 @@ def delete_vm(server_id)
306 306 # @return [Boolean] True if the vm exists
307 307 def has_vm?(server_id)
308 308 with_thread_name("has_vm?(#{server_id})") do
309   - server = @openstack.servers.get(server_id)
  309 + server = with_openstack { @openstack.servers.get(server_id) }
310 310 !server.nil?
311 311 end
312 312 end
@@ -318,7 +318,7 @@ def has_vm?(server_id)
318 318 # @return [void]
319 319 def reboot_vm(server_id)
320 320 with_thread_name("reboot_vm(#{server_id})") do
321   - server = @openstack.servers.get(server_id)
  321 + server = with_openstack { @openstack.servers.get(server_id) }
322 322 cloud_error("Server `#{server_id}' not found") unless server
323 323
324 324 soft_reboot(server)
@@ -336,12 +336,13 @@ def configure_networks(server_id, network_spec)
336 336 with_thread_name("configure_networks(#{server_id}, ...)") do
337 337 @logger.info("Configuring `#{server_id}' to use the following " \
338 338 "network settings: #{network_spec.pretty_inspect}")
339   -
340 339 network_configurator = NetworkConfigurator.new(network_spec)
341   - server = @openstack.servers.get(server_id)
342 340
343   - sg = @openstack.list_security_groups(server_id).body["security_groups"]
344   - actual = sg.collect { |s| s["name"] }.sort
  341 + server = with_openstack { @openstack.servers.get(server_id) }
  342 + cloud_error("Server `#{server_id}' not found") unless server
  343 +
  344 + sg = with_openstack { server.security_groups }
  345 + actual = sg.collect { |s| s.name }.sort
345 346 new = network_configurator.security_groups(@default_security_groups)
346 347
347 348 # If the security groups change, we need to recreate the VM
@@ -381,14 +382,14 @@ def create_disk(size, server_id = nil)
381 382 }
382 383
383 384 if server_id
384   - server = @openstack.servers.get(server_id)
  385 + server = with_openstack { @openstack.servers.get(server_id) }
385 386 if server && server.availability_zone
386 387 volume_params[:availability_zone] = server.availability_zone
387 388 end
388 389 end
389 390
390 391 @logger.info("Creating new volume...")
391   - volume = @openstack.volumes.create(volume_params)
  392 + volume = with_openstack { @openstack.volumes.create(volume_params) }
392 393
393 394 @logger.info("Creating new volume `#{volume.id}'...")
394 395 wait_resource(volume, :available)
@@ -406,14 +407,14 @@ def create_disk(size, server_id = nil)
406 407 def delete_disk(disk_id)
407 408 with_thread_name("delete_disk(#{disk_id})") do
408 409 @logger.info("Deleting volume `#{disk_id}'...")
409   - volume = @openstack.volumes.get(disk_id)
  410 + volume = with_openstack { @openstack.volumes.get(disk_id) }
410 411 if volume
411 412 state = volume.status
412 413 if state.to_sym != :available
413 414 cloud_error("Cannot delete volume `#{disk_id}', state is #{state}")
414 415 end
415 416
416   - volume.destroy
  417 + with_openstack { volume.destroy }
417 418 wait_resource(volume, :deleted, :status, true)
418 419 else
419 420 @logger.info("Volume `#{disk_id}' not found. Skipping.")
@@ -429,10 +430,10 @@ def delete_disk(disk_id)
429 430 # @return [void]
430 431 def attach_disk(server_id, disk_id)
431 432 with_thread_name("attach_disk(#{server_id}, #{disk_id})") do
432   - server = @openstack.servers.get(server_id)
  433 + server = with_openstack { @openstack.servers.get(server_id) }
433 434 cloud_error("Server `#{server_id}' not found") unless server
434 435
435   - volume = @openstack.volumes.get(disk_id)
  436 + volume = with_openstack { @openstack.volumes.get(disk_id) }
436 437 cloud_error("Volume `#{disk_id}' not found") unless volume
437 438
438 439 device_name = attach_volume(server, volume)
@@ -453,10 +454,10 @@ def attach_disk(server_id, disk_id)
453 454 # @return [void]
454 455 def detach_disk(server_id, disk_id)
455 456 with_thread_name("detach_disk(#{server_id}, #{disk_id})") do
456   - server = @openstack.servers.get(server_id)
  457 + server = with_openstack { @openstack.servers.get(server_id) }
457 458 cloud_error("Server `#{server_id}' not found") unless server
458 459
459   - volume = @openstack.volumes.get(disk_id)
  460 + volume = with_openstack { @openstack.volumes.get(disk_id) }
460 461 cloud_error("Volume `#{disk_id}' not found") unless volume
461 462
462 463 detach_volume(server, volume)
@@ -477,11 +478,13 @@ def detach_disk(server_id, disk_id)
477 478 # @return [void]
478 479 def set_vm_metadata(server_id, metadata)
479 480 with_thread_name("set_vm_metadata(#{server_id}, ...)") do
480   - server = @openstack.servers.get(server_id)
481   - cloud_error("Server `#{server_id}' not found") unless server
  481 + with_openstack do
  482 + server = @openstack.servers.get(server_id)
  483 + cloud_error("Server `#{server_id}' not found") unless server
482 484
483   - metadata.each do |name, value|
484   - TagManager.tag(server, name, value)
  485 + metadata.each do |name, value|
  486 + TagManager.tag(server, name, value)
  487 + end
485 488 end
486 489 end
487 490 end
@@ -506,7 +509,7 @@ def validate_deployment(old_manifest, new_manifest)
506 509 # @note this is a private method that is public to make it easier to test
507 510 def select_availability_zone(volumes, resource_pool_az)
508 511 if volumes && !volumes.empty?
509   - disks = volumes.map { |vid| @openstack.volumes.get(vid) }
  512 + disks = volumes.map { |vid| with_openstack { @openstack.volumes.get(vid) } }
510 513 ensure_same_availability_zone(disks, resource_pool_az)
511 514 disks.first.availability_zone
512 515 else
@@ -627,7 +630,7 @@ def update_agent_settings(server)
627 630 # @return [void]
628 631 def soft_reboot(server)
629 632 @logger.info("Soft rebooting server `#{server.id}'...")
630   - server.reboot
  633 + with_openstack { server.reboot }
631 634 wait_resource(server, :active, :state)
632 635 end
633 636
@@ -638,7 +641,7 @@ def soft_reboot(server)
638 641 # @return [void]
639 642 def hard_reboot(server)
640 643 @logger.info("Hard rebooting server `#{server.id}'...")
641   - server.reboot(type = 'HARD')
  644 + with_openstack { server.reboot(type = 'HARD') }
642 645 wait_resource(server, :active, :state)
643 646 end
644 647
@@ -649,8 +652,7 @@ def hard_reboot(server)
649 652 # @param [Fog::Compute::OpenStack::Volume] volume OpenStack volume
650 653 # @return [String] Device name
651 654 def attach_volume(server, volume)
652   - volume_attachments = @openstack.get_server_volumes(server.id).
653   - body['volumeAttachments']
  655 + volume_attachments = with_openstack { @openstack.get_server_volumes(server.id).body['volumeAttachments'] }
654 656 device_names = Set.new(volume_attachments.collect! { |v| v["device"] })
655 657
656 658 new_attachment = nil
@@ -662,10 +664,9 @@ def attach_volume(server, volume)
662 664 end
663 665 @logger.info("Attaching volume `#{volume.id}' to `#{server.id}', " \
664 666 "device name is `#{dev_name}'")
665   - if volume.attach(server.id, dev_name)
666   - wait_resource(volume, :"in-use")
667   - new_attachment = dev_name
668   - end
  667 + with_openstack { volume.attach(server.id, dev_name) }
  668 + wait_resource(volume, :"in-use")
  669 + new_attachment = dev_name
669 670 break
670 671 end
671 672 cloud_error("Server has too many disks attached") if new_attachment.nil?
@@ -680,8 +681,7 @@ def attach_volume(server, volume)
680 681 # @param [Fog::Compute::OpenStack::Volume] volume OpenStack volume
681 682 # @return [void]
682 683 def detach_volume(server, volume)
683   - volume_attachments = @openstack.get_server_volumes(server.id).
684   - body['volumeAttachments']
  684 + volume_attachments = with_openstack { @openstack.get_server_volumes(server.id).body['volumeAttachments'] }
685 685 device_map = volume_attachments.collect! { |v| v["volumeId"] }
686 686
687 687 unless device_map.include?(volume.id)
@@ -690,7 +690,7 @@ def detach_volume(server, volume)
690 690 end
691 691
692 692 @logger.info("Detaching volume `#{volume.id}' from `#{server.id}'...")
693   - volume.detach(server.id, volume.id)
  693 + with_openstack { volume.detach(server.id, volume.id) }
694 694 wait_resource(volume, :available)
695 695 end
696 696
@@ -702,7 +702,7 @@ def detach_volume(server, volume)
702 702 def upload_image(image_params)
703 703 @logger.info("Creating new image...")
704 704 started_at = Time.now
705   - image = @glance.images.create(image_params)
  705 + image = with_openstack { @glance.images.create(image_params) }
706 706 total = Time.now - started_at
707 707 @logger.info("Created new image `#{image.id}', took #{total}s")
708 708
48 bosh_openstack_cpi/lib/cloud/openstack/connection.rb
... ... @@ -1,48 +0,0 @@
1   -# Copyright (c) 2009-2013 VMware, Inc.
2   -
3   -module Bosh::OpenStackCloud
4   - class Connection
5   - include Helpers
6   -
7   - MAX_RETRYAFTER_TIME = 10 # Max number of seconds before retrying a call
8   -
9   - def initialize(service, params = {})
10   - @logger = Bosh::Clouds::Config.logger
11   - case service
12   - when :compute then @connection = Fog::Compute.new(params)
13   - when :image then @connection = Fog::Image.new(params)
14   - else cloud_error("Service #{service} not supported by OpenStack CPI")
15   - end
16   - end
17   -
18   - # Delegate all methods to Fog.
19   - def method_missing(method, *arguments, &block)
20   - return super unless @connection.respond_to?(method)
21   -
22   - begin
23   - @connection.send(method, *arguments, &block)
24   - rescue Excon::Errors::RequestEntityTooLarge => e
25   - # If we find a rate limit error, parse message, wait, and retry
26   - retried = false
27   - unless e.response.body.empty?
28   - begin
29   - message = JSON.parse(e.response.body)
30   - if message["overLimit"] && message["overLimit"]["retryAfter"]
31   - retryafter = message["overLimit"]["retryAfter"]
32   - wait_time = [MAX_RETRYAFTER_TIME, retryafter.to_i].min
33   - task_checkpoint
34   - @logger.debug("OpenStack API overLimit, waiting #{wait_time} " +
35   - "seconds before retrying") if @logger
36   - sleep(wait_time)
37   - retried = true
38   - retry
39   - end
40   - rescue JSON::ParserError
41   - # do nothing
42   - end
43   - end
44   - raise e unless retried
45   - end
46   - end
47   - end
48   -end
29 bosh_openstack_cpi/lib/cloud/openstack/helpers.rb
@@ -6,6 +6,8 @@ module Bosh::OpenStackCloud
6 6 module Helpers
7 7
8 8 DEFAULT_TIMEOUT = 600 # Default timeout for target state (in seconds)
  9 + MAX_RETRIES = 10 # Max number of retries
  10 + DEFAULT_RETRY_TIMEOUT = 1 # Default timeout before retrying a call (in seconds)
9 11
10 12 ##
11 13 # Raises CloudError exception
@@ -16,6 +18,33 @@ def cloud_error(message)
16 18 raise Bosh::Clouds::CloudError, message
17 19 end
18 20
  21 + def with_openstack
  22 + retries = 0
  23 + begin
  24 + yield
  25 + rescue Excon::Errors::RequestEntityTooLarge => e
  26 + # If we find a rate limit error, parse message, wait, and retry
  27 + unless e.response.body.empty? || retries >= MAX_RETRIES
  28 + begin
  29 + message = JSON.parse(e.response.body)
  30 + overlimit = message["overLimit"] || message["overLimitFault"]
  31 + if overlimit
  32 + task_checkpoint
  33 + wait_time = overlimit["retryAfter"] || DEFAULT_RETRY_TIMEOUT
  34 + @logger.debug("OpenStack API overLimit, waiting #{wait_time} " +
  35 + "seconds before retrying") if @logger
  36 + sleep(wait_time.to_i)
  37 + retries += 1
  38 + retry
  39 + end
  40 + rescue JSON::ParserError
  41 + # do nothing
  42 + end
  43 + end
  44 + raise e
  45 + end
  46 + end
  47 +
19 48 ##
20 49 # Waits for a resource to be on a target state
21 50 #
14 bosh_openstack_cpi/lib/cloud/openstack/network_configurator.rb
@@ -73,12 +73,14 @@ def configure(openstack, server)
73 73 else
74 74 # If there is no vip network we should disassociate any floating IP
75 75 # currently held by server (as it might have had floating IP before)
76   - addresses = openstack.addresses
77   - addresses.each do |address|
78   - if address.instance_id == server.id
79   - @logger.info("Disassociating floating IP `#{address.ip}' " \
80   - "from server `#{server.id}'")
81   - address.server = nil
  76 + with_openstack do
  77 + addresses = openstack.addresses
  78 + addresses.each do |address|
  79 + if address.instance_id == server.id
  80 + @logger.info("Disassociating floating IP `#{address.ip}' " \
  81 + "from server `#{server.id}'")
  82 + address.server = nil
  83 + end
82 84 end
83 85 end
84 86 end
26 bosh_openstack_cpi/lib/cloud/openstack/vip_network.rb
@@ -29,19 +29,21 @@ def configure(openstack, server)
29 29
30 30 # Check if the OpenStack floating IP is allocated. If true, disassociate
31 31 # it from any server before associating it to the new server
32   - address = openstack.addresses.find { |a| a.ip == @ip }
33   - if address
34   - unless address.instance_id.nil?
35   - @logger.info("Disassociating floating IP `#{@ip}' " \
36   - "from server `#{address.instance_id}'")
37   - address.server = nil
38   - end
  32 + with_openstack do
  33 + address = openstack.addresses.find { |a| a.ip == @ip }
  34 + if address
  35 + unless address.instance_id.nil?
  36 + @logger.info("Disassociating floating IP `#{@ip}' " \
  37 + "from server `#{address.instance_id}'")
  38 + address.server = nil
  39 + end
39 40
40   - @logger.info("Associating server `#{server.id}' " \
41   - "with floating IP `#{@ip}'")
42   - address.server = server
43   - else
44   - cloud_error("Floating IP #{@ip} not allocated")
  41 + @logger.info("Associating server `#{server.id}' " \
  42 + "with floating IP `#{@ip}'")
  43 + address.server = server
  44 + else
  45 + cloud_error("Floating IP #{@ip} not allocated")
  46 + end
45 47 end
46 48 end
47 49
8 bosh_openstack_cpi/spec/spec_helper.rb
@@ -62,10 +62,10 @@ def mock_cloud(options = nil)
62 62 addresses = double("addresses")
63 63 snapshots = double("snapshots")
64 64
65   - glance = double(Bosh::OpenStackCloud::Connection, :service => :image)
  65 + glance = double(Fog::Image)
66 66 Fog::Image.stub(:new).and_return(glance)
67 67
68   - openstack = double(Bosh::OpenStackCloud::Connection, :service => :compute)
  68 + openstack = double(Fog::Compute)
69 69
70 70 openstack.stub(:servers).and_return(servers)
71 71 openstack.stub(:images).and_return(images)
@@ -84,10 +84,10 @@ def mock_cloud(options = nil)
84 84 def mock_glance(options = nil)
85 85 images = double("images")
86 86
87   - openstack = double(Bosh::OpenStackCloud::Connection, :service => :compute)
  87 + openstack = double(Fog::Compute)
88 88 Fog::Compute.stub(:new).and_return(openstack)
89 89
90   - glance = double(Bosh::OpenStackCloud::Connection, :service => :image)
  90 + glance = double(Fog::Image)
91 91 glance.stub(:images).and_return(images)
92 92
93 93 Fog::Image.stub(:new).and_return(glance)
3  bosh_openstack_cpi/spec/unit/cloud_spec.rb
@@ -8,7 +8,8 @@
8 8 describe "creating via provider" do
9 9
10 10 it "can be created using Bosh::Cloud::Provider" do
11   - Bosh::OpenStackCloud::Connection.stub(:new)
  11 + Fog::Compute.stub(:new)
  12 + Fog::Image.stub(:new)
12 13 cloud = Bosh::Clouds::Provider.create(:openstack, mock_cloud_options)
13 14 cloud.should be_an_instance_of(Bosh::OpenStackCloud::Cloud)
14 15 end
21 bosh_openstack_cpi/spec/unit/configure_networks_spec.rb
@@ -11,13 +11,12 @@
11 11
12 12 it "forces recreation when security groups differ" do
13 13 server = double("server", :id => "i-test", :name => "i-test")
14   - sec_grp = double("security_group",
15   - :body => {"security_groups" => [{"name"=> "newgroup" }]})
  14 + security_group = double("security_groups", :name => "newgroups")
  15 +
  16 + server.should_receive(:security_groups).and_return([security_group])
16 17
17 18 cloud = mock_cloud do |openstack|
18 19 openstack.servers.should_receive(:get).with("i-test").and_return(server)
19   - openstack.should_receive(:list_security_groups).
20   - with("i-test").and_return(sec_grp)
21 20 end
22 21
23 22 expect {
@@ -29,14 +28,13 @@
29 28 server = double("server", :id => "i-test", :name => "i-test")
30 29 address = double("address", :id => "a-test", :ip => "10.0.0.1",
31 30 :instance_id => nil)
32   - sec_grp = double("security_group",
33   - :body => {"security_groups" => [{"name"=> "default" }]})
  31 + security_group = double("security_groups", :name => "default")
  32 +
  33 + server.should_receive(:security_groups).and_return([security_group])
34 34
35 35 cloud = mock_cloud do |openstack|
36 36 openstack.servers.should_receive(:get).with("i-test").and_return(server)
37 37 openstack.addresses.should_receive(:find).and_return(address)
38   - openstack.should_receive(:list_security_groups).
39   - with("i-test").and_return(sec_grp)
40 38 end
41 39
42 40 address.should_receive(:server=).with(server)
@@ -55,14 +53,13 @@
55 53 server = double("server", :id => "i-test", :name => "i-test")
56 54 address = double("address", :id => "a-test", :ip => "10.0.0.1",
57 55 :instance_id => "i-test")
58   - sec_grp = double("security_group",
59   - :body => {"security_groups" => [{"name"=> "default" }]})
  56 + security_group = double("security_groups", :name => "default")
  57 +
  58 + server.should_receive(:security_groups).and_return([security_group])
60 59
61 60 cloud = mock_cloud do |openstack|
62 61 openstack.servers.should_receive(:get).with("i-test").and_return(server)
63 62 openstack.addresses.should_receive(:each).and_yield(address)
64   - openstack.should_receive(:list_security_groups).
65   - with("i-test").and_return(sec_grp)
66 63 end
67 64
68 65 address.should_receive(:server=).with(nil)
132 bosh_openstack_cpi/spec/unit/connection_spec.rb
... ... @@ -1,132 +0,0 @@
1   -# Copyright (c) 2009-2013 VMware, Inc.
2   -
3   -require "spec_helper"
4   -
5   -describe Bosh::OpenStackCloud::Connection do
6   - before(:each) do
7   - Bosh::Clouds::Config.stub(:task_checkpoint)
8   - end
9   -
10   - describe "Compute service" do
11   - before(:each) do
12   - @fog_compute = double(Fog::Compute)
13   - Fog::Compute.stub(:new).and_return(@fog_compute)
14   - @connection = Bosh::OpenStackCloud::Connection.new(:compute, {})
15   - end
16   -
17   - it "should respond to a Fog::Compute method" do
18   - @fog_compute.should_receive(:respond_to?).with(:servers).and_return(true)
19   - @fog_compute.should_receive(:servers)
20   -
21   - @connection.servers
22   - end
23   -
24   - it "should raise an error if Fog::Compute doesn't respond to a method" do
25   - @fog_compute.should_receive(:respond_to?).with(:pair).and_return(false)
26   -
27   - expect {
28   - @connection.pair
29   - }.to raise_error(NoMethodError, /undefined method `pair' for/)
30   - end
31   - end
32   -
33   - describe "Image service" do
34   - before(:each) do
35   - @fog_image = double(Fog::Image)
36   - Fog::Image.stub(:new).and_return(@fog_image)
37   - @connection = Bosh::OpenStackCloud::Connection.new(:image, {})
38   - end
39   -
40   - it "should respond to a Fog::Image method" do
41   - @fog_image.should_receive(:respond_to?).with(:images).and_return(true)
42   - @fog_image.should_receive(:images)
43   -
44   - @connection.images
45   - end
46   -
47   - it "should raise an error if Fog::Image doesn't respond to a method" do
48   - @fog_image.should_receive(:respond_to?).with(:pair).and_return(false)
49   -
50   - expect {
51   - @connection.pair
52   - }.to raise_error(NoMethodError, /undefined method `pair' for/)
53   - end
54   - end
55   -
56   - describe "Unknow service" do
57   - it "should raise an unsupported service error" do
58   - expect {
59   - Bosh::OpenStackCloud::Connection.new(:pair, {})
60   - }.to raise_error(Bosh::Clouds::CloudError, /Service pair not supported by OpenStack CPI/)
61   - end
62   - end
63   -
64   - describe "RequestEntityTooLarge exception" do
65   - before(:each) do
66   - @fog_compute = double(Fog::Compute)
67   - Fog::Compute.stub(:new).and_return(@fog_compute)
68   - @connection = Bosh::OpenStackCloud::Connection.new(:compute, {})
69   - end
70   -
71   - it "should retry after the amount of seconds received at the response body" do
72   - body = { "overLimit" => {
73   - "message" => "This request was rate-limited.",
74   - "code" => 413,
75   - "retryAfter" => "1",
76   - "details" => "Only 10 POST request(s) can be made to * every minute."}
77   - }
78   - response = Excon::Response.new(:body => JSON.dump(body))
79   -
80   - @fog_compute.should_receive(:respond_to?).with(:servers).and_return(true)
81   - @fog_compute.should_receive(:servers).
82   - and_raise(Excon::Errors::RequestEntityTooLarge.new("", "", response))
83   - @connection.should_receive(:sleep).with(1)
84   - @fog_compute.should_receive(:servers)
85   -
86   - @connection.servers
87   - end
88   -
89   - it "should retry after the max number of seconds before retrying a call" do
90   - body = { "overLimit" => {
91   - "message" => "This request was rate-limited.",
92   - "code" => 413,
93   - "retryAfter" => "20",
94   - "details" => "Only 10 POST request(s) can be made to * every minute."}
95   - }
96   - response = Excon::Response.new(:body => JSON.dump(body))
97   -
98   - @fog_compute.should_receive(:respond_to?).with(:servers).and_return(true)
99   - @fog_compute.should_receive(:servers).
100   - and_raise(Excon::Errors::RequestEntityTooLarge.new("", "", response))
101   - @connection.should_receive(:sleep).with(10)
102   - @fog_compute.should_receive(:servers)
103   -
104   - @connection.servers
105   - end
106   -
107   - it "should raise an error if response has no body" do
108   - response = Excon::Response.new(:body => "")
109   -
110   - @fog_compute.should_receive(:respond_to?).with(:servers).and_return(true)
111   - @fog_compute.should_receive(:servers).
112   - and_raise(Excon::Errors::RequestEntityTooLarge.new("", "", response))
113   -
114   - expect {
115   - @connection.servers
116   - }.to raise_error(Excon::Errors::RequestEntityTooLarge)
117   - end
118   -
119   - it "should raise an error if response has no overlimit message" do
120   - body = "This request was rate-limited."
121   - response = Excon::Response.new(:body => JSON.dump(body))
122   -
123   - @fog_compute.should_receive(:respond_to?).with(:servers).and_return(true)
124   - @fog_compute.should_receive(:servers).
125   - and_raise(Excon::Errors::RequestEntityTooLarge.new("", "", response))
126   -
127   - expect {
128   - @connection.servers
129   - }.to raise_error(Excon::Errors::RequestEntityTooLarge)
130   - end
131   - end
132   -end
220 bosh_openstack_cpi/spec/unit/helpers_spec.rb
@@ -5,71 +5,169 @@
5 5
6 6 describe Bosh::OpenStackCloud::Helpers do
7 7 before(:each) do
  8 + @cloud = mock_cloud
8 9 Bosh::Clouds::Config.stub(:task_checkpoint)
9 10 end
10 11
11   - it "should time out" do
12   - cloud = mock_cloud
13   -
14   - resource = double("resource")
15   - resource.stub(:id).and_return("foobar")
16   - resource.stub(:reload).and_return(cloud)
17   - resource.stub(:status).and_return(:start)
18   - cloud.stub(:sleep)
19   -
20   - expect {
21   - cloud.wait_resource(resource, :stop, :status, false, 0.1)
22   - }.to raise_error Bosh::Clouds::CloudError, /Timed out/
23   - end
24   -
25   - it "should not time out" do
26   - cloud = mock_cloud
27   -
28   - resource = double("resource")
29   - resource.stub(:id).and_return("foobar")
30   - resource.stub(:reload).and_return(cloud)
31   - resource.stub(:status).and_return(:start, :stop)
32   - cloud.stub(:sleep)
33   -
34   - cloud.wait_resource(resource, :stop, :status, false, 0.1)
35   - end
36   -
37   - it "should raise Bosh::Clouds::CloudError if state is error" do
38   - cloud = mock_cloud
39   -
40   - resource = double("resource")
41   - resource.stub(:id).and_return("foobar")
42   - resource.stub(:reload).and_return(cloud)
43   - resource.stub(:status).and_return(:error)
44   - cloud.stub(:sleep)
45   -
46   - expect {
47   - cloud.wait_resource(resource, :stop, :status, false, 0.1)
48   - }.to raise_error Bosh::Clouds::CloudError, /state is error/
49   - end
50   -
51   - it "should raise Bosh::Clouds::CloudError if resource not found" do
52   - cloud = mock_cloud
53   -
54   - resource = double("resource")
55   - resource.stub(:id).and_return("foobar")
56   - resource.stub(:reload).and_return(nil)
57   - cloud.stub(:sleep)
58   -
59   - expect {
60   - cloud.wait_resource(resource, :deleted, :status, false, 0.1)
61   - }.to raise_error Bosh::Clouds::CloudError, /Resource not found/
  12 + describe "wait_resource" do
  13 + it "should time out" do
  14 + resource = double("resource")
  15 + resource.stub(:id).and_return("foobar")
  16 + resource.stub(:reload).and_return(@cloud)
  17 + resource.stub(:status).and_return(:start)
  18 + @cloud.stub(:sleep)
  19 +
  20 + expect {
  21 + @cloud.wait_resource(resource, :stop, :status, false, 0.1)
  22 + }.to raise_error Bosh::Clouds::CloudError, /Timed out/
  23 + end
  24 +
  25 + it "should not time out" do
  26 + resource = double("resource")
  27 + resource.stub(:id).and_return("foobar")
  28 + resource.stub(:reload).and_return(@cloud)
  29 + resource.stub(:status).and_return(:start, :stop)
  30 + @cloud.stub(:sleep)
  31 +
  32 + @cloud.wait_resource(resource, :stop, :status, false, 0.1)
  33 + end
  34 +
  35 + it "should raise Bosh::Clouds::CloudError if state is error" do
  36 + resource = double("resource")
  37 + resource.stub(:id).and_return("foobar")
  38 + resource.stub(:reload).and_return(@cloud)
  39 + resource.stub(:status).and_return(:error)
  40 + @cloud.stub(:sleep)
  41 +
  42 + expect {
  43 + @cloud.wait_resource(resource, :stop, :status, false, 0.1)
  44 + }.to raise_error Bosh::Clouds::CloudError, /state is error/
  45 + end
  46 +
  47 + it "should raise Bosh::Clouds::CloudError if resource not found" do
  48 + resource = double("resource")
  49 + resource.stub(:id).and_return("foobar")
  50 + resource.stub(:reload).and_return(nil)
  51 + @cloud.stub(:sleep)
  52 +
  53 + expect {
  54 + @cloud.wait_resource(resource, :deleted, :status, false, 0.1)
  55 + }.to raise_error Bosh::Clouds::CloudError, /Resource not found/
  56 + end
  57 +
  58 + it "should not raise and exception if resource not found" do
  59 + resource = double("resource")
  60 + resource.stub(:id).and_return("foobar")
  61 + resource.stub(:reload).and_return(nil)
  62 + resource.stub(:status).and_return(:deleted)
  63 + @cloud.stub(:sleep)
  64 +
  65 + @cloud.wait_resource(resource, :deleted, :status, true, 0.1)
  66 + end
62 67 end
63 68
64   - it "should not raise and exception if resource not found" do
65   - cloud = mock_cloud
66   -
67   - resource = double("resource")
68   - resource.stub(:id).and_return("foobar")
69   - resource.stub(:reload).and_return(nil)
70   - resource.stub(:status).and_return(:deleted)
71   - cloud.stub(:sleep)
72   -
73   - cloud.wait_resource(resource, :deleted, :status, true, 0.1)
  69 + describe "with_openstack" do
  70 + before(:each) do
  71 + @openstack = double("openstack")
  72 + end
  73 +
  74 + it "should raise the exception if not RequestEntityTooLarge exception" do
  75 + response = Excon::Response.new(:body => "")
  76 +
  77 + @openstack.should_receive(:servers)
  78 + .and_raise(Bosh::Clouds::CloudError)
  79 + @cloud.should_not_receive(:sleep)
  80 +
  81 + expect {
  82 + @cloud.with_openstack do
  83 + @openstack.servers
  84 + end
  85 + }.to raise_error(Bosh::Clouds::CloudError)
  86 + end
  87 +
  88 + it "should raise the exception if response has no body" do
  89 + response = Excon::Response.new(:body => "")
  90 +
  91 + @openstack.should_receive(:servers)
  92 + .and_raise(Excon::Errors::RequestEntityTooLarge.new("", "", response))
  93 + @cloud.should_not_receive(:sleep)
  94 +
  95 + expect {
  96 + @cloud.with_openstack do
  97 + @openstack.servers
  98 + end
  99 + }.to raise_error(Excon::Errors::RequestEntityTooLarge)
  100 + end
  101 +
  102 + it "should raise the exception if response is not JSON" do
  103 + response = Excon::Response.new(:body => "foo = bar")
  104 +
  105 + @openstack.should_receive(:servers)
  106 + .and_raise(Excon::Errors::RequestEntityTooLarge.new("", "", response))
  107 + @cloud.should_not_receive(:sleep)
  108 +
  109 + expect {
  110 + @cloud.with_openstack do
  111 + @openstack.servers
  112 + end
  113 + }.to raise_error(Excon::Errors::RequestEntityTooLarge)
  114 + end
  115 +
  116 + it "should retry the amount of seconds received at the response body" do
  117 + body = { "overLimit" => {
  118 + "message" => "This request was rate-limited.",
  119 + "code" => 413,
  120 + "retryAfter" => "5",
  121 + "details" => "Only 10 POST request(s) can be made to * every minute."}
  122 + }
  123 + response = Excon::Response.new(:body => JSON.dump(body))
  124 +
  125 + @openstack.should_receive(:servers)
  126 + .and_raise(Excon::Errors::RequestEntityTooLarge.new("", "", response))
  127 + @cloud.should_receive(:sleep).with(5)
  128 + @openstack.should_receive(:servers).and_return(nil)
  129 +
  130 + @cloud.with_openstack do
  131 + @openstack.servers
  132 + end
  133 + end
  134 +
  135 + it "should retry the default number of seconds if not set at the response body" do
  136 + body = { "overLimitFault" => {
  137 + "message" => "This request was rate-limited.",
  138 + "code" => 413,
  139 + "details" => "Only 10 POST request(s) can be made to * every minute."}
  140 + }
  141 + response = Excon::Response.new(:body => JSON.dump(body))
  142 +
  143 + @openstack.should_receive(:servers)
  144 + .and_raise(Excon::Errors::RequestEntityTooLarge.new("", "", response))
  145 + @cloud.should_receive(:sleep).with(1)
  146 + @openstack.should_receive(:servers).and_return(nil)
  147 +
  148 + @cloud.with_openstack do
  149 + @openstack.servers
  150 + end
  151 + end
  152 +
  153 + it "should retry the max number of retries" do
  154 + body = { "overLimit" => {
  155 + "message" => "This request was rate-limited.",
  156 + "code" => 413,
  157 + "retryAfter" => "5",
  158 + "details" => "Only 10 POST request(s) can be made to * every minute."}
  159 + }
  160 + response = Excon::Response.new(:body => JSON.dump(body))
  161 +
  162 + @openstack.should_receive(:servers).exactly(11)
  163 + .and_raise(Excon::Errors::RequestEntityTooLarge.new("", "", response))
  164 + @cloud.should_receive(:sleep).with(5).exactly(10)
  165 +
  166 + expect {
  167 + @cloud.with_openstack do
  168 + @openstack.servers
  169 + end
  170 + }.to raise_error(Excon::Errors::RequestEntityTooLarge)
  171 + end
74 172 end
75 173 end
3  bosh_openstack_cpi/spec/unit/validate_deployment_spec.rb
@@ -6,7 +6,8 @@
6 6 describe Bosh::OpenStackCloud::Cloud do
7 7
8 8 it "doesn't implement `validate_deployment'" do
9   - Bosh::OpenStackCloud::Connection.stub(:new)
  9 + Fog::Compute.stub(:new)
  10 + Fog::Image.stub(:new)
10 11 cloud = make_cloud
11 12 expect {
12 13 cloud.validate_deployment({}, {})

0 comments on commit 140ab42

Please sign in to comment.
Something went wrong with that request. Please try again.