class ESX::Host

Attributes

address[R]
free_license[R]
password[R]
templates_dir[RW]
user[R]

Public Class Methods

connect(host, user, password, insecure = true, opts = {}) click to toggle source

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
new(address, user, password, opts = {}) click to toggle source
# 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

copy_from_template(template_disk, destination) click to toggle source

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
cpu_cores() click to toggle source

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
create_net_dev(nic_id, spec) click to toggle source
# 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_vm(specification) click to toggle source

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
datacenter() click to toggle source
# File lib/esx.rb, line 410
def datacenter
  @_datacenter
end
datastores() click to toggle source

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
delete_template(template_disk) click to toggle source

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
has_template?(vmdk_file)
Alias for: template_exist?
host_info() click to toggle source

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
import_disk(source, destination, print_progress = false, params = {}) click to toggle source

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
import_disk_convert(source, destination, print_progress = false) click to toggle source

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
import_template(source, params = {}) click to toggle source
# 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
list_templates() click to toggle source
# 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
memory_size() click to toggle source

Host memory size in bytes

returns a Fixnum

# File lib/esx.rb, line 67
def memory_size
  @_host.hardware.memorySize.to_i
end
memory_usage() click to toggle source

Host memory usage in bytes

returns a Fixnum

# File lib/esx.rb, line 91
def memory_usage
  @_host.summary.quickStats.overallMemoryUsage * 1024 * 1024
end
name() click to toggle source

Returns the name of this host

# File lib/esx.rb, line 59
def name
  @_host.summary.config.name
end
power_state() click to toggle source

Power state of this host

poweredOn, poweredOff

# File lib/esx.rb, line 83
def power_state
  @_host.summary.runtime.powerState
end
remote_command(cmd) click to toggle source

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
template_exist?(vmdk_file) click to toggle source
# 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
Also aliased as: has_template?
upload_file(source, dest, print_progress = false) click to toggle source

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
vim=(vim) click to toggle source
# File lib/esx.rb, line 51
def vim=(vim)
  @_vim = vim
  @_datacenter = @_vim.serviceInstance.find_datacenter
  @_host = @_datacenter.hostFolder.children.first.host.first
end
virtual_machines() click to toggle source

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

convert_guest_id_for_vmdk(guest_id) click to toggle source
# 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
create_disk_spec(params) click to toggle source

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
recursive_virtual_machine(parentObject,array) click to toggle source
# 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