4c6a3f205bc5e6689b467c451ed1efcf500a9ba2
[csit.git] / resources / libraries / python / honeycomb / Routing.py
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:
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         :return: 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             "routing-protocol": [
102                 {
103                     "name": name,
104                     "description": "hc2vpp-csit test route",
105                     "enabled": "true",
106                     "type": "static",
107                     protocol: {
108                         "primary-vrf": vrf
109                     },
110                     "static-routes": {
111                         ip_version: {
112                             "route": data
113                         }
114                     }
115                 }
116             ]
117         }
118
119         path = "/routing-protocol/{0}".format(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 = "/routing-protocol/{0}".format(name)
136         return RoutingKeywords._set_routing_table_properties(node, path)
137
138     @staticmethod
139     def get_routing_table_oper(node, name, ip_version):
140         """Retrieve operational data about the specified routing table.
141
142         :param node: Honeycomb node.
143         :param name: Name of the routing table.
144         :param ip_version: IP protocol version, ipv4 or ipv6.
145         :type node: dict
146         :type name: str
147         :type ip_version: str
148         :returns: Routing table operational data.
149         :rtype: list
150         :raises HoneycombError: If the operation fails.
151         """
152
153         path = "/routing-protocol/{0}".format(name)
154         status_code, resp = HcUtil.\
155             get_honeycomb_data(node, "oper_routing_table", path)
156
157         if status_code != HTTPCodes.OK:
158             raise HoneycombError(
159                 "Not possible to get operational information about the "
160                 "routing tables. Status code: {0}.".format(status_code))
161
162         data = RoutingKeywords.clean_routing_oper_data(
163             resp['routing-protocol'][0]['static-routes']
164             ['hc2vpp-ietf-{0}-unicast-routing:{0}'.format(ip_version)]['route'])
165
166         return data
167
168     @staticmethod
169     def clean_routing_oper_data(data):
170         """Prepare received routing operational data to be verified against
171          expected data.
172
173         :param data: Routing operational data.
174         :type data: list
175         :returns: Routing operational data without entry ID numbers.
176         :rtype: list
177         """
178
179         for item in data:
180             # ID values are auto-incremented based on existing routes in VPP
181             item.pop("id", None)
182             if "next-hop-list" in item.keys():
183                 for item2 in item["next-hop-list"]["next-hop"]:
184                     item2.pop("id", None)
185
186             if "next-hop-list" in item.keys():
187                 # List items come in random order
188                 item["next-hop-list"]["next-hop"].sort()
189
190         return data
191
192     @staticmethod
193     def log_routing_configuration(node):
194         """Retrieve route configuration using VAT and print the response
195          to robot log.
196
197          :param node: VPP node.
198          :type node: dict
199          """
200
201         with VatTerminal(node) as vat:
202             vat.vat_terminal_exec_cmd("ip_fib_dump")
203
204     @staticmethod
205     def configure_interface_slaac(node, interface, slaac_data=None):
206         """Configure SLAAC on the specified interfaces.
207
208         :param node: Honeycomb node.
209         :param interface: Interface to configure SLAAC.
210         :param slaac_data: Dictionary of configurations to apply. \
211         If it is None then the existing configuration is removed.
212         :type node: dict
213         :type interface: str
214         :type slaac_data: dict of dicts
215         :returns: Content of response.
216         :rtype: bytearray
217         :raises HoneycombError: If RA could not be configured.
218         """
219
220         interface = Topology.convert_interface_reference(
221             node, interface, 'name')
222         interface_orig = interface
223         interface = interface.replace('/', '%2F')
224         path = 'interface/' + interface
225
226         if not slaac_data:
227             status_code, _ = HcUtil.delete_honeycomb_data(
228                 node, 'config_slaac', path)
229         else:
230             data = {
231                 'interface': [
232                     {
233                         'name': interface_orig,
234                         'ipv6-router-advertisements': slaac_data
235                     }
236                 ]
237             }
238
239             status_code, _ = HcUtil.put_honeycomb_data(
240                 node, 'config_slaac', data, path)
241
242         if status_code not in (HTTPCodes.OK, HTTPCodes.ACCEPTED):
243             raise HoneycombError(
244                 'Configuring SLAAC failed. Status code:{0}'.format(status_code))
245
246     @staticmethod
247     def get_interface_slaac_oper_data(node, interface):
248         """Get operational data about SLAAC table present on the node.
249
250         :param node: Honeycomb node.
251         :param interface: Interface SLAAC data are retrieved from.
252         :type node: dict
253         :type interface: str
254         :returns: dict of SLAAC operational data.
255         :rtype: dict
256         :raises HoneycombError: If status code differs from successful.
257         """
258         interface = Topology.convert_interface_reference(
259             node, interface, 'name')
260         interface = interface.replace('/', '%2F')
261         path = 'interface/' + interface
262
263         status_code, resp = HcUtil.\
264             get_honeycomb_data(node, "config_slaac", path)
265
266         if status_code != HTTPCodes.OK:
267             raise HoneycombError(
268                 "Not possible to get operational information about SLAAC. "
269                 "Status code: {0}.".format(status_code))
270         try:
271             dict_of_str = resp['interface'][0][
272                 'hc2vpp-ietf-ipv6-unicast-routing:ipv6-router-advertisements']
273             return {k: str(v) for k, v in dict_of_str.items()}
274         except (KeyError, TypeError):
275             return {}
276
277     @staticmethod
278     def configure_policer(node, policy_name, policer_data=None):
279         """Configure Policer on the specified node.
280
281         :param node: Honeycomb node.
282         :param policer_data: Dictionary of configurations to apply. \
283         If it is None then the existing configuration is removed.
284         :type node: dict
285         :type policer_data: dict
286         :returns: Content of response.
287         :rtype: bytearray
288         :raises HoneycombError: If policer could not be configured.
289         """
290
291         path = '/' + policy_name
292
293         if not policer_data:
294             status_code, _ = HcUtil.delete_honeycomb_data(
295                 node, 'config_policer', path)
296         else:
297             data = {
298                 'policer': policer_data
299             }
300
301             status_code, _ = HcUtil.put_honeycomb_data(
302                 node, 'config_policer', data, path)
303
304         if status_code not in (HTTPCodes.OK, HTTPCodes.ACCEPTED):
305             raise HoneycombError(
306                 'Configuring policer failed. Status code:{0}'\
307                     .format(status_code))
308
309     @staticmethod
310     def get_policer_oper_data(node, policy_name):
311         """Get operational data about Policer on the node.
312
313         :param node: Honeycomb node.
314         :type node: dict
315         :returns: dict of Policer operational data.
316         :rtype: dict
317         :raises HoneycombError: If status code differs from successful.
318         """
319
320         path = '/' + policy_name
321
322         status_code, resp = HcUtil.\
323             get_honeycomb_data(node, "oper_policer", path)
324
325         if status_code != HTTPCodes.OK:
326             raise HoneycombError(
327                 "Not possible to get operational information about Policer. "
328                 "Status code: {0}.".format(status_code))
329         try:
330             return resp['policer']
331         except (KeyError, TypeError):
332             return {}