From 88772b60035bd4bc06629aa8cc5cf76123d447f9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Yannig=20Perr=C3=A9?= Date: Sat, 12 Mar 2016 10:22:49 +0100 Subject: [PATCH 1/5] Add a way to restrict gathered facts in Ansible: - Using gather_subset options - By ignoring ohai/chef or facter/puppet facts --- docsite/rst/glossary.rst | 3 +- docsite/rst/intro_configuration.rst | 23 +++++++++++- examples/ansible.cfg | 15 ++++++++ lib/ansible/constants.py | 3 ++ lib/ansible/executor/play_iterator.py | 21 ++++++++++- lib/ansible/module_utils/facts.py | 29 +++++++++++++-- lib/ansible/playbook/play.py | 3 ++ test/integration/test_gathering_facts.yml | 45 +++++++++++++++++++++++ 8 files changed, 133 insertions(+), 9 deletions(-) create mode 100644 test/integration/test_gathering_facts.yml diff --git a/docsite/rst/glossary.rst b/docsite/rst/glossary.rst index d05481a621..7c5bd5f812 100644 --- a/docsite/rst/glossary.rst +++ b/docsite/rst/glossary.rst @@ -71,8 +71,7 @@ Facts Facts are simply things that are discovered about remote nodes. While they can be used in playbooks and templates just like variables, facts are things that are inferred, rather than set. Facts are automatically discovered by Ansible when running plays by executing the internal 'setup' module on the remote nodes. You never have to call the setup module explicitly, it just runs, but it can be disabled to save time if it is -not needed. For the convenience of users who are switching from other configuration management systems, the fact module will also pull in facts from the 'ohai' and 'facter' -tools if they are installed, which are fact libraries from Chef and Puppet, respectively. +not needed or to reduce to a subset. For the convenience of users who are switching from other configuration management systems, the fact module will also pull in facts from the 'ohai' and 'facter' tools if they are installed, which are fact libraries from Chef and Puppet, respectively. You can also ignore them and save time at runtime execution. Filter Plugin +++++++++++++ diff --git a/docsite/rst/intro_configuration.rst b/docsite/rst/intro_configuration.rst index 0bc6fbfad7..ddcf673618 100644 --- a/docsite/rst/intro_configuration.rst +++ b/docsite/rst/intro_configuration.rst @@ -353,6 +353,25 @@ This option can be useful for those wishing to save fact gathering time. Both 's gathering = smart +.. versionadded:: 2.1 + +You can specify a subset of gathered facts using the following options: + + gather_subset = all + +:all: gather all subsets +:min: gather a very limited set of facts +:network: gather min and network facts +:hardware: gather min and hardware facts (longest facts to retrieve) +:virtual: gather min and virtual facts + +You can combine them using comma separated list (ex: min,network,virtual) + +You can also disable puppet facter or chef ohai facts collection using following options: + + ignore_ohai = True + ignore_facter = True + hash_behaviour ============== @@ -367,7 +386,7 @@ official examples repos do not use this setting:: The valid values are either 'replace' (the default) or 'merge'. -.. versionadded: '2.0' +.. versionadded:: 2.0 If you want to merge hashes without changing the global settings, use the `combine` filter described in :doc:`playbooks_filters`. @@ -585,7 +604,7 @@ The directory will be created if it does not already exist. roles_path ========== -.. versionadded: '1.4' +.. versionadded:: 1.4 The roles path indicate additional directories beyond the 'roles/' subdirectory of a playbook project to search to find Ansible roles. For instance, if there was a source control repository of common roles and a different repository of playbooks, you might diff --git a/examples/ansible.cfg b/examples/ansible.cfg index 8465ccca4b..181630f9c6 100644 --- a/examples/ansible.cfg +++ b/examples/ansible.cfg @@ -31,6 +31,21 @@ # explicit - do not gather by default, must say gather_facts: True #gathering = implicit +# by default retrieve all facts subsets +# all - gather all subsets +# min - gather a very limited set of facts +# network - gather min and network facts +# hardware - gather hardware facts (longest facts to retrieve) +# virtual - gather min and virtual facts +# You can combine them using comma (ex: min,network,virtual) +#gather_subset = all + +# by default run ohai +#ignore_ohai = False + +# by default run facter +#ignore_facter = False + # additional paths to search for roles in, colon separated #roles_path = /etc/ansible/roles diff --git a/lib/ansible/constants.py b/lib/ansible/constants.py index 4def61e1aa..7e91233fca 100644 --- a/lib/ansible/constants.py +++ b/lib/ansible/constants.py @@ -156,6 +156,9 @@ DEFAULT_PRIVATE_ROLE_VARS = get_config(p, DEFAULTS, 'private_role_vars', 'ANSIBL DEFAULT_JINJA2_EXTENSIONS = get_config(p, DEFAULTS, 'jinja2_extensions', 'ANSIBLE_JINJA2_EXTENSIONS', None) DEFAULT_EXECUTABLE = get_config(p, DEFAULTS, 'executable', 'ANSIBLE_EXECUTABLE', '/bin/sh') DEFAULT_GATHERING = get_config(p, DEFAULTS, 'gathering', 'ANSIBLE_GATHERING', 'implicit').lower() +DEFAULT_GATHER_SUBSET = get_config(p, DEFAULTS, 'gather_subset', 'ANSIBLE_GATHER_SUBSET', 'all').lower() +DEFAULT_IGNORE_OHAI = get_config(p, DEFAULTS, 'ignore_ohai', 'ANSIBLE_IGNORE_OHAI', False, boolean=True) +DEFAULT_IGNORE_FACTER = get_config(p, DEFAULTS, 'ignore_facter', 'ANSIBLE_IGNORE_FACTER', False, boolean=True) DEFAULT_LOG_PATH = get_config(p, DEFAULTS, 'log_path', 'ANSIBLE_LOG_PATH', '', ispath=True) DEFAULT_FORCE_HANDLERS = get_config(p, DEFAULTS, 'force_handlers', 'ANSIBLE_FORCE_HANDLERS', False, boolean=True) DEFAULT_INVENTORY_IGNORE = get_config(p, DEFAULTS, 'inventory_ignore_extensions', 'ANSIBLE_INVENTORY_IGNORE', ["~", ".orig", ".bak", ".ini", ".cfg", ".retry", ".pyc", ".pyo"], islist=True) diff --git a/lib/ansible/executor/play_iterator.py b/lib/ansible/executor/play_iterator.py index 83abb40bbc..93321ce8ae 100644 --- a/lib/ansible/executor/play_iterator.py +++ b/lib/ansible/executor/play_iterator.py @@ -151,11 +151,30 @@ class PlayIterator: self._play = play self._blocks = [] + # Default options to gather + gather_subset = C.DEFAULT_GATHER_SUBSET + ignore_ohai = C.DEFAULT_IGNORE_OHAI + ignore_facter = C.DEFAULT_IGNORE_FACTER + + # Retrieve subset to gather + if self._play.gather_subset is not None: + gather_subset = self._play.gather_subset + # ignore ohai + if self._play.ignore_ohai is not None: + ignore_ohai = self._play.ignore_ohai + # ignore puppet facter + if self._play.ignore_facter is not None: + ignore_facter = self._play.ignore_facter + setup_block = Block(play=self._play) setup_task = Task(block=setup_block) setup_task.action = 'setup' setup_task.tags = ['always'] - setup_task.args = {} + setup_task.args = { + 'gather_subset': gather_subset, + 'ignore_ohai' : ignore_ohai, + 'ignore_facter': ignore_facter, + } setup_task.set_loader(self._play._loader) setup_block.block = [setup_task] diff --git a/lib/ansible/module_utils/facts.py b/lib/ansible/module_utils/facts.py index 1aa16c9fee..d683ee2a26 100644 --- a/lib/ansible/module_utils/facts.py +++ b/lib/ansible/module_utils/facts.py @@ -159,6 +159,9 @@ class Facts(object): { 'path' : '/usr/local/sbin/pkg', 'name' : 'pkgng' }, ] + # Allowed fact subset for gather_subset options + ALLOWED_FACT_SUBSET = frozenset([ 'all', 'min', 'network', 'hardware', 'virtual' ]) + def __init__(self, load_on_init=True): self.facts = {} @@ -3067,15 +3070,33 @@ def get_file_lines(path): return ret def ansible_facts(module): + # Retrieve module parameters + gather_subset = [ 'all' ] + if 'gather_subset' in module.params: + gather_subset = module.params['gather_subset'] + + # Retrieve all facts elements + if 'all' in gather_subset: + gather_subset = [ 'min', 'hardware', 'network', 'virtual' ] + + # Check subsets and forbid unallowed name + for subset in gather_subset: + if subset not in Facts.ALLOWED_FACT_SUBSET: + raise TypeError("Bad subset '%s' given to Ansible. gather_subset options allowed: %s" % (subset, ", ".join(Facts.ALLOWED_FACT_SUBSET))) + facts = {} facts.update(Facts().populate()) - facts.update(Hardware().populate()) - facts.update(Network(module).populate()) - facts.update(Virtual().populate()) + if 'hardware' in gather_subset: + facts.update(Hardware().populate()) + if 'network' in gather_subset: + facts.update(Network(module).populate()) + if 'virtual' in gather_subset: + facts.update(Virtual().populate()) + facts['gather_subset'] = gather_subset return facts # =========================================== - +# TODO: remove this dead code? def get_all_facts(module): setup_options = dict(module_setup=True) diff --git a/lib/ansible/playbook/play.py b/lib/ansible/playbook/play.py index c354b74549..c001419732 100644 --- a/lib/ansible/playbook/play.py +++ b/lib/ansible/playbook/play.py @@ -64,6 +64,9 @@ class Play(Base, Taggable, Become): # Connection _gather_facts = FieldAttribute(isa='bool', default=None, always_post_validate=True) + _gather_subset = FieldAttribute(isa='string', default=None, always_post_validate=True) + _ignore_facter = FieldAttribute(isa='bool', default=None, always_post_validate=True) + _ignore_ohai = FieldAttribute(isa='bool', default=None, always_post_validate=True) _hosts = FieldAttribute(isa='list', required=True, listof=string_types, always_post_validate=True) _name = FieldAttribute(isa='string', default='', always_post_validate=True) diff --git a/test/integration/test_gathering_facts.yml b/test/integration/test_gathering_facts.yml new file mode 100644 index 0000000000..03d707fbf1 --- /dev/null +++ b/test/integration/test_gathering_facts.yml @@ -0,0 +1,45 @@ +--- + +- hosts: localhost + tags: [ 'min' ] + connection: local + gather_subset: "min" + ignore_facter: yes + ignore_ohai: yes + gather_facts: yes + tasks: + - debug: var={{item}} + with_items: [ 'ansible_user_id', 'ansible_interfaces', 'ansible_mounts', 'ansible_virtualization_role' ] + +- hosts: localhost + tags: [ 'network' ] + connection: local + gather_subset: "network" + ignore_facter: yes + ignore_ohai: yes + gather_facts: yes + tasks: + - debug: var={{item}} + with_items: [ 'ansible_user_id', 'ansible_interfaces', 'ansible_mounts', 'ansible_virtualization_role' ] + +- hosts: localhost + tags: [ 'hardware' ] + connection: local + gather_subset: "hardware" + ignore_facter: yes + ignore_ohai: yes + gather_facts: yes + tasks: + - debug: var={{item}} + with_items: [ 'ansible_user_id', 'ansible_interfaces', 'ansible_mounts', 'ansible_virtualization_role' ] + +- hosts: localhost + tags: [ 'virtual' ] + connection: local + gather_subset: "virtual" + ignore_facter: yes + ignore_ohai: yes + gather_facts: yes + tasks: + - debug: var={{item}} + with_items: [ 'ansible_user_id', 'ansible_interfaces', 'ansible_mounts', 'ansible_virtualization_role' ] From d665911bab1e657169843e3fce99058902d213a8 Mon Sep 17 00:00:00 2001 From: Toshio Kuratomi Date: Mon, 14 Mar 2016 10:32:50 -0700 Subject: [PATCH 2/5] Allow FieldAttribute lists and sets to be set from a comma separated string --- lib/ansible/playbook/base.py | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) diff --git a/lib/ansible/playbook/base.py b/lib/ansible/playbook/base.py index d3752adf2f..c9fd2e84b4 100644 --- a/lib/ansible/playbook/base.py +++ b/lib/ansible/playbook/base.py @@ -334,7 +334,10 @@ class Base: if value is None: value = [] elif not isinstance(value, list): - value = [ value ] + if isinstance(value, string_types): + value = value.split(',') + else: + value = [ value ] if attribute.listof is not None: for item in value: if not isinstance(item, attribute.listof): @@ -346,11 +349,15 @@ class Base: elif attribute.isa == 'set': if value is None: value = set() - else: - if not isinstance(value, (list, set)): + elif not isinstance(value, (list, set)): + if isinstance(value, string_types): + value = value.split(',') + else: + # Making a list like this handles strings of + # text and bytes properly value = [ value ] - if not isinstance(value, set): - value = set(value) + if not isinstance(value, set): + value = set(value) elif attribute.isa == 'dict': if value is None: value = dict() From 5a1e35224b82e8f0776dfea3d34bcb005195bbdb Mon Sep 17 00:00:00 2001 From: Toshio Kuratomi Date: Mon, 14 Mar 2016 09:45:28 -0700 Subject: [PATCH 3/5] Make changes proposed during review of restrict fact gathering feature: * Make documentation examples into code blocks * Make code to call the subsets more general. * Made min subset always execute (cannot disable it). * Use a passed in modules parameter rather than global modules. This is needed for ziploader * Remove unneeded __init__() * Remove uneeded multiple inheritance from a base class * gather_facts is now a list type --- docsite/rst/intro_configuration.rst | 4 +- lib/ansible/module_utils/facts.py | 312 ++++++++++++---------------- lib/ansible/playbook/play.py | 2 +- 3 files changed, 131 insertions(+), 187 deletions(-) diff --git a/docsite/rst/intro_configuration.rst b/docsite/rst/intro_configuration.rst index ddcf673618..1f7c6ca313 100644 --- a/docsite/rst/intro_configuration.rst +++ b/docsite/rst/intro_configuration.rst @@ -355,7 +355,7 @@ This option can be useful for those wishing to save fact gathering time. Both 's .. versionadded:: 2.1 -You can specify a subset of gathered facts using the following options: +You can specify a subset of gathered facts using the following options:: gather_subset = all @@ -367,7 +367,7 @@ You can specify a subset of gathered facts using the following options: You can combine them using comma separated list (ex: min,network,virtual) -You can also disable puppet facter or chef ohai facts collection using following options: +You can also disable puppet facter or chef ohai facts collection using following options:: ignore_ohai = True ignore_facter = True diff --git a/lib/ansible/module_utils/facts.py b/lib/ansible/module_utils/facts.py index d683ee2a26..d38ce9207f 100644 --- a/lib/ansible/module_utils/facts.py +++ b/lib/ansible/module_utils/facts.py @@ -159,11 +159,9 @@ class Facts(object): { 'path' : '/usr/local/sbin/pkg', 'name' : 'pkgng' }, ] - # Allowed fact subset for gather_subset options - ALLOWED_FACT_SUBSET = frozenset([ 'all', 'min', 'network', 'hardware', 'virtual' ]) - - def __init__(self, load_on_init=True): + def __init__(self, module, load_on_init=True): + self.module = module self.facts = {} if load_on_init: @@ -218,14 +216,14 @@ class Facts(object): elif self.facts['system'] == 'AIX': # Attempt to use getconf to figure out architecture # fall back to bootinfo if needed - if module.get_bin_path('getconf'): - rc, out, err = module.run_command([module.get_bin_path('getconf'), - 'MACHINE_ARCHITECTURE']) + getconf_bin = self.module.get_bin_path('getconf') + if getconf_bin: + rc, out, err = self.module.run_command([getconf_bin, 'MACHINE_ARCHITECTURE']) data = out.split('\n') self.facts['architecture'] = data[0] else: - rc, out, err = module.run_command([module.get_bin_path('bootinfo'), - '-p']) + bootinfo_bin = self.module.get_bin_path('bootinfo') + rc, out, err = self.module.run_command([bootinfo_bin, '-p']) data = out.split('\n') self.facts['architecture'] = data[0] elif self.facts['system'] == 'OpenBSD': @@ -233,7 +231,7 @@ class Facts(object): def get_local_facts(self): - fact_path = module.params.get('fact_path', None) + fact_path = self.module.params.get('fact_path', None) if not fact_path or not os.path.exists(fact_path): return @@ -246,7 +244,7 @@ class Facts(object): # try to read it as json first # if that fails read it with ConfigParser # if that fails, skip it - rc, out, err = module.run_command(fn) + rc, out, err = self.module.run_command(fn) else: out = get_file_content(fn, default='') @@ -297,20 +295,20 @@ class Facts(object): # as it's much cleaner than this massive if-else if self.facts['system'] == 'AIX': self.facts['distribution'] = 'AIX' - rc, out, err = module.run_command("/usr/bin/oslevel") + rc, out, err = self.module.run_command("/usr/bin/oslevel") data = out.split('.') self.facts['distribution_version'] = data[0] self.facts['distribution_release'] = data[1] elif self.facts['system'] == 'HP-UX': self.facts['distribution'] = 'HP-UX' - rc, out, err = module.run_command("/usr/sbin/swlist |egrep 'HPUX.*OE.*[AB].[0-9]+\.[0-9]+'", use_unsafe_shell=True) + rc, out, err = self.module.run_command("/usr/sbin/swlist |egrep 'HPUX.*OE.*[AB].[0-9]+\.[0-9]+'", use_unsafe_shell=True) data = re.search('HPUX.*OE.*([AB].[0-9]+\.[0-9]+)\.([0-9]+).*', out) if data: self.facts['distribution_version'] = data.groups()[0] self.facts['distribution_release'] = data.groups()[1] elif self.facts['system'] == 'Darwin': self.facts['distribution'] = 'MacOSX' - rc, out, err = module.run_command("/usr/bin/sw_vers -productVersion") + rc, out, err = self.module.run_command("/usr/bin/sw_vers -productVersion") data = out.split()[-1] self.facts['distribution_version'] = data elif self.facts['system'] == 'FreeBSD': @@ -324,7 +322,7 @@ class Facts(object): elif self.facts['system'] == 'OpenBSD': self.facts['distribution'] = 'OpenBSD' self.facts['distribution_release'] = platform.release() - rc, out, err = module.run_command("/sbin/sysctl -n kern.version") + rc, out, err = self.module.run_command("/sbin/sysctl -n kern.version") match = re.match('OpenBSD\s[0-9]+.[0-9]+-(\S+)\s.*', out) if match: self.facts['distribution_version'] = match.groups()[0] @@ -414,7 +412,7 @@ class Facts(object): self.facts['distribution_release'] = ora_prefix + data break - uname_rc, uname_out, uname_err = module.run_command(['uname', '-v']) + uname_rc, uname_out, uname_err = self.module.run_command(['uname', '-v']) distribution_version = None if 'SmartOS' in data: self.facts['distribution'] = 'SmartOS' @@ -580,7 +578,7 @@ class Facts(object): # try various forms of querying pid 1 proc_1 = get_file_content('/proc/1/comm') if proc_1 is None: - rc, proc_1, err = module.run_command("ps -p 1 -o comm|tail -n 1", use_unsafe_shell=True) + rc, proc_1, err = self.module.run_command("ps -p 1 -o comm|tail -n 1", use_unsafe_shell=True) else: proc_1 = os.path.basename(proc_1) @@ -613,7 +611,7 @@ class Facts(object): elif self.facts['system'] == 'Linux': if self._check_systemd(): self.facts['service_mgr'] = 'systemd' - elif module.get_bin_path('initctl') and os.path.exists("/etc/init/"): + elif self.module.get_bin_path('initctl') and os.path.exists("/etc/init/"): self.facts['service_mgr'] = 'upstart' elif os.path.realpath('/sbin/rc') == '/sbin/openrc': self.facts['service_mgr'] = 'openrc' @@ -625,9 +623,9 @@ class Facts(object): self.facts['service_mgr'] = 'service' def get_lsb_facts(self): - lsb_path = module.get_bin_path('lsb_release') + lsb_path = self.module.get_bin_path('lsb_release') if lsb_path: - rc, out, err = module.run_command([lsb_path, "-a"]) + rc, out, err = self.module.run_command([lsb_path, "-a"]) if rc == 0: self.facts['lsb'] = {} for line in out.split('\n'): @@ -735,7 +733,7 @@ class Facts(object): def _check_systemd(self): # tools must be installed - if module.get_bin_path('systemctl'): + if self.module.get_bin_path('systemctl'): # this should show if systemd is the boot init system, if checking init faild to mark as systemd # these mirror systemd's own sd_boot test http://www.freedesktop.org/software/systemd/man/sd_booted.html @@ -845,9 +843,6 @@ class Hardware(Facts): subclass = sc return super(cls, subclass).__new__(subclass, *arguments, **keyword) - def __init__(self): - Facts.__init__(self) - def populate(self): return self.facts @@ -872,9 +867,6 @@ class LinuxHardware(Hardware): # Now we have all of these in a dict structure MEMORY_FACTS = ORIGINAL_MEMORY_FACTS.union(('Buffers', 'Cached', 'SwapCached')) - def __init__(self): - Hardware.__init__(self) - def populate(self): self.get_cpu_facts() self.get_memory_facts() @@ -1058,7 +1050,7 @@ class LinuxHardware(Hardware): else: # Fall back to using dmidecode, if available - dmi_bin = module.get_bin_path('dmidecode') + dmi_bin = self.module.get_bin_path('dmidecode') DMI_DICT = { 'bios_date': 'bios-release-date', 'bios_version': 'bios-version', @@ -1071,7 +1063,7 @@ class LinuxHardware(Hardware): } for (k, v) in DMI_DICT.items(): if dmi_bin is not None: - (rc, out, err) = module.run_command('%s -s %s' % (dmi_bin, v)) + (rc, out, err) = self.module.run_command('%s -s %s' % (dmi_bin, v)) if rc == 0: # Strip out commented lines (specific dmidecode output) thisvalue = ''.join([ line for line in out.split('\n') if not line.startswith('#') ]) @@ -1100,9 +1092,9 @@ class LinuxHardware(Hardware): uuid = uuids[fields[0]] else: uuid = 'NA' - lsblkPath = module.get_bin_path("lsblk") + lsblkPath = self.module.get_bin_path("lsblk") if lsblkPath: - rc, out, err = module.run_command("%s -ln --output UUID %s" % (lsblkPath, fields[0]), use_unsafe_shell=True) + rc, out, err = self.module.run_command("%s -ln --output UUID %s" % (lsblkPath, fields[0]), use_unsafe_shell=True) if rc == 0: uuid = out.strip() @@ -1121,9 +1113,9 @@ class LinuxHardware(Hardware): def get_device_facts(self): self.facts['devices'] = {} - lspci = module.get_bin_path('lspci') + lspci = self.module.get_bin_path('lspci') if lspci: - rc, pcidata, err = module.run_command([lspci, '-D']) + rc, pcidata, err = self.module.run_command([lspci, '-D']) else: pcidata = None @@ -1176,7 +1168,7 @@ class LinuxHardware(Hardware): part['sectorsize'] = get_file_content(part_sysdir + "/queue/logical_block_size") if not part['sectorsize']: part['sectorsize'] = get_file_content(part_sysdir + "/queue/hw_sector_size",512) - part['size'] = module.pretty_bytes((float(part['sectors']) * float(part['sectorsize']))) + part['size'] = self.module.pretty_bytes((float(part['sectors']) * float(part['sectorsize']))) d['partitions'][partname] = part d['rotational'] = get_file_content(sysdir + "/queue/rotational") @@ -1193,7 +1185,7 @@ class LinuxHardware(Hardware): d['sectorsize'] = get_file_content(sysdir + "/queue/logical_block_size") if not d['sectorsize']: d['sectorsize'] = get_file_content(sysdir + "/queue/hw_sector_size",512) - d['size'] = module.pretty_bytes(float(d['sectors']) * float(d['sectorsize'])) + d['size'] = self.module.pretty_bytes(float(d['sectors']) * float(d['sectorsize'])) d['host'] = "" @@ -1228,14 +1220,14 @@ class LinuxHardware(Hardware): def get_lvm_facts(self): """ Get LVM Facts if running as root and lvm utils are available """ - if os.getuid() == 0 and module.get_bin_path('vgs'): + if os.getuid() == 0 and self.module.get_bin_path('vgs'): lvm_util_options = '--noheadings --nosuffix --units g' - vgs_path = module.get_bin_path('vgs') + vgs_path = self.module.get_bin_path('vgs') #vgs fields: VG #PV #LV #SN Attr VSize VFree vgs={} if vgs_path: - rc, vg_lines, err = module.run_command( '%s %s' % (vgs_path, lvm_util_options)) + rc, vg_lines, err = self.module.run_command( '%s %s' % (vgs_path, lvm_util_options)) for vg_line in vg_lines.splitlines(): items = vg_line.split() vgs[items[0]] = {'size_g':items[-2], @@ -1243,12 +1235,12 @@ class LinuxHardware(Hardware): 'num_lvs': items[2], 'num_pvs': items[1]} - lvs_path = module.get_bin_path('lvs') + lvs_path = self.module.get_bin_path('lvs') #lvs fields: #LV VG Attr LSize Pool Origin Data% Move Log Copy% Convert lvs = {} if lvs_path: - rc, lv_lines, err = module.run_command( '%s %s' % (lvs_path, lvm_util_options)) + rc, lv_lines, err = self.module.run_command( '%s %s' % (lvs_path, lvm_util_options)) for lv_line in lv_lines.splitlines(): items = lv_line.split() lvs[items[0]] = {'size_g': items[3], 'vg': items[1]} @@ -1263,9 +1255,6 @@ class SunOSHardware(Hardware): """ platform = 'SunOS' - def __init__(self): - Hardware.__init__(self) - def populate(self): self.get_cpu_facts() self.get_memory_facts() @@ -1278,7 +1267,7 @@ class SunOSHardware(Hardware): def get_cpu_facts(self): physid = 0 sockets = {} - rc, out, err = module.run_command("/usr/bin/kstat cpu_info") + rc, out, err = self.module.run_command("/usr/bin/kstat cpu_info") self.facts['processor'] = [] for line in out.split('\n'): if len(line) < 1: @@ -1319,11 +1308,11 @@ class SunOSHardware(Hardware): self.facts['processor_count'] = len(self.facts['processor']) def get_memory_facts(self): - rc, out, err = module.run_command(["/usr/sbin/prtconf"]) + rc, out, err = self.module.run_command(["/usr/sbin/prtconf"]) for line in out.split('\n'): if 'Memory size' in line: self.facts['memtotal_mb'] = line.split()[2] - rc, out, err = module.run_command("/usr/sbin/swap -s") + rc, out, err = self.module.run_command("/usr/sbin/swap -s") allocated = long(out.split()[1][:-1]) reserved = long(out.split()[5][:-1]) used = long(out.split()[8][:-1]) @@ -1362,9 +1351,6 @@ class OpenBSDHardware(Hardware): platform = 'OpenBSD' DMESG_BOOT = '/var/run/dmesg.boot' - def __init__(self): - Hardware.__init__(self) - def populate(self): self.sysctl = self.get_sysctl() self.get_memory_facts() @@ -1374,7 +1360,7 @@ class OpenBSDHardware(Hardware): return self.facts def get_sysctl(self): - rc, out, err = module.run_command(["/sbin/sysctl", "hw"]) + rc, out, err = self.module.run_command(["/sbin/sysctl", "hw"]) if rc != 0: return dict() sysctl = dict() @@ -1403,7 +1389,7 @@ class OpenBSDHardware(Hardware): # procs memory page disks traps cpu # r b w avm fre flt re pi po fr sr wd0 fd0 int sys cs us sy id # 0 0 0 47512 28160 51 0 0 0 0 0 1 0 116 89 17 0 1 99 - rc, out, err = module.run_command("/usr/bin/vmstat") + rc, out, err = self.module.run_command("/usr/bin/vmstat") if rc == 0: self.facts['memfree_mb'] = long(out.splitlines()[-1].split()[4]) / 1024 self.facts['memtotal_mb'] = long(self.sysctl['hw.usermem']) / 1024 / 1024 @@ -1412,7 +1398,7 @@ class OpenBSDHardware(Hardware): # total: 69268 1K-blocks allocated, 0 used, 69268 available # And for older OpenBSD: # total: 69268k bytes allocated = 0k used, 69268k available - rc, out, err = module.run_command("/sbin/swapctl -sk") + rc, out, err = self.module.run_command("/sbin/swapctl -sk") if rc == 0: swaptrans = maketrans(' ', ' ') data = out.split() @@ -1423,7 +1409,7 @@ class OpenBSDHardware(Hardware): processor = [] dmesg_boot = get_file_content(OpenBSDHardware.DMESG_BOOT) if not dmesg_boot: - rc, dmesg_boot, err = module.run_command("/sbin/dmesg") + rc, dmesg_boot, err = self.module.run_command("/sbin/dmesg") i = 0 for line in dmesg_boot.splitlines(): if line.split(' ', 1)[0] == 'cpu%i:' % i: @@ -1455,9 +1441,6 @@ class FreeBSDHardware(Hardware): platform = 'FreeBSD' DMESG_BOOT = '/var/run/dmesg.boot' - def __init__(self): - Hardware.__init__(self) - def populate(self): self.get_cpu_facts() self.get_memory_facts() @@ -1471,12 +1454,12 @@ class FreeBSDHardware(Hardware): def get_cpu_facts(self): self.facts['processor'] = [] - rc, out, err = module.run_command("/sbin/sysctl -n hw.ncpu") + rc, out, err = self.module.run_command("/sbin/sysctl -n hw.ncpu") self.facts['processor_count'] = out.strip() dmesg_boot = get_file_content(FreeBSDHardware.DMESG_BOOT) if not dmesg_boot: - rc, dmesg_boot, err = module.run_command("/sbin/dmesg") + rc, dmesg_boot, err = self.module.run_command("/sbin/dmesg") for line in dmesg_boot.split('\n'): if 'CPU:' in line: cpu = re.sub(r'CPU:\s+', r"", line) @@ -1486,7 +1469,7 @@ class FreeBSDHardware(Hardware): def get_memory_facts(self): - rc, out, err = module.run_command("/sbin/sysctl vm.stats") + rc, out, err = self.module.run_command("/sbin/sysctl vm.stats") for line in out.split('\n'): data = line.split() if 'vm.stats.vm.v_page_size' in line: @@ -1501,7 +1484,7 @@ class FreeBSDHardware(Hardware): # Device 1M-blocks Used Avail Capacity # /dev/ada0p3 314368 0 314368 0% # - rc, out, err = module.run_command("/usr/sbin/swapinfo -k") + rc, out, err = self.module.run_command("/usr/sbin/swapinfo -k") lines = out.split('\n') if len(lines[-1]) == 0: lines.pop() @@ -1525,7 +1508,7 @@ class FreeBSDHardware(Hardware): def get_device_facts(self): sysdir = '/dev' self.facts['devices'] = {} - drives = re.compile('(ada?\d+|da\d+|a?cd\d+)') #TODO: rc, disks, err = module.run_command("/sbin/sysctl kern.disks") + drives = re.compile('(ada?\d+|da\d+|a?cd\d+)') #TODO: rc, disks, err = self.module.run_command("/sbin/sysctl kern.disks") slices = re.compile('(ada?\d+s\d+\w*|da\d+s\d+\w*)') if os.path.isdir(sysdir): dirlist = sorted(os.listdir(sysdir)) @@ -1543,7 +1526,7 @@ class FreeBSDHardware(Hardware): Use dmidecode executable if available''' # Fall back to using dmidecode, if available - dmi_bin = module.get_bin_path('dmidecode') + dmi_bin = self.module.get_bin_path('dmidecode') DMI_DICT = dict( bios_date='bios-release-date', bios_version='bios-version', @@ -1556,7 +1539,7 @@ class FreeBSDHardware(Hardware): ) for (k, v) in DMI_DICT.items(): if dmi_bin is not None: - (rc, out, err) = module.run_command('%s -s %s' % (dmi_bin, v)) + (rc, out, err) = self.module.run_command('%s -s %s' % (dmi_bin, v)) if rc == 0: # Strip out commented lines (specific dmidecode output) self.facts[k] = ''.join([ line for line in out.split('\n') if not line.startswith('#') ]) @@ -1587,9 +1570,6 @@ class NetBSDHardware(Hardware): platform = 'NetBSD' MEMORY_FACTS = ['MemTotal', 'SwapTotal', 'MemFree', 'SwapFree'] - def __init__(self): - Hardware.__init__(self) - def populate(self): self.get_cpu_facts() self.get_memory_facts() @@ -1665,9 +1645,6 @@ class AIX(Hardware): """ platform = 'AIX' - def __init__(self): - Hardware.__init__(self) - def populate(self): self.get_cpu_facts() self.get_memory_facts() @@ -1678,7 +1655,7 @@ class AIX(Hardware): self.facts['processor'] = [] - rc, out, err = module.run_command("/usr/sbin/lsdev -Cc processor") + rc, out, err = self.module.run_command("/usr/sbin/lsdev -Cc processor") if out: i = 0 for line in out.split('\n'): @@ -1691,19 +1668,19 @@ class AIX(Hardware): i += 1 self.facts['processor_count'] = int(i) - rc, out, err = module.run_command("/usr/sbin/lsattr -El " + cpudev + " -a type") + rc, out, err = self.module.run_command("/usr/sbin/lsattr -El " + cpudev + " -a type") data = out.split(' ') self.facts['processor'] = data[1] - rc, out, err = module.run_command("/usr/sbin/lsattr -El " + cpudev + " -a smt_threads") + rc, out, err = self.module.run_command("/usr/sbin/lsattr -El " + cpudev + " -a smt_threads") data = out.split(' ') self.facts['processor_cores'] = int(data[1]) def get_memory_facts(self): pagesize = 4096 - rc, out, err = module.run_command("/usr/bin/vmstat -v") + rc, out, err = self.module.run_command("/usr/bin/vmstat -v") for line in out.split('\n'): data = line.split() if 'memory pages' in line: @@ -1716,7 +1693,7 @@ class AIX(Hardware): # Device 1M-blocks Used Avail Capacity # /dev/ada0p3 314368 0 314368 0% # - rc, out, err = module.run_command("/usr/sbin/lsps -s") + rc, out, err = self.module.run_command("/usr/sbin/lsps -s") if out: lines = out.split('\n') data = lines[1].split() @@ -1726,7 +1703,7 @@ class AIX(Hardware): self.facts['swapfree_mb'] = long(swaptotal_mb * ( 100 - percused ) / 100) def get_dmi_facts(self): - rc, out, err = module.run_command("/usr/sbin/lsattr -El sys0 -a fwversion") + rc, out, err = self.module.run_command("/usr/sbin/lsattr -El sys0 -a fwversion") data = out.split() self.facts['firmware_version'] = data[1].strip('IBM,') @@ -1746,9 +1723,6 @@ class HPUX(Hardware): platform = 'HP-UX' - def __init__(self): - Hardware.__init__(self) - def populate(self): self.get_cpu_facts() self.get_memory_facts() @@ -1757,31 +1731,31 @@ class HPUX(Hardware): def get_cpu_facts(self): if self.facts['architecture'] == '9000/800': - rc, out, err = module.run_command("ioscan -FkCprocessor | wc -l", use_unsafe_shell=True) + rc, out, err = self.module.run_command("ioscan -FkCprocessor | wc -l", use_unsafe_shell=True) self.facts['processor_count'] = int(out.strip()) #Working with machinfo mess elif self.facts['architecture'] == 'ia64': if self.facts['distribution_version'] == "B.11.23": - rc, out, err = module.run_command("/usr/contrib/bin/machinfo | grep 'Number of CPUs'", use_unsafe_shell=True) + rc, out, err = self.module.run_command("/usr/contrib/bin/machinfo | grep 'Number of CPUs'", use_unsafe_shell=True) self.facts['processor_count'] = int(out.strip().split('=')[1]) - rc, out, err = module.run_command("/usr/contrib/bin/machinfo | grep 'processor family'", use_unsafe_shell=True) + rc, out, err = self.module.run_command("/usr/contrib/bin/machinfo | grep 'processor family'", use_unsafe_shell=True) self.facts['processor'] = re.search('.*(Intel.*)', out).groups()[0].strip() - rc, out, err = module.run_command("ioscan -FkCprocessor | wc -l", use_unsafe_shell=True) + rc, out, err = self.module.run_command("ioscan -FkCprocessor | wc -l", use_unsafe_shell=True) self.facts['processor_cores'] = int(out.strip()) if self.facts['distribution_version'] == "B.11.31": #if machinfo return cores strings release B.11.31 > 1204 - rc, out, err = module.run_command("/usr/contrib/bin/machinfo | grep core | wc -l", use_unsafe_shell=True) + rc, out, err = self.module.run_command("/usr/contrib/bin/machinfo | grep core | wc -l", use_unsafe_shell=True) if out.strip()== '0': - rc, out, err = module.run_command("/usr/contrib/bin/machinfo | grep Intel", use_unsafe_shell=True) + rc, out, err = self.module.run_command("/usr/contrib/bin/machinfo | grep Intel", use_unsafe_shell=True) self.facts['processor_count'] = int(out.strip().split(" ")[0]) #If hyperthreading is active divide cores by 2 - rc, out, err = module.run_command("/usr/sbin/psrset | grep LCPU", use_unsafe_shell=True) + rc, out, err = self.module.run_command("/usr/sbin/psrset | grep LCPU", use_unsafe_shell=True) data = re.sub(' +',' ',out).strip().split(' ') if len(data) == 1: hyperthreading = 'OFF' else: hyperthreading = data[1] - rc, out, err = module.run_command("/usr/contrib/bin/machinfo | grep logical", use_unsafe_shell=True) + rc, out, err = self.module.run_command("/usr/contrib/bin/machinfo | grep logical", use_unsafe_shell=True) data = out.strip().split(" ") if hyperthreading == 'ON': self.facts['processor_cores'] = int(data[0])/2 @@ -1790,54 +1764,54 @@ class HPUX(Hardware): self.facts['processor_cores'] = self.facts['processor_count'] else: self.facts['processor_cores'] = int(data[0]) - rc, out, err = module.run_command("/usr/contrib/bin/machinfo | grep Intel |cut -d' ' -f4-", use_unsafe_shell=True) + rc, out, err = self.module.run_command("/usr/contrib/bin/machinfo | grep Intel |cut -d' ' -f4-", use_unsafe_shell=True) self.facts['processor'] = out.strip() else: - rc, out, err = module.run_command("/usr/contrib/bin/machinfo | egrep 'socket[s]?$' | tail -1", use_unsafe_shell=True) + rc, out, err = self.module.run_command("/usr/contrib/bin/machinfo | egrep 'socket[s]?$' | tail -1", use_unsafe_shell=True) self.facts['processor_count'] = int(out.strip().split(" ")[0]) - rc, out, err = module.run_command("/usr/contrib/bin/machinfo | grep -e '[0-9] core' | tail -1", use_unsafe_shell=True) + rc, out, err = self.module.run_command("/usr/contrib/bin/machinfo | grep -e '[0-9] core' | tail -1", use_unsafe_shell=True) self.facts['processor_cores'] = int(out.strip().split(" ")[0]) - rc, out, err = module.run_command("/usr/contrib/bin/machinfo | grep Intel", use_unsafe_shell=True) + rc, out, err = self.module.run_command("/usr/contrib/bin/machinfo | grep Intel", use_unsafe_shell=True) self.facts['processor'] = out.strip() def get_memory_facts(self): pagesize = 4096 - rc, out, err = module.run_command("/usr/bin/vmstat | tail -1", use_unsafe_shell=True) + rc, out, err = self.module.run_command("/usr/bin/vmstat | tail -1", use_unsafe_shell=True) data = int(re.sub(' +',' ',out).split(' ')[5].strip()) self.facts['memfree_mb'] = pagesize * data / 1024 / 1024 if self.facts['architecture'] == '9000/800': try: - rc, out, err = module.run_command("grep Physical /var/adm/syslog/syslog.log") + rc, out, err = self.module.run_command("grep Physical /var/adm/syslog/syslog.log") data = re.search('.*Physical: ([0-9]*) Kbytes.*',out).groups()[0].strip() self.facts['memtotal_mb'] = int(data) / 1024 except AttributeError: #For systems where memory details aren't sent to syslog or the log has rotated, use parsed #adb output. Unfortunately /dev/kmem doesn't have world-read, so this only works as root. if os.access("/dev/kmem", os.R_OK): - rc, out, err = module.run_command("echo 'phys_mem_pages/D' | adb -k /stand/vmunix /dev/kmem | tail -1 | awk '{print $2}'", use_unsafe_shell=True) + rc, out, err = self.module.run_command("echo 'phys_mem_pages/D' | adb -k /stand/vmunix /dev/kmem | tail -1 | awk '{print $2}'", use_unsafe_shell=True) if not err: data = out self.facts['memtotal_mb'] = int(data) / 256 else: - rc, out, err = module.run_command("/usr/contrib/bin/machinfo | grep Memory", use_unsafe_shell=True) + rc, out, err = self.module.run_command("/usr/contrib/bin/machinfo | grep Memory", use_unsafe_shell=True) data = re.search('Memory[\ :=]*([0-9]*).*MB.*',out).groups()[0].strip() self.facts['memtotal_mb'] = int(data) - rc, out, err = module.run_command("/usr/sbin/swapinfo -m -d -f -q") + rc, out, err = self.module.run_command("/usr/sbin/swapinfo -m -d -f -q") self.facts['swaptotal_mb'] = int(out.strip()) - rc, out, err = module.run_command("/usr/sbin/swapinfo -m -d -f | egrep '^dev|^fs'", use_unsafe_shell=True) + rc, out, err = self.module.run_command("/usr/sbin/swapinfo -m -d -f | egrep '^dev|^fs'", use_unsafe_shell=True) swap = 0 for line in out.strip().split('\n'): swap += int(re.sub(' +',' ',line).split(' ')[3].strip()) self.facts['swapfree_mb'] = swap def get_hw_facts(self): - rc, out, err = module.run_command("model") + rc, out, err = self.module.run_command("model") self.facts['model'] = out.strip() if self.facts['architecture'] == 'ia64': separator = ':' if self.facts['distribution_version'] == "B.11.23": separator = '=' - rc, out, err = module.run_command("/usr/contrib/bin/machinfo |grep -i 'Firmware revision' | grep -v BMC", use_unsafe_shell=True) + rc, out, err = self.module.run_command("/usr/contrib/bin/machinfo |grep -i 'Firmware revision' | grep -v BMC", use_unsafe_shell=True) self.facts['firmware_version'] = out.split(separator)[1].strip() @@ -1854,9 +1828,6 @@ class Darwin(Hardware): """ platform = 'Darwin' - def __init__(self): - Hardware.__init__(self) - def populate(self): self.sysctl = self.get_sysctl() self.get_mac_facts() @@ -1865,7 +1836,7 @@ class Darwin(Hardware): return self.facts def get_sysctl(self): - rc, out, err = module.run_command(["/usr/sbin/sysctl", "hw", "machdep", "kern"]) + rc, out, err = self.module.run_command(["/usr/sbin/sysctl", "hw", "machdep", "kern"]) if rc != 0: return dict() sysctl = dict() @@ -1876,7 +1847,7 @@ class Darwin(Hardware): return sysctl def get_system_profile(self): - rc, out, err = module.run_command(["/usr/sbin/system_profiler", "SPHardwareDataType"]) + rc, out, err = self.module.run_command(["/usr/sbin/system_profiler", "SPHardwareDataType"]) if rc != 0: return dict() system_profile = dict() @@ -1887,7 +1858,7 @@ class Darwin(Hardware): return system_profile def get_mac_facts(self): - rc, out, err = module.run_command("sysctl hw.model") + rc, out, err = self.module.run_command("sysctl hw.model") if rc == 0: self.facts['model'] = out.splitlines()[-1].split()[1] self.facts['osversion'] = self.sysctl['kern.osversion'] @@ -1905,7 +1876,7 @@ class Darwin(Hardware): def get_memory_facts(self): self.facts['memtotal_mb'] = long(self.sysctl['hw.memsize']) / 1024 / 1024 - rc, out, err = module.run_command("sysctl hw.usermem") + rc, out, err = self.module.run_command("sysctl hw.usermem") if rc == 0: self.facts['memfree_mb'] = long(out.splitlines()[-1].split()[1]) / 1024 / 1024 @@ -1936,10 +1907,6 @@ class Network(Facts): subclass = sc return super(cls, subclass).__new__(subclass, *arguments, **keyword) - def __init__(self, module): - self.module = module - Facts.__init__(self) - def populate(self): return self.facts @@ -1953,9 +1920,6 @@ class LinuxNetwork(Network): """ platform = 'Linux' - def __init__(self, module): - Network.__init__(self, module) - def populate(self): ip_path = self.module.get_bin_path('ip') if ip_path is None: @@ -1987,7 +1951,7 @@ class LinuxNetwork(Network): continue if v == 'v6' and not socket.has_ipv6: continue - rc, out, err = module.run_command(command[v]) + rc, out, err = self.module.run_command(command[v]) if not out: # v6 routing may result in # RTNETLINK answers: Invalid argument @@ -2147,7 +2111,7 @@ class LinuxNetwork(Network): if not address == '::1': ips['all_ipv6_addresses'].append(address) - ip_path = module.get_bin_path("ip") + ip_path = self.module.get_bin_path("ip") args = [ip_path, 'addr', 'show', 'primary', device] rc, stdout, stderr = self.module.run_command(args) @@ -2182,16 +2146,13 @@ class GenericBsdIfconfigNetwork(Network): """ platform = 'Generic_BSD_Ifconfig' - def __init__(self, module): - Network.__init__(self, module) - def populate(self): - ifconfig_path = module.get_bin_path('ifconfig') + ifconfig_path = self.module.get_bin_path('ifconfig') if ifconfig_path is None: return self.facts - route_path = module.get_bin_path('route') + route_path = self.module.get_bin_path('route') if route_path is None: return self.facts @@ -2230,7 +2191,7 @@ class GenericBsdIfconfigNetwork(Network): if v == 'v6' and not socket.has_ipv6: continue - rc, out, err = module.run_command(command[v]) + rc, out, err = self.module.run_command(command[v]) if not out: # v6 routing may result in # RTNETLINK answers: Invalid argument @@ -2257,7 +2218,7 @@ class GenericBsdIfconfigNetwork(Network): # FreeBSD, DragonflyBSD, NetBSD, OpenBSD and OS X all implicitly add '-a' # when running the command 'ifconfig'. # Solaris must explicitly run the command 'ifconfig -a'. - rc, out, err = module.run_command([ifconfig_path, ifconfig_options]) + rc, out, err = self.module.run_command([ifconfig_path, ifconfig_options]) for line in out.split('\n'): @@ -2403,9 +2364,6 @@ class HPUXNetwork(Network): """ platform = 'HP-UX' - def __init__(self, module): - Network.__init__(self, module) - def populate(self): netstat_path = self.module.get_bin_path('netstat') if netstat_path is None: @@ -2418,7 +2376,7 @@ class HPUXNetwork(Network): return self.facts def get_default_interfaces(self): - rc, out, err = module.run_command("/usr/bin/netstat -nr") + rc, out, err = self.module.run_command("/usr/bin/netstat -nr") lines = out.split('\n') for line in lines: words = line.split() @@ -2429,7 +2387,7 @@ class HPUXNetwork(Network): def get_interfaces_info(self): interfaces = {} - rc, out, err = module.run_command("/usr/bin/netstat -ni") + rc, out, err = self.module.run_command("/usr/bin/netstat -ni") lines = out.split('\n') for line in lines: words = line.split() @@ -2445,7 +2403,7 @@ class HPUXNetwork(Network): 'address': address } return interfaces -class DarwinNetwork(GenericBsdIfconfigNetwork, Network): +class DarwinNetwork(GenericBsdIfconfigNetwork): """ This is the Mac OS X/Darwin Network Class. It uses the GenericBsdIfconfigNetwork unchanged @@ -2469,21 +2427,21 @@ class DarwinNetwork(GenericBsdIfconfigNetwork, Network): current_if['media_options'] = self.get_options(words[3]) -class FreeBSDNetwork(GenericBsdIfconfigNetwork, Network): +class FreeBSDNetwork(GenericBsdIfconfigNetwork): """ This is the FreeBSD Network Class. It uses the GenericBsdIfconfigNetwork unchanged. """ platform = 'FreeBSD' -class DragonFlyNetwork(GenericBsdIfconfigNetwork, Network): +class DragonFlyNetwork(GenericBsdIfconfigNetwork): """ This is the DragonFly Network Class. It uses the GenericBsdIfconfigNetwork unchanged. """ platform = 'DragonFly' -class AIXNetwork(GenericBsdIfconfigNetwork, Network): +class AIXNetwork(GenericBsdIfconfigNetwork): """ This is the AIX Network Class. It uses the GenericBsdIfconfigNetwork unchanged. @@ -2491,9 +2449,9 @@ class AIXNetwork(GenericBsdIfconfigNetwork, Network): platform = 'AIX' def get_default_interfaces(self, route_path): - netstat_path = module.get_bin_path('netstat') + netstat_path = self.module.get_bin_path('netstat') - rc, out, err = module.run_command([netstat_path, '-nr']) + rc, out, err = self.module.run_command([netstat_path, '-nr']) interface = dict(v4 = {}, v6 = {}) @@ -2518,14 +2476,14 @@ class AIXNetwork(GenericBsdIfconfigNetwork, Network): all_ipv4_addresses = [], all_ipv6_addresses = [], ) - rc, out, err = module.run_command([ifconfig_path, ifconfig_options]) + rc, out, err = self.module.run_command([ifconfig_path, ifconfig_options]) for line in out.split('\n'): if line: words = line.split() - # only this condition differs from GenericBsdIfconfigNetwork + # only this condition differs from GenericBsdIfconfigNetwork if re.match('^\w*\d*:', line): current_if = self.parse_interface_line(words) interfaces[ current_if['device'] ] = current_if @@ -2547,16 +2505,16 @@ class AIXNetwork(GenericBsdIfconfigNetwork, Network): self.parse_inet6_line(words, current_if, ips) else: self.parse_unknown_line(words, current_if, ips) - uname_path = module.get_bin_path('uname') + uname_path = self.module.get_bin_path('uname') if uname_path: - rc, out, err = module.run_command([uname_path, '-W']) + rc, out, err = self.module.run_command([uname_path, '-W']) # don't bother with wpars it does not work # zero means not in wpar if not rc and out.split()[0] == '0': if current_if['macaddress'] == 'unknown' and re.match('^en', current_if['device']): - entstat_path = module.get_bin_path('entstat') + entstat_path = self.module.get_bin_path('entstat') if entstat_path: - rc, out, err = module.run_command([entstat_path, current_if['device'] ]) + rc, out, err = self.module.run_command([entstat_path, current_if['device'] ]) if rc != 0: break for line in out.split('\n'): @@ -2571,9 +2529,9 @@ class AIXNetwork(GenericBsdIfconfigNetwork, Network): current_if['type'] = 'ether' # device must have mtu attribute in ODM if 'mtu' not in current_if: - lsattr_path = module.get_bin_path('lsattr') + lsattr_path = self.module.get_bin_path('lsattr') if lsattr_path: - rc, out, err = module.run_command([lsattr_path,'-El', current_if['device'] ]) + rc, out, err = self.module.run_command([lsattr_path,'-El', current_if['device'] ]) if rc != 0: break for line in out.split('\n'): @@ -2591,7 +2549,7 @@ class AIXNetwork(GenericBsdIfconfigNetwork, Network): current_if['macaddress'] = 'unknown' # will be overwritten later return current_if -class OpenBSDNetwork(GenericBsdIfconfigNetwork, Network): +class OpenBSDNetwork(GenericBsdIfconfigNetwork): """ This is the OpenBSD Network Class. It uses the GenericBsdIfconfigNetwork. @@ -2606,7 +2564,7 @@ class OpenBSDNetwork(GenericBsdIfconfigNetwork, Network): def parse_lladdr_line(self, words, current_if, ips): current_if['macaddress'] = words[1] -class SunOSNetwork(GenericBsdIfconfigNetwork, Network): +class SunOSNetwork(GenericBsdIfconfigNetwork): """ This is the SunOS Network Class. It uses the GenericBsdIfconfigNetwork. @@ -2627,7 +2585,7 @@ class SunOSNetwork(GenericBsdIfconfigNetwork, Network): all_ipv4_addresses = [], all_ipv6_addresses = [], ) - rc, out, err = module.run_command([ifconfig_path, '-a']) + rc, out, err = self.module.run_command([ifconfig_path, '-a']) for line in out.split('\n'): @@ -2711,9 +2669,6 @@ class Virtual(Facts): subclass = sc return super(cls, subclass).__new__(subclass, *arguments, **keyword) - def __init__(self): - Facts.__init__(self) - def populate(self): return self.facts @@ -2725,9 +2680,6 @@ class LinuxVirtual(Virtual): """ platform = 'Linux' - def __init__(self): - Virtual.__init__(self) - def populate(self): self.get_virtual_facts() return self.facts @@ -2844,9 +2796,9 @@ class LinuxVirtual(Virtual): self.facts['virtualization_type'] = 'powervm_lx86' elif re.match('^vendor_id.*IBM/S390', line): self.facts['virtualization_type'] = 'PR/SM' - lscpu = module.get_bin_path('lscpu') + lscpu = self.module.get_bin_path('lscpu') if lscpu: - rc, out, err = module.run_command(["lscpu"]) + rc, out, err = self.module.run_command(["lscpu"]) if rc == 0: for line in out.split("\n"): data = line.split(":", 1) @@ -2894,9 +2846,6 @@ class FreeBSDVirtual(Virtual): """ platform = 'FreeBSD' - def __init__(self): - Virtual.__init__(self) - def populate(self): self.get_virtual_facts() return self.facts @@ -2916,9 +2865,6 @@ class OpenBSDVirtual(Virtual): """ platform = 'OpenBSD' - def __init__(self): - Virtual.__init__(self) - def populate(self): self.get_virtual_facts() return self.facts @@ -2935,21 +2881,18 @@ class HPUXVirtual(Virtual): """ platform = 'HP-UX' - def __init__(self): - Virtual.__init__(self) - def populate(self): self.get_virtual_facts() return self.facts def get_virtual_facts(self): if os.path.exists('/usr/sbin/vecheck'): - rc, out, err = module.run_command("/usr/sbin/vecheck") + rc, out, err = self.module.run_command("/usr/sbin/vecheck") if rc == 0: self.facts['virtualization_type'] = 'guest' self.facts['virtualization_role'] = 'HP vPar' if os.path.exists('/opt/hpvm/bin/hpvminfo'): - rc, out, err = module.run_command("/opt/hpvm/bin/hpvminfo") + rc, out, err = self.module.run_command("/opt/hpvm/bin/hpvminfo") if rc == 0 and re.match('.*Running.*HPVM vPar.*', out): self.facts['virtualization_type'] = 'guest' self.facts['virtualization_role'] = 'HPVM vPar' @@ -2960,7 +2903,7 @@ class HPUXVirtual(Virtual): self.facts['virtualization_type'] = 'host' self.facts['virtualization_role'] = 'HPVM' if os.path.exists('/usr/sbin/parstatus'): - rc, out, err = module.run_command("/usr/sbin/parstatus") + rc, out, err = self.module.run_command("/usr/sbin/parstatus") if rc == 0: self.facts['virtualization_type'] = 'guest' self.facts['virtualization_role'] = 'HP nPar' @@ -2975,15 +2918,12 @@ class SunOSVirtual(Virtual): """ platform = 'SunOS' - def __init__(self): - Virtual.__init__(self) - def populate(self): self.get_virtual_facts() return self.facts def get_virtual_facts(self): - rc, out, err = module.run_command("/usr/sbin/prtdiag") + rc, out, err = self.module.run_command("/usr/sbin/prtdiag") for line in out.split('\n'): if 'VMware' in line: self.facts['virtualization_type'] = 'vmware' @@ -2999,7 +2939,7 @@ class SunOSVirtual(Virtual): self.facts['virtualization_role'] = 'guest' # Check if it's a zone if os.path.exists("/usr/bin/zonename"): - rc, out, err = module.run_command("/usr/bin/zonename") + rc, out, err = self.module.run_command("/usr/bin/zonename") if out.rstrip() != "global": self.facts['container'] = 'zone' # Check if it's a branded zone (i.e. Solaris 8/9 zone) @@ -3008,7 +2948,7 @@ class SunOSVirtual(Virtual): # If it's a zone check if we can detect if our global zone is itself virtualized. # Relies on the "guest tools" (e.g. vmware tools) to be installed if 'container' in self.facts and self.facts['container'] == 'zone': - rc, out, err = module.run_command("/usr/sbin/modinfo") + rc, out, err = self.module.run_command("/usr/sbin/modinfo") for line in out.split('\n'): if 'VMware' in line: self.facts['virtualization_type'] = 'vmware' @@ -3020,7 +2960,7 @@ class SunOSVirtual(Virtual): if os.path.exists("/usr/sbin/virtinfo"): # The output of virtinfo is different whether we are on a machine with logical # domains ('LDoms') on a T-series or domains ('Domains') on a M-series. Try LDoms first. - rc, out, err = module.run_command("/usr/sbin/virtinfo -p") + rc, out, err = self.module.run_command("/usr/sbin/virtinfo -p") # The output contains multiple lines with different keys like this: # DOMAINROLE|impl=LDoms|control=false|io=false|service=false|root=false # The output may also be not formatted and the returncode is set to 0 regardless of the error condition: @@ -3071,28 +3011,24 @@ def get_file_lines(path): def ansible_facts(module): # Retrieve module parameters - gather_subset = [ 'all' ] + gather_subset = ('all',) if 'gather_subset' in module.params: gather_subset = module.params['gather_subset'] # Retrieve all facts elements if 'all' in gather_subset: - gather_subset = [ 'min', 'hardware', 'network', 'virtual' ] - - # Check subsets and forbid unallowed name - for subset in gather_subset: - if subset not in Facts.ALLOWED_FACT_SUBSET: - raise TypeError("Bad subset '%s' given to Ansible. gather_subset options allowed: %s" % (subset, ", ".join(Facts.ALLOWED_FACT_SUBSET))) + gather_subset = FACT_SUBSETS.keys() + else: + # Check subsets and forbid unallowed name + for subset in gather_subset: + if subset not in FACT_SUBSETS.keys(): + raise TypeError("Bad subset '%s' given to Ansible. gather_subset options allowed: all, %s" % (subset, ", ".join(FACT_SUBSETS.keys()))) facts = {} - facts.update(Facts().populate()) - if 'hardware' in gather_subset: - facts.update(Hardware().populate()) - if 'network' in gather_subset: - facts.update(Network(module).populate()) - if 'virtual' in gather_subset: - facts.update(Virtual().populate()) facts['gather_subset'] = gather_subset + facts.update(Facts(module).populate()) + for subset in gather_subset: + facts.update(FACT_SUBSETS[subset](module).populate()) return facts # =========================================== @@ -3153,3 +3089,11 @@ def get_all_facts(module): setup_result['_ansible_verbose_override'] = True return setup_result + +### Note: have to define this at the bottom as it references classes defined earlier in this file +# Allowed fact subset for gather_subset options and what classes they use +FACT_SUBSETS = dict( + hardware=Hardware, + network=Network, + virtual=Virtual, +) diff --git a/lib/ansible/playbook/play.py b/lib/ansible/playbook/play.py index c001419732..6be4c5367e 100644 --- a/lib/ansible/playbook/play.py +++ b/lib/ansible/playbook/play.py @@ -64,7 +64,7 @@ class Play(Base, Taggable, Become): # Connection _gather_facts = FieldAttribute(isa='bool', default=None, always_post_validate=True) - _gather_subset = FieldAttribute(isa='string', default=None, always_post_validate=True) + _gather_subset = FieldAttribute(isa='list', default=None, always_post_validate=True) _ignore_facter = FieldAttribute(isa='bool', default=None, always_post_validate=True) _ignore_ohai = FieldAttribute(isa='bool', default=None, always_post_validate=True) _hosts = FieldAttribute(isa='list', required=True, listof=string_types, always_post_validate=True) From 512825455e7aa5687a121fda4ac4f934c82171e5 Mon Sep 17 00:00:00 2001 From: Toshio Kuratomi Date: Mon, 14 Mar 2016 14:50:27 -0700 Subject: [PATCH 4/5] Make ohai and facter work via module_utils Fact classes rather than in the setup module --- docsite/rst/glossary.rst | 4 +- docsite/rst/intro_configuration.rst | 27 ++-- examples/ansible.cfg | 13 +- lib/ansible/constants.py | 2 - lib/ansible/executor/play_iterator.py | 10 -- lib/ansible/module_utils/facts.py | 164 ++++++++++++++-------- lib/ansible/playbook/play.py | 2 - test/integration/Makefile | 5 +- test/integration/test_gathering_facts.yml | 12 +- 9 files changed, 135 insertions(+), 104 deletions(-) diff --git a/docsite/rst/glossary.rst b/docsite/rst/glossary.rst index 7c5bd5f812..74ff5d50e7 100644 --- a/docsite/rst/glossary.rst +++ b/docsite/rst/glossary.rst @@ -71,7 +71,7 @@ Facts Facts are simply things that are discovered about remote nodes. While they can be used in playbooks and templates just like variables, facts are things that are inferred, rather than set. Facts are automatically discovered by Ansible when running plays by executing the internal 'setup' module on the remote nodes. You never have to call the setup module explicitly, it just runs, but it can be disabled to save time if it is -not needed or to reduce to a subset. For the convenience of users who are switching from other configuration management systems, the fact module will also pull in facts from the 'ohai' and 'facter' tools if they are installed, which are fact libraries from Chef and Puppet, respectively. You can also ignore them and save time at runtime execution. +not needed or you can tell ansible to collect only a subset of the full facts via the `gather_subset:` option. For the convenience of users who are switching from other configuration management systems, the fact module will also pull in facts from the 'ohai' and 'facter' tools if they are installed, which are fact libraries from Chef and Puppet, respectively. (These may also be disabled via `gather_subset:`) Filter Plugin +++++++++++++ @@ -398,7 +398,7 @@ An optional conditional statement attached to a task that is used to determine i Van Halen +++++++++ -For no particular reason, other than the fact that Michael really likes them, all Ansible releases are codenamed after Van Halen songs. There is no preference given to David Lee Roth vs. Sammy Lee Hagar-era songs, and instrumentals are also allowed. It is unlikely that there will ever be a Jump release, but a Van Halen III codename release is possible. You never know. +For no particular reason, other than the fact that Michael really likes them, all Ansible 0.x and 1.x releases are codenamed after Van Halen songs. There is no preference given to David Lee Roth vs. Sammy Lee Hagar-era songs, and instrumentals are also allowed. It is unlikely that there will ever be a Jump release, but a Van Halen III codename release is possible. You never know. Vars (Variables) ++++++++++++++++ diff --git a/docsite/rst/intro_configuration.rst b/docsite/rst/intro_configuration.rst index 1f7c6ca313..419eeaf5f9 100644 --- a/docsite/rst/intro_configuration.rst +++ b/docsite/rst/intro_configuration.rst @@ -355,22 +355,29 @@ This option can be useful for those wishing to save fact gathering time. Both 's .. versionadded:: 2.1 -You can specify a subset of gathered facts using the following options:: +You can specify a subset of gathered facts using the following option:: gather_subset = all -:all: gather all subsets -:min: gather a very limited set of facts -:network: gather min and network facts -:hardware: gather min and hardware facts (longest facts to retrieve) -:virtual: gather min and virtual facts +:all: gather all subsets (the default) +:network: gather network facts +:hardware: gather hardware facts (longest facts to retrieve) +:virtual: gather facts about virtual machines hosted on the machine +:ohai: gather facts from ohai +:facter: gather facts from facter -You can combine them using comma separated list (ex: min,network,virtual) +You can combine them using a comma separated list (ex: network,virtual,facter) -You can also disable puppet facter or chef ohai facts collection using following options:: +You can also disable specific subsets by prepending with a `!` like this:: - ignore_ohai = True - ignore_facter = True + # Don't gather hardware facts, facts from chef's ohai or puppet's facter + gather_subset = !hardware,!ohai,!facter + +A set of basic facts are always collected no matter which additional subsets +are selected. If you want to collect the minimal amount of facts, use +`!all`:: + + gather_subset = !all hash_behaviour ============== diff --git a/examples/ansible.cfg b/examples/ansible.cfg index 181630f9c6..fe60bc71aa 100644 --- a/examples/ansible.cfg +++ b/examples/ansible.cfg @@ -33,19 +33,16 @@ # by default retrieve all facts subsets # all - gather all subsets -# min - gather a very limited set of facts # network - gather min and network facts # hardware - gather hardware facts (longest facts to retrieve) # virtual - gather min and virtual facts -# You can combine them using comma (ex: min,network,virtual) +# facter - import facts from facter +# ohai - import facts from ohai +# You can combine them using comma (ex: network,virtual) +# You can negate them using ! (ex: !hardware,!facter,!ohai) +# A minimal set of facts is always gathered. #gather_subset = all -# by default run ohai -#ignore_ohai = False - -# by default run facter -#ignore_facter = False - # additional paths to search for roles in, colon separated #roles_path = /etc/ansible/roles diff --git a/lib/ansible/constants.py b/lib/ansible/constants.py index 7e91233fca..365bda04d0 100644 --- a/lib/ansible/constants.py +++ b/lib/ansible/constants.py @@ -157,8 +157,6 @@ DEFAULT_JINJA2_EXTENSIONS = get_config(p, DEFAULTS, 'jinja2_extensions', 'ANSIBL DEFAULT_EXECUTABLE = get_config(p, DEFAULTS, 'executable', 'ANSIBLE_EXECUTABLE', '/bin/sh') DEFAULT_GATHERING = get_config(p, DEFAULTS, 'gathering', 'ANSIBLE_GATHERING', 'implicit').lower() DEFAULT_GATHER_SUBSET = get_config(p, DEFAULTS, 'gather_subset', 'ANSIBLE_GATHER_SUBSET', 'all').lower() -DEFAULT_IGNORE_OHAI = get_config(p, DEFAULTS, 'ignore_ohai', 'ANSIBLE_IGNORE_OHAI', False, boolean=True) -DEFAULT_IGNORE_FACTER = get_config(p, DEFAULTS, 'ignore_facter', 'ANSIBLE_IGNORE_FACTER', False, boolean=True) DEFAULT_LOG_PATH = get_config(p, DEFAULTS, 'log_path', 'ANSIBLE_LOG_PATH', '', ispath=True) DEFAULT_FORCE_HANDLERS = get_config(p, DEFAULTS, 'force_handlers', 'ANSIBLE_FORCE_HANDLERS', False, boolean=True) DEFAULT_INVENTORY_IGNORE = get_config(p, DEFAULTS, 'inventory_ignore_extensions', 'ANSIBLE_INVENTORY_IGNORE', ["~", ".orig", ".bak", ".ini", ".cfg", ".retry", ".pyc", ".pyo"], islist=True) diff --git a/lib/ansible/executor/play_iterator.py b/lib/ansible/executor/play_iterator.py index 93321ce8ae..e47d88ed7f 100644 --- a/lib/ansible/executor/play_iterator.py +++ b/lib/ansible/executor/play_iterator.py @@ -153,18 +153,10 @@ class PlayIterator: # Default options to gather gather_subset = C.DEFAULT_GATHER_SUBSET - ignore_ohai = C.DEFAULT_IGNORE_OHAI - ignore_facter = C.DEFAULT_IGNORE_FACTER # Retrieve subset to gather if self._play.gather_subset is not None: gather_subset = self._play.gather_subset - # ignore ohai - if self._play.ignore_ohai is not None: - ignore_ohai = self._play.ignore_ohai - # ignore puppet facter - if self._play.ignore_facter is not None: - ignore_facter = self._play.ignore_facter setup_block = Block(play=self._play) setup_task = Task(block=setup_block) @@ -172,8 +164,6 @@ class PlayIterator: setup_task.tags = ['always'] setup_task.args = { 'gather_subset': gather_subset, - 'ignore_ohai' : ignore_ohai, - 'ignore_facter': ignore_facter, } setup_task.set_loader(self._play._loader) setup_block.block = [setup_task] diff --git a/lib/ansible/module_utils/facts.py b/lib/ansible/module_utils/facts.py index d38ce9207f..50c9d4ca89 100644 --- a/lib/ansible/module_utils/facts.py +++ b/lib/ansible/module_utils/facts.py @@ -163,7 +163,10 @@ class Facts(object): self.module = module self.facts = {} - + ### TODO: Eventually, these should all get moved to populate(). But + # some of the values are currently being used by other subclasses (for + # instance, os_family and distribution). Have to sort out what to do + # about those first. if load_on_init: self.get_platform_facts() self.get_distribution_facts() @@ -2981,6 +2984,52 @@ class SunOSVirtual(Virtual): except ValueError: pass +class Ohai(Facts): + """ + This is a subclass of Facts for including information gathered from Ohai. + """ + + def populate(self): + self.run_ohai() + return self.facts + + def run_ohai(self): + ohai_path = self.module.get_bin_path('ohai') + if ohai_path is None: + return + rc, out, err = self.module.run_command(ohai_path) + try: + self.facts.update(json.loads(out)) + except: + pass + +class Facter(Facts): + """ + This is a subclass of Facts for including information gathered from Facter. + """ + def populate(self): + self.run_facter() + return self.facts + + def run_facter(self): + facter_path = self.module.get_bin_path('facter', opt_dirs=['/opt/puppetlabs/bin']) + cfacter_path = self.module.get_bin_path('cfacter', opt_dirs=['/opt/puppetlabs/bin']) + # Prefer to use cfacter if available + if cfacter_path is not None: + facter_path = cfacter_path + + if facter_path is None: + return + + # if facter is installed, and we can use --json because + # ruby-json is ALSO installed, include facter data in the JSON + rc, out, err = self.module.run_command(facter_path + " --puppet --json") + try: + self.facts = json.loads(out) + except: + pass + + def get_file_content(path, default=None, strip=True): data = default if os.path.exists(path) and os.access(path, os.R_OK): @@ -3009,76 +3058,70 @@ def get_file_lines(path): ret = [] return ret -def ansible_facts(module): - # Retrieve module parameters - gather_subset = ('all',) - if 'gather_subset' in module.params: - gather_subset = module.params['gather_subset'] - - # Retrieve all facts elements - if 'all' in gather_subset: - gather_subset = FACT_SUBSETS.keys() - else: - # Check subsets and forbid unallowed name - for subset in gather_subset: - if subset not in FACT_SUBSETS.keys(): - raise TypeError("Bad subset '%s' given to Ansible. gather_subset options allowed: all, %s" % (subset, ", ".join(FACT_SUBSETS.keys()))) - +def ansible_facts(module, gather_subset): facts = {} - facts['gather_subset'] = gather_subset + facts['gather_subset'] = list(gather_subset) facts.update(Facts(module).populate()) for subset in gather_subset: facts.update(FACT_SUBSETS[subset](module).populate()) return facts -# =========================================== -# TODO: remove this dead code? def get_all_facts(module): setup_options = dict(module_setup=True) - facts = ansible_facts(module) + + # Retrieve module parameters + gather_subset = module.params['gather_subset'] + + # Retrieve all facts elements + additional_subsets = set() + exclude_subsets = set() + for subset in gather_subset: + if subset == 'all': + additional_subsets.update(VALID_SUBSETS) + continue + if subset.startswith('!'): + subset = subset[1:] + if subset == 'all': + exclude_subsets.update(VALID_SUBSETS) + continue + exclude = True + else: + exclude = False + + if subset not in VALID_SUBSETS: + raise TypeError("Bad subset '%s' given to Ansible. gather_subset options allowed: all, %s" % (subset, ", ".join(FACT_SUBSETS.keys()))) + + if exclude: + exclude_subsets.add(subset) + else: + additional_subsets.add(subset) + + if not additional_subsets: + additional_subsets.update(VALID_SUBSETS) + + additional_subsets.difference_update(exclude_subsets) + + # facter and ohai are given a different prefix than other subsets + if 'facter' in additional_subsets: + additional_subsets.difference_update(('facter',)) + facter_ds = FACT_SUBSETS['facter'](module, load_on_init=False).populate() + if facter_ds: + for (k, v) in facter_ds.items(): + setup_options['facter_%s' % k.replace('-', '_')] = v + + if 'ohai' in additional_subsets: + additional_subsets.difference_update(('ohai',)) + ohai_ds = FACT_SUBSETS['ohai'](module, load_on_init=False).populate() + if ohai_ds: + for (k, v) in ohai_ds.items(): + setup_options['ohai_%s' % k.replace('-', '_')] = v + + facts = ansible_facts(module, additional_subsets) for (k, v) in facts.items(): setup_options["ansible_%s" % k.replace('-', '_')] = v - # Look for the path to the facter, cfacter, and ohai binaries and set - # the variable to that path. - - facter_path = module.get_bin_path('facter') - cfacter_path = module.get_bin_path('cfacter') - ohai_path = module.get_bin_path('ohai') - - # Prefer to use cfacter if available - if cfacter_path is not None: - facter_path = cfacter_path - # if facter is installed, and we can use --json because - # ruby-json is ALSO installed, include facter data in the JSON - - if facter_path is not None: - rc, out, err = module.run_command(facter_path + " --json") - facter = True - try: - facter_ds = json.loads(out) - except: - facter = False - if facter: - for (k,v) in facter_ds.items(): - setup_options["facter_%s" % k] = v - - # ditto for ohai - - if ohai_path is not None: - rc, out, err = module.run_command(ohai_path) - ohai = True - try: - ohai_ds = json.loads(out) - except: - ohai = False - if ohai: - for (k,v) in ohai_ds.items(): - k2 = "ohai_%s" % k.replace('-', '_') - setup_options[k2] = v - setup_result = { 'ansible_facts': {} } for (k,v) in setup_options.items(): @@ -3090,10 +3133,13 @@ def get_all_facts(module): return setup_result -### Note: have to define this at the bottom as it references classes defined earlier in this file # Allowed fact subset for gather_subset options and what classes they use +# Note: have to define this at the bottom as it references classes defined earlier in this file FACT_SUBSETS = dict( hardware=Hardware, network=Network, virtual=Virtual, + ohai=Ohai, + facter=Facter, ) +VALID_SUBSETS = frozenset(FACT_SUBSETS.keys()) diff --git a/lib/ansible/playbook/play.py b/lib/ansible/playbook/play.py index 6be4c5367e..b917985bdc 100644 --- a/lib/ansible/playbook/play.py +++ b/lib/ansible/playbook/play.py @@ -65,8 +65,6 @@ class Play(Base, Taggable, Become): # Connection _gather_facts = FieldAttribute(isa='bool', default=None, always_post_validate=True) _gather_subset = FieldAttribute(isa='list', default=None, always_post_validate=True) - _ignore_facter = FieldAttribute(isa='bool', default=None, always_post_validate=True) - _ignore_ohai = FieldAttribute(isa='bool', default=None, always_post_validate=True) _hosts = FieldAttribute(isa='list', required=True, listof=string_types, always_post_validate=True) _name = FieldAttribute(isa='string', default='', always_post_validate=True) diff --git a/test/integration/Makefile b/test/integration/Makefile index 07b2d3c78c..331022ce51 100644 --- a/test/integration/Makefile +++ b/test/integration/Makefile @@ -23,7 +23,7 @@ VAULT_PASSWORD_FILE = vault-password CONSUL_RUNNING := $(shell python consul_running.py) EUID := $(shell id -u -r) -all: setup test_test_infra parsing test_var_precedence unicode test_templating_settings environment non_destructive destructive includes blocks pull check_mode test_hash test_handlers test_group_by test_vault test_tags test_lookup_paths no_log test_connection +all: setup test_test_infra parsing test_var_precedence unicode test_templating_settings environment non_destructive destructive includes blocks pull check_mode test_hash test_handlers test_group_by test_vault test_tags test_lookup_paths no_log test_connection test_gathering_facts test_test_infra: [ "$$(ansible-playbook -i $(INVENTORY) test_test_infra.yml -e @$(VARS_FILE) $(CREDENTIALS_ARG) $(TEST_FLAGS) | fgrep works | xargs)" = "msg: fail works (True) msg: assert works (True)" ] @@ -70,6 +70,9 @@ unicode: setup test_templating_settings: setup ansible-playbook test_templating_settings.yml -i $(INVENTORY) -e outputdir=$(TEST_DIR) -e @$(VARS_FILE) $(CREDENTIALS_ARG) -v $(TEST_FLAGS) +test_gathering_facts: setup + ansible-playbook test_gathering_facts.yml -i $(INVENTORY) -e outputdir=$(TEST_DIR) -e @$(VARS_FILE) -v $(TEST_FLAGS) + environment: setup ansible-playbook test_environment.yml -i $(INVENTORY) -e outputdir=$(TEST_DIR) -e @$(VARS_FILE) $(CREDENTIALS_ARG) $(TEST_FLAGS) diff --git a/test/integration/test_gathering_facts.yml b/test/integration/test_gathering_facts.yml index 03d707fbf1..eb39c84b59 100644 --- a/test/integration/test_gathering_facts.yml +++ b/test/integration/test_gathering_facts.yml @@ -3,20 +3,16 @@ - hosts: localhost tags: [ 'min' ] connection: local - gather_subset: "min" - ignore_facter: yes - ignore_ohai: yes + gather_subset: "!all" gather_facts: yes tasks: - debug: var={{item}} - with_items: [ 'ansible_user_id', 'ansible_interfaces', 'ansible_mounts', 'ansible_virtualization_role' ] + with_items: [ 'ansible_user_id', 'ansible_interfaces', 'ansible_mounts', 'ansible_virtualization_role' ] - hosts: localhost tags: [ 'network' ] connection: local gather_subset: "network" - ignore_facter: yes - ignore_ohai: yes gather_facts: yes tasks: - debug: var={{item}} @@ -26,8 +22,6 @@ tags: [ 'hardware' ] connection: local gather_subset: "hardware" - ignore_facter: yes - ignore_ohai: yes gather_facts: yes tasks: - debug: var={{item}} @@ -37,8 +31,6 @@ tags: [ 'virtual' ] connection: local gather_subset: "virtual" - ignore_facter: yes - ignore_ohai: yes gather_facts: yes tasks: - debug: var={{item}} From f0e6d28815da7e730a39fd546649c06d0e7566c0 Mon Sep 17 00:00:00 2001 From: Toshio Kuratomi Date: Mon, 14 Mar 2016 18:48:40 -0700 Subject: [PATCH 5/5] Make integration tests for fact gathering assert on failure --- test/integration/inventory | 3 + test/integration/test_gathering_facts.yml | 111 ++++++++++++++++++---- 2 files changed, 98 insertions(+), 16 deletions(-) diff --git a/test/integration/inventory b/test/integration/inventory index bee36ce022..b833343839 100644 --- a/test/integration/inventory +++ b/test/integration/inventory @@ -4,6 +4,9 @@ testhost2 ansible_ssh_host=127.0.0.1 ansible_connection=local # For testing delegate_to testhost3 ansible_ssh_host=127.0.0.3 testhost4 ansible_ssh_host=127.0.0.4 +# For testing fact gathering +facthost[0:7] ansible_host=1270.0.0.1 ansible_connection=local + # the following inline declarations are accompanied # by (preferred) group_vars/ and host_vars/ variables diff --git a/test/integration/test_gathering_facts.yml b/test/integration/test_gathering_facts.yml index eb39c84b59..8e93be0a8c 100644 --- a/test/integration/test_gathering_facts.yml +++ b/test/integration/test_gathering_facts.yml @@ -1,37 +1,116 @@ --- -- hosts: localhost - tags: [ 'min' ] +- hosts: facthost0 + tags: [ 'fact_min' ] connection: local gather_subset: "!all" gather_facts: yes tasks: - - debug: var={{item}} - with_items: [ 'ansible_user_id', 'ansible_interfaces', 'ansible_mounts', 'ansible_virtualization_role' ] + - name: Test that only retrieving minimal facts work + assert: + that: + - '"{{ ansible_user_id|default("UNDEF") }}" != "UNDEF"' + - '"{{ ansible_interfaces|default("UNDEF") }}" == "UNDEF"' + - '"{{ ansible_mounts|default("UNDEF") }}" == "UNDEF"' + - '"{{ ansible_virtualization_role|default("UNDEF") }}" == "UNDEF"' -- hosts: localhost - tags: [ 'network' ] +- hosts: facthost1 + tags: [ 'fact_network' ] connection: local gather_subset: "network" gather_facts: yes tasks: - - debug: var={{item}} - with_items: [ 'ansible_user_id', 'ansible_interfaces', 'ansible_mounts', 'ansible_virtualization_role' ] + - name: Test that retrieving network facts work + assert: + that: + - '"{{ ansible_user_id|default("UNDEF") }}" != "UNDEF"' + - '"{{ ansible_interfaces|default("UNDEF") }}" != "UNDEF"' + - '"{{ ansible_mounts|default("UNDEF") }}" == "UNDEF"' + - '"{{ ansible_virtualization_role|default("UNDEF") }}" == "UNDEF"' -- hosts: localhost - tags: [ 'hardware' ] +- hosts: facthost2 + tags: [ 'fact_hardware' ] connection: local gather_subset: "hardware" gather_facts: yes tasks: - - debug: var={{item}} - with_items: [ 'ansible_user_id', 'ansible_interfaces', 'ansible_mounts', 'ansible_virtualization_role' ] + - name: Test that retrieving hardware facts work + assert: + that: + - '"{{ ansible_user_id|default("UNDEF") }}" != "UNDEF"' + - '"{{ ansible_interfaces|default("UNDEF") }}" == "UNDEF"' + - '"{{ ansible_mounts|default("UNDEF") }}" != "UNDEF"' + - '"{{ ansible_virtualization_role|default("UNDEF") }}" == "UNDEF"' -- hosts: localhost - tags: [ 'virtual' ] +- hosts: facthost3 + tags: [ 'fact_virtual' ] connection: local gather_subset: "virtual" gather_facts: yes tasks: - - debug: var={{item}} - with_items: [ 'ansible_user_id', 'ansible_interfaces', 'ansible_mounts', 'ansible_virtualization_role' ] + - name: Test that retrieving virtualization facts work + assert: + that: + - '"{{ ansible_user_id|default("UNDEF") }}" != "UNDEF"' + - '"{{ ansible_interfaces|default("UNDEF") }}" == "UNDEF"' + - '"{{ ansible_mounts|default("UNDEF") }}" == "UNDEF"' + - '"{{ ansible_virtualization_role|default("UNDEF") }}" != "UNDEF"' + +- hosts: facthost4 + tags: [ 'fact_comma_string' ] + connection: local + gather_subset: "virtual,network" + gather_facts: yes + tasks: + - name: Test that retrieving virtualization and network as a string works + assert: + that: + - '"{{ ansible_user_id|default("UNDEF") }}" != "UNDEF"' + - '"{{ ansible_interfaces|default("UNDEF") }}" != "UNDEF"' + - '"{{ ansible_mounts|default("UNDEF") }}" == "UNDEF"' + - '"{{ ansible_virtualization_role|default("UNDEF") }}" != "UNDEF"' + +- hosts: facthost5 + tags: [ 'fact_yaml_list' ] + connection: local + gather_subset: + - virtual + - network + gather_facts: yes + tasks: + - name: Test that retrieving virtualization and network as a string works + assert: + that: + - '"{{ ansible_user_id|default("UNDEF") }}" != "UNDEF"' + - '"{{ ansible_interfaces|default("UNDEF") }}" != "UNDEF"' + - '"{{ ansible_mounts|default("UNDEF") }}" == "UNDEF"' + - '"{{ ansible_virtualization_role|default("UNDEF") }}" != "UNDEF"' + +- hosts: facthost6 + tags: [ 'fact_negation' ] + connection: local + gather_subset: "!hardware" + gather_facts: yes + tasks: + - name: Test that negation of fact subsets work + assert: + that: + - '"{{ ansible_user_id|default("UNDEF") }}" != "UNDEF"' + - '"{{ ansible_interfaces|default("UNDEF") }}" != "UNDEF"' + - '"{{ ansible_mounts|default("UNDEF") }}" == "UNDEF"' + - '"{{ ansible_virtualization_role|default("UNDEF") }}" != "UNDEF"' + +- hosts: facthost7 + tags: [ 'fact_mixed_negation_addition' ] + connection: local + gather_subset: "!hardware,network" + gather_facts: yes + tasks: + - name: Test that negation and additional subsets work together + assert: + that: + - '"{{ ansible_user_id|default("UNDEF") }}" != "UNDEF"' + - '"{{ ansible_interfaces|default("UNDEF") }}" != "UNDEF"' + - '"{{ ansible_mounts|default("UNDEF") }}" == "UNDEF"' + - '"{{ ansible_virtualization_role|default("UNDEF") }}" == "UNDEF"' +