vagrant-conoha 0.1.0

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.
Files changed (109) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +22 -0
  3. data/.rubocop.yml +35 -0
  4. data/CHANGELOG.md +3 -0
  5. data/Gemfile +19 -0
  6. data/LICENSE +23 -0
  7. data/Rakefile +25 -0
  8. data/Vagrantfile +71 -0
  9. data/dummy.box +0 -0
  10. data/example_box/README.md +13 -0
  11. data/example_box/metadata.json +3 -0
  12. data/functional_tests/Vagrantfile +58 -0
  13. data/functional_tests/keys/vagrant-openstack +27 -0
  14. data/functional_tests/keys/vagrant-openstack.pub +1 -0
  15. data/functional_tests/run_tests.sh +142 -0
  16. data/lib/vagrant-conoha.rb +29 -0
  17. data/lib/vagrant-conoha/action.rb +227 -0
  18. data/lib/vagrant-conoha/action/abstract_action.rb +22 -0
  19. data/lib/vagrant-conoha/action/connect_openstack.rb +60 -0
  20. data/lib/vagrant-conoha/action/create_server.rb +154 -0
  21. data/lib/vagrant-conoha/action/create_stack.rb +68 -0
  22. data/lib/vagrant-conoha/action/delete_server.rb +53 -0
  23. data/lib/vagrant-conoha/action/delete_stack.rb +73 -0
  24. data/lib/vagrant-conoha/action/message.rb +19 -0
  25. data/lib/vagrant-conoha/action/provision.rb +60 -0
  26. data/lib/vagrant-conoha/action/read_ssh_info.rb +72 -0
  27. data/lib/vagrant-conoha/action/read_state.rb +43 -0
  28. data/lib/vagrant-conoha/action/resume.rb +24 -0
  29. data/lib/vagrant-conoha/action/start_server.rb +24 -0
  30. data/lib/vagrant-conoha/action/stop_server.rb +25 -0
  31. data/lib/vagrant-conoha/action/suspend.rb +24 -0
  32. data/lib/vagrant-conoha/action/sync_folders.rb +129 -0
  33. data/lib/vagrant-conoha/action/wait_accessible.rb +61 -0
  34. data/lib/vagrant-conoha/action/wait_active.rb +33 -0
  35. data/lib/vagrant-conoha/action/wait_stop.rb +33 -0
  36. data/lib/vagrant-conoha/catalog/openstack_catalog.rb +67 -0
  37. data/lib/vagrant-conoha/client/cinder.rb +39 -0
  38. data/lib/vagrant-conoha/client/domain.rb +159 -0
  39. data/lib/vagrant-conoha/client/glance.rb +65 -0
  40. data/lib/vagrant-conoha/client/heat.rb +49 -0
  41. data/lib/vagrant-conoha/client/http_utils.rb +116 -0
  42. data/lib/vagrant-conoha/client/keystone.rb +77 -0
  43. data/lib/vagrant-conoha/client/neutron.rb +48 -0
  44. data/lib/vagrant-conoha/client/nova.rb +212 -0
  45. data/lib/vagrant-conoha/client/openstack.rb +59 -0
  46. data/lib/vagrant-conoha/client/request_logger.rb +23 -0
  47. data/lib/vagrant-conoha/client/rest_utils.rb +25 -0
  48. data/lib/vagrant-conoha/command/abstract_command.rb +51 -0
  49. data/lib/vagrant-conoha/command/flavor_list.rb +24 -0
  50. data/lib/vagrant-conoha/command/image_list.rb +29 -0
  51. data/lib/vagrant-conoha/command/main.rb +51 -0
  52. data/lib/vagrant-conoha/command/network_list.rb +25 -0
  53. data/lib/vagrant-conoha/command/openstack_command.rb +16 -0
  54. data/lib/vagrant-conoha/command/reset.rb +20 -0
  55. data/lib/vagrant-conoha/command/subnet_list.rb +22 -0
  56. data/lib/vagrant-conoha/command/utils.rb +22 -0
  57. data/lib/vagrant-conoha/command/volume_list.rb +25 -0
  58. data/lib/vagrant-conoha/config.rb +390 -0
  59. data/lib/vagrant-conoha/config/http.rb +39 -0
  60. data/lib/vagrant-conoha/config_resolver.rb +285 -0
  61. data/lib/vagrant-conoha/errors.rb +187 -0
  62. data/lib/vagrant-conoha/logging.rb +39 -0
  63. data/lib/vagrant-conoha/plugin.rb +48 -0
  64. data/lib/vagrant-conoha/provider.rb +50 -0
  65. data/lib/vagrant-conoha/utils.rb +26 -0
  66. data/lib/vagrant-conoha/version.rb +15 -0
  67. data/lib/vagrant-conoha/version_checker.rb +76 -0
  68. data/locales/en.yml +393 -0
  69. data/spec/vagrant-conoha/action/connect_openstack_spec.rb +695 -0
  70. data/spec/vagrant-conoha/action/create_server_spec.rb +225 -0
  71. data/spec/vagrant-conoha/action/create_stack_spec.rb +99 -0
  72. data/spec/vagrant-conoha/action/delete_server_spec.rb +89 -0
  73. data/spec/vagrant-conoha/action/delete_stack_spec.rb +63 -0
  74. data/spec/vagrant-conoha/action/message_spec.rb +33 -0
  75. data/spec/vagrant-conoha/action/provision_spec.rb +104 -0
  76. data/spec/vagrant-conoha/action/read_ssh_info_spec.rb +190 -0
  77. data/spec/vagrant-conoha/action/read_state_spec.rb +81 -0
  78. data/spec/vagrant-conoha/action/resume_server_spec.rb +49 -0
  79. data/spec/vagrant-conoha/action/start_server_spec.rb +49 -0
  80. data/spec/vagrant-conoha/action/stop_server_spec.rb +49 -0
  81. data/spec/vagrant-conoha/action/suspend_server_spec.rb +49 -0
  82. data/spec/vagrant-conoha/action/sync_folders_spec.rb +155 -0
  83. data/spec/vagrant-conoha/action/wait_accessible_spec.rb +67 -0
  84. data/spec/vagrant-conoha/action/wait_active_spec.rb +53 -0
  85. data/spec/vagrant-conoha/action/wait_stop_spec.rb +53 -0
  86. data/spec/vagrant-conoha/action_spec.rb +120 -0
  87. data/spec/vagrant-conoha/client/cinder_spec.rb +127 -0
  88. data/spec/vagrant-conoha/client/glance_spec.rb +143 -0
  89. data/spec/vagrant-conoha/client/heat_spec.rb +128 -0
  90. data/spec/vagrant-conoha/client/keystone_spec.rb +150 -0
  91. data/spec/vagrant-conoha/client/neutron_spec.rb +171 -0
  92. data/spec/vagrant-conoha/client/nova_spec.rb +757 -0
  93. data/spec/vagrant-conoha/client/utils_spec.rb +176 -0
  94. data/spec/vagrant-conoha/command/flavor_list_spec.rb +43 -0
  95. data/spec/vagrant-conoha/command/image_list_spec.rb +95 -0
  96. data/spec/vagrant-conoha/command/network_list_spec.rb +65 -0
  97. data/spec/vagrant-conoha/command/reset_spec.rb +24 -0
  98. data/spec/vagrant-conoha/command/subnet_list_spec.rb +45 -0
  99. data/spec/vagrant-conoha/command/volume_list_spec.rb +40 -0
  100. data/spec/vagrant-conoha/config_resolver_spec.rb +860 -0
  101. data/spec/vagrant-conoha/config_spec.rb +373 -0
  102. data/spec/vagrant-conoha/e2e_spec.rb.save +27 -0
  103. data/spec/vagrant-conoha/provider_spec.rb +13 -0
  104. data/spec/vagrant-conoha/spec_helper.rb +37 -0
  105. data/spec/vagrant-conoha/utils_spec.rb +129 -0
  106. data/spec/vagrant-conoha/version_checker_spec.rb +39 -0
  107. data/stackrc +25 -0
  108. data/vagrant-conoha.gemspec +32 -0
  109. metadata +343 -0
@@ -0,0 +1,150 @@
1
+ require 'vagrant-conoha/spec_helper'
2
+
3
+ describe VagrantPlugins::ConoHa::KeystoneClient do
4
+ let(:http) do
5
+ double('http').tap do |http|
6
+ http.stub(:read_timeout) { 42 }
7
+ http.stub(:open_timeout) { 43 }
8
+ end
9
+ end
10
+
11
+ let(:config) do
12
+ double('config').tap do |config|
13
+ config.stub(:openstack_auth_url) { 'http://keystoneAuthV2/tokens' }
14
+ config.stub(:openstack_compute_url) { nil }
15
+ config.stub(:openstack_network_url) { nil }
16
+ config.stub(:tenant_name) { 'testTenant' }
17
+ config.stub(:username) { 'username' }
18
+ config.stub(:password) { 'password' }
19
+ config.stub(:http) { http }
20
+ end
21
+ end
22
+
23
+ let(:env) do
24
+ {}.tap do |env|
25
+ env[:ui] = double('ui')
26
+ env[:ui].stub(:info).with(anything)
27
+ env[:machine] = double('machine')
28
+ env[:machine].stub(:provider_config) { config }
29
+ end
30
+ end
31
+
32
+ let(:session) do
33
+ VagrantPlugins::ConoHa.session
34
+ end
35
+
36
+ describe 'authenticate' do
37
+ let(:keystone_request_headers) do
38
+ {
39
+ 'Accept' => 'application/json',
40
+ 'Content-Type' => 'application/json'
41
+ }
42
+ end
43
+
44
+ let(:keystone_request_body) do
45
+ '{"auth":{"tenantName":"testTenant","passwordCredentials":{"username":"username","password":"password"}}}'
46
+ end
47
+
48
+ let(:keystone_response_body) do
49
+ '{"access":{"token":{"id":"0123456789","tenant":{"id":"testTenantId"}},"serviceCatalog":[
50
+ {"endpoints":[{"id":"eid1","publicURL":"http://nova"}],"type":"compute"},
51
+ {"endpoints":[{"id":"eid2","publicURL":"http://neutron"}],"type":"network"}
52
+ ]}}'
53
+ end
54
+
55
+ before :each do
56
+ @keystone_client = VagrantPlugins::ConoHa.keystone
57
+ end
58
+
59
+ context 'with good credentials' do
60
+ it 'store token and tenant id' do
61
+ stub_request(:post, 'http://keystoneAuthV2/tokens')
62
+ .with(
63
+ body: keystone_request_body,
64
+ headers: keystone_request_headers)
65
+ .to_return(
66
+ status: 200,
67
+ body: keystone_response_body,
68
+ headers: keystone_request_headers)
69
+
70
+ @keystone_client.authenticate(env)
71
+
72
+ session.token.should eq('0123456789')
73
+ session.project_id.should eq('testTenantId')
74
+ end
75
+ end
76
+
77
+ context 'with wrong credentials' do
78
+ it 'raise an unauthorized error' do
79
+ stub_request(:post, 'http://keystoneAuthV2/tokens')
80
+ .with(
81
+ body: keystone_request_body,
82
+ headers: keystone_request_headers)
83
+ .to_return(
84
+ status: 401,
85
+ body: '{
86
+ "error": {
87
+ "message": "The request you have made requires authentication.",
88
+ "code": 401,
89
+ "title": "Unauthorized"
90
+ }
91
+ }',
92
+ headers: keystone_request_headers)
93
+
94
+ expect { @keystone_client.authenticate(env) }.to raise_error(Errors::AuthenticationFailed)
95
+ end
96
+ end
97
+
98
+ context 'with bad endpoint' do
99
+ it 'raise a BadAuthenticationEndpoint error' do
100
+ stub_request(:post, 'http://keystoneAuthV2/tokens')
101
+ .with(
102
+ body: keystone_request_body,
103
+ headers: keystone_request_headers)
104
+ .to_return(
105
+ status: 404)
106
+
107
+ expect { @keystone_client.authenticate(env) }.to raise_error(Errors::BadAuthenticationEndpoint)
108
+ end
109
+ end
110
+
111
+ context 'with /tokens suffix missing in URL' do
112
+ it 'raise add the suffix' do
113
+ config.stub(:openstack_auth_url) { 'http://keystoneAuthV2' }
114
+
115
+ stub_request(:post, 'http://keystoneAuthV2/tokens')
116
+ .with(
117
+ body: keystone_request_body,
118
+ headers: keystone_request_headers)
119
+ .to_return(
120
+ status: 200,
121
+ body: keystone_response_body,
122
+ headers: keystone_request_headers)
123
+
124
+ @keystone_client.authenticate(env)
125
+
126
+ session.token.should eq('0123456789')
127
+ session.project_id.should eq('testTenantId')
128
+ end
129
+ end
130
+
131
+ context 'with internal server error' do
132
+ it 'raise a VagrantOpenstackError error with response body as message' do
133
+ stub_request(:post, 'http://keystoneAuthV2/tokens')
134
+ .with(
135
+ body: keystone_request_body,
136
+ headers: keystone_request_headers)
137
+ .to_return(
138
+ status: 500,
139
+ body: 'Internal server error')
140
+
141
+ begin
142
+ @keystone_client.authenticate(env)
143
+ fail 'Expected Errors::VagrantOpenstackError'
144
+ rescue Errors::VagrantOpenstackError => e
145
+ e.message.should eq('Internal server error')
146
+ end
147
+ end
148
+ end
149
+ end
150
+ end
@@ -0,0 +1,171 @@
1
+ require 'vagrant-conoha/spec_helper'
2
+
3
+ describe VagrantPlugins::ConoHa::NeutronClient do
4
+ let(:http) do
5
+ double('http').tap do |http|
6
+ http.stub(:read_timeout) { 42 }
7
+ http.stub(:open_timeout) { 43 }
8
+ end
9
+ end
10
+
11
+ let(:config) do
12
+ double('config').tap do |config|
13
+ config.stub(:http) { http }
14
+ end
15
+ end
16
+
17
+ let(:env) do
18
+ {}.tap do |env|
19
+ env[:machine] = double('machine')
20
+ env[:machine].stub(:provider_config) { config }
21
+ end
22
+ end
23
+
24
+ let(:session) do
25
+ VagrantPlugins::ConoHa.session
26
+ end
27
+
28
+ before :each do
29
+ session.token = '123456'
30
+ session.project_id = 'a1b2c3'
31
+ session.endpoints = { network: 'http://neutron' }
32
+ @neutron_client = VagrantPlugins::ConoHa.neutron
33
+ end
34
+
35
+ describe 'get_private_networks' do
36
+ context 'with token' do
37
+ it 'returns only private networks for project in session' do
38
+ stub_request(:get, 'http://neutron/networks')
39
+ .with(
40
+ headers:
41
+ {
42
+ 'Accept' => 'application/json',
43
+ 'X-Auth-Token' => '123456'
44
+ })
45
+ .to_return(
46
+ status: 200,
47
+ body: '
48
+ {
49
+ "networks": [
50
+ { "name": "PublicNetwork", "tenant_id": "admin-tenant-id", "id": "net-pub" },
51
+ { "name": "net1", "tenant_id": "a1b2c3", "id": "net-1" },
52
+ { "name": "net2", "tenant_id": "a1b2c3", "id": "net-2" }
53
+ ]
54
+ }
55
+ ')
56
+
57
+ networks = @neutron_client.get_private_networks(env)
58
+
59
+ expect(networks.length).to eq(2)
60
+ expect(networks[0].id).to eq('net-1')
61
+ expect(networks[0].name).to eq('net1')
62
+ expect(networks[1].id).to eq('net-2')
63
+ expect(networks[1].name).to eq('net2')
64
+ end
65
+ end
66
+ end
67
+
68
+ describe 'get_all_networks' do
69
+ context 'with token' do
70
+ it 'returns all networks for project in session' do
71
+ stub_request(:get, 'http://neutron/networks')
72
+ .with(
73
+ headers:
74
+ {
75
+ 'Accept' => 'application/json',
76
+ 'X-Auth-Token' => '123456'
77
+ })
78
+ .to_return(
79
+ status: 200,
80
+ body: '
81
+ {
82
+ "networks": [
83
+ { "name": "PublicNetwork", "tenant_id": "admin-tenant-id", "id": "net-pub" },
84
+ { "name": "net1", "tenant_id": "a1b2c3", "id": "net-1" },
85
+ { "name": "net2", "tenant_id": "a1b2c3", "id": "net-2" }
86
+ ]
87
+ }
88
+ ')
89
+
90
+ networks = @neutron_client.get_all_networks(env)
91
+
92
+ expect(networks.length).to eq(3)
93
+ expect(networks[0].id).to eq('net-pub')
94
+ expect(networks[0].name).to eq('PublicNetwork')
95
+ expect(networks[1].id).to eq('net-1')
96
+ expect(networks[1].name).to eq('net1')
97
+ expect(networks[2].id).to eq('net-2')
98
+ expect(networks[2].name).to eq('net2')
99
+ end
100
+ end
101
+ end
102
+
103
+ describe 'get_subnets' do
104
+ context 'with token' do
105
+ it 'returns all available subnets' do
106
+ stub_request(:get, 'http://neutron/subnets')
107
+ .with(
108
+ headers:
109
+ {
110
+ 'Accept' => 'application/json',
111
+ 'X-Auth-Token' => '123456'
112
+ })
113
+ .to_return(
114
+ status: 200,
115
+ body: '
116
+ {
117
+ "subnets": [
118
+ { "id": "subnet-01", "name": "Subnet 1", "cidr": "192.168.1.0/24", "enable_dhcp": true, "network_id": "net-01" },
119
+ { "id": "subnet-02", "name": "Subnet 2", "cidr": "192.168.2.0/24", "enable_dhcp": false, "network_id": "net-01" },
120
+ { "id": "subnet-03", "name": "Subnet 3", "cidr": "192.168.100.0/24", "enable_dhcp": true, "network_id": "net-02" }
121
+ ]
122
+ }
123
+ ')
124
+
125
+ networks = @neutron_client.get_subnets(env)
126
+
127
+ expect(networks).to eq [Subnet.new('subnet-01', 'Subnet 1', '192.168.1.0/24', true, 'net-01'),
128
+ Subnet.new('subnet-02', 'Subnet 2', '192.168.2.0/24', false, 'net-01'),
129
+ Subnet.new('subnet-03', 'Subnet 3', '192.168.100.0/24', true, 'net-02')]
130
+ end
131
+ end
132
+ end
133
+
134
+ describe 'get_api_version_list' do
135
+ context 'basic' do
136
+ it 'returns version list' do
137
+ stub_request(:get, 'http://neutron/')
138
+ .with(header: { 'Accept' => 'application/json' })
139
+ .to_return(
140
+ status: 200,
141
+ body: '{
142
+ "versions": [
143
+ {
144
+ "status": "...",
145
+ "id": "v1.0",
146
+ "links": [
147
+ {
148
+ "href": "http://neutron/v1.0",
149
+ "rel": "self"
150
+ }
151
+ ]
152
+ },
153
+ {
154
+ "status": "CURRENT",
155
+ "id": "v2.0",
156
+ "links": [
157
+ {
158
+ "href": "http://neutron/v2.0",
159
+ "rel": "self"
160
+ }
161
+ ]
162
+ }
163
+ ]}')
164
+
165
+ versions = @neutron_client.get_api_version_list(env, :network)
166
+
167
+ expect(versions.size).to eq(2)
168
+ end
169
+ end
170
+ end
171
+ end
@@ -0,0 +1,757 @@
1
+ require 'vagrant-conoha/spec_helper'
2
+
3
+ describe VagrantPlugins::ConoHa::NovaClient do
4
+ include FakeFS::SpecHelpers::All
5
+
6
+ let(:filename) { 'key.pub' }
7
+ let(:ssh_key_content) { 'my public key' }
8
+
9
+ let(:http) do
10
+ double('http').tap do |http|
11
+ http.stub(:read_timeout) { 42 }
12
+ http.stub(:open_timeout) { 43 }
13
+ end
14
+ end
15
+
16
+ let(:config) do
17
+ double('config').tap do |config|
18
+ config.stub(:openstack_auth_url) { 'http://novaAuthV2' }
19
+ config.stub(:openstack_compute_url) { nil }
20
+ config.stub(:tenant_name) { 'testTenant' }
21
+ config.stub(:username) { 'username' }
22
+ config.stub(:password) { 'password' }
23
+ config.stub(:http) { http }
24
+ end
25
+ end
26
+
27
+ let(:file) do
28
+ double('file').tap do |file|
29
+ file.stub(:read) { ssh_key_content }
30
+ end
31
+ end
32
+
33
+ let(:env) do
34
+ {}.tap do |env|
35
+ env[:ui] = double('ui')
36
+ env[:ui].stub(:info).with(anything)
37
+ env[:machine] = double('machine')
38
+ env[:machine].stub(:provider_config) { config }
39
+ end
40
+ end
41
+
42
+ let(:session) do
43
+ VagrantPlugins::ConoHa.session
44
+ end
45
+
46
+ before :each do
47
+ session.token = '123456'
48
+ session.project_id = 'a1b2c3'
49
+ session.endpoints = { compute: 'http://nova/a1b2c3' }
50
+ @nova_client = VagrantPlugins::ConoHa.nova
51
+ end
52
+
53
+ describe 'instance_exists' do
54
+ context 'instance not found' do
55
+ it 'raise an InstanceNotFound error' do
56
+ stub_request(:post, 'http://nova/a1b2c3/servers/o1o2o3/action')
57
+ .with(
58
+ body: '{"os-start":null}',
59
+ headers:
60
+ {
61
+ 'Accept' => 'application/json',
62
+ 'Content-Type' => 'application/json',
63
+ 'X-Auth-Token' => '123456'
64
+ })
65
+ .to_return(
66
+ status: 404,
67
+ body: '{"itemNotFound": {"message": "Instance could not be found", "code": 404}}')
68
+
69
+ expect { @nova_client.start_server(env, 'o1o2o3') }.to raise_error(VagrantPlugins::ConoHa::Errors::InstanceNotFound)
70
+ end
71
+ end
72
+ end
73
+
74
+ describe 'get_all_flavors' do
75
+ context 'with token and project_id acquainted' do
76
+ it 'returns all flavors' do
77
+ stub_request(:get, 'http://nova/a1b2c3/flavors/detail')
78
+ .with(
79
+ headers:
80
+ {
81
+ 'Accept' => 'application/json',
82
+ 'X-Auth-Token' => '123456'
83
+ })
84
+ .to_return(
85
+ status: 200,
86
+ body: '{
87
+ "flavors": [
88
+ { "id": "f1", "name": "flavor1", "vcpus":"1", "ram": "1024", "disk": "10"},
89
+ { "id": "f2", "name": "flavor2", "vcpus":"2", "ram": "2048", "disk": "20"}
90
+ ]}')
91
+
92
+ flavors = @nova_client.get_all_flavors(env)
93
+
94
+ expect(flavors.length).to eq(2)
95
+ expect(flavors[0]).to eq(Flavor.new('f1', 'flavor1', '1', '1024', '10'))
96
+ expect(flavors[1]).to eq(Flavor.new('f2', 'flavor2', '2', '2048', '20'))
97
+ end
98
+ end
99
+ end
100
+
101
+ describe 'get_all_images' do
102
+ context 'with token and project_id acquainted' do
103
+ it 'returns all images' do
104
+ stub_request(:get, 'http://nova/a1b2c3/images')
105
+ .with(
106
+ headers:
107
+ {
108
+ 'Accept' => 'application/json',
109
+ 'X-Auth-Token' => '123456'
110
+ })
111
+ .to_return(
112
+ status: 200,
113
+ body: '{ "images": [ { "id": "i1", "name": "image1"}, { "id": "i2", "name": "image2"} ] }')
114
+
115
+ images = @nova_client.get_all_images(env)
116
+
117
+ expect(images.length).to eq(2)
118
+ expect(images[0].id).to eq('i1')
119
+ expect(images[0].name).to eq('image1')
120
+ expect(images[1].id).to eq('i2')
121
+ expect(images[1].name).to eq('image2')
122
+ end
123
+ end
124
+ end
125
+
126
+ describe 'create_server' do
127
+ context 'with token and project_id acquainted' do
128
+ it 'returns new instance id' do
129
+ stub_request(:post, 'http://nova/a1b2c3/servers')
130
+ .with(
131
+ body: '{"server":{"name":"inst","imageRef":"img","flavorRef":"flav","key_name":"key"}}',
132
+ headers:
133
+ {
134
+ 'Accept' => 'application/json',
135
+ 'Content-Type' => 'application/json',
136
+ 'X-Auth-Token' => '123456'
137
+ })
138
+ .to_return(status: 202, body: '{ "server": { "id": "o1o2o3" } }')
139
+
140
+ instance_id = @nova_client.create_server(env, name: 'inst', image_ref: 'img', flavor_ref: 'flav', networks: nil, keypair: 'key')
141
+
142
+ expect(instance_id).to eq('o1o2o3')
143
+ end
144
+
145
+ context 'with all options specified' do
146
+ it 'returns new instance id' do
147
+ stub_request(:post, 'http://nova/a1b2c3/servers')
148
+ .with(
149
+ body: '{"server":{"name":"inst","imageRef":"img","flavorRef":"flav","key_name":"key",'\
150
+ '"security_groups":[{"name":"default"}],"user_data":"dXNlcl9kYXRhX3Rlc3Q=\n","metadata":"metadata_test"},'\
151
+ '"os:scheduler_hints":"sched_hints_test"}',
152
+ headers:
153
+ {
154
+ 'Accept' => 'application/json',
155
+ 'Content-Type' => 'application/json',
156
+ 'X-Auth-Token' => '123456'
157
+ })
158
+ .to_return(status: 202, body: '{ "server": { "id": "o1o2o3" } }')
159
+
160
+ instance_id = @nova_client.create_server(
161
+ env,
162
+ name: 'inst',
163
+ image_ref: 'img',
164
+ flavor_ref: 'flav',
165
+ networks: nil,
166
+ keypair: 'key',
167
+ security_groups: [{ name: 'default' }],
168
+ user_data: 'user_data_test',
169
+ metadata: 'metadata_test',
170
+ scheduler_hints: 'sched_hints_test')
171
+
172
+ expect(instance_id).to eq('o1o2o3')
173
+ end
174
+ end
175
+
176
+ context 'with two networks' do
177
+ it 'returns new instance id' do
178
+ stub_request(:post, 'http://nova/a1b2c3/servers')
179
+ .with(
180
+ body: '{"server":{"name":"inst","imageRef":"img","flavorRef":"flav","key_name":"key","networks":[{"uuid":"net1"},{"uuid":"net2"}]}}',
181
+ headers:
182
+ {
183
+ 'Accept' => 'application/json',
184
+ 'Content-Type' => 'application/json',
185
+ 'X-Auth-Token' => '123456'
186
+ })
187
+ .to_return(status: 202, body: '{ "server": { "id": "o1o2o3" } }')
188
+
189
+ instance_id = @nova_client.create_server(env, name: 'inst', image_ref: 'img', flavor_ref: 'flav',
190
+ networks: [{ uuid: 'net1' }, { uuid: 'net2' }], keypair: 'key')
191
+
192
+ expect(instance_id).to eq('o1o2o3')
193
+ end
194
+ end
195
+
196
+ context 'with availability_zone' do
197
+ it 'returns new instance id' do
198
+ stub_request(:post, 'http://nova/a1b2c3/servers')
199
+ .with(
200
+ body: '{"server":{"name":"inst","imageRef":"img","flavorRef":"flav","key_name":"key","availability_zone":"avz"}}',
201
+ headers:
202
+ {
203
+ 'Accept' => 'application/json',
204
+ 'Content-Type' => 'application/json',
205
+ 'X-Auth-Token' => '123456'
206
+ })
207
+ .to_return(status: 202, body: '{ "server": { "id": "o1o2o3" } }')
208
+
209
+ instance_id = @nova_client.create_server(env, name: 'inst', image_ref: 'img', flavor_ref: 'flav', keypair: 'key', availability_zone: 'avz')
210
+
211
+ expect(instance_id).to eq('o1o2o3')
212
+ end
213
+ end
214
+
215
+ context 'with volume_boot creating volume' do
216
+ it 'create bootable volume and returns new instance id' do
217
+ stub_request(:post, 'http://nova/a1b2c3/servers')
218
+ .with(
219
+ body: '{"server":{"name":"inst","block_device_mapping_v2":[{"boot_index":"0","volume_size":"10","uuid":"image_id",'\
220
+ '"device_name":"vda","source_type":"image","destination_type":"volume","delete_on_termination":"false"}],'\
221
+ '"flavorRef":"flav","key_name":"key"}}',
222
+ headers:
223
+ {
224
+ 'Accept' => 'application/json',
225
+ 'Content-Type' => 'application/json',
226
+ 'X-Auth-Token' => '123456'
227
+ })
228
+ .to_return(status: 202, body: '{ "server": { "id": "o1o2o3" } }')
229
+
230
+ instance_id = @nova_client.create_server(env,
231
+ name: 'inst',
232
+ image_ref: nil,
233
+ volume_boot: { image: 'image_id', device: 'vda', size: '10',
234
+ delete_on_destroy: 'false' },
235
+ flavor_ref: 'flav',
236
+ keypair: 'key')
237
+ expect(instance_id).to eq('o1o2o3')
238
+ end
239
+ end
240
+
241
+ context 'with volume_boot id' do
242
+ it 'returns new instance id' do
243
+ stub_request(:post, 'http://nova/a1b2c3/servers')
244
+ .with(
245
+ body: '{"server":{"name":"inst","block_device_mapping":[{"volume_id":"vol","device_name":"vda"}],'\
246
+ '"flavorRef":"flav","key_name":"key"}}',
247
+ headers:
248
+ {
249
+ 'Accept' => 'application/json',
250
+ 'Content-Type' => 'application/json',
251
+ 'X-Auth-Token' => '123456'
252
+ })
253
+ .to_return(status: 202, body: '{ "server": { "id": "o1o2o3" } }')
254
+
255
+ instance_id = @nova_client.create_server(env,
256
+ name: 'inst',
257
+ volume_boot: { id: 'vol', device: 'vda' },
258
+ flavor_ref: 'flav',
259
+ keypair: 'key')
260
+ expect(instance_id).to eq('o1o2o3')
261
+ end
262
+ end
263
+ end
264
+ end
265
+
266
+ describe 'delete_server' do
267
+ context 'with token and project_id acquainted' do
268
+ it 'returns new instance id' do
269
+ stub_request(:delete, 'http://nova/a1b2c3/servers/o1o2o3')
270
+ .with(
271
+ headers: {
272
+ 'Accept' => 'application/json',
273
+ 'X-Auth-Token' => '123456'
274
+ })
275
+ .to_return(status: 204)
276
+
277
+ @nova_client.delete_server(env, 'o1o2o3')
278
+ end
279
+ end
280
+ end
281
+
282
+ describe 'import_keypair_from_file' do
283
+ context 'with token and project_id acquainted' do
284
+ it 'returns newly created keypair name' do
285
+ File.should_receive(:exist?).with(filename).and_return(true)
286
+ File.should_receive(:open).with(filename).and_return(file)
287
+ Kernel.stub(:rand).and_return(2_036_069_739_008)
288
+
289
+ stub_request(:post, 'http://nova/a1b2c3/os-keypairs')
290
+ .with(
291
+ body: "{\"keypair\":{\"name\":\"vagrant-generated-pzcvcpa8\",\"public_key\":\"#{ssh_key_content}\"}}",
292
+ headers: {
293
+ 'Accept' => 'application/json',
294
+ 'Content-Type' => 'application/json',
295
+ 'X-Auth-Token' => '123456' })
296
+ .to_return(status: 200, body: '
297
+ {
298
+ "keypair": {
299
+ "name": "created_key_name"
300
+ }
301
+ }')
302
+
303
+ @nova_client.import_keypair_from_file(env, filename)
304
+ end
305
+ end
306
+ end
307
+
308
+ describe 'delete_keypair_if_vagrant' do
309
+ context 'with token and project_id acquainted' do
310
+ context 'with keypair not generated by vagrant' do
311
+ it 'do nothing' do
312
+ stub_request(:get, 'http://nova/a1b2c3/servers/o1o2o3')
313
+ .with(headers:
314
+ {
315
+ 'Accept' => 'application/json',
316
+ 'X-Auth-Token' => '123456'
317
+ })
318
+ .to_return(status: 200, body: '
319
+ {
320
+ "server": {
321
+ "id": "o1o2o3",
322
+ "key_name": "other_key"
323
+ }
324
+ }
325
+ ')
326
+
327
+ @nova_client.delete_keypair_if_vagrant(env, 'o1o2o3')
328
+ end
329
+ end
330
+ context 'with keypair generated by vagrant' do
331
+ it 'deletes the key on nova' do
332
+ stub_request(:delete, 'http://nova/a1b2c3/os-keypairs/vagrant-generated-1234')
333
+ .with(headers: { 'X-Auth-Token' => '123456' })
334
+ .to_return(status: 202)
335
+ stub_request(:get, 'http://nova/a1b2c3/servers/o1o2o3')
336
+ .with(headers:
337
+ {
338
+ 'Accept' => 'application/json',
339
+ 'X-Auth-Token' => '123456'
340
+ })
341
+ .to_return(status: 200, body: '
342
+ {
343
+ "server": {
344
+ "id": "o1o2o3",
345
+ "key_name": "vagrant-generated-1234"
346
+ }
347
+ }
348
+ ')
349
+
350
+ @nova_client.delete_keypair_if_vagrant(env, 'o1o2o3')
351
+ end
352
+ end
353
+ context 'with keypair generated by vagrant and missing in server details' do
354
+ it 'do nothing' do
355
+ stub_request(:delete, 'http://nova/a1b2c3/os-keypairs/vagrant-generated-1234')
356
+ .with(headers: { 'X-Auth-Token' => '123456' })
357
+ .to_return(status: 202)
358
+ stub_request(:get, 'http://nova/a1b2c3/servers/o1o2o3')
359
+ .with(headers:
360
+ {
361
+ 'Accept' => 'application/json',
362
+ 'X-Auth-Token' => '123456'
363
+ })
364
+ .to_return(status: 200, body: '
365
+ {
366
+ "server": {
367
+ "id": "o1o2o3"
368
+ }
369
+ }
370
+ ')
371
+
372
+ @nova_client.delete_keypair_if_vagrant(env, 'o1o2o3')
373
+ end
374
+ end
375
+ end
376
+ end
377
+
378
+ describe 'suspend_server' do
379
+ context 'with token and project_id acquainted' do
380
+ it 'returns new instance id' do
381
+ stub_request(:post, 'http://nova/a1b2c3/servers/o1o2o3/action')
382
+ .with(
383
+ body: '{"suspend":null}',
384
+ headers:
385
+ {
386
+ 'Accept' => 'application/json',
387
+ 'Content-Type' => 'application/json',
388
+ 'X-Auth-Token' => '123456'
389
+ })
390
+ .to_return(status: 202)
391
+
392
+ @nova_client.suspend_server(env, 'o1o2o3')
393
+ end
394
+ end
395
+ end
396
+
397
+ describe 'resume_server' do
398
+ context 'with token and project_id acquainted' do
399
+ it 'returns new instance id' do
400
+ stub_request(:post, 'http://nova/a1b2c3/servers/o1o2o3/action')
401
+ .with(
402
+ body: '{"resume":null}',
403
+ headers:
404
+ {
405
+ 'Accept' => 'application/json',
406
+ 'Content-Type' => 'application/json',
407
+ 'X-Auth-Token' => '123456'
408
+ })
409
+ .to_return(status: 202)
410
+
411
+ @nova_client.resume_server(env, 'o1o2o3')
412
+ end
413
+ end
414
+ end
415
+
416
+ describe 'stop_server' do
417
+ context 'with token and project_id acquainted' do
418
+ it 'returns new instance id' do
419
+ stub_request(:post, 'http://nova/a1b2c3/servers/o1o2o3/action')
420
+ .with(
421
+ body: '{"os-stop":null}',
422
+ headers:
423
+ {
424
+ 'Accept' => 'application/json',
425
+ 'Content-Type' => 'application/json',
426
+ 'X-Auth-Token' => '123456'
427
+ })
428
+ .to_return(status: 202)
429
+
430
+ @nova_client.stop_server(env, 'o1o2o3')
431
+ end
432
+ end
433
+ end
434
+
435
+ describe 'start_server' do
436
+ context 'with token and project_id acquainted' do
437
+ it 'returns new instance id' do
438
+ stub_request(:post, 'http://nova/a1b2c3/servers/o1o2o3/action')
439
+ .with(
440
+ body: '{"os-start":null}',
441
+ headers:
442
+ {
443
+ 'Accept' => 'application/json',
444
+ 'Content-Type' => 'application/json',
445
+ 'X-Auth-Token' => '123456'
446
+ })
447
+ .to_return(status: 202)
448
+
449
+ @nova_client.start_server(env, 'o1o2o3')
450
+ end
451
+ end
452
+ end
453
+
454
+ describe 'get_all_floating_ips' do
455
+ context 'with token and project_id acquainted' do
456
+ it 'returns all floating ips' do
457
+ stub_request(:get, 'http://nova/a1b2c3/os-floating-ips')
458
+ .with(headers:
459
+ {
460
+ 'Accept' => 'application/json',
461
+ 'Accept-Encoding' => 'gzip, deflate',
462
+ 'User-Agent' => 'Ruby',
463
+ 'X-Auth-Token' => '123456'
464
+ })
465
+ .to_return(status: 200, body: '
466
+ {
467
+ "floating_ips": [
468
+ {"instance_id": "1234",
469
+ "ip": "185.39.216.45",
470
+ "fixed_ip": "192.168.0.54",
471
+ "id": "2345",
472
+ "pool": "PublicNetwork-01"
473
+ },
474
+ {
475
+ "instance_id": null,
476
+ "ip": "185.39.216.95",
477
+ "fixed_ip": null,
478
+ "id": "3456",
479
+ "pool": "PublicNetwork-02"
480
+ }]
481
+ }')
482
+
483
+ floating_ips = @nova_client.get_all_floating_ips(env)
484
+
485
+ expect(floating_ips).not_to be_nil
486
+ expect(floating_ips.size).to eq(2)
487
+ expect(floating_ips[0].ip).to eql('185.39.216.45')
488
+ expect(floating_ips[0].instance_id).to eql('1234')
489
+ expect(floating_ips[0].pool).to eql('PublicNetwork-01')
490
+ expect(floating_ips[1].ip).to eql('185.39.216.95')
491
+ expect(floating_ips[1].instance_id).to be(nil)
492
+ expect(floating_ips[1].pool).to eql('PublicNetwork-02')
493
+ end
494
+ end
495
+ end
496
+
497
+ describe 'get_all_floating_ips' do
498
+ context 'with token and project_id acquainted' do
499
+ it 'return newly allocated floating_ip' do
500
+ stub_request(:post, 'http://nova/a1b2c3/os-floating-ips')
501
+ .with(body: '{"pool":"pool-1"}',
502
+ headers: {
503
+ 'Accept' => 'application/json',
504
+ 'Content-Type' => 'application/json',
505
+ 'X-Auth-Token' => '123456' })
506
+ .to_return(status: 200, body: '
507
+ {
508
+ "floating_ip": {
509
+ "instance_id": null,
510
+ "ip": "183.45.67.89",
511
+ "fixed_ip": null,
512
+ "id": "o1o2o3",
513
+ "pool": "pool-1"
514
+ }
515
+ }')
516
+ floating_ip = @nova_client.allocate_floating_ip(env, 'pool-1')
517
+
518
+ expect(floating_ip.ip).to eql('183.45.67.89')
519
+ expect(floating_ip.instance_id).to be(nil)
520
+ expect(floating_ip.pool).to eql('pool-1')
521
+ end
522
+ end
523
+ end
524
+
525
+ describe 'get_server_details' do
526
+ context 'with token and project_id acquainted' do
527
+ it 'returns server details' do
528
+ stub_request(:get, 'http://nova/a1b2c3/servers/o1o2o3')
529
+ .with(headers:
530
+ {
531
+ 'Accept' => 'application/json',
532
+ 'X-Auth-Token' => '123456'
533
+ })
534
+ .to_return(status: 200, body: '
535
+ {
536
+ "server": {
537
+ "addresses": { "private": [ { "addr": "192.168.0.3", "version": 4 } ] },
538
+ "created": "2012-08-20T21:11:09Z",
539
+ "flavor": { "id": "1" },
540
+ "id": "o1o2o3",
541
+ "image": { "id": "i1" },
542
+ "name": "new-server-test",
543
+ "progress": 0,
544
+ "status": "ACTIVE",
545
+ "tenant_id": "openstack",
546
+ "updated": "2012-08-20T21:11:09Z",
547
+ "user_id": "fake"
548
+ }
549
+ }
550
+ ')
551
+
552
+ server = @nova_client.get_server_details(env, 'o1o2o3')
553
+
554
+ expect(server['id']).to eq('o1o2o3')
555
+ expect(server['status']).to eq('ACTIVE')
556
+ expect(server['tenant_id']).to eq('openstack')
557
+ expect(server['image']['id']).to eq('i1')
558
+ expect(server['flavor']['id']).to eq('1')
559
+ end
560
+ end
561
+ end
562
+
563
+ describe 'add_floating_ip' do
564
+ context 'with token and project_id acquainted and IP available' do
565
+ it 'returns server details' do
566
+ stub_request(:get, 'http://nova/a1b2c3/os-floating-ips')
567
+ .with(headers:
568
+ {
569
+ 'Accept' => 'application/json',
570
+ 'X-Auth-Token' => '123456'
571
+ })
572
+ .to_return(status: 200, body: '
573
+ {
574
+ "floating_ips": [
575
+ {
576
+ "fixed_ip": null,
577
+ "id": 1,
578
+ "instance_id": null,
579
+ "ip": "1.2.3.4",
580
+ "pool": "nova"
581
+ },
582
+ {
583
+ "fixed_ip": null,
584
+ "id": 2,
585
+ "instance_id": null,
586
+ "ip": "5.6.7.8",
587
+ "pool": "nova"
588
+ }
589
+ ]
590
+ }')
591
+
592
+ stub_request(:post, 'http://nova/a1b2c3/servers/o1o2o3/action')
593
+ .with(body: '{"addFloatingIp":{"address":"1.2.3.4"}}',
594
+ headers:
595
+ {
596
+ 'Accept' => 'application/json',
597
+ 'Content-Type' => 'application/json',
598
+ 'X-Auth-Token' => '123456'
599
+ })
600
+ .to_return(status: 202)
601
+
602
+ @nova_client.add_floating_ip(env, 'o1o2o3', '1.2.3.4')
603
+ end
604
+ end
605
+
606
+ context 'with token and project_id acquainted and IP already in use' do
607
+ it 'raise an error' do
608
+ stub_request(:get, 'http://nova/a1b2c3/os-floating-ips')
609
+ .with(headers:
610
+ {
611
+ 'Accept' => 'application/json',
612
+ 'X-Auth-Token' => '123456'
613
+ })
614
+ .to_return(status: 200, body: '
615
+ {
616
+ "floating_ips": [
617
+ {
618
+ "fixed_ip": null,
619
+ "id": 1,
620
+ "instance_id": "inst",
621
+ "ip": "1.2.3.4",
622
+ "pool": "nova"
623
+ },
624
+ {
625
+ "fixed_ip": null,
626
+ "id": 2,
627
+ "instance_id": null,
628
+ "ip": "5.6.7.8",
629
+ "pool": "nova"
630
+ }
631
+ ]
632
+ }')
633
+
634
+ expect { @nova_client.add_floating_ip(env, 'o1o2o3', '1.2.3.4') }.to raise_error(Errors::FloatingIPAlreadyAssigned)
635
+ end
636
+ end
637
+
638
+ context 'with token and project_id acquainted and IP not allocated' do
639
+ it 'raise an error' do
640
+ stub_request(:get, 'http://nova/a1b2c3/os-floating-ips')
641
+ .with(headers:
642
+ {
643
+ 'Accept' => 'application/json',
644
+ 'X-Auth-Token' => '123456'
645
+ })
646
+ .to_return(status: 200, body: '
647
+ {
648
+ "floating_ips": [
649
+ {
650
+ "fixed_ip": null,
651
+ "id": 2,
652
+ "instance_id": null,
653
+ "ip": "5.6.7.8",
654
+ "pool": "nova"
655
+ }
656
+ ]
657
+ }')
658
+
659
+ expect { @nova_client.add_floating_ip(env, 'o1o2o3', '1.2.3.4') }.to raise_error(Errors::FloatingIPNotAvailable)
660
+ end
661
+ end
662
+ end
663
+
664
+ describe 'get_floating_ip_pools' do
665
+ context 'with token and project_id acquainted' do
666
+ it 'should return floating ip pool' do
667
+ stub_request(:get, 'http://nova/a1b2c3/os-floating-ip-pools')
668
+ .with(headers: { 'Accept' => 'application/json', 'X-Auth-Token' => '123456' })
669
+ .to_return(status: 200, body: '
670
+ {
671
+ "floating_ip_pools": [
672
+ {
673
+ "name": "pool1"
674
+ },
675
+ {
676
+ "name": "pool2"
677
+ }
678
+ ]
679
+ }
680
+ ')
681
+
682
+ pools = @nova_client.get_floating_ip_pools(env)
683
+
684
+ expect(pools[0]['name']).to eq('pool1')
685
+ expect(pools[1]['name']).to eq('pool2')
686
+ end
687
+ end
688
+ end
689
+
690
+ describe 'get_floating_ips' do
691
+ context 'with token and project_id acquainted' do
692
+ it 'should return floating ip list' do
693
+ stub_request(:get, 'http://nova/a1b2c3/os-floating-ips')
694
+ .with(headers: { 'Accept' => 'application/json', 'X-Auth-Token' => '123456' })
695
+ .to_return(status: 200, body: '
696
+ {
697
+ "floating_ips": [
698
+ {
699
+ "fixed_ip": null,
700
+ "id": 1,
701
+ "instance_id": null,
702
+ "ip": "10.10.10.1",
703
+ "pool": "pool1"
704
+ },
705
+ {
706
+ "fixed_ip": null,
707
+ "id": 2,
708
+ "instance_id": "inst001",
709
+ "ip": "10.10.10.2",
710
+ "pool": "pool2"
711
+ }
712
+ ]
713
+ }
714
+ ')
715
+
716
+ ips = @nova_client.get_floating_ips(env)
717
+
718
+ expect(ips[0]['ip']).to eq('10.10.10.1')
719
+ expect(ips[0]['instance_id']).to eq(nil)
720
+ expect(ips[0]['pool']).to eq('pool1')
721
+
722
+ expect(ips[1]['ip']).to eq('10.10.10.2')
723
+ expect(ips[1]['instance_id']).to eq('inst001')
724
+ expect(ips[1]['pool']).to eq('pool2')
725
+ end
726
+ end
727
+ end
728
+
729
+ describe 'attach_volume' do
730
+ context 'with token and project_id acquainted' do
731
+ context 'with volume id and device' do
732
+ it 'call the nova api' do
733
+ stub_request(:post, 'http://nova/a1b2c3/servers/9876/os-volume_attachments')
734
+ .with(headers:
735
+ {
736
+ 'Accept' => 'application/json',
737
+ 'X-Auth-Token' => '123456'
738
+ },
739
+ body: '{"volumeAttachment":{"volumeId":"n1n2","device":"/dev/vdg"}}')
740
+ .to_return(status: 200, body: '
741
+ {
742
+ "volumeAttachment": {
743
+ "device": "/dev/vdg",
744
+ "id": "attachment-01",
745
+ "serverId": "9876",
746
+ "volumeId": "n1n2"
747
+ }
748
+ }
749
+ ')
750
+
751
+ attachment = @nova_client.attach_volume(env, '9876', 'n1n2', '/dev/vdg')
752
+ expect(attachment['id']).to eq('attachment-01')
753
+ end
754
+ end
755
+ end
756
+ end
757
+ end