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,
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',
204 enable_logging=False)
205 if status_code == HTTPCodes.OK:
206 raise HoneycombError('Honeycomb on node {0} is still '
207 'running.'.format(node['host']),
208 enable_logging=False)
209 elif status_code == HTTPCodes.NOT_FOUND:
210 raise HoneycombError('Honeycomb on node {0} is shutting'
211 ' down.'.format(node['host']),
212 enable_logging=False)
214 raise HoneycombError('Unexpected return code: {0}.'.
216 except HTTPRequestError:
217 logger.debug('Connection refused, checking the process '
221 (ret_code, _, _) = ssh.exec_command_sudo(cmd)
223 raise HoneycombError('Honeycomb on node {0} is still '
224 'running.'.format(node['host']),
225 enable_logging=False)
227 logger.info("Honeycomb on node {0} has stopped".
228 format(node['host']))
232 def configure_restconf_binding_address(node):
233 """Configure Honeycomb to accept restconf requests from all IP
234 addresses. IP version is determined by node data.
236 :param node: Information about a DUT node.
238 :raises HoneycombError: If the configuration could not be changed.
241 find = "restconf-binding-address"
243 IPv6Address(unicode(node["host"]))
244 # if management IP of the node is in IPv6 format
245 replace = '\\"restconf-binding-address\\": \\"0::0\\",'
246 except (AttributeError, AddressValueError):
247 replace = '\\"restconf-binding-address\\": \\"0.0.0.0\\",'
249 argument = '"/{0}/c\\ {1}"'.format(find, replace)
250 path = "{0}/config/honeycomb.json".format(Const.REMOTE_HC_DIR)
251 command = "sed -i {0} {1}".format(argument, path)
255 (ret_code, _, stderr) = ssh.exec_command_sudo(command)
257 raise HoneycombError("Failed to modify configuration on "
258 "node {0}, {1}".format(node, stderr))
261 def configure_jvpp_timeout(node, timeout=10):
262 """Configure timeout value for Java API commands Honeycomb sends to VPP.
264 :param node: Information about a DUT node.
265 :param timeout: Timeout value in seconds.
268 :raises HoneycombError: If the configuration could not be changed.
271 find = "jvpp-request-timeout"
272 replace = '\\"jvpp-request-timeout\\": {0}'.format(timeout)
274 argument = '"/{0}/c\\ {1}"'.format(find, replace)
275 path = "{0}/config/jvpp.json".format(Const.REMOTE_HC_DIR)
276 command = "sed -i {0} {1}".format(argument, path)
280 (ret_code, _, stderr) = ssh.exec_command_sudo(command)
282 raise HoneycombError("Failed to modify configuration on "
283 "node {0}, {1}".format(node, stderr))
286 def print_environment(nodes):
287 """Print information about the nodes to log. The information is defined
288 by commands in cmds tuple at the beginning of this method.
290 :param nodes: List of DUT nodes to get information about.
294 # TODO: When everything is set and running in VIRL env, transform this
295 # method to a keyword checking the environment.
303 "dpkg --list | grep openjdk",
304 "ls -la /opt/honeycomb")
307 if node['type'] == NodeType.DUT:
308 logger.info("Checking node {} ...".format(node['host']))
310 logger.info("Command: {}".format(cmd))
313 ssh.exec_command_sudo(cmd)
316 def print_ports(node):
317 """Uses "sudo netstat -anp | grep java" to print port where a java
320 :param node: Honeycomb node where we want to print the ports.
324 cmds = ("netstat -anp | grep java",
325 "ps -ef | grep [h]oneycomb")
327 logger.info("Checking node {} ...".format(node['host']))
329 logger.info("Command: {}".format(cmd))
332 ssh.exec_command_sudo(cmd)
335 def configure_log_level(node, level):
336 """Set Honeycomb logging to the specified level.
338 :param node: Honeycomb node.
339 :param level: Log level (INFO, DEBUG, TRACE).
344 find = 'logger name=\\"io.fd\\"'
345 replace = '<logger name=\\"io.fd\\" level=\\"{0}\\"/>'.format(level)
347 argument = '"/{0}/c\\ {1}"'.format(find, replace)
348 path = "{0}/config/logback.xml".format(Const.REMOTE_HC_DIR)
349 command = "sed -i {0} {1}".format(argument, path)
353 (ret_code, _, stderr) = ssh.exec_command_sudo(command)
355 raise HoneycombError("Failed to modify configuration on "
356 "node {0}, {1}".format(node, stderr))
359 def enable_module_features(node, *features):
360 """Configure Honeycomb to use VPP modules that are disabled by default.
362 ..Note:: If the module is not enabled in VPP, Honeycomb will
363 be unable to establish VPP connection.
365 :param node: Honeycomb node.
366 :param features: Features to enable.
368 :type features: string
369 :raises HoneycombError: If the configuration could not be changed.
372 disabled_features = {
373 "NSH": "io.fd.hc2vpp.vppnsh.impl.VppNshModule"
379 for feature in features:
380 if feature in disabled_features.keys():
381 # uncomment by replacing the entire line
382 find = replace = "{0}".format(disabled_features[feature])
384 argument = '"/{0}/c\\ {1}"'.format(find, replace)
385 path = "{0}/modules/*module-config"\
386 .format(Const.REMOTE_HC_DIR)
387 command = "sed -i {0} {1}".format(argument, path)
389 (ret_code, _, stderr) = ssh.exec_command_sudo(command)
391 raise HoneycombError("Failed to modify configuration on "
392 "node {0}, {1}".format(node, stderr))
394 raise HoneycombError(
395 "Unrecognized feature {0}.".format(feature))
398 def copy_java_libraries(node):
399 """Copy Java libraries installed by vpp-api-java package to honeycomb
402 This is a (temporary?) workaround for jvpp version mismatches.
404 :param node: Honeycomb node
410 (_, stdout, _) = ssh.exec_command_sudo(
411 "ls /usr/share/java | grep ^jvpp-*")
413 files = stdout.split("\n")[:-1]
416 # jvpp-registry-17.04.jar
417 # jvpp-core-17.04.jar
419 parts = item.split("-")
420 version = "{0}-SNAPSHOT".format(parts[2][:5])
421 artifact_id = "{0}-{1}".format(parts[0], parts[1])
423 directory = "{0}/lib/io/fd/vpp/{1}/{2}".format(
424 Const.REMOTE_HC_DIR, artifact_id, version)
425 cmd = "sudo mkdir -p {0}; " \
426 "sudo cp /usr/share/java/{1} {0}/{2}-{3}.jar".format(
427 directory, item, artifact_id, version)
429 (ret_code, _, stderr) = ssh.exec_command(cmd)
431 raise HoneycombError("Failed to copy JVPP libraries on "
432 "node {0}, {1}".format(node, stderr))
435 def setup_odl_client(node, odl_name):
436 """Start ODL client on the specified node.
438 Karaf should be located in /nfs/common, and VPP and Honeycomb should
439 already be running, otherwise the start will fail.
440 :param node: Node to start ODL client on.
441 :param odl_name: Name of ODL client version to use.
444 :raises HoneycombError: If Honeycomb fails to start.
447 logger.debug("Copying ODL Client to home dir.")
452 cmd = "cp -r /nfs/common/*karaf_{name}* ~/karaf".format(name=odl_name)
454 (ret_code, _, _) = ssh.exec_command_sudo(cmd)
455 if int(ret_code) != 0:
456 raise HoneycombError(
457 "Failed to copy ODL client on node {0}".format(node["host"]))
459 logger.console("\nStarting ODL client ...")
461 cmd = "~/*karaf*/bin/start"
465 (ret_code, _, _) = ssh.exec_command_sudo(cmd)
466 if int(ret_code) != 0:
467 raise HoneycombError('Node {0} failed to start ODL.'.
468 format(node['host']))
470 logger.info("Starting the ODL client on node {0} is "
471 "in progress ...".format(node['host']))
474 def check_odl_startup_state(node):
475 """Check the status of ODL client startup.
477 :param node: Honeycomb node.
479 :returns: True when ODL is started.
481 :raises HoneycombError: When the response is not code 200: OK.
484 path = HcUtil.read_path_from_url_file(
485 "odl_client/odl_netconf_connector")
486 expected_status_codes = (HTTPCodes.UNAUTHORIZED,
489 HTTPCodes.SERVICE_UNAVAILABLE,
490 HTTPCodes.INTERNAL_SERVER_ERROR)
492 status_code, _ = HTTPRequest.get(node, path, timeout=10,
493 enable_logging=False)
494 if status_code == HTTPCodes.OK:
495 logger.info("ODL client on node {0} is up and running".
496 format(node['host']))
497 elif status_code in expected_status_codes:
498 if status_code == HTTPCodes.UNAUTHORIZED:
499 logger.info('Unauthorized. If this triggers keyword '
500 'timeout, verify username and password.')
501 raise HoneycombError('ODL client on node {0} running but '
502 'not yet ready.'.format(node['host']),
503 enable_logging=False)
505 raise HoneycombError('Unexpected return code: {0}.'.
510 def mount_honeycomb_on_odl(node):
511 """Tell ODL client to mount Honeycomb instance over netconf.
513 :param node: Honeycomb node.
515 :raises HoneycombError: When the response is not code 200: OK.
518 path = HcUtil.read_path_from_url_file(
519 "odl_client/odl_netconf_connector")
521 url_file = "{0}/{1}".format(Const.RESOURCES_TPL_HC,
522 "odl_client/mount_honeycomb.xml")
524 with open(url_file) as template:
525 data = template.read()
527 status_code, _ = HTTPRequest.post(
528 node, path, headers={"Content-Type": "application/xml"},
529 payload=data, timeout=10, enable_logging=False)
531 if status_code == HTTPCodes.OK:
532 logger.info("ODL mount point configured successfully.")
533 elif status_code == HTTPCodes.CONFLICT:
534 logger.warn("ODL mount point was already configured.")
536 raise HoneycombError('Mount point configuration not successful')