esx 0.4.1 → 0.4.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -8,17 +8,20 @@
8
8
 
9
9
  * Define a Logger object
10
10
 
11
+ ```
11
12
  ESX::Log.info "foobar"
13
+ ```
12
14
 
13
15
  * Added some debugging with Log.debug
14
-
15
16
  * ESX::Host now has a *templates_dir* attribute and supports uploading and cloning templates:
16
17
 
18
+ ```
17
19
  host = ESX::Host.connect 'my-esx-host',
18
20
  'root',
19
21
  'password'
20
22
 
21
23
  host.import_template "/path/to/template.vmdk"
24
+ ```
22
25
 
23
26
  This will copy the "template.vmdk" file to the default templates_dir in ESX. Default templates dir is "/vmfs/volumes/datastore1/esx-gem/templates".
24
27
 
@@ -26,13 +29,18 @@ The template is automatically converted to VMDK thin format.
26
29
 
27
30
  Using the template:
28
31
 
32
+ ```
29
33
  host.copy_from_template "template.vmdk", "/vmfs/volumes/datastore1/foo.vmdk"
30
34
 
35
+ ````
36
+
31
37
  Sorter version:
32
38
 
39
+ ```
33
40
  host.import_disk "/path/to/local/template.vmdk", # local file
34
41
  "/vmfs/volumes/datastore1/foo.vmdk", # remote path in ESX
35
42
  { :use_template => true }
43
+ ```
36
44
 
37
45
  If the template "template.vmdk" is found, use that. Otherwise import the disk, save it as a template and clone the template to "/vmfs/volumes/datastore1/foo.vmdk"
38
46
 
data/Gemfile CHANGED
@@ -6,8 +6,12 @@ source "http://rubygems.org"
6
6
  # Add dependencies to develop your gem here.
7
7
  # Include everything needed to run rake, tests, features, etc.
8
8
  group :development do
9
+ gem "net-ssh"
10
+ gem "net-scp"
11
+ gem "rbvmomi"
12
+ gem "alchemist"
9
13
  gem "rspec", ">= 0"
10
- gem "bundler", "~> 1.0.0"
14
+ gem "bundler", "> 1.0.0"
11
15
  gem "jeweler"
12
16
  gem "simplecov", ">= 0"
13
17
  end
@@ -0,0 +1,51 @@
1
+ GEM
2
+ remote: http://rubygems.org/
3
+ specs:
4
+ alchemist (0.1.3)
5
+ builder (3.0.0)
6
+ diff-lcs (1.1.3)
7
+ git (1.2.5)
8
+ jeweler (1.8.4)
9
+ bundler (~> 1.0)
10
+ git (>= 1.2.5)
11
+ rake
12
+ rdoc
13
+ json (1.7.5)
14
+ multi_json (1.3.6)
15
+ net-scp (1.0.4)
16
+ net-ssh (>= 1.99.1)
17
+ net-ssh (2.5.2)
18
+ nokogiri (1.5.5)
19
+ rake (0.9.2.2)
20
+ rbvmomi (1.5.1)
21
+ builder
22
+ nokogiri (>= 1.4.1)
23
+ trollop
24
+ rdoc (3.12)
25
+ json (~> 1.4)
26
+ rspec (2.11.0)
27
+ rspec-core (~> 2.11.0)
28
+ rspec-expectations (~> 2.11.0)
29
+ rspec-mocks (~> 2.11.0)
30
+ rspec-core (2.11.1)
31
+ rspec-expectations (2.11.2)
32
+ diff-lcs (~> 1.1.3)
33
+ rspec-mocks (2.11.2)
34
+ simplecov (0.6.4)
35
+ multi_json (~> 1.0)
36
+ simplecov-html (~> 0.5.3)
37
+ simplecov-html (0.5.3)
38
+ trollop (1.16.2)
39
+
40
+ PLATFORMS
41
+ ruby
42
+
43
+ DEPENDENCIES
44
+ alchemist
45
+ bundler (> 1.0.0)
46
+ jeweler
47
+ net-scp
48
+ net-ssh
49
+ rbvmomi
50
+ rspec
51
+ simplecov
data/Rakefile CHANGED
@@ -12,7 +12,7 @@ Jeweler::Tasks.new do |gem|
12
12
  gem.summary = %Q{Simple RbVmomi wrapper library to manage VMWare ESX hosts}
13
13
  gem.description = %Q{Manage VMWare ESX hosts with ease}
14
14
  gem.email = "rubiojr@frameos.org"
15
- gem.authors = ["Sergio Rubio"]
15
+ gem.authors = ["Sergio Rubio","Massimo Maino"]
16
16
  # Include your dependencies below. Runtime dependencies are required when using your gem,
17
17
  # and development dependencies are only needed for development (ie running rake tasks, tests, etc)
18
18
  gem.add_runtime_dependency 'alchemist'
@@ -22,6 +22,7 @@ Jeweler::Tasks.new do |gem|
22
22
  gem.add_runtime_dependency 'net-scp'
23
23
  gem.add_runtime_dependency 'clamp'
24
24
  gem.files.exclude 'spec/data/*'
25
+ gem.files.include 'templates/*'
25
26
  # gem.add_development_dependency 'rspec', '> 1.2.3'
26
27
  end
27
28
  Jeweler::RubygemsDotOrgTasks.new
data/bin/esx CHANGED
@@ -5,12 +5,14 @@ require 'terminal-table/import'
5
5
  require 'clamp'
6
6
  require 'net/http'
7
7
  require 'fileutils'
8
+ require 'json'
8
9
 
9
10
  class BaseCommand < Clamp::Command
10
11
  parameter "ADDRESS", "ESX host address"
11
12
  option "--user", "USER", "Username", :default => "root"
12
13
  option "--password", "PASSWORD", "Password", :default => ""
13
14
  option "--debug", :flag, "Print debugging info"
15
+ option "--free-license", :flag, "If your Hypervisor have a free license", :default => false
14
16
  end
15
17
 
16
18
  class InfoCommand < BaseCommand
@@ -22,7 +24,7 @@ class InfoCommand < BaseCommand
22
24
  def execute
23
25
  begin
24
26
 
25
- host = ESX::Host.connect(address, user, password)
27
+ host = ESX::Host.connect(address, user, password, true, {:free_license=>free_license?})
26
28
 
27
29
  puts
28
30
  name = host.name.upcase
@@ -78,9 +80,9 @@ class DestroyVMCommand < BaseCommand
78
80
 
79
81
  def execute
80
82
  begin
81
- host = ESX::Host.connect address, user, password
83
+ host = ESX::Host.connect address, user, password, true, {:free_license=>free_license?}
82
84
  host.virtual_machines.each do |vm|
83
- if vm.name == vm_name
85
+ if vm.name == vm_name
84
86
  print "Destroying Virtual Machine '#{vm.name}'..."
85
87
  vm.power_off unless vm.power_state == 'poweredOff'
86
88
  vm.destroy
@@ -105,7 +107,7 @@ class PowerOffVMCommand < BaseCommand
105
107
 
106
108
  def execute
107
109
  begin
108
- host = ESX::Host.connect address, user, password
110
+ host = ESX::Host.connect address, user, password, true, {:free_license=>free_license?}
109
111
  host.virtual_machines.each do |vm|
110
112
  if vm.name == vm_name and vm.power_state == 'poweredOn'
111
113
  print "Powering Off VM #{vm_name}... "
@@ -131,7 +133,7 @@ class PowerOnVMCommand < BaseCommand
131
133
 
132
134
  def execute
133
135
  begin
134
- host = ESX::Host.connect address, user, password
136
+ host = ESX::Host.connect address, user, password, true, {:free_license=>free_license?}
135
137
  host.virtual_machines.each do |vm|
136
138
  if vm.name == vm_name and vm.power_state != 'poweredOn'
137
139
  print "Powering On VM #{vm_name}... "
@@ -150,6 +152,135 @@ class PowerOnVMCommand < BaseCommand
150
152
 
151
153
  end
152
154
 
155
+ class DeleteNICCommand < BaseCommand
156
+ parameter "ADDRESS", "ESX host address"
157
+ option "--vm-name", "VM_NAME", "Virtual Machine"
158
+ option "--mac-address", "MAC_ADDRESS", "MAC address to remove"
159
+ option "--force", :flag, "Force reboot"
160
+
161
+ def execute
162
+ begin
163
+ host = ESX::Host.connect address, user, password, true, {:free_license=>free_license?}
164
+
165
+ vm = host.virtual_machines.find{|x| x.name.eql? vm_name}
166
+ if vm
167
+ hd=vm.vm_object.config.hardware.device.grep(RbVmomi::VIM::VirtualEthernetCard).find{|nic| nic.props[:macAddress].eql?mac_address}
168
+ if hd
169
+ spec = RbVmomi::VIM.VirtualMachineConfigSpec({:deviceChange => [{:operation => :remove,:device => hd}]})
170
+ unless free_license?
171
+ vm.vm_object.ReconfigVM_Task(:spec => spec).wait_for_completion
172
+ else
173
+ #TODO: This is horrible! Find a way to reconfigure the VM without downtime
174
+ key = hd.key
175
+ vmx_file = vm.vm_object.config.files.vmPathName.gsub("[","/vmfs/volumes/").gsub(/]\s*/,"/")
176
+ puts "VMX file: #{vmx_file}" if debug?
177
+ eth_name = host.remote_command("fgrep #{mac_address} #{vmx_file} | cut -d. -f1")
178
+ if eth_name
179
+ eth_name.strip!
180
+ puts "#{eth_name} found!" if debug?
181
+ host.remote_command "sed -i -e '/.*#{eth_name}\..*/d' #{vmx_file}"
182
+ end
183
+ if force?
184
+ vm.power_off
185
+ host.remote_command "vim-cmd vmsvc/unregister #{vm.vmid}"
186
+ id = host.remote_command "vim-cmd solo/registervm #{vmx_file}"
187
+ host.remote_command "vim-cmd vmsvc/power.on #{id}"
188
+ else
189
+ puts "You have to power-off the vm unregister it and register it agin."
190
+ end
191
+ end
192
+ puts "Done."
193
+ else
194
+ puts "No NIC with #{mac_address} mac address was found!"
195
+ end
196
+ else
197
+ puts "No Virtual Machine found!"
198
+ end
199
+ rescue Exception => e
200
+ $stderr.puts "Can't connect to the host #{address}."
201
+ if debug?
202
+ $stderr.puts e.message
203
+ end
204
+ exit 1
205
+ end
206
+ end
207
+ end
208
+
209
+
210
+ class AddNICCommand < BaseCommand
211
+ parameter "ADDRESS", "ESX host address"
212
+ option "--vm-name", "VM_NAME", "Virtual Machine"
213
+ option "--mac-address", "MAC_ADDRESS", "MAC address to add", :default => nil
214
+ option "--network", "NETWORK", "Network name for new interface"
215
+ option "--force", :flag, "Force reboot"
216
+
217
+ def execute
218
+ begin
219
+ host = ESX::Host.connect address, user, password, true, {:free_license=>free_license?}
220
+ vm = host.virtual_machines.find{|x| x.name.eql? vm_name}
221
+ if vm
222
+ hd = vm.vm_object.config.hardware.device.grep(RbVmomi::VIM::VirtualEthernetCard).find{|nic| nic.props[:macAddress].eql?mac_address}
223
+ s = vm.vm_object.config.hardware.device.grep(RbVmomi::VIM::VirtualEthernetCard).size
224
+ if hd
225
+ puts "NIC with #{mac_address} mac address already exists!"
226
+ else
227
+ if mac_address
228
+ nics = {:macAddress => mac_address, :addressType => 'manual'}
229
+ else
230
+ nics = {:addressType => 'generated'}
231
+ end
232
+ spec = RbVmomi::VIM.VirtualMachineConfigSpec({:deviceChange => [{
233
+ :operation => :add,
234
+ :device => RbVmomi::VIM.VirtualE1000({
235
+ :key => s,
236
+ :deviceInfo => {
237
+ :label => "Network Adapter #{s}",
238
+ :summary => network || 'VM Network'
239
+ },
240
+ :backing => RbVmomi::VIM.VirtualEthernetCardNetworkBackingInfo(
241
+ :deviceName => network || 'VM Network'
242
+ )
243
+ }.merge(nics))
244
+ }]})
245
+ unless free_license?
246
+ vm.vm_object.ReconfigVM_Task(:spec => spec).wait_for_completion
247
+ else
248
+ #TODO: This is horrible! Find a way to reconfigure the VM without downtime
249
+ vmx_file = vm.vm_object.config.files.vmPathName.gsub("[","/vmfs/volumes/").gsub(/]\s*/,"/")
250
+ puts "VMX file: #{vmx_file}" if debug?
251
+ host.remote_command " echo 'ethernet#{s}.present = \"true\"' >> #{vmx_file}"
252
+ host.remote_command " echo 'ethernet#{s}.virtualDev = \"e1000\"' >> #{vmx_file}"
253
+ host.remote_command " echo 'ethernet#{s}.wakeOnPcktRcv = \"false\"' >> #{vmx_file}"
254
+ host.remote_command " echo 'ethernet#{s}.networkName = \"#{network || 'VM Network'}\"' >> #{vmx_file}"
255
+ if mac_address
256
+ host.remote_command " echo 'ethernet#{s}.addressType = \"static\"' >> #{vmx_file}"
257
+ host.remote_command " echo 'ethernet#{s}.address = \"#{mac_address}\"' >> #{vmx_file}"
258
+ else
259
+ host.remote_command " echo 'ethernet#{s}.addressType = \"generated\"' >> #{vmx_file}"
260
+ end
261
+ if force?
262
+ vm.power_off
263
+ host.remote_command "vim-cmd vmsvc/unregister #{vm.vmid}"
264
+ id = host.remote_command "vim-cmd solo/registervm #{vmx_file}"
265
+ host.remote_command "vim-cmd vmsvc/power.on #{id}"
266
+ else
267
+ puts "You have to power-off the vm unregister it and register it agin."
268
+ end
269
+ end
270
+ puts "Done."
271
+ end
272
+ else
273
+ puts "No Virtual Machine found!"
274
+ end
275
+ rescue Exception => e
276
+ $stderr.puts "Can't connect to the host #{address}."
277
+ if debug?
278
+ $stderr.puts e.message
279
+ end
280
+ exit 1
281
+ end
282
+ end
283
+ end
153
284
 
154
285
  class CreateVMCommand < BaseCommand
155
286
 
@@ -167,7 +298,7 @@ class CreateVMCommand < BaseCommand
167
298
 
168
299
  def execute
169
300
  begin
170
- host = ESX::Host.connect address, user, password
301
+ host = ESX::Host.connect address, user, password, true, {:free_license=>free_license?}
171
302
  rescue Exception => e
172
303
  $stderr.puts "Can't connect to the host #{address}."
173
304
  if debug?
@@ -181,7 +312,7 @@ class CreateVMCommand < BaseCommand
181
312
  downloaded_file = nil
182
313
  if disk_file.nil?
183
314
  # if --disk-file nil? create the VM without disk
184
- vm = host.create_vm :vm_name => name,
315
+ vm = host.create_vm :vm_name => name,
185
316
  :datastore => datastore, :disk_type => :flat, :memory => memory,
186
317
  :disk_size => disk_size,
187
318
  :guest_id => guest_id, :nics => [{:mac_address => mac_address, :network => vm_network}]
@@ -196,7 +327,7 @@ class CreateVMCommand < BaseCommand
196
327
  puts
197
328
  df = tmpfile
198
329
  rescue Exception => e
199
- FileUtils.rm_f(tmpfile)
330
+ FileUtils.rm_f(tmpfile)
200
331
  $stderr.puts "Error downloading file from #{downloaded_file}."
201
332
  $stderr.puts e.message if debug?
202
333
  exit 1
@@ -220,10 +351,10 @@ class CreateVMCommand < BaseCommand
220
351
 
221
352
  if not downloaded_file.nil?
222
353
  puts "Deleting tmp file #{df}" if debug?
223
- FileUtils.rm_f(df)
354
+ FileUtils.rm_f(df)
224
355
  end
225
- vm = host.create_vm :vm_name => name,
226
- :disk_file => "#{name}/#{name}.vmdk",
356
+ vm = host.create_vm :vm_name => name,
357
+ :disk_file => "#{name}/#{name}.vmdk",
227
358
  :datastore => datastore, :disk_type => :flat, :memory => memory,
228
359
  :guest_id => guest_id, :nics => [{mac_address => mac_address, :network => vm_network}]
229
360
  end
@@ -233,7 +364,7 @@ class CreateVMCommand < BaseCommand
233
364
  end
234
365
 
235
366
  def report_progress(progress, total, show_parts=true)
236
- line_reset = "\r\e[0K"
367
+ line_reset = "\r\e[0K"
237
368
  percent = (progress.to_f / total.to_f) * 100
238
369
  line = "Progress: #{percent.to_i}%"
239
370
  line << " (#{progress} / #{total})" if show_parts
@@ -284,11 +415,14 @@ class CreateVMCommand < BaseCommand
284
415
  end
285
416
 
286
417
  class DefaultCommand < Clamp::Command
287
- default_subcommand "info", "Display host info", InfoCommand
418
+ self.default_subcommand="info"
419
+ subcommand "info", "Display host info", InfoCommand
288
420
  subcommand "create-vm", "Create a VM", CreateVMCommand
289
421
  subcommand "poweron-vm", "Power On a VM", PowerOnVMCommand
290
422
  subcommand "poweroff-vm", "Power Off a VM", PowerOffVMCommand
291
423
  subcommand "destroy-vm", "Destroy VM", DestroyVMCommand
424
+ subcommand "add-nic", "Add NIC to a VM", AddNICCommand
425
+ subcommand "delete-nic", "Delete NIC from a VM", DeleteNICCommand
292
426
  end
293
427
 
294
428
  begin
data/lib/esx.rb CHANGED
@@ -3,11 +3,12 @@ require 'rbvmomi'
3
3
  require 'alchemist'
4
4
  require 'net/scp'
5
5
  require 'net/ssh'
6
+ require 'erb'
6
7
 
7
8
  module ESX
8
9
 
9
- VERSION = '0.4.1'
10
-
10
+ VERSION = '0.4.2'
11
+
11
12
  if !defined? Log or Log.nil?
12
13
  Log = Logger.new($stdout)
13
14
  Log.formatter = proc do |severity, datetime, progname, msg|
@@ -20,7 +21,7 @@ module ESX
20
21
 
21
22
  class Host
22
23
 
23
- attr_reader :address, :user, :password
24
+ attr_reader :address, :user, :password, :free_license
24
25
  attr_accessor :templates_dir
25
26
 
26
27
  def initialize(address, user, password, opts = {})
@@ -28,6 +29,10 @@ module ESX
28
29
  @password = password
29
30
  @user = user
30
31
  @templates_dir = opts[:templates_dir] || "/vmfs/volumes/datastore1/esx-gem/templates"
32
+ @free_license = opts[:free_license] || false
33
+ if @free_license and !@user.eql?"root"
34
+ raise Exception.new("Can't use Free License mode with user #{@user}. Please use 'root' user.")
35
+ end
31
36
  end
32
37
 
33
38
  # Connect to a ESX host
@@ -35,9 +40,9 @@ module ESX
35
40
  # Requires hostname/ip, username and password
36
41
  #
37
42
  # Host connection is insecure by default
38
- def self.connect(host, user, password, insecure=true)
43
+ def self.connect(host, user, password, insecure = true, opts = {})
39
44
  vim = RbVmomi::VIM.connect :host => host, :user => user, :password => password, :insecure => insecure
40
- host = Host.new(host, user,password)
45
+ host = Host.new(host, user,password, opts)
41
46
  host.vim = vim
42
47
  host
43
48
  end
@@ -77,16 +82,16 @@ module ESX
77
82
  def power_state
78
83
  @_host.summary.runtime.powerState
79
84
  end
80
-
81
- # Host memory usage in bytes
82
- #
85
+
86
+ # Host memory usage in bytes
87
+ #
83
88
  # returns a Fixnum
84
89
  #
85
90
  def memory_usage
86
91
  @_host.summary.quickStats.overallMemoryUsage * 1024 * 1024
87
92
  end
88
93
 
89
-
94
+
90
95
  # Return a list of ESX::Datastore objects available in this host
91
96
  #
92
97
  def datastores
@@ -98,7 +103,7 @@ module ESX
98
103
  end
99
104
 
100
105
  # Create a Virtual Machine
101
- #
106
+ #
102
107
  # Requires a Hash with the following keys:
103
108
  #
104
109
  # {
@@ -112,7 +117,7 @@ module ESX
112
117
  # :disk_type => flat, sparse (default flat)
113
118
  # }
114
119
  #
115
- # supported guest_id list:
120
+ # supported guest_id list:
116
121
  # http://pubs.vmware.com/vsphere-50/index.jsp?topic=/com.vmware.wssdk.apiref.doc_50/vim.vm.GuestOsDescriptor.GuestOsIdentifier.html
117
122
  #
118
123
  # Default values above.
@@ -153,7 +158,7 @@ module ESX
153
158
  }
154
159
  ]
155
160
  }
156
-
161
+
157
162
  #Add multiple nics
158
163
  nics_count = 0
159
164
  if spec[:nics]
@@ -162,19 +167,35 @@ module ESX
162
167
  {
163
168
  :operation => :add,
164
169
  :device => RbVmomi::VIM.VirtualE1000(create_net_dev(nics_count, nic_spec))
165
-
170
+
166
171
  }
167
172
  )
168
173
  nics_count += 1
169
174
  end
170
175
  end
171
176
  # VMDK provided, replace the empty vmdk
172
- vm_cfg[:deviceChange].push(create_disk_spec(:disk_file => spec[:disk_file],
177
+ vm_cfg[:deviceChange].push(create_disk_spec(:disk_file => spec[:disk_file],
173
178
  :disk_type => spec[:disk_type],
174
179
  :disk_size => spec[:disk_size],
175
180
  :datastore => spec[:datastore]))
176
181
 
177
- VM.wrap(@_datacenter.vmFolder.CreateVM_Task(:config => vm_cfg, :pool => @_datacenter.hostFolder.children.first.resourcePool).wait_for_completion)
182
+ unless @free_license
183
+ VM.wrap(@_datacenter.vmFolder.CreateVM_Task(:config => vm_cfg, :pool => @_datacenter.hostFolder.children.first.resourcePool).wait_for_completion,self)
184
+ else
185
+ gem_root = Gem::Specification.find_by_name("esx").gem_dir
186
+ template_path = File.join(gem_root, 'templates', 'vmx_template.erb')
187
+ erb = ERB.new File.read(template_path)
188
+ vmx = erb.result binding
189
+ tmp_vmx = Tempfile.new 'vmx'
190
+ tmp_vmx.write vmx
191
+ tmp_vmx.close
192
+ ds = spec[:datastore]||'datastore1'
193
+ ds = ds.gsub('[','').gsub(']','')
194
+ vmx_path = "/vmfs/volumes/#{ds}/#{spec[:vm_name]}/#{spec[:vm_name]}.vmx"
195
+ upload_file tmp_vmx.path, vmx_path
196
+ remote_command "vim-cmd solo/registervm #{vmx_path}"
197
+ VM.wrap(@_datacenter.find_vm(spec[:vm_name]),self)
198
+ end
178
199
  end
179
200
 
180
201
  def create_net_dev(nic_id, spec)
@@ -199,10 +220,10 @@ module ESX
199
220
  end
200
221
 
201
222
  # Return product info as an array of strings containing
202
- #
223
+ #
203
224
  # fullName, apiType, apiVersion, osType, productLineId, vendor, version
204
- #
205
- def host_info
225
+ #
226
+ def host_info
206
227
  [
207
228
  @_host.summary.config.product.fullName,
208
229
  @_host.summary.config.product.apiType,
@@ -215,11 +236,11 @@ module ESX
215
236
  end
216
237
 
217
238
  # Return a list of VM available in the inventory
218
- #
239
+ #
219
240
  def virtual_machines
220
241
  vms = []
221
- vm = @_datacenter.vmFolder.childEntity.each do |x|
222
- vms << VM.wrap(x)
242
+ vm = @_datacenter.vmFolder.childEntity.each do |x|
243
+ vms << VM.wrap(x,self)
223
244
  end
224
245
  vms
225
246
  end
@@ -283,8 +304,8 @@ module ESX
283
304
  Net::SSH.start(@address, @user, :password => @password) do |ssh|
284
305
  if (ssh.exec! "ls #{template} 2>/dev/null").nil?
285
306
  Log.error "Template #{template_disk} does not exist"
286
- raise "Template does not exist"
287
- end
307
+ raise "Template does not exist"
308
+ end
288
309
  ssh.exec!("rm -f #{template} && rm -f #{template_flat} 2>&1")
289
310
  end
290
311
  end
@@ -296,7 +317,7 @@ module ESX
296
317
  return dest_file if template_exist?(dest_file)
297
318
  Net::SSH.start(@address, @user, :password => @password) do |ssh|
298
319
  if (ssh.exec! "ls -la #{@templates_dir} 2>/dev/null").nil?
299
- # Create template dir
320
+ # Create template dir
300
321
  Log.debug "Creating templates dir #{@templates_dir}"
301
322
  ssh.exec "mkdir -p #{@templates_dir}"
302
323
  end
@@ -326,7 +347,7 @@ module ESX
326
347
  #
327
348
  # if params has :use_template => true, the disk is saved as a template in
328
349
  # @templates_dir and cloned from there.
329
- #
350
+ #
330
351
  # Destination directory must exist otherwise rises exception
331
352
  #
332
353
  def import_disk(source, destination, print_progress = false, params = {})
@@ -367,7 +388,11 @@ module ESX
367
388
  end
368
389
  end
369
390
  end
370
-
391
+
392
+ def datacenter
393
+ @_datacenter
394
+ end
395
+
371
396
  private
372
397
  #
373
398
  # disk_file
@@ -385,7 +410,7 @@ module ESX
385
410
  datastore = params[:datastore]
386
411
  datastore = datastore + " #{disk_file}" if not disk_file.nil?
387
412
  spec = {}
388
- if disk_type == :sparse
413
+ if disk_type == :sparse
389
414
  spec = {
390
415
  :operation => :add,
391
416
  :device => RbVmomi::VIM.VirtualDisk(
@@ -417,52 +442,71 @@ module ESX
417
442
 
418
443
  class VM
419
444
 
420
- attr_accessor :memory_size, :cpus, :ethernet_cards_number
421
- attr_accessor :name, :virtual_disks_number, :vm_object
445
+ attr_accessor :memory_size, :cpus, :ethernet_cards_number, :vmid
446
+ attr_accessor :name, :virtual_disks_number, :vm_object, :host
422
447
 
423
448
  # Wraps a RbVmomi::VirtualMachine object
424
449
  #
425
450
  # **This method should never be called manually.**
426
451
  #
427
- def self.wrap(vm)
452
+ def self.wrap(vm,host)
428
453
  _vm = VM.new
429
454
  _vm.name = vm.name
430
455
  ## HACK: for some reason vm.summary.config.memorySizeMB returns nil
431
456
  # under some conditions
432
457
  _vm.memory_size = vm.summary.config.memorySizeMB*1024*1024 rescue 0
433
458
  _vm.cpus = vm.summary.config.numCpu
434
- _vm.ethernet_cards_number = vm.summary.config.numEthernetCards
459
+ _vm.ethernet_cards_number = vm.summary.config.numEthernetCards
435
460
  _vm.virtual_disks_number = vm.summary.config.numVirtualDisks
436
461
  _vm.vm_object = vm
462
+ _vm.host = host
463
+ _vm.vmid = vm.to_s.scan(/\"([0-9]+)\"/).flatten.first
437
464
  _vm
438
465
  end
439
466
 
440
467
  # Returns the state of the VM as a string
441
468
  # 'poweredOff', 'poweredOn'
442
- #
469
+ #
443
470
  def power_state
444
471
  vm_object.summary.runtime.powerState
445
472
  end
446
473
 
447
474
  # Power On a VM
448
475
  def power_on
449
- vm_object.PowerOnVM_Task.wait_for_completion
476
+ unless host.free_license
477
+ vm_object.PowerOnVM_Task.wait_for_completion
478
+ else
479
+ host.remote_command "vim-cmd vmsvc/power.on #{vmid}"
480
+ end
450
481
  end
451
482
 
452
483
  # Power Off a VM
453
484
  def power_off
454
- vm_object.PowerOffVM_Task.wait_for_completion
485
+ unless host.free_license
486
+ vm_object.PowerOffVM_Task.wait_for_completion
487
+ else
488
+ host.remote_command "vim-cmd vmsvc/power.off #{vmid}"
489
+ end
455
490
  end
456
491
 
457
492
  # Destroy the VirtualMaching removing it from the inventory
458
493
  # and deleting the disk files
459
494
  def destroy
460
495
  #disks = vm_object.config.hardware.device.grep(RbVmomi::VIM::VirtualDisk)
461
- vm_object.Destroy_Task.wait_for_completion
496
+ unless host.free_license
497
+ vm_object.Destroy_Task.wait_for_completion
498
+ else
499
+ host.remote_command "vim-cmd vmsvc/power.off #{vmid}"
500
+ host.remote_command "vim-cmd vmsvc/destroy #{vmid}"
501
+ end
462
502
  end
463
503
 
464
504
  def reset
465
- vm_object.ResetVM_Task.wait_for_completion
505
+ unless host.free_license
506
+ vm_object.ResetVM_Task.wait_for_completion
507
+ else
508
+ host.remote_command "vim-cmd vmsvc/power.reset #{vmid}"
509
+ end
466
510
  end
467
511
 
468
512
  def guest_info
@@ -487,7 +531,7 @@ module ESX
487
531
  end
488
532
 
489
533
  class NetworkInterface
490
-
534
+
491
535
  attr_accessor :_wrapped_object
492
536
 
493
537
  # Accepts VirtualEthernetCard and GuestNicInfo objects
@@ -509,7 +553,7 @@ module ESX
509
553
  end
510
554
 
511
555
  def mac
512
- _wrapped_object.macAddress
556
+ _wrapped_object.macAddress
513
557
  end
514
558
 
515
559
  end
@@ -527,7 +571,7 @@ module ESX
527
571
  def ip_address
528
572
  _wrapped_object.ipAddress
529
573
  end
530
-
574
+
531
575
  def nics
532
576
  n = []
533
577
  _wrapped_object.net.each do |nic|
@@ -0,0 +1,27 @@
1
+ .encoding = "UTF-8"
2
+ config.version = "8"
3
+ virtualHW.version = "7"
4
+ vmci0.present = "true"
5
+ displayName = "<%= spec[:vm_name] %>"
6
+ numvcpus = "<%= spec[:cpus] %>"
7
+ memsize = "<%= spec[:memory] %>"
8
+ scsi0.present = "true"
9
+ scsi0.sharedBus = "none"
10
+ scsi0.virtualDev = "lsilogic"
11
+ scsi0:0.present = "true"
12
+ scsi0:0.fileName = "<%= spec[:disk_file].split("/").last %>"
13
+ scsi0:0.deviceType = "scsi-hardDisk"
14
+ guestOS = <%= spec[:guest_id] %>
15
+ floppy0.present = "FALSE"
16
+ <% spec[:nics].each_with_index do |nic,index| %>
17
+ ethernet<%= index %>.present = "true"
18
+ ethernet<%= index %>.virtualDev = "e1000"
19
+ ethernet<%= index %>.wakeOnPcktRcv = "false"
20
+ ethernet<%= index %>.networkName = "<%= nic[:network] %>"
21
+ <% if nic[:mac_address] %>
22
+ ethernet<%= index %>.addressType = "static"
23
+ ethernet<%= index %>.address = "<%= nic[:mac_address] %>"
24
+ <% else %>
25
+ ethernet<%= index %>.addressType = "generated"
26
+ <% end %>
27
+ <% end %>
metadata CHANGED
@@ -1,19 +1,84 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: esx
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.4.1
4
+ version: 0.4.2
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
8
8
  - Sergio Rubio
9
+ - Massimo Maino
9
10
  autorequire:
10
11
  bindir: bin
11
12
  cert_chain: []
12
- date: 2012-03-26 00:00:00.000000000 Z
13
+ date: 2012-12-08 00:00:00.000000000 Z
13
14
  dependencies:
15
+ - !ruby/object:Gem::Dependency
16
+ name: net-ssh
17
+ requirement: !ruby/object:Gem::Requirement
18
+ none: false
19
+ requirements:
20
+ - - ! '>='
21
+ - !ruby/object:Gem::Version
22
+ version: '0'
23
+ type: :development
24
+ prerelease: false
25
+ version_requirements: !ruby/object:Gem::Requirement
26
+ none: false
27
+ requirements:
28
+ - - ! '>='
29
+ - !ruby/object:Gem::Version
30
+ version: '0'
31
+ - !ruby/object:Gem::Dependency
32
+ name: net-scp
33
+ requirement: !ruby/object:Gem::Requirement
34
+ none: false
35
+ requirements:
36
+ - - ! '>='
37
+ - !ruby/object:Gem::Version
38
+ version: '0'
39
+ type: :development
40
+ prerelease: false
41
+ version_requirements: !ruby/object:Gem::Requirement
42
+ none: false
43
+ requirements:
44
+ - - ! '>='
45
+ - !ruby/object:Gem::Version
46
+ version: '0'
47
+ - !ruby/object:Gem::Dependency
48
+ name: rbvmomi
49
+ requirement: !ruby/object:Gem::Requirement
50
+ none: false
51
+ requirements:
52
+ - - ! '>='
53
+ - !ruby/object:Gem::Version
54
+ version: '0'
55
+ type: :development
56
+ prerelease: false
57
+ version_requirements: !ruby/object:Gem::Requirement
58
+ none: false
59
+ requirements:
60
+ - - ! '>='
61
+ - !ruby/object:Gem::Version
62
+ version: '0'
63
+ - !ruby/object:Gem::Dependency
64
+ name: alchemist
65
+ requirement: !ruby/object:Gem::Requirement
66
+ none: false
67
+ requirements:
68
+ - - ! '>='
69
+ - !ruby/object:Gem::Version
70
+ version: '0'
71
+ type: :development
72
+ prerelease: false
73
+ version_requirements: !ruby/object:Gem::Requirement
74
+ none: false
75
+ requirements:
76
+ - - ! '>='
77
+ - !ruby/object:Gem::Version
78
+ version: '0'
14
79
  - !ruby/object:Gem::Dependency
15
80
  name: rspec
16
- requirement: &13782400 !ruby/object:Gem::Requirement
81
+ requirement: !ruby/object:Gem::Requirement
17
82
  none: false
18
83
  requirements:
19
84
  - - ! '>='
@@ -21,21 +86,31 @@ dependencies:
21
86
  version: '0'
22
87
  type: :development
23
88
  prerelease: false
24
- version_requirements: *13782400
89
+ version_requirements: !ruby/object:Gem::Requirement
90
+ none: false
91
+ requirements:
92
+ - - ! '>='
93
+ - !ruby/object:Gem::Version
94
+ version: '0'
25
95
  - !ruby/object:Gem::Dependency
26
96
  name: bundler
27
- requirement: &13797440 !ruby/object:Gem::Requirement
97
+ requirement: !ruby/object:Gem::Requirement
28
98
  none: false
29
99
  requirements:
30
- - - ~>
100
+ - - ! '>'
31
101
  - !ruby/object:Gem::Version
32
102
  version: 1.0.0
33
103
  type: :development
34
104
  prerelease: false
35
- version_requirements: *13797440
105
+ version_requirements: !ruby/object:Gem::Requirement
106
+ none: false
107
+ requirements:
108
+ - - ! '>'
109
+ - !ruby/object:Gem::Version
110
+ version: 1.0.0
36
111
  - !ruby/object:Gem::Dependency
37
112
  name: jeweler
38
- requirement: &13795980 !ruby/object:Gem::Requirement
113
+ requirement: !ruby/object:Gem::Requirement
39
114
  none: false
40
115
  requirements:
41
116
  - - ! '>='
@@ -43,10 +118,15 @@ dependencies:
43
118
  version: '0'
44
119
  type: :development
45
120
  prerelease: false
46
- version_requirements: *13795980
121
+ version_requirements: !ruby/object:Gem::Requirement
122
+ none: false
123
+ requirements:
124
+ - - ! '>='
125
+ - !ruby/object:Gem::Version
126
+ version: '0'
47
127
  - !ruby/object:Gem::Dependency
48
128
  name: simplecov
49
- requirement: &13793800 !ruby/object:Gem::Requirement
129
+ requirement: !ruby/object:Gem::Requirement
50
130
  none: false
51
131
  requirements:
52
132
  - - ! '>='
@@ -54,10 +134,15 @@ dependencies:
54
134
  version: '0'
55
135
  type: :development
56
136
  prerelease: false
57
- version_requirements: *13793800
137
+ version_requirements: !ruby/object:Gem::Requirement
138
+ none: false
139
+ requirements:
140
+ - - ! '>='
141
+ - !ruby/object:Gem::Version
142
+ version: '0'
58
143
  - !ruby/object:Gem::Dependency
59
144
  name: alchemist
60
- requirement: &13791580 !ruby/object:Gem::Requirement
145
+ requirement: !ruby/object:Gem::Requirement
61
146
  none: false
62
147
  requirements:
63
148
  - - ! '>='
@@ -65,10 +150,15 @@ dependencies:
65
150
  version: '0'
66
151
  type: :runtime
67
152
  prerelease: false
68
- version_requirements: *13791580
153
+ version_requirements: !ruby/object:Gem::Requirement
154
+ none: false
155
+ requirements:
156
+ - - ! '>='
157
+ - !ruby/object:Gem::Version
158
+ version: '0'
69
159
  - !ruby/object:Gem::Dependency
70
160
  name: rbvmomi
71
- requirement: &13790180 !ruby/object:Gem::Requirement
161
+ requirement: !ruby/object:Gem::Requirement
72
162
  none: false
73
163
  requirements:
74
164
  - - ! '>='
@@ -76,10 +166,15 @@ dependencies:
76
166
  version: '0'
77
167
  type: :runtime
78
168
  prerelease: false
79
- version_requirements: *13790180
169
+ version_requirements: !ruby/object:Gem::Requirement
170
+ none: false
171
+ requirements:
172
+ - - ! '>='
173
+ - !ruby/object:Gem::Version
174
+ version: '0'
80
175
  - !ruby/object:Gem::Dependency
81
176
  name: terminal-table
82
- requirement: &13805300 !ruby/object:Gem::Requirement
177
+ requirement: !ruby/object:Gem::Requirement
83
178
  none: false
84
179
  requirements:
85
180
  - - ! '>='
@@ -87,10 +182,15 @@ dependencies:
87
182
  version: '0'
88
183
  type: :runtime
89
184
  prerelease: false
90
- version_requirements: *13805300
185
+ version_requirements: !ruby/object:Gem::Requirement
186
+ none: false
187
+ requirements:
188
+ - - ! '>='
189
+ - !ruby/object:Gem::Version
190
+ version: '0'
91
191
  - !ruby/object:Gem::Dependency
92
192
  name: net-ssh
93
- requirement: &13803800 !ruby/object:Gem::Requirement
193
+ requirement: !ruby/object:Gem::Requirement
94
194
  none: false
95
195
  requirements:
96
196
  - - ! '>='
@@ -98,10 +198,15 @@ dependencies:
98
198
  version: '0'
99
199
  type: :runtime
100
200
  prerelease: false
101
- version_requirements: *13803800
201
+ version_requirements: !ruby/object:Gem::Requirement
202
+ none: false
203
+ requirements:
204
+ - - ! '>='
205
+ - !ruby/object:Gem::Version
206
+ version: '0'
102
207
  - !ruby/object:Gem::Dependency
103
208
  name: net-scp
104
- requirement: &13802480 !ruby/object:Gem::Requirement
209
+ requirement: !ruby/object:Gem::Requirement
105
210
  none: false
106
211
  requirements:
107
212
  - - ! '>='
@@ -109,10 +214,15 @@ dependencies:
109
214
  version: '0'
110
215
  type: :runtime
111
216
  prerelease: false
112
- version_requirements: *13802480
217
+ version_requirements: !ruby/object:Gem::Requirement
218
+ none: false
219
+ requirements:
220
+ - - ! '>='
221
+ - !ruby/object:Gem::Version
222
+ version: '0'
113
223
  - !ruby/object:Gem::Dependency
114
224
  name: clamp
115
- requirement: &13800940 !ruby/object:Gem::Requirement
225
+ requirement: !ruby/object:Gem::Requirement
116
226
  none: false
117
227
  requirements:
118
228
  - - ! '>='
@@ -120,7 +230,12 @@ dependencies:
120
230
  version: '0'
121
231
  type: :runtime
122
232
  prerelease: false
123
- version_requirements: *13800940
233
+ version_requirements: !ruby/object:Gem::Requirement
234
+ none: false
235
+ requirements:
236
+ - - ! '>='
237
+ - !ruby/object:Gem::Version
238
+ version: '0'
124
239
  description: Manage VMWare ESX hosts with ease
125
240
  email: rubiojr@frameos.org
126
241
  executables:
@@ -133,6 +248,7 @@ files:
133
248
  - .document
134
249
  - CHANGELOG.md
135
250
  - Gemfile
251
+ - Gemfile.lock
136
252
  - LICENSE.txt
137
253
  - README.md
138
254
  - Rakefile
@@ -142,6 +258,7 @@ files:
142
258
  - spec/spec_helper.rb
143
259
  - spec/unit/host_spec.rb
144
260
  - spec/unit/vm_spec.rb
261
+ - templates/vmx_template.erb
145
262
  homepage: http://github.com/rubiojr/esx
146
263
  licenses:
147
264
  - MIT
@@ -163,7 +280,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
163
280
  version: '0'
164
281
  requirements: []
165
282
  rubyforge_project:
166
- rubygems_version: 1.8.17
283
+ rubygems_version: 1.8.24
167
284
  signing_key:
168
285
  specification_version: 3
169
286
  summary: Simple RbVmomi wrapper library to manage VMWare ESX hosts