Line length: Fix recent merges
[csit.git] / resources / libraries / python / Policer.py
1 # Copyright (c) 2021 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 """Policer utilities library."""
15
16 from enum import IntEnum
17
18 from resources.libraries.python.Constants import Constants
19 from resources.libraries.python.IPUtil import IpDscp
20 from resources.libraries.python.PapiExecutor import PapiSocketExecutor
21 from resources.libraries.python.topology import Topology
22
23
24 class PolicerRateType(IntEnum):
25     """Policer rate types."""
26     KBPS = 0
27     PPS = 1
28     INVALID = 2
29
30
31 class PolicerRoundType(IntEnum):
32     """Policer round types."""
33     ROUND_TO_CLOSEST = 0
34     ROUND_TO_UP = 1
35     ROUND_TO_DOWN = 2
36     ROUND_INVALID = 3
37
38
39 class PolicerType(IntEnum):
40     """Policer type."""
41     TYPE_1R2C = 0
42     TYPE_1R3C_RFC_2697 = 1
43     TYPE_2R3C_RFC_2698 = 2
44     TYPE_2R3C_RFC_4115 = 3
45     TYPE_2R3C_RFC_MEF5CF1 = 4
46     TYPE_MAX = 5
47
48
49 class PolicerAction(IntEnum):
50     """Policer action."""
51     DROP = 0
52     TRANSMIT = 1
53     MARK_AND_TRANSMIT = 2
54
55
56 class PolicerPreColor(IntEnum):
57     """Policer Pre-color."""
58     CONFORM_COLOR = 0
59     EXCEED_COLOR = 1
60     VIOLATE_COLOR = 2
61
62
63 class Policer:
64     """Policer utilities."""
65
66     # TODO: Pylint says too-many-arguments and too-many-locals.
67     # It is right, we should refactor the code
68     # and group similar arguments together (into documented classes).
69     # Note that even the call from Robot Framework
70     # is not very readable with this many arguments.
71     @staticmethod
72     def policer_set_configuration(
73             node, policer_name, cir, eir, cbs, ebs, rate_type, round_type,
74             policer_type, conform_action_type, exceed_action_type,
75             violate_action_type, color_aware, is_add=True, conform_dscp=None,
76             exceed_dscp=None, violate_dscp=None):
77         """Configure policer on VPP node.
78
79         :param node: VPP node.
80         :param policer_name: Name of the policer.
81         :param cir: Committed information rate.
82         :param eir: Excess (or Peak) information rate.
83         :param cbs: Committed burst size.
84         :param ebs: Excess (or Peak) burst size.
85         :param rate_type: Rate type.
86         :param round_type: Round type.
87         :param policer_type: Policer algorithm.
88         :param conform_action_type: Conform action type.
89         :param exceed_action_type: Exceed action type.
90         :param violate_action_type: Violate action type.
91         :param color_aware: Color-blind (cb) or color-aware (ca).
92         :param is_add: Add policer if True, else delete.
93         :param conform_dscp: DSCP for conform mark_and_transmit action.
94         :param exceed_dscp: DSCP for exceed mark_and_transmit action.
95         :param violate_dscp: DSCP for vilate mark_and_transmit action.
96         :type node: dict
97         :type policer_name: str
98         :type cir: int
99         :type eir: int
100         :type cbs: int
101         :type ebs: int
102         :type rate_type: str
103         :type round_type: str
104         :type policer_type: str
105         :type conform_action_type: str
106         :type exceed_action_type: str
107         :type violate_action_type: str
108         :type color_aware: str
109         :type is_add: bool
110         :type conform_dscp: str
111         :type exceed_dscp: str
112         :type violate_dscp: str
113         """
114         conform_action = dict(
115             type=getattr(PolicerAction, conform_action_type.upper()).value,
116             dscp=Policer.get_dscp_num_value(conform_dscp) if
117             conform_action_type.upper() == PolicerAction.MARK_AND_TRANSMIT.name
118             else 0
119         )
120         exceed_action = dict(
121             type=getattr(PolicerAction, exceed_action_type.upper()).value,
122             dscp=Policer.get_dscp_num_value(exceed_dscp) if
123             exceed_action_type.upper() == PolicerAction.MARK_AND_TRANSMIT.name
124             else 0
125         )
126         violate_action = dict(
127             type=getattr(PolicerAction, violate_action_type.upper()).value,
128             dscp=Policer.get_dscp_num_value(violate_dscp) if
129             violate_action_type.upper() == PolicerAction.MARK_AND_TRANSMIT.name
130             else 0
131         )
132
133         cmd = u"policer_add_del"
134         args = dict(
135             is_add=is_add,
136             name=str(policer_name),
137             cir=int(cir),
138             eir=int(eir),
139             cb=int(cbs),
140             eb=int(ebs),
141             rate_type=getattr(PolicerRateType, rate_type.upper()).value,
142             round_type=getattr(
143                 PolicerRoundType, f"ROUND_TO_{round_type.upper()}"
144             ).value,
145             type=getattr(PolicerType, f"TYPE_{policer_type.upper()}").value,
146             conform_action=conform_action,
147             exceed_action=exceed_action,
148             violate_action=violate_action,
149             color_aware=bool(color_aware == u"'ca'")
150         )
151         err_msg = f"Failed to configure policer {policer_name} " \
152             f"on host {node['host']}"
153
154         with PapiSocketExecutor(node) as papi_exec:
155             reply = papi_exec.add(cmd, **args).get_reply(err_msg)
156
157         return reply[u"policer_index"]
158
159     @staticmethod
160     def policer_classify_set_interface(
161             node, interface, ip4_table_index=Constants.BITWISE_NON_ZERO,
162             ip6_table_index=Constants.BITWISE_NON_ZERO,
163             l2_table_index=Constants.BITWISE_NON_ZERO, is_add=True):
164         """Set/unset policer classify interface.
165
166         :param node: VPP node.
167         :param interface: Interface name or sw_if_index to set/unset policer
168             classify.
169         :param ip4_table_index: IP4 classify table index (~0 to skip).
170             (Default value = ~0)
171         :param ip6_table_index: IP6 classify table index (~0 to skip).
172             (Default value = ~0)
173         :param l2_table_index: L2 classify table index (~0 to skip).
174             (Default value = ~0)
175         :param is_add: Set if True, else unset.
176         :type node: dict
177         :type interface: str or int
178         :type ip4_table_index: int
179         :type ip6_table_index: int
180         :type l2_table_index: int
181         :type is_add: bool
182         """
183         if isinstance(interface, str):
184             sw_if_index = Topology.get_interface_sw_index(node, interface)
185         else:
186             sw_if_index = interface
187
188         cmd = u"policer_classify_set_interface"
189         args = dict(
190             is_add=is_add,
191             sw_if_index=int(sw_if_index),
192             ip4_table_index=int(ip4_table_index),
193             ip6_table_index=int(ip6_table_index),
194             l2_table_index=int(l2_table_index)
195         )
196         err_msg = f"Failed to set/unset policer classify interface " \
197             f"{interface} on host {node[u'host']}"
198
199         with PapiSocketExecutor(node) as papi_exec:
200             papi_exec.add(cmd, **args).get_reply(err_msg)
201
202     @staticmethod
203     def policer_classify_get_precolor(precolor):
204         """Return policer pre-color numeric value.
205
206         :param precolor: Policer pre-color name.
207         :type precolor: str
208         :returns: Policer pre-color numeric value.
209         :rtype: int
210         """
211         return getattr(PolicerPreColor, precolor.upper()).value
212
213     @staticmethod
214     def get_dscp_num_value(dscp):
215         """Return DSCP numeric value.
216
217         :param dscp: DSCP name.
218         :type dscp: str
219         :returns: DSCP numeric value.
220         :rtype: int
221         """
222         return getattr(IpDscp, f"IP_API_DSCP_{dscp.upper()}").value