Ordering of hosts in a group - ansible

I'm using ansible 1.9.0 and was curious how it determines the order of the hosts in a group.
firsthost = groups.controlplane[0]
I was under the impression doing this would result in the first host listed in that group in the inventory file. I found that is not the case. Sometimes it's the 3rd or 4th. Doesnt seem to be any pattern to it. It is based on the first host in that group that responds when gathering facts or something?
UPDATE actually I made a typo, I'm using 2.9.0.
What could be causing this? I have a group of 5 hosts and it seems its always picking the 3rd one as it exists in the inventory file when I use controlplane[0].
GOnna whip up a simple playbook to demonstrate.

Related

How to exclude instances of the EC2 inventory in Ansible?

We have an Ansible server using EC2 dynamic inventory:
https://github.com/ansible/ansible/blob/devel/contrib/inventory/ec2.py
https://github.com/ansible/ansible/blob/devel/contrib/inventory/ec2.ini
However, with the number of instances we have, running ./ec2.py --list or ./ec2.py --refresh-cache returns a 28,000 line JSON response.
This I assume, causes it to randomly fail (returns a Python stack trace) as it only receives a partial response when sending a call to AWS, but is then fine if ran again.
Which is why I want to know if there's a way to cut this down.
I know there is a way to include specific instances by tag in the ec2.ini (i.e. # instance_filters = tag:env=staging), but with
the way our instances are tagged, is there a way to exclude
instances instead (something that would look similar to: # instance_filters = tag:name=!dev)?
is there a way to exclude instances instead
Just for completeness, I wanted to point out that the "inventory protocol" for ansible is super straightforward to implement, and they even have a JSON Schema for it.
You can see an example of the output it is expecting by running the newly included ansible-inventory script with --list to see the output it generates from one of the .ini style inventories, and then use that to emit your own:
$ printf 'somehost ansible_user=bob\n\n[some_group]\nsomehost\n' > sample
$ ansible-inventory -i ./sample --list
What I am suggesting is that you might have better luck making a custom inventory script, that does know your local business practices, rather than trying to force ec2.py into running a negation query (which, as best I can tell, it will not do).
To generate dynamic inventory, just make an executable -- as far as I know it can be in any language at all -- and then point the -i at the executable script instead of a "normal" file. Ansible will invoke that program, and operate on the JSON output as the inventory. There are several examples people have posted as gists, in all kinds of languages.
I would still love it if you would file an issue with ansible about ec2.py, because you have the situation that can make the bug report concrete for them in ways that a simple "it doesn't work for large inventories" doesn't capture. But in the mean time, writing your own inventory provider is actually less work than it sounds.
I use the option pattern_exclude in ec2.ini:
# If you want to exclude any hosts that match a certain regular expression
pattern_exclude = staging-*
and
hostname_variable = tag_Name

Ansible: using nested groups with vars

I have a situation where we have 3 tiers of boxes, in each tier we apply different variables settings (like where the cache dir is), but there are a bunch of defaults. I also need to override on a per node basis, which is usually done via inventory vars on the host itself. I am not sure what is the best way to organize the hosts so that the precedence works in my favor.
Here are the different things I have tried. In each case I have entries in the inventory file like this:
[bots-fancy]
fancy-1
[bots-super-fancy]
super-1
[bots-magical]
magic-1
magic-2 provider=aws
At first, I had each of them with a long string of variable definitions. I also had different group_var/bots/[bots-magical | bots-super-fancy | bots-fancy].yaml files. This quickly became untenable.
attempt 1: with playbook variables
In the playbook I had something like this:
---
hosts:
- bots
vars_files:
- "group_vars/bots/defaults.yml"
- "group_vars/bots/{{ groups_names[0] }}.yml"
roles:
- somethign
this worked (though yes brittle) but it wouldn't let me override on a per host basis. I had to set things different on nodes occasionally, but not on the whole group.
attempt 2: using group_vars for each
I added
[bots:children]
bots-fancy
bots-super-fancy
bots-magical
to the hosts file. Removed any vars_files from the playbook and created group_vars for each group. I added the default/shared settings to group_vars/bots.yaml. When I'd run the playbook, it would only load the bots group_vars it seemed. Ideally, I want it to load the bots and then override it with the bots-fancy. And then finally the values from the hosts file.
I am not sure the best way to structure these groups, so any input would be very helpful!
Not sure what is your problem. You should be fine with:
hosts:
[bots-a]
bot1
[bots-b]
bot2
[bots:children]
bots-a
bots-b
dirs:
./group_vars/bots.yml
./group_vars/bots-a.yml
./group_vars/bots-b.yml
There is a concept of group depth in Ansible (at least in recent versions). In this example, group variables for host bot2 will be populated in the following order:
depth 0: group all, all.yml (missing here, ignoring)
depth 1: group bots, bots.yml
depth 2: group bots-b, bots-b.yml
You can see details and processing order here in the source code.
So if you define defaults in bots.yml and specific values in bots-b.yml, you should achieve what you expect.

ansible playbook hosts, is there a way to filter by variable

I'm new to ansible so bear with me, if my question is a bit basic.
I have 10 WordPress sites with different themes, all of them are listed in the hosts file under 'production' group:
[production]
black.com
red.com
blue.com
Each site require different variables:
theme_name: black
Is there anyway that I can run a playbook on 'production' host group , using variable?
You can not easily* filter by variables. But you could add your hosts to additional groups like so:
[production]
black.com
red.com
blue.com
[black]
black.com
[red]
red.com
[blue]
blue.com
Now for the theme name you an create group-vars files. For example, for the red group you create the file group_vars/red with the content:
theme_name: red
Now you can run your playbook with the black, red and blue groups.
* I guess there is a way, by running filters on the hostvars dict and reduce it to a list of hostnames matching your criteria. But this seems to be overhead and against best practice. If you want to target a specific set of hosts you should have a group for them in the inventory.
Not sure your question is absolutely clear, but another possible answer to your questions might be:
You can assign variables directly to hosts or groups in the inventory, there is a good introduction as to how you can do this here.
Example lifted from the page, variables defined after the host entry:
[atlanta]
host1 http_port=80 maxRequestsPerChild=808
host2 http_port=303 maxRequestsPerChild=909
This should work fine for small amounts of hosts, although there are better approaches when working at scale.
hth.

How to loop over playbook include?

(I'm currently running Ansible 2.1)
I have a playbook that gathers a list of elements and I have another playbook (that calls different hosts and whatnot) using said element as the basis for most operations. Therefore, whenever I use with_items over the playbook, it causes an error.
The loop control section of the docs say that "In 2.0 you are again able to use with_ loops and task includes (but not playbook includes) ". Is there a workaround? I really need to be able to call multiple hosts in an included playbook that runs over a set of entries. Any workarounds, ideas for such or anything are greatly appreciated!
P.S. I could technically command: ansible-playbook but I dont want to go down that rabbit hole if necessary
I think I faced same issues, and by the way, migrating to shows more than in 'item' already in use.
refering to http://docs.ansible.com/ansible/playbooks_best_practices.html , you should have an inventory (that contains all your hosts), and a master playbook (even if theorical).
A good way, instead of including playbooks, is to design roles, even if empty. Try to find a "common" role for everything that could be applied to most of your hosts.Then, include additional roles depending of usage, this will permit you to trigg on correct hosts.
You can also have roles that do nothing (meaning, nothing in 'tasks'), but that contain set of variables that can be common for two roles (you avoid then duplicate entries).

Ansible and inventory specific vars

Is it possible to declare variables the inventory level? For example I have a inventory for my development env, and another one for my production env.
I have templates that I need populated with certain variables that are specific to either environment.
At the moment it seems I can only set these variables at the group or host level, but I want it to apply for the entire inventory to avoid repeating myself for each group or host I have defined!
It turns out, my previous answer was not so right. There is an easier solution. Just define the vars for the all group. I would have expected ansible complaining there is no matching group definition, but it works, I just tested it:
[some_group]
some_host
[another_group]
another_host
[all:vars]
some_var=some_value
But in this case you really are forced to define the vars in the inventory file and can not define it in group_vars/all, but this should be obvious.
Leaving this here for reference, but check out my other answer, there is a simpler way to do it.
I believe this is not directly possible. You can define variables directly in the inventory, but they still have to be defined per host or group.
You could create a group which inherits from all other groups of that inventory file and then assign the variables to this group.
Inventory file myInventory:
[some_group]
some_host
[another_group]
another_host
[myInventory:children]
some_group
another_group
[myInventory:vars]
some_var=some_value
Separate inventories are a very good idea. The above answer's fully accurate, but I don't believe you'll be able to follow that approach if your groups are split up across inventory files, which I think your question implies they are.
Depending on your platform (the mode being AWS), it's effortless to group all your machines by environment, and it doesn't matter which inventory or inventories pick them up. I would just add an environment group variable file.
Please edit if group_vars are something you're trying to avoid categorically. If that's the case, then appropriate or create your own inventory scripts (adding a constant variable to the ec2 one is very very easy).

Resources