knife-cosmic 0.2.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.
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