[Tooling update] health_checks

* Added:

- Retrieve contrail vrouter agent status from analyticsdb
- Retrieve filtered contrail vrouter agent info from analyticsdb
- Remove unwanted debug print from entropy_check function

Related-Prod: PROD-29236

Change-Id: I272a1fea3e39ec597647947cad798993f09e536a
diff --git a/README.rst b/README.rst
index 55b079c..fc8366b 100644
--- a/README.rst
+++ b/README.rst
@@ -865,6 +865,18 @@
 
   salt -C cmp001* health_checks.contrail_vrouter_agent_status
 
+Retrieve contrail vrouter agent status from analyticsdb
+
+.. code-block:: bash
+
+  salt-call health_checks.contrail_collector_agent_status
+
+Retrieve filtered contrail vrouter agent info from analyticsdb
+
+.. code-block:: bash
+
+  salt-call health_checks.contrail_vrouter_agent_info cmp001
+
 Fetch libvirt supported machine types for compute node
 
 .. code-block:: bash
diff --git a/_modules/health_checks.py b/_modules/health_checks.py
index ad43343..6180023 100644
--- a/_modules/health_checks.py
+++ b/_modules/health_checks.py
@@ -6,6 +6,7 @@
 import os
 import re
 import json
+import yaml
 
 __author__ = "Dzmitry Stremkouski"
 __copyright__ = "Copyright 2019, Mirantis Inc."
@@ -15,6 +16,53 @@
 stream = logging.StreamHandler()
 logger.addHandler(stream)
 
+try:
+    from yaml import CLoader as Loader, CDumper as Dumper
+except ImportError:
+    from yaml import Loader, Dumper
+
+default_vrouter_info_map = yaml.load("""
+ContrailConfig:
+- deleted
+- elements:uuid
+- elements:virtual_router_dpdk_enabled
+- elements:virtual_router_type
+VrouterAgent:
+- build_info:build-info:0:build-version
+- build_info:build-info:0:build-number
+- collector_server_list_cfg
+- config_file
+- control_ip
+- control_node_list_cfg
+- dns_server_list_cfg
+- dns_servers
+- down_interface_count
+- eth_name
+- headless_mode_cfg
+- hostname_cfg
+- hypervisor
+- mode
+- phy_if
+- platform
+- self_ip_list
+- total_interface_count
+- tunnel_type
+- vhost_cfg
+- vhost_if
+- vr_limits:max_interfaces
+- vr_limits:max_labels
+- vr_limits:max_mirror_entries
+- vr_limits:max_nexthops
+- vr_limits:max_vrfs
+- vr_limits:vrouter_max_bridge_entries
+- vr_limits:vrouter_max_flow_entries
+- vr_limits:vrouter_max_oflow_bridge_entries
+- vr_limits:vrouter_max_oflow_entries
+- xmpp_peer_list:*:ip
+- xmpp_peer_list:*:primary
+- xmpp_peer_list:*:status
+""", Loader=Loader)
+
 
 def _failed_minions(out, agent, failed_minions):
 
@@ -1044,7 +1092,6 @@
     failed_minions = []
     verified_minions = []
 
-    print out
     for minion in out:
         verified_minions.append(minion)
         entropy = int(out[minion]['ret'])
@@ -1315,6 +1362,94 @@
         return "Unsupported xml tree for this function %s" % str(req.text)
 
 
+def contrail_collector_agent_status(vr_name, api_host='auto', api_port=9081):
+
+    ''' Retrieve contrail vrouter agent status from analyticsdb '''
+
+    if api_host[0:4] == 'http':
+        url = api_host + ':' + str(api_port)
+    elif api_host == 'auto':
+        my_ip = __salt__['pillar.get']('_param:opencontrail_analytics_address')
+        url = 'http://' + my_ip+ ':' + str(api_port)
+    else:
+        url = 'http://' + api_host + ':' + str(api_port)
+
+    req = requests.get(url + '/analytics/uves/vrouter/' + vr_name + '?flat')
+    if int(req.status_code) != 200:
+        return "Could not fetch data from vrouter agent via %s.\nGot bad status code: %s\n%s" % (url, str(req.status_code), str(req.text))
+
+    return json.loads(req.text)
+
+
+def _get_object(json_obj, obj_path):
+
+    ''' Retrieve subelemet of an JSON object or value '''
+
+    if ':' in obj_path:
+        splitter = obj_path.split(':')
+        k = splitter[0]
+        v = ':'.join(splitter[1:])
+        if k.isdigit():
+            # Return specific element path
+            return [ _get_object(json_obj[int(k)], v) ]
+        elif k == '*':
+            l = []
+            for el in json_obj:
+                l.append(_get_object(el, v))
+            # Return all list elements from the path
+            return l
+        else:
+            # Contrail output may have nested JSON
+            if isinstance(json_obj, str) or isinstance(json_obj, unicode):
+                json_obj = json.loads(json_obj)
+            # Assume list. Return it
+            return { k: _get_object(json_obj[k], v) }
+    else:
+        return { obj_path: json_obj[obj_path] }
+
+
+def _deepmerge(o1, o2):
+
+    ''' Deep merge JSON objects '''
+
+    o3 = {}
+    if type(o1) == type(o2):
+        if type(o1) == dict or type(o1) == tuple:
+            for k in set(o1.keys() + o2.keys()):
+                if k in o1:
+                    if k in o2:
+                        o3[k] = _deepmerge(o1[k], o2[k])
+                    else:
+                        o3[k] = o1[k]
+                else:
+                    o3[k] = o2[k]
+        elif type(o1) == list or type(o1) == set:
+            o3 = [] + o2
+            for el in o3:
+                i = o3.index(el)
+                o3[i] = _deepmerge(o1[i], o2[i])
+        else:
+            o3 = o2
+    else:
+        o3 = o2
+
+    return o3
+
+
+def contrail_vrouter_agent_info(vr_name, filter_map=default_vrouter_info_map):
+
+    ''' Retrieve filtered contrail vrouter agent info from analyticsdb '''
+
+    vr_agent_status = contrail_collector_agent_status(vr_name)
+    vr_info = {}
+    for conf in filter_map:
+        vr_info[conf] = {}
+        for el_path in filter_map[conf]:
+            vr_info = _deepmerge(vr_info, { conf: _get_object(vr_agent_status[conf], el_path) } )
+
+    return vr_info
+
+
 def libvirt_capabilities():
 
     ''' JSON formatted libvirtcapabilities list '''