chef-provisioning-opennebula 0.3.4

Sign up to get free protection for your applications and to get access to all the features.
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
data/Rakefile ADDED
@@ -0,0 +1,6 @@
1
+ require 'bundler'
2
+ require 'bundler/gem_tasks'
3
+
4
+ task :spec do
5
+ require File.expand_path('spec/run')
6
+ end
@@ -0,0 +1,244 @@
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 Provider class.
17
+ #
18
+ class Chef
19
+ #
20
+ # Implementation of Provider class.
21
+ #
22
+ class Provider
23
+ #
24
+ # Implementation of Provider class.
25
+ #
26
+ class OneImage < Chef::Provider::LWRPBase
27
+ use_inline_resources
28
+
29
+ provides :one_image
30
+
31
+ attr_reader :image
32
+
33
+ def whyrun_supported?
34
+ true
35
+ end
36
+
37
+ def load_current_resource
38
+ end
39
+
40
+ def action_handler
41
+ @action_handler ||= Chef::Provisioning::ChefProviderActionHandler.new(self)
42
+ end
43
+
44
+ def exists?
45
+ new_driver = driver
46
+ @image = new_driver.one.get_resource('img', :name => new_resource.name)
47
+ !@image.nil?
48
+ end
49
+
50
+ action :allocate do
51
+ if exists?
52
+ action_handler.report_progress "image '#{new_resource.name}' already exists - nothing to do"
53
+ else
54
+ fail "'size' must be specified" unless new_resource.size
55
+ fail "'datastore_id' must be specified" unless new_resource.datastore_id
56
+
57
+ action_handler.perform_action "allocated image '#{new_resource.name}'" do
58
+ @image = new_driver.one.allocate_img(
59
+ new_resource.name,
60
+ new_resource.size,
61
+ new_resource.datastore_id,
62
+ new_resource.type || 'OS',
63
+ new_resource.fs_type || 'ext2',
64
+ new_resource.img_driver || 'qcow2',
65
+ new_resource.prefix || 'vd',
66
+ new_resource.persistent || false)
67
+ Chef::Log.info("Image '#{new_resource.name}' allocate in initial state #{@image.state_str}")
68
+ @new_resource.updated_by_last_action(true)
69
+ end
70
+ end
71
+ @image
72
+ end
73
+
74
+ action :create do
75
+ @image = action_allocate
76
+ case @image.state_str
77
+ when 'INIT', 'LOCKED'
78
+ action_handler.perform_action "wait for image '#{new_resource.name}' to be READY" do
79
+ current_driver.one.wait_for_img(new_resource.name, @image.id)
80
+ @new_resource.updated_by_last_action(true)
81
+ end
82
+ when 'READY', 'USED', 'USED_PERS'
83
+ action_handler.report_progress "image '#{new_resource.name}' is already in #{@image.state_str} state - nothing to do"
84
+ else
85
+ fail "Image #{new_resource.name} is in unexpected state '#{@image.state_str}'"
86
+ end
87
+ end
88
+
89
+ action :destroy do
90
+ if exists?
91
+ action_handler.perform_action "deleted image '#{new_resource.name}'" do
92
+ rc = @image.delete
93
+ fail "Failed to delete image '#{new_resource.name}' : #{rc.message}" if OpenNebula.is_error?(rc)
94
+ until new_driver.one.get_resource('img', :name => new_resource.name).nil?
95
+ Chef::Log.debug("Waiting for delete image to finish...")
96
+ sleep 1
97
+ end
98
+ @new_resource.updated_by_last_action(true)
99
+ end
100
+ else
101
+ action_handler.report_progress "image '#{new_resource.name}' does not exist - nothing to do"
102
+ end
103
+ end
104
+
105
+ action :attach do
106
+ fail "Missing attribute 'machine_id'" unless new_resource.machine_id
107
+ fail "Failed to attach disk - image '#{new_resource.name}' does not exist" unless exists?
108
+
109
+ vm = new_driver.one.get_resource('vm', new_resource.machine_id.is_a?(Integer) ? :id : :name => new_resource.machine_id)
110
+ fail "Failed to attach disk - VM '#{new_resource.machine}' does not exist" if vm.nil?
111
+ action_handler.perform_action "attached disk #{new_resource.name} to #{vm.name}" do
112
+ disk_hash = @image.to_hash
113
+ disk_tpl = "DISK = [ "
114
+ disk_tpl << " IMAGE = #{disk_hash['IMAGE']['NAME']}, IMAGE_UNAME = #{disk_hash['IMAGE']['UNAME']}"
115
+ disk_tpl << ", TARGET = #{new_resource.target}" if new_resource.target
116
+ disk_tpl << ", DEV_PREFIX = #{new_resource.prefix}" if new_resource.prefix
117
+ disk_tpl << ", CACHE = #{new_resource.cache}" if new_resource.cache
118
+ disk_tpl << "]"
119
+
120
+ disk_id = new_driver.one.get_disk_id(vm, disk_hash['IMAGE']['NAME'])
121
+ if !disk_id.nil?
122
+ action_handler.report_progress "disk is already attached" unless disk_id.nil?
123
+ elsif disk_id.nil?
124
+ action_handler.report_progress "disk not attached, attaching..."
125
+ rc = vm.disk_attach(disk_tpl)
126
+ new_driver.one.wait_for_vm(vm.id)
127
+ fail "Failed to attach disk to VM '#{vm.name}': #{rc.message}" if OpenNebula.is_error?(rc)
128
+ @new_resource.updated_by_last_action(true)
129
+ end
130
+ end
131
+ end
132
+
133
+ action :snapshot do
134
+ fail "Missing attribute 'machine_id'" unless new_resource.machine_id
135
+ fail "snapshot '#{new_resource.name}' already exists" if exists?
136
+ vm = new_driver.one.get_resource('vm', new_resource.machine_id.is_a?(Integer) ? :id : :name => new_resource.machine_id)
137
+ fail "Failed to create snapshot - VM '#{new_resource.machine_id}' does not exist" if vm.nil?
138
+ action_handler.perform_action "created snapshot from '#{new_resource.machine_id}'" do
139
+ disk_id = new_resource.disk_id.is_a?(Integer) ? new_resource.disk_id : new_driver.one.get_disk_id(vm, new_resource.disk_id)
140
+ fail "No disk '#{new_resource.disk_id}' found on '#{vm.name}'" if disk_id.nil?
141
+
142
+ @image = vm.disk_snapshot(disk_id, new_resource.name, "", true)
143
+ fail "Failed to create snapshot '#{new_resource.name}': #{@image.message}" if OpenNebula.is_error?(@image)
144
+
145
+ @image = new_driver.one.wait_for_img(new_resource.name, @image)
146
+ if new_resource.persistent
147
+ action_handler.report_progress "make image '#{new_resource.name}' persistent"
148
+ @image.persistent
149
+ end
150
+ @new_resource.updated_by_last_action(true)
151
+ end
152
+ end
153
+
154
+ action :upload do
155
+ fail "'datastore_id' is required" unless new_resource.datastore_id
156
+ fail "'image_file' is required" unless new_resource.image_file
157
+ fail "image_file #{new_resource.image_file} does not exist" unless ::File.exist? new_resource.image_file
158
+
159
+ file_url = "http://#{node['ipaddress']}/#{::File.basename(@new_resource.image_file)}"
160
+ description = @new_resource.description || "#{@new_resource.name} image"
161
+ image_driver = @new_resource.img_driver || 'qcow2'
162
+
163
+ if exists?
164
+ if @image.name == @new_resource.name &&
165
+ @image['PATH'] == file_url &&
166
+ @image['TEMPLATE/DRIVER'] == image_driver &&
167
+ @image['TEMPLATE/DESCRIPTION'] == description &&
168
+ @image['DATASTORE_ID'] == @new_resource.datastore_id.to_s
169
+ action_handler.report_progress("image '#{@new_resource.name}' (ID: #{@image.id}) already exists - nothing to do")
170
+ else
171
+ fail "image '#{new_resource.name}' already exists, but it is not the same image"
172
+ end
173
+ else
174
+ action_handler.perform_action "upload image '#{@new_resource.image_file}'" do
175
+ begin
176
+ pid = Process.spawn("sudo python -m SimpleHTTPServer 80", :chdir => ::File.dirname(@new_resource.image_file), STDOUT => "/dev/null", STDERR => "/dev/null", :pgroup => true)
177
+ fail "Failed to start 'SimpleHTTPServer'" if pid.nil?
178
+ new_driver.one.upload_img(
179
+ @new_resource.name,
180
+ @new_resource.datastore_id,
181
+ file_url,
182
+ image_driver,
183
+ description,
184
+ @new_resource.type,
185
+ @new_resource.prefix,
186
+ @new_resource.persistent,
187
+ @new_resource.public,
188
+ @new_resource.target,
189
+ @new_resource.disk_type,
190
+ @new_resource.source,
191
+ @new_resource.size,
192
+ @new_resource.fs_type)
193
+
194
+ @new_resource.updated_by_last_action(true)
195
+ ensure
196
+ system("sudo kill -9 -#{pid}")
197
+ end
198
+ end
199
+ end
200
+ end
201
+
202
+ action :download do
203
+ new_driver = driver
204
+
205
+ action_handler.perform_action "downloaded image '#{@new_resource.image_file}" do
206
+ download_url = ENV['ONE_DOWNLOAD'] || @new_resource.download_url
207
+ fail %('download_url' is a required attribute.
208
+ You can get the value for 'download_url' by loging into your OpenNebula CLI
209
+ and reading the ONE_DOWNLOAD environment variable) if download_url.nil?
210
+ # You can get the value for 'download_url' by loging into your OpenNebula CLI and reading the ONE_DOWNLOAD environment variable" if download_url.nil?
211
+ image = new_driver.one.get_resource('img', !@new_resource.image_id.nil? ? { :id => @new_resource.image_id } : { :name => @new_resource.name })
212
+ fail "Image 'NAME: #{@new_resource.name}/ID: #{@new_resource.image_id}' does not exist" if image.nil?
213
+ local_path = @new_resource.image_file || ::File.join(Chef::Config[:file_cache_path], "#{@new_resource.name}.qcow2")
214
+ fail "Will not overwrite an existing file: #{local_path}" if ::File.exist?(local_path)
215
+ command = "curl -o #{local_path} #{download_url}/#{::File.basename(::File.dirname(image['SOURCE']))}/#{::File.basename(image['SOURCE'])}"
216
+ rc = system(command)
217
+ fail rc if rc.nil?
218
+ fail "ERROR: #{rc}" unless rc
219
+ system("chmod 777 #{local_path}")
220
+ Chef::Log.info("Image downloaded from OpenNebula to: #{local_path}")
221
+ @new_resource.updated_by_last_action(true)
222
+ end
223
+ end
224
+
225
+ protected
226
+
227
+ def driver
228
+ if current_driver && current_driver.driver_url != new_driver.driver_url
229
+ fail "Cannot move '#{machine_spec.name}' from #{current_driver.driver_url} to #{new_driver.driver_url}: machine moving is not supported. Destroy and recreate."
230
+ end
231
+ fail "Driver not specified for one_image #{new_resource.name}" unless new_driver
232
+ new_driver
233
+ end
234
+
235
+ def new_driver
236
+ run_context.chef_provisioning.driver_for(new_resource.driver)
237
+ end
238
+
239
+ def current_driver
240
+ run_context.chef_provisioning.driver_for(run_context.chef_provisioning.current_driver) if run_context.chef_provisioning.current_driver
241
+ end
242
+ end
243
+ end
244
+ end
@@ -0,0 +1,95 @@
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 Provider class.
17
+ #
18
+ class Chef
19
+ #
20
+ # Implementation of Provider class.
21
+ #
22
+ class Provider
23
+ #
24
+ # Implementation of Provider class.
25
+ #
26
+ class OneTemplate < Chef::Provider::LWRPBase
27
+ use_inline_resources
28
+
29
+ provides :one_template
30
+
31
+ attr_reader :template
32
+
33
+ def whyrun_supported?
34
+ true
35
+ end
36
+
37
+ def load_current_resource
38
+ end
39
+
40
+ def action_handler
41
+ @action_handler ||= Chef::Provisioning::ChefProviderActionHandler.new(self)
42
+ end
43
+
44
+ def exists?
45
+ new_driver = driver
46
+ @template = new_driver.one.get_resource('tpl', :name => new_resource.name)
47
+ !@template.nil?
48
+ end
49
+
50
+ action :create do
51
+ if exists?
52
+ action_handler.report_progress "template '#{new_resource.name}' already exists - nothing to do"
53
+ else
54
+ fail "Missing attribute 'template_file' or 'template'" if !new_resource.template_file && !new_resource.template
55
+ action_handler.perform_action "create template '#{new_resource.name}'" do
56
+ template_str = File.read(new_resource.template_file) if new_resource.template_file
57
+ template_str = new_driver.one.create_template(new_resource.template) if new_resource.template
58
+ template_str << "\nNAME=\"#{new_resource.name}\""
59
+ @template = new_driver.one.allocate_template(template_str)
60
+ @new_resource.updated_by_last_action(true)
61
+ end
62
+ end
63
+ end
64
+
65
+ action :delete do
66
+ if !exists?
67
+ action_handler.report_progress "template '#{new_resource.name}' does not exists - nothing to do"
68
+ else
69
+ action_handler.perform_action "delete template '#{new_resource.name}'" do
70
+ @template.delete
71
+ @new_resource.updated_by_last_action(true)
72
+ end
73
+ end
74
+ end
75
+
76
+ protected
77
+
78
+ def driver
79
+ if current_driver && current_driver.driver_url != new_driver.driver_url
80
+ fail "Cannot move '#{machine_spec.name}' from #{current_driver.driver_url} to #{new_driver.driver_url}: machine moving is not supported. Destroy and recreate."
81
+ end
82
+ fail "Driver not specified for one_image #{new_resource.name}" unless new_driver
83
+ new_driver
84
+ end
85
+
86
+ def new_driver
87
+ run_context.chef_provisioning.driver_for(new_resource.driver)
88
+ end
89
+
90
+ def current_driver
91
+ run_context.chef_provisioning.driver_for(run_context.chef_provisioning.current_driver) if run_context.chef_provisioning.current_driver
92
+ end
93
+ end
94
+ end
95
+ end
@@ -0,0 +1,105 @@
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 Provider class.
17
+ #
18
+ class Chef
19
+ #
20
+ # Implementation of Provider class.
21
+ #
22
+ class Provider
23
+ #
24
+ # Implementation of Provider class.
25
+ #
26
+ class OneUser < Chef::Provider::LWRPBase
27
+ use_inline_resources
28
+
29
+ provides :one_user
30
+
31
+ attr_reader :current_user
32
+
33
+ def action_handler
34
+ @action_handler ||= Chef::Provisioning::ChefProviderActionHandler.new(self)
35
+ end
36
+
37
+ def exists?(filter)
38
+ new_driver = driver
39
+ @current_user = new_driver.one.get_resource('user', filter)
40
+ Chef::Log.debug("user '#{filter}' exists: #{!@current_user.nil?}")
41
+ !@current_user.nil?
42
+ end
43
+
44
+ action :create do
45
+ fail "Missing attribute 'password'" unless @new_resource.password
46
+
47
+ if exists?(:name => @new_resource.name)
48
+ action_handler.report_progress "user '#{@new_resource.name}' already exists - nothing to do"
49
+ else
50
+ action_handler.perform_action "create user '#{@new_resource.name}'" do
51
+ user = OpenNebula::User.new(OpenNebula::User.build_xml, @client)
52
+ rc = user.allocate(@new_resource.name, @new_resource.password) unless OpenNebula.is_error?(user)
53
+ Chef::Log.debug(template_str)
54
+ fail "failed to create vnet '#{@new_resource.name}': #{vnet.message}" if OpenNebula.is_error?(rc)
55
+ @new_resource.updated_by_last_action(true)
56
+ end
57
+ end
58
+ end
59
+
60
+ action :delete do
61
+ if exists?(:id => @new_resource.user_id, :name => @new_resource.name)
62
+ action_handler.perform_action "deleted user '#{new_resource.name}' (#{@current_user.id})" do
63
+ rc = @current_user.delete
64
+ fail "failed to delete user '#{@new_resource.name}': #{rc.message}" if OpenNebula.is_error?(rc)
65
+ @new_resource.updated_by_last_action(true)
66
+ end
67
+ else
68
+ action_handler.report_progress "user '#{new_resource.name}' does not exists - nothing to do"
69
+ end
70
+ end
71
+
72
+ action :update do
73
+ if exists?(:id => @new_resource.user_id, :name => @new_resource.name)
74
+ fail "':template' or ':template_file' attribute missing" if !@new_resource.template && !@new_resource.template_file
75
+ # hash = @current_user.to_hash
76
+ tpl = new_driver.one.create_template(@new_resource.template) if @new_resource.template
77
+ tpl = ::File.read(@new_resource.template_file) if @new_resource.template_file
78
+
79
+ rc = @current_user.update(tpl, true)
80
+ fail "failed to update user '#{@new_resource.name}': #{rc.message}" if OpenNebula.is_error?(rc)
81
+ else
82
+ fail "user '#{new_resource.name}' does not exists"
83
+ end
84
+ end
85
+
86
+ protected
87
+
88
+ def driver
89
+ if current_driver && current_driver.driver_url != new_driver.driver_url
90
+ fail "Cannot move '#{machine_spec.name}' from #{current_driver.driver_url} to #{new_driver.driver_url}: machine moving is not supported. Destroy and recreate."
91
+ end
92
+ fail "Driver not specified for one_image #{new_resource.name}" unless new_driver
93
+ new_driver
94
+ end
95
+
96
+ def new_driver
97
+ run_context.chef_provisioning.driver_for(new_resource.driver)
98
+ end
99
+
100
+ def current_driver
101
+ run_context.chef_provisioning.driver_for(run_context.chef_provisioning.current_driver) if run_context.chef_provisioning.current_driver
102
+ end
103
+ end
104
+ end
105
+ end