diff options
author | selias <samelias@cisco.com> | 2016-12-09 18:11:53 +0100 |
---|---|---|
committer | selias <samelias@cisco.com> | 2016-12-16 12:13:53 +0100 |
commit | 1813672eb9f6988046bc65167235ae37b088298c (patch) | |
tree | a6850005d93098cfe84bf13e0b40ddf702ecfbf4 /resources/libraries/python/honeycomb | |
parent | cf561a6e3d4c4fbd78ab6c9d0a9aa817bb3300fc (diff) |
CSIT-424: HC Test: JSON comparison function rework
When comparing multi-level JSON trees, the exceptions raised
carry more useful information.
Keys and values not present in reference data are ignored.
No longer puts chunks of honeycomb's log file into robot report. The
entire log file will be archived after https://gerrit.fd.io/r/4171
Change-Id: Ib597080fa3d9b2c43463c76ee0d52f317ea072e7
Signed-off-by: selias <samelias@cisco.com>
Diffstat (limited to 'resources/libraries/python/honeycomb')
-rw-r--r-- | resources/libraries/python/honeycomb/HcAPIKwInterfaces.py | 83 | ||||
-rw-r--r-- | resources/libraries/python/honeycomb/HoneycombUtil.py | 66 |
2 files changed, 61 insertions, 88 deletions
diff --git a/resources/libraries/python/honeycomb/HcAPIKwInterfaces.py b/resources/libraries/python/honeycomb/HcAPIKwInterfaces.py index 5658eea146..1e0883db27 100644 --- a/resources/libraries/python/honeycomb/HcAPIKwInterfaces.py +++ b/resources/libraries/python/honeycomb/HcAPIKwInterfaces.py @@ -1313,43 +1313,72 @@ class InterfaceKeywords(object): node, super_interface, path, None) @staticmethod - def compare_data_structures(data, ref, ignore=(), list_order=True): - """Checks if data obtained from UUT is as expected. + def compare_data_structures(data, ref, _path=''): + """Checks if data obtained from UUT is as expected. If it is not, + proceeds down the list/dictionary tree and finds the point of mismatch. :param data: Data to be checked. :param ref: Referential data used for comparison. - :param ignore: Dictionary keys to be ignored. - :param list_order: Whether to consider the order of list items\ - in comparison. + :param _path: Used in recursive calls, stores the path taken down + the JSON tree. :type data: dict :type ref: dict - :type ignore: iterable - :type list_order: bool + :type _path: str - :raises HoneycombError: If a parameter from referential data is not - present in operational data or if it has different value. + :raises HoneycombError: If the data structures do not match in some way, + or if they are not in deserialized JSON format. """ - errors = "" - - for key, item in ref.items(): - if key in ignore: - continue - try: - if data[key] != item: - if not list_order and sorted(data[key]) == sorted(item): - pass + if data == ref: + return True + + elif isinstance(data, dict) and isinstance(ref, dict): + for key in ref: + if key not in data: + raise HoneycombError( + "Key {key} is not present in path {path}. Keys in path:" + "{data_keys}".format( + key=key, + path=_path, + data_keys=data.keys())) + + if data[key] != ref[key]: + if isinstance(data[key], list) \ + or isinstance(data[key], dict): + InterfaceKeywords.compare_data_structures( + data[key], ref[key], + _path + '[{0}]'.format(key)) else: - errors += ("\nThe value of parameter '{0}' is " - "incorrect. It should be " - "'{1}' but it is '{2}'". - format(key, item, data[key])) - except KeyError: - errors += ("\nThe parameter '{0}' is not present in " - "operational data".format(key)) + raise HoneycombError( + "Data mismatch, key {key} in path {path} has value" + " {data}, but should be {ref}".format( + key=key, + path=_path, + data=data[key], + ref=ref[key])) + + elif isinstance(data, list) and isinstance(ref, list): + for item in ref: + if item not in data: + if isinstance(item, dict): + InterfaceKeywords.compare_data_structures( + data[0], item, + _path + '[{0}]'.format(ref.index(item))) + else: + raise HoneycombError( + "Data mismatch, list item {index} in path {path}" + " has value {data}, but should be {ref}".format( + index=ref.index(item), + path=_path, + data=data[0], + ref=item)) - if errors: - raise HoneycombError(errors) + else: + raise HoneycombError( + "Unexpected data type {data_type}, reference type is" + " {ref_type}. Must be list or dictionary.".format( + data_type=type(data), + ref_type=type(ref))) @staticmethod def compare_interface_lists(list1, list2): diff --git a/resources/libraries/python/honeycomb/HoneycombUtil.py b/resources/libraries/python/honeycomb/HoneycombUtil.py index 747c9dae8e..c290af9a6e 100644 --- a/resources/libraries/python/honeycomb/HoneycombUtil.py +++ b/resources/libraries/python/honeycomb/HoneycombUtil.py @@ -27,8 +27,7 @@ from enum import Enum, unique from robot.api import logger -from resources.libraries.python.ssh import SSH -from resources.libraries.python.HTTPRequest import HTTPRequest, HTTPCodes +from resources.libraries.python.HTTPRequest import HTTPRequest from resources.libraries.python.constants import Constants as Const @@ -302,10 +301,7 @@ class HoneycombUtil(object): base_path = HoneycombUtil.read_path_from_url_file(url_file) path = base_path + path status_code, resp = HTTPRequest.get(node, path) - response = loads(resp) - if status_code != HTTPCodes.OK: - HoneycombUtil.read_log_tail(node) - return status_code, response + return status_code, loads(resp) @staticmethod def put_honeycomb_data(node, url_file, data, path="", @@ -343,13 +339,9 @@ class HoneycombUtil(object): base_path = HoneycombUtil.read_path_from_url_file(url_file) path = base_path + path logger.trace(path) - (status_code, response) = HTTPRequest.put( + return HTTPRequest.put( node=node, path=path, headers=header, payload=data) - if status_code not in (HTTPCodes.OK, HTTPCodes.ACCEPTED): - HoneycombUtil.read_log_tail(node) - return status_code, response - @staticmethod def post_honeycomb_data(node, url_file, data=None, data_representation=DataRepresentation.JSON, @@ -383,13 +375,9 @@ class HoneycombUtil(object): data = dumps(data) path = HoneycombUtil.read_path_from_url_file(url_file) - (status_code, response) = HTTPRequest.post( + return HTTPRequest.post( node=node, path=path, headers=header, payload=data, timeout=timeout) - if status_code not in (HTTPCodes.OK, HTTPCodes.ACCEPTED): - HoneycombUtil.read_log_tail(node) - return status_code, response - @staticmethod def delete_honeycomb_data(node, url_file, path=""): """Delete data from Honeycomb according to given URL. @@ -407,48 +395,4 @@ class HoneycombUtil(object): base_path = HoneycombUtil.read_path_from_url_file(url_file) path = base_path + path - (status_code, response) = HTTPRequest.delete(node, path) - - if status_code != HTTPCodes.OK: - HoneycombUtil.read_log_tail(node) - return status_code, response - - @staticmethod - def read_log_tail(node, lines=120): - """Read the last N lines of the Honeycomb log file and print them - to robot log. - - :param node: Honeycomb node. - :param lines: Number of lines to read. - :type node: dict - :type lines: int - :returns: Last N log lines. - :rtype: str - """ - - logger.trace( - "HTTP request failed, " - "obtaining last {0} lines of Honeycomb log...".format(lines)) - - ssh = SSH() - ssh.connect(node) - cmd = "tail -n {0} /var/log/honeycomb/honeycomb.log".format(lines) - # ssh also logs the reply on trace level - (_, stdout, _) = ssh.exec_command(cmd, timeout=30) - - return stdout - - @staticmethod - def archive_honeycomb_log(node): - """Copy honeycomb log file from DUT node to VIRL for archiving. - - :param node: Honeycomb node. - :type node: dict - """ - - ssh = SSH() - ssh.connect(node) - - cmd = "cp /var/log/honeycomb/honeycomb.log /scratch/" - - ssh.exec_command_sudo(cmd) + return HTTPRequest.delete(node, path) |