aboutsummaryrefslogtreecommitdiffstats
path: root/resources/libraries/python/HoneycombSetup.py
diff options
context:
space:
mode:
authorselias <samuel.elias@pantheon.tech>2016-03-21 13:06:49 +0100
committerGerrit Code Review <gerrit@fd.io>2016-04-05 14:09:02 +0000
commita1383e569b184808780fbe0405402dea584902a9 (patch)
tree622a2bc62acc4df38b2993edc6082fd4c02ab139 /resources/libraries/python/HoneycombSetup.py
parent9551ac86e80d4a3ef779d556ecdc8d2afffae32b (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.py301
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