FIX: missing param in gbp_contract_add_del
[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  # pylint: disable=invalid-name
46     GBP_API_SUBNET_STITCHED_EXTERNAL = 3  # pylint: disable=invalid-name
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 GBPHashMode(IntEnum):
65     """GBP Hash Mode."""
66     GBP_API_HASH_MODE_SRC_IP = 1
67     GBP_API_HASH_MODE_DST_IP = 2
68     GBP_API_HASH_MODE_SYMETRIC = 3
69
70
71 class GBP(object):
72     """GBP utilities."""
73
74     @staticmethod
75     def gbp_route_domain_add(
76             node, rd_id=1, ip4_table_id=1, ip6_table_id=0,
77             ip4_uu_sw_if_index=0xffffffff, ip6_uu_sw_if_index=0xffffffff):
78         """Add GBP route domain.
79
80         :param node: Node to add GBP route domain on.
81         :param rd_id: GBP route domain ID.
82         :param ip4_table_id: IPv4 table.
83         :param ip6_table_id: IPv6 table.
84         :param ip4_uu_sw_if_index: IPv4 unicast interface index.
85         :param ip6_uu_sw_if_index: IPv6 unicast interface index.
86         :type node: dict
87         :type rd_id: int
88         :type ip4_table_id: int
89         :type ip6_table_id: int
90         :type ip4_uu_sw_if_index: int
91         :type ip6_uu_sw_if_index: int
92         """
93         cmd = 'gbp_route_domain_add'
94         err_msg = 'Failed to add GBP route domain on {node}!'\
95                   .format(node=node['host'])
96
97         args_in = dict(
98             rd=dict(
99                 rd_id=rd_id,
100                 ip4_table_id=ip4_table_id,
101                 ip6_table_id=ip6_table_id,
102                 ip4_uu_sw_if_index=ip4_uu_sw_if_index,
103                 ip6_uu_sw_if_index=ip6_uu_sw_if_index
104             )
105         )
106
107         with PapiSocketExecutor(node) as papi_exec:
108             papi_exec.add(cmd, **args_in).get_reply(err_msg)
109
110     @staticmethod
111     def gbp_bridge_domain_add(
112             node, bvi_sw_if_index, bd_id=1, rd_id=1,
113             uu_fwd_sw_if_index=0xffffffff, bm_flood_sw_if_index=0xffffffff):
114         """Add GBP bridge domain.
115
116         :param node: Node to add GBP bridge domain on.
117         :param bvi_sw_if_index: SW index of BVI/loopback interface.
118         :param bd_id: GBP bridge domain ID.
119         :param rd_id: GBP route domain ID.
120         :param uu_fwd_sw_if_index: Unicast forward interface index.
121         :param bm_flood_sw_if_index: Bcast/Mcast flood interface index.
122         :type node: dict
123         :type bvi_sw_if_index: int
124         :type bd_id: int
125         :type rd_id: int
126         :type uu_fwd_sw_if_index: int
127         :type bm_flood_sw_if_index: int
128         """
129         cmd = 'gbp_bridge_domain_add'
130         err_msg = 'Failed to add GBP bridge domain on {node}!'\
131                   .format(node=node['host'])
132
133         args_in = dict(
134             bd=dict(
135                 flags=getattr(
136                     GBPBridgeDomainFlags, 'GBP_BD_API_FLAG_NONE').value,
137                 bvi_sw_if_index=bvi_sw_if_index,
138                 uu_fwd_sw_if_index=uu_fwd_sw_if_index,
139                 bm_flood_sw_if_index=bm_flood_sw_if_index,
140                 bd_id=bd_id,
141                 rd_id=rd_id
142             )
143         )
144
145         with PapiSocketExecutor(node) as papi_exec:
146             papi_exec.add(cmd, **args_in).get_reply(err_msg)
147
148     @staticmethod
149     def gbp_endpoint_group_add(
150             node, sclass, bd_id=1, rd_id=1, vnid=1,
151             uplink_sw_if_index=0xffffffff, remote_ep_timeout=0xffffffff):
152         """Add GBP endpoint group.
153
154         :param node: Node to add GBP endpoint group on.
155         :param sclass: Source CLASS.
156         :param bd_id: GBP bridge domain ID.
157         :param rd_id: GBP route domain ID.
158         :param uplink_sw_if_index: Uplink interface index.
159         :param remote_ep_timeout: Remote endpoint interface index.
160         :param vnid: VNID.
161         :type node: dict
162         :type sclass: int
163         :type bd_id: int
164         :type rd_id: int
165         :type vnid: int
166         :type uplink_sw_if_index: int
167         :type remote_ep_timeout: int
168         """
169         cmd = 'gbp_endpoint_group_add'
170         err_msg = 'Failed to add GBP endpoint group on {node}!'\
171                   .format(node=node['host'])
172
173         args_in = dict(
174             epg=dict(
175                 uplink_sw_if_index=uplink_sw_if_index,
176                 bd_id=bd_id,
177                 rd_id=rd_id,
178                 vnid=vnid,
179                 sclass=sclass,
180                 retention=dict(
181                     remote_ep_timeout=remote_ep_timeout
182                 )
183             )
184         )
185
186         with PapiSocketExecutor(node) as papi_exec:
187             papi_exec.add(cmd, **args_in).get_reply(err_msg)
188
189     @staticmethod
190     def gbp_endpoint_add(node, sw_if_index, ip_addr, mac_addr, sclass):
191         """Add GBP endpoint.
192
193         :param node: Node to add GBP endpoint on.
194         :param sw_if_index: SW index of interface.
195         :param ip_addr: GBP route domain ID.
196         :param mac_addr: MAC address.
197         :param sclass: Source CLASS.
198         :type node: dict
199         :type sw_if_index: int
200         :type ip_addr: str
201         :type mac_addr: str
202         :type sclass: int
203         """
204         cmd = 'gbp_endpoint_add'
205         err_msg = 'Failed to add GBP endpoint on {node}!'\
206                   .format(node=node['host'])
207
208         ips = list()
209         ips.append(IPUtil.create_ip_address_object(
210             ip_address(unicode(ip_addr))))
211         tun_src = IPUtil.create_ip_address_object(
212             ip_address(unicode('0.0.0.0')))
213         tun_dst = IPUtil.create_ip_address_object(
214             ip_address(unicode('0.0.0.0')))
215
216         args_in = dict(
217             endpoint=dict(
218                 sw_if_index=sw_if_index,
219                 ips=ips,
220                 n_ips=len(ips),
221                 mac=L2Util.mac_to_bin(mac_addr),
222                 sclass=sclass,
223                 flags=getattr(
224                     GBPEndpointFlags, 'GBP_API_ENDPOINT_FLAG_EXTERNAL').value,
225                 tun=dict(
226                     src=tun_src,
227                     dst=tun_dst
228                 )
229             )
230         )
231
232         with PapiSocketExecutor(node) as papi_exec:
233             papi_exec.add(cmd, **args_in).get_reply(err_msg)
234
235     @staticmethod
236     def gbp_ext_itf_add_del(node, sw_if_index, bd_id=1, rd_id=1):
237         """Add external interface to GBP.
238
239         :param node: Node to add external GBP interface on.
240         :param sw_if_index: SW index of interface.
241         :param bd_id: GBP bridge domain ID.
242         :param rd_id: GBP route domain ID.
243         :type node: dict
244         :type sw_if_index: int
245         :type bd_id: int
246         :type rd_id: int
247         """
248         cmd = 'gbp_ext_itf_add_del'
249         err_msg = 'Failed to add external GBP interface on {node}!'\
250                   .format(node=node['host'])
251
252         args_in = dict(
253             is_add=1,
254             ext_itf=dict(
255                 sw_if_index=sw_if_index,
256                 bd_id=bd_id,
257                 rd_id=rd_id,
258                 flags=getattr(GBPExtItfFlags, 'GBP_API_EXT_ITF_F_NONE').value
259             )
260         )
261
262         with PapiSocketExecutor(node) as papi_exec:
263             papi_exec.add(cmd, **args_in).get_reply(err_msg)
264
265     @staticmethod
266     def gbp_subnet_add_del(
267             node, address, subnet_length, sclass, rd_id=1,
268             sw_if_index=0xffffffff):
269         """Add external interface to GBP.
270
271         :param node: Node to add GBP subnet on.
272         :param address: IPv4 adddress.
273         :param subnet_length: IPv4 address subnet.
274         :param sclass: Source CLASS.
275         :param rd_id: GBP route domain ID.
276         :param sw_if_index: Interface index.
277         :type node: dict
278         :type address: int
279         :type subnet_length: int
280         :type sclass: int
281         :type rd_id: int
282         :type sw_if_index: int
283         """
284         cmd = 'gbp_subnet_add_del'
285         err_msg = 'Failed to add GBP subnet on {node}!'\
286                   .format(node=node['host'])
287
288         args_in = dict(
289             is_add=1,
290             subnet=dict(
291                 type=getattr(GBPSubnetType, 'GBP_API_SUBNET_L3_OUT').value,
292                 sw_if_index=sw_if_index,
293                 sclass=sclass,
294                 prefix=dict(
295                     address=IPUtil.create_ip_address_object(
296                         ip_address(unicode(address))),
297                     len=int(subnet_length)
298                 ),
299                 rd_id=rd_id
300             )
301         )
302
303         with PapiSocketExecutor(node) as papi_exec:
304             papi_exec.add(cmd, **args_in).get_reply(err_msg)
305
306     @staticmethod
307     def gbp_contract_add_del(node, sclass, dclass, acl_index=0, hash_mode=None):
308         """Add GBP contract.
309
310         :param node: Node to add GBP contract on.
311         :param sclass: Source CLASS.
312         :param dclass: Destination CLASS.
313         :param acl_index: Index of ACL rule.
314         :param hash_mode: GBP contract hash mode.
315         :type node: dict
316         :type sclass: int
317         :type dclass: int
318         :type acl_index: int
319         :type hash_mode: str
320         """
321         cmd = 'gbp_contract_add_del'
322         err_msg = 'Failed to add GBP contract on {node}!'\
323                   .format(node=node['host'])
324
325         hash_mode = 'GBP_API_HASH_MODE_SRC_IP' if hash_mode is None \
326             else hash_mode
327         rule_permit = dict(
328             action=getattr(GBPRuleAction, 'GBP_API_RULE_PERMIT').value,
329             nh_set=dict(
330                 hash_mode=getattr(GBPHashMode, hash_mode).value,
331                 n_nhs=8,
332                 nhs=[dict()]*8,
333             )
334         )
335         rules = [rule_permit, rule_permit]
336
337         args_in = dict(
338             is_add=1,
339             contract=dict(
340                 acl_index=acl_index,
341                 sclass=sclass,
342                 dclass=dclass,
343                 n_rules=len(rules),
344                 rules=rules,
345                 n_ether_types=16,
346                 allowed_ethertypes=[0x800, 0x86dd] + [0]*14
347             )
348         )
349
350         with PapiSocketExecutor(node) as papi_exec:
351             papi_exec.add(cmd, **args_in).get_reply(err_msg)