clc-chef-metal-vsphere 0.3.21 → 0.3.22
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
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 08c53e98dafb6ce8c5371123ad27bd2d2b729875
|
4
|
+
data.tar.gz: abfe98c1921d109da27ffdadd3eb903d3c4737dd
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 67119cb4e94ec7424dca182d2911b9631082112a7bdd9d766c6b8c295fca6d0d00743e0d79393aca06f87effd1e55b11a5536b5dac8f2cac10ecc55de76ccaa3
|
7
|
+
data.tar.gz: 9ce904837ced486e9969071d33c1010e4660a9133178f145683262d7199db2ac18797ca416ddb2525975aef3c0423c0543ce7b8b6ba5e9e0299f657387037699
|
@@ -201,26 +201,27 @@ module ChefMetalVsphere
|
|
201
201
|
|
202
202
|
transport = nil
|
203
203
|
if !ip_for(bootstrap_options, vm).nil?
|
204
|
+
vm_ip = ip_for(bootstrap_options, vm)
|
204
205
|
transport = transport_for(machine_spec, machine_options, vm)
|
205
206
|
end
|
206
207
|
|
207
208
|
if transport.nil? || !transport.available?
|
208
|
-
action_handler.report_progress "waiting for customizations to complete"
|
209
|
+
action_handler.report_progress "waiting for customizations to complete and find #{vm_ip}"
|
209
210
|
now = Time.now.utc
|
210
|
-
until (Time.now.utc - now) >
|
211
|
+
until (Time.now.utc - now) > 90 || (vm.guest.net.map { |net| net.ipAddress}.flatten).include?(vm_ip) do
|
211
212
|
print "-"
|
212
213
|
sleep 5
|
213
214
|
end
|
214
|
-
if vm.guest.
|
215
|
+
if !(vm.guest.net.map { |net| net.ipAddress}.flatten).include?(vm_ip)
|
215
216
|
action_handler.report_progress "rebooting..."
|
216
217
|
if vm.guest.toolsRunningStatus != "guestToolsRunning"
|
217
|
-
action_handler.report_progress "tools have stopped. current power state is #{vm.runtime.powerState} and tools state is #{vm.toolsRunningStatus}. powering up server..."
|
218
|
+
action_handler.report_progress "tools have stopped. current power state is #{vm.runtime.powerState} and tools state is #{vm.guest.toolsRunningStatus}. powering up server..."
|
218
219
|
start_vm(vm)
|
219
220
|
else
|
220
221
|
restart_server(action_handler, machine_spec, vm)
|
221
222
|
end
|
222
223
|
now = Time.now.utc
|
223
|
-
until (Time.now.utc - now) > 60 || (
|
224
|
+
until (Time.now.utc - now) > 60 || (vm.guest.net.map { |net| net.ipAddress}.flatten).include?(vm_ip) do
|
224
225
|
print "-"
|
225
226
|
sleep 5
|
226
227
|
end
|
@@ -91,7 +91,76 @@ module ChefMetalVsphere
|
|
91
91
|
vim.serviceInstance.find_datacenter(dc_name) or raise("vSphere Datacenter not found [#{datacenter}]")
|
92
92
|
end
|
93
93
|
|
94
|
+
def network_adapter_for(operation, network_name, network_label, device_key)
|
95
|
+
nic_backing_info = RbVmomi::VIM::VirtualEthernetCardNetworkBackingInfo(:deviceName => network_name)
|
96
|
+
connectable = RbVmomi::VIM::VirtualDeviceConnectInfo(
|
97
|
+
:allowGuestControl => true,
|
98
|
+
:connected => true,
|
99
|
+
:startConnected => true)
|
100
|
+
device = RbVmomi::VIM::VirtualVmxnet3(
|
101
|
+
:backing => nic_backing_info,
|
102
|
+
:deviceInfo => RbVmomi::VIM::Description(:label => network_label, :summary => network_name),
|
103
|
+
:key => device_key,
|
104
|
+
:connectable => connectable)
|
105
|
+
RbVmomi::VIM::VirtualDeviceConfigSpec(
|
106
|
+
:operation => operation,
|
107
|
+
:device => device)
|
108
|
+
end
|
109
|
+
|
110
|
+
def find_ethernet_cards_for(vm)
|
111
|
+
vm.config.hardware.device.select {|d| d.is_a?(RbVmomi::VIM::VirtualEthernetCard)}
|
112
|
+
end
|
113
|
+
|
94
114
|
def do_vm_clone(dc_name, vm_template, vm_name, options)
|
115
|
+
deviceAdditions = []
|
116
|
+
|
117
|
+
clone_spec = RbVmomi::VIM.VirtualMachineCloneSpec(
|
118
|
+
location: relocate_spec_for(dc_name, vm_template, options),
|
119
|
+
powerOn: false,
|
120
|
+
template: false,
|
121
|
+
config: RbVmomi::VIM.VirtualMachineConfigSpec(:deviceChange => Array.new)
|
122
|
+
)
|
123
|
+
|
124
|
+
clone_spec.customization = customization_options_from(vm_template, vm_name, options)
|
125
|
+
|
126
|
+
unless options[:annotation].to_s.nil?
|
127
|
+
clone_spec.config.annotation = options[:annotation]
|
128
|
+
end
|
129
|
+
|
130
|
+
unless options[:num_cpus].to_s.nil?
|
131
|
+
clone_spec.config.numCPUs = options[:num_cpus]
|
132
|
+
end
|
133
|
+
|
134
|
+
unless options[:memory_mb].to_s.nil?
|
135
|
+
clone_spec.config.memoryMB = options[:memory_mb]
|
136
|
+
end
|
137
|
+
|
138
|
+
unless options[:network_name].nil?
|
139
|
+
deviceAdditions, changes = network_device_changes(vm_template, options)
|
140
|
+
clone_spec.config.deviceChange = changes
|
141
|
+
end
|
142
|
+
|
143
|
+
vm_template.CloneVM_Task(
|
144
|
+
name: vm_name,
|
145
|
+
folder: find_folder(dc_name, options[:vm_folder]),
|
146
|
+
spec: clone_spec
|
147
|
+
).wait_for_completion
|
148
|
+
|
149
|
+
vm = find_vm(dc_name, options[:vm_folder], vm_name)
|
150
|
+
|
151
|
+
unless options[:additional_disk_size_gb].to_s.nil?
|
152
|
+
deviceAdditions.push(virtual_disk_for(vm, options))
|
153
|
+
end
|
154
|
+
|
155
|
+
unless deviceAdditions.count == 0
|
156
|
+
task = vm.ReconfigVM_Task(:spec => RbVmomi::VIM.VirtualMachineConfigSpec(:deviceChange => deviceAdditions))
|
157
|
+
task.wait_for_completion
|
158
|
+
end
|
159
|
+
|
160
|
+
vm
|
161
|
+
end
|
162
|
+
|
163
|
+
def relocate_spec_for(dc_name, vm_template, options)
|
95
164
|
datacenter = dc(dc_name)
|
96
165
|
if options.has_key?(:host)
|
97
166
|
host = find_host(datacenter, options[:host])
|
@@ -105,15 +174,66 @@ module ChefMetalVsphere
|
|
105
174
|
unless options[:datastore].to_s.empty?
|
106
175
|
rspec.datastore = find_datastore(datacenter, options[:datastore])
|
107
176
|
end
|
177
|
+
rspec
|
178
|
+
end
|
108
179
|
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
180
|
+
def virtual_disk_for(vm, options)
|
181
|
+
if options[:datastore].to_s.empty?
|
182
|
+
raise ":datastore must be specified when adding a disk to a cloned vm"
|
183
|
+
end
|
184
|
+
idx = vm.disks.count
|
185
|
+
RbVmomi::VIM::VirtualDeviceConfigSpec(
|
186
|
+
:operation => :add,
|
187
|
+
:fileOperation => :create,
|
188
|
+
:device => RbVmomi::VIM.VirtualDisk(
|
189
|
+
:key => idx,
|
190
|
+
:backing => RbVmomi::VIM.VirtualDiskFlatVer2BackingInfo(
|
191
|
+
:fileName => "[#{options[:datastore]}]",
|
192
|
+
:diskMode => 'persistent',
|
193
|
+
:thinProvisioned => true
|
194
|
+
),
|
195
|
+
:capacityInKB => options[:additional_disk_size_gb] * 1024 * 1024,
|
196
|
+
:controllerKey => 1000,
|
197
|
+
:unitNumber => idx
|
198
|
+
)
|
113
199
|
)
|
200
|
+
end
|
114
201
|
|
115
|
-
|
202
|
+
def network_device_changes(vm_template, options)
|
203
|
+
additions = []
|
204
|
+
changes = []
|
205
|
+
networks=options[:network_name]
|
206
|
+
if networks.kind_of?(String)
|
207
|
+
networks=[networks]
|
208
|
+
end
|
209
|
+
|
210
|
+
cards = find_ethernet_cards_for(vm_template)
|
116
211
|
|
212
|
+
key = 4000
|
213
|
+
networks.each_index do | i |
|
214
|
+
if card = cards.shift
|
215
|
+
key = card.key
|
216
|
+
operation = RbVmomi::VIM::VirtualDeviceConfigSpecOperation('edit')
|
217
|
+
puts "changing template nic for #{networks[i]}"
|
218
|
+
changes.push(
|
219
|
+
network_adapter_for(operation, networks[i], "Network Adapter #{i+1}", key))
|
220
|
+
else
|
221
|
+
key = key + 1
|
222
|
+
operation = RbVmomi::VIM::VirtualDeviceConfigSpecOperation('add')
|
223
|
+
puts "will be adding nic for #{networks[i]}"
|
224
|
+
additions.push(
|
225
|
+
network_adapter_for(operation, networks[i], "Network Adapter #{i+1}", key))
|
226
|
+
end
|
227
|
+
end
|
228
|
+
[additions, changes]
|
229
|
+
end
|
230
|
+
|
231
|
+
def find_datastore(dc, datastore_name)
|
232
|
+
baseEntity = dc.datastore
|
233
|
+
baseEntity.find { |f| f.info.name == datastore_name } or raise "no such datastore #{datastore_name}"
|
234
|
+
end
|
235
|
+
|
236
|
+
def customization_options_from(vm_template, vm_name, options)
|
117
237
|
if options.has_key?(:customization_spec)
|
118
238
|
if(options[:customization_spec].is_a?(Hash))
|
119
239
|
cust_options = options[:customization_spec]
|
@@ -130,38 +250,12 @@ module ChefMetalVsphere
|
|
130
250
|
cust_global_ip_settings = RbVmomi::VIM::CustomizationGlobalIPSettings.new
|
131
251
|
cust_global_ip_settings.dnsServerList = cust_ip_settings.dnsServerList
|
132
252
|
cust_global_ip_settings.dnsSuffixList = [cust_domain]
|
133
|
-
cust_hostname =
|
134
|
-
cust_hostname ||= RbVmomi::VIM::CustomizationFixedName.new(:name => vm_name)
|
253
|
+
cust_hostname = hostname_from(options[:customization_spec], vm_name)
|
135
254
|
cust_hwclockutc = cust_options[:hw_clock_utc]
|
136
255
|
cust_timezone = cust_options[:time_zone]
|
137
256
|
|
138
257
|
if vm_template.config.guestId.start_with?('win')
|
139
|
-
|
140
|
-
:commandList => [
|
141
|
-
'winrm set winrm/config/client/auth @{Basic="true"}',
|
142
|
-
'winrm set winrm/config/service/auth @{Basic="true"}',
|
143
|
-
'winrm set winrm/config/service @{AllowUnencrypted="true"}',
|
144
|
-
'shutdown -l'])
|
145
|
-
cust_id = RbVmomi::VIM::CustomizationIdentification.new(
|
146
|
-
:joinWorkgroup => 'WORKGROUP')
|
147
|
-
cust_password = RbVmomi::VIM::CustomizationPassword(
|
148
|
-
:plainText => true,
|
149
|
-
:value => options[:ssh][:password])
|
150
|
-
cust_gui_unattended = RbVmomi::VIM::CustomizationGuiUnattended.new(
|
151
|
-
:autoLogon => true,
|
152
|
-
:autoLogonCount => 1,
|
153
|
-
:password => cust_password,
|
154
|
-
:timeZone => cust_options[:win_time_zone])
|
155
|
-
cust_userdata = RbVmomi::VIM::CustomizationUserData.new(
|
156
|
-
:computerName => cust_hostname,
|
157
|
-
:fullName => cust_options[:org_name],
|
158
|
-
:orgName => cust_options[:org_name],
|
159
|
-
:productId => cust_options[:product_id])
|
160
|
-
cust_prep = RbVmomi::VIM::CustomizationSysprep.new(
|
161
|
-
:guiRunOnce => cust_runonce,
|
162
|
-
:identification => cust_id,
|
163
|
-
:guiUnattended => cust_gui_unattended,
|
164
|
-
:userData => cust_userdata)
|
258
|
+
cust_prep = windows_prep_for(options, vm_name)
|
165
259
|
else
|
166
260
|
cust_prep = RbVmomi::VIM::CustomizationLinuxPrep.new(
|
167
261
|
:domain => cust_domain,
|
@@ -170,84 +264,52 @@ module ChefMetalVsphere
|
|
170
264
|
:timeZone => cust_timezone)
|
171
265
|
end
|
172
266
|
cust_adapter_mapping = [RbVmomi::VIM::CustomizationAdapterMapping.new(:adapter => cust_ip_settings)]
|
173
|
-
|
267
|
+
RbVmomi::VIM::CustomizationSpec.new(
|
174
268
|
:identity => cust_prep,
|
175
269
|
:globalIPSettings => cust_global_ip_settings,
|
176
270
|
:nicSettingMap => cust_adapter_mapping)
|
177
271
|
else
|
178
|
-
|
272
|
+
find_customization_spec(options[:customization_spec])
|
179
273
|
end
|
180
|
-
clone_spec.customization = cust_spec
|
181
|
-
end
|
182
|
-
|
183
|
-
unless options[:annotation].to_s.nil?
|
184
|
-
clone_spec.config.annotation = options[:annotation]
|
185
274
|
end
|
186
|
-
|
187
|
-
unless options[:num_cpus].to_s.nil?
|
188
|
-
clone_spec.config.numCPUs = options[:num_cpus]
|
189
|
-
end
|
190
|
-
|
191
|
-
unless options[:memory_mb].to_s.nil?
|
192
|
-
clone_spec.config.memoryMB = options[:memory_mb]
|
193
|
-
end
|
194
|
-
|
195
|
-
unless options[:network_name].to_s.nil?
|
196
|
-
config_spec_operation = RbVmomi::VIM::VirtualDeviceConfigSpecOperation('edit')
|
197
|
-
nic_backing_info = RbVmomi::VIM::VirtualEthernetCardNetworkBackingInfo(:deviceName => options[:network_name])
|
198
|
-
connectable = RbVmomi::VIM::VirtualDeviceConnectInfo(
|
199
|
-
:allowGuestControl => true,
|
200
|
-
:connected => true,
|
201
|
-
:startConnected => true)
|
202
|
-
device = RbVmomi::VIM::VirtualVmxnet3(
|
203
|
-
:backing => nic_backing_info,
|
204
|
-
:deviceInfo => RbVmomi::VIM::Description(:label => "Network adapter 1", :summary => options[:network_name]),
|
205
|
-
:key => 4000,
|
206
|
-
:connectable => connectable)
|
207
|
-
device_spec = RbVmomi::VIM::VirtualDeviceConfigSpec(
|
208
|
-
:operation => config_spec_operation,
|
209
|
-
:device => device)
|
210
|
-
|
211
|
-
clone_spec.config.deviceChange.push device_spec
|
212
|
-
end
|
213
|
-
|
214
|
-
vm_template.CloneVM_Task(
|
215
|
-
name: vm_name,
|
216
|
-
folder: find_folder(dc_name, options[:vm_folder]),
|
217
|
-
spec: clone_spec
|
218
|
-
).wait_for_completion
|
219
|
-
|
220
|
-
vm = find_vm(dc_name, options[:vm_folder], vm_name)
|
221
|
-
|
222
|
-
unless options[:additional_disk_size_gb].to_s.nil?
|
223
|
-
if options[:datastore].to_s.empty?
|
224
|
-
raise ":datastore must be specified when adding a disk to a cloned vm"
|
225
|
-
end
|
226
|
-
idx = vm.disks.count
|
227
|
-
task = vm.ReconfigVM_Task(:spec => RbVmomi::VIM.VirtualMachineConfigSpec(:deviceChange =>[RbVmomi::VIM::VirtualDeviceConfigSpec(
|
228
|
-
:operation => :add,
|
229
|
-
:fileOperation => :create,
|
230
|
-
:device => RbVmomi::VIM.VirtualDisk(
|
231
|
-
:key => idx,
|
232
|
-
:backing => RbVmomi::VIM.VirtualDiskFlatVer2BackingInfo(
|
233
|
-
:fileName => "[#{options[:datastore]}]",
|
234
|
-
:diskMode => 'persistent',
|
235
|
-
:thinProvisioned => true
|
236
|
-
),
|
237
|
-
:capacityInKB => options[:additional_disk_size_gb] * 1024 * 1024,
|
238
|
-
:controllerKey => 1000,
|
239
|
-
:unitNumber => idx
|
240
|
-
)
|
241
|
-
)]))
|
242
|
-
task.wait_for_completion
|
243
275
|
end
|
244
276
|
|
245
|
-
|
277
|
+
def windows_prep_for(options, vm_name)
|
278
|
+
cust_options = options[:customization_spec]
|
279
|
+
cust_runonce = RbVmomi::VIM::CustomizationGuiRunOnce.new(
|
280
|
+
:commandList => [
|
281
|
+
'winrm set winrm/config/client/auth @{Basic="true"}',
|
282
|
+
'winrm set winrm/config/service/auth @{Basic="true"}',
|
283
|
+
'winrm set winrm/config/service @{AllowUnencrypted="true"}',
|
284
|
+
'shutdown -l'])
|
285
|
+
cust_id = RbVmomi::VIM::CustomizationIdentification.new(
|
286
|
+
:joinWorkgroup => 'WORKGROUP')
|
287
|
+
cust_password = RbVmomi::VIM::CustomizationPassword(
|
288
|
+
:plainText => true,
|
289
|
+
:value => options[:ssh][:password])
|
290
|
+
cust_gui_unattended = RbVmomi::VIM::CustomizationGuiUnattended.new(
|
291
|
+
:autoLogon => true,
|
292
|
+
:autoLogonCount => 1,
|
293
|
+
:password => cust_password,
|
294
|
+
:timeZone => cust_options[:win_time_zone])
|
295
|
+
cust_userdata = RbVmomi::VIM::CustomizationUserData.new(
|
296
|
+
:computerName => hostname_from(cust_options, vm_name),
|
297
|
+
:fullName => cust_options[:org_name],
|
298
|
+
:orgName => cust_options[:org_name],
|
299
|
+
:productId => cust_options[:product_id])
|
300
|
+
RbVmomi::VIM::CustomizationSysprep.new(
|
301
|
+
:guiRunOnce => cust_runonce,
|
302
|
+
:identification => cust_id,
|
303
|
+
:guiUnattended => cust_gui_unattended,
|
304
|
+
:userData => cust_userdata)
|
246
305
|
end
|
247
306
|
|
248
|
-
def
|
249
|
-
|
250
|
-
|
307
|
+
def hostname_from(options, vm_name)
|
308
|
+
if options.key?(:hostname)
|
309
|
+
RbVmomi::VIM::CustomizationFixedName.new(:name => options[:hostname])
|
310
|
+
else
|
311
|
+
RbVmomi::VIM::CustomizationFixedName.new(:name => vm_name)
|
312
|
+
end
|
251
313
|
end
|
252
314
|
|
253
315
|
def find_host(dc, host_name)
|
@@ -14,7 +14,7 @@
|
|
14
14
|
:vm_folder => 'DLAB',
|
15
15
|
:datastore => 'QA1-2-SFIRE-01',
|
16
16
|
:num_cpus => 2,
|
17
|
-
:network_name => 'vlan_20_172.21.20',
|
17
|
+
:network_name => ['vlan_20_172.21.20', 'vlan10_172.21.10-vm'],
|
18
18
|
:memory_mb => 4096,
|
19
19
|
:resource_pool => 'CLSTR02/DLAB',
|
20
20
|
:additional_disk_size_gb => 50,
|
@@ -27,6 +27,7 @@
|
|
27
27
|
:convergence_options => {},
|
28
28
|
:customization_spec => {
|
29
29
|
:ipsettings => {
|
30
|
+
:ip => '172.21.20.190',
|
30
31
|
:subnetMask => '255.255.255.0',
|
31
32
|
:gateway => ["172.21.20.1"],
|
32
33
|
:dnsServerList => ["172.17.1.26","172.17.1.27"]
|
@@ -39,7 +39,6 @@ describe "vsphere_driver" do
|
|
39
39
|
|
40
40
|
before :all do
|
41
41
|
@vm_name = "cmvd-test-#{SecureRandom.hex}"
|
42
|
-
#@vm_name = "cmvd-test-ee744c7bd5588f743c175dfc4100f172"
|
43
42
|
@metal_config = eval File.read(File.expand_path('../config.rb', __FILE__))
|
44
43
|
Cheffish.honor_local_mode do
|
45
44
|
chef_server = Cheffish.default_chef_server(@metal_config)
|
@@ -74,8 +73,9 @@ describe "vsphere_driver" do
|
|
74
73
|
it "has the correct amount of memory" do
|
75
74
|
expect(@vm.config.hardware.memoryMB).to eq(@metal_config[:machine_options][:bootstrap_options][:memory_mb])
|
76
75
|
end
|
77
|
-
it "is on the correct
|
78
|
-
expect(@vm.network
|
76
|
+
it "is on the correct networks" do
|
77
|
+
expect(@vm.network.map {|n| n.name}).to include(@metal_config[:machine_options][:bootstrap_options][:network_name][0])
|
78
|
+
expect(@vm.network.map {|n| n.name}).to include(@metal_config[:machine_options][:bootstrap_options][:network_name][1])
|
79
79
|
end
|
80
80
|
it "is on the correct datastore" do
|
81
81
|
expect(@vm.datastore[0].name).to eq(@metal_config[:machine_options][:bootstrap_options][:datastore])
|
@@ -105,12 +105,12 @@ describe "vsphere_driver" do
|
|
105
105
|
it "has the correct IP address" do
|
106
106
|
if @vm.guest.toolsRunningStatus != "guestToolsRunning"
|
107
107
|
now = Time.now.utc
|
108
|
-
until (Time.now.utc - now) > 30 || (@vm.guest.toolsRunningStatus == "guestToolsRunning" &&
|
108
|
+
until (Time.now.utc - now) > 30 || (@vm.guest.toolsRunningStatus == "guestToolsRunning" && @vm.guest.net.count == 2 && @vm.guest.net[1].ipAddress[1] == @metal_config[:machine_options][:bootstrap_options][:customization_spec][:ipsettings][:ip]) do
|
109
109
|
print "."
|
110
110
|
sleep 5
|
111
111
|
end
|
112
112
|
end
|
113
|
-
expect(@vm.guest.ipAddress).to eq(@metal_config[:machine_options][:bootstrap_options][:customization_spec][:ipsettings][:ip])
|
113
|
+
expect(@vm.guest.net[1].ipAddress[1]).to eq(@metal_config[:machine_options][:bootstrap_options][:customization_spec][:ipsettings][:ip])
|
114
114
|
end
|
115
115
|
end
|
116
116
|
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: clc-chef-metal-vsphere
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.3.
|
4
|
+
version: 0.3.22
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- CenturyLink Cloud
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2014-07-
|
11
|
+
date: 2014-07-16 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: chef
|