chef-provisioning-opennebula 0.4.4 → 0.4.6
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +273 -4
- data/lib/chef/provider/one_flow_service.rb +346 -0
- data/lib/chef/provider/one_flow_template.rb +175 -0
- data/lib/chef/provider/one_image.rb +6 -6
- data/lib/chef/provider/one_template.rb +4 -6
- data/lib/chef/provider/one_user.rb +11 -14
- data/lib/chef/provider/one_vnet.rb +6 -9
- data/lib/chef/provider/one_vnet_lease.rb +3 -3
- data/lib/chef/provisioning/driver_init/opennebula.rb +1 -1
- data/lib/chef/provisioning/driver_init/server_version.rb +28 -0
- data/lib/chef/provisioning/opennebula_driver.rb +1 -1
- data/lib/chef/provisioning/opennebula_driver/credentials.rb +2 -2
- data/lib/chef/provisioning/opennebula_driver/driver.rb +132 -34
- data/lib/chef/provisioning/opennebula_driver/flow_lib.rb +491 -0
- data/lib/chef/provisioning/opennebula_driver/one_lib.rb +39 -49
- data/lib/chef/provisioning/opennebula_driver/resources.rb +2 -2
- data/lib/chef/provisioning/opennebula_driver/version.rb +2 -2
- data/lib/chef/resource/one_flow_service.rb +61 -0
- data/lib/chef/resource/one_flow_template.rb +53 -0
- data/lib/chef/resource/one_image.rb +2 -2
- data/lib/chef/resource/one_template.rb +1 -1
- data/lib/chef/resource/one_user.rb +1 -1
- data/lib/chef/resource/one_vnet.rb +2 -2
- data/lib/chef/resource/one_vnet_lease.rb +1 -1
- data/spec/config_sample.rb +12 -3
- data/spec/integration/test_all_integration_spec.rb +6 -272
- data/spec/integration/test_one_driver.rb +177 -0
- data/spec/integration/test_one_flow.rb +546 -0
- data/spec/recipes/OneDriver/{instantiate_one_template_spec.rb → allocate_change_profile.rb} +5 -7
- data/spec/recipes/OneDriver/{converge_back_two_vm_spec.rb → attach_one_image.rb} +4 -5
- data/spec/recipes/OneDriver/create_one_image.rb +20 -0
- data/spec/recipes/OneDriver/{create_one_template_int_spec.rb → create_one_template_ints.rb} +21 -4
- data/spec/recipes/OneDriver/create_one_template_mix.rb +51 -0
- data/spec/recipes/OneDriver/{create_one_image_spec.rb → create_one_template_strings.rb} +18 -7
- data/spec/recipes/OneDriver/{converge_back_one_vm_spec.rb → create_one_vnet.rb} +5 -5
- data/spec/recipes/OneDriver/delete/{OpenNebula-tpl-1-vm.rb → OpenNebula-test-img.rb} +2 -4
- data/spec/recipes/OneDriver/delete/{OpenNebula-back-1-vm.rb → OpenNebula-test-snap-img.rb} +2 -4
- data/spec/recipes/OneDriver/delete/OpenNebula-test-tpl-ints.rb +2 -4
- data/spec/recipes/OneDriver/delete/{OpenNebula-test-tpl.rb → OpenNebula-test-tpl-mix.rb} +2 -4
- data/spec/recipes/OneDriver/delete/OpenNebula-test-tpl-strings.rb +17 -0
- data/spec/recipes/OneDriver/delete/{OpenNebula-back-2-vm.rb → OpenNebula-test-vm-vnet.rb} +2 -4
- data/spec/recipes/OneDriver/delete/{OpenNebula-bootstrap-vm.rb → OpenNebula-test-vm.rb} +2 -4
- data/spec/recipes/OneDriver/delete/OpenNebula-test-vnet.rb +17 -0
- data/spec/recipes/OneDriver/{attach_back_one_vm_spec.rb → instantiate_one_template.rb} +7 -6
- data/spec/recipes/OneDriver/{converge_bootstrap_vm_spec.rb → instantiate_one_template_vnet.rb} +10 -8
- data/spec/recipes/OneDriver/snapshot_one_image.rb +19 -0
- data/spec/recipes/OneFlowService/action/boot.rb +19 -0
- data/spec/recipes/OneFlowService/action/delete.rb +18 -0
- data/spec/recipes/OneFlowService/action/delete_recreate.rb +18 -0
- data/spec/recipes/OneFlowService/action/hold.rb +19 -0
- data/spec/recipes/OneFlowService/action/poweroff.rb +18 -0
- data/spec/recipes/OneFlowService/action/poweroff_hard.rb +18 -0
- data/spec/recipes/OneFlowService/action/reboot.rb +18 -0
- data/spec/recipes/OneFlowService/action/reboot_hard.rb +18 -0
- data/spec/recipes/OneFlowService/action/release.rb +19 -0
- data/spec/recipes/OneFlowService/action/resume.rb +18 -0
- data/spec/recipes/OneFlowService/action/scale.rb +19 -0
- data/spec/recipes/OneFlowService/action/shutdown.rb +18 -0
- data/spec/recipes/OneFlowService/action/shutdown_hard.rb +18 -0
- data/spec/recipes/OneFlowService/action/shutdown_service.rb +17 -0
- data/spec/recipes/OneFlowService/action/snapshot_create.rb +18 -0
- data/spec/recipes/OneFlowService/action/stop.rb +18 -0
- data/spec/recipes/OneFlowService/action/suspend.rb +18 -0
- data/spec/recipes/OneFlowService/action/undeploy.rb +18 -0
- data/spec/recipes/OneFlowService/action/undeploy_hard.rb +18 -0
- data/spec/recipes/OneFlowService/chmod_simple_by_name.rb +19 -0
- data/spec/recipes/OneFlowService/chmod_simple_by_name_2.rb +18 -0
- data/spec/recipes/OneFlowService/chmod_tpl_opts.rb +31 -0
- data/spec/recipes/OneFlowService/delete/test_instance_template_options.rb +17 -0
- data/spec/recipes/OneFlowService/delete/test_role_action.rb +17 -0
- data/spec/recipes/OneFlowService/delete/test_simple_instance.rb +17 -0
- data/spec/recipes/OneFlowService/delete/test_simple_instance_by_id.rb +17 -0
- data/spec/recipes/{OneDriver/create_bootstrap_vm_spec.rb → OneFlowService/instance_role_action.rb} +19 -11
- data/spec/recipes/OneFlowService/instance_simple_by_id.rb +20 -0
- data/spec/recipes/OneFlowService/instance_simple_by_name.rb +18 -0
- data/spec/recipes/OneFlowService/instance_tpl_opts.rb +30 -0
- data/spec/recipes/OneFlowTemplate/chmod_simple_from_hash.rb +17 -0
- data/spec/recipes/OneFlowTemplate/chmod_update_simple_from_file.rb +19 -0
- data/spec/recipes/OneFlowTemplate/create_branch_from_one_id.rb +43 -0
- data/spec/recipes/OneFlowTemplate/create_branch_from_one_name.rb +20 -0
- data/spec/recipes/OneFlowTemplate/create_role_action_instance.rb +99 -0
- data/spec/recipes/OneFlowTemplate/create_simple_from_file.rb +18 -0
- data/spec/recipes/{OneDriver/attach_back_two_vm_spec.rb → OneFlowTemplate/create_simple_from_hash.rb} +8 -6
- data/spec/recipes/OneFlowTemplate/create_simple_from_web.rb +17 -0
- data/spec/recipes/{OneDriver/attach_one_image_spec.rb → OneFlowTemplate/create_simple_instance_tpl.rb} +8 -6
- data/spec/recipes/OneFlowTemplate/create_tpl_opts_from_file.rb +30 -0
- data/spec/recipes/OneFlowTemplate/create_tpl_opts_from_hash.rb +35 -0
- data/spec/recipes/OneFlowTemplate/delete/branch_from_one_id.rb +17 -0
- data/spec/recipes/OneFlowTemplate/delete/branch_from_one_name.rb +17 -0
- data/spec/recipes/OneFlowTemplate/delete/role_action_instance.rb +17 -0
- data/spec/recipes/OneFlowTemplate/delete/simple_from_file.rb +17 -0
- data/spec/recipes/OneFlowTemplate/delete/simple_from_hash.rb +17 -0
- data/spec/recipes/OneFlowTemplate/delete/simple_from_web.rb +17 -0
- data/spec/recipes/OneFlowTemplate/delete/simple_instance_tpl.rb +17 -0
- data/spec/recipes/OneFlowTemplate/delete/tpl_opts_from_file.rb +17 -0
- data/spec/recipes/OneFlowTemplate/delete/tpl_opts_from_hash.rb +17 -0
- data/spec/recipes/OneFlowTemplate/update_simple_from_hash.rb +57 -0
- data/spec/recipes/{driver_options_spec.rb → common.rb} +9 -3
- data/spec/spec_helper.rb +22 -17
- data/spec/support/opennebula_support.rb +75 -45
- metadata +114 -27
- data/spec/recipes/OneDriver/create_back_one_vm_spec.rb +0 -20
- data/spec/recipes/OneDriver/create_back_two_vm_spec.rb +0 -20
- data/spec/recipes/OneDriver/create_one_template_spec.rb +0 -21
- data/spec/recipes/OneDriver/delete/OpenNebula-bootstrap-img.rb +0 -19
- data/spec/recipes/OneDriver/delete/OpenNebula-snap-1-img.rb +0 -19
- data/spec/recipes/OneDriver/delete/OpenNebula-snap-2-img.rb +0 -19
- data/spec/recipes/OneDriver/snapshot_one_image_spec.rb +0 -21
- data/spec/recipes/OneDriver/snapshot_two_image_spec.rb +0 -21
@@ -0,0 +1,175 @@
|
|
1
|
+
# Copyright 2016, BlackBerry Limited
|
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 'rest-client'
|
16
|
+
require 'json'
|
17
|
+
|
18
|
+
#
|
19
|
+
# Implementation of Provider class.
|
20
|
+
#
|
21
|
+
class Chef
|
22
|
+
#
|
23
|
+
# Implementation of Provider class.
|
24
|
+
#
|
25
|
+
class Provider
|
26
|
+
#
|
27
|
+
# Implementation of Provider class.
|
28
|
+
#
|
29
|
+
class OneFlowTemplate < Chef::Provider::LWRPBase
|
30
|
+
use_inline_resources
|
31
|
+
|
32
|
+
provides :one_flow_template
|
33
|
+
|
34
|
+
def initialize(*args)
|
35
|
+
super
|
36
|
+
if !@new_resource.flow_url.nil?
|
37
|
+
flow_url = @new_resource.flow_url
|
38
|
+
elsif !run_context.chef_provisioning.flow_url.nil?
|
39
|
+
flow_url = run_context.chef_provisioning.flow_url
|
40
|
+
elsif !ENV['ONE_FLOW_URL'].nil?
|
41
|
+
flow_url = ENV['ONE_FLOW_URL']
|
42
|
+
else
|
43
|
+
fail 'OneFlow API URL must be specified.'
|
44
|
+
end
|
45
|
+
@flow_lib = Chef::Provisioning::OpenNebulaDriver::FlowLib.new(flow_url, driver.one.client.one_auth)
|
46
|
+
end
|
47
|
+
|
48
|
+
def whyrun_supported?
|
49
|
+
true
|
50
|
+
end
|
51
|
+
|
52
|
+
def load_current_resource
|
53
|
+
@current_resource = Chef::Resource::OneFlowTemplate.new(@new_resource.name, run_context)
|
54
|
+
|
55
|
+
template_name = @new_resource.name
|
56
|
+
|
57
|
+
unless @new_resource.template.nil?
|
58
|
+
tpl = load_template_hash
|
59
|
+
if tpl.nil?
|
60
|
+
@current_resource.exists = false
|
61
|
+
return
|
62
|
+
end
|
63
|
+
tpl = @flow_lib.merge_template(tpl, @new_resource.template_options, true, false)
|
64
|
+
template_name = tpl[:name] if tpl.key?(:name)
|
65
|
+
end
|
66
|
+
|
67
|
+
template_id = @flow_lib.get_unique_template_id(template_name, true)
|
68
|
+
|
69
|
+
@current_resource.exists = !template_id.nil?
|
70
|
+
return unless @current_resource.exists
|
71
|
+
|
72
|
+
@current_resource.template(@flow_lib.get_template(template_id))
|
73
|
+
@current_resource.mode(@flow_lib.get_template_permissions(template_id))
|
74
|
+
|
75
|
+
@current_resource.template_equal = @new_resource.template.nil? ? true : @flow_lib.hash_eq?(
|
76
|
+
@current_resource.template,
|
77
|
+
@flow_lib.normalize_template(@new_resource.name, driver, @flow_lib.merge_template(@current_resource.template, tpl), true)
|
78
|
+
)
|
79
|
+
@current_resource.mode_equal = @current_resource.mode == @new_resource.mode
|
80
|
+
@current_resource.equal = @current_resource.template_equal && @current_resource.mode_equal
|
81
|
+
end
|
82
|
+
|
83
|
+
def load_template_hash
|
84
|
+
template = {}
|
85
|
+
case @new_resource.template
|
86
|
+
when Hash
|
87
|
+
template = @new_resource.template
|
88
|
+
when Fixnum
|
89
|
+
template = @flow_lib.get_template(@new_resource.template)
|
90
|
+
when String
|
91
|
+
case @new_resource.template
|
92
|
+
when %r{^file://(.*)$}
|
93
|
+
template = JSON.parse(::File.read(Regexp.last_match[1]), :symbolize_names => true)
|
94
|
+
when %r{^(?:(\w+)(?::(.*?))?@)?(https?://.*)$}
|
95
|
+
response = RestClient::Request.execute(
|
96
|
+
method: :get,
|
97
|
+
url: Regexp.last_match[3],
|
98
|
+
user: Regexp.last_match[1],
|
99
|
+
password: Regexp.last_match[2]
|
100
|
+
)
|
101
|
+
template = JSON.parse(response, :symbolize_names => true)
|
102
|
+
else
|
103
|
+
tid = @flow_lib.get_unique_template_id(@new_resource.template)
|
104
|
+
template = @flow_lib.get_template(tid)
|
105
|
+
end
|
106
|
+
end
|
107
|
+
template
|
108
|
+
end
|
109
|
+
|
110
|
+
action :create do
|
111
|
+
fail 'Cannot specify template_options without a template' if !@current_resource.template_options.empty? && @current_resource.template_options.nil?
|
112
|
+
|
113
|
+
template_name = @new_resource.name
|
114
|
+
|
115
|
+
unless @new_resource.template.nil?
|
116
|
+
template = load_template_hash
|
117
|
+
|
118
|
+
if @new_resource.template_options.key?(:name)
|
119
|
+
template_name = @new_resource.template_options[:name]
|
120
|
+
elsif template.key?(:name)
|
121
|
+
template_name = template[:name]
|
122
|
+
end
|
123
|
+
end
|
124
|
+
|
125
|
+
if @current_resource.exists
|
126
|
+
unless @current_resource.equal
|
127
|
+
converge_by "updated template '#{template_name}'" do
|
128
|
+
template_id = @flow_lib.get_unique_template_id(template_name)
|
129
|
+
unless @new_resource.template.nil? && @current_resource.template_equal
|
130
|
+
template = @flow_lib.normalize_template(
|
131
|
+
@new_resource.name,
|
132
|
+
driver,
|
133
|
+
@flow_lib.merge_template(template, @new_resource.template_options, true, false),
|
134
|
+
true,
|
135
|
+
true
|
136
|
+
)
|
137
|
+
template = @flow_lib.normalize_template(@new_resource.name, driver, @flow_lib.merge_template(@current_resource.template, template))
|
138
|
+
@flow_lib.update_template(template_name, template)
|
139
|
+
end
|
140
|
+
unless @current_resource.mode_equal
|
141
|
+
@flow_lib.chmod_template(template_id, @new_resource.mode)
|
142
|
+
end
|
143
|
+
@new_resource.updated_by_last_action(true)
|
144
|
+
end
|
145
|
+
end
|
146
|
+
else
|
147
|
+
fail 'Cannot create template without template attribute' if @new_resource.template.nil?
|
148
|
+
converge_by "created template '#{template_name}'" do
|
149
|
+
template = @flow_lib.normalize_template(@new_resource.name, driver, @flow_lib.merge_template(template, @new_resource.template_options, true))
|
150
|
+
@flow_lib.create_template(template)
|
151
|
+
template_id = @flow_lib.get_unique_template_id(template_name)
|
152
|
+
@flow_lib.chmod_template(template_id, @new_resource.mode)
|
153
|
+
@new_resource.updated_by_last_action(true)
|
154
|
+
end
|
155
|
+
end
|
156
|
+
end
|
157
|
+
|
158
|
+
action :delete do
|
159
|
+
if @current_resource.exists
|
160
|
+
template_id = @flow_lib.get_unique_template_id(@new_resource.name, true)
|
161
|
+
converge_by "deleted template '#{@new_resource.name}'" do
|
162
|
+
@flow_lib.delete_template(template_id)
|
163
|
+
@new_resource.updated_by_last_action(true)
|
164
|
+
end
|
165
|
+
end
|
166
|
+
end
|
167
|
+
|
168
|
+
protected
|
169
|
+
|
170
|
+
def driver
|
171
|
+
@new_resource.driver.nil? ? run_context.chef_provisioning.current_driver : run_context.chef_provisioning.driver_for(@new_resource.driver)
|
172
|
+
end
|
173
|
+
end
|
174
|
+
end
|
175
|
+
end
|
@@ -1,4 +1,4 @@
|
|
1
|
-
# Copyright
|
1
|
+
# Copyright 2016, BlackBerry Limited
|
2
2
|
#
|
3
3
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
4
4
|
# you may not use this file except in compliance with the License.
|
@@ -49,7 +49,7 @@ class Chef
|
|
49
49
|
|
50
50
|
action :allocate do
|
51
51
|
if exists?
|
52
|
-
action_handler.report_progress "image '#{new_resource.name}' already exists -
|
52
|
+
action_handler.report_progress "image '#{new_resource.name}' already exists - (up to date)"
|
53
53
|
else
|
54
54
|
fail "'size' must be specified" unless new_resource.size
|
55
55
|
fail "'datastore_id' must be specified" unless new_resource.datastore_id
|
@@ -81,7 +81,7 @@ class Chef
|
|
81
81
|
@new_resource.updated_by_last_action(true)
|
82
82
|
end
|
83
83
|
when 'READY', 'USED', 'USED_PERS'
|
84
|
-
action_handler.report_progress "image '#{new_resource.name}' is already in #{@image.state_str} state -
|
84
|
+
action_handler.report_progress "image '#{new_resource.name}' is already in #{@image.state_str} state - (up to date)"
|
85
85
|
else
|
86
86
|
fail "Image #{new_resource.name} is in unexpected state '#{@image.state_str}'"
|
87
87
|
end
|
@@ -99,7 +99,7 @@ class Chef
|
|
99
99
|
@new_resource.updated_by_last_action(true)
|
100
100
|
end
|
101
101
|
else
|
102
|
-
action_handler.report_progress "image '#{new_resource.name}' does not exist -
|
102
|
+
action_handler.report_progress "image '#{new_resource.name}' does not exist - (up to date)"
|
103
103
|
end
|
104
104
|
end
|
105
105
|
|
@@ -120,7 +120,7 @@ class Chef
|
|
120
120
|
|
121
121
|
disk_id = new_driver.one.get_disk_id(vm, disk_hash['IMAGE']['NAME'])
|
122
122
|
if !disk_id.nil?
|
123
|
-
action_handler.report_progress "disk is already attached" unless disk_id.nil?
|
123
|
+
action_handler.report_progress "disk is already attached - (up to date)" unless disk_id.nil?
|
124
124
|
elsif disk_id.nil?
|
125
125
|
action_handler.report_progress "disk not attached, attaching..."
|
126
126
|
rc = vm.disk_attach(disk_tpl)
|
@@ -187,7 +187,7 @@ class Chef
|
|
187
187
|
@image['TEMPLATE/DRIVER'] == image_config[:driver] &&
|
188
188
|
@image['TEMPLATE/DESCRIPTION'] == image_config[:description] &&
|
189
189
|
@image['DATASTORE_ID'] == image_config[:datastore_id]
|
190
|
-
action_handler.report_progress("image '#{@new_resource.name}' (ID: #{@image.id}) already exists -
|
190
|
+
action_handler.report_progress("image '#{@new_resource.name}' (ID: #{@image.id}) already exists - (up to date)")
|
191
191
|
else
|
192
192
|
fail "image '#{new_resource.name}' already exists, but it is not the same image"
|
193
193
|
end
|
@@ -1,4 +1,4 @@
|
|
1
|
-
# Copyright
|
1
|
+
# Copyright 2016, BlackBerry Limited
|
2
2
|
#
|
3
3
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
4
4
|
# you may not use this file except in compliance with the License.
|
@@ -139,12 +139,10 @@ class Chef
|
|
139
139
|
end
|
140
140
|
|
141
141
|
def create_template
|
142
|
-
if new_resource.template_file && new_resource.template.
|
143
|
-
|
144
|
-
'exclusive.')
|
145
|
-
elsif new_resource.template_file
|
142
|
+
fail("Attributes 'template_file' and 'template' are mutually 'exclusive.'") if new_resource.template_file && !new_resource.template.empty?
|
143
|
+
if new_resource.template_file
|
146
144
|
::File.read(new_resource.template_file)
|
147
|
-
elsif new_resource.template.
|
145
|
+
elsif !new_resource.template.empty?
|
148
146
|
driver.one.create_template(new_resource.template)
|
149
147
|
else
|
150
148
|
fail("Missing attribute 'template_file' or 'template' in " \
|
@@ -1,4 +1,4 @@
|
|
1
|
-
# Copyright
|
1
|
+
# Copyright 2016, BlackBerry Limited
|
2
2
|
#
|
3
3
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
4
4
|
# you may not use this file except in compliance with the License.
|
@@ -45,7 +45,7 @@ class Chef
|
|
45
45
|
fail "Missing attribute 'password'" unless @new_resource.password
|
46
46
|
|
47
47
|
if exists?(:name => @new_resource.name)
|
48
|
-
action_handler.report_progress "user '#{@new_resource.name}' already exists -
|
48
|
+
action_handler.report_progress "user '#{@new_resource.name}' already exists - (up to date)"
|
49
49
|
else
|
50
50
|
action_handler.perform_action "create user '#{@new_resource.name}'" do
|
51
51
|
user = OpenNebula::User.new(OpenNebula::User.build_xml, @client)
|
@@ -65,22 +65,19 @@ class Chef
|
|
65
65
|
@new_resource.updated_by_last_action(true)
|
66
66
|
end
|
67
67
|
else
|
68
|
-
action_handler.report_progress "user '#{new_resource.name}' does not exists -
|
68
|
+
action_handler.report_progress "user '#{new_resource.name}' does not exists - (up to date)"
|
69
69
|
end
|
70
70
|
end
|
71
71
|
|
72
72
|
action :update do
|
73
|
-
|
74
|
-
|
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
|
73
|
+
fail "user '#{new_resource.name}' does not exists" unless exists?(:id => @new_resource.user_id, :name => @new_resource.name)
|
74
|
+
fail "':template' or ':template_file' attribute missing" unless @new_resource.template || @new_resource.template_file
|
78
75
|
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
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)
|
84
81
|
end
|
85
82
|
|
86
83
|
protected
|
@@ -89,7 +86,7 @@ class Chef
|
|
89
86
|
if current_driver && current_driver.driver_url != new_driver.driver_url
|
90
87
|
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
88
|
end
|
92
|
-
fail "Driver not specified for
|
89
|
+
fail "Driver not specified for one_user #{new_resource.name}" unless new_driver
|
93
90
|
new_driver
|
94
91
|
end
|
95
92
|
|
@@ -1,4 +1,4 @@
|
|
1
|
-
# Copyright
|
1
|
+
# Copyright 2016, BlackBerry Limited
|
2
2
|
#
|
3
3
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
4
4
|
# you may not use this file except in compliance with the License.
|
@@ -46,7 +46,7 @@ class Chef
|
|
46
46
|
fail "Missing attribute 'cluster_id'" unless @new_resource.cluster_id
|
47
47
|
|
48
48
|
if exists?(:name => @new_resource.name)
|
49
|
-
action_handler.report_progress "vnet '#{@new_resource.name}' already exists -
|
49
|
+
action_handler.report_progress "vnet '#{@new_resource.name}' already exists - (up to date)"
|
50
50
|
else
|
51
51
|
action_handler.perform_action "created vnet '#{@new_resource.name}' from '#{@new_resource.template_file}'" do
|
52
52
|
template_str = ::File.read(@new_resource.template_file) + "\nNAME=\"#{@new_resource.name}\""
|
@@ -67,7 +67,7 @@ class Chef
|
|
67
67
|
@new_resource.updated_by_last_action(true)
|
68
68
|
end
|
69
69
|
else
|
70
|
-
action_handler.report_progress "vnet '#{new_resource.name}' does not exists -
|
70
|
+
action_handler.report_progress "vnet '#{new_resource.name}' does not exists - (up to date)"
|
71
71
|
end
|
72
72
|
end
|
73
73
|
|
@@ -86,11 +86,8 @@ class Chef
|
|
86
86
|
else
|
87
87
|
same = ar_pool[0]['AR']['SIZE'].to_i == @new_resource.size
|
88
88
|
end
|
89
|
-
|
90
|
-
|
91
|
-
else
|
92
|
-
fail "vnet '#{@new_resource.name}' exists with different configuration"
|
93
|
-
end
|
89
|
+
fail "vnet '#{@new_resource.name}' exists with different configuration" unless same
|
90
|
+
action_handler.report_progress "vnet '#{@new_resource.name}' already exists - (up to date)"
|
94
91
|
else
|
95
92
|
fail "parent network '#{@new_resource.network}' does not exist" unless exists?(:id => @new_resource.network)
|
96
93
|
action_handler.perform_action "reserved vnet '#{@new_resource.name}'" do
|
@@ -107,7 +104,7 @@ class Chef
|
|
107
104
|
if current_driver && current_driver.driver_url != new_driver.driver_url
|
108
105
|
fail "Cannot move '#{machine_spec.name}' from #{current_driver.driver_url} to #{new_driver.driver_url}: machine moving is not supported. Destroy and recreate."
|
109
106
|
end
|
110
|
-
fail "Driver not specified for
|
107
|
+
fail "Driver not specified for one_vnet #{new_resource.name}" unless new_driver
|
111
108
|
new_driver
|
112
109
|
end
|
113
110
|
|
@@ -1,4 +1,4 @@
|
|
1
|
-
# Copyright
|
1
|
+
# Copyright 2016, BlackBerry Limited
|
2
2
|
#
|
3
3
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
4
4
|
# you may not use this file except in compliance with the License.
|
@@ -72,7 +72,7 @@ class Chef
|
|
72
72
|
@new_resource.updated_by_last_action(true)
|
73
73
|
end
|
74
74
|
else
|
75
|
-
action_handler.report_progress("#{@new_resource.name} is not present -
|
75
|
+
action_handler.report_progress("#{@new_resource.name} is not present - (up to date)")
|
76
76
|
end
|
77
77
|
end
|
78
78
|
|
@@ -102,7 +102,7 @@ class Chef
|
|
102
102
|
if current_driver && current_driver.driver_url != new_driver.driver_url
|
103
103
|
fail "Cannot move '#{machine_spec.name}' from #{current_driver.driver_url} to #{new_driver.driver_url}: machine moving is not supported. Destroy and recreate."
|
104
104
|
end
|
105
|
-
fail "Driver not specified for
|
105
|
+
fail "Driver not specified for one_vnet_lease #{new_resource.name}" unless new_driver
|
106
106
|
new_driver
|
107
107
|
end
|
108
108
|
|
@@ -0,0 +1,28 @@
|
|
1
|
+
# Copyright 2016, BlackBerry Limited
|
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
|
+
require 'json'
|
17
|
+
|
18
|
+
endpoint = ARGV[0].dup
|
19
|
+
credentials = ARGV[1].dup
|
20
|
+
options = JSON.parse(ARGV[2].dup)
|
21
|
+
|
22
|
+
version = OpenNebula::Client.new(credentials, endpoint, options).get_version
|
23
|
+
|
24
|
+
if version.is_a?(String)
|
25
|
+
puts version
|
26
|
+
else
|
27
|
+
puts version.inspect
|
28
|
+
end
|
@@ -1,4 +1,4 @@
|
|
1
|
-
# Copyright
|
1
|
+
# Copyright 2016, BlackBerry Limited
|
2
2
|
#
|
3
3
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
4
4
|
# you may not use this file except in compliance with the License.
|
@@ -37,7 +37,7 @@ class Chef
|
|
37
37
|
end
|
38
38
|
|
39
39
|
def default
|
40
|
-
fail 'No credentials loaded! Do you have a ~/.one/one_auth file?' if @credentials.
|
40
|
+
fail 'No credentials loaded! Do you have a ~/.one/one_auth file?' if @credentials.empty?
|
41
41
|
@credentials[ENV['ONE_DEFAULT_PROFILE'] || 'default'] || @credentials.first[1]
|
42
42
|
end
|
43
43
|
|
@@ -1,4 +1,4 @@
|
|
1
|
-
# Copyright
|
1
|
+
# Copyright 2016, BlackBerry Limited
|
2
2
|
#
|
3
3
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
4
4
|
# you may not use this file except in compliance with the License.
|
@@ -19,13 +19,88 @@ require 'chef/provisioning/convergence_strategy/install_sh'
|
|
19
19
|
require 'chef/provisioning/convergence_strategy/no_converge'
|
20
20
|
require 'chef/provisioning/transport/ssh'
|
21
21
|
require 'chef/provisioning/opennebula_driver/version'
|
22
|
-
require 'chef/provisioning/opennebula_driver/one_lib'
|
23
22
|
require 'chef/provisioning/opennebula_driver/credentials'
|
24
23
|
require 'net/ssh/proxy/command'
|
24
|
+
require 'json'
|
25
|
+
require 'open3'
|
26
|
+
|
27
|
+
class Chef
|
28
|
+
module DSL
|
29
|
+
#
|
30
|
+
# Module extension.
|
31
|
+
#
|
32
|
+
module Recipe
|
33
|
+
def with_flow_url(url)
|
34
|
+
run_context.chef_provisioning.with_flow_url(url)
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
class Chef
|
41
|
+
module Provisioning
|
42
|
+
#
|
43
|
+
# Class extension.
|
44
|
+
#
|
45
|
+
class ChefRunData
|
46
|
+
attr_accessor :flow_url
|
47
|
+
def with_flow_url(url)
|
48
|
+
@flow_url = url
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
25
53
|
|
26
54
|
class Chef
|
27
55
|
module Provisioning
|
56
|
+
# OpenNebulaDriver module.
|
28
57
|
module OpenNebulaDriver
|
58
|
+
def self.match_driver_url(url, allow_nil_profile = false)
|
59
|
+
scan = url.match(%r/opennebula:(https?:\/\/[^:\/]+ (?::[0-9]{2,5})? (?:\/[^:\s]+) ) :?([^:\s]+)?/x)
|
60
|
+
fail "'driver_url' option has invalid format: #{url}" if scan.nil?
|
61
|
+
endpoint = scan[1]
|
62
|
+
profile = scan[2]
|
63
|
+
fail "'driver_url' option is missing an endpoint: #{url}" if endpoint.nil?
|
64
|
+
fail "'driver_url' option is missing a profile: #{url}" if profile.nil? && !allow_nil_profile
|
65
|
+
[endpoint, profile]
|
66
|
+
end
|
67
|
+
|
68
|
+
def self.get_onelib(args)
|
69
|
+
endpoint = args[:endpoint]
|
70
|
+
credentials = args[:credentials]
|
71
|
+
options = args[:options] || {}
|
72
|
+
if args[:driver_url]
|
73
|
+
fail "OpenNebula driver_url cannot be #{args[:driver_url].class}, it must be a String" unless args[:driver_url].is_a?(String)
|
74
|
+
endpoint, profile = Chef::Provisioning::OpenNebulaDriver.match_driver_url(args[:driver_url])
|
75
|
+
one_profile = Chef::Provisioning::OpenNebulaDriver::Credentials.new[profile]
|
76
|
+
credentials = one_profile[:credentials]
|
77
|
+
options = one_profile[:options] || {}
|
78
|
+
end
|
79
|
+
fail "OpenNebula credentials cannot be #{credentials.class}, it must be a String" unless credentials.is_a?(String)
|
80
|
+
fail "OpenNebula endpoint cannot be #{endpoint.class}, it must be a String" unless endpoint.is_a?(String)
|
81
|
+
fail "OpenNebula options cannot be #{options.class}, it must be a Hash" unless options.is_a?(Hash)
|
82
|
+
server_version, = Open3.capture2e("ruby #{File.dirname(__FILE__)}/../driver_init/server_version.rb #{endpoint} #{credentials} #{options.to_json.inspect}")
|
83
|
+
server_version.strip!
|
84
|
+
fail server_version unless server_version =~ /^\d+\.\d+(?:\.\d+)?$/
|
85
|
+
begin
|
86
|
+
gem 'opennebula', "~> #{server_version}"
|
87
|
+
require 'opennebula'
|
88
|
+
rescue Gem::LoadError => e
|
89
|
+
e_inspect = e.inspect
|
90
|
+
raise e unless e_inspect.include?('already activated')
|
91
|
+
end
|
92
|
+
require 'chef/provisioning/opennebula_driver/one_lib'
|
93
|
+
gem_version = Gem.loaded_specs['opennebula'].version.to_s
|
94
|
+
if gem_version == server_version
|
95
|
+
Chef::Log.debug("You are using OpenNebula gem version #{gem_version} against OpenNebula server version #{server_version}")
|
96
|
+
else
|
97
|
+
Chef::Log.warn('OPENNEBULA GEM / SERVER VERSION MISMATCH')
|
98
|
+
Chef::Log.warn("You are using OpenNebula gem version #{gem_version} against OpenNebula server version #{server_version}")
|
99
|
+
Chef::Log.warn('Users may experience issues with this gem.')
|
100
|
+
end
|
101
|
+
OneLib.new(:credentials => credentials, :endpoint => endpoint, :options => options)
|
102
|
+
end
|
103
|
+
|
29
104
|
#
|
30
105
|
# A Driver instance represents a place where machines can be created
|
31
106
|
# and found, and contains methods to create, delete, start, stop, and
|
@@ -81,11 +156,14 @@ class Chef
|
|
81
156
|
#
|
82
157
|
# driver_options:
|
83
158
|
# credentials: bbsl-auto:text_pass credentials has precedence over secret_file
|
84
|
-
#
|
159
|
+
# endpoint: opennebula endpoint
|
160
|
+
# options: additional options for OpenNebula::Client
|
85
161
|
#
|
86
162
|
def initialize(driver_url, config)
|
87
163
|
super
|
88
|
-
@one =
|
164
|
+
@one = Chef::Provisioning::OpenNebulaDriver.get_onelib(:driver_url => driver_url)
|
165
|
+
@driver_url_with_profile = driver_url
|
166
|
+
@driver_url = @one.client.one_endpoint
|
89
167
|
end
|
90
168
|
|
91
169
|
def self.from_url(driver_url, config)
|
@@ -124,17 +202,17 @@ class Chef
|
|
124
202
|
instance = instance_for(machine_spec)
|
125
203
|
return machine_spec unless instance.nil?
|
126
204
|
|
127
|
-
unless machine_options
|
205
|
+
unless machine_options[:bootstrap_options]
|
128
206
|
fail "'bootstrap_options' must be specified"
|
129
207
|
end
|
130
208
|
check_unique_names(machine_options, machine_spec)
|
131
209
|
action_handler.perform_action "created vm '#{machine_spec.name}'" do
|
132
210
|
Chef::Log.debug(machine_options)
|
133
211
|
tpl = @one.get_template(machine_spec.name,
|
134
|
-
machine_options
|
212
|
+
machine_options[:bootstrap_options])
|
135
213
|
vm = @one.allocate_vm(tpl)
|
136
214
|
populate_node_object(machine_spec, machine_options, vm)
|
137
|
-
@one.chmod_resource(vm, machine_options
|
215
|
+
@one.chmod_resource(vm, machine_options[:bootstrap_options][:mode])
|
138
216
|
|
139
217
|
# This option allows to manipulate how the machine shows up
|
140
218
|
# in the OpenNebula UI and CLI tools. We either set the VM
|
@@ -224,11 +302,17 @@ class Chef
|
|
224
302
|
end
|
225
303
|
fail "Failed to destroy '#{instance.name}'. Current state: #{instance.state_str}" if instance.state_str != 'DONE'
|
226
304
|
end
|
305
|
+
elsif machine_spec.reference
|
306
|
+
Chef::Log.info("vm #{machine_spec.name} (#{machine_spec.reference['instance_id']}) does not exist - (up to date)")
|
227
307
|
else
|
228
|
-
Chef::Log.info("vm #{machine_spec.name}
|
308
|
+
Chef::Log.info("vm #{machine_spec.name} does not exist - (up to date)")
|
309
|
+
end
|
310
|
+
begin
|
311
|
+
strategy = convergence_strategy_for(machine_spec, machine_options)
|
312
|
+
strategy.cleanup_convergence(action_handler, machine_spec)
|
313
|
+
rescue Net::HTTPServerException => e
|
314
|
+
raise unless e.response.code == '404'
|
229
315
|
end
|
230
|
-
strategy = convergence_strategy_for(machine_spec, machine_options)
|
231
|
-
strategy.cleanup_convergence(action_handler, machine_spec)
|
232
316
|
end
|
233
317
|
|
234
318
|
# Stop the given machine.
|
@@ -251,7 +335,7 @@ class Chef
|
|
251
335
|
end
|
252
336
|
end
|
253
337
|
else
|
254
|
-
Chef::Log.info("vm #{machine_spec.name} (#{machine_spec.reference['instance_id']}) does not exist -
|
338
|
+
Chef::Log.info("vm #{machine_spec.name} (#{machine_spec.reference['instance_id']}) does not exist - (up to date)")
|
255
339
|
end
|
256
340
|
end
|
257
341
|
|
@@ -314,7 +398,7 @@ class Chef
|
|
314
398
|
def destroy_image(action_handler, image_spec, _image_options)
|
315
399
|
img = @one.get_resource(:image, :id => image_spec.location['image_id'].to_i)
|
316
400
|
if img.nil?
|
317
|
-
action_handler.report_progress "image #{image_spec.name} (#{image_spec.location['image_id']}) does not exist -
|
401
|
+
action_handler.report_progress "image #{image_spec.name} (#{image_spec.location['image_id']}) does not exist - (up to date)"
|
318
402
|
else
|
319
403
|
action_handler.perform_action "deleted image #{image_spec.name} (#{image_spec.location['image_id']})" do
|
320
404
|
rc = img.delete
|
@@ -413,6 +497,7 @@ class Chef
|
|
413
497
|
# @param [Array[ChefMetal::MachineSpec]] machine_specs
|
414
498
|
# An array of machine specs the load balancer should have
|
415
499
|
def allocate_load_balancer(_action_handler, _lb_spec, _lb_options, _machine_specs)
|
500
|
+
fail "'allocate_load_balancer' is not implemented"
|
416
501
|
end
|
417
502
|
|
418
503
|
# Make the load balancer ready
|
@@ -420,6 +505,7 @@ class Chef
|
|
420
505
|
# @param [ChefMetal::LoadBalancerSpec] lb_spec Frozen LB specification
|
421
506
|
# @param [Hash] lb_options A hash of options to pass the LB
|
422
507
|
def ready_load_balancer(_action_handler, _lb_spec, _lb_options, _machine_specs)
|
508
|
+
fail "'ready_load_balancer' is not implemented"
|
423
509
|
end
|
424
510
|
|
425
511
|
# Destroy the load balancer
|
@@ -427,6 +513,7 @@ class Chef
|
|
427
513
|
# @param [ChefMetal::LoadBalancerSpec] lb_spec Frozen LB specification
|
428
514
|
# @param [Hash] lb_options A hash of options to pass the LB
|
429
515
|
def destroy_load_balancer(_action_handler, _lb_spec, _lb_options)
|
516
|
+
fail "'destroy_load_balancer' is not implemented"
|
430
517
|
end
|
431
518
|
|
432
519
|
protected
|
@@ -444,7 +531,7 @@ class Chef
|
|
444
531
|
end
|
445
532
|
|
446
533
|
def check_unique_names(machine_options, machine_spec)
|
447
|
-
return unless machine_options
|
534
|
+
return unless machine_options[:bootstrap_options][:unique_names]
|
448
535
|
hostname = if machine_options[:vm_name] == :short
|
449
536
|
machine_spec.name.split('.').first
|
450
537
|
elsif machine_options[:vm_name].is_a?(String)
|
@@ -458,13 +545,13 @@ class Chef
|
|
458
545
|
end
|
459
546
|
|
460
547
|
def populate_node_object(machine_spec, machine_options, vm)
|
461
|
-
machine_spec.driver_url =
|
548
|
+
machine_spec.driver_url = @driver_url_with_profile
|
462
549
|
machine_spec.reference = {
|
463
550
|
'driver_version' => Chef::Provisioning::OpenNebulaDriver::VERSION,
|
464
551
|
'allocated_at' => Time.now.utc.to_s,
|
465
|
-
'image_id' => machine_options
|
466
|
-
'is_shutdown' => machine_options
|
467
|
-
'shutdown_hard' => machine_options
|
552
|
+
'image_id' => machine_options[:bootstrap_options][:image_id] || nil,
|
553
|
+
'is_shutdown' => machine_options[:bootstrap_options][:is_shutdown] || false,
|
554
|
+
'shutdown_hard' => machine_options[:bootstrap_options][:shutdown_hard] || false,
|
468
555
|
'instance_id' => vm.id,
|
469
556
|
'name' => vm.name,
|
470
557
|
'state' => vm.state_str
|
@@ -478,7 +565,7 @@ class Chef
|
|
478
565
|
end
|
479
566
|
|
480
567
|
def populate_img_object(image_spec, new_image)
|
481
|
-
image_spec.driver_url =
|
568
|
+
image_spec.driver_url = @driver_url_with_profile
|
482
569
|
image_spec.reference = {
|
483
570
|
'driver_version' => Chef::Provisioning::OpenNebulaDriver::VERSION,
|
484
571
|
'image_id' => new_image,
|
@@ -492,13 +579,15 @@ class Chef
|
|
492
579
|
def instance_for(machine_spec)
|
493
580
|
instance = nil
|
494
581
|
if machine_spec.reference
|
495
|
-
|
496
|
-
|
582
|
+
current_endpoint, = Chef::Provisioning::OpenNebulaDriver.match_driver_url(machine_spec.driver_url, true)
|
583
|
+
fail "Cannot move '#{machine_spec.name}' from #{current_endpoint} to #{driver_url}: machine moving is not supported. Destroy and recreate." if current_endpoint != driver_url
|
497
584
|
instance = @one.get_resource(:virtualmachine, :id => machine_spec.reference['instance_id'].to_i)
|
585
|
+
machine_spec.driver_url = @driver_url_with_profile
|
498
586
|
elsif machine_spec.location
|
499
|
-
|
500
|
-
|
587
|
+
current_endpoint, = Chef::Provisioning::OpenNebulaDriver.match_driver_url(machine_spec.driver_url, true)
|
588
|
+
fail "Cannot move '#{machine_spec.name}' from #{current_endpoint} to #{driver_url}: machine moving is not supported. Destroy and recreate." if current_endpoint != driver_url
|
501
589
|
instance = @one.get_resource(:virtualmachine, :id => machine_spec.location['server_id'].to_i)
|
590
|
+
machine_spec.driver_url = @driver_url_with_profile
|
502
591
|
unless instance.nil?
|
503
592
|
# Convert from previous driver
|
504
593
|
machine_spec.reference = {
|
@@ -518,9 +607,11 @@ class Chef
|
|
518
607
|
instance = instance_for(machine_spec)
|
519
608
|
fail "#{machine_spec.name} (#{machine_spec.reference['instance_id']}) does not exist!" if instance.nil?
|
520
609
|
# TODO: Support Windoze VMs (see chef-provisioning-vagrant)
|
521
|
-
Chef::Provisioning::Machine::UnixMachine.new(
|
522
|
-
|
523
|
-
|
610
|
+
Chef::Provisioning::Machine::UnixMachine.new(
|
611
|
+
machine_spec,
|
612
|
+
transport_for(machine_spec, machine_options, instance),
|
613
|
+
convergence_strategy_for(machine_spec, machine_options)
|
614
|
+
)
|
524
615
|
end
|
525
616
|
|
526
617
|
def get_ssh_user(machine_spec, machine_options)
|
@@ -539,7 +630,7 @@ class Chef
|
|
539
630
|
}.merge(machine_options[:ssh_options] || {})
|
540
631
|
ssh_options[:proxy] = Net::SSH::Proxy::Command.new(ssh_options[:proxy]) if ssh_options.key?(:proxy)
|
541
632
|
|
542
|
-
connection_timeout = machine_options[:connection_timeout] || 300
|
633
|
+
connection_timeout = machine_options[:connection_timeout] || 300 # default is 5 min
|
543
634
|
username = get_ssh_user(machine_spec, machine_options)
|
544
635
|
|
545
636
|
options = {}
|
@@ -552,21 +643,28 @@ class Chef
|
|
552
643
|
|
553
644
|
transport = Chef::Provisioning::Transport::SSH.new(machine_spec.reference['ip'], username, ssh_options, options, config)
|
554
645
|
|
555
|
-
|
556
|
-
|
646
|
+
rc = retryable_operation("Waiting for SSH connection", connection_timeout.to_i) { transport.available? }
|
647
|
+
fail "Failed to establish SSH connection to '#{machine_spec.name}'" if rc.nil?
|
648
|
+
transport
|
649
|
+
end
|
650
|
+
|
651
|
+
# Retry an operation until the timeout expires. Will always try at least once.
|
652
|
+
def retryable_operation(msg = "operation", timeout = 15, delay = 3)
|
653
|
+
return nil unless block_given?
|
557
654
|
start = Time.now
|
558
655
|
loop do
|
559
|
-
|
560
|
-
|
561
|
-
|
562
|
-
|
656
|
+
return true if yield
|
657
|
+
Chef::Log.info(msg)
|
658
|
+
sleep delay
|
659
|
+
break if (Time.now - start) > timeout
|
563
660
|
end
|
564
|
-
|
661
|
+
Chef::Log.error("Timed out waiting for operation to complete: '#{msg}'")
|
662
|
+
nil
|
565
663
|
end
|
566
664
|
|
567
665
|
def convergence_strategy_for(machine_spec, machine_options)
|
568
666
|
# TODO: Support Windoze VMs (see chef-provisioning-vagrant)
|
569
|
-
convergence_options = Cheffish::MergedConfig.new(machine_options[:convergence_options] || {})
|
667
|
+
convergence_options = Cheffish::MergedConfig.new(machine_options ? machine_options[:convergence_options] || {} : {})
|
570
668
|
|
571
669
|
if !machine_spec.reference
|
572
670
|
Chef::Provisioning::ConvergenceStrategy::NoConverge.new(convergence_options, config)
|