class ESX::Host
Attributes
Public Class Methods
Connect to a ESX
host
Requires hostname/ip, username and password
Host
connection is insecure by default
# File lib/esx.rb, line 44 def self.connect(host, user, password, insecure = true, opts = {}) vim = RbVmomi::VIM.connect :host => host, :user => user, :password => password, :insecure => insecure, :rev => opts[:rev]||'4.0' host = Host.new(host, user,password, opts) host.vim = vim host end
# File lib/esx.rb, line 28 def initialize(address, user, password, opts = {}) @address = address @password = password @user = user @templates_dir = opts[:templates_dir] || "/vmfs/volumes/datastore1/esx-gem/templates" @free_license = opts[:free_license] || false if @free_license and !@user.eql?"root" raise Exception.new("Can't use Free License mode with user #{@user}. Please use 'root' user.") end end
Public Instance Methods
Expects vmdk source file path and destination path
copy_from_template
“/home/fooser/my.vmdk”, “/vmfs/volumes/datastore1/foovm/foovm.vmdk”
Destination directory must exist otherwise rises exception
# File lib/esx.rb, line 353 def copy_from_template(template_disk, destination) Log.debug "Copying from template #{template_disk} to #{destination}" raise "Template does not exist" if not template_exist?(template_disk) source = File.join(@templates_dir, File.basename(template_disk)) Net::SSH.start(@address, @user, :password => @password) do |ssh| Log.debug "Clone disk #{source} to #{destination}" Log.debug ssh.exec!("vmkfstools -i #{source} --diskformat thin #{destination} 2>&1") end end
Number of CPU cores available in this host
returns a String
# File lib/esx.rb, line 75 def cpu_cores @_host.hardware.cpuInfo.numCpuCores end
# File lib/esx.rb, line 206 def create_net_dev(nic_id, spec) h = { :key => nic_id, :deviceInfo => { :label => "Network Adapter #{nic_id}", :summary => spec[:network] || 'VM Network' }, :backing => RbVmomi::VIM.VirtualEthernetCardNetworkBackingInfo( :deviceName => spec[:network] || 'VM Network' ) } if spec[:mac_address] h[:macAddress] = spec[:mac_address] h[:addressType] = 'manual' else h[:addressType] = 'generated' end h end
Create a Virtual Machine
Requires a Hash with the following keys:
{
:vm_name => name, (string, required) :cpus => 1, #(int, optional) :guest_id => 'otherGuest', #(string, optional) :disk_size => 4096, #(in MB, optional) :memory => 128, #(in MB, optional) :datastore => datastore1 #(string, optional) :disk_file => path to vmdk inside datastore (optional) :disk_type => flat, sparse (default flat)
}
supported guest_id list: pubs.vmware.com/vsphere-50/index.jsp?topic=/com.vmware.wssdk.apiref.doc_50/vim.vm.GuestOsDescriptor.GuestOsIdentifier.html
Default values above.
# File lib/esx.rb, line 125 def create_vm(specification) spec = specification spec[:cpus] = (specification[:cpus] || 1).to_i spec[:cpu_cores] = (specification[:cpu_cores] || 1).to_i spec[:guest_id] = specification[:guest_id] || 'otherGuest' if specification[:disk_size] spec[:disk_size] = (specification[:disk_size].to_i * 1024) else spec[:disk_size] = 4194304 end spec[:memory] = (specification[:memory] || 128).to_i if specification[:datastore] spec[:datastore] = "[#{specification[:datastore]}]" else spec[:datastore] = '[datastore1]' end vm_cfg = { :name => spec[:vm_name], :guestId => spec[:guest_id], :files => { :vmPathName => spec[:datastore] }, :numCPUs => spec[:cpus], :numCoresPerSocket => spec[:cpu_cores], :memoryMB => spec[:memory], :deviceChange => [ { :operation => :add, :device => RbVmomi::VIM.VirtualLsiLogicController( :key => 1000, :busNumber => 0, :sharedBus => :noSharing) } ], :extraConfig => [ { :key => 'bios.bootOrder', :value => 'ethernet0' } ] } #Add multiple nics nics_count = 0 if spec[:nics] spec[:nics].each do |nic_spec| vm_cfg[:deviceChange].push( { :operation => :add, :device => RbVmomi::VIM.VirtualE1000(create_net_dev(nics_count, nic_spec)) } ) nics_count += 1 end end # VMDK provided, replace the empty vmdk vm_cfg[:deviceChange].push(create_disk_spec(:disk_file => spec[:disk_file], :disk_type => spec[:disk_type], :disk_size => spec[:disk_size], :datastore => spec[:datastore])) unless @free_license VM.wrap(@_datacenter.vmFolder.CreateVM_Task(:config => vm_cfg, :pool => @_datacenter.hostFolder.children.first.resourcePool).wait_for_completion,self) else gem_root = Gem::Specification.find_by_name("esx").gem_dir template_path = File.join(gem_root, 'templates', 'vmx_template.erb') spec[:guest_id] = convert_guest_id_for_vmdk(spec[:guest_id]) erb = ERB.new File.read(template_path) vmx = erb.result binding tmp_vmx = Tempfile.new 'vmx' tmp_vmx.write vmx tmp_vmx.close ds = spec[:datastore]||'datastore1' ds = ds.gsub('[','').gsub(']','') vmx_path = "/vmfs/volumes/#{ds}/#{spec[:vm_name]}/#{spec[:vm_name]}.vmx" remote_command "mkdir -p #{File.dirname(vmx_path)}" upload_file tmp_vmx.path, vmx_path remote_command "vim-cmd solo/registervm #{vmx_path}" VM.wrap(@_datacenter.find_vm(spec[:vm_name]),self) end end
# File lib/esx.rb, line 410 def datacenter @_datacenter end
Return a list of ESX::Datastore
objects available in this host
# File lib/esx.rb, line 98 def datastores datastores = [] @_host.datastore.each do |ds| datastores << Datastore.wrap(ds) end datastores end
Expects fooimg.vmdk Trims path if /path/to/fooimg.vmdk
# File lib/esx.rb, line 318 def delete_template(template_disk) Log.debug "deleting template #{template_disk}" template = File.join(@templates_dir, File.basename(template_disk)) template_flat = File.join(@templates_dir, File.basename(template_disk, ".vmdk") + "-flat.vmdk") Net::SSH.start(@address, @user, :password => @password) do |ssh| if (ssh.exec! "ls #{template} 2>/dev/null").nil? Log.error "Template #{template_disk} does not exist" raise "Template does not exist" end ssh.exec!("rm -f #{template} && rm -f #{template_flat} 2>&1") end end
Return product info as an array of strings containing
fullName, apiType, apiVersion, osType, productLineId, vendor, version
# File lib/esx.rb, line 231 def host_info [ @_host.summary.config.product.fullName, @_host.summary.config.product.apiType, @_host.summary.config.product.apiVersion, @_host.summary.config.product.osType, @_host.summary.config.product.productLineId, @_host.summary.config.product.vendor, @_host.summary.config.product.version ] end
Imports a VMDK
if params has :use_template => true, the disk is saved as a template in @templates_dir and cloned from there.
Destination directory must exist otherwise rises exception
# File lib/esx.rb, line 371 def import_disk(source, destination, print_progress = false, params = {}) use_template = params[:use_template] || false if use_template Log.debug "import_disk :use_template => true" if !template_exist?(source) Log.debug "import_disk, template does not exist, importing." import_template(source, { :print_progress => print_progress }) end copy_from_template(source, destination) else import_disk_convert source, destination, print_progress end end
This method does all the heavy lifting when importing the disk. It also converts the imported VMDK to thin format
# File lib/esx.rb, line 389 def import_disk_convert(source, destination, print_progress = false) tmp_dest = destination + ".tmp" Net::SSH.start(@address, @user, :password => @password) do |ssh| if not (ssh.exec! "ls #{destination} 2>/dev/null").nil? raise Exception.new("Destination file #{destination} already exists") end Log.info "Uploading file... (#{File.basename(source)})" if print_progress ssh.scp.upload!(source, tmp_dest) do |ch, name, sent, total| if print_progress print "\rProgress: #{(sent.to_f * 100 / total.to_f).to_i}%" end end if print_progress Log.info "Converting disk..." ssh.exec "vmkfstools -i #{tmp_dest} --diskformat thin #{destination}; rm -f #{tmp_dest}" else ssh.exec "vmkfstools -i #{tmp_dest} --diskformat thin #{destination} >/dev/null 2>&1; rm -f #{tmp_dest}" end end end
# File lib/esx.rb, line 331 def import_template(source, params = {}) print_progress = params[:print_progress] || false dest_file = File.join(@templates_dir, File.basename(source)) Log.debug "Importing template #{source} to #{dest_file}" return dest_file if template_exist?(dest_file) Net::SSH.start(@address, @user, :password => @password) do |ssh| if (ssh.exec! "ls -la #{@templates_dir} 2>/dev/null").nil? # Create template dir Log.debug "Creating templates dir #{@templates_dir}" ssh.exec "mkdir -p #{@templates_dir}" end end import_disk_convert(source, dest_file, print_progress) end
# File lib/esx.rb, line 303 def list_templates templates = [] Net::SSH.start(@address, @user, :password => @password) do |ssh| output = (ssh.exec! "ls -l #{@templates_dir}/*-flat.vmdk 2>/dev/null") output.each_line do |t| templates << t.gsub(/-flat\.vmdk/,".vmdk").split().last.strip.chomp rescue next end unless output.nil? end templates end
Host
memory size in bytes
returns a Fixnum
# File lib/esx.rb, line 67 def memory_size @_host.hardware.memorySize.to_i end
Host
memory usage in bytes
returns a Fixnum
# File lib/esx.rb, line 91 def memory_usage @_host.summary.quickStats.overallMemoryUsage * 1024 * 1024 end
Returns the name of this host
# File lib/esx.rb, line 59 def name @_host.summary.config.name end
Power state of this host
poweredOn, poweredOff
# File lib/esx.rb, line 83 def power_state @_host.summary.runtime.powerState end
Run a command in the ESX
host via SSH
# File lib/esx.rb, line 269 def remote_command(cmd) output = "" Net::SSH.start(@address, @user, :password => @password) do |ssh| output = ssh.exec! cmd end output end
# File lib/esx.rb, line 291 def template_exist?(vmdk_file) template_file = File.join(@templates_dir, File.basename(vmdk_file)) Log.debug "Checking if template #{template_file} exists" Net::SSH.start(@address, @user, :password => @password) do |ssh| return false if (ssh.exec! "ls -la #{@templates_dir} 2>/dev/null").nil? return false if (ssh.exec! "ls #{template_file} 2>/dev/null").nil? end Log.debug "Template #{template_file} found" true end
Upload file
# File lib/esx.rb, line 280 def upload_file(source, dest, print_progress = false) Net::SSH.start(@address, @user, :password => @password) do |ssh| Log.info "Uploading file #{File.basename(source)}..." if print_progress ssh.scp.upload!(source, dest) do |ch, name, sent, total| if print_progress print "\rProgress: #{(sent.to_f * 100 / total.to_f).to_i}% completed" end end end end
# File lib/esx.rb, line 51 def vim=(vim) @_vim = vim @_datacenter = @_vim.serviceInstance.find_datacenter @_host = @_datacenter.hostFolder.children.first.host.first end
Return a list of VM
available in the inventory * recursive search of folders as well
# File lib/esx.rb, line 245 def virtual_machines vms = [] vm = @_datacenter.vmFolder vms = recursive_virtual_machine(vm,vms) vms end
Private Instance Methods
# File lib/esx.rb, line 460 def convert_guest_id_for_vmdk(guest_id) exceptions = { "winLonghornGuest" => "longhorn", "winLonghorn64Guest" => "longhorn-64", } return exceptions[guest_id] if exceptions[guest_id] guest_id.downcase.gsub(/guest/, '').gsub(/_?64/, '-64') end
disk_file datastore disk_size disk_type
# File lib/esx.rb, line 421 def create_disk_spec(params) disk_type = params[:disk_type] || :flat disk_file = params[:disk_file] if disk_type == :sparse and disk_file.nil? raise Exception.new("Creating sparse disks in ESX is not supported. Use an existing image.") end disk_size = params[:disk_size] datastore = params[:datastore] datastore = datastore + " #{disk_file}" if not disk_file.nil? spec = {} if disk_type == :sparse spec = { :operation => :add, :device => RbVmomi::VIM.VirtualDisk( :key => 0, :backing => RbVmomi::VIM.VirtualDiskSparseVer2BackingInfo( :fileName => datastore, :diskMode => :persistent), :controllerKey => 1000, :unitNumber => 0, :capacityInKB => disk_size) } else spec = { :operation => :add, :device => RbVmomi::VIM.VirtualDisk( :key => 0, :backing => RbVmomi::VIM.VirtualDiskFlatVer2BackingInfo( :fileName => datastore, :diskMode => :persistent), :controllerKey => 1000, :unitNumber => 0, :capacityInKB => disk_size) } end spec[:fileOperation] = :create if disk_file.nil? spec end
# File lib/esx.rb, line 253 def recursive_virtual_machine(parentObject,array) vm = parentObject.childEntity.each do |x| if x.to_s.match("Folder") array = recursive_virtual_machine(x,array) else array << VM.wrap(x,self) end end array end