Ansible basics, without which your playbooks are clump of pasta, part 3

In this part we stop talking about the simple and the pleasant and start talking about the difficult. Variables in Ansible: scope, precedence, recursive interpolation. For those who read to the end, a little bonus: a simplified priority table to live with. Previous parts: 1 , 2 .



Usually, a story about variables in Ansible starts with something very simple, which gives the reader the illusion that variables in Ansible are like in any other programming language. Mutable or non-mutable, local and global. This is not true.



This is not true.



Ansible has a unique variable model (memory model?), Which must be learned from scratch. And we will start considering it from the place where the values ​​are used (usually Ansible variables are considered from where they appear). Why? Because when telling in this direction, we have a directed graph, which is much easier to put in our heads.



Note - I said "values" because "variables" are just names to values. Variables have their own deep inner world, and about it in the second part.



Throughout this story, I will use the terms "appears" and / "is used". Appears - this is where the value was set. And "hits" - this is the place where the value begins to influence the work of Ansible, or rather, its side effects - on the real process of executing modules in the target system. While the variables are being moved from place to place in the section vars, the values ​​do not "get" anywhere and do not affect the surrounding world in any way.



This is the first important thought that you need to remember in your head: as long as the value has not been used by something that affects the world around it, it can contain any syntax errors and references to non-existent variables, etc., and this will not confuse anyone. Why so - read on.



So where are the values ​​used?



  1. . - copy: src=foo.conf dest=/etc/foo.conf. foo.conf /etc/foo.conf β€” . . , , , . , jinja, / , (, , , , ).
  2. ( action plugin) 'template' lookup plugin' template. ( lookup plugin , , ).
  3. play. play ( play) play. , jinja2 gather_facts, play. β€” hosts, remote_user .
  4. , . , ansible_host, ansbile_user, ansible_extra_ssh_args, ansible_transport .. .


, :



- name: Do not do this
  file: path=hello.txt state=touch
  loop: '{{ groups.all }}'
  vars:
     ansible_host: '{{ hostvars[item].ansible_host }}'


. "" ( , ).



?



  1. ansible_host ( ) {{ hostvars[item].ansible_host }}. . yaml, .
  2. loop. {{ groups.all }}. β€” jinja. , . loop , , item " ".
  3. hello.txt touch. jinja, .
  4. file ansible_host ssh β€” . , ansible_host Jinja, . , (.. loop). , jinja item, (.. item - ). , . ? . , .


β€” . , , - . set_fact, ( ), .



. , ( best practice) IP- :



allow_access: '{{ ansible_default_ipv4.address }}'


, setup ( gathering_facts), allow_access Jinja , β€” , .



Jinja



β€” Jinja ( ). , , (), ( ). : - . . - , . , . , ( , ..). , Jinja , .



:



- debug:
     msg: '{{ message }}'
  vars:
    foo: 'foo'
    foobar: '{{ foo + "bar" }}'
    message: 'This is {{ foobar }}' 


, 'msg' 'debug' ( , action plugin, ), {{ message }} . ({{ }}) message. This is {{foobar }}. This is {{ foo + "bar" }}. This is foobar. , .. . msg debug.



, , , .



WTF, " ".



- hosts: localhost
  tasks:
    - debug: msg={{foo}}
       vars:
          foo: ''{{ foo + 1 }}'
  vars:
     foo: 1


play, "" foo . play, . debug? msg . , {{ foo + 1 }}, {{ foo + 1 }} + 1 .. , .



:



- hosts: localhost
  tasks:
    - set_fact:
         foo: '{{ foo + 1 }}'
     - debug: msg={{foo}}
  vars:
     foo: 1


""? ? set_fact - foo. foo, . , , foo ( 1) , foo . , .



, , . .



.



- hosts: localhost
  vars:
    foo: '{{ bar * 2 }}'
  tasks:
    - debug: var=foo
      loop: [1,2,3]
      vars:
          bar: '{{ item + 10 }}'


vars play ( ). WTF , .



? 1, 2, 3. :

foo '{{ bar * 2 }}' bar '{{ item + 10 }}'. , vars ( play, ), ( ) , . . , "", - .



debug. foo {{ bar *2 }}, {{ (item + 10) * 2 }}. item ( loop') 22, 24, 26.



β€” , , , , .. scope/precedence ( ), , .



:



- hosts: localhost
  vars:
     foo: '{{ bar }}'
  tasks:
    - debug: var=foo
      vars:
         bar: 'one value'
     - debug: var=foo
       vars:
           bar: 'another value'


. , , bar . Mystery solved.



Jinja



, (, jinja) . jinja. {{ }}, {% if True %} "" {%endif %}. . yaml .



- foo_module:
      username: <
           {% for user in myusers %}
                   {% if user.lower() in good and user.upper() in other %}
                          {{ user }}
                    {% endif %}
           {% endfor %}


'content' file. :



- name: Configure sfcapd systemd service
  become: true
  copy:
    content: |
      [Unit]
      Description=sflow capture service
      [Service]
      Type=simple
      ExecStart=/usr/bin/sfcapd sfcapd -w -T{{ sflow_extensions }} -p {{ sflow_port }} -B {{ sflow_buffer_size }} -l {{ sflow_data_path }} -b {{ sflow_host }}
      Restart=always
      [Install]
      WantedBy=multi-user.target

    dest: /etc/systemd/system/sfcapd.service
  notify:
    - restart sfcapd


? 100500 . , . template, copy content. , .



. .



- (, prometheus Go), . yaml- .



Ansible : safe unsafe. safe- β€” , . unsafe .



:



- debug: 
    msg: !unsafe'{{ inventory_hostname }}'


" ", {{ inventory_hostname }}.

, .



β€” , ? , , : . , , ( ).





, , -, PHP .



.



---
- hosts: localhost
  gather_facts: false
  tasks:
    - name: Case1
      debug: var=item
      loop: '{{ [1,2,3,4] }}'
    - name: Case2
      debug: var=item
      loop: '{{ foo + bar }}'
    - name: Case3
      debug: var=item
      loop: ' {{ [9,10] }}'
  vars:
    foo: '[5,6'
    bar: '7,8]'


Case1 Jinja . , jinja2 , . , yaml'.



Case2 ( β€” , ) β€” , , - , . PHP.



Case3 β€” , .



Case2 Case3 β€” , . , , . ( ) json. , ( json').



Ansible:



    - name: Case6, space at the end
      debug: var=item
      loop: '{{ [15, 16] }} '
    - name: Case7, space at the start
      debug: var=item
      loop: ' {{ [15, 16] }}'


Case6 , loop ( ), Case7 , . ? . .



… . , , WTF' .



: , . 5 , , . ", ". , - β€” .



, : yaml, .



Scope ""



. -"", scope . :



  1. , inventory group_vars.
  2. play, play. ( , "play" β€” , ).
  3. task, .
  4. , . ( β€” ).


include_role , include_role ( ), β€” include_role. , include .



Scope β€” scope scope:



---
- hosts: localhost
  gather_facts: false
  vars:
    foo: 2
  tasks:
    - name: Case1
      debug: var=foo
      vars:
        foo: 1
    - name: Case2
      debug: var=foo


play, foo=1, scope , foo scope play, foo 2. ( precedence ). "" "", ( ). "" β€” include_vars, set_fact, register ..



Variable precedence



. include_role -"". , - scope, , . variable precedence ( β€” , ).



: , . , , http_port: 8088, , http_port: 80 . -e .. , , group_vars/.



, , foo



inventory.yaml   # [all:vars] foo=inventory
group_vars/all.yaml  # foo: g_v_a
playbook.yaml


playbook.yaml



- debug: var=foo


?



… , variable precedence , , group_vars .



, , : host_group_vars .



host_group_vars



( ) host_group_vars. , group vars host vars. , . group_vars/all.yaml , . playbook, playbook.



, , group_vars . , variable precedence (playbook), group_vars .



. group_vars , . , . , , , .. .



group_vars/other, group_vars/all, host_vars .. , .



, , β€” .



. precedence WTF, : ( ), hostvars .





, , WTF'.



  • role/defaults β€” . , . pre/post tasks.
  • group_vars/all β€” all
  • group_vars/other_groups
  • host_vars
  • gather_facts: true (host_facts)
  • play
  • block
  • task
  • set_fact/register. set_fact , (.. play).
  • -e .


, ( ). "". .



: group_vars/all group_vars/other_group.yaml.



,



, . include' (, , ), , import include', add_host, .. set_fact . , . jinja, . group_host_vars () play. , , .



Keep it clean, keep it simple.




All Articles