Skip to content

Commit

Permalink
[Dvaas]: Add library that allows DVaaS/Arriba to process user-provide…
Browse files Browse the repository at this point in the history
…d test vectors using a bare-bones, but sane API. Adding user-provided test vectors Tests.
  • Loading branch information
VSuryaprasad-HCL committed Nov 19, 2024
1 parent 04a2723 commit bb8e983
Show file tree
Hide file tree
Showing 6 changed files with 1,208 additions and 1 deletion.
51 changes: 51 additions & 0 deletions dvaas/BUILD.bazel
Original file line number Diff line number Diff line change
Expand Up @@ -256,3 +256,54 @@ cc_test(
"@com_google_googletest//:gtest_main",
],
)

cc_library(
name = "user_provided_packet_test_vector",
testonly = True,
srcs = ["user_provided_packet_test_vector.cc"],
hdrs = ["user_provided_packet_test_vector.h"],
deps = [
":test_vector",
":test_vector_cc_proto",
"//gutil:proto",
"//gutil:status",
"//p4_pdpi/packetlib",
"@com_google_absl//absl/status",
"@com_google_absl//absl/status:statusor",
"@com_google_absl//absl/strings",
"@com_google_absl//absl/types:span",
],
)

# go/golden-test-with-coverage
cc_test(
name = "user_provided_packet_test_vector_test",
srcs = ["user_provided_packet_test_vector_test.cc"],
linkstatic = True,
deps = [
":test_vector",
":test_vector_cc_proto",
":user_provided_packet_test_vector",
"//gutil:collections",
"//gutil:proto",
"//gutil:status_matchers",
"//gutil:testing",
"//p4_pdpi/packetlib:packetlib_cc_proto",
"@com_google_absl//absl/status",
"@com_google_absl//absl/status:statusor",
"@com_google_absl//absl/strings",
"@com_google_googletest//:gtest_main",
],
)

cmd_diff_test(
name = "user_provided_packet_test_vector_diff_test",
actual_cmd = " | ".join([
"$(execpath :user_provided_packet_test_vector_test)",
# Strip unnecessary lines for golden testing.
"sed '1,/^\\[ RUN/d'", # Strip everything up to a line beginning with '[ RUN'.
"sed '/^\\[/d'", # Strip every line beginning with '['.
]),
expected = "user_provided_packet_test_vector_test.expected",
tools = [":user_provided_packet_test_vector_test"],
)
113 changes: 113 additions & 0 deletions dvaas/user_provided_packet_test_vector.cc
Original file line number Diff line number Diff line change
@@ -0,0 +1,113 @@
#include "dvaas/user_provided_packet_test_vector.h"

#include <string>
#include <utility>

#include "absl/status/status.h"
#include "absl/status/statusor.h"
#include "absl/strings/escaping.h"
#include "absl/types/span.h"
#include "dvaas/test_vector.h"
#include "dvaas/test_vector.pb.h"
#include "gutil/proto.h"
#include "gutil/status.h"
#include "p4_pdpi/packetlib/packetlib.h"

namespace dvaas {

namespace {

// Checks that the given `input_packet` is well-formed, returning it with
// omittable fields filled in if that is the case, or an error otherwise.
absl::StatusOr<Packet> LegitimizePacket(Packet packet) {
RETURN_IF_ERROR(
packetlib::UpdateMissingComputedFields(*packet.mutable_parsed())
.status());
RETURN_IF_ERROR(packetlib::ValidatePacket(packet.parsed()));
ASSIGN_OR_RETURN(std::string raw_packet,
packetlib::RawSerializePacket(packet.parsed()));
packet.set_hex(absl::BytesToHexString(raw_packet));
return packet;
}

// Checks that the given `vector` is well-formed and if so adds it to
// `legitimized_test_vectors_by_id`, or returns error otherwise.
absl::Status LegitimizeTestVector(
PacketTestVector vector,
PacketTestVectorById& legitimized_test_vectors_by_id) {
if (vector.input().type() != SwitchInput::DATAPLANE) {
return gutil::UnimplementedErrorBuilder()
<< "only supported input type is DATAPLANE; found "
<< SwitchInput::Type_Name(vector.input().type());
}

// Legitimize input packet.
Packet& input_packet = *vector.mutable_input()->mutable_packet();
ASSIGN_OR_RETURN(int tag, ExtractTestPacketTag(input_packet.parsed()),
_.SetPrepend() << "invalid input packet: ");
ASSIGN_OR_RETURN(input_packet, LegitimizePacket(input_packet),
_.SetPrepend() << "invalid input packet: ");

// Legitimize acceptable outputs.
if (vector.acceptable_outputs().empty()) {
return gutil::InvalidArgumentErrorBuilder()
<< "must specify at least 1 acceptable output, but 0 were found";
}
for (SwitchOutput& output : *vector.mutable_acceptable_outputs()) {
// Punted output packets are not supported for now.
if (!output.packet_ins().empty()) {
return gutil::UnimplementedErrorBuilder()
<< "TODO: support vectors expecting `packet_ins` "
"(punting)";
}
// Legitimize forwarded output packets.
for (int i = 0; i < output.packets().size(); ++i) {
Packet& output_packet = *output.mutable_packets(i);
ASSIGN_OR_RETURN(
int output_tag, ExtractTestPacketTag(output_packet.parsed()),
_.SetPrepend() << "output packet #" << (i + 1) << " invalid: ");
ASSIGN_OR_RETURN(output_packet, LegitimizePacket(output_packet),
_.SetPrepend()
<< "output packet #" << (i + 1) << " invalid: ");
if (output_tag != tag) {
return gutil::InvalidArgumentErrorBuilder()
<< "mismatch of input packet tag vs output packet tag for "
"output packet #"
<< (i + 1) << ": " << tag << " vs " << output_tag;
}
}
}

// Add internalized vector to result.
const auto& [it, inserted] =
legitimized_test_vectors_by_id.insert({tag, vector});
if (!inserted) {
return gutil::InvalidArgumentErrorBuilder()
<< "user-provided packet test vectors must be tagged with unique "
"IDs in their payload, but found multiple test vectors with ID "
<< tag << ". Dumping offending test vectors:\n<"
<< gutil::PrintTextProto(it->second) << ">\n<"
<< gutil::PrintTextProto(vector) << ">\n";
}
return absl::OkStatus();
}

} // namespace

absl::StatusOr<PacketTestVectorById> LegitimizeUserProvidedTestVectors(
absl::Span<const PacketTestVector> user_provided_test_vectors) {
PacketTestVectorById legitimized_test_vectors_by_id;
for (const PacketTestVector& vector : user_provided_test_vectors) {
absl::Status status =
LegitimizeTestVector(vector, legitimized_test_vectors_by_id);
if (!status.ok()) {
return gutil::StatusBuilder(status.code())
<< "problem in user-provided packet test vector: "
<< status.message() << "\nDumping offending test vector:\n"
<< gutil::PrintTextProto(vector);
}
}
return legitimized_test_vectors_by_id;
}

} // namespace dvaas
59 changes: 59 additions & 0 deletions dvaas/user_provided_packet_test_vector.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
// Empowers users to specify custom packet test vectors that can be validated
// by DVaaS or Arriba.

// Copyright 2024 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

#ifndef PINS_DVAAS_USER_PROVIDED_PACKET_TEST_VECTOR_H_
#define PINS_DVAAS_USER_PROVIDED_PACKET_TEST_VECTOR_H_

#include <vector>

#include "absl/status/statusor.h"
#include "absl/types/span.h"
#include "dvaas/test_vector.h"
#include "dvaas/test_vector.pb.h"

namespace dvaas {

// Checks user-provided test vectors for well-formedness and prepares them for
// internal use by DVaaS/Arriba:
// * Fills in "omittable fields", see definition below.
// * Checks that each test vector is "well-formed", see definition below.
// * Returns updated, well-formed test vectors organized by ID, or returns an
// actionable, user-facing error if a test vector is not well-formed.
//
// The following `dvaas::Packet` message fields can be omitted by the user:
// * All `hex` fields.
// * All subfields of `packetlib::Packet` messages that are considered "computed
// fields" by packetlib. This includes checksum and length fields. See the
// packetlib library for the exact definition.
//
// To be "well-formed", a test vector must meet the following requirements:
// * Must specify at least 1 acceptable output.
// * Each input and output packet must:
// * Be valid according to `packetlib::ValidatePacket` after computed fields
// have been filled in.
// * Contain a test packet ID/tag according to `ExtractTestPacketTag`.
// This ID must be:
// * Shared among all packets within the test vector.
// * Unique among all test vectors.
// * The input must be of type `DATAPLANE` (other types may be supported in the
// future).
absl::StatusOr<PacketTestVectorById> LegitimizeUserProvidedTestVectors(
absl::Span<const PacketTestVector> user_provided_test_vectors);

} // namespace dvaas

#endif // PINS_DVAAS_USER_PROVIDED_PACKET_TEST_VECTOR_H_
Loading

0 comments on commit bb8e983

Please sign in to comment.