Skip to content

Commit 131988a

Browse files
NOS-6643..NOS-6648: fboss2 bgp policy as-path-list entry subcommand
1 parent b43dc70 commit 131988a

9 files changed

Lines changed: 672 additions & 0 deletions

File tree

cmake/CliFboss2.cmake

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -885,6 +885,8 @@ add_library(fboss2_config_lib
885885
fboss/cli/fboss2/commands/config/protocol/bgp/policy/as-path-list/BgpAsPathListCliUtils.h
886886
fboss/cli/fboss2/commands/config/protocol/bgp/policy/as-path-list/CmdConfigProtocolBgpPolicyAsPathList.cpp
887887
fboss/cli/fboss2/commands/config/protocol/bgp/policy/as-path-list/CmdConfigProtocolBgpPolicyAsPathList.h
888+
fboss/cli/fboss2/commands/config/protocol/bgp/policy/as-path-list/entry/CmdConfigProtocolBgpPolicyAsPathListEntry.cpp
889+
fboss/cli/fboss2/commands/config/protocol/bgp/policy/as-path-list/entry/CmdConfigProtocolBgpPolicyAsPathListEntry.h
888890
fboss/cli/fboss2/commands/config/ptp/CmdConfigPtp.cpp
889891
fboss/cli/fboss2/commands/config/ptp/CmdConfigPtp.h
890892
fboss/cli/fboss2/commands/config/ptp/transparent_clock/CmdConfigPtpTransparentClock.cpp

fboss/cli/fboss2/BUCK

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1102,6 +1102,7 @@ cpp_library(
11021102
"commands/config/protocol/bgp/peer-group/CmdConfigProtocolBgpPeerGroup.cpp",
11031103
"commands/config/protocol/bgp/policy/CmdConfigProtocolBgpPolicy.cpp",
11041104
"commands/config/protocol/bgp/policy/as-path-list/CmdConfigProtocolBgpPolicyAsPathList.cpp",
1105+
"commands/config/protocol/bgp/policy/as-path-list/entry/CmdConfigProtocolBgpPolicyAsPathListEntry.cpp",
11051106
"commands/config/protocol/static/CmdConfigProtocolStatic.cpp",
11061107
"commands/config/protocol/static/route/add/CmdConfigProtocolStaticRouteAdd.cpp",
11071108
"commands/config/ptp/CmdConfigPtp.cpp",
@@ -1214,6 +1215,7 @@ cpp_library(
12141215
"commands/config/protocol/bgp/policy/CmdConfigProtocolBgpPolicy.h",
12151216
"commands/config/protocol/bgp/policy/as-path-list/BgpAsPathListCliUtils.h",
12161217
"commands/config/protocol/bgp/policy/as-path-list/CmdConfigProtocolBgpPolicyAsPathList.h",
1218+
"commands/config/protocol/bgp/policy/as-path-list/entry/CmdConfigProtocolBgpPolicyAsPathListEntry.h",
12171219
"commands/config/protocol/static/CmdConfigProtocolStatic.h",
12181220
"commands/config/protocol/static/route/StaticRouteUtils.h",
12191221
"commands/config/protocol/static/route/add/CmdConfigProtocolStaticRouteAdd.h",

fboss/cli/fboss2/CmdListConfig.cpp

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@
4242
#include "fboss/cli/fboss2/commands/config/protocol/bgp/peer-group/CmdConfigProtocolBgpPeerGroup.h"
4343
#include "fboss/cli/fboss2/commands/config/protocol/bgp/policy/CmdConfigProtocolBgpPolicy.h"
4444
#include "fboss/cli/fboss2/commands/config/protocol/bgp/policy/as-path-list/CmdConfigProtocolBgpPolicyAsPathList.h"
45+
#include "fboss/cli/fboss2/commands/config/protocol/bgp/policy/as-path-list/entry/CmdConfigProtocolBgpPolicyAsPathListEntry.h"
4546
#include "fboss/cli/fboss2/commands/config/protocol/static/CmdConfigProtocolStatic.h"
4647
#include "fboss/cli/fboss2/commands/config/protocol/static/route/add/CmdConfigProtocolStaticRouteAdd.h"
4748
#include "fboss/cli/fboss2/commands/config/ptp/CmdConfigPtp.h"
@@ -395,6 +396,16 @@ const CommandTree& kConfigCommandTree() {
395396
CmdConfigProtocolBgpPolicyAsPathList>,
396397
argRegistrar<
397398
CmdConfigProtocolBgpPolicyAsPathListTraits>,
399+
{{
400+
"entry",
401+
"Configure an AS-path list entry: "
402+
"<seq-num> [<attribute> <value> ...] "
403+
"(asn-regexp, description, match-logic)",
404+
commandHandler<
405+
CmdConfigProtocolBgpPolicyAsPathListEntry>,
406+
argRegistrar<
407+
CmdConfigProtocolBgpPolicyAsPathListEntryTraits>,
408+
}},
398409
}},
399410
},
400411
},
Lines changed: 229 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,229 @@
1+
/*
2+
* Copyright (c) 2004-present, Facebook, Inc.
3+
* All rights reserved.
4+
*
5+
* This source code is licensed under the BSD-style license found in the
6+
* LICENSE file in the root directory of this source tree. An additional grant
7+
* of patent rights can be found in the PATENTS file in the same directory.
8+
*
9+
*/
10+
11+
#include "fboss/cli/fboss2/commands/config/protocol/bgp/policy/as-path-list/entry/CmdConfigProtocolBgpPolicyAsPathListEntry.h"
12+
13+
#include "fboss/cli/fboss2/CmdHandler.cpp"
14+
15+
#include <fmt/core.h>
16+
#include <neteng/fboss/bgp/public_tld/configerator/structs/neteng/fboss/bgp/gen-cpp2/bgp_config_types.h>
17+
#include <iostream>
18+
#include <map>
19+
#include <optional>
20+
#include <ostream>
21+
#include <stdexcept>
22+
#include <string>
23+
#include <string_view>
24+
#include <vector>
25+
#include "configerator/structs/neteng/bgp_policy/thrift/gen-cpp2/bgp_policy_types.h"
26+
#include "configerator/structs/neteng/bgp_policy/thrift/gen-cpp2/routing_policy_types.h"
27+
#include "fboss/cli/fboss2/commands/config/protocol/bgp/BgpCliAttrHandlers.h"
28+
#include "fboss/cli/fboss2/commands/config/protocol/bgp/BgpCliValueParsers.h"
29+
#include "fboss/cli/fboss2/commands/config/protocol/bgp/policy/as-path-list/BgpAsPathListCliUtils.h"
30+
#include "fboss/cli/fboss2/session/ConfigSession.h"
31+
#include "fboss/cli/fboss2/utils/CmdUtilsCommon.h"
32+
#include "fboss/cli/fboss2/utils/HostInfo.h"
33+
#include "fmt/format.h"
34+
35+
namespace facebook::fboss {
36+
37+
namespace {
38+
39+
// The attribute names, exactly as documented. Kept here so the
40+
// valid-attribute set and the handler table stay in sync.
41+
constexpr std::string_view kDescription = "description";
42+
constexpr std::string_view kAsnRegexp = "asn-regexp";
43+
constexpr std::string_view kMatchLogic = "match-logic";
44+
45+
// match-logic values (routing_policy.MatchValueLogicOperator names).
46+
constexpr std::string_view kMatchLogicEqual = "EQUAL";
47+
constexpr std::string_view kMatchLogicNotEqual = "NOT_EQUAL";
48+
49+
using AsPathListEntry = bgp::bgp_policy::AsPathListEntry;
50+
using MatchValueLogicOperator = bgp::routing_policy::MatchValueLogicOperator;
51+
using bgpcli::AttrHandler;
52+
using bgpcli::enumAttr;
53+
using bgpcli::joinedStringAttr;
54+
using bgpcli::ok;
55+
using bgpcli::Result;
56+
57+
// ---- value lookups ----------------------------------------------------------
58+
59+
std::optional<MatchValueLogicOperator> lookupMatchLogic(const std::string& s) {
60+
if (s == kMatchLogicEqual) {
61+
return MatchValueLogicOperator::EQUAL;
62+
}
63+
if (s == kMatchLogicNotEqual) {
64+
return MatchValueLogicOperator::NOT_EQUAL;
65+
}
66+
return std::nullopt;
67+
}
68+
69+
// ---- entry-level setters ----------------------------------------------------
70+
// Each writes one already-parsed, already-validated value. Parsing and message
71+
// text belong to the shared factories in BgpCliAttrHandlers.h.
72+
73+
void setDescription(AsPathListEntry& entry, const std::string& description) {
74+
entry.description() = description;
75+
}
76+
77+
// The AS path is modelled as an AsPathType union; the CLI sets the inline
78+
// `as_path` (AsPath) arm and its regexp, matching the thrift path
79+
// .as_path.as_path.asn_regexp.
80+
void setAsnRegexp(AsPathListEntry& entry, const std::string& regexp) {
81+
entry.as_path()->set_as_path().asn_regexp() = regexp;
82+
}
83+
84+
void setMatchLogic(AsPathListEntry& entry, MatchValueLogicOperator op) {
85+
entry.match_logic_type() = op;
86+
}
87+
88+
// ---- entry-level attribute registry -----------------------------------------
89+
// One line per documented attribute: its dispatch key, its value shape, and
90+
// the setter that stores it. asn-regexp is a joined string because an AS-path
91+
// regex separates ASNs with spaces (e.g. `^65000 65001$`), so its tokens must
92+
// be re-joined rather than required to be single.
93+
const std::map<std::string, AttrHandler<AsPathListEntry>, std::less<>>&
94+
entryAttrHandlers() {
95+
static const std::string kMatchLogicValues =
96+
fmt::format("{}|{}", kMatchLogicEqual, kMatchLogicNotEqual);
97+
static const std::map<std::string, AttrHandler<AsPathListEntry>, std::less<>>
98+
kHandlers = {
99+
{std::string(kAsnRegexp),
100+
joinedStringAttr<AsPathListEntry>(kAsnRegexp, setAsnRegexp)},
101+
{std::string(kDescription),
102+
joinedStringAttr<AsPathListEntry>(kDescription, setDescription)},
103+
{std::string(kMatchLogic),
104+
enumAttr<AsPathListEntry, MatchValueLogicOperator>(
105+
kMatchLogic,
106+
kMatchLogicValues,
107+
lookupMatchLogic,
108+
setMatchLogic)},
109+
};
110+
return kHandlers;
111+
}
112+
113+
std::string validAttrList() {
114+
std::string out;
115+
for (const auto& [name, _] : entryAttrHandlers()) {
116+
if (!out.empty()) {
117+
out += ", ";
118+
}
119+
out += name;
120+
}
121+
return out;
122+
}
123+
124+
} // namespace
125+
126+
// Parse + validate at construction so queryClient stays a thin dispatch.
127+
// Throwing std::invalid_argument is how the framework surfaces arg parse
128+
// errors (same mechanism as BgpAsPathListConfig).
129+
BgpAsPathListEntryConfig::BgpAsPathListEntryConfig(std::vector<std::string> v)
130+
: utils::BaseObjectArgType<std::string>(v) {
131+
if (v.empty()) {
132+
throw std::invalid_argument(
133+
"Error: entry <seq-num> is required, optionally followed by an "
134+
"<attribute> <value>");
135+
}
136+
auto seq = bgpcli::parseNonNegInt64(v[0]);
137+
if (!seq) {
138+
throw std::invalid_argument(
139+
fmt::format(
140+
"Error: entry <seq-num> must be a non-negative integer, got '{}'",
141+
v[0]));
142+
}
143+
seqNum_ = *seq;
144+
if (v.size() == 1) {
145+
return; // bare `entry <seq-num>`: create it
146+
}
147+
148+
attr_ = v[1];
149+
values_.assign(v.begin() + 2, v.end());
150+
151+
if (entryAttrHandlers().find(attr_) == entryAttrHandlers().end()) {
152+
throw std::invalid_argument(
153+
fmt::format(
154+
"Error: unknown as-path-list entry attribute '{}'. Valid "
155+
"attributes: {}",
156+
attr_,
157+
validAttrList()));
158+
}
159+
}
160+
161+
CmdConfigProtocolBgpPolicyAsPathListEntryTraits::RetType
162+
CmdConfigProtocolBgpPolicyAsPathListEntry::queryClient(
163+
const HostInfo& /* hostInfo */,
164+
const BgpAsPathListConfig& listArgs,
165+
const ObjectArgType& args) {
166+
// The parent parse accepts `as-path-list <name> <attr> <value> ... entry
167+
// ...`, but only the leaf (this command) runs — silently dropping the
168+
// list-level attribute would look like it was staged. Reject the mix.
169+
if (!listArgs.attr().empty()) {
170+
return fmt::format(
171+
"Error: configure as-path-list attributes and entry in separate "
172+
"commands (got as-path-list attribute '{}' alongside entry {})",
173+
listArgs.attr(),
174+
args.seqNum());
175+
}
176+
177+
auto& session = ConfigSession::getInstance();
178+
auto& cfg = session.getBgpConfig();
179+
const bool listCreated = !bgpcli::asPathListExists(cfg, listArgs.listName());
180+
auto& list = bgpcli::findOrCreateAsPathList(cfg, listArgs.listName());
181+
const bool entryCreated = !bgpcli::asPathListEntryExists(list, args.seqNum());
182+
auto& entry = bgpcli::findOrCreateAsPathListEntry(list, args.seqNum());
183+
184+
Result result = args.attr().empty()
185+
? ok(entryCreated
186+
? fmt::format(
187+
"Successfully created BGP as-path-list {} entry {}",
188+
listArgs.listName(),
189+
args.seqNum())
190+
: fmt::format(
191+
"BGP as-path-list {} entry {} already exists",
192+
listArgs.listName(),
193+
args.seqNum()))
194+
// The attribute is guaranteed valid: BgpAsPathListEntryConfig's
195+
// constructor rejects an unknown attribute before we get here.
196+
: entryAttrHandlers().find(args.attr())->second(entry, args.values());
197+
198+
if (result.ok) {
199+
if (!args.attr().empty()) {
200+
result.message += fmt::format(
201+
" for as-path-list {} entry {}", listArgs.listName(), args.seqNum());
202+
}
203+
session.saveBgpConfig();
204+
result.message +=
205+
fmt::format("\nConfig saved to: {}", session.getBgpSessionConfigPath());
206+
} else {
207+
// A rejected value must not leave a phantom entry (or a phantom list
208+
// implicitly created for it) visible to later lookups in this process.
209+
if (entryCreated) {
210+
list.as_path_list()->pop_back();
211+
}
212+
if (listCreated) {
213+
cfg.policies()->aspath_lists()->pop_back();
214+
}
215+
}
216+
return result.message;
217+
}
218+
219+
void CmdConfigProtocolBgpPolicyAsPathListEntry::printOutput(
220+
const RetType& output) {
221+
std::cout << output << std::endl;
222+
}
223+
224+
// Explicit template instantiation
225+
template void CmdHandler<
226+
CmdConfigProtocolBgpPolicyAsPathListEntry,
227+
CmdConfigProtocolBgpPolicyAsPathListEntryTraits>::run();
228+
229+
} // namespace facebook::fboss
Lines changed: 89 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,89 @@
1+
/*
2+
* Copyright (c) 2004-present, Facebook, Inc.
3+
* All rights reserved.
4+
*
5+
* This source code is licensed under the BSD-style license found in the
6+
* LICENSE file in the root directory of this source tree. An additional grant
7+
* of patent rights can be found in the PATENTS file in the same directory.
8+
*
9+
*/
10+
11+
#pragma once
12+
13+
#include <cstdint>
14+
#include <string>
15+
#include <vector>
16+
#include "CLI/App.hpp"
17+
#include "fboss/cli/fboss2/CmdHandler.h"
18+
#include "fboss/cli/fboss2/commands/config/protocol/bgp/policy/as-path-list/CmdConfigProtocolBgpPolicyAsPathList.h"
19+
#include "fboss/cli/fboss2/utils/CmdUtilsCommon.h"
20+
#include "fboss/cli/fboss2/utils/HostInfo.h"
21+
22+
namespace facebook::fboss {
23+
24+
// Parsed `entry <seq-num> [<attribute> <value> ...]`, validated at
25+
// construction. An entry (bgp_policy.AsPathListEntry in
26+
// AsPathList.as_path_list[]) is keyed by <seq-num>; the list it belongs to is
27+
// supplied by the parent command's args.
28+
//
29+
// Grammar (from the FBOSS proposed syntax):
30+
// ... as-path-list <name> entry <seq-num> (create/select)
31+
// ... as-path-list <name> entry <seq-num> asn-regexp <re>
32+
// ... as-path-list <name> entry <seq-num> description <string>
33+
// ... as-path-list <name> entry <seq-num> match-logic <op>
34+
class BgpAsPathListEntryConfig : public utils::BaseObjectArgType<std::string> {
35+
public:
36+
// NOLINTNEXTLINE(google-explicit-constructor)
37+
/* implicit */ BgpAsPathListEntryConfig(std::vector<std::string> v);
38+
int64_t seqNum() const {
39+
return seqNum_;
40+
}
41+
const std::string& attr() const {
42+
return attr_;
43+
}
44+
const std::vector<std::string>& values() const {
45+
return values_;
46+
}
47+
const static utils::ObjectArgTypeId id =
48+
utils::ObjectArgTypeId::OBJECT_ARG_TYPE_ID_MESSAGE;
49+
50+
private:
51+
int64_t seqNum_{0};
52+
std::string attr_; // matched dispatch key ("" = bare create)
53+
std::vector<std::string> values_;
54+
};
55+
56+
// The entry level of the as-path-list family as its own CLI11 subcommand; the
57+
// parent's parsed args arrive through the ancestor-args tuple, mirroring
58+
// CmdConfigProtocolBgpPolicyRoutingPolicyTerm.
59+
struct CmdConfigProtocolBgpPolicyAsPathListEntryTraits
60+
: public WriteCommandTraits {
61+
using ParentCmd = CmdConfigProtocolBgpPolicyAsPathList;
62+
static void addCliArg(CLI::App& cmd, std::vector<std::string>& args) {
63+
// Entry has no nested subcommands; stop CLI11's parent-chain fallthrough
64+
// from stealing a value token that spells `entry` (e.g. in a description).
65+
cmd.positionals_at_end();
66+
cmd.add_option("args", args, "<seq-num> [<attribute> <value> ...]");
67+
}
68+
using ObjectArgType = BgpAsPathListEntryConfig;
69+
using RetType = std::string;
70+
};
71+
72+
class CmdConfigProtocolBgpPolicyAsPathListEntry
73+
: public CmdHandler<
74+
CmdConfigProtocolBgpPolicyAsPathListEntry,
75+
CmdConfigProtocolBgpPolicyAsPathListEntryTraits> {
76+
public:
77+
using ObjectArgType =
78+
CmdConfigProtocolBgpPolicyAsPathListEntryTraits::ObjectArgType;
79+
using RetType = CmdConfigProtocolBgpPolicyAsPathListEntryTraits::RetType;
80+
81+
RetType queryClient(
82+
const HostInfo& hostInfo,
83+
const BgpAsPathListConfig& listArgs,
84+
const ObjectArgType& args);
85+
86+
void printOutput(const RetType& output);
87+
};
88+
89+
} // namespace facebook::fboss

fboss/cli/fboss2/test/config/BUCK

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ cpp_unittest(
1313
"CmdConfigArpTest.cpp",
1414
"CmdConfigBgpNeighborTest.cpp",
1515
"CmdConfigBgpPeerGroupTest.cpp",
16+
"CmdConfigBgpPolicyAsPathListEntryTest.cpp",
1617
"CmdConfigBgpPolicyAsPathListTest.cpp",
1718
"CmdConfigCoppTest.cpp",
1819
"CmdConfigDhcpTest.cpp",

0 commit comments

Comments
 (0)