Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Thinkit] Allow gNMI interface tests to accept P4Info as an input, Generate upper-64-bit IPv6 packet differences instead of lower-64 & gNMI port interface tests, led test. #847

Open
wants to merge 1 commit into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
Loading