diff --git a/tests/forwarding/BUILD.bazel b/tests/forwarding/BUILD.bazel index 5255b4ec..25f695bc 100644 --- a/tests/forwarding/BUILD.bazel +++ b/tests/forwarding/BUILD.bazel @@ -205,7 +205,6 @@ cc_library( srcs = ["l3_admit_test.cc"], hdrs = ["l3_admit_test.h"], deps = [ - ":mirror_blackbox_test_fixture", ":util", "//gutil:proto", "//gutil:status_matchers", @@ -215,16 +214,17 @@ cc_library( "//p4_pdpi:p4_runtime_session", "//p4_pdpi/packetlib", "//p4_pdpi/packetlib:packetlib_cc_proto", - "//sai_p4/instantiations/google:instantiations", - "//sai_p4/instantiations/google:sai_p4info_cc", "//tests/lib:p4info_helper", "//tests/lib:p4rt_fixed_table_programming_helper", "//tests/lib:packet_in_helper", + "//tests/lib:switch_test_setup_helpers", "//thinkit:mirror_testbed_fixture", + "//thinkit:switch", "@com_github_google_glog//:glog", "@com_github_p4lang_p4runtime//:p4info_cc_proto", "@com_github_p4lang_p4runtime//:p4runtime_cc_proto", "@com_google_absl//absl/status", + "@com_google_absl//absl/status:statusor", "@com_google_absl//absl/strings", "@com_google_absl//absl/strings:str_format", "@com_google_absl//absl/time", @@ -348,3 +348,11 @@ cc_library( "@com_google_googletest//:gtest", ], ) + +cc_library( + name = "packet_at_port", + hdrs = [ + "packet_at_port.h", + ], + deps = ["@com_google_absl//absl/strings"], +) diff --git a/tests/forwarding/l3_admit_test.cc b/tests/forwarding/l3_admit_test.cc index 9b50e4cd..2c0449bc 100644 --- a/tests/forwarding/l3_admit_test.cc +++ b/tests/forwarding/l3_admit_test.cc @@ -19,6 +19,7 @@ #include #include "absl/status/status.h" +#include "absl/status/statusor.h" #include "absl/strings/str_cat.h" #include "absl/strings/str_format.h" #include "absl/strings/string_view.h" @@ -26,9 +27,7 @@ #include "absl/time/clock.h" #include "absl/time/time.h" #include "glog/logging.h" -#include "gmock/gmock.h" #include "gutil/proto.h" -#include "gtest/gtest.h" #include "gutil/status_matchers.h" #include "lib/gnmi/gnmi_helper.h" #include "p4/v1/p4runtime.pb.h" @@ -42,6 +41,9 @@ #include "tests/lib/p4rt_fixed_table_programming_helper.h" #include "tests/lib/packet_in_helper.h" #include "thinkit/mirror_testbed_fixture.h" +#include "thinkit/switch.h" +#include "gmock/gmock.h" +#include "gtest/gtest.h" namespace pins { namespace { @@ -265,41 +267,63 @@ absl::Status SendUdpPacket(pdpi::P4RuntimeSession& session, return absl::OkStatus(); } +absl::StatusOr> +GetNUpInterfaceIDs(thinkit::Switch &device, int num_interfaces) { + // The test fixture pushes a new config during setup so we give the switch a + // few minutes to converge before failing to report no valid ports. + auto stop_time = absl::Now() + absl::Minutes(3); + absl::StatusOr> result; + do { + ASSIGN_OR_RETURN(auto gnmi_stub, device.CreateGnmiStub()); + result = pins_test::GetNUpInterfacePortIds(*gnmi_stub, num_interfaces); + } while (!result.ok() && absl::Now() < stop_time); + return result; +} + } // namespace -TEST_P(L3AdmitTestFixture, - DISABLED_L3PacketsAreRoutedOnlyWhenMacAddressIsInMyStation) { +TEST_P(L3AdmitTestFixture, L3PacketsAreRoutedOnlyWhenMacAddressIsInMyStation) { + // Get SUT and control ports to test on. + ASSERT_OK_AND_ASSIGN( + auto sut_ports, + GetNUpInterfaceIDs(GetParam().testbed_interface->GetMirrorTestbed().Sut(), + 1)); + ASSERT_OK_AND_ASSIGN( + auto control_ports, + GetNUpInterfaceIDs( + GetParam().testbed_interface->GetMirrorTestbed().ControlSwitch(), 1)); + // Punt all traffic arriving at the control switch, and collect them to verify // forwarding. std::unique_ptr packetio_control = - std::make_unique(&GetControlP4RuntimeSession(), + std::make_unique(control_switch_p4rt_session_.get(), PacketInHelper::NoFilter); ASSERT_OK( - PuntAllPacketsToController(GetControlP4RuntimeSession(), sai::GetIrP4Info(sai::Instantiation::kMiddleblock))); - + PuntAllPacketsToController(*control_switch_p4rt_session_, ir_p4info_)); + // Add an L3 route to enable forwarding. L3Route l3_route{ .vrf_id = "vrf-1", .switch_mac = "00:00:00:00:00:01", .switch_ip = std::make_pair("10.0.0.1", 32), - .peer_port = "1", + .peer_port = sut_ports[0], .peer_mac = "00:00:00:00:00:02", .peer_ip = "fe80::2", .router_interface_id = "rif-1", .nexthop_id = "nexthop-1", }; - ASSERT_OK(AddAndSetDefaultVrf(GetSutP4RuntimeSession(), sai::GetIrP4Info(sai::Instantiation::kMiddleblock), - l3_route.vrf_id)); - ASSERT_OK(AddL3Route(GetSutP4RuntimeSession(), sai::GetIrP4Info(sai::Instantiation::kMiddleblock), l3_route)); - + ASSERT_OK( + AddAndSetDefaultVrf(*sut_p4rt_session_, ir_p4info_, l3_route.vrf_id)); + ASSERT_OK(AddL3Route(*sut_p4rt_session_, ir_p4info_, l3_route)); + // Admit only 1 MAC address to the forwaring pipeline. ASSERT_OK(AdmitL3Route( - GetSutP4RuntimeSession(), sai::GetIrP4Info(sai::Instantiation::kMiddleblock), + *sut_p4rt_session_, ir_p4info_, L3AdmitOptions{ .priority = 2070, .dst_mac = std ::make_pair("00:01:02:03:04:05", "FF:FF:FF:FF:FF:FF"), })); - + // Send 2 sets of packets to the switch. The first set of packets should not // match the L3 admit MAC and therefore will be dropped. The second set of // packet should match the L3 admit MAC and therefore get forwarded. @@ -308,21 +332,19 @@ TEST_P(L3AdmitTestFixture, // Send the "bad" packets first to give them the most time. const std::string kBadPayload = "Testing L3 forwarding. This packet should be dropped."; - ASSERT_OK(SendUdpPacket(GetControlP4RuntimeSession(), - sai::GetIrP4Info(sai::Instantiation::kMiddleblock), - /*port_id=*/"1", kNumberOfTestPacket, + ASSERT_OK(SendUdpPacket(*control_switch_p4rt_session_, ir_p4info_, + control_ports[0], kNumberOfTestPacket, /*dst_mac=*/"00:aa:bb:cc:cc:dd", /*dst_ip=*/"10.0.0.1", kBadPayload)); - + // Then send the "good" packets. const std::string kGoodPayload = "Testing L3 forwarding. This packet should arrive to packet in."; - ASSERT_OK(SendUdpPacket(GetControlP4RuntimeSession(), - sai::GetIrP4Info(sai::Instantiation::kMiddleblock), - /*port_id=*/"1", kNumberOfTestPacket, + ASSERT_OK(SendUdpPacket(*control_switch_p4rt_session_, ir_p4info_, + control_ports[0], kNumberOfTestPacket, /*dst_mac=*/"00:01:02:03:04:05", /*dst_ip=*/"10.0.0.1", kGoodPayload)); - + absl::Time timeout = absl::Now() + absl::Minutes(1); int good_packet_count = 0; int bad_packet_count = 0; @@ -357,37 +379,42 @@ TEST_P(L3AdmitTestFixture, } TEST_P(L3AdmitTestFixture, L3AdmitCanUseMaskToAllowMultipleMacAddresses) { + // Get SUT and control ports to test on. + ASSERT_OK_AND_ASSIGN( + auto sut_ports, + GetNUpInterfaceIDs(GetParam().testbed_interface->GetMirrorTestbed().Sut(), + 1)); + ASSERT_OK_AND_ASSIGN( + auto control_ports, + GetNUpInterfaceIDs( + GetParam().testbed_interface->GetMirrorTestbed().ControlSwitch(), 1)); + // Punt all traffic arriving at the control switch, and collect them to verify // forwarding. std::unique_ptr packetio_control = - std::make_unique(&GetControlP4RuntimeSession(), + std::make_unique(control_switch_p4rt_session_.get(), PacketInHelper::NoFilter); ASSERT_OK( - PuntAllPacketsToController(GetControlP4RuntimeSession(), - sai::GetIrP4Info(sai::Instantiation::kMiddleblock))); + PuntAllPacketsToController(*control_switch_p4rt_session_, ir_p4info_)); // Add an L3 route to enable forwarding. L3Route l3_route{ .vrf_id = "vrf-1", .switch_mac = "00:00:00:00:00:01", .switch_ip = std::make_pair("10.0.0.1", 32), - .peer_port = "1", + .peer_port = sut_ports[0], .peer_mac = "00:00:00:00:00:02", .peer_ip = "fe80::2", .router_interface_id = "rif-1", .nexthop_id = "nexthop-1", }; - - ASSERT_OK(AddAndSetDefaultVrf(GetSutP4RuntimeSession(), - sai::GetIrP4Info(sai::Instantiation::kMiddleblock), - l3_route.vrf_id)); - ASSERT_OK(AddL3Route(GetSutP4RuntimeSession(), - sai::GetIrP4Info(sai::Instantiation::kMiddleblock), - l3_route)); + ASSERT_OK( + AddAndSetDefaultVrf(*sut_p4rt_session_, ir_p4info_, l3_route.vrf_id)); + ASSERT_OK(AddL3Route(*sut_p4rt_session_, ir_p4info_, l3_route)); // Admit multiple MAC addresses into L3 routing with a single L3 admit rule. ASSERT_OK(AdmitL3Route( - GetSutP4RuntimeSession(), sai::GetIrP4Info(sai::Instantiation::kMiddleblock), + *sut_p4rt_session_, ir_p4info_, L3AdmitOptions{ .priority = 2070, .dst_mac = std ::make_pair("00:01:02:03:00:05", "FF:FF:FF:FF:F0:FF"), @@ -400,9 +427,8 @@ TEST_P(L3AdmitTestFixture, L3AdmitCanUseMaskToAllowMultipleMacAddresses) { "Testing L3 forwarding. This packet should arrive to packet in."; for (int i = 0; i < 5; ++i) { std::string dst_mac = absl::StrFormat("00:01:02:03:%02d:05", i); - ASSERT_OK(SendUdpPacket(GetControlP4RuntimeSession(), - sai::GetIrP4Info(sai::Instantiation::kMiddleblock), - /*port_id=*/"1", kNumberOfTestPacket, dst_mac, + ASSERT_OK(SendUdpPacket(*control_switch_p4rt_session_, ir_p4info_, + control_ports[0], kNumberOfTestPacket, dst_mac, /*dst_ip=*/"10.0.0.1", kGoodPayload)); } @@ -435,42 +461,47 @@ TEST_P(L3AdmitTestFixture, L3AdmitCanUseMaskToAllowMultipleMacAddresses) { EXPECT_EQ(good_packet_count, 5 * kNumberOfTestPacket); } -TEST_P(L3AdmitTestFixture, DISABLED_L3AdmitCanUseInPortToRestrictMacAddresses) { +TEST_P(L3AdmitTestFixture, L3AdmitCanUseInPortToRestrictMacAddresses) { + // Get SUT and control ports to test on. + ASSERT_OK_AND_ASSIGN( + auto sut_ports, + GetNUpInterfaceIDs(GetParam().testbed_interface->GetMirrorTestbed().Sut(), + 1)); + ASSERT_OK_AND_ASSIGN( + auto control_ports, + GetNUpInterfaceIDs( + GetParam().testbed_interface->GetMirrorTestbed().ControlSwitch(), 2)); + // Punt all traffic arriving at the control switch, and collect them to verify // forwarding. std::unique_ptr packetio_control = - std::make_unique(&GetControlP4RuntimeSession(), + std::make_unique(control_switch_p4rt_session_.get(), PacketInHelper::NoFilter); ASSERT_OK( - PuntAllPacketsToController(GetControlP4RuntimeSession(), - sai::GetIrP4Info(sai::Instantiation::kMiddleblock))); + PuntAllPacketsToController(*control_switch_p4rt_session_, ir_p4info_)); // Add an L3 route to enable forwarding. L3Route l3_route{ .vrf_id = "vrf-1", .switch_mac = "00:00:00:00:00:01", .switch_ip = std::make_pair("10.0.0.1", 32), - .peer_port = "1", + .peer_port = sut_ports[0], .peer_mac = "00:00:00:00:00:02", .peer_ip = "fe80::2", .router_interface_id = "rif-1", .nexthop_id = "nexthop-1", }; - ASSERT_OK(AddAndSetDefaultVrf(GetSutP4RuntimeSession(), - sai::GetIrP4Info(sai::Instantiation::kMiddleblock), - l3_route.vrf_id)); - ASSERT_OK(AddL3Route(GetSutP4RuntimeSession(), - sai::GetIrP4Info(sai::Instantiation::kMiddleblock), - l3_route)); + ASSERT_OK( + AddAndSetDefaultVrf(*sut_p4rt_session_, ir_p4info_, l3_route.vrf_id)); + ASSERT_OK(AddL3Route(*sut_p4rt_session_, ir_p4info_, l3_route)); // Admit the MAC addresses only on port XYZ ASSERT_OK(AdmitL3Route( - GetSutP4RuntimeSession(), - sai::GetIrP4Info(sai::Instantiation::kMiddleblock), + *sut_p4rt_session_, ir_p4info_, L3AdmitOptions{ .priority = 2070, .dst_mac = std ::make_pair("00:01:02:03:00:05", "FF:FF:FF:FF:F0:FF"), - .in_port = "2", + .in_port = control_ports[0], })); // Send 2 sets of packets to the switch. The first set of packets should not @@ -481,18 +512,16 @@ TEST_P(L3AdmitTestFixture, DISABLED_L3AdmitCanUseInPortToRestrictMacAddresses) { // Send the "bad" packets first to give them the most time. const std::string kBadPayload = "Testing L3 forwarding. This packet should be dropped."; - ASSERT_OK(SendUdpPacket(GetControlP4RuntimeSession(), - sai::GetIrP4Info(sai::Instantiation::kMiddleblock), - /*port_id=*/"1", kNumberOfTestPacket, + ASSERT_OK(SendUdpPacket(*control_switch_p4rt_session_, ir_p4info_, + control_ports[1], kNumberOfTestPacket, /*dst_mac=*/"00:01:02:03:04:05", /*dst_ip=*/"10.0.0.1", kBadPayload)); - + // Then send the "good" packets. const std::string kGoodPayload = "Testing L3 forwarding. This packet should arrive to packet in."; - ASSERT_OK(SendUdpPacket(GetControlP4RuntimeSession(), - sai::GetIrP4Info(sai::Instantiation::kMiddleblock), - /*port_id=*/"2", kNumberOfTestPacket, + ASSERT_OK(SendUdpPacket(*control_switch_p4rt_session_, ir_p4info_, + control_ports[0], kNumberOfTestPacket, /*dst_mac=*/"00:01:02:03:04:05", /*dst_ip=*/"10.0.0.1", kGoodPayload)); @@ -531,67 +560,69 @@ TEST_P(L3AdmitTestFixture, DISABLED_L3AdmitCanUseInPortToRestrictMacAddresses) { } TEST_P(L3AdmitTestFixture, L3PacketsCanBeRoutedWithOnlyARouterInterface) { + // Only use 1 port because for the router interface L3 admit behavior to work + // the incomming packet needs to match the outgoing port. + ASSERT_OK_AND_ASSIGN( + auto sut_ports, + GetNUpInterfaceIDs(GetParam().testbed_interface->GetMirrorTestbed().Sut(), + 1)); + // Punt all traffic arriving at the control switch, and collect them to verify // forwarding. std::unique_ptr packetio_control = - std::make_unique(&GetControlP4RuntimeSession(), + std::make_unique(control_switch_p4rt_session_.get(), PacketInHelper::NoFilter); -ASSERT_OK( - PuntAllPacketsToController(GetControlP4RuntimeSession(), - sai::GetIrP4Info(sai::Instantiation::kMiddleblock))); + ASSERT_OK( + PuntAllPacketsToController(*control_switch_p4rt_session_, ir_p4info_)); -// Add an L3 route to enable forwarding, but do not add an explicit L3Admit + // Add an L3 route to enable forwarding, but do not add an explicit L3Admit // rule. -L3Route l3_route{ - .vrf_id = "vrf-1", - .switch_mac = "00:00:00:00:00:01", - .switch_ip = std::make_pair("10.0.0.1", 32), - .peer_port = "1", - .peer_mac = "00:00:00:00:00:02", - .peer_ip = "fe80::2", - .router_interface_id = "rif-1", - .nexthop_id = "nexthop-1", -}; -ASSERT_OK(AddAndSetDefaultVrf( - GetSutP4RuntimeSession(), - sai::GetIrP4Info(sai::Instantiation::kMiddleblock), l3_route.vrf_id)); -ASSERT_OK(AddL3Route(GetSutP4RuntimeSession(), - sai::GetIrP4Info(sai::Instantiation::kMiddleblock), - l3_route)); - -// Send 1 set of packets to the switch using the switch's MAC address from the -// L3 route. -const int kNumberOfTestPacket = 100; -const std::string kGoodPayload = - "Testing L3 forwarding. This packet should arrive to packet in."; -ASSERT_OK(SendUdpPacket(GetControlP4RuntimeSession(), - sai::GetIrP4Info(sai::Instantiation::kMiddleblock), - /*port_id=*/"1", kNumberOfTestPacket, - /*dst_mac=*/"00:00:00:00:00:01", - /*dst_ip=*/"10.0.0.1", kGoodPayload)); - -absl::Time timeout = absl::Now() + absl::Minutes(1); -int good_packet_count = 0; -while (good_packet_count < kNumberOfTestPacket) { - if (packetio_control->HasPacketInMessage()) { - ASSERT_OK_AND_ASSIGN(p4::v1::StreamMessageResponse response, - packetio_control->GetNextPacketInMessage()); - - // Verify this is the packet we expect. - packetlib::Packet packet_in = - packetlib::ParsePacket(response.packet().payload()); - if (response.update_case() == p4::v1::StreamMessageResponse::kPacket && - absl::StrContains(packet_in.payload(), kGoodPayload)) { - ++good_packet_count; - } else { - LOG(WARNING) << "Unexpected response: " << response.DebugString(); + L3Route l3_route{ + .vrf_id = "vrf-1", + .switch_mac = "00:00:00:00:00:01", + .switch_ip = std::make_pair("10.0.0.1", 32), + .peer_port = sut_ports[0], + .peer_mac = "00:00:00:00:00:02", + .peer_ip = "fe80::2", + .router_interface_id = "rif-1", + .nexthop_id = "nexthop-1", + }; + ASSERT_OK( + AddAndSetDefaultVrf(*sut_p4rt_session_, ir_p4info_, l3_route.vrf_id)); + ASSERT_OK(AddL3Route(*sut_p4rt_session_, ir_p4info_, l3_route)); + + // Send 1 set of packets to the switch using the switch's MAC address from the + // L3 route. + const int kNumberOfTestPacket = 100; + const std::string kGoodPayload = + "Testing L3 forwarding. This packet should arrive to packet in."; + ASSERT_OK(SendUdpPacket(*control_switch_p4rt_session_, ir_p4info_, + sut_ports[0], kNumberOfTestPacket, + /*dst_mac=*/"00:00:00:00:00:01", + /*dst_ip=*/"10.0.0.1", kGoodPayload)); + + absl::Time timeout = absl::Now() + absl::Minutes(1); + int good_packet_count = 0; + while (good_packet_count < kNumberOfTestPacket) { + if (packetio_control->HasPacketInMessage()) { + ASSERT_OK_AND_ASSIGN(p4::v1::StreamMessageResponse response, + packetio_control->GetNextPacketInMessage()); + + // Verify this is the packet we expect. + packetlib::Packet packet_in = + packetlib::ParsePacket(response.packet().payload()); + if (response.update_case() == p4::v1::StreamMessageResponse::kPacket && + absl::StrContains(packet_in.payload(), kGoodPayload)) { + ++good_packet_count; + } else { + LOG(WARNING) << "Unexpected response: " << response.DebugString(); + } } - } - if (absl::Now() > timeout) { - LOG(ERROR) << "Reached timeout waiting on packets to arrive."; - break; - } + if (absl::Now() > timeout) { + LOG(ERROR) << "Reached timeout waiting on packets to arrive."; + break; + } } LOG(INFO) << "Done collecting packets."; @@ -601,20 +632,28 @@ while (good_packet_count < kNumberOfTestPacket) { TEST_P(L3AdmitTestFixture, L3PacketsCanBeClassifiedByDestinationMac) { // Only run this test if the ACL_PRE_INGRESS table supports matching on // DST_MAC. - if (!TableHasMatchField(sai::GetIrP4Info(sai::Instantiation::kMiddleblock), - "acl_pre_ingress_table", "dst_mac")) { + if (!TableHasMatchField(ir_p4info_, "acl_pre_ingress_table", "dst_mac")) { GTEST_SKIP() << "Skipping because ACL_PRE_INGRESS table can not match on DST_MAC."; } + // Get SUT and control ports to test on. + ASSERT_OK_AND_ASSIGN( + auto sut_ports, + GetNUpInterfaceIDs(GetParam().testbed_interface->GetMirrorTestbed().Sut(), + 1)); + ASSERT_OK_AND_ASSIGN( + auto control_ports, + GetNUpInterfaceIDs( + GetParam().testbed_interface->GetMirrorTestbed().ControlSwitch(), 1)); + // Punt all traffic arriving at the control switch, and collect them to verify // forwarding. std::unique_ptr packetio_control = - std::make_unique(&GetControlP4RuntimeSession(), + std::make_unique(control_switch_p4rt_session_.get(), PacketInHelper::NoFilter); ASSERT_OK( - PuntAllPacketsToController(GetControlP4RuntimeSession(), - sai::GetIrP4Info(sai::Instantiation::kMiddleblock))); + PuntAllPacketsToController(*control_switch_p4rt_session_, ir_p4info_)); // This test uses 2 MAC addresses. Both will be admitted to L3 routing, but // only one will get assigned a VRF ID. We expect packets receiving the @@ -623,19 +662,16 @@ TEST_P(L3AdmitTestFixture, L3PacketsCanBeClassifiedByDestinationMac) { std::string vrf_id = "vrf-1"; std::string good_dst_mac = "00:00:00:00:00:03"; std::string drop_dst_mac = "00:00:00:00:00:04"; - ASSERT_OK(AllowVrfTrafficToDstMac(GetSutP4RuntimeSession(), - sai::GetIrP4Info(sai::Instantiation::kMiddleblock), + ASSERT_OK(AllowVrfTrafficToDstMac(*sut_p4rt_session_, ir_p4info_, good_dst_mac, vrf_id)); ASSERT_OK(AdmitL3Route( - GetSutP4RuntimeSession(), - sai::GetIrP4Info(sai::Instantiation::kMiddleblock), + *sut_p4rt_session_, ir_p4info_, L3AdmitOptions{ .priority = 2070, .dst_mac = std ::make_pair(good_dst_mac, "FF:FF:FF:FF:FF:FF"), })); ASSERT_OK(AdmitL3Route( - GetSutP4RuntimeSession(), - sai::GetIrP4Info(sai::Instantiation::kMiddleblock), + *sut_p4rt_session_, ir_p4info_, L3AdmitOptions{ .priority = 2070, .dst_mac = std ::make_pair(drop_dst_mac, "FF:FF:FF:FF:FF:FF"), @@ -646,14 +682,13 @@ TEST_P(L3AdmitTestFixture, L3PacketsCanBeClassifiedByDestinationMac) { .vrf_id = vrf_id, .switch_mac = "00:00:00:00:00:01", .switch_ip = std::make_pair("10.0.0.1", 32), - .peer_port = "1", + .peer_port = sut_ports[0], .peer_mac = "00:00:00:00:00:02", .peer_ip = "fe80::2", .router_interface_id = "rif-1", .nexthop_id = "nexthop-1", }; - ASSERT_OK(AddL3Route(GetSutP4RuntimeSession(), - sai::GetIrP4Info(sai::Instantiation::kMiddleblock), l3_route)); + ASSERT_OK(AddL3Route(*sut_p4rt_session_, ir_p4info_, l3_route)); // Send 2 set of packets to the switch. One using the expected destination // MAC (gets forwarded), and another using an unexpected destination MAC @@ -665,13 +700,11 @@ TEST_P(L3AdmitTestFixture, L3PacketsCanBeClassifiedByDestinationMac) { "Testing L3 forwarding. This packet should be dropped."; // Send the "bad" packets first to give them the most time. - ASSERT_OK(SendUdpPacket(GetControlP4RuntimeSession(), - sai::GetIrP4Info(sai::Instantiation::kMiddleblock), - /*port_id=*/"1", kNumberOfTestPacket, drop_dst_mac, + ASSERT_OK(SendUdpPacket(*control_switch_p4rt_session_, ir_p4info_, + control_ports[0], kNumberOfTestPacket, drop_dst_mac, /*dst_ip=*/"10.0.0.1", kBadPayload)); - ASSERT_OK(SendUdpPacket(GetControlP4RuntimeSession(), - sai::GetIrP4Info(sai::Instantiation::kMiddleblock), - /*port_id=*/"1", kNumberOfTestPacket, good_dst_mac, + ASSERT_OK(SendUdpPacket(*control_switch_p4rt_session_, ir_p4info_, + control_ports[0], kNumberOfTestPacket, good_dst_mac, /*dst_ip=*/"10.0.0.1", kGoodPayload)); // Wait for all the good packets to get punted back on the control switch. diff --git a/tests/forwarding/l3_admit_test.h b/tests/forwarding/l3_admit_test.h index 631c7bb7..b5a8142e 100644 --- a/tests/forwarding/l3_admit_test.h +++ b/tests/forwarding/l3_admit_test.h @@ -15,28 +15,52 @@ #define PINS_TESTS_FORWARDING_L3_ADMIT_TEST_H_ #include +#include +#include +#include "gutil/status_matchers.h" #include "p4/config/v1/p4info.pb.h" +#include "p4_pdpi/ir.h" #include "p4_pdpi/ir.pb.h" -#include "p4_pdpi/p4_runtime_session.h" -#include "sai_p4/instantiations/google/instantiations.h" -#include "sai_p4/instantiations/google/sai_p4info.h" -#include "tests/forwarding/mirror_blackbox_test_fixture.h" -#include "tests/lib/packet_in_helper.h" +#include "tests/lib/switch_test_setup_helpers.h" #include "thinkit/mirror_testbed_fixture.h" +#include "gmock/gmock.h" namespace pins { -class L3AdmitTestFixture : public pins_test::MirrorBlackboxTestFixture { - protected: - void TearDown() override { - // MirrorBlackboxTestFixture unnecessarily clears tables at TearDown. This - // is not harmful for other tests but is problematic for l3_admit_tests - // since the P4RT session to the controller is closed during the tests (see - // lib/packet_in_helper.h). Therefore, We bypass table clearance in - // TearDown. - MirrorTestbedFixture::TearDown(); +struct L3AdmitTestParams { + thinkit::MirrorTestbedInterface *testbed_interface; + p4::config::v1::P4Info p4info; +}; + +// This test assumes that the switch is set up with a gNMI config. +class L3AdmitTestFixture : public testing::TestWithParam { +protected: + void SetUp() override { + GetParam().testbed_interface->SetUp(); + + // Initialize the connection and clear table entries for the SUT and Control + // switch. + ASSERT_OK_AND_ASSIGN( + std::tie(sut_p4rt_session_, control_switch_p4rt_session_), + pins_test::ConfigureSwitchPairAndReturnP4RuntimeSessionPair( + GetParam().testbed_interface->GetMirrorTestbed().Sut(), + GetParam().testbed_interface->GetMirrorTestbed().ControlSwitch(), + /*gnmi_config=*/std::nullopt, GetParam().p4info)); + + ASSERT_OK_AND_ASSIGN(ir_p4info_, pdpi::CreateIrP4Info(GetParam().p4info)); } + + void TearDown() override { GetParam().testbed_interface->TearDown(); } + + ~L3AdmitTestFixture() override { delete GetParam().testbed_interface; } + + // This test runs on a mirror testbed setup so we open a P4RT connection to + // both switches. + std::unique_ptr sut_p4rt_session_; + std::unique_ptr control_switch_p4rt_session_; + + pdpi::IrP4Info ir_p4info_; }; } // namespace pins diff --git a/tests/forwarding/packet_at_port.h b/tests/forwarding/packet_at_port.h new file mode 100644 index 00000000..42b56bf6 --- /dev/null +++ b/tests/forwarding/packet_at_port.h @@ -0,0 +1,47 @@ +#ifndef PINS_TESTS_FORWARDING_PACKET_H_ +#define PINS_TESTS_FORWARDING_PACKET_H_ + +#include +#include +#include +#include +#include + +#include "absl/strings/escaping.h" +#include "absl/strings/string_view.h" + +namespace pins { + +// A packet, represented by the packet's data and the port that it was +// sent/received on. +struct PacketAtPort { + // TODO Change it to string type port. + int port; + std::string data; + + bool operator==(const PacketAtPort& other) const { + return port == other.port && data == other.data; + } + + bool operator<(const PacketAtPort& other) const { + return std::tie(port, data) < std::tie(other.port, other.data); + } + + template + friend H AbslHashValue(H h, const PacketAtPort& m); +}; + +inline std::ostream& operator<<(std::ostream& os, + const ::pins::PacketAtPort& packet) { + return os << "{port: " << packet.port << " length: " << packet.data.size() + << " data: \"" << absl::BytesToHexString(packet.data) << "\"}"; +} + +template +H AbslHashValue(H h, const PacketAtPort& m) { + return H::combine(std::move(h), m.port, m.data); +} + +} // namespace pins + +#endif // PINS_TESTS_FORWARDING_PACKET_H_ diff --git a/tests/qos/BUILD.bazel b/tests/qos/BUILD.bazel index ca585b1b..27affe5f 100644 --- a/tests/qos/BUILD.bazel +++ b/tests/qos/BUILD.bazel @@ -104,6 +104,7 @@ cc_library( "//lib:ixia_helper_cc_proto", "//lib/gnmi:gnmi_helper", "//lib/utils:json_utils", + "//lib/validator:validator_lib", "//p4_pdpi:ir", "//p4_pdpi:ir_cc_proto", "//p4_pdpi:p4_runtime_session", @@ -119,6 +120,7 @@ cc_library( "//tests/lib:switch_test_setup_helpers", "//thinkit:generic_testbed", "//thinkit:generic_testbed_fixture", + "//thinkit:mirror_testbed", "@com_github_gnmi//proto/gnmi:gnmi_cc_grpc_proto", "@com_github_p4lang_p4runtime//:p4info_cc_proto", "@com_github_p4lang_p4runtime//:p4runtime_cc_proto", diff --git a/tests/qos/cpu_qos_test.cc b/tests/qos/cpu_qos_test.cc index f9f8256d..d1602726 100644 --- a/tests/qos/cpu_qos_test.cc +++ b/tests/qos/cpu_qos_test.cc @@ -715,6 +715,21 @@ TEST_P(CpuQosTestWithoutIxia, << "for injected test packet: " << packet.DebugString(); initial_cpu_queue_state = cpu_queue_state; } + + // Ensure tha the switch did not punt packets to the controller via P4RT. + ASSERT_OK_AND_ASSIGN(std::vector pi_responses, + sut_p4rt_session->ReadStreamChannelResponsesAndFinish()); + for (const auto &pi_response : pi_responses) { + sai::PacketIn pd_packet; + EXPECT_OK(pdpi::PiPacketInToPd(ir_p4info, pi_response.packet(), &pd_packet)) + << "where packet = " << pi_response.packet().DebugString(); + ADD_FAILURE() << "SUT punted the following packet to the controller " + "via P4Runtime: " + << (pd_packet.ByteSizeLong() == 0 // Translation failed. + ? pi_response.packet().DebugString() + : pd_packet.DebugString()); + } + LOG(INFO) << "-- END OF TEST -----------------------------------------------"; } @@ -840,38 +855,15 @@ TEST_P(CpuQosTestWithoutIxia, PuntToCpuWithVlanTag) { pdpi::PartialPdTableEntryToPiTableEntry(ir_p4info, pd_acl_entry)); ASSERT_OK(pdpi::InstallPiTableEntry(sut_p4rt_session.get(), pi_acl_entry)); - struct VlanTestPacketCounters { - absl::Mutex mutex; - int num_vlan_packets_received ABSL_GUARDED_BY(mutex) = 0; - }; - - VlanTestPacketCounters packet_receive_info; - PacketInReceiver sut_packet_receiver( - *sut_p4rt_session, [&](const p4::v1::StreamMessageResponse pi_response) { - sai::StreamMessageResponse pd_response; - ASSERT_OK(pdpi::PiStreamMessageResponseToPd(ir_p4info, pi_response, - &pd_response)) - << " packet in PI to PD failed: " << pi_response.DebugString(); - ASSERT_TRUE(pd_response.has_packet()) - << " received unexpected stream message for packet in: " - << pd_response.DebugString(); - packetlib::Packet packet = - packetlib::ParsePacket(pd_response.packet().payload()); - EXPECT_EQ(packet.headers(1).vlan_header().vlan_identifier(), - ipv4_packet.headers(1).vlan_header().vlan_identifier()); - absl::MutexLock lock(&packet_receive_info.mutex); - packet_receive_info.num_vlan_packets_received++; - }); - - for (auto test_packet : test_packets) { - { - absl::MutexLock lock(&packet_receive_info.mutex); - packet_receive_info.num_vlan_packets_received = 0; - } + for (packetlib::Packet &test_packet : test_packets) { + // Start from fresh P4RT session. + ASSERT_OK_AND_ASSIGN(sut_p4rt_session, pdpi::P4RuntimeSession::Create(sut)); + // Send packets. + ASSERT_OK(packetlib::PadPacketToMinimumSize(test_packet)); + ASSERT_OK(packetlib::UpdateAllComputedFields(test_packet)); ASSERT_OK_AND_ASSIGN(const std::string raw_packet, packetlib::SerializePacket(test_packet)); - const int kPacketCount = 10; for (int iter = 0; iter < kPacketCount; iter++) { ASSERT_OK(pins::InjectEgressPacket( @@ -880,15 +872,26 @@ TEST_P(CpuQosTestWithoutIxia, PuntToCpuWithVlanTag) { /*packet_delay=*/std::nullopt)); } - ASSERT_OK(pins::TryUpToNTimes(10, /*delay=*/absl::Seconds(1), [&] { - absl::MutexLock lock(&packet_receive_info.mutex); - if (packet_receive_info.num_vlan_packets_received == kPacketCount) { - return absl::OkStatus(); - } - return absl::InternalError(absl::StrCat( - "Received packets: ", packet_receive_info.num_vlan_packets_received, - "Expected packets", kPacketCount)); - })); + // Verify we receive expected packets back. + absl::SleepFor(absl::Seconds(1)); + int num_vlan_packets_received = 0; + ASSERT_OK_AND_ASSIGN( + std::vector pi_responses, + sut_p4rt_session->ReadStreamChannelResponsesAndFinish()); + for (const auto &pi_response : pi_responses) { + sai::StreamMessageResponse pd_response; + ASSERT_OK(pdpi::PiStreamMessageResponseToPd(ir_p4info, pi_response, + &pd_response)) + << " packet in PI to PD failed: " << pi_response.DebugString(); + ASSERT_TRUE(pd_response.has_packet()) + << " received unexpected stream message for packet in: " + << pd_response.DebugString(); + packetlib::Packet punted_packet = + packetlib::ParsePacket(pd_response.packet().payload()); + EXPECT_THAT(punted_packet, EqualsProto(test_packet)); + num_vlan_packets_received += 1; + } + EXPECT_EQ(num_vlan_packets_received, kPacketCount); } LOG(INFO) << "-- END OF TEST -----------------------------------------------"; } diff --git a/tests/qos/frontpanel_qos_test.cc b/tests/qos/frontpanel_qos_test.cc index b690c4d0..df322306 100644 --- a/tests/qos/frontpanel_qos_test.cc +++ b/tests/qos/frontpanel_qos_test.cc @@ -19,6 +19,7 @@ #include #include #include +#include #include #include #include @@ -26,6 +27,7 @@ #include #include // NOLINT #include +#include #include #include "absl/algorithm/container.h" @@ -53,6 +55,7 @@ #include "lib/ixia_helper.h" #include "lib/ixia_helper.pb.h" #include "lib/utils/json_utils.h" +#include "lib/validator/validator_lib.h" #include "p4/v1/p4runtime.pb.h" #include "p4_pdpi/internal/ordered_map.h" #include "p4_pdpi/ir.h" @@ -71,6 +74,7 @@ #include "tests/qos/packet_in_receiver.h" #include "tests/qos/qos_test_util.h" #include "thinkit/generic_testbed.h" +#include "thinkit/mirror_testbed.h" #include "gmock/gmock.h" #include "gtest/gtest.h" @@ -93,22 +97,48 @@ template std::string ToString(const T &t) { return ss.str(); } -// Connects to Ixia on the given testbed and returns a string handle identifying -// the connection (aka "topology ref"). -absl::StatusOr ConnectToIxia(thinkit::GenericTestbed &testbed) { - ASSIGN_OR_RETURN(auto gnmi_stub, testbed.Sut().CreateGnmiStub()); - ASSIGN_OR_RETURN(std::vector ready_links, - GetReadyIxiaLinks(testbed, *gnmi_stub)); - if (ready_links.empty()) { - return gutil::UnavailableErrorBuilder() << "no Ixia-to-SUT link up"; +// Type of an object that performs some cleanup logic in its destructor. +using Cleanup = decltype(absl::Cleanup{std::function()}); + +// Modifies the buffer config of the given `egress_port` such that all ports +// fairly share all space dynamically, and return a `Cleanup` object that +// restores the previous config when the object is destructed. +absl::StatusOr ConfigureFairBufferConfigForPortUntilEndOfScope( + absl::string_view egress_port, gnmi::gNMI::StubInterface &gnmi) { + ASSIGN_OR_RETURN(const std::string kBufferProfileName, + GetBufferAllocationProfileByEgressPort(egress_port, gnmi)); + + // Before we update the buffer config, save the current config and + // prepare to restore it. + ASSIGN_OR_RETURN(const std::string kInitialBufferConfig, + GetBufferAllocationProfileConfig(kBufferProfileName, gnmi)); + Cleanup cleanup = absl::Cleanup(std::function([=, &gnmi] { + EXPECT_OK(UpdateBufferAllocationProfileConfig(kBufferProfileName, + kInitialBufferConfig, gnmi)) + << "failed to restore initial buffer config -- switch config may be " + "corrupted, causing subsequent tests to fail"; + })); + + // Set fair buffer config. + absl::flat_hash_map + buffer_config_by_queue_name; + // TODO: Read queue names from switch instead of + // hard-coding them here. + for (absl::string_view queue_name : + {"LLQ1", "LLQ2", "BE1", "AF1", "AF2", "AF3", "AF4", "NC1"}) { + buffer_config_by_queue_name[queue_name] = BufferParameters{ + .dedicated_buffer = 0, + .use_shared_buffer = true, + .shared_buffer_limit_type = + "openconfig-qos:DYNAMIC_BASED_ON_SCALING_FACTOR", + .dynamic_limit_scaling_factor = -3, + .shared_static_limit = 0, + }; } - absl::string_view ixia_interface = ready_links[0].ixia_interface; - ASSIGN_OR_RETURN(ixia::IxiaPortInfo ixia_port_info, - ixia::ExtractPortInfo(ixia_interface)); - ASSIGN_OR_RETURN( - std::string ixia_connection_handle, - pins_test::ixia::IxiaConnect(ixia_port_info.hostname, testbed)); - return ixia_connection_handle; + RETURN_IF_ERROR(SetBufferConfigParameters(kBufferProfileName, + buffer_config_by_queue_name, gnmi)); + + return std::move(cleanup); } // Installs the given table `entries` using the given P4Runtime session, @@ -395,6 +425,20 @@ GetIxiaLinks(thinkit::GenericTestbed &testbed, return links; } +// Returns a function that logs port debug information for both switches in a +// mirror testbed if the ports are down. +std::function +MakeLogPortDebugInfoFunction(thinkit::GenericTestbed &testbed, + absl::Span enabled_interfaces) { + return [&testbed, enabled_interfaces] { + if (absl::Status sut_port_status = + pins_test::PortsUp(testbed.Sut(), enabled_interfaces); + !sut_port_status.ok()) { + LOG(WARNING) << sut_port_status; + } + }; +} + // The purpose of this test is to verify that: // - Incoming IP packets are mapped to queues according to their DSCP. // - Queue peak information rates (PIRs) are enforced. @@ -544,12 +588,40 @@ TEST_P(FrontpanelQosTest, FormatJsonBestEffort(updated_scheduler_config))); // Connect to Ixia and fix global parameters. - ASSERT_OK_AND_ASSIGN(const std::string kIxiaHandle, ConnectToIxia(*testbed)); + ASSERT_OK_AND_ASSIGN(const std::string kIxiaHandle, + ixia::ConnectToIxia(*testbed)); ASSERT_OK_AND_ASSIGN(const std::string kIxiaSrcPortHandle, ixia::IxiaVport(kIxiaHandle, kIxiaSrcPort, *testbed)); ASSERT_OK_AND_ASSIGN(const std::string kIxiaDstPortHandle, ixia::IxiaVport(kIxiaHandle, kIxiaDstPort, *testbed)); + constexpr int64_t kSpeed400g = 400000000000; + constexpr int64_t kSpeed200g = 200000000000; + // Toggle speed to add coverage for b/246290651. + if (kSutEgressPortSpeedInBitsPerSecond != kSpeed200g) { + ASSERT_OK(SetPortSpeedInBitsPerSecond(PortSpeed(kSpeed200g), kSutEgressPort, + *gnmi_stub)); + ASSERT_OK(SetPortSpeedInBitsPerSecond( + PortSpeed(kSutEgressPortSpeedInBitsPerSecond), kSutEgressPort, + *gnmi_stub)); + } else { + ASSERT_OK(SetPortSpeedInBitsPerSecond(PortSpeed(kSpeed400g), kSutEgressPort, + *gnmi_stub)); + ASSERT_OK(SetPortSpeedInBitsPerSecond( + PortSpeed(kSutEgressPortSpeedInBitsPerSecond), kSutEgressPort, + *gnmi_stub)); + } + + std::vector required_interfaces = {kSutEgressPort}; + // Wait for all enabled interfaces to be up before sending packets. + ASSERT_OK(pins_test::OnFailure( + pins_test::WaitForCondition(pins_test::PortsUp, absl::Minutes(10), + testbed->Sut(), required_interfaces, + /*with_healthz=*/false), + MakeLogPortDebugInfoFunction(*testbed, required_interfaces))) + << "all required ports must be initialized on the SUT before sending " + "test packets"; + // Actual testing -- inject test IPv4 and IPv6 packets for each DSCP, and // check the behavior is as eexpted. constexpr int kMaxDscp = 63; @@ -674,15 +746,18 @@ TEST_P(FrontpanelQosTest, EXPECT_THAT(delta_counters, ResultOf(TotalPacketsForQueue, Ge(kIxiaTrafficStats.num_tx_frames()))); - // Protocol packets such as LLDP can be transmitted via queue during test. - // Add some tolerance to account for these packets. + // Protocol packets such as LLDP/NDv6 can be transmitted via queue during + // test. Add some tolerance to account for these packets. constexpr int kMaxToleratedExtraPackets = 5; EXPECT_THAT( delta_counters, ResultOf(TotalPacketsForQueue, Le(kIxiaTrafficStats.num_tx_frames() + kMaxToleratedExtraPackets))); EXPECT_THAT(delta_counters, Field(&QueueCounters::num_packets_transmitted, - Eq(kIxiaTrafficStats.num_rx_frames()))); + Ge(kIxiaTrafficStats.num_rx_frames()))); + EXPECT_THAT(delta_counters, Field(&QueueCounters::num_packets_transmitted, + Le(kIxiaTrafficStats.num_rx_frames() + + kMaxToleratedExtraPackets))); } } LOG(INFO) << "-- Test done -------------------------------------------------"; @@ -830,83 +905,11 @@ TEST_P(FrontpanelQosTest, WeightedRoundRobinWeightsAreRespected) { "corrupted, causing subsequent test to fail"; }); - // Save Buffer config and restore at end of the test. - ASSERT_OK_AND_ASSIGN( - const std::string kSutEgressPortBufferProfile, - GetBufferAllocationProfileByEgressPort(kSutEgressPort, *gnmi_stub)); - // Before we update the buffer config, save the current config and - // prepare to restore it at the end of the test. - ASSERT_OK_AND_ASSIGN(const std::string kInitialBufferConfig, - GetBufferAllocationProfileConfig( - kSutEgressPortBufferProfile, *gnmi_stub)); - const auto kRestoreBufferConfig = absl::Cleanup([&] { - EXPECT_OK(UpdateBufferAllocationProfileConfig( - kSutEgressPortBufferProfile, kInitialBufferConfig, *gnmi_stub)) - << "failed to restore initial buffer config -- switch config may be " - "corrupted, causing subsequent tests to fail"; - }); - - // Set equal bufer for all queues. - absl::flat_hash_map bufferConfigByQueueName = { - {"LLQ1", - {/*.dedicated_buffer =*/0, - /*.use_shared_buffer =*/true, - /*.shared_buffer_type =*/ - "openconfig-qos:DYNAMIC_BASED_ON_SCALING_FACTOR", - /*.dynamic_limit_scaling_factor =*/-3, - /*.shared_static_limit =*/0}}, - {"LLQ2", - {/*.dedicated_buffer =*/0, - /*.use_shared_buffer =*/true, - /*.shared_buffer_type =*/ - "openconfig-qos:DYNAMIC_BASED_ON_SCALING_FACTOR", - /*.dynamic_limit_scaling_factor =*/-3, - /*.shared_static_limit =*/0}}, - {"BE1", - {/*.dedicated_buffer =*/0, - /*.use_shared_buffer =*/true, - /*.shared_buffer_type =*/ - "openconfig-qos:DYNAMIC_BASED_ON_SCALING_FACTOR", - /*.dynamic_limit_scaling_factor =*/-3, - /*.shared_static_limit =*/0}}, - {"AF1", - {/*.dedicated_buffer =*/0, - /*.use_shared_buffer =*/true, - /*.shared_buffer_type =*/ - "openconfig-qos:DYNAMIC_BASED_ON_SCALING_FACTOR", - /*.dynamic_limit_scaling_factor =*/-3, - /*.shared_static_limit =*/0}}, - {"AF2", - {/*.dedicated_buffer =*/0, - /*.use_shared_buffer =*/true, - /*.shared_buffer_type =*/ - "openconfig-qos:DYNAMIC_BASED_ON_SCALING_FACTOR", - /*.dynamic_limit_scaling_factor =*/-3, - /*.shared_static_limit =*/0}}, - {"AF3", - {/*.dedicated_buffer =*/0, - /*.use_shared_buffer =*/true, - /*.shared_buffer_type =*/ - "openconfig-qos:DYNAMIC_BASED_ON_SCALING_FACTOR", - /*.dynamic_limit_scaling_factor =*/-3, - /*.shared_static_limit =*/0}}, - {"AF4", - {/*.dedicated_buffer =*/0, - /*.use_shared_buffer =*/true, - /*.shared_buffer_type =*/ - "openconfig-qos:DYNAMIC_BASED_ON_SCALING_FACTOR", - /*.dynamic_limit_scaling_factor =*/-3, - /*.shared_static_limit =*/0}}, - {"NC1", - {/*.dedicated_buffer =*/0, - /*.use_shared_buffer =*/true, - /*.shared_buffer_type =*/ - "openconfig-qos:DYNAMIC_BASED_ON_SCALING_FACTOR", - /*.dynamic_limit_scaling_factor =*/-3, - /*.shared_static_limit =*/0}}, - }; - ASSERT_OK(SetBufferConfigParameters(kSutEgressPortBufferProfile, - bufferConfigByQueueName, *gnmi_stub)); + // Configure fair buffer profile (until the end of the test) so we can ignore + // buffer carving effects. + ASSERT_OK_AND_ASSIGN(Cleanup restore_buffer_config, + ConfigureFairBufferConfigForPortUntilEndOfScope( + kSutEgressPort, *gnmi_stub)); // Set lower & upper bounds (CIRs/PIRs) such that: // - Round-robin-scheduled queues are not rate limited. @@ -942,7 +945,8 @@ TEST_P(FrontpanelQosTest, WeightedRoundRobinWeightsAreRespected) { // Connect to Ixia and fix some parameters. LOG(INFO) << "configuring Ixia traffic"; - ASSERT_OK_AND_ASSIGN(const std::string kIxiaHandle, ConnectToIxia(*testbed)); + ASSERT_OK_AND_ASSIGN(const std::string kIxiaHandle, + ixia::ConnectToIxia(*testbed)); ASSERT_OK_AND_ASSIGN( const std::string kIxiaMainSrcPortHandle, ixia::IxiaVport(kIxiaHandle, kIxiaMainSrcPort, *testbed)); @@ -1204,87 +1208,16 @@ TEST_P(FrontpanelQosTest, StrictQueuesAreStrictlyPrioritized) { "corrupted, causing subsequent test to fail"; }); - // Save Buffer config and restore at end of the test. - ASSERT_OK_AND_ASSIGN( - const std::string kSutEgressPortBufferProfile, - GetBufferAllocationProfileByEgressPort(kSutEgressPort, *gnmi_stub)); - // Before we update the buffer config, save the current config and - // prepare to restore it at the end of the test. - ASSERT_OK_AND_ASSIGN(const std::string kInitialBufferConfig, - GetBufferAllocationProfileConfig( - kSutEgressPortBufferProfile, *gnmi_stub)); - const auto kRestoreBufferConfig = absl::Cleanup([&] { - EXPECT_OK(UpdateBufferAllocationProfileConfig( - kSutEgressPortBufferProfile, kInitialBufferConfig, *gnmi_stub)) - << "failed to restore initial buffer config -- switch config may be " - "corrupted, causing subsequent tests to fail"; - }); - - // Set equal buffer for all queues. - absl::flat_hash_map bufferConfigByQueueName = { - {"LLQ1", - {/*.dedicated_buffer =*/0, - /*.use_shared_buffer =*/true, - /*.shared_buffer_type =*/ - "openconfig-qos:DYNAMIC_BASED_ON_SCALING_FACTOR", - /*.dynamic_limit_scaling_factor =*/-3, - /*.shared_static_limit =*/0}}, - {"LLQ2", - {/*.dedicated_buffer =*/0, - /*.use_shared_buffer =*/true, - /*.shared_buffer_type =*/ - "openconfig-qos:DYNAMIC_BASED_ON_SCALING_FACTOR", - /*.dynamic_limit_scaling_factor =*/-3, - /*.shared_static_limit =*/0}}, - {"BE1", - {/*.dedicated_buffer =*/0, - /*.use_shared_buffer =*/true, - /*.shared_buffer_type =*/ - "openconfig-qos:DYNAMIC_BASED_ON_SCALING_FACTOR", - /*.dynamic_limit_scaling_factor =*/-3, - /*.shared_static_limit =*/0}}, - {"AF1", - {/*.dedicated_buffer =*/0, - /*.use_shared_buffer =*/true, - /*.shared_buffer_type =*/ - "openconfig-qos:DYNAMIC_BASED_ON_SCALING_FACTOR", - /*.dynamic_limit_scaling_factor =*/-3, - /*.shared_static_limit =*/0}}, - {"AF2", - {/*.dedicated_buffer =*/0, - /*.use_shared_buffer =*/true, - /*.shared_buffer_type =*/ - "openconfig-qos:DYNAMIC_BASED_ON_SCALING_FACTOR", - /*.dynamic_limit_scaling_factor =*/-3, - /*.shared_static_limit =*/0}}, - {"AF3", - {/*.dedicated_buffer =*/0, - /*.use_shared_buffer =*/true, - /*.shared_buffer_type =*/ - "openconfig-qos:DYNAMIC_BASED_ON_SCALING_FACTOR", - /*.dynamic_limit_scaling_factor =*/-3, - /*.shared_static_limit =*/0}}, - {"AF4", - {/*.dedicated_buffer =*/0, - /*.use_shared_buffer =*/true, - /*.shared_buffer_type =*/ - "openconfig-qos:DYNAMIC_BASED_ON_SCALING_FACTOR", - /*.dynamic_limit_scaling_factor =*/-3, - /*.shared_static_limit =*/0}}, - {"NC1", - {/*.dedicated_buffer =*/0, - /*.use_shared_buffer =*/true, - /*.shared_buffer_type =*/ - "openconfig-qos:DYNAMIC_BASED_ON_SCALING_FACTOR", - /*.dynamic_limit_scaling_factor =*/-3, - /*.shared_static_limit =*/0}}, - }; - ASSERT_OK(SetBufferConfigParameters(kSutEgressPortBufferProfile, - bufferConfigByQueueName, *gnmi_stub)); + // Configure fair buffer profile (until the end of the test) so we can ignore + // buffer carving effects. + ASSERT_OK_AND_ASSIGN(Cleanup restore_buffer_config, + ConfigureFairBufferConfigForPortUntilEndOfScope( + kSutEgressPort, *gnmi_stub)); // Connect to Ixia and fix constant traffic parameters. LOG(INFO) << "connecting to Ixia"; - ASSERT_OK_AND_ASSIGN(const std::string kIxiaHandle, ConnectToIxia(*testbed)); + ASSERT_OK_AND_ASSIGN(const std::string kIxiaHandle, + ixia::ConnectToIxia(*testbed)); ASSERT_OK_AND_ASSIGN( const std::string kIxiaMainTrafficSrcPortHandle, ixia::IxiaVport(kIxiaHandle, kIxiaMainTrafficSrcPort, *testbed)); @@ -2066,7 +1999,8 @@ TEST_P(FrontpanelBufferTest, BufferCarving) { // Connect to Ixia and fix endpoints & parameters. LOG(INFO) << "configuring Ixia traffic"; - ASSERT_OK_AND_ASSIGN(const std::string kIxiaHandle, ConnectToIxia(*testbed)); + ASSERT_OK_AND_ASSIGN(const std::string kIxiaHandle, + ixia::ConnectToIxia(*testbed)); ASSERT_OK_AND_ASSIGN( const std::string kIxiaIpv4SrcPortHandle, ixia::IxiaVport(kIxiaHandle, kIxiaIpv4SrcPort, *testbed)); diff --git a/tests/qos/qos_test_util.cc b/tests/qos/qos_test_util.cc index 67369046..28a3796b 100644 --- a/tests/qos/qos_test_util.cc +++ b/tests/qos/qos_test_util.cc @@ -91,103 +91,6 @@ int64_t TotalPacketsForQueue(const QueueCounters &counters) { return counters.num_packets_dropped + counters.num_packets_transmitted; } -absl::Status SetPortSpeedInBitsPerSecond(const std::string &port_speed, - const std::string &iface, - gnmi::gNMI::StubInterface &gnmi_stub) { - std::string ops_config_path = absl::StrCat( - "interfaces/interface[name=", iface, "]/ethernet/config/port-speed"); - std::string ops_val = - absl::StrCat("{\"openconfig-if-ethernet:port-speed\":", port_speed, "}"); - - RETURN_IF_ERROR(pins_test::SetGnmiConfigPath(&gnmi_stub, ops_config_path, - GnmiSetType::kUpdate, ops_val)); - - return absl::OkStatus(); -} - -absl::StatusOr -GetPortSpeedInBitsPerSecond(const std::string &interface_name, - gnmi::gNMI::StubInterface &gnmi_stub) { - // Map keyed on openconfig speed string to value in bits per second. - // http://ops.openconfig.net/branches/models/master/docs/openconfig-interfaces.html#mod-openconfig-if-ethernet - const auto kPortSpeedTable = - absl::flat_hash_map({ - {"openconfig-if-ethernet:SPEED_100GB", 100000000000}, - {"openconfig-if-ethernet:SPEED_200GB", 200000000000}, - {"openconfig-if-ethernet:SPEED_400GB", 400000000000}, - }); - std::string speed_state_path = - absl::StrCat("interfaces/interface[name=", interface_name, - "]/ethernet/state/port-speed"); - - std::string parse_str = "openconfig-if-ethernet:port-speed"; - ASSIGN_OR_RETURN( - std::string response, - GetGnmiStatePathInfo(&gnmi_stub, speed_state_path, parse_str)); - - auto speed = kPortSpeedTable.find(StripQuotes(response)); - if (speed == kPortSpeedTable.end()) { - return absl::NotFoundError(response); - } - return speed->second; -} - -absl::Status SetPortMtu(int port_mtu, const std::string &interface_name, - gnmi::gNMI::StubInterface &gnmi_stub) { - std::string config_path = absl::StrCat( - "interfaces/interface[name=", interface_name, "]/config/mtu"); - std::string value = absl::StrCat("{\"config:mtu\":", port_mtu, "}"); - - RETURN_IF_ERROR(pins_test::SetGnmiConfigPath(&gnmi_stub, config_path, - GnmiSetType::kUpdate, value)); - - return absl::OkStatus(); -} - -absl::StatusOr CheckLinkUp(const std::string &iface, - gnmi::gNMI::StubInterface &gnmi_stub) { - std::string oper_status_state_path = - absl::StrCat("interfaces/interface[name=", iface, "]/state/oper-status"); - - std::string parse_str = "openconfig-interfaces:oper-status"; - ASSIGN_OR_RETURN( - std::string ops_response, - GetGnmiStatePathInfo(&gnmi_stub, oper_status_state_path, parse_str)); - - return ops_response == "\"UP\""; -} - -// Go over the connections and return vector of connections -// whose links are up. -absl::StatusOr> GetReadyIxiaLinks( - thinkit::GenericTestbed &generic_testbed, - gnmi::gNMI::StubInterface &gnmi_stub) { - std::vector links; - - absl::flat_hash_map interface_info = - generic_testbed.GetSutInterfaceInfo(); - // Loop through the interface_info looking for Ixia/SUT interface pairs, - // checking if the link is up. Add the pair to connections. - for (const auto &[interface, info] : interface_info) { - bool sut_link_up = false; - if (info.interface_modes.contains(thinkit::TRAFFIC_GENERATOR)) - { - ASSIGN_OR_RETURN(sut_link_up, CheckLinkUp(interface, gnmi_stub)); - if (sut_link_up) { - ASSIGN_OR_RETURN(int64_t bit_per_second, - GetPortSpeedInBitsPerSecond(interface, gnmi_stub)); - links.push_back(IxiaLink{ - .ixia_interface = info.peer_interface_name, - .sut_interface = interface, - .sut_interface_bits_per_second = bit_per_second, - }); - } - } - } - - return links; -} - absl::StatusOr> ParseIpv4DscpToQueueMapping(absl::string_view gnmi_config) { // TODO: Actually parse config -- hard-coded for now. diff --git a/tests/qos/qos_test_util.h b/tests/qos/qos_test_util.h index 2227d680..3f46dff9 100644 --- a/tests/qos/qos_test_util.h +++ b/tests/qos/qos_test_util.h @@ -56,49 +56,6 @@ absl::StatusOr GetGnmiQueueCounterWithTimestamp( // Get total packets (transmitted + dropped) for port queue. int64_t TotalPacketsForQueue(const QueueCounters &counters); -// Set port speed using gNMI. -absl::Status SetPortSpeedInBitsPerSecond(const std::string &port_speed, - const std::string &iface, - gnmi::gNMI::StubInterface &gnmi_stub); - -// Get configured port speed. -absl::StatusOr -GetPortSpeedInBitsPerSecond(const std::string &interface_name, - gnmi::gNMI::StubInterface &gnmi_stub); - -// Get configured port speed. -absl::StatusOr GetPortSpeed(const std::string &interface_name, - gnmi::gNMI::StubInterface &gnmi_stub); - -// Set port MTU using gNMI. -absl::Status SetPortMtu(int port_mtu, const std::string &interface_name, - gnmi::gNMI::StubInterface &gnmi_stub); - -// Set a port in loopback mode. -absl::Status SetPortLoopbackMode(bool port_loopback, - absl::string_view interface_name, - gnmi::gNMI::StubInterface &gnmi_stub); - -// Check if switch port link is up. -absl::StatusOr CheckLinkUp(const std::string &iface, - gnmi::gNMI::StubInterface &gnmi_stub); - -// Structure represents a link between SUT and Ixia. -// This is represented by Ixia interface name and the SUT's gNMI interface -// name. -struct IxiaLink { - std::string ixia_interface; - std::string sut_interface; - // Speed of the SUT interface in bits/second. - int64_t sut_interface_bits_per_second = 0; -}; - -// Go over the connections and return vector of connections -// whose links are up. -absl::StatusOr> GetReadyIxiaLinks( - thinkit::GenericTestbed &generic_testbed, - gnmi::gNMI::StubInterface &gnmi_stub); - // Parse IPv4 DSCP to queue mapping from gnmi configuration. absl::StatusOr> ParseIpv4DscpToQueueMapping(absl::string_view gnmi_config);