lib/esx.rb in esx-0.4.1 vs lib/esx.rb in esx-0.4.2

- old
+ new

@@ -1,15 +1,16 @@ require 'rubygems' require 'rbvmomi' require 'alchemist' require 'net/scp' require 'net/ssh' +require 'erb' module ESX - VERSION = '0.4.1' - + VERSION = '0.4.2' + if !defined? Log or Log.nil? Log = Logger.new($stdout) Log.formatter = proc do |severity, datetime, progname, msg| "[ESX] #{severity}: #{msg}\n" end @@ -18,28 +19,32 @@ Log.debug "Initializing logger" end class Host - attr_reader :address, :user, :password + attr_reader :address, :user, :password, :free_license attr_accessor :templates_dir 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 # Connect to a ESX host # # Requires hostname/ip, username and password # # Host connection is insecure by default - def self.connect(host, user, password, insecure=true) + def self.connect(host, user, password, insecure = true, opts = {}) vim = RbVmomi::VIM.connect :host => host, :user => user, :password => password, :insecure => insecure - host = Host.new(host, user,password) + host = Host.new(host, user,password, opts) host.vim = vim host end def vim=(vim) @@ -75,20 +80,20 @@ # poweredOn, poweredOff # def power_state @_host.summary.runtime.powerState end - - # Host memory usage in bytes - # + + # Host memory usage in bytes + # # returns a Fixnum # def memory_usage @_host.summary.quickStats.overallMemoryUsage * 1024 * 1024 end - + # Return a list of ESX::Datastore objects available in this host # def datastores datastores = [] @_host.datastore.each do |ds| @@ -96,11 +101,11 @@ end datastores end # Create a Virtual Machine - # + # # Requires a Hash with the following keys: # # { # :vm_name => name, (string, required) # :cpus => 1, #(int, optional) @@ -110,11 +115,11 @@ # :datastore => datastore1 #(string, optional) # :disk_file => path to vmdk inside datastore (optional) # :disk_type => flat, sparse (default flat) # } # - # supported guest_id list: + # supported guest_id list: # http://pubs.vmware.com/vsphere-50/index.jsp?topic=/com.vmware.wssdk.apiref.doc_50/vim.vm.GuestOsDescriptor.GuestOsIdentifier.html # # Default values above. def create_vm(specification) spec = specification @@ -151,32 +156,48 @@ :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], + 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])) - VM.wrap(@_datacenter.vmFolder.CreateVM_Task(:config => vm_cfg, :pool => @_datacenter.hostFolder.children.first.resourcePool).wait_for_completion) + 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') + 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" + 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 def create_net_dev(nic_id, spec) h = { :key => nic_id, @@ -197,14 +218,14 @@ end h end # Return product info as an array of strings containing - # + # # fullName, apiType, apiVersion, osType, productLineId, vendor, version - # - def host_info + # + def host_info [ @_host.summary.config.product.fullName, @_host.summary.config.product.apiType, @_host.summary.config.product.apiVersion, @_host.summary.config.product.osType, @@ -213,15 +234,15 @@ @_host.summary.config.product.version ] end # Return a list of VM available in the inventory - # + # def virtual_machines vms = [] - vm = @_datacenter.vmFolder.childEntity.each do |x| - vms << VM.wrap(x) + vm = @_datacenter.vmFolder.childEntity.each do |x| + vms << VM.wrap(x,self) end vms end # @@ -281,12 +302,12 @@ 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 + raise "Template does not exist" + end ssh.exec!("rm -f #{template} && rm -f #{template_flat} 2>&1") end end def import_template(source, params = {}) @@ -294,11 +315,11 @@ 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 + # 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) @@ -324,11 +345,11 @@ # # 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 # def import_disk(source, destination, print_progress = false, params = {}) use_template = params[:use_template] || false if use_template @@ -365,11 +386,15 @@ else ssh.exec "vmkfstools -i #{tmp_dest} --diskformat thin #{destination} >/dev/null 2>&1; rm -f #{tmp_dest}" end end end - + + def datacenter + @_datacenter + end + private # # disk_file # datastore # disk_size @@ -383,11 +408,11 @@ end disk_size = params[:disk_size] datastore = params[:datastore] datastore = datastore + " #{disk_file}" if not disk_file.nil? spec = {} - if disk_type == :sparse + if disk_type == :sparse spec = { :operation => :add, :device => RbVmomi::VIM.VirtualDisk( :key => 0, :backing => RbVmomi::VIM.VirtualDiskSparseVer2BackingInfo( @@ -415,56 +440,75 @@ end end class VM - attr_accessor :memory_size, :cpus, :ethernet_cards_number - attr_accessor :name, :virtual_disks_number, :vm_object + attr_accessor :memory_size, :cpus, :ethernet_cards_number, :vmid + attr_accessor :name, :virtual_disks_number, :vm_object, :host # Wraps a RbVmomi::VirtualMachine object # # **This method should never be called manually.** # - def self.wrap(vm) + def self.wrap(vm,host) _vm = VM.new _vm.name = vm.name ## HACK: for some reason vm.summary.config.memorySizeMB returns nil # under some conditions _vm.memory_size = vm.summary.config.memorySizeMB*1024*1024 rescue 0 _vm.cpus = vm.summary.config.numCpu - _vm.ethernet_cards_number = vm.summary.config.numEthernetCards + _vm.ethernet_cards_number = vm.summary.config.numEthernetCards _vm.virtual_disks_number = vm.summary.config.numVirtualDisks _vm.vm_object = vm + _vm.host = host + _vm.vmid = vm.to_s.scan(/\"([0-9]+)\"/).flatten.first _vm end # Returns the state of the VM as a string # 'poweredOff', 'poweredOn' - # + # def power_state vm_object.summary.runtime.powerState end # Power On a VM def power_on - vm_object.PowerOnVM_Task.wait_for_completion + unless host.free_license + vm_object.PowerOnVM_Task.wait_for_completion + else + host.remote_command "vim-cmd vmsvc/power.on #{vmid}" + end end # Power Off a VM def power_off - vm_object.PowerOffVM_Task.wait_for_completion + unless host.free_license + vm_object.PowerOffVM_Task.wait_for_completion + else + host.remote_command "vim-cmd vmsvc/power.off #{vmid}" + end end # Destroy the VirtualMaching removing it from the inventory # and deleting the disk files def destroy #disks = vm_object.config.hardware.device.grep(RbVmomi::VIM::VirtualDisk) - vm_object.Destroy_Task.wait_for_completion + unless host.free_license + vm_object.Destroy_Task.wait_for_completion + else + host.remote_command "vim-cmd vmsvc/power.off #{vmid}" + host.remote_command "vim-cmd vmsvc/destroy #{vmid}" + end end def reset - vm_object.ResetVM_Task.wait_for_completion + unless host.free_license + vm_object.ResetVM_Task.wait_for_completion + else + host.remote_command "vim-cmd vmsvc/power.reset #{vmid}" + end end def guest_info GuestInfo.wrap(vm_object.guest) end @@ -485,11 +529,11 @@ end end class NetworkInterface - + attr_accessor :_wrapped_object # Accepts VirtualEthernetCard and GuestNicInfo objects def self.wrap(obj) ni = NetworkInterface.new @@ -507,11 +551,11 @@ _wrapped_object.ipAddress.first end end def mac - _wrapped_object.macAddress + _wrapped_object.macAddress end end class GuestInfo @@ -525,10 +569,10 @@ end def ip_address _wrapped_object.ipAddress end - + def nics n = [] _wrapped_object.net.each do |nic| n << NetworkInterface.wrap(nic) end