with_sequence is incompatible with idempotency

I recently had a need for with_sequence, but it doesn't seem possible to do
a zero count of actions.

Suppose i have a local action to create N things, perhaps vms or floating
ips. Some number M < N may already be present.

I'd like to do

hosts: localhost
vars:
   expected_foo_count: 10

- name: Get the number of foos
   shell: echo M
   register: foo_count

- name: Create the missing number of foos
   shell: make_new_foos
   with_sequence: start=0 end="{{expected_foo_count -foo_count.stdout }}"

or with_sequence: count="{{expected_foo_count - foo_count.stdout}}"

This will work if M is strictly less than N, but if M=N (the idempotent
case) then ansible gives an error.

fatal: [localhost] => can't count backwards
I tried putting a "when" guard such as
when: "{{ expected_foo_count - foo_count.stdout > 0 }}"

But it appears "when" gets evaluated after the with_sequence conditional.
That in itself poses problems, since in branching code the register
variables may not be defined and you have to litter code with when: "{{
(register_var or '') | conditional }}".

The problem with the with_sequence is in the
lib/ansible/runner/lookup_plugins/sequence.py

    def generate_sequence(self):
        numbers = xrange(self.start, self.end+1, self.stride)

which means that contrary to conventional python such as
range(a,b) which gives a sequence starting at 'a' and ending at 'b-1', ie.
[a,b)

with_sequence: start=n end=m

runs over the sequence [n,m]. If n=m with_sequence still executes once, so
there is no possible way to have a with_sequence that runs zero times.

changing the generate_sequence code to

    def generate_sequence(self):
        numbers = xrange(self.start, self.end, self.stride)

Fixes the problem.

Is there a design reason behind not allowing for zero count sequences? If
so, how can a playbook including with_sequence be made idempotent?

kesten

You are throwing two words together that have nothing to do with each other.

with_sequence is a loop.

Idempotency is a mathematical statement that says “F(X) = F(F(X))” for how many times X is applied.

“This will work if M is strictly less than N, but if M=N (the idempotent case) then ansible gives an error.”

What you have here is Ansible giving an error. It has nothing to do with idempotency.

It may imply “with sequence can’t traverse a zero length list”, which more accurately describes the problem.

So, the best thing to do here is to share what the error is and we can go from there.

The error is
fatal: [localhost] => can’t count backwards

This occurs whenever count=0 or start=M end=M

If with_sequence is used for the purpose of bringing a system to a state = HAS N of something
then there should be a way of running the playbook twice and not having anything new created.
That’s what i meant by idempotent.

“with sequence can’t traverse a zero length list” accurately describes my concern

kesten

In similar case A workaround I used is to add 1 to your sequence count

see https://mail.google.com/mail/#search/label%3Aansible+with_sequence/1435e13b34e3a0e1

Phil

Phil, the link doesn’t work for me.

The link references a previous exchange similar to yours

The specific case was about using the lineinfile module and with_sequence

to add a parameter to all the kernel liles in grub.conf

here is a copy of what I wrote:

"

tasks 1: count the number of lines to replace (via shell module using and grep -E | wc -l )
using register to remember the linecounted

task 2: using lineinfile and with_sequence: linecounted

one caveat is that with sequence does not accept a 0 count ( a potential future improvement ?)

so just add 1 to the count (you do not care about doing it one more time)

then you can run those 2 tasks multiples times

works for me on grub.conf
"

Phil

ah, thanks phillippe.

I think in my case this won’t work since i DO care about doing it one more time.
The thing i’m doing is creating vms. But i don’t want to create 1 more if i already have my desired N.

k

sure !
So being able to traverse a zero length list would fix yours issue

and avoid the need for my workaround

Phil

So it seems easiest to just make it so it can create zero length lists.

Please make sure there’s a github ticket and this can be dealt with.

Tickets go here: github.com/ansible/ansible

(Fix pull requests are even better)

Thanks!