why unix | RBL service | netrs | please | ripcalc | linescroll
hosted services

hosted services

outline

Ansible solves several problems. One of the problems it solves very well is centralising management of systems.

ssh

In most setups you'll be using ansible with ssh as transport for data and commands. In some cases normal problems prevent commands running on the remote machine from finishing in a timely manner or finishing at all. Normal ssh timeouts will not help you here.

timeouts

Linux and unix systems are great at process wrapping. The vast majority of systems will let you specify which binaries to execute, and when then don't you can customise your PATH.

Ansible allows you to specify the ssh_command, however, I found it easiest to wrap my own:

#!/bin/sh

/bin/timeout 300 /bin/ssh "$@"

Was all that was needed.

This method is very effective at stopping long running commands, heavy work loads and remote filesystem blocks will not stop the controlling job.

tunnelling

In some cases you may need to funnel the connection through a 'jump' box (I don't like this term).

Host * !192.168.45.9
  ProxyCommand ssh -W %h:%p -l ansible@192.168.45.9

In the above example we exclude 192.168.45.9 from the proxy rule so that we can send the traffic via this host.

arbitrary scripts

If Ansible does not happen to have a module for your exact case, you can write your own inline scripts as follows:

- name: http port open
  shell: |
    ss -tln | egrep ':(80|443)\s'
  args:
    executable: /bin/bash

Python, which is white-space sensitive requires cmd in order to maintain indentation:

- name: http port open
  shell:
    cmd: |
      #!/usr/bin/python

      for i in range(1, 3):
          print i
  args:
    executable: /bin/bash


- name: http port open
  shell: |
    ss -tln | egrep ':(80|443)\s'
  args:
    executable: /bin/bash

system configuration stack

Sometimes you may wish to stack one set of system configuration on top of another. You may be tempted (like me) to use rsync to copy the files, since file tends to be slow when performing a presence check and copy.

Using rsync you may come up with this:

- name: copy
  shell: |
    rsync -avP --chown=root:root --rsync-path="sudo rsync" -e 'ssh
    -l ansibleuser' /usr/local/config/{{ config }}/* {{ inventory_hostname }}:/
  args:
    executable: /bin/bash
    warn: False
  become: yes
  delegate_to: localhost

What we're doing here is running rsync with a set of options. Importantly we're telling rsync to elevate with sudo --rsync-path="sudo...".

If we were to put two of these blocks, one after the other to solve a problem where 90% of the machines we config manage use /etc/hosts, but a small number must not have a few entries, we could end up with a race condition. Yes, I'm fully aware that lineinfile would be better for this, but slow if there were 10,000-ish entries to manage that way.

What I have come up with is a stackable form of applying rsync entries. In essence, we create a temporary directory, put the configuration in there, stacked, then copy it as a single operation. You can apply multiple configuration groups.

- name: configuration copy
  hosts: all
  tasks:

    - name: temp directory
      shell: |
        mktemp -d -t `date +%Y%m%d_%H%M%S`_XXXXXXXX
      register: temp_directory
      delegate_to: localhost

    - name: copy ansible
      vars:
        config: none
      shell: |
        #!/bin/sh

        rsync -avP --chown=root:root --rsync-path="sudo rsync" \
          -e 'ssh -l ansibleuser' \
          /usr/local/config/{{ item }}/* {{ temp_directory.stdout }}:/

      with_items: "{{ config.split( ' ' ) }}"
      delegate_to: localhost

    - name: copy result
      shell: |
        #!/bin/sh

        rsync -avP {{ temp_directory.stdout }}/ {{ inventory_hostname }}:/
      delegate_to: localhost

    - name: clean temp
      shell: |
        #!/bin/sh

        rmdir "{{ temp_directory.stdout }}"

      args:
        executable: /bin/bash
        warn: False
      delegate_to: localhost

What we're doing here is looping through a config variable named config and copying each of the directories that match the list under /usr/local/config to a temporary directory. Once that is done we copy the result to the destination machine.

We can define config with the following playbook command:

ansible-playbook -vv --extra-vars 'config="global web db ftp"' -i destwww, config_copy

[Errno 7] Argument list too long

I've been known in the past to set jobs up that include registers that may not be tiny, sometimes several megabytes.

When this happens, the shell code may be too large to be handed to the remote side in argument format, which is the way Ansible does things normally.

To get around this you can inform the shell task to take input via stdin, here's how:

- name: shell input
  shell: |
    #!/bin/sh

    cat >/tmp/large_file
  args:
    executable: /bin/bash
    stdin: "{{ large_output.stdout }}"

The snippet above will write the large_output.stdout contents into /tmp/large_file on the remote system.