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 """Implements keywords for Honeycomb setup."""
17 from xml.etree import ElementTree as ET
19 from robot.api import logger
21 from resources.libraries.python.topology import NodeType
22 from resources.libraries.python.ssh import SSH
23 from resources.libraries.python.HTTPRequest import HTTPRequest, \
24 HTTPRequestError, HTTP_CODES
25 from resources.libraries.python.constants import Constants as C
28 class HoneycombError(Exception):
29 """Exception(s) raised by methods working with Honeycomb."""
31 def __init__(self, msg, enable_logging=True):
32 """Sets the exception message and enables / disables logging
34 It is not wanted to log errors when using these keywords together
35 with keywords like "Wait until keyword succeeds".
37 :param msg: Message to be displayed and logged
38 :param enable_logging: When True, logging is enabled, otherwise
41 :type enable_logging: bool
43 super(HoneycombError, self).__init__()
45 self._repr_msg = self.__module__ + '.' + \
46 self.__class__.__name__ + ": " + self._msg
48 logger.error(self._msg)
49 logger.debug(self._repr_msg)
52 return repr(self._repr_msg)
55 return str(self._repr_msg)
58 class HoneycombSetup(object):
59 """Implements keywords for Honeycomb setup."""
65 def start_honeycomb_on_all_duts(nodes):
66 """Start honeycomb on all DUT nodes in topology.
68 :param nodes: all nodes in topology
71 logger.console("Starting honeycomb service")
73 for node in nodes.values():
74 if node['type'] == NodeType.DUT:
75 HoneycombSetup.start_honeycomb(node)
78 def start_honeycomb(node):
79 """Start up honeycomb on DUT node.
81 :param node: DUT node with honeycomb
83 :return: ret_code, stdout, stderr
85 :raises HoneycombError: if Honeycomb fails to start.
90 cmd = os.path.join(C.REMOTE_HC_DIR, "start")
91 (ret_code, stdout, stderr) = ssh.exec_command_sudo(cmd)
92 if int(ret_code) != 0:
93 logger.debug('stdout: {0}'.format(stdout))
94 logger.debug('stderr: {0}'.format(stderr))
95 raise HoneycombError('Node {0} failed to start honeycomb'.
97 return ret_code, stdout, stderr
100 def stop_honeycomb_on_all_duts(nodes):
101 """Stop the honeycomb service on all DUTs.
103 :param nodes: nodes in topology
105 :return: ret_code, stdout, stderr
107 :raises HoneycombError: if Honeycomb failed to stop.
109 logger.console("Shutting down honeycomb service")
111 for node in nodes.values():
112 if node['type'] == NodeType.DUT:
116 cmd = os.path.join(C.REMOTE_HC_DIR, "stop")
117 (ret_code, stdout, stderr) = ssh.exec_command_sudo(cmd)
118 if int(ret_code) != 0:
119 logger.debug('stdout: {0}'.format(stdout))
120 logger.debug('stderr: {0}'.format(stderr))
121 errors.append(node['host'])
123 logger.info("Honeycomb was successfully stopped on node {0}.".
124 format(node['host']))
126 raise HoneycombError('Node(s) {0} failed to stop honeycomb.'.
130 def check_honeycomb_startup_state(nodes):
131 """Check state of honeycomb service during startup.
133 Reads html path from template file vpp_version.url
135 Honeycomb node replies with connection refused or the following status
136 codes depending on startup progress: codes 200, 401, 403, 404, 503
138 :param nodes: nodes in topology
140 :return: True if all GETs returned code 200(OK)
144 url_file = os.path.join(C.RESOURCES_TPL_HC, "vpp_version.url")
145 with open(url_file) as template:
146 data = template.readline()
148 expected_status_codes = (HTTP_CODES["UNAUTHORIZED"],
149 HTTP_CODES["FORBIDDEN"],
150 HTTP_CODES["NOT_FOUND"],
151 HTTP_CODES["SERVICE_UNAVAILABLE"])
153 for node in nodes.values():
154 if node['type'] == NodeType.DUT:
155 status_code, _ = HTTPRequest.get(node, data, timeout=10,
156 enable_logging=False)
157 if status_code == HTTP_CODES["OK"]:
159 elif status_code in expected_status_codes:
160 if status_code == HTTP_CODES["UNAUTHORIZED"]:
161 logger.info('Unauthorized. If this triggers keyword '
162 'timeout, verify honeycomb '
163 'username and password')
164 raise HoneycombError('Honeycomb on node {0} running but '
165 'not yet ready.'.format(node['host']),
166 enable_logging=False)
168 raise HoneycombError('Unexpected return code: {0}'.
173 def check_honeycomb_shutdown_state(nodes):
174 """Check state of honeycomb service during shutdown.
176 Honeycomb node replies with connection refused or the following status
177 codes depending on shutdown progress: codes 200, 404
179 :param nodes: nodes in topology
181 :return: True if all GETs fail to connect
185 for node in nodes.values():
186 if node['type'] == NodeType.DUT:
188 status_code, _ = HTTPRequest.get(node, '/index.html',
190 enable_logging=False)
191 if status_code == HTTP_CODES["OK"]:
192 raise HoneycombError('Honeycomb on node {0} is still '
193 'running'.format(node['host']),
194 enable_logging=False)
195 elif status_code == HTTP_CODES["NOT_FOUND"]:
196 raise HoneycombError('Honeycomb on node {0} is shutting'
197 ' down'.format(node['host']),
198 enable_logging=False)
200 raise HoneycombError('Unexpected return code: {'
201 '0}'.format(status_code))
202 except HTTPRequestError:
203 logger.debug('Connection refused')
209 def add_vpp_to_honeycomb_network_topology(nodes, headers):
210 """Add vpp node to Honeycomb network topology.
212 :param nodes: all nodes in test topology
213 :param headers: headers to be used with PUT requests
216 :return: status code and response from PUT requests
218 :raises HoneycombError: if a node was not added to honeycomb topology
220 Reads HTML path from template file config_topology_node.url
221 Path to the node to be added, e.g.:
222 ("/restconf/config/network-topology:network-topology"
223 "/topology/topology-netconf/node/")
224 There must be "/" at the end, as generated node name is added
227 Reads payload data from template file add_vpp_to_topology.xml
228 Information about node as XML structure, e.g.:
229 <node xmlns="urn:TBD:params:xml:ns:yang:network-topology">
233 <host xmlns="urn:opendaylight:netconf-node-topology">
236 <port xmlns="urn:opendaylight:netconf-node-topology">
239 <username xmlns="urn:opendaylight:netconf-node-topology">
242 <password xmlns="urn:opendaylight:netconf-node-topology">
245 <tcp-only xmlns="urn:opendaylight:netconf-node-topology">
248 <keepalive-delay xmlns="urn:opendaylight:netconf-node-topology">
252 NOTE: The placeholders:
258 MUST be there as they are replaced by correct values.
261 with open(os.path.join(C.RESOURCES_TPL_HC, "config_topology_node.url"))\
263 path = template.readline()
266 xml_data = ET.parse(os.path.join(C.RESOURCES_TPL_HC,
267 "add_vpp_to_topology.xml"))
268 except ET.ParseError as err:
269 raise HoneycombError(repr(err))
270 data = ET.tostring(xml_data.getroot())
274 for node_name, node in nodes.items():
275 if node['type'] == NodeType.DUT:
277 payload = data.format(
280 vpp_port=node['honeycomb']["netconf_port"],
281 user=node['honeycomb']["user"],
282 passwd=node['honeycomb']["passwd"])
283 status_code, resp = HTTPRequest.put(
285 path=path + '/' + node_name,
288 if status_code != HTTP_CODES["OK"]:
289 raise HoneycombError(
290 "VPP {0} was not added to topology. "
291 "Status code: {1}".format(node["host"],
294 status_codes.append(status_code)
295 responses.append(resp)
297 except HTTPRequestError as err:
298 raise HoneycombError("VPP {0} was not added to topology.\n"
299 "{1}".format(node["host"], repr(err)))
301 return status_codes, responses