Class: Beaker::OpenStack
- Inherits:
-
Hypervisor
- Object
- Hypervisor
- Beaker::OpenStack
- Defined in:
- lib/beaker/hypervisor/openstack.rb
Overview
Beaker support for OpenStack This code is EXPERIMENTAL! Please file any issues/concerns at github.com/puppetlabs/beaker/issues
Constant Summary collapse
- SLEEPWAIT =
5
Constants inherited from Hypervisor
Constants included from HostPrebuiltSteps
HostPrebuiltSteps::APT_CFG, HostPrebuiltSteps::CUMULUS_PACKAGES, HostPrebuiltSteps::DEBIAN_PACKAGES, HostPrebuiltSteps::ETC_HOSTS_PATH, HostPrebuiltSteps::ETC_HOSTS_PATH_SOLARIS, HostPrebuiltSteps::FREEBSD_PACKAGES, HostPrebuiltSteps::IPS_PKG_REPO, HostPrebuiltSteps::NTPSERVER, HostPrebuiltSteps::OPENBSD_PACKAGES, HostPrebuiltSteps::PSWINDOWS_PACKAGES, HostPrebuiltSteps::ROOT_KEYS_SCRIPT, HostPrebuiltSteps::ROOT_KEYS_SYNC_CMD, HostPrebuiltSteps::ROOT_KEYS_SYNC_CMD_AIX, HostPrebuiltSteps::SLES10_PACKAGES, HostPrebuiltSteps::SLES_PACKAGES, HostPrebuiltSteps::SOLARIS10_PACKAGES, HostPrebuiltSteps::SOLARIS11_PACKAGES, HostPrebuiltSteps::TRIES, HostPrebuiltSteps::UNIX_PACKAGES, HostPrebuiltSteps::WINDOWS_PACKAGES
Instance Method Summary collapse
-
#cleanup ⇒ Object
Destroy any OpenStack instances.
-
#cleanup_storage(vm) ⇒ Object
Detach and delete guest volumes.
-
#enable_root(host) ⇒ Object
enable root on a single host (the current one presumably) but only if the username isn’t ‘root’.
-
#enable_root_on_hosts ⇒ void
private
Enables root access for a host when username is not root This method ripped from the aws_sdk implementation and is probably wrong because it iterates on a collection when there’s no guarantee the collection has all been brought up in openstack yet and will thus explode.
-
#flavor(f) ⇒ String
Provided a flavor name return the OpenStack id for that flavor.
-
#get_ip ⇒ Object
Get a floating IP address to associate with the instance, and allocate a new one if none are available.
-
#image(i) ⇒ String
Provided an image name return the OpenStack id for that image.
-
#initialize(openstack_hosts, options) ⇒ OpenStack
constructor
Create a new instance of the OpenStack hypervisor object.
-
#key_name(host) ⇒ String
private
Get key_name from options or generate a new rsa key and add it to OpenStack keypairs.
-
#network(n) ⇒ String
Provided a network name return the OpenStack id for that network.
-
#provision ⇒ Object
Create new instances in OpenStack.
-
#provision_storage(host, vm) ⇒ Object
Create and attach dynamic volumes.
-
#security_groups(sgs) ⇒ Array
Provided an array of security groups return that array if all security groups are present.
-
#volume_client_create ⇒ Fog::OpenStack::Volume
Create a volume client on request.
Methods inherited from Hypervisor
#configure, create, #generate_host_name, #proxy_package_manager, #validate
Methods included from HostPrebuiltSteps
#add_el_extras, #additive_hash_merge, #apt_get_update, #check_and_install_packages_if_needed, #construct_env, #copy_file_to_remote, #copy_ssh_to_root, #disable_iptables, #disable_se_linux, #disable_updates, #enable_root_login, #get_domain_name, #hack_etc_hosts, #install_one_of_packages, #package_proxy, #proxy_config, #set_env, #set_etc_hosts, #sync_root_keys, #timesync, #validate_host
Methods included from DSL::Patterns
Constructor Details
#initialize(openstack_hosts, options) ⇒ OpenStack
Create a new instance of the OpenStack hypervisor object
25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 |
# File 'lib/beaker/hypervisor/openstack.rb', line 25 def initialize(openstack_hosts, ) require 'fog' @options = @logger = [:logger] @hosts = openstack_hosts @vms = [] raise 'You must specify an Openstack API key (:openstack_api_key) for OpenStack instances!' unless @options[:openstack_api_key] raise 'You must specify an Openstack username (:openstack_username) for OpenStack instances!' unless @options[:openstack_username] raise 'You must specify an Openstack auth URL (:openstack_auth_url) for OpenStack instances!' unless @options[:openstack_auth_url] raise 'You must specify an Openstack tenant (:openstack_tenant) for OpenStack instances!' unless @options[:openstack_tenant] raise 'You must specify an Openstack network (:openstack_network) for OpenStack instances!' unless @options[:openstack_network] optionhash = {} optionhash[:provider] = :openstack optionhash[:openstack_api_key] = @options[:openstack_api_key] optionhash[:openstack_username] = @options[:openstack_username] optionhash[:openstack_auth_url] = @options[:openstack_auth_url] optionhash[:openstack_tenant] = @options[:openstack_tenant] optionhash[:openstack_region] = @options[:openstack_region] if @options[:openstack_region] @compute_client ||= Fog::Compute.new(optionhash) if not @compute_client 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]})" end networkoptionhash = {} networkoptionhash[:provider] = :openstack networkoptionhash[:openstack_api_key] = @options[:openstack_api_key] networkoptionhash[:openstack_username] = @options[:openstack_username] networkoptionhash[:openstack_auth_url] = @options[:openstack_auth_url] networkoptionhash[:openstack_tenant] = @options[:openstack_tenant] networkoptionhash[:openstack_region] = @options[:openstack_region] if @options[:openstack_region] @network_client ||= Fog::Network.new(networkoptionhash) if not @network_client 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]})" end end |
Instance Method Details
#cleanup ⇒ Object
Destroy any OpenStack instances
262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 |
# File 'lib/beaker/hypervisor/openstack.rb', line 262 def cleanup @logger.notify "Cleaning up OpenStack" @vms.each do |vm| cleanup_storage(vm) @logger.debug "Release floating IPs for OpenStack host #{vm.name}" floating_ips = vm.all_addresses # fetch and release its floating IPs floating_ips.each do |address| @compute_client.disassociate_address(vm.id, address['ip']) @compute_client.release_address(address['id']) end @logger.debug "Destroying OpenStack host #{vm.name}" vm.destroy if @options[:openstack_keyname].nil? @logger.debug "Deleting random keypair" @compute_client.delete_key_pair vm.key_name end end end |
#cleanup_storage(vm) ⇒ Object
Detach and delete guest volumes
165 166 167 168 169 170 171 172 |
# File 'lib/beaker/hypervisor/openstack.rb', line 165 def cleanup_storage vm vm.volumes.each do |vol| @logger.debug "Deleting volume #{vol.name} for OpenStack host #{vm.name}" vm.detach_volume(vol.id) vol.wait_for { ready? } vol.destroy end end |
#enable_root(host) ⇒ Object
enable root on a single host (the current one presumably) but only if the username isn’t ‘root’
295 296 297 298 299 300 301 302 |
# File 'lib/beaker/hypervisor/openstack.rb', line 295 def enable_root(host) if host['user'] != 'root' copy_ssh_to_root(host, @options) enable_root_login(host, @options) host['user'] = 'root' host.close end end |
#enable_root_on_hosts ⇒ void
This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.
This method returns an undefined value.
Enables root access for a host when username is not root This method ripped from the aws_sdk implementation and is probably wrong because it iterates on a collection when there’s no guarantee the collection has all been brought up in openstack yet and will thus explode
287 288 289 290 291 |
# File 'lib/beaker/hypervisor/openstack.rb', line 287 def enable_root_on_hosts @hosts.each do |host| enable_root(host) end end |
#flavor(f) ⇒ String
Provided a flavor name return the OpenStack id for that flavor
71 72 73 74 |
# File 'lib/beaker/hypervisor/openstack.rb', line 71 def flavor f @logger.debug "OpenStack: Looking up flavor '#{f}'" @compute_client.flavors.find { |x| x.name == f } || raise("Couldn't find flavor: #{f}") end |
#get_ip ⇒ Object
Get a floating IP address to associate with the instance, and allocate a new one if none are available
176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 |
# File 'lib/beaker/hypervisor/openstack.rb', line 176 def get_ip @logger.debug "Finding IP" ip = @compute_client.addresses.find { |ip| ip.instance_id.nil? } if ip.nil? begin @logger.debug "Creating IP" ip = @compute_client.addresses.create rescue Fog::Compute::OpenStack::NotFound # If there are no more floating IP addresses, allocate a # new one and try again. @compute_client.allocate_address(@options[:floating_ip_pool]) ip = @compute_client.addresses.find { |ip| ip.instance_id.nil? } end end raise 'Could not find or allocate an address' if not ip ip end |
#image(i) ⇒ String
Provided an image name return the OpenStack id for that image
79 80 81 82 |
# File 'lib/beaker/hypervisor/openstack.rb', line 79 def image i @logger.debug "OpenStack: Looking up image '#{i}'" @compute_client.images.find { |x| x.name == i } || raise("Couldn't find image: #{i}") end |
#key_name(host) ⇒ String
This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.
Get key_name from options or generate a new rsa key and add it to OpenStack keypairs
310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 |
# File 'lib/beaker/hypervisor/openstack.rb', line 310 def key_name(host) if @options[:openstack_keyname] @logger.debug "Adding optional key_name #{@options[:openstack_keyname]} to #{host.name} (#{host[:vmfqdn]})" @options[:openstack_keyname] else @logger.debug "Generate a new rsa key" key = OpenSSL::PKey::RSA.new 2048 type = key.ssh_type data = [ key.to_blob ].pack('m0') @logger.debug "Creating Openstack keypair for public key '#{type} #{data}'" @compute_client.create_key_pair host[:vmhostname], "#{type} #{data}" host['ssh'][:key_data] = [ key.to_pem ] host[:vmhostname] end end |
#network(n) ⇒ String
Provided a network name return the OpenStack id for that network
87 88 89 90 |
# File 'lib/beaker/hypervisor/openstack.rb', line 87 def network n @logger.debug "OpenStack: Looking up network '#{n}'" @network_client.networks.find { |x| x.name == n } || raise("Couldn't find network: #{n}") end |
#provision ⇒ Object
Create new instances in OpenStack
195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 |
# File 'lib/beaker/hypervisor/openstack.rb', line 195 def provision @logger.notify "Provisioning OpenStack" @hosts.each do |host| ip = get_ip host[:vmhostname] = ip.ip.gsub '.','-' host[:vmfqdn] = host[:vmhostname] + '.rfc1918.puppetlabs.net' host[:keyname] = key_name(host) @logger.debug "Provisioning #{host.name} (#{host[:vmfqdn]})" = { :flavor_ref => flavor(host[:flavor]).id, :image_ref => image(host[:image]).id, :nics => [ {'net_id' => network(@options[:openstack_network]).id } ], :name => host[:vmfqdn], :hostname => host[:vmfqdn], :user_data => host[:user_data] || "#cloud-config\nmanage_etc_hosts: true\n", :key_name => host[:keyname], } [:security_groups] = security_groups(@options[:security_group]) unless @options[:security_group].nil? vm = @compute_client.servers.create() #wait for the new instance to start up start = Time.now try = 1 attempts = @options[:timeout].to_i / SLEEPWAIT while try <= attempts begin vm.wait_for(5) { ready? } break rescue Fog::Errors::TimeoutError => e if try >= attempts @logger.debug "Failed to connect to new OpenStack instance #{host.name} (#{host[:vmfqdn]})" raise e end @logger.debug "Timeout connecting to instance #{host.name} (#{host[:vmfqdn]}), trying again..." end sleep SLEEPWAIT try += 1 end # Associate a public IP to the server ip.server = vm host[:ip] = ip.ip @logger.debug "OpenStack host #{host.name} (#{host[:vmfqdn]}) assigned ip: #{host[:ip]}" #set metadata vm..update({:jenkins_build_url => @options[:jenkins_build_url].to_s, :department => @options[:department].to_s, :project => @options[:project].to_s }) @vms << vm # Wait for the host to accept ssh logins host.wait_for_port(22) #enable root if user is not root enable_root(host) provision_storage(host, vm) end hack_etc_hosts @hosts, @options end |
#provision_storage(host, vm) ⇒ Object
Create and attach dynamic volumes
Creates an array of volumes and attaches them to the current host. The host bus type is determined by the image type, so by default devices appear as /dev/vdb, /dev/vdc etc. Setting the glance properties hw_disk_bus=scsi, hw_scsi_model=virtio-scsi will present them as /dev/sdb, /dev/sdc (or 2:0:0:1, 2:0:0:2 in SCSI addresses)
135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 |
# File 'lib/beaker/hypervisor/openstack.rb', line 135 def provision_storage host, vm if host['volumes'] # Lazily create the volume client if needed volume_client_create host['volumes'].keys.each_with_index do |volume, index| @logger.debug "Creating volume #{volume} for OpenStack host #{host.name}" # The node defintion file defines volume sizes in MB (due to precedent # with the vagrant virtualbox implementation) however OpenStack requires # this translating into GB openstack_size = host['volumes'][volume]['size'].to_i / 1000 # Create the volume and wait for it to become available vol = @volume_client.volumes.create( :size => openstack_size, :display_name => volume, :description => "Beaker volume: host=#{host.name} volume=#{volume}", ) vol.wait_for { ready? } # Fog needs a device name to attach as, so invent one. The guest # doesn't pay any attention to this device = "/dev/vd#{('b'.ord + index).chr}" vm.attach_volume(vol.id, device) end end end |
#security_groups(sgs) ⇒ Array
Provided an array of security groups return that array if all security groups are present
96 97 98 99 100 101 102 |
# File 'lib/beaker/hypervisor/openstack.rb', line 96 def security_groups sgs for sg in sgs @logger.debug "Openstack: Looking up security group '#{sg}'" @compute_client.security_groups.find { |x| x.name == sg } || raise("Couldn't find security group: #{sg}") sgs end end |
#volume_client_create ⇒ Fog::OpenStack::Volume
Create a volume client on request
106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 |
# File 'lib/beaker/hypervisor/openstack.rb', line 106 def volume_client_create = { :provider => :openstack, :openstack_api_key => @options[:openstack_api_key], :openstack_username => @options[:openstack_username], :openstack_auth_url => @options[:openstack_auth_url], :openstack_tenant => @options[:openstack_tenant], :openstack_region => @options[:openstack_region], } @volume_client ||= Fog::Volume.new() unless @volume_client raise "Unable to create OpenStack Volume instance"\ " (api_key: #{@options[:openstack_api_key]},"\ " username: #{@options[:openstack_username]},"\ " auth_url: #{@options[:openstack_auth_url]},"\ " tenant: #{@options[:openstack_tenant]})" end end |