knife-cosmic 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (55) hide show
  1. checksums.yaml +7 -0
  2. data/CHANGES.rdoc +186 -0
  3. data/LICENSE +202 -0
  4. data/README.rdoc +427 -0
  5. data/lib/chef/knife/cosmic_aag_list.rb +58 -0
  6. data/lib/chef/knife/cosmic_account_list.rb +87 -0
  7. data/lib/chef/knife/cosmic_base.rb +108 -0
  8. data/lib/chef/knife/cosmic_baselist.rb +111 -0
  9. data/lib/chef/knife/cosmic_cluster_list.rb +60 -0
  10. data/lib/chef/knife/cosmic_config_list.rb +56 -0
  11. data/lib/chef/knife/cosmic_disk_list.rb +58 -0
  12. data/lib/chef/knife/cosmic_domain_list.rb +53 -0
  13. data/lib/chef/knife/cosmic_firewallrule_create.rb +138 -0
  14. data/lib/chef/knife/cosmic_firewallrule_list.rb +62 -0
  15. data/lib/chef/knife/cosmic_forwardrule_create.rb +145 -0
  16. data/lib/chef/knife/cosmic_host_list.rb +61 -0
  17. data/lib/chef/knife/cosmic_hosts.rb +58 -0
  18. data/lib/chef/knife/cosmic_iso_list.rb +89 -0
  19. data/lib/chef/knife/cosmic_keypair_create.rb +72 -0
  20. data/lib/chef/knife/cosmic_keypair_delete.rb +60 -0
  21. data/lib/chef/knife/cosmic_keypair_list.rb +44 -0
  22. data/lib/chef/knife/cosmic_network_list.rb +63 -0
  23. data/lib/chef/knife/cosmic_oscategory_list.rb +50 -0
  24. data/lib/chef/knife/cosmic_ostype_list.rb +52 -0
  25. data/lib/chef/knife/cosmic_pod_list.rb +60 -0
  26. data/lib/chef/knife/cosmic_project_list.rb +63 -0
  27. data/lib/chef/knife/cosmic_publicip_list.rb +55 -0
  28. data/lib/chef/knife/cosmic_router_list.rb +64 -0
  29. data/lib/chef/knife/cosmic_securitygroup_list.rb +59 -0
  30. data/lib/chef/knife/cosmic_server_add_nic.rb +109 -0
  31. data/lib/chef/knife/cosmic_server_create.rb +674 -0
  32. data/lib/chef/knife/cosmic_server_delete.rb +153 -0
  33. data/lib/chef/knife/cosmic_server_list.rb +167 -0
  34. data/lib/chef/knife/cosmic_server_passwordreset.rb +91 -0
  35. data/lib/chef/knife/cosmic_server_reboot.rb +99 -0
  36. data/lib/chef/knife/cosmic_server_remove_nic.rb +101 -0
  37. data/lib/chef/knife/cosmic_server_start.rb +104 -0
  38. data/lib/chef/knife/cosmic_server_stop.rb +118 -0
  39. data/lib/chef/knife/cosmic_server_update.rb +47 -0
  40. data/lib/chef/knife/cosmic_service_list.rb +74 -0
  41. data/lib/chef/knife/cosmic_stack_create.rb +298 -0
  42. data/lib/chef/knife/cosmic_stack_delete.rb +79 -0
  43. data/lib/chef/knife/cosmic_template_create.rb +129 -0
  44. data/lib/chef/knife/cosmic_template_extract.rb +104 -0
  45. data/lib/chef/knife/cosmic_template_list.rb +88 -0
  46. data/lib/chef/knife/cosmic_template_register.rb +187 -0
  47. data/lib/chef/knife/cosmic_user_list.rb +62 -0
  48. data/lib/chef/knife/cosmic_volume_attach.rb +70 -0
  49. data/lib/chef/knife/cosmic_volume_create.rb +108 -0
  50. data/lib/chef/knife/cosmic_volume_delete.rb +97 -0
  51. data/lib/chef/knife/cosmic_volume_detach.rb +61 -0
  52. data/lib/chef/knife/cosmic_volume_list.rb +77 -0
  53. data/lib/chef/knife/cosmic_zone_list.rb +53 -0
  54. data/lib/knife-cosmic/connection.rb +1046 -0
  55. metadata +127 -0
@@ -0,0 +1,674 @@
1
+ #
2
+ # knife-cosmic author:: Robbert-Jan Sperna Weiland (<rspernaweiland@schubergphilis.com>)
3
+ # Original knife-cloudstack author:: Ryan Holmes (<rholmes@edmunds.com>)
4
+ # Original knife-cloudstack author:: Sander Botman (<sbotman@schubergphilis.com>)
5
+ # Original knife-cloudstack author:: Sander van Harmelen (<svanharmelen@schubergphilis.com>)
6
+ # Copyright:: Copyright (c) 2011 Edmunds, Inc.
7
+ # Copyright:: Copyright (c) 2013 Sander Botman.
8
+ # License:: Apache License, Version 2.0
9
+ #
10
+ # Licensed under the Apache License, Version 2.0 (the "License");
11
+ # you may not use this file except in compliance with the License.
12
+ # You may obtain a copy of the License at
13
+ #
14
+ # http://www.apache.org/licenses/LICENSE-2.0
15
+ #
16
+ # Unless required by applicable law or agreed to in writing, software
17
+ # distributed under the License is distributed on an "AS IS" BASIS,
18
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
19
+ # See the License for the specific language governing permissions and
20
+ # limitations under the License.
21
+ #
22
+
23
+ require 'chef/knife/cosmic_base'
24
+ require 'chef/knife/winrm_base'
25
+
26
+ module Knifecosmic
27
+ class CosmicServerCreate < Chef::Knife
28
+
29
+ include Chef::Knife::KnifecosmicBase
30
+ include Chef::Knife::WinrmBase
31
+
32
+ # Seconds to delay between detecting ssh and initiating the bootstrap
33
+ BOOTSTRAP_DELAY = 20
34
+ #The machine will reboot once so we need to handle that
35
+ WINRM_BOOTSTRAP_DELAY = 200
36
+
37
+ # Seconds to wait between ssh pings
38
+ SSH_POLL_INTERVAL = 10
39
+
40
+ deps do
41
+ require 'chef/knife/bootstrap'
42
+ require 'chef/knife/bootstrap_windows_winrm'
43
+ require 'chef/knife/bootstrap_windows_ssh'
44
+ require 'chef/knife/core/windows_bootstrap_context'
45
+ require 'chef/knife/winrm'
46
+ require 'socket'
47
+ require 'net/ssh/multi'
48
+ require 'chef/knife'
49
+ require 'chef/knife/bootstrap'
50
+ require 'chef/json_compat'
51
+ require 'knife-cosmic/connection'
52
+ require 'winrm'
53
+ require 'httpclient'
54
+ Chef::Knife::Bootstrap.load_deps
55
+ end
56
+
57
+ banner "knife cosmic server create [SERVER_NAME] (options)"
58
+
59
+ option :cosmic_service,
60
+ :short => "-S SERVICE",
61
+ :long => "--service SERVICE",
62
+ :description => "The cosmic service offering name",
63
+ :proc => Proc.new { |o| Chef::Config[:knife][:cosmic_service] = o },
64
+ :default => "M"
65
+
66
+ option :cosmic_template,
67
+ :short => "-T TEMPLATE",
68
+ :long => "--template TEMPLATE",
69
+ :description => "The cosmic template for the server",
70
+ :proc => Proc.new { |t| Chef::Config[:knife][:cosmic_template] = t }
71
+
72
+ option :cosmic_zone,
73
+ :short => "-Z ZONE",
74
+ :long => "--zone ZONE",
75
+ :description => "The cosmic zone for the server",
76
+ :proc => Proc.new { |z| Chef::Config[:knife][:cosmic_zone] = z }
77
+
78
+ option :cosmic_networks,
79
+ :short => "-W NETWORKS",
80
+ :long => "--networks NETWORK",
81
+ :description => "Comma separated list of cosmic network names",
82
+ :proc => lambda { |n| n.split(',').map {|sn| sn.strip}} ,
83
+ :default => []
84
+
85
+ option :cosmic_disk,
86
+ :short => "-D DISK",
87
+ :long => "--disk DISK",
88
+ :description => "The cosmic disk offering name",
89
+ :proc => Proc.new { |d| Chef::Config[:knife][:cosmic_disk] = d }
90
+
91
+ option :cosmic_diskcontroller,
92
+ :short => "-C DISKCONTROLLER",
93
+ :long => "--diskcontroller DISKCONTROLLER",
94
+ :description => "The cosmic diskcontroller type",
95
+ :proc => Proc.new { |d| Chef::Config[:knife][:cosmic_diskcontroller] = d }
96
+
97
+ option :size,
98
+ :long => "--size SIZE",
99
+ :description => "The arbitrary size (GB) for the DATADISK volume"
100
+
101
+ option :cosmic_hypervisor,
102
+ :long => '--cosmic-hypervisor HYPERVISOR',
103
+ :description => "The cosmic hypervisor type for the server"
104
+
105
+ option :cosmic_password,
106
+ :long => "--cosmic-password",
107
+ :description => "Enables auto-generated passwords by cosmic",
108
+ :boolean => true,
109
+ :default => true
110
+
111
+ option :public_ip,
112
+ :long => "--[no-]public-ip",
113
+ :description => "Allocate a public IP for this server",
114
+ :boolean => true,
115
+ :default => true
116
+
117
+ option :ik_private_ip,
118
+ :long => "--private-ip PRIVATE_IPV4_ADDRESS",
119
+ :description => "Pass a certain private ipv4 address to cosmic when deploying (only supported on isolated networks)"
120
+
121
+ option :chef_node_name,
122
+ :short => "-N NAME",
123
+ :long => "--node-name NAME",
124
+ :description => "The Chef node name for your new node"
125
+
126
+ option :ssh_user,
127
+ :short => "-x USERNAME",
128
+ :long => "--ssh-user USERNAME",
129
+ :description => "The ssh username"
130
+
131
+ option :ssh_password,
132
+ :short => "-P PASSWORD",
133
+ :long => "--ssh-password PASSWORD",
134
+ :description => "The ssh password"
135
+
136
+ option :ssh_port,
137
+ :long => "--ssh-port PORT",
138
+ :description => "The ssh port",
139
+ :default => "22"
140
+
141
+ option :identity_file,
142
+ :short => "-i IDENTITY_FILE",
143
+ :long => "--identity-file IDENTITY_FILE",
144
+ :description => "The SSH identity file used for authentication"
145
+
146
+ option :secret_file,
147
+ :long => "--secret-file SECRET_FILE",
148
+ :description => "The path to the file that contains the encryption key.",
149
+ :proc => lambda { |secret_file| Chef::Config[:knife][:secret_file] = secret_file }
150
+
151
+ option :secret,
152
+ :long => "--secret SECRET",
153
+ :description => "The encryption key that is used for values contained within a data bag item."
154
+
155
+ option :prerelease,
156
+ :long => "--prerelease",
157
+ :description => "Install the pre-release chef gems"
158
+
159
+ option :bootstrap_version,
160
+ :long => "--bootstrap-version VERSION",
161
+ :description => "The version of Chef to install",
162
+ :proc => Proc.new { |v| Chef::Config[:knife][:bootstrap_version] = v }
163
+
164
+ option :distro,
165
+ :short => "-d DISTRO",
166
+ :long => "--distro DISTRO",
167
+ :description => "Bootstrap a distro using a template",
168
+ :proc => Proc.new { |d| Chef::Config[:knife][:distro] = d },
169
+ :default => "chef-full"
170
+
171
+ option :template_file,
172
+ :long => "--template-file TEMPLATE",
173
+ :description => "Full path to location of template to use",
174
+ :proc => Proc.new { |t| Chef::Config[:knife][:template_file] = t },
175
+ :default => false
176
+
177
+ option :run_list,
178
+ :short => "-r RUN_LIST",
179
+ :long => "--run-list RUN_LIST",
180
+ :description => "Comma separated list of roles/recipes to apply",
181
+ :proc => lambda { |o| o.split(/[\s,]+/) },
182
+ :default => []
183
+
184
+ option :no_host_key_verify,
185
+ :long => "--no-host-key-verify",
186
+ :description => "Disable host key verification",
187
+ :boolean => true,
188
+ :default => false
189
+
190
+ option :bootstrap,
191
+ :long => "--[no-]bootstrap",
192
+ :description => "Disable Chef bootstrap",
193
+ :boolean => true,
194
+ :default => true
195
+
196
+ option :port_rules,
197
+ :short => "-p PORT_RULES",
198
+ :long => "--port-rules PORT_RULES",
199
+ :description => "Comma separated list of port forwarding rules, e.g. '25,53:4053,80:8080:TCP'",
200
+ :proc => lambda { |o| o.split(/[\s,]+/) },
201
+ :default => []
202
+
203
+ option :keypair,
204
+ :long => "--keypair NAME",
205
+ :description => "Name of the keypair that should be used to create the vm",
206
+ :default => false
207
+
208
+ option :static_nat,
209
+ :long => '--static-nat',
210
+ :description => 'Support Static NAT',
211
+ :boolean => true,
212
+ :default => false
213
+
214
+ option :ipfwd_rules,
215
+ :long => "--ipfwd-rules PORT_RULES",
216
+ :description => "Comma separated list of ip forwarding rules, e.g. '1024:10000:TCP,1024:2048,22'",
217
+ :proc => lambda { |o| o.split(/[\s,]+/) },
218
+ :default => []
219
+
220
+ option :aag,
221
+ :long => "--anti-affinity-groups GROUP_NAME",
222
+ :description => "Comma separated list of anti-affinity group names",
223
+ :default => false
224
+
225
+ option :fw_rules,
226
+ :short => "-f PORT_RULES",
227
+ :long => "--fw-rules PORT_RULES",
228
+ :description => "Comma separated list of firewall rules, e.g. 'TCP:192.168.0.0/16:1024:65535,TCP::22,UDP::123,ICMP'",
229
+ :proc => lambda { |o| o.split(/[\s,]+/) },
230
+ :default => []
231
+
232
+ option :bootstrap_protocol,
233
+ :long => "--bootstrap-protocol protocol",
234
+ :description => "Protocol to bootstrap windows servers. options: winrm/ssh",
235
+ :default => "ssh"
236
+
237
+ option :fqdn,
238
+ :long => '--fqdn',
239
+ :description => "FQDN which Kerberos Understands (only for Windows Servers)"
240
+
241
+ option :set_display_name,
242
+ :long => '--set-display-name',
243
+ :description => "Set the same server display name as Chef node name.",
244
+ :boolean => true,
245
+ :default => false
246
+
247
+ option :startvm,
248
+ :long => "--[no-]startvm",
249
+ :description => "Specify whether the VM should be started after creation.",
250
+ :boolean => true,
251
+ :default => true
252
+
253
+ option :bootstrap_proxy,
254
+ :long => "--bootstrap-proxy PROXY_URL",
255
+ :description => "The proxy server for the node being bootstrapped",
256
+ :proc => Proc.new { |p| Chef::Config[:knife][:bootstrap_proxy] = p }
257
+
258
+
259
+ option :bootstrap_wget_options,
260
+ :long => "--bootstrap-wget-options OPTIONS",
261
+ :description => "Add options to wget when installing chef-client",
262
+ :proc => Proc.new { |wo| Chef::Config[:knife][:bootstrap_wget_options] = wo }
263
+
264
+ def run
265
+ validate_base_options
266
+
267
+ Chef::Log.debug("Validate hostname and options")
268
+ hostname = @name_args.first
269
+ unless /^[a-zA-Z0-9][a-zA-Z0-9-]*$/.match hostname then
270
+ ui.error "Invalid hostname. Please specify a short hostname, not an fqdn (e.g. 'myhost' instead of 'myhost.domain.com')."
271
+ exit 1
272
+ end
273
+ validate_options
274
+
275
+ # This little peace of code sets the Chef node-name to the VM name when a node-name is not specifically given
276
+ unless locate_config_value :chef_node_name
277
+ config[:chef_node_name] = @name_args.first
278
+ end
279
+
280
+ if @windows_image and locate_config_value(:kerberos_realm)
281
+ Chef::Log.debug("Load additional gems for AD/Kerberos Authentication")
282
+ if @windows_platform
283
+ require 'em-winrs'
284
+ else
285
+ require 'gssapi'
286
+ end
287
+ end
288
+
289
+ $stdout.sync = true
290
+
291
+ Chef::Log.info("Creating instance with
292
+ service : #{locate_config_value(:cosmic_service)}
293
+ template : #{locate_config_value(:cosmic_template)}
294
+ disk : #{locate_config_value(:cosmic_disk)}
295
+ zone : #{locate_config_value(:cosmic_zone)}
296
+ project: #{locate_config_value(:cosmic_project)}
297
+ network: #{locate_config_value(:cosmic_networks)}")
298
+
299
+ print "\n#{ui.color("Waiting for Server to be created", :magenta)}"
300
+ params = {}
301
+ params['hypervisor'] = locate_config_value(:cosmic_hypervisor) if locate_config_value(:cosmic_hypervisor)
302
+
303
+ params['keypair'] = locate_config_value :keypair if locate_config_value :keypair
304
+ params['affinitygroupnames'] = locate_config_value :aag if locate_config_value :aag
305
+ params['displayname'] = if locate_config_value :set_display_name and locate_config_value :chef_node_name then locate_config_value :chef_node_name else hostname end
306
+ params['ipaddress'] = locate_config_value(:ik_private_ip) if locate_config_value(:ik_private_ip)
307
+ params['size'] = locate_config_value(:size) if locate_config_value(:size)
308
+ params['startvm'] = 'true'
309
+ params['startvm'] = config[:startvm].to_s if config[:startvm]
310
+ params['diskcontroller'] = locate_config_value(:cosmic_diskcontroller) if locate_config_value(:cosmic_diskcontroller)
311
+
312
+ @server = connection.create_server(
313
+ hostname,
314
+ locate_config_value(:cosmic_service),
315
+ locate_config_value(:cosmic_template),
316
+ locate_config_value(:cosmic_disk),
317
+ locate_config_value(:cosmic_zone),
318
+ locate_config_value(:cosmic_networks),
319
+ params
320
+ )
321
+
322
+ zone_name = locate_config_value(:cosmic_zone)
323
+ zone = zone_name ? connection.get_zone(zone_name) : connection.get_default_zone
324
+
325
+ config[:public_ip] = false if zone['networktype'] == 'Basic'
326
+ @server['public_ip'] = find_or_create_public_ip(@server, connection)
327
+
328
+ object_fields = []
329
+ object_fields << ui.color("Name:", :cyan)
330
+ object_fields << @server['name'].to_s
331
+ object_fields << ui.color("Password:", :cyan) if locate_config_value(:cosmic_password)
332
+ object_fields << @server['password'] if locate_config_value(:cosmic_password)
333
+ object_fields << ui.color("Public IP:", :cyan)
334
+ object_fields << @server['public_ip']
335
+ object_fields << ui.color("id:", :cyan)
336
+ object_fields << @server['id']
337
+
338
+ puts "\n"
339
+ puts ui.list(object_fields, :uneven_columns_across, 2)
340
+ puts "\n"
341
+
342
+ return unless config[:bootstrap]
343
+
344
+ if @bootstrap_protocol == 'ssh'
345
+ print "\n#{ui.color("Waiting for sshd on: #{public_ip}", :magenta)}"
346
+
347
+ print(".") until is_ssh_open?(public_ip) {
348
+ sleep BOOTSTRAP_DELAY
349
+ puts "\n"
350
+ }
351
+ elsif @bootstrap_protocol == 'winrm'
352
+ print "\n#{ui.color("Waiting for winrm to be active on: #{public_ip}", :magenta)}"
353
+ print(".") until tcp_test_winrm(public_ip,locate_config_value(:winrm_port)) {
354
+ sleep WINRM_BOOTSTRAP_DELAY
355
+ puts("\n")
356
+ }
357
+ else
358
+ puts "Cannot determine the bootstrap protocol."
359
+ puts "Please specify either ssh or winrm as bootstrap protocol."
360
+ exit 1
361
+ end
362
+
363
+ object_fields = []
364
+ object_fields << ui.color("Name:", :cyan)
365
+ object_fields << @server['name'].to_s
366
+ object_fields << ui.color("Public IP:", :cyan)
367
+ object_fields << @server['public_ip']
368
+ object_fields << ui.color("Environment:", :cyan)
369
+ object_fields << (config[:environment] || '_default')
370
+ object_fields << ui.color("Run List:", :cyan)
371
+ object_fields << config[:run_list].join(', ')
372
+
373
+ puts "\n"
374
+ puts ui.list(object_fields, :uneven_columns_across, 2)
375
+ puts "\n"
376
+
377
+ bootstrap(@server).run
378
+ end
379
+
380
+ def fetch_server_fqdn(ip_addr)
381
+ require 'resolv'
382
+ Resolv.getname(ip_addr)
383
+ end
384
+
385
+ def is_image_windows?
386
+ template_name = locate_config_value(:cosmic_template)
387
+ template = connection.get_template(template_name, locate_config_value(:cosmic_zone))
388
+ template = connection.get_iso(template_name, locate_config_value(:cosmic_zone)) unless template
389
+ if !template
390
+ ui.error("Template: #{template_name} does not exist!")
391
+ exit 1
392
+ end
393
+ return template['ostypename'].scan('Windows').length > 0
394
+ end
395
+
396
+ def validate_options
397
+ unless locate_config_value :cosmic_template
398
+ ui.error "cosmic template not specified"
399
+ exit 1
400
+ end
401
+ @windows_image = is_image_windows?
402
+ @windows_platform = is_platform_windows?
403
+
404
+ unless locate_config_value :cosmic_service
405
+ ui.error "cosmic service offering not specified"
406
+ exit 1
407
+ end
408
+ if config[:bootstrap]
409
+ if locate_config_value(:bootstrap_protocol) == 'ssh'
410
+ identity_file = locate_config_value :identity_file
411
+ ssh_user = locate_config_value :ssh_user
412
+ ssh_password = locate_config_value :ssh_password
413
+ unless identity_file || (ssh_user && ssh_password) || locate_config_value(:cosmic_password)
414
+ ui.error("You must specify either an ssh identity file or an ssh user and password")
415
+ exit 1
416
+ end
417
+ @bootstrap_protocol = 'ssh'
418
+ elsif locate_config_value(:bootstrap_protocol) == 'winrm'
419
+ if not @windows_image
420
+ ui.error("Only Windows Images support WinRM protocol for bootstrapping.")
421
+ exit 1
422
+ end
423
+ winrm_user = locate_config_value :winrm_user
424
+ winrm_password = locate_config_value :winrm_password
425
+ winrm_transport = locate_config_value :winrm_transport
426
+ winrm_port = locate_config_value :winrm_port
427
+ unless (winrm_user && winrm_transport && winrm_port) && (locate_config_value(:cosmic_password) || winrm_password)
428
+ ui.error("WinRM User, Password, Transport and Port are compulsory parameters")
429
+ exit 1
430
+ end
431
+ @bootstrap_protocol = 'winrm'
432
+ end
433
+ end
434
+ end
435
+
436
+ def find_or_create_public_ip(server, connection)
437
+ nic = connection.get_server_default_nic(server) || {}
438
+ if (config[:public_ip] == false)
439
+ nic['ipaddress']
440
+ else
441
+ puts("\nAllocate ip address, create forwarding rules")
442
+ ip_address = connection.associate_ip_address(server['zoneid'], locate_config_value(:cosmic_networks))
443
+ puts("\nAllocated IP Address: #{ip_address['ipaddress']}")
444
+ Chef::Log.debug("IP Address Info: #{ip_address}")
445
+
446
+ if locate_config_value :static_nat
447
+ Chef::Log.debug("Enabling static NAT for IP Address : #{ip_address['ipaddress']}")
448
+ connection.enable_static_nat(ip_address['id'], server['id'])
449
+ end
450
+ create_port_forwarding_rules(ip_address, server['id'], connection)
451
+ create_ip_forwarding_rules(ip_address, connection)
452
+ create_firewall_rules(ip_address, connection)
453
+ ip_address['ipaddress']
454
+ end
455
+ end
456
+
457
+ def create_port_forwarding_rules(ip_address, server_id, connection)
458
+ Chef::Log.debug("Creating IP Forwarding Rule")
459
+ rules = locate_config_value(:port_rules) || []
460
+ if config[:bootstrap]
461
+ if @bootstrap_protocol == 'ssh'
462
+ rules += ["#{locate_config_value(:ssh_port)}"] #SSH Port
463
+ elsif @bootstrap_protocol == 'winrm'
464
+ rules +=[locate_config_value(:winrm_port)]
465
+ else
466
+ puts("\nUnsupported bootstrap protocol : #{@bootstrap_protocol}")
467
+ exit 1
468
+ end
469
+ end
470
+ return if rules.empty?
471
+ rules.each do |rule|
472
+ args = rule.split(':')
473
+ public_port = args[0]
474
+ private_port = args[1] || args[0]
475
+ protocol = args[2] || "TCP"
476
+ if locate_config_value :static_nat
477
+ Chef::Log.debug("Creating IP Forwarding Rule for
478
+ #{ip_address['ipaddress']} with protocol: #{protocol}, public port: #{public_port}")
479
+ connection.create_ip_fwd_rule(ip_address['id'], protocol, public_port, public_port)
480
+ else
481
+ Chef::Log.debug("Creating Port Forwarding Rule for #{ip_address['id']} with protocol: #{protocol},
482
+ public port: #{public_port} and private port: #{private_port} and server: #{server_id}")
483
+ connection.create_port_forwarding_rule(ip_address['id'], private_port, protocol, public_port, server_id)
484
+ end
485
+ end
486
+ end
487
+
488
+ def create_ip_forwarding_rules(ip_address, connection)
489
+ Chef::Log.debug("Creating IP Forwarding Rule")
490
+ rules = locate_config_value(:ipfwd_rules)
491
+ return unless rules
492
+ rules.each do |rule|
493
+ args = rule.split(':')
494
+ startport = args[0]
495
+ endport = args[1] || args[0]
496
+ protocol = args[2] || "TCP"
497
+ if locate_config_value :static_nat
498
+ Chef::Log.debug("Creating IP Forwarding Rule for
499
+ #{ip_address['ipaddress']} with protocol: #{protocol}, startport: #{startport}, endport: #{endport}")
500
+ connection.create_ip_fwd_rule(ip_address['id'], protocol, startport, endport)
501
+ end
502
+ end
503
+ end
504
+
505
+ def create_firewall_rules(ip_address, connection)
506
+ Chef::Log.debug("Creating Firewall Rule")
507
+ rules = locate_config_value(:fw_rules)
508
+ return unless rules
509
+ icmptype={
510
+ '0' => {'code' => [0]},
511
+ '8' => {'code' => [0]},
512
+ '3' => {'code' => [0, 1]}
513
+ }
514
+ rules.each do |rule|
515
+ args = rule.split(':')
516
+ protocol = args[0]
517
+ cidr_list = (args[1].nil? || args[1].length == 0) ? "0.0.0.0/0" : args[1]
518
+ startport = args[2]
519
+ endport = args[3] || args[2]
520
+ if protocol == "ICMP"
521
+ icmptype.each do |type, value|
522
+ value['code'].each do |code_id|
523
+ Chef::Log.debug("Creating Firewall Rule for
524
+ #{ip_address['ipaddress']} with protocol: #{protocol}, icmptype: #{type}, icmpcode: #{code_id}, cidrList: #{cidr_list}")
525
+ connection.create_firewall_rule(ip_address['id'], protocol, type, code_id, cidr_list)
526
+ end
527
+ end
528
+ else
529
+ Chef::Log.debug("Creating Firewall Rule for
530
+ #{ip_address['ipaddress']} with protocol: #{protocol}, startport: #{startport}, endport: #{endport}, cidrList: #{cidr_list}")
531
+ connection.create_firewall_rule(ip_address['id'], protocol, startport, endport, cidr_list)
532
+ end
533
+ end
534
+ end
535
+
536
+ def tcp_test_winrm(hostname, port)
537
+ TCPSocket.new(hostname, port)
538
+ return true
539
+ rescue SocketError
540
+ sleep 2
541
+ false
542
+ rescue Errno::ETIMEDOUT
543
+ false
544
+ rescue Errno::EPERM
545
+ false
546
+ rescue Errno::ECONNREFUSED
547
+ sleep 2
548
+ false
549
+ rescue Errno::EHOSTUNREACH
550
+ sleep 2
551
+ false
552
+ rescue Errno::ENETUNREACH
553
+ sleep 2
554
+ false
555
+ end
556
+
557
+ #noinspection RubyArgCount,RubyResolve
558
+ def is_ssh_open?(ip)
559
+ s = Socket.new(Socket::AF_INET, Socket::SOCK_STREAM, 0)
560
+ sa = Socket.sockaddr_in(locate_config_value(:ssh_port), ip)
561
+
562
+ begin
563
+ s.connect_nonblock(sa)
564
+ rescue Errno::EINPROGRESS
565
+ resp = IO.select(nil, [s], nil, 1)
566
+ if resp.nil?
567
+ sleep SSH_POLL_INTERVAL
568
+ return false
569
+ end
570
+
571
+ begin
572
+ s.connect_nonblock(sa)
573
+ rescue Errno::EISCONN
574
+ Chef::Log.debug("sshd accepting connections on #{ip}")
575
+ yield
576
+ return true
577
+ rescue Errno::ECONNREFUSED, Errno::EHOSTUNREACH
578
+ sleep SSH_POLL_INTERVAL
579
+ return false
580
+ end
581
+ ensure
582
+ s && s.close
583
+ end
584
+ end
585
+
586
+ def is_platform_windows?
587
+ return RUBY_PLATFORM.scan('w32').size > 0
588
+ end
589
+
590
+ def bootstrap(server)
591
+ public_ip = server['public_ip']
592
+ if @windows_image
593
+ Chef::Log.debug("Windows Bootstrapping")
594
+ bootstrap_for_windows_node(server, public_ip)
595
+ else
596
+ Chef::Log.debug("Linux Bootstrapping")
597
+ bootstrap_for_node(server, public_ip)
598
+ end
599
+ end
600
+
601
+ def bootstrap_for_windows_node(server, fqdn)
602
+ if locate_config_value(:bootstrap_protocol) == 'winrm'
603
+ bootstrap = Chef::Knife::BootstrapWindowsWinrm.new
604
+ if locate_config_value(:kerberos_realm)
605
+ #Fetch AD/WINS based fqdn if any for Kerberos-based Auth
606
+ private_ip_address = connection.get_server_default_nic(server)["ipaddress"]
607
+ fqdn = locate_config_value(:fqdn) || fetch_server_fqdn(private_ip_address)
608
+ end
609
+ bootstrap.name_args = [fqdn]
610
+ bootstrap.config[:winrm_user] = locate_config_value(:winrm_user) || 'Administrator'
611
+ locate_config_value(:cosmic_password) ? bootstrap.config[:winrm_password] = server['password'] : bootstrap.config[:winrm_password] = locate_config_value(:winrm_password)
612
+ bootstrap.config[:winrm_transport] = locate_config_value(:winrm_transport)
613
+ bootstrap.config[:winrm_port] = locate_config_value(:winrm_port)
614
+ elsif locate_config_value(:bootstrap_protocol) == 'ssh'
615
+ bootstrap = Chef::Knife::BootstrapWindowsSsh.new
616
+ if locate_config_value(:cosmic_password)
617
+ bootstrap.config[:ssh_user] = locate_config_value(:ssh_user) || 'Administrator'
618
+ else
619
+ bootstrap.config[:ssh_user] = locate_config_value(:ssh_user)
620
+ end
621
+ locate_config_value(:cosmic_password) ? bootstrap.config[:ssh_password] = server['password'] : bootstrap.config[:ssh_password] = locate_config_value(:ssh_password)
622
+ bootstrap.config[:ssh_port] = locate_config_value(:ssh_port)
623
+ bootstrap.config[:identity_file] = locate_config_value(:identity_file)
624
+ bootstrap.config[:no_host_key_verify] = locate_config_value(:no_host_key_verify)
625
+ else
626
+ ui.error("Unsupported Bootstrapping Protocol. Supported : winrm, ssh")
627
+ exit 1
628
+ end
629
+ bootstrap.config[:chef_node_name] = locate_config_value(:chef_node_name) || server["name"]
630
+ bootstrap.config[:encrypted_data_bag_secret] = locate_config_value(:secret)
631
+ bootstrap.config[:encrypted_data_bag_secret_file] = locate_config_value(:secret_file)
632
+ bootstrap_common_params(bootstrap)
633
+ end
634
+
635
+ def bootstrap_common_params(bootstrap)
636
+ bootstrap.config[:run_list] = config[:run_list]
637
+ bootstrap.config[:prerelease] = config[:prerelease]
638
+ bootstrap.config[:bootstrap_version] = locate_config_value(:bootstrap_version)
639
+ bootstrap.config[:distro] = locate_config_value(:distro)
640
+ bootstrap.config[:template_file] = locate_config_value(:template_file)
641
+ bootstrap.config[:first_boot_attributes] = locate_config_value(:first_boot_attributes)
642
+ bootstrap.config[:environment] = locate_config_value(:environment)
643
+ bootstrap.config[:bootstrap_wget_options] = locate_config_value(:bootstrap_wget_options)
644
+ bootstrap.config[:bootstrap_proxy] = locate_config_value(:bootstrap_proxy)
645
+ bootstrap
646
+ end
647
+
648
+ def bootstrap_for_node(server,fqdn)
649
+ bootstrap = Chef::Knife::Bootstrap.new
650
+ bootstrap.name_args = [fqdn]
651
+ if locate_config_value(:cosmic_password)
652
+ bootstrap.config[:ssh_user] = locate_config_value(:ssh_user) || 'root'
653
+ else
654
+ bootstrap.config[:ssh_user] = locate_config_value(:ssh_user)
655
+ end
656
+ locate_config_value(:cosmic_password) ? bootstrap.config[:ssh_password] = server['password'] : bootstrap.config[:ssh_password] = locate_config_value(:ssh_password)
657
+ bootstrap.config[:ssh_port] = locate_config_value(:ssh_port) || 22
658
+ bootstrap.config[:identity_file] = locate_config_value(:identity_file)
659
+ bootstrap.config[:secret_file] = locate_config_value(:secret_file)
660
+ bootstrap.config[:secret] = locate_config_value(:secret)
661
+ bootstrap.config[:chef_node_name] = locate_config_value(:chef_node_name) || server["name"]
662
+ bootstrap.config[:use_sudo] = true unless locate_config_value(:ssh_user) == 'root'
663
+
664
+ # may be needed for vpc_mode
665
+ bootstrap.config[:host_key_verify] = config[:host_key_verify]
666
+ bootstrap_common_params(bootstrap)
667
+ end
668
+
669
+ def server
670
+ @server
671
+ end
672
+
673
+ end
674
+ end