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..c575f050 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" @@ -33,17 +35,17 @@ namespace { using ::nlohmann::json; -} // namespace - -void StripSymbolFromString(std::string& str, const char symbol) { +void StripSymbolFromString(std::string& str, 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); +} // namespace + +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 +107,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 +268,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 +300,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 +334,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 +381,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 +447,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 +505,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 +515,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 +528,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 +557,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 +566,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 +585,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