beaker-openstack 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +15 -0
- data/.gitignore +26 -0
- data/.simplecov +9 -0
- data/Gemfile +25 -0
- data/LICENSE +202 -0
- data/README.md +69 -0
- data/Rakefile +166 -0
- data/beaker-openstack.gemspec +38 -0
- data/bin/beaker-openstack +32 -0
- data/lib/beaker-openstack/version.rb +3 -0
- data/lib/beaker/hypervisor/openstack.rb +363 -0
- data/openstack.md +166 -0
- data/spec/beaker/hypervisor/openstack_spec.rb +238 -0
- data/spec/spec_helper.rb +17 -0
- metadata +212 -0
@@ -0,0 +1,38 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
$LOAD_PATH.unshift File.expand_path("../lib", __FILE__)
|
3
|
+
require 'beaker-openstack/version'
|
4
|
+
|
5
|
+
Gem::Specification.new do |s|
|
6
|
+
s.name = "beaker-openstack"
|
7
|
+
s.version = BeakerOpenstack::VERSION
|
8
|
+
s.authors = ["Rishi Javia, Kevin Imber, Tony Vu"]
|
9
|
+
s.email = ["rishi.javia@puppet.com, kevin.imber@puppet.com, tony.vu@puppet.com"]
|
10
|
+
s.homepage = "https://github.com/puppetlabs/beaker-openstack"
|
11
|
+
s.summary = %q{Beaker DSL Extension Helpers!}
|
12
|
+
s.description = %q{For use for the Beaker acceptance testing tool}
|
13
|
+
s.license = 'Apache2'
|
14
|
+
|
15
|
+
s.files = `git ls-files`.split("\n")
|
16
|
+
s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
|
17
|
+
s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
|
18
|
+
s.require_paths = ["lib"]
|
19
|
+
|
20
|
+
# Testing dependencies
|
21
|
+
s.add_development_dependency 'rspec', '~> 3.0'
|
22
|
+
s.add_development_dependency 'rspec-its'
|
23
|
+
s.add_development_dependency 'fakefs', '~> 0.6'
|
24
|
+
s.add_development_dependency 'rake', '~> 10.1'
|
25
|
+
s.add_development_dependency 'simplecov'
|
26
|
+
s.add_development_dependency 'pry', '~> 0.10'
|
27
|
+
|
28
|
+
# Documentation dependencies
|
29
|
+
s.add_development_dependency 'yard'
|
30
|
+
s.add_development_dependency 'markdown'
|
31
|
+
s.add_development_dependency 'thin'
|
32
|
+
|
33
|
+
# Run time dependencies
|
34
|
+
s.add_runtime_dependency 'stringify-hash', '~> 0.0.0'
|
35
|
+
s.add_runtime_dependency 'fog', '~> 1.38'
|
36
|
+
|
37
|
+
end
|
38
|
+
|
@@ -0,0 +1,32 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
require 'rubygems' unless defined?(Gem)
|
4
|
+
require 'beaker-openstack'
|
5
|
+
|
6
|
+
VERSION_STRING =
|
7
|
+
"
|
8
|
+
_ .--.
|
9
|
+
( ` )
|
10
|
+
beaker-openstack .-' `--,
|
11
|
+
_..----.. ( )`-.
|
12
|
+
.'_|` _|` _|( .__, )
|
13
|
+
/_| _| _| _( (_, .-'
|
14
|
+
;| _| _| _| '-'__,--'`--'
|
15
|
+
| _| _| _| _| |
|
16
|
+
_ || _| _| _| _| %s
|
17
|
+
_( `--.\\_| _| _| _|/
|
18
|
+
.-' )--,| _| _|.`
|
19
|
+
(__, (_ ) )_| _| /
|
20
|
+
`-.__.\\ _,--'\\|__|__/
|
21
|
+
;____;
|
22
|
+
\\YT/
|
23
|
+
||
|
24
|
+
|\"\"|
|
25
|
+
'=='
|
26
|
+
"
|
27
|
+
|
28
|
+
|
29
|
+
|
30
|
+
puts BeakerOpenstack::VERSION
|
31
|
+
|
32
|
+
exit 0
|
@@ -0,0 +1,363 @@
|
|
1
|
+
module Beaker
|
2
|
+
#Beaker support for OpenStack
|
3
|
+
#This code is EXPERIMENTAL!
|
4
|
+
#Please file any issues/concerns at https://github.com/puppetlabs/beaker/issues
|
5
|
+
class Openstack < Beaker::Hypervisor
|
6
|
+
|
7
|
+
SLEEPWAIT = 5
|
8
|
+
|
9
|
+
#Create a new instance of the OpenStack hypervisor object
|
10
|
+
#@param [<Host>] openstack_hosts The array of OpenStack hosts to provision
|
11
|
+
#@param [Hash{Symbol=>String}] options The options hash containing configuration values
|
12
|
+
#@option options [String] :openstack_api_key The key to access the OpenStack instance with (required)
|
13
|
+
#@option options [String] :openstack_username The username to access the OpenStack instance with (required)
|
14
|
+
#@option options [String] :openstack_auth_url The URL to access the OpenStack instance with (required)
|
15
|
+
#@option options [String] :openstack_tenant The tenant to access the OpenStack instance with (required)
|
16
|
+
#@option options [String] :openstack_region The region that each OpenStack instance should be provisioned on (optional)
|
17
|
+
#@option options [String] :openstack_network The network that each OpenStack instance should be contacted through (required)
|
18
|
+
#@option options [String] :openstack_keyname The name of an existing key pair that should be auto-loaded onto each
|
19
|
+
#@option options [Hash] :security_group An array of security groups to associate with the instance
|
20
|
+
# OpenStack instance (optional)
|
21
|
+
#@option options [String] :jenkins_build_url Added as metadata to each OpenStack instance
|
22
|
+
#@option options [String] :department Added as metadata to each OpenStack instance
|
23
|
+
#@option options [String] :project Added as metadata to each OpenStack instance
|
24
|
+
#@option options [Integer] :timeout The amount of time to attempt execution before quiting and exiting with failure
|
25
|
+
def initialize(openstack_hosts, options)
|
26
|
+
require 'fog'
|
27
|
+
@options = options
|
28
|
+
@logger = options[:logger]
|
29
|
+
@hosts = openstack_hosts
|
30
|
+
@vms = []
|
31
|
+
|
32
|
+
raise 'You must specify an Openstack API key (:openstack_api_key) for OpenStack instances!' unless @options[:openstack_api_key]
|
33
|
+
raise 'You must specify an Openstack username (:openstack_username) for OpenStack instances!' unless @options[:openstack_username]
|
34
|
+
raise 'You must specify an Openstack auth URL (:openstack_auth_url) for OpenStack instances!' unless @options[:openstack_auth_url]
|
35
|
+
raise 'You must specify an Openstack tenant (:openstack_tenant) for OpenStack instances!' unless @options[:openstack_tenant]
|
36
|
+
raise 'You must specify an Openstack network (:openstack_network) for OpenStack instances!' unless @options[:openstack_network]
|
37
|
+
|
38
|
+
# Common keystone authentication credentials
|
39
|
+
@credentials = {
|
40
|
+
:provider => :openstack,
|
41
|
+
:openstack_auth_url => @options[:openstack_auth_url],
|
42
|
+
:openstack_api_key => @options[:openstack_api_key],
|
43
|
+
:openstack_username => @options[:openstack_username],
|
44
|
+
:openstack_tenant => @options[:openstack_tenant],
|
45
|
+
:openstack_region => @options[:openstack_region],
|
46
|
+
}
|
47
|
+
|
48
|
+
# Keystone version 3 requires users and projects to be scoped
|
49
|
+
if @credentials[:openstack_auth_url].include?('/v3/')
|
50
|
+
@credentials[:openstack_user_domain] = @options[:openstack_user_domain] || 'Default'
|
51
|
+
@credentials[:openstack_project_domain] = @options[:openstack_project_domain] || 'Default'
|
52
|
+
end
|
53
|
+
|
54
|
+
@compute_client ||= Fog::Compute.new(@credentials)
|
55
|
+
|
56
|
+
if not @compute_client
|
57
|
+
raise "Unable to create OpenStack Compute instance (api key: #{@options[:openstack_api_key]}, username: #{@options[:openstack_username]}, auth_url: #{@options[:openstack_auth_url]}, tenant: #{@options[:openstack_tenant]})"
|
58
|
+
end
|
59
|
+
|
60
|
+
@network_client ||= Fog::Network.new(@credentials)
|
61
|
+
|
62
|
+
if not @network_client
|
63
|
+
raise "Unable to create OpenStack Network instance (api_key: #{@options[:openstack_api_key]}, username: #{@options[:openstack_username]}, auth_url: #{@options[:openstack_auth_url]}, tenant: #{@options[:openstack_tenant]})"
|
64
|
+
end
|
65
|
+
|
66
|
+
# Validate openstack_volume_support setting value, reset to boolean if passed via ENV value string
|
67
|
+
@options[:openstack_volume_support] = true if @options[:openstack_volume_support].to_s.match(/\btrue\b/i)
|
68
|
+
@options[:openstack_volume_support] = false if @options[:openstack_volume_support].to_s.match(/\bfalse\b/i)
|
69
|
+
[true,false].include? @options[:openstack_volume_support] or raise "Invalid openstack_volume_support setting, current: @options[:openstack_volume_support]"
|
70
|
+
|
71
|
+
end
|
72
|
+
|
73
|
+
#Provided a flavor name return the OpenStack id for that flavor
|
74
|
+
#@param [String] f The flavor name
|
75
|
+
#@return [String] Openstack id for provided flavor name
|
76
|
+
def flavor f
|
77
|
+
@logger.debug "OpenStack: Looking up flavor '#{f}'"
|
78
|
+
@compute_client.flavors.find { |x| x.name == f } || raise("Couldn't find flavor: #{f}")
|
79
|
+
end
|
80
|
+
|
81
|
+
#Provided an image name return the OpenStack id for that image
|
82
|
+
#@param [String] i The image name
|
83
|
+
#@return [String] Openstack id for provided image name
|
84
|
+
def image i
|
85
|
+
@logger.debug "OpenStack: Looking up image '#{i}'"
|
86
|
+
@compute_client.images.find { |x| x.name == i } || raise("Couldn't find image: #{i}")
|
87
|
+
end
|
88
|
+
|
89
|
+
#Provided a network name return the OpenStack id for that network
|
90
|
+
#@param [String] n The network name
|
91
|
+
#@return [String] Openstack id for provided network name
|
92
|
+
def network n
|
93
|
+
@logger.debug "OpenStack: Looking up network '#{n}'"
|
94
|
+
@network_client.networks.find { |x| x.name == n } || raise("Couldn't find network: #{n}")
|
95
|
+
end
|
96
|
+
|
97
|
+
#Provided an array of security groups return that array if all
|
98
|
+
#security groups are present
|
99
|
+
#@param [Array] sgs The array of security group names
|
100
|
+
#@return [Array] The array of security group names
|
101
|
+
def security_groups sgs
|
102
|
+
for sg in sgs
|
103
|
+
@logger.debug "Openstack: Looking up security group '#{sg}'"
|
104
|
+
@compute_client.security_groups.find { |x| x.name == sg } || raise("Couldn't find security group: #{sg}")
|
105
|
+
sgs
|
106
|
+
end
|
107
|
+
end
|
108
|
+
|
109
|
+
# Create a volume client on request
|
110
|
+
# @return [Fog::OpenStack::Volume] OpenStack volume client
|
111
|
+
def volume_client_create
|
112
|
+
@volume_client ||= Fog::Volume.new(@credentials)
|
113
|
+
unless @volume_client
|
114
|
+
raise "Unable to create OpenStack Volume instance"\
|
115
|
+
" (api_key: #{@options[:openstack_api_key]},"\
|
116
|
+
" username: #{@options[:openstack_username]},"\
|
117
|
+
" auth_url: #{@options[:openstack_auth_url]},"\
|
118
|
+
" tenant: #{@options[:openstack_tenant]})"
|
119
|
+
end
|
120
|
+
end
|
121
|
+
|
122
|
+
# Get a hash of volumes from the host
|
123
|
+
def get_volumes host
|
124
|
+
return host['volumes'] if host['volumes']
|
125
|
+
{}
|
126
|
+
end
|
127
|
+
|
128
|
+
# Get the API version
|
129
|
+
def get_volume_api_version
|
130
|
+
case @volume_client
|
131
|
+
when Fog::Volume::OpenStack::V1
|
132
|
+
1
|
133
|
+
else
|
134
|
+
-1
|
135
|
+
end
|
136
|
+
end
|
137
|
+
|
138
|
+
# Create and attach dynamic volumes
|
139
|
+
#
|
140
|
+
# Creates an array of volumes and attaches them to the current host.
|
141
|
+
# The host bus type is determined by the image type, so by default
|
142
|
+
# devices appear as /dev/vdb, /dev/vdc etc. Setting the glance
|
143
|
+
# properties hw_disk_bus=scsi, hw_scsi_model=virtio-scsi will present
|
144
|
+
# them as /dev/sdb, /dev/sdc (or 2:0:0:1, 2:0:0:2 in SCSI addresses)
|
145
|
+
#
|
146
|
+
# @param host [Hash] thet current host defined in the nodeset
|
147
|
+
# @param vm [Fog::Compute::OpenStack::Server] the server to attach to
|
148
|
+
def provision_storage host, vm
|
149
|
+
volumes = get_volumes(host)
|
150
|
+
if !volumes.empty?
|
151
|
+
# Lazily create the volume client if needed
|
152
|
+
volume_client_create
|
153
|
+
volumes.keys.each_with_index do |volume, index|
|
154
|
+
@logger.debug "Creating volume #{volume} for OpenStack host #{host.name}"
|
155
|
+
|
156
|
+
# The node defintion file defines volume sizes in MB (due to precedent
|
157
|
+
# with the vagrant virtualbox implementation) however OpenStack requires
|
158
|
+
# this translating into GB
|
159
|
+
openstack_size = volumes[volume]['size'].to_i / 1000
|
160
|
+
|
161
|
+
# Set up the volume creation arguments
|
162
|
+
args = {
|
163
|
+
:size => openstack_size,
|
164
|
+
:description => "Beaker volume: host=#{host.name} volume=#{volume}",
|
165
|
+
}
|
166
|
+
|
167
|
+
# Between version 1 and subsequent versions the API was updated to
|
168
|
+
# rename 'display_name' to just 'name' for better consistency
|
169
|
+
if get_volume_api_version == 1
|
170
|
+
args[:display_name] = volume
|
171
|
+
else
|
172
|
+
args[:name] = volume
|
173
|
+
end
|
174
|
+
|
175
|
+
# Create the volume and wait for it to become available
|
176
|
+
vol = @volume_client.volumes.create(**args)
|
177
|
+
vol.wait_for { ready? }
|
178
|
+
|
179
|
+
# Fog needs a device name to attach as, so invent one. The guest
|
180
|
+
# doesn't pay any attention to this
|
181
|
+
device = "/dev/vd#{('b'.ord + index).chr}"
|
182
|
+
vm.attach_volume(vol.id, device)
|
183
|
+
end
|
184
|
+
end
|
185
|
+
end
|
186
|
+
|
187
|
+
# Detach and delete guest volumes
|
188
|
+
# @param vm [Fog::Compute::OpenStack::Server] the server to detach from
|
189
|
+
def cleanup_storage vm
|
190
|
+
vm.volumes.each do |vol|
|
191
|
+
@logger.debug "Deleting volume #{vol.name} for OpenStack host #{vm.name}"
|
192
|
+
vm.detach_volume(vol.id)
|
193
|
+
vol.wait_for { ready? }
|
194
|
+
vol.destroy
|
195
|
+
end
|
196
|
+
end
|
197
|
+
|
198
|
+
# Get a floating IP address to associate with the instance, try
|
199
|
+
# to allocate a new one from the specified pool if none are available
|
200
|
+
def get_ip
|
201
|
+
begin
|
202
|
+
@logger.debug "Creating IP"
|
203
|
+
ip = @compute_client.addresses.create
|
204
|
+
rescue Fog::Compute::OpenStack::NotFound
|
205
|
+
# If there are no more floating IP addresses, allocate a
|
206
|
+
# new one and try again.
|
207
|
+
@compute_client.allocate_address(@options[:floating_ip_pool])
|
208
|
+
ip = @compute_client.addresses.find { |ip| ip.instance_id.nil? }
|
209
|
+
end
|
210
|
+
raise 'Could not find or allocate an address' if not ip
|
211
|
+
ip
|
212
|
+
end
|
213
|
+
|
214
|
+
#Create new instances in OpenStack
|
215
|
+
def provision
|
216
|
+
@logger.notify "Provisioning OpenStack"
|
217
|
+
|
218
|
+
@hosts.each do |host|
|
219
|
+
ip = get_ip
|
220
|
+
hostname = ip.ip.gsub('.','-')
|
221
|
+
host[:vmhostname] = hostname + '.rfc1918.puppetlabs.net'
|
222
|
+
create_or_associate_keypair(host, hostname)
|
223
|
+
@logger.debug "Provisioning #{host.name} (#{host[:vmhostname]})"
|
224
|
+
options = {
|
225
|
+
:flavor_ref => flavor(host[:flavor]).id,
|
226
|
+
:image_ref => image(host[:image]).id,
|
227
|
+
:nics => [ {'net_id' => network(@options[:openstack_network]).id } ],
|
228
|
+
:name => host[:vmhostname],
|
229
|
+
:hostname => host[:vmhostname],
|
230
|
+
:user_data => host[:user_data] || "#cloud-config\nmanage_etc_hosts: true\n",
|
231
|
+
:key_name => host[:keyname],
|
232
|
+
}
|
233
|
+
options[:security_groups] = security_groups(@options[:security_group]) unless @options[:security_group].nil?
|
234
|
+
vm = @compute_client.servers.create(options)
|
235
|
+
|
236
|
+
#wait for the new instance to start up
|
237
|
+
try = 1
|
238
|
+
attempts = @options[:timeout].to_i / SLEEPWAIT
|
239
|
+
|
240
|
+
while try <= attempts
|
241
|
+
begin
|
242
|
+
vm.wait_for(5) { ready? }
|
243
|
+
break
|
244
|
+
rescue Fog::Errors::TimeoutError => e
|
245
|
+
if try >= attempts
|
246
|
+
@logger.debug "Failed to connect to new OpenStack instance #{host.name} (#{host[:vmhostname]})"
|
247
|
+
raise e
|
248
|
+
end
|
249
|
+
@logger.debug "Timeout connecting to instance #{host.name} (#{host[:vmhostname]}), trying again..."
|
250
|
+
end
|
251
|
+
sleep SLEEPWAIT
|
252
|
+
try += 1
|
253
|
+
end
|
254
|
+
|
255
|
+
# Associate a public IP to the server
|
256
|
+
ip.server = vm
|
257
|
+
host[:ip] = ip.ip
|
258
|
+
|
259
|
+
@logger.debug "OpenStack host #{host.name} (#{host[:vmhostname]}) assigned ip: #{host[:ip]}"
|
260
|
+
|
261
|
+
#set metadata
|
262
|
+
vm.metadata.update({:jenkins_build_url => @options[:jenkins_build_url].to_s,
|
263
|
+
:department => @options[:department].to_s,
|
264
|
+
:project => @options[:project].to_s })
|
265
|
+
@vms << vm
|
266
|
+
|
267
|
+
# Wait for the host to accept ssh logins
|
268
|
+
host.wait_for_port(22)
|
269
|
+
|
270
|
+
#enable root if user is not root
|
271
|
+
enable_root(host)
|
272
|
+
|
273
|
+
provision_storage(host, vm) if @options[:openstack_volume_support]
|
274
|
+
@logger.notify "OpenStack Volume Support Disabled, can't provision volumes" if not @options[:openstack_volume_support]
|
275
|
+
end
|
276
|
+
|
277
|
+
hack_etc_hosts @hosts, @options
|
278
|
+
|
279
|
+
end
|
280
|
+
|
281
|
+
#Destroy any OpenStack instances
|
282
|
+
def cleanup
|
283
|
+
@logger.notify "Cleaning up OpenStack"
|
284
|
+
@vms.each do |vm|
|
285
|
+
cleanup_storage(vm) if @options[:openstack_volume_support]
|
286
|
+
@logger.debug "Release floating IPs for OpenStack host #{vm.name}"
|
287
|
+
floating_ips = vm.all_addresses # fetch and release its floating IPs
|
288
|
+
floating_ips.each do |address|
|
289
|
+
@compute_client.disassociate_address(vm.id, address['ip'])
|
290
|
+
@compute_client.release_address(address['id'])
|
291
|
+
end
|
292
|
+
@logger.debug "Destroying OpenStack host #{vm.name}"
|
293
|
+
vm.destroy
|
294
|
+
if @options[:openstack_keyname].nil?
|
295
|
+
@logger.debug "Deleting random keypair"
|
296
|
+
@compute_client.delete_key_pair vm.key_name
|
297
|
+
end
|
298
|
+
end
|
299
|
+
end
|
300
|
+
|
301
|
+
# Enables root access for a host when username is not root
|
302
|
+
# This method ripped from the aws_sdk implementation and is probably wrong
|
303
|
+
# because it iterates on a collection when there's no guarantee the collection
|
304
|
+
# has all been brought up in openstack yet and will thus explode
|
305
|
+
# @return [void]
|
306
|
+
# @api private
|
307
|
+
def enable_root_on_hosts
|
308
|
+
@hosts.each do |host|
|
309
|
+
enable_root(host)
|
310
|
+
end
|
311
|
+
end
|
312
|
+
|
313
|
+
# enable root on a single host (the current one presumably) but only
|
314
|
+
# if the username isn't 'root'
|
315
|
+
def enable_root(host)
|
316
|
+
if host['user'] != 'root'
|
317
|
+
copy_ssh_to_root(host, @options)
|
318
|
+
enable_root_login(host, @options)
|
319
|
+
host['user'] = 'root'
|
320
|
+
host.close
|
321
|
+
end
|
322
|
+
end
|
323
|
+
|
324
|
+
#Get key_name from options or generate a new rsa key and add it to
|
325
|
+
#OpenStack keypairs
|
326
|
+
#
|
327
|
+
#@param [Host] host The OpenStack host to provision
|
328
|
+
#@api private
|
329
|
+
def create_or_associate_keypair(host, keyname)
|
330
|
+
if @options[:openstack_keyname]
|
331
|
+
@logger.debug "Adding optional key_name #{@options[:openstack_keyname]} to #{host.name} (#{host[:vmhostname]})"
|
332
|
+
keyname = @options[:openstack_keyname]
|
333
|
+
else
|
334
|
+
@logger.debug "Generate a new rsa key"
|
335
|
+
|
336
|
+
# There is apparently an error that can occur when generating RSA keys, probably
|
337
|
+
# due to some timing issue, probably similar to the issue described here:
|
338
|
+
# https://github.com/negativecode/vines/issues/34
|
339
|
+
# In order to mitigate this error, we will simply try again up to three times, and
|
340
|
+
# then fail if we continue to error out.
|
341
|
+
begin
|
342
|
+
retries ||= 0
|
343
|
+
key = OpenSSL::PKey::RSA.new 2048
|
344
|
+
rescue OpenSSL::PKey::RSAError => e
|
345
|
+
retries += 1
|
346
|
+
if retries > 2
|
347
|
+
@logger.notify "error generating RSA key #{retries} times, exiting"
|
348
|
+
raise e
|
349
|
+
end
|
350
|
+
retry
|
351
|
+
end
|
352
|
+
|
353
|
+
type = key.ssh_type
|
354
|
+
data = [ key.to_blob ].pack('m0')
|
355
|
+
@logger.debug "Creating Openstack keypair '#{keyname}' for public key '#{type} #{data}'"
|
356
|
+
@compute_client.create_key_pair keyname, "#{type} #{data}"
|
357
|
+
host['ssh'][:key_data] = [ key.to_pem ]
|
358
|
+
end
|
359
|
+
|
360
|
+
host[:keyname] = keyname
|
361
|
+
end
|
362
|
+
end
|
363
|
+
end
|
data/openstack.md
ADDED
@@ -0,0 +1,166 @@
|
|
1
|
+
# Openstack
|
2
|
+
|
3
|
+
OpenStack is a free and open-source software platform for cloud computing. [Their Site](http://www.openstack.org/).
|
4
|
+
|
5
|
+
Considered **EXPERIMENTAL**, may break without notice.
|
6
|
+
|
7
|
+
# Getting Started
|
8
|
+
|
9
|
+
### Requirements
|
10
|
+
|
11
|
+
Get openstack Access & Security credentials:
|
12
|
+
|
13
|
+
- "openstack_api_key"
|
14
|
+
- "openstack_auth_url"
|
15
|
+
- "openstack_username"
|
16
|
+
- "openstack_tenant"
|
17
|
+
- "openstack_network"
|
18
|
+
- "openstack_keyname"
|
19
|
+
|
20
|
+
If you are using [OpenStack Dashboard "Horizon"](https://wiki.openstack.org/wiki/Horizon)
|
21
|
+
you can find these keys in next places:
|
22
|
+
|
23
|
+
1. login to "Horizon dashboard" -> "project" -> "Compute" -> "Access & Security" -> tab "API Access" -> "Download OpenStack RC File":
|
24
|
+
* "openstack_auth_url" == OS_AUTH_URL + "/tokens"
|
25
|
+
* "openstack_username" == OS_USERNAME
|
26
|
+
* "openstack_tenant" == OS_TENANT_NAME
|
27
|
+
2. "openstack_network": in "project" -> "Networks"
|
28
|
+
3. "openstack_keyname": in "project" -> "Compute" -> "Access & Security" -> tab "Key Pairs"
|
29
|
+
4. "openstack_api_key": Your user Password
|
30
|
+
|
31
|
+
### Setup a Openstack Hosts File
|
32
|
+
|
33
|
+
An Openstack hosts file looks like a typical hosts file,
|
34
|
+
except that there are a number of required properties that need to be added to every host
|
35
|
+
in order for the Openstack hypervisor to provision hosts properly.
|
36
|
+
|
37
|
+
**Basic Openstack hosts file**
|
38
|
+
|
39
|
+
HOSTS:
|
40
|
+
centos-6-master:
|
41
|
+
roles:
|
42
|
+
- master
|
43
|
+
- agent
|
44
|
+
- database
|
45
|
+
- dashboard
|
46
|
+
platform: el-6-x86_64
|
47
|
+
hypervisor: openstack
|
48
|
+
image: centos-6-x86_64-nocm
|
49
|
+
flavor: m1.large
|
50
|
+
|
51
|
+
CONFIG:
|
52
|
+
nfs_server: none
|
53
|
+
consoleport: 443
|
54
|
+
openstack_api_key: Pas$w0rd
|
55
|
+
openstack_username: user
|
56
|
+
openstack_auth_url: http://10.10.10.10:5000/v2.0/tokens
|
57
|
+
openstack_tenant: testing
|
58
|
+
openstack_network : testing
|
59
|
+
openstack_keyname : nopass
|
60
|
+
|
61
|
+
The `image` - image name.
|
62
|
+
|
63
|
+
The `flavor` - templates for VMs, defining sizes for RAM, disk, number of cores, and so on.
|
64
|
+
|
65
|
+
|
66
|
+
# Openstack-Specific Hosts File Settings
|
67
|
+
|
68
|
+
### user-data
|
69
|
+
|
70
|
+
"user data" - a blob of data that the user can specify when they launch an instance.
|
71
|
+
The instance can access this data through the metadata service or config drive with one of the next requests:
|
72
|
+
|
73
|
+
- curl http://169.254.169.254/2009-04-04/user-data
|
74
|
+
- curl http://169.254.169.254/openstack/2012-08-10/user_data
|
75
|
+
|
76
|
+
|
77
|
+
Examples of `user_data` you can find here: http://cloudinit.readthedocs.io/en/latest/topics/examples.html
|
78
|
+
|
79
|
+
Also if you plan use `user-data` make sure that 'cloud-init' package installed in your VM `image` and 'cloud-init' service is running.
|
80
|
+
|
81
|
+
**Example Openstack hosts file with user_data**
|
82
|
+
|
83
|
+
HOSTS:
|
84
|
+
centos-6-master:
|
85
|
+
roles:
|
86
|
+
- master
|
87
|
+
- agent
|
88
|
+
- database
|
89
|
+
- dashboard
|
90
|
+
platform: el-6-x86_64
|
91
|
+
image: centos-6-x86_64-nocm
|
92
|
+
flavor: m1.large
|
93
|
+
hypervisor: openstack
|
94
|
+
user_data: |
|
95
|
+
#cloud-config
|
96
|
+
bootcmd:
|
97
|
+
- echo 123 > /tmp/test.txt
|
98
|
+
CONFIG:
|
99
|
+
nfs_server: none
|
100
|
+
consoleport: 443
|
101
|
+
openstack_api_key: P1as$w0rd
|
102
|
+
openstack_username: user
|
103
|
+
openstack_auth_url: http://10.10.10.10:5000/v2.0/tokens
|
104
|
+
openstack_tenant: testing
|
105
|
+
openstack_network : testing
|
106
|
+
openstack_keyname : nopass
|
107
|
+
|
108
|
+
### Security groups
|
109
|
+
|
110
|
+
A security group is a set of rules for incoming and outgoing traffic to
|
111
|
+
an instance. You can associate a host with one or many security groups
|
112
|
+
in the `CONFIG` section of your hosts file:
|
113
|
+
|
114
|
+
security_group: ['my_sg', 'default']
|
115
|
+
|
116
|
+
This is an optional config parameter.
|
117
|
+
|
118
|
+
### Floating IP Pool
|
119
|
+
|
120
|
+
The name of the floating IP pool that a VM can grab IPs from. This is useful
|
121
|
+
if your organization doesn't have a public pool of floating IPs, or give each
|
122
|
+
user their own pool. It's used in allocating new IPs. It's an options
|
123
|
+
parameter in the CONFIG section of the host file:
|
124
|
+
|
125
|
+
floating_ip_pool: 'my_pool_name'
|
126
|
+
|
127
|
+
### Volumes
|
128
|
+
|
129
|
+
Attaching volumes to a VM is supported via the Cinder service. All versions are transparently
|
130
|
+
supported to cater for differences in the APIs. To create and attach volumes simply add hash
|
131
|
+
called 'volumes' to a host in the HOSTS section. Each key is the name given to the volume upon
|
132
|
+
resource creation. The value is a hash with a single integer parameter 'size' which defines the
|
133
|
+
volume size in MB.
|
134
|
+
|
135
|
+
**Example OpenStack hosts file with volumes**
|
136
|
+
|
137
|
+
HOSTS:
|
138
|
+
ceph:
|
139
|
+
roles:
|
140
|
+
- master
|
141
|
+
platform: ubuntu-16.04-amd64
|
142
|
+
hypervisor: openstack
|
143
|
+
flavor: m1.large
|
144
|
+
image: xenial-server-cloudimg-amd64-scsi
|
145
|
+
user: ubuntu
|
146
|
+
volumes:
|
147
|
+
osd0:
|
148
|
+
size: 10000
|
149
|
+
osd1:
|
150
|
+
size: 10000
|
151
|
+
osd2:
|
152
|
+
size: 10000
|
153
|
+
journal:
|
154
|
+
size: 1000
|
155
|
+
|
156
|
+
|
157
|
+
In the event you're using an OpenStack instance that does not deploy the volume service you can disable that functionality to prevent beaker runs from failing. Either using an ENV variable or setting the following value in the `CONFIG` section of your hosts file(valid values are `true` or `false`):
|
158
|
+
|
159
|
+
```
|
160
|
+
openstack_volume_support: false
|
161
|
+
```
|
162
|
+
|
163
|
+
You can also configure this setting via an environment variable:
|
164
|
+
|
165
|
+
```
|
166
|
+
export OS_VOLUME_SUPPORT=false
|