Network check HTML report

- uniform map generation
- POC for additional checks on env

Change-Id: I52edcc94f4d9826cbfb1159e5311514097a15f62
Related-PROD: PROD-32792
diff --git a/cfg_checker/reports/reporter.py b/cfg_checker/reports/reporter.py
index a624dd3..8059fab 100644
--- a/cfg_checker/reports/reporter.py
+++ b/cfg_checker/reports/reporter.py
@@ -4,6 +4,7 @@
 
 from cfg_checker.common import const
 from cfg_checker.common import logger_cli
+from cfg_checker.nodes import salt_master
 
 import jinja2
 
@@ -13,6 +14,12 @@
 pkg_dir = os.path.join(pkg_dir, os.pardir, os.pardir)
 pkg_dir = os.path.normpath(pkg_dir)
 
+# % threshhold values
+_disk_warn = 80
+_disk_critical = 90
+_ram_warn = 5
+_ram_critical = 3
+
 
 def line_breaks(text):
     # replace python linebreaks with html breaks
@@ -41,7 +48,7 @@
     return sorted(_list)[-1]
 
 
-def make_action_label(act):
+def make_pkg_action_label(act):
     _act_labels = {
         const.ACT_UPGRADE: "Upgrade possible",
         const.ACT_NEED_UP: "Needs upgrade",
@@ -52,7 +59,7 @@
     return _act_labels[act]
 
 
-def make_action_class(act):
+def make_pkg_action_class(act):
     _act_classes = {
         const.ACT_UPGRADE: "possible",
         const.ACT_NEED_UP: "needs_up",
@@ -63,7 +70,7 @@
     return _act_classes[act]
 
 
-def make_status_label(sts):
+def make_pkg_status_label(sts):
     _status_labels = {
         const.VERSION_OK: "OK",
         const.VERSION_UP: "Upgraded",
@@ -74,8 +81,12 @@
     return _status_labels[sts]
 
 
-def make_status_class(sts):
-    return const.all_statuses[sts]
+def make_pkg_status_class(sts):
+    return const.all_pkg_statuses[sts]
+
+
+def make_node_status(sts):
+    return const.node_status[sts]
 
 
 def make_repo_info(repos):
@@ -144,10 +155,11 @@
         self.jinja2_env.filters['get_max'] = get_max
 
         self.jinja2_env.filters['get_sorted_keys'] = get_sorted_keys
-        self.jinja2_env.filters['make_status_label'] = make_status_label
-        self.jinja2_env.filters['make_status_class'] = make_status_class
-        self.jinja2_env.filters['make_action_label'] = make_action_label
-        self.jinja2_env.filters['make_action_class'] = make_action_class
+        self.jinja2_env.filters['pkg_status_label'] = make_pkg_status_label
+        self.jinja2_env.filters['pkg_status_class'] = make_pkg_status_class
+        self.jinja2_env.filters['pkg_action_label'] = make_pkg_action_label
+        self.jinja2_env.filters['pkg_action_class'] = make_pkg_action_class
+        self.jinja2_env.filters['node_status_class'] = make_node_status
         self.jinja2_env.filters['make_repo_info'] = make_repo_info
 
         # render!
@@ -194,6 +206,130 @@
 class HTMLNetworkReport(_TMPLBase):
     tmpl = "network_check_tmpl.j2"
 
+    def _extend_data(self, data):
+        def get_bytes(value):
+            if value[-1] == 'G':
+                return int(float(value[:-1]) * 1024 * 1024 * 1024)
+            elif value[-1] == 'M':
+                return int(float(value[:-1]) * 1024 * 1024)
+            elif value[-1] == 'K':
+                return int(float(value[:-1]) * 1024)
+            else:
+                return int(value)
+
+        def _lscpu(field, key, _dict):
+            _f_cmd = salt_master.get_cmd_for_nodes
+            _cmd = "lscpu | grep -e \"^{}:\" | cut -d\":\" -f2 " \
+                   "| sed -e 's/^[[:space:]]*//'"
+            _f_cmd(_cmd.format(field), key, target_dict=_dict)
+
+        def _free(field, key, _dict):
+            _f_cmd = salt_master.get_cmd_for_nodes
+            _cmd = "free -h | sed -n '/Mem/s/ \\+/ /gp' | cut -d\" \" -f {}"
+            _f_cmd(_cmd.format(field), key, target_dict=_dict)
+
+        def _services(_dict):
+            _key = "services"
+            _key_r = "services_raw"
+            _f_cmd = salt_master.get_cmd_for_nodes
+            _cmd = "service --status-all"
+            _f_cmd(_cmd, _key_r, target_dict=_dict)
+            for node, dt in _dict.iteritems():
+                dt[_key] = {}
+                lines = dt[_key_r].splitlines()
+                for line in lines:
+                    li = line.split()
+                    _status = li[1]
+                    _name = li[3]
+                    if _status == '-':
+                        dt[_key][_name] = False
+                    elif _status == '+':
+                        dt[_key][_name] = True
+                    else:
+                        dt[_key][_name] = None
+                dt.pop(_key_r)
+
+        data["const"] = {
+            "ram_warn": _ram_warn,
+            "ram_critical": _ram_critical,
+            "disk_warn": _disk_warn,
+            "disk_critical": _disk_critical
+        }
+
+        # get kernel version
+        salt_master.get_cmd_for_nodes(
+            "uname -r",
+            "kernel",
+            target_dict=data["nodes"]
+        )
+        # cpu info
+        # Sample: VT-x, KVM, full
+        _lscpu("Virtualization", "virt_mode", data["nodes"])
+        _lscpu("Hypervisor vendor", "virt_vendor", data["nodes"])
+        _lscpu("Virtualization type", "virt_type", data["nodes"])
+        # sample: 4
+        _lscpu("CPU(s)", "cpus", data["nodes"])
+
+        # free ram
+        # sample: 16425392 14883144 220196
+        _free("2", "ram_total", data["nodes"])
+        _free("3", "ram_used", data["nodes"])
+        _free("4", "ram_free", data["nodes"])
+        _free("7", "ram_available", data["nodes"])
+        for _data in data["nodes"].itervalues():
+            _total = get_bytes(_data["ram_total"])
+            _avail = get_bytes(_data["ram_available"])
+            _m = _avail * 100.0 / _total
+            if _m < _ram_critical:
+                _data["ram_status"] = "fail"
+            elif _m < _ram_warn:
+                _data["ram_status"] = "warn"
+            else:
+                _data["ram_status"] = ""
+
+        # disk space
+        # sample: /dev/vda1 78G 33G 45G 43%
+        salt_master.get_cmd_for_nodes(
+            "df -h | sed -n '/^\\/dev/s/ \\+/ /gp' | cut -d\" \" -f 1-5",
+            "disk_raw",
+            target_dict=data["nodes"]
+        )
+        for _data in data["nodes"].itervalues():
+            _data["disk"] = {}
+            # show first device row by default
+            _data["disk_max_dev"] = None
+            _d = _data["disk"]
+            _r = _data["disk_raw"]
+            _r = _r.splitlines()
+            _max = -1
+            for idx in range(0, len(_r)):
+                _t = _r[idx].split()
+                _d[_t[0]] = {}
+                _d[_t[0]]['v'] = _t[1:]
+                _chk = int(_t[-1].split('%')[0])
+                if _chk > _max:
+                    _data["disk_max_dev"] = _t[0]
+                    _max = _chk
+                if _chk > _disk_critical:
+                    _d[_t[0]]['f'] = "fail"
+                elif _chk > _disk_warn:
+                    _d[_t[0]]['f'] = "warn"
+                else:
+                    _d[_t[0]]['f'] = ""
+
+        # prepare networks data for report
+        for net, net_v in data['map'].iteritems():
+            for node, ifs in net_v.iteritems():
+                for d in ifs:
+                    _err = "fail"
+                    d['interface_error'] = _err if d['interface_error'] else ""
+                    d['mtu_error'] = _err if d['mtu_error'] else ""
+                    d['status_error'] = _err if d['status_error'] else ""
+                    d['subnet_gateway_error'] = \
+                        _err if d['subnet_gateway_error'] else ""
+
+        _services(data["nodes"])
+
 
 class ReportToFile(object):
     def __init__(self, report, target):