Ansible and Db2 for z/OS – Even More Fun with Collections
Introduction
In the last blog on Ansible and z/OS we walked through how to create a collection of modules to provide extended functionality. There was a lot of great information in that post about getting this all together and publishing on to Ansible Galaxy, but a part that I hadn’t worked out when I wrote that one (okay, hadn’t read up on) was how to implement usable shared functions for the modules in the collection.
This should be really simple – and mostly it is – but the challenge is really how we direct the module to find the function in the collection without accidentally making the function visible to the outside world. More on this after a better explanation of the challenge with an example.
Collection Module Functions
The challenge is probably best defined as “Where does Ansible load my module’s external functions from?”. In Windows or Linux operating systems we have well defined environment variables (PATH, LD_LIBRARY_PATH) that give the operating system a search path in the filesystem directories.
An example – actually in use in the triton.db2_zos collection:
- The collection contains several modules that need to execute a Db2 for z/OS command through the TSO command processor:
- db2_cmd.py
- db2_get_bpsize.py
:
- We’d like to create a single shared function – fn_db2_cmd – to perform this task
Where To Put Shared Functions
In the collection directory structure, modules that can be used by Ansible are placed in the “plugins/modules” path. Functions that can be called by these modules are generally shared in the “plugins/module_utils” path. This keeps their available scope limited to the collection modules and prevents them being accidentally made available to Ansible callers.
How To Reference Shared Functions In Collection Modules
To reference one of these functions, we need to use a specific route in the “ansible_collections” namespace – i.e.
ansible_collections.<col_ns>.<collection>.plugins.module_utils.<fn_module>
Where
- <col_ns> = collection namespace
- <collection> = collection name
- <fn_module> is the name of the function module name
An Example
Returning to our example above, in the triton.db2_zos collection, we want to introduce a shared function module called fn_db2.py which contains a function fn_db2_cmd to execute Db2 for z/OS commands for us and return the results.
With the fn_db2.py shared function module saved in plugins/module_utils, the collection directory structure looks like this:
We want to use the fn_db2_cmd function in our Ansible modules (e.g. db2_cmd.py), so we have to import the function like this:
from ansible_collections.triton.db2_zos.plugins.module_utils.fn_db2 import fn_db2_cmd
Then we can call it in the usual way:
result = fn_db2_cmd(db2ssid,command)
Conclusions
This is a really simple way of implementing these functions with the proviso that we don’t want to expose them outside of the collection.
Ansible continues to gain popularity for its clean and simple approach to automating tasks – even on mainframes! Combined with IBM’s port of Python – including ZIIP offload – it provides a powerful way to implement tasks across the estate.