Outils pour utilisateurs

Outils du site


tuto:linux:saltstack

SaltStack

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 (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

<Include Declaration>:
  - <Module Reference>
  - <Module Reference>

<Extend Declaration>:
  <ID Declaration>:
    [<overrides>]
 
 
# standard declaration

<ID Declaration>:
  <State Module>:
    - <Function>
    - <Function Arg>
    - <Function Arg>
    - <Function Arg>
    - <Name>: <name>
    - <Requisite Declaration>:
      - <Requisite Reference>
      - <Requisite Reference>

 
# inline function and names

<ID Declaration>:
  <State Module>.<Function>:
    - <Function Arg>
    - <Function Arg>
    - <Function Arg>
    - <Names>:
      - <name>
      - <name>
      - <name>
    - <Requisite Declaration>:
      - <Requisite Reference>
      - <Requisite Reference>

 
# multiple states for single id

<ID Declaration>:
  <State Module>:
    - <Function>
    - <Function Arg>
    - <Name>: <name>
    - <Requisite Declaration>:
      - <Requisite Reference>
  <State Module>:
    - <Function>
    - <Function Arg>
    - <Names>:
      - <name>
      - <name>
    - <Requisite Declaration>:
      - <Requisite Reference>
  • 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

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

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_watchalors 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 listen /listen_in.

Contrairement à 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é 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 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

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

Créer son propre state

Créer son propre grain

Client salt en python

Renderer en python

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
tuto/linux/saltstack.txt · Dernière modification : 2019/07/29 17:54 de root