So I’ve been scavenging through a lot of posts to figure out how to deal with heterogeneous environments.
The two approaches that I see most are either using “group_by” or using “when”.
For example:
site.yml:
name: whatever
tasks:
group_by: key={{ansible_os_family}
hosts: Debian
roles:
role: rolename-debian
hosts: Darwin
roles:
role: rolename-darwin
My problem with this approach is that you are saying that a role is “OS” specific, even though it’s not. Perhaps this has to do with my definition of role, but as I see it a ‘role’ is a task that any given node can perform.
I should be able to assign the ‘role’ ‘nginx’ to a variety of hosts, how those hosts then implement that role should be defined within the ‘role’ definition.
Now supposedly you can do this using the ‘when’ conditional statement, you would then end up with something like:
include: brew.yml
when ansible_os_family == “Darwin”
However this is rather chatty, especially when these files include files of there own. And with chatty I mean every task, even when the ‘when’ clause is not matched is being shown, now you can set 'show_skipped_hosts" in the ansible configuration, however this still shows the headers of tasks that are (not) being processed.
Should I be dealing with this in a different fashion?
What I’m trying to accomplish is having a playbook that installs a package on a bunch of machines (running differents OS’s), then configure that package based on the OS and configure the service accordingly.
IMHO the latter approach is the way to go, however the ‘chattyness’ is killing my operators.
Most all ansible modules already abstract out OS details.
Package managers we want you to know - not only do package names change, but the way you interact with those packages change - think of how different Apache is between Ubuntu and CentOS for instance.
You can do things like
include: “{{ ansible_os_family }}.yml”
And you can also do things like use “include_vars” with similar tricks where you want to maintain differences.
In most cases you’ll only differ by variables except having a few tasks with “when” statements on them keying off the OS - which will also minimize task duplication.
Sure, but that’s exactly the thing I would like to deal with within a role, within a role however you can’t perform the ‘include’ you stated in your post as “ansible_os_family” doesn’t seem to evaluate.
To see how far I could come I went with the second approach that I mentioned in my original post.
With some tinkering I managed to abstract away most of the differences, it’s way too chatty though but it works.
However, I am stuck with the Handlers. As ‘service’ isn’t supported on OSX I had the idea of defining my own Handler like:
This works, except the ‘when’ clause isn’t processed, after all as per the manual, handlers are matched by name. I could then go down the lane of naming the service accordingly:
name: restart {{ ansible_os_family }}
however this doesn’t work either as you can’t seem to dynamically set the name of a handler, so the only approach that I can think of that does work is giving the handlers another name:
for example “restart nginx darwin / restart nginx ubuntu”.
However that means that in my generic code where I change a file I will have to make a choice which handler to notify based on the OS I’m running on; same problem as before, you can’t do:
notify:
restart nginx {{ ansible_os_family }}
Things would be a lot easier if ‘Service’ would be implemented for OS-X and package would be abstracted (like for example Salt does).
“It doesn’t work for me (unless I create a file called roles/foobar/tasks/{{ansible_os_family}}.yml… ).”
So in other words, it works
Also, public service announcement - Don’t use ansible_pkg_mgr like that - it won’t join mulitple package installs using with_items into single transactions. If you want to do a “when: ansible_pkg_mgr == ‘apt’” that will still keep it going.
“It doesn’t work for me (unless I create a file called roles/foobar/tasks/{{ansible_os_family}}.yml… ).”
So in other words, it works
No, sorry, I meant literally “{{ansible_os_family}}.yml” – it doesn’t seem to do templating for the included file name. I think I remember this being a popular feature request some time ago, but you wrote it’s important that the list of tasks in a play is fixed for every host (and it’s determined on parsing time, not run-time). Correct me if I missed something.
Also, public service announcement - Don’t use ansible_pkg_mgr like that - it won’t join mulitple package installs using with_items into single transactions. If you want to do a “when: ansible_pkg_mgr == ‘apt’” that will still keep it going.
Yes, in my roles I am working around that by supplying comma-separated lists myself instead of using with_items. Using ansible_pkg_mgr also has the disadvantage of not being able to use apt/yum/portage-specific parameters, but for virtually all of my package-manager needs it is sufficient – and so, so, so much nicer in the output
"It doesn't work for me (unless I create a file called
roles/foobar/tasks/{{ansible_os_family}}.yml... )."
So in other words, it works
No, sorry, I meant literally "{{ansible_os_family}}.yml" -- it doesn't
seem to do templating for the included file name. I think I remember this
being a popular feature request some time ago, but you wrote it's important
that the list of tasks in a play is fixed for every host (and it's
determined on parsing time, not run-time). Correct me if I missed something.
Sorry, yes, It is true you can't include off inventory scoped variables
like that.
That’s exactly the workaround I’m using now, however you can’t do something similar for ‘notify’.
e.g.
notify:
reload {{ ansible_os_family }}
doesn’t work, putting a when clause on the entire block just for the sake of using different notifiers means duplication of an entire block and again will also increase the chatty-ness
What’s the preferred way of dealing with variable notifiers?