knife-ovh-cloud 1.0.0.pre.1

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.
@@ -0,0 +1,46 @@
1
+ # Author:: Brian Dupras (<bdupras@rallydev.com>)
2
+ # License:: Apache License, Version 2.0
3
+
4
+ require 'chef/knife'
5
+ require 'chef/knife/base_ovh_cloud_command'
6
+ require 'rbvmomi'
7
+ require 'netaddr'
8
+
9
+ class Chef::Knife::OvhCloudVmPropertyGet < Chef::Knife::BaseOvhCloudCommand
10
+ banner "knife ovh cloud vm property get VMNAME PROPERTY. Gets a vApp Property on VMNAME."
11
+
12
+ get_common_options
13
+
14
+ def run
15
+ $stdout.sync = true
16
+ vmname = @name_args[0]
17
+ if vmname.nil?
18
+ show_usage
19
+ fatal_exit("You must specify a virtual machine name")
20
+ end
21
+
22
+ property_name = @name_args[1]
23
+ if property_name.nil?
24
+ show_usage
25
+ fatal_exit("You must specify a PROPERTY name (e.g. annotation)")
26
+ end
27
+ property_name = property_name.to_sym
28
+
29
+ vim = get_vim_connection
30
+
31
+ dc = get_datacenter
32
+ folder = find_folder(get_config(:folder)) || dc.vmFolder
33
+
34
+ vm = find_in_folder(folder, RbVmomi::VIM::VirtualMachine, vmname) or
35
+ abort "VM #{vmname} not found"
36
+
37
+ existing_property = vm.config.vAppConfig.property.find { |p| p.props[:id] == property_name.to_s }
38
+
39
+ if existing_property
40
+ puts existing_property.props[:value]
41
+ else
42
+ fatal_exit("PROPERTY [#{property_name.to_s}] not found")
43
+ end
44
+
45
+ end
46
+ end
@@ -0,0 +1,84 @@
1
+ # Author:: Brian Dupras (<bdupras@rallydev.com>)
2
+ # License:: Apache License, Version 2.0
3
+
4
+ require 'chef/knife'
5
+ require 'chef/knife/base_ovh_cloud_command'
6
+ require 'rbvmomi'
7
+ require 'netaddr'
8
+
9
+ class Chef::Knife::OvhCloudVmPropertySet < Chef::Knife::BaseOvhCloudCommand
10
+ banner "knife ovh cloud vm property set VMNAME PROPERTY VALUE. Sets a vApp Property on VMNAME."
11
+
12
+ get_common_options
13
+
14
+ option :ovf_environment_transport,
15
+ :long => "--ovf-environment-transport STRING",
16
+ :description => "Comma delimited string. Configures the transports to use for properties. Supported values are: iso and com.vmware.guestInfo."
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 = @name_args[1]
27
+ if property_name.nil?
28
+ show_usage
29
+ fatal_exit("You must specify a PROPERTY name (e.g. annotation)")
30
+ end
31
+ property_name = property_name.to_sym
32
+
33
+ property_value = @name_args[2]
34
+ if property_value.nil?
35
+ show_usage
36
+ fatal_exit("You must specify a PROPERTY value")
37
+ end
38
+
39
+ vim = get_vim_connection
40
+
41
+ dc = get_datacenter
42
+ folder = find_folder(get_config(:folder)) || dc.vmFolder
43
+
44
+ vm = find_in_folder(folder, RbVmomi::VIM::VirtualMachine, vmname) or
45
+ abort "VM #{vmname} not found"
46
+
47
+ if vm.config.vAppConfig && vm.config.vAppConfig.property
48
+ existing_property = vm.config.vAppConfig.property.find { |p| p.props[:id] == property_name.to_s }
49
+ end
50
+
51
+ if existing_property
52
+ operation = 'edit'
53
+ property_key = existing_property.props[:key]
54
+ else
55
+ operation = 'add'
56
+ property_key = property_name.object_id
57
+ end
58
+
59
+ vm_config_spec = RbVmomi::VIM.VirtualMachineConfigSpec(
60
+ :vAppConfig => RbVmomi::VIM.VmConfigSpec(
61
+ :property => [
62
+ RbVmomi::VIM.VAppPropertySpec(
63
+ :operation => operation,
64
+ :info => {
65
+ :key => property_key,
66
+ :id => property_name.to_s,
67
+ :type => 'string',
68
+ :userConfigurable => true,
69
+ :value => property_value
70
+ }
71
+ )
72
+ ]
73
+ )
74
+ )
75
+
76
+ unless config[:ovf_environment_transport].nil?
77
+ transport = config[:ovf_environment_transport].split(",")
78
+ transport = [""] if transport == [] ## because "".split returns [] and vmware wants [""]
79
+ vm_config_spec[:vAppConfig][:ovfEnvironmentTransport] = transport
80
+ end
81
+
82
+ vm.ReconfigVM_Task( :spec => vm_config_spec ).wait_for_completion
83
+ end
84
+ end
@@ -0,0 +1,48 @@
1
+ # Author:: Brian Dupras (<bdupras@rallydev.com>)
2
+ # License:: Apache License, Version 2.0
3
+
4
+ require 'chef/knife'
5
+ require 'chef/knife/base_ovh_cloud_command'
6
+ require 'rbvmomi'
7
+ require 'netaddr'
8
+
9
+ class Chef::Knife::OvhCloudVmQuery < Chef::Knife::BaseOvhCloudCommand
10
+ banner "knife ovh cloud query VMNAME QUERY. See \"http://pubs.vmware.com/vi3/sdk/ReferenceGuide/vim.VirtualMachine.html\" for allowed QUERY values."
11
+
12
+ get_common_options
13
+
14
+ def run
15
+ $stdout.sync = true
16
+ vmname = @name_args[0]
17
+ if vmname.nil?
18
+ show_usage
19
+ fatal_exit("You must specify a virtual machine name")
20
+ end
21
+
22
+ query_string = @name_args[1]
23
+ if query_string.nil?
24
+ show_usage
25
+ fatal_exit("You must specify a QUERY value (e.g. guest.ipAddress or network[0].name)")
26
+ end
27
+
28
+ vim = get_vim_connection
29
+
30
+ dc = get_datacenter
31
+ folder = find_folder(get_config(:folder)) || dc.vmFolder
32
+
33
+ vm = traverse_folders_for_vm(folder, vmname) or abort "VM #{vmname} not found"
34
+
35
+ # split QUERY by dots, and walk the object model
36
+ query = query_string.split '.'
37
+ result = vm
38
+ query.each do |part|
39
+ message, index = part.split(/[\[\]]/)
40
+ unless result.respond_to? message.to_sym
41
+ fatal_exit("\"#{query_string}\" not recognized.")
42
+ end
43
+
44
+ result = index ? result.send(message)[index.to_i] : result.send(message)
45
+ end
46
+ puts result
47
+ end
48
+ end
@@ -0,0 +1,129 @@
1
+ #
2
+ # License:: Apache License, Version 2.0
3
+ #
4
+
5
+ require 'chef/knife'
6
+ require 'chef/knife/base_ovh_cloud_command'
7
+ require 'rbvmomi'
8
+ require 'netaddr'
9
+
10
+
11
+ # Manage snapshots of a virtual machine
12
+ class Chef::Knife::OvhCloudVmSnapshot < Chef::Knife::BaseOvhCloudCommand
13
+
14
+ banner "knife ovh cloud vm snapshot VMNAME (options)"
15
+
16
+ get_common_options
17
+
18
+ option :list,
19
+ :long => "--list",
20
+ :description => "The current tree of snapshots"
21
+
22
+ option :create_new_snapshot,
23
+ :long => "--create SNAPSHOT",
24
+ :description => "Create a new snapshot off of the current snapshot."
25
+
26
+ option :remove_named_snapshot,
27
+ :long => "--remove SNAPSHOT",
28
+ :description => "Remove a named snapshot."
29
+
30
+ option :revert_snapshot,
31
+ :long => "--revert SNAPSHOT",
32
+ :description => "Revert to a named snapshot."
33
+
34
+ option :revert_current_snapshot,
35
+ :long => "--revert-current",
36
+ :description => "Revert to current snapshot.",
37
+ :boolean => false
38
+
39
+ option :power,
40
+ :long => "--start",
41
+ :description => "Indicates whether to start the VM after a successful revert",
42
+ :boolean => false
43
+
44
+ def run
45
+
46
+ $stdout.sync = true
47
+
48
+ vmname = @name_args[0]
49
+ if vmname.nil?
50
+ show_usage
51
+ ui.fatal("You must specify a virtual machine name")
52
+ exit 1
53
+ end
54
+
55
+ vim = get_vim_connection
56
+
57
+ baseFolder = find_folder(get_config(:folder));
58
+
59
+ vm = find_in_folder(baseFolder, RbVmomi::VIM::VirtualMachine, vmname) or
60
+ abort "VM #{vmname} not found"
61
+
62
+ if vm.snapshot
63
+ snapshot_list = vm.snapshot.rootSnapshotList
64
+ current_snapshot = vm.snapshot.currentSnapshot
65
+ end
66
+
67
+ if config[:list] && vm.snapshot
68
+ puts "Current snapshot tree: "
69
+ puts "#{vmname}"
70
+ snapshot_list.each { |i| puts display_node(i, current_snapshot) }
71
+ end
72
+
73
+ if config[:create_new_snapshot]
74
+ vm.CreateSnapshot_Task(:name => config[:create_new_snapshot], :description => "", :memory => false, :quiesce => false)
75
+ end
76
+
77
+ if config[:remove_named_snapshot]
78
+ ss_name = config[:remove_named_snapshot]
79
+ snapshot = find_node(snapshot_list, ss_name)
80
+ puts "Found snapshot #{ss_name} removing."
81
+ snapshot.RemoveSnapshot_Task(:removeChildren => false)
82
+ end
83
+
84
+ if config[:revert_current_snapshot]
85
+ puts "Reverting to Current Snapshot"
86
+ vm.RevertToCurrentSnapshot_Task(:suppressPowerOn => false).wait_for_completion
87
+ if get_config(:power)
88
+ vm.PowerOnVM_Task.wait_for_completion
89
+ puts "Powered on virtual machine #{vmname}"
90
+ end
91
+ end
92
+
93
+ if config[:revert_snapshot]
94
+ ss_name = config[:revert_snapshot]
95
+ snapshot = find_node(snapshot_list, ss_name)
96
+ snapshot.RevertToSnapshot_Task(:suppressPowerOn => false).wait_for_completion
97
+ if get_config(:power)
98
+ vm.PowerOnVM_Task.wait_for_completion
99
+ puts "Powered on virtual machine #{vmname}"
100
+ end
101
+ end
102
+ end
103
+
104
+ def find_node(tree, name)
105
+ snapshot = nil
106
+ tree.each do |node|
107
+ if node.name == name
108
+ snapshot = node.snapshot
109
+ elsif !node.childSnapshotList.empty?
110
+ snapshot = find_node(node.childSnapshotList, name)
111
+ end
112
+ end
113
+ return snapshot
114
+ end
115
+
116
+ def display_node(node, current, shift=1)
117
+ out = ""
118
+ out << "+--"*shift
119
+ if node.snapshot == current
120
+ out << "#{ui.color(node.name, :cyan)}" << "\n"
121
+ else
122
+ out << "#{node.name}" << "\n"
123
+ end
124
+ if !node.childSnapshotList.empty?
125
+ node.childSnapshotList.each { |item| out << display_node(item, current, shift+1) }
126
+ end
127
+ out
128
+ end
129
+ end
@@ -0,0 +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_ovh_cloud_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::OvhCloudVmState < Chef::Knife::BaseOvhCloudCommand
23
+
24
+ banner "knife ovh cloud 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
@@ -0,0 +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_ovh_cloud_command'
7
+
8
+ # Lists all known virtual machines in the configured datacenter
9
+ class Chef::Knife::OvhCloudVmVmdkAdd < Chef::Knife::BaseOvhCloudCommand
10
+
11
+ banner "knife ovh cloud 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