====== SaltStack ====== * {{:tuto:linux:install-saltstack.pdf|Tuto 1}} * [[https://blog.talpor.com/2014/07/saltstack-beginners-tutorial/|Tuto 2]] * Tuto 3 : * https://docs.saltstack.com/en/latest/ * https://docs.saltstack.com/en/getstarted/overview.html * https://docs.saltstack.com/en/getstarted/fundamentals/index.html * [[https://www.youtube.com/watch?v=8yDML4CbXH0|Tuto Video 1]] * [[https://github.com/saltstack/salt/wiki/Cheat-Sheet|Pense bête rapide]] ===== Configurer le master ===== $ systemctl start salt-master $ systemctl enable salt-master ===== Configurer le minion ===== * Editer le fichier ''/etc/salt/minion.d/master.conf'' et indiquer lui votre master : master: salt-master * On démarre et active au boot le service : $ systemctl start salt-minion $ systemctl enable salt-minion * On voit apparaître portable.localdomain dans les liste des minions qui n'ont pas encore été acceptés : salt-key -L a Accepted Keys: Denied Keys: Unaccepted Keys: portable.localdomain * On génère une fingerprint pour ce minion sur le minion : salt-call key.finger --local local: 14:e6:21:3b:0c:f9:0a:96:ea:5a:69:bb:2e:82:b4:c7:18:3c:4f:09:40:d2:46:8f:f9:09:e9:81:87:15:1a:0f * On donne cette figerprint à portable.localdomain : salt-key -f portable.localdomain Unaccepted Keys: portable.localdomain: 14:e6:21:3b:0c:f9:0a:96:ea:5a:69:bb:2e:82:b4:c7:18:3c:4f:09:40:d2:46:8f:f9:09:e9:81:87:15:1a:0f * Puis on accepte la clé : salt-key -a portable.localdomain The following keys are going to be accepted: Unaccepted Keys: portable.localdomain Proceed? [n/Y] Key for minion portable.localdomain accepted. * On vérifie les clés : salt-key -L a [sudo] Mot de passe de gigix :  Accepted Keys: portable.localdomain Denied Keys: Unaccepted Keys: Rejected Keys: * On vérifie le fonctionnement : sudo salt 'portable.localdomain' test.ping portable.localdomain: True * Sécurisons le minion avec la fingerprint du master. On récupère sa fingerprint publique : salt-key -f master.pub Local Keys: master.pub: 1b:5e:6d:ae:20:65:dd:f1:62:0b:9b:85:eb:3a:38:8d:ec:d6:cf:98:06:3d:ca:65:fb:a7:dd:c6:86:15:bb:96 * On édite le fichier ''/etc/salt/minion.d/master.conf'' : master: localhost master_finger: '1b:5e:6d:ae:20:65:dd:f1:62:0b:9b:85:eb:3a:38:8d:ec:d6:cf:98:06:3d:ca:65:fb:a7:dd:c6:86:15:bb:96' * On restart le service : systemctl restart salt-minion * On teste à nouveau : salt 'portable.localdomain' test.ping portable.localdomain: True ===== Afficher les clés ===== * La clé locale du minion: salt-call key.finger --local local: 14:e6:21:3b:0c:f9:0a:96:ea:5a:69:bb:2e:82:b4:c7:18:3c:4f:09:40:d2:46:8f:f9:09:e9:81:87:15:1a:0f * Les clés du master : salt-key -F Local Keys: master.pem: 83:9f:f3:37:c6:6c:f8:55:5e:84:c7:60:4d:10:59:ce:a2:81:23:47:1a:bf:df:c1:6f:f1:3f:85:48:ee:8a:b0 master.pub: 1b:5e:6d:ae:20:65:dd:f1:62:0b:9b:85:eb:3a:38:8d:ec:d6:cf:98:06:3d:ca:65:fb:a7:dd:c6:86:15:bb:96 Accepted Keys: portable.localdomain: 14:e6:21:3b:0c:f9:0a:96:ea:5a:69:bb:2e:82:b4:c7:18:3c:4f:09:40:d2:46:8f:f9:09:e9:81:87:15:1a:0f ===== Rafraichir les Pillar / Grains / .. ===== Les pillars sont chargés quand le minion démarre, ou on peu le faire manuellement : salt '*' saltutil.refresh_pillar salt '*' saltutil.refresh_grains Tout resynchroniser : salt '*' saltutil.sync_all ===== Exemples ===== **Client en standalone (serverless) :** salt-call --local grains.items **Exécution sur le master** : salt-run test.foo **Exécution vers un cloud provider** (https://docs.saltstack.com/en/latest/topics/cloud/index.html) : salt-cloud **Client vers serveur :** salt-call grains.items **Serveur vers clients via SSH (agentless) :** salt-ssh -i --roster-file=./roster 'node1' test.ping Exemple de fichier roster ([[https://docs.saltstack.com/en/latest/topics/ssh/roster.html|toutes les options du fichier roster]]) : node1: host: 192.168.122.1 user: root priv: /root/.ssh/id_rsa **Serveur vers clients via client salt :** salt '*' cmd.run 'ls -l /etc' salt '*' test.ping **Autres :** salt '*' cmd.run 'echo "Hello: $FIRST_NAME"' saltenv='{FIRST_NAME: "Joe"}' salt myminion grains.item pythonpath --out=pprint salt '*' cp.get_file salt://vimrc /etc/vimrc salt '*' cp.get_file "salt://{{grains.os}}/vimrc" /etc/vimrc template=jinja salt '*' -b 10 test.ping salt -G 'os:RedHat' --batch-size 25% apache.signal restart base: 'not webserv* and G@os:Debian or E@web-dc1-srv.* and ( ms-1 or G@id:ms-3 ) or J|@foo|bar|^foo:bar$': - match: compound - webserver ===== Documentation ===== States : salt '*' sys.doc salt '*' sys.doc cmd salt '*' sys.doc cmd.run Runner : salt-run doc.runner ===== Structure d'un SLS ===== : - - : : [] # standard declaration : : - - - - - : - : - - # inline function and names : .: - - - - : - - - - : - - # multiple states for single id : : - - - : - : - : - - - : - - - : - * Corrrecte : httpd: pkg.installed: [] Ou httpd: pkg: - installed httpd: pkg.installed: [] service.running: [] ius: pkgrepo.managed: - humanname: IUS Community Packages for Enterprise Linux 6 - $basearch - gpgcheck: 1 - baseurl: http://mirror.rackspace.com/ius/stable/CentOS/6/$basearch - gpgkey: http://dl.iuscommunity.org/pub/ius/IUS-COMMUNITY-GPG-KEY - names: - ius - ius-devel: - baseurl: http://mirror.rackspace.com/ius/development/CentOS/6/$basearch * Incorrecte : httpd: pkg.installed service.running ===== Exécution des sls ===== * Exécuter toutes les SLS : salt '*' state.apply * Exécuter la sls gigix : salt '*' state.apply gigix * Avec des paramètres et plusieurs sls : salt '*' state.apply mysls1,mysls2 test=true saltenv=dev pillarenv=dev pillar='{"key": "val"}' exclude="[{'id': 'id_to_exclude'}, {'sls': 'sls_to_exclude'}]" * Avec targetting par Pillar : salt --pillar 'webserver_role:dev' state.apply * Seulement un id d'une sls : salt-call --local --file-root=$PWD state.sls_id this_state_will_return_changes test local: ---------- ID: this_state_will_return_changes Function: test.succeed_with_changes Result: True Comment: Success! Started: 00:38:08.528352 Duration: 0.287 ms Changes: ---------- testing: ---------- new: Something pretended to change old: Unchanged Summary for local ------------ Succeeded: 1 (changed=1) Failed: 0 ------------ Total states run: 1 Total run time: 0.287 ms ===== Targeting ===== https://docs.saltstack.com/en/latest/ref/states/top.html#advanced-minion-targeting * globbing sur le nom des minions : salt 'web[1-5]' test.ping salt 'web[1,3]' test.ping salt '*.example.*' test.ping salt 'web?.example.net' test.ping base: 'web[1,3]': - match: glob - webserver * regexp sur le nom des minions salt -E 'web1-(prod|devel)' test.ping base: 'web1-(prod|devel)': - match: pcre - webserver * Liste de minion : salt -L 'web1,web2,web3' test.ping base: 'web1,web2,web3': - match: list - webserver * Grain : salt -G 'os:Fedora' test.ping 'node_type:webserver': - match: grain - webserver * Grain avec regexp : salt 'P@os:(RedHat|Fedora|CentOS)' test.ping 'os:Fedo.*': - match: grain_pcre - webserver * Pillar exacte : 'cheese:foo': - match: pillar_exact - webserver * Pillar globing : salt -I 'somekey:specialvalue' test.ping salt -I 'foo:bar:baz*' test.ping 'cheese:foo:baz*': - match: pillar - webserver * Pillar avec regexp : salt 'J@pdata:^(foo|bar)$' test.ping 'cheese:(swiss|american)': - match: pillar_pcre - webserver * Groupes (les nodesgroups sont à déclarer dans le fichier ''/etc/salt/master'') : nodegroups: group1: 'L@foo.domain.com,bar.domain.com,baz.domain.com and bl*.domain.com' group2: 'G@os:Debian and foo.domain.com' group3: 'G@os:Debian and N@group1' base: group1: - match: nodegroup - webserver * Network : salt -S 192.168.40.20 test.ping salt -S 2001:db8::/64 test.ping '172.16.0.0/12': - match: ipcidr - internal * Multiples : salt -C 'G@os:Debian and webser* or E@db.*' test.ping salt -C '* and not G@kernel:Darwin' test.ping salt -C '( ms-1 or G@id:ms-3 ) and G@id:ms-3' test.ping base: 'webserv* and G@os:Debian or E@web-dc1-srv.*': - match: compound - webserver * Range cluster (non supporté de base) : salt 'R@%test:APPS' test.ping CLUSTER: host1..100.test.com APPS: - frontend - backend - mysql * data ([[https://docs.saltstack.com/en/latest/ref/modules/all/salt.modules.data.html#module-salt.modules.data|avec le minion datastore]]) : ===== Désactiver un sls ===== salt '*' state.disable mysql Pour lister ceux qui ont été désactivés : salt '*' state.list_disabled ===== Réactiver un sls ===== salt '*' state.enable gigix ===== Voir les sls ===== salt '*' state.show_highstate salt '*' state.show_lowstate salt '*' state.show_low_sls foo saltenv=dev salt '*' state.show_sls core,edit.vim dev Voir le contenu top sls des serveurs : salt '*' state.show_top ===== Voir les sls en cours d'exécution ===== salt '*' state.running local: - The function "state.apply" is running as PID 13972 and was started at 2018, Mar 03 13:14:07.242163 with jid 20180303131407242163 ===== Voir les statistiques d'exécution des sls ===== salt '*' state.show_state_usage ===== Récupérer une variable ===== * Récupérer un Pillar : {{ salt['pillar.get']('foo:bar:baz', 'qux') }} * Récupérer un Grain : {{ salt['grains.get']('node_type', '') }} ===== Lister Pillar / Grains ===== * Grains : salt '*' grains.items * Pillar salt '*' pillar.items ===== Définir une variable ===== * Définir une variable : {% set the_node_type = salt['grains.get']('node_type', '') %} {% set name = { 'RedHat': 'httpd', 'Debian': 'apache2', }.get(grains.os_family) %} ===== Executer un module ===== * Appeler un module : {{ salt['module.function_name']('argument_1', 'argument_2') }} {{ salt['cmd.run']('pgrep -f redis-server') }} ===== Conditions ===== {% set ethhb = salt['pillar.get']('eth-hb', 'bond1') %} {% if grains['ip4_interfaces'][ethhb][0].startswith('192.168.1.') %} host.present: - ip: {{ grains['ip4_interfaces'][ethhb][0] }} - names: - {{ grains['host'] }}-hb {% else %} cmd.run: - name: "echo '{{ ethhb }} is not properly configured !'; exit 1" {% endif %} ===== Boucles ===== {% for user, uid in pillar.get('users', {}).items() %} {{user}}: user.present: - uid: {{uid}} {% endfor %} ===== Macro ===== La maccro est une sorte de fonction : # macro message {% macro message(name, comment, changes=False, result=False, failhard=True) -%} pacemaker-{{ sls }}-{{ name }}: test.configurable_test_state: - name: pacemaker-{{ sls }}-{{ name }} - changes: {{ changes }} - result: {{ result }} - comment: {{ comment }} - failhard: {{ failhard }} {%- endmacro %} {{ message('error-test', 'mon erreur !!!', failhard=False) }} ===== Quitter lors d'une erreur ===== Avec l'option **failhard** : /etc/yum.repos.d/company.repo: file.managed: - source: salt://company/yumrepo.conf - user: root - group: root - mode: 644 - order: 1 - failhard: True ===== Ordonnancement ===== L'ordonnancement est possible grâce à **require** ou **require_in** (sa dépendance inversée) : httpd: pkg.installed: [] service.running: - require: - pkg: httpd - sls: network vim: pkg.installed: - require_in: - file: /etc/vimrc /etc/vimrc: file.managed: - source: salt://edit/vimrc On peu également faire appel à ''order'' : apache: pkg.installed: - name: httpd - order: 1 Pour désigner le dernier : ''order: last'' ou ''order: -1''. ===== Exécution sur changement ===== * https://docs.saltstack.com/en/latest/ref/states/requisites.html L'exécution sur changement est possible grâce à **onchanges** ou **onchanges_in** (sa dépendance inversée). S'exécute seulement si le résultat de l'exécution cible est un succès et si la cible a des changements : Deploy server package: file.managed: - name: /usr/local/share/myapp.tar.xz - source: salt://myapp.tar.xz Extract server package: archive.extracted: - name: /usr/local/share/myapp - source: /usr/local/share/myapp.tar.xz - archive_format: tar - onchanges: - file: Deploy server package # (Archive arguments omitted for simplicity) # Match by ID declaration Extract server package: archive.extracted: - onchanges: - file: Deploy server package # Match by name parameter Extract server package: archive.extracted: - onchanges: - file: /usr/local/share/myapp.tar.xz Cela se fait également avec **watch** et **watch_in**. **watch** agit comme **require**, mais ajoute un comportement supplémentaire (mod_watch). C'est à dire que si le module à une fonction //mod_watch//alors celle-ci sera appellée. Tous les modules n'ont pas de fonction //mod_watch// et s'exécuteront donc comme un simple **require** : ntpd: service.running: - watch: - file: /etc/ntp.conf file.managed: - name: /etc/ntp.conf - source: salt://ntp/files/ntp.conf On peu également le faire avec [[https://docs.saltstack.com/en/latest/ref/states/requisites.html#listen-listen-in|listen /listen_in]]. Contrairement à [[https://docs.saltstack.com/en/latest/ref/states/requisites.html#watch|watch et watch_in]], listen et listen_in ne modifieront pas l'ordre des états et peuvent être utilisés pour s'assurer que vos états sont exécutés dans l'ordre dans lequel ils sont définis. restart-apache2: service.running: - name: apache2 - listen: - file: /etc/apache2/apache2.conf configure-apache2: file.managed: - name: /etc/apache2/apache2.conf - source: salt://apache2/apache2.conf restart-apache2: service.running: - name: apache2 configure-apache2: file.managed: - name: /etc/apache2/apache2.conf - source: salt://apache2/apache2.conf - listen_in: - service: apache2 Il y a aussi **prereq** et **prereq_in** expliqué [[https://docs.saltstack.com/en/latest/ref/states/requisites.html#prereq|ici]]. ===== Gestion des erreurs ===== La gestion des erreurs est possible grâce à **onfail** ou **onfail_in** (sa dépendance inverse). primary_mount: mount.mounted: - name: /mnt/share - device: 10.0.0.45:/share - fstype: nfs backup_mount: mount.mounted: - name: /mnt/share - device: 192.168.40.34:/share - fstype: nfs - onfail: - mount: primary_mount ===== Héritage d'arguments ===== L'héritage est possible grâce à **use** ou **use_in** (sa dépendance inverse). Dans cet exemple ''/etc/bar.conf'' va prendre les même arguments que ''/etc/foo.conf'' sauf pour la ''source'' qu'il aura redéfini: /etc/foo.conf: file.managed: - source: salt://foo.conf - template: jinja - mkdirs: True - user: apache - group: apache - mode: 755 /etc/bar.conf file.managed: - source: salt://bar.conf - use: - file: /etc/foo.conf ===== Exécuter en tant que ===== L'exécution **en tant que** est possible grâce à **runas** ou **runas_in** (sa dépendance inverse). django: pip.installed: - name: django >= 1.6, <= 1.7 - runas: daniel - require: - pkg: python-pip run_script: cmd.run: - name: Powershell -NonInteractive -ExecutionPolicy Bypass -File C:\\Temp\\script.ps1 - runas: frank - runas_password: supersecret ===== unless / onlyif ===== **unless** et **onlyif** lance des commandes shell. Dans l'exemple ci-dessous, vim sera installé quand **toutes les conditions seront fausses** : vim: pkg.installed: - unless: - rpm -q vim-enhanced - ls /usr/bin/vim **onlyif** est l'inverse qui signifie quand **toutes les conditions serons vraies**. ===== checkcmd ===== **checkcmd** permet de vérifier l'état attendu : comment-repo: file.replace: - name: /etc/yum.repos.d/fedora.repo - pattern: ^enabled=0 - repl: enabled=1 - check_cmd: - ! grep 'enabled=0' /etc/yum.repos.d/fedora.repo ===== retry ===== L'option [[https://docs.saltstack.com/en/latest/ref/states/requisites.html#retrying-states|retry]] lui permet d'être exécuté plusieurs fois jusqu'à ce qu'un résultat souhaité soit obtenu ou que le nombre maximum de tentatives ait été atteint. Le paramètre **attemps** contrôle le nombre maximum de fois que l'état sera exécuté. S'il n'est pas spécifié ou si une valeur non valide est spécifiée, la valeur par défaut sera 2. Le paramètre **until** définit le résultat requis pour arrêter de réessayer l'état. Si non spécifié ou si une valeur non valide est spécifiée alors la valeur par défaut sera True. Le paramètre **interval** définit la durée, en secondes, pendant laquelle le système attendra entre les tentatives. Si elle n'est pas spécifiée ou si une valeur non valide est spécifiée, la valeur par défaut sera 30. Le paramètre **splay** permet d'étendre l'intervalle. Si elle n'est pas spécifiée ou si une valeur non valide est spécifiée, la valeur par défaut est 0 (c'est-à-dire qu'il n'y aura pas d'intervalle). my_retried_state: pkg.installed: - name: nano - retry: attempts: 5 until: True interval: 60 splay: 10 install_nano: pkg.installed: - name: nano - retry: True wait_for_file: file.exists: - name: /path/to/file - retry: attempts: 15 interval: 30 ===== include / exclude ===== Fichier ''ssh/init.sls'' : openssh-client: pkg.installed /etc/ssh/ssh_config: file.managed: - user: root - group: root - mode: 644 - source: salt://ssh/ssh_config - require: - pkg: openssh-client Fichier ''ssh/server.sls'' : include: - ssh openssh-server: pkg.installed sshd: service.running: - require: - pkg: openssh-client - pkg: openssh-server - file: /etc/ssh/banner - file: /etc/ssh/sshd_config /etc/ssh/sshd_config: file.managed: - user: root - group: root - mode: 644 - source: salt://ssh/sshd_config - require: - pkg: openssh-server /etc/ssh/banner: file: - managed - user: root - group: root - mode: 644 - source: salt://ssh/banner - require: - pkg: openssh-server Fichier ''ssh/custom-server.sls'' : include: - ssh.server extend: /etc/ssh/banner: file: - source: salt://ssh/custom-banner On peut également exclure un id ou un sls : exclude: - sls: http - id: /etc/vimrc ===== provider ===== Il est de changer le comportement en spécifiant un provider. Par exemple systemd est le provider par défaut pour les services. Si on souhaite passer par init, on utilisera le provider service. httpd: service.running: - enable: True - provider: service emacs: pkg.installed: - provider: - cmd: customcmd ===== parallel ===== Tout est lancé en parallèle et prendra donc 10 secondes à la place de 15 : sleep 10: cmd.run: - parallel: True nginx: service.running: - parallel: True - require: - cmd: sleep 10 sleep 5: cmd.run: - parallel: True ===== backup des changement des fichiers ===== Le fichier ''/etc/salt/minion'' doit contenir la ligne suivant pour êtreactivé : ''backup_mode: minion''. /etc/ssh/sshd_config: file.managed: - source: salt://ssh/sshd_config - backup: minion * Pour lister les backups : salt foo.bar.com file.list_backups /tmp/foo.txt foo.bar.com: ---------- 0: ---------- Backup Time: Sat Jul 27 2013 17:48:41.738027 Location: /var/cache/salt/minion/file_backup/tmp/foo.txt_Sat_Jul_27_17:48:41_738027_2013 Size: 13 1: ---------- Backup Time: Sat Jul 27 2013 17:48:28.369804 Location: /var/cache/salt/minion/file_backup/tmp/foo.txt_Sat_Jul_27_17:48:28_369804_2013 Size: 35 * Pour restaurer un backup : salt foo.bar.com file.restore_backup /tmp/foo.txt 1 * Pour supprimer un backup : salt foo.bar.com file.delete_backup /tmp/foo.txt 0 ===== high / low state ===== * High : Lors de la définition de formules salt dans YAML, les données représentées sont appelées High Data par le compilateur. Lorsque les données sont initialement chargées dans le compilateur, il s'agit d'un seul grand dictionnaire python, ce dictionnaire peut être vu en exécutant : salt '*' state.show_highstate salt '*' state.show_highstate --out yaml salt '*' state.show_sls edit.vim --out pprint * Low : Cette structure "High Data" est ensuite compilée jusqu'au "Low Data". Les données basses correspondent à ce qui est nécessaire pour créer des exécutions individuelles dans le système de gestion de la configuration de Salt. Les données faibles sont une liste ordonnée d'appels d'état unique à exécuter. Les données faibles sont une liste ordonnée d'appels à état unique à exécuter. Une fois que les données sont compilées, l'ordre d'évaluation peut être vu. Les lowstates peuvent être visualisées en exécutant: salt '*' state.show_lowstate salt '*' state.low '{name: vim, state: pkg, fun: installed}' ===== Définir un Pillar ===== Les pillar sont à définir dans le fichier ''/srv/pillar/top.sls''. Exemples : {{ saltenv }}: '*': - common_pillar base: '*': - apache dev: 'os:Debian': - match: grain - vim test: '* and not G@os: Debian': - match: compound - emacs Fichier ''/srv/pillar/apache.sls'' : apache: lookup: name: httpd config: tmpl: salt://apache/files/httpd.conf Exemple pour y faire appel : {{ salt['pillar.get']('apache:lookup:name') }} ===== Gestion des états de retour ===== always-passes-with-any-kwarg: test.nop: - name: foo - something: else - foo: bar always-passes: test.succeed_without_changes: - name: foo always-fails: test.fail_without_changes: - name: foo always-changes-and-succeeds: test.succeed_with_changes: - name: foo always-changes-and-fails: test.fail_with_changes: - name: foo my-custom-combo: test.configurable_test_state: - name: foo - changes: True - result: False - comment: bar.baz is-pillar-foo-present-and-bar-is-int: test.check_pillar: - present: - foo - integer: - bar this_state_will_return_changes: test.succeed_with_changes this_state_will_NOT_return_changes: test.succeed_without_changes this_state_is_watching_another_state: test.succeed_without_changes: - comment: 'This is a custom comment' - watch: - test: this_state_will_return_changes - test: this_state_will_NOT_return_changes this_state_is_also_watching_another_state: test.succeed_without_changes: - watch: - test: this_state_will_NOT_return_changes ===== Gestion des environnements de configuration ===== Il prend le premier et s'il ne trouve pas il passe au suivant : file_roots: base: - /srv/salt/prod qa: - /srv/salt/qa - /srv/salt/prod dev: - /srv/salt/dev - /srv/salt/qa - /srv/salt/prod ===== Jinja ===== https://docs.saltstack.com/en/latest/topics/jinja/index.html ==== map jinja ==== On appellera ces fichiers **map.jinja**. {% set apache = salt['grains.filter_by']({ 'Debian': { 'server': 'apache2', 'service': 'apache2', 'conf': '/etc/apache2/apache.conf', }, 'RedHat': { 'server': 'httpd', 'service': 'httpd', 'conf': '/etc/httpd/httpd.conf', }, }, merge=salt['pillar.get']('apache:lookup')) %} {% set htop = salt['grains.filter_by']({ 'Suse': { 'pattern': salt['grains.filter_by']({ '12.3': 'htop', 'default': 'htop', }, grain='osrelease'), }, 'default': { 'pattern': 'htop' }, }, grain='os') %} On chargera par exemple le fichier ''apache/map.jinja'' dans un fichier sls de la façon suivante : {% from "apache/map.jinja" import apache with context %} apache: pkg.installed: - name: {{ apache.server }} service.running: - name: {{ apache.service }} - enable: True ===== runner ===== **salt-run** est toujours lancé à partir du master ! ==== manage ==== * Status des minions : $ salt-run manage.status down: - portable.localdomain up: * Minions down : $ salt-run manage.down - portable.localdomain * Minions up : $ salt-run manage.up - portable.localdomain * Version du client salstack des minions : $ salt-run manage.versions Master: 2017.7.3 Up to date: ---------- portable.localdomain: 2017.7.3 ==== orchestrate ==== # /srv/salt/orch/cleanfoo.sls cmd.run: salt.function: - tgt: '*' - arg: - rm -rf /tmp/foo salt-run state.orchestrate orch.cleanfoo # /srv/salt/orch/webserver.sls install_nginx: salt.state: - tgt: 'web*' - sls: - nginx salt-run state.orchestrate orch.webserver # /srv/salt/orch/web_setup.sls webserver_setup: salt.state: - tgt: 'web*' - highstate: True salt-run state.orchestrate orch.web_setup # /srv/salt/orch/deploy.sls create_instance: salt.runner: - name: cloud.profile - prof: cloud-centos - provider: cloud - instances: - server1 - opts: minion: master: master1 # /srv/salt/orch/deploy.sls {% set servers = salt['pillar.get']('servers', 'test') %} {% set master = salt['pillar.get']('master', 'salt') %} create_instance: salt.runner: - name: cloud.profile - prof: cloud-centos - provider: cloud - instances: - {{ servers }} - opts: minion: master: {{ master }} salt-run state.orchestrate orch.deploy pillar='{"servers": "newsystem1", "master": "mymaster"}' bootstrap_servers: salt.function: - name: cmd.run - tgt: 10.0.0.0/24 - tgt_type: ipcidr - arg: - bootstrap storage_setup: salt.state: - tgt: 'role:storage' - tgt_type: grain - sls: ceph - require: - salt: webserver_setup webserver_setup: salt.state: - tgt: 'web*' - highstate: True ===== mine ===== https://docs.saltstack.com/en/latest/topics/mine/index.html Une mine est identique à un **grains** mais moins statique. En effet celui-ci va être actualisé toutes les **60** minutes par défaut contrairement à un **grains** qui lui est actualisé seulement au démarrage du minion. On peut changer dans la configuration du minion ''/etc/salt/minion'' la variable **mine_interval** pour modifier le temps de collecte. La mine se déclare comme un **pillar** avec la fonction **mine_functions**. mine_functions: test.ping: [] network.ip_addrs: interface: eth0 cidr: '10.0.0.0/8' Pour forcer la maj de toutes les mines : salt '*' mine.update ===== beacons ===== https://docs.saltstack.com/en/latest/topics/beacons/index.html Les beacons permettent de placer des //hook// sur des événements sur les minions. Pour créer un beacons, créer le fichier ''/etc/salt/minion.d/beacons.conf'' : beacons: inotify: /etc/important_file: {} /opt: {} Un autre exemple : beacons: inotify: /etc/important_file: {} /opt: {} interval: 5 disable_during_state_run: True load: 1m: - 0.0 - 2.0 5m: - 0.0 - 1.5 15m: - 0.1 - 1.0 interval: 10 Par exemple si on modifie le fichier ''/etc/important_file'', cela va créer un event que l'on peu surveiller de cette façon sur le master : salt-run state.event pretty=true Ce qui affichera : salt/beacon/larry/inotify//etc/important_file { "_stamp": "2015-09-09T15:59:37.972753", "data": { "change": "IN_IGNORED", "id": "larry", "path": "/etc/important_file" }, "tag": "salt/beacon/larry/inotify//etc/important_file" } Un [#reactor] est la réponse du master à fournir à un minion lors d'un event sur un beacons. ===== reactor ===== https://docs.saltstack.com/en/latest/topics/reactor/index.html Les reactor sont à configurer sur le master ! Le reactor est la réponse à fournir aux [[#beacons]]. Sur le master, créer le fichier ''/srv/reactor/revert.sls'' : revert-file: local.state.apply: - tgt: {{ data['data']['id'] }} - arg: - maintain_important_file Il faut maintenant indiquer au reactor d'exécuter le fichier revert sls, pour cela on édite le fichier ''/etc/salt/master.d/reactor.conf'' : reactor: - salt/beacon/*/inotify//etc/important_file: - /srv/reactor/revert.sls Exemple de reactor : reactor: # Master config section "reactor" - 'salt/minion/*/start': # Match tag "salt/minion/*/start" - /srv/reactor/start.sls # Things to do when a minion starts - /srv/reactor/monitor.sls # Other things to do - 'salt/cloud/*/destroyed': # Globs can be used to match tags - /srv/reactor/destroy/*.sls # Globs can be used to match file names - 'myco/custom/event/tag': # React to custom event tags - salt://reactor/mycustom.sls # Reactor files can come from the salt fileserver ===== Variables utiles ===== * Configuration du minion : {{ opts['cachedir'] }} * Pillar {{ salt['pillar.get']('key', 'failover_value') }} {{ salt['pillar.get']('stuff:more:deeper') }} * Grains: {{ salt['grains.get']('os') }} * environnement salt (base par défaut) : {{ saltenv }} * nom du sls courant : {{ sls }} La variable sls contient la valeur de référence sls et n'est disponible que dans le fichier SLS réel (pas dans les fichiers référencés dans ce SLS). La valeur de référence sls est la valeur utilisée pour inclure les sls dans les fichiers top.sls ou via l'option include. * Chemin du sls : {{ slspath }} ===== Copie de fichier ===== Copie un fichier du master vers les minions : salt-cp '*' [ options ] SOURCE [SOURCE2 SOURCE3 ...] DEST ===== syndic ===== https://docs.saltstack.com/en/latest/topics/topology/syndic.html salt-syndic permet de compartimenter les minions avec plusieurs masters. Chacun des master gèrent un groupe de minions. Exemple : # salt-key -L Accepted Keys: my_syndic Denied Keys: Unaccepted Keys: Rejected Keys: # salt '*' test.ping minion_1: True minion_2: True minion_4: True minion_3: True ===== proxy ===== https://docs.saltstack.com/en/latest/topics/proxyminion/index.html salt-proxy permet de gérer des équipements où on ne peut pas installer de minion comme un switch réseau par exemple. ===== jobs ===== https://docs.saltstack.com/en/latest/topics/jobs/ https://docs.saltstack.com/en/latest/ref/runners/all/salt.runners.jobs.html * Affiche les derniers jobs : salt-run jobs.list_jobs * Lancer un job : On le stop avec Ctrl+c mais je job continue: sudo salt '*' test.sleep 100 ^C Exiting gracefully on Ctrl-c This job's jid is: 20180304011226668249 The minions may not have all finished running and any remaining minions will return upon completion. To look up the return data for this job later, run the following command: salt-run jobs.lookup_jid 20180304011226668249: * Savoir le status d'un jobs : Le job n'a pas fini car il ne retourne rien : sudo salt-run jobs.lookup_jid 20180304011226668249 Le job a terminé quand il retourne quelque chose : salt-run jobs.lookup_jid 20180304011226668249 portable.localdomain: True ===== publish ===== https://docs.saltstack.com/en/latest/ref/peer.html https://docs.saltstack.com/en/latest/ref/modules/all/salt.modules.publish.html Permet à un minion d'exécuter une commande sur un autre. Créer le fichier ''/etc/salt/master.d/peer.conf'' pour autoriser les master entre eux (cet exemple n'est pas sécurisé et est déconseillé) : peer: .*: - .* Redémarrer votre master et tester : salt '*' publish.publish '*' test.ping portable.localdomain: ---------- portable.localdomain: True salt-call publish.publish '*' test.ping local: ---------- portable.localdomain: True ===== Commentaires ===== Pour commenter dans un sls il suffit d'utiliser le ''#''. Pour commenter un block on peu passer en mode jinja avec ''{#'' et ''#}'' : {# mon commentaire ligne1 mon commentaire ligne2 #} ===== Gestion des environnements avec gitfs ===== Editer le fichier de configuration ''/etc/salt/master.d/gitfs.conf'' : top_file_merging_strategy: same state_top_saltenv: base fileserver_backend: #- roots - git gitfs_provider: pygit2 gitfs_remotes: - https://github.com/gigi206/salt-test.git **roots** correspond aux **file_roots**. Chaque tag ou branche est accessible avec saltenv : $ salt '*' state.apply saltenv=test $ salt '*' state.apply saltenv=base La branche **master** correspond au saltenv **base** ! ===== Créer son propre module ===== https://docs.saltstack.com/en/latest/ref/modules/index.html ===== Créer son propre state ===== https://docs.saltstack.com/en/latest/ref/states/writing.html ===== Créer son propre grain ===== https://docs.saltstack.com/en/latest/topics/grains/#writing-grains ===== Client salt en python ===== https://docs.saltstack.com/en/latest/ref/clients/ ===== Renderer en python ===== https://docs.saltstack.com/en/latest/ref/renderers/all/salt.renderers.py.html ===== Vars selon context ===== auth: - __path__ (path to your module dir) modules: - __pillar__ - __salt__ - __opts__ - __context__ ({'systemd.sd_booted': True}) - __grains__ runners: - __pillar__ - __salt__ - __opts__ - __grains__ returners: - __salt__ - __opts__ - __pillar__ - __grains__ pillars: - __salt__ (modules) - __opts__ - __pillar__ - __grains__ # nothing tops: - ['__builtins__', '__file__', 'subprocess', 'yaml', '__name__', '__package__', '__doc__'] outputters: - __opts__ - __pillar__ - __grains__ states: - __pillar__ - __low__ (lowstate structure?) - __env__ - __running__ - __lowstate__ - __salt__ - __opts__ - __grains__ log_handlers: - __path__ renderers: - __salt__ - Execution functions (i.e. __salt__['test.echo']('foo')) - __grains__ - Grains (i.e. __grains__['os']) - __pillar__ - Pillar data (i.e. __pillar__['foo']) - __opts__ - Minion configuration options - __env__ - The effective salt fileserver environment (i.e. base). Also referred to as a "saltenv". __env__ should not be modified in a pure python SLS file. To use a different environment, the environment should be set when executing the state. This can be done in a couple different ways: Using the saltenv argument on the salt CLI (i.e. salt '*' state.sls foo.bar.baz saltenv=env_name). By adding a saltenv argument to an individual state within the SLS file. In other words, adding a line like this to the state's data structure: {'saltenv': 'env_name'} - __sls__ - The SLS path of the file. For example, if the root of the base environment is /srv/salt, and the SLS file is /srv/salt/foo/bar/baz.sls, then __sls__ in that file will be foo.bar.baz. grains: - __salt__ - __opts__ - __pillar__ - __grains__ ===== Debug ===== * Option debug (**-l debug**) : salt-call --local -l debug --file-root=$PWD state.apply test=True * Tracer l'exécution de toutes les states : salt-call --local --file-root=$PWD state.orchestrate test=True * Voir tous les events : salt-run state.event pretty=True