Last active
December 15, 2015 18:19
-
-
Save MattMencel/5302820 to your computer and use it in GitHub Desktop.
This is a site specific script I use to bootstrap VMware templates using Chef, the knife-vsphere gem, and rbvmomi gem. It needs lots of work to make it less site specific, but shows how knife-vsphere and rbvmomi can be used to do some cool stuff.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/usr/bin/env ruby | |
# PREREQ: YOU MUST HAVE A TEMPLATE ALREADY BUILT. IF USING UBUNTU, | |
# MAKE SURE THE LAST STEP BEFORE YOU CREATE THE TEMPLATE IS THIS OR THE NETWORKING STEPS WON'T WORK. | |
# http://chris.dziemborowicz.com/blog/2010/07/25/fix-missing-eth0-when-cloning-ubuntu-vmware-virtual-machines/ | |
require 'rubygems' | |
require 'awesome_print' | |
require 'net/ping' | |
require 'rbvmomi' | |
require 'trollop' | |
include Net | |
Ping::TCP.service_check = true | |
opts = Trollop::options do | |
opt :ccpu, "Number of CPUs", :default => 1 | |
opt :cram, "Amount or RAM (in GB)", :default => 1 | |
opt :cspec, "Customization Spec", :type => :string | |
opt :cvlan, "VLAN Name", :type => :string | |
opt :dest_folder, "Destination Folder", :type => :string | |
opt :name, "VM name", :type => :string, :required => true | |
opt :pool, "Resource or Cluster Location", :type => :string | |
opt :template, "Template Name", :type => :string | |
opt :template_folder, "Template Folder", :type => :string | |
end | |
if opts[:dest_folder] == nil | |
folder_list = `knife vsphere vm list -r --only-folders`.split("\n") | |
folder_list.map! {|f| f.split(": ")[1]} | |
folder_list.delete_if {|e| e == nil } | |
folder_list.each_with_index {|e,i| puts "(#{i+1}) #{e}"} | |
puts "Select Destination Folder for VM:" | |
selection = gets.chomp.to_i | |
if selection > folder_list.length || selection < 1 | |
puts "Invalid Input" | |
exit | |
end | |
opts[:dest_folder] = folder_list.at(selection-1) | |
end | |
if opts[:cspec_list] == nil | |
cspec_list = `knife vsphere customization list`.split("\n") | |
cspec_list.map! {|f| f.split(": ")[1]} | |
cspec_list.delete_if {|e| e == nil } | |
cspec_list.each_with_index {|e,i| puts "(#{i+1}) #{e}"} | |
puts "Select Customization Spec for VM:" | |
selection = gets.chomp.to_i | |
if selection > cspec_list.length || selection < 1 | |
puts "Invalid Input" | |
exit | |
end | |
opts[:cspec] = cspec_list.at(selection-1) | |
end | |
if opts[:template_folder] == nil | |
folder_list = `knife vsphere vm list -r --only-folders`.split("\n") | |
folder_list.map! {|f| f.split(": ")[1]} | |
folder_list.each_with_index {|e,i| puts "(#{i+1}) #{e}"} | |
puts "Select Folder Location of Template:" | |
selection = gets.chomp.to_i | |
if selection > folder_list.length || selection < 1 | |
puts "Invalid Input" | |
exit | |
end | |
opts[:template_folder] = folder_list.at(selection-1) | |
end | |
if opts[:template] == nil | |
template_list = `knife vsphere template list -f #{opts[:template_folder]}`.split("\n") | |
template_list.map! {|f| f.split(": ")[1]} | |
template_list.delete_if {|e| e == nil } | |
template_list.each_with_index {|e,i| puts "(#{i+1}) #{e}"} | |
puts "Select Template for VM:" | |
selection = gets.chomp.to_i | |
if selection > template_list.length || selection < 1 | |
puts "Invalid Input" | |
exit | |
end | |
opts[:template] = template_list.at(selection-1) | |
end | |
if opts[:cvlan] == nil | |
cvlan_list = `knife vsphere vlan list`.split("\n") | |
cvlan_list.map! {|f| f.split(": ")[1]} | |
cvlan_list.delete_if {|e| e == nil } | |
cvlan_list.each_with_index {|e,i| puts "(#{i+1}) #{e}"} | |
puts "Select VLAN for VM:" | |
selection = gets.chomp.to_i | |
if selection > cvlan_list.length || selection < 1 | |
puts "Invalid Input" | |
exit | |
end | |
opts[:cvlan] = cvlan_list.at(selection-1) | |
end | |
if opts[:pool] == nil | |
pool_list = `knife vsphere pool list`.split("\n") | |
pool_list.map! {|f| f.split(": ")[1]} | |
pool_list.delete_if {|e| e == nil } | |
pool_list.each_with_index {|e,i| puts "(#{i+1}) #{e}"} | |
puts "Select Resource Pool or Cluster for VM:" | |
selection = gets.chomp.to_i | |
if selection > pool_list.length || selection < 1 | |
puts "Invalid Input" | |
exit | |
end | |
opts[:pool] = pool_list.at(selection-1) | |
end | |
## YOU HAVE TO CUSTOMIZE THIS CASE BLOCK WITH YOUR OWN VMWARE NETWORKS | |
case opts[:cvlan] | |
when "Main-02-UNIX-pg" | |
subnet = "10.50.2.0" | |
when "Main-08-ServerConsolidation-pg" | |
subnet = "10.50.8.0" | |
else | |
puts "No subnet defined for vlan #{opts[:cvlan]}. Update script..." | |
exit | |
end | |
puts "Cloning VM From Template with Knife (Chef)...this will take a few minutes..." | |
chef_vsphere_command = "knife vsphere vm clone #{opts[:name]} --dest-folder \"#{opts[:dest_folder]}\" -N #{opts[:name]} --cspec #{opts[:cspec]} --ccpu #{opts[:ccpu]} --cram #{opts[:cram]} --ctz \"America/Chicago\" --template #{opts[:template]} -f \"#{opts[:template_folder]}\" --cvlan #{opts[:cvlan]} --resource-pool \"#{opts[:pool]}\"" | |
puts chef_vsphere_command | |
begin | |
clone_vm = `#{chef_vsphere_command} 2>&1` | |
rescue Exception => e | |
ap e | |
puts "Clone Failed..." | |
exit | |
end | |
if clone_vm.include?("no such") || clone_vm.include?("ERROR") | |
puts "Clone Failed!! === #{clone_vm}" | |
exit | |
end | |
puts "Resetting NIC (BUG: Is not connected after cloning)" | |
vim = RbVmomi::VIM.connect host: 'VCENTERSERVER', user: 'VCENTERUSER', password: 'VCENTERPASS', :insecure => true | |
dc = vim.serviceInstance.find_datacenter("DATACENTERNAME") or fail "datacenter not found" | |
vm = dc.find_vm("/#{opts[:dest_folder]}/#{opts[:name]}") or fail "VM not found" | |
dnic = vm.config.hardware.device.grep(RbVmomi::VIM::VirtualEthernetCard).find{|nic| nic.props} | |
port = dnic[:backing][:port] | |
dev_info = dnic[:deviceInfo] | |
if dnic[:connectable][:startConnected].eql?false | |
spec = RbVmomi::VIM.VirtualMachineConfigSpec({ | |
:deviceChange => [{ | |
:operation => :remove, | |
:device => dnic | |
}] | |
}) | |
puts "Removing NIC" | |
vm.ReconfigVM_Task(:spec => spec).wait_for_completion | |
puts "Adding NIC" | |
spec = RbVmomi::VIM.VirtualMachineConfigSpec( | |
:deviceChange => [{ | |
:operation => :add, | |
:device => RbVmomi::VIM::VirtualVmxnet3( | |
:key => 0, | |
:deviceInfo => { | |
:label => dev_info[:label], | |
:summary => dev_info[:summary] | |
}, | |
:backing => RbVmomi::VIM::VirtualEthernetCardDistributedVirtualPortBackingInfo( | |
:port => port | |
), | |
:addressType => 'generated' | |
) | |
}] | |
) | |
vm.ReconfigVM_Task(:spec => spec).wait_for_completion | |
end | |
puts "Creating DHCP Entry For New VM..." | |
mac_addr = vm.macs | |
if mac_addr.size > 1 | |
puts "Cannot process VMs with more than one NIC yet...exiting" | |
exit | |
end | |
macaddress = mac_addr.values[0] | |
comment = "Created by Chef #{Date.today()}" | |
## THIS IS SITE SPECIFIC CODE TO MANAGE OUR DHCP. YOU"LL HAVE TO CUSTOMIZE FOR YOUR OWN SITE. | |
#result = `ssh root@someserver ~/dhcp_maintenance/create_static_printer_or_host --add --showip --name #{opts[:name]} --macaddress #{macaddress} --subnet #{subnet} --ticket utech --uid utech --comment "#{comment}"` | |
#puts result | |
#if !result.include?("Entry added") | |
# puts "Failed to add DHCP entry...exiting..." | |
# exit | |
#end | |
ip_addr = result.split(": ")[1].chomp | |
puts "Starting VM #{opts[:name]}" | |
puts `knife vsphere vm state #{opts[:name]} -f "#{opts[:dest_folder]}" -s on` | |
puts "Waiting for SSH on #{ip_addr}..." | |
x = 0 | |
alive = false | |
until x==25 || alive | |
puts "." | |
alive = Net::Ping::TCP.new(ip_addr, 22).ping? | |
break if alive | |
sleep 5 | |
x+=1 | |
end | |
if !alive | |
puts "VM is not running. Cannot continue with Chef Bootstrap..." | |
exit | |
end | |
## HAVE TO BOOTSTRAP IN TWO STEPS AT OUR SITE BECAUSE OF THE PROXY | |
puts "Bootstrapping Chef from Opscode" | |
`knife bootstrap #{ip_addr} -N #{opts[:name]} -x root -P PASS --bootstrap-proxy http://PROXYHOST:PORT -r 'role[base]'` | |
puts "Registering VM with Chef Server" | |
`knife bootstrap #{ip_addr} -N #{opts[:name]} -x root -P PASS -r 'role[base]'` |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment