esx 0.4.1 → 0.4.2

Sign up to get free protection for your applications and to get access to all the features.
@@ -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