vagrant-openstack-provider 0.3.4.pre → 0.4.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.
- data/CHANGELOG.md +25 -0
- data/Gemfile +3 -0
- data/Vagrantfile +1 -1
- data/lib/vagrant-openstack-provider/action/connect_openstack.rb +1 -0
- data/lib/vagrant-openstack-provider/action/create_server.rb +135 -19
- data/lib/vagrant-openstack-provider/action/read_ssh_info.rb +16 -2
- data/lib/vagrant-openstack-provider/client/cinder.rb +40 -0
- data/lib/vagrant-openstack-provider/client/domain.rb +64 -4
- data/lib/vagrant-openstack-provider/client/nova.rb +22 -2
- data/lib/vagrant-openstack-provider/client/openstack.rb +5 -0
- data/lib/vagrant-openstack-provider/command/main.rb +2 -1
- data/lib/vagrant-openstack-provider/command/volume_list.rb +27 -0
- data/lib/vagrant-openstack-provider/config.rb +64 -1
- data/lib/vagrant-openstack-provider/errors.rb +36 -0
- data/lib/vagrant-openstack-provider/version.rb +1 -1
- data/locales/en.yml +28 -0
- data/spec/vagrant-openstack-provider/action/connect_openstack_spec.rb +1 -0
- data/spec/vagrant-openstack-provider/action/create_server_spec.rb +250 -9
- data/spec/vagrant-openstack-provider/action/read_ssh_info_spec.rb +48 -3
- data/spec/vagrant-openstack-provider/client/cinder_spec.rb +114 -0
- data/spec/vagrant-openstack-provider/client/nova_spec.rb +50 -0
- data/spec/vagrant-openstack-provider/command/floatingip_list_spec.rb +2 -11
- data/spec/vagrant-openstack-provider/command/volume_list_spec.rb +33 -0
- data/spec/vagrant-openstack-provider/config_spec.rb +20 -0
- metadata +42 -12
- checksums.yaml +0 -7
@@ -51,10 +51,17 @@ module VagrantPlugins
|
|
51
51
|
def create_server(env, options)
|
52
52
|
server = {}.tap do |s|
|
53
53
|
s['name'] = options[:name]
|
54
|
-
|
54
|
+
if options[:image_ref].nil?
|
55
|
+
s['block_device_mapping'] = [{ volume_id: options[:volume_boot][:id], device_name: options[:volume_boot][:device] }]
|
56
|
+
else
|
57
|
+
s['imageRef'] = options[:image_ref]
|
58
|
+
end
|
55
59
|
s['flavorRef'] = options[:flavor_ref]
|
56
60
|
s['key_name'] = options[:keypair]
|
57
61
|
s['availability_zone'] = options[:availability_zone] unless options[:availability_zone].nil?
|
62
|
+
s['security_groups'] = options[:security_groups] unless options[:security_groups].nil?
|
63
|
+
s['user_data'] = options[:user_data] unless options[:user_data].nil?
|
64
|
+
s['metadata'] = options[:metadata] unless options[:metadata].nil?
|
58
65
|
unless options[:networks].nil? || options[:networks].empty?
|
59
66
|
s['networks'] = []
|
60
67
|
options[:networks].each do |uuid|
|
@@ -62,7 +69,9 @@ module VagrantPlugins
|
|
62
69
|
end
|
63
70
|
end
|
64
71
|
end
|
65
|
-
|
72
|
+
object = { server: server }
|
73
|
+
object[:scheduler_hints] = options[:scheduler_hints] unless options[:scheduler_hints].nil?
|
74
|
+
server = post(env, "#{@session.endpoints[:compute]}/servers", object.to_json)
|
66
75
|
JSON.parse(server)['server']['id']
|
67
76
|
end
|
68
77
|
|
@@ -133,6 +142,17 @@ module VagrantPlugins
|
|
133
142
|
JSON.parse(floating_ips)['floating_ips']
|
134
143
|
end
|
135
144
|
|
145
|
+
def attach_volume(env, server_id, volume_id, device = nil)
|
146
|
+
attachment = post(env, "#{@session.endpoints[:compute]}/servers/#{server_id}/os-volume_attachments",
|
147
|
+
{
|
148
|
+
volumeAttachment: {
|
149
|
+
volumeId: volume_id,
|
150
|
+
device: device
|
151
|
+
}
|
152
|
+
}.to_json)
|
153
|
+
JSON.parse(attachment)['volumeAttachment']
|
154
|
+
end
|
155
|
+
|
136
156
|
private
|
137
157
|
|
138
158
|
VM_STATES =
|
@@ -5,6 +5,7 @@ require 'json'
|
|
5
5
|
require 'vagrant-openstack-provider/client/keystone'
|
6
6
|
require 'vagrant-openstack-provider/client/nova'
|
7
7
|
require 'vagrant-openstack-provider/client/neutron'
|
8
|
+
require 'vagrant-openstack-provider/client/cinder'
|
8
9
|
|
9
10
|
module VagrantPlugins
|
10
11
|
module Openstack
|
@@ -41,5 +42,9 @@ module VagrantPlugins
|
|
41
42
|
def self.neutron
|
42
43
|
Openstack::NeutronClient.instance
|
43
44
|
end
|
45
|
+
|
46
|
+
def self.cinder
|
47
|
+
Openstack::CinderClient.instance
|
48
|
+
end
|
44
49
|
end
|
45
50
|
end
|
@@ -5,7 +5,8 @@ module VagrantPlugins
|
|
5
5
|
{ name: :'image-list', file: 'image_list' , clazz: 'ImageList' },
|
6
6
|
{ name: :'flavor-list', file: 'flavor_list', clazz: 'FlavorList' },
|
7
7
|
{ name: :'network-list', file: 'network_list', clazz: 'NetworkList' },
|
8
|
-
{ name: :'floatingip-list', file: 'floatingip_list', clazz: 'FloatingIpList' }
|
8
|
+
{ name: :'floatingip-list', file: 'floatingip_list', clazz: 'FloatingIpList' },
|
9
|
+
{ name: :'volume-list', file: 'volume_list', clazz: 'VolumeList' }
|
9
10
|
]
|
10
11
|
|
11
12
|
class Main < Vagrant.plugin('2', :command)
|
@@ -0,0 +1,27 @@
|
|
1
|
+
require 'vagrant-openstack-provider/command/utils'
|
2
|
+
require 'vagrant-openstack-provider/command/abstract_command'
|
3
|
+
|
4
|
+
module VagrantPlugins
|
5
|
+
module Openstack
|
6
|
+
module Command
|
7
|
+
class VolumeList < AbstractCommand
|
8
|
+
include VagrantPlugins::Openstack::Command::Utils
|
9
|
+
|
10
|
+
def self.synopsis
|
11
|
+
I18n.t('vagrant_openstack.command.volume_list_synopsis')
|
12
|
+
end
|
13
|
+
def cmd(name, argv, env)
|
14
|
+
fail Errors::NoArgRequiredForCommand, cmd: name unless argv.size == 0 || argv == ['--']
|
15
|
+
volumes = env[:openstack_client].cinder.get_all_volumes(env)
|
16
|
+
|
17
|
+
rows = []
|
18
|
+
volumes.each do |v|
|
19
|
+
attachment = "#{v.instance_id} (#{v.device})" unless v.instance_id.nil?
|
20
|
+
rows << [v.id, v.name, v.size, v.status, attachment]
|
21
|
+
end
|
22
|
+
display_table(env, ['Id', 'Name', 'Size (Go)', 'Status', 'Attachment (instance id and device)'], rows)
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
@@ -18,6 +18,11 @@ module VagrantPlugins
|
|
18
18
|
#
|
19
19
|
attr_accessor :openstack_network_url
|
20
20
|
|
21
|
+
# The block storage service url to access Openstack. If nil, it will read from
|
22
|
+
# hypermedia catalog form REST API
|
23
|
+
#
|
24
|
+
attr_accessor :openstack_volume_url
|
25
|
+
|
21
26
|
# The authentication endpoint. This defaults to Openstack's global authentication endpoint.
|
22
27
|
attr_accessor :openstack_auth_url
|
23
28
|
|
@@ -29,6 +34,10 @@ module VagrantPlugins
|
|
29
34
|
# expression to partially match a name.
|
30
35
|
attr_accessor :image
|
31
36
|
|
37
|
+
# Volume to boot the vm from
|
38
|
+
#
|
39
|
+
attr_accessor :volume_boot
|
40
|
+
|
32
41
|
#
|
33
42
|
# The name of the openstack project on witch the vm will be created.
|
34
43
|
#
|
@@ -76,6 +85,12 @@ module VagrantPlugins
|
|
76
85
|
# @return [String]
|
77
86
|
attr_accessor :floating_ip_pool
|
78
87
|
|
88
|
+
# if set to true, vagrant will always allocate floating ip instead of trying to reuse unassigned ones
|
89
|
+
# default to false
|
90
|
+
#
|
91
|
+
# @return [Boolean]
|
92
|
+
attr_accessor :floating_ip_pool_always_allocate
|
93
|
+
|
79
94
|
# Sync folder method. Can be either "rsync" or "none"
|
80
95
|
#
|
81
96
|
# @return [String]
|
@@ -86,6 +101,11 @@ module VagrantPlugins
|
|
86
101
|
# @return [Array]
|
87
102
|
attr_accessor :networks
|
88
103
|
|
104
|
+
# Volumes list that will be attached to the VM
|
105
|
+
#
|
106
|
+
# @return [Array]
|
107
|
+
attr_accessor :volumes
|
108
|
+
|
89
109
|
# Public key path to create OpenStack keypair
|
90
110
|
#
|
91
111
|
# @return [Array]
|
@@ -96,13 +116,35 @@ module VagrantPlugins
|
|
96
116
|
# @return [String]
|
97
117
|
attr_accessor :availability_zone
|
98
118
|
|
119
|
+
# Pass hints to the OpenStack scheduler, e.g. { "cell": "some cell name" }
|
120
|
+
attr_accessor :scheduler_hints
|
121
|
+
|
122
|
+
# List of strings representing the security groups to apply.
|
123
|
+
# e.g. ['ssh', 'http']
|
124
|
+
#
|
125
|
+
# @return [Array[String]]
|
126
|
+
attr_accessor :security_groups
|
127
|
+
|
128
|
+
# User data to be sent to the newly created OpenStack instance. Use this
|
129
|
+
# e.g. to inject a script at boot time.
|
130
|
+
#
|
131
|
+
# @return [String]
|
132
|
+
attr_accessor :user_data
|
133
|
+
|
134
|
+
# A Hash of metadata that will be sent to the instance for configuration
|
135
|
+
#
|
136
|
+
# @return [Hash]
|
137
|
+
attr_accessor :metadata
|
138
|
+
|
99
139
|
def initialize
|
100
140
|
@password = UNSET_VALUE
|
101
141
|
@openstack_compute_url = UNSET_VALUE
|
102
142
|
@openstack_network_url = UNSET_VALUE
|
143
|
+
@openstack_volume_url = UNSET_VALUE
|
103
144
|
@openstack_auth_url = UNSET_VALUE
|
104
145
|
@flavor = UNSET_VALUE
|
105
146
|
@image = UNSET_VALUE
|
147
|
+
@volume_boot = UNSET_VALUE
|
106
148
|
@tenant_name = UNSET_VALUE
|
107
149
|
@server_name = UNSET_VALUE
|
108
150
|
@username = UNSET_VALUE
|
@@ -112,10 +154,16 @@ module VagrantPlugins
|
|
112
154
|
@ssh_timeout = UNSET_VALUE
|
113
155
|
@floating_ip = UNSET_VALUE
|
114
156
|
@floating_ip_pool = UNSET_VALUE
|
157
|
+
@floating_ip_pool_always_allocate = UNSET_VALUE
|
115
158
|
@sync_method = UNSET_VALUE
|
116
159
|
@availability_zone = UNSET_VALUE
|
117
160
|
@networks = []
|
161
|
+
@volumes = []
|
118
162
|
@public_key_path = UNSET_VALUE
|
163
|
+
@scheduler_hints = UNSET_VALUE
|
164
|
+
@security_groups = UNSET_VALUE
|
165
|
+
@user_data = UNSET_VALUE
|
166
|
+
@metadata = UNSET_VALUE
|
119
167
|
end
|
120
168
|
|
121
169
|
# rubocop:disable Style/CyclomaticComplexity
|
@@ -123,25 +171,33 @@ module VagrantPlugins
|
|
123
171
|
@password = nil if @password == UNSET_VALUE
|
124
172
|
@openstack_compute_url = nil if @openstack_compute_url == UNSET_VALUE
|
125
173
|
@openstack_network_url = nil if @openstack_network_url == UNSET_VALUE
|
174
|
+
@openstack_volume_url = nil if @openstack_volume_url == UNSET_VALUE
|
126
175
|
@openstack_auth_url = nil if @openstack_auth_url == UNSET_VALUE
|
127
176
|
@flavor = nil if @flavor == UNSET_VALUE
|
128
177
|
@image = nil if @image == UNSET_VALUE
|
178
|
+
@volume_boot = nil if @volume_boot == UNSET_VALUE
|
129
179
|
@tenant_name = nil if @tenant_name == UNSET_VALUE
|
130
180
|
@server_name = nil if @server_name == UNSET_VALUE
|
131
181
|
@username = nil if @username == UNSET_VALUE
|
132
182
|
@rsync_includes = nil if @rsync_includes.empty?
|
133
183
|
@floating_ip = nil if @floating_ip == UNSET_VALUE
|
134
184
|
@floating_ip_pool = nil if @floating_ip_pool == UNSET_VALUE
|
185
|
+
@floating_ip_pool_always_allocate = false if floating_ip_pool_always_allocate == UNSET_VALUE
|
135
186
|
@sync_method = 'rsync' if @sync_method == UNSET_VALUE
|
136
187
|
@keypair_name = nil if @keypair_name == UNSET_VALUE
|
137
188
|
@public_key_path = nil if @public_key_path == UNSET_VALUE
|
138
189
|
@availability_zone = nil if @availability_zone == UNSET_VALUE
|
190
|
+
@scheduler_hints = nil if @scheduler_hints == UNSET_VALUE
|
191
|
+
@security_groups = nil if @security_groups == UNSET_VALUE
|
192
|
+
@user_data = nil if @user_data == UNSET_VALUE
|
193
|
+
@metadata = nil if @metadata == UNSET_VALUE
|
139
194
|
|
140
195
|
# The SSH values by default are nil, and the top-level config
|
141
196
|
# `config.ssh` values are used.
|
142
197
|
@ssh_username = nil if @ssh_username == UNSET_VALUE
|
143
198
|
@ssh_timeout = 180 if @ssh_timeout == UNSET_VALUE
|
144
199
|
@networks = nil if @networks.empty?
|
200
|
+
@volumes = nil if @volumes.empty?
|
145
201
|
end
|
146
202
|
# rubocop:enable Style/CyclomaticComplexity
|
147
203
|
|
@@ -155,8 +211,9 @@ module VagrantPlugins
|
|
155
211
|
errors << I18n.t('vagrant_openstack.config.password_required') unless @password
|
156
212
|
errors << I18n.t('vagrant_openstack.config.username_required') unless @username
|
157
213
|
|
214
|
+
validate_ssh_username(machine, errors)
|
215
|
+
|
158
216
|
if machine.config.ssh.private_key_path
|
159
|
-
# Waiting for https://github.com/mitchellh/vagrant/issues/4388 to improve this
|
160
217
|
puts I18n.t('vagrant_openstack.config.keypair_name_required').yellow unless @keypair_name || @public_key_path
|
161
218
|
else
|
162
219
|
errors << I18n.t('vagrant_openstack.config.private_key_missing') if @keypair_name || @public_key_path
|
@@ -165,6 +222,7 @@ module VagrantPlugins
|
|
165
222
|
{
|
166
223
|
openstack_compute_url: @openstack_compute_url,
|
167
224
|
openstack_network_url: @openstack_network_url,
|
225
|
+
openstack_volume_url: @openstack_volume_url,
|
168
226
|
openstack_auth_url: @openstack_auth_url
|
169
227
|
}.each_pair do |key, value|
|
170
228
|
errors << I18n.t('vagrant_openstack.config.invalid_uri', key: key, uri: value) unless value.nil? || valid_uri?(value)
|
@@ -173,6 +231,11 @@ module VagrantPlugins
|
|
173
231
|
{ 'Openstack Provider' => errors }
|
174
232
|
end
|
175
233
|
|
234
|
+
def validate_ssh_username(machine, errors)
|
235
|
+
puts I18n.t('vagrant_openstack.config.ssh_username_deprecated').yellow if @ssh_username
|
236
|
+
errors << I18n.t('vagrant_openstack.config.ssh_username_required') unless @ssh_username || machine.config.ssh.username
|
237
|
+
end
|
238
|
+
|
176
239
|
private
|
177
240
|
|
178
241
|
def valid_uri?(value)
|
@@ -63,6 +63,42 @@ module VagrantPlugins
|
|
63
63
|
class UnableToResolveSSHKey < VagrantOpenstackError
|
64
64
|
error_key(:unable_to_resolve_ssh_key)
|
65
65
|
end
|
66
|
+
|
67
|
+
class InvalidVolumeObject < VagrantOpenstackError
|
68
|
+
error_key(:invalid_volume_format)
|
69
|
+
end
|
70
|
+
|
71
|
+
class UnresolvedVolume < VagrantOpenstackError
|
72
|
+
error_key(:unresolved_volume)
|
73
|
+
end
|
74
|
+
|
75
|
+
class UnresolvedVolumeId < VagrantOpenstackError
|
76
|
+
error_key(:unresolved_volume_id)
|
77
|
+
end
|
78
|
+
|
79
|
+
class UnresolvedVolumeName < VagrantOpenstackError
|
80
|
+
error_key(:unresolved_volume_name)
|
81
|
+
end
|
82
|
+
|
83
|
+
class ConflictVolumeNameId < VagrantOpenstackError
|
84
|
+
error_key(:conflict_volume_name_id)
|
85
|
+
end
|
86
|
+
|
87
|
+
class MultipleVolumeName < VagrantOpenstackError
|
88
|
+
error_key(:multiple_volume_name)
|
89
|
+
end
|
90
|
+
|
91
|
+
class MissingBootOption < VagrantOpenstackError
|
92
|
+
error_key(:missing_boot_option)
|
93
|
+
end
|
94
|
+
|
95
|
+
class ConflictBootOption < VagrantOpenstackError
|
96
|
+
error_key(:conflict_boot_option)
|
97
|
+
end
|
98
|
+
|
99
|
+
class NoMatchingSshUsername < VagrantOpenstackError
|
100
|
+
error_key(:ssh_username_missing)
|
101
|
+
end
|
66
102
|
end
|
67
103
|
end
|
68
104
|
end
|
data/locales/en.yml
CHANGED
@@ -12,6 +12,8 @@ en:
|
|
12
12
|
Finding image for server...
|
13
13
|
finding_networks: |-
|
14
14
|
Finding network(s) for server...
|
15
|
+
finding_volumes: |-
|
16
|
+
Finding volume(s) to attach on server...
|
15
17
|
launching_server: |-
|
16
18
|
Launching a server with the following settings...
|
17
19
|
not_created: |-
|
@@ -60,6 +62,12 @@ en:
|
|
60
62
|
private_key_missing: |-
|
61
63
|
config.ssh.private_key_path is required when either keypair_name or
|
62
64
|
public_key_path is set in Vagrantfile
|
65
|
+
ssh_username_deprecated: |-
|
66
|
+
ssh_username provider config is deprecated for vagrant-openstack provider.
|
67
|
+
If you are using it, it will continue to work but we recommend to switch to the
|
68
|
+
standard vagrant configuration option `config.ssh.username` instead
|
69
|
+
ssh_username_required: |-
|
70
|
+
vagrant standard configuration option `ssh.username` is required
|
63
71
|
|
64
72
|
errors:
|
65
73
|
default: |-
|
@@ -106,6 +114,24 @@ en:
|
|
106
114
|
Vagrant was unable to resolve a valid ip to ssh on your OpenStack instance.
|
107
115
|
unable_to_resolve_ssh_key: |-
|
108
116
|
Vagrant was unable to resolve a valid ssh key to connect to your OpenStack instance. Please specify in your Vagrantfile either `public_key_path` or `keypair_name`.
|
117
|
+
invalid_volume_format: |-
|
118
|
+
Volume '%{volume}' is not valid.
|
119
|
+
unresolved_volume: |-
|
120
|
+
No matching volume with id or name '%{volume}'
|
121
|
+
unresolved_volume_id: |-
|
122
|
+
No matching volume with id '%{id}'
|
123
|
+
unresolved_volume_name: |-
|
124
|
+
No matching volume with name '%{name}'
|
125
|
+
conflict_volume_name_id: |-
|
126
|
+
One (and only one) of 'id' or 'name' must be specified in volume definition : %{volume}
|
127
|
+
multiple_volume_name: |-
|
128
|
+
More than one volume exists with name '%{name}'. In the case you can't use name in volume definition. Please, use id instead.
|
129
|
+
missing_boot_option: |-
|
130
|
+
Either 'image' or 'volume_boot' configuration must be provided
|
131
|
+
conflict_boot_option: |-
|
132
|
+
Only one of 'image' and 'volume_boot' configuration must be provided
|
133
|
+
ssh_username_missing: |-
|
134
|
+
Vagrant was unable to resolve which ssh username to use to connect to the machine. Please provide config parameter `ssh.username`
|
109
135
|
|
110
136
|
states:
|
111
137
|
short_active: |-
|
@@ -205,3 +231,5 @@ en:
|
|
205
231
|
List private networks in project
|
206
232
|
flaotingip_list_synopsis : |-
|
207
233
|
List floating IP and floating IP pools
|
234
|
+
volume_list_synopsis : |-
|
235
|
+
List existing volumes
|
@@ -16,6 +16,7 @@ describe VagrantPlugins::Openstack::Action::ConnectOpenstack do
|
|
16
16
|
config.stub(:openstack_auth_url) { 'http://keystoneAuthV2' }
|
17
17
|
config.stub(:openstack_compute_url) { nil }
|
18
18
|
config.stub(:openstack_network_url) { nil }
|
19
|
+
config.stub(:openstack_volume_url) { nil }
|
19
20
|
config.stub(:tenant_name) { 'testTenant' }
|
20
21
|
config.stub(:username) { 'username' }
|
21
22
|
config.stub(:password) { 'password' }
|
@@ -20,9 +20,11 @@ describe VagrantPlugins::Openstack::Action::CreateServer do
|
|
20
20
|
config.stub(:availability_zone) { 'AZ-01' }
|
21
21
|
config.stub(:floating_ip) { nil }
|
22
22
|
config.stub(:floating_ip_pool) { nil }
|
23
|
+
config.stub(:floating_ip_pool_always_allocate) { false }
|
23
24
|
config.stub(:keypair_name) { nil }
|
24
25
|
config.stub(:public_key_path) { nil }
|
25
26
|
config.stub(:networks) { nil }
|
27
|
+
config.stub(:volumes) { nil }
|
26
28
|
end
|
27
29
|
end
|
28
30
|
|
@@ -63,6 +65,22 @@ describe VagrantPlugins::Openstack::Action::CreateServer do
|
|
63
65
|
end
|
64
66
|
end
|
65
67
|
|
68
|
+
let(:cinder) do
|
69
|
+
double('cinder').tap do |cinder|
|
70
|
+
cinder.stub(:get_all_volumes).with(anything) do
|
71
|
+
[Volume.new('001', 'vol-01', '1', 'available', 'true', nil, nil),
|
72
|
+
Volume.new('002', 'vol-02', '2', 'available', 'true', nil, nil),
|
73
|
+
Volume.new('003', 'vol-03', '3', 'available', 'true', nil, nil),
|
74
|
+
Volume.new('004', 'vol-04', '4', 'available', 'false', nil, nil),
|
75
|
+
Volume.new('005', 'vol-05', '5', 'available', 'false', nil, nil),
|
76
|
+
Volume.new('006', 'vol-06', '6', 'available', 'false', nil, nil),
|
77
|
+
Volume.new('007', 'vol-07-08', '6', 'available', 'false', nil, nil),
|
78
|
+
Volume.new('008', 'vol-07-08', '6', 'available', 'false', nil, nil)]
|
79
|
+
|
80
|
+
end
|
81
|
+
end
|
82
|
+
end
|
83
|
+
|
66
84
|
let(:env) do
|
67
85
|
Hash.new.tap do |env|
|
68
86
|
env[:ui] = double('ui')
|
@@ -73,6 +91,7 @@ describe VagrantPlugins::Openstack::Action::CreateServer do
|
|
73
91
|
env[:openstack_client] = double('openstack_client')
|
74
92
|
env[:openstack_client].stub(:neutron) { neutron }
|
75
93
|
env[:openstack_client].stub(:nova) { nova }
|
94
|
+
env[:openstack_client].stub(:cinder) { cinder }
|
76
95
|
end
|
77
96
|
end
|
78
97
|
|
@@ -81,6 +100,23 @@ describe VagrantPlugins::Openstack::Action::CreateServer do
|
|
81
100
|
@action = CreateServer.new(nil, nil)
|
82
101
|
end
|
83
102
|
|
103
|
+
describe 'call' do
|
104
|
+
context 'with both image and volume_boot specified' do
|
105
|
+
it 'should raise an error' do
|
106
|
+
config.stub(:image) { 'linux-image' }
|
107
|
+
config.stub(:volume_boot) { 'linux-volume' }
|
108
|
+
expect { @action.call(env) }.to raise_error Errors::ConflictBootOption
|
109
|
+
end
|
110
|
+
end
|
111
|
+
context 'with neither image nor volume_boot specified' do
|
112
|
+
it 'should raise an error' do
|
113
|
+
config.stub(:image) { nil }
|
114
|
+
config.stub(:volume_boot) { nil }
|
115
|
+
expect { @action.call(env) }.to raise_error Errors::MissingBootOption
|
116
|
+
end
|
117
|
+
end
|
118
|
+
end
|
119
|
+
|
84
120
|
describe 'create_server' do
|
85
121
|
context 'with all options specified' do
|
86
122
|
it 'calls nova with all the options' do
|
@@ -89,18 +125,29 @@ describe VagrantPlugins::Openstack::Action::CreateServer do
|
|
89
125
|
name: 'testName',
|
90
126
|
flavor_ref: flavor.id,
|
91
127
|
image_ref: image.id,
|
128
|
+
volume_boot: nil,
|
92
129
|
networks: ['test-networks'],
|
93
130
|
keypair: 'test-keypair',
|
94
|
-
availability_zone: 'test-az'
|
131
|
+
availability_zone: 'test-az',
|
132
|
+
scheduler_hints: 'test-sched-hints',
|
133
|
+
security_groups: ['test-sec-groups'],
|
134
|
+
user_data: 'test-user_data',
|
135
|
+
metadata: 'test-metadata') do '1234'
|
95
136
|
end
|
96
137
|
|
97
138
|
options = {
|
98
139
|
flavor: flavor,
|
99
140
|
image: image,
|
100
141
|
networks: ['test-networks'],
|
142
|
+
volumes: [{ id: '001', device: :auto }, { id: '002', device: '/dev/vdc' }],
|
101
143
|
keypair_name: 'test-keypair',
|
102
|
-
availability_zone: 'test-az'
|
144
|
+
availability_zone: 'test-az',
|
145
|
+
scheduler_hints: 'test-sched-hints',
|
146
|
+
security_groups: ['test-sec-groups'],
|
147
|
+
user_data: 'test-user_data',
|
148
|
+
metadata: 'test-metadata'
|
103
149
|
}
|
150
|
+
|
104
151
|
expect(@action.create_server(env, options)).to eq '1234'
|
105
152
|
end
|
106
153
|
end
|
@@ -116,13 +163,31 @@ describe VagrantPlugins::Openstack::Action::CreateServer do
|
|
116
163
|
|
117
164
|
context 'with config.floating_pool specified' do
|
118
165
|
context 'if any ip in the same pool is available' do
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
|
166
|
+
context 'with config.floating_pool_always_allocate true' do
|
167
|
+
it 'allocate a new floating_ip from the pool' do
|
168
|
+
config.stub(:floating_ip_pool_always_allocate) { true }
|
169
|
+
nova.stub(:get_all_floating_ips).with(anything) do
|
170
|
+
[FloatingIP.new('80.81.82.84', 'pool-1', '1234'),
|
171
|
+
FloatingIP.new('80.81.82.83', 'pool-1', nil)]
|
172
|
+
end
|
173
|
+
nova.stub(:allocate_floating_ip).with(env, 'pool-1') do
|
174
|
+
FloatingIP.new('80.81.82.84', 'pool-1', nil)
|
175
|
+
end
|
176
|
+
config.stub(:floating_ip_pool) { 'pool-1' }
|
177
|
+
@action.resolve_floating_ip(env).should eq('80.81.82.84')
|
178
|
+
end
|
179
|
+
end
|
180
|
+
|
181
|
+
context 'with config.floating_pool_always_allocate false' do
|
182
|
+
it 'return one of the available ips' do
|
183
|
+
config.stub(:floating_ip_pool_always_allocate) { false }
|
184
|
+
nova.stub(:get_all_floating_ips).with(anything) do
|
185
|
+
[FloatingIP.new('80.81.82.84', 'pool-1', '1234'),
|
186
|
+
FloatingIP.new('80.81.82.83', 'pool-1', nil)]
|
187
|
+
end
|
188
|
+
config.stub(:floating_ip_pool) { 'pool-1' }
|
189
|
+
@action.resolve_floating_ip(env).should eq('80.81.82.83')
|
123
190
|
end
|
124
|
-
config.stub(:floating_ip_pool) { 'pool-1' }
|
125
|
-
@action.resolve_floating_ip(env).should eq('80.81.82.83')
|
126
191
|
end
|
127
192
|
end
|
128
193
|
|
@@ -200,7 +265,6 @@ describe VagrantPlugins::Openstack::Action::CreateServer do
|
|
200
265
|
nova.stub(:import_keypair) { 'my-keypair-imported' }
|
201
266
|
SSHKey.stub(:generate) { ssh_key }
|
202
267
|
File.should_receive(:write).with('/data/dir/my-keypair-imported', 'private key')
|
203
|
-
File.should_receive(:chmod).with(0600, '/data/dir/my-keypair-imported')
|
204
268
|
@action.generate_keypair(env).should eq('my-keypair-imported')
|
205
269
|
end
|
206
270
|
end
|
@@ -252,4 +316,181 @@ describe VagrantPlugins::Openstack::Action::CreateServer do
|
|
252
316
|
end
|
253
317
|
|
254
318
|
end
|
319
|
+
|
320
|
+
describe 'resolve_volume_boot' do
|
321
|
+
context 'with string volume id' do
|
322
|
+
it 'returns normalized volume' do
|
323
|
+
config.stub(:volume_boot) { '001' }
|
324
|
+
expect(@action.resolve_volume_boot(env)).to eq id: '001', device: 'vda'
|
325
|
+
end
|
326
|
+
end
|
327
|
+
|
328
|
+
context 'with string volume name' do
|
329
|
+
it 'returns normalized volume' do
|
330
|
+
config.stub(:volume_boot) { 'vol-01' }
|
331
|
+
expect(@action.resolve_volume_boot(env)).to eq id: '001', device: 'vda'
|
332
|
+
end
|
333
|
+
end
|
334
|
+
|
335
|
+
context 'with hash volume id' do
|
336
|
+
it 'returns normalized volume' do
|
337
|
+
config.stub(:volume_boot) { { id: '001' } }
|
338
|
+
expect(@action.resolve_volume_boot(env)).to eq id: '001', device: 'vda'
|
339
|
+
end
|
340
|
+
end
|
341
|
+
|
342
|
+
context 'with hash volume name' do
|
343
|
+
it 'returns normalized volume' do
|
344
|
+
config.stub(:volume_boot) { { name: 'vol-01' } }
|
345
|
+
expect(@action.resolve_volume_boot(env)).to eq id: '001', device: 'vda'
|
346
|
+
end
|
347
|
+
end
|
348
|
+
|
349
|
+
context 'with hash volume id and device' do
|
350
|
+
it 'returns normalized volume' do
|
351
|
+
config.stub(:volume_boot) { { id: '001', device: 'vdb' } }
|
352
|
+
expect(@action.resolve_volume_boot(env)).to eq id: '001', device: 'vdb'
|
353
|
+
end
|
354
|
+
end
|
355
|
+
|
356
|
+
context 'with hash volume name and device' do
|
357
|
+
it 'returns normalized volume' do
|
358
|
+
config.stub(:volume_boot) { { name: 'vol-01', device: 'vdb' } }
|
359
|
+
expect(@action.resolve_volume_boot(env)).to eq id: '001', device: 'vdb'
|
360
|
+
end
|
361
|
+
end
|
362
|
+
|
363
|
+
context 'with empty hash' do
|
364
|
+
it 'raises an error' do
|
365
|
+
config.stub(:volume_boot) { {} }
|
366
|
+
expect { @action.resolve_volume_boot(env) }.to raise_error(Errors::ConflictVolumeNameId)
|
367
|
+
end
|
368
|
+
end
|
369
|
+
|
370
|
+
context 'with invalid volume object' do
|
371
|
+
it 'raises an error' do
|
372
|
+
config.stub(:volume_boot) { 1 }
|
373
|
+
expect { @action.resolve_volume_boot(env) }.to raise_error(Errors::InvalidVolumeObject)
|
374
|
+
end
|
375
|
+
end
|
376
|
+
|
377
|
+
context 'with hash containing a bad id' do
|
378
|
+
it 'raises an error' do
|
379
|
+
config.stub(:volume_boot) { { id: 'not-exist' } }
|
380
|
+
expect { @action.resolve_volume_boot(env) }.to raise_error(Errors::UnresolvedVolumeId)
|
381
|
+
end
|
382
|
+
end
|
383
|
+
|
384
|
+
context 'with hash containing a bad name' do
|
385
|
+
it 'raises an error' do
|
386
|
+
config.stub(:volume_boot) { { name: 'not-exist' } }
|
387
|
+
expect { @action.resolve_volume_boot(env) }.to raise_error(Errors::UnresolvedVolumeName)
|
388
|
+
end
|
389
|
+
end
|
390
|
+
|
391
|
+
context 'with hash containing both id and name' do
|
392
|
+
it 'raises an error' do
|
393
|
+
config.stub(:volume_boot) { { id: '001', name: 'vol-01' } }
|
394
|
+
expect { @action.resolve_volume_boot(env) }.to raise_error(Errors::ConflictVolumeNameId)
|
395
|
+
end
|
396
|
+
end
|
397
|
+
|
398
|
+
context 'with hash containing a name matching more than one volume' do
|
399
|
+
it 'raises an error' do
|
400
|
+
config.stub(:volume_boot) { { name: 'vol-07-08' } }
|
401
|
+
expect { @action.resolve_volume_boot(env) }.to raise_error(Errors::MultipleVolumeName)
|
402
|
+
end
|
403
|
+
end
|
404
|
+
end
|
405
|
+
|
406
|
+
describe 'resolve_volumes' do
|
407
|
+
context 'with volume attached in all possible ways' do
|
408
|
+
it 'returns normalized volume list' do
|
409
|
+
|
410
|
+
config.stub(:volumes) do
|
411
|
+
['001',
|
412
|
+
'vol-02',
|
413
|
+
{ id: '003', device: '/dev/vdz' },
|
414
|
+
{ name: 'vol-04', device: '/dev/vdy' },
|
415
|
+
{ name: 'vol-05' },
|
416
|
+
{ id: '006' }]
|
417
|
+
end
|
418
|
+
|
419
|
+
expect(@action.resolve_volumes(env)).to eq [{ id: '001', device: nil },
|
420
|
+
{ id: '002', device: nil },
|
421
|
+
{ id: '003', device: '/dev/vdz' },
|
422
|
+
{ id: '004', device: '/dev/vdy' },
|
423
|
+
{ id: '005', device: nil },
|
424
|
+
{ id: '006', device: nil }]
|
425
|
+
end
|
426
|
+
end
|
427
|
+
|
428
|
+
context 'with invalid volume object' do
|
429
|
+
it 'raises an error' do
|
430
|
+
config.stub(:volumes) { [1] }
|
431
|
+
expect { @action.resolve_volumes(env) }.to raise_error(Errors::InvalidVolumeObject)
|
432
|
+
end
|
433
|
+
end
|
434
|
+
|
435
|
+
context 'with string that is neither an id nor name matching a volume' do
|
436
|
+
it 'raises an error' do
|
437
|
+
config.stub(:volumes) { ['not-exist'] }
|
438
|
+
expect { @action.resolve_volumes(env) }.to raise_error(Errors::UnresolvedVolume)
|
439
|
+
end
|
440
|
+
end
|
441
|
+
|
442
|
+
context 'with hash containing a bad id' do
|
443
|
+
it 'raises an error' do
|
444
|
+
config.stub(:volumes) { [{ id: 'not-exist' }] }
|
445
|
+
expect { @action.resolve_volumes(env) }.to raise_error(Errors::UnresolvedVolumeId)
|
446
|
+
end
|
447
|
+
end
|
448
|
+
|
449
|
+
context 'with hash containing a bad name' do
|
450
|
+
it 'raises an error' do
|
451
|
+
config.stub(:volumes) { [{ name: 'not-exist' }] }
|
452
|
+
expect { @action.resolve_volumes(env) }.to raise_error(Errors::UnresolvedVolumeName)
|
453
|
+
end
|
454
|
+
end
|
455
|
+
|
456
|
+
context 'with empty hash' do
|
457
|
+
it 'raises an error' do
|
458
|
+
config.stub(:volumes) { [{}] }
|
459
|
+
expect { @action.resolve_volumes(env) }.to raise_error(Errors::ConflictVolumeNameId)
|
460
|
+
end
|
461
|
+
end
|
462
|
+
|
463
|
+
context 'with hash containing both id and name' do
|
464
|
+
it 'raises an error' do
|
465
|
+
config.stub(:volumes) { [{ id: '001', name: 'vol-01' }] }
|
466
|
+
expect { @action.resolve_volumes(env) }.to raise_error(Errors::ConflictVolumeNameId)
|
467
|
+
end
|
468
|
+
end
|
469
|
+
|
470
|
+
context 'with hash containing both id and name' do
|
471
|
+
it 'raises an error' do
|
472
|
+
config.stub(:volumes) { [{ id: '001', name: 'vol-01' }] }
|
473
|
+
expect { @action.resolve_volumes(env) }.to raise_error(Errors::ConflictVolumeNameId)
|
474
|
+
end
|
475
|
+
end
|
476
|
+
|
477
|
+
context 'with hash containing a name matching more than one volume' do
|
478
|
+
it 'raises an error' do
|
479
|
+
config.stub(:volumes) { [{ name: 'vol-07-08' }] }
|
480
|
+
expect { @action.resolve_volumes(env) }.to raise_error(Errors::MultipleVolumeName)
|
481
|
+
end
|
482
|
+
end
|
483
|
+
end
|
484
|
+
|
485
|
+
describe 'attach_volumes' do
|
486
|
+
context 'with volume attached in all possible ways' do
|
487
|
+
it 'returns normalized volume list' do
|
488
|
+
nova.stub(:attach_volume).with(anything, anything, anything, anything) {}
|
489
|
+
nova.should_receive(:attach_volume).with(env, 'server-01', '001', nil)
|
490
|
+
nova.should_receive(:attach_volume).with(env, 'server-01', '002', '/dev/vdb')
|
491
|
+
|
492
|
+
@action.attach_volumes(env, 'server-01', [{ id: '001', device: nil }, { id: '002', device: '/dev/vdb' }])
|
493
|
+
end
|
494
|
+
end
|
495
|
+
end
|
255
496
|
end
|