chef-provisioning-vsphere 0.5.7 → 0.5.8

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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: e7308c027bc21589567543a1144202d7b8868641
4
- data.tar.gz: 0f7ce9c37b7ae5662446ca99feb473c61fbbb6ce
3
+ metadata.gz: 12dda7faea087859dd1c57ca94de5f57075f34ac
4
+ data.tar.gz: 8ed3e2836949def6569e148c067f4ee427405218
5
5
  SHA512:
6
- metadata.gz: f9c2f84cabb77cd7cc0540e3a4c648129d97360d4430d2c6ff9d1dd31f1afeb3429191aae723e82c8b2c5c033af5a6e54c779aebf4de851df138ce68a6ae02ec
7
- data.tar.gz: 5fa9251bbca1fe98791fe01d27bdf015118d32f5c1832172d0bdf4af075b5fb6d07ba8a13170773628a540852f32d5026c7b3b1e8e374a3a515ca899a2fc2c1e
6
+ metadata.gz: 2daa6f17429a7bc45099cf006178fd36da7f4dd684d288b67569f91dccbc2b2562271dfb049c209a237b64fc98efbd7127a4b97b2e5e4a542a020a36ebbaca8b
7
+ data.tar.gz: a7adc29123fade718ff1e960ac89d57c2a46811c11ce95ac32cf75252f6c4b95879a55fcf9e56920423861c737c765260e3db13846de2e298b2a81a2807a84e8
data/README.md CHANGED
@@ -76,6 +76,7 @@ This will use chef-zero and needs no chef server (only works for ssh). Note that
76
76
  - `[:use_linked_clone]` - (true/false) great for testing but not recommended for production.
77
77
  - `[:datacenter]` - Name of vsphere datacenter (*required*)
78
78
  - `[:template_name]` - path to vmware template (can be template or a shutown vm) (*required*)
79
+ - `[:template_folder]` - path to a folder containing the template (do not use if template is in the root vm folder)
79
80
  - `[:vm_folder]` - path to a folder where the machine will be created.
80
81
  - `[:datastore]` - name of datastore to use
81
82
  - `[:num_cpus]` - number of cpus to allocate to machine
@@ -0,0 +1,201 @@
1
+ module ChefProvisioningVsphere
2
+ class CloneSpecBuilder
3
+ def initialize(vsphere_helper, action_handler)
4
+ @vsphere_helper = vsphere_helper
5
+ @action_handler = action_handler
6
+ end
7
+
8
+ attr_reader :vsphere_helper
9
+ attr_reader :action_handler
10
+
11
+ def build(vm_template, vm_name, options)
12
+ clone_spec = RbVmomi::VIM.VirtualMachineCloneSpec(
13
+ location: relocate_spec_for(vm_template, options),
14
+ powerOn: false,
15
+ template: false,
16
+ config: RbVmomi::VIM.VirtualMachineConfigSpec(
17
+ :cpuHotAddEnabled => true,
18
+ :memoryHotAddEnabled => true,
19
+ :cpuHotRemoveEnabled => true,
20
+ :deviceChange => Array.new)
21
+ )
22
+
23
+ unless options[:annotation].to_s.nil?
24
+ clone_spec.config.annotation = options[:annotation]
25
+ end
26
+
27
+ unless options[:num_cpus].to_s.nil?
28
+ clone_spec.config.numCPUs = options[:num_cpus]
29
+ end
30
+
31
+ unless options[:memory_mb].to_s.nil?
32
+ clone_spec.config.memoryMB = options[:memory_mb]
33
+ end
34
+
35
+ unless options[:network_name].nil?
36
+ deviceAdditions, changes = vsphere_helper.network_device_changes(
37
+ action_handler,
38
+ vm_template,
39
+ options
40
+ )
41
+ clone_spec.config.deviceChange = changes
42
+ end
43
+
44
+ clone_spec.customization = customization_options_from(
45
+ vm_template,
46
+ vm_name,
47
+ options
48
+ )
49
+
50
+ clone_spec
51
+ end
52
+
53
+ def relocate_spec_for(vm_template, options)
54
+ rspec = RbVmomi::VIM.VirtualMachineRelocateSpec
55
+ host = nil
56
+
57
+ if options.has_key?(:host)
58
+ host = vsphere_helper.find_host(options[:host])
59
+ rspec.host = host
60
+ end
61
+
62
+ if options[:resource_pool]
63
+ rspec.pool = vsphere_helper.find_pool(options[:resource_pool])
64
+ elsif vm_template.config.template && !host.nil?
65
+ rspec.pool = host.parent.resourcePool # assign to the "invisible" pool root
66
+ elsif vm_template.config.template
67
+ raise 'either :host or :resource_pool must be specified when cloning from a VM Template'
68
+ end
69
+
70
+
71
+ if options[:use_linked_clone]
72
+ if vm_template.config.template
73
+ Chef::Log.warn("Using a VM Template, ignoring use_linked_clone.")
74
+ else
75
+ vsphere_helper.create_delta_disk(vm_template)
76
+ rspec.diskMoveType = :moveChildMostDiskBacking
77
+ end
78
+ end
79
+
80
+ unless options[:datastore].to_s.empty?
81
+ rspec.datastore = vsphere_helper.find_datastore(options[:datastore])
82
+ end
83
+
84
+ rspec
85
+ end
86
+
87
+ def customization_options_from(vm_template, vm_name, options)
88
+ if options.has_key?(:customization_spec)
89
+ if(options[:customization_spec].is_a?(Hash))
90
+ cust_options = options[:customization_spec]
91
+ ip_settings = cust_options[:ipsettings]
92
+ cust_domain = cust_options[:domain]
93
+
94
+ raise ArgumentError, 'domain is required' unless cust_domain
95
+ cust_ip_settings = nil
96
+ if ip_settings && ip_settings.key?(:ip)
97
+ unless cust_options[:ipsettings].key?(:subnetMask)
98
+ raise ArgumentError, 'subnetMask is required for static ip'
99
+ end
100
+ cust_ip_settings = RbVmomi::VIM::CustomizationIPSettings.new(
101
+ ip_settings)
102
+ action_handler.report_progress "customizing #{vm_name} \
103
+ with static IP #{ip_settings[:ip]}"
104
+ cust_ip_settings.ip = RbVmomi::VIM::CustomizationFixedIp(
105
+ :ipAddress => ip_settings[:ip])
106
+ end
107
+ if cust_ip_settings.nil?
108
+ cust_ip_settings= RbVmomi::VIM::CustomizationIPSettings.new(
109
+ :ip => RbVmomi::VIM::CustomizationDhcpIpGenerator.new())
110
+ cust_ip_settings.dnsServerList = ip_settings[:dnsServerList]
111
+ action_handler.report_progress "customizing #{vm_name} with /
112
+ dynamic IP and DNS: #{ip_settings[:dnsServerList]}"
113
+ end
114
+
115
+ cust_ip_settings.dnsDomain = cust_domain
116
+ global_ip_settings = RbVmomi::VIM::CustomizationGlobalIPSettings.new
117
+ global_ip_settings.dnsServerList = cust_ip_settings.dnsServerList
118
+ global_ip_settings.dnsSuffixList = [cust_domain]
119
+ cust_hostname = hostname_from(cust_options, vm_name)
120
+ cust_hwclockutc = cust_options[:hw_clock_utc]
121
+ cust_timezone = cust_options[:time_zone]
122
+
123
+ if vm_template.config.guestId.start_with?('win')
124
+ cust_prep = windows_prep_for(options, vm_name)
125
+ else
126
+ cust_prep = RbVmomi::VIM::CustomizationLinuxPrep.new(
127
+ :domain => cust_domain,
128
+ :hostName => cust_hostname,
129
+ :hwClockUTC => cust_hwclockutc,
130
+ :timeZone => cust_timezone
131
+ )
132
+ end
133
+ cust_adapter_mapping = [
134
+ RbVmomi::VIM::CustomizationAdapterMapping.new(
135
+ :adapter => cust_ip_settings)
136
+ ]
137
+ RbVmomi::VIM::CustomizationSpec.new(
138
+ :identity => cust_prep,
139
+ :globalIPSettings => global_ip_settings,
140
+ :nicSettingMap => cust_adapter_mapping
141
+ )
142
+ else
143
+ vsphere_helper.find_customization_spec(cust_options)
144
+ end
145
+ end
146
+ end
147
+
148
+ def hostname_from(options, vm_name)
149
+ hostname = options[:hostname] || vm_name
150
+ test = /^([a-zA-Z0-9]|[a-zA-Z0-9][a-zA-Z0-9\-]*[a-zA-Z0-9])$/
151
+ if !(hostname =~ test)
152
+ raise 'Only letters, numbers or hyphens in hostnames allowed'
153
+ end
154
+ RbVmomi::VIM::CustomizationFixedName.new(:name => hostname)
155
+ end
156
+
157
+ def windows_prep_for(options, vm_name)
158
+ cust_options = options[:customization_spec]
159
+ cust_runonce = RbVmomi::VIM::CustomizationGuiRunOnce.new(
160
+ :commandList => [
161
+ 'winrm set winrm/config/client/auth @{Basic="true"}',
162
+ 'winrm set winrm/config/service/auth @{Basic="true"}',
163
+ 'winrm set winrm/config/service @{AllowUnencrypted="true"}',
164
+ 'shutdown -l'])
165
+
166
+ cust_login_password = RbVmomi::VIM::CustomizationPassword(
167
+ :plainText => true,
168
+ :value => options[:ssh][:password])
169
+ if cust_options.has_key?(:domain) and cust_options[:domain] != 'local'
170
+ cust_domain_password = RbVmomi::VIM::CustomizationPassword(
171
+ :plainText => true,
172
+ :value => ENV['domainAdminPassword'] || cust_options[:domainAdminPassword])
173
+ cust_id = RbVmomi::VIM::CustomizationIdentification.new(
174
+ :joinDomain => cust_options[:domain],
175
+ :domainAdmin => cust_options[:domainAdmin],
176
+ :domainAdminPassword => cust_domain_password)
177
+ #puts "my env passwd is: #{ENV['domainAdminPassword']}"
178
+ action_handler.report_progress "joining domain #{cust_options[:domain]} /
179
+ with user: #{cust_options[:domainAdmin]}"
180
+ else
181
+ cust_id = RbVmomi::VIM::CustomizationIdentification.new(
182
+ :joinWorkgroup => 'WORKGROUP')
183
+ end
184
+ cust_gui_unattended = RbVmomi::VIM::CustomizationGuiUnattended.new(
185
+ :autoLogon => true,
186
+ :autoLogonCount => 1,
187
+ :password => cust_login_password,
188
+ :timeZone => cust_options[:win_time_zone])
189
+ cust_userdata = RbVmomi::VIM::CustomizationUserData.new(
190
+ :computerName => hostname_from(cust_options, vm_name),
191
+ :fullName => cust_options[:org_name],
192
+ :orgName => cust_options[:org_name],
193
+ :productId => cust_options[:product_id])
194
+ RbVmomi::VIM::CustomizationSysprep.new(
195
+ :guiRunOnce => cust_runonce,
196
+ :identification => cust_id,
197
+ :guiUnattended => cust_gui_unattended,
198
+ :userData => cust_userdata)
199
+ end
200
+ end
201
+ end
@@ -3,6 +3,7 @@ require 'cheffish/merged_config'
3
3
  require 'chef/provisioning/driver'
4
4
  require 'chef/provisioning/machine/windows_machine'
5
5
  require 'chef/provisioning/machine/unix_machine'
6
+ require 'chef/provisioning/vsphere_driver/clone_spec_builder'
6
7
  require 'chef/provisioning/vsphere_driver/version'
7
8
  require 'chef/provisioning/vsphere_driver/vsphere_helpers'
8
9
  require 'chef/provisioning/vsphere_driver/vsphere_url'
@@ -11,7 +12,6 @@ module ChefProvisioningVsphere
11
12
  # Provisions machines in vSphere.
12
13
  class VsphereDriver < Chef::Provisioning::Driver
13
14
  include Chef::Mixin::ShellOut
14
- include ChefProvisioningVsphere::Helpers
15
15
 
16
16
  def self.from_url(driver_url, config)
17
17
  VsphereDriver.new(driver_url, config)
@@ -163,7 +163,7 @@ module ChefProvisioningVsphere
163
163
  end
164
164
 
165
165
  def find_or_create_vm(bootstrap_options, machine_spec, action_handler)
166
- vm = find_vm(
166
+ vm = vsphere_helper.find_vm(
167
167
  bootstrap_options[:vm_folder],
168
168
  machine_spec.name
169
169
  )
@@ -231,7 +231,7 @@ module ChefProvisioningVsphere
231
231
  end
232
232
  return if networks.nil? || networks.count < 2
233
233
 
234
- new_nics = add_extra_nic(
234
+ new_nics = vsphere_helper.add_extra_nic(
235
235
  action_handler,
236
236
  vm_template_for(bootstrap_options),
237
237
  bootstrap_options,
@@ -313,7 +313,7 @@ module ChefProvisioningVsphere
313
313
  msg << vm.guest.toolsRunningStatus
314
314
  msg << '. powering up server...'
315
315
  action_handler.report_progress(msg.join)
316
- start_vm(vm)
316
+ vsphere_helper.start_vm(vm)
317
317
  else
318
318
  restart_server(action_handler, machine_spec, machine_options)
319
319
  end
@@ -395,7 +395,7 @@ module ChefProvisioningVsphere
395
395
  vm = vm_for(machine_spec)
396
396
  if vm
397
397
  action_handler.perform_action "Shutdown guest OS and power off VM [#{vm.parent.name}/#{vm.name}]" do
398
- stop_vm(vm)
398
+ vsphere_helper.stop_vm(vm)
399
399
  end
400
400
  end
401
401
  end
@@ -405,7 +405,7 @@ module ChefProvisioningVsphere
405
405
  vm = vm_for(machine_spec)
406
406
  if vm
407
407
  action_handler.perform_action "Power on VM [#{vm.parent.name}/#{vm.name}]" do
408
- start_vm(vm, machine_options[:bootstrap_options][:ssh][:port])
408
+ vsphere_helper.start_vm(vm, machine_options[:bootstrap_options][:ssh][:port])
409
409
  end
410
410
  end
411
411
  vm
@@ -484,25 +484,53 @@ module ChefProvisioningVsphere
484
484
 
485
485
  def vm_for(machine_spec)
486
486
  if machine_spec.location
487
- find_vm_by_id(machine_spec.location['server_id'])
487
+ vsphere_helper.find_vm_by_id(machine_spec.location['server_id'])
488
488
  else
489
489
  nil
490
490
  end
491
491
  end
492
492
 
493
493
  def clone_vm(action_handler, bootstrap_options, machine_name)
494
- do_vm_clone(
495
- action_handler,
496
- vm_template_for(bootstrap_options),
497
- machine_name,
498
- bootstrap_options
494
+ vm_template = vm_template_for(bootstrap_options)
495
+
496
+ spec_builder = CloneSpecBuilder.new(vsphere_helper, action_handler)
497
+ clone_spec = spec_builder.build(vm_template, machine_name, bootstrap_options)
498
+ Chef::Log.debug("Clone spec: #{clone_spec.pretty_inspect}")
499
+
500
+ vm_folder = vsphere_helper.find_folder(bootstrap_options[:vm_folder])
501
+ vm_template.CloneVM_Task(
502
+ name: machine_name,
503
+ folder: vm_folder,
504
+ spec: clone_spec
505
+ ).wait_for_completion
506
+
507
+ vm = vsphere_helper.find_vm(vm_folder, machine_name)
508
+
509
+ if bootstrap_options[:additional_disk_size_gb].to_i > 0
510
+ task = vm.ReconfigVM_Task(
511
+ spec: RbVmomi::VIM.VirtualMachineConfigSpec(
512
+ deviceChange: [
513
+ vsphere_helper.virtual_disk_for(vm, bootstrap_options)
514
+ ]
515
+ )
516
+ )
517
+ task.wait_for_completion
518
+ end
519
+
520
+ vm
521
+ end
522
+
523
+ def vsphere_helper
524
+ @vsphere_helper ||= VsphereHelper.new(
525
+ connect_options,
526
+ config[:machine_options][:bootstrap_options][:datacenter]
499
527
  )
500
528
  end
501
529
 
502
530
  def vm_template_for(bootstrap_options)
503
531
  template_folder = bootstrap_options[:template_folder]
504
532
  template_name = bootstrap_options[:template_name]
505
- find_vm(template_folder, template_name) ||
533
+ vsphere_helper.find_vm(template_folder, template_name) ||
506
534
  raise("vSphere VM Template not found [#{template_folder}/#{template_name}]")
507
535
  end
508
536
 
@@ -1,3 +1,3 @@
1
1
  module ChefProvisioningVsphere
2
- VERSION = '0.5.7'
2
+ VERSION = '0.5.8'
3
3
  end
@@ -1,16 +1,24 @@
1
1
  require 'rbvmomi'
2
2
 
3
3
  module ChefProvisioningVsphere
4
- module Helpers
4
+ class VsphereHelper
5
5
 
6
6
  if !$guest_op_managers
7
7
  $guest_op_managers = {}
8
8
  end
9
9
 
10
- def vim(options = connect_options)
10
+ def initialize(connect_options, datacenter_name)
11
+ @connect_options = connect_options
12
+ @datacenter_name = datacenter_name
13
+ end
14
+
15
+ attr_reader :connect_options
16
+ attr_reader :datacenter_name
17
+
18
+ def vim
11
19
  if @current_connection.nil? or @current_connection.serviceContent.sessionManager.currentSession.nil?
12
- puts "establishing connection to #{options[:host]}"
13
- @current_connection = RbVmomi::VIM.connect options
20
+ puts "establishing connection to #{connect_options[:host]}"
21
+ @current_connection = RbVmomi::VIM.connect connect_options
14
22
  str_conn = @current_connection.pretty_inspect # a string in the format of VIM(host ip)
15
23
 
16
24
  # we are caching guest operation managers in a global variable...terrible i know
@@ -31,22 +39,14 @@ module ChefProvisioningVsphere
31
39
  folder.find(vm_name, RbVmomi::VIM::VirtualMachine)
32
40
  end
33
41
 
34
- def find_vm_by_id(uuid, connection = vim)
35
- vm = connection.searchIndex.FindByUuid(
42
+ def find_vm_by_id(uuid)
43
+ vm = vim.searchIndex.FindByUuid(
36
44
  uuid: uuid,
37
45
  vmSearch: true,
38
46
  instanceUuid: true
39
47
  )
40
48
  end
41
49
 
42
- def vm_started?(vm, wait_on_port = 22)
43
- return false if vm.nil?
44
- state = vm.runtime.powerState
45
- return false unless state == 'poweredOn'
46
- return false unless port_ready?(vm, wait_on_port)
47
- return true
48
- end
49
-
50
50
  def vm_stopped?(vm)
51
51
  return true if vm.nil?
52
52
  state = vm.runtime.powerState
@@ -70,31 +70,6 @@ module ChefProvisioningVsphere
70
70
  end
71
71
  end
72
72
 
73
- def port_ready?(vm, port)
74
- vm_ip = vm.guest.ipAddress
75
- return false if vm_ip.nil?
76
-
77
- begin
78
- tcp_socket = TCPSocket.new(vm_ip, port)
79
- readable = IO.select([tcp_socket], nil, nil, 5)
80
- if readable
81
- true
82
- else
83
- false
84
- end
85
- rescue Errno::ETIMEDOUT
86
- false
87
- rescue Errno::EPERM
88
- false
89
- rescue Errno::ECONNREFUSED
90
- false
91
- rescue Errno::EHOSTUNREACH, Errno::ENETUNREACH
92
- false
93
- ensure
94
- tcp_socket && tcp_socket.close
95
- end
96
- end
97
-
98
73
  #folder could be like: /Level1/Level2/folder_name
99
74
  def find_folder(folder_name)
100
75
  base = datacenter.vmFolder
@@ -108,9 +83,8 @@ module ChefProvisioningVsphere
108
83
  end
109
84
 
110
85
  def datacenter
111
- dc_name = config[:machine_options][:bootstrap_options][:datacenter]
112
- @datacenter ||= vim.serviceInstance.find_datacenter(dc_name) ||
113
- raise("vSphere Datacenter not found [#{dc_name}]")
86
+ @datacenter ||= vim.serviceInstance.find_datacenter(datacenter_name) ||
87
+ raise("vSphere Datacenter not found [#{datacenter_name}]")
114
88
  end
115
89
 
116
90
  def network_adapter_for(operation, network_name, network_label, device_key, backing_info)
@@ -132,56 +106,6 @@ module ChefProvisioningVsphere
132
106
  vm.config.hardware.device.select {|d| d.is_a?(RbVmomi::VIM::VirtualEthernetCard)}
133
107
  end
134
108
 
135
- def do_vm_clone(action_handler, vm_template, vm_name, options)
136
- deviceAdditions = []
137
-
138
- clone_spec = RbVmomi::VIM.VirtualMachineCloneSpec(
139
- location: relocate_spec_for(vm_template, options),
140
- powerOn: false,
141
- template: false,
142
- config: RbVmomi::VIM.VirtualMachineConfigSpec(
143
- :cpuHotAddEnabled => true,
144
- :memoryHotAddEnabled => true,
145
- :cpuHotRemoveEnabled => true,
146
- :deviceChange => Array.new)
147
- )
148
-
149
- clone_spec.customization = customization_options_from(action_handler, vm_template, vm_name, options)
150
-
151
- unless options[:annotation].to_s.nil?
152
- clone_spec.config.annotation = options[:annotation]
153
- end
154
-
155
- unless options[:num_cpus].to_s.nil?
156
- clone_spec.config.numCPUs = options[:num_cpus]
157
- end
158
-
159
- unless options[:memory_mb].to_s.nil?
160
- clone_spec.config.memoryMB = options[:memory_mb]
161
- end
162
-
163
- unless options[:network_name].nil?
164
- deviceAdditions, changes = network_device_changes(action_handler, vm_template, options)
165
- clone_spec.config.deviceChange = changes
166
- end
167
-
168
- vm_folder = find_folder(options[:vm_folder])
169
- vm_template.CloneVM_Task(
170
- name: vm_name,
171
- folder: vm_folder,
172
- spec: clone_spec
173
- ).wait_for_completion
174
-
175
- vm = find_vm(vm_folder, vm_name)
176
-
177
- if options[:additional_disk_size_gb].to_i > 0
178
- task = vm.ReconfigVM_Task(:spec => RbVmomi::VIM.VirtualMachineConfigSpec(:deviceChange => [virtual_disk_for(vm, options)]))
179
- task.wait_for_completion
180
- end
181
-
182
- vm
183
- end
184
-
185
109
  def add_extra_nic(action_handler, vm_template, options, vm)
186
110
  deviceAdditions, changes = network_device_changes(action_handler, vm_template, options)
187
111
 
@@ -206,28 +130,6 @@ module ChefProvisioningVsphere
206
130
  end
207
131
  end
208
132
 
209
- def relocate_spec_for(vm_template, options)
210
- if options.has_key?(:host)
211
- host = find_host(options[:host])
212
- rspec = RbVmomi::VIM.VirtualMachineRelocateSpec(host: host)
213
- else
214
- pool = options[:resource_pool] ? find_pool(options[:resource_pool]) : vm_template.resourcePool
215
- rspec = RbVmomi::VIM.VirtualMachineRelocateSpec(pool: pool)
216
- raise 'either :host or :resource_pool must be specified when cloning from a VM Template' if pool.nil?
217
- end
218
-
219
- if options.has_key?(:use_linked_clone)
220
- create_delta_disk(vm_template)
221
- rspec.diskMoveType = :moveChildMostDiskBacking
222
- end
223
-
224
- unless options[:datastore].to_s.empty?
225
- rspec.datastore = find_datastore(options[:datastore])
226
- end
227
-
228
- rspec
229
- end
230
-
231
133
  def create_delta_disk(vm_template)
232
134
  disks = vm_template.config.hardware.device.grep(RbVmomi::VIM::VirtualDisk)
233
135
  disks.select { |disk| disk.backing.parent == nil }.each do |disk|
@@ -326,114 +228,12 @@ module ChefProvisioningVsphere
326
228
  datacenter.datastore.find { |f| f.info.name == datastore_name } or raise "no such datastore #{datastore_name}"
327
229
  end
328
230
 
329
- def customization_options_from(action_handler, vm_template, vm_name, options)
330
- if options.has_key?(:customization_spec)
331
- if(options[:customization_spec].is_a?(Hash))
332
- cust_options = options[:customization_spec]
333
- ip_settings = cust_options[:ipsettings]
334
- cust_domain = cust_options[:domain]
335
-
336
- raise ArgumentError, 'domain is required' unless cust_domain
337
- cust_ip_settings = nil
338
- if ip_settings && ip_settings.key?(:ip)
339
- unless cust_options[:ipsettings].key?(:subnetMask)
340
- raise ArgumentError, 'subnetMask is required for static ip'
341
- end
342
- cust_ip_settings = RbVmomi::VIM::CustomizationIPSettings.new(ip_settings)
343
- action_handler.report_progress "customizing #{vm_name} with static IP #{ip_settings[:ip]}"
344
- cust_ip_settings.ip = RbVmomi::VIM::CustomizationFixedIp(:ipAddress => ip_settings[:ip])
345
- end
346
- if cust_ip_settings.nil?
347
- cust_ip_settings= RbVmomi::VIM::CustomizationIPSettings.new(:ip => RbVmomi::VIM::CustomizationDhcpIpGenerator.new())
348
- cust_ip_settings.dnsServerList = ip_settings[:dnsServerList]
349
- action_handler.report_progress "customizing #{vm_name} with dynamic IP and DNS: #{ip_settings[:dnsServerList]}"
350
- end
351
-
352
- cust_ip_settings.dnsDomain = cust_domain
353
- cust_global_ip_settings = RbVmomi::VIM::CustomizationGlobalIPSettings.new
354
- cust_global_ip_settings.dnsServerList = cust_ip_settings.dnsServerList
355
- cust_global_ip_settings.dnsSuffixList = [cust_domain]
356
- cust_hostname = hostname_from(cust_options, vm_name)
357
- cust_hwclockutc = cust_options[:hw_clock_utc]
358
- cust_timezone = cust_options[:time_zone]
359
-
360
- if vm_template.config.guestId.start_with?('win')
361
- cust_prep = windows_prep_for(action_handler, options, vm_name)
362
- else
363
- cust_prep = RbVmomi::VIM::CustomizationLinuxPrep.new(
364
- :domain => cust_domain,
365
- :hostName => cust_hostname,
366
- :hwClockUTC => cust_hwclockutc,
367
- :timeZone => cust_timezone
368
- )
369
- end
370
- cust_adapter_mapping = [RbVmomi::VIM::CustomizationAdapterMapping.new(:adapter => cust_ip_settings)]
371
- RbVmomi::VIM::CustomizationSpec.new(
372
- :identity => cust_prep,
373
- :globalIPSettings => cust_global_ip_settings,
374
- :nicSettingMap => cust_adapter_mapping
375
- )
376
- else
377
- find_customization_spec(cust_options)
378
- end
379
- end
380
- end
381
-
382
- def windows_prep_for(action_handler, options, vm_name)
383
- cust_options = options[:customization_spec]
384
- cust_runonce = RbVmomi::VIM::CustomizationGuiRunOnce.new(
385
- :commandList => [
386
- 'winrm set winrm/config/client/auth @{Basic="true"}',
387
- 'winrm set winrm/config/service/auth @{Basic="true"}',
388
- 'winrm set winrm/config/service @{AllowUnencrypted="true"}',
389
- 'shutdown -l'])
390
-
391
- cust_login_password = RbVmomi::VIM::CustomizationPassword(
392
- :plainText => true,
393
- :value => options[:ssh][:password])
394
- if cust_options.has_key?(:domain) and cust_options[:domain] != 'local'
395
- cust_domain_password = RbVmomi::VIM::CustomizationPassword(
396
- :plainText => true,
397
- :value => ENV['domainAdminPassword'] || cust_options[:domainAdminPassword])
398
- cust_id = RbVmomi::VIM::CustomizationIdentification.new(
399
- :joinDomain => cust_options[:domain],
400
- :domainAdmin => cust_options[:domainAdmin],
401
- :domainAdminPassword => cust_domain_password)
402
- #puts "my env passwd is: #{ENV['domainAdminPassword']}"
403
- action_handler.report_progress "joining domain #{cust_options[:domain]} with user: #{cust_options[:domainAdmin]}"
404
- else
405
- cust_id = RbVmomi::VIM::CustomizationIdentification.new(
406
- :joinWorkgroup => 'WORKGROUP')
407
- end
408
- cust_gui_unattended = RbVmomi::VIM::CustomizationGuiUnattended.new(
409
- :autoLogon => true,
410
- :autoLogonCount => 1,
411
- :password => cust_login_password,
412
- :timeZone => cust_options[:win_time_zone])
413
- cust_userdata = RbVmomi::VIM::CustomizationUserData.new(
414
- :computerName => hostname_from(cust_options, vm_name),
415
- :fullName => cust_options[:org_name],
416
- :orgName => cust_options[:org_name],
417
- :productId => cust_options[:product_id])
418
- RbVmomi::VIM::CustomizationSysprep.new(
419
- :guiRunOnce => cust_runonce,
420
- :identification => cust_id,
421
- :guiUnattended => cust_gui_unattended,
422
- :userData => cust_userdata)
423
- end
424
-
425
- def hostname_from(options, vm_name)
426
- if options.key?(:hostname)
427
- RbVmomi::VIM::CustomizationFixedName.new(:name => options[:hostname])
428
- else
429
- RbVmomi::VIM::CustomizationFixedName.new(:name => vm_name)
430
- end
431
- end
432
-
433
231
  def find_entity(name, parent_folder, &block)
434
232
  parts = name.split('/').reject(&:empty?)
435
233
  parts.each do |item|
234
+ Chef::Log.debug("Identifying entity part: #{item} in folder type: #{parent_folder.class}")
436
235
  if parent_folder.is_a? RbVmomi::VIM::Folder
236
+ Chef::Log.debug('Parent folder is a folder')
437
237
  parent_folder = parent_folder.childEntity.find { |f| f.name == item }
438
238
  else
439
239
  parent_folder = block.call(parent_folder, item)
@@ -456,20 +256,28 @@ module ChefProvisioningVsphere
456
256
 
457
257
  raise "vSphere Host not found [#{host_name}]" if host.nil?
458
258
 
459
- if !host.is_a?(RbVmomi::VIM::HostSystem) && host.respond_to?(:host)
460
- host = host.host
259
+ if host.is_a?(RbVmomi::VIM::ComputeResource)
260
+ host = host.host.first
461
261
  end
462
262
  host
463
263
  end
464
264
 
465
265
  def find_pool(pool_name)
266
+ Chef::Log.debug("Finding pool: #{pool_name}")
466
267
  pool = find_entity(pool_name, datacenter.hostFolder) do |parent, part|
467
268
  case parent
468
- when RbVmomi::VIM::ClusterComputeResource || RbVmomi::VIM::ComputeResource
269
+ when RbVmomi::VIM::ClusterComputeResource, RbVmomi::VIM::ComputeResource
270
+ Chef::Log.debug("finding #{part} in a #{parent.class}: #{parent.name}")
271
+ Chef::Log.debug("Parent root pool has #{parent.resourcePool.resourcePool.count} pools")
272
+ parent.resourcePool.resourcePool.each { |p| Chef::Log.debug(p.name ) }
469
273
  parent.resourcePool.resourcePool.find { |f| f.name == part }
470
274
  when RbVmomi::VIM::ResourcePool
275
+ Chef::Log.debug("finding #{part} in a Resource Pool: #{parent.name}")
276
+ Chef::Log.debug("Pool has #{parent.resourcePool.count} pools")
277
+ parent.resourcePool.each { |p| Chef::Log.debug(p.name ) }
471
278
  parent.resourcePool.find { |f| f.name == part }
472
279
  else
280
+ Chef::Log.debug("parent of #{part} is unexpected type: #{parent.class}")
473
281
  nil
474
282
  end
475
283
  end
@@ -34,12 +34,11 @@ require 'chef/provisioning/machine_spec'
34
34
  # }
35
35
 
36
36
  describe 'vsphere_driver' do
37
- include ChefProvisioningVsphere::Helpers
38
-
39
37
  before :all do
40
38
  @vm_name = "cmvd-test-#{SecureRandom.hex}"
41
39
  @metal_config = eval File.read(File.expand_path('../config.rb', __FILE__))
42
40
  Cheffish.honor_local_mode do
41
+ Chef::Log.level = :debug
43
42
  chef_server = Cheffish.default_chef_server
44
43
  @machine_spec = Chef::Provisioning.chef_managed_entry_store(chef_server).new_entry(:machine, @vm_name)
45
44
  url = URI::VsphereUrl.from_config(@metal_config[:driver_options]).to_s
@@ -49,8 +48,11 @@ describe 'vsphere_driver' do
49
48
  @metal_config[:machine_options][:convergence_options] = {:chef_server => chef_server}
50
49
  machine = @driver.ready_machine(action_handler, @machine_spec, @metal_config[:machine_options])
51
50
  @server_id = @machine_spec.location['server_id']
52
- @connection = vim(@metal_config[:driver_options])
53
- @vm = find_vm_by_id(@server_id, @connection)
51
+ @vsphere_helper = ChefProvisioningVsphere::VsphereHelper.new(
52
+ @metal_config[:driver_options],
53
+ @metal_config[:machine_options][:bootstrap_options][:datacenter]
54
+ )
55
+ @vm = @vsphere_helper.find_vm_by_id(@server_id)
54
56
  end
55
57
  end
56
58
 
@@ -101,7 +103,7 @@ describe 'vsphere_driver' do
101
103
  end
102
104
  end
103
105
  it 'is in the correct datacenter' do
104
- expect(@connection.serviceInstance.find_datacenter(@metal_config[:machine_options][:bootstrap_options][:datacenter]).find_vm("#{@vm.parent.name}/#{@vm_name}")).not_to eq(nil)
106
+ expect(@vsphere_helper.vim.serviceInstance.find_datacenter(@metal_config[:machine_options][:bootstrap_options][:datacenter]).find_vm("#{@vm.parent.name}/#{@vm_name}")).not_to eq(nil)
105
107
  end
106
108
  it 'has an added disk of the correct size' do
107
109
  disk_count = @vm.disks.count
@@ -146,7 +148,7 @@ describe 'vsphere_driver' do
146
148
  @metal_config[:machine_options]
147
149
  )
148
150
  end
149
- vm = find_vm_by_id(@server_id, @connection)
151
+ vm = @vsphere_helper.find_vm_by_id(@server_id)
150
152
  expect(vm).to eq(nil)
151
153
  end
152
154
  end
@@ -0,0 +1,161 @@
1
+ require 'chef/provisioning/vsphere_driver'
2
+ require_relative 'support/fake_action_handler'
3
+ require_relative 'support/vsphere_helper_stub'
4
+
5
+ describe ChefProvisioningVsphere::CloneSpecBuilder do
6
+ let(:options) { Hash.new }
7
+ let(:vm_template) { double('template') }
8
+
9
+ before do
10
+ allow(vm_template).to receive_message_chain(:config, :guestId)
11
+ .and_return('guest')
12
+ allow(vm_template).to receive_message_chain(:config, :template)
13
+ .and_return(false)
14
+ end
15
+
16
+ subject do
17
+ builder = ChefProvisioningVsphere::CloneSpecBuilder.new(
18
+ ChefProvisioningVsphereStubs::VsphereHelperStub.new,
19
+ ChefProvisioningVsphereStubs::FakeActionHandler.new
20
+ )
21
+ builder.build(vm_template, 'machine_name', options)
22
+ end
23
+
24
+ context 'using linked clones' do
25
+ before { options[:use_linked_clone] = true }
26
+
27
+ it 'sets the disk move type of the relocation spec' do
28
+ expect(subject.location.diskMoveType).to be :moveChildMostDiskBacking
29
+ end
30
+ end
31
+
32
+ context 'using linked clone on a template source' do
33
+ before do
34
+ options[:use_linked_clone] = true
35
+ options[:host] = 'host'
36
+ allow(vm_template).to receive_message_chain(:config, :template)
37
+ .and_return(true)
38
+ end
39
+
40
+ it 'does not set the disk move type of the relocation spec' do
41
+ expect(subject.location.diskMoveType).to be nil
42
+ end
43
+ end
44
+
45
+ context 'not using linked clones' do
46
+ before { options[:use_linked_clone] = false }
47
+
48
+ it 'does not set the disk move type of the relocation spec' do
49
+ expect(subject.location.diskMoveType).to be nil
50
+ end
51
+ end
52
+
53
+ context 'specifying a host' do
54
+ before { options[:host] = 'host' }
55
+
56
+ it 'sets the host' do
57
+ expect(subject.location.host).to_not be nil
58
+ end
59
+ end
60
+
61
+ context 'not specifying a host' do
62
+ it 'does not set the host' do
63
+ expect(subject.location.host).to be nil
64
+ end
65
+ end
66
+
67
+ context 'specifying a pool' do
68
+ before { options[:resource_pool] = 'pool' }
69
+
70
+ it 'sets the pool' do
71
+ expect(subject.location.pool).to_not be nil
72
+ end
73
+ end
74
+
75
+ context 'not specifying a pool' do
76
+ it 'does not set the pool' do
77
+ expect(subject.location.pool).to be nil
78
+ end
79
+ end
80
+
81
+ context 'not specifying a pool but specifying a host on a template' do
82
+ before do
83
+ options[:host] = 'host'
84
+ allow(vm_template).to receive_message_chain(:config, :template)
85
+ .and_return(true)
86
+ end
87
+
88
+ it 'sets the pool to the hosts parent root pool' do
89
+ expect(subject.location.pool).to be subject.location.host.parent.resourcePool
90
+ end
91
+ end
92
+
93
+ context 'not specifying a pool or host when cloning from a template' do
94
+ before do
95
+ allow(vm_template).to receive_message_chain(:config, :template)
96
+ .and_return(true)
97
+ end
98
+
99
+ it 'raises an error' do
100
+ expect { subject }.to raise_error
101
+ end
102
+ end
103
+
104
+ context 'specifying a hostname' do
105
+ before do
106
+ options[:customization_spec] = {
107
+ ipsettings: {},
108
+ hostname: hostname,
109
+ domain: 'local'
110
+ }
111
+ end
112
+
113
+ context 'alpha characters only' do
114
+ let(:hostname) { 'myhost' }
115
+
116
+ it 'sets the spec hostname' do
117
+ expect(subject.customization.identity.hostName.name).to eq hostname
118
+ end
119
+ end
120
+
121
+ context 'alpha numeric characters only' do
122
+ let(:hostname) { 'myhost01' }
123
+
124
+ it 'sets the spec hostname' do
125
+ expect(subject.customization.identity.hostName.name).to eq hostname
126
+ end
127
+ end
128
+
129
+ context 'containing a dash' do
130
+ let(:hostname) { 'my-host01' }
131
+
132
+ it 'sets the spec hostname' do
133
+ expect(subject.customization.identity.hostName.name).to eq hostname
134
+ end
135
+ end
136
+
137
+ context 'containing an underscore' do
138
+ let(:hostname) { 'my_host' }
139
+
140
+ it 'raises an error' do
141
+ expect { subject }.to raise_error
142
+ end
143
+ end
144
+
145
+ context 'starting with a dash' do
146
+ let(:hostname) { '-myhost' }
147
+
148
+ it 'raises an error' do
149
+ expect { subject }.to raise_error
150
+ end
151
+ end
152
+
153
+ context 'ending with a dash' do
154
+ let(:hostname) { 'myhost-' }
155
+
156
+ it 'raises an error' do
157
+ expect { subject }.to raise_error
158
+ end
159
+ end
160
+ end
161
+ end
@@ -0,0 +1,7 @@
1
+ module ChefProvisioningVsphereStubs
2
+ class FakeActionHandler < Chef::Provisioning::ActionHandler
3
+ def puts(out)
4
+
5
+ end
6
+ end
7
+ end
@@ -0,0 +1,52 @@
1
+ module ChefProvisioningVsphereStubs
2
+ class VsphereHelperStub < ChefProvisioningVsphere::VsphereHelper
3
+ def initialize
4
+ end
5
+
6
+ def network_device_changes(action_handler, vm_template, options)
7
+ [
8
+ [RbVmomi::VIM::VirtualDeviceConfigSpec.new],
9
+ [RbVmomi::VIM::VirtualDeviceConfigSpec.new]
10
+ ]
11
+ end
12
+
13
+ def find_host(host_name)
14
+ RbVmomi::VIM::HostSystem.new
15
+ end
16
+
17
+ def find_pool(pool_name)
18
+ RbVmomi::VIM::ResourcePool.new(nil, nil)
19
+ end
20
+
21
+ def find_datastore(datastore_name)
22
+ RbVmomi::VIM::Datastore.new
23
+ end
24
+
25
+ def find_customization_spec(options)
26
+ RbVmomi::VIM::CustomizationSpec.new
27
+ end
28
+
29
+ def create_delta_disk(vm_template)
30
+ end
31
+ end
32
+ end
33
+
34
+ module RbVmomi
35
+ class VIM::HostSystem
36
+ attr_reader :parent
37
+
38
+ def parent
39
+ @parent ||= RbVmomi::VIM::ComputeResource.new
40
+ end
41
+ end
42
+ end
43
+
44
+ module RbVmomi
45
+ class VIM::ComputeResource
46
+ attr_reader :resourcePool
47
+
48
+ def resourcePool
49
+ @resourcePool ||= RbVmomi::VIM::ResourcePool.new(nil, nil)
50
+ end
51
+ end
52
+ end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: chef-provisioning-vsphere
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.5.7
4
+ version: 0.5.8
5
5
  platform: ruby
6
6
  authors:
7
7
  - CenturyLink Cloud
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2015-06-09 00:00:00.000000000 Z
11
+ date: 2015-06-19 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rbvmomi
@@ -103,6 +103,7 @@ files:
103
103
  - contribution-notice
104
104
  - lib/chef/provisioning/driver_init/vsphere.rb
105
105
  - lib/chef/provisioning/vsphere_driver.rb
106
+ - lib/chef/provisioning/vsphere_driver/clone_spec_builder.rb
106
107
  - lib/chef/provisioning/vsphere_driver/driver.rb
107
108
  - lib/chef/provisioning/vsphere_driver/version.rb
108
109
  - lib/chef/provisioning/vsphere_driver/vsphere_helpers.rb
@@ -112,6 +113,9 @@ files:
112
113
  - spec/integration_tests/vsphere_driver_spec.rb
113
114
  - spec/unit_tests/VsphereDriver_spec.rb
114
115
  - spec/unit_tests/VsphereUrl_spec.rb
116
+ - spec/unit_tests/clone_spec_builder_spec.rb
117
+ - spec/unit_tests/support/fake_action_handler.rb
118
+ - spec/unit_tests/support/vsphere_helper_stub.rb
115
119
  homepage: https://github.com/tier3/chef-provisioning-vsphere
116
120
  licenses:
117
121
  - MIT
@@ -141,4 +145,7 @@ test_files:
141
145
  - spec/integration_tests/vsphere_driver_spec.rb
142
146
  - spec/unit_tests/VsphereDriver_spec.rb
143
147
  - spec/unit_tests/VsphereUrl_spec.rb
148
+ - spec/unit_tests/clone_spec_builder_spec.rb
149
+ - spec/unit_tests/support/fake_action_handler.rb
150
+ - spec/unit_tests/support/vsphere_helper_stub.rb
144
151
  has_rdoc: