1 # Copyright (c) 2016 Cisco and/or its affiliates.
2 # Licensed under the Apache License, Version 2.0 (the "License");
3 # you may not use this file except in compliance with the License.
4 # You may obtain a copy of the License at:
6 # http://www.apache.org/licenses/LICENSE-2.0
8 # Unless required by applicable law or agreed to in writing, software
9 # distributed under the License is distributed on an "AS IS" BASIS,
10 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
11 # See the License for the specific language governing permissions and
12 # limitations under the License.
14 """Implementation of keywords for Honeycomb setup."""
16 from robot.api import logger
18 from resources.libraries.python.HTTPRequest import HTTPRequest, HTTPCodes, \
20 from resources.libraries.python.constants import Constants as Const
21 from resources.libraries.python.honeycomb.HoneycombUtil import HoneycombError
22 from resources.libraries.python.honeycomb.HoneycombUtil \
23 import HoneycombUtil as HcUtil
24 from resources.libraries.python.ssh import SSH
25 from resources.libraries.python.topology import NodeType
28 class HoneycombSetup(object):
29 """Implements keywords for Honeycomb setup.
31 The keywords implemented in this class make possible to:
34 - check the Honeycomb start-up state,
35 - check the Honeycomb shutdown state,
36 - add VPP to the topology.
43 def start_honeycomb_on_duts(*nodes):
44 """Start Honeycomb on specified DUT nodes.
46 This keyword starts the Honeycomb service on specified DUTs.
47 The keyword just starts the Honeycomb and does not check its startup
48 state. Use the keyword "Check Honeycomb Startup State" to check if the
49 Honeycomb is up and running.
50 Honeycomb must be installed in "/opt" directory, otherwise the start
52 :param nodes: List of nodes to start Honeycomb on.
54 :raises HoneycombError: If Honeycomb fails to start.
57 HoneycombSetup.print_environment(nodes)
59 logger.console("\nStarting Honeycomb service ...")
61 cmd = "sudo service honeycomb start"
64 if node['type'] == NodeType.DUT:
67 (ret_code, _, _) = ssh.exec_command_sudo(cmd)
68 if int(ret_code) != 0:
69 raise HoneycombError('Node {0} failed to start Honeycomb.'.
72 logger.info("Starting the Honeycomb service on node {0} is "
73 "in progress ...".format(node['host']))
76 def stop_honeycomb_on_duts(*nodes):
77 """Stop the Honeycomb service on specified DUT nodes.
79 This keyword stops the Honeycomb service on specified nodes. It just
80 stops the Honeycomb and does not check its shutdown state. Use the
81 keyword "Check Honeycomb Shutdown State" to check if Honeycomb has
83 :param nodes: List of nodes to stop Honeycomb on.
85 :raises HoneycombError: If Honeycomb failed to stop.
87 logger.console("\nShutting down Honeycomb service ...")
89 cmd = "sudo service honeycomb stop"
93 if node['type'] == NodeType.DUT:
96 (ret_code, _, _) = ssh.exec_command_sudo(cmd)
97 if int(ret_code) != 0:
98 errors.append(node['host'])
100 logger.info("Stopping the Honeycomb service on node {0} is "
101 "in progress ...".format(node['host']))
103 raise HoneycombError('Node(s) {0} failed to stop Honeycomb.'.
107 def restart_honeycomb_and_vpp_on_duts(*nodes):
108 """Restart the Honeycomb service on specified DUT nodes.
110 Use the keyword "Check Honeycomb Startup State" to check when Honeycomb
112 :param nodes: List of nodes to restart Honeycomb on.
114 :raises HoneycombError: If Honeycomb failed to restart.
116 logger.console("\nRestarting Honeycomb service ...")
118 cmd = "sudo service honeycomb restart && sudo service vpp restart"
122 if node['type'] == NodeType.DUT:
125 (ret_code, _, _) = ssh.exec_command_sudo(cmd)
126 if int(ret_code) != 0:
127 errors.append(node['host'])
129 logger.info("Restart of Honeycomb and VPP on node {0} is "
130 "in progress ...".format(node['host']))
132 raise HoneycombError('Node(s) {0} failed to restart Honeycomb'
137 def check_honeycomb_startup_state(*nodes):
138 """Check state of Honeycomb service during startup on specified nodes.
140 Reads html path from template file oper_vpp_version.url.
142 Honeycomb nodes reply with connection refused or the following status
143 codes depending on startup progress: codes 200, 401, 403, 404, 500, 503
145 :param nodes: List of DUT nodes starting Honeycomb.
147 :return: True if all GETs returned code 200(OK).
150 path = HcUtil.read_path_from_url_file("oper_vpp_version")
151 expected_status_codes = (HTTPCodes.UNAUTHORIZED,
154 HTTPCodes.SERVICE_UNAVAILABLE,
155 HTTPCodes.INTERNAL_SERVER_ERROR)
158 if node['type'] == NodeType.DUT:
159 HoneycombSetup.print_ports(node)
160 status_code, _ = HTTPRequest.get(node, path, timeout=10,
161 enable_logging=False)
162 if status_code == HTTPCodes.OK:
163 logger.info("Honeycomb on node {0} is up and running".
164 format(node['host']))
165 elif status_code in expected_status_codes:
166 if status_code == HTTPCodes.UNAUTHORIZED:
167 logger.info('Unauthorized. If this triggers keyword '
168 'timeout, verify Honeycomb username and '
170 raise HoneycombError('Honeycomb on node {0} running but '
171 'not yet ready.'.format(node['host']),
172 enable_logging=False)
174 raise HoneycombError('Unexpected return code: {0}.'.
177 status_code, _ = HcUtil.get_honeycomb_data(
178 node, "config_vpp_interfaces")
179 if status_code != HTTPCodes.OK:
180 raise HoneycombError('Honeycomb on node {0} running but '
181 'not yet ready.'.format(node['host']),
182 enable_logging=False)
186 def check_honeycomb_shutdown_state(*nodes):
187 """Check state of Honeycomb service during shutdown on specified nodes.
189 Honeycomb nodes reply with connection refused or the following status
190 codes depending on shutdown progress: codes 200, 404.
192 :param nodes: List of DUT nodes stopping Honeycomb.
194 :return: True if all GETs fail to connect.
197 cmd = "ps -ef | grep -v grep | grep honeycomb"
199 if node['type'] == NodeType.DUT:
201 status_code, _ = HTTPRequest.get(node, '/index.html',
203 enable_logging=False)
204 if status_code == HTTPCodes.OK:
205 raise HoneycombError('Honeycomb on node {0} is still '
206 'running.'.format(node['host']),
207 enable_logging=False)
208 elif status_code == HTTPCodes.NOT_FOUND:
209 raise HoneycombError('Honeycomb on node {0} is shutting'
210 ' down.'.format(node['host']),
211 enable_logging=False)
213 raise HoneycombError('Unexpected return code: {0}.'.
215 except HTTPRequestError:
216 logger.debug('Connection refused, checking the process '
220 (ret_code, _, _) = ssh.exec_command_sudo(cmd)
222 raise HoneycombError('Honeycomb on node {0} is still '
223 'running.'.format(node['host']),
224 enable_logging=False)
226 logger.info("Honeycomb on node {0} has stopped".
227 format(node['host']))
231 def configure_unsecured_access(*nodes):
232 """Configure Honeycomb to allow restconf requests through insecure HTTP
233 used by tests. By default this is only allowed for localhost.
235 :param nodes: All nodes in test topology.
237 :raises HoneycombError: If the configuration could not be changed.
239 # TODO: Modify tests to use HTTPS instead.
241 find = "restconf-binding-address"
242 replace = '\\"restconf-binding-address\\": \\"0.0.0.0\\",'
244 argument = '"/{0}/c\\ {1}"'.format(find, replace)
245 path = "{0}/config/honeycomb.json".format(Const.REMOTE_HC_DIR)
246 command = "sed -i {0} {1}".format(argument, path)
250 if node['type'] == NodeType.DUT:
252 (ret_code, _, stderr) = ssh.exec_command_sudo(command)
254 raise HoneycombError("Failed to modify configuration on "
255 "node {0}, {1}".format(node, stderr))
258 def print_environment(nodes):
259 """Print information about the nodes to log. The information is defined
260 by commands in cmds tuple at the beginning of this method.
262 :param nodes: List of DUT nodes to get information about.
266 # TODO: When everything is set and running in VIRL env, transform this
267 # method to a keyword checking the environment.
275 "dpkg --list | grep openjdk",
276 "ls -la /opt/honeycomb")
279 if node['type'] == NodeType.DUT:
280 logger.info("Checking node {} ...".format(node['host']))
282 logger.info("Command: {}".format(cmd))
285 ssh.exec_command_sudo(cmd)
288 def print_ports(node):
289 """Uses "sudo netstat -anp | grep java" to print port where a java
292 :param node: Honeycomb node where we want to print the ports.
296 cmds = ("netstat -anp | grep java",
297 "ps -ef | grep [h]oneycomb")
299 logger.info("Checking node {} ...".format(node['host']))
301 logger.info("Command: {}".format(cmd))
304 ssh.exec_command_sudo(cmd)
307 def configure_log_level(node, level):
308 """Set Honeycomb logging to the specified level.
310 :param node: Honeycomb node.
311 :param level: Log level (INFO, DEBUG, TRACE).
316 find = 'logger name=\\"io.fd\\"'
317 replace = '<logger name=\\"io.fd\\" level=\\"{0}\\"/>'.format(level)
319 argument = '"/{0}/c\\ {1}"'.format(find, replace)
320 path = "{0}/config/logback.xml".format(Const.REMOTE_HC_DIR)
321 command = "sed -i {0} {1}".format(argument, path)
325 (ret_code, _, stderr) = ssh.exec_command_sudo(command)
327 raise HoneycombError("Failed to modify configuration on "
328 "node {0}, {1}".format(node, stderr))
331 def enable_module_features(node, *features):
332 """Configure Honeycomb to use VPP modules that are disabled by default.
334 ..Note:: If the module is not enabled in VPP, Honeycomb will
335 be unable to establish VPP connection.
337 :param node: Honeycomb node.
338 :param features: Features to enable.
340 :type features: string
341 :raises HoneycombError: If the configuration could not be changed.
344 disabled_features = {
345 "NSH": "io.fd.hc2vpp.vppnsh.impl.VppNshModule"
351 for feature in features:
352 if feature in disabled_features.keys():
353 # uncomment by replacing the entire line
354 find = replace = "{0}".format(disabled_features[feature])
356 argument = '"/{0}/c\\ {1}"'.format(find, replace)
357 path = "{0}/modules/*module-config"\
358 .format(Const.REMOTE_HC_DIR)
359 command = "sed -i {0} {1}".format(argument, path)
361 (ret_code, _, stderr) = ssh.exec_command_sudo(command)
363 raise HoneycombError("Failed to modify configuration on "
364 "node {0}, {1}".format(node, stderr))
366 raise HoneycombError(
367 "Unrecognized feature {0}.".format(feature))
370 def copy_java_libraries(node):
371 """Copy Java libraries installed by vpp-api-java package to honeycomb
374 This is a (temporary?) workaround for jvpp version mismatches.
376 :param node: Honeycomb node
382 (_, stdout, _) = ssh.exec_command_sudo(
383 "ls /usr/share/java | grep ^jvpp-*")
385 files = stdout.split("\n")[:-1]
388 # jvpp-registry-17.04.jar
389 # jvpp-core-17.04.jar
391 parts = item.split("-")
392 version = "{0}-SNAPSHOT".format(parts[2][:5])
393 artifact_id = "{0}-{1}".format(parts[0], parts[1])
395 directory = "{0}/lib/io/fd/vpp/{1}/{2}".format(
396 Const.REMOTE_HC_DIR, artifact_id, version)
397 cmd = "sudo mkdir -p {0}; " \
398 "sudo cp /usr/share/java/{1} {0}/{2}-{3}.jar".format(
399 directory, item, artifact_id, version)
401 (ret_code, _, stderr) = ssh.exec_command(cmd)
403 raise HoneycombError("Failed to copy JVPP libraries on "
404 "node {0}, {1}".format(node, stderr))
407 def find_odl_client(node):
408 """Check if there is a karaf directory in home.
410 :param node: Honeycomb node.
412 :returns: True if ODL client is present on node, else False.
418 (ret_code, stdout, _) = ssh.exec_command_sudo(
422 return not bool(ret_code)
425 def start_odl_client(node):
426 """Start ODL client on the specified node.
428 karaf should be located in home directory, and VPP and Honeycomb should
429 already be running, otherwise the start will fail.
430 :param node: Nodes to start ODL client on.
432 :raises HoneycombError: If Honeycomb fails to start.
435 logger.console("\nStarting ODL client ...")
437 cmd = "~/*karaf*/bin/start"
441 (ret_code, _, _) = ssh.exec_command_sudo(cmd)
442 if int(ret_code) != 0:
443 raise HoneycombError('Node {0} failed to start ODL.'.
444 format(node['host']))
446 logger.info("Starting the ODL client on node {0} is "
447 "in progress ...".format(node['host']))
450 def check_odl_startup_state(node):
451 """Check the status of ODL client startup.
453 :param node: Honeycomb node.
455 :returns: True when ODL is started.
457 :raises HoneycombError: When the response is not code 200: OK.
460 path = HcUtil.read_path_from_url_file(
461 "odl_client/odl_netconf_connector")
462 expected_status_codes = (HTTPCodes.UNAUTHORIZED,
465 HTTPCodes.SERVICE_UNAVAILABLE,
466 HTTPCodes.INTERNAL_SERVER_ERROR)
468 status_code, _ = HTTPRequest.get(node, path, timeout=10,
469 enable_logging=False)
470 if status_code == HTTPCodes.OK:
471 logger.info("ODL client on node {0} is up and running".
472 format(node['host']))
473 elif status_code in expected_status_codes:
474 if status_code == HTTPCodes.UNAUTHORIZED:
475 logger.info('Unauthorized. If this triggers keyword '
476 'timeout, verify username and password.')
477 raise HoneycombError('ODL client on node {0} running but '
478 'not yet ready.'.format(node['host']),
479 enable_logging=False)
481 raise HoneycombError('Unexpected return code: {0}.'.
486 def mount_honeycomb_on_odl(node):
487 """Tell ODL client to mount Honeycomb instance over netconf.
489 :param node: Honeycomb node.
491 :raises HoneycombError: When the response is not code 200: OK.
494 path = HcUtil.read_path_from_url_file(
495 "odl_client/odl_netconf_connector")
497 url_file = "{0}/{1}".format(Const.RESOURCES_TPL_HC,
498 "odl_client/mount_honeycomb.xml")
500 with open(url_file) as template:
501 data = template.read()
503 status_code, _ = HTTPRequest.post(
504 node, path, headers={"Content-Type": "application/xml"},
505 payload=data, timeout=10, enable_logging=False)
507 if status_code == HTTPCodes.OK:
508 logger.info("ODL mount point configured successfully.")
509 elif status_code == HTTPCodes.CONFLICT:
510 logger.warn("ODL mount point was already configured.")
512 raise HoneycombError('Mount point configuration not successful')