Repository: ansible/ansible

Issue:
module_utils atomic_move causes traceback when safe write fails
##### ISSUE TYPE
 - Bug Report

##### COMPONENT NAME
module_utils

##### ANSIBLE VERSION
```
ansible 2.2.1.0
  config file = /Users/cobli/cobli/herbie/deploy/ansible/ansible.cfg
  configured module search path = Default w/o overrides
```

##### CONFIGURATION
Irrelevant

##### OS / ENVIRONMENT
Irrelevant

##### SUMMARY
module_utils.basic.atomic_move crashes when attempting to handle some exceptions due to a typo, attempting to use an uninitialized variable `exception` instead of calling `get_exception`

##### STEPS TO REPRODUCE
Use the lineinfile to modify /etc/hosts (a bind-mounted file) in a Docker container.

##### EXPECTED RESULTS
A failed task with the corresponding error message.

##### ACTUAL RESULTS
Traceback.

```
An exception occurred during task execution. The full traceback is:
Traceback (most recent call last):
  File "/tmp/ansible_puPX1w/ansible_module_lineinfile.py", line 425, in <module>
    main()
  File "/tmp/ansible_puPX1w/ansible_module_lineinfile.py", line 417, in main
    ins_aft, ins_bef, create, backup, backrefs)
  File "/tmp/ansible_puPX1w/ansible_module_lineinfile.py", line 298, in present
    write_changes(module, b_lines, dest)
  File "/tmp/ansible_puPX1w/ansible_module_lineinfile.py", line 175, in write_changes
    unsafe_writes=module.params['unsafe_writes'])
  File "/tmp/ansible_puPX1w/ansible_modlib.zip/ansible/module_utils/basic.py", line 2001, in atomic_move
NameError: global name 'exception' is not defined

failed: [sensu-1] (item=172.17.0.5) => {
    "failed": true,
    "invocation": {
        "module_name": "lineinfile"
    },
    "item": "172.17.0.5",
    "module_stderr": "Traceback (most recent call last):\n  File \"/tmp/ansible_puPX1w/ansible_module_lineinfile.py\", line 425, in <module>\n    main()\n  File \"/tmp/ansible_puPX1w/ansible_module_lineinfile.py\", line 417, in main\n    ins_aft, ins_bef, create, backup, backrefs)\n  File \"/tmp/ansible_puPX1w/ansible_module_lineinfile.py\", line 298, in present\n    write_changes(module, b_lines, dest)\n  File \"/tmp/ansible_puPX1w/ansible_module_lineinfile.py\", line 175, in write_changes\n    unsafe_writes=module.params['unsafe_writes'])\n  File \"/tmp/ansible_puPX1w/ansible_modlib.zip/ansible/module_utils/basic.py\", line 2001, in atomic_move\nNameError: global name 'exception' is not defined\n",
    "module_stdout": "",
    "msg": "MODULE FAILURE"
}
```


Discussion:
Looks like 8b08a28c895666b9fc673e5bf26bd91b2be77fe6 may be the culprit here.

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
@@ -2086,12 +2086,14 @@ def atomic_move(self, src, dest, unsafe_writes=False):
                         try:
                             os.rename(b_tmp_dest_name, b_dest)
                         except (shutil.Error, OSError, IOError):
+                            e = get_exception()
                             if unsafe_writes:
-                                self._unsafe_writes(b_tmp_dest_name, b_dest, get_exception())
+                                self._unsafe_writes(b_tmp_dest_name, b_dest, e)
                             else:
-                                self.fail_json(msg='Could not replace file: %s to %s: %s' % (src, dest, exception))
+                                self.fail_json(msg='Could not replace file: %s to %s: %s' % (src, dest, e))
                     except (shutil.Error, OSError, IOError):
-                        self.fail_json(msg='Could not replace file: %s to %s: %s' % (src, dest, exception))
+                        e = get_exception()
+                        self.fail_json(msg='Could not replace file: %s to %s: %s' % (src, dest, e))
                 finally:
                     self.cleanup(b_tmp_dest_name)
 


Test patch: