rvc 1.5.0 → 1.6.0
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.
- data/README.rdoc +1 -1
- data/Rakefile +2 -1
- data/VERSION +1 -1
- data/bin/rvc +53 -9
- data/lib/rvc/completion.rb +57 -19
- data/lib/rvc/extensions/ComputeResource.rb +2 -2
- data/lib/rvc/extensions/DVPortSetting.rb +108 -0
- data/lib/rvc/extensions/Datacenter.rb +19 -4
- data/lib/rvc/extensions/Datastore.rb +6 -1
- data/lib/rvc/extensions/DistributedVirtualPort.rb +146 -0
- data/lib/rvc/extensions/DistributedVirtualPortgroup.rb +274 -10
- data/lib/rvc/extensions/DistributedVirtualSwitch.rb +124 -3
- data/lib/rvc/extensions/Folder.rb +9 -2
- data/lib/rvc/extensions/HostSystem.rb +60 -0
- data/lib/rvc/extensions/ManagedEntity.rb +19 -0
- data/lib/rvc/extensions/ParaVirtualSCSIController.rb +25 -0
- data/lib/rvc/extensions/PerfCounterInfo.rb +26 -0
- data/lib/rvc/extensions/PerformanceManager.rb +83 -0
- data/lib/rvc/extensions/ResourcePool.rb +21 -0
- data/lib/rvc/extensions/VirtualDevice.rb +59 -0
- data/lib/rvc/extensions/VirtualDisk.rb +25 -0
- data/lib/rvc/extensions/VirtualEthernetCard.rb +32 -0
- data/lib/rvc/extensions/VirtualMachine.rb +112 -1
- data/lib/rvc/field.rb +122 -0
- data/lib/rvc/filesystem_session.rb +20 -0
- data/lib/rvc/inventory.rb +35 -12
- data/lib/rvc/known_hosts.rb +20 -0
- data/lib/rvc/memory_session.rb +20 -0
- data/lib/rvc/modules.rb +67 -7
- data/lib/rvc/modules/alarm.rb +37 -0
- data/lib/rvc/modules/basic.rb +172 -41
- data/lib/rvc/modules/cluster.rb +18 -2
- data/lib/rvc/modules/core.rb +63 -0
- data/lib/rvc/modules/datastore.rb +158 -0
- data/lib/rvc/modules/device.rb +275 -0
- data/lib/rvc/modules/esxcli.rb +193 -0
- data/lib/rvc/modules/find.rb +125 -0
- data/lib/rvc/modules/issue.rb +33 -0
- data/lib/rvc/modules/perf.rb +284 -0
- data/lib/rvc/modules/permissions.rb +20 -0
- data/lib/rvc/modules/resource_pool.rb +69 -0
- data/lib/rvc/modules/role.rb +23 -3
- data/lib/rvc/modules/snapshot.rb +20 -0
- data/lib/rvc/modules/vds.rb +605 -0
- data/lib/rvc/modules/vim.rb +103 -26
- data/lib/rvc/modules/vm.rb +93 -220
- data/lib/rvc/modules/vnc.rb +50 -13
- data/lib/rvc/option_parser.rb +50 -2
- data/lib/rvc/readline-ffi.rb +2 -1
- data/lib/rvc/shell.rb +34 -33
- data/lib/rvc/util.rb +120 -2
- data/test/test_fs.rb +9 -5
- data/test/test_metric.rb +79 -0
- metadata +33 -3
data/lib/rvc/modules/vim.rb
CHANGED
@@ -30,10 +30,31 @@ URI_REGEX = %r{
|
|
30
30
|
@
|
31
31
|
)?
|
32
32
|
([^@:]+)
|
33
|
-
(?::(
|
33
|
+
(?::(\d{1,5}))?
|
34
|
+
(?::([0-9a-z]{64}))?
|
34
35
|
$
|
35
36
|
}x
|
36
37
|
|
38
|
+
class RbVmomi::VIM
|
39
|
+
include RVC::InventoryObject
|
40
|
+
|
41
|
+
def children
|
42
|
+
rootFolder.children
|
43
|
+
end
|
44
|
+
|
45
|
+
def self.folder?
|
46
|
+
true
|
47
|
+
end
|
48
|
+
|
49
|
+
def display_info
|
50
|
+
puts serviceContent.about.fullName
|
51
|
+
end
|
52
|
+
|
53
|
+
def _connection
|
54
|
+
self
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
37
58
|
opts :connect do
|
38
59
|
summary 'Open a connection to ESX/VC'
|
39
60
|
arg :uri, "Host to connect to"
|
@@ -49,14 +70,15 @@ def connect uri, opts
|
|
49
70
|
username = match[1] || ENV['RBVMOMI_USER']
|
50
71
|
password = match[2] || ENV['RBVMOMI_PASSWORD']
|
51
72
|
host = match[3]
|
52
|
-
|
73
|
+
port = match[4] || 443
|
74
|
+
certdigest = match[5] || opts[:certdigest]
|
53
75
|
bad_cert = false
|
54
76
|
|
55
77
|
vim = nil
|
56
78
|
loop do
|
57
79
|
begin
|
58
80
|
vim = RbVmomi::VIM.new :host => host,
|
59
|
-
:port =>
|
81
|
+
:port => port,
|
60
82
|
:path => '/sdk',
|
61
83
|
:ns => 'urn:vim25',
|
62
84
|
:rev => (opts[:rev]||'4.0'),
|
@@ -73,15 +95,27 @@ def connect uri, opts
|
|
73
95
|
end
|
74
96
|
|
75
97
|
if bad_cert
|
76
|
-
# Fall back to SSH-style known_hosts
|
77
98
|
peer_public_key = vim.http.peer_cert.public_key
|
78
|
-
|
99
|
+
# if user specified a hash on the commandline, verify against that
|
100
|
+
if certdigest
|
101
|
+
if certdigest != Digest::SHA2.hexdigest(peer_public_key.to_s())
|
102
|
+
err "Bad certificate digest specified for #{host}!"
|
103
|
+
end
|
104
|
+
else
|
105
|
+
# Fall back to SSH-style known_hosts
|
106
|
+
check_known_hosts(host, peer_public_key)
|
107
|
+
end
|
79
108
|
end
|
80
109
|
|
81
110
|
unless opts[:rev]
|
82
111
|
# negotiate API version
|
83
112
|
rev = vim.serviceContent.about.apiVersion
|
84
|
-
|
113
|
+
env_rev = ENV['RVC_VIMREV']
|
114
|
+
if env_rev && env_rev.to_f == 0
|
115
|
+
vim.rev = env_rev
|
116
|
+
else
|
117
|
+
vim.rev = [rev, env_rev || '5.0'].min
|
118
|
+
end
|
85
119
|
end
|
86
120
|
|
87
121
|
isVC = vim.serviceContent.about.apiType == "VirtualCenter"
|
@@ -106,15 +140,19 @@ def connect uri, opts
|
|
106
140
|
loaded_from_keychain = password
|
107
141
|
end
|
108
142
|
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
|
143
|
+
if opts[:cookie]
|
144
|
+
vim.cookie = opts[:cookie]
|
145
|
+
else
|
146
|
+
password_given = password != nil
|
147
|
+
loop do
|
148
|
+
begin
|
149
|
+
password = prompt_password unless password_given
|
150
|
+
vim.serviceContent.sessionManager.Login :userName => username,
|
151
|
+
:password => password
|
152
|
+
break
|
153
|
+
rescue RbVmomi::VIM::InvalidLogin
|
154
|
+
err $!.message if password_given
|
155
|
+
end
|
118
156
|
end
|
119
157
|
end
|
120
158
|
|
@@ -147,7 +185,7 @@ def prompt_password
|
|
147
185
|
end
|
148
186
|
|
149
187
|
def keychain_password username , hostname
|
150
|
-
return nil unless RbConfig::CONFIG['host_os'] =~ /^
|
188
|
+
return nil unless RbConfig::CONFIG['host_os'] =~ /^darwin1[01]/
|
151
189
|
|
152
190
|
begin
|
153
191
|
require 'osx_keychain'
|
@@ -198,16 +236,6 @@ def check_known_hosts host, peer_public_key
|
|
198
236
|
end
|
199
237
|
end
|
200
238
|
|
201
|
-
class RbVmomi::VIM
|
202
|
-
def display_info
|
203
|
-
puts serviceContent.about.fullName
|
204
|
-
end
|
205
|
-
|
206
|
-
def _connection
|
207
|
-
self
|
208
|
-
end
|
209
|
-
end
|
210
|
-
|
211
239
|
|
212
240
|
opts :tasks do
|
213
241
|
summary "Watch tasks in progress"
|
@@ -269,3 +297,52 @@ def tasks
|
|
269
297
|
view.DestroyView if view
|
270
298
|
end
|
271
299
|
end
|
300
|
+
|
301
|
+
|
302
|
+
opts :logbundles do
|
303
|
+
summary "Download log bundles"
|
304
|
+
arg :servers, "VIM connection and/or ESX hosts", :lookup => [RbVmomi::VIM, VIM::HostSystem], :multi => true
|
305
|
+
opt :dest, "Destination directory", :default => '.'
|
306
|
+
end
|
307
|
+
|
308
|
+
DEFAULT_SERVER_PLACEHOLDER = '0.0.0.0'
|
309
|
+
|
310
|
+
def logbundles servers, opts
|
311
|
+
vim = single_connection servers
|
312
|
+
diagMgr = vim.serviceContent.diagnosticManager
|
313
|
+
name = vim.host
|
314
|
+
FileUtils.mkdir_p opts[:dest]
|
315
|
+
|
316
|
+
hosts = servers.grep VIM::HostSystem
|
317
|
+
include_default = servers.member? vim
|
318
|
+
|
319
|
+
puts "#{Time.now}: Generating log bundles..."
|
320
|
+
bundles =
|
321
|
+
begin
|
322
|
+
diagMgr.GenerateLogBundles_Task(:includeDefault => include_default, :host => hosts).wait_for_completion
|
323
|
+
rescue VIM::TaskInProgress
|
324
|
+
$!.task.wait_for_completion
|
325
|
+
end
|
326
|
+
|
327
|
+
dest_path = nil
|
328
|
+
bundles.each do |b|
|
329
|
+
uri = URI.parse(b.url.sub('*', DEFAULT_SERVER_PLACEHOLDER))
|
330
|
+
bundle_name = b.system ? b.system.name : name
|
331
|
+
dest_path = File.join(opts[:dest], "#{bundle_name}-" + File.basename(uri.path))
|
332
|
+
puts "#{Time.now}: Downloading bundle #{b.url} to #{dest_path}"
|
333
|
+
uri.host = vim.http.address if uri.host == DEFAULT_SERVER_PLACEHOLDER
|
334
|
+
Net::HTTP.get_response uri do |res|
|
335
|
+
File.open dest_path, 'w' do |io|
|
336
|
+
res.read_body do |data|
|
337
|
+
io.write data
|
338
|
+
if $stdout.tty?
|
339
|
+
$stdout.write '.'
|
340
|
+
$stdout.flush
|
341
|
+
end
|
342
|
+
end
|
343
|
+
end
|
344
|
+
puts if $stdout.tty?
|
345
|
+
end
|
346
|
+
end
|
347
|
+
dest_path
|
348
|
+
end
|
data/lib/rvc/modules/vm.rb
CHANGED
@@ -68,13 +68,39 @@ def suspend vms
|
|
68
68
|
end
|
69
69
|
|
70
70
|
|
71
|
+
opts :wait_for_shutdown do
|
72
|
+
summary "Waits for a VM to shutdown"
|
73
|
+
arg :vm, nil, :multi => true, :lookup => VIM::VirtualMachine
|
74
|
+
opt :timeout, "Timeout in seconds", :type => :int, :default => 300
|
75
|
+
opt :delay, "Interval in seconds", :type => :int, :default => 5
|
76
|
+
end
|
77
|
+
|
78
|
+
def wait_for_shutdown vms, opts
|
79
|
+
finish_time = Time.now + opts[:timeout]
|
80
|
+
while Time.now < finish_time
|
81
|
+
all_off = true
|
82
|
+
vms.each do |vm|
|
83
|
+
if vm.summary.runtime.powerState == 'poweredOn'
|
84
|
+
all_off = false
|
85
|
+
end
|
86
|
+
end
|
87
|
+
return if all_off
|
88
|
+
sleep [opts[:delay], finish_time - Time.now].min
|
89
|
+
end
|
90
|
+
puts "WARNING: At least one VM did not shut down!"
|
91
|
+
end
|
92
|
+
|
93
|
+
|
71
94
|
opts :shutdown_guest do
|
72
95
|
summary "Shut down guest OS"
|
73
96
|
arg :vm, nil, :multi => true, :lookup => VIM::VirtualMachine
|
97
|
+
opt :timeout, "Timeout for guest shut down in seconds", :type => :int, :default => nil
|
98
|
+
opt :delay, "Interval between checks for guest shut down in seconds", :type => :int, :default => nil
|
74
99
|
end
|
75
100
|
|
76
|
-
def shutdown_guest vms
|
101
|
+
def shutdown_guest vms, opts
|
77
102
|
vms.each(&:ShutdownGuest)
|
103
|
+
wait_for_shutdown vms, opts unless opts[:timeout].nil?
|
78
104
|
end
|
79
105
|
|
80
106
|
|
@@ -102,44 +128,25 @@ opts :create do
|
|
102
128
|
summary "Create a new VM"
|
103
129
|
arg :name, "Destination", :lookup_parent => VIM::Folder
|
104
130
|
opt :pool, "Resource pool", :short => 'p', :type => :string, :lookup => VIM::ResourcePool
|
105
|
-
opt :host, "Host", :short => '
|
131
|
+
opt :host, "Host", :short => 'o', :type => :string, :lookup => VIM::HostSystem
|
106
132
|
opt :datastore, "Datastore", :short => 'd', :type => :string, :lookup => VIM::Datastore
|
107
|
-
opt :disksize, "Size in KB of primary disk (or add a unit of <M|G|T>)", :short => 's', :type => :string, :default => "4000000"
|
108
133
|
opt :memory, "Size in MB of memory", :short => 'm', :type => :int, :default => 128
|
109
|
-
opt :
|
134
|
+
opt :cpus, "Number of CPUs", :short => 'c', :type => :int, :default => 1
|
135
|
+
opt :guest_id, "Guest OS", :short => 'g', :default => "otherGuest" # XXX tab complete
|
136
|
+
|
110
137
|
text <<-EOB
|
111
138
|
|
112
|
-
|
113
|
-
|
139
|
+
No disks or network adapters are initially present. Use device.add_disk and
|
140
|
+
device.add_net to do this.
|
114
141
|
|
142
|
+
Example:
|
143
|
+
vm.create ~/vm/foo --pool ~/host/my_cluster/resourcePool --datastore ~/datastore/my_datastore
|
144
|
+
device.add_disk ~/vm/foo -s 30G
|
145
|
+
device.add_net ~/vm/foo ~/network/VM\\ Network
|
115
146
|
EOB
|
116
147
|
end
|
117
148
|
|
118
149
|
|
119
|
-
def realdisksize( size )
|
120
|
-
size.downcase!
|
121
|
-
if size =~ /([0-9][0-9,]*)([mgt])?/i
|
122
|
-
size = $1.delete(',').to_i
|
123
|
-
unit = $2
|
124
|
-
|
125
|
-
case unit
|
126
|
-
when 'm'
|
127
|
-
return size * 1024
|
128
|
-
when 'g'
|
129
|
-
return size * ( 1024 ** 2 )
|
130
|
-
when 't'
|
131
|
-
return size * ( 1024 ** 3 )
|
132
|
-
when nil
|
133
|
-
return size
|
134
|
-
else
|
135
|
-
err "Unknown size modifer of '#{unit}'"
|
136
|
-
end
|
137
|
-
else
|
138
|
-
err "Problem with #{size}"
|
139
|
-
end
|
140
|
-
end
|
141
|
-
|
142
|
-
|
143
150
|
def create dest, opts
|
144
151
|
err "must specify resource pool (--pool)" unless opts[:pool]
|
145
152
|
err "must specify datastore (--datastore)" unless opts[:datastore]
|
@@ -148,33 +155,12 @@ def create dest, opts
|
|
148
155
|
datastore_path = "[#{opts[:datastore].name}]"
|
149
156
|
config = {
|
150
157
|
:name => name,
|
151
|
-
:guestId =>
|
158
|
+
:guestId => opts[:guest_id],
|
152
159
|
:files => { :vmPathName => datastore_path },
|
153
160
|
:numCPUs => opts[:cpucount],
|
154
161
|
:memoryMB => opts[:memory],
|
155
162
|
:deviceChange => [
|
156
163
|
{
|
157
|
-
:operation => :add,
|
158
|
-
:device => VIM.VirtualLsiLogicController(
|
159
|
-
:key => 1000,
|
160
|
-
:busNumber => 0,
|
161
|
-
:sharedBus => :noSharing
|
162
|
-
)
|
163
|
-
}, {
|
164
|
-
:operation => :add,
|
165
|
-
:fileOperation => :create,
|
166
|
-
:device => VIM.VirtualDisk(
|
167
|
-
:key => -1,
|
168
|
-
:backing => VIM.VirtualDiskFlatVer2BackingInfo(
|
169
|
-
:fileName => datastore_path,
|
170
|
-
:diskMode => :persistent,
|
171
|
-
:thinProvisioned => true
|
172
|
-
),
|
173
|
-
:controllerKey => 1000,
|
174
|
-
:unitNumber => 0,
|
175
|
-
:capacityInKB => realdisksize( opts[:disksize] )
|
176
|
-
)
|
177
|
-
}, {
|
178
164
|
:operation => :add,
|
179
165
|
:device => VIM.VirtualCdrom(
|
180
166
|
:key => -2,
|
@@ -189,19 +175,6 @@ def create dest, opts
|
|
189
175
|
:controllerKey => 200,
|
190
176
|
:unitNumber => 0
|
191
177
|
)
|
192
|
-
}, {
|
193
|
-
:operation => :add,
|
194
|
-
:device => VIM.VirtualE1000(
|
195
|
-
:key => -3,
|
196
|
-
:deviceInfo => {
|
197
|
-
:label => 'Network Adapter 1',
|
198
|
-
:summary => 'VM Network'
|
199
|
-
},
|
200
|
-
:backing => VIM.VirtualEthernetCardNetworkBackingInfo(
|
201
|
-
:deviceName => 'VM Network'
|
202
|
-
),
|
203
|
-
:addressType => 'generated'
|
204
|
-
)
|
205
178
|
}
|
206
179
|
],
|
207
180
|
}
|
@@ -211,30 +184,6 @@ def create dest, opts
|
|
211
184
|
end
|
212
185
|
|
213
186
|
|
214
|
-
opts :insert_cdrom do
|
215
|
-
summary "Put a disc in a virtual CDROM drive"
|
216
|
-
arg :vm, nil, :lookup => VIM::VirtualMachine
|
217
|
-
arg :iso, "Path to the ISO image on a datastore", :lookup => VIM::Datastore::FakeDatastoreFile
|
218
|
-
end
|
219
|
-
|
220
|
-
def insert_cdrom vm, iso
|
221
|
-
device = vm.config.hardware.device.grep(VIM::VirtualCdrom)[0]
|
222
|
-
err "No virtual CDROM drive found" unless device
|
223
|
-
|
224
|
-
device.backing = VIM.VirtualCdromIsoBackingInfo(:fileName => iso.datastore_path)
|
225
|
-
|
226
|
-
spec = {
|
227
|
-
:deviceChange => [
|
228
|
-
{
|
229
|
-
:operation => :edit,
|
230
|
-
:device => device
|
231
|
-
}
|
232
|
-
]
|
233
|
-
}
|
234
|
-
|
235
|
-
vm.ReconfigVM_Task(:spec => spec)
|
236
|
-
end
|
237
|
-
|
238
187
|
opts :register do
|
239
188
|
summary "Register a VM already in a datastore"
|
240
189
|
arg :file, "RVC path to the VMX file", :lookup => VIM::Datastore::FakeDatastoreFile
|
@@ -326,22 +275,26 @@ def kill vms
|
|
326
275
|
CMD.basic.destroy vms unless vms.empty?
|
327
276
|
end
|
328
277
|
|
329
|
-
|
330
278
|
opts :answer do
|
331
279
|
summary "Answer a VM question"
|
332
|
-
arg :vm, nil, :lookup => VIM::VirtualMachine
|
333
280
|
arg :choice, "Answer ID"
|
281
|
+
arg :vm, nil, :lookup => VIM::VirtualMachine, :multi => true
|
334
282
|
end
|
335
283
|
|
336
|
-
def answer
|
337
|
-
|
338
|
-
|
339
|
-
|
340
|
-
|
341
|
-
|
284
|
+
def answer str, vms
|
285
|
+
vms.each do |vm|
|
286
|
+
begin
|
287
|
+
if q = vm.runtime.question
|
288
|
+
choice = q.choice.choiceInfo.find { |x| x.label == str }
|
289
|
+
err("invalid answer") unless choice
|
290
|
+
vm.AnswerVM :questionId => q.id, :answerChoice => choice.key
|
291
|
+
end
|
292
|
+
rescue
|
293
|
+
puts "#{vm.name rescue vm}: #{$!.message}"
|
294
|
+
end
|
295
|
+
end
|
342
296
|
end
|
343
297
|
|
344
|
-
|
345
298
|
opts :layout do
|
346
299
|
summary "Display info about VM files"
|
347
300
|
arg :vm, nil, :lookup => VIM::VirtualMachine
|
@@ -354,43 +307,6 @@ def layout vm
|
|
354
307
|
end
|
355
308
|
|
356
309
|
|
357
|
-
opts :devices do
|
358
|
-
summary "Display info about VM devices"
|
359
|
-
arg :vm, nil, :lookup => VIM::VirtualMachine
|
360
|
-
end
|
361
|
-
|
362
|
-
def devices vm
|
363
|
-
devs = vm.config.hardware.device
|
364
|
-
devs.each do |dev|
|
365
|
-
tags = []
|
366
|
-
tags << (dev.connectable.connected ? :connected : :disconnected) if dev.props.member? :connectable
|
367
|
-
puts "#{dev.deviceInfo.label} (#{dev.class}): #{dev.deviceInfo.summary}; #{tags * ' '}"
|
368
|
-
end
|
369
|
-
end
|
370
|
-
|
371
|
-
|
372
|
-
opts :connect do
|
373
|
-
summary "Connect a virtual device"
|
374
|
-
arg :vm, nil, :lookup => VIM::VirtualMachine
|
375
|
-
arg :label, "Device label"
|
376
|
-
end
|
377
|
-
|
378
|
-
def connect vm, label
|
379
|
-
change_device_connectivity vm, label, true
|
380
|
-
end
|
381
|
-
|
382
|
-
|
383
|
-
opts :disconnect do
|
384
|
-
summary "Disconnect a virtual device"
|
385
|
-
arg :vm, nil, :lookup => VIM::VirtualMachine
|
386
|
-
arg :label, "Device label"
|
387
|
-
end
|
388
|
-
|
389
|
-
def disconnect vm, label
|
390
|
-
change_device_connectivity vm, label, false
|
391
|
-
end
|
392
|
-
|
393
|
-
|
394
310
|
opts :find do
|
395
311
|
summary "Display a menu of VMX files to register"
|
396
312
|
arg :datastore, nil, :lookup => VIM::Datastore
|
@@ -417,37 +333,37 @@ def find ds, opts
|
|
417
333
|
end
|
418
334
|
|
419
335
|
|
420
|
-
opts :
|
336
|
+
opts :extra_config do
|
421
337
|
summary "Display extraConfig options"
|
422
338
|
arg :vm, nil, :lookup => VIM::VirtualMachine
|
423
339
|
arg :regex, "Regexes to filter keys", :multi => true, :required => false
|
424
340
|
end
|
425
341
|
|
426
|
-
def
|
427
|
-
|
342
|
+
def extra_config vm, regexes
|
343
|
+
_extra_config(vm, *regexes.map { |x| /#{x}/ })
|
428
344
|
end
|
429
345
|
|
430
346
|
|
431
|
-
opts :
|
347
|
+
opts :set_extra_config do
|
432
348
|
summary "Set extraConfig options"
|
433
349
|
arg :vm, nil, :lookup => VIM::VirtualMachine
|
434
350
|
arg 'key=value', "extraConfig key/value pairs", :multi => true
|
435
351
|
end
|
436
352
|
|
437
|
-
def
|
353
|
+
def set_extra_config vm, pairs
|
438
354
|
h = Hash[pairs.map { |x| x.split('=', 2).tap { |a| a << '' if a.size == 1 } }]
|
439
|
-
|
355
|
+
_set_extra_config vm, h
|
440
356
|
end
|
441
357
|
|
442
358
|
|
443
|
-
def
|
359
|
+
def _set_extra_config vm, hash
|
444
360
|
cfg = {
|
445
361
|
:extraConfig => hash.map { |k,v| { :key => k, :value => v } },
|
446
362
|
}
|
447
363
|
vm.ReconfigVM_Task(:spec => cfg).wait_for_completion
|
448
364
|
end
|
449
365
|
|
450
|
-
def
|
366
|
+
def _extra_config vm, *regexes
|
451
367
|
vm.config.extraConfig.each do |h|
|
452
368
|
if regexes.empty? or regexes.any? { |r| h[:key] =~ r }
|
453
369
|
puts "#{h[:key]}: #{h[:value]}"
|
@@ -492,6 +408,34 @@ def rvc vm, opts
|
|
492
408
|
system_fg(cmd, env)
|
493
409
|
end
|
494
410
|
|
411
|
+
opts :rdp do
|
412
|
+
summary "Connect via RDP"
|
413
|
+
arg :vms, nil, :lookup => VIM::VirtualMachine, :multi => true
|
414
|
+
opt :resolution, "Desired resolution", :type => :string, :default => ($rdpResolution ? $rdpResolution : '1024x768')
|
415
|
+
opt :username, "Username", :type => :string, :default => 'Administrator'
|
416
|
+
opt :password, "Password", :type => :string, :default => ($rdpDefaultPassword ? $rdpDefaultPassword : '')
|
417
|
+
end
|
418
|
+
|
419
|
+
rvc_alias :rdp, :rdp
|
420
|
+
|
421
|
+
def rdp vms, h
|
422
|
+
resolution = h[:resolution]
|
423
|
+
if !resolution
|
424
|
+
resolution = $rdpResolution ? $rdpResolution : '1024x768'
|
425
|
+
end
|
426
|
+
vms.each do |vm|
|
427
|
+
ip = vm_ip vm
|
428
|
+
|
429
|
+
begin
|
430
|
+
timeout(1) { TCPSocket.new ip, 3389; up = true }
|
431
|
+
rescue
|
432
|
+
puts "#{vm.name}: Warning: Looks like the RDP port is not responding"
|
433
|
+
end
|
434
|
+
|
435
|
+
cmd = "rdesktop -u '#{h[:username]}' -p '#{h[:password]}' -g#{resolution} #{ip} >/dev/null 2>&1 &"
|
436
|
+
system(cmd)
|
437
|
+
end
|
438
|
+
end
|
495
439
|
|
496
440
|
opts :ping do
|
497
441
|
summary "Ping a VM"
|
@@ -542,71 +486,11 @@ ensure
|
|
542
486
|
end
|
543
487
|
|
544
488
|
|
545
|
-
opts :add_net_device do
|
546
|
-
summary "Add a network adapter to a virtual machine"
|
547
|
-
arg :vm, nil, :lookup => VIM::VirtualMachine
|
548
|
-
opt :type, "Adapter type", :default => 'e1000'
|
549
|
-
opt :network, "Network to connect to", :default => 'VM Network'
|
550
|
-
end
|
551
|
-
|
552
|
-
def add_net_device vm, opts
|
553
|
-
case opts[:type]
|
554
|
-
when 'e1000'
|
555
|
-
_add_net_device vm, VIM::VirtualE1000, opts[:network]
|
556
|
-
when 'vmxnet3'
|
557
|
-
_add_net_device vm, VIM::VirtualVmxnet3, opts[:network]
|
558
|
-
else err "unknown device"
|
559
|
-
end
|
560
|
-
end
|
561
|
-
|
562
|
-
|
563
|
-
def _add_device vm, dev
|
564
|
-
spec = {
|
565
|
-
:deviceChange => [
|
566
|
-
{ :operation => :add, :device => dev },
|
567
|
-
]
|
568
|
-
}
|
569
|
-
vm.ReconfigVM_Task(:spec => spec).wait_for_completion
|
570
|
-
end
|
571
|
-
|
572
|
-
def _add_net_device vm, klass, network
|
573
|
-
_add_device vm, klass.new(
|
574
|
-
:key => -1,
|
575
|
-
:deviceInfo => {
|
576
|
-
:summary => network,
|
577
|
-
:label => `uuidgen`.chomp
|
578
|
-
},
|
579
|
-
:backing => VIM.VirtualEthernetCardNetworkBackingInfo(
|
580
|
-
:deviceName => network
|
581
|
-
),
|
582
|
-
:addressType => 'generated'
|
583
|
-
)
|
584
|
-
end
|
585
|
-
|
586
|
-
|
587
|
-
opts :remove_device do
|
588
|
-
summary "Remove a virtual device"
|
589
|
-
arg :vm, nil, :lookup => VIM::VirtualMachine
|
590
|
-
arg :label, "Device label"
|
591
|
-
end
|
592
|
-
|
593
|
-
def remove_device vm, label
|
594
|
-
dev = vm.config.hardware.device.find { |x| x.deviceInfo.label == label }
|
595
|
-
err "no such device" unless dev
|
596
|
-
spec = {
|
597
|
-
:deviceChange => [
|
598
|
-
{ :operation => :remove, :device => dev },
|
599
|
-
]
|
600
|
-
}
|
601
|
-
vm.ReconfigVM_Task(:spec => spec).wait_for_completion
|
602
|
-
end
|
603
|
-
|
604
|
-
|
605
489
|
opts :migrate do
|
606
490
|
summary "Migrate a VM"
|
607
491
|
arg :vm, nil, :lookup => VIM::VirtualMachine, :multi => true
|
608
492
|
opt :pool, "Resource pool", :short => 'p', :type => :string, :lookup => VIM::ResourcePool
|
609
|
-
opt :host, "Host", :short => '
|
493
|
+
opt :host, "Host", :short => 'o', :type => :string, :lookup => VIM::HostSystem
|
610
494
|
end
|
611
495
|
|
612
496
|
def migrate vms, opts
|
@@ -621,10 +505,10 @@ opts :clone do
|
|
621
505
|
arg :src, nil, :lookup => VIM::VirtualMachine
|
622
506
|
arg :dst, "Path to new VM", :lookup_parent => VIM::Folder
|
623
507
|
opt :pool, "Resource pool", :short => 'p', :type => :string, :lookup => VIM::ResourcePool
|
624
|
-
opt :host, "Host", :short => '
|
508
|
+
opt :host, "Host", :short => 'o', :type => :string, :lookup => VIM::HostSystem
|
625
509
|
opt :template, "Create a template", :short => 't'
|
626
510
|
opt :linked, "Create a linked clone", :short => 'l'
|
627
|
-
opt :
|
511
|
+
opt :power_on, "Power on VM after clone"
|
628
512
|
end
|
629
513
|
|
630
514
|
def clone src, dst, opts
|
@@ -645,12 +529,11 @@ def clone src, dst, opts
|
|
645
529
|
:pool => opts[:pool],
|
646
530
|
},
|
647
531
|
:template => opts[:template],
|
648
|
-
:powerOn => opts[:
|
532
|
+
:powerOn => opts[:power_on],
|
649
533
|
})
|
650
534
|
progress [task]
|
651
535
|
end
|
652
536
|
|
653
|
-
|
654
537
|
def deltaize_disks vm
|
655
538
|
real_disks = vm.config.hardware.device.grep(VIM::VirtualDisk).select { |x| x.backing.parent == nil }
|
656
539
|
unless real_disks.empty?
|
@@ -703,6 +586,8 @@ opts :modify_memory do
|
|
703
586
|
end
|
704
587
|
|
705
588
|
def modify_memory vm, opts
|
589
|
+
err "VM needs to be off" unless vm.summary.runtime.powerState == 'poweredOff'
|
590
|
+
err "memory must be a multiple of 4MB" unless ( opts[:size] % 4 ) == 0
|
706
591
|
spec = { :memoryMB => opts[:size] }
|
707
592
|
tasks [vm], :ReconfigVM, :spec => spec
|
708
593
|
end
|
@@ -730,18 +615,6 @@ def find_vmx_files ds
|
|
730
615
|
files
|
731
616
|
end
|
732
617
|
|
733
|
-
def change_device_connectivity vm, label, connected
|
734
|
-
dev = vm.config.hardware.device.find { |x| x.deviceInfo.label == label }
|
735
|
-
err "no such device" unless dev
|
736
|
-
dev.connectable.connected = connected
|
737
|
-
spec = {
|
738
|
-
:deviceChange => [
|
739
|
-
{ :operation => :edit, :device => dev },
|
740
|
-
]
|
741
|
-
}
|
742
|
-
vm.ReconfigVM_Task(:spec => spec).wait_for_completion
|
743
|
-
end
|
744
|
-
|
745
618
|
def vm_ip vm
|
746
619
|
summary = vm.summary
|
747
620
|
|