From 38b055143a3b6b07523b59789679678b9e853456 Mon Sep 17 00:00:00 2001
From: Seth Vidal <skvidal@fedoraproject.org>
Date: Sat, 15 Jun 2013 18:31:31 -0400
Subject: [PATCH] cut and docs for facts.d mechanism for setup module

---
 system/setup | 59 ++++++++++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 59 insertions(+)

diff --git a/system/setup b/system/setup
index 274fb469ee9..b626ead79c4 100644
--- a/system/setup
+++ b/system/setup
@@ -30,6 +30,9 @@ import struct
 import datetime
 import getpass
 import subprocess
+import ConfigParser
+import StringIO
+
 
 DOCUMENTATION = '''
 ---
@@ -42,6 +45,15 @@ options:
             - if supplied, only return facts that match this shell-style (fnmatch) wildcard.
         required: false
         default: '*'
+    fact_path:
+        version_added: "1.3"
+        description:
+            - path used for local ansible facts (*.fact) - files in this dir
+              will be run (if executable) and their results be added to ansible_local facts
+              if a file is not executable it is read. 
+              File/results format can be json or ini-format
+        required: false
+        default: '/etc/ansible/facts.d'
 description:
      - This module is automatically called by playbooks to gather useful
        variables about remote hosts that can be used in playbooks. It can also be
@@ -131,6 +143,7 @@ class Facts(object):
         self.get_lsb_facts()
         self.get_date_time_facts()
         self.get_user_facts()
+        self.get_local_facts()
 
     def populate(self):
         return self.facts
@@ -169,6 +182,51 @@ class Facts(object):
             self.facts['architecture'] = data[0]
 
 
+    def get_local_facts(self):
+
+        fact_path = module.params.get('fact_path', None)
+        if not fact_path or not os.path.exists(fact_path):
+            return
+            
+        local = {}
+        for fn in sorted(glob.glob(fact_path + '/*.fact')):
+            # where it will sit under local facts
+            fact_base = os.path.basename(fn).replace('.fact','')
+            if os.access(fn, os.X_OK):
+                # run it
+                # 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)
+            else:
+                out = open(fn).read()
+            
+            # load raw json
+            fact = 'loading %s' % fact_base
+            try:
+                fact = json.loads(out)
+            except ValueError, e:
+                # load raw ini
+                cp = ConfigParser.ConfigParser()
+                try:
+                    cp.readfp(StringIO.StringIO(out))
+                except ConfigParser.Error, e:
+                    fact="error loading fact - please check content"
+                else:
+                    fact = {}
+                    #print cp.sections()
+                    for sect in cp.sections():
+                        if sect not in fact:
+                            fact[sect] = {}
+                        for opt in cp.options(sect):
+                            val = cp.get(sect, opt)
+                            fact[sect][opt]=val
+
+            local[fact_base] = fact
+        if not local:
+            return
+        self.facts['local'] = local
+            
     # platform.dist() is deprecated in 2.6
     # in 2.6 and newer, you should use platform.linux_distribution()
     def get_distribution_facts(self):
@@ -2002,6 +2060,7 @@ def main():
     module = AnsibleModule(
         argument_spec = dict(
             filter=dict(default="*", required=False),
+            fact_path=dict(default='/etc/ansible/facts.d', required=False),
         ),
         supports_check_mode = True,
     )