diff --git a/CMakeLists.txt b/CMakeLists.txt index 8102b35c76c5a..49516f0d1423a 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -881,6 +881,101 @@ add_fbthrift_cpp_library( fboss/lib/i2c/i2c_controller_stats.thrift ) +# BGP thrift files +# Configerator BGP policy thrift files +add_fbthrift_cpp_library( + cfgr_bgp_attr_cpp2 + configerator/structs/neteng/fboss/bgp/if/bgp_attr.thrift + OPTIONS + json + reflection +) + +add_fbthrift_cpp_library( + cfgr_nsf_policy_cpp2 + configerator/structs/neteng/bgp_policy/thrift/nsf_policy.thrift + OPTIONS + json + reflection +) + +add_fbthrift_cpp_library( + cfgr_routing_policy_cpp2 + configerator/structs/neteng/bgp_policy/thrift/routing_policy.thrift + OPTIONS + json + reflection +) + +add_fbthrift_cpp_library( + cfgr_bgp_policy_cpp2 + configerator/structs/neteng/bgp_policy/thrift/bgp_policy.thrift + OPTIONS + json + reflection + DEPENDS + cfgr_nsf_policy_cpp2 + cfgr_routing_policy_cpp2 +) + +add_fbthrift_cpp_library( + cfgr_rib_policy_cpp2 + configerator/structs/neteng/bgp_policy/thrift/rib_policy.thrift + OPTIONS + json + reflection + DEPENDS + cfgr_bgp_attr_cpp2 + cfgr_bgp_policy_cpp2 + cfgr_routing_policy_cpp2 +) + +add_fbthrift_cpp_library( + bgp_config_cpp2 + configerator/structs/neteng/fboss/bgp/bgp_config.thrift + OPTIONS + json + reflection + DEPENDS + cfgr_bgp_attr_cpp2 + cfgr_bgp_policy_cpp2 +) + +add_fbthrift_cpp_library( + policy_thrift_cpp2 + neteng/fboss/bgp/if/policy_thrift.thrift + OPTIONS + json + reflection +) + +add_fbthrift_cpp_library( + bgp_thrift_cpp2 + neteng/fboss/bgp/if/bgp_thrift.thrift + SERVICES + TBgpService + OPTIONS + json + reflection + DEPENDS + cfgr_bgp_attr_cpp2 + bgp_config_cpp2 + cfgr_bgp_policy_cpp2 + cfgr_rib_policy_cpp2 + policy_thrift_cpp2 + fb303_cpp2 +) + +add_fbthrift_cpp_library( + bgp_summary_cpp2 + fboss/cli/fboss2/commands/show/bgp/summary/bgp_summary.thrift + OPTIONS + json + reflection + DEPENDS + bgp_thrift_cpp2 +) + add_fbthrift_cpp_library( io_stats_cpp2 fboss/lib/if/io_stats.thrift diff --git a/cmake/CliFboss2.cmake b/cmake/CliFboss2.cmake index 994e61995250a..bbbdd0547856e 100644 --- a/cmake/CliFboss2.cmake +++ b/cmake/CliFboss2.cmake @@ -406,6 +406,7 @@ add_library(fboss2_lib fboss/cli/fboss2/CmdStreamHandler.h fboss/cli/fboss2/CmdStreamHandler.cpp fboss/cli/fboss2/CmdStreamHandlerImpl.cpp + fboss/cli/fboss2/CmdHandlerImplBgp.cpp fboss/cli/fboss2/CmdArgsLists.cpp fboss/cli/fboss2/CmdList.cpp fboss/cli/fboss2/CmdLocalOptions.cpp @@ -572,6 +573,46 @@ add_library(fboss2_lib fboss/cli/fboss2/commands/show/transceiver/eeprom/CmdShowTransceiverEeprom.cpp fboss/cli/fboss2/commands/show/transceiver/eeprom/CmdShowTransceiverEepromDump.h fboss/cli/fboss2/commands/show/transceiver/eeprom/CmdShowTransceiverEepromDump.cpp + fboss/cli/fboss2/commands/show/bgp/CmdShowUtils.h + fboss/cli/fboss2/commands/show/bgp/CmdShowUtils.cpp + fboss/cli/fboss2/commands/show/bgp/CmdShowVersionBgp.h + fboss/cli/fboss2/commands/show/bgp/CmdShowVersionBgp.cpp + fboss/cli/fboss2/commands/show/bgp/CmdShowBgpOriginatedRoutes.h + fboss/cli/fboss2/commands/show/bgp/CmdShowBgpOriginatedRoutes.cpp + fboss/cli/fboss2/commands/show/bgp/changelist/CmdShowBgpChangelist.h + fboss/cli/fboss2/commands/show/bgp/changelist/CmdShowBgpChangelist.cpp + fboss/cli/fboss2/commands/show/bgp/config/CmdShowConfigRunningBgp.h + fboss/cli/fboss2/commands/show/bgp/config/CmdShowConfigRunningBgp.cpp + fboss/cli/fboss2/commands/show/bgp/config/CmdShowConfigTraits.h + fboss/cli/fboss2/commands/show/bgp/shadowrib/CmdShowBgpShadowRib.h + fboss/cli/fboss2/commands/show/bgp/shadowrib/CmdShowBgpShadowRib.cpp + fboss/cli/fboss2/commands/show/bgp/stats/CmdShowBgpStatsAttrs.h + fboss/cli/fboss2/commands/show/bgp/stats/CmdShowBgpStatsEntries.h + fboss/cli/fboss2/commands/show/bgp/stats/CmdShowBgpStatsEntries.cpp + fboss/cli/fboss2/commands/show/bgp/stats/CmdShowBgpStatsPolicy.h + fboss/cli/fboss2/commands/show/bgp/stats/CmdShowBgpStatsPolicy.cpp + fboss/cli/fboss2/commands/show/bgp/summary/CmdShowBgpSummary.h + fboss/cli/fboss2/commands/show/bgp/summary/egress/CmdShowBgpSummaryEgress.h + fboss/cli/fboss2/commands/show/bgp/summary/egress/CmdShowBgpSummaryEgress.cpp + fboss/cli/fboss2/commands/show/bgp/table/CmdShowBgpTable.h + fboss/cli/fboss2/commands/show/bgp/table/CmdShowBgpTable.cpp + fboss/cli/fboss2/commands/show/bgp/table/CmdShowBgpTableCommunity.h + fboss/cli/fboss2/commands/show/bgp/table/CmdShowBgpTableDetail.h + fboss/cli/fboss2/commands/show/bgp/table/CmdShowBgpTableMoreSpecifics.h + fboss/cli/fboss2/commands/show/bgp/table/CmdShowBgpTablePrefix.h + fboss/cli/fboss2/commands/show/bgp/neighbors/CmdShowBgpNeighbors.h + fboss/cli/fboss2/commands/show/bgp/neighbors/session_id/CmdBgpNeighborsSessionId.h + fboss/cli/fboss2/commands/show/bgp/neighbors/advertised/BgpNeighborsAdvertisedDryRun.h + fboss/cli/fboss2/commands/show/bgp/neighbors/advertised/BgpNeighborsAdvertisedPostPolicy.h + fboss/cli/fboss2/commands/show/bgp/neighbors/advertised/BgpNeighborsAdvertisedPrePolicy.h + fboss/cli/fboss2/commands/show/bgp/neighbors/advertised/BgpNeighborsAdvertisedRejected.h + fboss/cli/fboss2/commands/show/bgp/neighbors/received/BgpNeighborsReceivedPostPolicy.h + fboss/cli/fboss2/commands/show/bgp/neighbors/received/BgpNeighborsReceivedPrePolicy.h + fboss/cli/fboss2/commands/show/bgp/neighbors/received/BgpNeighborsReceivedRejected.h + fboss/cli/fboss2/commands/show/bgp/stream/CmdShowBgpStreamSummary.h + fboss/cli/fboss2/commands/show/bgp/stream/CmdShowBgpStreamSubscriber.h + fboss/cli/fboss2/commands/show/bgp/stream/subscriber/CmdShowBgpStreamSubscriberPrePolicy.h + fboss/cli/fboss2/commands/show/bgp/stream/subscriber/CmdShowBgpStreamSubscriberPostPolicy.h fboss/cli/fboss2/commands/start/pcap/CmdStartPcap.h fboss/cli/fboss2/commands/start/pcap/CmdStartPcap.cpp fboss/cli/fboss2/commands/stop/pcap/CmdStopPcap.h @@ -623,6 +664,14 @@ target_link_libraries(fboss2_lib Folly::folly input_balance_util cli_model + cfgr_bgp_attr_cpp2 + cfgr_bgp_policy_cpp2 + cfgr_rib_policy_cpp2 + cfgr_routing_policy_cpp2 + cfgr_nsf_policy_cpp2 + policy_thrift_cpp2 + bgp_thrift_cpp2 + bgp_summary_cpp2 show_acl_model show_agent_model show_aggregateport_model diff --git a/cmake/CliFboss2Test.cmake b/cmake/CliFboss2Test.cmake index b863416541657..f7733b0e01831 100644 --- a/cmake/CliFboss2Test.cmake +++ b/cmake/CliFboss2Test.cmake @@ -71,6 +71,32 @@ add_executable(fboss2_cmd_test fboss/cli/fboss2/test/CmdShowRouteSummaryTest.cpp fboss/cli/fboss2/test/CmdShowTeFlowTest.cpp # fboss/cli/fboss2/test/CmdShowTransceiverTest.cpp - excluded (depends on configerator bgp namespace) + fboss/cli/fboss2/test/CmdBgpTestUtils.cpp + fboss/cli/fboss2/test/CmdShowBgpChangelistTest.cpp + fboss/cli/fboss2/test/CmdShowBgpNeighborsAdvertisedPostPolicyTest.cpp + fboss/cli/fboss2/test/CmdShowBgpNeighborsAdvertisedPrePolicyTest.cpp + fboss/cli/fboss2/test/CmdShowBgpNeighborsAdvertisedRejectedTest.cpp + fboss/cli/fboss2/test/CmdShowBgpNeighborsReceivedPostPolicyTest.cpp + fboss/cli/fboss2/test/CmdShowBgpNeighborsReceivedPrePolicyTest.cpp + fboss/cli/fboss2/test/CmdShowBgpNeighborsReceivedRejectedTest.cpp + fboss/cli/fboss2/test/CmdShowBgpNeighborsTest.cpp + fboss/cli/fboss2/test/CmdShowBgpOriginatedRoutesTest.cpp + fboss/cli/fboss2/test/CmdShowBgpShadowRibTest.cpp + fboss/cli/fboss2/test/CmdShowBgpStatsAttrsTest.cpp + fboss/cli/fboss2/test/CmdShowBgpStatsEntriesTest.cpp + fboss/cli/fboss2/test/CmdShowBgpStatsPolicyTest.cpp + fboss/cli/fboss2/test/CmdShowBgpStreamSubscriberTest.cpp + fboss/cli/fboss2/test/CmdShowBgpStreamSummaryTest.cpp + fboss/cli/fboss2/test/CmdShowBgpSummaryEgressTest.cpp + fboss/cli/fboss2/test/CmdShowBgpSummaryTest.cpp + fboss/cli/fboss2/test/CmdShowBgpTableCommunityTest.cpp + fboss/cli/fboss2/test/CmdShowBgpTableDetailTest.cpp + fboss/cli/fboss2/test/CmdShowBgpTableMoreSpecificsTest.cpp + fboss/cli/fboss2/test/CmdShowBgpTablePrefixTest.cpp + fboss/cli/fboss2/test/CmdShowBgpTableTest.cpp + fboss/cli/fboss2/test/CmdShowConfigTestUtils.cpp + fboss/cli/fboss2/test/config/CmdConfigTestBase.cpp + fboss/cli/fboss2/test/config/BgpConfigSessionTest.cpp fboss/cli/fboss2/test/CmdStartPcapTest.cpp fboss/cli/fboss2/test/CmdStopPcapTest.cpp fboss/cli/fboss2/test/GitTest.cpp diff --git a/fboss/cli/fboss2/BUCK b/fboss/cli/fboss2/BUCK index 55ee76758b2f9..8b537add55fe8 100644 --- a/fboss/cli/fboss2/BUCK +++ b/fboss/cli/fboss2/BUCK @@ -228,6 +228,8 @@ cpp_library( "//folly/logging:logging", "//thrift/lib/cpp2/async:rocket_client_channel", "//thrift/lib/cpp2/op:get", + "//thrift/lib/cpp2/visitation:visitation", + "//thrift/lib/thrift:metadata-cpp2-types", ], exported_external_deps = [ "CLI11", @@ -339,6 +341,36 @@ cpp_library( "commands/show/facebook/bgp/updategroup/CmdShowBgpUpdateGroup.h", "commands/start/facebook/bgp/profiler/CmdStartBgpProfiler.h", "commands/stop/facebook/bgp/profiler/CmdStopBgpProfiler.h", + "commands/show/bgp/CmdShowBgpInitializationEvents.h", + "commands/show/bgp/CmdShowBgpOriginatedRoutes.h", + "commands/show/bgp/CmdShowUtils.h", + "commands/show/bgp/CmdShowVersionBgp.h", + "commands/show/bgp/changelist/CmdShowBgpChangelist.h", + "commands/show/bgp/config/CmdShowConfigRunningBgp.h", + "commands/show/bgp/neighbors/CmdShowBgpNeighbors.h", + "commands/show/bgp/neighbors/advertised/BgpNeighborsAdvertisedDryRun.h", + "commands/show/bgp/neighbors/advertised/BgpNeighborsAdvertisedPostPolicy.h", + "commands/show/bgp/neighbors/advertised/BgpNeighborsAdvertisedPrePolicy.h", + "commands/show/bgp/neighbors/advertised/BgpNeighborsAdvertisedRejected.h", + "commands/show/bgp/neighbors/received/BgpNeighborsReceivedPostPolicy.h", + "commands/show/bgp/neighbors/received/BgpNeighborsReceivedPrePolicy.h", + "commands/show/bgp/neighbors/received/BgpNeighborsReceivedRejected.h", + "commands/show/bgp/neighbors/session_id/CmdBgpNeighborsSessionId.h", + "commands/show/bgp/shadowrib/CmdShowBgpShadowRib.h", + "commands/show/bgp/stats/CmdShowBgpStatsAttrs.h", + "commands/show/bgp/stats/CmdShowBgpStatsEntries.h", + "commands/show/bgp/stats/CmdShowBgpStatsPolicy.h", + "commands/show/bgp/stream/CmdShowBgpStreamSubscriber.h", + "commands/show/bgp/stream/CmdShowBgpStreamSummary.h", + "commands/show/bgp/stream/subscriber/CmdShowBgpStreamSubscriberPostPolicy.h", + "commands/show/bgp/stream/subscriber/CmdShowBgpStreamSubscriberPrePolicy.h", + "commands/show/bgp/summary/CmdShowBgpSummary.h", + "commands/show/bgp/summary/egress/CmdShowBgpSummaryEgress.h", + "commands/show/bgp/table/CmdShowBgpTable.h", + "commands/show/bgp/table/CmdShowBgpTableCommunity.h", + "commands/show/bgp/table/CmdShowBgpTableDetail.h", + "commands/show/bgp/table/CmdShowBgpTableMoreSpecifics.h", + "commands/show/bgp/table/CmdShowBgpTablePrefix.h", ], exported_deps = [ "fbsource//third-party/fmt:fmt", @@ -347,9 +379,11 @@ cpp_library( ":cmd-handler", ":cmd-show-utils", # @manual ":table-utils", + "//configerator/structs/neteng/bgp_policy/thrift:bgp_policy-cpp2-types", "//configerator/structs/neteng/bgp_policy/thrift:rib_policy-cpp2-types", "//configerator/structs/neteng/fboss/bgp:bgp_config-cpp2-types", "//configerator/structs/neteng/fboss/bgp/if:bgp_attr-cpp2-types", + "//fboss/cli/fboss2/commands/show/bgp/summary:bgp_summary-cpp2-types", "//fboss/cli/fboss2/commands/show/facebook/bgp/ribpolicy/criteria:model-cpp2-types", "//fboss/cli/fboss2/commands/show/facebook/bgp/summary:bgp_summary-cpp2-types", "//fboss/cli/fboss2/commands/show/facebook/bgp/updategroup:bgp_update_group-cpp2-types", @@ -844,9 +878,11 @@ cpp_library( "//fboss/cli/fboss2/commands/show/facebook:model-cpp2-types", "//fboss/cli/fboss2/commands/show/facebook/bgp/ribpolicy/criteria:model-cpp2-types", "//fboss/cli/fboss2/commands/show/facebook/bgp/summary:bgp_summary-cpp2-types", + "//fboss/cli/fboss2/commands/show/facebook/bgp/summary:bgp_summary-cpp2-visitation", "//fboss/cli/fboss2/commands/show/facebook/bgp/techsupport:bgp_techsupport-cpp2-types", "//fboss/cli/fboss2/commands/show/facebook/bgp/updategroup:bgp_update_group-cpp2-types", "//fboss/cli/fboss2/commands/show/facebook/bmc:model-cpp2-types", + "//fboss/cli/fboss2/commands/show/facebook/bmc:model-cpp2-visitation", "//fboss/cli/fboss2/commands/show/facebook/debugdump:model-cpp2-types", "//fboss/cli/fboss2/commands/show/facebook/environment:model-cpp2-types", "//fboss/cli/fboss2/commands/show/facebook/environment/fan:model-cpp2-types", diff --git a/fboss/cli/fboss2/CmdHandlerImplBgp.cpp b/fboss/cli/fboss2/CmdHandlerImplBgp.cpp new file mode 100644 index 0000000000000..48d132502026e --- /dev/null +++ b/fboss/cli/fboss2/CmdHandlerImplBgp.cpp @@ -0,0 +1,111 @@ +/* + * 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/CmdHandler.cpp" + +// NOLINTBEGIN(misc-include-cleaner) +// IWYU pragma: begin_keep +#include "fboss/cli/fboss2/commands/show/bgp/CmdShowBgpOriginatedRoutes.h" +#include "fboss/cli/fboss2/commands/show/bgp/CmdShowVersionBgp.h" +#include "fboss/cli/fboss2/commands/show/bgp/changelist/CmdShowBgpChangelist.h" +#include "fboss/cli/fboss2/commands/show/bgp/config/CmdShowConfigRunningBgp.h" +#include "fboss/cli/fboss2/commands/show/bgp/neighbors/CmdShowBgpNeighbors.h" +#include "fboss/cli/fboss2/commands/show/bgp/neighbors/advertised/BgpNeighborsAdvertisedDryRun.h" +#include "fboss/cli/fboss2/commands/show/bgp/neighbors/advertised/BgpNeighborsAdvertisedPostPolicy.h" +#include "fboss/cli/fboss2/commands/show/bgp/neighbors/advertised/BgpNeighborsAdvertisedPrePolicy.h" +#include "fboss/cli/fboss2/commands/show/bgp/neighbors/advertised/BgpNeighborsAdvertisedRejected.h" +#include "fboss/cli/fboss2/commands/show/bgp/neighbors/received/BgpNeighborsReceivedPostPolicy.h" +#include "fboss/cli/fboss2/commands/show/bgp/neighbors/received/BgpNeighborsReceivedPrePolicy.h" +#include "fboss/cli/fboss2/commands/show/bgp/neighbors/received/BgpNeighborsReceivedRejected.h" +#include "fboss/cli/fboss2/commands/show/bgp/neighbors/session_id/CmdBgpNeighborsSessionId.h" +#include "fboss/cli/fboss2/commands/show/bgp/shadowrib/CmdShowBgpShadowRib.h" +#include "fboss/cli/fboss2/commands/show/bgp/stats/CmdShowBgpStatsAttrs.h" +#include "fboss/cli/fboss2/commands/show/bgp/stats/CmdShowBgpStatsEntries.h" +#include "fboss/cli/fboss2/commands/show/bgp/stats/CmdShowBgpStatsPolicy.h" +#include "fboss/cli/fboss2/commands/show/bgp/stream/CmdShowBgpStreamSubscriber.h" +#include "fboss/cli/fboss2/commands/show/bgp/stream/CmdShowBgpStreamSummary.h" +#include "fboss/cli/fboss2/commands/show/bgp/stream/subscriber/CmdShowBgpStreamSubscriberPostPolicy.h" +#include "fboss/cli/fboss2/commands/show/bgp/stream/subscriber/CmdShowBgpStreamSubscriberPrePolicy.h" +#include "fboss/cli/fboss2/commands/show/bgp/summary/CmdShowBgpSummary.h" +#include "fboss/cli/fboss2/commands/show/bgp/summary/egress/CmdShowBgpSummaryEgress.h" +#include "fboss/cli/fboss2/commands/show/bgp/table/CmdShowBgpTable.h" +#include "fboss/cli/fboss2/commands/show/bgp/table/CmdShowBgpTableCommunity.h" +#include "fboss/cli/fboss2/commands/show/bgp/table/CmdShowBgpTableDetail.h" +#include "fboss/cli/fboss2/commands/show/bgp/table/CmdShowBgpTableMoreSpecifics.h" +#include "fboss/cli/fboss2/commands/show/bgp/table/CmdShowBgpTablePrefix.h" +// IWYU pragma: end_keep +// NOLINTEND(misc-include-cleaner) + +namespace facebook::fboss { + +// Explicit template instantiations for BGP commands +template void +CmdHandler::run(); +template void +CmdHandler::run(); +template void CmdHandler::run(); +template void CmdHandler::run(); +template void CmdHandler< + BgpNeighborsAdvertisedDryRun, + BgpNeighborsAdvertisedDryRunTraits>::run(); +template void CmdHandler< + BgpNeighborsAdvertisedPostPolicy, + BgpNeighborsAdvertisedPostPolicyTraits>::run(); +template void CmdHandler< + BgpNeighborsAdvertisedPrePolicy, + BgpNeighborsAdvertisedPrePolicyTraits>::run(); +template void CmdHandler< + BgpNeighborsAdvertisedRejected, + BgpNeighborsAdvertisedRejectedTraits>::run(); +template void CmdHandler< + BgpNeighborsReceivedPostPolicy, + BgpNeighborsReceivedPostPolicyTraits>::run(); +template void CmdHandler< + BgpNeighborsReceivedPrePolicy, + BgpNeighborsReceivedPrePolicyTraits>::run(); +template void CmdHandler< + BgpNeighborsReceivedRejected, + BgpNeighborsReceivedRejectedTraits>::run(); +template void +CmdHandler::run(); +template void +CmdHandler::run(); +template void CmdHandler::run(); +template void +CmdHandler::run(); +template void +CmdHandler::run(); +template void +CmdHandler::run(); +template void +CmdHandler::run(); +template void CmdHandler< + CmdShowBgpStreamSubscriberPostPolicy, + CmdShowBgpStreamSubscriberPostPolicyTraits>::run(); +template void CmdHandler< + CmdShowBgpStreamSubscriberPrePolicy, + CmdShowBgpStreamSubscriberPrePolicyTraits>::run(); +template void +CmdHandler::run(); +template void CmdHandler::run(); +template void +CmdHandler::run(); +template void CmdHandler::run(); +template void +CmdHandler::run(); +template void +CmdHandler::run(); +template void CmdHandler< + CmdShowBgpTableMoreSpecifics, + CmdShowBgpTableMoreSpecificsTraits>::run(); +template void +CmdHandler::run(); + +} // namespace facebook::fboss diff --git a/fboss/cli/fboss2/CmdList.cpp b/fboss/cli/fboss2/CmdList.cpp index 72b789db63c12..6a66f164fcd86 100644 --- a/fboss/cli/fboss2/CmdList.cpp +++ b/fboss/cli/fboss2/CmdList.cpp @@ -10,8 +10,8 @@ #include "fboss/cli/fboss2/CmdList.h" -#include -#include "fboss/cli/fboss2/CmdHandler.h" +#include // NOLINT(misc-include-cleaner) +#include "fboss/cli/fboss2/CmdHandler.h" // NOLINT(misc-include-cleaner) #include "fboss/cli/fboss2/commands/bounce/interface/CmdBounceInterface.h" #include "fboss/cli/fboss2/commands/clear/CmdClearArp.h" #include "fboss/cli/fboss2/commands/clear/CmdClearInterfaceCounters.h" @@ -99,6 +99,36 @@ #include "fboss/cli/fboss2/commands/stream/fsdb/CmdStreamSubFsdbOperState.h" #include "fboss/cli/fboss2/commands/stream/fsdb/CmdStreamSubFsdbOperStats.h" +// BGP show commands +#include "fboss/cli/fboss2/commands/show/bgp/CmdShowBgpOriginatedRoutes.h" +#include "fboss/cli/fboss2/commands/show/bgp/CmdShowVersionBgp.h" +#include "fboss/cli/fboss2/commands/show/bgp/changelist/CmdShowBgpChangelist.h" +#include "fboss/cli/fboss2/commands/show/bgp/config/CmdShowConfigRunningBgp.h" +#include "fboss/cli/fboss2/commands/show/bgp/neighbors/CmdShowBgpNeighbors.h" +#include "fboss/cli/fboss2/commands/show/bgp/neighbors/advertised/BgpNeighborsAdvertisedDryRun.h" +#include "fboss/cli/fboss2/commands/show/bgp/neighbors/advertised/BgpNeighborsAdvertisedPostPolicy.h" +#include "fboss/cli/fboss2/commands/show/bgp/neighbors/advertised/BgpNeighborsAdvertisedPrePolicy.h" +#include "fboss/cli/fboss2/commands/show/bgp/neighbors/advertised/BgpNeighborsAdvertisedRejected.h" +#include "fboss/cli/fboss2/commands/show/bgp/neighbors/received/BgpNeighborsReceivedPostPolicy.h" +#include "fboss/cli/fboss2/commands/show/bgp/neighbors/received/BgpNeighborsReceivedPrePolicy.h" +#include "fboss/cli/fboss2/commands/show/bgp/neighbors/received/BgpNeighborsReceivedRejected.h" +#include "fboss/cli/fboss2/commands/show/bgp/neighbors/session_id/CmdBgpNeighborsSessionId.h" +#include "fboss/cli/fboss2/commands/show/bgp/shadowrib/CmdShowBgpShadowRib.h" +#include "fboss/cli/fboss2/commands/show/bgp/stats/CmdShowBgpStatsAttrs.h" +#include "fboss/cli/fboss2/commands/show/bgp/stats/CmdShowBgpStatsEntries.h" +#include "fboss/cli/fboss2/commands/show/bgp/stats/CmdShowBgpStatsPolicy.h" +#include "fboss/cli/fboss2/commands/show/bgp/stream/CmdShowBgpStreamSubscriber.h" +#include "fboss/cli/fboss2/commands/show/bgp/stream/CmdShowBgpStreamSummary.h" +#include "fboss/cli/fboss2/commands/show/bgp/stream/subscriber/CmdShowBgpStreamSubscriberPostPolicy.h" +#include "fboss/cli/fboss2/commands/show/bgp/stream/subscriber/CmdShowBgpStreamSubscriberPrePolicy.h" +#include "fboss/cli/fboss2/commands/show/bgp/summary/CmdShowBgpSummary.h" +#include "fboss/cli/fboss2/commands/show/bgp/summary/egress/CmdShowBgpSummaryEgress.h" +#include "fboss/cli/fboss2/commands/show/bgp/table/CmdShowBgpTable.h" +#include "fboss/cli/fboss2/commands/show/bgp/table/CmdShowBgpTableCommunity.h" +#include "fboss/cli/fboss2/commands/show/bgp/table/CmdShowBgpTableDetail.h" +#include "fboss/cli/fboss2/commands/show/bgp/table/CmdShowBgpTableMoreSpecifics.h" +#include "fboss/cli/fboss2/commands/show/bgp/table/CmdShowBgpTablePrefix.h" + namespace facebook::fboss { const CommandTree& kCommandTree() { @@ -138,6 +168,141 @@ const CommandTree& kCommandTree() { validFilterHandler, argTypeHandler}, + {"show", + "bgp", + "Show BGP information", + {{"changelist", + "Show BGP changelist", + commandHandler, + argTypeHandler}, + + {"config", + "Show BGP configuration", + {{"running", + "Show running BGP configuration", + commandHandler, + argTypeHandler}}}, + + {"neighbors", + "Show BGP neighbors", + commandHandler, + argTypeHandler, + {{"advertised", + "Show BGP advertised routes", + {{"dry-run", + "Show BGP advertised routes (dry-run)", + commandHandler, + argTypeHandler}, + {"post-policy", + "Show BGP advertised routes (post-policy)", + commandHandler, + argTypeHandler}, + {"pre-policy", + "Show BGP advertised routes (pre-policy)", + commandHandler, + argTypeHandler}, + {"rejected", + "Show BGP advertised rejected routes", + commandHandler, + argTypeHandler}}}, + {"received", + "Show BGP received routes", + {{"post-policy", + "Show BGP received routes (post-policy)", + commandHandler, + argTypeHandler}, + {"pre-policy", + "Show BGP received routes (pre-policy)", + commandHandler, + argTypeHandler}, + {"rejected", + "Show BGP received rejected routes", + commandHandler, + argTypeHandler}}}, + {"session-id", + "Show BGP neighbor by session ID", + commandHandler, + argTypeHandler}}}, + + {"originated-routes", + "Show BGP originated routes", + commandHandler, + argTypeHandler}, + + {"shadowrib", + "Show BGP shadow RIB", + commandHandler, + argTypeHandler}, + + {"stats", + "Show BGP statistics", + {{"attrs", + "Show BGP attribute statistics", + commandHandler, + argTypeHandler}, + {"entries", + "Show BGP entry statistics", + commandHandler, + argTypeHandler}, + {"policy", + "Show BGP policy statistics", + commandHandler, + argTypeHandler}}}, + + {"stream", + "Show BGP stream information", + {{"subscriber", + "Show BGP stream subscribers", + commandHandler, + argTypeHandler, + {{"post-policy", + "Show BGP stream subscribers (post-policy)", + commandHandler, + argTypeHandler}, + {"pre-policy", + "Show BGP stream subscribers (pre-policy)", + commandHandler, + argTypeHandler}}}, + {"summary", + "Show BGP stream summary", + commandHandler, + argTypeHandler}}}, + + {"summary", + "Show BGP summary", + commandHandler, + argTypeHandler, + {{"egress", + "Show BGP summary egress", + commandHandler, + argTypeHandler}}}, + + {"table", + "Show BGP routing table", + commandHandler, + argTypeHandler, + {{"community", + "Show BGP routes by community", + commandHandler, + argTypeHandler}, + {"detail", + "Show BGP table details", + commandHandler, + argTypeHandler}, + {"more-specifics", + "Show BGP more specific routes", + commandHandler, + argTypeHandler}, + {"prefix", + "Show BGP routes by prefix", + commandHandler, + argTypeHandler}}}, + + {"version", + "Show BGP version", + commandHandler, + argTypeHandler}}}, + {"show", "fabric", "Show Fabric connectivity", diff --git a/fboss/cli/fboss2/commands/config/protocol/bgp/BgpConfigSession.cpp b/fboss/cli/fboss2/commands/config/protocol/bgp/BgpConfigSession.cpp index 483e28533a9a6..f5378e3609638 100644 --- a/fboss/cli/fboss2/commands/config/protocol/bgp/BgpConfigSession.cpp +++ b/fboss/cli/fboss2/commands/config/protocol/bgp/BgpConfigSession.cpp @@ -133,6 +133,8 @@ void BgpConfigSession::clearSession() { if (stubMode_) { std::cout << "[STUB MODE] Would clear session file: " << sessionConfigPath_ << std::endl; + bgpConfig_ = getDefaultBgpConfig(); + configLoaded_ = false; return; } diff --git a/fboss/cli/fboss2/commands/show/bgp/CmdShowBgpInitializationEvents.h b/fboss/cli/fboss2/commands/show/bgp/CmdShowBgpInitializationEvents.h new file mode 100644 index 0000000000000..e8f1b36f56b6a --- /dev/null +++ b/fboss/cli/fboss2/commands/show/bgp/CmdShowBgpInitializationEvents.h @@ -0,0 +1,37 @@ +/* + * 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 "fboss/cli/fboss2/CmdHandler.h" +#include "neteng/fboss/bgp/if/gen-cpp2/bgp_thrift_types.h" + +namespace facebook::fboss { + +struct CmdShowBgpInitializationEventsTraits : public ReadCommandTraits { + static constexpr utils::ObjectArgTypeId ObjectArgTypeId = + utils::ObjectArgTypeId::OBJECT_ARG_TYPE_ID_NONE; + using ObjectArgType = std::monostate; + using RetType = ::facebook::neteng::fboss::bgp::thrift:: + map_bgp_thriftBgpInitializationEvent_i64_cpptemplate_stdunordered_map_895; +}; + +class CmdShowBgpInitializationEvents + : public CmdHandler< + CmdShowBgpInitializationEvents, + CmdShowBgpInitializationEventsTraits> { + public: + using RetType = CmdShowBgpInitializationEventsTraits::RetType; + + RetType queryClient(const HostInfo& hostInfo); + void printOutput(const RetType& events, std::ostream& out = std::cout); +}; + +} // namespace facebook::fboss diff --git a/fboss/cli/fboss2/commands/show/bgp/CmdShowBgpOriginatedRoutes.cpp b/fboss/cli/fboss2/commands/show/bgp/CmdShowBgpOriginatedRoutes.cpp new file mode 100644 index 0000000000000..e5687a865b51c --- /dev/null +++ b/fboss/cli/fboss2/commands/show/bgp/CmdShowBgpOriginatedRoutes.cpp @@ -0,0 +1,83 @@ +/* + * 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/show/bgp/CmdShowBgpOriginatedRoutes.h" + +#include + +#include "fboss/cli/fboss2/commands/show/bgp/CmdShowUtils.h" +#include "fboss/cli/fboss2/utils/CmdClientUtilsCommon.h" +#include "fboss/cli/fboss2/utils/Table.h" +#include "neteng/fboss/bgp/if/gen-cpp2/TBgpService.h" + +namespace facebook::fboss { +using facebook::fboss::utils::Table; +using facebook::neteng::fboss::bgp::thrift::TOriginatedRouteWithHost; + +CmdShowBgpOriginatedRoutes::RetType CmdShowBgpOriginatedRoutes::queryClient( + const HostInfo& hostInfo) { + std::vector routes; + auto client = utils::createClient>(hostInfo); + + client->sync_getOriginatedRoutes(routes); + TOriginatedRouteWithHost result; + result.tOriginatedRoutes() = std::move(routes); + result.host() = hostInfo.getName(); + result.oobName() = hostInfo.getOobName(); + result.ip() = hostInfo.getIpStr(); + return result; +} + +void CmdShowBgpOriginatedRoutes::printOutput( + const RetType& originatedRouteWithHost, + std::ostream& out) { + // Table provides a cleaner display and reduces the spacing needed + // for the output to the minimum. + Table table; + table.setHeader( + {"Prefix", + "Communities", + "Supporting Route Cnt", + "Minimum supporting route", + "Require Nexthop Resolution"}); + + for (const auto& route : *originatedRouteWithHost.tOriginatedRoutes()) { + std::string ip_version; + const auto& route_prefix = *route.prefix(); + const auto ip_address = folly::IPAddress::fromBinary( + folly::ByteRange(folly::StringPiece(*route_prefix.prefix_bin()))); + + const auto prefix = + fmt::format("{}/{}", ip_address.str(), *route_prefix.num_bits()); + const HostInfo hostInfo( + *originatedRouteWithHost.host(), + *originatedRouteWithHost.oobName(), + folly::IPAddress(*originatedRouteWithHost.ip())); + const auto communities = printCommunities( + apache::thrift::can_throw(*route.communities()), + hostInfo, + true /* print community with multi-lines */); + + table.addRow( + {prefix, + communities, + folly::to( + folly::copy(route.supporting_route_count().value())), + folly::to( + folly::copy(route.minimum_supporting_routes().value())), + route.require_nexthop_resolution() + ? folly::to(*route.require_nexthop_resolution()) + : "N/A"}); + } + out << table << std::endl; +} + +} // namespace facebook::fboss diff --git a/fboss/cli/fboss2/commands/show/bgp/CmdShowBgpOriginatedRoutes.h b/fboss/cli/fboss2/commands/show/bgp/CmdShowBgpOriginatedRoutes.h new file mode 100644 index 0000000000000..0a34f8adb792c --- /dev/null +++ b/fboss/cli/fboss2/commands/show/bgp/CmdShowBgpOriginatedRoutes.h @@ -0,0 +1,38 @@ +/* + * 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 "fboss/cli/fboss2/CmdHandler.h" +#include "neteng/fboss/bgp/if/gen-cpp2/bgp_thrift_types.h" + +namespace facebook::fboss { +using facebook::neteng::fboss::bgp::thrift::TOriginatedRoute; + +struct CmdShowBgpOriginatedRoutesTraits : public ReadCommandTraits { + static constexpr utils::ObjectArgTypeId ObjectArgTypeId = + utils::ObjectArgTypeId::OBJECT_ARG_TYPE_ID_NONE; + using ObjectArgType = std::monostate; + using RetType = + facebook::neteng::fboss::bgp::thrift::TOriginatedRouteWithHost; +}; + +class CmdShowBgpOriginatedRoutes : public CmdHandler< + CmdShowBgpOriginatedRoutes, + CmdShowBgpOriginatedRoutesTraits> { + public: + using RetType = CmdShowBgpOriginatedRoutesTraits::RetType; + + RetType queryClient(const HostInfo& hostInfo); + void printOutput( + const RetType& originatedRouteWithHost, + std::ostream& out = std::cout); +}; +} // namespace facebook::fboss diff --git a/fboss/cli/fboss2/commands/show/bgp/CmdShowUtils.cpp b/fboss/cli/fboss2/commands/show/bgp/CmdShowUtils.cpp new file mode 100644 index 0000000000000..3dfe40b4a01a6 --- /dev/null +++ b/fboss/cli/fboss2/commands/show/bgp/CmdShowUtils.cpp @@ -0,0 +1,1387 @@ +/* + * 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. + * + */ + +#ifndef IS_OSS +#include +#include +#include +#endif + +#include + +#ifndef IS_OSS +#include "common/strings/StringUtil.h" +#include "common/time/TimeUtil.h" +#include "configerator/structs/neteng/fboss/push/forwarding_stack/gen-cpp2/fbpkg_map_types.h" +#else +#include "fboss/cli/fboss2/oss/NetTools.h" +#endif +#include "configerator/structs/neteng/fboss/bgp/if/gen-cpp2/bgp_attr_types.h" + +#include "configerator/structs/neteng/bgp_policy/thrift/gen-cpp2/bgp_policy_types.h" // NOLINT(misc-include-cleaner) +#include "fboss/agent/AddressUtil.h" +#include "fboss/cli/fboss2/CmdLocalOptions.h" +#include "fboss/cli/fboss2/commands/show/bgp/CmdShowUtils.h" +#include "fboss/cli/fboss2/utils/CmdClientUtilsCommon.h" +#include "fboss/cli/fboss2/utils/CmdUtilsCommon.h" +#include "fboss/cli/fboss2/utils/HostInfo.h" +#include "fboss/cli/fboss2/utils/Table.h" +#include "folly/IPAddress.h" +#include "folly/Range.h" +#include "folly/String.h" +#include "folly/json.h" +#include "folly/json/dynamic.h" +#include "neteng/fboss/bgp/if/gen-cpp2/TBgpService.h" +#include "neteng/fboss/bgp/if/gen-cpp2/bgp_thrift_types.h" +#ifndef IS_OSS +#include "nettools/bgplib/BgpStructs.h" +#include "nettools/bgplib/BgpUtil.h" +#include "nettools/bgplib/if/gen-cpp2/BgpStructs_types.h" +#include "nettools/skynet/if/gen-cpp2/Query_types.h" +#include "nettools/skynet/if/gen-cpp2/SkynetStructs_types.h" +#include "nettools/skynet/lib/cpp/SkynetThriftClient.h" +#endif +#include "thrift/lib/cpp/util/EnumUtils.h" + +namespace facebook::fboss { +using namespace neteng::fboss::bgp::thrift; +using folly::IPAddress; +using neteng::fboss::bgp_attr::TAsPath; +using neteng::fboss::bgp_attr::TAsPathSegType; +using neteng::fboss::bgp_attr::TBgpAfi; + +#ifdef IS_OSS +std::string formatBgpOrigin(int32_t origin) { + switch (origin) { + case 0: + return "BGP_ORIGIN_IGP"; + case 1: + return "BGP_ORIGIN_EGP"; + case 2: + return "BGP_ORIGIN_INCOMPLETE"; + default: + return fmt::format("BGP_ORIGIN_{}", origin); + } +} +#endif +using nettools::bgplib::BgpAttrCommunityC; + +using apache::thrift::Client; +using apache::thrift::util::enumNameSafe; +using facebook::fboss::utils::Table; +using facebook::neteng::fboss::bgp::thrift::TBgpAddPathNegotiated; +using facebook::neteng::fboss::bgp::thrift::TBgpService; +using facebook::neteng::fboss::bgp::thrift::TBgpSessionDetail; +using neteng::fboss::bgp::thrift::TBgpPeerState; + +#ifndef IS_OSS +using namespace facebook::nettools::skynet; +#endif + +void cmdShowVersion(CmdShowVersionTraits::RetType& binaryVersion) { + std::cout << "Package Name: " << binaryVersion["build_package_name"] + << std::endl; + std::cout << "Package Info: " << binaryVersion["build_package_info"] + << std::endl; + +#ifndef IS_OSS + auto package_version = getPackageId( + binaryVersion["build_package_version"], + binaryVersion["build_package_info"]); +#else + auto package_version = binaryVersion["build_package_version"]; +#endif + + std::cout << "Package Version: " << package_version << std::endl; + std::cout << "Build Details: " << std::endl; + std::cout << "\t Host: " << binaryVersion["build_host"] << std::endl; + std::cout << "\t Time: " << binaryVersion["build_time"] << std::endl; + std::cout << "\t User: " << binaryVersion["build_user"] << std::endl; + std::cout << "\t Path: " << binaryVersion["build_path"] << std::endl; + std::cout << "\t Platform: " << binaryVersion["build_platform"] << std::endl; + std::cout << "\t Revision: " << binaryVersion["build_revision"] << std::endl; +} + +#ifndef IS_OSS +// Convert the package version into human readable package ID +std::string getPackageId( + const std::string& packageVersion, + const std::string& packageInfo) { + using facebook::config::ConfigeratorConfig; + // Retrieve the mapped version ID to the UUID from the config file + constexpr std::string_view kFbpkgMapConfigPath = + "neteng/fboss/push/forwarding_stack/fbpkg_map"; + ConfigeratorConfig fbpkgmap_cfg( + kFbpkgMapConfigPath); + + if (auto cfgPtr = fbpkgmap_cfg.get()) { + const auto& map_versions = cfgPtr->versions().value(); + if (const auto& it = map_versions.find(packageInfo); + it != map_versions.end()) { + return it->second; + } + } + return packageVersion; +} +#endif + +const std::string printCommunities( + const std::vector& peer_communities, + const HostInfo& hostInfo, + bool isMultiLine) { + std::vector communities; + communities.reserve(peer_communities.size()); + for (const auto& comm : peer_communities) { + communities.emplace_back( + BgpAttrCommunityC::createBgpAttrCommunity( + folly::to(*comm.community())) + ->to_string()); + } + + auto communitySet = getCommunitySet(hostInfo); + const auto& communityNames = + nettools::bgplib::findCommunities(communities, communitySet); + + std::vector output; + for (const auto& [set, alias] : communityNames) { + if (alias != nettools::bgplib::kNullMessage && + folly::StringPiece(alias).startsWith("COMM_")) { + const std::string name = alias.substr(5); // Trim 'COMM_' from alias name + output.emplace_back(name + "/" + folly::join(',', set)); + } else { + const std::string name = alias; + output.emplace_back(name + "/" + folly::join(',', set)); + } + } + + return isMultiLine ? folly::join("\n", output) : folly::join(", ", output); +} + +#ifndef IS_OSS +/* The returned output is same as the upper function printCommunities but from + * the source of FBNet For each records, when it's going to print "Communities: + * COMMUNITY_NAME/12345:12345". Function printFBnetCommunities() gets the + * COMMUNITY_NAME from FBNet rather than bgpMnemonics + */ +const std::string printFBnetCommunities( + const std::vector& peer_communities) { + std::vector communities; + communities.reserve(peer_communities.size()); + for (const auto& comm : peer_communities) { + communities.emplace_back( + BgpAttrCommunityC::createBgpAttrCommunity( + folly::to(folly::copy(comm.community().value()))) + ->to_string()); + } + + const auto& communityNames = findCommunitiesByFBNet(communities); + + std::vector output; + for (const auto& [set, alias] : communityNames) { + const std::string name = alias; + output.emplace_back(name + "/" + folly::join(',', set)); + } + return folly::join(", ", output); +} +#endif // IS_OSS + +#ifndef IS_OSS +// Read bgp community data DesiredCommunity from FBNet through Skynet. +// DesiredCommunity has attributes: name, value, obj_uuid, network_type, +// origin_device and prefix_type, here we only access "name" and "value". +// getSkynetData() sends query to Skynet client With a query of including +// the required attribute and its require value, and the field attribute +// that will return. After response from Skynet client, getSkynetData() +// returns a vector that contains all the records that meet the requirement +std::vector getSkynetData( + std::string attributeValue, + std::string searchAttribute, + std::string field) { + SkynetThriftClient skynet{ + "skynet_thrift", + "fbcode:fboss/cli/fboss2/commands/show/bgp/CmdShowUtils.cpp"}; + std::vector resultSet; + auto& eventBase = *EventBaseManager::get()->getEventBase(); + Expr expr1; + expr1.name() = searchAttribute, expr1.op() = Op::EQUAL, + expr1.values() = std::vector({std::move(attributeValue)}); + Query query; + query.exprs() = std::vector({expr1}); + skynet.getObjects( + [&](ClientReceiveState&& state) { + std::vector results; + try { + SkynetThriftClient::recv_getObjects(results, state); + if (results.size() > 0) { + for (const auto& community : results) { + if (field == "name") { + resultSet.push_back(community.name().value()); + } else if (field == "value") { + resultSet.push_back(community.value().value()); + } + } + } + } catch (std::exception& e) { + LOG(INFO) << "error while running async: " << e.what() << std::endl; + } + }, + query, + {"name", "value"}); + eventBase.loop(); + return (resultSet); +} + +// Get the values from communityList and get the community source from FBNet +// Community souce are stores in doubleValueResult, singleValueResult +// for further use, and can help to mock Skynet results in unit_tests +// Get the potential names by the order: +// community with 2 values (addDoubleValueCommunity) +// community with only 1 value (addSingleValueCommunity) +// community without value (addNullCommunity) +const std::map, std::string> findCommunitiesByFBNet( + std::vector communityList) { + std::map singleName; + std::map, std::string> resultMap; + std::vector> doubleValueResult; + for (const std::string& value : communityList) { + std::vector nameList = getSkynetData(value, "value", "name"); + for (const std::string& name : nameList) { + doubleValueResult.push_back({name, value}); + } + } + resultMap = addDoubleValueCommunity( + std::move(resultMap), communityList, singleName, doubleValueResult); + std::vector singleValueResult; + for (std::map::iterator singleIterator = + singleName.begin(); + singleIterator != singleName.end(); + ++singleIterator) { + if (getSkynetData(singleIterator->first, "name", "value").size() == 1) { + singleValueResult.push_back(singleIterator->first); + } + } + resultMap = addSingleValueCommunity( + std::move(resultMap), communityList, singleName, singleValueResult); + return (addNullCommunity(std::move(resultMap), communityList)); +} + +// read the names at each value +// if it's a new name, store it to singleName +// if it's a second time, which means that it's a valid community +// remove from singleName and store it to resultMap +std::map, std::string> addDoubleValueCommunity( + std::map, std::string> resultMap, + std::vector& communityList, + std::map& singleName, + const std::vector>& doubleValueResult) { + for (std::vector skynetData : doubleValueResult) { + std::string communityName = *skynetData.begin(); + std::string communityValue = *(++skynetData.begin()); + std::map::iterator singleIterator = + singleName.find(communityName); + if (singleIterator != singleName.end()) { + resultMap.insert( + std::pair, std::string>( + {singleIterator->second, communityValue}, communityName)); + singleName.erase(communityName); + if (auto it = std::find( + communityList.begin(), + communityList.end(), + singleName.find(communityName)->second); + it != communityList.end()) { + communityList.erase(it); + } + if (auto it = std::find( + communityList.begin(), communityList.end(), communityValue); + it != communityList.end()) { + communityList.erase(it); + } + } else { + singleName.insert( + std::pair(communityName, communityValue)); + } + } + return (resultMap); +} + +// Read the names in singleValueResult if they only have 1 value, +// which means they are valid community with only one value, +// store them at resultMap +std::map, std::string> addSingleValueCommunity( + std::map, std::string> resultMap, + std::vector& communityList, + std::map& singleName, + const std::vector& singleValueResult) { + for (const std::string& name : singleValueResult) { + resultMap.insert( + std::pair, std::string>( + {singleName.find(name)->second}, name)); + if (auto it = std::find( + communityList.begin(), + communityList.end(), + singleName.find(name)->second); + it != communityList.end()) { + communityList.erase(it); + } + } + return (resultMap); +} + +// Store the names that are not valid with 1 or 2 names to NA +std::map, std::string> addNullCommunity( + std::map, std::string> resultMap, + std::vector& communityList) { + const std::string nullMessage = "(NA)"; + for (const std::string& value : communityList) { + resultMap.insert( + std::pair, std::string>({value}, nullMessage)); + } + return (resultMap); +} +#endif // IS_OSS - End of Skynet functions + +float translateLinkBandwidthValue(const uint32_t linkBandwidthValue) { + // helper function to translate unsigned int value to float + // background: for two-byte ASN ext community with LINK_BANDWIDTH_SUB_TYPE + // the value of the community is an unsigned int encoding a float number + // and this function helps translate that back to float, + // assuming machine has single prevision binary32 float architecture. + // Ref: IEEE-754-1985 specification and + // BgpExtCommunityLinkBandWidthTypeC::getLBW() + union { + uint32_t intVal; + float floatVal; + } tmp; + tmp.floatVal = 0.0f; // To avoid lint error + tmp.intVal = linkBandwidthValue; + return tmp.floatVal; +} + +const std::string printExtCommunities( + const std::vector& extCommunities) { + if (extCommunities.empty()) { + return ""; + } + + std::vector commStrs; + for (const auto& community : extCommunities) { + const auto& communityUnion = community.u().value(); + if (communityUnion.getType() != TBgpExtCommUnion::Type::two_byte_asn) { + commStrs.emplace_back("non-two-byte-asn"); + continue; + } + + const auto& extCommunity = communityUnion.get_two_byte_asn(); + if (folly::copy(extCommunity.type().value()) == + static_cast(TBgpTwoByteAsnExtCommType::LINK_BANDWIDTH_TYPE)) { + if (folly::copy(extCommunity.sub_type().value()) == + static_cast( + TBgpTwoByteAsnExtCommSubType::LINK_BANDWIDTH_SUB_TYPE)) { + auto decodeLbwExtComm = CmdLocalOptions::getInstance()->getLocalOption( + "show_bgp_neighbors", kGar); + if (decodeLbwExtComm.empty()) { + decodeLbwExtComm = CmdLocalOptions::getInstance()->getLocalOption( + "show_bgp_table", kGar); + } + if (decodeLbwExtComm == "true") { +#ifndef IS_OSS + bgp::nsf_policy::NsfTeWeightEncoding encoding; + encoding.l2_encoding() = bgp::nsf_policy::NsfL2TeWeightEncoding(); + encoding.l2_encoding()->rack_id() = 4; + encoding.l2_encoding()->plane_id() = 4; + encoding.l2_encoding()->remote_rack_capacity() = 8; + encoding.l2_encoding()->spine_capacity() = 8; + encoding.l2_encoding()->local_rack_capacity() = 8; + auto decodedValues = + bgp::decodeValues(extCommunity.value().value(), encoding); + commStrs.emplace_back( + nettools::bgplib::BgpPathC::topoInfoToStr(decodedValues)); +#else + commStrs.emplace_back( + fmt::format( + "LBW{}:AS({}):RawValue({}):LbwValue({})", + ((folly::copy(extCommunity.type().value()) & 0x40) == 0) + ? "(T)" + : "", + folly::copy(extCommunity.asn().value()), + folly::copy(extCommunity.value().value()), + utils::formatBandwidth(translateLinkBandwidthValue( + folly::copy(extCommunity.value().value()))))); +#endif + } else { + commStrs.emplace_back( + fmt::format( + "LBW{}:AS({}):RawValue({}):LbwValue({})", + ((folly::copy(extCommunity.type().value()) & 0x40) == 0) + ? "(T)" + : "", + folly::copy(extCommunity.asn().value()), + folly::copy(extCommunity.value().value()), + utils::formatBandwidth(translateLinkBandwidthValue( + folly::copy(extCommunity.value().value()))))); + } + } else { + commStrs.emplace_back( + fmt::format( + "Type({}):SubType({}):AS({}):Value({})", + folly::copy(extCommunity.type().value()), + folly::copy(extCommunity.sub_type().value()), + folly::copy(extCommunity.asn().value()), + folly::copy(extCommunity.value().value()))); + } + } + } + return folly::join(" ", commStrs); +} + +const std::string printAsPath(const TAsPath& asPath) { + const std::map segFormat = { + {TAsPathSegType::AS_SEQUENCE, "{}"}, + {TAsPathSegType::AS_CONFED_SEQUENCE, "({})"}, + {TAsPathSegType::AS_SET, "{{{}}}"}, + {TAsPathSegType::AS_CONFED_SET, "({{{}}})"}}; + + std::vector segments; + segments.reserve(asPath.size()); + for (const auto& path : asPath) { + // TODO(michaeluw): fully deprecate asns T113736668 + // Use asns_4_byte if set, otherwise use asns + std::string asnsStr; + if (!path.asns_4_byte().value().empty()) { + asnsStr = folly::join('_', path.asns_4_byte().value()); + } else { + asnsStr = folly::join('_', path.asns().value()); + } + segments.emplace_back( + fmt::format( + fmt::runtime(segFormat.at(folly::copy(path.seg_type().value()))), + std::move(asnsStr))); + } + return folly::join('_', segments); +} + +const std::string printLocalPrefs( + int localPref, + const std::map& mnemonics) { + if (nettools::bgplib::isAristaDevice()) { + return folly::to(localPref); + } + + std::string prefName = nettools::bgplib::kNullMessage; + const auto& prefMnemonic = mnemonics.find(localPref); + if (prefMnemonic != mnemonics.end()) { + // Strip away LOCALPREF_ from the mnemonic name + static const std::string kLocalPref = "LOCALPREF_"; + if (folly::StringPiece(prefMnemonic->second).startsWith(kLocalPref)) { + prefName = prefMnemonic->second.substr(kLocalPref.size()); + } else { + prefName = prefMnemonic->second; + } + } + return fmt::format("{}/{}", prefName, localPref); +} + +const std::string printClusterList(const std::vector& clusterList) { + std::vector clusters; + clusters.reserve(clusterList.size()); + for (const auto& cluster : clusterList) { + clusters.emplace_back(IPAddress::fromLongHBO(cluster).str()); + } + return folly::join(',', clusters); +} + +namespace { +// Static state for mnemonic caching - defined here to be accessible by both +// functions +struct MnemonicCache { + bool firstTimeRetreivingMnemonics = true; + std::map prefsMnemonics; + bool firstTimeGettingCommunities = true; + std::map, std::string> communitySetMap; +}; + +MnemonicCache& getMnemonicCache() { + static MnemonicCache cache; + return cache; +} +} // namespace + +std::map, std::string> getCommunitySet( + const HostInfo& hostInfo) { + auto& cache = getMnemonicCache(); + + if (!cache.firstTimeGettingCommunities) { + return cache.communitySetMap; + } + + cache.firstTimeGettingCommunities = false; + const auto bgp_mnemonics = getLocalBgpConfig(hostInfo); + if (bgp_mnemonics.find("communities") == bgp_mnemonics.items().end()) { + return cache.communitySetMap; + } + for (const auto& mnemonic : bgp_mnemonics.at("communities")) { + std::set communities; + if (mnemonic.find("communities") == mnemonic.items().end()) { + continue; + } + for (const auto& community : mnemonic.at("communities")) { + communities.insert(community.asString()); + } + if (mnemonic.find("name") == mnemonic.items().end()) { + cache.communitySetMap[communities] = nettools::bgplib::kNullMessage; + } else { + cache.communitySetMap[communities] = mnemonic.at("name").asString(); + } + } + return cache.communitySetMap; +} + +folly::dynamic getLocalBgpConfig(const HostInfo& hostInfo) { + auto client = + utils::createClient>(hostInfo); + std::string config; + client->sync_getRunningConfig(config); + return folly::parseJson(config); +} + +const std::map getLocalPrefMnemonics( + const HostInfo& hostInfo) { + auto& cache = getMnemonicCache(); + + if (!cache.firstTimeRetreivingMnemonics) { + return cache.prefsMnemonics; + } + + cache.firstTimeRetreivingMnemonics = false; + const auto bgp_mnemonics = getLocalBgpConfig(hostInfo); + + if (bgp_mnemonics.empty()) { + return cache.prefsMnemonics; + } + + if (bgp_mnemonics.find("localprefs") == bgp_mnemonics.items().end()) { + return cache.prefsMnemonics; + } + for (const auto& mnemonic : bgp_mnemonics.at("localprefs")) { + if (mnemonic.find("localpref") == mnemonic.items().end()) { + continue; + } + cache.prefsMnemonics[mnemonic.at("localpref").asInt()] = + mnemonic.at("name").asString(); + } + return cache.prefsMnemonics; +} + +void resetBgpMnemonicCaches() { + auto& cache = getMnemonicCache(); + cache.firstTimeRetreivingMnemonics = true; + cache.prefsMnemonics.clear(); + cache.firstTimeGettingCommunities = true; + cache.communitySetMap.clear(); +} + +const std::vector printRoutesInformation( + const std::map>& networks, + const HostInfo& hostInfo, + bool showPolicy) { + std::vector routes; + const auto& prefMnemonics = getLocalPrefMnemonics(hostInfo); + for (const auto& [ipPrefix, bgpPaths] : networks) { + for (const auto& path : bgpPaths) { + const auto ip_address = IPAddress::fromBinary( + folly::ByteRange(folly::StringPiece(ipPrefix.prefix_bin().value()))); + const auto prefix = fmt::format( + "{}/{}", ip_address.str(), folly::copy(ipPrefix.num_bits().value())); + + std::string nextHop; + if (path.next_hop().value().prefix_bin().value().empty()) { + nextHop = (folly::copy(ipPrefix.afi().value()) == TBgpAfi::AFI_IPV4) + ? "0.0.0.0" + : "::"; + } else { + nextHop = IPAddress::fromBinary( + folly::ByteRange( + folly::StringPiece( + path.next_hop().value().prefix_bin().value()))) + .str(); + } + + std::string routerOriginatorId = " -- "; + if (const auto& originatorId = + apache::thrift::get_pointer(path.originator_id())) { + routerOriginatorId = IPAddress::fromLongHBO(*originatorId).str(); + } else if ( + const auto& routerId = + apache::thrift::get_pointer(path.router_id())) { + routerOriginatorId = IPAddress::fromLongHBO(*routerId).str(); + } + + const auto& clusterList = + apache::thrift::get_pointer(path.cluster_list()); + std::string clusterListStr = (clusterList && !clusterList->empty()) + ? fmt::format("[{}]", printClusterList(*clusterList)) + : "[]"; + + const std::string communitiesStr = + (apache::thrift::get_pointer(path.communities())) + ? printCommunities( + *apache::thrift::get_pointer(path.communities()), hostInfo) + : ""; + const std::string extCommunitiesStr = + (apache::thrift::get_pointer(path.extCommunities())) + ? printExtCommunities( + *apache::thrift::get_pointer(path.extCommunities())) + : ""; + const std::string localPrefsStr = + (apache::thrift::get_pointer(path.local_pref())) + ? printLocalPrefs( + *apache::thrift::get_pointer(path.local_pref()), prefMnemonics) + : ""; + const std::string originStr = (apache::thrift::get_pointer(path.origin())) +#ifndef IS_OSS + ? apache::thrift::util::enumNameSafe( + static_cast( + *apache::thrift::get_pointer(path.origin()))) +#else + ? formatBgpOrigin(*apache::thrift::get_pointer(path.origin())) +#endif + : nettools::bgplib::kNullMessage; + + std::string routeInformation = fmt::format( + "---\n" + "Network: {}\n" + "Nexthop: {}\n" + "Router/OriginatorId: {}\n" + "ClusterList: {}\n" + "Communities: {}\n" + "ExtCommunities: {}\n" + "AsPath: {}\n" + "LocalPref: {}\n" + "Origin: {}\n" + "MED: {}\n" + "LastModified: {}", + prefix, + nextHop, + routerOriginatorId.data(), + clusterListStr, + communitiesStr, + extCommunitiesStr, + printAsPath(path.as_path().value()), + localPrefsStr, + (originStr != nettools::bgplib::kNullMessage) + ? originStr.substr(11) // Trim BGP_ORIGIN_ from origin string + : originStr, + path.med().has_value() + ? std::to_string(*path.med()) + : (path.multi_exit_disc().has_value() + ? std::to_string(*path.multi_exit_disc()) + : "Not set"), + (folly::copy(path.last_modified_time().value())) + ? utils::parseTimeToTimeStamp( + folly::copy(path.last_modified_time().value()) / 1000) + : "Not set"); + + if (showPolicy) { + std::string policyStr = "Not set"; + if (apache::thrift::get_pointer(path.policy_name()) && + !apache::thrift::get_pointer(path.policy_name())->empty()) { + policyStr = *apache::thrift::get_pointer(path.policy_name()); + } + routeInformation += fmt::format("\nPolicy: {}", policyStr); + } + routes.emplace_back(routeInformation); + } + } + return routes; +} + +void printBgpNeighborsOutput( + const std::vector& neighbors, + std::ostream& out) { + constexpr int kKeepAliveRobustCount = 3; + if (neighbors.empty()) { + return; + } + uint32_t neighborIndex = 0; + for (auto const& neighbor : neighbors) { + const std::string& myAddr = *neighbor.my_addr(); + const std::string& peerAddr = *neighbor.peer_addr(); + // Make a mutable copy so we can trim whitespace + std::string description = + folly::trimWhitespace(*neighbor.description()).str(); + apache::thrift::optional_field_ref details = + neighbor.details(); + const TBgpPeer& peer = *neighbor.peer(); + const TBgpPeerState peerState = *peer.peer_state(); + const int holdTime = *peer.hold_time(); + + if (myAddr.size() == 0 || !details.has_value()) { + out << "Missing details or IP Address: " << myAddr << " " << peerAddr; + return; + } + + // prevent printing number with comma, e.g: 65,000 + out.imbue(std::locale("C")); + if (neighbors.size() > 1) { + out << "------------------------------------------------------------------" + << std::endl; + out << fmt::format( + "neighbor {} of {}", neighborIndex + 1, neighbors.size()) + << std::endl; + } + + // TODO(michaeluw): T113736668 deprecate i32 asns field + uint32_t remote_as = *peer.remote_as_4_byte() > 0 ? *peer.remote_as_4_byte() + : *peer.remote_as(); + out << "BGP neighbor is " << peerAddr << ", remote AS " << remote_as + << ", "; + out << "Confed Peer: " << (*details->confed_peer() ? "" : "not ") + << "configured" << std::endl; + out << " Description: " << description << std::endl; + out << " BGP version 4, remote router ID " + << IPAddress::fromLong(*details->remote_bgp_id()) << std::endl; + if (neighbor.rib_version().has_value() && *neighbor.rib_version() > 0) { + out << " RIB version: " << *neighbor.rib_version() << std::endl; + } + out << " Hold time is " << holdTime << "s, KeepAlive interval is " + << holdTime / kKeepAliveRobustCount << "s" << std::endl; + + if (peerState == neteng::fboss::bgp::thrift::TBgpPeerState::ESTABLISHED) { + out << " BGP state is " << enumNameSafe(peerState) << ", up for " + << utils::getPrettyElapsedTime( + utils::getEpochFromDuration(*neighbor.uptime())) + << std::endl; + + if (*neighbor.num_resets() > 0) { + out << " Flapped " << *neighbor.num_resets() << " times, last flap " + << utils::getPrettyElapsedTime( + utils::getEpochFromDuration(*neighbor.reset_time())) + << " ago" << std::endl; + out << " Last reset reason: " << *neighbor.last_reset_reason() + << std::endl; + } + } else { + out << " BGP state is " << enumNameSafe(peerState) << std::endl; + if (peerState >= neteng::fboss::bgp::thrift::TBgpPeerState::ACTIVE && + details->active_time().has_value()) { + out << " The TCP socket has been up for " + << utils ::getPrettyElapsedTime( + utils::getEpochFromDuration(*details->active_time())) + << "." << std::endl; + } + if (*neighbor.num_resets() > 0) { + out << " Flapped " << *neighbor.num_resets() << " times"; + if (*neighbor.reset_time() > 0) { + out << ", last went down " + << utils ::getPrettyElapsedTime( + utils::getEpochFromDuration(*neighbor.reset_time())) + << " ago"; + } + out << std::endl; + out << " Last reset reason: " << *neighbor.last_reset_reason() + << std::endl; + } + } + + out << " UCMP Link Bandwidth:" << std::endl; + out << " Advertise: " + << enumNameSafe(*neighbor.advertise_link_bandwidth()) << std::endl; + out << " Receive : " << enumNameSafe(*neighbor.receive_link_bandwidth()) + << std::endl; + if (neighbor.link_bandwidth_bps().has_value()) { + out << " Value : " + << utils::formatBandwidth(*neighbor.link_bandwidth_bps()) + << std::endl; + } + + printBgpCapabilities(*details, out); + out << " Add Path: "; + if (peer.add_path().has_value()) { + out << "Configured - " << enumNameSafe(*peer.add_path()); + printAddPathCapability(*details->add_path_capabilities(), out); + out << std::endl; + } else { + out << "DISABLED\n" << std::endl; + } + + const auto rcvdPrefix = *neighbor.prepolicy_rcvd_prefix_count(); + const auto sentPrefix = *neighbor.postpolicy_sent_prefix_count(); + + Table table; + table.setHeader({"Prefix statistics:", "Sent", "Rcvd"}); + table.addRow( + {"IPv4, IPv6 Unicast:", + folly::to(sentPrefix), + folly::to(rcvdPrefix)}); + + out << table << std::endl; + + if (auto sentAnnoucementsIpv4 = + *details->sent_update_announcements_ipv4()) { + out << "BGP update announcements sent ipv4: " << sentAnnoucementsIpv4 + << std::endl; + } + if (auto sentAnnoucementsIpv6 = + *details->sent_update_announcements_ipv6()) { + out << "BGP update announcements sent ipv6: " << sentAnnoucementsIpv6 + << std::endl; + } + if (auto sentWithdrawals = *details->sent_update_withdrawals()) { + out << "BGP update withdrawals sent: " << sentWithdrawals << std::endl; + } + if (auto recvdAnnoucementsIpv4 = + *details->recv_update_announcements_ipv4()) { + out << "BGP update announcements received ipv4: " << recvdAnnoucementsIpv4 + << std::endl; + } + if (auto recvdAnnoucementsIpv6 = + *details->recv_update_announcements_ipv6()) { + out << "BGP update announcements received ipv6: " << recvdAnnoucementsIpv6 + << std::endl; + } + if (auto recvdWithdrawals = *details->recv_update_withdrawals()) { + out << "BGP update withdrawals received: " << recvdWithdrawals + << std::endl; + } + if (auto enforceFirstAsRejects = *details->enforce_first_as_rejects()) { + out << "Number of enforce-first-as validation rejections: " + << enforceFirstAsRejects << std::endl; + } + // TODO(michaeluw): T113736668 deprecate i32 asns field + uint32_t local_as = *peer.local_as_4_byte() > 0 ? *peer.local_as_4_byte() + : *peer.local_as(); + out << "Local AS is " << local_as << ", "; + out << "local router ID " << *details->local_router_id() << std::endl; + out << "Local TCP address is " << myAddr << ", local port is " + << ntohs(*details->local_port()) << std::endl; + out << "Remote TCP address is " << peerAddr << ", remote port is " + << ntohs(*details->peer_port()) << std::endl; + + out << "TCP connection-mode is " << enumNameSafe(*details->connect_mode()) + << std::endl; + if (auto flaps = *details->num_of_flaps()) { + out << "Number of session terminations: " << flaps << std::endl; + } + + if (const auto lastResetHoldTimer = *peer.lastResetHoldTimer()) { + printTimestamp(lastResetHoldTimer, "HoldTimer last reset at", out); + } + if (const auto lastSentKeepAlive = *peer.lastSentKeepAlive()) { + printTimestamp(lastSentKeepAlive, "KeepAlive last received at", out); + } + if (const auto lastRcvdKeepAlive = *peer.lastRcvdKeepAlive()) { + printTimestamp(lastRcvdKeepAlive, "KeepAlive last sent at", out); + } + if (peerState == TBgpPeerState::ESTABLISHED) { + if (const auto eorSentTime = *details->eor_sent_time()) { + printTimestamp(eorSentTime, "EOR Sent at", out); + } else { + out << "EOR not yet sent" << std::endl; + } + if (const auto eorRcvdTime = *details->eor_received_time()) { + printTimestamp(eorRcvdTime, "EOR Received at", out); + } else { + out << "EOR not yet received" << std::endl; + } + } + if (neighbors.size() > 1) { + out << "------------------------------------------------------------------" + << std::endl; + } + out << std::endl; + ++neighborIndex; + } +} + +void printBgpCapabilities(const TBgpSessionDetail& details, std::ostream& out) { + out << " Neighbor Capabilities:" << std::endl; + if (folly::copy(details.ipv4_unicast().value())) { + out << " Multiprotocol IPv4 Unicast: negotiated" << std::endl; + } + if (folly::copy(details.ipv6_unicast().value())) { + out << " Multiprotocol IPv6 Unicast: negotiated" << std::endl; + } + if (folly::copy(details.rr_client().value())) { + out << " Route Refresh: advertised" << std::endl; + } + if (const auto remoteRestartTime = + folly::copy(details.gr_remote_restart_time().value())) { + out << " Graceful Restart: received, peer restart-time is " + << remoteRestartTime << "s" << std::endl; + } else { + out << " Graceful Restart: not received" << std::endl; + } + if (const auto restartTime = folly::copy(details.gr_restart_time().value())) { + out << " Graceful Restart: sent, local restart-time is " << restartTime + << "s" << std::endl; + } else { + out << " Graceful Restart: NOT sent" << std::endl; + } +} + +void printAddPathCapability( + const std::vector& capabilities, + std::ostream& out) { + std::vector negotiations; + negotiations.reserve(capabilities.size()); + for (const auto& capability : capabilities) { + const auto afi = enumNameSafe(folly::copy(capability.afi().value())); + const auto addPathCapability = + enumNameSafe(folly::copy(capability.add_path().value())); + negotiations.emplace_back( + fmt::format("{} {}", afi, addPathCapability).data()); + } + if (!negotiations.empty()) { + out << ", Negotiated - " << folly::join(", ", negotiations) << std::endl; + } +} + +void printRouteHeader(std::string& out, bool detailed) { + // Helper function to construct print lines of header for advertised and + // received rooutes. Add marker information + out = + "Markers: * - Best entries (used for forwarding), @ - Entry used to advertise across area\n"; + if (!detailed) { + out += + "Acronyms: SP - Source Preference, PP - Path Preference, D - Distance\n" + " MN - Min-Nexthops\n"; + } +} + +const std::string formatBytes(size_t n) { + const std::vector suffix{"B", "KB", "MB", "GB"}; + size_t i = 0; + double out = n; + for (i = 0; i < 4 && n >= 1024; i++) { + out = n / 1024.0; + n /= 1024; + } + out = (static_cast(out * 100.0)) / 100.0; + return fmt::format("{} {}", out, suffix[i]); +} + +timeval splitFractionalSecondsFromTimer(const long timer) { + constexpr int kOneSecond = 1000; + struct timeval tv; + tv.tv_sec = timer / kOneSecond; // get total amount of seconds + tv.tv_usec = (timer % kOneSecond); // get fractional seconds + + tv.tv_usec = lrint(tv.tv_usec); // round fs to nearest decimal + return tv; +} + +void printTimestamp( + const long timeToParse, + const std::string& outputMessage, + std::ostream& out) { + constexpr std::string_view kTimeFormat = "%Y-%m-%d %H:%M:%S %Z"; + const auto splitTime = splitFractionalSecondsFromTimer(timeToParse); + std::string formattedTime; // Timestamp storage + stringFormatTimestamp(splitTime.tv_sec, kTimeFormat.data(), &formattedTime); + + // stringFormatTimestamp returns a formatted time like the following: + // 2021-10-27 00:00:06 PDT + // + // In order to add the fractional seconds, we find the last space before the + // timestamp's timezone and concatenate the fractional seconds. + // 2021-10-27 00:00:06 PDT -> 2021-10-27 00:00:06.475 PDT + int index = formattedTime.find_last_of(' '); + out << outputMessage + << fmt::format( + " {}.{} {}", + formattedTime.substr(0, index), // Date and time + splitTime.tv_usec, // fractional seconds + formattedTime.substr(index + 1)) // timezone + << std::endl; +} + +std::map> filterNetworks( + const std::map>& networks, + const std::vector& prefixes) { + std::vector prefixList; + for (const auto& ip : prefixes) { + // Split the Ip Address and subnet mask + std::size_t maskIndex = ip.find_last_of('/'); + const auto address = folly::IPAddress(ip.substr(0, maskIndex)); + const auto binaryAddress = facebook::network::toBinaryAddress(address); + + TIpPrefix ipPrefix; + ipPrefix.prefix_bin() = binaryAddress.addr().value().toStdString(); + ipPrefix.num_bits() = folly::to(ip.substr(maskIndex + 1)); + ipPrefix.afi() = (address.isV4()) ? TBgpAfi::AFI_IPV4 : TBgpAfi::AFI_IPV6; + + prefixList.emplace_back(ipPrefix); + } + + std::map> filteredNetworks; + for (const auto& prefix : prefixList) { + if (const auto& it = networks.find(prefix); it != networks.end()) { + filteredNetworks.try_emplace(it->first, it->second); + } + } + return filteredNetworks; +} + +const std::map> getRejectedNetworks( + const std::map>& receivedNetworks, + const std::map>& acceptedNetworks) { + std::map> rejectedNetworks; + for (const auto& [route, path] : receivedNetworks) { + if (acceptedNetworks.find(route) == acceptedNetworks.end()) { + rejectedNetworks.try_emplace(route, path); + } + } + return rejectedNetworks; +} + +const std::map> vectorizePaths( + const std::map& networks) { + std::map> vectorizedNetworks; + for (const auto& [route, path] : networks) { + std::vector vectorizedPath = {path}; + vectorizedNetworks.try_emplace(route, std::move(vectorizedPath)); + } + return vectorizedNetworks; +} + +void sortEntries(std::vector& entries) { + // Sort the entries by their prefix + std::sort( + entries.begin(), + entries.end(), + [](const TRibEntry& entryA, const TRibEntry& entryB) { + return entryA.prefix().value() < entryB.prefix().value(); + }); + + for (auto& entry : entries) { + for (auto& [group, path] : *entry.paths()) { + // Sort RIB entries by nexthop + std::sort( + path.begin(), + path.end(), + [](const TBgpPath& pathA, const TBgpPath& pathB) { + return pathA.next_hop().value() < pathB.next_hop().value(); + }); + } + } +} + +float getLinkBandwidthFromExtCommunities( + const std::vector& extCommunities) { + for (const auto& comm : extCommunities) { + const auto& commUnion = comm.u().value(); + + if (commUnion.getType() != TBgpExtCommUnion::Type::two_byte_asn) { + // not a two-byte ASN + continue; + } + const auto& asn = commUnion.get_two_byte_asn(); + + if (folly::copy(asn.type().value()) != + static_cast(TBgpTwoByteAsnExtCommType::LINK_BANDWIDTH_TYPE)) { + // not a link bandwidth value + continue; + } + + if ((folly::copy(asn.sub_type().value()) == + static_cast( + TBgpTwoByteAsnExtCommSubType::LINK_BANDWIDTH_SUB_TYPE))) { + return translateLinkBandwidthValue(folly::copy(asn.value().value())); + } + } + return -1.0f; +} + +bool hasBestPath(const TRibEntry& entry) { + for (const auto& [group, paths] : entry.paths().value()) { + for (const auto& path : paths) { + if (path.is_best_path().value_or(false /* default */)) { + return true; + } + } + } + return false; +} + +void printRIBEntries( + std::ostream& out, + TRibEntryWithHost& data, + const bool detail, + const std::string& originator, + bool fbnetsource) { + out << "Markers: * - One of the best entries, @ - The best entry, % - Pending best path selection" + << std::endl; + out << "Acronyms: ASP - AS Path, LP - Local Preference, LBW - Link Bandwidth, LM - Last Modified" + << std::endl; + auto& entries = *data.tRibEntries(); + sortEntries(entries); + const HostInfo hostInfo( + *data.host(), *data.oobName(), folly::IPAddress(*data.ip())); + const auto& prefMnemonics = getLocalPrefMnemonics(hostInfo); + + for (const auto& entry : entries) { + const auto& bestGroup = entry.best_group().value(); + const auto& bestNextHop = entry.best_next_hop().value(); + const bool entryHasBestPath = hasBestPath(entry); + int totalPaths = 0, ecmpPaths = 0; + std::vector pathsToPrint; + + for (const auto& [group, paths] : entry.paths().value()) { + // Accounting for ecmp and total paths + const bool isEcmpPath = (group == bestGroup); + totalPaths += paths.size(); + if (isEcmpPath) { + ecmpPaths += paths.size(); + } + + for (const auto& path : paths) { + const auto& nextHop = path.next_hop().value(); + + // If the TRibEntry contains a best path as indicated by bgp++'s RIB + // then use best_path indicator; otherwise infer from best_next_hop on + // the TBgpPath obj. This only applies for ECMP paths. + const bool bestEcmpPath = entryHasBestPath + ? path.is_best_path().value_or(false) + : (isEcmpPath && bestNextHop == nextHop); + + std::string marker = (isEcmpPath) ? "*" : " "; + marker += (bestEcmpPath) ? "@" : " "; + + std::string peerIdStr = "--"; + if (const auto& peerId = apache::thrift::get_pointer(path.peer_id())) { + peerIdStr = IPAddress::fromBinary( + folly::ByteRange( + folly::StringPiece(peerId->prefix_bin().value()))) + .str(); + } + + std::string peerDescriptionStr = + path.peer_description() ? *path.peer_description() : "--"; + + std::string asPathStr = "[]"; + const auto& asPath = path.as_path().value(); + if (!asPath.empty()) { + asPathStr = printAsPath(asPath); + } + + // Router or Originator ID - Set the following to originator id + // if populated (i.e. route-reflector case) else use router-id. + std::string routerOriginatorIdStr = "--"; + if (const auto& originatorId = + apache::thrift::get_pointer(path.originator_id())) { + routerOriginatorIdStr = + IPAddress::fromLongHBO((uint32_t)*originatorId).str(); + } else if ( + const auto& routerId = + apache::thrift::get_pointer(path.router_id())) { + routerOriginatorIdStr = + IPAddress::fromLongHBO((uint32_t)*routerId).str(); + } + + if (!originator.empty() && originator != routerOriginatorIdStr) { + continue; + } + + const auto& clusterList = + apache::thrift::get_pointer(path.cluster_list()); + std::string clusterListStr = (clusterList && !clusterList->empty()) + ? fmt::format("[{}]", printClusterList(*clusterList)) + : "[]"; + + const auto& nextHopStr = (folly::copy(nextHop.num_bits().value()) != 0) + ? IPAddress::fromBinary( + folly::ByteRange( + folly::StringPiece(nextHop.prefix_bin().value()))) + .str() + : "SELF"; + + const std::string originStr = + (apache::thrift::get_pointer(path.origin())) +#ifndef IS_OSS + ? apache::thrift::util::enumNameSafe( + static_cast( + *apache::thrift::get_pointer(path.origin()))) +#else + ? formatBgpOrigin(*apache::thrift::get_pointer(path.origin())) +#endif + : nettools::bgplib::kNullMessage; + + std::string lbwStr = "None"; + std::string extCommunitiesStr; + if (const auto& extCommunities = + apache::thrift::get_pointer(path.extCommunities())) { + float linkBandwidth = + getLinkBandwidthFromExtCommunities(*extCommunities); + if (linkBandwidth > 0.0f) { + lbwStr = utils::formatBandwidth(linkBandwidth); + } + extCommunitiesStr = printExtCommunities(*extCommunities); + } + + std::string pathToPrint = fmt::format( + "{} from {} ({}) via {} | LBW: {} | Origin: {} | " + "LP: {} | ASP: {} | LM: {} | NH Weight: {} | MED: {} | ID: {} (rcvd) {} (sent) | Weight: {}{} | IgpCost: {}", + marker, + peerIdStr, + peerDescriptionStr, + nextHopStr, + lbwStr, + (originStr != nettools::bgplib::kNullMessage) + ? originStr.substr(11) // Trim BGP_ORIGIN_ from origin string + : originStr, + printLocalPrefs( + *apache::thrift::get_pointer(path.local_pref()), prefMnemonics), + asPathStr, + utils::getPrettyElapsedTime( + folly::copy(path.last_modified_time().value()) / 1000000), + path.next_hop_weight() ? std::to_string(*path.next_hop_weight()) + : "N/A", + path.med().has_value() + ? std::to_string(*path.med()) + : (path.multi_exit_disc().has_value() + ? std::to_string(*path.multi_exit_disc()) + : "N/A"), + path.path_id() ? std::to_string(*path.path_id()) : "N/A", + path.path_id_to_send() ? std::to_string(*path.path_id_to_send()) + : "N/A", + path.weight() ? std::to_string(*path.weight()) : "0", + path.topologyInfo() ? fmt::format( + " | {}", + nettools::bgplib::BgpPathC::topoInfoToStr( + *path.topologyInfo())) + : "", + path.igp_cost() ? std::to_string(*path.igp_cost()) : "N/A"); + + std::string communityToPrint; +#ifndef IS_OSS + if (fbnetsource) { + communityToPrint = printFBnetCommunities( + *apache::thrift::get_pointer(path.communities())); + } else { +#endif + const HostInfo hostInfo( + *data.host(), *data.oobName(), folly::IPAddress(*data.ip())); + communityToPrint = printCommunities( + *apache::thrift::get_pointer(path.communities()), hostInfo); +#ifndef IS_OSS + } +#endif + + if (detail) { + pathToPrint += fmt::format( + "\n Router/Originator: {} | ClusterList: {}" + "\n Communities: {}", + routerOriginatorIdStr, + clusterListStr, + communityToPrint); + if (!extCommunitiesStr.empty()) { + pathToPrint += + fmt::format("\n ExtCommunities: {}", extCommunitiesStr); + } + + const auto& filterDescrp = + apache::thrift::get_pointer(path.bestpath_filter_descr()); + if (filterDescrp && !filterDescrp->empty()) { + pathToPrint += fmt::format( + "\n BestPath Rejection Reason: {}", *filterDescrp); + } + } + pathsToPrint.emplace_back(pathToPrint); + } + } + const auto entryIp = IPAddress::fromBinary( + folly::ByteRange( + folly::StringPiece(entry.prefix().value().prefix_bin().value()))); + const auto prefix = fmt::format( + "{}/{}", + entryIp.str(), + folly::copy(entry.prefix().value().num_bits().value())); + // Check if path selection is pending. This can happen when IGP cost + // has changed but best-path hasn't been recalculated yet. The "%" marker + // indicates the path-selection results displayed are in a transient state. + // Wait for the final result after the marker is cleared. + std::string pendingMarker; + if (entry.path_selection_pending().has_value() && + entry.path_selection_pending().value()) { + pendingMarker = "%"; + } + out << fmt::format( + "\n>{} {}, Selected {}/{} paths\n", + pendingMarker, + prefix, + ecmpPaths, + totalPaths); + out << folly::join('\n', pathsToPrint) << std::endl; + } +} + +bool isValidCommunity(std::string_view communityName) { + const size_t delimiter = communityName.find(':'); + if (delimiter != std::string::npos) { + const auto& it = communityName.begin(); + return std::all_of(it, it + delimiter - 1, ::isdigit) && + std::all_of(it + delimiter + 1, communityName.end(), ::isdigit); + } + return false; +} + +const std::vector communityNameToValue( + const std::string& communityName, + const HostInfo& hostInfo) { + static const auto& bgpMnemonics = getLocalBgpConfig(hostInfo); + + // First Case: The community name comes in format 'asn:value'. + // if so, we need to make sure the values are valid (integers). + if (isValidCommunity(communityName)) { + return {communityName}; + } else { + // Second Case: The community name is sometimes prefixed with + // COMM_. + // example: COMM_ADMIT_CONTROLLER or + // ADMIT_CONTROLLER + // If so we need to make sure that the name is present on bgp mnemonics + // to return the right community set values. + std::vector communitySet; + const auto& communities = bgpMnemonics.at("communities"); + const std::string commNamePrefix = "COMM_"; + for (const auto& community : communities) { + if (community["name"] == communityName || + community["name"] == + fmt::format("{}{}", commNamePrefix, communityName)) { + for (const auto& curCommunity : community["communities"]) { + communitySet.emplace_back(curCommunity.asString()); + } + return communitySet; + } + } + } + throw std::invalid_argument( + fmt::format("Unknown Community Name: {}", communityName)); +} + +const std::string communityToString(const TBgpCommunity& community) { + return fmt::format( + "{}:{}", + folly::copy(community.asn().value()), + folly::copy(community.value().value())); +} + +void stringFormatTimestamp( + time_t timestamp, + const char* format, + std::string* output) { + std::tm tm_info; + localtime_r(×tamp, &tm_info); + std::ostringstream oss; + oss << std::put_time(&tm_info, format); + *output = oss.str(); +} + +} // namespace facebook::fboss diff --git a/fboss/cli/fboss2/commands/show/bgp/CmdShowUtils.h b/fboss/cli/fboss2/commands/show/bgp/CmdShowUtils.h new file mode 100644 index 0000000000000..038d488d0012b --- /dev/null +++ b/fboss/cli/fboss2/commands/show/bgp/CmdShowUtils.h @@ -0,0 +1,241 @@ +/* + * 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 "folly/json/dynamic.h" + +#include "configerator/structs/neteng/fboss/bgp/if/gen-cpp2/bgp_attr_types.h" +#include "fboss/cli/fboss2/CmdHandler.h" +#include "fboss/cli/fboss2/utils/CmdUtilsCommon.h" +#include "neteng/fboss/bgp/if/gen-cpp2/bgp_thrift_types.h" + +namespace { +inline constexpr auto kGar = "--decode-gar-lbw-ext-comm"; +} + +namespace facebook::fboss { +using namespace neteng::fboss::bgp::thrift; +using neteng::fboss::bgp_attr::TAsPath; +using neteng::fboss::bgp_attr::TBgpCommunity; +using neteng::fboss::bgp_attr::TIpPrefix; + +namespace { +// This function computes a computation by recursively looking for available +// samples ahead of an offset. +template +void computeCombinations( + int offset, + int k, + std::vector& currentCombination, + std::vector>& combinations, + const std::vector& container) { + if (k == 0) { + combinations.push_back(currentCombination); + return; + } + + for (int i = offset; i <= container.size() - k; ++i) { + currentCombination.push_back(container.at(i)); + computeCombinations( + i + 1, k - 1, currentCombination, combinations, container); + currentCombination.pop_back(); + } +} +} // namespace + +struct CmdShowVersionTraits : public ReadCommandTraits { + static constexpr utils::ObjectArgTypeId ObjectArgTypeId = + utils::ObjectArgTypeId::OBJECT_ARG_TYPE_ID_NONE; + using ObjectArgType = std::monostate; + using RetType = std::map; +}; + +void cmdShowVersion(CmdShowVersionTraits::RetType& binaryVersion); +std::string getPackageId( + const std::string& packageVersion, + const std::string& packageInfo); + +std::map, std::string> getCommunitySet( + const HostInfo& host); + +const std::string printCommunities( + const std::vector& peer_communities, + const HostInfo& hostInfo, + bool isMultiLine = false); +const std::string printFBnetCommunities( + const std::vector& peer_communities); + +/* + * Get bgp community information from DesiredCommunity at FBNet + * with input of the attribute, coppesponding value and aimed field + * return a vector that contains all the records that meet the requirement + */ +std::vector getSkynetData( + std::string attributeValue, + std::string searchAttribute, + std::string field); + +/** + * get the community names by the input value + * conduct name_to_value search at FBNet + * based on the size of the values, will conduct different method + * to allocate the community name + */ +const std::map, std::string> findCommunitiesByFBNet( + std::vector communityList); + +// Get the names in doubleValueResult that have 2 values found +std::map, std::string> addDoubleValueCommunity( + std::map, std::string> resultMap, + std::vector& communityList, + std::map& singleName, + const std::vector>& doubleValueResult); + +// Get names in singleValueResult if they only have 1 value, +std::map, std::string> addSingleValueCommunity( + std::map, std::string> resultMap, + std::vector& communityList, + std::map& singleName, + const std::vector& singleValueResult); + +// Store the values that are not valid to NA +std::map, std::string> addNullCommunity( + std::map, std::string> resultMap, + std::vector& communityList); + +const std::string printClusterList(const std::vector& clusterList); +const std::string printExtCommunities( + const std::vector& extCommunities); +const std::string printAsPath(const TAsPath& asPath); +const std::string printLocalPrefs( + int localPref, + const std::map& mnemonics); +folly::dynamic getLocalBgpConfig(const HostInfo& host); +const std::map getLocalPrefMnemonics( + const HostInfo& hostInfo); +// Reset static caches for testing +void resetBgpMnemonicCaches(); +const std::vector printRoutesInformation( + const std::map>& networks, + const HostInfo& hostInfo, + bool showPolicy); +// BGP Neighbors printOutput +void printTimestamp( + long timeToParse, + const std::string& outputMessage, + std::ostream& out); +timeval splitFractionalSecondsFromTimer(long timer); +void printAddPathCapability( + const std::vector& capabilities, + std::ostream& out); +void printBgpCapabilities(const TBgpSessionDetail& details, std::ostream& out); +void printBgpNeighborsOutput( + const std::vector& neighbors, + std::ostream& out); +void printRouteHeader(std::string& out, bool detailed); +// void printAdjacencies( +// std::map>& +// neighbors, +// std::ostream& out); +// void filterReceivedRoutes( +// openr::thrift::ReceivedRouteFilter& filter, +// const std::vector& addrs, +// std::vector node, +// std::vector area); +// void filterAdvertisedRoutes( +// openr::thrift::AdvertisedRouteFilter& filter, +// const std::vector& addrs, +// const std::string& prefixType = ""); +// void printReceivedRoutesDetail( +// const std::vector& routes, +// std::ostream& out); +// void printOpenrAdvertisedRoute( +// const std::vector& routes, +// std::ostream& out); +// void printAdvertisedRoutesDetail( +// const std::vector& routes, +// std::ostream& out); +const std::string formatBytes(size_t n); +// void printOpenrReceivedRoutes( +// const std::vector& routes, +// const std::unordered_map& tagNames, +// std::ostream& out, +// bool detailed, +// bool tag2Name = false); +// Prints entries for bgp table commands +void printRIBEntries( + std::ostream& out, + TRibEntryWithHost& entries, + bool detail = false, + const std::string& originator = "", + bool fbnetsource = false); +std::map> filterNetworks( + const std::map>& networks, + const std::vector& prefixes); +// Find the routes that where advertised/received but not accepted by a peer +const std::map> getRejectedNetworks( + const std::map>& receivedNetworks, + const std::map>& acceptedNetworks); +// Searches for link bandwidth in a list of ext-communities. +float getLinkBandwidthFromExtCommunities( + const std::vector& extCommunities); +// This functions maps from std::map to +// std::map> +const std::map> vectorizePaths( + const std::map& networks); + +// This function gets all the combinations of a given container +// in k number of samples. The return value is a list with all the +// combinations. +// +// Example: container = [1, 2, 3], k = 2 +// The formula to compute combinations C(n, k): +// C(n, k) = n! / (k! * (n - k)!) = 3! / (2! * (3 - 2)!) = 3 combinations +// +// Result = {[1, 2], [1, 3], [2, 3]} +template +std::vector> getCombinations( + const std::vector& container, + int k) { + const int size = container.size(); + if (k > size || k < 0) { + throw std::out_of_range("Number of samples (k) out of combination range."); + } + if (k == size) { + return {container}; + } + if (k == 0) { + return {}; + } + + std::vector currentCombination; + std::vector> combinations; + computeCombinations(0, k, currentCombination, combinations, container); + return combinations; +} + +// Function to sort entries by prefix and next hop. +void sortEntries(std::vector& entries); +// Return communities for a given community mnemonic. +const std::vector communityNameToValue( + const std::string& communityName, + const HostInfo& hostInfo); +// Returns asn:value from community +const std::string communityToString(const TBgpCommunity& community); +// Checks for a given community that the asn and value are integers +bool isValidCommunity(std::string_view communityName); + +void stringFormatTimestamp( + time_t timestamp, + const char* format, + std::string* output); + +} // namespace facebook::fboss diff --git a/fboss/cli/fboss2/commands/show/bgp/CmdShowVersionBgp.cpp b/fboss/cli/fboss2/commands/show/bgp/CmdShowVersionBgp.cpp new file mode 100644 index 0000000000000..58aff1eb2f304 --- /dev/null +++ b/fboss/cli/fboss2/commands/show/bgp/CmdShowVersionBgp.cpp @@ -0,0 +1,44 @@ +/* + * Copyright (c) 2004-present, Meta Platforms, 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 "CmdShowVersionBgp.h" + +#include "fboss/cli/fboss2/commands/show/bgp/CmdShowUtils.h" +#include "fboss/cli/fboss2/utils/CmdClientUtilsCommon.h" // NOLINT(misc-include-cleaner) +#include "fboss/cli/fboss2/utils/HostInfo.h" +#include "neteng/fboss/bgp/if/gen-cpp2/TBgpService.h" // NOLINT(misc-include-cleaner) + +namespace facebook::fboss { + +CmdShowVersionBgp::RetType CmdShowVersionBgp::queryClient( + const HostInfo& hostInfo) { + RetType retVal; + +#ifndef IS_OSS + auto client = utils::createClient>(hostInfo); + + client->sync_getRegexExportedValues(retVal, "build_.*"); +#else + // OSS: getRegexExportedValues method not available in TBgpService + // This command requires Meta-internal fb303 extensions + throw std::runtime_error( + "BGP version command not supported in OSS build - " + "getRegexExportedValues method not available"); +#endif + + return retVal; +} + +void CmdShowVersionBgp::printOutput(RetType& bgpVer) { + cmdShowVersion(bgpVer); +} + +} // namespace facebook::fboss diff --git a/fboss/cli/fboss2/commands/show/bgp/CmdShowVersionBgp.h b/fboss/cli/fboss2/commands/show/bgp/CmdShowVersionBgp.h new file mode 100644 index 0000000000000..9b2a53e6e6bdc --- /dev/null +++ b/fboss/cli/fboss2/commands/show/bgp/CmdShowVersionBgp.h @@ -0,0 +1,28 @@ +/* + * Copyright (c) 2004-present, Meta Platforms, 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 "fboss/cli/fboss2/CmdHandler.h" +#include "fboss/cli/fboss2/commands/show/bgp/CmdShowUtils.h" + +namespace facebook::fboss { + +class CmdShowVersionBgp + : public CmdHandler { + public: + using RetType = CmdShowVersionTraits::RetType; + + RetType queryClient(const HostInfo& hostInfo); + + void printOutput(RetType& bgpVer); +}; + +} // namespace facebook::fboss diff --git a/fboss/cli/fboss2/commands/show/bgp/changelist/CmdShowBgpChangelist.cpp b/fboss/cli/fboss2/commands/show/bgp/changelist/CmdShowBgpChangelist.cpp new file mode 100644 index 0000000000000..0feefdb037aed --- /dev/null +++ b/fboss/cli/fboss2/commands/show/bgp/changelist/CmdShowBgpChangelist.cpp @@ -0,0 +1,43 @@ +/* + * 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/show/bgp/changelist/CmdShowBgpChangelist.h" + +#include "fboss/cli/fboss2/commands/show/bgp/CmdShowUtils.h" +#include "fboss/cli/fboss2/utils/CmdClientUtilsCommon.h" +#include "neteng/fboss/bgp/if/gen-cpp2/TBgpService.h" + +namespace facebook::fboss { + +CmdShowBgpChangelist::RetType CmdShowBgpChangelist::queryClient( + const HostInfo& hostInfo) { + std::vector entriesIPv4; + std::vector entriesIPv6; + auto client = utils::createClient>(hostInfo); + + client->sync_getChangeListEntries(entriesIPv4, TBgpAfi::AFI_IPV4); + client->sync_getChangeListEntries(entriesIPv6, TBgpAfi::AFI_IPV6); + + // Append both entries into a single vector + entriesIPv4.insert(entriesIPv4.end(), entriesIPv6.begin(), entriesIPv6.end()); + TRibEntryWithHost result; + result.tRibEntries() = std::move(entriesIPv4); + result.host() = hostInfo.getName(); + result.oobName() = hostInfo.getOobName(); + result.ip() = hostInfo.getIpStr(); + return result; +} + +void CmdShowBgpChangelist::printOutput(RetType& entries, std::ostream& out) { + printRIBEntries(out, entries); +} + +} // namespace facebook::fboss diff --git a/fboss/cli/fboss2/commands/show/bgp/changelist/CmdShowBgpChangelist.h b/fboss/cli/fboss2/commands/show/bgp/changelist/CmdShowBgpChangelist.h new file mode 100644 index 0000000000000..feb587379278c --- /dev/null +++ b/fboss/cli/fboss2/commands/show/bgp/changelist/CmdShowBgpChangelist.h @@ -0,0 +1,37 @@ +/* + * 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 "fboss/cli/fboss2/CmdHandler.h" +#include "fboss/cli/fboss2/commands/show/bgp/CmdShowUtils.h" // NOLINT(misc-include-cleaner) +#include "neteng/fboss/bgp/if/gen-cpp2/bgp_thrift_types.h" + +namespace facebook::fboss { +using neteng::fboss::bgp::thrift::TRibEntry; +using neteng::fboss::bgp::thrift::TRibEntryWithHost; +using neteng::fboss::bgp_attr::TBgpAfi; + +struct CmdShowBgpChangelistTraits : public ReadCommandTraits { + using ParentCmd = void; + static constexpr utils::ObjectArgTypeId ObjectArgTypeId = + utils::ObjectArgTypeId::OBJECT_ARG_TYPE_ID_NONE; + using ObjectArgType = std::monostate; + using RetType = TRibEntryWithHost; +}; + +class CmdShowBgpChangelist + : public CmdHandler { + public: + using RetType = CmdShowBgpChangelistTraits::RetType; + RetType queryClient(const HostInfo& hostInfo); + void printOutput(RetType& entries, std::ostream& out = std::cout); +}; +} // namespace facebook::fboss diff --git a/fboss/cli/fboss2/commands/show/bgp/config/CmdShowConfigRunningBgp.cpp b/fboss/cli/fboss2/commands/show/bgp/config/CmdShowConfigRunningBgp.cpp new file mode 100644 index 0000000000000..3ab03315883c3 --- /dev/null +++ b/fboss/cli/fboss2/commands/show/bgp/config/CmdShowConfigRunningBgp.cpp @@ -0,0 +1,39 @@ +/* + * 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/show/bgp/config/CmdShowConfigRunningBgp.h" + +#include +#include "fboss/cli/fboss2/utils/CmdClientUtilsCommon.h" +#include "neteng/fboss/bgp/if/gen-cpp2/TBgpService.h" + +namespace facebook::fboss { + +CmdShowConfigRunningBgp::RetType CmdShowConfigRunningBgp::queryClient( + const HostInfo& hostInfo) { + RetType retVal; + std::string configStr; + + auto client = utils::createClient>(hostInfo); + + client->sync_getRunningConfig(configStr); + + retVal = folly::parseJson(configStr); + return retVal; +} + +void CmdShowConfigRunningBgp::printOutput( + RetType& bgpConfig, + std::ostream& out) { + out << folly::toPrettyJson(bgpConfig) << std::endl; +} + +} // namespace facebook::fboss diff --git a/fboss/cli/fboss2/commands/show/bgp/config/CmdShowConfigRunningBgp.h b/fboss/cli/fboss2/commands/show/bgp/config/CmdShowConfigRunningBgp.h new file mode 100644 index 0000000000000..d8bf784d8bbde --- /dev/null +++ b/fboss/cli/fboss2/commands/show/bgp/config/CmdShowConfigRunningBgp.h @@ -0,0 +1,27 @@ +/* + * 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 "fboss/cli/fboss2/CmdHandler.h" +#include "fboss/cli/fboss2/commands/show/bgp/config/CmdShowConfigTraits.h" + +namespace facebook::fboss { + +class CmdShowConfigRunningBgp + : public CmdHandler { + public: + using RetType = CmdShowConfigDynamicTraits::RetType; + + RetType queryClient(const HostInfo& hostInfo); + void printOutput(RetType& bgpConfig, std::ostream& out = std::cout); +}; + +} // namespace facebook::fboss diff --git a/fboss/cli/fboss2/commands/show/bgp/config/CmdShowConfigTraits.h b/fboss/cli/fboss2/commands/show/bgp/config/CmdShowConfigTraits.h new file mode 100644 index 0000000000000..bbb794f0459c8 --- /dev/null +++ b/fboss/cli/fboss2/commands/show/bgp/config/CmdShowConfigTraits.h @@ -0,0 +1,31 @@ +/* + * 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 "fboss/cli/fboss2/CmdHandler.h" + +namespace facebook::fboss { + +// Traits for BGP config commands that return dynamic JSON +struct CmdShowConfigDynamicTraits : public BaseCommandTraits { + using RetType = folly::dynamic; +}; + +// Traits for the "show config running bgp" command +struct CmdShowConfigRunningBgpTraits : public BaseCommandTraits { + static constexpr utils::ObjectArgTypeId ObjectArgTypeId = + utils::ObjectArgTypeId::OBJECT_ARG_TYPE_ID_NONE; + using ObjectArgType = std::monostate; + using RetType = folly::dynamic; +}; + +} // namespace facebook::fboss diff --git a/fboss/cli/fboss2/commands/show/bgp/neighbors/CmdShowBgpNeighbors.h b/fboss/cli/fboss2/commands/show/bgp/neighbors/CmdShowBgpNeighbors.h new file mode 100644 index 0000000000000..cb59ed61a2c69 --- /dev/null +++ b/fboss/cli/fboss2/commands/show/bgp/neighbors/CmdShowBgpNeighbors.h @@ -0,0 +1,72 @@ +/* + * 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 "fboss/cli/fboss2/CmdHandler.h" +#include "fboss/cli/fboss2/commands/show/bgp/CmdShowUtils.h" +#include "fboss/cli/fboss2/utils/CmdClientUtilsCommon.h" +#include "neteng/fboss/bgp/if/gen-cpp2/TBgpService.h" +#include "neteng/fboss/bgp/if/gen-cpp2/bgp_thrift_types.h" + +namespace facebook::fboss { + +using facebook::neteng::fboss::bgp::thrift::TBgpSession; + +struct CmdShowBgpNeighborsTraits : public ReadCommandTraits { + using ParentCmd = void; + static constexpr utils::ObjectArgTypeId ObjectArgTypeId = + utils::ObjectArgTypeId::OBJECT_ARG_TYPE_ID_IP_LIST; + using ObjectArgType = std::vector; + using RetType = std::vector; + + std::vector LocalOptions = { + {kGar, "Show decoded GAR link bandwidth ext community"}}; +}; + +class CmdShowBgpNeighbors + : public CmdHandler { + public: + using RetType = CmdShowBgpNeighborsTraits::RetType; + // - If no arguments are passed, the command will query all the neighbors + // + // - The first argument will always be considered as the neighbor ip + // - The second argument will always be consdiered as the session id + // - Every other address after the session id will be ignored. + // + // Note: Currently '--session_id' flag is no supported on FBOSS2. + // In the mean time, the way to query over a neighbor with a session id, + // the id must be passed right after the peer IP like the following: + // fboss2 show bgp neighbors 1.2.3.4 5.6.7.8 + RetType queryClient( + const HostInfo& hostInfo, + const ObjectArgType& queriedIps) { + std::vector sessions; + + auto client = utils::createClient>(hostInfo); + + if (queriedIps.size() > 1) { + std::cout << "Displaying information from neighbor: " << queriedIps[0] + << ", session id: " << queriedIps[1] << std::endl; + client->sync_getBgpNeighborsFromSession( + sessions, /*p_peerId=*/queriedIps[0], /*sessionBgpId=*/queriedIps[1]); + } else { + client->sync_getBgpNeighbors(sessions, queriedIps); + } + + return sessions; + } + + void printOutput(const RetType& neighbors, std::ostream& out = std::cout) { + printBgpNeighborsOutput(neighbors, out); + } +}; +} // namespace facebook::fboss diff --git a/fboss/cli/fboss2/commands/show/bgp/neighbors/advertised/BgpNeighborsAdvertisedDryRun.h b/fboss/cli/fboss2/commands/show/bgp/neighbors/advertised/BgpNeighborsAdvertisedDryRun.h new file mode 100644 index 0000000000000..270491386ae65 --- /dev/null +++ b/fboss/cli/fboss2/commands/show/bgp/neighbors/advertised/BgpNeighborsAdvertisedDryRun.h @@ -0,0 +1,86 @@ +/* + * 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 "configerator/structs/neteng/fboss/bgp/if/gen-cpp2/bgp_attr_types.h" +#include "fboss/cli/fboss2/CmdHandler.h" +#include "fboss/cli/fboss2/commands/show/bgp/CmdShowUtils.h" +#include "fboss/cli/fboss2/commands/show/bgp/neighbors/CmdShowBgpNeighbors.h" +#include "fboss/cli/fboss2/utils/CmdClientUtilsCommon.h" +#include "fboss/cli/fboss2/utils/CmdUtilsCommon.h" +#include "fboss/cli/fboss2/utils/HostInfo.h" +#include "neteng/fboss/bgp/if/gen-cpp2/bgp_thrift_types.h" + +namespace facebook::fboss { +using neteng::fboss::bgp::thrift::TBgpPath; +using neteng::fboss::bgp_attr::TIpPrefix; + +struct BgpNeighborsAdvertisedDryRunTraits : public ReadCommandTraits { + using ParentCmd = CmdShowBgpNeighbors; + static constexpr utils::ObjectArgTypeId ObjectArgTypeId = + utils::ObjectArgTypeId::OBJECT_ARG_TYPE_ID_IP_LIST; + using ObjectArgType = std::vector; + using RetType = NetworkPathWithHost; +}; + +class BgpNeighborsAdvertisedDryRun : public CmdHandler< + BgpNeighborsAdvertisedDryRun, + BgpNeighborsAdvertisedDryRunTraits> { + public: + using RetType = BgpNeighborsAdvertisedDryRunTraits::RetType; + using ObjectArgType = BgpNeighborsAdvertisedDryRun::ObjectArgType; + + RetType queryClient( + const HostInfo& hostInfo, + const std::vector& queriedIps, + const ObjectArgType& prefixes) { + if (queriedIps.empty()) { + std::cout + << "This command must have an IP address as argument: fboss2 show bgp neighbors [ADDR] dryrun-post-policy [PREFIX]" + << std::endl; + return {}; + } + const std::string kBgpDryRunPath = "/var/tmp/bgpcpp.conf"; + auto client = utils::createClient>(hostInfo); + + std::cout << "Attempting to read file from " << kBgpDryRunPath << "\n" + << std::endl; + + std::map networks; + client->sync_getDryRunPostfilterAdvertisedNetworks( + networks, queriedIps[0], kBgpDryRunPath); + + NetworkPathWithHost result; + result.networkPath() = vectorizePaths(networks); + if (!prefixes.empty()) { + result.networkPath() = filterNetworks(*result.networkPath(), prefixes); + } + result.host() = hostInfo.getName(); + result.oobName() = hostInfo.getOobName(); + result.ip() = hostInfo.getIpStr(); + return result; + } + + void printOutput( + const RetType& networksWithHost, + std::ostream& out = std::cout) { + const HostInfo hostInfo( + *networksWithHost.host(), + *networksWithHost.oobName(), + folly::IPAddress(*networksWithHost.ip())); + std::vector output = printRoutesInformation( + *networksWithHost.networkPath(), hostInfo, /*showPolicy=*/true); + out << folly::join('\n', output) << std::endl; + } +}; + +} // namespace facebook::fboss diff --git a/fboss/cli/fboss2/commands/show/bgp/neighbors/advertised/BgpNeighborsAdvertisedPostPolicy.h b/fboss/cli/fboss2/commands/show/bgp/neighbors/advertised/BgpNeighborsAdvertisedPostPolicy.h new file mode 100644 index 0000000000000..844dc7e3d87aa --- /dev/null +++ b/fboss/cli/fboss2/commands/show/bgp/neighbors/advertised/BgpNeighborsAdvertisedPostPolicy.h @@ -0,0 +1,83 @@ +/* + * 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 "configerator/structs/neteng/fboss/bgp/if/gen-cpp2/bgp_attr_types.h" +#include "fboss/cli/fboss2/CmdHandler.h" +#include "fboss/cli/fboss2/commands/show/bgp/CmdShowUtils.h" +#include "fboss/cli/fboss2/commands/show/bgp/neighbors/CmdShowBgpNeighbors.h" +#include "fboss/cli/fboss2/utils/CmdClientUtilsCommon.h" +#include "fboss/cli/fboss2/utils/CmdUtilsCommon.h" +#include "fboss/cli/fboss2/utils/HostInfo.h" +#include "folly/String.h" +#include "neteng/fboss/bgp/if/gen-cpp2/bgp_thrift_types.h" + +namespace facebook::fboss { +using neteng::fboss::bgp::thrift::TBgpPath; +using neteng::fboss::bgp_attr::TIpPrefix; + +struct BgpNeighborsAdvertisedPostPolicyTraits : public ReadCommandTraits { + using ParentCmd = CmdShowBgpNeighbors; + static constexpr utils::ObjectArgTypeId ObjectArgTypeId = + utils::ObjectArgTypeId::OBJECT_ARG_TYPE_ID_IP_LIST; + using ObjectArgType = std::vector; + using RetType = NetworkPathWithHost; +}; + +class BgpNeighborsAdvertisedPostPolicy + : public CmdHandler< + BgpNeighborsAdvertisedPostPolicy, + BgpNeighborsAdvertisedPostPolicyTraits> { + public: + using RetType = BgpNeighborsAdvertisedPostPolicyTraits::RetType; + using ObjectArgType = BgpNeighborsAdvertisedPostPolicy::ObjectArgType; + + RetType queryClient( + const HostInfo& hostInfo, + const std::vector& queriedIps, + const ObjectArgType& prefixes) { + if (queriedIps.empty()) { + std::cout + << "This command must have an IP address as argument: fboss2 show bgp neighbors [ADDR] post-policy [PREFIX]" + << std::endl; + return {}; + } + auto client = utils::createClient>(hostInfo); + std::map> advertisedNetworks; + client->sync_getPostfilterAdvertisedNetworks2( + advertisedNetworks, queriedIps[0]); + + if (!prefixes.empty()) { + advertisedNetworks = filterNetworks(advertisedNetworks, prefixes); + } + NetworkPathWithHost result; + result.networkPath() = std::move(advertisedNetworks); + result.host() = hostInfo.getName(); + result.oobName() = hostInfo.getOobName(); + result.ip() = hostInfo.getIpStr(); + return result; + } + + void printOutput( + const RetType& routesWithHost, + std::ostream& out = std::cout) { + const HostInfo hostInfo( + *routesWithHost.host(), + *routesWithHost.oobName(), + folly::IPAddress(*routesWithHost.ip())); + std::vector output = printRoutesInformation( + *routesWithHost.networkPath(), hostInfo, /*showPolicy=*/true); + out << folly::join('\n', output) << std::endl; + } +}; + +} // namespace facebook::fboss diff --git a/fboss/cli/fboss2/commands/show/bgp/neighbors/advertised/BgpNeighborsAdvertisedPrePolicy.h b/fboss/cli/fboss2/commands/show/bgp/neighbors/advertised/BgpNeighborsAdvertisedPrePolicy.h new file mode 100644 index 0000000000000..aaf326ec57507 --- /dev/null +++ b/fboss/cli/fboss2/commands/show/bgp/neighbors/advertised/BgpNeighborsAdvertisedPrePolicy.h @@ -0,0 +1,82 @@ +/* + * 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 "configerator/structs/neteng/fboss/bgp/if/gen-cpp2/bgp_attr_types.h" +#include "fboss/cli/fboss2/CmdHandler.h" +#include "fboss/cli/fboss2/commands/show/bgp/CmdShowUtils.h" +#include "fboss/cli/fboss2/commands/show/bgp/neighbors/CmdShowBgpNeighbors.h" +#include "fboss/cli/fboss2/utils/CmdClientUtilsCommon.h" +#include "fboss/cli/fboss2/utils/CmdUtilsCommon.h" +#include "fboss/cli/fboss2/utils/HostInfo.h" +#include "folly/String.h" +#include "neteng/fboss/bgp/if/gen-cpp2/bgp_thrift_types.h" + +namespace facebook::fboss { +using neteng::fboss::bgp::thrift::TBgpPath; +using neteng::fboss::bgp_attr::TIpPrefix; + +struct BgpNeighborsAdvertisedPrePolicyTraits : public ReadCommandTraits { + using ParentCmd = CmdShowBgpNeighbors; + static constexpr utils::ObjectArgTypeId ObjectArgTypeId = + utils::ObjectArgTypeId::OBJECT_ARG_TYPE_ID_IP_LIST; + using ObjectArgType = std::vector; + using RetType = NetworkPathWithHost; +}; + +class BgpNeighborsAdvertisedPrePolicy + : public CmdHandler< + BgpNeighborsAdvertisedPrePolicy, + BgpNeighborsAdvertisedPrePolicyTraits> { + public: + using RetType = BgpNeighborsAdvertisedPrePolicyTraits::RetType; + using ObjectArgType = BgpNeighborsAdvertisedPrePolicy::ObjectArgType; + + RetType queryClient( + const HostInfo& hostInfo, + const std::vector& queriedIps, + const ObjectArgType& prefixes) { + NetworkPathWithHost result; + if (queriedIps.empty()) { + std::cout + << "This command must have an IP address as argument: fboss2 show bgp neighbors [ADDR] pre-policy [PREFIX]" + << std::endl; + return result; + } + auto client = utils::createClient>(hostInfo); + std::map> advertisedNetworks; + client->sync_getPrefilterAdvertisedNetworks2( + advertisedNetworks, queriedIps[0]); + + if (!prefixes.empty()) { + advertisedNetworks = filterNetworks(advertisedNetworks, prefixes); + } + result.networkPath() = std::move(advertisedNetworks); + result.host() = hostInfo.getName(); + result.oobName() = hostInfo.getOobName(); + result.ip() = hostInfo.getIpStr(); + return result; + } + + void printOutput( + const RetType& routesWithHost, + std::ostream& out = std::cout) { + const HostInfo hostInfo( + *routesWithHost.host(), + *routesWithHost.oobName(), + folly::IPAddress(*routesWithHost.ip())); + std::vector output = printRoutesInformation( + *routesWithHost.networkPath(), hostInfo, /*showPolicy=*/false); + out << folly::join('\n', output) << std::endl; + } +}; +} // namespace facebook::fboss diff --git a/fboss/cli/fboss2/commands/show/bgp/neighbors/advertised/BgpNeighborsAdvertisedRejected.h b/fboss/cli/fboss2/commands/show/bgp/neighbors/advertised/BgpNeighborsAdvertisedRejected.h new file mode 100644 index 0000000000000..573e340c3a4b7 --- /dev/null +++ b/fboss/cli/fboss2/commands/show/bgp/neighbors/advertised/BgpNeighborsAdvertisedRejected.h @@ -0,0 +1,90 @@ +/* + * 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 "configerator/structs/neteng/fboss/bgp/if/gen-cpp2/bgp_attr_types.h" +#include "fboss/cli/fboss2/CmdHandler.h" +#include "fboss/cli/fboss2/commands/show/bgp/CmdShowUtils.h" +#include "fboss/cli/fboss2/commands/show/bgp/neighbors/CmdShowBgpNeighbors.h" +#include "fboss/cli/fboss2/utils/CmdClientUtilsCommon.h" +#include "fboss/cli/fboss2/utils/CmdUtilsCommon.h" +#include "fboss/cli/fboss2/utils/HostInfo.h" +#include "folly/String.h" +#include "neteng/fboss/bgp/if/gen-cpp2/bgp_thrift_types.h" + +namespace facebook::fboss { +using neteng::fboss::bgp::thrift::TBgpPath; +using neteng::fboss::bgp_attr::TIpPrefix; + +struct BgpNeighborsAdvertisedRejectedTraits : public ReadCommandTraits { + using ParentCmd = CmdShowBgpNeighbors; + static constexpr utils::ObjectArgTypeId ObjectArgTypeId = + utils::ObjectArgTypeId::OBJECT_ARG_TYPE_ID_IP_LIST; + using ObjectArgType = std::vector; + using RetType = NetworkPathWithHost; +}; + +class BgpNeighborsAdvertisedRejected + : public CmdHandler< + BgpNeighborsAdvertisedRejected, + BgpNeighborsAdvertisedRejectedTraits> { + public: + using RetType = BgpNeighborsAdvertisedRejectedTraits::RetType; + using ObjectArgType = BgpNeighborsAdvertisedRejected::ObjectArgType; + + RetType queryClient( + const HostInfo& hostInfo, + const std::vector& queriedIps, + const ObjectArgType& prefixes) { + if (queriedIps.empty()) { + std::cout + << "This command must have an IP address as argument: fboss2 show bgp neighbors [ADDR] rejected [PREFIX]" + << std::endl; + return {}; + } + auto client = utils::createClient>(hostInfo); + std::map> receivedNetworks; + std::map> acceptedNetworks; + + client->sync_getPrefilterAdvertisedNetworks2( + receivedNetworks, queriedIps[0]); + client->sync_getPostfilterAdvertisedNetworks2( + acceptedNetworks, queriedIps[0]); + + std::map> rejectedNetworks = + getRejectedNetworks(receivedNetworks, acceptedNetworks); + + if (!prefixes.empty()) { + rejectedNetworks = filterNetworks(rejectedNetworks, prefixes); + } + + NetworkPathWithHost result; + result.networkPath() = std::move(rejectedNetworks); + result.host() = hostInfo.getName(); + result.oobName() = hostInfo.getOobName(); + result.ip() = hostInfo.getIpStr(); + return result; + } + + void printOutput( + const RetType& routesWithHost, + std::ostream& out = std::cout) { + const HostInfo hostInfo( + *routesWithHost.host(), + *routesWithHost.oobName(), + folly::IPAddress(*routesWithHost.ip())); + std::vector output = printRoutesInformation( + *routesWithHost.networkPath(), hostInfo, /*showPolicy=*/true); + out << folly::join('\n', output) << std::endl; + } +}; +} // namespace facebook::fboss diff --git a/fboss/cli/fboss2/commands/show/bgp/neighbors/received/BgpNeighborsReceivedPostPolicy.h b/fboss/cli/fboss2/commands/show/bgp/neighbors/received/BgpNeighborsReceivedPostPolicy.h new file mode 100644 index 0000000000000..55ef2bcd8eff3 --- /dev/null +++ b/fboss/cli/fboss2/commands/show/bgp/neighbors/received/BgpNeighborsReceivedPostPolicy.h @@ -0,0 +1,100 @@ +/* + * 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 "configerator/structs/neteng/fboss/bgp/if/gen-cpp2/bgp_attr_types.h" +#include "fboss/cli/fboss2/CmdHandler.h" +#include "fboss/cli/fboss2/commands/show/bgp/CmdShowUtils.h" +#include "fboss/cli/fboss2/commands/show/bgp/neighbors/CmdShowBgpNeighbors.h" +#include "fboss/cli/fboss2/utils/CmdClientUtilsCommon.h" +#include "fboss/cli/fboss2/utils/CmdUtilsCommon.h" +#include "fboss/cli/fboss2/utils/HostInfo.h" +#include "folly/String.h" +#include "neteng/fboss/bgp/if/gen-cpp2/bgp_thrift_types.h" + +namespace facebook::fboss { +using neteng::fboss::bgp::thrift::TBgpPath; +using neteng::fboss::bgp_attr::TIpPrefix; + +struct BgpNeighborsReceivedPostPolicyTraits : public ReadCommandTraits { + using ParentCmd = CmdShowBgpNeighbors; + static constexpr utils::ObjectArgTypeId ObjectArgTypeId = + utils::ObjectArgTypeId::OBJECT_ARG_TYPE_ID_IP_LIST; + using ObjectArgType = std::vector; + using RetType = NetworkPathWithHost; +}; + +class BgpNeighborsReceivedPostPolicy + : public CmdHandler< + BgpNeighborsReceivedPostPolicy, + BgpNeighborsReceivedPostPolicyTraits> { + public: + using RetType = BgpNeighborsReceivedPostPolicyTraits::RetType; + using ObjectArgType = BgpNeighborsReceivedPostPolicy::ObjectArgType; + + // - The first argument will always be considered as the neighbor ip + // - The second argument will always be consdiered as the session id + // - Every other address after the session id will be ignored. + // + // Note: Currently '--session_id' flag is no supported on FBOSS2. + // In the mean time, the way to query over a neighbor with a session id, + // the id must be passed right after the peer IP like the following: + // fboss2 show bgp neighbors 1.2.3.4 5.6.7.8 + RetType queryClient( + const HostInfo& hostInfo, + const std::vector& queriedIps, + const ObjectArgType& prefixes) { + NetworkPathWithHost result; + if (queriedIps.empty()) { + std::cout + << "This command must have an IP address as argument: fboss2 show bgp neighbors [ADDR] post-policy [PREFIX]" + << std::endl; + return result; + } + auto client = utils::createClient>(hostInfo); + std::map> receivedNetworks; + + if (queriedIps.size() > 1) { + std::cout << "Displaying information from neighbor: " << queriedIps[0] + << ", session id: " << queriedIps[1] << std::endl; + client->sync_getPostfilterReceivedNetworksFromSession2( + receivedNetworks, queriedIps[0], queriedIps[1]); + } else { + client->sync_getPostfilterReceivedNetworks2( + receivedNetworks, queriedIps[0]); + } + + if (!prefixes.empty()) { + receivedNetworks = filterNetworks(receivedNetworks, prefixes); + } + + result.networkPath() = std::move(receivedNetworks); + result.host() = hostInfo.getName(); + result.oobName() = hostInfo.getOobName(); + result.ip() = hostInfo.getIpStr(); + return result; + } + + void printOutput( + const RetType& routesWithHost, + std::ostream& out = std::cout) { + const HostInfo hostInfo( + *routesWithHost.host(), + *routesWithHost.oobName(), + folly::IPAddress(*routesWithHost.ip())); + std::vector output = printRoutesInformation( + *routesWithHost.networkPath(), hostInfo, /*showPolicy=*/true); + out << folly::join('\n', output) << std::endl; + } +}; + +} // namespace facebook::fboss diff --git a/fboss/cli/fboss2/commands/show/bgp/neighbors/received/BgpNeighborsReceivedPrePolicy.h b/fboss/cli/fboss2/commands/show/bgp/neighbors/received/BgpNeighborsReceivedPrePolicy.h new file mode 100644 index 0000000000000..8ae249f7c6321 --- /dev/null +++ b/fboss/cli/fboss2/commands/show/bgp/neighbors/received/BgpNeighborsReceivedPrePolicy.h @@ -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. + * + */ + +#pragma once + +#include "configerator/structs/neteng/fboss/bgp/if/gen-cpp2/bgp_attr_types.h" +#include "fboss/cli/fboss2/CmdHandler.h" +#include "fboss/cli/fboss2/commands/show/bgp/CmdShowUtils.h" +#include "fboss/cli/fboss2/commands/show/bgp/neighbors/CmdShowBgpNeighbors.h" +#include "fboss/cli/fboss2/utils/CmdClientUtilsCommon.h" +#include "fboss/cli/fboss2/utils/CmdUtilsCommon.h" +#include "fboss/cli/fboss2/utils/HostInfo.h" +#include "folly/String.h" +#include "neteng/fboss/bgp/if/gen-cpp2/bgp_thrift_types.h" + +namespace facebook::fboss { +using neteng::fboss::bgp::thrift::TBgpPath; +using neteng::fboss::bgp_attr::TIpPrefix; + +struct BgpNeighborsReceivedPrePolicyTraits : public ReadCommandTraits { + using ParentCmd = CmdShowBgpNeighbors; + static constexpr utils::ObjectArgTypeId ObjectArgTypeId = + utils::ObjectArgTypeId::OBJECT_ARG_TYPE_ID_IP_LIST; + using ObjectArgType = std::vector; + using RetType = NetworkPathWithHost; +}; + +class BgpNeighborsReceivedPrePolicy : public CmdHandler< + BgpNeighborsReceivedPrePolicy, + BgpNeighborsReceivedPrePolicyTraits> { + public: + using RetType = BgpNeighborsReceivedPrePolicyTraits::RetType; + using ObjectArgType = BgpNeighborsReceivedPrePolicy::ObjectArgType; + + // - The first argument will always be considered as the neighbor ip + // - The second argument will always be consdiered as the session id + // - Every other address after the session id will be ignored. + // + // Note: Currently '--session_id' flag is no supported on FBOSS2. + // In the mean time, the way to query over a neighbor with a session id, + // the id must be passed right after the peer IP like the following: + // fboss2 show bgp neighbors 1.2.3.4 5.6.7.8 + RetType queryClient( + const HostInfo& hostInfo, + const std::vector& queriedIps, + const ObjectArgType& prefixes) { + if (queriedIps.empty()) { + std::cout + << "This command must have an IP address as argument: fboss2 show bgp neighbors [ADDR] pre-policy [PREFIX]" + << std::endl; + return {}; + } + auto client = utils::createClient>(hostInfo); + std::map> receivedNetworks; + + if (queriedIps.size() > 1) { + std::cout << "Displaying information from neighbor: " << queriedIps[0] + << ", session id: " << queriedIps[1] << std::endl; + client->sync_getPrefilterReceivedNetworksFromSession2( + receivedNetworks, queriedIps[0], queriedIps[1]); + } else { + client->sync_getPrefilterReceivedNetworks2( + receivedNetworks, queriedIps[0]); + } + + if (!prefixes.empty()) { + receivedNetworks = filterNetworks(receivedNetworks, prefixes); + } + NetworkPathWithHost result; + result.networkPath() = std::move(receivedNetworks); + result.host() = hostInfo.getName(); + result.oobName() = hostInfo.getOobName(); + result.ip() = hostInfo.getIpStr(); + return result; + } + + void printOutput( + const RetType& routesWithHost, + std::ostream& out = std::cout) { + const HostInfo hostInfo( + *routesWithHost.host(), + *routesWithHost.oobName(), + folly::IPAddress(*routesWithHost.ip())); + std::vector output = printRoutesInformation( + *routesWithHost.networkPath(), hostInfo, /*showPolicy=*/false); + out << folly::join('\n', output) << std::endl; + } +}; +} // namespace facebook::fboss diff --git a/fboss/cli/fboss2/commands/show/bgp/neighbors/received/BgpNeighborsReceivedRejected.h b/fboss/cli/fboss2/commands/show/bgp/neighbors/received/BgpNeighborsReceivedRejected.h new file mode 100644 index 0000000000000..f579eb02d057f --- /dev/null +++ b/fboss/cli/fboss2/commands/show/bgp/neighbors/received/BgpNeighborsReceivedRejected.h @@ -0,0 +1,90 @@ +/* + * 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 "configerator/structs/neteng/fboss/bgp/if/gen-cpp2/bgp_attr_types.h" +#include "fboss/cli/fboss2/CmdHandler.h" +#include "fboss/cli/fboss2/commands/show/bgp/CmdShowUtils.h" +#include "fboss/cli/fboss2/commands/show/bgp/neighbors/CmdShowBgpNeighbors.h" +#include "fboss/cli/fboss2/utils/CmdClientUtilsCommon.h" +#include "fboss/cli/fboss2/utils/CmdUtilsCommon.h" +#include "fboss/cli/fboss2/utils/HostInfo.h" +#include "folly/String.h" +#include "neteng/fboss/bgp/if/gen-cpp2/bgp_thrift_types.h" + +namespace facebook::fboss { +using neteng::fboss::bgp::thrift::TBgpPath; +using neteng::fboss::bgp_attr::TIpPrefix; + +struct BgpNeighborsReceivedRejectedTraits : public ReadCommandTraits { + using ParentCmd = CmdShowBgpNeighbors; + static constexpr utils::ObjectArgTypeId ObjectArgTypeId = + utils::ObjectArgTypeId::OBJECT_ARG_TYPE_ID_IP_LIST; + using ObjectArgType = std::vector; + using RetType = NetworkPathWithHost; +}; + +class BgpNeighborsReceivedRejected : public CmdHandler< + BgpNeighborsReceivedRejected, + BgpNeighborsReceivedRejectedTraits> { + public: + using RetType = BgpNeighborsReceivedRejectedTraits::RetType; + using ObjectArgType = BgpNeighborsReceivedRejected::ObjectArgType; + + RetType queryClient( + const HostInfo& hostInfo, + const std::vector& queriedIps, + const ObjectArgType& prefixes) { + if (queriedIps.empty()) { + std::cout + << "This command must have an IP address as argument: fboss2 show bgp neighbors [ADDR] rejected [PREFIX]" + << std::endl; + return {}; + } + auto client = utils::createClient>(hostInfo); + std::map> receivedNetworks; + std::map> acceptedNetworks; + + client->sync_getPrefilterReceivedNetworks2(receivedNetworks, queriedIps[0]); + client->sync_getPostfilterReceivedNetworks2( + acceptedNetworks, queriedIps[0]); + + std::map> rejectedNetworks = + getRejectedNetworks(receivedNetworks, acceptedNetworks); + + if (!prefixes.empty()) { + rejectedNetworks = filterNetworks(rejectedNetworks, prefixes); + } + + NetworkPathWithHost result; + result.networkPath() = std::move(rejectedNetworks); + result.host() = hostInfo.getName(); + result.oobName() = hostInfo.getOobName(); + result.ip() = hostInfo.getIpStr(); + + return result; + } + + void printOutput( + const RetType& routesWithHost, + std::ostream& out = std::cout) { + const HostInfo hostInfo( + *routesWithHost.host(), + *routesWithHost.oobName(), + folly::IPAddress(*routesWithHost.ip())); + std::vector output = printRoutesInformation( + *routesWithHost.networkPath(), hostInfo, /*showPolicy=*/true); + out << folly::join('\n', output) << std::endl; + } +}; + +} // namespace facebook::fboss diff --git a/fboss/cli/fboss2/commands/show/bgp/neighbors/session_id/CmdBgpNeighborsSessionId.h b/fboss/cli/fboss2/commands/show/bgp/neighbors/session_id/CmdBgpNeighborsSessionId.h new file mode 100644 index 0000000000000..efc66e91a2513 --- /dev/null +++ b/fboss/cli/fboss2/commands/show/bgp/neighbors/session_id/CmdBgpNeighborsSessionId.h @@ -0,0 +1,61 @@ +/* + * 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 "fboss/cli/fboss2/CmdHandler.h" +#include "fboss/cli/fboss2/commands/show/bgp/CmdShowUtils.h" +#include "fboss/cli/fboss2/commands/show/bgp/neighbors/CmdShowBgpNeighbors.h" +#include "neteng/fboss/bgp/if/gen-cpp2/TBgpService.h" +#include "neteng/fboss/bgp/if/gen-cpp2/bgp_thrift_types.h" // NOLINT(misc-include-cleaner) + +namespace facebook::fboss { + +struct CmdBgpNeighborsSessionIdTraits : public ReadCommandTraits { + using ParentCmd = CmdShowBgpNeighbors; + static constexpr utils::ObjectArgTypeId ObjectArgTypeId = + utils::ObjectArgTypeId::OBJECT_ARG_TYPE_ID_PEERID_LIST; + using ObjectArgType = std::vector; + using RetType = std::vector; +}; + +class CmdBgpNeighborsSessionId : public CmdHandler< + CmdBgpNeighborsSessionId, + CmdBgpNeighborsSessionIdTraits> { + public: + using RetType = CmdBgpNeighborsSessionIdTraits::RetType; + + RetType queryClient( + const HostInfo& hostInfo, + const ObjectArgType& queriedIps, + const ObjectArgType& queriedSessions) { + std::vector sessions; + + if (queriedSessions.empty()) { + std::cout + << "This command must have a session ID as argument: fboss2 show bgp neighbors [ADDR] session_id [Session ID]" + << std::endl; + return {}; + } + + auto client = utils::createClient>(hostInfo); + + client->sync_getBgpNeighborsFromSession( + sessions, queriedIps[0], queriedSessions[0]); + + return sessions; + } + + void printOutput(const RetType& neighbors, std::ostream& out = std::cout) { + printBgpNeighborsOutput(neighbors, out); + } +}; +} // namespace facebook::fboss diff --git a/fboss/cli/fboss2/commands/show/bgp/shadowrib/CmdShowBgpShadowRib.cpp b/fboss/cli/fboss2/commands/show/bgp/shadowrib/CmdShowBgpShadowRib.cpp new file mode 100644 index 0000000000000..38b1fed1cd061 --- /dev/null +++ b/fboss/cli/fboss2/commands/show/bgp/shadowrib/CmdShowBgpShadowRib.cpp @@ -0,0 +1,43 @@ +/* + * 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/show/bgp/shadowrib/CmdShowBgpShadowRib.h" + +#include "fboss/cli/fboss2/commands/show/bgp/CmdShowUtils.h" +#include "fboss/cli/fboss2/utils/CmdClientUtilsCommon.h" +#include "neteng/fboss/bgp/if/gen-cpp2/TBgpService.h" + +namespace facebook::fboss { + +CmdShowBgpShadowRib::RetType CmdShowBgpShadowRib::queryClient( + const HostInfo& hostInfo) { + std::vector entriesIPv4; + std::vector entriesIPv6; + auto client = utils::createClient>(hostInfo); + + client->sync_getShadowRibEntries(entriesIPv4, TBgpAfi::AFI_IPV4); + client->sync_getShadowRibEntries(entriesIPv6, TBgpAfi::AFI_IPV6); + + // Append both entries into a single vector + entriesIPv4.insert(entriesIPv4.end(), entriesIPv6.begin(), entriesIPv6.end()); + TRibEntryWithHost result; + result.tRibEntries() = std::move(entriesIPv4); + result.host() = hostInfo.getName(); + result.oobName() = hostInfo.getOobName(); + result.ip() = hostInfo.getIpStr(); + return result; +} + +void CmdShowBgpShadowRib::printOutput(RetType& entries, std::ostream& out) { + printRIBEntries(out, entries); +} + +} // namespace facebook::fboss diff --git a/fboss/cli/fboss2/commands/show/bgp/shadowrib/CmdShowBgpShadowRib.h b/fboss/cli/fboss2/commands/show/bgp/shadowrib/CmdShowBgpShadowRib.h new file mode 100644 index 0000000000000..d23be6fe3b11b --- /dev/null +++ b/fboss/cli/fboss2/commands/show/bgp/shadowrib/CmdShowBgpShadowRib.h @@ -0,0 +1,37 @@ +/* + * 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 "fboss/cli/fboss2/CmdHandler.h" +#include "fboss/cli/fboss2/commands/show/bgp/CmdShowUtils.h" // NOLINT(misc-include-cleaner) +#include "neteng/fboss/bgp/if/gen-cpp2/bgp_thrift_types.h" + +namespace facebook::fboss { +using neteng::fboss::bgp::thrift::TRibEntry; +using neteng::fboss::bgp::thrift::TRibEntryWithHost; +using neteng::fboss::bgp_attr::TBgpAfi; + +struct CmdShowBgpShadowRibTraits : public ReadCommandTraits { + using ParentCmd = void; + static constexpr utils::ObjectArgTypeId ObjectArgTypeId = + utils::ObjectArgTypeId::OBJECT_ARG_TYPE_ID_NONE; + using ObjectArgType = std::monostate; + using RetType = TRibEntryWithHost; +}; + +class CmdShowBgpShadowRib + : public CmdHandler { + public: + using RetType = CmdShowBgpShadowRibTraits::RetType; + RetType queryClient(const HostInfo& hostInfo); + void printOutput(RetType& entries, std::ostream& out = std::cout); +}; +} // namespace facebook::fboss diff --git a/fboss/cli/fboss2/commands/show/bgp/stats/CmdShowBgpStatsAttrs.h b/fboss/cli/fboss2/commands/show/bgp/stats/CmdShowBgpStatsAttrs.h new file mode 100644 index 0000000000000..5d3008d77aa90 --- /dev/null +++ b/fboss/cli/fboss2/commands/show/bgp/stats/CmdShowBgpStatsAttrs.h @@ -0,0 +1,70 @@ +/* + * 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 "fboss/cli/fboss2/CmdHandler.h" +#include "fboss/cli/fboss2/utils/CmdClientUtilsCommon.h" +#include "neteng/fboss/bgp/if/gen-cpp2/TBgpService.h" +#include "neteng/fboss/bgp/if/gen-cpp2/bgp_thrift_types.h" + +namespace facebook::fboss { +using facebook::neteng::fboss::bgp::thrift::TAttributeStats; + +struct CmdShowBgpStatsAttrsTraits : public ReadCommandTraits { + static constexpr utils::ObjectArgTypeId ObjectArgTypeId = + utils::ObjectArgTypeId::OBJECT_ARG_TYPE_ID_NONE; + using ObjectArgType = std::monostate; + using RetType = TAttributeStats; +}; + +class CmdShowBgpStatsAttrs + : public CmdHandler { + public: + using RetType = CmdShowBgpStatsAttrsTraits::RetType; + + RetType queryClient(const HostInfo& hostInfo) { + TAttributeStats stats; + auto client = utils::createClient>(hostInfo); + client->sync_getAttributeStats(stats); + return stats; + } + + void printOutput(const RetType& stats, std::ostream& out = std::cout) { + out << "BGP attribute statistics:" << std::endl; + out << " Total number of attributes: " + << folly::copy(stats.total_num_of_attributes().value()) << std::endl; + out << " Total number of unique attributes: " + << folly::copy(stats.total_unique_attributes().value()) << std::endl; + out << fmt::format( + " Average attribute reference count: {:.2f}\n", + folly::copy(stats.avg_attribute_refcount().value())); + out << fmt::format( + " Average community list length: {:.2f}\n", + folly::copy(stats.avg_community_list_len().value())); + out << fmt::format( + " Average extended community list length: {:.2f}\n", + folly::copy(stats.avg_extcommunity_list_len().value())); + out << fmt::format( + " Average as path length: {:.2f}\n", + folly::copy(stats.avg_as_path_len().value())); + out << fmt::format( + " Average cluster list length: {:.2f}\n", + folly::copy(stats.avg_cluster_list_len().value())); + out << fmt::format( + " Average topology info length: {:.2f}\n", + folly::copy(stats.avg_topology_info_len().value())); + } +}; +} // namespace facebook::fboss diff --git a/fboss/cli/fboss2/commands/show/bgp/stats/CmdShowBgpStatsEntries.cpp b/fboss/cli/fboss2/commands/show/bgp/stats/CmdShowBgpStatsEntries.cpp new file mode 100644 index 0000000000000..2004cf8c22cac --- /dev/null +++ b/fboss/cli/fboss2/commands/show/bgp/stats/CmdShowBgpStatsEntries.cpp @@ -0,0 +1,48 @@ +/* + * 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/show/bgp/stats/CmdShowBgpStatsEntries.h" + +#include + +#include "fboss/cli/fboss2/utils/CmdClientUtilsCommon.h" +#include "neteng/fboss/bgp/if/gen-cpp2/TBgpService.h" + +namespace facebook::fboss { + +CmdShowBgpStatsEntries::RetType CmdShowBgpStatsEntries::queryClient( + const HostInfo& hostInfo) { + TEntryStats stats; + auto client = utils::createClient>(hostInfo); + client->sync_getEntryStats(stats); + return stats; +} + +void CmdShowBgpStatsEntries::printOutput( + const RetType& stats, + std::ostream& out) { + out << "BGP entry statistics:" << std::endl; + out << " Total number of unicast routes: " + << folly::copy(stats.total_ucast_routes().value()) << std::endl; + out << " Total number of rib paths: " + << folly::copy(stats.total_rib_paths().value()) << std::endl; + out << " Total number of adjribs: " + << folly::copy(stats.total_adj_ribs().value()) << std::endl; + out << " Total number of originated routes: " + << folly::copy(stats.total_originated_routes().value()) << std::endl; + out << " Total number of shadow rib entries: " + << folly::copy(stats.total_shadow_rib_entries().value()) << std::endl; + out << " Total number of tracked netlink wrapper interfaces: " + << folly::copy(stats.total_netlink_wrapper_interfaces().value()) + << std::endl; +} + +} // namespace facebook::fboss diff --git a/fboss/cli/fboss2/commands/show/bgp/stats/CmdShowBgpStatsEntries.h b/fboss/cli/fboss2/commands/show/bgp/stats/CmdShowBgpStatsEntries.h new file mode 100644 index 0000000000000..50ab83338b234 --- /dev/null +++ b/fboss/cli/fboss2/commands/show/bgp/stats/CmdShowBgpStatsEntries.h @@ -0,0 +1,35 @@ +/* + * 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 "fboss/cli/fboss2/CmdHandler.h" +#include "neteng/fboss/bgp/if/gen-cpp2/bgp_thrift_types.h" + +namespace facebook::fboss { +using facebook::neteng::fboss::bgp::thrift::TEntryStats; + +struct CmdShowBgpStatsEntriesTraits : public ReadCommandTraits { + static constexpr utils::ObjectArgTypeId ObjectArgTypeId = + utils::ObjectArgTypeId::OBJECT_ARG_TYPE_ID_NONE; + using ObjectArgType = std::monostate; + using RetType = TEntryStats; +}; + +class CmdShowBgpStatsEntries + : public CmdHandler { + public: + using RetType = CmdShowBgpStatsEntriesTraits::RetType; + + RetType queryClient(const HostInfo& hostInfo); + void printOutput(const RetType& stats, std::ostream& out = std::cout); +}; + +} // namespace facebook::fboss diff --git a/fboss/cli/fboss2/commands/show/bgp/stats/CmdShowBgpStatsPolicy.cpp b/fboss/cli/fboss2/commands/show/bgp/stats/CmdShowBgpStatsPolicy.cpp new file mode 100644 index 0000000000000..f1bd9b5125373 --- /dev/null +++ b/fboss/cli/fboss2/commands/show/bgp/stats/CmdShowBgpStatsPolicy.cpp @@ -0,0 +1,77 @@ +/* + * 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/show/bgp/stats/CmdShowBgpStatsPolicy.h" + +#include +#include + +#include "fboss/cli/fboss2/utils/CmdClientUtilsCommon.h" +#include "neteng/fboss/bgp/if/gen-cpp2/TBgpService.h" + +namespace facebook::fboss { + +CmdShowBgpStatsPolicy::RetType CmdShowBgpStatsPolicy::queryClient( + const HostInfo& hostInfo) { + TPolicyStats policy_stats; + auto client = utils::createClient>(hostInfo); + + client->sync_getPolicyStats(policy_stats); + return policy_stats; +} + +void CmdShowBgpStatsPolicy::printOutput( + const RetType& policy_stats, + std::ostream& out) { + out << "BGP policy statistics:"; + for (const auto& stats : policy_stats.policy_statement_stats().value()) { + const auto statement_prefix_count = + folly::copy(stats.prefix_hit_count().value()); + out << fmt::format("\n Policy Name: {}", stats.name().value()); + out << fmt::format( + "\n\t Number of executions: {}", + folly::copy(stats.num_of_runs().value())); + out << fmt::format( + "\n\t Number of hits: {} prefixes", statement_prefix_count); + out << fmt::format( + "\n\t Average time of execution: {}us", + folly::copy(stats.avg_time().value())); + out << fmt::format( + "\n\t Maximum time of execution: {}us", + folly::copy(stats.max_time().value())); + + out << "\n\t Term Description Hits/Misses"; + + int term_number = 1, hit_count_so_far = 0; + for (const auto& term_stats : stats.term_stats().value()) { + const auto& term_prefix_count = + folly::copy(term_stats.prefix_hit_count().value()); + out << fmt::format( + "\n\t {:<5} {:<48} {:>6}/{:<6}", + term_number, + term_stats.description().value(), + term_prefix_count, + (statement_prefix_count - term_prefix_count - hit_count_so_far)); + + ++term_number; + hit_count_so_far += term_prefix_count; + } + if (hit_count_so_far <= statement_prefix_count) { + out << fmt::format( + "\n\t {:<5} {:<48} {:>6}/0\n", + term_number, + "Default deny (Implicit)", + statement_prefix_count - hit_count_so_far); + } + } +} + +} // namespace facebook::fboss diff --git a/fboss/cli/fboss2/commands/show/bgp/stats/CmdShowBgpStatsPolicy.h b/fboss/cli/fboss2/commands/show/bgp/stats/CmdShowBgpStatsPolicy.h new file mode 100644 index 0000000000000..f518508a9a197 --- /dev/null +++ b/fboss/cli/fboss2/commands/show/bgp/stats/CmdShowBgpStatsPolicy.h @@ -0,0 +1,34 @@ +/* + * 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 "fboss/cli/fboss2/CmdHandler.h" +#include "neteng/fboss/bgp/if/gen-cpp2/policy_thrift_types.h" + +namespace facebook::fboss { +using facebook::neteng::routing::policy::thrift::TPolicyStats; + +struct CmdShowBgpStatsPolicyTraits : public ReadCommandTraits { + static constexpr utils::ObjectArgTypeId ObjectArgTypeId = + utils::ObjectArgTypeId::OBJECT_ARG_TYPE_ID_NONE; + using ObjectArgType = std::monostate; + using RetType = TPolicyStats; +}; + +class CmdShowBgpStatsPolicy + : public CmdHandler { + public: + using RetType = CmdShowBgpStatsPolicyTraits::RetType; + + RetType queryClient(const HostInfo& hostInfo); + void printOutput(const RetType& policy_stats, std::ostream& out = std::cout); +}; +} // namespace facebook::fboss diff --git a/fboss/cli/fboss2/commands/show/bgp/stream/CmdShowBgpStreamSubscriber.h b/fboss/cli/fboss2/commands/show/bgp/stream/CmdShowBgpStreamSubscriber.h new file mode 100644 index 0000000000000..e648aa6377b87 --- /dev/null +++ b/fboss/cli/fboss2/commands/show/bgp/stream/CmdShowBgpStreamSubscriber.h @@ -0,0 +1,58 @@ +/* + * 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 "fboss/cli/fboss2/CmdHandler.h" +#include "fboss/cli/fboss2/commands/show/bgp/CmdShowUtils.h" // NOLINT(misc-include-cleaner) +#include "fboss/cli/fboss2/utils/CmdClientUtilsCommon.h" // NOLINT(misc-include-cleaner) +#include "fboss/cli/fboss2/utils/CmdUtilsCommon.h" +#include "fboss/cli/fboss2/utils/HostInfo.h" + +namespace facebook::fboss { +using namespace neteng::fboss::bgp::thrift; + +struct CmdShowBgpStreamSubscriberTraits : public ReadCommandTraits { + using ParentCmd = void; + static constexpr utils::ObjectArgTypeId ObjectArgTypeId = + utils::ObjectArgTypeId::OBJECT_ARG_TYPE_ID_PEERID_LIST; + using ObjectArgType = std::vector; + using RetType = std::vector; +}; + +class CmdShowBgpStreamSubscriber : public CmdHandler< + CmdShowBgpStreamSubscriber, + CmdShowBgpStreamSubscriberTraits> { + public: + using RetType = CmdShowBgpStreamSubscriberTraits::RetType; + using ObjectArgType = CmdShowBgpStreamSubscriberTraits::ObjectArgType; + RetType queryClient(const HostInfo&, const ObjectArgType& peerIds) { + if (peerIds.empty()) { + std::cout + << "No subscriber id entered. Usage: fboss2 show bgp stream subscriber pre-policy/post-policy" + << std::endl; + } + return peerIds; + } + + void printOutput(RetType& peerIds) { + if (!peerIds.empty()) { + std::cout + << fmt::format( + "Missing policy argument: fboss2 show bgp stream subscriber {} pre-policy/post-policy", + peerIds[0]) + << std::endl; + } + } +}; + +} // namespace facebook::fboss diff --git a/fboss/cli/fboss2/commands/show/bgp/stream/CmdShowBgpStreamSummary.h b/fboss/cli/fboss2/commands/show/bgp/stream/CmdShowBgpStreamSummary.h new file mode 100644 index 0000000000000..519069c77af32 --- /dev/null +++ b/fboss/cli/fboss2/commands/show/bgp/stream/CmdShowBgpStreamSummary.h @@ -0,0 +1,85 @@ +/* + * 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 "fboss/cli/fboss2/CmdHandler.h" +#include "fboss/cli/fboss2/commands/show/bgp/CmdShowUtils.h" // NOLINT(misc-include-cleaner) +#include "fboss/cli/fboss2/utils/CmdClientUtilsCommon.h" +#include "fboss/cli/fboss2/utils/CmdUtilsCommon.h" +#include "fboss/cli/fboss2/utils/HostInfo.h" +#include "fboss/cli/fboss2/utils/Table.h" +#include "neteng/fboss/bgp/if/gen-cpp2/TBgpService.h" +#include "neteng/fboss/bgp/if/gen-cpp2/bgp_thrift_types.h" + +namespace facebook::fboss { +using namespace neteng::fboss::bgp::thrift; +using facebook::fboss::utils::Table; + +struct CmdShowBgpStreamSummaryTraits : public ReadCommandTraits { + using ParentCmd = void; + static constexpr utils::ObjectArgTypeId ObjectArgTypeId = + utils::ObjectArgTypeId::OBJECT_ARG_TYPE_ID_NONE; + using ObjectArgType = std::monostate; + using RetType = std::vector; +}; + +class CmdShowBgpStreamSummary : public CmdHandler< + CmdShowBgpStreamSummary, + CmdShowBgpStreamSummaryTraits> { + public: + using RetType = CmdShowBgpStreamSummaryTraits::RetType; + using ObjectArgType = CmdShowBgpStreamSummary::ObjectArgType; + + RetType queryClient(const HostInfo& hostInfo) { + auto client = utils::createClient>(hostInfo); + std::vector sessions; + client->sync_getBgpStreamSessions(sessions); + + return sessions; + } + + void printOutput(RetType& sessions, std::ostream& out = std::cout) { + if (sessions.empty()) { + return; + } + + std::sort( + sessions.begin(), + sessions.end(), + [](const TBgpStreamSession& a, const TBgpStreamSession& b) -> bool { + return folly::copy(a.peer_id().value()) < + folly::copy(b.peer_id().value()); + }); + + out << "BGP stream summary information for subscribers\n" << std::endl; + Table table; + table.setHeader({ + "Peer ID", + "Name", + "NumRoutes", + "Uptime", + }); + + for (const auto& session : sessions) { + table.addRow( + {folly::to(folly::copy(session.peer_id().value())), + session.subscriber_name().value(), + folly::to(*session.sent_prefix_count()), + utils::getPrettyElapsedTime( + utils::getEpochFromDuration( + folly::copy(session.uptime().value())))}); + } + out << table << std::endl; + } +}; + +} // namespace facebook::fboss diff --git a/fboss/cli/fboss2/commands/show/bgp/stream/subscriber/CmdShowBgpStreamSubscriberPostPolicy.h b/fboss/cli/fboss2/commands/show/bgp/stream/subscriber/CmdShowBgpStreamSubscriberPostPolicy.h new file mode 100644 index 0000000000000..8562005fb6a59 --- /dev/null +++ b/fboss/cli/fboss2/commands/show/bgp/stream/subscriber/CmdShowBgpStreamSubscriberPostPolicy.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 "fboss/cli/fboss2/CmdHandler.h" +#include "fboss/cli/fboss2/commands/show/bgp/CmdShowUtils.h" +#include "fboss/cli/fboss2/commands/show/bgp/stream/CmdShowBgpStreamSubscriber.h" +#include "fboss/cli/fboss2/utils/CmdClientUtilsCommon.h" +#include "fboss/cli/fboss2/utils/CmdUtilsCommon.h" +#include "fboss/cli/fboss2/utils/HostInfo.h" +#include "fboss/cli/fboss2/utils/Table.h" +#include "neteng/fboss/bgp/if/gen-cpp2/TBgpService.h" +#include "neteng/fboss/bgp/if/gen-cpp2/bgp_thrift_types.h" + +namespace facebook::fboss { +using namespace neteng::fboss::bgp::thrift; +using facebook::fboss::utils::Table; + +struct CmdShowBgpStreamSubscriberPostPolicyTraits : public ReadCommandTraits { + using ParentCmd = CmdShowBgpStreamSubscriber; + static constexpr utils::ObjectArgTypeId ObjectArgTypeId = + utils::ObjectArgTypeId::OBJECT_ARG_TYPE_ID_IP_LIST; + using ObjectArgType = std::vector; + using RetType = NetworkPathWithHost; +}; + +class CmdShowBgpStreamSubscriberPostPolicy + : public CmdHandler< + CmdShowBgpStreamSubscriberPostPolicy, + CmdShowBgpStreamSubscriberPostPolicyTraits> { + public: + using RetType = CmdShowBgpStreamSubscriberPostPolicyTraits::RetType; + using ObjectArgType = CmdShowBgpStreamSubscriberPostPolicy::ObjectArgType; + + RetType queryClient( + const HostInfo& hostInfo, + const std::vector& peerIds, + const std::vector& prefixes) { + if (peerIds.empty()) { + throw std::invalid_argument( + "No subscriber ID provided. Hint: use `fboss2 show bgp stream summary` to see the subscribers.\n" + "Usage: `fboss2 show bgp stream subscriber post-policy`\n" + "Example: `fboss2 show bgp stream subscriber 1 post-policy`"); + } + + std::map> routes; + auto client = utils::createClient>(hostInfo); + client->sync_getSubscriberNetworkInfo( + routes, folly::to(peerIds[0]), "post-policy"); + + if (!prefixes.empty()) { + routes = filterNetworks(routes, prefixes); + } + + NetworkPathWithHost result; + result.networkPath() = std::move(routes); + result.host() = hostInfo.getName(); + result.oobName() = hostInfo.getOobName(); + result.ip() = hostInfo.getIpStr(); + + return result; + } + + void printOutput(RetType& routesWithHost, std::ostream& out = std::cout) { + const HostInfo hostInfo( + *routesWithHost.host(), + *routesWithHost.oobName(), + folly::IPAddress(*routesWithHost.ip())); + std::vector output = printRoutesInformation( + *routesWithHost.networkPath(), hostInfo, /*showPolicy=*/true); + out << folly::join('\n', output) << std::endl; + } +}; + +} // namespace facebook::fboss diff --git a/fboss/cli/fboss2/commands/show/bgp/stream/subscriber/CmdShowBgpStreamSubscriberPrePolicy.h b/fboss/cli/fboss2/commands/show/bgp/stream/subscriber/CmdShowBgpStreamSubscriberPrePolicy.h new file mode 100644 index 0000000000000..75d88d0b8aad7 --- /dev/null +++ b/fboss/cli/fboss2/commands/show/bgp/stream/subscriber/CmdShowBgpStreamSubscriberPrePolicy.h @@ -0,0 +1,83 @@ +/* + * 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 "fboss/cli/fboss2/CmdHandler.h" +#include "fboss/cli/fboss2/commands/show/bgp/CmdShowUtils.h" +#include "fboss/cli/fboss2/commands/show/bgp/stream/CmdShowBgpStreamSubscriber.h" +#include "fboss/cli/fboss2/utils/CmdClientUtilsCommon.h" +#include "fboss/cli/fboss2/utils/CmdUtilsCommon.h" +#include "fboss/cli/fboss2/utils/HostInfo.h" +#include "fboss/cli/fboss2/utils/Table.h" +#include "neteng/fboss/bgp/if/gen-cpp2/TBgpService.h" +#include "neteng/fboss/bgp/if/gen-cpp2/bgp_thrift_types.h" + +namespace facebook::fboss { +using namespace neteng::fboss::bgp::thrift; +using facebook::fboss::utils::Table; + +struct CmdShowBgpStreamSubscriberPrePolicyTraits : public ReadCommandTraits { + using ParentCmd = CmdShowBgpStreamSubscriber; + static constexpr utils::ObjectArgTypeId ObjectArgTypeId = + utils::ObjectArgTypeId::OBJECT_ARG_TYPE_ID_IP_LIST; + using ObjectArgType = std::vector; + using RetType = NetworkPathWithHost; +}; + +class CmdShowBgpStreamSubscriberPrePolicy + : public CmdHandler< + CmdShowBgpStreamSubscriberPrePolicy, + CmdShowBgpStreamSubscriberPrePolicyTraits> { + public: + using RetType = CmdShowBgpStreamSubscriberPrePolicyTraits::RetType; + using ObjectArgType = CmdShowBgpStreamSubscriberPrePolicy::ObjectArgType; + + RetType queryClient( + const HostInfo& hostInfo, + const std::vector& peerIds, + const std::vector& prefixes) { + if (peerIds.empty()) { + throw std::invalid_argument( + "No subscriber ID provided. Hint: use `fboss2 show bgp stream summary` to see the subscribers.\n" + "Usage: `fboss2 show bgp stream subscriber pre-policy`\n" + "Example: fboss2 show bgp stream subscriber 1 pre-policy"); + } + + std::map> routes; + auto client = utils::createClient>(hostInfo); + client->sync_getSubscriberNetworkInfo( + routes, folly::to(peerIds[0]), "pre-policy"); + + if (!prefixes.empty()) { + routes = filterNetworks(routes, prefixes); + } + NetworkPathWithHost result; + result.networkPath() = std::move(routes); + result.host() = hostInfo.getName(); + result.oobName() = hostInfo.getOobName(); + result.ip() = hostInfo.getIpStr(); + + return result; + } + + void printOutput(RetType& routesWithHost, std::ostream& out = std::cout) { + const HostInfo hostInfo( + *routesWithHost.host(), + *routesWithHost.oobName(), + folly::IPAddress(*routesWithHost.ip())); + std::vector output = printRoutesInformation( + *routesWithHost.networkPath(), hostInfo, /*showPolicy=*/false); + out << folly::join('\n', output) << std::endl; + } +}; + +} // namespace facebook::fboss diff --git a/fboss/cli/fboss2/commands/show/bgp/summary/BUCK b/fboss/cli/fboss2/commands/show/bgp/summary/BUCK new file mode 100644 index 0000000000000..3a9569cee3b82 --- /dev/null +++ b/fboss/cli/fboss2/commands/show/bgp/summary/BUCK @@ -0,0 +1,15 @@ +load("@fbcode_macros//build_defs:thrift_library.bzl", "thrift_library") + +oncall("fboss_agent_push") + +thrift_library( + name = "bgp_summary", + languages = [ + "cpp2", + ], + thrift_cpp2_options = "json", + thrift_srcs = {"bgp_summary.thrift": []}, + deps = [ + "//neteng/fboss/bgp/if:bgp_thrift", + ], +) diff --git a/fboss/cli/fboss2/commands/show/bgp/summary/CmdShowBgpSummary.h b/fboss/cli/fboss2/commands/show/bgp/summary/CmdShowBgpSummary.h new file mode 100644 index 0000000000000..d8c2afc815b4a --- /dev/null +++ b/fboss/cli/fboss2/commands/show/bgp/summary/CmdShowBgpSummary.h @@ -0,0 +1,220 @@ +/* + * Copyright (c) 2004-present, Meta Platforms, 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 // NOLINT(misc-include-cleaner) + +#include +#include // NOLINT(misc-include-cleaner) +#include +#include + +#include "fboss/cli/fboss2/CmdHandler.h" +#include "fboss/cli/fboss2/commands/show/bgp/summary/gen-cpp2/bgp_summary_types.h" +#include "fboss/cli/fboss2/utils/CmdClientUtilsCommon.h" +#include "fboss/cli/fboss2/utils/Table.h" +#include "neteng/fboss/bgp/if/gen-cpp2/TBgpService.h" +#include "neteng/fboss/bgp/if/gen-cpp2/bgp_thrift_types.h" +#include "thrift/lib/cpp/util/EnumUtils.h" + +namespace facebook::fboss { +using apache::thrift::util::enumNameSafe; +using facebook::fboss::utils::Table; +using facebook::neteng::fboss::bgp::thrift::TBgpLocalConfig; +using facebook::neteng::fboss::bgp::thrift::TBgpSession; +using neteng::fboss::bgp::thrift::TBgpDrainState; +using neteng::fboss::bgp::thrift::TBgpPeerState; +using std::chrono::duration_cast; +using std::chrono::system_clock; + +struct CmdShowBgpSummaryTraits : public ReadCommandTraits { + static constexpr utils::ObjectArgTypeId ObjectArgTypeId = + utils::ObjectArgTypeId::OBJECT_ARG_TYPE_ID_NONE; + using ObjectArgType = std::monostate; + using RetType = cli::ShowBgpSummaryModel; +}; + +class CmdShowBgpSummary + : public CmdHandler { + public: + using RetType = CmdShowBgpSummaryTraits::RetType; + + RetType queryClient(const HostInfo& hostInfo) { + std::vector sessions; + TBgpLocalConfig config; + TBgpDrainState drain_state; + + auto client = utils::createClient>(hostInfo); + + client->sync_getBgpSessions(sessions); + client->sync_getBgpLocalConfig(config); + client->sync_getDrainState(drain_state); + + return createModel(sessions, config, drain_state); + } + + void printOutput(const RetType& bgpSummary, std::ostream& out = std::cout) { + const auto& config = bgpSummary.config().value(); + const auto& sessions = bgpSummary.sessions().value(); + const auto& drain_state = bgpSummary.drain_state().value(); + + // prevent printing number with comma, e.g: 65,000 + out.imbue(std::locale("C")); + + out << "BGP summary information for VRF default" << std::endl; + // Retrive the router's Ip Address from its ID. + char ip_buff[INET_ADDRSTRLEN]; + auto id = folly::copy(config.my_router_id().value()); + inet_ntop( + AF_INET, &((struct in_addr*)&id)->s_addr, ip_buff, INET_ADDRSTRLEN); + + // TODO(michaeluw): T113736668 deprecate i32 asns field + const uint32_t local_as = folly::copy(config.local_as_4_byte().value()) + ? static_cast(folly::copy(config.local_as_4_byte().value())) + : folly::copy(config.local_as().value()); + + out << "Router ID - " << ip_buff << ", Local ASN - " << local_as; + + // TODO(michaeluw): T113736668 deprecate i32 asns field + if (const auto& confed_asn = + folly::copy(config.local_confed_as_4_byte().value())) { + out << ", Confed ASN - " << confed_asn << std::endl; + } else if ( + const auto& confed_asn_old = + apache::thrift::get_pointer(config.local_confed_as())) { + out << ", Confed ASN - " << *confed_asn_old << std::endl; + } else { + out << std::endl; + } + // Add BGP drain status + const auto& drain_state_value = drain_state.drain_state().value(); + out << fmt::format( + "BGP Switch Drain State: {}", enumNameSafe(drain_state_value)) + << std::endl; + + // Add UCMP config header + out << "UCMP weight programming is "; + if (config.program_ucmp_weights().value()) { + out << "ENABLED, with normalization width of " + << folly::copy(config.ucmp_width().value()) << std::endl; + } else { + out << "DISABLED" << std::endl; + } + + // Show if update-group is enabled + out << "Update group is "; + if (config.enable_update_group().value()) { + out << "ENABLED" << std::endl; + } else { + out << "DISABLED" << std::endl; + } + + // Add Summary + const uint64_t total_neighbors = sessions.size(); + uint64_t up_neighbors = 0, paths_rcvd = 0, paths_sent = 0; + + Table table; + table.setHeader( + {"Peer", + "AS", + "State", + "GR", + "PR", + "PS", + "Uptime", + "Downtime", + "Description", + "Session ID", + "Flaps"}); + for (const auto& bgpSession : sessions) { + const auto& peer = bgpSession.peer().value(); + up_neighbors += + (folly::copy(peer.peer_state().value()) == + TBgpPeerState::ESTABLISHED); + + auto rcvd_prefix = *bgpSession.prepolicy_rcvd_prefix_count(); + auto sent_prefix = *bgpSession.postpolicy_sent_prefix_count(); + + paths_rcvd += rcvd_prefix; + paths_sent += sent_prefix; + + const uint32_t peer_remote_as = + folly::copy(peer.remote_as_4_byte().value()) + ? folly::copy(peer.remote_as_4_byte().value()) + : folly::copy(peer.remote_as().value()); + + std::string uptimeDurationString; + + if (*peer.peer_state() == TBgpPeerState::ESTABLISHED) { + uptimeDurationString = utils::getPrettyElapsedTime( + utils::getEpochFromDuration(*bgpSession.uptime())); + } + + std::string downtimeDurationString; + if (((*peer.peer_state() == TBgpPeerState::IDLE || + *peer.peer_state() == TBgpPeerState::IDLE_ADMIN)) && + (*bgpSession.num_resets() > 0)) { + // If the peer is IDLE but the num_resets() == 0, then the peer has + // never been in ESTABLISHED state and hence does not have the + // downtime yet. + downtimeDurationString = utils::getPrettyElapsedTime( + utils::getEpochFromDuration(*bgpSession.reset_time())); + } + + table.addRow({ + bgpSession.peer_addr().value(), + folly::to(peer_remote_as), + enumNameSafe(folly::copy(peer.peer_state().value())).substr(0, 4), + folly::copy(peer.graceful().value()) ? "Y" : "N", + folly::to(rcvd_prefix), + folly::to(sent_prefix), + uptimeDurationString, + downtimeDurationString, + bgpSession.description().value(), + bgpSession.peer_bgp_id().value(), + folly::to(folly::copy(bgpSession.num_resets().value())), + }); + } + + out << "Peers: UP - " << up_neighbors << ", TOTAL - " << total_neighbors + << std::endl; + out << "Paths: Received - " << paths_rcvd << ", Sent - " << paths_sent + << std::endl; + out << "Acronyms: PS - Prefixes Sent, PR - Prefixes Received, HT - Hold Time\n" + << std::endl; + out << table << std::endl; + } + + // Move the data from bgp_thrift:TBgpSession to + // bgp_summary_thrift:TBgpSummary + RetType createModel( + std::vector& sessions, + TBgpLocalConfig& config, + TBgpDrainState& drain_state) { + std::sort( + sessions.begin(), + sessions.end(), + [](const TBgpSession& a, const TBgpSession& b) { + return a.peer_addr().value() < b.peer_addr().value(); + }); + + RetType model; + model.sessions() = sessions; + model.config() = config; + model.drain_state() = drain_state; + return model; + } +}; + +} // namespace facebook::fboss diff --git a/fboss/cli/fboss2/commands/show/bgp/summary/bgp_summary.thrift b/fboss/cli/fboss2/commands/show/bgp/summary/bgp_summary.thrift new file mode 100644 index 0000000000000..55d8135db766d --- /dev/null +++ b/fboss/cli/fboss2/commands/show/bgp/summary/bgp_summary.thrift @@ -0,0 +1,12 @@ +include "neteng/fboss/bgp/if/bgp_thrift.thrift" + +package "facebook.com/fboss/cli" + +namespace cpp2 facebook.fboss.cli + +struct ShowBgpSummaryModel { + 1: list sessions; + 2: bgp_thrift.TBgpLocalConfig config; + 3: bgp_thrift.TBgpDrainState drain_state; + 4: list peer_egress_stats; +} diff --git a/fboss/cli/fboss2/commands/show/bgp/summary/egress/CmdShowBgpSummaryEgress.cpp b/fboss/cli/fboss2/commands/show/bgp/summary/egress/CmdShowBgpSummaryEgress.cpp new file mode 100644 index 0000000000000..dbb4ff6bd489b --- /dev/null +++ b/fboss/cli/fboss2/commands/show/bgp/summary/egress/CmdShowBgpSummaryEgress.cpp @@ -0,0 +1,346 @@ +/* + * Copyright (c) 2004-present, Meta Platforms, 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/show/bgp/summary/egress/CmdShowBgpSummaryEgress.h" + +#include +#include // NOLINT(misc-include-cleaner) +#include +#include +#include +#include + +#include "fboss/cli/fboss2/utils/CmdClientUtilsCommon.h" +#include "fboss/cli/fboss2/utils/Table.h" +#include "neteng/fboss/bgp/if/gen-cpp2/TBgpService.h" +#include "thrift/lib/cpp/util/EnumUtils.h" // NOLINT(misc-include-cleaner) + +namespace facebook::fboss { + +CmdShowBgpSummaryEgress::RetType CmdShowBgpSummaryEgress::queryClient( + const HostInfo& hostInfo) { + std::vector peerEgressStats; + + auto client = utils::createClient>(hostInfo); + + client->sync_getPeerEgressStats(peerEgressStats); + + return createModel(peerEgressStats); +} + +void CmdShowBgpSummaryEgress::printOutput( + const RetType& model, + std::ostream& out) { + const auto& peerEgressStats = model.peer_egress_stats().value(); + + out.imbue(std::locale("C")); + + printGroupSummary(peerEgressStats, out); + out << std::endl; + out << std::endl; + printPeerSummary(peerEgressStats, out); +} + +Table CmdShowBgpSummaryEgress::makePeerTable( + const std::vector& peerEgressStats) { + Table table; + table.setHeader({ + "Peer", + "Description", + "Group Name", + "State", + "Uptime", + "Downtime", + "PR", + "PS", + "UR", + "US", + "SU", + "IQ Blocks", + "Total IQ Wait (ms)", + "Last IQ Block", + "SQ Blocks", + "Total SQ Wait (ms)", + "Last SQ Block", + "Socket Buffered", + "Last Socket Buffered", + }); + + for (const auto& stats : peerEgressStats) { + if (!stats.session().has_value()) { + continue; + } + + const auto& session = stats.session().value(); + const auto& peer = session.peer().value(); + + std::string uptimeDurationString; + if (*peer.peer_state() == TBgpPeerState::ESTABLISHED) { + uptimeDurationString = utils::getPrettyElapsedTime( + utils::getEpochFromDuration(*session.uptime())); + } + + std::string downtimeDurationString; + if (((*peer.peer_state() == TBgpPeerState::IDLE || + *peer.peer_state() == TBgpPeerState::IDLE_ADMIN)) && + (*session.num_resets() > 0)) { + downtimeDurationString = utils::getPrettyElapsedTime( + utils::getEpochFromDuration(*session.reset_time())); + } + + /* PR, PS, UR, US, SU */ + auto rcvd_prefix = *session.prepolicy_rcvd_prefix_count(); + auto sent_prefix = *session.postpolicy_sent_prefix_count(); + auto recv_update_msgs = *session.recv_update_msgs(); + auto sent_update_msgs = *session.sent_update_msgs(); + auto suppressed_updates = + stats.transient_route_updates_suppressed().value_or(0); + + /* iqueue (boundedAdjRibOutQueue_) related stats */ + auto iq_blocks = stats.adjribout_queue_blocks().value_or(0); + auto iq_wait = stats.adjribout_queue_total_block_duration().value_or(0); + std::string last_iq_block; + if (stats.last_adjribout_queue_block_time() && + *stats.last_adjribout_queue_block_time() > 0) { + last_iq_block = + utils::parseTimeToTimeStamp(*stats.last_adjribout_queue_block_time()); + } + + /* sendQueue_ related stats */ + auto sq_blocks = stats.send_queue_blocks().value_or(0); + auto sq_wait = stats.send_queue_total_block_duration().value_or(0); + std::string last_sq_block; + if (stats.last_send_queue_block_time() && + *stats.last_send_queue_block_time() > 0) { + last_sq_block = + utils::parseTimeToTimeStamp(*stats.last_send_queue_block_time()); + } + + /* async socket stats */ + auto sb = stats.total_async_socket_buffered().value_or(0); + std::string last_sb_time; + if (stats.last_socket_buffered_time() && + *stats.last_socket_buffered_time() > 0) { + last_sb_time = + utils::parseTimeToTimeStamp(*stats.last_socket_buffered_time()); + } + + table.addRow({ + session.peer_addr().value(), /* Peer */ + session.description().value(), /* Description */ + stats.group_name().value_or(kNoGroupName), /* Group Name */ + enumNameSafe(folly::copy(peer.peer_state().value())) + .substr(0, 4), /* State */ + uptimeDurationString, /* Uptime */ + downtimeDurationString, /* Downtime */ + folly::to(rcvd_prefix), /* PR */ + folly::to(sent_prefix), /* PS */ + folly::to(recv_update_msgs), /* UR */ + folly::to(sent_update_msgs), /* US */ + folly::to(suppressed_updates), /* SU */ + folly::to(iq_blocks), /* IQ Blocks */ + folly::to(iq_wait), /* Total IQ Wait */ + last_iq_block, /* Last IQ Block */ + folly::to(sq_blocks), /* SQ Blocks */ + folly::to(sq_wait), /* Total SQ Wait */ + last_sq_block, /* Last SQ Block */ + folly::to(sb), /* Socket Buffered */ + last_sb_time, /* Last Socket Buffered */ + }); + } + return table; +} + +void CmdShowBgpSummaryEgress::printPeerSummary( + const std::vector& peerEgressStats, + std::ostream& out) { + out << "BGP Peer Egress Summary" << std::endl; + out << "Acronyms: PR - Prefixes Received, PS - Prefixes Sent, " + << "UR - Update Messages Received, US - Update Messages Sent, \n" + << "SU - Suppressed Updates (updates not sent due to packing state compression), " + << "IQ - Per-peer Input Queue to BGP I/O thread, " + << "SQ - Per-peer Send Queue to socket \n" + << std::endl; + + out << makePeerTable(peerEgressStats) << std::endl; +} + +Table CmdShowBgpSummaryEgress::makeGroupTable( + const std::vector& peerEgressStats) { + Table groupSummaryView; + groupSummaryView.setHeader( + {"Percentile", + "Group Name", + "PR", + "PS", + "UR", + "US", + "SU", + "IQ Blocks", + "Total IQ Wait (ms)", + "Last IQ Block", + "SQ Blocks", + "Total SQ Wait (ms)", + "Last SQ Block", + "Socket Buffered", + "Last Socket Buffered"}); + + std::map>> groupedValues; + + /* Group all non-string data by @group_name. */ + for (const auto& stats : peerEgressStats) { + if (!stats.session().has_value()) { + continue; + } + + const auto& session = *stats.session(); + + /* Skip IDLE and IDLE_ADMIN peers from percentile calculations. */ + const auto& peerState = session.peer()->peer_state(); + if (*peerState == TBgpPeerState::IDLE || + *peerState == TBgpPeerState::IDLE_ADMIN) { + continue; + } + + std::string group = stats.group_name().value_or(kNoGroupName); + + /* Get group entry if it exists; otherwise create new one in map. */ + if (groupedValues.find(group) == groupedValues.end()) { + groupedValues[group] = + std::vector>(PeerMetric::MAX_VALUE); + } + + auto& metricVectors = groupedValues[group]; + + /* PR, PS, UR, US, SU */ + metricVectors[PeerMetric::PrefixesRcvd].push_back( + *session.prepolicy_rcvd_prefix_count()); + metricVectors[PeerMetric::PrefixesSent].push_back( + *session.postpolicy_sent_prefix_count()); + metricVectors[PeerMetric::UpdatesRcvd].push_back( + *session.recv_update_msgs()); + metricVectors[PeerMetric::UpdatesSent].push_back( + *session.sent_update_msgs()); + metricVectors[PeerMetric::SuppressedUpdates].push_back( + stats.transient_route_updates_suppressed().value_or(0)); + + /* iqueue metrics */ + metricVectors[PeerMetric::TotalAdjRibOutQueueBlocks].push_back( + stats.adjribout_queue_blocks().value_or(0)); + metricVectors[PeerMetric::TotalAdjRibOutQueueWait].push_back( + stats.adjribout_queue_total_block_duration().value_or(0)); + metricVectors[PeerMetric::LastAdjRibOutQueueBlock].push_back( + stats.last_adjribout_queue_block_time().value_or(0)); + + /* sendQueue metrics */ + metricVectors[PeerMetric::TotalSendQueueBlocks].push_back( + stats.send_queue_blocks().value_or(0)); + metricVectors[PeerMetric::TotalSendQueueWait].push_back( + stats.send_queue_total_block_duration().value_or(0)); + metricVectors[PeerMetric::LastSendQueueBlock].push_back( + stats.last_send_queue_block_time().value_or(0)); + + /* async socket metrics */ + metricVectors[PeerMetric::TotalSocketBuffered].push_back( + stats.total_async_socket_buffered().value_or(0)); + metricVectors[PeerMetric::LastSocketBuffered].push_back( + stats.last_socket_buffered_time().value_or(0)); + } + + /* Sort data for percentile computation. */ + for (auto& [group, metricVectors] : groupedValues) { + for (int m = 0; m < metricVectors.size(); ++m) { + auto& vec = metricVectors[m]; + std::sort(vec.begin(), vec.end()); + } + } + + std::vector percentiles = {"p50", "p95", "p99"}; + std::vector percentileValues = {0.50, 0.95, 0.99}; + + for (const auto& [group, metricVectors] : groupedValues) { + for (size_t p = 0; p < percentiles.size(); ++p) { + std::vector row; + row.push_back(percentiles[p]); + row.push_back(group); + + for (size_t i = 0; i < metricVectors.size(); ++i) { + const auto& vec = metricVectors[i]; + if (vec.empty()) { + row.emplace_back("0"); + } else { + size_t index = + static_cast((vec.size() - 1) * percentileValues[p]); + auto& val = vec[index]; + if (kTimeMetrics.contains(static_cast(i))) { + row.push_back( + folly::to( + val > 0 ? utils::parseTimeToTimeStamp(val) : "")); + } else { + row.push_back(folly::to(val)); + } + } + } + + groupSummaryView.addRow({ + row[0], // Percentile + row[1], // Group Name + row[2], // PR + row[3], // PS + row[4], // UR + row[5], // US + row[6], // SU + row[7], // IQ Blocks + row[8], // Total IQ Wait + row[9], // Last IQ Block + row[10], // SQ Blocks + row[11], // Total SQ Wait + row[12], // Last SQ Block + row[13], // Total Socket Buffered + row[14], // Last Socket Buffered + }); + } + } + return groupSummaryView; +} + +void CmdShowBgpSummaryEgress::printGroupSummary( + const std::vector& peerEgressStats, + std::ostream& out) { + out << "BGP Peer Egress Summary by Group Percentiles" << std::endl; + out << "Acronyms: PR - Prefixes Received, PS - Prefixes Sent, " + << "UR - Update Messages Received, US - Update Messages Sent, \n" + << "SU - Suppressed Updates (updates not sent due to packing state compression), " + << "IQ - Per-peer Input Queue to BGP I/O thread, " + << "SQ - Per-peer Send Queue to socket \n" + << std::endl; + out << makeGroupTable(peerEgressStats) << std::endl; +} + +CmdShowBgpSummaryEgress::RetType CmdShowBgpSummaryEgress::createModel( + std::vector& peerEgressStats) { + std::sort( + peerEgressStats.begin(), + peerEgressStats.end(), + [](const TPeerEgressStats& a, const TPeerEgressStats& b) { + if (!a.session().has_value() || !b.session().has_value()) { + return false; + } + return a.session()->peer_addr().value() < + b.session()->peer_addr().value(); + }); + + RetType model; + model.peer_egress_stats() = peerEgressStats; + return model; +} + +} // namespace facebook::fboss diff --git a/fboss/cli/fboss2/commands/show/bgp/summary/egress/CmdShowBgpSummaryEgress.h b/fboss/cli/fboss2/commands/show/bgp/summary/egress/CmdShowBgpSummaryEgress.h new file mode 100644 index 0000000000000..17bdc5ce6d347 --- /dev/null +++ b/fboss/cli/fboss2/commands/show/bgp/summary/egress/CmdShowBgpSummaryEgress.h @@ -0,0 +1,79 @@ +/* + * Copyright (c) 2004-present, Meta Platforms, 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 "fboss/cli/fboss2/CmdHandler.h" +#include "fboss/cli/fboss2/commands/show/bgp/summary/gen-cpp2/bgp_summary_types.h" +#include "fboss/cli/fboss2/utils/Table.h" +#include "neteng/fboss/bgp/if/gen-cpp2/bgp_thrift_types.h" + +namespace facebook::fboss { +using apache::thrift::util::enumNameSafe; +using facebook::fboss::utils::Table; +using facebook::neteng::fboss::bgp::thrift::TBgpPeerState; +using facebook::neteng::fboss::bgp::thrift::TBgpSession; +using facebook::neteng::fboss::bgp::thrift::TPeerEgressStats; + +enum PeerMetric { + PrefixesRcvd = 0, + PrefixesSent = 1, + UpdatesRcvd = 2, + UpdatesSent = 3, + SuppressedUpdates = 4, + TotalAdjRibOutQueueBlocks = 5, + TotalAdjRibOutQueueWait = 6, + LastAdjRibOutQueueBlock = 7, + TotalSendQueueBlocks = 8, + TotalSendQueueWait = 9, + LastSendQueueBlock = 10, + TotalSocketBuffered = 11, + LastSocketBuffered = 12, + MAX_VALUE, +}; + +inline const std::string kNoGroupName = "NONE"; + +inline const std::unordered_set kTimeMetrics = { + LastAdjRibOutQueueBlock, + LastSendQueueBlock, + LastSocketBuffered, +}; + +struct CmdShowBgpSummaryEgressTraits : public ReadCommandTraits { + static constexpr utils::ObjectArgTypeId ObjectArgTypeId = + utils::ObjectArgTypeId::OBJECT_ARG_TYPE_ID_NONE; + using ObjectArgType = std::monostate; + using RetType = cli::ShowBgpSummaryModel; +}; + +class CmdShowBgpSummaryEgress : public CmdHandler< + CmdShowBgpSummaryEgress, + CmdShowBgpSummaryEgressTraits> { + public: + using RetType = CmdShowBgpSummaryEgressTraits::RetType; + + RetType queryClient(const HostInfo& hostInfo); + void printOutput(const RetType& model, std::ostream& out = std::cout); + Table makePeerTable(const std::vector& peerEgressStats); + void printPeerSummary( + const std::vector& peerEgressStats, + std::ostream& out); + Table makeGroupTable(const std::vector& peerEgressStats); + void printGroupSummary( + const std::vector& peerEgressStats, + std::ostream& out); + RetType createModel(std::vector& peerEgressStats); +}; + +} // namespace facebook::fboss diff --git a/fboss/cli/fboss2/commands/show/bgp/table/CmdShowBgpTable.cpp b/fboss/cli/fboss2/commands/show/bgp/table/CmdShowBgpTable.cpp new file mode 100644 index 0000000000000..1ffbcd1d005e3 --- /dev/null +++ b/fboss/cli/fboss2/commands/show/bgp/table/CmdShowBgpTable.cpp @@ -0,0 +1,43 @@ +/* + * 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/show/bgp/table/CmdShowBgpTable.h" + +#include "fboss/cli/fboss2/commands/show/bgp/CmdShowUtils.h" +#include "fboss/cli/fboss2/utils/CmdClientUtilsCommon.h" +#include "neteng/fboss/bgp/if/gen-cpp2/TBgpService.h" + +namespace facebook::fboss { + +CmdShowBgpTable::RetType CmdShowBgpTable::queryClient( + const HostInfo& hostInfo) { + std::vector entriesIPv4; + std::vector entriesIPv6; + auto client = utils::createClient>(hostInfo); + + client->sync_getRibEntries(entriesIPv4, TBgpAfi::AFI_IPV4); + client->sync_getRibEntries(entriesIPv6, TBgpAfi::AFI_IPV6); + + // Append both entries into a single vector + entriesIPv4.insert(entriesIPv4.end(), entriesIPv6.begin(), entriesIPv6.end()); + TRibEntryWithHost result; + result.tRibEntries() = std::move(entriesIPv4); + result.host() = hostInfo.getName(); + result.oobName() = hostInfo.getOobName(); + result.ip() = hostInfo.getIpStr(); + return result; +} + +void CmdShowBgpTable::printOutput(RetType& entries, std::ostream& out) { + printRIBEntries(out, entries); +} + +} // namespace facebook::fboss diff --git a/fboss/cli/fboss2/commands/show/bgp/table/CmdShowBgpTable.h b/fboss/cli/fboss2/commands/show/bgp/table/CmdShowBgpTable.h new file mode 100644 index 0000000000000..92a0628a5531c --- /dev/null +++ b/fboss/cli/fboss2/commands/show/bgp/table/CmdShowBgpTable.h @@ -0,0 +1,40 @@ +/* + * 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 "fboss/cli/fboss2/CmdHandler.h" +#include "fboss/cli/fboss2/commands/show/bgp/CmdShowUtils.h" +#include "fboss/cli/fboss2/utils/CmdUtilsCommon.h" +#include "neteng/fboss/bgp/if/gen-cpp2/bgp_thrift_types.h" + +namespace facebook::fboss { +using namespace neteng::fboss::bgp::thrift; +using namespace neteng::fboss::bgp_attr; + +struct CmdShowBgpTableTraits : public ReadCommandTraits { + using ParentCmd = void; + static constexpr utils::ObjectArgTypeId ObjectArgTypeId = + utils::ObjectArgTypeId::OBJECT_ARG_TYPE_ID_NONE; + using ObjectArgType = std::monostate; + using RetType = TRibEntryWithHost; + + std::vector LocalOptions = { + {kGar, "Show decoded GAR link bandwidth ext community"}}; +}; + +class CmdShowBgpTable + : public CmdHandler { + public: + using RetType = CmdShowBgpTableTraits::RetType; + RetType queryClient(const HostInfo& hostInfo); + void printOutput(RetType& entries, std::ostream& out = std::cout); +}; +} // namespace facebook::fboss diff --git a/fboss/cli/fboss2/commands/show/bgp/table/CmdShowBgpTableCommunity.h b/fboss/cli/fboss2/commands/show/bgp/table/CmdShowBgpTableCommunity.h new file mode 100644 index 0000000000000..fdbcda790859f --- /dev/null +++ b/fboss/cli/fboss2/commands/show/bgp/table/CmdShowBgpTableCommunity.h @@ -0,0 +1,115 @@ +/* + * 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 "fboss/cli/fboss2/CmdHandler.h" +#include "fboss/cli/fboss2/commands/show/bgp/CmdShowUtils.h" +#include "fboss/cli/fboss2/commands/show/bgp/table/CmdShowBgpTable.h" +#include "fboss/cli/fboss2/utils/CmdClientUtilsCommon.h" +#include "fboss/cli/fboss2/utils/CmdUtilsCommon.h" +#include "fboss/cli/fboss2/utils/HostInfo.h" +#include "neteng/fboss/bgp/if/gen-cpp2/TBgpService.h" +#include "neteng/fboss/bgp/if/gen-cpp2/bgp_thrift_types.h" + +namespace facebook::fboss { +using namespace neteng::fboss::bgp::thrift; + +struct CmdShowBgpTableCommunityTraits : public ReadCommandTraits { + using ParentCmd = CmdShowBgpTable; + static constexpr utils::ObjectArgTypeId ObjectArgTypeId = + utils::ObjectArgTypeId::OBJECT_ARG_TYPE_ID_COMMUNITY_LIST; + using ObjectArgType = std::vector; + using RetType = TRibEntryWithHost; +}; + +class CmdShowBgpTableCommunity : public CmdHandler< + CmdShowBgpTableCommunity, + CmdShowBgpTableCommunityTraits> { + public: + using ObjectArgType = CmdShowBgpTableCommunityTraits::RetType; + using RetType = CmdShowBgpTableCommunityTraits::RetType; + + RetType queryClient( + const HostInfo& hostInfo, + const std::vector& queriedCommunities) { + std::vector entries; + TRibEntryWithHost result; + auto client = utils::createClient>(hostInfo); + + if (queriedCommunities.empty()) { + std::cout + << "No community entered. Usage: fboss2 show bgp table community " + << std::endl; + return result; + } + + std::vector communityValues; + try { + communityValues = communityNameToValue(queriedCommunities[0], hostInfo); + } catch (std::invalid_argument& e) { + std::cout << e.what() << std::endl; + return {}; + } + + client->sync_getRibEntriesForCommunity( + entries, TBgpAfi::AFI_ALL, communityValues[0]); + + auto data = (communityValues.size() > 1) + ? filterEntriesByCommunities(entries, communityValues[1]) + : entries; + result.tRibEntries() = std::move(data); + result.host() = hostInfo.getName(); + result.oobName() = hostInfo.getOobName(); + result.ip() = hostInfo.getIpStr(); + return result; + } + + void printOutput(RetType& data, std::ostream& out = std::cout) { + if (!data.tRibEntries()->empty()) { + printRIBEntries(out, data, /*detail=*/true); + } + } + + std::vector filterEntriesByCommunities( + std::vector& entries, + const std::string& remainingCommunity) { + for (auto& entry : entries) { + auto& pathsMap = *entry.paths(); + for (auto it = pathsMap.begin(); it != pathsMap.end();) { + const auto& [name, paths] = *it; + std::vector filteredPaths; + for (const auto& path : paths) { + for (const auto& comm : + *apache::thrift::get_pointer(path.communities())) { + const std::string communityString = communityToString(comm); + if (remainingCommunity == communityString) { + filteredPaths.emplace_back(path); + } + } + } + if (filteredPaths.empty()) { + it = pathsMap.erase(it); // erase returns iterator to next element + } else { + ++it; + } + } + } + std::vector result; + for (const auto& entry : entries) { + if (!entry.paths().value().empty()) { + result.emplace_back(entry); + } + } + return result; + } +}; +} // namespace facebook::fboss diff --git a/fboss/cli/fboss2/commands/show/bgp/table/CmdShowBgpTableDetail.h b/fboss/cli/fboss2/commands/show/bgp/table/CmdShowBgpTableDetail.h new file mode 100644 index 0000000000000..bb5cfd18cf642 --- /dev/null +++ b/fboss/cli/fboss2/commands/show/bgp/table/CmdShowBgpTableDetail.h @@ -0,0 +1,61 @@ +/* + * 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 "fboss/cli/fboss2/CmdHandler.h" +#include "fboss/cli/fboss2/commands/show/bgp/CmdShowUtils.h" +#include "fboss/cli/fboss2/utils/CmdClientUtilsCommon.h" +#include "fboss/cli/fboss2/utils/CmdUtilsCommon.h" +#include "fboss/cli/fboss2/utils/HostInfo.h" +#include "neteng/fboss/bgp/if/gen-cpp2/TBgpService.h" +#include "neteng/fboss/bgp/if/gen-cpp2/bgp_thrift_types.h" + +namespace facebook::fboss { +using namespace neteng::fboss::bgp::thrift; +using namespace neteng::fboss::bgp_attr; + +struct CmdShowBgpTableDetailTraits : public ReadCommandTraits { + using ParentCmd = void; + static constexpr utils::ObjectArgTypeId ObjectArgTypeId = + utils::ObjectArgTypeId::OBJECT_ARG_TYPE_ID_NONE; + using ObjectArgType = std::monostate; + using RetType = TRibEntryWithHost; +}; + +class CmdShowBgpTableDetail + : public CmdHandler { + public: + using RetType = CmdShowBgpTableDetailTraits::RetType; + RetType queryClient(const HostInfo& hostInfo) { + std::vector entriesIPv4; + std::vector entriesIPv6; + auto client = utils::createClient>(hostInfo); + + client->sync_getRibEntries(entriesIPv4, TBgpAfi::AFI_IPV4); + client->sync_getRibEntries(entriesIPv6, TBgpAfi::AFI_IPV6); + + // Append both entries into a single vector + entriesIPv4.insert( + entriesIPv4.end(), entriesIPv6.begin(), entriesIPv6.end()); + TRibEntryWithHost result; + result.tRibEntries() = std::move(entriesIPv4); + result.host() = hostInfo.getName(); + result.oobName() = hostInfo.getOobName(); + result.ip() = hostInfo.getIpStr(); + return result; + } + + void printOutput(RetType& entries, std::ostream& out = std::cout) { + printRIBEntries(out, entries, /*detail=*/true); + } +}; +} // namespace facebook::fboss diff --git a/fboss/cli/fboss2/commands/show/bgp/table/CmdShowBgpTableMoreSpecifics.h b/fboss/cli/fboss2/commands/show/bgp/table/CmdShowBgpTableMoreSpecifics.h new file mode 100644 index 0000000000000..2e7bb79cf8cca --- /dev/null +++ b/fboss/cli/fboss2/commands/show/bgp/table/CmdShowBgpTableMoreSpecifics.h @@ -0,0 +1,72 @@ +/* + * 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 "fboss/cli/fboss2/CmdHandler.h" +#include "fboss/cli/fboss2/commands/show/bgp/CmdShowUtils.h" +#include "fboss/cli/fboss2/commands/show/bgp/table/CmdShowBgpTable.h" +#include "fboss/cli/fboss2/utils/CmdClientUtilsCommon.h" +#include "fboss/cli/fboss2/utils/CmdUtilsCommon.h" +#include "fboss/cli/fboss2/utils/HostInfo.h" +#include "neteng/fboss/bgp/if/gen-cpp2/TBgpService.h" +#include "neteng/fboss/bgp/if/gen-cpp2/bgp_thrift_types.h" + +namespace facebook::fboss { +using namespace neteng::fboss::bgp::thrift; + +struct CmdShowBgpTableMoreSpecificsTraits : public ReadCommandTraits { + using ParentCmd = CmdShowBgpTable; + static constexpr utils::ObjectArgTypeId ObjectArgTypeId = + utils::ObjectArgTypeId::OBJECT_ARG_TYPE_ID_IP_LIST; + using ObjectArgType = std::vector; + using RetType = TRibEntryWithHost; +}; + +class CmdShowBgpTableMoreSpecifics : public CmdHandler< + CmdShowBgpTableMoreSpecifics, + CmdShowBgpTableMoreSpecificsTraits> { + public: + using ObjectArgType = CmdShowBgpTableMoreSpecificsTraits::RetType; + using RetType = CmdShowBgpTableMoreSpecificsTraits::RetType; + + RetType queryClient( + const HostInfo& hostInfo, + const std::vector& prefixes) { + std::vector entries; + auto client = utils::createClient>(hostInfo); + TRibEntryWithHost result; + if (prefixes.empty()) { + std::cout + << "No prefixes entered. Usage: fboss2 show bgp table more-specifics " + << std::endl; + return result; + } + + std::vector newEntry; + for (const auto& prefix : prefixes) { + client->sync_getRibSubprefixes(newEntry, prefix); + entries.insert(entries.end(), newEntry.begin(), newEntry.end()); + } + result.tRibEntries() = std::move(entries); + result.host() = hostInfo.getName(); + result.oobName() = hostInfo.getOobName(); + result.ip() = hostInfo.getIpStr(); + return result; + } + + void printOutput(RetType& data, std::ostream& out = std::cout) { + if (!data.tRibEntries()->empty()) { + printRIBEntries(out, data, /*detail=*/true); + } + } +}; +} // namespace facebook::fboss diff --git a/fboss/cli/fboss2/commands/show/bgp/table/CmdShowBgpTablePrefix.h b/fboss/cli/fboss2/commands/show/bgp/table/CmdShowBgpTablePrefix.h new file mode 100644 index 0000000000000..8904bd0e76ab3 --- /dev/null +++ b/fboss/cli/fboss2/commands/show/bgp/table/CmdShowBgpTablePrefix.h @@ -0,0 +1,72 @@ +/* + * 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 "fboss/cli/fboss2/CmdHandler.h" +#include "fboss/cli/fboss2/commands/show/bgp/CmdShowUtils.h" +#include "fboss/cli/fboss2/commands/show/bgp/table/CmdShowBgpTable.h" +#include "fboss/cli/fboss2/utils/CmdClientUtilsCommon.h" +#include "fboss/cli/fboss2/utils/CmdUtilsCommon.h" +#include "fboss/cli/fboss2/utils/HostInfo.h" +#include "neteng/fboss/bgp/if/gen-cpp2/TBgpService.h" +#include "neteng/fboss/bgp/if/gen-cpp2/bgp_thrift_types.h" + +namespace facebook::fboss { +using namespace neteng::fboss::bgp::thrift; + +struct CmdShowBgpTablePrefixTraits : public ReadCommandTraits { + using ParentCmd = CmdShowBgpTable; + static constexpr utils::ObjectArgTypeId ObjectArgTypeId = + utils::ObjectArgTypeId::OBJECT_ARG_TYPE_ID_IP_LIST; + using ObjectArgType = std::vector; + using RetType = TRibEntryWithHost; +}; + +class CmdShowBgpTablePrefix + : public CmdHandler { + public: + using ObjectArgType = CmdShowBgpTablePrefixTraits::RetType; + using RetType = CmdShowBgpTablePrefixTraits::RetType; + + RetType queryClient( + const HostInfo& hostInfo, + const std::vector& prefixes) { + std::vector allEntries; + auto client = utils::createClient>(hostInfo); + TRibEntryWithHost result; + + if (prefixes.empty()) { + std::cout + << "No prefixes entered. Usage: fboss2 show bgp table prefix " + << std::endl; + return result; + } + + for (const auto& prefix : prefixes) { + std::vector newEntry; + client->sync_getRibPrefix(newEntry, prefix); + allEntries.insert(allEntries.end(), newEntry.begin(), newEntry.end()); + } + result.tRibEntries() = std::move(allEntries); + result.host() = hostInfo.getName(); + result.oobName() = hostInfo.getOobName(); + result.ip() = hostInfo.getIpStr(); + return result; + } + + void printOutput(RetType& data, std::ostream& out = std::cout) { + if (!data.tRibEntries()->empty()) { + printRIBEntries(out, data, /*detail=*/true); + } + } +}; +} // namespace facebook::fboss diff --git a/fboss/cli/fboss2/oss/NetTools.h b/fboss/cli/fboss2/oss/NetTools.h new file mode 100644 index 0000000000000..540e97b424187 --- /dev/null +++ b/fboss/cli/fboss2/oss/NetTools.h @@ -0,0 +1,116 @@ +/* + * 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 +#include +#include + +namespace nettools { +namespace bgplib { + +// Stub for BGP origin attribute enum +enum class BgpAttrOrigin { + IGP = 0, + EGP = 1, + INCOMPLETE = 2, +}; + +// Stub for BGP community attribute +class BgpAttrCommunityC { + public: + // Convert 32-bit community value to asn:value format + explicit BgpAttrCommunityC(const std::string& commStr) : commStr_(commStr) {} + + static std::shared_ptr createBgpAttrCommunity( + const std::string& commStr) { + return std::make_shared(commStr); + } + + std::string to_string() const { + // Convert 32-bit community to asn:value format + // BGP communities are encoded as: (asn << 16) | value + try { + uint32_t comm32 = std::stoul(commStr_); + uint16_t asn = (comm32 >> 16) & 0xFFFF; + uint16_t value = comm32 & 0xFFFF; + return std::to_string(asn) + ":" + std::to_string(value); + } catch (...) { + // If conversion fails, return original string + return commStr_; + } + } + + private: + std::string commStr_; +}; + +// Constant for null/empty message +constexpr const char* kNullMessage = ""; + +// Well-known BGP communities (subset) +// Format: "asn:value" -> "ALIAS" +inline const std::map& getWellKnownCommunities() { + static const std::map wellKnown = { + {"65000:1", "NO_EXPORT"}, + {"65000:2", "NO_ADVERTISE"}, + {"65000:3", "NO_EXPORT_SUBCONFED"}, + // Add more as needed + }; + return wellKnown; +} + +// Stub for finding communities in a set +// Returns a map of community sets to their aliases +inline std::map, std::string> findCommunities( + const std::vector& communities, + const std::map, std::string>& communitySet) { + std::map, std::string> result; + const auto& wellKnown = getWellKnownCommunities(); + + for (const auto& comm : communities) { + std::set commSet; + commSet.insert(comm); + + // Check config-sourced community names first, then well-known aliases + auto configIt = communitySet.find(commSet); + if (configIt != communitySet.end()) { + result[commSet] = configIt->second; + } else { + auto wellKnownIt = wellKnown.find(comm); + result[commSet] = + (wellKnownIt != wellKnown.end()) ? wellKnownIt->second : kNullMessage; + } + } + return result; +} + +// Stub for BgpPathC topology info conversion +class BgpPathC { + public: + // Accept any type for topoInfoToStr - we don't know the exact type + template + static std::string topoInfoToStr(const T& /* values */) { + // Return placeholder - tests will show what's expected + return "TopoInfo(stub)"; + } +}; + +// Stub for device type check +inline bool isAristaDevice() { + // Return false - not an Arista device + return false; +} + +} // namespace bgplib +} // namespace nettools diff --git a/fboss/cli/fboss2/test/BUCK b/fboss/cli/fboss2/test/BUCK index 327669a19e6cc..edbe03d58ffcc 100644 --- a/fboss/cli/fboss2/test/BUCK +++ b/fboss/cli/fboss2/test/BUCK @@ -14,6 +14,7 @@ cpp_library( exported_deps = [ "fbsource//third-party/googletest:gmock", "fbsource//third-party/googletest:gtest", + ":bgp_mock_client", "//fboss/agent/if:ctrl-cpp2-services", "//fboss/agent/if:ctrl-cpp2-types", "//fboss/cli/fboss2:cmd-common-utils", @@ -25,14 +26,54 @@ cpp_library( "//fboss/lib:thrift_service_utils", "//fboss/qsfp_service/if:qsfp-cpp2-services", "//fboss/qsfp_service/if:transceiver-cpp2-types", + "//neteng/fboss/bgp/if:bgp_thrift-cpp2-services", "//folly:network_address", + "//folly/coro:gmock_helpers", "//folly/io/async:async_socket", - "//neteng/fboss/bgp/if:bgp_thrift-cpp2-services", "//thrift/lib/cpp2:server", "//thrift/lib/cpp2/util:util", ], ) +cpp_library( + name = "bgp_test_utils", + srcs = ["CmdBgpTestUtils.cpp"], + headers = [ + "CmdBgpTestUtils.h", + ], + exported_deps = [ + "//fboss/agent:address_utils", + "//neteng/fboss/bgp/if:bgp_thrift-cpp2-types", + "//folly:network_address", + ], + exported_external_deps = [ + ("boost", None, "boost_algorithm"), + ], +) + +cpp_library( + name = "bgp_mock_client", + headers = [ + "bgp/MockBgpClient.h", + ], + exported_deps = [ + "fbsource//third-party/googletest:gmock", + "//neteng/fboss/bgp/if:bgp_thrift-cpp2-services", + "//neteng/fboss/bgp/if:bgp_thrift-cpp2-types", + ], +) + +cpp_library( + name = "config_test_utils", + srcs = ["CmdShowConfigTestUtils.cpp"], + headers = [ + "CmdShowConfigTestUtils.h", + ], + exported_deps = [ + "//folly/json:dynamic", + ], +) + cpp_unittest( name = "framework_test", srcs = [ @@ -74,6 +115,28 @@ cpp_unittest( "CmdShowAggregatePortTest.cpp", "CmdShowArpTest.cpp", "CmdShowConfigRunningTest.cpp", + "CmdShowBgpChangelistTest.cpp", + "CmdShowBgpNeighborsAdvertisedPostPolicyTest.cpp", + "CmdShowBgpNeighborsAdvertisedPrePolicyTest.cpp", + "CmdShowBgpNeighborsAdvertisedRejectedTest.cpp", + "CmdShowBgpNeighborsReceivedPostPolicyTest.cpp", + "CmdShowBgpNeighborsReceivedPrePolicyTest.cpp", + "CmdShowBgpNeighborsReceivedRejectedTest.cpp", + "CmdShowBgpNeighborsTest.cpp", + "CmdShowBgpOriginatedRoutesTest.cpp", + "CmdShowBgpShadowRibTest.cpp", + "CmdShowBgpStatsAttrsTest.cpp", + "CmdShowBgpStatsEntriesTest.cpp", + "CmdShowBgpStatsPolicyTest.cpp", + "CmdShowBgpStreamSubscriberTest.cpp", + "CmdShowBgpStreamSummaryTest.cpp", + "CmdShowBgpSummaryEgressTest.cpp", + "CmdShowBgpSummaryTest.cpp", + "CmdShowBgpTableCommunityTest.cpp", + "CmdShowBgpTableDetailTest.cpp", + "CmdShowBgpTableMoreSpecificsTest.cpp", + "CmdShowBgpTablePrefixTest.cpp", + "CmdShowBgpTableTest.cpp", "CmdShowCpuPortTest.cpp", "CmdShowExampleTest.cpp", "CmdShowFlowletTest.cpp", @@ -115,7 +178,10 @@ cpp_unittest( deps = [ "fbsource//third-party/fmt:fmt", "fbsource//third-party/googletest:gmock", + ":bgp_mock_client", + ":bgp_test_utils", ":cmd_test_utils", + ":config_test_utils", "//configerator/structs/neteng/fboss/bgp:bgp_config-cpp2-types", "//fboss/agent:address_utils", "//fboss/agent/if:common-cpp2-types", @@ -125,6 +191,7 @@ cpp_unittest( "//fboss/cli/fboss2:fboss2-config-lib", "//fboss/cli/fboss2:fboss2-lib", "//fboss/cli/fboss2/commands/show/acl:model-cpp2-types", + "//fboss/cli/fboss2/commands/show/bgp/summary:bgp_summary-cpp2-types", "//fboss/cli/fboss2/commands/show/agent:model-cpp2-types", "//fboss/cli/fboss2/commands/show/aggregateport:model-cpp2-types", "//fboss/cli/fboss2/commands/show/arp:model-cpp2-types", @@ -153,6 +220,7 @@ cpp_unittest( "//folly:string", "//folly/json:dynamic", "//neteng/fboss/bgp/if:bgp_thrift-cpp2-services", + "//neteng/fboss/bgp/if:bgp_thrift-cpp2-types", "//thrift/lib/cpp2/protocol:protocol", "//thrift/lib/cpp2/reflection:testing", ], diff --git a/fboss/cli/fboss2/test/CmdBgpTestUtils.cpp b/fboss/cli/fboss2/test/CmdBgpTestUtils.cpp new file mode 100644 index 0000000000000..22dffa79b3758 --- /dev/null +++ b/fboss/cli/fboss2/test/CmdBgpTestUtils.cpp @@ -0,0 +1,333 @@ +/* + * 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/test/CmdBgpTestUtils.h" +#ifndef IS_OSS +#include "configerator/distribution/api/ScopedConfigeratorFake.h" +#endif +#include "neteng/fboss/bgp/if/gen-cpp2/bgp_thrift_types.h" + +#include // NOLINT(misc-include-cleaner) +#include +#include // NOLINT(misc-include-cleaner) +#include +#include +#include + +#include +#include +#include +#include +#include + +namespace facebook::fboss { +using namespace std::chrono; +using namespace neteng::fboss::bgp::thrift; +using namespace neteng::fboss::bgp_attr; +#ifndef IS_OSS +using configerator::ScopedConfigeratorFake; +#endif + +const std::string kBestGroupName = "bestGroupName"; +const std::string kDefaultGroupName = "defaultGroupName"; +const std::string kIpAddress = "8.0.0.0/32"; +const std::string kPeerId = "1.2.3.4"; +const std::string kPeerDescription = "one.two.three.four"; +const std::string kNextHop = "8.0.0.1"; +const std::string kRibEntryMarkersHeader = + "Markers: * - One of the best entries, @ - The best entry, % - Pending best path selection\n" + "Acronyms: ASP - AS Path, LP - Local Preference, LBW - Link Bandwidth, LM - Last Modified\n"; + +// Common BGP peer state constants +const TBgpPeerState kEstablishedPeerState = TBgpPeerState::ESTABLISHED; +const TBgpPeerState kIdlePeerState = TBgpPeerState::IDLE; + +// Common established session constants +const int kEstablishedRemoteAS = 1; +const std::string_view kEstablishedPeerAddress = "1.2.3.4"; +const std::string_view kEstablishedRemoteBgpId = "4.3.2.1"; +const std::string_view kEstablishedDescription = "fsw001.p015.f01.prn6"; +const int kUptime = 1000; +const int kEstablishedDowntime = 2000; +const int kEstablishedNumResets = 9; +const bool kEstablishedIsGraceful = true; +const int kEstablishedPrePolicyRcvdPrefixCount = 2; +const int kEstablishedPostPolicySentPrefixCount = 2; +const int kEstablishedRecvUpdateMsgs = 100; +const int kEstablishedSentUpdateMsgs = 150; +const int kEstablishedBackpressureEvents = 10; +const int kEstablishedSuppressedUpdates = 5; + +// Common idle session constants +const int kIdleRemoteAS = 4651; +const std::string_view kIdlePeerAddress = "2.3.4.5"; +const std::string_view kIdleRemoteBgpId = "6.7.8.9"; +const std::string_view kIdleDescription = "fsw007.p015.f01.prn6"; +const bool kIdleIsGraceful = false; +const int kIdlePrePolicyRcvdPrefixCount = 0; +const int kIdlePostPolicySentPrefixCount = 0; +const int kIdleDowntime = 2500; +const int kIdleNumResets = 33; +const int kIdleRecvUpdateMsgs = 50; +const int kIdleSentUpdateMsgs = 75; +const int kIdleBackpressureEvents = 20; +const int kIdleSuppressedUpdates = 15; + +// Common description constants +const std::string_view kIbgpDescription = "IBGP v4 Peers"; + +// Common group name constants +const std::string_view kEstablishedGroupName = "ESTABLISHED_GROUP"; +const std::string_view kIdleGroupName = "IDLE_GROUP"; +const std::string_view kIbgpGroupName = "IBGP_GROUP"; + +// Common socket write constants +const int64_t kEstablishedLastSocketWrite = 0; +const int64_t kIdleLastSocketWrite = 0; + +TIpPrefix getPrefix(const std::string& ipAddress) { + auto maskIndex = ipAddress.find_last_of('/'); + const auto address = (maskIndex == std::string::npos) + ? folly::IPAddress(ipAddress) + : folly::IPAddress(ipAddress.substr(0, maskIndex)); + const auto kBinaryAddress = facebook::network::toBinaryAddress(address); + + TIpPrefix ipPrefix; + ipPrefix.prefix_bin() = kBinaryAddress.addr().value().toStdString(); + ipPrefix.num_bits() = (maskIndex != std::string::npos) + ? folly::to(ipAddress.substr(maskIndex + 1)) + : (address.isV4()) ? 32 + : 128; + ipPrefix.afi() = TBgpAfi::AFI_IPV4; + return ipPrefix; +} + +TBgpPath buildPath( + const std::string& prefixAddress, + const std::string& nextHopAddress, + const std::string& peerId, + const std::string& peerDescription, + const bool setCommunity, + const bool setAsPath, + const bool setExtCommunity, + const std::optional& clusterList, + const std::optional& originatorId, + const bool setPolicy, + const std::optional& nextHopWeight, + const std::optional isBestPath, + const bool setMed, + const bool setReceivedPathId, + const bool setPathIdToSend, + const bool setWeight, + const bool setIgpCost) { + TBgpPath path; + path.next_hop() = getPrefix(nextHopAddress); + path.peer_id() = getPrefix(peerId); + path.peer_description() = peerDescription; + path.local_pref() = 25; + path.origin() = 2; + path.last_modified_time() = 1635278860724 * 1000; + path.bestpath_filter_descr() = + "Router-Id, Filter Criterion: Choose Lowest Value"; + if (nextHopWeight.has_value()) { + path.next_hop_weight() = nextHopWeight.value(); + } + + if (setCommunity) { + TBgpCommunity community; + community.community() = 4294390177; + community.asn() = 65527; + community.value() = 12705; + path.communities() = std::initializer_list({community}); + } + if (setExtCommunity) { + TBgpTwoByteAsnExtComm twoByteAsn; + twoByteAsn.type() = 64; + twoByteAsn.sub_type() = 2; + twoByteAsn.asn() = 3; + twoByteAsn.value() = 4; + + TBgpExtCommUnion communityUnion; + communityUnion.two_byte_asn() = twoByteAsn; + + TBgpExtCommunity extCommunity; + extCommunity.u() = communityUnion; + + path.extCommunities() = + std::initializer_list({extCommunity}); + } + if (setAsPath) { + TAsPathSeg pathSegment; + pathSegment.seg_type() = TAsPathSegType::AS_SEQUENCE; + pathSegment.asns() = {65301}; + + path.as_path() = std::initializer_list({pathSegment}); + } + if (clusterList) { + path.cluster_list() = {htonl(*clusterList)}; + } + if (originatorId) { + path.originator_id() = htonl(*originatorId); + } + if (setPolicy) { + path.policy_name() = "Accepted/Modified by PROPAGATE_RSW_FSW_IN term N/A"; + } + if (isBestPath) { + path.is_best_path() = isBestPath.value_or(false /* default */); + } + if (setMed) { + path.med() = 10; + } + if (setReceivedPathId) { + path.path_id() = 5; + } + if (setPathIdToSend) { + path.path_id_to_send() = 6; + } + if (setWeight) { + path.weight() = 20; + } + if (setIgpCost) { + path.igp_cost() = 100; + } + return path; +} + +std::map> getReceivedNetworks( + const std::string& prefixAddress, + const std::string& nextHopAddress, + const bool setCommunity, + const bool setAsPath, + const bool setExtCommunity, + const std::optional& clusterList, + const std::optional& originatorId, + const bool setPolicy, + const bool setMed) { + std::map> queriedNetworks = { + {getPrefix(prefixAddress), + {buildPath( + prefixAddress, + nextHopAddress, + kPeerId, + kPeerDescription, + setCommunity, + setAsPath, + setExtCommunity, + clusterList, + originatorId, + setPolicy, + std::nullopt, + std::nullopt, + setMed)}}}; + return queriedNetworks; +} + +TRibEntry buildEntry( + const std::string& ipAddress, + const std::string& nextHop, + const std::map>& paths, + const bool omitBestPath) { + TRibEntry entry; + entry.best_group() = kBestGroupName; + if (!omitBestPath) { + entry.best_next_hop() = getPrefix(nextHop); + } + entry.prefix() = getPrefix(ipAddress); + entry.paths() = paths; + return entry; +} + +TRibEntry buildEntry( + const std::string& ipAddress, + const std::string& nextHop, + const std::string& peerId, + const std::string& peerDescription, + const std::optional& pathNextHopWeight, + const bool omitBestPath, + const bool omitBestPaths) { + std::map> paths = { + {omitBestPaths ? kDefaultGroupName : kBestGroupName, + {buildPath( + ipAddress, + nextHop, + peerId, + peerDescription, + true, // setCommunity + true, // setAsPath + true, // setExtCommunity + kClusterList, + kOriginatorId, + false, // setPolicy + pathNextHopWeight)}}}; + return buildEntry(ipAddress, nextHop, paths, omitBestPath); +} + +void maskDateInOutput(std::string& output) { + std::size_t dateEndIndex = 0; + // get the next "LM: " + std::size_t dateStartIndex = output.find("LM: ", dateEndIndex); + // newOutput is essentially output with all the date masked by "#" + std::string newOutput; + + while (dateStartIndex != std::string::npos) { + // point to the end of the "LM: " instance + dateStartIndex += 4; + + // add the message between the end of the last date and the start of the + // next date, followed by "#" + newOutput += + output.substr(dateEndIndex, dateStartIndex - dateEndIndex) + "#"; + + // we should be able to find the end of the date + // output.find is never std::string::npos + dateEndIndex = output.find('s', dateStartIndex) + 1; + + // get the next "LM: " + dateStartIndex = output.find("LM: ", dateEndIndex); + } + newOutput += output.substr(dateEndIndex); + + output = newOutput; +} + +TRibEntry buildEntryByCommunity(const std::string& name, int asn, int value) { + TRibEntry entry = buildEntry(); + entry.best_group() = name; + auto paths = entry.paths(); + // Update group name from path + auto node = paths->extract("bestGroupName"); + node.key() = name; + paths->insert(std::move(node)); + + // Set new community values + if (auto communities = paths->at(name).at(0).communities()) { + auto& community = communities->at(0); + community.asn() = asn; + community.value() = value; + } + + return entry; +} + +TBgpStreamSession buildStreamSession( + std::string_view name, + int uptime, + int32_t peerId, + std::string_view peerAddr, + int16_t prefixCount) { + TBgpStreamSession session; + session.subscriber_name() = name.data(); + session.uptime() = uptime; + session.peer_id() = peerId; + session.peer_addr() = peerAddr.data(); + session.sent_prefix_count() = prefixCount; + return session; +} +} // namespace facebook::fboss diff --git a/fboss/cli/fboss2/test/CmdBgpTestUtils.h b/fboss/cli/fboss2/test/CmdBgpTestUtils.h new file mode 100644 index 0000000000000..56aee3dcf4680 --- /dev/null +++ b/fboss/cli/fboss2/test/CmdBgpTestUtils.h @@ -0,0 +1,150 @@ +/* + * 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 // NOLINT(misc-include-cleaner) +#include +#include + +#include +#ifndef IS_OSS +#include "configerator/distribution/api/ScopedConfigeratorFake.h" +#endif + +namespace facebook::fboss { +using namespace neteng::fboss::bgp::thrift; +using namespace neteng::fboss::bgp_attr; +using namespace std::chrono; +#ifndef IS_OSS +using configerator::ScopedConfigeratorFake; +#endif + +// Test constants. +extern const std::string kBestGroupName; +extern const std::string kDefaultGroupName; +extern const std::string kIpAddress; +extern const std::string kPeerId; +extern const std::string kPeerDescription; +extern const std::string kNextHop; +extern const std::string kRibEntryMarkersHeader; +constexpr std::uint32_t kClusterList = 0x02010101; +constexpr std::uint32_t kOriginatorId = 0x03020202; + +// Common BGP peer state constants +extern const TBgpPeerState kEstablishedPeerState; +extern const TBgpPeerState kIdlePeerState; + +// Common established session constants +extern const int kEstablishedRemoteAS; +extern const std::string_view kEstablishedPeerAddress; +extern const std::string_view kEstablishedRemoteBgpId; +extern const std::string_view kEstablishedDescription; +extern const int kUptime; +extern const int kEstablishedDowntime; +extern const int kEstablishedNumResets; +extern const bool kEstablishedIsGraceful; +extern const int kEstablishedPrePolicyRcvdPrefixCount; +extern const int kEstablishedPostPolicySentPrefixCount; +extern const int kEstablishedRecvUpdateMsgs; +extern const int kEstablishedSentUpdateMsgs; +extern const int kEstablishedBackpressureEvents; +extern const int kEstablishedSuppressedUpdates; + +// Common idle session constants +extern const int kIdleRemoteAS; +extern const std::string_view kIdlePeerAddress; +extern const std::string_view kIdleRemoteBgpId; +extern const std::string_view kIdleDescription; +extern const bool kIdleIsGraceful; +extern const int kIdlePrePolicyRcvdPrefixCount; +extern const int kIdlePostPolicySentPrefixCount; +extern const int kIdleDowntime; +extern const int kIdleNumResets; +extern const int kIdleRecvUpdateMsgs; +extern const int kIdleSentUpdateMsgs; +extern const int kIdleBackpressureEvents; +extern const int kIdleSuppressedUpdates; + +// Common description constants +extern const std::string_view kIbgpDescription; + +// Common group name constants +extern const std::string_view kEstablishedGroupName; +extern const std::string_view kIdleGroupName; +extern const std::string_view kIbgpGroupName; + +// Common socket write constants +extern const int64_t kEstablishedLastSocketWrite; +extern const int64_t kIdleLastSocketWrite; + +TIpPrefix getPrefix(const std::string& ipAddress = kIpAddress); +TBgpPath buildPath( + const std::string& prefixAddress = kIpAddress, + const std::string& nextHop = kNextHop, + const std::string& peerId = kPeerId, + const std::string& peerDescription = kPeerDescription, + bool setCommunity = true, + bool setAsPath = true, + bool setExtCommunity = false, + const std::optional& clusterList = std::nullopt, + const std::optional& originatorId = std::nullopt, + bool setPolicy = true, + const std::optional& nextHopWeight = std::nullopt, + const std::optional isBestPath = std::nullopt, + bool setMed = true, + bool setReceivedPathId = true, + bool setPathIdToSend = true, + bool setWeight = true, + bool setIgpCost = true); + +std::map> getReceivedNetworks( + const std::string& prefixAddress = kIpAddress, + const std::string& nextHopAddress = kNextHop, + bool setCommunity = true, + bool setAsPath = true, + bool setExtCommunity = false, + const std::optional& clusterList = std::nullopt, + const std::optional& originatorId = std::nullopt, + bool setPolicy = true, + bool setMed = true); + +TRibEntry buildEntry( + const std::string& ipAddress, + const std::string& nextHop, + const std::map>& paths, + // if set to true, there's no best path selected for advertisement + bool omitBestPath = false); + +TRibEntry buildEntry( + const std::string& ipAddress = kIpAddress, + const std::string& nextHop = kNextHop, + const std::string& peerId = kPeerId, + const std::string& peerDescription = kPeerDescription, + const std::optional& pathNextHopWeight = std::nullopt, + // if set to true, there's no best path selected for advertisement + bool omitBestPath = false, + // if set to true, there's no best paths installed to FIB + bool omitBestPaths = false); + +// Mask the date from bgp table output. Since the last modified time +// duration will change every time the test is ran, we need to mask +// that part of the output to avoid conflicts with the tests. +// +// output: received output +void maskDateInOutput(std::string& output); +TRibEntry buildEntryByCommunity(const std::string& name, int asn, int value); +TBgpStreamSession buildStreamSession( + std::string_view name = "myStreamName.01.abcd2.facebook.com", + int uptime = 1000, + int32_t peerId = 1, + std::string_view peerAddr = kPeerId, + int16_t prefixCount = 14); +} // namespace facebook::fboss diff --git a/fboss/cli/fboss2/test/CmdHandlerTestBase.h b/fboss/cli/fboss2/test/CmdHandlerTestBase.h index 8d6f154abdffd..f17fd39526218 100644 --- a/fboss/cli/fboss2/test/CmdHandlerTestBase.h +++ b/fboss/cli/fboss2/test/CmdHandlerTestBase.h @@ -1,19 +1,21 @@ // (c) Facebook, Inc. and its affiliates. Confidential and proprietary. +#pragma once + #include #include -#include +#include // NOLINT(misc-include-cleaner) #include #include #include "fboss/cli/fboss2/CmdGlobalOptions.h" +#include "fboss/cli/fboss2/commands/show/bgp/CmdShowUtils.h" #include "fboss/cli/fboss2/test/MockClients.h" +#include "fboss/cli/fboss2/test/bgp/MockBgpClient.h" #include "fboss/cli/fboss2/utils/HostInfo.h" #include "fboss/lib/ThriftServiceUtils.h" -#pragma once - namespace facebook::fboss { class CmdHandlerTestBase : public ::testing::Test { @@ -21,7 +23,7 @@ class CmdHandlerTestBase : public ::testing::Test { void SetUp() override { mockedAgent_ = std::make_shared(); mockedQsfpService_ = std::make_shared(); - mockedBgpService_ = std::make_shared(); + // mockedBgpService_ is created in setupMockedBgpServer() when needed mockedFsdb_ = std::make_shared(); localHost_ = std::make_unique( "test.host", "test-oob.host", folly::IPAddressV6("::1")); @@ -36,14 +38,14 @@ class CmdHandlerTestBase : public ::testing::Test { void setupMockedAgentServer() { mockedAgentServer_ = std::make_unique( - mockedAgent_, createFastMockServerConfig()); + mockedAgent_, "::1", 0, createFastMockServerConfig()); // set global agent thrift port to the fake server created on localhost CmdGlobalOptions::getInstance()->setAgentThriftPort( mockedAgentServer_->getAddress().getPort()); mockedQsfpServer_ = std::make_unique( - mockedQsfpService_, createFastMockServerConfig()); + mockedQsfpService_, "::1", 0, createFastMockServerConfig()); // set global agent thrift port to the fake server created on localhost CmdGlobalOptions::getInstance()->setQsfpThriftPort( mockedQsfpServer_->getAddress().getPort()); @@ -58,19 +60,22 @@ class CmdHandlerTestBase : public ::testing::Test { } void setupMockedBgpServer() { -#ifndef IS_OSS + // Create a fresh mock service to avoid stale expectations between tests + mockedBgpService_ = std::make_shared(); mockedBgpServer_ = std::make_unique( - mockedBgpService_, createFastMockServerConfig()); + mockedBgpService_, "::1", 0, createFastMockServerConfig()); CmdGlobalOptions::getInstance()->setBgpThriftPort( mockedBgpServer_->getAddress().getPort()); -#endif } void TearDown() override { - // stop agent servers + // stop agent and BGP servers mockedAgentServer_.reset(); mockedFsdbServer_.reset(); + mockedBgpServer_.reset(); + // Reset BGP mnemonic caches to ensure fresh data for next test + resetBgpMnemonicCaches(); } auto& getMockAgent() { @@ -89,6 +94,11 @@ class CmdHandlerTestBase : public ::testing::Test { return *mockedFsdb_; } + // Alias for getBgpService() used by BGP tests + auto& getMockBgp() { + return *mockedBgpService_; + } + const auto& localhost() { return *localHost_; } @@ -106,10 +116,24 @@ class CmdHandlerTestBase : public ::testing::Test { std::shared_ptr mockedQsfpService_; - std::shared_ptr mockedBgpService_; + std::shared_ptr mockedBgpService_; std::shared_ptr mockedFsdb_; std::unique_ptr localHost_; }; + +// Helper macro to compare vectors of Thrift structs element by element +// EXPECT_THRIFT_EQ doesn't work with vectors, only individual structs +#define EXPECT_THRIFT_EQ_VECTOR(actual, expected) \ + do { \ + ASSERT_EQ((actual).size(), (expected).size()) \ + << "Vector sizes differ: actual=" << (actual).size() \ + << " expected=" << (expected).size(); \ + for (size_t i = 0; i < (expected).size(); ++i) { \ + EXPECT_THRIFT_EQ((actual)[i], (expected)[i]) \ + << "Mismatch at index " << i; \ + } \ + } while (0) + } // namespace facebook::fboss diff --git a/fboss/cli/fboss2/test/CmdShowBgpChangelistTest.cpp b/fboss/cli/fboss2/test/CmdShowBgpChangelistTest.cpp new file mode 100644 index 0000000000000..3daf45ff34dac --- /dev/null +++ b/fboss/cli/fboss2/test/CmdShowBgpChangelistTest.cpp @@ -0,0 +1,224 @@ +/* + * 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 +#include +#include +#include +#include // NOLINT(misc-include-cleaner) +#include +#include "fboss/cli/fboss2/test/CmdHandlerTestBase.h" + +#include "configerator/structs/neteng/bgp_policy/thrift/gen-cpp2/bgp_policy_types.h" // NOLINT(misc-include-cleaner) +#include "fboss/cli/fboss2/commands/show/bgp/changelist/CmdShowBgpChangelist.h" +#include "fboss/cli/fboss2/test/CmdBgpTestUtils.h" +#include "neteng/fboss/bgp/if/gen-cpp2/bgp_thrift_types.h" +#ifndef IS_OSS +#include "nettools/common/TestUtils.h" +#endif + +using facebook::neteng::fboss::bgp::thrift::TRibEntry; +using facebook::neteng::fboss::bgp::thrift::TRibEntryWithHost; +using facebook::neteng::fboss::bgp_attr::TBgpAfi; +using ::testing::_; +using ::testing::Invoke; + +namespace facebook::fboss { +class CmdShowBgpChangelistTestFixture : public CmdHandlerTestBase { + public: + std::vector entriesIPv4_; + std::vector entriesIPv6_; + std::vector combinedEntries_; + + void SetUp() override { + CmdHandlerTestBase::SetUp(); + entriesIPv4_ = {buildEntry()}; + entriesIPv6_ = { + buildEntry("2001::1/64", "2001::2", "2001::3", "two00one::three")}; + combineEntries(); + } + + void combineEntries() { + this->combinedEntries_ = this->entriesIPv4_; + this->combinedEntries_.insert( + combinedEntries_.end(), entriesIPv6_.begin(), entriesIPv6_.end()); + } +}; + +TEST_F(CmdShowBgpChangelistTestFixture, queryClient) { + setupMockedBgpServer(); + EXPECT_CALL(getMockBgp(), getChangeListEntries(_, TBgpAfi::AFI_IPV4)) + .WillOnce(Invoke([&](std::vector& entries, TBgpAfi) { + entries = entriesIPv4_; + })); + + EXPECT_CALL(getMockBgp(), getChangeListEntries(_, TBgpAfi::AFI_IPV6)) + .WillOnce(Invoke([&](std::vector& entries, TBgpAfi) { + entries = entriesIPv6_; + })); + auto result = CmdShowBgpChangelist().queryClient(localhost()); + EXPECT_THRIFT_EQ_VECTOR(*result.tRibEntries(), combinedEntries_); +} + +TEST_F(CmdShowBgpChangelistTestFixture, printOutput) { + combinedEntries_.emplace_back( + buildEntry("8.0.0.1/32", "8.0.0.2", "8.1.2.8", "eight.one.two.eight")); + combinedEntries_.emplace_back( + buildEntry("2001::3/64", "2001::4", "2001::5", "two00one::five")); + + // clang-format off + folly::dynamic cpsConfigValue = folly::dynamic::object + ("communities", + folly::dynamic::array( + folly::dynamic::object("name", "FABRIC_POD_RSW_LOOP") + ("description", "rsw loopback") + ("communities", folly::dynamic::array("65527:12705")) + ) + ) + ("localprefs", + folly::dynamic::array( + folly::dynamic::object("localpref", 20) + ("name", "LOCALPREF_CTRL_BACKUP") + ("description", "low-priority supplementary/backup routes from bgp controller"), + folly::dynamic::object("localpref", 25) + ("name", "LOCALPREF_DEPRIO") + ("description", "deprioritized local preference value")) + ); + // clang-format on + + setupMockedBgpServer(); + EXPECT_CALL(getMockBgp(), getRunningConfig(_)) + .WillRepeatedly(Invoke([cpsConfigValue](std::string& config) { + config = folly::toPrettyJson(cpsConfigValue); + })); + std::stringstream ss; + TRibEntryWithHost tRibEntryWithHost; + tRibEntryWithHost.tRibEntries() = combinedEntries_; + tRibEntryWithHost.host() = localhost().getName(); + tRibEntryWithHost.oobName() = localhost().getOobName(); + tRibEntryWithHost.ip() = localhost().getIpStr(); + CmdShowBgpChangelist().printOutput(tRibEntryWithHost, ss); + std::string output = ss.str(); + + std::string expectedOutput = kRibEntryMarkersHeader + + "\n> 8.0.0.0/32, Selected 1/1 paths\n" + "*@ from 1.2.3.4 (one.two.three.four) via 8.0.0.1 | LBW: None | Origin: INCOMPLETE | LP: DEPRIO/25 | ASP: 65301 | LM: # | NH Weight: N/A | MED: 10 | ID: 5 (rcvd) 6 (sent) | Weight: 20 | IgpCost: 100\n" + "\n> 8.0.0.1/32, Selected 1/1 paths\n" + "*@ from 8.1.2.8 (eight.one.two.eight) via 8.0.0.2 | LBW: None | Origin: INCOMPLETE | LP: DEPRIO/25 | ASP: 65301 | LM: # | NH Weight: N/A | MED: 10 | ID: 5 (rcvd) 6 (sent) | Weight: 20 | IgpCost: 100\n" + "\n> 2001::1/64, Selected 1/1 paths\n" + "*@ from 2001::3 (two00one::three) via 2001::2 | LBW: None | Origin: INCOMPLETE | LP: DEPRIO/25 | ASP: 65301 | LM: # | NH Weight: N/A | MED: 10 | ID: 5 (rcvd) 6 (sent) | Weight: 20 | IgpCost: 100\n" + "\n> 2001::3/64, Selected 1/1 paths\n" + "*@ from 2001::5 (two00one::five) via 2001::4 | LBW: None | Origin: INCOMPLETE | LP: DEPRIO/25 | ASP: 65301 | LM: # | NH Weight: N/A | MED: 10 | ID: 5 (rcvd) 6 (sent) | Weight: 20 | IgpCost: 100\n"; + + maskDateInOutput(output); + EXPECT_EQ(output, expectedOutput); +} + +#ifndef IS_OSS +// Test the CPS annotation (Meta-internal feature, not available in OSS) +TEST_F(CmdShowBgpChangelistTestFixture, printCPSOutput) { + // PathSelector contains the info for the active criteria + bgp::rib_policy::TPathSelector activeCriteria1; + activeCriteria1.bgp_native_path_selection_min_nexthop() = 20; + auto pathOverriddenEntry1 = buildEntry( + "8.0.0.1/32", + "8.0.0.2", + "8.1.2.8", + "eight.one.two.eight", + std::nullopt, + true /* omit best path */, + true /* omit best paths */); + pathOverriddenEntry1.active_cps_criteria() = std::move(activeCriteria1); + combinedEntries_.push_back(std::move(pathOverriddenEntry1)); + auto pathOverriddenEntry3 = buildEntry( + "8.0.0.3/32", + "8.0.0.2", + "8.1.2.8", + "eight.one.two.eight", + std::nullopt, + true /* omit best path */); + bgp::rib_policy::TPathSelector activeCriteria3; + activeCriteria3.bgp_native_path_selection_min_nexthop() = 20; + activeCriteria3.relax_bgp_native_path_selection_min_nexthop() = true; + pathOverriddenEntry3.active_cps_criteria() = std::move(activeCriteria3); + combinedEntries_.push_back(std::move(pathOverriddenEntry3)); + + bgp::rib_policy::TPathSelector activeCriteria2; + bgp::rib_policy::TPathSelectionCriteria tCriteria; + tCriteria.min_nexthop() = 13; + activeCriteria2.criteria_list()->push_back(std::move(tCriteria)); + auto pathOverriddenEntry2 = + buildEntry("2001::3/64", "2001::4", "2001::5", "two00one::five"); + pathOverriddenEntry2.active_cps_criteria() = std::move(activeCriteria2); + combinedEntries_.push_back(std::move(pathOverriddenEntry2)); + // clang-format off + folly::dynamic cpsConfigValue2 = folly::dynamic::object + ("communities", + folly::dynamic::array( + folly::dynamic::object("name", "FABRIC_POD_RSW_LOOP") + ("description", "rsw loopback") + ("communities", folly::dynamic::array("65527:12705")) + ) + ) + ("localprefs", + folly::dynamic::array( + folly::dynamic::object("localpref", 20) + ("name", "LOCALPREF_CTRL_BACKUP") + ("description", "low-priority supplementary/backup routes from bgp controller"), + folly::dynamic::object("localpref", 25) + ("name", "LOCALPREF_DEPRIO") + ("description", "deprioritized local preference value")) + ); + // clang-format on + + setupMockedBgpServer(); + EXPECT_CALL(getMockBgp(), getRunningConfig(_)) + .WillRepeatedly(Invoke([cpsConfigValue2](std::string& config) { + config = folly::toPrettyJson(cpsConfigValue2); + })); + std::stringstream ss; + TRibEntryWithHost tRibEntryWithHost; + tRibEntryWithHost.tRibEntries() = combinedEntries_; + tRibEntryWithHost.host() = localhost().getName(); + tRibEntryWithHost.oobName() = localhost().getOobName(); + tRibEntryWithHost.ip() = localhost().getIpStr(); + CmdShowBgpChangelist().printOutput(tRibEntryWithHost, ss); + std::string output = ss.str(); + + std::string expectedOutput = kRibEntryMarkersHeader + + "\n> 8.0.0.0/32, Selected 1/1 paths\n" + "*@ from 1.2.3.4 (one.two.three.four) via 8.0.0.1 | LBW: None | Origin: INCOMPLETE | LP: DEPRIO/25 | ASP: 65301 | LM: # | NH Weight: N/A | MED: 10 | ID: 5 (rcvd) 6 (sent) | Weight: 20 | IgpCost: 100\n" + "\n> 8.0.0.1/32, Selected 0/1 paths\n" + "Path overridden by CPS:\n" + " prefix: 8.0.0.1/32\n" + " default BGP multipath selector\n" + " BGP native min nexthop: 20\n" + " from 8.1.2.8 (eight.one.two.eight) via 8.0.0.2 | LBW: None | Origin: INCOMPLETE | LP: DEPRIO/25 | ASP: 65301 | LM: # | NH Weight: N/A | MED: 10 | ID: 5 (rcvd) 6 (sent) | Weight: 20 | IgpCost: 100\n" + "\n> 8.0.0.3/32, Selected 1/1 paths\n" + "Path overridden by CPS:\n" + " prefix: 8.0.0.3/32\n" + " default BGP multipath selector\n" + " BGP native min nexthop: 20, relaxed: true\n" + "* from 8.1.2.8 (eight.one.two.eight) via 8.0.0.2 | LBW: None | Origin: INCOMPLETE | LP: DEPRIO/25 | ASP: 65301 | LM: # | NH Weight: N/A | MED: 10 | ID: 5 (rcvd) 6 (sent) | Weight: 20 | IgpCost: 100\n" + "\n> 2001::1/64, Selected 1/1 paths\n" + "*@ from 2001::3 (two00one::three) via 2001::2 | LBW: None | Origin: INCOMPLETE | LP: DEPRIO/25 | ASP: 65301 | LM: # | NH Weight: N/A | MED: 10 | ID: 5 (rcvd) 6 (sent) | Weight: 20 | IgpCost: 100\n" + "\n> 2001::3/64, Selected 1/1 paths\n" + "Path overridden by CPS:\n" + " prefix: 2001::3/64\n" + " active criteria:\n" + " path_matchers:\n" + " min nexthop: 13\n" + "*@ from 2001::5 (two00one::five) via 2001::4 | LBW: None | Origin: INCOMPLETE | LP: DEPRIO/25 | ASP: 65301 | LM: # | NH Weight: N/A | MED: 10 | ID: 5 (rcvd) 6 (sent) | Weight: 20 | IgpCost: 100\n"; + + maskDateInOutput(output); + EXPECT_EQ(output, expectedOutput); +} +#endif // IS_OSS +} // namespace facebook::fboss diff --git a/fboss/cli/fboss2/test/CmdShowBgpNeighborsAdvertisedPostPolicyTest.cpp b/fboss/cli/fboss2/test/CmdShowBgpNeighborsAdvertisedPostPolicyTest.cpp new file mode 100644 index 0000000000000..c46a14d146dde --- /dev/null +++ b/fboss/cli/fboss2/test/CmdShowBgpNeighborsAdvertisedPostPolicyTest.cpp @@ -0,0 +1,185 @@ +/* + * 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 +#include +#include +#include // NOLINT(misc-include-cleaner) +#include +#include +#include "fboss/cli/fboss2/test/CmdHandlerTestBase.h" + +#include "configerator/structs/neteng/fboss/bgp/if/gen-cpp2/bgp_attr_types.h" +#include "fboss/agent/AddressUtil.h" // NOLINT(misc-include-cleaner) +#include "fboss/cli/fboss2/commands/show/bgp/neighbors/advertised/BgpNeighborsAdvertisedPostPolicy.h" +#include "fboss/cli/fboss2/test/CmdBgpTestUtils.h" +#include "folly/IPAddress.h" // NOLINT(misc-include-cleaner) +#include "neteng/fboss/bgp/if/gen-cpp2/bgp_thrift_types.h" +#ifndef IS_OSS +#include "nettools/common/TestUtils.h" +#endif + +using namespace ::testing; +using facebook::neteng::fboss::bgp::thrift::TBgpPath; +using facebook::neteng::fboss::bgp_attr::TIpPrefix; +namespace facebook::fboss { + +class NeighborsAdvertisedPostPolicyTestFixture : public CmdHandlerTestBase { + public: + std::map> advertisedNetworks_; + std::string lookableIp_; + void SetUp() override { + CmdHandlerTestBase::SetUp(); + advertisedNetworks_ = getReceivedNetworks( + "8.0.0.0/32", // prefixAddress + "8.0.0.1"); // nextHopAddress + lookableIp_ = "1.2.3.4"; + } +}; + +class NeighborsAdvertisedPostPolicyTestFixtureWithoutMed + : public CmdHandlerTestBase { + public: + std::map> advertisedNetworks_; + void SetUp() override { + CmdHandlerTestBase::SetUp(); + advertisedNetworks_ = getReceivedNetworks( + "8.0.0.0/32", // prefixAddress + "8.0.0.1", // nextHopAddress + true, // setCommunity + true, // setAsPath + false, // setExtCommunity + std::nullopt, // clusterList + std::nullopt, // originatorId + true, // setPolicy + false // setMed + ); + } +}; + +TEST_F(NeighborsAdvertisedPostPolicyTestFixture, queryClient) { + setupMockedBgpServer(); + EXPECT_CALL(getMockBgp(), getPostfilterAdvertisedNetworks2(_, _)) + .WillOnce(Invoke([&](std::map>& networks, + std::unique_ptr queriedIp) { + networks = advertisedNetworks_; + queriedIp = std::make_unique(lookableIp_); + })); + auto results = BgpNeighborsAdvertisedPostPolicy().queryClient( + localhost(), {lookableIp_}, {}); + ASSERT_EQ(results.networkPath()->size(), advertisedNetworks_.size()); + for (const auto& [prefix, paths] : advertisedNetworks_) { + ASSERT_TRUE(results.networkPath()->count(prefix)); + EXPECT_THRIFT_EQ_VECTOR(results.networkPath()->at(prefix), paths); + } +} + +TEST_F(NeighborsAdvertisedPostPolicyTestFixture, printOutput) { + setupMockedBgpServer(); + EXPECT_CALL(getMockBgp(), getRunningConfig(_)) + .WillRepeatedly(Invoke([&](std::string& config) { + // clang-format off + folly::dynamic value = folly::dynamic::object + ("communities", + folly::dynamic::array( + folly::dynamic::object("name", "FABRIC_POD_RSW_LOOP") + ("description", "rsw loopback") + ("communities", folly::dynamic::array("65527:12705")) + ) + ) + ("localprefs", + folly::dynamic::array( + folly::dynamic::object("localpref", 20) + ("name", "LOCALPREF_CTRL_BACKUP") + ("description", "low-priority supplementary/backup routes from bgp controller"), + folly::dynamic::object("localpref", 25) + ("name", "LOCALPREF_DEPRIO") + ("description", "deprioritized local preference value")) + ); + // clang-format on + config = folly::toPrettyJson(value); + })); + std::stringstream ss; + NetworkPathWithHost networkPathWithHost; + networkPathWithHost.networkPath() = advertisedNetworks_; + networkPathWithHost.host() = localhost().getName(); + networkPathWithHost.oobName() = localhost().getOobName(); + networkPathWithHost.ip() = localhost().getIpStr(); + BgpNeighborsAdvertisedPostPolicy().printOutput(networkPathWithHost, ss); + std::string output = ss.str(); + + std::string expectedOutput = + "---\n" + "Network: 8.0.0.0/32\n" + "Nexthop: 8.0.0.1\n" + "Router/OriginatorId: -- \n" + "ClusterList: []\n" + "Communities: FABRIC_POD_RSW_LOOP/65527:12705\n" + "ExtCommunities: \n" + "AsPath: 65301\n" + "LocalPref: DEPRIO/25\n" + "Origin: INCOMPLETE\n" + "MED: 10\n" + "LastModified: 2021-10-26 13:07:40.724 PDT\n" + "Policy: Accepted/Modified by PROPAGATE_RSW_FSW_IN term N/A\n"; + EXPECT_EQ(output, expectedOutput); +} + +TEST_F(NeighborsAdvertisedPostPolicyTestFixtureWithoutMed, printOutput) { + setupMockedBgpServer(); + EXPECT_CALL(getMockBgp(), getRunningConfig(_)) + .WillRepeatedly(Invoke([&](std::string& config) { + // clang-format off + folly::dynamic value = folly::dynamic::object + ("communities", + folly::dynamic::array( + folly::dynamic::object("name", "FABRIC_POD_RSW_LOOP") + ("description", "rsw loopback") + ("communities", folly::dynamic::array("65527:12705")) + ) + ) + ("localprefs", + folly::dynamic::array( + folly::dynamic::object("localpref", 20) + ("name", "LOCALPREF_CTRL_BACKUP") + ("description", "low-priority supplementary/backup routes from bgp controller"), + folly::dynamic::object("localpref", 25) + ("name", "LOCALPREF_DEPRIO") + ("description", "deprioritized local preference value")) + ); + // clang-format on + config = folly::toPrettyJson(value); + })); + std::stringstream ss; + NetworkPathWithHost networkPathWithHost; + networkPathWithHost.networkPath() = advertisedNetworks_; + networkPathWithHost.host() = localhost().getName(); + networkPathWithHost.oobName() = localhost().getOobName(); + networkPathWithHost.ip() = localhost().getIpStr(); + BgpNeighborsAdvertisedPostPolicy().printOutput(networkPathWithHost, ss); + std::string output = ss.str(); + + std::string expectedOutput = + "---\n" + "Network: 8.0.0.0/32\n" + "Nexthop: 8.0.0.1\n" + "Router/OriginatorId: -- \n" + "ClusterList: []\n" + "Communities: FABRIC_POD_RSW_LOOP/65527:12705\n" + "ExtCommunities: \n" + "AsPath: 65301\n" + "LocalPref: DEPRIO/25\n" + "Origin: INCOMPLETE\n" + "MED: Not set\n" + "LastModified: 2021-10-26 13:07:40.724 PDT\n" + "Policy: Accepted/Modified by PROPAGATE_RSW_FSW_IN term N/A\n"; + EXPECT_EQ(output, expectedOutput); +} +} // namespace facebook::fboss diff --git a/fboss/cli/fboss2/test/CmdShowBgpNeighborsAdvertisedPrePolicyTest.cpp b/fboss/cli/fboss2/test/CmdShowBgpNeighborsAdvertisedPrePolicyTest.cpp new file mode 100644 index 0000000000000..1ac10eabe28e9 --- /dev/null +++ b/fboss/cli/fboss2/test/CmdShowBgpNeighborsAdvertisedPrePolicyTest.cpp @@ -0,0 +1,193 @@ +/* + * 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 +#include +#include +#include // NOLINT(misc-include-cleaner) +#include +#include +#include +#include "fboss/cli/fboss2/test/CmdHandlerTestBase.h" + +#include "configerator/structs/neteng/fboss/bgp/if/gen-cpp2/bgp_attr_types.h" +#include "fboss/agent/AddressUtil.h" // NOLINT(misc-include-cleaner) +#include "fboss/cli/fboss2/commands/show/bgp/neighbors/advertised/BgpNeighborsAdvertisedPrePolicy.h" +#include "fboss/cli/fboss2/test/CmdBgpTestUtils.h" +#include "folly/IPAddress.h" // NOLINT(misc-include-cleaner) +#include "neteng/fboss/bgp/if/gen-cpp2/bgp_thrift_types.h" +#ifndef IS_OSS +#include "nettools/common/TestUtils.h" +#endif + +using namespace ::testing; +using namespace facebook::neteng::fboss::bgp::thrift; +using facebook::neteng::fboss::bgp_attr::TAsPath; +namespace facebook::fboss { + +class NeighborsAdvertisedPrePolicyTestFixture : public CmdHandlerTestBase { + public: + std::map> advertisedNetworks_; + std::string lookableIp_; + void SetUp() override { + CmdHandlerTestBase::SetUp(); + advertisedNetworks_ = getReceivedNetworks( + "8.0.0.0/32", // prefixAddress + "8.0.0.1", // nextHopAddress + true, // setCommunity + true, // setAsPath + false, // setExtCommunity + std::nullopt, // clusterList + std::nullopt, // originatorId + false); // setPolicy + lookableIp_ = "1.2.3.4"; + } +}; + +class NeighborsAdvertisedPrePolicyTestFixtureWithoutMed + : public CmdHandlerTestBase { + public: + std::map> advertisedNetworks_; + void SetUp() override { + CmdHandlerTestBase::SetUp(); + advertisedNetworks_ = getReceivedNetworks( + "8.0.0.0/32", // prefixAddress + "8.0.0.1", // nextHopAddress + true, // setCommunity + true, // setAsPath + false, // setExtCommunity + std::nullopt, // clusterList + std::nullopt, // originatorId + false, // setPolicy + false); // setMed + } +}; + +TEST_F(NeighborsAdvertisedPrePolicyTestFixture, queryClient) { + setupMockedBgpServer(); + EXPECT_CALL(getMockBgp(), getPrefilterAdvertisedNetworks2(_, _)) + .WillOnce(Invoke([&](std::map>& networks, + std::unique_ptr queriedIp) { + networks = advertisedNetworks_; + queriedIp = std::make_unique(lookableIp_); + })); + auto results = BgpNeighborsAdvertisedPrePolicy().queryClient( + localhost(), {lookableIp_}, {}); + ASSERT_EQ(results.networkPath()->size(), advertisedNetworks_.size()); + for (const auto& [prefix, paths] : advertisedNetworks_) { + ASSERT_TRUE(results.networkPath()->count(prefix)); + EXPECT_THRIFT_EQ_VECTOR(results.networkPath()->at(prefix), paths); + } +} + +TEST_F(NeighborsAdvertisedPrePolicyTestFixture, printOutputWithUnsetValues) { + setupMockedBgpServer(); + EXPECT_CALL(getMockBgp(), getRunningConfig(_)) + .WillRepeatedly(Invoke([&](std::string& config) { + // clang-format off + folly::dynamic value = folly::dynamic::object + ("communities", + folly::dynamic::array( + folly::dynamic::object("name", "FABRIC_POD_RSW_LOOP") + ("description", "rsw loopback") + ("communities", folly::dynamic::array("65527:12705")) + ) + ) + ("localprefs", + folly::dynamic::array( + folly::dynamic::object("localpref", 20) + ("name", "LOCALPREF_CTRL_BACKUP") + ("description", "low-priority supplementary/backup routes from bgp controller"), + folly::dynamic::object("localpref", 25) + ("name", "LOCALPREF_DEPRIO") + ("description", "deprioritized local preference value")) + ); + // clang-format on + config = folly::toPrettyJson(value); + })); + + std::stringstream ss; + NetworkPathWithHost networkPathWithHost; + networkPathWithHost.networkPath() = advertisedNetworks_; + networkPathWithHost.host() = localhost().getName(); + networkPathWithHost.oobName() = localhost().getOobName(); + networkPathWithHost.ip() = localhost().getIpStr(); + BgpNeighborsAdvertisedPrePolicy().printOutput(networkPathWithHost, ss); + std::string output = ss.str(); + + std::string expectedOutput = + "---\n" + "Network: 8.0.0.0/32\n" + "Nexthop: 8.0.0.1\n" + "Router/OriginatorId: -- \n" + "ClusterList: []\n" + "Communities: FABRIC_POD_RSW_LOOP/65527:12705\n" + "ExtCommunities: \n" + "AsPath: 65301\n" + "LocalPref: DEPRIO/25\n" + "Origin: INCOMPLETE\n" + "MED: 10\n" + "LastModified: 2021-10-26 13:07:40.724 PDT\n"; + EXPECT_EQ(output, expectedOutput); +} + +TEST_F( + NeighborsAdvertisedPrePolicyTestFixtureWithoutMed, + printOutputWithUnsetValues) { + setupMockedBgpServer(); + EXPECT_CALL(getMockBgp(), getRunningConfig(_)) + .WillRepeatedly(Invoke([&](std::string& config) { + // clang-format off + folly::dynamic value = folly::dynamic::object + ("communities", + folly::dynamic::array( + folly::dynamic::object("name", "FABRIC_POD_RSW_LOOP") + ("description", "rsw loopback") + ("communities", folly::dynamic::array("65527:12705")) + ) + ) + ("localprefs", + folly::dynamic::array( + folly::dynamic::object("localpref", 20) + ("name", "LOCALPREF_CTRL_BACKUP") + ("description", "low-priority supplementary/backup routes from bgp controller"), + folly::dynamic::object("localpref", 25) + ("name", "LOCALPREF_DEPRIO") + ("description", "deprioritized local preference value")) + ); + // clang-format on + config = folly::toPrettyJson(value); + })); + + std::stringstream ss; + NetworkPathWithHost networkPathWithHost; + networkPathWithHost.networkPath() = advertisedNetworks_; + networkPathWithHost.host() = localhost().getName(); + networkPathWithHost.oobName() = localhost().getOobName(); + networkPathWithHost.ip() = localhost().getIpStr(); + BgpNeighborsAdvertisedPrePolicy().printOutput(networkPathWithHost, ss); + std::string output = ss.str(); + + std::string expectedOutput = + "---\n" + "Network: 8.0.0.0/32\n" + "Nexthop: 8.0.0.1\n" + "Router/OriginatorId: -- \n" + "ClusterList: []\n" + "Communities: FABRIC_POD_RSW_LOOP/65527:12705\n" + "ExtCommunities: \n" + "AsPath: 65301\n" + "LocalPref: DEPRIO/25\n" + "Origin: INCOMPLETE\n" + "MED: Not set\n" + "LastModified: 2021-10-26 13:07:40.724 PDT\n"; + EXPECT_EQ(output, expectedOutput); +} +} // namespace facebook::fboss diff --git a/fboss/cli/fboss2/test/CmdShowBgpNeighborsAdvertisedRejectedTest.cpp b/fboss/cli/fboss2/test/CmdShowBgpNeighborsAdvertisedRejectedTest.cpp new file mode 100644 index 0000000000000..b1413493dfc3a --- /dev/null +++ b/fboss/cli/fboss2/test/CmdShowBgpNeighborsAdvertisedRejectedTest.cpp @@ -0,0 +1,210 @@ +/* + * 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 +#include +#include +#include // NOLINT(misc-include-cleaner) +#include +#include // NOLINT(misc-include-cleaner) +#include // NOLINT(misc-include-cleaner) +#include +#include "fboss/cli/fboss2/test/CmdHandlerTestBase.h" + +#include "configerator/structs/neteng/fboss/bgp/if/gen-cpp2/bgp_attr_types.h" +#include "fboss/agent/AddressUtil.h" // NOLINT(misc-include-cleaner) +#include "fboss/cli/fboss2/commands/show/bgp/neighbors/advertised/BgpNeighborsAdvertisedRejected.h" +#include "fboss/cli/fboss2/test/CmdBgpTestUtils.h" +#include "folly/IPAddress.h" // NOLINT(misc-include-cleaner) +#include "neteng/fboss/bgp/if/gen-cpp2/bgp_thrift_types.h" +#ifndef IS_OSS +#include "nettools/common/TestUtils.h" +#endif + +using namespace ::testing; +using facebook::neteng::fboss::bgp::thrift::TBgpPath; +using facebook::neteng::fboss::bgp_attr::TIpPrefix; +namespace facebook::fboss { + +class NeighborsAdvertisedRejectedTestFixture : public CmdHandlerTestBase { + public: + std::map> advertisedNetworks_; + std::map> acceptedNetworks_; + std::map> mergedNetwoks_; + std::string lookableIp_; + void SetUp() override { + CmdHandlerTestBase::SetUp(); + lookableIp_ = "8.0.0.0"; + advertisedNetworks_ = getReceivedNetworks( + lookableIp_ + "/32", // prefixAddress + "8.0.0.1"); // nextHopAddress + + acceptedNetworks_ = getReceivedNetworks( + "8.0.0.2/32", // prefixAddress + "8.1.2.1"); // nextHopAddress + + mergedNetwoks_ = advertisedNetworks_; + mergedNetwoks_.insert(acceptedNetworks_.begin(), acceptedNetworks_.end()); + } +}; + +class NeighborsAdvertisedRejectedTestFixtureWithoutMed + : public CmdHandlerTestBase { + public: + std::map> advertisedNetworks_; + std::map> acceptedNetworks_; + std::map> mergedNetwoks_; + std::string lookableIp_; + void SetUp() override { + CmdHandlerTestBase::SetUp(); + lookableIp_ = "8.0.0.0"; + advertisedNetworks_ = getReceivedNetworks( + lookableIp_ + "/32", // prefixAddress + "8.0.0.1", // nextHopAddress + true, // setCommunity + true, // setAsPath + false, // setExtCommunity + std::nullopt, // clusterList + std::nullopt, // originatorId + true, // setPolicy + false); // setMed + } +}; + +// Test that one route is accepted when two routes are advertised +TEST_F( + NeighborsAdvertisedRejectedTestFixture, + queryClientWithTwoAdvertisedRoutes) { + setupMockedBgpServer(); + EXPECT_CALL(getMockBgp(), getPrefilterAdvertisedNetworks2(_, _)) + .WillOnce(Invoke([&](std::map>& networks, + std::unique_ptr queriedIp) { + networks = mergedNetwoks_; + queriedIp = std::make_unique(lookableIp_); + })); + + EXPECT_CALL(getMockBgp(), getPostfilterAdvertisedNetworks2(_, _)) + .WillOnce(Invoke([&](std::map>& networks, + std::unique_ptr queriedIp) { + networks = acceptedNetworks_; + queriedIp = std::make_unique(lookableIp_); + })); + + auto results = BgpNeighborsAdvertisedRejected().queryClient( + localhost(), {lookableIp_}, {}); + ASSERT_EQ(results.networkPath()->size(), advertisedNetworks_.size()); + for (const auto& [prefix, paths] : advertisedNetworks_) { + ASSERT_TRUE(results.networkPath()->count(prefix)); + EXPECT_THRIFT_EQ_VECTOR(results.networkPath()->at(prefix), paths); + } +} + +TEST_F(NeighborsAdvertisedRejectedTestFixture, printOutput) { + setupMockedBgpServer(); + EXPECT_CALL(getMockBgp(), getRunningConfig(_)) + .WillRepeatedly(Invoke([&](std::string& config) { + // clang-format off + folly::dynamic value = folly::dynamic::object + ("communities", + folly::dynamic::array( + folly::dynamic::object("name", "FABRIC_POD_RSW_LOOP") + ("description", "rsw loopback") + ("communities", folly::dynamic::array("65527:12705")) + ) + ) + ("localprefs", + folly::dynamic::array( + folly::dynamic::object("localpref", 20) + ("name", "LOCALPREF_CTRL_BACKUP") + ("description", "low-priority supplementary/backup routes from bgp controller"), + folly::dynamic::object("localpref", 25) + ("name", "LOCALPREF_DEPRIO") + ("description", "deprioritized local preference value")) + ); + // clang-format on + config = folly::toPrettyJson(value); + })); + std::stringstream ss; + NetworkPathWithHost networkPathWithHost; + networkPathWithHost.networkPath() = advertisedNetworks_; + networkPathWithHost.host() = localhost().getName(); + networkPathWithHost.oobName() = localhost().getOobName(); + networkPathWithHost.ip() = localhost().getIpStr(); + BgpNeighborsAdvertisedRejected().printOutput(networkPathWithHost, ss); + std::string output = ss.str(); + + std::string expectedOutput = + "---\n" + "Network: 8.0.0.0/32\n" + "Nexthop: 8.0.0.1\n" + "Router/OriginatorId: -- \n" + "ClusterList: []\n" + "Communities: FABRIC_POD_RSW_LOOP/65527:12705\n" + "ExtCommunities: \n" + "AsPath: 65301\n" + "LocalPref: DEPRIO/25\n" + "Origin: INCOMPLETE\n" + "MED: 10\n" + "LastModified: 2021-10-26 13:07:40.724 PDT\n" + "Policy: Accepted/Modified by PROPAGATE_RSW_FSW_IN term N/A\n"; + EXPECT_EQ(output, expectedOutput); +} + +TEST_F(NeighborsAdvertisedRejectedTestFixtureWithoutMed, printOutput) { + setupMockedBgpServer(); + EXPECT_CALL(getMockBgp(), getRunningConfig(_)) + .WillRepeatedly(Invoke([&](std::string& config) { + // clang-format off + folly::dynamic value = folly::dynamic::object + ("communities", + folly::dynamic::array( + folly::dynamic::object("name", "FABRIC_POD_RSW_LOOP") + ("description", "rsw loopback") + ("communities", folly::dynamic::array("65527:12705")) + ) + ) + ("localprefs", + folly::dynamic::array( + folly::dynamic::object("localpref", 20) + ("name", "LOCALPREF_CTRL_BACKUP") + ("description", "low-priority supplementary/backup routes from bgp controller"), + folly::dynamic::object("localpref", 25) + ("name", "LOCALPREF_DEPRIO") + ("description", "deprioritized local preference value")) + ); + // clang-format on + config = folly::toPrettyJson(value); + })); + std::stringstream ss; + NetworkPathWithHost networkPathWithHost; + networkPathWithHost.networkPath() = advertisedNetworks_; + networkPathWithHost.host() = localhost().getName(); + networkPathWithHost.oobName() = localhost().getOobName(); + networkPathWithHost.ip() = localhost().getIpStr(); + BgpNeighborsAdvertisedRejected().printOutput(networkPathWithHost, ss); + std::string output = ss.str(); + + std::string expectedOutput = + "---\n" + "Network: 8.0.0.0/32\n" + "Nexthop: 8.0.0.1\n" + "Router/OriginatorId: -- \n" + "ClusterList: []\n" + "Communities: FABRIC_POD_RSW_LOOP/65527:12705\n" + "ExtCommunities: \n" + "AsPath: 65301\n" + "LocalPref: DEPRIO/25\n" + "Origin: INCOMPLETE\n" + "MED: Not set\n" + "LastModified: 2021-10-26 13:07:40.724 PDT\n" + "Policy: Accepted/Modified by PROPAGATE_RSW_FSW_IN term N/A\n"; + EXPECT_EQ(output, expectedOutput); +} +} // namespace facebook::fboss diff --git a/fboss/cli/fboss2/test/CmdShowBgpNeighborsReceivedPostPolicyTest.cpp b/fboss/cli/fboss2/test/CmdShowBgpNeighborsReceivedPostPolicyTest.cpp new file mode 100644 index 0000000000000..b4205a01b4f13 --- /dev/null +++ b/fboss/cli/fboss2/test/CmdShowBgpNeighborsReceivedPostPolicyTest.cpp @@ -0,0 +1,185 @@ +/* + * 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 +#include +#include +#include // NOLINT(misc-include-cleaner) +#include +#include +#include "fboss/cli/fboss2/test/CmdHandlerTestBase.h" + +#include "configerator/structs/neteng/fboss/bgp/if/gen-cpp2/bgp_attr_types.h" +#include "fboss/agent/AddressUtil.h" // NOLINT(misc-include-cleaner) +#include "fboss/cli/fboss2/commands/show/bgp/neighbors/received/BgpNeighborsReceivedPostPolicy.h" +#include "fboss/cli/fboss2/test/CmdBgpTestUtils.h" +#include "folly/IPAddress.h" // NOLINT(misc-include-cleaner) +#include "neteng/fboss/bgp/if/gen-cpp2/bgp_thrift_types.h" +#ifndef IS_OSS +#include "nettools/common/TestUtils.h" +#endif + +using namespace ::testing; +using facebook::neteng::fboss::bgp::thrift::TBgpPath; +using facebook::neteng::fboss::bgp_attr::TAsPath; +using facebook::neteng::fboss::bgp_attr::TIpPrefix; +namespace facebook::fboss { + +class NeighborsReceivedPostPolicyTestFixture : public CmdHandlerTestBase { + public: + std::map> receivedNetworks_; + std::string lookableIp_; + void SetUp() override { + CmdHandlerTestBase::SetUp(); + receivedNetworks_ = getReceivedNetworks( + "8.0.0.0/32", // prefixAddress + "8.0.0.1"); // nextHopAddress + lookableIp_ = "1.2.3.4"; + } +}; + +class NeighborsReceivedPostPolicyTestFixtureWithoutMed + : public CmdHandlerTestBase { + public: + std::map> receivedNetworks_; + void SetUp() override { + CmdHandlerTestBase::SetUp(); + receivedNetworks_ = getReceivedNetworks( + "8.0.0.0/32", // prefixAddress + "8.0.0.1", // nextHopAddress + true, // setCommunity + true, // setAsPath + false, // setExtCommunity + std::nullopt, // clusterList + std::nullopt, // originatorId + true, // setPolicy + false); // setMed + } +}; + +TEST_F(NeighborsReceivedPostPolicyTestFixture, queryClient) { + setupMockedBgpServer(); + EXPECT_CALL(getMockBgp(), getPostfilterReceivedNetworks2(_, _)) + .WillOnce(Invoke([&](std::map>& networks, + std::unique_ptr queriedIp) { + networks = receivedNetworks_; + queriedIp = std::make_unique(lookableIp_); + })); + auto results = BgpNeighborsReceivedPostPolicy().queryClient( + localhost(), {lookableIp_}, {}); + ASSERT_EQ(results.networkPath()->size(), receivedNetworks_.size()); + for (const auto& [prefix, paths] : receivedNetworks_) { + ASSERT_TRUE(results.networkPath()->count(prefix)); + EXPECT_THRIFT_EQ_VECTOR(results.networkPath()->at(prefix), paths); + } +} + +TEST_F(NeighborsReceivedPostPolicyTestFixture, printOutput) { + setupMockedBgpServer(); + EXPECT_CALL(getMockBgp(), getRunningConfig(_)) + .WillRepeatedly(Invoke([&](std::string& config) { + // clang-format off + folly::dynamic value = folly::dynamic::object + ("communities", + folly::dynamic::array( + folly::dynamic::object("name", "FABRIC_POD_RSW_LOOP") + ("description", "rsw loopback") + ("communities", folly::dynamic::array("65527:12705")) + ) + ) + ("localprefs", + folly::dynamic::array( + folly::dynamic::object("localpref", 20) + ("name", "LOCALPREF_CTRL_BACKUP") + ("description", "low-priority supplementary/backup routes from bgp controller"), + folly::dynamic::object("localpref", 25) + ("name", "LOCALPREF_DEPRIO") + ("description", "deprioritized local preference value")) + ); + // clang-format on + config = folly::toPrettyJson(value); + })); + std::stringstream ss; + NetworkPathWithHost networkPathWithHost; + networkPathWithHost.networkPath() = receivedNetworks_; + networkPathWithHost.host() = localhost().getName(); + networkPathWithHost.oobName() = localhost().getOobName(); + networkPathWithHost.ip() = localhost().getIpStr(); + BgpNeighborsReceivedPostPolicy().printOutput(networkPathWithHost, ss); + std::string output = ss.str(); + + std::string expectedOutput = + "---\n" + "Network: 8.0.0.0/32\n" + "Nexthop: 8.0.0.1\n" + "Router/OriginatorId: -- \n" + "ClusterList: []\n" + "Communities: FABRIC_POD_RSW_LOOP/65527:12705\n" + "ExtCommunities: \n" + "AsPath: 65301\n" + "LocalPref: DEPRIO/25\n" + "Origin: INCOMPLETE\n" + "MED: 10\n" + "LastModified: 2021-10-26 13:07:40.724 PDT\n" + "Policy: Accepted/Modified by PROPAGATE_RSW_FSW_IN term N/A\n"; + EXPECT_EQ(output, expectedOutput); +} + +TEST_F(NeighborsReceivedPostPolicyTestFixtureWithoutMed, printOutput) { + setupMockedBgpServer(); + EXPECT_CALL(getMockBgp(), getRunningConfig(_)) + .WillRepeatedly(Invoke([&](std::string& config) { + // clang-format off + folly::dynamic value = folly::dynamic::object + ("communities", + folly::dynamic::array( + folly::dynamic::object("name", "FABRIC_POD_RSW_LOOP") + ("description", "rsw loopback") + ("communities", folly::dynamic::array("65527:12705")) + ) + ) + ("localprefs", + folly::dynamic::array( + folly::dynamic::object("localpref", 20) + ("name", "LOCALPREF_CTRL_BACKUP") + ("description", "low-priority supplementary/backup routes from bgp controller"), + folly::dynamic::object("localpref", 25) + ("name", "LOCALPREF_DEPRIO") + ("description", "deprioritized local preference value")) + ); + // clang-format on + config = folly::toPrettyJson(value); + })); + std::stringstream ss; + NetworkPathWithHost networkPathWithHost; + networkPathWithHost.networkPath() = receivedNetworks_; + networkPathWithHost.host() = localhost().getName(); + networkPathWithHost.oobName() = localhost().getOobName(); + networkPathWithHost.ip() = localhost().getIpStr(); + BgpNeighborsReceivedPostPolicy().printOutput(networkPathWithHost, ss); + std::string output = ss.str(); + + std::string expectedOutput = + "---\n" + "Network: 8.0.0.0/32\n" + "Nexthop: 8.0.0.1\n" + "Router/OriginatorId: -- \n" + "ClusterList: []\n" + "Communities: FABRIC_POD_RSW_LOOP/65527:12705\n" + "ExtCommunities: \n" + "AsPath: 65301\n" + "LocalPref: DEPRIO/25\n" + "Origin: INCOMPLETE\n" + "MED: Not set\n" + "LastModified: 2021-10-26 13:07:40.724 PDT\n" + "Policy: Accepted/Modified by PROPAGATE_RSW_FSW_IN term N/A\n"; + EXPECT_EQ(output, expectedOutput); +} +} // namespace facebook::fboss diff --git a/fboss/cli/fboss2/test/CmdShowBgpNeighborsReceivedPrePolicyTest.cpp b/fboss/cli/fboss2/test/CmdShowBgpNeighborsReceivedPrePolicyTest.cpp new file mode 100644 index 0000000000000..f5838afac1958 --- /dev/null +++ b/fboss/cli/fboss2/test/CmdShowBgpNeighborsReceivedPrePolicyTest.cpp @@ -0,0 +1,244 @@ +/* + * 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 +#include +#include +#include // NOLINT(misc-include-cleaner) +#include +#include +#include "fboss/cli/fboss2/test/CmdHandlerTestBase.h" + +#include "configerator/structs/neteng/fboss/bgp/if/gen-cpp2/bgp_attr_types.h" +#include "fboss/agent/AddressUtil.h" // NOLINT(misc-include-cleaner) +#include "fboss/cli/fboss2/commands/show/bgp/neighbors/received/BgpNeighborsReceivedPrePolicy.h" +#include "fboss/cli/fboss2/test/CmdBgpTestUtils.h" +#include "folly/IPAddress.h" // NOLINT(misc-include-cleaner) +#include "neteng/fboss/bgp/if/gen-cpp2/bgp_thrift_types.h" +#ifndef IS_OSS +#include "nettools/common/TestUtils.h" +#endif + +using namespace ::testing; +using facebook::neteng::fboss::bgp::thrift::TBgpPath; +using facebook::neteng::fboss::bgp_attr::TAsPath; +using facebook::neteng::fboss::bgp_attr::TIpPrefix; +namespace facebook::fboss { + +class NeighborsReceivedPrePolicyTestFixture : public CmdHandlerTestBase { + public: + std::map> receivedNetworks_; + std::string lookableIp_; + void SetUp() override { + CmdHandlerTestBase::SetUp(); + receivedNetworks_ = getReceivedNetworks( + "8.0.0.0/32", // prefixAddress + "8.0.0.1", // nextHopAddress + true, // setCommunity + true, // setAsPath + false, // setExtCommunity + std::nullopt, // clusterList + std::nullopt, // originatorId + false); // setPolicy + lookableIp_ = "1.2.3.4"; + } +}; + +class NeighborsReceivedPrePolicyTestFixtureWithoutMed + : public CmdHandlerTestBase { + public: + std::map> receivedNetworks_; + void SetUp() override { + CmdHandlerTestBase::SetUp(); + receivedNetworks_ = getReceivedNetworks( + "8.0.0.0/32", // prefixAddress + "8.0.0.1", // nextHopAddress + true, // setCommunity + true, // setAsPath + false, // setExtCommunity + std::nullopt, // clusterList + std::nullopt, // originatorId + false, // setPolicy + false); // setMed + } +}; + +TEST_F(NeighborsReceivedPrePolicyTestFixture, queryClient) { + setupMockedBgpServer(); + EXPECT_CALL(getMockBgp(), getPrefilterReceivedNetworks2(_, _)) + .WillOnce(Invoke([&](std::map>& networks, + std::unique_ptr queriedIp) { + networks = receivedNetworks_; + queriedIp = std::make_unique(lookableIp_); + })); + auto results = BgpNeighborsReceivedPrePolicy().queryClient( + localhost(), {lookableIp_}, {}); + ASSERT_EQ(results.networkPath()->size(), receivedNetworks_.size()); + for (const auto& [prefix, paths] : receivedNetworks_) { + ASSERT_TRUE(results.networkPath()->count(prefix)); + EXPECT_THRIFT_EQ_VECTOR(results.networkPath()->at(prefix), paths); + } +} + +TEST_F(NeighborsReceivedPrePolicyTestFixture, printOutputWithUnsetValues) { + setupMockedBgpServer(); + EXPECT_CALL(getMockBgp(), getRunningConfig(_)) + .WillRepeatedly(Invoke([&](std::string& config) { + // clang-format off + folly::dynamic value = folly::dynamic::object + ("communities", + folly::dynamic::array( + folly::dynamic::object("name", "FABRIC_POD_RSW_LOOP") + ("description", "rsw loopback") + ("communities", folly::dynamic::array("65527:12705")) + ) + ) + ("localprefs", + folly::dynamic::array( + folly::dynamic::object("localpref", 20) + ("name", "LOCALPREF_CTRL_BACKUP") + ("description", "low-priority supplementary/backup routes from bgp controller"), + folly::dynamic::object("localpref", 25) + ("name", "LOCALPREF_DEPRIO") + ("description", "deprioritized local preference value")) + ); + // clang-format on + config = folly::toPrettyJson(value); + })); + std::stringstream ss; + NetworkPathWithHost networkPathWithHost; + networkPathWithHost.networkPath() = receivedNetworks_; + networkPathWithHost.host() = localhost().getName(); + networkPathWithHost.oobName() = localhost().getOobName(); + networkPathWithHost.ip() = localhost().getIpStr(); + BgpNeighborsReceivedPrePolicy().printOutput(networkPathWithHost, ss); + std::string output = ss.str(); + + std::string expectedOutput = + "---\n" + "Network: 8.0.0.0/32\n" + "Nexthop: 8.0.0.1\n" + "Router/OriginatorId: -- \n" + "ClusterList: []\n" + "Communities: FABRIC_POD_RSW_LOOP/65527:12705\n" + "ExtCommunities: \n" + "AsPath: 65301\n" + "LocalPref: DEPRIO/25\n" + "Origin: INCOMPLETE\n" + "MED: 10\n" + "LastModified: 2021-10-26 13:07:40.724 PDT\n"; + EXPECT_EQ(output, expectedOutput); +} + +TEST_F(NeighborsReceivedPrePolicyTestFixture, printOutputWithAllValues) { + setupMockedBgpServer(); + EXPECT_CALL(getMockBgp(), getRunningConfig(_)) + .WillRepeatedly(Invoke([&](std::string& config) { + // clang-format off + folly::dynamic value = folly::dynamic::object + ("communities", + folly::dynamic::array( + folly::dynamic::object("name", "FABRIC_POD_RSW_LOOP") + ("description", "rsw loopback") + ("communities", folly::dynamic::array("65527:12705")) + ) + ) + ("localprefs", + folly::dynamic::array( + folly::dynamic::object("localpref", 20) + ("name", "LOCALPREF_CTRL_BACKUP") + ("description", "low-priority supplementary/backup routes from bgp controller"), + folly::dynamic::object("localpref", 25) + ("name", "LOCALPREF_DEPRIO") + ("description", "deprioritized local preference value")) + ); + // clang-format on + config = folly::toPrettyJson(value); + })); + receivedNetworks_ = getReceivedNetworks( + "8.0.0.0/32", "8.0.0.1", true, true, true, 0x02010101, 0x03020202, false); + + std::stringstream ss; + NetworkPathWithHost networkPathWithHost; + networkPathWithHost.networkPath() = receivedNetworks_; + networkPathWithHost.host() = localhost().getName(); + networkPathWithHost.oobName() = localhost().getOobName(); + networkPathWithHost.ip() = localhost().getIpStr(); + BgpNeighborsReceivedPrePolicy().printOutput(networkPathWithHost, ss); + std::string output = ss.str(); + + std::string expectedOutput = + "---\n" + "Network: 8.0.0.0/32\n" + "Nexthop: 8.0.0.1\n" + "Router/OriginatorId: 2.2.2.3\n" + "ClusterList: [1.1.1.2]\n" + "Communities: FABRIC_POD_RSW_LOOP/65527:12705\n" + "ExtCommunities: Type(64):SubType(2):AS(3):Value(4)\n" + "AsPath: 65301\n" + "LocalPref: DEPRIO/25\n" + "Origin: INCOMPLETE\n" + "MED: 10\n" + "LastModified: 2021-10-26 13:07:40.724 PDT\n"; + EXPECT_EQ(output, expectedOutput); +} + +TEST_F( + NeighborsReceivedPrePolicyTestFixtureWithoutMed, + printOutputWithUnsetValues) { + setupMockedBgpServer(); + EXPECT_CALL(getMockBgp(), getRunningConfig(_)) + .WillRepeatedly(Invoke([&](std::string& config) { + // clang-format off + folly::dynamic value = folly::dynamic::object + ("communities", + folly::dynamic::array( + folly::dynamic::object("name", "FABRIC_POD_RSW_LOOP") + ("description", "rsw loopback") + ("communities", folly::dynamic::array("65527:12705")) + ) + ) + ("localprefs", + folly::dynamic::array( + folly::dynamic::object("localpref", 20) + ("name", "LOCALPREF_CTRL_BACKUP") + ("description", "low-priority supplementary/backup routes from bgp controller"), + folly::dynamic::object("localpref", 25) + ("name", "LOCALPREF_DEPRIO") + ("description", "deprioritized local preference value")) + ); + // clang-format on + config = folly::toPrettyJson(value); + })); + std::stringstream ss; + NetworkPathWithHost networkPathWithHost; + networkPathWithHost.networkPath() = receivedNetworks_; + networkPathWithHost.host() = localhost().getName(); + networkPathWithHost.oobName() = localhost().getOobName(); + networkPathWithHost.ip() = localhost().getIpStr(); + BgpNeighborsReceivedPrePolicy().printOutput(networkPathWithHost, ss); + std::string output = ss.str(); + + std::string expectedOutput = + "---\n" + "Network: 8.0.0.0/32\n" + "Nexthop: 8.0.0.1\n" + "Router/OriginatorId: -- \n" + "ClusterList: []\n" + "Communities: FABRIC_POD_RSW_LOOP/65527:12705\n" + "ExtCommunities: \n" + "AsPath: 65301\n" + "LocalPref: DEPRIO/25\n" + "Origin: INCOMPLETE\n" + "MED: Not set\n" + "LastModified: 2021-10-26 13:07:40.724 PDT\n"; + EXPECT_EQ(output, expectedOutput); +} +} // namespace facebook::fboss diff --git a/fboss/cli/fboss2/test/CmdShowBgpNeighborsReceivedRejectedTest.cpp b/fboss/cli/fboss2/test/CmdShowBgpNeighborsReceivedRejectedTest.cpp new file mode 100644 index 0000000000000..1405c13fce95b --- /dev/null +++ b/fboss/cli/fboss2/test/CmdShowBgpNeighborsReceivedRejectedTest.cpp @@ -0,0 +1,274 @@ +/* + * 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 +#include +#include +#include // NOLINT(misc-include-cleaner) +#include +#include // NOLINT(misc-include-cleaner) +#include // NOLINT(misc-include-cleaner) +#include +#include "fboss/cli/fboss2/test/CmdHandlerTestBase.h" + +#include "configerator/structs/neteng/fboss/bgp/if/gen-cpp2/bgp_attr_types.h" +#include "fboss/agent/AddressUtil.h" // NOLINT(misc-include-cleaner) +#include "fboss/cli/fboss2/commands/show/bgp/neighbors/received/BgpNeighborsReceivedRejected.h" +#include "fboss/cli/fboss2/test/CmdBgpTestUtils.h" +#include "folly/IPAddress.h" // NOLINT(misc-include-cleaner) +#include "neteng/fboss/bgp/if/gen-cpp2/bgp_thrift_types.h" +#ifndef IS_OSS +#include "nettools/common/TestUtils.h" +#endif + +using namespace ::testing; +using facebook::neteng::fboss::bgp::thrift::TBgpPath; +using facebook::neteng::fboss::bgp_attr::TAsPath; +using facebook::neteng::fboss::bgp_attr::TAsPathSeg; +using facebook::neteng::fboss::bgp_attr::TBgpCommunity; +using facebook::neteng::fboss::bgp_attr::TIpPrefix; +namespace facebook::fboss { + +class NeighborsReceivedRejectedTestFixture : public CmdHandlerTestBase { + public: + std::map> receivedNetworks_; + std::map> acceptedNetworks_; + std::map> mergedNetwoks_; + std::string lookableIp_; + void SetUp() override { + CmdHandlerTestBase::SetUp(); + lookableIp_ = "8.0.0.0"; + receivedNetworks_ = getReceivedNetworks( + lookableIp_ + "/32", // prefixAddress + "8.0.0.1"); // nextHopAddress + acceptedNetworks_ = getReceivedNetworks( + "8.0.0.2/32", // prefixAddress + "8.1.2.1"); // nextHopAddress + + mergedNetwoks_ = receivedNetworks_; + mergedNetwoks_.insert(acceptedNetworks_.begin(), acceptedNetworks_.end()); + } + + TIpPrefix getPrefix(const std::string& ipAddress) { + const auto kBinaryAddress = + facebook::network::toBinaryAddress(folly::IPAddress(ipAddress)); + TIpPrefix prefix; + prefix.prefix_bin() = kBinaryAddress.addr().value().toStdString(); + prefix.afi() = TBgpAfi::AFI_IPV4; + prefix.num_bits() = 32; + return prefix; + } + + private: + std::map> createRoutesMap( + const std::string& nextHop, + long commRef, + int asns, + int localPref, + int origin, + const std::string& network) { + const auto kNextHopAddress = + facebook::network::toBinaryAddress(folly::IPAddress(nextHop)); + + TBgpCommunity community; + community.community() = commRef; + + TAsPathSeg pathSegment; + pathSegment.seg_type() = TAsPathSegType::AS_SEQUENCE; + pathSegment.asns() = {asns}; + + TBgpPath path; + path.next_hop()->prefix_bin() = + kNextHopAddress.addr().value().toStdString(); + path.communities() = {community}; + path.extCommunities() = {}; + path.as_path() = {pathSegment}; + path.local_pref() = localPref; + path.origin() = origin; + path.last_modified_time() = 1635278860724 * 1000; + path.policy_name() = "Accepted/Modified by PROPAGATE_RSW_FSW_IN term N/A"; + + return {{getPrefix(network), {path}}}; + } +}; + +class NeighborsReceivedRejectedTestFixtureWithoutMed + : public CmdHandlerTestBase { + public: + std::map> receivedNetworks_; + std::string lookableIp_; + void SetUp() override { + CmdHandlerTestBase::SetUp(); + lookableIp_ = "8.0.0.0"; + receivedNetworks_ = getReceivedNetworks( + lookableIp_ + "/32", // prefixAddress + "8.0.0.1", // nextHopAddress + true, // setCommunity + true, // setAsPath + false, // setExtCommunity + std::nullopt, // clusterList + std::nullopt, // originatorId + true, // setPolicy + false); // setMed + } +}; + +TEST_F(NeighborsReceivedRejectedTestFixture, queryClient) { + setupMockedBgpServer(); + EXPECT_CALL(getMockBgp(), getPrefilterReceivedNetworks2(_, _)) + .WillOnce(Invoke([&](std::map>& networks, + std::unique_ptr queriedIp) { + networks = receivedNetworks_; + queriedIp = std::make_unique(lookableIp_); + })); + + EXPECT_CALL(getMockBgp(), getPostfilterReceivedNetworks2(_, _)) + .WillOnce(Invoke([&](std::map>& networks, + std::unique_ptr queriedIp) { + networks = acceptedNetworks_; + queriedIp = std::make_unique(lookableIp_); + })); + + auto results = BgpNeighborsReceivedRejected().queryClient( + localhost(), {lookableIp_}, {}); + ASSERT_EQ(results.networkPath()->size(), receivedNetworks_.size()); + for (const auto& [prefix, paths] : receivedNetworks_) { + ASSERT_TRUE(results.networkPath()->count(prefix)); + EXPECT_THRIFT_EQ_VECTOR(results.networkPath()->at(prefix), paths); + } +} + +TEST_F(NeighborsReceivedRejectedTestFixture, queryClientWithTwoReceivedRoutes) { + setupMockedBgpServer(); + EXPECT_CALL(getMockBgp(), getPrefilterReceivedNetworks2(_, _)) + .WillOnce(Invoke([&](std::map>& networks, + std::unique_ptr queriedIp) { + networks = mergedNetwoks_; + queriedIp = std::make_unique(lookableIp_); + })); + + EXPECT_CALL(getMockBgp(), getPostfilterReceivedNetworks2(_, _)) + .WillOnce(Invoke([&](std::map>& networks, + std::unique_ptr queriedIp) { + networks = acceptedNetworks_; + queriedIp = std::make_unique(lookableIp_); + })); + + auto results = BgpNeighborsReceivedRejected().queryClient( + localhost(), {lookableIp_}, {}); + ASSERT_EQ(results.networkPath()->size(), receivedNetworks_.size()); + for (const auto& [prefix, paths] : receivedNetworks_) { + ASSERT_TRUE(results.networkPath()->count(prefix)); + EXPECT_THRIFT_EQ_VECTOR(results.networkPath()->at(prefix), paths); + } +} + +TEST_F(NeighborsReceivedRejectedTestFixture, printOutput) { + setupMockedBgpServer(); + EXPECT_CALL(getMockBgp(), getRunningConfig(_)) + .WillRepeatedly(Invoke([&](std::string& config) { + // clang-format off + folly::dynamic value = folly::dynamic::object + ("communities", + folly::dynamic::array( + folly::dynamic::object("name", "FABRIC_POD_RSW_LOOP") + ("description", "rsw loopback") + ("communities", folly::dynamic::array("65527:12705")) + ) + ) + ("localprefs", + folly::dynamic::array( + folly::dynamic::object("localpref", 20) + ("name", "LOCALPREF_CTRL_BACKUP") + ("description", "low-priority supplementary/backup routes from bgp controller"), + folly::dynamic::object("localpref", 25) + ("name", "LOCALPREF_DEPRIO") + ("description", "deprioritized local preference value")) + ); + // clang-format on + config = folly::toPrettyJson(value); + })); + std::stringstream ss; + NetworkPathWithHost networkPathWithHost; + networkPathWithHost.networkPath() = receivedNetworks_; + networkPathWithHost.host() = localhost().getName(); + networkPathWithHost.oobName() = localhost().getOobName(); + networkPathWithHost.ip() = localhost().getIpStr(); + BgpNeighborsReceivedRejected().printOutput(networkPathWithHost, ss); + std::string output = ss.str(); + + std::string expectedOutput = + "---\n" + "Network: 8.0.0.0/32\n" + "Nexthop: 8.0.0.1\n" + "Router/OriginatorId: -- \n" + "ClusterList: []\n" + "Communities: FABRIC_POD_RSW_LOOP/65527:12705\n" + "ExtCommunities: \n" + "AsPath: 65301\n" + "LocalPref: DEPRIO/25\n" + "Origin: INCOMPLETE\n" + "MED: 10\n" + "LastModified: 2021-10-26 13:07:40.724 PDT\n" + "Policy: Accepted/Modified by PROPAGATE_RSW_FSW_IN term N/A\n"; + EXPECT_EQ(output, expectedOutput); +} + +TEST_F(NeighborsReceivedRejectedTestFixtureWithoutMed, printOutput) { + setupMockedBgpServer(); + EXPECT_CALL(getMockBgp(), getRunningConfig(_)) + .WillRepeatedly(Invoke([&](std::string& config) { + // clang-format off + folly::dynamic value = folly::dynamic::object + ("communities", + folly::dynamic::array( + folly::dynamic::object("name", "FABRIC_POD_RSW_LOOP") + ("description", "rsw loopback") + ("communities", folly::dynamic::array("65527:12705")) + ) + ) + ("localprefs", + folly::dynamic::array( + folly::dynamic::object("localpref", 20) + ("name", "LOCALPREF_CTRL_BACKUP") + ("description", "low-priority supplementary/backup routes from bgp controller"), + folly::dynamic::object("localpref", 25) + ("name", "LOCALPREF_DEPRIO") + ("description", "deprioritized local preference value")) + ); + // clang-format on + config = folly::toPrettyJson(value); + })); + std::stringstream ss; + NetworkPathWithHost networkPathWithHost; + networkPathWithHost.networkPath() = receivedNetworks_; + networkPathWithHost.host() = localhost().getName(); + networkPathWithHost.oobName() = localhost().getOobName(); + networkPathWithHost.ip() = localhost().getIpStr(); + BgpNeighborsReceivedRejected().printOutput(networkPathWithHost, ss); + std::string output = ss.str(); + + std::string expectedOutput = + "---\n" + "Network: 8.0.0.0/32\n" + "Nexthop: 8.0.0.1\n" + "Router/OriginatorId: -- \n" + "ClusterList: []\n" + "Communities: FABRIC_POD_RSW_LOOP/65527:12705\n" + "ExtCommunities: \n" + "AsPath: 65301\n" + "LocalPref: DEPRIO/25\n" + "Origin: INCOMPLETE\n" + "MED: Not set\n" + "LastModified: 2021-10-26 13:07:40.724 PDT\n" + "Policy: Accepted/Modified by PROPAGATE_RSW_FSW_IN term N/A\n"; + EXPECT_EQ(output, expectedOutput); +} +} // namespace facebook::fboss diff --git a/fboss/cli/fboss2/test/CmdShowBgpNeighborsTest.cpp b/fboss/cli/fboss2/test/CmdShowBgpNeighborsTest.cpp new file mode 100644 index 0000000000000..2bdf6e047e3df --- /dev/null +++ b/fboss/cli/fboss2/test/CmdShowBgpNeighborsTest.cpp @@ -0,0 +1,694 @@ +/* + * 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 +#include +#include // NOLINT(misc-include-cleaner) +#include +#include +#include // NOLINT(misc-include-cleaner) +#include +#include "fboss/cli/fboss2/test/CmdHandlerTestBase.h" + +#include "configerator/structs/neteng/fboss/bgp/if/gen-cpp2/bgp_attr_types.h" +#include "fboss/cli/fboss2/commands/show/bgp/neighbors/CmdShowBgpNeighbors.h" +#include "neteng/fboss/bgp/if/gen-cpp2/bgp_thrift_types.h" +#ifndef IS_OSS +#include "nettools/common/TestUtils.h" +#endif + +using namespace ::testing; +using facebook::neteng::fboss::bgp::thrift::TBgpAddPathNegotiated; +using facebook::neteng::fboss::bgp::thrift::TBgpPeer; +using facebook::neteng::fboss::bgp::thrift::TBgpSession; +using facebook::neteng::fboss::bgp::thrift::TBgpSessionDetail; + +// Constants for queried IPs +const std::string kLookableIP = "1.2.3.4"; + +namespace facebook::fboss { + +class CmdShowBgpNeighborsTestFixture : public CmdHandlerTestBase { + public: + std::vector establishedNeighborSession_; + std::vector idleNeighborSession_; + std::vector activeNeighborSession_; + std::vector allNeighborSessions_; + std::vector lookableIp_; + void SetUp() override { + CmdHandlerTestBase::SetUp(); + populateMockNeighbors(); + establishedNeighborSession_ = getEstablishedNeighborSession(); + allNeighborSessions_ = getAllNeighborSessions(); + idleNeighborSession_ = getIdleNeighborSession(); + activeNeighborSession_ = getActiveNeighborSession(); + + lookableIp_ = {kLookableIP}; + } + + private: + TBgpSession kNeighborSession1, kNeighborSession2, kNeighborSession3, + kNeighborSession4; + void populateMockNeighbors() { + // + // Populate kNeighborSession1 + // + // kNeighborSession1 {peerAddr 1.2.3.4, remoteBgpId 123456 /* 64.226.1.0 */} + // TBgpPeerState::ESTABLISHED; + // + kNeighborSession1.my_addr() = "5.6.7.8"; + kNeighborSession1.peer_addr() = "1.2.3.4"; + kNeighborSession1.prepolicy_rcvd_prefix_count() = 10; + kNeighborSession1.postpolicy_sent_prefix_count() = 2; + kNeighborSession1.description() = "abc001.d001.e01.fgh1"; + kNeighborSession1.uptime() = 847295; + kNeighborSession1.reset_time() = 729000; + kNeighborSession1.num_resets() = 20; + kNeighborSession1.last_reset_reason() = "MANUAL_STOP"; + kNeighborSession1.advertise_link_bandwidth() = + AdvertiseLinkBandwidth::DISABLE; + kNeighborSession1.receive_link_bandwidth() = ReceiveLinkBandwidth::ACCEPT; + kNeighborSession1.link_bandwidth_bps() = 1.25e+10; + + TBgpPeer peer; + peer.local_as_4_byte() = 2001; + peer.remote_as_4_byte() = 0xfffffff0; // 4294967280 + peer.hold_time() = 15; + peer.peer_state() = TBgpPeerState::ESTABLISHED; + peer.lastResetHoldTimer() = 1635278860724; + peer.lastSentKeepAlive() = 1635278859716; + peer.lastRcvdKeepAlive() = 1635278860724; + + TBgpSessionDetail details; + details.confed_peer() = true; + details.remote_bgp_id() = 123456; // 64.226.1.0 + details.rr_client() = false; + details.connect_mode() = TBgpSessionConnectMode::PASSIVE_ACTIVE; + details.peer_port() = 45824; + details.local_router_id() = "2.3.5.6"; + details.local_port() = 58283; + details.ipv4_unicast() = true; + details.ipv6_unicast() = false; + details.gr_restart_time() = 120; + details.gr_remote_restart_time() = 60; + details.eor_sent_time() = 1635271191576; + details.eor_received_time() = 1635271195614; + details.num_of_flaps() = 20; + + details.sent_update_announcements_ipv4() = 8; + details.sent_update_announcements_ipv6() = 7; + details.sent_update_withdrawals() = 6; + + details.recv_update_announcements_ipv4() = 5; + details.recv_update_announcements_ipv6() = 4; + details.recv_update_withdrawals() = 3; + + details.enforce_first_as_rejects() = 71; + + kNeighborSession1.peer() = peer; + kNeighborSession1.details() = details; + kNeighborSession1.rib_version() = 12345; + + // + // Populate kNeighborSession2 + // + // kNeighborSession2 has the same remote peerAddress as kNeighborSession1, + // but different remoteBgpId 999999999 or 255.201.154.59 + // + // kNeighborSession2 {peerAddr 1.2.3.4, remoteBgpId 999999999 /* + // 255.201.154.59 */} + // TBgpPeerState::ESTABLISHED; + // + + kNeighborSession2 = kNeighborSession1; + if (kNeighborSession2.details()) { // avoid linter + kNeighborSession2.details()->remote_bgp_id() = + 999999999; // 255.201.154.59 + } + + // + // Populate IDLE kNeighborSession3 + // + // This kNeighborSession3 peer major information will be different from the + // first two kNeighborSessions. + // The minor details will be the same to make this peer information + // complete. + // + // kNeighborSession3 {peerAddr 101.102.103.104, remoteBgpId 1010987903 /* + // 127.115.66.60 */ } TBgpPeerState::IDLE; + // AS number = 4008636128 + // + kNeighborSession3 = kNeighborSession1; + kNeighborSession3.peer_addr() = "101.102.103.104"; + kNeighborSession3.description() = "def001.k001.e01.fgh1"; + kNeighborSession3.peer()->remote_as_4_byte() = 0xeeeeeee0; // 4008636128 + kNeighborSession3.peer()->peer_state() = TBgpPeerState::IDLE; + kNeighborSession3.rib_version() = 0; // IDLE peers don't show RIB version + if (kNeighborSession3.details()) { + kNeighborSession3.details()->remote_bgp_id() = + 1010987903; // 127.115.66.60 + } + + // + // Populate ACTIVE kNeighborSession4 + // + // This kNeighborSession4 peer major information will be different from the + // first three kNeighborSessions. + // + // kNeighborSession4 {peerAddr 105.106.107.108, remoteBgpId 1010987904 /* + // 128.115.66.60*/ } TBgpPeerState::ACTIVE; + // AS number = 4008636129 + // + kNeighborSession4 = kNeighborSession1; + kNeighborSession4.peer_addr() = "105.106.107.108"; + kNeighborSession4.description() = "ghi001.j001.k01.ll1"; + kNeighborSession4.peer()->remote_as_4_byte() = 0xeeeeeee1; // 4008636129 + kNeighborSession4.peer()->peer_state() = TBgpPeerState::ACTIVE; + kNeighborSession4.reset_time() = 50000; + kNeighborSession4.num_resets() = 10; + kNeighborSession4.rib_version() = 0; // ACTIVE peers don't show RIB version + if (kNeighborSession4.details()) { + kNeighborSession4.details()->remote_bgp_id() = + 1010987904; // 128.115.66.60 + kNeighborSession4.details()->active_time() = 2777; + } + } + std::vector getEstablishedNeighborSession() { + return {kNeighborSession1}; + } + std::vector getIdleNeighborSession() { + return {kNeighborSession3}; + } + + std::vector getActiveNeighborSession() { + return {kNeighborSession4}; + } + + std::vector getAllNeighborSessions() { + return { + kNeighborSession1, + kNeighborSession2, + kNeighborSession3, + kNeighborSession4}; + } +}; + +TEST_F(CmdShowBgpNeighborsTestFixture, queryClient) { + setupMockedBgpServer(); + EXPECT_CALL(getMockBgp(), getBgpNeighbors(_, _)) + .WillOnce( + Invoke([&](std::vector& neighbors, + std::unique_ptr> queriedIp) { + neighbors = establishedNeighborSession_; + queriedIp = std::make_unique>(lookableIp_); + })); + auto results = CmdShowBgpNeighbors().queryClient(localhost(), lookableIp_); + EXPECT_THRIFT_EQ_VECTOR(results, establishedNeighborSession_); +} + +TEST_F(CmdShowBgpNeighborsTestFixture, printOutputWithoutAddCapabilities) { + std::stringstream ss; + CmdShowBgpNeighbors().printOutput(establishedNeighborSession_, ss); + std::string output = ss.str(); + + std::string expectedOutput = + "BGP neighbor is 1.2.3.4, remote AS 4294967280, Confed Peer: configured\n" + " Description: abc001.d001.e01.fgh1\n" + " BGP version 4, remote router ID 64.226.1.0\n" + " RIB version: 12345\n" + " Hold time is 15s, KeepAlive interval is 5s\n" + " BGP state is ESTABLISHED, up for 0h 14m 7s\n" + " Flapped 20 times, last flap 0h 12m 9s ago\n" + " Last reset reason: MANUAL_STOP\n" + " UCMP Link Bandwidth:\n" + " Advertise: DISABLE\n" + " Receive : ACCEPT\n" + " Value : 99Gbps\n" + " Neighbor Capabilities:\n" + " Multiprotocol IPv4 Unicast: negotiated\n" + " Graceful Restart: received, peer restart-time is 60s\n" + " Graceful Restart: sent, local restart-time is 120s\n" + " Add Path: DISABLED\n\n" + " Prefix statistics: Sent Rcvd \n" + "-------------------------------------\n" + " IPv4, IPv6 Unicast: 2 10 \n\n" + "BGP update announcements sent ipv4: 8\n" + "BGP update announcements sent ipv6: 7\n" + "BGP update withdrawals sent: 6\n" + "BGP update announcements received ipv4: 5\n" + "BGP update announcements received ipv6: 4\n" + "BGP update withdrawals received: 3\n" + "Number of enforce-first-as validation rejections: 71\n" + "Local AS is 2001, local router ID 2.3.5.6\n" + "Local TCP address is 5.6.7.8, local port is 44003\n" + "Remote TCP address is 1.2.3.4, remote port is 179\n" + "TCP connection-mode is PASSIVE_ACTIVE\n" + "Number of session terminations: 20\n" + "HoldTimer last reset at 2021-10-26 13:07:40.724 PDT\n" + "KeepAlive last received at 2021-10-26 13:07:39.716 PDT\n" + "KeepAlive last sent at 2021-10-26 13:07:40.724 PDT\n" + "EOR Sent at 2021-10-26 10:59:51.576 PDT\n" + "EOR Received at 2021-10-26 10:59:55.614 PDT\n\n"; + + EXPECT_EQ(output, expectedOutput); +} + +TEST_F( + CmdShowBgpNeighborsTestFixture, + printOutputWithoutAddCapabilitiesBothAsnField) { + // TODO(michaeluw): T113736668 deprecate this when we deprecate i32 asns field + // both field present + std::stringstream ss; + auto peer = establishedNeighborSession_.at(0).peer(); + peer->local_as() = 1111; + peer->remote_as() = 2222; + CmdShowBgpNeighbors().printOutput(establishedNeighborSession_, ss); + std::string output = ss.str(); + + std::string expectedOutput = + "BGP neighbor is 1.2.3.4, remote AS 4294967280, Confed Peer: configured\n" + " Description: abc001.d001.e01.fgh1\n" + " BGP version 4, remote router ID 64.226.1.0\n" + " RIB version: 12345\n" + " Hold time is 15s, KeepAlive interval is 5s\n" + " BGP state is ESTABLISHED, up for 0h 14m 7s\n" + " Flapped 20 times, last flap 0h 12m 9s ago\n" + " Last reset reason: MANUAL_STOP\n" + " UCMP Link Bandwidth:\n" + " Advertise: DISABLE\n" + " Receive : ACCEPT\n" + " Value : 99Gbps\n" + " Neighbor Capabilities:\n" + " Multiprotocol IPv4 Unicast: negotiated\n" + " Graceful Restart: received, peer restart-time is 60s\n" + " Graceful Restart: sent, local restart-time is 120s\n" + " Add Path: DISABLED\n\n" + " Prefix statistics: Sent Rcvd \n" + "-------------------------------------\n" + " IPv4, IPv6 Unicast: 2 10 \n\n" + "BGP update announcements sent ipv4: 8\n" + "BGP update announcements sent ipv6: 7\n" + "BGP update withdrawals sent: 6\n" + "BGP update announcements received ipv4: 5\n" + "BGP update announcements received ipv6: 4\n" + "BGP update withdrawals received: 3\n" + "Number of enforce-first-as validation rejections: 71\n" + "Local AS is 2001, local router ID 2.3.5.6\n" + "Local TCP address is 5.6.7.8, local port is 44003\n" + "Remote TCP address is 1.2.3.4, remote port is 179\n" + "TCP connection-mode is PASSIVE_ACTIVE\n" + "Number of session terminations: 20\n" + "HoldTimer last reset at 2021-10-26 13:07:40.724 PDT\n" + "KeepAlive last received at 2021-10-26 13:07:39.716 PDT\n" + "KeepAlive last sent at 2021-10-26 13:07:40.724 PDT\n" + "EOR Sent at 2021-10-26 10:59:51.576 PDT\n" + "EOR Received at 2021-10-26 10:59:55.614 PDT\n\n"; + + EXPECT_EQ(output, expectedOutput); +} + +TEST_F( + CmdShowBgpNeighborsTestFixture, + printOutputWithoutAddCapabilitiesOldAsnField) { + // TODO(michaeluw): T113736668 deprecate this when we deprecate i32 asns field + std::stringstream ss; + + TBgpPeer peer; + peer.local_as() = 2001; + peer.remote_as() = 6000; + peer.hold_time() = 15; + peer.peer_state() = TBgpPeerState::ESTABLISHED; + peer.lastResetHoldTimer() = 1635278860724; + peer.lastSentKeepAlive() = 1635278859716; + peer.lastRcvdKeepAlive() = 1635278860724; + + establishedNeighborSession_.at(0).peer() = peer; + + CmdShowBgpNeighbors().printOutput(establishedNeighborSession_, ss); + std::string output = ss.str(); + + std::string expectedOutput = + "BGP neighbor is 1.2.3.4, remote AS 6000, Confed Peer: configured\n" + " Description: abc001.d001.e01.fgh1\n" + " BGP version 4, remote router ID 64.226.1.0\n" + " RIB version: 12345\n" + " Hold time is 15s, KeepAlive interval is 5s\n" + " BGP state is ESTABLISHED, up for 0h 14m 7s\n" + " Flapped 20 times, last flap 0h 12m 9s ago\n" + " Last reset reason: MANUAL_STOP\n" + " UCMP Link Bandwidth:\n" + " Advertise: DISABLE\n" + " Receive : ACCEPT\n" + " Value : 99Gbps\n" + " Neighbor Capabilities:\n" + " Multiprotocol IPv4 Unicast: negotiated\n" + " Graceful Restart: received, peer restart-time is 60s\n" + " Graceful Restart: sent, local restart-time is 120s\n" + " Add Path: DISABLED\n\n" + " Prefix statistics: Sent Rcvd \n" + "-------------------------------------\n" + " IPv4, IPv6 Unicast: 2 10 \n\n" + "BGP update announcements sent ipv4: 8\n" + "BGP update announcements sent ipv6: 7\n" + "BGP update withdrawals sent: 6\n" + "BGP update announcements received ipv4: 5\n" + "BGP update announcements received ipv6: 4\n" + "BGP update withdrawals received: 3\n" + "Number of enforce-first-as validation rejections: 71\n" + "Local AS is 2001, local router ID 2.3.5.6\n" + "Local TCP address is 5.6.7.8, local port is 44003\n" + "Remote TCP address is 1.2.3.4, remote port is 179\n" + "TCP connection-mode is PASSIVE_ACTIVE\n" + "Number of session terminations: 20\n" + "HoldTimer last reset at 2021-10-26 13:07:40.724 PDT\n" + "KeepAlive last received at 2021-10-26 13:07:39.716 PDT\n" + "KeepAlive last sent at 2021-10-26 13:07:40.724 PDT\n" + "EOR Sent at 2021-10-26 10:59:51.576 PDT\n" + "EOR Received at 2021-10-26 10:59:55.614 PDT\n\n"; + + EXPECT_EQ(output, expectedOutput); +} + +TEST_F(CmdShowBgpNeighborsTestFixture, printOutputWithAddCapabilities) { + TBgpAddPathNegotiated pathNegotiations; + pathNegotiations.afi() = TBgpAfi::AFI_IPV4; + pathNegotiations.add_path() = AddPath::BOTH; + + auto& session = establishedNeighborSession_.at(0); + session.details()->add_path_capabilities() = {pathNegotiations}; + session.peer()->add_path() = AddPath::BOTH; + + std::stringstream ss; + CmdShowBgpNeighbors().printOutput(establishedNeighborSession_, ss); + std::string output = ss.str(); + + std::string expectedOutput = + "BGP neighbor is 1.2.3.4, remote AS 4294967280, Confed Peer: configured\n" + " Description: abc001.d001.e01.fgh1\n" + " BGP version 4, remote router ID 64.226.1.0\n" + " RIB version: 12345\n" + " Hold time is 15s, KeepAlive interval is 5s\n" + " BGP state is ESTABLISHED, up for 0h 14m 7s\n" + " Flapped 20 times, last flap 0h 12m 9s ago\n" + " Last reset reason: MANUAL_STOP\n" + " UCMP Link Bandwidth:\n" + " Advertise: DISABLE\n" + " Receive : ACCEPT\n" + " Value : 99Gbps\n" + " Neighbor Capabilities:\n" + " Multiprotocol IPv4 Unicast: negotiated\n" + " Graceful Restart: received, peer restart-time is 60s\n" + " Graceful Restart: sent, local restart-time is 120s\n" + " Add Path: Configured - BOTH, Negotiated - AFI_IPV4 BOTH\n\n" + " Prefix statistics: Sent Rcvd \n" + "-------------------------------------\n" + " IPv4, IPv6 Unicast: 2 10 \n\n" + "BGP update announcements sent ipv4: 8\n" + "BGP update announcements sent ipv6: 7\n" + "BGP update withdrawals sent: 6\n" + "BGP update announcements received ipv4: 5\n" + "BGP update announcements received ipv6: 4\n" + "BGP update withdrawals received: 3\n" + "Number of enforce-first-as validation rejections: 71\n" + "Local AS is 2001, local router ID 2.3.5.6\n" + "Local TCP address is 5.6.7.8, local port is 44003\n" + "Remote TCP address is 1.2.3.4, remote port is 179\n" + "TCP connection-mode is PASSIVE_ACTIVE\n" + "Number of session terminations: 20\n" + "HoldTimer last reset at 2021-10-26 13:07:40.724 PDT\n" + "KeepAlive last received at 2021-10-26 13:07:39.716 PDT\n" + "KeepAlive last sent at 2021-10-26 13:07:40.724 PDT\n" + "EOR Sent at 2021-10-26 10:59:51.576 PDT\n" + "EOR Received at 2021-10-26 10:59:55.614 PDT\n\n"; + + EXPECT_EQ(output, expectedOutput); +} + +TEST_F( + CmdShowBgpNeighborsTestFixture, + printOutputIdleNeighborWithAddCapabilities) { + std::stringstream ss; + CmdShowBgpNeighbors().printOutput(idleNeighborSession_, ss); + std::string output = ss.str(); + + std::string expectedOutput = + "BGP neighbor is 101.102.103.104, remote AS 4008636128, Confed Peer: configured\n" + " Description: def001.k001.e01.fgh1\n" + " BGP version 4, remote router ID 127.115.66.60\n" + " Hold time is 15s, KeepAlive interval is 5s\n" + " BGP state is IDLE\n" + " Flapped 20 times, last went down 0h 12m 9s ago\n" + " Last reset reason: MANUAL_STOP\n" + " UCMP Link Bandwidth:\n" + " Advertise: DISABLE\n" + " Receive : ACCEPT\n" + " Value : 99Gbps\n" + " Neighbor Capabilities:\n" + " Multiprotocol IPv4 Unicast: negotiated\n" + " Graceful Restart: received, peer restart-time is 60s\n" + " Graceful Restart: sent, local restart-time is 120s\n" + " Add Path: DISABLED\n\n" + " Prefix statistics: Sent Rcvd \n" + "-------------------------------------\n" + " IPv4, IPv6 Unicast: 2 10 \n\n" + "BGP update announcements sent ipv4: 8\n" + "BGP update announcements sent ipv6: 7\n" + "BGP update withdrawals sent: 6\n" + "BGP update announcements received ipv4: 5\n" + "BGP update announcements received ipv6: 4\n" + "BGP update withdrawals received: 3\n" + "Number of enforce-first-as validation rejections: 71\n" + "Local AS is 2001, local router ID 2.3.5.6\n" + "Local TCP address is 5.6.7.8, local port is 44003\n" + "Remote TCP address is 101.102.103.104, remote port is 179\n" + "TCP connection-mode is PASSIVE_ACTIVE\n" + "Number of session terminations: 20\n" + "HoldTimer last reset at 2021-10-26 13:07:40.724 PDT\n" + "KeepAlive last received at 2021-10-26 13:07:39.716 PDT\n" + "KeepAlive last sent at 2021-10-26 13:07:40.724 PDT\n\n"; + + EXPECT_EQ(output, expectedOutput); +} + +TEST_F( + CmdShowBgpNeighborsTestFixture, + printOutputActiveNeighborWithAddCapabilities) { + std::stringstream ss; + CmdShowBgpNeighbors().printOutput(activeNeighborSession_, ss); + std::string output = ss.str(); + + std::string expectedOutput = + "BGP neighbor is 105.106.107.108, remote AS 4008636129, Confed Peer: configured\n" + " Description: ghi001.j001.k01.ll1\n" + " BGP version 4, remote router ID 128.115.66.60\n" + " Hold time is 15s, KeepAlive interval is 5s\n" + " BGP state is ACTIVE\n" + " The TCP socket has been up for 0h 0m 2s.\n" + " Flapped 10 times, last went down 0h 0m 50s ago\n" + " Last reset reason: MANUAL_STOP\n" + " UCMP Link Bandwidth:\n" + " Advertise: DISABLE\n" + " Receive : ACCEPT\n" + " Value : 99Gbps\n" + " Neighbor Capabilities:\n" + " Multiprotocol IPv4 Unicast: negotiated\n" + " Graceful Restart: received, peer restart-time is 60s\n" + " Graceful Restart: sent, local restart-time is 120s\n" + " Add Path: DISABLED\n\n" + " Prefix statistics: Sent Rcvd \n" + "-------------------------------------\n" + " IPv4, IPv6 Unicast: 2 10 \n\n" + "BGP update announcements sent ipv4: 8\n" + "BGP update announcements sent ipv6: 7\n" + "BGP update withdrawals sent: 6\n" + "BGP update announcements received ipv4: 5\n" + "BGP update announcements received ipv6: 4\n" + "BGP update withdrawals received: 3\n" + "Number of enforce-first-as validation rejections: 71\n" + "Local AS is 2001, local router ID 2.3.5.6\n" + "Local TCP address is 5.6.7.8, local port is 44003\n" + "Remote TCP address is 105.106.107.108, remote port is 179\n" + "TCP connection-mode is PASSIVE_ACTIVE\n" + "Number of session terminations: 20\n" + "HoldTimer last reset at 2021-10-26 13:07:40.724 PDT\n" + "KeepAlive last received at 2021-10-26 13:07:39.716 PDT\n" + "KeepAlive last sent at 2021-10-26 13:07:40.724 PDT\n\n"; + + EXPECT_EQ(output, expectedOutput); +} + +TEST_F(CmdShowBgpNeighborsTestFixture, printAllNeighbors) { + std::stringstream ss; + CmdShowBgpNeighbors().printOutput(allNeighborSessions_, ss); + std::string output = ss.str(); + + std::string expectedOutput = + "------------------------------------------------------------------\n" + "neighbor 1 of 4\n" + "BGP neighbor is 1.2.3.4, remote AS 4294967280, Confed Peer: configured\n" + " Description: abc001.d001.e01.fgh1\n" + " BGP version 4, remote router ID 64.226.1.0\n" + " RIB version: 12345\n" + " Hold time is 15s, KeepAlive interval is 5s\n" + " BGP state is ESTABLISHED, up for 0h 14m 7s\n" + " Flapped 20 times, last flap 0h 12m 9s ago\n" + " Last reset reason: MANUAL_STOP\n" + " UCMP Link Bandwidth:\n" + " Advertise: DISABLE\n" + " Receive : ACCEPT\n" + " Value : 99Gbps\n" + " Neighbor Capabilities:\n" + " Multiprotocol IPv4 Unicast: negotiated\n" + " Graceful Restart: received, peer restart-time is 60s\n" + " Graceful Restart: sent, local restart-time is 120s\n" + " Add Path: DISABLED\n\n" + " Prefix statistics: Sent Rcvd \n" + "-------------------------------------\n" + " IPv4, IPv6 Unicast: 2 10 \n\n" + "BGP update announcements sent ipv4: 8\n" + "BGP update announcements sent ipv6: 7\n" + "BGP update withdrawals sent: 6\n" + "BGP update announcements received ipv4: 5\n" + "BGP update announcements received ipv6: 4\n" + "BGP update withdrawals received: 3\n" + "Number of enforce-first-as validation rejections: 71\n" + "Local AS is 2001, local router ID 2.3.5.6\n" + "Local TCP address is 5.6.7.8, local port is 44003\n" + "Remote TCP address is 1.2.3.4, remote port is 179\n" + "TCP connection-mode is PASSIVE_ACTIVE\n" + "Number of session terminations: 20\n" + "HoldTimer last reset at 2021-10-26 13:07:40.724 PDT\n" + "KeepAlive last received at 2021-10-26 13:07:39.716 PDT\n" + "KeepAlive last sent at 2021-10-26 13:07:40.724 PDT\n" + "EOR Sent at 2021-10-26 10:59:51.576 PDT\n" + "EOR Received at 2021-10-26 10:59:55.614 PDT\n" + "------------------------------------------------------------------\n" + "\n" + "------------------------------------------------------------------\n" + "neighbor 2 of 4\n" + "BGP neighbor is 1.2.3.4, remote AS 4294967280, Confed Peer: configured\n" + " Description: abc001.d001.e01.fgh1\n" + " BGP version 4, remote router ID 255.201.154.59\n" + " RIB version: 12345\n" + " Hold time is 15s, KeepAlive interval is 5s\n" + " BGP state is ESTABLISHED, up for 0h 14m 7s\n" + " Flapped 20 times, last flap 0h 12m 9s ago\n" + " Last reset reason: MANUAL_STOP\n" + " UCMP Link Bandwidth:\n" + " Advertise: DISABLE\n" + " Receive : ACCEPT\n" + " Value : 99Gbps\n" + " Neighbor Capabilities:\n" + " Multiprotocol IPv4 Unicast: negotiated\n" + " Graceful Restart: received, peer restart-time is 60s\n" + " Graceful Restart: sent, local restart-time is 120s\n" + " Add Path: DISABLED\n\n" + " Prefix statistics: Sent Rcvd \n" + "-------------------------------------\n" + " IPv4, IPv6 Unicast: 2 10 \n\n" + "BGP update announcements sent ipv4: 8\n" + "BGP update announcements sent ipv6: 7\n" + "BGP update withdrawals sent: 6\n" + "BGP update announcements received ipv4: 5\n" + "BGP update announcements received ipv6: 4\n" + "BGP update withdrawals received: 3\n" + "Number of enforce-first-as validation rejections: 71\n" + "Local AS is 2001, local router ID 2.3.5.6\n" + "Local TCP address is 5.6.7.8, local port is 44003\n" + "Remote TCP address is 1.2.3.4, remote port is 179\n" + "TCP connection-mode is PASSIVE_ACTIVE\n" + "Number of session terminations: 20\n" + "HoldTimer last reset at 2021-10-26 13:07:40.724 PDT\n" + "KeepAlive last received at 2021-10-26 13:07:39.716 PDT\n" + "KeepAlive last sent at 2021-10-26 13:07:40.724 PDT\n" + "EOR Sent at 2021-10-26 10:59:51.576 PDT\n" + "EOR Received at 2021-10-26 10:59:55.614 PDT\n" + "------------------------------------------------------------------\n" + "\n" + "------------------------------------------------------------------\n" + "neighbor 3 of 4\n" + "BGP neighbor is 101.102.103.104, remote AS 4008636128, Confed Peer: configured\n" + " Description: def001.k001.e01.fgh1\n" + " BGP version 4, remote router ID 127.115.66.60\n" + " Hold time is 15s, KeepAlive interval is 5s\n" + " BGP state is IDLE\n" + " Flapped 20 times, last went down 0h 12m 9s ago\n" + " Last reset reason: MANUAL_STOP\n" + " UCMP Link Bandwidth:\n" + " Advertise: DISABLE\n" + " Receive : ACCEPT\n" + " Value : 99Gbps\n" + " Neighbor Capabilities:\n" + " Multiprotocol IPv4 Unicast: negotiated\n" + " Graceful Restart: received, peer restart-time is 60s\n" + " Graceful Restart: sent, local restart-time is 120s\n" + " Add Path: DISABLED\n\n" + " Prefix statistics: Sent Rcvd \n" + "-------------------------------------\n" + " IPv4, IPv6 Unicast: 2 10 \n\n" + "BGP update announcements sent ipv4: 8\n" + "BGP update announcements sent ipv6: 7\n" + "BGP update withdrawals sent: 6\n" + "BGP update announcements received ipv4: 5\n" + "BGP update announcements received ipv6: 4\n" + "BGP update withdrawals received: 3\n" + "Number of enforce-first-as validation rejections: 71\n" + "Local AS is 2001, local router ID 2.3.5.6\n" + "Local TCP address is 5.6.7.8, local port is 44003\n" + "Remote TCP address is 101.102.103.104, remote port is 179\n" + "TCP connection-mode is PASSIVE_ACTIVE\n" + "Number of session terminations: 20\n" + "HoldTimer last reset at 2021-10-26 13:07:40.724 PDT\n" + "KeepAlive last received at 2021-10-26 13:07:39.716 PDT\n" + "KeepAlive last sent at 2021-10-26 13:07:40.724 PDT\n" + "------------------------------------------------------------------\n" + "\n" + "------------------------------------------------------------------\n" + "neighbor 4 of 4\n" + "BGP neighbor is 105.106.107.108, remote AS 4008636129, Confed Peer: configured\n" + " Description: ghi001.j001.k01.ll1\n" + " BGP version 4, remote router ID 128.115.66.60\n" + " Hold time is 15s, KeepAlive interval is 5s\n" + " BGP state is ACTIVE\n" + " The TCP socket has been up for 0h 0m 2s.\n" + " Flapped 10 times, last went down 0h 0m 50s ago\n" + " Last reset reason: MANUAL_STOP\n" + " UCMP Link Bandwidth:\n" + " Advertise: DISABLE\n" + " Receive : ACCEPT\n" + " Value : 99Gbps\n" + " Neighbor Capabilities:\n" + " Multiprotocol IPv4 Unicast: negotiated\n" + " Graceful Restart: received, peer restart-time is 60s\n" + " Graceful Restart: sent, local restart-time is 120s\n" + " Add Path: DISABLED\n\n" + " Prefix statistics: Sent Rcvd \n" + "-------------------------------------\n" + " IPv4, IPv6 Unicast: 2 10 \n\n" + "BGP update announcements sent ipv4: 8\n" + "BGP update announcements sent ipv6: 7\n" + "BGP update withdrawals sent: 6\n" + "BGP update announcements received ipv4: 5\n" + "BGP update announcements received ipv6: 4\n" + "BGP update withdrawals received: 3\n" + "Number of enforce-first-as validation rejections: 71\n" + "Local AS is 2001, local router ID 2.3.5.6\n" + "Local TCP address is 5.6.7.8, local port is 44003\n" + "Remote TCP address is 105.106.107.108, remote port is 179\n" + "TCP connection-mode is PASSIVE_ACTIVE\n" + "Number of session terminations: 20\n" + "HoldTimer last reset at 2021-10-26 13:07:40.724 PDT\n" + "KeepAlive last received at 2021-10-26 13:07:39.716 PDT\n" + "KeepAlive last sent at 2021-10-26 13:07:40.724 PDT\n" + "------------------------------------------------------------------\n" + "\n"; + EXPECT_EQ(output, expectedOutput); +} +} // namespace facebook::fboss diff --git a/fboss/cli/fboss2/test/CmdShowBgpOriginatedRoutesTest.cpp b/fboss/cli/fboss2/test/CmdShowBgpOriginatedRoutesTest.cpp new file mode 100644 index 0000000000000..5755dd0f2a1e9 --- /dev/null +++ b/fboss/cli/fboss2/test/CmdShowBgpOriginatedRoutesTest.cpp @@ -0,0 +1,122 @@ +/* + * 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 +#include +#include +#include +#include +#include "fboss/cli/fboss2/test/CmdHandlerTestBase.h" + +#include "common/network/if/gen-cpp2/Address_types.h" +#include "configerator/structs/neteng/fboss/bgp/if/gen-cpp2/bgp_attr_types.h" +#include "fboss/agent/AddressUtil.h" +#include "fboss/cli/fboss2/commands/show/bgp/CmdShowBgpOriginatedRoutes.h" +#include "folly/IPAddress.h" +#include "folly/Range.h" +#include "folly/json/json.h" +#include "neteng/fboss/bgp/if/gen-cpp2/bgp_thrift_types.h" + +using namespace ::testing; +using facebook::neteng::fboss::bgp::thrift::TOriginatedRoute; +using facebook::neteng::fboss::bgp_attr::TBgpAfi; +using facebook::neteng::fboss::bgp_attr::TBgpCommunity; +using facebook::neteng::fboss::bgp_attr::TIpPrefix; +namespace facebook::fboss { + +const auto kIpVersion = TBgpAfi::AFI_IPV4; +const auto kBinaryAddress = + facebook::network::toBinaryAddress(folly::IPAddress("8.0.0.0")); +const auto kAddressMask = 32; +const auto kCommunityNumber = 4294390177; +const auto kSupportingRoutes = 0; + +class CmdShowBgpOriginatedRoutesTestFixture : public CmdHandlerTestBase { + public: + std::vector routes_; + void SetUp() override { + CmdHandlerTestBase::SetUp(); + routes_ = getOriginatedRoutes(); + } + + std::vector getOriginatedRoutes() { + TIpPrefix ip_prefix; + ip_prefix.afi() = kIpVersion; + ip_prefix.prefix_bin() = kBinaryAddress.addr().value().toStdString(); + ip_prefix.num_bits() = kAddressMask; + + TBgpCommunity bgp_community; + bgp_community.community() = kCommunityNumber; + + TOriginatedRoute queried_route; + queried_route.prefix() = ip_prefix; + queried_route.communities() = {bgp_community}; + queried_route.supporting_route_count() = kSupportingRoutes; + queried_route.minimum_supporting_routes() = kSupportingRoutes; + + return {queried_route}; + } +}; + +TEST_F(CmdShowBgpOriginatedRoutesTestFixture, queryClient) { + setupMockedBgpServer(); + EXPECT_CALL(getMockBgp(), getOriginatedRoutes(_)) + .WillOnce(Invoke([&](auto& entries) { entries = routes_; })); + + auto results = CmdShowBgpOriginatedRoutes().queryClient(localhost()); + const auto routes = *results.tOriginatedRoutes(); + ASSERT_EQ(routes.size(), 1); + + const auto& prefix = routes[0].prefix().value(); + EXPECT_EQ(prefix.get_afi(), kIpVersion); + EXPECT_EQ(prefix.get_prefix_bin(), kBinaryAddress.addr()->toStdString()); + EXPECT_EQ(prefix.get_num_bits(), kAddressMask); + + const auto& communites = + apache::thrift::get_pointer(routes[0].communities())[0]; + ASSERT_EQ(communites.size(), 1); + EXPECT_EQ(communites[0].get_community(), kCommunityNumber); + EXPECT_EQ(routes[0].get_supporting_route_count(), kSupportingRoutes); + EXPECT_EQ(routes[0].get_minimum_supporting_routes(), kSupportingRoutes); +} + +TEST_F(CmdShowBgpOriginatedRoutesTestFixture, printOutput) { + setupMockedBgpServer(); + EXPECT_CALL(getMockBgp(), getRunningConfig(_)) + .WillOnce(Invoke([&](std::string& config) { + // clang-format off + folly::dynamic value = folly::dynamic::object + ("communities", + folly::dynamic::array( + folly::dynamic::object("name", "FABRIC_POD_RSW_LOOP") + ("description", "rsw loopback") + ("communities", folly::dynamic::array("65527:12705")) + ) + ); + // clang-format on + config = folly::toPrettyJson(value); + })); + std::stringstream ss; + TOriginatedRouteWithHost originatedRouteWithHost; + originatedRouteWithHost.tOriginatedRoutes() = routes_; + originatedRouteWithHost.host() = localhost().getName(); + originatedRouteWithHost.oobName() = localhost().getOobName(); + originatedRouteWithHost.ip() = localhost().getIpStr(); + CmdShowBgpOriginatedRoutes().printOutput(originatedRouteWithHost, ss); + + std::string output = ss.str(); + std::string expectedOutput = + " Prefix Communities Supporting Route Cnt Minimum supporting route Require Nexthop Resolution \n" + "-------------------------------------------------------------------------------------------------------------------------------\n" + " 8.0.0.0/32 FABRIC_POD_RSW_LOOP/65527:12705 0 0 N/A \n\n"; + + EXPECT_EQ(output, expectedOutput); +} +} // namespace facebook::fboss diff --git a/fboss/cli/fboss2/test/CmdShowBgpShadowRibTest.cpp b/fboss/cli/fboss2/test/CmdShowBgpShadowRibTest.cpp new file mode 100644 index 0000000000000..892fe2be4508b --- /dev/null +++ b/fboss/cli/fboss2/test/CmdShowBgpShadowRibTest.cpp @@ -0,0 +1,222 @@ +/* + * 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 +#include +#include +#include +#include // NOLINT(misc-include-cleaner) +#include +#include "fboss/cli/fboss2/test/CmdHandlerTestBase.h" + +#include "configerator/structs/neteng/bgp_policy/thrift/gen-cpp2/bgp_policy_types.h" // NOLINT(misc-include-cleaner) +#include "fboss/cli/fboss2/commands/show/bgp/shadowrib/CmdShowBgpShadowRib.h" +#include "fboss/cli/fboss2/test/CmdBgpTestUtils.h" +#include "neteng/fboss/bgp/if/gen-cpp2/bgp_thrift_types.h" +#ifndef IS_OSS +#include "nettools/common/TestUtils.h" +#endif + +using facebook::neteng::fboss::bgp::thrift::TRibEntry; +using facebook::neteng::fboss::bgp::thrift::TRibEntryWithHost; +using facebook::neteng::fboss::bgp_attr::TBgpAfi; +using ::testing::_; +using ::testing::Invoke; + +namespace facebook::fboss { +class CmdShowBgpShadowRibTestFixture : public CmdHandlerTestBase { + public: + std::vector entriesIPv4_; + std::vector entriesIPv6_; + std::vector combinedEntries_; + + void SetUp() override { + CmdHandlerTestBase::SetUp(); + entriesIPv4_ = {buildEntry()}; + entriesIPv6_ = { + buildEntry("2001::1/64", "2001::2", "2001::3", "two00one::three")}; + combineEntries(); + } + + void combineEntries() { + this->combinedEntries_ = this->entriesIPv4_; + this->combinedEntries_.insert( + combinedEntries_.end(), entriesIPv6_.begin(), entriesIPv6_.end()); + } +}; + +TEST_F(CmdShowBgpShadowRibTestFixture, queryClient) { + setupMockedBgpServer(); + EXPECT_CALL(getMockBgp(), getShadowRibEntries(_, TBgpAfi::AFI_IPV4)) + .WillOnce(Invoke([&](std::vector& entries, TBgpAfi) { + entries = entriesIPv4_; + })); + + EXPECT_CALL(getMockBgp(), getShadowRibEntries(_, TBgpAfi::AFI_IPV6)) + .WillOnce(Invoke([&](std::vector& entries, TBgpAfi) { + entries = entriesIPv6_; + })); + auto result = CmdShowBgpShadowRib().queryClient(localhost()); + EXPECT_THRIFT_EQ_VECTOR(*result.tRibEntries(), combinedEntries_); +} + +TEST_F(CmdShowBgpShadowRibTestFixture, printOutput) { + combinedEntries_.emplace_back( + buildEntry("8.0.0.1/32", "8.0.0.2", "8.1.2.8", "eight.one.two.eight")); + combinedEntries_.emplace_back( + buildEntry("2001::3/64", "2001::4", "2001::5", "two00one::five")); + + setupMockedBgpServer(); + EXPECT_CALL(getMockBgp(), getRunningConfig(_)) + .WillRepeatedly(Invoke([&](std::string& config) { + // clang-format off + folly::dynamic value = folly::dynamic::object + ("communities", + folly::dynamic::array( + folly::dynamic::object("name", "FABRIC_POD_RSW_LOOP") + ("description", "rsw loopback") + ("communities", folly::dynamic::array("65527:12705")) + ) + ) + ("localprefs", + folly::dynamic::array( + folly::dynamic::object("localpref", 20) + ("name", "LOCALPREF_CTRL_BACKUP") + ("description", "low-priority supplementary/backup routes from bgp controller"), + folly::dynamic::object("localpref", 25) + ("name", "LOCALPREF_DEPRIO") + ("description", "deprioritized local preference value")) + ); + // clang-format on + config = folly::toPrettyJson(value); + })); + std::stringstream ss; + TRibEntryWithHost tRibEntryWithHost; + tRibEntryWithHost.tRibEntries() = combinedEntries_; + tRibEntryWithHost.host() = localhost().getName(); + tRibEntryWithHost.oobName() = localhost().getOobName(); + tRibEntryWithHost.ip() = localhost().getIpStr(); + CmdShowBgpShadowRib().printOutput(tRibEntryWithHost, ss); + std::string output = ss.str(); + + std::string expectedOutput = kRibEntryMarkersHeader + + "\n> 8.0.0.0/32, Selected 1/1 paths\n" + "*@ from 1.2.3.4 (one.two.three.four) via 8.0.0.1 | LBW: None | Origin: INCOMPLETE | LP: DEPRIO/25 | ASP: 65301 | LM: # | NH Weight: N/A | MED: 10 | ID: 5 (rcvd) 6 (sent) | Weight: 20 | IgpCost: 100\n" + "\n> 8.0.0.1/32, Selected 1/1 paths\n" + "*@ from 8.1.2.8 (eight.one.two.eight) via 8.0.0.2 | LBW: None | Origin: INCOMPLETE | LP: DEPRIO/25 | ASP: 65301 | LM: # | NH Weight: N/A | MED: 10 | ID: 5 (rcvd) 6 (sent) | Weight: 20 | IgpCost: 100\n" + "\n> 2001::1/64, Selected 1/1 paths\n" + "*@ from 2001::3 (two00one::three) via 2001::2 | LBW: None | Origin: INCOMPLETE | LP: DEPRIO/25 | ASP: 65301 | LM: # | NH Weight: N/A | MED: 10 | ID: 5 (rcvd) 6 (sent) | Weight: 20 | IgpCost: 100\n" + "\n> 2001::3/64, Selected 1/1 paths\n" + "*@ from 2001::5 (two00one::five) via 2001::4 | LBW: None | Origin: INCOMPLETE | LP: DEPRIO/25 | ASP: 65301 | LM: # | NH Weight: N/A | MED: 10 | ID: 5 (rcvd) 6 (sent) | Weight: 20 | IgpCost: 100\n"; + + maskDateInOutput(output); + EXPECT_EQ(output, expectedOutput); +} + +#ifndef IS_OSS +// Test the CPS annotation (Meta-internal feature) +TEST_F(CmdShowBgpShadowRibTestFixture, printCPSOutput) { + // PathSelector contains the info for the active criteria + bgp::rib_policy::TPathSelector activeCriteria1; + activeCriteria1.bgp_native_path_selection_min_nexthop() = 20; + auto pathOverriddenEntry1 = buildEntry( + "8.0.0.1/32", + "8.0.0.2", + "8.1.2.8", + "eight.one.two.eight", + std::nullopt, + true /* omit best path */, + true /* omit best paths */); + pathOverriddenEntry1.active_cps_criteria() = std::move(activeCriteria1); + combinedEntries_.push_back(std::move(pathOverriddenEntry1)); + auto pathOverriddenEntry3 = buildEntry( + "8.0.0.3/32", + "8.0.0.2", + "8.1.2.8", + "eight.one.two.eight", + std::nullopt, + true /* omit best path */); + bgp::rib_policy::TPathSelector activeCriteria3; + activeCriteria3.bgp_native_path_selection_min_nexthop() = 20; + activeCriteria3.relax_bgp_native_path_selection_min_nexthop() = true; + pathOverriddenEntry3.active_cps_criteria() = std::move(activeCriteria3); + combinedEntries_.push_back(std::move(pathOverriddenEntry3)); + + bgp::rib_policy::TPathSelector activeCriteria2; + bgp::rib_policy::TPathSelectionCriteria tCriteria; + tCriteria.min_nexthop() = 13; + activeCriteria2.criteria_list()->push_back(std::move(tCriteria)); + auto pathOverriddenEntry2 = + buildEntry("2001::3/64", "2001::4", "2001::5", "two00one::five"); + pathOverriddenEntry2.active_cps_criteria() = std::move(activeCriteria2); + combinedEntries_.push_back(std::move(pathOverriddenEntry2)); + setupMockedBgpServer(); + EXPECT_CALL(getMockBgp(), getRunningConfig(_)) + .WillRepeatedly(Invoke([&](std::string& config) { + // clang-format off + folly::dynamic value = folly::dynamic::object + ("communities", + folly::dynamic::array( + folly::dynamic::object("name", "FABRIC_POD_RSW_LOOP") + ("description", "rsw loopback") + ("communities", folly::dynamic::array("65527:12705")) + ) + ) + ("localprefs", + folly::dynamic::array( + folly::dynamic::object("localpref", 20) + ("name", "LOCALPREF_CTRL_BACKUP") + ("description", "low-priority supplementary/backup routes from bgp controller"), + folly::dynamic::object("localpref", 25) + ("name", "LOCALPREF_DEPRIO") + ("description", "deprioritized local preference value")) + ); + // clang-format on + config = folly::toPrettyJson(value); + })); + std::stringstream ss; + TRibEntryWithHost tRibEntryWithHost; + tRibEntryWithHost.tRibEntries() = combinedEntries_; + tRibEntryWithHost.host() = localhost().getName(); + tRibEntryWithHost.oobName() = localhost().getOobName(); + tRibEntryWithHost.ip() = localhost().getIpStr(); + CmdShowBgpShadowRib().printOutput(tRibEntryWithHost, ss); + std::string output = ss.str(); + + std::string expectedOutput = kRibEntryMarkersHeader + + "\n> 8.0.0.0/32, Selected 1/1 paths\n" + "*@ from 1.2.3.4 (one.two.three.four) via 8.0.0.1 | LBW: None | Origin: INCOMPLETE | LP: DEPRIO/25 | ASP: 65301 | LM: # | NH Weight: N/A | MED: 10 | ID: 5 (rcvd) 6 (sent) | Weight: 20 | IgpCost: 100\n" + "\n> 8.0.0.1/32, Selected 0/1 paths\n" + "Path overridden by CPS:\n" + " prefix: 8.0.0.1/32\n" + " default BGP multipath selector\n" + " BGP native min nexthop: 20\n" + " from 8.1.2.8 (eight.one.two.eight) via 8.0.0.2 | LBW: None | Origin: INCOMPLETE | LP: DEPRIO/25 | ASP: 65301 | LM: # | NH Weight: N/A | MED: 10 | ID: 5 (rcvd) 6 (sent) | Weight: 20 | IgpCost: 100\n" + "\n> 8.0.0.3/32, Selected 1/1 paths\n" + "Path overridden by CPS:\n" + " prefix: 8.0.0.3/32\n" + " default BGP multipath selector\n" + " BGP native min nexthop: 20, relaxed: true\n" + "* from 8.1.2.8 (eight.one.two.eight) via 8.0.0.2 | LBW: None | Origin: INCOMPLETE | LP: DEPRIO/25 | ASP: 65301 | LM: # | NH Weight: N/A | MED: 10 | ID: 5 (rcvd) 6 (sent) | Weight: 20 | IgpCost: 100\n" + "\n> 2001::1/64, Selected 1/1 paths\n" + "*@ from 2001::3 (two00one::three) via 2001::2 | LBW: None | Origin: INCOMPLETE | LP: DEPRIO/25 | ASP: 65301 | LM: # | NH Weight: N/A | MED: 10 | ID: 5 (rcvd) 6 (sent) | Weight: 20 | IgpCost: 100\n" + "\n> 2001::3/64, Selected 1/1 paths\n" + "Path overridden by CPS:\n" + " prefix: 2001::3/64\n" + " active criteria:\n" + " path_matchers:\n" + " min nexthop: 13\n" + "*@ from 2001::5 (two00one::five) via 2001::4 | LBW: None | Origin: INCOMPLETE | LP: DEPRIO/25 | ASP: 65301 | LM: # | NH Weight: N/A | MED: 10 | ID: 5 (rcvd) 6 (sent) | Weight: 20 | IgpCost: 100\n"; + + maskDateInOutput(output); + EXPECT_EQ(output, expectedOutput); +} +#endif // IS_OSS +} // namespace facebook::fboss diff --git a/fboss/cli/fboss2/test/CmdShowBgpStatsAttrsTest.cpp b/fboss/cli/fboss2/test/CmdShowBgpStatsAttrsTest.cpp new file mode 100644 index 0000000000000..9ea25fc622488 --- /dev/null +++ b/fboss/cli/fboss2/test/CmdShowBgpStatsAttrsTest.cpp @@ -0,0 +1,90 @@ +/* + * 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 +#include +#include "fboss/cli/fboss2/test/CmdHandlerTestBase.h" + +#include "fboss/cli/fboss2/commands/show/bgp/stats/CmdShowBgpStatsAttrs.h" +#include "neteng/fboss/bgp/if/gen-cpp2/bgp_thrift_types.h" + +using namespace ::testing; +using facebook::neteng::fboss::bgp::thrift::TAttributeStats; +namespace facebook::fboss { + +const int kTotalNumberOfAttributes = 1; +const int KTotalUniqueAttributes = 2; +const float kAvgAttrRefCount = 1.3578; +const float kAvgCommunityListLen = 0.0; +const float kAvgExtCommunityListLen = 3.4567; +const float kAvgASPathLen = 1.064; +const float kAvgClusterListLen = 3.1415; +const float kAvgTopologyInfoLen = 1.618; + +class CmdShowBgpStatsAttrsTestFixture : public CmdHandlerTestBase { + public: + TAttributeStats stats_; + + void SetUp() override { + CmdHandlerTestBase::SetUp(); + stats_ = getStats(); + } + + TAttributeStats getStats() { + TAttributeStats queriedStats; + + queriedStats.total_num_of_attributes() = kTotalNumberOfAttributes; + queriedStats.total_unique_attributes() = KTotalUniqueAttributes; + queriedStats.avg_attribute_refcount() = kAvgAttrRefCount; + queriedStats.avg_community_list_len() = kAvgCommunityListLen; + queriedStats.avg_extcommunity_list_len() = kAvgExtCommunityListLen; + queriedStats.avg_as_path_len() = kAvgASPathLen; + queriedStats.avg_cluster_list_len() = kAvgClusterListLen; + queriedStats.avg_topology_info_len() = kAvgTopologyInfoLen; + + return queriedStats; + } +}; + +TEST_F(CmdShowBgpStatsAttrsTestFixture, queryClient) { + setupMockedBgpServer(); + EXPECT_CALL(getMockBgp(), getAttributeStats(_)) + .WillOnce(Invoke([&](auto& entries) { entries = stats_; })); + + auto results = CmdShowBgpStatsAttrs().queryClient(localhost()); + EXPECT_EQ(kTotalNumberOfAttributes, results.get_total_num_of_attributes()); + EXPECT_EQ(KTotalUniqueAttributes, results.get_total_unique_attributes()); + EXPECT_EQ(kAvgAttrRefCount, results.get_avg_attribute_refcount()); + EXPECT_EQ(kAvgCommunityListLen, results.get_avg_community_list_len()); + EXPECT_EQ(kAvgExtCommunityListLen, results.get_avg_extcommunity_list_len()); + EXPECT_EQ(kAvgASPathLen, results.get_avg_as_path_len()); + EXPECT_EQ(kAvgClusterListLen, results.avg_cluster_list_len()); + EXPECT_EQ(kAvgTopologyInfoLen, results.avg_topology_info_len()); +} + +TEST_F(CmdShowBgpStatsAttrsTestFixture, printOutput) { + std::stringstream ss; + CmdShowBgpStatsAttrs().printOutput(stats_, ss); + std::string output = ss.str(); + + std::string expectedOutput = + "BGP attribute statistics:\n" + " Total number of attributes: 1\n" + " Total number of unique attributes: 2\n" + " Average attribute reference count: 1.36\n" + " Average community list length: 0.00\n" + " Average extended community list length: 3.46\n" + " Average as path length: 1.06\n" + " Average cluster list length: 3.14\n" + " Average topology info length: 1.62\n"; + + EXPECT_EQ(expectedOutput, output); +} +} // namespace facebook::fboss diff --git a/fboss/cli/fboss2/test/CmdShowBgpStatsEntriesTest.cpp b/fboss/cli/fboss2/test/CmdShowBgpStatsEntriesTest.cpp new file mode 100644 index 0000000000000..1d9136f232417 --- /dev/null +++ b/fboss/cli/fboss2/test/CmdShowBgpStatsEntriesTest.cpp @@ -0,0 +1,83 @@ +/* + * 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 +#include +#include "fboss/cli/fboss2/test/CmdHandlerTestBase.h" + +#include "fboss/cli/fboss2/commands/show/bgp/stats/CmdShowBgpStatsEntries.h" +#include "neteng/fboss/bgp/if/gen-cpp2/bgp_thrift_types.h" + +using namespace ::testing; +using facebook::neteng::fboss::bgp::thrift::TEntryStats; +namespace facebook::fboss { + +const int kTotalUcastRoutes = 30; +const int kTotalRibPaths = 20; +const int kTotalAdjRibs = 15; +const int kTotalOriginatedRoutes = 2; +const int kTotalShadowRibEntries = 10; +const int kTotalNetlinkInterfaces = 32; + +class CmdShowBgpStatsEntriesTestFixture : public CmdHandlerTestBase { + public: + TEntryStats stats_; + + void SetUp() override { + CmdHandlerTestBase::SetUp(); + stats_ = getStats(); + } + + TEntryStats getStats() { + TEntryStats queriedStats; + + queriedStats.total_ucast_routes() = kTotalUcastRoutes; + queriedStats.total_rib_paths() = kTotalRibPaths; + queriedStats.total_adj_ribs() = kTotalAdjRibs; + queriedStats.total_originated_routes() = kTotalOriginatedRoutes; + queriedStats.total_shadow_rib_entries() = kTotalShadowRibEntries; + queriedStats.total_netlink_wrapper_interfaces() = kTotalNetlinkInterfaces; + + return queriedStats; + } +}; + +TEST_F(CmdShowBgpStatsEntriesTestFixture, queryClient) { + setupMockedBgpServer(); + EXPECT_CALL(getMockBgp(), getEntryStats(_)) + .WillOnce(Invoke([&](auto& entries) { entries = stats_; })); + + auto results = CmdShowBgpStatsEntries().queryClient(localhost()); + EXPECT_EQ(kTotalUcastRoutes, results.total_ucast_routes()); + EXPECT_EQ(kTotalRibPaths, results.total_rib_paths()); + EXPECT_EQ(kTotalAdjRibs, results.total_adj_ribs()); + EXPECT_EQ(kTotalOriginatedRoutes, results.total_originated_routes()); + EXPECT_EQ(kTotalShadowRibEntries, results.total_shadow_rib_entries()); + EXPECT_EQ( + kTotalNetlinkInterfaces, results.total_netlink_wrapper_interfaces()); +} + +TEST_F(CmdShowBgpStatsEntriesTestFixture, printOutput) { + std::stringstream ss; + CmdShowBgpStatsEntries().printOutput(stats_, ss); + std::string output = ss.str(); + + std::string expectedOutput = + "BGP entry statistics:\n" + " Total number of unicast routes: 30\n" + " Total number of rib paths: 20\n" + " Total number of adjribs: 15\n" + " Total number of originated routes: 2\n" + " Total number of shadow rib entries: 10\n" + " Total number of tracked netlink wrapper interfaces: 32\n"; + + EXPECT_EQ(expectedOutput, output); +} +} // namespace facebook::fboss diff --git a/fboss/cli/fboss2/test/CmdShowBgpStatsPolicyTest.cpp b/fboss/cli/fboss2/test/CmdShowBgpStatsPolicyTest.cpp new file mode 100644 index 0000000000000..55881fe6a09e5 --- /dev/null +++ b/fboss/cli/fboss2/test/CmdShowBgpStatsPolicyTest.cpp @@ -0,0 +1,112 @@ +/* + * 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. + * + */ + +#ifndef IS_OSS +#include +#include +#include +#include +#include "fboss/cli/fboss2/test/CmdHandlerTestBase.h" + +#include "fboss/cli/fboss2/commands/show/bgp/stats/CmdShowBgpStatsPolicy.h" +#include "neteng/routing/policy/if/gen-cpp2/policy_thrift_types.h" + +using namespace ::testing; +using facebook::neteng::routing::policy::thrift::TPolicyStatementStats; +using facebook::neteng::routing::policy::thrift::TPolicyStats; +using facebook::neteng::routing::policy::thrift::TPolicyTermStats; +namespace facebook::fboss { + +const std::string_view kStatementName = "RSW-TEST"; +const int kStatementPrefixHitCount = 300; +const int kStatementAvgTime = 12; +const int kStatementMaxTime = 200; +const int kStatementNumRuns = 300; +const std::string_view kTermName = "term-test"; +const std::string_view kTermDescription = "backup anycast path"; +const int kTermPrefixHitCount = 200; + +class CmdShowBgpStatsPolicyTestFixture : public CmdHandlerTestBase { + public: + TPolicyStats stats_; + + void SetUp() override { + CmdHandlerTestBase::SetUp(); + stats_ = getStats(); + } + + TPolicyStats getStats() { + TPolicyStatementStats statement_stats; + statement_stats.name() = kStatementName; + statement_stats.prefix_hit_count() = kStatementPrefixHitCount; + statement_stats.avg_time() = kStatementAvgTime; + statement_stats.max_time() = kStatementMaxTime; + statement_stats.num_of_runs() = kStatementNumRuns; + + TPolicyTermStats term_stats; + term_stats.name() = kTermName; + term_stats.description() = kTermDescription; + term_stats.prefix_hit_count() = kTermPrefixHitCount; + + statement_stats.term_stats() = {term_stats}; + + TPolicyStats queriedSats; + queriedSats.policy_statement_stats() = {statement_stats}; + + return queriedSats; + } +}; + +TEST_F(CmdShowBgpStatsPolicyTestFixture, queryClient) { + setupMockedBgpServer(); + EXPECT_CALL(getMockBgp(), getPolicyStats(_)) + .WillOnce(Invoke([&](auto& entries) { entries = stats_; })); + + auto results = CmdShowBgpStatsPolicy().queryClient(localhost()); + + const auto& statements = results.policy_statement_stats().value(); + ASSERT_EQ(statements.size(), 1); + + const auto& st_stats = statements[0]; + EXPECT_EQ(st_stats.get_name(), kStatementName); + EXPECT_EQ(st_stats.get_prefix_hit_count(), kStatementPrefixHitCount); + EXPECT_EQ(st_stats.get_avg_time(), kStatementAvgTime); + EXPECT_EQ(st_stats.get_max_time(), kStatementMaxTime); + EXPECT_EQ(st_stats.get_num_of_runs(), kStatementNumRuns); + + const auto& term_stats = st_stats.term_stats().value(); + ASSERT_EQ(term_stats.size(), 1); + + const auto& tr_stats = term_stats[0]; + EXPECT_EQ(tr_stats.get_name(), kTermName); + EXPECT_EQ(tr_stats.get_description(), kTermDescription); + EXPECT_EQ(tr_stats.get_prefix_hit_count(), kTermPrefixHitCount); +} + +TEST_F(CmdShowBgpStatsPolicyTestFixture, printOutput) { + std::stringstream ss; + CmdShowBgpStatsPolicy().printOutput(stats_, ss); + + std::string output = ss.str(); + std::string expectedOutput = + "BGP policy statistics:" + "\n Policy Name: RSW-TEST" + "\n\t Number of executions: 300" + "\n\t Number of hits: 300 prefixes" + "\n\t Average time of execution: 12us" + "\n\t Maximum time of execution: 200us" + "\n\t Term Description Hits/Misses" + "\n\t 1 backup anycast path 200/100 " + "\n\t 2 Default deny (Implicit) 100/0\n"; + + EXPECT_EQ(expectedOutput, output); +} +} // namespace facebook::fboss +#endif // IS_OSS diff --git a/fboss/cli/fboss2/test/CmdShowBgpStreamSubscriberTest.cpp b/fboss/cli/fboss2/test/CmdShowBgpStreamSubscriberTest.cpp new file mode 100644 index 0000000000000..a96818fef6c1e --- /dev/null +++ b/fboss/cli/fboss2/test/CmdShowBgpStreamSubscriberTest.cpp @@ -0,0 +1,51 @@ +/* + * 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 +#include +#include + +#include "fboss/cli/fboss2/commands/show/bgp/stream/subscriber/CmdShowBgpStreamSubscriberPostPolicy.h" +#include "fboss/cli/fboss2/commands/show/bgp/stream/subscriber/CmdShowBgpStreamSubscriberPrePolicy.h" +#include "fboss/cli/fboss2/test/CmdBgpTestUtils.h" // NOLINT(misc-include-cleaner) +#include "fboss/cli/fboss2/test/CmdHandlerTestBase.h" +#include "neteng/fboss/bgp/if/gen-cpp2/bgp_thrift_types.h" + +using namespace ::testing; +using namespace facebook::neteng::fboss::bgp::thrift; +namespace facebook::fboss { +class CmdShowBgpStreamSubscriberTestFixture : public CmdHandlerTestBase { + public: + std::map> sessions_; + + void SetUp() override { + CmdHandlerTestBase::SetUp(); + } +}; + +// As the output for the subscriber command is the same as the +// neighbors policy outputs, we only need to make sure the right +// thrift call is being made in this unit test. +TEST_F(CmdShowBgpStreamSubscriberTestFixture, queryClientPrePolicy) { + setupMockedBgpServer(); + EXPECT_CALL(getMockBgp(), getSubscriberNetworkInfo(_, _, _)); + + auto results = + CmdShowBgpStreamSubscriberPrePolicy().queryClient(localhost(), {"1"}, {}); +} + +TEST_F(CmdShowBgpStreamSubscriberTestFixture, queryClientPostPolicy) { + setupMockedBgpServer(); + EXPECT_CALL(getMockBgp(), getSubscriberNetworkInfo(_, _, _)); + + auto results = CmdShowBgpStreamSubscriberPostPolicy().queryClient( + localhost(), {"1"}, {}); +} +} // namespace facebook::fboss diff --git a/fboss/cli/fboss2/test/CmdShowBgpStreamSummaryTest.cpp b/fboss/cli/fboss2/test/CmdShowBgpStreamSummaryTest.cpp new file mode 100644 index 0000000000000..acc9e4ef6d967 --- /dev/null +++ b/fboss/cli/fboss2/test/CmdShowBgpStreamSummaryTest.cpp @@ -0,0 +1,68 @@ +/* + * 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 +#include +#include // NOLINT(misc-include-cleaner) +#include + +#include "fboss/cli/fboss2/commands/show/bgp/stream/CmdShowBgpStreamSummary.h" +#include "fboss/cli/fboss2/commands/show/bgp/summary/gen-cpp2/bgp_summary_types.h" +#include "fboss/cli/fboss2/test/CmdBgpTestUtils.h" +#include "fboss/cli/fboss2/test/CmdHandlerTestBase.h" +#include "neteng/fboss/bgp/if/gen-cpp2/bgp_thrift_types.h" +#ifndef IS_OSS +#include "nettools/common/TestUtils.h" +#endif + +using namespace ::testing; +using facebook::neteng::fboss::bgp::thrift::TBgpStreamSession; +namespace facebook::fboss { +class CmdShowBgpStreamSummaryTestFixture : public CmdHandlerTestBase { + public: + std::vector sessions_; + + void SetUp() override { + CmdHandlerTestBase::SetUp(); + sessions_ = { + buildStreamSession( + /*name=*/"anotherStreamName.02.efgh3.facebook.com", + /*uptime=*/1000, + /*peerId=*/2, + /*peerAddr=*/"5.6.7.8", + /*prefixCount=*/13), + buildStreamSession()}; + } +}; + +TEST_F(CmdShowBgpStreamSummaryTestFixture, queryClient) { + setupMockedBgpServer(); + EXPECT_CALL(getMockBgp(), getBgpStreamSessions(_)) + .WillOnce(Invoke([&](auto& sessions) { sessions = sessions_; })); + + auto results = CmdShowBgpStreamSummary().queryClient(localhost()); + EXPECT_THRIFT_EQ_VECTOR(results, sessions_); +} + +TEST_F(CmdShowBgpStreamSummaryTestFixture, printOutput) { + std::stringstream ss; + CmdShowBgpStreamSummary().printOutput(sessions_, ss); + std::string output = ss.str(); + + std::string expectedOutput = + "BGP stream summary information for subscribers\n\n" + " Peer ID Name NumRoutes Uptime \n" + "----------------------------------------------------------------------------\n" + " 1 myStreamName.01.abcd2.facebook.com 14 0h 0m 1s \n" + " 2 anotherStreamName.02.efgh3.facebook.com 13 0h 0m 1s \n\n"; + + EXPECT_EQ(output, expectedOutput); +} +} // namespace facebook::fboss diff --git a/fboss/cli/fboss2/test/CmdShowBgpSummaryEgressTest.cpp b/fboss/cli/fboss2/test/CmdShowBgpSummaryEgressTest.cpp new file mode 100644 index 0000000000000..45ab1d1e09f22 --- /dev/null +++ b/fboss/cli/fboss2/test/CmdShowBgpSummaryEgressTest.cpp @@ -0,0 +1,251 @@ +/* + * 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 +#include +#include // NOLINT(misc-include-cleaner) +#include +#include "fboss/cli/fboss2/test/CmdHandlerTestBase.h" + +#include "fboss/cli/fboss2/commands/show/bgp/summary/egress/CmdShowBgpSummaryEgress.h" +#include "fboss/cli/fboss2/commands/show/bgp/summary/gen-cpp2/bgp_summary_types.h" +#include "fboss/cli/fboss2/test/CmdBgpTestUtils.h" +#include "neteng/fboss/bgp/if/gen-cpp2/bgp_thrift_types.h" +#ifndef IS_OSS +#include "nettools/common/TestUtils.h" +#endif + +using namespace ::testing; +using facebook::neteng::fboss::bgp::thrift::TBgpSession; +using facebook::neteng::fboss::bgp::thrift::TPeerEgressStats; + +namespace facebook::fboss { + +class CmdShowBgpSummaryEgressTestFixture : public CmdHandlerTestBase { + public: + std::vector peerEgressStats_; + std::vector largeDataset_; + + void SetUp() override { + CmdHandlerTestBase::SetUp(); + peerEgressStats_ = getBasicTestData(); + largeDataset_ = getLargeTestData(); + } + + std::vector getBasicTestData() { + TBgpSession establishedSession; + + auto establishedPeer = establishedSession.peer(); + establishedPeer->peer_state() = kEstablishedPeerState; + establishedPeer->remote_as_4_byte() = kEstablishedRemoteAS; + establishedPeer->graceful() = kEstablishedIsGraceful; + + establishedSession.prepolicy_rcvd_prefix_count() = + kEstablishedPrePolicyRcvdPrefixCount; + establishedSession.postpolicy_sent_prefix_count() = + kEstablishedPostPolicySentPrefixCount; + + establishedSession.peer_addr() = kEstablishedPeerAddress; + establishedSession.uptime() = kUptime; + establishedSession.reset_time() = kEstablishedDowntime; + establishedSession.num_resets() = kEstablishedNumResets; + establishedSession.description() = kEstablishedDescription; + establishedSession.peer_bgp_id() = kEstablishedRemoteBgpId; + establishedSession.recv_update_msgs() = kEstablishedRecvUpdateMsgs; + establishedSession.sent_update_msgs() = kEstablishedSentUpdateMsgs; + + TPeerEgressStats establishedStats; + establishedStats.session() = establishedSession; + establishedStats.group_name() = kEstablishedGroupName; + establishedStats.last_adjribout_queue_block_time() = + kEstablishedLastSocketWrite; + establishedStats.adjribout_queue_blocks() = kEstablishedBackpressureEvents; + establishedStats.transient_route_updates_suppressed() = + kEstablishedSuppressedUpdates; + + TBgpSession idleSession; + + auto idlePeer = idleSession.peer(); + idlePeer->peer_state() = kIdlePeerState; + idlePeer->remote_as_4_byte() = kIdleRemoteAS; + idlePeer->graceful() = kIdleIsGraceful; + + idleSession.prepolicy_rcvd_prefix_count() = kIdlePrePolicyRcvdPrefixCount; + idleSession.postpolicy_sent_prefix_count() = kIdlePostPolicySentPrefixCount; + + idleSession.peer_addr() = kIdlePeerAddress; + idleSession.uptime() = kUptime; + idleSession.reset_time() = kIdleDowntime; + idleSession.num_resets() = kIdleNumResets; + idleSession.description() = kIdleDescription; + idleSession.peer_bgp_id() = kIdleRemoteBgpId; + idleSession.recv_update_msgs() = kIdleRecvUpdateMsgs; + idleSession.sent_update_msgs() = kIdleSentUpdateMsgs; + + TPeerEgressStats idleStats; + idleStats.session() = idleSession; + idleStats.group_name() = kIdleGroupName; + idleStats.adjribout_queue_blocks() = kIdleBackpressureEvents; + idleStats.transient_route_updates_suppressed() = kIdleSuppressedUpdates; + + return {establishedStats, idleStats}; + } + + std::vector getLargeTestData(int numStats = 100) { + std::vector result; + + for (int i = 1; i <= numStats; ++i) { + TBgpSession session; + TBgpPeer peer; + peer.peer_state() = kEstablishedPeerState; + peer.remote_as_4_byte() = i; + peer.graceful() = true; + session.peer() = peer; + + session.peer_addr() = fmt::format("{}.{}.{}.{}", i, i + 1, i + 2, i + 3); + session.uptime() = i * 10; + session.reset_time() = 0; + session.description() = kIbgpDescription; + session.peer_bgp_id() = + fmt::format("{}.{}.{}.{}", i + 3, i + 2, i + 1, i); + session.prepolicy_rcvd_prefix_count() = 0; + session.postpolicy_sent_prefix_count() = 0; + session.recv_update_msgs() = i * 5; + session.sent_update_msgs() = i * 10; + + TPeerEgressStats stats; + stats.group_name() = kIbgpGroupName; + stats.session() = session; + stats.transient_route_updates_suppressed() = i; + stats.adjribout_queue_blocks() = i; + stats.adjribout_queue_total_block_duration() = i; + stats.send_queue_blocks() = i; + stats.send_queue_total_block_duration() = i; + stats.total_async_socket_buffered() = i; + + auto iMinutes = i * 60 * 1000; + stats.last_adjribout_queue_block_time() = iMinutes; + stats.last_send_queue_block_time() = iMinutes; + stats.last_socket_buffered_time() = iMinutes; + + result.push_back(stats); + } + + TBgpSession idleSession; + TBgpPeer idlePeer; + idlePeer.peer_state() = kIdlePeerState; + idlePeer.remote_as_4_byte() = kIdleRemoteAS; + idlePeer.graceful() = kIdleIsGraceful; + idleSession.peer() = idlePeer; + + idleSession.prepolicy_rcvd_prefix_count() = kIdlePrePolicyRcvdPrefixCount; + idleSession.postpolicy_sent_prefix_count() = kIdlePostPolicySentPrefixCount; + idleSession.peer_addr() = "200.201.202.203"; + idleSession.uptime() = 0; + idleSession.reset_time() = kIdleDowntime; + idleSession.num_resets() = kIdleNumResets; + idleSession.description() = kIdleDescription; + idleSession.peer_bgp_id() = "203.202.201.200"; + idleSession.recv_update_msgs() = kIdleRecvUpdateMsgs; + idleSession.sent_update_msgs() = kIdleSentUpdateMsgs; + + TPeerEgressStats idleStats; + idleStats.session() = idleSession; + idleStats.group_name() = kIdleGroupName; + idleStats.last_adjribout_queue_block_time() = kIdleLastSocketWrite; + idleStats.adjribout_queue_blocks() = kIdleBackpressureEvents; + idleStats.transient_route_updates_suppressed() = kIdleSuppressedUpdates; + idleStats.send_queue_blocks() = 0; + idleStats.send_queue_total_block_duration() = 0; + idleStats.last_send_queue_block_time() = 0; + idleStats.total_async_socket_buffered() = 0; + idleStats.last_socket_buffered_time() = 0; + idleStats.adjribout_queue_total_block_duration() = 0; + + result.push_back(idleStats); + + return result; + } + + cli::ShowBgpSummaryModel getBasicModel() { + cli::ShowBgpSummaryModel model; + model.peer_egress_stats() = getBasicTestData(); + return model; + } + + cli::ShowBgpSummaryModel getLargeModel(int numStats = 100) { + cli::ShowBgpSummaryModel model; + auto stats = getLargeTestData(numStats); + std::sort( + stats.begin(), + stats.end(), + [](const TPeerEgressStats& a, const TPeerEgressStats& b) { + if (!a.session().has_value() || !b.session().has_value()) { + return false; + } + return a.session()->peer_addr().value() < + b.session()->peer_addr().value(); + }); + model.peer_egress_stats() = stats; + return model; + } +}; + +TEST_F(CmdShowBgpSummaryEgressTestFixture, queryClient) { + setupMockedBgpServer(); + EXPECT_CALL(getMockBgp(), getPeerEgressStats(_)) + .WillOnce(Invoke([&](auto& entries) { entries = peerEgressStats_; })); + + auto results = CmdShowBgpSummaryEgress().queryClient(localhost()); + EXPECT_THRIFT_EQ(results, getBasicModel()); +} + +TEST_F(CmdShowBgpSummaryEgressTestFixture, printGroupSummary) { + std::stringstream ss; + CmdShowBgpSummaryEgress().printGroupSummary( + *getLargeModel().peer_egress_stats(), ss); + std::string output = ss.str(); + + std::string expectedOutput = + "BGP Peer Egress Summary by Group Percentiles\n" + "Acronyms: PR - Prefixes Received, PS - Prefixes Sent, UR - Update Messages Received, US - Update Messages Sent, \n" + "SU - Suppressed Updates (updates not sent due to packing state compression), IQ - Per-peer Input Queue to BGP I/O thread, SQ - Per-peer Send Queue to socket \n\n" + " Percentile Group Name PR PS UR US SU IQ Blocks Total IQ Wait (ms) Last IQ Block SQ Blocks Total SQ Wait (ms) Last SQ Block Socket Buffered Last Socket Buffered \n" + "------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------\n" + " p50 IBGP_GROUP 0 0 250 500 50 50 50 1969-12-31 16:50:00.0 PST 50 50 1969-12-31 16:50:00.0 PST 50 1969-12-31 16:50:00.0 PST \n" + " p95 IBGP_GROUP 0 0 475 950 95 95 95 1969-12-31 17:35:00.0 PST 95 95 1969-12-31 17:35:00.0 PST 95 1969-12-31 17:35:00.0 PST \n" + " p99 IBGP_GROUP 0 0 495 990 99 99 99 1969-12-31 17:39:00.0 PST 99 99 1969-12-31 17:39:00.0 PST 99 1969-12-31 17:39:00.0 PST \n\n"; + + EXPECT_EQ(output, expectedOutput); +} + +TEST_F(CmdShowBgpSummaryEgressTestFixture, printPeerSummary) { + std::stringstream ss; + CmdShowBgpSummaryEgress().printPeerSummary( + *getLargeModel(5).peer_egress_stats(), ss); + std::string output = ss.str(); + + std::string expectedOutput = + "BGP Peer Egress Summary\n" + "Acronyms: PR - Prefixes Received, PS - Prefixes Sent, UR - Update Messages Received, US - Update Messages Sent, \n" + "SU - Suppressed Updates (updates not sent due to packing state compression), IQ - Per-peer Input Queue to BGP I/O thread, SQ - Per-peer Send Queue to socket \n\n" + " Peer Description Group Name State Uptime Downtime PR PS UR US SU IQ Blocks Total IQ Wait (ms) Last IQ Block SQ Blocks Total SQ Wait (ms) Last SQ Block Socket Buffered Last Socket Buffered \n" + "--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------\n" + " 1.2.3.4 IBGP v4 Peers IBGP_GROUP ESTA 0h 0m 0s 0 0 5 10 1 1 1 1969-12-31 16:01:00.0 PST 1 1 1969-12-31 16:01:00.0 PST 1 1969-12-31 16:01:00.0 PST \n" + " 2.3.4.5 IBGP v4 Peers IBGP_GROUP ESTA 0h 0m 0s 0 0 10 20 2 2 2 1969-12-31 16:02:00.0 PST 2 2 1969-12-31 16:02:00.0 PST 2 1969-12-31 16:02:00.0 PST \n" + " 200.201.202.203 fsw007.p015.f01.prn6 IDLE_GROUP IDLE 0h 0m 2s 0 0 50 75 15 20 0 0 0 0 \n" + " 3.4.5.6 IBGP v4 Peers IBGP_GROUP ESTA 0h 0m 0s 0 0 15 30 3 3 3 1969-12-31 16:03:00.0 PST 3 3 1969-12-31 16:03:00.0 PST 3 1969-12-31 16:03:00.0 PST \n" + " 4.5.6.7 IBGP v4 Peers IBGP_GROUP ESTA 0h 0m 0s 0 0 20 40 4 4 4 1969-12-31 16:04:00.0 PST 4 4 1969-12-31 16:04:00.0 PST 4 1969-12-31 16:04:00.0 PST \n" + " 5.6.7.8 IBGP v4 Peers IBGP_GROUP ESTA 0h 0m 0s 0 0 25 50 5 5 5 1969-12-31 16:05:00.0 PST 5 5 1969-12-31 16:05:00.0 PST 5 1969-12-31 16:05:00.0 PST \n\n"; + + EXPECT_EQ(output, expectedOutput); +} + +} // namespace facebook::fboss diff --git a/fboss/cli/fboss2/test/CmdShowBgpSummaryTest.cpp b/fboss/cli/fboss2/test/CmdShowBgpSummaryTest.cpp new file mode 100644 index 0000000000000..ccc2d603fe154 --- /dev/null +++ b/fboss/cli/fboss2/test/CmdShowBgpSummaryTest.cpp @@ -0,0 +1,355 @@ +/* + * 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 +#include +#include // NOLINT(misc-include-cleaner) +#include + +#include "fboss/cli/fboss2/commands/show/bgp/summary/CmdShowBgpSummary.h" +#include "fboss/cli/fboss2/commands/show/bgp/summary/gen-cpp2/bgp_summary_types.h" +#include "fboss/cli/fboss2/test/CmdBgpTestUtils.h" +#include "fboss/cli/fboss2/test/CmdHandlerTestBase.h" +#include "neteng/fboss/bgp/if/gen-cpp2/bgp_thrift_types.h" +#ifndef IS_OSS +#include "nettools/common/TestUtils.h" +#endif + +using namespace ::testing; +using facebook::neteng::fboss::bgp::thrift::TBgpDrainState; +using facebook::neteng::fboss::bgp::thrift::TBgpLocalConfig; +using facebook::neteng::fboss::bgp::thrift::TBgpSession; + +namespace facebook::fboss { +// Constants for TBgpLocalConfig +const int kRouterId = 1234; +const int kLocalAS = 6000; +const int kLocalConfedAs = 700; +const bool kUCMPWeights = false; +const bool kEnableUpdateGroup = true; + +// Constants for TBgpDrainState +const std::vector kDrainedInterfaces = {"eth1/1/1", "eth1/2/1"}; + +class CmdShowBgpSummaryTestFixture : public CmdHandlerTestBase { + public: + std::vector sessions_; + TBgpLocalConfig localConfig_; + TBgpDrainState drainState_; + + void SetUp() override { + CmdHandlerTestBase::SetUp(); + sessions_ = getQueriedSession(); + localConfig_ = getQueriedConfig(); + drainState_ = getQueriedDrainState(); + } + + std::vector getQueriedSession() { + TBgpSession establishedSession; + + auto establishedPeer = establishedSession.peer(); + establishedPeer->peer_state() = kEstablishedPeerState; + establishedPeer->remote_as_4_byte() = kEstablishedRemoteAS; + establishedPeer->graceful() = kEstablishedIsGraceful; + + establishedSession.prepolicy_rcvd_prefix_count() = + kEstablishedPrePolicyRcvdPrefixCount; + establishedSession.postpolicy_sent_prefix_count() = + kEstablishedPostPolicySentPrefixCount; + + establishedSession.peer_addr() = kEstablishedPeerAddress; + establishedSession.uptime() = kUptime; + establishedSession.reset_time() = kEstablishedDowntime; + establishedSession.num_resets() = kEstablishedNumResets; + establishedSession.description() = kEstablishedDescription; + establishedSession.peer_bgp_id() = kEstablishedRemoteBgpId; + + TBgpSession idleSession; + + auto idlePeer = idleSession.peer(); + idlePeer->peer_state() = kIdlePeerState; + idlePeer->remote_as_4_byte() = kIdleRemoteAS; + idlePeer->graceful() = kIdleIsGraceful; + + idleSession.prepolicy_rcvd_prefix_count() = kIdlePrePolicyRcvdPrefixCount; + idleSession.postpolicy_sent_prefix_count() = kIdlePostPolicySentPrefixCount; + + idleSession.peer_addr() = kIdlePeerAddress; + idleSession.uptime() = kUptime; + idleSession.reset_time() = kIdleDowntime; + idleSession.num_resets() = kIdleNumResets; + idleSession.description() = kIdleDescription; + idleSession.peer_bgp_id() = kIdleRemoteBgpId; + + return {establishedSession, idleSession}; + } + + TBgpLocalConfig getQueriedConfig() { + TBgpLocalConfig config; + + config.my_router_id() = kRouterId; + config.local_as_4_byte() = kLocalAS; + config.local_confed_as_4_byte() = kLocalConfedAs; + config.program_ucmp_weights() = kUCMPWeights; + config.enable_update_group() = kEnableUpdateGroup; + + return config; + } + + TBgpDrainState getQueriedDrainState() { + TBgpDrainState drain_state; + + drain_state.drain_state() = + facebook::bgp::bgp_policy::DrainState::UNDRAINED; + drain_state.drained_interfaces() = kDrainedInterfaces; + + return drain_state; + } + + cli::ShowBgpSummaryModel getModelBgpSummary() { + cli::ShowBgpSummaryModel model; + + model.sessions() = getQueriedSession(); + model.config() = getQueriedConfig(); + model.drain_state() = getQueriedDrainState(); + + return model; + } + + cli::ShowBgpSummaryModel getModelBgpSummaryOldAsn() { + // TODO(michaeluw): T113736668 deprecate this when we deprecate i32 asns + // field. To be safe, build from scratch instead of manipulating result of + // getModelBgpSummary + cli::ShowBgpSummaryModel model; + + TBgpLocalConfig config; + + config.my_router_id() = kRouterId; + config.local_as() = kLocalAS; + config.local_confed_as() = kLocalConfedAs; + config.program_ucmp_weights() = kUCMPWeights; + config.enable_update_group() = kEnableUpdateGroup; + + model.config() = std::move(config); + + TBgpSession establishedSession; + + auto establishedPeer = establishedSession.peer(); + establishedPeer->peer_state() = kEstablishedPeerState; + establishedPeer->remote_as() = kEstablishedRemoteAS; + establishedPeer->graceful() = kEstablishedIsGraceful; + + establishedSession.prepolicy_rcvd_prefix_count() = + kEstablishedPrePolicyRcvdPrefixCount; + establishedSession.postpolicy_sent_prefix_count() = + kEstablishedPostPolicySentPrefixCount; + + establishedSession.peer_addr() = kEstablishedPeerAddress; + establishedSession.uptime() = kUptime; + establishedSession.reset_time() = kEstablishedDowntime; + establishedSession.num_resets() = kEstablishedNumResets; + establishedSession.description() = kEstablishedDescription; + establishedSession.peer_bgp_id() = kEstablishedRemoteBgpId; + + TBgpSession idleSession; + + auto idlePeer = idleSession.peer(); + idlePeer->peer_state() = kIdlePeerState; + idlePeer->remote_as() = kIdleRemoteAS; + idlePeer->graceful() = kIdleIsGraceful; + + idleSession.prepolicy_rcvd_prefix_count() = kIdlePrePolicyRcvdPrefixCount; + idleSession.postpolicy_sent_prefix_count() = kIdlePostPolicySentPrefixCount; + + idleSession.peer_addr() = kIdlePeerAddress; + idleSession.uptime() = kUptime; + idleSession.reset_time() = kIdleDowntime; + idleSession.num_resets() = kIdleNumResets; + idleSession.description() = kIdleDescription; + idleSession.peer_bgp_id() = kIdleRemoteBgpId; + + model.sessions() = {std::move(establishedSession), std::move(idleSession)}; + + TBgpDrainState drainState; + + drainState.drain_state() = facebook::bgp::bgp_policy::DrainState::DRAINED; + drainState.drained_interfaces() = kDrainedInterfaces; + model.drain_state() = std::move(drainState); + + return model; + } +}; + +TEST_F(CmdShowBgpSummaryTestFixture, queryClient) { + setupMockedBgpServer(); + EXPECT_CALL(getMockBgp(), getBgpSessions(_)) + .WillOnce(Invoke([&](auto& entries) { entries = sessions_; })); + + EXPECT_CALL(getMockBgp(), getBgpLocalConfig(_)) + .WillOnce(Invoke([&](auto& entries) { entries = localConfig_; })); + + EXPECT_CALL(getMockBgp(), getDrainState(_)) + .WillOnce(Invoke([&](auto& entries) { entries = drainState_; })); + + auto results = CmdShowBgpSummary().queryClient(localhost()); + EXPECT_THRIFT_EQ(results, getModelBgpSummary()); +} + +TEST_F(CmdShowBgpSummaryTestFixture, printOutput) { + std::stringstream ss; + CmdShowBgpSummary().printOutput(getModelBgpSummary(), ss); + std::string output = ss.str(); + + std::string expectedOutput = + "BGP summary information for VRF default\n" + "Router ID - 210.4.0.0, Local ASN - 6000, Confed ASN - 700\n" + "BGP Switch Drain State: UNDRAINED\n" + "UCMP weight programming is DISABLED\n" + "Update group is ENABLED\n" + "Peers: UP - 1, TOTAL - 2\n" + "Paths: Received - 2, Sent - 2\n" + "Acronyms: PS - Prefixes Sent, PR - Prefixes Received, HT - Hold Time\n\n" + " Peer AS State GR PR PS Uptime Downtime Description Session ID Flaps \n" + "-----------------------------------------------------------------------------------------------------------\n" + " 1.2.3.4 1 ESTA Y 2 2 0h 0m 1s fsw001.p015.f01.prn6 4.3.2.1 9 \n" + " 2.3.4.5 4651 IDLE N 0 0 0h 0m 2s fsw007.p015.f01.prn6 6.7.8.9 33 \n\n"; + + EXPECT_EQ(output, expectedOutput); +} + +TEST_F(CmdShowBgpSummaryTestFixture, printOutputWhenConfedAsnIsZero) { + std::stringstream ss; + auto model = getModelBgpSummary(); + model.config()->local_confed_as_4_byte() = 0; // zero confed asn + CmdShowBgpSummary().printOutput(model, ss); + std::string output = ss.str(); + + std::string expectedOutput = + "BGP summary information for VRF default\n" + "Router ID - 210.4.0.0, Local ASN - 6000\n" + "BGP Switch Drain State: UNDRAINED\n" + "UCMP weight programming is DISABLED\n" + "Update group is ENABLED\n" + "Peers: UP - 1, TOTAL - 2\n" + "Paths: Received - 2, Sent - 2\n" + "Acronyms: PS - Prefixes Sent, PR - Prefixes Received, HT - Hold Time\n\n" + " Peer AS State GR PR PS Uptime Downtime Description Session ID Flaps \n" + "-----------------------------------------------------------------------------------------------------------\n" + " 1.2.3.4 1 ESTA Y 2 2 0h 0m 1s fsw001.p015.f01.prn6 4.3.2.1 9 \n" + " 2.3.4.5 4651 IDLE N 0 0 0h 0m 2s fsw007.p015.f01.prn6 6.7.8.9 33 \n\n"; + + EXPECT_EQ(output, expectedOutput); +} + +TEST_F(CmdShowBgpSummaryTestFixture, printOutputWithOldAsnField) { + // TODO(michaeluw): deprecate this when we deprecate i32 asns field + // Test with only old field present + // s T113736668 + std::stringstream ss; + CmdShowBgpSummary().printOutput(getModelBgpSummaryOldAsn(), ss); + std::string output = ss.str(); + + std::string expectedOutput = + "BGP summary information for VRF default\n" + "Router ID - 210.4.0.0, Local ASN - 6000, Confed ASN - 700\n" + "BGP Switch Drain State: DRAINED\n" + "UCMP weight programming is DISABLED\n" + "Update group is ENABLED\n" + "Peers: UP - 1, TOTAL - 2\n" + "Paths: Received - 2, Sent - 2\n" + "Acronyms: PS - Prefixes Sent, PR - Prefixes Received, HT - Hold Time\n\n" + " Peer AS State GR PR PS Uptime Downtime Description Session ID Flaps \n" + "-----------------------------------------------------------------------------------------------------------\n" + " 1.2.3.4 1 ESTA Y 2 2 0h 0m 1s fsw001.p015.f01.prn6 4.3.2.1 9 \n" + " 2.3.4.5 4651 IDLE N 0 0 0h 0m 2s fsw007.p015.f01.prn6 6.7.8.9 33 \n\n"; + + EXPECT_EQ(output, expectedOutput); +} + +TEST_F(CmdShowBgpSummaryTestFixture, printOutputBothAsnFieldPresent) { + // TODO(michaeluw): deprecate this when we deprecate i32 asns field + // Test with only new and old field present + // s T113736668 + std::stringstream ss; + auto model = getModelBgpSummary(); + model.config()->local_as() = 123; + model.config()->local_confed_as() = 123; + CmdShowBgpSummary().printOutput(model, ss); + std::string output = ss.str(); + + std::string expectedOutput = + "BGP summary information for VRF default\n" + "Router ID - 210.4.0.0, Local ASN - 6000, Confed ASN - 700\n" + "BGP Switch Drain State: UNDRAINED\n" + "UCMP weight programming is DISABLED\n" + "Update group is ENABLED\n" + "Peers: UP - 1, TOTAL - 2\n" + "Paths: Received - 2, Sent - 2\n" + "Acronyms: PS - Prefixes Sent, PR - Prefixes Received, HT - Hold Time\n\n" + " Peer AS State GR PR PS Uptime Downtime Description Session ID Flaps \n" + "-----------------------------------------------------------------------------------------------------------\n" + " 1.2.3.4 1 ESTA Y 2 2 0h 0m 1s fsw001.p015.f01.prn6 4.3.2.1 9 \n" + " 2.3.4.5 4651 IDLE N 0 0 0h 0m 2s fsw007.p015.f01.prn6 6.7.8.9 33 \n\n"; + + EXPECT_EQ(output, expectedOutput); +} + +#ifndef IS_OSS +// Meta-internal drain states (WARM_DRAINED, SOFT_DRAINED) not available in OSS +TEST_F(CmdShowBgpSummaryTestFixture, printOutputWithDifferentDrainStates) { + std::stringstream ss; + auto model = getModelBgpSummary(); + model.drain_state()->drain_state() = + bgp::bgp_policy::DrainState::WARM_DRAINED; + CmdShowBgpSummary().printOutput(model, ss); + std::string output = ss.str(); + + std::string expectedOutput = + "BGP summary information for VRF default\n" + "Router ID - 210.4.0.0, Local ASN - 6000, Confed ASN - 700\n" + "BGP Switch Drain State: WARM_DRAINED\n" + "UCMP weight programming is DISABLED\n" + "Update group is ENABLED\n" + "Peers: UP - 1, TOTAL - 2\n" + "Paths: Received - 2, Sent - 2\n" + "Acronyms: PS - Prefixes Sent, PR - Prefixes Received, HT - Hold Time\n\n" + " Peer AS State GR PR PS Uptime Downtime Description Session ID Flaps \n" + "-----------------------------------------------------------------------------------------------------------\n" + " 1.2.3.4 1 ESTA Y 2 2 0h 0m 1s fsw001.p015.f01.prn6 4.3.2.1 9 \n" + " 2.3.4.5 4651 IDLE N 0 0 0h 0m 2s fsw007.p015.f01.prn6 6.7.8.9 33 \n\n"; + + EXPECT_EQ(output, expectedOutput); + + model.drain_state()->drain_state() = + bgp::bgp_policy::DrainState::SOFT_DRAINED; + ss.str(""); + ss.clear(); + CmdShowBgpSummary().printOutput(model, ss); + output = ss.str(); + + expectedOutput = + "BGP summary information for VRF default\n" + "Router ID - 210.4.0.0, Local ASN - 6000, Confed ASN - 700\n" + "BGP Switch Drain State: SOFT_DRAINED\n" + "UCMP weight programming is DISABLED\n" + "Update group is ENABLED\n" + "Peers: UP - 1, TOTAL - 2\n" + "Paths: Received - 2, Sent - 2\n" + "Acronyms: PS - Prefixes Sent, PR - Prefixes Received, HT - Hold Time\n\n" + " Peer AS State GR PR PS Uptime Downtime Description Session ID Flaps \n" + "-----------------------------------------------------------------------------------------------------------\n" + " 1.2.3.4 1 ESTA Y 2 2 0h 0m 1s fsw001.p015.f01.prn6 4.3.2.1 9 \n" + " 2.3.4.5 4651 IDLE N 0 0 0h 0m 2s fsw007.p015.f01.prn6 6.7.8.9 33 \n\n"; + + EXPECT_EQ(output, expectedOutput); +} +#endif // IS_OSS + +} // namespace facebook::fboss diff --git a/fboss/cli/fboss2/test/CmdShowBgpTableCommunityTest.cpp b/fboss/cli/fboss2/test/CmdShowBgpTableCommunityTest.cpp new file mode 100644 index 0000000000000..7ad6069be979b --- /dev/null +++ b/fboss/cli/fboss2/test/CmdShowBgpTableCommunityTest.cpp @@ -0,0 +1,90 @@ +/* + * 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 +#include +#include +#include "fboss/cli/fboss2/test/CmdHandlerTestBase.h" + +#include + +#include "fboss/cli/fboss2/commands/show/bgp/table/CmdShowBgpTableCommunity.h" +#include "fboss/cli/fboss2/test/CmdBgpTestUtils.h" +#include "neteng/fboss/bgp/if/gen-cpp2/bgp_thrift_types.h" + +using namespace ::testing; +using namespace facebook::neteng::fboss::bgp::thrift; +namespace facebook::fboss { +const std::string groupNameA = "groupNameA"; +const int asnA = 123; +const int valueA = 4567; +const std::string groupNameB = "groupNameB"; +const int asnB = 891; +const int valueB = 1112; +const std::string communityString = "123:4567"; + +class CmdShowBgpTableCommunityTestFixture : public CmdHandlerTestBase { + public: + std::vector entries_; + void SetUp() override { + CmdHandlerTestBase::SetUp(); + entries_ = { + buildEntryByCommunity(groupNameA, asnA, valueA), + buildEntryByCommunity(groupNameB, asnB, valueB)}; + } +}; + +// Since we are not testing filtering and output is already +// being tested in other table command unit tests, we just want +// to make sure the right thrift call is being made. +TEST_F(CmdShowBgpTableCommunityTestFixture, queryClient) { + setupMockedBgpServer(); + EXPECT_CALL(getMockBgp(), getRibEntriesForCommunity(_, _, _)); + EXPECT_CALL(getMockBgp(), getRunningConfig(_)) + .WillOnce(Invoke([&](std::string& config) { + // clang-format off + folly::dynamic value = folly::dynamic::object + ("communities", + folly::dynamic::array( + folly::dynamic::object("name", "FABRIC_POD_RSW_LOOP") + ("description", "rsw loopback") + ("communities", folly::dynamic::array("65527:12705")) + ) + ) + ("localprefs", + folly::dynamic::array( + folly::dynamic::object("localpref", 20) + ("name", "LOCALPREF_CTRL_BACKUP") + ("description", "low-priority supplementary/backup routes from bgp controller")) + ); + // clang-format on + config = folly::toPrettyJson(value); + })); + auto result = + CmdShowBgpTableCommunity().queryClient(localhost(), {communityString}); +} + +TEST_F(CmdShowBgpTableCommunityTestFixture, filterEntriesByCommunities) { + auto results = CmdShowBgpTableCommunity().filterEntriesByCommunities( + entries_, communityString); + + ASSERT_EQ(results.size(), 1); + const auto& entry = results.at(0); + + EXPECT_EQ(entry.get_best_group(), groupNameA); + const auto& paths = entry.paths().value(); + ASSERT_EQ(paths.size(), 1); + const auto& communities = + apache::thrift::get_pointer(paths.at(groupNameA).at(0).communities()); + ASSERT_EQ(communities->size(), 1); + EXPECT_EQ(communities->at(0).get_asn(), asnA); + EXPECT_EQ(communities->at(0).get_value(), valueA); +} +} // namespace facebook::fboss diff --git a/fboss/cli/fboss2/test/CmdShowBgpTableDetailTest.cpp b/fboss/cli/fboss2/test/CmdShowBgpTableDetailTest.cpp new file mode 100644 index 0000000000000..1659ddc9e7f48 --- /dev/null +++ b/fboss/cli/fboss2/test/CmdShowBgpTableDetailTest.cpp @@ -0,0 +1,117 @@ +/* + * 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 +#include +#include +#include // NOLINT(misc-include-cleaner) +#include +#include "fboss/cli/fboss2/test/CmdHandlerTestBase.h" + +#include + +#include "fboss/cli/fboss2/commands/show/bgp/table/CmdShowBgpTableDetail.h" +#include "fboss/cli/fboss2/test/CmdBgpTestUtils.h" +#include "neteng/fboss/bgp/if/gen-cpp2/bgp_thrift_types.h" +#ifndef IS_OSS +#include "nettools/common/TestUtils.h" +#endif + +using namespace ::testing; +using namespace facebook::neteng::fboss::bgp::thrift; +namespace facebook::fboss { +class CmdShowBgpTableDetailTestFixture : public CmdHandlerTestBase { + public: + std::vector entriesIPv4_; + std::vector entriesIPv6_; + std::vector combinedEntries_; + + void SetUp() override { + CmdHandlerTestBase::SetUp(); + entriesIPv4_ = {buildEntry()}; + entriesIPv6_ = { + buildEntry("2001::1/64", "2001::2", "2001::3", "two00one::three", 7)}; + combineEntries(); + } + + void combineEntries() { + this->combinedEntries_ = this->entriesIPv4_; + this->combinedEntries_.insert( + combinedEntries_.end(), entriesIPv6_.begin(), entriesIPv6_.end()); + } +}; + +TEST_F(CmdShowBgpTableDetailTestFixture, queryClient) { + setupMockedBgpServer(); + EXPECT_CALL(getMockBgp(), getRibEntries(_, TBgpAfi::AFI_IPV4)) + .WillOnce(Invoke([&](std::vector& entries, TBgpAfi) { + entries = entriesIPv4_; + })); + + EXPECT_CALL(getMockBgp(), getRibEntries(_, TBgpAfi::AFI_IPV6)) + .WillOnce(Invoke([&](std::vector& entries, TBgpAfi) { + entries = entriesIPv6_; + })); + auto result = CmdShowBgpTableDetail().queryClient(localhost()); + EXPECT_THRIFT_EQ_VECTOR(*result.tRibEntries(), combinedEntries_); +} + +TEST_F(CmdShowBgpTableDetailTestFixture, printOutput) { + setupMockedBgpServer(); + EXPECT_CALL(getMockBgp(), getRunningConfig(_)) + .WillRepeatedly(Invoke([&](std::string& config) { + // clang-format off + folly::dynamic value = folly::dynamic::object + ("communities", + folly::dynamic::array( + folly::dynamic::object("name", "FABRIC_POD_RSW_LOOP") + ("description", "rsw loopback") + ("communities", folly::dynamic::array("65527:12705")) + ) + ) + ("localprefs", + folly::dynamic::array( + folly::dynamic::object("localpref", 20) + ("name", "LOCALPREF_CTRL_BACKUP") + ("description", "low-priority supplementary/backup routes from bgp controller"), + folly::dynamic::object("localpref", 25) + ("name", "LOCALPREF_DEPRIO") + ("description", "deprioritized local preference value")) + ); + // clang-format on + config = folly::toPrettyJson(value); + })); + std::stringstream ss; + TRibEntryWithHost tRibEntryWithHost; + tRibEntryWithHost.tRibEntries() = combinedEntries_; + tRibEntryWithHost.host() = localhost().getName(); + tRibEntryWithHost.oobName() = localhost().getOobName(); + tRibEntryWithHost.ip() = localhost().getIpStr(); + CmdShowBgpTableDetail().printOutput(tRibEntryWithHost, ss); + std::string output = ss.str(); + + std::string expectedOutput = kRibEntryMarkersHeader + + "\n> 8.0.0.0/32, Selected 1/1 paths\n" + "*@ from 1.2.3.4 (one.two.three.four) via 8.0.0.1 | LBW: None | Origin: INCOMPLETE | LP: DEPRIO/25 | ASP: 65301 | LM: # | NH Weight: N/A | MED: 10 | ID: 5 (rcvd) 6 (sent) | Weight: 20 | IgpCost: 100" + "\n Router/Originator: 2.2.2.3 | ClusterList: [1.1.1.2]\n" + " Communities: FABRIC_POD_RSW_LOOP/65527:12705\n" + " ExtCommunities: Type(64):SubType(2):AS(3):Value(4)\n" + " BestPath Rejection Reason: Router-Id, Filter Criterion: Choose Lowest Value\n" + "\n> 2001::1/64, Selected 1/1 paths\n" + "*@ from 2001::3 (two00one::three) via 2001::2 | LBW: None | Origin: INCOMPLETE | LP: DEPRIO/25 | ASP: 65301 | LM: # | NH Weight: 7 | MED: 10 | ID: 5 (rcvd) 6 (sent) | Weight: 20 | IgpCost: 100" + "\n Router/Originator: 2.2.2.3 | ClusterList: [1.1.1.2]\n" + " Communities: FABRIC_POD_RSW_LOOP/65527:12705\n" + " ExtCommunities: Type(64):SubType(2):AS(3):Value(4)\n" + " BestPath Rejection Reason: Router-Id, Filter Criterion: Choose Lowest Value\n"; + + maskDateInOutput(output); + EXPECT_EQ(output, expectedOutput); +} +} // namespace facebook::fboss diff --git a/fboss/cli/fboss2/test/CmdShowBgpTableMoreSpecificsTest.cpp b/fboss/cli/fboss2/test/CmdShowBgpTableMoreSpecificsTest.cpp new file mode 100644 index 0000000000000..c7325e51e65ed --- /dev/null +++ b/fboss/cli/fboss2/test/CmdShowBgpTableMoreSpecificsTest.cpp @@ -0,0 +1,100 @@ +/* + * 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 +#include +#include +#include +#include // NOLINT(misc-include-cleaner) +#include +#include +#include "fboss/cli/fboss2/test/CmdHandlerTestBase.h" + +#include "fboss/cli/fboss2/commands/show/bgp/table/CmdShowBgpTableMoreSpecifics.h" +#include "fboss/cli/fboss2/test/CmdBgpTestUtils.h" +#include "neteng/fboss/bgp/if/gen-cpp2/bgp_thrift_types.h" +#ifndef IS_OSS +#include "nettools/common/TestUtils.h" +#endif + +using namespace ::testing; +using namespace facebook::neteng::fboss::bgp::thrift; +namespace facebook::fboss { +class CmdShowBgpTableMoreSpecificsTestFixture : public CmdHandlerTestBase { + public: + std::vector queriedEntry_; + const std::string kPrefixToQuery = "8.0.0.0/32"; + + void SetUp() override { + CmdHandlerTestBase::SetUp(); + queriedEntry_ = { + buildEntry(kPrefixToQuery, "8.0.0.1", "1.2.3.4", "one.two.three.four")}; + } +}; + +TEST_F(CmdShowBgpTableMoreSpecificsTestFixture, queryClient) { + setupMockedBgpServer(); + EXPECT_CALL(getMockBgp(), getRibSubprefixes(_, _)) + .WillOnce(Invoke( + [&](std::vector& entries, std::unique_ptr) { + entries = queriedEntry_; + })); + + auto result = + CmdShowBgpTableMoreSpecifics().queryClient(localhost(), {kPrefixToQuery}); + EXPECT_THRIFT_EQ_VECTOR(*result.tRibEntries(), queriedEntry_); +} + +TEST_F(CmdShowBgpTableMoreSpecificsTestFixture, printOutput) { + setupMockedBgpServer(); + EXPECT_CALL(getMockBgp(), getRunningConfig(_)) + .WillRepeatedly(Invoke([&](std::string& config) { + // clang-format off + folly::dynamic value = folly::dynamic::object + ("communities", + folly::dynamic::array( + folly::dynamic::object("name", "FABRIC_POD_RSW_LOOP") + ("description", "rsw loopback") + ("communities", folly::dynamic::array("65527:12705")) + ) + ) + ("localprefs", + folly::dynamic::array( + folly::dynamic::object("localpref", 20) + ("name", "LOCALPREF_CTRL_BACKUP") + ("description", "low-priority supplementary/backup routes from bgp controller"), + folly::dynamic::object("localpref", 25) + ("name", "LOCALPREF_DEPRIO") + ("description", "deprioritized local preference value")) + ); + // clang-format on + config = folly::toPrettyJson(value); + })); + std::stringstream ss; + TRibEntryWithHost tRibEntryWithHost; + tRibEntryWithHost.tRibEntries() = queriedEntry_; + tRibEntryWithHost.host() = localhost().getName(); + tRibEntryWithHost.oobName() = localhost().getOobName(); + tRibEntryWithHost.ip() = localhost().getIpStr(); + CmdShowBgpTableMoreSpecifics().printOutput(tRibEntryWithHost, ss); + std::string output = ss.str(); + + std::string expectedOutput = kRibEntryMarkersHeader + + "\n> 8.0.0.0/32, Selected 1/1 paths\n" + "*@ from 1.2.3.4 (one.two.three.four) via 8.0.0.1 | LBW: None | Origin: INCOMPLETE | LP: DEPRIO/25 | ASP: 65301 | LM: # | NH Weight: N/A | MED: 10 | ID: 5 (rcvd) 6 (sent) | Weight: 20 | IgpCost: 100" + "\n Router/Originator: 2.2.2.3 | ClusterList: [1.1.1.2]\n" + " Communities: FABRIC_POD_RSW_LOOP/65527:12705\n" + " ExtCommunities: Type(64):SubType(2):AS(3):Value(4)\n" + " BestPath Rejection Reason: Router-Id, Filter Criterion: Choose Lowest Value\n"; + + maskDateInOutput(output); + EXPECT_EQ(output, expectedOutput); +} +} // namespace facebook::fboss diff --git a/fboss/cli/fboss2/test/CmdShowBgpTablePrefixTest.cpp b/fboss/cli/fboss2/test/CmdShowBgpTablePrefixTest.cpp new file mode 100644 index 0000000000000..d722186175228 --- /dev/null +++ b/fboss/cli/fboss2/test/CmdShowBgpTablePrefixTest.cpp @@ -0,0 +1,297 @@ +/* + * 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 +#include +#include +#include +#include // NOLINT(misc-include-cleaner) +#include +#include +#include +#include "fboss/cli/fboss2/test/CmdHandlerTestBase.h" + +#include "fboss/cli/fboss2/commands/show/bgp/table/CmdShowBgpTablePrefix.h" +#include "fboss/cli/fboss2/test/CmdBgpTestUtils.h" +#include "neteng/fboss/bgp/if/gen-cpp2/bgp_thrift_types.h" +#ifndef IS_OSS +#include "nettools/common/TestUtils.h" +#endif + +using namespace ::testing; +using namespace facebook::neteng::fboss::bgp::thrift; +namespace facebook::fboss { +class CmdShowBgpTablePrefixTestFixture : public CmdHandlerTestBase { + public: + std::vector queriedEntry_; + const std::string kPrefixToQuery = "8.0.0.0/32"; + + void SetUp() override { + CmdHandlerTestBase::SetUp(); + queriedEntry_ = { + buildEntry(kPrefixToQuery, kNextHop, kPeerId, kPeerDescription)}; + } + + void setupConfig() { + EXPECT_CALL(getMockBgp(), getRunningConfig(_)) + .WillRepeatedly(Invoke([&](std::string& config) { + // clang-format off + folly::dynamic value = folly::dynamic::object + ("communities", + folly::dynamic::array( + folly::dynamic::object("name", "FABRIC_POD_RSW_LOOP") + ("description", "rsw loopback") + ("communities", folly::dynamic::array("65527:12705")) + ) + ) + ("localprefs", + folly::dynamic::array( + folly::dynamic::object("localpref", 20) + ("name", "LOCALPREF_CTRL_BACKUP") + ("description", "low-priority supplementary/backup routes from bgp controller"), + folly::dynamic::object("localpref", 25) + ("name", "LOCALPREF_DEPRIO") + ("description", "deprioritized local preference value")) + ); + // clang-format on + config = folly::toPrettyJson(value); + })); + } + + TBgpPath buildFixedPath( + const std::string& peerId, + const std::string& nextHop, + const bool isBestPath = false) { + // Path created will have peerId as the peerDescription too. + return buildPath( + kPrefixToQuery, + nextHop, + peerId, // peerId + peerId, // peerDescription + true, // setCommunity + true, // setAsPath + true, // setExtCommunity + std::nullopt, // clusterList + kOriginatorId, // originatorId + false, // setPolicy + std::nullopt, // pathNextHopWeight + isBestPath, + true, // setMed + true, // setRecvPathId + true, // setWeight + true // setIgpCost + ); + } +}; + +TEST_F(CmdShowBgpTablePrefixTestFixture, queryClient) { + setupMockedBgpServer(); + EXPECT_CALL(getMockBgp(), getRibPrefix(_, _)) + .WillOnce(Invoke( + [&](std::vector& entries, std::unique_ptr) { + entries = queriedEntry_; + })); + + auto result = + CmdShowBgpTablePrefix().queryClient(localhost(), {kPrefixToQuery}); + EXPECT_THRIFT_EQ_VECTOR(*result.tRibEntries(), queriedEntry_); +} + +TEST_F(CmdShowBgpTablePrefixTestFixture, queryClientWithInvalidPrefix) { + const std::string invalidPrefix = "1.1.1.1/32"; + setupMockedBgpServer(); + EXPECT_CALL(getMockBgp(), getRibPrefix(_, _)) + .WillOnce(Invoke([&](std::vector& entries, + std::unique_ptr) { entries = {}; })); + + auto result = + CmdShowBgpTablePrefix().queryClient(localhost(), {invalidPrefix}); + EXPECT_THAT(*result.tRibEntries(), IsEmpty()); +} + +TEST_F(CmdShowBgpTablePrefixTestFixture, printOutput) { + // Setup. + setupMockedBgpServer(); + setupConfig(); + + // Create TRibEntry (mocked output @queriedEntry_) from getRibEntries. + TRibEntryWithHost tRibEntryWithHost; + tRibEntryWithHost.tRibEntries() = queriedEntry_; + tRibEntryWithHost.host() = localhost().getName(); + tRibEntryWithHost.oobName() = localhost().getOobName(); + tRibEntryWithHost.ip() = localhost().getIpStr(); + + std::stringstream ss; + CmdShowBgpTablePrefix().printOutput(tRibEntryWithHost, ss); + std::string output = ss.str(); + // Expect one selected path. + std::string expectedOutput = kRibEntryMarkersHeader + + "\n> 8.0.0.0/32, Selected 1/1 paths\n" + "*@ from 1.2.3.4 (one.two.three.four) via 8.0.0.1 | LBW: None | Origin: INCOMPLETE | LP: DEPRIO/25 | ASP: 65301 | LM: # | NH Weight: N/A | MED: 10 | ID: 5 (rcvd) 6 (sent) | Weight: 20 | IgpCost: 100" + "\n Router/Originator: 2.2.2.3 | ClusterList: [1.1.1.2]\n" + " Communities: FABRIC_POD_RSW_LOOP/65527:12705\n" + " ExtCommunities: Type(64):SubType(2):AS(3):Value(4)\n" + " BestPath Rejection Reason: Router-Id, Filter Criterion: Choose Lowest Value\n"; + + maskDateInOutput(output); + EXPECT_EQ(output, expectedOutput); +} + +TEST_F(CmdShowBgpTablePrefixTestFixture, PrintOutput_OnlyDefaultPaths) { + // Setup. + setupMockedBgpServer(); + setupConfig(); + + // Create TRibEntry with 2 default paths. + TRibEntryWithHost tRibEntryWithHost; + TRibEntry entry = buildEntry( + kPrefixToQuery, + kNextHop, // nextHop + { + {kDefaultGroupName, + { + buildFixedPath( + "1.2.3.2", // peerId + kNextHop), + buildFixedPath( + "1.2.3.3", // peerId + kNextHop), + }}, + }, // paths map + true /* omitBestPath */); + tRibEntryWithHost.tRibEntries()->push_back(std::move(entry)); + tRibEntryWithHost.host() = localhost().getName(); + tRibEntryWithHost.oobName() = localhost().getOobName(); + tRibEntryWithHost.ip() = localhost().getIpStr(); + + // Show output. + std::stringstream ss; + CmdShowBgpTablePrefix().printOutput(tRibEntryWithHost, ss); + std::string output = ss.str(); + + // No paths should be marked with * or @ as we do not have any best paths. + std::string expectedOutput = kRibEntryMarkersHeader + + "\n> 8.0.0.0/32, Selected 0/2 paths\n" + // path 1 + " from 1.2.3.2 (1.2.3.2) via 8.0.0.1 | LBW: None | Origin: INCOMPLETE | LP: DEPRIO/25 | ASP: 65301 | LM: # | NH Weight: N/A | MED: 10 | ID: 5 (rcvd) 6 (sent) | Weight: 20 | IgpCost: 100" + "\n Router/Originator: 2.2.2.3 | ClusterList: []\n" + " Communities: FABRIC_POD_RSW_LOOP/65527:12705\n" + " ExtCommunities: Type(64):SubType(2):AS(3):Value(4)\n" + " BestPath Rejection Reason: Router-Id, Filter Criterion: Choose Lowest Value\n" + // path 2 + " from 1.2.3.3 (1.2.3.3) via 8.0.0.1 | LBW: None | Origin: INCOMPLETE | LP: DEPRIO/25 | ASP: 65301 | LM: # | NH Weight: N/A | MED: 10 | ID: 5 (rcvd) 6 (sent) | Weight: 20 | IgpCost: 100" + "\n Router/Originator: 2.2.2.3 | ClusterList: []\n" + " Communities: FABRIC_POD_RSW_LOOP/65527:12705\n" + " ExtCommunities: Type(64):SubType(2):AS(3):Value(4)\n" + " BestPath Rejection Reason: Router-Id, Filter Criterion: Choose Lowest Value\n"; + + maskDateInOutput(output); + EXPECT_EQ(output, expectedOutput); +} + +TEST_F( + CmdShowBgpTablePrefixTestFixture, + PrintOutput_SelectBestPathWithIsBestPath) { + // Setup. + setupMockedBgpServer(); + setupConfig(); + + // Create TRibEntry with 3 paths; 2 as best paths, 1 as default path. + TRibEntryWithHost tRibEntryWithHost; + TRibEntry entry = buildEntry( + kPrefixToQuery, + kNextHop, // nextHop + { + {kBestGroupName, + { + buildFixedPath( + "1.2.3.2", // peerId + kNextHop, + true), // isBestPath + buildFixedPath("1.2.3.3" /* peerId */, kNextHop), + }}, + {kDefaultGroupName, + { + buildFixedPath("1.2.3.4" /* peerId */, kNextHop), + }}, + }, // paths map + false); // omitBestPath + tRibEntryWithHost.tRibEntries()->push_back(std::move(entry)); + tRibEntryWithHost.host() = localhost().getName(); + tRibEntryWithHost.oobName() = localhost().getOobName(); + tRibEntryWithHost.ip() = localhost().getIpStr(); + + // Show output. + std::stringstream ss; + CmdShowBgpTablePrefix().printOutput(tRibEntryWithHost, ss); + std::string output = ss.str(); + + // When is_best_path is set on TBgpPath, we indicate this path is selected. + // Only 1.2.3.2 should be selected as the very best entry (@). + std::string expectedOutput = kRibEntryMarkersHeader + + "\n> 8.0.0.0/32, Selected 2/3 paths\n" + // SELECTED path 1 (best path group) + "*@ from 1.2.3.2 (1.2.3.2) via 8.0.0.1 | LBW: None | Origin: INCOMPLETE | LP: DEPRIO/25 | ASP: 65301 | LM: # | NH Weight: N/A | MED: 10 | ID: 5 (rcvd) 6 (sent) | Weight: 20 | IgpCost: 100" + "\n Router/Originator: 2.2.2.3 | ClusterList: []\n" + " Communities: FABRIC_POD_RSW_LOOP/65527:12705\n" + " ExtCommunities: Type(64):SubType(2):AS(3):Value(4)\n" + " BestPath Rejection Reason: Router-Id, Filter Criterion: Choose Lowest Value\n" + // path 2 (best path group) + "* from 1.2.3.3 (1.2.3.3) via 8.0.0.1 | LBW: None | Origin: INCOMPLETE | LP: DEPRIO/25 | ASP: 65301 | LM: # | NH Weight: N/A | MED: 10 | ID: 5 (rcvd) 6 (sent) | Weight: 20 | IgpCost: 100" + "\n Router/Originator: 2.2.2.3 | ClusterList: []\n" + " Communities: FABRIC_POD_RSW_LOOP/65527:12705\n" + " ExtCommunities: Type(64):SubType(2):AS(3):Value(4)\n" + " BestPath Rejection Reason: Router-Id, Filter Criterion: Choose Lowest Value\n" + // path 3 (default path group) + " from 1.2.3.4 (1.2.3.4) via 8.0.0.1 | LBW: None | Origin: INCOMPLETE | LP: DEPRIO/25 | ASP: 65301 | LM: # | NH Weight: N/A | MED: 10 | ID: 5 (rcvd) 6 (sent) | Weight: 20 | IgpCost: 100" + "\n Router/Originator: 2.2.2.3 | ClusterList: []\n" + " Communities: FABRIC_POD_RSW_LOOP/65527:12705\n" + " ExtCommunities: Type(64):SubType(2):AS(3):Value(4)\n" + " BestPath Rejection Reason: Router-Id, Filter Criterion: Choose Lowest Value\n"; + + maskDateInOutput(output); + EXPECT_EQ(output, expectedOutput); +} + +TEST_F(CmdShowBgpTablePrefixTestFixture, PrintOutput_PathSelectionPending) { + // Test that path_selection_pending indicator is displayed when set + setupMockedBgpServer(); + setupConfig(); + + // Create TRibEntry with path_selection_pending = true + TRibEntryWithHost tRibEntryWithHost; + TRibEntry entry = + buildEntry(kPrefixToQuery, kNextHop, kPeerId, kPeerDescription); + entry.path_selection_pending() = true; + tRibEntryWithHost.tRibEntries()->push_back(std::move(entry)); + tRibEntryWithHost.host() = localhost().getName(); + tRibEntryWithHost.oobName() = localhost().getOobName(); + tRibEntryWithHost.ip() = localhost().getIpStr(); + + std::stringstream ss; + CmdShowBgpTablePrefix().printOutput(tRibEntryWithHost, ss); + std::string output = ss.str(); + + // Verify that the % marker is in the output when path selection is pending. + // This indicates the path-selection results displayed are in a transient + // state - wait for the final result after the marker is cleared. + std::string expectedOutput = kRibEntryMarkersHeader + + "\n>% 8.0.0.0/32, Selected 1/1 paths\n" + "*@ from 1.2.3.4 (one.two.three.four) via 8.0.0.1 | LBW: None | Origin: INCOMPLETE | LP: DEPRIO/25 | ASP: 65301 | LM: # | NH Weight: N/A | MED: 10 | ID: 5 (rcvd) 6 (sent) | Weight: 20 | IgpCost: 100" + "\n Router/Originator: 2.2.2.3 | ClusterList: [1.1.1.2]\n" + " Communities: FABRIC_POD_RSW_LOOP/65527:12705\n" + " ExtCommunities: Type(64):SubType(2):AS(3):Value(4)\n" + " BestPath Rejection Reason: Router-Id, Filter Criterion: Choose Lowest Value\n"; + + maskDateInOutput(output); + EXPECT_EQ(output, expectedOutput); +} + +} // namespace facebook::fboss diff --git a/fboss/cli/fboss2/test/CmdShowBgpTableTest.cpp b/fboss/cli/fboss2/test/CmdShowBgpTableTest.cpp new file mode 100644 index 0000000000000..f8dba5e05ea8b --- /dev/null +++ b/fboss/cli/fboss2/test/CmdShowBgpTableTest.cpp @@ -0,0 +1,219 @@ +/* + * 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 +#include +#include +#include +#include // NOLINT(misc-include-cleaner) +#include +#include "fboss/cli/fboss2/test/CmdHandlerTestBase.h" + +#include "configerator/structs/neteng/bgp_policy/thrift/gen-cpp2/bgp_policy_types.h" // NOLINT(misc-include-cleaner) +#include "fboss/cli/fboss2/commands/show/bgp/table/CmdShowBgpTable.h" +#include "fboss/cli/fboss2/test/CmdBgpTestUtils.h" +#include "neteng/fboss/bgp/if/gen-cpp2/bgp_thrift_types.h" +#ifndef IS_OSS +#include "nettools/common/TestUtils.h" +#endif + +using namespace ::testing; +using namespace facebook::neteng::fboss::bgp::thrift; +using namespace facebook::bgp::bgp_policy; +namespace facebook::fboss { +class CmdShowBgpTableTestFixture : public CmdHandlerTestBase { + public: + std::vector entriesIPv4_; + std::vector entriesIPv6_; + std::vector combinedEntries_; + + void SetUp() override { + CmdHandlerTestBase::SetUp(); + entriesIPv4_ = {buildEntry()}; + entriesIPv6_ = { + buildEntry("2001::1/64", "2001::2", "2001::3", "two00one::three")}; + combineEntries(); + } + + void combineEntries() { + this->combinedEntries_ = this->entriesIPv4_; + this->combinedEntries_.insert( + combinedEntries_.end(), entriesIPv6_.begin(), entriesIPv6_.end()); + } +}; + +TEST_F(CmdShowBgpTableTestFixture, queryClient) { + setupMockedBgpServer(); + EXPECT_CALL(getMockBgp(), getRibEntries(_, TBgpAfi::AFI_IPV4)) + .WillOnce(Invoke([&](std::vector& entries, TBgpAfi) { + entries = entriesIPv4_; + })); + + EXPECT_CALL(getMockBgp(), getRibEntries(_, TBgpAfi::AFI_IPV6)) + .WillOnce(Invoke([&](std::vector& entries, TBgpAfi) { + entries = entriesIPv6_; + })); + auto result = CmdShowBgpTable().queryClient(localhost()); + EXPECT_THRIFT_EQ_VECTOR(*result.tRibEntries(), combinedEntries_); +} + +TEST_F(CmdShowBgpTableTestFixture, printOutput) { + combinedEntries_.emplace_back( + buildEntry("8.0.0.1/32", "8.0.0.2", "8.1.2.8", "eight.one.two.eight")); + combinedEntries_.emplace_back( + buildEntry("2001::3/64", "2001::4", "2001::5", "two00one::five")); + + setupMockedBgpServer(); + EXPECT_CALL(getMockBgp(), getRunningConfig(_)) + .WillRepeatedly(Invoke([&](std::string& config) { + // clang-format off + folly::dynamic value = folly::dynamic::object + ("communities", + folly::dynamic::array( + folly::dynamic::object("name", "FABRIC_POD_RSW_LOOP") + ("description", "rsw loopback") + ("communities", folly::dynamic::array("65527:12705")) + ) + ) + ("localprefs", + folly::dynamic::array( + folly::dynamic::object("localpref", 20) + ("name", "LOCALPREF_CTRL_BACKUP") + ("description", "low-priority supplementary/backup routes from bgp controller"), + folly::dynamic::object("localpref", 25) + ("name", "LOCALPREF_DEPRIO") + ("description", "deprioritized local preference value")) + ); + // clang-format on + config = folly::toPrettyJson(value); + })); + std::stringstream ss; + TRibEntryWithHost tRibEntryWithHost; + tRibEntryWithHost.tRibEntries() = combinedEntries_; + tRibEntryWithHost.host() = localhost().getName(); + tRibEntryWithHost.oobName() = localhost().getOobName(); + tRibEntryWithHost.ip() = localhost().getIpStr(); + CmdShowBgpTable().printOutput(tRibEntryWithHost, ss); + std::string output = ss.str(); + + std::string expectedOutput = kRibEntryMarkersHeader + + "\n> 8.0.0.0/32, Selected 1/1 paths\n" + "*@ from 1.2.3.4 (one.two.three.four) via 8.0.0.1 | LBW: None | Origin: INCOMPLETE | LP: DEPRIO/25 | ASP: 65301 | LM: # | NH Weight: N/A | MED: 10 | ID: 5 (rcvd) 6 (sent) | Weight: 20 | IgpCost: 100\n" + "\n> 8.0.0.1/32, Selected 1/1 paths\n" + "*@ from 8.1.2.8 (eight.one.two.eight) via 8.0.0.2 | LBW: None | Origin: INCOMPLETE | LP: DEPRIO/25 | ASP: 65301 | LM: # | NH Weight: N/A | MED: 10 | ID: 5 (rcvd) 6 (sent) | Weight: 20 | IgpCost: 100\n" + "\n> 2001::1/64, Selected 1/1 paths\n" + "*@ from 2001::3 (two00one::three) via 2001::2 | LBW: None | Origin: INCOMPLETE | LP: DEPRIO/25 | ASP: 65301 | LM: # | NH Weight: N/A | MED: 10 | ID: 5 (rcvd) 6 (sent) | Weight: 20 | IgpCost: 100\n" + "\n> 2001::3/64, Selected 1/1 paths\n" + "*@ from 2001::5 (two00one::five) via 2001::4 | LBW: None | Origin: INCOMPLETE | LP: DEPRIO/25 | ASP: 65301 | LM: # | NH Weight: N/A | MED: 10 | ID: 5 (rcvd) 6 (sent) | Weight: 20 | IgpCost: 100\n"; + + maskDateInOutput(output); + EXPECT_EQ(output, expectedOutput); +} + +#ifndef IS_OSS +// Test the CPS annotation (Meta-internal feature) +TEST_F(CmdShowBgpTableTestFixture, printCPSOutput) { + // PathSelector contains the info for the active criteria + TPathSelector activeCriteria1; + activeCriteria1.bgp_native_path_selection_min_nexthop() = 20; + auto pathOverriddenEntry1 = buildEntry( + "8.0.0.1/32", + "8.0.0.2", + "8.1.2.8", + "eight.one.two.eight", + std::nullopt, + true /* omit best path */, + true /* omit best paths */); + pathOverriddenEntry1.active_cps_criteria() = std::move(activeCriteria1); + combinedEntries_.push_back(std::move(pathOverriddenEntry1)); + auto pathOverriddenEntry3 = buildEntry( + "8.0.0.3/32", + "8.0.0.2", + "8.1.2.8", + "eight.one.two.eight", + std::nullopt, + true /* omit best path */); + TPathSelector activeCriteria3; + activeCriteria3.bgp_native_path_selection_min_nexthop() = 20; + activeCriteria3.relax_bgp_native_path_selection_min_nexthop() = true; + pathOverriddenEntry3.active_cps_criteria() = std::move(activeCriteria3); + combinedEntries_.push_back(std::move(pathOverriddenEntry3)); + + TPathSelector activeCriteria2; + TPathSelectionCriteria tCriteria; + tCriteria.min_nexthop() = 13; + activeCriteria2.criteria_list()->push_back(std::move(tCriteria)); + auto pathOverriddenEntry2 = + buildEntry("2001::3/64", "2001::4", "2001::5", "two00one::five"); + pathOverriddenEntry2.active_cps_criteria() = std::move(activeCriteria2); + combinedEntries_.push_back(std::move(pathOverriddenEntry2)); + setupMockedBgpServer(); + EXPECT_CALL(getMockBgp(), getRunningConfig(_)) + .WillRepeatedly(Invoke([&](std::string& config) { + // clang-format off + folly::dynamic value = folly::dynamic::object + ("communities", + folly::dynamic::array( + folly::dynamic::object("name", "FABRIC_POD_RSW_LOOP") + ("description", "rsw loopback") + ("communities", folly::dynamic::array("65527:12705")) + ) + ) + ("localprefs", + folly::dynamic::array( + folly::dynamic::object("localpref", 20) + ("name", "LOCALPREF_CTRL_BACKUP") + ("description", "low-priority supplementary/backup routes from bgp controller"), + folly::dynamic::object("localpref", 25) + ("name", "LOCALPREF_DEPRIO") + ("description", "deprioritized local preference value")) + ); + // clang-format on + config = folly::toPrettyJson(value); + })); + std::stringstream ss; + TRibEntryWithHost tRibEntryWithHost; + tRibEntryWithHost.tRibEntries() = combinedEntries_; + tRibEntryWithHost.host() = localhost().getName(); + tRibEntryWithHost.oobName() = localhost().getOobName(); + tRibEntryWithHost.ip() = localhost().getIpStr(); + CmdShowBgpTable().printOutput(tRibEntryWithHost, ss); + std::string output = ss.str(); + + std::string expectedOutput = kRibEntryMarkersHeader + + "\n> 8.0.0.0/32, Selected 1/1 paths\n" + "*@ from 1.2.3.4 (one.two.three.four) via 8.0.0.1 | LBW: None | Origin: INCOMPLETE | LP: DEPRIO/25 | ASP: 65301 | LM: # | NH Weight: N/A | MED: 10 | ID: 5 (rcvd) 6 (sent) | Weight: 20 | IgpCost: 100\n" + "\n> 8.0.0.1/32, Selected 0/1 paths\n" + "Path overridden by CPS:\n" + " prefix: 8.0.0.1/32\n" + " default BGP multipath selector\n" + " BGP native min nexthop: 20\n" + " from 8.1.2.8 (eight.one.two.eight) via 8.0.0.2 | LBW: None | Origin: INCOMPLETE | LP: DEPRIO/25 | ASP: 65301 | LM: # | NH Weight: N/A | MED: 10 | ID: 5 (rcvd) 6 (sent) | Weight: 20 | IgpCost: 100\n" + "\n> 8.0.0.3/32, Selected 1/1 paths\n" + "Path overridden by CPS:\n" + " prefix: 8.0.0.3/32\n" + " default BGP multipath selector\n" + " BGP native min nexthop: 20, relaxed: true\n" + "* from 8.1.2.8 (eight.one.two.eight) via 8.0.0.2 | LBW: None | Origin: INCOMPLETE | LP: DEPRIO/25 | ASP: 65301 | LM: # | NH Weight: N/A | MED: 10 | ID: 5 (rcvd) 6 (sent) | Weight: 20 | IgpCost: 100\n" + "\n> 2001::1/64, Selected 1/1 paths\n" + "*@ from 2001::3 (two00one::three) via 2001::2 | LBW: None | Origin: INCOMPLETE | LP: DEPRIO/25 | ASP: 65301 | LM: # | NH Weight: N/A | MED: 10 | ID: 5 (rcvd) 6 (sent) | Weight: 20 | IgpCost: 100\n" + "\n> 2001::3/64, Selected 1/1 paths\n" + "Path overridden by CPS:\n" + " prefix: 2001::3/64\n" + " active criteria:\n" + " path_matchers:\n" + " min nexthop: 13\n" + "*@ from 2001::5 (two00one::five) via 2001::4 | LBW: None | Origin: INCOMPLETE | LP: DEPRIO/25 | ASP: 65301 | LM: # | NH Weight: N/A | MED: 10 | ID: 5 (rcvd) 6 (sent) | Weight: 20 | IgpCost: 100\n"; + + maskDateInOutput(output); + EXPECT_EQ(output, expectedOutput); +} +#endif // IS_OSS +} // namespace facebook::fboss diff --git a/fboss/cli/fboss2/test/CmdShowConfigTestUtils.cpp b/fboss/cli/fboss2/test/CmdShowConfigTestUtils.cpp new file mode 100644 index 0000000000000..97f5f57461ff8 --- /dev/null +++ b/fboss/cli/fboss2/test/CmdShowConfigTestUtils.cpp @@ -0,0 +1,74 @@ +/* + * 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. + * + */ + +#ifndef IS_OSS +#include + +#include +#include "fboss/cli/fboss2/test/CmdShowConfigTestUtils.h" + +namespace facebook::fboss::show_config_utils { + +std::string getAgentRunningConfig() { + folly::dynamic agentRunningConfig = folly::dynamic::object("version", 0)( + "switchSettings", + folly::dynamic::object("l2LearningMode", 1)("qcmEnable", false)); + + return folly::toJson(agentRunningConfig); +} + +std::string getBgpRunningConfig() { + folly::dynamic bgpRunningConfig = folly::dynamic::object( + "router_id", "10.170.224.128")("local_as", 65401)("local_as_4_byte", 0); + + return folly::toJson(bgpRunningConfig); +} + +std::map getConfigVersion() { + std::map configVer{ + {"base_hash", "ba6b9e85e2d4bdc152b5fa11d17eed58bd90b16e"}, + {"base_version_hash", "08527b0ff4c5690d9e2d764b2dd69a3b89ee786a"}}; + + return configVer; +} + +std::string getConfigVersionOutput() { + std::stringstream buffer; + auto configVerMap = getConfigVersion(); + for (auto& kv : configVerMap) { + buffer << kv.first << ": " << kv.second << std::endl; + } + + return buffer.str(); +} + +std::vector<::fboss::coop::CoopConfigPatcher> createCoopConfigPatchers() { + ::fboss::coop::CoopConfigPatcher configPatcher1; + + configPatcher1.name() = "name1"; + configPatcher1.description() = "description1"; + configPatcher1.owner() = "owner1"; + + ::fboss::coop::CoopConfigPatcher configPatcher2; + configPatcher2.name() = "name2"; + configPatcher2.description() = "description2"; + configPatcher2.owner() = "owner2"; + return {configPatcher1, configPatcher2}; +} + +folly::dynamic getCoopPatcher() { + std::string jsonStr = + "[{\"description\" : \"description1\",\"name\" : \"name1\",\"owner\" : \"owner1\",\"persistent\" : false,\"patcher\" : {}},{\"description\" : \"description2\",\"name\" : \"name2\",\"owner\" : \"owner2\",\"persistent\" : false,\"patcher\" : {}}]"; + auto coopPatcher = folly::parseJson(jsonStr); + + return coopPatcher; +} +} // namespace facebook::fboss::show_config_utils +#endif // IS_OSS diff --git a/fboss/cli/fboss2/test/CmdShowConfigTestUtils.h b/fboss/cli/fboss2/test/CmdShowConfigTestUtils.h new file mode 100644 index 0000000000000..d140b21c5b58a --- /dev/null +++ b/fboss/cli/fboss2/test/CmdShowConfigTestUtils.h @@ -0,0 +1,28 @@ +/* + * 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 +#ifndef IS_OSS +#include + +#include "neteng/fboss/coop/if/gen-cpp2/CoopService.h" +#include "neteng/fboss/coop/if/gen-cpp2/coop_constants.h" + +namespace facebook::fboss::show_config_utils { +std::string getAgentRunningConfig(); +std::string getBgpRunningConfig(); + +std::map getConfigVersion(); +std::string getConfigVersionOutput(); + +std::vector<::fboss::coop::CoopConfigPatcher> createCoopConfigPatchers(); +folly::dynamic getCoopPatcher(); +} // namespace facebook::fboss::show_config_utils +#endif // IS_OSS diff --git a/fboss/cli/fboss2/test/CmdShowPortTest.cpp b/fboss/cli/fboss2/test/CmdShowPortTest.cpp index da91f3edb3426..d8770ea0b56ce 100644 --- a/fboss/cli/fboss2/test/CmdShowPortTest.cpp +++ b/fboss/cli/fboss2/test/CmdShowPortTest.cpp @@ -15,8 +15,8 @@ #include "fboss/cli/fboss2/test/CmdHandlerTestBase.h" #ifndef IS_OSS -#include "configerator/structs/neteng/fboss/bgp/gen-cpp2/bgp_config_types.h" #include "neteng/fboss/bgp/if/gen-cpp2/TBgpService.h" +#include "neteng/fboss/bgp/if/gen-cpp2/bgp_thrift_types.h" using namespace facebook::neteng::fboss::bgp::thrift; #endif diff --git a/fboss/cli/fboss2/test/MockClients.h b/fboss/cli/fboss2/test/MockClients.h index 86c30df8d688c..2fb05f542b60f 100644 --- a/fboss/cli/fboss2/test/MockClients.h +++ b/fboss/cli/fboss2/test/MockClients.h @@ -55,7 +55,7 @@ class MockAgentCounters : public AgentCountersIf { MOCK_METHOD(void, getAgentCounters, (hostInfo, int, FbSwHwAgentCounters&)); }; -class MockFbossCtrlAgent : public FbossCtrlSvIf { +class MockFbossCtrlAgent : public apache::thrift::ServiceHandler { public: MOCK_METHOD(void, reloadConfig, ()); MOCK_METHOD(void, getAclTableGroup, (AclTableThrift&)); @@ -139,7 +139,8 @@ class MockFbossCtrlAgent : public FbossCtrlSvIf { MOCK_METHOD(void, getAllInterfaces, (InterfaceDetailMap)); }; -class MockFbossQsfpService : public QsfpServiceSvIf { +class MockFbossQsfpService + : public apache::thrift::ServiceHandler { public: using transceiverEntries = std::map&; diff --git a/fboss/cli/fboss2/test/bgp/MockBgpClient.h b/fboss/cli/fboss2/test/bgp/MockBgpClient.h new file mode 100644 index 0000000000000..32f62c6c5100c --- /dev/null +++ b/fboss/cli/fboss2/test/bgp/MockBgpClient.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 + +#include "configerator/structs/neteng/bgp_policy/thrift/gen-cpp2/bgp_policy_types.h" // NOLINT(misc-include-cleaner) +#include "configerator/structs/neteng/fboss/bgp/if/gen-cpp2/bgp_attr_types.h" +#include "neteng/fboss/bgp/if/gen-cpp2/TBgpService.h" +#include "neteng/fboss/bgp/if/gen-cpp2/bgp_thrift_types.h" +#include "neteng/fboss/bgp/if/gen-cpp2/policy_thrift_types.h" + +namespace facebook::fboss { +using namespace facebook::neteng::fboss::bgp::thrift; +using namespace facebook::neteng::fboss::bgp_attr; +using namespace facebook::bgp::bgp_policy; +using facebook::neteng::routing::policy::thrift::TPolicyStats; + +class MockBgpClient : public apache::thrift::ServiceHandler { + public: + MOCK_METHOD(void, getBgpSessions, (std::vector&)); + MOCK_METHOD(void, getBgpLocalConfig, (TBgpLocalConfig&)); + MOCK_METHOD(void, getDrainState, (TBgpDrainState&)); + MOCK_METHOD(void, getAttributeStats, (TAttributeStats&)); + MOCK_METHOD(void, getEntryStats, (TEntryStats&)); + MOCK_METHOD(void, getPolicyStats, (TPolicyStats&)); + MOCK_METHOD(void, getOriginatedRoutes, (std::vector&)); + MOCK_METHOD( + void, + getBgpNeighbors, + (std::vector&, std::unique_ptr>)); + MOCK_METHOD(void, getPeerEgressStats, (std::vector&)); + + using networks = std::map>&; + MOCK_METHOD( + void, + getPrefilterReceivedNetworks2, + (networks, std::unique_ptr)); + MOCK_METHOD( + void, + getPostfilterReceivedNetworks2, + (networks, std::unique_ptr)); + MOCK_METHOD( + void, + getPrefilterAdvertisedNetworks2, + (networks, std::unique_ptr)); + MOCK_METHOD( + void, + getPostfilterAdvertisedNetworks2, + (networks, std::unique_ptr)); + MOCK_METHOD(void, getRibEntries, (std::vector&, TBgpAfi)); + MOCK_METHOD(void, getChangeListEntries, (std::vector&, TBgpAfi)); + MOCK_METHOD(void, getShadowRibEntries, (std::vector&, TBgpAfi)); + MOCK_METHOD(void, getRunningConfig, (std::string&)); + MOCK_METHOD( + void, + getRibPrefix, + (std::vector&, std::unique_ptr)); + MOCK_METHOD( + void, + getRibSubprefixes, + (std::vector&, std::unique_ptr)); + MOCK_METHOD( + void, + getRibEntriesForCommunity, + (std::vector&, TBgpAfi, std::unique_ptr)); + MOCK_METHOD(void, getBgpStreamSessions, (std::vector&)); + MOCK_METHOD( + void, + getSubscriberNetworkInfo, + (networks, int32_t, std::unique_ptr)); + MOCK_METHOD(void, setDebugLevel, (TBgpDebugLevel)); +}; +} // namespace facebook::fboss diff --git a/fboss/cli/fboss2/test/config/BgpConfigSessionTest.cpp b/fboss/cli/fboss2/test/config/BgpConfigSessionTest.cpp index 0b0923a1c92a8..014360fb4f924 100644 --- a/fboss/cli/fboss2/test/config/BgpConfigSessionTest.cpp +++ b/fboss/cli/fboss2/test/config/BgpConfigSessionTest.cpp @@ -2,8 +2,9 @@ #include "fboss/cli/fboss2/test/config/CmdConfigTestBase.h" -#include +#include // NOLINT(misc-include-cleaner) #include +#include "fboss/cli/fboss2/test/CmdHandlerTestBase.h" // NOLINT(misc-include-cleaner) #include #include diff --git a/fboss/cli/fboss2/utils/CmdClientUtils.cpp b/fboss/cli/fboss2/utils/CmdClientUtils.cpp index e56953f4b4a0d..a0542ba6efcb3 100644 --- a/fboss/cli/fboss2/utils/CmdClientUtils.cpp +++ b/fboss/cli/fboss2/utils/CmdClientUtils.cpp @@ -64,6 +64,13 @@ createClient(const HostInfo& hostInfo) { return utils::createFanServiceClient(hostInfo); } +template <> +std::unique_ptr< + apache::thrift::Client> +createClient(const HostInfo& hostInfo) { + return utils::createBgpClient(hostInfo); +} + MultiSwitchRunState getMultiSwitchRunState(const HostInfo& hostInfo) { auto client = utils::createClient>(hostInfo); diff --git a/fboss/cli/fboss2/utils/CmdClientUtils.h b/fboss/cli/fboss2/utils/CmdClientUtils.h index fe71b2db57fd8..7a563669e807c 100644 --- a/fboss/cli/fboss2/utils/CmdClientUtils.h +++ b/fboss/cli/fboss2/utils/CmdClientUtils.h @@ -18,6 +18,7 @@ #include "fboss/led_service/if/gen-cpp2/LedService.h" #include "fboss/platform/fan_service/if/gen-cpp2/FanService.h" #include "fboss/qsfp_service/if/gen-cpp2/QsfpService.h" +#include "neteng/fboss/bgp/if/gen-cpp2/TBgpService.h" namespace facebook::fboss::utils { @@ -56,6 +57,10 @@ std::unique_ptr< apache::thrift::Client> createFanServiceClient(const HostInfo& hostInfo); +std::unique_ptr< + apache::thrift::Client> +createBgpClient(const HostInfo& hostInfo); + MultiSwitchRunState getMultiSwitchRunState(const HostInfo& hostInfo); int getNumHwSwitches(const HostInfo& hostInfo); bool isMultiSwitchEnabled(const HostInfo& hostInfo); diff --git a/fboss/cli/fboss2/utils/CmdUtilsCommon.cpp b/fboss/cli/fboss2/utils/CmdUtilsCommon.cpp index 78d21dc7e1f8f..4de00eebdb927 100644 --- a/fboss/cli/fboss2/utils/CmdUtilsCommon.cpp +++ b/fboss/cli/fboss2/utils/CmdUtilsCommon.cpp @@ -21,8 +21,10 @@ #include #include +#include #include -#include +#include +#include #include using folly::ByteRange; @@ -207,6 +209,13 @@ const std::string parseTimeToTimeStamp(const long& timeToParse) { #ifndef IS_OSS constexpr std::string_view kTimeFormat = "%Y-%m-%d %H:%M:%S %Z"; stringFormatTimestamp(splitTime.tv_sec, kTimeFormat.data(), &formattedTime); +#else + // OSS: Use std::put_time instead of Meta-internal stringFormatTimestamp + std::tm tm_info; + localtime_r(&splitTime.tv_sec, &tm_info); + std::ostringstream oss; + oss << std::put_time(&tm_info, "%Y-%m-%d %H:%M:%S %Z"); + formattedTime = oss.str(); #endif // stringFormatTimestamp returns a formatted time like the following: diff --git a/fboss/cli/fboss2/utils/oss/CmdClientUtils.cpp b/fboss/cli/fboss2/utils/oss/CmdClientUtils.cpp index 969a82161dc2e..1a9f85361dd3f 100644 --- a/fboss/cli/fboss2/utils/oss/CmdClientUtils.cpp +++ b/fboss/cli/fboss2/utils/oss/CmdClientUtils.cpp @@ -14,6 +14,7 @@ #include "fboss/fsdb/if/gen-cpp2/FsdbService.h" #include "fboss/platform/fan_service/if/gen-cpp2/FanService.h" #include "fboss/qsfp_service/if/gen-cpp2/QsfpService.h" +#include "neteng/fboss/bgp/if/gen-cpp2/TBgpService.h" namespace facebook::fboss::utils { @@ -83,4 +84,12 @@ createFanServiceClient(const HostInfo& hostInfo) { facebook::fboss::platform::fan_service::FanService>>(hostInfo, port); } +std::unique_ptr< + apache::thrift::Client> +createBgpClient(const HostInfo& hostInfo) { + auto bgpPort = CmdGlobalOptions::getInstance()->getBgpThriftPort(); + return createPlaintextClient>(hostInfo, bgpPort); +} + } // namespace facebook::fboss::utils