(Developers) how to handle OS variance/detection in modules -- BSDs, other Unixes, etc

Nigel sent in a nice patch to allow the service module to do some
things different on BSD.

This is very much based on how we do things in the setup module, but a
little more simplified.

I've adapted it further.

This is the pattern we want to follow when adding better non-Linux
support to various modules.

Please test out the user module and let me know if there are any
problems, as changes have been rather large.

I would then perhaps suggest that if folks are intersted in assuring
all of the modules are happy on the BSDs, Solarises, etc, we being
making other modules start to follow
this pattern when we know they will have OS variance.

https://github.com/ansible/ansible/blob/devel/library/user

--Michael

I’m in the position of having written a module for Solaris SMF
services and SVR4 packages, both of which I was planning to send a
pull request for this week.

I had written entirely new modules for these for 2 reasons:

  1. I had read somewhere, although I can’t remember where, that the
    philosophy of Ansible was that when there are different features (e.g.
    yum vs apt) that there should be different modules, avoiding the need
    to rely on a lowest common denominator.

  2. Both of them have attributes that do not appear in the existing
    modules (e.g SMF services have properties that can be set on them,
    different for every service).

It sounds from the below that I now need to think of a different
approach. Is that true?

More directly relevant to the immediate discussion is that in this new
model, it’s not clear to me how to handle features that are present
only in a single implementation. For example, at user creation time
Solaris allows the assignment of RBAC profiles and auths, project
membership, process privileges and resource controls among others. How
would I implement these options in a generic user module? I may have
missed something while reading the source on my phone screen, so feel
free to point at an example.

I'm in the position of having written a module for Solaris SMF
services and SVR4 packages, both of which I was planning to send a
pull request for this week.

I had written entirely new modules for these for 2 reasons:

1) I had read somewhere, although I can't remember where, that the
philosophy of Ansible was that when there are different features (e.g.
yum vs apt) that there should be different modules, avoiding the need
to rely on a lowest common denominator.

Yep, definitely true of packaging. We're trying to keep user and
service together now though, and the service
module I think is eventually going to be refactored to use the new way
'user' does OS detection, and that will clean it
up some.

The main reason I like packaging seperate is the package names are
almost never the same between distros, and the idea
that you can have an easy OS independent playbook for something
involving a package is pretty unlikely. This just
makes it evident what you are dealing with.

I think this also ends up keeping the various packaging code a lot
easier to sort through.

2) Both of them have attributes that do not appear in the existing
modules (e.g SMF services have properties that can be set on them,
different for every service).

There might be a way to just add a "properties" option to the service
module in this case. Maybe you can share
what you have on github so I could understand better?

I'm not really a Solaris guy.

It sounds from the below that I now need to think of a different
approach. Is that true?

More directly relevant to the immediate discussion is that in this new
model, it's not clear to me how to handle features that are present
only in a single implementation. For example, at user creation time
Solaris allows the assignment of RBAC profiles and auths, project
membership, process privileges and resource controls among others. How
would I implement these options in a generic user module? I may have
missed something while reading the source on my phone screen, so feel
free to point at an example.

Basically in this case what we would do is make the module option
available to *all* types of modules, and then
it should probably raise an error if used in a class that didn't support it.

We might also just name these parameters with a common prefix like
"solaris_" to make it clear they are only used
there, if we are pretty sure they are not applicable to other
operating systems in a similar way.

But it would be really really nice if "user: name=timmy state=present"
generally works on all platforms, for cases
when people don't need to worry about those extra properties... if
that's not the way it works, I'm ok with that.

Let me know if you have further questions and so forth. That's just
basically what I'm thinking at present.

I would *VERY* much like to see our modules learn to be smarter about
the other Unixes, so I'm quite excited by this.

I'm in the position of having written a module for Solaris SMF
services and SVR4 packages, both of which I was planning to send a
pull request for this week.

Please do!

        -JP

I’m in the position of having written a module for Solaris SMF
services and SVR4 packages, both of which I was planning to send a
pull request for this week.

I had written entirely new modules for these for 2 reasons:

  1. I had read somewhere, although I can’t remember where, that the
    philosophy of Ansible was that when there are different features (e.g.
    yum vs apt) that there should be different modules, avoiding the need
    to rely on a lowest common denominator.

Yep, definitely true of packaging. We’re trying to keep user and
service together now though, and the service
module I think is eventually going to be refactored to use the new way
‘user’ does OS detection, and that will clean it
up some.

The main reason I like packaging seperate is the package names are
almost never the same between distros, and the idea
that you can have an easy OS independent playbook for something
involving a package is pretty unlikely. This just
makes it evident what you are dealing with.

I think this also ends up keeping the various packaging code a lot
easier to sort through.

I agree. Just to add more fun, Solaris 11 introduced a new packaging system (IPS), while still supporting the old one. All the native packages have new-style names, while new-style packages exist with old-style names to ease transistion. So, yet another module to come for IPS.

For those playing at home, my SVR4 (old-style) package module is at https://github.com/brontitall/ansible/blob/svr4pkg/library/svr4pkg

  1. Both of them have attributes that do not appear in the existing
    modules (e.g SMF services have properties that can be set on them,
    different for every service).

There might be a way to just add a “properties” option to the service
module in this case. Maybe you can share
what you have on github so I could understand better?

I’m not really a Solaris guy.

My SMF (service management facility) module is at https://github.com/brontitall/ansible/blob/smf/library/smf

At the moment, I have it accept arbitrary key=value options and treat all those it doesn’t recognize as properties to set. They all have a propgroup/property style name, so perhaps that’s a toe-hold to make something more general. Further, I just realised that I currently have no support for setting options that are not already present, since creating them requires providing a data type (e.g. count:, astring:, etc). This is a rarer, but still needed, procedure. So I need to think of a way to support that operation.

It sounds from the below that I now need to think of a different
approach. Is that true?

More directly relevant to the immediate discussion is that in this new
model, it’s not clear to me how to handle features that are present
only in a single implementation. For example, at user creation time
Solaris allows the assignment of RBAC profiles and auths, project
membership, process privileges and resource controls among others. How
would I implement these options in a generic user module? I may have
missed something while reading the source on my phone screen, so feel
free to point at an example.

Basically in this case what we would do is make the module option
available to all types of modules, and then
it should probably raise an error if used in a class that didn’t support it.

We might also just name these parameters with a common prefix like
“solaris_” to make it clear they are only used
there, if we are pretty sure they are not applicable to other
operating systems in a similar way.

But it would be really really nice if “user: name=timmy state=present”
generally works on all platforms, for cases
when people don’t need to worry about those extra properties… if
that’s not the way it works, I’m ok with that.

As a data point, as far as I have tested, the basics of user creation already work on Solaris, since the most basic useradd command options are the same as on Linux. I haven’t tried every option though.

> I'm in the position of having written a module for Solaris SMF
> services and SVR4 packages, both of which I was planning to send a
> pull request for this week.

Cool! I'm glad to see some action in ansible on solaris.

As a data point, as far as I have tested, the basics of user creation
already work on Solaris, since the most basic useradd command options are
the same as on Linux. I haven't tried every option though.

I think the "biggest" thing missing is probably the password support.
Solaris (as far as I know) has no good way to automate setting a
password, short of editing the /etc/shadow file or automating the
prompts at the passwd command. I believe there are some third-party
tools out there that do it but they're not part of solaris. As far as
I know all the other CM tools just edit /etc/shadow.

Ah, yes, you're right of course. This limitation of the OS means that CM
tools bypass PAM for password setting, which is … suboptimal.