bin/esx in esx-0.1.1 vs bin/esx in esx-0.2
- old
+ new
@@ -1,62 +1,197 @@
#!/usr/bin/env ruby
require 'rubygems'
require 'esx'
require 'terminal-table/import'
+require 'clamp'
+require 'net/http'
+require 'fileutils'
-host_addr = ARGV[0]
-
-if host_addr.nil?
- $stderr.puts 'Usage: esx HOST'
- exit 1
+class BaseCommand < Clamp::Command
+ parameter "ADDRESS", "ESX host address"
+ option "--user", "USER", "Username", :default => "root"
+ option "--password", "PASSWORD", "Password", :default => ""
+ option "--debug", :flag, "Print debugging info"
end
-print "Username: "
-user = $stdin.gets.strip.chomp
+class InfoCommand < BaseCommand
+ parameter "ADDRESS", "ESX host address"
+ option "--user", "USER", "Username", :default => "root"
+ option "--password", "PASSWORD", "Password", :default => ""
-system "stty -echo"
-print "Password: "
-pass = $stdin.gets.strip.chomp
-system "stty echo"
-puts
+ def execute
+ begin
-begin
+ host = ESX::Host.connect(address, user, password)
- host = ESX::Host.connect(host_addr, user, pass)
+ puts
+ name = host.name.upcase
+ puts "*" * name.size
+ puts name
+ puts "*" * name.size
+ puts "Memory Size: %s GB" % host.memory_size.bytes.to.gigabytes.to_f.round
+ puts "Memory Usage: %s GB" % host.memory_usage.bytes.to.gigabytes.to_f.round
+ puts "Cpu Cores: %s" % host.cpu_cores
+ puts "Power State: %s" % host.power_state
+ puts "Hosted VMs: %s" % host.virtual_machines.size
+ puts "Running VMs: %s" % (host.virtual_machines.find_all{ |vm| vm.power_state == 'poweredOn' }).size
- puts
- name = host.name.upcase
- puts "*" * name.size
- puts name
- puts "*" * name.size
- puts "Memory Size: %s" % host.memory_size.bytes.to.megabytes.to_i
- puts "Memory Usage: %s" % host.memory_usage.bytes.to.megabytes.to_i
- puts "Cpu Cores: %s" % host.cpu_cores
- puts "Power State: %s" % host.power_state
- puts "\nVirtual Machines:"
- user_table = table do |t|
- t.headings = "NAME","MEMORY","CPUS","NICS","DISKS"
- host.virtual_machines.each do |vm|
- t << [vm.name,vm.memory_size.bytes.to.megabytes.to_i, vm.cpus, vm.ethernet_cards_number, vm.virtual_disks_number]
+ if not host.virtual_machines.empty?
+ puts "\nVirtual Machines:"
+ user_table = table do |t|
+ t.headings = "NAME","MEMORY","CPUS","NICS","DISKS", "STATE"
+ host.virtual_machines.each do |vm|
+ t << [vm.name,vm.memory_size.bytes.to.megabytes.to_i, vm.cpus, vm.ethernet_cards_number, vm.virtual_disks_number, vm.power_state]
+ end
+ end
+ puts user_table
+ end
+
+ puts "\nDatastores:"
+ user_table = table do |t|
+ t.headings = "NAME","CAPACITY","FREESPACE","ACCESIBLE","TYPE","URL"
+ host.datastores.each do |ds|
+ dsname = ds.name
+ if dsname.size > 20
+ dsname = dsname[0..19] + '...'
+ end
+ t << [dsname,ds.capacity, ds.free_space, ds.datastore_type, ds.accessible, ds.url]
+ end
+ end
+ puts user_table
+ puts
+
+ rescue Exception => e
+ puts "Error connecting to the ESX host"
+ puts "\n#{e.message}"
+ if debug?
+ puts e.backtrace
+ end
end
end
- puts user_table
- puts "\nDatastores:"
- user_table = table do |t|
- t.headings = "NAME","CAPACITY","FREESPACE","ACCESIBLE","TYPE","URL"
- host.datastores.each do |ds|
- dsname = ds.name
- if dsname.size > 20
- dsname = dsname[0..19] + '...'
+end
+
+class CreateVMCommand < BaseCommand
+
+ parameter "ADDRESS", "ESX host address"
+ option "--disk-file", "DISK_FILE", "VMDK file to import", :attribute_name => :disk_file
+ option "--guest-id", "GUEST_ID", "GuestID value", :attribute_name => :guest_id, :default => 'otherGuest'
+ option "--name", "VM NAME", "Virtual Machine name (required)"
+ option "--memory", "MEMORY", "VM Memory size in MB", :default => 512
+ option "--tmpdir", "TMP DIR", "tmp dir used to download files", :default => "/tmp"
+ option "--datastore", "DATASTORE", "Datastore used to host the disk", :default => "datastore1"
+ option "--poweron", :flag, "Power on the VM after creation"
+
+ def execute
+ begin
+ host = ESX::Host.connect address, user, password
+ rescue Exception => e
+ $stderr.puts "Can't connect to the host #{address}."
+ if debug?
+ $stderr.puts e.message
end
- t << [dsname,ds.capacity, ds.free_space, ds.datastore_type, ds.accessible, ds.url]
+ exit 1
end
+ downloaded_file = nil
+ df = disk_file.dup
+ if df.strip.chomp =~ /^http/
+ begin
+ downloaded_file = disk_file.dup
+ tmpfile = "#{tmpdir}/#{Time.now.to_i}.esx"
+ puts "Downloading file... (#{tmpfile})"
+ download! downloaded_file, tmpfile
+ puts
+ df = tmpfile
+ rescue Exception => e
+ FileUtils.rm_f(tmpfile)
+ $stderr.puts "Error downloading file from #{downloaded_file}."
+ $stderr.puts e.message if debug?
+ exit 1
+ end
+ end
+ raise Exception.new("Invalid disk file") if not File.exist?(df)
+ if not name
+ $stderr.puts "Invalid VM name."
+ $stderr.puts "Use --name option to specify the VM name"
+ exit 1
+ end
+ host.remote_command "mkdir /vmfs/volumes/#{datastore}/#{name}"
+
+ begin
+ host.import_disk df, "/vmfs/volumes/#{datastore}/#{name}/#{name}.vmdk"
+ rescue Exception => e
+ $stderr.puts "Error uploading file to /vmfs/volumes/#{datastore}/#{name}/#{name}.vmdk"
+ $stderr.puts e.message if debug?
+ exit 1
+ end
+
+ if not downloaded_file.nil?
+ puts "Deleting tmp file #{df}" if debug?
+ FileUtils.rm_f(df)
+ end
+ vm = host.create_vm :vm_name => name,
+ :disk_file => "#{name}/#{name}.vmdk",
+ :datastore => datastore, :disk_type => :flat, :memory => memory,
+ :guest_id => guest_id
+ if poweron?
+ vm.power_on
+ end
end
- puts user_table
- puts
-rescue Exception => e
- puts "Error connecting to the ESX host"
- puts "\n#{e.message}"
- puts e.backtrace
+ def report_progress(progress, total, show_parts=true)
+ line_reset = "\r\e[0K"
+ percent = (progress.to_f / total.to_f) * 100
+ line = "Progress: #{percent.to_i}%"
+ line << " (#{progress} / #{total})" if show_parts
+ line = "#{line_reset}#{line}"
+ $stdout.sync = true
+ $stdout.print line
+ end
+
+ def download!(source_url, destination_file)
+ dst = File.open(destination_file, 'w')
+ proxy_uri = URI.parse(ENV["http_proxy"] || "")
+ uri = URI.parse(source_url)
+ http = Net::HTTP.new(uri.host, uri.port, proxy_uri.host, proxy_uri.port, proxy_uri.user, proxy_uri.password)
+
+ if uri.scheme == "https"
+ http.use_ssl = true
+ http.verify_mode = OpenSSL::SSL::VERIFY_NONE
+ end
+
+ res = http.start do |h|
+ h.request_get(uri.request_uri) do |response|
+ total = response.content_length
+ progress = 0
+ segment_count = 0
+
+ response.read_body do |segment|
+ # Report the progress out
+ progress += segment.length
+ segment_count += 1
+
+ # Progress reporting is limited to every 25 segments just so
+ # we're not constantly updating
+ if segment_count % 25 == 0
+ report_progress(progress, total)
+ segment_count = 0
+ end
+ # Store the segment
+ dst.write(segment)
+ end
+ end
+ end
+
+ raise Exception.new("HTTP Error #{res}") if res.class != Net::HTTPOK
+ ensure
+ dst.close
+ end
+
end
+
+class DefaultCommand < Clamp::Command
+ default_subcommand "info", "Display host info", InfoCommand
+ subcommand "create-vm", "Create a VM", CreateVMCommand
+end
+
+DefaultCommand.run