Repository: ansible/ansible

Issue:
redis cache using --flush-cache fails: cannot concatenate 'str' and 'Host' objects
##### ISSUE TYPE
 - Bug Report

##### COMPONENT NAME
cache/redis.py

##### ANSIBLE VERSION
```
ansible 2.2.0.0
  config file = /etc/ansible/ansible.cfg
  configured module search path = Default w/o overrides
```

##### CONFIGURATION
```
fact_caching=redis
```

##### OS / ENVIRONMENT
CentOS 7, though unlikely to be OS specific

##### SUMMARY
Using the redis caching fails if there are already items in the cache.

##### STEPS TO REPRODUCE

Populate redis cache, normal gather facts playbook suffices.
Run playbook to gather facts again w/ --flush-cache.


##### EXPECTED RESULTS
No error.

##### ACTUAL RESULTS

```
/usr/bin/ansible-playbook playbooks/gather-facts.yml -i inventory/_test-all --flush-cache -vvvvv
Using /home/jenkins-slave/slave/workspace/Ansible Fact Sync/ansible.cfg as config file
ERROR! Unexpected Exception: cannot concatenate 'str' and 'Host' objects
the full traceback was:

Traceback (most recent call last):
  File "/usr/bin/ansible-playbook", line 103, in <module>
    exit_code = cli.run()
  File "/usr/lib/python2.7/site-packages/ansible/cli/playbook.py", line 154, in run
    variable_manager.clear_facts(host)
  File "/usr/lib/python2.7/site-packages/ansible/vars/__init__.py", line 640, in clear_facts
    del self._fact_cache[hostname]
  File "/usr/lib/python2.7/site-packages/ansible/plugins/cache/__init__.py", line 52, in __delitem__
    self._plugin.delete(key)
  File "/usr/lib/python2.7/site-packages/ansible/plugins/cache/redis.py", line 89, in delete
    self._cache.delete(self._make_key(key))
  File "/usr/lib/python2.7/site-packages/ansible/plugins/cache/redis.py", line 54, in _make_key
    return self._prefix + key
TypeError: cannot concatenate 'str' and 'Host' objects
```



Discussion:
I can reproduce this in 2.3/devel

```
[fedora-23:ansible (fix_tests %)]$ ansible-playbook -i hosts -v -v -v ping.yml --flush-cache
Using ansible_redis_caching.cfg as config file
ERROR! Unexpected Exception: coercing to Unicode: need string or buffer, Host found
the full traceback was:

Traceback (most recent call last):
  File "/home/adrian/src/ansible/bin/ansible-playbook", line 103, in <module>
    exit_code = cli.run()
  File "/home/adrian/src/ansible/lib/ansible/cli/playbook.py", line 152, in run
    variable_manager.clear_facts(host)
  File "/home/adrian/src/ansible/lib/ansible/vars/__init__.py", line 646, in clear_facts
    del self._fact_cache[hostname]
  File "/home/adrian/src/ansible/lib/ansible/plugins/cache/__init__.py", line 52, in __delitem__
    self._plugin.delete(key)
  File "/home/adrian/src/ansible/lib/ansible/plugins/cache/redis.py", line 89, in delete
    self._cache.delete(self._make_key(key))
  File "/home/adrian/src/ansible/lib/ansible/plugins/cache/redis.py", line 54, in _make_key
    return self._prefix + key
TypeError: coercing to Unicode: need string or buffer, Host found
```

Resolving patch:
diff --git a/lib/ansible/cli/playbook.py b/lib/ansible/cli/playbook.py
--- a/lib/ansible/cli/playbook.py
+++ b/lib/ansible/cli/playbook.py
@@ -148,8 +148,7 @@ def run(self):
 
         # flush fact cache if requested
         if self.options.flush_cache:
-            for host in inventory.list_hosts():
-                variable_manager.clear_facts(host)
+            self._flush_cache(inventory, variable_manager)
 
         # create the playbook executor, which manages running the plays via a task queue manager
         pbex = PlaybookExecutor(playbooks=self.args, inventory=inventory, variable_manager=variable_manager, loader=loader, options=self.options, passwords=passwords)
@@ -218,3 +217,8 @@ def _process_block(b):
             return 0
         else:
             return results
+
+    def _flush_cache(self, inventory, variable_manager):
+        for host in inventory.list_hosts():
+            hostname = host.get_name()
+            variable_manager.clear_facts(hostname)


Test patch:
diff --git a/test/units/cli/test_playbook.py b/test/units/cli/test_playbook.py
new file mode 100644
--- /dev/null
+++ b/test/units/cli/test_playbook.py
@@ -0,0 +1,40 @@
+# (c) 2016, Adrian Likins <alikins@redhat.com>
+#
+# This file is part of Ansible
+#
+# Ansible is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# Ansible is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with Ansible.  If not, see <http://www.gnu.org/licenses/>.
+
+# Make coding more python3-ish
+from __future__ import (absolute_import, division, print_function)
+__metaclass__ = type
+
+from ansible.compat.tests import unittest
+from units.mock.loader import DictDataLoader
+
+from ansible.inventory import Inventory
+from ansible.vars import VariableManager
+
+from ansible.cli.playbook import PlaybookCLI
+
+
+class TestPlaybookCLI(unittest.TestCase):
+    def test_flush_cache(self):
+        cli = PlaybookCLI(args=["--flush-cache", "foobar.yml"])
+
+        variable_manager = VariableManager()
+        fake_loader = DictDataLoader({'foobar.yml': ""})
+        inventory = Inventory(loader=fake_loader, variable_manager=variable_manager, host_list=['testhost'])
+
+        cli._flush_cache(inventory, variable_manager)
+        self.assertFalse('testhost' in variable_manager._fact_cache)