From 400b8cd385b2133b6c944613ba48c5befd432692 Mon Sep 17 00:00:00 2001 From: VSuryaprasad-hcl <159443973+VSuryaprasad-HCL@users.noreply.github.com> Date: Wed, 25 Dec 2024 01:25:33 +0000 Subject: [PATCH] [Thinkit] Add a util to verify queue limits are correctly set.Associate test case Id. Add a queue counter log in sFlow test. fix thread issue. Use absl::CleanUp to join threads in case tests failed. Fix parameter name.Add util to check if sFlow is enabled in gNMI config. (#875) Co-authored-by: kishanps --- tests/forwarding/smoke_test.cc | 121 ------- .../system/packet_forwarding_tests.cc | 98 ++--- tests/qos/qos_test_util.cc | 9 + tests/qos/qos_test_util.h | 2 + tests/sflow/BUILD.bazel | 2 + tests/sflow/sflow_test.cc | 336 +++++++++++------- tests/sflow/sflow_test.h | 4 +- tests/sflow/sflow_util.cc | 51 ++- tests/sflow/sflow_util.h | 15 +- tests/sflow/sflow_util_test.cc | 83 +++++ 10 files changed, 378 insertions(+), 343 deletions(-) diff --git a/tests/forwarding/smoke_test.cc b/tests/forwarding/smoke_test.cc index 43fa879b..6c4ff8ee 100644 --- a/tests/forwarding/smoke_test.cc +++ b/tests/forwarding/smoke_test.cc @@ -199,127 +199,6 @@ TEST_P(SmokeTestFixture, FixedTableAddModifyDeleteOk) { ASSERT_OK(pdpi::SetMetadataAndSendPiWriteRequest(&GetSutP4RuntimeSession(), pi_request)); - const sai::WriteRequest pd_delete = gutil::ParseProtoOrDie( - R"pb( - updates { - type: DELETE - table_entry { - acl_ingress_table_entry { - match { is_ip { value: "0x1" } } - priority: 10 - action { acl_forward {} } - } - } - } - )pb"); - ASSERT_OK_AND_ASSIGN(p4::v1::WriteRequest pi_delete, - pdpi::PdWriteRequestToPi( - sai::GetIrP4Info(sai::Instantiation::kMiddleblock), pd_delete)); - - // Insert works. - ASSERT_OK(pdpi::SetMetadataAndSendPiWriteRequest(&GetSutP4RuntimeSession(), - pi_insert)); - - // ACL table entries are expected to contain counter data. However, it's - // updated periodically and may not be avaialable immediatly after writing so - // we poll the entry for a few seconds until we see the data. - absl::Time timeout = absl::Now() + absl::Seconds(11); - p4::v1::ReadResponse pi_read_response; - p4::v1::ReadRequest pi_read_request; - pi_read_request.add_entities()->mutable_table_entry(); - do { - ASSERT_OK_AND_ASSIGN(pi_read_response, - pdpi::SetMetadataAndSendPiReadRequest( - &GetSutP4RuntimeSession(), pi_read_request)); - ASSERT_EQ(pi_read_response.entities_size(), 1); - - if (absl::Now() > timeout) { - FAIL() << "ACL table entry does not have counter data."; - } - } while (!pi_read_response.entities(0).table_entry().has_counter_data()); - - // Many ACL table attribues are NOT modifiable currently due to missing SAI - // implementation. Because there are no production use-cases, these are - // de-prioritized. - ASSERT_THAT(pdpi::SetMetadataAndSendPiWriteRequest(&GetSutP4RuntimeSession(), - pi_modify), - StatusIs(absl::StatusCode::kUnknown)); - - // Delete works. - ASSERT_OK(pdpi::SetMetadataAndSendPiWriteRequest(&GetSutP4RuntimeSession(), - pi_delete)); -} - -TEST_P(SmokeTestFixture, FixedTableAddModifyDeleteOk) { - p4::v1::WriteRequest pi_request; - ASSERT_OK_AND_ASSIGN( - *pi_request.add_updates(), - pins::VrfTableUpdate( - sai::GetIrP4Info(sai::Instantiation::kMiddleblock), p4::v1::Update::INSERT, "vrf-1")); - ASSERT_OK_AND_ASSIGN( - *pi_request.add_updates(), - pins::RouterInterfaceTableUpdate(sai::GetIrP4Info(sai::Instantiation::kMiddleblock), - p4::v1::Update::INSERT, - "router-intf-1", /*port=*/"1", - /*src_mac=*/"00:01:02:03:04:05")); - ASSERT_OK_AND_ASSIGN( - *pi_request.add_updates(), - pins::NeighborTableUpdate(sai::GetIrP4Info(sai::Instantiation::kMiddleblock), - p4::v1::Update::INSERT, - "router-intf-1", - /*neighbor_id=*/"fe80::0000:00ff:fe17:5f80", - /*dst_mac=*/"00:01:02:03:04:06")); - ASSERT_OK_AND_ASSIGN( - *pi_request.add_updates(), - pins::NexthopTableUpdate(sai::GetIrP4Info(sai::Instantiation::kMiddleblock), - p4::v1::Update::INSERT, - "nexthop-1", "router-intf-1", - /*neighbor_id=*/"fe80::0000:00ff:fe17:5f80")); - ASSERT_OK(pdpi::SetMetadataAndSendPiWriteRequest(&GetSutP4RuntimeSession(), - pi_request)); - - // Add and modify IPV4 table entry with different number of action params. - pi_request.Clear(); - ASSERT_OK_AND_ASSIGN( - *pi_request.add_updates(), - pins::Ipv4TableUpdate( - sai::GetIrP4Info(sai::Instantiation::kMiddleblock), - p4::v1::Update::INSERT, - pins::IpTableOptions{ - .vrf_id = "vrf-1", - .dst_addr_lpm = std::make_pair("20.0.0.1", 32), - .action = pins::IpTableOptions::Action::kSetNextHopId, - .nexthop_id = "nexthop-1", - })); - ASSERT_OK(pdpi::SetMetadataAndSendPiWriteRequest(&GetSutP4RuntimeSession(), - pi_request)); - pi_request.Clear(); - ASSERT_OK_AND_ASSIGN( - *pi_request.add_updates(), - pins::Ipv4TableUpdate(sai::GetIrP4Info(sai::Instantiation::kMiddleblock), - p4::v1::Update::MODIFY, - pins::IpTableOptions{ - .vrf_id = "vrf-1", - .dst_addr_lpm = std::make_pair("20.0.0.1", 32), - .action = pins::IpTableOptions::Action::kDrop, - })); - ASSERT_OK(pdpi::SetMetadataAndSendPiWriteRequest(&GetSutP4RuntimeSession(), - pi_request)); - - pi_request.Clear(); - ASSERT_OK_AND_ASSIGN( - *pi_request.add_updates(), - pins::Ipv4TableUpdate(sai::GetIrP4Info(sai::Instantiation::kMiddleblock), - p4::v1::Update::DELETE, - pins::IpTableOptions{ - .vrf_id = "vrf-1", - .dst_addr_lpm = std::make_pair("20.0.0.1", 32), - .action = pins::IpTableOptions::Action::kDrop, - })); - - ASSERT_OK(pdpi::SetMetadataAndSendPiWriteRequest(&GetSutP4RuntimeSession(), - pi_request)); - // This used to fail with a read error. ASSERT_OK(pdpi::ClearTableEntries(&GetSutP4RuntimeSession())); } diff --git a/tests/integration/system/packet_forwarding_tests.cc b/tests/integration/system/packet_forwarding_tests.cc index 18009057..0e9ab99e 100644 --- a/tests/integration/system/packet_forwarding_tests.cc +++ b/tests/integration/system/packet_forwarding_tests.cc @@ -20,9 +20,14 @@ #include #include "absl/container/flat_hash_map.h" +#include "absl/flags/flag.h" +#include "absl/numeric/int128.h" #include "absl/status/status.h" #include "absl/status/statusor.h" +#include "absl/strings/match.h" #include "absl/strings/numbers.h" +#include "absl/strings/str_cat.h" +#include "absl/strings/str_join.h" #include "absl/strings/string_view.h" #include "absl/strings/substitute.h" #include "absl/synchronization/mutex.h" @@ -37,6 +42,7 @@ #include "gutil/status_matchers.h" #include "gutil/testing.h" #include "lib/basic_traffic/basic_p4rt_util.h" +#include "lib/basic_traffic/basic_traffic.h" #include "lib/gnmi/gnmi_helper.h" #include "lib/utils/generic_testbed_utils.h" #include "p4/v1/p4runtime.pb.h" @@ -50,14 +56,18 @@ #include "sai_p4/instantiations/google/instantiations.h" #include "sai_p4/instantiations/google/sai_p4info.h" #include "sai_p4/instantiations/google/sai_pd.pb.h" +#include "tests/lib/switch_test_setup_helpers.h" #include "thinkit/control_device.h" #include "thinkit/generic_testbed.h" #include "thinkit/proto/generic_testbed.pb.h" #include "thinkit/switch.h" +ABSL_FLAG(bool, push_p4_info, true, "Push P4 info to SUT."); + namespace pins_test { namespace { +constexpr int kMtu[] = {1500, 5000, 9000}; // Test packet proto message sent from control switch to sut. constexpr absl::string_view kTestPacket = R"pb( @@ -74,72 +84,28 @@ constexpr absl::string_view kTestPacket = R"pb( ihl: "0x5" dscp: "0x03" ecn: "0x0" - total_length: "0x00d4" identification: "0x0000" flags: "0x0" fragment_offset: "0x0000" ttl: "0x20" protocol: "0x11" - checksum: "0x8c07" ipv4_source: "1.2.3.4" - ipv4_destination: "10.0.0.1" + ipv4_destination: "$0" } } headers { udp_header { source_port: "0x0000" destination_port: "0x0000" } } payload: "Basic L3 test packet")pb"; -// Sets up route from source port to destination port on sut. -absl::Status SetupRoute(pdpi::P4RuntimeSession& p4_session, int src_port_id, - int dst_port_id) { - RETURN_IF_ERROR(pins_test::basic_traffic::ProgramTrafficVrf(&p4_session, - sai::GetIrP4Info(sai::Instantiation::kMiddleblock))); - - RETURN_IF_ERROR( - basic_traffic::ProgramRouterInterface(&p4_session, src_port_id, - sai::GetIrP4Info(sai::Instantiation::kMiddleblock))); - - RETURN_IF_ERROR( - basic_traffic::ProgramRouterInterface(&p4_session, dst_port_id, - sai::GetIrP4Info(sai::Instantiation::kMiddleblock))); - - return basic_traffic::ProgramIPv4Route(&p4_session, dst_port_id, - sai::GetIrP4Info(sai::Instantiation::kMiddleblock)); -} - -// Sets up route from source port to destination port on sut. -absl::Status SetupRoute(pdpi::P4RuntimeSession& p4_session, int src_port_id, - int dst_port_id) { - RETURN_IF_ERROR(pins_test::basic_traffic::ProgramTrafficVrf(&p4_session, - sai::GetIrP4Info(sai::Instantiation::kMiddleblock))); - - RETURN_IF_ERROR( - basic_traffic::ProgramRouterInterface(&p4_session, src_port_id, - sai::GetIrP4Info(sai::Instantiation::kMiddleblock))); - - RETURN_IF_ERROR( - basic_traffic::ProgramRouterInterface(&p4_session, dst_port_id, - sai::GetIrP4Info(sai::Instantiation::kMiddleblock))); - - return basic_traffic::ProgramIPv4Route(&p4_session, dst_port_id, - sai::GetIrP4Info(sai::Instantiation::kMiddleblock)); -} - -// Sets up route from source port to destination port on sut. -absl::Status SetupRoute(pdpi::P4RuntimeSession& p4_session, int src_port_id, - int dst_port_id) { - RETURN_IF_ERROR(pins_test::basic_traffic::ProgramTrafficVrf(&p4_session, - sai::GetIrP4Info(sai::Instantiation::kMiddleblock))); - - RETURN_IF_ERROR( - basic_traffic::ProgramRouterInterface(&p4_session, src_port_id, - sai::GetIrP4Info(sai::Instantiation::kMiddleblock))); - - RETURN_IF_ERROR( - basic_traffic::ProgramRouterInterface(&p4_session, dst_port_id, - sai::GetIrP4Info(sai::Instantiation::kMiddleblock))); - - return basic_traffic::ProgramIPv4Route(&p4_session, dst_port_id, - sai::GetIrP4Info(sai::Instantiation::kMiddleblock)); +// Pushes the P4 Info to SUT if the flag push_p4_info is set to true and returns +// the P4 Runtime Session +absl::StatusOr> P4InfoPush( + const p4::config::v1::P4Info& p4_info, thinkit::GenericTestbed& testbed) { + std::optional p4info = std::nullopt; + if (absl::GetFlag(FLAGS_push_p4_info)) { + p4info = p4_info; + } + return ConfigureSwitchAndReturnP4RuntimeSession( + testbed.Sut(), /*gnmi_config=*/std::nullopt, p4info); } // Sets up route from source port to destination port on sut. @@ -177,20 +143,6 @@ TEST_P(PacketForwardingTestFixture, PacketForwardingTest) { ASSERT_OK_AND_ASSIGN(auto stub, testbed->Sut().CreateGnmiStub()); ASSERT_OK_AND_ASSIGN(auto port_id_by_interface, GetAllInterfaceNameToPortId(*stub)); - for (const auto& [interface, info] : testbed->GetSutInterfaceInfo()) { - if (info.interface_modes.contains(thinkit::CONTROL_INTERFACE)) { - int port_id; - EXPECT_TRUE(absl::SimpleAtoi(port_id_by_interface[interface], &port_id)); - LOG(INFO) << "@ " << interface << ":" << info.peer_interface_name << ":" - << port_id; - sut_interfaces.push_back(interface); - peer_interfaces.push_back(info.peer_interface_name); - sut_interface_ports.push_back(port_id); - } - if (sut_interfaces.size() == 2) { - break; - } - } // Set the `source_interface` to the first SUT control interface. const InterfaceLink& source_interface = control_interfaces[0]; @@ -225,22 +177,22 @@ TEST_P(PacketForwardingTestFixture, PacketForwardingTest) { // Make test packet based on destination port ID. const auto test_packet = - gutil::ParseProtoOrDie(kTestPacket); + gutil::ParseProtoOrDie(absl::Substitute( + kTestPacket, basic_traffic::PortIdToIP(destination_port_id))); ASSERT_OK_AND_ASSIGN(std::string test_packet_data, packetlib::SerializePacket(test_packet)); absl::Mutex mutex; std::vector received_packets; - + static constexpr int kPacketsToSend = 10; { ASSERT_OK_AND_ASSIGN(auto finalizer, testbed.get()->ControlDevice().CollectPackets()); LOG(INFO) << "Sending Packet to " << source_interface.peer_interface; - LOG(INFO) << "Test packet data: " << test_packet.DebugString(); - for (int i = 0; i < 10; i++) { + for (int i = 0; i < kPacketsToSend; i++) { // Send packet to SUT. ASSERT_OK(testbed->ControlDevice().SendPacket( source_interface.peer_interface, test_packet_data)) diff --git a/tests/qos/qos_test_util.cc b/tests/qos/qos_test_util.cc index c369aeba..4543cad8 100644 --- a/tests/qos/qos_test_util.cc +++ b/tests/qos/qos_test_util.cc @@ -7,6 +7,15 @@ #include "lib/gnmi/gnmi_helper.h" namespace pins_test { + +QueueCounters operator-(const QueueCounters &x, const QueueCounters &y) { + return QueueCounters{ + .num_packets_transmitted = + x.num_packets_transmitted - y.num_packets_transmitted, + .num_packet_dropped = x.num_packet_dropped - y.num_packet_dropped, + }; +} + absl::StatusOr GetGnmiQueueCounters( absl::string_view port, absl::string_view queue, gnmi::gNMI::StubInterface &gnmi_stub) { diff --git a/tests/qos/qos_test_util.h b/tests/qos/qos_test_util.h index 804b7386..46dccc0a 100644 --- a/tests/qos/qos_test_util.h +++ b/tests/qos/qos_test_util.h @@ -27,6 +27,8 @@ inline std::ostream &operator<<(std::ostream &os, counters.num_packets_transmitted, counters.num_packet_dropped); } +QueueCounters operator-(const QueueCounters &x, const QueueCounters &y); + // Get queue counters for a port queue. absl::StatusOr GetGnmiQueueCounters( absl::string_view port, absl::string_view queue, diff --git a/tests/sflow/BUILD.bazel b/tests/sflow/BUILD.bazel index 70231178..c6cef94d 100644 --- a/tests/sflow/BUILD.bazel +++ b/tests/sflow/BUILD.bazel @@ -40,6 +40,7 @@ cc_library( "//tests/lib:p4rt_fixed_table_programming_helper", "//tests/lib:switch_test_setup_helpers", "//tests/qos:gnmi_parsers", + "//tests/qos:qos_test_util", "//thinkit:generic_testbed", "//thinkit:generic_testbed_fixture", "//thinkit:mirror_testbed", @@ -49,6 +50,7 @@ cc_library( "@com_github_gnmi//proto/gnmi:gnmi_cc_grpc_proto", "@com_github_google_glog//:glog", "@com_github_grpc_grpc//:grpc++", + "@com_google_absl//absl/cleanup", "@com_google_absl//absl/container:flat_hash_map", "@com_google_absl//absl/container:flat_hash_set", "@com_google_absl//absl/status", diff --git a/tests/sflow/sflow_test.cc b/tests/sflow/sflow_test.cc index 3c4a63da..c8d1e550 100644 --- a/tests/sflow/sflow_test.cc +++ b/tests/sflow/sflow_test.cc @@ -25,6 +25,7 @@ #include #include +#include "absl/cleanup/cleanup.h" #include "absl/container/flat_hash_map.h" #include "absl/container/flat_hash_set.h" #include "absl/status/status.h" @@ -60,6 +61,7 @@ #include "tests/lib/p4rt_fixed_table_programming_helper.h" #include "tests/lib/switch_test_setup_helpers.h" #include "tests/qos/gnmi_parsers.h" +#include "tests/qos/qos_test_util.h" #include "tests/sflow/sflow_util.h" #include "thinkit/generic_testbed.h" #include "thinkit/mirror_testbed.h" @@ -117,6 +119,8 @@ constexpr absl::string_view kVrfIdPrefix = "vrf-"; // Used for sFLow collector. constexpr char kLocalLoopbackIpv6[] = "::1"; +constexpr absl::string_view kSflowQueueName = "BE1"; + // Returns IP address in dot-decimal notation, e.g. "192.168.2.1". std::string GetSrcIpv4AddrByPortId(const int port_id) { return netaddr::Ipv4Address(std::bitset<32>(kIpV4Src + port_id)).ToString(); @@ -467,6 +471,10 @@ absl::Status SendSflowTraffic(absl::Span traffic_refs, LOG(INFO) << "Read initial packet counters."; ASSIGN_OR_RETURN(std::vector initial_in_counters, GetIxiaInterfaceCounters(ixia_links, gnmi_stub)); + ASSIGN_OR_RETURN( + auto initial_queue_counter, + pins_test::GetGnmiQueueCounters("CPU", kSflowQueueName, *gnmi_stub)); + absl::Time start_time = absl::Now(); RETURN_IF_ERROR(SendNPacketsToSut( traffic_refs, topology_ref, @@ -487,11 +495,18 @@ absl::Status SendSflowTraffic(absl::Span traffic_refs, << ". Interface: " << ixia_links[i].sut_interface << ". Sent " << pkt_count << ". Received " << delta.in_pkts << "."; } + ASSIGN_OR_RETURN( + auto final_queue_counter, + pins_test::GetGnmiQueueCounters("CPU", kSflowQueueName, *gnmi_stub)); + // Show CPU counter data. auto delta = DeltaCounters(initial_in_counters.back(), final_in_counters.back()); LOG(INFO) << "\nIngress Deltas (\"CPU\"):\n"; ShowCounters(delta); + LOG(INFO) << "CPU " << kSflowQueueName << " queue counter delta:\n" + << final_queue_counter - initial_queue_counter + << " \n total time: " << (absl::Now() - start_time); return absl::OkStatus(); } @@ -858,6 +873,8 @@ void SflowTestFixture::TearDown() { // 2. Collect sFlow samples via sflowtool on SUT. // 3. Validate the result is as expected. TEST_P(SflowTestFixture, VerifyIngressSamplingForNoMatchPackets) { + testbed_->Environment().SetTestCaseID("6c980337-c187-4f19-99a9-e03b6dcf24b5"); + const IxiaLink& ingress_link = ready_links_[0]; Port ingress_port = Port{ .interface_name = ingress_link.sut_interface, @@ -873,25 +890,30 @@ TEST_P(SflowTestFixture, VerifyIngressSamplingForNoMatchPackets) { SetUpIxiaTraffic({ingress_link}, *testbed_, kPacketsNum, kPacketsPerSecond, pkt_size)); - // Start sflowtool on SUT. std::string sflow_result; - ASSERT_OK_AND_ASSIGN( - std::thread sflow_tool_thread, - StartSflowCollector( - ssh_client_, testbed_->Sut().ChassisName(), - kSflowtoolLineFormatTemplate, - /*sflowtool_runtime=*/kPacketsNum / kPacketsPerSecond + 30, - sflow_result)); - - // Send packets from Ixia to SUT. - ASSERT_OK(SendSflowTraffic(ixia_ref_pair.first, ixia_ref_pair.second, - {ingress_link}, *testbed_, gnmi_stub_.get(), - kPacketsNum, kPacketsPerSecond)); - - // Wait for sflowtool to finish. - if (sflow_tool_thread.joinable()) { - sflow_tool_thread.join(); + { + // Start sflowtool on SUT. + ASSERT_OK_AND_ASSIGN( + std::thread sflow_tool_thread, + StartSflowCollector( + ssh_client_, testbed_->Sut().ChassisName(), + kSflowtoolLineFormatTemplate, + /*sflowtool_runtime=*/kPacketsNum / kPacketsPerSecond + 30, + sflow_result)); + + // Wait for sflowtool to finish. + absl::Cleanup clean_up([&sflow_tool_thread] { + if (sflow_tool_thread.joinable()) { + sflow_tool_thread.join(); + } + }); + + // Send packets from Ixia to SUT. + ASSERT_OK(SendSflowTraffic(ixia_ref_pair.first, ixia_ref_pair.second, + {ingress_link}, *testbed_, gnmi_stub_.get(), + kPacketsNum, kPacketsPerSecond)); } + LOG(INFO) << "sFlow samples:\n" << sflow_result; VerifySflowResult( sflow_result, kSourceMac.ToHexString(), kDstMac.ToHexString(), @@ -952,25 +974,30 @@ TEST_P(SflowTestFixture, VerifyIngressSamplingForForwardedPackets) { SetUpIxiaTraffic({ingress_link}, *testbed_, kPacketsNum, kPacketsPerSecond, pkt_size)); - // Start sflowtool on SUT. std::string sflow_result; - ASSERT_OK_AND_ASSIGN( - std::thread sflow_tool_thread, - StartSflowCollector( - ssh_client_, testbed_->Sut().ChassisName(), - kSflowtoolLineFormatTemplate, - /*sflowtool_runtime=*/kPacketsNum / kPacketsPerSecond + 10, - sflow_result)); - - // Send packets via Ixia. - ASSERT_OK(SendSflowTraffic(ixia_ref_pair.first, ixia_ref_pair.second, - {ingress_link}, *testbed_, gnmi_stub_.get(), - kPacketsNum, kPacketsPerSecond)); - - // Wait for sflowtool to finish. - if (sflow_tool_thread.joinable()) { - sflow_tool_thread.join(); + { + // Start sflowtool on SUT. + ASSERT_OK_AND_ASSIGN( + std::thread sflow_tool_thread, + StartSflowCollector( + ssh_client_, testbed_->Sut().ChassisName(), + kSflowtoolLineFormatTemplate, + /*sflowtool_runtime=*/kPacketsNum / kPacketsPerSecond + 30, + sflow_result)); + + // Wait for sflowtool to finish. + absl::Cleanup clean_up([&sflow_tool_thread] { + if (sflow_tool_thread.joinable()) { + sflow_tool_thread.join(); + } + }); + + // Send packets from Ixia to SUT. + ASSERT_OK(SendSflowTraffic(ixia_ref_pair.first, ixia_ref_pair.second, + {ingress_link}, *testbed_, gnmi_stub_.get(), + kPacketsNum, kPacketsPerSecond)); } + LOG(INFO) << "sFlow samples:\n" << sflow_result; EXPECT_OK(testbed_->Environment().StoreTestArtifact("sflow_result.txt", sflow_result)); @@ -1015,25 +1042,30 @@ TEST_P(SflowTestFixture, VerifyIngressSamplesForDropPackets) { SetUpIxiaTraffic({ingress_link}, *testbed_, kPacketsNum, kPacketsPerSecond, pkt_size)); - // Start sflowtool on SUT. std::string sflow_result; - ASSERT_OK_AND_ASSIGN( - std::thread sflow_tool_thread, - StartSflowCollector( - ssh_client_, testbed_->Sut().ChassisName(), - kSflowtoolLineFormatTemplate, - /*sflowtool_runtime=*/kPacketsNum / kPacketsPerSecond + 10, - sflow_result)); - - // Send packets via Ixia. - ASSERT_OK(SendSflowTraffic(ixia_ref_pair.first, ixia_ref_pair.second, - {ingress_link}, *testbed_, gnmi_stub_.get(), - kPacketsNum, kPacketsPerSecond)); - - // Wait for sflowtool to finish. - if (sflow_tool_thread.joinable()) { - sflow_tool_thread.join(); + { + // Start sflowtool on SUT. + ASSERT_OK_AND_ASSIGN( + std::thread sflow_tool_thread, + StartSflowCollector( + ssh_client_, testbed_->Sut().ChassisName(), + kSflowtoolLineFormatTemplate, + /*sflowtool_runtime=*/kPacketsNum / kPacketsPerSecond + 30, + sflow_result)); + + // Wait for sflowtool to finish. + absl::Cleanup clean_up([&sflow_tool_thread] { + if (sflow_tool_thread.joinable()) { + sflow_tool_thread.join(); + } + }); + + // Send packets from Ixia to SUT. + ASSERT_OK(SendSflowTraffic(ixia_ref_pair.first, ixia_ref_pair.second, + {ingress_link}, *testbed_, gnmi_stub_.get(), + kPacketsNum, kPacketsPerSecond)); } + LOG(INFO) << "sFlow samples:\n" << sflow_result; VerifySflowResult( sflow_result, kSourceMac.ToHexString(), kDstMac.ToHexString(), @@ -1079,24 +1111,30 @@ TEST_P(SflowTestFixture, DISABLED_VerifyIngressSamplesForP4rtPuntTraffic) { SetUpIxiaTraffic({ingress_link}, *testbed_, kPacketsNum, kPacketsPerSecond, pkt_size)); - // Start sflowtool on SUT. std::string sflow_result; - ASSERT_OK_AND_ASSIGN( - std::thread sflow_tool_thread, - StartSflowCollector( - ssh_client_, testbed_->Sut().ChassisName(), - kSflowtoolLineFormatTemplate, - /*sflowtool_runtime=*/kPacketsNum / kPacketsPerSecond + 10, - sflow_result)); - - // Send packets via Ixia. - ASSERT_OK(SendSflowTraffic(ixia_ref_pair.first, ixia_ref_pair.second, - {ingress_link}, *testbed_, gnmi_stub_.get(), - kPacketsNum, kPacketsPerSecond)); - // Wait for sflowtool to finish. - if (sflow_tool_thread.joinable()) { - sflow_tool_thread.join(); + { + // Start sflowtool on SUT. + ASSERT_OK_AND_ASSIGN( + std::thread sflow_tool_thread, + StartSflowCollector( + ssh_client_, testbed_->Sut().ChassisName(), + kSflowtoolLineFormatTemplate, + /*sflowtool_runtime=*/kPacketsNum / kPacketsPerSecond + 30, + sflow_result)); + + // Wait for sflowtool to finish. + absl::Cleanup clean_up([&sflow_tool_thread] { + if (sflow_tool_thread.joinable()) { + sflow_tool_thread.join(); + } + }); + + // Send packets from Ixia to SUT. + ASSERT_OK(SendSflowTraffic(ixia_ref_pair.first, ixia_ref_pair.second, + {ingress_link}, *testbed_, gnmi_stub_.get(), + kPacketsNum, kPacketsPerSecond)); } + LOG(INFO) << "sFlow samples:\n" << sflow_result; VerifySflowResult( sflow_result, kSourceMac.ToHexString(), kDstMac.ToHexString(), @@ -1143,23 +1181,29 @@ TEST_P(SampleSizeTest, VerifySamplingSizeWorks) { // Start sflowtool on SUT. std::string sflow_result; - ASSERT_OK_AND_ASSIGN( - std::thread sflow_tool_thread, - StartSflowCollector( - ssh_client_, testbed_->Sut().ChassisName(), - kSflowtoolFullFormatTemplate, - /*sflowtool_runtime=*/kPacketsNum / kPacketsPerSecond + 10, - sflow_result)); - - // Send packets with kPacketSize from Ixia to SUT. - ASSERT_OK(SendSflowTraffic(ixia_ref_pair.first, ixia_ref_pair.second, - {ingress_link}, *testbed_, gnmi_stub_.get(), - kPacketsNum, kPacketsPerSecond)); - - // Wait for sflowtool to finish. - if (sflow_tool_thread.joinable()) { - sflow_tool_thread.join(); + { + // Start sflowtool on SUT. + ASSERT_OK_AND_ASSIGN( + std::thread sflow_tool_thread, + StartSflowCollector( + ssh_client_, testbed_->Sut().ChassisName(), + kSflowtoolFullFormatTemplate, + /*sflowtool_runtime=*/kPacketsNum / kPacketsPerSecond + 30, + sflow_result)); + + // Wait for sflowtool to finish. + absl::Cleanup clean_up([&sflow_tool_thread] { + if (sflow_tool_thread.joinable()) { + sflow_tool_thread.join(); + } + }); + + // Send packets from Ixia to SUT. + ASSERT_OK(SendSflowTraffic(ixia_ref_pair.first, ixia_ref_pair.second, + {ingress_link}, *testbed_, gnmi_stub_.get(), + kPacketsNum, kPacketsPerSecond)); } + LOG(INFO) << "sFlow samples with sampling size " << sample_size << ":\n" << sflow_result; EXPECT_OK(testbed_->Environment().StoreTestArtifact("sflow_result.txt", @@ -1197,24 +1241,30 @@ TEST_P(SampleRateTest, VerifySamplingRateWorks) { SetUpIxiaTraffic({ingress_link}, *testbed_, packets_num, traffic_rate, pkt_size)); - // Start sflowtool on SUT. std::string sflow_result; - ASSERT_OK_AND_ASSIGN( - std::thread sflow_tool_thread, - StartSflowCollector(ssh_client_, testbed_->Sut().ChassisName(), - kSflowtoolLineFormatTemplate, - /*sflowtool_runtime=*/packets_num / traffic_rate + 10, - sflow_result)); - - // Send packets from Ixia to SUT. - ASSERT_OK(SendSflowTraffic(ixia_ref_pair.first, ixia_ref_pair.second, - {ingress_link}, *testbed_, gnmi_stub_.get(), - packets_num, traffic_rate)); - - // Wait for sflowtool to finish. - if (sflow_tool_thread.joinable()) { - sflow_tool_thread.join(); + { + // Start sflowtool on SUT. + ASSERT_OK_AND_ASSIGN( + std::thread sflow_tool_thread, + StartSflowCollector( + ssh_client_, testbed_->Sut().ChassisName(), + kSflowtoolLineFormatTemplate, + /*sflowtool_runtime=*/packets_num / traffic_rate + 30, + sflow_result)); + + // Wait for sflowtool to finish. + absl::Cleanup clean_up([&sflow_tool_thread] { + if (sflow_tool_thread.joinable()) { + sflow_tool_thread.join(); + } + }); + + // Send packets from Ixia to SUT. + ASSERT_OK(SendSflowTraffic(ixia_ref_pair.first, ixia_ref_pair.second, + {ingress_link}, *testbed_, gnmi_stub_.get(), + packets_num, traffic_rate)); } + EXPECT_OK(testbed_->Environment().StoreTestArtifact( absl::Substitute("sflow_result_sampling_rate_$0_result.txt", sample_rate), sflow_result)); @@ -1526,25 +1576,30 @@ TEST_P(SflowMirrorTestFixture, TestInbandPathToSflowCollector) { ASSERT_OK(ProgramRoutesForIpv6(*sut_p4_session_, GetIrP4Info(), vrf_id, collector_ipv6, next_hop_id)); - // Start sflowtool on control switch. std::string sflow_result; - ASSERT_OK_AND_ASSIGN( - std::thread sflow_tool_thread, - StartSflowCollector( - GetParam().ssh_client, testbed.ControlSwitch().ChassisName(), - kSflowtoolLineFormatTemplate, - /*sflowtool_runtime=*/packets_num / kInbandTrafficPps + 20, - sflow_result)); - - // Send packets from control switch. - ASSERT_OK(SendNPacketsFromSwitch(packets_num, traffic_port.port_id, - GetIrP4Info(), *control_p4_session_, - testbed.Environment())); - - // Wait for sflowtool to finish and check sFlow result. - if (sflow_tool_thread.joinable()) { - sflow_tool_thread.join(); + { + // Start sflowtool on control switch. + ASSERT_OK_AND_ASSIGN( + std::thread sflow_tool_thread, + StartSflowCollector( + GetParam().ssh_client, testbed.ControlSwitch().ChassisName(), + kSflowtoolLineFormatTemplate, + /*sflowtool_runtime=*/packets_num / kInbandTrafficPps + 30, + sflow_result)); + + // Wait for sflowtool to finish. + absl::Cleanup clean_up([&sflow_tool_thread] { + if (sflow_tool_thread.joinable()) { + sflow_tool_thread.join(); + } + }); + + // Send packets from control switch. + ASSERT_OK(SendNPacketsFromSwitch(packets_num, traffic_port.port_id, + GetIrP4Info(), *control_p4_session_, + testbed.Environment())); } + EXPECT_OK(testbed.Environment().StoreTestArtifact("sflow_result.txt", sflow_result)); ASSERT_FALSE(sflow_result.empty()); @@ -1589,30 +1644,35 @@ TEST_P(SflowMirrorTestFixture, TestSamplingWorksOnAllInterfaces) { // Start sflowtool on peer switch. std::string sflow_result; - ASSERT_OK_AND_ASSIGN( - std::thread sflow_tool_thread, - StartSflowCollector( - GetParam().ssh_client, testbed.Sut().ChassisName(), - kSflowtoolLineFormatTemplate, - /*sflowtool_runtime=*/ - (packets_num / kInbandTrafficPps + 3) * port_id_per_port_name.size() + - 20, - sflow_result)); - - // Send packets from control switch on all UP interfaces. - for (const auto& [unused, port_id_str] : port_id_per_port_name) { - int port_id; - ASSERT_TRUE(absl::SimpleAtoi(port_id_str, &port_id)) - << port_id_str << " is not a valid port id."; - ASSERT_OK(SendNPacketsFromSwitch(packets_num, port_id, GetIrP4Info(), - *control_p4_session_, - testbed.Environment())); + { + ASSERT_OK_AND_ASSIGN( + std::thread sflow_tool_thread, + StartSflowCollector(GetParam().ssh_client, testbed.Sut().ChassisName(), + kSflowtoolLineFormatTemplate, + /*sflowtool_runtime=*/ + (packets_num / kInbandTrafficPps + 3) * + port_id_per_port_name.size() + + 20, + sflow_result)); + + // Wait for sflowtool to finish. + absl::Cleanup clean_up([&sflow_tool_thread] { + if (sflow_tool_thread.joinable()) { + sflow_tool_thread.join(); + } + }); + + // Send packets from control switch on all UP interfaces. + for (const auto& [unused, port_id_str] : port_id_per_port_name) { + int port_id; + ASSERT_TRUE(absl::SimpleAtoi(port_id_str, &port_id)) + << port_id_str << " is not a valid port id."; + ASSERT_OK(SendNPacketsFromSwitch(packets_num, port_id, GetIrP4Info(), + *control_p4_session_, + testbed.Environment())); + } } - // Wait for sflowtool to finish and check sFlow result. - if (sflow_tool_thread.joinable()) { - sflow_tool_thread.join(); - } for (const auto& [interface_name, port_id_str] : port_id_per_port_name) { int port_id; ASSERT_TRUE(absl::SimpleAtoi(port_id_str, &port_id)) diff --git a/tests/sflow/sflow_test.h b/tests/sflow/sflow_test.h index cc3fa70a..09c3e857 100644 --- a/tests/sflow/sflow_test.h +++ b/tests/sflow/sflow_test.h @@ -77,7 +77,7 @@ class SampleSizeTest : public SflowTestFixture {}; class SampleRateTest : public SflowTestFixture {}; -struct SflowInbandTestParams { +struct SflowMirrorTestParams { thinkit::MirrorTestbedInterface* testbed_interface; thinkit::SSHClient* ssh_client; std::string sut_gnmi_config; @@ -91,7 +91,7 @@ struct Port { }; class SflowMirrorTestFixture - : public ::testing::TestWithParam { + : public ::testing::TestWithParam { protected: void SetUp() override; diff --git a/tests/sflow/sflow_util.cc b/tests/sflow/sflow_util.cc index 59281ccc..9ecfbfe8 100644 --- a/tests/sflow/sflow_util.cc +++ b/tests/sflow/sflow_util.cc @@ -62,17 +62,28 @@ constexpr absl::string_view kSflowGnmiStateCollectorPortPath = } // namespace +absl::StatusOr IsSflowConfigEnabled(absl::string_view gnmi_config) { + ASSIGN_OR_RETURN(auto gnmi_config_json, json_yang::ParseJson(gnmi_config)); + return gnmi_config_json.find("openconfig-sampling:sampling") != + gnmi_config_json.end() && + gnmi_config_json["openconfig-sampling:sampling"] + ["openconfig-sampling-sflow:sflow"]["config"] + ["enabled"]; +} + absl::Status VerifyGnmiStateConverged(gnmi::gNMI::StubInterface* gnmi_stub, absl::string_view state_path, - absl::string_view expected_value) { - ASSIGN_OR_RETURN(std::string state_value, - pins_test::GetGnmiStatePathInfo(gnmi_stub, state_path)); + absl::string_view expected_value, + absl::string_view resp_parse_str) { + ASSIGN_OR_RETURN( + std::string state_value, + pins_test::GetGnmiStatePathInfo(gnmi_stub, state_path, resp_parse_str)); if (expected_value == state_value) { return absl::OkStatus(); } return absl::FailedPreconditionError( - absl::StrCat("Actual state path: ", state_path, " value is ", state_value, - ", expected value is ", expected_value)); + absl::StrCat("State path: [", state_path, "] actual value is ", + state_value, ", expected value is ", expected_value)); } absl::Status SetSflowSamplingSize(gnmi::gNMI::StubInterface* gnmi_stub, @@ -85,7 +96,7 @@ absl::Status SetSflowSamplingSize(gnmi::gNMI::StubInterface* gnmi_stub, return pins_test::WaitForCondition(VerifyGnmiStateConverged, timeout, gnmi_stub, kSflowGnmiStateSampleSizePath, - ops_val); + ops_val, /*resp_parse_str=*/""); } absl::Status SetSflowConfigEnabled(gnmi::gNMI::StubInterface* gnmi_stub, @@ -97,7 +108,7 @@ absl::Status SetSflowConfigEnabled(gnmi::gNMI::StubInterface* gnmi_stub, return pins_test::WaitForCondition(VerifyGnmiStateConverged, timeout, gnmi_stub, kSflowGnmiStateEnablePath, - ops_val); + ops_val, /*resp_parse_str=*/""); } absl::Status SetSflowIngressSamplingRate(gnmi::gNMI::StubInterface* gnmi_stub, @@ -116,7 +127,7 @@ absl::Status SetSflowIngressSamplingRate(gnmi::gNMI::StubInterface* gnmi_stub, return pins_test::WaitForCondition( VerifyGnmiStateConverged, timeout, gnmi_stub, absl::Substitute(kSflowGnmiStateInterfaceSampleRatePath, interface), - ops_val); + ops_val, /*resp_parse_str=*/""); } absl::Status VerifySflowStatesConverged( @@ -353,4 +364,28 @@ GetSflowSamplingRateForInterfaces( return interface_to_sample_rate; } +absl::Status VerifySflowQueueLimitState(gnmi::gNMI::StubInterface* gnmi_stub, + int queue_number, + int expected_queue_limit, + absl::Duration timeout) { + const std::string kQueueLimitStatePath = absl::Substitute( + R"(/qos/scheduler-policies/scheduler-policy[name=cpu_scheduler]/schedulers/scheduler[sequence=$0]/two-rate-three-color/state)", + queue_number); + auto verify_queue_limit = [&]() -> absl::Status { + ASSIGN_OR_RETURN( + std::string state_value, + pins_test::GetGnmiStatePathInfo(gnmi_stub, kQueueLimitStatePath, + "openconfig-qos:state")); + ASSIGN_OR_RETURN(const auto resp_json, json_yang::ParseJson(state_value)); + if (resp_json["google-pins-qos:pir-pkts"] == + absl::StrCat(expected_queue_limit)) { + return absl::OkStatus(); + } + return absl::FailedPreconditionError(absl::StrCat( + "State path: [", kQueueLimitStatePath, "] actual value is ", + state_value, ", expected value is ", expected_queue_limit)); + }; + return pins_test::WaitForCondition(verify_queue_limit, timeout); +} + } // namespace pins diff --git a/tests/sflow/sflow_util.h b/tests/sflow/sflow_util.h index 41cc7fb0..5a0a1678 100644 --- a/tests/sflow/sflow_util.h +++ b/tests/sflow/sflow_util.h @@ -26,11 +26,18 @@ namespace pins { +// Returns true iff +// ["openconfig-sampling:sampling"]["openconfig-sampling-sflow:sflow"]["config"]["enabled"] +// exists and equals true. Returns a InvalidArgumentError if failed to parse +// config. +absl::StatusOr IsSflowConfigEnabled(absl::string_view gnmi_config); + // Reads value from `state_path` and verifies it is the same with // `expected_value`. Returns a FailedPreconditionError if not matched. absl::Status VerifyGnmiStateConverged(gnmi::gNMI::StubInterface* gnmi_stub, absl::string_view state_path, - absl::string_view expected_value); + absl::string_view expected_value, + absl::string_view resp_parse_str = ""); // Sets sFLow sampling size to `sampling_size` and checks if it's applied to // corresponding state path in `timeout`. Returns error if failed. @@ -85,5 +92,11 @@ GetSflowSamplingRateForInterfaces( gnmi::gNMI::StubInterface* gnmi_stub, const absl::flat_hash_set& interfaces); +// Verifies that cpu_scheduler limit for `queue_sequence` is set to +// `expected_queue_limit`. +absl::Status VerifySflowQueueLimitState( + gnmi::gNMI::StubInterface* gnmi_stub, int queue_number, + int expected_queue_limit, absl::Duration timeout = absl::Seconds(5)); + } // namespace pins #endif // PINS_TESTS_SFLOW_SFLOW_UTIL_H_ diff --git a/tests/sflow/sflow_util_test.cc b/tests/sflow/sflow_util_test.cc index a29ec141..35a7d224 100644 --- a/tests/sflow/sflow_util_test.cc +++ b/tests/sflow/sflow_util_test.cc @@ -167,6 +167,55 @@ constexpr absl::string_view kResultJson = R"json({ } })json"; +TEST(SflowconfigTest, SflowEnabledTrue) { + const std::string sflow_config = R"json({ + "openconfig-sampling:sampling": { + "openconfig-sampling-sflow:sflow": { + "config": { + "agent-id-ipv6": "8002:12::aab0", + "enabled": true, + "polling-interval": 0, + "sample-size": 12 + } + } + } +})json"; + EXPECT_THAT(IsSflowConfigEnabled(sflow_config), IsOkAndHolds(true)); +} + +TEST(SflowconfigTest, SflowEnabledFalse) { + const std::string sflow_config = R"json({ + "openconfig-sampling:sampling": { + "openconfig-sampling-sflow:sflow": { + "config": { + "agent-id-ipv6": "8002:12::aab0", + "enabled": false, + "polling-interval": 0, + "sample-size": 12 + } + } + } +})json"; + EXPECT_THAT(IsSflowConfigEnabled(sflow_config), IsOkAndHolds(false)); +} + +TEST(SflowconfigTest, NoSflowConfigEnabledFalse) { + const std::string interface_config = R"json({ + "openconfig-interfaces:interfaces": { + "interface": [ + { + "name": "bond0", + "state": { + "openconfig-p4rt:id": 1, + "oper-status": "UP" + } + } + ] + } +})json"; + EXPECT_THAT(IsSflowConfigEnabled(interface_config), IsOkAndHolds(false)); +} + TEST(SflowconfigTest, AppendSflowConfigSuccess) { EXPECT_THAT( UpdateSflowConfig(kGnmiConfig, @@ -541,5 +590,39 @@ TEST(SflowUtilTest, GetSampleRateSuccessForAllInterfaces) { IsOkAndHolds(UnorderedPointwise(Eq(), expected_map))); } +TEST(SflowUtilTest, UpdateQueueLimitSucceed) { + const int kQueueNumberForBE1 = 5; + gnmi::MockgNMIStub stub; + ON_CALL(stub, Get).WillByDefault(DoAll( + SetArgPointee<2>(gutil::ParseProtoOrDie( + R"pb(notification { + timestamp: 1664316653291898311 + prefix { origin: "openconfig" } + update { + path { + elem { name: "openconfig-qos:qos" } + elem { name: "scheduler-policies" } + elem { + name: "scheduler-policy" + key { key: "name" value: "cpu_scheduler" } + } + elem { name: "schedulers" } + elem { + name: "scheduler" + key { key: "sequence" value: "5" } + } + elem { name: "two-rate-three-colo" } + elem { name: "state" } + } + val { + json_ietf_val: "{\"openconfig-qos:state\":{\"google-pins-qos:bc-pkts\":0,\"google-pins-qos:be-pkts\":4,\"google-pins-qos:cir-pkts\":\"0\",\"google-pins-qos:pir-pkts\":\"120\"}}" + } + } + })pb")), + Return(grpc::Status::OK))); + ASSERT_OK(VerifySflowQueueLimitState(&stub, kQueueNumberForBE1, + /*expected_queue_limit=*/120)); +} + } // namespace } // namespace pins