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