Optionally propagate pillar data from Salt to reclass

Optionally, data from pillars that run before the reclass ``ext_pillar``
(i.e. Salt's builtin ``pillar_roots``, as well as other ``ext_pillar``
modules listed before the ``reclass_adapter``) can be made available to
reclass. Please use this with caution as referencing data from Salt in
the inventory will make it harder or impossible to run |reclass| in
other environments. This feature is therefore turned off by default and
must be explicitly enabled in the Salt master configuration file, like
this:

  reclass: &reclass
      […]
      propagate_pillar_data_to_reclass: True

Signed-off-by: martin f. krafft <madduck@madduck.net>
diff --git a/doc/source/changelog.rst b/doc/source/changelog.rst
index 25bda17..9f91569 100644
--- a/doc/source/changelog.rst
+++ b/doc/source/changelog.rst
@@ -5,6 +5,8 @@
 ========= ========== ========================================================
 Version   Date       Changes
 ========= ========== ========================================================
+                     * Salt: pillar data from previous pillars are now
+                       available to reclass parameter interpolation
                      * yaml_fs: classes may be defined in subdirectories
                        (closes: #12, #19, #20)
                      * Migrate Salt adapter to new core API (closes: #18)
diff --git a/doc/source/salt.rst b/doc/source/salt.rst
index f0cc733..a5f9a73 100644
--- a/doc/source/salt.rst
+++ b/doc/source/salt.rst
@@ -178,9 +178,9 @@
 Similarly, all parameters that are collected and merged eventually end up in
 the pillar data of a specific node.
 
-However, the pillar data of a node include all the information about classes
-and applications, so you could theoretically use them to target your Salt
-calls at groups of nodes defined in the |reclass| inventory, e.g.
+The pillar data of a node include all the information about classes and
+applications, so you could theoretically use them to target your Salt calls at
+groups of nodes defined in the |reclass| inventory, e.g.
 
 ::
 
@@ -189,5 +189,17 @@
 Unfortunately, this does not work yet, please stay tuned, and let me know
 if you figure out a way. `Salt issue #5787`_ is also of relevance.
 
+Optionally, data from pillars that run before the |reclass| ``ext_pillar``
+(i.e. Salt's builtin ``pillar_roots``, as well as other ``ext_pillar`` modules
+listed before the ``reclass_adapter``) can be made available to |reclass|.
+Please use this with caution as referencing data from Salt in the inventory
+will make it harder or impossible to run |reclass| in other environments. This
+feature is therefore turned off by default and must be explicitly enabled in
+the Salt master configuration file, like this::
+
+  reclass: &reclass
+      […]
+      propagate_pillar_data_to_reclass: True
+
 .. include:: substs.inc
 .. include:: extrefs.inc
diff --git a/reclass/adapters/salt.py b/reclass/adapters/salt.py
index dfffc6e..1b45823 100755
--- a/reclass/adapters/salt.py
+++ b/reclass/adapters/salt.py
@@ -23,13 +23,17 @@
                inventory_base_uri=OPT_INVENTORY_BASE_URI,
                nodes_uri=OPT_NODES_URI,
                classes_uri=OPT_CLASSES_URI,
-               class_mappings=None):
+               class_mappings=None,
+               propagate_pillar_data_to_reclass=False):
 
     nodes_uri, classes_uri = path_mangler(inventory_base_uri,
                                           nodes_uri, classes_uri)
     storage = get_storage(storage_type, nodes_uri, classes_uri,
                           default_environment='base')
-    reclass = Core(storage, class_mappings)
+    input_data = None
+    if propagate_pillar_data_to_reclass:
+        input_data = pillar
+    reclass = Core(storage, class_mappings, input_data=input_data)
 
     data = reclass.nodeinfo(minion_id)
     params = data.get('parameters', {})
@@ -50,7 +54,7 @@
                                           nodes_uri, classes_uri)
     storage = get_storage(storage_type, nodes_uri, classes_uri,
                           default_environment='base')
-    reclass = Core(storage, class_mappings)
+    reclass = Core(storage, class_mappings, input_data=None)
 
     # if the minion_id is not None, then return just the applications for the
     # specific minion, otherwise return the entire top data (which we need for
diff --git a/reclass/core.py b/reclass/core.py
index f65fb82..76bd0a8 100644
--- a/reclass/core.py
+++ b/reclass/core.py
@@ -13,14 +13,15 @@
 #import sys
 import fnmatch
 import shlex
-from reclass.datatypes import Entity, Classes
+from reclass.datatypes import Entity, Classes, Parameters
 from reclass.errors import MappingFormatError, ClassNotFound
 
 class Core(object):
 
-    def __init__(self, storage, class_mappings):
+    def __init__(self, storage, class_mappings, input_data=None):
         self._storage = storage
         self._class_mappings = class_mappings
+        self._input_data = input_data
 
     @staticmethod
     def _get_timestamp():
@@ -53,9 +54,9 @@
             key = '/{0}/'.format(key)
         return key, list(lexer)
 
-    def _populate_with_class_mappings(self, nodename):
+    def _get_class_mappings_entity(self, nodename):
         if not self._class_mappings:
-            return Entity(name='empty')
+            return Entity(name='empty (class mappings)')
         c = Classes()
         for mapping in self._class_mappings:
             matched = False
@@ -74,6 +75,12 @@
         return Entity(classes=c,
                       name='class mappings for node {0}'.format(nodename))
 
+    def _get_input_data_entity(self):
+        if not self._input_data:
+            return Entity(name='empty (input data)')
+        p = Parameters(self._input_data)
+        return Entity(parameters=p, name='input data')
+
     def _recurse_entity(self, entity, merge_base=None, seen=None, nodename=None):
         if seen is None:
             seen = {}
@@ -104,7 +111,9 @@
 
     def _nodeinfo(self, nodename):
         node_entity = self._storage.get_node(nodename)
-        base_entity = self._populate_with_class_mappings(node_entity.name)
+        base_entity = Entity(name='base')
+        base_entity.merge(self._get_class_mappings_entity(node_entity.name))
+        base_entity.merge(self._get_input_data_entity())
         seen = {}
         merge_base = self._recurse_entity(base_entity, seen=seen,
                                           nodename=base_entity.name)