From 63efb5f50d1a8565a0374684b828e6f6b9026a61 Mon Sep 17 00:00:00 2001 From: kishanps Date: Sun, 17 Nov 2024 20:13:55 -0800 Subject: [PATCH 1/7] [Thinkit] Adding thinkit_gnmi_interface_util_tests file. --- tests/BUILD.bazel | 20 ++ tests/thinkit_gnmi_interface_util_tests.cc | 281 +++++++++++++++++++++ 2 files changed, 301 insertions(+) create mode 100644 tests/thinkit_gnmi_interface_util_tests.cc diff --git a/tests/BUILD.bazel b/tests/BUILD.bazel index 5bca0289..39939d19 100644 --- a/tests/BUILD.bazel +++ b/tests/BUILD.bazel @@ -115,3 +115,23 @@ cc_library( "@com_google_googletest//:gtest", ], ) + +cc_test( + name = "thinkit_gnmi_interface_util_tests", + srcs = ["thinkit_gnmi_interface_util_tests.cc"], + deps = [ + ":thinkit_gnmi_tests", + "//gutil:proto_matchers", + "//gutil:status_matchers", + "//thinkit:mock_ssh_client", + "//thinkit:mock_switch", + "@com_github_gnmi//proto/gnmi:gnmi_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/status", + "@com_google_absl//absl/strings", + "@com_google_googletest//:gtest_main", + "@com_google_protobuf//:protobuf", + ], +) diff --git a/tests/thinkit_gnmi_interface_util_tests.cc b/tests/thinkit_gnmi_interface_util_tests.cc new file mode 100644 index 00000000..bc8923fe --- /dev/null +++ b/tests/thinkit_gnmi_interface_util_tests.cc @@ -0,0 +1,281 @@ +#include +#include +#include +#include + +#include "absl/container/flat_hash_map.h" +#include "absl/status/status.h" +#include "absl/strings/str_cat.h" +#include "absl/strings/string_view.h" +#include "gmock/gmock.h" +#include "google/protobuf/text_format.h" +#include "gtest/gtest.h" +#include "gutil/proto_matchers.h" +#include "gutil/status_matchers.h" +#include "include/nlohmann/json.hpp" +#include "proto/gnmi/gnmi.grpc.pb.h" +#include "proto/gnmi/gnmi.pb.h" +#include "proto/gnmi/gnmi_mock.grpc.pb.h" +#include "tests/thinkit_gnmi_interface_util.h" +#include "tests/thinkit_util.h" +#include "thinkit/mock_ssh_client.h" +#include "thinkit/mock_switch.h" + +namespace pins_test { +using gutil::EqualsProto; +using gutil::StatusIs; +using ::nlohmann::json; +using ::testing::_; +using ::testing::ContainerEq; +using ::testing::DoAll; +using ::testing::HasSubstr; +using ::testing::Return; +using ::testing::ReturnRefOfCopy; +using ::testing::SetArgPointee; + +class GNMIThinkitInterfaceUtilityTest : public ::testing::Test { + protected: + void SetUp() override { + ON_CALL(mock_switch_, ChassisName()) + .WillByDefault(ReturnRefOfCopy(std::string("chassis_1"))); + } + thinkit::MockSSHClient mock_ssh_client_; + thinkit::MockSwitch mock_switch_; + gnmi::MockgNMIStub mock_gnmi_stub_; +}; + +TEST_F(GNMIThinkitInterfaceUtilityTest, + TestGetSupportedBreakoutModesForPortAnySuccess) { + const std::string port = "Ethernet0"; + std::vector expected_breakout_modes = { + "1x400G", "2x200G", "2x100G", "2x40G", "4x100G"}; + + const std::string interface_info = + R"pb({ "breakout_modes": "1x400G, 2x200G[100G,40G], 4x100G" } + )pb"; + EXPECT_THAT(pins_test::GetSupportedBreakoutModesForPort(interface_info, port), + expected_breakout_modes); +} + +TEST_F(GNMIThinkitInterfaceUtilityTest, + TestGetSupportedBreakoutModesForPortChannelizedSuccess) { + const std::string port = "Ethernet0"; + std::vector expected_breakout_modes = {"2x200G", "2x100G", + "2x40G", "4x100G"}; + + const std::string interface_info = + R"pb({ "breakout_modes": "1x400G, 2x200G[100G,40G], 4x100G" } + )pb"; + EXPECT_THAT(pins_test::GetSupportedBreakoutModesForPort( + interface_info, port, pins_test::BreakoutType::kChannelized), + expected_breakout_modes); +} + +TEST_F(GNMIThinkitInterfaceUtilityTest, + TestGetSupportedBreakoutModesForPortBreakoutModesNotFoundFailure) { + const std::string port = "Ethernet0"; + const std::string interface_info = + R"pb({} + )pb"; + EXPECT_THAT( + pins_test::GetSupportedBreakoutModesForPort(interface_info, port), + StatusIs(absl::StatusCode::kInternal, + HasSubstr(absl::StrCat("Supported breakout modes not found for ", + port, " in platform.json")))); +} + +TEST_F(GNMIThinkitInterfaceUtilityTest, + TestGetSupportedBreakoutModesForPortNumBreakoutsIntConversionFailure) { + const std::string port = "Ethernet0"; + const std::string interface_info = + R"pb({ "breakout_modes": "Xx400G, 2x200G[100G,40G], 4x100G" } + )pb"; + EXPECT_THAT(pins_test::GetSupportedBreakoutModesForPort( + interface_info, port, pins_test::BreakoutType::kChannelized), + StatusIs(absl::StatusCode::kInternal, + HasSubstr("Failed to convert string (X) to integer"))); +} + +TEST_F(GNMIThinkitInterfaceUtilityTest, + TestGetRandomPortWithSupportedBreakoutModesAnySuccess) { + auto mock_gnmi_stub_ptr = absl::make_unique(); + absl::flat_hash_map + expected_port_info; + pins_test::RandomPortBreakoutInfo r; + r.port_name = "Ethernet0"; + r.curr_breakout_mode = "1x400G"; + r.supported_breakout_mode = "2x200G"; + expected_port_info["Ethernet0"] = r; + r.port_name = "Ethernet8"; + r.curr_breakout_mode = "2x200G"; + r.supported_breakout_mode = "1x400G"; + expected_port_info["Ethernet8"] = r; + const std::string platform_json_contents = + R"pb({ + "interfaces": { + "Ethernet0": { + "default_brkout_mode": "1x400G", + "breakout_modes": "1x400G, 2x200G[100G,40G]" + }, + "Ethernet8": { + "default_brkout_mode": "2x200G", + "breakout_modes": "1x400G, 2x200G[100G,40G]" + }, + "Ethernet12": { "breakout_modes": "1x200G[100G,40G]" } + } + } + )pb"; + gnmi::GetRequest req; + ASSERT_TRUE(google::protobuf::TextFormat::ParseFromString( + R"pb(prefix { origin: "openconfig" } + path { elem { name: "interfaces" } } + type: STATE)pb", + &req)); + gnmi::GetResponse resp; + ASSERT_TRUE(google::protobuf::TextFormat::ParseFromString( + R"pb(notification { + timestamp: 1631864194292383538 + prefix { origin: "openconfig" } + update { + path { elem { name: "interfaces" } } + val { + json_ietf_val: "{\"openconfig-interfaces:interfaces\":{\"interface\":[{\"name\":\"Ethernet0\",\"state\":{\"oper-status\":\"UP\"}},{\"name\":\"Ethernet8\",\"state\":{\"oper-status\":\"UP\"}}]}}" + } + } + } + )pb", + &resp)); + EXPECT_CALL(*mock_gnmi_stub_ptr, Get(_, EqualsProto(req), _)) + .WillOnce(DoAll(SetArgPointee<2>(resp), Return(grpc::Status::OK))); + auto random_port_info = pins_test::GetRandomPortWithSupportedBreakoutModes( + *mock_gnmi_stub_ptr, platform_json_contents); + ASSERT_OK(random_port_info.status()); + EXPECT_THAT(random_port_info.value().port_name, + expected_port_info[random_port_info.value().port_name].port_name); + EXPECT_THAT(random_port_info.value().curr_breakout_mode, + expected_port_info[random_port_info.value().port_name] + .curr_breakout_mode); + EXPECT_THAT(random_port_info.value().supported_breakout_mode, + expected_port_info[random_port_info.value().port_name] + .supported_breakout_mode); +} + +TEST_F(GNMIThinkitInterfaceUtilityTest, + TestGetRandomPortWithSupportedBreakoutModesChannelizedSuccess) { + auto mock_gnmi_stub_ptr = absl::make_unique(); + absl::flat_hash_map + expected_port_info; + pins_test::RandomPortBreakoutInfo r; + r.port_name = "Ethernet0"; + r.curr_breakout_mode = "1x400G"; + r.supported_breakout_mode = "2x200G"; + expected_port_info["Ethernet0"] = r; + const std::string platform_json_contents = + R"pb({ + "interfaces": { + "Ethernet0": { + "default_brkout_mode": "1x400G", + "breakout_modes": "1x400G, 2x200G[100G,40G]" + }, + "Ethernet8": { + "default_brkout_mode": "1x400G", + "breakout_modes": "1x400G" + }, + "Ethernet16": { + "default_brkout_mode": "1x400G", + "breakout_modes": "1x400G" + } + } + } + )pb"; + gnmi::GetRequest req; + ASSERT_TRUE(google::protobuf::TextFormat::ParseFromString( + R"pb(prefix { origin: "openconfig" } + path { elem { name: "interfaces" } } + type: STATE)pb", + &req)); + gnmi::GetResponse resp; + ASSERT_TRUE(google::protobuf::TextFormat::ParseFromString( + R"pb(notification { + timestamp: 1631864194292383538 + prefix { origin: "openconfig" } + update { + path { elem { name: "interfaces" } } + val { + json_ietf_val: "{\"openconfig-interfaces:interfaces\":{\"interface\":[{\"name\":\"Ethernet0\",\"state\":{\"oper-status\":\"UP\"}},{\"name\":\"Ethernet8\",\"state\":{\"oper-status\":\"UP\"}},{\"name\":\"Ethernet16\",\"state\":{\"oper-status\":\"UP\"}}]}}" + } + } + } + )pb", + &resp)); + EXPECT_CALL(*mock_gnmi_stub_ptr, Get(_, EqualsProto(req), _)) + .WillOnce(DoAll(SetArgPointee<2>(resp), Return(grpc::Status::OK))); + auto random_port_info = pins_test::GetRandomPortWithSupportedBreakoutModes( + *mock_gnmi_stub_ptr, platform_json_contents, + pins_test::BreakoutType::kChannelized); + ASSERT_OK(random_port_info.status()); + EXPECT_THAT(random_port_info.value().port_name, + expected_port_info[random_port_info.value().port_name].port_name); + EXPECT_THAT(random_port_info.value().curr_breakout_mode, + expected_port_info[random_port_info.value().port_name] + .curr_breakout_mode); + EXPECT_THAT(random_port_info.value().supported_breakout_mode, + expected_port_info[random_port_info.value().port_name] + .supported_breakout_mode); +} + +TEST_F(GNMIThinkitInterfaceUtilityTest, + TestGetRandomPortWithSupportedBreakoutModesOperStatusMapGetFailure) { + auto mock_gnmi_stub_ptr = absl::make_unique(); + gnmi::GetRequest req; + ASSERT_TRUE(google::protobuf::TextFormat::ParseFromString( + R"pb(prefix { origin: "openconfig" } + path { elem { name: "interfaces" } } + type: STATE)pb", + &req)); + EXPECT_CALL(*mock_gnmi_stub_ptr, Get(_, EqualsProto(req), _)) + .WillOnce(Return(grpc::Status(grpc::StatusCode::DEADLINE_EXCEEDED, ""))); + EXPECT_THAT( + pins_test::GetRandomPortWithSupportedBreakoutModes(*mock_gnmi_stub_ptr, + ""), + StatusIs(absl::StatusCode::kDeadlineExceeded, + HasSubstr("Failed to get oper-status map for ports on switch"))); +} + +TEST_F(GNMIThinkitInterfaceUtilityTest, + TestGetRandomPortWithSupportedBreakoutModesIntConversionFailure) { + auto mock_gnmi_stub_ptr = absl::make_unique(); + gnmi::GetRequest req; + ASSERT_TRUE(google::protobuf::TextFormat::ParseFromString( + R"pb(prefix { origin: "openconfig" } + path { elem { name: "interfaces" } } + type: STATE)pb", + &req)); + ASSERT_TRUE(google::protobuf::TextFormat::ParseFromString( + R"pb(prefix { origin: "openconfig" } + path { elem { name: "interfaces" } } + type: STATE)pb", + &req)); + gnmi::GetResponse resp; + ASSERT_TRUE(google::protobuf::TextFormat::ParseFromString( + R"pb(notification { + timestamp: 1631864194292383538 + prefix { origin: "openconfig" } + update { + path { elem { name: "interfaces" } } + val { + json_ietf_val: "{\"openconfig-interfaces:interfaces\":{\"interface\":[{\"name\":\"EthernetX\",\"state\":{\"oper-status\":\"UP\"}}, {\"name\":\"Ethernet8\",\"state\":{\"oper-status\":\"UP\"}}]}}" + } + } + } + )pb", + &resp)); + EXPECT_CALL(*mock_gnmi_stub_ptr, Get(_, EqualsProto(req), _)) + .WillOnce(DoAll(SetArgPointee<2>(resp), Return(grpc::Status::OK))); + EXPECT_THAT(pins_test::GetRandomPortWithSupportedBreakoutModes( + *mock_gnmi_stub_ptr, ""), + StatusIs(absl::StatusCode::kInternal, + HasSubstr("Failed to convert string (X) to integer"))); +} + +} // namespace pins_test From 8d8a858a8f4de9b6648f933e1c7c652cde566f33 Mon Sep 17 00:00:00 2001 From: kishanps Date: Sun, 17 Nov 2024 20:32:22 -0800 Subject: [PATCH 2/7] [Thinkit] Adding test cases to thinkit_gnmi_interface_util_tests file. --- tests/thinkit_gnmi_interface_util_tests.cc | 304 +++++++++++++++++++++ 1 file changed, 304 insertions(+) diff --git a/tests/thinkit_gnmi_interface_util_tests.cc b/tests/thinkit_gnmi_interface_util_tests.cc index bc8923fe..dadb48f9 100644 --- a/tests/thinkit_gnmi_interface_util_tests.cc +++ b/tests/thinkit_gnmi_interface_util_tests.cc @@ -278,4 +278,308 @@ TEST_F(GNMIThinkitInterfaceUtilityTest, HasSubstr("Failed to convert string (X) to integer"))); } +TEST_F(GNMIThinkitInterfaceUtilityTest, + TestGetRandomPortWithSupportedBreakoutModesNoOperUpPortsFailure) { + auto mock_gnmi_stub_ptr = absl::make_unique(); + const std::string platform_json_contents = + R"pb({ + "interfaces": { + "Ethernet0": { + "default_brkout_mode": "1x400G", + "breakout_modes": "1x400G, 2x200G[100G,40G]" + }, + "Ethernet8": { + "default_brkout_mode": "2x200G", + "breakout_modes": "1x400G, 2x200G[100G,40G]" + }, + "Ethernet12": { "breakout_modes": "1x200G[100G,40G]" } + } + } + )pb"; + gnmi::GetRequest req; + ASSERT_TRUE(google::protobuf::TextFormat::ParseFromString( + R"pb(prefix { origin: "openconfig" } + path { elem { name: "interfaces" } } + type: STATE)pb", + &req)); + gnmi::GetResponse resp; + ASSERT_TRUE(google::protobuf::TextFormat::ParseFromString( + R"pb(notification { + timestamp: 1631864194292383538 + prefix { origin: "openconfig" } + update { + path { elem { name: "interfaces" } } + val { + json_ietf_val: "{\"openconfig-interfaces:interfaces\":{\"interface\":[{\"name\":\"Ethernet0\",\"state\":{\"oper-status\":\"DOWN\"}}, {\"name\":\"Ethernet8\",\"state\":{\"oper-status\":\"DOWN\"}}]}}" + } + } + } + )pb", + &resp)); + EXPECT_CALL(*mock_gnmi_stub_ptr, Get(_, EqualsProto(req), _)) + .WillOnce(DoAll(SetArgPointee<2>(resp), Return(grpc::Status::OK))); + EXPECT_THAT( + pins_test::GetRandomPortWithSupportedBreakoutModes( + *mock_gnmi_stub_ptr, platform_json_contents), + StatusIs(absl::StatusCode::kInternal, + HasSubstr("No operationally up parent ports found on switch"))); +} + +TEST_F(GNMIThinkitInterfaceUtilityTest, + TestGetRandomPortWithSupportedBreakoutModesInterfacesNotFoundInFailure) { + auto mock_gnmi_stub_ptr = absl::make_unique(); + const std::string platform_json_contents = + R"pb({} + )pb"; + gnmi::GetRequest req; + ASSERT_TRUE(google::protobuf::TextFormat::ParseFromString( + R"pb(prefix { origin: "openconfig" } + path { elem { name: "interfaces" } } + type: STATE)pb", + &req)); + gnmi::GetResponse resp; + ASSERT_TRUE(google::protobuf::TextFormat::ParseFromString( + R"pb(notification { + timestamp: 1631864194292383538 + prefix { origin: "openconfig" } + update { + path { elem { name: "interfaces" } } + val { + json_ietf_val: "{\"openconfig-interfaces:interfaces\":{\"interface\":[{\"name\":\"Ethernet0\",\"state\":{\"oper-status\":\"UP\"}}]}}" + } + } + } + )pb", + &resp)); + EXPECT_CALL(*mock_gnmi_stub_ptr, Get(_, EqualsProto(req), _)) + .WillOnce(DoAll(SetArgPointee<2>(resp), Return(grpc::Status::OK))); + EXPECT_THAT(pins_test::GetRandomPortWithSupportedBreakoutModes( + *mock_gnmi_stub_ptr, platform_json_contents), + StatusIs(absl::StatusCode::kInternal, + HasSubstr("Interfaces not found in platform.json"))); +} + +TEST_F(GNMIThinkitInterfaceUtilityTest, + TestGetRandomPortWithSupportedBreakoutModesInterfaceNotFoundFailure) { + auto mock_gnmi_stub_ptr = absl::make_unique(); + const std::string platform_json_contents = + R"pb({ "interfaces": {} } + )pb"; + gnmi::GetRequest req; + ASSERT_TRUE(google::protobuf::TextFormat::ParseFromString( + R"pb(prefix { origin: "openconfig" } + path { elem { name: "interfaces" } } + type: STATE)pb", + &req)); + gnmi::GetResponse resp; + ASSERT_TRUE(google::protobuf::TextFormat::ParseFromString( + R"pb(notification { + timestamp: 1631864194292383538 + prefix { origin: "openconfig" } + update { + path { elem { name: "interfaces" } } + val { + json_ietf_val: "{\"openconfig-interfaces:interfaces\":{\"interface\":[{\"name\":\"Ethernet0\",\"state\":{\"oper-status\":\"UP\"}}]}}" + } + } + } + )pb", + &resp)); + EXPECT_CALL(*mock_gnmi_stub_ptr, Get(_, EqualsProto(req), _)) + .WillOnce(DoAll(SetArgPointee<2>(resp), Return(grpc::Status::OK))); + EXPECT_THAT( + pins_test::GetRandomPortWithSupportedBreakoutModes( + *mock_gnmi_stub_ptr, platform_json_contents), + StatusIs(absl::StatusCode::kInternal, + HasSubstr("Ethernet0 entry not found in platform.json"))); +} + +TEST_F(GNMIThinkitInterfaceUtilityTest, + TestGetRandomPortWithSupportedBreakoutModesDefaultModeNotFoundFailure) { + auto mock_gnmi_stub_ptr = absl::make_unique(); + const std::string platform_json_contents = + R"pb({ + "interfaces": { + "Ethernet0": { "breakout_modes": "1x400G, 2x200G[100G,40G]" } + } + } + )pb"; + gnmi::GetRequest req; + ASSERT_TRUE(google::protobuf::TextFormat::ParseFromString( + R"pb(prefix { origin: "openconfig" } + path { elem { name: "interfaces" } } + type: STATE)pb", + &req)); + gnmi::GetResponse resp; + ASSERT_TRUE(google::protobuf::TextFormat::ParseFromString( + R"pb(notification { + timestamp: 1631864194292383538 + prefix { origin: "openconfig" } + update { + path { elem { name: "interfaces" } } + val { + json_ietf_val: "{\"openconfig-interfaces:interfaces\":{\"interface\":[{\"name\":\"Ethernet0\",\"state\":{\"oper-status\":\"UP\"}}]}}" + } + } + } + )pb", + &resp)); + EXPECT_CALL(*mock_gnmi_stub_ptr, Get(_, EqualsProto(req), _)) + .WillOnce(DoAll(SetArgPointee<2>(resp), Return(grpc::Status::OK))); + EXPECT_THAT(pins_test::GetRandomPortWithSupportedBreakoutModes( + *mock_gnmi_stub_ptr, platform_json_contents), + StatusIs(absl::StatusCode::kInternal, + HasSubstr("Default breakout mode not found for " + "Ethernet0 in platform.json"))); +} + +TEST_F( + GNMIThinkitInterfaceUtilityTest, + TestGetRandomPortWithSupportedBreakoutModesSupportedModesNotFoundnFailure) { + auto mock_gnmi_stub_ptr = absl::make_unique(); + const std::string platform_json_contents = + R"pb({ + "interfaces": { "Ethernet0": { "default_brkout_mode": "1x400G" } } + } + )pb"; + gnmi::GetRequest req; + ASSERT_TRUE(google::protobuf::TextFormat::ParseFromString( + R"pb(prefix { origin: "openconfig" } + path { elem { name: "interfaces" } } + type: STATE)pb", + &req)); + gnmi::GetResponse resp; + ASSERT_TRUE(google::protobuf::TextFormat::ParseFromString( + R"pb(notification { + timestamp: 1631864194292383538 + prefix { origin: "openconfig" } + update { + path { elem { name: "interfaces" } } + val { + json_ietf_val: "{\"openconfig-interfaces:interfaces\":{\"interface\":[{\"name\":\"Ethernet0\",\"state\":{\"oper-status\":\"UP\"}}]}}" + } + } + } + )pb", + &resp)); + EXPECT_CALL(*mock_gnmi_stub_ptr, Get(_, EqualsProto(req), _)) + .WillOnce(DoAll(SetArgPointee<2>(resp), Return(grpc::Status::OK))); + EXPECT_THAT( + pins_test::GetRandomPortWithSupportedBreakoutModes( + *mock_gnmi_stub_ptr, platform_json_contents), + StatusIs(absl::StatusCode::kInternal, + HasSubstr( + "Breakout modes not found for Ethernet0 in platform.json"))); +} + +TEST_F( + GNMIThinkitInterfaceUtilityTest, + TestGetRandomPortWithSupportedBreakoutModesNoSupportedBreakoutTypeFailure) { + auto mock_gnmi_stub_ptr = absl::make_unique(); + const std::string platform_json_contents = + R"pb({ + "interfaces": { + "Ethernet0": { + "default_brkout_mode": "1x400G", + "breakout_modes": "1x400G" + }, + "Ethernet8": { + "default_brkout_mode": "1x400G", + "breakout_modes": "1x400G" + } + } + } + )pb"; + gnmi::GetRequest req; + ASSERT_TRUE(google::protobuf::TextFormat::ParseFromString( + R"pb(prefix { origin: "openconfig" } + path { elem { name: "interfaces" } } + type: STATE)pb", + &req)); + gnmi::GetResponse resp; + ASSERT_TRUE(google::protobuf::TextFormat::ParseFromString( + R"pb(notification { + timestamp: 1631864194292383538 + prefix { origin: "openconfig" } + update { + path { elem { name: "interfaces" } } + val { + json_ietf_val: "{\"openconfig-interfaces:interfaces\":{\"interface\":[{\"name\":\"Ethernet0\",\"state\":{\"oper-status\":\"UP\"}},{\"name\":\"Ethernet8\",\"state\":{\"oper-status\":\"UP\"}}]}}" + } + } + } + )pb", + &resp)); + EXPECT_CALL(*mock_gnmi_stub_ptr, Get(_, EqualsProto(req), _)) + .WillOnce(DoAll(SetArgPointee<2>(resp), Return(grpc::Status::OK))); + EXPECT_THAT( + pins_test::GetRandomPortWithSupportedBreakoutModes( + *mock_gnmi_stub_ptr, platform_json_contents, + pins_test::BreakoutType::kChannelized), + StatusIs(absl::StatusCode::kInternal, + HasSubstr( + "No random interface with supported breakout modes found"))); +} + +TEST_F(GNMIThinkitInterfaceUtilityTest, + TestGetExpectedPortInfoForBreakoutModeUnchannelizedBreakoutModeSuccess) { + const std::string port = "Ethernet0"; + absl::string_view breakout_mode = "1x400G"; + + auto breakout_info = + pins_test::GetExpectedPortInfoForBreakoutMode(port, breakout_mode); + ASSERT_OK(breakout_info.status()); + EXPECT_EQ(breakout_info.value()["Ethernet0"].physical_channels, + "[0,1,2,3,4,5,6,7]"); +} + +TEST_F(GNMIThinkitInterfaceUtilityTest, + TestGetExpectedPortInfoForBreakoutModeChannelizedBreakoutModeSuccess) { + const std::string port = "Ethernet0"; + absl::string_view breakout_mode = "2x200G"; + + auto breakout_info = + pins_test::GetExpectedPortInfoForBreakoutMode(port, breakout_mode); + ASSERT_OK(breakout_info.status()); + EXPECT_EQ(breakout_info.value()["Ethernet0"].physical_channels, "[0,1,2,3]"); + EXPECT_EQ(breakout_info.value()["Ethernet4"].physical_channels, "[4,5,6,7]"); +} + +TEST_F(GNMIThinkitInterfaceUtilityTest, + TestGetExpectedPortInfoForBreakoutModeMixedBreakoutModeSuccess) { + const std::string port = "Ethernet0"; + absl::string_view breakout_mode = "1x200G+2x100G"; + + auto breakout_info = + pins_test::GetExpectedPortInfoForBreakoutMode(port, breakout_mode); + ASSERT_OK(breakout_info.status()); + EXPECT_EQ(breakout_info.value()["Ethernet0"].physical_channels, "[0,1,2,3]"); + EXPECT_EQ(breakout_info.value()["Ethernet4"].physical_channels, "[4,5]"); + EXPECT_EQ(breakout_info.value()["Ethernet6"].physical_channels, "[6,7]"); +} + +TEST_F( + GNMIThinkitInterfaceUtilityTest, + TestGetExpectedPortInfoForBreakoutModeAlternatedMixedBreakoutModeSuccess) { + const std::string port = "Ethernet0"; + absl::string_view breakout_mode = "2x100G+1x200G"; + auto breakout_info = + pins_test::GetExpectedPortInfoForBreakoutMode(port, breakout_mode); + ASSERT_OK(breakout_info.status()); + EXPECT_EQ(breakout_info.value()["Ethernet0"].physical_channels, "[0,1]"); + EXPECT_EQ(breakout_info.value()["Ethernet2"].physical_channels, "[2,3]"); + EXPECT_EQ(breakout_info.value()["Ethernet4"].physical_channels, "[4,5,6,7]"); +} + +TEST_F(GNMIThinkitInterfaceUtilityTest, + TestGetExpectedPortInfoForBreakoutModeWithQuotesSuccess) { + const std::string port = "Ethernet0"; + absl::string_view breakout_mode = "\"1x400G\""; + auto breakout_info = + pins_test::GetExpectedPortInfoForBreakoutMode(port, breakout_mode); + ASSERT_OK(breakout_info.status()); + EXPECT_EQ(breakout_info.value()["Ethernet0"].physical_channels, + "[0,1,2,3,4,5,6,7]"); +} + } // namespace pins_test From 6b630dc0c5a4107f3470cf3bb298df3ec8599f48 Mon Sep 17 00:00:00 2001 From: kishanps Date: Mon, 18 Nov 2024 04:31:00 -0800 Subject: [PATCH 3/7] [Thinkit] Adding test cases to thinkit_gnmi_interface_util_tests file. --- tests/thinkit_gnmi_interface_util_tests.cc | 312 +++++++++++++++++++++ 1 file changed, 312 insertions(+) diff --git a/tests/thinkit_gnmi_interface_util_tests.cc b/tests/thinkit_gnmi_interface_util_tests.cc index dadb48f9..d25d0677 100644 --- a/tests/thinkit_gnmi_interface_util_tests.cc +++ b/tests/thinkit_gnmi_interface_util_tests.cc @@ -582,4 +582,316 @@ TEST_F(GNMIThinkitInterfaceUtilityTest, "[0,1,2,3,4,5,6,7]"); } +TEST_F(GNMIThinkitInterfaceUtilityTest, + TestGetExpectedPortInfoForBreakoutModeEmptyBreakoutModeFailure) { + const std::string port = "Ethernet0"; + absl::string_view breakout_mode = ""; + + EXPECT_THAT( + pins_test::GetExpectedPortInfoForBreakoutMode(port, breakout_mode), + StatusIs(absl::StatusCode::kInternal, + HasSubstr("Found empty breakout mode"))); +} + +TEST_F(GNMIThinkitInterfaceUtilityTest, + TestGetExpectedPortInfoForBreakoutModePortNumberIntConversionFailure) { + const std::string port = "EthernetX"; + absl::string_view breakout_mode = "1x400G"; + + EXPECT_THAT( + pins_test::GetExpectedPortInfoForBreakoutMode(port, breakout_mode), + StatusIs(absl::StatusCode::kInternal, + HasSubstr("Failed to convert string (X) to integer"))); +} + +TEST_F(GNMIThinkitInterfaceUtilityTest, + TestGetExpectedPortInfoForBreakoutModeNonParentPortFailure) { + const std::string port = "Ethernet4"; + absl::string_view breakout_mode = "1x400G"; + + EXPECT_THAT( + pins_test::GetExpectedPortInfoForBreakoutMode(port, breakout_mode), + StatusIs(absl::StatusCode::kInternal, + HasSubstr("Requested port (Ethernet4) is not a parent port"))); +} + +TEST_F(GNMIThinkitInterfaceUtilityTest, + TestGetExpectedPortInfoForBreakoutModeNumBreakoutsIntConversionFailure) { + const std::string port = "Ethernet0"; + absl::string_view breakout_mode = "InvalidNumBreakoutsx400G"; + + EXPECT_THAT( + pins_test::GetExpectedPortInfoForBreakoutMode(port, breakout_mode), + StatusIs( + absl::StatusCode::kInternal, + HasSubstr( + "Failed to convert string (InvalidNumBreakouts) to integer"))); +} + +TEST_F(GNMIThinkitInterfaceUtilityTest, + TestGetExpectedPortInfoForBreakoutModeInvalidBreakoutModeFailure) { + const std::string port = "Ethernet0"; + absl::string_view breakout_mode = "3x200G+2x100G"; + + EXPECT_THAT( + pins_test::GetExpectedPortInfoForBreakoutMode(port, breakout_mode), + StatusIs(absl::StatusCode::kInternal, + HasSubstr(absl::StrCat("Invalid breakout mode (", breakout_mode, + ") found")))); +} + +TEST_F(GNMIThinkitInterfaceUtilityTest, + TestGetBreakoutStateInfoForPortSuccess) { + auto mock_gnmi_stub_ptr = absl::make_unique(); + const std::string port = "Ethernet0"; + const std::string breakout_mode = "1x400G"; + gnmi::GetRequest physical_channels_req; + ASSERT_TRUE(google::protobuf::TextFormat::ParseFromString( + R"pb(prefix { origin: "openconfig" } + path { + elem { name: "interfaces" } + elem { + name: "interface" + key { key: "name" value: "Ethernet0" } + } + elem { name: "state" } + elem { name: "physical-channel" } + } + type: STATE)pb", + &physical_channels_req)); + gnmi::GetResponse physical_channels_resp; + ASSERT_TRUE(google::protobuf::TextFormat::ParseFromString( + R"pb(notification { + timestamp: 1632102697805380043 + prefix { origin: "openconfig" } + update { + path { + elem { name: "interfaces" } + elem { + name: "interface" + key { key: "name" value: "Ethernet0" } + } + elem { name: "state" } + elem { name: "physical-channel" } + } + val { + json_ietf_val: "{\"openconfig-platform-transceiver:physical-channel\":[0,1,2,3,4,5,6,7]}" + } + } + })pb", + &physical_channels_resp)); + gnmi::GetRequest oper_status_req; + ASSERT_TRUE(google::protobuf::TextFormat::ParseFromString( + R"pb(prefix { origin: "openconfig" } + path { + elem { name: "interfaces" } + elem { + name: "interface" + key { key: "name" value: "Ethernet0" } + } + elem { name: "state" } + elem { name: "oper-status" } + } + type: STATE)pb", + &oper_status_req)); + gnmi::GetResponse oper_status_resp; + ASSERT_TRUE(google::protobuf::TextFormat::ParseFromString( + R"pb(notification { + timestamp: 1632102697699213032 + prefix { origin: "openconfig" } + update { + path { + elem { name: "interfaces" } + elem { + name: "interface" + key { key: "name" value: "Ethernet0" } + } + elem { name: "state" } + elem { name: "oper-status" } + } + val { + json_ietf_val: "{\"openconfig-interfaces:oper-status\":\"UP\"}" + } + } + })pb", + &oper_status_resp)); + EXPECT_CALL(*mock_gnmi_stub_ptr, + Get(_, EqualsProto(physical_channels_req), _)) + .WillOnce(DoAll(SetArgPointee<2>(physical_channels_resp), + Return(grpc::Status::OK))); + EXPECT_CALL(*mock_gnmi_stub_ptr, Get(_, EqualsProto(oper_status_req), _)) + .WillOnce( + DoAll(SetArgPointee<2>(oper_status_resp), Return(grpc::Status::OK))); + auto breakout_state_info = pins_test::GetBreakoutStateInfoForPort( + mock_gnmi_stub_ptr.get(), port, breakout_mode); + ASSERT_OK(breakout_state_info.status()); + EXPECT_EQ(breakout_state_info.value()["Ethernet0"].physical_channels, + "[0,1,2,3,4,5,6,7]"); + EXPECT_EQ(breakout_state_info.value()["Ethernet0"].oper_status, "\"UP\""); +} + +TEST_F(GNMIThinkitInterfaceUtilityTest, + TestGetBreakoutStateInfoForPortExpectedBreakoutInfoFailure) { + auto mock_gnmi_stub_ptr = absl::make_unique(); + const std::string port = "Ethernet0"; + const std::string breakout_mode = ""; + EXPECT_THAT(pins_test::GetBreakoutStateInfoForPort(mock_gnmi_stub_ptr.get(), + port, breakout_mode), + StatusIs(absl::StatusCode::kInternal, + HasSubstr(("Found empty breakout mode")))); +} + +TEST_F(GNMIThinkitInterfaceUtilityTest, + TestGetBreakoutStateInfoForPortPhysicalChannelsGetFailure) { + auto mock_gnmi_stub_ptr = absl::make_unique(); + const std::string port = "Ethernet0"; + const std::string breakout_mode = "1x400G"; + gnmi::GetRequest physical_channels_req; + ASSERT_TRUE(google::protobuf::TextFormat::ParseFromString( + R"pb(prefix { origin: "openconfig" } + path { + elem { name: "interfaces" } + elem { + name: "interface" + key { key: "name" value: "Ethernet0" } + } + elem { name: "state" } + elem { name: "physical-channel" } + } + type: STATE)pb", + &physical_channels_req)); + gnmi::GetRequest oper_status_req; + ASSERT_TRUE(google::protobuf::TextFormat::ParseFromString( + R"pb(prefix { origin: "openconfig" } + path { + elem { name: "interfaces" } + elem { + name: "interface" + key { key: "name" value: "Ethernet0" } + } + elem { name: "state" } + elem { name: "oper-status" } + } + type: STATE)pb", + &oper_status_req)); + gnmi::GetResponse oper_status_resp; + ASSERT_TRUE(google::protobuf::TextFormat::ParseFromString( + R"pb(notification { + timestamp: 1632102697699213032 + prefix { origin: "openconfig" } + update { + path { + elem { name: "interfaces" } + elem { + name: "interface" + key { key: "name" value: "Ethernet0" } + } + elem { name: "state" } + elem { name: "oper-status" } + } + val { + json_ietf_val: "{\"openconfig-interfaces:oper-status\":\"UP\"}" + } + } + })pb", + &oper_status_resp)); + EXPECT_CALL(*mock_gnmi_stub_ptr, Get(_, EqualsProto(oper_status_req), _)) + .WillOnce( + DoAll(SetArgPointee<2>(oper_status_resp), Return(grpc::Status::OK))); + EXPECT_CALL(*mock_gnmi_stub_ptr, + Get(_, EqualsProto(physical_channels_req), _)) + .WillOnce(Return(grpc::Status(grpc::StatusCode::DEADLINE_EXCEEDED, ""))); + EXPECT_THAT(pins_test::GetBreakoutStateInfoForPort(mock_gnmi_stub_ptr.get(), + port, breakout_mode), + StatusIs(absl::StatusCode::kDeadlineExceeded, + HasSubstr("Failed to get GNMI state path value for " + "physical-channels for port Ethernet0"))); +} + +TEST_F(GNMIThinkitInterfaceUtilityTest, + TestGetBreakoutStateInfoForPortOperStatusGetFailure) { + auto mock_gnmi_stub_ptr = absl::make_unique(); + const std::string port = "Ethernet0"; + const std::string breakout_mode = "1x400G"; + gnmi::GetRequest oper_status_req; + ASSERT_TRUE(google::protobuf::TextFormat::ParseFromString( + R"pb(prefix { origin: "openconfig" } + path { + elem { name: "interfaces" } + elem { + name: "interface" + key { key: "name" value: "Ethernet0" } + } + elem { name: "state" } + elem { name: "oper-status" } + } + type: STATE)pb", + &oper_status_req)); + EXPECT_CALL(*mock_gnmi_stub_ptr, Get(_, EqualsProto(oper_status_req), _)) + .WillOnce(Return(grpc::Status(grpc::StatusCode::DEADLINE_EXCEEDED, ""))); + EXPECT_THAT(pins_test::GetBreakoutStateInfoForPort(mock_gnmi_stub_ptr.get(), + port, breakout_mode), + StatusIs(absl::StatusCode::kDeadlineExceeded, + HasSubstr("Failed to get GNMI state path value for " + "oper-status for port Ethernet0"))); +} + +TEST_F(GNMIThinkitInterfaceUtilityTest, + TestGetBreakoutModeConfigFromStringUnchannelizedBreakoutModeSuccess) { + const std::string port_index = "1"; + const std::string breakout_mode = "1x400G"; + gnmi::SetRequest req, expected_breakout_config; + const std::string expected_breakout_config_str = R"pb( + prefix { origin: "openconfig" } + replace { + path { + elem { name: "components" } + elem { + name: "component" + key { key: "name" value: "1/1" } + } + elem { name: "port" } + elem { name: "breakout-mode" } + } + val { + json_ietf_val: "{ \"openconfig-platform-port: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 } ] } }" + } + } + )pb"; + ASSERT_TRUE(google::protobuf::TextFormat::ParseFromString( + expected_breakout_config_str, &expected_breakout_config)); + ASSERT_OK(pins_test::GetBreakoutModeConfigFromString(req, port_index, + breakout_mode)); + EXPECT_THAT(req, EqualsProto(expected_breakout_config)); +} + +TEST_F(GNMIThinkitInterfaceUtilityTest, + TestGetBreakoutModeConfigFromStringChannelizedBreakoutModeSuccess) { + const std::string port_index = "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 { + elem { name: "components" } + elem { + name: "component" + key { key: "name" value: "1/1" } + } + elem { name: "port" } + elem { name: "breakout-mode" } + } + val { + json_ietf_val: "{ \"openconfig-platform-port: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 } ] } }" + } + } + )pb"; + ASSERT_TRUE(google::protobuf::TextFormat::ParseFromString( + expected_breakout_config_str, &expected_breakout_config)); + ASSERT_OK(pins_test::GetBreakoutModeConfigFromString(req, port_index, + breakout_mode)); + EXPECT_THAT(req, EqualsProto(expected_breakout_config)); +} + } // namespace pins_test From 601b9bbdd8162cbf6e941540a03ee036cebe682c Mon Sep 17 00:00:00 2001 From: kishanps Date: Mon, 18 Nov 2024 04:51:39 -0800 Subject: [PATCH 4/7] [Thinkit] Adding few more test cases to thinkit_gnmi_interface_util_tests file. --- tests/thinkit_gnmi_interface_util_tests.cc | 383 ++++++++++++++++++++- 1 file changed, 379 insertions(+), 4 deletions(-) diff --git a/tests/thinkit_gnmi_interface_util_tests.cc b/tests/thinkit_gnmi_interface_util_tests.cc index d25d0677..32e6d582 100644 --- a/tests/thinkit_gnmi_interface_util_tests.cc +++ b/tests/thinkit_gnmi_interface_util_tests.cc @@ -867,7 +867,7 @@ TEST_F(GNMIThinkitInterfaceUtilityTest, TEST_F(GNMIThinkitInterfaceUtilityTest, TestGetBreakoutModeConfigFromStringChannelizedBreakoutModeSuccess) { - const std::string port_index = "1"; + const std::string port_index = "1"; const std::string breakout_mode = "2x200G"; gnmi::SetRequest req, expected_breakout_config; const std::string expected_breakout_config_str = R"pb( @@ -881,11 +881,40 @@ TEST_F(GNMIThinkitInterfaceUtilityTest, } elem { name: "port" } elem { name: "breakout-mode" } - } + } val { json_ietf_val: "{ \"openconfig-platform-port: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 } ] } }" - } - } + } + } + )pb"; + ASSERT_TRUE(google::protobuf::TextFormat::ParseFromString( + expected_breakout_config_str, &expected_breakout_config)); + ASSERT_OK(pins_test::GetBreakoutModeConfigFromString(req, port_index, + breakout_mode)); + EXPECT_THAT(req, EqualsProto(expected_breakout_config)); +} + +TEST_F(GNMIThinkitInterfaceUtilityTest, + TestGetBreakoutModeConfigFromStringMixedBreakoutModeSuccess) { + const std::string port_index = "1"; + const std::string breakout_mode = "1x200G+2x100G"; + gnmi::SetRequest req, expected_breakout_config; + const std::string expected_breakout_config_str = R"pb( + prefix { origin: "openconfig" } + replace { + path { + elem { name: "components" } + elem { + name: "component" + key { key: "name" value: "1/1" } + } + elem { name: "port" } + elem { name: "breakout-mode" } + } + val { + json_ietf_val: "{ \"openconfig-platform-port: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 } ] } }" + } + } )pb"; ASSERT_TRUE(google::protobuf::TextFormat::ParseFromString( expected_breakout_config_str, &expected_breakout_config)); @@ -894,4 +923,350 @@ TEST_F(GNMIThinkitInterfaceUtilityTest, EXPECT_THAT(req, EqualsProto(expected_breakout_config)); } +TEST_F(GNMIThinkitInterfaceUtilityTest, + TestGetBreakoutModeConfigFromStringIntConversionFailure) { + const std::string port_index = "1"; + const std::string breakout_mode = "Xx400G"; + gnmi::SetRequest req; + EXPECT_THAT(pins_test::GetBreakoutModeConfigFromString(req, port_index, + breakout_mode), + StatusIs(absl::StatusCode::kInternal, + HasSubstr("Failed to convert string (X) to integer"))); +} + +TEST_F(GNMIThinkitInterfaceUtilityTest, + TestGetNonExistingPortsAfterBreakoutForBreakoutAppliedSuccess) { + absl::flat_hash_map + orig_breakout_info; + orig_breakout_info["Ethernet0"] = + pins_test::PortBreakoutInfo{"[0,1,2,3,4,5,6,7]", pins_test::kStateUp}; + absl::flat_hash_map + new_breakout_info; + new_breakout_info["Ethernet0"] = + pins_test::PortBreakoutInfo{"[0,1,2,3]", pins_test::kStateUp}; + new_breakout_info["Ethernet4"] = + pins_test::PortBreakoutInfo{"[4,5,6,7]", pins_test::kStateUp}; + + std::vector expected_non_existing_ports; + EXPECT_THAT(GetNonExistingPortsAfterBreakout(orig_breakout_info, + new_breakout_info, true), + ContainerEq(expected_non_existing_ports)); +} + +TEST_F(GNMIThinkitInterfaceUtilityTest, + TestGetNonExistingPortsAfterBreakoutForBreakoutAppliedAlternateSuccess) { + absl::flat_hash_map + orig_breakout_info; + orig_breakout_info["Ethernet0"] = + pins_test::PortBreakoutInfo{"[0,1,2,3]", pins_test::kStateUp}; + orig_breakout_info["Ethernet4"] = + pins_test::PortBreakoutInfo{"[4,5,6,7]", pins_test::kStateUp}; + absl::flat_hash_map + new_breakout_info; + new_breakout_info["Ethernet0"] = + pins_test::PortBreakoutInfo{"[0,1,2,3,4,5,6,7]", pins_test::kStateUp}; + + std::vector expected_non_existing_ports{"Ethernet4"}; + EXPECT_THAT(GetNonExistingPortsAfterBreakout(orig_breakout_info, + new_breakout_info, true), + ContainerEq(expected_non_existing_ports)); +} + +TEST_F(GNMIThinkitInterfaceUtilityTest, + TestGetNonExistingPortsAfterBreakoutForBreakoutNotAppliedSuccess) { + absl::flat_hash_map + orig_breakout_info; + orig_breakout_info["Ethernet0"] = + pins_test::PortBreakoutInfo{"[0,1,2,3,4,5,6,7]", pins_test::kStateUp}; + absl::flat_hash_map + new_breakout_info; + new_breakout_info["Ethernet0"] = + pins_test::PortBreakoutInfo{"[0,1,2,3]", pins_test::kStateDown}; + new_breakout_info["Ethernet4"] = + pins_test::PortBreakoutInfo{"[4,5,6,7]", pins_test::kStateDown}; + + std::vector expected_non_existing_ports{"Ethernet4"}; + EXPECT_THAT(GetNonExistingPortsAfterBreakout(orig_breakout_info, + new_breakout_info, false), + ContainerEq(expected_non_existing_ports)); +} + +TEST_F( + GNMIThinkitInterfaceUtilityTest, + TestGetNonExistingPortsAfterBreakoutForBreakoutNotAppliedAlternateSuccess) { + absl::flat_hash_map + orig_breakout_info; + orig_breakout_info["Ethernet0"] = + pins_test::PortBreakoutInfo{"[0,1,2,3]", pins_test::kStateUp}; + orig_breakout_info["Ethernet4"] = + pins_test::PortBreakoutInfo{"[4,5,6,7]", pins_test::kStateUp}; + absl::flat_hash_map + new_breakout_info; + new_breakout_info["Ethernet0"] = + pins_test::PortBreakoutInfo{"[0,1,2,3,4,5,6,7]", pins_test::kStateDown}; + + std::vector expected_non_existing_ports{}; + EXPECT_THAT(GetNonExistingPortsAfterBreakout(orig_breakout_info, + new_breakout_info, false), + ContainerEq(expected_non_existing_ports)); +} + +TEST_F(GNMIThinkitInterfaceUtilityTest, + TestValidateBreakoutStateEmptyExpectedInfoFailure) { + auto mock_gnmi_stub_ptr = absl::make_unique(); + absl::flat_hash_map + expected_port_info; + std::vector non_existing_port_list; + EXPECT_THAT( + pins_test::ValidateBreakoutState( + mock_gnmi_stub_ptr.get(), expected_port_info, non_existing_port_list), + StatusIs(absl::StatusCode::kInternal, + HasSubstr("Expected port info map is empty"))); +} + +TEST_F(GNMIThinkitInterfaceUtilityTest, + TestValidateBreakoutStateOperStatusMatchFailure) { + auto mock_gnmi_stub_ptr = absl::make_unique(); + absl::flat_hash_map + expected_port_info; + expected_port_info["Ethernet0"] = + pins_test::PortBreakoutInfo{"[0,1,2,3,4,5,6,7]", pins_test::kStateUp}; + std::vector non_existing_port_list; + gnmi::GetRequest oper_status_req; + ASSERT_TRUE(google::protobuf::TextFormat::ParseFromString( + R"pb(prefix { origin: "openconfig" } + path { + elem { name: "interfaces" } + elem { + name: "interface" + key { key: "name" value: "Ethernet0" } + } + elem { name: "state" } + elem { name: "oper-status" } + } + type: STATE)pb", + &oper_status_req)); + gnmi::GetResponse oper_status_resp; + ASSERT_TRUE(google::protobuf::TextFormat::ParseFromString( + R"pb(notification { + timestamp: 1632102697699213032 + prefix { origin: "openconfig" } + update { + path { + elem { name: "interfaces" } + elem { + name: "interface" + key { key: "name" value: "Ethernet0" } + } + elem { name: "state" } + elem { name: "oper-status" } + } + val { + json_ietf_val: "{\"openconfig-interfaces:oper-status\":\"DOWN\"}" + } + } + })pb", + &oper_status_resp)); + EXPECT_CALL(*mock_gnmi_stub_ptr, Get(_, EqualsProto(oper_status_req), _)) + .WillOnce( + DoAll(SetArgPointee<2>(oper_status_resp), Return(grpc::Status::OK))); + EXPECT_THAT( + pins_test::ValidateBreakoutState( + mock_gnmi_stub_ptr.get(), expected_port_info, non_existing_port_list), + StatusIs(absl::StatusCode::kInternal, + HasSubstr(absl::StrCat( + "Port oper-status match failed for port Ethernet0. got: \"", + pins_test::kStateDown, + "\", want:", expected_port_info["Ethernet0"].oper_status)))); +} + +TEST_F(GNMIThinkitInterfaceUtilityTest, + TestValidateBreakoutStatePhysicalChannelsMatchFailure) { + auto mock_gnmi_stub_ptr = absl::make_unique(); + absl::flat_hash_map + expected_port_info; + expected_port_info["Ethernet0"] = + pins_test::PortBreakoutInfo{"[0,1,2,3]", pins_test::kStateUp}; + std::vector non_existing_port_list; + gnmi::GetRequest oper_status_req; + ASSERT_TRUE(google::protobuf::TextFormat::ParseFromString( + R"pb(prefix { origin: "openconfig" } + path { + elem { name: "interfaces" } + elem { + name: "interface" + key { key: "name" value: "Ethernet0" } + } + elem { name: "state" } + elem { name: "oper-status" } + } + type: STATE)pb", + &oper_status_req)); + gnmi::GetResponse oper_status_resp; + ASSERT_TRUE(google::protobuf::TextFormat::ParseFromString( + R"pb(notification { + timestamp: 1632102697699213032 + prefix { origin: "openconfig" } + update { + path { + elem { name: "interfaces" } + elem { + name: "interface" + key { key: "name" value: "Ethernet0" } + } + elem { name: "state" } + elem { name: "oper-status" } + } + val { + json_ietf_val: "{\"openconfig-interfaces:oper-status\":\"UP\"}" + } + } + })pb", + &oper_status_resp)); + gnmi::GetRequest physical_channels_req; + ASSERT_TRUE(google::protobuf::TextFormat::ParseFromString( + R"pb(prefix { origin: "openconfig" } + path { + elem { name: "interfaces" } + elem { + name: "interface" + key { key: "name" value: "Ethernet0" } + } + elem { name: "state" } + elem { name: "physical-channel" } + } + type: STATE)pb", + &physical_channels_req)); + gnmi::GetResponse physical_channels_resp; + ASSERT_TRUE(google::protobuf::TextFormat::ParseFromString( + R"pb(notification { + timestamp: 1632102697805380043 + prefix { origin: "openconfig" } + update { + path { + elem { name: "interfaces" } + elem { + name: "interface" + key { key: "name" value: "Ethernet0" } + } + elem { name: "state" } + elem { name: "physical-channel" } + } + val { + json_ietf_val: "{\"openconfig-platform-transceiver:physical-channel\":[0,1,2,3,4,5,6,7]}" + } + } + })pb", + &physical_channels_resp)); + EXPECT_CALL(*mock_gnmi_stub_ptr, Get(_, EqualsProto(oper_status_req), _)) + .WillOnce( + DoAll(SetArgPointee<2>(oper_status_resp), Return(grpc::Status::OK))); + EXPECT_CALL(*mock_gnmi_stub_ptr, + Get(_, EqualsProto(physical_channels_req), _)) + .WillOnce(DoAll(SetArgPointee<2>(physical_channels_resp), + Return(grpc::Status::OK))); + EXPECT_THAT( + pins_test::ValidateBreakoutState( + mock_gnmi_stub_ptr.get(), expected_port_info, non_existing_port_list), + StatusIs(absl::StatusCode::kInternal, + HasSubstr(absl::StrCat( + "Physical channel match failed for port Ethernet0. got: " + "[0,1,2,3,4,5,6,7], want: ", + expected_port_info["Ethernet0"].physical_channels)))); +} + +TEST_F(GNMIThinkitInterfaceUtilityTest, + TestValidateBreakoutStateNonExistingPortListMatchFailure) { + auto mock_gnmi_stub_ptr = absl::make_unique(); + absl::flat_hash_map + expected_port_info; + expected_port_info["Ethernet0"] = + pins_test::PortBreakoutInfo{"[0,1,2,3,4,5,6,7]", pins_test::kStateUp}; + std::vector non_existing_port_list{"Ethernet0"}; + gnmi::GetRequest oper_status_req; + ASSERT_TRUE(google::protobuf::TextFormat::ParseFromString( + R"pb(prefix { origin: "openconfig" } + path { + elem { name: "interfaces" } + elem { + name: "interface" + key { key: "name" value: "Ethernet0" } + } + elem { name: "state" } + elem { name: "oper-status" } + } + type: STATE)pb", + &oper_status_req)); + gnmi::GetResponse oper_status_resp; + ASSERT_TRUE(google::protobuf::TextFormat::ParseFromString( + R"pb(notification { + timestamp: 1632102697699213032 + prefix { origin: "openconfig" } + update { + path { + elem { name: "interfaces" } + elem { + name: "interface" + key { key: "name" value: "Ethernet0" } + } + elem { name: "state" } + elem { name: "oper-status" } + } + val { + json_ietf_val: "{\"openconfig-interfaces:oper-status\":\"UP\"}" + } + } + })pb", + &oper_status_resp)); + gnmi::GetRequest physical_channels_req; + ASSERT_TRUE(google::protobuf::TextFormat::ParseFromString( + R"pb(prefix { origin: "openconfig" } + path { + elem { name: "interfaces" } + elem { + name: "interface" + key { key: "name" value: "Ethernet0" } + } + elem { name: "state" } + elem { name: "physical-channel" } + } + type: STATE)pb", + &physical_channels_req)); + gnmi::GetResponse physical_channels_resp; + ASSERT_TRUE(google::protobuf::TextFormat::ParseFromString( + R"pb(notification { + timestamp: 1632102697805380043 + prefix { origin: "openconfig" } + update { + path { + elem { name: "interfaces" } + elem { + name: "interface" + key { key: "name" value: "Ethernet0" } + } + elem { name: "state" } + elem { name: "physical-channel" } + } + val { + json_ietf_val: "{\"openconfig-platform-transceiver:physical-channel\":[0,1,2,3,4,5,6,7]}" + } + } + })pb", + &physical_channels_resp)); + EXPECT_CALL(*mock_gnmi_stub_ptr, Get(_, EqualsProto(oper_status_req), _)) + .Times(2) + .WillRepeatedly( + DoAll(SetArgPointee<2>(oper_status_resp), Return(grpc::Status::OK))); + EXPECT_CALL(*mock_gnmi_stub_ptr, + Get(_, EqualsProto(physical_channels_req), _)) + .WillOnce(DoAll(SetArgPointee<2>(physical_channels_resp), + Return(grpc::Status::OK))); + EXPECT_THAT( + pins_test::ValidateBreakoutState( + mock_gnmi_stub_ptr.get(), expected_port_info, non_existing_port_list), + StatusIs(absl::StatusCode::kInternal, + HasSubstr("Unexpected port (Ethernet0) found after " + "application of breakout mode"))); +} + } // namespace pins_test From 7de9f7f4ed6083342daefe78a27de95bb3e583d9 Mon Sep 17 00:00:00 2001 From: kishanps Date: Mon, 18 Nov 2024 05:46:45 -0800 Subject: [PATCH 5/7] [Thinkit] Adding few more test cases to thinkit_gnmi_interface_util_tests file --- tests/thinkit_gnmi_interface_util_tests.cc | 146 +++++++++++++++++++++ 1 file changed, 146 insertions(+) diff --git a/tests/thinkit_gnmi_interface_util_tests.cc b/tests/thinkit_gnmi_interface_util_tests.cc index 32e6d582..cab7b71a 100644 --- a/tests/thinkit_gnmi_interface_util_tests.cc +++ b/tests/thinkit_gnmi_interface_util_tests.cc @@ -1269,4 +1269,150 @@ TEST_F(GNMIThinkitInterfaceUtilityTest, "application of breakout mode"))); } +TEST_F(GNMIThinkitInterfaceUtilityTest, TestValidateBreakoutStateSuccess) { + auto mock_gnmi_stub_ptr = absl::make_unique(); + absl::flat_hash_map + expected_port_info; + expected_port_info["Ethernet0"] = + pins_test::PortBreakoutInfo{"[0,1,2,3,4,5,6,7]", pins_test::kStateUp}; + std::vector non_existing_port_list{}; + gnmi::GetRequest oper_status_req; + ASSERT_TRUE(google::protobuf::TextFormat::ParseFromString( + R"pb(prefix { origin: "openconfig" } + path { + elem { name: "interfaces" } + elem { + name: "interface" + key { key: "name" value: "Ethernet0" } + } + elem { name: "state" } + elem { name: "oper-status" } + } + type: STATE)pb", + &oper_status_req)); + gnmi::GetResponse oper_status_resp; + ASSERT_TRUE(google::protobuf::TextFormat::ParseFromString( + R"pb(notification { + timestamp: 1632102697699213032 + prefix { origin: "openconfig" } + update { + path { + elem { name: "interfaces" } + elem { + name: "interface" + key { key: "name" value: "Ethernet0" } + } + elem { name: "state" } + elem { name: "oper-status" } + } + val { + json_ietf_val: "{\"openconfig-interfaces:oper-status\":\"UP\"}" + } + } + })pb", + &oper_status_resp)); + gnmi::GetRequest physical_channels_req; + ASSERT_TRUE(google::protobuf::TextFormat::ParseFromString( + R"pb(prefix { origin: "openconfig" } + path { + elem { name: "interfaces" } + elem { + name: "interface" + key { key: "name" value: "Ethernet0" } + } + elem { name: "state" } + elem { name: "physical-channel" } + } + type: STATE)pb", + &physical_channels_req)); + gnmi::GetResponse physical_channels_resp; + ASSERT_TRUE(google::protobuf::TextFormat::ParseFromString( + R"pb(notification { + timestamp: 1632102697805380043 + prefix { origin: "openconfig" } + update { + path { + elem { name: "interfaces" } + elem { + name: "interface" + key { key: "name" value: "Ethernet0" } + } + elem { name: "state" } + elem { name: "physical-channel" } + } + val { + json_ietf_val: "{\"openconfig-platform-transceiver:physical-channel\":[0,1,2,3,4,5,6,7]}" + } + } + })pb", + &physical_channels_resp)); + EXPECT_CALL(*mock_gnmi_stub_ptr, Get(_, EqualsProto(oper_status_req), _)) + .WillOnce( + DoAll(SetArgPointee<2>(oper_status_resp), Return(grpc::Status::OK))); + EXPECT_CALL(*mock_gnmi_stub_ptr, + Get(_, EqualsProto(physical_channels_req), _)) + .WillOnce(DoAll(SetArgPointee<2>(physical_channels_resp), + Return(grpc::Status::OK))); + EXPECT_EQ( + pins_test::ValidateBreakoutState( + mock_gnmi_stub_ptr.get(), expected_port_info, non_existing_port_list), + absl::OkStatus()); +} + +TEST_F(GNMIThinkitInterfaceUtilityTest, TestGetPortIndexSuccess) { + const std::string platform_json_contents = + R"pb({ "interfaces": { "Ethernet0": { "index": "1,1,1,1,1,1,1,1" } } } + )pb"; + const std::string port = "Ethernet0"; + const std::string expected_port_index = "1"; + EXPECT_THAT(pins_test::GetPortIndex(platform_json_contents, port), + expected_port_index); +} + +TEST_F(GNMIThinkitInterfaceUtilityTest, + TestGetPortIndexInterfacesNotFoundFailure) { + const std::string platform_json_contents = + R"pb({} + )pb"; + const std::string port = "Ethernet0"; + const std::string expected_port_index = ""; + EXPECT_THAT(pins_test::GetPortIndex(platform_json_contents, port), + StatusIs(absl::StatusCode::kInternal, + HasSubstr("Interfaces not found in platform.json"))); +} + +TEST_F(GNMIThinkitInterfaceUtilityTest, + TestGetPortIndexInterfaceNotFoundFailure) { + const std::string platform_json_contents = + R"pb({ "interfaces": {} } + )pb"; + const std::string port = "Ethernet0"; + const std::string expected_port_index = ""; + EXPECT_THAT(pins_test::GetPortIndex(platform_json_contents, port), + StatusIs(absl::StatusCode::kInternal, + HasSubstr(absl::StrCat( + port, " entry not found in platform.json")))); +} + +TEST_F(GNMIThinkitInterfaceUtilityTest, TestGetPortIndexIndexNotFoundFailure) { + const std::string platform_json_contents = + R"pb({ "interfaces": { "Ethernet0": {} } } + )pb"; + const std::string port = "Ethernet0"; + const std::string expected_port_index = ""; + EXPECT_THAT(pins_test::GetPortIndex(platform_json_contents, port), + StatusIs(absl::StatusCode::kInternal, + HasSubstr(absl::StrCat("Index not found for ", port, + " in platform.json")))); +} + +TEST_F(GNMIThinkitInterfaceUtilityTest, + TestConstructSupportedBreakoutModeSuccess) { + std::string num_breakouts = " 1"; + std::string breakout_speed = "400G "; + const std::string expected_breakout_mode = "1x400G"; + EXPECT_THAT( + pins_test::ConstructSupportedBreakoutMode(num_breakouts, breakout_speed), + expected_breakout_mode); +} } // namespace pins_test From dbe502a2160b51b4bc797d156fbf951ccfa7f040 Mon Sep 17 00:00:00 2001 From: kishanps Date: Mon, 10 Jan 2022 11:52:44 -0800 Subject: [PATCH 6/7] [Thinkit] gNMI port interface tests. --- tests/BUILD.bazel | 32 +++- tests/thinkit_gnmi_interface_tests.cc | 58 +++++-- tests/thinkit_gnmi_interface_util.cc | 169 ++++++++++++++++--- tests/thinkit_gnmi_interface_util.h | 6 + tests/thinkit_gnmi_interface_util_tests.cc | 182 +++++++++++++++------ 5 files changed, 359 insertions(+), 88 deletions(-) diff --git a/tests/BUILD.bazel b/tests/BUILD.bazel index c8539cbc..5e111900 100644 --- a/tests/BUILD.bazel +++ b/tests/BUILD.bazel @@ -54,24 +54,49 @@ cc_library( ], ) +cc_library( + name = "thinkit_gnmi_interface_util", + testonly = 1, + srcs = ["thinkit_gnmi_interface_util.cc"], + hdrs = ["thinkit_gnmi_interface_util.h"], + deps = [ + ":thinkit_gnmi_subscribe_tests", + "//gutil:status", + "//gutil:testing", + "//lib/gnmi:gnmi_helper", + "//p4_pdpi:pd", + "//sai_p4/instantiations/google:sai_pd_cc_proto", + "//thinkit:ssh_client", + "//thinkit:switch", + "@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/status", + "@com_google_absl//absl/status:statusor", + "@com_google_absl//absl/strings", + "@com_google_googletest//:gtest", + ], +) + cc_library( name = "thinkit_gnmi_tests", testonly = 1, srcs = [ "thinkit_gnmi_interface_tests.cc", - "thinkit_gnmi_interface_util.cc", ], hdrs = [ "thinkit_gnmi_interface_tests.h", - "thinkit_gnmi_interface_util.h", "thinkit_util.h", ], deps = [ - "//gutil:status", + ":thinkit_gnmi_interface_util", + "//gutil:collections", "//gutil:status_matchers", "//gutil:testing", "//lib/gnmi:gnmi_helper", "//p4_pdpi:pd", + "//sai_p4/instantiations/google:instantiations", + "//sai_p4/instantiations/google:sai_p4info_cc", "//sai_p4/instantiations/google:sai_pd_cc_proto", "//thinkit:ssh_client", "//thinkit:switch", @@ -121,6 +146,7 @@ cc_test( name = "thinkit_gnmi_interface_util_tests", srcs = ["thinkit_gnmi_interface_util_tests.cc"], deps = [ + ":thinkit_gnmi_interface_util", ":thinkit_gnmi_tests", "//gutil:proto_matchers", "//gutil:status_matchers", diff --git a/tests/thinkit_gnmi_interface_tests.cc b/tests/thinkit_gnmi_interface_tests.cc index 3dd240e6..0bf54e7d 100644 --- a/tests/thinkit_gnmi_interface_tests.cc +++ b/tests/thinkit_gnmi_interface_tests.cc @@ -34,6 +34,7 @@ #include "glog/logging.h" #include "gmock/gmock.h" #include "gtest/gtest.h" +#include "gutil/collections.h" #include "gutil/status.h" #include "gutil/status_matchers.h" #include "gutil/testing.h" @@ -41,6 +42,8 @@ #include "lib/gnmi/gnmi_helper.h" #include "p4_pdpi/pd.h" #include "proto/gnmi/gnmi.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 "tests/thinkit_gnmi_interface_util.h" #include "tests/thinkit_util.h" @@ -362,9 +365,15 @@ void BreakoutDuringPortInUse(thinkit::Switch& sut, ASSERT_NE(in_use_port, port_info.port_name); } + // Get the p4rt port index of the port on which to install router interface. + ASSERT_OK_AND_ASSIGN(auto port_id_by_interface, + GetAllInterfaceNameToPortId(*sut_gnmi_stub)); + ASSERT_OK_AND_ASSIGN(std::string in_use_port_index_value, + gutil::FindOrStatus(port_id_by_interface, in_use_port)); + int in_use_port_index; + ASSERT_TRUE(absl::SimpleAtoi(in_use_port_index_value, &in_use_port_index)); + // Configure router interface on selected port to install port dependency. - ASSERT_OK_AND_ASSIGN(auto in_use_port_index, - GetPortIndex(platform_json_contents, in_use_port)); std::unique_ptr sut_p4_session; ASSERT_OK_AND_ASSIGN(sut_p4_session, pdpi::P4RuntimeSession::Create(sut)); const sai::TableEntry pd_entry = @@ -378,8 +387,21 @@ void BreakoutDuringPortInUse(thinkit::Switch& sut, } )pb", in_use_port_index)); + ASSERT_OK(pdpi::SetMetadataAndSetForwardingPipelineConfig( + sut_p4_session.get(), + p4::v1::SetForwardingPipelineConfigRequest::RECONCILE_AND_COMMIT, + sai::GetP4Info(sai::Instantiation::kMiddleblock))) + << "SetForwardingPipelineConfig: Failed to push P4Info: "; + ASSERT_OK(pdpi::ClearTableEntries(sut_p4_session.get())); + + p4::config::v1::P4Info p4info = + sai::GetP4Info(sai::Instantiation::kMiddleblock); + ASSERT_OK_AND_ASSIGN(auto ir_p4info, pdpi::CreateIrP4Info(p4info)); ASSERT_OK_AND_ASSIGN(const p4::v1::TableEntry pi_entry, pdpi::PartialPdTableEntryToPiTableEntry(pdpi::IrP4Info(), pd_entry)); + + LOG(INFO) << "Installing router interface on port " << in_use_port + << " on SUT"; ASSERT_OK(pdpi::InstallPiTableEntry(sut_p4_session.get(), pi_entry)); // Get breakout config for the new breakout mode. @@ -391,10 +413,16 @@ void BreakoutDuringPortInUse(thinkit::Switch& sut, ASSERT_OK(GetBreakoutModeConfigFromString(req, port_index, port_info.supported_breakout_mode)); - // Apply breakout config on port. Expect the set operation to fail since the - // port/its child is in use. + // Apply breakout config on port. Expect the set operation to fail + // since the port/its child is in use. + LOG(INFO) << "Configuring breakout mode " << port_info.supported_breakout_mode + << " on port " << port_info.port_name << " on DUT"; auto status = sut_gnmi_stub->Set(&context, req, &resp); ASSERT_NE(status.ok(), true); + EXPECT_THAT(status.error_message(), + HasSubstr(absl::StrCat( + "SET failed: YangToDb_port_breakout_subtree_xfmr: port ", + port_info.port_name, " is in use"))); // Get expected port information for new breakout mode. ASSERT_OK_AND_ASSIGN( @@ -404,14 +432,20 @@ void BreakoutDuringPortInUse(thinkit::Switch& sut, auto non_existing_port_list = GetNonExistingPortsAfterBreakout( orig_breakout_info, new_breakout_info, false); // Verify breakout related state paths. - ASSERT_OK(ValidateBreakoutState(sut_gnmi_stub, new_breakout_info, + ASSERT_OK(ValidateBreakoutState(sut_gnmi_stub, orig_breakout_info, non_existing_port_list)); // Delete the created router interface on the port using P4 interface. + LOG(INFO) << "Deleting router interface on SUT"; ASSERT_OK(pdpi::ClearTableEntries(sut_p4_session.get())); - // Wait for port dependency to be deleted and breakout config to go through. - absl::SleepFor(absl::Seconds(10)); + // Retry port breakout. + grpc::ClientContext context2; + status = sut_gnmi_stub->Set(&context2, req, &resp); + ASSERT_EQ(status.ok(), true); + + // Wait for breakout config to go through. + absl::SleepFor(absl::Seconds(30)); // Verify that the config is successfully applied now. non_existing_port_list = GetNonExistingPortsAfterBreakout( @@ -422,13 +456,17 @@ void BreakoutDuringPortInUse(thinkit::Switch& sut, // Restore original port breakout config on port under test. ASSERT_OK(GetBreakoutModeConfigFromString(req, port_index, port_info.curr_breakout_mode)); - ASSERT_OK(sut_gnmi_stub->Set(&context, req, &resp)); - absl::SleepFor(absl::Seconds(5)); + LOG(INFO) << "Restoring original breakout mode " + << port_info.curr_breakout_mode << " on port " + << port_info.port_name << " on DUT"; + grpc::ClientContext context3; + ASSERT_OK(sut_gnmi_stub->Set(&context3, req, &resp)); + absl::SleepFor(absl::Seconds(60)); // Verify that the config is successfully applied. non_existing_port_list = GetNonExistingPortsAfterBreakout( orig_breakout_info, new_breakout_info, false); - ASSERT_OK(ValidateBreakoutState(sut_gnmi_stub, new_breakout_info, + ASSERT_OK(ValidateBreakoutState(sut_gnmi_stub, orig_breakout_info, non_existing_port_list)); } diff --git a/tests/thinkit_gnmi_interface_util.cc b/tests/thinkit_gnmi_interface_util.cc index 3c8e6e64..3e28d7c7 100644 --- a/tests/thinkit_gnmi_interface_util.cc +++ b/tests/thinkit_gnmi_interface_util.cc @@ -123,6 +123,27 @@ absl::StatusOr> GetSupportedBreakoutModesForPort( return modes; } +absl::StatusOr BreakoutResultsInSpeedChangeOnly( + const std::string& port, const std::string& curr_breakout_mode, + const std::string& new_breakout_mode) { + // Get list of interfaces for current and new breakout modes. + ASSIGN_OR_RETURN(auto curr_port_info, GetExpectedPortInfoForBreakoutMode( + port, curr_breakout_mode)); + ASSIGN_OR_RETURN(auto new_port_info, + GetExpectedPortInfoForBreakoutMode(port, new_breakout_mode)); + for (auto& key : curr_port_info) { + if (!new_port_info.count(key.first)) { + return false; + } + } + for (auto& key : new_port_info) { + if (!curr_port_info.count(key.first)) { + return false; + } + } + return true; +} + absl::StatusOr GetRandomPortWithSupportedBreakoutModes( gnmi::gNMI::StubInterface& sut_gnmi_stub, const std::string& platform_json_contents, @@ -134,6 +155,12 @@ absl::StatusOr GetRandomPortWithSupportedBreakoutModes( /*timeout=*/absl::Seconds(60)), _ << "Failed to get oper-status map for ports on switch"); + // Consider only ports that have p4rt ID modelled as this ID is required to + // configure P4RT router interface on the port. + ASSIGN_OR_RETURN(auto port_id_by_interface, + GetAllInterfaceNameToPortId(sut_gnmi_stub), + _ << "Failed to get interface name to p4rt id map"); + // Consider only operationally up front panel parent ports. std::vector up_parent_port_list; for (auto& intf : interface_to_oper_status_map) { @@ -172,6 +199,12 @@ absl::StatusOr GetRandomPortWithSupportedBreakoutModes( auto port = up_parent_port_list[index]; port_info.port_name = port; + // Check if port has a p4rt id value in the state path. + if (!port_id_by_interface.count(port_info.port_name)) { + up_parent_port_list.erase(up_parent_port_list.begin() + index); + continue; + } + // Get the port entry from platform.json interfaces info. const auto interface_json = interfaces_json->find(port); if (interface_json == interfaces_json->end()) { @@ -199,9 +232,16 @@ absl::StatusOr GetRandomPortWithSupportedBreakoutModes( // Get a supported breakout mode other than current breakout mode. for (const auto& supported_breakout_mode : supported_breakout_modes) { if (supported_breakout_mode != port_info.curr_breakout_mode) { - port_info.supported_breakout_mode = supported_breakout_mode; - StripSymbolFromString(port_info.supported_breakout_mode, '\"'); - break; + // Check if new breakout mode would result in a speed change only. + ASSIGN_OR_RETURN(auto speed_change_only, + BreakoutResultsInSpeedChangeOnly( + port_info.port_name, port_info.curr_breakout_mode, + supported_breakout_mode)); + if (!speed_change_only) { + port_info.supported_breakout_mode = supported_breakout_mode; + StripSymbolFromString(port_info.supported_breakout_mode, '\"'); + break; + } } } if (!port_info.supported_breakout_mode.empty()) { @@ -321,6 +361,54 @@ GetBreakoutStateInfoForPort(gnmi::gNMI::StubInterface* sut_gnmi_stub, return port_info; } +absl::StatusOr GenerateComponentBreakoutConfig( + absl::string_view breakout_speed, int index, int num_breakouts, + absl::string_view num_physical_channels) { + auto component_config = absl::Substitute( + R"pb({ + "config": { + "breakout-speed": "openconfig-if-ethernet:SPEED_$0B", + "index": $1, + "num-breakouts": $2, + "num-physical-channels": $3 + }, + "index": $1 + })pb", + breakout_speed, index, num_breakouts, num_physical_channels); + return component_config; +} + +absl::StatusOr GenerateInterfaceBreakoutConfig( + absl::string_view port, const int id, absl::string_view breakout_speed) { + auto interface_config = absl::Substitute( + R"pb({ + "config": { + "enabled": true, + "loopback-mode": false, + "mtu": 9216, + "name": "$0", + "type": "iana-if-type:ethernetCsmacd" + }, + "name": "$0", + "openconfig-if-ethernet:ethernet": { + "config": { "port-speed": "openconfig-if-ethernet:SPEED_$1B" } + }, + "subinterfaces": { + "subinterface": + [ { + "config": { "index": 0 }, + "index": 0, + "openconfig-if-ip:ipv6": { + "unnumbered": { "config": { "enabled": true } } + } + }] + } + } + )pb", + port, breakout_speed); + return interface_config; +} + absl::Status GetBreakoutModeConfigFromString( gnmi::SetRequest& req, const absl::string_view port_index, const absl::string_view breakout_mode) { @@ -332,8 +420,18 @@ absl::Status GetBreakoutModeConfigFromString( // 1x200G} std::vector modes = absl::StrSplit(breakout_mode, '+'); std::vector group_configs; + std::vector interface_configs; auto max_channels_in_group = kMaxPortLanes / modes.size(); auto index = 0; + + // Get current port number. + int curr_port_number; + if (!absl::SimpleAtoi(port_index, &curr_port_number)) { + return gutil::InternalErrorBuilder().LogError() + << "Failed to convert string (" << port_index << ") to integer"; + } + curr_port_number = (curr_port_number - 1) * kMaxPortLanes; + for (auto& mode : modes) { auto num_breakouts_str = mode.substr(0, mode.find('x')); int num_breakouts; @@ -344,36 +442,57 @@ absl::Status GetBreakoutModeConfigFromString( } auto breakout_speed = mode.substr(mode.find('x') + 1); auto num_physical_channels = max_channels_in_group / num_breakouts; - auto group_config = absl::Substitute( - R"pb({ - "config": { - "breakout-speed": "openconfig-if-ethernet:SPEED_$0B", - "index": $1, - "num-breakouts": $2, - "num-physical-channels": $3 - }, - "index": $1 - })pb", - breakout_speed, index, num_breakouts, - std::to_string(num_physical_channels)); + ASSIGN_OR_RETURN( + auto group_config, + GenerateComponentBreakoutConfig(breakout_speed, index, num_breakouts, + std::to_string(num_physical_channels))); group_configs.push_back(group_config); + + // Get the interface config for all ports corresponding to current breakout + // group. + for (int i = 0; i < num_breakouts; i++) { + auto port = absl::StrCat(kEthernet, std::to_string(curr_port_number)); + ASSIGN_OR_RETURN(auto interfaceConfig, + GenerateInterfaceBreakoutConfig(port, curr_port_number, + breakout_speed)); + interface_configs.push_back(interfaceConfig); + int offset = max_channels_in_group / num_breakouts; + curr_port_number += offset; + } index += 1; } - std::string main; + std::string componentConfig, interfaceConfig; for (auto& group_config : group_configs) { - main += absl::StrCat(group_config, ","); + componentConfig += absl::StrCat(group_config, ","); } - // Pop the last comma from the group config array. - main.pop_back(); - auto kBreakoutConfig = absl::Substitute( - R"pb({ "openconfig-platform-port:groups": { "group": [ $0 ] } })pb", - main); + for (auto& interface_config : interface_configs) { + interfaceConfig += absl::StrCat(interface_config, ","); + } + // Pop the last comma from the component and interface config array. + componentConfig.pop_back(); + interfaceConfig.pop_back(); + auto kBreakoutConfig = absl::Substitute( + R"pb({ + "openconfig-interfaces:interfaces": { "interface": [ $0 ] }, + "openconfig-platform:components": { + "component": + [ { + "name": "$1", + "config": { "name": "$1" }, + "port": { + "config": { "port-id": $2 }, + "breakout-mode": { "groups": { "group": [ $3 ] } } + } + }] + } + })pb", + interfaceConfig, absl::StrCat("1/", port_index), port_index, + componentConfig); // Build GNMI config set request for given port breakout mode. - ASSIGN_OR_RETURN(req, - BuildGnmiSetRequest(kBreakoutPath, GnmiSetType::kReplace, - kBreakoutConfig)); + ASSIGN_OR_RETURN( + req, BuildGnmiSetRequest("", GnmiSetType::kReplace, kBreakoutConfig)); return absl::OkStatus(); } diff --git a/tests/thinkit_gnmi_interface_util.h b/tests/thinkit_gnmi_interface_util.h index 729b5867..44fee3c6 100644 --- a/tests/thinkit_gnmi_interface_util.h +++ b/tests/thinkit_gnmi_interface_util.h @@ -58,6 +58,12 @@ absl::StatusOr> GetSupportedBreakoutModesForPort( const std::string& interface_info, const std::string& port, const BreakoutType breakout_type = BreakoutType::kAny); +// BreakoutResultsInSpeedChangeOnly returns whether changing from current to new +// breakout mode would result in a speed change only. +absl::StatusOr BreakoutResultsInSpeedChangeOnly( + const std::string& port, const std::string& curr_breakout_Mode, + const std::string& new_breakout_mode); + // GetRandomPortWithSupportedBreakoutModes attempts to get a random port from // list of front panel ports that supports at least one more breakout mode other // than the currently configured breakout mode. diff --git a/tests/thinkit_gnmi_interface_util_tests.cc b/tests/thinkit_gnmi_interface_util_tests.cc index cab7b71a..42fbec18 100644 --- a/tests/thinkit_gnmi_interface_util_tests.cc +++ b/tests/thinkit_gnmi_interface_util_tests.cc @@ -7,6 +7,7 @@ #include "absl/status/status.h" #include "absl/strings/str_cat.h" #include "absl/strings/string_view.h" +#include "absl/strings/substitute.h" #include "gmock/gmock.h" #include "google/protobuf/text_format.h" #include "gtest/gtest.h" @@ -139,14 +140,14 @@ TEST_F(GNMIThinkitInterfaceUtilityTest, update { path { elem { name: "interfaces" } } val { - json_ietf_val: "{\"openconfig-interfaces:interfaces\":{\"interface\":[{\"name\":\"Ethernet0\",\"state\":{\"oper-status\":\"UP\"}},{\"name\":\"Ethernet8\",\"state\":{\"oper-status\":\"UP\"}}]}}" + json_ietf_val: "{\"openconfig-interfaces:interfaces\":{\"interface\":[{\"name\":\"Ethernet0\",\"state\":{\"oper-status\":\"UP\",\"openconfig-p4rt:id\": 1}},{\"name\":\"Ethernet8\",\"state\":{\"oper-status\":\"UP\",\"openconfig-p4rt:id\": 2}}]}}" } } } )pb", &resp)); EXPECT_CALL(*mock_gnmi_stub_ptr, Get(_, EqualsProto(req), _)) - .WillOnce(DoAll(SetArgPointee<2>(resp), Return(grpc::Status::OK))); + .WillRepeatedly(DoAll(SetArgPointee<2>(resp), Return(grpc::Status::OK))); auto random_port_info = pins_test::GetRandomPortWithSupportedBreakoutModes( *mock_gnmi_stub_ptr, platform_json_contents); ASSERT_OK(random_port_info.status()); @@ -202,14 +203,14 @@ TEST_F(GNMIThinkitInterfaceUtilityTest, update { path { elem { name: "interfaces" } } val { - json_ietf_val: "{\"openconfig-interfaces:interfaces\":{\"interface\":[{\"name\":\"Ethernet0\",\"state\":{\"oper-status\":\"UP\"}},{\"name\":\"Ethernet8\",\"state\":{\"oper-status\":\"UP\"}},{\"name\":\"Ethernet16\",\"state\":{\"oper-status\":\"UP\"}}]}}" + json_ietf_val: "{\"openconfig-interfaces:interfaces\":{\"interface\":[{\"name\":\"Ethernet0\",\"state\":{\"oper-status\":\"UP\",\"openconfig-p4rt:id\": 1}},{\"name\":\"Ethernet8\",\"state\":{\"oper-status\":\"UP\"}},{\"name\":\"Ethernet16\",\"state\":{\"oper-status\":\"UP\",\"openconfig-p4rt:id\": 2}}]}}" } } } )pb", &resp)); EXPECT_CALL(*mock_gnmi_stub_ptr, Get(_, EqualsProto(req), _)) - .WillOnce(DoAll(SetArgPointee<2>(resp), Return(grpc::Status::OK))); + .WillRepeatedly(DoAll(SetArgPointee<2>(resp), Return(grpc::Status::OK))); auto random_port_info = pins_test::GetRandomPortWithSupportedBreakoutModes( *mock_gnmi_stub_ptr, platform_json_contents, pins_test::BreakoutType::kChannelized); @@ -242,6 +243,39 @@ TEST_F(GNMIThinkitInterfaceUtilityTest, HasSubstr("Failed to get oper-status map for ports on switch"))); } +TEST_F(GNMIThinkitInterfaceUtilityTest, + TestGetRandomPortWithSupportedBreakoutModesIntfNameToPortIdGetFailure) { + auto mock_gnmi_stub_ptr = absl::make_unique(); + gnmi::GetRequest req; + ASSERT_TRUE(google::protobuf::TextFormat::ParseFromString( + R"pb(prefix { origin: "openconfig" } + path { elem { name: "interfaces" } } + type: STATE)pb", + &req)); + gnmi::GetResponse resp; + ASSERT_TRUE(google::protobuf::TextFormat::ParseFromString( + R"pb(notification { + timestamp: 1631864194292383538 + prefix { origin: "openconfig" } + update { + path { elem { name: "interfaces" } } + val { + json_ietf_val: "{\"openconfig-interfaces:interfaces\":{\"interface\":[{\"name\":\"Ethernet0\",\"state\":{\"oper-status\":\"UP\",\"openconfig-p4rt:id\": 1}},{\"name\":\"Ethernet8\",\"state\":{\"oper-status\":\"UP\"}},{\"name\":\"Ethernet16\",\"state\":{\"oper-status\":\"UP\",\"openconfig-p4rt:id\": 2}}]}}" + } + } + } + )pb", + &resp)); + EXPECT_CALL(*mock_gnmi_stub_ptr, Get(_, EqualsProto(req), _)) + .WillOnce(DoAll(SetArgPointee<2>(resp), Return(grpc::Status::OK))) + .WillOnce(Return(grpc::Status(grpc::StatusCode::DEADLINE_EXCEEDED, ""))); + EXPECT_THAT( + pins_test::GetRandomPortWithSupportedBreakoutModes(*mock_gnmi_stub_ptr, + ""), + StatusIs(absl::StatusCode::kDeadlineExceeded, + HasSubstr("Failed to get interface name to p4rt id map"))); +} + TEST_F(GNMIThinkitInterfaceUtilityTest, TestGetRandomPortWithSupportedBreakoutModesIntConversionFailure) { auto mock_gnmi_stub_ptr = absl::make_unique(); @@ -271,7 +305,7 @@ TEST_F(GNMIThinkitInterfaceUtilityTest, )pb", &resp)); EXPECT_CALL(*mock_gnmi_stub_ptr, Get(_, EqualsProto(req), _)) - .WillOnce(DoAll(SetArgPointee<2>(resp), Return(grpc::Status::OK))); + .WillRepeatedly(DoAll(SetArgPointee<2>(resp), Return(grpc::Status::OK))); EXPECT_THAT(pins_test::GetRandomPortWithSupportedBreakoutModes( *mock_gnmi_stub_ptr, ""), StatusIs(absl::StatusCode::kInternal, @@ -310,14 +344,14 @@ TEST_F(GNMIThinkitInterfaceUtilityTest, update { path { elem { name: "interfaces" } } val { - json_ietf_val: "{\"openconfig-interfaces:interfaces\":{\"interface\":[{\"name\":\"Ethernet0\",\"state\":{\"oper-status\":\"DOWN\"}}, {\"name\":\"Ethernet8\",\"state\":{\"oper-status\":\"DOWN\"}}]}}" + json_ietf_val: "{\"openconfig-interfaces:interfaces\":{\"interface\":[{\"name\":\"Ethernet0\",\"state\":{\"oper-status\":\"DOWN\",\"openconfig-p4rt:id\": 1}}, {\"name\":\"Ethernet8\",\"state\":{\"oper-status\":\"DOWN\",\"openconfig-p4rt:id\": 2}}]}}" } } } )pb", &resp)); EXPECT_CALL(*mock_gnmi_stub_ptr, Get(_, EqualsProto(req), _)) - .WillOnce(DoAll(SetArgPointee<2>(resp), Return(grpc::Status::OK))); + .WillRepeatedly(DoAll(SetArgPointee<2>(resp), Return(grpc::Status::OK))); EXPECT_THAT( pins_test::GetRandomPortWithSupportedBreakoutModes( *mock_gnmi_stub_ptr, platform_json_contents), @@ -325,6 +359,54 @@ TEST_F(GNMIThinkitInterfaceUtilityTest, HasSubstr("No operationally up parent ports found on switch"))); } +TEST_F(GNMIThinkitInterfaceUtilityTest, + TestGetRandomPortWithSupportedBreakoutModesNoPortsWithP4rtIDFailure) { + auto mock_gnmi_stub_ptr = absl::make_unique(); + const std::string platform_json_contents = + R"pb({ + "interfaces": { + "Ethernet0": { + "default_brkout_mode": "1x400G", + "breakout_modes": "1x400G, 2x200G[100G,40G]" + }, + "Ethernet8": { + "default_brkout_mode": "2x200G", + "breakout_modes": "1x400G, 2x200G[100G,40G]" + }, + "Ethernet12": { "breakout_modes": "1x200G[100G,40G]" } + } + } + )pb"; + gnmi::GetRequest req; + ASSERT_TRUE(google::protobuf::TextFormat::ParseFromString( + R"pb(prefix { origin: "openconfig" } + path { elem { name: "interfaces" } } + type: STATE)pb", + &req)); + gnmi::GetResponse resp; + ASSERT_TRUE(google::protobuf::TextFormat::ParseFromString( + R"pb(notification { + timestamp: 1631864194292383538 + prefix { origin: "openconfig" } + update { + path { elem { name: "interfaces" } } + val { + json_ietf_val: "{\"openconfig-interfaces:interfaces\":{\"interface\":[{\"name\":\"Ethernet0\",\"state\":{\"oper-status\":\"UP\"}}, {\"name\":\"Ethernet8\",\"state\":{\"oper-status\":\"UP\"}}]}}" + } + } + } + )pb", + &resp)); + EXPECT_CALL(*mock_gnmi_stub_ptr, Get(_, EqualsProto(req), _)) + .WillRepeatedly(DoAll(SetArgPointee<2>(resp), Return(grpc::Status::OK))); + EXPECT_THAT( + pins_test::GetRandomPortWithSupportedBreakoutModes( + *mock_gnmi_stub_ptr, platform_json_contents), + StatusIs(absl::StatusCode::kInternal, + HasSubstr( + "No random interface with supported breakout modes found"))); +} + TEST_F(GNMIThinkitInterfaceUtilityTest, TestGetRandomPortWithSupportedBreakoutModesInterfacesNotFoundInFailure) { auto mock_gnmi_stub_ptr = absl::make_unique(); @@ -352,7 +434,7 @@ TEST_F(GNMIThinkitInterfaceUtilityTest, )pb", &resp)); EXPECT_CALL(*mock_gnmi_stub_ptr, Get(_, EqualsProto(req), _)) - .WillOnce(DoAll(SetArgPointee<2>(resp), Return(grpc::Status::OK))); + .WillRepeatedly(DoAll(SetArgPointee<2>(resp), Return(grpc::Status::OK))); EXPECT_THAT(pins_test::GetRandomPortWithSupportedBreakoutModes( *mock_gnmi_stub_ptr, platform_json_contents), StatusIs(absl::StatusCode::kInternal, @@ -379,14 +461,14 @@ TEST_F(GNMIThinkitInterfaceUtilityTest, update { path { elem { name: "interfaces" } } val { - json_ietf_val: "{\"openconfig-interfaces:interfaces\":{\"interface\":[{\"name\":\"Ethernet0\",\"state\":{\"oper-status\":\"UP\"}}]}}" + json_ietf_val: "{\"openconfig-interfaces:interfaces\":{\"interface\":[{\"name\":\"Ethernet0\",\"state\":{\"oper-status\":\"UP\",\"openconfig-p4rt:id\": 1}}]}}" } } } )pb", &resp)); EXPECT_CALL(*mock_gnmi_stub_ptr, Get(_, EqualsProto(req), _)) - .WillOnce(DoAll(SetArgPointee<2>(resp), Return(grpc::Status::OK))); + .WillRepeatedly(DoAll(SetArgPointee<2>(resp), Return(grpc::Status::OK))); EXPECT_THAT( pins_test::GetRandomPortWithSupportedBreakoutModes( *mock_gnmi_stub_ptr, platform_json_contents), @@ -418,14 +500,14 @@ TEST_F(GNMIThinkitInterfaceUtilityTest, update { path { elem { name: "interfaces" } } val { - json_ietf_val: "{\"openconfig-interfaces:interfaces\":{\"interface\":[{\"name\":\"Ethernet0\",\"state\":{\"oper-status\":\"UP\"}}]}}" + json_ietf_val: "{\"openconfig-interfaces:interfaces\":{\"interface\":[{\"name\":\"Ethernet0\",\"state\":{\"oper-status\":\"UP\",\"openconfig-p4rt:id\": 1}}]}}" } } } )pb", &resp)); EXPECT_CALL(*mock_gnmi_stub_ptr, Get(_, EqualsProto(req), _)) - .WillOnce(DoAll(SetArgPointee<2>(resp), Return(grpc::Status::OK))); + .WillRepeatedly(DoAll(SetArgPointee<2>(resp), Return(grpc::Status::OK))); EXPECT_THAT(pins_test::GetRandomPortWithSupportedBreakoutModes( *mock_gnmi_stub_ptr, platform_json_contents), StatusIs(absl::StatusCode::kInternal, @@ -435,7 +517,7 @@ TEST_F(GNMIThinkitInterfaceUtilityTest, TEST_F( GNMIThinkitInterfaceUtilityTest, - TestGetRandomPortWithSupportedBreakoutModesSupportedModesNotFoundnFailure) { + TestGetRandomPortWithSupportedBreakoutModesSupportedModesNotFoundFailure) { auto mock_gnmi_stub_ptr = absl::make_unique(); const std::string platform_json_contents = R"pb({ @@ -456,14 +538,14 @@ TEST_F( update { path { elem { name: "interfaces" } } val { - json_ietf_val: "{\"openconfig-interfaces:interfaces\":{\"interface\":[{\"name\":\"Ethernet0\",\"state\":{\"oper-status\":\"UP\"}}]}}" + json_ietf_val: "{\"openconfig-interfaces:interfaces\":{\"interface\":[{\"name\":\"Ethernet0\",\"state\":{\"oper-status\":\"UP\",\"openconfig-p4rt:id\": 1}}]}}" } } } )pb", &resp)); EXPECT_CALL(*mock_gnmi_stub_ptr, Get(_, EqualsProto(req), _)) - .WillOnce(DoAll(SetArgPointee<2>(resp), Return(grpc::Status::OK))); + .WillRepeatedly(DoAll(SetArgPointee<2>(resp), Return(grpc::Status::OK))); EXPECT_THAT( pins_test::GetRandomPortWithSupportedBreakoutModes( *mock_gnmi_stub_ptr, platform_json_contents), @@ -504,14 +586,14 @@ TEST_F( update { path { elem { name: "interfaces" } } val { - json_ietf_val: "{\"openconfig-interfaces:interfaces\":{\"interface\":[{\"name\":\"Ethernet0\",\"state\":{\"oper-status\":\"UP\"}},{\"name\":\"Ethernet8\",\"state\":{\"oper-status\":\"UP\"}}]}}" + json_ietf_val: "{\"openconfig-interfaces:interfaces\":{\"interface\":[{\"name\":\"Ethernet0\",\"state\":{\"oper-status\":\"UP\",\"openconfig-p4rt:id\": 1}},{\"name\":\"Ethernet8\",\"state\":{\"oper-status\":\"UP\",\"openconfig-p4rt:id\": 2}}]}}" } } } )pb", &resp)); EXPECT_CALL(*mock_gnmi_stub_ptr, Get(_, EqualsProto(req), _)) - .WillOnce(DoAll(SetArgPointee<2>(resp), Return(grpc::Status::OK))); + .WillRepeatedly(DoAll(SetArgPointee<2>(resp), Return(grpc::Status::OK))); EXPECT_THAT( pins_test::GetRandomPortWithSupportedBreakoutModes( *mock_gnmi_stub_ptr, platform_json_contents, @@ -841,23 +923,15 @@ TEST_F(GNMIThinkitInterfaceUtilityTest, const std::string port_index = "1"; const std::string breakout_mode = "1x400G"; gnmi::SetRequest req, expected_breakout_config; - const std::string expected_breakout_config_str = R"pb( + const std::string expected_breakout_config_str = + R"pb( prefix { origin: "openconfig" } replace { - path { - elem { name: "components" } - elem { - name: "component" - key { key: "name" value: "1/1" } - } - elem { name: "port" } - elem { name: "breakout-mode" } - } + path {} val { - json_ietf_val: "{ \"openconfig-platform-port: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 } ] } }" + json_ietf_val: "{\n \"openconfig-interfaces:interfaces\": { \"interface\": [ {\n \"config\": {\n \"enabled\": true,\n \"loopback-mode\": false,\n \"mtu\": 9216,\n \"name\": \"Ethernet0\",\n \"type\": \"iana-if-type:ethernetCsmacd\"\n },\n \"name\": \"Ethernet0\",\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"; + })pb"; ASSERT_TRUE(google::protobuf::TextFormat::ParseFromString( expected_breakout_config_str, &expected_breakout_config)); ASSERT_OK(pins_test::GetBreakoutModeConfigFromString(req, port_index, @@ -873,17 +947,9 @@ TEST_F(GNMIThinkitInterfaceUtilityTest, const std::string expected_breakout_config_str = R"pb( prefix { origin: "openconfig" } replace { - path { - elem { name: "components" } - elem { - name: "component" - key { key: "name" value: "1/1" } - } - elem { name: "port" } - elem { name: "breakout-mode" } - } + path {} val { - json_ietf_val: "{ \"openconfig-platform-port: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 } ] } }" + json_ietf_val: "{\n \"openconfig-interfaces:interfaces\": { \"interface\": [ {\n \"config\": {\n \"enabled\": true,\n \"loopback-mode\": false,\n \"mtu\": 9216,\n \"name\": \"Ethernet0\",\n \"type\": \"iana-if-type:ethernetCsmacd\"\n },\n \"name\": \"Ethernet0\",\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\": \"Ethernet4\",\n \"type\": \"iana-if-type:ethernetCsmacd\"\n },\n \"name\": \"Ethernet4\",\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"; @@ -902,17 +968,9 @@ TEST_F(GNMIThinkitInterfaceUtilityTest, const std::string expected_breakout_config_str = R"pb( prefix { origin: "openconfig" } replace { - path { - elem { name: "components" } - elem { - name: "component" - key { key: "name" value: "1/1" } - } - elem { name: "port" } - elem { name: "breakout-mode" } - } + path {} val { - json_ietf_val: "{ \"openconfig-platform-port: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 } ] } }" + json_ietf_val: "{\n \"openconfig-interfaces:interfaces\": { \"interface\": [ {\n \"config\": {\n \"enabled\": true,\n \"loopback-mode\": false,\n \"mtu\": 9216,\n \"name\": \"Ethernet0\",\n \"type\": \"iana-if-type:ethernetCsmacd\"\n },\n \"name\": \"Ethernet0\",\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\": \"Ethernet4\",\n \"type\": \"iana-if-type:ethernetCsmacd\"\n },\n \"name\": \"Ethernet4\",\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\": \"Ethernet6\",\n \"type\": \"iana-if-type:ethernetCsmacd\"\n },\n \"name\": \"Ethernet6\",\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"; @@ -1415,4 +1473,28 @@ TEST_F(GNMIThinkitInterfaceUtilityTest, pins_test::ConstructSupportedBreakoutMode(num_breakouts, breakout_speed), expected_breakout_mode); } + +TEST_F(GNMIThinkitInterfaceUtilityTest, + TestBreakoutResultsInSpeedChangeOnlySuccess) { + EXPECT_THAT(pins_test::BreakoutResultsInSpeedChangeOnly("Ethernet0", "1x400G", + "1x100G"), + true); + EXPECT_THAT(pins_test::BreakoutResultsInSpeedChangeOnly( + "Ethernet0", "1x200G(4)+2x100G(4)", "1x100G(4)+2x40G(4)"), + true); + EXPECT_THAT(pins_test::BreakoutResultsInSpeedChangeOnly("Ethernet0", "1x400G", + "2x200G"), + false); + EXPECT_THAT(pins_test::BreakoutResultsInSpeedChangeOnly( + "Ethernet0", "1x200G(4)+2x40G(4)", "1x200G(4)+1x40G(4)"), + false); +} + +TEST_F(GNMIThinkitInterfaceUtilityTest, + TestBreakoutResultsInSpeedChangeOnlyInvalidPortFailure) { + EXPECT_THAT(pins_test::BreakoutResultsInSpeedChangeOnly("EthernetX", "1x400G", + "1x100G"), + StatusIs(absl::StatusCode::kInternal, + HasSubstr("Failed to convert string (X) to integer"))); +} } // namespace pins_test From 56dbdd5ace9e90bbd1c69afbdb4064ba99e0e6f9 Mon Sep 17 00:00:00 2001 From: kishanps Date: Mon, 6 Dec 2021 20:23:58 -0800 Subject: [PATCH 7/7] [Thinkit] gNMI port breakout tests, Make const parameters const or string_view & gNMI port interface tests. PiperOrigin-RevId: 424183653 --- tests/thinkit_gnmi_interface_tests.cc | 7 +- tests/thinkit_gnmi_interface_util.cc | 155 +++++--- tests/thinkit_gnmi_interface_util.h | 26 +- tests/thinkit_gnmi_interface_util_tests.cc | 398 ++++++++++++++++++++- 4 files changed, 515 insertions(+), 71 deletions(-) diff --git a/tests/thinkit_gnmi_interface_tests.cc b/tests/thinkit_gnmi_interface_tests.cc index 0bf54e7d..0053e8f7 100644 --- a/tests/thinkit_gnmi_interface_tests.cc +++ b/tests/thinkit_gnmi_interface_tests.cc @@ -410,7 +410,8 @@ void BreakoutDuringPortInUse(thinkit::Switch& sut, grpc::ClientContext context; ASSERT_OK_AND_ASSIGN(auto port_index, GetPortIndex(platform_json_contents, port_info.port_name)); - ASSERT_OK(GetBreakoutModeConfigFromString(req, port_index, + ASSERT_OK(GetBreakoutModeConfigFromString(req, sut_gnmi_stub, port_index, + port_info.port_name, port_info.supported_breakout_mode)); // Apply breakout config on port. Expect the set operation to fail @@ -454,8 +455,10 @@ void BreakoutDuringPortInUse(thinkit::Switch& sut, non_existing_port_list)); // Restore original port breakout config on port under test. - ASSERT_OK(GetBreakoutModeConfigFromString(req, port_index, + ASSERT_OK(GetBreakoutModeConfigFromString(req, sut_gnmi_stub, port_index, + port_info.port_name, port_info.curr_breakout_mode)); + LOG(INFO) << "Restoring original breakout mode " << port_info.curr_breakout_mode << " on port " << port_info.port_name << " on DUT"; diff --git a/tests/thinkit_gnmi_interface_util.cc b/tests/thinkit_gnmi_interface_util.cc index 3e28d7c7..fcb21243 100644 --- a/tests/thinkit_gnmi_interface_util.cc +++ b/tests/thinkit_gnmi_interface_util.cc @@ -15,6 +15,8 @@ #include "absl/strings/match.h" #include "absl/strings/numbers.h" #include "absl/strings/str_cat.h" +#include "absl/strings/str_join.h" +#include "absl/strings/str_replace.h" #include "absl/strings/str_split.h" #include "absl/strings/string_view.h" #include "absl/strings/substitute.h" @@ -35,15 +37,11 @@ using ::nlohmann::json; } // namespace -void StripSymbolFromString(std::string& str, const char symbol) { - str.erase(remove(str.begin(), str.end(), symbol), str.end()); -} - -std::string ConstructSupportedBreakoutMode(std::string& num_breakouts, - std::string& breakout_speed) { - StripSymbolFromString(num_breakouts, ' '); - StripSymbolFromString(breakout_speed, ' '); - return absl::StrCat(num_breakouts, "x", breakout_speed); +std::string ConstructSupportedBreakoutMode(absl::string_view num_breakouts, + absl::string_view breakout_speed) { + std::string breakout_mode = absl::StrCat(num_breakouts, "x", breakout_speed); + StripSymbolFromString(breakout_mode, ' '); + return breakout_mode; } absl::StatusOr> GetSupportedBreakoutModesForPort( @@ -105,8 +103,8 @@ absl::StatusOr> GetSupportedBreakoutModesForPort( if (breakout_type == BreakoutType::kChannelized) { for (const auto& mode : modes) { // A breakout mode is a channelized mode if it is either a mixed mode (eg. - // 1x200G+2x100G) or it results in more than one number of interfaces - // (eg. 2x200G). + // 1x200G(4)+2x100G(4)) or it results in more than one number of + // interfaces (eg. 2x200G). auto num_breakouts_str = mode.substr(0, mode.find('x')); int num_breakouts; if (!absl::SimpleAtoi(num_breakouts_str, &num_breakouts)) { @@ -266,8 +264,8 @@ GetExpectedPortInfoForBreakoutMode(const std::string& port, } // For a mixed breakout mode, get "+" separated breakout groups. - // Eg. For a mixed breakout mode of "2x100G + 1x200G"; modes = {2x100G, - // 1x200G} + // Eg. For a mixed breakout mode of "2x100G(4) + 1x200G(4)"; modes = + // {2x100G(4), 1x200G(4)} std::vector modes = absl::StrSplit(breakout_mode, '+'); // Get maximum physical channels in a breakout group which is max // lanes per physical port/number of groups in a breakout mode. @@ -298,9 +296,9 @@ GetExpectedPortInfoForBreakoutMode(const std::string& port, // For each resulting interface, construct the front panel interface name // using offset from the parent port. For a breakout mode of Ethernet0 => - // 2x100G+1x200G, the max channels per group would be 4 (8 max lanes per - // port/2 groups). Hence, breakout mode 2x100G (numBreakouts=2) would have - // an offset of 2 and 1x200G(numBreakouts=1) would have an offset of 1 + // 2x100(4)G+1x200G(4), the max channels per group would be 4 (8 max lanes + // per port/2 groups). Hence, breakout mode 2x100G (numBreakouts=2) would + // have an offset of 2 and 1x200G(numBreakouts=1) would have an offset of 1 // leading to interfaces Ethernet0, Ethernet2 for mode 2x100G and // Ethernet4 for mode 1x200G. for (int i = 0; i < num_breakouts; i++) { @@ -332,7 +330,8 @@ absl::StatusOr> GetBreakoutStateInfoForPort(gnmi::gNMI::StubInterface* sut_gnmi_stub, const std::string& port, absl::string_view breakout_mode) { - ASSIGN_OR_RETURN(auto port_info, + absl::flat_hash_map port_info; + ASSIGN_OR_RETURN(port_info, GetExpectedPortInfoForBreakoutMode(port, breakout_mode)); for (auto& p : port_info) { auto if_state_path = absl::StrCat("interfaces/interface[name=", p.first, @@ -378,8 +377,46 @@ absl::StatusOr GenerateComponentBreakoutConfig( return component_config; } +absl::StatusOr IsCopperPort(gnmi::gNMI::StubInterface* sut_gnmi_stub, + absl::string_view port) { + // Get transceiver name for the port. + auto state_path = + absl::StrCat("interfaces/interface[name=", port, "]/state/transceiver"); + auto resp_parse_str = "openconfig-platform-transceiver:transceiver"; + ASSIGN_OR_RETURN( + auto xcvrd_name, + GetGnmiStatePathInfo(sut_gnmi_stub, state_path, resp_parse_str), + _ << "Failed to get GNMI state path value for port transceiver for " + "port " + << port); + StripSymbolFromString(xcvrd_name, '\"'); + + // TODO: Replace with PMD type when supported. + // Get cable length for the port transceiver. + state_path = + absl::StrCat("components/component[name=", xcvrd_name, + "]/transceiver/state/openconfig-platform-ext:cable-length"); + resp_parse_str = "openconfig-platform-ext:cable-length"; + ASSIGN_OR_RETURN( + auto cable_length_str, + GetGnmiStatePathInfo(sut_gnmi_stub, state_path, resp_parse_str), + _ << "Failed to get GNMI state path value for cable-length for " + "port " + << port); + StripSymbolFromString(cable_length_str, '\"'); + + // Only cable lengths of copper ports are a positive value. + float cable_length; + if (!absl::SimpleAtof(cable_length_str, &cable_length)) { + return gutil::InternalErrorBuilder().LogError() + << "Failed to convert string (" << cable_length_str << ") to float"; + } + return (cable_length > 0); +} + absl::StatusOr GenerateInterfaceBreakoutConfig( - absl::string_view port, const int id, absl::string_view breakout_speed) { + absl::string_view port, const int id, absl::string_view breakout_speed, + const bool is_copper_port) { auto interface_config = absl::Substitute( R"pb({ "config": { @@ -406,18 +443,50 @@ absl::StatusOr GenerateInterfaceBreakoutConfig( } )pb", port, breakout_speed); + if (is_copper_port) { + interface_config = absl::Substitute( + R"pb({ + "config": { + "enabled": true, + "loopback-mode": false, + "mtu": 9216, + "name": "$0", + "type": "iana-if-type:ethernetCsmacd" + }, + "name": "$0", + "openconfig-if-ethernet:ethernet": { + "config": { + "port-speed": "openconfig-if-ethernet:SPEED_$1B", + "standalone-link-training": true + } + }, + "subinterfaces": { + "subinterface": + [ { + "config": { "index": 0 }, + "index": 0, + "openconfig-if-ip:ipv6": { + "unnumbered": { "config": { "enabled": true } } + } + }] + } + } + )pb", + port, breakout_speed); + } return interface_config; } absl::Status GetBreakoutModeConfigFromString( - gnmi::SetRequest& req, const absl::string_view port_index, + gnmi::SetRequest& req, gnmi::gNMI::StubInterface* sut_gnmi_stub, + const absl::string_view port_index, const absl::string_view intf_name, const absl::string_view breakout_mode) { std::string kBreakoutPath = absl::StrCat("components/component[name=1/", port_index, "]/port/breakout-mode"); // Get breakout groups corresponding to breakout mode. // For a mixed breakout mode, get "+" separated breakout groups. - // Eg. For a mixed breakout mode of "2x100G + 1x200G"; modes = {2x100G, - // 1x200G} + // Eg. For a mixed breakout mode of "2x100G(4) + 1x200G(4)"; modes = + // {2x100G(4), 1x200G(4)} std::vector modes = absl::StrSplit(breakout_mode, '+'); std::vector group_configs; std::vector interface_configs; @@ -432,7 +501,9 @@ absl::Status GetBreakoutModeConfigFromString( } curr_port_number = (curr_port_number - 1) * kMaxPortLanes; - for (auto& mode : modes) { + ASSIGN_OR_RETURN(bool is_copper_port, IsCopperPort(sut_gnmi_stub, intf_name)); + + for (const auto& mode : modes) { auto num_breakouts_str = mode.substr(0, mode.find('x')); int num_breakouts; if (!absl::SimpleAtoi(num_breakouts_str, &num_breakouts)) { @@ -440,7 +511,8 @@ absl::Status GetBreakoutModeConfigFromString( << "Failed to convert string (" << num_breakouts_str << ") to integer"; } - auto breakout_speed = mode.substr(mode.find('x') + 1); + auto xpos = mode.find('x'); + auto breakout_speed = mode.substr(xpos + 1, mode.find('(') - xpos - 1); auto num_physical_channels = max_channels_in_group / num_breakouts; ASSIGN_OR_RETURN( auto group_config, @@ -452,26 +524,19 @@ absl::Status GetBreakoutModeConfigFromString( // group. for (int i = 0; i < num_breakouts; i++) { auto port = absl::StrCat(kEthernet, std::to_string(curr_port_number)); - ASSIGN_OR_RETURN(auto interfaceConfig, - GenerateInterfaceBreakoutConfig(port, curr_port_number, - breakout_speed)); - interface_configs.push_back(interfaceConfig); + ASSIGN_OR_RETURN( + auto interface_config, + GenerateInterfaceBreakoutConfig(port, curr_port_number, + breakout_speed, is_copper_port)); + interface_configs.push_back(interface_config); int offset = max_channels_in_group / num_breakouts; curr_port_number += offset; } index += 1; } - std::string componentConfig, interfaceConfig; - for (auto& group_config : group_configs) { - componentConfig += absl::StrCat(group_config, ","); - } - for (auto& interface_config : interface_configs) { - interfaceConfig += absl::StrCat(interface_config, ","); - } - // Pop the last comma from the component and interface config array. - componentConfig.pop_back(); - interfaceConfig.pop_back(); + std::string full_component_config = absl::StrJoin(group_configs, ","); + std::string full_interface_config = absl::StrJoin(interface_configs, ","); auto kBreakoutConfig = absl::Substitute( R"pb({ @@ -488,8 +553,8 @@ absl::Status GetBreakoutModeConfigFromString( }] } })pb", - interfaceConfig, absl::StrCat("1/", port_index), port_index, - componentConfig); + full_interface_config, absl::StrCat("1/", port_index), port_index, + full_component_config); // Build GNMI config set request for given port breakout mode. ASSIGN_OR_RETURN( req, BuildGnmiSetRequest("", GnmiSetType::kReplace, kBreakoutConfig)); @@ -497,12 +562,11 @@ absl::Status GetBreakoutModeConfigFromString( } std::vector GetNonExistingPortsAfterBreakout( - absl::flat_hash_map& orig_port_info, - absl::flat_hash_map& new_port_info, + const absl::flat_hash_map& orig_port_info, + const absl::flat_hash_map& new_port_info, bool expected_success) { std::vector nonExistingPortList; - absl::flat_hash_map*orig_map = &orig_port_info, - *new_map = &new_port_info; + const auto *orig_map = &orig_port_info, *new_map = &new_port_info; if (!expected_success) { orig_map = &new_port_info; new_map = &orig_port_info; @@ -517,8 +581,9 @@ std::vector GetNonExistingPortsAfterBreakout( absl::Status ValidateBreakoutState( gnmi::gNMI::StubInterface* sut_gnmi_stub, - absl::flat_hash_map& expected_port_info, - std::vector& non_existing_ports_list) { + const absl::flat_hash_map& + expected_port_info, + const std::vector& non_existing_ports_list) { if (expected_port_info.empty()) { return gutil::InternalErrorBuilder().LogError() << "Expected port info map is empty"; diff --git a/tests/thinkit_gnmi_interface_util.h b/tests/thinkit_gnmi_interface_util.h index 44fee3c6..73d5e666 100644 --- a/tests/thinkit_gnmi_interface_util.h +++ b/tests/thinkit_gnmi_interface_util.h @@ -31,9 +31,9 @@ namespace pins_test { -const int kMaxPortLanes = 8; -const int kEthernetLen = 8; -constexpr char kEthernet[] = "Ethernet"; +inline constexpr int kMaxPortLanes = 8; +inline constexpr int kEthernetLen = 8; +inline constexpr char kEthernet[] = "Ethernet"; // // PortBreakoutInfo contains physical channels and operational status for an // // interface. @@ -94,7 +94,8 @@ GetExpectedPortInfoForBreakoutMode(const std::string& port, // breakoutSpeed1 + numBreakouts2 x breakoutSpeed2 + ... Eg: "1x400G", 2x100G + // 1x200G" absl::Status GetBreakoutModeConfigFromString( - gnmi::SetRequest& req, const absl::string_view index, + gnmi::SetRequest& req, gnmi::gNMI::StubInterface* sut_gnmi_stub, + const absl::string_view port_index, const absl::string_view intf_name, const absl::string_view breakout_mode); // GetNonExistingPortsAfterBreakout returns list of ports that were part of a @@ -107,8 +108,8 @@ absl::Status GetBreakoutModeConfigFromString( // the port, ports in original breakout config that were not in new breakout // config should no longer exist as new breakout is now applied. std::vector GetNonExistingPortsAfterBreakout( - absl::flat_hash_map& orig_port_info, - absl::flat_hash_map& new_port_info, + const absl::flat_hash_map& orig_port_info, + const absl::flat_hash_map& new_port_info, bool expected_success); // ValidateBreakoutState checks the breakout related state paths with the @@ -121,13 +122,18 @@ std::vector GetNonExistingPortsAfterBreakout( // new mode. absl::Status ValidateBreakoutState( gnmi::gNMI::StubInterface* sut_gnmi_stub, - absl::flat_hash_map& expected_port_info, - std::vector& non_existing_ports_list); + const absl::flat_hash_map& + expected_port_info, + const std::vector& non_existing_ports_list); absl::StatusOr GetPortIndex( const std::string& platform_json_contents, absl::string_view port); -std::string ConstructSupportedBreakoutMode(std::string& num_breakouts, - std::string& breakout_speed); +std::string ConstructSupportedBreakoutMode(absl::string_view num_breakouts, + absl::string_view breakout_speed); + +// IsCopperPort returns whether the port is copper or optic. +absl::StatusOr IsCopperPort(gnmi::gNMI::StubInterface* sut_gnmi_stub, + absl::string_view 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 42fbec18..0a9baa9f 100644 --- a/tests/thinkit_gnmi_interface_util_tests.cc +++ b/tests/thinkit_gnmi_interface_util_tests.cc @@ -34,6 +34,98 @@ using ::testing::Return; using ::testing::ReturnRefOfCopy; using ::testing::SetArgPointee; +constexpr char get_xcvrd_req_str[] = + R"pb(prefix { origin: "openconfig" } + path { + elem { name: "interfaces" } + elem { + name: "interface" + key { key: "name" value: "Ethernet0" } + } + elem { name: "state" } + elem { name: "transceiver" } + } + type: STATE)pb"; + +constexpr char get_xcvrd_resp_str[] = + R"pb(notification { + timestamp: 1631864194292383538 + prefix { origin: "openconfig" } + update { + path { + elem { name: "interfaces" } + elem { + name: "interface" + key { key: "name" value: "Ethernet0" } + } + elem { name: "state" } + elem { name: "transceiver" } + } + val { + json_ietf_val: "{\"openconfig-platform-transceiver:transceiver\":\"Ethernet0\"}" + } + } + } + )pb"; + +constexpr char cable_len_req_str[] = + R"pb(prefix { origin: "openconfig" } + path { + elem { name: "components" } + elem { + name: "component" + key { key: "name" value: "Ethernet0" } + } + elem { name: "transceiver" } + elem { name: "state" } + elem { name: "openconfig-platform-ext:cable-length" } + } + type: STATE)pb"; + +constexpr char cable_len_resp_copper_str[] = + R"pb(notification { + timestamp: 1631864194292383538 + prefix { origin: "openconfig" } + update { + path { + elem { name: "components" } + elem { + name: "component" + key { key: "name" value: "Ethernet0" } + } + elem { name: "transceiver" } + elem { name: "state" } + elem { name: "openconfig-platform-ext:cable-length" } + } + val { + json_ietf_val: "{\"openconfig-platform-ext:cable-length\":\"10\"}" + } + } + } + )pb"; + +constexpr char cable_len_resp_optic_str[] = + R"pb(notification { + timestamp: 1631864194292383538 + prefix { origin: "openconfig" } + update { + path { + elem { name: "components" } + elem { + name: "component" + key { key: "name" value: "Ethernet0" } + } + elem { name: "transceiver" } + elem { name: "state" } + elem { name: "cable-length" } + } + val { + json_ietf_val: "{\"openconfig-platform-ext:cable-length\":\"0\"}" + } + } + } + )pb"; + class GNMIThinkitInterfaceUtilityTest : public ::testing::Test { protected: void SetUp() override { @@ -630,7 +722,7 @@ TEST_F(GNMIThinkitInterfaceUtilityTest, TEST_F(GNMIThinkitInterfaceUtilityTest, TestGetExpectedPortInfoForBreakoutModeMixedBreakoutModeSuccess) { const std::string port = "Ethernet0"; - absl::string_view breakout_mode = "1x200G+2x100G"; + absl::string_view breakout_mode = "1x200G(4)+2x100G(4)"; auto breakout_info = pins_test::GetExpectedPortInfoForBreakoutMode(port, breakout_mode); @@ -644,7 +736,7 @@ TEST_F( GNMIThinkitInterfaceUtilityTest, TestGetExpectedPortInfoForBreakoutModeAlternatedMixedBreakoutModeSuccess) { const std::string port = "Ethernet0"; - absl::string_view breakout_mode = "2x100G+1x200G"; + absl::string_view breakout_mode = "2x100G(4)+1x200G(4)"; auto breakout_info = pins_test::GetExpectedPortInfoForBreakoutMode(port, breakout_mode); ASSERT_OK(breakout_info.status()); @@ -713,7 +805,7 @@ TEST_F(GNMIThinkitInterfaceUtilityTest, TEST_F(GNMIThinkitInterfaceUtilityTest, TestGetExpectedPortInfoForBreakoutModeInvalidBreakoutModeFailure) { const std::string port = "Ethernet0"; - absl::string_view breakout_mode = "3x200G+2x100G"; + absl::string_view breakout_mode = "3x200G(4)+2x100G(4)"; EXPECT_THAT( pins_test::GetExpectedPortInfoForBreakoutMode(port, breakout_mode), @@ -921,6 +1013,7 @@ TEST_F(GNMIThinkitInterfaceUtilityTest, TEST_F(GNMIThinkitInterfaceUtilityTest, TestGetBreakoutModeConfigFromStringUnchannelizedBreakoutModeSuccess) { const std::string port_index = "1"; + const std::string intf_name = "Ethernet0"; const std::string breakout_mode = "1x400G"; gnmi::SetRequest req, expected_breakout_config; const std::string expected_breakout_config_str = @@ -934,14 +1027,34 @@ TEST_F(GNMIThinkitInterfaceUtilityTest, })pb"; ASSERT_TRUE(google::protobuf::TextFormat::ParseFromString( expected_breakout_config_str, &expected_breakout_config)); - ASSERT_OK(pins_test::GetBreakoutModeConfigFromString(req, port_index, - breakout_mode)); + 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 cable_len_req; + ASSERT_TRUE(google::protobuf::TextFormat::ParseFromString(cable_len_req_str, + &cable_len_req)); + gnmi::GetResponse cable_len_resp; + ASSERT_TRUE(google::protobuf::TextFormat::ParseFromString( + cable_len_resp_optic_str, &cable_len_resp)); + EXPECT_CALL(*mock_gnmi_stub_ptr, Get(_, EqualsProto(cable_len_req), _)) + .WillOnce( + DoAll(SetArgPointee<2>(cable_len_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)); } TEST_F(GNMIThinkitInterfaceUtilityTest, TestGetBreakoutModeConfigFromStringChannelizedBreakoutModeSuccess) { const std::string port_index = "1"; + const std::string intf_name = "Ethernet0"; const std::string breakout_mode = "2x200G"; gnmi::SetRequest req, expected_breakout_config; const std::string expected_breakout_config_str = R"pb( @@ -955,15 +1068,35 @@ TEST_F(GNMIThinkitInterfaceUtilityTest, )pb"; ASSERT_TRUE(google::protobuf::TextFormat::ParseFromString( expected_breakout_config_str, &expected_breakout_config)); - ASSERT_OK(pins_test::GetBreakoutModeConfigFromString(req, port_index, - breakout_mode)); + 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 cable_len_req; + ASSERT_TRUE(google::protobuf::TextFormat::ParseFromString(cable_len_req_str, + &cable_len_req)); + gnmi::GetResponse cable_len_resp; + ASSERT_TRUE(google::protobuf::TextFormat::ParseFromString( + cable_len_resp_optic_str, &cable_len_resp)); + EXPECT_CALL(*mock_gnmi_stub_ptr, Get(_, EqualsProto(cable_len_req), _)) + .WillOnce( + DoAll(SetArgPointee<2>(cable_len_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)); } TEST_F(GNMIThinkitInterfaceUtilityTest, TestGetBreakoutModeConfigFromStringMixedBreakoutModeSuccess) { const std::string port_index = "1"; - const std::string breakout_mode = "1x200G+2x100G"; + const std::string intf_name = "Ethernet0"; + const std::string breakout_mode = "1x200G(4)+2x100G(4)"; gnmi::SetRequest req, expected_breakout_config; const std::string expected_breakout_config_str = R"pb( prefix { origin: "openconfig" } @@ -976,20 +1109,132 @@ TEST_F(GNMIThinkitInterfaceUtilityTest, )pb"; ASSERT_TRUE(google::protobuf::TextFormat::ParseFromString( expected_breakout_config_str, &expected_breakout_config)); - ASSERT_OK(pins_test::GetBreakoutModeConfigFromString(req, port_index, - breakout_mode)); + 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 cable_len_req; + ASSERT_TRUE(google::protobuf::TextFormat::ParseFromString(cable_len_req_str, + &cable_len_req)); + gnmi::GetResponse cable_len_resp; + ASSERT_TRUE(google::protobuf::TextFormat::ParseFromString( + cable_len_resp_optic_str, &cable_len_resp)); + EXPECT_CALL(*mock_gnmi_stub_ptr, Get(_, EqualsProto(cable_len_req), _)) + .WillOnce( + DoAll(SetArgPointee<2>(cable_len_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)); +} + +TEST_F(GNMIThinkitInterfaceUtilityTest, + TestGetBreakoutModeConfigFromStringCopperPortSuccess) { + const std::string port_index = "1"; + const std::string intf_name = "Ethernet0"; + const std::string breakout_mode = "1x200G(4)+2x100G(4)"; + 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\": \"Ethernet0\",\n \"type\": \"iana-if-type:ethernetCsmacd\"\n },\n \"name\": \"Ethernet0\",\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\": \"Ethernet4\",\n \"type\": \"iana-if-type:ethernetCsmacd\"\n },\n \"name\": \"Ethernet4\",\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\": \"Ethernet6\",\n \"type\": \"iana-if-type:ethernetCsmacd\"\n },\n \"name\": \"Ethernet6\",\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"; + 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 cable_len_req; + ASSERT_TRUE(google::protobuf::TextFormat::ParseFromString(cable_len_req_str, + &cable_len_req)); + gnmi::GetResponse cable_len_resp; + ASSERT_TRUE(google::protobuf::TextFormat::ParseFromString( + cable_len_resp_copper_str, &cable_len_resp)); + EXPECT_CALL(*mock_gnmi_stub_ptr, Get(_, EqualsProto(cable_len_req), _)) + .WillOnce( + DoAll(SetArgPointee<2>(cable_len_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)); } TEST_F(GNMIThinkitInterfaceUtilityTest, TestGetBreakoutModeConfigFromStringIntConversionFailure) { const std::string port_index = "1"; + const std::string intf_name = "Ethernet0"; const std::string breakout_mode = "Xx400G"; + 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 cable_len_req; + ASSERT_TRUE(google::protobuf::TextFormat::ParseFromString(cable_len_req_str, + &cable_len_req)); + gnmi::GetResponse cable_len_resp; + ASSERT_TRUE(google::protobuf::TextFormat::ParseFromString( + cable_len_resp_optic_str, &cable_len_resp)); + EXPECT_CALL(*mock_gnmi_stub_ptr, Get(_, EqualsProto(cable_len_req), _)) + .WillOnce( + DoAll(SetArgPointee<2>(cable_len_resp), Return(grpc::Status::OK))); gnmi::SetRequest req; - EXPECT_THAT(pins_test::GetBreakoutModeConfigFromString(req, port_index, - breakout_mode), - StatusIs(absl::StatusCode::kInternal, - HasSubstr("Failed to convert string (X) to integer"))); + EXPECT_THAT( + pins_test::GetBreakoutModeConfigFromString( + req, mock_gnmi_stub_ptr.get(), port_index, intf_name, breakout_mode), + StatusIs(absl::StatusCode::kInternal, + HasSubstr("Failed to convert string (X) to integer"))); +} + +TEST_F(GNMIThinkitInterfaceUtilityTest, + TestGetBreakoutModeConfigFromStringIsCopperPortFailure) { + const std::string port_index = "1"; + const std::string intf_name = "Ethernet0"; + const std::string breakout_mode = "Xx400G"; + auto mock_gnmi_stub_ptr = absl::make_unique(); + gnmi::GetRequest get_xcvrd_req; + ASSERT_TRUE(google::protobuf::TextFormat::ParseFromString( + R"pb(prefix { origin: "openconfig" } + path { + elem { name: "interfaces" } + elem { + name: "interface" + key { key: "name" value: "Ethernet0" } + } + elem { name: "state" } + elem { name: "transceiver" } + } + type: STATE)pb", + &get_xcvrd_req)); + EXPECT_CALL(*mock_gnmi_stub_ptr, Get(_, EqualsProto(get_xcvrd_req), _)) + .WillOnce(Return(grpc::Status(grpc::StatusCode::DEADLINE_EXCEEDED, ""))); + gnmi::SetRequest req; + EXPECT_THAT( + pins_test::GetBreakoutModeConfigFromString( + req, mock_gnmi_stub_ptr.get(), port_index, intf_name, breakout_mode), + StatusIs(absl::StatusCode::kDeadlineExceeded, + HasSubstr("Failed to get GNMI state path value for port " + "transceiver for port Ethernet0"))); } TEST_F(GNMIThinkitInterfaceUtilityTest, @@ -1497,4 +1742,129 @@ TEST_F(GNMIThinkitInterfaceUtilityTest, StatusIs(absl::StatusCode::kInternal, HasSubstr("Failed to convert string (X) to integer"))); } + +TEST_F(GNMIThinkitInterfaceUtilityTest, TestIsCopperPortSuccessOpticPort) { + auto mock_gnmi_stub_ptr = absl::make_unique(); + gnmi::GetRequest req; + ASSERT_TRUE( + google::protobuf::TextFormat::ParseFromString(get_xcvrd_req_str, &req)); + gnmi::GetResponse resp; + ASSERT_TRUE( + google::protobuf::TextFormat::ParseFromString(get_xcvrd_resp_str, &resp)); + EXPECT_CALL(*mock_gnmi_stub_ptr, Get(_, EqualsProto(req), _)) + .WillOnce(DoAll(SetArgPointee<2>(resp), Return(grpc::Status::OK))); + gnmi::GetRequest cable_len_req; + ASSERT_TRUE(google::protobuf::TextFormat::ParseFromString(cable_len_req_str, + &cable_len_req)); + gnmi::GetResponse cable_len_resp; + ASSERT_TRUE(google::protobuf::TextFormat::ParseFromString( + cable_len_resp_optic_str, &cable_len_resp)); + EXPECT_CALL(*mock_gnmi_stub_ptr, Get(_, EqualsProto(cable_len_req), _)) + .WillOnce( + DoAll(SetArgPointee<2>(cable_len_resp), Return(grpc::Status::OK))); + EXPECT_THAT(pins_test::IsCopperPort(mock_gnmi_stub_ptr.get(), "Ethernet0"), + false); +} + +TEST_F(GNMIThinkitInterfaceUtilityTest, TestIsCopperPortSuccessCopperPort) { + auto mock_gnmi_stub_ptr = absl::make_unique(); + gnmi::GetRequest req; + ASSERT_TRUE( + google::protobuf::TextFormat::ParseFromString(get_xcvrd_req_str, &req)); + gnmi::GetResponse resp; + ASSERT_TRUE( + google::protobuf::TextFormat::ParseFromString(get_xcvrd_resp_str, &resp)); + EXPECT_CALL(*mock_gnmi_stub_ptr, Get(_, EqualsProto(req), _)) + .WillOnce(DoAll(SetArgPointee<2>(resp), Return(grpc::Status::OK))); + gnmi::GetRequest cable_len_req; + ASSERT_TRUE(google::protobuf::TextFormat::ParseFromString(cable_len_req_str, + &cable_len_req)); + gnmi::GetResponse cable_len_resp; + ASSERT_TRUE(google::protobuf::TextFormat::ParseFromString( + cable_len_resp_copper_str, &cable_len_resp)); + EXPECT_CALL(*mock_gnmi_stub_ptr, Get(_, EqualsProto(cable_len_req), _)) + .WillOnce( + DoAll(SetArgPointee<2>(cable_len_resp), Return(grpc::Status::OK))); + EXPECT_THAT(pins_test::IsCopperPort(mock_gnmi_stub_ptr.get(), "Ethernet0"), + true); +} + +TEST_F(GNMIThinkitInterfaceUtilityTest, TestIsCopperPortTransceiverGetFailure) { + auto mock_gnmi_stub_ptr = absl::make_unique(); + gnmi::GetRequest req; + ASSERT_TRUE( + google::protobuf::TextFormat::ParseFromString(get_xcvrd_req_str, &req)); + EXPECT_CALL(*mock_gnmi_stub_ptr, Get(_, EqualsProto(req), _)) + .WillOnce(Return(grpc::Status(grpc::StatusCode::DEADLINE_EXCEEDED, ""))); + EXPECT_THAT(pins_test::IsCopperPort(mock_gnmi_stub_ptr.get(), "Ethernet0"), + StatusIs(absl::StatusCode::kDeadlineExceeded, + HasSubstr("Failed to get GNMI state path value for " + "port transceiver for port Ethernet0"))); +} + +TEST_F(GNMIThinkitInterfaceUtilityTest, TestIsCopperPortCableLengthGetFailure) { + auto mock_gnmi_stub_ptr = absl::make_unique(); + gnmi::GetRequest req; + ASSERT_TRUE( + google::protobuf::TextFormat::ParseFromString(get_xcvrd_req_str, &req)); + gnmi::GetResponse resp; + ASSERT_TRUE( + google::protobuf::TextFormat::ParseFromString(get_xcvrd_resp_str, &resp)); + EXPECT_CALL(*mock_gnmi_stub_ptr, Get(_, EqualsProto(req), _)) + .WillOnce(DoAll(SetArgPointee<2>(resp), Return(grpc::Status::OK))); + gnmi::GetRequest cable_len_req; + ASSERT_TRUE(google::protobuf::TextFormat::ParseFromString(cable_len_req_str, + &cable_len_req)); + EXPECT_CALL(*mock_gnmi_stub_ptr, Get(_, EqualsProto(cable_len_req), _)) + .WillOnce(Return(grpc::Status(grpc::StatusCode::DEADLINE_EXCEEDED, ""))); + EXPECT_THAT(pins_test::IsCopperPort(mock_gnmi_stub_ptr.get(), "Ethernet0"), + StatusIs(absl::StatusCode::kDeadlineExceeded, + HasSubstr("Failed to get GNMI state path value for " + "cable-length for port Ethernet0"))); +} + +TEST_F(GNMIThinkitInterfaceUtilityTest, + TestIsCopperPortFloatConversionFailure) { + auto mock_gnmi_stub_ptr = absl::make_unique(); + gnmi::GetRequest req; + ASSERT_TRUE( + google::protobuf::TextFormat::ParseFromString(get_xcvrd_req_str, &req)); + gnmi::GetResponse resp; + ASSERT_TRUE( + google::protobuf::TextFormat::ParseFromString(get_xcvrd_resp_str, &resp)); + EXPECT_CALL(*mock_gnmi_stub_ptr, Get(_, EqualsProto(req), _)) + .WillOnce(DoAll(SetArgPointee<2>(resp), Return(grpc::Status::OK))); + gnmi::GetRequest cable_len_req; + ASSERT_TRUE(google::protobuf::TextFormat::ParseFromString(cable_len_req_str, + &cable_len_req)); + gnmi::GetResponse cable_len_resp; + ASSERT_TRUE(google::protobuf::TextFormat::ParseFromString( + R"pb(notification { + timestamp: 1631864194292383538 + prefix { origin: "openconfig" } + update { + path { + elem { name: "components" } + elem { + name: "component" + key { key: "name" value: "Ethernet0" } + } + elem { name: "transceiver" } + elem { name: "state" } + elem { name: "openconfig-platform-ext:cable-length" } + } + val { + json_ietf_val: "{\"openconfig-platform-ext:cable-length\":\"XYZ\"}" + } + } + } + )pb", + &cable_len_resp)); + EXPECT_CALL(*mock_gnmi_stub_ptr, Get(_, EqualsProto(cable_len_req), _)) + .WillOnce( + DoAll(SetArgPointee<2>(cable_len_resp), Return(grpc::Status::OK))); + EXPECT_THAT(pins_test::IsCopperPort(mock_gnmi_stub_ptr.get(), "Ethernet0"), + StatusIs(absl::StatusCode::kInternal, + HasSubstr("Failed to convert string (XYZ) to float"))); +} } // namespace pins_test