X-Git-Url: https://gerrit.fd.io/r/gitweb?p=csit.git;a=blobdiff_plain;f=resources%2Flibraries%2Fpython%2Fhoneycomb%2FHoneycombUtil.py;h=59483f4672daefc861594bd356185d3a92db7922;hp=2f8392ecb5648c24734c3be3906971d64b48e53d;hb=67f247b4015b76641c6fab7c49e7d9862043c856;hpb=036f36181c177e4c77f22cdbdcb87fb2f94df3bf diff --git a/resources/libraries/python/honeycomb/HoneycombUtil.py b/resources/libraries/python/honeycomb/HoneycombUtil.py index 2f8392ecb5..59483f4672 100644 --- a/resources/libraries/python/honeycomb/HoneycombUtil.py +++ b/resources/libraries/python/honeycomb/HoneycombUtil.py @@ -1,4 +1,4 @@ -# Copyright (c) 2016 Cisco and/or its affiliates. +# Copyright (c) 2018 Cisco and/or its affiliates. # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at: @@ -27,8 +27,9 @@ from enum import Enum, unique from robot.api import logger +from resources.libraries.python.ssh import SSH from resources.libraries.python.HTTPRequest import HTTPRequest -from resources.libraries.python.constants import Constants as Const +from resources.libraries.python.Constants import Constants as Const @unique @@ -61,12 +62,14 @@ class HoneycombError(Exception): When raising this exception, put this information to the message in this order: + - short description of the encountered problem (parameter msg), - relevant messages if there are any collected, e.g., from caught exception (optional parameter details), - relevant data if there are any collected (optional parameter details). - The logging is performed on two levels: 1. error - short description of the - problem; 2. debug - detailed information. + + The logging is performed on two levels: 1. error - short description of the + problem; 2. debug - detailed information. """ def __init__(self, msg, details='', enable_logging=True): @@ -78,7 +81,7 @@ class HoneycombError(Exception): :param msg: Message to be displayed and logged. :param enable_logging: When True, logging is enabled, otherwise - logging is disabled. + logging is disabled. :type msg: str :type enable_logging: bool """ @@ -86,7 +89,6 @@ class HoneycombError(Exception): self._msg = "{0}: {1}".format(self.__class__.__name__, msg) self._details = details if enable_logging: - logger.error(self._msg) logger.debug(self._details) def __repr__(self): @@ -109,10 +111,11 @@ class HoneycombUtil(object): structure. There are also two supportive methods implemented: - - read_path_from_url_file which reads URL file and returns a path (see - docs/honeycomb_url_files.rst). - - parse_json_response which parses data from response in JSON representation - according to given path. + + - read_path_from_url_file which reads URL file and returns a path (see + docs/honeycomb_url_files.rst). + - parse_json_response which parses data from response in JSON + representation according to given path. """ def __init__(self): @@ -120,13 +123,14 @@ class HoneycombUtil(object): @staticmethod def read_path_from_url_file(url_file): - """Read path from *.url file. + """Read path from .url file. + + For more information about .url file see docs/honeycomb_url_files.rst - For more information about *.url file see docs/honeycomb_url_files.rst :param url_file: URL file. The argument contains only the name of file - without extension, not the full path. + without extension, not the full path. :type url_file: str - :return: Requested path. + :returns: Requested path. :rtype: str """ @@ -143,42 +147,45 @@ class HoneycombUtil(object): Path format: The path is a tuple with items navigating to requested data. The items can be strings or tuples: - - string item represents a dictionary key in data, - - tuple item represents list item in data. - - Example: - data = \ - { - "interfaces": { - "interface": [ - { - "name": "GigabitEthernet0/8/0", - "enabled": "true", - "type": "iana-if-type:ethernetCsmacd", - }, - { - "name": "local0", - "enabled": "false", - "type": "iana-if-type:ethernetCsmacd", - } - ] - } - } + + - string item represents a dictionary key in data, + - tuple item represents list item in data. + + Example:: + + data = \ + { + "interfaces": { + "interface": [ + { + "name": "GigabitEthernet0/8/0", + "enabled": "true", + "type": "iana-if-type:ethernetCsmacd", + }, + { + "name": "local0", + "enabled": "false", + "type": "iana-if-type:ethernetCsmacd", + } + ] + } + } path = ("interfaces", ("interface", "name", "local0"), "enabled") This path points to "false". - The tuple ("interface", "name", "local0") consists of: - index 0 - dictionary key pointing to a list, - index 1 - key which identifies an item in the list, it is also marked as - the key in corresponding yang file. - index 2 - key value. + The tuple ("interface", "name", "local0") consists of:: + + index 0 - dictionary key pointing to a list, + index 1 - key which identifies an item in the list, it is also marked + as the key in corresponding yang file. + index 2 - key value. :param data: Data received from Honeycomb REST API. :param path: Path to data we want to find. :type data: dict :type path: tuple - :return: Data represented by path. + :returns: Data represented by path. :rtype: str, dict, or list :raises HoneycombError: If the data has not been found. """ @@ -205,7 +212,7 @@ class HoneycombUtil(object): :param path: Path to data we want to remove. :type data: dict :type path: tuple - :return: Original data without removed part. + :returns: Original data without removed part. :rtype: dict """ @@ -245,7 +252,7 @@ class HoneycombUtil(object): :type data: dict :type path: tuple :type new_value: str, dict or list - :return: Original data with the new value. + :returns: Original data with the new value. :rtype: dict """ @@ -285,41 +292,53 @@ class HoneycombUtil(object): return origin_data @staticmethod - def get_honeycomb_data(node, url_file): + def get_honeycomb_data(node, url_file, path=""): """Retrieve data from Honeycomb according to given URL. :param node: Honeycomb node. :param url_file: URL file. The argument contains only the name of file - without extension, not the full path. + without extension, not the full path. + :param path: Path which is added to the base path to identify the data. :type node: dict :type url_file: str - :return: Status code and content of response. - :rtype tuple + :type path: str + :returns: Status code and content of response. + :rtype: tuple """ - path = HoneycombUtil.read_path_from_url_file(url_file) + base_path = HoneycombUtil.read_path_from_url_file(url_file) + path = base_path + path status_code, resp = HTTPRequest.get(node, path) - return status_code, loads(resp) + + try: + data = loads(resp) + except ValueError: + logger.debug("Failed to deserialize JSON data.") + data = None + + return status_code, data @staticmethod - def put_honeycomb_data(node, url_file, data, + def put_honeycomb_data(node, url_file, data, path="", data_representation=DataRepresentation.JSON): """Send configuration data using PUT request and return the status code and response content. :param node: Honeycomb node. :param url_file: URL file. The argument contains only the name of file - without extension, not the full path. + without extension, not the full path. :param data: Configuration data to be sent to Honeycomb. + :param path: Path which is added to the base path to identify the data. :param data_representation: How the data is represented. :type node: dict :type url_file: str :type data: dict, str + :type path: str :type data_representation: DataRepresentation - :return: Status code and content of response. + :returns: Status code and content of response. :rtype: tuple :raises HoneycombError: If the given data representation is not defined - in HEADERS. + in HEADERS. """ try: @@ -332,9 +351,11 @@ class HoneycombUtil(object): logger.trace(data) - path = HoneycombUtil.read_path_from_url_file(url_file) - return HTTPRequest.put(node=node, path=path, headers=header, - payload=data) + base_path = HoneycombUtil.read_path_from_url_file(url_file) + path = base_path + path + logger.trace(path) + return HTTPRequest.put( + node=node, path=path, headers=header, payload=data) @staticmethod def post_honeycomb_data(node, url_file, data=None, @@ -344,20 +365,20 @@ class HoneycombUtil(object): :param node: Honeycomb node. :param url_file: URL file. The argument contains only the name of file - without extension, not the full path. + without extension, not the full path. :param data: Configuration data to be sent to Honeycomb. :param data_representation: How the data is represented. :param timeout: How long to wait for the server to send data before - giving up. + giving up. :type node: dict :type url_file: str :type data: dict, str :type data_representation: DataRepresentation :type timeout: int - :return: Status code and content of response. + :returns: Status code and content of response. :rtype: tuple :raises HoneycombError: If the given data representation is not defined - in HEADERS. + in HEADERS. """ try: @@ -369,21 +390,123 @@ class HoneycombUtil(object): data = dumps(data) path = HoneycombUtil.read_path_from_url_file(url_file) - return HTTPRequest.post(node=node, path=path, headers=header, - payload=data, timeout=timeout) + return HTTPRequest.post( + node=node, path=path, headers=header, payload=data, timeout=timeout) @staticmethod - def delete_honeycomb_data(node, url_file): + def delete_honeycomb_data(node, url_file, path=""): """Delete data from Honeycomb according to given URL. :param node: Honeycomb node. :param url_file: URL file. The argument contains only the name of file - without extension, not the full path. + without extension, not the full path. + :param path: Path which is added to the base path to identify the data. :type node: dict :type url_file: str - :return: Status code and content of response. - :rtype tuple + :type path: str + :returns: Status code and content of response. + :rtype: tuple """ - path = HoneycombUtil.read_path_from_url_file(url_file) + base_path = HoneycombUtil.read_path_from_url_file(url_file) + path = base_path + path return HTTPRequest.delete(node, path) + + @staticmethod + def append_honeycomb_log(node, suite_name): + """Append Honeycomb log for the current test suite to the full log. + + :param node: Honeycomb node. + :param suite_name: Name of the current test suite. ${SUITE_NAME} + variable in robotframework. + :type node: dict + :type suite_name: str + """ + + ssh = SSH() + ssh.connect(node) + + ssh.exec_command( + "echo '{separator}' >> /tmp/honeycomb.log".format(separator="="*80)) + ssh.exec_command( + "echo 'Log for suite: {suite}' >> /tmp/honeycomb.log".format( + suite=suite_name)) + ssh.exec_command( + "cat {hc_log} >> /tmp/honeycomb.log".format( + hc_log=Const.REMOTE_HC_LOG)) + + @staticmethod + def append_odl_log(node, odl_name, suite_name): + """Append ODL karaf log for the current test suite to the full log. + + :param node: Honeycomb node. + :param odl_name: Name of ODL client version to use. + :param suite_name: Name of the current test suite. ${SUITE_NAME} + variable in robotframework. + :type node: dict + :type odl_name: str + :type suite_name: str + """ + + ssh = SSH() + ssh.connect(node) + + ssh.exec_command( + "echo '{separator}' >> /tmp/karaf.log".format(separator="="*80)) + ssh.exec_command( + "echo 'Log for suite: {suite}' >> /tmp/karaf.log".format( + suite=suite_name)) + ssh.exec_command( + "cat /tmp/karaf_{odl_name}/data/log/karaf.log >> /tmp/karaf.log" + .format(odl_name=odl_name)) + + @staticmethod + def clear_honeycomb_log(node): + """Delete the Honeycomb log file for the current test suite. + + :param node: Honeycomb node. + :type node: dict + """ + + ssh = SSH() + ssh.connect(node) + + ssh.exec_command("sudo rm {hc_log}".format(hc_log=Const.REMOTE_HC_LOG)) + + @staticmethod + def archive_honeycomb_log(node, perf=False): + """Copy honeycomb log file from DUT node to VIRL for archiving. + + :param node: Honeycomb node. + :param perf: Alternate handling, for use with performance test topology. + :type node: dict + :type perf: bool + """ + + ssh = SSH() + ssh.connect(node) + + if not perf: + cmd = "cp /tmp/honeycomb.log /scratch/" + ssh.exec_command_sudo(cmd, timeout=60) + else: + ssh.scp( + ".", + "/tmp/honeycomb.log", + get=True, + timeout=60) + ssh.exec_command("rm /tmp/honeycomb.log") + + @staticmethod + def archive_odl_log(node): + """Copy ODL karaf log file from DUT node to VIRL for archiving. + + :param node: Honeycomb node. + :type node: dict + """ + + ssh = SSH() + ssh.connect(node) + + cmd = "cp /tmp/karaf.log /scratch/" + ssh.exec_command_sudo(cmd, timeout=60)