knife-vsphere 0.6.0 → 0.7.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -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
- $default[:vmdk_type] = "thin"
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
- vmdk_datastore = vm.datastore[0]
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
- vmdk_spec = RbVmomi::VIM::FileBackedVirtualDiskSpec(
49
- :adapterType => "lsiLogic",
50
- :capacityKb => vmdk_size_kb,
51
- :diskType => vmdk_type
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
- ui.info "Creating VMDK"
55
- ui.info "#{ui.color "Capacity:", :cyan} #{size} GB"
56
- ui.info "#{ui.color "Disk:", :cyan} #{vmdk_name}"
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 get_config(:noop)
59
- ui.info "#{ui.color "Skipping disk creation process because --noop specified.", :red}"
141
+ if available_controllers.length > 0
142
+ use_controller = available_controllers[0]
143
+ puts "using #{use_controller}"
60
144
  else
61
- vdm.CreateVirtualDisk_Task(
62
- :datacenter => get_datacenter,
63
- :name => vmdk_name,
64
- :spec => vmdk_spec
65
- ).wait_for_completion
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
- ui.info "Attaching VMDK to #{vmname}"
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
- controller = find_device(vm,"SCSI controller 0")
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 => controller.device.size + 1
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.6.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.6.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-02 00:00:00 -05:00
13
+ date: 2013-06-05 00:00:00 -05:00
14
14
  default_executable:
15
15
  dependencies:
16
16
  - !ruby/object:Gem::Dependency