From 3cce3a09cfee9063fe13820b5e1fdc3c06550d8c Mon Sep 17 00:00:00 2001 From: Hillol Chakraborty Date: Thu, 6 Aug 2026 16:16:10 +0000 Subject: [PATCH 1/5] [Nexthop] fboss2 config/delete protocol bgp policy routing-policy MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Adds `config protocol bgp policy routing-policy [ ...]` and `delete protocol bgp policy routing-policy ` — the fourth BGP policy object-type command family, beneath the `policy` grouping node alongside as-path-list, community-list, and prefix-list. - routing-policy dispatcher keyed by policy name writing bgp_policy.BgpPolicies.bgp_policy_statements[] (BgpPolicyStatement) through the typed ConfigSession. Policy level: description (joined seq-num-keyed `term ` level and its match/action attributes land as follow-ups. - delete removes the whole policy statement by name; extra tokens are rejected. - unit tests (11) + commit-path integration tests mirroring ConfigBgpPolicyCommunityListTest, verifying the description lands in bgpd's running config and that bgpd adopts a policy statement with no terms. Co-Authored-By: Claude Fable 5 --- cmake/CliFboss2.cmake | 4 + cmake/CliFboss2TestConfig.cmake | 2 + cmake/CliFboss2TestIntegrationTest.cmake | 1 + fboss/cli/fboss2/BUCK | 4 + fboss/cli/fboss2/CmdListConfig.cpp | 20 ++ .../bgp/policy/CmdConfigProtocolBgpPolicy.h | 2 +- ...mdConfigProtocolBgpPolicyRoutingPolicy.cpp | 195 ++++++++++++++++++ .../CmdConfigProtocolBgpPolicyRoutingPolicy.h | 84 ++++++++ ...mdDeleteProtocolBgpPolicyRoutingPolicy.cpp | 97 +++++++++ .../CmdDeleteProtocolBgpPolicyRoutingPolicy.h | 65 ++++++ fboss/cli/fboss2/test/config/BUCK | 2 + .../CmdConfigBgpPolicyRoutingPolicyTest.cpp | 156 ++++++++++++++ .../CmdDeleteBgpPolicyRoutingPolicyTest.cpp | 120 +++++++++++ fboss/cli/fboss2/test/integration_test/BUCK | 1 + .../ConfigBgpPolicyRoutingPolicyTest.cpp | 121 +++++++++++ 15 files changed, 873 insertions(+), 1 deletion(-) create mode 100644 fboss/cli/fboss2/commands/config/protocol/bgp/policy/routing-policy/CmdConfigProtocolBgpPolicyRoutingPolicy.cpp create mode 100644 fboss/cli/fboss2/commands/config/protocol/bgp/policy/routing-policy/CmdConfigProtocolBgpPolicyRoutingPolicy.h create mode 100644 fboss/cli/fboss2/commands/delete/protocol/bgp/policy/routing-policy/CmdDeleteProtocolBgpPolicyRoutingPolicy.cpp create mode 100644 fboss/cli/fboss2/commands/delete/protocol/bgp/policy/routing-policy/CmdDeleteProtocolBgpPolicyRoutingPolicy.h create mode 100644 fboss/cli/fboss2/test/config/CmdConfigBgpPolicyRoutingPolicyTest.cpp create mode 100644 fboss/cli/fboss2/test/config/CmdDeleteBgpPolicyRoutingPolicyTest.cpp create mode 100644 fboss/cli/fboss2/test/integration_test/ConfigBgpPolicyRoutingPolicyTest.cpp diff --git a/cmake/CliFboss2.cmake b/cmake/CliFboss2.cmake index 05acc9aeb1528..144cda2ecec3a 100644 --- a/cmake/CliFboss2.cmake +++ b/cmake/CliFboss2.cmake @@ -984,6 +984,8 @@ add_library(fboss2_config_lib fboss/cli/fboss2/commands/config/protocol/bgp/policy/prefix-list/CmdConfigProtocolBgpPolicyPrefixList.h fboss/cli/fboss2/commands/config/protocol/bgp/policy/prefix-list/entry/CmdConfigProtocolBgpPolicyPrefixListEntry.cpp fboss/cli/fboss2/commands/config/protocol/bgp/policy/prefix-list/entry/CmdConfigProtocolBgpPolicyPrefixListEntry.h + fboss/cli/fboss2/commands/config/protocol/bgp/policy/routing-policy/CmdConfigProtocolBgpPolicyRoutingPolicy.cpp + fboss/cli/fboss2/commands/config/protocol/bgp/policy/routing-policy/CmdConfigProtocolBgpPolicyRoutingPolicy.h fboss/cli/fboss2/commands/config/ptp/CmdConfigPtp.cpp fboss/cli/fboss2/commands/config/ptp/CmdConfigPtp.h fboss/cli/fboss2/commands/config/ptp/transparent_clock/CmdConfigPtpTransparentClock.cpp @@ -1128,6 +1130,8 @@ add_library(fboss2_config_lib fboss/cli/fboss2/commands/delete/protocol/bgp/policy/community-list/CmdDeleteProtocolBgpPolicyCommunityList.h fboss/cli/fboss2/commands/delete/protocol/bgp/policy/prefix-list/CmdDeleteProtocolBgpPolicyPrefixList.cpp fboss/cli/fboss2/commands/delete/protocol/bgp/policy/prefix-list/CmdDeleteProtocolBgpPolicyPrefixList.h + fboss/cli/fboss2/commands/delete/protocol/bgp/policy/routing-policy/CmdDeleteProtocolBgpPolicyRoutingPolicy.cpp + fboss/cli/fboss2/commands/delete/protocol/bgp/policy/routing-policy/CmdDeleteProtocolBgpPolicyRoutingPolicy.h fboss/cli/fboss2/commands/delete/protocol/CmdDeleteProtocol.h fboss/cli/fboss2/commands/delete/protocol/static/CmdDeleteProtocolStatic.cpp fboss/cli/fboss2/commands/delete/protocol/static/CmdDeleteProtocolStatic.h diff --git a/cmake/CliFboss2TestConfig.cmake b/cmake/CliFboss2TestConfig.cmake index 7ea07c6f8b142..f0a4a4b7ce5cb 100644 --- a/cmake/CliFboss2TestConfig.cmake +++ b/cmake/CliFboss2TestConfig.cmake @@ -18,6 +18,7 @@ add_executable(fboss2_cmd_config_test fboss/cli/fboss2/test/config/CmdConfigBgpPolicyCommunityListTest.cpp fboss/cli/fboss2/test/config/CmdConfigBgpPolicyPrefixListTest.cpp fboss/cli/fboss2/test/config/CmdConfigBgpPolicyPrefixListEntryTest.cpp + fboss/cli/fboss2/test/config/CmdConfigBgpPolicyRoutingPolicyTest.cpp fboss/cli/fboss2/test/config/CmdConfigCoppTest.cpp fboss/cli/fboss2/test/config/CmdConfigDataPlaneTrafficPolicyTest.cpp fboss/cli/fboss2/test/config/CmdConfigDhcpTest.cpp @@ -58,6 +59,7 @@ add_executable(fboss2_cmd_config_test fboss/cli/fboss2/test/config/CmdDeleteBgpPolicyAsPathListTest.cpp fboss/cli/fboss2/test/config/CmdDeleteBgpPolicyCommunityListTest.cpp fboss/cli/fboss2/test/config/CmdDeleteBgpPolicyPrefixListTest.cpp + fboss/cli/fboss2/test/config/CmdDeleteBgpPolicyRoutingPolicyTest.cpp fboss/cli/fboss2/test/config/CmdDeleteConfigInterfaceTest.cpp fboss/cli/fboss2/test/config/CmdDeleteCoppQueueTest.cpp fboss/cli/fboss2/test/config/CmdDeleteCoppReasonTest.cpp diff --git a/cmake/CliFboss2TestIntegrationTest.cmake b/cmake/CliFboss2TestIntegrationTest.cmake index 02d8e1add9da4..b572867cfcc09 100644 --- a/cmake/CliFboss2TestIntegrationTest.cmake +++ b/cmake/CliFboss2TestIntegrationTest.cmake @@ -22,6 +22,7 @@ add_executable(fboss2_integration_test fboss/cli/fboss2/test/integration_test/ConfigBgpPolicyCommunityListTest.cpp fboss/cli/fboss2/test/integration_test/ConfigBgpPolicyPrefixListTest.cpp fboss/cli/fboss2/test/integration_test/ConfigBgpPolicyPrefixListEntryTest.cpp + fboss/cli/fboss2/test/integration_test/ConfigBgpPolicyRoutingPolicyTest.cpp fboss/cli/fboss2/test/integration_test/ConfigBgpSessionTest.cpp fboss/cli/fboss2/test/integration_test/ConfigConcurrentSessionsTest.cpp fboss/cli/fboss2/test/integration_test/ConfigHostnameTest.cpp diff --git a/fboss/cli/fboss2/BUCK b/fboss/cli/fboss2/BUCK index eb83f150b2fa5..4c2d76ad1a09f 100644 --- a/fboss/cli/fboss2/BUCK +++ b/fboss/cli/fboss2/BUCK @@ -1218,6 +1218,7 @@ cpp_library( "commands/config/protocol/bgp/policy/community-list/CmdConfigProtocolBgpPolicyCommunityList.cpp", "commands/config/protocol/bgp/policy/prefix-list/CmdConfigProtocolBgpPolicyPrefixList.cpp", "commands/config/protocol/bgp/policy/prefix-list/entry/CmdConfigProtocolBgpPolicyPrefixListEntry.cpp", + "commands/config/protocol/bgp/policy/routing-policy/CmdConfigProtocolBgpPolicyRoutingPolicy.cpp", "commands/config/protocol/static/CmdConfigProtocolStatic.cpp", "commands/config/protocol/static/route/add/CmdConfigProtocolStaticRouteAdd.cpp", "commands/config/ptp/CmdConfigPtp.cpp", @@ -1289,6 +1290,7 @@ cpp_library( "commands/delete/protocol/bgp/policy/as-path-list/CmdDeleteProtocolBgpPolicyAsPathList.cpp", "commands/delete/protocol/bgp/policy/community-list/CmdDeleteProtocolBgpPolicyCommunityList.cpp", "commands/delete/protocol/bgp/policy/prefix-list/CmdDeleteProtocolBgpPolicyPrefixList.cpp", + "commands/delete/protocol/bgp/policy/routing-policy/CmdDeleteProtocolBgpPolicyRoutingPolicy.cpp", "commands/delete/protocol/static/CmdDeleteProtocolStatic.cpp", "commands/delete/protocol/static/route/CmdDeleteProtocolStaticRoute.cpp", "commands/delete/qos/CmdDeleteQos.cpp", @@ -1370,6 +1372,7 @@ cpp_library( "commands/config/protocol/bgp/policy/prefix-list/BgpPrefixListCliUtils.h", "commands/config/protocol/bgp/policy/prefix-list/CmdConfigProtocolBgpPolicyPrefixList.h", "commands/config/protocol/bgp/policy/prefix-list/entry/CmdConfigProtocolBgpPolicyPrefixListEntry.h", + "commands/config/protocol/bgp/policy/routing-policy/CmdConfigProtocolBgpPolicyRoutingPolicy.h", "commands/config/protocol/static/CmdConfigProtocolStatic.h", "commands/config/protocol/static/route/StaticRouteUtils.h", "commands/config/protocol/static/route/add/CmdConfigProtocolStaticRouteAdd.h", @@ -1442,6 +1445,7 @@ cpp_library( "commands/delete/protocol/bgp/policy/as-path-list/CmdDeleteProtocolBgpPolicyAsPathList.h", "commands/delete/protocol/bgp/policy/community-list/CmdDeleteProtocolBgpPolicyCommunityList.h", "commands/delete/protocol/bgp/policy/prefix-list/CmdDeleteProtocolBgpPolicyPrefixList.h", + "commands/delete/protocol/bgp/policy/routing-policy/CmdDeleteProtocolBgpPolicyRoutingPolicy.h", "commands/delete/protocol/static/CmdDeleteProtocolStatic.h", "commands/delete/protocol/static/route/CmdDeleteProtocolStaticRoute.h", "commands/delete/qos/CmdDeleteQos.h", diff --git a/fboss/cli/fboss2/CmdListConfig.cpp b/fboss/cli/fboss2/CmdListConfig.cpp index 3a1ef56a11529..3c5f21a33e8ac 100644 --- a/fboss/cli/fboss2/CmdListConfig.cpp +++ b/fboss/cli/fboss2/CmdListConfig.cpp @@ -47,6 +47,7 @@ #include "fboss/cli/fboss2/commands/config/protocol/bgp/policy/community-list/CmdConfigProtocolBgpPolicyCommunityList.h" #include "fboss/cli/fboss2/commands/config/protocol/bgp/policy/prefix-list/CmdConfigProtocolBgpPolicyPrefixList.h" #include "fboss/cli/fboss2/commands/config/protocol/bgp/policy/prefix-list/entry/CmdConfigProtocolBgpPolicyPrefixListEntry.h" +#include "fboss/cli/fboss2/commands/config/protocol/bgp/policy/routing-policy/CmdConfigProtocolBgpPolicyRoutingPolicy.h" #include "fboss/cli/fboss2/commands/config/protocol/static/CmdConfigProtocolStatic.h" #include "fboss/cli/fboss2/commands/config/protocol/static/route/add/CmdConfigProtocolStaticRouteAdd.h" #include "fboss/cli/fboss2/commands/config/ptp/CmdConfigPtp.h" @@ -115,6 +116,7 @@ #include "fboss/cli/fboss2/commands/delete/protocol/bgp/policy/as-path-list/CmdDeleteProtocolBgpPolicyAsPathList.h" #include "fboss/cli/fboss2/commands/delete/protocol/bgp/policy/community-list/CmdDeleteProtocolBgpPolicyCommunityList.h" #include "fboss/cli/fboss2/commands/delete/protocol/bgp/policy/prefix-list/CmdDeleteProtocolBgpPolicyPrefixList.h" +#include "fboss/cli/fboss2/commands/delete/protocol/bgp/policy/routing-policy/CmdDeleteProtocolBgpPolicyRoutingPolicy.h" #include "fboss/cli/fboss2/commands/delete/protocol/static/CmdDeleteProtocolStatic.h" #include "fboss/cli/fboss2/commands/delete/protocol/static/route/CmdDeleteProtocolStaticRoute.h" #include "fboss/cli/fboss2/commands/delete/qos/CmdDeleteQos.h" @@ -470,6 +472,16 @@ const CommandTree& kConfigCommandTree() { argRegistrar< CmdConfigProtocolBgpPolicyPrefixListEntryTraits>, }}, + }, + { + "routing-policy", + "Configure BGP routing-policy: " + "[ ...] " + "(description)", + commandHandler< + CmdConfigProtocolBgpPolicyRoutingPolicy>, + argRegistrar< + CmdConfigProtocolBgpPolicyRoutingPolicyTraits>, }}, }, }, @@ -842,6 +854,14 @@ const CommandTree& kConfigCommandTree() { commandHandler, argRegistrar< CmdDeleteProtocolBgpPolicyPrefixListTraits>, + }, + { + "routing-policy", + "Delete a BGP routing-policy: ", + commandHandler< + CmdDeleteProtocolBgpPolicyRoutingPolicy>, + argRegistrar< + CmdDeleteProtocolBgpPolicyRoutingPolicyTraits>, }}, }}, }, diff --git a/fboss/cli/fboss2/commands/config/protocol/bgp/policy/CmdConfigProtocolBgpPolicy.h b/fboss/cli/fboss2/commands/config/protocol/bgp/policy/CmdConfigProtocolBgpPolicy.h index df4e340b6a1ad..fdddf4e063967 100644 --- a/fboss/cli/fboss2/commands/config/protocol/bgp/policy/CmdConfigProtocolBgpPolicy.h +++ b/fboss/cli/fboss2/commands/config/protocol/bgp/policy/CmdConfigProtocolBgpPolicy.h @@ -20,7 +20,7 @@ namespace facebook::fboss { // Grouping node for `config protocol bgp policy ...`. Holds no // state of its own; the per-object-type dispatchers (as-path-list, -// community-list, prefix-list, and later routing-policy) are its subcommands. +// community-list, prefix-list, routing-policy) are its subcommands. // Mirrors CmdConfigProtocolBgp. struct CmdConfigProtocolBgpPolicyTraits : public WriteCommandTraits { using ParentCmd = CmdConfigProtocolBgp; diff --git a/fboss/cli/fboss2/commands/config/protocol/bgp/policy/routing-policy/CmdConfigProtocolBgpPolicyRoutingPolicy.cpp b/fboss/cli/fboss2/commands/config/protocol/bgp/policy/routing-policy/CmdConfigProtocolBgpPolicyRoutingPolicy.cpp new file mode 100644 index 0000000000000..491be24a11c2d --- /dev/null +++ b/fboss/cli/fboss2/commands/config/protocol/bgp/policy/routing-policy/CmdConfigProtocolBgpPolicyRoutingPolicy.cpp @@ -0,0 +1,195 @@ +/* + * Copyright (c) 2004-present, Facebook, Inc. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + * + */ + +#include "fboss/cli/fboss2/commands/config/protocol/bgp/policy/routing-policy/CmdConfigProtocolBgpPolicyRoutingPolicy.h" + +#include "fboss/cli/fboss2/CmdHandler.cpp" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "configerator/structs/neteng/bgp_policy/thrift/gen-cpp2/bgp_policy_types.h" +#include "fboss/cli/fboss2/commands/config/protocol/bgp/BgpCliAttrHandlers.h" +#include "fboss/cli/fboss2/commands/config/protocol/bgp/BgpCliValueParsers.h" +#include "fboss/cli/fboss2/session/ConfigSession.h" +#include "fboss/cli/fboss2/utils/CmdUtilsCommon.h" +#include "fboss/cli/fboss2/utils/HostInfo.h" +#include "fmt/format.h" + +namespace facebook::fboss { + +namespace { + +// The attribute names, exactly as documented. Kept here so the valid-attribute +// set and the handler table stay in sync. +constexpr std::string_view kObjectName = "routing-policy"; +constexpr std::string_view kDescription = "description"; + +using BgpPolicyStatement = bgp::bgp_policy::BgpPolicyStatement; +using bgpcli::AttrHandler; +using bgpcli::joinedStringAttr; +using bgpcli::ok; +using bgpcli::Result; + +// ---- policy-level setters --------------------------------------------------- +// Each writes one already-parsed, already-validated value. Parsing and message +// text belong to the shared factories in BgpCliAttrHandlers.h. + +void setDescription( + BgpPolicyStatement& policy, + const std::string& description) { + policy.description() = description; +} + +// ---- policy-level attribute registry --------------------------------------- +// One line per documented attribute: its dispatch key, its value shape, and +// the setter that stores it. +const std::map, std::less<>>& +policyAttrHandlers() { + static const std:: + map, std::less<>> + kHandlers = { + {std::string(kDescription), + joinedStringAttr( + kDescription, setDescription)}, + }; + return kHandlers; +} + +std::string validAttrList() { + std::string out; + for (const auto& [name, _] : policyAttrHandlers()) { + if (!out.empty()) { + out += ", "; + } + out += name; + } + return out; +} + +// Find the routing-policy statement keyed by name, creating it if absent. +// Setting an attribute on a not-yet-created policy implicitly creates it, so +// command ordering stays forgiving; a bare `routing-policy ` creates one +// explicitly. BgpPolicyStatement's only key field is `name`. +BgpPolicyStatement& findOrCreatePolicy( + bgp::thrift::BgpConfig& cfg, + const std::string& name) { + auto& policies = *cfg.policies().ensure().bgp_policy_statements(); + for (auto& policy : policies) { + if (*policy.name() == name) { + return policy; + } + } + policies.emplace_back(); + auto& policy = policies.back(); + policy.name() = name; + return policy; +} + +bool policyExists(const bgp::thrift::BgpConfig& cfg, const std::string& name) { + if (!cfg.policies().has_value()) { + return false; + } + for (const auto& policy : *cfg.policies()->bgp_policy_statements()) { + if (*policy.name() == name) { + return true; + } + } + return false; +} + +} // namespace + +// Parse + validate at construction so queryClient stays a thin dispatch. +// Throwing std::invalid_argument is how the framework surfaces arg parse +// errors (same mechanism as BgpCommunityListConfig). +BgpRoutingPolicyConfig::BgpRoutingPolicyConfig(std::vector v) + : utils::BaseObjectArgType(v) { + if (v.empty()) { + throw std::invalid_argument( + "Error: routing-policy is required, optionally followed by " + "an "); + } + if (v[0].empty()) { + throw std::invalid_argument( + fmt::format("Error: {} name must not be empty", kObjectName)); + } + policyName_ = v[0]; + if (v.size() == 1) { + return; // bare `routing-policy `: create it + } + + attr_ = v[1]; + values_.assign(v.begin() + 2, v.end()); + + if (policyAttrHandlers().find(attr_) == policyAttrHandlers().end()) { + throw std::invalid_argument( + fmt::format( + "Error: unknown routing-policy attribute '{}'. Valid " + "attributes: {}", + attr_, + validAttrList())); + } +} + +CmdConfigProtocolBgpPolicyRoutingPolicyTraits::RetType +CmdConfigProtocolBgpPolicyRoutingPolicy::queryClient( + const HostInfo& /* hostInfo */, + const ObjectArgType& args) { + auto& session = ConfigSession::getInstance(); + auto& cfg = session.getBgpConfig(); + const bool policyCreated = !policyExists(cfg, args.policyName()); + auto& policy = findOrCreatePolicy(cfg, args.policyName()); + + Result result = args.attr().empty() + ? ok(policyCreated + ? fmt::format( + "Successfully created BGP routing-policy {}", + args.policyName()) + : fmt::format( + "BGP routing-policy {} already exists", args.policyName())) + // The attribute is guaranteed valid: BgpRoutingPolicyConfig's + // constructor rejects an unknown attribute before we get here. + : policyAttrHandlers().find(args.attr())->second(policy, args.values()); + + if (result.ok) { + if (!args.attr().empty()) { + result.message += + fmt::format(" for routing-policy {}", args.policyName()); + } + session.saveBgpConfig(); + result.message += + fmt::format("\nConfig saved to: {}", session.getBgpSessionConfigPath()); + } else if (policyCreated) { + // Drop the phantom policy so a rejected value is not visible to later + // lookups in the same process. + cfg.policies()->bgp_policy_statements()->pop_back(); + } + return result.message; +} + +void CmdConfigProtocolBgpPolicyRoutingPolicy::printOutput( + const RetType& output) { + std::cout << output << std::endl; +} + +// Explicit template instantiation +template void CmdHandler< + CmdConfigProtocolBgpPolicyRoutingPolicy, + CmdConfigProtocolBgpPolicyRoutingPolicyTraits>::run(); + +} // namespace facebook::fboss diff --git a/fboss/cli/fboss2/commands/config/protocol/bgp/policy/routing-policy/CmdConfigProtocolBgpPolicyRoutingPolicy.h b/fboss/cli/fboss2/commands/config/protocol/bgp/policy/routing-policy/CmdConfigProtocolBgpPolicyRoutingPolicy.h new file mode 100644 index 0000000000000..83bf5bf70724e --- /dev/null +++ b/fboss/cli/fboss2/commands/config/protocol/bgp/policy/routing-policy/CmdConfigProtocolBgpPolicyRoutingPolicy.h @@ -0,0 +1,84 @@ +/* + * Copyright (c) 2004-present, Facebook, Inc. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + * + */ + +#pragma once + +#include +#include +#include "CLI/App.hpp" +#include "fboss/cli/fboss2/CmdHandler.h" +#include "fboss/cli/fboss2/commands/config/protocol/bgp/policy/CmdConfigProtocolBgpPolicy.h" +#include "fboss/cli/fboss2/utils/CmdUtilsCommon.h" +#include "fboss/cli/fboss2/utils/HostInfo.h" + +namespace facebook::fboss { + +// Parsed `routing-policy [ ...]`, validated at +// construction. A routing-policy (bgp_policy.BgpPolicyStatement) is keyed by +// ; anything after the name is a policy-level attribute. Mirrors +// BgpCommunityListConfig minus the nested member level — the seq-num-keyed +// `term ` level lands as a follow-up. +// +// Grammar (from the FBOSS proposed syntax): +// routing-policy (create/select policy) +// routing-policy description (policy attribute) +class BgpRoutingPolicyConfig : public utils::BaseObjectArgType { + public: + // NOLINTNEXTLINE(google-explicit-constructor) + /* implicit */ BgpRoutingPolicyConfig(std::vector v); + const std::string& policyName() const { + return policyName_; + } + const std::string& attr() const { + return attr_; + } + const std::vector& values() const { + return values_; + } + const static utils::ObjectArgTypeId id = + utils::ObjectArgTypeId::OBJECT_ARG_TYPE_ID_MESSAGE; + + private: + std::string policyName_; + std::string attr_; // matched dispatch key ("" = bare create) + std::vector values_; +}; + +// Single handler for the whole `config protocol bgp policy routing-policy` +// family; dispatch and per-attribute parsing live in the .cpp so a new tunable +// is a one-entry change. Mirrors CmdConfigProtocolBgpPolicyCommunityList. +struct CmdConfigProtocolBgpPolicyRoutingPolicyTraits + : public WriteCommandTraits { + using ParentCmd = CmdConfigProtocolBgpPolicy; + static void addCliArg(CLI::App& cmd, std::vector& args) { + // Stops CLI11 from classifying attribute tokens as subcommands once the + // policy name is consumed. See CmdConfigProtocolBgpNeighborTraits. + cmd.positionals_at_end(); + cmd.add_option("args", args, " [ ...]"); + } + using ObjectArgType = BgpRoutingPolicyConfig; + using RetType = std::string; +}; + +class CmdConfigProtocolBgpPolicyRoutingPolicy + : public CmdHandler< + CmdConfigProtocolBgpPolicyRoutingPolicy, + CmdConfigProtocolBgpPolicyRoutingPolicyTraits> { + public: + using ObjectArgType = + CmdConfigProtocolBgpPolicyRoutingPolicyTraits::ObjectArgType; + using RetType = CmdConfigProtocolBgpPolicyRoutingPolicyTraits::RetType; + + RetType queryClient(const HostInfo& hostInfo, const ObjectArgType& args); + + void printOutput(const RetType& output); +}; + +} // namespace facebook::fboss diff --git a/fboss/cli/fboss2/commands/delete/protocol/bgp/policy/routing-policy/CmdDeleteProtocolBgpPolicyRoutingPolicy.cpp b/fboss/cli/fboss2/commands/delete/protocol/bgp/policy/routing-policy/CmdDeleteProtocolBgpPolicyRoutingPolicy.cpp new file mode 100644 index 0000000000000..608e062632fd1 --- /dev/null +++ b/fboss/cli/fboss2/commands/delete/protocol/bgp/policy/routing-policy/CmdDeleteProtocolBgpPolicyRoutingPolicy.cpp @@ -0,0 +1,97 @@ +/* + * Copyright (c) 2004-present, Facebook, Inc. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + * + */ + +#include "fboss/cli/fboss2/commands/delete/protocol/bgp/policy/routing-policy/CmdDeleteProtocolBgpPolicyRoutingPolicy.h" + +#include "fboss/cli/fboss2/CmdHandler.cpp" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "fboss/cli/fboss2/session/ConfigSession.h" +#include "fboss/cli/fboss2/utils/CmdUtilsCommon.h" +#include "fboss/cli/fboss2/utils/HostInfo.h" +#include "fmt/format.h" + +namespace facebook::fboss { + +namespace { +constexpr std::string_view kObjectName = "routing-policy"; +} // namespace + +// Parse + validate at construction so queryClient stays a thin dispatch. +BgpRoutingPolicyRef::BgpRoutingPolicyRef(std::vector v) + : utils::BaseObjectArgType(v) { + if (v.empty()) { + throw std::invalid_argument( + "Error: delete protocol bgp policy routing-policy requires "); + } + if (v[0].empty()) { + throw std::invalid_argument( + fmt::format("Error: {} name must not be empty", kObjectName)); + } + // Unlike the config grammar, nothing may follow the name: there are no + // attributes to delete through this command. + if (v.size() > 1) { + throw std::invalid_argument( + fmt::format( + "Error: unexpected token '{}'. Usage: delete protocol bgp " + "policy routing-policy ", + v[1])); + } + policyName_ = v[0]; +} + +CmdDeleteProtocolBgpPolicyRoutingPolicyTraits::RetType +CmdDeleteProtocolBgpPolicyRoutingPolicy::queryClient( + const HostInfo& /* hostInfo */, + const ObjectArgType& args) { + auto& session = ConfigSession::getInstance(); + auto& cfg = session.getBgpConfig(); + if (!cfg.policies().has_value()) { + // Nothing is persisted for an unknown policy, so a typo'd delete can't + // stage an unrelated session change. + return fmt::format( + "Error: BGP routing-policy {} not found", args.policyName()); + } + auto& policies = *cfg.policies()->bgp_policy_statements(); + auto it = + std::find_if(policies.begin(), policies.end(), [&](const auto& policy) { + return *policy.name() == args.policyName(); + }); + if (it == policies.end()) { + return fmt::format( + "Error: BGP routing-policy {} not found", args.policyName()); + } + policies.erase(it); + session.saveBgpConfig(); + return fmt::format( + "Successfully deleted BGP routing-policy {}\nConfig saved to: {}", + args.policyName(), + session.getBgpSessionConfigPath()); +} + +void CmdDeleteProtocolBgpPolicyRoutingPolicy::printOutput( + const RetType& output) { + std::cout << output << std::endl; +} + +// Explicit template instantiation +template void CmdHandler< + CmdDeleteProtocolBgpPolicyRoutingPolicy, + CmdDeleteProtocolBgpPolicyRoutingPolicyTraits>::run(); + +} // namespace facebook::fboss diff --git a/fboss/cli/fboss2/commands/delete/protocol/bgp/policy/routing-policy/CmdDeleteProtocolBgpPolicyRoutingPolicy.h b/fboss/cli/fboss2/commands/delete/protocol/bgp/policy/routing-policy/CmdDeleteProtocolBgpPolicyRoutingPolicy.h new file mode 100644 index 0000000000000..c5f8c98354913 --- /dev/null +++ b/fboss/cli/fboss2/commands/delete/protocol/bgp/policy/routing-policy/CmdDeleteProtocolBgpPolicyRoutingPolicy.h @@ -0,0 +1,65 @@ +/* + * Copyright (c) 2004-present, Facebook, Inc. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + * + */ + +#pragma once + +#include +#include +#include "CLI/App.hpp" +#include "fboss/cli/fboss2/CmdHandler.h" +#include "fboss/cli/fboss2/commands/delete/protocol/bgp/policy/CmdDeleteProtocolBgpPolicy.h" +#include "fboss/cli/fboss2/utils/CmdUtilsCommon.h" +#include "fboss/cli/fboss2/utils/HostInfo.h" + +namespace facebook::fboss { + +// Parsed `delete protocol bgp policy routing-policy `, validated at +// construction. The name is the policy's identity, matched exactly the same +// way the config command stores it; the whole policy statement is deleted. +// Mirrors BgpCommunityListRef minus the nested member level. +class BgpRoutingPolicyRef : public utils::BaseObjectArgType { + public: + // NOLINTNEXTLINE(google-explicit-constructor) + /* implicit */ BgpRoutingPolicyRef(std::vector v); + const std::string& policyName() const { + return policyName_; + } + const static utils::ObjectArgTypeId id = + utils::ObjectArgTypeId::OBJECT_ARG_TYPE_ID_MESSAGE; + + private: + std::string policyName_; +}; + +struct CmdDeleteProtocolBgpPolicyRoutingPolicyTraits + : public WriteCommandTraits { + using ParentCmd = CmdDeleteProtocolBgpPolicy; + static void addCliArg(CLI::App& cmd, std::vector& args) { + cmd.add_option("args", args, ""); + } + using ObjectArgType = BgpRoutingPolicyRef; + using RetType = std::string; +}; + +class CmdDeleteProtocolBgpPolicyRoutingPolicy + : public CmdHandler< + CmdDeleteProtocolBgpPolicyRoutingPolicy, + CmdDeleteProtocolBgpPolicyRoutingPolicyTraits> { + public: + using ObjectArgType = + CmdDeleteProtocolBgpPolicyRoutingPolicyTraits::ObjectArgType; + using RetType = CmdDeleteProtocolBgpPolicyRoutingPolicyTraits::RetType; + + RetType queryClient(const HostInfo& hostInfo, const ObjectArgType& args); + + void printOutput(const RetType& output); +}; + +} // namespace facebook::fboss diff --git a/fboss/cli/fboss2/test/config/BUCK b/fboss/cli/fboss2/test/config/BUCK index 7073b7407b4e0..71a5ec60e06f8 100644 --- a/fboss/cli/fboss2/test/config/BUCK +++ b/fboss/cli/fboss2/test/config/BUCK @@ -19,6 +19,7 @@ cpp_unittest( "CmdConfigBgpPolicyCommunityListTest.cpp", "CmdConfigBgpPolicyPrefixListEntryTest.cpp", "CmdConfigBgpPolicyPrefixListTest.cpp", + "CmdConfigBgpPolicyRoutingPolicyTest.cpp", "CmdConfigCoppTest.cpp", "CmdConfigDataPlaneTrafficPolicyTest.cpp", "CmdConfigDhcpTest.cpp", @@ -59,6 +60,7 @@ cpp_unittest( "CmdDeleteBgpPolicyAsPathListTest.cpp", "CmdDeleteBgpPolicyCommunityListTest.cpp", "CmdDeleteBgpPolicyPrefixListTest.cpp", + "CmdDeleteBgpPolicyRoutingPolicyTest.cpp", "CmdDeleteConfigInterfaceTest.cpp", "CmdDeleteCoppQueueTest.cpp", "CmdDeleteCoppReasonTest.cpp", diff --git a/fboss/cli/fboss2/test/config/CmdConfigBgpPolicyRoutingPolicyTest.cpp b/fboss/cli/fboss2/test/config/CmdConfigBgpPolicyRoutingPolicyTest.cpp new file mode 100644 index 0000000000000..cf1ba7b789563 --- /dev/null +++ b/fboss/cli/fboss2/test/config/CmdConfigBgpPolicyRoutingPolicyTest.cpp @@ -0,0 +1,156 @@ +// (c) Meta Platforms, Inc. and affiliates. Confidential and proprietary. + +#include +#include +#include + +#include +#include +#include +#include + +#include "configerator/structs/neteng/bgp_policy/thrift/gen-cpp2/bgp_policy_types.h" +#include "fboss/cli/fboss2/commands/config/protocol/bgp/policy/routing-policy/CmdConfigProtocolBgpPolicyRoutingPolicy.h" +#include "fboss/cli/fboss2/session/ConfigSession.h" +#include "fboss/cli/fboss2/test/config/CmdConfigTestBase.h" +#include "fboss/cli/fboss2/utils/HostInfo.h" + +using namespace ::testing; + +namespace facebook::fboss { + +// The routing-policy dispatcher only touches the BGP side of ConfigSession, +// which seeds from thrift schema defaults when neither a staged session nor a +// system bgpcpp.conf exists — so no seed agent config is needed (mirrors +// CmdConfigBgpPolicyCommunityListTest). +class CmdConfigBgpPolicyRoutingPolicyTestFixture : public CmdConfigTestBase { + public: + CmdConfigBgpPolicyRoutingPolicyTestFixture() + : CmdConfigTestBase("bgp_routing_policy_test_%%%%-%%%%-%%%%", "") {} + + void SetUp() override { + CmdConfigTestBase::SetUp(); + setupTestableConfigSession(); + } + + std::string run(const std::vector& tokens) { + CmdConfigProtocolBgpPolicyRoutingPolicy cmd; + HostInfo hostInfo("testhost"); + return cmd.queryClient(hostInfo, BgpRoutingPolicyConfig(tokens)); + } + + const std::vector& policies() { + return *ConfigSession::getInstance() + .getBgpConfig() + .policies() + .ensure() + .bgp_policy_statements(); + } + + bool sessionFileExists() { + return std::filesystem::exists( + ConfigSession::getInstance().getBgpSessionConfigPath()); + } +}; + +// ============================================================================== +// BgpRoutingPolicyConfig (arg) validation +// ============================================================================== + +TEST_F(CmdConfigBgpPolicyRoutingPolicyTestFixture, argValidation) { + // Bare create policy. + auto barePolicy = BgpRoutingPolicyConfig({"RM100"}); + EXPECT_EQ(barePolicy.policyName(), "RM100"); + EXPECT_TRUE(barePolicy.attr().empty()); + + // Policy-level attribute. + auto policyAttr = + BgpRoutingPolicyConfig({"RM100", "description", "my", "policy"}); + EXPECT_EQ(policyAttr.policyName(), "RM100"); + EXPECT_EQ(policyAttr.attr(), "description"); + EXPECT_EQ(policyAttr.values(), std::vector({"my", "policy"})); + + // Invalid: empty, empty name, unknown attribute. + EXPECT_THROW(BgpRoutingPolicyConfig({}), std::invalid_argument); + EXPECT_THROW(BgpRoutingPolicyConfig({""}), std::invalid_argument); + EXPECT_THROW( + BgpRoutingPolicyConfig({"RM100", "no-such-attr", "1"}), + std::invalid_argument); + // term is a follow-up, not yet a valid second token. + EXPECT_THROW( + BgpRoutingPolicyConfig({"RM100", "term", "10"}), std::invalid_argument); +} + +// ============================================================================== +// Policy-level handlers +// ============================================================================== + +TEST_F(CmdConfigBgpPolicyRoutingPolicyTestFixture, bareCreatePolicy) { + auto result = run({"RM100"}); + EXPECT_THAT( + result, HasSubstr("Successfully created BGP routing-policy RM100")); + ASSERT_EQ(policies().size(), 1); + EXPECT_EQ(*policies()[0].name(), "RM100"); + EXPECT_FALSE(policies()[0].description().has_value()); + EXPECT_TRUE(sessionFileExists()); + + run({"RM200"}); + ASSERT_EQ(policies().size(), 2); + EXPECT_EQ(*policies()[1].name(), "RM200"); +} + +TEST_F(CmdConfigBgpPolicyRoutingPolicyTestFixture, setDescription) { + auto result = run({"RM100", "description", "spine", "export", "policy"}); + EXPECT_THAT(result, HasSubstr("Successfully set description")); + EXPECT_THAT(result, HasSubstr("for routing-policy RM100")); + ASSERT_EQ(policies().size(), 1); + // Multi-token description is re-joined. + EXPECT_EQ(*policies()[0].description(), "spine export policy"); +} + +TEST_F(CmdConfigBgpPolicyRoutingPolicyTestFixture, namedPoliciesAreDistinct) { + run({"RM100", "description", "one"}); + run({"RM200", "description", "two"}); + ASSERT_EQ(policies().size(), 2); + // Re-referencing an existing policy by name updates it, not appends. + run({"RM100", "description", "one-updated"}); + ASSERT_EQ(policies().size(), 2); + EXPECT_EQ(*policies()[0].description(), "one-updated"); + EXPECT_EQ(*policies()[1].description(), "two"); +} + +TEST_F(CmdConfigBgpPolicyRoutingPolicyTestFixture, reReferenceReportsExisting) { + run({"RM100"}); + // A second bare reference must not claim to have created it again. + EXPECT_THAT(run({"RM100"}), HasSubstr("already exists")); + ASSERT_EQ(policies().size(), 1); +} + +// ============================================================================== +// Reject paths — the error is surfaced and nothing is persisted +// ============================================================================== + +TEST_F(CmdConfigBgpPolicyRoutingPolicyTestFixture, emptyDescriptionRejected) { + auto result = run({"RM100", "description"}); + EXPECT_THAT(result, HasSubstr("Error: description requires ")); + // The rejected value must not leave a phantom policy behind. + EXPECT_TRUE(policies().empty()); + EXPECT_FALSE(sessionFileExists()) + << "session file should not exist after rejected input"; +} + +TEST_F( + CmdConfigBgpPolicyRoutingPolicyTestFixture, + rejectedValueOnExistingPolicyKeepsPolicy) { + // Create the policy first, then reject a value on it. + run({"RM100", "description", "keep-me"}); + ASSERT_EQ(policies().size(), 1); + + auto result = run({"RM100", "description"}); + EXPECT_THAT(result, HasSubstr("Error: description requires ")); + // The pre-existing policy survives untouched. + ASSERT_EQ(policies().size(), 1); + EXPECT_EQ(*policies()[0].description(), "keep-me"); +} + +} // namespace facebook::fboss diff --git a/fboss/cli/fboss2/test/config/CmdDeleteBgpPolicyRoutingPolicyTest.cpp b/fboss/cli/fboss2/test/config/CmdDeleteBgpPolicyRoutingPolicyTest.cpp new file mode 100644 index 0000000000000..b82f97f3707bc --- /dev/null +++ b/fboss/cli/fboss2/test/config/CmdDeleteBgpPolicyRoutingPolicyTest.cpp @@ -0,0 +1,120 @@ +// (c) Meta Platforms, Inc. and affiliates. Confidential and proprietary. + +#include +#include +#include + +#include +#include +#include +#include + +#include "configerator/structs/neteng/bgp_policy/thrift/gen-cpp2/bgp_policy_types.h" +#include "fboss/cli/fboss2/commands/config/protocol/bgp/policy/routing-policy/CmdConfigProtocolBgpPolicyRoutingPolicy.h" +#include "fboss/cli/fboss2/commands/delete/protocol/bgp/policy/routing-policy/CmdDeleteProtocolBgpPolicyRoutingPolicy.h" +#include "fboss/cli/fboss2/session/ConfigSession.h" +#include "fboss/cli/fboss2/test/config/CmdConfigTestBase.h" +#include "fboss/cli/fboss2/utils/HostInfo.h" + +using namespace ::testing; + +namespace facebook::fboss { + +// Deleting only touches the BGP side of ConfigSession, which seeds from thrift +// schema defaults when neither a staged session nor a system bgpcpp.conf +// exists — so no seed agent config is needed (mirrors +// CmdDeleteBgpPolicyCommunityListTest). +class CmdDeleteBgpPolicyRoutingPolicyTestFixture : public CmdConfigTestBase { + public: + CmdDeleteBgpPolicyRoutingPolicyTestFixture() + : CmdConfigTestBase("bgp_routing_policy_delete_test_%%%%-%%%%-%%%%", "") { + } + + void SetUp() override { + CmdConfigTestBase::SetUp(); + setupTestableConfigSession(); + } + + std::string configure(const std::vector& tokens) { + CmdConfigProtocolBgpPolicyRoutingPolicy cmd; + HostInfo hostInfo("testhost"); + return cmd.queryClient(hostInfo, BgpRoutingPolicyConfig(tokens)); + } + + std::string del(const std::vector& tokens) { + CmdDeleteProtocolBgpPolicyRoutingPolicy cmd; + HostInfo hostInfo("testhost"); + return cmd.queryClient(hostInfo, BgpRoutingPolicyRef(tokens)); + } + + const std::vector& policies() { + return *ConfigSession::getInstance() + .getBgpConfig() + .policies() + .ensure() + .bgp_policy_statements(); + } + + bool sessionFileExists() { + return std::filesystem::exists( + ConfigSession::getInstance().getBgpSessionConfigPath()); + } +}; + +// ============================================================================== +// BgpRoutingPolicyRef (arg) validation +// ============================================================================== + +TEST_F(CmdDeleteBgpPolicyRoutingPolicyTestFixture, argValidation) { + auto ref = BgpRoutingPolicyRef({"RM100"}); + EXPECT_EQ(ref.policyName(), "RM100"); + + // Invalid: empty, empty name, extra tokens. + EXPECT_THROW(BgpRoutingPolicyRef({}), std::invalid_argument); + EXPECT_THROW(BgpRoutingPolicyRef({""}), std::invalid_argument); + EXPECT_THROW(BgpRoutingPolicyRef({"RM100", "RM200"}), std::invalid_argument); + EXPECT_THROW( + BgpRoutingPolicyRef({"RM100", "description"}), std::invalid_argument); +} + +// ============================================================================== +// queryClient +// ============================================================================== + +TEST_F(CmdDeleteBgpPolicyRoutingPolicyTestFixture, deleteExistingPolicy) { + configure({"RM100", "description", "delete-me"}); + configure({"RM200", "description", "keep"}); + ASSERT_EQ(policies().size(), 2); + + auto result = del({"RM100"}); + EXPECT_THAT( + result, HasSubstr("Successfully deleted BGP routing-policy RM100")); + // The other policy survives untouched. + ASSERT_EQ(policies().size(), 1); + EXPECT_EQ(*policies()[0].name(), "RM200"); + EXPECT_EQ(*policies()[0].description(), "keep"); + EXPECT_TRUE(sessionFileExists()); +} + +TEST_F( + CmdDeleteBgpPolicyRoutingPolicyTestFixture, + deleteUnknownPolicyRejected) { + auto result = del({"NO-SUCH-POLICY"}); + EXPECT_THAT( + result, HasSubstr("Error: BGP routing-policy NO-SUCH-POLICY not found")); + // Nothing was persisted for the failed delete. + EXPECT_FALSE(sessionFileExists()) + << "session file should not exist after rejected delete"; +} + +TEST_F( + CmdDeleteBgpPolicyRoutingPolicyTestFixture, + deleteUnknownLeavesOthersIntact) { + configure({"RM100", "description", "one"}); + auto result = del({"RM200"}); + EXPECT_THAT(result, HasSubstr("not found")); + ASSERT_EQ(policies().size(), 1); + EXPECT_EQ(*policies()[0].name(), "RM100"); +} + +} // namespace facebook::fboss diff --git a/fboss/cli/fboss2/test/integration_test/BUCK b/fboss/cli/fboss2/test/integration_test/BUCK index 91dfdccc697f3..bc6e011006227 100644 --- a/fboss/cli/fboss2/test/integration_test/BUCK +++ b/fboss/cli/fboss2/test/integration_test/BUCK @@ -29,6 +29,7 @@ cpp_binary( "ConfigBgpPolicyCommunityListTest.cpp", "ConfigBgpPolicyPrefixListEntryTest.cpp", "ConfigBgpPolicyPrefixListTest.cpp", + "ConfigBgpPolicyRoutingPolicyTest.cpp", "ConfigBgpSessionTest.cpp", "ConfigConcurrentSessionsTest.cpp", "ConfigCoppTest.cpp", diff --git a/fboss/cli/fboss2/test/integration_test/ConfigBgpPolicyRoutingPolicyTest.cpp b/fboss/cli/fboss2/test/integration_test/ConfigBgpPolicyRoutingPolicyTest.cpp new file mode 100644 index 0000000000000..a4d18c88f2f6c --- /dev/null +++ b/fboss/cli/fboss2/test/integration_test/ConfigBgpPolicyRoutingPolicyTest.cpp @@ -0,0 +1,121 @@ +// (c) Meta Platforms, Inc. and affiliates. Confidential and proprietary. + +/** + * End-to-end tests for `fboss2-dev config protocol bgp policy routing-policy + * [ ...]`. + * + * Scope: the routing-policy (bgp_policy.BgpPolicyStatement) and its + * policy-level attributes. Every test stages the change AND commits it, then + * asserts the value landed at the correct thrift field path inside the + * matching .policies.bgp_policy_statements[] entry of bgpd's running config + * (via getRunningConfig RPC) — which also confirms bgpd accepts and adopts a + * policy statement with no terms. Stage-only behavior (attribute parsing, + * validation, rejection) is covered by the unit tests; an integration test + * that never commits exercises no daemon. + * + * Requirements: + * - The fboss2-dev binary under test (config subcommand tree). + * - HOME is set (the session file lives under $HOME/.fboss2). + * - bgpd is installed/active (commit restarts it). + */ + +#include +#include +#include + +#include "fboss/cli/fboss2/test/integration_test/ConfigBgpTestBase.h" +#include "folly/json/dynamic.h" +#include "gmock/gmock.h" + +using namespace facebook::fboss; +using ::testing::HasSubstr; +using ::testing::Not; + +namespace { +// Test-only routing-policy name, unlikely to collide with a real policy in +// the device's running BGP config. +const std::string kPolicy = "FBOSS2-TEST-ROUTING-POLICY"; +} // namespace + +class ConfigBgpPolicyRoutingPolicyTest : public ConfigBgpTestBase { + protected: + // Stage `config protocol bgp policy routing-policy ` WITHOUT + // clearing the staged session, so attributes can accumulate across + // invocations. Returns the staged session JSON. + folly::dynamic stageRoutingPolicy(const std::vector& tokens) { + std::vector args = { + "config", "protocol", "bgp", "policy", "routing-policy"}; + args.insert(args.end(), tokens.begin(), tokens.end()); + auto result = runCli(args); + EXPECT_EQ(result.exitCode, 0) + << "stdout=" << result.stdout << " stderr=" << result.stderr; + EXPECT_THAT(result.stdout, Not(HasSubstr("Error:"))); + return readBgpSessionConfig(); + } + + // The .policies.bgp_policy_statements[] entry named `name`, or nullptr. + static const folly::dynamic* findPolicy( + const folly::dynamic& config, + const std::string& name) { + if (config.count("policies") == 0 || + config["policies"].count("bgp_policy_statements") == 0) { + return nullptr; + } + for (const auto& policy : config["policies"]["bgp_policy_statements"]) { + if (policy.count("name") && policy["name"].asString() == name) { + return &policy; + } + } + return nullptr; + } +}; + +TEST_F(ConfigBgpPolicyRoutingPolicyTest, SetDescriptionAndCommit) { + discardSession(); + clearBgpSession(); + stageRoutingPolicy({kPolicy, "description", "test", "spine", "export"}); + commitAndGetSha(); + ASSERT_TRUE(waitForBgpDaemonActive()) + << "bgpd did not return active after commit; state=" + << bgpDaemonActiveState(); + // Two layers: the promoted file proves what the commit wrote; the daemon's + // getRunningConfig RPC proves bgpd parsed and adopted the + // .policies.bgp_policy_statements blob. + auto config = readSystemBgpConfig(); + const auto* policy = findPolicy(config, kPolicy); + ASSERT_NE(policy, nullptr) + << "committed config has no routing-policy " << kPolicy; + EXPECT_EQ((*policy)["description"].asString(), "test spine export"); + + auto running = readRunningBgpConfigViaRpc(); + const auto* runningPolicy = findPolicy(running, kPolicy); + ASSERT_NE(runningPolicy, nullptr) + << "bgpd's running config has no routing-policy " << kPolicy; + EXPECT_EQ((*runningPolicy)["description"].asString(), "test spine export"); +} + +TEST_F(ConfigBgpPolicyRoutingPolicyTest, DeletePolicyAndCommit) { + // Land a routing-policy in the system config, then delete it through a + // second commit and verify it is gone from bgpd's running config. + discardSession(); + clearBgpSession(); + stageRoutingPolicy({kPolicy, "description", "delete-me"}); + commitAndGetSha(); + ASSERT_TRUE(waitForBgpDaemonActive()) + << "bgpd did not return active after commit; state=" + << bgpDaemonActiveState(); + ASSERT_NE(findPolicy(readRunningBgpConfigViaRpc(), kPolicy), nullptr) + << "setup commit did not land the routing-policy in bgpd's running " + "config"; + + clearBgpSession(); + auto result = runCli( + {"delete", "protocol", "bgp", "policy", "routing-policy", kPolicy}); + EXPECT_THAT(result.stdout, HasSubstr("Successfully deleted")); + commitAndGetSha(); + ASSERT_TRUE(waitForBgpDaemonActive()) + << "bgpd did not return active after delete commit; state=" + << bgpDaemonActiveState(); + EXPECT_EQ(findPolicy(readRunningBgpConfigViaRpc(), kPolicy), nullptr) + << "deleted routing-policy still present in bgpd's running config"; +} From fcbb5fd5623096c7f4315003e9bdd7cfec5021a5 Mon Sep 17 00:00:00 2001 From: Hillol Chakraborty Date: Tue, 15 Sep 2026 12:25:08 +0000 Subject: [PATCH 2/5] Guard routing-policy delete against neighbor, peer-group and network6 references Deleting a routing-policy that a neighbor or peer-group still names as its ingress/egress policy, or that a network6 prefix names, committed a dangling reference; bgpd fails the config load on it and crash-loops. The delete now refuses and names every referrer (`neighbor ingress|egress`, `peer-group ingress|egress`, `network6 `), the same rule the peer-group, as-path-list and prefix-list deletes apply. The tests stage the referrers through the real neighbor, peer-group and global CLIs, which live below this change. Co-Authored-By: Claude Fable 5.1 --- ...mdDeleteProtocolBgpPolicyRoutingPolicy.cpp | 51 ++++++++++++ .../CmdDeleteBgpPolicyRoutingPolicyTest.cpp | 79 +++++++++++++++++++ 2 files changed, 130 insertions(+) diff --git a/fboss/cli/fboss2/commands/delete/protocol/bgp/policy/routing-policy/CmdDeleteProtocolBgpPolicyRoutingPolicy.cpp b/fboss/cli/fboss2/commands/delete/protocol/bgp/policy/routing-policy/CmdDeleteProtocolBgpPolicyRoutingPolicy.cpp index 608e062632fd1..be21104831994 100644 --- a/fboss/cli/fboss2/commands/delete/protocol/bgp/policy/routing-policy/CmdDeleteProtocolBgpPolicyRoutingPolicy.cpp +++ b/fboss/cli/fboss2/commands/delete/protocol/bgp/policy/routing-policy/CmdDeleteProtocolBgpPolicyRoutingPolicy.cpp @@ -13,6 +13,7 @@ #include "fboss/cli/fboss2/CmdHandler.cpp" #include +#include #include #include #include @@ -30,6 +31,44 @@ namespace facebook::fboss { namespace { constexpr std::string_view kObjectName = "routing-policy"; + +// Everything that names a routing-policy and that bgpd resolves at config +// load: a neighbor's or peer-group's ingress/egress policy and a network6 +// prefix's policy. Labelled the way the CLI addresses each referrer. +std::vector findReferencesToRoutingPolicy( + const bgp::thrift::BgpConfig& cfg, + const std::string& policyName) { + std::vector referrers; + auto matches = [&](const auto& field) { + return field.has_value() && *field == policyName; + }; + for (const auto& peer : *cfg.peers()) { + if (matches(peer.ingress_policy_name())) { + referrers.push_back( + fmt::format("neighbor {} ingress", *peer.peer_addr())); + } + if (matches(peer.egress_policy_name())) { + referrers.push_back(fmt::format("neighbor {} egress", *peer.peer_addr())); + } + } + if (cfg.peer_groups().has_value()) { + for (const auto& group : *cfg.peer_groups()) { + if (matches(group.ingress_policy_name())) { + referrers.push_back( + fmt::format("peer-group {} ingress", *group.name())); + } + if (matches(group.egress_policy_name())) { + referrers.push_back(fmt::format("peer-group {} egress", *group.name())); + } + } + } + for (const auto& network : *cfg.networks6()) { + if (matches(network.policy_name())) { + referrers.push_back(fmt::format("network6 {}", *network.prefix())); + } + } + return referrers; +} } // namespace // Parse + validate at construction so queryClient stays a thin dispatch. @@ -76,6 +115,18 @@ CmdDeleteProtocolBgpPolicyRoutingPolicy::queryClient( return fmt::format( "Error: BGP routing-policy {} not found", args.policyName()); } + // ingress/egress_policy_name and network6 policy_name resolve against this + // policy by name at daemon load; erasing it while something still names it + // would commit a dangling reference and fail the load. Refuse and name the + // referrers instead. + auto referrers = findReferencesToRoutingPolicy(cfg, args.policyName()); + if (!referrers.empty()) { + return fmt::format( + "Error: BGP routing-policy {} is still referenced by {}; re-point or " + "remove those references first", + args.policyName(), + folly::join(", ", referrers)); + } policies.erase(it); session.saveBgpConfig(); return fmt::format( diff --git a/fboss/cli/fboss2/test/config/CmdDeleteBgpPolicyRoutingPolicyTest.cpp b/fboss/cli/fboss2/test/config/CmdDeleteBgpPolicyRoutingPolicyTest.cpp index b82f97f3707bc..be629917aba0f 100644 --- a/fboss/cli/fboss2/test/config/CmdDeleteBgpPolicyRoutingPolicyTest.cpp +++ b/fboss/cli/fboss2/test/config/CmdDeleteBgpPolicyRoutingPolicyTest.cpp @@ -10,6 +10,9 @@ #include #include "configerator/structs/neteng/bgp_policy/thrift/gen-cpp2/bgp_policy_types.h" +#include "fboss/cli/fboss2/commands/config/protocol/bgp/global/CmdConfigProtocolBgpGlobal.h" +#include "fboss/cli/fboss2/commands/config/protocol/bgp/neighbor/CmdConfigProtocolBgpNeighbor.h" +#include "fboss/cli/fboss2/commands/config/protocol/bgp/peer-group/CmdConfigProtocolBgpPeerGroup.h" #include "fboss/cli/fboss2/commands/config/protocol/bgp/policy/routing-policy/CmdConfigProtocolBgpPolicyRoutingPolicy.h" #include "fboss/cli/fboss2/commands/delete/protocol/bgp/policy/routing-policy/CmdDeleteProtocolBgpPolicyRoutingPolicy.h" #include "fboss/cli/fboss2/session/ConfigSession.h" @@ -55,6 +58,27 @@ class CmdDeleteBgpPolicyRoutingPolicyTestFixture : public CmdConfigTestBase { .bgp_policy_statements(); } + // The referrers are staged through the real CLIs that live below this + // level, so the guard is exercised against exactly what those commands + // write. + std::string configureNeighbor(const std::vector& tokens) { + CmdConfigProtocolBgpNeighbor cmd; + HostInfo hostInfo("testhost"); + return cmd.queryClient(hostInfo, BgpNeighborConfig(tokens)); + } + + std::string configurePeerGroup(const std::vector& tokens) { + CmdConfigProtocolBgpPeerGroup cmd; + HostInfo hostInfo("testhost"); + return cmd.queryClient(hostInfo, BgpPeerGroupConfig(tokens)); + } + + std::string configureGlobal(const std::vector& tokens) { + CmdConfigProtocolBgpGlobal cmd; + HostInfo hostInfo("testhost"); + return cmd.queryClient(hostInfo, BgpGlobalConfig(tokens)); + } + bool sessionFileExists() { return std::filesystem::exists( ConfigSession::getInstance().getBgpSessionConfigPath()); @@ -117,4 +141,59 @@ TEST_F( EXPECT_EQ(*policies()[0].name(), "RM100"); } +// ============================================================================== +// Reference guard — a policy a neighbor, peer-group or network6 still names is +// not deletable +// ============================================================================== + +TEST_F( + CmdDeleteBgpPolicyRoutingPolicyTestFixture, + deleteReferencedPolicyRejected) { + configure({"RM100", "description", "in-use"}); + configureNeighbor({"10.0.0.2", "ingress-policy", "RM100"}); + + auto result = del({"RM100"}); + EXPECT_THAT(result, HasSubstr("still referenced")); + // The refusal names the referrer so the user can act on it. + EXPECT_THAT(result, HasSubstr("neighbor 10.0.0.2 ingress")); + EXPECT_THAT(result, HasSubstr("re-point or remove those references first")); + ASSERT_EQ(policies().size(), 1); + EXPECT_EQ(*policies()[0].name(), "RM100"); + EXPECT_EQ(*policies()[0].description(), "in-use"); +} + +TEST_F( + CmdDeleteBgpPolicyRoutingPolicyTestFixture, + deleteReferencedPolicyNamesEveryReferrer) { + configure({"RM100"}); + configureNeighbor({"10.0.0.2", "egress-policy", "RM100"}); + configurePeerGroup({"SPINE", "ingress-policy", "RM100"}); + configurePeerGroup({"SPINE", "egress-policy", "RM100"}); + configureGlobal({"network6", "add", "2001:db8::/64", "policy", "RM100"}); + + auto result = del({"RM100"}); + EXPECT_THAT(result, HasSubstr("neighbor 10.0.0.2 egress")); + EXPECT_THAT(result, HasSubstr("peer-group SPINE ingress")); + EXPECT_THAT(result, HasSubstr("peer-group SPINE egress")); + EXPECT_THAT(result, HasSubstr("network6 2001:db8::/64")); + ASSERT_EQ(policies().size(), 1); +} + +TEST_F( + CmdDeleteBgpPolicyRoutingPolicyTestFixture, + referenceToOtherPolicyDoesNotBlockDelete) { + configure({"RM100"}); + configure({"RM200"}); + // Referrers of RM100 must not block deleting RM200. + configureNeighbor({"10.0.0.2", "ingress-policy", "RM100"}); + configurePeerGroup({"SPINE", "egress-policy", "RM100"}); + configureGlobal({"network6", "add", "2001:db8::/64", "policy", "RM100"}); + + auto result = del({"RM200"}); + EXPECT_THAT( + result, HasSubstr("Successfully deleted BGP routing-policy RM200")); + ASSERT_EQ(policies().size(), 1); + EXPECT_EQ(*policies()[0].name(), "RM100"); +} + } // namespace facebook::fboss From 54122e06066b1a4eeeadc4cb1761ab464f67d559 Mon Sep 17 00:00:00 2001 From: Hillol Chakraborty Date: Tue, 15 Sep 2026 12:25:08 +0000 Subject: [PATCH 3/5] Emit routing-policy commands from bgp_json_to_cli.py Adds generate_routing_policy_commands (description) and closes the policy block with the bgp_policy_statements loop, after every list they reference by name. A policy-level `result` other than the DENY default has no CLI spelling (only a term action result does) and is emitted as a `# WARNING:` line. Terms follow in the term subcommand change. Co-Authored-By: Claude Fable 5.1 --- fboss/cli/fboss2/tools/bgp_json_to_cli.py | 49 +++++++++++++++++++ .../tools/tests/test_bgp_json_to_cli.py | 48 ++++++++++++++++++ 2 files changed, 97 insertions(+) diff --git a/fboss/cli/fboss2/tools/bgp_json_to_cli.py b/fboss/cli/fboss2/tools/bgp_json_to_cli.py index b93c21e9f7bc4..9ba9776d90f79 100644 --- a/fboss/cli/fboss2/tools/bgp_json_to_cli.py +++ b/fboss/cli/fboss2/tools/bgp_json_to_cli.py @@ -783,6 +783,52 @@ def generate_prefix_list_entry_commands( return commands +_FLOW_CONTROL_ACTION_NAMES = { + 1: "ACCEPT", + 2: "DENY", + 3: "NEXT_TERM", + 4: "NEXT_POLICY", + 5: "LOG_AND_NEXT_TERM", + 6: "LOG_AND_ACCEPT", + 7: "LOG_AND_DENY", +} + + +def _flow_control_action_name(raw: Any) -> str: + if isinstance(raw, str): + return raw + return _FLOW_CONTROL_ACTION_NAMES.get(int(raw), str(raw)) + + +def generate_routing_policy_commands(policy: dict[str, Any]) -> list[str]: + """Generate `config protocol bgp policy routing-policy` commands for one + policy statement. The policy-level `result` has no CLI spelling (only a + term's action result does) and surfaces as a warning when not the DENY + default.""" + name = policy.get("name", "") + if not name: + return [] + + prefix = f"config protocol bgp policy routing-policy {escape_shell_arg(name)}" + commands = [] + if policy.get("description"): + commands.append( + f"{prefix} description {escape_shell_arg(policy['description'])}" + ) + if "result" in policy and _flow_control_action_name(policy["result"]) != "DENY": + commands.append( + _warning( + f"routing-policy {name}: policy-level result " + f"{_flow_control_action_name(policy['result'])} has no CLI " + "equivalent; not emitted" + ) + ) + if not commands: + # Nothing to set: still recreate the (empty) routing-policy by name. + commands.append(prefix) + return commands + + def generate_policy_commands(config: dict[str, Any]) -> list[str]: """Generate the `config protocol bgp policy ...` commands. @@ -798,6 +844,9 @@ def generate_policy_commands(config: dict[str, Any]) -> list[str]: commands.extend(generate_community_list_commands(community_list)) for prefix_list in policies.get("prefix_lists", []): commands.extend(generate_prefix_list_commands(prefix_list)) + # Routing-policies last: they reference the lists above by name. + for policy in policies.get("bgp_policy_statements", []): + commands.extend(generate_routing_policy_commands(policy)) return commands diff --git a/fboss/cli/fboss2/tools/tests/test_bgp_json_to_cli.py b/fboss/cli/fboss2/tools/tests/test_bgp_json_to_cli.py index b969ca74baccb..bb3ac2b46d8e3 100644 --- a/fboss/cli/fboss2/tools/tests/test_bgp_json_to_cli.py +++ b/fboss/cli/fboss2/tools/tests/test_bgp_json_to_cli.py @@ -31,6 +31,7 @@ generate_policy_commands, generate_prefix_list_commands, generate_prefix_list_entry_commands, + generate_routing_policy_commands, json_to_cli, ) @@ -1429,6 +1430,53 @@ def test_entries_emitted_inside_list(self) -> None: ) +class GenerateRoutingPolicyCommandsTest(unittest.TestCase): + """Tests for generate_routing_policy_commands (routing-policy grammar).""" + + PREFIX = "config protocol bgp policy routing-policy RM" + + def test_empty_name_returns_empty(self) -> None: + self.assertEqual(generate_routing_policy_commands({"description": "x"}), []) + + def test_bare_policy_is_recreated(self) -> None: + self.assertEqual( + generate_routing_policy_commands({"name": "RM"}), [self.PREFIX] + ) + + def test_description(self) -> None: + commands = generate_routing_policy_commands( + {"name": "RM", "description": "import from spine"} + ) + self.assertEqual(commands, [f"{self.PREFIX} description 'import from spine'"]) + + def test_result_deny_default_silent_other_warns(self) -> None: + self.assertEqual( + generate_routing_policy_commands({"name": "RM", "result": 2}), [self.PREFIX] + ) + commands = generate_routing_policy_commands({"name": "RM", "result": "ACCEPT"}) + self.assertEqual(len(commands), 1) + self.assertTrue(commands[0].startswith("# WARNING:")) + self.assertIn("ACCEPT", commands[0]) + + def test_policy_block_order_lists_then_policies(self) -> None: + config = { + "policies": { + "bgp_policy_statements": [{"name": "RM"}], + "aspath_lists": [{"name": "A"}], + "prefix_lists": [{"name": "PL"}], + } + } + commands = generate_policy_commands(config) + self.assertEqual( + commands, + [ + "config protocol bgp policy as-path-list A", + "config protocol bgp policy prefix-list PL", + self.PREFIX, + ], + ) + + class GenerateScriptCommandsTest(unittest.TestCase): """Tests for generate_exec_commands.""" From 2a28c6b507b2aba8cfae7c14d10c8ce3a28116b5 Mon Sep 17 00:00:00 2001 From: Hillol Chakraborty Date: Wed, 16 Sep 2026 07:46:02 +0000 Subject: [PATCH 4/5] Make BGP routing-policy delete idempotent Delete mirrors add: deleting an absent policy is a success with a `Warning:` and saves nothing, so a replayed script stays idempotent. The neighbor/peer-group/network6 reference guard still errors. Co-Authored-By: Claude Fable 5.1 --- ...mdDeleteProtocolBgpPolicyRoutingPolicy.cpp | 14 ++++---- .../CmdDeleteBgpPolicyRoutingPolicyTest.cpp | 33 +++++++++++++++---- 2 files changed, 34 insertions(+), 13 deletions(-) diff --git a/fboss/cli/fboss2/commands/delete/protocol/bgp/policy/routing-policy/CmdDeleteProtocolBgpPolicyRoutingPolicy.cpp b/fboss/cli/fboss2/commands/delete/protocol/bgp/policy/routing-policy/CmdDeleteProtocolBgpPolicyRoutingPolicy.cpp index be21104831994..8cde40a9cf604 100644 --- a/fboss/cli/fboss2/commands/delete/protocol/bgp/policy/routing-policy/CmdDeleteProtocolBgpPolicyRoutingPolicy.cpp +++ b/fboss/cli/fboss2/commands/delete/protocol/bgp/policy/routing-policy/CmdDeleteProtocolBgpPolicyRoutingPolicy.cpp @@ -100,11 +100,14 @@ CmdDeleteProtocolBgpPolicyRoutingPolicy::queryClient( const ObjectArgType& args) { auto& session = ConfigSession::getInstance(); auto& cfg = session.getBgpConfig(); + // Delete mirrors add: an absent target is already the requested end state, + // so this is a success with a warning. Nothing is saved, so a typo'd delete + // can't stage an unrelated session change. + auto absent = fmt::format( + "Warning: BGP routing-policy {} does not exist; nothing to delete", + args.policyName()); if (!cfg.policies().has_value()) { - // Nothing is persisted for an unknown policy, so a typo'd delete can't - // stage an unrelated session change. - return fmt::format( - "Error: BGP routing-policy {} not found", args.policyName()); + return absent; } auto& policies = *cfg.policies()->bgp_policy_statements(); auto it = @@ -112,8 +115,7 @@ CmdDeleteProtocolBgpPolicyRoutingPolicy::queryClient( return *policy.name() == args.policyName(); }); if (it == policies.end()) { - return fmt::format( - "Error: BGP routing-policy {} not found", args.policyName()); + return absent; } // ingress/egress_policy_name and network6 policy_name resolve against this // policy by name at daemon load; erasing it while something still names it diff --git a/fboss/cli/fboss2/test/config/CmdDeleteBgpPolicyRoutingPolicyTest.cpp b/fboss/cli/fboss2/test/config/CmdDeleteBgpPolicyRoutingPolicyTest.cpp index be629917aba0f..00b002a25826c 100644 --- a/fboss/cli/fboss2/test/config/CmdDeleteBgpPolicyRoutingPolicyTest.cpp +++ b/fboss/cli/fboss2/test/config/CmdDeleteBgpPolicyRoutingPolicyTest.cpp @@ -120,15 +120,34 @@ TEST_F(CmdDeleteBgpPolicyRoutingPolicyTestFixture, deleteExistingPolicy) { EXPECT_TRUE(sessionFileExists()); } -TEST_F( - CmdDeleteBgpPolicyRoutingPolicyTestFixture, - deleteUnknownPolicyRejected) { +// Delete mirrors add: an absent target is a success with a warning, never an +// error, so a replayed script stays idempotent. +TEST_F(CmdDeleteBgpPolicyRoutingPolicyTestFixture, deleteUnknownPolicyWarns) { auto result = del({"NO-SUCH-POLICY"}); EXPECT_THAT( - result, HasSubstr("Error: BGP routing-policy NO-SUCH-POLICY not found")); - // Nothing was persisted for the failed delete. + result, + HasSubstr( + "Warning: BGP routing-policy NO-SUCH-POLICY does not exist; nothing " + "to delete")); + EXPECT_THAT(result, Not(HasSubstr("Error:"))); + // Nothing changed, so nothing is staged. EXPECT_FALSE(sessionFileExists()) - << "session file should not exist after rejected delete"; + << "session file should not exist after a no-op delete"; +} + +TEST_F(CmdDeleteBgpPolicyRoutingPolicyTestFixture, deleteTwiceIsIdempotent) { + configure({"RM100", "description", "delete-me"}); + EXPECT_THAT(del({"RM100"}), HasSubstr("Successfully deleted")); + EXPECT_TRUE(policies().empty()); + + auto again = del({"RM100"}); + EXPECT_THAT( + again, + HasSubstr( + "Warning: BGP routing-policy RM100 does not exist; nothing to " + "delete")); + EXPECT_THAT(again, Not(HasSubstr("Error:"))); + EXPECT_TRUE(policies().empty()); } TEST_F( @@ -136,7 +155,7 @@ TEST_F( deleteUnknownLeavesOthersIntact) { configure({"RM100", "description", "one"}); auto result = del({"RM200"}); - EXPECT_THAT(result, HasSubstr("not found")); + EXPECT_THAT(result, HasSubstr("does not exist")); ASSERT_EQ(policies().size(), 1); EXPECT_EQ(*policies()[0].name(), "RM100"); } From 0cc08e84b50c068fbc434292be80525fc0f3556d Mon Sep 17 00:00:00 2001 From: Hillol Chakraborty Date: Fri, 16 Oct 2026 06:55:49 +0000 Subject: [PATCH 5/5] Use the canonical Meta copyright header in new files --- .../routing-policy/CmdConfigProtocolBgpPolicyRoutingPolicy.cpp | 2 +- .../routing-policy/CmdConfigProtocolBgpPolicyRoutingPolicy.h | 2 +- .../routing-policy/CmdDeleteProtocolBgpPolicyRoutingPolicy.cpp | 2 +- .../routing-policy/CmdDeleteProtocolBgpPolicyRoutingPolicy.h | 2 +- .../fboss2/test/config/CmdConfigBgpPolicyRoutingPolicyTest.cpp | 2 +- .../fboss2/test/config/CmdDeleteBgpPolicyRoutingPolicyTest.cpp | 2 +- .../test/integration_test/ConfigBgpPolicyRoutingPolicyTest.cpp | 2 +- 7 files changed, 7 insertions(+), 7 deletions(-) diff --git a/fboss/cli/fboss2/commands/config/protocol/bgp/policy/routing-policy/CmdConfigProtocolBgpPolicyRoutingPolicy.cpp b/fboss/cli/fboss2/commands/config/protocol/bgp/policy/routing-policy/CmdConfigProtocolBgpPolicyRoutingPolicy.cpp index 491be24a11c2d..c324f2bfa43ad 100644 --- a/fboss/cli/fboss2/commands/config/protocol/bgp/policy/routing-policy/CmdConfigProtocolBgpPolicyRoutingPolicy.cpp +++ b/fboss/cli/fboss2/commands/config/protocol/bgp/policy/routing-policy/CmdConfigProtocolBgpPolicyRoutingPolicy.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2004-present, Facebook, Inc. + * Copyright (c) Meta Platforms, Inc. and affiliates. * All rights reserved. * * This source code is licensed under the BSD-style license found in the diff --git a/fboss/cli/fboss2/commands/config/protocol/bgp/policy/routing-policy/CmdConfigProtocolBgpPolicyRoutingPolicy.h b/fboss/cli/fboss2/commands/config/protocol/bgp/policy/routing-policy/CmdConfigProtocolBgpPolicyRoutingPolicy.h index 83bf5bf70724e..1b0792c094de9 100644 --- a/fboss/cli/fboss2/commands/config/protocol/bgp/policy/routing-policy/CmdConfigProtocolBgpPolicyRoutingPolicy.h +++ b/fboss/cli/fboss2/commands/config/protocol/bgp/policy/routing-policy/CmdConfigProtocolBgpPolicyRoutingPolicy.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2004-present, Facebook, Inc. + * Copyright (c) Meta Platforms, Inc. and affiliates. * All rights reserved. * * This source code is licensed under the BSD-style license found in the diff --git a/fboss/cli/fboss2/commands/delete/protocol/bgp/policy/routing-policy/CmdDeleteProtocolBgpPolicyRoutingPolicy.cpp b/fboss/cli/fboss2/commands/delete/protocol/bgp/policy/routing-policy/CmdDeleteProtocolBgpPolicyRoutingPolicy.cpp index 8cde40a9cf604..d0d7213c96113 100644 --- a/fboss/cli/fboss2/commands/delete/protocol/bgp/policy/routing-policy/CmdDeleteProtocolBgpPolicyRoutingPolicy.cpp +++ b/fboss/cli/fboss2/commands/delete/protocol/bgp/policy/routing-policy/CmdDeleteProtocolBgpPolicyRoutingPolicy.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2004-present, Facebook, Inc. + * Copyright (c) Meta Platforms, Inc. and affiliates. * All rights reserved. * * This source code is licensed under the BSD-style license found in the diff --git a/fboss/cli/fboss2/commands/delete/protocol/bgp/policy/routing-policy/CmdDeleteProtocolBgpPolicyRoutingPolicy.h b/fboss/cli/fboss2/commands/delete/protocol/bgp/policy/routing-policy/CmdDeleteProtocolBgpPolicyRoutingPolicy.h index c5f8c98354913..e9c12bba9ef06 100644 --- a/fboss/cli/fboss2/commands/delete/protocol/bgp/policy/routing-policy/CmdDeleteProtocolBgpPolicyRoutingPolicy.h +++ b/fboss/cli/fboss2/commands/delete/protocol/bgp/policy/routing-policy/CmdDeleteProtocolBgpPolicyRoutingPolicy.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2004-present, Facebook, Inc. + * Copyright (c) Meta Platforms, Inc. and affiliates. * All rights reserved. * * This source code is licensed under the BSD-style license found in the diff --git a/fboss/cli/fboss2/test/config/CmdConfigBgpPolicyRoutingPolicyTest.cpp b/fboss/cli/fboss2/test/config/CmdConfigBgpPolicyRoutingPolicyTest.cpp index cf1ba7b789563..07d92e2960f04 100644 --- a/fboss/cli/fboss2/test/config/CmdConfigBgpPolicyRoutingPolicyTest.cpp +++ b/fboss/cli/fboss2/test/config/CmdConfigBgpPolicyRoutingPolicyTest.cpp @@ -1,4 +1,4 @@ -// (c) Meta Platforms, Inc. and affiliates. Confidential and proprietary. +// Copyright (c) Meta Platforms, Inc. and affiliates. #include #include diff --git a/fboss/cli/fboss2/test/config/CmdDeleteBgpPolicyRoutingPolicyTest.cpp b/fboss/cli/fboss2/test/config/CmdDeleteBgpPolicyRoutingPolicyTest.cpp index 00b002a25826c..4d944c6ebc0f9 100644 --- a/fboss/cli/fboss2/test/config/CmdDeleteBgpPolicyRoutingPolicyTest.cpp +++ b/fboss/cli/fboss2/test/config/CmdDeleteBgpPolicyRoutingPolicyTest.cpp @@ -1,4 +1,4 @@ -// (c) Meta Platforms, Inc. and affiliates. Confidential and proprietary. +// Copyright (c) Meta Platforms, Inc. and affiliates. #include #include diff --git a/fboss/cli/fboss2/test/integration_test/ConfigBgpPolicyRoutingPolicyTest.cpp b/fboss/cli/fboss2/test/integration_test/ConfigBgpPolicyRoutingPolicyTest.cpp index a4d18c88f2f6c..5156c321748fb 100644 --- a/fboss/cli/fboss2/test/integration_test/ConfigBgpPolicyRoutingPolicyTest.cpp +++ b/fboss/cli/fboss2/test/integration_test/ConfigBgpPolicyRoutingPolicyTest.cpp @@ -1,4 +1,4 @@ -// (c) Meta Platforms, Inc. and affiliates. Confidential and proprietary. +// Copyright (c) Meta Platforms, Inc. and affiliates. /** * End-to-end tests for `fboss2-dev config protocol bgp policy routing-policy