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.
- checksums.yaml +7 -0
- data/CHANGES.rdoc +186 -0
- data/LICENSE +202 -0
- data/README.rdoc +427 -0
- data/lib/chef/knife/cosmic_aag_list.rb +58 -0
- data/lib/chef/knife/cosmic_account_list.rb +87 -0
- data/lib/chef/knife/cosmic_base.rb +108 -0
- data/lib/chef/knife/cosmic_baselist.rb +111 -0
- data/lib/chef/knife/cosmic_cluster_list.rb +60 -0
- data/lib/chef/knife/cosmic_config_list.rb +56 -0
- data/lib/chef/knife/cosmic_disk_list.rb +58 -0
- data/lib/chef/knife/cosmic_domain_list.rb +53 -0
- data/lib/chef/knife/cosmic_firewallrule_create.rb +138 -0
- data/lib/chef/knife/cosmic_firewallrule_list.rb +62 -0
- data/lib/chef/knife/cosmic_forwardrule_create.rb +145 -0
- data/lib/chef/knife/cosmic_host_list.rb +61 -0
- data/lib/chef/knife/cosmic_hosts.rb +58 -0
- data/lib/chef/knife/cosmic_iso_list.rb +89 -0
- data/lib/chef/knife/cosmic_keypair_create.rb +72 -0
- data/lib/chef/knife/cosmic_keypair_delete.rb +60 -0
- data/lib/chef/knife/cosmic_keypair_list.rb +44 -0
- data/lib/chef/knife/cosmic_network_list.rb +63 -0
- data/lib/chef/knife/cosmic_oscategory_list.rb +50 -0
- data/lib/chef/knife/cosmic_ostype_list.rb +52 -0
- data/lib/chef/knife/cosmic_pod_list.rb +60 -0
- data/lib/chef/knife/cosmic_project_list.rb +63 -0
- data/lib/chef/knife/cosmic_publicip_list.rb +55 -0
- data/lib/chef/knife/cosmic_router_list.rb +64 -0
- data/lib/chef/knife/cosmic_securitygroup_list.rb +59 -0
- data/lib/chef/knife/cosmic_server_add_nic.rb +109 -0
- data/lib/chef/knife/cosmic_server_create.rb +674 -0
- data/lib/chef/knife/cosmic_server_delete.rb +153 -0
- data/lib/chef/knife/cosmic_server_list.rb +167 -0
- data/lib/chef/knife/cosmic_server_passwordreset.rb +91 -0
- data/lib/chef/knife/cosmic_server_reboot.rb +99 -0
- data/lib/chef/knife/cosmic_server_remove_nic.rb +101 -0
- data/lib/chef/knife/cosmic_server_start.rb +104 -0
- data/lib/chef/knife/cosmic_server_stop.rb +118 -0
- data/lib/chef/knife/cosmic_server_update.rb +47 -0
- data/lib/chef/knife/cosmic_service_list.rb +74 -0
- data/lib/chef/knife/cosmic_stack_create.rb +298 -0
- data/lib/chef/knife/cosmic_stack_delete.rb +79 -0
- data/lib/chef/knife/cosmic_template_create.rb +129 -0
- data/lib/chef/knife/cosmic_template_extract.rb +104 -0
- data/lib/chef/knife/cosmic_template_list.rb +88 -0
- data/lib/chef/knife/cosmic_template_register.rb +187 -0
- data/lib/chef/knife/cosmic_user_list.rb +62 -0
- data/lib/chef/knife/cosmic_volume_attach.rb +70 -0
- data/lib/chef/knife/cosmic_volume_create.rb +108 -0
- data/lib/chef/knife/cosmic_volume_delete.rb +97 -0
- data/lib/chef/knife/cosmic_volume_detach.rb +61 -0
- data/lib/chef/knife/cosmic_volume_list.rb +77 -0
- data/lib/chef/knife/cosmic_zone_list.rb +53 -0
- data/lib/knife-cosmic/connection.rb +1046 -0
- 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
|