Skip to content

Commit 986c8b8

Browse files
authored
Enhanced Everflow test cases to support multi-asic (#2694)
Why/What I did: Enhanced everflow test cases to support multiasic. This is continuation of the #2621 which made generic infra changes How I did: Brought the notion of namespace in port mapping . Minigraph changes were done as part of #2516 Programming of VTYSH command for namespace Added Logic to create source_port set so that mirror traffic is sent across namespace and within namespace.
1 parent ea6535e commit 986c8b8

File tree

4 files changed

+207
-130
lines changed

4 files changed

+207
-130
lines changed

tests/common/devices.py

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2084,6 +2084,13 @@ def get_namespace_from_asic_id(self, asic_id):
20842084
# Raise an error if we reach here
20852085
raise ValueError("Invalid asic_id '{}' passed as input".format(asic_id))
20862086

2087+
def get_vtysh_cmd_for_namespace(self, cmd, namespace):
2088+
asic_id = self.get_asic_id_from_namespace(namespace)
2089+
if asic_id == DEFAULT_ASIC_ID:
2090+
return cmd
2091+
ns_cmd = cmd.replace('vtysh', 'vtysh -n {}'.format(asic_id))
2092+
return ns_cmd
2093+
20872094
def __getattr__(self, attr):
20882095
""" To support calling an ansible module on a MultiAsicSonicHost.
20892096

tests/everflow/everflow_test_utilities.py

Lines changed: 124 additions & 73 deletions
Original file line numberDiff line numberDiff line change
@@ -46,8 +46,16 @@ def setup_info(duthosts, rand_one_dut_hostname, tbinfo):
4646
"""
4747
duthost = duthosts[rand_one_dut_hostname]
4848

49-
tor_ports = []
50-
spine_ports = []
49+
# { namespace : [tor ports] }
50+
tor_ports_namespace_map = defaultdict(list)
51+
# { namespace : [spine ports] }
52+
spine_ports_namespace_map = defaultdict(list)
53+
54+
# { set of namespace tor ports belongs }
55+
tor_ports_namespace = set()
56+
# { set of namespace spine ports belongs }
57+
spine_ports_namespace = set()
58+
5159

5260
# Gather test facts
5361
mg_facts = duthost.get_extended_minigraph_facts(tbinfo)
@@ -57,9 +65,28 @@ def setup_info(duthosts, rand_one_dut_hostname, tbinfo):
5765
# TODO: The ACL tests do something really similar, I imagine we could refactor this bit.
5866
for dut_port, neigh in mg_facts["minigraph_neighbors"].items():
5967
if "T0" in neigh["name"]:
60-
tor_ports.append(dut_port)
68+
# Add Tor ports to namespace
69+
tor_ports_namespace_map[neigh['namespace']].append(dut_port)
70+
tor_ports_namespace.add(neigh['namespace'])
6171
elif "T2" in neigh["name"]:
62-
spine_ports.append(dut_port)
72+
# Add Spine ports to namespace
73+
spine_ports_namespace_map[neigh['namespace']].append(dut_port)
74+
spine_ports_namespace.add(neigh['namespace'])
75+
76+
# Set of TOR ports only Namespace
77+
tor_only_namespace = tor_ports_namespace.difference(spine_ports_namespace)
78+
# Set of Spine ports only Namespace
79+
spine_only_namespace = spine_ports_namespace.difference(tor_ports_namespace)
80+
81+
# Randomly choose from TOR_only Namespace if present else just use first one
82+
tor_namespace = random.choice(tuple(tor_only_namespace)) if tor_only_namespace else tuple(tor_ports_namespace)[0]
83+
# Randomly choose from Spine_only Namespace if present else just use first one
84+
spine_namespace = random.choice(tuple(spine_only_namespace)) if spine_only_namespace else tuple(spine_ports_namespace)[0]
85+
86+
# Get the corresponding namespace ports
87+
tor_ports = tor_ports_namespace_map[tor_namespace]
88+
spine_ports = spine_ports_namespace_map[spine_namespace]
89+
6390

6491
switch_capabilities = switch_capability_facts["switch_capabilities"]["switch"]
6592

@@ -137,75 +164,73 @@ def get_port_info(in_port_list, out_port_list, out_port_ptf_id_list, out_port_la
137164
"src_port_ptf_id": str(mg_facts["minigraph_ptf_indices"][spine_ports[0]]),
138165
"dest_port": tor_dest_ports,
139166
"dest_port_ptf_id": tor_dest_ports_ptf_id,
140-
"dest_port_lag_name": tor_dest_lag_name
167+
"dest_port_lag_name": tor_dest_lag_name,
168+
"namespace": tor_namespace
141169
},
142170
"spine": {
143171
"src_port": tor_ports[0],
144172
"src_port_ptf_id": str(mg_facts["minigraph_ptf_indices"][tor_ports[0]]),
145173
"dest_port": spine_dest_ports,
146174
"dest_port_ptf_id": spine_dest_ports_ptf_id,
147-
"dest_port_lag_name": spine_dest_lag_name
175+
"dest_port_lag_name": spine_dest_lag_name,
176+
"namespace": spine_namespace
148177
},
149178
"port_index_map": {
150179
k: v
151180
for k, v in mg_facts["minigraph_ptf_indices"].items()
152181
if k in mg_facts["minigraph_ports"]
182+
},
183+
# { ptf_port_id : namespace }
184+
"port_index_namespace_map" : {
185+
v: mg_facts["minigraph_neighbors"][k]['namespace']
186+
for k, v in mg_facts["minigraph_ptf_indices"].items()
187+
if k in mg_facts["minigraph_ports"]
153188
}
154189
}
155190

156-
# NOTE: This is important to add since for the Policer test case regular packets
157-
# and mirror packets can go to same interface, which causes tail drop of
158-
# police packets and impacts test case cir/cbs calculation.
159-
#
160-
# We are making sure regular traffic has a dedicated route and does not use
161-
# the default route.
162-
163-
peer_ip, _ = get_neighbor_info(duthost, spine_dest_ports[3], tbinfo)
164-
165-
# Disable recursive route resolution as we have test case where we check
166-
# if better unresolved route is there then it should not be picked by Mirror state DB
167-
# This change is triggeed by Sonic PR#https://github.com/Azure/sonic-buildimage/pull/5600
168-
duthost.shell("vtysh -c \"configure terminal\" -c \"no ip nht resolve-via-default\"")
169-
170-
add_route(duthost, "30.0.0.1/24", peer_ip)
171-
191+
# Disable BGP so that we don't keep on bouncing back mirror packets
192+
# If we send TTL=1 packet we don't need this but in multi-asic TTL > 1
193+
duthost.command("sudo config bgp shutdown all")
194+
time.sleep(60)
172195
duthost.command("mkdir -p {}".format(DUT_RUN_DIR))
173-
196+
174197
yield setup_information
175-
198+
199+
# Enable BGP again
200+
duthost.command("sudo config bgp startup all")
201+
time.sleep(60)
176202
duthost.command("rm -rf {}".format(DUT_RUN_DIR))
177-
178-
remove_route(duthost, "30.0.0.1/24", peer_ip)
179-
180-
duthost.shell("vtysh -c \"configure terminal\" -c \"ip nht resolve-via-default\"")
181-
203+
182204

183205
# TODO: This should be refactored to some common area of sonic-mgmt.
184-
def add_route(duthost, prefix, nexthop):
206+
def add_route(duthost, prefix, nexthop, namespace):
185207
"""
186208
Add a route to the DUT.
187209
188210
Args:
189211
duthost: DUT fixture
190212
prefix: IP prefix for the route
191213
nexthop: next hop for the route
214+
namespace: namsespace/asic to add the route
192215
193216
"""
194-
duthost.shell("vtysh -c \"configure terminal\" -c \"ip route {} {}\"".format(prefix, nexthop))
217+
duthost.shell(duthost.get_vtysh_cmd_for_namespace("vtysh -c \"configure terminal\" -c \"ip route {} {}\"".format(prefix, nexthop), namespace))
218+
195219

196220

197221
# TODO: This should be refactored to some common area of sonic-mgmt.
198-
def remove_route(duthost, prefix, nexthop):
222+
def remove_route(duthost, prefix, nexthop, namespace):
199223
"""
200224
Remove a route from the DUT.
201225
202226
Args:
203227
duthost: DUT fixture
204228
prefix: IP prefix to remove
205229
nexthop: next hop to remove
230+
namespace: namsespace/asic to remove the route
206231
207232
"""
208-
duthost.shell("vtysh -c \"configure terminal\" -c \"no ip route {} {}\"".format(prefix, nexthop))
233+
duthost.shell(duthost.get_vtysh_cmd_for_namespace("vtysh -c \"configure terminal\" -c \"no ip route {} {}\"".format(prefix, nexthop), namespace))
209234

210235

211236
# TODO: This should be refactored to some common area of sonic-mgmt.
@@ -220,7 +245,7 @@ def get_neighbor_info(duthost, dest_port, tbinfo, resolved=True):
220245
221246
"""
222247
if not resolved:
223-
return "20.20.20.100", None
248+
return "20.20.20.100"
224249

225250
mg_facts = duthost.get_extended_minigraph_facts(tbinfo)
226251

@@ -229,7 +254,7 @@ def get_neighbor_info(duthost, dest_port, tbinfo, resolved=True):
229254
peer_ip = bgp_peer["addr"]
230255
break
231256

232-
return peer_ip, duthost.shell("ip neigh show {} | awk -F\" \" \"{{print $5}}\"".format(peer_ip))["stdout"]
257+
return peer_ip
233258

234259

235260
# TODO: This can probably be moved to a shared location in a later PR.
@@ -475,7 +500,8 @@ def send_and_check_mirror_packets(self,
475500
mirror_packet,
476501
src_port=None,
477502
dest_ports=None,
478-
expect_recv=True):
503+
expect_recv=True,
504+
valid_across_namespace=True):
479505
expected_mirror_packet = self._get_expected_mirror_packet(mirror_session,
480506
setup,
481507
duthost,
@@ -487,42 +513,63 @@ def send_and_check_mirror_packets(self,
487513
if not dest_ports:
488514
dest_ports = [self._get_monitor_port(setup, mirror_session, duthost)]
489515

490-
ptfadapter.dataplane.flush()
491-
testutils.send(ptfadapter, src_port, mirror_packet)
492-
493-
if expect_recv:
494-
_, received_packet = testutils.verify_packet_any_port(
495-
ptfadapter,
496-
expected_mirror_packet,
497-
ports=dest_ports
498-
)
499-
logging.info("Received packet: %s", packet.Ether(received_packet).summary())
500-
501-
inner_packet = self._extract_mirror_payload(received_packet, len(mirror_packet))
502-
logging.info("Received inner packet: %s", inner_packet.summary())
503-
504-
inner_packet = Mask(inner_packet)
505-
506-
# For egress mirroring, we expect the DUT to have modified the packet
507-
# before forwarding it. Specifically:
508-
#
509-
# - In L2 the SMAC and DMAC will change.
510-
# - In L3 the TTL and checksum will change.
511-
#
512-
# We know what the TTL and SMAC should be after going through the pipeline,
513-
# but DMAC and checksum are trickier. For now, update the TTL and SMAC, and
514-
# mask off the DMAC and IP Checksum to verify the packet contents.
515-
if self.mirror_type() == "egress":
516-
mirror_packet[packet.IP].ttl -= 1
517-
mirror_packet[packet.Ether].src = setup["router_mac"]
518-
519-
inner_packet.set_do_not_care_scapy(packet.Ether, "dst")
520-
inner_packet.set_do_not_care_scapy(packet.IP, "chksum")
521-
522-
logging.info("Expected inner packet: %s", mirror_packet.summary())
523-
pytest_assert(inner_packet.pkt_match(mirror_packet), "Mirror payload does not match received packet")
524-
else:
525-
testutils.verify_no_packet_any(ptfadapter, expected_mirror_packet, dest_ports)
516+
# In Below logic idea is to send traffic in such a way so that mirror traffic
517+
# will need to go across namespaces and within namespace. If source and mirror destination
518+
# namespace are different then traffic mirror will go across namespace via (backend asic)
519+
# else via same namespace(asic)
520+
521+
src_port_namespace = self._get_port_namespace(setup, int(src_port))
522+
dest_ports_namespace = self._get_port_namespace(setup,int (dest_ports[0]))
523+
524+
src_port_set = set()
525+
526+
# Some of test scenario are not valid across namespaces so test will explicltly pass
527+
# valid_across_namespace as False (default is True)
528+
if valid_across_namespace == True or src_port_namespace == dest_ports_namespace:
529+
src_port_set.add(src_port)
530+
531+
# To verify same namespace mirroring we will add destination port also to the Source Port Set
532+
if src_port_namespace != dest_ports_namespace:
533+
src_port_set.add(dest_ports[0])
534+
535+
# Loop through Source Port Set and send traffic on each source port of the set
536+
for src_port in src_port_set:
537+
ptfadapter.dataplane.flush()
538+
testutils.send(ptfadapter, src_port, mirror_packet)
539+
540+
if expect_recv:
541+
_, received_packet = testutils.verify_packet_any_port(
542+
ptfadapter,
543+
expected_mirror_packet,
544+
ports=dest_ports
545+
)
546+
logging.info("Received packet: %s", packet.Ether(received_packet).summary())
547+
548+
inner_packet = self._extract_mirror_payload(received_packet, len(mirror_packet))
549+
logging.info("Received inner packet: %s", inner_packet.summary())
550+
551+
inner_packet = Mask(inner_packet)
552+
553+
# For egress mirroring, we expect the DUT to have modified the packet
554+
# before forwarding it. Specifically:
555+
#
556+
# - In L2 the SMAC and DMAC will change.
557+
# - In L3 the TTL and checksum will change.
558+
#
559+
# We know what the TTL and SMAC should be after going through the pipeline,
560+
# but DMAC and checksum are trickier. For now, update the TTL and SMAC, and
561+
# mask off the DMAC and IP Checksum to verify the packet contents.
562+
if self.mirror_type() == "egress":
563+
mirror_packet[packet.IP].ttl -= 1
564+
mirror_packet[packet.Ether].src = setup["router_mac"]
565+
566+
inner_packet.set_do_not_care_scapy(packet.Ether, "dst")
567+
inner_packet.set_do_not_care_scapy(packet.IP, "chksum")
568+
569+
logging.info("Expected inner packet: %s", mirror_packet.summary())
570+
pytest_assert(inner_packet.pkt_match(mirror_packet), "Mirror payload does not match received packet")
571+
else:
572+
testutils.verify_no_packet_any(ptfadapter, expected_mirror_packet, dest_ports)
526573

527574
def _get_expected_mirror_packet(self, mirror_session, setup, duthost, mirror_packet):
528575
payload = mirror_packet.copy()
@@ -552,6 +599,7 @@ def _get_expected_mirror_packet(self, mirror_session, setup, duthost, mirror_pac
552599
expected_packet.set_do_not_care_scapy(packet.IP, "len")
553600
expected_packet.set_do_not_care_scapy(packet.IP, "flags")
554601
expected_packet.set_do_not_care_scapy(packet.IP, "chksum")
602+
expected_packet.set_do_not_care_scapy(packet.IP, "ttl")
555603

556604
# The fanout switch may modify this value en route to the PTF so we should ignore it, even
557605
# though the session does have a DSCP specified.
@@ -573,7 +621,7 @@ def _mirror_session_info(self, session_name, asic_type):
573621
session_src_ip = "1.1.1.1"
574622
session_dst_ip = "2.2.2.2"
575623
session_dscp = "8"
576-
session_ttl = "1"
624+
session_ttl = "4"
577625

578626
if "mellanox" == asic_type:
579627
session_gre = 0x8949
@@ -596,6 +644,9 @@ def _mirror_session_info(self, session_name, asic_type):
596644
"session_gre": session_gre,
597645
"session_prefixes": session_prefixes
598646
}
647+
648+
def _get_port_namespace(self,setup, port):
649+
return setup["port_index_namespace_map"][port]
599650

600651
def _get_random_src_port(self, setup):
601652
return setup["port_index_map"][random.choice(setup["port_index_map"].keys())]

tests/everflow/test_everflow_ipv6.py

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -38,13 +38,15 @@ def setup_mirror_session_dest_ip_route(self, duthosts, rand_one_dut_hostname, tb
3838
"""
3939

4040
duthost = duthosts[rand_one_dut_hostname]
41+
duthost.shell(duthost.get_vtysh_cmd_for_namespace("vtysh -c \"config\" -c \"router bgp\" -c \"address-family ipv4\" -c \"redistribute static\"",setup_info["tor"]["namespace"]))
4142
tx_port = setup_info["tor"]["dest_port"][0]
42-
peer_ip, _ = everflow_utils.get_neighbor_info(duthost, tx_port, tbinfo)
43-
everflow_utils.add_route(duthost, setup_mirror_session["session_prefixes"][0], peer_ip)
43+
peer_ip = everflow_utils.get_neighbor_info(duthost, tx_port, tbinfo)
44+
everflow_utils.add_route(duthost, setup_mirror_session["session_prefixes"][0], peer_ip, setup_info["tor"]["namespace"])
4445
self.tx_port_ids = self._get_tx_port_id_list([setup_info["tor"]["dest_port_ptf_id"][0]])
4546
time.sleep(5)
4647
yield
47-
everflow_utils.remove_route(duthost, setup_mirror_session["session_prefixes"][0], peer_ip)
48+
everflow_utils.remove_route(duthost, setup_mirror_session["session_prefixes"][0], peer_ip, setup_info["tor"]["namespace"])
49+
duthost.shell(duthost.get_vtysh_cmd_for_namespace("vtysh -c \"config\" -c \"router bgp\" -c \"address-family ipv4\" -c \"no redistribute static\"",setup_info["tor"]["namespace"]))
4850

4951
def test_src_ipv6_mirroring(self, setup_info, setup_mirror_session, ptfadapter, duthosts, rand_one_dut_hostname):
5052
"""Verify that we can match on Source IPv6 addresses."""

0 commit comments

Comments
 (0)