Playbooks

As we already learn in the other lessons, playbooks are yaml files where we group tasks.

A playbook starts with an hosts mark specifying the hosts to run the playbook to.

# Yaml files usually start with 3 dashes: ---
# It's just a separator.
---
- hosts: web
  tasks: 
  ...

In yaml, a playbook is a list of hosts entries.

- hosts: localhost
  tasks:
  - name: one or more tasks to be run on localhost
    ...

- hosts: web
  tasks:
  - name: followed by tasks to be run on web hostgroup
    ...

We can even pick a single element in an host group, or add tags entries to restrict executions

- hosts: db[0]
  tags: beware
  tasks:
  - name: .. and then on the first db node ;)
          ...
  - name: run many tasks...

You can limit execution of a single task on a single host within a group using run_once .

- hosts: db
  tags: beware
  tasks:
  - name: Only this task is run on one of the db nodes
    run_once: yes

  - name: Other tasks are run on all nodes!

We can even delegate the execution on a given host (eg. the ansible one)

- hosts: db
  tasks:
  - name: run this task from the local ansible host
    run_once: yes
    delegate_to: localhost
    shell: |
      remove a node from the balancer

In [ ]:
cd /notebooks/exercise-07

Goal

  • fact gathering (hostvars)
  • user and skel
  • install packages and enable service (not on containers)
  • lineinfile
  • curl
  • file, copy & fetch

Creating small reports.

More fun with:

  • iterations
  • ignore_errors
  • with_fileglob and remote_fileglob ;)

Creating small reports.

Gathering facts

When run, a playbook gathers facts about hosts (remember the setup module?).

Gathering facts may be time-consuming, so you can tune it via ansible.cfg or disable it

- hosts: web
  gather_facts: no
  tasks:
  ...

We can use the predefined variables too:

  • group_names
  • groups
  • hostvars
  • environment

In [ ]:
!ansible-playbook debug.yml --tags gather_facts

Exercise:

modify debug.yml to disable fact gathering and use the following cell to test it


In [ ]:
# Test the exercise here

Hints

  • use ansible_facts as possible
  • avoid gathering facts with uname & co
  • test and template your iterations statically instead of continuosly gathering facts

hostvars

Ansible allows referencing facts between hosts, so that we're able to get eg. a list of all webserver ip addresses.

Between set variables we have:

  • hostvars
  • groups

In [ ]:
!ansible-playbook debug.yml --tags hostvars

Exercise

Did you note anything about the hostvars keys printed out by the previous command?

Answer: they are related to the setup module output

Filters

We can process hostvars using jinja filters (see the facts and variable lesson).

Here is a list of useful filters

Remember: a filter is essentially a function returning a function, like a python lambda.

int_filter = lambda x: int(x)

A more complex filter:

- name: This is a getter
  debug:
    msg: >
      {{ ['host1', 'host2'] | 
           map('extract', hostvars, ['key1', .. , 'keyN']) 
      }}

where

hostvars_getter = lambda host: hostvars[host]['key1'][..]['keyN']

You can pipeline filters and test incrementally.


In [ ]:
!ansible-playbook debug.yml --tags filters

Packages, User and Files

Creating user and installing packages is easy

  - name: Install apache
    apt: item="{{item}}" state=present
    with_items:
    - apache2
    - curl

  - name: Remove wget
    apt: item=wget state=absent

In [ ]:
!cat package-user.yml

In [ ]:
!ansible-playbook package-user.yml

In [ ]:
!tree /tmp/fetched/

Files and Directories

Creating files and directories.

lineinfile / blockinfile

with_fileglob


In [ ]:
!cat files-content.yml

In [ ]:
!ansible-playbook -i inventory files-content.yml

shell module reloaded

Ansible can be used to reproduce issues and gather command output.

Though the standard workflow can be done registering output in a temporary variable


In [ ]:
!cat shell-output-01.yml

In [ ]:
!ansible-playbook shell-output-01.yml

This approach has its limits.

Exercise

Exercise

modify shell-output-01.yml so that:

  • every command stdout/stderr is redirected to a given file
  • before and after every command output print a header and a footer (eg. the expected output is like
--- START COMMAND: cat /etc/resolv.conf --
nameserver 172.17.0.1
-- END COMMAND: cat /etc/resolv.conf

HINTS:

  • use shell redirection instead of register
  • use with_items to process many different commands

In [ ]:
!ansible-playbook shell-output-02.yml -i ../web

using shell for testing, changing hints

In ansible, shell and command calls:

  • always changes the machine state and are colored with brown;
  • are skipped by ansible-playbook --check to avoid accidental modifications.

If you know a task is not modifying the host (eg. pgrep, ls) you can set changed_when: no.

If you want to run a shell|command task when --check, disable the check_mode control.

Here's a full example!

  - name: This shell task will not modify the system
    shell: |
      pgrep -fa tomcat
    changed_when: no
    check_mode: no

Exercise

Write the changed-when.yml playbook which:

  • checks for the following processes on all nodes: ansible, ssh, oracle
  • checks for the presence of /tmp/didit.txt file
  • all tests should be run even with --check
  • creates an empty /tmp/didit.txt file only when not run in --check

Does changed_when changes ansible behavior when a command fails?


In [ ]:
!ansible-playbook --check changed-when.yml

In [ ]:
!ansible-playbook changed-when.yml

Server configuration

Server configuration modules include:

- systemd
- service
- mount


service manages and enables services

 - name: Restart httpd
   service: 
     name: httpd
     state: restarted
     enabled: true

systemd manages and enables services with systemd. It can reload systemd configuration too


  - name: Reload docker with new systemd config
    systemd:
      state: restarted
      name: docker
      daemon_reload: yes

mount populates entries in /etc/fstab and mounts associate filesystem.

  - name: Check if mongod LUN is mounted
    register: mongod_on_storage
    mount:
      path: /var/lib/mongo
      src: /dev/sdc
      fstype: xfs
      state: present  # creates an entry in /etc/fstab. `mounted` does the actual mount.

Exercise

Write the mount-bind.yml playbook which:

  • runs only on ONE host of the web group
  • creates a bind mountpoint from /var to /mnt/bind into /etc/fstab, eventually creating /mnt/bind
  • mount the new fstab entry
  • all tests should be run even with --check

This playbook is expected to fail in this course environment: try to understand why.


In [ ]:
!ansible-playbook --check mount-bind.yml

In [ ]:
!ansible-playbook -v mount-bind.yml

In [ ]: