diff options
author | selias <samuel.elias@pantheon.tech> | 2016-03-21 13:06:49 +0100 |
---|---|---|
committer | Gerrit Code Review <gerrit@fd.io> | 2016-04-05 14:09:02 +0000 |
commit | a1383e569b184808780fbe0405402dea584902a9 (patch) | |
tree | 622a2bc62acc4df38b2993edc6082fd4c02ab139 /resources/libraries/python/HoneycombSetup.py | |
parent | 9551ac86e80d4a3ef779d556ecdc8d2afffae32b (diff) |
Setup and check honeycomb on all DUTs
- methods implementing HTTP requests (PUT,GET,POST,DELETE)
- methods for parsing HTTP responses
- methods for honeycomb setup on DUT
- updated constants.py
- keywords for honeycomb setup and communication
- simple honeycomb sanity test (not enabled for jenkins job runs)
Change-Id: I589f0ca56cc01072b92fe9363aed16a4098aee40
Signed-off-by: selias <samuel.elias@pantheon.tech>
Diffstat (limited to 'resources/libraries/python/HoneycombSetup.py')
-rw-r--r-- | resources/libraries/python/HoneycombSetup.py | 301 |
1 files changed, 301 insertions, 0 deletions
diff --git a/resources/libraries/python/HoneycombSetup.py b/resources/libraries/python/HoneycombSetup.py new file mode 100644 index 0000000000..de05eff6ed --- /dev/null +++ b/resources/libraries/python/HoneycombSetup.py @@ -0,0 +1,301 @@ +# Copyright (c) 2016 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: +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +"""Implements keywords for Honeycomb setup.""" + +import os.path +from xml.etree import ElementTree as ET + +from robot.api import logger + +from resources.libraries.python.topology import NodeType +from resources.libraries.python.ssh import SSH +from resources.libraries.python.HTTPRequest import HTTPRequest, \ + HTTPRequestError, HTTP_CODES +from resources.libraries.python.constants import Constants as C + + +class HoneycombError(Exception): + """Exception(s) raised by methods working with Honeycomb.""" + + def __init__(self, msg, enable_logging=True): + """Sets the exception message and enables / disables logging + + It is not wanted to log errors when using these keywords together + with keywords like "Wait until keyword succeeds". + + :param msg: Message to be displayed and logged + :param enable_logging: When True, logging is enabled, otherwise + logging is disabled. + :type msg: str + :type enable_logging: bool + """ + super(HoneycombError, self).__init__() + self._msg = msg + self._repr_msg = self.__module__ + '.' + \ + self.__class__.__name__ + ": " + self._msg + if enable_logging: + logger.error(self._msg) + logger.debug(self._repr_msg) + + def __repr__(self): + return repr(self._repr_msg) + + def __str__(self): + return str(self._repr_msg) + + +class HoneycombSetup(object): + """Implements keywords for Honeycomb setup.""" + + def __init__(self): + pass + + @staticmethod + def start_honeycomb_on_all_duts(nodes): + """Start honeycomb on all DUT nodes in topology. + + :param nodes: all nodes in topology + :type nodes: dict + """ + logger.console("Starting honeycomb service") + + for node in nodes.values(): + if node['type'] == NodeType.DUT: + HoneycombSetup.start_honeycomb(node) + + @staticmethod + def start_honeycomb(node): + """Start up honeycomb on DUT node. + + :param node: DUT node with honeycomb + :type node: dict + :return: ret_code, stdout, stderr + :rtype: tuple + :raises HoneycombError: if Honeycomb fails to start. + """ + + ssh = SSH() + ssh.connect(node) + cmd = os.path.join(C.REMOTE_HC_DIR, "start") + (ret_code, stdout, stderr) = ssh.exec_command_sudo(cmd) + if int(ret_code) != 0: + logger.debug('stdout: {0}'.format(stdout)) + logger.debug('stderr: {0}'.format(stderr)) + raise HoneycombError('Node {0} failed to start honeycomb'. + format(node['host'])) + return ret_code, stdout, stderr + + @staticmethod + def stop_honeycomb_on_all_duts(nodes): + """Stop the honeycomb service on all DUTs. + + :param nodes: nodes in topology + :type nodes: dict + :return: ret_code, stdout, stderr + :rtype: tuple + :raises HoneycombError: if Honeycomb failed to stop. + """ + logger.console("Shutting down honeycomb service") + errors = [] + for node in nodes.values(): + if node['type'] == NodeType.DUT: + + ssh = SSH() + ssh.connect(node) + cmd = os.path.join(C.REMOTE_HC_DIR, "stop") + (ret_code, stdout, stderr) = ssh.exec_command_sudo(cmd) + if int(ret_code) != 0: + logger.debug('stdout: {0}'.format(stdout)) + logger.debug('stderr: {0}'.format(stderr)) + errors.append(node['host']) + continue + logger.info("Honeycomb was successfully stopped on node {0}.". + format(node['host'])) + if errors: + raise HoneycombError('Node(s) {0} failed to stop honeycomb.'. + format(errors)) + + @staticmethod + def check_honeycomb_startup_state(nodes): + """Check state of honeycomb service during startup. + + Reads html path from template file vpp_version.url + + Honeycomb node replies with connection refused or the following status + codes depending on startup progress: codes 200, 401, 403, 404, 503 + + :param nodes: nodes in topology + :type nodes: dict + :return: True if all GETs returned code 200(OK) + :rtype bool + """ + + url_file = os.path.join(C.RESOURCES_TPL_HC, "vpp_version.url") + with open(url_file) as template: + data = template.readline() + + expected_status_codes = (HTTP_CODES["UNAUTHORIZED"], + HTTP_CODES["FORBIDDEN"], + HTTP_CODES["NOT_FOUND"], + HTTP_CODES["SERVICE_UNAVAILABLE"]) + + for node in nodes.values(): + if node['type'] == NodeType.DUT: + status_code, _ = HTTPRequest.get(node, data, timeout=10, + enable_logging=False) + if status_code == HTTP_CODES["OK"]: + pass + elif status_code in expected_status_codes: + if status_code == HTTP_CODES["UNAUTHORIZED"]: + logger.info('Unauthorized. If this triggers keyword ' + 'timeout, verify honeycomb ' + 'username and password') + raise HoneycombError('Honeycomb on node {0} running but ' + 'not yet ready.'.format(node['host']), + enable_logging=False) + else: + raise HoneycombError('Unexpected return code: {0}'. + format(status_code)) + return True + + @staticmethod + def check_honeycomb_shutdown_state(nodes): + """Check state of honeycomb service during shutdown. + + Honeycomb node replies with connection refused or the following status + codes depending on shutdown progress: codes 200, 404 + + :param nodes: nodes in topology + :type nodes: dict + :return: True if all GETs fail to connect + :rtype bool + """ + + for node in nodes.values(): + if node['type'] == NodeType.DUT: + try: + status_code, _ = HTTPRequest.get(node, '/index.html', + timeout=5, + enable_logging=False) + if status_code == HTTP_CODES["OK"]: + raise HoneycombError('Honeycomb on node {0} is still ' + 'running'.format(node['host']), + enable_logging=False) + elif status_code == HTTP_CODES["NOT_FOUND"]: + raise HoneycombError('Honeycomb on node {0} is shutting' + ' down'.format(node['host']), + enable_logging=False) + else: + raise HoneycombError('Unexpected return code: {' + '0}'.format(status_code)) + except HTTPRequestError: + logger.debug('Connection refused') + + return True + + + @staticmethod + def add_vpp_to_honeycomb_network_topology(nodes, headers): + """Add vpp node to Honeycomb network topology. + + :param nodes: all nodes in test topology + :param headers: headers to be used with PUT requests + :type nodes: dict + :type headers: dict + :return: status code and response from PUT requests + :rtype: tuple + :raises HoneycombError: if a node was not added to honeycomb topology + + Reads HTML path from template file config_topology_node.url + Path to the node to be added, e.g.: + ("/restconf/config/network-topology:network-topology" + "/topology/topology-netconf/node/") + There must be "/" at the end, as generated node name is added + at the end. + + Reads payload data from template file add_vpp_to_topology.xml + Information about node as XML structure, e.g.: + <node xmlns="urn:TBD:params:xml:ns:yang:network-topology"> + <node-id> + {vpp_host} + </node-id> + <host xmlns="urn:opendaylight:netconf-node-topology"> + {vpp_ip} + </host> + <port xmlns="urn:opendaylight:netconf-node-topology"> + {vpp_port} + </port> + <username xmlns="urn:opendaylight:netconf-node-topology"> + {user} + </username> + <password xmlns="urn:opendaylight:netconf-node-topology"> + {passwd} + </password> + <tcp-only xmlns="urn:opendaylight:netconf-node-topology"> + false + </tcp-only> + <keepalive-delay xmlns="urn:opendaylight:netconf-node-topology"> + 0 + </keepalive-delay> + </node> + NOTE: The placeholders: + {vpp_host} + {vpp_ip} + {vpp_port} + {user} + {passwd} + MUST be there as they are replaced by correct values. + """ + + with open(os.path.join(C.RESOURCES_TPL_HC, "config_topology_node.url"))\ + as template: + path = template.readline() + + try: + xml_data = ET.parse(os.path.join(C.RESOURCES_TPL_HC, + "add_vpp_to_topology.xml")) + except ET.ParseError as err: + raise HoneycombError(repr(err)) + data = ET.tostring(xml_data.getroot()) + + status_codes = [] + responses = [] + for node_name, node in nodes.items(): + if node['type'] == NodeType.DUT: + try: + payload = data.format( + vpp_host=node_name, + vpp_ip=node["host"], + vpp_port=node['honeycomb']["netconf_port"], + user=node['honeycomb']["user"], + passwd=node['honeycomb']["passwd"]) + status_code, resp = HTTPRequest.put( + node=node, + path=path + '/' + node_name, + headers=headers, + payload=payload) + if status_code != HTTP_CODES["OK"]: + raise HoneycombError( + "VPP {0} was not added to topology. " + "Status code: {1}".format(node["host"], + status_code)) + + status_codes.append(status_code) + responses.append(resp) + + except HTTPRequestError as err: + raise HoneycombError("VPP {0} was not added to topology.\n" + "{1}".format(node["host"], repr(err))) + + return status_codes, responses |