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
30 class HoneycombSetup(object):
31 """Implements keywords for Honeycomb setup.
33 The keywords implemented in this class make possible to:
36 - check the Honeycomb start-up state,
37 - check the Honeycomb shutdown state,
38 - add VPP to the topology.
45 def start_honeycomb_on_duts(*nodes):
46 """Start Honeycomb on specified DUT nodes.
48 This keyword starts the Honeycomb service on specified DUTs.
49 The keyword just starts the Honeycomb and does not check its startup
50 state. Use the keyword "Check Honeycomb Startup State" to check if the
51 Honeycomb is up and running.
52 Honeycomb must be installed in "/opt" directory, otherwise the start
54 :param nodes: List of nodes to start Honeycomb on.
56 :raises HoneycombError: If Honeycomb fails to start.
59 HoneycombSetup.print_environment(nodes)
61 logger.console("\nStarting Honeycomb service ...")
63 cmd = "sudo service honeycomb start"
66 if node['type'] == NodeType.DUT:
69 (ret_code, _, _) = ssh.exec_command_sudo(cmd)
70 if int(ret_code) != 0:
71 raise HoneycombError('Node {0} failed to start Honeycomb.'.
74 logger.info("Starting the Honeycomb service on node {0} is "
75 "in progress ...".format(node['host']))
78 def stop_honeycomb_on_duts(*nodes):
79 """Stop the Honeycomb service on specified DUT nodes.
81 This keyword stops the Honeycomb service on specified nodes. It just
82 stops the Honeycomb and does not check its shutdown state. Use the
83 keyword "Check Honeycomb Shutdown State" to check if Honeycomb has
85 :param nodes: List of nodes to stop Honeycomb on.
87 :raises HoneycombError: If Honeycomb failed to stop.
89 logger.console("\nShutting down Honeycomb service ...")
91 cmd = "sudo service honeycomb stop"
95 if node['type'] == NodeType.DUT:
98 (ret_code, _, _) = ssh.exec_command_sudo(cmd)
99 if int(ret_code) != 0:
100 errors.append(node['host'])
102 logger.info("Stopping the Honeycomb service on node {0} is "
103 "in progress ...".format(node['host']))
105 raise HoneycombError('Node(s) {0} failed to stop Honeycomb.'.
109 def restart_honeycomb_and_vpp_on_duts(*nodes):
110 """Restart the Honeycomb service on specified DUT nodes.
112 Use the keyword "Check Honeycomb Startup State" to check when Honeycomb
114 :param nodes: List of nodes to restart Honeycomb on.
116 :raises HoneycombError: If Honeycomb failed to restart.
118 logger.console("\nRestarting Honeycomb service ...")
120 cmd = "sudo service honeycomb restart && sudo service vpp restart"
124 if node['type'] == NodeType.DUT:
127 (ret_code, _, _) = ssh.exec_command_sudo(cmd)
128 if int(ret_code) != 0:
129 errors.append(node['host'])
131 logger.info("Restart of Honeycomb and VPP on node {0} is "
132 "in progress ...".format(node['host']))
134 raise HoneycombError('Node(s) {0} failed to restart Honeycomb'
139 def check_honeycomb_startup_state(*nodes):
140 """Check state of Honeycomb service during startup on specified nodes.
142 Reads html path from template file oper_vpp_version.url.
144 Honeycomb nodes reply with connection refused or the following status
145 codes depending on startup progress: codes 200, 401, 403, 404, 500, 503
147 :param nodes: List of DUT nodes starting Honeycomb.
149 :return: True if all GETs returned code 200(OK).
152 path = HcUtil.read_path_from_url_file("oper_vpp_version")
153 expected_status_codes = (HTTPCodes.UNAUTHORIZED,
156 HTTPCodes.SERVICE_UNAVAILABLE,
157 HTTPCodes.INTERNAL_SERVER_ERROR)
160 if node['type'] == NodeType.DUT:
161 HoneycombSetup.print_ports(node)
162 status_code, _ = HTTPRequest.get(node, path, timeout=10,
163 enable_logging=False)
164 if status_code == HTTPCodes.OK:
165 logger.info("Honeycomb on node {0} is up and running".
166 format(node['host']))
167 elif status_code in expected_status_codes:
168 if status_code == HTTPCodes.UNAUTHORIZED:
169 logger.info('Unauthorized. If this triggers keyword '
170 'timeout, verify Honeycomb username and '
172 raise HoneycombError('Honeycomb on node {0} running but '
173 'not yet ready.'.format(node['host']),
174 enable_logging=False)
176 raise HoneycombError('Unexpected return code: {0}.'.
179 status_code, _ = HcUtil.get_honeycomb_data(
180 node, "config_vpp_interfaces")
181 if status_code != HTTPCodes.OK:
182 raise HoneycombError('Honeycomb on node {0} running but '
183 'not yet ready.'.format(node['host']),
184 enable_logging=False)
188 def check_honeycomb_shutdown_state(*nodes):
189 """Check state of Honeycomb service during shutdown on specified nodes.
191 Honeycomb nodes reply with connection refused or the following status
192 codes depending on shutdown progress: codes 200, 404.
194 :param nodes: List of DUT nodes stopping Honeycomb.
196 :return: True if all GETs fail to connect.
199 cmd = "ps -ef | grep -v grep | grep honeycomb"
201 if node['type'] == NodeType.DUT:
203 status_code, _ = HTTPRequest.get(node, '/index.html',
205 enable_logging=False)
206 if status_code == HTTPCodes.OK:
207 raise HoneycombError('Honeycomb on node {0} is still '
208 'running.'.format(node['host']),
209 enable_logging=False)
210 elif status_code == HTTPCodes.NOT_FOUND:
211 raise HoneycombError('Honeycomb on node {0} is shutting'
212 ' down.'.format(node['host']),
213 enable_logging=False)
215 raise HoneycombError('Unexpected return code: {0}.'.
217 except HTTPRequestError:
218 logger.debug('Connection refused, checking the process '
222 (ret_code, _, _) = ssh.exec_command_sudo(cmd)
224 raise HoneycombError('Honeycomb on node {0} is still '
225 'running.'.format(node['host']),
226 enable_logging=False)
228 logger.info("Honeycomb on node {0} has stopped".
229 format(node['host']))
233 def configure_restconf_binding_address(node):
234 """Configure Honeycomb to accept restconf requests from all IP
235 addresses. IP version is determined by node data.
237 :param node: Information about a DUT node.
239 :raises HoneycombError: If the configuration could not be changed.
242 find = "restconf-binding-address"
244 IPv6Address(unicode(node["host"]))
245 # if management IP of the node is in IPv6 format
246 replace = '\\"restconf-binding-address\\": \\"0::0\\",'
247 except (AttributeError, AddressValueError):
248 replace = '\\"restconf-binding-address\\": \\"0.0.0.0\\",'
250 argument = '"/{0}/c\\ {1}"'.format(find, replace)
251 path = "{0}/config/honeycomb.json".format(Const.REMOTE_HC_DIR)
252 command = "sed -i {0} {1}".format(argument, path)
256 (ret_code, _, stderr) = ssh.exec_command_sudo(command)
258 raise HoneycombError("Failed to modify configuration on "
259 "node {0}, {1}".format(node, stderr))
262 def print_environment(nodes):
263 """Print information about the nodes to log. The information is defined
264 by commands in cmds tuple at the beginning of this method.
266 :param nodes: List of DUT nodes to get information about.
270 # TODO: When everything is set and running in VIRL env, transform this
271 # method to a keyword checking the environment.
279 "dpkg --list | grep openjdk",
280 "ls -la /opt/honeycomb")
283 if node['type'] == NodeType.DUT:
284 logger.info("Checking node {} ...".format(node['host']))
286 logger.info("Command: {}".format(cmd))
289 ssh.exec_command_sudo(cmd)
292 def print_ports(node):
293 """Uses "sudo netstat -anp | grep java" to print port where a java
296 :param node: Honeycomb node where we want to print the ports.
300 cmds = ("netstat -anp | grep java",
301 "ps -ef | grep [h]oneycomb")
303 logger.info("Checking node {} ...".format(node['host']))
305 logger.info("Command: {}".format(cmd))
308 ssh.exec_command_sudo(cmd)
311 def configure_log_level(node, level):
312 """Set Honeycomb logging to the specified level.
314 :param node: Honeycomb node.
315 :param level: Log level (INFO, DEBUG, TRACE).
320 find = 'logger name=\\"io.fd\\"'
321 replace = '<logger name=\\"io.fd\\" level=\\"{0}\\"/>'.format(level)
323 argument = '"/{0}/c\\ {1}"'.format(find, replace)
324 path = "{0}/config/logback.xml".format(Const.REMOTE_HC_DIR)
325 command = "sed -i {0} {1}".format(argument, path)
329 (ret_code, _, stderr) = ssh.exec_command_sudo(command)
331 raise HoneycombError("Failed to modify configuration on "
332 "node {0}, {1}".format(node, stderr))
335 def enable_module_features(node, *features):
336 """Configure Honeycomb to use VPP modules that are disabled by default.
338 ..Note:: If the module is not enabled in VPP, Honeycomb will
339 be unable to establish VPP connection.
341 :param node: Honeycomb node.
342 :param features: Features to enable.
344 :type features: string
345 :raises HoneycombError: If the configuration could not be changed.
348 disabled_features = {
349 "NSH": "io.fd.hc2vpp.vppnsh.impl.VppNshModule"
355 for feature in features:
356 if feature in disabled_features.keys():
357 # uncomment by replacing the entire line
358 find = replace = "{0}".format(disabled_features[feature])
360 argument = '"/{0}/c\\ {1}"'.format(find, replace)
361 path = "{0}/modules/*module-config"\
362 .format(Const.REMOTE_HC_DIR)
363 command = "sed -i {0} {1}".format(argument, path)
365 (ret_code, _, stderr) = ssh.exec_command_sudo(command)
367 raise HoneycombError("Failed to modify configuration on "
368 "node {0}, {1}".format(node, stderr))
370 raise HoneycombError(
371 "Unrecognized feature {0}.".format(feature))
374 def copy_java_libraries(node):
375 """Copy Java libraries installed by vpp-api-java package to honeycomb
378 This is a (temporary?) workaround for jvpp version mismatches.
380 :param node: Honeycomb node
386 (_, stdout, _) = ssh.exec_command_sudo(
387 "ls /usr/share/java | grep ^jvpp-*")
389 files = stdout.split("\n")[:-1]
392 # jvpp-registry-17.04.jar
393 # jvpp-core-17.04.jar
395 parts = item.split("-")
396 version = "{0}-SNAPSHOT".format(parts[2][:5])
397 artifact_id = "{0}-{1}".format(parts[0], parts[1])
399 directory = "{0}/lib/io/fd/vpp/{1}/{2}".format(
400 Const.REMOTE_HC_DIR, artifact_id, version)
401 cmd = "sudo mkdir -p {0}; " \
402 "sudo cp /usr/share/java/{1} {0}/{2}-{3}.jar".format(
403 directory, item, artifact_id, version)
405 (ret_code, _, stderr) = ssh.exec_command(cmd)
407 raise HoneycombError("Failed to copy JVPP libraries on "
408 "node {0}, {1}".format(node, stderr))
411 def configure_odl_client(node, odl_name):
412 """Start ODL client on the specified node.
414 Karaf should be located in /mnt/common, and VPP and Honeycomb should
415 already be running, otherwise the start will fail.
416 :param node: Node to start ODL client on.
417 :param odl_name: Name of ODL client version to use.
420 :raises HoneycombError: If Honeycomb fails to start.
423 logger.debug("Copying ODL Client to home dir.")
428 cmd = "cp -r /mnt/common/*karaf_{name}* ~/karaf".format(name=odl_name)
430 (ret_code, _, _) = ssh.exec_command_sudo(cmd)
431 if int(ret_code) != 0:
432 raise HoneycombError(
433 "Failed to copy ODL client on node {0}".format(node["host"]))
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')