First, I should warn you that what you are attempting is very likely
to turn into a large time sink without a fully satisfactory outcome on
the other side. You should probably reevaluate the scope of what you
are doing. Ask yourself these questions:
* Why support ansible less than 2.4? 2.3 and earlier are unsupported
by upstream. Any bugs there (including security bugs) will never be
fixed.
* Why try to shoehorn this into the ansible.module_utils framework? It
will be much easier to work with such old versions and current
versions and potentially new versions if you make your common code
into a library that is installed by the users into the remote machine
(host it on pypi for users to pip install or make system packages for
them to use their favorite distro tools or come up with something in
your own role that drops the file into a known location so your module
can add it to sys.path). You can also have your users install your
extensions into ansible's official module_utils directory on their
control machines. Then ansible can find it when it constructs modules
to send over the wire.
* Working at such a low level runs the risk of breaking when we next
enhance how modules are processed, sent over the wire, and invoked
remotely. A simpler solution that deviates less from the standard way
modules operate is less likely to break.
Those disclaimers out of the way, the first thing to figure out is
what your parameters actually are. You talk about adding custom
module_utils, circumventing ansiballz, and using imp in your module to
import your custom module_utils however, these are three different
things.
Ansiballz is the way we locate module_utils and package them together
with the module to be sent over the wire for execution by python on
the remote machine. It seems for your purposes you are trying to make
use of this to bundle your module_utils file with your modules
automatically. This had been available since 2.1.
Custom module_utils allows you to put a module_utils file into a
directory other than site-packages/ansible/module_utils and ansiballz
will find it when searching for the libraries it will package with the
module to transport over the wire. This has been available since 2.3.
Loading of libraries in the modules is entirely separate from the
latter two. You are free to add to sys.path inside of the module or
imp.load inside of the module but those won't affect what ansiballz
found on the controller side.
Taking a look at the code here:
https://github.com/Juniper/ansible-junos-stdlib/blob/master/library/juniper_junos_facts.py#L174
(Let me know if that's not the code that you are using), I think that
you're sorta taking the route that I mentioned above about trying not
to use the module_utils facility since you want to support old
versions of Ansible. Instead of hardcoding a path that your installer
establishes as I suggested, it looks like you've chosen to make that
path configurable via a module parameter. That seems like it will
work fine. It steps entirely outside of the AnsiballZ framework so
there won't be conflicts with it.
So it looks like the problem is you want to both use this code which
circumvents AnsiballZ and also *not* circumvent AnsiballZ. That's not
going to work. If you're going to work outside of the framework you
need to stay outside of the framework. If you're going to work within
the framework, you need to stay within the framework. AnsiballZ scans
module and module_utils code for python imports (Not a specific
string, btw, but imports in the abstract syntax tree). Any imports of
module_utils code that it finds are used to include those module_utils
into an archive with the module code and shipped to the remote machine
for execution. There is no facility to say "Include this module_util
file if you can find it. Otherwise, skip it". Being strict on the
controller side allows us to be less paranoid in module code...
Modules can safely assume that any module_utils import that they make
will have the module_utils file available to them. You do not have to
code a module to handle missing module_utils.
I would recommend dropping support for older Ansible versions as the
best way to handle this. However, if you feel you can't do that, then
you should commit fully to stepping outside of the framework. Do not
use module_utils at all for your common code. Instead, install it as
you do for Ansible-2.1 and 2.2 on every other version of Ansible as
well. Instead of trying to use import_juniper_junos_common only on
old versions of Ansible, use it on every version of Ansible. By doing
those things, you'll be able to stay outside of the framework and not
interfere with how AnsiballZ functions.
-Toshio