1 # Copyright (c) 2017 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 ipaddress import IPv6Address, AddressValueError
18 from robot.api import logger
20 from resources.libraries.python.HTTPRequest import HTTPRequest, HTTPCodes, \
22 from resources.libraries.python.constants import Constants as Const
23 from resources.libraries.python.honeycomb.HoneycombUtil import HoneycombError
24 from resources.libraries.python.honeycomb.HoneycombUtil \
25 import HoneycombUtil as HcUtil
26 from resources.libraries.python.ssh import SSH
27 from resources.libraries.python.topology import NodeType
28 from resources.libraries.python.DUTSetup import DUTSetup
31 class HoneycombSetup(object):
32 """Implements keywords for Honeycomb setup.
34 The keywords implemented in this class make possible to:
37 - check the Honeycomb start-up state,
38 - check the Honeycomb shutdown state,
39 - add VPP to the topology.
46 def start_honeycomb_on_duts(*nodes):
47 """Start Honeycomb on specified DUT nodes.
49 This keyword starts the Honeycomb service on specified DUTs.
50 The keyword just starts the Honeycomb and does not check its startup
51 state. Use the keyword "Check Honeycomb Startup State" to check if the
52 Honeycomb is up and running.
53 Honeycomb must be installed in "/opt" directory, otherwise the start
55 :param nodes: List of nodes to start Honeycomb on.
57 :raises HoneycombError: If Honeycomb fails to start.
60 HoneycombSetup.print_environment(nodes)
62 logger.console("\n(re)Starting Honeycomb service ...")
64 cmd = "sudo service honeycomb start"
67 if node['type'] == NodeType.DUT:
70 (ret_code, _, _) = ssh.exec_command_sudo(cmd)
71 if int(ret_code) != 0:
72 raise HoneycombError('Node {0} failed to start Honeycomb.'.
75 logger.info("Starting the Honeycomb service on node {0} is "
76 "in progress ...".format(node['host']))
79 def stop_honeycomb_on_duts(*nodes):
80 """Stop the Honeycomb service on specified DUT nodes.
82 This keyword stops the Honeycomb service on specified nodes. It just
83 stops the Honeycomb and does not check its shutdown state. Use the
84 keyword "Check Honeycomb Shutdown State" to check if Honeycomb has
86 :param nodes: List of nodes to stop Honeycomb on.
88 :raises HoneycombError: If Honeycomb failed to stop.
90 logger.console("\nShutting down Honeycomb service ...")
92 cmd = "sudo service honeycomb stop"
96 if node['type'] == NodeType.DUT:
99 (ret_code, _, _) = ssh.exec_command_sudo(cmd)
100 if int(ret_code) != 0:
101 errors.append(node['host'])
103 logger.info("Stopping the Honeycomb service on node {0} is "
104 "in progress ...".format(node['host']))
106 raise HoneycombError('Node(s) {0} failed to stop Honeycomb.'.
110 def check_honeycomb_startup_state(*nodes):
111 """Check state of Honeycomb service during startup on specified nodes.
113 Reads html path from template file oper_vpp_version.url.
115 Honeycomb nodes reply with connection refused or the following status
116 codes depending on startup progress: codes 200, 401, 403, 404, 500, 503
118 :param nodes: List of DUT nodes starting Honeycomb.
120 :return: True if all GETs returned code 200(OK).
123 path = HcUtil.read_path_from_url_file("oper_vpp_version")
124 expected_status_codes = (HTTPCodes.UNAUTHORIZED,
127 HTTPCodes.SERVICE_UNAVAILABLE,
128 HTTPCodes.INTERNAL_SERVER_ERROR)
131 if node['type'] == NodeType.DUT:
132 HoneycombSetup.print_ports(node)
133 status_code, _ = HTTPRequest.get(node, path,
134 enable_logging=False)
135 if status_code == HTTPCodes.OK:
136 logger.info("Honeycomb on node {0} is up and running".
137 format(node['host']))
138 elif status_code in expected_status_codes:
139 if status_code == HTTPCodes.UNAUTHORIZED:
140 logger.info('Unauthorized. If this triggers keyword '
141 'timeout, verify Honeycomb username and '
143 raise HoneycombError('Honeycomb on node {0} running but '
144 'not yet ready.'.format(node['host']),
145 enable_logging=False)
147 raise HoneycombError('Unexpected return code: {0}.'.
150 status_code, _ = HcUtil.get_honeycomb_data(
151 node, "config_vpp_interfaces")
152 if status_code != HTTPCodes.OK:
153 raise HoneycombError('Honeycomb on node {0} running but '
154 'not yet ready.'.format(node['host']),
155 enable_logging=False)
159 def check_honeycomb_shutdown_state(*nodes):
160 """Check state of Honeycomb service during shutdown on specified nodes.
162 Honeycomb nodes reply with connection refused or the following status
163 codes depending on shutdown progress: codes 200, 404.
165 :param nodes: List of DUT nodes stopping Honeycomb.
167 :return: True if all GETs fail to connect.
170 cmd = "ps -ef | grep -v grep | grep honeycomb"
172 if node['type'] == NodeType.DUT:
174 status_code, _ = HTTPRequest.get(node, '/index.html',
175 enable_logging=False)
176 if status_code == HTTPCodes.OK:
177 raise HoneycombError('Honeycomb on node {0} is still '
178 'running.'.format(node['host']),
179 enable_logging=False)
180 elif status_code == HTTPCodes.NOT_FOUND:
181 raise HoneycombError('Honeycomb on node {0} is shutting'
182 ' down.'.format(node['host']),
183 enable_logging=False)
185 raise HoneycombError('Unexpected return code: {0}.'.
187 except HTTPRequestError:
188 logger.debug('Connection refused, checking the process '
192 (ret_code, _, _) = ssh.exec_command_sudo(cmd)
194 raise HoneycombError('Honeycomb on node {0} is still '
195 'running.'.format(node['host']),
196 enable_logging=False)
198 logger.info("Honeycomb on node {0} has stopped".
199 format(node['host']))
203 def configure_restconf_binding_address(node):
204 """Configure Honeycomb to accept restconf requests from all IP
205 addresses. IP version is determined by node data.
207 :param node: Information about a DUT node.
209 :raises HoneycombError: If the configuration could not be changed.
212 find = "restconf-binding-address"
214 IPv6Address(unicode(node["host"]))
215 # if management IP of the node is in IPv6 format
216 replace = '\\"restconf-binding-address\\": \\"0::0\\",'
217 except (AttributeError, AddressValueError):
218 replace = '\\"restconf-binding-address\\": \\"0.0.0.0\\",'
220 argument = '"/{0}/c\\ {1}"'.format(find, replace)
221 path = "{0}/config/honeycomb.json".format(Const.REMOTE_HC_DIR)
222 command = "sed -i {0} {1}".format(argument, path)
226 (ret_code, _, stderr) = ssh.exec_command_sudo(command)
228 raise HoneycombError("Failed to modify configuration on "
229 "node {0}, {1}".format(node, stderr))
232 def configure_jvpp_timeout(node, timeout=10):
233 """Configure timeout value for Java API commands Honeycomb sends to VPP.
235 :param node: Information about a DUT node.
236 :param timeout: Timeout value in seconds.
239 :raises HoneycombError: If the configuration could not be changed.
242 find = "jvpp-request-timeout"
243 replace = '\\"jvpp-request-timeout\\": {0}'.format(timeout)
245 argument = '"/{0}/c\\ {1}"'.format(find, replace)
246 path = "{0}/config/jvpp.json".format(Const.REMOTE_HC_DIR)
247 command = "sed -i {0} {1}".format(argument, path)
251 (ret_code, _, stderr) = ssh.exec_command_sudo(command)
253 raise HoneycombError("Failed to modify configuration on "
254 "node {0}, {1}".format(node, stderr))
257 def print_environment(nodes):
258 """Print information about the nodes to log. The information is defined
259 by commands in cmds tuple at the beginning of this method.
261 :param nodes: List of DUT nodes to get information about.
265 # TODO: When everything is set and running in VIRL env, transform this
266 # method to a keyword checking the environment.
274 "dpkg --list | grep openjdk",
275 "ls -la /opt/honeycomb")
278 if node['type'] == NodeType.DUT:
279 logger.info("Checking node {} ...".format(node['host']))
281 logger.info("Command: {}".format(cmd))
284 ssh.exec_command_sudo(cmd)
287 def print_ports(node):
288 """Uses "sudo netstat -anp | grep java" to print port where a java
291 :param node: Honeycomb node where we want to print the ports.
295 cmds = ("netstat -anp | grep java",
296 "ps -ef | grep [h]oneycomb")
298 logger.info("Checking node {} ...".format(node['host']))
300 logger.info("Command: {}".format(cmd))
303 ssh.exec_command_sudo(cmd)
306 def configure_log_level(node, level):
307 """Set Honeycomb logging to the specified level.
309 :param node: Honeycomb node.
310 :param level: Log level (INFO, DEBUG, TRACE).
315 find = 'logger name=\\"io.fd\\"'
316 replace = '<logger name=\\"io.fd\\" level=\\"{0}\\"/>'.format(level)
318 argument = '"/{0}/c\\ {1}"'.format(find, replace)
319 path = "{0}/config/logback.xml".format(Const.REMOTE_HC_DIR)
320 command = "sed -i {0} {1}".format(argument, path)
324 (ret_code, _, stderr) = ssh.exec_command_sudo(command)
326 raise HoneycombError("Failed to modify configuration on "
327 "node {0}, {1}".format(node, stderr))
330 def manage_honeycomb_features(node, feature, disable=False):
331 """Configure Honeycomb to use features that are disabled by default, or
332 disable previously enabled features.
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 feature: Feature to enable.
339 :param disable: Disable the specified feature instead of enabling it.
341 :type feature: string
343 :raises HoneycombError: If the configuration could not be changed.
346 disabled_features = {
347 "NSH": "io.fd.hc2vpp.vppnsh.impl.VppNshModule"
353 if feature in disabled_features.keys():
354 # uncomment by replacing the entire line
355 find = replace = "{0}".format(disabled_features[feature])
357 replace = "// {0}".format(find)
359 argument = '"/{0}/c\\ {1}"'.format(find, replace)
360 path = "{0}/modules/*module-config"\
361 .format(Const.REMOTE_HC_DIR)
362 command = "sed -i {0} {1}".format(argument, path)
364 (ret_code, _, stderr) = ssh.exec_command_sudo(command)
366 raise HoneycombError("Failed to modify configuration on "
367 "node {0}, {1}".format(node, stderr))
369 raise HoneycombError(
370 "Unrecognized feature {0}.".format(feature))
373 def copy_java_libraries(node):
374 """Copy Java libraries installed by vpp-api-java package to honeycomb
377 This is a (temporary?) workaround for jvpp version mismatches.
379 :param node: Honeycomb node
385 (_, stdout, _) = ssh.exec_command_sudo(
386 "ls /usr/share/java | grep ^jvpp-*")
388 files = stdout.split("\n")[:-1]
391 # jvpp-registry-17.04.jar
392 # jvpp-core-17.04.jar
394 parts = item.split("-")
395 version = "{0}-SNAPSHOT".format(parts[2][:5])
396 artifact_id = "{0}-{1}".format(parts[0], parts[1])
398 directory = "{0}/lib/io/fd/vpp/{1}/{2}".format(
399 Const.REMOTE_HC_DIR, artifact_id, version)
400 cmd = "sudo mkdir -p {0}; " \
401 "sudo cp /usr/share/java/{1} {0}/{2}-{3}.jar".format(
402 directory, item, artifact_id, version)
404 (ret_code, _, stderr) = ssh.exec_command(cmd)
406 raise HoneycombError("Failed to copy JVPP libraries on "
407 "node {0}, {1}".format(node, stderr))
410 def copy_odl_client(node, odl_name, src_path, dst_path):
411 """Copy ODL Client from source path to destination path.
413 :param node: Honeycomb node.
414 :param odl_name: Name of ODL client version to use.
415 :param src_path: Source Path where to find ODl client.
416 :param dst_path: Destination path.
421 :raises HoneycombError: If the operation fails.
427 cmd = "cp -r {src}/*karaf_{odl_name}* {dst}".format(
428 src=src_path, odl_name=odl_name, dst=dst_path)
430 ret_code, _, _ = ssh.exec_command(cmd, timeout=30)
431 if int(ret_code) != 0:
432 raise HoneycombError(
433 "Failed to copy ODL client on node {0}".format(node["host"]))
436 def setup_odl_client(node, path):
437 """Start ODL client on the specified node.
439 Karaf should be located in the provided path, and VPP and Honeycomb
440 should already be running, otherwise the start will fail.
441 :param node: Node to start ODL client on.
442 :param path: Path to ODL client on node.
445 :raises HoneycombError: If Honeycomb fails to start.
448 logger.console("\nStarting ODL client ...")
452 cmd = "{path}/*karaf*/bin/start clean".format(path=path)
453 ret_code, _, _ = ssh.exec_command_sudo(cmd)
455 if int(ret_code) != 0:
456 raise HoneycombError('Node {0} failed to start ODL.'.
457 format(node['host']))
459 logger.info("Starting the ODL client on node {0} is "
460 "in progress ...".format(node['host']))
463 def install_odl_features(node, path, *features):
464 """Install required features on a running ODL client.
466 :param node: Honeycomb node.
467 :param path: Path to ODL client on node.
468 :param features: Optional, list of additional features to install.
477 cmd = "{path}/*karaf*/bin/client -u karaf feature:install " \
478 "odl-restconf-all odl-netconf-connector-all".format(path=path)
479 for feature in features:
480 cmd += " {0}".format(feature)
482 ret_code, _, stderr = ssh.exec_command_sudo(cmd, timeout=120)
484 if int(ret_code) != 0:
485 raise HoneycombError("Feature install did not succeed.")
488 def check_odl_startup_state(node):
489 """Check the status of ODL client startup.
491 :param node: Honeycomb node.
493 :returns: True when ODL is started.
495 :raises HoneycombError: When the response is not code 200: OK.
498 path = HcUtil.read_path_from_url_file(
499 "odl_client/odl_netconf_connector")
500 expected_status_codes = (HTTPCodes.UNAUTHORIZED,
503 HTTPCodes.SERVICE_UNAVAILABLE,
504 HTTPCodes.INTERNAL_SERVER_ERROR)
506 status_code, _ = HTTPRequest.get(node, path, timeout=10,
507 enable_logging=False)
508 if status_code == HTTPCodes.OK:
509 logger.info("ODL client on node {0} is up and running".
510 format(node['host']))
511 elif status_code in expected_status_codes:
512 if status_code == HTTPCodes.UNAUTHORIZED:
513 logger.info('Unauthorized. If this triggers keyword '
514 'timeout, verify username and password.')
515 raise HoneycombError('ODL client on node {0} running but '
516 'not yet ready.'.format(node['host']),
517 enable_logging=False)
519 raise HoneycombError('Unexpected return code: {0}.'.
524 def mount_honeycomb_on_odl(node):
525 """Tell ODL client to mount Honeycomb instance over netconf.
527 :param node: Honeycomb node.
529 :raises HoneycombError: When the response is not code 200: OK.
532 path = HcUtil.read_path_from_url_file(
533 "odl_client/odl_netconf_connector")
535 url_file = "{0}/{1}".format(Const.RESOURCES_TPL_HC,
536 "odl_client/mount_honeycomb.xml")
538 with open(url_file) as template:
539 data = template.read()
541 status_code, _ = HTTPRequest.post(
542 node, path, headers={"Content-Type": "application/xml"},
543 payload=data, timeout=10, enable_logging=False)
545 if status_code == HTTPCodes.OK:
546 logger.info("ODL mount point configured successfully.")
547 elif status_code == HTTPCodes.CONFLICT:
548 logger.info("ODL mount point was already configured.")
550 raise HoneycombError('Mount point configuration not successful')
553 def stop_odl_client(node, path):
554 """Stop ODL client service on the specified node.
556 :param node: Node to start ODL client on.
557 :param path: Path to ODL client.
560 :raises HoneycombError: If ODL client fails to stop.
566 cmd = "{0}/*karaf*/bin/stop".format(path)
570 ret_code, _, _ = ssh.exec_command_sudo(cmd)
571 if int(ret_code) != 0:
572 logger.warn("ODL Client refused to shut down.")
573 cmd = "pkill -f 'karaf'"
574 (ret_code, _, _) = ssh.exec_command_sudo(cmd)
575 if int(ret_code) != 0:
576 raise HoneycombError('Node {0} failed to stop ODL.'.
577 format(node['host']))
579 logger.info("ODL client service stopped.")
582 def stop_vpp_service(node):
583 """Stop VPP service on the specified node.
585 :param node: VPP node.
587 :raises RuntimeError: If VPP fails to stop.
592 cmd = "service vpp stop"
593 ret_code, _, _ = ssh.exec_command_sudo(cmd)
594 if int(ret_code) != 0:
595 raise RuntimeError("Could not stop VPP service on node {0}".format(