« Ansible : Quirks » : différence entre les versions

De Justine's wiki
Aller à la navigation Aller à la recherche
Aucun résumé des modifications
Aucun résumé des modifications
 
(3 versions intermédiaires par la même utilisatrice non affichées)
Ligne 1 : Ligne 1 :
[[Category:Ansible]]
= Variables Utiles & Quirks divers=
= Variables Utiles & Quirks divers=
Ansible contient des variable globales assez utiles. Il contient également beaucoup de fonctions particulières et part un peu dans tous les sens; cependant, on trouve toujours réponse à ses problèmes.
Ansible contient des variable globales assez utiles. Il contient également beaucoup de fonctions particulières et part un peu dans tous les sens; cependant, on trouve toujours réponse à ses problèmes.
Ligne 388 : Ligne 389 :
     - debug: msg="the value of foo.txt is {{ contents }}"
     - debug: msg="the value of foo.txt is {{ contents }}"
</syntaxhighlight>
</syntaxhighlight>
= Rebond SSH =
Dans une configuration réseau en mode Bastion, on peut avoir besoin de faire un rebond pour accéder à nos machines. Dans ce cas, cela demande un peu de configuration (mais rien de compliqué). Ici, les machines du groupe "safe" ne sont accessible que par la machine "gate", qui n'accepte les connexions SSH que sur son port 220 (le port 22 étant pris par, par exemple, un EndleSSH :D ).
<source>
[safe]
foo.example.xyz
bar.example.xyz
baz.example.xyz
[safe:vars]
ansible_ssh_common_args='-o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null -o ProxyCommand="ssh -W %h:%p -q gate.example.xyz -p 220"'
</source>
= Rendre un argument optionnel avec default(omit) =
Dans le cas suivant:
<nowiki>
    - name: Start the container
      community.docker.docker_container:
        name: "{{ container_name }}"
        image: "{{ image_name }}:{{ image_tag | default(latest) }}"
        ports: "{{ container_ports | default(omit) }}"
[...]
</nowiki>
...en ce qui concerne l'argument "ports", on veut que celui-ci ne soit présenté au module docker que si la variable "container_ports" existe. C'est ce que signifie default(omit) : soit ma variable existe et l'argument ports est présenté, soit elle n'existe pas et l'argument n'est pas présenté. C'est vraiment utile pour améliorer la modularité des playbooks !
= Un if/else =
* https://www.redhat.com/sysadmin/ansible-coding-programming
Apparement, Jinja permet de faire des if/else, mais j'ai l'impression que ça ne peut se faire que sur de l'attribuation de variables (grâce à Jinja).
<nowiki>
---
- name: Jinja2 IF
  hosts: localhost
  vars:
    change_provided: True
    job_name: death_star
    change_string: destroy
    current_date_time: 1977-01-01
  tasks:
    - name: Set base schedule name
      ansible.builtin.set_fact:
        base_schedule_name: "{%- if (change_provided | bool) -%}
                            {{ job_name }}_{{ change_string }}
                            {%- else -%}
                            {{ job_name }}_{{ current_date_time }}
                            {%- endif -%}"
    - name: Show value
      ansible.builtin.debug:
        msg: "{{ base_schedule_name }}"
</nowiki>
Je remets l'explication issue de l'article:
* The {% and %} are delimiters indicating a Jinja2 code. In this case, there are extra - marks to indicate to Jinja2 that I want to remove the space characters I have before the variables (the spaces are only for aesthetics).
* If change_provided is true, I combine job_name with the underscore and change_string. Only this content is assigned, and any spaces before or after the variables are omitted because I previously used {%- and -%}.
* The else statement is similar to what you might know from other languages. In this case, I combine job_name with an underscore and current_date_time.
* Finally, the endif closes the if.

Dernière version du 13 septembre 2022 à 12:14

Variables Utiles & Quirks divers

Ansible contient des variable globales assez utiles. Il contient également beaucoup de fonctions particulières et part un peu dans tous les sens; cependant, on trouve toujours réponse à ses problèmes.

Ne pas avoir à ajouter l'hôte au fichier known_hosts

Ajouter dans la partie [group:vars]:

ansible_ssh_common_args='-o StrictHostKeyChecking=no'

Sudo no password pose problème

Quand il est utilisé avec un user nopassword, ansible nous casse les pieds quand il faut faire du sudo. Il faut ajouter la conf suivante dans ansible.cfg:

[defaults]
sudo_flags=-H -S

Des caractères spéciaux dans les mots de passe

En cas d'utilisation d'un fichier d'inventaire chiffré contenant des mots de passe, il faut que ceux-ci soient entre simples quotes afin d'éviter que les caractères spéciaux comme $ ne soient interprétés.

blocks

- name: Attempt and graceful roll back demo
  block:
    - debug:
        msg: 'I execute normally'
    - name: i force a failure
      command: /bin/false
    - debug:
        msg: 'I never execute, due to the above task failing, :-('
  rescue:
    - debug:
        msg: 'I caught an error'
    - name: i force a failure in middle of recovery! >:-)
      command: /bin/false
    - debug:
        msg: 'I also never execute :-('
  always:
    - debug:
        msg: "This always executes"

tags

Il est possible de taguer des tâches afin de "faire du tri". Le but est de pouvoir choisir de n'exécuter par exemple que certains tags (avec --tags), ou de les éviter (avec --skip-tags). Ajouter des tags sur des tâches individuelles est assez simple :

<syntaxhighlight lang='yaml'> tasks: - yum:

   name: "Modèle:Item"
   state: present
 loop:
 - httpd
 - memcached
 tags:
 - packages

- template:

   src: templates/src.j2
   dest: /etc/foo.conf
 tags:
 - configuration

</syntaxhighlight>

Bien entendu, on peut appliquer le même tag à plusieurs tâches.

Héritage des tags

L'héritage de tags, c'est le fait d'ajouter à un play, des tâches importées, ou autre... des tags qui seront appliqués à toutes les tâches incluses. Cela se fait avec la syntaxe "tags: " :

<syntaxhighlight lang='yaml'> - hosts: all

 tags:
 - bar
 tasks:
   ...

- hosts: all

 tags: [ foo ]
 tasks:
   ...

</syntaxhighlight>

tag spécial

Il existe un tags spécial : always. Placer ce tag fait en sorte que l'élément associé sera toujours exécuté.

Accéder à la documentation

La documentation est intégrée ! Il suffit d'utiliser Ansible-doc :

<syntaxhighlight lang='bash'> [justine@argonaut filetree]$ ansible-doc --help Usage: ansible-doc [-l|-F|-s] [options] [-t <plugin type> ] [plugin]

plugin documentation tool

Options:

 -h, --help            show this help message and exit
 -j, --json            **For internal testing only** Dump json metadata for
                       all plugins.
 -l, --list            List available plugins
 -F, --list_files      Show plugin names and their source files without
                       summaries (implies --list)
 -M MODULE_PATH, --module-path=MODULE_PATH
                       prepend colon-separated path(s) to module library (def
                       ault=~/.ansible/plugins/modules:/usr/share/ansible/plu
                       gins/modules)
 -s, --snippet         Show playbook snippet for specified plugin(s)
 -t TYPE, --type=TYPE  Choose which plugin type (defaults to module).
                       Available plugin types are : ('become', 'cache',
                       'callback', 'cliconf', 'connection', 'httpapi',
                       'inventory', 'lookup', 'shell', 'module', 'strategy',
                       'vars')
 -v, --verbose         verbose mode (-vvv for more, -vvvv to enable
                       connection debugging)
 --version             show program's version number, config file location,
                       configured module search path, module location,
                       executable location and exit

See man pages for Ansible CLI options or website for tutorials https://docs.ansible.com </syntaxhighlight>

Le module find

Le module find est un peu casse-pieds. Il si je le register dans "fichiers", alors ma variable fichiers contiendra une liste appellée "files" (et d'autres : cf la doc), laquelle sera une liste de collection clefs-valeurs :

<syntaxhighlight lang="yaml">

       [
       { path: "/var/tmp/test1",
         mode: "0644",
         "...": "...",
         checksum: 16fac7be61a6e4591a33ef4b729c5c3302307523
       },
       { path: "/var/tmp/test2",
         "...": "..."
       },
       ]

</syntaxhighlight>

Y accéder est un peu embêtant. Mettons que j'ai besoin d'accéder au path, je peux faire ça :

<syntaxhighlight lang='yaml'> --- - hosts: all

 become: true
 tasks:
   - name: Récupérer les fichiers
     find:
       patterns: "*log"
       paths: /var/log
       recurse: yes
     register: fichiers
   - name: debuggage
     debug:
       msg: "Modèle:Item.path"
     with_items: "Modèle:Fichiers.files"

</syntaxhighlight>

Mais bon, ce n'est pas génial... Ici, on doit faire une itération sur la liste et extraire les paths à chaque fois.

On peut aussi utiliser un filtre Jinja2:

<syntaxhighlight lang='yaml'> --- - hosts: all

 become: true
 tasks:
   - name: Récupérer les fichiers
     find:
       patterns: "*log"
       paths: /var/log
       recurse: yes
     register: fichiers
   - name: debuggage
     debug:
       msg: "{{ fichiers['files'] | map(attribute='path') | list }}"     

</syntaxhighlight>

Cette fois-ci, on a une liste de tous les paths. Mais bon, ce n'est pas génial non plus. Si j'avais des listes dans des listes, je devrais utiliser with_nested.

Assigner des variables au milieu d'un playbook

Pour cela, il faut utiliser le module set_fact. En reprenant l'exemple juste avant :

<syntaxhighlight lang='yaml'> --- - hosts: all

 become: true
 tasks:
   - name: Récupérer les fichiers
     find:
       patterns: "*log"
       paths: /var/log
       recurse: yes
     register: fichiers
   - name: Une nouvelle variable  
     set_fact:
       chemins: "{{ fichiers['files'] | map(attribute='path') | list }}"
   - name: debuggage
     debug:
       msg: "Modèle:Chemins"     

</syntaxhighlight>

Register les sorties de tasks

On capturer les sorties de tâches avec register. Non seulement ça marche de façon particulière avec certains modules comme find (qui renvoient un truc bien précis à ce moment là), mais il faut savoir que *tous* les modules renvoient quelque chose.

<syntaxhighlight lang='yaml'> - hosts: all

 tasks:
 - name: Ansible register with_items example
   shell: "find *.txt"
   args:
     chdir: "/Users/mdtutorials2/Documents/Ansible"
   register: with_output
 - shell: "cp Modèle:Item Modèle:Item_bkp"
   with_items:
     - "Modèle:With output.stdout lines"
  1. Ici, on s'en sert pour itérer sur la sortie de la commande shell envoyée.

</syntaxhighlight>

Chaque module fait son retour comme il veut; mais les retours sont en général des ensembles de valeurs auxquelles on peut accéder. Par exemple :

<syntaxhighlight lang='yaml'>

   - name: shellcode
     shell: echo hi
     register: hello
     changed_when: false
   - name: debuggage
     debug:
       var: hello

</syntaxhighlight>

Renvoie ça :

<syntaxhighlight lang="yaml"> ok: [localhost] => {

   "hello": {
       "changed": false, 
       "cmd": "echo hi", 
       "delta": "0:00:00.001968", 
       "end": "2019-10-01 23:58:28.548570", 
       "failed": false, 
       "rc": 0, 
       "start": "2019-10-01 23:58:28.546602", 
       "stderr": "", 
       "stderr_lines": [], 
       "stdout": "hi", 
       "stdout_lines": [
           "hi"
       ]
   }

} </syntaxhighlight>

Je peux accéder au return code avec hello.rc :

<syntaxhighlight lang="yaml"> TASK [debug2] ********************************************************************************** ok: [localhost] => {

   "hello.rc": "0"

} </syntaxhighlight>


Gestion de la réentrance (idempotence)

Args : Creates

Utile dans l'utilisation de modules comme command, ou shell. Ces modules peuvent prendre des arguments avec "args:"; l'argument "creates" sert à préciser ce qu'une commande créée sur la machine. Ça sert à garder l'idempotence : Si ma commande est sensée créer un fichier mais que ce fichier existe déjà, elle ne relancera pas la commande.

Attention : ça ne semble pas fonctionner avec raw. Super. Raw ne prend quasiment pas d'arguments, à part le choix de shell; il faut donc bien les construire.

<syntaxhighlight lang='yaml'> --- - hosts: all

 become: true
 tasks:
   - name: Création de fichier
     command: dnf install python -y
     args:
       creates: "/usr/bin/python"

</syntaxhighlight>

Args: changed_when / failed_when

Les modules shell ou command ne sont pas géniaux pour gérer la réentrée. Cependant, on peut leur indiquer quand ce qu'ils font est un changement ou pas (afin d'afficher "changed" ou "ok". Pour ça, on a un autre argument : changed_when. On a aussi failed_when, qui fonctionne sur le même principe. Dans tous les cas, il faut faire un register sur le retour de module. Déjà, on peut annuler complètement les "changed" (si on sait ce qu'on fait) avec false:

<syntaxhighlight lang="yaml"> tasks:

 - shell: /usr/bin/billybass --mode="take me to the river"
   register: bass_result
   changed_when: "bass_result.rc != 2"
 # this will never report 'changed' status
 - shell: wall 'beep'
   changed_when: False

</syntaxhighlight>

On peut aussi annuler le change à certains conditions, principalement du texte dans la sortie standard / d'erreur :

<syntaxhighlight lang="yaml"> - name: Une commande

 shell: /usr/bin/something
 register: _
 changed_when: '"truc" in _.stdout.lower()'

- name: example of many failed_when conditions with OR

 shell: "./myBinary"
 register: ret
 failed_when: >
   ("No such file or directory" in ret.stdout) or
   (ret.stderr != ) or
   (ret.rc == 10)

</syntaxhighlight>

De longues variables

Le linter d'Ansible 'naime pas les lignes de plus de 120 caractères. Quand on a une longue variable, on peut utiliser diverses méthodes.

[ Voir : https://adminswerk.de/multi-line-string-yaml-ansible-I/ ]

                      >     |            "     '     >-     >+     |-     |+
-------------------------|------|-----|-----|-----|------|------|------|------  
Trailing spaces   | Kept | Kept |     |     |     | Kept | Kept | Kept | Kept
Single newline => | _    | \n   | _   | _   | _   | _    |  _   | \n   | \n
Double newline => | \n   | \n\n | \n  | \n  | \n  | \n   |  \n  | \n\n | \n\n
Final newline  => | \n   | \n   |     |     |     |      |  \n  |      | \n
Final dbl nl's => |      |      |     |     |     |      | Kept |      | Kept  
In-line newlines  | No   | No   | No  | \n  | No  | No   | No   | No   | No
Spaceless newlines| No   | No   | No  | \   | No  | No   | No   | No   | No 
Single quote      | '    | '    | '   | '   | ''  | '    | '    | '    | '
Double quote      | "    | "    | "   | \"  | "   | "    | "    | "    | "
Backslash         | \    | \    | \   | \\  | \   | \    | \    | \    | \
" #", ": "        | Ok   | Ok   | No  | Ok  | Ok  | Ok   | Ok   | Ok   | Ok
Can start on same | No   | No   | Yes | Yes | Yes | No   | No   | No   | No
line as key       |

Et pour avoir une longue variable sur plusieurs lignes, sans espaces ni RIEN de rajoutés, il faut juste échapper l'espace et utiliser des doubles quotes:

<syntaxhighlight lang="yaml"> variable: "abcdef\

          ghijkl"

</syntaxhighlight>

Boucler sur une liste + agir de façon conditionnelle (loop, when)

Voir aussi : https://docs.ansible.com/ansible/latest/user_guide/playbooks_conditionals.html#loops-and-conditionals

Ci-dessous, un exemple de boucle avec loop + une condition:

<syntaxhighlight lang='yaml'>

   - name: Ouverture ports
     firewalld:
       port: "Modèle:Item"
       permanent: yes
       state: enabled
     loop: "Modèle:Fwports"
     when: fwports is defined

</syntaxhighlight>

Ici, fwports est une liste définie comme ceci dans mon inventaire :

fwports='["80/tcp", "443/tcp"]'

Lookups

Les lookups permettent d'aller chercher des trucs.

<syntaxhighlight lang="yaml"> - hosts: all

 vars:
    contents: "Modèle:Lookup('file', '/etc/foo.txt')"
 tasks:
    - debug: msg="the value of foo.txt is Modèle:Contents"

</syntaxhighlight>

Rebond SSH

Dans une configuration réseau en mode Bastion, on peut avoir besoin de faire un rebond pour accéder à nos machines. Dans ce cas, cela demande un peu de configuration (mais rien de compliqué). Ici, les machines du groupe "safe" ne sont accessible que par la machine "gate", qui n'accepte les connexions SSH que sur son port 220 (le port 22 étant pris par, par exemple, un EndleSSH :D ).

<source> [safe] foo.example.xyz bar.example.xyz baz.example.xyz

[safe:vars] ansible_ssh_common_args='-o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null -o ProxyCommand="ssh -W %h:%p -q gate.example.xyz -p 220"' </source>

Rendre un argument optionnel avec default(omit)

Dans le cas suivant:

    - name: Start the container
      community.docker.docker_container:
        name: "{{ container_name }}"
        image: "{{ image_name }}:{{ image_tag | default(latest) }}"
        ports: "{{ container_ports | default(omit) }}"
[...]


...en ce qui concerne l'argument "ports", on veut que celui-ci ne soit présenté au module docker que si la variable "container_ports" existe. C'est ce que signifie default(omit) : soit ma variable existe et l'argument ports est présenté, soit elle n'existe pas et l'argument n'est pas présenté. C'est vraiment utile pour améliorer la modularité des playbooks !

Un if/else

Apparement, Jinja permet de faire des if/else, mais j'ai l'impression que ça ne peut se faire que sur de l'attribuation de variables (grâce à Jinja).

---
- name: Jinja2 IF
  hosts: localhost
  vars:
    change_provided: True
    job_name: death_star
    change_string: destroy
    current_date_time: 1977-01-01
  tasks:
    - name: Set base schedule name
      ansible.builtin.set_fact:
        base_schedule_name: "{%- if (change_provided | bool) -%}
                            {{ job_name }}_{{ change_string }}
                            {%- else -%}
                            {{ job_name }}_{{ current_date_time }}
                            {%- endif -%}"

    - name: Show value
      ansible.builtin.debug:
        msg: "{{ base_schedule_name }}"

Je remets l'explication issue de l'article:

  • The {% and %} are delimiters indicating a Jinja2 code. In this case, there are extra - marks to indicate to Jinja2 that I want to remove the space characters I have before the variables (the spaces are only for aesthetics).
  • If change_provided is true, I combine job_name with the underscore and change_string. Only this content is assigned, and any spaces before or after the variables are omitted because I previously used {%- and -%}.
  • The else statement is similar to what you might know from other languages. In this case, I combine job_name with an underscore and current_date_time.
  • Finally, the endif closes the if.