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