ff0c133934b5ae4e8c0dbfc1b5485c14f0bc176f
[csit.git] / resources / libraries / python / GBP.py
1 # Copyright (c) 2019 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 """GBP utilities library."""
15
16 from enum import IntEnum
17 from ipaddress import ip_address
18
19 from resources.libraries.python.IPUtil import IPUtil
20 from resources.libraries.python.L2Util import L2Util
21 from resources.libraries.python.PapiExecutor import PapiSocketExecutor
22
23
24 class GBPEndpointFlags(IntEnum):
25     """GBP Endpoint Flags."""
26     GBP_API_ENDPOINT_FLAG_NONE = 0
27     GBP_API_ENDPOINT_FLAG_BOUNCE = 1
28     GBP_API_ENDPOINT_FLAG_REMOTE = 2
29     GBP_API_ENDPOINT_FLAG_LEARNT = 4
30     GBP_API_ENDPOINT_FLAG_EXTERNAL = 8
31
32
33 class GBPBridgeDomainFlags(IntEnum):
34     """GBP Bridge Domain Flags."""
35     GBP_BD_API_FLAG_NONE = 0
36     GBP_BD_API_FLAG_DO_NOT_LEARN = 1
37     GBP_BD_API_FLAG_UU_FWD_DROP = 2
38     GBP_BD_API_FLAG_MCAST_DROP = 4
39     GBP_BD_API_FLAG_UCAST_ARP = 8
40
41
42 class GBPSubnetType(IntEnum):
43     """GBP Subnet Type."""
44     GBP_API_SUBNET_TRANSPORT = 1
45     GBP_API_SUBNET_STITCHED_INTERNAL = 2
46     GBP_API_SUBNET_STITCHED_EXTERNAL = 3
47     GBP_API_SUBNET_L3_OUT = 4
48     GBP_API_SUBNET_ANON_L3_OUT = 5
49
50
51 class GBPExtItfFlags(IntEnum):
52     """GBP External Interface Flags."""
53     GBP_API_EXT_ITF_F_NONE = 0
54     GBP_API_EXT_ITF_F_ANON = 1
55
56
57 class GBPRuleAction(IntEnum):
58     """GBP Rule Action."""
59     GBP_API_RULE_PERMIT = 1
60     GBP_API_RULE_DENY = 2
61     GBP_API_RULE_REDIRECT = 3
62
63
64 class GBP(object):
65     """GBP utilities."""
66
67     @staticmethod
68     def gbp_route_domain_add(
69             node, rd_id=1, ip4_table_id=1, ip6_table_id=0,
70             ip4_uu_sw_if_index=0xffffffff, ip6_uu_sw_if_index=0xffffffff):
71         """Add GBP route domain.
72
73         :param node: Node to add GBP route domain on.
74         :param rd_id: GBP route domain ID.
75         :param ip4_table_id: IPv4 table.
76         :param ip6_table_id: IPv6 table.
77         :param ip4_uu_sw_if_index: IPv4 unicast interface index.
78         :param ip6_uu_sw_if_index: IPv6 unicast interface index.
79         :type node: dict
80         :type rd_id: int
81         :type ip4_table_id: int
82         :type ip6_table_id: int
83         :type ip4_uu_sw_if_index: int
84         :type ip6_uu_sw_if_index: int
85         """
86         cmd = 'gbp_route_domain_add'
87         err_msg = 'Failed to add GBP route domain on {node}!'\
88                   .format(node=node['host'])
89
90         args_in = dict(
91             rd=dict(
92                 rd_id=rd_id,
93                 ip4_table_id=ip4_table_id,
94                 ip6_table_id=ip6_table_id,
95                 ip4_uu_sw_if_index=ip4_uu_sw_if_index,
96                 ip6_uu_sw_if_index=ip6_uu_sw_if_index
97             )
98         )
99
100         with PapiSocketExecutor(node) as papi_exec:
101             papi_exec.add(cmd, **args_in).get_reply(err_msg)
102
103     @staticmethod
104     def gbp_bridge_domain_add(
105             node, bvi_sw_if_index, bd_id=1, rd_id=1,
106             uu_fwd_sw_if_index=0xffffffff, bm_flood_sw_if_index=0xffffffff):
107         """Add GBP bridge domain.
108
109         :param node: Node to add GBP bridge domain on.
110         :param bvi_sw_if_index: SW index of BVI/loopback interface.
111         :param bd_id: GBP bridge domain ID.
112         :param rd_id: GBP route domain ID.
113         :param uu_fwd_sw_if_index: Unicast forward interface index.
114         :param bm_flood_sw_if_index: Bcast/Mcast flood interface index.
115         :type node: dict
116         :type bvi_sw_if_index: int
117         :type bd_id: int
118         :type rd_id: int
119         :type uu_fwd_sw_if_index: int
120         :type bm_flood_sw_if_index: int
121         """
122         cmd = 'gbp_bridge_domain_add'
123         err_msg = 'Failed to add GBP bridge domain on {node}!'\
124                   .format(node=node['host'])
125
126         args_in = dict(
127             bd=dict(
128                 flags=getattr(GBPBridgeDomainFlags,
129                               'GBP_BD_API_FLAG_NONE').value,
130                 bvi_sw_if_index=bvi_sw_if_index,
131                 uu_fwd_sw_if_index=uu_fwd_sw_if_index,
132                 bm_flood_sw_if_index=bm_flood_sw_if_index,
133                 bd_id=bd_id,
134                 rd_id=rd_id
135             )
136         )
137
138         with PapiSocketExecutor(node) as papi_exec:
139             papi_exec.add(cmd, **args_in).get_reply(err_msg)
140
141     @staticmethod
142     def gbp_endpoint_group_add(
143             node, sclass, bd_id=1, rd_id=1, vnid=1,
144             uplink_sw_if_index=0xffffffff, remote_ep_timeout=0xffffffff):
145         """Add GBP endpoint group.
146
147         :param node: Node to add GBP endpoint group on.
148         :param sclass: Source CLASS.
149         :param bd_id: GBP bridge domain ID.
150         :param rd_id: GBP route domain ID.
151         :param uplink_sw_if_index: Uplink interface index.
152         :param remote_ep_timeout: Remote endpoint interface index.
153         :param vnid: VNID.
154         :type node: dict
155         :type sclass: int
156         :type bd_id: int
157         :type rd_id: int
158         :type vnid: int
159         :type uplink_sw_if_index: int
160         :type remote_ep_timeout: int
161         """
162         cmd = 'gbp_endpoint_group_add'
163         err_msg = 'Failed to add GBP endpoint group on {node}!'\
164                   .format(node=node['host'])
165
166         args_in = dict(
167             epg=dict(
168                 uplink_sw_if_index=uplink_sw_if_index,
169                 bd_id=bd_id,
170                 rd_id=rd_id,
171                 vnid=vnid,
172                 sclass=sclass,
173                 retention=dict(
174                     remote_ep_timeout=remote_ep_timeout
175                 )
176             )
177         )
178
179         with PapiSocketExecutor(node) as papi_exec:
180             papi_exec.add(cmd, **args_in).get_reply(err_msg)
181
182     @staticmethod
183     def gbp_endpoint_add(node, sw_if_index, ip_addr, mac_addr, sclass):
184         """Add GBP endpoint.
185
186         :param node: Node to add GBP endpoint on.
187         :param sw_if_index: SW index of interface.
188         :param ip_addr: GBP route domain ID.
189         :param mac_addr: MAC address.
190         :param sclass: Source CLASS.
191         :type node: dict
192         :type sw_if_index: int
193         :type ip_addr: str
194         :type mac_addr: str
195         :type sclass: int
196         """
197         cmd = 'gbp_endpoint_add'
198         err_msg = 'Failed to add GBP endpoint on {node}!'\
199                   .format(node=node['host'])
200
201         ips = list()
202         ips.append(IPUtil.create_ip_address_object(
203             ip_address(unicode(ip_addr))))
204         tun_src = IPUtil.create_ip_address_object(
205             ip_address(unicode('0.0.0.0')))
206         tun_dst = IPUtil.create_ip_address_object(
207             ip_address(unicode('0.0.0.0')))
208
209         args_in = dict(
210             endpoint=dict(
211                 sw_if_index=sw_if_index,
212                 ips=ips,
213                 n_ips=len(ips),
214                 mac=L2Util.mac_to_bin(mac_addr),
215                 sclass=sclass,
216                 flags=getattr(GBPEndpointFlags,
217                               'GBP_API_ENDPOINT_FLAG_EXTERNAL').value,
218                 tun=dict(
219                     src=tun_src,
220                     dst=tun_dst
221                 )
222             )
223         )
224
225         with PapiSocketExecutor(node) as papi_exec:
226             papi_exec.add(cmd, **args_in).get_reply(err_msg)
227
228     @staticmethod
229     def gbp_ext_itf_add_del(node, sw_if_index, bd_id=1, rd_id=1):
230         """Add external interface to GBP.
231
232         :param node: Node to add external GBP interface on.
233         :param sw_if_index: SW index of interface.
234         :param bd_id: GBP bridge domain ID.
235         :param rd_id: GBP route domain ID.
236         :type node: dict
237         :type sw_if_index: int
238         :type bd_id: int
239         :type rd_id: int
240         """
241         cmd = 'gbp_ext_itf_add_del'
242         err_msg = 'Failed to add external GBP interface on {node}!'\
243                   .format(node=node['host'])
244
245         args_in = dict(
246             is_add=1,
247             ext_itf=dict(
248                 sw_if_index=sw_if_index,
249                 bd_id=bd_id,
250                 rd_id=rd_id,
251                 flags=getattr(GBPExtItfFlags,
252                               'GBP_API_EXT_ITF_F_NONE').value
253             )
254         )
255
256         with PapiSocketExecutor(node) as papi_exec:
257             papi_exec.add(cmd, **args_in).get_reply(err_msg)
258
259     @staticmethod
260     def gbp_subnet_add_del(
261             node, address, subnet_length, sclass, rd_id=1,
262             sw_if_index=0xffffffff):
263         """Add external interface to GBP.
264
265         :param node: Node to add GBP subnet on.
266         :param address: IPv4 adddress.
267         :param subnet_length: IPv4 address subnet.
268         :param sclass: Source CLASS.
269         :param rd_id: GBP route domain ID.
270         :param sw_if_index: Interface index.
271         :type node: dict
272         :type address: int
273         :type subnet_length: int
274         :type sclass: int
275         :type rd_id: int
276         :type sw_if_index: int
277         """
278         cmd = 'gbp_subnet_add_del'
279         err_msg = 'Failed to add GBP subnet on {node}!'\
280                   .format(node=node['host'])
281
282         args_in = dict(
283             is_add=1,
284             subnet=dict(
285                 type=getattr(GBPSubnetType,
286                              'GBP_API_SUBNET_L3_OUT').value,
287                 sw_if_index=sw_if_index,
288                 sclass=sclass,
289                 prefix=dict(
290                     address=IPUtil.create_ip_address_object(
291                         ip_address(unicode(address))),
292                     len=int(subnet_length)
293                 ),
294                 rd_id=rd_id
295             )
296         )
297
298         with PapiSocketExecutor(node) as papi_exec:
299             papi_exec.add(cmd, **args_in).get_reply(err_msg)
300
301     @staticmethod
302     def gbp_contract_add_del(node, sclass, dclass, acl_index=0):
303         """Add GBP contract.
304
305         :param node: Node to add GBP contract on.
306         :param sclass: Source CLASS.
307         :param dclass: Destination CLASS.
308         :param acl_index: Index of ACL rule.
309         :type node: dict
310         :type sclass: int
311         :type dclass: int
312         :type acl_index: int
313         """
314         cmd = 'gbp_contract_add_del'
315         err_msg = 'Failed to add GBP contract on {node}!'\
316                   .format(node=node['host'])
317
318         rule_permit = dict(
319             action=getattr(GBPRuleAction,
320                            'GBP_API_RULE_PERMIT').value,
321             nh_set=dict(
322                 hash_mode=list(),
323                 n_nhs=8,
324                 nhs=[dict()]*8,
325             )
326         )
327         rules = [rule_permit, rule_permit]
328
329         args_in = dict(
330             is_add=1,
331             contract=dict(
332                 acl_index=acl_index,
333                 sclass=sclass,
334                 dclass=dclass,
335                 n_rules=len(rules),
336                 rules=rules,
337                 n_ether_types=16,
338                 allowed_ethertypes=[0x800, 0x86dd] + [0]*14
339             )
340         )
341
342         with PapiSocketExecutor(node) as papi_exec:
343             papi_exec.add(cmd, **args_in).get_reply(err_msg)