1 # Copyright (c) 2018 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:
6 # http://www.apache.org/licenses/LICENSE-2.0
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.
14 """L2 Utilities Library."""
16 from textwrap import wrap
18 from robot.api.deco import keyword
20 from resources.libraries.python.topology import Topology
21 from resources.libraries.python.VatExecutor import VatExecutor, VatTerminal
22 from resources.libraries.python.ssh import exec_cmd_no_error
26 """Utilities for l2 configuration."""
29 def mac_to_int(mac_str):
30 """Convert MAC address from string format (e.g. 01:02:03:04:05:06) to
31 integer representation (1108152157446).
33 :param mac_str: MAC address in string representation.
35 :returns: Integer representation of MAC address.
38 return int(mac_str.replace(':', ''), 16)
41 def int_to_mac(mac_int):
42 """Convert MAC address from integer representation (e.g. 1108152157446)
43 to string format (01:02:03:04:05:06).
45 :param mac_int: MAC address in integer representation.
47 :returns: String representation of MAC address.
50 return ':'.join(wrap("{:012x}".format(mac_int), width=2))
53 def vpp_add_l2fib_entry(node, mac, interface, bd_id):
54 """ Create a static L2FIB entry on a vpp node.
56 :param node: Node to add L2FIB entry on.
57 :param mac: Destination mac address.
58 :param interface: Interface name or sw_if_index.
59 :param bd_id: Bridge domain id.
62 :type interface: str or int
65 if isinstance(interface, basestring):
66 sw_if_index = Topology.get_interface_sw_index(node, interface)
68 sw_if_index = interface
69 VatExecutor.cmd_from_template(node, "add_l2_fib_entry.vat",
71 interface=sw_if_index)
74 def create_l2_bd(node, bd_id, flood=1, uu_flood=1, forward=1, learn=1,
76 """Create a l2 bridge domain on the chosen VPP node
78 Execute "bridge_domain_add_del bd_id {bd_id} flood {flood} uu-flood 1
79 forward {forward} learn {learn} arp-term {arp_term}" VAT command on
82 :param node: Node where we wish to crate the l2 bridge domain.
83 :param bd_id: Bridge domain index number.
84 :param flood: Enable flooding.
85 :param uu_flood: Enable uu_flood.
86 :param forward: Enable forwarding.
87 :param learn: Enable mac address learning to fib.
88 :param arp_term: Enable arp_termination.
97 VatExecutor.cmd_from_template(node, "l2_bd_create.vat",
98 bd_id=bd_id, flood=flood,
99 uu_flood=uu_flood, forward=forward,
100 learn=learn, arp_term=arp_term)
103 def add_interface_to_l2_bd(node, interface, bd_id, shg=0):
104 """Add a interface to the l2 bridge domain.
106 Get SW IF ID and add it to the bridge domain.
108 :param node: Node where we want to execute the command that does this.
109 :param interface: Interface name.
110 :param bd_id: Bridge domain index number to add Interface name to.
111 :param shg: Split horizon group.
117 sw_if_index = Topology.get_interface_sw_index(node, interface)
118 L2Util.add_sw_if_index_to_l2_bd(node, sw_if_index, bd_id, shg)
121 def add_sw_if_index_to_l2_bd(node, sw_if_index, bd_id, shg=0):
122 """Add interface with sw_if_index to l2 bridge domain.
124 Execute the "sw_interface_set_l2_bridge sw_if_index {sw_if_index}
125 bd_id {bd_id} shg {shg} enable" VAT command on the given node.
127 :param node: Node where we want to execute the command that does this.
128 :param sw_if_index: Interface index.
129 :param bd_id: Bridge domain index number to add SW IF ID to.
130 :param shg: Split horizon group.
132 :type sw_if_index: int
136 VatExecutor.cmd_from_template(node, "l2_bd_add_sw_if_index.vat",
137 bd_id=bd_id, sw_if_index=sw_if_index,
141 @keyword('Create dict used in bridge domain template file for node '
142 '"${node}" with links "${link_names}" and bd_id "${bd_id}"')
143 def create_bridge_domain_vat_dict(node, link_names, bd_id):
144 """Create dictionary that can be used in l2 bridge domain template.
146 The resulting dictionary looks like this:
147 'interface1': interface name of first interface
148 'interface2': interface name of second interface
149 'bd_id': bridge domain index
151 :param node: Node data dictionary.
152 :param link_names: List of names of links the bridge domain should be
154 :param bd_id: Bridge domain index number.
156 :type link_names: list
157 :returns: Dictionary used to generate l2 bridge domain VAT configuration
161 bd_dict = Topology().get_interfaces_by_link_names(node, link_names)
162 bd_dict['bd_id'] = bd_id
166 def vpp_add_l2_bridge_domain(node, bd_id, port_1, port_2, learn=True):
167 """Add L2 bridge domain with 2 interfaces to the VPP node.
169 :param node: Node to add L2BD on.
170 :param bd_id: Bridge domain ID.
171 :param port_1: First interface name added to L2BD.
172 :param port_2: Second interface name added to L2BD.
173 :param learn: Enable/disable MAC learn.
180 sw_if_index1 = Topology.get_interface_sw_index(node, port_1)
181 sw_if_index2 = Topology.get_interface_sw_index(node, port_2)
182 VatExecutor.cmd_from_template(node,
183 'l2_bridge_domain.vat',
184 sw_if_id1=sw_if_index1,
185 sw_if_id2=sw_if_index2,
190 def vpp_setup_bidirectional_cross_connect(node, interface1, interface2):
191 """Create bidirectional cross-connect between 2 interfaces on vpp node.
193 :param node: Node to add bidirectional cross-connect.
194 :param interface1: First interface name or sw_if_index.
195 :param interface2: Second interface name or sw_if_index.
197 :type interface1: str or int
198 :type interface2: str or int
201 if isinstance(interface1, basestring):
202 sw_iface1 = Topology().get_interface_sw_index(node, interface1)
204 sw_iface1 = interface1
206 if isinstance(interface2, basestring):
207 sw_iface2 = Topology().get_interface_sw_index(node, interface2)
209 sw_iface2 = interface2
211 with VatTerminal(node) as vat:
212 vat.vat_terminal_exec_cmd_from_template('l2_xconnect.vat',
213 interface1=sw_iface1,
214 interface2=sw_iface2)
215 vat.vat_terminal_exec_cmd_from_template('l2_xconnect.vat',
216 interface1=sw_iface2,
217 interface2=sw_iface1)
220 def vpp_setup_bidirectional_l2_patch(node, interface1, interface2):
221 """Create bidirectional l2 patch between 2 interfaces on vpp node.
223 :param node: Node to add bidirectional l2 patch.
224 :param interface1: First interface name or sw_if_index.
225 :param interface2: Second interface name or sw_if_index.
227 :type interface1: str or int
228 :type interface2: str or int
231 if isinstance(interface1, basestring):
232 sw_iface1 = Topology().get_interface_sw_index(node, interface1)
234 sw_iface1 = interface1
236 if isinstance(interface2, basestring):
237 sw_iface2 = Topology().get_interface_sw_index(node, interface2)
239 sw_iface2 = interface2
241 with VatTerminal(node) as vat:
242 vat.vat_terminal_exec_cmd_from_template('l2_patch.vat',
243 interface1=sw_iface1,
244 interface2=sw_iface2)
245 vat.vat_terminal_exec_cmd_from_template('l2_patch.vat',
246 interface1=sw_iface2,
247 interface2=sw_iface1)
250 def linux_add_bridge(node, br_name, if_1, if_2, set_up=True):
251 """Bridge two interfaces on linux node.
253 :param node: Node to add bridge on.
254 :param br_name: Bridge name.
255 :param if_1: First interface to be added to the bridge.
256 :param if_2: Second interface to be added to the bridge.
257 :param set_up: Change bridge interface state to up after create bridge.
258 Optional. Default: True.
265 cmd = 'brctl addbr {0}'.format(br_name)
266 exec_cmd_no_error(node, cmd, sudo=True)
267 cmd = 'brctl addif {0} {1}'.format(br_name, if_1)
268 exec_cmd_no_error(node, cmd, sudo=True)
269 cmd = 'brctl addif {0} {1}'.format(br_name, if_2)
270 exec_cmd_no_error(node, cmd, sudo=True)
272 cmd = 'ip link set dev {0} up'.format(br_name)
273 exec_cmd_no_error(node, cmd, sudo=True)
276 def linux_del_bridge(node, br_name, set_down=True):
277 """Delete bridge from linux node.
279 ..note:: The network interface corresponding to the bridge must be
280 down before it can be deleted!
282 :param node: Node to delete bridge from.
283 :param br_name: Bridge name.
284 :param set_down: Change bridge interface state to down before delbr
285 command. Optional. Default: True.
291 cmd = 'ip link set dev {0} down'.format(br_name)
292 exec_cmd_no_error(node, cmd, sudo=True)
293 cmd = 'brctl delbr {0}'.format(br_name)
294 exec_cmd_no_error(node, cmd, sudo=True)
297 def vpp_get_bridge_domain_data(node, bd_id=None):
298 """Get all bridge domain data from a VPP node. If a domain ID number is
299 provided, return only data for the matching bridge domain.
301 :param node: VPP node to get bridge domain data from.
302 :param bd_id: Numeric ID of a specific bridge domain.
305 :returns: List of dictionaries containing data for each bridge domain,
306 or a single dictionary for the specified bridge domain.
309 with VatTerminal(node) as vat:
310 response = vat.vat_terminal_exec_cmd_from_template("l2_bd_dump.vat")
314 if bd_id is not None:
315 for bridge_domain in data:
316 if bridge_domain["bd_id"] == bd_id:
323 def l2_vlan_tag_rewrite(node, interface, tag_rewrite_method,
324 push_dot1q=True, tag1_id=None, tag2_id=None):
325 """Rewrite tags in ethernet frame.
327 :param node: Node to rewrite tags.
328 :param interface: Interface on which rewrite tags.
329 :param tag_rewrite_method: Method of tag rewrite.
330 :param push_dot1q: Optional parameter to disable to push dot1q tag
332 :param tag1_id: Optional tag1 ID for VLAN.
333 :param tag2_id: Optional tag2 ID for VLAN.
335 :type interface: str or int
336 :type tag_rewrite_method: str
337 :type push_dot1q: bool
341 push_dot1q = 'push_dot1q 0' if not push_dot1q else ''
343 tag1_id = 'tag1 {0}'.format(tag1_id) if tag1_id else ''
344 tag2_id = 'tag2 {0}'.format(tag2_id) if tag2_id else ''
346 if isinstance(interface, basestring):
347 iface_key = Topology.get_interface_by_name(node, interface)
348 sw_if_index = Topology.get_interface_sw_index(node, iface_key)
350 sw_if_index = interface
352 with VatTerminal(node) as vat:
353 vat.vat_terminal_exec_cmd_from_template("l2_vlan_tag_rewrite.vat",
354 sw_if_index=sw_if_index,
357 push_dot1q=push_dot1q,
358 tag1_optional=tag1_id,
359 tag2_optional=tag2_id)
362 def delete_bridge_domain_vat(node, bd_id):
363 """Delete the specified bridge domain from the node.
365 :param node: VPP node to delete a bridge domain from.
366 :param bd_id: Bridge domain ID.
371 with VatTerminal(node) as vat:
372 vat.vat_terminal_exec_cmd_from_template(
373 "l2_bridge_domain_delete.vat", bd_id=bd_id)
376 def delete_l2_fib_entry(node, bd_id, mac):
377 """Delete the specified L2 FIB entry.
379 :param node: VPP node.
380 :param bd_id: Bridge domain ID.
381 :param mac: MAC address used as the key in L2 FIB entry.
387 with VatTerminal(node) as vat:
388 vat.vat_terminal_exec_cmd_from_template("l2_fib_entry_delete.vat",
393 def get_l2_fib_table_vat(node, bd_index):
394 """Retrieves the L2 FIB table using VAT.
396 :param node: VPP node.
397 :param bd_index: Index of the bridge domain.
400 :returns: L2 FIB table.
404 bd_data = L2Util.vpp_get_bridge_domain_data(node)
405 bd_id = bd_data[bd_index-1]["bd_id"]
408 with VatTerminal(node) as vat:
409 table = vat.vat_terminal_exec_cmd_from_template(
410 "l2_fib_table_dump.vat", bd_id=bd_id)
417 def get_l2_fib_entry_vat(node, bd_index, mac):
418 """Retrieves the L2 FIB entry specified by MAC address using VAT.
420 :param node: VPP node.
421 :param bd_index: Index of the bridge domain.
422 :param mac: MAC address used as the key in L2 FIB data structure.
426 :returns: L2 FIB entry
430 table = L2Util.get_l2_fib_table_vat(node, bd_index)
432 if entry["mac"] == mac: