Skip to content

Instantly share code, notes, and snippets.

@marthag
Created July 22, 2012 17:14
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 2 You must be signed in to fork a gist
  • Save marthag/3160342 to your computer and use it in GitHub Desktop.
Save marthag/3160342 to your computer and use it in GitHub Desktop.
Zone creation script
#!/usr/bin/env ruby
%w(erb git net/ssh resolv chef chef/knife/cookbook_upload chef/cookbook_uploader chef/knife/node_run_list_add chef/knife/bootstrap net/ssh/multi chef/knife/core/bootstrap_context chef/knife/ssh mixlib/cli).each do |gem|
begin
require gem
rescue LoadError
raise "Could not load gem #{gem}, please install with sudo gem install #{gem}"
end
end
# setup the command line options
class MyCLI
include Mixlib::CLI
option :debug,
:short => "-d",
:long => "--debug",
:description => "Turn on debugging messages",
:boolean => true,
:default => false
option :git,
:short => "-t",
:long => "--git",
:description => "Add/commit/push new recipes to git",
:boolean => true,
:default => false
option :global,
:short => "-g GLOBAL",
:long => "--global GLOBAL",
:description => "The FQDN of the server to create the zone on",
:required => true
option :help,
:short => "-h",
:long => "--help",
:description => "Show this message",
:on => :tail,
:boolean => true,
:show_options => true,
:exit => 0
option :knife_config,
:short => "-c KNIFE_CONFIG",
:long => "--config KNIFE_CONFIG",
:description => "Knife configuration file (defaults to ~/.chef/knife.rb)",
:default => "~/.chef/knife.rb"
option :net,
:short => "-n NET",
:long => "--net NET",
:description => "Network information for the new zone, in the form: ipaddress[/cidr]:interface",
:required => true
option :run_list,
:short => "-r",
:long => "--run_list RUN_LIST",
:description => "Specify the run_list for the new zone",
:default => ""
option :ssh_password,
:short => "-s SSH_PASSWORD",
:long => "--password SSH_PASSWORD",
:description => "SSH password to use",
:required => true
option :ssh_port,
:short => "-P SSH_PORT",
:long => "--port SSH_PORT",
:description => "SSH port to use (defaults to 22)",
:default => 22
option :zone,
:short => "-z ZONE",
:long => "--zone ZONE",
:description => "The hostname of the new zone",
:required => true
option :zpool,
:short => "-p ZPOOL",
:long => "--zpool ZPOOL",
:description => "Name of the zpool to use (defaults to rpool)",
:default => "rpool"
end
# parse the command line options
cli = MyCLI.new
cli.parse_options
debug = cli.config[:debug]
zone_hostname = cli.config[:zone]
net = cli.config[:net]
global = cli.config[:global]
zpool = cli.config[:zpool]
run_list = cli.config[:run_list]
use_git = cli.config[:git]
ssh_password = cli.config[:ssh_password]
ssh_port = cli.config[:ssh_port]
knife_rb = File.expand_path(cli.config[:knife_config])
if debug
p cli.config
end
crypted_password = ssh_password.crypt("wh")
zone_ip = net.split(/\/|:/)[0]
if debug
puts "Zone ip is " + zone_ip
end
# set the FQDN
zone_name_array = global.split('.')
zone_fqdn = zone_hostname + '.' + zone_name_array[1..-1].join('.')
# check if the zone name resolves
no_dns = false
begin
Resolv.getaddress(zone_fqdn)
rescue Exception => e
no_dns = true
puts "Warning: #{zone_fqdn} does not resolve, please add to DNS!"
end
if debug
puts "Zpool is " + zpool
end
# load the user's knife.rb
Chef::Config.from_file(knife_rb)
#server_url = Chef::Config[:server_url]
cookbook_path = Chef::Config[:cookbook_path][0]
if debug
puts "Putting recipes in " + cookbook_path
# puts "Server URL is " + server_url
end
# setup the recipe templates
def write_template(template,file)
File.open(file, "w") do |f|
f.write(template.result)
end
end
template = ERB.new <<-EOF
zfs "<%= zpool %>/<%= zone_hostname %>"
directory "/<%= zpool %>/<%= zone_hostname %>" do
mode "0700"
end
zone "<%= zone_hostname %>" do
path "/<%= zpool %>/<%= zone_hostname %>"
password "<%= crypted_password %>"
nets [ "<%= net %>" ]
<% if no_dns %> sysidcfg_template "sysidcfg_nodns.erb"<% end %>
end
EOF
# write the recipe files
print "Creating new recipes..."
write_template(template, File.expand_path("#{cookbook_path}/zone/recipes/#{zone_hostname}.rb"))
puts "done."
# add the recipes you created to git
if use_git
print "Adding new recipes to git..."
g = Git.open(chef_path) #, :log => Logger.new(STDOUT))
g.add("#{cookbook_path}/zone/recipes/#{zone_hostname}.rb")
# don't fail if recipes haven't changed
begin
g.commit("adding zone #{zone_hostname}")
rescue
end
# don't fail if push fails
begin
g.push
rescue
end
puts "done."
end
# upload the new cookbook
begin
puts "Uploading zone cookbook..."
ck = Chef::Knife::CookbookUpload.new(["zone"])
ck.read_config_file(knife_rb)
ck.run
puts "done."
rescue Exception => e
raise "Uploading zone cookbook failed: " + e.message
end
# add the new recipe to the global node
global_node=Chef::Node.load(global)
if debug
puts global_node.recipes
end
unless global_node.recipe?("zone::#{zone_hostname}")
begin
print "Adding new recipe zone::#{zone_hostname} to #{global}..."
gn=Chef::Knife::NodeRunListAdd.new([global, "recipe[zone::#{zone_hostname}]"])
gn.read_config_file(knife_rb)
gn.run
rescue Exception => e
raise "Adding new recipe zone::#{zone_hostname} to #{global} failed: " + e.message
end
puts "done."
end
# run chef-client on the global zone to create the new zone
exit_code = 0
begin
puts "Running chef-client on #{global}..."
Net::SSH.start(global, "root", :port => ssh_port, :password => ssh_password ) do |session|
channel = session.open_channel do |chan|
chan.request_pty
chan.exec("chef-client -l warn") do |ch, success|
raise "chef-client run failed." unless success
chan.on_request('exit-status') do |ch, data|
exit_code += data.read_long
if debug
puts("ssh exec exit-status: #{exit_code}")
end
if exit_code != 0
raise "chef-client run failed, zone was not created!"
end
end
ch.on_data do |c, data|
print data
end
ch.on_close { puts "done." }
end
end
channel.wait
end
rescue Exception => e
raise "SSH to #{global} failed: " + e.message
end
# wait for boot to finish
begin
Net::SSH.start(zone_ip, "root", :port => ssh_port, :password => ssh_password ) do |session|
end
rescue Exception => e
print "Waiting for zone to finish booting..."
sleep 90
puts "done."
end
if no_dns
begin
Net::SSH.start(global, "root", :port => ssh_port, :password => ssh_password ) do |session|
session.exec!("cp /etc/resolv.conf /#{zpool}/#{zone_hostname}/root/etc/ ;cp /etc/nsswitch.conf /#{zpool}/#{zone_hostname}/root/etc/")
end
rescue Exception => e
raise "SSH to #{global} failed: " + e.message
end
end
begin
print "Bootstrapping #{zone_fqdn} with roles #{run_list}..."
bs=Chef::Knife::Bootstrap.new(["-d", "solaris", "-r", run_list, "-p", ssh_port, "-P", ssh_password, "--no-host-key-verify", zone_ip])
bs.read_config_file(knife_rb)
bs.run
puts "done."
rescue Exception => e
raise "Bootstrap of #{zone_fqdn} failed: " + e.message
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment