<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>YAML on Zwindler's Reflection</title><link>https://blog.zwindler.fr/tags/yaml/</link><description>Recent content in YAML on Zwindler's Reflection</description><generator>Hugo -- gohugo.io</generator><language>fr</language><copyright>Licensed under CC BY-SA 4.0</copyright><lastBuildDate>Mon, 07 Feb 2022 07:00:00 +0000</lastBuildDate><atom:link href="https://blog.zwindler.fr/tags/yaml/index.xml" rel="self" type="application/rss+xml"/><item><title>Ansible : subtilités avec « defined » et « skipped »</title><link>https://blog.zwindler.fr/2022/02/07/ansible-subtilite-defined-skipped/</link><pubDate>Mon, 07 Feb 2022 07:00:00 +0000</pubDate><guid>https://blog.zwindler.fr/2022/02/07/ansible-subtilite-defined-skipped/</guid><description>&lt;img src="https://blog.zwindler.fr/2018/10/ansible_logo.webp" alt="Featured image of post Ansible : subtilités avec « defined » et « skipped »" /&gt;&lt;h2 id="ansible-des-fois-cest-pénible"&gt;Ansible des fois c’est pénible
&lt;/h2&gt;&lt;p&gt;Je vous parle souvent d’Ansible car c’est vraiment un outil qui a changé ma vie d’Ops. Pour autant, des fois c’est up peu pénible à comprendre&amp;hellip;&lt;/p&gt;
&lt;p&gt;Dans cet article je vais vous parler d’un de ces moments où j’ai vraiment pesté contre les Devs (c’est pas la première fois&amp;hellip;). L’erreur initiale était mienne (&lt;em&gt;a priori&lt;/em&gt;) mais les workarounds que j’ai testés me paraissaient légitimes&amp;hellip;&lt;/p&gt;
&lt;p&gt;Et cet article va me permettre de vous illustrer 2 concepts utiles pour vos tâches et variables Ansible qui sont skipped et defined.&lt;/p&gt;
&lt;h2 id="cest-lhistoire-de-deux-tâches"&gt;C’est l’histoire de deux tâches
&lt;/h2&gt;&lt;p&gt;Pour remettre les choses dans le contexte : j’ai certaines actions que j’effectue ou non selon les cas sur un groupe d’hôtes donnés. Et c’était typiquement le cas ici…&lt;/p&gt;
&lt;p&gt;&lt;em&gt;Note: le code Ansible en lui-même n’est pas impeccable, on peut (et d’ailleurs, on va) faire beaucoup plus propre ; ce n’est pas ce que je veux montrer ici.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;Voilà les tâches incriminés :&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-yaml" data-lang="yaml"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;- &lt;span class="nt"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;Get some content from a command&amp;#34;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;command&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;command outputting something&amp;#34;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;changed_when&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;register&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;command_output&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;when&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;not_in_test_env&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;- &lt;span class="nt"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;Write command_output to file&amp;#34;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;copy&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;dest&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;{{ command_output_file_path }}&amp;#34;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;content&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;{{ command_output.stdout }}&amp;#34;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Le contenu des deux tâches importe peu, car c’est plus des limitations d’Ansible que je vais vous présenter ensuite qui importe. On pourra retrouver cette limitation dans d’autres cas plus pertinents.&lt;/p&gt;
&lt;p&gt;Ce qu’il faut retenir, c’est que :&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;la première tâche génère un output texte et enregistre le contenu dans une variable &lt;strong&gt;&lt;em&gt;command_output&lt;/em&gt;&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;la seconde tâche copie le contenu de cette variable dans un fichier texte&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Cela pourrait donc être n’importe quelle variable qu’on collecte de n’importe quelle autre façon&amp;hellip; On peut donc légitimement appliquer ce genre d’opération dans d’autres contextes, avec certains nodes où on souhaite exécuter ces deux actions et certains autres, non.&lt;/p&gt;
&lt;h2 id="et-là-cest-le-drame"&gt;Et là, c’est le drame.
&lt;/h2&gt;&lt;p&gt;Lors du moment où j’ai eu mon erreur, &lt;strong&gt;j’étais persuadé d’avoir bien&lt;/strong&gt; &lt;strong&gt;mis un when: not_in_test_env&lt;/strong&gt; pour skip la 2ème tâche aussi si la première l’est. Visiblement ce n’est pas le cas puisque je n’ai pas pu reproduire&amp;hellip;&lt;/p&gt;
&lt;p&gt;Dans le premier cas qui nous intéresse donc, si &lt;em&gt;&lt;strong&gt;not_in_test_env&lt;/strong&gt;&lt;/em&gt; est positionné à &lt;strong&gt;true&lt;/strong&gt;, pas de problème.&lt;/p&gt;
&lt;p&gt;En revanche, si je saute la première partie grâce à la variable &lt;em&gt;&lt;strong&gt;not_in_test_env&lt;/strong&gt;&lt;/em&gt; positionnée à &lt;strong&gt;false&lt;/strong&gt; dans les fichiers de configuration de l’inventaire, vu qu’a priori j’ai fait une erreur et n’ai pas correctement écrit mon when dans la 2ème tâche, la première tâche est bien « Skipped » lors de l’exécution du playbook, mais la tâche suivante « Fail » misérablement.&lt;/p&gt;
&lt;p&gt;Et pour cause&amp;hellip; Ansible essaye à tout prix de résoudre la variable &lt;strong&gt;command_output.stdout&lt;/strong&gt; alors même que la tâche va être « Skipped ». Sur le moment, je n’arrivais pas à comprendre POURQUOI ça n’étais pas skip et j’ai donc essayer de trouver des parades.&lt;/p&gt;
&lt;h2 id="pile-tu-gagnes-"&gt;Pile, tu gagnes, &amp;hellip;
&lt;/h2&gt;&lt;p&gt;Ma première idée a été de tenter de feinter l’erreur en initialisant la variable dans le cas où elle n’est pas renseignée car la tâche est « Skipped ».&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-yaml" data-lang="yaml"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;- &lt;span class="nt"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;Write command_output to file&amp;#34;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;copy&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;dest&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;{{ command_output_file_path }}&amp;#34;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;content&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;{{ command_output.stdout | default(&amp;#39;&amp;#39;) }}&amp;#34;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Manque de bol pour moi&amp;hellip; je suis encore sur une vieille version d’Ansible, le &lt;strong&gt;| default( »)&lt;/strong&gt; ne fonctionne pas sur les sous-variables (ici &lt;strong&gt;.stdout&lt;/strong&gt;).&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Beginning in version 2.8, attempting to access an attribute of an Undefined value in Jinja will return another Undefined value, rather than throwing an error immediately. This means that you can now simply use a default with a value in a nested data structure (in other words, &lt;code&gt;{{ foo.bar.baz | default('DEFAULT') }}&lt;/code&gt;) when you do not know if the intermediate values are defined.&lt;/p&gt;
&lt;p&gt;&lt;a class="link" href="https://docs.ansible.com/ansible/latest/user_guide/playbooks_filters.html" target="_blank" rel="noopener"
&gt;https://docs.ansible.com/ansible/latest/user_guide/playbooks_filters.html&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;strong&gt;| default&lt;/strong&gt; ou pas, Ansible essaye donc de résoudre &lt;strong&gt;command_output.stdout&lt;/strong&gt; ; la tâche continue donc de « Fail »&amp;hellip;&lt;/p&gt;
&lt;h2 id="-face-je-perd"&gt;&amp;hellip; Face, je perd
&lt;/h2&gt;&lt;p&gt;En passant en mode « debug », j’ai remarqué quelque chose que je ne savais pas. Quand on « Skip » une tâche avec un &lt;strong&gt;register:&lt;/strong&gt;, la variable pas enregistrée (car skip) n’est &lt;strong&gt;pas&lt;/strong&gt; vide. Mon problème est que &lt;strong&gt;command_output.stdout&lt;/strong&gt; n’existe effectivement pas, mais &lt;strong&gt;command_output&lt;/strong&gt; si.&lt;/p&gt;
&lt;p&gt;Plus précisément, Ansible injecte une sous entrée &lt;strong&gt;command_output.skipped&lt;/strong&gt; positionnée à « true ».&lt;/p&gt;
&lt;p&gt;Je me suis donc dit : banco ! Je vais modifier ma condition pour qu’elle empêche l’exécution de la tâche si &lt;strong&gt;command_output.skipped&lt;/strong&gt; existe (car ça veut dire que &lt;strong&gt;command_output.stdout&lt;/strong&gt; n’existe pas).&lt;/p&gt;
&lt;p&gt;Ça donne quelque chose comme ça :&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-yaml" data-lang="yaml"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;- &lt;span class="nt"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;Get some content from a command&amp;#34;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;command&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;command outputting something&amp;#34;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;changed_when&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;register&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;command_output&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;when&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;not_in_test_env&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;- &lt;span class="nt"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;Write command_output to file&amp;#34;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;copy&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;dest&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;{{ command_output_file_path }}&amp;#34;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;content&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;{{ command_output.stdout }}&amp;#34;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;when&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;not command_output.skipped&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Et là, dans le cas où &lt;strong&gt;not_in_test_env&lt;/strong&gt; est false, ça ne fail plus.&lt;/p&gt;
&lt;p&gt;Victoire ? Non bien sûr&amp;hellip; car &lt;strong&gt;command_output.skipped&lt;/strong&gt; n’existe pas (pas de skipped = false) quand la tâche est exécutée (c’est à dire « pas skipped »). Retour à la case départ : maintenant ça « Fail » dans le cas où on ne « Skip » plus&amp;hellip;&lt;/p&gt;
&lt;h2 id="sur-la-tranche-je-perds-aussi"&gt;Sur la tranche, je perds aussi
&lt;/h2&gt;&lt;p&gt;Dernière idée, on peut se dire que le plus simple / propre, c’est tout simplement de tester le fait qu’une variable (que ce soit &lt;strong&gt;command_output.stdout&lt;/strong&gt; ou &lt;strong&gt;command_output.skipped&lt;/strong&gt;) soit « defined » pour décider si on exécute ou pas la tâche.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-yaml" data-lang="yaml"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;- &lt;span class="nt"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;Write command_output to file&amp;#34;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;copy&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;dest&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;{{ command_output_file_path }}&amp;#34;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;content&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;{{ command_output.stdout }}&amp;#34;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;when&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;command_output.stdout is defined&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Mais évidemment, pour que la blague soit complète, ce qu’il faut savoir c’est qu’une solution à base de &lt;strong&gt;command_output.skipped is defined&lt;/strong&gt;, ne peut pas marcher&amp;hellip;&lt;/p&gt;
&lt;p&gt;En fait, il n’est pas possible avec Ansible d’utiliser &lt;strong&gt;is defined&lt;/strong&gt; pour vérifier l’existence (ou non) d’un sous élément.&lt;/p&gt;
&lt;h2 id="solutions"&gt;Solutions
&lt;/h2&gt;&lt;p&gt;La solution la plus simple, que je croyais dur comme fer avoir implémenté (mais mes tests additionnels me prouvent que non) c’est simplement d’ajouter un &lt;strong&gt;when: not_in_test_env&lt;/strong&gt; à la 2ème tâche&amp;hellip;&lt;/p&gt;
&lt;p&gt;Dans la même veine, et qui permet en plus d’éviter d’initialiser pour rien &lt;strong&gt;&lt;em&gt;command_output.skipped&lt;/em&gt;&lt;/strong&gt;, il aurait pu être malin de simplement utiliser un &lt;strong&gt;block:&lt;/strong&gt; avec le when, englobant les deux tâches :&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-yaml" data-lang="yaml"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;- &lt;span class="nt"&gt;block&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;- &lt;span class="nt"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;Get some content from a command&amp;#34;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;command&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;command outputting something&amp;#34;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;changed_when&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;register&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;command_output&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;- &lt;span class="nt"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;Write command_output to file&amp;#34;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;copy&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;dest&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;{{ command_output_file_path }}&amp;#34;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;content&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;{{ command_output.stdout }}&amp;#34;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;when&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;not_in_test_env&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Et la vraie solution si jamais vous voulez absolument vérifier si la tâche précédente a été skipped ou pas, est d’utiliser la syntaxe suivante :&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-yaml" data-lang="yaml"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;- &lt;span class="nt"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;Write command_output to file&amp;#34;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;copy&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;dest&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;{{ command_output_file_path }}&amp;#34;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;content&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;{{ command_output.stdout }}&amp;#34;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;when&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;skipped&amp;#39;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;not in command_output&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Cette syntaxe n’est pas hyper évidente de prime abord, mais c’est la seule qui fonctionne dans Ansible pour faire ça&amp;hellip;&lt;/p&gt;
&lt;h2 id="bonus"&gt;Bonus
&lt;/h2&gt;&lt;p&gt;Un lecteur (&amp;lsquo;Jof) m&amp;rsquo;a fait remarquer qu&amp;rsquo;il y a une autre solution qui marche dans tous les cas :&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-yaml" data-lang="yaml"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;- &lt;span class="l"&gt;when command_output|skipped&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Merci à lui !&lt;/p&gt;
&lt;h2 id="conclusion"&gt;Conclusion
&lt;/h2&gt;&lt;p&gt;Le vrai problème ici est quand même que j’avais vraiment besoin de vacances pour ne pas avoir été capable de trouver l’erreur aussi basique ;-).&lt;/p&gt;
&lt;p&gt;La seconde est que j’utilise une version antédiluvienne d’Ansible et que ça irait beaucoup mieux avec des versions plus récentes (pour le &lt;strong&gt;| default&lt;/strong&gt; notamment).&lt;/p&gt;
&lt;p&gt;La dernière chose à retenir est que quand on veut vérifier la présence ou non d’un sous élément dans une variable, le mieux reste d’utiliser &lt;strong&gt;in&lt;/strong&gt; ou &lt;strong&gt;not in&lt;/strong&gt; plutôt que &lt;strong&gt;is defined&lt;/strong&gt; qui ne fonctionne pas dans tous les cas.&lt;/p&gt;
&lt;h2 id="sources-additionnelles"&gt;Sources additionnelles
&lt;/h2&gt;&lt;ul&gt;
&lt;li&gt;&lt;a class="link" href="https://stackoverflow.com/questions/56066503/i-am-having-ansible-issues-with-register-command-when-using-when-in-tasks" target="_blank" rel="noopener"
&gt;stackoverflow.com/questions/56066503/i-am-having-ansible-issues-with-register-command-when-using-when-in-tasks&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="link" href="github.com/ansible/ansible/issues/17500#issuecomment-246370020" &gt;github.com/ansible/ansible/issues/17500#issuecomment-246370020&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="link" href="github.com/ansible/ansible/issues/4297#issuecomment-356427588" &gt;github.com/ansible/ansible/issues/4297#issuecomment-356427588&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</description></item><item><title>Récupérer les informations sur la distribution avec les facts Ansible</title><link>https://blog.zwindler.fr/2018/11/21/recuperer-les-informations-sur-la-distribution-avec-les-facts-ansible/</link><pubDate>Wed, 21 Nov 2018 12:45:40 +0000</pubDate><guid>https://blog.zwindler.fr/2018/11/21/recuperer-les-informations-sur-la-distribution-avec-les-facts-ansible/</guid><description>&lt;img src="https://blog.zwindler.fr/2018/10/ansible_logo.webp" alt="Featured image of post Récupérer les informations sur la distribution avec les facts Ansible" /&gt;&lt;h2 id="les-facts-dansible"&gt;Les facts d’Ansible
&lt;/h2&gt;&lt;p&gt;Si vous suivez le blog, vous savez que &lt;a class="link" href="https://blog.zwindler.fr/recherche/?keyword=ansible" &gt;j’utilise énormément Ansible&lt;/a&gt;. J’ai même été faire un &lt;a class="link" href="https://blog.zwindler.fr/2018/11/09/bdx-i-o-2018-ami-developpeur-deviens-un-ops-sans-effort-avec-ansible" &gt;talk sur le sujet à BDX I/O 2018, à l’ENSEIRB&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Aujourd’hui, plutôt que de vous donner un playbook tout fait pour installer sans effort une des multiples applications dont j’ai automatisé le déploiement avec Ansible, je vous propose d’explorer une des features les plus importantes et utiles d’Ansible, les &lt;strong&gt;facts&lt;/strong&gt;, par le biais d’un petit exemple.&lt;/p&gt;
&lt;h2 id="cest-quoi-les-facts-dans-ansible"&gt;C’est quoi les facts dans Ansible
&lt;/h2&gt;&lt;p&gt;Si vous avez déjà lancé un playbook, vous avez sûrement remarqué lors de son exécution, que la première tâche qui est réalisée n’est pas une tâche que vous avez demandée explicitement.&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;ansible-playbook -i inventory/prod/blop -u blop --private-key=blop.key configure-blop.yml
[...]]
TASK [Gathering Facts] *********************************************************
ok: [blop-vm1]
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Cette tâche &lt;strong&gt;[Gathering Facts]&lt;/strong&gt;, qui ne fait à première vue rien, correspond en fait à la connexion à la ou les machines sur lesquelles seront exécutées les playbooks. Si la connexion échoue, le playbook échouera sur cette cible, et continuera éventuellement sur les autres (s’il en reste). Si l’hôte répond, alors Ansible en profite pour récupérer moult informations en rapport avec cette machines et les stockent dans une variable &lt;strong&gt;ansible_facts&lt;/strong&gt;.&lt;/p&gt;
&lt;h2 id="a-quoi-ça-ressemble-"&gt;A quoi ça ressemble ?
&lt;/h2&gt;&lt;p&gt;Comme je ne disais, à première vue, cette commande de fait rien. Si on ne sait pas qu’elle stocke des informations dans une variables, on ne peut rien en faire.&lt;/p&gt;
&lt;p&gt;Un bon moyen d’avoir un premier aperçu de ce qu’on peut en faire est de les afficher. On peut faire ça avec &lt;a class="link" href="http://docs.ansible.com/ansible/latest/collections/ansible/builtin/setup_module.html" target="_blank" rel="noopener"
&gt;le module « setup »&lt;/a&gt;, qui est en réalité le même module qui est appelé lors de l’étape &lt;strong&gt;[Gathering facts]&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;Le retour se fera au format JSON, ce qui est certes peu digeste, mais facilitera grandement les manipulations de type « filtre » (via le flag filter intégré ou via l’utilitaire &lt;strong&gt;jq&lt;/strong&gt; par exemple).&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;ansible blop-vm1 i inventory/prod/blop -m setup
blop-vm1 | SUCCESS =&amp;gt; {
&amp;#34;ansible_facts&amp;#34;: {
&amp;#34;ansible_all_ipv4_addresses&amp;#34;: [
&amp;#34;10.1.1.10&amp;#34;
],
&amp;#34;ansible_apparmor&amp;#34;: {
&amp;#34;status&amp;#34;: &amp;#34;enabled&amp;#34;
},
[...]
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Je ne vous copie-colle pas tout, rien que pour cette machine, j’ai 719 lignes&amp;hellip; La quantité d’informations disponible est impressionnante, et on peut même en venir à se demander à quoi ça va bien pouvoir nous servir.&lt;/p&gt;
&lt;h2 id="ok-on-en-fait-quoi-"&gt;Ok, on en fait quoi ?
&lt;/h2&gt;&lt;p&gt;Du coup je profite de cet article pour faire un tuto tout simple et qui peut être utile dans de nombreux cas, réaliser des opérations différentes en fonction de la distribution de la machine concernée.&lt;/p&gt;
&lt;p&gt;L’information qui va nous intéresser ici concerne donc les remontées en tant que « nom » ou « version » d’une distribution. Je vais vous économiser la lecture de nos 700+ lignes, et vous indiquer que vous pouvez trouver ces informations en filtrant sur les mots clés « ansible_distribution&amp;hellip; » et « ansible_os&amp;hellip; »&lt;/p&gt;
&lt;p&gt;Voilà ce qu’on pourrait obtenir sur une machine CentOS :&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;ansible blop-vm1 -m setup -a &amp;#39;filter=ansible_distribution*&amp;#39;
blop-vm1 | SUCCESS =&amp;gt; {
&amp;#34;ansible_facts&amp;#34;: {
&amp;#34;ansible_distribution&amp;#34;: &amp;#34;CentOS&amp;#34;,
&amp;#34;ansible_distribution_major_version&amp;#34;: &amp;#34;7&amp;#34;,
&amp;#34;ansible_distribution_release&amp;#34;: &amp;#34;Core&amp;#34;,
&amp;#34;ansible_distribution_version&amp;#34;: &amp;#34;7.2.1511&amp;#34;
},
&amp;#34;changed&amp;#34;: false
}
ansible blop-vm1 -m setup -a &amp;#39;filter=ansible_os_family&amp;#39;
blop-vm1 | SUCCESS =&amp;gt; {
&amp;#34;ansible_facts&amp;#34;: {
&amp;#34;ansible_os_family&amp;#34;: &amp;#34;RedHat&amp;#34;
},
&amp;#34;changed&amp;#34;: false
}
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Et sur une machine Ubuntu :&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;ansible blop-vm2 -m setup -a &amp;#39;filter=ansible_distribution*&amp;#39;
ansible blop-vm2 | SUCCESS =&amp;gt; {
&amp;#34;ansible_facts&amp;#34;: {
&amp;#34;ansible_distribution&amp;#34;: &amp;#34;Ubuntu&amp;#34;,
&amp;#34;ansible_distribution_file_parsed&amp;#34;: true,
&amp;#34;ansible_distribution_file_path&amp;#34;: &amp;#34;/etc/os-release&amp;#34;,
&amp;#34;ansible_distribution_file_variety&amp;#34;: &amp;#34;Debian&amp;#34;,
&amp;#34;ansible_distribution_major_version&amp;#34;: &amp;#34;16&amp;#34;,
&amp;#34;ansible_distribution_release&amp;#34;: &amp;#34;xenial&amp;#34;,
&amp;#34;ansible_distribution_version&amp;#34;: &amp;#34;16.04&amp;#34;
},
&amp;#34;changed&amp;#34;: false
}
ansible blop-vm2 -m setup -a &amp;#39;filter=ansible_os*&amp;#39;
blop-vm2 | SUCCESS =&amp;gt; {
&amp;#34;ansible_facts&amp;#34;: {
&amp;#34;ansible_os_family&amp;#34;: &amp;#34;Debian&amp;#34;
},
&amp;#34;changed&amp;#34;: false
}
&lt;/code&gt;&lt;/pre&gt;&lt;h2 id="utiliser-les-facts-dans-un-playbook"&gt;Utiliser les facts dans un playbook
&lt;/h2&gt;&lt;p&gt;Pour continuer dans l’exemple, on va faire une chose qu’il ne faut jamais faire : désactiver le firewall et SELinux.&lt;/p&gt;
&lt;p&gt;Imaginons que vous souhaitiez quand même le faire. Si vous lancez ce playbook sur tout votre parc et qu’il n’est pas homogène, vous allez tomber sur des groupes de machines Ubuntu, CentOS 5, 6, 7&amp;hellip;&lt;/p&gt;
&lt;p&gt;Un moyen de gérer les cas particuliers pourrait être de les classer tous vos hosts dans des groupes, et de créer un playbook pour chaque OS/version, restreint à un groupe de machines uniquement.&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;---
- hosts: rhelcentos6only
tasks:
[…]
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Ce n’est d’ailleurs pas une mauvaise idée, surtout pour gérer les distrib aussi différentes que RHEL et Ubuntu par exemple.&lt;/p&gt;
&lt;p&gt;En revanche, dans le cas de RHEL, on a des différences qui apparaissent à partir de la RHEL 7, notamment la gestion du Firewall qui passe de IPtables à firewalld.&lt;/p&gt;
&lt;p&gt;On va donc faire appel à la clause &lt;strong&gt;when:&lt;/strong&gt; de ansible, qui conditionne l’exécution d’une tâche (ou d’un rôle ou d’un block) à une validation. Et ça donnerait quelque chose comme ça :&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;---
- hosts: rhelcentos6only
tasks:
- name: &amp;#34;shutdown and disable IPtables for RHEL &amp;lt;= 6&amp;#34;
service:
name=iptables
state=stopped
enabled=no
when: (ansible_distribution == &amp;#34;CentOS&amp;#34; or ansible_distribution == &amp;#34;RedHat&amp;#34;) and
(ansible_distribution_major_version &amp;lt;= &amp;#34;6&amp;#34;)
- name: &amp;#34;shutdown and disable firewalld for RHEL &amp;gt;= 7&amp;#34;
service:
name=firewalld
state=stopped
enabled=no
when: (ansible_distribution == &amp;#34;CentOS&amp;#34; or ansible_distribution == &amp;#34;RedHat&amp;#34;) and
(ansible_distribution_major_version &amp;gt;= &amp;#34;7&amp;#34;)
- name: &amp;#34;disable selinux&amp;#34;
selinux:
state=disabled
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Dans cet exemple, lors de l’exécution du playbook, on aura donc, pour chaque VM CentOS ou RHEL, une tâche qui sera exécutée, et l’autre qui sera marquée « skipping » (en bleu clair) et le playbook marchera pour toutes les RHEL, quelque soit leur version.&lt;/p&gt;
&lt;h2 id="aller-plus-loin"&gt;Aller plus loin
&lt;/h2&gt;&lt;p&gt;Il existe une page spécifique sur la documentation d’Ansible pour parler des conditions dans les playbooks, accessible à l’adresse suivante : &lt;a class="link" href="https://docs.ansible.com/ansible/latest/playbook_guide/playbooks_conditionals.html" target="_blank" rel="noopener"
&gt;https://docs.ansible.com/ansible/latest/playbook_guide/playbooks_conditionals.html&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Vous pouvez aussi utiliser les facts dans les options des tasks, et dans les templates, en encadrant le nom de la variable souhaitée avec des « doubles curly braces » : &lt;code&gt;{{ xxx }}&lt;/code&gt;&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;- command: &amp;#34;echo {{ ma_super_variable }}&amp;#34;
&lt;/code&gt;&lt;/pre&gt;&lt;h2 id="sources"&gt;Sources
&lt;/h2&gt;&lt;ul&gt;
&lt;li&gt;&lt;a class="link" href="http://docs.ansible.com/ansible/latest/collections/ansible/builtin/setup_module.html" target="_blank" rel="noopener"
&gt;docs.ansible.com/ansible/latest/collections/ansible/builtin/setup_module.html&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="link" href="https://docs.ansible.com/ansible/latest/playbook_guide/playbooks_conditionals.html" target="_blank" rel="noopener"
&gt;docs.ansible.com/ansible/latest/playbook_guide/playbooks_conditionals.html&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</description></item><item><title>Se simplifier Kubernetes avec Helm et les Charts</title><link>https://blog.zwindler.fr/2018/02/06/se-simplifier-kubernetes-helm-charts/</link><pubDate>Tue, 06 Feb 2018 12:45:16 +0000</pubDate><guid>https://blog.zwindler.fr/2018/02/06/se-simplifier-kubernetes-helm-charts/</guid><description>&lt;img src="https://blog.zwindler.fr/2018/02/Kubernetes-Helm.webp" alt="Featured image of post Se simplifier Kubernetes avec Helm et les Charts" /&gt;&lt;h2 id="helm-tiller-charts--cest-quoi-tout-ça-"&gt;Helm, Tiller, Charts : c’est quoi tout ça ?
&lt;/h2&gt;&lt;p&gt;Ceux qui suivent un peu le blog savent que &lt;a class="link" href="https://blog.zwindler.fr/recherche/?keyword=Kubernetes" &gt;je bidouille pas mal avec Kubernetes en ce moment&lt;/a&gt;. Dans mon travail précédent on commençait à peut être se dire que Swarm n’allait pas être suffisant (ahaha) et j’avais donc décidé de prendre les devants histoire de savoir de quoi je parle au moment où la décision serait prise de foncer ;-). Et rapidement je me suis rendu compte, qu’avec Kubernetes, on se retrouve vite à devoir écrire du YAML à tour de bras pour configurer nos ressources (&lt;a class="link" href="https://blog.zwindler.fr/2017/10/24/tutoriel-xwiki-ma-premier-appli-stateful-sur-kubernetes/" &gt;« petit » exemple avec XWiki, une application Tomcat + SGBDr&lt;/a&gt;). C’est là que Helm (et Tiller) et les Charts entrent en jeu.&lt;/p&gt;
&lt;p&gt;L’idée derrière Helm et les Charts ? Avoir un magasin en ligne d’applications multi-tiers déployables en une seule ligne de commande.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;The package manager for Kubernetes&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Il faut voir celà comme un magasin un peu sur la philosophie de Dockerhub (applications officielles ou fournies par la communauté), permettant de déployer sur notre cluster des applications potentiellement complexes et/ou multi-tiers de manière automatique (un peu comme un docker-compose mais pour Kubernetes).&lt;/p&gt;
&lt;p&gt;Avant d’aller plus loin, un peu de terminologie :&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Charts&lt;/strong&gt; : Collection de fichiers YAML variabilisés décrivant des ressources qui, misent bout à bout, donnent une application déployable sur Kubernetes&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Helm&lt;/strong&gt; : Client permettant de récupérer des Charts et de les appliquer sur un cluster Kubernetes&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Tiller&lt;/strong&gt; : Partie serveur permettant à un client Helm donner des ordres au cluster Kubernetes visé&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="installation-de-helm"&gt;Installation de Helm
&lt;/h2&gt;&lt;p&gt;Ne faisons pas durer le suspens plus longtemps et attaquons nous à l’installation des composants nécessaires !&lt;/p&gt;
&lt;p&gt;A noter, je pars du principe que vous disposez d’un cluster Kubernetes fonctionnel et que celui ci tourne sous Linux. Si ce n’est pas le cas je vous propose de lire mes tutoriels sur le déploiement de cluster Kubernetes, soit avec &lt;a class="link" href="https://blog.zwindler.fr/2017/10/05/installer-kubernetes-kubespray-ansible/" &gt;Ansible (Kubespray)&lt;/a&gt; soit avec &lt;a class="link" href="https://blog.zwindler.fr/2017/06/07/installer-cluster-kubernetes-vm-centos/" &gt;l’outil Kubeadm&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;A l’heure où j’écris ces lignes, l’outil Helm est disponible à sa version 2.8.0. Vous pouvez &lt;a class="link" href="https://github.com/kubernetes/helm" target="_blank" rel="noopener"
&gt;retrouver la documentation officielle sur le Github&lt;/a&gt; et &lt;a class="link" href="https://docs.helm.sh/using_helm/#quickstart-guide" target="_blank" rel="noopener"
&gt;sur le site de Helm&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;En réalité, il s’agit simplement d’une copie du binaire.&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;curl -o helm.tar.gz https://kubernetes-helm.storage.googleapis.com/helm-v2.8.0-linux-amd64.tar.gz
tar xzf helm.tar.gz
mv linux-amd64/helm /usr/local/bin
&lt;/code&gt;&lt;/pre&gt;&lt;h2 id="contexte"&gt;Contexte
&lt;/h2&gt;&lt;p&gt;Dans le cas où vous ne seriez pas directement sur une des machines du cluster, il est nécessaire de disposer de &lt;strong&gt;kubectl&lt;/strong&gt; et surtout d’avoir correctement configuré son « contexte ».&lt;/p&gt;
&lt;p&gt;Dans le cas où le contexte n’est pas configuré vous aurez le message suivant :&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;kubectl config current-context
error: current-context is not set
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Cependant, si vous vous mettez en SSH directement sur un serveur qui est master, cette étape est potentiellement inutile (le contexte par défaut est administrateur dans ce cas).&lt;/p&gt;
&lt;p&gt;On vérifie qu’on arrive bien à requêter le cluster :&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;kubectl cluster-info
Kubernetes master is running at http://localhost:8080
KubeDNS is running at http://localhost:8080/api/v1/namespaces/kube-system/services/kube-dns/proxy
&lt;/code&gt;&lt;/pre&gt;&lt;h2 id="tiller"&gt;Tiller
&lt;/h2&gt;&lt;p&gt;Maintenant qu’on dispose d’un client Helm ayant accès au contexte de notre cluster, on peut installer Tiller. Utiliser la commande « helm init ». Attention : Tiller sera installé dans le contexte courant, ne vous trompez pas de cluster ;-).&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;helm init
Creating /root/.helm
Creating /root/.helm/repository
Creating /root/.helm/repository/cache
Creating /root/.helm/repository/local
Creating /root/.helm/plugins
Creating /root/.helm/starters
Creating /root/.helm/cache/archive
Creating /root/.helm/repository/repositories.yaml
Adding stable repo with URL: https://kubernetes-charts.storage.googleapis.com
Adding local repo with URL: http://127.0.0.1:8879/charts
$HELM_HOME has been configured at /root/.helm.
Tiller (the Helm server-side component) has been installed into your Kubernetes Cluster.
Happy Helming!
&lt;/code&gt;&lt;/pre&gt;&lt;h2 id="et-maintenant-on-fait-quoi-"&gt;Et maintenant, on fait quoi ?
&lt;/h2&gt;&lt;p&gt;Maintenant qu’on a installé tous les composants dont nous avions besoin (et oui déjà), on va se contenter de dans un premier temps de suivre les exemples fournis. La documentation officielle nous propose de déployer une base MySQL.&lt;/p&gt;
&lt;p&gt;Bonne idée, voyons ce que ça donne !&lt;/p&gt;
&lt;p&gt;La première étape qu’on nous demande de faire est de mettre à jour la liste des Charts disponibles sur le dépôt officiel :&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;helm repo update #mettre à jour la liste des charts disponibles sur le store officiel
Hang tight while we grab the latest from your chart repositories...
...Skip local chart repository
...Successfully got an update from the &amp;#34;stable&amp;#34; chart repository
Update Complete. ⎈ Happy Helming!⎈
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;J’adore le petit caractère « barre de navigateur » ;-)&lt;/p&gt;
&lt;p&gt;Plus sérieusement, l’étape d’après est simplement de faire un &lt;strong&gt;helm install&lt;/strong&gt;, qui va se charger de récupérer tous les YAML nécessaires pour déployer MySQL. La liste des composants Kubernetes créés apparaitra ensuite à l’écran (et vous pouvez les retrouver sur votre cluster avec un &lt;strong&gt;kubectl&lt;/strong&gt;).&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;helm install stable/mysql
NAME: silly-moose
LAST DEPLOYED: Tue Dec 5 10:12:58 2017
NAMESPACE: default
STATUS: DEPLOYED
RESOURCES:
==&amp;gt; v1/Secret
NAME TYPE DATA AGE
silly-moose-mysql Opaque 2 1m
==&amp;gt; v1/PersistentVolumeClaim
NAME STATUS VOLUME CAPACITY ACCESS MODES STORAGECLASS AGE
silly-moose-mysql Pending 1m
==&amp;gt; v1/Service
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
silly-moose-mysql ClusterIP 10.233.54.164 3306/TCP 1m
==&amp;gt; v1beta1/Deployment
NAME DESIRED CURRENT UP-TO-DATE AVAILABLE AGE
silly-moose-mysql 1 1 1 0 1m
==&amp;gt; v1/Pod(related)
NAME READY STATUS RESTARTS AGE
silly-moose-mysql-3998799777-hk5fj 0/1 Pending 0 1m
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Sans surprise on retrouve un &lt;strong&gt;Secret&lt;/strong&gt; (le mot de passe de votre utilisateur), un &lt;strong&gt;PersistentVolumeClaim&lt;/strong&gt; (pour stocker vos données), un &lt;strong&gt;Service&lt;/strong&gt; de type &lt;em&gt;ClusterIP&lt;/em&gt; pour accéder et un &lt;strong&gt;Deployment&lt;/strong&gt; pour MySQL lui même.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;A noter :&lt;/strong&gt; par défaut, Kubernetes donne un &lt;em&gt;petit nom aléatoire&lt;/em&gt; à nos composants pour les rendre uniques, car nous n’avons pas donné de nom en arguments.&lt;/p&gt;
&lt;p&gt;On remarquera la délicate attention des gens qui ont écrit le Chart et qui affiche, une fois le déploiement terminé, les étapes suivantes pour vérifier que tout fonctionne et se connecter une première fois.&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;NOTES:
MySQL can be accessed via port 3306 on the following DNS name from within your cluster:
silly-moose-mysql.default.svc.cluster.local
To get your root password run:
kubectl get secret --namespace default silly-moose-mysql -o jsonpath=&amp;#34;{.data.mysql-root-password}&amp;#34; | base64 --decode; echo
To connect to your database:
1. Run an Ubuntu pod that you can use as a client:
kubectl run -i --tty ubuntu --image=ubuntu:16.04 --restart=Never -- bash -il
2. Install the mysql client:
$ apt-get update &amp;amp;&amp;amp; apt-get install mysql-client -y
3. Connect using the mysql cli, then provide your password:
$ mysql -h silly-moose-mysql -p
&lt;/code&gt;&lt;/pre&gt;&lt;h2 id="ok-je-teste-alors-"&gt;OK je teste alors !
&lt;/h2&gt;&lt;p&gt;Et normalement, si vous essayez, &lt;strong&gt;ça ne marchera pas !&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Pourquoi ça ? On a bien tous les composants pour faire marcher MySQL.&lt;/p&gt;
&lt;p&gt;Tous ?&lt;/p&gt;
&lt;p&gt;Non ! Un irréductible gaulois résiste à l’envahisseur. Vous l’avez trouvé ?&lt;/p&gt;
&lt;p&gt;&lt;img src="https://blog.zwindler.fr/2017/12/pvc_for_mysql.avif"
loading="lazy"
&gt;&lt;/p&gt;
&lt;p&gt;Et oui ! La documentation nous dit juste de faire un &lt;strong&gt;helm install&lt;/strong&gt;&amp;hellip; sans nous rappeler que le &lt;strong&gt;PersistentVolumeClaim&lt;/strong&gt; qui sera créé nécessite &amp;hellip; un &lt;strong&gt;PersistentVolume&lt;/strong&gt; ! Pour ceux qui n’auraient pas révisé leur terminologie Kubernetes, en gros MySQL attend qu’un disque se libère mais aucun n’est disponible.&lt;/p&gt;
&lt;p&gt;Le &lt;strong&gt;Deployment&lt;/strong&gt; se bloque donc à l’étape de &lt;em&gt;Claim&lt;/em&gt; et y restera indéfiniment tant qu’un PV compatible n’est pas disponible.&lt;/p&gt;
&lt;p&gt;&lt;img src="https://blog.zwindler.fr/2017/12/pvc_not_bound.avif"
loading="lazy"
&gt;&lt;/p&gt;
&lt;p&gt;On doit donc créer un PV. Personnellement &lt;a class="link" href="https://blog.zwindler.fr/recherche/?keyword=gluster" &gt;j’ai créé un volume gluster parce que j’aime bien GlusterFS&lt;/a&gt; et j’ai créé le PV manquant dans Kubernetes.&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;gluster volume create gluster-pv-mysql replica 3 node01:/brick/gluster-pv-mysql node02:/brick/gluster-pv-mysql node03:/brick/gluster-pv-mysql force
volume create: gluster-pv-mysql: success: please start the volume to access data
gluster volume start gluster-pv-mysql
volume start: gluster-pv-mysql: success
cat &amp;gt; gluster-pv-mysql.yaml &amp;lt;&amp;lt; EOF
apiVersion: v1
kind: PersistentVolume
metadata:
name: gluster-pv-mysql
spec:
capacity:
storage: 20Gi
accessModes:
- ReadWriteOnce
glusterfs:
endpoints: gluster-cluster
path: /gluster-pv-mysql
readOnly: false
persistentVolumeReclaimPolicy: Delete
EOF
kubectl apply -f gluster-pv-mysql.yaml
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;A partir de là, votre &lt;strong&gt;Deployment&lt;/strong&gt; devrait reprendre la main.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Facile, hein ?&lt;/strong&gt;&lt;/p&gt;
&lt;h2 id="ouais-ok-mais-moi-je-veux-modifier-le-nom-de-ma-base-mysql"&gt;Ouais ok mais moi je veux modifier le nom de ma base MySQL
&lt;/h2&gt;&lt;p&gt;Ok on est capable de déployer MySQL, super. Mais généralement on veut personnaliser les composants qu’on déploie !&lt;/p&gt;
&lt;p&gt;C’est bien évidemment prévu par les Charts, c’est même leur but premier.&lt;/p&gt;
&lt;p&gt;Pour savoir ce qui peut être personnalisé et qui dépendra de chaque &lt;strong&gt;Chart&lt;/strong&gt; que vous utiliserez, pas le choix : il faut aller lire &lt;a class="link" href="https://github.com/kubernetes/charts/tree/master/stable/mysql" target="_blank" rel="noopener"
&gt;la documentation du Chart de MySQL (ici sur Github)&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;&lt;img src="https://blog.zwindler.fr/2017/12/stable_mysql_chart_variables.avif"
loading="lazy"
&gt;&lt;/p&gt;
&lt;p&gt;Pour vous donner un petit exemple de ce qu’on peut faire avec notre MySQL, voici quelques arguments utiles que vous pouvez personnaliser :&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;helm install --name test \
--set mysqlLRootPassword=secretpassword,mysqlUser=zwindler,mysqlPassword=supermotdepasse,mysqlDatabase=zwindlerdb \
stable/mysql
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Après déploiement, on peut retester la connexion via un container ubuntu temporaire (comme la fois d’avant), et s’assurer qu’on peut se connecter sur notre base MySQL avec notre nouvel utilisateur &lt;em&gt;zwindler&lt;/em&gt; et son &lt;em&gt;supermotdepasse&lt;/em&gt; !&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;[root@node01 ~]# kubectl run -i --tty ubuntu --image=ubuntu:16.04 --restart=Never -- bash -il
If you don&amp;#39;t see a command prompt, try pressing enter.
root@ubuntu:/#
root@ubuntu:/# apt-get update &amp;amp;&amp;amp; apt-get install mysql-client -y
[…]
mysql -h test-mysql -u zwindler -p
#supermotdepasse
&lt;/code&gt;&lt;/pre&gt;&lt;h2 id="et-on-nettoie-tout--supprimer-un-chart"&gt;Et on nettoie tout : supprimer un Chart
&lt;/h2&gt;&lt;p&gt;Dans le cas où vous voudriez tout nettoyer d’un coup, vous pouvez utiliser Helm pour supprimer en même temps tous les composants que vous auriez déployés via un « helm install mon-chart ».&lt;/p&gt;
&lt;p&gt;Ici, je peux supprimer l’ensemble des composants qui ont été créés dans cet exemple « silly-moose-mysql ».&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;helm delete silly-moose-mysql
release &amp;#34;silly-moose-mysql&amp;#34; deleted
&lt;/code&gt;&lt;/pre&gt;</description></item><item><title>Automatisation de la supervision avec Centreon et Ansible (3)</title><link>https://blog.zwindler.fr/2016/12/07/automatisation-de-la-supervision-avec-centreon-et-ansible-3/</link><pubDate>Wed, 07 Dec 2016 13:00:47 +0000</pubDate><guid>https://blog.zwindler.fr/2016/12/07/automatisation-de-la-supervision-avec-centreon-et-ansible-3/</guid><description>&lt;img src="https://blog.zwindler.fr/2016/12/centreon_clapi_3.webp" alt="Featured image of post Automatisation de la supervision avec Centreon et Ansible (3)" /&gt;&lt;h2 id="articles-précédents-sur-centreon-et-ansible"&gt;Articles précédents sur Centreon et Ansible
&lt;/h2&gt;&lt;p&gt;Cet article fait suite aux deux articles rédigés en mai 2015 par Cédric Temple (&lt;a class="link" href="http://www.monitoring-fr.org/2015/05/automatisation-de-la-supervision-exemple-avec-centreon-et-ansible-1/" target="_blank" rel="noopener"
&gt;ici&lt;/a&gt; et &lt;a class="link" href="http://www.monitoring-fr.org/2015/05/automatisation-de-la-supervision-exemple-avec-centreon-et-ansible-2/" target="_blank" rel="noopener"
&gt;ici&lt;/a&gt;) qui donnent des pistes pour automatiser l’ajout de nœuds à superviser dans un console Centreon grâce à Ansible et CLAPI.&lt;/p&gt;
&lt;p&gt;Pour ceux qui ne connaissent pas &lt;a class="link" href="https://www.ansible.com/" target="_blank" rel="noopener"
&gt;Ansible&lt;/a&gt;, je vous invite fortement à vous documenter sur cet outil de déploiement et de gestion de configuration très complet et qui bénéficie en ce moment d’un fort engouement, surtout depuis qu’Ansible a été racheté par RedHat ;-).&lt;/p&gt;
&lt;p&gt;Et pour ceux qui ne connaissent pas &lt;a class="link" href="https://docs.centreon.com/fr/docs/api/clapi/" target="_blank" rel="noopener"
&gt;CLAPI&lt;/a&gt;, il s’agit d’une API mise à disposition avec &lt;a class="link" href="https://www.centreon.com/fr/" target="_blank" rel="noopener"
&gt;Centreon&lt;/a&gt;. Elle est installée par défaut dans les dernières versions. Elle permet de réaliser la totalité des opérations pouvant être faites depuis la console web en ligne de commande.&lt;/p&gt;
&lt;p&gt;Les deux articles sur &lt;strong&gt;Monitoring-fr&lt;/strong&gt; que je cite au début sont une bonne base pour appréhender ces deux sujets. Pour autant, on peut encore aller un peu plus loin et c’est pourquoi je vous propose de repartir de là où s’arrête l’article précédent.&lt;/p&gt;
&lt;h2 id="lexemple-de-lajout-de-lhôte"&gt;L’exemple de l’ajout de l’hôte
&lt;/h2&gt;&lt;p&gt;A la fin de l’article précédent, on sait donc :&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;installer automatiquement les clients de supervision sur un nouvel hôte&lt;/li&gt;
&lt;li&gt;et réaliser de manière unitaire via Ansible et des playbooks l’ajout et la modification d’hôtes dans Centreon.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;J’aimerai revenir sur l’ajout d’hôtes dans Centreon. Pour reprendre l’exemple du playbook d’ajout d’hôtes de Cédric :&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;cat centreon_add_host.yml
- name: centreon add host
shell: /usr/share/centreon/www/modules/centreon-clapi/core/centreon -u admin -p admin -o HOST -a add -v &amp;#39;{{ inventory_hostname }};{{ inventory_hostname }};{{ ansible_default_ipv4.address }};generic-host;central;ALL_HOST&amp;#39;
delegate_to: superviseur.company.lan
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Et voici le fichier d’inventaire dans lequel on déclare l’ensemble de nos serveurs, triés par groupes.&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;cat /etc/ansible/hosts #fichier hosts d&amp;#39;Ansible
#ne pas confondre avec le hosts d&amp;#39;un serveur Linux
[serveurs-centreon]
superviseur.company.lan
pollerprod.company.lan
[serveurs-prod]
srv-prod-01
srv-prod-02
[serveurs-rect]
srv-rect-01
srv-rect-02
[nouveaux]
srv-nouveau-01
srv-nouveau-02
[serveurs-linux:children]
serveurs-prod
serveurs-rect
nouveaux
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Dans cet exemple simple, la première chose qu’on se dit si on regarde un peu rapidement, c’est qu’on aurait pu réaliser cet ajout à la main (ou via un script) avec CLAPI.&lt;/p&gt;
&lt;p&gt;Le gain apporté par Ansible se situe « seulement » dans l’apport des variables &lt;em&gt;inventory_hostname&lt;/em&gt; et &lt;em&gt;ansible_default_ipv4.address&lt;/em&gt;.&lt;/p&gt;
&lt;p&gt;Ici on tire partie de la faculté qu’a Ansible de récupérer des « facts » sur les machines, telle que l’adresse IP, que nous n’avons pas eu à renseigner.&lt;/p&gt;
&lt;p&gt;C’est déjà un bon point par rapport à un script ou une commande à la main &amp;hellip; mais on peut faire mieux !!&lt;/p&gt;
&lt;h2 id="aller-plus-loin--avec-les-variables-de-groupes"&gt;Aller plus loin : avec les variables de groupes
&lt;/h2&gt;&lt;p&gt;La première chose que je vais montrer dans cet article est qu’on peut également affecter aux groupes d’hôtes dans Ansible des variables.&lt;/p&gt;
&lt;p&gt;Dans notre exemple, je vais donc créer un répertoire &lt;em&gt;group_vars&lt;/em&gt; dans lequel je vais créer des fichiers de configuration dédiés à chaque groupe. J’en créé un par groupe (ou pas), et Ansible analysera automatiquement le répertoire group_vars et appliquera à mes hôtes toutes ces variables en fonction de ses groupes.&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;cat /etc/ansible/group_vars/serveurs-linux.yml
#NRPE Servers
nrpe_servers: [&amp;#39;192.168.100.10&amp;#39;, &amp;#39;192.168.100.11&amp;#39;]
#Poller Centreon
centreon_server: &amp;#39;superviseur.company.lan&amp;#39;
centreon_pollername : &amp;#39;Central&amp;#39;
centreon_clapi_username : &amp;#39;admin&amp;#39;
centreon_clapi_password : &amp;#39;admin&amp;#39;
centreon_clapi_path : &amp;#39;/usr/share/centreon/www/modules/centreon-clapi/core/centreon&amp;#39;
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Je modifie donc mon playbook de la façon suivante :&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;cat centreon_add_host.yml
---
- name: centreon add host
shell: &amp;#39;{{centreon_clapi_path}}&amp;#39; -u &amp;#39;{{ centreon_clapi_username }}&amp;#39; -p &amp;#39;{{ centreon_clapi_password }}&amp;#39; -o HOST -a add -v &amp;#39;{{ inventory_hostname }};{{ inventory_hostname }};{{ ansible_default_ipv4.address }};generic-host;{{ centreon_pollername }};ALL_HOST&amp;#39;
delegate_to: &amp;#39;{{centreon_server}}&amp;#39;
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Le playbook est beaucoup plus &lt;em&gt;réutilisable&lt;/em&gt; qu’avant. Qu’est ce qui a changé ?&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;J’ai variabilisé le chemin vers le binaire d’appel de CLAPI, qui était quand même vraiment long  (Même si on préfèrera probablement plutôt ajouter le chemin dans le PATH, simplement) !&lt;/li&gt;
&lt;li&gt;Je n’ai plus à renseigner le username et le mot de passe pour CLAPI à chaque appel.&lt;/li&gt;
&lt;li&gt;Si j’ai plusieurs serveurs Centreon (plusieurs réseaux distincts par exemple), je n’ai qu’à créer un groupe et un fichier de variables dans group_vars dans lequel je renseignerai « centreon_server », « clapi_username » et « clapi_password » différents. Mon playbook restera inchangé.&lt;/li&gt;
&lt;li&gt;Si j’ai plusieurs pollers (pollerprod pour la prod dans cet exemple), je peux affecter le serveur directement au bon poller avec la variable « centreon_pollername ».&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="encore-un-peu-plus-loin--avec-les-groupes-et-lhéritage"&gt;Encore un peu plus loin : avec les groupes et l’héritage
&lt;/h2&gt;&lt;p&gt;Dans la configuration exemple que je donne plus haut, vous aurez peut être remarqué le groupe &lt;strong&gt;serveurs-linux&lt;/strong&gt; qui regroupe tous les serveurs des groupes serveurs-prod, serveurs-rect et nouveaux.&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;[serveurs-linux:children]
serveurs-prod
serveurs-rect
nouveaux
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;De cette manière, tous les paramètres communs à tous les serveurs linux peuvent être configurés au niveau serveurs-linux. Les variables seront héritées lors de l’exécution du playbook.&lt;/p&gt;
&lt;p&gt;On peut aussi utiliser les groupes comme conditions de l’exécution d’un playbook. Ainsi, le playbook suivant ne s’exécutera que si le serveur fait parti du groupe serveurs-prod.&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;cat centreon_add_host_prod.yml
---
- name: centreon add host when in serveurs-prod
shell: &amp;#39;{{centreon_clapi_path}}&amp;#39; -u &amp;#39;{{ centreon_clapi_username }}&amp;#39; -p &amp;#39;{{ centreon_clapi_password }}&amp;#39; -o HOST -a add -v &amp;#39;{{ inventory_hostname }};{{ inventory_hostname }};{{ ansible_default_ipv4.address }};generic-host;{{ centreon_pollername }};ALL_HOST&amp;#39;
delegate_to: &amp;#39;{{centreon_server}}&amp;#39;
when: &amp;#34;&amp;#39;serveurs-prod&amp;#39; in group_names&amp;#34;
&lt;/code&gt;&lt;/pre&gt;&lt;h2 id="toujours-plus-loin--avec-les-variables-et-les-templates"&gt;Toujours plus loin : avec les variables et les templates
&lt;/h2&gt;&lt;p&gt;Les variables sont vraiment un gros plus !&lt;/p&gt;
&lt;p&gt;Elles me permettent notamment de configurer les fichiers &lt;strong&gt;nrpe.conf&lt;/strong&gt; (ou &lt;strong&gt;ntpd.conf&lt;/strong&gt;, &lt;strong&gt;snmpd&lt;/strong&gt;, &lt;strong&gt;resolv.conf&lt;/strong&gt;, &amp;hellip;) pour accepter les connexions des bons serveurs en fonction de la localisation ou du groupe.&lt;/p&gt;
&lt;p&gt;La fonctionnalité la plus utile dans ce cas là est l’utilisation de &lt;strong&gt;templates&lt;/strong&gt;. Il s’agit de fichiers de configurations &lt;em&gt;variabilisés&lt;/em&gt; qui sont déployés uniquement si nécessaire sur les serveurs cibles en fonction de leurs variables.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Exemple d’un template de fichier nrpe.conf&lt;/strong&gt;&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;{{ ansible_managed }}
cat nrpe.conf.j2
log_facility=daemon
pid_file=/var/run/nrpe.pid
server_port=5666
nrpe_user=nagios
nrpe_group=nagios
allowed_hosts={% for i in nrpe_servers %} {{ i }}, {% endfor %}
debug=0
command_timeout=60
connection_timeout=300
command[check_users]=/libexec/check_users -w 5 -c 10
[...]
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Le playbook suivant permettra de le déployer sur l’ensemble de mes serveurs CentOS et Redhat le package du client &lt;strong&gt;nrpe&lt;/strong&gt; et ses dépendances et configurer le fichier &lt;strong&gt;nrpe.cfg&lt;/strong&gt; &lt;em&gt;en fonction du contexte&lt;/em&gt; (pour ceux qui n’ont pas encore ce fichier ou une version différente).&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;cat nrpe.yml
---
- hosts: all
tasks:
- yum: name={{ item }} state=installed
with_items:
- nrpe.x86_64
- net-snmp
- sysstat
- ...
- template: src=nrpe.cfg.j2 dest=/etc/nagios/nrpe.cfg
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Une fois exécuté avec la commande suivante, Ansible balayera l’ensemble des serveurs renseignés dans le fichier /etc/ansible/hosts, vérifiera qu’ils ont tous les packages &lt;strong&gt;nrpe&lt;/strong&gt; d’installés, puis déploiera sur les serveurs nécessaires le fichier de configuration en fonction des variables de chacun.&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;ansible-playbook -i /etc/ansible/hosts /etc/ansible/nrpe.yml
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Et dans mon exemple on obtiendra un fichier de la forme suivante :&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;# Ansible managed: nrpe.cfg.j2 modified on 2016-08-14 10:52:51 by ansible on hostname
log_facility=daemon
pid_file=/var/run/nrpe.pid
server_port=5666
nrpe_user=nagios
nrpe_group=nagios
allowed_hosts=192.168.100.10, 192.168.100.11,
debug=0
command_timeout=60
connection_timeout=300
command[check_users]=/libexec/check_users -w 5 -c 10
[...]
&lt;/code&gt;&lt;/pre&gt;&lt;h2 id="mais-ansible-est-avant-tout-un-gestionnaire-de-configuration"&gt;Mais Ansible est avant tout un gestionnaire de configuration
&lt;/h2&gt;&lt;p&gt;Ansible ne sert pas uniquement à déployer des applications sur un serveur ou à exécuter des tâches automatisables. Au delà de ces rôles qu’il rempli à merveille, Ansible est surtout un gestionnaire de configuration (comme Puppet, Salt, &amp;hellip;).&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;Ansible&lt;/em&gt; is the simplest way to automate apps and IT infrastructure. Application Deployment + Configuration Management + Continuous Delivery.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;On le voit avec mon dernier exemple : le but est de garantir qu’un ensemble de serveurs donnés, on a &lt;strong&gt;toujours&lt;/strong&gt; la même configuration.&lt;/p&gt;
&lt;p&gt;C’est le principe fondamental des outils de gestion de configuration et on comprend vite le gain :&lt;/p&gt;
&lt;p style="padding-left: 30px;"&gt;
Si une modification est réalisée par erreur, elle sera automatiquement corrigée au prochain passage du playbook =&gt; on n’aura pas besoin d’attendre qu’un administrateur passe par hasard sur un serveur pour remarquer une anomalie sur le NTP par exemple.
&lt;/p&gt;
&lt;p&gt;Appliqué à la supervision et Centreon&lt;/p&gt;
&lt;p style="padding-left: 30px;"&gt;
Je garanti que tous les serveurs inscris dans Ansible sont automatiquement ajoutés dans Centreon, en fonction de leur groupe. Si un administrateur oublie d’ajouter un serveur ou en supprime un, il sera ré-ajouté à l’exécution d’après sans intervention manuelle.
&lt;/p&gt;
&lt;p&gt;C’est donc de cette manière qu’il est conseillé d’utiliser Ansible. Le playbook n’est pas là pour être joué une fois par serveur, au fil de l’eau, car c’est source d’erreur ou d’oublis. On préférera plutôt exécuter régulièrement l’ensemble des playbooks qui définissent le SI sur l’ensemble des serveurs pour s’assurer qu’ils sont tous « conformes », sur tous les aspects dont la supervision.&lt;/p&gt;
&lt;p&gt;Le réel gain en terme d’automatisation ce trouve ici.&lt;/p&gt;
&lt;h2 id="et-dans-larticle-qui-va-suivre-"&gt;Et dans l’article qui va suivre ?
&lt;/h2&gt;&lt;p&gt;Et c’est sur cette deuxième partie que nous nous attarderons dans le prochain article : la gestion de playbook pour automatiser la configuration de la supervision pour l’ensemble de nos hôtes via des playbooks idempotent (#teasing) !&lt;/p&gt;</description></item></channel></rss>