Ansible - einfaches Playbook-Beispiel: Bedingtes Kopieren
Im Eingangskapitel Grundlagen haben wir uns mit der Installation bereits befasst. Auch haben wir uns schon in den beiden Kapiteln Playbooks und YAML - was ist das? eingehend mit den Hintergrundinformationen beschäftigt, so dass wir uns nun mit unsere Playbooks beschäftigen können.
Beispiel 4: bedingtes Kopieren
Im vierten Beispiel wollen wir die Konfigurationsdatei mailserver.guru.repo
für das Repository repo.mailserver.guru auf alle unsere definierten CentOS-Hosts kopieren. Dabei müssen wir natürlich beachten, dass sich die Datei zwischen den Versionen CentOS 7 und CentOS 8 unterscheiden, wenn auch nur geringfügig!
Script anlegen
Das Script legen wir wie auch schon bei den anderen Beispielen zuvor im Verzeichnis ~/ansible
an
$ vim 04_repro.yml
- 04_repro.yml
--- - hosts: all become: true vars: sudoers: ansible tasks: - name: Place repo-file mailserver.guru for CentOS 8 right in place copy: src: /home/django/ansible/files/CentOS8/mailserver.guru.repo dest: /etc/yum.repos.d/ when: - ansible_facts['distribution'] == "CentOS" - ansible_facts['distribution_major_version'] == "8" - name: Place repo-file mailserver.guru for CentOS 7 right in place copy: src: files/CentOS7/mailserver.guru.repo dest: /etc/yum.repos.d/ when: - ansible_facts['distribution'] == "CentOS" - ansible_facts['distribution_major_version'] == "7" ...
Im Arbeitsverzeichnis für unsere Ansible hatten wir zu Beginn bereits ein Verzeichnis für lokale (Konfigurationsdateien) angelegt. Zur besseren Unterscheidung legen wir uns dort noch zwei Verzeichnisse an, in denen wir die Releaseversionsabhängigen Dateien vorhalten werden.
$ mkdir ~/ansible/files/CentOS7 $ mkdir ~/ansible/files/CentOS8
Die CentOS 7 spezifische Repo-Datei mailserver.guru.repo
speichern wir nun im Verzeichnis ~/ansible/files/CentOS7
.
$ vim ~/ansible/files/CentOS7/mailserver.guru.repo
- ~/ansible/files/CentOS7/mailserver.guru.repo
# Repository mailserver.guru [mailserver.guru-os] name=Extra (Mailserver-)Packages for Enterprise Linux 7 - $basearch baseurl=http://repo.mailserver.guru/7/os/$basearch priority=5 enabled=1 gpgcheck=1 gpgkey=file:///etc/pki/rpm-gpg/MAILSERVER.GURU-RPM-GPG-KEY-CentOS-7 [mailserver.guru-testing] name=Testing (Mailserver-)Packages for Enterprise Linux 7 - $basearch baseurl=http://repo.mailserver.guru/7/testing/$basearch/ #priority=5 enabled=0 gpgcheck=1 gpgkey=file:///etc/pki/rpm-gpg/MAILSERVER.GURU-RPM-GPG-KEY-CentOS-7
Die CentOS 8 spezifische Repo-Datei mailserver.guru.repo
speichern wir entsprechend im Verzeichnis ~/ansible/files/CentOS8
ab.
$ vim ~/ansible/files/CentOS8/mailserver.guru.repo
- ~/ansible/files/CentOS8/mailserver.guru.repo
# Repository mailserver.guru [mailserver.guru-os] name=Extra (Mailserver-)Packages for Enterprise Linux 8 - $basearch baseurl=http://repo.mailserver.guru/8/os/$basearch priority=5 enabled=1 gpgcheck=1 gpgkey=file:///etc/pki/rpm-gpg/MAILSERVER.GURU-RPM-GPG-KEY-CentOS-8 [mailserver.guru-testing] name=Testing (Mailserver-)Packages for Enterprise Linux 8 - $basearch baseurl=http://repo.mailserver.guru/8/testing/$basearch/ #priority=5 enabled=0 gpgcheck=1 gpgkey=file:///etc/pki/rpm-gpg/MAILSERVER.GURU-RPM-GPG-KEY-CentOS-8
Script Beschreibung
Unser Playbook, welches alle Hosts anspricht besteht im Wesentlichen aus zwei Tasks/Aufgaben. Abhängig von den beiden Ansible Facts distribution
und distribution_major_version
wird die Repository-Konfigurations-Datei mailserver.guru.repo
auf den entsprechenden Zielhost kopiert.
Zum Kopieren wird dann das Ansible Modul copy verwendet. Da wir das RPM-Paket ansible-doc installiert hatten, könne wir auch auf der Konsole direkt Informationen zu dem Modul beziehen. Allgemeine Infos zu dem Modul erhalten wir mit folgendem Befehl:
$ ansible-doc copy
COPY (/usr/lib/python2.7/site-packages/ansible/modules/files/copy.py)
The `copy' module copies a file from the local or remote machine to a location on the remote
machine. Use the [fetch] module to copy files from remote locations to the local box. If you need
variable interpolation in copied files, use the [template] module. For Windows targets, use the
[win_copy] module instead.
* note: This module has a corresponding action plugin.
OPTIONS (= is mandatory):
- attributes
Attributes the file or directory should have. To get supported flags look at the man page for
`chattr' on the target system. This string should contain the attributes in the same order as the
one displayed by `lsattr'.
(Aliases: attr)[Default: None]
version_added: 2.3
- backup
Create a backup file including the timestamp information so you can get the original file back if
you somehow clobbered it incorrectly.
[Default: no]
type: bool
version_added: 0.7
- content
When used instead of `src', sets the contents of a file directly to the specified value. For
anything advanced or with formatting also look at the template module.
[Default: (null)]
version_added: 1.1
- decrypt
This option controls the autodecryption of source files using vault.
[Default: Yes]
type: bool
version_added: 2.4
= dest
Remote absolute path where the file should be copied to. If `src' is a directory, this must be a
directory too. If `dest' is a nonexistent path and if either `dest' ends with "/" or `src' is a
directory, `dest' is created. If `src' and `dest' are files, the parent directory of `dest' isn't
created: the task fails if it doesn't already exist.
- directory_mode
When doing a recursive copy set the mode for the directories. If this is not set we will use the
system defaults. The mode is only set on directories which are newly created, and will not affect
those that already existed.
[Default: (null)]
version_added: 1.5
- follow
This flag indicates that filesystem links in the destination, if they exist, should be followed.
[Default: no]
type: bool
version_added: 1.8
- force
the default is `yes', which will replace the remote file when contents are different than the
source. If `no', the file will only be transferred if the destination does not exist.
(Aliases: thirsty)[Default: yes]
type: bool
version_added: 1.1
- group
Name of the group that should own the file/directory, as would be fed to `chown'.
[Default: None]
- local_follow
This flag indicates that filesystem links in the source tree, if they exist, should be followed.
[Default: yes]
type: bool
version_added: 2.4
- mode
Mode the file or directory should be. For those used to `/usr/bin/chmod' remember that modes are
actually octal numbers (like 0644). Leaving off the leading zero will likely have unexpected
results. As of version 1.8, the mode may be specified as a symbolic mode (for example, `u+rwx' or
`u=rw,g=r,o=r').
[Default: None]
- owner
Name of the user that should own the file/directory, as would be fed to `chown'.
[Default: None]
- remote_src
If `no', it will search for `src' at originating/master machine.
If `yes' it will go to the remote/target machine for the `src'. Default is `no'.
Currently `remote_src' does not support recursive copying.
[Default: no]
type: bool
version_added: 2.0
- selevel
Level part of the SELinux file context. This is the MLS/MCS attribute, sometimes known as the
`range'. `_default' feature works as for `seuser'.
[Default: s0]
- serole
Role part of SELinux file context, `_default' feature works as for `seuser'.
[Default: None]
- setype
Type part of SELinux file context, `_default' feature works as for `seuser'.
[Default: None]
- seuser
User part of SELinux file context. Will default to system policy, if applicable. If set to
`_default', it will use the `user' portion of the policy if available.
[Default: None]
- src
Local path to a file to copy to the remote server; can be absolute or relative. If path is a
directory, it is copied recursively. In this case, if path ends with "/", only inside contents of
that directory are copied to destination. Otherwise, if it does not end with "/", the directory
itself with all contents is copied. This behavior is similar to Rsync.
[Default: (null)]
- unsafe_writes
Normally this module uses atomic operations to prevent data corruption or inconsistent reads from
the target files, sometimes systems are configured or just broken in ways that prevent this. One
example are docker mounted files, they cannot be updated atomically and can only be done in an
unsafe manner.
This boolean option allows ansible to fall back to unsafe methods of updating files for those
cases in which you do not have any other choice. Be aware that this is subject to race conditions
and can lead to data corruption.
[Default: False]
type: bool
version_added: 2.2
- validate
The validation command to run before copying into place. The path to the file to validate is
passed in via '%s' which must be present as in the example below. The command is passed securely
so shell features like expansion and pipes won't work.
[Default: None]
NOTES:
* The [copy] module recursively copy facility does not scale to lots (>hundreds) of files.
For alternative, see [synchronize] module, which is a wrapper around `rsync'.
* For Windows targets, use the [win_copy] module instead.
AUTHOR: Ansible Core Team, Michael DeHaan
EXTENDS_DOCUMENTATION_FRAGMENT: files, validate, decrypt
METADATA:
status:
- stableinterface
supported_by: core
EXAMPLES:
# Example from Ansible Playbooks
- copy:
src: /srv/myfiles/foo.conf
dest: /etc/foo.conf
owner: foo
group: foo
mode: 0644
# The same example as above, but using a symbolic mode equivalent to 0644
- copy:
src: /srv/myfiles/foo.conf
dest: /etc/foo.conf
owner: foo
group: foo
mode: u=rw,g=r,o=r
# Another symbolic mode example, adding some permissions and removing others
- copy:
src: /srv/myfiles/foo.conf
dest: /etc/foo.conf
owner: foo
group: foo
mode: u+rw,g-wx,o-rwx
# Copy a new "ntp.conf file into place, backing up the original if it differs from the copied version
- copy:
src: /mine/ntp.conf
dest: /etc/ntp.conf
owner: root
group: root
mode: 0644
backup: yes
# Copy a new "sudoers" file into place, after passing validation with visudo
- copy:
src: /mine/sudoers
dest: /etc/sudoers
validate: /usr/sbin/visudo -cf %s
# Copy a "sudoers" file on the remote machine for editing
- copy:
src: /etc/sudoers
dest: /etc/sudoers.edit
remote_src: yes
validate: /usr/sbin/visudo -cf %s
# Create a CSV file from your complete inventory using an inline template
- hosts: all
tasks:
- copy:
content: |
HOSTNAME;IPADDRESS;FQDN;OSNAME;OSVERSION;PROCESSOR;ARCHITECTURE;MEMORY;
{% for host in hostvars %}
{% set vars = hostvars[host|string] %}
{{ vars.ansible_hostname }};{{ vars.remote_host }};{{ vars.ansible_fqdn }};{{ vars.ansible_distribution }};{{ vars.ansible_d
{% endfor %}
dest: /some/path/systems.csv
backup: yes
run_once: yes
delegate_to: localhost
RETURN VALUES:
dest:
description: destination file/path
returned: success
type: string
sample: /path/to/file.txt
src:
description: source file used for the copy on the target machine
returned: changed
type: string
sample: /home/httpd/.ansible/tmp/ansible-tmp-1423796390.97-147729857856000/source
md5sum:
description: md5 checksum of the file after running copy
returned: when supported
type: string
sample: 2a5aeecc61dc98c4d780b14b330e3282
checksum:
description: sha1 checksum of the file after running copy
returned: success
type: string
sample: 6e642bb8dd5c2e027bf21dd923337cbb4214f827
backup_file:
description: name of backup file created
returned: changed and if backup=yes
type: string
sample: /path/to/file.txt.2015-02-12@22:09~
gid:
description: group id of the file, after execution
returned: success
type: int
sample: 100
group:
description: group of the file, after execution
returned: success
type: string
sample: httpd
owner:
description: owner of the file, after execution
returned: success
type: string
sample: httpd
uid:
description: owner id of the file, after execution
returned: success
type: int
sample: 100
mode:
description: permissions of the target, after execution
returned: success
type: string
sample: 0644
size:
description: size of the target, after execution
returned: success
type: int
sample: 1220
state:
description: state of the target, after execution
returned: success
type: string
sample: file
Wollen wir direkt ein snippet für unser Playbook zum Kopieren haben, geben wir bei dem Befehl ansible-doc copy
noch die Option -s
an.
$ ansible-doc copy -s
- name: Copies files to remote locations copy: attributes: # Attributes the file or directory should have. To get supported flags look at the man page for `chattr' on the target system. This string should contain the attributes in the same order as the one displayed by `lsattr'. backup: # Create a backup file including the timestamp information so you can get the original file back if you somehow clobbered it incorrectly. content: # When used instead of `src', sets the contents of a file directly to the specified value. For anything advanced or with formatting also look at the template module. decrypt: # This option controls the autodecryption of source files using vault. dest: # (required) Remote absolute path where the file should be copied to. If `src' is a directory, this must be a directory too. If `dest' is a nonexistent path and if either `dest' ends with "/" or `src' is a directory, `dest' is created. If `src' and `dest' are files, the parent directory of `dest' isn't created: the task fails if it doesn't already exist. directory_mode: # When doing a recursive copy set the mode for the directories. If this is not set we will use the system defaults. The mode is only set on directories which are newly created, and will not affect those that already existed. follow: # This flag indicates that filesystem links in the destination, if they exist, should be followed. force: # the default is `yes', which will replace the remote file when contents are different than the source. If `no', the file will only be transferred if the destination does not exist. group: # Name of the group that should own the file/directory, as would be fed to `chown'. local_follow: # This flag indicates that filesystem links in the source tree, if they exist, should be followed. mode: # Mode the file or directory should be. For those used to `/usr/bin/chmod' remember that modes are actually octal numbers (like 0644). Leaving off the leading zero will likely have unexpected results. As of version 1.8, the mode may be specified as a symbolic mode (for example, `u+rwx' or `u=rw,g=r,o=r'). owner: # Name of the user that should own the file/directory, as would be fed to `chown'. remote_src: # If `no', it will search for `src' at originating/master machine. If `yes' it will go to the remote/target machine for the `src'. Default is `no'. Currently `remote_src' does not support recursive copying. selevel: # Level part of the SELinux file context. This is the MLS/MCS attribute, sometimes known as the `range'. `_default' feature works as for `seuser'. serole: # Role part of SELinux file context, `_default' feature works as for `seuser'. setype: # Type part of SELinux file context, `_default' feature works as for `seuser'. seuser: # User part of SELinux file context. Will default to system policy, if applicable. If set to `_default', it will use the `user' portion of the policy if available. src: # Local path to a file to copy to the remote server; can be absolute or relative. If path is a directory, it is copied recursively. In this case, if path ends with "/", only inside contents of that directory are copied to destination. Otherwise, if it does not end with "/", the directory itself with all contents is copied. This behavior is similar to Rsync. unsafe_writes: # Normally this module uses atomic operations to prevent data corruption or inconsistent reads from the target files, sometimes systems are configured or just broken in ways that prevent this. One example are docker mounted files, they cannot be updated atomically and can only be done in an unsafe manner. This boolean option allows ansible to fall back to unsafe methods of updating files for those cases in which you do not have any other choice. Be aware that this is subject to race conditions and can lead to data corruption. validate: # The validation command to run before copying into place. The path to the file to validate is passed in via '%s' which must be present as in the example below. The command is passed securely so shell features like expansion and pipes won't work.
Script ausführen
Zum Kopieren der unterschiedlichen Dateien rufen wir nun unser Playbook wie folgt auf:
$ ansible-playbook -v 04_repro.yml
Using /etc/ansible/ansible.cfg as config file BECOME password: PLAY [all] ***************************************************************************************************************************** TASK [Gathering Facts] ***************************************************************************************************************** ok: [www7.dmz.nausch.org] ok: [www8.dmz.nausch.org]
TASK [Place repo-file mailserver.guru for CentOS 8 right in place] ********************************************************************* skipping: [www7.dmz.nausch.org] => {"changed": false, "skip_reason": "Conditional result was False"} changed: [www8.dmz.nausch.org] => {"changed": true, "checksum": "e9ab494c29df71d4e869c5b0bf68caf865ef74c9", "dest": "/etc/yum.repos.d/mailserver.guru.repo", "gid": 0, "group": "root", "md5sum": "f21be9d7340e512c004747204b54a2b5", "mode": "0644", "owner": "root", "secontext": "system_u:object_r:system_conf_t:s0", "size": 614, "src": "/home/ansible/.ansible/tmp/ansible-tmp-1578253275.4362092-25566445227282/source", "state": "file", "uid": 0}
TASK [Place repo-file mailserver.guru for CentOS 7 right in place] ********************************************************************* skipping: [www8.dmz.nausch.org] => {"changed": false, "skip_reason": "Conditional result was False"} changed: [www7.dmz.nausch.org] => {"changed": true, "checksum": "0fac1360785f0e02e074b4cc4f785e181f6620b9", "dest": "/etc/yum.repos.d/mailserver.guru.repo", "gid": 0, "group": "root", "md5sum": "90d8ca6369ff514b3c3dc3ddfac4ebdb", "mode": "0644", "owner": "root", "secontext": "system_u:object_r:system_conf_t:s0", "size": 614, "src": "/home/ansible/.ansible/tmp/ansible-tmp-1578253277.3541374-16074731401428/source", "state": "file", "uid": 0}
PLAY RECAP *************************************************************************************************************************************** www7.dmz.nausch.org : ok=2 changed=1 unreachable=0 failed=0 skipped=1 rescued=0 ignored=0 www8.dmz.nausch.org : ok=2 changed=1 unreachable=0 failed=0 skipped=1 rescued=0 ignored=0
Die blau markierten Zeilen bzw. in der Zusammenfassung genannten skipped=1 brauchen uns nicht beunruhigen.
Die Ursache hierzu ist einfach erklärt: Da wir die Prüfung auf die CentOS-Version 7 und 8 auf beiden Hosts ausführen, führt natürlich immer nur zu einem Treffer, der jeweils „falsche“ wird einfach übersprungen.
Ergebnis
Auf dem Zielhost findet sich nun unsere gewünschte Datei mit dem zugehörigen Inhalt an Ort und Stelle.
# ll /etc/yum.repos.d/mailserver.guru.repo
-rw-r--r--. 1 root root 614 Jan 5 20:41 /etc/yum.repos.d/mailserver.guru.repo