diff --git a/lib/ansible/module_utils/facts/__init__.py b/lib/ansible/module_utils/facts/__init__.py
index e69de29bb2..67d388fbd7 100644
--- a/lib/ansible/module_utils/facts/__init__.py
+++ b/lib/ansible/module_utils/facts/__init__.py
@@ -0,0 +1,21 @@
+# 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 .
+
+from __future__ import (absolute_import, division, print_function)
+__metaclass__ = type
+
+# import from the compat api because 2.0-2.3 had a module_utils.facts.ansible_facts
+# and get_all_facts in top level namespace
+from ansible.module_utils.facts.compat import ansible_facts, get_all_facts # noqa
diff --git a/lib/ansible/module_utils/facts/compat.py b/lib/ansible/module_utils/facts/compat.py
new file mode 100644
index 0000000000..ae51d5a455
--- /dev/null
+++ b/lib/ansible/module_utils/facts/compat.py
@@ -0,0 +1,75 @@
+# 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 .
+
+from __future__ import (absolute_import, division, print_function)
+__metaclass__ = type
+
+from ansible.module_utils.facts.namespace import PrefixFactNamespace
+from ansible.module_utils.facts import default_collectors
+from ansible.module_utils.facts import ansible_collector
+
+
+def get_all_facts(module):
+ '''compat api for ansible 2.2/2.3 module_utils.facts.get_all_facts method
+
+ Expects module to be an instance of AnsibleModule, with a 'gather_subset' param.
+
+ returns a dict mapping the bare fact name ('default_ipv4' with no 'ansible_' namespace) to
+ the fact value.'''
+
+ gather_subset = module.params['gather_subset']
+ return ansible_facts(module, gather_subset=gather_subset)
+
+
+def ansible_facts(module, gather_subset=None):
+ '''Compat api for ansible 2.0/2.2/2.3 module_utils.facts.ansible_facts method
+
+ 2.3/2.3 expects a gather_subset arg.
+ 2.0/2.1 does not except a gather_subset arg
+
+ So make gather_subsets an optional arg, defaulting to configured DEFAULT_GATHER_TIMEOUT
+
+ 'module' should be an instance of an AnsibleModule.
+
+ returns a dict mapping the bare fact name ('default_ipv4' with no 'ansible_' namespace) to
+ the fact value.
+ '''
+
+ gather_subset = gather_subset or module.params.get('gather_subset', ['all'])
+ gather_timeout = module.params.get('gather_timeout', 10)
+ filter_spec = module.params.get('filter', '*')
+
+ minimal_gather_subset = frozenset(['apparmor', 'caps', 'cmdline', 'date_time',
+ 'distribution', 'dns', 'env', 'fips', 'local', 'lsb',
+ 'pkg_mgr', 'platform', 'python', 'selinux',
+ 'service_mgr', 'ssh_pub_keys', 'user'])
+
+ all_collector_classes = default_collectors.collectors
+
+ # don't add a prefix
+ namespace = PrefixFactNamespace(namespace_name='ansible',
+ prefix='')
+
+ fact_collector = \
+ ansible_collector.get_ansible_collector(all_collector_classes=all_collector_classes,
+ namespace=namespace,
+ filter_spec=filter_spec,
+ gather_subset=gather_subset,
+ gather_timeout=gather_timeout,
+ minimal_gather_subset=minimal_gather_subset)
+
+ facts_dict = fact_collector.collect(module=module)
+
+ return facts_dict
diff --git a/lib/ansible/module_utils/facts/default_collectors.py b/lib/ansible/module_utils/facts/default_collectors.py
index 7a5caedbb7..ffd14a9e9a 100644
--- a/lib/ansible/module_utils/facts/default_collectors.py
+++ b/lib/ansible/module_utils/facts/default_collectors.py
@@ -123,7 +123,7 @@ collectors = [ApparmorFactCollector,
OpenBSDVirtualCollector,
NetBSDVirtualCollector,
SunOSVirtualCollector,
- HPUXVirtualCollector]
+ HPUXVirtualCollector,
-external_collectors = [FacterFactCollector,
- OhaiFactCollector]
+ FacterFactCollector,
+ OhaiFactCollector]
diff --git a/test/integration/targets/gathering_facts/test_gathering_facts.yml b/test/integration/targets/gathering_facts/test_gathering_facts.yml
index fab438a4ec..6b3ed31d9e 100644
--- a/test/integration/targets/gathering_facts/test_gathering_facts.yml
+++ b/test/integration/targets/gathering_facts/test_gathering_facts.yml
@@ -32,6 +32,7 @@
- 'ansible_mounts|default("UNDEF_NET") != "UNDEF_HW"'
- 'ansible_virtualization_role|default("UNDEF_VIRT") != "UNDEF_VIRT"'
+
- hosts: facthost19
tags: [ 'fact_min' ]
connection: local
@@ -260,3 +261,18 @@
assert:
that:
- '"{{ ansible_local.testfact.fact_dir }}" == "custom"'
+
+- hosts: facthost20
+ tags: [ 'fact_facter_ohai' ]
+ connection: local
+ gather_subset:
+ - facter
+ - ohai
+ gather_facts: yes
+ tasks:
+ - name: Test that retrieving facter and ohai doesnt fail
+ assert:
+ # not much to assert here, aside from not crashing, since test images dont have
+ # facter/ohai
+ that:
+ - 'ansible_user_id|default("UNDEF_MIN") != "UNDEF_MIN"'
diff --git a/test/units/module_utils/facts/test_ansible_collector.py b/test/units/module_utils/facts/test_ansible_collector.py
index fdf7a75ebc..52290a8835 100644
--- a/test/units/module_utils/facts/test_ansible_collector.py
+++ b/test/units/module_utils/facts/test_ansible_collector.py
@@ -293,3 +293,21 @@ class TestMinimalCollectedFacts(TestCollectedFacts):
expected_facts = ['gather_subset',
'module_setup']
not_expected_facts = ['lsb']
+
+
+class TestFacterCollectedFacts(TestCollectedFacts):
+ gather_subset = ['!all', 'facter']
+ min_fact_count = 1
+ max_fact_count = 10
+ expected_facts = ['gather_subset',
+ 'module_setup']
+ not_expected_facts = ['lsb']
+
+
+class TestOhaiCollectedFacts(TestCollectedFacts):
+ gather_subset = ['!all', 'ohai']
+ min_fact_count = 1
+ max_fact_count = 10
+ expected_facts = ['gather_subset',
+ 'module_setup']
+ not_expected_facts = ['lsb']
diff --git a/test/units/module_utils/facts/test_collector.py b/test/units/module_utils/facts/test_collector.py
index 76c291a936..9cc48b5a26 100644
--- a/test/units/module_utils/facts/test_collector.py
+++ b/test/units/module_utils/facts/test_collector.py
@@ -155,6 +155,30 @@ class TestCollectorClassesFromGatherSubset(unittest.TestCase):
self.assertIsInstance(res, list)
self.assertEqual(res, [default_collectors.EnvFactCollector])
+ def test_facter(self):
+ res = self._classes(all_collector_classes=default_collectors.collectors,
+ gather_subset=set(['env', 'facter']))
+ self.assertIsInstance(res, list)
+ self.assertEqual(set(res),
+ set([default_collectors.EnvFactCollector,
+ default_collectors.FacterFactCollector]))
+
+ def test_facter_ohai(self):
+ res = self._classes(all_collector_classes=default_collectors.collectors,
+ gather_subset=set(['env', 'facter', 'ohai']))
+ self.assertIsInstance(res, list)
+ self.assertEqual(set(res),
+ set([default_collectors.EnvFactCollector,
+ default_collectors.FacterFactCollector,
+ default_collectors.OhaiFactCollector]))
+
+ def test_just_facter(self):
+ res = self._classes(all_collector_classes=default_collectors.collectors,
+ gather_subset=set(['facter']))
+ self.assertIsInstance(res, list)
+ self.assertEqual(set(res),
+ set([default_collectors.FacterFactCollector]))
+
def test_collector_specified_multiple_times(self):
res = self._classes(all_collector_classes=default_collectors.collectors,
gather_subset=set(['platform', 'all', 'machine']))