Does Ansible provide predefined variables to use in scripts? I am looking for variables like:
- current host
- current group
- current user
- current task
- current role
etc.
I have not found these variables among these from “gathering facts”.
Does Ansible provide predefined variables to use in scripts? I am looking for variables like:
I have not found these variables among these from “gathering facts”.
Here are the ones that I can recall and easily find in the source:
vars
always_run
changed_when
delegate_to
failed_when
ignore_errors
inventory_dir
playbook_dir
register
The vars variable, is a dictionary, that contains all of the above vars as key/value pairs.
inventory_file
inventory_hostname_short
inventory_hostname
ansible_ssh_user
hostvars
group_names
groups
defaults
environment
Also, anything from --extra_vars, host_vars/, group_vars/, facts.d/, register, etc…
Some of this is mentioned on http://www.ansibleworks.com/docs/playbooks_variables.html , pay some special attention to http://www.ansibleworks.com/docs/playbooks_variables.html#id22
But to address some of your specifics:
- current host
inventory_hostname, and inventory_hostname_short
- current group
There isn’t really a var that says, this is the current group being acted on, but per host has a ‘groups’ var containing all groups that host is a member of
- current user
Perhaps ansible_ssh_user ?
- current task
This is not exposed
- current role
This is not exposed
Matt’s answer above is slightly incorrect.
Things like “always_run” and such are keywords, not variables that are usable in scripts.
(The statement about their being other variables is not accurate and will be removed next time docs are pushed, I have that queued up right now)
You should also look into facts:
http://ansibleworks.com/docs/playbooks_variables.html#id17
I think if you need a variable that defines your current role, it’s probably the sign of a modeling problem – with a few possible exceptions. The role shouldn’t have to know what it’s name is to operate correctly.
I disagree that needing the current role is a modeling problem. I’ve actually just ran into this problem, but I’m willing to hear better solutions.
We have 40 different web applications and a set of load balancers, so we need to have settings for the webservers like what port their nginx
should listen on but the load balancer also needs to know these settings so that it can configure the pools for each node.
How do you store settings that are globally accessible but attached to a specific role if you don’t know what the current role is?
For example our settings file looks like this:
services:
anonweb:
repo: anonweb
version: upgrade_latest_packages
port: 8500
nginx_port: 6005
paths:
addressbookweb:
repo: AddressBookWeb
version: develop
port: 8765
nginx_port: 6014
paths:
So we configure a pool for all the nodes in the addressbookweb group to listen on nginx port 6014, and then when a request to
/addressbook comes into the loadbalancer it routes to that specific pool. But when we setup the addressbook nodes we also need
to configure nginx properly to listen on that port.
So we do this by doing:
services[current_role].nginx_port
But do access current_role we had to set a variable for every role.
"I disagree that needing the current role is a modeling problem. "
“We have 40 different web applications and a set of load balancers, so we need to have settings for the webservers like what port their nginx
should listen on but the load balancer also needs to know these settings so that it can configure the pools for each node.”
This is a bit of a logical fallacy – lots of folks using Ansible have a lot more than 40 different web applications, for one. Secondly, having variables like nginx_port would allow access to this via hostvars.
All of this could be done with group_vars just fine without needing to know the path of the current role as a variable.
I don’t believe a role needs to know it’s current name, because of all the vars/defaults/tasks/templates machinery allows finding those sub-paths without needing those things.
This may just be more of a case of rethinking how you are modelling things in Ansible.
What you are describing sounds a lot like what I have at my day job -- hundreds of instances of dozens of Java applications each with their own properties like unique ports, file paths and whatnot.
We addressed the things you are talking about by treating each app as a host regardless of physical server and from there used host and groups vars, not roles for what you are trying to do. Essentially roles start with tasks and sometimes specialized modules. The variables and defaults are there to support those tasks. Recently I created a module that reads the properties file all of these application use for their configuration and imports them as facts so later roles and playbooks can remain in sync with little to no effort.
We did toy with something like your approach of wrapping application specific meta data as roles, but found that was messy and hard to manage. (Roles get checked in and treated like code, inventory not so much.)
So based on my experience I think your struggles are a modeling problem like Michael suggested.
<tim/>
Good points Tim!
I should probably add that when I wrote ansible, I was thinking all about inventory and groups as a method of categorization.
The roles are all about applying behavior to specific combinations of groups - and roles are just abstractions around task and variable boilerplate.
Larger organizations are going to be more and more inclined to dynamic inventory and sources of truth - but the point at which someone needs this can vary.
How about this?
{ include: …/…/…/roles/pythonapp/tasks/main.yml, app_role: anonweb }
{ include: …/…/…/roles/pythonapp/tasks/main.yml, app_role: addressbookweb }
Ansible variables seem to have a global scope, because if I set one within the role it seems to be visible to all roles (which surprised me as I was expecting roles to be more self-contained since they’re supposed to be reusable, shared on Galaxy, etc.). All roles are referencing the same named variable are sharing the same global variable. But the parameterized roles and parameterized includes are more akin to parameter passing and so the state is not shared.
“Ansible variables seem to have a global scope, because if I set one within the role it seems to be visible to all roles”
No exactly true for roles.
Ansible variables set in one role are available in roles further down so you can have roles that describe an environment, but they do have scope. In fact, you are guaranteed access to that particular value, without it being clobbered.
We’ve been through this a few times today on this list, in fact
When you say “further down”, are you referring to:
?
Talking with a colleague, I was assuming #1 and he was assuming #2.
I created a git repo that illustrates some behavior that surprised me. Most likely this is because I’m misunderstanding the paradigms at play; there’s a small chance that it’s a bug, but I figure the misunderstanding is more likely.
https://github.com/msabramo/ansible-roles-example
I would love to see an explanation of why this works the way it does so that I can have a deeper understanding of Ansible.
Thanks!
Marc
I mean Roles utilized after other roles.
I’m not going to read your git repo as we’ve been through this alot.
“I would love to see an explanation of why this works the way it does”
It’s so you can have environmental role data like so:
hosts: concord
roles:
eastcoast_datacenter_config
puppies
hosts: lexington
roles:
eastcoast_datacenter_config
apache
random_app
And define variables in roles and have those variables, like the name of an NTP server, available further down.
OK, thanks for clarifying. That’s good to know, since I want to understand clearly how things work before we get deep into this and start advocating for company-wide usage.
You might consider adding a note to the documentation about this (probably the “Variables” page). I looked around quite a bit and couldn’t find anything that quite covered this “inter-role” behavior of variables. The basic gist being “variables set in one role are available to roles that execute sequentially afterwards”.
I think we now understand and we’ve worked around our issue with a parameterized include, which we found to work by trial and error on our own.
That said, you may or may not be interested to know that in the git repo that I mentioned, I actually saw behavior that looks like a role was getting a value for a variable set in a LATER role. Not a big deal to us as we’re using parameterized includes now, but I mention it just in case it’s interesting or surprising.
" I actually saw behavior that looks like a role was getting a value for a variable set in a LATER role"
I am occasionally incorrect.
Vars do get compiled down first, tasks run in order, as variables do run in order, but I’d expect that.
My apologies on not remembering correctly - I do think that’s still fine due to the built-in protections around variable clobbering in roles (won’t happen, etc).
Cool. Mistakes happen. The important thing is that you explained why it works the way it does, so we understand what to expect.
Sorry to bring this up again. Was there an answer for - current group?
I appreciate that I can find the groups that are set for a host but I need to know what the current group that is being acted on.
The group is defined in the playbook but I need to loop over the hosts for that group in a template and don’t want all possible groups that the host is associated with
Many thanks
current_host = inventory_hostname
current_group = no singular, we always flatten to host, you can check
the current host's groups in "group_names" list of the current host
current_user = lookup('env', 'USER') ?
current_task = nothing represents the current task .. since you are in
the current task ..., not sure what you want here.
current_role = role_path|basename
Ok thanks,
I think I will need to pass it through from the playbook, the issue is that on a flat deployment where every group has the same host, when I use group_names I get all possible groups, not the one currently being handled.
This meant that I ended up with a duplicate list of about 12 hosts. Is it possible to append to a list in jinja. If so I could loop over groups_names, then over the hosts in that group and build up a list of hosts then uniq the list
{% for group in group_names %}
{% for host in groups[group] %}
…
<Location /admin>
SetHandler otas-handler
OTASHost 172.31.5.20 19202
OTASHost 172.31.5.20 19202
OTASHost 172.31.5.20 19202
OTASHost 172.31.5.20 19202
…
Thanks for the consise list of the others, very useful.
You seem to want the play_hosts variable (current active hosts in current play).
Yep that does indeed sound like what I need. Will give it a shot
Many thanks
Brian,
This is incredibly useful info and could be helpful to add to the official documentation.
Similar to the play_hosts variable, is there something like a “play_roles” variable that is an ordered list of roles for the current play? I’d like to be able to trigger certain behavior (restarts and such) based on the presence or absence of particular roles in that list.