knife-xapi 0.5.4 → 0.6.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/lib/chef/knife/xapi_base.rb +168 -152
- data/lib/chef/knife/xapi_guest_create.rb +186 -167
- data/lib/chef/knife/xapi_guest_delete.rb +28 -30
- data/lib/chef/knife/xapi_guest_list.rb +13 -15
- data/lib/chef/knife/xapi_guest_start.rb +5 -6
- data/lib/chef/knife/xapi_guest_stop.rb +4 -7
- data/lib/chef/knife/xapi_network_list.rb +10 -13
- data/lib/chef/knife/xapi_vdi_attach.rb +33 -40
- data/lib/chef/knife/xapi_vdi_create.rb +24 -26
- data/lib/chef/knife/xapi_vdi_delete.rb +30 -33
- data/lib/chef/knife/xapi_vdi_detach.rb +36 -43
- data/lib/chef/knife/xapi_vdi_list.rb +17 -21
- data/lib/chef/knife/xapi_vlan_list.rb +6 -9
- data/lib/chef/knife/xapi_vmselect.rb +7 -9
- data/lib/knife-xapi/version.rb +1 -1
- data/lib/xenapi/xenapi.rb +2 -3
- data/lib/xenapi/xenapi/async_dispatcher.rb +2 -2
- data/lib/xenapi/xenapi/client.rb +53 -55
- data/lib/xenapi/xenapi/errors.rb +3 -3
- data/lib/xenapi/xenapi/xmlrpc_client.rb +3 -3
- metadata +19 -33
@@ -18,7 +18,6 @@
|
|
18
18
|
# limitations under the License.
|
19
19
|
#
|
20
20
|
|
21
|
-
|
22
21
|
require 'chef/knife/xapi_base'
|
23
22
|
|
24
23
|
class Chef
|
@@ -27,136 +26,142 @@ class Chef
|
|
27
26
|
require 'timeout'
|
28
27
|
include Chef::Knife::XapiBase
|
29
28
|
|
30
|
-
Chef::Knife::XapiBase.set_defaults(
|
31
|
-
:
|
32
|
-
:
|
33
|
-
:
|
34
|
-
:
|
35
|
-
:
|
36
|
-
:
|
37
|
-
:
|
38
|
-
:
|
39
|
-
:
|
40
|
-
:
|
41
|
-
:
|
42
|
-
:
|
43
|
-
:
|
44
|
-
|
29
|
+
Chef::Knife::XapiBase.set_defaults(
|
30
|
+
domain: '',
|
31
|
+
ssh_user: 'root',
|
32
|
+
ssh_port: '22',
|
33
|
+
ping_timeout: 600,
|
34
|
+
install_repo: 'http://isoredirect.centos.org/centos/6/os/x86_64/',
|
35
|
+
kernel_params: 'graphical utf8',
|
36
|
+
xapi_disk_size: '8g',
|
37
|
+
xapi_cpus: '1',
|
38
|
+
xapi_mem: '1g',
|
39
|
+
bootstrap_template: 'chef-full',
|
40
|
+
template_file: false,
|
41
|
+
run_list: [],
|
42
|
+
json_attributes: {}
|
43
|
+
)
|
45
44
|
|
46
45
|
deps do
|
47
46
|
require 'chef/knife/bootstrap'
|
48
47
|
Chef::Knife::Bootstrap.load_deps
|
49
48
|
end
|
50
49
|
|
51
|
-
banner
|
50
|
+
banner 'knife xapi guest create NAME [NETWORKS] (options)'
|
52
51
|
|
53
52
|
option :xapi_vm_template,
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
53
|
+
short: '-T Template Name Label',
|
54
|
+
long: '--xapi-vm-template',
|
55
|
+
proc: proc { |key| Chef::Config[:knife][:xapi_vm_template] = key },
|
56
|
+
description: 'xapi template name to create from. accepts an string or regex'
|
58
57
|
|
59
58
|
option :install_repo,
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
59
|
+
short: "-R If you're using a builtin template you will need to specify a repo url",
|
60
|
+
long: '--install-repo',
|
61
|
+
description: 'Install repo for this template (if needed)',
|
62
|
+
proc: proc { |key| Chef::Config[:knife][:install_repo] = key }
|
63
|
+
|
64
|
+
option :macaddress,
|
65
|
+
short: '-m MAC Address',
|
66
|
+
long: '--mac-address',
|
67
|
+
description: 'Use a pre-generated MAC address (optional)',
|
68
|
+
proc: proc { |key| Chef::Config[:knife][:macaddress] = key }
|
64
69
|
|
65
70
|
option :xapi_sr,
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
71
|
+
short: '-S Storage repo to provision VM from',
|
72
|
+
long: '--xapi-sr',
|
73
|
+
proc: proc { |key| Chef::Config[:knife][:xapi_sr] = key },
|
74
|
+
description: 'The Xen SR to use, If blank will use pool/hypervisor default'
|
70
75
|
|
71
76
|
option :kernel_params,
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
77
|
+
short: '-B Set of kernel boot params to pass to the vm',
|
78
|
+
long: '--kernel-params',
|
79
|
+
description: "You can add more boot options to the vm e.g.: \"ks='http://foo.local/ks'\"",
|
80
|
+
proc: proc { |key| Chef::Config[:knife][:kernel_params] = key }
|
76
81
|
|
77
82
|
option :xapi_skip_disk,
|
78
|
-
|
79
|
-
|
80
|
-
|
83
|
+
long: '--xapi-skip-disk',
|
84
|
+
proc: proc { |key| Chef::Config[:knife][:xapi_skip_disk] = key },
|
85
|
+
description: "Don't try to add disks to the new VM"
|
81
86
|
|
82
87
|
option :xapi_disk_size,
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
88
|
+
short: '-D Size of disk. 1g 512m etc',
|
89
|
+
long: '--xapi-disk-size',
|
90
|
+
description: "The size of the root disk, use 'm' 'g' 't' if no unit specified assumes g",
|
91
|
+
proc: proc { |key| Chef::Config[:knife][:xapi_disk_size] = key.to_s }
|
87
92
|
|
88
93
|
option :xapi_cpus,
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
94
|
+
short: '-C Number of VCPUs to provision',
|
95
|
+
long: '--xapi-cpus',
|
96
|
+
description: 'Number of VCPUS this vm should have 1 4 8 etc',
|
97
|
+
proc: proc { |key| Chef::Config[:knife][:xapi_cpus] = key.to_s }
|
93
98
|
|
94
99
|
option :xapi_mem,
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
100
|
+
short: '-M Ammount of memory to provision',
|
101
|
+
long: '--xapi-mem',
|
102
|
+
description: 'Ammount of memory the VM should have specify with m g etc 512m, 2g if no unit spcified it assumes gigabytes',
|
103
|
+
proc: proc { |key| Chef::Config[:knife][:xapi_mem] = key.to_s }
|
99
104
|
|
100
105
|
option :chef_node_name,
|
101
|
-
|
102
|
-
|
103
|
-
|
106
|
+
short: '-N NAME',
|
107
|
+
long: '--node-name NAME',
|
108
|
+
description: 'The Chef node name for your new node'
|
104
109
|
|
105
110
|
option :ssh_key_name,
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
111
|
+
short: '-S KEY',
|
112
|
+
long: '--ssh-key KEY',
|
113
|
+
proc: proc { |key| Chef::Config[:knife][:ssh_key_name] = key },
|
114
|
+
description: 'The SSH key id'
|
110
115
|
|
111
116
|
option :ssh_user,
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
|
117
|
+
short: '-x USERNAME',
|
118
|
+
long: '--ssh-user USERNAME',
|
119
|
+
description: 'The ssh username',
|
120
|
+
proc: proc { |key| Chef::Config[:knife][:ssh_user] = key }
|
116
121
|
|
117
122
|
option :ssh_password,
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
|
123
|
+
short: '-P PASSWORD',
|
124
|
+
long: '--ssh-password PASSWORD',
|
125
|
+
proc: proc { |key| Chef::Config[:knife][:ssh_password] = key },
|
126
|
+
description: 'The ssh password'
|
122
127
|
|
123
128
|
option :ssh_port,
|
124
|
-
|
125
|
-
|
126
|
-
|
129
|
+
short: '-p PORT',
|
130
|
+
long: '--ssh-port PORT',
|
131
|
+
description: 'The ssh port'
|
127
132
|
|
128
133
|
option :ping_timeout,
|
129
|
-
|
130
|
-
|
134
|
+
long: '--ping-timeout',
|
135
|
+
description: 'Seconds to timeout waiting for ip from guest'
|
131
136
|
|
132
137
|
option :bootstrap_version,
|
133
|
-
|
134
|
-
|
138
|
+
long: '--bootstrap-version VERSION',
|
139
|
+
description: 'The version of Chef to install'
|
135
140
|
|
136
141
|
option :bootstrap_template,
|
137
|
-
|
138
|
-
|
139
|
-
|
142
|
+
short: '-d Template Name',
|
143
|
+
long: '--bootstrap-template Template Name',
|
144
|
+
description: 'Bootstrap using a specific template'
|
140
145
|
|
141
146
|
option :template_file,
|
142
|
-
|
143
|
-
|
144
|
-
|
147
|
+
short: '-F FILEPATH',
|
148
|
+
long: '--template-file TEMPLATE',
|
149
|
+
description: 'Full path to location of template to use'
|
145
150
|
|
146
151
|
option :json_attributes,
|
147
|
-
|
148
|
-
|
149
|
-
|
150
|
-
|
152
|
+
short: '-j JSON_ATTRIBS',
|
153
|
+
long: '--json-attributes',
|
154
|
+
description: 'A JSON string to be added to the first run of chef-client',
|
155
|
+
proc: lambda { |o| JSON.parse(o) }
|
151
156
|
|
152
157
|
option :run_list,
|
153
|
-
|
154
|
-
|
155
|
-
|
156
|
-
|
158
|
+
short: '-r RUN_LIST',
|
159
|
+
long: '--run-list RUN_LIST',
|
160
|
+
description: 'Comma separated list of roles/recipes to apply',
|
161
|
+
proc: lambda { |o| o.split(/[\s,]+/) }
|
157
162
|
|
158
163
|
def tcp_test_ssh(hostname)
|
159
|
-
tcp_socket = TCPSocket.new(hostname, locate_config_value(:ssh_port)
|
164
|
+
tcp_socket = TCPSocket.new(hostname, locate_config_value(:ssh_port))
|
160
165
|
readable = IO.select([tcp_socket], nil, nil, 5)
|
161
166
|
if readable
|
162
167
|
Chef::Log.debug("sshd accepting connections on #{hostname}, banner is #{tcp_socket.gets}")
|
@@ -195,35 +200,53 @@ class Chef
|
|
195
200
|
end
|
196
201
|
end
|
197
202
|
|
198
|
-
def pingable?(guest_ip, timeout=5)
|
203
|
+
def pingable?(guest_ip, timeout = 5)
|
199
204
|
sleep(20)
|
200
205
|
system "ping -c 1 -t #{timeout} #{guest_ip} >/dev/null"
|
201
206
|
end
|
202
207
|
|
203
208
|
def get_guest_ip(vm_ref)
|
204
|
-
|
205
|
-
|
206
|
-
|
207
|
-
|
208
|
-
|
209
|
-
|
210
|
-
|
211
|
-
|
212
|
-
|
213
|
-
|
214
|
-
|
215
|
-
guest_ip = networks["0/ip"]
|
216
|
-
end
|
209
|
+
timeout(locate_config_value(:ping_timeout).to_i) do
|
210
|
+
ui.msg 'Waiting for guest ip address'
|
211
|
+
guest_ip = ''
|
212
|
+
while guest_ip.empty?
|
213
|
+
print('.')
|
214
|
+
sleep @initial_sleep_delay ||= 10
|
215
|
+
vgm = xapi.VM.get_guest_metrics(vm_ref)
|
216
|
+
next if 'OpaqueRef:NULL' == vgm
|
217
|
+
networks = xapi.VM_guest_metrics.get_networks(vgm)
|
218
|
+
if networks.key?('0/ip')
|
219
|
+
guest_ip = networks['0/ip']
|
217
220
|
end
|
218
|
-
puts "\n"
|
219
|
-
return guest_ip
|
220
221
|
end
|
221
|
-
|
222
|
-
|
222
|
+
puts "\n"
|
223
|
+
return guest_ip
|
223
224
|
end
|
225
|
+
rescue Timeout::Error
|
226
|
+
ui.msg 'Timeout waiting for XAPI to report IP address '
|
224
227
|
end
|
225
228
|
|
226
229
|
|
230
|
+
def resolve_sr_ref
|
231
|
+
ui_sr = locate_config_value(:xapi_sr)
|
232
|
+
sr_ref = nil
|
233
|
+
if locate_config_value(:xapi_sr)
|
234
|
+
if is_uuid?(ui_sr)
|
235
|
+
sr_ref = get_sr_by_uuid(ui_sr)
|
236
|
+
else
|
237
|
+
sr_ref = get_sr_by_name(ui_sr)
|
238
|
+
end
|
239
|
+
else
|
240
|
+
sr_ref = find_default_sr
|
241
|
+
end
|
242
|
+
|
243
|
+
if sr_ref.nil?
|
244
|
+
ui.error "SR specified not found or can't be used Aborting"
|
245
|
+
fail(vm_ref) if sr_ref.nil?
|
246
|
+
end
|
247
|
+
sr_ref
|
248
|
+
end
|
249
|
+
|
227
250
|
def run
|
228
251
|
server_name = @name_args[0]
|
229
252
|
domainname = locate_config_value(:domain)
|
@@ -234,11 +257,18 @@ class Chef
|
|
234
257
|
end
|
235
258
|
|
236
259
|
# get the template vm we are going to build from
|
237
|
-
template_ref = find_template(
|
238
|
-
|
239
|
-
|
240
|
-
|
241
|
-
|
260
|
+
template_ref = find_template(locate_config_value(:xapi_vm_template))
|
261
|
+
sr_ref = resolve_sr_ref
|
262
|
+
|
263
|
+
if locate_config_value(:xapi_sr)
|
264
|
+
Chef::Log.debug "Copying Guest from Template: #{h.color(template_ref, :bold, :cyan)}"
|
265
|
+
task = xapi.Async.VM.copy(template_ref, fqdn, sr_ref)
|
266
|
+
else
|
267
|
+
Chef::Log.debug "Cloning Guest from Template: #{h.color(template_ref, :bold, :cyan)}"
|
268
|
+
task = xapi.Async.VM.clone(template_ref, fqdn)
|
269
|
+
end
|
270
|
+
|
271
|
+
ui.msg 'Waiting on Template Clone'
|
242
272
|
vm_ref = get_task_ref(task)
|
243
273
|
|
244
274
|
Chef::Log.debug "New VM ref: #{vm_ref}"
|
@@ -251,23 +281,25 @@ class Chef
|
|
251
281
|
repo = locate_config_value(:install_repo)
|
252
282
|
|
253
283
|
# make sure we don't clobber existing params
|
254
|
-
other_config =
|
284
|
+
other_config = {}
|
255
285
|
record = xapi.VM.get_record(vm_ref)
|
256
|
-
if record.
|
257
|
-
other_config = record[
|
286
|
+
if record.key? 'other_config'
|
287
|
+
other_config = record['other_config']
|
258
288
|
end
|
259
|
-
other_config[
|
260
|
-
|
289
|
+
other_config['install-repository'] = repo
|
290
|
+
|
291
|
+
# remove any disk config/xml template might be trying to do (ubuntu)
|
261
292
|
other_config.delete_if {|k,v| k=="disks"}
|
293
|
+
|
262
294
|
Chef::Log.debug "Other_config: #{other_config.inspect}"
|
263
295
|
xapi.VM.set_other_config(vm_ref, other_config)
|
264
296
|
|
265
|
-
cpus = locate_config_value(
|
297
|
+
cpus = locate_config_value(:xapi_cpus).to_s
|
266
298
|
|
267
|
-
xapi.VM.set_VCPUs_max(
|
268
|
-
xapi.VM.set_VCPUs_at_startup(
|
299
|
+
xapi.VM.set_VCPUs_max(vm_ref, cpus)
|
300
|
+
xapi.VM.set_VCPUs_at_startup(vm_ref, cpus)
|
269
301
|
|
270
|
-
memory_size = input_to_bytes(
|
302
|
+
memory_size = input_to_bytes(locate_config_value(:xapi_mem)).to_s
|
271
303
|
# static-min <= dynamic-min = dynamic-max = static-max
|
272
304
|
xapi.VM.set_memory_limits(vm_ref, memory_size, memory_size, memory_size, memory_size)
|
273
305
|
|
@@ -283,100 +315,89 @@ class Chef
|
|
283
315
|
boot_args << " domain=#{domainname}" unless boot_args.match(/domain=.+\s?/)
|
284
316
|
boot_args << " dnsdomain=#{domainname}" unless boot_args.match(/dnsdomain=.+\s?/)
|
285
317
|
|
286
|
-
xapi.VM.set_PV_args(
|
318
|
+
xapi.VM.set_PV_args(vm_ref, boot_args)
|
287
319
|
|
288
320
|
# TODO: validate that the vm gets a network here
|
289
321
|
networks = @name_args[1..-1]
|
290
322
|
# if the user has provided networks
|
291
323
|
if networks.length >= 1
|
292
|
-
clear_vm_vifs(
|
324
|
+
clear_vm_vifs(xapi.VM.get_record(vm_ref))
|
293
325
|
networks.each_with_index do |net, index|
|
294
326
|
add_vif_by_name(vm_ref, index, net)
|
295
327
|
end
|
296
328
|
end
|
297
329
|
|
298
|
-
|
299
|
-
sr_ref = nil
|
300
|
-
if locate_config_value(:xapi_sr)
|
301
|
-
sr_ref = get_sr_by_name( locate_config_value(:xapi_sr) )
|
302
|
-
else
|
303
|
-
sr_ref = find_default_sr
|
304
|
-
end
|
305
|
-
|
306
|
-
if sr_ref.nil?
|
307
|
-
ui.error "SR specified not found or can't be used Aborting"
|
308
|
-
fail(vm_ref) if sr_ref.nil?
|
309
|
-
end
|
310
|
-
Chef::Log.debug "SR: #{h.color sr_ref, :cyan}"
|
311
|
-
|
330
|
+
Chef::Log.debug "SR: #{h.color sr_ref, :cyan}"
|
312
331
|
|
332
|
+
unless locate_config_value(:xapi_skip_disk)
|
313
333
|
disk_size = locate_config_value(:xapi_disk_size)
|
314
|
-
# setup disks
|
315
|
-
if disk_size
|
316
|
-
# when a template already has disks, we decide the position number based on it.
|
317
|
-
position = xapi.VM.get_VBDs(vm_ref).length
|
334
|
+
# setup disks
|
335
|
+
if !disk_size.nil? && disk_size.to_i > 0
|
336
|
+
# when a template already has disks, we decide the position number based on it.
|
337
|
+
position = xapi.VM.get_VBDs(vm_ref).length
|
318
338
|
|
319
339
|
# Create the VDI
|
320
|
-
vdi_ref = create_vdi("#{server_name}-root", sr_ref, locate_config_value(:xapi_disk_size)
|
340
|
+
vdi_ref = create_vdi("#{server_name}-root", sr_ref, locate_config_value(:xapi_disk_size))
|
321
341
|
fail(vm_ref) unless vdi_ref
|
322
342
|
|
323
343
|
# Attach the VDI to the VM
|
324
344
|
# if its position is 0 set it bootable
|
325
|
-
position == 0 ? bootable=true : bootable=false
|
345
|
+
position == 0 ? bootable = true : bootable = false
|
326
346
|
|
327
347
|
vbd_ref = create_vbd(vm_ref, vdi_ref, position, bootable)
|
328
348
|
fail(vm_ref) unless vbd_ref
|
329
349
|
end
|
330
350
|
end
|
331
351
|
|
332
|
-
ui.msg "Provisioning new Guest: #{h.color(fqdn, :bold, :cyan
|
333
|
-
ui.msg "Boot Args: #{h.color boot_args
|
334
|
-
ui.msg "Install Repo: #{ h.color(repo
|
335
|
-
ui.msg "Memory: #{ h.color(
|
336
|
-
ui.msg "CPUs: #{ h.color(
|
337
|
-
ui.msg "Disk: #{ h.color(
|
352
|
+
ui.msg "Provisioning new Guest: #{h.color(fqdn, :bold, :cyan)}"
|
353
|
+
ui.msg "Boot Args: #{h.color boot_args, :bold, :cyan}"
|
354
|
+
ui.msg "Install Repo: #{ h.color(repo, :bold, :cyan)}"
|
355
|
+
ui.msg "Memory: #{ h.color(locate_config_value(:xapi_mem).to_s, :bold, :cyan)}"
|
356
|
+
ui.msg "CPUs: #{ h.color(locate_config_value(:xapi_cpus).to_s, :bold, :cyan)}"
|
357
|
+
ui.msg "Disk: #{ h.color(disk_size.to_s, :bold, :cyan)}"
|
338
358
|
provisioned = xapi.VM.provision(vm_ref)
|
339
359
|
|
340
|
-
ui.msg "Starting new Guest #{h.color(
|
360
|
+
ui.msg "Starting new Guest #{h.color(provisioned, :cyan)} "
|
341
361
|
start(vm_ref)
|
342
362
|
|
343
363
|
exit 0 unless locate_config_value(:run_list)
|
344
|
-
rescue
|
345
|
-
ui.msg "#{h.color 'ERROR:'} #{h.color(
|
364
|
+
rescue => e
|
365
|
+
ui.msg "#{h.color 'ERROR:'} #{h.color(e.message, :red)}"
|
346
366
|
# have to use join here to pass a string to highline
|
347
|
-
puts
|
348
|
-
ui.msg "#{h.color(
|
349
|
-
|
367
|
+
puts 'Nested backtrace:'
|
368
|
+
ui.msg "#{h.color(e.backtrace.join("\n"), :yellow)}"
|
369
|
+
raise(vm_ref)
|
350
370
|
end
|
351
371
|
|
352
372
|
if locate_config_value(:run_list).empty?
|
353
|
-
unless
|
373
|
+
unless locate_config_value(:template_file) or
|
374
|
+
locate_config_value(:bootstrap_template) != 'chef-full'
|
354
375
|
exit 0
|
355
376
|
end
|
356
377
|
end
|
357
378
|
|
358
379
|
guest_addr = wait_for_guest_ip(vm_ref)
|
359
|
-
if guest_addr.nil?
|
360
|
-
ui.msg(
|
380
|
+
if guest_addr.nil? || guest_addr.empty?
|
381
|
+
ui.msg('ip seems wrong using host+domain name instead')
|
361
382
|
guest_addr = "#{server_name}.#{domainname}"
|
362
383
|
end
|
363
384
|
ui.msg "Trying to connect to guest @ #{guest_addr} "
|
364
385
|
|
365
386
|
begin
|
366
387
|
timeout(480) do
|
367
|
-
print(
|
388
|
+
print('.') until tcp_test_ssh(guest_addr) do
|
368
389
|
sleep @initial_sleep_delay ||= 10
|
369
|
-
ui.msg(
|
370
|
-
|
390
|
+
ui.msg("#{ h.color 'OK!', :green}")
|
391
|
+
end
|
371
392
|
end
|
372
393
|
rescue Timeout::Error
|
373
|
-
ui.msg
|
374
|
-
|
394
|
+
ui.msg 'Timeout trying to login Wont bootstrap'
|
395
|
+
raise
|
375
396
|
end
|
376
397
|
|
377
398
|
begin
|
378
399
|
bootstrap = Chef::Knife::Bootstrap.new
|
379
|
-
bootstrap.name_args = [
|
400
|
+
bootstrap.name_args = [guest_addr]
|
380
401
|
bootstrap.config[:run_list] = locate_config_value(:run_list)
|
381
402
|
bootstrap.config[:ssh_user] = locate_config_value(:ssh_user)
|
382
403
|
bootstrap.config[:ssh_port] = locate_config_value(:ssh_port)
|
@@ -393,15 +414,13 @@ class Chef
|
|
393
414
|
bootstrap.config[:run_list] = locate_config_value(:run_list)
|
394
415
|
|
395
416
|
bootstrap.run
|
396
|
-
rescue
|
397
|
-
ui.msg "#{h.color 'ERROR:'} #{h.color(
|
398
|
-
puts
|
399
|
-
ui.msg "#{h.color(
|
400
|
-
|
417
|
+
rescue => e
|
418
|
+
ui.msg "#{h.color 'ERROR:'} #{h.color(e.message, :red)}"
|
419
|
+
puts 'Nested backtrace:'
|
420
|
+
ui.msg "#{h.color(e.backtrace.join("\n"), :yellow)}"
|
421
|
+
raise
|
401
422
|
end
|
402
423
|
end
|
403
|
-
|
404
424
|
end
|
405
425
|
end
|
406
426
|
end
|
407
|
-
|