CSIT-366 IPv4dp - baseline vhost-user
[csit.git] / resources / libraries / python / L2Util.py
1 # Copyright (c) 2016 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 """L2 Utilities Library."""
15
16 from robot.api.deco import keyword
17
18 from resources.libraries.python.topology import Topology
19 from resources.libraries.python.VatExecutor import VatExecutor, VatTerminal
20 from resources.libraries.python.ssh import exec_cmd_no_error
21
22
23 class L2Util(object):
24     """Utilities for l2 configuration."""
25
26     @staticmethod
27     def vpp_add_l2fib_entry(node, mac, interface, bd_id):
28         """ Create a static L2FIB entry on a vpp node.
29
30         :param node: Node to add L2FIB entry on.
31         :param mac: Destination mac address.
32         :param interface: Interface name or sw_if_index.
33         :param bd_id: Bridge domain id.
34         :type node: dict
35         :type mac: str
36         :type interface: str or int
37         :type bd_id: int
38         """
39         if isinstance(interface, basestring):
40             sw_if_index = Topology.get_interface_sw_index(node, interface)
41         else:
42             sw_if_index = interface
43         VatExecutor.cmd_from_template(node, "add_l2_fib_entry.vat",
44                                       mac=mac, bd=bd_id,
45                                       interface=sw_if_index)
46
47     @staticmethod
48     def create_l2_bd(node, bd_id, flood=1, uu_flood=1, forward=1, learn=1,
49                      arp_term=0):
50         """Create a l2 bridge domain on the chosen VPP node
51
52         Execute "bridge_domain_add_del bd_id {bd_id} flood {flood} uu-flood 1
53         forward {forward} learn {learn} arp-term {arp_term}" VAT command on
54         the node.
55
56         :param node: Node where we wish to crate the l2 bridge domain.
57         :param bd_id: Bridge domain index number.
58         :param flood: Enable flooding.
59         :param uu_flood: Enable uu_flood.
60         :param forward: Enable forwarding.
61         :param learn: Enable mac address learning to fib.
62         :param arp_term: Enable arp_termination.
63         :type node: dict
64         :type bd_id: int
65         :type flood: bool
66         :type uu_flood: bool
67         :type forward: bool
68         :type learn: bool
69         :type arp_term:bool
70         """
71         VatExecutor.cmd_from_template(node, "l2_bd_create.vat",
72                                       bd_id=bd_id, flood=flood,
73                                       uu_flood=uu_flood, forward=forward,
74                                       learn=learn, arp_term=arp_term)
75
76     @staticmethod
77     def add_interface_to_l2_bd(node, interface, bd_id, shg=0):
78         """Add a interface to the l2 bridge domain.
79
80         Get SW IF ID and add it to the bridge domain.
81
82         :param node: Node where we want to execute the command that does this.
83         :param interface: Interface name.
84         :param bd_id: Bridge domain index number to add Interface name to.
85         :param shg: Split horizon group.
86         :type node: dict
87         :type interface: str
88         :type bd_id: int
89         :type shg: int
90         """
91         sw_if_index = Topology.get_interface_sw_index(node, interface)
92         L2Util.add_sw_if_index_to_l2_bd(node, sw_if_index, bd_id, shg)
93
94     @staticmethod
95     def add_sw_if_index_to_l2_bd(node, sw_if_index, bd_id, shg=0):
96         """Add interface with sw_if_index to l2 bridge domain.
97
98         Execute the "sw_interface_set_l2_bridge sw_if_index {sw_if_index}
99         bd_id {bd_id} shg {shg} enable" VAT command on the given node.
100
101         :param node: Node where we want to execute the command that does this.
102         :param sw_if_index: Interface index.
103         :param bd_id: Bridge domain index number to add SW IF ID to.
104         :param shg: Split horizon group.
105         :type node: dict
106         :type sw_if_index: int
107         :type bd_id: int
108         :type shg: int
109         :return:
110         """
111         VatExecutor.cmd_from_template(node, "l2_bd_add_sw_if_index.vat",
112                                       bd_id=bd_id, sw_if_index=sw_if_index,
113                                       shg=shg)
114
115     @staticmethod
116     @keyword('Create dict used in bridge domain template file for node '
117              '"${node}" with links "${link_names}" and bd_id "${bd_id}"')
118     def create_bridge_domain_vat_dict(node, link_names, bd_id):
119         """Create dictionary that can be used in l2 bridge domain template.
120
121         The resulting dictionary looks like this:
122         'interface1': interface name of first interface
123         'interface2': interface name of second interface
124         'bd_id': bridge domain index
125
126         :param node: Node data dictionary.
127         :param link_names: List of names of links the bridge domain should be
128         connecting.
129         :param bd_id: Bridge domain index number.
130         :type node: dict
131         :type link_names: list
132         :return: Dictionary used to generate l2 bridge domain VAT configuration
133         from template file.
134         :rtype: dict
135         """
136         bd_dict = Topology().get_interfaces_by_link_names(node, link_names)
137         bd_dict['bd_id'] = bd_id
138         return bd_dict
139
140     @staticmethod
141     def vpp_add_l2_bridge_domain(node, bd_id, port_1, port_2, learn=True):
142         """Add L2 bridge domain with 2 interfaces to the VPP node.
143
144         :param node: Node to add L2BD on.
145         :param bd_id: Bridge domain ID.
146         :param port_1: First interface name added to L2BD.
147         :param port_2: Second interface name added to L2BD.
148         :param learn: Enable/disable MAC learn.
149         :type node: dict
150         :type bd_id: int
151         :type port_1: str
152         :type port_2: str
153         :type learn: bool
154         """
155         sw_if_index1 = Topology.get_interface_sw_index(node, port_1)
156         sw_if_index2 = Topology.get_interface_sw_index(node, port_2)
157         VatExecutor.cmd_from_template(node,
158                                       'l2_bridge_domain.vat',
159                                       sw_if_id1=sw_if_index1,
160                                       sw_if_id2=sw_if_index2,
161                                       bd_id=bd_id,
162                                       learn=int(learn))
163
164     @staticmethod
165     def vpp_setup_bidirectional_cross_connect(node, interface1, interface2):
166         """Create bidirectional cross-connect between 2 interfaces on vpp node.
167
168         :param node: Node to add bidirectional cross-connect.
169         :param interface1: First interface name or sw_if_index.
170         :param interface2: Second interface name or sw_if_index.
171         :type node: dict
172         :type interface1: str or int
173         :type interface2: str or int
174         """
175
176         if isinstance(interface1, basestring):
177             sw_iface1 = Topology().get_interface_sw_index(node, interface1)
178         else:
179             sw_iface1 = interface1
180
181         if isinstance(interface2, basestring):
182             sw_iface2 = Topology().get_interface_sw_index(node, interface2)
183         else:
184             sw_iface2 = interface2
185
186         with VatTerminal(node) as vat:
187             vat.vat_terminal_exec_cmd_from_template('l2_xconnect.vat',
188                                                     interface1=sw_iface1,
189                                                     interface2=sw_iface2)
190             vat.vat_terminal_exec_cmd_from_template('l2_xconnect.vat',
191                                                     interface1=sw_iface2,
192                                                     interface2=sw_iface1)
193
194     @staticmethod
195     def linux_add_bridge(node, br_name, if_1, if_2, set_up=True):
196         """Bridge two interfaces on linux node.
197
198         :param node: Node to add bridge on.
199         :param br_name: Bridge name.
200         :param if_1: First interface to be added to the bridge.
201         :param if_2: Second interface to be added to the bridge.
202         :param set_up: Change bridge interface state to up after create bridge.
203         Optional. Default: True.
204         :type node: dict
205         :type br_name: str
206         :type if_1: str
207         :type if_2: str
208         :type set_up: bool
209         """
210         cmd = 'brctl addbr {0}'.format(br_name)
211         exec_cmd_no_error(node, cmd, sudo=True)
212         cmd = 'brctl addif {0} {1}'.format(br_name, if_1)
213         exec_cmd_no_error(node, cmd, sudo=True)
214         cmd = 'brctl addif {0} {1}'.format(br_name, if_2)
215         exec_cmd_no_error(node, cmd, sudo=True)
216         if set_up:
217             cmd = 'ip link set dev {0} up'.format(br_name)
218             exec_cmd_no_error(node, cmd, sudo=True)
219
220     @staticmethod
221     def setup_network_namespace(node, namespace_name, interface_name,
222                                 ip_address, prefix):
223         """Setup namespace on given node and attach interface and IP to
224         this namespace. Applicable also on TG node.
225
226         :param node: Node to set namespace on.
227         :param namespace_name: Namespace name.
228         :param interface_name: Interface name.
229         :param ip_address: IP address of namespace's interface.
230         :param prefix: IP address prefix length.
231         :type node: dict
232         :type namespace_name: str
233         :type vhost_if: str
234         :type ip_address: str
235         :type prefix: int
236
237         """
238         cmd = ('ip netns add {0}'.format(namespace_name))
239         exec_cmd_no_error(node, cmd, sudo=True)
240
241         cmd = ('ip link set dev {0} up netns {1}'.format(interface_name,
242                                                          namespace_name))
243         exec_cmd_no_error(node, cmd, sudo=True)
244
245         cmd = ('ip netns exec {0} ip addr add {1}/{2} dev {3}'.format(
246             namespace_name, ip_address, prefix, interface_name))
247         exec_cmd_no_error(node, cmd, sudo=True)
248
249     @staticmethod
250     def linux_del_bridge(node, br_name, set_down=True):
251         """Delete bridge from linux node.
252
253         :param node: Node to delete bridge from.
254         :param br_name: Bridge name.
255         :param set_down: Change bridge interface state to down before delbr
256         command. Optional. Default: True.
257         :type node: str
258         :type br_name: str
259         :type set_down: bool
260         ..note:: The network interface corresponding to the bridge must be
261         down before it can be deleted!
262         """
263         if set_down:
264             cmd = 'ip link set dev {0} down'.format(br_name)
265             exec_cmd_no_error(node, cmd, sudo=True)
266         cmd = 'brctl delbr {0}'.format(br_name)
267         exec_cmd_no_error(node, cmd, sudo=True)
268
269     @staticmethod
270     def vpp_get_bridge_domain_data(node, bd_id=None):
271         """Get all bridge domain data from a VPP node. If a domain ID number is
272         provided, return only data for the matching bridge domain.
273
274         :param node: VPP node to get bridge domain data from.
275         :param bd_id: Numeric ID of a specific bridge domain.
276         :type node: dict
277         :type bd_id: int
278         :return: List of dictionaries containing data for each bridge domain, or
279          a single dictionary for the specified bridge domain.
280         :rtype: list or dict
281         """
282         with VatTerminal(node) as vat:
283             response = vat.vat_terminal_exec_cmd_from_template("l2_bd_dump.vat")
284
285         data = response[0]
286
287         if bd_id is not None:
288             for bridge_domain in data:
289                 if bridge_domain["bd_id"] == bd_id:
290
291                     return bridge_domain
292
293         return data
294
295     @staticmethod
296     def l2_vlan_tag_rewrite(node, interface, tag_rewrite_method,
297                             push_dot1q=True, tag1_id=None, tag2_id=None):
298         """Rewrite tags in ethernet frame.
299
300         :param node: Node to rewrite tags.
301         :param interface: Interface on which rewrite tags.
302         :param tag_rewrite_method: Method of tag rewrite.
303         :param push_dot1q: Optional parameter to disable to push dot1q tag
304          instead of dot1ad.
305         :param tag1_id: Optional tag1 ID for VLAN.
306         :param tag2_id: Optional tag2 ID for VLAN.
307         :type node: dict
308         :type interface: str or int
309         :type tag_rewrite_method: str
310         :type push_dot1q: bool
311         :type tag1_id: int
312         :type tag2_id: int
313         """
314         push_dot1q = 'push_dot1q 0' if not push_dot1q else ''
315
316         tag1_id = 'tag1 {0}'.format(tag1_id) if tag1_id else ''
317         tag2_id = 'tag2 {0}'.format(tag2_id) if tag2_id else ''
318
319         if isinstance(interface, basestring):
320             iface_key = Topology.get_interface_by_name(node, interface)
321             sw_if_index = Topology.get_interface_sw_index(node, iface_key)
322         else:
323             sw_if_index = interface
324
325         with VatTerminal(node) as vat:
326             vat.vat_terminal_exec_cmd_from_template("l2_vlan_tag_rewrite.vat",
327                                                     sw_if_index=sw_if_index,
328                                                     tag_rewrite_method=
329                                                     tag_rewrite_method,
330                                                     push_dot1q=push_dot1q,
331                                                     tag1_optional=tag1_id,
332                                                     tag2_optional=tag2_id)
333
334     @staticmethod
335     def delete_bridge_domain_vat(node, bd_id):
336         """Delete the specified bridge domain from the node.
337
338         :param node: VPP node to delete a bridge domain from.
339         :param bd_id: Bridge domain ID.
340         :type node: dict
341         :type bd_id: int
342         """
343
344         with VatTerminal(node) as vat:
345             vat.vat_terminal_exec_cmd_from_template(
346                 "l2_bridge_domain_delete.vat", bd_id=bd_id)
347
348     @staticmethod
349     def delete_l2_fib_entry(node, bd_id, mac):
350         """Delete the specified L2 FIB entry.
351
352         :param node: VPP node.
353         :param bd_id: Bridge domain ID.
354         :param mac: MAC address used as the key in L2 FIB entry.
355         :type node: dict
356         :type bd_id: int
357         :type mac: str
358         """
359
360         with VatTerminal(node) as vat:
361             vat.vat_terminal_exec_cmd_from_template("l2_fib_entry_delete.vat",
362                                                     mac=mac,
363                                                     bd_id=bd_id)
364
365     @staticmethod
366     def get_l2_fib_table_vat(node, bd_index):
367         """Retrieves the L2 FIB table using VAT.
368
369         :param node: VPP node.
370         :param bd_index: Index of the bridge domain.
371         :type node: dict
372         :type bd_index: int
373         :return: L2 FIB table.
374         :rtype: list
375         """
376
377         bd_data = L2Util.vpp_get_bridge_domain_data(node)
378         bd_id = bd_data[bd_index-1]["bd_id"]
379
380         try:
381             with VatTerminal(node) as vat:
382                 table = vat.vat_terminal_exec_cmd_from_template(
383                     "l2_fib_table_dump.vat", bd_id=bd_id)
384
385             return table[0]
386         except ValueError:
387             return []
388
389     @staticmethod
390     def get_l2_fib_entry_vat(node, bd_index, mac):
391         """Retrieves the L2 FIB entry specified by MAC address using VAT.
392
393         :param node: VPP node.
394         :param bd_index: Index of the bridge domain.
395         :param mac: MAC address used as the key in L2 FIB data structure.
396         :type node: dict
397         :type bd_index: int
398         :type mac: str
399         :return: L2 FIB entry
400         :rtype: dict
401         """
402
403         table = L2Util.get_l2_fib_table_vat(node, bd_index)
404         for entry in table:
405             if entry["mac"] == mac:
406                 return entry
407         return {}