Skip to content

Commit

Permalink
[Thinkit] Allow gNMI interface tests to accept P4Info as an input, Ge…
Browse files Browse the repository at this point in the history
…nerate upper-64-bit IPv6 packet differences instead of lower-64 & gNMI port interface tests, led test.
  • Loading branch information
kishanps authored and divyagayathri-hcl committed Dec 11, 2024
1 parent 7eea0f4 commit bf96423
Show file tree
Hide file tree
Showing 8 changed files with 320 additions and 246 deletions.
1 change: 1 addition & 0 deletions tests/BUILD.bazel
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,7 @@ cc_library(
"@com_github_gnmi//proto/gnmi:gnmi_cc_grpc_proto",
"@com_github_google_glog//:glog",
"@com_github_nlohmann_json//:nlohmann_json",
"@com_github_p4lang_p4runtime//:p4info_cc_proto",
"@com_google_absl//absl/container:flat_hash_map",
"@com_google_absl//absl/container:flat_hash_set",
"@com_google_absl//absl/status",
Expand Down
1 change: 1 addition & 0 deletions tests/forwarding/BUILD.bazel
Original file line number Diff line number Diff line change
Expand Up @@ -130,6 +130,7 @@ cc_library(
deps = [
"//dvaas:test_vector",
"//p4_pdpi/netaddr:ipv4_address",
"//p4_pdpi/netaddr:ipv6_address",
"//p4_pdpi/netaddr:mac_address",
"//p4_pdpi/packetlib",
"@com_google_absl//absl/base:core_headers",
Expand Down
51 changes: 24 additions & 27 deletions tests/forwarding/packet_test_util.cc
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
#include "tests/forwarding/packet_test_util.h"

namespace pins {
#include "p4_pdpi/netaddr/ipv6_address.h"

using ::packetlib::EthernetHeader;
using ::packetlib::IpDscp;
Expand All @@ -33,17 +34,16 @@ using ::packetlib::Ipv6Header;
using ::packetlib::UdpHeader;
using ::packetlib::UdpPort;

const uint64_t kBaseDstMac = 234;
const uint64_t kMaxMacAddr = static_cast<uint64_t>(1) << (6 * 8);
namespace {
constexpr uint64_t kBaseDstMac = 234;
constexpr uint64_t kMacAddrSize = static_cast<uint64_t>(1) << (48);

// Base IPv4 address for generating the outer IP header for packets that are not
// supposed to be decapped.
const uint32_t kBaseIpV4Src = 0x01020304; // 1.2.3.4
const uint32_t kBaseIpV4Dst = 0x02030405; // 2.3.4.5
const uint32_t kBaseDecapIpV4Src = 0x0a020304; // 10.2.3.4
const uint32_t kBaseDecapIpV4Dst = 0x14030405; // 20.3.4.5

namespace {
constexpr uint32_t kBaseIpV4Src = 0x01020304; // 1.2.3.4
constexpr uint32_t kBaseIpV4Dst = 0x02030405; // 2.3.4.5
constexpr uint32_t kBaseDecapIpV4Src = 0x0a020304; // 10.2.3.4
constexpr uint32_t kBaseDecapIpV4Dst = 0x14030405; // 20.3.4.5

std::string PacketFieldToString(const PacketField field) {
switch (field) {
Expand Down Expand Up @@ -152,7 +152,7 @@ bool IsValidTestConfiguration(const TestConfiguration& config) {

// Returns the ith destination MAC that is used when varying that field.
netaddr::MacAddress GetIthDstMac(int i) {
return netaddr::MacAddress(std::bitset<48>(kBaseDstMac + i % kMaxMacAddr));
return netaddr::MacAddress(std::bitset<48>(kBaseDstMac + i % kMacAddrSize));
}

// Returns a human-readable description of a test config.
Expand All @@ -175,18 +175,21 @@ std::string TestConfigurationToPayload(const TestConfiguration& config) {
// and vary in exactly one field (the one specified in the config).
absl::StatusOr<packetlib::Packet> GenerateIthPacket(
const TestConfiguration& config, int index) {
constexpr uint64_t kDefaultSrcIpUpper = 0x0001000200030004;
constexpr uint64_t kDefaultDstIpUpper = 0x0002000300040005;
constexpr uint64_t kDefaultSrcMac = 123;

packetlib::Packet packet;
const auto& field = config.field;

EthernetHeader* eth = packet.add_headers()->mutable_ethernet_header();

uint64_t default_src_mac = 123;
eth->set_ethernet_source(
netaddr::MacAddress(std::bitset<48>(default_src_mac)).ToString());
netaddr::MacAddress(std::bitset<48>(kDefaultSrcMac)).ToString());
if (field == PacketField::kEthernetSrc) {
eth->set_ethernet_source(
netaddr::MacAddress(
std::bitset<48>(default_src_mac + index % kMaxMacAddr))
std::bitset<48>(kDefaultSrcMac + index % kMacAddrSize))
.ToString());
}
eth->set_ethernet_destination(
Expand Down Expand Up @@ -239,20 +242,14 @@ absl::StatusOr<packetlib::Packet> GenerateIthPacket(
ip->set_fragment_offset(IpFragmentOffset(0));
} else {
Ipv6Header* ip = packet.add_headers()->mutable_ipv6_header();
auto default_src = absl::MakeUint128(0x0001000200030004, 0);
if (field == PacketField::kIpSrc) {
ip->set_ipv6_source(
netaddr::Ipv6Address(default_src + index).ToString());
} else {
ip->set_ipv6_source(netaddr::Ipv6Address(default_src).ToString());
}
auto default_dst = absl::MakeUint128(0x0002000300040005, 0);
if (field == PacketField::kIpDst) {
ip->set_ipv6_destination(
netaddr::Ipv6Address(default_dst + index).ToString());
} else {
ip->set_ipv6_destination(netaddr::Ipv6Address(default_dst).ToString());
}
auto src_ip = absl::MakeUint128(
kDefaultSrcIpUpper + (field == PacketField::kIpSrc ? index : 0), 0);
ip->set_ipv6_source(netaddr::Ipv6Address(src_ip).ToString());

auto dst_ip = absl::MakeUint128(
kDefaultDstIpUpper + (field == PacketField::kIpDst ? index : 0), 0);
ip->set_ipv6_destination(netaddr::Ipv6Address(dst_ip).ToString());

ip->set_hop_limit(IpHopLimit(hop_limit));
ip->set_dscp(IpDscp(dscp));
uint32_t flow_label = 0;
Expand Down Expand Up @@ -310,7 +307,7 @@ absl::StatusOr<packetlib::Packet> GenerateIthPacket(
ip->set_fragment_offset(IpFragmentOffset(0));
} else {
Ipv6Header* ip = packet.add_headers()->mutable_ipv6_header();
auto default_inner_src = absl::MakeUint128(0x00030000400050006, 0);
auto default_inner_src = absl::MakeUint128(0x0003000400050006, 0);
if (field == PacketField::kInnerIpSrc) {
ip->set_ipv6_source(
netaddr::Ipv6Address(default_inner_src + index).ToString());
Expand Down
21 changes: 11 additions & 10 deletions tests/thinkit_gnmi_interface_tests.cc
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,8 @@ void BreakoutDuringPortInUse(thinkit::Switch& sut,
gnmi::gNMI::StubInterface* sut_gnmi_stub,
RandomPortBreakoutInfo port_info,
const std::string& platform_json_contents,
bool test_child_port_in_use) {
bool test_child_port_in_use,
const p4::config::v1::P4Info& p4_info) {
// Get the original breakout info on the port.
// This contains the state values of physical channels and
// operational status information for ports in original breakout mode.
Expand Down Expand Up @@ -119,13 +120,11 @@ void BreakoutDuringPortInUse(thinkit::Switch& sut,
ASSERT_OK(pdpi::SetMetadataAndSetForwardingPipelineConfig(
sut_p4_session.get(),
p4::v1::SetForwardingPipelineConfigRequest::RECONCILE_AND_COMMIT,
sai::GetP4Info(sai::Instantiation::kMiddleblock)))
p4_info))
<< "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(auto ir_p4info, pdpi::CreateIrP4Info(p4_info));
ASSERT_OK_AND_ASSIGN(const p4::v1::TableEntry pi_entry,
pdpi::PartialPdTableEntryToPiTableEntry(pdpi::IrP4Info(), pd_entry));

Expand Down Expand Up @@ -203,26 +202,28 @@ void BreakoutDuringPortInUse(thinkit::Switch& sut,
}

void TestGNMIParentPortInUseDuringBreakout(
thinkit::Switch& sut, std::string& platform_json_contents) {
thinkit::Switch& sut, std::string& platform_json_contents,
const p4::config::v1::P4Info& p4_info) {
ASSERT_OK_AND_ASSIGN(auto sut_gnmi_stub, sut.CreateGnmiStub());
// Get a random port from list of front panel ports that supports at least
// one breakout mode of required type other than its current mode.
ASSERT_OK_AND_ASSIGN(auto port, GetRandomPortWithSupportedBreakoutModes(
*sut_gnmi_stub, platform_json_contents,
BreakoutType::kAny));
BreakoutDuringPortInUse(sut, sut_gnmi_stub.get(), port,
platform_json_contents, false);
platform_json_contents, false, p4_info);
}

void TestGNMIChildPortInUseDuringBreakout(thinkit::Switch& sut,
std::string& platform_json_contents) {
void TestGNMIChildPortInUseDuringBreakout(
thinkit::Switch& sut, std::string& platform_json_contents,
const p4::config::v1::P4Info& p4_info) {
ASSERT_OK_AND_ASSIGN(auto sut_gnmi_stub, sut.CreateGnmiStub());
// Get a random port from list of front panel ports that supports at least
// one breakout mode of required type other than its current mode.
ASSERT_OK_AND_ASSIGN(auto port, GetRandomPortWithSupportedBreakoutModes(
*sut_gnmi_stub, platform_json_contents,
BreakoutType::kChannelized));
BreakoutDuringPortInUse(sut, sut_gnmi_stub.get(), port,
platform_json_contents, true);
platform_json_contents, true, p4_info);
}
} // namespace pins_test
18 changes: 13 additions & 5 deletions tests/thinkit_gnmi_interface_tests.h
Original file line number Diff line number Diff line change
Expand Up @@ -18,26 +18,34 @@
#include "absl/container/flat_hash_map.h"
#include "absl/container/flat_hash_set.h"
#include "absl/strings/string_view.h"
#include "p4/config/v1/p4info.pb.h"
#include "sai_p4/instantiations/google/instantiations.h"
#include "sai_p4/instantiations/google/sai_p4info.h"
#include "tests/thinkit_gnmi_interface_util.h"
#include "thinkit/ssh_client.h"
#include "thinkit/switch.h"

namespace pins_test {

// Test port breakout during parent port in use.
void TestGNMIParentPortInUseDuringBreakout(thinkit::Switch& sut,
std::string& platform_json_contents);
void TestGNMIParentPortInUseDuringBreakout(
thinkit::Switch& sut, std::string& platform_json_contents,
const p4::config::v1::P4Info& p4_info =
sai::GetP4Info(sai::Instantiation::kMiddleblock));

// Test port breakout during child port in use.
void TestGNMIChildPortInUseDuringBreakout(thinkit::Switch& sut,
std::string& platform_json_contents);
void TestGNMIChildPortInUseDuringBreakout(
thinkit::Switch& sut, std::string& platform_json_contents,
const p4::config::v1::P4Info& p4_info =
sai::GetP4Info(sai::Instantiation::kMiddleblock));

// Helper function to test port in use.
void BreakoutDuringPortInUse(thinkit::Switch& sut,
gnmi::gNMI::StubInterface* sut_gnmi_stub,
RandomPortBreakoutInfo port_info,
const std::string& platform_json_contents,
bool test_child_port_in_use);
bool test_child_port_in_use,
const p4::config::v1::P4Info& p4_info);

// Test port breakout during parent port in use.
void TestGNMIParentPortInUseDuringBreakout(thinkit::Switch& sut,
Expand Down
82 changes: 53 additions & 29 deletions tests/thinkit_gnmi_interface_util.cc
Original file line number Diff line number Diff line change
Expand Up @@ -163,14 +163,15 @@ absl::StatusOr<RandomPortBreakoutInfo> GetRandomPortWithSupportedBreakoutModes(
std::vector<std::string> up_parent_port_list;
for (auto& intf : interface_to_oper_status_map) {
if (absl::StartsWith(intf.first, kEthernet)) {
auto port_number_str = intf.first.substr(kEthernetLen);
int port_number;
if (!absl::SimpleAtoi(port_number_str, &port_number)) {
ASSIGN_OR_RETURN(std::vector<std::string> slot_port_lane,
GetSlotPortLaneForPort(intf.first));
int curr_lane_number;
if (!absl::SimpleAtoi(slot_port_lane[kLaneIndex], &curr_lane_number)) {
return gutil::InternalErrorBuilder().LogError()
<< "Failed to convert string (" << port_number_str
<< "Failed to convert string (" << slot_port_lane[kLaneIndex]
<< ") to integer";
}
if (((port_number % kMaxPortLanes) == 0) && intf.second == kStateUp) {
if ((curr_lane_number == 1) && intf.second == kStateUp) {
up_parent_port_list.push_back(intf.first);
}
}
Expand Down Expand Up @@ -255,6 +256,22 @@ absl::StatusOr<RandomPortBreakoutInfo> GetRandomPortWithSupportedBreakoutModes(
return port_info;
}

absl::StatusOr<std::vector<std::string>> GetSlotPortLaneForPort(
const absl::string_view port) {
if (!absl::StartsWith(port, kEthernet)) {
return absl::InvalidArgumentError(
absl::StrCat("Requested port (", port, ") is not a front panel port"));
}
auto slot_port_lane = port.substr(kEthernetLen);
std::vector<std::string> values = absl::StrSplit(slot_port_lane, '/');
if (values.size() != 3) {
return absl::InvalidArgumentError(
absl::StrCat("Requested port (", port,
") does not have a valid format (EthernetX/Y/Z)"));
}
return values;
}

absl::StatusOr<absl::flat_hash_map<std::string, pins_test::PortBreakoutInfo>>
GetExpectedPortInfoForBreakoutMode(const std::string& port,
absl::string_view breakout_mode) {
Expand All @@ -270,17 +287,19 @@ GetExpectedPortInfoForBreakoutMode(const std::string& port,
// Get maximum physical channels in a breakout group which is max
// lanes per physical port/number of groups in a breakout mode.
auto max_channels_in_group = kMaxPortLanes / modes.size();
auto port_number_str = port.substr(kEthernetLen);
int current_port_number;
if (!absl::SimpleAtoi(port_number_str, &current_port_number)) {
ASSIGN_OR_RETURN(std::vector<std::string> slot_port_lane,
GetSlotPortLaneForPort(port));
int curr_lane_number;
if (!absl::SimpleAtoi(slot_port_lane[kLaneIndex], &curr_lane_number)) {
return gutil::InternalErrorBuilder().LogError()
<< "Failed to convert string (" << port_number_str << ") to integer";
<< "Failed to convert string (" << slot_port_lane[kLaneIndex]
<< ") to integer";
}
if (current_port_number % kMaxPortLanes != 0) {
return gutil::InternalErrorBuilder().LogError()
<< "Requested port (" << port << ") is not a parent port";
// Lane number for a master port is always 1.
if (curr_lane_number != 1) {
return absl::InvalidArgumentError(
absl::StrCat("Requested port (", port, ") is not a parent port"));
}

auto current_physical_channel = 0;
absl::flat_hash_map<std::string, pins_test::PortBreakoutInfo>
expected_breakout_info;
Expand All @@ -295,14 +314,16 @@ 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 =>
// 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.
// using offset from the parent port. For a breakout mode of Ethernet1/1/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 Ethernet1/1/1, Ethernet1/1/3 for mode 2x100G
// and Ethernet1/1/5 for mode 1x200G.
for (int i = 0; i < num_breakouts; i++) {
auto port = absl::StrCat(kEthernet, std::to_string(current_port_number));
auto port = absl::StrCat(kEthernet, slot_port_lane[kSlotIndex], "/",
slot_port_lane[kPortIndex], "/",
std::to_string(curr_lane_number));
// Populate expected physical channels for each port.
// Physical channels are between 0 to 7.
int offset = max_channels_in_group / num_breakouts;
Expand All @@ -319,7 +340,7 @@ GetExpectedPortInfoForBreakoutMode(const std::string& port,
}
physical_channels += "]";
current_physical_channel += offset;
current_port_number += offset;
curr_lane_number += offset;
expected_breakout_info[port] = PortBreakoutInfo{physical_channels};
}
}
Expand Down Expand Up @@ -495,13 +516,14 @@ absl::Status GetBreakoutModeConfigFromString(
auto index = 0;

// Get current port number.
int current_port_number;
if (!absl::SimpleAtoi(port_index, &current_port_number)) {
ASSIGN_OR_RETURN(std::vector<std::string> slot_port_lane,
GetSlotPortLaneForPort(intf_name));
int curr_lane_number;
if (!absl::SimpleAtoi(slot_port_lane[kLaneIndex], &curr_lane_number)) {
return gutil::InternalErrorBuilder().LogError()
<< "Failed to convert string (" << port_index << ") to integer";
<< "Failed to convert string (" << slot_port_lane[kLaneIndex]
<< ") to integer";
}
current_port_number = (current_port_number - 1) * kMaxPortLanes;

ASSIGN_OR_RETURN(bool is_copper_port, IsCopperPort(sut_gnmi_stub, intf_name));

for (const auto& mode : modes) {
Expand All @@ -524,14 +546,16 @@ absl::Status GetBreakoutModeConfigFromString(
// 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(current_port_number));
auto port = absl::StrCat(kEthernet, slot_port_lane[kSlotIndex], "/",
slot_port_lane[kPortIndex], "/",
std::to_string(curr_lane_number));
ASSIGN_OR_RETURN(
auto interface_config,
GenerateInterfaceBreakoutConfig(port, current_port_number,
GenerateInterfaceBreakoutConfig(port, curr_lane_number,
breakout_speed, is_copper_port));
interface_configs.push_back(interface_config);
int offset = max_channels_in_group / num_breakouts;
current_port_number += offset;
curr_lane_number += offset;
}
index += 1;
}
Expand Down
8 changes: 8 additions & 0 deletions tests/thinkit_gnmi_interface_util.h
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,9 @@ namespace pins_test {
inline constexpr int kMaxPortLanes = 8;
inline constexpr int kEthernetLen = 8;
inline constexpr char kEthernet[] = "Ethernet";
const int kSlotIndex = 0;
const int kPortIndex = 1;
const int kLaneIndex = 2;

// // PortBreakoutInfo contains physical channels and operational status for an
// // interface.
Expand Down Expand Up @@ -136,5 +139,10 @@ std::string ConstructSupportedBreakoutMode(absl::string_view num_breakouts,
// IsCopperPort returns whether the port is copper or optic.
absl::StatusOr<bool> IsCopperPort(gnmi::gNMI::StubInterface* sut_gnmi_stub,
absl::string_view port);

// Returns vector of <slot/port/lane> from front panel port
// Ethernet<slot/port/lane>
absl::StatusOr<std::vector<std::string>> GetSlotPortLaneForPort(
const absl::string_view port);
} // namespace pins_test
#endif // PINS_TESTS_THINKIT_GNMI_INTERFACE_UTIL_H_
Loading

0 comments on commit bf96423

Please sign in to comment.