chef-provisioning-vsphere 0.5.7 → 0.5.8

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