Class: KnifeCloudstack::CsServerCreate

Inherits:
Chef::Knife
  • Object
show all
Defined in:
lib/chef/knife/cs_server_create.rb

Constant Summary collapse

BOOTSTRAP_DELAY =

Seconds to delay between detecting ssh and initiating the bootstrap

3
SSH_POLL_INTERVAL =

Seconds to wait between ssh pings

2

Instance Method Summary collapse

Instance Method Details

#bootstrap_for_node(host) ⇒ Object



296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
# File 'lib/chef/knife/cs_server_create.rb', line 296

def bootstrap_for_node(host)
  bootstrap = Chef::Knife::Bootstrap.new
  bootstrap.name_args = [host]
  bootstrap.config[:run_list] = config[:run_list]
  bootstrap.config[:ssh_user] = config[:ssh_user]
  bootstrap.config[:ssh_password] = config[:ssh_password]
  bootstrap.config[:identity_file] = config[:identity_file]
  bootstrap.config[:chef_node_name] = config[:chef_node_name] if config[:chef_node_name]
  bootstrap.config[:prerelease] = config[:prerelease]
  bootstrap.config[:bootstrap_version] = locate_config_value(:bootstrap_version)
  bootstrap.config[:distro] = locate_config_value(:distro)
  bootstrap.config[:use_sudo] = true
  bootstrap.config[:template_file] = locate_config_value(:template_file)
  bootstrap.config[:environment] = config[:environment]
  # may be needed for vpc_mode
  bootstrap.config[:no_host_key_verify] = config[:no_host_key_verify]
  bootstrap
end

#create_port_forwarding_rules(ip_address_id, server_id, connection) ⇒ Object



252
253
254
255
256
257
258
259
260
261
262
263
264
# File 'lib/chef/knife/cs_server_create.rb', line 252

def create_port_forwarding_rules(ip_address_id, server_id, connection)
  rules = locate_config_value(:port_rules)
  return unless rules

  rules.each do |rule|
    args = rule.split(':')
    public_port = args[0]
    private_port = args[1] || args[0]
    protocol = args[2] || "TCP"
    connection.create_port_forwarding_rule(ip_address_id, private_port, protocol, public_port, server_id)
  end

end

#find_or_create_public_ip(server, connection) ⇒ Object



237
238
239
240
241
242
243
244
245
246
247
248
249
250
# File 'lib/chef/knife/cs_server_create.rb', line 237

def find_or_create_public_ip(server, connection)
  nic = connection.get_server_default_nic(server) || {}
  puts "#{ui.color("Not allocating public IP for server", :red)}" unless config[:public_ip]

  if (config[:public_ip] == false) || (nic['type'] != 'Virtual') then
    nic['ipaddress']
  else
    # create ip address, ssh forwarding rule and optional forwarding rules
    ip_address = connection.associate_ip_address(server['zoneid'])
    ssh_rule = connection.create_port_forwarding_rule(ip_address['id'], "22", "TCP", "22", server['id'])
    create_port_forwarding_rules(ip_address['id'], server['id'], connection)
    ssh_rule['ipaddress']
  end
end

#is_ssh_open?(ip) ⇒ Boolean

noinspection RubyArgCount,RubyResolve

Returns:

  • (Boolean)


267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
# File 'lib/chef/knife/cs_server_create.rb', line 267

def is_ssh_open?(ip)
  s = Socket.new(Socket::AF_INET, Socket::SOCK_STREAM, 0)
  sa = Socket.sockaddr_in(22, ip)

  begin
    s.connect_nonblock(sa)
  rescue Errno::EINPROGRESS
    resp = IO.select(nil, [s], nil, 1)
    if resp.nil?
      sleep SSH_POLL_INTERVAL
      return false
    end

    begin
      s.connect_nonblock(sa)
    rescue Errno::EISCONN
      Chef::Log.debug("sshd accepting connections on #{ip}")
      yield
      return true
    rescue Errno::ECONNREFUSED, Errno::EHOSTUNREACH
      sleep SSH_POLL_INTERVAL
      return false
    end
  ensure
    s && s.close
  end
end

#locate_config_value(key) ⇒ Object



315
316
317
318
# File 'lib/chef/knife/cs_server_create.rb', line 315

def locate_config_value(key)
  key = key.to_sym
  Chef::Config[:knife][key] || config[key]
end

#runObject



161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
# File 'lib/chef/knife/cs_server_create.rb', line 161

def run

  # validate hostname and options
  hostname = @name_args.first
  unless /^[a-zA-Z0-9][a-zA-Z0-9-]*$/.match hostname then
    ui.error "Invalid hostname. Please specify a short hostname, not an fqdn (e.g. 'myhost' instead of 'myhost.domain.com')."
    exit 1
  end
  validate_options

  $stdout.sync = true

  connection = CloudstackClient::Connection.new(
      locate_config_value(:cloudstack_url),
      locate_config_value(:cloudstack_api_key),
      locate_config_value(:cloudstack_secret_key)
  )

  print "#{ui.color("Waiting for server", :magenta)}"
  server = connection.create_server(
      hostname,
      locate_config_value(:cloudstack_service),
      locate_config_value(:cloudstack_template),
      locate_config_value(:cloudstack_zone),
      locate_config_value(:cloudstack_networks)
  )

  public_ip = find_or_create_public_ip(server, connection)

  puts "\n\n"
  puts "#{ui.color("Name", :cyan)}: #{server['name']}"
  puts "#{ui.color("Public IP", :cyan)}: #{public_ip}"

  return if config[:no_bootstrap]

  print "\n#{ui.color("Waiting for sshd", :magenta)}"

  print(".") until is_ssh_open?(public_ip) {
    sleep BOOTSTRAP_DELAY
    puts "\n"
  }

config[:ssh_password] = server['password']

  bootstrap_for_node(public_ip).run

  puts "\n"
  puts "#{ui.color("Name", :cyan)}: #{server['name']}"
  puts "#{ui.color("Public IP", :cyan)}: #{public_ip}"
  puts "#{ui.color("Environment", :cyan)}: #{config[:environment] || '_default'}"
  puts "#{ui.color("Run List", :cyan)}: #{config[:run_list].join(', ')}"

end

#validate_optionsObject



215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
# File 'lib/chef/knife/cs_server_create.rb', line 215

def validate_options

  unless locate_config_value :cloudstack_template
    ui.error "Cloudstack template not specified"
    exit 1
  end

  unless locate_config_value :cloudstack_service
    ui.error "Cloudstack service offering not specified"
    exit 1
  end

  identity_file = locate_config_value :identity_file
  ssh_user = locate_config_value :ssh_user
  ssh_password = locate_config_value :ssh_password
  unless identity_file || (ssh_user && ssh_password)
    ui.error("You must specify either an ssh identity file or an ssh user and password")
    #exit 1
  end
end