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