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.
Files changed (54) hide show
  1. data/README.rdoc +1 -1
  2. data/Rakefile +2 -1
  3. data/VERSION +1 -1
  4. data/bin/rvc +53 -9
  5. data/lib/rvc/completion.rb +57 -19
  6. data/lib/rvc/extensions/ComputeResource.rb +2 -2
  7. data/lib/rvc/extensions/DVPortSetting.rb +108 -0
  8. data/lib/rvc/extensions/Datacenter.rb +19 -4
  9. data/lib/rvc/extensions/Datastore.rb +6 -1
  10. data/lib/rvc/extensions/DistributedVirtualPort.rb +146 -0
  11. data/lib/rvc/extensions/DistributedVirtualPortgroup.rb +274 -10
  12. data/lib/rvc/extensions/DistributedVirtualSwitch.rb +124 -3
  13. data/lib/rvc/extensions/Folder.rb +9 -2
  14. data/lib/rvc/extensions/HostSystem.rb +60 -0
  15. data/lib/rvc/extensions/ManagedEntity.rb +19 -0
  16. data/lib/rvc/extensions/ParaVirtualSCSIController.rb +25 -0
  17. data/lib/rvc/extensions/PerfCounterInfo.rb +26 -0
  18. data/lib/rvc/extensions/PerformanceManager.rb +83 -0
  19. data/lib/rvc/extensions/ResourcePool.rb +21 -0
  20. data/lib/rvc/extensions/VirtualDevice.rb +59 -0
  21. data/lib/rvc/extensions/VirtualDisk.rb +25 -0
  22. data/lib/rvc/extensions/VirtualEthernetCard.rb +32 -0
  23. data/lib/rvc/extensions/VirtualMachine.rb +112 -1
  24. data/lib/rvc/field.rb +122 -0
  25. data/lib/rvc/filesystem_session.rb +20 -0
  26. data/lib/rvc/inventory.rb +35 -12
  27. data/lib/rvc/known_hosts.rb +20 -0
  28. data/lib/rvc/memory_session.rb +20 -0
  29. data/lib/rvc/modules.rb +67 -7
  30. data/lib/rvc/modules/alarm.rb +37 -0
  31. data/lib/rvc/modules/basic.rb +172 -41
  32. data/lib/rvc/modules/cluster.rb +18 -2
  33. data/lib/rvc/modules/core.rb +63 -0
  34. data/lib/rvc/modules/datastore.rb +158 -0
  35. data/lib/rvc/modules/device.rb +275 -0
  36. data/lib/rvc/modules/esxcli.rb +193 -0
  37. data/lib/rvc/modules/find.rb +125 -0
  38. data/lib/rvc/modules/issue.rb +33 -0
  39. data/lib/rvc/modules/perf.rb +284 -0
  40. data/lib/rvc/modules/permissions.rb +20 -0
  41. data/lib/rvc/modules/resource_pool.rb +69 -0
  42. data/lib/rvc/modules/role.rb +23 -3
  43. data/lib/rvc/modules/snapshot.rb +20 -0
  44. data/lib/rvc/modules/vds.rb +605 -0
  45. data/lib/rvc/modules/vim.rb +103 -26
  46. data/lib/rvc/modules/vm.rb +93 -220
  47. data/lib/rvc/modules/vnc.rb +50 -13
  48. data/lib/rvc/option_parser.rb +50 -2
  49. data/lib/rvc/readline-ffi.rb +2 -1
  50. data/lib/rvc/shell.rb +34 -33
  51. data/lib/rvc/util.rb +120 -2
  52. data/test/test_fs.rb +9 -5
  53. data/test/test_metric.rb +79 -0
  54. metadata +33 -3
@@ -48,9 +48,9 @@ def add_host cluster, hostname, opts
48
48
  :sslThumbprint => sslThumbprint,
49
49
  }
50
50
  task = cluster.AddHost_Task :spec => spec,
51
- :asConnected => false
51
+ :asConnected => true
52
52
  begin
53
- task.wait_for_completion
53
+ one_progress task
54
54
  break
55
55
  rescue VIM::SSLVerifyFault
56
56
  puts "SSL thumbprint: #{$!.fault.thumbprint}"
@@ -62,3 +62,19 @@ def add_host cluster, hostname, opts
62
62
  end
63
63
  end
64
64
  end
65
+
66
+
67
+ opts :configure_ha do
68
+ summary "Configure HA on a cluster"
69
+ arg :cluster, nil, :lookup => VIM::ClusterComputeResource
70
+ opt :disabled, "Disable HA", :default => false
71
+ end
72
+
73
+ def configure_ha cluster, opts
74
+ spec = VIM::ClusterConfigSpecEx(
75
+ :dasConfig => {
76
+ :enabled => !opts[:disabled],
77
+ }
78
+ )
79
+ one_progress(cluster.ReconfigureComputeResource_Task :spec => spec, :modify => true)
80
+ end
@@ -0,0 +1,63 @@
1
+ # Copyright (c) 2011 VMware, Inc. All Rights Reserved.
2
+ #
3
+ # Permission is hereby granted, free of charge, to any person obtaining a copy
4
+ # of this software and associated documentation files (the "Software"), to deal
5
+ # in the Software without restriction, including without limitation the rights
6
+ # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7
+ # copies of the Software, and to permit persons to whom the Software is
8
+ # furnished to do so, subject to the following conditions:
9
+ #
10
+ # The above copyright notice and this permission notice shall be included in
11
+ # all copies or substantial portions of the Software.
12
+ #
13
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14
+ # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15
+ # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16
+ # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17
+ # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18
+ # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
19
+ # THE SOFTWARE.
20
+
21
+ opts :quit do
22
+ summary "Exit RVC"
23
+ end
24
+
25
+ rvc_alias :quit
26
+ rvc_alias :quit, :exit
27
+ rvc_alias :quit, :q
28
+
29
+ def quit
30
+ exit
31
+ end
32
+
33
+
34
+ opts :reload do
35
+ summary "Reload RVC command modules and extensions"
36
+ opt :verbose, "Display filenames loaded", :short => 'v', :default => false
37
+ end
38
+
39
+ rvc_alias :reload
40
+
41
+ def reload opts
42
+ old_verbose = $VERBOSE
43
+ $VERBOSE = nil unless opts[:verbose]
44
+
45
+ RVC.reload_modules opts[:verbose]
46
+
47
+ typenames = Set.new(VIM.loader.typenames.select { |x| VIM.const_defined? x })
48
+ dirs = VIM.extension_dirs
49
+ dirs.each do |path|
50
+ Dir.open(path) do |dir|
51
+ dir.each do |file|
52
+ next unless file =~ /\.rb$/
53
+ next unless typenames.member? $`
54
+ file_path = File.join(dir, file)
55
+ puts "loading #{$`} extensions from #{file_path}" if opts[:verbose]
56
+ load file_path
57
+ end
58
+ end
59
+ end
60
+
61
+ ensure
62
+ $VERBOSE = old_verbose
63
+ end
@@ -18,6 +18,8 @@
18
18
  # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
19
19
  # THE SOFTWARE.
20
20
 
21
+ require 'terminal-table/import'
22
+
21
23
  opts :download do
22
24
  summary "Download a file from a datastore"
23
25
  arg 'datastore-path', "Filename on the datastore", :lookup => VIM::Datastore::FakeDatastoreFile
@@ -163,3 +165,159 @@ end
163
165
  def http_path dc_name, ds_name, path
164
166
  "/folder/#{URI.escape path}?dcPath=#{URI.escape dc_name}&dsName=#{URI.escape ds_name}"
165
167
  end
168
+
169
+
170
+ opts :find_orphans do
171
+ summary "Finds directories on the datastore that don't belong to any registered VM"
172
+ arg :datastore, nil, :lookup => VIM::Datastore
173
+ opt :mark, "Name of the mark to save results in", :required => false, :type => :string
174
+ end
175
+
176
+ def find_orphans ds, opts
177
+ pc = ds._connection.serviceContent.propertyCollector
178
+ vms = ds.vm
179
+
180
+ puts "Collecting file information about #{vms.length} VMs ... (this may take a while)"
181
+ dsName = ds.name
182
+ vmFiles = pc.collectMultiple vms, 'layoutEx.file'
183
+
184
+ puts "Collecting file information on datastore '#{dsName}' ..."
185
+ dsBrowser = ds.browser
186
+ result = dsBrowser.SearchDatastore_Task(
187
+ :datastorePath => "[#{dsName}] ",
188
+ :searchSpec => {
189
+ :details => {
190
+ :fileType => true,
191
+ :fileSize => false,
192
+ :fileOwner => false,
193
+ :modification => false
194
+ }
195
+ }
196
+ ).wait_for_completion
197
+ dsDirectories = result.file.grep(RbVmomi::VIM::FolderFileInfo).map(&:path)
198
+
199
+ puts "Checking for any VMs that got added inbetween ..."
200
+ addedVms = ds.vm - vms
201
+ if addedVms.length > 0
202
+ puts "Processing #{addedVms.length} new VMs ..."
203
+ vmFiles.merge!(pc.collectMultiple addedVms, 'layoutEx.file')
204
+ end
205
+
206
+ begin
207
+ perDSUsage = pc.collectMultiple vms, 'storage.perDatastoreUsage'
208
+ rescue RbVmomi::Fault => ex
209
+ if ex.fault.is_a?(RbVmomi::VIM::ManagedObjectNotFound)
210
+ vms = vms - [ex.fault.obj]
211
+ retry
212
+ end
213
+ perDSUsage = []
214
+ raise
215
+ end
216
+
217
+ puts "Cross-referencing VM files with files on datastore '#{dsName}' ..."
218
+ vmFilenameHash = Hash[vmFiles.map do |vm, info|
219
+ [
220
+ vm,
221
+ info["layoutEx.file"].map{|x| x.name}.select{|x| x =~ /^\[#{dsName}\] /}.map{|x| x.gsub(/^\[#{dsName}\] /, '')}
222
+ ]
223
+ end]
224
+ filenames = vmFilenameHash.values.flatten(1)
225
+ vmDirectories = filenames.map{ |x| x.split('/').first }.uniq
226
+ orphanDirectories = (dsDirectories - vmDirectories).reject { |x| x =~ /^\./ }
227
+ puts "Found #{orphanDirectories.length} potentially orphaned directories"
228
+
229
+ puts "Composing list of potentially orphaned files ... (this may take a while)"
230
+ data = orphanDirectories.map do |dir|
231
+ begin
232
+ result = dsBrowser.SearchDatastoreSubFolders_Task(
233
+ :datastorePath => "[#{dsName}] #{dir}/",
234
+ :searchSpec => {
235
+ :details => {
236
+ :fileType => false,
237
+ :fileSize => true,
238
+ :fileOwner => false,
239
+ :modification => false
240
+ }
241
+ }
242
+ ).wait_for_completion
243
+ files = result.map(&:file).flatten
244
+ dirSize = files.map(&:fileSize).sum
245
+ $stdout.write "."
246
+ $stdout.flush
247
+ [dir, dirSize, files.length]
248
+ rescue
249
+ puts "failed to search #{dir.inspect}: #{$!.message}"
250
+ nil
251
+ end
252
+ end.compact
253
+ puts
254
+ puts
255
+
256
+ if data.empty?
257
+ puts "No orphans found"
258
+ else
259
+ puts(table do
260
+ data.sort_by { |a| a[1] }.each do |x|
261
+ dir, dirSize, numFiles = x
262
+ self.headings = 'Directory', 'Space Used', '# Files'
263
+ add_row [dir, "#{dirSize.metric}B", numFiles]
264
+ end
265
+ end)
266
+ end
267
+
268
+ puts
269
+
270
+ totalSize = data.map{|x| x[1]}.sum
271
+ dsSummary = ds.summary
272
+ vmDsUsage = perDSUsage.map{|vm, x| x['storage.perDatastoreUsage'].find{|y| y.datastore == ds}}.reject{|x| x == nil}
273
+ committed = vmDsUsage.map{|x| x.committed}.sum
274
+ unshared = vmDsUsage.map{|x| x.unshared}.sum
275
+ otherSpace = (dsSummary.capacity - dsSummary.freeSpace) - unshared
276
+ puts "Provisioned on Datastore: #{dsSummary.uncommitted.metric}B"
277
+ puts "Capacity of Datastore: #{dsSummary.capacity.metric}B"
278
+ puts "Free Space on Datastore: #{dsSummary.freeSpace.metric}B"
279
+ puts "VMs Provisioned on Datastore: #{vmDsUsage.map(&:uncommitted).sum.metric}B"
280
+ puts "VMs Used on Datastore: #{committed.metric}B"
281
+ puts "VMs Unshared on Datastore: #{vmDsUsage.map(&:unshared).sum.metric}B"
282
+ puts "Unaccounted space: #{otherSpace.metric}B"
283
+ puts "Total size of detected potential orphans: #{totalSize.metric}B"
284
+ puts
285
+
286
+ results = data.map do |dirInfo|
287
+ RbVmomi::VIM::Datastore::FakeDatastoreFolder.new(ds, "#{dirInfo[0]}")
288
+ end
289
+ opts[:mark] ||= "#{dsName}_orphans"
290
+ CMD.mark.mark opts[:mark], results
291
+ puts "Saved results to mark '#{opts[:mark]}'"
292
+
293
+ i = 0
294
+ results.each do |r|
295
+ display_path = r.path
296
+ puts "#{i} #{display_path}"
297
+ CMD.mark.mark i.to_s, [r]
298
+ i += 1
299
+ end
300
+ end
301
+
302
+
303
+ opts :delete do
304
+ summary "Deletes the specified files or folders from the datastore"
305
+ arg :objs, nil, :multi => true, :lookup => RVC::InventoryObject
306
+ end
307
+
308
+ def delete objs
309
+ fm = nil
310
+ tasks = objs.map do |obj|
311
+ isFolder = obj.is_a?(RbVmomi::VIM::Datastore::FakeDatastoreFolder)
312
+ isFile = obj.is_a?(RbVmomi::VIM::Datastore::FakeDatastoreFile)
313
+ err "Parameter is neither file nor folder" if !isFolder && !isFile
314
+
315
+ ds = obj.datastore
316
+ dc = ds.path.find{|x| x[0].is_a? RbVmomi::VIM::Datacenter}[0]
317
+ fm ||= ds._connection.serviceContent.fileManager
318
+ dsPath = "[#{ds.name}] #{obj.path}"
319
+ puts "Deleting #{dsPath}"
320
+ fm.DeleteDatastoreFile_Task(:name => dsPath, :datacenter => dc)
321
+ end
322
+ progress(tasks)
323
+ end
@@ -0,0 +1,275 @@
1
+ opts :connect do
2
+ summary "Connect a virtual device"
3
+ arg :device, nil, :lookup => VIM::VirtualDevice, :multi => true
4
+ end
5
+
6
+ def connect devs
7
+ change_devices_connectivity devs, true
8
+ end
9
+
10
+
11
+ opts :disconnect do
12
+ summary "Disconnect a virtual device"
13
+ arg :device, nil, :lookup => VIM::VirtualDevice, :multi => true
14
+ end
15
+
16
+ def disconnect devs
17
+ change_devices_connectivity devs, false
18
+ end
19
+
20
+
21
+ opts :remove do
22
+ summary "Remove a virtual device"
23
+ arg :device, nil, :lookup => VIM::VirtualDevice, :multi => true
24
+ end
25
+
26
+ def remove devs
27
+ vm_devs = devs.group_by(&:rvc_vm)
28
+ tasks = vm_devs.map do |vm,my_devs|
29
+ device_changes = my_devs.map do |dev|
30
+ fileOp = dev.backing.is_a?(VIM::VirtualDeviceFileBackingInfo) ? 'destroy' : nil
31
+ { :operation => :remove, :fileOperation => fileOp, :device => dev }
32
+ end
33
+ spec = { :deviceChange => device_changes }
34
+ vm.ReconfigVM_Task(:spec => spec)
35
+ end
36
+
37
+ progress tasks
38
+ end
39
+
40
+
41
+ opts :add_net do
42
+ summary "Add a network adapter to a virtual machine"
43
+ arg :vm, nil, :lookup => VIM::VirtualMachine
44
+ arg :network, nil, :lookup => VIM::Network
45
+ opt :type, "Adapter type", :default => 'e1000'
46
+ end
47
+
48
+ NET_DEVICE_CLASSES = {
49
+ 'e1000' => VIM::VirtualE1000,
50
+ 'vmxnet3' => VIM::VirtualVmxnet3,
51
+ }
52
+
53
+ def add_net vm, network, opts
54
+ klass = NET_DEVICE_CLASSES[opts[:type]] or err "unknown network adapter type #{opts[:type].inspect}"
55
+
56
+ case network
57
+ when VIM::DistributedVirtualPortgroup
58
+ switch, pg_key = network.collect 'config.distributedVirtualSwitch', 'key'
59
+ port = VIM.DistributedVirtualSwitchPortConnection(
60
+ :switchUuid => switch.uuid,
61
+ :portgroupKey => pg_key)
62
+ summary = network.name
63
+ backing = VIM.VirtualEthernetCardDistributedVirtualPortBackingInfo(:port => port)
64
+ when VIM::Network
65
+ summary = network.name
66
+ backing = VIM.VirtualEthernetCardNetworkBackingInfo(:deviceName => network.name)
67
+ else fail
68
+ end
69
+
70
+ _add_device vm, nil, klass.new(
71
+ :key => -1,
72
+ :deviceInfo => {
73
+ :summary => summary,
74
+ :label => "",
75
+ },
76
+ :backing => backing,
77
+ :addressType => 'generated'
78
+ )
79
+ end
80
+
81
+
82
+ opts :add_disk do
83
+ summary "Add a hard drive to a virtual machine"
84
+ arg :vm, nil, :lookup => VIM::VirtualMachine
85
+ opt :size, 'Size', :default => '10G'
86
+ opt :controller, 'Virtual controller', :type => :string, :lookup => VIM::VirtualController
87
+ end
88
+
89
+ def add_disk vm, opts
90
+ controller, unit_number = pick_controller vm, opts[:controller], [VIM::VirtualSCSIController, VIM::VirtualIDEController]
91
+ id = "disk-#{controller.key}-#{unit_number}"
92
+ filename = "#{File.dirname(vm.summary.config.vmPathName)}/#{id}.vmdk"
93
+ _add_device vm, :create, VIM::VirtualDisk(
94
+ :key => -1,
95
+ :backing => VIM.VirtualDiskFlatVer2BackingInfo(
96
+ :fileName => filename,
97
+ :diskMode => :persistent,
98
+ :thinProvisioned => true
99
+ ),
100
+ :capacityInKB => MetricNumber.parse(opts[:size]).to_i/1000,
101
+ :controllerKey => controller.key,
102
+ :unitNumber => unit_number
103
+ )
104
+ end
105
+
106
+
107
+ opts :add_cdrom do
108
+ summary "Add a cdrom drive"
109
+ arg :vm, nil, :lookup => VIM::VirtualMachine
110
+ opt :controller, 'Virtual controller', :type => :string, :lookup => VIM::VirtualIDEController
111
+ end
112
+
113
+ def add_cdrom vm, opts
114
+ controller, unit_number = pick_controller vm, opts[:controller], [VIM::VirtualIDEController]
115
+ id = "cdrom-#{controller.key}-#{unit_number}"
116
+ _add_device vm, nil, VIM.VirtualCdrom(
117
+ :controllerKey => controller.key,
118
+ :key => -1,
119
+ :unitNumber => unit_number,
120
+ :backing => VIM.VirtualCdromAtapiBackingInfo(
121
+ :deviceName => id,
122
+ :useAutoDetect => false
123
+ ),
124
+ :connectable => VIM.VirtualDeviceConnectInfo(
125
+ :allowGuestControl => true,
126
+ :connected => true,
127
+ :startConnected => true
128
+ )
129
+ )
130
+ end
131
+
132
+
133
+ opts :insert_cdrom do
134
+ summary "Put a disc in a virtual CDROM drive"
135
+ arg :dev, nil, :lookup => VIM::VirtualDevice
136
+ arg :iso, "Path to the ISO image on a datastore", :lookup => VIM::Datastore::FakeDatastoreFile
137
+ end
138
+
139
+ def insert_cdrom dev, iso
140
+ vm = dev.rvc_vm
141
+ backing = VIM.VirtualCdromIsoBackingInfo(:fileName => iso.datastore_path)
142
+
143
+ spec = {
144
+ :deviceChange => [
145
+ {
146
+ :operation => :edit,
147
+ :device => dev.class.new(
148
+ :key => dev.key,
149
+ :controllerKey => dev.controllerKey,
150
+ :backing => backing)
151
+ }
152
+ ]
153
+ }
154
+
155
+ progress [vm.ReconfigVM_Task(:spec => spec)]
156
+ end
157
+
158
+
159
+ SCSI_CONTROLLER_TYPES = {
160
+ 'pvscsi' => VIM::ParaVirtualSCSIController,
161
+ 'buslogic' => VIM::VirtualBusLogicController,
162
+ 'lsilogic' => VIM::VirtualLsiLogicController,
163
+ 'lsilogic-sas' => VIM::VirtualLsiLogicSASController,
164
+ }
165
+
166
+ SCSI_BUS_NUMBERS = [0, 1, 2, 3]
167
+
168
+ opts :add_scsi_controller do
169
+ summary "Add a virtual SCSI controller to a VM"
170
+ arg :vm, nil, :lookup => VIM::VirtualMachine
171
+ opt :type, SCSI_CONTROLLER_TYPES.keys*'/', :default => 'lsilogic' # TODO tab complete
172
+ opt :sharing, VIM::VirtualSCSISharing.values*'/', :default => 'noSharing' # TODO tab complete
173
+ opt :hot_add, "Enable hot-add/remove", :default => nil
174
+ end
175
+
176
+ def add_scsi_controller vm, opts
177
+ klass = SCSI_CONTROLLER_TYPES[opts[:type]] or err "invalid SCSI controller type #{opts[:type].inspect}"
178
+ err "invalid value for --sharing" unless VIM::VirtualSCSISharing.values.member? opts[:sharing]
179
+
180
+ existing_devices, = vm.collect 'config.hardware.device'
181
+ used_bus_numbers = existing_devices.grep(VIM::VirtualSCSIController).map(&:busNumber)
182
+ bus_number = (SCSI_BUS_NUMBERS - used_bus_numbers).min
183
+ err "unable to allocate a bus number, too many SCSI controllers" unless bus_number
184
+
185
+ controller = klass.new(
186
+ :key => -1,
187
+ :busNumber => bus_number,
188
+ :sharedBus => opts[:sharing],
189
+ :hotAddRemove => opts[:hot_add]
190
+ )
191
+
192
+ _add_device vm, nil, controller
193
+ end
194
+
195
+
196
+ opts :add_serial do
197
+ summary "Add a virtual serial port to a VM"
198
+ arg :vm, nil, :lookup => VIM::VirtualMachine
199
+ end
200
+
201
+ def add_serial vm
202
+ # create an initial no-op backing
203
+ backing = VIM::VirtualSerialPortURIBackingInfo(:direction => :client, :serviceURI => 'localhost:0')
204
+ _add_device vm, nil, VIM::VirtualSerialPort(:yieldOnPoll => true, :key => -1, :backing => backing)
205
+ end
206
+
207
+
208
+ opts :connect_serial_uri do
209
+ summary "Connect a virtual serial port to the given network URI"
210
+ arg :dev, nil, :lookup => VIM::VirtualSerialPort
211
+ arg :uri, "URI", :type => :string
212
+ opt :client, "Connect to another machine", :short => 'c'
213
+ opt :server, "Listen for incoming connections", :short => 's'
214
+ conflicts :client, :server
215
+ end
216
+
217
+ def connect_serial_uri dev, uri, opts
218
+ err "must specify --client or --server" unless opts[:client] || opts[:server]
219
+ direction = opts[:client] ? 'client' : 'server'
220
+ dev = dev.dup
221
+ dev.backing = VIM::VirtualSerialPortURIBackingInfo(:direction => direction, :serviceURI => uri)
222
+ spec = { :deviceChange => [ { :operation => :edit, :device => dev } ] }
223
+ progress [dev.rvc_vm.ReconfigVM_Task(:spec => spec)]
224
+ end
225
+
226
+
227
+ def _add_device vm, fileOp, dev
228
+ spec = {
229
+ :deviceChange => [
230
+ { :operation => :add, :fileOperation => fileOp, :device => dev },
231
+ ]
232
+ }
233
+ task = vm.ReconfigVM_Task(:spec => spec)
234
+ result = progress([task])[task]
235
+ if result == nil
236
+ new_device = vm.collect('config.hardware.device')[0].grep(dev.class).last
237
+ puts "Added device #{new_device.name}"
238
+ end
239
+ end
240
+
241
+ def change_devices_connectivity devs, connected
242
+ if dev = devs.find { |dev| dev.connectable.nil? }
243
+ err "#{dev.name} is not connectable."
244
+ end
245
+
246
+ vm_devs = devs.group_by(&:rvc_vm)
247
+ tasks = vm_devs.map do |vm,my_devs|
248
+ device_changes = my_devs.map do |dev|
249
+ dev = dev.dup
250
+ dev.connectable = dev.connectable.dup
251
+ dev.connectable.connected = connected
252
+ dev.connectable.startConnected = connected
253
+ { :operation => :edit, :device => dev }
254
+ end
255
+ spec = { :deviceChange => device_changes }
256
+ vm.ReconfigVM_Task(:spec => spec)
257
+ end
258
+
259
+ progress tasks
260
+ end
261
+
262
+ def pick_controller vm, controller, controller_classes
263
+ existing_devices, = vm.collect 'config.hardware.device'
264
+
265
+ controller ||= existing_devices.find do |dev|
266
+ controller_classes.any? { |klass| dev.is_a? klass } &&
267
+ dev.device.length < 2
268
+ end
269
+ err "no suitable controller found" unless controller
270
+
271
+ used_unit_numbers = existing_devices.select { |dev| dev.controllerKey == controller.key }.map(&:unitNumber)
272
+ unit_number = (used_unit_numbers.max||-1) + 1
273
+
274
+ [controller, unit_number]
275
+ end