knife-vsphere 0.9.9 → 1.0.0.pre

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.
@@ -1,111 +1,127 @@
1
- #
2
- # Author:: Ezra Pagel (<ezra@cpan.org>)
3
- # License:: Apache License, Version 2.0
4
- #
5
-
6
- require 'chef/knife'
7
- require 'chef/knife/base_vsphere_command'
8
- require 'rbvmomi'
9
- require 'netaddr'
10
-
11
- PsOn = 'poweredOn'
12
- PsOff = 'poweredOff'
13
- PsSuspended = 'suspended'
14
-
15
- PowerStates = {
16
- PsOn => 'powered on',
17
- PsOff => 'powered off',
18
- PsSuspended => 'suspended'
19
- }
20
-
21
- # Manage power state of a virtual machine
22
- class Chef::Knife::VsphereVmState < Chef::Knife::BaseVsphereCommand
23
-
24
- banner "knife vsphere vm state VMNAME (options)"
25
-
26
- get_common_options
27
-
28
- option :state,
29
- :short => "-s STATE",
30
- :long => "--state STATE",
31
- :description => "The power state to transition the VM into; one of on|off|suspended"
32
-
33
- option :wait_port,
34
- :short => "-w PORT",
35
- :long => "--wait-port PORT",
36
- :description => "Wait for VM to be accessible on a port"
37
-
38
- option :shutdown,
39
- :short => "-g",
40
- :long => "--shutdown",
41
- :description => "Guest OS shutdown"
42
-
43
- def run
44
-
45
- $stdout.sync = true
46
-
47
- vmname = @name_args[0]
48
- if vmname.nil?
49
- show_usage
50
- ui.fatal("You must specify a virtual machine name")
51
- exit 1
52
- end
53
-
54
- vim = get_vim_connection
55
-
56
- baseFolder = find_folder(get_config(:folder));
57
-
58
- vm = find_in_folder(baseFolder, RbVmomi::VIM::VirtualMachine, vmname) or
59
- abort "VM #{vmname} not found"
60
-
61
- state = vm.runtime.powerState
62
-
63
- if config[:state].nil?
64
- puts "VM #{vmname} is currently " + PowerStates[vm.runtime.powerState]
65
- else
66
-
67
- case config[:state]
68
- when 'on'
69
- if state == PsOn
70
- puts "Virtual machine #{vmname} was already powered on"
71
- else
72
- vm.PowerOnVM_Task.wait_for_completion
73
- puts "Powered on virtual machine #{vmname}"
74
- end
75
- when 'off'
76
- if state == PsOff
77
- puts "Virtual machine #{vmname} was already powered off"
78
- else
79
- if get_config(:shutdown)
80
- vm.ShutdownGuest
81
- print "Waiting for virtual machine #{vmname} to shut down..."
82
- until vm.runtime.powerState == PsOff do
83
- sleep 2
84
- print "."
85
- end
86
- puts "done"
87
- else
88
- vm.PowerOffVM_Task.wait_for_completion
89
- puts "Powered off virtual machine #{vmname}"
90
- end
91
- end
92
- when 'suspend'
93
- if state == PowerStates['suspended']
94
- puts "Virtual machine #{vmname} was already suspended"
95
- else
96
- vm.SuspendVM_Task.wait_for_completion
97
- puts "Suspended virtual machine #{vmname}"
98
- end
99
- when 'reset'
100
- vm.ResetVM_Task.wait_for_completion
101
- puts "Reset virtual machine #{vmname}"
102
- end
103
-
104
- if get_config(:wait_port)
105
- print "Waiting for port #{get_config(:wait_port)}..."
106
- print "." until tcp_test_port_vm(vm, get_config(:wait_port))
107
- puts "done"
108
- end
109
- end
110
- end
111
- end
1
+ #
2
+ # Author:: Ezra Pagel (<ezra@cpan.org>)
3
+ # License:: Apache License, Version 2.0
4
+ #
5
+
6
+ require 'chef/knife'
7
+ require 'chef/knife/base_vsphere_command'
8
+ require 'rbvmomi'
9
+ require 'netaddr'
10
+
11
+ PsOn = 'poweredOn'
12
+ PsOff = 'poweredOff'
13
+ PsSuspended = 'suspended'
14
+
15
+ PowerStates = {
16
+ PsOn => 'powered on',
17
+ PsOff => 'powered off',
18
+ PsSuspended => 'suspended'
19
+ }
20
+
21
+ # Manage power state of a virtual machine
22
+ class Chef::Knife::VsphereVmState < Chef::Knife::BaseVsphereCommand
23
+
24
+ banner "knife vsphere vm state VMNAME (options)"
25
+
26
+ get_common_options
27
+
28
+ option :state,
29
+ :short => "-s STATE",
30
+ :long => "--state STATE",
31
+ :description => "The power state to transition the VM into; one of on|off|suspended"
32
+
33
+ option :wait_port,
34
+ :short => "-w PORT",
35
+ :long => "--wait-port PORT",
36
+ :description => "Wait for VM to be accessible on a port"
37
+
38
+ option :shutdown,
39
+ :short => "-g",
40
+ :long => "--shutdown",
41
+ :description => "Guest OS shutdown"
42
+
43
+ option :recursive,
44
+ :short => "-r",
45
+ :long => "--recursive",
46
+ :description => "Search all folders"
47
+
48
+ def run
49
+
50
+ $stdout.sync = true
51
+
52
+ vmname = @name_args[0]
53
+ if vmname.nil?
54
+ show_usage
55
+ ui.fatal("You must specify a virtual machine name")
56
+ exit 1
57
+ end
58
+
59
+ vim = get_vim_connection
60
+
61
+ if get_config(:recursive)
62
+ vms = get_vms(vmname)
63
+ if vms.length > 1
64
+ abort "More than one VM with name #{vmname} found:\n" + vms.map{ |vm| get_path_to_object(vm) }.join("\n")
65
+ end
66
+ if vms.length == 0
67
+ abort "VM #{vmname} not found"
68
+ end
69
+ vm = vms[0]
70
+ else
71
+ baseFolder = find_folder(get_config(:folder));
72
+
73
+ vm = find_in_folder(baseFolder, RbVmomi::VIM::VirtualMachine, vmname) or
74
+ abort "VM #{vmname} not found"
75
+ end
76
+
77
+ state = vm.runtime.powerState
78
+
79
+ if config[:state].nil?
80
+ puts "VM #{vmname} is currently " + PowerStates[vm.runtime.powerState]
81
+ else
82
+
83
+ case config[:state]
84
+ when 'on'
85
+ if state == PsOn
86
+ puts "Virtual machine #{vmname} was already powered on"
87
+ else
88
+ vm.PowerOnVM_Task.wait_for_completion
89
+ puts "Powered on virtual machine #{vmname}"
90
+ end
91
+ when 'off'
92
+ if state == PsOff
93
+ puts "Virtual machine #{vmname} was already powered off"
94
+ else
95
+ if get_config(:shutdown)
96
+ vm.ShutdownGuest
97
+ print "Waiting for virtual machine #{vmname} to shut down..."
98
+ until vm.runtime.powerState == PsOff do
99
+ sleep 2
100
+ print "."
101
+ end
102
+ puts "done"
103
+ else
104
+ vm.PowerOffVM_Task.wait_for_completion
105
+ puts "Powered off virtual machine #{vmname}"
106
+ end
107
+ end
108
+ when 'suspend'
109
+ if state == PowerStates['suspended']
110
+ puts "Virtual machine #{vmname} was already suspended"
111
+ else
112
+ vm.SuspendVM_Task.wait_for_completion
113
+ puts "Suspended virtual machine #{vmname}"
114
+ end
115
+ when 'reset'
116
+ vm.ResetVM_Task.wait_for_completion
117
+ puts "Reset virtual machine #{vmname}"
118
+ end
119
+
120
+ if get_config(:wait_port)
121
+ print "Waiting for port #{get_config(:wait_port)}..."
122
+ print "." until tcp_test_port_vm(vm, get_config(:wait_port))
123
+ puts "done"
124
+ end
125
+ end
126
+ end
127
+ end
@@ -1,241 +1,241 @@
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.class == RbVmomi::VIM::VirtualLsiLogicController
120
- if scsi_tree[device.controllerKey].nil?
121
- scsi_tree[device.key]=Hash.new()
122
- scsi_tree[device.key]['children'] = Array.new();
123
- scsi_tree[device.key]['device'] = device;
124
- end
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
+ # 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.class == RbVmomi::VIM::VirtualLsiLogicController
120
+ if scsi_tree[device.controllerKey].nil?
121
+ scsi_tree[device.key]=Hash.new()
122
+ scsi_tree[device.key]['children'] = Array.new();
123
+ scsi_tree[device.key]['device'] = device;
124
+ end
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