chef-metal-vsphere 0.1.3

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