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