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

[Dvaas] Allow packet_ins for custom test vectors. #801

Open
wants to merge 3 commits 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
4 changes: 3 additions & 1 deletion dvaas/BUILD.bazel
Original file line number Diff line number Diff line change
Expand Up @@ -252,7 +252,9 @@ cc_library(
":test_vector_cc_proto",
"//gutil:proto",
"//gutil:status",
"//p4_pdpi:ir",
"//p4_pdpi/packetlib",
"@com_github_p4lang_p4runtime//:p4runtime_cc_proto",
"@com_google_absl//absl/status",
"@com_google_absl//absl/status:statusor",
"@com_google_absl//absl/strings",
Expand All @@ -274,6 +276,7 @@ cc_test(
"//gutil:status_matchers",
"//gutil:testing",
"//p4_pdpi/packetlib:packetlib_cc_proto",
"//p4_pdpi/testing:test_p4info_cc",
"@com_google_absl//absl/status",
"@com_google_absl//absl/status:statusor",
"@com_google_absl//absl/strings",
Expand Down Expand Up @@ -376,7 +379,6 @@ cc_library(
"//lib/gnmi:gnmi_helper",
"//lib/gnmi:openconfig_cc_proto",
"//lib/p4rt:p4rt_port",
"//p4_pdpi:ir",
"//p4_pdpi:ir_cc_proto",
"//p4_pdpi:p4_runtime_session",
"//p4_pdpi:p4_runtime_session_extras",
Expand Down
7 changes: 4 additions & 3 deletions dvaas/dataplane_validation.cc
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,6 @@
#include "lib/gnmi/openconfig.pb.h"
#include "lib/p4rt/p4rt_port.h"
#include "p4/v1/p4runtime.pb.h"
#include "p4_pdpi/ir.h"
#include "p4_pdpi/ir.pb.h"
#include "p4_pdpi/p4_runtime_session.h"
#include "p4_pdpi/p4_runtime_session_extras.h"
Expand Down Expand Up @@ -254,8 +253,10 @@ absl::StatusOr<ValidationResult> DataplaneValidator::ValidateDataplane(
GenerateTestVectors(params, sut, *backend_, *writer));
} else {
LOG(INFO) << "Checking user-provided test vectors for well-formedness";
ASSIGN_OR_RETURN(test_vectors, LegitimizeUserProvidedTestVectors(
params.packet_test_vector_override));
ASSIGN_OR_RETURN(pdpi::IrP4Info ir_info, pdpi::GetIrP4Info(*sut.p4rt));
ASSIGN_OR_RETURN(test_vectors,
LegitimizeUserProvidedTestVectors(
params.packet_test_vector_override, ir_info));
}
RETURN_IF_ERROR(
writer->AppendToTestArtifact("test_vectors.txt", ToString(test_vectors)));
Expand Down
13 changes: 11 additions & 2 deletions dvaas/packet_injection.cc
Original file line number Diff line number Diff line change
Expand Up @@ -153,10 +153,19 @@ absl::StatusOr<PacketTestRuns> SendTestPacketsAndCollectOutputs(
if (packet_test_vector.input().type() == SwitchInput::DATAPLANE) {
const Packet& packet = packet_test_vector.input().packet();

// Get corresponding control switch port for the packet's ingress port.
ASSIGN_OR_RETURN(const P4rtPortId sut_ingress_port,
P4rtPortId::MakeFromP4rtEncoding(packet.port()));
ASSIGN_OR_RETURN(
const P4rtPortId control_switch_port,
parameters.mirror_testbed_port_map
.GetControlSwitchPortConnectedToSutPort(sut_ingress_port));

// Inject to egress of control switch.
RETURN_IF_ERROR(pins::InjectEgressPacket(
packet.port(), absl::HexStringToBytes(packet.hex()),
control_ir_p4info, &control_switch, injection_delay));
control_switch_port.GetP4rtEncoding(),
absl::HexStringToBytes(packet.hex()), control_ir_p4info,
&control_switch, injection_delay));
} else {
return absl::UnimplementedError(
absl::StrCat("Test vector input type not supported\n",
Expand Down
23 changes: 21 additions & 2 deletions dvaas/port_id_map.cc
Original file line number Diff line number Diff line change
Expand Up @@ -88,9 +88,28 @@ MirrorTestbedP4rtPortIdMap::GetSutPortConnectedToControlSwitchPort(
absl::Substitute("Control port '$0' was not found in control switch "
"to SUT P4RT port ID map.",
control_port));
} else {
return it->second;
}
return it->second;
}

absl::StatusOr<pins_test::P4rtPortId>
MirrorTestbedP4rtPortIdMap::GetControlSwitchPortConnectedToSutPort(
const pins_test::P4rtPortId& sut_port) const {
// Handle implicit identity map.
if (!control_to_sut_port_map_.has_value()) return sut_port;

// Handle explicit map.
const auto it = absl::c_find_if(*control_to_sut_port_map_,
[&](const auto& control_sut_port) {
return control_sut_port.second == sut_port;
});
if (it == control_to_sut_port_map_->end()) {
return absl::NotFoundError(
absl::Substitute("SUT port '$0' was not found in control switch "
"to control switch P4RT port ID map.",
sut_port));
}
return it->first;
}

absl::Status CheckAndStoreMappedAndUnmappedPortIds(
Expand Down
6 changes: 6 additions & 0 deletions dvaas/port_id_map.h
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,12 @@ class MirrorTestbedP4rtPortIdMap {
absl::StatusOr<pins_test::P4rtPortId> GetSutPortConnectedToControlSwitchPort(
const pins_test::P4rtPortId& control_port) const;

// Returns the P4RT port ID of the control switch interface connected to the
// interface on the SUT with the given P4RT port ID according to the port
// mapping.
absl::StatusOr<pins_test::P4rtPortId> GetControlSwitchPortConnectedToSutPort(
const pins_test::P4rtPortId& sut_port) const;

private:
MirrorTestbedP4rtPortIdMap(
absl::flat_hash_map<pins_test::P4rtPortId, pins_test::P4rtPortId>
Expand Down
57 changes: 30 additions & 27 deletions dvaas/port_id_map_test.cc
Original file line number Diff line number Diff line change
Expand Up @@ -43,10 +43,17 @@ TEST(MirrorTestbedP4rtPortIdMap,
MirrorTestbedP4rtPortIdMap::CreateFromControlSwitchToSutPortMap(
{{port_1, port_2}}));

// Control -> SUT.
ASSERT_THAT(port_id_map.GetSutPortConnectedToControlSwitchPort(port_2),
StatusIs(absl::StatusCode::kNotFound));
ASSERT_THAT(port_id_map.GetSutPortConnectedToControlSwitchPort(port_3),
StatusIs(absl::StatusCode::kNotFound));

// SUT -> Control.
ASSERT_THAT(port_id_map.GetControlSwitchPortConnectedToSutPort(port_1),
StatusIs(absl::StatusCode::kNotFound));
ASSERT_THAT(port_id_map.GetControlSwitchPortConnectedToSutPort(port_3),
StatusIs(absl::StatusCode::kNotFound));
}

TEST(MirrorTestbedP4rtPortIdMap,
Expand Down Expand Up @@ -79,10 +86,17 @@ TEST(MirrorTestbedP4rtPortIdMap,
MirrorTestbedP4rtPortIdMap::CreateFromSutToControlSwitchPortMap(
{{port_1, port_2}}));

// Control -> SUT.
ASSERT_THAT(port_id_map.GetSutPortConnectedToControlSwitchPort(port_1),
StatusIs(absl::StatusCode::kNotFound));
ASSERT_THAT(port_id_map.GetSutPortConnectedToControlSwitchPort(port_3),
StatusIs(absl::StatusCode::kNotFound));

// SUT -> Control.
ASSERT_THAT(port_id_map.GetControlSwitchPortConnectedToSutPort(port_2),
StatusIs(absl::StatusCode::kNotFound));
ASSERT_THAT(port_id_map.GetControlSwitchPortConnectedToSutPort(port_3),
StatusIs(absl::StatusCode::kNotFound));
}

TEST(MirrorTestbedP4rtPortIdMap,
Expand All @@ -101,32 +115,7 @@ TEST(MirrorTestbedP4rtPortIdMap,
StatusIs(absl::StatusCode::kInvalidArgument));
}

TEST(MirrorTestbedP4rtPortIdMap,
RetrunsCorrectSutPortGivenControlPortGivenControlToSutMapping) {
ASSERT_OK_AND_ASSIGN(const auto port_1,
P4rtPortId::MakeFromP4rtEncoding("1"));
ASSERT_OK_AND_ASSIGN(const auto port_2,
P4rtPortId::MakeFromP4rtEncoding("2"));
ASSERT_OK_AND_ASSIGN(const auto port_3,
P4rtPortId::MakeFromP4rtEncoding("3"));
ASSERT_OK_AND_ASSIGN(const auto port_4,
P4rtPortId::MakeFromP4rtEncoding("4"));

ASSERT_OK_AND_ASSIGN(
const auto port_id_map,
MirrorTestbedP4rtPortIdMap::CreateFromControlSwitchToSutPortMap({
{port_1, port_2},
{port_3, port_4},
}));

ASSERT_THAT(port_id_map.GetSutPortConnectedToControlSwitchPort(port_1),
IsOkAndHolds(Eq(port_2)));
ASSERT_THAT(port_id_map.GetSutPortConnectedToControlSwitchPort(port_3),
IsOkAndHolds(Eq(port_4)));
}

TEST(MirrorTestbedP4rtPortIdMap,
RetrunsCorrectSutPortGivenControlPortGivenSutToControlMapping) {
TEST(MirrorTestbedP4rtPortIdMap, ReturnsCorrectPortGivenSutToControlMapping) {
ASSERT_OK_AND_ASSIGN(const auto port_1,
P4rtPortId::MakeFromP4rtEncoding("1"));
ASSERT_OK_AND_ASSIGN(const auto port_2,
Expand All @@ -143,23 +132,37 @@ TEST(MirrorTestbedP4rtPortIdMap,
{port_3, port_4},
}));

// Control -> SUT.
ASSERT_THAT(port_id_map.GetSutPortConnectedToControlSwitchPort(port_2),
IsOkAndHolds(Eq(port_1)));
ASSERT_THAT(port_id_map.GetSutPortConnectedToControlSwitchPort(port_4),
IsOkAndHolds(Eq(port_3)));

// SUT -> Control.
ASSERT_THAT(port_id_map.GetControlSwitchPortConnectedToSutPort(port_1),
IsOkAndHolds(Eq(port_2)));
ASSERT_THAT(port_id_map.GetControlSwitchPortConnectedToSutPort(port_3),
IsOkAndHolds(Eq(port_4)));
}

TEST(MirrorTestbedP4rtPortIdMap, RetrunsCorrectSutPortWithImplicitIdentityMap) {
TEST(MirrorTestbedP4rtPortIdMap, ReturnsCorrectPortWithImplicitIdentityMap) {
const auto port_id_map = MirrorTestbedP4rtPortIdMap::CreateIdentityMap();
ASSERT_OK_AND_ASSIGN(const auto port_1,
P4rtPortId::MakeFromP4rtEncoding("1"));
ASSERT_OK_AND_ASSIGN(const auto port_2,
P4rtPortId::MakeFromP4rtEncoding("2"));

// Control -> SUT.
ASSERT_THAT(port_id_map.GetSutPortConnectedToControlSwitchPort(port_1),
IsOkAndHolds(Eq(port_1)));
ASSERT_THAT(port_id_map.GetSutPortConnectedToControlSwitchPort(port_2),
IsOkAndHolds(Eq(port_2)));

// SUT -> Control.
ASSERT_THAT(port_id_map.GetControlSwitchPortConnectedToSutPort(port_1),
IsOkAndHolds(Eq(port_1)));
ASSERT_THAT(port_id_map.GetControlSwitchPortConnectedToSutPort(port_2),
IsOkAndHolds(Eq(port_2)));
}

} // namespace
Expand Down
114 changes: 62 additions & 52 deletions dvaas/test_run_validation.cc
Original file line number Diff line number Diff line change
Expand Up @@ -99,11 +99,13 @@ bool CompareSwitchOutputs(SwitchOutput actual_output,
return false;
}

if (actual_output.packet_ins_size() != expected_output.packet_ins_size()) {
*listener << "has mismatched number of packet ins (actual: "
<< actual_output.packet_ins_size()
<< " expected: " << expected_output.packet_ins_size() << ")\n";
return false;
if (!params.treat_expected_and_actual_outputs_as_having_no_packet_ins) {
if (actual_output.packet_ins_size() != expected_output.packet_ins_size()) {
*listener << "has mismatched number of packet ins (actual: "
<< actual_output.packet_ins_size()
<< " expected: " << expected_output.packet_ins_size() << ")\n";
return false;
}
}

std::sort(actual_output.mutable_packets()->pointer_begin(),
Expand Down Expand Up @@ -139,58 +141,63 @@ bool CompareSwitchOutputs(SwitchOutput actual_output,
}
}

for (int i = 0; i < expected_output.packet_ins_size(); ++i) {
const PacketIn& actual_packet_in = actual_output.packet_ins(i);
const PacketIn& expected_packet_in = expected_output.packet_ins(i);

MessageDifferencer differ;
for (auto* field : params.ignored_packetlib_fields)
differ.IgnoreField(field);
std::string diff;
differ.ReportDifferencesToString(&diff);
if (!differ.Compare(expected_packet_in.parsed(),
actual_packet_in.parsed())) {
*listener << "has packet in " << i
<< " with mismatched header fields:\n " << Indent(2, diff);
return false;
}

// Check that received packet in has desired metadata (except for ignored
// metadata).
for (const auto& expected_metadata : expected_packet_in.metadata()) {
if (params.ignored_packet_in_metadata.contains(expected_metadata.name()))
continue;

std::optional<pdpi::IrPacketMetadata> actual_metadata =
GetPacketInMetadataByName(actual_packet_in, expected_metadata.name());
if (!actual_metadata.has_value()) {
*listener << "has packet in " << i << " with missing metadata field '"
<< expected_metadata.name() << "':\n " << Indent(2, diff);
return false;
}

if (!differ.Compare(expected_metadata.value(),
actual_metadata->value())) {
if (!params.treat_expected_and_actual_outputs_as_having_no_packet_ins) {
for (int i = 0; i < expected_output.packet_ins_size(); ++i) {
const PacketIn& actual_packet_in = actual_output.packet_ins(i);
const PacketIn& expected_packet_in = expected_output.packet_ins(i);

MessageDifferencer differ;
for (auto* field : params.ignored_packetlib_fields)
differ.IgnoreField(field);
std::string diff;
differ.ReportDifferencesToString(&diff);
if (!differ.Compare(expected_packet_in.parsed(),
actual_packet_in.parsed())) {
*listener << "has packet in " << i
<< " with mismatched value for metadata field '"
<< expected_metadata.name() << "':\n " << Indent(2, diff);
<< " with mismatched header fields:\n " << Indent(2, diff);
return false;
}
}

// Check that received packet in does not have additional metadata (except
// for ignored metadata).
for (const auto& actual_metadata : actual_packet_in.metadata()) {
if (params.ignored_packet_in_metadata.contains(actual_metadata.name()))
continue;
// Check that received packet in has desired metadata (except for ignored
// metadata).
for (const auto& expected_metadata : expected_packet_in.metadata()) {
if (params.ignored_packet_in_metadata.contains(
expected_metadata.name()))
continue;

std::optional<pdpi::IrPacketMetadata> actual_metadata =
GetPacketInMetadataByName(actual_packet_in,
expected_metadata.name());
if (!actual_metadata.has_value()) {
*listener << "has packet in " << i << " with missing metadata field '"
<< expected_metadata.name() << "':\n " << Indent(2, diff);
return false;
}

if (!differ.Compare(expected_metadata.value(),
actual_metadata->value())) {
*listener << "has packet in " << i
<< " with mismatched value for metadata field '"
<< expected_metadata.name() << "':\n " << Indent(2, diff);
return false;
}
}

std::optional<pdpi::IrPacketMetadata> expected_metadata =
GetPacketInMetadataByName(expected_packet_in, actual_metadata.name());
if (!expected_metadata.has_value()) {
*listener << "has packet in " << i
<< " with additional metadata field '"
<< actual_metadata.name() << "':\n " << Indent(2, diff);
return false;
// Check that received packet in does not have additional metadata (except
// for ignored metadata).
for (const auto& actual_metadata : actual_packet_in.metadata()) {
if (params.ignored_packet_in_metadata.contains(actual_metadata.name()))
continue;

std::optional<pdpi::IrPacketMetadata> expected_metadata =
GetPacketInMetadataByName(expected_packet_in,
actual_metadata.name());
if (!expected_metadata.has_value()) {
*listener << "has packet in " << i
<< " with additional metadata field '"
<< actual_metadata.name() << "':\n " << Indent(2, diff);
return false;
}
}
}
}
Expand Down Expand Up @@ -370,6 +377,9 @@ PacketTestValidationResult ValidateTestRun(
}),
")");
}
if (diff_params.treat_expected_and_actual_outputs_as_having_no_packet_ins) {
absl::StrAppend(&expectation, "\n (ignoring packet-ins)");
}
std::string actual = DescribeActual(test_run.test_vector().input(),
actual_output_characterization);
if (actual_matches_expected) {
Expand Down
8 changes: 8 additions & 0 deletions dvaas/test_run_validation.h
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,14 @@ struct SwitchOutputDiffParams {
// in `ignored_metadata_for_validation`, the field is ignored during
// comparison.
absl::flat_hash_set<std::string> ignored_packet_in_metadata;

// If true, packet-ins (punted packets) are ignored while comparing expected
// and actual output. Note that packet-ins are completely ignored on both
// sides. Effectively the expected and actual outputs are compared assuming
// neither has any packet-ins.
// TODO: Remove and replace with
// `ignore_missing_packet_ins_in_actual_output`.
bool treat_expected_and_actual_outputs_as_having_no_packet_ins = false;
};

// Validates the given `test_vector` using the parameters in `params` and
Expand Down
Loading
Loading