chef-metal-vsphere 0.1.3

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.
data/LICENSE ADDED
@@ -0,0 +1,20 @@
1
+ Copyright (c) 2014 Rally Software Development Corp
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,77 @@
1
+ chef-metal-vsphere
2
+ ==================
3
+
4
+ This is a [chef-metal](https://github.com/opscode/chef-metal) provisioner for [VMware vSphere](http://www.vmware.com/products/vsphere).
5
+
6
+ Currently, chef-metal-vsphere supports provisioning Unix/ssh guest VMs.
7
+
8
+ Try It Out
9
+ ----------
10
+
11
+ ### vSphere VM Template
12
+
13
+ Create or obtain a unix/linux VM template. The VM template must:
14
+
15
+ - be capable of installing Chef 11.8 or newer
16
+ - run vmware-tools on system boot (provides visiblity to ip address of the running VM)
17
+ - provide access via ssh
18
+ - provide a user account with NOPASSWD sudo
19
+
20
+ ### Example recipe
21
+ require 'chef_metal_vsphere'
22
+
23
+ with_vsphere_provisioner vsphere_host: 'vcenter-host-name',
24
+ vsphere_insecure: true,
25
+ vsphere_user: 'you_user_name',
26
+ vsphere_password: 'your_mothers_maiden_name' # consider using a chef-vault
27
+
28
+ with_provisioner_options('bootstrap_options' => {
29
+ datacenter: 'datacenter_name',
30
+ cluster: 'cluster_name',
31
+ resource_pool: 'resource_pool_name', # often the same as the cluster_name
32
+ datastore: 'datastore_name',
33
+ template_name: 'name_of_template_vm', # may be a VM or a VM Template
34
+ template_folder: 'folder_containing_template_vm',
35
+ vm_folder: 'folder_to_clone_vms_into',
36
+
37
+ ssh: { # net-ssh start() options
38
+ user: 'username_on_vm', # must have nopasswd sudo
39
+ password: 'name_of_your_first_pet', # consider using a chef-vault
40
+ port: 22,
41
+ auth_methods: ['password'],
42
+ user_known_hosts_file: '/dev/null', # don't do this in production
43
+ paranoid: false, # don't do this in production, either
44
+ keys: [ ], # consider using a chef-vault
45
+ keys_only: false
46
+ }
47
+ })
48
+
49
+ 1.upto 2 do |n|
50
+ machine "metal_#{n}" do
51
+ action [:create]
52
+ end
53
+
54
+ machine_file "/tmp/metal_#{n}.txt" do
55
+ machine "metal_#{n}"
56
+ content "Hello machine #{n}!"
57
+ end
58
+
59
+ machine "metal_#{n}" do
60
+ action [:stop]
61
+ end
62
+
63
+ machine "metal_#{n}" do
64
+ # note: no need to :stop before :delete
65
+ action [:delete]
66
+ end
67
+
68
+ end
69
+
70
+ This will clone your VM template to create two VMware Virtual Machines, "metal_1" and "metal_2", in the vSphere Folder specified by vm_folder, bootstrapped to an empty runlist. It will then stop (guest shutdown) and delete the vms.
71
+
72
+ Bugs and Contact
73
+ ----------------
74
+
75
+ Please submit bugs at [https://github.com/RallySoftware-cookbooks/chef-metal-vsphere], contact Brian Dupras on Twitter at @briandupras, email at rallysoftware-cookbooks@rallydev.com.
76
+
77
+ *Warning* if you get an rbvmomi error regarding VMODL::AnyType, add `gem 'nokogiri', '1.5.5'` to your dependencies.
data/Rakefile ADDED
@@ -0,0 +1,30 @@
1
+ require 'bundler'
2
+ require 'bundler/gem_tasks'
3
+
4
+ def gem_server
5
+ @gem_server ||= ENV['GEM_SERVER'] || 'rubygems.org'
6
+ end
7
+
8
+ module Bundler
9
+ class GemHelper
10
+ unless gem_server == 'rubygems.org'
11
+ unless method_defined?(:rubygem_push)
12
+ raise NoMethodError, "Monkey patching Bundler::GemHelper#rubygem_push failed: did the Bundler API change???"
13
+ end
14
+
15
+ def rubygem_push(path)
16
+ print "Username: "
17
+ username = STDIN.gets.chomp
18
+ print "Password: "
19
+ password = STDIN.gets.chomp
20
+
21
+ gem_server_url = "https://#{username}:#{password}@#{gem_server}/"
22
+ sh %{gem push #{path} --host #{gem_server_url}}
23
+
24
+ Bundler.ui.confirm "Pushed #{name} #{version} to #{gem_server}"
25
+ end
26
+
27
+ puts "Monkey patched Bundler::GemHelper#rubygem_push to push to #{gem_server}."
28
+ end
29
+ end
30
+ end
@@ -0,0 +1,3 @@
1
+ require 'chef_metal_vsphere/vsphere_provisioner'
2
+
3
+ ChefMetal.add_registered_provisioner_class('vsphere', ChefMetalVsphere::VsphereProvisioner)
@@ -0,0 +1,3 @@
1
+ module ChefMetalVsphere
2
+ VERSION = '0.1.3'
3
+ end
@@ -0,0 +1,127 @@
1
+ module ChefMetalVsphere
2
+ module Helpers
3
+
4
+ def vim
5
+ # reconnect on every call - connections may silently timeout during long operations (e.g. cloning)
6
+ conn_opts = {
7
+ :host => connect_options['vsphere_host'],
8
+ :port => connect_options['vsphere_port'],
9
+ :path => connect_options['vshere_path'],
10
+ :use_ssl => connect_options['vsphere_ssl'],
11
+ :insecure => connect_options['vsphere_insecure'],
12
+ :proxyHost => connect_options['proxy_host'],
13
+ :proxyPort => connect_options['proxy_port'],
14
+ :user => connect_options['vsphere_user'],
15
+ :password => connect_options['vsphere_password']
16
+ }
17
+
18
+ vim = RbVmomi::VIM.connect conn_opts
19
+ return vim
20
+ end
21
+
22
+ def find_vm(dc_name, vm_folder, vm_name)
23
+ folder = find_folder(dc_name, vm_folder) or raise("vSphere Folder not found [#{vm_folder}] for vm #{vm_name}")
24
+ vm = folder.find(vm_name, RbVmomi::VIM::VirtualMachine)
25
+ end
26
+
27
+ def start_vm(vm, wait_on_port = 22)
28
+ state = vm.runtime.powerState
29
+ unless state == 'poweredOn'
30
+ vm.PowerOnVM_Task.wait_for_completion
31
+ end
32
+
33
+ sleep 1 until port_ready?(vm, wait_on_port)
34
+ end
35
+
36
+ def stop_vm(vm)
37
+ begin
38
+ vm.ShutdownGuest
39
+ sleep 2 until vm.runtime.powerState == 'poweredOff'
40
+ rescue
41
+ vm.PowerOffVM_Task.wait_for_completion
42
+ end
43
+ end
44
+
45
+ def port_ready?(vm, port)
46
+ vm_ip = vm.guest.ipAddress
47
+ return false if vm_ip.nil?
48
+
49
+ begin
50
+ tcp_socket = TCPSocket.new(vm_ip, port)
51
+ readable = IO.select([tcp_socket], nil, nil, 5)
52
+ if readable
53
+ true
54
+ else
55
+ false
56
+ end
57
+ rescue Errno::ETIMEDOUT
58
+ false
59
+ rescue Errno::EPERM
60
+ false
61
+ rescue Errno::ECONNREFUSED
62
+ false
63
+ rescue Errno::EHOSTUNREACH, Errno::ENETUNREACH
64
+ false
65
+ ensure
66
+ tcp_socket && tcp_socket.close
67
+ end
68
+ end
69
+
70
+ #folder could be like: /Level1/Level2/folder_name
71
+ def find_folder(dc_name, folder_name)
72
+ #dc(dc_name).vmFolder.childEntity.grep(RbVmomi::VIM::Folder).find { |x| x.name == folder_name }
73
+ baseEntity = dc(dc_name).vmFolder
74
+ entityArray = folder_name.split('/')
75
+ entityArray.each do |entityArrItem|
76
+ if entityArrItem != ''
77
+ baseEntity = baseEntity.childEntity.grep(RbVmomi::VIM::Folder).find { |f| f.name == entityArrItem }
78
+ end
79
+ end
80
+ baseEntity
81
+ end
82
+
83
+ def dc(dc_name)
84
+ vim.serviceInstance.find_datacenter(dc_name) or raise("vSphere Datacenter not found [#{datacenter}]")
85
+ end
86
+
87
+ def do_vm_clone(dc_name, vm_template, vm_name, options)
88
+ pool = options['resource_pool'] ? find_pool(dc(dc_name), options['resource_pool']) : vm_template.resourcePool
89
+ raise ':resource_pool must be specified when cloning from a VM Template' if pool.nil?
90
+
91
+ clone_spec = RbVmomi::VIM.VirtualMachineCloneSpec(
92
+ location: RbVmomi::VIM.VirtualMachineRelocateSpec(pool: pool),
93
+ powerOn: false,
94
+ template: false
95
+ )
96
+
97
+ vm_template.CloneVM_Task(
98
+ name: vm_name,
99
+ folder: find_folder(dc_name, options['vm_folder']),
100
+ spec: clone_spec
101
+ ).wait_for_completion
102
+ end
103
+
104
+ def find_pool(dc, pool_name)
105
+ baseEntity = dc.hostFolder
106
+ entityArray = pool_name.split('/')
107
+ entityArray.each do |entityArrItem|
108
+ if entityArrItem != ''
109
+ if baseEntity.is_a? RbVmomi::VIM::Folder
110
+ baseEntity = baseEntity.childEntity.find { |f| f.name == entityArrItem } or nil
111
+ elsif baseEntity.is_a? RbVmomi::VIM::ClusterComputeResource or baseEntity.is_a? RbVmomi::VIM::ComputeResource
112
+ baseEntity = baseEntity.resourcePool.resourcePool.find { |f| f.name == entityArrItem } or nil
113
+ elsif baseEntity.is_a? RbVmomi::VIM::ResourcePool
114
+ baseEntity = baseEntity.resourcePool.find { |f| f.name == entityArrItem } or nil
115
+ else
116
+ baseEntity = nil
117
+ end
118
+ end
119
+ end
120
+
121
+ raise "vSphere ResourcePool not found [#{pool_name}]" if baseEntity.nil?
122
+
123
+ baseEntity = baseEntity.resourcePool if not baseEntity.is_a?(RbVmomi::VIM::ResourcePool) and baseEntity.respond_to?(:resourcePool)
124
+ baseEntity
125
+ end
126
+ end
127
+ end
@@ -0,0 +1,299 @@
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
+ action_handler.perform_action "Start VM and wait for ssh [#{vm_folder}/#{vm_name}]" do
141
+ start_vm(vm, wait_on_port)
142
+ end
143
+
144
+ machine = machine_for(node)
145
+
146
+ machine
147
+ end
148
+
149
+ # Connect to machine without acquiring it
150
+ def connect_to_machine(node)
151
+ machine_for(node)
152
+ end
153
+
154
+ def delete_machine(action_handler, node)
155
+ if node['normal'] && node['normal']['provisioner_output']
156
+ provisioner_output = node['normal']['provisioner_output']
157
+ else
158
+ provisioner_output = {}
159
+ end
160
+ vm_name = provisioner_output['vm_name'] || node['name']
161
+ vm_folder = provisioner_output['bootstrap_options']['vm_folder']
162
+ vm = vm_for(node)
163
+
164
+ unless vm.nil?
165
+ action_handler.perform_action "Delete VM [#{vm_folder}/#{vm_name}]" do
166
+ vm.PowerOffVM_Task.wait_for_completion unless vm.runtime.powerState == 'poweredOff'
167
+ vm.Destroy_Task.wait_for_completion
168
+ end
169
+ end
170
+ end
171
+
172
+ def stop_machine(action_handler, node)
173
+ if node['normal'] && node['normal']['provisioner_output']
174
+ provisioner_output = node['normal']['provisioner_output']
175
+ else
176
+ provisioner_output = {}
177
+ end
178
+ vm_name = provisioner_output['vm_name'] || node['name']
179
+ vm_folder = provisioner_output['bootstrap_options']['vm_folder']
180
+ action_handler.perform_action "Guest shutdown and power off VM [#{vm_folder}/#{vm_name}]" do
181
+ stop_vm(vm_for(node))
182
+ end
183
+ end
184
+
185
+ protected
186
+
187
+ def provisioner_url
188
+ "vsphere://#{connect_options['vsphere_host']}:#{connect_options['vsphere_port']}#{connect_options['vsphere_path']}?ssl=#{connect_options['vsphere_ssl']}&insecure=#{connect_options['vsphere_insecure']}"
189
+ end
190
+
191
+ def vm_instance(action_handler, node)
192
+ bootstrap_options = node['normal']['provisioner_output']['bootstrap_options']
193
+
194
+ datacenter = bootstrap_options['datacenter']
195
+ vm_name = node['normal']['provisioner_output']['vm_name']
196
+ vm_folder = bootstrap_options['vm_folder']
197
+
198
+ vm = find_vm(datacenter, vm_folder, vm_name)
199
+ return vm unless vm.nil?
200
+
201
+ action_handler.perform_action "Clone a new VM instance for [#{vm_folder}/#{vm_name}]" do
202
+ vm = clone_vm(vm_name, bootstrap_options)
203
+ end
204
+
205
+ vm
206
+ end
207
+
208
+ def clone_vm(vm_name, bootstrap_options)
209
+ datacenter = bootstrap_options['datacenter']
210
+ template_folder = bootstrap_options['template_folder']
211
+ template_name = bootstrap_options['template_name']
212
+ vm_template = find_vm(datacenter, template_folder, template_name) or raise("vSphere VM Template not found [#{template_folder}/#{template_name}]")
213
+
214
+ vm = do_vm_clone(datacenter, vm_template, vm_name, bootstrap_options)
215
+ end
216
+
217
+ def machine_for(node)
218
+ if is_windows?(vm_for(node))
219
+ ChefMetal::Machine::WindowsMachine.new(node, transport_for(node), convergence_strategy_for(node))
220
+ else
221
+ ChefMetal::Machine::UnixMachine.new(node, transport_for(node), convergence_strategy_for(node))
222
+ end
223
+ end
224
+
225
+
226
+ def vm_for(node)
227
+ bootstrap_options = node['normal']['provisioner_output']['bootstrap_options']
228
+ datacenter = bootstrap_options['datacenter']
229
+ vm_folder = bootstrap_options['vm_folder']
230
+ vm_name = node['normal']['provisioner_output']['vm_name']
231
+ vm = find_vm(datacenter, vm_folder, vm_name)
232
+ end
233
+
234
+ def is_windows?(vm)
235
+ vm.guest.guestFamily == 'windowsGuest'
236
+ end
237
+
238
+ def convergence_strategy_for(node)
239
+ if is_windows?(vm_for(node))
240
+ @windows_convergence_strategy ||= begin
241
+ options = {}
242
+ provisioner_options = node['normal']['provisioner_options'] || {}
243
+ options[:chef_client_timeout] = provisioner_options['chef_client_timeout'] if provisioner_options.has_key?('chef_client_timeout')
244
+ ChefMetal::ConvergenceStrategy::InstallMsi.new(options)
245
+ end
246
+ else
247
+ @unix_convergence_strategy ||= begin
248
+ options = {}
249
+ provisioner_options = node['normal']['provisioner_options'] || {}
250
+ options[:chef_client_timeout] = provisioner_options['chef_client_timeout'] if provisioner_options.has_key?('chef_client_timeout')
251
+ ChefMetal::ConvergenceStrategy::InstallCached.new(options)
252
+ end
253
+ end
254
+ end
255
+
256
+ def transport_for(node)
257
+ if is_windows?(vm_for(node))
258
+ create_winrm_transport(node)
259
+ else
260
+ create_ssh_transport(node)
261
+ end
262
+ end
263
+
264
+ def create_winrm_transport(node)
265
+ raise 'Windows guest VMs are not yet supported'
266
+ end
267
+
268
+ def create_ssh_transport(node)
269
+ bootstrap_options = node['normal']['provisioner_output']['bootstrap_options']
270
+
271
+ hostname = vm_for(node).guest.ipAddress
272
+ ssh_user = bootstrap_options['ssh']['user']
273
+ ssh_options = symbolize_keys(bootstrap_options['ssh'])
274
+ transport_options = {
275
+ :prefix => 'sudo '
276
+ }
277
+ ChefMetal::Transport::SSH.new(hostname, ssh_user, ssh_options, transport_options)
278
+ end
279
+
280
+ def stringify_keys(h)
281
+ Hash === h ?
282
+ Hash[
283
+ h.map do |k, v|
284
+ [k.respond_to?(:to_s) ? k.to_s : k, stringify_keys(v)]
285
+ end
286
+ ] : h
287
+ end
288
+
289
+ def symbolize_keys(h)
290
+ Hash === h ?
291
+ Hash[
292
+ h.map do |k, v|
293
+ [k.respond_to?(:to_sym) ? k.to_sym : k, symbolize_keys(v)]
294
+ end
295
+ ] : h
296
+ end
297
+
298
+ end
299
+ end
@@ -0,0 +1,12 @@
1
+ require 'chef_metal'
2
+ require 'chef_metal_vsphere/vsphere_provisioner'
3
+
4
+ class Chef
5
+ module DSL
6
+ module Recipe
7
+ def with_vsphere_provisioner(options = {}, &block)
8
+ run_context.chef_metal.with_provisioner(ChefMetalVsphere::VsphereProvisioner.new(options), &block)
9
+ end
10
+ end
11
+ end
12
+ end
metadata ADDED
@@ -0,0 +1,125 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: chef-metal-vsphere
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.3
5
+ prerelease:
6
+ platform: ruby
7
+ authors:
8
+ - Rally Software Development Corp
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2014-05-09 00:00:00.000000000 Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: chef
16
+ requirement: !ruby/object:Gem::Requirement
17
+ none: false
18
+ requirements:
19
+ - - ! '>='
20
+ - !ruby/object:Gem::Version
21
+ version: '0'
22
+ type: :runtime
23
+ prerelease: false
24
+ version_requirements: !ruby/object:Gem::Requirement
25
+ none: false
26
+ requirements:
27
+ - - ! '>='
28
+ - !ruby/object:Gem::Version
29
+ version: '0'
30
+ - !ruby/object:Gem::Dependency
31
+ name: rbvmomi
32
+ requirement: !ruby/object:Gem::Requirement
33
+ none: false
34
+ requirements:
35
+ - - ! '>='
36
+ - !ruby/object:Gem::Version
37
+ version: '0'
38
+ type: :runtime
39
+ prerelease: false
40
+ version_requirements: !ruby/object:Gem::Requirement
41
+ none: false
42
+ requirements:
43
+ - - ! '>='
44
+ - !ruby/object:Gem::Version
45
+ version: '0'
46
+ - !ruby/object:Gem::Dependency
47
+ name: rspec
48
+ requirement: !ruby/object:Gem::Requirement
49
+ none: false
50
+ requirements:
51
+ - - ! '>='
52
+ - !ruby/object:Gem::Version
53
+ version: '0'
54
+ type: :development
55
+ prerelease: false
56
+ version_requirements: !ruby/object:Gem::Requirement
57
+ none: false
58
+ requirements:
59
+ - - ! '>='
60
+ - !ruby/object:Gem::Version
61
+ version: '0'
62
+ - !ruby/object:Gem::Dependency
63
+ name: rake
64
+ requirement: !ruby/object:Gem::Requirement
65
+ none: false
66
+ requirements:
67
+ - - ! '>='
68
+ - !ruby/object:Gem::Version
69
+ version: '0'
70
+ type: :development
71
+ prerelease: false
72
+ version_requirements: !ruby/object:Gem::Requirement
73
+ none: false
74
+ requirements:
75
+ - - ! '>='
76
+ - !ruby/object:Gem::Version
77
+ version: '0'
78
+ description: Provisioner for creating vSphere VM instances in Chef Metal.
79
+ email: rallysoftware-cookbooks@rallydev.com
80
+ executables: []
81
+ extensions: []
82
+ extra_rdoc_files:
83
+ - README.md
84
+ - LICENSE
85
+ files:
86
+ - Rakefile
87
+ - LICENSE
88
+ - README.md
89
+ - lib/chef_metal/provisioner_init/vsphere_init.rb
90
+ - lib/chef_metal_vsphere/version.rb
91
+ - lib/chef_metal_vsphere/vsphere_helpers.rb
92
+ - lib/chef_metal_vsphere/vsphere_provisioner.rb
93
+ - lib/chef_metal_vsphere.rb
94
+ homepage: https://github.com/RallySoftware-cookbooks/chef-metal-vsphere
95
+ licenses:
96
+ - MIT
97
+ post_install_message:
98
+ rdoc_options: []
99
+ require_paths:
100
+ - lib
101
+ required_ruby_version: !ruby/object:Gem::Requirement
102
+ none: false
103
+ requirements:
104
+ - - ! '>='
105
+ - !ruby/object:Gem::Version
106
+ version: '0'
107
+ segments:
108
+ - 0
109
+ hash: -2191173413187633211
110
+ required_rubygems_version: !ruby/object:Gem::Requirement
111
+ none: false
112
+ requirements:
113
+ - - ! '>='
114
+ - !ruby/object:Gem::Version
115
+ version: '0'
116
+ segments:
117
+ - 0
118
+ hash: -2191173413187633211
119
+ requirements: []
120
+ rubyforge_project:
121
+ rubygems_version: 1.8.28
122
+ signing_key:
123
+ specification_version: 3
124
+ summary: Provisioner for creating vSphere VM instances in Chef Metal.
125
+ test_files: []