bosh_openstack_cpi 0.0.4 → 0.0.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/README.md +1 -1
- data/lib/cloud/openstack/cloud.rb +311 -197
- data/lib/cloud/openstack/dynamic_network.rb +4 -2
- data/lib/cloud/openstack/helpers.rb +43 -13
- data/lib/cloud/openstack/network.rb +1 -1
- data/lib/cloud/openstack/network_configurator.rb +23 -15
- data/lib/cloud/openstack/registry_client.rb +29 -11
- data/lib/cloud/openstack/version.rb +1 -1
- data/lib/cloud/openstack/vip_network.rb +19 -20
- data/spec/assets/sample_config.yml +14 -0
- data/spec/integration/cpi_test.rb +119 -0
- data/spec/spec_helper.rb +6 -1
- data/spec/unit/attach_disk_spec.rb +37 -21
- data/spec/unit/configure_networks_spec.rb +49 -26
- data/spec/unit/create_disk_spec.rb +17 -17
- data/spec/unit/create_stemcell_spec.rb +78 -20
- data/spec/unit/create_vm_spec.rb +80 -16
- data/spec/unit/delete_disk_spec.rb +5 -3
- data/spec/unit/delete_stemcell_spec.rb +18 -11
- data/spec/unit/delete_vm_spec.rb +1 -2
- data/spec/unit/detach_disk_spec.rb +23 -12
- data/spec/unit/helpers_spec.rb +46 -6
- data/spec/unit/network_configurator_spec.rb +8 -8
- data/spec/unit/reboot_vm_spec.rb +2 -4
- data/spec/unit/validate_deployment_spec.rb +2 -1
- metadata +9 -5
@@ -2,7 +2,7 @@
|
|
2
2
|
|
3
3
|
module Bosh::OpenStackCloud
|
4
4
|
##
|
5
|
-
#
|
5
|
+
# Represents OpenStack dynamic network: where IaaS sets VM's IP
|
6
6
|
class DynamicNetwork < Network
|
7
7
|
|
8
8
|
##
|
@@ -17,8 +17,10 @@ module Bosh::OpenStackCloud
|
|
17
17
|
##
|
18
18
|
# Configures OpenStack dynamic network. Right now it's a no-op,
|
19
19
|
# as dynamic networks are completely managed by OpenStack
|
20
|
+
#
|
20
21
|
# @param [Fog::Compute::OpenStack] openstack Fog OpenStack Compute client
|
21
|
-
# @param [Fog::Compute::OpenStack::Server] server OpenStack server to
|
22
|
+
# @param [Fog::Compute::OpenStack::Server] server OpenStack server to
|
23
|
+
# configure
|
22
24
|
def configure(openstack, server)
|
23
25
|
end
|
24
26
|
|
@@ -4,44 +4,74 @@ module Bosh::OpenStackCloud
|
|
4
4
|
|
5
5
|
module Helpers
|
6
6
|
|
7
|
-
DEFAULT_TIMEOUT =
|
7
|
+
DEFAULT_TIMEOUT = 600 # Default timeout for target state (in seconds)
|
8
8
|
|
9
|
-
|
9
|
+
##
|
10
10
|
# Raises CloudError exception
|
11
11
|
#
|
12
|
+
# @param [String] message Message about what went wrong
|
12
13
|
def cloud_error(message)
|
13
14
|
@logger.error(message) if @logger
|
14
15
|
raise Bosh::Clouds::CloudError, message
|
15
16
|
end
|
16
17
|
|
17
|
-
|
18
|
+
##
|
18
19
|
# Waits for a resource to be on a target state
|
19
20
|
#
|
20
|
-
|
21
|
+
# @param [Fog::Model] resource Resource to query
|
22
|
+
# @param [Symbol] target_state Resource's state desired
|
23
|
+
# @param [Symbol] state_method Resource's method to fetch state
|
24
|
+
# @param [Boolean] allow_notfound true if resource could be not found
|
25
|
+
# @param [Integer] timeout Timeout for target state (in seconds)
|
26
|
+
def wait_resource(resource, target_state, state_method = :status,
|
27
|
+
allow_notfound = false, timeout = DEFAULT_TIMEOUT)
|
21
28
|
|
22
29
|
started_at = Time.now
|
23
|
-
|
24
|
-
|
30
|
+
desc = resource.class.name.split("::").last.to_s + " `" +
|
31
|
+
resource.id.to_s + "'"
|
32
|
+
|
33
|
+
loop do
|
34
|
+
task_checkpoint
|
25
35
|
|
26
|
-
while state.to_sym != target_state
|
27
36
|
duration = Time.now - started_at
|
28
37
|
|
29
38
|
if duration > timeout
|
30
39
|
cloud_error("Timed out waiting for #{desc} to be #{target_state}")
|
31
40
|
end
|
32
41
|
|
33
|
-
|
34
|
-
|
35
|
-
|
42
|
+
if @logger
|
43
|
+
@logger.debug("Waiting for #{desc} to be #{target_state} " \
|
44
|
+
"(#{duration}s)")
|
45
|
+
end
|
36
46
|
|
47
|
+
# If resource reload is nil, perhaps it's because resource went away
|
48
|
+
# (ie: a destroy operation). Don't raise an exception if this is
|
49
|
+
# expected (allow_notfound).
|
37
50
|
if resource.reload.nil?
|
38
|
-
|
51
|
+
break if allow_notfound
|
52
|
+
cloud_error("#{desc}: Resource not found")
|
39
53
|
else
|
40
|
-
state = resource.send(state_method).downcase
|
54
|
+
state = resource.send(state_method).downcase.to_sym
|
41
55
|
end
|
56
|
+
|
57
|
+
# This is not a very strong convention, but some resources
|
58
|
+
# have 'error' and 'failed' states, we probably don't want to keep
|
59
|
+
# waiting if we're in these states. Alternatively we could introduce a
|
60
|
+
# set of 'loop breaker' states but that doesn't seem very helpful
|
61
|
+
# at the moment
|
62
|
+
if state == :error || state == :failed
|
63
|
+
cloud_error("#{desc} state is #{state}, expected #{target_state}")
|
64
|
+
end
|
65
|
+
|
66
|
+
break if state == target_state
|
67
|
+
|
68
|
+
sleep(1)
|
42
69
|
end
|
43
70
|
|
44
|
-
|
71
|
+
if @logger
|
72
|
+
total = Time.now - started_at
|
73
|
+
@logger.info("#{desc} is now #{target_state}, took #{total}s")
|
74
|
+
end
|
45
75
|
end
|
46
76
|
|
47
77
|
end
|
@@ -8,15 +8,13 @@ module Bosh::OpenStackCloud
|
|
8
8
|
# a number of sanity checks for the network spec provided by director
|
9
9
|
# to make sure we don't apply something OpenStack doesn't understand how to
|
10
10
|
# deal with.
|
11
|
-
#
|
12
11
|
class NetworkConfigurator
|
13
12
|
include Helpers
|
14
13
|
|
15
14
|
##
|
16
15
|
# Creates new network spec
|
17
16
|
#
|
18
|
-
# @param [Hash] spec
|
19
|
-
# TODO Add network configuration examples
|
17
|
+
# @param [Hash] spec Raw network spec passed by director
|
20
18
|
def initialize(spec)
|
21
19
|
unless spec.is_a?(Hash)
|
22
20
|
raise ArgumentError, "Invalid spec, Hash expected, " \
|
@@ -37,20 +35,19 @@ module Bosh::OpenStackCloud
|
|
37
35
|
cloud_error("More than one dynamic network for `#{name}'")
|
38
36
|
else
|
39
37
|
@dynamic_network = DynamicNetwork.new(name, spec)
|
40
|
-
|
41
|
-
extract_security_groups(spec)
|
38
|
+
@security_groups += extract_security_groups(spec)
|
42
39
|
end
|
43
40
|
when "vip"
|
44
41
|
if @vip_network
|
45
42
|
cloud_error("More than one vip network for `#{name}'")
|
46
43
|
else
|
47
44
|
@vip_network = VipNetwork.new(name, spec)
|
45
|
+
@security_groups += extract_security_groups(spec)
|
48
46
|
end
|
49
47
|
else
|
50
|
-
cloud_error("Invalid network type `#{network_type}': OpenStack
|
51
|
-
"can only handle `dynamic' and `vip' network types")
|
48
|
+
cloud_error("Invalid network type `#{network_type}': OpenStack " \
|
49
|
+
"CPI can only handle `dynamic' and `vip' network types")
|
52
50
|
end
|
53
|
-
|
54
51
|
end
|
55
52
|
|
56
53
|
if @dynamic_network.nil?
|
@@ -58,6 +55,12 @@ module Bosh::OpenStackCloud
|
|
58
55
|
end
|
59
56
|
end
|
60
57
|
|
58
|
+
##
|
59
|
+
# Applies network configuration to the vm
|
60
|
+
#
|
61
|
+
# @param [Fog::Compute::OpenStack] openstack Fog OpenStack Compute client
|
62
|
+
# @param [Fog::Compute::OpenStack::Server] server OpenStack server to
|
63
|
+
# configure
|
61
64
|
def configure(openstack, server)
|
62
65
|
@dynamic_network.configure(openstack, server)
|
63
66
|
|
@@ -81,33 +84,38 @@ module Bosh::OpenStackCloud
|
|
81
84
|
# Returns the security groups for this network configuration, or
|
82
85
|
# the default security groups if the configuration does not contain
|
83
86
|
# security groups
|
87
|
+
#
|
84
88
|
# @param [Array] default Default security groups
|
85
89
|
# @return [Array] security groups
|
86
90
|
def security_groups(default)
|
87
91
|
if @security_groups.empty? && default
|
88
|
-
|
92
|
+
default
|
89
93
|
else
|
90
|
-
|
94
|
+
@security_groups.sort
|
91
95
|
end
|
92
96
|
end
|
93
97
|
|
98
|
+
private
|
99
|
+
|
94
100
|
##
|
95
101
|
# Extracts the security groups from the network configuration
|
102
|
+
#
|
96
103
|
# @param [Hash] network_spec Network specification
|
104
|
+
# @return [Array] security groups
|
97
105
|
# @raise [ArgumentError] if the security groups in the network_spec
|
98
106
|
# is not an Array
|
99
|
-
def extract_security_groups(
|
100
|
-
if
|
101
|
-
cloud_properties =
|
107
|
+
def extract_security_groups(network_spec)
|
108
|
+
if network_spec && network_spec["cloud_properties"]
|
109
|
+
cloud_properties = network_spec["cloud_properties"]
|
102
110
|
if cloud_properties && cloud_properties["security_groups"]
|
103
111
|
unless cloud_properties["security_groups"].is_a?(Array)
|
104
112
|
raise ArgumentError, "security groups must be an Array"
|
105
113
|
end
|
106
|
-
|
114
|
+
return cloud_properties["security_groups"]
|
107
115
|
end
|
108
116
|
end
|
117
|
+
[]
|
109
118
|
end
|
110
119
|
|
111
120
|
end
|
112
|
-
|
113
121
|
end
|
@@ -1,6 +1,23 @@
|
|
1
1
|
# Copyright (c) 2012 Piston Cloud Computing, Inc.
|
2
2
|
|
3
3
|
module Bosh::OpenStackCloud
|
4
|
+
##
|
5
|
+
# Represents OpenStack Registry Client. It performs CRUD operations against
|
6
|
+
# the OpenStack Registry.
|
7
|
+
#
|
8
|
+
# Settings example:
|
9
|
+
# settings = {
|
10
|
+
# "vm" => {
|
11
|
+
# "name" => server_name
|
12
|
+
# },
|
13
|
+
# "agent_id" => agent_id,
|
14
|
+
# "networks" => network_spec,
|
15
|
+
# "disks" => {
|
16
|
+
# "system" => "/dev/vda",
|
17
|
+
# "ephemeral" => "/dev/vdb",
|
18
|
+
# "persistent" => {"volume_id" => device_name}
|
19
|
+
# }
|
20
|
+
# }
|
4
21
|
class RegistryClient
|
5
22
|
include Helpers
|
6
23
|
|
@@ -8,6 +25,12 @@ module Bosh::OpenStackCloud
|
|
8
25
|
attr_reader :user
|
9
26
|
attr_reader :password
|
10
27
|
|
28
|
+
##
|
29
|
+
# Creates a new Registry client
|
30
|
+
#
|
31
|
+
# @param [String] endpoint Registry endpoint URL
|
32
|
+
# @param [String] user Registry user
|
33
|
+
# @param [String] password Registry password
|
11
34
|
def initialize(endpoint, user, password)
|
12
35
|
@endpoint = endpoint
|
13
36
|
|
@@ -17,7 +40,6 @@ module Bosh::OpenStackCloud
|
|
17
40
|
|
18
41
|
@user = user
|
19
42
|
@password = password
|
20
|
-
|
21
43
|
auth = Base64.encode64("#{@user}:#{@password}").gsub("\n", "")
|
22
44
|
|
23
45
|
@headers = {
|
@@ -30,6 +52,7 @@ module Bosh::OpenStackCloud
|
|
30
52
|
|
31
53
|
##
|
32
54
|
# Update server settings in the registry
|
55
|
+
#
|
33
56
|
# @param [String] server_id OpenStack server id
|
34
57
|
# @param [Hash] settings New agent settings
|
35
58
|
# @return [Boolean]
|
@@ -42,8 +65,7 @@ module Bosh::OpenStackCloud
|
|
42
65
|
payload = Yajl::Encoder.encode(settings)
|
43
66
|
url = "#{@endpoint}/servers/#{server_id}/settings"
|
44
67
|
|
45
|
-
response = @client.put(url, payload, @headers)
|
46
|
-
|
68
|
+
response = @client.put(url, {:body => payload, :header => @headers})
|
47
69
|
if response.status != 200
|
48
70
|
cloud_error("Cannot update settings for `#{server_id}', " \
|
49
71
|
"got HTTP #{response.status}")
|
@@ -54,27 +76,25 @@ module Bosh::OpenStackCloud
|
|
54
76
|
|
55
77
|
##
|
56
78
|
# Read server settings from the registry
|
79
|
+
#
|
57
80
|
# @param [String] server_id OpenStack server id
|
58
81
|
# @return [Hash] Agent settings
|
59
82
|
def read_settings(server_id)
|
60
83
|
url = "#{@endpoint}/servers/#{server_id}/settings"
|
61
84
|
|
62
|
-
response = @client.get(url, {
|
63
|
-
|
85
|
+
response = @client.get(url, {:header => @headers})
|
64
86
|
if response.status != 200
|
65
87
|
cloud_error("Cannot read settings for `#{server_id}', " \
|
66
88
|
"got HTTP #{response.status}")
|
67
89
|
end
|
68
90
|
|
69
91
|
body = Yajl::Parser.parse(response.body)
|
70
|
-
|
71
92
|
unless body.is_a?(Hash)
|
72
93
|
cloud_error("Invalid registry response, Hash expected, " \
|
73
94
|
"got #{body.class}: #{body}")
|
74
95
|
end
|
75
96
|
|
76
97
|
settings = Yajl::Parser.parse(body["settings"])
|
77
|
-
|
78
98
|
unless settings.is_a?(Hash)
|
79
99
|
cloud_error("Invalid settings format, " \
|
80
100
|
"Hash expected, got #{settings.class}: " \
|
@@ -82,20 +102,19 @@ module Bosh::OpenStackCloud
|
|
82
102
|
end
|
83
103
|
|
84
104
|
settings
|
85
|
-
|
86
105
|
rescue Yajl::ParseError
|
87
106
|
cloud_error("Cannot parse settings for `#{server_id}'")
|
88
107
|
end
|
89
108
|
|
90
109
|
##
|
91
110
|
# Delete server settings from the registry
|
111
|
+
#
|
92
112
|
# @param [String] server_id OpenStack server id
|
93
113
|
# @return [Boolean]
|
94
114
|
def delete_settings(server_id)
|
95
115
|
url = "#{@endpoint}/servers/#{server_id}/settings"
|
96
116
|
|
97
|
-
response = @client.delete(url, @headers)
|
98
|
-
|
117
|
+
response = @client.delete(url, {:header => @headers})
|
99
118
|
if response.status != 200
|
100
119
|
cloud_error("Cannot delete settings for `#{server_id}', " \
|
101
120
|
"got HTTP #{response.status}")
|
@@ -105,5 +124,4 @@ module Bosh::OpenStackCloud
|
|
105
124
|
end
|
106
125
|
|
107
126
|
end
|
108
|
-
|
109
127
|
end
|
@@ -2,7 +2,8 @@
|
|
2
2
|
|
3
3
|
module Bosh::OpenStackCloud
|
4
4
|
##
|
5
|
-
#
|
5
|
+
# Represents OpenStack vip network: where users sets VM's IP (floating IP's
|
6
|
+
# in OpenStack)
|
6
7
|
class VipNetwork < Network
|
7
8
|
|
8
9
|
##
|
@@ -15,33 +16,31 @@ module Bosh::OpenStackCloud
|
|
15
16
|
end
|
16
17
|
|
17
18
|
##
|
18
|
-
# Configures vip network
|
19
|
+
# Configures OpenStack vip network
|
19
20
|
#
|
20
21
|
# @param [Fog::Compute::OpenStack] openstack Fog OpenStack Compute client
|
21
|
-
# @param [Fog::Compute::OpenStack::Server] server OpenStack server to
|
22
|
+
# @param [Fog::Compute::OpenStack::Server] server OpenStack server to
|
23
|
+
# configure
|
22
24
|
def configure(openstack, server)
|
23
25
|
if @ip.nil?
|
24
26
|
cloud_error("No IP provided for vip network `#{@name}'")
|
25
27
|
end
|
26
28
|
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
addresses.each do |address|
|
36
|
-
if address.ip == @ip
|
37
|
-
address.server = nil unless address.instance_id.nil?
|
38
|
-
address.server = server
|
39
|
-
address_id = address.id
|
40
|
-
break
|
29
|
+
# Check if the OpenStack floating IP is allocated. If true, disassociate
|
30
|
+
# it from any server before associating it to the new server
|
31
|
+
address = openstack.addresses.find { |a| a.ip == @ip }
|
32
|
+
if address
|
33
|
+
unless address.instance_id.nil?
|
34
|
+
@logger.info("Disassociating floating IP `#{@ip}' " \
|
35
|
+
"from server `#{address.instance_id}'")
|
36
|
+
address.server = nil
|
41
37
|
end
|
42
|
-
|
43
|
-
|
44
|
-
|
38
|
+
|
39
|
+
@logger.info("Associating server `#{server.id}' " \
|
40
|
+
"with floating IP `#{@ip}'")
|
41
|
+
address.server = server
|
42
|
+
else
|
43
|
+
cloud_error("Floating IP #{@ip} not allocated")
|
45
44
|
end
|
46
45
|
end
|
47
46
|
|
@@ -0,0 +1,14 @@
|
|
1
|
+
---
|
2
|
+
openstack:
|
3
|
+
auth_url: http://127.0.0.1:5000/v2.0/tokens
|
4
|
+
username: foo
|
5
|
+
api_key: bar
|
6
|
+
tenant: bosh
|
7
|
+
region:
|
8
|
+
default_key_name: default
|
9
|
+
default_security_groups: ["default"]
|
10
|
+
|
11
|
+
registry:
|
12
|
+
endpoint: http://127.0.0.1:25695
|
13
|
+
user: admin
|
14
|
+
password: admin
|
@@ -0,0 +1,119 @@
|
|
1
|
+
# Copyright (c) 2012 Piston Cloud Computing, Inc.
|
2
|
+
|
3
|
+
require File.expand_path("../../spec_helper", __FILE__)
|
4
|
+
require "tempfile"
|
5
|
+
|
6
|
+
##
|
7
|
+
# BOSH OpenStack CPI Integration tests
|
8
|
+
#
|
9
|
+
# To run integration test:
|
10
|
+
# 1. Prepare OpenStack CPI configuration file (a sample can be found at
|
11
|
+
# spec/assets/sample_config.yml);
|
12
|
+
# 2. Set CPI_CONFIG_FILE env variable to point to OpenStack config file;
|
13
|
+
# 3. Download a public OpenStack stemcell: 'bosh download public stemcell ...';
|
14
|
+
# 4. Untar the OpenStack stemcell, you'll find and image file;
|
15
|
+
# 5. Set STEMCELL_FILE env variable to point to OpenStack image file;
|
16
|
+
# 6. Optional: Set FLOATING_IP env variable with an allocated OpenStack
|
17
|
+
# Floating IP if you want to test vip networks;
|
18
|
+
# 7. Start OpenStack Registry manually (see bosh/openstack_registry);
|
19
|
+
# 8. Run 'bundle exec rspec --color spec/integration/cpi_test.rb'.
|
20
|
+
describe Bosh::OpenStackCloud::Cloud do
|
21
|
+
|
22
|
+
before(:each) do
|
23
|
+
unless ENV["CPI_CONFIG_FILE"]
|
24
|
+
raise "Please provide CPI_CONFIG_FILE environment variable"
|
25
|
+
end
|
26
|
+
unless ENV["STEMCELL_FILE"]
|
27
|
+
raise "Please provide STEMCELL_UUID environment variable"
|
28
|
+
end
|
29
|
+
@config = YAML.load_file(ENV["CPI_CONFIG_FILE"])
|
30
|
+
@logger = Logger.new(STDOUT)
|
31
|
+
Bosh::Clouds::Config.stub(:task_checkpoint)
|
32
|
+
end
|
33
|
+
|
34
|
+
let(:cpi) do
|
35
|
+
cpi = Bosh::OpenStackCloud::Cloud.new(@config)
|
36
|
+
cpi.logger = @logger
|
37
|
+
|
38
|
+
# As we inject the configuration file from the outside, we don't care
|
39
|
+
# about spinning up the registry ourselves. However we don't want to bother
|
40
|
+
# OpenStack at all if registry is not working, so just in case we perform
|
41
|
+
# a test health check against whatever has been provided.
|
42
|
+
cpi.registry.update_settings("foo", { "bar" => "baz" })
|
43
|
+
cpi.registry.read_settings("foo").should == { "bar" => "baz"}
|
44
|
+
cpi.registry.delete_settings("foo")
|
45
|
+
|
46
|
+
cpi
|
47
|
+
end
|
48
|
+
|
49
|
+
it "exercises a VM lifecycle" do
|
50
|
+
unique_name = UUIDTools::UUID.random_create.to_s
|
51
|
+
cpi.stub(:generate_unique_name).and_return(unique_name)
|
52
|
+
|
53
|
+
stemcell_id = cpi.create_stemcell(
|
54
|
+
ENV["STEMCELL_FILE"],
|
55
|
+
{
|
56
|
+
"name" => "bosh-stemcell",
|
57
|
+
"version" => "0.6.7",
|
58
|
+
"infrastructure" => "openstack",
|
59
|
+
"disk_format" => "qcow2",
|
60
|
+
"container_format" => "bare"
|
61
|
+
})
|
62
|
+
|
63
|
+
server_id = cpi.create_vm(
|
64
|
+
"agent-007", stemcell_id,
|
65
|
+
{ "instance_type" => "m1.small" },
|
66
|
+
{ "default" => { "type" => "dynamic" }},
|
67
|
+
[], { "key" => "value" })
|
68
|
+
|
69
|
+
server_id.should_not be_nil
|
70
|
+
|
71
|
+
settings = cpi.registry.read_settings("vm-#{unique_name}")
|
72
|
+
settings["vm"].should be_a(Hash)
|
73
|
+
settings["vm"]["name"].should == "vm-#{unique_name}"
|
74
|
+
settings["agent_id"].should == "agent-007"
|
75
|
+
settings["networks"].should == { "default" => { "type" => "dynamic" }}
|
76
|
+
settings["disks"].should == {
|
77
|
+
"system" => "/dev/vda",
|
78
|
+
"ephemeral" => "/dev/vdb",
|
79
|
+
"persistent" => {}
|
80
|
+
}
|
81
|
+
settings["env"].should == { "key" => "value" }
|
82
|
+
|
83
|
+
volume_id = cpi.create_disk(1024)
|
84
|
+
volume_id.should_not be_nil
|
85
|
+
|
86
|
+
cpi.attach_disk(server_id, volume_id)
|
87
|
+
settings = cpi.registry.read_settings("vm-#{unique_name}")
|
88
|
+
settings["disks"]["persistent"].should == { volume_id => "/dev/vdc" }
|
89
|
+
|
90
|
+
cpi.reboot_vm(server_id)
|
91
|
+
|
92
|
+
if ENV["FLOATING_IP"]
|
93
|
+
cpi.configure_networks(server_id,
|
94
|
+
{ "default" => { "type" => "dynamic" },
|
95
|
+
"floating" => { "type" => "vip",
|
96
|
+
"ip" => ENV["FLOATING_IP"] }
|
97
|
+
})
|
98
|
+
settings = cpi.registry.read_settings("vm-#{unique_name}")
|
99
|
+
settings["networks"].should == {
|
100
|
+
"default" => { "type" => "dynamic" },
|
101
|
+
"floating" => { "type" => "vip",
|
102
|
+
"ip" => ENV["FLOATING_IP"] }
|
103
|
+
}
|
104
|
+
end
|
105
|
+
|
106
|
+
cpi.detach_disk(server_id, volume_id)
|
107
|
+
settings = cpi.registry.read_settings("vm-#{unique_name}")
|
108
|
+
settings["disks"]["persistent"].should == {}
|
109
|
+
|
110
|
+
cpi.delete_vm(server_id)
|
111
|
+
cpi.delete_disk(volume_id)
|
112
|
+
cpi.delete_stemcell(stemcell_id)
|
113
|
+
|
114
|
+
expect {
|
115
|
+
cpi.registry.read_settings("vm-#{unique_name}")
|
116
|
+
}.to raise_error(/HTTP 404/)
|
117
|
+
end
|
118
|
+
|
119
|
+
end
|