class Kitchen::Driver::Openstack
This takes from the Base Class and creates the OpenStack driver.
Public Instance Methods
config_server_name()
click to toggle source
Set the proper server name in the config
# File lib/kitchen/driver/openstack.rb, line 66 def config_server_name return if config[:server_name] config[:server_name] = if config[:server_name_prefix] server_name_prefix(config[:server_name_prefix]) else default_name end end
create(state)
click to toggle source
# File lib/kitchen/driver/openstack.rb, line 76 def create(state) config_server_name if state[:server_id] info "#{config[:server_name]} (#{state[:server_id]}) already exists." return end disable_ssl_validation if config[:disable_ssl_validation] server = create_server state[:server_id] = server.id # this is due to the glance_caching issues. Annoying yes, but necessary. debug "Waiting for a max time of:#{config[:glance_cache_wait_timeout]} seconds for OpenStack server to be in ACTIVE state" server.wait_for(config[:glance_cache_wait_timeout]) do sleep(1) raise(Kitchen::InstanceFailure, "OpenStack server ID <#{state[:server_id]}> build failed to ERROR state") if failed? ready? end info "OpenStack server ID <#{state[:server_id]}> created" if config[:floating_ip] attach_ip(server, config[:floating_ip]) elsif config[:floating_ip_pool] attach_ip_from_pool(server, config[:floating_ip_pool]) end state[:hostname] = get_ip(server) wait_for_server(state) add_ohai_hint(state) rescue Fog::Errors::Error, Excon::Errors::Error => ex raise ActionFailed, ex.message end
destroy(state)
click to toggle source
# File lib/kitchen/driver/openstack.rb, line 108 def destroy(state) return if state[:server_id].nil? disable_ssl_validation if config[:disable_ssl_validation] server = compute.servers.get(state[:server_id]) unless server.nil? if config[:floating_ip_pool] && config[:allocate_floating_ip] info "Retrieve the floating IP" pub, priv = get_public_private_ips(server) pub, = parse_ips(pub, priv) pub_ip = pub[config[:public_ip_order].to_i] || nil if pub_ip info "Retrieve the ID of floating IP <#{pub_ip}>" floating_ip_id = network.list_floating_ips(floating_ip_address: pub_ip).body["floatingips"][0]["id"] network.delete_floating_ip(floating_ip_id) info "OpenStack Floating IP <#{pub_ip}> released." end end server.destroy end info "OpenStack instance <#{state[:server_id]}> destroyed." state.delete(:server_id) state.delete(:hostname) end
Private Instance Methods
add_ohai_hint(state)
click to toggle source
# File lib/kitchen/driver/openstack.rb, line 400 def add_ohai_hint(state) if bourne_shell? info "Adding OpenStack hint for ohai" mkdir_cmd = "sudo mkdir -p #{hints_path}" touch_cmd = "sudo bash -c 'echo {} > #{hints_path}/openstack.json'" instance.transport.connection(state).execute( "#{mkdir_cmd} && #{touch_cmd}" ) elsif windows_os? info "Adding OpenStack hint for ohai" touch_cmd = "New-Item #{hints_path}\\openstack.json" touch_cmd_args = "-Value '{}' -Force -Type file" instance.transport.connection(state).execute( "#{touch_cmd} #{touch_cmd_args}" ) end end
attach_ip(server, ip)
click to toggle source
# File lib/kitchen/driver/openstack.rb, line 334 def attach_ip(server, ip) info "Attaching floating IP <#{ip}>" server.associate_address ip end
attach_ip_from_pool(server, pool)
click to toggle source
# File lib/kitchen/driver/openstack.rb, line 308 def attach_ip_from_pool(server, pool) @@ip_pool_lock.synchronize do info "Attaching floating IP from <#{pool}> pool" if config[:allocate_floating_ip] network_id = network .list_networks( name: pool ).body["networks"][0]["id"] resp = network.create_floating_ip(network_id) ip = resp.body["floatingip"]["floating_ip_address"] info "Created floating IP <#{ip}> from <#{pool}> pool" config[:floating_ip] = ip else free_addrs = compute.addresses.map do |i| i.ip if i.fixed_ip.nil? && i.instance_id.nil? && i.pool == pool end.compact if free_addrs.empty? raise ActionFailed, "No available IPs in pool <#{pool}>" end config[:floating_ip] = free_addrs[0] end attach_ip(server, config[:floating_ip]) end end
compute()
click to toggle source
# File lib/kitchen/driver/openstack.rb, line 164 def compute Fog::OpenStack::Compute.new(openstack_server) end
connection_options()
click to toggle source
# File lib/kitchen/driver/openstack.rb, line 156 def connection_options %i{read_timeout write_timeout connect_timeout} end
countdown(seconds)
click to toggle source
# File lib/kitchen/driver/openstack.rb, line 440 def countdown(seconds) date1 = Time.now + seconds while Time.now < date1 Kernel.print "." sleep 10 end end
create_server()
click to toggle source
# File lib/kitchen/driver/openstack.rb, line 176 def create_server server_def = init_configuration raise(ActionFailed, "Cannot specify both network_ref and network_id") if config[:network_id] && config[:network_ref] if config[:network_id] networks = [].concat([config[:network_id]]) server_def[:nics] = networks.flatten.map do |net_id| { "net_id" => net_id } end elsif config[:network_ref] networks = [].concat([config[:network_ref]]) server_def[:nics] = networks.flatten.map do |net| { "net_id" => find_network(net).id } end end if config[:block_device_mapping] server_def[:block_device_mapping] = get_bdm(config) end %i{ security_groups key_name user_data config_drive metadata }.each do |c| server_def[c] = optional_config(c) if config[c] end if config[:cloud_config] raise(ActionFailed, "Cannot specify both cloud_config and user_data") if config[:user_data] server_def[:user_data] = Kitchen::Util.stringified_hash(config[:cloud_config]).to_yaml.gsub(/^---\n/, "#cloud-config\n") end # Can't use the Fog bootstrap and/or setup methods here; they require a # public IP address that can't be guaranteed to exist across all # OpenStack deployments (e.g. TryStack ARM only has private IPs). compute.servers.create(server_def) end
default_name()
click to toggle source
Generate what should be a unique server name up to 63 total chars Base name: 15 Username: 15 Hostname: 23 Random string: 7 Separators: 3
¶ ↑
Total: 63
# File lib/kitchen/driver/openstack.rb, line 273 def default_name [ instance.name.gsub(/\W/, "")[0..14], ((Etc.getpwuid ? Etc.getpwuid.name : Etc.getlogin) || "nologin").gsub(/\W/, "")[0..14], Socket.gethostname.gsub(/\W/, "")[0..22], Array.new(7) { rand(36).to_s(36) }.join, ].join("-") end
disable_ssl_validation()
click to toggle source
# File lib/kitchen/driver/openstack.rb, line 422 def disable_ssl_validation require "excon" unless defined?(Excon) Excon.defaults[:ssl_verify_peer] = false end
filter_ips(addresses)
click to toggle source
# File lib/kitchen/driver/openstack.rb, line 381 def filter_ips(addresses) if config[:use_ipv6] addresses.select { |i| IPAddr.new(i["addr"]).ipv6? } else addresses.select { |i| IPAddr.new(i["addr"]).ipv4? } end end
find_flavor(flavor_ref)
click to toggle source
# File lib/kitchen/driver/openstack.rb, line 249 def find_flavor(flavor_ref) flavor = find_matching(compute.flavors, flavor_ref) raise(ActionFailed, "Flavor not found") unless flavor debug "Selected flavor: #{flavor.id} #{flavor.name}" flavor end
find_image(image_ref)
click to toggle source
# File lib/kitchen/driver/openstack.rb, line 241 def find_image(image_ref) image = find_matching(compute.images, image_ref) raise(ActionFailed, "Image not found") unless image debug "Selected image: #{image.id} #{image.name}" image end
find_matching(collection, name)
click to toggle source
# File lib/kitchen/driver/openstack.rb, line 448 def find_matching(collection, name) name = name.to_s if name.start_with?("/") && name.end_with?("/") regex = Regexp.new(name[1...-1]) # check for regex name match collection.each { |single| return single if regex&.match?(single.name) } else # check for exact id match collection.each { |single| return single if single.id == name } # check for exact name match collection.each { |single| return single if single.name == name } end nil end
find_network(network_ref)
click to toggle source
# File lib/kitchen/driver/openstack.rb, line 257 def find_network(network_ref) net = find_matching(network.networks.all, network_ref) raise(ActionFailed, "Network not found") unless net debug "Selected net: #{net.id} #{net.name}" net end
get_bdm(config)
click to toggle source
# File lib/kitchen/driver/openstack.rb, line 172 def get_bdm(config) volume.get_bdm(config, openstack_server) end
get_ip(server)
click to toggle source
# File lib/kitchen/driver/openstack.rb, line 352 def get_ip(server) if config[:floating_ip] debug "Using floating ip: #{config[:floating_ip]}" return config[:floating_ip] end # make sure we have the latest info info "Waiting for network information to be available..." begin w = server.wait_for { !addresses.empty? } debug "Waited #{w[:duration]} seconds for network information." rescue Fog::Errors::TimeoutError raise ActionFailed, "Could not get network information (timed out)" end # should also work for private networks if config[:openstack_network_name] debug "Using configured net: #{config[:openstack_network_name]}" return filter_ips(server.addresses[config[:openstack_network_name]]).first["addr"] end pub, priv = get_public_private_ips(server) priv = server.ip_addresses if Array(pub).empty? && Array(priv).empty? pub, priv = parse_ips(pub, priv) pub[config[:public_ip_order].to_i] || priv[config[:private_ip_order].to_i] || raise(ActionFailed, "Could not find an IP") end
get_public_private_ips(server)
click to toggle source
# File lib/kitchen/driver/openstack.rb, line 339 def get_public_private_ips(server) begin pub = server.public_ip_addresses priv = server.private_ip_addresses rescue Fog::OpenStack::Compute::NotFound, Excon::Errors::Forbidden # See Fog issue: https://github.com/fog/fog/issues/2160 addrs = server.addresses addrs["public"] && pub = addrs["public"].map { |i| i["addr"] } addrs["private"] && priv = addrs["private"].map { |i| i["addr"] } end [pub, priv] end
hints_path()
click to toggle source
# File lib/kitchen/driver/openstack.rb, line 418 def hints_path Ohai.config[:hints_path][0] end
init_configuration()
click to toggle source
# File lib/kitchen/driver/openstack.rb, line 218 def init_configuration raise(ActionFailed, "Cannot specify both image_ref and image_id") if config[:image_id] && config[:image_ref] raise(ActionFailed, "Cannot specify both flavor_ref and flavor_id") if config[:flavor_id] && config[:flavor_ref] { name: config[:server_name], image_ref: config[:image_id] || find_image(config[:image_ref]).id, flavor_ref: config[:flavor_id] || find_flavor(config[:flavor_ref]).id, availability_zone: config[:availability_zone], } end
network()
click to toggle source
# File lib/kitchen/driver/openstack.rb, line 160 def network Fog::OpenStack::Network.new(openstack_server) end
openstack_server()
click to toggle source
# File lib/kitchen/driver/openstack.rb, line 136 def openstack_server server_def = { connection_options: {}, } required_server_settings.each { |s| server_def[s] = config[s] } optional_server_settings.each { |s| server_def[s] = config[s] if config[s] } connection_options.each { |s| server_def[:connection_options][s] = config[s] if config[s] } server_def end
optional_config(c)
click to toggle source
# File lib/kitchen/driver/openstack.rb, line 230 def optional_config(c) case c when :security_groups config[c] if config[c].is_a?(Array) when :user_data File.open(config[c], &:read) if File.exist?(config[c]) else config[c] end end
optional_server_settings()
click to toggle source
# File lib/kitchen/driver/openstack.rb, line 150 def optional_server_settings Fog::OpenStack::Compute.recognized.select do |k| k.to_s.start_with?("openstack") end - required_server_settings end
parse_ips(pub, priv)
click to toggle source
# File lib/kitchen/driver/openstack.rb, line 389 def parse_ips(pub, priv) pub = Array(pub) priv = Array(priv) if config[:use_ipv6] [pub, priv].each { |n| n.select! { |i| IPAddr.new(i).ipv6? } } else [pub, priv].each { |n| n.select! { |i| IPAddr.new(i).ipv4? } } end [pub, priv] end
required_server_settings()
click to toggle source
# File lib/kitchen/driver/openstack.rb, line 146 def required_server_settings %i{openstack_username openstack_api_key openstack_auth_url openstack_domain_id} end
server_name_prefix(server_name_prefix)
click to toggle source
# File lib/kitchen/driver/openstack.rb, line 282 def server_name_prefix(server_name_prefix) # Generate what should be a unique server name with given prefix # of up to 63 total chars # # Provided prefix: variable, max 54 # Separator: 1 # Random string: 8 # =================== # Max: 63 # if server_name_prefix.length > 54 warn "Server name prefix too long, truncated to 54 characters" server_name_prefix = server_name_prefix[0..53] end server_name_prefix.gsub!(/\W/, "") if server_name_prefix.empty? warn "Server name prefix empty or invalid; using fully generated name" default_name else random_suffix = ("a".."z").to_a.sample(8).join server_name_prefix + "-" + random_suffix end end
volume()
click to toggle source
# File lib/kitchen/driver/openstack.rb, line 168 def volume Volume.new(logger) end
wait_for_server(state)
click to toggle source
# File lib/kitchen/driver/openstack.rb, line 427 def wait_for_server(state) if config[:server_wait] info "Sleeping for #{config[:server_wait]} seconds to let your server start up..." countdown(config[:server_wait]) end info "Waiting for server to be ready..." instance.transport.connection(state).wait_until_ready rescue error "Server #{state[:hostname]} (#{state[:server_id]}) not reachable. Destroying server..." destroy(state) raise end