knife-ovh-cloud 1.0.0.pre.1

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