CSIT-427: Honeycomb ietf-ACL tests - mixed layers
[csit.git] / resources / libraries / python / honeycomb / HcAPIKwACL.py
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:
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 ACL data structures using
15 Honeycomb REST API."""
16
17 from resources.libraries.python.topology import Topology
18 from resources.libraries.python.HTTPRequest import HTTPCodes
19 from resources.libraries.python.honeycomb.HoneycombSetup import HoneycombError
20 from resources.libraries.python.honeycomb.HoneycombUtil \
21     import HoneycombUtil as HcUtil
22 from resources.libraries.python.honeycomb.HoneycombUtil \
23     import DataRepresentation
24
25
26 class ACLKeywords(object):
27     """Implementation of keywords which make it possible to:
28     - add classify table(s),
29     - remove classify table(s),
30     - get operational data about classify table(s),
31     - add classify session(s),
32     - remove classify session(s),
33     - get operational data about classify sessions(s).
34     """
35
36     def __init__(self):
37         pass
38
39     @staticmethod
40     def _set_classify_table_properties(node, path, data=None):
41         """Set classify 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 to PUT is not
52         200 = OK.
53         """
54
55         if data:
56             status_code, resp = HcUtil.\
57                 put_honeycomb_data(node, "config_classify_table", data, path,
58                                    data_representation=DataRepresentation.JSON)
59         else:
60             status_code, resp = HcUtil.\
61                 delete_honeycomb_data(node, "config_classify_table", path)
62
63         if status_code != HTTPCodes.OK:
64             raise HoneycombError(
65                 "The configuration of classify table was not successful. "
66                 "Status code: {0}.".format(status_code))
67         return resp
68
69     @staticmethod
70     def add_classify_table(node, table):
71         """Add a classify table to the list of classify tables. The keyword does
72         not validate given data.
73
74         :param node: Honeycomb node.
75         :param table: Classify table to be added.
76         :type node: dict
77         :type table: dict
78         :return: Content of response.
79         :rtype: bytearray
80         """
81
82         path = "/classify-table/" + table["name"]
83         data = {"classify-table": [table, ]}
84         return ACLKeywords._set_classify_table_properties(node, path, data)
85
86     @staticmethod
87     def remove_all_classify_tables(node):
88         """Remove all classify tables defined on the node.
89
90         :param node: Honeycomb node.
91         :type node: dict
92         :return: Content of response.
93         :rtype: bytearray
94         """
95
96         return ACLKeywords._set_classify_table_properties(node, path="")
97
98     @staticmethod
99     def remove_classify_table(node, table_name):
100         """Remove the given classify table.
101
102         :param node: Honeycomb node.
103         :param table_name: Name of the classify table to be removed.
104         :type node: dict
105         :type table_name: str
106         :return: Content of response.
107         :rtype: bytearray
108         """
109
110         path = "/classify-table/" + table_name
111         return ACLKeywords._set_classify_table_properties(node, path)
112
113     @staticmethod
114     def get_all_classify_tables_oper_data(node):
115         """Get operational data about all classify tables present on the node.
116
117         :param node: Honeycomb node.
118         :type node: dict
119         :return: List of classify tables.
120         :rtype: list
121         """
122
123         status_code, resp = HcUtil.\
124             get_honeycomb_data(node, "oper_classify_table")
125
126         if status_code != HTTPCodes.OK:
127             raise HoneycombError(
128                 "Not possible to get operational information about the "
129                 "classify tables. Status code: {0}.".format(status_code))
130         try:
131             return resp["vpp-classifier"]["classify-table"]
132         except (KeyError, TypeError):
133             return []
134
135     @staticmethod
136     def get_classify_table_oper_data(node, table_name):
137         """Get operational data about the given classify table.
138
139         :param node: Honeycomb node.
140         :param table_name: Name of the classify table.
141         :type node: dict
142         :type table_name: str
143         :return: Operational data about the given classify table.
144         :rtype: dict
145         """
146
147         path = "/classify-table/" + table_name
148         status_code, resp = HcUtil.\
149             get_honeycomb_data(node, "oper_classify_table", path)
150
151         if status_code != HTTPCodes.OK:
152             raise HoneycombError(
153                 "Not possible to get operational information about the "
154                 "classify tables. Status code: {0}.".format(status_code))
155         try:
156             return resp["classify-table"][0]
157         except (KeyError, TypeError):
158             return []
159
160     @staticmethod
161     def get_all_classify_tables_cfg_data(node):
162         """Get configuration data about all classify tables present on the node.
163
164         :param node: Honeycomb node.
165         :type node: dict
166         :return: List of classify tables.
167         :rtype: list
168         """
169
170         status_code, resp = HcUtil.\
171             get_honeycomb_data(node, "config_classify_table")
172
173         if status_code != HTTPCodes.OK:
174             raise HoneycombError(
175                 "Not possible to get operational information about the "
176                 "classify tables. Status code: {0}.".format(status_code))
177         try:
178             return resp["vpp-classifier"]["classify-table"]
179         except (KeyError, TypeError):
180             return []
181
182     @staticmethod
183     def add_classify_session(node, table_name, session):
184         """Add a classify session to the classify table.
185
186         :param node: Honeycomb node.
187         :param table_name: Name of the classify table.
188         :param session: Classify session to be added to the classify table.
189         :type node: dict
190         :type table_name: str
191         :type session: dict
192         :return: Content of response.
193         :rtype: bytearray
194         """
195
196         path = "/classify-table/" + table_name + \
197                "/classify-session/" + session["match"]
198         data = {"classify-session": [session, ]}
199         return ACLKeywords._set_classify_table_properties(node, path, data)
200
201     @staticmethod
202     def remove_classify_session(node, table_name, session_match):
203         """Remove the given classify session from the classify table.
204
205         :param node: Honeycomb node.
206         :param table_name: Name of the classify table.
207         :param session_match: Classify session match.
208         :type node: dict
209         :type table_name: str
210         :type session_match: str
211         :return: Content of response.
212         :rtype: bytearray
213         """
214
215         path = "/classify-table/" + table_name + \
216                "/classify-session/" + session_match
217         return ACLKeywords._set_classify_table_properties(node, path)
218
219     @staticmethod
220     def get_all_classify_sessions_oper_data(node, table_name):
221         """Get operational data about all classify sessions in the classify
222         table.
223
224         :param node: Honeycomb node.
225         :param table_name: Name of the classify table.
226         :type node: dict
227         :type table_name: str
228         :return: List of classify sessions present in the classify table.
229         :rtype: list
230         """
231
232         table_data = ACLKeywords.get_classify_table_oper_data(node, table_name)
233         try:
234             return table_data["classify-table"][0]["classify-session"]
235         except (KeyError, TypeError):
236             return []
237
238     @staticmethod
239     def get_classify_session_oper_data(node, table_name, session_match):
240         """Get operational data about the given classify session in the classify
241         table.
242
243         :param node: Honeycomb node.
244         :param table_name: Name of the classify table.
245         :param session_match: Classify session match.
246         :type node: dict
247         :type table_name: str
248         :type session_match: str
249         :return: Classify session operational data.
250         :rtype: dict
251         """
252
253         path = "/classify-table/" + table_name + \
254                "/classify-session/" + session_match
255         status_code, resp = HcUtil.\
256             get_honeycomb_data(node, "oper_classify_table", path)
257
258         if status_code != HTTPCodes.OK:
259             raise HoneycombError(
260                 "Not possible to get operational information about the "
261                 "classify tables. Status code: {0}.".format(status_code))
262         try:
263             return resp["classify-session"][0]
264         except (KeyError, TypeError):
265             return {}
266
267     @staticmethod
268     def create_ietf_classify_chain(node, list_name, layer, data):
269         """Create classify chain using the ietf-acl node.
270
271         :param node: Honeycomb node.
272         :param list_name: Name for the classify list.
273         :param layer: Network layer to classify on.
274         :param data: Dictionary of settings to send to Honeycomb.
275         :type node: dict
276         :type list_name: str
277         :type layer: string
278         :type data: dict
279
280         :return: Content of response.
281         :rtype: bytearray
282         :raises HoneycombError: If the operation fails.
283         """
284         layer = layer.lower()
285         suffix_dict = {"l2": "eth",
286                        "l3_ip4": "ipv4",
287                        "l3_ip6": "ipv6",
288                        "mixed": "mixed"
289                        }
290         if layer == "l4":
291             raise NotImplementedError
292         try:
293             suffix = suffix_dict[layer]
294         except KeyError:
295             raise ValueError("Unexpected value of layer argument {0}."
296                              "Valid options are: {1}"
297                              .format(layer, suffix_dict.keys()))
298
299         if layer == "mixed":
300             path = "/acl/vpp-acl:{0}-acl/{1}"
301         else:
302             path = "/acl/ietf-access-control-list:{0}-acl/{1}"
303
304         path = path.format(suffix, list_name)
305
306         status_code, resp = HcUtil.put_honeycomb_data(
307             node, "config_ietf_classify_chain", data, path)
308
309         if status_code != HTTPCodes.OK:
310             raise HoneycombError(
311                 "Could not create classify chain."
312                 "Status code: {0}.".format(status_code))
313
314         return resp
315
316     @staticmethod
317     def set_ietf_interface_acl(node, interface, layer, direction, list_name,
318                                default_action, mode=None):
319         """Assign an interface to an ietf-acl classify chain.
320
321         :param node: Honeycomb node.
322         :param interface: Name of an interface on the node.
323         :param layer: Network layer to classify packets on.
324         Valid options are: L2, L3, L4. Mixed ACL not supported yet.
325         :param direction: Classify incoming or outgiong packets.
326         Valid options are: ingress, egress
327         :param list_name: Name of an ietf-acl classify chain.
328         :param default_action: Default classifier action: permit or deny.
329         :param mode: When using mixed layers, this specifies operational mode
330         of the interface - L2 or L3. If layer is not "mixed", this argument
331         will be ignored.
332         :type node: dict
333         :type interface: str or int
334         :type layer: str
335         :type direction: str
336         :type list_name: str
337         :type default_action: str
338         :type mode: str
339
340         :return: Content of response.
341         :rtype: bytearray
342         :raises HoneycombError: If the operation fails.
343         """
344
345         layer = layer.lower()
346         if mode is not None:
347             mode = mode.lower()
348         interface = Topology.convert_interface_reference(
349             node, interface, "name")
350
351         interface = interface.replace("/", "%2F")
352
353         if direction not in ("ingress", "egress"):
354             raise ValueError("Unknown traffic direction {0}. "
355                              "Valid options are: ingress, egress."
356                              .format(direction))
357
358         path = "/interface/{0}/ietf-acl/{1}/access-lists".format(
359             interface, direction)
360
361         types = {
362             "ietf": "ietf-access-control-list:{0}-acl",
363             "vpp": "vpp-acl:{0}-acl"}
364         layers = {
365             "l2": {"mode": "l2", "acl_type": types['ietf'].format("eth")},
366             "l3_ip4": {"mode": "l3", "acl_type": types['ietf'].format("ipv4")},
367             "l3_ip6": {"mode": "l3", "acl_type": types['ietf'].format("ipv6")},
368             "mixed": {"mode": mode, "acl_type": types['vpp'].format("mixed")}
369             }
370
371         if layer == "L4":
372             raise NotImplementedError
373         else:
374             try:
375                 data = {
376                     "access-lists": {
377                         "acl": [
378                             {
379                                 "type": layers[layer]['acl_type'],
380                                 "name": list_name
381                             }
382                         ],
383                         "default-action": default_action,
384                         "mode": layers[layer]['mode']
385                     }
386                 }
387             except KeyError:
388                 raise ValueError("Unknown network layer {0}. "
389                                  "Valid options are: {1}".format(
390                                     layer, layers.keys()))
391
392         status_code, resp = HcUtil.put_honeycomb_data(
393             node, "config_vpp_interfaces", data, path)
394
395         if status_code != HTTPCodes.OK:
396             raise HoneycombError(
397                 "Could not configure ACL on interface. "
398                 "Status code: {0}.".format(status_code))
399
400         return resp
401
402     @staticmethod
403     def delete_ietf_interface_acls(node, interface):
404         """Remove all ietf-acl assignments from an interface.
405
406         :param node: Honeycomb node.
407         :param interface: Name of an interface on the node.
408         :type node: dict
409         :type interface: str or int"""
410
411         interface = Topology.convert_interface_reference(
412             node, interface, "name")
413
414         interface = interface.replace("/", "%2F")
415
416         path = "/interface/{0}/ietf-acl/".format(interface)
417         status_code, _ = HcUtil.delete_honeycomb_data(
418             node, "config_vpp_interfaces", path)
419
420         if status_code != HTTPCodes.OK:
421             raise HoneycombError(
422                 "Could not remove ACL assignment from interface. "
423                 "Status code: {0}.".format(status_code))
424
425     @staticmethod
426     def delete_ietf_classify_chains(node):
427         """Remove all classify chains from the ietf-acl node.
428
429         :param node: Honeycomb node.
430         :type node: dict
431         """
432
433         status_code, _ = HcUtil.delete_honeycomb_data(
434             node, "config_ietf_classify_chain")
435
436         if status_code != HTTPCodes.OK:
437             raise HoneycombError(
438                 "Could not remove ietf-acl chain. "
439                 "Status code: {0}.".format(status_code))