Looping over roles ?

5,832 views
Skip to first unread message

Michel Blanc

unread,
Nov 5, 2013, 11:43:54 AM11/5/13
to ansible...@googlegroups.com
Fellows,

I trying to wrap my head around a simple problem.
I have a playbook (role) that installs ruby with rbenv.
Actually, it uses ruby.version host/group variable to install the
correct ruby version :

group_vars/some_servers_that_wants_ruby.yml :
---
ruby:
version: 1.9.3-p448


However, I would like to change it so it can install several rubies on
target servers, using :

group_vars/some_servers_that_wants_ruby.yml :
---
ruby:
versions:
- 1.9.3-p448
- 2.0.0-p247

The easiest way to achieve this would be to do :

- hosts: some_servers_that_wants_ruby
roles:
- { role: ruby, version: "{{ item }}",
with_items: ruby.versions }

However, roles 'looping' seems to be bad : I get a deprecation warning
("[DEPRECATION WARNING]: include + with_items is an unsupported feature").

Of course, I could have a playbook that would do :

- hosts: some_servers_that_wants_ruby
roles:
- { role: ruby, version: "1.9.3-p448" }
- [ role: ruby, version: "2.0.0-p247" }

However, I really like to keep variables in host/groups_vars, and logic
in the playbooks. This yields more generic and reusable playbooks, and
doesn't require to write a playbook for specific hosts or groups. It is
also much simpler since you know that what you're looking for is in
host_vars or group_vars.

Looping in the role tasks is not really an option : the tasks are
somewhat complicated and require many (ordered) steps that wouldn't play
nice using with_items on a per-task basis.

Is there any way to achieve this ?

Thanks,

M
--
Michel Blanc
{ :github => "@leucos", :twitter => "@b9m", :gpg => "0X24B35C22" }

James Cammarata

unread,
Nov 5, 2013, 12:09:49 PM11/5/13
to ansible...@googlegroups.com
Yes, pass the array in via the role arguments and then loop over them inside the role itself. For example:

roles:
 - { role: ruby, versions: [ "1.9.3-p448", "2.0.0-p247"]}

Then use "with_items: versions" inside the role.


--
You received this message because you are subscribed to the Google Groups "Ansible Project" group.
To unsubscribe from this group and stop receiving emails from it, send an email to ansible-proje...@googlegroups.com.
For more options, visit https://groups.google.com/groups/opt_out.



--

James Cammarata <jcamm...@ansibleworks.com>
Sr. Software Engineer, AnsibleWorks, Inc.
http://www.ansibleworks.com/

Michel Blanc

unread,
Nov 6, 2013, 1:11:30 AM11/6/13
to ansible...@googlegroups.com
On 05/11/2013 18:09, James Cammarata wrote:
> Yes, pass the array in via the role arguments and then loop over them
> inside the role itself. For example:
>
> roles:
> - { role: ruby, versions: [ "1.9.3-p448", "2.0.0-p247"]}
>
> Then use "with_items: versions" inside the role.


Thanks James.

The problem, as explained previously, is that I can hardly use
with_items within a role's task since I use chained actions with
registered variables. It simply wouldn't work.

For instance, before installing ruby, an action checks if the requested
version is already installed (and registers a variable). I don't think
this would survive a "with_items" loop (unless it is possible to
register hash entries, but I don't think so).

Jerome Wagner

unread,
Nov 6, 2013, 5:22:32 AM11/6/13
to ansible...@googlegroups.com
Hello,

I am also looking for a way to loop over a set of tasks.

I used to do it with role.with_items just as you first posted but it has been deprecated.

The deprecation is for good reasons I think because the plays are unrolled before execution and even if role.with_items work in simple cases, I believe there are more complex inventory cases where it does weird things.

One option is to push the with_items down to the task level. This is what I did for my multi-versions installs but I did not need to register variables so it was simpler.

The best would be to make a role loopable with minimum modifications (pushing with_items down to the tasks level is rather painful imho)

Your question made me think about a potential new way to do it. it will look as a hack to many but may work to unroll the loop. The basis of the idea is to consider that the main use case is to loop N times over the role, N being bounded to for example 5.

Let's say you have you role : "ruby_install_one_version"

Now you define a new role "loop"
- { role: "loop", over: "ruby_install_one_version", iconf: [ { version: "1.9.3-p448"}, { version: "2.0.0-p247" } ] }

in meta.main.yml / dependency, you add something like (I still have a hard time with $ and {{}} in these contexts and I am not sure .length is the way to do it)

 - { role: "{{ over }}", conf: $iconf[0], when: "0 < iconf.length" }
 - { role: "{{ over }}", conf: $iconf[1], when: "1 < iconf.length" }
 - { role: "{{ over }}", conf: $iconf[2], when: "2 < iconf.length" }
 - { role: "{{ over }}", conf: $iconf[3], when: "3 < iconf.length" }
 - { role: "{{ over }}", conf: $iconf[4], when: "4 < iconf.length" }


The details would have to be refined but I think it could work to have the roles unroll as independent blocks of tasks rather than inside each task ; and if it works, it would look cleaner to me than obscuring the initial role with with_items and with_nested all over the place.

Tell me if you try it !

Jerome

Michel Blanc

unread,
Nov 6, 2013, 11:26:44 AM11/6/13
to ansible...@googlegroups.com
On 06/11/2013 11:22, Jerome Wagner wrote:
> Hello,

Hey Jerome,

> I am also looking for a way to loop over a set of tasks.

[...]

> One option is to push the with_items down to the task level. This is
> what I did for my multi-versions installs but I did not need to register
> variables so it was simpler.
>
> The best would be to make a role loopable with minimum modifications
> (pushing with_items down to the tasks level is rather painful imho)

[...]

> The details would have to be refined but I think it could work to have
> the roles unroll as independent blocks of tasks rather than inside each
> task ; and if it works, it would look cleaner to me than obscuring the
> initial role with with_items and with_nested all over the place.
>
> Tell me if you try it !

That's hell of a hack, but it works like a charm (with few changes) !

$ cat roles/ruby/tasks/main.yml
---
# Common prereqs (e.g. build tools, libs)
- include: common-prereqs.yml

- include: rbenv-setup.yml
when: ruby.install
sudo: yes
sudo_user: "{{ ruby.deploy_user }}"

# Installing required ruby versions
- include: rbenv-install.yml ruby_version={{ version }}
when: ruby.install
sudo: yes
sudo_user: "{{ ruby.deploy_user }}"

....

$ cat roles/ruby-multi/meta/main.yml
---
dependencies:
- { role: ruby, version: "{{ versions[0] }}",
when: "0 < versions|length" }
- { role: ruby, version: "{{ versions[1] }}",
when: "1 < versions|length" }
- { role: ruby, version: "{{ versions[2] }}",
when: "2 < versions|length" }
....

Thanks a lot Jerome for the cool trick. Ask if you'd like the full
ruby/rbenv scoop.

John Jarvis

unread,
Nov 6, 2013, 11:32:16 AM11/6/13
to ansible...@googlegroups.com
I would like to see your full ruby role if you don't mind making it available.
-John

Michel Blanc

unread,
Nov 6, 2013, 5:35:17 PM11/6/13
to ansible...@googlegroups.com
On 06/11/2013 17:32, John Jarvis wrote:
> I would like to see your full ruby role if you don't mind making it available.

Hi John,

I've setup a repos with a Vagrantfile so you can try it out :
https://github.com/leucos/ansible-rbenv-playbook

Rough edges, comments & PRs welcome.

Jerome Wagner

unread,
Nov 7, 2013, 1:43:47 PM11/7/13
to ansible...@googlegroups.com
I am glad the looping works it means that you can go from a "single" install script to a "multi" without modification and that is interesting.

Note that in your 
ruby_multi/tasks/main.yml
you can add a fail test when the versions array has a length superior to the looping cardinality of your multi. This way you will be informed if your array is not fully looped over.

note also that there might be other issues to look at and solve with notify: restart when doing a multi version of a server install. It might be necessary to restart all versions even if only one version is notified. At least until the notify: can accept parameters.

I do not use ruby that much but thanks for the script it is interesting and made me learn a few tricks.

Jerome

Michael DeHaan

unread,
Nov 10, 2013, 4:28:21 PM11/10/13
to ansible...@googlegroups.com
"since I use chained actions with
registered variables. It simply wouldn't work."

Not sure what a "chained variable" is.




--
You received this message because you are subscribed to the Google Groups "Ansible Project" group.
To unsubscribe from this group and stop receiving emails from it, send an email to ansible-proje...@googlegroups.com.
For more options, visit https://groups.google.com/groups/opt_out.



--
Michael DeHaan <mic...@ansibleworks.com>
CTO, AnsibleWorks, Inc.
http://www.ansibleworks.com/

Michel Blanc

unread,
Nov 11, 2013, 2:25:21 AM11/11/13
to ansible...@googlegroups.com
On 10/11/2013 22:28, Michael DeHaan wrote:

>> The problem, as explained previously, is that I can hardly use
>> with_items within a role's task since I use chained actions with
>> registered variables. It simply wouldn't work.
>>
> "since I use chained actions with
> registered variables. It simply wouldn't work."
>
> Not sure what a "chained variable" is.
>

It's registered variables. I use conditional tasks that depend on
previously registered variables (for instance, I check if the requested
ruby version is already installed and depending on the result, I install
it or not).

I'm not sure I can do that using 'with_items' for the ruby versions :
registered variables would be overriden for each item I suppose, unless
there is some facility that would put the results in a hash instead of a
simple variable.

Filias Heidt

unread,
Sep 3, 2015, 6:00:53 AM9/3/15
to Ansible Project
I just had the same problem. But since your post is a few years old, I wondered if there might be a better solution by now. If there isn't, I'll try the "hack" :D.

Thanks! 

Co S

unread,
Nov 26, 2015, 12:46:12 PM11/26/15
to Ansible Project
I have the dilemma.
In my case the list size can vary so the above hack doesn't work.

Does Ansible 2.0 solve this problem?

Filias Heidt

unread,
Nov 27, 2015, 7:04:59 AM11/27/15
to Ansible Project
In ansible 2.0 include will be possible with with_ -- so that would solve this problem, I guess. 

Bence Takács

unread,
Jun 22, 2016, 10:59:35 AM6/22/16
to Ansible Project
I think a permanent solution is needed: many people is looking for that feature.
https://groups.google.com/forum/#!topic/ansible-project/B5547FiIhYA
http://phillbarber.blogspot.hu/2015/10/ansibles-for-loops.html

Is that solved in 2.0?
Or only includes + with_items (but for tasks only not for roles)
... as written here: http://stackoverflow.com/questions/30785281/one-loop-over-multiple-ansible-tasks#answer-35128533

Regards:
   Bence
Reply all
Reply to author
Forward
0 new messages