chef-provisioning-opennebula 0.3.4

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.
Files changed (41) hide show
  1. checksums.yaml +7 -0
  2. data/LICENSE +201 -0
  3. data/README.md +532 -0
  4. data/Rakefile +6 -0
  5. data/lib/chef/provider/one_image.rb +244 -0
  6. data/lib/chef/provider/one_template.rb +95 -0
  7. data/lib/chef/provider/one_user.rb +105 -0
  8. data/lib/chef/provider/one_vnet.rb +122 -0
  9. data/lib/chef/provider/one_vnet_lease.rb +133 -0
  10. data/lib/chef/provisioning/driver_init/opennebula.rb +17 -0
  11. data/lib/chef/provisioning/opennebula_driver.rb +18 -0
  12. data/lib/chef/provisioning/opennebula_driver/credentials.rb +105 -0
  13. data/lib/chef/provisioning/opennebula_driver/driver.rb +572 -0
  14. data/lib/chef/provisioning/opennebula_driver/one_lib.rb +352 -0
  15. data/lib/chef/provisioning/opennebula_driver/resources.rb +20 -0
  16. data/lib/chef/provisioning/opennebula_driver/version.rb +30 -0
  17. data/lib/chef/resource/one_image.rb +65 -0
  18. data/lib/chef/resource/one_template.rb +46 -0
  19. data/lib/chef/resource/one_user.rb +50 -0
  20. data/lib/chef/resource/one_vnet.rb +51 -0
  21. data/lib/chef/resource/one_vnet_lease.rb +46 -0
  22. data/spec/integration/test_all_integration_spec.rb +102 -0
  23. data/spec/recipes/attach_back_one_vm_spec.rb +20 -0
  24. data/spec/recipes/attach_back_two_vm_spec.rb +20 -0
  25. data/spec/recipes/attach_one_image_spec.rb +20 -0
  26. data/spec/recipes/converge_back_one_vm_spec.rb +19 -0
  27. data/spec/recipes/converge_back_two_vm_spec.rb +19 -0
  28. data/spec/recipes/converge_bootstrap_vm_spec.rb +34 -0
  29. data/spec/recipes/create_back_one_vm_spec.rb +20 -0
  30. data/spec/recipes/create_back_two_vm_spec.rb +20 -0
  31. data/spec/recipes/create_bootstrap_vm_spec.rb +34 -0
  32. data/spec/recipes/create_one_image_spec.rb +21 -0
  33. data/spec/recipes/create_one_template_spec.rb +52 -0
  34. data/spec/recipes/delete_all_spec.rb +47 -0
  35. data/spec/recipes/driver_options_spec.rb +70 -0
  36. data/spec/recipes/instantiate_one_template_spec.rb +35 -0
  37. data/spec/recipes/snapshot_one_image_spec.rb +21 -0
  38. data/spec/recipes/snapshot_two_image_spec.rb +21 -0
  39. data/spec/spec_helper.rb +35 -0
  40. data/spec/support/opennebula_support.rb +64 -0
  41. metadata +168 -0
@@ -0,0 +1,352 @@
1
+ # Copyright 2015, BlackBerry, Inc.
2
+ #
3
+ # Licensed under the Apache License, Version 2.0 (the "License");
4
+ # you may not use this file except in compliance with the License.
5
+ # You may obtain a copy of the License at
6
+ #
7
+ # http://www.apache.org/licenses/LICENSE-2.0
8
+ #
9
+ # Unless required by applicable law or agreed to in writing, software
10
+ # distributed under the License is distributed on an "AS IS" BASIS,
11
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ # See the License for the specific language governing permissions and
13
+ # limitations under the License.
14
+
15
+ require 'opennebula'
16
+
17
+ require 'opennebula/document'
18
+
19
+ #
20
+ # Sample Document definition.
21
+ #
22
+ module OpenNebula
23
+ #
24
+ # Implementation.
25
+ #
26
+ class CustomObject < Document
27
+ DOCUMENT_TYPE = 555
28
+ end
29
+ end
30
+
31
+ #
32
+ # Implementation.
33
+ #
34
+ class Chef
35
+ #
36
+ # Module extention.
37
+ #
38
+ module Provisioning
39
+ #
40
+ # Module extention.
41
+ #
42
+ module OpenNebulaDriver
43
+ #
44
+ # ONE error.
45
+ #
46
+ class OpenNebulaException < Exception
47
+ end
48
+
49
+ #
50
+ # Implementation.
51
+ #
52
+ class OneLib
53
+ attr_accessor :client
54
+
55
+ def initialize(credentials, endpoint, options = {})
56
+ @client = OpenNebula::Client.new(credentials, endpoint, options)
57
+ rc = @client.get_version
58
+ raise OpenNebulaException, rc.message if OpenNebula.is_error?(rc)
59
+ end
60
+
61
+ # TODO: add filtering to pool retrieval (type, start, end, user)
62
+ def get_pool(type)
63
+ fail "pool type must be specified" if type.nil?
64
+ case type
65
+ when 'acl'
66
+ OpenNebula::AclPool.new(@client)
67
+ when 'cluster'
68
+ OpenNebula::ClusterPool.new(@client)
69
+ when 'datastore'
70
+ OpenNebula::DatastorePool.new(@client)
71
+ when 'doc'
72
+ OpenNebula::DocumentPool.new(@client)
73
+ when 'jsondoc'
74
+ OpenNebula::DocumentPoolJSON.new(@client)
75
+ when 'group'
76
+ OpenNebula::GroupPool.new(@client)
77
+ when 'host'
78
+ OpenNebula::HostPool.new(@client)
79
+ when 'img'
80
+ OpenNebula::ImagePool.new(@client, -1)
81
+ when 'secgroup'
82
+ OpenNebula::SecurityGroupPool.new(@client)
83
+ when 'tpl'
84
+ OpenNebula::TemplatePool.new(@client, -1)
85
+ when 'user'
86
+ OpenNebula::UserPool.new(@client)
87
+ when 'vdc'
88
+ OpenNebula::VdcPool.new(@client)
89
+ when 'vm'
90
+ OpenNebula::VirtualMachinePool.new(@client)
91
+ when 'vnet'
92
+ OpenNebula::VirtualNetworkPool.new(@client)
93
+ else
94
+ fail "Invalid pool type specified."
95
+ end
96
+ end
97
+
98
+ def get_resource(resource_type, filter = {})
99
+ if filter.empty?
100
+ Chef::Log.warn("get_resource: 'name' or 'id' must be provided")
101
+ return nil
102
+ end
103
+ pool = get_pool(resource_type)
104
+
105
+ if resource_type != 'user' && filter[:id] && !filter[:id].nil?
106
+ pool.info!(-2, filter[:id].to_i, filter[:id].to_i)
107
+ return pool.first
108
+ end
109
+
110
+ if resource_type == 'user'
111
+ pool.info
112
+ else
113
+ pool.info!(-2, -1, -1) if resource_type != 'user'
114
+ end
115
+ resources = []
116
+ pool.each do |res|
117
+ resources << res if res.name == filter[:name]
118
+ end
119
+ return nil if resources.size == 0
120
+ return resources[0] if resources.size == 1
121
+ resources
122
+ end
123
+
124
+ def allocate_vm(template)
125
+ vm = OpenNebula::VirtualMachine.new(OpenNebula::VirtualMachine.build_xml, @client)
126
+ raise OpenNebulaException, vm.message if OpenNebula.is_error?(vm)
127
+
128
+ Chef::Log.debug(template)
129
+ rc = vm.allocate(template)
130
+ raise OpenNebulaException, rc.message if OpenNebula.is_error?(rc)
131
+ vm
132
+ end
133
+
134
+ def wait_for_vm(id, end_state = nil)
135
+ end_state ||= 'RUNNING'
136
+ vm = get_resource('vm', :id => id)
137
+ fail "Did not find VM with ID: #{id}" unless vm
138
+ while vm.lcm_state_str != end_state.upcase
139
+ vm.info
140
+ Chef::Log.debug("Waiting for VM '#{id}' to be in '#{end_state.upcase}' state: '#{vm.lcm_state_str}'")
141
+ fail "'#{vm.name}'' failed. Current state: #{vm.state_str}" if vm.state_str == 'FAILED' || vm.lcm_state_str == 'FAILURE'
142
+ sleep(2)
143
+ end
144
+ vm
145
+ end
146
+
147
+ def upload_img(name, ds_id, path, driver, description, type, prefix, persistent, pub, target, disk_type, source, size, fstype)
148
+ template = <<-EOTPL
149
+ NAME = #{name}
150
+ PATH = \"#{path}\"
151
+ DRIVER = #{driver}
152
+ DESCRIPTION = \"#{description}\"
153
+ EOTPL
154
+
155
+ template << "TYPE = #{type}\n" unless type.nil?
156
+ template << "PERSISTENT = YES\n" if !persistent.nil? && persistent
157
+ template << "DEV_PREFIX = #{prefix}\n" unless prefix.nil?
158
+ template << "PUBLIC = YES\n" if !pub.nil? && pub
159
+ template << "TARGET = #{target}\n" unless target.nil?
160
+ template << "DISK_TYPE = #{disk_type}\n" unless disk_type.nil?
161
+ template << "SOURCE = #{source}\n" unless source.nil?
162
+ template << "SIZE = #{size}" unless size.nil?
163
+ template << "FSTYPE = #{fstype}\n" unless fstype.nil?
164
+
165
+ Chef::Log.debug("\n#{template}")
166
+
167
+ image = OpenNebula::Image.new(OpenNebula::Image.build_xml, @client)
168
+ raise OpenNebulaException, image.message if OpenNebula.is_error?(image)
169
+ rc = image.allocate(template, ds_id)
170
+ raise OpenNebulaException, rc.message if OpenNebula.is_error?(rc)
171
+ Chef::Log.debug("Waiting for image '#{name}' (#{image.id}) to be ready")
172
+ wait_for_img(name, image.id)
173
+ end
174
+
175
+ def allocate_img(name, size, ds_id, type, fstype, driver, prefix, persistent)
176
+ template = <<-EOT
177
+ NAME = #{name}
178
+ TYPE = #{type}
179
+ FSTYPE = #{fstype}
180
+ SIZE = #{size}
181
+ PERSISTENT = #{persistent ? 'YES' : 'NO'}
182
+
183
+ DRIVER = #{driver}
184
+ DEV_PREFIX = #{prefix}
185
+ EOT
186
+
187
+ img = OpenNebula::Image.new(OpenNebula::Image.build_xml, @client)
188
+ raise OpenNebulaException, img.message if OpenNebula.is_error?(img)
189
+
190
+ rc = img.allocate(template, ds_id)
191
+ raise OpenNebulaException, rc.message if OpenNebula.is_error?(rc)
192
+
193
+ Chef::Log.debug("Allocated disk image #{name} (#{img.id})")
194
+ img
195
+ end
196
+
197
+ def wait_for_img(name, img_id)
198
+ cur_state = nil
199
+ image = nil
200
+ state = 'INIT'
201
+ pool = get_pool('img')
202
+ while state == 'INIT' || state == 'LOCKED'
203
+ pool.info!(-2, img_id, img_id)
204
+ pool.each do |img|
205
+ next unless img.id == img_id
206
+ cur_state = img.state_str
207
+ image = img
208
+ Chef::Log.debug("Image #{img_id} state: '#{cur_state}'")
209
+ state = cur_state
210
+ break
211
+ end
212
+ sleep(2)
213
+ end
214
+ fail "Failed to create #{name} image. State = '#{state}'" if state != 'READY'
215
+ Chef::Log.info("Image #{name} is in READY state")
216
+ image
217
+ end
218
+
219
+ def get_disk_id(vm, disk_name)
220
+ fail "VM cannot be nil" if vm.nil?
221
+ disk_id = nil
222
+ vm.each('TEMPLATE/DISK') { |disk| disk_id = disk['DISK_ID'].to_i if disk['IMAGE'] == disk_name }
223
+ disk_id
224
+ end
225
+
226
+ def allocate_vnet(template_str, cluster_id)
227
+ vnet = OpenNebula::Vnet.new(OpenNebula::Vnet.build_xml, @client)
228
+ rc = vnet.allocate(template_str, cluster_id) unless OpenNebula.is_error?(vnet)
229
+ raise OpenNebulaException, rc.message if OpenNebula.is_error?(rc)
230
+ vnet
231
+ end
232
+
233
+ def allocate_template(template_str)
234
+ tpl = OpenNebula::Template.new(OpenNebula::Template.build_xml, @client)
235
+ rc = tpl.allocate(template_str) unless OpenNebula.is_error?(tpl)
236
+ raise OpenNebulaException, rc.message if OpenNebula.is_error?(rc)
237
+ rc
238
+ end
239
+
240
+ def recursive_merge(dest, source)
241
+ source.each do |k, v|
242
+ if source[k].is_a?(Hash)
243
+ if dest[k].is_a?(Array)
244
+ dest[k] = v.dup
245
+ else
246
+ dest[k] = {} unless dest[k]
247
+ recursive_merge(dest[k], v)
248
+ end
249
+ elsif source[k].is_a?(Array)
250
+ dest[k] = v.dup
251
+ else
252
+ dest[k] = v
253
+ end
254
+ end
255
+ end
256
+
257
+ #
258
+ # This will retrieve a VM template from one of the following locations:
259
+ # :template_name - template located in OpenNebula
260
+ # :template_id - template located in OpenNebula
261
+ # :template_file - local file containing the VM template
262
+ # :template - Hash containing equivalent structure as a VM template
263
+ #
264
+ def get_template(name, options)
265
+ t_hash = nil
266
+ if !options[:template_name].nil? || !options[:template_id].nil?
267
+ t_hash = template_from_one(options)
268
+ elsif !options[:template_file].nil?
269
+ t_hash = template_from_file(options)
270
+ elsif !options[:template].nil?
271
+ t_hash = template_from_hash(options)
272
+ else
273
+ fail "To create a VM you must specify one of ':template', ':template_id', ':template_name', or ':template' options in ':bootstrap_options'"
274
+ end
275
+ fail "Inavlid VM template : #{t_hash}" if t_hash.nil? || t_hash.empty?
276
+ tpl_updates = options[:template_options] || {}
277
+ if options[:user_variables]
278
+ Chef::Log.warn("':user_variables' will be deprecated in next version in favour of ':template_options'") if options.key?(:user_variables)
279
+ recursive_merge(tpl_updates, options[:user_variables])
280
+ end
281
+ recursive_merge(t_hash, tpl_updates) unless tpl_updates.empty?
282
+ t_hash['NAME'] = options[:enforce_chef_fqdn] ? name : name.split('.')[0]
283
+ tpl = create_template(t_hash)
284
+ Chef::Log.debug(tpl)
285
+ tpl
286
+ end
287
+
288
+ def template_from_one(options)
289
+ t = get_resource('tpl', :name => options[:template_name]) if options[:template_name]
290
+ t = get_resource('tpl', :id => options[:template_id]) if options[:template_id]
291
+ fail "Template '#{options}' does not exist" if t.nil?
292
+ t.to_hash["VMTEMPLATE"]["TEMPLATE"]
293
+ end
294
+
295
+ def template_from_file(options)
296
+ t_hash = nil
297
+ doc = OpenNebula::CustomObject.new(OpenNebula::CustomObject.build_xml, @client)
298
+ unless OpenNebula.is_error?(doc)
299
+ rc = doc.allocate("#{File.read(options[:template_file])}")
300
+ fail "Failed to allocate OpenNebula document: #{rc.message}" if OpenNebula.is_error?(rc)
301
+ doc.info!
302
+ t_hash = doc.to_hash['DOCUMENT']['TEMPLATE']
303
+ doc.delete
304
+ end
305
+ t_hash
306
+ end
307
+
308
+ def template_from_hash(options)
309
+ Chef::Log.debug("TEMPLATE_JSON: #{options[:template]}")
310
+ options[:template]
311
+ end
312
+
313
+ #
314
+ # This method will create a VM template from parameters provided
315
+ # in the 't' Hash. The hash must have equivalent structure as the
316
+ # VM template.
317
+ #
318
+ def create_template(t, level = 0)
319
+ tpl = ""
320
+ count = t.size
321
+ index = 1
322
+ t.each do |k, v|
323
+ if v.is_a?(Hash)
324
+ level.times { tpl << " " }
325
+ # DISK and NIC is just for backward compatibility
326
+ # it should be replaced by Array
327
+ k = 'DISK' if k =~ /^DISK/
328
+ k = 'NIC' if k =~ /^NIC/
329
+ tpl << "#{k} = [\n"
330
+ tpl << create_template(v, (level + 1))
331
+ (level - 1).times { tpl << " " }
332
+ tpl << "]\n"
333
+ elsif v.is_a?(Array)
334
+ level.times { tpl << " " }
335
+ v.each do |e|
336
+ tpl << "#{k} = [\n"
337
+ tpl << create_template(e, (level + 1))
338
+ tpl << "]\n"
339
+ end
340
+ else
341
+ comma = (index < count) && level > 0
342
+ level.times { tpl << " " }
343
+ tpl << "#{k} = \"#{v}\"" << (comma ? ",\n" : "\n")
344
+ index += 1
345
+ end
346
+ end
347
+ tpl
348
+ end
349
+ end
350
+ end
351
+ end
352
+ end
@@ -0,0 +1,20 @@
1
+ # Copyright 2015, BlackBerry, Inc.
2
+ #
3
+ # Licensed under the Apache License, Version 2.0 (the "License");
4
+ # you may not use this file except in compliance with the License.
5
+ # You may obtain a copy of the License at
6
+ #
7
+ # http://www.apache.org/licenses/LICENSE-2.0
8
+ #
9
+ # Unless required by applicable law or agreed to in writing, software
10
+ # distributed under the License is distributed on an "AS IS" BASIS,
11
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ # See the License for the specific language governing permissions and
13
+ # limitations under the License.
14
+
15
+ resources = %w( image template vnet vnet_lease user )
16
+
17
+ resources.each do |r|
18
+ Chef::Log.debug "OpenNebula driver loading resource: one_#{r}"
19
+ require "chef/resource/one_#{r}"
20
+ end
@@ -0,0 +1,30 @@
1
+ # Copyright 2015, BlackBerry, Inc.
2
+ #
3
+ # Licensed under the Apache License, Version 2.0 (the "License");
4
+ # you may not use this file except in compliance with the License.
5
+ # You may obtain a copy of the License at
6
+ #
7
+ # http://www.apache.org/licenses/LICENSE-2.0
8
+ #
9
+ # Unless required by applicable law or agreed to in writing, software
10
+ # distributed under the License is distributed on an "AS IS" BASIS,
11
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ # See the License for the specific language governing permissions and
13
+ # limitations under the License.
14
+
15
+ #
16
+ # Implementation of Version class.
17
+ #
18
+ class Chef
19
+ #
20
+ # Extending module.
21
+ #
22
+ module Provisioning
23
+ #
24
+ # Extending module.
25
+ #
26
+ module OpenNebulaDriver
27
+ VERSION = '0.3.4'
28
+ end
29
+ end
30
+ end
@@ -0,0 +1,65 @@
1
+ # Copyright 2015, BlackBerry, Inc.
2
+ #
3
+ # Licensed under the Apache License, Version 2.0 (the "License");
4
+ # you may not use this file except in compliance with the License.
5
+ # You may obtain a copy of the License at
6
+ #
7
+ # http://www.apache.org/licenses/LICENSE-2.0
8
+ #
9
+ # Unless required by applicable law or agreed to in writing, software
10
+ # distributed under the License is distributed on an "AS IS" BASIS,
11
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ # See the License for the specific language governing permissions and
13
+ # limitations under the License.
14
+
15
+ require 'chef/provider/one_image'
16
+
17
+ #
18
+ # Implementation of Resource class.
19
+ #
20
+ class Chef
21
+ #
22
+ # Implementation of Resource class.
23
+ #
24
+ class Resource
25
+ #
26
+ # Implementation of Resource class.
27
+ #
28
+ class OneImage < Chef::Resource::LWRPBase
29
+ resource_name :one_image
30
+
31
+ actions :allocate, :create, :destroy, :attach, :snapshot, :upload, :download
32
+ default_action :create
33
+
34
+ attribute :name, :kind_of => String, :name_attribute => true
35
+ attribute :size, :kind_of => Integer
36
+ attribute :datastore_id, :kind_of => Integer
37
+ attribute :driver_options, :kind_of => Hash
38
+ attribute :type, :kind_of => String, :equal_to => %w(OS CDROM DATABLOCK KERNEL RAMDISK CONTEXT)
39
+ attribute :description, :kind_of => String
40
+ attribute :fs_type, :kind_of => String
41
+ attribute :img_driver, :kind_of => String, :default => 'qcow2'
42
+ attribute :prefix, :kind_of => String, :equal_to => %w(vd xvd sd hd)
43
+ attribute :persistent, :kind_of => [TrueClass, FalseClass]
44
+ attribute :public, :kind_of => [TrueClass, FalseClass]
45
+ attribute :disk_type, :kind_of => String
46
+ attribute :source, :kind_of => String
47
+ attribute :target, :kind_of => String
48
+ attribute :machine_id, :kind_of => [String, Integer]
49
+ attribute :disk_id, :kind_of => [String, Integer]
50
+ attribute :image_file, :kind_of => String
51
+ attribute :cache, :kind_of => String
52
+ attribute :driver
53
+
54
+ attribute :download_url, :kind_of => String
55
+ attribute :image_id, :kind_of => Integer
56
+
57
+ def initialize(*args)
58
+ super
59
+ @chef_environment = run_context.cheffish.current_environment
60
+ @chef_server = run_context.cheffish.current_chef_server
61
+ @driver = run_context.chef_provisioning.current_driver
62
+ end
63
+ end
64
+ end
65
+ end