knife-vsphere 1.0.1 → 1.2.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 (39) hide show
  1. checksums.yaml +4 -4
  2. data/lib/chef/knife/base_vsphere_command.rb +383 -371
  3. data/lib/chef/knife/customization_helper.rb +40 -0
  4. data/lib/chef/knife/vsphere_cluster_list.rb +47 -0
  5. data/lib/chef/knife/vsphere_cpu_ratio.rb +41 -45
  6. data/lib/chef/knife/vsphere_customization_list.rb +24 -29
  7. data/lib/chef/knife/vsphere_datastore_list.rb +68 -72
  8. data/lib/chef/knife/vsphere_datastore_maxfree.rb +48 -49
  9. data/lib/chef/knife/vsphere_datastorecluster_list.rb +66 -71
  10. data/lib/chef/knife/vsphere_datastorecluster_maxfree.rb +75 -84
  11. data/lib/chef/knife/vsphere_folder_list.rb +28 -30
  12. data/lib/chef/knife/vsphere_hosts_list.rb +42 -42
  13. data/lib/chef/knife/vsphere_pool_list.rb +46 -48
  14. data/lib/chef/knife/vsphere_pool_query.rb +58 -58
  15. data/lib/chef/knife/vsphere_template_list.rb +30 -32
  16. data/lib/chef/knife/vsphere_vlan_create.rb +51 -0
  17. data/lib/chef/knife/vsphere_vlan_list.rb +35 -37
  18. data/lib/chef/knife/vsphere_vm_clone.rb +834 -581
  19. data/lib/chef/knife/vsphere_vm_config.rb +48 -46
  20. data/lib/chef/knife/vsphere_vm_delete.rb +70 -66
  21. data/lib/chef/knife/vsphere_vm_execute.rb +62 -66
  22. data/lib/chef/knife/vsphere_vm_list.rb +57 -61
  23. data/lib/chef/knife/vsphere_vm_markastemplate.rb +48 -54
  24. data/lib/chef/knife/vsphere_vm_migrate.rb +73 -0
  25. data/lib/chef/knife/vsphere_vm_move.rb +88 -0
  26. data/lib/chef/knife/vsphere_vm_net.rb +57 -0
  27. data/lib/chef/knife/vsphere_vm_property_get.rb +44 -46
  28. data/lib/chef/knife/vsphere_vm_property_set.rb +83 -84
  29. data/lib/chef/knife/vsphere_vm_query.rb +48 -48
  30. data/lib/chef/knife/vsphere_vm_snapshot.rb +124 -130
  31. data/lib/chef/knife/vsphere_vm_state.rb +122 -127
  32. data/lib/chef/knife/vsphere_vm_toolsconfig.rb +54 -52
  33. data/lib/chef/knife/vsphere_vm_vmdk_add.rb +234 -241
  34. data/lib/chef/knife/vsphere_vm_wait_sysprep.rb +54 -0
  35. data/lib/knife-vsphere/version.rb +3 -4
  36. metadata +43 -15
  37. data/lib/chef/knife/vshpere_vm_migrate.rb +0 -80
  38. data/lib/chef/knife/vshpere_vm_move.rb +0 -92
  39. data/lib/chef/knife/vshpere_vm_net.rb +0 -57
@@ -1,52 +1,54 @@
1
- # Author:: Malte Heidenreich
2
- # License:: Apache License, Version 2.0
3
-
4
- require 'chef/knife'
5
- require 'chef/knife/base_vsphere_command'
6
- require 'rbvmomi'
7
- require 'netaddr'
8
-
9
- class Chef::Knife::VsphereVmToolsconfig < Chef::Knife::BaseVsphereCommand
10
- banner "knife vsphere vm toolsconfig PROPERTY VALUE. See \"https://www.vmware.com/support/developer/vc-sdk/visdk25pubs/ReferenceGuide/vim.vm.ToolsConfigInfo.html\" for available properties and types."
11
-
12
- option :empty,
13
- :short => "-e",
14
- :long => "--empty",
15
- :description => "Allow empty string"
16
- get_common_options
17
-
18
- def run
19
- $stdout.sync = true
20
- vmname = @name_args[0]
21
- if vmname.nil?
22
- show_usage
23
- fatal_exit("You must specify a virtual machine name")
24
- end
25
-
26
- property = @name_args[1]
27
- if property.nil?
28
- show_usage
29
- fatal_exit("You must specify a property to modify")
30
- end
31
-
32
- value = @name_args[2]
33
- if value.nil? && !get_config(:empty)
34
- show_usage
35
- fatal_exit("You must specify a value")
36
- end
37
-
38
- value = "" if get_config(:empty)
39
-
40
- vim = get_vim_connection
41
-
42
- dc = get_datacenter
43
- folder = find_folder(get_config(:folder)) || dc.vmFolder
44
-
45
- vm = traverse_folders_for_vm(folder, vmname) or abort "VM #{vmname} not found"
46
-
47
- vmConfigSpec = RbVmomi::VIM.VirtualMachineConfigSpec(:tools => RbVmomi::VIM.ToolsConfigInfo(property => value))
48
- vm.ReconfigVM_Task(:spec => vmConfigSpec)
49
-
50
- puts "property #{property} updated successfully"
51
- end
52
- end
1
+ # Author:: Malte Heidenreich
2
+ # License:: Apache License, Version 2.0
3
+
4
+ require 'chef/knife'
5
+ require 'chef/knife/base_vsphere_command'
6
+ require 'rbvmomi'
7
+ require 'netaddr'
8
+
9
+ class Chef::Knife::VsphereVmToolsconfig < Chef::Knife::BaseVsphereCommand
10
+ banner "knife vsphere vm toolsconfig PROPERTY VALUE.
11
+ See \"https://www.vmware.com/support/developer/vc-sdk/visdk25pubs/ReferenceGuide/vim.vm.ToolsConfigInfo.html\"
12
+ for available properties and types."
13
+
14
+ option :empty,
15
+ short: '-e',
16
+ long: '--empty',
17
+ description: 'Allow empty string'
18
+ common_options
19
+
20
+ def run
21
+ $stdout.sync = true
22
+ vmname = @name_args[0]
23
+ if vmname.nil?
24
+ show_usage
25
+ fatal_exit('You must specify a virtual machine name')
26
+ end
27
+
28
+ property = @name_args[1]
29
+ if property.nil?
30
+ show_usage
31
+ fatal_exit('You must specify a property to modify')
32
+ end
33
+
34
+ value = @name_args[2]
35
+ if value.nil? && !get_config(:empty)
36
+ show_usage
37
+ fatal_exit('You must specify a value')
38
+ end
39
+
40
+ value = '' if get_config(:empty)
41
+
42
+ vim_connection
43
+
44
+ dc = datacenter
45
+ folder = find_folder(get_config(:folder)) || dc.vmFolder
46
+
47
+ vm = traverse_folders_for_vm(folder, vmname) || abort("VM #{vmname} not found")
48
+
49
+ vm_config_spec = RbVmomi::VIM.VirtualMachineConfigSpec(tools: RbVmomi::VIM.ToolsConfigInfo(property => value))
50
+ vm.ReconfigVM_Task(spec: vm_config_spec)
51
+
52
+ puts "property #{property} updated successfully"
53
+ end
54
+ end
@@ -1,241 +1,234 @@
1
- #
2
- # Author:: Brian Flad (<bflad417@gmail.com>)
3
- # License:: Apache License, Version 2.0
4
- #
5
- require 'chef/knife'
6
- require 'chef/knife/base_vsphere_command'
7
-
8
- # Lists all known virtual machines in the configured datacenter
9
- class Chef::Knife::VsphereVmVmdkAdd < Chef::Knife::BaseVsphereCommand
10
-
11
- banner "knife vsphere vm vmdk add"
12
-
13
- get_common_options
14
-
15
- option :vmdk_type,
16
- :long => "--vmdk-type TYPE",
17
- :description => "Type of VMDK"
18
- # this is a bad idea as it will let you overcommit SAN by 400% or more. thick is a more "sane" default
19
- $default[:vmdk_type] = "thin"
20
-
21
- option :target_lun,
22
- :long => "--target-lun NAME",
23
- :description => "name of target LUN"
24
-
25
- def run
26
- $stdout.sync = true
27
-
28
- vmname = @name_args[0]
29
- if vmname.nil?
30
- show_usage
31
- ui.fatal("You must specify a virtual machine name")
32
- exit 1
33
- end
34
-
35
- size = @name_args[1]
36
- if size.nil?
37
- ui.fatal "You need a VMDK size!"
38
- show_usage
39
- exit 1
40
- end
41
-
42
- vim = get_vim_connection
43
- vdm = vim.serviceContent.virtualDiskManager
44
- vm = get_vm(vmname)
45
- if vm.nil?
46
- puts "Could not find #{vmname}"
47
- return
48
- end
49
-
50
- target_lun = get_config(:target_lun) unless get_config(:target_lun).nil?
51
- vmdk_size_kb = size.to_i * 1024 * 1024
52
-
53
- if target_lun.nil?
54
- vmdk_datastore = choose_datastore(vm.datastore, size)
55
- exit -1 if vmdk_datastore.nil?
56
- else
57
- vmdk_datastores = find_datastores_regex(target_lun)
58
- vmdk_datastore = choose_datastore(vmdk_datastores, size)
59
- exit -1 if vmdk_datastore.nil?
60
- vmdk_dir = "[#{vmdk_datastore.name}] #{vmname}"
61
- # create the vm folder on the LUN or subsequent operations will fail.
62
- if not vmdk_datastore.exists? vmname
63
- dc = get_datacenter
64
- dc._connection.serviceContent.fileManager.MakeDirectory :name => vmdk_dir, :datacenter => dc, :createParentDirectories => false
65
- end
66
- end
67
-
68
- puts "Choosing: #{vmdk_datastore.name}"
69
-
70
- # now we need to inspect the files in this datastore to get our next file name
71
- next_vmdk = 1
72
- pc = vmdk_datastore._connection.serviceContent.propertyCollector
73
- vms = vmdk_datastore.vm
74
- vmFiles = pc.collectMultiple vms, 'layoutEx.file'
75
- vmFiles.keys.each do |vm|
76
- vmFiles[vm]["layoutEx.file"].each do |layout|
77
- if layout.name.match(/^\[#{vmdk_datastore.name}\] #{vmname}\/#{vmname}_([0-9]+).vmdk/)
78
- num = $1
79
- if next_vmdk <= num.to_i
80
- next_vmdk = num.to_i + 1
81
- end
82
- end
83
- end
84
- end
85
- vmdk_fileName = "#{vmname}/#{vmname}_#{next_vmdk}.vmdk"
86
- vmdk_name = "[#{vmdk_datastore.name}] #{vmdk_fileName}"
87
- vmdk_type = get_config(:vmdk_type)
88
- vmdk_type = "preallocated" if vmdk_type == "thick"
89
- puts "Next vmdk name is => #{vmdk_name}"
90
-
91
- # create the disk
92
- if not vmdk_datastore.exists? vmdk_fileName
93
- vmdk_spec = RbVmomi::VIM::FileBackedVirtualDiskSpec(
94
- :adapterType => "lsiLogic",
95
- :capacityKb => vmdk_size_kb,
96
- :diskType => vmdk_type
97
- )
98
- ui.info "Creating VMDK"
99
- ui.info "#{ui.color "Capacity:", :cyan} #{size} GB"
100
- ui.info "#{ui.color "Disk:", :cyan} #{vmdk_name}"
101
-
102
- if get_config(:noop)
103
- ui.info "#{ui.color "Skipping disk creation process because --noop specified.", :red}"
104
- else
105
- vdm.CreateVirtualDisk_Task(
106
- :datacenter => get_datacenter,
107
- :name => vmdk_name,
108
- :spec => vmdk_spec
109
- ).wait_for_completion
110
- end
111
- end
112
- ui.info "Attaching VMDK to #{vmname}"
113
-
114
- # now we run through the SCSI controllers to see if there's an available one
115
- available_controllers = Array.new()
116
- use_controller = nil
117
- scsi_tree = Hash.new()
118
- vm.config.hardware.device.each do |device|
119
- if device.is_a? RbVmomi::VIM::VirtualSCSIController
120
- if scsi_tree[device.controllerKey].nil?
121
- scsi_tree[device.key]=Hash.new()
122
- scsi_tree[device.key]['children'] = Array.new();
123
- end
124
- scsi_tree[device.key]['device'] = device;
125
- end
126
- if device.class == RbVmomi::VIM::VirtualDisk
127
- if scsi_tree[device.controllerKey].nil?
128
- scsi_tree[device.controllerKey]=Hash.new()
129
- scsi_tree[device.controllerKey]['children'] = Array.new();
130
- end
131
- scsi_tree[device.controllerKey]['children'].push(device)
132
- end
133
- end
134
- scsi_tree.keys.sort.each do |controller|
135
- if scsi_tree[controller]['children'].length < 15
136
- available_controllers.push(scsi_tree[controller]['device'].deviceInfo.label)
137
- end
138
- end
139
-
140
- if available_controllers.length > 0
141
- use_controller = available_controllers[0]
142
- puts "using #{use_controller}"
143
- else
144
-
145
- if scsi_tree.keys.length < 4
146
-
147
- # Add a controller if none are available
148
- puts "no controllers available. Will attempt to create"
149
- new_scsi_key = scsi_tree.keys.sort[scsi_tree.length - 1] + 1
150
- new_scsi_busNumber = scsi_tree[scsi_tree.keys.sort[scsi_tree.length - 1]]['device'].busNumber + 1
151
-
152
- controller_device = RbVmomi::VIM::VirtualLsiLogicController(
153
- :key => new_scsi_key,
154
- :busNumber => new_scsi_busNumber,
155
- :sharedBus => :noSharing
156
- )
157
-
158
- device_config_spec = RbVmomi::VIM::VirtualDeviceConfigSpec(
159
- :device => controller_device,
160
- :operation => RbVmomi::VIM::VirtualDeviceConfigSpecOperation("add")
161
- )
162
-
163
- vm_config_spec = RbVmomi::VIM::VirtualMachineConfigSpec(
164
- :deviceChange => [device_config_spec]
165
- )
166
-
167
- if get_config(:noop)
168
- ui.info "#{ui.color "Skipping controller creation process because --noop specified.", :red}"
169
- else
170
- vm.ReconfigVM_Task(:spec => vm_config_spec).wait_for_completion
171
- end
172
- else
173
- ui.info "Controllers maxed out at 4."
174
- exit -1
175
- end
176
- end
177
-
178
- # now go back and get the new device's name
179
- vm.config.hardware.device.each do |device|
180
- if device.class == RbVmomi::VIM::VirtualLsiLogicController
181
- if device.key == new_scsi_key
182
- use_controller = device.deviceInfo.label
183
- end
184
- end
185
- end
186
-
187
- # add the disk
188
- controller = find_device(vm, use_controller)
189
-
190
- used_unitNumbers = Array.new()
191
- scsi_tree.keys.sort.each do |c|
192
- if controller.key == scsi_tree[c]['device'].key
193
- used_unitNumbers.push(scsi_tree[c]['device'].scsiCtlrUnitNumber)
194
- scsi_tree[c]['children'].each do |disk|
195
- used_unitNumbers.push(disk.unitNumber)
196
- end
197
- end
198
- end
199
-
200
- available_unitNumbers = Array.new
201
- (0 .. 15).each do |scsi_id|
202
- if used_unitNumbers.grep(scsi_id).length > 0
203
- else
204
- available_unitNumbers.push(scsi_id)
205
- end
206
- end
207
-
208
- # ensure we don't try to add the controllers SCSI ID
209
- new_unitNumber = available_unitNumbers.sort[0]
210
- puts "using SCSI ID #{new_unitNumber}"
211
-
212
- vmdk_backing = RbVmomi::VIM::VirtualDiskFlatVer2BackingInfo(
213
- :datastore => vmdk_datastore,
214
- :diskMode => "persistent",
215
- :fileName => vmdk_name
216
- )
217
-
218
- device = RbVmomi::VIM::VirtualDisk(
219
- :backing => vmdk_backing,
220
- :capacityInKB => vmdk_size_kb,
221
- :controllerKey => controller.key,
222
- :key => -1,
223
- :unitNumber => new_unitNumber
224
- )
225
-
226
- device_config_spec = RbVmomi::VIM::VirtualDeviceConfigSpec(
227
- :device => device,
228
- :operation => RbVmomi::VIM::VirtualDeviceConfigSpecOperation("add")
229
- )
230
-
231
- vm_config_spec = RbVmomi::VIM::VirtualMachineConfigSpec(
232
- :deviceChange => [device_config_spec]
233
- )
234
-
235
- if get_config(:noop)
236
- ui.info "#{ui.color "Skipping disk attaching process because --noop specified.", :red}"
237
- else
238
- vm.ReconfigVM_Task(:spec => vm_config_spec).wait_for_completion
239
- end
240
- end
241
- end
1
+ #
2
+ # Author:: Brian Flad (<bflad417@gmail.com>)
3
+ # License:: Apache License, Version 2.0
4
+ #
5
+ require 'chef/knife'
6
+ require 'chef/knife/base_vsphere_command'
7
+
8
+ # Add a new disk to a virtual machine
9
+ class Chef::Knife::VsphereVmVmdkAdd < Chef::Knife::BaseVsphereCommand
10
+ banner 'knife vsphere vm vmdk add VMNAME DISK_GB'
11
+
12
+ common_options
13
+
14
+ option :vmdk_type,
15
+ long: '--vmdk-type TYPE',
16
+ description: 'Type of VMDK',
17
+ # this is a bad idea as it will let you overcommit SAN by 400% or more. thick is a more "sane" default
18
+ default: 'thin'
19
+
20
+ option :target_lun,
21
+ long: '--target-lun NAME',
22
+ description: 'name of target LUN'
23
+
24
+ def run
25
+ $stdout.sync = true
26
+
27
+ vmname = @name_args[0]
28
+ if vmname.nil?
29
+ show_usage
30
+ ui.fatal('You must specify a virtual machine name')
31
+ exit 1
32
+ end
33
+
34
+ size = @name_args[1]
35
+ if size.nil?
36
+ ui.fatal 'You need a VMDK size!'
37
+ show_usage
38
+ exit 1
39
+ end
40
+
41
+ vim = vim_connection
42
+ vdm = vim.serviceContent.virtualDiskManager
43
+ vm = get_vm(vmname)
44
+ if vm.nil?
45
+ puts "Could not find #{vmname}"
46
+ return
47
+ end
48
+
49
+ target_lun = get_config(:target_lun) unless get_config(:target_lun).nil?
50
+ vmdk_size_kb = size.to_i * 1024 * 1024
51
+
52
+ if target_lun.nil?
53
+ vmdk_datastore = choose_datastore(vm.datastore, size)
54
+ exit(-1) if vmdk_datastore.nil?
55
+ else
56
+ vmdk_datastores = find_datastores_regex(target_lun)
57
+ vmdk_datastore = choose_datastore(vmdk_datastores, size)
58
+ exit(-1) if vmdk_datastore.nil?
59
+ vmdk_dir = "[#{vmdk_datastore.name}] #{vmname}"
60
+ # create the vm folder on the LUN or subsequent operations will fail.
61
+ unless vmdk_datastore.exists? vmname
62
+ dc = datacenter
63
+ dc._connection.serviceContent.fileManager.MakeDirectory name: vmdk_dir, datacenter: dc, createParentDirectories: false
64
+ end
65
+ end
66
+
67
+ puts "Choosing: #{vmdk_datastore.name}"
68
+
69
+ # now we need to inspect the files in this datastore to get our next file name
70
+ next_vmdk = 1
71
+ pc = vmdk_datastore._connection.serviceContent.propertyCollector
72
+ vms = vmdk_datastore.vm
73
+ vm_files = pc.collectMultiple vms, 'layoutEx.file'
74
+ vm_files.keys.each do |vmFile|
75
+ vm_files[vmFile]['layoutEx.file'].each do |layout|
76
+ if layout.name.match(/^\[#{vmdk_datastore.name}\] #{vmname}\/#{vmname}_([0-9]+).vmdk/)
77
+ num = Regexp.last_match(1)
78
+ next_vmdk = num.to_i + 1 if next_vmdk <= num.to_i
79
+ end
80
+ end
81
+ end
82
+ vmdk_file_name = "#{vmname}/#{vmname}_#{next_vmdk}.vmdk"
83
+ vmdk_name = "[#{vmdk_datastore.name}] #{vmdk_file_name}"
84
+ vmdk_type = get_config(:vmdk_type)
85
+ vmdk_type = 'preallocated' if vmdk_type == 'thick'
86
+ puts "Next vmdk name is => #{vmdk_name}"
87
+
88
+ # create the disk
89
+ unless vmdk_datastore.exists? vmdk_file_name
90
+ vmdk_spec = RbVmomi::VIM::FileBackedVirtualDiskSpec(
91
+ adapterType: 'lsiLogic',
92
+ capacityKb: vmdk_size_kb,
93
+ diskType: vmdk_type
94
+ )
95
+ ui.info 'Creating VMDK'
96
+ ui.info "#{ui.color 'Capacity:', :cyan} #{size} GB"
97
+ ui.info "#{ui.color 'Disk:', :cyan} #{vmdk_name}"
98
+
99
+ if get_config(:noop)
100
+ ui.info "#{ui.color 'Skipping disk creation process because --noop specified.', :red}"
101
+ else
102
+ vdm.CreateVirtualDisk_Task(
103
+ datacenter: datacenter,
104
+ name: vmdk_name,
105
+ spec: vmdk_spec
106
+ ).wait_for_completion
107
+ end
108
+ end
109
+ ui.info "Attaching VMDK to #{vmname}"
110
+
111
+ # now we run through the SCSI controllers to see if there's an available one
112
+ available_controllers = []
113
+ use_controller = nil
114
+ scsi_tree = {}
115
+ vm.config.hardware.device.each do |device|
116
+ if device.is_a? RbVmomi::VIM::VirtualSCSIController
117
+ if scsi_tree[device.controllerKey].nil?
118
+ scsi_tree[device.key] = {}
119
+ scsi_tree[device.key]['children'] = []
120
+ end
121
+ scsi_tree[device.key]['device'] = device
122
+ end
123
+ next unless device.class == RbVmomi::VIM::VirtualDisk
124
+ if scsi_tree[device.controllerKey].nil?
125
+ scsi_tree[device.controllerKey] = {}
126
+ scsi_tree[device.controllerKey]['children'] = []
127
+ end
128
+ scsi_tree[device.controllerKey]['children'].push(device)
129
+ end
130
+ scsi_tree.keys.sort.each do |controller|
131
+ if scsi_tree[controller]['children'].length < 15
132
+ available_controllers.push(scsi_tree[controller]['device'].deviceInfo.label)
133
+ end
134
+ end
135
+
136
+ if available_controllers.length > 0
137
+ use_controller = available_controllers[0]
138
+ puts "using #{use_controller}"
139
+ else
140
+
141
+ if scsi_tree.keys.length < 4
142
+
143
+ # Add a controller if none are available
144
+ puts 'no controllers available. Will attempt to create'
145
+ new_scsi_key = scsi_tree.keys.sort[scsi_tree.length - 1] + 1
146
+ new_scsi_bus_number = scsi_tree[scsi_tree.keys.sort[scsi_tree.length - 1]]['device'].busNumber + 1
147
+
148
+ controller_device = RbVmomi::VIM::VirtualLsiLogicController(
149
+ key: new_scsi_key,
150
+ busNumber: new_scsi_bus_number,
151
+ sharedBus: :noSharing
152
+ )
153
+
154
+ device_config_spec = RbVmomi::VIM::VirtualDeviceConfigSpec(
155
+ device: controller_device,
156
+ operation: RbVmomi::VIM::VirtualDeviceConfigSpecOperation('add')
157
+ )
158
+
159
+ vm_config_spec = RbVmomi::VIM::VirtualMachineConfigSpec(
160
+ deviceChange: [device_config_spec]
161
+ )
162
+
163
+ if get_config(:noop)
164
+ ui.info "#{ui.color 'Skipping controller creation process because --noop specified.', :red}"
165
+ else
166
+ vm.ReconfigVM_Task(spec: vm_config_spec).wait_for_completion
167
+ end
168
+ else
169
+ ui.info 'Controllers maxed out at 4.'
170
+ exit(-1)
171
+ end
172
+ end
173
+
174
+ # now go back and get the new device's name
175
+ vm.config.hardware.device.each do |device|
176
+ if device.class == RbVmomi::VIM::VirtualLsiLogicController
177
+ use_controller = device.deviceInfo.label if device.key == new_scsi_key
178
+ end
179
+ end
180
+
181
+ # add the disk
182
+ controller = find_device(vm, use_controller)
183
+
184
+ used_unit_numbers = []
185
+ scsi_tree.keys.sort.each do |c|
186
+ next unless controller.key == scsi_tree[c]['device'].key
187
+ used_unit_numbers.push(scsi_tree[c]['device'].scsiCtlrUnitNumber)
188
+ scsi_tree[c]['children'].each do |disk|
189
+ used_unit_numbers.push(disk.unitNumber)
190
+ end
191
+ end
192
+
193
+ available_unit_numbers = []
194
+ (0..15).each do |scsi_id|
195
+ if used_unit_numbers.grep(scsi_id).length > 0
196
+ else
197
+ available_unit_numbers.push(scsi_id)
198
+ end
199
+ end
200
+
201
+ # ensure we don't try to add the controllers SCSI ID
202
+ new_unit_number = available_unit_numbers.sort[0]
203
+ puts "using SCSI ID #{new_unit_number}"
204
+
205
+ vmdk_backing = RbVmomi::VIM::VirtualDiskFlatVer2BackingInfo(
206
+ datastore: vmdk_datastore,
207
+ diskMode: 'persistent',
208
+ fileName: vmdk_name
209
+ )
210
+
211
+ device = RbVmomi::VIM::VirtualDisk(
212
+ backing: vmdk_backing,
213
+ capacityInKB: vmdk_size_kb,
214
+ controllerKey: controller.key,
215
+ key: -1,
216
+ unitNumber: new_unit_number
217
+ )
218
+
219
+ device_config_spec = RbVmomi::VIM::VirtualDeviceConfigSpec(
220
+ device: device,
221
+ operation: RbVmomi::VIM::VirtualDeviceConfigSpecOperation('add')
222
+ )
223
+
224
+ vm_config_spec = RbVmomi::VIM::VirtualMachineConfigSpec(
225
+ deviceChange: [device_config_spec]
226
+ )
227
+
228
+ if get_config(:noop)
229
+ ui.info "#{ui.color 'Skipping disk attaching process because --noop specified.', :red}"
230
+ else
231
+ vm.ReconfigVM_Task(spec: vm_config_spec).wait_for_completion
232
+ end
233
+ end
234
+ end