esx 0.2.4 → 0.3.1

Sign up to get free protection for your applications and to get access to all the features.
data/README.md CHANGED
@@ -102,6 +102,8 @@ __esx create-vm --user root --password foo --name esx-rubiojr --disk-file /path/
102
102
 
103
103
  # Testing
104
104
 
105
+ Run 'bundler install' to install required deps.
106
+
105
107
  Run rspec from the base dir. By default, the tests try to connect to an ESX host named esx-test-host with user root and no password. Edit spec/spec_helper.rb to fit your needs.
106
108
 
107
109
  # Copyright
data/bin/esx CHANGED
@@ -71,15 +71,96 @@ class InfoCommand < BaseCommand
71
71
  end
72
72
  end
73
73
 
74
+ class DestroyVMCommand < BaseCommand
75
+
76
+ parameter "ADDRESS", "ESX host address"
77
+ option "--vm-name", "VM_NAME", "Virtual Machine to destroy"
78
+
79
+ def execute
80
+ begin
81
+ host = ESX::Host.connect address, user, password
82
+ host.virtual_machines.each do |vm|
83
+ if vm.name == vm_name
84
+ print "Destroying Virtual Machine '#{vm.name}'..."
85
+ vm.power_off unless vm.power_state == 'poweredOff'
86
+ vm.destroy
87
+ puts "Done."
88
+ end
89
+ end
90
+ rescue Exception => e
91
+ $stderr.puts "Can't connect to the host #{address}."
92
+ if debug?
93
+ $stderr.puts e.message
94
+ end
95
+ exit 1
96
+ end
97
+ end
98
+
99
+ end
100
+
101
+ class PowerOffVMCommand < BaseCommand
102
+
103
+ parameter "ADDRESS", "ESX host address"
104
+ option "--vm-name", "VM_NAME", "Virtual Machine to Power Off"
105
+
106
+ def execute
107
+ begin
108
+ host = ESX::Host.connect address, user, password
109
+ host.virtual_machines.each do |vm|
110
+ if vm.name == vm_name and vm.power_state == 'poweredOn'
111
+ print "Powering Off VM #{vm_name}... "
112
+ vm.power_off
113
+ puts "Done."
114
+ end
115
+ end
116
+ rescue Exception => e
117
+ $stderr.puts "Can't connect to the host #{address}."
118
+ if debug?
119
+ $stderr.puts e.message
120
+ end
121
+ exit 1
122
+ end
123
+ end
124
+
125
+ end
126
+
127
+ class PowerOnVMCommand < BaseCommand
128
+
129
+ parameter "ADDRESS", "ESX host address"
130
+ option "--vm-name", "VM_NAME", "Virtual Machine to Power On"
131
+
132
+ def execute
133
+ begin
134
+ host = ESX::Host.connect address, user, password
135
+ host.virtual_machines.each do |vm|
136
+ if vm.name == vm_name and vm.power_state != 'poweredOn'
137
+ print "Powering On VM #{vm_name}... "
138
+ vm.power_on
139
+ puts "Done."
140
+ end
141
+ end
142
+ rescue Exception => e
143
+ $stderr.puts "Can't connect to the host #{address}."
144
+ if debug?
145
+ $stderr.puts e.message
146
+ end
147
+ exit 1
148
+ end
149
+ end
150
+
151
+ end
152
+
153
+
74
154
  class CreateVMCommand < BaseCommand
75
155
 
76
156
  parameter "ADDRESS", "ESX host address"
77
157
  option "--disk-file", "DISK_FILE", "VMDK file to import", :attribute_name => :disk_file
78
- option "--disk-size", "DISK_SIZE", "VM Disk size", :attribute_name => :disk_size, :default => 8192
158
+ option "--disk-size", "DISK_SIZE", "VM Disk size in MB", :attribute_name => :disk_size, :default => 8192
79
159
  option "--guest-id", "GUEST_ID", "GuestID value", :attribute_name => :guest_id, :default => 'otherGuest'
80
160
  option "--name", "VM NAME", "Virtual Machine name (required)"
81
161
  option "--memory", "MEMORY", "VM Memory size in MB", :default => 512
82
162
  option "--mac-address", "MAC", "VM Nic1 MAC address", :default => nil
163
+ option "--vm-network", "VM NETWORK", "Network where nic is attached to", :default => 'VM Network'
83
164
  option "--tmpdir", "TMP DIR", "tmp dir used to download files", :default => "/tmp"
84
165
  option "--datastore", "DATASTORE", "Datastore used to host the disk", :default => "datastore1"
85
166
  option "--poweron", :flag, "Power on the VM after creation"
@@ -103,7 +184,7 @@ class CreateVMCommand < BaseCommand
103
184
  vm = host.create_vm :vm_name => name,
104
185
  :datastore => datastore, :disk_type => :flat, :memory => memory,
105
186
  :disk_size => disk_size,
106
- :guest_id => guest_id, :mac_address => mac_address
187
+ :guest_id => guest_id, :nics => [{:mac_address => mac_address, :network => vm_network}]
107
188
  else
108
189
  df = disk_file.dup
109
190
  if df.strip.chomp =~ /^http/
@@ -205,6 +286,9 @@ end
205
286
  class DefaultCommand < Clamp::Command
206
287
  default_subcommand "info", "Display host info", InfoCommand
207
288
  subcommand "create-vm", "Create a VM", CreateVMCommand
289
+ subcommand "poweron-vm", "Power On a VM", PowerOnVMCommand
290
+ subcommand "poweroff-vm", "Power Off a VM", PowerOffVMCommand
291
+ subcommand "destroy-vm", "Destroy VM", DestroyVMCommand
208
292
  end
209
293
 
210
294
  begin
data/lib/esx.rb CHANGED
@@ -6,7 +6,7 @@ require 'net/ssh'
6
6
 
7
7
  module ESX
8
8
 
9
- VERSION = '0.2.4'
9
+ VERSION = '0.3.1'
10
10
 
11
11
  class Host
12
12
 
@@ -44,26 +44,34 @@ module ESX
44
44
 
45
45
  # Host memory size in bytes
46
46
  #
47
+ # returns a Fixnum
48
+ #
47
49
  def memory_size
48
- @_host.hardware.memorySize
50
+ @_host.hardware.memorySize.to_i
49
51
  end
50
52
 
51
53
  # Number of CPU cores available in this host
52
54
  #
55
+ # returns a String
56
+ #
53
57
  def cpu_cores
54
58
  @_host.hardware.cpuInfo.numCpuCores
55
59
  end
56
60
 
57
61
  # Power state of this host
58
62
  #
63
+ # poweredOn, poweredOff
64
+ #
59
65
  def power_state
60
66
  @_host.summary.runtime.powerState
61
67
  end
62
68
 
63
69
  # Host memory usage in bytes
70
+ #
71
+ # returns a Fixnum
64
72
  #
65
73
  def memory_usage
66
- @_host.summary.quickStats.overallMemoryUsage.megabytes.to.bytes.to_i
74
+ @_host.summary.quickStats.overallMemoryUsage * 1024 * 1024
67
75
  end
68
76
 
69
77
 
@@ -124,10 +132,6 @@ module ESX
124
132
  :key => 1000,
125
133
  :busNumber => 0,
126
134
  :sharedBus => :noSharing)
127
- },
128
- {
129
- :operation => :add,
130
- :device => RbVmomi::VIM.VirtualE1000(create_net_dev(specification))
131
135
  }
132
136
  ],
133
137
  :extraConfig => [
@@ -137,7 +141,21 @@ module ESX
137
141
  }
138
142
  ]
139
143
  }
140
-
144
+
145
+ #Add multiple nics
146
+ nics_count = 0
147
+ if spec[:nics]
148
+ spec[:nics].each do |nic_spec|
149
+ vm_cfg[:deviceChange].push(
150
+ {
151
+ :operation => :add,
152
+ :device => RbVmomi::VIM.VirtualE1000(create_net_dev(nics_count, nic_spec))
153
+
154
+ }
155
+ )
156
+ nics_count += 1
157
+ end
158
+ end
141
159
  # VMDK provided, replace the empty vmdk
142
160
  vm_cfg[:deviceChange].push(create_disk_spec(:disk_file => spec[:disk_file],
143
161
  :disk_type => spec[:disk_type],
@@ -147,17 +165,18 @@ module ESX
147
165
  VM.wrap(@_datacenter.vmFolder.CreateVM_Task(:config => vm_cfg, :pool => @_datacenter.hostFolder.children.first.resourcePool).wait_for_completion)
148
166
  end
149
167
 
150
- def create_net_dev(spec)
168
+ def create_net_dev(nic_id, spec)
151
169
  h = {
152
- :key => 0,
170
+ :key => nic_id,
153
171
  :deviceInfo => {
154
- :label => 'Network Adapter 1',
155
- :summary => 'VM Network'
172
+ :label => "Network Adapter #{nic_id}",
173
+ :summary => spec[:network] || 'VM Network'
156
174
  },
157
175
  :backing => RbVmomi::VIM.VirtualEthernetCardNetworkBackingInfo(
158
- :deviceName => 'VM Network'
176
+ :deviceName => spec[:network] || 'VM Network'
159
177
  )
160
178
  }
179
+
161
180
  if spec[:mac_address]
162
181
  h[:macAddress] = spec[:mac_address]
163
182
  h[:addressType] = 'manual'
@@ -298,7 +317,7 @@ module ESX
298
317
  def self.wrap(vm)
299
318
  _vm = VM.new
300
319
  _vm.name = vm.name
301
- _vm.memory_size = vm.summary.config.memorySizeMB.megabytes.to.bytes
320
+ _vm.memory_size = vm.summary.config.memorySizeMB*1024*1024
302
321
  _vm.cpus = vm.summary.config.numCpu
303
322
  _vm.ethernet_cards_number = vm.summary.config.numEthernetCards
304
323
  _vm.virtual_disks_number = vm.summary.config.numVirtualDisks
@@ -324,22 +343,9 @@ module ESX
324
343
  end
325
344
 
326
345
  # Destroy the VirtualMaching removing it from the inventory
327
- #
328
- # This operation does not destroy VM disks
329
- #
346
+ # and deleting the disk files
330
347
  def destroy
331
- disks = vm_object.config.hardware.device.grep(RbVmomi::VIM::VirtualDisk)
332
- #disks.select { |x| x.backing.parent == nil }.each do |disk|
333
- # spec = {
334
- # :deviceChange => [
335
- # {
336
- # :operation => :remove,
337
- # :device => disk
338
- # }
339
- # ]
340
- # }
341
- # vm_object.ReconfigVM_Task(:spec => spec).wait_for_completion
342
- #end
348
+ #disks = vm_object.config.hardware.device.grep(RbVmomi::VIM::VirtualDisk)
343
349
  vm_object.Destroy_Task.wait_for_completion
344
350
  end
345
351
 
Binary file
@@ -24,4 +24,27 @@ module ESXTestHelpers
24
24
  ""
25
25
  end
26
26
 
27
+ def test_data_dir
28
+ File.dirname(__FILE__) + '/data'
29
+ end
30
+
31
+ def test_host_object
32
+ @test_host = ESX::Host.connect(esx_host, esx_user, esx_password)
33
+ end
34
+
35
+ def create_simple_vm
36
+ name = 'test1GB'
37
+ disk_size = 1024
38
+ datastore = 'datastore1'
39
+ guest_id = 'otherGuest'
40
+ memory = 512
41
+ nics = [{ :mac_address => nil, :network => nil }]
42
+
43
+ vm = test_host_object.create_vm :vm_name => name,
44
+ :datastore => datastore, :disk_type => :flat, :memory => memory,
45
+ :disk_size => disk_size,
46
+ :guest_id => guest_id, :nics => nics
47
+ vm
48
+ end
49
+
27
50
  end
@@ -7,6 +7,12 @@ describe "ESX host" do
7
7
  @test_host = ESX::Host.connect(esx_host, esx_user, esx_password)
8
8
  end
9
9
 
10
+ after do
11
+ @test_host.virtual_machines.each do |vm|
12
+ vm.destroy
13
+ end
14
+ end
15
+
10
16
  it "connects to and ESX with a valid user/pass" do
11
17
  host = nil
12
18
  lambda do
@@ -18,4 +24,125 @@ describe "ESX host" do
18
24
  it "retrives the host name" do
19
25
  @test_host.name.should_not be_nil
20
26
  end
27
+
28
+ it "has no virtual machines" do
29
+ @test_host.virtual_machines.should be_empty
30
+ end
31
+
32
+ it "creates one one virtual machine with a 1GB empty disk, 512MB RAB, 1NIC" do
33
+ name = 'test1GB'
34
+ disk_size = 1024
35
+ datastore = 'datastore1'
36
+ guest_id = 'otherGuest'
37
+ memory = 512
38
+ nics = [{ :mac_address => nil, :network => nil }]
39
+
40
+ vm = @test_host.create_vm :vm_name => name,
41
+ :datastore => datastore, :disk_type => :flat, :memory => memory,
42
+ :disk_size => disk_size,
43
+ :guest_id => guest_id, :nics => nics
44
+ @test_host.virtual_machines.size.should eql(1)
45
+
46
+ vm = @test_host.virtual_machines.find { |vm| vm.name == 'test1GB' }
47
+ vm.should_not be_nil
48
+ vm.memory_size.should eql(512 * 1024 * 1024)
49
+ vm.ethernet_cards_number.should eql(1)
50
+ vm.virtual_disks_number.should eql(1)
51
+ end
52
+
53
+ it "should be able to create a VM with 2 nics" do
54
+ name = 'test1GB'
55
+ disk_size = 1024
56
+ datastore = 'datastore1'
57
+ guest_id = 'otherGuest'
58
+ memory = 512
59
+ nics = [{ :mac_address => nil, :network => nil }, { :mac_address => nil, :network => nil }]
60
+
61
+ vm = @test_host.create_vm :vm_name => name,
62
+ :datastore => datastore, :disk_type => :flat, :memory => memory,
63
+ :disk_size => disk_size,
64
+ :guest_id => guest_id, :nics => nics
65
+
66
+ vm.ethernet_cards_number.should eql(2)
67
+ end
68
+
69
+ it "should create a VM with 0 NICs if no NIC config is specified" do
70
+ name = 'test1GB'
71
+ disk_size = 1024
72
+ datastore = 'datastore1'
73
+ guest_id = 'otherGuest'
74
+ memory = 512
75
+
76
+ vm = @test_host.create_vm :vm_name => name,
77
+ :datastore => datastore, :disk_type => :flat, :memory => memory,
78
+ :disk_size => disk_size,
79
+ :guest_id => guest_id
80
+
81
+ vm.ethernet_cards_number.should eql(0)
82
+ end
83
+
84
+ it "should create a VM with 1 NICs and an autogenerated MAC address" do
85
+ name = 'test1GB'
86
+ disk_size = 1024
87
+ datastore = 'datastore1'
88
+ guest_id = 'otherGuest'
89
+ memory = 512
90
+ nics = [{}]
91
+
92
+ vm = @test_host.create_vm :vm_name => name,
93
+ :datastore => datastore, :disk_type => :flat, :memory => memory,
94
+ :disk_size => disk_size,
95
+ :guest_id => guest_id, :nics => nics
96
+
97
+ vm.ethernet_cards_number.should eql(1)
98
+ vm.nics.first.mac.should eql("")
99
+ end
100
+
101
+ it "has an array of datastores with one datastore named datastore1" do
102
+ @test_host.datastores.is_a?(Array).should be_true
103
+ @test_host.datastores.first.should be_a ESX::Datastore
104
+ @test_host.datastores.first.name.should eql('datastore1')
105
+ end
106
+
107
+ it "should be able to create and destroy a VM" do
108
+ name = 'test1GB'
109
+ disk_size = 1024
110
+ datastore = 'datastore1'
111
+ guest_id = 'otherGuest'
112
+ memory = 512
113
+ nics = [{ :mac_address => nil, :network => nil }]
114
+
115
+ vm = @test_host.create_vm :vm_name => name,
116
+ :datastore => datastore, :disk_type => :flat, :memory => memory,
117
+ :disk_size => disk_size,
118
+ :guest_id => guest_id, :nics => nics
119
+ @test_host.virtual_machines.size.should eql(1)
120
+ vm = @test_host.virtual_machines.find { |vm| vm.name == 'test1GB' }
121
+ vm.destroy
122
+ @test_host.virtual_machines.size.should eql(0)
123
+ end
124
+
125
+ it "should be able to create a VM with a custom MAC address" do
126
+ name = 'test1GB'
127
+ disk_size = 1024
128
+ datastore = 'datastore1'
129
+ guest_id = 'otherGuest'
130
+ memory = 512
131
+ nics = [{ :mac_address => '00:01:02:03:04:05', :network => nil }]
132
+
133
+ vm = @test_host.create_vm :vm_name => name,
134
+ :datastore => datastore, :disk_type => :flat, :memory => memory,
135
+ :disk_size => disk_size,
136
+ :guest_id => guest_id, :nics => nics
137
+ vm = @test_host.virtual_machines.find { |vm| vm.name == name }
138
+ vm.nics.first.mac.should eql('00:01:02:03:04:05')
139
+ end
140
+
141
+ it "should have valid property types" do
142
+ @test_host.memory_size.should be_a Fixnum
143
+ @test_host.memory_size.should be > 0
144
+ @test_host.memory_usage.should be_a Fixnum
145
+ @test_host.memory_usage.should be > 0
146
+ end
147
+
21
148
  end
@@ -0,0 +1,31 @@
1
+ require 'spec_helper'
2
+
3
+ describe "ESX Virtual Machine" do
4
+ include ESXTestHelpers
5
+
6
+ before do
7
+ @test_host = ESX::Host.connect(esx_host, esx_user, esx_password)
8
+ end
9
+
10
+ after do
11
+ @test_host.virtual_machines.each do |vm|
12
+ vm.destroy
13
+ end
14
+ end
15
+
16
+ it "should have an Array of NetworkInterfaces" do
17
+ vm = create_simple_vm
18
+ vm.nics.size.should eql(1)
19
+ vm.nics.first.should be_a ESX::NetworkInterface
20
+ end
21
+
22
+ it "should have valid property types" do
23
+ vm = create_simple_vm
24
+ vm.memory_size.should be_a Fixnum
25
+ vm.memory_size.should be > 0
26
+ vm.nics.should be_an Array
27
+ vm.power_state.should be_a String
28
+ end
29
+
30
+ end
31
+
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: esx
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.4
4
+ version: 0.3.1
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -9,11 +9,11 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2011-11-29 00:00:00.000000000 Z
12
+ date: 2012-01-03 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: rspec
16
- requirement: &11459820 !ruby/object:Gem::Requirement
16
+ requirement: &17898020 !ruby/object:Gem::Requirement
17
17
  none: false
18
18
  requirements:
19
19
  - - ! '>='
@@ -21,10 +21,10 @@ dependencies:
21
21
  version: '0'
22
22
  type: :development
23
23
  prerelease: false
24
- version_requirements: *11459820
24
+ version_requirements: *17898020
25
25
  - !ruby/object:Gem::Dependency
26
26
  name: bundler
27
- requirement: &11457160 !ruby/object:Gem::Requirement
27
+ requirement: &17894660 !ruby/object:Gem::Requirement
28
28
  none: false
29
29
  requirements:
30
30
  - - ~>
@@ -32,10 +32,10 @@ dependencies:
32
32
  version: 1.0.0
33
33
  type: :development
34
34
  prerelease: false
35
- version_requirements: *11457160
35
+ version_requirements: *17894660
36
36
  - !ruby/object:Gem::Dependency
37
37
  name: jeweler
38
- requirement: &11469480 !ruby/object:Gem::Requirement
38
+ requirement: &17928200 !ruby/object:Gem::Requirement
39
39
  none: false
40
40
  requirements:
41
41
  - - ! '>='
@@ -43,10 +43,10 @@ dependencies:
43
43
  version: '0'
44
44
  type: :development
45
45
  prerelease: false
46
- version_requirements: *11469480
46
+ version_requirements: *17928200
47
47
  - !ruby/object:Gem::Dependency
48
48
  name: simplecov
49
- requirement: &11467120 !ruby/object:Gem::Requirement
49
+ requirement: &17926060 !ruby/object:Gem::Requirement
50
50
  none: false
51
51
  requirements:
52
52
  - - ! '>='
@@ -54,10 +54,10 @@ dependencies:
54
54
  version: '0'
55
55
  type: :development
56
56
  prerelease: false
57
- version_requirements: *11467120
57
+ version_requirements: *17926060
58
58
  - !ruby/object:Gem::Dependency
59
59
  name: alchemist
60
- requirement: &11497120 !ruby/object:Gem::Requirement
60
+ requirement: &17922780 !ruby/object:Gem::Requirement
61
61
  none: false
62
62
  requirements:
63
63
  - - ! '>='
@@ -65,10 +65,10 @@ dependencies:
65
65
  version: '0'
66
66
  type: :runtime
67
67
  prerelease: false
68
- version_requirements: *11497120
68
+ version_requirements: *17922780
69
69
  - !ruby/object:Gem::Dependency
70
70
  name: rbvmomi
71
- requirement: &11493140 !ruby/object:Gem::Requirement
71
+ requirement: &17949440 !ruby/object:Gem::Requirement
72
72
  none: false
73
73
  requirements:
74
74
  - - ! '>='
@@ -76,10 +76,10 @@ dependencies:
76
76
  version: '0'
77
77
  type: :runtime
78
78
  prerelease: false
79
- version_requirements: *11493140
79
+ version_requirements: *17949440
80
80
  - !ruby/object:Gem::Dependency
81
81
  name: terminal-table
82
- requirement: &11584320 !ruby/object:Gem::Requirement
82
+ requirement: &17947300 !ruby/object:Gem::Requirement
83
83
  none: false
84
84
  requirements:
85
85
  - - ! '>='
@@ -87,10 +87,10 @@ dependencies:
87
87
  version: '0'
88
88
  type: :runtime
89
89
  prerelease: false
90
- version_requirements: *11584320
90
+ version_requirements: *17947300
91
91
  - !ruby/object:Gem::Dependency
92
92
  name: net-ssh
93
- requirement: &11582280 !ruby/object:Gem::Requirement
93
+ requirement: &17945200 !ruby/object:Gem::Requirement
94
94
  none: false
95
95
  requirements:
96
96
  - - ! '>='
@@ -98,10 +98,10 @@ dependencies:
98
98
  version: '0'
99
99
  type: :runtime
100
100
  prerelease: false
101
- version_requirements: *11582280
101
+ version_requirements: *17945200
102
102
  - !ruby/object:Gem::Dependency
103
103
  name: net-scp
104
- requirement: &11580560 !ruby/object:Gem::Requirement
104
+ requirement: &17942780 !ruby/object:Gem::Requirement
105
105
  none: false
106
106
  requirements:
107
107
  - - ! '>='
@@ -109,10 +109,10 @@ dependencies:
109
109
  version: '0'
110
110
  type: :runtime
111
111
  prerelease: false
112
- version_requirements: *11580560
112
+ version_requirements: *17942780
113
113
  - !ruby/object:Gem::Dependency
114
114
  name: clamp
115
- requirement: &11578480 !ruby/object:Gem::Requirement
115
+ requirement: &17969780 !ruby/object:Gem::Requirement
116
116
  none: false
117
117
  requirements:
118
118
  - - ! '>='
@@ -120,7 +120,7 @@ dependencies:
120
120
  version: '0'
121
121
  type: :runtime
122
122
  prerelease: false
123
- version_requirements: *11578480
123
+ version_requirements: *17969780
124
124
  description: Manage VMWare ESX hosts with ease
125
125
  email: rubiojr@frameos.org
126
126
  executables:
@@ -138,8 +138,10 @@ files:
138
138
  - bin/esx
139
139
  - examples/basics.rb
140
140
  - lib/esx.rb
141
+ - spec/data/tc.vmdk
141
142
  - spec/spec_helper.rb
142
143
  - spec/unit/host_spec.rb
144
+ - spec/unit/vm_spec.rb
143
145
  homepage: http://github.com/rubiojr/esx
144
146
  licenses:
145
147
  - MIT