9

Background: in Puppet it is possible to execute a command unless it has already been executed:

exec { '/bin/echo root >> /usr/lib/cron/cron.allow':
  path   => '/usr/bin:/usr/sbin:/bin',
  unless => 'grep root /usr/lib/cron/cron.allow 2>/dev/null',
}

Aim: to execute a command unless it has already been run in Ansible

Methods

tasks/main.yml

- name: add vhost sensu
  command: rabbitmqctl add_vhost /sensu

Results

TASK [ansible-rabbitmq : add vhost sensu] **************************************
fatal: [111.222.333.444]: FAILED! => {"changed": true, "cmd": ["rabbitmqctl",
"add_vhost", "/sensu"], "delta": "0:00:00.210140", "end": 
"2016-07-29 12:55:19.384455", "failed": true, "rc": 2, "start":
"2016-07-29 12:55:19.174315", "stderr": "Error: vhost_already_exists: /sensu", 
"stdout": "Creating vhost \"/sensu\" ...", "stdout_lines": 
["Creating vhost \"/sensu\" ..."], "warnings": []}

Discussion

Googling unless ansible showed this document about when. Based on that documentation, a when statement was added:

- name: add vhost sensu
  command: rabbitmqctl add_vhost /sensu
  when: rabbitmqctl list_vhosts | grep sensu

running the code resulted in:

fatal: [192.168.0.9]: FAILED! => {"failed": true, "msg": "The conditional
 check 'rabbitmqctl list_vhosts | grep sensu' failed. The error was: template
 error while templating string: expected token 'end of statement block', got
 'list_vhosts'. String: {% if rabbitmqctl list_vhosts | grep sensu %} True {%
 else %} False {% endif %}\n\nThe error appears to have been in '/etc/ansible
/roles/ansible-rabbitmq/tasks/main.yml': line 10, column 3, but may\nbe
 elsewhere in the file depending on the exact syntax problem.\n\nThe
 offending line appears to be:\n\n\n- name: add vhost sensu\n  ^ here\n"}
  1. First of all, imagine that when was successful then the command will not run and then it looks more like onlyif in Puppet.
  2. Secondly, if the when would be successful should an escalation mark be used in order to simulate an unless?
  3. Use of register. What if that file is lost or the vhost has been removed by for example a human? Puppet's unless always executes the commands so that it is clear whether the command needs to be executed.
030
  • 5,901
  • 13
  • 68
  • 110

3 Answers3

9

I think what you're looking do is this:

- name: get vhosts
  command: rabbitmqctl list_vhosts
  register: vhosts
  changed_when: false

- name: add vhost sensu
  command: rabbitmqctl add_vhost /sensu
  when: "'/sensu' not in vhosts.stdout"

Re: #3 register does not create a file. If you're capturing the output of rabbitmqctl list_vhosts via register, the contents will be as valid as the system's current state.

jscott
  • 24,484
  • 8
  • 79
  • 100
3

The problem is the line when: rabbitmqctl list_vhosts | grep sensu. It is not possible to use bash here.

You need to put the rabbitmqctl list_vhosts | grep sensu in a separate task and register the result to use it in the when clause. You can use not filter to get unless like behavior.

Something like this should work:

- name: Get rabbitmq vhosts.
  command: rabbitmqctl list_vhosts | grep sensu
  register: rabbitmq_vhosts

- name: add vhost sensu
  command: rabbitmqctl add_vhost /sensu
  when: not 'sensu' in rabbitmq_vhosts.stdout

The Get rabbitmq vhosts. in this example will always be executed. The add vhost sensu only if the string sensu is not in the registered rabbitmq_vhosts.

Consult the documentation on conditionals and jinja filters for more information.

Henrik Pingel
  • 9,380
  • 2
  • 28
  • 39
  • The issue persists: `fatal: [IP]: FAILED! => {"changed": true, "cmd": ["rabbitmqctl", "add_vhost", "/sensu"], "delta": "0:00:00.198681", "end": "2016-07-29 13:43:00.870193", "failed": true, "rc": 2, "start": "2016-07-29 13:43:00.671512", "stderr": "Error: vhost_already_exists: /sensu", "stdout": "Creating vhost \"/sensu\" ...", "stdout_lines": ["Creating vhost \"/sensu\" ..."], "warnings": []}` – 030 Jul 29 '16 at 13:48
  • hm, probably my fault. I guess it was a missing `.stdout` edited my answer. – Henrik Pingel Jul 29 '16 at 17:38
0

The when option is the only thing Ansible has regarding conditions. But you can not directly define a command there. when expects a Jinja expression and furthermore is evaluated on the Ansible control host. So you first need to run a task to fetch the result and register it.

- shell: rabbitmqctl list_vhosts | grep sensu
  register: sensu_vhosts

- name: add vhost sensu
  command: rabbitmqctl add_vhost /sensu
  when: sensu_vhosts.stdout_lines < 1

stdout_lines is an array of all lines the shell task returned. So you can count the number of entries and only run your task when 0 items have been returned

udondan
  • 2,061
  • 15
  • 18
  • 2
    if the /sensu vhost does not exist it results in: `fatal: [IP]: FAILED! => {"changed": true, "cmd": "rabbitmqctl list_vhosts | grep sensu", "delta": "0:00:00.198769", "end": "2016-07-29 13:45:59.069981", "failed": true, "rc": 1, "start": "2016-07-29 13:45:58.871212", "stderr": "", "stdout": "", "stdout_lines": [], "warnings": []}` – 030 Jul 29 '16 at 13:47