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

$db[:ansible_playbook_process]

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

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

"method" => "Reboot",
"params" => 777 # vmid

# So VM will be rebooted after

Examples:

Hosts example:

Run After example:

Parameters:

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

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

  • uid (Fixnum) (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) (defaults to: 'default')
    • 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



288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
# File 'modules/ansible/db.rb', line 288

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.



269
270
271
# File 'modules/ansible/db.rb', line 269

def end_time
  @end_time
end

#hostsObject (readonly)

Returns the value of attribute hosts.



269
270
271
# File 'modules/ansible/db.rb', line 269

def hosts
  @hosts
end

#idObject (readonly)

Returns the value of attribute id.



269
270
271
# File 'modules/ansible/db.rb', line 269

def id
  @id
end

#install_idObject (readonly)

Returns the value of attribute install_id.



269
270
271
# File 'modules/ansible/db.rb', line 269

def install_id
  @install_id
end

#start_timeObject (readonly)

Returns the value of attribute start_time.



269
270
271
# File 'modules/ansible/db.rb', line 269

def start_time
  @start_time
end

Class Method Details

.listObject

Lists all Processes from DB



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

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:

  • proc_id (Fixnum)
    • Process ID

  • client (OpenNebula::Client) (defaults to: nil)
    • not required

Returns:



320
321
322
# File 'modules/ansible/db.rb', line 320

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



437
438
439
440
441
# File 'modules/ansible/db.rb', line 437

def delete
    @status = 'done'
ensure
    update
end

#humanObject

Returns object with State to humanreadable replaced in Hash form



451
452
453
454
455
# File 'modules/ansible/db.rb', line 451

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



326
327
328
329
330
331
332
333
334
335
336
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
# File 'modules/ansible/db.rb', line 326

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



457
458
459
460
461
462
463
464
465
466
467
# File 'modules/ansible/db.rb', line 457

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



390
391
392
393
394
395
396
397
398
399
400
401
402
403
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
# File 'modules/ansible/db.rb', line 390

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



443
444
445
# File 'modules/ansible/db.rb', line 443

def status
    STATUS[@status]
end

#to_hashObject

Returns object as is in Hash form



447
448
449
# File 'modules/ansible/db.rb', line 447

def to_hash
    get_me
end