1

First off thank you for any assistance here, this is my first post on here despite using it as a great resource for years!

I have recently been learning and using Ansible a lot more as part of a new role I'm in for work and my manager has asked me to make a change that I find a little confusing.

I have a task that runs a shell command and loops through the command using the with_items module

- name: add non-existent buckets
  shell: "timeout 30 mc --config-dir={{ mc_config }} mb opt/{{item}}"
  with_items: "{{ bucket_list }}"

I have been asked to change this to point directly at the variables rather than expanding them first and I am struggling to understand what this means and how to go about it.

I have tried various ways of writing this out but no luck whatsoever. The frustrating part of all this is that the above works but apparently makes the Ansible engine do more work than it needs to do.

Thanks in advance and please let me know if I need to provide any more information!

U880D
  • 8,601
  • 6
  • 24
  • 40
Getafix
  • 13
  • 4
  • 3
    `I have been asked to change this to point directly at the variables rather than expanding them first and I am struggling to understand what this means and how to go about it` <= well maybe you should ask the guy who had this brilliant idea what he means exactly and to provide an example because there is basically no way to point to var in ansible without expanding it with jinja2 expansion markers. – Zeitounator Jul 29 '22 at 09:46
  • I understand that you like to increase the performance and decrease execution time of tasks. Since there might be different possible solutions depending on what command you are using, can you provide the real command therefore? In example for the [`yum` module](https://docs.ansible.com/ansible/latest/collections/ansible/builtin/yum_module.html#notes): "_When used with a `loop:` each package will be processed individually, it is much more efficient to pass the list directly to the name option_". Without knowing what you try to achieve one could just guess. – U880D Jul 29 '22 at 09:46
  • @U880D I have edited the example to the actual task now. – Getafix Jul 29 '22 at 10:06
  • @Zeitounator This is also my understanding of how ansible deals with vars but isn't what he wants, I was told that what that is currently doing is equivalent to; with_items: "{{ variable1 }}" => with_items: "[ var1: val1, var2: val2 ]" – Getafix Jul 29 '22 at 10:06
  • Well this is exactly how ansible works so your guy is asking you to rewrite ansible or jinja2 unless there is something I totally don't get. There is very little chance you'll get a meaningfull answer here until you clarify the problem and have some precise example returned by your manager. – Zeitounator Jul 29 '22 at 10:29
  • What is `mc` doing? Looking up the man page for midnight commander via `man mc` I wasn't able to find an option `--config-dir` and `mb`. At the end there is list of directories `opt/{bucket_list}` with which you do like what? Can you describe what you try to achieve? – U880D Jul 29 '22 at 10:44
  • After further research it seems that you like to create multiple new buckets or directories under a specified path `opt/` in a [MinIO Baremetal Infrastructure - mc mb command](https://docs.min.io/minio/baremetal/reference/minio-mc/mc-mb.html). So probably you like to perform an equivalent of `mkdir -p /opt/{this,is,my,list,of,subdirectories,to,create}`, not in a loop but with providing the list for the command, right? – U880D Jul 29 '22 at 11:26
  • 1
    @U880D apologies for not adding in enough detail, yes I am creating minIO buckets with this task using the minIO client (mc) commands, I have two var files with buckets in a list format that will be called on depending on what inventory is used (one for dev one for production). you're example with the `mkdir` sounds pretty spot on, is there a way to pass a list to a command and get it to iterate over each item without a loop? my experience with ansible tells me no but that's not really saying much as I'm not vastly experienced with this. – Getafix Jul 29 '22 at 11:59
  • Unfortunately I'm unable to get an example of what he wants, am currently playing with different list formats and using ansible's debug – Getafix Jul 29 '22 at 12:02

1 Answers1

2

I understand that you like to know "How to increase the performance and decrease execution time of specific tasks?". I understand that you like to create multiple new buckets or directories under a specified path opt/ in a MinIO Baremetal Infrastructure via the mc mb command.

It is an equivalent of mkdir -p /opt/{this,is,my,list,of,subdirectories,to,create}. I understand that you do not want to loop over the command, instead providing a directory list for the command.

To achieve for goal you may have a look into the following example, documentation and further links.

---
- hosts: test
  become: false
  gather_facts: false

  vars:

    DIR_LIST: [this,is,my,list,of,subdirectories,to,create]

  tasks:

  - name: Create subdirectories (loop)
    shell:
      cmd: "mkdir -p /home/{{ ansible_user }}/{{ item }}"
      warn: false
    loop: "{{ DIR_LIST }}"

  - name: Create subdirectories (list)
    shell:
      cmd: "mkdir -p /home/{{ ansible_user }}/{% raw %}{{% endraw %}{{ DIR_LIST | join(',') }}{% raw %}}{% endraw %}"
      warn: false

resulting into run and execution times of

TASK [Create subdirectories (loop)] ******************
changed: [test.example.com] => (item=this)
changed: [test.example.com] => (item=is)
changed: [test.example.com] => (item=my)
changed: [test.example.com] => (item=list)
changed: [test.example.com] => (item=of)
changed: [test.example.com] => (item=subdirectories)
changed: [test.example.com] => (item=to)
changed: [test.example.com] => (item=create)
Friday 29 July 2022 (0:00:14.023) 0:00:14.064 ********

TASK [Create subdirectories (list)] ******************
changed: [test.example.com]

Friday 29 July 2022 (0:00:02.172) 0:00:16.236 ********
======================================================
Create subdirectories (loop) ------------------ 14.02s
Create subdirectories (list) ------------------- 2.17s

Whereby looping over modules or commands and providing one parameter for the module or command per run results into a lot of overhead, providing the list directly to the command or module might be possible and increase performance and decrease runtime and resource consumption.

An (other) example for this is the yum module

When used with a loop: each package will be processed individually, it is much more efficient to pass the list directly to the name option

If your command mc mb produce similar results as mentioned in documentation

You can also use mc mb against the local filesystem to produce similar results to the mkdir -p commandline tool.

you might be able provide a list of directories to the command via Ansible too, in example like

mc mb opt/{this,is,my,list,of,subdirectories,to,create}

For this the command needs to include curly braces which needs to be escaped in Ansible. The directory list would need to be a string, whereby the directories are comma separated. For this you can use the join() filter.

Is there a way to pass a list to a command and get it to iterate over each item without a loop?

Yes, finally you would end up with

- name: add non-existent buckets
  shell: 
    cmd: "timeout 30 mc --config-dir={{ mc_config }} mb opt/{% raw %}{{% endraw %}{{ bucket_list | join(',') }}{% raw %}}{% endraw %}"

Further Q&A

Further Documentation

Further Readings

U880D
  • 8,601
  • 6
  • 24
  • 40
  • I haven't had the chance to test this out but this is beyond what I hoped for when I posed the question! @U880D thank you for such a comprehensive answer and all the links to docs. I will spend some time going through I would like to further my understanding of what you have provided here as there are some things I have never encountered! I don't have the rep to upvote your reply so all I can do is show my appreciation here! – Getafix Jul 30 '22 at 09:02
  • @Getafix, thanks for feedback. Maybe you can just accept it as answer. Please take note that it might be necessary to adjust the value of `timeout 30 ` depending on your storage backend, infrastructure, IOps, etc.. Since I haven't such or a similiar backend at hand I couldn't perform full distributed tests. – U880D Jul 31 '22 at 11:22