ansible / ansible

Ansible is a radically simple IT automation platform that makes your applications and systems easier to deploy and maintain. Automate everything from code deployment to network configuration to cloud management, in a language that approaches plain English, using SSH, with no agents to install on remote systems. https://docs.ansible.com.
https://www.ansible.com/
GNU General Public License v3.0
62.52k stars 23.85k forks source link

Support slicing based on hostname #83533

Closed skwde closed 2 months ago

skwde commented 3 months ago

Summary

Support for slicing based on the hostname (and not number of nodes)

E.g. Specifying something like node[009:012] should deploy to node009 to node012.

Instead it deploys to node018 to node021.

There is also an old related issue: https://github.com/ansible/ansible/issues/9670

Issue Type

Feature Idea

Component Name

playbook host specification

Additional Information

Use the playbook test-play.yaml

- name: Test stuff
  gather_facts: false
  hosts: node[009:012]

  tasks:
    - name: DEBUG
      ansible.builtin.debug:
        msg: test

with the inventory inventory.yaml

node:
  children:
    cpu:
      hosts:
        node[009:020]

and run

ansible-playbook test-play.yaml -i inventory.yaml 

to reproduce above output.

To correctly deploy to the correct hosts in above case one needs to deploy to node[0:3] or node[000:003].

Code of Conduct

ansibot commented 3 months ago

Files identified in the description:

If these files are incorrect, please update the component name section of the description or use the component bot command.

bcoca commented 3 months ago

something like this?:

hosts: "node{{range(9,13)|join(', node')}}"

I need to lookup the padding part, but this is mostly already supported via templating

skwde commented 2 months ago

I need to lookup the padding part, but this is mostly already supported via templating

This is exactly the point. In addition the padding is not really straight forward (I wasn't able to come up with something by quickly checking...). This also means that it is hard to remember....

Moreover templating is not supported for one shot commands?!

Forcing ansible's internal numbering of hosts instead of the actual hostname in the inventory is super confusing and very error prone.

bcoca commented 2 months ago

Moreover templating is not supported for one shot commands?!

do you mean 'adhoc'?

Forcing ansible's internal numbering of hosts instead of the actual hostname in the inventory is super confusing and very error prone.

I don't understand this

In any case you can also just create groups to do this, see constructed and generated inventory plugins.

skwde commented 2 months ago

Ohh yes, of course I meant 'adhoc' commands. Sorry for confusing the naming.

Forcing ansible's internal numbering of hosts instead of the actual hostname in the inventory is super confusing and very error prone.

I don't understand this

Ok, what I tried to imply here is that ansible seems to assign internal numbers to host names, i.e. node[009:020] are numbered from 0 to 11 and are consequently also available by using this numbers (instead of the hostname number 009 to 020). This is super confusing and error prone. This also means that the host ansible refers to changes when (some) of node[001-008] are brought back.

Naturally I expected ansible that slicing is done on the hostname number (i.e. as they appear in the inventory) and not on the numbers assigned by ansible internally (starting at 0).

Well, an inventory group already exists spanning all nodes. Sometimes it is however helpful to specify the hostrange. In above mentioned case it is still easy by just removing 9 from the start and end. It gets however even more inconvenient when a node 'in between' misses.

I never used dynamic inventories. Do they solve the problem?

bcoca commented 2 months ago

Ansible does NOT add internal numbering, it just uses the inventory you define.

The only way node[x:y] works is if you have a group named node that has hosts, then you are using Python range semantics on the group list (most programming languages list indecies are 0 based), which should correspond to definition order.

This is not Ansible assigning numbers to the host, this is you accessing a list you defined.

nitzmahone commented 2 months ago

Given the inconsistency of this kind of behavior across users, if you need some special selection behavior, I'd suggest a custom Jinja filter or other templating magic. It doesn't appear that there's a common enough use case to elevate this to a core feature at this time.

We've created #83557 to address the inconsistency with adhoc not accepting templates- thanks!

skwde commented 2 months ago

@bcoca @nitzmahone

Thanks for considering template support in adhoc commands. This would certainly be a nice feature. Unfortunately though, this won't give an easy and easy to remember fix because left padding (of lists) is not possible without defining a filter / a lengthy workaround ("templating magic").

Ansible does NOT add internal numbering, it just uses the inventory you define.

But It sure looks like it, considering that python range semantics are used while there is no support for doing slicing like node[00001:00004] in python. I.e. I can left pad with as many zeros I like and I still get a valid node range out of it, so ansible must do something in addition here.

The only way node[x:y] works is if you have a group named node that has hosts, then you are using Python range semantics on the group list (most programming languages list indecies are 0 based), which should correspond to definition order.

This at least explains my confusion of having groups being named as the hostname with their number. However I can imagine many people running into this problem.

It doesn't appear that there's a common enough use case to elevate this to a core feature at this time.

Ok, that's fine but at least it should be clear that different mechanisms are at work here. Please also consider the following:

I now see that having a group name like node and hosts like node*, was the source of all evil here, an is probably a bad idea for an ansible inventory. Nevertheless I think that using the same syntax (e.g. node[009:020]) for hostname slicing in the inventory and in the play host definition while both refer to different things just introduces confusion.

bcoca commented 2 months ago

But It sure looks like it,

Only because you were misunderstanding what you are using, in your case node[x:y] is not going from the name of the hosts, but operating on a list node which contains the hosts part of the node group you defined. There is no support for padding because this is not based on host name, it is a numeric index on a list. i.e node = [ 'node001', 'node002 , ..., node123, node333, ... ].

Please also consider the following:

I would not use runtime warnings for these , as all of it is explained in the documentation already. These 'features' (including slicing) are well documented as part of host targeting semantics. Slicing is specifically explained her as part of groups: https://docs.ansible.com/ansible/latest/inventory_guide/intro_patterns.html#slicing-at-specific-items

For your use case, you might even be better off using regexes (I should have mentioned this earlier, but i always forget the feature). https://docs.ansible.com/ansible/latest/inventory_guide/intro_patterns.html#using-regexes-in-patterns

skwde commented 2 months ago

As mentioned above I accept that slicing only works on groups and not on hostnames. Thanks for the hint with regexes this serves my use case well when I want to split rollout a bit further down.

There is no support for padding because this is not based on host name, it is a numeric index on a list

Conversion to int might be done in the background which at least explains this weird behavior. But actually it is not a numeric index and you do support padding here otherwise something like node[00000000:05] (on the group name) would not be possible, right?

Sorry to be a bit picky here but this really added to a lot of confusion in the past...

bcoca commented 2 months ago

Not really, what happens is that we parse [ \d*:\d*] in a regex as strings, then cast into ints and then pass them as an index to the python list:

x = int('00001')
print(x)
>> 1
skwde commented 2 months ago

Yeah, the strings in the slicing are converted to int and become numeric indices in the background (the string 05 vs the int 5).

As mentioned [00000000:05] is not a valid python slicing the individual number strings (00000000 and 05) as extracted by you via the regex are converted to int before they can be used for python list slicing... That is what I meant above.

So probably the regex should match for ints only with something like 0|[1-9]{1}[0-9]* to disable padding and other weirdness but that is not up to me to decide ;).