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