1#!/usr/bin/python
2# -*- coding: utf-8 -*-
3
4# (c) 2012, Michael DeHaan <michael.dehaan@gmail.com>
5# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
6
7from __future__ import absolute_import, division, print_function
8__metaclass__ = type
9
10
11DOCUMENTATION = '''
12---
13module: setup
14version_added: historical
15short_description: Gathers facts about remote hosts
16options:
17    gather_subset:
18        version_added: "2.1"
19        description:
20            - "If supplied, restrict the additional facts collected to the given subset.
21              Possible values: C(all), C(min), C(hardware), C(network), C(virtual), C(ohai), and
22              C(facter). Can specify a list of values to specify a larger subset.
23              Values can also be used with an initial C(!) to specify that
24              that specific subset should not be collected.  For instance:
25              C(!hardware,!network,!virtual,!ohai,!facter). If C(!all) is specified
26              then only the min subset is collected. To avoid collecting even the
27              min subset, specify C(!all,!min). To collect only specific facts,
28              use C(!all,!min), and specify the particular fact subsets.
29              Use the filter parameter if you do not want to display some collected
30              facts."
31        default: "all"
32    gather_timeout:
33        version_added: "2.2"
34        description:
35            - Set the default timeout in seconds for individual fact gathering.
36        default: 10
37    filter:
38        version_added: "1.1"
39        description:
40            - If supplied, only return facts that match this shell-style (fnmatch) wildcard.
41        default: "*"
42    fact_path:
43        version_added: "1.3"
44        description:
45            - Path used for local ansible facts (C(*.fact)) - files in this dir
46              will be run (if executable) and their results be added to C(ansible_local) facts.
47              If a file is not executable it is read instead.
48              File/results format can be JSON or INI-format. The default C(fact_path) can be
49              specified in C(ansible.cfg) for when setup is automatically called as part of
50              C(gather_facts).
51              NOTE - For windows clients, the results will be added to a variable named after the
52              local file (without extension suffix), rather than C(ansible_local).
53            - Since Ansible 2.1, Windows hosts can use C(fact_path). Make sure that this path
54              exists on the target host. Files in this path MUST be PowerShell scripts C(.ps1)
55              which outputs an object. This object will be formatted by Ansible as json so the
56              script should be outputting a raw hashtable, array, or other primitive object.
57        default: /usr/local/etc/ansible/facts.d
58description:
59    - This module is automatically called by playbooks to gather useful
60      variables about remote hosts that can be used in playbooks. It can also be
61      executed directly by C(/usr/bin/ansible) to check what variables are
62      available to a host. Ansible provides many I(facts) about the system,
63      automatically.
64    - This module is also supported for Windows targets.
65notes:
66    - More ansible facts will be added with successive releases. If I(facter) or
67      I(ohai) are installed, variables from these programs will also be snapshotted
68      into the JSON file for usage in templating. These variables are prefixed
69      with C(facter_) and C(ohai_) so it's easy to tell their source. All variables are
70      bubbled up to the caller. Using the ansible facts and choosing to not
71      install I(facter) and I(ohai) means you can avoid Ruby-dependencies on your
72      remote systems. (See also M(community.general.facter) and M(community.general.ohai).)
73    - The filter option filters only the first level subkey below ansible_facts.
74    - If the target host is Windows, you will not currently have the ability to use
75      C(filter) as this is provided by a simpler implementation of the module.
76    - This module is also supported for Windows targets.
77    - This module should be run with elevated privileges on BSD systems to gather facts like ansible_product_version.
78    - Supports C(check_mode).
79author:
80    - "Ansible Core Team"
81    - "Michael DeHaan"
82'''
83
84EXAMPLES = """
85# Display facts from all hosts and store them indexed by I(hostname) at C(/tmp/facts).
86# ansible all -m ansible.builtin.setup --tree /tmp/facts
87
88# Display only facts regarding memory found by ansible on all hosts and output them.
89# ansible all -m ansible.builtin.setup -a 'filter=ansible_*_mb'
90
91# Display only facts returned by facter.
92# ansible all -m ansible.builtin.setup -a 'filter=facter_*'
93
94# Collect only facts returned by facter.
95# ansible all -m ansible.builtin.setup -a 'gather_subset=!all,!any,facter'
96
97- name: Collect only facts returned by facter
98  ansible.builtin.setup:
99    gather_subset:
100      - '!all'
101      - '!any'
102      - facter
103
104# Display only facts about certain interfaces.
105# ansible all -m ansible.builtin.setup -a 'filter=ansible_eth[0-2]'
106
107# Restrict additional gathered facts to network and virtual (includes default minimum facts)
108# ansible all -m ansible.builtin.setup -a 'gather_subset=network,virtual'
109
110# Collect only network and virtual (excludes default minimum facts)
111# ansible all -m ansible.builtin.setup -a 'gather_subset=!all,!any,network,virtual'
112
113# Do not call puppet facter or ohai even if present.
114# ansible all -m ansible.builtin.setup -a 'gather_subset=!facter,!ohai'
115
116# Only collect the default minimum amount of facts:
117# ansible all -m ansible.builtin.setup -a 'gather_subset=!all'
118
119# Collect no facts, even the default minimum subset of facts:
120# ansible all -m ansible.builtin.setup -a 'gather_subset=!all,!min'
121
122# Display facts from Windows hosts with custom facts stored in C(C:\\custom_facts).
123# ansible windows -m ansible.builtin.setup -a "fact_path='c:\\custom_facts'"
124"""
125
126# import module snippets
127from ..module_utils.basic import AnsibleModule
128
129from ansible.module_utils._text import to_text
130from ansible.module_utils.facts import ansible_collector, default_collectors
131from ansible.module_utils.facts.collector import CollectorNotFoundError, CycleFoundInFactDeps, UnresolvedFactDep
132from ansible.module_utils.facts.namespace import PrefixFactNamespace
133
134
135def main():
136    module = AnsibleModule(
137        argument_spec=dict(
138            gather_subset=dict(default=["all"], required=False, type='list'),
139            gather_timeout=dict(default=10, required=False, type='int'),
140            filter=dict(default="*", required=False),
141            fact_path=dict(default='/usr/local/etc/ansible/facts.d', required=False, type='path'),
142        ),
143        supports_check_mode=True,
144    )
145
146    gather_subset = module.params['gather_subset']
147    gather_timeout = module.params['gather_timeout']
148    filter_spec = module.params['filter']
149
150    # TODO: this mimics existing behavior where gather_subset=["!all"] actually means
151    #       to collect nothing except for the below list
152    # TODO: decide what '!all' means, I lean towards making it mean none, but likely needs
153    #       some tweaking on how gather_subset operations are performed
154    minimal_gather_subset = frozenset(['apparmor', 'caps', 'cmdline', 'date_time',
155                                       'distribution', 'dns', 'env', 'fips', 'local',
156                                       'lsb', 'pkg_mgr', 'platform', 'python', 'selinux',
157                                       'service_mgr', 'ssh_pub_keys', 'user'])
158
159    all_collector_classes = default_collectors.collectors
160
161    # rename namespace_name to root_key?
162    namespace = PrefixFactNamespace(namespace_name='ansible',
163                                    prefix='ansible_')
164
165    try:
166        fact_collector = ansible_collector.get_ansible_collector(all_collector_classes=all_collector_classes,
167                                                                 namespace=namespace,
168                                                                 filter_spec=filter_spec,
169                                                                 gather_subset=gather_subset,
170                                                                 gather_timeout=gather_timeout,
171                                                                 minimal_gather_subset=minimal_gather_subset)
172    except (TypeError, CollectorNotFoundError, CycleFoundInFactDeps, UnresolvedFactDep) as e:
173        # bad subset given, collector, idk, deps declared but not found
174        module.fail_json(msg=to_text(e))
175
176    facts_dict = fact_collector.collect(module=module)
177
178    module.exit_json(ansible_facts=facts_dict)
179
180
181if __name__ == '__main__':
182    main()
183