469a707c3297b3dcc1d29288f1a2aaa10f228188
[csit.git] / resources / libraries / python / Classify.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 """Classify utilities library."""
15
16 from robot.api import logger
17
18 from resources.libraries.python.VatExecutor import VatExecutor, VatTerminal
19 from resources.libraries.python.topology import Topology
20
21
22 class Classify(object):
23     """Classify utilities."""
24
25     @staticmethod
26     def vpp_creates_classify_table_l3(node, ip_version, direction):
27         """Create classify table for IP address filtering.
28
29         :param node: VPP node to create classify table.
30         :param ip_version: Version of IP protocol.
31         :param direction: Direction of traffic - src/dst.
32         :type node: dict
33         :type ip_version: str
34         :type direction: str
35         :returns (table_index, skip_n, match_n)
36         table_index: Classify table index.
37         skip_n: Number of skip vectors.
38         match_n: Number of match vectors.
39         :rtype: tuple(int, int, int)
40         :raises RuntimeError: If VPP can't create table.
41         """
42
43         output = VatExecutor.cmd_from_template(node, "classify_add_table.vat",
44                                                ip_version=ip_version,
45                                                direction=direction)
46
47         if output[0]["retval"] == 0:
48             table_index = output[0]["new_table_index"]
49             skip_n = output[0]["skip_n_vectors"]
50             match_n = output[0]["match_n_vectors"]
51             logger.trace('Classify table with table_index {} created on node {}'
52                          .format(table_index, node['host']))
53         else:
54             raise RuntimeError('Unable to create classify table on node {}'
55                                .format(node['host']))
56
57         return table_index, skip_n, match_n
58
59     @staticmethod
60     def vpp_creates_classify_table_l2(node, direction):
61         """Create classify table for MAC address filtering.
62
63         :param node: VPP node to create classify table.
64         :param direction: Direction of traffic - src/dst.
65         :type node: dict
66         :type direction: str
67         :returns (table_index, skip_n, match_n)
68         table_index: Classify table index.
69         skip_n: Number of skip vectors.
70         match_n: Number of match vectors.
71         :rtype: tuple(int, int, int)
72         :raises RuntimeError: If VPP can't create table.
73         """
74         output = VatExecutor.cmd_from_template(node,
75                                                "classify_add_table_l2.vat",
76                                                direction=direction)
77
78         if output[0]["retval"] == 0:
79             table_index = output[0]["new_table_index"]
80             skip_n = output[0]["skip_n_vectors"]
81             match_n = output[0]["match_n_vectors"]
82             logger.trace('Classify table with table_index {} created on node {}'
83                          .format(table_index, node['host']))
84         else:
85             raise RuntimeError('Unable to create classify table on node {}'
86                                .format(node['host']))
87
88         return table_index, skip_n, match_n
89
90     @staticmethod
91     def vpp_creates_classify_table_hex(node, hex_mask):
92         """Create classify table with hex mask.
93
94         :param node: VPP node to create classify table based on hex mask.
95         :param hex_mask: Classify hex mask.
96         :type node: dict
97         :type hex_mask: str
98         :returns (table_index, skip_n, match_n)
99         table_index: Classify table index.
100         skip_n: Number of skip vectors.
101         match_n: Number of match vectors.
102         :rtype: tuple(int, int, int)
103         :raises RuntimeError: If VPP can't create table.
104         """
105         output = VatExecutor.cmd_from_template(node,
106                                                "classify_add_table_hex.vat",
107                                                hex_mask=hex_mask)
108
109         if output[0]["retval"] == 0:
110             table_index = output[0]["new_table_index"]
111             skip_n = output[0]["skip_n_vectors"]
112             match_n = output[0]["match_n_vectors"]
113             logger.trace('Classify table with table_index {} created on node {}'
114                          .format(table_index, node['host']))
115         else:
116             raise RuntimeError('Unable to create classify table on node {}'
117                                .format(node['host']))
118
119         return table_index, skip_n, match_n
120
121     @staticmethod
122     def vpp_configures_classify_session_l3(node, acl_method, table_index,
123                                            skip_n, match_n, ip_version,
124                                            direction, address):
125         """Configuration of classify session for IP address filtering.
126
127         :param node: VPP node to setup classify session.
128         :param acl_method: ACL method - deny/permit.
129         :param table_index: Classify table index.
130         :param skip_n: Number of skip vectors based on mask.
131         :param match_n: Number of match vectors based on mask.
132         :param ip_version: Version of IP protocol.
133         :param direction: Direction of traffic - src/dst.
134         :param address: IPv4 or IPv6 address.
135         :type node: dict
136         :type acl_method: str
137         :type table_index: int
138         :type skip_n: int
139         :type match_n: int
140         :type ip_version: str
141         :type direction: str
142         :type address: str
143         """
144         with VatTerminal(node) as vat:
145             vat.vat_terminal_exec_cmd_from_template("classify_add_session.vat",
146                                                     acl_method=acl_method,
147                                                     table_index=table_index,
148                                                     skip_n=skip_n,
149                                                     match_n=match_n,
150                                                     ip_version=ip_version,
151                                                     direction=direction,
152                                                     address=address)
153
154     @staticmethod
155     def vpp_configures_classify_session_l2(node, acl_method, table_index,
156                                            skip_n, match_n, direction, address):
157         """Configuration of classify session for MAC address filtering.
158
159         :param node: VPP node to setup classify session.
160         :param acl_method: ACL method - deny/permit.
161         :param table_index: Classify table index.
162         :param skip_n: Number of skip vectors based on mask.
163         :param match_n: Number of match vectors based on mask.
164         :param direction: Direction of traffic - src/dst.
165         :param address: IPv4 or IPv6 address.
166         :type node: dict
167         :type acl_method: str
168         :type table_index: int
169         :type skip_n: int
170         :type match_n: int
171         :type direction: str
172         :type address: str
173         """
174         with VatTerminal(node) as vat:
175             vat.vat_terminal_exec_cmd_from_template(
176                 "classify_add_session_l2.vat",
177                 acl_method=acl_method,
178                 table_index=table_index,
179                 skip_n=skip_n,
180                 match_n=match_n,
181                 direction=direction,
182                 address=address)
183
184     @staticmethod
185     def vpp_configures_classify_session_hex(node, acl_method, table_index,
186                                             skip_n, match_n, hex_value):
187         """Configuration of classify session with hex value.
188
189         :param node: VPP node to setup classify session.
190         :param acl_method: ACL method - deny/permit.
191         :param table_index: Classify table index.
192         :param skip_n: Number of skip vectors based on mask.
193         :param match_n: Number of match vectors based on mask.
194         :param hex_value: Classify hex value.
195         :type node: dict
196         :type acl_method: str
197         :type table_index: int
198         :type skip_n: int
199         :type match_n: int
200         :type hex_value: str
201         """
202         with VatTerminal(node) as vat:
203             vat.vat_terminal_exec_cmd_from_template(
204                 "classify_add_session_hex.vat",
205                 acl_method=acl_method,
206                 table_index=table_index,
207                 skip_n=skip_n,
208                 match_n=match_n,
209                 hex_value=hex_value)
210
211     @staticmethod
212     def vpp_configures_classify_session_generic(node, session_type, table_index,
213                                                 skip_n, match_n, match,
214                                                 match2=''):
215         """Configuration of classify session.
216
217         :param node: VPP node to setup classify session.
218         :param session_type: Session type - hit-next, l2-hit-next, acl-hit-next
219         or policer-hit-next, and their respective parameters.
220         :param table_index: Classify table index.
221         :param skip_n: Number of skip vectors based on mask.
222         :param match_n: Number of match vectors based on mask.
223         :param match: Match value - l2, l3, l4 or hex, and their
224         respective parameters.
225         :param match2: Additional match values, to avoid using overly long
226         variables in RobotFramework.
227         :type node: dict
228         :type session_type: str
229         :type table_index: int
230         :type skip_n: int
231         :type match_n: int
232         :type match: str
233         :type match2: str
234         """
235
236         match = ' '.join((match, match2))
237
238         with VatTerminal(node) as vat:
239             vat.vat_terminal_exec_cmd_from_template(
240                 "classify_add_session_generic.vat",
241                 type=session_type,
242                 table_index=table_index,
243                 skip_n=skip_n,
244                 match_n=match_n,
245                 match=match,
246             )
247
248     @staticmethod
249     def compute_classify_hex_mask(ip_version, protocol, direction):
250         """Compute classify hex mask for TCP or UDP packet matching.
251
252         :param ip_version: Version of IP protocol.
253         :param protocol: Type of protocol.
254         :param direction: Traffic direction.
255         :type ip_version: str
256         :type protocol: str
257         :type direction: str
258         :returns: Classify hex mask.
259         :rtype : str
260         :raises ValueError: If protocol is not TCP or UDP.
261         :raises ValueError: If direction is not source or destination or
262                             source + destination.
263         """
264         if protocol == 'TCP' or protocol == 'UDP':
265             base_mask = Classify._compute_base_mask(ip_version)
266
267             if direction == 'source':
268                 return base_mask + 'FFFF0000'
269             elif direction == 'destination':
270                 return base_mask + '0000FFFF'
271             elif direction == 'source + destination':
272                 return base_mask + 'FFFFFFFF'
273             else:
274                 raise ValueError("Invalid direction!")
275         else:
276             raise ValueError("Invalid protocol!")
277
278     @staticmethod
279     def compute_classify_hex_value(hex_mask, source_port, destination_port):
280         """Compute classify hex value for TCP or UDP packet matching.
281
282         :param hex_mask: Classify hex mask.
283         :param source_port: Source TCP/UDP port.
284         :param destination_port: Destination TCP/UDP port.
285         :type hex_mask: str
286         :type source_port: str
287         :type destination_port: str
288         :returns: Classify hex value.
289         :rtype: str
290         """
291         source_port_hex = Classify._port_convert(source_port)
292         destination_port_hex = Classify._port_convert(destination_port)
293
294         return hex_mask[:-8] + source_port_hex + destination_port_hex
295
296     @staticmethod
297     def _port_convert(port):
298         """Convert port number for classify hex table format.
299
300         :param port: TCP/UDP port number.
301         :type port: str
302         :returns: TCP/UDP port number in 4-digit hexadecimal format.
303         :rtype: str
304         """
305         return '{0:04x}'.format(int(port))
306
307     @staticmethod
308     def _compute_base_mask(ip_version):
309         """Compute base classify hex mask based on IP version.
310
311         :param ip_version: Version of IP protocol.
312         :type ip_version: str
313         :return: Base hex mask.
314         :rtype: str
315         """
316         if ip_version == 'ip4':
317             return 68 * '0'
318             # base value of classify hex table for IPv4 TCP/UDP ports
319         elif ip_version == 'ip6':
320             return 108 * '0'
321             # base value of classify hex table for IPv6 TCP/UDP ports
322         else:
323             raise ValueError("Invalid IP version!")
324
325     @staticmethod
326     def get_classify_table_data(node, table_index):
327         """Retrieve settings for classify table by ID.
328
329         :param node: VPP node to retrieve classify data from.
330         :param table_index: Index of a specific classify table.
331         :type node: dict
332         :type table_index: int
333         :returns: Classify table settings.
334         :rtype: dict
335         """
336         with VatTerminal(node) as vat:
337             data = vat.vat_terminal_exec_cmd_from_template(
338                 "classify_table_info.vat",
339                 table_id=table_index
340             )
341         return data[0]
342
343     @staticmethod
344     def get_classify_session_data(node, table_index, session_index=None):
345         """Retrieve settings for all classify sessions in a table,
346         or for a specific classify session.
347
348         :param node: VPP node to retrieve classify data from.
349         :param table_index: Index of a classify table.
350         :param session_index: Index of a specific classify session. (Optional)
351         :type node: dict
352         :type table_index: int
353         :type session_index: int
354         :returns: List of classify session settings, or a dictionary of settings
355          for a specific classify session.
356         :rtype: list or dict
357         """
358         with VatTerminal(node) as vat:
359             data = vat.vat_terminal_exec_cmd_from_template(
360                 "classify_session_dump.vat",
361                 table_id=table_index
362             )
363         if session_index is not None:
364             return data[0][session_index]
365         else:
366             return data[0]
367
368     @staticmethod
369     def vpp_log_plugin_acl_settings(node):
370         """Retrieve configured settings from the ACL plugin
371          and write to robot log.
372
373         :param node: VPP node.
374         :type node: dict
375         """
376         try:
377             VatExecutor.cmd_from_template(
378                 node, "acl_plugin/acl_dump.vat")
379         except (ValueError, RuntimeError):
380             # Fails to parse JSON data in response, but it is still logged
381             pass
382
383     @staticmethod
384     def vpp_log_plugin_acl_interface_assignment(node):
385         """Retrieve interface assignment from the ACL plugin
386         and write to robot log.
387
388         :param node: VPP node.
389         :type node: dict
390         """
391         try:
392             VatExecutor.cmd_from_template(
393                 node, "acl_plugin/acl_interface_dump.vat", json_out=False)
394         except RuntimeError:
395             # Fails to parse response, but it is still logged
396             pass
397
398     @staticmethod
399     def set_acl_list_for_interface(node, interface, acl_type, acl_idx=None):
400         """Set the list of input or output ACLs applied to the interface. It
401         unapplies any previously applied ACLs.
402
403         :param node: VPP node to set ACL on.
404         :param interface: Interface name or sw_if_index.
405         :param acl_type: Type of ACL(s) - input or output.
406         :param acl_idx: Index(ies) of ACLs to be applied on the interface.
407         :type node: dict
408         :type interface: str or int
409         :type acl_type: str
410         :type acl_idx: list
411         :raises RuntimeError: If unable to set ACL list for the interface.
412         """
413         sw_if_index = Topology.get_interface_sw_index(node, interface) \
414             if isinstance(interface, basestring) else interface
415
416         acl_list = acl_type + ' ' + ' '.join(str(idx) for idx in acl_idx) \
417             if acl_idx else acl_type
418
419         try:
420             with VatTerminal(node, json_param=False) as vat:
421                 vat.vat_terminal_exec_cmd_from_template(
422                     "acl_plugin/acl_interface_set_acl_list.vat",
423                     interface=sw_if_index, acl_list=acl_list)
424         except:
425             raise RuntimeError("Setting of ACL list for interface {0} failed "
426                                "on node {1}".format(interface, node['host']))
427
428     @staticmethod
429     def add_replace_acl(node, acl_idx=None, ip_ver="ipv4", action="permit",
430                         src=None, dst=None, sport=None, dport=None, proto=None,
431                         tcpflg_val=None, tcpflg_mask=None):
432         """Add a new ACL or replace the existing one. To replace an existing
433         ACL, pass the ID of this ACL.
434
435         :param node: VPP node to set ACL on.
436         :param acl_idx: ID of ACL. (Optional)
437         :param ip_ver: IP version. (Optional)
438         :param action: ACL action. (Optional)
439         :param src: Source IP in format IP/plen. (Optional)
440         :param dst: Destination IP in format IP/plen. (Optional)
441         :param sport: Source port or ICMP4/6 type - range format X-Y allowed.
442          (Optional)
443         :param dport: Destination port or ICMP4/6 code - range format X-Y
444          allowed. (Optional)
445         :param proto: L4 protocol (http://www.iana.org/assignments/protocol-
446          numbers/protocol-numbers.xhtml). (Optional)
447         :param tcpflg_val: TCP flags value. (Optional)
448         :param tcpflg_mask: TCP flags mask. (Optional)
449         :type node: dict
450         :type acl_idx: int
451         :type ip_ver: str
452         :type action: str
453         :type src: str
454         :type dst: str
455         :type sport: str or int
456         :type dport: str or int
457         :type proto: int
458         :type tcpflg_val: int
459         :type tcpflg_mask: int
460         :raises RuntimeError: If unable to add or replace ACL.
461         """
462         acl_idx = '{0}'.format(acl_idx) if acl_idx else ''
463
464         src = 'src {0}'.format(src) if src else ''
465
466         dst = 'dst {0}'.format(dst) if dst else ''
467
468         sport = 'sport {0}'.format(sport) if sport else ''
469
470         dport = 'dport {0}'.format(dport) if dport else ''
471
472         proto = 'proto {0}'.format(proto) if proto else ''
473
474         tcpflags = 'tcpflags {0} {1}'.format(tcpflg_val, tcpflg_mask) \
475             if tcpflg_val and tcpflg_mask else ''
476
477         try:
478             with VatTerminal(node, json_param=False) as vat:
479                 vat.vat_terminal_exec_cmd_from_template(
480                     "acl_plugin/acl_add_replace.vat", acl_idx=acl_idx,
481                     ip_ver=ip_ver, action=action, src=src, dst=dst, sport=sport,
482                     dport=dport, proto=proto, tcpflags=tcpflags)
483         except:
484             raise RuntimeError("Adding or replacing of ACL failed on "
485                                "node {0}".format(node['host']))
486
487     @staticmethod
488     def add_replace_acl_multi_entries(node, acl_idx=None, rules=None):
489         """Add a new ACL or replace the existing one. To replace an existing
490         ACL, pass the ID of this ACL.
491
492         :param node: VPP node to set ACL on.
493         :param acl_idx: ID of ACL. (Optional)
494         :param rules: Required rules. (Optional)
495         :type node: dict
496         :type acl_idx: int
497         :type rules: str
498         :raises RuntimeError: If unable to add or replace ACL.
499         """
500         acl_idx = '{0}'.format(acl_idx) if acl_idx else ''
501
502         rules = '{0}'.format(rules) if rules else ''
503
504         try:
505             with VatTerminal(node, json_param=False) as vat:
506                 vat.vat_terminal_exec_cmd_from_template(
507                     "acl_plugin/acl_add_replace.vat", acl_idx=acl_idx,
508                     ip_ver=rules, action='', src='', dst='', sport='',
509                     dport='', proto='', tcpflags='')
510         except:
511             raise RuntimeError("Adding or replacing of ACL failed on "
512                                "node {0}".format(node['host']))
513
514     @staticmethod
515     def delete_acl(node, idx):
516         """Delete required ACL.
517
518         :param node: VPP node to delete ACL on.
519         :param idx: Index of ACL to be deleted.
520         :type node: dict
521         :type idx: int or str
522         :raises RuntimeError: If unable to delete ACL.
523         """
524         try:
525             with VatTerminal(node, json_param=False) as vat:
526                 vat.vat_terminal_exec_cmd_from_template(
527                     "acl_plugin/acl_delete.vat", idx=idx)
528         except:
529             raise RuntimeError("Deletion of ACL failed on node {0}".
530                                format(node['host']))
531
532     @staticmethod
533     def cli_show_acl(node, acl_idx=None):
534         """Show ACLs.
535
536         :param node: VPP node to show ACL on.
537         :param acl_idx: Index of ACL to be shown.
538         :type node: dict
539         :type acl_idx: int or str
540         :raises RuntimeError: If unable to delete ACL.
541         """
542         acl_idx = '{0}'.format(acl_idx) if acl_idx else ''
543
544         try:
545             with VatTerminal(node, json_param=False) as vat:
546                 vat.vat_terminal_exec_cmd_from_template(
547                     "acl_plugin/show_acl.vat", idx=acl_idx)
548         except:
549             raise RuntimeError("Failed to show ACL on node {0}".
550                                format(node['host']))