Gestion de configuration - Variables#
Introduction#
Salt propose trois types de variables utilisables pour paramétrer la configuration d’une infrastructure, qui peuvent être classées selon leur origine :
Grains : données essentiellement statiques collectées au démarrage du minion
Pillars : données de configuration globale, déclarées sur le master
Mine : données poussées par les minions sur le master pour les rendre lisibles par les autres minions (cf. Gestion avancée d’infrastructures avec Salt)
Grains#
Généralités#
Les grains sont des données quasi-statiques collectées sur le minion lors de son démarrage.
On y trouve en particulier les informations sur les caractéristiques matérielles et logicielles du minion
Ils permettent d’adapter des states en fonction de caractéristiques du minion.
Il est possible d’écrire ses propres grains sous la forme de modules Python placés dans
/path/to/salt/states/_grains
Les informations peuvent être rechargées en utilisant
saltutil.sync_grains
.
Exemples de grains#
Ils peuvent être des informations sur la plate-forme tels que :
id
fqdn
os
os_family
oscodename
(par ex. bullseye)osrelease
(par ex. 11)
Exemples de grains sur le matériel#
Ou des informations matérielles (obtenues via dmidecode
) comme :
cpu_model
cpu_flag
cpuarch
(par ex. x86_64)manufacturer
(par ex. Dell Inc.)productname
(par ex. PowerEdge 2950)serialnumber
Récolte des données#
Les grains sont collectés lors du démarrage de
salt-minion
Il faut donc considérer ces informations comme statiques
Certaines de ces données peuvent varier dans le temps
par exemple la quantité de RAM disponible sur un système est variable dans les environnements virtualisés
cette variabilité n’est pas détectée et n’est pas prise en charge automatiquement par salt : les données des grains sont collectées au démarrage uniquement
On peut demander explicitement à un minion de mettre à jour ses grains :
master:~$ salt '*' saltutil.refresh_grains
Manipuler les grains#
append
ajoute un élément à une valeur de grains de type liste
delval
supprime la valeur d’un grain
filter_by
recherche la valeur associée à l’OS du minion dans le grain
get
essaye de trouver la valeur associée à la clef (éventuellement composite) passée en argument
item
retourne la valeur d’un grain
items
retourne la valeur pour tous les grains
ls
liste les grains (clefs)
remove
supprime un élément d’une valeur de grain de type liste
setval
configure la valeur d’un grain
Des informations ad hoc peuvent être ajoutées à la configuration
des minions. Ces valeurs de grains sont stockées dans les fichiers
/etc/salt/minion
ou /etc/salt/grains
.
Toutes les fonctions qui modifient les valeurs de grains affectent en pratique le fichier de configuration des grains du minion.
Grains - exemples#
root@salt:~# salt 'machine1' grains.append mygrain 12
machine1:
----------
mygrain:
- 12
root@salt:~# salt 'machine1' grains.append mygrain 13
machine1:
----------
mygrain:
- 12
- 13
root@salt:~# salt 'machine1' grains.remove mygrain 12
machine1:
----------
mygrain:
- 13
root@salt:~# salt 'machine1' grains.delkey mygrain force=True
machine1:
----------
changes:
----------
mygrain:
None
comment:
result:
True
root@salt:~# salt 'machine1' grains.item mygrain
machine1:
----------
mygrain:
master:~$ salt \* grains.get ip_interfaces:eth0
master:~$ salt \* grains.item ipv4 ipv6
master:~$ salt \* grains.item fqdn_ip4
À partir d’un minion :
root@machine1:~# salt-call grains.get ip_interfaces:eth0
local:
- 10.1.0.3
Utilisation des grains#
Les valeurs des grains sont souvent utilisées
dans des templates Jinja2 pour :
paramétrer les states,
pour générer des fichiers de configuration,
adapter les fichiers de configuration en fonction de valeurs de grains
au sein des
execution modules
salt
Utilisation des grains dans un fichier .sls
#
Dans un fichier SLS cela donne :
installation-apache-sur-redhat-et-ubuntu:
pkg.installed:
{% if grains['os'] == 'RedHat' %}
- name: httpd
{% elif grains['os'] == 'Ubuntu' %}
- name: apache2
{% endif %}
Utilisation des grains dans un module
#
Pour utiliser un grains dans un execution module
ou dans un
state module
, on peut utiliser la variable __grains__
qui se
présente comme un dictionnaire :
def _get_cron_info():
"""
Returns the proper group owner and path
to the cron directory
"""
owner = "root"
if __grains__["os"] == "FreeBSD":
group = "wheel"
crontab_dir = "/var/cron/tabs"
elif __grains__["os"] == "OpenBSD":
group = "crontab"
crontab_dir = "/var/cron/tabs"
#elif ...
Grains - filter_by
#
La fonction grains.filter_by
permet d’utiliser un dictionnaire de
correspondances pour éviter d’utiliser des if/elif/else
(comme
dans l’exemple précédent) :
{% set apache = salt['grains.filter_by']({
'RedHat': {
'pkgname': 'httpd',
},
'Ubuntu': {
'pkgname': 'apache2',
},
},
merge=salt['pillar.get']('apache:lookup'),
default='Ubuntu',
)%}
state to install apache:
pkg:
- name: {{ apache.pkgname }}
Remarques:
filter_by
utilise le grainos_family
par défautl’argument
merge
indique éventuellement une clef de pillar dont la valeur est un dictionnaire dont les valeurs pourront surcharger celles du dictionnaire de correspondancesl’argument
default
spécifie la clef par défaut qui sera utilisée si la valeur du grain n’est pas dans la table de correspondance
Fournir la valeur d’un grain directement#
La valeur d’un grain est évaluée dans l’ordre suivant :
core grains
générés dynamiquement au lancement de salt-minion à partir d’une définition standard/etc/salt/grains
fichier sur le minion mis à jour pargrains.{set,del}val
/etc/salt/minion
fichier sur le minion qui contient une sectiongrains:
_grains
générés dynamiquement au lancement de salt-minion à partir de la définition synchronisée par le master
La valeur lue dans /etc/salt/grains
peut donc être remplacée par celle issue de l’exécution
des fonctions définies dans _grains
.
Définir ses propres grains#
Il est facile d’écrire ses propres modules de récolement de grains sous forme de modules Python:
Les fichiers Python doivent être disponibles sur le master dans le dossier
/srv/salt/_grains
Chacune des fonctions définies dans ces modules Python définissent un nouveau grain
Exemple :
def shell():
"""
Return the default shell to use on this system
"""
# Provides:
# shell
return {"shell": os.environ.get("SHELL", "/bin/sh")}
Éléments importants :
la fonction doit renvoyer un dictionnaire de grains
la clef du dictionnaire correspond au nom du grain, le nom de la fonction ne doit pas commencer par underscore
_
.une fonction de grain peut renvoyer plusieurs grains (plusieurs clefs dans le dictionnaire)
le dictionnaire renvoyé ne doit contenir que des valeurs sérialisables en YAML (mais il est possible d’imbriquer des dictionnaires et des listes)
s’il n’y a pas de grain à fournir, renvoyer un dictionnaire vide.
Pillars#
Présentation#
Ce sont des variables globales rendues disponibles sur un ou plusieurs minions
définies sur le master
dans un répertoire différent des states
Ils permettent de stocker :
des données sensibles (par ex. clefs privées, mots de passe, etc.)
des méta-données de l’infrastructure (par ex. emplacement géographique, rôles dans l’infrastructure, etc.)
données arbitraires (par ex. tokens d’accès à des API, secrets partagés, clefs publiques des serveurs, etc.)
Vue d’ensemble#
Déclaration des pillars#
Les pillars sont déclarés dans des fichiers
.sls
placés dans un répertoire/srv/pillar
(configurable)Ce sont des structures de données libres au format YAML
Ce répertoire est paramétrable dans le fichier de configuration de
salt-master
Les pillars sont attribués à des minions dans un fichier
top.sls
selon le même mécanisme que lesstates
Manipulation des pillars#
Module de manipulation du pillar system de salt
data
demande les valeurs de pillar au master et les retourne
ext
genère le pillar et applique un pillar externe explicite
get
retourne la valeur de pillar associée à une clef donnée (éventuellement composite)
item
retourne la valeur de pillar associée à une clef donnée
items
retourne le dictionnaire de tous les pillars d’un minion
raw
retourne les valeurs des pillars tels qu’elles sont stockées dans le fichier de cache (dictionnaire
__pillar__
)
Assigner des pillars à un minion#
Pour envoyer des données de pillar à un minion :
il faut qu’il soit sélectionné dans le
top.sls
utiliser la commande salt
saltutil.refresh_pillar
Avertissement
Les erreurs de compilations des pillars sont visibles dans les logs du
salt-master
et visibles dans _errors
des pillars.
Exemple#
/srv/pillar/top.sls
:
base:
'machine2':
- zone
/srv/pillar/zone.sls
:
zone: intranet
location: Paris
Interroger les pillars :
master:~$ salt '*' saltutil.refresh_pillar
master:~$ salt '*' pillar.items
machine1:
----------
machine2:
----------
location:
paris
zone:
intranet
Pillars - utilisation dans les states#
Les pillars sont utilisables dans les templates Jinja2 ou pour
écrire des execution modules
ou des state modules
(comme les
grains) :
{% if pillar['zone'] == 'intranet' %}
[snip]
{% endif %}
pillar.get
permet de définir une valeur par défaut :
{% if pillar.get('zone', 'not-defined') == 'intranet' %}
[snip]
{% endif %}
Espace de nom#
Il n’y a pas d’espace de nom induit par le nom d’un fichier
.sls
dans lequel se trouve une déclaration de pillar/srv/pillars/users.sls
dev-users: alice: 42 bob: 53
la valeur sera obtenue avec
salt '*' pillar.get dev-users:alice
.Si la même variable est définie dans deux fichiers
.sls
, la deuxième définition sera masquée. Pour merger les variables, il faut configurer le master.
include
#
On peut utiliser la directive include
comme pour les states
, par exemple :
/srv/pillars/users.sls
dev-users:
- alice
- bob
- paul
dev-users-sudo:
- bob
- paul
Peut être utilisé dans un autre pillar :
/srv/pillars/intranet.sls
include:
- users
defaults
#
La directive defaults
permet d’introduire des variables :
/srv/pillars/users.sls
dev-users:
- alice
- bob
- paul
dev-users-sudo:
- defaults : {{ sudo }}
Permet :
/srv/pillars/intranet.sls
include:
- users:
defaults:
sudo: ['bob', 'paul']
key: dev-users-sudo
Dictionnaires imbriqués#
On peut accéder à des valeurs au fond de dictionnaire ou de listes
imbriquées à l’aide de la commande pillar.get
:
foo:
bar:
baz: qux
en utilisant la syntaxe Python :
{{ pillar['foo']['bar']['baz'] }}
ou avec le séparateur “:” :
{{ pillar.get('foo:bar:baz') }}
Mine#
Généralités#
Chaque minion peut pousser sur le master des informations dans sa mine où tous les autres minions pourront les lire.
ces informations sont nécessairement produites par des commandes salt
la déclaration des éléments poussés par un minion se fait dans le fichier de configuration de son
salt-minion
pas de sécurité : tous les minions peuvent lire toutes les informations publiées
seul le minion peut modifier les informations de sa mine
Intérêt :
cache de données
faciliter l’écriture de configurations maitre-esclave
Ajouter des données dans la mine#
Dans la configuration du minion /etc/salt/minion
définir les paramètres mine_functions
et mine_interval
:
mine_functions: test.ping: [] disk.usage: [] mine_interval: 60
dans cet exemple, les données sont envoyées toutes les heures (intervalle de 60 minutes) et aucun argument n’est passé aux commandes
test.ping
etdisk.usage
(listes vides)On peut aussi envoyer une requête pour forcer le rafraîchissement de la mine avec le module de commande
mine
:master:~$ salt '*' mine.send disk.usage master:~$ salt '*' mine.send test.ping
Les données de la mine du
<minion_id>
sont stockées sur le master dans:/var/cache/salt/master/minions/<minion_id>/mine.p
Récupérer des informations de la mine#
Depuis un minion :
machine1# salt-call mine.get machine2 test.ping
Ou depuis le master :
master# salt machine1 mine.get machine2 test.ping
machine1:
----------
machine2:
True
Dans les 2 cas, machine1
exécute la commande salt mine.get
qui
demande au master la valeur stockée dans la mine résultant de
l’exécution de test.ping
par machine2
si on remplace
machine1
par\*
, tous les minions vont demander cette même information au mastersi on remplace
machine2
par\*
,machine1
demande au master l’usage disque stocké dans la mine par tous les minions
Mine - utilisation dans les states#
Pour parcourir la mine
depuis un état ou un template jinja
, il
faut utiliser l’appel à mine.get
.
Penser à utiliser .items()
pour parcourir à la fois les clefs et
les valeurs d’un dictionnaire.
{% for machineid in salt['mine.get']('*', 'test.ping') %}
file configuration for {{ machineid }}:
file.managed:
- source: salt://file.conf
- name: /etc/service/hosts/{{ machineid }}.conf
{% endfor %}
Vider la mine#
On peut sélectivement supprimer un type d’information de la mine depuis le minion qui les a envoyées :
master:~$ salt 'minion1' mine.delete 'disk.usage'
ou vider toutes les informations de la mine pour tous les minions
master:~$ salt '*' mine.flush
Attention : si un minion qui a déjà poussé des données dans la
mine est inaccessible ou ne répond au moment l’exécution de cette
commande, elles ne seront pas supprimées par le flush
: seul le
minion peut ajouter ou enlever des informations à sa mine sur le
master.
Mine - runners#
Avec les runners coté master, on peut obtenir des informations sur la mine, mais aussi supprimer des informations.
salt-run cache.mine
salt-run cache.clear_mine_func tgt='*' \
clear_mine_func_flag='network.interfaces'
salt-run cache.clear_mine