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("\nStarting 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 restart_honeycomb_and_vpp_on_duts(*nodes):
111 """Restart the Honeycomb service on specified DUT nodes.
113 Use the keyword "Check Honeycomb Startup State" to check when Honeycomb
115 :param nodes: List of nodes to restart Honeycomb on.
117 :raises HoneycombError: If Honeycomb failed to restart.
119 logger.console("\nRestarting Honeycomb service ...")
121 cmd = "sudo service honeycomb restart "
125 if node['type'] == NodeType.DUT:
128 (ret_code, _, _) = ssh.exec_command_sudo(cmd)
129 if int(ret_code) != 0:
130 errors.append(node['host'])
132 DUTSetup.setup_dut(node)
133 except Exception as err:
135 errors.append(node['host'])
137 logger.info("Restart of Honeycomb and VPP on node {0} is "
138 "in progress ...".format(node['host']))
140 raise HoneycombError('Node(s) {0} failed to restart Honeycomb'
145 def check_honeycomb_startup_state(*nodes):
146 """Check state of Honeycomb service during startup on specified nodes.
148 Reads html path from template file oper_vpp_version.url.
150 Honeycomb nodes reply with connection refused or the following status
151 codes depending on startup progress: codes 200, 401, 403, 404, 500, 503
153 :param nodes: List of DUT nodes starting Honeycomb.
155 :return: True if all GETs returned code 200(OK).
158 path = HcUtil.read_path_from_url_file("oper_vpp_version")
159 expected_status_codes = (HTTPCodes.UNAUTHORIZED,
162 HTTPCodes.SERVICE_UNAVAILABLE,
163 HTTPCodes.INTERNAL_SERVER_ERROR)
166 if node['type'] == NodeType.DUT:
167 HoneycombSetup.print_ports(node)
168 status_code, _ = HTTPRequest.get(node, path,
169 enable_logging=False)
170 if status_code == HTTPCodes.OK:
171 logger.info("Honeycomb on node {0} is up and running".
172 format(node['host']))
173 elif status_code in expected_status_codes:
174 if status_code == HTTPCodes.UNAUTHORIZED:
175 logger.info('Unauthorized. If this triggers keyword '
176 'timeout, verify Honeycomb username and '
178 raise HoneycombError('Honeycomb on node {0} running but '
179 'not yet ready.'.format(node['host']),
180 enable_logging=False)
182 raise HoneycombError('Unexpected return code: {0}.'.
185 status_code, _ = HcUtil.get_honeycomb_data(
186 node, "config_vpp_interfaces")
187 if status_code != HTTPCodes.OK:
188 raise HoneycombError('Honeycomb on node {0} running but '
189 'not yet ready.'.format(node['host']),
190 enable_logging=False)
194 def check_honeycomb_shutdown_state(*nodes):
195 """Check state of Honeycomb service during shutdown on specified nodes.
197 Honeycomb nodes reply with connection refused or the following status
198 codes depending on shutdown progress: codes 200, 404.
200 :param nodes: List of DUT nodes stopping Honeycomb.
202 :return: True if all GETs fail to connect.
205 cmd = "ps -ef | grep -v grep | grep honeycomb"
207 if node['type'] == NodeType.DUT:
209 status_code, _ = HTTPRequest.get(node, '/index.html',
210 enable_logging=False)
211 if status_code == HTTPCodes.OK:
212 raise HoneycombError('Honeycomb on node {0} is still '
213 'running.'.format(node['host']),
214 enable_logging=False)
215 elif status_code == HTTPCodes.NOT_FOUND:
216 raise HoneycombError('Honeycomb on node {0} is shutting'
217 ' down.'.format(node['host']),
218 enable_logging=False)
220 raise HoneycombError('Unexpected return code: {0}.'.
222 except HTTPRequestError:
223 logger.debug('Connection refused, checking the process '
227 (ret_code, _, _) = ssh.exec_command_sudo(cmd)
229 raise HoneycombError('Honeycomb on node {0} is still '
230 'running.'.format(node['host']),
231 enable_logging=False)
233 logger.info("Honeycomb on node {0} has stopped".
234 format(node['host']))
238 def configure_restconf_binding_address(node):
239 """Configure Honeycomb to accept restconf requests from all IP
240 addresses. IP version is determined by node data.
242 :param node: Information about a DUT node.
244 :raises HoneycombError: If the configuration could not be changed.
247 find = "restconf-binding-address"
249 IPv6Address(unicode(node["host"]))
250 # if management IP of the node is in IPv6 format
251 replace = '\\"restconf-binding-address\\": \\"0::0\\",'
252 except (AttributeError, AddressValueError):
253 replace = '\\"restconf-binding-address\\": \\"0.0.0.0\\",'
255 argument = '"/{0}/c\\ {1}"'.format(find, replace)
256 path = "{0}/config/honeycomb.json".format(Const.REMOTE_HC_DIR)
257 command = "sed -i {0} {1}".format(argument, path)
261 (ret_code, _, stderr) = ssh.exec_command_sudo(command)
263 raise HoneycombError("Failed to modify configuration on "
264 "node {0}, {1}".format(node, stderr))
267 def configure_jvpp_timeout(node, timeout=10):
268 """Configure timeout value for Java API commands Honeycomb sends to VPP.
270 :param node: Information about a DUT node.
271 :param timeout: Timeout value in seconds.
274 :raises HoneycombError: If the configuration could not be changed.
277 find = "jvpp-request-timeout"
278 replace = '\\"jvpp-request-timeout\\": {0}'.format(timeout)
280 argument = '"/{0}/c\\ {1}"'.format(find, replace)
281 path = "{0}/config/jvpp.json".format(Const.REMOTE_HC_DIR)
282 command = "sed -i {0} {1}".format(argument, path)
286 (ret_code, _, stderr) = ssh.exec_command_sudo(command)
288 raise HoneycombError("Failed to modify configuration on "
289 "node {0}, {1}".format(node, stderr))
292 def print_environment(nodes):
293 """Print information about the nodes to log. The information is defined
294 by commands in cmds tuple at the beginning of this method.
296 :param nodes: List of DUT nodes to get information about.
300 # TODO: When everything is set and running in VIRL env, transform this
301 # method to a keyword checking the environment.
309 "dpkg --list | grep openjdk",
310 "ls -la /opt/honeycomb")
313 if node['type'] == NodeType.DUT:
314 logger.info("Checking node {} ...".format(node['host']))
316 logger.info("Command: {}".format(cmd))
319 ssh.exec_command_sudo(cmd)
322 def print_ports(node):
323 """Uses "sudo netstat -anp | grep java" to print port where a java
326 :param node: Honeycomb node where we want to print the ports.
330 cmds = ("netstat -anp | grep java",
331 "ps -ef | grep [h]oneycomb")
333 logger.info("Checking node {} ...".format(node['host']))
335 logger.info("Command: {}".format(cmd))
338 ssh.exec_command_sudo(cmd)
341 def configure_log_level(node, level):
342 """Set Honeycomb logging to the specified level.
344 :param node: Honeycomb node.
345 :param level: Log level (INFO, DEBUG, TRACE).
350 find = 'logger name=\\"io.fd\\"'
351 replace = '<logger name=\\"io.fd\\" level=\\"{0}\\"/>'.format(level)
353 argument = '"/{0}/c\\ {1}"'.format(find, replace)
354 path = "{0}/config/logback.xml".format(Const.REMOTE_HC_DIR)
355 command = "sed -i {0} {1}".format(argument, path)
359 (ret_code, _, stderr) = ssh.exec_command_sudo(command)
361 raise HoneycombError("Failed to modify configuration on "
362 "node {0}, {1}".format(node, stderr))
365 def enable_module_features(node, *features):
366 """Configure Honeycomb to use VPP modules that are disabled by default.
368 ..Note:: If the module is not enabled in VPP, Honeycomb will
369 be unable to establish VPP connection.
371 :param node: Honeycomb node.
372 :param features: Features to enable.
374 :type features: string
375 :raises HoneycombError: If the configuration could not be changed.
378 disabled_features = {
379 "NSH": "io.fd.hc2vpp.vppnsh.impl.VppNshModule"
385 for feature in features:
386 if feature in disabled_features.keys():
387 # uncomment by replacing the entire line
388 find = replace = "{0}".format(disabled_features[feature])
390 argument = '"/{0}/c\\ {1}"'.format(find, replace)
391 path = "{0}/modules/*module-config"\
392 .format(Const.REMOTE_HC_DIR)
393 command = "sed -i {0} {1}".format(argument, path)
395 (ret_code, _, stderr) = ssh.exec_command_sudo(command)
397 raise HoneycombError("Failed to modify configuration on "
398 "node {0}, {1}".format(node, stderr))
400 raise HoneycombError(
401 "Unrecognized feature {0}.".format(feature))
404 def copy_java_libraries(node):
405 """Copy Java libraries installed by vpp-api-java package to honeycomb
408 This is a (temporary?) workaround for jvpp version mismatches.
410 :param node: Honeycomb node
416 (_, stdout, _) = ssh.exec_command_sudo(
417 "ls /usr/share/java | grep ^jvpp-*")
419 files = stdout.split("\n")[:-1]
422 # jvpp-registry-17.04.jar
423 # jvpp-core-17.04.jar
425 parts = item.split("-")
426 version = "{0}-SNAPSHOT".format(parts[2][:5])
427 artifact_id = "{0}-{1}".format(parts[0], parts[1])
429 directory = "{0}/lib/io/fd/vpp/{1}/{2}".format(
430 Const.REMOTE_HC_DIR, artifact_id, version)
431 cmd = "sudo mkdir -p {0}; " \
432 "sudo cp /usr/share/java/{1} {0}/{2}-{3}.jar".format(
433 directory, item, artifact_id, version)
435 (ret_code, _, stderr) = ssh.exec_command(cmd)
437 raise HoneycombError("Failed to copy JVPP libraries on "
438 "node {0}, {1}".format(node, stderr))
441 def configure_odl_client(node, odl_name):
442 """Start ODL client on the specified node.
444 Karaf should be located in /mnt/common, and VPP and Honeycomb should
445 already be running, otherwise the start will fail.
446 :param node: Node to start ODL client on.
447 :param odl_name: Name of ODL client version to use.
450 :raises HoneycombError: If Honeycomb fails to start.
453 logger.debug("Copying ODL Client to home dir.")
458 cmd = "cp -r /mnt/common/*karaf_{name}* ~/karaf".format(name=odl_name)
460 (ret_code, _, _) = ssh.exec_command_sudo(cmd)
461 if int(ret_code) != 0:
462 raise HoneycombError(
463 "Failed to copy ODL client on node {0}".format(node["host"]))
465 logger.console("\nStarting ODL client ...")
467 cmd = "~/*karaf*/bin/start"
471 (ret_code, _, _) = ssh.exec_command_sudo(cmd)
472 if int(ret_code) != 0:
473 raise HoneycombError('Node {0} failed to start ODL.'.
474 format(node['host']))
476 logger.info("Starting the ODL client on node {0} is "
477 "in progress ...".format(node['host']))
480 def check_odl_startup_state(node):
481 """Check the status of ODL client startup.
483 :param node: Honeycomb node.
485 :returns: True when ODL is started.
487 :raises HoneycombError: When the response is not code 200: OK.
490 path = HcUtil.read_path_from_url_file(
491 "odl_client/odl_netconf_connector")
492 expected_status_codes = (HTTPCodes.UNAUTHORIZED,
495 HTTPCodes.SERVICE_UNAVAILABLE,
496 HTTPCodes.INTERNAL_SERVER_ERROR)
498 status_code, _ = HTTPRequest.get(node, path, timeout=10,
499 enable_logging=False)
500 if status_code == HTTPCodes.OK:
501 logger.info("ODL client on node {0} is up and running".
502 format(node['host']))
503 elif status_code in expected_status_codes:
504 if status_code == HTTPCodes.UNAUTHORIZED:
505 logger.info('Unauthorized. If this triggers keyword '
506 'timeout, verify username and password.')
507 raise HoneycombError('ODL client on node {0} running but '
508 'not yet ready.'.format(node['host']),
509 enable_logging=False)
511 raise HoneycombError('Unexpected return code: {0}.'.
516 def mount_honeycomb_on_odl(node):
517 """Tell ODL client to mount Honeycomb instance over netconf.
519 :param node: Honeycomb node.
521 :raises HoneycombError: When the response is not code 200: OK.
524 path = HcUtil.read_path_from_url_file(
525 "odl_client/odl_netconf_connector")
527 url_file = "{0}/{1}".format(Const.RESOURCES_TPL_HC,
528 "odl_client/mount_honeycomb.xml")
530 with open(url_file) as template:
531 data = template.read()
533 status_code, _ = HTTPRequest.post(
534 node, path, headers={"Content-Type": "application/xml"},
535 payload=data, timeout=10, enable_logging=False)
537 if status_code == HTTPCodes.OK:
538 logger.info("ODL mount point configured successfully.")
539 elif status_code == HTTPCodes.CONFLICT:
540 logger.warn("ODL mount point was already configured.")
542 raise HoneycombError('Mount point configuration not successful')