chef-provisioning-vsphere 0.8.4 → 0.9.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.
- checksums.yaml +4 -4
- data/README.md +272 -269
- data/chef-provisioning-vsphere.gemspec +29 -28
- data/lib/chef/provisioning/vsphere_driver/clone_spec_builder.rb +201 -205
- data/lib/chef/provisioning/vsphere_driver/driver.rb +688 -679
- data/lib/chef/provisioning/vsphere_driver/version.rb +3 -3
- data/lib/chef/provisioning/vsphere_driver/vsphere_helpers.rb +343 -341
- data/spec/integration_tests/vsphere_driver_spec.rb +158 -158
- metadata +17 -3
@@ -1,29 +1,30 @@
|
|
1
|
-
$:.unshift(File.dirname(__FILE__) + '/lib')
|
2
|
-
require 'chef/provisioning/vsphere_driver/version'
|
3
|
-
|
4
|
-
Gem::Specification.new do |s|
|
5
|
-
s.name = 'chef-provisioning-vsphere'
|
6
|
-
s.version = ChefProvisioningVsphere::VERSION
|
7
|
-
s.platform = Gem::Platform::RUBY
|
8
|
-
s.extra_rdoc_files = ['README.md']
|
9
|
-
s.summary = 'Provisioner for creating vSphere VM instances in Chef Provisioning.'
|
10
|
-
s.description = s.summary
|
11
|
-
s.authors = ['CenturyLink Cloud']
|
12
|
-
s.email = 'matt.wrock@CenturyLinkCloud.com'
|
13
|
-
s.homepage = 'https://github.com/tier3/chef-provisioning-vsphere'
|
14
|
-
s.license = 'MIT'
|
15
|
-
|
16
|
-
s.bindir = 'bin'
|
17
|
-
s.executables = %w( )
|
18
|
-
|
19
|
-
s.require_path = 'lib'
|
20
|
-
s.files = `git ls-files -z`.split("\x0")
|
21
|
-
s.test_files = s.files.grep(%r{^(test|spec|features)/})
|
22
|
-
|
23
|
-
s.add_dependency 'rbvmomi', '~> 1.8.0', '>= 1.8.2'
|
24
|
-
s.add_dependency 'chef-provisioning', '~>1.1'
|
25
|
-
|
26
|
-
|
27
|
-
s.add_development_dependency '
|
28
|
-
s.add_development_dependency '
|
1
|
+
$:.unshift(File.dirname(__FILE__) + '/lib')
|
2
|
+
require 'chef/provisioning/vsphere_driver/version'
|
3
|
+
|
4
|
+
Gem::Specification.new do |s|
|
5
|
+
s.name = 'chef-provisioning-vsphere'
|
6
|
+
s.version = ChefProvisioningVsphere::VERSION
|
7
|
+
s.platform = Gem::Platform::RUBY
|
8
|
+
s.extra_rdoc_files = ['README.md']
|
9
|
+
s.summary = 'Provisioner for creating vSphere VM instances in Chef Provisioning.'
|
10
|
+
s.description = s.summary
|
11
|
+
s.authors = ['CenturyLink Cloud']
|
12
|
+
s.email = 'matt.wrock@CenturyLinkCloud.com'
|
13
|
+
s.homepage = 'https://github.com/tier3/chef-provisioning-vsphere'
|
14
|
+
s.license = 'MIT'
|
15
|
+
|
16
|
+
s.bindir = 'bin'
|
17
|
+
s.executables = %w( )
|
18
|
+
|
19
|
+
s.require_path = 'lib'
|
20
|
+
s.files = `git ls-files -z`.split("\x0")
|
21
|
+
s.test_files = s.files.grep(%r{^(test|spec|features)/})
|
22
|
+
|
23
|
+
s.add_dependency 'rbvmomi', '~> 1.8.0', '>= 1.8.2'
|
24
|
+
s.add_dependency 'chef-provisioning', '~>1.1'
|
25
|
+
s.add_dependency 'winrm', '~> 1.6'
|
26
|
+
|
27
|
+
s.add_development_dependency 'rspec'
|
28
|
+
s.add_development_dependency 'rake'
|
29
|
+
s.add_development_dependency 'rubocop', '~> 0.29'
|
29
30
|
end
|
@@ -1,205 +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
|
-
options[:customization_spec].is_a?(Cheffish::MergedConfig)
|
91
|
-
cust_options = options[:customization_spec]
|
92
|
-
ip_settings = cust_options[:ipsettings]
|
93
|
-
cust_domain = cust_options[:domain]
|
94
|
-
|
95
|
-
raise ArgumentError, 'domain is required' unless cust_domain
|
96
|
-
cust_ip_settings = nil
|
97
|
-
if ip_settings && ip_settings.key?(:ip)
|
98
|
-
unless cust_options[:ipsettings].key?(:subnetMask)
|
99
|
-
raise ArgumentError, 'subnetMask is required for static ip'
|
100
|
-
end
|
101
|
-
cust_ip_settings = RbVmomi::VIM::CustomizationIPSettings.new(
|
102
|
-
ip_settings)
|
103
|
-
action_handler.report_progress "customizing #{vm_name} \
|
104
|
-
with static IP #{ip_settings[:ip]}"
|
105
|
-
cust_ip_settings.ip = RbVmomi::VIM::CustomizationFixedIp(
|
106
|
-
:ipAddress => ip_settings[:ip])
|
107
|
-
end
|
108
|
-
if cust_ip_settings.nil?
|
109
|
-
cust_ip_settings= RbVmomi::VIM::CustomizationIPSettings.new(
|
110
|
-
:ip => RbVmomi::VIM::CustomizationDhcpIpGenerator.new())
|
111
|
-
end
|
112
|
-
|
113
|
-
if ip_settings && ip_settings.key?(:dnsServerList)
|
114
|
-
cust_ip_settings.dnsServerList = ip_settings[:dnsServerList]
|
115
|
-
action_handler.report_progress "customizing #{vm_name} with /
|
116
|
-
dynamic IP and DNS: #{ip_settings[:dnsServerList]}"
|
117
|
-
end
|
118
|
-
|
119
|
-
cust_ip_settings.dnsDomain = cust_domain
|
120
|
-
global_ip_settings = RbVmomi::VIM::CustomizationGlobalIPSettings.new
|
121
|
-
global_ip_settings.dnsServerList = cust_ip_settings.dnsServerList
|
122
|
-
global_ip_settings.dnsSuffixList = [cust_domain]
|
123
|
-
cust_hostname = hostname_from(cust_options, vm_name)
|
124
|
-
cust_hwclockutc = cust_options[:hw_clock_utc]
|
125
|
-
cust_timezone = cust_options[:time_zone]
|
126
|
-
|
127
|
-
if vm_template.config.guestId.start_with?('win')
|
128
|
-
cust_prep = windows_prep_for(options, vm_name)
|
129
|
-
else
|
130
|
-
cust_prep = RbVmomi::VIM::CustomizationLinuxPrep.new(
|
131
|
-
:domain => cust_domain,
|
132
|
-
:hostName => cust_hostname,
|
133
|
-
:hwClockUTC => cust_hwclockutc,
|
134
|
-
:timeZone => cust_timezone
|
135
|
-
)
|
136
|
-
end
|
137
|
-
cust_adapter_mapping = [
|
138
|
-
RbVmomi::VIM::CustomizationAdapterMapping.new(
|
139
|
-
:adapter => cust_ip_settings)
|
140
|
-
]
|
141
|
-
RbVmomi::VIM::CustomizationSpec.new(
|
142
|
-
:identity => cust_prep,
|
143
|
-
:globalIPSettings => global_ip_settings,
|
144
|
-
:nicSettingMap => cust_adapter_mapping
|
145
|
-
)
|
146
|
-
else
|
147
|
-
vsphere_helper.find_customization_spec(cust_options)
|
148
|
-
end
|
149
|
-
end
|
150
|
-
end
|
151
|
-
|
152
|
-
def hostname_from(options, vm_name)
|
153
|
-
hostname = options[:hostname] || vm_name
|
154
|
-
test = /^([a-zA-Z0-9]|[a-zA-Z0-9][a-zA-Z0-9\-]*[a-zA-Z0-9])$/
|
155
|
-
if !(hostname =~ test)
|
156
|
-
raise 'Only letters, numbers or hyphens in hostnames allowed'
|
157
|
-
end
|
158
|
-
RbVmomi::VIM::CustomizationFixedName.new(:name => hostname)
|
159
|
-
end
|
160
|
-
|
161
|
-
def windows_prep_for(options, vm_name)
|
162
|
-
cust_options = options[:customization_spec]
|
163
|
-
cust_runonce = RbVmomi::VIM::CustomizationGuiRunOnce.new(
|
164
|
-
:commandList => [
|
165
|
-
|
166
|
-
|
167
|
-
|
168
|
-
|
169
|
-
|
170
|
-
|
171
|
-
|
172
|
-
|
173
|
-
|
174
|
-
|
175
|
-
:
|
176
|
-
:
|
177
|
-
|
178
|
-
|
179
|
-
:
|
180
|
-
|
181
|
-
|
182
|
-
|
183
|
-
|
184
|
-
|
185
|
-
|
186
|
-
|
187
|
-
|
188
|
-
|
189
|
-
|
190
|
-
:
|
191
|
-
:
|
192
|
-
:
|
193
|
-
|
194
|
-
|
195
|
-
:
|
196
|
-
:
|
197
|
-
:
|
198
|
-
|
199
|
-
|
200
|
-
|
201
|
-
|
202
|
-
:userData => cust_userdata)
|
203
|
-
end
|
204
|
-
end
|
205
|
-
end
|
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
|
+
options[:customization_spec].is_a?(Cheffish::MergedConfig)
|
91
|
+
cust_options = options[:customization_spec]
|
92
|
+
ip_settings = cust_options[:ipsettings]
|
93
|
+
cust_domain = cust_options[:domain]
|
94
|
+
|
95
|
+
raise ArgumentError, 'domain is required' unless cust_domain
|
96
|
+
cust_ip_settings = nil
|
97
|
+
if ip_settings && ip_settings.key?(:ip)
|
98
|
+
unless cust_options[:ipsettings].key?(:subnetMask)
|
99
|
+
raise ArgumentError, 'subnetMask is required for static ip'
|
100
|
+
end
|
101
|
+
cust_ip_settings = RbVmomi::VIM::CustomizationIPSettings.new(
|
102
|
+
ip_settings)
|
103
|
+
action_handler.report_progress "customizing #{vm_name} \
|
104
|
+
with static IP #{ip_settings[:ip]}"
|
105
|
+
cust_ip_settings.ip = RbVmomi::VIM::CustomizationFixedIp(
|
106
|
+
:ipAddress => ip_settings[:ip])
|
107
|
+
end
|
108
|
+
if cust_ip_settings.nil?
|
109
|
+
cust_ip_settings= RbVmomi::VIM::CustomizationIPSettings.new(
|
110
|
+
:ip => RbVmomi::VIM::CustomizationDhcpIpGenerator.new())
|
111
|
+
end
|
112
|
+
|
113
|
+
if ip_settings && ip_settings.key?(:dnsServerList)
|
114
|
+
cust_ip_settings.dnsServerList = ip_settings[:dnsServerList]
|
115
|
+
action_handler.report_progress "customizing #{vm_name} with /
|
116
|
+
dynamic IP and DNS: #{ip_settings[:dnsServerList]}"
|
117
|
+
end
|
118
|
+
|
119
|
+
cust_ip_settings.dnsDomain = cust_domain
|
120
|
+
global_ip_settings = RbVmomi::VIM::CustomizationGlobalIPSettings.new
|
121
|
+
global_ip_settings.dnsServerList = cust_ip_settings.dnsServerList
|
122
|
+
global_ip_settings.dnsSuffixList = [cust_domain]
|
123
|
+
cust_hostname = hostname_from(cust_options, vm_name)
|
124
|
+
cust_hwclockutc = cust_options[:hw_clock_utc]
|
125
|
+
cust_timezone = cust_options[:time_zone]
|
126
|
+
|
127
|
+
if vm_template.config.guestId.start_with?('win')
|
128
|
+
cust_prep = windows_prep_for(options, vm_name)
|
129
|
+
else
|
130
|
+
cust_prep = RbVmomi::VIM::CustomizationLinuxPrep.new(
|
131
|
+
:domain => cust_domain,
|
132
|
+
:hostName => cust_hostname,
|
133
|
+
:hwClockUTC => cust_hwclockutc,
|
134
|
+
:timeZone => cust_timezone
|
135
|
+
)
|
136
|
+
end
|
137
|
+
cust_adapter_mapping = [
|
138
|
+
RbVmomi::VIM::CustomizationAdapterMapping.new(
|
139
|
+
:adapter => cust_ip_settings)
|
140
|
+
]
|
141
|
+
RbVmomi::VIM::CustomizationSpec.new(
|
142
|
+
:identity => cust_prep,
|
143
|
+
:globalIPSettings => global_ip_settings,
|
144
|
+
:nicSettingMap => cust_adapter_mapping
|
145
|
+
)
|
146
|
+
else
|
147
|
+
vsphere_helper.find_customization_spec(cust_options)
|
148
|
+
end
|
149
|
+
end
|
150
|
+
end
|
151
|
+
|
152
|
+
def hostname_from(options, vm_name)
|
153
|
+
hostname = options[:hostname] || vm_name
|
154
|
+
test = /^([a-zA-Z0-9]|[a-zA-Z0-9][a-zA-Z0-9\-]*[a-zA-Z0-9])$/
|
155
|
+
if !(hostname =~ test)
|
156
|
+
raise 'Only letters, numbers or hyphens in hostnames allowed'
|
157
|
+
end
|
158
|
+
RbVmomi::VIM::CustomizationFixedName.new(:name => hostname)
|
159
|
+
end
|
160
|
+
|
161
|
+
def windows_prep_for(options, vm_name)
|
162
|
+
cust_options = options[:customization_spec]
|
163
|
+
cust_runonce = RbVmomi::VIM::CustomizationGuiRunOnce.new(
|
164
|
+
:commandList => cust_options[:run_once]) unless cust_options[:run_once].nil?
|
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
|