knife-clc 0.0.1 → 0.0.2.pre
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 +4 -4
- data/README.md +62 -40
- data/lib/chef/knife/clc_ip_create.rb +1 -0
- data/lib/chef/knife/clc_server_create.rb +25 -528
- data/lib/clc/client.rb +1 -1
- data/lib/knife-clc/async/config_options.rb +18 -0
- data/lib/knife-clc/async.rb +11 -0
- data/lib/knife-clc/base/config_options.rb +26 -0
- data/lib/knife-clc/base.rb +56 -0
- data/lib/knife-clc/bootstrap/bootstrapper.rb +92 -0
- data/lib/knife-clc/bootstrap/config_options.rb +66 -0
- data/lib/knife-clc/bootstrap/connectivity_helper.rb +39 -0
- data/lib/knife-clc/bootstrap/methods/async_linux_package.rb +41 -0
- data/lib/knife-clc/bootstrap/methods/async_windows_package.rb +69 -0
- data/lib/knife-clc/bootstrap/methods/sync_linux_ssh.rb +67 -0
- data/lib/knife-clc/bootstrap/methods/sync_windows_winrm.rb +61 -0
- data/lib/knife-clc/bootstrap/subcommand_loader.rb +18 -0
- data/lib/knife-clc/bootstrap/validator.rb +149 -0
- data/lib/knife-clc/bootstrap.rb +20 -0
- data/lib/knife-clc/cloud_extensions/cloud_adapter.rb +35 -0
- data/lib/knife-clc/cloud_extensions.rb +11 -0
- data/lib/knife-clc/ip_assignment/config_options.rb +29 -0
- data/lib/knife-clc/ip_assignment/ip_assigner.rb +41 -0
- data/lib/knife-clc/ip_assignment/mapper.rb +20 -0
- data/lib/knife-clc/ip_assignment/validator.rb +74 -0
- data/lib/knife-clc/ip_assignment.rb +20 -0
- data/lib/knife-clc/server_launch/config_options.rb +145 -0
- data/lib/knife-clc/server_launch/mapper.rb +40 -0
- data/lib/knife-clc/server_launch/server_launcher.rb +40 -0
- data/lib/knife-clc/server_launch/validator.rb +100 -0
- data/lib/knife-clc/server_launch.rb +21 -0
- data/lib/knife-clc/version.rb +1 -1
- metadata +44 -4
@@ -1,440 +1,28 @@
|
|
1
|
-
require 'chef/knife/clc_base'
|
2
1
|
require 'chef/knife/clc_server_show'
|
3
|
-
|
4
|
-
require '
|
2
|
+
|
3
|
+
require 'knife-clc/base'
|
4
|
+
require 'knife-clc/async'
|
5
|
+
require 'knife-clc/cloud_extensions'
|
6
|
+
require 'knife-clc/server_launch'
|
7
|
+
require 'knife-clc/bootstrap'
|
8
|
+
require 'knife-clc/ip_assignment'
|
5
9
|
|
6
10
|
class Chef
|
7
11
|
class Knife
|
8
12
|
class ClcServerCreate < Knife
|
9
|
-
include Knife::
|
13
|
+
include ::Knife::Clc::Base
|
14
|
+
include ::Knife::Clc::Async
|
15
|
+
include ::Knife::Clc::CloudExtensions
|
16
|
+
include ::Knife::Clc::ServerLaunch
|
17
|
+
include ::Knife::Clc::IpAssignment
|
18
|
+
include ::Knife::Clc::Bootstrap
|
10
19
|
|
11
20
|
banner 'knife clc server create (options)'
|
12
21
|
|
13
|
-
option :clc_name,
|
14
|
-
:long => '--name NAME',
|
15
|
-
:description => 'Name of the server to create',
|
16
|
-
:on => :head
|
17
|
-
|
18
|
-
option :clc_description,
|
19
|
-
:long => '--description DESCRIPTION',
|
20
|
-
:description => 'User-defined description of this server',
|
21
|
-
:on => :head
|
22
|
-
|
23
|
-
option :clc_group,
|
24
|
-
:long => '--group ID',
|
25
|
-
:description => 'ID of the parent group',
|
26
|
-
:on => :head
|
27
|
-
|
28
|
-
option :clc_source_server,
|
29
|
-
:long => '--source-server ID',
|
30
|
-
:description => 'ID of the server to use a source. May be the ID of a template, or when cloning, an existing server ID',
|
31
|
-
:on => :head
|
32
|
-
|
33
|
-
option :clc_managed,
|
34
|
-
:long => '--managed',
|
35
|
-
:boolean => true,
|
36
|
-
:description => 'Whether to create the server as managed or not',
|
37
|
-
:on => :head
|
38
|
-
|
39
|
-
option :clc_managed_backup,
|
40
|
-
:long => '--managed-backup',
|
41
|
-
:boolean => true,
|
42
|
-
:description => 'Whether to add managed backup to the server',
|
43
|
-
:on => :head
|
44
|
-
|
45
|
-
option :clc_primary_dns,
|
46
|
-
:long => '--primary-dns ADDRESS',
|
47
|
-
:description => 'Primary DNS to set on the server',
|
48
|
-
:on => :head
|
49
|
-
|
50
|
-
option :clc_secondary_dns,
|
51
|
-
:long => '--secondary-dns ADDRESS',
|
52
|
-
:description => 'Secondary DNS to set on the server',
|
53
|
-
:on => :head
|
54
|
-
|
55
|
-
option :clc_network,
|
56
|
-
:long => '--network ID',
|
57
|
-
:description => 'ID of the network to which to deploy the server',
|
58
|
-
:on => :head
|
59
|
-
|
60
|
-
option :clc_ip,
|
61
|
-
:long => '--ip ADDRESS',
|
62
|
-
:description => 'IP address to assign to the server',
|
63
|
-
:on => :head
|
64
|
-
|
65
|
-
option :clc_server_password,
|
66
|
-
:long => '--server-password PASSWORD',
|
67
|
-
:description => 'Password of administrator or root user on server',
|
68
|
-
:on => :head
|
69
|
-
|
70
|
-
option :clc_source_server_password,
|
71
|
-
:long => '--source-server-password PASSWORD',
|
72
|
-
:description => 'Password of the source server, used only when creating a clone from an existing server',
|
73
|
-
:on => :head
|
74
|
-
|
75
|
-
option :clc_cpu,
|
76
|
-
:long => '--cpu COUNT',
|
77
|
-
:description => 'Number of processors to configure the server with',
|
78
|
-
:on => :head
|
79
|
-
|
80
|
-
option :clc_cpu_autoscale_policy,
|
81
|
-
:long => '--cpu-autoscale-policy ID',
|
82
|
-
:description => 'ID of the vertical CPU Autoscale policy to associate the server with',
|
83
|
-
:on => :head
|
84
|
-
|
85
|
-
option :clc_memory,
|
86
|
-
:long => '--memory COUNT',
|
87
|
-
:description => 'Number of GB of memory to configure the server with',
|
88
|
-
:on => :head
|
89
|
-
|
90
|
-
option :clc_type,
|
91
|
-
:long => '--type TYPE',
|
92
|
-
:description => 'Whether to create a standard or hyperscale server',
|
93
|
-
:on => :head
|
94
|
-
|
95
|
-
option :clc_storage_type,
|
96
|
-
:long => '--storage-type TYPE',
|
97
|
-
:description => 'For standard servers, whether to use standard or premium storage',
|
98
|
-
:on => :head
|
99
|
-
|
100
|
-
option :clc_anti_affinity_policy,
|
101
|
-
:long => '--anti-affinity-policy ID',
|
102
|
-
:description => 'ID of the Anti-Affinity policy to associate the server with',
|
103
|
-
:on => :head
|
104
|
-
|
105
|
-
option :clc_custom_fields,
|
106
|
-
:long => '--custom-field KEY=VALUE',
|
107
|
-
:description => 'Custom field key-value pair',
|
108
|
-
:on => :head,
|
109
|
-
:proc => ->(param) do
|
110
|
-
Chef::Config[:knife][:clc_custom_fields] ||= []
|
111
|
-
Chef::Config[:knife][:clc_custom_fields] << param
|
112
|
-
end
|
113
|
-
|
114
|
-
option :clc_disks,
|
115
|
-
:long => '--disk PATH,SIZE,TYPE',
|
116
|
-
:description => 'Configuration for an additional server disk',
|
117
|
-
:on => :head,
|
118
|
-
:proc => ->(param) do
|
119
|
-
Chef::Config[:knife][:clc_disks] ||= []
|
120
|
-
Chef::Config[:knife][:clc_disks] << param
|
121
|
-
end
|
122
|
-
|
123
|
-
option :clc_ttl,
|
124
|
-
:long => '--ttl DATETIME',
|
125
|
-
:description => 'Date/time that the server should be deleted',
|
126
|
-
:on => :head
|
127
|
-
|
128
|
-
option :clc_packages,
|
129
|
-
:long => '--package ID,KEY_1=VALUE[,KEY_2=VALUE]',
|
130
|
-
:description => 'Package to run on the server after it has been built',
|
131
|
-
:on => :head,
|
132
|
-
:proc => ->(param) do
|
133
|
-
Chef::Config[:knife][:clc_packages] ||= []
|
134
|
-
Chef::Config[:knife][:clc_packages] << param
|
135
|
-
end
|
136
|
-
|
137
|
-
option :clc_configuration,
|
138
|
-
:long => '--configuration ID',
|
139
|
-
:description => 'Specifies the identifier for the specific configuration type of bare metal server to deploy',
|
140
|
-
:on => :head
|
141
|
-
|
142
|
-
option :clc_os_type,
|
143
|
-
:long => '--os-type TYPE',
|
144
|
-
:description => 'Specifies the OS to provision with the bare metal server',
|
145
|
-
:on => :head
|
146
|
-
|
147
|
-
option :clc_allowed_protocols,
|
148
|
-
:long => '--allow PROTOCOL:FROM[-TO]',
|
149
|
-
:description => 'Assigns public IP with permissions for specified protocol',
|
150
|
-
:on => :head,
|
151
|
-
:proc => ->(param) do
|
152
|
-
Chef::Config[:knife][:clc_allowed_protocols] ||= []
|
153
|
-
Chef::Config[:knife][:clc_allowed_protocols] << param
|
154
|
-
end
|
155
|
-
|
156
|
-
option :clc_sources,
|
157
|
-
:long => '--source CIDR',
|
158
|
-
:description => 'The source IP address range allowed to access the new public IP address',
|
159
|
-
:on => :head,
|
160
|
-
:proc => ->(param) do
|
161
|
-
Chef::Config[:knife][:clc_sources] ||= []
|
162
|
-
Chef::Config[:knife][:clc_sources] << param
|
163
|
-
end
|
164
|
-
|
165
|
-
option :clc_wait,
|
166
|
-
:long => '--wait',
|
167
|
-
:description => 'Wait for operation completion',
|
168
|
-
:boolean => true,
|
169
|
-
:default => false,
|
170
|
-
:on => :head
|
171
|
-
|
172
|
-
option :clc_bootstrap,
|
173
|
-
:long => '--bootstrap',
|
174
|
-
:description => 'Bootstrap launched server using standard `knife bootstrap` command',
|
175
|
-
:boolean => true,
|
176
|
-
:default => false,
|
177
|
-
:on => :head
|
178
|
-
|
179
|
-
option :clc_bootstrap_private,
|
180
|
-
:long => '--bootstrap-private',
|
181
|
-
:description => 'Bootstrap from private network. Requires client or SSH gateway to have an access to private network of the server',
|
182
|
-
:boolean => true,
|
183
|
-
:default => false,
|
184
|
-
:on => :head
|
185
|
-
|
186
22
|
def parse_and_validate_parameters
|
187
|
-
|
188
|
-
|
189
|
-
|
190
|
-
|
191
|
-
unless config[:clc_group]
|
192
|
-
errors << 'Group ID is required'
|
193
|
-
end
|
194
|
-
|
195
|
-
unless config[:clc_source_server]
|
196
|
-
errors << 'Source server ID is required'
|
197
|
-
end
|
198
|
-
|
199
|
-
unless config[:clc_cpu]
|
200
|
-
errors << 'Number of CPUs is required'
|
201
|
-
end
|
202
|
-
|
203
|
-
unless config[:clc_memory]
|
204
|
-
errors << 'Number of memory GBs is required'
|
205
|
-
end
|
206
|
-
|
207
|
-
unless config[:clc_type]
|
208
|
-
errors << 'Type is required'
|
209
|
-
end
|
210
|
-
|
211
|
-
custom_fields = config[:clc_custom_fields]
|
212
|
-
if custom_fields && custom_fields.any?
|
213
|
-
parse_custom_fields(custom_fields)
|
214
|
-
end
|
215
|
-
|
216
|
-
disks = config[:clc_disks]
|
217
|
-
if disks && disks.any?
|
218
|
-
parse_disks(disks)
|
219
|
-
end
|
220
|
-
|
221
|
-
packages = config[:clc_packages]
|
222
|
-
if packages && packages.any?
|
223
|
-
parse_packages(packages)
|
224
|
-
end
|
225
|
-
|
226
|
-
permissions = config[:clc_allowed_protocols]
|
227
|
-
if permissions && permissions.any?
|
228
|
-
parse_protocol_permissions(permissions)
|
229
|
-
end
|
230
|
-
|
231
|
-
sources = config[:clc_sources]
|
232
|
-
if sources && sources.any?
|
233
|
-
parse_sources(sources)
|
234
|
-
end
|
235
|
-
|
236
|
-
bootstrap = config[:clc_bootstrap]
|
237
|
-
if bootstrap
|
238
|
-
check_chef_server_connectivity
|
239
|
-
check_server_platform
|
240
|
-
if config[:clc_wait]
|
241
|
-
check_bootstrap_connectivity_params
|
242
|
-
else
|
243
|
-
check_bootstrap_node_connectivity_params
|
244
|
-
end
|
245
|
-
end
|
246
|
-
end
|
247
|
-
|
248
|
-
def check_chef_server_connectivity
|
249
|
-
Chef::Node.list
|
250
|
-
rescue Exception => e
|
251
|
-
errors << 'Could not connect to Chef Server: ' + e.message
|
252
|
-
end
|
253
|
-
|
254
|
-
def check_bootstrap_node_connectivity_params
|
255
|
-
command = bootstrap_command
|
256
|
-
# Chef 12.0 does not have bootstrap context accessor and validates key by itself
|
257
|
-
return unless command.respond_to?(:bootstrap_context)
|
258
|
-
|
259
|
-
context = command.bootstrap_context
|
260
|
-
unless context.validation_key
|
261
|
-
errors << "Validatorless async bootstrap is not supported. Validation key #{Chef::Config[:validation_key]} not found"
|
262
|
-
end
|
263
|
-
end
|
264
|
-
|
265
|
-
def check_bootstrap_connectivity_params
|
266
|
-
return if indirect_bootstrap?
|
267
|
-
|
268
|
-
if public_ip_requested?
|
269
|
-
errors << 'Bootstrapping requires SSH access to the server' unless ssh_access_requested?
|
270
|
-
else
|
271
|
-
errors << 'Bootstrapping requires public IP access to the server. Ignore this check with --bootstrap-private'
|
272
|
-
end
|
273
|
-
end
|
274
|
-
|
275
|
-
def check_server_platform
|
276
|
-
return unless config[:clc_group] && config[:clc_source_server]
|
277
|
-
|
278
|
-
if template = find_source_template
|
279
|
-
windows_platform = template['osType'] =~ /windows/
|
280
|
-
elsif server = find_source_server
|
281
|
-
windows_platform = server['os'] =~ /windows/
|
282
|
-
end
|
283
|
-
|
284
|
-
if windows_platform
|
285
|
-
errors << 'Bootstrapping is available for Linux platform only'
|
286
|
-
end
|
287
|
-
rescue Clc::CloudExceptions::Error => e
|
288
|
-
errors << "Could not derive server bootstrap platform: #{e.message}"
|
289
|
-
end
|
290
|
-
|
291
|
-
def find_source_template
|
292
|
-
group = connection.show_group(config[:clc_group])
|
293
|
-
datacenter_id = group['locationId']
|
294
|
-
connection.list_templates(datacenter_id).find do |template|
|
295
|
-
template['name'] == config[:clc_source_server]
|
296
|
-
end
|
297
|
-
end
|
298
|
-
|
299
|
-
def find_source_server
|
300
|
-
connection.show_server(config[:clc_source_server])
|
301
|
-
end
|
302
|
-
|
303
|
-
def public_ip_requested?
|
304
|
-
config[:clc_allowed_protocols] && config[:clc_allowed_protocols].any?
|
305
|
-
end
|
306
|
-
|
307
|
-
def ssh_access_requested?
|
308
|
-
ssh_port = requested_ssh_port
|
309
|
-
|
310
|
-
config[:clc_allowed_protocols].find do |permission|
|
311
|
-
protocol, from, to = permission.values_at('protocol', 'port', 'portTo')
|
312
|
-
next unless protocol == 'tcp'
|
313
|
-
next unless from
|
314
|
-
|
315
|
-
to ||= from
|
316
|
-
|
317
|
-
Range.new(from, to).include? ssh_port
|
318
|
-
end
|
319
|
-
end
|
320
|
-
|
321
|
-
def requested_ssh_port
|
322
|
-
(config[:ssh_port] && Integer(config[:ssh_port])) || 22
|
323
|
-
end
|
324
|
-
|
325
|
-
def parse_custom_fields(custom_fields)
|
326
|
-
custom_fields.map! do |param|
|
327
|
-
key, value = param.split('=', 2)
|
328
|
-
|
329
|
-
unless key && value
|
330
|
-
errors << "Custom field definition #{param} is malformed"
|
331
|
-
next
|
332
|
-
end
|
333
|
-
|
334
|
-
{ 'id' => key, 'value' => value }
|
335
|
-
end
|
336
|
-
end
|
337
|
-
|
338
|
-
def parse_disks(disks)
|
339
|
-
disks.map! do |param|
|
340
|
-
path, size, type = param.split(',', 3)
|
341
|
-
|
342
|
-
unless path && size && type
|
343
|
-
errors << "Disk definition #{param} is malformed"
|
344
|
-
end
|
345
|
-
|
346
|
-
{ 'path' => path, 'sizeGB' => size, 'type' => type }
|
347
|
-
end
|
348
|
-
end
|
349
|
-
|
350
|
-
def parse_packages(packages)
|
351
|
-
packages.map! do |param|
|
352
|
-
begin
|
353
|
-
id, package_params = param.split(',', 2)
|
354
|
-
package_params = package_params.split(',').map { |pair| Hash[*pair.split('=', 2)] }
|
355
|
-
{ 'packageId' => id, 'parameters' => package_params }
|
356
|
-
rescue Exception => e
|
357
|
-
errors << "Package definition #{param} is malformed"
|
358
|
-
end
|
359
|
-
end
|
360
|
-
end
|
361
|
-
|
362
|
-
def parse_protocol_permissions(permissions)
|
363
|
-
permissions.map! do |param|
|
364
|
-
protocol, port_range = param.split(':', 2)
|
365
|
-
|
366
|
-
case protocol.downcase
|
367
|
-
when 'ssh', 'sftp' then { 'protocol' => 'tcp', 'port' => 22 }
|
368
|
-
when 'rdp' then { 'protocol' => 'tcp', 'port' => 3389 }
|
369
|
-
when 'icmp' then { 'protocol' => 'icmp' }
|
370
|
-
when 'http' then [{ 'protocol' => 'tcp', 'port' => 80 }, { 'protocol' => 'tcp', 'port' => 8080 }]
|
371
|
-
when 'https' then { 'protocol' => 'tcp', 'port' => 443 }
|
372
|
-
when 'ftp' then { 'protocol' => 'tcp', 'port' => 21 }
|
373
|
-
when 'ftps' then { 'protocol' => 'tcp', 'port' => 990 }
|
374
|
-
when 'udp', 'tcp'
|
375
|
-
unless port_range
|
376
|
-
errors << "No ports specified for #{param}"
|
377
|
-
else
|
378
|
-
ports = port_range.split('-').map do |port_string|
|
379
|
-
Integer(port_string) rescue nil
|
380
|
-
end
|
381
|
-
|
382
|
-
if ports.any?(&:nil?) || ports.size > 2 || ports.size < 1
|
383
|
-
errors << "Malformed port range for #{param}"
|
384
|
-
end
|
385
|
-
|
386
|
-
{
|
387
|
-
'protocol' => protocol.downcase,
|
388
|
-
'port' => ports[0],
|
389
|
-
'portTo' => ports[1]
|
390
|
-
}.keep_if { |_, value| value }
|
391
|
-
end
|
392
|
-
else
|
393
|
-
errors << "Unsupported protocol for #{param}"
|
394
|
-
end
|
395
|
-
end
|
396
|
-
|
397
|
-
permissions.flatten!
|
398
|
-
end
|
399
|
-
|
400
|
-
def parse_sources(sources)
|
401
|
-
sources.map! do |cidr|
|
402
|
-
{ 'cidr' => cidr }
|
403
|
-
end
|
404
|
-
end
|
405
|
-
|
406
|
-
def prepare_launch_params
|
407
|
-
{
|
408
|
-
'name' => config[:clc_name],
|
409
|
-
'description' => config[:clc_description],
|
410
|
-
'groupId' => config[:clc_group],
|
411
|
-
'sourceServerId' => config[:clc_source_server],
|
412
|
-
'isManagedOS' => config[:clc_managed],
|
413
|
-
'isManagedBackup' => config[:clc_managed_backup],
|
414
|
-
'primaryDns' => config[:clc_primary_dns],
|
415
|
-
'secondaryDns' => config[:clc_secondary_dns],
|
416
|
-
'networkId' => config[:clc_network],
|
417
|
-
'ipAddress' => config[:clc_ip],
|
418
|
-
'password' => config[:clc_server_password],
|
419
|
-
'sourceServerPassword' => config[:clc_source_server_password],
|
420
|
-
'cpu' => config[:clc_cpu].to_i,
|
421
|
-
'cpuAutoscalePolicyId' => config[:clc_cpu_autoscale_policy],
|
422
|
-
'memoryGB' => config[:clc_memory].to_i,
|
423
|
-
'type' => config[:clc_type],
|
424
|
-
'storageType' => config[:clc_storage_type],
|
425
|
-
'antiAffinityPolicyId' => config[:clc_anti_affinity_policy],
|
426
|
-
'customFields' => config[:clc_custom_fields],
|
427
|
-
'additionalDisks' => config[:clc_disks],
|
428
|
-
'ttl' => config[:clc_ttl],
|
429
|
-
'packages' => config[:clc_packages],
|
430
|
-
}.delete_if { |_, value| !value.kind_of?(Integer) && (value.nil? || value.empty?) }
|
431
|
-
end
|
432
|
-
|
433
|
-
def prepare_ip_params
|
434
|
-
{
|
435
|
-
'ports' => config[:clc_allowed_protocols],
|
436
|
-
'sourceRestrictions' => config[:clc_sources]
|
437
|
-
}.delete_if { |_, value| value.nil? || value.empty? }
|
23
|
+
server_launcher.prepare
|
24
|
+
ip_assigner.prepare
|
25
|
+
bootstrapper.prepare
|
438
26
|
end
|
439
27
|
|
440
28
|
def execute
|
@@ -443,22 +31,24 @@ class Chef
|
|
443
31
|
|
444
32
|
def sync_create_server
|
445
33
|
ui.info 'Requesting server launch...'
|
446
|
-
links =
|
34
|
+
links = server_launcher.execute
|
447
35
|
connection.wait_for(links['operation']['id']) { putc '.' }
|
448
36
|
ui.info "\n"
|
449
37
|
ui.info "Server has been launched"
|
450
38
|
|
39
|
+
server = connection.follow(links['resource'])
|
40
|
+
|
451
41
|
if config[:clc_allowed_protocols]
|
452
42
|
ui.info 'Requesting public IP...'
|
453
|
-
|
454
|
-
ip_links = connection.create_ip_address(server['id'], prepare_ip_params)
|
43
|
+
ip_links = ip_assigner.execute(server['id'])
|
455
44
|
connection.wait_for(ip_links['operation']['id']) { putc '.' }
|
456
45
|
ui.info "\n"
|
457
46
|
ui.info 'Public IP has been assigned'
|
47
|
+
server = connection.follow(links['resource'])
|
458
48
|
end
|
459
49
|
|
460
50
|
if config[:clc_bootstrap]
|
461
|
-
sync_bootstrap(
|
51
|
+
bootstrapper.sync_bootstrap(server)
|
462
52
|
end
|
463
53
|
|
464
54
|
argv = [links['resource']['id'], '--uuid', '--creds']
|
@@ -474,22 +64,20 @@ class Chef
|
|
474
64
|
end
|
475
65
|
|
476
66
|
def async_create_server
|
477
|
-
launch_params = prepare_launch_params
|
478
|
-
|
479
67
|
if config[:clc_bootstrap]
|
480
|
-
|
68
|
+
bootstrapper.async_bootstrap(server_launcher.launch_parameters)
|
481
69
|
ui.info 'Bootstrap has been scheduled'
|
482
70
|
end
|
483
71
|
|
484
72
|
ui.info 'Requesting server launch...'
|
485
|
-
links =
|
73
|
+
links = server_launcher.execute
|
486
74
|
ui.info 'Launch request has been sent'
|
487
75
|
ui.info "You can check launch operation status with 'knife clc operation show #{links['operation']['id']}'"
|
488
76
|
|
489
77
|
if config[:clc_allowed_protocols]
|
490
78
|
ui.info 'Requesting public IP...'
|
491
79
|
server = connection.follow(links['resource'])
|
492
|
-
ip_links =
|
80
|
+
ip_links = ip_assigner.execute(server['id'])
|
493
81
|
ui.info 'Public IP request has been sent'
|
494
82
|
ui.info "You can check assignment operation status with 'knife clc operation show #{ip_links['operation']['id']}'"
|
495
83
|
end
|
@@ -499,97 +87,6 @@ class Chef
|
|
499
87
|
|
500
88
|
ui.info "You can check server status later with 'knife clc server show #{argv.join(' ')}'"
|
501
89
|
end
|
502
|
-
|
503
|
-
def sync_bootstrap(uuid)
|
504
|
-
server = connection.show_server(uuid, true)
|
505
|
-
|
506
|
-
ensure_server_powered_on(server)
|
507
|
-
|
508
|
-
command = bootstrap_command
|
509
|
-
|
510
|
-
command.name_args = [get_server_fqdn(server)]
|
511
|
-
|
512
|
-
username, password = config.values_at(:ssh_user, :ssh_password)
|
513
|
-
unless username && password
|
514
|
-
creds = get_server_credentials(server)
|
515
|
-
command.config.merge!(:ssh_user => creds['userName'], :ssh_password => creds['password'])
|
516
|
-
end
|
517
|
-
|
518
|
-
command.config[:chef_node_name] ||= server['name']
|
519
|
-
|
520
|
-
retry_on_timeouts { command.run }
|
521
|
-
end
|
522
|
-
|
523
|
-
def retry_on_timeouts(tries = 2, &block)
|
524
|
-
yield
|
525
|
-
rescue Errno::ETIMEDOUT => e
|
526
|
-
tries -= 1
|
527
|
-
|
528
|
-
if tries > 0
|
529
|
-
ui.info 'Retrying host connection...'
|
530
|
-
retry
|
531
|
-
else
|
532
|
-
raise
|
533
|
-
end
|
534
|
-
end
|
535
|
-
|
536
|
-
def ensure_server_powered_on(server)
|
537
|
-
return unless server['details']['powerState'] == 'stopped'
|
538
|
-
ui.info 'Requesting server power on...'
|
539
|
-
links = connection.power_on_server(server['id'])
|
540
|
-
connection.wait_for(links['operation']['id']) { putc '.' }
|
541
|
-
ui.info "\n"
|
542
|
-
ui.info 'Server has been powered on'
|
543
|
-
end
|
544
|
-
|
545
|
-
def add_bootstrapping_params(launch_params)
|
546
|
-
launch_params['packages'] ||= []
|
547
|
-
launch_params['packages'] << package_for_async_bootstrap
|
548
|
-
end
|
549
|
-
|
550
|
-
def package_for_async_bootstrap
|
551
|
-
{
|
552
|
-
'packageId' => 'a5d9d04369df4276a4f98f2ca7f7872b',
|
553
|
-
'parameters' => {
|
554
|
-
'Mode' => 'Ssh',
|
555
|
-
'Script' => bootstrap_command.render_template
|
556
|
-
}
|
557
|
-
}
|
558
|
-
end
|
559
|
-
|
560
|
-
def get_server_fqdn(server)
|
561
|
-
if indirect_bootstrap?
|
562
|
-
private_ips = server['details']['ipAddresses'].map { |addr| addr['internal'] }.compact
|
563
|
-
private_ips.first
|
564
|
-
else
|
565
|
-
public_ips = server['details']['ipAddresses'].map { |addr| addr['public'] }.compact
|
566
|
-
public_ips.first
|
567
|
-
end
|
568
|
-
end
|
569
|
-
|
570
|
-
def indirect_bootstrap?
|
571
|
-
config[:clc_bootstrap_private] || config[:ssh_gateway]
|
572
|
-
end
|
573
|
-
|
574
|
-
def get_server_credentials(server)
|
575
|
-
creds_link = server['links'].find { |link| link['rel'] == 'credentials' }
|
576
|
-
connection.follow(creds_link) if creds_link
|
577
|
-
end
|
578
|
-
|
579
|
-
def self.bootstrap_command_class
|
580
|
-
Chef::Knife::Bootstrap
|
581
|
-
end
|
582
|
-
|
583
|
-
def bootstrap_command
|
584
|
-
command_class = self.class.bootstrap_command_class
|
585
|
-
command_class.load_deps
|
586
|
-
command = command_class.new
|
587
|
-
command.config.merge!(config)
|
588
|
-
command.configure_chef
|
589
|
-
command
|
590
|
-
end
|
591
|
-
|
592
|
-
self.options.merge!(bootstrap_command_class.options)
|
593
90
|
end
|
594
91
|
end
|
595
92
|
end
|
data/lib/clc/client.rb
CHANGED
@@ -0,0 +1,18 @@
|
|
1
|
+
module Knife
|
2
|
+
module Clc
|
3
|
+
module Async
|
4
|
+
class ConfigOptions
|
5
|
+
def self.attach(command_class)
|
6
|
+
command_class.class_eval do
|
7
|
+
option :clc_wait,
|
8
|
+
:long => '--wait',
|
9
|
+
:description => 'Wait for operation completion',
|
10
|
+
:boolean => true,
|
11
|
+
:default => false,
|
12
|
+
:on => :head
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
module Knife
|
2
|
+
module Clc
|
3
|
+
module Base
|
4
|
+
class ConfigOptions
|
5
|
+
def self.attach(command_class)
|
6
|
+
command_class.class_eval do
|
7
|
+
option :clc_username,
|
8
|
+
:long => '--username NAME',
|
9
|
+
:description => 'Name of the user to access CLC API',
|
10
|
+
:on => :head
|
11
|
+
|
12
|
+
option :clc_password,
|
13
|
+
:long => '--password PASSWORD',
|
14
|
+
:description => 'Password for CLC user account',
|
15
|
+
:on => :head
|
16
|
+
|
17
|
+
option :clc_endpoint,
|
18
|
+
:long => '--endpoint URL',
|
19
|
+
:description => 'Alternative CLC API URL',
|
20
|
+
:on => :head
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|