Class: AnsiblePlaybookProcess

Inherits:
Object
  • Object
show all
Defined in:
modules/ansible/db.rb

Overview

Ansible Playbook run process implementation

Constant Summary collapse

TABLE =

DB Table name

'ansible_playbook_process'
FIELDS =

DB Table columns names

%w(
  uid playbook_id install_id
  create_time start_time end_time
  status log hosts
  vars playbook_name runnable
  comment codes run_after
)
STATUS =

Process states dictionary

{
  '0' => 'PENDING',
  '1' => 'RUNNING',
  'ok' => 'SUCCESS',
  'changed' => 'CHANGED',
  'unreachable' => 'UNREACHABLE',
  'failed' => 'FAILED',
  '6' => 'LOST',
  'done' => 'DONE'
}
DB =

Getting table object from DB object

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(proc_id: nil, playbook_id: nil, uid: nil, hosts: {}, vars: {}, comment: '', _auth: 'default', run_after: {}) ⇒ AnsiblePlaybookProcess

Returns a new instance of AnsiblePlaybookProcess.

Examples:

Hosts example:

hosts: { 'vmid' => [ip:port, credentials]}

Run After example:

{
  "method" => "Reboot",
  "params" => 777 # vmid
} # So VM will be rebooted after

Parameters:

  • proc_id (Integer) (defaults to: nil)
    • Process will be loaded from DB if given

  • playbook_id (Integer) (defaults to: nil)
    • Playbook object ID to use

  • uid (Integer) (defaults to: nil)
    • User ID who initiates the process

  • hosts (Hash) (defaults to: {})
    • see example

  • vars (Hash) (defaults to: {})
    • Variables that should be inserted in PB

  • comment (String) (defaults to: '')
    • Anything you want to tell another users or admins about this Process

  • auth (String)
    • auth driver to use, now is only one supported - default, which uses login and password pair

  • run_after (Hash) (defaults to: {})

Options Hash (run_after:):

  • method (String)
    • IONe method name to call after Ansible will end its work

  • params (Array)
    • Params for this method, see example



298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
# File 'modules/ansible/db.rb', line 298

def initialize proc_id: nil, playbook_id: nil, uid: nil, hosts: {}, vars: {}, comment: '', _auth: 'default', run_after: {}
  if proc_id.nil? then
    @uid, @playbook_id = uid, playbook_id
    @install_id = SecureRandom.uuid + '-' + Date.today.strftime
    @create_time, @start_time, @end_time = Time.now.to_i, -1, -1
    @status = '0'
    @log = ''
    @comment = comment.to_s
    @hosts = hosts
    @vars = vars
    @playbook = AnsiblePlaybook.new(id: @playbook_id)
    @playbook_name, @runnable = @playbook.runnable(@vars).to_a[0]
    @codes = ''
    @run_after = run_after
  else
    @id = proc_id
    sync
  end
rescue
  @playbook = @playbook_name = @runnable = ''
  @status = 'done'
ensure
  allocate if @id.nil?
end

Instance Attribute Details

#end_timeObject (readonly)

Returns the value of attribute end_time.



279
280
281
# File 'modules/ansible/db.rb', line 279

def end_time
  @end_time
end

#hostsObject (readonly)

Returns the value of attribute hosts.



279
280
281
# File 'modules/ansible/db.rb', line 279

def hosts
  @hosts
end

#idObject (readonly)

Returns the value of attribute id.



279
280
281
# File 'modules/ansible/db.rb', line 279

def id
  @id
end

#install_idObject (readonly)

Returns the value of attribute install_id.



279
280
281
# File 'modules/ansible/db.rb', line 279

def install_id
  @install_id
end

#start_timeObject (readonly)

Returns the value of attribute start_time.



279
280
281
# File 'modules/ansible/db.rb', line 279

def start_time
  @start_time
end

Class Method Details

.listObject

Lists all Processes from DB



491
492
493
494
495
496
497
498
# File 'modules/ansible/db.rb', line 491

def self.list
  result = DB.all
  result.map { |pb| pb.to_s! }
  result.each do |app|
    app['status'] = STATUS[app['status']]
  end
  result
end

.new_with_id(proc_id, _client = nil) ⇒ AnsiblePlaybookProcess

OpenNebula::PoolElement-like initializer

Parameters:

Returns:



331
332
333
# File 'modules/ansible/db.rb', line 331

def self.new_with_id proc_id, _client = nil
  self.new(proc_id: proc_id)
end

Instance Method Details

#deleteObject

Sets Process state to Done



454
455
456
457
458
# File 'modules/ansible/db.rb', line 454

def delete
  @status = 'done'
ensure
  update
end

#humanObject

Returns object with State to humanreadable replaced in Hash form



471
472
473
474
475
# File 'modules/ansible/db.rb', line 471

def human
  r = to_hash
  r['status'] = STATUS[r['status']]
  r
end

#run(thread = true) ⇒ Object

Start Process

Parameters:

  • thread (Boolean) (defaults to: true)
    • Runs in another Thread and returns its object if true



337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
# File 'modules/ansible/db.rb', line 337

def run thread = true
  return nil if STATUS.keys.index(@status) > 0

  @start_time, @status = Time.now.to_i, '1'

  update

  process = Proc.new do
    attempt = 0
    begin
      attempt += 1
      Net::SSH.start(ANSIBLE_HOST, ANSIBLE_HOST_USER, :port => ANSIBLE_HOST_PORT) do | ssh |
        # Create local Playbook version
        File.open("/tmp/#{@install_id}.yml", 'w') do |file|
          file.write(@runnable.gsub('<%group%>', @install_id))
        end
        # Upload Playbook to Ansible host
        ssh.sftp.upload!("/tmp/#{@install_id}.yml", "/tmp/#{@install_id}.yml")
        # Create local Hosts File
        File.open("/tmp/#{@install_id}.ini", 'w') do |file|
          file.write("[#{@install_id}]\n")
          @hosts.values.each do |host|
            unless host[1].nil? then
              cred = host[1].split ':'
              cred = "ansible_user=#{cred[0]} ansible_password=#{cred[1]}"
            else
              cred = ''
            end
            file.write("#{host[0]} #{cred}\n")
          end
        end
        # Upload Hosts file
        ssh.sftp.upload!("/tmp/#{@install_id}.ini", "/tmp/#{@install_id}.ini")
        # Creating run log
        ssh.exec!("echo 'START' > /tmp/#{@install_id}.runlog")
        # Run Playbook
        ssh.exec!(
          "ansible-playbook /tmp/#{@install_id}.yml -i /tmp/#{@install_id}.ini >> /tmp/#{@install_id}.runlog; echo 'DONE' >> /tmp/#{@install_id}.runlog"
        )

        @end_time = Time.now.to_i
      end
      clean
      scan
    rescue => e
      @status = 'failed'
      @log += "\nAttempt #{attempt}:\nInternal Error #{e.class}:\n" + e.message + "\n#{'-' * 20}\nBacktrace:\n" + e.backtrace.join("\n")
      @comment = "STRESSTEST with ATTEMPTS"
      update
      retry if attempt < 3
    ensure
      update
    end
  end
  if thread then
    Thread.new do
      process.call
    end
  else
    process.call
  end
ensure
  update
end

#run_afterObject

Runs method from run_after field



478
479
480
481
482
483
484
485
486
487
488
# File 'modules/ansible/db.rb', line 478

def run_after
  return if @run_after['method'].nil?

  if @run_after['params'].nil? then
    IONe.new($client, $db).send(@run_after['method'])
  elsif @run_after['params'].class == Array then
    IONe.new($client, $db).send(@run_after['method'], *@run_after['params'])
  else
    IONe.new($client, $db).send(@run_after['method'], @run_after['params'])
  end
end

#scanObject

Note:

Normally it runs automatically, you shouldn't do it by yourself

Scans Ansible log file after its work end



404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
# File 'modules/ansible/db.rb', line 404

def scan
  return nil if STATUS.keys.index(@status) > 1

  Net::SSH.start(ANSIBLE_HOST, ANSIBLE_HOST_USER, :port => ANSIBLE_HOST_PORT) do | ssh |
    ssh.sftp.download!("/tmp/#{@install_id}.runlog", "/tmp/#{@install_id}.runlog")
    @log = File.read("/tmp/#{@install_id}.runlog")
    if @log.split(/\n/)[-1] == 'DONE' then
      ssh.sftp.remove("/tmp/#{@install_id}.runlog")
      @log.slice!("START\n")
      @log.slice!("\nDONE\n")
    else
      @log = ""
      return
    end
  end if @log == ""

  codes = {}

  @log.split('PLAY RECAP').last.split(/\n/).map do | host |
    host = host.split("\n").last.split(" ")
    next if host.size == 1

    codes.store host[0], {}
    host[-4..-1].map do |code|
      code = code.split("=")
      codes[host[0]].store(code.first, code.last.to_i)
    end
  end

  if codes.values.inject(0) { |sum, vals| sum + vals['failed'] } != 0 then
    @status = 'failed'
  elsif codes.values.inject(0) { |sum, vals| sum + vals['unreachable'] } != 0 then
    @status = 'unreachable'
  else
    @status = codes.values.last.keys.map do | key |
      { key => codes.values.inject(0) { |sum, vals| sum + vals[key] } }
    end.sort_by { |attribute| attribute.values.last }.last.keys.last
  end

  @codes = codes

  run_after
rescue => e
  puts e.message, e.backtrace
  @status = '6'
ensure
  update
end

#statusObject

Returns humanreadable Process state



461
462
463
# File 'modules/ansible/db.rb', line 461

def status
  STATUS[@status]
end

#to_hashObject

Returns object as is in Hash form



466
467
468
# File 'modules/ansible/db.rb', line 466

def to_hash
  get_me
end