Add 'trace-key' command
Change-Id: I796e15544a9224ed53f435354a29f2616b541b97
diff --git a/reclass_tools/reclass_models.py b/reclass_tools/reclass_models.py
index 4f85e5e..265c37a 100644
--- a/reclass_tools/reclass_models.py
+++ b/reclass_tools/reclass_models.py
@@ -12,17 +12,94 @@
# License for the specific language governing permissions and limitations
# under the License.
+import copy
+
import reclass
# from reclass.adapters import salt as reclass_salt
from reclass import config as reclass_config
from reclass import core as reclass_core
+from reclass import defaults as reclass_defaults
+from reclass.utils.refvalue import RefValue
+import yaml
from reclass_tools import helpers
# import salt.cli.call
# import salt.cli.caller
-def get_core():
+def refvalue_representer(dumper, data):
+ return dumper.represent_str(
+ data._assemble(
+ lambda s: s.join(reclass_defaults.PARAMETER_INTERPOLATION_SENTINELS)))
+yaml.add_representer(RefValue, refvalue_representer)
+
+class ReclassCore(reclass_core.Core):
+ """Track the specific key
+
+ :param key: string with dot-separated keys
+ """
+ track_key_path = None
+
+ def __init__(self, storage, class_mappings, input_data=None,
+ key=None):
+ if key:
+ self.track_key_path = key.split('.')
+ if 'parameters' not in self.track_key_path:
+ raise Exception("Please use the key path starting from 'parameters'.")
+ # Remove the first 'parameters' element because the model entities
+ # keep parameters in different object format.
+ self.track_key_path = self.track_key_path[1:]
+
+ super(ReclassCore, self).__init__(storage, class_mappings, input_data)
+
+ def _recurse_entity(self, entity, merge_base=None, seen=None, nodename=None):
+ if seen is None:
+ seen = {}
+ if '__visited' not in seen:
+ seen['__visited'] = []
+
+ orig_visited = copy.deepcopy(seen['__visited'])
+ seen['__visited'].append(entity.name)
+
+ result = super(ReclassCore, self)._recurse_entity(entity,
+ merge_base,
+ seen,
+ nodename)
+ if self.track_key_path:
+ key = helpers.get_nested_key(entity.parameters.as_dict(),
+ path=self.track_key_path)
+ if key:
+ print("# " + ' < '.join(seen['__visited']))
+ out_dict = {}
+ helpers.create_nested_key(out_dict, ['parameters'] + self.track_key_path, key)
+ print(yaml.dump(out_dict,
+ default_flow_style=False))
+
+ # Reset the data collected by child entries
+ seen['__visited'] = orig_visited
+
+ return result
+
+ def _nodeinfo(self, nodename):
+ if self.track_key_path:
+ print("\n" + nodename)
+ print("-" * len(nodename))
+
+ result = super(ReclassCore, self)._nodeinfo(nodename)
+
+ if self.track_key_path:
+ key = helpers.get_nested_key(result.parameters.as_dict(),
+ path=self.track_key_path)
+ if key:
+ print("### Final result after interpolation: ###")
+ out_dict = {}
+ helpers.create_nested_key(out_dict, ['parameters'] + self.track_key_path, key)
+ print(yaml.dump(out_dict,
+ default_flow_style=False))
+ return result
+
+
+def get_core(key=None):
"""Initializes reclass Core() using /etc/reclass settings"""
defaults = reclass_config.find_and_read_configfile()
@@ -34,7 +111,8 @@
storage = reclass.get_storage(storage_type, nodes_uri, classes_uri,
default_environment='base')
- return reclass_core.Core(storage, None, None)
+ #key = '_param.keepalived_vip_interface'
+ return ReclassCore(storage, None, None, key=key)
# def get_minion_domain():
@@ -58,11 +136,31 @@
return inventory
+def nodes_list(domain=None):
+ core = get_core()
+ nodes = core._storage.enumerate_nodes()
+ if domain is not None:
+ nodes = [node for node in nodes
+ if node.endswith(domain)]
+ return nodes
+
+
def get_nodeinfo(minion_id):
core = get_core()
return core.nodeinfo(minion_id)
+def trace_key(key, domain=None, node=None):
+ if node:
+ nodes = [node]
+ else:
+ nodes = nodes_list(domain=domain)
+
+ core = get_core(key=key)
+ for node in nodes:
+ core.nodeinfo(node)
+
+
def vcp_list(domain=None, inventory=None):
"""List VCP node names