diff --git a/bazel/BUILD.otg-models.bazel b/bazel/BUILD.otg-models.bazel index bd288107f..f90bbbb66 100644 --- a/bazel/BUILD.otg-models.bazel +++ b/bazel/BUILD.otg-models.bazel @@ -22,6 +22,7 @@ cc_proto_library( cc_grpc_library( name = "otg_grpc_proto", srcs = [":otg_proto"], + generate_mocks = True, grpc_only = True, deps = [":otg_cc_proto"], ) diff --git a/dvaas/dataplane_validation.cc b/dvaas/dataplane_validation.cc index fa045eb41..2d60cf2ed 100644 --- a/dvaas/dataplane_validation.cc +++ b/dvaas/dataplane_validation.cc @@ -1121,16 +1121,17 @@ absl::StatusOr DataplaneValidator::ValidateDataplane( pins_test::MirrorSutP4rtPortIdConfigToControlSwitch(testbed)); // Ensure that all enabled ports are up for control switch. - RETURN_IF_ERROR( - pins_test::WaitForEnabledInterfacesToBeUp(testbed.ControlSwitch())) + RETURN_IF_ERROR(pins_test::WaitForEnabledEthernetInterfacesToBeUp( + testbed.ControlSwitch())) .SetPrepend() - << "expected enabled interfaces on control switch to be up: "; + << "expected enabled ethernet interfaces on control switch to be up: "; } // Ensure that all enabled ports are up for SUT. - RETURN_IF_ERROR(pins_test::WaitForEnabledInterfacesToBeUp(testbed.Sut())) + RETURN_IF_ERROR( + pins_test::WaitForEnabledEthernetInterfacesToBeUp(testbed.Sut())) .SetPrepend() - << "expected enabled interfaces on SUT to be up: "; + << "expected enabled ethernet interfaces on SUT to be up: "; // Do not return on error in order to restore the original control switch // gNMI interface config's P4RT IDs. diff --git a/dvaas/mirror_testbed_config.cc b/dvaas/mirror_testbed_config.cc index 9a35a417f..5c7f1f8da 100644 --- a/dvaas/mirror_testbed_config.cc +++ b/dvaas/mirror_testbed_config.cc @@ -422,11 +422,12 @@ absl::Status MirrorTestbedConfigurator::ConfigureForForwardingTest( // Ensure that all enabled ports are up. if (params.wait_for_all_enabled_interfaces_to_be_up) { - RETURN_IF_ERROR(pins_test::WaitForEnabledInterfacesToBeUp(testbed_.Sut())) + RETURN_IF_ERROR( + pins_test::WaitForEnabledEthernetInterfacesToBeUp(testbed_.Sut())) .SetPrepend() << "expected enabled interfaces on SUT to be up: "; - RETURN_IF_ERROR( - pins_test::WaitForEnabledInterfacesToBeUp(testbed_.ControlSwitch())) + RETURN_IF_ERROR(pins_test::WaitForEnabledEthernetInterfacesToBeUp( + testbed_.ControlSwitch())) .SetPrepend() << "expected enabled interfaces on control switch to be up: "; } diff --git a/lib/BUILD.bazel b/lib/BUILD.bazel index 9ed368723..02e57d0bc 100644 --- a/lib/BUILD.bazel +++ b/lib/BUILD.bazel @@ -240,3 +240,34 @@ cc_test( ], ) +cc_library( + name = "otg_helper", + testonly = True, + srcs = ["otg_helper.cc"], + hdrs = ["otg_helper.h"], + deps = [ + "//gutil/gutil:status", + "@com_github_grpc_grpc//:grpc++", + "@com_github_otg_models//:otg_cc_proto", + "@com_github_otg_models//:otg_grpc_proto", + "@com_google_absl//absl/log", + "@com_google_absl//absl/status", + "@com_google_absl//absl/strings:string_view", + ], +) + +cc_test( + name = "otg_helper_test", + srcs = ["otg_helper_test.cc"], + deps = [ + ":otg_helper", + "//gutil/gutil:proto_matchers", + "//gutil/gutil:status_matchers", + "@com_github_grpc_grpc//:grpc++", + "@com_github_otg_models//:otg_cc_proto", + "@com_github_otg_models//:otg_grpc_proto", + "@com_google_absl//absl/status", + "@com_google_absl//absl/strings:string_view", + "@com_google_googletest//:gtest_main", + ], +) diff --git a/lib/gnmi/gnmi_helper.cc b/lib/gnmi/gnmi_helper.cc index 60745f059..72424aae1 100644 --- a/lib/gnmi/gnmi_helper.cc +++ b/lib/gnmi/gnmi_helper.cc @@ -2288,6 +2288,52 @@ GetAllInterfaceCounters(gnmi::gNMI::StubInterface& gnmi_stub) { return counters; } +Counters Counters::operator-(const Counters& other) const { + return Counters{ + in_pkts - other.in_pkts, + out_pkts - other.out_pkts, + in_octets - other.in_octets, + out_octets - other.out_octets, + in_unicast_pkts - other.in_unicast_pkts, + out_unicast_pkts - other.out_unicast_pkts, + in_multicast_pkts - other.in_multicast_pkts, + out_multicast_pkts - other.out_multicast_pkts, + in_broadcast_pkts - other.in_broadcast_pkts, + out_broadcast_pkts - other.out_broadcast_pkts, + in_errors - other.in_errors, + out_errors - other.out_errors, + in_discards - other.in_discards, + out_discards - other.out_discards, + in_buffer_discards - other.in_buffer_discards, + in_maxsize_exceeded - other.in_maxsize_exceeded, + in_fcs_errors - other.in_fcs_errors, + in_ipv4_pkts - other.in_ipv4_pkts, + out_ipv4_pkts - other.out_ipv4_pkts, + in_ipv6_pkts - other.in_ipv6_pkts, + out_ipv6_pkts - other.out_ipv6_pkts, + in_ipv6_discarded_pkts - other.in_ipv6_discarded_pkts, + out_ipv6_discarded_pkts - other.out_ipv6_discarded_pkts, + timestamp_ns - other.timestamp_ns, + }; +} + +absl::StatusOr GetCountersForInterface( + absl::string_view interface_name, gnmi::gNMI::StubInterface& gnmi_stub) { + ASSIGN_OR_RETURN( + std::string interface_info, + GetGnmiStatePathInfo( + &gnmi_stub, + absl::StrCat("interfaces/interface[name=", interface_name, "]"), + "openconfig-interfaces:interface")); + ASSIGN_OR_RETURN(json interface_json, json_yang::ParseJson(interface_info)); + if (!interface_json.is_array() || interface_json.empty()) { + return absl::InternalError(absl::StrCat( + "Expecting counters for interface ", interface_name, + " to have a non-zero JSON array, but got: ", interface_info)); + } + return GetCountersForInterface(interface_json[0]); +} + absl::StatusOr GetBadIntervalsCounter( absl::string_view interface_name, gnmi::gNMI::StubInterface& gnmi_stub) { ASSIGN_OR_RETURN(json port_counters_json, diff --git a/lib/gnmi/gnmi_helper.h b/lib/gnmi/gnmi_helper.h index 119cb647d..90ae47eea 100644 --- a/lib/gnmi/gnmi_helper.h +++ b/lib/gnmi/gnmi_helper.h @@ -165,6 +165,50 @@ struct Counters { std::optional carrier_transitions; uint64_t timestamp_ns = 0; std::optional blackhole_counters; + // Returns the difference between two counters. + Counters operator-(const Counters& other) const; + template + friend void AbslStringify(Sink& sink, const Counters& c) { + absl::Format(&sink, "in_pkts: %v\n", c.in_pkts); + absl::Format(&sink, "out_pkts: %v\n", c.out_pkts); + absl::Format(&sink, "in_octets: %v\n", c.in_octets); + absl::Format(&sink, "out_octets: %v\n", c.out_octets); + absl::Format(&sink, "in_unicast_pkts: %v\n", c.in_unicast_pkts); + absl::Format(&sink, "out_unicast_pkts: %v\n", c.out_unicast_pkts); + absl::Format(&sink, "in_multicast_pkts: %v\n", c.in_multicast_pkts); + absl::Format(&sink, "out_multicast_pkts: %v\n", c.out_multicast_pkts); + absl::Format(&sink, "in_broadcast_pkts: %v\n", c.in_broadcast_pkts); + absl::Format(&sink, "out_broadcast_pkts: %v\n", c.out_broadcast_pkts); + absl::Format(&sink, "in_errors: %v\n", c.in_errors); + absl::Format(&sink, "out_errors: %v\n", c.out_errors); + absl::Format(&sink, "in_discards: %v\n", c.in_discards); + absl::Format(&sink, "out_discards: %v\n", c.out_discards); + absl::Format(&sink, "in_buffer_discards: %v\n", c.in_buffer_discards); + absl::Format(&sink, "in_maxsize_exceeded: %v\n", c.in_maxsize_exceeded); + absl::Format(&sink, "in_fcs_errors: %v\n", c.in_fcs_errors); + absl::Format(&sink, "in_ipv4_pkts: %v\n", c.in_ipv4_pkts); + absl::Format(&sink, "out_ipv4_pkts: %v\n", c.out_ipv4_pkts); + absl::Format(&sink, "in_ipv6_pkts: %v\n", c.in_ipv6_pkts); + absl::Format(&sink, "out_ipv6_pkts: %v\n", c.out_ipv6_pkts); + absl::Format(&sink, "in_ipv6_discarded_pkts: %v\n", + c.in_ipv6_discarded_pkts); + absl::Format(&sink, "out_ipv6_discarded_pkts: %v\n", + c.out_ipv6_discarded_pkts); + if (c.carrier_transitions.has_value()) { + absl::Format(&sink, "carrier_transitions: %v\n", *c.carrier_transitions); + } + absl::Format(&sink, "timestamp_ns: %v\n", c.timestamp_ns); + if (c.blackhole_counters.has_value()) { + absl::Format(&sink, "blackhole_counters.in_discard_events: %v\n", + c.blackhole_counters.value().in_discard_events); + absl::Format(&sink, "blackhole_counters.out_discard_events: %v\n", + c.blackhole_counters.value().out_discard_events); + absl::Format(&sink, "blackhole_counters.in_error_events: %v\n", + c.blackhole_counters.value().in_error_events); + absl::Format(&sink, "blackhole_counters.fec_not_correctable_events: %v\n", + c.blackhole_counters.value().fec_not_correctable_events); + } + } }; struct BlackholeSwitchCounters { @@ -640,6 +684,10 @@ absl::StatusOr GetPortPfcRxEnable( absl::StatusOr> GetAllInterfaceCounters(gnmi::gNMI::StubInterface& gnmi_stub); +// Gets counters for an interface. +absl::StatusOr GetCountersForInterface( + absl::string_view interface_name, gnmi::gNMI::StubInterface& gnmi_stub); + // Gets blackhole counters for an interface. absl::StatusOr GetBlackholePortCounters( absl::string_view interface_name, gnmi::gNMI::StubInterface& gnmi_stub); diff --git a/lib/otg_helper.cc b/lib/otg_helper.cc new file mode 100644 index 000000000..213357ac2 --- /dev/null +++ b/lib/otg_helper.cc @@ -0,0 +1,117 @@ +#include "lib/otg_helper.h" + +#include "absl/log/log.h" +#include "absl/status/status.h" +#include "absl/strings/string_view.h" +#include "artifacts/otg.grpc.pb.h" +#include "artifacts/otg.pb.h" +#include "grpcpp/client_context.h" +#include "gutil/gutil/status.h" + +namespace pins_test::otg_helper { + +void AddPorts(otg::Config& config, absl::string_view src_port_name, + absl::string_view dst_port_name, + absl::string_view src_port_location, + absl::string_view dst_port_location) { + otg::Port* src_port = config.add_ports(); + otg::Port* dst_port = config.add_ports(); + src_port->set_name(src_port_name); + dst_port->set_name(dst_port_name); + src_port->set_location(src_port_location); + dst_port->set_location(dst_port_location); +} + +otg::Flow& CreateFlow(otg::Config& config, absl::string_view src_port_name, + absl::string_view dst_port_name, + absl::string_view flow_name) { + otg::Flow* flow = config.add_flows(); + flow->set_name(flow_name); + flow->mutable_tx_rx()->set_choice(otg::FlowTxRx::Choice::port); + flow->mutable_tx_rx()->mutable_port()->set_tx_name(src_port_name); + flow->mutable_tx_rx()->mutable_port()->set_rx_name(dst_port_name); + return *flow; +} + +void SetFlowSize(otg::Flow& flow, int flow_size) { + flow.mutable_size()->set_choice(otg::FlowSize::Choice::fixed); + flow.mutable_size()->set_fixed(flow_size); +} + +void SetFlowDuration(otg::Flow& flow, int pkt_count) { + flow.mutable_duration()->set_choice(otg::FlowDuration::Choice::fixed_packets); + flow.mutable_duration()->mutable_fixed_packets()->set_packets(pkt_count); +} + +void SetFlowRatePps(otg::Flow& flow, int flow_rate) { + flow.mutable_rate()->set_choice(otg::FlowRate::Choice::pps); + flow.mutable_rate()->set_pps(flow_rate); +} + +otg::FlowEthernet& AddEthernetHeader(otg::Flow& flow, absl::string_view src_mac, + absl::string_view dst_mac) { + otg::FlowHeader* eth_packet = flow.add_packet(); + eth_packet->set_choice(otg::FlowHeader::Choice::ethernet); + otg::FlowEthernet* eth_header = eth_packet->mutable_ethernet(); + eth_header->mutable_src()->set_choice( + otg::PatternFlowEthernetSrc::Choice::value); + eth_header->mutable_dst()->set_choice( + otg::PatternFlowEthernetDst::Choice::value); + eth_header->mutable_src()->set_value(src_mac); + eth_header->mutable_dst()->set_value(dst_mac); + return *eth_header; +} + +otg::FlowIpv4& AddIPv4Header(otg::Flow& flow, absl::string_view src_ipv4, + absl::string_view dst_ipv4) { + otg::FlowHeader* ipv4_packet = flow.add_packet(); + ipv4_packet->set_choice(otg::FlowHeader::Choice::ipv4); + otg::FlowIpv4* ipv4_header = ipv4_packet->mutable_ipv4(); + ipv4_header->mutable_src()->set_choice( + otg::PatternFlowIpv4Src::Choice::value); + ipv4_header->mutable_dst()->set_choice( + otg::PatternFlowIpv4Dst::Choice::value); + ipv4_header->mutable_src()->set_value(src_ipv4); + ipv4_header->mutable_dst()->set_value(dst_ipv4); + return *ipv4_header; +} + +void SetIPv4Priority(otg::FlowIpv4& ip_packet, int dscp, int ecn) { + ip_packet.mutable_priority()->set_choice(otg::FlowIpv4Priority::Choice::dscp); + ip_packet.mutable_priority()->mutable_dscp()->mutable_phb()->set_value(dscp); + ip_packet.mutable_priority()->mutable_dscp()->mutable_ecn()->set_value(ecn); +} + +otg::FlowIpv6& AddIPv6Header(otg::Flow& flow, absl::string_view src_ipv6, + absl::string_view dst_ipv6) { + otg::FlowHeader* ipv6_packet = flow.add_packet(); + ipv6_packet->set_choice(otg::FlowHeader::Choice::ipv6); + otg::FlowIpv6* ipv6_header = ipv6_packet->mutable_ipv6(); + ipv6_header->mutable_src()->set_choice( + otg::PatternFlowIpv6Src::Choice::value); + ipv6_header->mutable_dst()->set_choice( + otg::PatternFlowIpv6Dst::Choice::value); + ipv6_header->mutable_src()->set_value(src_ipv6); + ipv6_header->mutable_dst()->set_value(dst_ipv6); + return *ipv6_header; +} + +absl::Status SetTrafficTransmissionState( + otg::Openapi::StubInterface& otg_stub, + otg::StateTrafficFlowTransmit::State::Enum transmission_state) { + otg::SetControlStateRequest set_state_request; + otg::SetControlStateResponse set_state_response; + grpc::ClientContext set_state_context; + set_state_request.mutable_control_state()->set_choice( + otg::ControlState::Choice::traffic); + set_state_request.mutable_control_state()->mutable_traffic()->set_choice( + otg::StateTraffic::Choice::flow_transmit); + set_state_request.mutable_control_state() + ->mutable_traffic() + ->mutable_flow_transmit() + ->set_state(transmission_state); + return gutil::GrpcStatusToAbslStatus(otg_stub.SetControlState( + &set_state_context, set_state_request, &set_state_response)); +} + +} // namespace pins_test::otg_helper diff --git a/lib/otg_helper.h b/lib/otg_helper.h new file mode 100644 index 000000000..0cb507405 --- /dev/null +++ b/lib/otg_helper.h @@ -0,0 +1,44 @@ +#ifndef PINS_LIB_OTG_HELPER_H_ +#define PINS_LIB_OTG_HELPER_H_ + +#include "absl/log/log.h" +#include "absl/status/status.h" +#include "absl/strings/string_view.h" +#include "artifacts/otg.grpc.pb.h" +#include "artifacts/otg.pb.h" + +namespace pins_test::otg_helper { + +void AddPorts(otg::Config& config, absl::string_view src_port_name, + absl::string_view dst_port_name, + absl::string_view src_port_location, + absl::string_view dst_port_location); + +otg::Flow& CreateFlow(otg::Config& config, absl::string_view src_port_name, + absl::string_view dst_port_name, + absl::string_view flow_name); + +void SetFlowSize(otg::Flow& flow, int flow_size); + +void SetFlowDuration(otg::Flow& flow, int pkt_count); + +void SetFlowRatePps(otg::Flow& flow, int flow_rate); + +otg::FlowEthernet& AddEthernetHeader(otg::Flow& flow, absl::string_view src_mac, + absl::string_view dst_mac); + +otg::FlowIpv4& AddIPv4Header(otg::Flow& flow, absl::string_view src_ipv4, + absl::string_view dst_ipv4); + +void SetIPv4Priority(otg::FlowIpv4& ip_packet, int dscp, int ecn); + +otg::FlowIpv6& AddIPv6Header(otg::Flow& flow, absl::string_view src_ipv6, + absl::string_view dst_ipv6); + +absl::Status SetTrafficTransmissionState( + otg::Openapi::StubInterface& otg_stub, + otg::StateTrafficFlowTransmit::State::Enum transmission_state); + +} // namespace pins_test::otg_helper + +#endif // PINS_LIB_OTG_HELPER_H_ diff --git a/lib/otg_helper_test.cc b/lib/otg_helper_test.cc new file mode 100644 index 000000000..a1a8cb3db --- /dev/null +++ b/lib/otg_helper_test.cc @@ -0,0 +1,195 @@ +#include "lib/otg_helper.h" + +#include "absl/status/status.h" +#include "absl/strings/string_view.h" +#include "artifacts/otg.pb.h" +#include "artifacts/otg_mock.grpc.pb.h" +#include "gmock/gmock.h" +#include "grpcpp/support/status.h" +#include "gtest/gtest.h" +#include "gutil/gutil/proto_matchers.h" +#include "gutil/gutil/status_matchers.h" + +namespace pins_test::otg_helper { +namespace { + +using ::gutil::EqualsProto; +using ::gutil::StatusIs; +using ::testing::_; +using ::testing::Return; + +TEST(OtgHelperTest, AddPortsTest) { + otg::Config config; + + AddPorts(config, /*src_port_name=*/"eth-1/1", /*dst_port_name=*/"eth-1/2", + /*src_port_location=*/"00:00:00:00:00:1A", + /*dst_port_location=*/"00:00:00:00:00:1B"); + + EXPECT_THAT(config, EqualsProto(R"pb( + ports { name: "eth-1/1" location: "00:00:00:00:00:1A" } + ports { name: "eth-1/2" location: "00:00:00:00:00:1B" } + )pb")); +} + +TEST(OtgHelperTest, CreateFlowTest) { + otg::Config config; + otg::Flow& flow = CreateFlow(config, /*src_port_name=*/"eth-1/1", + /*dst_port_name=*/"eth-1/2", + /*flow_name=*/"test_flow"); + + EXPECT_THAT(config, EqualsProto(R"pb( + flows { + name: "test_flow" + tx_rx { + choice: port + port { tx_name: "eth-1/1" rx_name: "eth-1/2" } + } + } + )pb")); + + EXPECT_THAT(flow, EqualsProto(config.flows(0))); +} + +TEST(OtgHelperTest, SetFlowSizeTest) { + otg::Flow flow; + SetFlowSize(flow, /*flow_size=*/128); + + EXPECT_THAT(flow, EqualsProto(R"pb( + size { choice: fixed fixed: 128 } + )pb")); +} + +TEST(OtgHelperTest, SetFlowDurationTest) { + otg::Flow flow; + SetFlowDuration(flow, /*pkt_count=*/1000); + + EXPECT_THAT(flow, EqualsProto(R"pb( + duration { + choice: fixed_packets + fixed_packets { packets: 1000 } + } + )pb")); +} + +TEST(OtgHelperTest, SetFlowRatePpsTest) { + otg::Flow flow; + SetFlowRatePps(flow, /*flow_rate=*/100); + + EXPECT_THAT(flow, EqualsProto(R"pb( + rate { choice: pps pps: 100 } + )pb")); +} + +TEST(OtgHelperTest, AddEthernetHeaderTest) { + otg::Flow flow; + otg::FlowEthernet& eth_header = + AddEthernetHeader(flow, /*src_mac=*/"00:00:00:00:00:1A", + /*dst_mac=*/"00:00:00:00:00:1B"); + + EXPECT_THAT(flow, EqualsProto(R"pb( + packet { + choice: ethernet + ethernet { + src { choice: value value: "00:00:00:00:00:1A" } + dst { choice: value value: "00:00:00:00:00:1B" } + } + } + )pb")); + + EXPECT_THAT(eth_header, EqualsProto(flow.packet(0).ethernet())); +} + +TEST(OtgHelperTest, AddIPv4HeaderTest) { + otg::Flow flow; + otg::FlowIpv4& ipv4_header = + AddIPv4Header(flow, /*src_ipv4=*/"192.0.2.1", /*dst_ipv4=*/"192.0.2.2"); + + EXPECT_THAT(flow, EqualsProto(R"pb( + packet { + choice: ipv4 + ipv4 { + src { choice: value value: "192.0.2.1" } + dst { choice: value value: "192.0.2.2" } + } + } + )pb")); + + EXPECT_THAT(ipv4_header, EqualsProto(flow.packet(0).ipv4())); +} + +TEST(OtgHelperTest, SetIPv4PriorityTest) { + otg::FlowIpv4 ipv4_header; + + SetIPv4Priority(ipv4_header, /*dscp=*/8, /*ecn=*/2); + + EXPECT_THAT(ipv4_header, EqualsProto(R"pb( + priority { + choice: dscp + dscp { + phb { value: 8 } + ecn { value: 2 } + } + } + )pb")); +} + +TEST(OtgHelperTest, AddIPv6HeaderTest) { + otg::Flow flow; + otg::FlowIpv6& ipv6_header = AddIPv6Header(flow, /*src_ipv6=*/"2001:db8::1", + /*dst_ipv6=*/"2001:db8::2"); + + EXPECT_THAT(flow, EqualsProto(R"pb( + packet { + choice: ipv6 + ipv6 { + src { choice: value value: "2001:db8::1" } + dst { choice: value value: "2001:db8::2" } + } + } + )pb")); + + EXPECT_THAT(ipv6_header, EqualsProto(flow.packet(0).ipv6())); +} + +TEST(OtgHelperTest, SetTrafficTransmissionStateSuccess) { + otg::MockOpenapiStub mock_stub; + + EXPECT_CALL(mock_stub, SetControlState(_, EqualsProto(R"pb( + control_state { + choice: traffic + traffic { + choice: flow_transmit + flow_transmit { state: start } + } + } + )pb"), + _)) + .WillOnce(Return(grpc::Status::OK)); + + EXPECT_OK(SetTrafficTransmissionState( + mock_stub, otg::StateTrafficFlowTransmit::State::start)); +} + +TEST(OtgHelperTest, SetTrafficTransmissionStateFailure) { + otg::MockOpenapiStub mock_stub; + + EXPECT_CALL(mock_stub, SetControlState(_, EqualsProto(R"pb( + control_state { + choice: traffic + traffic { + choice: flow_transmit + flow_transmit { state: stop } + } + } + )pb"), + _)) + .WillOnce( + Return(grpc::Status(grpc::StatusCode::INTERNAL, "Mock RPC error"))); + + EXPECT_THAT(SetTrafficTransmissionState( + mock_stub, otg::StateTrafficFlowTransmit::State::stop), + StatusIs(absl::StatusCode::kInternal, "Mock RPC error")); +} + +} // namespace +} // namespace pins_test::otg_helper diff --git a/lib/utils/BUILD.bazel b/lib/utils/BUILD.bazel index 30514b6ee..b743f59da 100644 --- a/lib/utils/BUILD.bazel +++ b/lib/utils/BUILD.bazel @@ -97,6 +97,18 @@ cc_library( ], ) +cc_library( + name = "constants", + testonly = True, + srcs = ["constants.cc"], + hdrs = ["constants.h"], + deps = [ + "//thinkit/proto:generic_testbed_cc_proto", + "@com_github_otg_models//:otg_cc_proto", + "@com_google_absl//absl/time", + ], +) + cc_test( name = "generic_testbed_utils_test", srcs = ["generic_testbed_utils_test.cc"], diff --git a/lib/utils/constants.cc b/lib/utils/constants.cc new file mode 100644 index 000000000..7b00033a3 --- /dev/null +++ b/lib/utils/constants.cc @@ -0,0 +1,26 @@ +// Copyright 2025 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "lib/utils/constants.h" + +#include "absl/time/time.h" +#include "artifacts/otg.pb.h" +#include "thinkit/proto/generic_testbed.pb.h" + +namespace pins_test { + +// TODO: Use params to override the default timeouts. +absl::Duration GetColdRebootWaitForUpTime() { return kColdRebootWaitForUpTime; } + +} // namespace pins_test diff --git a/lib/utils/constants.h b/lib/utils/constants.h new file mode 100644 index 000000000..0fdb28eeb --- /dev/null +++ b/lib/utils/constants.h @@ -0,0 +1,33 @@ +// Copyright 2025 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef PINS_LIB_UTILS_CONSTANTS_H_ +#define PINS_LIB_UTILS_CONSTANTS_H_ + +#include "absl/time/time.h" +#include "artifacts/otg.pb.h" + +namespace pins_test { + +// TODO: Reduce reboot up time. +// Don't use this function in the test code as much as possible. +// Timeouts for all platforms. +inline constexpr absl::Duration kColdRebootWaitForUpTime = absl::Minutes(6); + +// Returns the time to wait for the SUT to cold reboot. +absl::Duration GetColdRebootWaitForUpTime(); + +} // namespace pins_test + +#endif // PINS_LIB_UTILS_CONSTANTS_H_ diff --git a/p4_fuzzer/fuzzer_config.h b/p4_fuzzer/fuzzer_config.h index e5748841a..94c389fc2 100644 --- a/p4_fuzzer/fuzzer_config.h +++ b/p4_fuzzer/fuzzer_config.h @@ -23,6 +23,7 @@ #include "absl/container/flat_hash_set.h" #include "absl/status/status.h" #include "absl/status/statusor.h" +#include "absl/strings/string_view.h" #include "lib/p4rt/p4rt_port.h" #include "p4/config/v1/p4info.pb.h" #include "p4/v1/p4runtime.pb.h" @@ -138,6 +139,11 @@ struct ConfigParams { // By default, do nothing. return absl::OkStatus(); }; + // A function for determining whether resource checks on `table_name` + // should be skipped. + std::function + IgnoreResourceExhaustionForTable = + [](absl::string_view table_name) { return false; }; }; class FuzzerConfig { @@ -231,7 +237,10 @@ class FuzzerConfig { GetModifyFuzzedMulticastGroupEntry() const { return params_.ModifyFuzzedMulticastGroupEntry; } - + const std::function& + GetIgnoreResourceExhaustionForTable() const { + return params_.IgnoreResourceExhaustionForTable; + } private: explicit FuzzerConfig() {} diff --git a/p4_pdpi/p4_runtime_session.cc b/p4_pdpi/p4_runtime_session.cc index 218e06f23..012165c50 100644 --- a/p4_pdpi/p4_runtime_session.cc +++ b/p4_pdpi/p4_runtime_session.cc @@ -615,7 +615,11 @@ absl::Status SplitSortedUpdatesIntoBatchesAndSend( } } // namespace -absl::Status ClearEntities(P4RuntimeSession& session) { +absl::Status ClearEntities( + P4RuntimeSession& session, + absl::AnyInvocable& entities_that_failed_to_be_deleted)> + execute_on_failure) { // Get P4Info from Switch. It is needed to sequence the delete requests. ASSIGN_OR_RETURN( p4::v1::GetForwardingPipelineConfigResponse response, diff --git a/p4_pdpi/p4_runtime_session.h b/p4_pdpi/p4_runtime_session.h index f4e3cae4d..5158ff8e7 100644 --- a/p4_pdpi/p4_runtime_session.h +++ b/p4_pdpi/p4_runtime_session.h @@ -388,8 +388,13 @@ ReadPiCounterData(P4RuntimeSession *session, // Checks that a read from `session` returns no entities. absl::Status CheckNoEntities(P4RuntimeSession &session); -// Deletes all entities read from `session`. -absl::Status ClearEntities(P4RuntimeSession &session); +// Deletes all entities read from `session`. If `execute_on_failure` is +// provided, it will be called with the entities that failed to be deleted. +absl::Status ClearEntities( + P4RuntimeSession& session, + absl::AnyInvocable& entities_that_failed_to_be_deleted)> + execute_on_failure = nullptr); // Resets all counters in all `TableEntry`s to zero. // Applies to both `counter_data` and `meter_counter_data`. diff --git a/tests/BUILD.bazel b/tests/BUILD.bazel index 2290bb066..550315bd4 100644 --- a/tests/BUILD.bazel +++ b/tests/BUILD.bazel @@ -32,8 +32,9 @@ cc_library( "//gutil/gutil:status", "//gutil/gutil:status_matchers", "//lib/gnmi:gnmi_helper", - "//p4_pdpi:p4_runtime_session", + "//lib/utils:constants", "//lib/validator:validator_lib", + "//p4_pdpi:p4_runtime_session", "//thinkit:mirror_testbed", "//thinkit:ssh_client", "//thinkit:switch", diff --git a/tests/forwarding/BUILD.bazel b/tests/forwarding/BUILD.bazel index fd58361cb..c1af0aaa8 100644 --- a/tests/forwarding/BUILD.bazel +++ b/tests/forwarding/BUILD.bazel @@ -61,6 +61,7 @@ cc_library( deps = [ ":mirror_blackbox_test_fixture", ":test_data", + "//gutil/gutil:status", "//gutil/gutil:proto_matchers", "//gutil/gutil:status_matchers", "//gutil/gutil:testing", @@ -80,12 +81,13 @@ cc_library( "//thinkit:mirror_testbed", "//thinkit:mirror_testbed_fixture", "//thinkit:test_environment", + "@com_github_grpc_grpc//:grpc++_unsecure", + "@com_github_p4lang_p4runtime//:p4runtime_cc_grpc", "@com_github_p4lang_p4runtime//:p4runtime_cc_proto", "@com_google_absl//absl/status", "@com_google_absl//absl/strings", "@com_google_absl//absl/time", "@com_google_googletest//:gtest", - "@com_google_protobuf//:protobuf", ], alwayslink = True, ) @@ -588,6 +590,7 @@ cc_library( "//sai_p4/instantiations/google/test_tools:test_entries", "//tests/lib:p4rt_fixed_table_programming_helper", "//tests/lib:switch_test_setup_helpers", + "//tests/qos:qos_test_util", "//thinkit:mirror_testbed", "//thinkit:mirror_testbed_fixture", "//thinkit:test_environment", diff --git a/tests/forwarding/fuzzer_tests.cc b/tests/forwarding/fuzzer_tests.cc index 3f11a92e2..e479078a7 100644 --- a/tests/forwarding/fuzzer_tests.cc +++ b/tests/forwarding/fuzzer_tests.cc @@ -28,6 +28,7 @@ #include "absl/random/random.h" #include "absl/random/seed_sequences.h" #include "absl/status/status.h" +#include "absl/strings/match.h" #include "absl/strings/str_cat.h" #include "absl/strings/str_join.h" #include "absl/strings/string_view.h" @@ -90,19 +91,21 @@ namespace { // Buffer time to wind down testing after the test iterations are complete. constexpr absl::Duration kEndOfTestBuffer = absl::Minutes(10); - -// Returns true if the given table should be masked with `current_version`. -bool IsMaskedResource(absl::string_view table_name, - gutil::Version current_version) { - absl::flat_hash_set masked_tables = {}; - return masked_tables.contains(table_name); -} +constexpr absl::string_view kResourceLimitFuzzerTestcase = + "P4rtWriteAndCheckNoInternalErrors"; } // namespace // FuzzerTestFixture class functions void FuzzerTestFixture::SetUp() { + const testing::TestInfo* const test_info = + testing::UnitTest::GetInstance()->current_test_info(); + // Expect link flaps only for the resource limit fuzzer test case as P4 table + // size is changed in the test which reboots the switch. + if (absl::StrContains(test_info->name(), kResourceLimitFuzzerTestcase)) { + GetParam().mirror_testbed->ExpectLinkFlaps(); + } GetParam().mirror_testbed->SetUp(); if (auto& id = GetParam().test_case_id; id.has_value()) { GetParam().mirror_testbed->GetMirrorTestbed().Environment().SetTestCaseID( @@ -172,17 +175,6 @@ TEST_P(FuzzerTestFixture, P4rtWriteAndCheckNoInternalErrors) { pins_test::ConfigureSwitchAndReturnP4RuntimeSession( sut, GetParam().gnmi_config, GetParam().p4info)); - // Current switch version. - ASSERT_OK_AND_ASSIGN( - gutil::Version current_version, - gutil::ParseVersion(GetParam().p4info.pkg_info().version())); - - // TODO: Remove version check when the P4Info version in release - // is equal or higher than SAI_P4_PKGINFO_VERSION_USES_FAIL_ON_FIRST. - ASSERT_OK_AND_ASSIGN( - gutil::Version first_version_with_fail_on_first, - gutil::ParseVersion(SAI_P4_PKGINFO_VERSION_USES_FAIL_ON_FIRST)); - // Record gNMI config and P4Info that we plan to push for debugging purposes. if (GetParam().gnmi_config.has_value()) { ASSERT_OK(environment.StoreTestArtifact("gnmi_config.txt", @@ -325,8 +317,7 @@ TEST_P(FuzzerTestFixture, P4rtWriteAndCheckNoInternalErrors) { // Ensure that the responses from the switch correctly use fail-on-first // ordering. - if (!GetParam().do_not_enforce_fail_on_first_switch_ordering && - current_version >= first_version_with_fail_on_first) { + if (!GetParam().do_not_enforce_fail_on_first_switch_ordering) { bool encountered_first_error = false; for (const pdpi::IrUpdateStatus& status : response.rpc_response().statuses()) { @@ -355,20 +346,14 @@ TEST_P(FuzzerTestFixture, P4rtWriteAndCheckNoInternalErrors) { EXPECT_NE(status.code(), google::rpc::Code::INTERNAL) << "Fuzzing should never cause an INTERNAL error, but got: " << status.DebugString(); - // Check resource exhaustion. - // Check for invalid multicast resource exhaustion - // once multicast resource are modeled. - if (status.code() == google::rpc::Code::RESOURCE_EXHAUSTED && - update.entity().has_table_entry()) { - int table_id = update.entity().table_entry().table_id(); + if (status.code() == google::rpc::Code::RESOURCE_EXHAUSTED) { ASSERT_OK_AND_ASSIGN( - const pdpi::IrTableDefinition& table, - gutil::FindOrStatus(config.GetIrP4Info().tables_by_id(), table_id)); + std::string table_name, + pdpi::EntityToTableName(config.GetIrP4Info(), update.entity())); - // If this isn't a specifically masked resource, then check if resource - // exhaustion is allowed. - if (!IsMaskedResource(table.preamble().alias(), current_version)) { - // Check that table is allowed to have exhausted resources. + // If this isn't a specifically masked resource, then check if + // resource exhaustion is allowed. + if (!config.GetIgnoreResourceExhaustionForTable()(table_name)) { ASSERT_OK(switch_state_->ResourceExhaustedIsAllowed(update)) << "\nUpdate = " << update.DebugString() << "\nState = " << switch_state_->SwitchStateSummary(); @@ -521,10 +506,7 @@ TEST_P(FuzzerTestFixture, P4rtWriteAndCheckNoInternalErrors) { for (const p4::v1::Update& update : pi_updates) { // If the switch doesn't support fail-on-first, batch requests based on // rank AND number of updates. - // TODO: Remove version check when the P4Info version in - // release is equal or higher than - // SAI_P4_PKGINFO_VERSION_USES_FAIL_ON_FIRST. - if (current_version < first_version_with_fail_on_first) { + if (!GetParam().do_not_enforce_fail_on_first_switch_ordering) { ASSERT_OK_AND_ASSIGN( std::string table_name, pdpi::EntityToTableName(config.GetIrP4Info(), update.entity())); diff --git a/tests/forwarding/fuzzer_tests.h b/tests/forwarding/fuzzer_tests.h index e34edfd3a..a8d8d1d39 100644 --- a/tests/forwarding/fuzzer_tests.h +++ b/tests/forwarding/fuzzer_tests.h @@ -133,6 +133,11 @@ struct FuzzerTestFixtureParams { // By default, do nothing. return absl::OkStatus(); }; + // A function for determining whether resource checks on `table_name` + // should be skipped. + std::function + IgnoreResourceExhaustionForTable = + [](absl::string_view table_name) { return false; }; }; class FuzzerTestFixture diff --git a/tests/forwarding/l3_admit_test.cc b/tests/forwarding/l3_admit_test.cc index 525128dcc..c0808da2f 100644 --- a/tests/forwarding/l3_admit_test.cc +++ b/tests/forwarding/l3_admit_test.cc @@ -518,6 +518,10 @@ TEST_P(L3AdmitTestFixture, L3AdmitCanUseMaskToAllowMultipleMacAddresses) { } TEST_P(L3AdmitTestFixture, L3AdmitCanUseInPortToRestrictMacAddresses) { + if (!pins::TableHasMatchField(ir_p4info_, "l3_admit_table", "in_port")) { + GTEST_SKIP() << "Skipping because l3_admit table in p4info does not " + "support match on in_port."; + } // Get SUT and control ports to test on. ASSERT_OK_AND_ASSIGN( @@ -608,6 +612,17 @@ TEST_P(L3AdmitTestFixture, L3AdmitCanUseInPortToRestrictMacAddresses) { TEST_P(L3AdmitTestFixture, L3PacketsCanBeRoutedWithOnlyARouterInterface) { + // TODO: This is a temporary workaround to mask l3 admit legacy + // RIF test on the testbeds that do not need legacy RIF. Legacy RIFs are not + // needed for Pod and should be removed from P4 models. Once non-legacy RIF + // is enforced for Pod, move this test filter back to whether legacy RIF is + // supported or not. + if (GetParam().skip_testing_legacy_rifs) { + GTEST_SKIP() + << "Skipping because there is no use case to use the router_interfaces " + "table entries that program l3_admit table."; + } + // 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( diff --git a/tests/forwarding/l3_admit_test.h b/tests/forwarding/l3_admit_test.h index 0a5805035..0a2462e9e 100644 --- a/tests/forwarding/l3_admit_test.h +++ b/tests/forwarding/l3_admit_test.h @@ -38,6 +38,7 @@ namespace pins { struct L3AdmitTestParams { thinkit::MirrorTestbedInterface *testbed_interface; std::optional p4info; + bool skip_testing_legacy_rifs; }; // This test assumes that the switch is set up with a gNMI config. diff --git a/tests/forwarding/l3_multicast_test.cc b/tests/forwarding/l3_multicast_test.cc index 436a01154..50f78163c 100644 --- a/tests/forwarding/l3_multicast_test.cc +++ b/tests/forwarding/l3_multicast_test.cc @@ -80,6 +80,11 @@ constexpr netaddr::MacAddress kOriginalSrcMacAddress(0x00, 0x22, 0x33, 0x44, constexpr netaddr::MacAddress kDropSrcMacAddress(0x02, 0x2a, 0x10, 0x00, 0x00, 0x02); constexpr int kDefaultInstance = 0; +constexpr netaddr::Ipv4Address kAllMulticastIpv4AddressMatch(224, 0, 0, 0); +constexpr netaddr::Ipv4Address kAllMulticastIpv4AddressMask(240, 0, 0, 0); +static const netaddr::Ipv6Address kAllMulticastIpv6AddressMatch = + netaddr::Ipv6Address(0xff00, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0); + // Pair of port ID and instance. struct ReplicaPair { std::string port_id; @@ -434,16 +439,43 @@ absl::Status SetupDefaultMulticastProgramming( // In the default traffic test setup, we only send traffic on one port // (port_index 0), so we only need to add one ACL entry. const std::string& port_id = params.sut_port_ids[0]; - ASSIGN_OR_RETURN(std::vector acl_entities, + ASSIGN_OR_RETURN(std::vector acl_v6_entities, + sai::EntryBuilder() + .AddIngressAclEntryRedirectingToMulticastGroup( + /*multicast_group_id=*/1, + {.in_port = port_id, + .route_hit = false, + .is_ipv6 = true, + .dst_ipv6 = + sai::P4RuntimeTernary{ + .value = kAllMulticastIpv6AddressMatch, + .mask = kAllMulticastIpv6AddressMatch, + }}) + .LogPdEntries() + .GetDedupedPiEntities(ir_p4info)); + + RETURN_IF_ERROR( + pdpi::InstallPiEntities(&session, ir_p4info, acl_v6_entities)); + entities_created.insert(entities_created.end(), acl_v6_entities.begin(), + acl_v6_entities.end()); + + ASSIGN_OR_RETURN(std::vector acl_v4_entities, sai::EntryBuilder() .AddIngressAclEntryRedirectingToMulticastGroup( /*multicast_group_id=*/1, - {.in_port = port_id, .route_hit = false}) + {.in_port = port_id, + .route_hit = false, + .is_ipv4 = true, + .dst_ip = + sai::P4RuntimeTernary{ + .value = kAllMulticastIpv4AddressMatch, + .mask = kAllMulticastIpv4AddressMask}}) .LogPdEntries() .GetDedupedPiEntities(ir_p4info)); - RETURN_IF_ERROR(pdpi::InstallPiEntities(&session, ir_p4info, acl_entities)); - entities_created.insert(entities_created.end(), acl_entities.begin(), - acl_entities.end()); + RETURN_IF_ERROR( + pdpi::InstallPiEntities(&session, ir_p4info, acl_v4_entities)); + entities_created.insert(entities_created.end(), acl_v4_entities.begin(), + acl_v4_entities.end()); return absl::OkStatus(); } @@ -1575,17 +1607,44 @@ TEST_P(L3MulticastTestFixture, ConfirmAclRedirectOverridesIpMulticastTable) { // Setup the ACL redirect path to use multicast group 2. constexpr int kMulticastGroup2 = 2; const std::string& input_port_id = sut_ports_ids[0]; - ASSERT_OK_AND_ASSIGN( - std::vector acl_entities, - sai::EntryBuilder() - .AddIngressAclEntryRedirectingToMulticastGroup( - kMulticastGroup2, {.in_port = input_port_id, .route_hit = true}) - .LogPdEntries() - .GetDedupedPiEntities(ir_p4info_)); + ASSERT_OK_AND_ASSIGN(std::vector acl_v6_entities, + sai::EntryBuilder() + .AddIngressAclEntryRedirectingToMulticastGroup( + kMulticastGroup2, + {.in_port = input_port_id, + .route_hit = true, + .is_ipv6 = true, + .dst_ipv6 = + sai::P4RuntimeTernary{ + .value = kAllMulticastIpv6AddressMatch, + .mask = kAllMulticastIpv6AddressMatch, + }}) + .LogPdEntries() + .GetDedupedPiEntities(ir_p4info_)); ASSERT_OK(pdpi::InstallPiEntities(sut_p4rt_session_.get(), ir_p4info_, - acl_entities)); - entities_created.insert(entities_created.end(), acl_entities.begin(), - acl_entities.end()); + acl_v6_entities)); + entities_created.insert(entities_created.end(), acl_v6_entities.begin(), + acl_v6_entities.end()); + LOG(INFO) << "Added " << entities_created.size() << " entities."; + + ASSERT_OK_AND_ASSIGN(std::vector acl_v4_entities, + sai::EntryBuilder() + .AddIngressAclEntryRedirectingToMulticastGroup( + kMulticastGroup2, + {.in_port = input_port_id, + .route_hit = true, + .is_ipv4 = true, + .dst_ip = + sai::P4RuntimeTernary{ + .value = kAllMulticastIpv4AddressMatch, + .mask = kAllMulticastIpv4AddressMask, + }}) + .LogPdEntries() + .GetDedupedPiEntities(ir_p4info_)); + ASSERT_OK(pdpi::InstallPiEntities(sut_p4rt_session_.get(), ir_p4info_, + acl_v4_entities)); + entities_created.insert(entities_created.end(), acl_v4_entities.begin(), + acl_v4_entities.end()); LOG(INFO) << "Added " << entities_created.size() << " entities."; // Inject test packets that would match the IP multicast table that would @@ -1950,12 +2009,11 @@ TEST_P(L3MulticastTestFixture, DeleteMulticastGroupWhileInUseFails) { ipmc_entities)); // Attempting to delete multicast group while in use results in an error. - EXPECT_THAT( - ClearEntities(*sut_p4rt_session_, ir_p4info_, mc_entities), - StatusIs(absl::StatusCode::kUnknown, - AllOf(HasSubstr("#1: INVALID_ARGUMENT"), - HasSubstr("[OrchAgent] Multicast group"), - HasSubstr("cannot be deleted because route entries")))); + EXPECT_THAT(ClearEntities(*sut_p4rt_session_, ir_p4info_, mc_entities), + StatusIs(absl::StatusCode::kUnknown, + AllOf(HasSubstr("#1: INVALID_ARGUMENT"), + HasSubstr("[OrchAgent] Multicast group"), + HasSubstr("cannot be deleted")))); // Clean up. EXPECT_OK(ClearEntities(*sut_p4rt_session_, ir_p4info_, ipmc_entities)); diff --git a/tests/forwarding/multicast_fallback_group_test.cc b/tests/forwarding/multicast_fallback_group_test.cc index 3bf2a81f4..d2e1909bb 100644 --- a/tests/forwarding/multicast_fallback_group_test.cc +++ b/tests/forwarding/multicast_fallback_group_test.cc @@ -59,12 +59,15 @@ #include "tests/forwarding/util.h" #include "tests/lib/p4rt_fixed_table_programming_helper.h" #include "tests/lib/switch_test_setup_helpers.h" +#include "tests/qos/qos_test_util.h" #include "thinkit/mirror_testbed.h" #include "thinkit/test_environment.h" namespace pins { namespace { +using ::pins_test::kMaxQueueCounterUpdateTime; +using ::pins_test::QueueCounters; // Vrf used in the test. constexpr absl::string_view kVrfId = "vrf-1"; @@ -79,6 +82,9 @@ constexpr int kMulticastInstance = 1; // Multicast group id used in the test. constexpr int kMulticastGroupId = 1; +// Maximum multicast group id used in the test. +constexpr int kMaxMulticastGroupId = 500; + // Multicast IP address used in the test. constexpr auto kMulticastDstIpv4 = netaddr::Ipv4Address(0xe0, 0, 0, 0x1); @@ -100,6 +106,9 @@ constexpr int kDefaultInputPortIndex = 0; // Default replica port index. constexpr int kDefaultReplicaPortIndex = 1; +// Default multicast queue. +constexpr absl::string_view multicast_queue_ = "MULTICAST"; + // Punts all packets on the control switch. absl::Status SetUpControlSwitch(pdpi::P4RuntimeSession& p4_session) { // Trap all packets on control switch. @@ -161,9 +170,10 @@ absl::flat_hash_map CountNumPacketsPerPort( // Generates a multicast packet. absl::StatusOr GenerateMulticastPacket() { std::string packet_hex = - "01005e01010100000000007b08004528005c000000002011b66201020304e00000010929" - "11d700485c7a6669656c643d49505f44535420697076343d3120656e6361707065643d30" - "20696e6e65725f697076343d302064656361703d302e2e2e2e2e2e2e2e2e2e2e2e2e"; + "01005e01010100000000007b080045340077000100004001964a01020304e00000010800" + "4f8700000000303030303030303030303030303030303030303030303030303030303030" + "303030303030303030303030303030303030303030303030303030303030303030303030" + "30303030303030303030303030303030303030303030303030"; return packetlib::ParsePacket(absl::HexStringToBytes(packet_hex)); } @@ -259,7 +269,8 @@ absl::Status InstallMulticastRitfs(pdpi::P4RuntimeSession& switch_session, absl::Status InstallMulticastGroup(pdpi::P4RuntimeSession& switch_session, absl::string_view default_replica_port, - const std::vector& ports) { + const std::vector& ports, + int multicast_group_id = kMulticastGroupId) { sai::EntryBuilder entry_builder; std::vector backup_replicas; for (int r = 1; r < ports.size(); ++r) { @@ -273,7 +284,7 @@ absl::Status InstallMulticastGroup(pdpi::P4RuntimeSession& switch_session, sai_replicas.push_back( sai::Replica{.egress_port = std::string(default_replica_port), .instance = kMulticastInstance}); - entry_builder.AddMulticastGroupEntry(kMulticastGroupId, sai_replicas); + entry_builder.AddMulticastGroupEntry(multicast_group_id, sai_replicas); return entry_builder.LogPdEntries().InstallDedupedEntities(switch_session); } @@ -316,21 +327,19 @@ absl::Status MulticastFallbackGroupTestFixture::SetUpSut( RETURN_IF_ERROR(pdpi::InstallPiTableEntry(sut_p4_session_.get(), pi_entity.table_entry())); - - std::vector replica_ports_with_default_port = replica_ports_; - replica_ports_with_default_port.push_back( + replica_ports_with_default_port_ = replica_ports_; + replica_ports_with_default_port_.push_back( sut_port_ids_[kDefaultReplicaPortIndex].GetP4rtEncoding()); std::vector replica_ports_with_default_and_input_port = - replica_ports_with_default_port; - replica_ports_with_default_port.push_back( + replica_ports_with_default_port_; + replica_ports_with_default_port_.push_back( sut_port_ids_[kDefaultInputPortIndex].GetP4rtEncoding()); // Programs the required vlan, vlan members, and multicast ritfs. RETURN_IF_ERROR(InstallVlanMembership( *sut_p4_session_, replica_ports_with_default_and_input_port)); - RETURN_IF_ERROR( - InstallMulticastRitfs(*sut_p4_session_, replica_ports_with_default_port)); - + RETURN_IF_ERROR(InstallMulticastRitfs(*sut_p4_session_, + replica_ports_with_default_port_)); // Programs the multicast group. RETURN_IF_ERROR(InstallMulticastGroup( *sut_p4_session_, @@ -459,6 +468,15 @@ TEST_P(MulticastFallbackGroupTestFixture, MeasureMulticastFallbackDuration) { "and export vector is not defined"; } + // Programs the multicast group. + for (int group_id = kMulticastGroupId + 1; group_id <= kMaxMulticastGroupId; + ++group_id) { + ASSERT_OK(InstallMulticastGroup( + *sut_p4_session_, + sut_port_ids_[kDefaultReplicaPortIndex].GetP4rtEncoding(), + replica_ports_, group_id)); + } + // Get port_name to port id mapping for the control switch. ASSERT_OK_AND_ASSIGN(const auto port_name_per_port_id, pins_test::GetPortNamePerPortId(*control_gnmi_stub_)); @@ -568,6 +586,17 @@ TEST_P(MulticastFallbackGroupTestFixture, VerifyMulticastRestoreAction) { ASSERT_OK_AND_ASSIGN( const auto& port_name, gutil::FindOrStatus(port_name_per_port_id, replica_ports_[0])); + std::vector kInitialQueueCounters; + for (int i = 0; i < replica_ports_with_default_port_.size(); ++i) { + ASSERT_OK_AND_ASSIGN( + const auto& port_name, + gutil::FindOrStatus(port_name_per_port_id, + replica_ports_with_default_port_[i])); + ASSERT_OK_AND_ASSIGN(QueueCounters queue_counters, + pins_test::GetGnmiQueueCounters( + port_name, multicast_queue_, *sut_gnmi_stub_)); + kInitialQueueCounters.push_back(queue_counters); + } int64_t total_packets_sent; int64_t total_packets_received; @@ -627,9 +656,43 @@ TEST_P(MulticastFallbackGroupTestFixture, VerifyMulticastRestoreAction) { total_packets_sent = test_data_.total_packets_sent; total_packets_received = test.output.size(); } + // Verify that the target egress queue counters incremented as expected. + int64_t total_queue_counters = 0; + for (int i = 0; i < replica_ports_with_default_port_.size(); ++i) { + const absl::Time kDeadline = absl::Now() + kMaxQueueCounterUpdateTime; + LOG(INFO) << "polling queue counters for port " + << replica_ports_with_default_port_[i] << " (this may take up to " + << kMaxQueueCounterUpdateTime << ")"; + ASSERT_OK_AND_ASSIGN( + const auto& port_name, + gutil::FindOrStatus(port_name_per_port_id, + replica_ports_with_default_port_[i])); + QueueCounters final_counters, delta_counters; + do { + ASSERT_OK_AND_ASSIGN(final_counters, + pins_test::GetGnmiQueueCounters( + port_name, multicast_queue_, *sut_gnmi_stub_)); + delta_counters = final_counters - kInitialQueueCounters[i]; + } while (delta_counters.num_packets_transmitted < total_packets_sent && + absl::Now() < kDeadline); + + total_queue_counters += delta_counters.num_packets_transmitted; + } - ASSERT_EQ(2 * total_packets_sent, total_packets_received) - << "Packet loss or duplicate packets received"; + ASSERT_EQ(total_queue_counters, 2 * total_packets_sent) + << "Mismatch in expected: " << 2 * total_packets_sent + << " and actual: " << total_queue_counters << " packets received"; + + // TODO: Remove this tolerance once the bug is fixed. + // The loss is due to the peer port status sync time. + int tolerate_packet_loss = 100; + // Assert that there are no duplicate packets. + ASSERT_LE(total_packets_received, 2 * total_packets_sent) + << "Duplicate packets received"; + // Assert that there is a maximum of 100 packets lost. + ASSERT_GE(total_packets_received + tolerate_packet_loss, + 2 * total_packets_sent) + << "Packet loss more than expected"; } // Bring down/up ports and verify traffic is distributed to the first up port in diff --git a/tests/forwarding/multicast_fallback_group_test.h b/tests/forwarding/multicast_fallback_group_test.h index f4645ed01..5ddc383c1 100644 --- a/tests/forwarding/multicast_fallback_group_test.h +++ b/tests/forwarding/multicast_fallback_group_test.h @@ -80,6 +80,8 @@ class MulticastFallbackGroupTestFixture std::vector control_port_ids_; // Replica ports for the multicast group on the SUT. std::vector replica_ports_; + // Replica ports for the multicast group on the SUT with the default port. + std::vector replica_ports_with_default_port_; // Replica ports for the multicast group on the control switch. std::vector control_replica_ports_; // Map from SUT port ID to control switch port ID. diff --git a/tests/forwarding/packet_at_port.h b/tests/forwarding/packet_at_port.h index 3436547b3..477c7037d 100644 --- a/tests/forwarding/packet_at_port.h +++ b/tests/forwarding/packet_at_port.h @@ -17,6 +17,7 @@ namespace pins { struct PacketAtPort { // TODO Change it to string type port. int port; + // This string represents the packet's serialized data (bytestring). std::string data; }; diff --git a/tests/forwarding/smoke_test.cc b/tests/forwarding/smoke_test.cc index 67639a066..5e45d1a6b 100644 --- a/tests/forwarding/smoke_test.cc +++ b/tests/forwarding/smoke_test.cc @@ -28,11 +28,14 @@ #include "absl/time/time.h" #include "gmock/gmock.h" #include "google/protobuf/util/message_differencer.h" +#include "grpcpp/client_context.h" #include "gtest/gtest.h" #include "gutil/gutil/proto_matchers.h" +#include "gutil/gutil/status.h" #include "gutil/gutil/status_matchers.h" #include "gutil/gutil/testing.h" #include "lib/gnmi/gnmi_helper.h" +#include "p4/v1/p4runtime.grpc.pb.h" #include "p4/v1/p4runtime.pb.h" #include "p4_pdpi/ir.h" #include "p4_pdpi/ir.pb.h" @@ -52,10 +55,9 @@ namespace pins_test { namespace { -using ::gutil::EqualsProto; using ::gutil::IsOk; using ::gutil::StatusIs; -using ::testing::ElementsAre; +using ::testing::AnyOf; using ::testing::Not; TEST_P(SmokeTestFixture, CanEstablishConnections) { @@ -75,6 +77,28 @@ TEST_P(SmokeTestFixture, CanEstablishConnections) { ASSERT_NE(control_switch_p4rt_session, nullptr); } +TEST_P(SmokeTestFixture, CanSendCapabilitiesRequest) { + thinkit::MirrorTestbed& testbed = + GetParam().mirror_testbed->GetMirrorTestbed(); + ASSERT_OK(pins_test::ConfigureSwitch( + testbed.Sut(), pins_test::PinsConfigView{ + .gnmi_config = GetParam().gnmi_config, + .p4info = GetParam().p4info, + })); + ASSERT_OK_AND_ASSIGN( + std::unique_ptr sut_p4rt_stub, + testbed.Sut().CreateP4RuntimeStub()); + + p4::v1::CapabilitiesRequest request; + p4::v1::CapabilitiesResponse response; + grpc::ClientContext context; + // TODO: Remove the Unimplemented error once the release + // supported RPC is rolled out to all switches. + EXPECT_THAT(gutil::GrpcStatusToAbslStatus( + sut_p4rt_stub->Capabilities(&context, request, &response)), + AnyOf(IsOk(), StatusIs(absl::StatusCode::kUnimplemented))); +} + TEST_P(SmokeTestFixture, AclTableAddModifyDeleteOk) { thinkit::MirrorTestbed& testbed = GetParam().mirror_testbed->GetMirrorTestbed(); @@ -641,5 +665,95 @@ TEST_P(SmokeTestFixture, DeleteReferencedMulticastRifNotOk) { EXPECT_OK(pdpi::InstallIrEntities(*sut_p4rt_session, read_entities)); } +// Check that unicast routes with a multicast destination range are accepted by +// the switch. We may disallow this via a p4-constraint in the future, but need +// the capability as a temporary workaround as of 2023-12-08. +TEST_P(SmokeTestFixture, CanInstallIpv4TableEntriesWithMulticastDstIp) { + thinkit::MirrorTestbed &testbed = + GetParam().mirror_testbed->GetMirrorTestbed(); + ASSERT_OK_AND_ASSIGN( + std::unique_ptr sut, + pins_test::ConfigureSwitchAndReturnP4RuntimeSession( + testbed.Sut(), GetParam().gnmi_config, GetParam().p4info)); + ASSERT_OK_AND_ASSIGN(pdpi::IrP4Info p4info, pdpi::GetIrP4Info(*sut)); + ASSERT_OK_AND_ASSIGN( + std::vector pi_entities, + sai::EntryBuilder().AddVrfEntry("vrf").GetDedupedPiEntities(p4info)); + ASSERT_OK(pdpi::InstallPiEntities(sut.get(), p4info, pi_entities)); + ASSERT_OK(pdpi::InstallPdTableEntries(*sut, R"pb( + entries { + ipv4_table_entry { + match { + vrf_id: "vrf" + ipv4_dst { value: "224.0.0.0" prefix_length: 8 } + } + action { drop {} } + } + } + entries { + ipv4_table_entry { + match { + vrf_id: "vrf" + ipv4_dst { value: "224.2.3.4" prefix_length: 32 } + } + action { drop {} } + } + } + )pb")); +} + +// Check that unicast routes with a multicast destination range are accepted by +// the switch. We may disallow this via a p4-constraint in the future, but need +// the capability as a temporary workaround as of 2023-12-08. +TEST_P(SmokeTestFixture, CanInstallIpv6TableEntriesWithMulticastDstIp) { + // Skip test for unsupported platforms. + if (GetParam().does_not_support_ipv6_entries_multicast_dest_ip) { + GTEST_SKIP() << "Skipping test since it is not supported on this platform."; + } + + thinkit::MirrorTestbed &testbed = + GetParam().mirror_testbed->GetMirrorTestbed(); + ASSERT_OK_AND_ASSIGN( + std::unique_ptr sut, + pins_test::ConfigureSwitchAndReturnP4RuntimeSession( + testbed.Sut(), GetParam().gnmi_config, GetParam().p4info)); + ASSERT_OK_AND_ASSIGN(pdpi::IrP4Info p4info, pdpi::GetIrP4Info(*sut)); + ASSERT_OK_AND_ASSIGN( + std::vector pi_entities, + sai::EntryBuilder().AddVrfEntry("vrf").GetDedupedPiEntities(p4info)); + ASSERT_OK(pdpi::InstallPiEntities(sut.get(), p4info, pi_entities)); + // TODO: Use `sai::EntryBuilder` instead of hard-coding the entries + // here. + ASSERT_OK(pdpi::InstallPdTableEntries(*sut, R"pb( + entries { + ipv6_table_entry { + match { + vrf_id: "vrf" + ipv6_dst { value: "ff00::" prefix_length: 8 } + } + action { drop {} } + } + } + entries { + ipv6_table_entry { + match { + vrf_id: "vrf" + ipv6_dst { value: "ff00:1234:5678:9012::" prefix_length: 64 } + } + action { drop {} } + } + } + entries { + ipv6_table_entry { + match { + vrf_id: "vrf" + ipv6_dst { value: "ff00::1234" prefix_length: 128 } + } + action { drop {} } + } + } + )pb")); +} + } // namespace } // namespace pins diff --git a/tests/forwarding/smoke_test.h b/tests/forwarding/smoke_test.h index 3df25f053..9b259629e 100644 --- a/tests/forwarding/smoke_test.h +++ b/tests/forwarding/smoke_test.h @@ -33,6 +33,9 @@ struct SmokeTestParams { // Use on platforms that don't support GRE tunnels to avoid using them in // tests. bool does_not_support_gre_tunnels; + // Use on platforms that don't support IPv6 entries with multicast destination + // IP address. + bool does_not_support_ipv6_entries_multicast_dest_ip; }; class SmokeTestFixture : public testing::TestWithParam { diff --git a/tests/gnmi/BUILD.bazel b/tests/gnmi/BUILD.bazel index 1c208431b..e2d25577d 100644 --- a/tests/gnmi/BUILD.bazel +++ b/tests/gnmi/BUILD.bazel @@ -162,84 +162,3 @@ cc_library( ], alwayslink = True, ) - -cc_library( - name = "blackhole_congestion_counters_ixia_test", - testonly = True, - srcs = ["blackhole_congestion_counters_ixia_test.cc"], - hdrs = ["blackhole_congestion_counters_ixia_test.h"], - deps = [ - "//gutil/gutil:collections", - "//gutil/gutil:proto", - "//gutil/gutil:status_matchers", - "//gutil/gutil:testing", - "//lib:ixia_helper", - "//lib/gnmi:gnmi_helper", - "//lib/utils:generic_testbed_utils", - "//lib/utils:json_utils", - "//p4_pdpi:p4_runtime_session", - "//p4_pdpi/packetlib:packetlib_cc_proto", - "//sai_p4/instantiations/google:sai_pd_cc_proto", - "//sai_p4/instantiations/google/test_tools:test_entries", - "//tests/lib:switch_test_setup_helpers", - "//thinkit:generic_testbed", - "//thinkit:generic_testbed_fixture", - "//thinkit:switch", - "//thinkit/proto:generic_testbed_cc_proto", - "@com_github_gnmi//proto/gnmi:gnmi_cc_grpc_proto", - "@com_github_nlohmann_json//:nlohmann_json", - "@com_google_absl//absl/container:flat_hash_map", - "@com_google_absl//absl/log", - "@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/strings:string_view", - "@com_google_absl//absl/time", - "@com_google_absl//absl/types:optional", - "@com_google_googletest//:gtest", - ], - alwayslink = True, -) - -cc_library( - name = "blackhole_congestion_counters_without_ixia_test", - testonly = True, - srcs = ["blackhole_congestion_counters_without_ixia_test.cc"], - hdrs = ["blackhole_congestion_counters_without_ixia_test.h"], - deps = [ - "//gutil/gutil:collections", - "//gutil/gutil:status_matchers", - "//gutil/gutil:testing", - "//lib/gnmi:gnmi_helper", - "//lib/utils:generic_testbed_utils", - "//lib/utils:json_utils", - "//p4_pdpi:p4_runtime_session", - "//p4_pdpi/netaddr:ipv4_address", - "//p4_pdpi/netaddr:ipv6_address", - "//p4_pdpi/packetlib", - "//p4_pdpi/packetlib:packetlib_cc_proto", - "//sai_p4/instantiations/google:sai_pd_cc_proto", - "//sai_p4/instantiations/google/test_tools:test_entries", - "//tests/lib:switch_test_setup_helpers", - "//thinkit:control_device", - "//thinkit:generic_testbed", - "//thinkit:generic_testbed_fixture", - "//thinkit:ssh_client", - "//thinkit:switch", - "//thinkit/proto:generic_testbed_cc_proto", - "@com_github_gnmi//proto/gnmi:gnmi_cc_grpc_proto", - "@com_github_nlohmann_json//:nlohmann_json", - "@com_google_absl//absl/container:flat_hash_map", - "@com_google_absl//absl/log", - "@com_google_absl//absl/memory", - "@com_google_absl//absl/status", - "@com_google_absl//absl/status:statusor", - "@com_google_absl//absl/strings", - "@com_google_absl//absl/strings:string_view", - "@com_google_absl//absl/time", - "@com_google_absl//absl/types:optional", - "@com_google_googletest//:gtest", - ], - alwayslink = True, -) diff --git a/tests/gnmi/blackhole_congestion_counters_ixia_test.cc b/tests/gnmi/blackhole_congestion_counters_ixia_test.cc deleted file mode 100644 index 1446d4ce8..000000000 --- a/tests/gnmi/blackhole_congestion_counters_ixia_test.cc +++ /dev/null @@ -1,614 +0,0 @@ -// Copyright 2025 Google LLC -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#include "tests/gnmi/blackhole_congestion_counters_ixia_test.h" - -#include - -#include -#include -#include -#include -#include - -#include "absl/container/flat_hash_map.h" -#include "absl/log/log.h" -#include "absl/status/status.h" -#include "absl/status/statusor.h" -#include "absl/strings/numbers.h" -#include "absl/strings/str_cat.h" -#include "absl/strings/str_format.h" -#include "absl/strings/string_view.h" -#include "absl/time/clock.h" -#include "absl/time/time.h" -#include "absl/types/optional.h" -#include "gmock/gmock.h" -#include "gtest/gtest.h" -#include "gutil/gutil/collections.h" -#include "gutil/gutil/proto.h" -#include "gutil/gutil/status.h" -#include "gutil/gutil/status_matchers.h" -#include "gutil/gutil/testing.h" -#include "include/nlohmann/json.hpp" -#include "lib/gnmi/gnmi_helper.h" -#include "lib/ixia_helper.h" -#include "lib/utils/generic_testbed_utils.h" -#include "lib/utils/json_utils.h" -#include "p4_pdpi/p4_runtime_session.h" -#include "p4_pdpi/packetlib/packetlib.pb.h" -#include "proto/gnmi/gnmi.grpc.pb.h" -#include "sai_p4/instantiations/google/sai_pd.pb.h" -#include "sai_p4/instantiations/google/test_tools/test_entries.h" -#include "tests/lib/switch_test_setup_helpers.h" -#include "thinkit/generic_testbed.h" -#include "thinkit/generic_testbed_fixture.h" -#include "thinkit/proto/generic_testbed.pb.h" -#include "thinkit/switch.h" - -namespace pins_test { -namespace { - -using ::nlohmann::json; - -constexpr int kInErrorsThresh = 10; -constexpr double kOutDiscardsRateThresh = 0.05; -constexpr int kDecimalToMilliPercent = 1e5; -constexpr uint64_t kOutDiscardsRateThreshMilliPercent = - kOutDiscardsRateThresh * kDecimalToMilliPercent; - -const char kOpenConfigSystemParseKey[] = "openconfig-system:system"; -const char kPinsDiagParseKey[] = "diag:diag"; - -// Takes a `gnmi_config` from the config generator and returns a modified -// version that sets up blackhole and congestion related configs and thresholds -// for the testbed. If the testbed does not support blackhole and congestion -// monitoring, returns an error status. -absl::StatusOr SetUpBlackholeCongestionConfig( - absl::string_view gnmi_config) { - ASSIGN_OR_RETURN(json gnmi_config_json, json_yang::ParseJson(gnmi_config)); - - // Gets the switch-level and port-level configs. - json& switch_config = - gnmi_config_json[kOpenConfigSystemParseKey][kPinsDiagParseKey]["config"]; - json& port_profiles = - gnmi_config_json[kOpenConfigSystemParseKey][kPinsDiagParseKey] - ["port-profiles"]["port-profile"]; - - if (switch_config.empty() || port_profiles.empty()) { - return absl::UnimplementedError( - "Blackhole and congestion monitoring not supported on testbed: Failed " - "to get switch-level or port-level configs."); - } - - // Updates switch-level configs. - switch_config["blackhole-monitoring-enable"] = true; - switch_config["congestion-monitoring-enable"] = true; - - // Updates port-level configs. - for (json& port_profile : port_profiles) { - port_profile["config"]["in-errors-threshold"] = kInErrorsThresh; - port_profile["config"]["out-discards-rate-threshold"] = - kOutDiscardsRateThreshMilliPercent; - // Updates queue-level configs. - for (json& queue_profile : port_profile["queues"]["queue"]) { - if (queue_profile["qname"] == "NC1") { - queue_profile["config"]["dropped-rate-threshold"] = - kOutDiscardsRateThreshMilliPercent; - } - } - } - - return json_yang::DumpJson(gnmi_config_json); -} - -struct IxiaSetUpResult { - // Ixia reference URL to topology. - std::string topology_ref; - // Ixia reference URL to traffic. - std::string traffic_ref; -}; - -absl::StatusOr SetUpIxiaForInFcsErrorTest( - absl::string_view ixia_interface, thinkit::GenericTestbed& generic_testbed, - int frame_rate, int frame_count) { - constexpr absl::string_view kLldpMulticastMac = "01:80:c2:00:00:0e"; - constexpr absl::string_view kSrcMac = "00:01:02:03:04:05"; - - ASSIGN_OR_RETURN(ixia::IxiaPortInfo ixia_port, - ixia::ExtractPortInfo(ixia_interface)); - - // Connect to the Ixia. - ASSIGN_OR_RETURN(std::string topology_ref, - ixia::IxiaConnect(ixia_port.hostname, generic_testbed)); - - // Connect to the Ixia card/port. - ASSIGN_OR_RETURN(std::string vport_ref, - ixia::IxiaVport(topology_ref, ixia_port.card, ixia_port.port, - generic_testbed)); - - // Start a session. - ASSIGN_OR_RETURN(std::string traffic_ref, - ixia::IxiaSession(vport_ref, generic_testbed)); - - // Set the frame rate. - RETURN_IF_ERROR(ixia::SetFrameRate(traffic_ref, frame_rate, generic_testbed)); - - // Set the frame count. - RETURN_IF_ERROR( - ixia::SetFrameCount(traffic_ref, frame_count, generic_testbed)); - - // Set the destination MAC address to LLDP multicast. - RETURN_IF_ERROR( - ixia::SetDestMac(traffic_ref, kLldpMulticastMac, generic_testbed)); - - // Set the source MAC address. - RETURN_IF_ERROR(ixia::SetSrcMac(traffic_ref, kSrcMac, generic_testbed)); - - // PATCH to /ixnetwork/traffic/trafficItem/1/configElement/1 - // with {"crc":"badCrc"} - const std::string badcrc_path = absl::StrCat(traffic_ref, "/configElement/1"); - const std::string badcrc_json = "{\"crc\":\"badCrc\"}"; - ASSIGN_OR_RETURN(thinkit::HttpResponse badcrc_response, - generic_testbed.SendRestRequestToIxia( - thinkit::RequestType::kPatch, badcrc_path, badcrc_json)); - EXPECT_EQ(badcrc_response.response_code, 200) << badcrc_response.response; - if (badcrc_response.response_code != 200) { - return absl::InternalError( - absl::StrCat("Failed to patch ", badcrc_path, " with ", badcrc_json, - ". Response: ", badcrc_response.response)); - } - - return IxiaSetUpResult{topology_ref, traffic_ref}; -} - -TEST_P(BlackholeCongestionCountersIxiaTestFixture, - TestInFcsErrorsAboveThreshIncrementBlackholeCounters) { - constexpr uint32_t kInFcsErrorsPerSecond = kInErrorsThresh + 5; - constexpr uint32_t kInFcsErrorsDurationSeconds = 10; - constexpr uint32_t kInFcsErrorsCount = - kInFcsErrorsPerSecond * kInFcsErrorsDurationSeconds; - - ASSERT_OK_AND_ASSIGN( - InErrorCounters in_fcs_error_counters_delta, - TriggerInFcsErrors(kInFcsErrorsPerSecond, kInFcsErrorsCount)); - - // Check the changes are as expected. - EXPECT_EQ(in_fcs_error_counters_delta.port_in_error_packets, - kInFcsErrorsCount); - EXPECT_GE(in_fcs_error_counters_delta.port_blackhole_in_error_events, 1); - EXPECT_GE(in_fcs_error_counters_delta.switch_blackhole_in_error_events, 1); - EXPECT_GE(in_fcs_error_counters_delta.switch_blackhole_events, 1); -} - -TEST_P(BlackholeCongestionCountersIxiaTestFixture, - TestInFcsErrorsBelowThreshNotIncrementBlackholeCounters) { - constexpr uint32_t kInFcsErrorsPerSecond = kInErrorsThresh - 5; - constexpr uint32_t kInFcsErrorsDurationSeconds = 10; - constexpr uint32_t kInFcsErrorsCount = - kInFcsErrorsPerSecond * kInFcsErrorsDurationSeconds; - - ASSERT_OK_AND_ASSIGN( - InErrorCounters in_fcs_error_counters_delta, - TriggerInFcsErrors(kInFcsErrorsPerSecond, kInFcsErrorsCount)); - - // Check the changes are as expected. - EXPECT_EQ(in_fcs_error_counters_delta.port_in_error_packets, - kInFcsErrorsCount); - EXPECT_EQ(in_fcs_error_counters_delta.port_blackhole_in_error_events, 0); - EXPECT_EQ(in_fcs_error_counters_delta.switch_blackhole_in_error_events, 0); - EXPECT_EQ(in_fcs_error_counters_delta.switch_blackhole_events, 0); -} - -constexpr absl::string_view kOutDiscardTestTrafficName = - "OutDiscardTestTraffic"; - -absl::StatusOr SetUpIxiaForOutDiscardTest( - absl::string_view ixia_tx_port, absl::string_view ixia_rx_port, - thinkit::GenericTestbed& generic_testbed, const int frame_size_in_bytes, - const int frame_rate, const int dscp) { - // Extract Ixia port info. - ASSIGN_OR_RETURN(ixia::IxiaPortInfo ixia_tx_port_info, - ixia::ExtractPortInfo(ixia_tx_port)); - ASSIGN_OR_RETURN(ixia::IxiaPortInfo ixia_rx_port_info, - ixia::ExtractPortInfo(ixia_rx_port)); - - // Connect to Ixia. - ASSIGN_OR_RETURN( - std::string topology_ref, - ixia::IxiaConnect(ixia_tx_port_info.hostname, generic_testbed)); - - // Get Ixia reference to Ixia ports. - ASSIGN_OR_RETURN(std::string tx_vport_ref, - ixia::IxiaVport(topology_ref, ixia_tx_port_info.card, - ixia_tx_port_info.port, generic_testbed)); - ASSIGN_OR_RETURN(std::string rx_vport_ref, - ixia::IxiaVport(topology_ref, ixia_rx_port_info.card, - ixia_rx_port_info.port, generic_testbed)); - - // Set up traffic items with source and destination ports. - ASSIGN_OR_RETURN( - std::string traffic_ref, - ixia::SetUpTrafficItem(tx_vport_ref, rx_vport_ref, - kOutDiscardTestTrafficName, generic_testbed)); - - // Set up traffic parameters. - ixia::TrafficParameters traffic_parameters = { - .frame_size_in_bytes = frame_size_in_bytes, - .traffic_speed = ixia::FramesPerSecond{frame_rate}, - }; - traffic_parameters.ip_parameters = ixia::Ipv4TrafficParameters{ - .priority = - ixia::IpPriority{ - .dscp = dscp, - .ecn = 0, - }, - }; - RETURN_IF_ERROR(ixia::SetTrafficParameters(traffic_ref, traffic_parameters, - generic_testbed)); - - return IxiaSetUpResult{ - .topology_ref = topology_ref, - .traffic_ref = traffic_ref, - }; -} - -absl::StatusOr GetQueuePir(absl::string_view port_name, - absl::string_view queue_name, - gnmi::gNMI::StubInterface& gnmi_stub) { - // Gets scheduler policy name for port. - const std::string kSchedulerPolicyNamePath = absl::StrFormat( - "qos/interfaces/interface[interface-id=%s]/output/scheduler-policy/" - "config/name", - port_name); - const std::string kSchedulerPolicyNameParseStr = "openconfig-qos:name"; - ASSIGN_OR_RETURN( - std::string port_scheduler_policy_name, - ReadGnmiPath(&gnmi_stub, kSchedulerPolicyNamePath, - gnmi::GetRequest::CONFIG, kSchedulerPolicyNameParseStr)); - port_scheduler_policy_name = - std::string(StripQuotes(port_scheduler_policy_name)); - - // Pulls scheduler policy config. - const std::string kSchedulerPolicyPath = - absl::StrFormat("qos/scheduler-policies/scheduler-policy[name=%s]", - port_scheduler_policy_name); - const std::string kSchedulerPolicyParseStr = - "openconfig-qos:scheduler-policy"; - ASSIGN_OR_RETURN( - const std::string scheduler_policy_raw_config, - ReadGnmiPath(&gnmi_stub, kSchedulerPolicyPath, gnmi::GetRequest::CONFIG, - kSchedulerPolicyParseStr)); - ASSIGN_OR_RETURN( - openconfig::Qos::SchedulerPolicy scheduler_policy_proto_config, - gutil::ParseJsonAsProto( - StripBrackets(scheduler_policy_raw_config), - /*ignore_unknown_fields=*/true)); - - // Looks for config for the queue. - for (openconfig::Qos::Scheduler& scheduler : - *scheduler_policy_proto_config.mutable_schedulers() - ->mutable_scheduler()) { - if (scheduler.inputs().input_size() != 1) continue; - const std::string kQueue = scheduler.inputs().input(0).config().queue(); - if (kQueue != queue_name) continue; - if (scheduler.config().type() != - "openconfig-qos-types:TWO_RATE_THREE_COLOR") { - continue; - } - openconfig::Qos::Scheduler::TwoRateThreeColor::Config& config = - *scheduler.mutable_two_rate_three_color()->mutable_config(); - int64_t pir; - if (!absl::SimpleAtoi(config.pir(), &pir)) { - return absl::InvalidArgumentError( - absl::StrCat("Invalid pir: ", config.pir())); - } - return pir; - } - - return absl::NotFoundError( - absl::StrCat("No scheduler found for queue: ", queue_name)); -} - -TEST_P(BlackholeCongestionCountersIxiaTestFixture, - TestCongestionsAboveThreshIncrementOutDiscardsAndCongestionCounters) { - constexpr double kOutDiscardsRate = kOutDiscardsRateThresh + 0.025; - constexpr absl::Duration kTrafficDuration = absl::Seconds(5); - constexpr double kOutDiscardsRateTolerance = 0.025; - - ASSERT_OK_AND_ASSIGN(OutDiscardCounters out_discard_counters, - TriggerOutDiscards(kOutDiscardsRate, kTrafficDuration)); - - // Check the changes are as expected. - double observed_out_discard_rate = - (double)out_discard_counters.port_out_discard_packets / - out_discard_counters.port_out_packets; - LOG(INFO) << "Observed out discard rate: " << observed_out_discard_rate; - EXPECT_NEAR(observed_out_discard_rate, kOutDiscardsRate, - kOutDiscardsRateTolerance); - EXPECT_GE(out_discard_counters.port_blackhole_out_discard_events, 1); - EXPECT_GE(out_discard_counters.switch_blackhole_out_discard_events, 1); - EXPECT_GE(out_discard_counters.switch_blackhole_events, 1); - EXPECT_GE(out_discard_counters.queue_dropped_packet_events, 1); - EXPECT_GE(out_discard_counters.switch_congestion_events, 1); -} - -TEST_P(BlackholeCongestionCountersIxiaTestFixture, - TestCongestionsBelowThreshNotIncrementOutDiscardsAndCongestionCounters) { - constexpr double kOutDiscardsRate = kOutDiscardsRateThresh - 0.025; - constexpr absl::Duration kTrafficDuration = absl::Seconds(5); - constexpr double kOutDiscardsRateTolerance = 0.025; - - ASSERT_OK_AND_ASSIGN(OutDiscardCounters out_discard_counters, - TriggerOutDiscards(kOutDiscardsRate, kTrafficDuration)); - - // Check the changes are as expected. - double observed_out_discard_rate = - (double)out_discard_counters.port_out_discard_packets / - out_discard_counters.port_out_packets; - LOG(INFO) << "Observed out discard rate: " << observed_out_discard_rate; - EXPECT_NEAR(observed_out_discard_rate, kOutDiscardsRate, - kOutDiscardsRateTolerance); - EXPECT_EQ(out_discard_counters.port_blackhole_out_discard_events, 0); - EXPECT_EQ(out_discard_counters.switch_blackhole_out_discard_events, 0); - EXPECT_EQ(out_discard_counters.switch_blackhole_events, 0); - EXPECT_EQ(out_discard_counters.queue_dropped_packet_events, 0); - EXPECT_EQ(out_discard_counters.switch_congestion_events, 0); -} - -} // namespace - -void BlackholeCongestionCountersIxiaTestFixture::SetUp() { - thinkit::GenericTestbedFixture<>::SetUp(); - // Pick a testbed with an Ixia Traffic Generator. A SUT is assumed. - thinkit::TestRequirements requirements = - gutil::ParseProtoOrDie( - R"pb(interface_requirements { - count: 2 - interface_mode: TRAFFIC_GENERATOR - })pb"); - - ASSERT_OK_AND_ASSIGN(generic_testbed_, - GetTestbedWithRequirements(requirements)); - - ASSERT_OK_AND_ASSIGN(std::string blackhole_congestion_config, - SetUpBlackholeCongestionConfig(GetParam().gnmi_config)); - ASSERT_OK(generic_testbed_->Environment().StoreTestArtifact( - "gnmi_config.txt", GetParam().gnmi_config)); - ASSERT_OK(generic_testbed_->Environment().StoreTestArtifact( - "blackhole_congestion_config.txt", blackhole_congestion_config)); - - // Hook up to gNMI. - ASSERT_OK_AND_ASSIGN(gnmi_stub_, generic_testbed_->Sut().CreateGnmiStub()); - - // Set up P4 Runtime session. - ASSERT_OK_AND_ASSIGN(sut_p4_session_, - ConfigureSwitchAndReturnP4RuntimeSession( - generic_testbed_->Sut(), blackhole_congestion_config, - GetParam().p4_info)); - - ASSERT_OK_AND_ASSIGN( - traffic_generator_links_, - GetUpLinks(GetAllTrafficGeneratorLinks, *generic_testbed_)); - ASSERT_FALSE(traffic_generator_links_.empty()) << "Ixia links are not ready"; -} - -void BlackholeCongestionCountersIxiaTestFixture::TearDown() { - // Restores the gNMI config and clears table entries. - ASSERT_OK(PushGnmiConfig(generic_testbed_->Sut(), GetParam().gnmi_config)); - ASSERT_OK(pdpi::ClearEntities(*sut_p4_session_)); - ASSERT_OK(sut_p4_session_->Finish()); - thinkit::GenericTestbedFixture<>::TearDown(); -} - -absl::StatusOr -BlackholeCongestionCountersIxiaTestFixture::TriggerInFcsErrors( - int frame_rate_per_second, int frame_count) { - const std::string ixia_interface = traffic_generator_links_[0].peer_interface; - const std::string sut_interface = traffic_generator_links_[0].sut_interface; - - ASSIGN_OR_RETURN( - IxiaSetUpResult ixia_setup_result, - SetUpIxiaForInFcsErrorTest(ixia_interface, *generic_testbed_, - frame_rate_per_second, frame_count)); - - // Read some initial counters via gMNI from the SUT. - ASSIGN_OR_RETURN( - const uint64_t initial_port_in_errors, - GetInterfaceCounter("in-errors", sut_interface, gnmi_stub_.get())); - ASSIGN_OR_RETURN( - const BlackholeSwitchCounters initial_blackhole_switch_counters, - GetBlackholeSwitchCounters(*gnmi_stub_)); - ASSIGN_OR_RETURN(const BlackholePortCounters initial_blackhole_port_counters, - GetBlackholePortCounters(sut_interface, *gnmi_stub_)); - - RETURN_IF_ERROR(ixia::StartTraffic(ixia_setup_result.traffic_ref, - ixia_setup_result.topology_ref, - *generic_testbed_)); - - absl::SleepFor(absl::Seconds(frame_count / frame_rate_per_second)); - - // Re-read the same counters via gMNI from the SUT. - ASSIGN_OR_RETURN( - const uint64_t final_port_in_errors, - GetInterfaceCounter("in-errors", sut_interface, gnmi_stub_.get())); - ASSIGN_OR_RETURN( - const BlackholeSwitchCounters final_blackhole_switch_counters, - GetBlackholeSwitchCounters(*gnmi_stub_)); - ASSIGN_OR_RETURN(const BlackholePortCounters final_blackhole_port_counters, - GetBlackholePortCounters(sut_interface, *gnmi_stub_)); - - // Compute the change for each counter. - const uint64_t port_in_errors_delta = - final_port_in_errors - initial_port_in_errors; - const BlackholeSwitchCounters blackhole_switch_delta = - final_blackhole_switch_counters - initial_blackhole_switch_counters; - const BlackholePortCounters blackhole_port_delta = - final_blackhole_port_counters - initial_blackhole_port_counters; - - return InErrorCounters{ - .port_in_error_packets = port_in_errors_delta, - .port_blackhole_in_error_events = blackhole_port_delta.in_error_events, - .switch_blackhole_in_error_events = - blackhole_switch_delta.in_error_events, - // Sometimes fec_not_correctable_events occur which the test can't - // control, so subtract those from the switch blackhole counter. - .switch_blackhole_events = - blackhole_switch_delta.blackhole_events - - blackhole_switch_delta.fec_not_correctable_events, - }; -} - -absl::StatusOr -BlackholeCongestionCountersIxiaTestFixture::TriggerOutDiscards( - const double out_discard_rate, const absl::Duration traffic_duration) { - // Use NC1 queue to control congestion drop rate. - constexpr int kNc1Dscp = 50; - constexpr absl::string_view kNc1QueueName = "NC1"; - - EXPECT_GE(traffic_generator_links_.size(), 2); - if (traffic_generator_links_.size() < 2) { - return absl::InternalError(absl::StrCat( - "Test requires at least 2 SUT ports connected to an Ixia")); - } - - const std::string ixia_tx_port = traffic_generator_links_[0].peer_interface; - const std::string sut_in_port = traffic_generator_links_[0].sut_interface; - - const std::string ixia_rx_port = traffic_generator_links_[1].peer_interface; - const std::string sut_out_port = traffic_generator_links_[1].sut_interface; - - // Look up the port IDs used by P4RT for the SUT interfaces. - absl::flat_hash_map port_id_by_interface; - ASSIGN_OR_RETURN(port_id_by_interface, - GetAllInterfaceNameToPortId(*gnmi_stub_)); - ASSIGN_OR_RETURN(const std::string sut_in_port_id, - gutil::FindOrStatus(port_id_by_interface, sut_in_port)); - ASSIGN_OR_RETURN(const std::string sut_out_port_id, - gutil::FindOrStatus(port_id_by_interface, sut_out_port)); - - constexpr int kDefaultFrameSizeinBytes = 1514; - constexpr int kBytesToBits = 8; - - // Get egress port NC1 queue speed in bits per second. - ASSIGN_OR_RETURN(const int64_t out_queue_pir, - GetQueuePir(sut_out_port, kNc1QueueName, *gnmi_stub_)); - LOG(INFO) << "SUT out port: " << sut_out_port << " Queue: " << kNc1QueueName; - LOG(INFO) << "Egress queue pir (bits/second): " << out_queue_pir; - - const double frame_rate_at_line_speed_of_out_queue = - (double)out_queue_pir / (kDefaultFrameSizeinBytes * kBytesToBits); - // Set the traffic frame rate to be above the out discards rate threshold. - const int64_t traffic_frame_rate = - ceil(frame_rate_at_line_speed_of_out_queue * (1 + out_discard_rate)); - LOG(INFO) << "Traffic rate (bits/second): " - << traffic_frame_rate * kDefaultFrameSizeinBytes * kBytesToBits; - - // Set up Ixia. - ASSIGN_OR_RETURN(IxiaSetUpResult ixia_setup_result, - SetUpIxiaForOutDiscardTest( - ixia_tx_port, ixia_rx_port, *generic_testbed_, - kDefaultFrameSizeinBytes, traffic_frame_rate, kNc1Dscp)); - - // Clear entries and install entries forwarding all packets to egress port. - RETURN_IF_ERROR(pdpi::ClearEntities(*sut_p4_session_)); - RETURN_IF_ERROR(sai::EntryBuilder() - .AddEntriesForwardingIpPacketsToGivenPort( - sut_out_port_id, sai::IpVersion::kIpv4And6) - .LogPdEntries() - .InstallDedupedEntities(*sut_p4_session_)); - - // Read some initial counters via GNMI from the SUT. - ASSIGN_OR_RETURN( - const uint64_t initial_port_out_pkts, - GetInterfaceCounter("out-pkts", sut_out_port, gnmi_stub_.get())); - ASSIGN_OR_RETURN( - const uint64_t initial_port_out_discards, - GetInterfaceCounter("out-discards", sut_out_port, gnmi_stub_.get())); - ASSIGN_OR_RETURN(BlackholeSwitchCounters initial_blackhole_switch_counters, - GetBlackholeSwitchCounters(*gnmi_stub_)); - ASSIGN_OR_RETURN(BlackholePortCounters initial_blackhole_port_counters, - GetBlackholePortCounters(sut_out_port, *gnmi_stub_)); - ASSIGN_OR_RETURN( - const uint64_t initial_queue_dropped_packet_events, - GetCongestionQueueCounter(sut_out_port, kNc1QueueName, *gnmi_stub_)); - ASSIGN_OR_RETURN(const uint64_t initial_switch_congestion_events, - GetCongestionSwitchCounter(*gnmi_stub_)); - - RETURN_IF_ERROR(ixia::StartTraffic(ixia_setup_result.traffic_ref, - ixia_setup_result.topology_ref, - *generic_testbed_)); - - absl::SleepFor(traffic_duration); - - RETURN_IF_ERROR( - ixia::StopTraffic(ixia_setup_result.traffic_ref, *generic_testbed_)); - - ASSIGN_OR_RETURN( - const ixia::TrafficItemStats ixia_traffic_stats, - ixia::GetTrafficItemStats(ixia_setup_result.topology_ref, - kOutDiscardTestTrafficName, *generic_testbed_)); - const int64_t observed_traffic_rate = - ixia::BytesPerSecondReceived(ixia_traffic_stats); - LOG(INFO) << "Observed traffic rate (bits/second): " - << observed_traffic_rate * kBytesToBits; - - // Re-read the same counters via GNMI from the SUT. - ASSIGN_OR_RETURN( - const uint64_t final_port_out_pkts, - GetInterfaceCounter("out-pkts", sut_out_port, gnmi_stub_.get())); - ASSIGN_OR_RETURN( - const uint64_t final_port_out_discards, - GetInterfaceCounter("out-discards", sut_out_port, gnmi_stub_.get())); - ASSIGN_OR_RETURN(BlackholeSwitchCounters final_blackhole_switch_counters, - GetBlackholeSwitchCounters(*gnmi_stub_)); - ASSIGN_OR_RETURN(BlackholePortCounters final_blackhole_port_counters, - GetBlackholePortCounters(sut_out_port, *gnmi_stub_)); - ASSIGN_OR_RETURN( - const uint64_t final_queue_dropped_packet_events, - GetCongestionQueueCounter(sut_out_port, kNc1QueueName, *gnmi_stub_)); - ASSIGN_OR_RETURN(const uint64_t final_switch_congestion_events, - GetCongestionSwitchCounter(*gnmi_stub_)); - - // Compute the change for each counter. - const uint64_t port_out_pkts_delta = - final_port_out_pkts - initial_port_out_pkts; - const uint64_t port_out_discards_delta = - final_port_out_discards - initial_port_out_discards; - BlackholeSwitchCounters blackhole_switch_delta = - final_blackhole_switch_counters - initial_blackhole_switch_counters; - BlackholePortCounters blackhole_port_delta = - final_blackhole_port_counters - initial_blackhole_port_counters; - const uint64_t queue_dropped_packet_events_delta = - final_queue_dropped_packet_events - initial_queue_dropped_packet_events; - const uint64_t switch_congestion_events_delta = - final_switch_congestion_events - initial_switch_congestion_events; - - return OutDiscardCounters{ - .port_out_packets = port_out_pkts_delta, - .port_out_discard_packets = port_out_discards_delta, - .port_blackhole_out_discard_events = - blackhole_port_delta.out_discard_events, - .switch_blackhole_out_discard_events = - blackhole_switch_delta.out_discard_events, - // Sometimes fec_not_correctable_events occur which the test can't - // control, so subtract those from the switch blackhole counter. - .switch_blackhole_events = - blackhole_switch_delta.blackhole_events - - blackhole_switch_delta.fec_not_correctable_events, - .queue_dropped_packet_events = queue_dropped_packet_events_delta, - .switch_congestion_events = switch_congestion_events_delta, - }; -} - -} // namespace pins_test diff --git a/tests/gnmi/blackhole_congestion_counters_ixia_test.h b/tests/gnmi/blackhole_congestion_counters_ixia_test.h deleted file mode 100644 index 4c4841eb9..000000000 --- a/tests/gnmi/blackhole_congestion_counters_ixia_test.h +++ /dev/null @@ -1,66 +0,0 @@ -// Copyright 2025 Google LLC -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -#ifndef PINS_TEST_GNMI_BLACKHOLE_CONGESTION_COUNTERS_IXIA_TEST_H_ -#define PINS_TEST_GNMI_BLACKHOLE_CONGESTION_COUNTERS_IXIA_TEST_H_ - -#include -#include -#include - -#include "absl/status/statusor.h" -#include "absl/time/time.h" -#include "lib/utils/generic_testbed_utils.h" -#include "p4_pdpi/p4_runtime_session.h" -#include "proto/gnmi/gnmi.grpc.pb.h" -#include "thinkit/generic_testbed.h" -#include "thinkit/generic_testbed_fixture.h" - -namespace pins_test { - -struct InErrorCounters { - uint64_t port_in_error_packets; - uint64_t port_blackhole_in_error_events; - uint64_t switch_blackhole_in_error_events; - uint64_t switch_blackhole_events; -}; - -struct OutDiscardCounters { - uint64_t port_out_packets; - uint64_t port_out_discard_packets; - uint64_t port_blackhole_out_discard_events; - uint64_t switch_blackhole_out_discard_events; - uint64_t switch_blackhole_events; - uint64_t queue_dropped_packet_events; - uint64_t switch_congestion_events; -}; - -class BlackholeCongestionCountersIxiaTestFixture - : public thinkit::GenericTestbedFixture<> { - protected: - // Acquires testbed with 3 pairs of connected ports between SUT and Ixia. - void SetUp() override; - void TearDown() override; - absl::StatusOr TriggerInFcsErrors(int frame_rate_per_second, - int frame_count); - absl::StatusOr TriggerOutDiscards( - double out_discard_rate, absl::Duration traffic_duration); - std::unique_ptr generic_testbed_; - std::unique_ptr gnmi_stub_; - std::vector traffic_generator_links_; - std::unique_ptr sut_p4_session_; -}; - -} // namespace pins_test - -#endif // PINS_TEST_GNMI_BLACKHOLE_CONGESTION_COUNTERS_IXIA_TEST_H_ diff --git a/tests/gnmi/blackhole_congestion_counters_without_ixia_test.cc b/tests/gnmi/blackhole_congestion_counters_without_ixia_test.cc deleted file mode 100644 index fc702273a..000000000 --- a/tests/gnmi/blackhole_congestion_counters_without_ixia_test.cc +++ /dev/null @@ -1,580 +0,0 @@ -// Copyright 2025 Google LLC -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#include "tests/gnmi/blackhole_congestion_counters_without_ixia_test.h" - -#include -#include -#include -#include - -#include "absl/container/flat_hash_map.h" -#include "absl/log/log.h" -#include "absl/status/status.h" -#include "absl/strings/string_view.h" -#include "absl/strings/substitute.h" -#include "absl/time/clock.h" -#include "absl/time/time.h" -#include "absl/types/optional.h" -#include "gmock/gmock.h" -#include "gtest/gtest.h" -#include "gutil/gutil/collections.h" -#include "gutil/gutil/status.h" -#include "gutil/gutil/status_matchers.h" -#include "gutil/gutil/testing.h" -#include "include/nlohmann/json.hpp" -#include "lib/gnmi/gnmi_helper.h" -#include "lib/utils/generic_testbed_utils.h" -#include "lib/utils/json_utils.h" -#include "p4_pdpi/netaddr/ipv4_address.h" -#include "p4_pdpi/netaddr/ipv6_address.h" -#include "p4_pdpi/p4_runtime_session.h" -#include "p4_pdpi/packetlib/packetlib.h" -#include "p4_pdpi/packetlib/packetlib.pb.h" -#include "sai_p4/instantiations/google/sai_pd.pb.h" -#include "sai_p4/instantiations/google/test_tools/test_entries.h" -#include "tests/lib/switch_test_setup_helpers.h" -#include "thinkit/control_device.h" -#include "thinkit/generic_testbed.h" -#include "thinkit/generic_testbed_fixture.h" -#include "thinkit/proto/generic_testbed.pb.h" -#include "thinkit/switch.h" - -namespace pins_test { -namespace { - -using ::nlohmann::json; - -const sai::Ipv4Lpm kIpv4Lpm = {.dst_ip = netaddr::Ipv4Address(10, 10, 20, 0), - .prefix_len = 24}; -const sai::Ipv6Lpm kIpv6Lpm = {.dst_ip = netaddr::Ipv6Address(0xF105, 0x102), - .prefix_len = 64}; -const sai::IpForwardingParams kIpForwardingParams = {.ipv4_lpm = kIpv4Lpm, - .ipv6_lpm = kIpv6Lpm}; -constexpr absl::string_view kIpv4DstIpForL3Hit = "10.10.20.10"; -constexpr absl::string_view kIpv4DstIpForL3Miss = "10.10.30.10"; -constexpr absl::string_view kIpv6DstIpForL3Hit = "F105:0102::2356"; -constexpr absl::string_view kIpv6DstIpForL3Miss = "F205:102::9845"; - -constexpr int kDecimalToMilliPercent = 1e5; -constexpr int kLpmMissesThresh = 10; -constexpr double kInDiscardsRateThresh = 0.05; -constexpr double kOutDiscardsRateThresh = 0.05; -constexpr uint64_t kInDiscardsRateThreshMilliPercent = - kInDiscardsRateThresh * kDecimalToMilliPercent; -constexpr uint64_t kOutDiscardsRateThreshMilliPercent = - kOutDiscardsRateThresh * kDecimalToMilliPercent; -constexpr int kMinInPacketsThresh = 100; -constexpr int kMinOutPacketsThresh = 100; -constexpr int kEccSingleBitErrorsThresh = 0; -constexpr int kRecoveredParityErrorsThresh = 0; - -const char kOpenConfigSystemParseKey[] = "openconfig-system:system"; -const char kPinsDiagParseKey[] = "diag:diag"; - -// Takes a `gnmi_config` from the config generator and returns a modified -// version that sets up blackhole and congestion related configs and thresholds -// for the testbed. If the testbed does not support blackhole and congestion -// monitoring, returns an error status. -absl::StatusOr SetUpBlackholeCongestionConfig( - absl::string_view gnmi_config) { - ASSIGN_OR_RETURN(json gnmi_config_json, json_yang::ParseJson(gnmi_config)); - - // Gets the switch-level and port-level configs. - json& switch_config = - gnmi_config_json[kOpenConfigSystemParseKey][kPinsDiagParseKey]["config"]; - json& port_profiles = - gnmi_config_json[kOpenConfigSystemParseKey][kPinsDiagParseKey] - ["port-profiles"]["port-profile"]; - - if (switch_config.empty() || port_profiles.empty()) { - return absl::NotFoundError( - "Blackhole and congestion monitoring not supported on testbed: Failed " - "to get switch-level or port-level configs."); - } - - // Updates switch-level configs. - switch_config["blackhole-monitoring-enable"] = true; - switch_config["congestion-monitoring-enable"] = true; - switch_config["ecc-single-bit-errors-threshold"] = kEccSingleBitErrorsThresh; - switch_config["recovered-parity-errors-threshold"] = - kRecoveredParityErrorsThresh; - switch_config["lpm-misses-threshold"] = kLpmMissesThresh; - - // Updates port-level configs. - for (json& port_profile : port_profiles) { - port_profile["config"]["in-discards-rate-threshold"] = - kInDiscardsRateThreshMilliPercent; - port_profile["config"]["out-discards-rate-threshold"] = - kOutDiscardsRateThreshMilliPercent; - port_profile["config"]["min-incoming-pkt-threshold"] = kMinInPacketsThresh; - port_profile["config"]["min-outgoing-pkt-threshold"] = kMinOutPacketsThresh; - } - - return json_yang::DumpJson(gnmi_config_json); -} - -// Packet proto messages sent from control switch to SUT. -constexpr absl::string_view kIpv4TestPacket = R"pb( - headers { - ethernet_header { - ethernet_destination: "00:1A:11:17:5F:80" - ethernet_source: "00:01:02:03:04:05" - ethertype: "0x0800" - } - } - headers { - ipv4_header { - version: "0x4" - ihl: "0x5" - dscp: "0x03" - ecn: "0x0" - identification: "0x0000" - flags: "0x0" - fragment_offset: "0x0000" - ttl: "0x20" - protocol: "0x11" - ipv4_source: "1.2.3.4" - ipv4_destination: "$0" - } - } - headers { udp_header { source_port: "0x0000" destination_port: "0x0000" } } - payload: "Basic IPv4 test packet")pb"; - -constexpr absl::string_view kIpv6TestPacket = R"pb( - headers { - ethernet_header { - ethernet_destination: "00:1A:11:17:5F:80" - ethernet_source: "00:01:02:03:04:05" - ethertype: "0x86dd" - } - } - headers { - ipv6_header { - dscp: "0x03" - ecn: "0x0" - flow_label: "0x00000" - next_header: "0xfd" # Reserved for experimentation. - hop_limit: "0x40" - ipv6_source: "2001:db8:0:12::1" - ipv6_destination: "$0" - } - } - payload: "Basic IPv6 test packet")pb"; - -absl::StatusOr MakeTestPacket(sai::IpVersion ip_version, - absl::string_view dst_ip) { - packetlib::Packet test_packet; - if (ip_version == sai::IpVersion::kIpv4) { - test_packet = gutil::ParseProtoOrDie( - absl::Substitute(kIpv4TestPacket, dst_ip)); - } else { - test_packet = gutil::ParseProtoOrDie( - absl::Substitute(kIpv6TestPacket, dst_ip)); - } - LOG(INFO) << "Test packet to send: " << test_packet.DebugString(); - return packetlib::SerializePacket(test_packet); -} - -// Send packets from control switch to SUT. -absl::Status SendPackets(thinkit::ControlDevice& control_device, - absl::string_view control_port, - sai::IpVersion ip_version, absl::string_view dst_ip, - uint32_t packets_count) { - // Make test packet. - ASSIGN_OR_RETURN(std::string test_packet, MakeTestPacket(ip_version, dst_ip)); - - // Send packet to SUT. - for (uint32_t i = 0; i < packets_count; ++i) { - RETURN_IF_ERROR(control_device.SendPacket(control_port, test_packet)) - << "failed to inject the packet."; - } - LOG(INFO) << "Successfully sent " << packets_count << " packets."; - return absl::OkStatus(); -} - -TEST_P(BlackholeCongestionCountersWithoutIxiaTestFixture, - TestIpv4LpmMissesAboveThreshIncrementBlackholeLpmMissCounters) { - constexpr uint32_t kLpmMissesCount = kLpmMissesThresh + 5; - - ASSERT_OK_AND_ASSIGN(LpmMissCounters lpm_miss_counters, - TriggerLpmMisses(sai::IpVersion::kIpv4, kLpmMissesCount, - /*lpm_hit_packets_count=*/0)); - - // Check the changes are as expected. - EXPECT_EQ(lpm_miss_counters.port_in_packets, kLpmMissesCount); - EXPECT_GE(lpm_miss_counters.switch_blackhole_lpm_miss_events, 1); - EXPECT_GE(lpm_miss_counters.switch_blackhole_events, 1); -} - -TEST_P(BlackholeCongestionCountersWithoutIxiaTestFixture, - TestIpv6LpmMissesAboveThreshIncrementBlackholeLpmMissCounters) { - constexpr uint32_t kLpmMissesCount = kLpmMissesThresh + 5; - - ASSERT_OK_AND_ASSIGN(LpmMissCounters lpm_miss_counters, - TriggerLpmMisses(sai::IpVersion::kIpv6, kLpmMissesCount, - /*lpm_hit_packets_count=*/0)); - - // Check the changes are as expected. - EXPECT_EQ(lpm_miss_counters.port_in_packets, kLpmMissesCount); - EXPECT_GE(lpm_miss_counters.switch_blackhole_lpm_miss_events, 1); - EXPECT_GE(lpm_miss_counters.switch_blackhole_events, 1); -} - -TEST_P(BlackholeCongestionCountersWithoutIxiaTestFixture, - TestIpv4LpmMissesBelowThreshNotIncrementBlackholeLpmMissCounters) { - constexpr uint32_t kLpmMissesCount = kLpmMissesThresh - 5; - - ASSERT_OK_AND_ASSIGN(LpmMissCounters lpm_miss_counters, - TriggerLpmMisses(sai::IpVersion::kIpv4, kLpmMissesCount, - /*lpm_hit_packets_count=*/0)); - - // Check the changes are as expected. - EXPECT_EQ(lpm_miss_counters.port_in_packets, kLpmMissesCount); - EXPECT_EQ(lpm_miss_counters.switch_blackhole_lpm_miss_events, 0); - EXPECT_EQ(lpm_miss_counters.switch_blackhole_events, 0); -} - -TEST_P(BlackholeCongestionCountersWithoutIxiaTestFixture, - TestIpv6LpmMissesBelowThreshNotIncrementBlackholeLpmMissCounters) { - constexpr uint32_t kLpmMissesCount = kLpmMissesThresh - 5; - - ASSERT_OK_AND_ASSIGN(LpmMissCounters lpm_miss_counters, - TriggerLpmMisses(sai::IpVersion::kIpv6, kLpmMissesCount, - /*lpm_hit_packets_count=*/0)); - - // Check the changes are as expected. - EXPECT_EQ(lpm_miss_counters.port_in_packets, kLpmMissesCount); - EXPECT_EQ(lpm_miss_counters.switch_blackhole_lpm_miss_events, 0); - EXPECT_EQ(lpm_miss_counters.switch_blackhole_events, 0); -} - -TEST_P(BlackholeCongestionCountersWithoutIxiaTestFixture, - TestInDiscardsAboveThreshIncrementBlackholeInDiscardCounters) { - constexpr double kInDiscardsRate = kInDiscardsRateThresh + 0.03; - constexpr uint32_t kInPacketsCount = kMinInPacketsThresh + 50; - constexpr uint32_t kLpmMissesCount = kInPacketsCount * kInDiscardsRate; - constexpr uint32_t kLpmHitsCount = kInPacketsCount - kLpmMissesCount; - - // Use LPM misses to trigger in-discard events. - ASSERT_OK_AND_ASSIGN( - LpmMissCounters lpm_miss_counters, - TriggerLpmMisses(sai::IpVersion::kIpv4, kLpmMissesCount, kLpmHitsCount)); - - // Check the changes are as expected. - EXPECT_EQ(lpm_miss_counters.port_in_packets, kLpmMissesCount + kLpmHitsCount); - EXPECT_EQ(lpm_miss_counters.port_in_discards, kLpmMissesCount); - EXPECT_GE(lpm_miss_counters.switch_blackhole_in_discard_events, 1); - EXPECT_GE(lpm_miss_counters.switch_blackhole_events, 1); -} - -TEST_P(BlackholeCongestionCountersWithoutIxiaTestFixture, - TestInDiscardsBelowThreshNotIncrementBlackholeInDiscardCounters) { - constexpr double kInDiscardsRate = kInDiscardsRateThresh - 0.03; - constexpr uint32_t kInPacketsCount = kMinInPacketsThresh + 50; - constexpr uint32_t kLpmMissesCount = kInPacketsCount * kInDiscardsRate; - constexpr uint32_t kLpmHitsCount = kInPacketsCount - kLpmMissesCount; - - // Use LPM misses to trigger in-discard events. - ASSERT_OK_AND_ASSIGN( - LpmMissCounters lpm_miss_counters, - TriggerLpmMisses(sai::IpVersion::kIpv4, kLpmMissesCount, kLpmHitsCount)); - - // Check the changes are as expected. - EXPECT_EQ(lpm_miss_counters.port_in_packets, kLpmMissesCount + kLpmHitsCount); - EXPECT_EQ(lpm_miss_counters.port_in_discards, kLpmMissesCount); - EXPECT_EQ(lpm_miss_counters.switch_blackhole_in_discard_events, 0); - EXPECT_EQ(lpm_miss_counters.switch_blackhole_events, 0); -} - -TEST_P(BlackholeCongestionCountersWithoutIxiaTestFixture, - TestPacketCountBelowThreshNotIncrementBlackholeInDiscardCounters) { - constexpr double kInDiscardsRate = kInDiscardsRateThresh + 0.03; - constexpr uint32_t kInPacketsCount = kMinInPacketsThresh - 50; - constexpr uint32_t kLpmMissesCount = kInPacketsCount * kInDiscardsRate; - constexpr uint32_t kLpmHitsCount = kInPacketsCount - kLpmMissesCount; - - // Use LPM misses to trigger in-discard events. - ASSERT_OK_AND_ASSIGN( - LpmMissCounters lpm_miss_counters, - TriggerLpmMisses(sai::IpVersion::kIpv4, kLpmMissesCount, kLpmHitsCount)); - - // Check the changes are as expected. - EXPECT_EQ(lpm_miss_counters.port_in_packets, kLpmMissesCount + kLpmHitsCount); - EXPECT_EQ(lpm_miss_counters.port_in_discards, kLpmMissesCount); - EXPECT_EQ(lpm_miss_counters.switch_blackhole_in_discard_events, 0); - EXPECT_EQ(lpm_miss_counters.switch_blackhole_events, 0); -} - -TEST_P(BlackholeCongestionCountersWithoutIxiaTestFixture, - TestAclDropAboveThreshIncrementBlackholeOutDiscardsCounters) { - constexpr double kOutDiscardsRate = kOutDiscardsRateThresh + 0.03; - constexpr uint32_t kOutPacketsCount = kMinOutPacketsThresh + 50; - constexpr uint32_t kOutDiscardsCount = kOutPacketsCount * kOutDiscardsRate; - - ASSERT_OK_AND_ASSIGN(OutDiscardCounters out_discard_counters, - TriggerOutDiscards(kOutDiscardsCount, kOutPacketsCount)); - - // Check the changes are as expected. - EXPECT_GE(out_discard_counters.port_blackhole_out_discard_events, 1); - EXPECT_GE(out_discard_counters.switch_blackhole_out_discard_events, 1); - EXPECT_GE(out_discard_counters.switch_blackhole_events, 1); -} - -TEST_P(BlackholeCongestionCountersWithoutIxiaTestFixture, - TestAclDropBelowThreshNotIncrementOutDiscardsCounters) { - constexpr double kOutDiscardsRate = kOutDiscardsRateThresh - 0.03; - constexpr uint32_t kOutPacketsCount = kMinOutPacketsThresh + 50; - constexpr uint32_t kOutDiscardsCount = kOutPacketsCount * kOutDiscardsRate; - - ASSERT_OK_AND_ASSIGN(OutDiscardCounters out_discard_counters, - TriggerOutDiscards(kOutDiscardsCount, kOutPacketsCount)); - - // Check the changes are as expected. - EXPECT_EQ(out_discard_counters.port_blackhole_out_discard_events, 0); - EXPECT_EQ(out_discard_counters.switch_blackhole_out_discard_events, 0); - EXPECT_EQ(out_discard_counters.switch_blackhole_events, 0); -} - -TEST_P(BlackholeCongestionCountersWithoutIxiaTestFixture, - TestAclDropWithLowTrafficNotIncrementOutDiscardsCounters) { - constexpr uint32_t kOutPacketsCount = kMinOutPacketsThresh - 50; - constexpr uint32_t kOutDiscardsCount = kOutPacketsCount; - - ASSERT_OK_AND_ASSIGN(OutDiscardCounters out_discard_counters, - TriggerOutDiscards(kOutDiscardsCount, kOutPacketsCount)); - - // Check the changes are as expected. - EXPECT_EQ(out_discard_counters.port_blackhole_out_discard_events, 0); - EXPECT_EQ(out_discard_counters.switch_blackhole_out_discard_events, 0); - EXPECT_EQ(out_discard_counters.switch_blackhole_events, 0); -} - -} // namespace - -void BlackholeCongestionCountersWithoutIxiaTestFixture::SetUp() { - thinkit::GenericTestbedFixture< - BlackholeCongestionCountersWithoutIxiaTestFixtureParams>::SetUp(); - thinkit::TestRequirements requirements = - gutil::ParseProtoOrDie( - R"pb(interface_requirements { - count: 1 - interface_mode: CONTROL_INTERFACE - })pb"); - - ASSERT_OK_AND_ASSIGN(generic_testbed_, - GetTestbedWithRequirements(requirements)); - - ASSERT_OK_AND_ASSIGN(std::string blackhole_congestion_config, - SetUpBlackholeCongestionConfig(GetParam().gnmi_config)); - ASSERT_OK(generic_testbed_->Environment().StoreTestArtifact( - "gnmi_config.txt", GetParam().gnmi_config)); - ASSERT_OK(generic_testbed_->Environment().StoreTestArtifact( - "blackhole_congestion_config.txt", blackhole_congestion_config)); - - // Hook up to gNMI. - ASSERT_OK_AND_ASSIGN(gnmi_stub_, generic_testbed_->Sut().CreateGnmiStub()); - - // Set up P4 Runtime session. - ASSERT_OK_AND_ASSIGN(sut_p4_session_, - ConfigureSwitchAndReturnP4RuntimeSession( - generic_testbed_->Sut(), blackhole_congestion_config, - GetParam().p4_info)); - - ASSERT_OK_AND_ASSIGN(control_links_, - GetUpLinks(GetAllControlLinks, *generic_testbed_)); - ASSERT_FALSE(control_links_.empty()) - << "Need at least 1 SUT interface to test"; -} - -void BlackholeCongestionCountersWithoutIxiaTestFixture::TearDown() { - // Restores the gNMI config and clears table entries. - ASSERT_OK(PushGnmiConfig(generic_testbed_->Sut(), GetParam().gnmi_config)); - ASSERT_OK(pdpi::ClearEntities(*sut_p4_session_)); - ASSERT_OK(sut_p4_session_->Finish()); - thinkit::GenericTestbedFixture< - BlackholeCongestionCountersWithoutIxiaTestFixtureParams>::TearDown(); -} - -absl::StatusOr -BlackholeCongestionCountersWithoutIxiaTestFixture::TriggerLpmMisses( - sai::IpVersion ip_version, uint32_t lpm_miss_packets_count, - uint32_t lpm_hit_packets_count) { - const std::string control_interface = control_links_[0].peer_interface; - const std::string sut_interface = control_links_[0].sut_interface; - - absl::flat_hash_map port_id_by_interface; - ASSIGN_OR_RETURN(port_id_by_interface, - GetAllInterfaceNameToPortId(*gnmi_stub_)); - ASSIGN_OR_RETURN(const std::string sut_port_id, - gutil::FindOrStatus(port_id_by_interface, sut_interface)); - - thinkit::ControlDevice& control_device = generic_testbed_->ControlDevice(); - - RETURN_IF_ERROR(pdpi::ClearEntities(*sut_p4_session_)); - RETURN_IF_ERROR(sai::EntryBuilder() - .AddEntriesForwardingIpPacketsToGivenPort( - sut_port_id, kIpForwardingParams) - .LogPdEntries() - .InstallDedupedEntities(*sut_p4_session_)); - - // Read some initial counters via gNMI from the SUT. - ASSIGN_OR_RETURN( - const uint64_t initial_port_in_pkts, - GetInterfaceCounter("in-pkts", sut_interface, gnmi_stub_.get())); - ASSIGN_OR_RETURN( - const uint64_t initial_port_in_discards, - GetInterfaceCounter("in-discards", sut_interface, gnmi_stub_.get())); - ASSIGN_OR_RETURN( - const BlackholeSwitchCounters initial_blackhole_switch_counters, - GetBlackholeSwitchCounters(*gnmi_stub_)); - - LOG(INFO) << "Sending test packets on port: " << control_interface; - if (ip_version == sai::IpVersion::kIpv4) { - RETURN_IF_ERROR(SendPackets(control_device, control_interface, ip_version, - kIpv4DstIpForL3Miss, lpm_miss_packets_count)); - RETURN_IF_ERROR(SendPackets(control_device, control_interface, ip_version, - kIpv4DstIpForL3Hit, lpm_hit_packets_count)); - } else { - RETURN_IF_ERROR(SendPackets(control_device, control_interface, ip_version, - kIpv6DstIpForL3Miss, lpm_miss_packets_count)); - RETURN_IF_ERROR(SendPackets(control_device, control_interface, ip_version, - kIpv6DstIpForL3Hit, lpm_hit_packets_count)); - } - - // Wait some time before capturing the port stats. - absl::SleepFor(absl::Seconds(15)); - - // Re-read the same counters via GNMI from the SUT. - ASSIGN_OR_RETURN( - const uint64_t final_port_in_pkts, - GetInterfaceCounter("in-pkts", sut_interface, gnmi_stub_.get())); - ASSIGN_OR_RETURN( - const uint64_t final_port_in_discards, - GetInterfaceCounter("in-discards", sut_interface, gnmi_stub_.get())); - ASSIGN_OR_RETURN( - const BlackholeSwitchCounters final_blackhole_switch_counters, - GetBlackholeSwitchCounters(*gnmi_stub_)); - - // Compute the change for each counter. - const uint64_t port_in_pkts_delta = final_port_in_pkts - initial_port_in_pkts; - const uint64_t port_in_discards_delta = - final_port_in_discards - initial_port_in_discards; - const BlackholeSwitchCounters blackhole_switch_delta = - final_blackhole_switch_counters - initial_blackhole_switch_counters; - - return LpmMissCounters{ - .port_in_packets = port_in_pkts_delta, - .port_in_discards = port_in_discards_delta, - .switch_blackhole_lpm_miss_events = - blackhole_switch_delta.lpm_miss_events, - .switch_blackhole_in_discard_events = - blackhole_switch_delta.in_discard_events, - // Sometimes fec_not_correctable_events occur which the test can't - // control, so subtract those from the switch blackhole counter. - .switch_blackhole_events = - blackhole_switch_delta.blackhole_events - - blackhole_switch_delta.fec_not_correctable_events, - }; -} - -absl::StatusOr -BlackholeCongestionCountersWithoutIxiaTestFixture::TriggerOutDiscards( - uint32_t out_discards_count, uint32_t out_packets_count) { - const std::string control_interface = control_links_[0].peer_interface; - const std::string sut_interface = control_links_[0].sut_interface; - - LOG(INFO) << "sut_interface: " << sut_interface; - - absl::flat_hash_map port_id_by_interface; - ASSIGN_OR_RETURN(port_id_by_interface, - GetAllInterfaceNameToPortId(*gnmi_stub_)); - ASSIGN_OR_RETURN(const std::string sut_port_id, - gutil::FindOrStatus(port_id_by_interface, sut_interface)); - - thinkit::ControlDevice& control_device = generic_testbed_->ControlDevice(); - - // Clear entries and set up a route to forward all packets to the SUT port. - // Set up egress ACL to drop all IPv6 packets for testing out-discard - // counters. - RETURN_IF_ERROR(pdpi::ClearEntities(*sut_p4_session_)); - RETURN_IF_ERROR(sai::EntryBuilder() - .AddEntriesForwardingIpPacketsToGivenPort(sut_port_id) - .AddEgressAclDroppingIpPackets(sai::IpVersion::kIpv6) - .LogPdEntries() - .InstallDedupedEntities(*sut_p4_session_)); - - // Read some initial counters via GNMI from the SUT. - ASSIGN_OR_RETURN( - const uint64_t initial_port_out_pkts, - GetInterfaceCounter("out-pkts", sut_interface, gnmi_stub_.get())); - ASSIGN_OR_RETURN( - const uint64_t initial_port_out_discards, - GetInterfaceCounter("out-discards", sut_interface, gnmi_stub_.get())); - ASSIGN_OR_RETURN(BlackholeSwitchCounters initial_blackhole_switch_counters, - GetBlackholeSwitchCounters(*gnmi_stub_)); - ASSIGN_OR_RETURN(BlackholePortCounters initial_blackhole_port_counters, - GetBlackholePortCounters(sut_interface, *gnmi_stub_)); - - LOG(INFO) << "Sending test packets on port: " << control_interface; - // All IPv6 packets will be dropped by the egress ACL. - RETURN_IF_ERROR(SendPackets(control_device, control_interface, - sai::IpVersion::kIpv6, kIpv6DstIpForL3Hit, - out_discards_count)); - // All IPv4 packets will be forwarded to the SUT port. - RETURN_IF_ERROR(SendPackets(control_device, control_interface, - sai::IpVersion::kIpv4, kIpv4DstIpForL3Hit, - out_packets_count)); - - // Wait some time before capturing the port stats. - absl::SleepFor(absl::Seconds(15)); - - // Re-read the same counters via GNMI from the SUT. - ASSIGN_OR_RETURN( - const uint64_t final_port_out_pkts, - GetInterfaceCounter("out-pkts", sut_interface, gnmi_stub_.get())); - ASSIGN_OR_RETURN( - const uint64_t final_port_out_discards, - GetInterfaceCounter("out-discards", sut_interface, gnmi_stub_.get())); - ASSIGN_OR_RETURN(BlackholeSwitchCounters final_blackhole_switch_counters, - GetBlackholeSwitchCounters(*gnmi_stub_)); - ASSIGN_OR_RETURN(BlackholePortCounters final_blackhole_port_counters, - GetBlackholePortCounters(sut_interface, *gnmi_stub_)); - - // Compute the change for each counter. - const uint64_t port_out_pkts_delta = - final_port_out_pkts - initial_port_out_pkts; - const uint64_t port_out_discards_delta = - final_port_out_discards - initial_port_out_discards; - BlackholeSwitchCounters blackhole_switch_delta = - final_blackhole_switch_counters - initial_blackhole_switch_counters; - BlackholePortCounters blackhole_port_delta = - final_blackhole_port_counters - initial_blackhole_port_counters; - - return OutDiscardCounters{ - .port_out_packets = port_out_pkts_delta, - .port_out_discard_packets = port_out_discards_delta, - .port_blackhole_out_discard_events = - blackhole_port_delta.out_discard_events, - .switch_blackhole_out_discard_events = - blackhole_switch_delta.out_discard_events, - // Sometimes fec_not_correctable_events occur which the test can't - // control, so subtract those from the switch blackhole counter. - .switch_blackhole_events = - blackhole_switch_delta.blackhole_events - - blackhole_switch_delta.fec_not_correctable_events, - }; -} - -} // namespace pins_test diff --git a/tests/gnmi/blackhole_congestion_counters_without_ixia_test.h b/tests/gnmi/blackhole_congestion_counters_without_ixia_test.h deleted file mode 100644 index 18dfd7e45..000000000 --- a/tests/gnmi/blackhole_congestion_counters_without_ixia_test.h +++ /dev/null @@ -1,80 +0,0 @@ -// Copyright 2025 Google LLC -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -#ifndef PINS_TEST_GNMI_BLACKHOLE_CONGESTION_COUNTERS_WITHOUT_IXIA_TEST_H_ -#define PINS_TEST_GNMI_BLACKHOLE_CONGESTION_COUNTERS_WITHOUT_IXIA_TEST_H_ - -#include -#include -#include -#include - -#include "absl/memory/memory.h" -#include "absl/status/statusor.h" -#include "lib/gnmi/gnmi_helper.h" -#include "lib/utils/generic_testbed_utils.h" -#include "p4_pdpi/p4_runtime_session.h" -#include "proto/gnmi/gnmi.grpc.pb.h" -#include "sai_p4/instantiations/google/test_tools/test_entries.h" -#include "thinkit/generic_testbed.h" -#include "thinkit/generic_testbed_fixture.h" -#include "thinkit/ssh_client.h" - -namespace pins_test { - -struct LpmMissCounters { - uint64_t port_in_packets; - uint64_t port_in_discards; - uint64_t switch_blackhole_lpm_miss_events; - uint64_t switch_blackhole_in_discard_events; - uint64_t switch_blackhole_events; -}; - -struct OutDiscardCounters { - uint64_t port_out_packets; - uint64_t port_out_discard_packets; - uint64_t port_blackhole_out_discard_events; - uint64_t switch_blackhole_out_discard_events; - uint64_t switch_blackhole_events; -}; - -struct BlackholeCongestionCountersWithoutIxiaTestFixtureParams { - thinkit::GenericTestbedInterface* testbed_interface; - std::string gnmi_config; - p4::config::v1::P4Info p4_info; - thinkit::SSHClient* ssh_client; -}; - -class BlackholeCongestionCountersWithoutIxiaTestFixture - : public thinkit::GenericTestbedFixture< - BlackholeCongestionCountersWithoutIxiaTestFixtureParams> { - protected: - void SetUp() override; - void TearDown() override; - absl::StatusOr TriggerLpmMisses( - sai::IpVersion ip_version, uint32_t lpm_miss_packets_count, - uint32_t lpm_hit_packets_count); - absl::StatusOr TriggerOutDiscards( - uint32_t out_discards_count, uint32_t out_packets_count); - std::unique_ptr generic_testbed_; - std::unique_ptr gnmi_stub_; - std::vector control_links_; - std::unique_ptr sut_p4_session_; - // Takes ownership of the SSHClient parameter. - std::unique_ptr ssh_client_ = - absl::WrapUnique(this->GetParam().ssh_client); -}; - -} // namespace pins_test - -#endif // PINS_TEST_GNMI_BLACKHOLE_CONGESTION_COUNTERS_WITHOUT_IXIA_TEST_H_ diff --git a/tests/gnmi/util.cc b/tests/gnmi/util.cc index b86f18ed5..2a852fa47 100644 --- a/tests/gnmi/util.cc +++ b/tests/gnmi/util.cc @@ -128,20 +128,22 @@ absl::Status SetAdminStatus(gnmi::gNMI::StubInterface* gnmi_stub, absl::Status FlapLink(gnmi::gNMI::StubInterface &gnmi_stub, thinkit::GenericTestbed &generic_testbed, absl::string_view sut_interface, - const thinkit::InterfaceInfo &host_interface_info) { + const thinkit::InterfaceInfo& host_interface_info, + bool is_ixia_testbed) { // Check if both SUT and Peer interfaces are up. LOG(INFO) << "Validate SUT interface " << sut_interface << " state before bringing down the SUT interface"; RETURN_IF_ERROR( CheckSutInterfaceOperStatus(gnmi_stub, sut_interface, OperStatus::kUp)); - LOG(INFO) << "Validate Peer interface " - << host_interface_info.peer_interface_name - << " state before bringing down the SUT interface"; - RETURN_IF_ERROR(CheckControlDeviceInterfaceLinkState( - generic_testbed, host_interface_info.peer_interface_name, - /*is_link_up=*/true, host_interface_info.peer_device_index)); - + if (!is_ixia_testbed) { + LOG(INFO) << "Validate Peer interface " + << host_interface_info.peer_interface_name + << " state before bringing down the SUT interface"; + RETURN_IF_ERROR(CheckControlDeviceInterfaceLinkState( + generic_testbed, host_interface_info.peer_interface_name, + /*is_link_up=*/true, host_interface_info.peer_device_index)); + } // Sets admin-status Down through gNMI. LOG(INFO) << "Set SUT interface: " << sut_interface << " admin link state down."; @@ -158,6 +160,11 @@ absl::Status FlapLink(gnmi::gNMI::StubInterface &gnmi_stub, << " state after bringing up the SUT interface"; RETURN_IF_ERROR( CheckSutInterfaceOperStatus(gnmi_stub, sut_interface, OperStatus::kUp)); + + // Return early for IXIA testbed as we cannot flap/validate the link on + // the IXIA end. + if (is_ixia_testbed) return absl::OkStatus(); + LOG(INFO) << "Validate Peer interface: " << host_interface_info.peer_interface_name << " state after bringing up the SUT interface"; diff --git a/tests/gnmi/util.h b/tests/gnmi/util.h index 28e434f56..2066f85b2 100644 --- a/tests/gnmi/util.h +++ b/tests/gnmi/util.h @@ -16,7 +16,8 @@ absl::Status SetAdminStatus(gnmi::gNMI::StubInterface* gnmi_stub, absl::Status FlapLink(gnmi::gNMI::StubInterface &gnmi_stub, thinkit::GenericTestbed &generic_testbed, absl::string_view sut_interface, - const thinkit::InterfaceInfo &host_interface_info); + const thinkit::InterfaceInfo& host_interface_info, + bool is_ixia_testbed = false); } // namespace pins_test diff --git a/tests/integration/system/nsf/BUILD.bazel b/tests/integration/system/nsf/BUILD.bazel index 16b792930..f2d0a7bcf 100644 --- a/tests/integration/system/nsf/BUILD.bazel +++ b/tests/integration/system/nsf/BUILD.bazel @@ -65,6 +65,7 @@ cc_library( "//p4_pdpi:p4_runtime_session_extras", "//p4_pdpi:pd", "//p4_pdpi:sequencing", + "//sai_p4/instantiations/google:instantiations", "//sai_p4/instantiations/google:sai_pd_cc_proto", "//sai_p4/instantiations/google/test_tools:table_entry_generator", "//tests/integration/system/nsf/interfaces:component_validator", @@ -105,6 +106,8 @@ cc_library( "//gutil/gutil:proto", "//gutil/gutil:status_matchers", "//lib/gnmi:gnmi_helper", + "//lib/validator:validator_lib", + "//sai_p4/instantiations/google:instantiations", "//tests/integration/system/nsf/interfaces:component_validator", "//tests/integration/system/nsf/interfaces:flow_programmer", "//tests/integration/system/nsf/interfaces:image_config_params", @@ -112,6 +115,7 @@ cc_library( "//tests/integration/system/nsf/interfaces:test_params", "//tests/integration/system/nsf/interfaces:testbed", "//tests/integration/system/nsf/interfaces:traffic_helper", + "//thinkit:mirror_testbed", "//thinkit:ssh_client", "//thinkit:switch", "//thinkit:test_environment", @@ -238,6 +242,7 @@ cc_library( ":util", "//gutil/gutil:status_matchers", "//lib/gnmi:gnmi_helper", + "//lib/utils:constants", "//p4_pdpi:ir_cc_proto", "//sai_p4/instantiations/google:sai_pd_cc_proto", "//tests/integration/system/nsf/interfaces:component_validator", @@ -250,7 +255,6 @@ cc_library( "//thinkit:switch", "//thinkit:test_environment", "@com_google_absl//absl/log", - "@com_google_absl//absl/flags:flag", "@com_google_absl//absl/random", "@com_google_absl//absl/status", "@com_google_absl//absl/strings", diff --git a/tests/integration/system/nsf/component_validators/p4rt_validator.cc b/tests/integration/system/nsf/component_validators/p4rt_validator.cc index a17d95c3c..0505f1ef6 100644 --- a/tests/integration/system/nsf/component_validators/p4rt_validator.cc +++ b/tests/integration/system/nsf/component_validators/p4rt_validator.cc @@ -55,7 +55,7 @@ absl::Status VerifyAclRecarvingTelemetry(absl::string_view version, // Add release specific objects in the allow list. absl::flat_hash_set allowlist; - + std::string allowlist_str; for (const auto obj : allowlist) { absl::StrAppend(&allowlist_str, obj, ", "); diff --git a/tests/integration/system/nsf/interfaces/BUILD.bazel b/tests/integration/system/nsf/interfaces/BUILD.bazel index 360431c5f..e9b07bc74 100644 --- a/tests/integration/system/nsf/interfaces/BUILD.bazel +++ b/tests/integration/system/nsf/interfaces/BUILD.bazel @@ -97,6 +97,7 @@ cc_library( ":scenario", ":testbed", ":traffic_helper", + "//sai_p4/instantiations/google:instantiations", "//thinkit:ssh_client", ], ) diff --git a/tests/integration/system/nsf/interfaces/test_params.h b/tests/integration/system/nsf/interfaces/test_params.h index f10fcf80d..3b1ee1e42 100644 --- a/tests/integration/system/nsf/interfaces/test_params.h +++ b/tests/integration/system/nsf/interfaces/test_params.h @@ -20,6 +20,7 @@ #include #include +#include "sai_p4/instantiations/google/instantiations.h" #include "tests/integration/system/nsf/interfaces/component_validator.h" #include "tests/integration/system/nsf/interfaces/flow_programmer.h" #include "tests/integration/system/nsf/interfaces/image_config_params.h" @@ -46,6 +47,7 @@ struct NsfTestParams { std::function(NsfUpgradeScenario)> get_test_case_ids; bool enable_interface_validation_during_nsf = true; bool enable_dynamic_replay = false; + sai::Instantiation sut_instantiation = sai::Instantiation::kTor; }; } // namespace pins_test diff --git a/tests/integration/system/nsf/nsf_acl_flow_coverage_test.cc b/tests/integration/system/nsf/nsf_acl_flow_coverage_test.cc index 4dbe32ac8..6a7a05449 100644 --- a/tests/integration/system/nsf/nsf_acl_flow_coverage_test.cc +++ b/tests/integration/system/nsf/nsf_acl_flow_coverage_test.cc @@ -76,7 +76,8 @@ TEST_P(NsfAclFlowCoverageTestFixture, NsfAclFlowCoverageTest) { ASSERT_OK(flow_programmer_->ProgramFlows(image_config_param, testbed_, *ssh_client_)); LOG(INFO) << "Programming ACL flows"; - ASSERT_OK(ProgramAclFlows(sut, image_config_param.p4_info)); + ASSERT_OK(ProgramAclFlows(sut, image_config_param.p4_info, + GetParam().sut_instantiation)); LOG(INFO) << "Starting the traffic"; ASSERT_OK(traffic_helper_->StartTraffic(testbed_)); diff --git a/tests/integration/system/nsf/nsf_concurrent_config_push_flow_programming_test.cc b/tests/integration/system/nsf/nsf_concurrent_config_push_flow_programming_test.cc index 58b239d15..5a75799e8 100644 --- a/tests/integration/system/nsf/nsf_concurrent_config_push_flow_programming_test.cc +++ b/tests/integration/system/nsf/nsf_concurrent_config_push_flow_programming_test.cc @@ -20,10 +20,8 @@ #include #include "absl/log/log.h" -#include "absl/flags/flag.h" #include "absl/random/random.h" #include "absl/status/status.h" -#include "absl/strings/match.h" #include "absl/strings/str_cat.h" #include "absl/time/clock.h" #include "absl/time/time.h" @@ -34,6 +32,7 @@ #include "gutil/gutil/status.h" #include "gutil/gutil/status_matchers.h" #include "lib/gnmi/gnmi_helper.h" +#include "lib/utils/constants.h" #include "p4_pdpi/ir.pb.h" #include "sai_p4/instantiations/google/sai_pd.pb.h" #include "tests/integration/system/nsf/compare_p4flows.h" @@ -58,11 +57,7 @@ constexpr int kIsolatedLacpSystemPriority = 512; constexpr int kFlowPrepDuration = 3; constexpr int kMinNsfDelayDuration = kFlowPrepDuration + 1; constexpr int kMaxNsfDelayDuration = kMinNsfDelayDuration + 10; -// TODO: Remove the below constants once hsflowd changes are -// rolled out to release. -constexpr int kMinNsfDelayReleaseDuration = 15; -constexpr int kMaxNsfDelayReleaseDuration = 25; -constexpr absl::Duration kTurnUpTimeout = absl::Minutes(6); + constexpr char kInterfaceToRemove[] = "Ethernet1/10/1"; constexpr int kMaxGnmiGetClients = 15; constexpr int kMaxGnmiSubscribeClients = 10; @@ -92,7 +87,7 @@ void NsfConcurrentConfigPushFlowProgrammingTestFixture::TearDown() { ASSERT_OK(PushConfig(GetParam().image_config_params[0], GetSut(testbed_), *ssh_client_)); ASSERT_OK(WaitForSwitchState(GetSut(testbed_), SwitchState::kReady, - kTurnUpTimeout, *ssh_client_, + GetColdRebootWaitForUpTime(), *ssh_client_, GetConnectedInterfacesForSut(testbed_))); } TearDownTestbed(testbed_interface_); @@ -144,18 +139,8 @@ TEST_P(NsfConcurrentConfigPushFlowProgrammingTestFixture, sut, environment)); absl::BitGen gen; - // TODO - Remove the below code once hsflowd changes are - // rolled out to release. - bool is_release_image = true; - - int nsf_delay_duration; - if (is_release_image) { - nsf_delay_duration = absl::uniform_int_distribution( - kMinNsfDelayReleaseDuration, kMaxNsfDelayReleaseDuration)(gen); - } else { - nsf_delay_duration = absl::uniform_int_distribution( - kMinNsfDelayDuration, kMaxNsfDelayDuration)(gen); - } + int nsf_delay_duration = absl::uniform_int_distribution( + kMinNsfDelayDuration, kMaxNsfDelayDuration)(gen); // Config Push thread. absl::Status config_push_status = absl::UnknownError("Yet to push config"); @@ -173,17 +158,7 @@ TEST_P(NsfConcurrentConfigPushFlowProgrammingTestFixture, // Flow Programming thread. absl::Status flow_programming_status = absl::UnknownError("Yet to program flows"); - auto flow_programming_func = [&sut, &image_config_param, - &is_release_image]() -> absl::Status { - // TODO - Remove the below code once hsflowd changes are - // rolled out to release. - if (is_release_image) { - // Flows used in the test takes ~6s to get programmed. - if (kMinNsfDelayDuration > 6) { - absl::SleepFor(absl::Seconds(kMinNsfDelayDuration - 6)); - } - } - + auto flow_programming_func = [&sut, &image_config_param]() -> absl::Status { LOG(INFO) << "Programming flows"; RETURN_IF_ERROR(ProgramFlowsBasedOnTable(sut, image_config_param.p4_info, "ipv6_table")); diff --git a/tests/integration/system/nsf/nsf_link_flap_test.cc b/tests/integration/system/nsf/nsf_link_flap_test.cc index 0f6a96574..5bef4a2af 100644 --- a/tests/integration/system/nsf/nsf_link_flap_test.cc +++ b/tests/integration/system/nsf/nsf_link_flap_test.cc @@ -52,7 +52,8 @@ absl::StatusOr FlapAllLinksAndReportDuration( gnmi::gNMI::StubInterface& gnmi_stub, thinkit::GenericTestbed& generic_testbed, absl::flat_hash_map - host_interface_infos) { + host_interface_infos, + bool is_ixia_testbed) { // Create a flat_hash_map to store the link flap results for all the links // where the key is the SUT link name and the value is the link flap // status. @@ -75,9 +76,11 @@ absl::StatusOr FlapAllLinksAndReportDuration( host_interface_infos) { // Flap all the links in parallel. std::thread thread([&gnmi_stub, &generic_testbed, &sut_interface, - &host_interface_info, &link_flap_status] { - link_flap_status[sut_interface] = FlapLink( - gnmi_stub, generic_testbed, sut_interface, host_interface_info); + &host_interface_info, &link_flap_status, + &is_ixia_testbed] { + link_flap_status[sut_interface] = + FlapLink(gnmi_stub, generic_testbed, sut_interface, + host_interface_info, is_ixia_testbed); }); link_threads.push_back(std::move(thread)); } @@ -120,11 +123,17 @@ absl::StatusOr FlapAllLinksAndReportDuration( TEST_P(NsfLinkFlapTestFixture, NsfLinkFlapTest) { LOG(INFO) << "Get testbed requirements."; thinkit::TestRequirements requirements = - gutil::ParseProtoOrDie( - R"pb(interface_requirements { - count: 1 - interface_mode: CONTROL_INTERFACE - })pb"); + GetParam().is_ixia_testbed + ? gutil::ParseProtoOrDie( + R"pb(interface_requirements { + count: 1 + interface_mode: TRAFFIC_GENERATOR + })pb") + : gutil::ParseProtoOrDie( + R"pb(interface_requirements { + count: 1 + interface_mode: CONTROL_INTERFACE + })pb"); ASSERT_OK_AND_ASSIGN(std::unique_ptr generic_testbed, GetTestbedWithRequirements(requirements)); @@ -145,12 +154,13 @@ TEST_P(NsfLinkFlapTestFixture, NsfLinkFlapTest) { << " Peer device index: " << info.peer_device_index; continue; } - if (info.interface_modes.contains(thinkit::CONTROL_INTERFACE)) { + if (info.interface_modes.contains(thinkit::CONTROL_INTERFACE) || + info.interface_modes.contains(thinkit::TRAFFIC_GENERATOR)) { host_interface_infos[interface] = info; } else { - LOG(WARNING) - << "Skipping the interface " - << interface << " as interface mode is not CONTROL_INTERFACE."; + LOG(WARNING) << "Skipping the interface " << interface + << " as interface mode is neither CONTROL_INTERFACE nor " + "TRAFFIC_GENERATOR."; } } if (host_interface_infos.empty()) { @@ -164,7 +174,8 @@ TEST_P(NsfLinkFlapTestFixture, NsfLinkFlapTest) { // Flap links and report the time taken to flap all the links. const absl::StatusOr pre_nsf_link_flap_time = FlapAllLinksAndReportDuration(*gnmi_stub, *generic_testbed, - host_interface_infos); + host_interface_infos, + GetParam().is_ixia_testbed); if (pre_nsf_link_flap_time.ok()) { LOG(INFO) << "Pre-NSF Link Flap Time: " << pre_nsf_link_flap_time.value(); EXPECT_LE(pre_nsf_link_flap_time.value(), kLinkFlapTimeout) @@ -178,11 +189,11 @@ TEST_P(NsfLinkFlapTestFixture, NsfLinkFlapTest) { ASSERT_OK(DoNsfRebootAndWaitForSwitchReadyOrRecover(generic_testbed.get(), *GetParam().ssh_client)); LOG(INFO) << "Flap links after NSF Reboot."; - // Flap links and report the time taken to flap all the links. const absl::StatusOr post_nsf_link_flap_time = FlapAllLinksAndReportDuration(*gnmi_stub, *generic_testbed, - host_interface_infos); + host_interface_infos, + GetParam().is_ixia_testbed); if (post_nsf_link_flap_time.ok()) { LOG(INFO) << "Post-NSF Link Flap Time: " << post_nsf_link_flap_time.value(); EXPECT_LE(post_nsf_link_flap_time.value(), kLinkFlapTimeout) diff --git a/tests/integration/system/nsf/nsf_link_flap_test.h b/tests/integration/system/nsf/nsf_link_flap_test.h index 5ece7b03e..50454bdf9 100644 --- a/tests/integration/system/nsf/nsf_link_flap_test.h +++ b/tests/integration/system/nsf/nsf_link_flap_test.h @@ -27,6 +27,7 @@ struct NsfLinkFlapTestParams { thinkit::GenericTestbedInterface* testbed_interface; std::shared_ptr ssh_client; std::vector image_config_params; + bool is_ixia_testbed; }; class NsfLinkFlapTestFixture diff --git a/tests/integration/system/nsf/upgrade_test.cc b/tests/integration/system/nsf/upgrade_test.cc index bd9a7e2c0..b66f8d638 100644 --- a/tests/integration/system/nsf/upgrade_test.cc +++ b/tests/integration/system/nsf/upgrade_test.cc @@ -36,8 +36,10 @@ #include "gutil/gutil/proto.h" #include "gutil/gutil/status_matchers.h" #include "lib/gnmi/gnmi_helper.h" +#include "lib/validator/validator_lib.h" #include "p4/config/v1/p4info.pb.h" #include "p4/v1/p4runtime.pb.h" +#include "sai_p4/instantiations/google/instantiations.h" #include "tests/integration/system/nsf/compare_p4flows.h" #include "tests/integration/system/nsf/interfaces/component_validator.h" #include "tests/integration/system/nsf/interfaces/flow_programmer.h" @@ -47,6 +49,7 @@ #include "tests/integration/system/nsf/interfaces/traffic_helper.h" #include "tests/integration/system/nsf/milestone.h" #include "tests/integration/system/nsf/util.h" +#include "thinkit/mirror_testbed.h" #include "thinkit/proto/generic_testbed.pb.h" #include "thinkit/switch.h" #include "thinkit/test_environment.h" @@ -364,7 +367,8 @@ absl::Status NsfUpgradeTest::NsfUpgradeOrReboot( LOG(INFO) << upgrade_path << ": Proceeding with config push after ACL flow program"; - status = ProgramAclFlows(GetSut(testbed_), curr_image_config.p4_info); + status = ProgramAclFlows(GetSut(testbed_), curr_image_config.p4_info, + GetParam().sut_instantiation); if (!status.ok()) { AppendErrorStatus(overall_status, absl::InternalError(absl::StrFormat( @@ -398,7 +402,8 @@ absl::Status NsfUpgradeTest::NsfUpgradeOrReboot( status.message()))); } - status = ProgramAclFlows(GetSut(testbed_), next_image_config.p4_info); + status = ProgramAclFlows(GetSut(testbed_), next_image_config.p4_info, + GetParam().sut_instantiation); if (!status.ok()) { AppendErrorStatus(overall_status, absl::InternalError(absl::StrFormat( diff --git a/tests/integration/system/nsf/util.cc b/tests/integration/system/nsf/util.cc index e5ce62d48..a44f5866d 100644 --- a/tests/integration/system/nsf/util.cc +++ b/tests/integration/system/nsf/util.cc @@ -42,6 +42,7 @@ #include "gutil/gutil/status.h" #include "gutil/gutil/testing.h" #include "lib/gnmi/gnmi_helper.h" +#include "lib/utils/constants.h" #include "lib/utils/generic_testbed_utils.h" #include "lib/validator/validator_lib.h" #include "p4/v1/p4runtime.pb.h" @@ -83,7 +84,6 @@ using ::p4::v1::ReadResponse; using ::p4::v1::Update; constexpr absl::Duration kPollingInterval = absl::Seconds(10); -constexpr absl::Duration kTurnUpTimeout = absl::Minutes(6); constexpr absl::Duration kTurnDownTimeout = absl::Minutes(2); constexpr int kEpochMarginalError = 2; constexpr std::array kAclFlows = { @@ -130,6 +130,40 @@ constexpr std::array kAclFlows = { } )pb"}; +constexpr std::array kAclFlowsForMiddleBlock = { + R"pb( + entries { + acl_ingress_table_entry { + match { ether_type { value: "0x88cc" mask: "0xffff" } } + action { acl_trap { qos_queue: "INBAND_PRIORITY_4" } } + priority: 2050 + } + } + entries { + acl_ingress_security_table_entry { + match { ether_type { value: "0x88cc" mask: "0xffff" } } + action { acl_deny {} } + priority: 4600 + } + } + )pb", + R"pb( + entries { + acl_ingress_table_entry { + match { ether_type { value: "0x0806" mask: "0xffff" } } + action { acl_trap { qos_queue: "INBAND_PRIORITY_3" } } + priority: 2031 + } + } + entries { + acl_ingress_security_table_entry { + match { ether_type { value: "0x0806" mask: "0xffff" } } + action { acl_deny {} } + priority: 4600 + } + } + )pb"}; + std::string GetSwitchStateString(SwitchState state) { switch (state) { case SwitchState::kUp: @@ -166,10 +200,13 @@ void AppendIgnoredP4SnapshotFields(MessageDifferencer* differencer) { differencer->set_repeated_field_comparison(MessageDifferencer::AS_SET); } -sai::TableEntries GetAclFlowEntries() { +sai::TableEntries GetAclFlowEntries(sai::Instantiation instantiation) { absl::BitGen gen; - int random_index = absl::Uniform(gen, 0, kAclFlows.size()); - return gutil::ParseProtoOrDie(kAclFlows[random_index]); + auto acl_flows = instantiation == sai::Instantiation::kMiddleblock + ? kAclFlowsForMiddleBlock + : kAclFlows; + int random_index = absl::Uniform(gen, 0, acl_flows.size()); + return gutil::ParseProtoOrDie(acl_flows[random_index]); } } // namespace @@ -585,8 +622,9 @@ absl::Status InstallRebootPushConfig( std::vector interfaces_to_check = GetConnectedInterfacesForSut(testbed); SwitchState intent_state = SwitchState::kReady; - RETURN_IF_ERROR(WaitForSwitchState(sut, intent_state, kTurnUpTimeout, - ssh_client, interfaces_to_check)); + RETURN_IF_ERROR(WaitForSwitchState(sut, intent_state, + GetColdRebootWaitForUpTime(), ssh_client, + interfaces_to_check)); LOG(INFO) << "Initial setup of image install, cold reboot and config push is " "complete."; @@ -595,7 +633,7 @@ absl::Status InstallRebootPushConfig( absl::Status ValidateTestbedState( const Testbed &testbed, thinkit::SSHClient &ssh_client, - absl::Nullable image_config_param, + const ImageConfigParams* image_config_param, bool check_interfaces_up, absl::Span sut_interfaces, absl::Span control_switch_interfaces) { // TODO: Add validation for SUT stack image label. @@ -654,7 +692,8 @@ absl::Status NsfReboot(const Testbed &testbed) { absl::Status WaitForReboot(const Testbed& testbed, thinkit::SSHClient& ssh_client, bool check_interfaces_up, - absl::Span interfaces) { + absl::Span interfaces, + absl::Duration timeout) { LOG(INFO) << "Waiting for switch to go down and come back up"; // Wait for switch to go down and come back up. thinkit::Switch& sut = GetSut(testbed); @@ -670,12 +709,12 @@ absl::Status WaitForReboot(const Testbed& testbed, check_interfaces_up ? SwitchState::kReady : SwitchState::kReadyWithoutInterfacesUp, - kTurnUpTimeout, ssh_client, interfaces); + timeout, ssh_client, interfaces); } absl::Status WaitForNsfReboot(const Testbed &testbed, thinkit::SSHClient &ssh_client, - absl::Nullable image_config_param, + const ImageConfigParams* image_config_param, bool check_interfaces_up, absl::Span sut_interfaces, bool collect_debug_logs_for_nsf_success, absl::Span control_switch_interfaces) { @@ -721,7 +760,7 @@ WaitForNsfReboot(const Testbed &testbed, thinkit::SSHClient &ssh_client, absl::Status DoNsfRebootAndWaitForSwitchReady( const Testbed &testbed, thinkit::SSHClient &ssh_client, - absl::Nullable image_config_param, + const ImageConfigParams* image_config_param, bool check_interfaces_up, absl::Span sut_interfaces, absl::Span control_switch_interfaces) { thinkit::Switch& sut = GetSut(testbed); @@ -747,7 +786,7 @@ absl::Status DoNsfRebootAndWaitForSwitchReady( absl::Status DoNsfRebootAndWaitForSwitchReadyOrRecover( const Testbed& testbed, thinkit::SSHClient& ssh_client, - absl::Nullable image_config_param, + const ImageConfigParams* image_config_param, bool check_interfaces_up, absl::Span sut_interfaces, absl::Span control_switch_interfaces) { thinkit::Switch& sut = GetSut(testbed); @@ -836,8 +875,9 @@ absl::Status PushConfig(const ImageConfigParams& image_config_param, } absl::Status ProgramAclFlows(thinkit::Switch& thinkit_switch, - const P4Info& p4_info) { - sai::TableEntries entries = GetAclFlowEntries(); + const P4Info& p4_info, + sai::Instantiation sut_instantiation) { + sai::TableEntries entries = GetAclFlowEntries(sut_instantiation); ASSIGN_OR_RETURN(std::unique_ptr sut_p4rt, pdpi::P4RuntimeSession::Create(thinkit_switch)); ASSIGN_OR_RETURN(pdpi::IrP4Info ir_p4info, pdpi::CreateIrP4Info(p4_info)); diff --git a/tests/integration/system/nsf/util.h b/tests/integration/system/nsf/util.h index c9cf9f688..07736533a 100644 --- a/tests/integration/system/nsf/util.h +++ b/tests/integration/system/nsf/util.h @@ -30,7 +30,9 @@ #include "absl/strings/string_view.h" #include "absl/time/time.h" #include "absl/types/span.h" +#include "lib/utils/constants.h" #include "proto/gnmi/gnmi.grpc.pb.h" +#include "sai_p4/instantiations/google/instantiations.h" #include "sai_p4/instantiations/google/sai_pd.pb.h" #include "tests/integration/system/nsf/interfaces/component_validator.h" #include "tests/integration/system/nsf/interfaces/image_config_params.h" @@ -172,7 +174,7 @@ absl::Status InstallRebootPushConfig( // `image_config_param` is provided. absl::Status ValidateTestbedState( const Testbed &testbed, thinkit::SSHClient &ssh_client, - absl::Nullable image_config_param = nullptr, + const ImageConfigParams* image_config_param = nullptr, bool check_interfaces_up = true, absl::Span sut_interfaces = {}, absl::Span control_switch_interfaces = {}); @@ -192,13 +194,14 @@ absl::Status NsfReboot(const Testbed &testbed); absl::Status WaitForReboot(const Testbed& testbed, thinkit::SSHClient& ssh_client, bool check_interfaces_up = true, - absl::Span interfaces = {}); + absl::Span interfaces = {}, + absl::Duration timeout = kColdRebootWaitForUpTime); // Waits for the SUT to warm reboot. If `check_interfaces_up` is `true`, it // additionally checks whether all the SUT interfaces are UP after turnup. absl::Status WaitForNsfReboot( const Testbed &testbed, thinkit::SSHClient &ssh_client, - absl::Nullable image_config_param = nullptr, + const ImageConfigParams* image_config_param = nullptr, bool check_interfaces_up = true, absl::Span sut_interfaces = {}, bool collect_debug_logs_for_nsf_success = true, @@ -207,7 +210,7 @@ absl::Status WaitForNsfReboot( // Performs NSF Reboot and waits for the SUT to be ready. absl::Status DoNsfRebootAndWaitForSwitchReady( const Testbed &testbed, thinkit::SSHClient &ssh_client, - absl::Nullable image_config_param = nullptr, + const ImageConfigParams* image_config_param = nullptr, bool check_interfaces_up = true, absl::Span sut_interfaces = {}, absl::Span control_switch_interfaces = {}); @@ -215,7 +218,7 @@ absl::Status DoNsfRebootAndWaitForSwitchReady( // NSF reboot failure, a cold reboot is executed on the switch to recover it. absl::Status DoNsfRebootAndWaitForSwitchReadyOrRecover( const Testbed& testbed, thinkit::SSHClient& ssh_client, - absl::Nullable image_config_param = nullptr, + const ImageConfigParams* image_config_param = nullptr, bool check_interfaces_up = true, absl::Span sut_interfaces = {}, absl::Span control_switch_interfaces = {}); @@ -238,7 +241,8 @@ absl::Status PushConfig(const ImageConfigParams& image_config_param, bool is_inband_testbed = false); absl::Status ProgramAclFlows(thinkit::Switch& thinkit_switch, - const p4::config::v1::P4Info& p4_info); + const p4::config::v1::P4Info& p4_info, + sai::Instantiation sut_instantiation); // Program flows based on table_name to the capacity. absl::Status ProgramFlowsBasedOnTable(thinkit::Switch& thinkit_switch, diff --git a/tests/lib/BUILD.bazel b/tests/lib/BUILD.bazel index eb3c89255..b0dc0080e 100644 --- a/tests/lib/BUILD.bazel +++ b/tests/lib/BUILD.bazel @@ -110,6 +110,7 @@ cc_library( "//gutil/gutil:collections", "//gutil/gutil:proto", "//gutil/gutil:status", + "//gutil/gutil:test_artifact_writer", "//lib/gnmi:gnmi_helper", "//lib/gnmi:openconfig_cc_proto", "//lib/p4rt:p4rt_port", @@ -132,7 +133,6 @@ cc_library( "@com_google_absl//absl/status:statusor", "@com_google_absl//absl/strings", "@com_google_absl//absl/time", - "@com_google_absl//absl/types:optional", "@com_google_absl//absl/types:span", "@com_google_absl//absl/flags:parse", "@com_google_googletest//:gtest", diff --git a/tests/lib/p4rt_fixed_table_programming_helper.cc b/tests/lib/p4rt_fixed_table_programming_helper.cc index adc7ae935..c138e7c57 100644 --- a/tests/lib/p4rt_fixed_table_programming_helper.cc +++ b/tests/lib/p4rt_fixed_table_programming_helper.cc @@ -166,6 +166,12 @@ absl::StatusOr RouterInterfaceTableUpdate( absl::string_view router_interface_id, absl::string_view port, absl::string_view src_mac) { pdpi::IrUpdate ir_update; + // Use unicast_set_port_and_src_mac if set_port_and_src_mac is not supported + // in the P4Info. + absl::string_view action_name = "set_port_and_src_mac"; + if (!ir_p4_info.actions_by_name().contains(action_name)) { + action_name = "unicast_set_port_and_src_mac"; + } RETURN_IF_ERROR(gutil::ReadProtoFromString( absl::Substitute(R"pb( type: $0 @@ -177,20 +183,20 @@ absl::StatusOr RouterInterfaceTableUpdate( exact { str: "$1" } } action { - name: "set_port_and_src_mac" + name: "$2" params { name: "port" - value { str: "$2" } + value { str: "$3" } } params { name: "src_mac" - value { mac: "$3" } + value { mac: "$4" } } } } } )pb", - type, router_interface_id, port, src_mac), + type, router_interface_id, action_name, port, src_mac), &ir_update)) << "invalid pdpi::IrUpdate string."; return pdpi::IrUpdateToPi(ir_p4_info, ir_update); diff --git a/tests/lib/packet_generator.cc b/tests/lib/packet_generator.cc index 25ea4d9ec..bcafe6704 100644 --- a/tests/lib/packet_generator.cc +++ b/tests/lib/packet_generator.cc @@ -58,14 +58,6 @@ int SkipValues(int value, const absl::btree_set& skip) { return value; } -const absl::btree_set& ReservedFlowLabelsLower16() { - static const auto* const kPorts = new absl::btree_set({ - 0x7103, - 0x7104, - }); - return *kPorts; -} - template Proto ParseTextProtoOrDie(absl::string_view text) { auto proto = gutil::ParseTextProto(text); @@ -377,8 +369,7 @@ void SetFieldValue(Field field, int value, packetlib::Packet& packet) { << Ipv6Header(packet).flow_label(); } flow_label = field == Field::kFlowLabelLower16 - ? (flow_label & ~0xffff) + - SkipValues(value, ReservedFlowLabelsLower16()) + ? (flow_label & ~0xffff) + value : (flow_label & 0xffff) + (value << 16); Ipv6Header(packet).set_flow_label(packetlib::IpFlowLabel(flow_label)); } break; diff --git a/tests/lib/switch_test_setup_helpers.cc b/tests/lib/switch_test_setup_helpers.cc index c9630dd08..9affd6a9e 100644 --- a/tests/lib/switch_test_setup_helpers.cc +++ b/tests/lib/switch_test_setup_helpers.cc @@ -30,6 +30,7 @@ #include "absl/status/status.h" #include "absl/status/statusor.h" #include "absl/strings/str_cat.h" +#include "absl/strings/str_join.h" #include "absl/strings/string_view.h" #include "absl/time/clock.h" #include "absl/time/time.h" @@ -41,6 +42,7 @@ #include "gutil/gutil/proto.h" #include "gutil/gutil/status.h" #include "google/protobuf/repeated_ptr_field.h" +#include "gutil/gutil/test_artifact_writer.h" #include "lib/gnmi/gnmi_helper.h" #include "lib/gnmi/openconfig.pb.h" #include "lib/p4rt/p4rt_port.h" @@ -83,7 +85,16 @@ absl::Status TryClearingEntities( return absl::OkStatus(); } - RETURN_IF_ERROR(pdpi::ClearEntities(**session)); + auto store_entities_upon_failure = + [&thinkit_switch](const std::vector& + entities_that_failed_to_be_deleted) { + gutil::BazelTestArtifactWriter artifact_writer; + return artifact_writer.StoreTestArtifact( + absl::StrCat("entities_that_failed_to_be_deleted_", + thinkit_switch.ChassisName(), ".txt"), + absl::StrJoin(entities_that_failed_to_be_deleted, "\n")); + }; + RETURN_IF_ERROR(pdpi::ClearEntities(**session, store_entities_upon_failure)); RETURN_IF_ERROR(session.value()->Finish()); return absl::OkStatus(); } @@ -326,6 +337,34 @@ absl::Status WaitForEnabledInterfacesToBeUp( /*with_healthz=*/false); } +absl::Status WaitForEnabledEthernetInterfacesToBeUp( + thinkit::Switch& thinkit_switch, absl::Duration timeout) { + absl::Time start = absl::Now(); + ASSIGN_OR_RETURN(std::unique_ptr gnmi_stub, + thinkit_switch.CreateGnmiStub()); + + // Get all enabled, Ethernet interfaces from the config. + ASSIGN_OR_RETURN( + const pins_test::openconfig::Interfaces enabled_ethernet_interfaces, + pins_test::GetMatchingInterfacesAsProto( + *gnmi_stub, gnmi::GetRequest::CONFIG, + /*predicate=*/pins_test::IsEnabledEthernetInterface, timeout)); + + std::vector enabled_ethernet_interface_names; + enabled_ethernet_interface_names.reserve( + enabled_ethernet_interfaces.interfaces_size()); + for (const auto& interface : enabled_ethernet_interfaces.interfaces()) { + enabled_ethernet_interface_names.push_back(interface.name()); + } + + // Wait for all enabled, Ethernet interfaces to be up. + timeout = timeout - (absl::Now() - start); + return pins_test::WaitForCondition(pins_test::PortsUp, timeout, + thinkit_switch, + enabled_ethernet_interface_names, + /*with_healthz=*/false); +} + // Gets every P4runtime port used in `entries`. absl::StatusOr> GetPortsUsed( const pdpi::IrP4Info& info, std::vector entries) { diff --git a/tests/lib/switch_test_setup_helpers.h b/tests/lib/switch_test_setup_helpers.h index efb624dbb..bad12795c 100644 --- a/tests/lib/switch_test_setup_helpers.h +++ b/tests/lib/switch_test_setup_helpers.h @@ -15,6 +15,7 @@ #ifndef PINS_TESTS_LIB_SWITCH_TEST_SETUP_HELPERS_H_ #define PINS_TESTS_LIB_SWITCH_TEST_SETUP_HELPERS_H_ +#include #include #include #include @@ -73,6 +74,11 @@ absl::Status WaitForEnabledInterfacesToBeUp( std::function> on_failure = std::nullopt); +// Reads the enabled ethernet interfaces from the switch and waits up to +// `timeout` until they are all up. +absl::Status WaitForEnabledEthernetInterfacesToBeUp( + thinkit::Switch& thinkit_switch, absl::Duration timeout = absl::Minutes(5)); + // Gets the set of P4 Runtime port IDs used in `entries`. absl::StatusOr> GetPortsUsed( const pdpi::IrP4Info& info, std::vector entries); diff --git a/tests/qos/BUILD.bazel b/tests/qos/BUILD.bazel index 4dfcb54a3..2b4532056 100644 --- a/tests/qos/BUILD.bazel +++ b/tests/qos/BUILD.bazel @@ -112,6 +112,7 @@ cc_library( "//lib:ixia_helper", "//lib/gnmi:gnmi_helper", "//lib/gnmi:openconfig_cc_proto", + "//lib/utils:generic_testbed_utils", "//p4_pdpi:ir", "//p4_pdpi:ir_cc_proto", "//p4_pdpi:p4_runtime_session", @@ -228,13 +229,13 @@ cc_library( "//tests/lib:switch_test_setup_helpers", "//thinkit:generic_testbed", "//thinkit:generic_testbed_fixture", - "//thinkit:mirror_testbed", "//thinkit:ssh_client", "//thinkit:switch", "@com_github_gnmi//proto/gnmi:gnmi_cc_grpc_proto", "@com_github_p4lang_p4runtime//:p4info_cc_proto", "@com_github_p4lang_p4runtime//:p4runtime_cc_proto", "@com_google_absl//absl/algorithm:container", + "@com_google_absl//absl/base:core_headers", "@com_google_absl//absl/cleanup", "@com_google_absl//absl/container:btree", "@com_google_absl//absl/container:flat_hash_map", @@ -242,8 +243,10 @@ cc_library( "@com_google_absl//absl/status", "@com_google_absl//absl/strings", "@com_google_absl//absl/strings:str_format", + "@com_google_absl//absl/synchronization", "@com_google_absl//absl/time", "@com_google_absl//absl/types:optional", + "@com_google_absl//absl/types:span", "@com_google_googletest//:gtest", ], alwayslink = True, diff --git a/tests/qos/cpu_qos_test.cc b/tests/qos/cpu_qos_test.cc index 168e118e7..c9dd1d950 100644 --- a/tests/qos/cpu_qos_test.cc +++ b/tests/qos/cpu_qos_test.cc @@ -115,6 +115,7 @@ const sai::NexthopRewriteOptions kNextHopRewriteOptions = { // Size of the "frame check sequence" (FCS) that is part of Layer 2 Ethernet // frames. constexpr int kFrameCheckSequenceSize = 4; +constexpr int kAcceptablePacketLossPercent = 20; absl::Status NsfRebootHelper(const Testbed &testbed, std::shared_ptr ssh_client) { @@ -1217,11 +1218,14 @@ TEST_P(CpuQosTestWithoutIxia, TrafficToSwitchInbandGetsMappedToCorrectQueues) { int expected_queue_counters_after_test_packets = TotalPacketsForQueue(queue_counters_before_test_packet) + kPacketCount; + int kAllowedPacketDrops = + (kPacketCount * kAcceptablePacketLossPercent) / (100); // We terminate early if this fails, as that can cause this loop to get // out of sync when counters increment after a long delay, resulting in // confusing error messages where counters increment by 2. EXPECT_THAT(TotalPacketsForQueue(queue_counters_after_test_packet), - AllOf(Ge(expected_queue_counters_after_test_packets), + AllOf(Ge(expected_queue_counters_after_test_packets - + kAllowedPacketDrops), Le(expected_queue_counters_after_test_packets + kMaxAllowedUnsolicitedPackets))) << "Counters for queue " << target_queue << " did not increment within " @@ -1230,11 +1234,20 @@ TEST_P(CpuQosTestWithoutIxia, TrafficToSwitchInbandGetsMappedToCorrectQueues) { "unsolicited packets:\n" << test_packet.packet.DebugString() << "\nBefore : " << queue_counters_before_test_packet - << "\nAfter : " << queue_counters_after_test_packet - << "\nExpected: " << expected_queue_counters_after_test_packets + << "\nAfter : " << queue_counters_after_test_packet << "\nExpected: " + << expected_queue_counters_after_test_packets - kAllowedPacketDrops << " to " << expected_queue_counters_after_test_packets + kMaxAllowedUnsolicitedPackets; + LOG(INFO) << " got: " + << TotalPacketsForQueue(queue_counters_after_test_packet) + << ", want: (" + << expected_queue_counters_after_test_packets - + kAllowedPacketDrops + << " - " + << expected_queue_counters_after_test_packets + + kMaxAllowedUnsolicitedPackets + << ")"; } LOG(INFO) << "-- END OF TEST -----------------------------------------------"; } @@ -1485,6 +1498,7 @@ TEST_P(CpuQosTestWithIxia, TestCPUQueueAssignmentAndQueueRateLimit) { std::string ixia_interface = ready_links[0].ixia_interface; std::string sut_interface = ready_links[0].sut_interface; + // Disable sFlow since it would interfere with the test results. ASSERT_OK(pins::SetSflowConfigEnabled(gnmi_stub.get(), /*enabled=*/false)); diff --git a/tests/qos/cpu_qos_test.h b/tests/qos/cpu_qos_test.h index 670f067b2..8415c3aff 100644 --- a/tests/qos/cpu_qos_test.h +++ b/tests/qos/cpu_qos_test.h @@ -22,7 +22,7 @@ #include #include - +#include "absl/strings/match.h" #include "absl/strings/string_view.h" #include "gtest/gtest.h" #include "gutil/gutil/status_matchers.h" @@ -44,6 +44,8 @@ struct PacketAndExpectedTargetQueue { packetlib::Packet packet; absl::string_view target_queue; }; +constexpr absl::string_view kNoUnexpectedPacketsPristineSwitchStateTest = + "NoUnexpectedPacketsReachCpuInPristineSwitchState"; // Parameters used by the tests that don't require an Ixia. struct ParamsForTestsWithoutIxia { @@ -75,6 +77,16 @@ class CpuQosTestWithoutIxia : public testing::TestWithParam { protected: void SetUp() override { + const testing::TestInfo* const test_info = + testing::UnitTest::GetInstance()->current_test_info(); + // TODO: Remove the link flap expectation once the bug is + // fixed. Expect link flaps only for the + // NoUnexpectedPacketsReachCpuInPristineSwitchState test case as P4 info + // change reboots the switch. + if (absl::StrContains(test_info->name(), + kNoUnexpectedPacketsPristineSwitchStateTest)) { + GetParam().testbed_interface->ExpectLinkFlaps(); + } GetParam().testbed_interface->SetUp(); // TODO: Port to thinkit::GenericTestbed. diff --git a/tests/qos/frontpanel_qos_test.cc b/tests/qos/frontpanel_qos_test.cc index 39c8ba827..d5b841455 100644 --- a/tests/qos/frontpanel_qos_test.cc +++ b/tests/qos/frontpanel_qos_test.cc @@ -14,24 +14,21 @@ #include "tests/qos/frontpanel_qos_test.h" -#include #include -#include #include #include #include -#include #include #include #include #include #include -#include #include #include #include #include "absl/algorithm/container.h" +#include "absl/base/thread_annotations.h" #include "absl/cleanup/cleanup.h" #include "absl/container/btree_set.h" #include "absl/container/flat_hash_map.h" @@ -40,12 +37,13 @@ #include "absl/strings/str_cat.h" #include "absl/strings/str_format.h" #include "absl/strings/str_join.h" -#include "absl/strings/str_replace.h" #include "absl/strings/string_view.h" #include "absl/strings/substitute.h" +#include "absl/synchronization/mutex.h" #include "absl/time/clock.h" #include "absl/time/time.h" #include "absl/types/optional.h" +#include "absl/types/span.h" #include "gmock/gmock.h" #include "gtest/gtest.h" #include "gutil/gutil/collections.h" @@ -80,7 +78,6 @@ #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 "thinkit/switch.h" namespace pins_test { @@ -744,6 +741,7 @@ TEST_P(FrontpanelQosTest, (kDscpsByQueueName == kDscpsByMulticastQueueName); QueueCounters kInitialQueueCounters; + Counters kInitialPortCounters; // Actual testing -- inject test IPv4 and IPv6 packets for each DSCP, // and check the behavior is as eexpted. constexpr int kMaxDscp = 63; @@ -788,6 +786,9 @@ TEST_P(FrontpanelQosTest, ASSERT_OK_AND_ASSIGN( kInitialQueueCounters, GetGnmiQueueCounters(kSutEgressPort, kTargetQueue, *gnmi_stub)); + ASSERT_OK_AND_ASSIGN( + kInitialPortCounters, + GetCountersForInterface(kSutEgressPort, *gnmi_stub)); } // Configure & start test packet flow. const std::string kTrafficName = @@ -873,7 +874,8 @@ TEST_P(FrontpanelQosTest, ixia::BytesPerSecondReceived(kIxiaTrafficStats); LOG(INFO) << "observed traffic rate (bytes/second): " << kObservedTrafficRate; - const int kAbsoluteError = kObservedTrafficRate - kTargetQueuePir; + const int64_t kAbsoluteError = + kObservedTrafficRate - kTargetQueuePir; const double kRelativeErrorPercent = 100. * kAbsoluteError / kTargetQueuePir; constexpr double kTolerancePercent = 10; // +-10% tolerance. @@ -906,6 +908,17 @@ TEST_P(FrontpanelQosTest, } while (TotalPacketsForQueue(delta_counters) < kIxiaTrafficStats.num_tx_frames() && absl::Now() < kDeadline); + ASSERT_OK_AND_ASSIGN( + Counters final_port_counters, + GetCountersForInterface(kSutEgressPort, *gnmi_stub)); + + SCOPED_TRACE( + absl::StrCat("port counters incremented by ", + kInitialPortCounters - final_port_counters)); + SCOPED_TRACE( + absl::StrCat("Initial port counters: ", kInitialPortCounters)); + SCOPED_TRACE( + absl::StrCat("Final port counters: ", final_port_counters)); LOG(INFO) << "queue counters incremented by " << delta_counters; LOG(INFO) << "Queue Counters.\nBefore: " << ToString(kInitialQueueCounters) @@ -1240,6 +1253,9 @@ TEST_P(FrontpanelQosTest, WeightedRoundRobinWeightsAreRespected) { ASSERT_OK_AND_ASSIGN(sut_p4rt, pdpi::P4RuntimeSession::Create(sut)); } // Start traffic. + Counters kInitialPortCounters; + ASSERT_OK_AND_ASSIGN(kInitialPortCounters, + GetCountersForInterface(kSutEgressPort, *gnmi_stub)); LOG(INFO) << "starting traffic"; ASSERT_OK(ixia::StartTraffic(traffic_items, kIxiaHandle, *testbed)); auto stop_traffic = absl::Cleanup( @@ -1257,6 +1273,14 @@ TEST_P(FrontpanelQosTest, WeightedRoundRobinWeightsAreRespected) { // weights. ASSERT_OK_AND_ASSIGN(const ixia::TrafficStats kTrafficStats, ixia::GetAllTrafficItemStats(kIxiaHandle, *testbed)); + + ASSERT_OK_AND_ASSIGN(Counters final_port_counters, + GetCountersForInterface(kSutEgressPort, *gnmi_stub)); + + SCOPED_TRACE(absl::StrCat("port counters incremented by ", + final_port_counters - kInitialPortCounters)); + SCOPED_TRACE(absl::StrCat("Initial port counters: ", kInitialPortCounters)); + SCOPED_TRACE(absl::StrCat("Final port counters: ", final_port_counters)); absl::flat_hash_map num_rx_frames_by_queue; for (auto &[traffic_item_name, stats] : Ordered(kTrafficStats.stats_by_traffic_item())) { @@ -1274,7 +1298,9 @@ TEST_P(FrontpanelQosTest, WeightedRoundRobinWeightsAreRespected) { ASSERT_OK_AND_ASSIGN(int64_t weight, gutil::FindOrStatus(weights_by_queue_name, queue)); const double kExpectedFraction = 1. * weight / total_weight; - const double kActualFraction = 1. * num_rx_frames / total_num_rx_frames; + // If received packets are 0, then the fraction is 0 (can't divide by 0). + const double kActualFraction = + total_num_rx_frames == 0 ? 0 : 1. * num_rx_frames / total_num_rx_frames; const double kAbsoluteError = kActualFraction - kExpectedFraction; const double kRelativeErrorPercent = 100. * kAbsoluteError / kExpectedFraction; @@ -1685,6 +1711,18 @@ TEST_P(FrontpanelQosTest, StrictQueuesAreStrictlyPrioritized) { LOG(INFO) << "NSF reboot complete, sending traffic again to ensure " "the forwarding traffic is not disrupted."; } + bool not_after_nsf_reboot = + !GetParam().nsf_reboot || + test_operation == TestOperations::NsfRebootAndTrafficTest; + Counters kInitialPortCounters; + if (not_after_nsf_reboot) { + // Get initial port counters after NSF Reboot is complete since + // gnmi calls cannot be made during NSF reboot. + ASSERT_OK_AND_ASSIGN( + kInitialPortCounters, + GetCountersForInterface(kSutEgressPort, *gnmi_stub)); + } + // Run traffic for a while, then obtain stats. LOG(INFO) << "starting traffic (" << traffic_items.size() << " items)"; // Occasionally the Ixia API cannot keep up and starting traffic @@ -1703,6 +1741,23 @@ TEST_P(FrontpanelQosTest, StrictQueuesAreStrictlyPrioritized) { ASSERT_OK_AND_ASSIGN( const ixia::TrafficStats kTrafficStats, ixia::GetAllTrafficItemStats(kIxiaHandle, *testbed)); + + if (not_after_nsf_reboot) { + // Get port counters after NSF Reboot is complete since + // gnmi calls cannot be made during NSF reboot. + ASSERT_OK_AND_ASSIGN( + Counters final_port_counters, + GetCountersForInterface(kSutEgressPort, *gnmi_stub)); + + SCOPED_TRACE( + absl::StrCat("port counters incremented by ", + final_port_counters - kInitialPortCounters)); + SCOPED_TRACE( + absl::StrCat("Initial port counters: ", kInitialPortCounters)); + SCOPED_TRACE( + absl::StrCat("Final port counters: ", final_port_counters)); + } + LOG(INFO) << "validating traffic stats against expectation"; double bytes_per_second_received = 0; for (auto &[traffic_item, stats] : @@ -2127,6 +2182,9 @@ TEST_P(FrontpanelQosTest, TestWredEcnMarking) { ResetEcnTestPacketCounters(packet_receive_info); + Counters kInitialPortCounters; + ASSERT_OK_AND_ASSIGN(kInitialPortCounters, + GetCountersForInterface(kSutOutPort, *gnmi_stub)); ASSERT_OK(pins::TryUpToNTimes(3, /*delay=*/absl::Seconds(1), [&] { return pins_test::ixia::StartTraffic(ixia_setup_result.traffic_refs, ixia_setup_result.topology_ref, @@ -2163,13 +2221,27 @@ TEST_P(FrontpanelQosTest, TestWredEcnMarking) { // Wait for a bit to collect queue statistics. constexpr absl::Duration kQueueStatsWaitTime = absl::Seconds(5); absl::SleepFor(kQueueStatsWaitTime); + + ASSERT_OK_AND_ASSIGN(Counters final_port_counters, + GetCountersForInterface(kSutOutPort, *gnmi_stub)); + + SCOPED_TRACE(absl::StrCat("port counters incremented by ", + final_port_counters - kInitialPortCounters)); + SCOPED_TRACE( + absl::StrCat("Initial port counters: ", kInitialPortCounters)); + SCOPED_TRACE( + absl::StrCat("Final port counters: ", final_port_counters)); // Read counters of the target queue. ASSERT_OK_AND_ASSIGN( const QueueCounters queue_counters_after_test_packet, GetGnmiQueueCounters(/*port=*/kSutOutPort, /*queue=*/target_queue, *gnmi_stub)); - + + SCOPED_TRACE(absl::StrCat("Queue counters before test packet: ", + ToString(queue_counters_before_test_packet))); + SCOPED_TRACE(absl::StrCat("Queue counters after test packet: ", + ToString(queue_counters_after_test_packet))); // This test expects WRED config to only mark packets and not // drop. Expect no drops in target queue and queue transmit // counter increments. @@ -2525,6 +2597,10 @@ TEST_P(FrontpanelBufferTest, BufferCarving) { ASSERT_OK(DoNsfRebootAndWaitForSwitchReadyOrRecover( testbed.get(), *GetParam().default_params.ssh_client_for_nsf)); } + Counters kInitialPortCounters; + ASSERT_OK_AND_ASSIGN(kInitialPortCounters, + GetCountersForInterface(kSutEgressPort, *gnmi_stub)); + // Start traffic. LOG(INFO) << "starting traffic"; ASSERT_OK(ixia::StartTraffic(traffic_items, kIxiaHandle, *testbed)); @@ -2536,6 +2612,13 @@ TEST_P(FrontpanelBufferTest, BufferCarving) { // config. ASSERT_OK_AND_ASSIGN(const ixia::TrafficStats kTrafficStats, ixia::GetAllTrafficItemStats(kIxiaHandle, *testbed)); + + ASSERT_OK_AND_ASSIGN(Counters final_port_counters, + GetCountersForInterface(kSutEgressPort, *gnmi_stub)); + SCOPED_TRACE(absl::StrCat("port counters incremented by ", + final_port_counters - kInitialPortCounters)); + SCOPED_TRACE(absl::StrCat("Initial port counters: ", kInitialPortCounters)); + SCOPED_TRACE(absl::StrCat("Final port counters: ", final_port_counters)); absl::flat_hash_map rx_frames_by_buffer_config; for (auto &[traffic_item_name, stats] : diff --git a/tests/qos/punt_qos_test.cc b/tests/qos/punt_qos_test.cc index b27207638..d9367b75e 100644 --- a/tests/qos/punt_qos_test.cc +++ b/tests/qos/punt_qos_test.cc @@ -89,15 +89,15 @@ absl::Status NsfRebootHelper(const Testbed &testbed, // 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) { + 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) { + 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)); @@ -393,6 +393,7 @@ TEST_P(PuntQoSTestWithIxia, SetDscpAndQueuesAndDenyAboveRateLimit) { ASSERT_OK_AND_ASSIGN( QueueCounters initial_cpu_queue_counters, GetGnmiQueueCounters("CPU", queue_name, *sut_gnmi_stub_)); + ASSERT_OK(ixia::SetTrafficParameters( ixia_traffic_handle_, traffic_parameters, *generic_testbed_)); @@ -722,7 +723,7 @@ TEST_P(PuntQoSTestWithIxia, MirrorFailover) { return ixia::StartTraffic(ixia_traffic_handle_, ixia_handle_, *generic_testbed_); })); - + if (GetParam().nsf_reboot && GetParam().ssh_client_for_nsf) { ASSERT_OK( NsfRebootHelper(generic_testbed_.get(), GetParam().ssh_client_for_nsf)); @@ -845,7 +846,7 @@ TEST_P(PuntQoSTestWithIxia, MulticastReplicationToCpu) { ASSERT_OK(ixia::SetTrafficParameters(ixia_traffic_handle_, traffix_parameters, *generic_testbed_)); - + if (GetParam().nsf_reboot && GetParam().ssh_client_for_nsf) { ASSERT_OK( NsfRebootHelper(generic_testbed_.get(), GetParam().ssh_client_for_nsf)); diff --git a/tests/sflow/BUILD.bazel b/tests/sflow/BUILD.bazel index 1354f1559..34219275c 100644 --- a/tests/sflow/BUILD.bazel +++ b/tests/sflow/BUILD.bazel @@ -28,7 +28,7 @@ cc_library( "//gutil/gutil:collections", "//gutil/gutil:status_matchers", "//gutil/gutil:testing", - "//lib:ixia_helper", + "//lib:otg_helper", "//lib/gnmi:gnmi_helper", "//lib/utils:json_utils", "//lib/validator:validator_lib", @@ -48,7 +48,6 @@ cc_library( "//tests/integration/system/nsf:compare_p4flows", "//tests/integration/system/nsf:util", "//tests/integration/system/nsf/interfaces:image_config_params", - "//tests/integration/system/nsf/interfaces:testbed", "//tests/lib:p4rt_fixed_table_programming_helper", "//tests/lib:switch_test_setup_helpers", "//tests/qos:gnmi_parsers", @@ -62,6 +61,9 @@ cc_library( "//thinkit:test_environment", "//thinkit/proto:generic_testbed_cc_proto", "@com_github_gnmi//proto/gnmi:gnmi_cc_grpc_proto", + "@com_github_grpc_grpc//:grpc++", + "@com_github_otg_models//:otg_cc_proto", + "@com_github_otg_models//:otg_grpc_proto", "@com_github_p4lang_p4runtime//:p4info_cc_proto", "@com_github_p4lang_p4runtime//:p4runtime_cc_proto", "@com_google_absl//absl/cleanup", diff --git a/tests/sflow/sflow_test.cc b/tests/sflow/sflow_test.cc index e65c9aa77..d15f19ddf 100644 --- a/tests/sflow/sflow_test.cc +++ b/tests/sflow/sflow_test.cc @@ -44,14 +44,17 @@ #include "absl/time/clock.h" #include "absl/time/time.h" #include "absl/types/span.h" +#include "artifacts/otg.grpc.pb.h" +#include "artifacts/otg.pb.h" #include "gmock/gmock.h" +#include "grpcpp/client_context.h" #include "gtest/gtest.h" #include "gutil/gutil/collections.h" #include "gutil/gutil/status.h" #include "gutil/gutil/status_matchers.h" #include "gutil/gutil/testing.h" #include "lib/gnmi/gnmi_helper.h" -#include "lib/ixia_helper.h" +#include "lib/otg_helper.h" #include "lib/utils/json_utils.h" #include "lib/validator/validator_lib.h" #include "p4/config/v1/p4info.pb.h" @@ -71,7 +74,6 @@ #include "tests/forwarding/util.h" #include "tests/integration/system/nsf/compare_p4flows.h" #include "tests/integration/system/nsf/interfaces/image_config_params.h" -#include "tests/integration/system/nsf/interfaces/testbed.h" #include "tests/integration/system/nsf/util.h" #include "tests/lib/p4rt_fixed_table_programming_helper.h" #include "tests/lib/switch_test_setup_helpers.h" @@ -97,6 +99,22 @@ using ::testing::Gt; using ::testing::HasSubstr; using ::testing::UnorderedElementsAreArray; +using ::grpc::ClientContext; +using ::otg::GetConfigResponse; +using ::otg::Openapi; +using ::otg::SetConfigRequest; +using ::otg::SetConfigResponse; +using ::otg::StateTrafficFlowTransmit; + +using ::pins_test::otg_helper::AddEthernetHeader; +using ::pins_test::otg_helper::AddIPv4Header; +using ::pins_test::otg_helper::AddPorts; +using ::pins_test::otg_helper::CreateFlow; +using ::pins_test::otg_helper::SetFlowDuration; +using ::pins_test::otg_helper::SetFlowRatePps; +using ::pins_test::otg_helper::SetFlowSize; +using ::pins_test::otg_helper::SetTrafficTransmissionState; + // Number of packets sent to one port. constexpr int kPacketsNum = 1000000; @@ -107,33 +125,38 @@ constexpr int kPacketsPerSecond = 16000; // the sFlow datagram. constexpr int kSampleHeaderSize = 512; -// Once accumulated data reaches kMaxPacketSize, sFlow would generate an sFlow -// datagram. -constexpr int kMaxPacketSize = 1400; - // Some fixed port numbers. constexpr int kCpuPort = 0x3FFFFFFF; constexpr int kDropPort = 256; +// The constants defined for SONIC +NosParameters nos_param_sonic = { + "", // kRedisExecPrefix + "docker exec sflow bash -c ", // kSflowContainerExecPrefix + "sudo ", // kSudoCmdPrefix + "INBAND_PRIORITY_3", // kCpuQueueName + "/usr/bin/", // kRedisCliPath + true // kIsSonicDebianLinux +}; + // Sflowtool binary name in the collector. constexpr absl::string_view kSflowToolName = "sflowtool"; constexpr absl::string_view kSflowtoolLineFormatTemplate = - "/etc/init.d/sflow-container exec '$0 -l -p $1 &" - " pid=$$!; sleep $2; kill -SIGTERM $$pid;'"; + "'$0 -l -p $1 & pid=$$!; sleep $2; kill -SIGTERM $$pid;'"; constexpr absl::string_view kSflowtoolLineFormatNonStopTemplate = - "/etc/init.d/sflow-container exec '$0 -l -p $1' || true"; + "'$0 -l -p $1' || true"; constexpr absl::string_view kSflowtoolFullFormatTemplate = - "/etc/init.d/sflow-container exec '$0 -p $1 &" - " pid=$$!; sleep $2; kill -SIGTERM $$pid;'"; + "'$0 -p $1 & pid=$$!; sleep $2; kill -SIGTERM $$pid;'"; constexpr absl::string_view kTcpdumpForTos = "tcpdump -c $0 -i lo -vv -eX udp and port $1"; constexpr absl::Duration kCommandTimeout = absl::Seconds(5); constexpr absl::Duration kFreezeTimeout = absl::Seconds(30); +constexpr absl::Duration kDelayTimeForIxia = absl::Seconds(5); constexpr char kFreeze[] = R"(/usr/tools/bin/redis-cli -n 1 "PUBLISH" "NSF_MANAGER_COMMON_NOTIFICATION_CHANNEL" "[\"freeze\",\"freeze\"]")"; @@ -146,11 +169,11 @@ constexpr int kNsfTrafficTestDurationSecs = 10; constexpr auto kIpv4Src = netaddr::Ipv4Address(192, 168, 10, 1); constexpr uint32_t kIpv4Dst = 0x01020304; // 1.2.3.4 // Ixia flow details. -constexpr auto kDstMac = netaddr::MacAddress(02, 02, 02, 02, 02, 03); +constexpr auto kDstMac = + netaddr::MacAddress(0x00, 0x1a, 0x11, 0x17, 0x5f, 0x80); constexpr auto kSourceMac = netaddr::MacAddress(00, 01, 02, 03, 04, 05); constexpr int kSamplingRateInterval = 4000; -constexpr absl::string_view kSrcIp6Address = "2001:db8:0:12::1"; // Buffering and software bottlenecks can cause some amount of variance in rate // measured end to end. @@ -159,11 +182,11 @@ constexpr absl::string_view kSrcIp6Address = "2001:db8:0:12::1"; constexpr double kTolerance = 0.20; // Thredshold percentage of packets punted to CPU to be considered passing -constexpr int kPassingPercentageCpuPunted = 80; +constexpr int kPassingPercentageCpuPunted = 75; // Thredshold percentage of packets received out of total packets sent -constexpr int kPassingPercentagePktRecv = 99; +constexpr int kPassingPercentagePktRecv = 98; // Thredshold percentage of packets punted out of total packets sent -constexpr int kPassingPercentagePktPunt = 99; +constexpr int kPassingPercentagePktPunt = 98; // Vrf prefix used in the test. constexpr absl::string_view kVrfIdPrefix = "vrf-"; @@ -180,13 +203,23 @@ constexpr int kSflowOutPacketsTos = 0x80; constexpr absl::string_view kEtherTypeIpv4 = "0x0800"; constexpr absl::string_view kEtherTypeIpv6 = "0x86dd"; +std::string SflowContainerExecCmdStr(const NosParameters& nos_param, + std::string cmd_name) { + return nos_param.kSflowContainerExecPrefix + cmd_name; +} + +std::string SflowContainerExecCmdStr(const NosParameters& nos_param, + absl::string_view cmd_name) { + return SflowContainerExecCmdStr(nos_param, std::string(cmd_name)); +} + absl::StatusOr GetSflowQueueName( - gnmi::gNMI::StubInterface* gnmi_stub) { + gnmi::gNMI::StubInterface* gnmi_stub, const std::string& cpu_queue_name) { ASSIGN_OR_RETURN(auto cpu_queues, pins_test::GetQueuesByEgressPort("CPU", *gnmi_stub)); for (const auto& queue_name : cpu_queues) { - if (queue_name == "INBAND_PRIORITY_2") { - return "INBAND_PRIORITY_2"; + if (queue_name == cpu_queue_name) { + return cpu_queue_name; } } return "BE1"; @@ -426,85 +459,64 @@ absl::StatusOr ReadCounters(std::string iface, return cnt; } +struct IxiaTrafficSettings { + Openapi::StubInterface& traffic_client; + int64_t traffic_rate; + int sampling_rate; +}; + // The packets are all same for one port. Use port_id as the index for // generating packets. -absl::Status SendNPacketsToSut(absl::Span traffic_ref, - absl::string_view topology_ref, - absl::Duration runtime, - thinkit::GenericTestbed& testbed) { +absl::Status SendNPacketsToSut(Openapi::StubInterface& traffic_client, + absl::Duration runtime) { // Send Ixia traffic. - RETURN_IF_ERROR( - pins_test::ixia::StartTraffic(traffic_ref, topology_ref, testbed)); + RETURN_IF_ERROR(SetTrafficTransmissionState( + traffic_client, StateTrafficFlowTransmit::State::start)); - // Wait for Traffic to be sent. - absl::SleepFor(runtime); + // Wait for Traffic to be sent. Add a little extra time kDelayTimeForIxia (5 + // sec) + absl::SleepFor(runtime + kDelayTimeForIxia); // Stop Ixia traffic. - RETURN_IF_ERROR(pins_test::ixia::StopTraffic(traffic_ref, testbed)); - - return absl::OkStatus(); + return SetTrafficTransmissionState(traffic_client, + StateTrafficFlowTransmit::State::stop); } -// Set up Ixia traffic with given parameters and return the traffic ref and -// topology ref string. -absl::StatusOr, std::string>> -SetUpIxiaTraffic(absl::Span ixia_links, - thinkit::GenericTestbed& testbed, const int64_t pkt_count, - const int64_t pkt_rate, const int frame_size = 1000) { - std::vector traffic_refs; - std::string topology_ref; - for (const IxiaLink& ixia_link : ixia_links) { - LOG(INFO) << __func__ << " Ixia if:" << ixia_link.ixia_interface - << " sut if:" << ixia_link.sut_interface - << " port id:" << ixia_link.port_id; - - std::string ixia_interface = ixia_link.ixia_interface; - std::string sut_interface = ixia_link.sut_interface; - - // Set up Ixia traffic. - ASSIGN_OR_RETURN(pins_test::ixia::IxiaPortInfo ixia_port, - pins_test::ixia::ExtractPortInfo(ixia_interface)); - ASSIGN_OR_RETURN(std::string topology_ref_tmp, - pins_test::ixia::IxiaConnect(ixia_port.hostname, testbed)); - if (topology_ref.empty()) { - topology_ref = topology_ref_tmp; - } else { - EXPECT_EQ(topology_ref, topology_ref_tmp); - } - - ASSIGN_OR_RETURN(std::string vport_ref, - pins_test::ixia::IxiaVport(topology_ref, ixia_port.card, - ixia_port.port, testbed)); - - ASSIGN_OR_RETURN(std::string traffic_ref, - pins_test::ixia::IxiaSession(vport_ref, testbed)); - - RETURN_IF_ERROR( - pins_test::ixia::SetFrameRate(traffic_ref, pkt_rate, testbed)); - - RETURN_IF_ERROR( - pins_test::ixia::SetFrameCount(traffic_ref, pkt_count, testbed)); - - RETURN_IF_ERROR( - pins_test::ixia::SetFrameSize(traffic_ref, frame_size, testbed)); - - RETURN_IF_ERROR(pins_test::ixia::SetSrcMac(traffic_ref, - kSourceMac.ToString(), testbed)); - - RETURN_IF_ERROR( - pins_test::ixia::SetDestMac(traffic_ref, kDstMac.ToString(), testbed)); - - RETURN_IF_ERROR(pins_test::ixia::AppendIPv4(traffic_ref, testbed)); - - RETURN_IF_ERROR( - pins_test::ixia::SetSrcIPv4(traffic_ref, kIpv4Src.ToString(), testbed)); - // Use Ipv4 dst address to differentiate different ports. - RETURN_IF_ERROR(pins_test::ixia::SetDestIPv4( - traffic_ref, GetDstIpv4AddrByPortId(ixia_link.port_id), testbed)); - - traffic_refs.push_back(traffic_ref); - } - return std::make_pair(traffic_refs, topology_ref); +// Set up Ixia traffic with given parameters and return the traffic_client. +absl::StatusOr SetUpIxiaTraffic( + absl::Span ixia_links, thinkit::GenericTestbed& testbed, + const int64_t pkt_count, const int64_t pkt_rate, + gnmi::gNMI::StubInterface& gnmi_stub, const int frame_size = 1000) { + const IxiaLink& tx_link = ixia_links[0]; + const IxiaLink& rx_link = ixia_links[1]; + + // Create config. + SetConfigRequest set_config_request; + otg::Config* config = set_config_request.mutable_config(); + + AddPorts(*config, tx_link.ixia_interface, rx_link.ixia_interface, + tx_link.ixia_location, rx_link.ixia_location); + + otg::Flow& flow = CreateFlow(*config, tx_link.ixia_interface, + rx_link.ixia_interface, "sflow"); + SetFlowSize(flow, frame_size); + SetFlowDuration(flow, pkt_count); + SetFlowRatePps(flow, pkt_rate); + + AddEthernetHeader(flow, kSourceMac.ToString(), kDstMac.ToString()); + AddIPv4Header(flow, kIpv4Src.ToString(), + GetDstIpv4AddrByPortId(tx_link.port_id)); + + // Set the config. + SetConfigResponse set_config_response; + ClientContext set_config_context; + Openapi::StubInterface* traffic_client = testbed.GetTrafficClient(); + LOG(INFO) << "Set Config Request: " << set_config_request; + RETURN_IF_ERROR(gutil::GrpcStatusToAbslStatus(traffic_client->SetConfig( + &set_config_context, set_config_request, &set_config_response))); + LOG(INFO) << "Set Config Response: " << absl::StrCat(set_config_response); + + return traffic_client; } // Get the packet counters on SUT interface connected to Ixia. @@ -529,12 +541,10 @@ absl::StatusOr> GetIxiaInterfaceCounters( return counters; } -absl::Status SetIxiaTrafficParams(absl::string_view traffic_ref, - int64_t pkts_num, int64_t traffic_rate, - thinkit::GenericTestbed& generic_testbed) { - RETURN_IF_ERROR(pins_test::ixia::SetFrameRate(traffic_ref, traffic_rate, - generic_testbed)); - return pins_test::ixia::SetFrameCount(traffic_ref, pkts_num, generic_testbed); +void SetIxiaTrafficParams(otg::Flow& flow, int64_t pkts_num, + int64_t traffic_rate) { + SetFlowDuration(flow, pkts_num); + SetFlowRatePps(flow, traffic_rate); } // Run sflowtool on SUT in a new thread. Returns the thread to let caller to @@ -553,7 +563,7 @@ absl::StatusOr RunSflowCollectorForNSecs( sflow_tool_result, ssh_client.RunCommand( device_name, ssh_command, - /*timeout=*/absl::Seconds(sflowtool_runtime + 10))); + /*timeout=*/absl::Seconds(sflowtool_runtime + 20))); }); // Sleep to wait sflowtool to start. absl::SleepFor(absl::Seconds(5)); @@ -592,15 +602,98 @@ void StopSflowtool(thinkit::SSHClient &ssh_client, /*timeout=*/absl::Seconds(5))); } +// Needs to find out the pid of sFlow on switch manually. +absl::StatusOr RunTopCommandForSflow( + thinkit::SSHClient& ssh_client, absl::string_view device_name, int run_time, + std::string& result) { + std::thread thread = std::thread([&result, &ssh_client, run_time, + device_name]() { + const int interval = 30; + int times = run_time / interval; + LOG(INFO) << "RunTopCommandForSflow run_time: " << run_time + << " times: " << times; + // Run top command every interval. Whole times = run_time/interval. + const std::string ssh_command = + absl::StrCat("top -b -d ", interval, " -p $(pidof hsflowd) -n ", times); + LOG(INFO) << "RunTopCommandForSflow ssh command: " << ssh_command; + ASSERT_OK_AND_ASSIGN(result, ssh_client.RunCommand( + device_name, ssh_command, + /*timeout=*/absl::Seconds(run_time + 20))); + }); + return thread; +} + +// start_time=$(date +%s) + +// while [ $(date +%s) -lt $(($start_time + 36000)) ]; do +// mpstat +// sleep 130 +// done +absl::StatusOr ShowCpuIdle(thinkit::SSHClient& ssh_client, + absl::string_view device_name, + const int run_time, + std::string& result) { + std::thread thread = + std::thread([&result, &ssh_client, run_time, device_name]() { + absl::Time cur_time = absl::Now(); + while (absl::Now() <= cur_time + absl::Seconds(run_time)) { + const std::string ssh_command = "mpstat"; + // LOG(INFO) << "ShowCpuIdle ssh command:" << ssh_command; + ASSERT_OK_AND_ASSIGN( + std::string cur_result, + ssh_client.RunCommand(device_name, ssh_command, + /*timeout=*/absl::Seconds(10))); + LOG(INFO) << "ShowCpuIdle result:" << cur_result; + result += cur_result; + absl::SleepFor(absl::Seconds(100)); + } + }); + return thread; +} + +// #!/bin/bash + +// start_time=$(date +%s) + +// while [ $(date +%s) -lt $(($start_time + 36000)) ]; do +// pmap $(pidof hsflowd) | tail -n 1 | awk '/[0-9]K/{print $2}' +// sleep 60 +// done +absl::StatusOr ShowMemory(thinkit::SSHClient& ssh_client, + absl::string_view device_name, + const int run_time, + std::string& result) { + std::thread thread = + std::thread([&result, &ssh_client, run_time, device_name]() { + absl::Time cur_time = absl::Now(); + while (absl::Now() <= cur_time + absl::Seconds(run_time)) { + const std::string ssh_command = + "pmap $(pidof hsflowd) | tail -n 1 | awk '/[0-9]K/{print $2}'"; + // LOG(INFO) << "ShowMemory ssh command:" << ssh_command; + ASSERT_OK_AND_ASSIGN( + std::string cur_result, + ssh_client.RunCommand(device_name, ssh_command, + /*timeout=*/absl::Seconds(10))); + LOG(INFO) << "ShowMemory result:" << cur_result; + result += cur_result; + absl::SleepFor(absl::Seconds(100)); + } + }); + return thread; +} + // Run tcpdump on SUT in a new thread. Returns the thread to let caller to // wait for the finish. absl::StatusOr CaptureTcpdumpForNPackets( thinkit::SSHClient& ssh_client, absl::string_view device_name, - int packets_count, int port, const int runtime, std::string& result) { - std::thread thread = std::thread([device_name, packets_count, port, runtime, - &ssh_client, &result]() { - std::string ssh_command = - absl::Substitute(kTcpdumpForTos, packets_count, port); + int packets_count, int port, const int runtime, + const NosParameters& nos_param, std::string& result) { + std::string ssh_command = + absl::Substitute(nos_param.kSudoCmdPrefix + std::string(kTcpdumpForTos), + packets_count, port); + LOG(INFO) << "ssh command:" << ssh_command; + std::thread thread = std::thread([device_name, runtime, &ssh_client, + ssh_command, &result]() { LOG(INFO) << "ssh command:" << ssh_command; ASSERT_OK_AND_ASSIGN( result, ssh_client.RunCommand(device_name, ssh_command, @@ -623,26 +716,26 @@ absl::StatusOr GetHsflowdPid(thinkit::SSHClient& ssh_client, // Send packets to SUT and validate packet counters via gNMI. This function // expects no drops on CPU queue. -absl::Status SendSflowTraffic(absl::Span traffic_refs, - absl::string_view topology_ref, +absl::Status SendSflowTraffic(Openapi::StubInterface& traffic_client, absl::Span ixia_links, thinkit::GenericTestbed& testbed, gnmi::gNMI::StubInterface* gnmi_stub, - const int64_t pkt_count, const int64_t pkt_rate) { + const int64_t pkt_count, const int64_t pkt_rate, + const std::string& cpu_queue_name) { // Read initial counters via GNMI from the SUT LOG(INFO) << "Read initial packet counters."; ASSIGN_OR_RETURN(std::vector initial_in_counters, GetIxiaInterfaceCounters(ixia_links, gnmi_stub)); - ASSIGN_OR_RETURN(std::string sflow_queue_name, GetSflowQueueName(gnmi_stub)); + ASSIGN_OR_RETURN(std::string sflow_queue_name, + GetSflowQueueName(gnmi_stub, cpu_queue_name)); ASSIGN_OR_RETURN( auto initial_queue_counter, pins_test::GetGnmiQueueCounters("CPU", sflow_queue_name, *gnmi_stub)); absl::Time start_time = absl::Now(); RETURN_IF_ERROR(SendNPacketsToSut( - traffic_refs, topology_ref, - /*runtime=*/absl::Seconds(std::ceil(1.0f * pkt_count / pkt_rate)), - testbed)); + traffic_client, + /*runtime=*/absl::Seconds(std::ceil(1.0f * pkt_count / pkt_rate)))); // Sleep to wait for the counters to be reflected. absl::SleepFor(absl::Seconds(10)); @@ -694,12 +787,13 @@ struct IxiaCounterStats { absl::StatusOr GetIxiaCounterStats( absl::Span ixia_links, gnmi::gNMI::StubInterface* gnmi_stub, - const int64_t pkt_count) { + const int64_t pkt_count, const std::string& cpu_queue_name) { // Read initial counters via GNMI from the SUT LOG(INFO) << "Read initial packet counters."; ASSIGN_OR_RETURN(std::vector initial_in_counters, GetIxiaInterfaceCounters(ixia_links, gnmi_stub)); - ASSIGN_OR_RETURN(std::string sflow_queue_name, GetSflowQueueName(gnmi_stub)); + ASSIGN_OR_RETURN(std::string sflow_queue_name, + GetSflowQueueName(gnmi_stub, cpu_queue_name)); ASSIGN_OR_RETURN( auto initial_queue_counter, pins_test::GetGnmiQueueCounters("CPU", sflow_queue_name, *gnmi_stub)); @@ -787,17 +881,6 @@ int GetSflowSamplesOnSut(const std::string& sflowtool_output, return count; } -// Get port speed by reading interface/ethernet/state/port-speed path. -absl::StatusOr GetPortSpeed(absl::string_view iface, - gnmi::gNMI::StubInterface* gnmi_stub) { - std::string ops_state_path = absl::StrCat("interfaces/interface[name=", iface, - "]/ethernet/state/port-speed"); - - std::string ops_parse_str = "openconfig-if-ethernet:port-speed"; - return pins_test::GetGnmiStatePathInfo(gnmi_stub, ops_state_path, - ops_parse_str); -} - // Check interface/state/oper-status value to validate if link is up. absl::StatusOr CheckLinkUp(absl::string_view interface, gnmi::gNMI::StubInterface& gnmi_stub) { @@ -857,6 +940,7 @@ absl::StatusOr> GetIxiaConnectedUpLinks( << *port_id; ixia_links.push_back(IxiaLink{ .ixia_interface = info.peer_interface_name, + .ixia_location = info.peer_traffic_location, .sut_interface = interface, .port_id = std::stoi(*port_id), }); @@ -1051,13 +1135,11 @@ void VerifySflowResult(absl::string_view sflowtool_output, IsOkAndHolds(true)) << "Expected src ip: " << src_ip << ". Actual src ip: " << fields[kSrcIpIdx]; - // Since PINs cap at 1028 packet size for punt packets, the maximum value - // of sample's packet size field would be 1028. + // TODO: check if maximum packet size is 1028. If so, + // replace with absl::StrCat(std::min(*packet_size, 1028)) if (packet_size.has_value()) { - EXPECT_EQ(fields[kPktSizeIdx], - absl::StrCat(std::min(*packet_size, 1028))) - << "Expected packet size: " - << absl::StrCat(std::min(*packet_size, 1028)) + EXPECT_EQ(fields[kPktSizeIdx], absl::StrCat(*packet_size)) + << "Expected packet size: " << absl::StrCat(*packet_size) << ". Actual packet size: " << fields[kPktSizeIdx]; } EXPECT_EQ(fields[kSamplingRateIdx], absl::StrCat(sampling_rate)); @@ -1076,21 +1158,27 @@ void CollectDriverDebugs(thinkit::SSHClient* ssh_client, // db contents void CollectSflowDebugs(thinkit::SSHClient* ssh_client, absl::string_view device_name, absl::string_view prefix, - thinkit::TestEnvironment& environment) { - CollectDriverDebugs(ssh_client, device_name, prefix, environment); + thinkit::TestEnvironment& environment, + const NosParameters& nos_param) { + // TODO: make this a param of instantiation + if (!nos_param.kIsSonicDebianLinux) { + CollectDriverDebugs(ssh_client, device_name, prefix, environment); + } // /etc/hsflowd.auto might not exist on the switch if no collector config. auto result_or = ssh_client->RunCommand( device_name, /*command=*/ - "docker exec sflow bash -c \"cat /etc/hsflowd.auto\"", absl::Seconds(20)); + SflowContainerExecCmdStr(nos_param, + std::string("'cat /etc/hsflowd.auto'")), + absl::Seconds(20)); if (result_or.ok()) { EXPECT_OK(environment.StoreTestArtifact( absl::StrCat(prefix, "sflow_container_hsflowd_auto.txt"), *result_or)); } result_or = ssh_client->RunCommand(device_name, /*command=*/ - "docker exec sflow bash -c \"ps -ef\"", + SflowContainerExecCmdStr(nos_param, std::string("'ps -ef'")), absl::Seconds(20)); if (result_or.ok()) { EXPECT_OK(environment.StoreTestArtifact( @@ -1110,52 +1198,57 @@ void CollectSflowDebugs(thinkit::SSHClient* ssh_client, absl::StrCat(prefix, "ipv6_neigh_show.txt"), result)); // APPL_DB - ASSERT_OK_AND_ASSIGN(result, ssh_client->RunCommand( - device_name, - /*command=*/ - "ctr tasks exec --exec-id sflow_test db-con " - "redis-dump -H 127.0.0.1 -p 6379 -d 0 -y", - absl::Seconds(20))); + ASSERT_OK_AND_ASSIGN( + result, + ssh_client->RunCommand(device_name, + /*command=*/ + nos_param.kRedisExecPrefix + + "redis-dump -H 127.0.0.1 -p 6379 -d 0 -y", + absl::Seconds(20))); EXPECT_OK(environment.StoreTestArtifact( absl::StrCat(prefix, "sut_appl_db.txt"), result)); // ASIC_DB - ASSERT_OK_AND_ASSIGN(result, ssh_client->RunCommand( - device_name, - /*command=*/ - "ctr tasks exec --exec-id sflow_test db-con " - "redis-dump -H 127.0.0.1 -p 6379 -d 1 -y", - absl::Seconds(20))); + ASSERT_OK_AND_ASSIGN( + result, + ssh_client->RunCommand(device_name, + /*command=*/ + nos_param.kRedisExecPrefix + + "redis-dump -H 127.0.0.1 -p 6379 -d 1 -y", + absl::Seconds(20))); EXPECT_OK(environment.StoreTestArtifact( absl::StrCat(prefix, "sut_asic_db.txt"), result)); // CONFIG_DB - ASSERT_OK_AND_ASSIGN(result, ssh_client->RunCommand( - device_name, - /*command=*/ - "ctr tasks exec --exec-id sflow_test db-con " - "redis-dump -H 127.0.0.1 -p 6379 -d 4 -y", - absl::Seconds(20))); + ASSERT_OK_AND_ASSIGN( + result, + ssh_client->RunCommand(device_name, + /*command=*/ + nos_param.kRedisExecPrefix + + "redis-dump -H 127.0.0.1 -p 6379 -d 4 -y", + absl::Seconds(20))); EXPECT_OK(environment.StoreTestArtifact( absl::StrCat(prefix, "sut_config_db.txt"), result)); // STATE_DB - ASSERT_OK_AND_ASSIGN(result, ssh_client->RunCommand( - device_name, - /*command=*/ - "ctr tasks exec --exec-id sflow_test db-con " - "redis-dump -H 127.0.0.1 -p 6379 -d 6 -y", - absl::Seconds(20))); + ASSERT_OK_AND_ASSIGN( + result, + ssh_client->RunCommand(device_name, + /*command=*/ + nos_param.kRedisExecPrefix + + "redis-dump -H 127.0.0.1 -p 6379 -d 6 -y", + absl::Seconds(20))); EXPECT_OK(environment.StoreTestArtifact( absl::StrCat(prefix, "sut_state_db.txt"), result)); // APPL_STATE_DB - ASSERT_OK_AND_ASSIGN(result, ssh_client->RunCommand( - device_name, - /*command=*/ - "ctr tasks exec --exec-id sflow_test db-con " - "redis-dump -H 127.0.0.1 -p 6379 -d 14 -y", - absl::Seconds(20))); + ASSERT_OK_AND_ASSIGN( + result, + ssh_client->RunCommand(device_name, + /*command=*/ + nos_param.kRedisExecPrefix + + "redis-dump -H 127.0.0.1 -p 6379 -d 14 -y", + absl::Seconds(20))); EXPECT_OK(environment.StoreTestArtifact( absl::StrCat(prefix, "sut_appl_state_db.txt"), result)); } @@ -1259,15 +1352,8 @@ absl::StatusOr GetPortIdFromInterfaceName( return port_id; } -struct IxiaTrafficSettings { - std::string traffic_ref; - std::string topology_ref; - int64_t traffic_rate; - int sampling_rate; -}; - absl::StatusOr SetUpIxiaTrafficForSflowNsf( - const IxiaLink& ingress_link, + absl::Span ixia_links, const absl::flat_hash_set& sflow_enabled_interfaces, thinkit::GenericTestbed* testbed, gnmi::gNMI::StubInterface* gnmi_stub) { absl::flat_hash_map initial_interfaces_to_sample_rate; @@ -1278,25 +1364,18 @@ absl::StatusOr SetUpIxiaTrafficForSflowNsf( const int interface_sample_rate = initial_interfaces_to_sample_rate.begin()->second; - // Set up Ixia traffic. - // ixia_ref_pair would include the traffic reference and topology reference - // which could be used to send traffic later. - std::pair, std::string> ixia_ref_pair; - ASSIGN_OR_RETURN(ixia_ref_pair, - SetUpIxiaTraffic({ingress_link}, *testbed, /*pkt_count=*/0, - /*pkt_rate=*/0)); - const std::string traffic_ref = ixia_ref_pair.first[0]; - const std::string topology_ref = ixia_ref_pair.second; - // Generate 10 samples/sec. int64_t traffic_rate = 10 * interface_sample_rate; - RETURN_IF_ERROR(SetIxiaTrafficParams( - traffic_ref, traffic_rate * kNsfTrafficTestDurationSecs, traffic_rate, - *testbed)); + + // Set up Ixia traffic. + ASSIGN_OR_RETURN( + Openapi::StubInterface * traffic_client, + SetUpIxiaTraffic(ixia_links, *testbed, + /*pkt_count=*/traffic_rate * kNsfTrafficTestDurationSecs, + /*pkt_rate=*/traffic_rate, *gnmi_stub)); return IxiaTrafficSettings{ - .traffic_ref = traffic_ref, - .topology_ref = topology_ref, + .traffic_client = *traffic_client, .traffic_rate = traffic_rate, .sampling_rate = interface_sample_rate, }; @@ -1305,17 +1384,19 @@ absl::StatusOr SetUpIxiaTrafficForSflowNsf( absl::StatusOr GetSflowSamplesAfterSendingIxiaTraffic( thinkit::GenericTestbed* testbed, thinkit::SSHClient* ssh_client, const IxiaLink& ingress_link, - const IxiaTrafficSettings& ixia_traffic_settings, - const int collector_port) { + const IxiaTrafficSettings& ixia_traffic_settings, const int collector_port, + const NosParameters& nos_param) { // Start sflowtool on SUT. std::string sflow_result; { - ASSIGN_OR_RETURN(std::thread sflow_tool_thread, - RunSflowCollectorForNSecs( - *ssh_client, testbed->Sut().ChassisName(), - kSflowtoolLineFormatTemplate, collector_port, - /*sflowtool_runtime=*/kNsfTrafficTestDurationSecs + 30, - sflow_result)); + ASSIGN_OR_RETURN( + std::thread sflow_tool_thread, + RunSflowCollectorForNSecs( + *ssh_client, testbed->Sut().ChassisName(), + SflowContainerExecCmdStr(nos_param, kSflowtoolLineFormatTemplate), + collector_port, + /*sflowtool_runtime=*/kNsfTrafficTestDurationSecs + 30, + sflow_result)); // Wait for sflowtool to finish. absl::Cleanup clean_up([&sflow_tool_thread] { @@ -1325,12 +1406,9 @@ absl::StatusOr GetSflowSamplesAfterSendingIxiaTraffic( }); RETURN_IF_ERROR(SendNPacketsToSut( - std::vector{ - std::string(ixia_traffic_settings.traffic_ref)}, - ixia_traffic_settings.topology_ref, + ixia_traffic_settings.traffic_client, /*runtime=*/ - absl::Seconds(std::ceil(1.0f * kNsfTrafficTestDurationSecs)), - *testbed)); + absl::Seconds(std::ceil(1.0f * kNsfTrafficTestDurationSecs)))); // Sleep to wait for the counters to be reflected. absl::SleepFor(absl::Seconds(10)); @@ -1348,10 +1426,10 @@ absl::StatusOr GetSflowSamplesAfterSendingIxiaTraffic( void PerformBackoffTest( thinkit::GenericTestbed* testbed, gnmi::gNMI::StubInterface* gnmi_stub, - thinkit::SSHClient* ssh_client, const IxiaLink& ingress_link, + thinkit::SSHClient* ssh_client, absl::Span ixia_links, const std::string& sut_gnmi_config, const absl::flat_hash_set& sflow_enabled_interfaces, - const int collector_port) { + const int collector_port, const NosParameters& nos_param) { // Read initial sample rate from testbed. absl::flat_hash_map initial_interfaces_to_sample_rate; ASSERT_OK_AND_ASSIGN(initial_interfaces_to_sample_rate, @@ -1362,21 +1440,15 @@ void PerformBackoffTest( const int interface_sample_rate = initial_interfaces_to_sample_rate.begin()->second; - // Set up Ixia traffic. - // ixia_ref_pair would include the traffic reference and topology reference - // which could be used to send traffic later. - std::pair, std::string> ixia_ref_pair; - ASSERT_OK_AND_ASSIGN(ixia_ref_pair, - SetUpIxiaTraffic({ingress_link}, *testbed, 0, 0)); - const std::string traffic_ref = ixia_ref_pair.first[0], - topology_ref = ixia_ref_pair.second; - - // Setup Ixia for normal traffic speed - it would generate 10 - // samples/sec. + const IxiaLink& ingress_link = ixia_links[0]; + int64_t traffic_rate = 10 * interface_sample_rate; - ASSERT_OK(SetIxiaTrafficParams(traffic_ref, - traffic_rate * kBackoffTrafficDurationSecs, - traffic_rate, *testbed)); + + ASSERT_OK_AND_ASSIGN( + Openapi::StubInterface * traffic_client, + SetUpIxiaTraffic(ixia_links, *testbed, + traffic_rate * kBackoffTrafficDurationSecs, traffic_rate, + *gnmi_stub)); // Start sflowtool on SUT. std::string sflow_result; @@ -1385,7 +1457,8 @@ void PerformBackoffTest( std::thread sflow_tool_thread, RunSflowCollectorForNSecs( *ssh_client, testbed->Sut().ChassisName(), - kSflowtoolLineFormatTemplate, collector_port, + SflowContainerExecCmdStr(nos_param, kSflowtoolLineFormatTemplate), + collector_port, /*sflowtool_runtime=*/kBackoffTrafficDurationSecs + 30, sflow_result)); @@ -1398,10 +1471,9 @@ void PerformBackoffTest( // Send packets from Ixia to SUT. ASSERT_OK(SendSflowTraffic( - std::vector{std::string(traffic_ref)}, topology_ref, - {ingress_link}, *testbed, gnmi_stub, - /*pkt_count=*/traffic_rate * kBackoffTrafficDurationSecs, - traffic_rate)); + *traffic_client, {ingress_link}, *testbed, gnmi_stub, + /*pkt_count=*/traffic_rate * kBackoffTrafficDurationSecs, traffic_rate, + nos_param.kCpuQueueName)); } ASSERT_OK(testbed->Environment().StoreTestArtifact( @@ -1421,8 +1493,28 @@ void PerformBackoffTest( // generate kBackOffThresholdSamples per sec. traffic_rate = kBackOffThresholdSamples * interface_sample_rate; int64_t packets_num = traffic_rate * kBackoffTrafficDurationSecs; + + GetConfigResponse get_config_response; + ClientContext get_config_context; + ASSERT_OK( - SetIxiaTrafficParams(traffic_ref, packets_num, traffic_rate, *testbed)); + traffic_client->GetConfig(&get_config_context, {}, &get_config_response)); + + SetConfigRequest set_config_request; + + otg::Config* config = set_config_request.mutable_config(); + *config = get_config_response.config(); + otg::Flow* flow = config->mutable_flows(0); + SetIxiaTrafficParams(*flow, packets_num, traffic_rate); + + { + SetConfigResponse set_config_response; + ClientContext set_config_context; + LOG(INFO) << "Set Config Request: " << absl::StrCat(set_config_request); + ASSERT_OK(traffic_client->SetConfig(&set_config_context, set_config_request, + &set_config_response)); + LOG(INFO) << "Set Config Response: " << absl::StrCat(set_config_response); + } // Start sflowtool on SUT. { @@ -1430,7 +1522,8 @@ void PerformBackoffTest( std::thread sflow_tool_thread, RunSflowCollectorForNSecs( *ssh_client, testbed->Sut().ChassisName(), - kSflowtoolLineFormatTemplate, collector_port, + SflowContainerExecCmdStr(nos_param, kSflowtoolLineFormatTemplate), + collector_port, /*sflowtool_runtime=*/kBackoffTrafficDurationSecs + 30, sflow_result)); @@ -1444,10 +1537,9 @@ void PerformBackoffTest( // Send packets from Ixia to SUT. We set the `expected_drop_ratio` to 0.05 // since Ixia traffic unavoidably causes some drops on BE1 queue. ASSERT_OK(SendSflowTraffic( - std::vector{std::string(traffic_ref)}, topology_ref, - {ingress_link}, *testbed, gnmi_stub, - /*pkt_count=*/traffic_rate * kBackoffTrafficDurationSecs, - traffic_rate)); + *traffic_client, {ingress_link}, *testbed, gnmi_stub, + /*pkt_count=*/traffic_rate * kBackoffTrafficDurationSecs, traffic_rate, + nos_param.kCpuQueueName)); } ASSERT_OK(testbed->Environment().StoreTestArtifact( @@ -1483,9 +1575,18 @@ void PerformBackoffTest( // Use a normal traffic speed, sample rate should remain as doubled. traffic_rate = 10 * interface_sample_rate; - ASSERT_OK(SetIxiaTrafficParams(traffic_ref, - traffic_rate * kBackoffTrafficDurationSecs, - traffic_rate, *testbed)); + packets_num = traffic_rate * kBackoffTrafficDurationSecs; + + SetIxiaTrafficParams(*flow, packets_num, traffic_rate); + + { + SetConfigResponse set_config_response; + ClientContext set_config_context; + LOG(INFO) << "Set Config Request: " << absl::StrCat(set_config_request); + ASSERT_OK(traffic_client->SetConfig(&set_config_context, set_config_request, + &set_config_response)); + LOG(INFO) << "Set Config Response: " << absl::StrCat(set_config_response); + } { // Start sflowtool on SUT. @@ -1493,7 +1594,8 @@ void PerformBackoffTest( std::thread sflow_tool_thread, RunSflowCollectorForNSecs( *ssh_client, testbed->Sut().ChassisName(), - kSflowtoolLineFormatTemplate, collector_port, + SflowContainerExecCmdStr(nos_param, kSflowtoolLineFormatTemplate), + collector_port, /*sflowtool_runtime=*/kBackoffTrafficDurationSecs + 30, sflow_result)); @@ -1506,10 +1608,9 @@ void PerformBackoffTest( // Send packets from Ixia to SUT. ASSERT_OK(SendSflowTraffic( - std::vector{std::string(traffic_ref)}, topology_ref, - {ingress_link}, *testbed, gnmi_stub, - /*pkt_count=*/traffic_rate * kBackoffTrafficDurationSecs, - traffic_rate)); + *traffic_client, {ingress_link}, *testbed, gnmi_stub, + /*pkt_count=*/traffic_rate * kBackoffTrafficDurationSecs, traffic_rate, + nos_param.kCpuQueueName)); } ASSERT_OK(testbed->Environment().StoreTestArtifact( @@ -1534,9 +1635,18 @@ void PerformBackoffTest( // Use a normal traffic speed, sample rate should remain as doubled. traffic_rate = 10 * interface_sample_rate; - ASSERT_OK(SetIxiaTrafficParams(traffic_ref, - traffic_rate * kBackoffTrafficDurationSecs, - traffic_rate, *testbed)); + packets_num = traffic_rate * kBackoffTrafficDurationSecs; + + SetIxiaTrafficParams(*flow, packets_num, traffic_rate); + + { + SetConfigResponse set_config_response; + ClientContext set_config_context; + LOG(INFO) << "Set Config Request: " << absl::StrCat(set_config_request); + ASSERT_OK(traffic_client->SetConfig(&set_config_context, set_config_request, + &set_config_response)); + LOG(INFO) << "Set Config Response: " << absl::StrCat(set_config_response); + } { // Start sflowtool on SUT. @@ -1544,7 +1654,8 @@ void PerformBackoffTest( std::thread sflow_tool_thread, RunSflowCollectorForNSecs( *ssh_client, testbed->Sut().ChassisName(), - kSflowtoolLineFormatTemplate, collector_port, + SflowContainerExecCmdStr(nos_param, kSflowtoolLineFormatTemplate), + collector_port, /*sflowtool_runtime=*/kBackoffTrafficDurationSecs + 30, sflow_result)); @@ -1557,10 +1668,9 @@ void PerformBackoffTest( // Send packets from Ixia to SUT. ASSERT_OK(SendSflowTraffic( - std::vector{std::string(traffic_ref)}, topology_ref, - {ingress_link}, *testbed, gnmi_stub, - /*pkt_count=*/traffic_rate * kBackoffTrafficDurationSecs, - traffic_rate)); + *traffic_client, {ingress_link}, *testbed, gnmi_stub, + /*pkt_count=*/traffic_rate * kBackoffTrafficDurationSecs, traffic_rate, + nos_param.kCpuQueueName)); } ASSERT_OK(testbed->Environment().StoreTestArtifact( @@ -1593,8 +1703,17 @@ void SflowTestFixture::SetUp() { testbed_, GetParam().testbed_interface->GetTestbedWithRequirements(requirements)); + nos_param_ = GetParam().nos_is_sonic ? nos_param_sonic : GetParam().nos_param; CollectSflowDebugs(ssh_client_, testbed_->Sut().ChassisName(), - /*prefix=*/"preconfig_", testbed_->Environment()); + /*prefix=*/"preconfig_", testbed_->Environment(), + nos_param_); + + ASSERT_OK(GetParam().ssh_client->RunCommand( + testbed_->Sut().ChassisName(), + /*command=*/ + nos_param_.kRedisExecPrefix + + "redis-cli -n 14 hset \"PORT_TABLE:CPU\" \"NULL\" \"NULL\"", + absl::Seconds(20))); std::vector> collector_address_and_port; const std::string& gnmi_config = GetParam().gnmi_config; @@ -1612,6 +1731,7 @@ void SflowTestFixture::SetUp() { absl::flat_hash_map sflow_enabled_interfaces; ASSERT_OK_AND_ASSIGN(sflow_enabled_interfaces, GetSflowInterfacesFromSut(*testbed_)); + ASSERT_OK_AND_ASSIGN( gnmi_config_with_sflow_, UpdateSflowConfig(gnmi_config, agent_addr_ipv6, @@ -1637,18 +1757,20 @@ void SflowTestFixture::SetUp() { ASSERT_OK_AND_ASSIGN(ready_links_, GetIxiaConnectedUpLinks(*testbed_, *gnmi_stub_)); - ASSERT_FALSE(ready_links_.empty()) << "Ixia links are not ready"; + ASSERT_GE(ready_links_.size(), 2) << "Ixia links are not ready"; CollectSflowDebugs(ssh_client_, testbed_->Sut().ChassisName(), - /*prefix=*/"pretest_", testbed_->Environment()); + /*prefix=*/"pretest_", testbed_->Environment(), + nos_param_); } void SflowTestFixture::TearDown() { LOG(INFO) << "\n------ TearDown START ------\n"; ASSERT_NE(testbed_, nullptr); - + CollectSflowDebugs(ssh_client_, testbed_->Sut().ChassisName(), - /*prefix=*/"posttest_", testbed_->Environment()); + /*prefix=*/"posttest_", testbed_->Environment(), + nos_param_); if (sut_p4_session_ != nullptr) { EXPECT_OK(OutputTableEntriesToArtifact( @@ -1674,6 +1796,7 @@ void SflowTestFixture::TearDown() { if (GetParam().testbed_interface != nullptr) { delete GetParam().testbed_interface; } + LOG(INFO) << "\n------ TearDown END ------\n"; } @@ -1690,13 +1813,11 @@ TEST_P(SflowTestFixture, VerifyIngressSamplingForNoMatchPackets) { }; const int pkt_size = 500; - // ixia_ref_pair would include the traffic reference and topology reference - // which could be used to send traffic later. - std::pair, std::string> ixia_ref_pair; // Set up Ixia traffic. - ASSERT_OK_AND_ASSIGN(ixia_ref_pair, - SetUpIxiaTraffic({ingress_link}, *testbed_, kPacketsNum, - kPacketsPerSecond, pkt_size)); + ASSERT_OK_AND_ASSIGN( + Openapi::StubInterface * traffic_client, + SetUpIxiaTraffic({ready_links_[0], ready_links_[1]}, *testbed_, + kPacketsNum, kPacketsPerSecond, *gnmi_stub_, pkt_size)); std::string sflow_result; { @@ -1705,7 +1826,8 @@ TEST_P(SflowTestFixture, VerifyIngressSamplingForNoMatchPackets) { std::thread sflow_tool_thread, RunSflowCollectorForNSecs( *ssh_client_, testbed_->Sut().ChassisName(), - kSflowtoolLineFormatTemplate, collector_port_, + SflowContainerExecCmdStr(nos_param_, kSflowtoolLineFormatTemplate), + collector_port_, /*sflowtool_runtime=*/kPacketsNum / kPacketsPerSecond + 30, sflow_result)); @@ -1717,15 +1839,16 @@ TEST_P(SflowTestFixture, VerifyIngressSamplingForNoMatchPackets) { }); // 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)); + ASSERT_OK(SendSflowTraffic(*traffic_client, {ingress_link}, *testbed_, + gnmi_stub_.get(), kPacketsNum, kPacketsPerSecond, + nos_param_.kCpuQueueName)); } LOG(INFO) << "sFlow samples:\n" << sflow_result; EXPECT_OK(testbed_->Environment().StoreTestArtifact("sflow_result.txt", sflow_result)); - VerifySflowResult(sflow_result, ingress_port.port_id, kDropPort, + VerifySflowResult(sflow_result, ingress_port.port_id, + !nos_param_.kIsSonicDebianLinux ? kDropPort : kCpuPort, kSourceMac.ToHexString(), kDstMac.ToHexString(), kEtherTypeIpv4, kIpv4Src.ToString(), GetDstIpv4AddrByPortId(ingress_link.port_id), pkt_size, @@ -1751,7 +1874,6 @@ TEST_P(SflowTestFixture, VerifyIngressSamplingForNoMatchPackets) { // Verifies ingress sampling could work when forwarding traffic. TEST_P(SflowTestFixture, VerifyIngressSamplingForForwardedPackets) { - const IxiaLink& ingress_link = ready_links_[0]; Port ingress_port = Port{ .interface_name = ingress_link.sut_interface, @@ -1780,12 +1902,11 @@ TEST_P(SflowTestFixture, VerifyIngressSamplingForForwardedPackets) { egress_next_hop_id)); const int pkt_size = 500; - // Set up Ixia traffic. ixia_ref_pair would include the traffic reference and - // topology reference which could be used to send traffic later. - std::pair, std::string> ixia_ref_pair; - ASSERT_OK_AND_ASSIGN(ixia_ref_pair, - SetUpIxiaTraffic({ingress_link}, *testbed_, kPacketsNum, - kPacketsPerSecond, pkt_size)); + + ASSERT_OK_AND_ASSIGN( + Openapi::StubInterface * traffic_client, + SetUpIxiaTraffic({ready_links_[0], ready_links_[1]}, *testbed_, + kPacketsNum, kPacketsPerSecond, *gnmi_stub_, pkt_size)); std::string sflow_result; { @@ -1794,7 +1915,8 @@ TEST_P(SflowTestFixture, VerifyIngressSamplingForForwardedPackets) { std::thread sflow_tool_thread, RunSflowCollectorForNSecs( *ssh_client_, testbed_->Sut().ChassisName(), - kSflowtoolLineFormatTemplate, collector_port_, + SflowContainerExecCmdStr(nos_param_, kSflowtoolLineFormatTemplate), + collector_port_, /*sflowtool_runtime=*/kPacketsNum / kPacketsPerSecond + 30, sflow_result)); @@ -1806,9 +1928,9 @@ TEST_P(SflowTestFixture, VerifyIngressSamplingForForwardedPackets) { }); // 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)); + ASSERT_OK(SendSflowTraffic(*traffic_client, {ingress_link}, *testbed_, + gnmi_stub_.get(), kPacketsNum, kPacketsPerSecond, + nos_param_.kCpuQueueName)); } LOG(INFO) << "sFlow samples:\n" << sflow_result; @@ -1841,7 +1963,6 @@ TEST_P(SflowTestFixture, VerifyIngressSamplingForForwardedPackets) { // Verifies ingress sampling could work when dropping packets. TEST_P(SflowTestFixture, VerifyIngressSamplesForDropPackets) { - const IxiaLink& ingress_link = ready_links_[0]; Port ingress_port = Port{ .interface_name = ingress_link.sut_interface, @@ -1851,12 +1972,10 @@ TEST_P(SflowTestFixture, VerifyIngressSamplesForDropPackets) { SetUpAclDrop(*sut_p4_session_, GetIrP4Info(), ingress_port.port_id)); const int pkt_size = 500; - // Set up Ixia traffic. ixia_ref_pair would include the traffic reference and - // topology reference which could be used to send traffic later. - std::pair, std::string> ixia_ref_pair; - ASSERT_OK_AND_ASSIGN(ixia_ref_pair, - SetUpIxiaTraffic({ingress_link}, *testbed_, kPacketsNum, - kPacketsPerSecond, pkt_size)); + ASSERT_OK_AND_ASSIGN( + Openapi::StubInterface * traffic_client, + SetUpIxiaTraffic({ready_links_[0], ready_links_[1]}, *testbed_, + kPacketsNum, kPacketsPerSecond, *gnmi_stub_, pkt_size)); std::string sflow_result; { @@ -1865,7 +1984,8 @@ TEST_P(SflowTestFixture, VerifyIngressSamplesForDropPackets) { std::thread sflow_tool_thread, RunSflowCollectorForNSecs( *ssh_client_, testbed_->Sut().ChassisName(), - kSflowtoolLineFormatTemplate, collector_port_, + SflowContainerExecCmdStr(nos_param_, kSflowtoolLineFormatTemplate), + collector_port_, /*sflowtool_runtime=*/kPacketsNum / kPacketsPerSecond + 30, sflow_result)); @@ -1877,15 +1997,16 @@ TEST_P(SflowTestFixture, VerifyIngressSamplesForDropPackets) { }); // 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)); + ASSERT_OK(SendSflowTraffic(*traffic_client, {ingress_link}, *testbed_, + gnmi_stub_.get(), kPacketsNum, kPacketsPerSecond, + nos_param_.kCpuQueueName)); } LOG(INFO) << "sFlow samples:\n" << sflow_result; EXPECT_OK(testbed_->Environment().StoreTestArtifact("sflow_result.txt", sflow_result)); - VerifySflowResult(sflow_result, ingress_port.port_id, kDropPort, + VerifySflowResult(sflow_result, ingress_port.port_id, + !nos_param_.kIsSonicDebianLinux ? kDropPort : kCpuPort, kSourceMac.ToHexString(), kDstMac.ToHexString(), kEtherTypeIpv4, kIpv4Src.ToString(), GetDstIpv4AddrByPortId(ingress_link.port_id), pkt_size, @@ -1916,7 +2037,6 @@ TEST_P(SflowTestFixture, VerifyIngressSamplesForDropPackets) { // 3. Send traffic from Ixia. // 4. Validate the packets are all get punted and sFlowtool has expected result. TEST_P(SflowTestFixture, VerifyIngressSamplesForP4rtPuntTraffic) { - const IxiaLink& ingress_link = ready_links_[0]; Port ingress_port = Port{ .interface_name = ingress_link.sut_interface, @@ -1934,12 +2054,11 @@ TEST_P(SflowTestFixture, VerifyIngressSamplesForP4rtPuntTraffic) { const int packets_num = 100000; const int traffic_speed = 400; const int packet_size = 1000; - // Set up Ixia traffic. ixia_ref_pair would include the traffic reference - // and topology reference which could be used to send traffic later. - std::pair, std::string> ixia_ref_pair; - ASSERT_OK_AND_ASSIGN(ixia_ref_pair, - SetUpIxiaTraffic({ingress_link}, *testbed_, packets_num, - traffic_speed, packet_size)); + + ASSERT_OK_AND_ASSIGN( + Openapi::StubInterface * traffic_client, + SetUpIxiaTraffic({ready_links_[0], ready_links_[1]}, *testbed_, + packets_num, traffic_speed, *gnmi_stub_, packet_size)); std::string sflow_result; absl::Time start_time; @@ -1959,7 +2078,8 @@ TEST_P(SflowTestFixture, VerifyIngressSamplesForP4rtPuntTraffic) { std::thread sflow_tool_thread, RunSflowCollectorForNSecs( *GetParam().ssh_client, testbed_->Sut().ChassisName(), - kSflowtoolLineFormatTemplate, collector_port_, + SflowContainerExecCmdStr(nos_param_, kSflowtoolLineFormatTemplate), + collector_port_, /*sflowtool_runtime=*/ packets_num / traffic_speed + 30, sflow_result)); @@ -1970,9 +2090,9 @@ TEST_P(SflowTestFixture, VerifyIngressSamplesForP4rtPuntTraffic) { }); start_time = absl::Now(); - ASSERT_OK(SendSflowTraffic(ixia_ref_pair.first, ixia_ref_pair.second, - {ingress_link}, *testbed_, gnmi_stub_.get(), - packets_num, traffic_speed)); + ASSERT_OK(SendSflowTraffic(*traffic_client, {ingress_link}, *testbed_, + gnmi_stub_.get(), packets_num, traffic_speed, + nos_param_.kCpuQueueName)); } EXPECT_OK(testbed_->Environment().StoreTestArtifact("sflow_result.txt", sflow_result)); @@ -2055,16 +2175,16 @@ TEST_P(SampleSizeTest, VerifySamplingSizeWorks) { sample_size = GetParam().sample_size; ASSERT_NE(packet_size, 0); ASSERT_NE(sample_size, 0); - // ixia_ref_pair would include the traffic reference and topology reference - // which could be used to send traffic later. - std::pair, std::string> ixia_ref_pair; + ASSERT_OK(SetSflowSamplingSize(gnmi_stub_.get(), sample_size)); const IxiaLink& ingress_link = ready_links_[0]; // Set up Ixia traffic. - ASSERT_OK_AND_ASSIGN(ixia_ref_pair, - SetUpIxiaTraffic({ingress_link}, *testbed_, kPacketsNum, - kPacketsPerSecond, packet_size)); + ASSERT_OK_AND_ASSIGN( + Openapi::StubInterface * traffic_client, + SetUpIxiaTraffic({ready_links_[0], ready_links_[1]}, *testbed_, + kPacketsNum, kPacketsPerSecond, *gnmi_stub_, + packet_size)); // Start sflowtool on SUT. std::string sflow_result; @@ -2074,7 +2194,8 @@ TEST_P(SampleSizeTest, VerifySamplingSizeWorks) { std::thread sflow_tool_thread, RunSflowCollectorForNSecs( *ssh_client_, testbed_->Sut().ChassisName(), - kSflowtoolFullFormatTemplate, collector_port_, + SflowContainerExecCmdStr(nos_param_, kSflowtoolFullFormatTemplate), + collector_port_, /*sflowtool_runtime=*/kPacketsNum / kPacketsPerSecond + 30, sflow_result)); @@ -2086,9 +2207,9 @@ TEST_P(SampleSizeTest, VerifySamplingSizeWorks) { }); // 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)); + ASSERT_OK(SendSflowTraffic(*traffic_client, {ingress_link}, *testbed_, + gnmi_stub_.get(), kPacketsNum, kPacketsPerSecond, + nos_param_.kCpuQueueName)); } LOG(INFO) << "sFlow samples with sampling size " << sample_size << ":\n" @@ -2122,13 +2243,11 @@ TEST_P(SampleRateTest, VerifySamplingRateWorks) { ASSERT_OK(SetSflowIngressSamplingRate( gnmi_stub_.get(), ingress_link.sut_interface, sample_rate)); - // ixia_ref_pair would include the traffic reference and topology reference - // which could be used to send traffic later. - std::pair, std::string> ixia_ref_pair; // Set up Ixia traffic. - ASSERT_OK_AND_ASSIGN(ixia_ref_pair, - SetUpIxiaTraffic({ingress_link}, *testbed_, packets_num, - traffic_rate, pkt_size)); + ASSERT_OK_AND_ASSIGN( + Openapi::StubInterface * traffic_client, + SetUpIxiaTraffic({ready_links_[0], ready_links_[1]}, *testbed_, + packets_num, traffic_rate, *gnmi_stub_, pkt_size)); std::string sflow_result; { @@ -2137,7 +2256,8 @@ TEST_P(SampleRateTest, VerifySamplingRateWorks) { std::thread sflow_tool_thread, RunSflowCollectorForNSecs( *ssh_client_, testbed_->Sut().ChassisName(), - kSflowtoolLineFormatTemplate, collector_port_, + SflowContainerExecCmdStr(nos_param_, kSflowtoolLineFormatTemplate), + collector_port_, /*sflowtool_runtime=*/packets_num / traffic_rate + 30, sflow_result)); @@ -2149,9 +2269,9 @@ TEST_P(SampleRateTest, VerifySamplingRateWorks) { }); // 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)); + ASSERT_OK(SendSflowTraffic(*traffic_client, {ingress_link}, *testbed_, + gnmi_stub_.get(), packets_num, traffic_rate, + nos_param_.kCpuQueueName)); } EXPECT_OK(testbed_->Environment().StoreTestArtifact( @@ -2206,8 +2326,177 @@ TEST_P(BackoffTest, VerifyBackoffWorks) { } } PerformBackoffTest(testbed_.get(), gnmi_stub_.get(), ssh_client_, - ready_links_[0], gnmi_config_with_sflow_, - sflow_enabled_interfaces, collector_port_); + {ready_links_[0], ready_links_[1]}, + gnmi_config_with_sflow_, sflow_enabled_interfaces, + collector_port_, nos_param_); +} + +// Sflow measurement test: +// Measure sflow sampling rate, CPU usage, mem usage +TEST_P(MeasurementTest, BasicMeasurementTest) { + + IxiaLink& ingress_link = ready_links_[0]; + IxiaLink& egress_link = ready_links_[1]; + for (int i = 0; i < ready_links_.size(); i++) { + if (ready_links_[i].sut_interface == "Ethernet1/1/1") { + ingress_link = ready_links_[i]; + if (i == 1) { + egress_link = ready_links_[0]; + } + } + } + + const int sample_rate = GetParam().sample_rate; + ASSERT_GT(sample_rate, 0); + int run_time = GetParam().run_time; + int pkt_size = GetParam().packet_size; + int64_t traffic_speed = GetParam().traffic_speed; // bandwidth in G + int64_t packets_num = 1000000000 / 8 / pkt_size * run_time * traffic_speed; + ASSERT_GT(packets_num, 0); + + const int64_t traffic_rate = packets_num / run_time; + LOG(INFO) << "sflow measurement test parameters: packets num: " << packets_num + << " run_time: " << run_time << " traffic rate: " << traffic_rate + << " sample_rate: " << sample_rate << " pkt_size: " << pkt_size; + LOG(INFO) << "sflow Measurement test: interface: " + << ingress_link.sut_interface; + + ASSERT_OK(SetSflowIngressSamplingRate( + gnmi_stub_.get(), ingress_link.sut_interface, sample_rate)); + LOG(INFO) << "Interface for sending traffic: " << ingress_link.sut_interface; + + ASSERT_OK_AND_ASSIGN( + Openapi::StubInterface * traffic_client, + SetUpIxiaTraffic({ingress_link, egress_link}, *testbed_, packets_num, + traffic_rate, *gnmi_stub_, pkt_size)); + + std::vector actual_result; + int its = GetParam().iterations; + for (int i = 0; i < its; ++i) { + // Start sflowtool on SUT. + LOG(INFO) << "Test_Step 1: Start sflowtool on SUT."; + std::string sflow_result; + + ASSERT_OK_AND_ASSIGN( + std::thread sflow_tool_thread, + RunSflowCollectorForNSecs( + *ssh_client_, testbed_->Sut().ChassisName(), + SflowContainerExecCmdStr(nos_param_, kSflowtoolLineFormatTemplate), + collector_port_, + /*sflowtool_runtime=*/run_time + 60, sflow_result)); + + std::string top_command_result; + std::string cpu_idle_result; + std::string memory_usage; + std::thread top_thread; + std::thread cpu_idle_thread; + std::thread memory_usage_thread; + bool measure_cpumem = GetParam().measure_cpumem; + if (measure_cpumem) { + ASSERT_OK_AND_ASSIGN( + top_thread, + RunTopCommandForSflow(*ssh_client_, testbed_->Sut().ChassisName(), + run_time, top_command_result)); + + ASSERT_OK_AND_ASSIGN( + cpu_idle_thread, + ShowCpuIdle(*ssh_client_, testbed_->Sut().ChassisName(), run_time, + cpu_idle_result)); + + ASSERT_OK_AND_ASSIGN( + memory_usage_thread, + ShowMemory(*ssh_client_, testbed_->Sut().ChassisName(), run_time, + memory_usage)); + } + + // Send packets from Ixia to SUT. + ASSERT_OK(SendSflowTraffic(*traffic_client, {ingress_link}, *testbed_, + gnmi_stub_.get(), packets_num, traffic_rate, + nos_param_.kCpuQueueName)); + + // Wait for sflowtool to finish. + if (sflow_tool_thread.joinable()) { + sflow_tool_thread.join(); + } + + if (measure_cpumem) { + if (top_thread.joinable()) { + top_thread.join(); + } + if (cpu_idle_thread.joinable()) { + cpu_idle_thread.join(); + } + if (memory_usage_thread.joinable()) { + memory_usage_thread.join(); + } + } + + EXPECT_OK(testbed_->Environment().StoreTestArtifact( + absl::Substitute("sflow_result_sampling_rate_$0_run_$1_result.txt", + sample_rate, i), + sflow_result)); + + if (measure_cpumem) { + EXPECT_OK(testbed_->Environment().AppendToTestArtifact( + "sflow_top_result.txt", + absl::Substitute( + "\n\n\n==================Test_run: $0==================\n\n\n", + i))); + EXPECT_OK(testbed_->Environment().AppendToTestArtifact( + "sflow_top_result.txt", top_command_result)); + EXPECT_OK(testbed_->Environment().AppendToTestArtifact( + "cpu_idle_rate.txt", + absl::Substitute( + "\n\n\n==================Test_run: $0==================\n\n\n", + i))); + EXPECT_OK(testbed_->Environment().AppendToTestArtifact( + "cpu_idle_rate.txt", cpu_idle_result)); + EXPECT_OK(testbed_->Environment().AppendToTestArtifact( + "memory_usage.txt", + absl::Substitute( + "\n\n\n==================Test_run: $0==================\n\n\n", + i))); + EXPECT_OK(testbed_->Environment().AppendToTestArtifact("memory_usage.txt", + memory_usage)); + } + + VerifySflowResult( + sflow_result, ingress_link.port_id, + /*output_port=*/std::nullopt, kSourceMac.ToHexString(), + kDstMac.ToHexString(), kEtherTypeIpv4, kIpv4Src.ToString(), + GetDstIpv4AddrByPortId(ingress_link.port_id), pkt_size, sample_rate); + + // Verify sflowtool result. Since we use port id to generate packets, we use + // port id to filter sFlow packets. + const int sample_count = + GetSflowSamplesOnSut(sflow_result, ingress_link.port_id); + LOG(INFO) << "Test start to verify sflowtool result, sample_count: " + << sample_count; + const double expected_count = + static_cast(packets_num) / static_cast(sample_rate); + SflowResult result = SflowResult{ + .sut_interface = ingress_link.sut_interface, + .packets = packets_num, + .sampling_rate = sample_rate, + .expected_samples = static_cast(expected_count), + .actual_samples = sample_count, + }; + LOG(INFO) << "------ Test result ------\n" << result.DebugString(); + actual_result.push_back(sample_count); + + // TODO: tune the tolerance rate of sampling rate test + // since sample count seems like to be more deviated when the sample rate + // is high. + EXPECT_GE(sample_count, expected_count * (1 - kTolerance)) + << "Not enough samples on " << ingress_link.sut_interface; + EXPECT_LE(sample_count, expected_count * (1 + kTolerance)); + } + LOG(INFO) << "Test overall result saved"; + EXPECT_OK(testbed_->Environment().StoreTestArtifact( + absl::Substitute( + "sflow_result_sampling_rate_$0_pkt_size_$1_result_samples.txt", + sample_rate, pkt_size), + absl::StrJoin(actual_result, "\n"))); } // 1. Perform backoff on a switch. @@ -2222,7 +2511,8 @@ TEST_P(BackoffTest, VerifyBackOffWorksAfterNsf) { GTEST_SKIP() << "NSF is disabled, skip VerifyBackOffWorksAfterNsf test."; } - ASSERT_GE(ready_links_.size(), 2) << "Needs two ready ixia links for testing"; + ASSERT_GE(ready_links_.size(), 4) + << "Needs four ready ixia links for testing"; absl::flat_hash_map sflow_interfaces; ASSERT_OK_AND_ASSIGN(sflow_interfaces, GetSflowInterfacesFromSut(*testbed_)); absl::flat_hash_set sflow_enabled_interfaces; @@ -2235,8 +2525,9 @@ TEST_P(BackoffTest, VerifyBackOffWorksAfterNsf) { { SCOPED_TRACE("Backoff test before NSF"); ASSERT_NO_FATAL_FAILURE(PerformBackoffTest( - testbed_.get(), gnmi_stub_.get(), ssh_client_, ready_links_[1], - gnmi_config_with_sflow_, sflow_enabled_interfaces, collector_port_)); + testbed_.get(), gnmi_stub_.get(), ssh_client_, + {ready_links_[2], ready_links_[3]}, gnmi_config_with_sflow_, + sflow_enabled_interfaces, collector_port_, nos_param_)); } absl::flat_hash_map interfaces_to_sample_rate; ASSERT_OK_AND_ASSIGN(interfaces_to_sample_rate, @@ -2261,7 +2552,7 @@ TEST_P(BackoffTest, VerifyBackOffWorksAfterNsf) { ASSERT_OK(pins_test::WaitForCondition( CheckStateDbPortIndexTableExists, absl::Minutes(2), *ssh_client_, - testbed_->Sut().ChassisName(), + testbed_->Sut().ChassisName(), nos_param_.kRedisCliPath, std::vector{sflow_enabled_interfaces.begin(), sflow_enabled_interfaces.end()})); @@ -2273,8 +2564,9 @@ TEST_P(BackoffTest, VerifyBackOffWorksAfterNsf) { { SCOPED_TRACE("Backoff test after NSF"); ASSERT_NO_FATAL_FAILURE(PerformBackoffTest( - testbed_.get(), gnmi_stub_.get(), ssh_client_, ready_links_[0], - gnmi_config_with_sflow_, sflow_enabled_interfaces, collector_port_)); + testbed_.get(), gnmi_stub_.get(), ssh_client_, + {ready_links_[0], ready_links_[1]}, gnmi_config_with_sflow_, + sflow_enabled_interfaces, collector_port_, nos_param_)); } } @@ -2285,11 +2577,13 @@ void SflowNsfTestFixture::TearDown() { testbed_->Sut(), testbed_->Environment())); ASSERT_OK(pins_test::WaitForReboot(testbed_.get(), *ssh_client_, /*check_interfaces_up=*/false)); + // Restore P4 session after cold reboot. ASSERT_OK_AND_ASSIGN( sut_p4_session_, pins_test::ConfigureSwitchAndReturnP4RuntimeSession( testbed_->Sut(), gnmi_config_with_sflow_, GetP4Info())); + SflowTestFixture::TearDown(); } @@ -2309,8 +2603,9 @@ TEST_P(SflowNsfTestFixture, VerifySflowAfterFreezeAndUnfreeze) { } ASSERT_OK_AND_ASSIGN( IxiaTrafficSettings ixia_traffic_settings, - SetUpIxiaTrafficForSflowNsf(ready_links_[0], sflow_enabled_interfaces, - testbed_.get(), gnmi_stub_.get())); + SetUpIxiaTrafficForSflowNsf({ready_links_[0], ready_links_[1]}, + sflow_enabled_interfaces, testbed_.get(), + gnmi_stub_.get())); const int64_t pkt_count = ixia_traffic_settings.traffic_rate * kNsfTrafficTestDurationSecs; // The minimum percentage of samples that should be recorded during the test. @@ -2319,29 +2614,32 @@ TEST_P(SflowNsfTestFixture, VerifySflowAfterFreezeAndUnfreeze) { constexpr double kMinExpectedSamplesPct = 0.7; const int64_t expected_number_of_samples = pkt_count / ixia_traffic_settings.sampling_rate; + // Record initial Ixia counters to verify that traffic is only sent on the // first interface and that the expected number of packets are sent. This // logic has to be performed outside of // `GetSflowSamplesAfterSendingIxiaTraffic` since that function might be // called while the switch is frozen, and the gNMI interface would be // unavailable. - ASSERT_OK_AND_ASSIGN(IxiaCounterStats ixia_counters_before_nsf, - GetIxiaCounterStats(ready_links_, gnmi_stub_.get(), - /*pkt_count=*/pkt_count)); + ASSERT_OK_AND_ASSIGN( + IxiaCounterStats ixia_counters_before_nsf, + GetIxiaCounterStats(ready_links_, gnmi_stub_.get(), + /*pkt_count=*/pkt_count, nos_param_.kCpuQueueName)); // More than 0 samples are recorded before NSF. EXPECT_THAT( GetSflowSamplesAfterSendingIxiaTraffic( testbed_.get(), ssh_client_, ready_links_[0], ixia_traffic_settings, - collector_port_), + collector_port_, nos_param_), IsOkAndHolds(Gt(expected_number_of_samples * kMinExpectedSamplesPct))); // Validate counters after sending Ixia traffic. EXPECT_OK(ValidateIxiaInterfaceCounters(ready_links_, gnmi_stub_.get(), ixia_counters_before_nsf)); constexpr int kNumNsfReboots = 2; for (int i = 0; i < kNumNsfReboots; i++) { - ASSERT_OK_AND_ASSIGN(IxiaCounterStats ixia_counters_during_nsf, - GetIxiaCounterStats(ready_links_, gnmi_stub_.get(), - /*pkt_count=*/pkt_count)); + ASSERT_OK_AND_ASSIGN( + IxiaCounterStats ixia_counters_during_nsf, + GetIxiaCounterStats(ready_links_, gnmi_stub_.get(), + /*pkt_count=*/pkt_count, nos_param_.kCpuQueueName)); // Freeze the switch. ASSERT_OK(ssh_client_ ->RunCommand(testbed_.get()->Sut().ChassisName(), kFreeze, @@ -2351,7 +2649,7 @@ TEST_P(SflowNsfTestFixture, VerifySflowAfterFreezeAndUnfreeze) { // Flow sampling is disabled (0 samples recorded) during NSF. EXPECT_THAT(GetSflowSamplesAfterSendingIxiaTraffic( testbed_.get(), ssh_client_, ready_links_[0], - ixia_traffic_settings, collector_port_), + ixia_traffic_settings, collector_port_, nos_param_), IsOkAndHolds(Eq(0))); // Unfreeze the switch. ASSERT_OK(ssh_client_ @@ -2361,14 +2659,15 @@ TEST_P(SflowNsfTestFixture, VerifySflowAfterFreezeAndUnfreeze) { absl::SleepFor(kFreezeTimeout); EXPECT_OK(ValidateIxiaInterfaceCounters(ready_links_, gnmi_stub_.get(), ixia_counters_during_nsf)); - ASSERT_OK_AND_ASSIGN(IxiaCounterStats ixia_counters_after_nsf, - GetIxiaCounterStats(ready_links_, gnmi_stub_.get(), - /*pkt_count=*/pkt_count)); + ASSERT_OK_AND_ASSIGN( + IxiaCounterStats ixia_counters_after_nsf, + GetIxiaCounterStats(ready_links_, gnmi_stub_.get(), + /*pkt_count=*/pkt_count, nos_param_.kCpuQueueName)); // More than 0 samples are recorded after switch is unfrozen. EXPECT_THAT( GetSflowSamplesAfterSendingIxiaTraffic( testbed_.get(), ssh_client_, ready_links_[0], ixia_traffic_settings, - collector_port_), + collector_port_, nos_param_), IsOkAndHolds(Gt(expected_number_of_samples * kMinExpectedSamplesPct))); EXPECT_OK(ValidateIxiaInterfaceCounters(ready_links_, gnmi_stub_.get(), ixia_counters_after_nsf)); @@ -2432,7 +2731,7 @@ absl::Status SendNPacketsFromSwitch( LOG(INFO) << "Ingress Deltas (" << interface << "):\n"; ShowCounters(delta); // There might be some bearable drop. - EXPECT_GE(delta.in_pkts, (double)0.85 * num_packets) + EXPECT_GE(delta.in_pkts, (double)0.75 * num_packets) << "Sent " << num_packets << " on interface: " << interface << " port_id: " << port_id << ". Received: " << delta.in_pkts; @@ -2760,11 +3059,13 @@ void SflowMirrorTestFixture::SetUp() { thinkit::MirrorTestbed& testbed = GetParam().testbed_interface->GetMirrorTestbed(); + nos_param_ = GetParam().nos_is_sonic ? nos_param_sonic : GetParam().nos_param; + // Push gNMI config to SUT switch. const std::string& sut_gnmi_config = GetParam().sut_gnmi_config; ASSERT_OK(testbed.Environment().StoreTestArtifact("sut_gnmi_config.txt", sut_gnmi_config)); - + collector_port_ = GetSflowCollectorPort(); ASSERT_OK_AND_ASSIGN( sut_p4_session_, @@ -2774,6 +3075,19 @@ void SflowMirrorTestFixture::SetUp() { sut_p4_info_, pdpi::GetOrSetP4Info(*sut_p4_session_, GetParam().sut_p4_info)); ASSERT_OK_AND_ASSIGN(sut_ir_p4_info_, pdpi::GetIrP4Info(*sut_p4_session_)); + + ASSERT_OK(GetParam().ssh_client->RunCommand( + testbed.Sut().ChassisName(), + /*command=*/ + nos_param_.kRedisExecPrefix + + "redis-cli -n 14 hset \"PORT_TABLE:CPU\" \"NULL\" \"NULL\"", + absl::Seconds(20))); + ASSERT_OK(GetParam().ssh_client->RunCommand( + testbed.ControlSwitch().ChassisName(), + /*command=*/ + nos_param_.kRedisExecPrefix + + "redis-cli -n 14 hset \"PORT_TABLE:CPU\" \"NULL\" \"NULL\"", + absl::Seconds(20))); // Push gNMI config to control switch. const std::string& control_gnmi_config = GetParam().control_gnmi_config; @@ -2807,13 +3121,13 @@ void SflowMirrorTestFixture::SetUp() { agent_address_ = sut_loopback0_ipv6s[0].ToString(); CollectSflowDebugs(GetParam().ssh_client, testbed.Sut().ChassisName(), - /*prefix=*/"pretest_", testbed.Environment()); + /*prefix=*/"pretest_", testbed.Environment(), nos_param_); } void SflowMirrorTestFixture::TearDown() { auto& testbed = GetParam().testbed_interface->GetMirrorTestbed(); CollectSflowDebugs(GetParam().ssh_client, testbed.Sut().ChassisName(), - /*prefix=*/"posttest_", testbed.Environment()); + /*prefix=*/"posttest_", testbed.Environment(), nos_param_); if (sut_p4_session_ != nullptr) { EXPECT_OK(pdpi::ClearTableEntries(sut_p4_session_.get())); EXPECT_OK(sut_p4_session_->Finish()); @@ -2832,7 +3146,7 @@ void SflowMirrorTestFixture::TearDown() { absl::Status SflowMirrorTestFixture::NsfRebootAndWaitForConvergence( thinkit::MirrorTestbed& testbed, absl::string_view gnmi_config) { CollectSflowDebugs(GetParam().ssh_client, testbed.Sut().ChassisName(), - /*prefix=*/"pre_nsf_", testbed.Environment()); + /*prefix=*/"pre_nsf_", testbed.Environment(), nos_param_); LOG(INFO) << "Start NSF reboot on switch"; ASSIGN_OR_RETURN(::p4::v1::ReadResponse p4flow_snapshot_before_nsf, pins_test::TakeP4FlowSnapshot(testbed.Sut())); @@ -2842,7 +3156,7 @@ absl::Status SflowMirrorTestFixture::NsfRebootAndWaitForConvergence( RETURN_IF_ERROR(pins_test::DoNsfRebootAndWaitForSwitchReadyOrRecover( &testbed, *GetParam().ssh_client, &image_config_params)); CollectSflowDebugs(GetParam().ssh_client, testbed.Sut().ChassisName(), - /*prefix=*/"post_nsf_", testbed.Environment()); + /*prefix=*/"post_nsf_", testbed.Environment(), nos_param_); ASSIGN_OR_RETURN(::p4::v1::ReadResponse p4flow_snapshot_after_nsf, pins_test::TakeP4FlowSnapshot(testbed.Sut())); RETURN_IF_ERROR(pins_test::CompareP4FlowSnapshots(p4flow_snapshot_before_nsf, @@ -2863,7 +3177,6 @@ TEST_P(SflowRebootTestFixture, ChangeCollectorConfigOnNsfReboot) { GTEST_SKIP() << "Skip TestNsfUpgradeGnpsiCollector since NSF is not enabled."; } - // Configure SUT with control switch loopback0 ip as collector. ASSERT_OK_AND_ASSIGN( auto control_loopback0_ipv6s, @@ -2936,7 +3249,8 @@ TEST_P(SflowRebootTestFixture, ChangeCollectorConfigOnNsfReboot) { } ASSERT_OK(pins_test::WaitForCondition( CheckStateDbPortIndexTableExists, absl::Minutes(2), - *GetParam().ssh_client, testbed.Sut().ChassisName(), interface_names)); + *GetParam().ssh_client, testbed.Sut().ChassisName(), + nos_param_.kRedisCliPath, interface_names)); ASSERT_OK(pins_test::WaitForCondition(pins_test::PortsUp, absl::Minutes(3), testbed.Sut(), interface_names, /*with_healthz=*/false)); @@ -2997,17 +3311,19 @@ TEST_P(SflowRebootTestFixture, ChangeCollectorConfigOnNsfReboot) { std::thread control_sflowtool_thread, RunSflowCollectorForNSecs( *GetParam().ssh_client, testbed.ControlSwitch().ChassisName(), - kSflowtoolLineFormatTemplate, collector_port_, + SflowContainerExecCmdStr(nos_param_, kSflowtoolLineFormatTemplate), + collector_port_, /*sflowtool_runtime=*/30, control_sflow_result)); ASSERT_OK_AND_ASSIGN( std::thread sut_sflowtool_thread, - RunSflowCollectorForNSecs(*GetParam().ssh_client, - testbed.Sut().ChassisName(), - kSflowtoolLineFormatTemplate, collector_port_, - /*sflowtool_runtime=*/30, sut_sflow_result)); - - ASSERT_OK_AND_ASSIGN(std::string sflow_queue_name, - GetSflowQueueName(sut_gnmi_stub_.get())); + RunSflowCollectorForNSecs( + *GetParam().ssh_client, testbed.Sut().ChassisName(), + SflowContainerExecCmdStr(nos_param_, kSflowtoolLineFormatTemplate), + collector_port_, + /*sflowtool_runtime=*/30, sut_sflow_result)); + ASSERT_OK_AND_ASSIGN( + std::string sflow_queue_name, + GetSflowQueueName(sut_gnmi_stub_.get(), nos_param_.kCpuQueueName)); ASSERT_OK_AND_ASSIGN(auto initial_queue_counter, pins_test::GetGnmiQueueCounters( "CPU", sflow_queue_name, *sut_gnmi_stub_)); @@ -3102,6 +3418,9 @@ TEST_P(SflowRebootTestFixture, ChangeCollectorConfigOnNsfReboot) { // | control | <------- | SUT | TEST_P(SflowMirrorTestFixture, TestInbandPathToSflowCollector) { + if (!GetParam().run_all_tests) { + GTEST_SKIP() << "Skip the test on new platform, to be run later"; + } thinkit::MirrorTestbed& testbed = GetParam().testbed_interface->GetMirrorTestbed(); @@ -3175,19 +3494,22 @@ TEST_P(SflowMirrorTestFixture, TestInbandPathToSflowCollector) { std::thread control_sflowtool_thread, RunSflowCollectorForNSecs( *GetParam().ssh_client, testbed.ControlSwitch().ChassisName(), - kSflowtoolLineFormatTemplate, collector_port_, + SflowContainerExecCmdStr(nos_param_, kSflowtoolLineFormatTemplate), + collector_port_, /*sflowtool_runtime=*/packets_num / kInbandTrafficPps + 30, control_sflow_result)); ASSERT_OK_AND_ASSIGN( std::thread sut_sflowtool_thread, RunSflowCollectorForNSecs( *GetParam().ssh_client, testbed.Sut().ChassisName(), - kSflowtoolLineFormatTemplate, collector_port_, + SflowContainerExecCmdStr(nos_param_, kSflowtoolLineFormatTemplate), + collector_port_, /*sflowtool_runtime=*/packets_num / kInbandTrafficPps + 30, sut_sflow_result)); - ASSERT_OK_AND_ASSIGN(std::string sflow_queue_name, - GetSflowQueueName(sut_gnmi_stub_.get())); + ASSERT_OK_AND_ASSIGN( + std::string sflow_queue_name, + GetSflowQueueName(sut_gnmi_stub_.get(), nos_param_.kCpuQueueName)); ASSERT_OK_AND_ASSIGN(auto initial_queue_counter, pins_test::GetGnmiQueueCounters( "CPU", sflow_queue_name, *sut_gnmi_stub_)); @@ -3295,6 +3617,9 @@ TEST_P(SflowMirrorTestFixture, TestSflowDscpValue) { ASSERT_OK_AND_ASSIGN(Port traffic_port, GetUnusedUpPort(*sut_gnmi_stub_, /*used_port=*/"")); + LOG(INFO) << "Test parameters: traffic port name: " + << traffic_port.interface_name << " id: " << traffic_port.port_id + << " collector port: " << collector_port_; absl::flat_hash_map sflow_enabled_interfaces{ {traffic_port.interface_name, true}}; @@ -3308,6 +3633,7 @@ TEST_P(SflowMirrorTestFixture, TestSflowDscpValue) { ASSERT_OK(testbed.Environment().StoreTestArtifact( "sut_gnmi_config_with_sflow.txt", json_yang::FormatJsonBestEffort(sut_gnmi_config_with_sflow))); + LOG(INFO) << "Test gnmi config: " << sut_gnmi_config_with_sflow; ASSERT_OK( pins_test::PushGnmiConfig(testbed.Sut(), sut_gnmi_config_with_sflow)); @@ -3332,19 +3658,21 @@ TEST_P(SflowMirrorTestFixture, TestSflowDscpValue) { const int runtime_secs = packets_num / kInbandTrafficPps + 30; ASSERT_OK_AND_ASSIGN( std::thread sflow_tool_thread, - RunSflowCollectorForNSecs(*GetParam().ssh_client, - testbed.Sut().ChassisName(), - kSflowtoolLineFormatTemplate, collector_port_, - runtime_secs, sflow_result)); + RunSflowCollectorForNSecs( + *GetParam().ssh_client, testbed.Sut().ChassisName(), + SflowContainerExecCmdStr(nos_param_, kSflowtoolLineFormatTemplate), + collector_port_, runtime_secs, sflow_result)); ASSERT_OK_AND_ASSIGN( std::thread tcpdump_thread, CaptureTcpdumpForNPackets(*GetParam().ssh_client, testbed.Sut().ChassisName(), /*packets_count=*/5, collector_port_, - runtime_secs, tcpdump_result)); + runtime_secs, nos_param_, tcpdump_result)); - ASSERT_OK_AND_ASSIGN(std::string sflow_queue_name, - GetSflowQueueName(sut_gnmi_stub_.get())); + ASSERT_OK_AND_ASSIGN( + std::string sflow_queue_name, + GetSflowQueueName(sut_gnmi_stub_.get(), nos_param_.kCpuQueueName)); + LOG(INFO) << "Test using sflow queue: " << sflow_queue_name; ASSERT_OK_AND_ASSIGN(auto initial_queue_counter, pins_test::GetGnmiQueueCounters( "CPU", sflow_queue_name, *sut_gnmi_stub_)); @@ -3377,14 +3705,15 @@ TEST_P(SflowMirrorTestFixture, TestSflowDscpValue) { traffic_port.interface_name, sut_gnmi_stub_.get(), GetControlIrP4Info(), *control_p4_session_, testbed.Environment())); } + EXPECT_OK(testbed.Environment().StoreTestArtifact("sflow_result.txt", + sflow_result)); EXPECT_OK(testbed.Environment().StoreTestArtifact("tcpdump_result.txt", tcpdump_result)); ASSERT_THAT(ExtractTosFromTcpdumpResult(tcpdump_result), IsOkAndHolds(kSflowOutPacketsTos)); - EXPECT_OK(testbed.Environment().StoreTestArtifact("sflow_result.txt", - sflow_result)); - - VerifySflowResult(sflow_result, traffic_port.port_id, kDropPort, + + VerifySflowResult(sflow_result, traffic_port.port_id, + !nos_param_.kIsSonicDebianLinux ? kDropPort : kCpuPort, kSourceMac.ToHexString(), kDstMac.ToHexString(), kEtherTypeIpv4, kIpv4Src.ToString(), GetDstIpv4AddrByPortId(control_switch_port_id), @@ -3440,12 +3769,14 @@ TEST_P(SflowMirrorTestFixture, TestSamplingWorksOnAllInterfaces) { { ASSERT_OK_AND_ASSIGN( std::thread sflow_tool_thread, - RunSflowCollectorNonStop(*GetParam().ssh_client, - testbed.Sut().ChassisName(), - kSflowtoolLineFormatNonStopTemplate, - collector_port_, sflow_result)); - ASSERT_OK_AND_ASSIGN(std::string sflow_queue_name, - GetSflowQueueName(sut_gnmi_stub_.get())); + RunSflowCollectorNonStop( + *GetParam().ssh_client, testbed.Sut().ChassisName(), + SflowContainerExecCmdStr(nos_param_, + kSflowtoolLineFormatNonStopTemplate), + collector_port_, sflow_result)); + ASSERT_OK_AND_ASSIGN( + std::string sflow_queue_name, + GetSflowQueueName(sut_gnmi_stub_.get(), nos_param_.kCpuQueueName)); ASSERT_OK_AND_ASSIGN(auto initial_queue_counter, pins_test::GetGnmiQueueCounters( "CPU", sflow_queue_name, *sut_gnmi_stub_)); @@ -3581,7 +3912,8 @@ TEST_P(SflowRebootTestFixture, TestSamplingWorksAfterReboot) { std::thread sflow_tool_thread, RunSflowCollectorForNSecs( *GetParam().ssh_client, testbed.Sut().ChassisName(), - kSflowtoolLineFormatTemplate, collector_port_, + SflowContainerExecCmdStr(nos_param_, kSflowtoolLineFormatTemplate), + collector_port_, /*sflowtool_runtime=*/ (num_packets / kInbandTrafficPps + 3) * traffic_interfaces_and_port_ids.size() + @@ -3675,8 +4007,14 @@ TEST_P(SflowRebootTestFixture, TestSamplingWorksAfterReboot) { collector_address_and_port, sflow_enabled_interfaces)); ASSERT_OK(pins_test::WaitForCondition( CheckStateDbPortIndexTableExists, absl::Minutes(2), - *GetParam().ssh_client, testbed.Sut().ChassisName(), interface_names)); + *GetParam().ssh_client, testbed.Sut().ChassisName(), + nos_param_.kRedisCliPath, interface_names)); LOG(INFO) << "Sflow states are converged after reboot."; + + if (nos_param_.kIsSonicDebianLinux) { + // Sleep for 2 more minutes + absl::SleepFor(absl::Seconds(120)); + } for (const auto& [interface_name, unused] : sflow_enabled_interfaces) { ASSERT_OK_AND_ASSIGN(packets_sampled_per_interface[interface_name], @@ -3689,10 +4027,11 @@ TEST_P(SflowRebootTestFixture, TestSamplingWorksAfterReboot) { // Start sflowtool on SUT. ASSERT_OK_AND_ASSIGN( std::thread sflow_tool_thread, - RunSflowCollectorNonStop(*GetParam().ssh_client, - testbed.Sut().ChassisName(), - kSflowtoolLineFormatNonStopTemplate, - collector_port_, sflow_result)); + RunSflowCollectorNonStop( + *GetParam().ssh_client, testbed.Sut().ChassisName(), + SflowContainerExecCmdStr(nos_param_, + kSflowtoolLineFormatNonStopTemplate), + collector_port_, sflow_result)); // Wait for sflowtool to finish. auto clean_up = absl::Cleanup( @@ -3822,7 +4161,8 @@ TEST_P(SflowRebootTestFixture, TestNoSamplesOnDisabledInterfacesAfterReboot) { collector_address_and_port, sflow_enabled_interfaces)); ASSERT_OK(pins_test::WaitForCondition( CheckStateDbPortIndexTableExists, absl::Seconds(30), - *GetParam().ssh_client, testbed.Sut().ChassisName(), traffic_interfaces)); + *GetParam().ssh_client, testbed.Sut().ChassisName(), + nos_param_.kRedisCliPath, traffic_interfaces)); const int num_packets = 10000; std::string sflow_result; @@ -3830,11 +4170,11 @@ TEST_P(SflowRebootTestFixture, TestNoSamplesOnDisabledInterfacesAfterReboot) { // Start sflowtool on SUT. ASSERT_OK_AND_ASSIGN( std::thread sflow_tool_thread, - RunSflowCollectorNonStop(*GetParam().ssh_client, - testbed.Sut().ChassisName(), - kSflowtoolLineFormatNonStopTemplate, - collector_port_, sflow_result)); - + RunSflowCollectorNonStop( + *GetParam().ssh_client, testbed.Sut().ChassisName(), + SflowContainerExecCmdStr(nos_param_, + kSflowtoolLineFormatNonStopTemplate), + collector_port_, sflow_result)); // Wait for sflowtool to finish. auto clean_up = absl::Cleanup( [&sflow_tool_thread, &chassis_name = testbed.Sut().ChassisName()] { @@ -3898,17 +4238,24 @@ TEST_P(SflowRebootTestFixture, TestNoSamplesOnDisabledInterfacesAfterReboot) { collector_address_and_port, sflow_enabled_interfaces)); ASSERT_OK(pins_test::WaitForCondition( CheckStateDbPortIndexTableExists, absl::Minutes(2), - *GetParam().ssh_client, testbed.Sut().ChassisName(), traffic_interfaces)); + *GetParam().ssh_client, testbed.Sut().ChassisName(), + nos_param_.kRedisCliPath, traffic_interfaces)); LOG(INFO) << "Sflow states are converged after reboot."; + + if (nos_param_.kIsSonicDebianLinux) { + // Sleep for 2 more minutes + absl::SleepFor(absl::Seconds(120)); + } { // Start sflowtool on SUT. ASSERT_OK_AND_ASSIGN( std::thread sflow_tool_thread, - RunSflowCollectorNonStop(*GetParam().ssh_client, - testbed.Sut().ChassisName(), - kSflowtoolLineFormatNonStopTemplate, - collector_port_, sflow_result)); + RunSflowCollectorNonStop( + *GetParam().ssh_client, testbed.Sut().ChassisName(), + SflowContainerExecCmdStr(nos_param_, + kSflowtoolLineFormatNonStopTemplate), + collector_port_, sflow_result)); // Wait for sflowtool to finish. auto clean_up = absl::Cleanup( @@ -4024,7 +4371,9 @@ TEST_P(SflowMirrorTestFixture, TestIp2MePacketsAreSampledAndPunted) { ShowCounters(delta); LOG(INFO) << "End verifying punting packets to CPU works"; // Make sure at least a certain percent of packets are punted to CPU. - if (delta.in_pkts < packets_num * kPassingPercentageCpuPunted / 100) { + uint64_t cpu_recv = + nos_param_.kIsSonicDebianLinux ? delta.out_pkts : delta.in_pkts; + if (cpu_recv < packets_num * kPassingPercentageCpuPunted / 100) { GTEST_SKIP() << "The SUT switch does not punt expected number of packets to CPU." << " Expected: " << packets_num << ". Actual: " << delta.in_pkts; @@ -4066,7 +4415,8 @@ TEST_P(SflowMirrorTestFixture, TestIp2MePacketsAreSampledAndPunted) { std::thread sflow_tool_thread, RunSflowCollectorForNSecs( *GetParam().ssh_client, testbed.Sut().ChassisName(), - kSflowtoolLineFormatTemplate, collector_port_, + SflowContainerExecCmdStr(nos_param_, kSflowtoolLineFormatTemplate), + collector_port_, /*sflowtool_runtime=*/ packets_num / traffic_speed + 20, sflow_result)); @@ -4120,6 +4470,9 @@ TEST_P(SflowMirrorTestFixture, TestIp2MePacketsAreSampledAndPunted) { // affecting the other tests in longevity workflow. Enable the test once the // issue related to minor alarms is fixed. TEST_P(SflowRebootTestFixture, TestHsflowdRestartSucceed) { + if (!GetParam().run_all_tests) { + GTEST_SKIP() << "Skip the test on new platform, to be run later"; + } thinkit::MirrorTestbed& testbed = GetParam().testbed_interface->GetMirrorTestbed(); auto& ssh_client = *GetParam().ssh_client; @@ -4144,7 +4497,7 @@ TEST_P(SflowRebootTestFixture, TestHsflowdRestartSucceed) { } return absl::FailedPreconditionError( absl::Substitute("Expected: hsflowd pid should exist and differ " - "from $0. Acutal: hsflowd pid: $1.", + "from $0. Actual: hsflowd pid: $1.", pid, hsflowd_pid)); }, absl::Seconds(120))); @@ -4168,6 +4521,9 @@ TEST_P(SflowRebootTestFixture, TestHsflowdRestartSucceed) { // 3. Send packets from control switch via these new ports. // 4. Verify there are samples generated for each interface. TEST_P(SflowPortBreakoutTest, TestPortbreakoutWorks) { + if (!GetParam().run_all_tests) { + GTEST_SKIP() << "Skip the test on new platform, to be run later"; + } GetParam().testbed_interface->ExpectLinkFlaps(); // Set collector address. thinkit::MirrorTestbed& testbed = @@ -4185,6 +4541,9 @@ TEST_P(SflowPortBreakoutTest, TestPortbreakoutWorks) { GetParam().control_gnmi_config, control_loopback0_ipv6s[0].ToString())); // Perform breakout on SUT. + CollectSflowDebugs(GetParam().ssh_client, testbed.Sut().ChassisName(), + /*prefix=*/"predpb_sut_", testbed.Environment(), + nos_param_); pins_test::SflowBreakoutTestOption sut_option{ .sampling_rate = kInbandSamplingRate, .sampling_header_size = kSampleHeaderSize, @@ -4194,15 +4553,24 @@ TEST_P(SflowPortBreakoutTest, TestPortbreakoutWorks) { .mirror_broken_out = false, }; ASSERT_OK_AND_ASSIGN( - std::string platform_json_contents, + std::string platform_json_contents_sut, FetchPlatformJsonContents(*GetParam().ssh_client, testbed.Sut(), GetParam().platform_json_path)); + ASSERT_OK(testbed.Environment().StoreTestArtifact( + "predpb_sut_platform_json.txt", platform_json_contents_sut)); ASSERT_OK_AND_ASSIGN( const pins_test::SflowBreakoutResult& sut_breakout_result, - pins_test::TestBreakoutWithSflowConfig( - testbed.Sut(), platform_json_contents, GetSutP4Info(), sut_option)); + pins_test::TestBreakoutWithSflowConfig(testbed.Sut(), + platform_json_contents_sut, + GetSutP4Info(), sut_option)); + CollectSflowDebugs(GetParam().ssh_client, testbed.Sut().ChassisName(), + /*prefix=*/"postdpb_sut_", testbed.Environment(), + nos_param_); // Perform breakout on control switch with the same ports as SUT. + CollectSflowDebugs( + GetParam().ssh_client, testbed.ControlSwitch().ChassisName(), + /*prefix=*/"predpb_control_", testbed.Environment(), nos_param_); pins_test::SflowBreakoutTestOption control_option{ .sampling_rate = kInbandSamplingRate, .sampling_header_size = kSampleHeaderSize, @@ -4217,11 +4585,20 @@ TEST_P(SflowPortBreakoutTest, TestPortbreakoutWorks) { &config = GetParam().control_gnmi_config] { ASSERT_OK(pins_test::PushGnmiConfig(control_switch, config)); }); + ASSERT_OK_AND_ASSIGN( + std::string platform_json_contents_control, + FetchPlatformJsonContents(*GetParam().ssh_client, testbed.ControlSwitch(), + GetParam().platform_json_path)); + ASSERT_OK(testbed.Environment().StoreTestArtifact( + "predpb_control_platform_json.txt", platform_json_contents_control)); ASSERT_OK_AND_ASSIGN( const pins_test::SflowBreakoutResult& control_breakout_result, TestBreakoutWithSflowConfig(testbed.ControlSwitch(), - platform_json_contents, GetControlP4Info(), - control_option)); + platform_json_contents_control, + GetControlP4Info(), control_option)); + CollectSflowDebugs( + GetParam().ssh_client, testbed.ControlSwitch().ChassisName(), + /*prefix=*/"postdpb_control_", testbed.Environment(), nos_param_); ASSERT_THAT( sut_breakout_result.breakout_ports, UnorderedElementsAreArray(control_breakout_result.breakout_ports)); @@ -4246,14 +4623,16 @@ TEST_P(SflowPortBreakoutTest, TestPortbreakoutWorks) { std::thread sflow_tool_thread, RunSflowCollectorForNSecs( *GetParam().ssh_client, testbed.Sut().ChassisName(), - kSflowtoolLineFormatTemplate, collector_port_, + SflowContainerExecCmdStr(nos_param_, kSflowtoolLineFormatTemplate), + collector_port_, /*sflowtool_runtime=*/ (packets_num / kInbandTrafficPps + 3) * sut_breakout_result.breakout_ports.size() + 100, sflow_result)); - ASSERT_OK_AND_ASSIGN(const auto kSflowQueueName, - GetSflowQueueName(sut_gnmi_stub_.get())); + ASSERT_OK_AND_ASSIGN( + const auto kSflowQueueName, + GetSflowQueueName(sut_gnmi_stub_.get(), nos_param_.kCpuQueueName)); ASSERT_OK_AND_ASSIGN(auto initial_queue_counter, pins_test::GetGnmiQueueCounters("CPU", kSflowQueueName, *sut_gnmi_stub_)); diff --git a/tests/sflow/sflow_test.h b/tests/sflow/sflow_test.h index 846d74d28..67dbe4bfb 100644 --- a/tests/sflow/sflow_test.h +++ b/tests/sflow/sflow_test.h @@ -32,6 +32,16 @@ namespace pins { +// The constants structure for NOS +struct NosParameters { + std::string kRedisExecPrefix; + std::string kSflowContainerExecPrefix; + std::string kSudoCmdPrefix; + std::string kCpuQueueName; + std::string kRedisCliPath; + bool kIsSonicDebianLinux; +}; + struct SflowTestParams { thinkit::GenericTestbedInterface* testbed_interface; thinkit::SSHClient* ssh_client; @@ -42,8 +52,15 @@ struct SflowTestParams { int sample_size; // For sampling rate tests. int sample_rate; + uint64_t traffic_speed; + uint64_t run_time; + int iterations; + bool measure_cpumem; // For NSF tests. bool nsf_enabled; + NosParameters nos_param; + bool nos_is_sonic; + bool run_all_tests; }; // Structure represents a link between SUT and Ixia. @@ -51,6 +68,7 @@ struct SflowTestParams { // name and its corresponding p4 runtime id. struct IxiaLink { std::string ixia_interface; + std::string ixia_location; std::string sut_interface; int port_id; }; @@ -76,6 +94,8 @@ class SflowTestFixture : public ::testing::TestWithParam { // first collector config port. If config does not have any collector config, // it would be set to 6343. int collector_port_; + + NosParameters nos_param_; }; class SampleSizeTest : public SflowTestFixture {}; @@ -84,6 +104,8 @@ class SampleRateTest : public SflowTestFixture {}; class BackoffTest : public SflowTestFixture {}; +class MeasurementTest : public SflowTestFixture {}; + class SflowNsfTestFixture : public SflowTestFixture { void TearDown() override; }; @@ -101,6 +123,10 @@ struct SflowMirrorTestParams { // If enabled, warm reboot would be used in tests instead of cold reboot. bool nsf_enabled; + + NosParameters nos_param; + bool nos_is_sonic; + bool run_all_tests; }; struct Port { @@ -139,6 +165,8 @@ class SflowMirrorTestFixture // first collector config port. If config does not have any collector config, // it would be set to 6343. int collector_port_; + + NosParameters nos_param_; }; class SflowRebootTestFixture : public SflowMirrorTestFixture {}; diff --git a/tests/sflow/sflow_util.cc b/tests/sflow/sflow_util.cc index c0a950a93..6a10a888d 100644 --- a/tests/sflow/sflow_util.cc +++ b/tests/sflow/sflow_util.cc @@ -16,6 +16,7 @@ #include #include +#include #include #include "absl/container/btree_map.h" @@ -580,24 +581,25 @@ absl::StatusOr IsSameIpAddressStr(const std::string& ip1, ipv4_address_1.ok()) { return ip1 == ip2; } - ASSIGN_OR_RETURN(netaddr::Ipv6Address ipv6_adddress_1, + ASSIGN_OR_RETURN(netaddr::Ipv6Address ipv6_address_1, netaddr::Ipv6Address::OfString(ip1)); - ASSIGN_OR_RETURN(netaddr::Ipv6Address ipv6_adddress_2, + ASSIGN_OR_RETURN(netaddr::Ipv6Address ipv6_address_2, netaddr::Ipv6Address::OfString(ip2)); - - return ipv6_adddress_1 == ipv6_adddress_2; + + return ipv6_address_1 == ipv6_address_2; } int GetSflowCollectorPort() { return kSflowStandaloneCollectorPort; } absl::Status CheckStateDbPortIndexTableExists( thinkit::SSHClient& ssh_client, absl::string_view device_name, + const std::string redis_cli_path, absl::Span interfaces) { ASSIGN_OR_RETURN( std::string ssh_result, ssh_client.RunCommand( device_name, - /*command=*/"/usr/tools/bin/redis-cli -n 6 keys PORT_INDEX_TABLE*", + /*command=*/redis_cli_path + "redis-cli -n 6 keys PORT_INDEX_TABLE*", /*timeout=*/absl::Seconds(5))); LOG(INFO) << "ssh_result: " << ssh_result; absl::flat_hash_set port_index_table_interfaces; diff --git a/tests/sflow/sflow_util.h b/tests/sflow/sflow_util.h index 99e6d3ea7..82113aedd 100644 --- a/tests/sflow/sflow_util.h +++ b/tests/sflow/sflow_util.h @@ -167,7 +167,7 @@ int GetSflowCollectorPort(); // Checks if state db port index table exists for `interfaces`. absl::Status CheckStateDbPortIndexTableExists( thinkit::SSHClient& ssh_client, absl::string_view device_name, - absl::Span interfaces); + const std::string redis_cli_path, absl::Span interfaces); } // 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 340512342..f8040cd6a 100644 --- a/tests/sflow/sflow_util_test.cc +++ b/tests/sflow/sflow_util_test.cc @@ -1359,9 +1359,9 @@ TEST(PortIndexTest, SshCommandFailCheckFail) { .WillByDefault(Return(absl::InternalError("No ssh client"))); const std::vector port_names = {"Ethernet1/1/1", "Ethernet1/1/2"}; - EXPECT_THAT( - CheckStateDbPortIndexTableExists(mock_ssh_client, "switch_x", port_names), - StatusIs(absl::StatusCode::kInternal)); + EXPECT_THAT(CheckStateDbPortIndexTableExists(mock_ssh_client, "switch_x", + "/usr/tools/bin/", port_names), + StatusIs(absl::StatusCode::kInternal)); } TEST(PortIndexTest, SshCommandNoNilCheckSuccess) { @@ -1375,7 +1375,7 @@ PORT_INDEX_TABLE|Ethernet1/13/1)")); const std::vector port_names = { "Ethernet1/10/1", "Ethernet1/31/1", "Ethernet1/13/1"}; EXPECT_OK(CheckStateDbPortIndexTableExists(mock_ssh_client, "switch_x", - port_names)); + "/usr/tools/bin/", port_names)); } TEST(PortIndexTest, SshCommandNilCheckFail) { @@ -1383,9 +1383,9 @@ TEST(PortIndexTest, SshCommandNilCheckFail) { ON_CALL(mock_ssh_client, RunCommand).WillByDefault(Return("nil")); const std::vector port_names = {"Ethernet1/1/1", "Ethernet1/1/2"}; - EXPECT_THAT( - CheckStateDbPortIndexTableExists(mock_ssh_client, "switch_x", port_names), - StatusIs(absl::StatusCode::kFailedPrecondition)); + EXPECT_THAT(CheckStateDbPortIndexTableExists(mock_ssh_client, "switch_x", + "/usr/tools/bin/", port_names), + StatusIs(absl::StatusCode::kFailedPrecondition)); } TEST(PortIndexTest, SshCommandEmptyCheckFail) { @@ -1394,7 +1394,8 @@ TEST(PortIndexTest, SshCommandEmptyCheckFail) { const std::vector port_names = {"Ethernet1/1/1", "Ethernet1/1/2"}; EXPECT_THAT( - CheckStateDbPortIndexTableExists(mock_ssh_client, "switch_x", port_names), + CheckStateDbPortIndexTableExists(mock_ssh_client, "switch_x", + "/usr/tools/bin/", port_names), StatusIs(absl::StatusCode::kFailedPrecondition, AllOf(HasSubstr("Ethernet1/1/1"), HasSubstr("Ethernet1/1/2")))); } diff --git a/tests/thinkit_gnmi_interface_util.cc b/tests/thinkit_gnmi_interface_util.cc index cc6e6308a..874e9a274 100644 --- a/tests/thinkit_gnmi_interface_util.cc +++ b/tests/thinkit_gnmi_interface_util.cc @@ -640,6 +640,25 @@ absl::StatusOr GenerateComponentBreakoutConfig( return component_config; } +absl::StatusOr IsCopperPhysicalPort( + gnmi::gNMI::StubInterface* sut_gnmi_stub, absl::string_view phy_port) { + auto state_path = absl::StrFormat( + "components/component[name=%s]/transceiver/state/ethernet-pmd", phy_port); + auto resp_parse_str = "openconfig-platform-transceiver:ethernet-pmd"; + ASSIGN_OR_RETURN( + auto ethernet_pmd, + GetGnmiStatePathInfo(sut_gnmi_stub, state_path, resp_parse_str), + _ << "Failed to get GNMI state path value for ethernet-pmd for " + "port " + << phy_port); + StripSymbolFromString(ethernet_pmd, '\"'); + + // PMD state path value for copper ports ends in CR2/CR4/CR8. + auto pos = ethernet_pmd.find_last_of('_'); + LOG(INFO) << "ethernet_pmd: " << ethernet_pmd; + return (ethernet_pmd.substr(pos + 1, 2) == "CR"); +} + absl::StatusOr IsCopperPort(gnmi::gNMI::StubInterface* sut_gnmi_stub, absl::string_view port) { // Get transceiver name for the port. @@ -654,21 +673,9 @@ absl::StatusOr IsCopperPort(gnmi::gNMI::StubInterface* sut_gnmi_stub, << port); StripSymbolFromString(xcvrd_name, '\"'); - state_path = absl::StrFormat( - "components/component[name=%s]/transceiver/state/ethernet-pmd", - xcvrd_name); - resp_parse_str = "openconfig-platform-transceiver:ethernet-pmd"; - ASSIGN_OR_RETURN( - auto ethernet_pmd, - GetGnmiStatePathInfo(sut_gnmi_stub, state_path, resp_parse_str), - _ << "Failed to get GNMI state path value for ethernet-pmd for " - "port " - << port); - StripSymbolFromString(ethernet_pmd, '\"'); - - // PMD state path value for copper ports ends in CR2/CR4/CR8. - auto pos = ethernet_pmd.find_last_of('_'); - return (ethernet_pmd.substr(pos + 1, 2) == "CR"); + ASSIGN_OR_RETURN(auto is_copper_phy_port, + IsCopperPhysicalPort(sut_gnmi_stub, xcvrd_name)); + return is_copper_phy_port; } absl::StatusOr ComputePortIDForPort( diff --git a/tests/thinkit_gnmi_interface_util.h b/tests/thinkit_gnmi_interface_util.h index 8965c673e..ee51d13d1 100644 --- a/tests/thinkit_gnmi_interface_util.h +++ b/tests/thinkit_gnmi_interface_util.h @@ -168,10 +168,14 @@ absl::StatusOr GetPortIndex( std::string ConstructSupportedBreakoutMode(absl::string_view num_breakouts, absl::string_view breakout_speed); -// IsCopperPort returns whether the port is copper or optic. +// IsCopperPort returns whether the logical port is copper or optic. absl::StatusOr IsCopperPort(gnmi::gNMI::StubInterface* sut_gnmi_stub, absl::string_view port); +// IsCopperPhysicalPort returns whether the physical port is copper or optic. +absl::StatusOr IsCopperPhysicalPort( + gnmi::gNMI::StubInterface* sut_gnmi_stub, absl::string_view phy_port); + // Returns (slot/port/lane) info for a front panel port Ethernet absl::StatusOr GetSlotPortLaneForPort( const absl::string_view port); diff --git a/tests/thinkit_sanity_tests.cc b/tests/thinkit_sanity_tests.cc index 4827f0d90..f1b4120c5 100644 --- a/tests/thinkit_sanity_tests.cc +++ b/tests/thinkit_sanity_tests.cc @@ -40,6 +40,7 @@ #include "gutil/gutil/status_matchers.h" #include "include/nlohmann/json.hpp" #include "lib/gnmi/gnmi_helper.h" +#include "lib/utils/constants.h" #include "lib/validator/validator_lib.h" #include "p4/v1/p4runtime.grpc.pb.h" #include "p4_pdpi/p4_runtime_session.h" @@ -64,8 +65,6 @@ using ::testing::HasSubstr; constexpr int kEpochMarginalError = 2; constexpr absl::Duration kColdRebootWaitForDownTime = absl::Seconds(75); -// TODO: Reduce reboot up time. -constexpr absl::Duration kColdRebootWaitForUpTime = absl::Minutes(6); constexpr char kV3ReleaseConfigBlob[] = R"({ "openconfig-platform:components" : { "component" : [ @@ -360,6 +359,7 @@ void TestGnoiSystemColdReboot(thinkit::Switch& sut, // Wait for system to become reachable over gNOI. start_time = absl::Now(); absl::Status status; + const absl::Duration kColdRebootWaitForUpTime = GetColdRebootWaitForUpTime(); while (absl::Now() < (start_time + kColdRebootWaitForUpTime)) { status = SwitchReady(sut, interfaces); if (status.ok()) {