knife-vsphere 0.6.0 → 0.7.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.
- data/lib/chef/knife/BaseVsphereCommand.rb +76 -0
- data/lib/chef/knife/vsphere_vm_vmdk_add.rb +163 -23
- data/lib/knife-vsphere/version.rb +4 -4
- metadata +2 -2
@@ -107,6 +107,35 @@ class Chef
|
|
107
107
|
@password ||= ui.ask("Enter your password: ") { |q| q.echo = false }
|
108
108
|
end
|
109
109
|
|
110
|
+
def get_vm(vmname)
|
111
|
+
vim = get_vim_connection
|
112
|
+
baseFolder = find_folder(get_config(:folder));
|
113
|
+
retval = traverse_folders_for_vm(baseFolder,vmname)
|
114
|
+
return retval
|
115
|
+
end
|
116
|
+
|
117
|
+
def traverse_folders_for_vm(folder,vmname)
|
118
|
+
# not sure why @vm is necessary, but it returns class Array
|
119
|
+
# instead of class VirtualMachine without it... ugh
|
120
|
+
@vm = nil
|
121
|
+
folders = find_all_in_folder(folder, RbVmomi::VIM::Folder)
|
122
|
+
folders.each do |child|
|
123
|
+
traverse_folders_for_vm(child,vmname)
|
124
|
+
vms = find_all_in_folder(folder,RbVmomi::VIM::VirtualMachine)
|
125
|
+
vms.each do |vm|
|
126
|
+
if vm.name == vmname
|
127
|
+
@vm = vm
|
128
|
+
return @vm
|
129
|
+
end
|
130
|
+
end
|
131
|
+
end
|
132
|
+
return @vm
|
133
|
+
end
|
134
|
+
|
135
|
+
def get_datacenter
|
136
|
+
dc = config[:vim].serviceInstance.find_datacenter(get_config(:vsphere_dc)) or abort "datacenter not found"
|
137
|
+
end
|
138
|
+
|
110
139
|
def find_folder(folderName)
|
111
140
|
dcname = get_config(:vsphere_dc)
|
112
141
|
dc = config[:vim].serviceInstance.find_datacenter(dcname) or abort "datacenter not found"
|
@@ -154,6 +183,53 @@ class Chef
|
|
154
183
|
baseEntity
|
155
184
|
end
|
156
185
|
|
186
|
+
def choose_datastore(dstores,size)
|
187
|
+
vmdk_size_kb = size.to_i * 1024 * 1024
|
188
|
+
vmdk_size_B = size.to_i * 1024 * 1024 * 1024
|
189
|
+
|
190
|
+
candidates = []
|
191
|
+
dstores.each do |store|
|
192
|
+
avail = number_to_human_size(store.summary[:freeSpace])
|
193
|
+
cap = number_to_human_size(store.summary[:capacity])
|
194
|
+
puts "#{ui.color("Datastore", :cyan)}: #{store.name} (#{avail}(#{store.summary[:freeSpace]}) / #{cap})"
|
195
|
+
|
196
|
+
# vm's can span multiple datastores, so instead of grabbing the first one
|
197
|
+
# let's find the first datastore with the available space on a LUN the vm
|
198
|
+
# is already using, or use a specified LUN (if given)
|
199
|
+
|
200
|
+
|
201
|
+
if ( store.summary[:freeSpace] - vmdk_size_B ) > 0
|
202
|
+
# also let's not use more than 90% of total space to save room for snapshots.
|
203
|
+
cap_remains = 100 * ( (store.summary[:freeSpace].to_f - vmdk_size_B.to_f ) / store.summary[:capacity].to_f )
|
204
|
+
if(cap_remains.to_i > 10)
|
205
|
+
candidates.push(store)
|
206
|
+
end
|
207
|
+
end
|
208
|
+
end
|
209
|
+
if candidates.length > 0
|
210
|
+
vmdk_datastore = candidates[0]
|
211
|
+
else
|
212
|
+
puts "Insufficient space on all LUNs designated or assigned to the virtual machine. Please specify a new target."
|
213
|
+
vmdk_datastore = nil
|
214
|
+
end
|
215
|
+
return vmdk_datastore
|
216
|
+
end
|
217
|
+
|
218
|
+
|
219
|
+
def find_datastores_regex(regex)
|
220
|
+
stores = Array.new()
|
221
|
+
puts "Looking for all datastores that match /#{regex}/"
|
222
|
+
dcname = get_config(:vsphere_dc)
|
223
|
+
dc = config[:vim].serviceInstance.find_datacenter(dcname) or abort "datacenter not found"
|
224
|
+
baseEntity = dc.datastore
|
225
|
+
baseEntity.each do |ds|
|
226
|
+
if ds.name.match /#{regex}/
|
227
|
+
stores.push ds
|
228
|
+
end
|
229
|
+
end
|
230
|
+
return stores
|
231
|
+
end
|
232
|
+
|
157
233
|
def find_datastore(dsName)
|
158
234
|
dcname = get_config(:vsphere_dc)
|
159
235
|
dc = config[:vim].serviceInstance.find_datacenter(dcname) or abort "datacenter not found"
|
@@ -15,7 +15,12 @@ class Chef::Knife::VsphereVmVmdkAdd < Chef::Knife::BaseVsphereCommand
|
|
15
15
|
option :vmdk_type,
|
16
16
|
:long => "--vmdk-type TYPE",
|
17
17
|
:description => "Type of VMDK"
|
18
|
-
|
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"
|
19
24
|
|
20
25
|
def run
|
21
26
|
$stdout.sync = true
|
@@ -37,42 +42,177 @@ class Chef::Knife::VsphereVmVmdkAdd < Chef::Knife::BaseVsphereCommand
|
|
37
42
|
vim = get_vim_connection
|
38
43
|
vdm = vim.serviceContent.virtualDiskManager
|
39
44
|
vm = get_vm(vmname)
|
45
|
+
if vm.nil?
|
46
|
+
puts "Could not find #{vmname}"
|
47
|
+
return
|
48
|
+
end
|
40
49
|
|
41
|
-
|
42
|
-
vmdk_fileName = "#{vmname}/#{vmname}_1.vmdk"
|
43
|
-
vmdk_name = "[#{vmdk_datastore.name}] #{vmdk_fileName}"
|
50
|
+
target_lun = get_config(:target_lun) unless get_config(:target_lun).nil?
|
44
51
|
vmdk_size_kb = size.to_i * 1024 * 1024
|
52
|
+
vmdk_size_B = size.to_i * 1024 * 1024 * 1024
|
53
|
+
|
54
|
+
if target_lun.nil?
|
55
|
+
vmdk_datastore = choose_datastore(vm.datastore,size)
|
56
|
+
exit -1 if vmdk_datastore.nil?
|
57
|
+
else
|
58
|
+
vmdk_datastores = find_datastores_regex(target_lun)
|
59
|
+
vmdk_datastore = choose_datastore(vmdk_datastores,size)
|
60
|
+
exit -1 if vmdk_datastore.nil?
|
61
|
+
vmdk_dir = "[#{vmdk_datastore.name}] #{vmname}"
|
62
|
+
# create the vm folder on the LUN or subsequent operations will fail.
|
63
|
+
if not vmdk_datastore.exists? vmname
|
64
|
+
dc = get_datacenter
|
65
|
+
dc._connection.serviceContent.fileManager.MakeDirectory :name => vmdk_dir, :datacenter => dc, :createParentDirectories => false
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
puts "Choosing: #{vmdk_datastore.name}"
|
70
|
+
|
71
|
+
# now we need to inspect the files in this datastore to get our next file name
|
72
|
+
next_vmdk = 1
|
73
|
+
pc = vmdk_datastore._connection.serviceContent.propertyCollector
|
74
|
+
vms = vmdk_datastore.vm
|
75
|
+
vmFiles = pc.collectMultiple vms, 'layoutEx.file'
|
76
|
+
vmFiles.keys.each do |vm|
|
77
|
+
vmFiles[vm]["layoutEx.file"].each do |layout|
|
78
|
+
if layout.name.match(/^\[#{vmdk_datastore.name}\] #{vmname}\/#{vmname}_([0-9]+).vmdk/)
|
79
|
+
num = $1
|
80
|
+
if next_vmdk <= num.to_i
|
81
|
+
next_vmdk = num.to_i + 1
|
82
|
+
end
|
83
|
+
end
|
84
|
+
end
|
85
|
+
end
|
86
|
+
vmdk_fileName = "#{vmname}/#{vmname}_#{next_vmdk}.vmdk"
|
87
|
+
vmdk_name = "[#{vmdk_datastore.name}] #{vmdk_fileName}"
|
45
88
|
vmdk_type = get_config(:vmdk_type)
|
46
89
|
vmdk_type = "preallocated" if vmdk_type == "thick"
|
90
|
+
puts "Next vmdk name is => #{vmdk_name}"
|
47
91
|
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
92
|
+
# create the disk
|
93
|
+
if not vmdk_datastore.exists? vmdk_fileName
|
94
|
+
vmdk_spec = RbVmomi::VIM::FileBackedVirtualDiskSpec(
|
95
|
+
:adapterType => "lsiLogic",
|
96
|
+
:capacityKb => vmdk_size_kb,
|
97
|
+
:diskType => vmdk_type
|
98
|
+
)
|
99
|
+
ui.info "Creating VMDK"
|
100
|
+
ui.info "#{ui.color "Capacity:", :cyan} #{size} GB"
|
101
|
+
ui.info "#{ui.color "Disk:", :cyan} #{vmdk_name}"
|
102
|
+
|
103
|
+
if get_config(:noop)
|
104
|
+
ui.info "#{ui.color "Skipping disk creation process because --noop specified.", :red}"
|
105
|
+
else
|
106
|
+
vdm.CreateVirtualDisk_Task(
|
107
|
+
:datacenter => get_datacenter,
|
108
|
+
:name => vmdk_name,
|
109
|
+
:spec => vmdk_spec
|
110
|
+
).wait_for_completion
|
111
|
+
end
|
112
|
+
end
|
113
|
+
ui.info "Attaching VMDK to #{vmname}"
|
53
114
|
|
54
|
-
|
55
|
-
|
56
|
-
|
115
|
+
# now we run through the SCSI controllers to see if there's an available one
|
116
|
+
available_controllers = Array.new()
|
117
|
+
use_controller = nil
|
118
|
+
scsi_tree = Hash.new()
|
119
|
+
vm.config.hardware.device.each do |device|
|
120
|
+
if device.class == RbVmomi::VIM::VirtualLsiLogicController
|
121
|
+
if scsi_tree[device.controllerKey].nil?
|
122
|
+
scsi_tree[device.key]=Hash.new()
|
123
|
+
scsi_tree[device.key]['children'] = Array.new();
|
124
|
+
scsi_tree[device.key]['device'] = device;
|
125
|
+
end
|
126
|
+
end
|
127
|
+
if device.class == RbVmomi::VIM::VirtualDisk
|
128
|
+
if scsi_tree[device.controllerKey].nil?
|
129
|
+
scsi_tree[device.controllerKey]=Hash.new()
|
130
|
+
scsi_tree[device.controllerKey]['children'] = Array.new();
|
131
|
+
end
|
132
|
+
scsi_tree[device.controllerKey]['children'].push(device)
|
133
|
+
end
|
134
|
+
end
|
135
|
+
scsi_tree.keys.sort.each do |controller|
|
136
|
+
if scsi_tree[controller]['children'].length < 15
|
137
|
+
available_controllers.push(scsi_tree[controller]['device'].deviceInfo.label)
|
138
|
+
end
|
139
|
+
end
|
57
140
|
|
58
|
-
if
|
59
|
-
|
141
|
+
if available_controllers.length > 0
|
142
|
+
use_controller = available_controllers[0]
|
143
|
+
puts "using #{use_controller}"
|
60
144
|
else
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
145
|
+
|
146
|
+
if scsi_tree.keys.length < 4
|
147
|
+
|
148
|
+
# Add a controller if none are available
|
149
|
+
puts "no controllers available. Will attempt to create"
|
150
|
+
new_scsi_key = scsi_tree.keys.sort[scsi_tree.length - 1] + 1
|
151
|
+
new_scsi_busNumber = scsi_tree[scsi_tree.keys.sort[scsi_tree.length - 1]]['device'].busNumber + 1
|
152
|
+
|
153
|
+
controller_device = RbVmomi::VIM::VirtualLsiLogicController(
|
154
|
+
:key => new_scsi_key,
|
155
|
+
:busNumber => new_scsi_busNumber,
|
156
|
+
:sharedBus => :noSharing
|
157
|
+
)
|
158
|
+
|
159
|
+
device_config_spec = RbVmomi::VIM::VirtualDeviceConfigSpec(
|
160
|
+
:device => controller_device,
|
161
|
+
:operation => RbVmomi::VIM::VirtualDeviceConfigSpecOperation("add")
|
162
|
+
)
|
163
|
+
|
164
|
+
vm_config_spec = RbVmomi::VIM::VirtualMachineConfigSpec(
|
165
|
+
:deviceChange => [device_config_spec]
|
166
|
+
)
|
167
|
+
|
168
|
+
if get_config(:noop)
|
169
|
+
ui.info "#{ui.color "Skipping controller creation process because --noop specified.", :red}"
|
170
|
+
else
|
171
|
+
vm.ReconfigVM_Task(:spec => vm_config_spec).wait_for_completion
|
172
|
+
end
|
173
|
+
else
|
174
|
+
ui.info "Controllers maxed out at 4."
|
175
|
+
exit -1;
|
176
|
+
end
|
177
|
+
end
|
178
|
+
|
179
|
+
# now go back and get the new device's name
|
180
|
+
vm.config.hardware.device.each do |device|
|
181
|
+
if device.class == RbVmomi::VIM::VirtualLsiLogicController
|
182
|
+
if device.key == new_scsi_key
|
183
|
+
use_controller = device.deviceInfo.label
|
184
|
+
end
|
185
|
+
end
|
66
186
|
end
|
67
187
|
|
68
|
-
|
188
|
+
# add the disk
|
189
|
+
controller = find_device(vm,use_controller)
|
190
|
+
|
191
|
+
used_unitNumbers = Array.new()
|
192
|
+
scsi_tree.keys.sort.each do |c|
|
193
|
+
if controller.key == scsi_tree[c]['device'].key
|
194
|
+
used_unitNumbers.push(scsi_tree[c]['device'].scsiCtlrUnitNumber)
|
195
|
+
scsi_tree[c]['children'].each do |disk|
|
196
|
+
used_unitNumbers.push(disk.unitNumber)
|
197
|
+
end
|
198
|
+
end
|
199
|
+
end
|
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
|
69
207
|
|
70
|
-
|
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}"
|
71
211
|
|
72
212
|
vmdk_backing = RbVmomi::VIM::VirtualDiskFlatVer2BackingInfo(
|
73
213
|
:datastore => vmdk_datastore,
|
74
214
|
:diskMode => "persistent",
|
75
|
-
:fileName => vmdk_name
|
215
|
+
:fileName => vmdk_name
|
76
216
|
)
|
77
217
|
|
78
218
|
device = RbVmomi::VIM::VirtualDisk(
|
@@ -80,7 +220,7 @@ class Chef::Knife::VsphereVmVmdkAdd < Chef::Knife::BaseVsphereCommand
|
|
80
220
|
:capacityInKB => vmdk_size_kb,
|
81
221
|
:controllerKey => controller.key,
|
82
222
|
:key => -1,
|
83
|
-
:unitNumber =>
|
223
|
+
:unitNumber => new_unitNumber
|
84
224
|
)
|
85
225
|
|
86
226
|
device_config_spec = RbVmomi::VIM::VirtualDeviceConfigSpec(
|
@@ -1,4 +1,4 @@
|
|
1
|
-
module KnifeVsphere
|
2
|
-
VERSION = "0.
|
3
|
-
end
|
4
|
-
|
1
|
+
module KnifeVsphere
|
2
|
+
VERSION = "0.7.0"
|
3
|
+
end
|
4
|
+
|
metadata
CHANGED
@@ -2,7 +2,7 @@
|
|
2
2
|
name: knife-vsphere
|
3
3
|
version: !ruby/object:Gem::Version
|
4
4
|
prerelease:
|
5
|
-
version: 0.
|
5
|
+
version: 0.7.0
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
8
8
|
- Ezra Pagel
|
@@ -10,7 +10,7 @@ autorequire:
|
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
12
|
|
13
|
-
date: 2013-06-
|
13
|
+
date: 2013-06-05 00:00:00 -05:00
|
14
14
|
default_executable:
|
15
15
|
dependencies:
|
16
16
|
- !ruby/object:Gem::Dependency
|