Fix Tap failing tests
[csit.git] / resources / libraries / python / honeycomb / Routing.py
1 # Copyright (c) 2018 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:
5 #
6 #     http://www.apache.org/licenses/LICENSE-2.0
7 #
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.
13
14 """This module implements keywords to manipulate routing tables using
15 Honeycomb REST API."""
16
17 from robot.api import logger
18
19 from resources.libraries.python.topology import Topology
20 from resources.libraries.python.HTTPRequest import HTTPCodes
21 from resources.libraries.python.honeycomb.HoneycombSetup import HoneycombError
22 from resources.libraries.python.honeycomb.HoneycombUtil \
23     import HoneycombUtil as HcUtil
24 from resources.libraries.python.honeycomb.HoneycombUtil \
25     import DataRepresentation
26 from resources.libraries.python.VatExecutor import VatTerminal
27
28
29 class RoutingKeywords(object):
30     """Implementation of keywords which make it possible to:
31     - add/remove routing tables,
32     - add/remove routing table entries
33     - get operational data about routing tables,
34     """
35
36     def __init__(self):
37         pass
38
39     @staticmethod
40     def _set_routing_table_properties(node, path, data=None):
41         """Set routing table properties and check the return code.
42
43         :param node: Honeycomb node.
44         :param path: Path which is added to the base path to identify the data.
45         :param data: The new data to be set. If None, the item will be removed.
46         :type node: dict
47         :type path: str
48         :type data: dict
49         :returns: Content of response.
50         :rtype: bytearray
51         :raises HoneycombError: If the status code in response is not
52         200 = OK.
53         """
54
55         if data:
56             status_code, resp = HcUtil.\
57                 put_honeycomb_data(node, "config_routing_table", data, path,
58                                    data_representation=DataRepresentation.JSON)
59         else:
60             status_code, resp = HcUtil.\
61                 delete_honeycomb_data(node, "config_routing_table", path)
62
63         if status_code not in (HTTPCodes.OK, HTTPCodes.ACCEPTED):
64             if data is None and '"error-tag":"data-missing"' in resp:
65                 logger.debug("data does not exist in path.")
66             else:
67                 raise HoneycombError(
68                     "The configuration of routing table was not successful. "
69                     "Status code: {0}.".format(status_code))
70         return resp
71
72     @staticmethod
73     def configure_routing_table(node, name, ip_version, data, vrf=1,
74                                 special=False):
75         """Configure a routing table according to the data provided.
76
77         :param node: Honeycomb node.
78         :param name: Name for the table.
79         :param ip_version: IP protocol version, ipv4 or ipv6.
80         :param data: Route configuration that should be set.
81         :param vrf: vrf-id to attach configuration to.
82         :param special: Must be True if the configuration is a special route.
83         :type node: dict
84         :type name: str
85         :type ip_version: str
86         :type data: dict
87         :type vrf: int
88         :type special: bool
89         :returns: Content of response.
90         :rtype: bytearray
91         """
92         if special:
93             ip_version = "hc2vpp-ietf-{0}-unicast-routing:{0}".format(
94                 ip_version)
95             protocol = "vpp-routing:vpp-protocol-attributes"
96         else:
97             ip_version = ip_version
98             protocol = "vpp-protocol-attributes"
99
100         full_data = {
101             "control-plane-protocol": [
102                 {
103                     "name": name,
104                     "description": "hc2vpp-csit test route",
105                     "type": "static",
106                     protocol: {
107                         "primary-vrf": vrf
108                     },
109                     "static-routes": {
110                         ip_version: {
111                             "route": data
112                         }
113                     }
114                 }
115             ]
116         }
117
118         path = "/control-plane-protocol/hc2vpp-ietf-routing:static/{0}".format(
119             name)
120         return RoutingKeywords._set_routing_table_properties(
121             node, path, full_data)
122
123     @staticmethod
124     def delete_routing_table(node, name):
125         """Delete the specified routing table from configuration data.
126
127         :param node: Honeycomb node.
128         :param name: Name of the table.
129         :type node: dict
130         :type name: str
131         :returns: Content of response.
132         :rtype: bytearray
133         """
134
135         path = "/control-plane-protocol/hc2vpp-ietf-routing:static/{0}".format(
136             name)
137         return RoutingKeywords._set_routing_table_properties(node, path)
138
139     @staticmethod
140     def get_routing_table_oper(node, name, ip_version):
141         """Retrieve operational data about the specified routing table.
142
143         :param node: Honeycomb node.
144         :param name: Name of the routing table.
145         :param ip_version: IP protocol version, ipv4 or ipv6.
146         :type node: dict
147         :type name: str
148         :type ip_version: str
149         :returns: Routing table operational data.
150         :rtype: list
151         :raises HoneycombError: If the operation fails.
152         """
153
154         path = "/control-plane-protocol/hc2vpp-ietf-routing:static/{0}".format(
155             name)
156         status_code, resp = HcUtil.\
157             get_honeycomb_data(node, "oper_routing_table", path)
158
159         if status_code != HTTPCodes.OK:
160             raise HoneycombError(
161                 "Not possible to get operational information about the "
162                 "routing tables. Status code: {0}.".format(status_code))
163
164         data = RoutingKeywords.clean_routing_oper_data(
165             resp['control-plane-protocol'][0]['static-routes']
166             ['hc2vpp-ietf-{0}-unicast-routing:{0}'.format(ip_version)]['route'])
167
168         return data
169
170     @staticmethod
171     def clean_routing_oper_data(data):
172         """Prepare received routing operational data to be verified against
173          expected data.
174
175         :param data: Routing operational data.
176         :type data: list
177         :returns: Routing operational data without entry ID numbers.
178         :rtype: list
179         """
180
181         for item in data:
182             # ID values are auto-incremented based on existing routes in VPP
183             item.pop("id", None)
184             if "next-hop-list" in item.keys():
185                 for item2 in item["next-hop-list"]["next-hop"]:
186                     item2.pop("id", None)
187
188             if "next-hop-list" in item.keys():
189                 # List items come in random order
190                 item["next-hop-list"]["next-hop"].sort()
191
192         return data
193
194     @staticmethod
195     def log_routing_configuration(node):
196         """Retrieve route configuration using VAT and print the response
197          to robot log.
198
199          :param node: VPP node.
200          :type node: dict
201          """
202
203         with VatTerminal(node) as vat:
204             vat.vat_terminal_exec_cmd("ip_fib_dump")
205
206     @staticmethod
207     def configure_interface_slaac(node, interface, slaac_data=None):
208         """Configure SLAAC on the specified interfaces.
209
210         :param node: Honeycomb node.
211         :param interface: Interface to configure SLAAC.
212         :param slaac_data: Dictionary of configurations to apply. \
213         If it is None then the existing configuration is removed.
214         :type node: dict
215         :type interface: str
216         :type slaac_data: dict of dicts
217         :returns: Content of response.
218         :rtype: bytearray
219         :raises HoneycombError: If RA could not be configured.
220         """
221
222         interface = Topology.convert_interface_reference(
223             node, interface, 'name')
224         interface = interface.replace('/', '%2F')
225         path = 'interface/' + interface + '/ipv6/ipv6-router-advertisements'
226
227         if not slaac_data:
228             status_code, _ = HcUtil.delete_honeycomb_data(
229                 node, 'config_slaac', path)
230         else:
231             data = {
232                 'ipv6-router-advertisements': slaac_data
233             }
234
235             status_code, _ = HcUtil.put_honeycomb_data(
236                 node, 'config_slaac', data, path)
237
238         if status_code not in (HTTPCodes.OK, HTTPCodes.ACCEPTED):
239             raise HoneycombError(
240                 'Configuring SLAAC failed. Status code:{0}'.format(status_code))
241
242     @staticmethod
243     def get_interface_slaac_oper_data(node, interface):
244         """Get operational data about SLAAC table present on the node.
245
246         :param node: Honeycomb node.
247         :param interface: Interface SLAAC data are retrieved from.
248         :type node: dict
249         :type interface: str
250         :returns: dict of SLAAC operational data.
251         :rtype: dict
252         :raises HoneycombError: If status code differs from successful.
253         """
254         interface = Topology.convert_interface_reference(
255             node, interface, 'name')
256         interface = interface.replace('/', '%2F')
257         path = 'interface/' + interface + '/ipv6/ipv6-router-advertisements'
258
259         status_code, resp = HcUtil.\
260             get_honeycomb_data(node, "config_slaac", path)
261
262         if status_code != HTTPCodes.OK:
263             raise HoneycombError(
264                 "Not possible to get operational information about SLAAC. "
265                 "Status code: {0}.".format(status_code))
266         try:
267             dict_of_str = resp[
268                 'hc2vpp-ietf-ipv6-unicast-routing:ipv6-router-advertisements']
269             return {k: str(v) for k, v in dict_of_str.items()}
270         except (KeyError, TypeError):
271             return {}
272
273     @staticmethod
274     def configure_policer(node, policy_name, policer_data=None):
275         """Configure Policer on the specified node.
276
277         :param node: Honeycomb node.
278         :param policer_data: Dictionary of configurations to apply. \
279         If it is None then the existing configuration is removed.
280         :type node: dict
281         :type policer_data: dict
282         :returns: Content of response.
283         :rtype: bytearray
284         :raises HoneycombError: If policer could not be configured.
285         """
286
287         path = '/' + policy_name
288
289         if not policer_data:
290             status_code, _ = HcUtil.delete_honeycomb_data(
291                 node, 'config_policer', path)
292         else:
293             data = {
294                 'policer': policer_data
295             }
296
297             status_code, _ = HcUtil.put_honeycomb_data(
298                 node, 'config_policer', data, path)
299
300         if status_code not in (HTTPCodes.OK, HTTPCodes.ACCEPTED):
301             raise HoneycombError(
302                 'Configuring policer failed. Status code:{0}'\
303                     .format(status_code))
304
305     @staticmethod
306     def get_policer_oper_data(node, policy_name):
307         """Get operational data about Policer on the node.
308
309         :param node: Honeycomb node.
310         :type node: dict
311         :returns: dict of Policer operational data.
312         :rtype: dict
313         :raises HoneycombError: If status code differs from successful.
314         """
315
316         path = '/' + policy_name
317
318         status_code, resp = HcUtil.\
319             get_honeycomb_data(node, "oper_policer", path)
320
321         if status_code != HTTPCodes.OK:
322             raise HoneycombError(
323                 "Not possible to get operational information about Policer. "
324                 "Status code: {0}.".format(status_code))
325         try:
326             return resp['policer']
327         except (KeyError, TypeError):
328             return {}