Skip to content

Commit efa4a3b

Browse files
P4 Infra Teamcopybara-github
authored andcommitted
[Modular group programming] Support checking requirements from parsed annotations.
PiperOrigin-RevId: 954726306
1 parent f107159 commit efa4a3b

7 files changed

Lines changed: 381 additions & 24 deletions

‎p4_pdpi/BUILD.bazel‎

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -548,6 +548,16 @@ cc_library(
548548
deps = ["@abseil-cpp//absl/strings:str_format"],
549549
)
550550

551+
proto_library(
552+
name = "wcmp_group_mode_proto",
553+
srcs = ["wcmp_group_mode.proto"],
554+
)
555+
556+
cc_proto_library(
557+
name = "wcmp_group_mode_cc_proto",
558+
deps = [":wcmp_group_mode_proto"],
559+
)
560+
551561
cc_library(
552562
name = "annotation_parser",
553563
srcs = [
@@ -557,9 +567,13 @@ cc_library(
557567
"annotation_parser.h",
558568
],
559569
deps = [
570+
":wcmp_group_mode_cc_proto",
560571
"@abseil-cpp//absl/status:statusor",
561572
"@abseil-cpp//absl/strings",
573+
"@gutil//gutil:proto",
562574
"@gutil//gutil:status",
575+
"@p4runtime//proto/p4/config/v1:p4info_cc_proto",
576+
"@p4runtime//proto/p4/config/v1:p4types_cc_proto",
563577
],
564578
)
565579

@@ -573,6 +587,7 @@ cc_test(
573587
"@abseil-cpp//absl/strings",
574588
"@googletest//:gtest_main",
575589
"@gutil//gutil:status_matchers",
590+
"@p4runtime//proto/p4/config/v1:p4types_cc_proto",
576591
],
577592
)
578593

‎p4_pdpi/annotation_parser.cc‎

Lines changed: 142 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,9 +20,13 @@
2020
#include <vector>
2121

2222
#include "absl/status/statusor.h"
23+
#include "absl/strings/ascii.h"
24+
#include "absl/strings/numbers.h"
2325
#include "absl/strings/str_replace.h"
2426
#include "absl/strings/str_split.h"
27+
#include "gutil/proto.h"
2528
#include "gutil/status.h"
29+
#include "p4/config/v1/p4types.pb.h"
2630

2731
namespace pdpi {
2832
namespace {
@@ -175,4 +179,142 @@ absl::StatusOr<std::vector<std::string>> ParseAsArgList(std::string value) {
175179
}
176180

177181
} // namespace annotation
182+
183+
absl::StatusOr<std::vector<WcmpGroupMode>> ParseRequiredModesFromActionProfile(
184+
const p4::config::v1::ActionProfile& action_profile) {
185+
std::vector<WcmpGroupMode> modes;
186+
187+
auto clean_token = [](absl::string_view s) -> std::string {
188+
s = absl::StripAsciiWhitespace(s);
189+
if (s.size() >= 2 && s.front() == '"' && s.back() == '"') {
190+
s = s.substr(1, s.size() - 2);
191+
s = absl::StripAsciiWhitespace(s);
192+
}
193+
return std::string(s);
194+
};
195+
196+
auto parse_kv_pairs = [&](const p4::config::v1::KeyValuePairList& kv_list) {
197+
WcmpGroupMode mode;
198+
for (const auto& kv : kv_list.kv_pairs()) {
199+
if (kv.key() == "action_selection_mode") {
200+
std::string val = clean_token(kv.value().string_value());
201+
if (val == "HASH" || val == "hash") {
202+
mode.set_action_selection_mode(WcmpGroupMode::HASH);
203+
} else if (val == "RANDOM" || val == "random") {
204+
mode.set_action_selection_mode(WcmpGroupMode::RANDOM);
205+
} else if (val == "DEFAULT_MODE_DETERMINED_BY_ACTION_SELECTOR" ||
206+
val == "default_mode_determined_by_action_selector") {
207+
mode.set_action_selection_mode(
208+
WcmpGroupMode::DEFAULT_MODE_DETERMINED_BY_ACTION_SELECTOR);
209+
}
210+
} else if (kv.key() == "size_semantics") {
211+
std::string val = clean_token(kv.value().string_value());
212+
if (val == "sum_of_weights" || val == "SUM_OF_WEIGHTS") {
213+
mode.set_size_semantics(WcmpGroupMode::SUM_OF_WEIGHTS);
214+
} else if (val == "sum_of_members" || val == "SUM_OF_MEMBERS") {
215+
mode.set_size_semantics(WcmpGroupMode::SUM_OF_MEMBERS);
216+
} else if (val == "DEFAULT_SIZE_DETERMINED_BY_ACTION_SELECTOR" ||
217+
val == "default_size_determined_by_action_selector") {
218+
mode.set_size_semantics(
219+
WcmpGroupMode::DEFAULT_SIZE_DETERMINED_BY_ACTION_SELECTOR);
220+
}
221+
} else if (kv.key() == "member_multiplier") {
222+
if (kv.value().has_int64_value()) {
223+
mode.set_member_multiplier(kv.value().int64_value());
224+
}
225+
} else if (kv.key() == "max_member_weight") {
226+
if (kv.value().has_int64_value()) {
227+
mode.set_max_member_weight(kv.value().int64_value());
228+
}
229+
}
230+
}
231+
modes.push_back(mode);
232+
};
233+
234+
auto parse_mode_args = [&](const std::vector<std::string>& args) {
235+
if (args.size() < 3) return;
236+
WcmpGroupMode mode;
237+
238+
std::string action_selection_mode = clean_token(args[0]);
239+
if (action_selection_mode == "HASH" || action_selection_mode == "hash") {
240+
mode.set_action_selection_mode(WcmpGroupMode::HASH);
241+
} else if (action_selection_mode == "RANDOM" ||
242+
action_selection_mode == "random") {
243+
mode.set_action_selection_mode(WcmpGroupMode::RANDOM);
244+
} else if (action_selection_mode ==
245+
"DEFAULT_MODE_DETERMINED_BY_ACTION_SELECTOR" ||
246+
action_selection_mode ==
247+
"default_mode_determined_by_action_selector") {
248+
mode.set_action_selection_mode(
249+
WcmpGroupMode::DEFAULT_MODE_DETERMINED_BY_ACTION_SELECTOR);
250+
}
251+
252+
std::vector<std::string> size_semantics_parts =
253+
absl::StrSplit(clean_token(args[1]), absl::MaxSplits(':', 1));
254+
if (!size_semantics_parts.empty()) {
255+
std::string size_semantics = clean_token(size_semantics_parts[0]);
256+
if (size_semantics == "sum_of_weights" ||
257+
size_semantics == "SUM_OF_WEIGHTS") {
258+
mode.set_size_semantics(WcmpGroupMode::SUM_OF_WEIGHTS);
259+
} else if (size_semantics == "sum_of_members" ||
260+
size_semantics == "SUM_OF_MEMBERS") {
261+
mode.set_size_semantics(WcmpGroupMode::SUM_OF_MEMBERS);
262+
} else if (size_semantics ==
263+
"DEFAULT_SIZE_DETERMINED_BY_ACTION_SELECTOR" ||
264+
size_semantics ==
265+
"default_size_determined_by_action_selector") {
266+
mode.set_size_semantics(
267+
WcmpGroupMode::DEFAULT_SIZE_DETERMINED_BY_ACTION_SELECTOR);
268+
}
269+
if (size_semantics_parts.size() > 1 &&
270+
mode.size_semantics() == WcmpGroupMode::SUM_OF_MEMBERS) {
271+
int64_t val;
272+
if (absl::SimpleAtoi(clean_token(size_semantics_parts[1]), &val)) {
273+
mode.set_member_multiplier(val);
274+
}
275+
}
276+
}
277+
278+
int64_t val;
279+
if (absl::SimpleAtoi(clean_token(args[2]), &val)) {
280+
if (mode.size_semantics() == WcmpGroupMode::SUM_OF_MEMBERS) {
281+
mode.set_max_member_weight(val);
282+
} else if (mode.size_semantics() == WcmpGroupMode::SUM_OF_WEIGHTS) {
283+
mode.set_member_multiplier(val);
284+
}
285+
}
286+
287+
modes.push_back(mode);
288+
};
289+
290+
for (const auto& sa : action_profile.preamble().structured_annotations()) {
291+
if (sa.name() != "required_modes") continue;
292+
if (sa.has_kv_pair_list()) {
293+
parse_kv_pairs(sa.kv_pair_list());
294+
} else if (sa.has_expression_list()) {
295+
std::vector<std::string> args;
296+
for (const auto& expr : sa.expression_list().expressions()) {
297+
args.push_back(expr.string_value());
298+
}
299+
parse_mode_args(args);
300+
}
301+
}
302+
303+
std::vector<WcmpGroupMode> deduplicated_modes;
304+
for (const auto& mode : modes) {
305+
bool exists = false;
306+
for (const auto& existing : deduplicated_modes) {
307+
if (google::protobuf::util::MessageDifferencer::Equals(mode, existing)) {
308+
exists = true;
309+
break;
310+
}
311+
}
312+
if (!exists) {
313+
deduplicated_modes.push_back(mode);
314+
}
315+
}
316+
317+
return deduplicated_modes;
318+
}
319+
178320
} // namespace pdpi

‎p4_pdpi/annotation_parser.h‎

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,8 @@
2323
#include "absl/status/statusor.h"
2424
#include "absl/strings/string_view.h"
2525
#include "gutil/status.h"
26+
#include "p4/config/v1/p4info.pb.h"
27+
#include "p4_pdpi/wcmp_group_mode.pb.h"
2628

2729
// This header file defines libraries for parsing P4 annotations.
2830
//
@@ -176,6 +178,11 @@ absl::StatusOr<std::vector<std::string>> GetAllAnnotationBodies(
176178
annotation::Raw);
177179
}
178180

181+
// Parses switch-specific required WCMP modes from `@required_modes(...)`
182+
// or `structured_annotations` in the ActionProfile's preamble.
183+
absl::StatusOr<std::vector<WcmpGroupMode>> ParseRequiredModesFromActionProfile(
184+
const p4::config::v1::ActionProfile& action_profile);
185+
179186
} // namespace pdpi
180187

181188
#endif // P4_INFRA_P4_PDPI_ANNOTATION_PARSER_H_

‎p4_pdpi/annotation_parser_test.cc‎

Lines changed: 93 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@
2929
#include "gmock/gmock.h"
3030
#include "gtest/gtest.h"
3131
#include "gutil/status_matchers.h"
32+
#include "p4/config/v1/p4types.pb.h"
3233

3334
namespace pdpi {
3435
namespace annotation {
@@ -543,4 +544,96 @@ INSTANTIATE_TEST_SUITE_P(
543544

544545
} // namespace
545546
} // namespace annotation
547+
548+
TEST(ParseRequiredModesFromActionProfileTest,
549+
ParsesStructuredAnnotationKeyValuePairList) {
550+
p4::config::v1::ActionProfile ap;
551+
auto* sa = ap.mutable_preamble()->add_structured_annotations();
552+
sa->set_name("required_modes");
553+
auto* kv_list = sa->mutable_kv_pair_list();
554+
555+
auto* kv1 = kv_list->add_kv_pairs();
556+
kv1->set_key("action_selection_mode");
557+
kv1->mutable_value()->set_string_value("HASH");
558+
559+
auto* kv2 = kv_list->add_kv_pairs();
560+
kv2->set_key("size_semantics");
561+
kv2->mutable_value()->set_string_value("sum_of_weights");
562+
563+
auto* kv3 = kv_list->add_kv_pairs();
564+
kv3->set_key("member_multiplier");
565+
kv3->mutable_value()->set_int64_value(1);
566+
567+
auto* kv4 = kv_list->add_kv_pairs();
568+
kv4->set_key("max_member_weight");
569+
kv4->mutable_value()->set_int64_value(0);
570+
571+
ASSERT_OK_AND_ASSIGN(std::vector<WcmpGroupMode> modes,
572+
ParseRequiredModesFromActionProfile(ap));
573+
574+
ASSERT_EQ(modes.size(), 1);
575+
EXPECT_EQ(modes[0].action_selection_mode(), WcmpGroupMode::HASH);
576+
EXPECT_EQ(modes[0].size_semantics(), WcmpGroupMode::SUM_OF_WEIGHTS);
577+
EXPECT_EQ(modes[0].member_multiplier(), 1);
578+
EXPECT_EQ(modes[0].max_member_weight(), 0);
579+
}
580+
581+
TEST(ParseRequiredModesFromActionProfileTest,
582+
ParsesStructuredAnnotationExpressionList) {
583+
p4::config::v1::ActionProfile ap;
584+
auto* sa = ap.mutable_preamble()->add_structured_annotations();
585+
sa->set_name("required_modes");
586+
auto* expr_list = sa->mutable_expression_list();
587+
588+
expr_list->add_expressions()->set_string_value("HASH");
589+
expr_list->add_expressions()->set_string_value("sum_of_members: 1");
590+
expr_list->add_expressions()->set_string_value("4095");
591+
592+
ASSERT_OK_AND_ASSIGN(std::vector<WcmpGroupMode> modes,
593+
ParseRequiredModesFromActionProfile(ap));
594+
595+
ASSERT_EQ(modes.size(), 1);
596+
EXPECT_EQ(modes[0].action_selection_mode(), WcmpGroupMode::HASH);
597+
EXPECT_EQ(modes[0].size_semantics(), WcmpGroupMode::SUM_OF_MEMBERS);
598+
EXPECT_EQ(modes[0].member_multiplier(), 1);
599+
EXPECT_EQ(modes[0].max_member_weight(), 4095);
600+
}
601+
602+
TEST(ParseRequiredModesFromActionProfileTest,
603+
DeduplicatesStructuredAnnotations) {
604+
p4::config::v1::ActionProfile ap;
605+
606+
auto add_structured_mode = [&](const std::string& mode_str,
607+
const std::string& size_str, int mult,
608+
int weight) {
609+
auto* sa = ap.mutable_preamble()->add_structured_annotations();
610+
sa->set_name("required_modes");
611+
auto* kv_list = sa->mutable_kv_pair_list();
612+
613+
auto* kv1 = kv_list->add_kv_pairs();
614+
kv1->set_key("action_selection_mode");
615+
kv1->mutable_value()->set_string_value(mode_str);
616+
617+
auto* kv2 = kv_list->add_kv_pairs();
618+
kv2->set_key("size_semantics");
619+
kv2->mutable_value()->set_string_value(size_str);
620+
621+
auto* kv3 = kv_list->add_kv_pairs();
622+
kv3->set_key("member_multiplier");
623+
kv3->mutable_value()->set_int64_value(mult);
624+
625+
auto* kv4 = kv_list->add_kv_pairs();
626+
kv4->set_key("max_member_weight");
627+
kv4->mutable_value()->set_int64_value(weight);
628+
};
629+
630+
add_structured_mode("HASH", "sum_of_weights", 1, 0);
631+
add_structured_mode("HASH", "sum_of_weights", 1, 0);
632+
633+
ASSERT_OK_AND_ASSIGN(std::vector<WcmpGroupMode> modes,
634+
ParseRequiredModesFromActionProfile(ap));
635+
636+
EXPECT_EQ(modes.size(), 1);
637+
}
638+
546639
} // namespace pdpi

0 commit comments

Comments
 (0)