diff --git a/tests/BUILD.bazel b/tests/BUILD.bazel index 6bdd1608..397e97d7 100644 --- a/tests/BUILD.bazel +++ b/tests/BUILD.bazel @@ -76,6 +76,7 @@ cc_library( "@com_google_absl//absl/strings", "@com_google_absl//absl/strings:str_format", "@com_google_googletest//:gtest", + "@com_googlesource_code_re2//:re2", ], ) diff --git a/tests/forwarding/BUILD.bazel b/tests/forwarding/BUILD.bazel index 25f695bc..82e15ba6 100644 --- a/tests/forwarding/BUILD.bazel +++ b/tests/forwarding/BUILD.bazel @@ -209,6 +209,7 @@ cc_library( "//gutil:proto", "//gutil:status_matchers", "//lib/gnmi:gnmi_helper", + "//lib/gnmi:openconfig_cc_proto", "//p4_pdpi:ir", "//p4_pdpi:ir_cc_proto", "//p4_pdpi:p4_runtime_session", @@ -218,6 +219,7 @@ cc_library( "//tests/lib:p4rt_fixed_table_programming_helper", "//tests/lib:packet_in_helper", "//tests/lib:switch_test_setup_helpers", + "//thinkit:mirror_testbed", "//thinkit:mirror_testbed_fixture", "//thinkit:switch", "@com_github_google_glog//:glog", diff --git a/tests/forwarding/l3_admit_test.cc b/tests/forwarding/l3_admit_test.cc index 2c0449bc..29252369 100644 --- a/tests/forwarding/l3_admit_test.cc +++ b/tests/forwarding/l3_admit_test.cc @@ -555,7 +555,18 @@ TEST_P(L3AdmitTestFixture, L3AdmitCanUseInPortToRestrictMacAddresses) { } LOG(INFO) << "Done collecting packets."; - EXPECT_EQ(good_packet_count, kNumberOfTestPacket); + if (GetParam() + .testbed_interface->GetMirrorTestbed() + .Environment() + .MaskKnownFailures()) { + // TODO: Reduce expected count by tolerance level. + const int kDropTolerance = 1; + int adjusted_good_packets = kNumberOfTestPacket - kDropTolerance; + EXPECT_GE(good_packet_count, adjusted_good_packets); + EXPECT_LE(good_packet_count, kNumberOfTestPacket); + } else { + EXPECT_EQ(good_packet_count, kNumberOfTestPacket); + } EXPECT_EQ(bad_packet_count, 0); } diff --git a/tests/forwarding/l3_admit_test.h b/tests/forwarding/l3_admit_test.h index b5a8142e..16a4166d 100644 --- a/tests/forwarding/l3_admit_test.h +++ b/tests/forwarding/l3_admit_test.h @@ -19,10 +19,13 @@ #include #include "gutil/status_matchers.h" +#include "lib/gnmi/gnmi_helper.h" +#include "lib/gnmi/openconfig.pb.h" #include "p4/config/v1/p4info.pb.h" #include "p4_pdpi/ir.h" #include "p4_pdpi/ir.pb.h" #include "tests/lib/switch_test_setup_helpers.h" +#include "thinkit/mirror_testbed.h" #include "thinkit/mirror_testbed_fixture.h" #include "gmock/gmock.h" @@ -39,15 +42,34 @@ class L3AdmitTestFixture : public testing::TestWithParam { void SetUp() override { GetParam().testbed_interface->SetUp(); + thinkit::MirrorTestbed& testbed = + GetParam().testbed_interface->GetMirrorTestbed(); + // Initialize the connection and clear table entries for the SUT and Control // switch. ASSERT_OK_AND_ASSIGN( std::tie(sut_p4rt_session_, control_switch_p4rt_session_), pins_test::ConfigureSwitchPairAndReturnP4RuntimeSessionPair( - GetParam().testbed_interface->GetMirrorTestbed().Sut(), - GetParam().testbed_interface->GetMirrorTestbed().ControlSwitch(), + testbed.Sut(), testbed.ControlSwitch(), /*gnmi_config=*/std::nullopt, GetParam().p4info)); + // The L3Admit tests assume identical P4RT port IDs are used between the SUT + // and control switch. So sending a packet from a given port ID on the + // control switch means it will arrive on the same port ID on the SUT. To + // achieve this, we mirror the SUTs OpenConfig interfaces <-> P4RT Port ID + // mapping to the control switch. + ASSERT_OK_AND_ASSIGN( + std::unique_ptr sut_gnmi_stub, + testbed.Sut().CreateGnmiStub()); + ASSERT_OK_AND_ASSIGN(const pins_test::openconfig::Interfaces sut_interfaces, + pins_test::GetInterfacesAsProto( + *sut_gnmi_stub, gnmi::GetRequest::CONFIG)); + ASSERT_OK_AND_ASSIGN( + std::unique_ptr control_gnmi_stub, + testbed.ControlSwitch().CreateGnmiStub()); + ASSERT_OK( + pins_test::SetInterfaceP4rtIds(*control_gnmi_stub, sut_interfaces)); + ASSERT_OK_AND_ASSIGN(ir_p4info_, pdpi::CreateIrP4Info(GetParam().p4info)); } diff --git a/tests/forwarding/smoke_test.cc b/tests/forwarding/smoke_test.cc index ce3575b5..04fba72c 100644 --- a/tests/forwarding/smoke_test.cc +++ b/tests/forwarding/smoke_test.cc @@ -43,7 +43,7 @@ TEST_P(SmokeTestFixture, SessionsAreNonNull) { ASSERT_NE(&GetControlP4RuntimeSession(), nullptr); } -TEST_P(SmokeTestFixture, AclTableAddDeleteOkButModifyFails) { +TEST_P(SmokeTestFixture, AclTableAddModifyDeleteOk) { const sai::WriteRequest pd_insert = gutil::ParseProtoOrDie( R"pb( updates { @@ -117,11 +117,8 @@ TEST_P(SmokeTestFixture, AclTableAddDeleteOkButModifyFails) { } } while (!pi_read_response.entities(0).table_entry().has_counter_data()); - // To avoid any test failures during the submission process (test running with - // the pre-7.1 image), skip this check for now. - // ASSERT_OK(pdpi::SetMetadataAndSendPiWriteRequest(&GetSutP4RuntimeSession(), - // pi_modify)); - + ASSERT_OK(pdpi::SetMetadataAndSendPiWriteRequest(&GetSutP4RuntimeSession(), + pi_modify)); // Delete works. ASSERT_OK(pdpi::SetMetadataAndSendPiWriteRequest(&GetSutP4RuntimeSession(), pi_delete)); diff --git a/tests/gnoi/bert_tests.cc b/tests/gnoi/bert_tests.cc index 41d335c6..ea44cbde 100644 --- a/tests/gnoi/bert_tests.cc +++ b/tests/gnoi/bert_tests.cc @@ -491,26 +491,22 @@ void VerifyOperStatusOnSetOfSutInterfaces(gnmi::gNMI::StubInterface& gnmi_stub, } } -absl::Status ValidateControlSwitchPortsUp( - thinkit::ControlDevice& control_device, - const std::vector& interfaces) { - ASSIGN_OR_RETURN( - const auto up_interfaces, - control_device.GetUpLinks(absl::Span(interfaces))); - - std::vector down_interfaces; - for (const std::string& interface : interfaces) { - if (!up_interfaces.contains(interface)) { - down_interfaces.push_back(interface); - } +absl::Status ValidatePortsUp( + thinkit::Switch& sut, thinkit::ControlDevice& control_device, + const std::vector& sut_interfaces, + const std::vector& control_device_interfaces) { + absl::Status sut_ports_up_status = + pins_test::PortsUp(sut, absl::Span(sut_interfaces)); + absl::Status control_switch_ports_up_status = control_device.ValidatePortsUp( + absl::Span(control_device_interfaces)); + + if (sut_ports_up_status.ok() && control_switch_ports_up_status.ok()) { + return absl::OkStatus(); } - if (!down_interfaces.empty()) { - return absl::InternalError( - absl::StrCat("Some interfaces are not up on control switch: ", - absl::StrJoin(down_interfaces, "\n"))); - } - return absl::OkStatus(); + EXPECT_OK(sut_ports_up_status); + EXPECT_OK(control_switch_ports_up_status); + return absl::InternalError("PortsUp validation failed."); } std::vector SelectNInterfacesFromList( @@ -864,11 +860,9 @@ TEST_P(BertTest, StartBertSucceeds) { GTEST_SKIP() << "No SUT interfaces to test"; } thinkit::Switch& sut = generic_testbed_->Sut(); - ASSERT_OK( - pins_test::PortsUp(sut, absl::Span(sut_interfaces_))); - thinkit::ControlDevice& control_device = generic_testbed_->ControlDevice(); - ASSERT_OK(ValidateControlSwitchPortsUp(control_device, peer_interfaces_)); + ASSERT_OK( + ValidatePortsUp(sut, control_device, sut_interfaces_, peer_interfaces_)); // Select 2 operational state "up" ports. sut_test_interfaces_ = absl::GetFlag(FLAGS_interfaces); @@ -1031,9 +1025,9 @@ TEST_P(BertTest, StartBertSucceeds) { HasSubstr("exists")))) << "Response: " << bert_response.ShortDebugString(); } + ASSERT_OK( - pins_test::PortsUp(sut, absl::Span(sut_interfaces_))); - ASSERT_OK(ValidateControlSwitchPortsUp(control_device, peer_interfaces_)); + ValidatePortsUp(sut, control_device, sut_interfaces_, peer_interfaces_)); } // Runs the BERT test on current maximum allowed number of interfaces. During @@ -1047,11 +1041,9 @@ TEST_P(BertTest, RunBertOnMaximumAllowedPorts) { GTEST_SKIP() << "No SUT interfaces to test"; } thinkit::Switch& sut = generic_testbed_->Sut(); - ASSERT_OK( - pins_test::PortsUp(sut, absl::Span(sut_interfaces_))); - thinkit::ControlDevice& control_device = generic_testbed_->ControlDevice(); - ASSERT_OK(ValidateControlSwitchPortsUp(control_device, peer_interfaces_)); + ASSERT_OK( + ValidatePortsUp(sut, control_device, sut_interfaces_, peer_interfaces_)); // Get all the interfaces that are operational status "UP". sut_test_interfaces_ = sut_interfaces_; @@ -1180,8 +1172,7 @@ TEST_P(BertTest, RunBertOnMaximumAllowedPorts) { absl::SleepFor(kPortsUpWaitTime); ASSERT_OK( - pins_test::PortsUp(sut, absl::Span(sut_interfaces_))); - ASSERT_OK(ValidateControlSwitchPortsUp(control_device, peer_interfaces_)); + ValidatePortsUp(sut, control_device, sut_interfaces_, peer_interfaces_)); } // Run BERT on a port. Issue StopBERT on the SUT port, this causes BERT to @@ -1192,11 +1183,9 @@ TEST_P(BertTest, StopBertSucceeds) { GTEST_SKIP() << "No SUT interfaces to test"; } thinkit::Switch& sut = generic_testbed_->Sut(); - ASSERT_OK( - pins_test::PortsUp(sut, absl::Span(sut_interfaces_))); - thinkit::ControlDevice& control_device = generic_testbed_->ControlDevice(); - ASSERT_OK(ValidateControlSwitchPortsUp(control_device, peer_interfaces_)); + ASSERT_OK( + ValidatePortsUp(sut, control_device, sut_interfaces_, peer_interfaces_)); // Select one operational state "up" port. std::string interface = absl::GetFlag(FLAGS_interface); @@ -1350,8 +1339,7 @@ TEST_P(BertTest, StopBertSucceeds) { } ASSERT_OK( - pins_test::PortsUp(sut, absl::Span(sut_interfaces_))); - ASSERT_OK(ValidateControlSwitchPortsUp(control_device, peer_interfaces_)); + ValidatePortsUp(sut, control_device, sut_interfaces_, peer_interfaces_)); } } // namespace bert diff --git a/tests/lib/BUILD.bazel b/tests/lib/BUILD.bazel index 2aa8bb46..b2ded6e4 100644 --- a/tests/lib/BUILD.bazel +++ b/tests/lib/BUILD.bazel @@ -95,6 +95,7 @@ cc_library( "//tests:thinkit_sanity_tests", "//thinkit:mirror_testbed", "//thinkit:switch", + "@com_github_gnmi//proto/gnmi:gnmi_cc_grpc_proto", "@com_github_google_glog//:glog", "@com_github_p4lang_p4runtime//:p4info_cc_proto", "@com_github_p4lang_p4runtime//:p4runtime_cc_proto", diff --git a/tests/lib/switch_test_setup_helpers.cc b/tests/lib/switch_test_setup_helpers.cc index d8382fee..1e5bb33a 100644 --- a/tests/lib/switch_test_setup_helpers.cc +++ b/tests/lib/switch_test_setup_helpers.cc @@ -29,6 +29,7 @@ #include "p4/v1/p4runtime.pb.h" #include "p4_pdpi/ir_tools.h" #include "p4_pdpi/p4_runtime_session.h" +#include "proto/gnmi/gnmi.grpc.pb.h" #include "tests/thinkit_sanity_tests.h" namespace pins_test { @@ -61,14 +62,6 @@ absl::Status TryClearingTableEntries( return absl::OkStatus(); } -absl::Status PushGnmiAndWaitForConvergence(thinkit::Switch& thinkit_switch, - const std::string& gnmi_config, - absl::Duration gnmi_timeout) { - RETURN_IF_ERROR(PushGnmiConfig(thinkit_switch, gnmi_config)); - return WaitForGnmiPortIdConvergence(thinkit_switch, gnmi_config, - gnmi_timeout); -} - // Wrapper around `TestGnoiSystemColdReboot` that ensures we don't ignore fatal // failures. absl::Status Reboot(thinkit::Switch& thinkit_switch) { @@ -162,13 +155,21 @@ ConfigureSwitchAndReturnP4RuntimeSession( RETURN_IF_ERROR(TryClearingTableEntries(thinkit_switch, metadata)); if (gnmi_config.has_value()) { - RETURN_IF_ERROR( - PushGnmiAndWaitForConvergence(thinkit_switch, *gnmi_config, - /*gnmi_timeout=*/kGnmiTimeoutDefault)); + RETURN_IF_ERROR(PushGnmiConfig(thinkit_switch, *gnmi_config)); } - return CreateP4RuntimeSessionAndOptionallyPushP4Info(thinkit_switch, p4info, - metadata); + ASSIGN_OR_RETURN(auto p4rt_session, + CreateP4RuntimeSessionAndOptionallyPushP4Info( + thinkit_switch, p4info, metadata)); + + // Ensure that the P4RT port IDs configured on the switch are reflected in the + // state before returning. + ASSIGN_OR_RETURN(std::unique_ptr gnmi_stub, + thinkit_switch.CreateGnmiStub()); + RETURN_IF_ERROR( + WaitForGnmiPortIdConvergence(*gnmi_stub, + /*gnmi_timeout=*/kGnmiTimeoutDefault)); + return p4rt_session; } absl::StatusOr, diff --git a/tests/lib/switch_test_setup_helpers_test.cc b/tests/lib/switch_test_setup_helpers_test.cc index 502e114c..21715729 100644 --- a/tests/lib/switch_test_setup_helpers_test.cc +++ b/tests/lib/switch_test_setup_helpers_test.cc @@ -13,210 +13,39 @@ #include "absl/strings/str_cat.h" #include "absl/strings/str_format.h" #include "absl/strings/string_view.h" -#include "absl/strings/substitute.h" -#include "absl/time/time.h" #include "absl/types/span.h" -#include "gmock/gmock.h" #include "grpcpp/test/mock_stream.h" -#include "gtest/gtest.h" #include "gutil/status.h" #include "gutil/testing.h" #include "lib/gnmi/gnmi_helper.h" #include "p4/config/v1/p4info.pb.h" #include "p4/v1/p4runtime.pb.h" -#include "p4/v1/p4runtime_mock.grpc.pb.h" #include "p4_pdpi/ir.pb.h" #include "p4_pdpi/p4_runtime_session.h" -#include "p4_pdpi/pd.h" +#include "p4_pdpi/p4_runtime_session_mocking.h" #include "p4_pdpi/testing/test_p4info.h" #include "proto/gnmi/gnmi_mock.grpc.pb.h" #include "sai_p4/instantiations/google/instantiations.h" #include "sai_p4/instantiations/google/sai_p4info.h" #include "sai_p4/instantiations/google/sai_pd.pb.h" #include "thinkit/mock_switch.h" +#include "gmock/gmock.h" +#include "gtest/gtest.h" namespace pins_test { namespace { -using ::testing::_; using ::testing::AnyNumber; -using ::testing::EqualsProto; using ::testing::InSequence; -using ::testing::NiceMock; using ::testing::Not; using ::testing::Return; using ::testing::ReturnRef; using ::testing::StrictMock; using ::testing::status::IsOk; -// One of the tables and actions from -// http://google3/blaze-out/genfiles/third_party/pins_infra/p4_pdpi/testing/test_p4info_embed.cc?l=13 -// These need to correspond to the values in our p4info because it is checked -// when sequencing updates to clear tables on the switch. -constexpr uint32_t kTableId = 33554433; -constexpr uint32_t kActionId = 16777217; - // Any constant is fine here. constexpr uint32_t kDeviceId = 100; -// This is the only action that will work everywhere. -constexpr p4::v1::SetForwardingPipelineConfigRequest::Action - kForwardingPipelineAction = - p4::v1::SetForwardingPipelineConfigRequest::RECONCILE_AND_COMMIT; - -p4::v1::Uint128 ConstructElectionId( - const pdpi::P4RuntimeSessionOptionalArgs& metadata) { - p4::v1::Uint128 election_id; - election_id.set_high(absl::Uint128High64(metadata.election_id)); - election_id.set_low(absl::Uint128Low64(metadata.election_id)); - return election_id; -} - -p4::v1::MasterArbitrationUpdate ConstructMasterArbitrationUpdate( - const pdpi::P4RuntimeSessionOptionalArgs& metadata) { - p4::v1::MasterArbitrationUpdate master_arbitration_update; - *master_arbitration_update.mutable_election_id() = - ConstructElectionId(metadata); - master_arbitration_update.set_device_id(kDeviceId); - master_arbitration_update.mutable_role()->set_name(metadata.role); - return master_arbitration_update; -} - -// Configures the given `MockP4RuntimeStub` such that the given sequence of -// table entries will be returned for the next P4RT Read request. -void SetNextReadResponse(p4::v1::MockP4RuntimeStub& mock_p4rt_stub, - std::vector read_entries) { - EXPECT_CALL(mock_p4rt_stub, ReadRaw) - .WillOnce([read_entries = std::move(read_entries)](auto*, auto&) { - auto* reader = - new grpc::testing::MockClientReader(); - InSequence s; - for (const auto& entry : read_entries) { - EXPECT_CALL(*reader, Read) - .WillOnce([=](p4::v1::ReadResponse* response) -> bool { - *response->add_entities()->mutable_table_entry() = entry; - return true; - }); - } - EXPECT_CALL(*reader, Read).WillOnce(Return(false)); - EXPECT_CALL(*reader, Finish).WillOnce(Return(grpc::Status::OK)); - return reader; - }); -} - -// Mocks a P4RuntimeSession::Create call with a stub by constructing a -// ReaderWriter mock stream and mocking an arbitration handshake. This function -// does not perform any of these operations, it only sets up expectations. -[[nodiscard]] grpc::testing::MockClientReaderWriter< - p4::v1::StreamMessageRequest, p4::v1::StreamMessageResponse> & -MockP4RuntimeSessionCreateAndReturnStreamChannel( - p4::v1::MockP4RuntimeStub &stub, - const pdpi::P4RuntimeSessionOptionalArgs &metadata) { - // The ReaderWriter stream constructed from the stub. This needs to be - // malloced as it is automatically freed when the unique pointer that it - // will be wrapped in is freed. The stream is wrapped in StreamChannel, - // which is the method of the stub that calls StreamChannelRaw, but is not - // itself mocked. - auto* stream = new NiceMock>(); - EXPECT_CALL(stub, StreamChannelRaw).WillOnce(Return(stream)); - - // A valid MasterArbitrationUpdate sent as request and response. - auto master_arbitration_update = ConstructMasterArbitrationUpdate(metadata); - - // Ensures that we write some sort of arbitration request... - p4::v1::StreamMessageRequest arbitration_request; - *arbitration_request.mutable_arbitration() = master_arbitration_update; - EXPECT_CALL(*stream, Write(EqualsProto(arbitration_request), _)) - .WillOnce(Return(true)); - - // ... and return a valid response. - EXPECT_CALL(*stream, Read) - .WillOnce([=](p4::v1::StreamMessageResponse* arbitration_response) { - *arbitration_response->mutable_arbitration() = - master_arbitration_update; - return true; - }); - return *stream; -} - -// Constructs a table entry using the predefined table id, kTableId, and action -// id, kActionId. -p4::v1::TableEntry ConstructTableEntry() { - p4::v1::TableEntry table_entry; - table_entry.set_table_id(kTableId); - table_entry.mutable_action()->mutable_action()->set_action_id(kActionId); - return table_entry; -} - -// Sets up a write request to delete the given table entry. -p4::v1::WriteRequest ConstructDeleteRequest( - const pdpi::P4RuntimeSessionOptionalArgs& metadata, - const p4::v1::TableEntry& table_entry) { - p4::v1::Update delete_update; - delete_update.set_type(p4::v1::Update::DELETE); - *delete_update.mutable_entity()->mutable_table_entry() = table_entry; - - p4::v1::WriteRequest delete_request; - *delete_request.add_updates() = delete_update; - delete_request.set_device_id(kDeviceId); - delete_request.set_role(metadata.role); - *delete_request.mutable_election_id() = ConstructElectionId(metadata); - return delete_request; -} - -// Mocks a `CheckNoEntries` call using the stub in a previously -// mocked P4RuntimeSession. -// Ensures that there are no table entries remaining. -void MockCheckNoEntries(p4::v1::MockP4RuntimeStub& stub) { - // We need to return a p4info to get to the stage where we read tables. - EXPECT_CALL(stub, GetForwardingPipelineConfig) - .WillOnce([=](auto, auto, - p4::v1::GetForwardingPipelineConfigResponse* - get_pipeline_response) { - get_pipeline_response->mutable_config()->mutable_p4info(); - return grpc::Status::OK; - }); - - SetNextReadResponse(stub, {}); -} - -// Mocks a `ClearTableEntries` call using the stub and p4info in a previously -// mocked P4RuntimeSession. -// Pulls the p4info from the switch, then reads a table entry, deletes it, and -// reads again ensuring that there are no table entries remaining. -void MockClearTableEntries(p4::v1::MockP4RuntimeStub& stub, - const p4::config::v1::P4Info& p4info, - const pdpi::P4RuntimeSessionOptionalArgs& metadata) { - // We need to return a valid p4info to get to the stage where we read tables. - EXPECT_CALL(stub, GetForwardingPipelineConfig) - .WillOnce([=](auto, auto, - p4::v1::GetForwardingPipelineConfigResponse* - get_pipeline_response) { - *get_pipeline_response->mutable_config()->mutable_p4info() = p4info; - return grpc::Status::OK; - }); - - { - InSequence s; - p4::v1::TableEntry table_entry = ConstructTableEntry(); - - // We return a table entry so that the function exercises the deletion - // portion of clearing table entries. - SetNextReadResponse(stub, {table_entry}); - - // Mocks the call to delete table entry that we have created. - EXPECT_CALL( - stub, - Write(_, EqualsProto(ConstructDeleteRequest(metadata, table_entry)), _)) - .Times(1); - - // Mocks a `CheckNoEntries` call, ensuring that the tables are really - // cleared. - MockCheckNoEntries(stub); - } -} - // Sets up expectation that `ClearTableEntries(mock_switch, metadata)` is // called. void ExpectCallToClearTableEntries( @@ -225,11 +54,8 @@ void ExpectCallToClearTableEntries( EXPECT_CALL(mock_switch, CreateP4RuntimeStub()).WillOnce([=] { InSequence s; auto stub = std::make_unique<::p4::v1::MockP4RuntimeStub>(); - auto &stream_channel = - MockP4RuntimeSessionCreateAndReturnStreamChannel(*stub, metadata); + MockP4RuntimeSessionCreate(*stub, metadata); MockClearTableEntries(*stub, p4info, metadata); - // From the call to Finish. - EXPECT_CALL(stream_channel, Read).WillOnce(Return(false)); return stub; }); } @@ -250,10 +76,21 @@ void ExpectCallToPushGnmiConfig(thinkit::MockSwitch& mock_switch) { } // Mocks a successful `WaitForGnmiPortIdConvergence` call where the ports given -// by `interfaces` have converged. +// by `interfaces` are retrieved from the config path and have converged in the +// state path. void MockGnmiConvergence( gnmi::MockgNMIStub& mock_gnmi_stub, const std::vector& interfaces) { + InSequence s; + EXPECT_CALL(mock_gnmi_stub, Get) + .WillOnce([=](auto, auto, gnmi::GetResponse* response) { + *response->add_notification() + ->add_update() + ->mutable_val() + ->mutable_json_ietf_val() = + OpenConfigWithInterfaces(GnmiFieldType::kConfig, interfaces); + return grpc::Status::OK; + }); EXPECT_CALL(mock_gnmi_stub, Get) .WillOnce([=](auto, auto, gnmi::GetResponse* response) { *response->add_notification() @@ -278,36 +115,6 @@ void ExpectCallToWaitForGnmiPortIdConvergence( }); } -// Sets up expectation that -// `pins_test::ExpectCallToPushGnmiAndWaitForConvergence(mock_switch, _, _)` is -// called, mocking response that the given`interfaces` have converged. -void ExpectCallToPushGnmiAndWaitForConvergence( - thinkit::MockSwitch& mock_switch, - const std::vector& interfaces) { - InSequence s; - ExpectCallToPushGnmiConfig(mock_switch); - ExpectCallToWaitForGnmiPortIdConvergence(mock_switch, interfaces); -} - -// Constructs a valid forwarding pipeline config request with the given p4info -// and metadata. -p4::v1::SetForwardingPipelineConfigRequest -ConstructForwardingPipelineConfigRequest( - const pdpi::P4RuntimeSessionOptionalArgs& metadata, - const p4::config::v1::P4Info& p4info, - absl::optional p4_device_config = absl::nullopt) { - p4::v1::SetForwardingPipelineConfigRequest request; - request.set_device_id(kDeviceId); - request.set_role(metadata.role); - *request.mutable_election_id() = ConstructElectionId(metadata); - request.set_action(kForwardingPipelineAction); - *request.mutable_config()->mutable_p4info() = p4info; - if (p4_device_config.has_value()) { - *request.mutable_config()->mutable_p4_device_config() = *p4_device_config; - } - return request; -} - void ExpectCallToCreateP4RuntimeSessionAndOptionallyPushP4Info( thinkit::MockSwitch& mock_switch, const std::optional& p4info, @@ -315,15 +122,14 @@ void ExpectCallToCreateP4RuntimeSessionAndOptionallyPushP4Info( EXPECT_CALL(mock_switch, CreateP4RuntimeStub).WillOnce([=] { InSequence s; auto stub = absl::make_unique>(); - // Ignore returned stream channel mock since we have no use for it. - (void)MockP4RuntimeSessionCreateAndReturnStreamChannel(*stub, metadata); + MockP4RuntimeSessionCreate(*stub, metadata); if (p4info.has_value()) { // TODO: Test the path where `GetForwardingPipelineConfig` // returns a non-empty P4Info. EXPECT_CALL(*stub, GetForwardingPipelineConfig).Times(1); EXPECT_CALL(*stub, SetForwardingPipelineConfig).Times(1); } - MockCheckNoEntries(*stub); + pdpi::MockCheckNoEntries(*stub, p4info); return stub; }); @@ -359,10 +165,11 @@ void MockConfigureSwitchAndReturnP4RuntimeSession( InSequence s; ExpectCallToClearTableEntries(mock_switch, pdpi::GetTestP4Info(), metadata); if (gnmi_config.has_value()) { - ExpectCallToPushGnmiAndWaitForConvergence(mock_switch, interfaces); + ExpectCallToPushGnmiConfig(mock_switch); } ExpectCallToCreateP4RuntimeSessionAndOptionallyPushP4Info(mock_switch, p4info, metadata); + ExpectCallToWaitForGnmiPortIdConvergence(mock_switch, interfaces); } } diff --git a/tests/qos/frontpanel_qos_test.h b/tests/qos/frontpanel_qos_test.h index 04340d0e..7480494f 100644 --- a/tests/qos/frontpanel_qos_test.h +++ b/tests/qos/frontpanel_qos_test.h @@ -44,6 +44,7 @@ struct QosTestParams { p4::config::v1::P4Info p4info; std::string ingress_ports[2]; std::string egress_port_under_test; + std::string name; }; class FrontpanelQosTest : public testing::TestWithParam { diff --git a/tests/thinkit_gnmi_interface_util.cc b/tests/thinkit_gnmi_interface_util.cc index 99e4a300..93c7dfe9 100644 --- a/tests/thinkit_gnmi_interface_util.cc +++ b/tests/thinkit_gnmi_interface_util.cc @@ -23,15 +23,16 @@ #include "absl/strings/str_split.h" #include "absl/strings/string_view.h" #include "absl/strings/substitute.h" -#include "gmock/gmock.h" -#include "gtest/gtest.h" #include "gutil/status.h" #include "include/nlohmann/json.hpp" #include "lib/gnmi/gnmi_helper.h" #include "proto/gnmi/gnmi.grpc.pb.h" +#include "re2/re2.h" #include "tests/thinkit_util.h" #include "thinkit/ssh_client.h" #include "thinkit/switch.h" +#include "gmock/gmock.h" +#include "gtest/gtest.h" namespace pins_test { namespace { @@ -544,10 +545,40 @@ absl::StatusOr IsCopperPort(gnmi::gNMI::StubInterface* sut_gnmi_stub, return (ethernet_pmd.substr(pos + 1, 2) == "CR"); } -absl::StatusOr GenerateInterfaceBreakoutConfig( - absl::string_view port, const int id, absl::string_view breakout_speed, - const bool is_copper_port) { +absl::StatusOr +ComputePortIDForPort(gnmi::gNMI::StubInterface *sut_gnmi_stub, + absl::string_view port) { + // Try to get currently configured id for the port from the switch. + std::string if_state_path = + absl::StrCat("interfaces/interface[name=", port, "]/state/id"); + std::string resp_parse_str = "openconfig-interfaces:id"; + uint32_t id; + auto state_path_response = + GetGnmiStatePathInfo(sut_gnmi_stub, if_state_path, resp_parse_str); + if (state_path_response.ok() && !state_path_response.value().empty()) { + if (!absl::SimpleAtoi(state_path_response.value(), &id)) { + return gutil::InternalErrorBuilder().LogError() + << "Failed to convert string (" << state_path_response.value() + << ") to integer"; + } + return id; + } + // Generate ID same as that used by controller, if not found on switch. + ASSIGN_OR_RETURN(auto is_parent, IsParentPort(port)); + ASSIGN_OR_RETURN(auto slot_port_lane, GetSlotPortLaneForPort(port)); + // Port ID is same as port index/parent port number for parent ports. + if (is_parent) { + return slot_port_lane.port; + } + // Port ID is computed for child ports using + // (laneIndex*512 + parentPortNumber + 1) + return (slot_port_lane.lane * 512 + slot_port_lane.port + 1); +} +absl::StatusOr GenerateInterfaceBreakoutConfig( + gnmi::gNMI::StubInterface *sut_gnmi_stub, absl::string_view port, + absl::string_view breakout_speed, const bool is_copper_port) { + ASSIGN_OR_RETURN(auto id, ComputePortIDForPort(sut_gnmi_stub, port)); auto interface_config = absl::Substitute( R"pb({ "config": { @@ -555,7 +586,8 @@ absl::StatusOr GenerateInterfaceBreakoutConfig( "loopback-mode": false, "mtu": 9216, "name": "$0", - "type": "iana-if-type:ethernetCsmacd" + "type": "iana-if-type:ethernetCsmacd", + "id": $2 }, "name": "$0", "openconfig-if-ethernet:ethernet": { @@ -573,7 +605,7 @@ absl::StatusOr GenerateInterfaceBreakoutConfig( } } )pb", - port, breakout_speed); + port, breakout_speed, id); if (is_copper_port) { interface_config = absl::Substitute( R"pb({ @@ -582,7 +614,8 @@ absl::StatusOr GenerateInterfaceBreakoutConfig( "loopback-mode": false, "mtu": 9216, "name": "$0", - "type": "iana-if-type:ethernetCsmacd" + "type": "iana-if-type:ethernetCsmacd", + "id": $2 }, "name": "$0", "openconfig-if-ethernet:ethernet": { @@ -603,7 +636,7 @@ absl::StatusOr GenerateInterfaceBreakoutConfig( } } )pb", - port, breakout_speed); + port, breakout_speed, id); } return interface_config; } @@ -652,10 +685,10 @@ absl::StatusOr GetBreakoutModeConfigJson( auto port = absl::StrCat(kEthernet, slot_port_lane.slot, "/", slot_port_lane.port, "/", std::to_string(curr_lane_number)); - ASSIGN_OR_RETURN( - auto interface_config, - GenerateInterfaceBreakoutConfig(port, curr_lane_number, - breakout_speed, is_copper_port)); + ASSIGN_OR_RETURN(auto interface_config, + GenerateInterfaceBreakoutConfig(sut_gnmi_stub, port, + breakout_speed, + is_copper_port)); interface_configs.push_back(interface_config); int offset = max_channels_in_group / num_breakouts; curr_lane_number += offset; @@ -802,4 +835,13 @@ absl::StatusOr IsParentPort(absl::string_view port) { // Lane number for a master port is always 1. return slot_port_lane.lane == 1; } + +// Returns an EK_PHYSICAL_PORT name given an EK_PORT name. +absl::StatusOr DeriveEkPhysicalPort(absl::string_view ek_port) { + int slot; + int port; + RET_CHECK(RE2::FullMatch(ek_port, R"(\w+(\d+)\/(\d+)(\/\d+)*)", &slot, &port)) + << "no match found for " << ek_port; + return absl::StrCat("phy-", slot, "/", port); +} } // namespace pins_test diff --git a/tests/thinkit_gnmi_interface_util.h b/tests/thinkit_gnmi_interface_util.h index 862f1719..2587eace 100644 --- a/tests/thinkit_gnmi_interface_util.h +++ b/tests/thinkit_gnmi_interface_util.h @@ -176,5 +176,9 @@ absl::StatusOr IsParentPort(absl::string_view port); // channelized or no. absl::StatusOr IsChannelizedBreakoutMode(const std::string& mode); +// Returns an EK_PHYSICAL_PORT name given an EK_PORT name. +// For example, the call below would return "phy-1/2". +// DeriveEkPhysicalPort("Ethernet1/2/5") +absl::StatusOr DeriveEkPhysicalPort(absl::string_view ek_port); } // namespace pins_test #endif // PINS_TESTS_THINKIT_GNMI_INTERFACE_UTIL_H_ diff --git a/tests/thinkit_gnmi_interface_util_tests.cc b/tests/thinkit_gnmi_interface_util_tests.cc index ec84a7c3..a9c87d7d 100644 --- a/tests/thinkit_gnmi_interface_util_tests.cc +++ b/tests/thinkit_gnmi_interface_util_tests.cc @@ -357,6 +357,59 @@ class GNMIThinkitInterfaceUtilityTest : public ::testing::Test { return resp; } + absl::StatusOr portIDGetReq(absl::string_view port) { + gnmi::GetRequest req; + if (!google::protobuf::TextFormat::ParseFromString( + absl::Substitute( + R"pb(prefix { origin: "openconfig" } + path { + elem { name: "interfaces" } + elem { + name: "interface" + key { key: "name" value: "$0" } + } + elem { name: "state" } + elem { name: "id" } + } + type: STATE)pb", + port), + &req)) { + return gutil::InternalErrorBuilder().LogError() + << "Failed to parse request into proto."; + } + return req; + } + + absl::StatusOr portIDGetResp(absl::string_view port, + absl::string_view id) { + gnmi::GetResponse resp; + if (!google::protobuf::TextFormat::ParseFromString(absl::Substitute( + R"pb(notification { + timestamp: 1631864194292383538 + prefix { origin: "openconfig" } + update { + path { + elem { name: "openconfig-interfaces:interfaces" } + elem { + name: "interface" + key: { key: "name" value: "$0" } + } + elem { name: "state" } + elem { name: "id" } + } + val { + json_ietf_val: "{\"openconfig-interfaces:id\":$1}" + } + } + })pb", + port, id), + &resp)) { + return gutil::InternalErrorBuilder().LogError() + << "Failed to parse response into proto."; + } + return resp; + } + thinkit::MockSwitch mock_switch_; gnmi::MockgNMIStub mock_gnmi_stub_; }; @@ -1484,7 +1537,7 @@ TEST_F(GNMIThinkitInterfaceUtilityTest, replace { path {} val { - json_ietf_val: "{\n \"openconfig-interfaces:interfaces\": { \"interface\": [ {\n \"config\": {\n \"enabled\": true,\n \"loopback-mode\": false,\n \"mtu\": 9216,\n \"name\": \"Ethernet1/1/1\",\n \"type\": \"iana-if-type:ethernetCsmacd\"\n },\n \"name\": \"Ethernet1/1/1\",\n \"openconfig-if-ethernet:ethernet\": {\n \"config\": { \"port-speed\": \"openconfig-if-ethernet:SPEED_400GB\" }\n },\n \"subinterfaces\": {\n \"subinterface\":\n [ {\n \"config\": { \"index\": 0 },\n \"index\": 0,\n \"openconfig-if-ip:ipv6\": {\n \"unnumbered\": { \"config\": { \"enabled\": true } }\n }\n }]\n }\n }\n ] },\n \"openconfig-platform:components\": {\n \"component\":\n [ {\n \"name\": \"1/1\",\n \"config\": { \"name\": \"1/1\" },\n \"port\": {\n \"config\": { \"port-id\": 1 },\n \"breakout-mode\": { \"groups\": { \"group\": [ {\n \"config\": {\n \"breakout-speed\": \"openconfig-if-ethernet:SPEED_400GB\",\n \"index\": 0,\n \"num-breakouts\": 1,\n \"num-physical-channels\": 8\n },\n \"index\": 0\n } ] } }\n }\n }]\n }\n }" + json_ietf_val: "{\n \"openconfig-interfaces:interfaces\": { \"interface\": [ {\n \"config\": {\n \"enabled\": true,\n \"loopback-mode\": false,\n \"mtu\": 9216,\n \"name\": \"Ethernet1/1/1\",\n \"type\": \"iana-if-type:ethernetCsmacd\",\n \"id\": 1\n },\n \"name\": \"Ethernet1/1/1\",\n \"openconfig-if-ethernet:ethernet\": {\n \"config\": { \"port-speed\": \"openconfig-if-ethernet:SPEED_400GB\" }\n },\n \"subinterfaces\": {\n \"subinterface\":\n [ {\n \"config\": { \"index\": 0 },\n \"index\": 0,\n \"openconfig-if-ip:ipv6\": {\n \"unnumbered\": { \"config\": { \"enabled\": true } }\n }\n }]\n }\n }\n ] },\n \"openconfig-platform:components\": {\n \"component\":\n [ {\n \"name\": \"1/1\",\n \"config\": { \"name\": \"1/1\" },\n \"port\": {\n \"config\": { \"port-id\": 1 },\n \"breakout-mode\": { \"groups\": { \"group\": [ {\n \"config\": {\n \"breakout-speed\": \"openconfig-if-ethernet:SPEED_400GB\",\n \"index\": 0,\n \"num-breakouts\": 1,\n \"num-physical-channels\": 8\n },\n \"index\": 0\n } ] } }\n }\n }]\n }\n }" } })pb"; ASSERT_TRUE(google::protobuf::TextFormat::ParseFromString( @@ -1508,6 +1561,10 @@ TEST_F(GNMIThinkitInterfaceUtilityTest, EXPECT_CALL(*mock_gnmi_stub_ptr, Get(_, EqualsProto(ethernet_pmd_req), _)) .WillOnce( DoAll(SetArgPointee<2>(ethernet_pmd_resp), Return(grpc::Status::OK))); + ASSERT_OK_AND_ASSIGN(auto id_req, portIDGetReq("Ethernet1/1/1")); + ASSERT_OK_AND_ASSIGN(auto id_resp, portIDGetResp("Ethernet1/1/1", "1")); + EXPECT_CALL(*mock_gnmi_stub_ptr, Get(_, EqualsProto(id_req), _)) + .WillOnce(DoAll(SetArgPointee<2>(id_resp), Return(grpc::Status::OK))); ASSERT_OK(pins_test::GetBreakoutModeConfigFromString( req, mock_gnmi_stub_ptr.get(), port_index, intf_name, breakout_mode)); EXPECT_THAT(req, EqualsProto(expected_breakout_config)); @@ -1524,7 +1581,56 @@ TEST_F(GNMIThinkitInterfaceUtilityTest, replace { path {} val { - json_ietf_val: "{\n \"openconfig-interfaces:interfaces\": { \"interface\": [ {\n \"config\": {\n \"enabled\": true,\n \"loopback-mode\": false,\n \"mtu\": 9216,\n \"name\": \"Ethernet1/1/1\",\n \"type\": \"iana-if-type:ethernetCsmacd\"\n },\n \"name\": \"Ethernet1/1/1\",\n \"openconfig-if-ethernet:ethernet\": {\n \"config\": { \"port-speed\": \"openconfig-if-ethernet:SPEED_200GB\" }\n },\n \"subinterfaces\": {\n \"subinterface\":\n [ {\n \"config\": { \"index\": 0 },\n \"index\": 0,\n \"openconfig-if-ip:ipv6\": {\n \"unnumbered\": { \"config\": { \"enabled\": true } }\n }\n }]\n }\n }\n ,{\n \"config\": {\n \"enabled\": true,\n \"loopback-mode\": false,\n \"mtu\": 9216,\n \"name\": \"Ethernet1/1/5\",\n \"type\": \"iana-if-type:ethernetCsmacd\"\n },\n \"name\": \"Ethernet1/1/5\",\n \"openconfig-if-ethernet:ethernet\": {\n \"config\": { \"port-speed\": \"openconfig-if-ethernet:SPEED_200GB\" }\n },\n \"subinterfaces\": {\n \"subinterface\":\n [ {\n \"config\": { \"index\": 0 },\n \"index\": 0,\n \"openconfig-if-ip:ipv6\": {\n \"unnumbered\": { \"config\": { \"enabled\": true } }\n }\n }]\n }\n }\n ] },\n \"openconfig-platform:components\": {\n \"component\":\n [ {\n \"name\": \"1/1\",\n \"config\": { \"name\": \"1/1\" },\n \"port\": {\n \"config\": { \"port-id\": 1 },\n \"breakout-mode\": { \"groups\": { \"group\": [ {\n \"config\": {\n \"breakout-speed\": \"openconfig-if-ethernet:SPEED_200GB\",\n \"index\": 0,\n \"num-breakouts\": 2,\n \"num-physical-channels\": 4\n },\n \"index\": 0\n } ] } }\n }\n }]\n }\n }" + json_ietf_val: "{\n \"openconfig-interfaces:interfaces\": { \"interface\": [ {\n \"config\": {\n \"enabled\": true,\n \"loopback-mode\": false,\n \"mtu\": 9216,\n \"name\": \"Ethernet1/1/1\",\n \"type\": \"iana-if-type:ethernetCsmacd\",\n \"id\": 1\n },\n \"name\": \"Ethernet1/1/1\",\n \"openconfig-if-ethernet:ethernet\": {\n \"config\": { \"port-speed\": \"openconfig-if-ethernet:SPEED_200GB\" }\n },\n \"subinterfaces\": {\n \"subinterface\":\n [ {\n \"config\": { \"index\": 0 },\n \"index\": 0,\n \"openconfig-if-ip:ipv6\": {\n \"unnumbered\": { \"config\": { \"enabled\": true } }\n }\n }]\n }\n }\n ,{\n \"config\": {\n \"enabled\": true,\n \"loopback-mode\": false,\n \"mtu\": 9216,\n \"name\": \"Ethernet1/1/5\",\n \"type\": \"iana-if-type:ethernetCsmacd\",\n \"id\": 2562\n },\n \"name\": \"Ethernet1/1/5\",\n \"openconfig-if-ethernet:ethernet\": {\n \"config\": { \"port-speed\": \"openconfig-if-ethernet:SPEED_200GB\" }\n },\n \"subinterfaces\": {\n \"subinterface\":\n [ {\n \"config\": { \"index\": 0 },\n \"index\": 0,\n \"openconfig-if-ip:ipv6\": {\n \"unnumbered\": { \"config\": { \"enabled\": true } }\n }\n }]\n }\n }\n ] },\n \"openconfig-platform:components\": {\n \"component\":\n [ {\n \"name\": \"1/1\",\n \"config\": { \"name\": \"1/1\" },\n \"port\": {\n \"config\": { \"port-id\": 1 },\n \"breakout-mode\": { \"groups\": { \"group\": [ {\n \"config\": {\n \"breakout-speed\": \"openconfig-if-ethernet:SPEED_200GB\",\n \"index\": 0,\n \"num-breakouts\": 2,\n \"num-physical-channels\": 4\n },\n \"index\": 0\n } ] } }\n }\n }]\n }\n }" + } + } + )pb"; + ASSERT_TRUE(google::protobuf::TextFormat::ParseFromString( + expected_breakout_config_str, &expected_breakout_config)); + auto mock_gnmi_stub_ptr = absl::make_unique(); + gnmi::GetRequest get_xcvrd_req; + ASSERT_TRUE(google::protobuf::TextFormat::ParseFromString(get_xcvrd_req_str, + &get_xcvrd_req)); + gnmi::GetResponse get_xcvrd_resp; + ASSERT_TRUE(google::protobuf::TextFormat::ParseFromString(get_xcvrd_resp_str, + &get_xcvrd_resp)); + EXPECT_CALL(*mock_gnmi_stub_ptr, Get(_, EqualsProto(get_xcvrd_req), _)) + .WillOnce( + DoAll(SetArgPointee<2>(get_xcvrd_resp), Return(grpc::Status::OK))); + gnmi::GetRequest ethernet_pmd_req; + ASSERT_TRUE(google::protobuf::TextFormat::ParseFromString( + ethernet_pmd_req_str, ðernet_pmd_req)); + gnmi::GetResponse ethernet_pmd_resp; + ASSERT_TRUE(google::protobuf::TextFormat::ParseFromString( + ethernet_pmd_resp_optic_str, ðernet_pmd_resp)); + EXPECT_CALL(*mock_gnmi_stub_ptr, Get(_, EqualsProto(ethernet_pmd_req), _)) + .WillOnce( + DoAll(SetArgPointee<2>(ethernet_pmd_resp), Return(grpc::Status::OK))); + ASSERT_OK_AND_ASSIGN(auto id_req1, portIDGetReq("Ethernet1/1/1")); + ASSERT_OK_AND_ASSIGN(auto id_resp1, portIDGetResp("Ethernet1/1/1", "1")); + ASSERT_OK_AND_ASSIGN(auto id_req2, portIDGetReq("Ethernet1/1/5")); + ASSERT_OK_AND_ASSIGN(auto id_resp2, portIDGetResp("Ethernet1/1/5", "2562")); + EXPECT_CALL(*mock_gnmi_stub_ptr, Get(_, EqualsProto(id_req1), _)) + .WillOnce(DoAll(SetArgPointee<2>(id_resp1), Return(grpc::Status::OK))); + EXPECT_CALL(*mock_gnmi_stub_ptr, Get(_, EqualsProto(id_req2), _)) + .WillOnce(DoAll(SetArgPointee<2>(id_resp2), Return(grpc::Status::OK))); + ASSERT_OK(pins_test::GetBreakoutModeConfigFromString( + req, mock_gnmi_stub_ptr.get(), port_index, intf_name, breakout_mode)); + EXPECT_THAT(req, EqualsProto(expected_breakout_config)); +} + +TEST_F(GNMIThinkitInterfaceUtilityTest, + TestGetBreakoutModeConfigFromStringWithComputedIDSuccess) { + const std::string port_index = "1"; + const std::string intf_name = "Ethernet1/1/1"; + const std::string breakout_mode = "2x200G"; + gnmi::SetRequest req, expected_breakout_config; + const std::string expected_breakout_config_str = R"pb( + prefix { origin: "openconfig" } + replace { + path {} + val { + json_ietf_val: "{\n \"openconfig-interfaces:interfaces\": { \"interface\": [ {\n \"config\": {\n \"enabled\": true,\n \"loopback-mode\": false,\n \"mtu\": 9216,\n \"name\": \"Ethernet1/1/1\",\n \"type\": \"iana-if-type:ethernetCsmacd\",\n \"id\": 1\n },\n \"name\": \"Ethernet1/1/1\",\n \"openconfig-if-ethernet:ethernet\": {\n \"config\": { \"port-speed\": \"openconfig-if-ethernet:SPEED_200GB\" }\n },\n \"subinterfaces\": {\n \"subinterface\":\n [ {\n \"config\": { \"index\": 0 },\n \"index\": 0,\n \"openconfig-if-ip:ipv6\": {\n \"unnumbered\": { \"config\": { \"enabled\": true } }\n }\n }]\n }\n }\n ,{\n \"config\": {\n \"enabled\": true,\n \"loopback-mode\": false,\n \"mtu\": 9216,\n \"name\": \"Ethernet1/1/5\",\n \"type\": \"iana-if-type:ethernetCsmacd\",\n \"id\": 2562\n },\n \"name\": \"Ethernet1/1/5\",\n \"openconfig-if-ethernet:ethernet\": {\n \"config\": { \"port-speed\": \"openconfig-if-ethernet:SPEED_200GB\" }\n },\n \"subinterfaces\": {\n \"subinterface\":\n [ {\n \"config\": { \"index\": 0 },\n \"index\": 0,\n \"openconfig-if-ip:ipv6\": {\n \"unnumbered\": { \"config\": { \"enabled\": true } }\n }\n }]\n }\n }\n ] },\n \"openconfig-platform:components\": {\n \"component\":\n [ {\n \"name\": \"1/1\",\n \"config\": { \"name\": \"1/1\" },\n \"port\": {\n \"config\": { \"port-id\": 1 },\n \"breakout-mode\": { \"groups\": { \"group\": [ {\n \"config\": {\n \"breakout-speed\": \"openconfig-if-ethernet:SPEED_200GB\",\n \"index\": 0,\n \"num-breakouts\": 2,\n \"num-physical-channels\": 4\n },\n \"index\": 0\n } ] } }\n }\n }]\n }\n }" } } )pb"; @@ -1549,6 +1655,12 @@ TEST_F(GNMIThinkitInterfaceUtilityTest, EXPECT_CALL(*mock_gnmi_stub_ptr, Get(_, EqualsProto(ethernet_pmd_req), _)) .WillOnce( DoAll(SetArgPointee<2>(ethernet_pmd_resp), Return(grpc::Status::OK))); + ASSERT_OK_AND_ASSIGN(auto id_req1, portIDGetReq("Ethernet1/1/1")); + ASSERT_OK_AND_ASSIGN(auto id_req2, portIDGetReq("Ethernet1/1/5")); + EXPECT_CALL(*mock_gnmi_stub_ptr, Get(_, EqualsProto(id_req1), _)) + .WillOnce(Return(grpc::Status(grpc::StatusCode::DEADLINE_EXCEEDED, ""))); + EXPECT_CALL(*mock_gnmi_stub_ptr, Get(_, EqualsProto(id_req2), _)) + .WillOnce(Return(grpc::Status(grpc::StatusCode::DEADLINE_EXCEEDED, ""))); ASSERT_OK(pins_test::GetBreakoutModeConfigFromString( req, mock_gnmi_stub_ptr.get(), port_index, intf_name, breakout_mode)); EXPECT_THAT(req, EqualsProto(expected_breakout_config)); @@ -1565,7 +1677,7 @@ TEST_F(GNMIThinkitInterfaceUtilityTest, replace { path {} val { - json_ietf_val: "{\n \"openconfig-interfaces:interfaces\": { \"interface\": [ {\n \"config\": {\n \"enabled\": true,\n \"loopback-mode\": false,\n \"mtu\": 9216,\n \"name\": \"Ethernet1/1/1\",\n \"type\": \"iana-if-type:ethernetCsmacd\"\n },\n \"name\": \"Ethernet1/1/1\",\n \"openconfig-if-ethernet:ethernet\": {\n \"config\": { \"port-speed\": \"openconfig-if-ethernet:SPEED_200GB\" }\n },\n \"subinterfaces\": {\n \"subinterface\":\n [ {\n \"config\": { \"index\": 0 },\n \"index\": 0,\n \"openconfig-if-ip:ipv6\": {\n \"unnumbered\": { \"config\": { \"enabled\": true } }\n }\n }]\n }\n }\n ,{\n \"config\": {\n \"enabled\": true,\n \"loopback-mode\": false,\n \"mtu\": 9216,\n \"name\": \"Ethernet1/1/5\",\n \"type\": \"iana-if-type:ethernetCsmacd\"\n },\n \"name\": \"Ethernet1/1/5\",\n \"openconfig-if-ethernet:ethernet\": {\n \"config\": { \"port-speed\": \"openconfig-if-ethernet:SPEED_100GB\" }\n },\n \"subinterfaces\": {\n \"subinterface\":\n [ {\n \"config\": { \"index\": 0 },\n \"index\": 0,\n \"openconfig-if-ip:ipv6\": {\n \"unnumbered\": { \"config\": { \"enabled\": true } }\n }\n }]\n }\n }\n ,{\n \"config\": {\n \"enabled\": true,\n \"loopback-mode\": false,\n \"mtu\": 9216,\n \"name\": \"Ethernet1/1/7\",\n \"type\": \"iana-if-type:ethernetCsmacd\"\n },\n \"name\": \"Ethernet1/1/7\",\n \"openconfig-if-ethernet:ethernet\": {\n \"config\": { \"port-speed\": \"openconfig-if-ethernet:SPEED_100GB\" }\n },\n \"subinterfaces\": {\n \"subinterface\":\n [ {\n \"config\": { \"index\": 0 },\n \"index\": 0,\n \"openconfig-if-ip:ipv6\": {\n \"unnumbered\": { \"config\": { \"enabled\": true } }\n }\n }]\n }\n }\n ] },\n \"openconfig-platform:components\": {\n \"component\":\n [ {\n \"name\": \"1/1\",\n \"config\": { \"name\": \"1/1\" },\n \"port\": {\n \"config\": { \"port-id\": 1 },\n \"breakout-mode\": { \"groups\": { \"group\": [ {\n \"config\": {\n \"breakout-speed\": \"openconfig-if-ethernet:SPEED_200GB\",\n \"index\": 0,\n \"num-breakouts\": 1,\n \"num-physical-channels\": 4\n },\n \"index\": 0\n },{\n \"config\": {\n \"breakout-speed\": \"openconfig-if-ethernet:SPEED_100GB\",\n \"index\": 1,\n \"num-breakouts\": 2,\n \"num-physical-channels\": 2\n },\n \"index\": 1\n } ] } }\n }\n }]\n }\n }" + json_ietf_val: "{\n \"openconfig-interfaces:interfaces\": { \"interface\": [ {\n \"config\": {\n \"enabled\": true,\n \"loopback-mode\": false,\n \"mtu\": 9216,\n \"name\": \"Ethernet1/1/1\",\n \"type\": \"iana-if-type:ethernetCsmacd\",\n \"id\": 1\n },\n \"name\": \"Ethernet1/1/1\",\n \"openconfig-if-ethernet:ethernet\": {\n \"config\": { \"port-speed\": \"openconfig-if-ethernet:SPEED_200GB\" }\n },\n \"subinterfaces\": {\n \"subinterface\":\n [ {\n \"config\": { \"index\": 0 },\n \"index\": 0,\n \"openconfig-if-ip:ipv6\": {\n \"unnumbered\": { \"config\": { \"enabled\": true } }\n }\n }]\n }\n }\n ,{\n \"config\": {\n \"enabled\": true,\n \"loopback-mode\": false,\n \"mtu\": 9216,\n \"name\": \"Ethernet1/1/5\",\n \"type\": \"iana-if-type:ethernetCsmacd\",\n \"id\": 2562\n },\n \"name\": \"Ethernet1/1/5\",\n \"openconfig-if-ethernet:ethernet\": {\n \"config\": { \"port-speed\": \"openconfig-if-ethernet:SPEED_100GB\" }\n },\n \"subinterfaces\": {\n \"subinterface\":\n [ {\n \"config\": { \"index\": 0 },\n \"index\": 0,\n \"openconfig-if-ip:ipv6\": {\n \"unnumbered\": { \"config\": { \"enabled\": true } }\n }\n }]\n }\n }\n ,{\n \"config\": {\n \"enabled\": true,\n \"loopback-mode\": false,\n \"mtu\": 9216,\n \"name\": \"Ethernet1/1/7\",\n \"type\": \"iana-if-type:ethernetCsmacd\",\n \"id\": 3586\n },\n \"name\": \"Ethernet1/1/7\",\n \"openconfig-if-ethernet:ethernet\": {\n \"config\": { \"port-speed\": \"openconfig-if-ethernet:SPEED_100GB\" }\n },\n \"subinterfaces\": {\n \"subinterface\":\n [ {\n \"config\": { \"index\": 0 },\n \"index\": 0,\n \"openconfig-if-ip:ipv6\": {\n \"unnumbered\": { \"config\": { \"enabled\": true } }\n }\n }]\n }\n }\n ] },\n \"openconfig-platform:components\": {\n \"component\":\n [ {\n \"name\": \"1/1\",\n \"config\": { \"name\": \"1/1\" },\n \"port\": {\n \"config\": { \"port-id\": 1 },\n \"breakout-mode\": { \"groups\": { \"group\": [ {\n \"config\": {\n \"breakout-speed\": \"openconfig-if-ethernet:SPEED_200GB\",\n \"index\": 0,\n \"num-breakouts\": 1,\n \"num-physical-channels\": 4\n },\n \"index\": 0\n },{\n \"config\": {\n \"breakout-speed\": \"openconfig-if-ethernet:SPEED_100GB\",\n \"index\": 1,\n \"num-breakouts\": 2,\n \"num-physical-channels\": 2\n },\n \"index\": 1\n } ] } }\n }\n }]\n }\n }" } } )pb"; @@ -1590,6 +1702,18 @@ TEST_F(GNMIThinkitInterfaceUtilityTest, EXPECT_CALL(*mock_gnmi_stub_ptr, Get(_, EqualsProto(ethernet_pmd_req), _)) .WillOnce( DoAll(SetArgPointee<2>(ethernet_pmd_resp), Return(grpc::Status::OK))); + ASSERT_OK_AND_ASSIGN(auto id_req1, portIDGetReq("Ethernet1/1/1")); + ASSERT_OK_AND_ASSIGN(auto id_resp1, portIDGetResp("Ethernet1/1/1", "1")); + ASSERT_OK_AND_ASSIGN(auto id_req2, portIDGetReq("Ethernet1/1/5")); + ASSERT_OK_AND_ASSIGN(auto id_resp2, portIDGetResp("Ethernet1/1/5", "2562")); + ASSERT_OK_AND_ASSIGN(auto id_req3, portIDGetReq("Ethernet1/1/7")); + ASSERT_OK_AND_ASSIGN(auto id_resp3, portIDGetResp("Ethernet1/1/7", "3586")); + EXPECT_CALL(*mock_gnmi_stub_ptr, Get(_, EqualsProto(id_req1), _)) + .WillOnce(DoAll(SetArgPointee<2>(id_resp1), Return(grpc::Status::OK))); + EXPECT_CALL(*mock_gnmi_stub_ptr, Get(_, EqualsProto(id_req2), _)) + .WillOnce(DoAll(SetArgPointee<2>(id_resp2), Return(grpc::Status::OK))); + EXPECT_CALL(*mock_gnmi_stub_ptr, Get(_, EqualsProto(id_req3), _)) + .WillOnce(DoAll(SetArgPointee<2>(id_resp3), Return(grpc::Status::OK))); ASSERT_OK(pins_test::GetBreakoutModeConfigFromString( req, mock_gnmi_stub_ptr.get(), port_index, intf_name, breakout_mode)); EXPECT_THAT(req, EqualsProto(expected_breakout_config)); @@ -1606,7 +1730,7 @@ TEST_F(GNMIThinkitInterfaceUtilityTest, replace { path {} val { - json_ietf_val: "{\n \"openconfig-interfaces:interfaces\": { \"interface\": [ {\n \"config\": {\n \"enabled\": true,\n \"loopback-mode\": false,\n \"mtu\": 9216,\n \"name\": \"Ethernet1/1/1\",\n \"type\": \"iana-if-type:ethernetCsmacd\"\n },\n \"name\": \"Ethernet1/1/1\",\n \"openconfig-if-ethernet:ethernet\": {\n \"config\": {\n \"port-speed\": \"openconfig-if-ethernet:SPEED_200GB\",\n \"standalone-link-training\": true\n }\n },\n \"subinterfaces\": {\n \"subinterface\":\n [ {\n \"config\": { \"index\": 0 },\n \"index\": 0,\n \"openconfig-if-ip:ipv6\": {\n \"unnumbered\": { \"config\": { \"enabled\": true } }\n }\n }]\n }\n }\n ,{\n \"config\": {\n \"enabled\": true,\n \"loopback-mode\": false,\n \"mtu\": 9216,\n \"name\": \"Ethernet1/1/5\",\n \"type\": \"iana-if-type:ethernetCsmacd\"\n },\n \"name\": \"Ethernet1/1/5\",\n \"openconfig-if-ethernet:ethernet\": {\n \"config\": {\n \"port-speed\": \"openconfig-if-ethernet:SPEED_100GB\",\n \"standalone-link-training\": true\n }\n },\n \"subinterfaces\": {\n \"subinterface\":\n [ {\n \"config\": { \"index\": 0 },\n \"index\": 0,\n \"openconfig-if-ip:ipv6\": {\n \"unnumbered\": { \"config\": { \"enabled\": true } }\n }\n }]\n }\n }\n ,{\n \"config\": {\n \"enabled\": true,\n \"loopback-mode\": false,\n \"mtu\": 9216,\n \"name\": \"Ethernet1/1/7\",\n \"type\": \"iana-if-type:ethernetCsmacd\"\n },\n \"name\": \"Ethernet1/1/7\",\n \"openconfig-if-ethernet:ethernet\": {\n \"config\": {\n \"port-speed\": \"openconfig-if-ethernet:SPEED_100GB\",\n \"standalone-link-training\": true\n }\n },\n \"subinterfaces\": {\n \"subinterface\":\n [ {\n \"config\": { \"index\": 0 },\n \"index\": 0,\n \"openconfig-if-ip:ipv6\": {\n \"unnumbered\": { \"config\": { \"enabled\": true } }\n }\n }]\n }\n }\n ] },\n \"openconfig-platform:components\": {\n \"component\":\n [ {\n \"name\": \"1/1\",\n \"config\": { \"name\": \"1/1\" },\n \"port\": {\n \"config\": { \"port-id\": 1 },\n \"breakout-mode\": { \"groups\": { \"group\": [ {\n \"config\": {\n \"breakout-speed\": \"openconfig-if-ethernet:SPEED_200GB\",\n \"index\": 0,\n \"num-breakouts\": 1,\n \"num-physical-channels\": 4\n },\n \"index\": 0\n },{\n \"config\": {\n \"breakout-speed\": \"openconfig-if-ethernet:SPEED_100GB\",\n \"index\": 1,\n \"num-breakouts\": 2,\n \"num-physical-channels\": 2\n },\n \"index\": 1\n } ] } }\n }\n }]\n }\n }" + json_ietf_val: "{\n \"openconfig-interfaces:interfaces\": { \"interface\": [ {\n \"config\": {\n \"enabled\": true,\n \"loopback-mode\": false,\n \"mtu\": 9216,\n \"name\": \"Ethernet1/1/1\",\n \"type\": \"iana-if-type:ethernetCsmacd\",\n \"id\": 1\n },\n \"name\": \"Ethernet1/1/1\",\n \"openconfig-if-ethernet:ethernet\": {\n \"config\": {\n \"port-speed\": \"openconfig-if-ethernet:SPEED_200GB\",\n \"standalone-link-training\": true\n }\n },\n \"subinterfaces\": {\n \"subinterface\":\n [ {\n \"config\": { \"index\": 0 },\n \"index\": 0,\n \"openconfig-if-ip:ipv6\": {\n \"unnumbered\": { \"config\": { \"enabled\": true } }\n }\n }]\n }\n }\n ,{\n \"config\": {\n \"enabled\": true,\n \"loopback-mode\": false,\n \"mtu\": 9216,\n \"name\": \"Ethernet1/1/5\",\n \"type\": \"iana-if-type:ethernetCsmacd\",\n \"id\": 2562\n },\n \"name\": \"Ethernet1/1/5\",\n \"openconfig-if-ethernet:ethernet\": {\n \"config\": {\n \"port-speed\": \"openconfig-if-ethernet:SPEED_100GB\",\n \"standalone-link-training\": true\n }\n },\n \"subinterfaces\": {\n \"subinterface\":\n [ {\n \"config\": { \"index\": 0 },\n \"index\": 0,\n \"openconfig-if-ip:ipv6\": {\n \"unnumbered\": { \"config\": { \"enabled\": true } }\n }\n }]\n }\n }\n ,{\n \"config\": {\n \"enabled\": true,\n \"loopback-mode\": false,\n \"mtu\": 9216,\n \"name\": \"Ethernet1/1/7\",\n \"type\": \"iana-if-type:ethernetCsmacd\",\n \"id\": 3586\n },\n \"name\": \"Ethernet1/1/7\",\n \"openconfig-if-ethernet:ethernet\": {\n \"config\": {\n \"port-speed\": \"openconfig-if-ethernet:SPEED_100GB\",\n \"standalone-link-training\": true\n }\n },\n \"subinterfaces\": {\n \"subinterface\":\n [ {\n \"config\": { \"index\": 0 },\n \"index\": 0,\n \"openconfig-if-ip:ipv6\": {\n \"unnumbered\": { \"config\": { \"enabled\": true } }\n }\n }]\n }\n }\n ] },\n \"openconfig-platform:components\": {\n \"component\":\n [ {\n \"name\": \"1/1\",\n \"config\": { \"name\": \"1/1\" },\n \"port\": {\n \"config\": { \"port-id\": 1 },\n \"breakout-mode\": { \"groups\": { \"group\": [ {\n \"config\": {\n \"breakout-speed\": \"openconfig-if-ethernet:SPEED_200GB\",\n \"index\": 0,\n \"num-breakouts\": 1,\n \"num-physical-channels\": 4\n },\n \"index\": 0\n },{\n \"config\": {\n \"breakout-speed\": \"openconfig-if-ethernet:SPEED_100GB\",\n \"index\": 1,\n \"num-breakouts\": 2,\n \"num-physical-channels\": 2\n },\n \"index\": 1\n } ] } }\n }\n }]\n }\n }" } } )pb"; @@ -1631,6 +1755,19 @@ TEST_F(GNMIThinkitInterfaceUtilityTest, EXPECT_CALL(*mock_gnmi_stub_ptr, Get(_, EqualsProto(ethernet_pmd_req), _)) .WillOnce( DoAll(SetArgPointee<2>(ethernet_pmd_resp), Return(grpc::Status::OK))); + ASSERT_OK_AND_ASSIGN(auto id_req1, portIDGetReq("Ethernet1/1/1")); + ASSERT_OK_AND_ASSIGN(auto id_resp1, portIDGetResp("Ethernet1/1/1", "1")); + ASSERT_OK_AND_ASSIGN(auto id_req2, portIDGetReq("Ethernet1/1/5")); + ASSERT_OK_AND_ASSIGN(auto id_resp2, portIDGetResp("Ethernet1/1/5", "2562")); + ASSERT_OK_AND_ASSIGN(auto id_req3, portIDGetReq("Ethernet1/1/7")); + ASSERT_OK_AND_ASSIGN(auto id_resp3, portIDGetResp("Ethernet1/1/7", "3586")); + EXPECT_CALL(*mock_gnmi_stub_ptr, Get(_, EqualsProto(id_req1), _)) + .WillOnce(DoAll(SetArgPointee<2>(id_resp1), Return(grpc::Status::OK))); + EXPECT_CALL(*mock_gnmi_stub_ptr, Get(_, EqualsProto(id_req2), _)) + .WillOnce(DoAll(SetArgPointee<2>(id_resp2), Return(grpc::Status::OK))); + EXPECT_CALL(*mock_gnmi_stub_ptr, Get(_, EqualsProto(id_req3), _)) + .WillOnce(DoAll(SetArgPointee<2>(id_resp3), Return(grpc::Status::OK))); + ASSERT_OK(pins_test::GetBreakoutModeConfigFromString( req, mock_gnmi_stub_ptr.get(), port_index, intf_name, breakout_mode)); EXPECT_THAT(req, EqualsProto(expected_breakout_config)); @@ -2676,4 +2813,16 @@ TEST_F(GNMIThinkitInterfaceUtilityTest, TestIsParentPortIntConversionFailure) { HasSubstr("Failed to convert string (X) to integer"))); } +TEST_F(GNMIThinkitInterfaceUtilityTest, TestDeriveEkPhysicalPortSuccess) { + const std::string port = "Ethernet1/1/2"; + EXPECT_THAT(DeriveEkPhysicalPort(port), "phy-1/1"); +} + +TEST_F(GNMIThinkitInterfaceUtilityTest, TestDeriveEkPhysicalPortFailure) { + const std::string port = "Ethernet1/X"; + EXPECT_THAT( + DeriveEkPhysicalPort(port), + StatusIs(absl::StatusCode::kInternal, HasSubstr("no match found for "))); +} + } // namespace pins_test