rvc 1.5.0 → 1.6.0

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