bosh_openstack_cpi 0.0.2

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.
@@ -0,0 +1,95 @@
1
+ # Copyright (c) 2012 Piston Cloud Computing, Inc.
2
+
3
+ require File.expand_path("../../spec_helper", __FILE__)
4
+
5
+ describe Bosh::OpenStackCloud::Cloud do
6
+
7
+ before(:each) do
8
+ @registry = mock_registry
9
+ end
10
+
11
+ it "attaches an OpenStack volume to a server" do
12
+ server = double("server", :id => "i-test", :name => "i-test")
13
+ volume = double("volume", :id => "v-foobar")
14
+ volume_attachments = double("body", :body => {"volumeAttachments" => []})
15
+ attachment = double("attachment", :device => "/dev/vdc")
16
+
17
+ cloud = mock_cloud do |openstack|
18
+ openstack.servers.should_receive(:get).with("i-test").and_return(server)
19
+ openstack.volumes.should_receive(:get).with("v-foobar").and_return(volume)
20
+ openstack.should_receive(:get_server_volumes).and_return(volume_attachments)
21
+ end
22
+
23
+ volume.should_receive(:attach).with(server.id, "/dev/vdc").and_return(attachment)
24
+ volume.should_receive(:status).and_return(:available)
25
+ cloud.should_receive(:wait_resource).with(volume, :available, :"in-use")
26
+
27
+ old_settings = { "foo" => "bar" }
28
+ new_settings = {
29
+ "foo" => "bar",
30
+ "disks" => {
31
+ "persistent" => {
32
+ "v-foobar" => "/dev/vdc"
33
+ }
34
+ }
35
+ }
36
+
37
+ @registry.should_receive(:read_settings).with("i-test").and_return(old_settings)
38
+ @registry.should_receive(:update_settings).with("i-test", new_settings)
39
+
40
+ cloud.attach_disk("i-test", "v-foobar")
41
+ end
42
+
43
+ it "picks available device name" do
44
+ server = double("server", :id => "i-test", :name => "i-test")
45
+ volume = double("volume", :id => "v-foobar")
46
+ volume_attachments = double("body", :body => {"volumeAttachments" => [{"device" => "/dev/vdc"}, {"device" => "/dev/vdd"}]})
47
+ attachment = double("attachment", :device => "/dev/vdd")
48
+
49
+ cloud = mock_cloud do |openstack|
50
+ openstack.servers.should_receive(:get).with("i-test").and_return(server)
51
+ openstack.volumes.should_receive(:get).with("v-foobar").and_return(volume)
52
+ openstack.should_receive(:get_server_volumes).and_return(volume_attachments)
53
+ end
54
+
55
+ volume.should_receive(:attach).with(server.id, "/dev/vde").and_return(attachment)
56
+ volume.should_receive(:status).and_return(:available)
57
+ cloud.should_receive(:wait_resource).with(volume, :available, :"in-use")
58
+
59
+ old_settings = { "foo" => "bar" }
60
+ new_settings = {
61
+ "foo" => "bar",
62
+ "disks" => {
63
+ "persistent" => {
64
+ "v-foobar" => "/dev/vde"
65
+ }
66
+ }
67
+ }
68
+
69
+ @registry.should_receive(:read_settings).with("i-test").and_return(old_settings)
70
+ @registry.should_receive(:update_settings).with("i-test", new_settings)
71
+
72
+ cloud.attach_disk("i-test", "v-foobar")
73
+ end
74
+
75
+ it "raises an error when vdc..vdz are all reserved" do
76
+ server = double("server", :id => "i-test", :name => "i-test")
77
+ volume = double("volume", :id => "v-foobar")
78
+ all_mappings = ("c".."z").inject([]) do |array, char|
79
+ array << {"device" => "/dev/vd#{char}"}
80
+ array
81
+ end
82
+ volume_attachments = double("body", :body => {"volumeAttachments" => all_mappings})
83
+
84
+ cloud = mock_cloud do |openstack|
85
+ openstack.servers.should_receive(:get).with("i-test").and_return(server)
86
+ openstack.volumes.should_receive(:get).with("v-foobar").and_return(volume)
87
+ openstack.should_receive(:get_server_volumes).and_return(volume_attachments)
88
+ end
89
+
90
+ expect {
91
+ cloud.attach_disk("i-test", "v-foobar")
92
+ }.to raise_error(Bosh::Clouds::CloudError, /too many disks attached/)
93
+ end
94
+
95
+ end
@@ -0,0 +1,18 @@
1
+ # Copyright (c) 2012 Piston Cloud Computing, Inc.
2
+
3
+ require File.expand_path("../../spec_helper", __FILE__)
4
+
5
+ describe Bosh::OpenStackCloud::Cloud do
6
+
7
+ describe "creating via provider" do
8
+
9
+ it "can be created using Bosh::Cloud::Provider" do
10
+ Fog::Compute.stub(:new)
11
+ Fog::Image.stub(:new)
12
+ cloud = Bosh::Clouds::Provider.create(:openstack, mock_cloud_options)
13
+ cloud.should be_an_instance_of(Bosh::OpenStackCloud::Cloud)
14
+ end
15
+
16
+ end
17
+
18
+ end
@@ -0,0 +1,83 @@
1
+ # Copyright (c) 2012 Piston Cloud Computing, Inc.
2
+
3
+ require File.expand_path("../../spec_helper", __FILE__)
4
+
5
+ describe Bosh::OpenStackCloud::Cloud do
6
+
7
+ before(:each) do
8
+ @registry = mock_registry
9
+ end
10
+
11
+ it "adds floating ip to the server for vip network" do
12
+ server = double("server", :id => "i-test", :name => "i-test")
13
+ address = double("address", :id => "a-test", :ip => "10.0.0.1", :instance_id => nil)
14
+
15
+ cloud = mock_cloud do |openstack|
16
+ openstack.servers.should_receive(:get).with("i-test").and_return(server)
17
+ openstack.addresses.should_receive(:each).and_yield(address)
18
+ end
19
+
20
+ address.should_receive(:server=).with(server)
21
+
22
+ old_settings = { "foo" => "bar", "networks" => "baz" }
23
+ new_settings = { "foo" => "bar", "networks" => combined_network_spec }
24
+
25
+ @registry.should_receive(:read_settings).with("i-test").and_return(old_settings)
26
+ @registry.should_receive(:update_settings).with("i-test", new_settings)
27
+
28
+ cloud.configure_networks("i-test", combined_network_spec)
29
+ end
30
+
31
+ it "removes floating ip from the server if vip network is gone" do
32
+ server = double("server", :id => "i-test", :name => "i-test")
33
+ address = double("address", :id => "a-test", :ip => "10.0.0.1", :instance_id => "i-test")
34
+
35
+ cloud = mock_cloud do |openstack|
36
+ openstack.servers.should_receive(:get).with("i-test").and_return(server)
37
+ openstack.addresses.should_receive(:each).and_yield(address)
38
+ end
39
+
40
+ address.should_receive(:server=).with(nil)
41
+
42
+ old_settings = { "foo" => "bar", "networks" => combined_network_spec }
43
+ new_settings = { "foo" => "bar", "networks" => { "net_a" => dynamic_network_spec } }
44
+
45
+ @registry.should_receive(:read_settings).with("i-test").and_return(old_settings)
46
+ @registry.should_receive(:update_settings).with("i-test", new_settings)
47
+
48
+ cloud.configure_networks("i-test", "net_a" => dynamic_network_spec)
49
+ end
50
+
51
+ it "performs network sanity check" do
52
+ server = double("server", :id => "i-test", :name => "i-test")
53
+
54
+ expect {
55
+ cloud = mock_cloud do |openstack|
56
+ openstack.servers.should_receive(:get).with("i-test").and_return(server)
57
+ end
58
+ cloud.configure_networks("i-test", "net_a" => vip_network_spec)
59
+ }.to raise_error(Bosh::Clouds::CloudError, "At least one dynamic network should be defined")
60
+
61
+ expect {
62
+ cloud = mock_cloud do |openstack|
63
+ openstack.servers.should_receive(:get).with("i-test").and_return(server)
64
+ end
65
+ cloud.configure_networks("i-test", "net_a" => vip_network_spec, "net_b" => vip_network_spec)
66
+ }.to raise_error(Bosh::Clouds::CloudError, /More than one vip network/)
67
+
68
+ expect {
69
+ cloud = mock_cloud do |openstack|
70
+ openstack.servers.should_receive(:get).with("i-test").and_return(server)
71
+ end
72
+ cloud.configure_networks("i-test", "net_a" => dynamic_network_spec, "net_b" => dynamic_network_spec)
73
+ }.to raise_error(Bosh::Clouds::CloudError, /More than one dynamic network/)
74
+
75
+ expect {
76
+ cloud = mock_cloud do |openstack|
77
+ openstack.servers.should_receive(:get).with("i-test").and_return(server)
78
+ end
79
+ cloud.configure_networks("i-test", "net_a" => { "type" => "foo" })
80
+ }.to raise_error(Bosh::Clouds::CloudError, /Invalid network type `foo'/)
81
+ end
82
+
83
+ end
@@ -0,0 +1,82 @@
1
+ # Copyright (c) 2012 Piston Cloud Computing, Inc.
2
+
3
+ require File.expand_path("../../spec_helper", __FILE__)
4
+
5
+ describe Bosh::OpenStackCloud::Cloud do
6
+
7
+ it "creates an OpenStack volume" do
8
+ unique_name = UUIDTools::UUID.random_create.to_s
9
+ disk_params = {
10
+ :name => "volume-#{unique_name}",
11
+ :description => "",
12
+ :size => 2,
13
+ :availability_zone => "nova"
14
+ }
15
+ volume = double("volume", :id => "v-foobar")
16
+
17
+ cloud = mock_cloud do |openstack|
18
+ openstack.volumes.should_receive(:create).with(disk_params).and_return(volume)
19
+ end
20
+
21
+ cloud.should_receive(:generate_unique_name).and_return(unique_name)
22
+ volume.should_receive(:status).and_return(:creating)
23
+ cloud.should_receive(:wait_resource).with(volume, :creating, :available)
24
+
25
+ cloud.create_disk(2048).should == "v-foobar"
26
+ end
27
+
28
+ it "rounds up disk size" do
29
+ unique_name = UUIDTools::UUID.random_create.to_s
30
+ disk_params = {
31
+ :name => "volume-#{unique_name}",
32
+ :description => "",
33
+ :size => 3,
34
+ :availability_zone => "nova"
35
+ }
36
+ volume = double("volume", :id => "v-foobar")
37
+
38
+ cloud = mock_cloud do |openstack|
39
+ openstack.volumes.should_receive(:create).with(disk_params).and_return(volume)
40
+ end
41
+
42
+ cloud.should_receive(:generate_unique_name).and_return(unique_name)
43
+ volume.should_receive(:status).and_return(:creating)
44
+ cloud.should_receive(:wait_resource).with(volume, :creating, :available)
45
+
46
+ cloud.create_disk(2049)
47
+ end
48
+
49
+ it "check min and max disk size" do
50
+ expect {
51
+ mock_cloud.create_disk(100)
52
+ }.to raise_error(Bosh::Clouds::CloudError, /minimum disk size is 1 GiB/)
53
+
54
+ expect {
55
+ mock_cloud.create_disk(2000 * 1024)
56
+ }.to raise_error(Bosh::Clouds::CloudError, /maximum disk size is 1 TiB/)
57
+ end
58
+
59
+ it "puts disk in the same AZ as a server" do
60
+ unique_name = UUIDTools::UUID.random_create.to_s
61
+ disk_params = {
62
+ :name => "volume-#{unique_name}",
63
+ :description => "",
64
+ :size => 1,
65
+ :availability_zone => "foobar-land"
66
+ }
67
+ server = double("server", :id => "i-test", :availability_zone => "foobar-land")
68
+ volume = double("volume", :id => "v-foobar")
69
+
70
+ cloud = mock_cloud do |openstack|
71
+ openstack.servers.should_receive(:get).with("i-test").and_return(server)
72
+ openstack.volumes.should_receive(:create).with(disk_params).and_return(volume)
73
+ end
74
+
75
+ cloud.should_receive(:generate_unique_name).and_return(unique_name)
76
+ volume.should_receive(:status).and_return(:creating)
77
+ cloud.should_receive(:wait_resource).with(volume, :creating, :available)
78
+
79
+ cloud.create_disk(1024, "i-test")
80
+ end
81
+
82
+ end
@@ -0,0 +1,50 @@
1
+ # Copyright (c) 2012 Piston Cloud Computing, Inc.
2
+
3
+ require File.expand_path("../../spec_helper", __FILE__)
4
+
5
+ describe Bosh::OpenStackCloud::Cloud do
6
+
7
+ before :each do
8
+ @tmp_dir = Dir.mktmpdir
9
+ end
10
+
11
+ describe "Image upload based flow" do
12
+
13
+ it "creates stemcell by uploading an image via Glance" do
14
+ image = double("image", :id => "i-bar", :name => "i-bar")
15
+ unique_name = UUIDTools::UUID.random_create.to_s
16
+ image_params = {
17
+ :name => "BOSH-#{unique_name}",
18
+ :disk_format => "ami",
19
+ :container_format => "ami",
20
+ :location => "#{@tmp_dir}/root.img",
21
+ :is_public => true,
22
+ :properties => {
23
+ :kernel_id => "k-id",
24
+ :ramdisk_id => "r-id",
25
+ }
26
+ }
27
+
28
+ cloud = mock_glance do |glance|
29
+ glance.images.should_receive(:create).with(image_params).and_return(image)
30
+ end
31
+
32
+ Dir.should_receive(:mktmpdir).and_yield(@tmp_dir)
33
+ cloud.should_receive(:unpack_image).with(@tmp_dir, "/tmp/foo")
34
+ cloud.should_receive(:generate_unique_name).and_return(unique_name)
35
+ image.should_receive(:status).and_return(:queued)
36
+ cloud.should_receive(:wait_resource).with(image, :queued, :active)
37
+
38
+ sc_id = cloud.create_stemcell("/tmp/foo", {
39
+ "container_format" => "ami",
40
+ "disk_format" => "ami",
41
+ "kernel_id" => "k-id",
42
+ "ramdisk_id" => "r-id"
43
+ })
44
+
45
+ sc_id.should == "i-bar"
46
+ end
47
+
48
+ end
49
+
50
+ end
@@ -0,0 +1,142 @@
1
+ # Copyright (c) 2012 Piston Cloud Computing, Inc.
2
+
3
+ require File.expand_path("../../spec_helper", __FILE__)
4
+
5
+ describe Bosh::OpenStackCloud::Cloud, "create_vm" do
6
+
7
+ def agent_settings(unique_name, network_spec = dynamic_network_spec)
8
+ {
9
+ "vm" => {
10
+ "name" => "vm-#{unique_name}"
11
+ },
12
+ "agent_id" => "agent-id",
13
+ "networks" => { "network_a" => network_spec },
14
+ "disks" => {
15
+ "system" => "/dev/vda",
16
+ "ephemeral" => "/dev/vdb",
17
+ "persistent" => {}
18
+ },
19
+ "env" => {
20
+ "test_env" => "value"
21
+ },
22
+ "foo" => "bar", # Agent env
23
+ "baz" => "zaz"
24
+ }
25
+ end
26
+
27
+ def openstack_params(unique_name, user_data, security_groups=[])
28
+ {
29
+ :name=>"vm-#{unique_name}",
30
+ :image_ref => "sc-id",
31
+ :flavor_ref => "f-test",
32
+ :key_name => "test_key",
33
+ :security_groups => security_groups.map { |secgrp| {:name => secgrp} },
34
+ :user_data => Yajl::Encoder.encode(user_data),
35
+ :availability_zone => "foobar-1a"
36
+ }
37
+ end
38
+
39
+ before(:each) do
40
+ @registry = mock_registry
41
+ end
42
+
43
+ it "creates an OpenStack server and polls until it's ready" do
44
+ unique_name = UUIDTools::UUID.random_create.to_s
45
+ user_data = {
46
+ "registry" => {
47
+ "endpoint" => "http://registry:3333"
48
+ },
49
+ "server" => {
50
+ "name" => "vm-#{unique_name}"
51
+ }
52
+ }
53
+ server = double("server", :id => "i-test", :name => "i-test")
54
+ image = double("image", :id => "sc-id", :name => "sc-id")
55
+ flavor = double("flavor", :id => "f-test", :name => "m1.tiny")
56
+ address = double("address", :id => "a-test", :ip => "10.0.0.1", :instance_id => "i-test")
57
+
58
+ cloud = mock_cloud do |openstack|
59
+ openstack.servers.should_receive(:create).with(openstack_params(unique_name, user_data)).and_return(server)
60
+ openstack.images.should_receive(:find).and_return(image)
61
+ openstack.flavors.should_receive(:find).and_return(flavor)
62
+ openstack.addresses.should_receive(:each).and_yield(address)
63
+ end
64
+
65
+ cloud.should_receive(:generate_unique_name).and_return(unique_name)
66
+ address.should_receive(:server=).with(nil)
67
+ server.should_receive(:state).and_return(:build)
68
+ cloud.should_receive(:wait_resource).with(server, :build, :active, :state)
69
+
70
+ @registry.should_receive(:update_settings).with("i-test", agent_settings(unique_name))
71
+
72
+ vm_id = cloud.create_vm("agent-id", "sc-id",
73
+ resource_pool_spec,
74
+ { "network_a" => dynamic_network_spec },
75
+ nil, { "test_env" => "value" })
76
+ vm_id.should == "i-test"
77
+ end
78
+
79
+ it "creates an OpenStack server with security group" do
80
+ unique_name = UUIDTools::UUID.random_create.to_s
81
+ user_data = {
82
+ "registry" => {
83
+ "endpoint" => "http://registry:3333"
84
+ },
85
+ "server" => {
86
+ "name" => "vm-#{unique_name}"
87
+ }
88
+ }
89
+ security_groups = %w[foo bar]
90
+ network_spec = dynamic_network_spec
91
+ network_spec["cloud_properties"] = { "security_groups" => security_groups }
92
+ server = double("server", :id => "i-test", :name => "i-test")
93
+ image = double("image", :id => "sc-id", :name => "sc-id")
94
+ flavor = double("flavor", :id => "f-test", :name => "m1.tiny")
95
+ address = double("address", :id => "a-test", :ip => "10.0.0.1", :instance_id => nil)
96
+
97
+ cloud = mock_cloud do |openstack|
98
+ openstack.servers.should_receive(:create).with(openstack_params(unique_name, user_data, security_groups)).and_return(server)
99
+ openstack.images.should_receive(:find).and_return(image)
100
+ openstack.flavors.should_receive(:find).and_return(flavor)
101
+ openstack.addresses.should_receive(:each).and_yield(address)
102
+ end
103
+
104
+ cloud.should_receive(:generate_unique_name).and_return(unique_name)
105
+ server.should_receive(:state).and_return(:build)
106
+ cloud.should_receive(:wait_resource).with(server, :build, :active, :state)
107
+
108
+ @registry.should_receive(:update_settings).with("i-test", agent_settings(unique_name, network_spec))
109
+
110
+ vm_id = cloud.create_vm("agent-id", "sc-id",
111
+ resource_pool_spec,
112
+ { "network_a" => network_spec },
113
+ nil, { "test_env" => "value" })
114
+ vm_id.should == "i-test"
115
+ end
116
+
117
+ it "associates server with floating ip if vip network is provided" do
118
+ server = double("server", :id => "i-test", :name => "i-test")
119
+ image = double("image", :id => "sc-id", :name => "sc-id")
120
+ flavor = double("flavor", :id => "f-test", :name => "m1.tiny")
121
+ address = double("address", :id => "a-test", :ip => "10.0.0.1", :instance_id => "i-test")
122
+
123
+ cloud = mock_cloud do |openstack|
124
+ openstack.servers.should_receive(:create).and_return(server)
125
+ openstack.images.should_receive(:find).and_return(image)
126
+ openstack.flavors.should_receive(:find).and_return(flavor)
127
+ openstack.addresses.should_receive(:each).and_yield(address)
128
+ end
129
+
130
+ address.should_receive(:server=).with(nil)
131
+ address.should_receive(:server=).with(server)
132
+ server.should_receive(:state).and_return(:build)
133
+ cloud.should_receive(:wait_resource).with(server, :build, :active, :state)
134
+
135
+ @registry.should_receive(:update_settings)
136
+
137
+ vm_id = cloud.create_vm("agent-id", "sc-id",
138
+ resource_pool_spec,
139
+ combined_network_spec)
140
+ end
141
+
142
+ end