chef-metal-vsphere 0.2.0 → 0.3.0.beta.1
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/.gitignore +27 -0
- data/CHANGELOG.md +6 -0
- data/Gemfile +3 -0
- data/{LICENSE → LICENSE.txt} +4 -2
- data/README.md +29 -42
- data/Rakefile +31 -22
- data/TODO.md +41 -0
- data/chef-metal-vpshere.gemspec +34 -0
- data/lib/chef_metal/driver_init/vsphere.rb +3 -0
- data/lib/chef_metal_vsphere.rb +1 -11
- data/lib/chef_metal_vsphere/version.rb +1 -1
- data/lib/chef_metal_vsphere/vsphere_driver.rb +325 -0
- data/spec/driver_spec.rb +21 -0
- data/spec/spec_helper.rb +2 -0
- data/test/.chef/knife.rb +4 -0
- data/test/cookbooks/test/libraries/vmonkey_helper.rb +34 -0
- data/test/cookbooks/test/recipes/destroy_all.rb +8 -0
- data/test/cookbooks/test/recipes/simple.rb +11 -0
- data/test/cookbooks/test/recipes/vagrant.rb +11 -0
- data/test/cookbooks/test/recipes/vsphere.rb +18 -0
- metadata +111 -34
- data/lib/chef_metal/provisioner_init/vsphere_init.rb +0 -3
- data/lib/chef_metal_vsphere/vsphere_helpers.rb +0 -168
- data/lib/chef_metal_vsphere/vsphere_provisioner.rb +0 -310
@@ -1,310 +0,0 @@
|
|
1
|
-
require 'chef_metal/provisioner'
|
2
|
-
require 'chef_metal/machine/windows_machine'
|
3
|
-
require 'chef_metal/machine/unix_machine'
|
4
|
-
require 'chef_metal/convergence_strategy/install_msi'
|
5
|
-
require 'chef_metal/convergence_strategy/install_cached'
|
6
|
-
require 'chef_metal/transport/ssh'
|
7
|
-
require 'chef_metal_vsphere/version'
|
8
|
-
require 'rbvmomi'
|
9
|
-
require 'chef_metal_vsphere/vsphere_helpers'
|
10
|
-
|
11
|
-
module ChefMetalVsphere
|
12
|
-
# Provisions machines in vSphere.
|
13
|
-
class VsphereProvisioner < ChefMetal::Provisioner
|
14
|
-
|
15
|
-
include Chef::Mixin::ShellOut
|
16
|
-
include ChefMetalVsphere::Helpers
|
17
|
-
|
18
|
-
def self.inflate(node)
|
19
|
-
url = node['normal']['provisioner_output']['provisioner_url']
|
20
|
-
scheme, provider, id = url.split(':', 3)
|
21
|
-
VsphereProvisioner.new({ :provider => provider }, id)
|
22
|
-
end
|
23
|
-
|
24
|
-
# Create a new Vsphere provisioner.
|
25
|
-
#
|
26
|
-
# ## Parameters
|
27
|
-
# connect_options - hash of options to be passed to RbVmomi::VIM.connect
|
28
|
-
# :vsphere_host - required - hostname of the vSphere API server
|
29
|
-
# :vsphere_port - optional - port on the vSphere API server (default: 443)
|
30
|
-
# :vshere_path - optional - path on the vSphere API server (default: /sdk)
|
31
|
-
# :vsphere_ssl - optional - true to use ssl in connection to vSphere API server (default: true)
|
32
|
-
# :vsphere_insecure - optional - true to ignore ssl certificate validation errors in connection to vSphere API server (default: false)
|
33
|
-
# :vsphere_user - required - user name to use in connection to vSphere API server
|
34
|
-
# :vsphere_password - required - password to use in connection to vSphere API server
|
35
|
-
# :proxy_host - optional - http proxy host to use in connection to vSphere API server (default: none)
|
36
|
-
# :proxy_port - optional - http proxy port to use in connection to vSphere API server (default: none)
|
37
|
-
def initialize(connect_options)
|
38
|
-
connect_options = stringify_keys(connect_options)
|
39
|
-
default_connect_options = {
|
40
|
-
'vsphere_port' => 443,
|
41
|
-
'vsphere_ssl' => true,
|
42
|
-
'vsphere_insecure' => false,
|
43
|
-
'vsphere_path' => '/sdk'
|
44
|
-
}
|
45
|
-
|
46
|
-
@connect_options = default_connect_options.merge(connect_options)
|
47
|
-
|
48
|
-
required_options = %w( vsphere_host vsphere_user vsphere_password )
|
49
|
-
missing_options = []
|
50
|
-
required_options.each do |opt|
|
51
|
-
missing_options << opt unless @connect_options.has_key?(opt)
|
52
|
-
end
|
53
|
-
unless missing_options.empty?
|
54
|
-
raise "missing required options: #{missing_options.join(', ')}"
|
55
|
-
end
|
56
|
-
|
57
|
-
# test vim connection
|
58
|
-
vim || raise("cannot connect to [#{provisioner_url}]")
|
59
|
-
|
60
|
-
@connect_options
|
61
|
-
end
|
62
|
-
|
63
|
-
attr_reader :connect_options
|
64
|
-
|
65
|
-
# Acquire a machine, generally by provisioning it. Returns a Machine
|
66
|
-
# object pointing at the machine, allowing useful actions like setup,
|
67
|
-
# converge, execute, file and directory. The Machine object will have a
|
68
|
-
# "node" property which must be saved to the server (if it is any
|
69
|
-
# different from the original node object).
|
70
|
-
#
|
71
|
-
# ## Parameters
|
72
|
-
# action_handler - the action_handler object that is calling this method; this
|
73
|
-
# is generally a action_handler, but could be anything that can support the
|
74
|
-
# ChefMetal::ActionHandler interface (i.e., in the case of the test
|
75
|
-
# kitchen metal driver for acquiring and destroying VMs; see the base
|
76
|
-
# class for what needs providing).
|
77
|
-
# node - node object (deserialized json) representing this machine. If
|
78
|
-
# the node has a provisioner_options hash in it, these will be used
|
79
|
-
# instead of options provided by the provisioner. TODO compare and
|
80
|
-
# fail if different?
|
81
|
-
# node will have node['normal']['provisioner_options'] in it with any options.
|
82
|
-
# It is a hash with this format:
|
83
|
-
#
|
84
|
-
# -- provisioner_url: vsphere://host:port?ssl=[true|false]&insecure=[true|false]
|
85
|
-
# -- bootstrap_options: hash of options to pass to RbVmomi::VIM::VirtualMachine::CloneTask()
|
86
|
-
# :datacenter
|
87
|
-
# :resource_pool
|
88
|
-
# :cluster
|
89
|
-
# :datastore
|
90
|
-
# :template_name
|
91
|
-
# :template_folder
|
92
|
-
# :vm_folder
|
93
|
-
# :winrm {...} (not yet implemented)
|
94
|
-
# :ssh {...}
|
95
|
-
#
|
96
|
-
# Example bootstrap_options for vSphere:
|
97
|
-
# TODO: add other CloneTask params, e.g.: datastore, annotation, resource_pool, ...
|
98
|
-
# 'bootstrap_options' => {
|
99
|
-
# 'template_name' =>'centos6.small',
|
100
|
-
# 'template_folder' =>'Templates',
|
101
|
-
# 'vm_folder' => 'MyApp'
|
102
|
-
# }
|
103
|
-
#
|
104
|
-
# node['normal']['provisioner_output'] will be populated with information
|
105
|
-
# about the created machine. For vSphere, it is a hash with this
|
106
|
-
# format:
|
107
|
-
#
|
108
|
-
# -- provisioner_url: vsphere:host:port?ssl=[true|false]&insecure=[true|false]
|
109
|
-
# -- vm_folder: name of the vSphere folder containing the VM
|
110
|
-
#
|
111
|
-
def acquire_machine(action_handler, node)
|
112
|
-
# Set up the provisioner output
|
113
|
-
provisioner_options = stringify_keys(node['normal']['provisioner_options'])
|
114
|
-
|
115
|
-
vm_name = node['name']
|
116
|
-
old_provisioner_output = node['normal']['provisioner_output']
|
117
|
-
node['normal']['provisioner_output'] = provisioner_output = {
|
118
|
-
'provisioner_url' => provisioner_url,
|
119
|
-
'vm_name' => vm_name,
|
120
|
-
'bootstrap_options' => provisioner_options['bootstrap_options']
|
121
|
-
}
|
122
|
-
|
123
|
-
bootstrap_options = node['normal']['provisioner_output']['bootstrap_options']
|
124
|
-
vm_folder = bootstrap_options['vm_folder']
|
125
|
-
|
126
|
-
if bootstrap_options['ssh']
|
127
|
-
wait_on_port = bootstrap_options['ssh']['port']
|
128
|
-
raise "Must specify bootstrap_options[:ssh][:port]" if wait_on_port.nil?
|
129
|
-
else
|
130
|
-
raise 'bootstrapping is currently supported for ssh only'
|
131
|
-
# wait_on_port = bootstrap_options['winrm']['port']
|
132
|
-
end
|
133
|
-
|
134
|
-
# TODO compare new options to existing and fail if we cannot change it
|
135
|
-
# over (perhaps introduce a boolean that will force a delete and recreate
|
136
|
-
# in such a case)
|
137
|
-
|
138
|
-
vm = vm_instance(action_handler, node)
|
139
|
-
|
140
|
-
unless vm_started?(vm, wait_on_port)
|
141
|
-
action_handler.perform_action "Start VM and wait for port #{wait_on_port}" do
|
142
|
-
start_vm(vm, wait_on_port)
|
143
|
-
end
|
144
|
-
end
|
145
|
-
|
146
|
-
machine = machine_for(node)
|
147
|
-
|
148
|
-
machine
|
149
|
-
end
|
150
|
-
|
151
|
-
# Connect to machine without acquiring it
|
152
|
-
def connect_to_machine(node)
|
153
|
-
machine_for(node)
|
154
|
-
end
|
155
|
-
|
156
|
-
def delete_machine(action_handler, node)
|
157
|
-
if node['normal'] && node['normal']['provisioner_output']
|
158
|
-
provisioner_output = node['normal']['provisioner_output']
|
159
|
-
else
|
160
|
-
provisioner_output = {}
|
161
|
-
end
|
162
|
-
vm_name = provisioner_output['vm_name'] || node['name']
|
163
|
-
vm_folder = provisioner_output['bootstrap_options']['vm_folder']
|
164
|
-
vm = vm_for(node)
|
165
|
-
|
166
|
-
unless vm.nil?
|
167
|
-
action_handler.perform_action "Delete VM [#{vm_folder}/#{vm_name}]" do
|
168
|
-
vm.PowerOffVM_Task.wait_for_completion unless vm.runtime.powerState == 'poweredOff'
|
169
|
-
vm.Destroy_Task.wait_for_completion
|
170
|
-
end
|
171
|
-
end
|
172
|
-
end
|
173
|
-
|
174
|
-
def stop_machine(action_handler, node)
|
175
|
-
if node['normal'] && node['normal']['provisioner_output']
|
176
|
-
provisioner_output = node['normal']['provisioner_output']
|
177
|
-
else
|
178
|
-
provisioner_output = {}
|
179
|
-
end
|
180
|
-
vm_name = provisioner_output['vm_name'] || node['name']
|
181
|
-
vm_folder = provisioner_output['bootstrap_options']['vm_folder']
|
182
|
-
vm = vm_for(node)
|
183
|
-
|
184
|
-
unless vm_stopped?(vm)
|
185
|
-
action_handler.perform_action "Shutdown guest OS and power off VM [#{vm_folder}/#{vm_name}]" do
|
186
|
-
stop_vm(vm)
|
187
|
-
end
|
188
|
-
end
|
189
|
-
end
|
190
|
-
|
191
|
-
protected
|
192
|
-
|
193
|
-
def provisioner_url
|
194
|
-
"vsphere://#{connect_options['vsphere_host']}:#{connect_options['vsphere_port']}#{connect_options['vsphere_path']}?ssl=#{connect_options['vsphere_ssl']}&insecure=#{connect_options['vsphere_insecure']}"
|
195
|
-
end
|
196
|
-
|
197
|
-
def vm_instance(action_handler, node)
|
198
|
-
bootstrap_options = node['normal']['provisioner_output']['bootstrap_options']
|
199
|
-
|
200
|
-
datacenter = bootstrap_options['datacenter']
|
201
|
-
vm_name = node['normal']['provisioner_output']['vm_name']
|
202
|
-
vm_folder = bootstrap_options['vm_folder']
|
203
|
-
|
204
|
-
vm = find_vm(datacenter, vm_folder, vm_name)
|
205
|
-
return vm unless vm.nil?
|
206
|
-
|
207
|
-
action_handler.perform_action "Clone a new VM instance from [#{bootstrap_options['template_folder']}/#{bootstrap_options['template_name']}]" do
|
208
|
-
vm = clone_vm(vm_name, bootstrap_options)
|
209
|
-
end
|
210
|
-
|
211
|
-
vm
|
212
|
-
end
|
213
|
-
|
214
|
-
def clone_vm(vm_name, bootstrap_options)
|
215
|
-
datacenter = bootstrap_options['datacenter']
|
216
|
-
template_folder = bootstrap_options['template_folder']
|
217
|
-
template_name = bootstrap_options['template_name']
|
218
|
-
|
219
|
-
vm_template = find_vm(datacenter, template_folder, template_name) or raise("vSphere VM Template not found [#{template_folder}/#{template_name}]")
|
220
|
-
|
221
|
-
vm = do_vm_clone(datacenter, vm_template, vm_name, bootstrap_options)
|
222
|
-
end
|
223
|
-
|
224
|
-
def machine_for(node)
|
225
|
-
vm = vm_for(node) or raise "VM for node #{node['name']} has not been created!"
|
226
|
-
|
227
|
-
if is_windows?(vm)
|
228
|
-
ChefMetal::Machine::WindowsMachine.new(node, transport_for(node), convergence_strategy_for(node))
|
229
|
-
else
|
230
|
-
ChefMetal::Machine::UnixMachine.new(node, transport_for(node), convergence_strategy_for(node))
|
231
|
-
end
|
232
|
-
end
|
233
|
-
|
234
|
-
|
235
|
-
def vm_for(node)
|
236
|
-
bootstrap_options = node['normal']['provisioner_output']['bootstrap_options']
|
237
|
-
datacenter = bootstrap_options['datacenter']
|
238
|
-
vm_folder = bootstrap_options['vm_folder']
|
239
|
-
vm_name = node['normal']['provisioner_output']['vm_name']
|
240
|
-
vm = find_vm(datacenter, vm_folder, vm_name)
|
241
|
-
vm
|
242
|
-
end
|
243
|
-
|
244
|
-
def is_windows?(vm)
|
245
|
-
return false if vm.nil?
|
246
|
-
vm.guest.guestFamily == 'windowsGuest'
|
247
|
-
end
|
248
|
-
|
249
|
-
def convergence_strategy_for(node)
|
250
|
-
if is_windows?(vm_for(node))
|
251
|
-
@windows_convergence_strategy ||= begin
|
252
|
-
options = {}
|
253
|
-
provisioner_options = node['normal']['provisioner_options'] || {}
|
254
|
-
options[:chef_client_timeout] = provisioner_options['chef_client_timeout'] if provisioner_options.has_key?('chef_client_timeout')
|
255
|
-
ChefMetal::ConvergenceStrategy::InstallMsi.new(options)
|
256
|
-
end
|
257
|
-
else
|
258
|
-
@unix_convergence_strategy ||= begin
|
259
|
-
options = {}
|
260
|
-
provisioner_options = node['normal']['provisioner_options'] || {}
|
261
|
-
options[:chef_client_timeout] = provisioner_options['chef_client_timeout'] if provisioner_options.has_key?('chef_client_timeout')
|
262
|
-
ChefMetal::ConvergenceStrategy::InstallCached.new(options)
|
263
|
-
end
|
264
|
-
end
|
265
|
-
end
|
266
|
-
|
267
|
-
def transport_for(node)
|
268
|
-
if is_windows?(vm_for(node))
|
269
|
-
create_winrm_transport(node)
|
270
|
-
else
|
271
|
-
create_ssh_transport(node)
|
272
|
-
end
|
273
|
-
end
|
274
|
-
|
275
|
-
def create_winrm_transport(node)
|
276
|
-
raise 'Windows guest VMs are not yet supported'
|
277
|
-
end
|
278
|
-
|
279
|
-
def create_ssh_transport(node)
|
280
|
-
bootstrap_options = node['normal']['provisioner_output']['bootstrap_options']
|
281
|
-
vm = vm_for(node) or raise "VM for node #{node['name']} has not been created!"
|
282
|
-
|
283
|
-
hostname = vm.guest.ipAddress
|
284
|
-
ssh_user = bootstrap_options['ssh']['user']
|
285
|
-
ssh_options = symbolize_keys(bootstrap_options['ssh'])
|
286
|
-
transport_options = {
|
287
|
-
:prefix => 'sudo '
|
288
|
-
}
|
289
|
-
ChefMetal::Transport::SSH.new(hostname, ssh_user, ssh_options, transport_options)
|
290
|
-
end
|
291
|
-
|
292
|
-
def stringify_keys(h)
|
293
|
-
Hash === h ?
|
294
|
-
Hash[
|
295
|
-
h.map do |k, v|
|
296
|
-
[k.respond_to?(:to_s) ? k.to_s : k, stringify_keys(v)]
|
297
|
-
end
|
298
|
-
] : h
|
299
|
-
end
|
300
|
-
|
301
|
-
def symbolize_keys(h)
|
302
|
-
Hash === h ?
|
303
|
-
Hash[
|
304
|
-
h.map do |k, v|
|
305
|
-
[k.respond_to?(:to_sym) ? k.to_sym : k, symbolize_keys(v)]
|
306
|
-
end
|
307
|
-
] : h
|
308
|
-
end
|
309
|
-
end
|
310
|
-
end
|