@@ -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 ())]
0 commit comments