lib/chef/knife/openstack_server_create.rb in knife-openstack-0.6.2 vs lib/chef/knife/openstack_server_create.rb in knife-openstack-0.7.0

- old
+ new

@@ -1,9 +1,9 @@ # # Author:: Seth Chisamore (<schisamo@opscode.com>) # Author:: Matt Ray (<matt@opscode.com>) -# Copyright:: Copyright (c) 2011-2012 Opscode, Inc. +# Copyright:: Copyright (c) 2011-2013 Opscode, Inc. # License:: Apache License, Version 2.0 # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at @@ -47,28 +47,27 @@ :short => "-I IMAGE_ID", :long => "--image IMAGE_ID", :description => "The image ID for the server", :proc => Proc.new { |i| Chef::Config[:knife][:image] = i } - # option :security_groups, - # :short => "-G X,Y,Z", - # :long => "--groups X,Y,Z", - # :description => "The security groups for this server", - # :default => ["default"], - # :proc => Proc.new { |groups| groups.split(',') } + option :security_groups, + :short => "-G X,Y,Z", + :long => "--groups X,Y,Z", + :description => "The security groups for this server", + :default => ["default"], + :proc => Proc.new { |groups| groups.split(',') } option :chef_node_name, :short => "-N NAME", :long => "--node-name NAME", :description => "The Chef node name for your new node" option :floating_ip, - :short => "-a", - :long => "--floating-ip", - :boolean => true, - :default => false, - :description => "Request to associate a floating IP address to the new OpenStack node. Assumes IPs have been allocated to the project." + :short => "-a [IP]", + :long => "--floating-ip [IP]", + :default => "-1", + :description => "Request to associate a floating IP address to the new OpenStack node. Assumes IPs have been allocated to the project. Specific IP is optional." option :private_network, :long => "--private-network", :description => "Use the private IP for bootstrapping rather than the public IP", :boolean => true, @@ -146,11 +145,11 @@ rescue Errno::EPERM false rescue Errno::ECONNREFUSED sleep 2 false - rescue Errno::EHOSTUNREACH + rescue Errno::EHOSTUNREACH, Errno::ENETUNREACH sleep 2 false rescue Errno::ENETUNREACH sleep 2 false @@ -161,83 +160,96 @@ def run $stdout.sync = true validate! - connection = Fog::Compute.new( - :provider => 'OpenStack', - :openstack_username => Chef::Config[:knife][:openstack_username], - :openstack_api_key => Chef::Config[:knife][:openstack_password], - :openstack_auth_url => Chef::Config[:knife][:openstack_auth_url], - :openstack_tenant => Chef::Config[:knife][:openstack_tenant] - ) - #servers require a name, generate one if not passed node_name = get_node_name(config[:chef_node_name]) server_def = { :name => node_name, :image_ref => locate_config_value(:image), :flavor_ref => locate_config_value(:flavor), - # :security_group => locate_config_value(:security_groups), - :key_name => Chef::Config[:knife][:openstack_ssh_key_id], - :personality => [{ - "path" => "/etc/chef/ohai/hints/openstack.json", - "contents" => '' - }] + :security_groups => locate_config_value(:security_groups), + :key_name => locate_config_value(:openstack_ssh_key_id) } Chef::Log.debug("Name #{node_name}") Chef::Log.debug("Image #{locate_config_value(:image)}") Chef::Log.debug("Flavor #{locate_config_value(:flavor)}") - # Chef::Log.debug("Groups #{locate_config_value(:security_groups)}") + Chef::Log.debug("Requested Floating IP #{locate_config_value(:floating_ip)}") + Chef::Log.debug("Security Groups #{locate_config_value(:security_groups)}") Chef::Log.debug("Creating server #{server_def}") - server = connection.servers.create(server_def) + begin + server = connection.servers.create(server_def) + rescue Excon::Errors::BadRequest => e + response = Chef::JSONCompat.from_json(e.response.body) + if response['badRequest']['code'] == 400 + if response['badRequest']['message'] =~ /Invalid flavorRef/ + ui.fatal("Bad request (400): Invalid flavor specified: #{server_def[:flavor_ref]}") + exit 1 + else + ui.fatal("Bad request (400): #{response['badRequest']['message']}") + exit 1 + end + else + ui.fatal("Unknown server error (#{response['badRequest']['code']}): #{response['badRequest']['message']}") + raise e + end + end + msg_pair("Instance Name", server.name) msg_pair("Instance ID", server.id) - # msg_pair("Security Groups", server.groups.join(", ")) - msg_pair("SSH Keypair", server.key_name) print "\n#{ui.color("Waiting for server", :magenta)}" # wait for it to be ready to do stuff server.wait_for { print "."; ready? } puts("\n") msg_pair("Flavor", server.flavor['id']) msg_pair("Image", server.image['id']) - msg_pair("Public IP Address", server.public_ip_address['addr']) if server.public_ip_address + msg_pair("SSH Identity File", config[:identity_file]) + msg_pair("SSH Keypair", server.key_name) if server.key_name + msg_pair("SSH Password", server.password) if (server.password && !server.key_name) + Chef::Log.debug("Addresses #{server.addresses}") + msg_pair("Public IP Address", primary_public_ip_address(server.addresses)) if primary_public_ip_address(server.addresses) - if config[:floating_ip] - associated = false - connection.addresses.each do |address| - if address.instance_id.nil? - server.associate_address(address.ip) - #a bit of a hack, but server.reload takes a long time - server.addresses['public'].push({"version"=>4,"addr"=>address.ip}) - associated = true - msg_pair("Floating IP Address", address.ip) - break + floating_address = locate_config_value(:floating_ip) + Chef::Log.debug("Floating IP Address requested #{floating_address}") + unless (floating_address == '-1') #no floating IP requested + addresses = connection.addresses + #floating requested without value + if floating_address.nil? + free_floating = addresses.find_index {|a| a.fixed_ip.nil?} + if free_floating.nil? #no free floating IP found + ui.error("Unable to assign a Floating IP from allocated IPs.") + exit 1 + else + floating_address = addresses[free_floating].ip end end - unless associated - ui.error("Unable to associate floating IP.") - exit 1 - end + server.associate_address(floating_address) + #a bit of a hack, but server.reload takes a long time + (server.addresses['public'] ||= []) << {"version"=>4,"addr"=>floating_address} + msg_pair("Floating IP Address", floating_address) end - Chef::Log.debug("Public IP Address actual #{server.public_ip_address['addr']}") if server.public_ip_address - msg_pair("Private IP Address", server.private_ip_address['addr']) if server.private_ip_address + Chef::Log.debug("Addresses #{server.addresses}") + Chef::Log.debug("Public IP Address actual: #{primary_public_ip_address(server.addresses)}") if primary_public_ip_address(server.addresses) + msg_pair("Private IP Address", primary_private_ip_address(server.addresses)) if primary_private_ip_address(server.addresses) + #which IP address to bootstrap - bootstrap_ip_address = server.public_ip_address['addr'] if server.public_ip_address + bootstrap_ip_address = primary_public_ip_address(server.addresses) if primary_public_ip_address(server.addresses) if config[:private_network] - bootstrap_ip_address = server.private_ip_address['addr'] + bootstrap_ip_address = primary_private_ip_address(server.addresses) end - Chef::Log.debug("Bootstrap IP Address #{bootstrap_ip_address}") + + Chef::Log.debug("Bootstrap IP Address: #{bootstrap_ip_address}") if bootstrap_ip_address.nil? ui.error("No IP address available for bootstrapping.") exit 1 end @@ -253,44 +265,84 @@ puts "\n" msg_pair("Instance Name", server.name) msg_pair("Instance ID", server.id) msg_pair("Flavor", server.flavor['id']) msg_pair("Image", server.image['id']) - # msg_pair("Security Groups", server.groups.join(", ")) - msg_pair("SSH Keypair", server.key_name) - msg_pair("Public IP Address", server.public_ip_address['addr']) if server.public_ip_address - msg_pair("Private IP Address", server.private_ip_address['addr']) if server.private_ip_address + msg_pair("SSH Keypair", server.key_name) if server.key_name + msg_pair("SSH Password", server.password) if (server.password && !server.key_name) + msg_pair("Public IP Address", primary_public_ip_address(server.addresses)) if primary_public_ip_address(server.addresses) + msg_pair("Private IP Address", primary_private_ip_address(server.addresses)) if primary_private_ip_address(server.addresses) msg_pair("Environment", config[:environment] || '_default') msg_pair("Run List", config[:run_list].join(', ')) end def bootstrap_for_node(server, bootstrap_ip_address) bootstrap = Chef::Knife::Bootstrap.new bootstrap.name_args = [bootstrap_ip_address] bootstrap.config[:run_list] = config[:run_list] bootstrap.config[:ssh_user] = config[:ssh_user] + bootstrap.config[:ssh_password] = server.password bootstrap.config[:identity_file] = config[:identity_file] bootstrap.config[:host_key_verify] = config[:host_key_verify] bootstrap.config[:chef_node_name] = server.name bootstrap.config[:prerelease] = config[:prerelease] bootstrap.config[:bootstrap_version] = locate_config_value(:bootstrap_version) bootstrap.config[:distro] = locate_config_value(:distro) bootstrap.config[:use_sudo] = true unless config[:ssh_user] == 'root' bootstrap.config[:template_file] = locate_config_value(:template_file) bootstrap.config[:environment] = config[:environment] + # let ohai know we're using OpenStack + Chef::Config[:knife][:hints] ||= {} + Chef::Config[:knife][:hints]['openstack'] ||= {} bootstrap end - def ami - @ami ||= connection.images.get(locate_config_value(:image)) + def flavor + @flavor ||= connection.flavors.get(locate_config_value(:flavor)) end + def image + @image ||= connection.images.get(locate_config_value(:image)) + end + + def is_floating_ip_valid + address = locate_config_value(:floating_ip) + if address == '-1' #no floating IP requested + return true + end + addresses = connection.addresses + return false if addresses.empty? #no floating IPs + #floating requested without value + if address.nil? + if addresses.find_index {|a| a.fixed_ip.nil?} + return true + else + return false #no floating IPs available + end + end + #floating requested with value + if addresses.find_index {|a| a.ip == address} + return true + else + return false #requested floating IP does not exist + end + end + def validate! + super([:image, :openstack_username, :openstack_password, :openstack_auth_url]) - super([:image, :openstack_ssh_key_id, :openstack_username, :openstack_password, :openstack_auth_url]) + if flavor.nil? + ui.error("You have not provided a valid flavor ID. Please note the options for this value are -f or --flavor.") + exit 1 + end - if ami.nil? - ui.error("You have not provided a valid image ID. Please note the short option for this value recently changed from '-i' to '-I'.") + if image.nil? + ui.error("You have not provided a valid image ID. Please note the options for this value are -I or --image.") + exit 1 + end + + if !is_floating_ip_valid + ui.error("You have either requested an invalid floating IP address or none are available.") exit 1 end end #generate a random name if chef_node_name is empty