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 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 find_odl_client(node):
412 """Check if there is a karaf directory in home.
414 :param node: Honeycomb node.
416 :returns: True if ODL client is present on node, else False.
422 (ret_code, stdout, _) = ssh.exec_command_sudo(
426 return not bool(ret_code)
429 def start_odl_client(node):
430 """Start ODL client on the specified node.
432 karaf should be located in home directory, and VPP and Honeycomb should
433 already be running, otherwise the start will fail.
434 :param node: Nodes to start ODL client on.
436 :raises HoneycombError: If Honeycomb fails to start.
439 logger.console("\nStarting ODL client ...")
441 cmd = "~/*karaf*/bin/start"
445 (ret_code, _, _) = ssh.exec_command_sudo(cmd)
446 if int(ret_code) != 0:
447 raise HoneycombError('Node {0} failed to start ODL.'.
448 format(node['host']))
450 logger.info("Starting the ODL client on node {0} is "
451 "in progress ...".format(node['host']))
454 def check_odl_startup_state(node):
455 """Check the status of ODL client startup.
457 :param node: Honeycomb node.
459 :returns: True when ODL is started.
461 :raises HoneycombError: When the response is not code 200: OK.
464 path = HcUtil.read_path_from_url_file(
465 "odl_client/odl_netconf_connector")
466 expected_status_codes = (HTTPCodes.UNAUTHORIZED,
469 HTTPCodes.SERVICE_UNAVAILABLE,
470 HTTPCodes.INTERNAL_SERVER_ERROR)
472 status_code, _ = HTTPRequest.get(node, path, timeout=10,
473 enable_logging=False)
474 if status_code == HTTPCodes.OK:
475 logger.info("ODL client on node {0} is up and running".
476 format(node['host']))
477 elif status_code in expected_status_codes:
478 if status_code == HTTPCodes.UNAUTHORIZED:
479 logger.info('Unauthorized. If this triggers keyword '
480 'timeout, verify username and password.')
481 raise HoneycombError('ODL client on node {0} running but '
482 'not yet ready.'.format(node['host']),
483 enable_logging=False)
485 raise HoneycombError('Unexpected return code: {0}.'.
490 def mount_honeycomb_on_odl(node):
491 """Tell ODL client to mount Honeycomb instance over netconf.
493 :param node: Honeycomb node.
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")
501 url_file = "{0}/{1}".format(Const.RESOURCES_TPL_HC,
502 "odl_client/mount_honeycomb.xml")
504 with open(url_file) as template:
505 data = template.read()
507 status_code, _ = HTTPRequest.post(
508 node, path, headers={"Content-Type": "application/xml"},
509 payload=data, timeout=10, enable_logging=False)
511 if status_code == HTTPCodes.OK:
512 logger.info("ODL mount point configured successfully.")
513 elif status_code == HTTPCodes.CONFLICT:
514 logger.warn("ODL mount point was already configured.")
516 raise HoneycombError('Mount point configuration not successful')