aboutsummaryrefslogtreecommitdiffstats
path: root/resources/libraries/python
diff options
context:
space:
mode:
authorselias <samelias@cisco.com>2017-03-07 15:42:02 +0100
committerTibor Frank <tifrank@cisco.com>2017-03-21 12:00:46 +0000
commit4f4eaa1d52f3bdbe3caecdc1d6a024c369a2834a (patch)
tree44d1d559b187dbdf10eae12acc4da19affa1a5f4 /resources/libraries/python
parent8003aa2c3d1a0d4c1dbf3b6286d841a47b5fbb4c (diff)
CSIT-536 HC Test: support testing with ODL client
Reuses existing restconf test cases to test Honeycomb's netconf interface. When seding the requests to ODL's Honeycomb mountpoint, ODL translates these requests to netconf RPCs. Add new tag "honeycomb_odl" that excludes incompatible test cases. Change-Id: Ie293449da0129b02db85e30aa4f3266928d7d9f6 Signed-off-by: selias <samelias@cisco.com>
Diffstat (limited to 'resources/libraries/python')
-rw-r--r--resources/libraries/python/HTTPRequest.py33
-rw-r--r--resources/libraries/python/constants.py3
-rw-r--r--resources/libraries/python/honeycomb/HcAPIKwInterfaces.py2
-rw-r--r--resources/libraries/python/honeycomb/HcPersistence.py40
-rw-r--r--resources/libraries/python/honeycomb/HoneycombSetup.py138
5 files changed, 210 insertions, 6 deletions
diff --git a/resources/libraries/python/HTTPRequest.py b/resources/libraries/python/HTTPRequest.py
index 1f5df52c01..adf3d168eb 100644
--- a/resources/libraries/python/HTTPRequest.py
+++ b/resources/libraries/python/HTTPRequest.py
@@ -22,6 +22,7 @@ from enum import IntEnum, unique
from robot.api.deco import keyword
from robot.api import logger
+from robot.libraries.BuiltIn import BuiltIn
from requests import request, RequestException, Timeout, TooManyRedirects, \
HTTPError, ConnectionError
@@ -36,6 +37,7 @@ class HTTPCodes(IntEnum):
UNAUTHORIZED = 401
FORBIDDEN = 403
NOT_FOUND = 404
+ CONFLICT = 409
INTERNAL_SERVER_ERROR = 500
SERVICE_UNAVAILABLE = 503
@@ -167,8 +169,23 @@ class HTTPRequest(object):
5. there is any other unexpected HTTP request exception.
"""
timeout = kwargs["timeout"]
+
+ if BuiltIn().get_variable_value("${use_odl_client}"):
+ # TODO: node["honeycomb"]["odl_port"]
+ port = 8181
+ odl_url_part = "/network-topology:network-topology/topology/" \
+ "topology-netconf/node/vpp/yang-ext:mount"
+ else:
+ port = node["honeycomb"]["port"]
+ odl_url_part = ""
+
+ try:
+ path = path.format(odl_url_part=odl_url_part)
+ except KeyError:
+ pass
+
url = HTTPRequest.create_full_url(node['host'],
- node['honeycomb']['port'],
+ port,
path)
try:
auth = HTTPBasicAuth(node['honeycomb']['user'],
@@ -254,7 +271,8 @@ class HTTPRequest(object):
@staticmethod
@keyword(name="HTTP Post")
- def post(node, path, headers=None, payload=None, json=None, timeout=10):
+ def post(node, path, headers=None, payload=None, json=None, timeout=10,
+ enable_logging=True):
"""Sends a POST request and returns the response and status code.
:param node: Honeycomb node.
@@ -265,18 +283,23 @@ class HTTPRequest(object):
:param json: JSON formatted string to send in the body of the Request.
:param timeout: How long to wait for the server to send data before
giving up, as a float, or a (connect timeout, read timeout) tuple.
+ :param enable_logging: Used to suppress errors when checking ODL
+ state during suite setup and teardown. When True, logging is enabled,
+ otherwise logging is disabled.
:type node: dict
:type path: str
:type headers: dict
:type payload: dict, bytes, or file-like object
:type json: str
:type timeout: float or tuple
+ :type enable_logging: bool
:return: Status code and content of response.
:rtype: tuple
"""
- return HTTPRequest._http_request('POST', node, path, headers=headers,
- data=payload, json=json,
- timeout=timeout)
+ return HTTPRequest._http_request('POST', node, path,
+ enable_logging=enable_logging,
+ headers=headers, data=payload,
+ json=json, timeout=timeout)
@staticmethod
@keyword(name="HTTP Delete")
diff --git a/resources/libraries/python/constants.py b/resources/libraries/python/constants.py
index a280b51beb..4c3a8ff451 100644
--- a/resources/libraries/python/constants.py
+++ b/resources/libraries/python/constants.py
@@ -37,3 +37,6 @@ class Constants(object):
# Honeycomb templates location
RESOURCES_TPL_HC = 'resources/templates/honeycomb'
+
+ # ODL Client Restconf listener port
+ ODL_PORT = 8181
diff --git a/resources/libraries/python/honeycomb/HcAPIKwInterfaces.py b/resources/libraries/python/honeycomb/HcAPIKwInterfaces.py
index 3351ebf5d3..b4746e2118 100644
--- a/resources/libraries/python/honeycomb/HcAPIKwInterfaces.py
+++ b/resources/libraries/python/honeycomb/HcAPIKwInterfaces.py
@@ -1544,7 +1544,7 @@ class InterfaceKeywords(object):
"""
interface = intf.replace("/", "%2F")
- path = "/interface/{0}/pbb-rewrite/".format(interface)
+ path = "/interface/{0}/pbb-rewrite".format(interface)
status_code, resp = HcUtil. \
put_honeycomb_data(node, "config_vpp_interfaces", params, path,
data_representation=DataRepresentation.JSON)
diff --git a/resources/libraries/python/honeycomb/HcPersistence.py b/resources/libraries/python/honeycomb/HcPersistence.py
index c76a276098..83d756f9d3 100644
--- a/resources/libraries/python/honeycomb/HcPersistence.py
+++ b/resources/libraries/python/honeycomb/HcPersistence.py
@@ -102,3 +102,43 @@ class HcPersistence(object):
for command in commands:
(_, stdout, _) = ssh.exec_command_sudo(command)
logger.info(stdout)
+
+
+ @staticmethod
+ def configure_persistence(node, state):
+ """Enable or disable Honeycomb configuration data persistence.
+
+ :param node: Honeycomb node.
+ :param state: Enable or Disable.
+ :type node: dict
+ :type state: str
+ :raises ValueError: If the state argument is incorrect.
+ :raises HoneycombError: If the operation fails.
+ """
+
+ state = state.lower()
+ if state == "enable":
+ state = "true"
+ elif state == "disable":
+ state = "false"
+ else:
+ raise ValueError("Unexpected value of state argument:"
+ " {0} provided. Must be enable or disable."
+ .format(state))
+
+ for setting in ("persist-config", "persist-context"):
+ # find the setting, replace entire line with 'setting: state'
+ find = '\\"{setting}\\":'.format(setting=setting)
+ replace = '\\"{setting}\\": \\"{state}\\",'.format(
+ setting=setting, state=state)
+
+ argument = '"/{0}/c\\ {1}"'.format(find, replace)
+ path = "{0}/config/honeycomb.json".format(Const.REMOTE_HC_DIR)
+ command = "sed -i {0} {1}".format(argument, path)
+
+ ssh = SSH()
+ ssh.connect(node)
+ (ret_code, _, stderr) = ssh.exec_command_sudo(command)
+ if ret_code != 0:
+ raise HoneycombError("Failed to modify configuration on "
+ "node {0}, {1}".format(node, stderr))
diff --git a/resources/libraries/python/honeycomb/HoneycombSetup.py b/resources/libraries/python/honeycomb/HoneycombSetup.py
index 239ac10191..117b3e57c4 100644
--- a/resources/libraries/python/honeycomb/HoneycombSetup.py
+++ b/resources/libraries/python/honeycomb/HoneycombSetup.py
@@ -104,6 +104,36 @@ class HoneycombSetup(object):
format(errors))
@staticmethod
+ def restart_honeycomb_and_vpp_on_duts(*nodes):
+ """Restart the Honeycomb service on specified DUT nodes.
+
+ Use the keyword "Check Honeycomb Startup State" to check when Honeycomb
+ is fully restarted.
+ :param nodes: List of nodes to restart Honeycomb on.
+ :type nodes: list
+ :raises HoneycombError: If Honeycomb failed to restart.
+ """
+ logger.console("\nRestarting Honeycomb service ...")
+
+ cmd = "sudo service honeycomb restart && sudo service vpp restart"
+ errors = []
+
+ for node in nodes:
+ if node['type'] == NodeType.DUT:
+ ssh = SSH()
+ ssh.connect(node)
+ (ret_code, _, _) = ssh.exec_command_sudo(cmd)
+ if int(ret_code) != 0:
+ errors.append(node['host'])
+ else:
+ logger.info("Restart of Honeycomb and VPP on node {0} is "
+ "in progress ...".format(node['host']))
+ if errors:
+ raise HoneycombError('Node(s) {0} failed to restart Honeycomb'
+ ' and/or VPP.'.
+ format(errors))
+
+ @staticmethod
def check_honeycomb_startup_state(*nodes):
"""Check state of Honeycomb service during startup on specified nodes.
@@ -372,3 +402,111 @@ class HoneycombSetup(object):
if ret_code != 0:
raise HoneycombError("Failed to copy JVPP libraries on "
"node {0}, {1}".format(node, stderr))
+
+ @staticmethod
+ def find_odl_client(node):
+ """Check if there is a karaf directory in home.
+
+ :param node: Honeycomb node.
+ :type node: dict
+ :returns: True if ODL client is present on node, else False.
+ :rtype: bool
+ """
+
+ ssh = SSH()
+ ssh.connect(node)
+ (ret_code, stdout, _) = ssh.exec_command_sudo(
+ "ls ~ | grep karaf")
+
+ logger.debug(stdout)
+ return not bool(ret_code)
+
+ @staticmethod
+ def start_odl_client(node):
+ """Start ODL client on the specified node.
+
+ karaf should be located in home directory, and VPP and Honeycomb should
+ already be running, otherwise the start will fail.
+ :param node: Nodes to start ODL client on.
+ :type node: dict
+ :raises HoneycombError: If Honeycomb fails to start.
+ """
+
+ logger.console("\nStarting ODL client ...")
+
+ cmd = "~/*karaf*/bin/start"
+
+ ssh = SSH()
+ ssh.connect(node)
+ (ret_code, _, _) = ssh.exec_command_sudo(cmd)
+ if int(ret_code) != 0:
+ raise HoneycombError('Node {0} failed to start ODL.'.
+ format(node['host']))
+ else:
+ logger.info("Starting the ODL client on node {0} is "
+ "in progress ...".format(node['host']))
+
+ @staticmethod
+ def check_odl_startup_state(node):
+ """Check the status of ODL client startup.
+
+ :param node: Honeycomb node.
+ :param node: dict
+ :returns: True when ODL is started.
+ :rtype: bool
+ :raises HoneycombError: When the response is not code 200: OK.
+ """
+
+ path = HcUtil.read_path_from_url_file(
+ "odl_client/odl_netconf_connector")
+ expected_status_codes = (HTTPCodes.UNAUTHORIZED,
+ HTTPCodes.FORBIDDEN,
+ HTTPCodes.NOT_FOUND,
+ HTTPCodes.SERVICE_UNAVAILABLE,
+ HTTPCodes.INTERNAL_SERVER_ERROR)
+
+ status_code, _ = HTTPRequest.get(node, path, timeout=10,
+ enable_logging=False)
+ if status_code == HTTPCodes.OK:
+ logger.info("ODL client on node {0} is up and running".
+ format(node['host']))
+ elif status_code in expected_status_codes:
+ if status_code == HTTPCodes.UNAUTHORIZED:
+ logger.info('Unauthorized. If this triggers keyword '
+ 'timeout, verify username and password.')
+ raise HoneycombError('ODL client 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 mount_honeycomb_on_odl(node):
+ """Tell ODL client to mount Honeycomb instance over netconf.
+
+ :param node: Honeycomb node.
+ :type node: dict
+ :raises HoneycombError: When the response is not code 200: OK.
+ """
+
+ path = HcUtil.read_path_from_url_file(
+ "odl_client/odl_netconf_connector")
+
+ url_file = "{0}/{1}".format(Const.RESOURCES_TPL_HC,
+ "odl_client/mount_honeycomb.xml")
+
+ with open(url_file) as template:
+ data = template.read()
+
+ status_code, _ = HTTPRequest.post(
+ node, path, headers={"Content-Type": "application/xml"},
+ payload=data, timeout=10, enable_logging=False)
+
+ if status_code == HTTPCodes.OK:
+ logger.info("ODL mount point configured successfully.")
+ elif status_code == HTTPCodes.CONFLICT:
+ logger.warn("ODL mount point was already configured.")
+ else:
+ raise HoneycombError('Mount point configuration not successful')