Inhaltsverzeichnis

Ansible - einfaches Playbook-Beispiel: Bedingtes Kopieren

Bild: Ansible Logo

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 nausch.org.repo für das Repository repo.nausch.org 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 nausch.org for CentOS 8 right in place
      copy:
        src: /home/django/ansible/files/CentOS8/nausch.org.repo 
        dest: /etc/yum.repos.d/
      when:
        - ansible_facts['distribution'] == "CentOS"
        - ansible_facts['distribution_major_version'] == "8"
 
    - name: Place repo-file nausch.org for CentOS 7 right in place 
      copy:
        src: files/CentOS7/nausch.org.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 nausch.org.repo speichern wir nun im Verzeichnis ~/ansible/files/CentOS7.

 $ vim ~/ansible/files/CentOS7/nausch.org.repo
~/ansible/files/CentOS7/nausch.org.repo
# Repository nausch.org
[nausch.org-os]
name=Extra (Mailserver-)Packages for Enterprise Linux 7 - $basearch
baseurl=http://repo.nausch.org/7/os/$basearch
priority=5
enabled=1
gpgcheck=1
gpgkey=file:///etc/pki/rpm-gpg/MAILSERVER.GURU-RPM-GPG-KEY-CentOS-7
 
 
[nausch.org-testing]
name=Testing (Mailserver-)Packages for Enterprise Linux 7 - $basearch
baseurl=http://repo.nausch.org/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 nausch.org.repo speichern wir entsprechend im Verzeichnis ~/ansible/files/CentOS8 ab.

 $ vim ~/ansible/files/CentOS8/nausch.org.repo
~/ansible/files/CentOS8/nausch.org.repo
# Repository nausch.org
[nausch.org-os]
name=Extra (Mailserver-)Packages for Enterprise Linux 8 - $basearch
baseurl=http://repo.nausch.org/8/os/$basearch
priority=5
enabled=1
gpgcheck=1
gpgkey=file:///etc/pki/rpm-gpg/MAILSERVER.GURU-RPM-GPG-KEY-CentOS-8
 
 
[nausch.org-testing]
name=Testing (Mailserver-)Packages for Enterprise Linux 8 - $basearch
baseurl=http://repo.nausch.org/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 nausch.org.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 nausch.org 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/nausch.org.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 nausch.org 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/nausch.org.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/nausch.org.repo
-rw-r--r--. 1 root root 614 Jan  5 20:41 /etc/yum.repos.d/nausch.org.repo

Links