knife-xapi 0.5.4 → 0.6.0
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.
- 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
|
-
|