Repository: ansible/ansible

Issue:
Cannot use command/shell module to run `ansible --version`: "ImportError: No module named constants"
##### ISSUE TYPE
- Bug Report
##### ANSIBLE VERSION

```
ansible 2.1.0.0 (stable-2.1 5a9e826647) last updated 2016/04/28 15:33:33 (GMT -400)
  lib/ansible/modules/core: (detached HEAD 6498f6e4d0) last updated 2016/04/27 19:42:05 (GMT -400)
  lib/ansible/modules/extras: (detached HEAD 5747418146) last updated 2016/04/28 14:26:35 (GMT -400)
  config file = /Users/jladd/.ansible.cfg
  configured module search path = Default w/o overrides
```
##### CONFIGURATION

```
[ssh_connection]
control_path = %(directory)s/%%h-%%r
```
##### OS / ENVIRONMENT

N/A
##### SUMMARY

The Tower team has a playbook that deploys an ec2 system, installs Ansible, and installs Tower. After installing ansible, we use ansible to run `ansible --version` on the test system to make sure that ansible was installed. This works with ansible v2.0.2.0. When you do this with Ansible v2.1.0, though, you get the following error:

```
ansible all -i inventory -m command -a "ansible --version"
localhost | FAILED | rc=1 >>
Traceback (most recent call last):
  File "/Users/jladd/ansible/stable-2.1/bin/ansible", line 44, in <module>
    import ansible.constants as C
ImportError: No module named constants
```
##### STEPS TO REPRODUCE

Create the following inventory file:

```
[local]
localhost ansible_connection=local
```

Run the following:

```
ansible all -i inventory -m command -a "ansible --version"
```
##### EXPECTED RESULTS

Expect this to return ansible version - for example:

```
ansible all -i inventory -m command -a "ansible --version"
localhost | SUCCESS | rc=0 >>
ansible 2.0.2.0
  config file = /Users/jladd/.ansible.cfg
  configured module search path = Default w/o overrides
```
##### ACTUAL RESULTS

Seeing error `ImportError: No module named constants`:

```
ansible -vvvv all -i inventory -m command -a "ansible --version"
Using /Users/jladd/.ansible.cfg as config file
Loaded callback minimal of type stdout, v2.0
<localhost> ESTABLISH LOCAL CONNECTION FOR USER: jladd
<localhost> EXEC /bin/sh -c '( umask 77 && mkdir -p "` echo $HOME/.ansible/tmp/ansible-tmp-1461872895.43-53508417178816 `" && echo "` echo $HOME/.ansible/tmp/ansible-tmp-1461872895.43-53508417178816 `" )'
<localhost> PUT /var/folders/9f/lfcc9zd96fngv4bpptdg86700000gn/T/tmplGbPBF TO /Users/jladd/.ansible/tmp/ansible-tmp-1461872895.43-53508417178816/command
<localhost> EXEC /bin/sh -c 'LANG=en_US.UTF-8 LC_ALL=en_US.UTF-8 LC_MESSAGES=en_US.UTF-8 /usr/bin/python /Users/jladd/.ansible/tmp/ansible-tmp-1461872895.43-53508417178816/command; rm -rf "/Users/jladd/.ansible/tmp/ansible-tmp-1461872895.43-53508417178816/" > /dev/null 2>&1'
localhost | FAILED | rc=1 >>
Traceback (most recent call last):
  File "/Users/jladd/ansible/stable-2.1/bin/ansible", line 44, in <module>
    import ansible.constants as C
ImportError: No module named constants
```



Discussion:
Confirmed that this:
- Fails on devel 
- Fails on 2.1.0 (stable-2.1)
- Works on 2.0.2.0
- Works on 1.9.6 (stable-1.9)

Reproduced with the following test which also points out the problem ...

```
[jtanner@ansidev AP-15655]$ cat checkpython.py 
#!/usr/bin/env python

import sys
print(sys.path)

import ansible
print ansible.__file__

import ansible.constants


[jtanner@ansidev AP-15655]$ ansible localhost -vvvv -c local -m shell -a "./checkpython.py"
No config file found; using defaults
 [WARNING]: Host file not found: /etc/ansible/hosts

Loaded callback minimal of type stdout, v2.0
<127.0.0.1> ESTABLISH LOCAL CONNECTION FOR USER: jtanner
<127.0.0.1> EXEC /bin/sh -c '( umask 77 && mkdir -p "` echo $HOME/.ansible/tmp/ansible-tmp-1461876101.38-41882275521445 `" && echo "` echo $HOME/.ansible/tmp/ansible-tmp-1461876101.38-41882275521445 `" )'
<127.0.0.1> PUT /tmp/tmpIB0ig6 TO /home/jtanner/.ansible/tmp/ansible-tmp-1461876101.38-41882275521445/command
<127.0.0.1> EXEC /bin/sh -c 'LANG=en_US.UTF-8 LC_ALL=en_US.UTF-8 LC_MESSAGES=en_US.UTF-8 /usr/bin/python /home/jtanner/.ansible/tmp/ansible-tmp-1461876101.38-41882275521445/command; rm -rf "/home/jtanner/.ansible/tmp/ansible-tmp-1461876101.38-41882275521445/" > /dev/null 2>&1'
localhost | FAILED | rc=1 >>
['/workspace/issues/AP-15655', '/tmp/ansible_OQMNB_/ansible_modlib.zip', '/tmp/ansible.v2.1.0.0-0.1.rc1/lib', '/workspace/issues/AP-15655', '/usr/lib64/python27.zip', '/usr/lib64/python2.7', '/usr/lib64/python2.7/plat-linux2', '/usr/lib64/python2.7/lib-tk', '/usr/lib64/python2.7/lib-old', '/usr/lib64/python2.7/lib-dynload', '/usr/lib64/python2.7/site-packages', '/usr/lib/python2.7/site-packages']
/tmp/ansible_OQMNB_/ansible_modlib.zip/ansible/__init__.pyTraceback (most recent call last):
  File "./checkpython.py", line 9, in <module>
    import ansible.constants
ImportError: No module named constants



```


Resolving patch:
diff --git a/lib/ansible/module_utils/basic.py b/lib/ansible/module_utils/basic.py
--- a/lib/ansible/module_utils/basic.py
+++ b/lib/ansible/module_utils/basic.py
@@ -1975,6 +1975,19 @@ def run_command(self, args, check_rc=False, close_fds=True, executable=None, dat
             old_env_vals['PATH'] = os.environ['PATH']
             os.environ['PATH'] = "%s:%s" % (path_prefix, os.environ['PATH'])
 
+        # If using test-module and explode, the remote lib path will resemble ...
+        #   /tmp/test_module_scratch/debug_dir/ansible/module_utils/basic.py
+        # If using ansible or ansible-playbook with a remote system ...
+        #   /tmp/ansible_vmweLQ/ansible_modlib.zip/ansible/module_utils/basic.py
+
+        # Clean out python paths set by ziploader
+        if 'PYTHONPATH' in os.environ:
+            pypaths = os.environ['PYTHONPATH'].split(':')
+            pypaths = [x for x in pypaths \
+                        if not x.endswith('/ansible_modlib.zip') \
+                        and not x.endswith('/debug_dir')]
+            os.environ['PYTHONPATH'] = ':'.join(pypaths)
+
         # create a printable version of the command for use
         # in reporting later, which strips out things like
         # passwords from the args list
@@ -2016,7 +2029,6 @@ def run_command(self, args, check_rc=False, close_fds=True, executable=None, dat
             stdin=st_in,
             stdout=subprocess.PIPE,
             stderr=subprocess.PIPE,
-            env=os.environ,
         )
 
         if cwd and os.path.isdir(cwd):


Test patch: