Reclass Diff reorganize and updates
diff --git a/cfg_checker/reclass_cmp.py b/cfg_checker/reclass_cmp.py
index ee658bd..f7c34d6 100644
--- a/cfg_checker/reclass_cmp.py
+++ b/cfg_checker/reclass_cmp.py
@@ -10,9 +10,35 @@
 from cfg_checker.common import logger, logger_cli
 
 
+def get_element(element_path, input_data):     
+    paths = element_path.split(":")
+    data = input_data
+    for i in range(0, len(paths)):
+        data = data[paths[i]]
+    return data
+
+
+def pop_element(element_path, input_data):     
+    paths = element_path.split(":")
+    data = input_data
+    # Search for last dict
+    for i in range(0, len(paths)-1):
+        data = data[paths[i]]
+    # pop the actual element
+    return data.pop(paths[-1])
+
+
 class ModelComparer(object):
     """Collection of functions to compare model data.
     """
+    # key order is important
+    _model_parts = {
+        "01_nodes": "nodes",
+        "02_system": "classes:system",
+        "03_cluster": "classes:cluster",
+        "04_other": "classes"
+    }
+    
     models = {}
     models_path = "/srv/salt/reclass"
     model_name_1 = "source"
@@ -97,189 +123,214 @@
             # creating dict structure out of folder list. Pure python magic
             parent = reduce(dict.get, folders[:-1], raw_tree)
             parent[folders[-1]] = subdir
+        
+        self.models[name] = {}
+        # Brake in according to pathes
+        _parts = self._model_parts.keys()
+        _parts = sorted(_parts)
+        for ii in range(0, len(_parts)):
+            self.models[name][_parts[ii]] = pop_element(
+                self._model_parts[_parts[ii]],
+                raw_tree[root_key]
+            )
+        
         # save it as a single data object
-        self.models[name] = raw_tree[root_key]
+        self.models[name]["all_diffs"] = raw_tree[root_key]
         return True
 
+    def find_changes(self, dict1, dict2, path=""):
+        _report = {}
+        for k in dict1.keys():
+            # yamls might load values as non-str types
+            if not isinstance(k, str):
+                _new_path = path + ":" + str(k)
+            else:
+                _new_path = path + ":" + k
+            # ignore _source key
+            if k == "_source":
+                continue
+            # check if this is an env name cluster entry
+            if dict2 is not None and \
+                    k == self.model_name_1 and \
+                    self.model_name_2 in dict2.keys():
+                k1 = self.model_name_1
+                k2 = self.model_name_2
+                if type(dict1[k1]) is dict:
+                    if path == "":
+                        _new_path = k1
+                    _child_report = self.find_changes(
+                        dict1[k1],
+                        dict2[k2],
+                        _new_path
+                    )
+                    _report.update(_child_report)
+            elif dict2 is None or k not in dict2:
+                # no key in dict2
+                _report[_new_path] = {
+                    "type": "value",
+                    "raw_values": [dict1[k], "N/A"],
+                    "str_values": [
+                        "{}".format(dict1[k]),
+                        "n/a"
+                    ]
+                }
+                logger.info(
+                    "{}: {}, {}".format(_new_path, dict1[k], "N/A")
+                )
+            else:
+                if type(dict1[k]) is dict:
+                    if path == "":
+                        _new_path = k
+                    _child_report = self.find_changes(
+                        dict1[k],
+                        dict2[k],
+                        _new_path
+                    )
+                    _report.update(_child_report)
+                elif type(dict1[k]) is list and type(dict2[k]) is list:
+                    # use ifilterfalse to compare lists of dicts
+                    try:
+                        _removed = list(
+                            itertools.ifilterfalse(
+                                lambda x: x in dict2[k],
+                                dict1[k]
+                            )
+                        )
+                        _added = list(
+                            itertools.ifilterfalse(
+                                lambda x: x in dict1[k],
+                                dict2[k]
+                            )
+                        )
+                    except TypeError as e:
+                        # debug routine,
+                        # should not happen, due to list check above
+                        logger.error(
+                            "Caught lambda type mismatch: {}".format(
+                                e.message
+                            )
+                        )
+                        logger_cli.warning(
+                            "Types mismatch for correct compare: "
+                            "{}, {}".format(
+                                type(dict1[k]),
+                                type(dict2[k])
+                            )
+                        )
+                        _removed = None
+                        _added = None
+                    _original = ["= {}".format(item) for item in dict1[k]]
+                    if _removed or _added:
+                        _removed_str_lst = ["- {}".format(item)
+                                            for item in _removed]
+                        _added_str_lst = ["+ {}".format(item)
+                                            for item in _added]
+                        _report[_new_path] = {
+                            "type": "list",
+                            "raw_values": [
+                                dict1[k],
+                                _removed_str_lst + _added_str_lst
+                            ],
+                            "str_values": [
+                                "{}".format('\n'.join(_original)),
+                                "{}\n{}".format(
+                                    '\n'.join(_removed_str_lst),
+                                    '\n'.join(_added_str_lst)
+                                )
+                            ]
+                        }
+                        logger.info(
+                            "{}:\n"
+                            "{} original items total".format(
+                                _new_path,
+                                len(dict1[k])
+                            )
+                        )
+                        if _removed:
+                            logger.info(
+                                "{}".format('\n'.join(_removed_str_lst))
+                            )
+                        if _added:
+                            logger.info(
+                                "{}".format('\n'.join(_added_str_lst))
+                            )
+                else:
+                    # in case of type mismatch
+                    # considering it as not equal
+                    d1 = dict1
+                    d2 = dict2
+                    val1 = d1[k] if isinstance(d1, dict) else d1
+                    val2 = d2[k] if isinstance(d2, dict) else d2
+                    try:
+                        match = val1 == val2
+                    except TypeError as e:
+                        logger.warning(
+                            "One of the values is not a dict: "
+                            "{}, {}".format(
+                                str(dict1),
+                                str(dict2)
+                            ))
+                        match = False
+                    if not match:
+                        _report[_new_path] = {
+                            "type": "value",
+                            "raw_values": [val1, val2],
+                            "str_values": [
+                                "{}".format(val1),
+                                "{}".format(val2)
+                            ]
+                        }
+                        logger.info("{}: {}, {}".format(
+                            _new_path,
+                            val1,
+                            val2
+                        ))
+        return _report
+
+
     def generate_model_report_tree(self):
         """Use two loaded models to generate comparison table with
         values are groupped by YAML files
         """
-        def find_changes(dict1, dict2, path=""):
-            _report = {}
-            for k in dict1.keys():
-                # yamls might load values as non-str types
-                if not isinstance(k, str):
-                    _new_path = path + ":" + str(k)
-                else:
-                    _new_path = path + ":" + k
-                # ignore _source key
-                if k == "_source":
-                    continue
-                # check if this is an env name cluster entry
-                if dict2 is not None and \
-                        k == self.model_name_1 and \
-                        self.model_name_2 in dict2.keys():
-                    k1 = self.model_name_1
-                    k2 = self.model_name_2
-                    if type(dict1[k1]) is dict:
-                        if path == "":
-                            _new_path = k1
-                        _child_report = find_changes(
-                            dict1[k1],
-                            dict2[k2],
-                            _new_path
-                        )
-                        _report.update(_child_report)
-                elif dict2 is None or k not in dict2:
-                    # no key in dict2
-                    _report[_new_path] = {
-                        "type": "value",
-                        "raw_values": [dict1[k], "N/A"],
-                        "str_values": [
-                            "{}".format(dict1[k]),
-                            "n/a"
-                        ]
-                    }
-                    logger.info(
-                        "{}: {}, {}".format(_new_path, dict1[k], "N/A")
-                    )
-                else:
-                    if type(dict1[k]) is dict:
-                        if path == "":
-                            _new_path = k
-                        _child_report = find_changes(
-                            dict1[k],
-                            dict2[k],
-                            _new_path
-                        )
-                        _report.update(_child_report)
-                    elif type(dict1[k]) is list and type(dict2[k]) is list:
-                        # use ifilterfalse to compare lists of dicts
-                        try:
-                            _removed = list(
-                                itertools.ifilterfalse(
-                                    lambda x: x in dict2[k],
-                                    dict1[k]
-                                )
-                            )
-                            _added = list(
-                                itertools.ifilterfalse(
-                                    lambda x: x in dict1[k],
-                                    dict2[k]
-                                )
-                            )
-                        except TypeError as e:
-                            # debug routine,
-                            # should not happen, due to list check above
-                            logger.error(
-                                "Caught lambda type mismatch: {}".format(
-                                    e.message
-                                )
-                            )
-                            logger_cli.warning(
-                                "Types mismatch for correct compare: "
-                                "{}, {}".format(
-                                    type(dict1[k]),
-                                    type(dict2[k])
-                                )
-                            )
-                            _removed = None
-                            _added = None
-                        _original = ["= {}".format(item) for item in dict1[k]]
-                        if _removed or _added:
-                            _removed_str_lst = ["- {}".format(item)
-                                                for item in _removed]
-                            _added_str_lst = ["+ {}".format(item)
-                                              for item in _added]
-                            _report[_new_path] = {
-                                "type": "list",
-                                "raw_values": [
-                                    dict1[k],
-                                    _removed_str_lst + _added_str_lst
-                                ],
-                                "str_values": [
-                                    "{}".format('\n'.join(_original)),
-                                    "{}\n{}".format(
-                                        '\n'.join(_removed_str_lst),
-                                        '\n'.join(_added_str_lst)
-                                    )
-                                ]
-                            }
-                            logger.info(
-                                "{}:\n"
-                                "{} original items total".format(
-                                    _new_path,
-                                    len(dict1[k])
-                                )
-                            )
-                            if _removed:
-                                logger.info(
-                                    "{}".format('\n'.join(_removed_str_lst))
-                                )
-                            if _added:
-                                logger.info(
-                                    "{}".format('\n'.join(_added_str_lst))
-                                )
-                    else:
-                        # in case of type mismatch
-                        # considering it as not equal
-                        d1 = dict1
-                        d2 = dict2
-                        val1 = d1[k] if isinstance(d1, dict) else d1
-                        val2 = d2[k] if isinstance(d2, dict) else d2
-                        try:
-                            match = val1 == val2
-                        except TypeError as e:
-                            logger.warning(
-                                "One of the values is not a dict: "
-                                "{}, {}".format(
-                                    str(dict1),
-                                    str(dict2)
-                                ))
-                            match = False
-                        if not match:
-                            _report[_new_path] = {
-                                "type": "value",
-                                "raw_values": [val1, val2],
-                                "str_values": [
-                                    "{}".format(val1),
-                                    "{}".format(val2)
-                                ]
-                            }
-                            logger.info("{}: {}, {}".format(
-                                _new_path,
-                                val1,
-                                val2
-                            ))
-            return _report
-        # tmp report for keys
-        diff_report = find_changes(
-            self.models[self.model_name_1],
-            self.models[self.model_name_2]
-        )
-        # prettify the report
-        for key in diff_report.keys():
-            # break the key in two parts
-            _ext = ".yml"
-            if ".yaml" in key:
-                _ext = ".yaml"
-            _split = key.split(_ext)
-            _file_path = _split[0]
-            _param_path = "none"
-            if len(_split) > 1:
-                _param_path = _split[1]
-            diff_report[key].update({
-                "class_file": _file_path + _ext,
-                "param": _param_path,
-            })
+        # We are to cut both models into logical pieces
+        # nodes, will not be equal most of the time
+        # system, must be pretty much the same or we in trouble
+        # cluster, will be the most curious part for comparison
+        # other, all of the rest
 
-        diff_report["diff_names"] = [self.model_name_1, self.model_name_2]
-        return diff_report
+        _diff_report = {}
+        for _key in self._model_parts.keys():
+            # tmp report for keys
+            _tmp_diffs = self.find_changes(
+                self.models[self.model_name_1][_key],
+                self.models[self.model_name_2][_key]
+            )
+            # prettify the report
+            for key in _tmp_diffs.keys():
+                # break the key in two parts
+                _ext = ".yml"
+                if ".yaml" in key:
+                    _ext = ".yaml"
+                _split = key.split(_ext)
+                _file_path = _split[0]
+                _param_path = "none"
+                if len(_split) > 1:
+                    _param_path = _split[1]
+                _tmp_diffs[key].update({
+                    "class_file": _file_path + _ext,
+                    "param": _param_path,
+                })
+            _diff_report[_key[3:]] = {
+                "path": self._model_parts[_key],
+                "diffs": _tmp_diffs
+            }
+
+        _diff_report["diff_names"] = [self.model_name_1, self.model_name_2]
+        return _diff_report
 
 
 def compare_models():
-    # Do actual compare using hardcoded model names
+    # Do actual compare using model names from the class
     mComparer = ModelComparer()
     mComparer.load_model_tree(
         mComparer.model_name_1,
@@ -289,18 +340,25 @@
         mComparer.model_name_2,
         mComparer.model_path_2
     )
+    # Models should have similar structure to be compared
+    # classes/system
+    # classes/cluster
+    # nodes
+    
     diffs = mComparer.generate_model_report_tree()
 
     report_file = \
         mComparer.model_name_1 + "-vs-" + mComparer.model_name_2 + ".html"
+    # HTML report class is post-callable
     report = reporter.ReportToFile(
         reporter.HTMLModelCompare(),
         report_file
     )
     logger_cli.info("...generating report to {}".format(report_file))
+    # report will have tabs for each of the comparable entities in diffs
     report({
         "nodes": {},
-        "diffs": diffs
+        "all_diffs": diffs,
     })
     # with open("./gen_tree.json", "w+") as _out:
     #     _out.write(json.dumps(mComparer.generate_model_report_tree))
diff --git a/cfg_checker/reporter.py b/cfg_checker/reporter.py
index a0eb1b3..c0f2c58 100644
--- a/cfg_checker/reporter.py
+++ b/cfg_checker/reporter.py
@@ -59,9 +59,12 @@
     def __call__(self, payload):
         # init data structures
         data = self.common_data()
+        # payload should have pre-sorted structure
+        # system, nodes, clusters, and the rest in other
         data.update({
             "nodes": payload['nodes'],
-            "diffs": payload['diffs']
+            "all_diffs": payload['diffs'],
+            "tabs": {}
         })
 
         # add template specific data
@@ -150,10 +153,12 @@
 
     def _extend_data(self, data):
         # move names into separate place
-        data["names"] = data["diffs"].pop("diff_names")
-
+        data["names"] = data["all_diffs"].pop("diff_names")
+        data["tabs"] = data.pop("all_diffs")
+        
         # counters - mdl_diff
-        data['counters']['mdl_diff'] = len(data["diffs"].keys())
+        for _tab in data["tabs"].keys():
+            data['counters'][_tab] = len(data["tabs"][_tab]["diffs"].keys())
 
 
 class HTMLNetworkReport(_TMPLBase):
diff --git a/templates/model_tree_cmp_tmpl.j2 b/templates/model_tree_cmp_tmpl.j2
index cc543e0..c3b798f 100644
--- a/templates/model_tree_cmp_tmpl.j2
+++ b/templates/model_tree_cmp_tmpl.j2
@@ -204,12 +204,15 @@
 </head>
 <body onload="init()">
 <div class="tab">
-  <button class="tablinks" onclick="openTab(event, 'mdl_changes')">
-    <div class="node_name">Model changes</div>
-    <div class="smallgreytext">({{ counters['mdl_diff'] }})</div>
+{% for tab in tabs.keys() | sort %}
+  <button class="tablinks" onclick="openTab(event, '{{ tab }}')">
+    <div class="node_name">{{ tab }}</div>
+    <div class="smallgreytext">({{ counters[tab] }})</div>
   </button>
+{% endfor %}
 </div>
-<div id="mdl_changes" class="tabcontent">
+{% for tab in tabs.keys() | sort %}
+<div id="{{ tab }}" class="tabcontent">
     <table class="pkgversions">
         <tbody>
         <tr>
@@ -217,23 +220,24 @@
             <td class="Header">{{ names[0] }}</td>
             <td class="Header">{{ names[1] }}</td>
         </tr>
-        <tr><td colspan=3>Changes found in the model</td></tr>
-        {% for diff in diffs.keys() | sort %}
+        <tr><td colspan=3>Changes found in {{ tabs[tab]["path"] }}</td></tr>
+        {% for diff in tabs[tab]["diffs"].keys() | sort %}
         <tr>
             <td class="pkgName">
-                <div class="class_file">{{ diffs[diff]["class_file"] }}</div>
-                <div class="param">{{ diffs[diff]["param"] }}</div>
+                <div class="class_file">{{ tabs[tab]["diffs"][diff]["class_file"] }}</div>
+                <div class="param">{{ tabs[tab]["diffs"][diff]["param"] }}</div>
             </td>
             <td class="version">
-                <div><pre>{{ diffs[diff]["str_values"][0] | linebreaks }}</pre></div>
+                <div><pre>{{ tabs[tab]["diffs"][diff]["str_values"][0] | linebreaks }}</pre></div>
             </td>
             <td class="version">
-                <div><pre>{{ diffs[diff]["str_values"][1] | linebreaks }}</pre></div>
+                <div><pre>{{ tabs[tab]["diffs"][diff]["str_values"][1] | linebreaks }}</pre></div>
             </td>
         </tr>
         {% endfor %}
         </tbody>
     </table>
 </div>
+{% endfor %}
 </body>
 </html>
\ No newline at end of file