Repository: ansible/ansible

Issue:
Bug in Conditional Include
Hi,

I know that when using conditionals on an include, 'All the tasks get evaluated, but the conditional is applied to each and every task'.  However this breaks when some of that tasks register variables and other tasks in the group use those variable.

Example:

main.yml:

```
- include: extra.yml
  when: do_extra is defined
```

extra.yml:

```
- name: check if we can do task A
  shell: check_if_task_A_possible
  register: A_possible
  ignore_errors: yes

- name: task A
  shell: run_task_A
  when: A_possible.rc == 0
```

Now if you run main.yml and 'do_extra' is not defined, the run will fail on 'task A'  because when the 'when' condition is evaluated, the variable A_possible will not exist.

It is not sufficient to just add the top-level include conditional above the other because right now it looks like the two conditions are compounded and tested together which will still fail because A_possible is not defined.  I think you would have to run the file level conditional before the task level ones to keep this from happening.

Add a split filter.
Often it is convenient to pass an argument on the command line that could be used as an iterable.  This patch provides a 'split' filter which mimics the python str.split interface.

An example playbook using it:

```

---
- hosts: localhost
  tasks:
    - debug: msg="{{ item }}"
      with_items: "{{ foo|split(sep=',') }}"
```

Executing it:

```
$ ansible-playbook test.yml -e "foo=1,2,3"

PLAY [localhost] ************************************************************** 

GATHERING FACTS *************************************************************** 
ok: [localhost]

TASK: [debug msg="{{item}}"] ************************************************** 
ok: [localhost] => (item=1) => {"item": "1", "msg": "1"}
ok: [localhost] => (item=2) => {"item": "2", "msg": "2"}
ok: [localhost] => (item=3) => {"item": "3", "msg": "3"}

PLAY RECAP ******************************************************************** 
localhost                  : ok=2    changed=0    unreachable=0    failed=0   
```



Discussion:
This is in queue to see if it is reproducible.

Seems reproducible:

``` bash
$ cat play.yml
- hosts: localhost
  gather_facts: False
  tasks:
  - include: extra.yml
    when: do_extra is defined

$ cat extra.yml
- name: check if we can do task A
  shell: echo check_if_task_A_possible
  register: A_possible
  ignore_errors: yes

- name: task A
  shell: echo run_task_A
  when: A_possible.rc == 0

$ ansible-playbook --version; ansible-playbook -i localhost, play.yml -v
ansible-playbook 1.3 (devel c3a8b6ff07) last updated 2013/07/18 21:05:00 (GMT +200)

PLAY [localhost] ************************************************************** 

TASK: [check if we can do task A] ********************************************* 
skipping: [localhost]

TASK: [task A] **************************************************************** 
fatal: [localhost] => Conditional expression must evaluate to True or False: {% if A_possible.rc == 0 %} True {% else %} False {% endif %}

FATAL: all hosts have already failed -- aborting

PLAY RECAP ******************************************************************** 
           to retry, use: --limit @/var/tmp/ansible/play.retry

localhost                  : ok=0    changed=0    unreachable=1    failed=0 
```

I see two possible solutions on playbook syntax level:
- when: A_possible.rc|default(None) == 0
- when: A_possible.skipped == False and A_possible.rc == 0

This is simular to a change in syntax I had to make when `.skipped`was introduced in 1.2

This is actually not specific to conditionals on task includes, but behaviour that should be expected when checking the registered: variable of a task with a conditional: you allways need to check if that task didn't get skipped. (Though I agree this is more difficult to foresee when including a task.)

``` bash
$ cat play.yml
- hosts: localhost
  gather_facts: False
  tasks:
   - name: check if we can do task A
     shell: echo check_if_task_A_possible
     register: A_possible
     ignore_errors: yes
     when: do_extra is defined

   - name: task A
     shell: echo run_task_A
     when: A_possible.rc == 0

$ ansible-playbook --version; ansible-playbook -i localhost, play.yml -v
ansible-playbook 1.3 (devel c3a8b6ff07) last updated 2013/07/18 21:05:00 (GMT +200)

PLAY [localhost] ************************************************************** 

TASK: [check if we can do task A] ********************************************* 
skipping: [localhost]

TASK: [task A] **************************************************************** 
fatal: [localhost] => Conditional expression must evaluate to True or False: {% if A_possible.rc == 0 %} True {% else %} False {% endif %}

FATAL: all hosts have already failed -- aborting

PLAY RECAP ******************************************************************** 
           to retry, use: --limit @/var/tmp/ansible/play.retry

localhost                  : ok=0    changed=0    unreachable=1    failed=0   
```

From list discussions we are going to make the conditional on the include insert vs append.

This was fixed on the development branch (for 1.3) yesterday, let me know if you have any problems, thanks!

I was wondering if the commit for this fix could be posted in this thread?

Has this been fixed? It seems I'm still hitting the bug with version 1.3.3.

Running the example above this is what I get...

MichelDrew:tmp drmichel$ ansible-playbook -i localhost play.yml -v

PLAY [localhost] **************************************************************

TASK: [check if we can do task A] *********************************************
skipping: [127.0.0.1]

TASK: [task A] ****************************************************************
fatal: [127.0.0.1] => error while evaluating conditional: {% if A_possible.rc == 0 %} True {% else %} False {% endif %}

FATAL: all hosts have already failed -- aborting

PLAY RECAP ********************************************************************
           to retry, use: --limit @/Users/drmichel/play.retry

127.0.0.1                  : ok=0    changed=0    unreachable=1    failed=0

Same problem here with current ansible version.

```
$:[~]:$ ls -laR roles/bug/
total 0
drwxr-xr-x   3 myuser  staff  102 24 Jul 10:41 .
drwxr-xr-x  25 myuser  staff  850 24 Jul 10:40 ..
drwxr-xr-x   5 myuser  staff  170 24 Jul 11:00 tasks

roles/bug//tasks:
total 24
drwxr-xr-x  5 myuser  staff  170 24 Jul 11:00 .
drwxr-xr-x  3 myuser  staff  102 24 Jul 10:41 ..
-rw-r--r--@ 1 myuser  staff  100 24 Jul 10:51 main.yml
-rw-r--r--@ 1 myuser  staff   86 24 Jul 11:00 test1.yml
-rw-r--r--@ 1 myuser  staff   19 24 Jul 10:52 test2.yml

$:[~]:$ cat roles/bug/tasks/main.yml 
- stat: path=.
  register: myvar1

- include: test1.yml
  when: not myvar1.stat.exists

$:[~]:$ cat roles/bug/tasks/test1.yml
- stat: path=.
  register: myvar2

- include: test2.yml
  when: not myvar2.stat.exists

$:[~]:$ cat roles/bug/tasks/test2.yml
- command: echo Foo

$:[~]:$ ansible-playbook -i inventory.ini playbook.yml -v --limit vagrant

PLAY [vagrant] **************************************************************** 

GATHERING FACTS *************************************************************** 
ok: [127.0.0.1]

TASK: [bug | stat path=. get_md5=false] *************************************** 
ok: [127.0.0.1] => {"changed": false, "stat": {"atime": 1405925636.4001133, "ctime": 1405693353.1919267, "dev": 65024, "exists": true, "gid": 900, "inode": 1046667, "isblk": false, "ischr": false, "isdir": true, "isfifo": false, "isgid": false, "islnk": false, "isreg": false, "issock": false, "isuid": false, "mode": "0755", "mtime": 1405693353.1919267, "nlink": 6, "pw_name": "vagrant", "rgrp": true, "roth": true, "rusr": true, "size": 4096, "uid": 900, "wgrp": false, "woth": false, "wusr": true, "xgrp": true, "xoth": true, "xusr": true}}

TASK: [bug | stat path=.] ***************************************************** 
skipping: [127.0.0.1]

TASK: [bug | command echo Foo] ************************************************ 
fatal: [127.0.0.1] => error while evaluating conditional: not myvar2.stat.exists

FATAL: all hosts have already failed -- aborting

PLAY RECAP ******************************************************************** 
           to retry, use: --limit @/Users/myuser/playbook.retry

127.0.0.1                  : ok=2    changed=0    unreachable=1    failed=0   

$:[~]:$ ansible --version
ansible 1.6.8
```

I also have this same problem with Ansible 1.7.2. A variable captchered with register in one include is not visible in another include. All in the same play/playbook. Where can I find more documentation regarding this behaviour. What is the "normal" expected behaviour?

I'm having similar problem, where _with_subelements_ gets evaluated when _include_ condition is not satisfied.

```
root@localhost:~/ansible# ansible --version
ansible 1.9.4
  configured module search path = None
```

```
root@localhost:~/ansible# tree
.
├── inventory
├── playbook.yml
└── roles
    └── test
        └── tasks
            ├── main.yml
            └── test.yml

3 directories, 4 files
```

```
root@localhost:~/ansible# cat playbook.yml
---

- hosts: localhost
  connection: local
  roles:
    - test
```

```
root@localhost:~/ansible# cat roles/test/tasks/main.yml
---

- name: include test task
  include: test.yml
  when: a is defined
```

```
root@localhost:~/ansible# cat roles/test/tasks/test.yml
---

- name: test task
  command: echo {{item.0}} - {{item.1}}
  with_subelements:
    - b
    - c
```

```
root@localhost:~/ansible# ansible-playbook playbook.yml -i inventory -vv

PLAY [localhost] **************************************************************

GATHERING FACTS ***************************************************************
<localhost> REMOTE_MODULE setup
ok: [localhost]

TASK: [test | test task] ******************************************************
fatal: [localhost] => subelements lookup expects a dictionary, got 'b'

FATAL: all hosts have already failed -- aborting

PLAY RECAP ********************************************************************
localhost                  : ok=1    changed=0    unreachable=1    failed=0
```

This can already be done as:

```
{{ foo.split(",") }}
```

Any reason to not just use that?  That better mimics the Python split interface :)

Oh, I suppose that doesn't actually return a list, right?

Actually that works great!  Thanks!

On Thu, Jul 11, 2013 at 6:48 AM, Michael DeHaan notifications@github.comwrote:

> Oh, I suppose that doesn't actually return a list, right?
> 
> —
> Reply to this email directly or view it on GitHubhttps://github.com/ansible/ansible/pull/3498#issuecomment-20808345
> .

Also, you can chain filters, string's split can only be used at the start
of the chain


Resolving patch:
diff --git a/lib/ansible/modules/windows/win_robocopy.py b/lib/ansible/modules/windows/win_robocopy.py
--- a/lib/ansible/modules/windows/win_robocopy.py
+++ b/lib/ansible/modules/windows/win_robocopy.py
@@ -129,7 +129,7 @@
     returned: always
     type: string
     sample: "/e /purge"
-return_code:
+rc:
     description: The return code retuned by robocopy.
     returned: success
     type: int


Test patch: