diff --git a/cmake/CliFboss2.cmake b/cmake/CliFboss2.cmake index 851db19f95518..9fad20b32a21a 100644 --- a/cmake/CliFboss2.cmake +++ b/cmake/CliFboss2.cmake @@ -854,122 +854,23 @@ add_library(fboss2_config_lib fboss/cli/fboss2/commands/config/mac/aging_time/CmdConfigMacAgingTime.h fboss/cli/fboss2/commands/config/protocol/CmdConfigProtocol.cpp fboss/cli/fboss2/commands/config/protocol/CmdConfigProtocol.h + fboss/cli/fboss2/commands/config/protocol/bgp/BgpCliValueParsers.h + fboss/cli/fboss2/commands/config/protocol/bgp/BgpPeerAddr.h fboss/cli/fboss2/commands/config/protocol/bgp/BgpConfigSession.cpp fboss/cli/fboss2/commands/config/protocol/bgp/BgpConfigSession.h fboss/cli/fboss2/commands/config/protocol/bgp/CmdConfigProtocolBgp.cpp fboss/cli/fboss2/commands/config/protocol/bgp/CmdConfigProtocolBgp.h fboss/cli/fboss2/commands/config/protocol/bgp/global/CmdConfigProtocolBgpGlobal.cpp fboss/cli/fboss2/commands/config/protocol/bgp/global/CmdConfigProtocolBgpGlobal.h - fboss/cli/fboss2/commands/config/protocol/bgp/global/CmdConfigProtocolBgpGlobalClusterId.cpp - fboss/cli/fboss2/commands/config/protocol/bgp/global/CmdConfigProtocolBgpGlobalClusterId.h - fboss/cli/fboss2/commands/config/protocol/bgp/global/CmdConfigProtocolBgpGlobalConfedAsn.cpp - fboss/cli/fboss2/commands/config/protocol/bgp/global/CmdConfigProtocolBgpGlobalConfedAsn.h - fboss/cli/fboss2/commands/config/protocol/bgp/global/CmdConfigProtocolBgpGlobalHoldTime.cpp - fboss/cli/fboss2/commands/config/protocol/bgp/global/CmdConfigProtocolBgpGlobalHoldTime.h - fboss/cli/fboss2/commands/config/protocol/bgp/global/CmdConfigProtocolBgpGlobalLocalAsn.cpp - fboss/cli/fboss2/commands/config/protocol/bgp/global/CmdConfigProtocolBgpGlobalLocalAsn.h - fboss/cli/fboss2/commands/config/protocol/bgp/global/CmdConfigProtocolBgpGlobalNetwork6Add.cpp - fboss/cli/fboss2/commands/config/protocol/bgp/global/CmdConfigProtocolBgpGlobalNetwork6Add.h - fboss/cli/fboss2/commands/config/protocol/bgp/global/CmdConfigProtocolBgpGlobalRouterId.cpp - fboss/cli/fboss2/commands/config/protocol/bgp/global/CmdConfigProtocolBgpGlobalRouterId.h - fboss/cli/fboss2/commands/config/protocol/bgp/global/CmdConfigProtocolBgpGlobalSwitchLimitMaxGoldenVips.cpp - fboss/cli/fboss2/commands/config/protocol/bgp/global/CmdConfigProtocolBgpGlobalSwitchLimitMaxGoldenVips.h - fboss/cli/fboss2/commands/config/protocol/bgp/global/CmdConfigProtocolBgpGlobalSwitchLimitOverloadProtectionMode.cpp - fboss/cli/fboss2/commands/config/protocol/bgp/global/CmdConfigProtocolBgpGlobalSwitchLimitOverloadProtectionMode.h - fboss/cli/fboss2/commands/config/protocol/bgp/global/CmdConfigProtocolBgpGlobalSwitchLimitPrefixLimit.cpp - fboss/cli/fboss2/commands/config/protocol/bgp/global/CmdConfigProtocolBgpGlobalSwitchLimitPrefixLimit.h - fboss/cli/fboss2/commands/config/protocol/bgp/global/CmdConfigProtocolBgpGlobalSwitchLimitTotalPathLimit.cpp - fboss/cli/fboss2/commands/config/protocol/bgp/global/CmdConfigProtocolBgpGlobalSwitchLimitTotalPathLimit.h + fboss/cli/fboss2/commands/config/protocol/bgp/neighbor/CmdConfigProtocolBgpNeighbor.cpp + fboss/cli/fboss2/commands/config/protocol/bgp/neighbor/CmdConfigProtocolBgpNeighbor.h fboss/cli/fboss2/commands/config/protocol/bgp/peer-group/CmdConfigProtocolBgpPeerGroup.cpp fboss/cli/fboss2/commands/config/protocol/bgp/peer-group/CmdConfigProtocolBgpPeerGroup.h - fboss/cli/fboss2/commands/config/protocol/bgp/peer-group/CmdConfigProtocolBgpPeerGroupConfedPeer.cpp - fboss/cli/fboss2/commands/config/protocol/bgp/peer-group/CmdConfigProtocolBgpPeerGroupConfedPeer.h - fboss/cli/fboss2/commands/config/protocol/bgp/peer-group/CmdConfigProtocolBgpPeerGroupDescription.cpp - fboss/cli/fboss2/commands/config/protocol/bgp/peer-group/CmdConfigProtocolBgpPeerGroupDescription.h - fboss/cli/fboss2/commands/config/protocol/bgp/peer-group/CmdConfigProtocolBgpPeerGroupDisableIpv4Afi.cpp - fboss/cli/fboss2/commands/config/protocol/bgp/peer-group/CmdConfigProtocolBgpPeerGroupDisableIpv4Afi.h - fboss/cli/fboss2/commands/config/protocol/bgp/peer-group/CmdConfigProtocolBgpPeerGroupEgressPolicy.cpp - fboss/cli/fboss2/commands/config/protocol/bgp/peer-group/CmdConfigProtocolBgpPeerGroupEgressPolicy.h - fboss/cli/fboss2/commands/config/protocol/bgp/peer-group/CmdConfigProtocolBgpPeerGroupIngressPolicy.cpp - fboss/cli/fboss2/commands/config/protocol/bgp/peer-group/CmdConfigProtocolBgpPeerGroupIngressPolicy.h - fboss/cli/fboss2/commands/config/protocol/bgp/peer-group/CmdConfigProtocolBgpPeerGroupMaxRoutes.cpp - fboss/cli/fboss2/commands/config/protocol/bgp/peer-group/CmdConfigProtocolBgpPeerGroupMaxRoutes.h - fboss/cli/fboss2/commands/config/protocol/bgp/peer-group/CmdConfigProtocolBgpPeerGroupNextHopSelf.cpp - fboss/cli/fboss2/commands/config/protocol/bgp/peer-group/CmdConfigProtocolBgpPeerGroupNextHopSelf.h - fboss/cli/fboss2/commands/config/protocol/bgp/peer-group/CmdConfigProtocolBgpPeerGroupPeerTag.cpp - fboss/cli/fboss2/commands/config/protocol/bgp/peer-group/CmdConfigProtocolBgpPeerGroupPeerTag.h - fboss/cli/fboss2/commands/config/protocol/bgp/peer-group/CmdConfigProtocolBgpPeerGroupRemoteAsn.cpp - fboss/cli/fboss2/commands/config/protocol/bgp/peer-group/CmdConfigProtocolBgpPeerGroupRemoteAsn.h - fboss/cli/fboss2/commands/config/protocol/bgp/peer-group/CmdConfigProtocolBgpPeerGroupRrClient.cpp - fboss/cli/fboss2/commands/config/protocol/bgp/peer-group/CmdConfigProtocolBgpPeerGroupRrClient.h - fboss/cli/fboss2/commands/config/protocol/bgp/peer-group/CmdConfigProtocolBgpPeerGroupTimers.cpp - fboss/cli/fboss2/commands/config/protocol/bgp/peer-group/CmdConfigProtocolBgpPeerGroupTimers.h - fboss/cli/fboss2/commands/config/protocol/bgp/peer-group/CmdConfigProtocolBgpPeerGroupTimersHoldTime.cpp - fboss/cli/fboss2/commands/config/protocol/bgp/peer-group/CmdConfigProtocolBgpPeerGroupTimersHoldTime.h - fboss/cli/fboss2/commands/config/protocol/bgp/peer-group/CmdConfigProtocolBgpPeerGroupTimersKeepalive.cpp - fboss/cli/fboss2/commands/config/protocol/bgp/peer-group/CmdConfigProtocolBgpPeerGroupTimersKeepalive.h - fboss/cli/fboss2/commands/config/protocol/bgp/peer-group/CmdConfigProtocolBgpPeerGroupTimersOutDelay.cpp - fboss/cli/fboss2/commands/config/protocol/bgp/peer-group/CmdConfigProtocolBgpPeerGroupTimersOutDelay.h - fboss/cli/fboss2/commands/config/protocol/bgp/peer-group/CmdConfigProtocolBgpPeerGroupTimersWithdrawUnprogDelay.cpp - fboss/cli/fboss2/commands/config/protocol/bgp/peer-group/CmdConfigProtocolBgpPeerGroupTimersWithdrawUnprogDelay.h - fboss/cli/fboss2/commands/config/protocol/bgp/peer-group/CmdConfigProtocolBgpPeerGroupV4OverV6Nh.cpp - fboss/cli/fboss2/commands/config/protocol/bgp/peer-group/CmdConfigProtocolBgpPeerGroupV4OverV6Nh.h - fboss/cli/fboss2/commands/config/protocol/bgp/peer-group/CmdConfigProtocolBgpPeerGroupWarningLimit.cpp - fboss/cli/fboss2/commands/config/protocol/bgp/peer-group/CmdConfigProtocolBgpPeerGroupWarningLimit.h - fboss/cli/fboss2/commands/config/protocol/bgp/peer-group/CmdConfigProtocolBgpPeerGroupWarningOnly.cpp - fboss/cli/fboss2/commands/config/protocol/bgp/peer-group/CmdConfigProtocolBgpPeerGroupWarningOnly.h - fboss/cli/fboss2/commands/config/protocol/bgp/peer/CmdConfigProtocolBgpPeer.cpp - fboss/cli/fboss2/commands/config/protocol/bgp/peer/CmdConfigProtocolBgpPeer.h - fboss/cli/fboss2/commands/config/protocol/bgp/peer/CmdConfigProtocolBgpPeerAdvertiseLbw.cpp - fboss/cli/fboss2/commands/config/protocol/bgp/peer/CmdConfigProtocolBgpPeerAdvertiseLbw.h - fboss/cli/fboss2/commands/config/protocol/bgp/peer/CmdConfigProtocolBgpPeerDescription.cpp - fboss/cli/fboss2/commands/config/protocol/bgp/peer/CmdConfigProtocolBgpPeerDescription.h - fboss/cli/fboss2/commands/config/protocol/bgp/peer/CmdConfigProtocolBgpPeerDisableIpv4Afi.cpp - fboss/cli/fboss2/commands/config/protocol/bgp/peer/CmdConfigProtocolBgpPeerDisableIpv4Afi.h - fboss/cli/fboss2/commands/config/protocol/bgp/peer/CmdConfigProtocolBgpPeerEgressPolicy.cpp - fboss/cli/fboss2/commands/config/protocol/bgp/peer/CmdConfigProtocolBgpPeerEgressPolicy.h - fboss/cli/fboss2/commands/config/protocol/bgp/peer/CmdConfigProtocolBgpPeerHoldTime.cpp - fboss/cli/fboss2/commands/config/protocol/bgp/peer/CmdConfigProtocolBgpPeerHoldTime.h - fboss/cli/fboss2/commands/config/protocol/bgp/peer/CmdConfigProtocolBgpPeerIngressPolicy.cpp - fboss/cli/fboss2/commands/config/protocol/bgp/peer/CmdConfigProtocolBgpPeerIngressPolicy.h - fboss/cli/fboss2/commands/config/protocol/bgp/peer/CmdConfigProtocolBgpPeerLinkBandwidth.cpp - fboss/cli/fboss2/commands/config/protocol/bgp/peer/CmdConfigProtocolBgpPeerLinkBandwidth.h - fboss/cli/fboss2/commands/config/protocol/bgp/peer/CmdConfigProtocolBgpPeerLocalAddr.cpp - fboss/cli/fboss2/commands/config/protocol/bgp/peer/CmdConfigProtocolBgpPeerLocalAddr.h - fboss/cli/fboss2/commands/config/protocol/bgp/peer/CmdConfigProtocolBgpPeerMaxRoutes.cpp - fboss/cli/fboss2/commands/config/protocol/bgp/peer/CmdConfigProtocolBgpPeerMaxRoutes.h - fboss/cli/fboss2/commands/config/protocol/bgp/peer/CmdConfigProtocolBgpPeerNextHop4.cpp - fboss/cli/fboss2/commands/config/protocol/bgp/peer/CmdConfigProtocolBgpPeerNextHop4.h - fboss/cli/fboss2/commands/config/protocol/bgp/peer/CmdConfigProtocolBgpPeerNextHop6.cpp - fboss/cli/fboss2/commands/config/protocol/bgp/peer/CmdConfigProtocolBgpPeerNextHop6.h - fboss/cli/fboss2/commands/config/protocol/bgp/peer/CmdConfigProtocolBgpPeerNextHopSelf.cpp - fboss/cli/fboss2/commands/config/protocol/bgp/peer/CmdConfigProtocolBgpPeerNextHopSelf.h - fboss/cli/fboss2/commands/config/protocol/bgp/peer/CmdConfigProtocolBgpPeerPassive.cpp - fboss/cli/fboss2/commands/config/protocol/bgp/peer/CmdConfigProtocolBgpPeerPassive.h - fboss/cli/fboss2/commands/config/protocol/bgp/peer/CmdConfigProtocolBgpPeerPeerGroup.cpp - fboss/cli/fboss2/commands/config/protocol/bgp/peer/CmdConfigProtocolBgpPeerPeerGroup.h - fboss/cli/fboss2/commands/config/protocol/bgp/peer/CmdConfigProtocolBgpPeerPeerId.cpp - fboss/cli/fboss2/commands/config/protocol/bgp/peer/CmdConfigProtocolBgpPeerPeerId.h - fboss/cli/fboss2/commands/config/protocol/bgp/peer/CmdConfigProtocolBgpPeerRemoteAsn.cpp - fboss/cli/fboss2/commands/config/protocol/bgp/peer/CmdConfigProtocolBgpPeerRemoteAsn.h - fboss/cli/fboss2/commands/config/protocol/bgp/peer/CmdConfigProtocolBgpPeerRrClient.cpp - fboss/cli/fboss2/commands/config/protocol/bgp/peer/CmdConfigProtocolBgpPeerRrClient.h - fboss/cli/fboss2/commands/config/protocol/bgp/peer/CmdConfigProtocolBgpPeerTimers.cpp - fboss/cli/fboss2/commands/config/protocol/bgp/peer/CmdConfigProtocolBgpPeerTimers.h - fboss/cli/fboss2/commands/config/protocol/bgp/peer/CmdConfigProtocolBgpPeerTimersKeepalive.cpp - fboss/cli/fboss2/commands/config/protocol/bgp/peer/CmdConfigProtocolBgpPeerTimersKeepalive.h - fboss/cli/fboss2/commands/config/protocol/bgp/peer/CmdConfigProtocolBgpPeerTimersOutDelay.cpp - fboss/cli/fboss2/commands/config/protocol/bgp/peer/CmdConfigProtocolBgpPeerTimersOutDelay.h - fboss/cli/fboss2/commands/config/protocol/bgp/peer/CmdConfigProtocolBgpPeerTimersWithdrawUnprogDelay.cpp - fboss/cli/fboss2/commands/config/protocol/bgp/peer/CmdConfigProtocolBgpPeerTimersWithdrawUnprogDelay.h - fboss/cli/fboss2/commands/config/protocol/bgp/peer/CmdConfigProtocolBgpPeerType.cpp - fboss/cli/fboss2/commands/config/protocol/bgp/peer/CmdConfigProtocolBgpPeerType.h - fboss/cli/fboss2/commands/config/protocol/bgp/peer/CmdConfigProtocolBgpPeerV4OverV6Nh.cpp - fboss/cli/fboss2/commands/config/protocol/bgp/peer/CmdConfigProtocolBgpPeerV4OverV6Nh.h - fboss/cli/fboss2/commands/config/protocol/bgp/peer/CmdConfigProtocolBgpPeerWarningLimit.cpp - fboss/cli/fboss2/commands/config/protocol/bgp/peer/CmdConfigProtocolBgpPeerWarningLimit.h - fboss/cli/fboss2/commands/config/protocol/bgp/peer/CmdConfigProtocolBgpPeerWarningOnly.cpp - fboss/cli/fboss2/commands/config/protocol/bgp/peer/CmdConfigProtocolBgpPeerWarningOnly.h + fboss/cli/fboss2/commands/config/protocol/bgp/BgpCliAttrHandlers.h + fboss/cli/fboss2/commands/config/protocol/bgp/policy/CmdConfigProtocolBgpPolicy.cpp + fboss/cli/fboss2/commands/config/protocol/bgp/policy/CmdConfigProtocolBgpPolicy.h + fboss/cli/fboss2/commands/config/protocol/bgp/policy/as-path-list/CmdConfigProtocolBgpPolicyAsPathList.cpp + fboss/cli/fboss2/commands/config/protocol/bgp/policy/as-path-list/CmdConfigProtocolBgpPolicyAsPathList.h fboss/cli/fboss2/commands/config/ptp/CmdConfigPtp.cpp fboss/cli/fboss2/commands/config/ptp/CmdConfigPtp.h fboss/cli/fboss2/commands/config/ptp/transparent_clock/CmdConfigPtpTransparentClock.cpp @@ -1017,6 +918,28 @@ add_library(fboss2_config_lib fboss/cli/fboss2/commands/config/tunnel/ip_in_ip/decap/CmdConfigTunnelIpInIpDecap.h fboss/cli/fboss2/commands/config/tunnel/ip_in_ip/encap/CmdConfigTunnelIpInIpEncap.cpp fboss/cli/fboss2/commands/config/tunnel/ip_in_ip/encap/CmdConfigTunnelIpInIpEncap.h + fboss/cli/fboss2/commands/delete/interface/CmdDeleteInterface.cpp + fboss/cli/fboss2/commands/delete/interface/CmdDeleteInterface.h + fboss/cli/fboss2/commands/delete/interface/ipv6/CmdDeleteInterfaceIpv6.cpp + fboss/cli/fboss2/commands/delete/interface/ipv6/CmdDeleteInterfaceIpv6.h + fboss/cli/fboss2/commands/delete/interface/ipv6/ndp/CmdDeleteInterfaceIpv6Ndp.cpp + fboss/cli/fboss2/commands/delete/interface/ipv6/ndp/CmdDeleteInterfaceIpv6Ndp.h + fboss/cli/fboss2/commands/delete/protocol/CmdDeleteProtocol.cpp + fboss/cli/fboss2/commands/delete/protocol/CmdDeleteProtocol.h + fboss/cli/fboss2/commands/delete/protocol/static/CmdDeleteProtocolStatic.cpp + fboss/cli/fboss2/commands/delete/protocol/static/CmdDeleteProtocolStatic.h + fboss/cli/fboss2/commands/delete/protocol/static/route/CmdDeleteProtocolStaticRoute.cpp + fboss/cli/fboss2/commands/delete/protocol/static/route/CmdDeleteProtocolStaticRoute.h + fboss/cli/fboss2/commands/delete/tunnel/CmdDeleteTunnel.cpp + fboss/cli/fboss2/commands/delete/tunnel/CmdDeleteTunnel.h + fboss/cli/fboss2/commands/delete/tunnel/ip_in_ip/CmdDeleteTunnelIpInIp.cpp + fboss/cli/fboss2/commands/delete/tunnel/ip_in_ip/CmdDeleteTunnelIpInIp.h + fboss/cli/fboss2/commands/delete/tunnel/ip_in_ip/TunnelIpInIpDeleteUtils.cpp + fboss/cli/fboss2/commands/delete/tunnel/ip_in_ip/TunnelIpInIpDeleteUtils.h + fboss/cli/fboss2/commands/delete/tunnel/ip_in_ip/decap/CmdDeleteTunnelIpInIpDecap.cpp + fboss/cli/fboss2/commands/delete/tunnel/ip_in_ip/decap/CmdDeleteTunnelIpInIpDecap.h + fboss/cli/fboss2/commands/delete/tunnel/ip_in_ip/encap/CmdDeleteTunnelIpInIpEncap.cpp + fboss/cli/fboss2/commands/delete/tunnel/ip_in_ip/encap/CmdDeleteTunnelIpInIpEncap.h fboss/cli/fboss2/commands/config/vlan/CmdConfigVlan.cpp fboss/cli/fboss2/commands/config/vlan/CmdConfigVlan.h fboss/cli/fboss2/commands/config/vlan/CmdConfigVlanDefault.cpp @@ -1040,6 +963,16 @@ add_library(fboss2_config_lib fboss/cli/fboss2/commands/delete/interface/ipv6/ndp/CmdDeleteInterfaceIpv6Ndp.cpp fboss/cli/fboss2/commands/delete/interface/ipv6/ndp/CmdDeleteInterfaceIpv6Ndp.h fboss/cli/fboss2/commands/delete/protocol/CmdDeleteProtocol.cpp + fboss/cli/fboss2/commands/delete/protocol/bgp/CmdDeleteProtocolBgp.cpp + fboss/cli/fboss2/commands/delete/protocol/bgp/CmdDeleteProtocolBgp.h + fboss/cli/fboss2/commands/delete/protocol/bgp/neighbor/CmdDeleteProtocolBgpNeighbor.cpp + fboss/cli/fboss2/commands/delete/protocol/bgp/neighbor/CmdDeleteProtocolBgpNeighbor.h + fboss/cli/fboss2/commands/delete/protocol/bgp/peer-group/CmdDeleteProtocolBgpPeerGroup.cpp + fboss/cli/fboss2/commands/delete/protocol/bgp/peer-group/CmdDeleteProtocolBgpPeerGroup.h + fboss/cli/fboss2/commands/delete/protocol/bgp/policy/CmdDeleteProtocolBgpPolicy.cpp + fboss/cli/fboss2/commands/delete/protocol/bgp/policy/CmdDeleteProtocolBgpPolicy.h + fboss/cli/fboss2/commands/delete/protocol/bgp/policy/as-path-list/CmdDeleteProtocolBgpPolicyAsPathList.cpp + fboss/cli/fboss2/commands/delete/protocol/bgp/policy/as-path-list/CmdDeleteProtocolBgpPolicyAsPathList.h fboss/cli/fboss2/commands/delete/protocol/CmdDeleteProtocol.h fboss/cli/fboss2/commands/delete/protocol/static/CmdDeleteProtocolStatic.cpp fboss/cli/fboss2/commands/delete/protocol/static/CmdDeleteProtocolStatic.h @@ -1075,6 +1008,7 @@ target_link_libraries(fboss2_config_lib agent_dir_util common_file_utils switch_config_cpp2 + bgp_config_cpp2 switchinfo_utils platform_mapping Folly::folly diff --git a/cmake/CliFboss2TestConfig.cmake b/cmake/CliFboss2TestConfig.cmake index cc59995a086ef..b0c31f9bf8bd0 100644 --- a/cmake/CliFboss2TestConfig.cmake +++ b/cmake/CliFboss2TestConfig.cmake @@ -7,6 +7,8 @@ add_executable(fboss2_cmd_config_test fboss/cli/fboss2/test/config/CmdConfigAdminDistanceTest.cpp fboss/cli/fboss2/test/config/CmdConfigAppliedInfoTest.cpp fboss/cli/fboss2/test/config/CmdConfigArpTest.cpp + fboss/cli/fboss2/test/config/CmdConfigBgpNeighborTest.cpp + fboss/cli/fboss2/test/config/CmdConfigBgpPeerGroupTest.cpp fboss/cli/fboss2/test/config/CmdConfigIcmpV4UnavailableSrcAddrTest.cpp fboss/cli/fboss2/test/config/CmdConfigCoppTest.cpp fboss/cli/fboss2/test/config/CmdConfigDhcpTest.cpp @@ -32,6 +34,8 @@ add_executable(fboss2_cmd_config_test fboss/cli/fboss2/test/config/CmdConfigVlanManagerTest.cpp fboss/cli/fboss2/test/config/CmdConfigVlanPortTaggingModeTest.cpp fboss/cli/fboss2/test/config/CmdConfigVlanStaticMacTest.cpp + fboss/cli/fboss2/test/config/CmdDeleteBgpNeighborTest.cpp + fboss/cli/fboss2/test/config/CmdDeleteBgpPeerGroupTest.cpp fboss/cli/fboss2/test/config/CmdDeleteConfigInterfaceTest.cpp fboss/cli/fboss2/test/config/CmdDeleteInterfaceIpv6NdpTest.cpp fboss/cli/fboss2/test/config/CmdDeleteInterfaceTest.cpp diff --git a/cmake/CliFboss2TestIntegrationTest.cmake b/cmake/CliFboss2TestIntegrationTest.cmake index d3a4d68250994..51c447647057c 100644 --- a/cmake/CliFboss2TestIntegrationTest.cmake +++ b/cmake/CliFboss2TestIntegrationTest.cmake @@ -10,6 +10,10 @@ add_executable(fboss2_integration_test fboss/cli/fboss2/test/integration_test/Fboss2IntegrationTest.cpp fboss/cli/fboss2/test/integration_test/ConfigAdminDistanceTest.cpp fboss/cli/fboss2/test/integration_test/ConfigArpTest.cpp + fboss/cli/fboss2/test/integration_test/ConfigBgpGlobalTest.cpp + fboss/cli/fboss2/test/integration_test/ConfigBgpNeighborTest.cpp + fboss/cli/fboss2/test/integration_test/ConfigBgpPeerGroupTest.cpp + fboss/cli/fboss2/test/integration_test/ConfigBgpSessionTest.cpp fboss/cli/fboss2/test/integration_test/ConfigConcurrentSessionsTest.cpp fboss/cli/fboss2/test/integration_test/ConfigHostnameTest.cpp fboss/cli/fboss2/test/integration_test/ConfigIcmpV4UnavailableSrcAddrTest.cpp diff --git a/fboss/cli/fboss2/BUCK b/fboss/cli/fboss2/BUCK index a88ba397906a3..17ed48510dd62 100644 --- a/fboss/cli/fboss2/BUCK +++ b/fboss/cli/fboss2/BUCK @@ -1069,61 +1069,10 @@ cpp_library( "commands/config/protocol/bgp/BgpConfigSession.cpp", "commands/config/protocol/bgp/CmdConfigProtocolBgp.cpp", "commands/config/protocol/bgp/global/CmdConfigProtocolBgpGlobal.cpp", - "commands/config/protocol/bgp/global/CmdConfigProtocolBgpGlobalClusterId.cpp", - "commands/config/protocol/bgp/global/CmdConfigProtocolBgpGlobalConfedAsn.cpp", - "commands/config/protocol/bgp/global/CmdConfigProtocolBgpGlobalHoldTime.cpp", - "commands/config/protocol/bgp/global/CmdConfigProtocolBgpGlobalLocalAsn.cpp", - "commands/config/protocol/bgp/global/CmdConfigProtocolBgpGlobalNetwork6Add.cpp", - "commands/config/protocol/bgp/global/CmdConfigProtocolBgpGlobalRouterId.cpp", - "commands/config/protocol/bgp/global/CmdConfigProtocolBgpGlobalSwitchLimitMaxGoldenVips.cpp", - "commands/config/protocol/bgp/global/CmdConfigProtocolBgpGlobalSwitchLimitOverloadProtectionMode.cpp", - "commands/config/protocol/bgp/global/CmdConfigProtocolBgpGlobalSwitchLimitPrefixLimit.cpp", - "commands/config/protocol/bgp/global/CmdConfigProtocolBgpGlobalSwitchLimitTotalPathLimit.cpp", + "commands/config/protocol/bgp/neighbor/CmdConfigProtocolBgpNeighbor.cpp", "commands/config/protocol/bgp/peer-group/CmdConfigProtocolBgpPeerGroup.cpp", - "commands/config/protocol/bgp/peer-group/CmdConfigProtocolBgpPeerGroupConfedPeer.cpp", - "commands/config/protocol/bgp/peer-group/CmdConfigProtocolBgpPeerGroupDescription.cpp", - "commands/config/protocol/bgp/peer-group/CmdConfigProtocolBgpPeerGroupDisableIpv4Afi.cpp", - "commands/config/protocol/bgp/peer-group/CmdConfigProtocolBgpPeerGroupEgressPolicy.cpp", - "commands/config/protocol/bgp/peer-group/CmdConfigProtocolBgpPeerGroupIngressPolicy.cpp", - "commands/config/protocol/bgp/peer-group/CmdConfigProtocolBgpPeerGroupMaxRoutes.cpp", - "commands/config/protocol/bgp/peer-group/CmdConfigProtocolBgpPeerGroupNextHopSelf.cpp", - "commands/config/protocol/bgp/peer-group/CmdConfigProtocolBgpPeerGroupPeerTag.cpp", - "commands/config/protocol/bgp/peer-group/CmdConfigProtocolBgpPeerGroupRemoteAsn.cpp", - "commands/config/protocol/bgp/peer-group/CmdConfigProtocolBgpPeerGroupRrClient.cpp", - "commands/config/protocol/bgp/peer-group/CmdConfigProtocolBgpPeerGroupTimers.cpp", - "commands/config/protocol/bgp/peer-group/CmdConfigProtocolBgpPeerGroupTimersHoldTime.cpp", - "commands/config/protocol/bgp/peer-group/CmdConfigProtocolBgpPeerGroupTimersKeepalive.cpp", - "commands/config/protocol/bgp/peer-group/CmdConfigProtocolBgpPeerGroupTimersOutDelay.cpp", - "commands/config/protocol/bgp/peer-group/CmdConfigProtocolBgpPeerGroupTimersWithdrawUnprogDelay.cpp", - "commands/config/protocol/bgp/peer-group/CmdConfigProtocolBgpPeerGroupV4OverV6Nh.cpp", - "commands/config/protocol/bgp/peer-group/CmdConfigProtocolBgpPeerGroupWarningLimit.cpp", - "commands/config/protocol/bgp/peer-group/CmdConfigProtocolBgpPeerGroupWarningOnly.cpp", - "commands/config/protocol/bgp/peer/CmdConfigProtocolBgpPeer.cpp", - "commands/config/protocol/bgp/peer/CmdConfigProtocolBgpPeerAdvertiseLbw.cpp", - "commands/config/protocol/bgp/peer/CmdConfigProtocolBgpPeerDescription.cpp", - "commands/config/protocol/bgp/peer/CmdConfigProtocolBgpPeerDisableIpv4Afi.cpp", - "commands/config/protocol/bgp/peer/CmdConfigProtocolBgpPeerEgressPolicy.cpp", - "commands/config/protocol/bgp/peer/CmdConfigProtocolBgpPeerHoldTime.cpp", - "commands/config/protocol/bgp/peer/CmdConfigProtocolBgpPeerIngressPolicy.cpp", - "commands/config/protocol/bgp/peer/CmdConfigProtocolBgpPeerLinkBandwidth.cpp", - "commands/config/protocol/bgp/peer/CmdConfigProtocolBgpPeerLocalAddr.cpp", - "commands/config/protocol/bgp/peer/CmdConfigProtocolBgpPeerMaxRoutes.cpp", - "commands/config/protocol/bgp/peer/CmdConfigProtocolBgpPeerNextHop4.cpp", - "commands/config/protocol/bgp/peer/CmdConfigProtocolBgpPeerNextHop6.cpp", - "commands/config/protocol/bgp/peer/CmdConfigProtocolBgpPeerNextHopSelf.cpp", - "commands/config/protocol/bgp/peer/CmdConfigProtocolBgpPeerPassive.cpp", - "commands/config/protocol/bgp/peer/CmdConfigProtocolBgpPeerPeerGroup.cpp", - "commands/config/protocol/bgp/peer/CmdConfigProtocolBgpPeerPeerId.cpp", - "commands/config/protocol/bgp/peer/CmdConfigProtocolBgpPeerRemoteAsn.cpp", - "commands/config/protocol/bgp/peer/CmdConfigProtocolBgpPeerRrClient.cpp", - "commands/config/protocol/bgp/peer/CmdConfigProtocolBgpPeerTimers.cpp", - "commands/config/protocol/bgp/peer/CmdConfigProtocolBgpPeerTimersKeepalive.cpp", - "commands/config/protocol/bgp/peer/CmdConfigProtocolBgpPeerTimersOutDelay.cpp", - "commands/config/protocol/bgp/peer/CmdConfigProtocolBgpPeerTimersWithdrawUnprogDelay.cpp", - "commands/config/protocol/bgp/peer/CmdConfigProtocolBgpPeerType.cpp", - "commands/config/protocol/bgp/peer/CmdConfigProtocolBgpPeerV4OverV6Nh.cpp", - "commands/config/protocol/bgp/peer/CmdConfigProtocolBgpPeerWarningLimit.cpp", - "commands/config/protocol/bgp/peer/CmdConfigProtocolBgpPeerWarningOnly.cpp", + "commands/config/protocol/bgp/policy/CmdConfigProtocolBgpPolicy.cpp", + "commands/config/protocol/bgp/policy/as-path-list/CmdConfigProtocolBgpPolicyAsPathList.cpp", "commands/config/protocol/static/CmdConfigProtocolStatic.cpp", "commands/config/protocol/static/route/add/CmdConfigProtocolStaticRouteAdd.cpp", "commands/config/ptp/CmdConfigPtp.cpp", @@ -1162,6 +1111,11 @@ cpp_library( "commands/delete/interface/ipv6/CmdDeleteInterfaceIpv6.cpp", "commands/delete/interface/ipv6/ndp/CmdDeleteInterfaceIpv6Ndp.cpp", "commands/delete/protocol/CmdDeleteProtocol.cpp", + "commands/delete/protocol/bgp/CmdDeleteProtocolBgp.cpp", + "commands/delete/protocol/bgp/neighbor/CmdDeleteProtocolBgpNeighbor.cpp", + "commands/delete/protocol/bgp/peer-group/CmdDeleteProtocolBgpPeerGroup.cpp", + "commands/delete/protocol/bgp/policy/CmdDeleteProtocolBgpPolicy.cpp", + "commands/delete/protocol/bgp/policy/as-path-list/CmdDeleteProtocolBgpPolicyAsPathList.cpp", "commands/delete/protocol/static/CmdDeleteProtocolStatic.cpp", "commands/delete/protocol/static/route/CmdDeleteProtocolStaticRoute.cpp", "commands/delete/tunnel/CmdDeleteTunnel.cpp", @@ -1205,47 +1159,16 @@ cpp_library( "commands/config/mac/CmdConfigMac.h", "commands/config/mac/aging_time/CmdConfigMacAgingTime.h", "commands/config/protocol/CmdConfigProtocol.h", + "commands/config/protocol/bgp/BgpCliValueParsers.h", + "commands/config/protocol/bgp/BgpPeerAddr.h", "commands/config/protocol/bgp/BgpConfigSession.h", "commands/config/protocol/bgp/CmdConfigProtocolBgp.h", "commands/config/protocol/bgp/global/CmdConfigProtocolBgpGlobal.h", - "commands/config/protocol/bgp/global/CmdConfigProtocolBgpGlobalClusterId.h", - "commands/config/protocol/bgp/global/CmdConfigProtocolBgpGlobalConfedAsn.h", - "commands/config/protocol/bgp/global/CmdConfigProtocolBgpGlobalHoldTime.h", - "commands/config/protocol/bgp/global/CmdConfigProtocolBgpGlobalLocalAsn.h", - "commands/config/protocol/bgp/global/CmdConfigProtocolBgpGlobalRouterId.h", + "commands/config/protocol/bgp/neighbor/CmdConfigProtocolBgpNeighbor.h", "commands/config/protocol/bgp/peer-group/CmdConfigProtocolBgpPeerGroup.h", - "commands/config/protocol/bgp/peer-group/CmdConfigProtocolBgpPeerGroupConfedPeer.h", - "commands/config/protocol/bgp/peer-group/CmdConfigProtocolBgpPeerGroupDescription.h", - "commands/config/protocol/bgp/peer-group/CmdConfigProtocolBgpPeerGroupDisableIpv4Afi.h", - "commands/config/protocol/bgp/peer-group/CmdConfigProtocolBgpPeerGroupEgressPolicy.h", - "commands/config/protocol/bgp/peer-group/CmdConfigProtocolBgpPeerGroupIngressPolicy.h", - "commands/config/protocol/bgp/peer-group/CmdConfigProtocolBgpPeerGroupMaxRoutes.h", - "commands/config/protocol/bgp/peer-group/CmdConfigProtocolBgpPeerGroupNextHopSelf.h", - "commands/config/protocol/bgp/peer-group/CmdConfigProtocolBgpPeerGroupRemoteAsn.h", - "commands/config/protocol/bgp/peer-group/CmdConfigProtocolBgpPeerGroupRrClient.h", - "commands/config/protocol/bgp/peer-group/CmdConfigProtocolBgpPeerGroupTimers.h", - "commands/config/protocol/bgp/peer-group/CmdConfigProtocolBgpPeerGroupTimersHoldTime.h", - "commands/config/protocol/bgp/peer-group/CmdConfigProtocolBgpPeerGroupTimersKeepalive.h", - "commands/config/protocol/bgp/peer-group/CmdConfigProtocolBgpPeerGroupTimersOutDelay.h", - "commands/config/protocol/bgp/peer-group/CmdConfigProtocolBgpPeerGroupV4OverV6Nh.h", - "commands/config/protocol/bgp/peer/CmdConfigProtocolBgpPeer.h", - "commands/config/protocol/bgp/peer/CmdConfigProtocolBgpPeerAdvertiseLbw.h", - "commands/config/protocol/bgp/peer/CmdConfigProtocolBgpPeerDescription.h", - "commands/config/protocol/bgp/peer/CmdConfigProtocolBgpPeerDisableIpv4Afi.h", - "commands/config/protocol/bgp/peer/CmdConfigProtocolBgpPeerEgressPolicy.h", - "commands/config/protocol/bgp/peer/CmdConfigProtocolBgpPeerHoldTime.h", - "commands/config/protocol/bgp/peer/CmdConfigProtocolBgpPeerIngressPolicy.h", - "commands/config/protocol/bgp/peer/CmdConfigProtocolBgpPeerLinkBandwidth.h", - "commands/config/protocol/bgp/peer/CmdConfigProtocolBgpPeerLocalAddr.h", - "commands/config/protocol/bgp/peer/CmdConfigProtocolBgpPeerMaxRoutes.h", - "commands/config/protocol/bgp/peer/CmdConfigProtocolBgpPeerNextHop4.h", - "commands/config/protocol/bgp/peer/CmdConfigProtocolBgpPeerNextHop6.h", - "commands/config/protocol/bgp/peer/CmdConfigProtocolBgpPeerNextHopSelf.h", - "commands/config/protocol/bgp/peer/CmdConfigProtocolBgpPeerPassive.h", - "commands/config/protocol/bgp/peer/CmdConfigProtocolBgpPeerPeerGroup.h", - "commands/config/protocol/bgp/peer/CmdConfigProtocolBgpPeerRemoteAsn.h", - "commands/config/protocol/bgp/peer/CmdConfigProtocolBgpPeerRrClient.h", - "commands/config/protocol/bgp/peer/CmdConfigProtocolBgpPeerV4OverV6Nh.h", + "commands/config/protocol/bgp/BgpCliAttrHandlers.h", + "commands/config/protocol/bgp/policy/CmdConfigProtocolBgpPolicy.h", + "commands/config/protocol/bgp/policy/as-path-list/CmdConfigProtocolBgpPolicyAsPathList.h", "commands/config/protocol/static/CmdConfigProtocolStatic.h", "commands/config/protocol/static/route/StaticRouteUtils.h", "commands/config/protocol/static/route/add/CmdConfigProtocolStaticRouteAdd.h", @@ -1285,6 +1208,11 @@ cpp_library( "commands/delete/interface/ipv6/CmdDeleteInterfaceIpv6.h", "commands/delete/interface/ipv6/ndp/CmdDeleteInterfaceIpv6Ndp.h", "commands/delete/protocol/CmdDeleteProtocol.h", + "commands/delete/protocol/bgp/CmdDeleteProtocolBgp.h", + "commands/delete/protocol/bgp/neighbor/CmdDeleteProtocolBgpNeighbor.h", + "commands/delete/protocol/bgp/peer-group/CmdDeleteProtocolBgpPeerGroup.h", + "commands/delete/protocol/bgp/policy/CmdDeleteProtocolBgpPolicy.h", + "commands/delete/protocol/bgp/policy/as-path-list/CmdDeleteProtocolBgpPolicyAsPathList.h", "commands/delete/protocol/static/CmdDeleteProtocolStatic.h", "commands/delete/protocol/static/route/CmdDeleteProtocolStaticRoute.h", "commands/delete/tunnel/CmdDeleteTunnel.h", @@ -1329,6 +1257,7 @@ cpp_library( ":cmd-handler", ":fboss2-lib", "//common/network/if:if-cpp2-types", + "//configerator/structs/neteng/fboss/bgp:bgp_config-cpp2-types", "//fboss/agent:agent_config-cpp2-types", "//fboss/agent:fboss-types", "//fboss/agent:platform_config-cpp2-types", diff --git a/fboss/cli/fboss2/CmdListConfig.cpp b/fboss/cli/fboss2/CmdListConfig.cpp index 86731f8390dda..5a3c70562bd35 100644 --- a/fboss/cli/fboss2/CmdListConfig.cpp +++ b/fboss/cli/fboss2/CmdListConfig.cpp @@ -37,61 +37,10 @@ #include "fboss/cli/fboss2/commands/config/protocol/CmdConfigProtocol.h" #include "fboss/cli/fboss2/commands/config/protocol/bgp/CmdConfigProtocolBgp.h" #include "fboss/cli/fboss2/commands/config/protocol/bgp/global/CmdConfigProtocolBgpGlobal.h" -#include "fboss/cli/fboss2/commands/config/protocol/bgp/global/CmdConfigProtocolBgpGlobalClusterId.h" -#include "fboss/cli/fboss2/commands/config/protocol/bgp/global/CmdConfigProtocolBgpGlobalConfedAsn.h" -#include "fboss/cli/fboss2/commands/config/protocol/bgp/global/CmdConfigProtocolBgpGlobalHoldTime.h" -#include "fboss/cli/fboss2/commands/config/protocol/bgp/global/CmdConfigProtocolBgpGlobalLocalAsn.h" -#include "fboss/cli/fboss2/commands/config/protocol/bgp/global/CmdConfigProtocolBgpGlobalNetwork6Add.h" -#include "fboss/cli/fboss2/commands/config/protocol/bgp/global/CmdConfigProtocolBgpGlobalRouterId.h" -#include "fboss/cli/fboss2/commands/config/protocol/bgp/global/CmdConfigProtocolBgpGlobalSwitchLimitMaxGoldenVips.h" -#include "fboss/cli/fboss2/commands/config/protocol/bgp/global/CmdConfigProtocolBgpGlobalSwitchLimitOverloadProtectionMode.h" -#include "fboss/cli/fboss2/commands/config/protocol/bgp/global/CmdConfigProtocolBgpGlobalSwitchLimitPrefixLimit.h" -#include "fboss/cli/fboss2/commands/config/protocol/bgp/global/CmdConfigProtocolBgpGlobalSwitchLimitTotalPathLimit.h" +#include "fboss/cli/fboss2/commands/config/protocol/bgp/neighbor/CmdConfigProtocolBgpNeighbor.h" #include "fboss/cli/fboss2/commands/config/protocol/bgp/peer-group/CmdConfigProtocolBgpPeerGroup.h" -#include "fboss/cli/fboss2/commands/config/protocol/bgp/peer-group/CmdConfigProtocolBgpPeerGroupConfedPeer.h" -#include "fboss/cli/fboss2/commands/config/protocol/bgp/peer-group/CmdConfigProtocolBgpPeerGroupDescription.h" -#include "fboss/cli/fboss2/commands/config/protocol/bgp/peer-group/CmdConfigProtocolBgpPeerGroupDisableIpv4Afi.h" -#include "fboss/cli/fboss2/commands/config/protocol/bgp/peer-group/CmdConfigProtocolBgpPeerGroupEgressPolicy.h" -#include "fboss/cli/fboss2/commands/config/protocol/bgp/peer-group/CmdConfigProtocolBgpPeerGroupIngressPolicy.h" -#include "fboss/cli/fboss2/commands/config/protocol/bgp/peer-group/CmdConfigProtocolBgpPeerGroupMaxRoutes.h" -#include "fboss/cli/fboss2/commands/config/protocol/bgp/peer-group/CmdConfigProtocolBgpPeerGroupNextHopSelf.h" -#include "fboss/cli/fboss2/commands/config/protocol/bgp/peer-group/CmdConfigProtocolBgpPeerGroupPeerTag.h" -#include "fboss/cli/fboss2/commands/config/protocol/bgp/peer-group/CmdConfigProtocolBgpPeerGroupRemoteAsn.h" -#include "fboss/cli/fboss2/commands/config/protocol/bgp/peer-group/CmdConfigProtocolBgpPeerGroupRrClient.h" -#include "fboss/cli/fboss2/commands/config/protocol/bgp/peer-group/CmdConfigProtocolBgpPeerGroupTimers.h" -#include "fboss/cli/fboss2/commands/config/protocol/bgp/peer-group/CmdConfigProtocolBgpPeerGroupTimersHoldTime.h" -#include "fboss/cli/fboss2/commands/config/protocol/bgp/peer-group/CmdConfigProtocolBgpPeerGroupTimersKeepalive.h" -#include "fboss/cli/fboss2/commands/config/protocol/bgp/peer-group/CmdConfigProtocolBgpPeerGroupTimersOutDelay.h" -#include "fboss/cli/fboss2/commands/config/protocol/bgp/peer-group/CmdConfigProtocolBgpPeerGroupTimersWithdrawUnprogDelay.h" -#include "fboss/cli/fboss2/commands/config/protocol/bgp/peer-group/CmdConfigProtocolBgpPeerGroupV4OverV6Nh.h" -#include "fboss/cli/fboss2/commands/config/protocol/bgp/peer-group/CmdConfigProtocolBgpPeerGroupWarningLimit.h" -#include "fboss/cli/fboss2/commands/config/protocol/bgp/peer-group/CmdConfigProtocolBgpPeerGroupWarningOnly.h" -#include "fboss/cli/fboss2/commands/config/protocol/bgp/peer/CmdConfigProtocolBgpPeer.h" -#include "fboss/cli/fboss2/commands/config/protocol/bgp/peer/CmdConfigProtocolBgpPeerAdvertiseLbw.h" -#include "fboss/cli/fboss2/commands/config/protocol/bgp/peer/CmdConfigProtocolBgpPeerDescription.h" -#include "fboss/cli/fboss2/commands/config/protocol/bgp/peer/CmdConfigProtocolBgpPeerDisableIpv4Afi.h" -#include "fboss/cli/fboss2/commands/config/protocol/bgp/peer/CmdConfigProtocolBgpPeerEgressPolicy.h" -#include "fboss/cli/fboss2/commands/config/protocol/bgp/peer/CmdConfigProtocolBgpPeerHoldTime.h" -#include "fboss/cli/fboss2/commands/config/protocol/bgp/peer/CmdConfigProtocolBgpPeerIngressPolicy.h" -#include "fboss/cli/fboss2/commands/config/protocol/bgp/peer/CmdConfigProtocolBgpPeerLinkBandwidth.h" -#include "fboss/cli/fboss2/commands/config/protocol/bgp/peer/CmdConfigProtocolBgpPeerLocalAddr.h" -#include "fboss/cli/fboss2/commands/config/protocol/bgp/peer/CmdConfigProtocolBgpPeerMaxRoutes.h" -#include "fboss/cli/fboss2/commands/config/protocol/bgp/peer/CmdConfigProtocolBgpPeerNextHop4.h" -#include "fboss/cli/fboss2/commands/config/protocol/bgp/peer/CmdConfigProtocolBgpPeerNextHop6.h" -#include "fboss/cli/fboss2/commands/config/protocol/bgp/peer/CmdConfigProtocolBgpPeerNextHopSelf.h" -#include "fboss/cli/fboss2/commands/config/protocol/bgp/peer/CmdConfigProtocolBgpPeerPassive.h" -#include "fboss/cli/fboss2/commands/config/protocol/bgp/peer/CmdConfigProtocolBgpPeerPeerGroup.h" -#include "fboss/cli/fboss2/commands/config/protocol/bgp/peer/CmdConfigProtocolBgpPeerPeerId.h" -#include "fboss/cli/fboss2/commands/config/protocol/bgp/peer/CmdConfigProtocolBgpPeerRemoteAsn.h" -#include "fboss/cli/fboss2/commands/config/protocol/bgp/peer/CmdConfigProtocolBgpPeerRrClient.h" -#include "fboss/cli/fboss2/commands/config/protocol/bgp/peer/CmdConfigProtocolBgpPeerTimers.h" -#include "fboss/cli/fboss2/commands/config/protocol/bgp/peer/CmdConfigProtocolBgpPeerTimersKeepalive.h" -#include "fboss/cli/fboss2/commands/config/protocol/bgp/peer/CmdConfigProtocolBgpPeerTimersOutDelay.h" -#include "fboss/cli/fboss2/commands/config/protocol/bgp/peer/CmdConfigProtocolBgpPeerTimersWithdrawUnprogDelay.h" -#include "fboss/cli/fboss2/commands/config/protocol/bgp/peer/CmdConfigProtocolBgpPeerType.h" -#include "fboss/cli/fboss2/commands/config/protocol/bgp/peer/CmdConfigProtocolBgpPeerV4OverV6Nh.h" -#include "fboss/cli/fboss2/commands/config/protocol/bgp/peer/CmdConfigProtocolBgpPeerWarningLimit.h" -#include "fboss/cli/fboss2/commands/config/protocol/bgp/peer/CmdConfigProtocolBgpPeerWarningOnly.h" +#include "fboss/cli/fboss2/commands/config/protocol/bgp/policy/CmdConfigProtocolBgpPolicy.h" +#include "fboss/cli/fboss2/commands/config/protocol/bgp/policy/as-path-list/CmdConfigProtocolBgpPolicyAsPathList.h" #include "fboss/cli/fboss2/commands/config/protocol/static/CmdConfigProtocolStatic.h" #include "fboss/cli/fboss2/commands/config/protocol/static/route/add/CmdConfigProtocolStaticRouteAdd.h" #include "fboss/cli/fboss2/commands/config/ptp/CmdConfigPtp.h" @@ -129,6 +78,11 @@ #include "fboss/cli/fboss2/commands/delete/interface/ipv6/CmdDeleteInterfaceIpv6.h" #include "fboss/cli/fboss2/commands/delete/interface/ipv6/ndp/CmdDeleteInterfaceIpv6Ndp.h" #include "fboss/cli/fboss2/commands/delete/protocol/CmdDeleteProtocol.h" +#include "fboss/cli/fboss2/commands/delete/protocol/bgp/CmdDeleteProtocolBgp.h" +#include "fboss/cli/fboss2/commands/delete/protocol/bgp/neighbor/CmdDeleteProtocolBgpNeighbor.h" +#include "fboss/cli/fboss2/commands/delete/protocol/bgp/peer-group/CmdDeleteProtocolBgpPeerGroup.h" +#include "fboss/cli/fboss2/commands/delete/protocol/bgp/policy/CmdDeleteProtocolBgpPolicy.h" +#include "fboss/cli/fboss2/commands/delete/protocol/bgp/policy/as-path-list/CmdDeleteProtocolBgpPolicyAsPathList.h" #include "fboss/cli/fboss2/commands/delete/protocol/static/CmdDeleteProtocolStatic.h" #include "fboss/cli/fboss2/commands/delete/protocol/static/route/CmdDeleteProtocolStaticRoute.h" #include "fboss/cli/fboss2/commands/delete/tunnel/CmdDeleteTunnel.h" @@ -358,454 +312,71 @@ const CommandTree& kConfigCommandTree() { { { "global", - "Configure BGP global settings", + "Configure BGP global settings: " + "(router-id, local-asn, hold-time, confed-asn, " + "count-confeds-in-as-path-len, " + "graceful-restart-time, rib-allocated-path-ids, " + "network6, switch-limit[-total-path|" + "-max-golden-vips|-overload-protection-mode])", commandHandler, argRegistrar, - { - { - "router-id", - "Set BGP router identifier", - commandHandler< - CmdConfigProtocolBgpGlobalRouterId>, - argRegistrar< - CmdConfigProtocolBgpGlobalRouterIdTraits>, - }, - { - "local-asn", - "Set local AS number", - commandHandler< - CmdConfigProtocolBgpGlobalLocalAsn>, - argRegistrar< - CmdConfigProtocolBgpGlobalLocalAsnTraits>, - }, - { - "hold-time", - "Set BGP hold time in seconds", - commandHandler< - CmdConfigProtocolBgpGlobalHoldTime>, - argRegistrar< - CmdConfigProtocolBgpGlobalHoldTimeTraits>, - }, - { - "confed-asn", - "Set BGP confederation AS number", - commandHandler< - CmdConfigProtocolBgpGlobalConfedAsn>, - argRegistrar< - CmdConfigProtocolBgpGlobalConfedAsnTraits>, - }, - { - "cluster-id", - "Set route reflector cluster ID", - commandHandler< - CmdConfigProtocolBgpGlobalClusterId>, - argRegistrar< - CmdConfigProtocolBgpGlobalClusterIdTraits>, - }, - { - "network6", - "Add IPv6 network to advertise", - commandHandler< - CmdConfigProtocolBgpGlobalNetwork6Add>, - argRegistrar< - CmdConfigProtocolBgpGlobalNetwork6AddTraits>, - }, - { - "switch-limit", - "Set switch limit prefix-limit", - commandHandler< - CmdConfigProtocolBgpGlobalSwitchLimitPrefixLimit>, - argRegistrar< - CmdConfigProtocolBgpGlobalSwitchLimitPrefixLimitTraits>, - }, - { - "switch-limit-total-path", - "Set switch limit total-path-limit", - commandHandler< - CmdConfigProtocolBgpGlobalSwitchLimitTotalPathLimit>, - argRegistrar< - CmdConfigProtocolBgpGlobalSwitchLimitTotalPathLimitTraits>, - }, - { - "switch-limit-max-golden-vips", - "Set switch limit max-golden-vips", - commandHandler< - CmdConfigProtocolBgpGlobalSwitchLimitMaxGoldenVips>, - argRegistrar< - CmdConfigProtocolBgpGlobalSwitchLimitMaxGoldenVipsTraits>, - }, - { - "switch-limit-overload-protection-mode", - "Set switch limit overload-protection-mode", - commandHandler< - CmdConfigProtocolBgpGlobalSwitchLimitOverloadProtectionMode>, - argRegistrar< - CmdConfigProtocolBgpGlobalSwitchLimitOverloadProtectionModeTraits>, - }, - }, }, { "peer-group", - "Configure BGP peer group", + "Configure BGP peer-group: " + "[ ...] (remote-asn, local-asn, " + "description, peer-tag, ingress-policy, " + "egress-policy, rr-client, confed-peer, " + "redistribute-peer, enhanced-route-refresh, " + "connect-mode, next-hop-self, add-path send|receive, " + "afi disable-ipv4-afi|disable-ipv6-afi|" + "ipv4-over-ipv6-nh, " + "graceful-restart restart-time|stateful-ha, " + "max-route pre-filter|post-filter|" + "pre-warning-threshold|post-warning-threshold|" + "pre-warning-only|post-warning-only, " + "timers hold-time|keepalive|out-delay|" + "withdraw-unprog-delay)", commandHandler, argRegistrar, - { - { - "description", - "Set peer group description", - commandHandler< - CmdConfigProtocolBgpPeerGroupDescription>, - argRegistrar< - CmdConfigProtocolBgpPeerGroupDescriptionTraits>, - }, - { - "remote-asn", - "Set remote AS number", - commandHandler< - CmdConfigProtocolBgpPeerGroupRemoteAsn>, - argRegistrar< - CmdConfigProtocolBgpPeerGroupRemoteAsnTraits>, - }, - { - "next-hop-self", - "Enable/disable next-hop-self", - commandHandler< - CmdConfigProtocolBgpPeerGroupNextHopSelf>, - argRegistrar< - CmdConfigProtocolBgpPeerGroupNextHopSelfTraits>, - }, - { - "confed-peer", - "Enable/disable confederation peer", - commandHandler< - CmdConfigProtocolBgpPeerGroupConfedPeer>, - argRegistrar< - CmdConfigProtocolBgpPeerGroupConfedPeerTraits>, - }, - { - "disable-ipv4-afi", - "Enable/disable IPv4 AFI", - commandHandler< - CmdConfigProtocolBgpPeerGroupDisableIpv4Afi>, - argRegistrar< - CmdConfigProtocolBgpPeerGroupDisableIpv4AfiTraits>, - }, - { - "v4-over-v6-nh", - "Enable/disable v4-over-v6 next-hop", - commandHandler< - CmdConfigProtocolBgpPeerGroupV4OverV6Nh>, - argRegistrar< - CmdConfigProtocolBgpPeerGroupV4OverV6NhTraits>, - }, - { - "rr-client", - "Enable/disable route reflector client", - commandHandler< - CmdConfigProtocolBgpPeerGroupRrClient>, - argRegistrar< - CmdConfigProtocolBgpPeerGroupRrClientTraits>, - }, - { - "ingress-policy", - "Set ingress policy", - commandHandler< - CmdConfigProtocolBgpPeerGroupIngressPolicy>, - argRegistrar< - CmdConfigProtocolBgpPeerGroupIngressPolicyTraits>, - }, - { - "egress-policy", - "Set egress policy", - commandHandler< - CmdConfigProtocolBgpPeerGroupEgressPolicy>, - argRegistrar< - CmdConfigProtocolBgpPeerGroupEgressPolicyTraits>, - }, - { - "max-routes", - "Set maximum routes", - commandHandler< - CmdConfigProtocolBgpPeerGroupMaxRoutes>, - argRegistrar< - CmdConfigProtocolBgpPeerGroupMaxRoutesTraits>, - }, - { - "peer-tag", - "Set peer tag", - commandHandler< - CmdConfigProtocolBgpPeerGroupPeerTag>, - argRegistrar< - CmdConfigProtocolBgpPeerGroupPeerTagTraits>, - }, - { - "warning-only", - "Enable/disable warning-only mode", - commandHandler< - CmdConfigProtocolBgpPeerGroupWarningOnly>, - argRegistrar< - CmdConfigProtocolBgpPeerGroupWarningOnlyTraits>, - }, - { - "warning-limit", - "Set warning limit percentage", - commandHandler< - CmdConfigProtocolBgpPeerGroupWarningLimit>, - argRegistrar< - CmdConfigProtocolBgpPeerGroupWarningLimitTraits>, - }, - { - "timers", - "Configure BGP timers", - commandHandler< - CmdConfigProtocolBgpPeerGroupTimers>, - argRegistrar< - CmdConfigProtocolBgpPeerGroupTimersTraits>, - { - { - "hold-time", - "Set hold time", - commandHandler< - CmdConfigProtocolBgpPeerGroupTimersHoldTime>, - argRegistrar< - CmdConfigProtocolBgpPeerGroupTimersHoldTimeTraits>, - }, - { - "keepalive", - "Set keepalive interval", - commandHandler< - CmdConfigProtocolBgpPeerGroupTimersKeepalive>, - argRegistrar< - CmdConfigProtocolBgpPeerGroupTimersKeepaliveTraits>, - }, - { - "out-delay", - "Set output delay", - commandHandler< - CmdConfigProtocolBgpPeerGroupTimersOutDelay>, - argRegistrar< - CmdConfigProtocolBgpPeerGroupTimersOutDelayTraits>, - }, - { - "withdraw-unprog-delay", - "Set withdraw unprogrammed delay", - commandHandler< - CmdConfigProtocolBgpPeerGroupTimersWithdrawUnprogDelay>, - argRegistrar< - CmdConfigProtocolBgpPeerGroupTimersWithdrawUnprogDelayTraits>, - }, - }, - }, - }, }, { - "peer", - "Configure BGP peer", - commandHandler, - argRegistrar, - { - { - "peer-group", - "Set peer group", - commandHandler< - CmdConfigProtocolBgpPeerPeerGroup>, - argRegistrar< - CmdConfigProtocolBgpPeerPeerGroupTraits>, - }, - { - "description", - "Set peer description", - commandHandler< - CmdConfigProtocolBgpPeerDescription>, - argRegistrar< - CmdConfigProtocolBgpPeerDescriptionTraits>, - }, - { - "remote-asn", - "Set remote AS number", - commandHandler< - CmdConfigProtocolBgpPeerRemoteAsn>, - argRegistrar< - CmdConfigProtocolBgpPeerRemoteAsnTraits>, - }, - { - "local-addr", - "Set local address", - commandHandler< - CmdConfigProtocolBgpPeerLocalAddr>, - argRegistrar< - CmdConfigProtocolBgpPeerLocalAddrTraits>, - }, - { - "next-hop4", - "Set IPv4 next-hop", - commandHandler< - CmdConfigProtocolBgpPeerNextHop4>, - argRegistrar< - CmdConfigProtocolBgpPeerNextHop4Traits>, - }, - { - "next-hop6", - "Set IPv6 next-hop", - commandHandler< - CmdConfigProtocolBgpPeerNextHop6>, - argRegistrar< - CmdConfigProtocolBgpPeerNextHop6Traits>, - }, - { - "next-hop-self", - "Enable/disable next-hop-self", - commandHandler< - CmdConfigProtocolBgpPeerNextHopSelf>, - argRegistrar< - CmdConfigProtocolBgpPeerNextHopSelfTraits>, - }, - { - "disable-ipv4-afi", - "Enable/disable IPv4 AFI", - commandHandler< - CmdConfigProtocolBgpPeerDisableIpv4Afi>, - argRegistrar< - CmdConfigProtocolBgpPeerDisableIpv4AfiTraits>, - }, - { - "v4-over-v6-nh", - "Enable/disable v4-over-v6 next-hop", - commandHandler< - CmdConfigProtocolBgpPeerV4OverV6Nh>, - argRegistrar< - CmdConfigProtocolBgpPeerV4OverV6NhTraits>, - }, - { - "rr-client", - "Enable/disable route reflector client", - commandHandler< - CmdConfigProtocolBgpPeerRrClient>, - argRegistrar< - CmdConfigProtocolBgpPeerRrClientTraits>, - }, - { - "passive", - "Enable/disable passive mode", - commandHandler< - CmdConfigProtocolBgpPeerPassive>, - argRegistrar< - CmdConfigProtocolBgpPeerPassiveTraits>, - }, - { - "type", - "Set peer type", - commandHandler, - argRegistrar< - CmdConfigProtocolBgpPeerTypeTraits>, - }, - { - "ingress-policy", - "Set ingress policy", - commandHandler< - CmdConfigProtocolBgpPeerIngressPolicy>, - argRegistrar< - CmdConfigProtocolBgpPeerIngressPolicyTraits>, - }, - { - "egress-policy", - "Set egress policy", - commandHandler< - CmdConfigProtocolBgpPeerEgressPolicy>, - argRegistrar< - CmdConfigProtocolBgpPeerEgressPolicyTraits>, - }, - { - "max-routes", - "Set maximum routes", - commandHandler< - CmdConfigProtocolBgpPeerMaxRoutes>, - argRegistrar< - CmdConfigProtocolBgpPeerMaxRoutesTraits>, - }, - { - "peer-id", - "Set peer identifier", - commandHandler< - CmdConfigProtocolBgpPeerPeerId>, - argRegistrar< - CmdConfigProtocolBgpPeerPeerIdTraits>, - }, - { - "advertise-lbw", - "Enable/disable link bandwidth advertisement", - commandHandler< - CmdConfigProtocolBgpPeerAdvertiseLbw>, - argRegistrar< - CmdConfigProtocolBgpPeerAdvertiseLbwTraits>, - }, - { - "link-bandwidth", - "Set link bandwidth", - commandHandler< - CmdConfigProtocolBgpPeerLinkBandwidth>, - argRegistrar< - CmdConfigProtocolBgpPeerLinkBandwidthTraits>, - }, - { - "hold-time", - "Set hold time", - commandHandler< - CmdConfigProtocolBgpPeerHoldTime>, - argRegistrar< - CmdConfigProtocolBgpPeerHoldTimeTraits>, - }, - { - "warning-only", - "Enable/disable warning-only mode", - commandHandler< - CmdConfigProtocolBgpPeerWarningOnly>, - argRegistrar< - CmdConfigProtocolBgpPeerWarningOnlyTraits>, - }, - { - "warning-limit", - "Set warning limit percentage", - commandHandler< - CmdConfigProtocolBgpPeerWarningLimit>, - argRegistrar< - CmdConfigProtocolBgpPeerWarningLimitTraits>, - }, - { - "timers", - "Configure BGP timers", - commandHandler< - CmdConfigProtocolBgpPeerTimers>, - argRegistrar< - CmdConfigProtocolBgpPeerTimersTraits>, - { - { - "keepalive", - "Set keepalive interval", - commandHandler< - CmdConfigProtocolBgpPeerTimersKeepalive>, - argRegistrar< - CmdConfigProtocolBgpPeerTimersKeepaliveTraits>, - }, - { - "out-delay", - "Set output delay", - commandHandler< - CmdConfigProtocolBgpPeerTimersOutDelay>, - argRegistrar< - CmdConfigProtocolBgpPeerTimersOutDelayTraits>, - }, - { - "withdraw-unprog-delay", - "Set withdraw unprogrammed delay", - commandHandler< - CmdConfigProtocolBgpPeerTimersWithdrawUnprogDelay>, - argRegistrar< - CmdConfigProtocolBgpPeerTimersWithdrawUnprogDelayTraits>, - }, - }, - }, - }, + "neighbor", + "Configure BGP neighbor: " + "[ ...] (remote-asn, local-asn, " + "description, peer-tag, peer-group, ingress-policy, " + "egress-policy, rr-client, confed-peer, " + "redistribute-peer, enhanced-route-refresh, " + "connect-mode, next-hop4, next-hop6, next-hop-self, " + "peer-id, type, link-bandwidth, advertise-lbw, " + "receive-lbw, add-path send|receive, " + "afi disable-ipv4-afi|disable-ipv6-afi|" + "ipv4-over-ipv6-nh, bind-addr address, " + "graceful-restart restart-time|stateful-ha, " + "max-route pre-filter|post-filter|" + "pre-warning-threshold|post-warning-threshold|" + "pre-warning-only|post-warning-only, " + "timers hold-time|keepalive|out-delay|" + "withdraw-unprog-delay)", + commandHandler, + argRegistrar, + }, + { + "policy", + "Configure BGP policy objects", + commandHandler, + argRegistrar, + {{ + "as-path-list", + "Configure BGP AS-path list: " + "[entry ] [ ...] " + "(description; entry " + "asn-regexp|description|match-logic)", + commandHandler< + CmdConfigProtocolBgpPolicyAsPathList>, + argRegistrar< + CmdConfigProtocolBgpPolicyAsPathListTraits>, + }}, }, }, }, @@ -1050,31 +621,62 @@ const CommandTree& kConfigCommandTree() { commandHandler, argTypeHandler, {{ - "static", - "Delete static routing configuration", - commandHandler, - argTypeHandler, - {{ - "ip", - "Delete IPv4 static routes", - {{ - "route", - "Delete IPv4 static route", - commandHandler, - argRegistrar, - }}, - }, - { - "ipv6", - "Delete IPv6 static routes", - {{ - "route", - "Delete IPv6 static route", - commandHandler, - argRegistrar, - }}, - }}, - }}, + "bgp", + "Delete BGP configuration objects", + commandHandler, + argTypeHandler, + {{ + "neighbor", + "Delete a BGP neighbor: ", + commandHandler, + argRegistrar, + }, + { + "peer-group", + "Delete a BGP peer-group: ", + commandHandler, + argRegistrar, + }, + { + "policy", + "Delete BGP policy objects", + commandHandler, + argTypeHandler, + {{ + "as-path-list", + "Delete a BGP AS-path list: ", + commandHandler, + argRegistrar< + CmdDeleteProtocolBgpPolicyAsPathListTraits>, + }}, + }}, + }, + { + "static", + "Delete static routing configuration", + commandHandler, + argTypeHandler, + {{ + "ip", + "Delete IPv4 static routes", + {{ + "route", + "Delete IPv4 static route", + commandHandler, + argRegistrar, + }}, + }, + { + "ipv6", + "Delete IPv6 static routes", + {{ + "route", + "Delete IPv6 static route", + commandHandler, + argRegistrar, + }}, + }}, + }}, }, {"delete", diff --git a/fboss/cli/fboss2/cli_metadata.thrift b/fboss/cli/fboss2/cli_metadata.thrift index 774c7dee5dc49..f933911900a53 100644 --- a/fboss/cli/fboss2/cli_metadata.thrift +++ b/fboss/cli/fboss2/cli_metadata.thrift @@ -19,11 +19,13 @@ enum ConfigActionLevel { HITLESS = 0, // Can be applied with reloadConfig() - default AGENT_WARMBOOT = 1, // Requires agent warmboot restart AGENT_COLDBOOT = 2, // Requires agent coldboot restart (clears ASIC state) + BGP_RESTART = 3, // Requires a restart of the bgpd (BGP++) service } // Identifier for different services that can be configured enum ServiceType { AGENT = 1, + BGP = 2, // The bgpd (BGP++) routing daemon } // Metadata stored alongside the session configuration file. diff --git a/fboss/cli/fboss2/commands/config/protocol/bgp/BgpCliAttrHandlers.h b/fboss/cli/fboss2/commands/config/protocol/bgp/BgpCliAttrHandlers.h new file mode 100644 index 0000000000000..9b0f14386d238 --- /dev/null +++ b/fboss/cli/fboss2/commands/config/protocol/bgp/BgpCliAttrHandlers.h @@ -0,0 +1,108 @@ +/* + * 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 +#include +#include +#include +#include "fboss/cli/fboss2/commands/config/protocol/bgp/BgpCliValueParsers.h" +#include "fmt/format.h" + +/** + * Generic, target-typed per-attribute handler factories shared by the BGP + * `config protocol bgp policy ...` dispatchers. + * + * Unlike the per-command factories in the neighbor/peer-group dispatchers + * (which are hard-wired to a single thrift struct), these are templated on the + * target type T so the same parsing/validation logic can mutate any policy + * object (AsPathList, AsPathListEntry, and — as later object types land — + * CommunityList, PrefixList, BgpPolicyTerm, ...). A handler validates its + * tokens and either mutates T or returns an error the caller surfaces without + * persisting. + */ +namespace facebook::fboss::bgpcli { + +using Tokens = std::vector; + +// A per-attribute handler mutating a typed thrift target T in place. +template +using AttrHandler = std::function; + +// A single-token string value (names, regex patterns, policy names). +template +AttrHandler stringAttr( + std::string_view name, + std::string_view valueName, + std::function set) { + return [name, valueName, set = std::move(set)]( + T& target, const Tokens& values) -> Result { + if (values.size() != 1) { + return err(fmt::format("Error: {} requires <{}>", name, valueName)); + } + set(target, values[0]); + return ok(fmt::format("Successfully set {} to: {}", name, values[0])); + }; +} + +// A free-text string value that may span multiple CLI tokens (descriptions); +// the tokens are re-joined with single spaces. +template +AttrHandler joinedStringAttr( + std::string_view name, + std::function set) { + return + [name, set = std::move(set)](T& target, const Tokens& values) -> Result { + if (values.empty()) { + return err(fmt::format("Error: {} requires ", name)); + } + std::string joined = values[0]; + for (size_t i = 1; i < values.size(); ++i) { + joined += " " + values[i]; + } + set(target, joined); + return ok(fmt::format("Successfully set {} to: {}", name, joined)); + }; +} + +// A value drawn from a fixed set of names, mapped to an enum by `lookup` +// (returns std::nullopt for an unrecognized name). `valueDesc` documents the +// accepted names in both the usage and the rejection message. +template +AttrHandler enumAttr( + std::string_view name, + std::string_view valueDesc, + std::function(const std::string&)> lookup, + std::function set) { + return [name, valueDesc, lookup = std::move(lookup), set = std::move(set)]( + T& target, const Tokens& values) -> Result { + if (values.size() != 1) { + return err(fmt::format("Error: {} requires <{}>", name, valueDesc)); + } + auto parsed = lookup(values[0]); + if (!parsed) { + return err( + fmt::format( + "Error: Invalid {} value '{}'; expected {}", + name, + values[0], + valueDesc)); + } + set(target, *parsed); + return ok(fmt::format("Successfully set {} to: {}", name, values[0])); + }; +} + +} // namespace facebook::fboss::bgpcli diff --git a/fboss/cli/fboss2/commands/config/protocol/bgp/BgpCliValueParsers.h b/fboss/cli/fboss2/commands/config/protocol/bgp/BgpCliValueParsers.h new file mode 100644 index 0000000000000..6e80deec368b4 --- /dev/null +++ b/fboss/cli/fboss2/commands/config/protocol/bgp/BgpCliValueParsers.h @@ -0,0 +1,96 @@ +/* + * 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 +#include +#include + +/** + * Value parsing helpers shared by the BGP config dispatchers + * (`config protocol bgp global` / `config protocol bgp neighbor`). + * + * All parsers return std::nullopt on invalid input instead of throwing so a + * handler can reject the value with a user-facing message and leave the + * session unpersisted. + */ +namespace facebook::fboss::bgpcli { + +// Outcome of an attribute handler: on failure the message is returned to the +// user and the session is NOT persisted, so a rejected value never lands on +// disk. +struct Result { + bool ok; + std::string message; +}; + +inline Result ok(std::string message) { + return Result{true, std::move(message)}; +} + +inline Result err(std::string message) { + return Result{false, std::move(message)}; +} + +inline std::optional parseBool(const std::string& value) { + if (value == "true" || value == "1" || value == "yes") { + return true; + } + if (value == "false" || value == "0" || value == "no") { + return false; + } + return std::nullopt; +} + +template +std::optional parseInt(const std::string& value) { + try { + return folly::to(value); + } catch (const std::exception&) { + return std::nullopt; + } +} + +// Parse a non-negative value that must fit in int32 (used for second-valued +// timers and min-routes). +inline std::optional parseNonNegInt32(const std::string& value) { + auto parsed = parseInt(value); + if (!parsed || *parsed < 0 || *parsed > std::numeric_limits::max()) { + return std::nullopt; + } + return static_cast(*parsed); +} + +// Parse a non-negative int64 (used for route limits). +inline std::optional parseNonNegInt64(const std::string& value) { + auto parsed = parseInt(value); + if (!parsed || *parsed < 0) { + return std::nullopt; + } + return parsed; +} + +// Parse a 4-byte ASN (RFC 6793): an unsigned value in [0, 2^32-1]. The thrift +// fields are i64, so an unchecked uint64 parse would let an out-of-range ASN +// wrap or exceed the protocol range and be persisted. +inline std::optional parseAsn4Byte(const std::string& value) { + auto parsed = parseInt(value); + if (!parsed || *parsed > std::numeric_limits::max()) { + return std::nullopt; + } + return static_cast(*parsed); +} + +} // namespace facebook::fboss::bgpcli diff --git a/fboss/cli/fboss2/commands/config/protocol/bgp/BgpConfigSession.cpp b/fboss/cli/fboss2/commands/config/protocol/bgp/BgpConfigSession.cpp index f5378e3609638..637d5aa16337f 100644 --- a/fboss/cli/fboss2/commands/config/protocol/bgp/BgpConfigSession.cpp +++ b/fboss/cli/fboss2/commands/config/protocol/bgp/BgpConfigSession.cpp @@ -13,9 +13,16 @@ #include #include #include +#include #include +#include #include #include +#include +#include +#include +#include +#include "folly/json/dynamic.h" namespace fs = std::filesystem; @@ -42,6 +49,7 @@ folly::dynamic getDefaultBgpConfig() { } // namespace BgpConfigSession::BgpConfigSession() { + // NOLINTNEXTLINE(concurrency-mt-unsafe): single-threaded CLI const char* homeDir = std::getenv("HOME"); if (homeDir == nullptr) { throw std::runtime_error("HOME environment variable not set"); @@ -56,6 +64,7 @@ BgpConfigSession::BgpConfigSession() { bgpConfig_ = getDefaultBgpConfig(); // Check for stub mode via environment variable + // NOLINTNEXTLINE(concurrency-mt-unsafe): single-threaded CLI const char* stubEnv = std::getenv("FBOSS_BGP_STUB_MODE"); if (stubEnv != nullptr && std::string(stubEnv) == "1") { stubMode_ = true; @@ -208,20 +217,6 @@ std::optional BgpConfigSession::getConfedAsn() const { return std::nullopt; } -void BgpConfigSession::setClusterId(const std::string& clusterId) { - ensureConfigLoaded(); - bgpConfig_["cluster_id"] = clusterId; -} - -std::optional BgpConfigSession::getClusterId() const { - const_cast(this)->ensureConfigLoaded(); - if (bgpConfig_.count("cluster_id") && - !bgpConfig_["cluster_id"].asString().empty()) { - return bgpConfig_["cluster_id"].asString(); - } - return std::nullopt; -} - void BgpConfigSession::setListenAddress(const std::string& listenAddr) { ensureConfigLoaded(); bgpConfig_["listen_addr"] = listenAddr; @@ -462,284 +457,6 @@ std::optional BgpConfigSession::getUcmpWidth() const { return std::nullopt; } -// ==================== Peer Configuration ==================== - -folly::dynamic& BgpConfigSession::findOrCreatePeer( - const std::string& peerAddr) { - ensureConfigLoaded(); - auto& peers = bgpConfig_["peers"]; - - // Find existing peer - for (auto& peer : peers) { - if (peer["peer_addr"].asString() == peerAddr) { - return peer; - } - } - - // Create new peer with required fields - folly::dynamic newPeer = folly::dynamic::object("peer_addr", peerAddr)( - "local_addr", "")("next_hop4", "")("next_hop6", ""); - - peers.push_back(std::move(newPeer)); - return peers[peers.size() - 1]; -} - -void BgpConfigSession::createPeer(const std::string& peerAddr) { - findOrCreatePeer(peerAddr); -} - -void BgpConfigSession::setPeerRemoteAsn( - const std::string& peerAddr, - uint64_t remoteAsn) { - findOrCreatePeer(peerAddr)["remote_as_4_byte"] = - static_cast(remoteAsn); -} - -void BgpConfigSession::setPeerLocalAsn( - const std::string& peerAddr, - uint64_t localAsn) { - findOrCreatePeer(peerAddr)["local_as"] = static_cast(localAsn); -} - -void BgpConfigSession::setPeerDescription( - const std::string& peerAddr, - const std::string& description) { - findOrCreatePeer(peerAddr)["description"] = description; -} - -void BgpConfigSession::setPeerLocalAddr( - const std::string& peerAddr, - const std::string& localAddr) { - findOrCreatePeer(peerAddr)["local_addr"] = localAddr; -} - -void BgpConfigSession::setPeerNextHop4( - const std::string& peerAddr, - const std::string& nextHop4) { - findOrCreatePeer(peerAddr)["next_hop4"] = nextHop4; -} - -void BgpConfigSession::setPeerNextHop6( - const std::string& peerAddr, - const std::string& nextHop6) { - findOrCreatePeer(peerAddr)["next_hop6"] = nextHop6; -} - -void BgpConfigSession::setPeerGroupName( - const std::string& peerAddr, - const std::string& peerGroupName) { - findOrCreatePeer(peerAddr)["peer_group_name"] = peerGroupName; -} - -void BgpConfigSession::setPeerPassive( - const std::string& peerAddr, - bool isPassive) { - findOrCreatePeer(peerAddr)["is_passive"] = isPassive; -} - -void BgpConfigSession::setPeerRrClient( - const std::string& peerAddr, - bool isRrClient) { - findOrCreatePeer(peerAddr)["is_rr_client"] = isRrClient; -} - -void BgpConfigSession::setPeerNextHopSelf( - const std::string& peerAddr, - bool nextHopSelf) { - findOrCreatePeer(peerAddr)["next_hop_self"] = nextHopSelf; -} - -void BgpConfigSession::setPeerConfedPeer( - const std::string& peerAddr, - bool isConfedPeer) { - findOrCreatePeer(peerAddr)["is_confed_peer"] = isConfedPeer; -} - -void BgpConfigSession::setPeerPort(const std::string& peerAddr, int32_t port) { - findOrCreatePeer(peerAddr)["peer_port"] = port; -} - -void BgpConfigSession::setPeerLocalPort( - const std::string& peerAddr, - int32_t port) { - findOrCreatePeer(peerAddr)["local_port"] = port; -} - -void BgpConfigSession::setPeerConnectMode( - const std::string& peerAddr, - const std::string& connectMode) { - findOrCreatePeer(peerAddr)["connect_mode"] = connectMode; -} - -// Peer timers helper -folly::dynamic& BgpConfigSession::ensurePeerTimers( - const std::string& peerAddr) { - auto& peer = findOrCreatePeer(peerAddr); - if (!peer.count("bgp_peer_timers")) { - peer["bgp_peer_timers"] = folly::dynamic::object; - } - return peer["bgp_peer_timers"]; -} - -void BgpConfigSession::setPeerHoldTime( - const std::string& peerAddr, - int32_t holdTime) { - ensurePeerTimers(peerAddr)["hold_time_seconds"] = holdTime; -} - -void BgpConfigSession::setPeerKeepalive( - const std::string& peerAddr, - int32_t keepalive) { - ensurePeerTimers(peerAddr)["keep_alive_seconds"] = keepalive; -} - -void BgpConfigSession::setPeerOutDelay( - const std::string& peerAddr, - int32_t outDelay) { - ensurePeerTimers(peerAddr)["out_delay_seconds"] = outDelay; -} - -void BgpConfigSession::setPeerGracefulRestartTime( - const std::string& peerAddr, - int32_t seconds) { - findOrCreatePeer(peerAddr)["graceful_restart_seconds"] = seconds; -} - -void BgpConfigSession::setPeerStatefulHa( - const std::string& peerAddr, - bool enable) { - findOrCreatePeer(peerAddr)["enable_stateful_ha"] = enable; -} - -// Peer AFI/SAFI -void BgpConfigSession::setPeerDisableIpv4Afi( - const std::string& peerAddr, - bool disable) { - findOrCreatePeer(peerAddr)["disable_ipv4_afi"] = disable; -} - -void BgpConfigSession::setPeerDisableIpv6Afi( - const std::string& peerAddr, - bool disable) { - findOrCreatePeer(peerAddr)["disable_ipv6_afi"] = disable; -} - -void BgpConfigSession::setPeerIpv4LabeledUnicast( - const std::string& peerAddr, - bool enable) { - findOrCreatePeer(peerAddr)["is_afi_ipv4_lu_configured"] = enable; -} - -void BgpConfigSession::setPeerIpv6LabeledUnicast( - const std::string& peerAddr, - bool enable) { - findOrCreatePeer(peerAddr)["is_afi_ipv6_lu_configured"] = enable; -} - -void BgpConfigSession::setPeerIpv4OverIpv6Nh( - const std::string& peerAddr, - bool enable) { - findOrCreatePeer(peerAddr)["v4_over_v6_nexthop"] = enable; -} - -// Peer add-path helper -folly::dynamic& BgpConfigSession::ensurePeerAddPath( - const std::string& peerAddr) { - auto& peer = findOrCreatePeer(peerAddr); - if (!peer.count("add_path")) { - peer["add_path"] = folly::dynamic::object; - } - return peer["add_path"]; -} - -void BgpConfigSession::setPeerAddPathSend( - const std::string& peerAddr, - bool enable) { - ensurePeerAddPath(peerAddr)["send"] = enable; -} - -void BgpConfigSession::setPeerAddPathReceive( - const std::string& peerAddr, - bool enable) { - ensurePeerAddPath(peerAddr)["receive"] = enable; -} - -// Peer AS-path settings -void BgpConfigSession::setPeerRemovePrivateAs( - const std::string& peerAddr, - bool remove) { - findOrCreatePeer(peerAddr)["remove_private_as"] = remove; -} - -void BgpConfigSession::setPeerEnforceFirstAs( - const std::string& peerAddr, - bool enforce) { - findOrCreatePeer(peerAddr)["enforce_first_as"] = enforce; -} - -// Peer policies -void BgpConfigSession::setPeerIngressPolicy( - const std::string& peerAddr, - const std::string& policy) { - findOrCreatePeer(peerAddr)["ingress_policy_name"] = policy; -} - -void BgpConfigSession::setPeerEgressPolicy( - const std::string& peerAddr, - const std::string& policy) { - findOrCreatePeer(peerAddr)["egress_policy_name"] = policy; -} - -// Peer route limits helpers -folly::dynamic& BgpConfigSession::ensurePeerPreFilter( - const std::string& peerAddr) { - auto& peer = findOrCreatePeer(peerAddr); - if (!peer.count("pre_filter")) { - peer["pre_filter"] = folly::dynamic::object; - } - return peer["pre_filter"]; -} - -folly::dynamic& BgpConfigSession::ensurePeerPostFilter( - const std::string& peerAddr) { - auto& peer = findOrCreatePeer(peerAddr); - if (!peer.count("post_filter")) { - peer["post_filter"] = folly::dynamic::object; - } - return peer["post_filter"]; -} - -void BgpConfigSession::setPeerPreFilterMaxRoutes( - const std::string& peerAddr, - int64_t maxRoutes) { - ensurePeerPreFilter(peerAddr)["max_routes"] = maxRoutes; -} - -void BgpConfigSession::setPeerPostFilterMaxRoutes( - const std::string& peerAddr, - int64_t maxRoutes) { - ensurePeerPostFilter(peerAddr)["max_routes"] = maxRoutes; -} - -// Peer link bandwidth -void BgpConfigSession::setPeerAdvertiseLbw( - const std::string& peerAddr, - bool advertise) { - findOrCreatePeer(peerAddr)["advertise_link_bandwidth"] = advertise; -} - -void BgpConfigSession::setPeerReceiveLbw( - const std::string& peerAddr, - bool receive) { - findOrCreatePeer(peerAddr)["receive_link_bandwidth"] = receive; -} - -void BgpConfigSession::setPeerLbwValue( - const std::string& peerAddr, - int64_t bps) { - findOrCreatePeer(peerAddr)["link_bandwidth_bps"] = bps; -} - // ==================== Peer Group Configuration ==================== folly::dynamic& BgpConfigSession::findOrCreatePeerGroup( @@ -1038,47 +755,6 @@ void BgpConfigSession::setPeerGroupWithdrawUnprogDelay( ensurePeerGroupTimers(groupName)["withdraw_unprog_delay_seconds"] = seconds; } -// ==================== Peer Identification (RSW compatibility) -// ==================== - -void BgpConfigSession::setPeerPeerId( - const std::string& peerAddr, - const std::string& peerId) { - findOrCreatePeer(peerAddr)["peer_id"] = peerId; -} - -void BgpConfigSession::setPeerType( - const std::string& peerAddr, - const std::string& type) { - findOrCreatePeer(peerAddr)["type"] = type; -} - -void BgpConfigSession::setPeerPeerTag( - const std::string& peerAddr, - const std::string& peerTag) { - findOrCreatePeer(peerAddr)["peer_tag"] = peerTag; -} - -// Peer pre_filter warning settings -void BgpConfigSession::setPeerPreFilterWarningLimit( - const std::string& peerAddr, - int64_t limit) { - ensurePeerPreFilter(peerAddr)["warning_limit"] = limit; -} - -void BgpConfigSession::setPeerPreFilterWarningOnly( - const std::string& peerAddr, - bool warningOnly) { - ensurePeerPreFilter(peerAddr)["warning_only"] = warningOnly; -} - -// Peer timer: withdraw_unprog_delay_seconds -void BgpConfigSession::setPeerWithdrawUnprogDelay( - const std::string& peerAddr, - int32_t seconds) { - ensurePeerTimers(peerAddr)["withdraw_unprog_delay_seconds"] = seconds; -} - // ==================== Networks Configuration ==================== void BgpConfigSession::addNetwork6( diff --git a/fboss/cli/fboss2/commands/config/protocol/bgp/BgpConfigSession.h b/fboss/cli/fboss2/commands/config/protocol/bgp/BgpConfigSession.h index 30569f5a59bde..1ff564e7f70ad 100644 --- a/fboss/cli/fboss2/commands/config/protocol/bgp/BgpConfigSession.h +++ b/fboss/cli/fboss2/commands/config/protocol/bgp/BgpConfigSession.h @@ -11,6 +11,7 @@ #pragma once #include +#include #include #include @@ -42,7 +43,6 @@ namespace facebook::fboss { * - local_as_4_byte: i64 - Local AS number (RFC 6793) * - hold_time: i32 - Hold time in seconds (default 30) * - local_confed_as_4_byte: i64 - Confederation AS number - * - cluster_id: string - Route reflector cluster ID * - peers: list - List of BGP peers * - peer_groups: list - List of peer groups * @@ -103,10 +103,6 @@ class BgpConfigSession { void setConfedAsn(uint64_t asn); std::optional getConfedAsn() const; - // cluster_id: string - Route reflector cluster ID - void setClusterId(const std::string& clusterId); - std::optional getClusterId() const; - // listen_addr: string - Listen address for BGP sessions void setListenAddress(const std::string& listenAddr); std::optional getListenAddress() const; @@ -182,125 +178,6 @@ class BgpConfigSession { void setUcmpWidth(int32_t width); std::optional getUcmpWidth() const; - // ==================== Peer Configuration (Level: P) ==================== - // These map to BgpPeer thrift struct fields - - // Create/get peer by peer_addr - void createPeer(const std::string& peerAddr); - - // remote_as_4_byte: i64 - Peer's ASN - void setPeerRemoteAsn(const std::string& peerAddr, uint64_t remoteAsn); - - // local_as: i64 - Local ASN override for this peer - void setPeerLocalAsn(const std::string& peerAddr, uint64_t localAsn); - - // description: string - Peer description - void setPeerDescription( - const std::string& peerAddr, - const std::string& description); - - // local_addr: string - Local address to connect/listen on - void setPeerLocalAddr( - const std::string& peerAddr, - const std::string& localAddr); - - // next_hop4: string - IPv4 next-hop for outgoing updates - void setPeerNextHop4( - const std::string& peerAddr, - const std::string& nextHop4); - - // next_hop6: string - IPv6 next-hop for outgoing updates - void setPeerNextHop6( - const std::string& peerAddr, - const std::string& nextHop6); - - // peer_group_name: string - Reference to peer group - void setPeerGroupName( - const std::string& peerAddr, - const std::string& peerGroupName); - - // is_passive: bool - Listen for incoming connections - void setPeerPassive(const std::string& peerAddr, bool isPassive); - - // is_rr_client: bool - Route reflector client - void setPeerRrClient(const std::string& peerAddr, bool isRrClient); - - // next_hop_self: bool - Set next-hop to self - void setPeerNextHopSelf(const std::string& peerAddr, bool nextHopSelf); - - // is_confed_peer: bool - Confederation peer - void setPeerConfedPeer(const std::string& peerAddr, bool isConfedPeer); - - // peer_port: i32 - Remote peer port - void setPeerPort(const std::string& peerAddr, int32_t port); - - // local_port: i32 - Local source port - void setPeerLocalPort(const std::string& peerAddr, int32_t port); - - // connect_mode: string - PASSIVE_ACTIVE, PASSIVE_ONLY, ACTIVE_ONLY - void setPeerConnectMode( - const std::string& peerAddr, - const std::string& connectMode); - - // Peer timers - void setPeerHoldTime(const std::string& peerAddr, int32_t holdTime); - void setPeerKeepalive(const std::string& peerAddr, int32_t keepalive); - void setPeerOutDelay(const std::string& peerAddr, int32_t outDelay); - - // Peer graceful restart - void setPeerGracefulRestartTime(const std::string& peerAddr, int32_t seconds); - void setPeerStatefulHa(const std::string& peerAddr, bool enable); - - // Peer AFI/SAFI - void setPeerDisableIpv4Afi(const std::string& peerAddr, bool disable); - void setPeerDisableIpv6Afi(const std::string& peerAddr, bool disable); - void setPeerIpv4LabeledUnicast(const std::string& peerAddr, bool enable); - void setPeerIpv6LabeledUnicast(const std::string& peerAddr, bool enable); - void setPeerIpv4OverIpv6Nh(const std::string& peerAddr, bool enable); - - // Peer add-path - void setPeerAddPathSend(const std::string& peerAddr, bool enable); - void setPeerAddPathReceive(const std::string& peerAddr, bool enable); - - // Peer AS-path settings - void setPeerRemovePrivateAs(const std::string& peerAddr, bool remove); - void setPeerEnforceFirstAs(const std::string& peerAddr, bool enforce); - - // Peer policies - void setPeerIngressPolicy( - const std::string& peerAddr, - const std::string& policy); - void setPeerEgressPolicy( - const std::string& peerAddr, - const std::string& policy); - - // Peer route limits - void setPeerPreFilterMaxRoutes( - const std::string& peerAddr, - int64_t maxRoutes); - void setPeerPostFilterMaxRoutes( - const std::string& peerAddr, - int64_t maxRoutes); - - // Peer link bandwidth - void setPeerAdvertiseLbw(const std::string& peerAddr, bool advertise); - void setPeerReceiveLbw(const std::string& peerAddr, bool receive); - void setPeerLbwValue(const std::string& peerAddr, int64_t bps); - - // Peer identification (for RSW config compatibility) - void setPeerPeerId(const std::string& peerAddr, const std::string& peerId); - void setPeerType(const std::string& peerAddr, const std::string& type); - void setPeerPeerTag(const std::string& peerAddr, const std::string& peerTag); - - // Peer pre_filter warning settings - void setPeerPreFilterWarningLimit(const std::string& peerAddr, int64_t limit); - void setPeerPreFilterWarningOnly( - const std::string& peerAddr, - bool warningOnly); - - // Peer timer: withdraw_unprog_delay_seconds - void setPeerWithdrawUnprogDelay(const std::string& peerAddr, int32_t seconds); - // ==================== Peer Group Configuration (Level: PG) // ==================== These map to PeerGroup thrift struct fields @@ -468,23 +345,16 @@ class BgpConfigSession { void loadConfig(); void ensureConfigLoaded(); - // Helper to find or create a peer in the peers list - folly::dynamic& findOrCreatePeer(const std::string& peerAddr); - // Helper to find or create a peer group in the peer_groups list folly::dynamic& findOrCreatePeerGroup(const std::string& groupName); - // Helper to ensure peer timers exist - folly::dynamic& ensurePeerTimers(const std::string& peerAddr); + // Helper to ensure peer group timers exist folly::dynamic& ensurePeerGroupTimers(const std::string& groupName); // Helper to ensure add_path config exists - folly::dynamic& ensurePeerAddPath(const std::string& peerAddr); folly::dynamic& ensurePeerGroupAddPath(const std::string& groupName); // Helper to ensure pre_filter/post_filter config exists - folly::dynamic& ensurePeerPreFilter(const std::string& peerAddr); - folly::dynamic& ensurePeerPostFilter(const std::string& peerAddr); folly::dynamic& ensurePeerGroupPreFilter(const std::string& groupName); folly::dynamic& ensurePeerGroupPostFilter(const std::string& groupName); }; diff --git a/fboss/cli/fboss2/commands/config/protocol/bgp/BgpPeerAddr.h b/fboss/cli/fboss2/commands/config/protocol/bgp/BgpPeerAddr.h new file mode 100644 index 0000000000000..26b4c458b4aee --- /dev/null +++ b/fboss/cli/fboss2/commands/config/protocol/bgp/BgpPeerAddr.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 +#include +#include +#include +#include + +/** + * Peer-address helpers shared by the BGP neighbor commands (`config protocol + * bgp neighbor` / `delete protocol bgp neighbor`). + * + * peer_addr is the identity of a BgpPeer, but the same address can be spelled + * many ways (2001:DB8::1 vs 2001:db8::1), and passive listening sessions may + * use prefix notation (2001:db8::/64, per bgp_config.thrift). Both commands + * normalize the CLI-supplied address once and compare stored entries in + * normalized form so every spelling addresses the same peer. + */ +namespace facebook::fboss::bgpcli { + +// Normalize an address (or prefix, for passive listening sessions) to its +// canonical string form. nullopt if the input parses as neither. +inline std::optional normalizeBgpPeerAddr(const std::string& s) { + if (auto addr = folly::IPAddress::tryFromString(s)) { + return addr->str(); + } + if (auto network = folly::IPAddress::tryCreateNetwork( + s, /* defaultCidr */ -1, /* applyMask */ false)) { + return folly::IPAddress::networkToString(*network); + } + return std::nullopt; +} + +// Find the peer whose (normalized) peer_addr matches the given normalized +// address; peers().end() if absent. Stored entries may predate normalization, +// so their normalized form is compared when possible. +inline std::vector::iterator findBgpPeer( + bgp::thrift::BgpConfig& cfg, + const std::string& normalizedAddr) { + auto& peers = *cfg.peers(); + for (auto it = peers.begin(); it != peers.end(); ++it) { + const std::string key = + normalizeBgpPeerAddr(*it->peer_addr()).value_or(*it->peer_addr()); + if (key == normalizedAddr) { + return it; + } + } + return peers.end(); +} + +} // namespace facebook::fboss::bgpcli diff --git a/fboss/cli/fboss2/commands/config/protocol/bgp/global/CmdConfigProtocolBgpGlobal.cpp b/fboss/cli/fboss2/commands/config/protocol/bgp/global/CmdConfigProtocolBgpGlobal.cpp index ff97042a344b6..b824d1aaae0e7 100644 --- a/fboss/cli/fboss2/commands/config/protocol/bgp/global/CmdConfigProtocolBgpGlobal.cpp +++ b/fboss/cli/fboss2/commands/config/protocol/bgp/global/CmdConfigProtocolBgpGlobal.cpp @@ -12,11 +12,395 @@ #include "fboss/cli/fboss2/CmdHandler.cpp" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "fboss/cli/fboss2/commands/config/protocol/bgp/BgpCliValueParsers.h" +#include "fboss/cli/fboss2/session/ConfigSession.h" +#include "fboss/cli/fboss2/utils/CmdUtilsCommon.h" +#include "fboss/cli/fboss2/utils/HostInfo.h" +#include "fmt/format.h" + namespace facebook::fboss { +namespace { + +// CLI attribute names. Kept here (not as raw string literals at the dispatch +// sites) so the valid-attribute set and the handler table stay in sync. +constexpr std::string_view kRouterId = "router-id"; +constexpr std::string_view kLocalAsn = "local-asn"; +constexpr std::string_view kHoldTime = "hold-time"; +constexpr std::string_view kConfedAsn = "confed-asn"; +constexpr std::string_view kNetwork6 = "network6"; +constexpr std::string_view kSwitchLimit = "switch-limit"; +constexpr std::string_view kSwitchLimitTotalPath = "switch-limit-total-path"; +constexpr std::string_view kSwitchLimitMaxGoldenVips = + "switch-limit-max-golden-vips"; +constexpr std::string_view kSwitchLimitOverloadProtectionMode = + "switch-limit-overload-protection-mode"; +constexpr std::string_view kCountConfedsInAsPathLen = + "count-confeds-in-as-path-len"; +constexpr std::string_view kGracefulRestartTime = "graceful-restart-time"; +constexpr std::string_view kRibAllocatedPathIds = "rib-allocated-path-ids"; + +using Tokens = std::vector; +using BgpConfig = bgp::thrift::BgpConfig; +// Value parsers and the handler Result type are shared with the neighbor +// dispatcher (see BgpCliValueParsers.h). +using bgpcli::err; +using bgpcli::ok; +using bgpcli::parseBool; +using bgpcli::parseInt; +using bgpcli::parseNonNegInt32; +using bgpcli::Result; + +// ---- per-attribute handlers ------------------------------------------------ +// Each handler mutates the typed bgp::thrift::BgpConfig directly. Field names +// map 1:1 to bgp_config.thrift, so the compiler validates them. + +Result applyRouterId(BgpConfig& cfg, const Tokens& values) { + if (values.size() != 1) { + return err("Error: router-id requires "); + } + cfg.router_id() = values[0]; + return ok(fmt::format("Successfully set BGP router-id to: {}", values[0])); +} + +Result applyLocalAsn(BgpConfig& cfg, const Tokens& values) { + if (values.size() != 1) { + return err("Error: local-asn requires "); + } + auto asn = bgpcli::parseAsn4Byte(values[0]); + if (!asn) { + return err( + fmt::format( + "Error: Invalid local-asn value '{}'; expected an unsigned " + "4-byte ASN", + values[0])); + } + cfg.local_as_4_byte() = *asn; + return ok(fmt::format("Successfully set BGP local-asn to: {}", *asn)); +} + +Result applyConfedAsn(BgpConfig& cfg, const Tokens& values) { + if (values.size() != 1) { + return err("Error: confed-asn requires "); + } + auto asn = bgpcli::parseAsn4Byte(values[0]); + if (!asn) { + return err( + fmt::format( + "Error: Invalid confed-asn value '{}'; expected an unsigned " + "4-byte ASN", + values[0])); + } + cfg.local_confed_as_4_byte() = *asn; + return ok( + fmt::format("Successfully set BGP confederation AS number to: {}", *asn)); +} + +Result applyHoldTime(BgpConfig& cfg, const Tokens& values) { + if (values.size() != 1) { + return err("Error: hold-time requires "); + } + auto seconds = parseNonNegInt32(values[0]); + if (!seconds) { + return err(fmt::format("Error: Invalid hold-time value '{}'", values[0])); + } + cfg.hold_time() = *seconds; + return ok( + fmt::format("Successfully set BGP hold-time to: {} seconds", *seconds)); +} + +Result applyGracefulRestartTime(BgpConfig& cfg, const Tokens& values) { + if (values.size() != 1) { + return err("Error: graceful-restart-time requires "); + } + auto seconds = parseNonNegInt32(values[0]); + if (!seconds) { + return err( + fmt::format( + "Error: graceful-restart-time must be a non-negative integer " + "that fits in int32, got '{}'", + values[0])); + } + cfg.graceful_restart_convergence_seconds() = *seconds; + return ok( + fmt::format( + "Successfully set BGP graceful-restart-time to: {} seconds", + *seconds)); +} + +Result applyCountConfedsInAsPathLen(BgpConfig& cfg, const Tokens& values) { + if (values.size() != 1) { + return err("Error: count-confeds-in-as-path-len requires "); + } + auto enable = parseBool(values[0]); + if (!enable) { + return err( + fmt::format( + "Error: Invalid value '{}'; expected true or false", values[0])); + } + cfg.count_confeds_in_as_path_len() = *enable; + return ok( + fmt::format( + "Successfully {} count-confeds-in-as-path-len", + *enable ? "enabled" : "disabled")); +} + +Result applyRibAllocatedPathIds(BgpConfig& cfg, const Tokens& values) { + if (values.size() != 1) { + return err("Error: rib-allocated-path-ids requires "); + } + auto enable = parseBool(values[0]); + if (!enable) { + return err( + fmt::format( + "Error: Invalid value '{}'; expected true or false", values[0])); + } + // enable_rib_allocated_path_id lives inside the nested bgp_setting_config. + cfg.bgp_setting_config().ensure().enable_rib_allocated_path_id() = *enable; + return ok( + fmt::format( + "Successfully {} rib-allocated-path-ids", + *enable ? "enabled" : "disabled")); +} + +// Shared implementation for the switch-limit attributes, which differ only in +// the field setter, the field name in the success message, and the upper bound +// (i64 fields use INT64_MAX; the i32 max_golden_vips field uses INT32_MAX so an +// out-of-range value is rejected rather than silently truncated). All limits +// must be non-negative. +Result applyInt64Limit( + BgpConfig& cfg, + const Tokens& values, + std::string_view attrName, + std::string_view fieldName, + int64_t maxValue, + const std::function& setter) { + if (values.size() != 1) { + return err(fmt::format("Error: {} requires ", attrName)); + } + auto limit = parseInt(values[0]); + if (!limit) { + return err( + fmt::format("Error: Invalid {} value '{}'", attrName, values[0])); + } + if (*limit < 0 || *limit > maxValue) { + return err( + fmt::format( + "Error: {} value '{}' is out of range [0, {}]", + attrName, + values[0], + maxValue)); + } + setter(cfg, *limit); + return ok( + fmt::format( + "Successfully set switch_limit_config {} to: {}", fieldName, *limit)); +} + +Result applyOverloadProtectionMode(BgpConfig& cfg, const Tokens& values) { + if (values.size() != 1) { + return err("Error: switch-limit-overload-protection-mode requires "); + } + auto mode = parseInt(values[0]); + if (!mode) { + return err( + fmt::format( + "Error: Invalid overload-protection-mode value '{}'", values[0])); + } + cfg.switch_limit_config().ensure().overload_protection_mode() = + static_cast(*mode); + return ok( + fmt::format( + "Successfully set switch_limit_config overload_protection_mode to: {}", + *mode)); +} + +Result applyNetwork6(BgpConfig& cfg, const Tokens& values) { + // Usage: network6 add [policy ] [install-to-fib ] + // [min-routes ] + if (values.size() < 2 || values[0] != "add") { + return err( + "Error: usage: network6 add [policy ] " + "[install-to-fib ] [min-routes ]"); + } + std::string prefix = values[1]; + std::string policyName; + bool installToFib = false; + int32_t minSupportingRoutes = 0; + + for (size_t i = 2; i < values.size(); i++) { + if (values[i] == "policy" && i + 1 < values.size()) { + policyName = values[++i]; + } else if (values[i] == "install-to-fib" && i + 1 < values.size()) { + auto parsed = parseBool(values[++i]); + if (!parsed) { + return err( + fmt::format( + "Error: Invalid install-to-fib value '{}'; expected true or false", + values[i])); + } + installToFib = *parsed; + } else if (values[i] == "min-routes" && i + 1 < values.size()) { + auto parsed = parseNonNegInt32(values[++i]); + if (!parsed) { + return err( + fmt::format("Error: Invalid min-routes value '{}'", values[i])); + } + minSupportingRoutes = *parsed; + } else { + return err( + fmt::format("Error: unexpected network6 token '{}'", values[i])); + } + } + + // Find-or-update the networks6 entry keyed by prefix. + auto& networks = *cfg.networks6(); + bgp::thrift::BgpNetwork* entry = nullptr; + for (auto& n : networks) { + if (*n.prefix() == prefix) { + entry = &n; + break; + } + } + if (entry == nullptr) { + networks.emplace_back(); + entry = &networks.back(); + } + entry->prefix() = prefix; + entry->policy_name() = policyName; + entry->install_to_fib() = installToFib; + entry->minimum_supporting_routes() = minSupportingRoutes; + + return ok( + fmt::format( + "Successfully added network6 prefix: {} (policy: {}, install_to_fib: {}, " + "min_routes: {})", + prefix, + policyName.empty() ? "(none)" : policyName, + installToFib ? "true" : "false", + minSupportingRoutes)); +} + +using AttrHandler = std::function; + +const std::map& attrHandlers() { + static const std::map kHandlers = { + {kRouterId, applyRouterId}, + {kLocalAsn, applyLocalAsn}, + {kConfedAsn, applyConfedAsn}, + {kHoldTime, applyHoldTime}, + {kGracefulRestartTime, applyGracefulRestartTime}, + {kCountConfedsInAsPathLen, applyCountConfedsInAsPathLen}, + {kRibAllocatedPathIds, applyRibAllocatedPathIds}, + {kNetwork6, applyNetwork6}, + {kSwitchLimit, + [](BgpConfig& cfg, const Tokens& v) { + return applyInt64Limit( + cfg, + v, + kSwitchLimit, + "prefix_limit", + std::numeric_limits::max(), + [](BgpConfig& c, int64_t l) { + c.switch_limit_config().ensure().prefix_limit() = l; + }); + }}, + {kSwitchLimitTotalPath, + [](BgpConfig& cfg, const Tokens& v) { + return applyInt64Limit( + cfg, + v, + kSwitchLimitTotalPath, + "total_path_limit", + std::numeric_limits::max(), + [](BgpConfig& c, int64_t l) { + c.switch_limit_config().ensure().total_path_limit() = l; + }); + }}, + {kSwitchLimitMaxGoldenVips, + [](BgpConfig& cfg, const Tokens& v) { + // max_golden_vips is an i32 field; bound it so an out-of-range value + // is rejected rather than silently truncated by the static_cast. + return applyInt64Limit( + cfg, + v, + kSwitchLimitMaxGoldenVips, + "max_golden_vips", + std::numeric_limits::max(), + [](BgpConfig& c, int64_t l) { + c.switch_limit_config().ensure().max_golden_vips() = + static_cast(l); + }); + }}, + {kSwitchLimitOverloadProtectionMode, applyOverloadProtectionMode}, + }; + return kHandlers; +} + +std::string validAttrList() { + std::string out; + for (const auto& [name, _] : attrHandlers()) { + if (!out.empty()) { + out += ", "; + } + out += std::string(name); + } + return out; +} + +} // namespace + +// Parse + validate at construction so queryClient stays a thin dispatch. +// Throwing std::invalid_argument is how the framework surfaces arg parse +// errors (same mechanism as InterfacesConfig / parseTokens). +BgpGlobalConfig::BgpGlobalConfig(std::vector v) + : utils::BaseObjectArgType(v) { + if (v.empty()) { + throw std::invalid_argument( + fmt::format( + "Error: attribute is required. Valid attributes: {}", + validAttrList())); + } + if (attrHandlers().find(v[0]) == attrHandlers().end()) { + throw std::invalid_argument( + fmt::format( + "Error: unknown global attribute '{}'. Valid attributes: {}", + v[0], + validAttrList())); + } + attr_ = v[0]; + values_.assign(v.begin() + 1, v.end()); +} + CmdConfigProtocolBgpGlobalTraits::RetType -CmdConfigProtocolBgpGlobal::queryClient(const HostInfo& /* hostInfo */) { - return "BGP global configuration. Use subcommands: router-id, local-asn, hold-time, graceful-restart, best-path, link-bw, nh-tracking, network-originate"; +CmdConfigProtocolBgpGlobal::queryClient( + const HostInfo& /* hostInfo */, + const ObjectArgType& args) { + auto& session = ConfigSession::getInstance(); + + // The attribute is guaranteed valid: BgpGlobalConfig's constructor rejects + // an empty/unknown attribute before we get here. + auto handler = attrHandlers().find(args.attr()); + Result result = handler->second(session.getBgpConfig(), args.values()); + if (result.ok) { + session.saveBgpConfig(); + result.message += + fmt::format("\nConfig saved to: {}", session.getBgpSessionConfigPath()); + } + return result.message; } void CmdConfigProtocolBgpGlobal::printOutput(const RetType& output) { diff --git a/fboss/cli/fboss2/commands/config/protocol/bgp/global/CmdConfigProtocolBgpGlobal.h b/fboss/cli/fboss2/commands/config/protocol/bgp/global/CmdConfigProtocolBgpGlobal.h index 0a6faa7a09fb6..2c64bc0d4d537 100644 --- a/fboss/cli/fboss2/commands/config/protocol/bgp/global/CmdConfigProtocolBgpGlobal.h +++ b/fboss/cli/fboss2/commands/config/protocol/bgp/global/CmdConfigProtocolBgpGlobal.h @@ -10,14 +10,50 @@ #pragma once +#include +#include +#include "CLI/App.hpp" #include "fboss/cli/fboss2/CmdHandler.h" #include "fboss/cli/fboss2/commands/config/protocol/bgp/CmdConfigProtocolBgp.h" +#include "fboss/cli/fboss2/utils/CmdUtilsCommon.h" +#include "fboss/cli/fboss2/utils/HostInfo.h" namespace facebook::fboss { +// Parsed `config protocol bgp global [value ...]`, validated at +// construction. values() is greedy to keep network6's multi-token tail. +class BgpGlobalConfig : public utils::BaseObjectArgType { + public: + // NOLINTNEXTLINE(google-explicit-constructor) + /* implicit */ BgpGlobalConfig(std::vector v); + const std::string& attr() const { + return attr_; + } + const std::vector& values() const { + return values_; + } + const static utils::ObjectArgTypeId id = + utils::ObjectArgTypeId::OBJECT_ARG_TYPE_ID_MESSAGE; + + private: + std::string attr_; + std::vector values_; +}; + +// Single handler for the whole `config protocol bgp global` family. The +// attribute name is the first positional token and the remaining tokens are +// its value(s); dispatch and per-attribute parsing live in the .cpp so adding +// a new global tunable is a one-entry change rather than a new command class. struct CmdConfigProtocolBgpGlobalTraits : public WriteCommandTraits { using ParentCmd = CmdConfigProtocolBgp; - using ObjectArgType = std::monostate; + static void addCliArg(CLI::App& cmd, std::vector& args) { + // Stop CLI11's parent-chain subcommand fallthrough from stealing value + // tokens that happen to match a sibling command name (e.g. a policy + // named "peer-group"); see CmdConfigProtocolBgpNeighborTraits. + cmd.positionals_at_end(); + cmd.add_option("args", args, " [value ...]"); + } + using ObjectArgType = BgpGlobalConfig; using RetType = std::string; }; @@ -28,7 +64,7 @@ class CmdConfigProtocolBgpGlobal : public CmdHandler< using ObjectArgType = CmdConfigProtocolBgpGlobalTraits::ObjectArgType; using RetType = CmdConfigProtocolBgpGlobalTraits::RetType; - RetType queryClient(const HostInfo& hostInfo); + RetType queryClient(const HostInfo& hostInfo, const ObjectArgType& args); void printOutput(const RetType& output); }; diff --git a/fboss/cli/fboss2/commands/config/protocol/bgp/global/CmdConfigProtocolBgpGlobalClusterId.cpp b/fboss/cli/fboss2/commands/config/protocol/bgp/global/CmdConfigProtocolBgpGlobalClusterId.cpp deleted file mode 100644 index ed1b541d0298f..0000000000000 --- a/fboss/cli/fboss2/commands/config/protocol/bgp/global/CmdConfigProtocolBgpGlobalClusterId.cpp +++ /dev/null @@ -1,49 +0,0 @@ -/* - * Copyright (c) 2004-present, Facebook, Inc. - * All rights reserved. - * - * This source code is licensed under the BSD-style license found in the - * LICENSE file in the root directory of this source tree. An additional grant - * of patent rights can be found in the PATENTS file in the same directory. - * - */ - -#include "fboss/cli/fboss2/commands/config/protocol/bgp/global/CmdConfigProtocolBgpGlobalClusterId.h" - -#include "fboss/cli/fboss2/CmdHandler.cpp" - -#include -#include "fboss/cli/fboss2/commands/config/protocol/bgp/BgpConfigSession.h" - -namespace facebook::fboss { - -CmdConfigProtocolBgpGlobalClusterIdTraits::RetType -CmdConfigProtocolBgpGlobalClusterId::queryClient( - const HostInfo& /* hostInfo */, - const ObjectArgType& clusterIdList) { - auto& session = BgpConfigSession::getInstance(); - - if (clusterIdList.data().empty()) { - return "Error: cluster-id is required"; - } - - std::string clusterId = clusterIdList.data()[0]; - session.setClusterId(clusterId); - session.saveConfig(); - - return fmt::format( - "Successfully set BGP cluster-id to: {}\nConfig saved to: {}", - clusterId, - session.getSessionConfigPath()); -} - -void CmdConfigProtocolBgpGlobalClusterId::printOutput(const RetType& output) { - std::cout << output << std::endl; -} - -// Explicit template instantiation -template void CmdHandler< - CmdConfigProtocolBgpGlobalClusterId, - CmdConfigProtocolBgpGlobalClusterIdTraits>::run(); - -} // namespace facebook::fboss diff --git a/fboss/cli/fboss2/commands/config/protocol/bgp/global/CmdConfigProtocolBgpGlobalClusterId.h b/fboss/cli/fboss2/commands/config/protocol/bgp/global/CmdConfigProtocolBgpGlobalClusterId.h deleted file mode 100644 index 8345a5b485888..0000000000000 --- a/fboss/cli/fboss2/commands/config/protocol/bgp/global/CmdConfigProtocolBgpGlobalClusterId.h +++ /dev/null @@ -1,42 +0,0 @@ -/* - * 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/config/protocol/bgp/global/CmdConfigProtocolBgpGlobal.h" -#include "fboss/cli/fboss2/utils/CmdUtils.h" - -namespace facebook::fboss { - -struct CmdConfigProtocolBgpGlobalClusterIdTraits : public WriteCommandTraits { - using ParentCmd = CmdConfigProtocolBgpGlobal; - static void addCliArg(CLI::App& cmd, std::vector& args) { - cmd.add_option("ipAddrs", args, "IPv4 or IPv6 addr(s)"); - } - using ObjectArgType = utils::IPList; - using RetType = std::string; -}; - -class CmdConfigProtocolBgpGlobalClusterId - : public CmdHandler< - CmdConfigProtocolBgpGlobalClusterId, - CmdConfigProtocolBgpGlobalClusterIdTraits> { - public: - using ObjectArgType = - CmdConfigProtocolBgpGlobalClusterIdTraits::ObjectArgType; - using RetType = CmdConfigProtocolBgpGlobalClusterIdTraits::RetType; - - RetType queryClient(const HostInfo& hostInfo, const ObjectArgType& clusterId); - - void printOutput(const RetType& output); -}; - -} // namespace facebook::fboss diff --git a/fboss/cli/fboss2/commands/config/protocol/bgp/global/CmdConfigProtocolBgpGlobalConfedAsn.cpp b/fboss/cli/fboss2/commands/config/protocol/bgp/global/CmdConfigProtocolBgpGlobalConfedAsn.cpp deleted file mode 100644 index bcc6512e89a38..0000000000000 --- a/fboss/cli/fboss2/commands/config/protocol/bgp/global/CmdConfigProtocolBgpGlobalConfedAsn.cpp +++ /dev/null @@ -1,57 +0,0 @@ -/* - * Copyright (c) 2004-present, Facebook, Inc. - * All rights reserved. - * - * This source code is licensed under the BSD-style license found in the - * LICENSE file in the root directory of this source tree. An additional grant - * of patent rights can be found in the PATENTS file in the same directory. - * - */ - -#include "fboss/cli/fboss2/commands/config/protocol/bgp/global/CmdConfigProtocolBgpGlobalConfedAsn.h" - -#include "fboss/cli/fboss2/CmdHandler.cpp" - -#include -#include -#include "fboss/cli/fboss2/commands/config/protocol/bgp/BgpConfigSession.h" - -namespace facebook::fboss { - -CmdConfigProtocolBgpGlobalConfedAsnTraits::RetType -CmdConfigProtocolBgpGlobalConfedAsn::queryClient( - const HostInfo& /* hostInfo */, - const ObjectArgType& confedAsnMsg) { - auto& session = BgpConfigSession::getInstance(); - - if (confedAsnMsg.data().empty()) { - return "Error: confed-asn value is required"; - } - - std::string asnStr = confedAsnMsg.data()[0]; - uint64_t confedAsn; - try { - confedAsn = folly::to(asnStr); - } catch (const std::exception& e) { - return fmt::format("Error: Invalid confed-asn '{}': {}", asnStr, e.what()); - } - - session.setConfedAsn(confedAsn); - session.saveConfig(); - - return fmt::format( - "Successfully set BGP confederation AS number to: {}\nConfig saved to: {}", - confedAsn, - session.getSessionConfigPath()); -} - -void CmdConfigProtocolBgpGlobalConfedAsn::printOutput(const RetType& output) { - std::cout << output << std::endl; -} - -// Explicit template instantiation -template void CmdHandler< - CmdConfigProtocolBgpGlobalConfedAsn, - CmdConfigProtocolBgpGlobalConfedAsnTraits>::run(); - -} // namespace facebook::fboss diff --git a/fboss/cli/fboss2/commands/config/protocol/bgp/global/CmdConfigProtocolBgpGlobalConfedAsn.h b/fboss/cli/fboss2/commands/config/protocol/bgp/global/CmdConfigProtocolBgpGlobalConfedAsn.h deleted file mode 100644 index 2020e1b4cb1f6..0000000000000 --- a/fboss/cli/fboss2/commands/config/protocol/bgp/global/CmdConfigProtocolBgpGlobalConfedAsn.h +++ /dev/null @@ -1,42 +0,0 @@ -/* - * 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/config/protocol/bgp/global/CmdConfigProtocolBgpGlobal.h" -#include "fboss/cli/fboss2/utils/CmdUtils.h" - -namespace facebook::fboss { - -struct CmdConfigProtocolBgpGlobalConfedAsnTraits : public WriteCommandTraits { - using ParentCmd = CmdConfigProtocolBgpGlobal; - static void addCliArg(CLI::App& cmd, std::vector& args) { - cmd.add_option("msg", args, "Message"); - } - using ObjectArgType = utils::Message; - using RetType = std::string; -}; - -class CmdConfigProtocolBgpGlobalConfedAsn - : public CmdHandler< - CmdConfigProtocolBgpGlobalConfedAsn, - CmdConfigProtocolBgpGlobalConfedAsnTraits> { - public: - using ObjectArgType = - CmdConfigProtocolBgpGlobalConfedAsnTraits::ObjectArgType; - using RetType = CmdConfigProtocolBgpGlobalConfedAsnTraits::RetType; - - RetType queryClient(const HostInfo& hostInfo, const ObjectArgType& confedAsn); - - void printOutput(const RetType& output); -}; - -} // namespace facebook::fboss diff --git a/fboss/cli/fboss2/commands/config/protocol/bgp/global/CmdConfigProtocolBgpGlobalHoldTime.cpp b/fboss/cli/fboss2/commands/config/protocol/bgp/global/CmdConfigProtocolBgpGlobalHoldTime.cpp deleted file mode 100644 index c526e68c97028..0000000000000 --- a/fboss/cli/fboss2/commands/config/protocol/bgp/global/CmdConfigProtocolBgpGlobalHoldTime.cpp +++ /dev/null @@ -1,60 +0,0 @@ -/* - * Copyright (c) 2004-present, Facebook, Inc. - * All rights reserved. - * - * This source code is licensed under the BSD-style license found in the - * LICENSE file in the root directory of this source tree. An additional grant - * of patent rights can be found in the PATENTS file in the same directory. - * - */ - -#include "fboss/cli/fboss2/commands/config/protocol/bgp/global/CmdConfigProtocolBgpGlobalHoldTime.h" - -#include "fboss/cli/fboss2/CmdHandler.cpp" - -#include -#include -#include "fboss/cli/fboss2/commands/config/protocol/bgp/BgpConfigSession.h" - -namespace facebook::fboss { - -CmdConfigProtocolBgpGlobalHoldTimeTraits::RetType -CmdConfigProtocolBgpGlobalHoldTime::queryClient( - const HostInfo& /* hostInfo */, - const ObjectArgType& holdTimeMsg) { - auto& session = BgpConfigSession::getInstance(); - - if (holdTimeMsg.data().empty()) { - return "Error: hold-time value is required"; - } - - // Extract string and parse to uint32_t - std::string holdTimeStr = holdTimeMsg.data()[0]; - uint32_t holdTime; - try { - holdTime = folly::to(holdTimeStr); - } catch (const std::exception& e) { - return fmt::format( - "Error: Invalid hold-time value '{}': {}", holdTimeStr, e.what()); - } - - // Set the hold-time in the BGP config session - session.setHoldTime(holdTime); - session.saveConfig(); - - return fmt::format( - "Successfully set BGP hold-time to: {} seconds\nConfig saved to: {}", - holdTime, - session.getSessionConfigPath()); -} - -void CmdConfigProtocolBgpGlobalHoldTime::printOutput(const RetType& output) { - std::cout << output << std::endl; -} - -// Explicit template instantiation -template void CmdHandler< - CmdConfigProtocolBgpGlobalHoldTime, - CmdConfigProtocolBgpGlobalHoldTimeTraits>::run(); - -} // namespace facebook::fboss diff --git a/fboss/cli/fboss2/commands/config/protocol/bgp/global/CmdConfigProtocolBgpGlobalHoldTime.h b/fboss/cli/fboss2/commands/config/protocol/bgp/global/CmdConfigProtocolBgpGlobalHoldTime.h deleted file mode 100644 index 1db1004d320b3..0000000000000 --- a/fboss/cli/fboss2/commands/config/protocol/bgp/global/CmdConfigProtocolBgpGlobalHoldTime.h +++ /dev/null @@ -1,41 +0,0 @@ -/* - * 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/config/protocol/bgp/global/CmdConfigProtocolBgpGlobal.h" -#include "fboss/cli/fboss2/utils/CmdUtils.h" - -namespace facebook::fboss { - -struct CmdConfigProtocolBgpGlobalHoldTimeTraits : public WriteCommandTraits { - using ParentCmd = CmdConfigProtocolBgpGlobal; - static void addCliArg(CLI::App& cmd, std::vector& args) { - cmd.add_option("msg", args, "Message"); - } - using ObjectArgType = utils::Message; - using RetType = std::string; -}; - -class CmdConfigProtocolBgpGlobalHoldTime - : public CmdHandler< - CmdConfigProtocolBgpGlobalHoldTime, - CmdConfigProtocolBgpGlobalHoldTimeTraits> { - public: - using ObjectArgType = CmdConfigProtocolBgpGlobalHoldTimeTraits::ObjectArgType; - using RetType = CmdConfigProtocolBgpGlobalHoldTimeTraits::RetType; - - RetType queryClient(const HostInfo& hostInfo, const ObjectArgType& holdTime); - - void printOutput(const RetType& output); -}; - -} // namespace facebook::fboss diff --git a/fboss/cli/fboss2/commands/config/protocol/bgp/global/CmdConfigProtocolBgpGlobalLocalAsn.cpp b/fboss/cli/fboss2/commands/config/protocol/bgp/global/CmdConfigProtocolBgpGlobalLocalAsn.cpp deleted file mode 100644 index 81a8571197a79..0000000000000 --- a/fboss/cli/fboss2/commands/config/protocol/bgp/global/CmdConfigProtocolBgpGlobalLocalAsn.cpp +++ /dev/null @@ -1,59 +0,0 @@ -/* - * Copyright (c) 2004-present, Facebook, Inc. - * All rights reserved. - * - * This source code is licensed under the BSD-style license found in the - * LICENSE file in the root directory of this source tree. An additional grant - * of patent rights can be found in the PATENTS file in the same directory. - * - */ - -#include "fboss/cli/fboss2/commands/config/protocol/bgp/global/CmdConfigProtocolBgpGlobalLocalAsn.h" - -#include "fboss/cli/fboss2/CmdHandler.cpp" - -#include -#include "fboss/cli/fboss2/commands/config/protocol/bgp/BgpConfigSession.h" - -namespace facebook::fboss { - -CmdConfigProtocolBgpGlobalLocalAsnTraits::RetType -CmdConfigProtocolBgpGlobalLocalAsn::queryClient( - const HostInfo& /* hostInfo */, - const ObjectArgType& localAsnMsg) { - auto& session = BgpConfigSession::getInstance(); - - if (localAsnMsg.data().empty()) { - return "Error: local-asn value is required"; - } - - // Extract string and parse to uint32_t - std::string asnStr = localAsnMsg.data()[0]; - uint32_t localAsn; - try { - localAsn = folly::to(asnStr); - } catch (const std::exception& e) { - return fmt::format( - "Error: Invalid local-asn value '{}': {}", asnStr, e.what()); - } - - // Set the local-asn in the BGP config session - session.setLocalAsn(localAsn); - session.saveConfig(); - - return fmt::format( - "Successfully set BGP local-asn to: {}\nConfig saved to: {}", - localAsn, - session.getSessionConfigPath()); -} - -void CmdConfigProtocolBgpGlobalLocalAsn::printOutput(const RetType& output) { - std::cout << output << std::endl; -} - -// Explicit template instantiation -template void CmdHandler< - CmdConfigProtocolBgpGlobalLocalAsn, - CmdConfigProtocolBgpGlobalLocalAsnTraits>::run(); - -} // namespace facebook::fboss diff --git a/fboss/cli/fboss2/commands/config/protocol/bgp/global/CmdConfigProtocolBgpGlobalLocalAsn.h b/fboss/cli/fboss2/commands/config/protocol/bgp/global/CmdConfigProtocolBgpGlobalLocalAsn.h deleted file mode 100644 index d39cc07e490bf..0000000000000 --- a/fboss/cli/fboss2/commands/config/protocol/bgp/global/CmdConfigProtocolBgpGlobalLocalAsn.h +++ /dev/null @@ -1,41 +0,0 @@ -/* - * 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/config/protocol/bgp/global/CmdConfigProtocolBgpGlobal.h" -#include "fboss/cli/fboss2/utils/CmdUtils.h" - -namespace facebook::fboss { - -struct CmdConfigProtocolBgpGlobalLocalAsnTraits : public WriteCommandTraits { - using ParentCmd = CmdConfigProtocolBgpGlobal; - static void addCliArg(CLI::App& cmd, std::vector& args) { - cmd.add_option("msg", args, "Message"); - } - using ObjectArgType = utils::Message; - using RetType = std::string; -}; - -class CmdConfigProtocolBgpGlobalLocalAsn - : public CmdHandler< - CmdConfigProtocolBgpGlobalLocalAsn, - CmdConfigProtocolBgpGlobalLocalAsnTraits> { - public: - using ObjectArgType = CmdConfigProtocolBgpGlobalLocalAsnTraits::ObjectArgType; - using RetType = CmdConfigProtocolBgpGlobalLocalAsnTraits::RetType; - - RetType queryClient(const HostInfo& hostInfo, const ObjectArgType& localAsn); - - void printOutput(const RetType& output); -}; - -} // namespace facebook::fboss diff --git a/fboss/cli/fboss2/commands/config/protocol/bgp/global/CmdConfigProtocolBgpGlobalNetwork6Add.cpp b/fboss/cli/fboss2/commands/config/protocol/bgp/global/CmdConfigProtocolBgpGlobalNetwork6Add.cpp deleted file mode 100644 index 336f5f50f6f70..0000000000000 --- a/fboss/cli/fboss2/commands/config/protocol/bgp/global/CmdConfigProtocolBgpGlobalNetwork6Add.cpp +++ /dev/null @@ -1,84 +0,0 @@ -/* - * Copyright (c) 2004-present, Facebook, Inc. - * All rights reserved. - * - * This source code is licensed under the BSD-style license found in the - * LICENSE file in the root directory of this source tree. An additional grant - * of patent rights can be found in the PATENTS file in the same directory. - * - */ - -#include "fboss/cli/fboss2/commands/config/protocol/bgp/global/CmdConfigProtocolBgpGlobalNetwork6Add.h" - -#include "fboss/cli/fboss2/CmdHandler.cpp" - -#include -#include -#include "fboss/cli/fboss2/commands/config/protocol/bgp/BgpConfigSession.h" - -namespace facebook::fboss { - -CmdConfigProtocolBgpGlobalNetwork6AddTraits::RetType -CmdConfigProtocolBgpGlobalNetwork6Add::queryClient( - const HostInfo& /* hostInfo */, - const ObjectArgType& args) { - auto& session = BgpConfigSession::getInstance(); - - // args.data() returns a vector of strings - // Command: "config protocol bgp global network6 add [policy ] - // [install-to-fib ] [min-routes ]" - // After "network6", args contains: ["add", "", ...] - // So we need to skip tokens[0] which is "add" - const auto& tokens = args.data(); - - if (tokens.size() < 2) { - return "Error: prefix is required. Usage: network6 add [policy " - "] [install-to-fib ] [min-routes ]"; - } - - // tokens[0] is "add", tokens[1] is the actual prefix - std::string prefixStr = tokens[1]; - std::string policyName; - bool installToFib = false; - int32_t minSupportingRoutes = 0; - - // Start parsing optional args from index 2 - for (size_t i = 2; i < tokens.size(); i++) { - if (tokens[i] == "policy" && i + 1 < tokens.size()) { - policyName = tokens[++i]; - } else if (tokens[i] == "install-to-fib" && i + 1 < tokens.size()) { - std::string val = tokens[++i]; - installToFib = (val == "true" || val == "1"); - } else if (tokens[i] == "min-routes" && i + 1 < tokens.size()) { - std::string minRoutesStr = tokens[++i]; - try { - minSupportingRoutes = folly::to(minRoutesStr); - } catch (const std::exception& e) { - return fmt::format( - "Error: Invalid min-routes value '{}': {}", minRoutesStr, e.what()); - } - } - } - - session.addNetwork6(prefixStr, policyName, installToFib, minSupportingRoutes); - session.saveConfig(); - - return fmt::format( - "Successfully added network6 prefix: {} (policy: {}, install_to_fib: {}, min_routes: {})\nConfig saved to: {}", - prefixStr, - policyName.empty() ? "(none)" : policyName, - installToFib ? "true" : "false", - minSupportingRoutes, - session.getSessionConfigPath()); -} - -void CmdConfigProtocolBgpGlobalNetwork6Add::printOutput(const RetType& output) { - std::cout << output << std::endl; -} - -// Explicit template instantiation -template void CmdHandler< - CmdConfigProtocolBgpGlobalNetwork6Add, - CmdConfigProtocolBgpGlobalNetwork6AddTraits>::run(); - -} // namespace facebook::fboss diff --git a/fboss/cli/fboss2/commands/config/protocol/bgp/global/CmdConfigProtocolBgpGlobalNetwork6Add.h b/fboss/cli/fboss2/commands/config/protocol/bgp/global/CmdConfigProtocolBgpGlobalNetwork6Add.h deleted file mode 100644 index 885e7b3e2b63a..0000000000000 --- a/fboss/cli/fboss2/commands/config/protocol/bgp/global/CmdConfigProtocolBgpGlobalNetwork6Add.h +++ /dev/null @@ -1,42 +0,0 @@ -/* - * 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/config/protocol/bgp/global/CmdConfigProtocolBgpGlobal.h" -#include "fboss/cli/fboss2/utils/CmdUtils.h" - -namespace facebook::fboss { - -struct CmdConfigProtocolBgpGlobalNetwork6AddTraits : public WriteCommandTraits { - using ParentCmd = CmdConfigProtocolBgpGlobal; - static void addCliArg(CLI::App& cmd, std::vector& args) { - cmd.add_option("msg", args, "Message"); - } - using ObjectArgType = utils::Message; - using RetType = std::string; -}; - -class CmdConfigProtocolBgpGlobalNetwork6Add - : public CmdHandler< - CmdConfigProtocolBgpGlobalNetwork6Add, - CmdConfigProtocolBgpGlobalNetwork6AddTraits> { - public: - using ObjectArgType = - CmdConfigProtocolBgpGlobalNetwork6AddTraits::ObjectArgType; - using RetType = CmdConfigProtocolBgpGlobalNetwork6AddTraits::RetType; - - RetType queryClient(const HostInfo& hostInfo, const ObjectArgType& args); - - void printOutput(const RetType& output); -}; - -} // namespace facebook::fboss diff --git a/fboss/cli/fboss2/commands/config/protocol/bgp/global/CmdConfigProtocolBgpGlobalRouterId.cpp b/fboss/cli/fboss2/commands/config/protocol/bgp/global/CmdConfigProtocolBgpGlobalRouterId.cpp deleted file mode 100644 index c7093026cd1e7..0000000000000 --- a/fboss/cli/fboss2/commands/config/protocol/bgp/global/CmdConfigProtocolBgpGlobalRouterId.cpp +++ /dev/null @@ -1,52 +0,0 @@ -/* - * Copyright (c) 2004-present, Facebook, Inc. - * All rights reserved. - * - * This source code is licensed under the BSD-style license found in the - * LICENSE file in the root directory of this source tree. An additional grant - * of patent rights can be found in the PATENTS file in the same directory. - * - */ - -#include "fboss/cli/fboss2/commands/config/protocol/bgp/global/CmdConfigProtocolBgpGlobalRouterId.h" - -#include "fboss/cli/fboss2/CmdHandler.cpp" - -#include -#include "fboss/cli/fboss2/commands/config/protocol/bgp/BgpConfigSession.h" - -namespace facebook::fboss { - -CmdConfigProtocolBgpGlobalRouterIdTraits::RetType -CmdConfigProtocolBgpGlobalRouterId::queryClient( - const HostInfo& /* hostInfo */, - const ObjectArgType& routerIdList) { - auto& session = BgpConfigSession::getInstance(); - - if (routerIdList.data().empty()) { - return "Error: router-id is required"; - } - - // Extract the first IP from the list as router-id - std::string routerId = routerIdList.data()[0]; - - // Set the router-id in the BGP config session - session.setRouterId(routerId); - session.saveConfig(); - - return fmt::format( - "Successfully set BGP router-id to: {}\nConfig saved to: {}", - routerId, - session.getSessionConfigPath()); -} - -void CmdConfigProtocolBgpGlobalRouterId::printOutput(const RetType& output) { - std::cout << output << std::endl; -} - -// Explicit template instantiation -template void CmdHandler< - CmdConfigProtocolBgpGlobalRouterId, - CmdConfigProtocolBgpGlobalRouterIdTraits>::run(); - -} // namespace facebook::fboss diff --git a/fboss/cli/fboss2/commands/config/protocol/bgp/global/CmdConfigProtocolBgpGlobalRouterId.h b/fboss/cli/fboss2/commands/config/protocol/bgp/global/CmdConfigProtocolBgpGlobalRouterId.h deleted file mode 100644 index dd2f2871144c5..0000000000000 --- a/fboss/cli/fboss2/commands/config/protocol/bgp/global/CmdConfigProtocolBgpGlobalRouterId.h +++ /dev/null @@ -1,41 +0,0 @@ -/* - * 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/config/protocol/bgp/global/CmdConfigProtocolBgpGlobal.h" -#include "fboss/cli/fboss2/utils/CmdUtils.h" - -namespace facebook::fboss { - -struct CmdConfigProtocolBgpGlobalRouterIdTraits : public WriteCommandTraits { - using ParentCmd = CmdConfigProtocolBgpGlobal; - static void addCliArg(CLI::App& cmd, std::vector& args) { - cmd.add_option("ipAddrs", args, "IPv4 or IPv6 addr(s)"); - } - using ObjectArgType = utils::IPList; - using RetType = std::string; -}; - -class CmdConfigProtocolBgpGlobalRouterId - : public CmdHandler< - CmdConfigProtocolBgpGlobalRouterId, - CmdConfigProtocolBgpGlobalRouterIdTraits> { - public: - using ObjectArgType = CmdConfigProtocolBgpGlobalRouterIdTraits::ObjectArgType; - using RetType = CmdConfigProtocolBgpGlobalRouterIdTraits::RetType; - - RetType queryClient(const HostInfo& hostInfo, const ObjectArgType& routerId); - - void printOutput(const RetType& output); -}; - -} // namespace facebook::fboss diff --git a/fboss/cli/fboss2/commands/config/protocol/bgp/global/CmdConfigProtocolBgpGlobalSwitchLimitMaxGoldenVips.cpp b/fboss/cli/fboss2/commands/config/protocol/bgp/global/CmdConfigProtocolBgpGlobalSwitchLimitMaxGoldenVips.cpp deleted file mode 100644 index 6ef8c4fd536f5..0000000000000 --- a/fboss/cli/fboss2/commands/config/protocol/bgp/global/CmdConfigProtocolBgpGlobalSwitchLimitMaxGoldenVips.cpp +++ /dev/null @@ -1,51 +0,0 @@ -/* - * Copyright (c) 2004-present, Facebook, Inc. - * All rights reserved. - * - * This source code is licensed under the BSD-style license found in the - * LICENSE file in the root directory of this source tree. An additional grant - * of patent rights can be found in the PATENTS file in the same directory. - * - */ - -#include "fboss/cli/fboss2/commands/config/protocol/bgp/global/CmdConfigProtocolBgpGlobalSwitchLimitMaxGoldenVips.h" - -#include "fboss/cli/fboss2/CmdHandler.cpp" - -#include -#include "fboss/cli/fboss2/commands/config/protocol/bgp/BgpConfigSession.h" - -namespace facebook::fboss { - -CmdConfigProtocolBgpGlobalSwitchLimitMaxGoldenVipsTraits::RetType -CmdConfigProtocolBgpGlobalSwitchLimitMaxGoldenVips::queryClient( - const HostInfo& /* hostInfo */, - const ObjectArgType& limitStr) { - auto& session = BgpConfigSession::getInstance(); - - if (limitStr.data().empty()) { - return "Error: max-golden-vips value is required"; - } - - int64_t limit = std::stoll(limitStr.data()[0]); - - session.setSwitchLimitMaxGoldenVips(limit); - session.saveConfig(); - - return fmt::format( - "Successfully set switch_limit_config max_golden_vips to: {}\nConfig saved to: {}", - limit, - session.getSessionConfigPath()); -} - -void CmdConfigProtocolBgpGlobalSwitchLimitMaxGoldenVips::printOutput( - const RetType& output) { - std::cout << output << std::endl; -} - -// Explicit template instantiation -template void CmdHandler< - CmdConfigProtocolBgpGlobalSwitchLimitMaxGoldenVips, - CmdConfigProtocolBgpGlobalSwitchLimitMaxGoldenVipsTraits>::run(); - -} // namespace facebook::fboss diff --git a/fboss/cli/fboss2/commands/config/protocol/bgp/global/CmdConfigProtocolBgpGlobalSwitchLimitMaxGoldenVips.h b/fboss/cli/fboss2/commands/config/protocol/bgp/global/CmdConfigProtocolBgpGlobalSwitchLimitMaxGoldenVips.h deleted file mode 100644 index c28eab45fc3f9..0000000000000 --- a/fboss/cli/fboss2/commands/config/protocol/bgp/global/CmdConfigProtocolBgpGlobalSwitchLimitMaxGoldenVips.h +++ /dev/null @@ -1,44 +0,0 @@ -/* - * 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/config/protocol/bgp/global/CmdConfigProtocolBgpGlobal.h" -#include "fboss/cli/fboss2/utils/CmdUtils.h" - -namespace facebook::fboss { - -struct CmdConfigProtocolBgpGlobalSwitchLimitMaxGoldenVipsTraits - : public WriteCommandTraits { - using ParentCmd = CmdConfigProtocolBgpGlobal; - static void addCliArg(CLI::App& cmd, std::vector& args) { - cmd.add_option("msg", args, "Message"); - } - using ObjectArgType = utils::Message; - using RetType = std::string; -}; - -class CmdConfigProtocolBgpGlobalSwitchLimitMaxGoldenVips - : public CmdHandler< - CmdConfigProtocolBgpGlobalSwitchLimitMaxGoldenVips, - CmdConfigProtocolBgpGlobalSwitchLimitMaxGoldenVipsTraits> { - public: - using ObjectArgType = - CmdConfigProtocolBgpGlobalSwitchLimitMaxGoldenVipsTraits::ObjectArgType; - using RetType = - CmdConfigProtocolBgpGlobalSwitchLimitMaxGoldenVipsTraits::RetType; - - RetType queryClient(const HostInfo& hostInfo, const ObjectArgType& limit); - - void printOutput(const RetType& output); -}; - -} // namespace facebook::fboss diff --git a/fboss/cli/fboss2/commands/config/protocol/bgp/global/CmdConfigProtocolBgpGlobalSwitchLimitOverloadProtectionMode.cpp b/fboss/cli/fboss2/commands/config/protocol/bgp/global/CmdConfigProtocolBgpGlobalSwitchLimitOverloadProtectionMode.cpp deleted file mode 100644 index 78c634bf4bcd2..0000000000000 --- a/fboss/cli/fboss2/commands/config/protocol/bgp/global/CmdConfigProtocolBgpGlobalSwitchLimitOverloadProtectionMode.cpp +++ /dev/null @@ -1,51 +0,0 @@ -/* - * Copyright (c) 2004-present, Facebook, Inc. - * All rights reserved. - * - * This source code is licensed under the BSD-style license found in the - * LICENSE file in the root directory of this source tree. An additional grant - * of patent rights can be found in the PATENTS file in the same directory. - * - */ - -#include "fboss/cli/fboss2/commands/config/protocol/bgp/global/CmdConfigProtocolBgpGlobalSwitchLimitOverloadProtectionMode.h" - -#include "fboss/cli/fboss2/CmdHandler.cpp" - -#include -#include "fboss/cli/fboss2/commands/config/protocol/bgp/BgpConfigSession.h" - -namespace facebook::fboss { - -CmdConfigProtocolBgpGlobalSwitchLimitOverloadProtectionModeTraits::RetType -CmdConfigProtocolBgpGlobalSwitchLimitOverloadProtectionMode::queryClient( - const HostInfo& /* hostInfo */, - const ObjectArgType& modeStr) { - auto& session = BgpConfigSession::getInstance(); - - if (modeStr.data().empty()) { - return "Error: overload-protection-mode value is required"; - } - - int32_t mode = std::stoi(modeStr.data()[0]); - - session.setSwitchLimitOverloadProtectionMode(mode); - session.saveConfig(); - - return fmt::format( - "Successfully set switch_limit_config overload_protection_mode to: {}\nConfig saved to: {}", - mode, - session.getSessionConfigPath()); -} - -void CmdConfigProtocolBgpGlobalSwitchLimitOverloadProtectionMode::printOutput( - const RetType& output) { - std::cout << output << std::endl; -} - -// Explicit template instantiation -template void CmdHandler< - CmdConfigProtocolBgpGlobalSwitchLimitOverloadProtectionMode, - CmdConfigProtocolBgpGlobalSwitchLimitOverloadProtectionModeTraits>::run(); - -} // namespace facebook::fboss diff --git a/fboss/cli/fboss2/commands/config/protocol/bgp/global/CmdConfigProtocolBgpGlobalSwitchLimitOverloadProtectionMode.h b/fboss/cli/fboss2/commands/config/protocol/bgp/global/CmdConfigProtocolBgpGlobalSwitchLimitOverloadProtectionMode.h deleted file mode 100644 index 2a155cf1460a4..0000000000000 --- a/fboss/cli/fboss2/commands/config/protocol/bgp/global/CmdConfigProtocolBgpGlobalSwitchLimitOverloadProtectionMode.h +++ /dev/null @@ -1,46 +0,0 @@ -/* - * 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/config/protocol/bgp/global/CmdConfigProtocolBgpGlobal.h" -#include "fboss/cli/fboss2/utils/CmdUtils.h" - -namespace facebook::fboss { - -struct CmdConfigProtocolBgpGlobalSwitchLimitOverloadProtectionModeTraits - : public WriteCommandTraits { - using ParentCmd = CmdConfigProtocolBgpGlobal; - static void addCliArg(CLI::App& cmd, std::vector& args) { - cmd.add_option("msg", args, "Message"); - } - using ObjectArgType = utils::Message; - using RetType = std::string; -}; - -class CmdConfigProtocolBgpGlobalSwitchLimitOverloadProtectionMode - : public CmdHandler< - CmdConfigProtocolBgpGlobalSwitchLimitOverloadProtectionMode, - CmdConfigProtocolBgpGlobalSwitchLimitOverloadProtectionModeTraits> { - public: - using ObjectArgType = - CmdConfigProtocolBgpGlobalSwitchLimitOverloadProtectionModeTraits:: - ObjectArgType; - using RetType = - CmdConfigProtocolBgpGlobalSwitchLimitOverloadProtectionModeTraits:: - RetType; - - RetType queryClient(const HostInfo& hostInfo, const ObjectArgType& mode); - - void printOutput(const RetType& output); -}; - -} // namespace facebook::fboss diff --git a/fboss/cli/fboss2/commands/config/protocol/bgp/global/CmdConfigProtocolBgpGlobalSwitchLimitPrefixLimit.cpp b/fboss/cli/fboss2/commands/config/protocol/bgp/global/CmdConfigProtocolBgpGlobalSwitchLimitPrefixLimit.cpp deleted file mode 100644 index 0241e189f6f1c..0000000000000 --- a/fboss/cli/fboss2/commands/config/protocol/bgp/global/CmdConfigProtocolBgpGlobalSwitchLimitPrefixLimit.cpp +++ /dev/null @@ -1,54 +0,0 @@ -/* - * Copyright (c) 2004-present, Facebook, Inc. - * All rights reserved. - * - * This source code is licensed under the BSD-style license found in the - * LICENSE file in the root directory of this source tree. An additional grant - * of patent rights can be found in the PATENTS file in the same directory. - * - */ - -#include "fboss/cli/fboss2/commands/config/protocol/bgp/global/CmdConfigProtocolBgpGlobalSwitchLimitPrefixLimit.h" - -#include "fboss/cli/fboss2/CmdHandler.cpp" - -#include -#include "fboss/cli/fboss2/commands/config/protocol/bgp/BgpConfigSession.h" - -namespace facebook::fboss { - -CmdConfigProtocolBgpGlobalSwitchLimitPrefixLimitTraits::RetType -CmdConfigProtocolBgpGlobalSwitchLimitPrefixLimit::queryClient( - const HostInfo& /* hostInfo */, - const ObjectArgType& limitStr) { - auto& session = BgpConfigSession::getInstance(); - - // Command: "config protocol bgp global switch-limit " - // After "switch-limit", args contains just [""] - const auto& tokens = limitStr.data(); - if (tokens.empty()) { - return "Error: prefix-limit value is required. Usage: switch-limit "; - } - - int64_t limit = std::stoll(tokens[0]); - - session.setSwitchLimitPrefixLimit(limit); - session.saveConfig(); - - return fmt::format( - "Successfully set switch_limit_config prefix_limit to: {}\nConfig saved to: {}", - limit, - session.getSessionConfigPath()); -} - -void CmdConfigProtocolBgpGlobalSwitchLimitPrefixLimit::printOutput( - const RetType& output) { - std::cout << output << std::endl; -} - -// Explicit template instantiation -template void CmdHandler< - CmdConfigProtocolBgpGlobalSwitchLimitPrefixLimit, - CmdConfigProtocolBgpGlobalSwitchLimitPrefixLimitTraits>::run(); - -} // namespace facebook::fboss diff --git a/fboss/cli/fboss2/commands/config/protocol/bgp/global/CmdConfigProtocolBgpGlobalSwitchLimitPrefixLimit.h b/fboss/cli/fboss2/commands/config/protocol/bgp/global/CmdConfigProtocolBgpGlobalSwitchLimitPrefixLimit.h deleted file mode 100644 index d6496f037c749..0000000000000 --- a/fboss/cli/fboss2/commands/config/protocol/bgp/global/CmdConfigProtocolBgpGlobalSwitchLimitPrefixLimit.h +++ /dev/null @@ -1,44 +0,0 @@ -/* - * 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/config/protocol/bgp/global/CmdConfigProtocolBgpGlobal.h" -#include "fboss/cli/fboss2/utils/CmdUtils.h" - -namespace facebook::fboss { - -struct CmdConfigProtocolBgpGlobalSwitchLimitPrefixLimitTraits - : public WriteCommandTraits { - using ParentCmd = CmdConfigProtocolBgpGlobal; - static void addCliArg(CLI::App& cmd, std::vector& args) { - cmd.add_option("msg", args, "Message"); - } - using ObjectArgType = utils::Message; - using RetType = std::string; -}; - -class CmdConfigProtocolBgpGlobalSwitchLimitPrefixLimit - : public CmdHandler< - CmdConfigProtocolBgpGlobalSwitchLimitPrefixLimit, - CmdConfigProtocolBgpGlobalSwitchLimitPrefixLimitTraits> { - public: - using ObjectArgType = - CmdConfigProtocolBgpGlobalSwitchLimitPrefixLimitTraits::ObjectArgType; - using RetType = - CmdConfigProtocolBgpGlobalSwitchLimitPrefixLimitTraits::RetType; - - RetType queryClient(const HostInfo& hostInfo, const ObjectArgType& limit); - - void printOutput(const RetType& output); -}; - -} // namespace facebook::fboss diff --git a/fboss/cli/fboss2/commands/config/protocol/bgp/global/CmdConfigProtocolBgpGlobalSwitchLimitTotalPathLimit.cpp b/fboss/cli/fboss2/commands/config/protocol/bgp/global/CmdConfigProtocolBgpGlobalSwitchLimitTotalPathLimit.cpp deleted file mode 100644 index b0d67f57ec616..0000000000000 --- a/fboss/cli/fboss2/commands/config/protocol/bgp/global/CmdConfigProtocolBgpGlobalSwitchLimitTotalPathLimit.cpp +++ /dev/null @@ -1,51 +0,0 @@ -/* - * Copyright (c) 2004-present, Facebook, Inc. - * All rights reserved. - * - * This source code is licensed under the BSD-style license found in the - * LICENSE file in the root directory of this source tree. An additional grant - * of patent rights can be found in the PATENTS file in the same directory. - * - */ - -#include "fboss/cli/fboss2/commands/config/protocol/bgp/global/CmdConfigProtocolBgpGlobalSwitchLimitTotalPathLimit.h" - -#include "fboss/cli/fboss2/CmdHandler.cpp" - -#include -#include "fboss/cli/fboss2/commands/config/protocol/bgp/BgpConfigSession.h" - -namespace facebook::fboss { - -CmdConfigProtocolBgpGlobalSwitchLimitTotalPathLimitTraits::RetType -CmdConfigProtocolBgpGlobalSwitchLimitTotalPathLimit::queryClient( - const HostInfo& /* hostInfo */, - const ObjectArgType& limitStr) { - auto& session = BgpConfigSession::getInstance(); - - if (limitStr.data().empty()) { - return "Error: total-path-limit value is required"; - } - - int64_t limit = std::stoll(limitStr.data()[0]); - - session.setSwitchLimitTotalPathLimit(limit); - session.saveConfig(); - - return fmt::format( - "Successfully set switch_limit_config total_path_limit to: {}\nConfig saved to: {}", - limit, - session.getSessionConfigPath()); -} - -void CmdConfigProtocolBgpGlobalSwitchLimitTotalPathLimit::printOutput( - const RetType& output) { - std::cout << output << std::endl; -} - -// Explicit template instantiation -template void CmdHandler< - CmdConfigProtocolBgpGlobalSwitchLimitTotalPathLimit, - CmdConfigProtocolBgpGlobalSwitchLimitTotalPathLimitTraits>::run(); - -} // namespace facebook::fboss diff --git a/fboss/cli/fboss2/commands/config/protocol/bgp/global/CmdConfigProtocolBgpGlobalSwitchLimitTotalPathLimit.h b/fboss/cli/fboss2/commands/config/protocol/bgp/global/CmdConfigProtocolBgpGlobalSwitchLimitTotalPathLimit.h deleted file mode 100644 index 23e702fa743d7..0000000000000 --- a/fboss/cli/fboss2/commands/config/protocol/bgp/global/CmdConfigProtocolBgpGlobalSwitchLimitTotalPathLimit.h +++ /dev/null @@ -1,44 +0,0 @@ -/* - * 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/config/protocol/bgp/global/CmdConfigProtocolBgpGlobal.h" -#include "fboss/cli/fboss2/utils/CmdUtils.h" - -namespace facebook::fboss { - -struct CmdConfigProtocolBgpGlobalSwitchLimitTotalPathLimitTraits - : public WriteCommandTraits { - using ParentCmd = CmdConfigProtocolBgpGlobal; - static void addCliArg(CLI::App& cmd, std::vector& args) { - cmd.add_option("msg", args, "Message"); - } - using ObjectArgType = utils::Message; - using RetType = std::string; -}; - -class CmdConfigProtocolBgpGlobalSwitchLimitTotalPathLimit - : public CmdHandler< - CmdConfigProtocolBgpGlobalSwitchLimitTotalPathLimit, - CmdConfigProtocolBgpGlobalSwitchLimitTotalPathLimitTraits> { - public: - using ObjectArgType = - CmdConfigProtocolBgpGlobalSwitchLimitTotalPathLimitTraits::ObjectArgType; - using RetType = - CmdConfigProtocolBgpGlobalSwitchLimitTotalPathLimitTraits::RetType; - - RetType queryClient(const HostInfo& hostInfo, const ObjectArgType& limit); - - void printOutput(const RetType& output); -}; - -} // namespace facebook::fboss diff --git a/fboss/cli/fboss2/commands/config/protocol/bgp/neighbor/CmdConfigProtocolBgpNeighbor.cpp b/fboss/cli/fboss2/commands/config/protocol/bgp/neighbor/CmdConfigProtocolBgpNeighbor.cpp new file mode 100644 index 0000000000000..ffaf55c396533 --- /dev/null +++ b/fboss/cli/fboss2/commands/config/protocol/bgp/neighbor/CmdConfigProtocolBgpNeighbor.cpp @@ -0,0 +1,745 @@ +/* + * Copyright (c) 2004-present, Facebook, Inc. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + * + */ + +#include "fboss/cli/fboss2/commands/config/protocol/bgp/neighbor/CmdConfigProtocolBgpNeighbor.h" + +#include "fboss/cli/fboss2/CmdHandler.cpp" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "configerator/structs/neteng/fboss/bgp/if/gen-cpp2/bgp_attr_types.h" +#include "fboss/cli/fboss2/commands/config/protocol/bgp/BgpCliValueParsers.h" +#include "fboss/cli/fboss2/commands/config/protocol/bgp/BgpPeerAddr.h" +#include "fboss/cli/fboss2/session/ConfigSession.h" +#include "fboss/cli/fboss2/utils/CmdUtilsCommon.h" +#include "fboss/cli/fboss2/utils/HostInfo.h" +#include "fmt/format.h" +#include "thrift/lib/cpp/Thrift.h" + +namespace facebook::fboss { + +namespace { + +// CLI attribute names, exactly as documented (two-token attributes keep their +// space). Kept here (not as raw string literals at the dispatch sites) so the +// valid-attribute set and the handler table stay in sync. +constexpr std::string_view kRemoteAsn = "remote-asn"; +constexpr std::string_view kLocalAsn = "local-asn"; +constexpr std::string_view kDescription = "description"; +constexpr std::string_view kPeerTag = "peer-tag"; +constexpr std::string_view kPeerGroup = "peer-group"; +constexpr std::string_view kIngressPolicy = "ingress-policy"; +constexpr std::string_view kEgressPolicy = "egress-policy"; +constexpr std::string_view kRrClient = "rr-client"; +constexpr std::string_view kConfedPeer = "confed-peer"; +constexpr std::string_view kRedistributePeer = "redistribute-peer"; +constexpr std::string_view kEnhancedRouteRefresh = "enhanced-route-refresh"; +constexpr std::string_view kConnectMode = "connect-mode"; +constexpr std::string_view kPeerPort = "peer-port"; +constexpr std::string_view kAddPathSend = "add-path send"; +constexpr std::string_view kAddPathReceive = "add-path receive"; +constexpr std::string_view kAfiDisableIpv4Afi = "afi disable-ipv4-afi"; +constexpr std::string_view kAfiDisableIpv6Afi = "afi disable-ipv6-afi"; +constexpr std::string_view kAfiIpv4OverIpv6Nh = "afi ipv4-over-ipv6-nh"; +constexpr std::string_view kAfiIpv4LabeledUnicast = "afi ipv4-labeled-unicast"; +constexpr std::string_view kAfiIpv6LabeledUnicast = "afi ipv6-labeled-unicast"; +constexpr std::string_view kBindAddrAddress = "bind-addr address"; +constexpr std::string_view kBindAddrPort = "bind-addr port"; +constexpr std::string_view kGracefulRestartTime = + "graceful-restart restart-time"; +constexpr std::string_view kGracefulRestartStatefulHa = + "graceful-restart stateful-ha"; +constexpr std::string_view kMaxRoutePreFilter = "max-route pre-filter"; +constexpr std::string_view kMaxRoutePostFilter = "max-route post-filter"; +constexpr std::string_view kMaxRoutePostWarningThreshold = + "max-route post-warning-threshold"; +constexpr std::string_view kTimersHoldTime = "timers hold-time"; +constexpr std::string_view kTimersKeepalive = "timers keepalive"; +constexpr std::string_view kTimersOutDelay = "timers out-delay"; +// Parity attributes: not in the documented neighbor grammar, but supported by +// the per-attribute peer commands this dispatcher replaces (and by real +// BgpPeer thrift fields), so dropping them would regress existing usage. +constexpr std::string_view kNextHop4 = "next-hop4"; +constexpr std::string_view kNextHop6 = "next-hop6"; +constexpr std::string_view kNextHopSelf = "next-hop-self"; +constexpr std::string_view kPeerId = "peer-id"; +constexpr std::string_view kPeerType = "type"; +constexpr std::string_view kLinkBandwidth = "link-bandwidth"; +constexpr std::string_view kAdvertiseLbw = "advertise-lbw"; +constexpr std::string_view kReceiveLbw = "receive-lbw"; +constexpr std::string_view kTimersWithdrawUnprogDelay = + "timers withdraw-unprog-delay"; +constexpr std::string_view kMaxRoutePreWarningThreshold = + "max-route pre-warning-threshold"; +constexpr std::string_view kMaxRoutePreWarningOnly = + "max-route pre-warning-only"; +constexpr std::string_view kMaxRoutePostWarningOnly = + "max-route post-warning-only"; + +// connect-mode values +constexpr std::string_view kConnectModePassive = "PASSIVE"; +constexpr std::string_view kConnectModeActive = "ACTIVE"; +constexpr std::string_view kConnectModeBoth = "BOTH"; + +using Tokens = std::vector; +using BgpPeer = bgp::thrift::BgpPeer; +using bgpcli::err; +using bgpcli::ok; +using bgpcli::parseBool; +using bgpcli::parseInt; +using bgpcli::parseNonNegInt32; +using bgpcli::parseNonNegInt64; +using bgpcli::Result; +using facebook::neteng::fboss::bgp_attr::AddPath; +using facebook::neteng::fboss::bgp_attr::AdvertiseLinkBandwidth; +using facebook::neteng::fboss::bgp_attr::ReceiveLinkBandwidth; + +// ---- per-attribute handlers ------------------------------------------------ +// Each handler mutates the typed bgp::thrift::BgpPeer directly. Field names +// map 1:1 to bgp_config.thrift, so the compiler validates them. Handlers are +// built from the factories below; only attributes with non-trivial parsing +// (connect-mode, add-path) have bespoke functions. + +using AttrHandler = std::function; + +AttrHandler boolAttr( + std::string_view name, + std::function set) { + return [name, set = std::move(set)]( + BgpPeer& peer, const Tokens& values) -> Result { + if (values.size() != 1) { + return err(fmt::format("Error: {} requires ", name)); + } + auto enable = parseBool(values[0]); + if (!enable) { + return err( + fmt::format( + "Error: Invalid {} value '{}'; expected true or false", + name, + values[0])); + } + set(peer, *enable); + return ok( + fmt::format( + "Successfully {} {}", *enable ? "enabled" : "disabled", name)); + }; +} + +// A single-token string value (policy names, tags, group names). +AttrHandler stringAttr( + std::string_view name, + std::string_view valueName, + std::function set) { + return [name, valueName, set = std::move(set)]( + BgpPeer& peer, const Tokens& values) -> Result { + if (values.size() != 1) { + return err(fmt::format("Error: {} requires <{}>", name, valueName)); + } + set(peer, values[0]); + return ok(fmt::format("Successfully set {} to: {}", name, values[0])); + }; +} + +// A 4-byte ASN (RFC 6793): unsigned, bounded to [0, 2^32-1]. +AttrHandler asnAttr( + std::string_view name, + std::function set) { + return [name, set = std::move(set)]( + BgpPeer& peer, const Tokens& values) -> Result { + if (values.size() != 1) { + return err(fmt::format("Error: {} requires ", name)); + } + auto asn = bgpcli::parseAsn4Byte(values[0]); + if (!asn) { + return err( + fmt::format( + "Error: Invalid {} value '{}'; expected an unsigned 4-byte ASN", + name, + values[0])); + } + set(peer, *asn); + return ok(fmt::format("Successfully set {} to: {}", name, *asn)); + }; +} + +// A validated IP address value, optionally restricted to one family, stored +// normalized. +AttrHandler ipAttr( + std::string_view name, + std::function set, + std::optional requireV6 = std::nullopt) { + return [name, set = std::move(set), requireV6]( + BgpPeer& peer, const Tokens& values) -> Result { + if (values.size() != 1) { + return err(fmt::format("Error: {} requires ", name)); + } + auto addr = folly::IPAddress::tryFromString(values[0]); + if (!addr.hasValue()) { + return err( + fmt::format("Error: Invalid {} address '{}'", name, values[0])); + } + if (requireV6.has_value() && addr->isV6() != *requireV6) { + return err( + fmt::format( + "Error: {} requires an IPv{} address, got '{}'", + name, + *requireV6 ? 6 : 4, + values[0])); + } + set(peer, addr->str()); + return ok(fmt::format("Successfully set {} to: {}", name, addr->str())); + }; +} + +// A thrift enum attribute. Accepts the enum value name (e.g. BEST_PATH), the +// integer value, or true/false as aliases for 1/0 (the replaced per-attribute +// peer commands took booleans, so existing usage keeps working); membership is +// validated so an unknown mode is rejected rather than persisted. +template +AttrHandler enumAttr( + std::string_view name, + std::function set) { + return [name, set = std::move(set)]( + BgpPeer& peer, const Tokens& values) -> Result { + if (values.size() != 1) { + return err(fmt::format("Error: {} requires ", name)); + } + EnumT mode; + bool valid = false; + if (auto parsed = parseInt(values[0])) { + mode = static_cast(*parsed); + valid = apache::thrift::TEnumTraits::findName(mode) != nullptr; + } else if (auto enable = parseBool(values[0])) { + mode = static_cast(*enable ? 1 : 0); + valid = true; + } else { + valid = apache::thrift::TEnumTraits::findValue(values[0], &mode); + } + if (!valid) { + std::string names; + for (auto n : apache::thrift::TEnumTraits::names) { + names += names.empty() ? "" : ", "; + names += std::string(n); + } + return err( + fmt::format( + "Error: {} value '{}' is not a valid mode; expected one of: {}", + name, + values[0], + names)); + } + set(peer, mode); + return ok( + fmt::format( + "Successfully set {} to: {}", name, static_cast(mode))); + }; +} + +// A non-negative int32 seconds value (timers). +AttrHandler secondsAttr( + std::string_view name, + std::function set) { + return [name, set = std::move(set)]( + BgpPeer& peer, const Tokens& values) -> Result { + if (values.size() != 1) { + return err(fmt::format("Error: {} requires ", name)); + } + auto seconds = parseNonNegInt32(values[0]); + if (!seconds) { + return err( + fmt::format( + "Error: {} must be a non-negative integer that fits in int32, " + "got '{}'", + name, + values[0])); + } + set(peer, *seconds); + return ok( + fmt::format("Successfully set {} to: {} seconds", name, *seconds)); + }; +} + +// A non-negative int64 route-count value (RouteLimit fields). +AttrHandler routeCountAttr( + std::string_view name, + std::function set) { + return [name, set = std::move(set)]( + BgpPeer& peer, const Tokens& values) -> Result { + if (values.size() != 1) { + return err(fmt::format("Error: {} requires ", name)); + } + auto limit = parseNonNegInt64(values[0]); + if (!limit) { + return err( + fmt::format( + "Error: {} must be a non-negative integer, got '{}'", + name, + values[0])); + } + set(peer, *limit); + return ok(fmt::format("Successfully set {} to: {}", name, *limit)); + }; +} + +// An attribute the CLI must refuse: either bgp_config.thrift has no field for +// it (persisting it would stage dead config the daemon ignores) or the knob is +// not per-neighbor. The reason is surfaced to the user instead. +AttrHandler rejectedAttr(std::string_view name, std::string_view reason) { + return [name, reason]( + BgpPeer& /* peer */, const Tokens& /* values */) -> Result { + return err(fmt::format("Error: {} is not supported: {}", name, reason)); + }; +} + +Result applyDescription(BgpPeer& peer, const Tokens& values) { + if (values.empty()) { + return err(fmt::format("Error: {} requires ", kDescription)); + } + // The description may span multiple CLI tokens; re-join them. + std::string description = values[0]; + for (size_t i = 1; i < values.size(); i++) { + description += " " + values[i]; + } + peer.description() = description; + return ok( + fmt::format("Successfully set {} to: {}", kDescription, description)); +} + +Result applyConnectMode(BgpPeer& peer, const Tokens& values) { + // The thrift model only has is_passive (listen vs actively connect); there + // is no representation for BOTH, so it is rejected rather than silently + // mapped. + if (values.size() != 1) { + return err( + fmt::format( + "Error: {} requires <{}|{}>", + kConnectMode, + kConnectModePassive, + kConnectModeActive)); + } + if (values[0] == kConnectModePassive) { + peer.is_passive() = true; + } else if (values[0] == kConnectModeActive) { + peer.is_passive() = false; + } else if (values[0] == kConnectModeBoth) { + return err( + fmt::format( + "Error: {} {} has no representation in bgp_config.thrift " + "(BgpPeer only models is_passive); use {} or {}", + kConnectMode, + kConnectModeBoth, + kConnectModePassive, + kConnectModeActive)); + } else { + return err( + fmt::format( + "Error: Invalid {} value '{}'; expected {} or {}", + kConnectMode, + values[0], + kConnectModePassive, + kConnectModeActive)); + } + return ok(fmt::format("Successfully set {} to: {}", kConnectMode, values[0])); +} + +// add_path is a single enum whose values form a bitmask by design: +// RECEIVE=1, SEND=2, BOTH=3(=RECEIVE|SEND). Merge the requested direction +// into the current value; clearing the last direction unsets the field. +Result applyAddPath(BgpPeer& peer, bool send, const Tokens& values) { + const std::string_view name = send ? kAddPathSend : kAddPathReceive; + if (values.size() != 1) { + return err(fmt::format("Error: {} requires ", name)); + } + auto enable = parseBool(values[0]); + if (!enable) { + return err( + fmt::format( + "Error: Invalid {} value '{}'; expected true or false", + name, + values[0])); + } + int bits = peer.add_path() ? static_cast(*peer.add_path()) : 0; + const int bit = send ? static_cast(AddPath::SEND) + : static_cast(AddPath::RECEIVE); + bits = *enable ? (bits | bit) : (bits & ~bit); + if (bits == 0) { + peer.add_path().reset(); + } else { + peer.add_path() = static_cast(bits); + } + return ok( + fmt::format( + "Successfully {} {}", *enable ? "enabled" : "disabled", name)); +} + +const std::map>& attrHandlers() { + static const std::map> kHandlers = { + {std::string(kRemoteAsn), + asnAttr( + kRemoteAsn, + [](BgpPeer& p, int64_t v) { p.remote_as_4_byte() = v; })}, + {std::string(kLocalAsn), + asnAttr( + kLocalAsn, [](BgpPeer& p, int64_t v) { p.local_as_4_byte() = v; })}, + {std::string(kDescription), applyDescription}, + {std::string(kPeerTag), + stringAttr( + kPeerTag, + "string", + [](BgpPeer& p, const std::string& v) { p.peer_tag() = v; })}, + {std::string(kPeerGroup), + stringAttr( + kPeerGroup, + "name", + [](BgpPeer& p, const std::string& v) { p.peer_group_name() = v; })}, + {std::string(kIngressPolicy), + stringAttr( + kIngressPolicy, + "policy-name", + [](BgpPeer& p, const std::string& v) { + p.ingress_policy_name() = v; + })}, + {std::string(kEgressPolicy), + stringAttr( + kEgressPolicy, + "policy-name", + [](BgpPeer& p, const std::string& v) { + p.egress_policy_name() = v; + })}, + {std::string(kRrClient), + boolAttr(kRrClient, [](BgpPeer& p, bool v) { p.is_rr_client() = v; })}, + {std::string(kConfedPeer), + boolAttr( + kConfedPeer, [](BgpPeer& p, bool v) { p.is_confed_peer() = v; })}, + {std::string(kRedistributePeer), + boolAttr( + kRedistributePeer, + [](BgpPeer& p, bool v) { p.is_redistribute_peer() = v; })}, + {std::string(kEnhancedRouteRefresh), + boolAttr( + kEnhancedRouteRefresh, + [](BgpPeer& p, bool v) { p.enhanced_route_refresh() = v; })}, + {std::string(kConnectMode), applyConnectMode}, + {std::string(kPeerPort), + rejectedAttr( + kPeerPort, + "BgpPeer has no per-neighbor port field; the daemon listens on " + "the global listen_port")}, + {std::string(kAddPathSend), + [](BgpPeer& p, const Tokens& v) { return applyAddPath(p, true, v); }}, + {std::string(kAddPathReceive), + [](BgpPeer& p, const Tokens& v) { return applyAddPath(p, false, v); }}, + {std::string(kAfiDisableIpv4Afi), + boolAttr( + kAfiDisableIpv4Afi, + [](BgpPeer& p, bool v) { p.disable_ipv4_afi() = v; })}, + {std::string(kAfiDisableIpv6Afi), + boolAttr( + kAfiDisableIpv6Afi, + [](BgpPeer& p, bool v) { p.disable_ipv6_afi() = v; })}, + {std::string(kAfiIpv4OverIpv6Nh), + boolAttr( + kAfiIpv4OverIpv6Nh, + [](BgpPeer& p, bool v) { p.v4_over_v6_nexthop() = v; })}, + {std::string(kAfiIpv4LabeledUnicast), + rejectedAttr( + kAfiIpv4LabeledUnicast, + "bgp_config.thrift has no labeled-unicast field on BgpPeer")}, + {std::string(kAfiIpv6LabeledUnicast), + rejectedAttr( + kAfiIpv6LabeledUnicast, + "bgp_config.thrift has no labeled-unicast field on BgpPeer")}, + {std::string(kBindAddrAddress), + ipAttr( + kBindAddrAddress, + [](BgpPeer& p, const std::string& v) { p.local_addr() = v; })}, + {std::string(kBindAddrPort), + rejectedAttr( + kBindAddrPort, + "BgpPeer has no per-neighbor port field; the daemon listens on " + "the global listen_port")}, + {std::string(kGracefulRestartTime), + secondsAttr( + kGracefulRestartTime, + [](BgpPeer& p, int32_t v) { + p.bgp_peer_timers().ensure().graceful_restart_seconds() = v; + })}, + {std::string(kGracefulRestartStatefulHa), + boolAttr( + kGracefulRestartStatefulHa, + [](BgpPeer& p, bool v) { p.enable_stateful_ha() = v; })}, + {std::string(kMaxRoutePreFilter), + routeCountAttr( + kMaxRoutePreFilter, + [](BgpPeer& p, int64_t v) { + p.pre_filter().ensure().max_routes() = v; + })}, + {std::string(kMaxRoutePostFilter), + routeCountAttr( + kMaxRoutePostFilter, + [](BgpPeer& p, int64_t v) { + p.post_filter().ensure().max_routes() = v; + })}, + // RouteLimit.warning_limit is an absolute route count in the thrift + // schema, not a percentage. + {std::string(kMaxRoutePostWarningThreshold), + routeCountAttr( + kMaxRoutePostWarningThreshold, + [](BgpPeer& p, int64_t v) { + p.post_filter().ensure().warning_limit() = v; + })}, + {std::string(kTimersHoldTime), + secondsAttr( + kTimersHoldTime, + [](BgpPeer& p, int32_t v) { + p.bgp_peer_timers().ensure().hold_time_seconds() = v; + })}, + {std::string(kTimersKeepalive), + secondsAttr( + kTimersKeepalive, + [](BgpPeer& p, int32_t v) { + p.bgp_peer_timers().ensure().keep_alive_seconds() = v; + })}, + {std::string(kTimersOutDelay), + secondsAttr( + kTimersOutDelay, + [](BgpPeer& p, int32_t v) { + p.bgp_peer_timers().ensure().out_delay_seconds() = v; + })}, + {std::string(kTimersWithdrawUnprogDelay), + secondsAttr( + kTimersWithdrawUnprogDelay, + [](BgpPeer& p, int32_t v) { + p.bgp_peer_timers().ensure().withdraw_unprog_delay_seconds() = v; + })}, + {std::string(kNextHop4), + ipAttr( + kNextHop4, + [](BgpPeer& p, const std::string& v) { p.next_hop4() = v; }, + /* requireV6 */ false)}, + {std::string(kNextHop6), + ipAttr( + kNextHop6, + [](BgpPeer& p, const std::string& v) { p.next_hop6() = v; }, + /* requireV6 */ true)}, + {std::string(kNextHopSelf), + boolAttr( + kNextHopSelf, [](BgpPeer& p, bool v) { p.next_hop_self() = v; })}, + {std::string(kPeerId), + stringAttr( + kPeerId, + "string", + [](BgpPeer& p, const std::string& v) { p.peer_id() = v; })}, + {std::string(kPeerType), + stringAttr( + kPeerType, + "string", + [](BgpPeer& p, const std::string& v) { p.type() = v; })}, + // Bits (not bytes) per second, with optional K/M/G suffix (e.g. "100G"), + // validated so garbage never reaches bgpd's config parser. + {std::string(kLinkBandwidth), + [](BgpPeer& p, const Tokens& values) -> Result { + if (values.size() != 1) { + return err( + fmt::format( + "Error: {} requires [K|M|G]", + kLinkBandwidth)); + } + const std::string& v = values[0]; + const size_t digits = + v.find_first_not_of("0123456789") == std::string::npos + ? v.size() + : v.find_first_not_of("0123456789"); + const bool valid = digits > 0 && + (digits == v.size() || + (digits == v.size() - 1 && + (v.back() == 'K' || v.back() == 'M' || v.back() == 'G'))); + if (!valid) { + return err( + fmt::format( + "Error: Invalid {} value '{}'; expected digits with an " + "optional K/M/G suffix (e.g. 100G)", + kLinkBandwidth, + v)); + } + p.link_bandwidth_bps() = v; + return ok( + fmt::format("Successfully set {} to: {}", kLinkBandwidth, v)); + }}, + {std::string(kAdvertiseLbw), + enumAttr( + kAdvertiseLbw, + [](BgpPeer& p, AdvertiseLinkBandwidth v) { + p.advertise_link_bandwidth() = v; + })}, + {std::string(kReceiveLbw), + enumAttr( + kReceiveLbw, + [](BgpPeer& p, ReceiveLinkBandwidth v) { + p.receive_link_bandwidth() = v; + })}, + {std::string(kMaxRoutePreWarningThreshold), + routeCountAttr( + kMaxRoutePreWarningThreshold, + [](BgpPeer& p, int64_t v) { + p.pre_filter().ensure().warning_limit() = v; + })}, + {std::string(kMaxRoutePreWarningOnly), + boolAttr( + kMaxRoutePreWarningOnly, + [](BgpPeer& p, bool v) { + p.pre_filter().ensure().warning_only() = v; + })}, + {std::string(kMaxRoutePostWarningOnly), + boolAttr( + kMaxRoutePostWarningOnly, + [](BgpPeer& p, bool v) { + p.post_filter().ensure().warning_only() = v; + })}, + }; + return kHandlers; +} + +std::string validAttrList() { + std::string out; + for (const auto& [name, _] : attrHandlers()) { + if (!out.empty()) { + out += ", "; + } + out += name; + } + return out; +} + +// Find the peer keyed by (normalized) peer_addr, creating it if absent. +// Setting an attribute on a not-yet-created neighbor implicitly creates it, +// so command ordering stays forgiving; a bare `neighbor ` creates one +// explicitly. +bgp::thrift::BgpPeer& findOrCreatePeer( + bgp::thrift::BgpConfig& cfg, + const std::string& peerAddr) { + auto& peers = *cfg.peers(); + if (auto it = bgpcli::findBgpPeer(cfg, peerAddr); it != peers.end()) { + return *it; + } + peers.emplace_back(); + auto& peer = peers.back(); + peer.peer_addr() = peerAddr; + // local_addr / next_hop4 / next_hop6 are non-optional thrift fields that + // bgpd parses with folly::IPAddress at config load — an empty string makes + // the daemon abort (IPAddressFormatException) before serving any config. + // Seed them with the unspecified address of the right family; the user + // overrides them via `bind-addr address` / `next-hop4` / `next-hop6`. + const bool isV6 = peerAddr.find(':') != std::string::npos; + peer.local_addr() = isV6 ? "::" : "0.0.0.0"; + peer.next_hop4() = "0.0.0.0"; + peer.next_hop6() = "::"; + return peer; +} + +} // namespace + +// Parse + validate at construction so queryClient stays a thin dispatch. +// Throwing std::invalid_argument is how the framework surfaces arg parse +// errors (same mechanism as BgpGlobalConfig). +BgpNeighborConfig::BgpNeighborConfig(std::vector v) + : utils::BaseObjectArgType(v) { + if (v.empty()) { + throw std::invalid_argument( + "Error: neighbor is required, followed by an optional " + " "); + } + auto normalized = bgpcli::normalizeBgpPeerAddr(v[0]); + if (!normalized) { + throw std::invalid_argument( + fmt::format("Error: Invalid neighbor address '{}'", v[0])); + } + peerAddr_ = std::move(*normalized); + + if (v.size() == 1) { + return; // bare `neighbor `: create the peer + } + + // Attributes may span two tokens (e.g. `timers hold-time`); match the + // longest prefix of the remaining tokens against the dispatch table. + const auto& handlers = attrHandlers(); + if (v.size() >= 3) { + std::string twoToken = v[1] + " " + v[2]; + if (handlers.find(twoToken) != handlers.end()) { + attr_ = std::move(twoToken); + values_.assign(v.begin() + 3, v.end()); + return; + } + } + if (handlers.find(v[1]) != handlers.end()) { + attr_ = v[1]; + values_.assign(v.begin() + 2, v.end()); + return; + } + throw std::invalid_argument( + fmt::format( + "Error: unknown neighbor attribute '{}'. Valid attributes: {}", + v[1], + validAttrList())); +} + +CmdConfigProtocolBgpNeighborTraits::RetType +CmdConfigProtocolBgpNeighbor::queryClient( + const HostInfo& /* hostInfo */, + const ObjectArgType& args) { + auto& session = ConfigSession::getInstance(); + auto& cfg = session.getBgpConfig(); + const bool created = + bgpcli::findBgpPeer(cfg, args.peerAddr()) == cfg.peers()->end(); + auto& peer = findOrCreatePeer(cfg, args.peerAddr()); + + Result result = args.attr().empty() + ? ok(fmt::format("Successfully created BGP neighbor {}", args.peerAddr())) + // The attribute is guaranteed valid: BgpNeighborConfig's constructor + // rejects an unknown attribute before we get here. + : attrHandlers().find(args.attr())->second(peer, args.values()); + if (result.ok) { + if (!args.attr().empty()) { + result.message += fmt::format(" for neighbor {}", args.peerAddr()); + } + session.saveBgpConfig(); + result.message += + fmt::format("\nConfig saved to: {}", session.getBgpSessionConfigPath()); + } else if (created) { + // A rejected value must not leave a half-created peer in the in-memory + // config (visible to later lookups in the same process, e.g. tests). + cfg.peers()->pop_back(); + } + return result.message; +} + +void CmdConfigProtocolBgpNeighbor::printOutput(const RetType& output) { + std::cout << output << std::endl; +} + +// Explicit template instantiation +template void CmdHandler< + CmdConfigProtocolBgpNeighbor, + CmdConfigProtocolBgpNeighborTraits>::run(); + +} // namespace facebook::fboss diff --git a/fboss/cli/fboss2/commands/config/protocol/bgp/neighbor/CmdConfigProtocolBgpNeighbor.h b/fboss/cli/fboss2/commands/config/protocol/bgp/neighbor/CmdConfigProtocolBgpNeighbor.h new file mode 100644 index 0000000000000..7b15488692b42 --- /dev/null +++ b/fboss/cli/fboss2/commands/config/protocol/bgp/neighbor/CmdConfigProtocolBgpNeighbor.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 +#include +#include "CLI/App.hpp" +#include "fboss/cli/fboss2/CmdHandler.h" +#include "fboss/cli/fboss2/commands/config/protocol/bgp/CmdConfigProtocolBgp.h" +#include "fboss/cli/fboss2/utils/CmdUtilsCommon.h" +#include "fboss/cli/fboss2/utils/HostInfo.h" + +namespace facebook::fboss { + +// Parsed `config protocol bgp neighbor [ ...]`, +// validated at construction. The neighbor address is normalized via +// folly::IPAddress so different spellings of the same address (e.g. +// 2001:DB8::1 vs 2001:db8::1) key the same peer. Attributes may span two +// tokens (e.g. `timers hold-time`); the dispatch table is matched +// longest-prefix-first. A bare `neighbor ` (no attribute) creates the +// peer. +class BgpNeighborConfig : public utils::BaseObjectArgType { + public: + // NOLINTNEXTLINE(google-explicit-constructor) + /* implicit */ BgpNeighborConfig(std::vector v); + const std::string& peerAddr() const { + return peerAddr_; + } + const std::string& attr() const { + return attr_; + } + const std::vector& values() const { + return values_; + } + const static utils::ObjectArgTypeId id = + utils::ObjectArgTypeId::OBJECT_ARG_TYPE_ID_MESSAGE; + + private: + std::string peerAddr_; + std::string attr_; // matched dispatch key ("" = bare create) + std::vector values_; +}; + +// Single handler for the whole `config protocol bgp neighbor` family. The +// first positional token is the neighbor address, the next one or two tokens +// name the attribute, and the remaining tokens are its value(s); dispatch and +// per-attribute parsing live in the .cpp so adding a new neighbor tunable is +// a one-entry change rather than a new command class. Mirrors +// CmdConfigProtocolBgpGlobal. +struct CmdConfigProtocolBgpNeighborTraits : public WriteCommandTraits { + using ParentCmd = CmdConfigProtocolBgp; + static void addCliArg(CLI::App& cmd, std::vector& args) { + // positionals_at_end: once the first positional (the neighbor address, + // which never collides with a command name) is consumed, CLI11 stops + // classifying later tokens as subcommands. Without this, an attribute + // token that matches a sibling command name (e.g. `peer-group`) is + // stolen by CLI11's parent-chain subcommand fallthrough and the + // remaining tokens are misparsed. + cmd.positionals_at_end(); + cmd.add_option("args", args, " [ ...]"); + } + using ObjectArgType = BgpNeighborConfig; + using RetType = std::string; +}; + +class CmdConfigProtocolBgpNeighbor : public CmdHandler< + CmdConfigProtocolBgpNeighbor, + CmdConfigProtocolBgpNeighborTraits> { + public: + using ObjectArgType = CmdConfigProtocolBgpNeighborTraits::ObjectArgType; + using RetType = CmdConfigProtocolBgpNeighborTraits::RetType; + + RetType queryClient(const HostInfo& hostInfo, const ObjectArgType& args); + + void printOutput(const RetType& output); +}; + +} // namespace facebook::fboss diff --git a/fboss/cli/fboss2/commands/config/protocol/bgp/peer-group/CmdConfigProtocolBgpPeerGroup.cpp b/fboss/cli/fboss2/commands/config/protocol/bgp/peer-group/CmdConfigProtocolBgpPeerGroup.cpp index 9fdb301293a89..841005cc587a7 100644 --- a/fboss/cli/fboss2/commands/config/protocol/bgp/peer-group/CmdConfigProtocolBgpPeerGroup.cpp +++ b/fboss/cli/fboss2/commands/config/protocol/bgp/peer-group/CmdConfigProtocolBgpPeerGroup.cpp @@ -13,30 +13,543 @@ #include "fboss/cli/fboss2/CmdHandler.cpp" #include -#include "fboss/cli/fboss2/commands/config/protocol/bgp/BgpConfigSession.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "configerator/structs/neteng/fboss/bgp/if/gen-cpp2/bgp_attr_types.h" +#include "fboss/cli/fboss2/commands/config/protocol/bgp/BgpCliValueParsers.h" +#include "fboss/cli/fboss2/session/ConfigSession.h" +#include "fboss/cli/fboss2/utils/CmdUtilsCommon.h" +#include "fboss/cli/fboss2/utils/HostInfo.h" +#include "fmt/format.h" namespace facebook::fboss { +namespace { + +// CLI attribute names, exactly as documented (two-token attributes keep their +// space). Kept here (not as raw string literals at the dispatch sites) so the +// valid-attribute set and the handler table stay in sync. +constexpr std::string_view kRemoteAsn = "remote-asn"; +constexpr std::string_view kLocalAsn = "local-asn"; +constexpr std::string_view kDescription = "description"; +constexpr std::string_view kPeerTag = "peer-tag"; +constexpr std::string_view kIngressPolicy = "ingress-policy"; +constexpr std::string_view kEgressPolicy = "egress-policy"; +constexpr std::string_view kRrClient = "rr-client"; +constexpr std::string_view kRedistributePeer = "redistribute-peer"; +constexpr std::string_view kEnhancedRouteRefresh = "enhanced-route-refresh"; +constexpr std::string_view kConnectMode = "connect-mode"; +constexpr std::string_view kAddPathSend = "add-path send"; +constexpr std::string_view kAddPathReceive = "add-path receive"; +constexpr std::string_view kAfiDisableIpv4Afi = "afi disable-ipv4-afi"; +constexpr std::string_view kAfiDisableIpv6Afi = "afi disable-ipv6-afi"; +constexpr std::string_view kAfiIpv4OverIpv6Nh = "afi ipv4-over-ipv6-nh"; +constexpr std::string_view kGracefulRestartTime = + "graceful-restart restart-time"; +constexpr std::string_view kGracefulRestartStatefulHa = + "graceful-restart stateful-ha"; +constexpr std::string_view kMaxRoutePreFilter = "max-route pre-filter"; +constexpr std::string_view kMaxRoutePostFilter = "max-route post-filter"; +constexpr std::string_view kMaxRoutePostWarningThreshold = + "max-route post-warning-threshold"; +constexpr std::string_view kTimersHoldTime = "timers hold-time"; +constexpr std::string_view kTimersKeepalive = "timers keepalive"; +constexpr std::string_view kTimersOutDelay = "timers out-delay"; +// Parity attributes: not in the documented peer-group grammar, but supported +// by the per-attribute peer-group commands this dispatcher replaces (and by +// real PeerGroup thrift fields), so dropping them would regress existing +// usage. +constexpr std::string_view kConfedPeer = "confed-peer"; +constexpr std::string_view kNextHopSelf = "next-hop-self"; +constexpr std::string_view kTimersWithdrawUnprogDelay = + "timers withdraw-unprog-delay"; +constexpr std::string_view kMaxRoutePreWarningThreshold = + "max-route pre-warning-threshold"; +constexpr std::string_view kMaxRoutePreWarningOnly = + "max-route pre-warning-only"; +constexpr std::string_view kMaxRoutePostWarningOnly = + "max-route post-warning-only"; + +// connect-mode values +constexpr std::string_view kConnectModePassive = "PASSIVE"; +constexpr std::string_view kConnectModeActive = "ACTIVE"; +constexpr std::string_view kConnectModeBoth = "BOTH"; + +using Tokens = std::vector; +using PeerGroup = bgp::thrift::PeerGroup; +using bgpcli::err; +using bgpcli::ok; +using bgpcli::parseBool; +using bgpcli::parseNonNegInt32; +using bgpcli::parseNonNegInt64; +using bgpcli::Result; +using facebook::neteng::fboss::bgp_attr::AddPath; + +// ---- per-attribute handlers ------------------------------------------------ +// Each handler mutates the typed bgp::thrift::PeerGroup directly. Field names +// map 1:1 to bgp_config.thrift, so the compiler validates them. Handlers are +// built from the factories below; only attributes with non-trivial parsing +// (connect-mode, add-path) have bespoke functions. + +using AttrHandler = std::function; + +AttrHandler boolAttr( + std::string_view name, + std::function set) { + return [name, set = std::move(set)]( + PeerGroup& group, const Tokens& values) -> Result { + if (values.size() != 1) { + return err(fmt::format("Error: {} requires ", name)); + } + auto enable = parseBool(values[0]); + if (!enable) { + return err( + fmt::format( + "Error: Invalid {} value '{}'; expected true or false", + name, + values[0])); + } + set(group, *enable); + return ok( + fmt::format( + "Successfully {} {}", *enable ? "enabled" : "disabled", name)); + }; +} + +// A single-token string value (policy names, tags). +AttrHandler stringAttr( + std::string_view name, + std::string_view valueName, + std::function set) { + return [name, valueName, set = std::move(set)]( + PeerGroup& group, const Tokens& values) -> Result { + if (values.size() != 1) { + return err(fmt::format("Error: {} requires <{}>", name, valueName)); + } + set(group, values[0]); + return ok(fmt::format("Successfully set {} to: {}", name, values[0])); + }; +} + +// A 4-byte ASN (RFC 6793): unsigned, bounded to [0, 2^32-1]. +AttrHandler asnAttr( + std::string_view name, + std::function set) { + return [name, set = std::move(set)]( + PeerGroup& group, const Tokens& values) -> Result { + if (values.size() != 1) { + return err(fmt::format("Error: {} requires ", name)); + } + auto asn = bgpcli::parseAsn4Byte(values[0]); + if (!asn) { + return err( + fmt::format( + "Error: Invalid {} value '{}'; expected an unsigned 4-byte ASN", + name, + values[0])); + } + set(group, *asn); + return ok(fmt::format("Successfully set {} to: {}", name, *asn)); + }; +} + +// A non-negative int32 seconds value (timers). +AttrHandler secondsAttr( + std::string_view name, + std::function set) { + return [name, set = std::move(set)]( + PeerGroup& group, const Tokens& values) -> Result { + if (values.size() != 1) { + return err(fmt::format("Error: {} requires ", name)); + } + auto seconds = parseNonNegInt32(values[0]); + if (!seconds) { + return err( + fmt::format( + "Error: {} must be a non-negative integer that fits in int32, " + "got '{}'", + name, + values[0])); + } + set(group, *seconds); + return ok( + fmt::format("Successfully set {} to: {} seconds", name, *seconds)); + }; +} + +// A non-negative int64 route-count value (RouteLimit fields). +AttrHandler routeCountAttr( + std::string_view name, + std::function set) { + return [name, set = std::move(set)]( + PeerGroup& group, const Tokens& values) -> Result { + if (values.size() != 1) { + return err(fmt::format("Error: {} requires ", name)); + } + auto limit = parseNonNegInt64(values[0]); + if (!limit) { + return err( + fmt::format( + "Error: {} must be a non-negative integer, got '{}'", + name, + values[0])); + } + set(group, *limit); + return ok(fmt::format("Successfully set {} to: {}", name, *limit)); + }; +} + +// An attribute the CLI must refuse: bgp_config.thrift has no field for it on +// PeerGroup (persisting it would stage dead config the daemon ignores) or the +// knob is not per-peer-group. The reason is surfaced to the user instead. +Result applyDescription(PeerGroup& group, const Tokens& values) { + if (values.empty()) { + return err(fmt::format("Error: {} requires ", kDescription)); + } + // The description may span multiple CLI tokens; re-join them. + std::string description = values[0]; + for (size_t i = 1; i < values.size(); i++) { + description += " " + values[i]; + } + group.description() = description; + return ok( + fmt::format("Successfully set {} to: {}", kDescription, description)); +} + +Result applyConnectMode(PeerGroup& group, const Tokens& values) { + // The thrift model only has is_passive (listen vs actively connect); there + // is no representation for BOTH, so it is rejected rather than silently + // mapped. + if (values.size() != 1) { + return err( + fmt::format( + "Error: {} requires <{}|{}>", + kConnectMode, + kConnectModePassive, + kConnectModeActive)); + } + if (values[0] == kConnectModePassive) { + group.is_passive() = true; + } else if (values[0] == kConnectModeActive) { + group.is_passive() = false; + } else if (values[0] == kConnectModeBoth) { + return err( + fmt::format( + "Error: {} {} has no representation in bgp_config.thrift " + "(PeerGroup only models is_passive); use {} or {}", + kConnectMode, + kConnectModeBoth, + kConnectModePassive, + kConnectModeActive)); + } else { + return err( + fmt::format( + "Error: Invalid {} value '{}'; expected {} or {}", + kConnectMode, + values[0], + kConnectModePassive, + kConnectModeActive)); + } + return ok(fmt::format("Successfully set {} to: {}", kConnectMode, values[0])); +} + +// add_path is a single enum whose values form a bitmask by design: +// RECEIVE=1, SEND=2, BOTH=3(=RECEIVE|SEND). Merge the requested direction +// into the current value; clearing the last direction unsets the field. +Result applyAddPath(PeerGroup& group, bool send, const Tokens& values) { + const std::string_view name = send ? kAddPathSend : kAddPathReceive; + if (values.size() != 1) { + return err(fmt::format("Error: {} requires ", name)); + } + auto enable = parseBool(values[0]); + if (!enable) { + return err( + fmt::format( + "Error: Invalid {} value '{}'; expected true or false", + name, + values[0])); + } + int bits = group.add_path() ? static_cast(*group.add_path()) : 0; + const int bit = send ? static_cast(AddPath::SEND) + : static_cast(AddPath::RECEIVE); + bits = *enable ? (bits | bit) : (bits & ~bit); + if (bits == 0) { + group.add_path().reset(); + } else { + group.add_path() = static_cast(bits); + } + return ok( + fmt::format( + "Successfully {} {}", *enable ? "enabled" : "disabled", name)); +} + +const std::map>& attrHandlers() { + static const std::map> kHandlers = { + {std::string(kRemoteAsn), + asnAttr( + kRemoteAsn, + [](PeerGroup& g, int64_t v) { g.remote_as_4_byte() = v; })}, + {std::string(kLocalAsn), + asnAttr( + kLocalAsn, + [](PeerGroup& g, int64_t v) { g.local_as_4_byte() = v; })}, + {std::string(kDescription), applyDescription}, + {std::string(kPeerTag), + stringAttr( + kPeerTag, + "string", + [](PeerGroup& g, const std::string& v) { g.peer_tag() = v; })}, + {std::string(kIngressPolicy), + stringAttr( + kIngressPolicy, + "policy-name", + [](PeerGroup& g, const std::string& v) { + g.ingress_policy_name() = v; + })}, + {std::string(kEgressPolicy), + stringAttr( + kEgressPolicy, + "policy-name", + [](PeerGroup& g, const std::string& v) { + g.egress_policy_name() = v; + })}, + {std::string(kRrClient), + boolAttr(kRrClient, [](PeerGroup& g, bool v) { g.is_rr_client() = v; })}, + {std::string(kConfedPeer), + boolAttr( + kConfedPeer, [](PeerGroup& g, bool v) { g.is_confed_peer() = v; })}, + {std::string(kRedistributePeer), + boolAttr( + kRedistributePeer, + [](PeerGroup& g, bool v) { g.is_redistribute_peer() = v; })}, + {std::string(kEnhancedRouteRefresh), + boolAttr( + kEnhancedRouteRefresh, + [](PeerGroup& g, bool v) { g.enhanced_route_refresh() = v; })}, + {std::string(kConnectMode), applyConnectMode}, + {std::string(kAddPathSend), + [](PeerGroup& g, const Tokens& v) { return applyAddPath(g, true, v); }}, + {std::string(kAddPathReceive), + [](PeerGroup& g, const Tokens& v) { return applyAddPath(g, false, v); }}, + {std::string(kAfiDisableIpv4Afi), + boolAttr( + kAfiDisableIpv4Afi, + [](PeerGroup& g, bool v) { g.disable_ipv4_afi() = v; })}, + {std::string(kAfiDisableIpv6Afi), + boolAttr( + kAfiDisableIpv6Afi, + [](PeerGroup& g, bool v) { g.disable_ipv6_afi() = v; })}, + {std::string(kAfiIpv4OverIpv6Nh), + boolAttr( + kAfiIpv4OverIpv6Nh, + [](PeerGroup& g, bool v) { g.v4_over_v6_nexthop() = v; })}, + {std::string(kGracefulRestartTime), + secondsAttr( + kGracefulRestartTime, + [](PeerGroup& g, int32_t v) { + g.bgp_peer_timers().ensure().graceful_restart_seconds() = v; + })}, + {std::string(kGracefulRestartStatefulHa), + boolAttr( + kGracefulRestartStatefulHa, + [](PeerGroup& g, bool v) { g.enable_stateful_ha() = v; })}, + {std::string(kMaxRoutePreFilter), + routeCountAttr( + kMaxRoutePreFilter, + [](PeerGroup& g, int64_t v) { + g.pre_filter().ensure().max_routes() = v; + })}, + {std::string(kMaxRoutePostFilter), + routeCountAttr( + kMaxRoutePostFilter, + [](PeerGroup& g, int64_t v) { + g.post_filter().ensure().max_routes() = v; + })}, + // RouteLimit.warning_limit is an absolute route count in the thrift + // schema, not a percentage. + {std::string(kMaxRoutePostWarningThreshold), + routeCountAttr( + kMaxRoutePostWarningThreshold, + [](PeerGroup& g, int64_t v) { + g.post_filter().ensure().warning_limit() = v; + })}, + {std::string(kTimersHoldTime), + secondsAttr( + kTimersHoldTime, + [](PeerGroup& g, int32_t v) { + g.bgp_peer_timers().ensure().hold_time_seconds() = v; + })}, + {std::string(kTimersKeepalive), + secondsAttr( + kTimersKeepalive, + [](PeerGroup& g, int32_t v) { + g.bgp_peer_timers().ensure().keep_alive_seconds() = v; + })}, + {std::string(kTimersOutDelay), + secondsAttr( + kTimersOutDelay, + [](PeerGroup& g, int32_t v) { + g.bgp_peer_timers().ensure().out_delay_seconds() = v; + })}, + {std::string(kTimersWithdrawUnprogDelay), + secondsAttr( + kTimersWithdrawUnprogDelay, + [](PeerGroup& g, int32_t v) { + g.bgp_peer_timers().ensure().withdraw_unprog_delay_seconds() = v; + })}, + {std::string(kNextHopSelf), + boolAttr( + kNextHopSelf, [](PeerGroup& g, bool v) { g.next_hop_self() = v; })}, + {std::string(kMaxRoutePreWarningThreshold), + routeCountAttr( + kMaxRoutePreWarningThreshold, + [](PeerGroup& g, int64_t v) { + g.pre_filter().ensure().warning_limit() = v; + })}, + {std::string(kMaxRoutePreWarningOnly), + boolAttr( + kMaxRoutePreWarningOnly, + [](PeerGroup& g, bool v) { + g.pre_filter().ensure().warning_only() = v; + })}, + {std::string(kMaxRoutePostWarningOnly), + boolAttr( + kMaxRoutePostWarningOnly, + [](PeerGroup& g, bool v) { + g.post_filter().ensure().warning_only() = v; + })}, + }; + return kHandlers; +} + +std::string validAttrList() { + std::string out; + for (const auto& [name, _] : attrHandlers()) { + if (!out.empty()) { + out += ", "; + } + out += name; + } + return out; +} + +// Find the peer-group keyed by name, creating it if absent. Setting an +// attribute on a not-yet-created group implicitly creates it, so command +// ordering stays forgiving; a bare `peer-group ` creates one explicitly. +// Unlike a BgpPeer, PeerGroup's only non-optional field is `name`, so no +// address seeding is needed. +bgp::thrift::PeerGroup& findOrCreatePeerGroup( + bgp::thrift::BgpConfig& cfg, + const std::string& groupName) { + auto& groups = cfg.peer_groups().ensure(); + for (auto& group : groups) { + if (*group.name() == groupName) { + return group; + } + } + groups.emplace_back(); + auto& group = groups.back(); + group.name() = groupName; + return group; +} + +// Whether a peer-group with the given name already exists. +bool peerGroupExists( + const bgp::thrift::BgpConfig& cfg, + const std::string& groupName) { + if (!cfg.peer_groups().has_value()) { + return false; + } + for (const auto& group : *cfg.peer_groups()) { + if (*group.name() == groupName) { + return true; + } + } + return false; +} + +} // namespace + +// Parse + validate at construction so queryClient stays a thin dispatch. +// Throwing std::invalid_argument is how the framework surfaces arg parse +// errors (same mechanism as BgpNeighborConfig). +BgpPeerGroupConfig::BgpPeerGroupConfig(std::vector v) + : utils::BaseObjectArgType(v) { + if (v.empty()) { + throw std::invalid_argument( + "Error: peer-group is required, followed by an optional " + " "); + } + if (v[0].empty()) { + throw std::invalid_argument("Error: peer-group name must not be empty"); + } + groupName_ = v[0]; + + if (v.size() == 1) { + return; // bare `peer-group `: create the group + } + + // Attributes may span two tokens (e.g. `timers hold-time`); match the + // longest prefix of the remaining tokens against the dispatch table. + const auto& handlers = attrHandlers(); + if (v.size() >= 3) { + std::string twoToken = v[1] + " " + v[2]; + if (handlers.find(twoToken) != handlers.end()) { + attr_ = std::move(twoToken); + values_.assign(v.begin() + 3, v.end()); + return; + } + } + if (handlers.find(v[1]) != handlers.end()) { + attr_ = v[1]; + values_.assign(v.begin() + 2, v.end()); + return; + } + throw std::invalid_argument( + fmt::format( + "Error: unknown peer-group attribute '{}'. Valid attributes: {}", + v[1], + validAttrList())); +} + CmdConfigProtocolBgpPeerGroupTraits::RetType CmdConfigProtocolBgpPeerGroup::queryClient( const HostInfo& /* hostInfo */, - const ObjectArgType& groupNameMsg) { - auto& session = BgpConfigSession::getInstance(); + const ObjectArgType& args) { + auto& session = ConfigSession::getInstance(); + auto& cfg = session.getBgpConfig(); + const bool created = !peerGroupExists(cfg, args.groupName()); + auto& group = findOrCreatePeerGroup(cfg, args.groupName()); - if (groupNameMsg.data().empty()) { - return "Error: peer-group name is required"; + Result result = args.attr().empty() + ? ok(fmt::format( + "Successfully created BGP peer-group {}", args.groupName())) + // The attribute is guaranteed valid: BgpPeerGroupConfig's constructor + // rejects an unknown attribute before we get here. + : attrHandlers().find(args.attr())->second(group, args.values()); + if (result.ok) { + if (!args.attr().empty()) { + result.message += fmt::format(" for peer-group {}", args.groupName()); + } + session.saveBgpConfig(); + result.message += + fmt::format("\nConfig saved to: {}", session.getBgpSessionConfigPath()); + } else if (created) { + // A rejected value must not leave a half-created group in the in-memory + // config (visible to later lookups in the same process, e.g. tests). + cfg.peer_groups()->pop_back(); } - - std::string groupName = groupNameMsg.data()[0]; - session.createPeerGroup(groupName); - session.saveConfig(); - - return fmt::format( - "BGP peer-group {} created.\n" - "Use subcommands: remote-asn, description, hold-time, keepalive\n" - "Config saved to: {}", - groupName, - session.getSessionConfigPath()); + return result.message; } void CmdConfigProtocolBgpPeerGroup::printOutput(const RetType& output) { diff --git a/fboss/cli/fboss2/commands/config/protocol/bgp/peer-group/CmdConfigProtocolBgpPeerGroup.h b/fboss/cli/fboss2/commands/config/protocol/bgp/peer-group/CmdConfigProtocolBgpPeerGroup.h index 1c6d74e0f375a..1eca3e696eff3 100644 --- a/fboss/cli/fboss2/commands/config/protocol/bgp/peer-group/CmdConfigProtocolBgpPeerGroup.h +++ b/fboss/cli/fboss2/commands/config/protocol/bgp/peer-group/CmdConfigProtocolBgpPeerGroup.h @@ -10,18 +10,54 @@ #pragma once +#include +#include +#include "CLI/App.hpp" #include "fboss/cli/fboss2/CmdHandler.h" #include "fboss/cli/fboss2/commands/config/protocol/bgp/CmdConfigProtocolBgp.h" -#include "fboss/cli/fboss2/utils/CmdUtils.h" +#include "fboss/cli/fboss2/utils/CmdUtilsCommon.h" +#include "fboss/cli/fboss2/utils/HostInfo.h" namespace facebook::fboss { +// Parsed `peer-group [ ...]`, validated at +// construction. Attributes may span two tokens (e.g. `timers hold-time`), +// matched longest-prefix-first. Bare `peer-group ` creates the group. +// Mirrors BgpNeighborConfig. +class BgpPeerGroupConfig : public utils::BaseObjectArgType { + public: + // NOLINTNEXTLINE(google-explicit-constructor) + /* implicit */ BgpPeerGroupConfig(std::vector v); + const std::string& groupName() const { + return groupName_; + } + const std::string& attr() const { + return attr_; + } + const std::vector& values() const { + return values_; + } + const static utils::ObjectArgTypeId id = + utils::ObjectArgTypeId::OBJECT_ARG_TYPE_ID_MESSAGE; + + private: + std::string groupName_; + std::string attr_; // matched dispatch key ("" = bare create) + std::vector values_; +}; + +// Single handler for the whole `config protocol bgp peer-group` family; +// dispatch and per-attribute parsing live in the .cpp so a new tunable is a +// one-entry change. Mirrors CmdConfigProtocolBgpNeighbor. struct CmdConfigProtocolBgpPeerGroupTraits : public WriteCommandTraits { using ParentCmd = CmdConfigProtocolBgp; static void addCliArg(CLI::App& cmd, std::vector& args) { - cmd.add_option("msg", args, "Message"); + // Stops CLI11 from classifying attribute tokens as subcommands once the + // group name is consumed. See CmdConfigProtocolBgpNeighborTraits. + cmd.positionals_at_end(); + cmd.add_option("args", args, " [ ...]"); } - using ObjectArgType = utils::Message; + using ObjectArgType = BgpPeerGroupConfig; using RetType = std::string; }; @@ -32,7 +68,7 @@ class CmdConfigProtocolBgpPeerGroup : public CmdHandler< using ObjectArgType = CmdConfigProtocolBgpPeerGroupTraits::ObjectArgType; using RetType = CmdConfigProtocolBgpPeerGroupTraits::RetType; - RetType queryClient(const HostInfo& hostInfo, const ObjectArgType& groupName); + RetType queryClient(const HostInfo& hostInfo, const ObjectArgType& args); void printOutput(const RetType& output); }; diff --git a/fboss/cli/fboss2/commands/config/protocol/bgp/peer-group/CmdConfigProtocolBgpPeerGroupConfedPeer.cpp b/fboss/cli/fboss2/commands/config/protocol/bgp/peer-group/CmdConfigProtocolBgpPeerGroupConfedPeer.cpp deleted file mode 100644 index 13b8c1bd31e76..0000000000000 --- a/fboss/cli/fboss2/commands/config/protocol/bgp/peer-group/CmdConfigProtocolBgpPeerGroupConfedPeer.cpp +++ /dev/null @@ -1,61 +0,0 @@ -/* - * Copyright (c) 2004-present, Facebook, Inc. - * All rights reserved. - * - * This source code is licensed under the BSD-style license found in the - * LICENSE file in the root directory of this source tree. An additional grant - * of patent rights can be found in the PATENTS file in the same directory. - * - */ - -#include "fboss/cli/fboss2/commands/config/protocol/bgp/peer-group/CmdConfigProtocolBgpPeerGroupConfedPeer.h" - -#include "fboss/cli/fboss2/CmdHandler.cpp" - -#include -#include "fboss/cli/fboss2/commands/config/protocol/bgp/BgpConfigSession.h" - -namespace facebook::fboss { - -CmdConfigProtocolBgpPeerGroupConfedPeerTraits::RetType -CmdConfigProtocolBgpPeerGroupConfedPeer::queryClient( - const HostInfo& /* hostInfo */, - const utils::Message& peerGroupNameMsg, - const ObjectArgType& enableMsg) { - auto& session = BgpConfigSession::getInstance(); - - if (peerGroupNameMsg.data().empty()) { - return "Error: peer-group name is required"; - } - std::string peerGroupName = peerGroupNameMsg.data()[0]; - - if (enableMsg.data().empty()) { - return fmt::format( - "Error: enable value required for peer-group '{}'", peerGroupName); - } - std::string enableStr = enableMsg.data()[0]; - bool enable = (enableStr == "true" || enableStr == "1" || enableStr == "yes"); - - session.createPeerGroup(peerGroupName); - session.setPeerGroupConfedPeer(peerGroupName, enable); - session.saveConfig(); - - return fmt::format( - "Successfully {} confederation-peer for peer-group '{}'\n" - "Config saved to: {}", - enable ? "enabled" : "disabled", - peerGroupName, - session.getSessionConfigPath()); -} - -void CmdConfigProtocolBgpPeerGroupConfedPeer::printOutput( - const RetType& output) { - std::cout << output << std::endl; -} - -// Explicit template instantiation -template void CmdHandler< - CmdConfigProtocolBgpPeerGroupConfedPeer, - CmdConfigProtocolBgpPeerGroupConfedPeerTraits>::run(); - -} // namespace facebook::fboss diff --git a/fboss/cli/fboss2/commands/config/protocol/bgp/peer-group/CmdConfigProtocolBgpPeerGroupConfedPeer.h b/fboss/cli/fboss2/commands/config/protocol/bgp/peer-group/CmdConfigProtocolBgpPeerGroupConfedPeer.h deleted file mode 100644 index 32fb0f19c28a6..0000000000000 --- a/fboss/cli/fboss2/commands/config/protocol/bgp/peer-group/CmdConfigProtocolBgpPeerGroupConfedPeer.h +++ /dev/null @@ -1,46 +0,0 @@ -/* - * 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/config/protocol/bgp/peer-group/CmdConfigProtocolBgpPeerGroup.h" -#include "fboss/cli/fboss2/utils/CmdUtils.h" - -namespace facebook::fboss { - -struct CmdConfigProtocolBgpPeerGroupConfedPeerTraits - : public WriteCommandTraits { - using ParentCmd = CmdConfigProtocolBgpPeerGroup; - static void addCliArg(CLI::App& cmd, std::vector& args) { - cmd.add_option("msg", args, "Message"); - } - using ObjectArgType = utils::Message; - using RetType = std::string; -}; - -class CmdConfigProtocolBgpPeerGroupConfedPeer - : public CmdHandler< - CmdConfigProtocolBgpPeerGroupConfedPeer, - CmdConfigProtocolBgpPeerGroupConfedPeerTraits> { - public: - using ObjectArgType = - CmdConfigProtocolBgpPeerGroupConfedPeerTraits::ObjectArgType; - using RetType = CmdConfigProtocolBgpPeerGroupConfedPeerTraits::RetType; - - RetType queryClient( - const HostInfo& hostInfo, - const utils::Message& peerGroupName, - const ObjectArgType& enable); - - void printOutput(const RetType& output); -}; - -} // namespace facebook::fboss diff --git a/fboss/cli/fboss2/commands/config/protocol/bgp/peer-group/CmdConfigProtocolBgpPeerGroupDescription.cpp b/fboss/cli/fboss2/commands/config/protocol/bgp/peer-group/CmdConfigProtocolBgpPeerGroupDescription.cpp deleted file mode 100644 index f316e3477be66..0000000000000 --- a/fboss/cli/fboss2/commands/config/protocol/bgp/peer-group/CmdConfigProtocolBgpPeerGroupDescription.cpp +++ /dev/null @@ -1,57 +0,0 @@ -/* - * Copyright (c) 2004-present, Facebook, Inc. - * All rights reserved. - * - * This source code is licensed under the BSD-style license found in the - * LICENSE file in the root directory of this source tree. An additional grant - * of patent rights can be found in the PATENTS file in the same directory. - * - */ - -#include "fboss/cli/fboss2/commands/config/protocol/bgp/peer-group/CmdConfigProtocolBgpPeerGroupDescription.h" - -#include "fboss/cli/fboss2/CmdHandler.cpp" - -#include -#include "fboss/cli/fboss2/commands/config/protocol/bgp/BgpConfigSession.h" - -namespace facebook::fboss { - -CmdConfigProtocolBgpPeerGroupDescriptionTraits::RetType -CmdConfigProtocolBgpPeerGroupDescription::queryClient( - const HostInfo& /* hostInfo */, - const utils::Message& groupNameMsg, - const ObjectArgType& descriptionMsg) { - auto& session = BgpConfigSession::getInstance(); - - if (groupNameMsg.data().empty()) { - return "Error: peer-group name is required"; - } - if (descriptionMsg.data().empty()) { - return "Error: description is required"; - } - - std::string groupName = groupNameMsg.data()[0]; - std::string description = descriptionMsg.data()[0]; - - session.setPeerGroupDescription(groupName, description); - session.saveConfig(); - - return fmt::format( - "Successfully set peer-group {} description to: {}\nConfig saved to: {}", - groupName, - description, - session.getSessionConfigPath()); -} - -void CmdConfigProtocolBgpPeerGroupDescription::printOutput( - const RetType& output) { - std::cout << output << std::endl; -} - -// Explicit template instantiation -template void CmdHandler< - CmdConfigProtocolBgpPeerGroupDescription, - CmdConfigProtocolBgpPeerGroupDescriptionTraits>::run(); - -} // namespace facebook::fboss diff --git a/fboss/cli/fboss2/commands/config/protocol/bgp/peer-group/CmdConfigProtocolBgpPeerGroupDescription.h b/fboss/cli/fboss2/commands/config/protocol/bgp/peer-group/CmdConfigProtocolBgpPeerGroupDescription.h deleted file mode 100644 index 37ee936eeaaa1..0000000000000 --- a/fboss/cli/fboss2/commands/config/protocol/bgp/peer-group/CmdConfigProtocolBgpPeerGroupDescription.h +++ /dev/null @@ -1,46 +0,0 @@ -/* - * 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/config/protocol/bgp/peer-group/CmdConfigProtocolBgpPeerGroup.h" -#include "fboss/cli/fboss2/utils/CmdUtils.h" - -namespace facebook::fboss { - -struct CmdConfigProtocolBgpPeerGroupDescriptionTraits - : public WriteCommandTraits { - using ParentCmd = CmdConfigProtocolBgpPeerGroup; - static void addCliArg(CLI::App& cmd, std::vector& args) { - cmd.add_option("msg", args, "Message"); - } - using ObjectArgType = utils::Message; - using RetType = std::string; -}; - -class CmdConfigProtocolBgpPeerGroupDescription - : public CmdHandler< - CmdConfigProtocolBgpPeerGroupDescription, - CmdConfigProtocolBgpPeerGroupDescriptionTraits> { - public: - using ObjectArgType = - CmdConfigProtocolBgpPeerGroupDescriptionTraits::ObjectArgType; - using RetType = CmdConfigProtocolBgpPeerGroupDescriptionTraits::RetType; - - RetType queryClient( - const HostInfo& hostInfo, - const utils::Message& groupName, - const ObjectArgType& description); - - void printOutput(const RetType& output); -}; - -} // namespace facebook::fboss diff --git a/fboss/cli/fboss2/commands/config/protocol/bgp/peer-group/CmdConfigProtocolBgpPeerGroupDisableIpv4Afi.cpp b/fboss/cli/fboss2/commands/config/protocol/bgp/peer-group/CmdConfigProtocolBgpPeerGroupDisableIpv4Afi.cpp deleted file mode 100644 index 0ab361e4cbeb7..0000000000000 --- a/fboss/cli/fboss2/commands/config/protocol/bgp/peer-group/CmdConfigProtocolBgpPeerGroupDisableIpv4Afi.cpp +++ /dev/null @@ -1,62 +0,0 @@ -/* - * Copyright (c) 2004-present, Facebook, Inc. - * All rights reserved. - * - * This source code is licensed under the BSD-style license found in the - * LICENSE file in the root directory of this source tree. An additional grant - * of patent rights can be found in the PATENTS file in the same directory. - * - */ - -#include "fboss/cli/fboss2/commands/config/protocol/bgp/peer-group/CmdConfigProtocolBgpPeerGroupDisableIpv4Afi.h" - -#include "fboss/cli/fboss2/CmdHandler.cpp" - -#include -#include "fboss/cli/fboss2/commands/config/protocol/bgp/BgpConfigSession.h" - -namespace facebook::fboss { - -CmdConfigProtocolBgpPeerGroupDisableIpv4AfiTraits::RetType -CmdConfigProtocolBgpPeerGroupDisableIpv4Afi::queryClient( - const HostInfo& /* hostInfo */, - const utils::Message& peerGroupNameMsg, - const ObjectArgType& disableMsg) { - auto& session = BgpConfigSession::getInstance(); - - if (peerGroupNameMsg.data().empty()) { - return "Error: peer-group name is required"; - } - std::string peerGroupName = peerGroupNameMsg.data()[0]; - - if (disableMsg.data().empty()) { - return fmt::format( - "Error: disable value required for peer-group '{}'", peerGroupName); - } - std::string disableStr = disableMsg.data()[0]; - bool disable = - (disableStr == "true" || disableStr == "1" || disableStr == "yes"); - - session.createPeerGroup(peerGroupName); - session.setPeerGroupDisableIpv4Afi(peerGroupName, disable); - session.saveConfig(); - - return fmt::format( - "Successfully {} IPv4 AFI for peer-group '{}'\n" - "Config saved to: {}", - disable ? "disabled" : "enabled", - peerGroupName, - session.getSessionConfigPath()); -} - -void CmdConfigProtocolBgpPeerGroupDisableIpv4Afi::printOutput( - const RetType& output) { - std::cout << output << std::endl; -} - -// Explicit template instantiation -template void CmdHandler< - CmdConfigProtocolBgpPeerGroupDisableIpv4Afi, - CmdConfigProtocolBgpPeerGroupDisableIpv4AfiTraits>::run(); - -} // namespace facebook::fboss diff --git a/fboss/cli/fboss2/commands/config/protocol/bgp/peer-group/CmdConfigProtocolBgpPeerGroupDisableIpv4Afi.h b/fboss/cli/fboss2/commands/config/protocol/bgp/peer-group/CmdConfigProtocolBgpPeerGroupDisableIpv4Afi.h deleted file mode 100644 index cd06d0f7568d8..0000000000000 --- a/fboss/cli/fboss2/commands/config/protocol/bgp/peer-group/CmdConfigProtocolBgpPeerGroupDisableIpv4Afi.h +++ /dev/null @@ -1,46 +0,0 @@ -/* - * 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/config/protocol/bgp/peer-group/CmdConfigProtocolBgpPeerGroup.h" -#include "fboss/cli/fboss2/utils/CmdUtils.h" - -namespace facebook::fboss { - -struct CmdConfigProtocolBgpPeerGroupDisableIpv4AfiTraits - : public WriteCommandTraits { - using ParentCmd = CmdConfigProtocolBgpPeerGroup; - static void addCliArg(CLI::App& cmd, std::vector& args) { - cmd.add_option("msg", args, "Message"); - } - using ObjectArgType = utils::Message; - using RetType = std::string; -}; - -class CmdConfigProtocolBgpPeerGroupDisableIpv4Afi - : public CmdHandler< - CmdConfigProtocolBgpPeerGroupDisableIpv4Afi, - CmdConfigProtocolBgpPeerGroupDisableIpv4AfiTraits> { - public: - using ObjectArgType = - CmdConfigProtocolBgpPeerGroupDisableIpv4AfiTraits::ObjectArgType; - using RetType = CmdConfigProtocolBgpPeerGroupDisableIpv4AfiTraits::RetType; - - RetType queryClient( - const HostInfo& hostInfo, - const utils::Message& peerGroupName, - const ObjectArgType& disable); - - void printOutput(const RetType& output); -}; - -} // namespace facebook::fboss diff --git a/fboss/cli/fboss2/commands/config/protocol/bgp/peer-group/CmdConfigProtocolBgpPeerGroupEgressPolicy.cpp b/fboss/cli/fboss2/commands/config/protocol/bgp/peer-group/CmdConfigProtocolBgpPeerGroupEgressPolicy.cpp deleted file mode 100644 index de09a95dedf21..0000000000000 --- a/fboss/cli/fboss2/commands/config/protocol/bgp/peer-group/CmdConfigProtocolBgpPeerGroupEgressPolicy.cpp +++ /dev/null @@ -1,61 +0,0 @@ -/* - * Copyright (c) 2004-present, Facebook, Inc. - * All rights reserved. - * - * This source code is licensed under the BSD-style license found in the - * LICENSE file in the root directory of this source tree. An additional grant - * of patent rights can be found in the PATENTS file in the same directory. - * - */ - -#include "fboss/cli/fboss2/commands/config/protocol/bgp/peer-group/CmdConfigProtocolBgpPeerGroupEgressPolicy.h" - -#include "fboss/cli/fboss2/CmdHandler.cpp" - -#include -#include "fboss/cli/fboss2/commands/config/protocol/bgp/BgpConfigSession.h" - -namespace facebook::fboss { - -CmdConfigProtocolBgpPeerGroupEgressPolicyTraits::RetType -CmdConfigProtocolBgpPeerGroupEgressPolicy::queryClient( - const HostInfo& /* hostInfo */, - const utils::Message& peerGroupNameMsg, - const ObjectArgType& policyMsg) { - auto& session = BgpConfigSession::getInstance(); - - if (peerGroupNameMsg.data().empty()) { - return "Error: peer-group name is required"; - } - std::string peerGroupName = peerGroupNameMsg.data()[0]; - - if (policyMsg.data().empty()) { - return fmt::format( - "Error: egress policy name is required for peer-group '{}'", - peerGroupName); - } - std::string policyName = policyMsg.data()[0]; - - session.createPeerGroup(peerGroupName); - session.setPeerGroupEgressPolicy(peerGroupName, policyName); - session.saveConfig(); - - return fmt::format( - "Successfully set egress policy for peer-group '{}' to: '{}'\n" - "Config saved to: {}", - peerGroupName, - policyName, - session.getSessionConfigPath()); -} - -void CmdConfigProtocolBgpPeerGroupEgressPolicy::printOutput( - const RetType& output) { - std::cout << output << std::endl; -} - -// Explicit template instantiation -template void CmdHandler< - CmdConfigProtocolBgpPeerGroupEgressPolicy, - CmdConfigProtocolBgpPeerGroupEgressPolicyTraits>::run(); - -} // namespace facebook::fboss diff --git a/fboss/cli/fboss2/commands/config/protocol/bgp/peer-group/CmdConfigProtocolBgpPeerGroupEgressPolicy.h b/fboss/cli/fboss2/commands/config/protocol/bgp/peer-group/CmdConfigProtocolBgpPeerGroupEgressPolicy.h deleted file mode 100644 index e55ae153a80d7..0000000000000 --- a/fboss/cli/fboss2/commands/config/protocol/bgp/peer-group/CmdConfigProtocolBgpPeerGroupEgressPolicy.h +++ /dev/null @@ -1,46 +0,0 @@ -/* - * 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/config/protocol/bgp/peer-group/CmdConfigProtocolBgpPeerGroup.h" -#include "fboss/cli/fboss2/utils/CmdUtils.h" - -namespace facebook::fboss { - -struct CmdConfigProtocolBgpPeerGroupEgressPolicyTraits - : public WriteCommandTraits { - using ParentCmd = CmdConfigProtocolBgpPeerGroup; - static void addCliArg(CLI::App& cmd, std::vector& args) { - cmd.add_option("msg", args, "Message"); - } - using ObjectArgType = utils::Message; - using RetType = std::string; -}; - -class CmdConfigProtocolBgpPeerGroupEgressPolicy - : public CmdHandler< - CmdConfigProtocolBgpPeerGroupEgressPolicy, - CmdConfigProtocolBgpPeerGroupEgressPolicyTraits> { - public: - using ObjectArgType = - CmdConfigProtocolBgpPeerGroupEgressPolicyTraits::ObjectArgType; - using RetType = CmdConfigProtocolBgpPeerGroupEgressPolicyTraits::RetType; - - RetType queryClient( - const HostInfo& hostInfo, - const utils::Message& peerGroupName, - const ObjectArgType& policyName); - - void printOutput(const RetType& output); -}; - -} // namespace facebook::fboss diff --git a/fboss/cli/fboss2/commands/config/protocol/bgp/peer-group/CmdConfigProtocolBgpPeerGroupIngressPolicy.cpp b/fboss/cli/fboss2/commands/config/protocol/bgp/peer-group/CmdConfigProtocolBgpPeerGroupIngressPolicy.cpp deleted file mode 100644 index 3e7b7fe50d2b9..0000000000000 --- a/fboss/cli/fboss2/commands/config/protocol/bgp/peer-group/CmdConfigProtocolBgpPeerGroupIngressPolicy.cpp +++ /dev/null @@ -1,61 +0,0 @@ -/* - * Copyright (c) 2004-present, Facebook, Inc. - * All rights reserved. - * - * This source code is licensed under the BSD-style license found in the - * LICENSE file in the root directory of this source tree. An additional grant - * of patent rights can be found in the PATENTS file in the same directory. - * - */ - -#include "fboss/cli/fboss2/commands/config/protocol/bgp/peer-group/CmdConfigProtocolBgpPeerGroupIngressPolicy.h" - -#include "fboss/cli/fboss2/CmdHandler.cpp" - -#include -#include "fboss/cli/fboss2/commands/config/protocol/bgp/BgpConfigSession.h" - -namespace facebook::fboss { - -CmdConfigProtocolBgpPeerGroupIngressPolicyTraits::RetType -CmdConfigProtocolBgpPeerGroupIngressPolicy::queryClient( - const HostInfo& /* hostInfo */, - const utils::Message& peerGroupNameMsg, - const ObjectArgType& policyMsg) { - auto& session = BgpConfigSession::getInstance(); - - if (peerGroupNameMsg.data().empty()) { - return "Error: peer-group name is required"; - } - std::string peerGroupName = peerGroupNameMsg.data()[0]; - - if (policyMsg.data().empty()) { - return fmt::format( - "Error: ingress policy name is required for peer-group '{}'", - peerGroupName); - } - std::string policyName = policyMsg.data()[0]; - - session.createPeerGroup(peerGroupName); - session.setPeerGroupIngressPolicy(peerGroupName, policyName); - session.saveConfig(); - - return fmt::format( - "Successfully set ingress policy for peer-group '{}' to: '{}'\n" - "Config saved to: {}", - peerGroupName, - policyName, - session.getSessionConfigPath()); -} - -void CmdConfigProtocolBgpPeerGroupIngressPolicy::printOutput( - const RetType& output) { - std::cout << output << std::endl; -} - -// Explicit template instantiation -template void CmdHandler< - CmdConfigProtocolBgpPeerGroupIngressPolicy, - CmdConfigProtocolBgpPeerGroupIngressPolicyTraits>::run(); - -} // namespace facebook::fboss diff --git a/fboss/cli/fboss2/commands/config/protocol/bgp/peer-group/CmdConfigProtocolBgpPeerGroupIngressPolicy.h b/fboss/cli/fboss2/commands/config/protocol/bgp/peer-group/CmdConfigProtocolBgpPeerGroupIngressPolicy.h deleted file mode 100644 index f3345936405ae..0000000000000 --- a/fboss/cli/fboss2/commands/config/protocol/bgp/peer-group/CmdConfigProtocolBgpPeerGroupIngressPolicy.h +++ /dev/null @@ -1,46 +0,0 @@ -/* - * 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/config/protocol/bgp/peer-group/CmdConfigProtocolBgpPeerGroup.h" -#include "fboss/cli/fboss2/utils/CmdUtils.h" - -namespace facebook::fboss { - -struct CmdConfigProtocolBgpPeerGroupIngressPolicyTraits - : public WriteCommandTraits { - using ParentCmd = CmdConfigProtocolBgpPeerGroup; - static void addCliArg(CLI::App& cmd, std::vector& args) { - cmd.add_option("msg", args, "Message"); - } - using ObjectArgType = utils::Message; - using RetType = std::string; -}; - -class CmdConfigProtocolBgpPeerGroupIngressPolicy - : public CmdHandler< - CmdConfigProtocolBgpPeerGroupIngressPolicy, - CmdConfigProtocolBgpPeerGroupIngressPolicyTraits> { - public: - using ObjectArgType = - CmdConfigProtocolBgpPeerGroupIngressPolicyTraits::ObjectArgType; - using RetType = CmdConfigProtocolBgpPeerGroupIngressPolicyTraits::RetType; - - RetType queryClient( - const HostInfo& hostInfo, - const utils::Message& peerGroupName, - const ObjectArgType& policyName); - - void printOutput(const RetType& output); -}; - -} // namespace facebook::fboss diff --git a/fboss/cli/fboss2/commands/config/protocol/bgp/peer-group/CmdConfigProtocolBgpPeerGroupMaxRoutes.cpp b/fboss/cli/fboss2/commands/config/protocol/bgp/peer-group/CmdConfigProtocolBgpPeerGroupMaxRoutes.cpp deleted file mode 100644 index a3af4c904f8b4..0000000000000 --- a/fboss/cli/fboss2/commands/config/protocol/bgp/peer-group/CmdConfigProtocolBgpPeerGroupMaxRoutes.cpp +++ /dev/null @@ -1,71 +0,0 @@ -/* - * Copyright (c) 2004-present, Facebook, Inc. - * All rights reserved. - * - * This source code is licensed under the BSD-style license found in the - * LICENSE file in the root directory of this source tree. An additional grant - * of patent rights can be found in the PATENTS file in the same directory. - * - */ - -#include "fboss/cli/fboss2/commands/config/protocol/bgp/peer-group/CmdConfigProtocolBgpPeerGroupMaxRoutes.h" - -#include "fboss/cli/fboss2/CmdHandler.cpp" - -#include -#include -#include "fboss/cli/fboss2/commands/config/protocol/bgp/BgpConfigSession.h" - -namespace facebook::fboss { - -CmdConfigProtocolBgpPeerGroupMaxRoutesTraits::RetType -CmdConfigProtocolBgpPeerGroupMaxRoutes::queryClient( - const HostInfo& /* hostInfo */, - const utils::Message& peerGroupNameMsg, - const ObjectArgType& maxRoutesMsg) { - auto& session = BgpConfigSession::getInstance(); - - if (peerGroupNameMsg.data().empty()) { - return "Error: peer-group name is required"; - } - std::string peerGroupName = peerGroupNameMsg.data()[0]; - - if (maxRoutesMsg.data().empty()) { - return fmt::format( - "Error: max-routes value is required for peer-group '{}'", - peerGroupName); - } - - int64_t maxRoutes; - try { - maxRoutes = folly::to(maxRoutesMsg.data()[0]); - } catch (const std::exception& e) { - return fmt::format( - "Error: invalid max-routes value '{}': {}", - maxRoutesMsg.data()[0], - e.what()); - } - - session.createPeerGroup(peerGroupName); - session.setPeerGroupPreFilterMaxRoutes(peerGroupName, maxRoutes); - session.saveConfig(); - - return fmt::format( - "Successfully set max-routes for peer-group '{}' to: {}\n" - "Config saved to: {}", - peerGroupName, - maxRoutes, - session.getSessionConfigPath()); -} - -void CmdConfigProtocolBgpPeerGroupMaxRoutes::printOutput( - const RetType& output) { - std::cout << output << std::endl; -} - -// Explicit template instantiation -template void CmdHandler< - CmdConfigProtocolBgpPeerGroupMaxRoutes, - CmdConfigProtocolBgpPeerGroupMaxRoutesTraits>::run(); - -} // namespace facebook::fboss diff --git a/fboss/cli/fboss2/commands/config/protocol/bgp/peer-group/CmdConfigProtocolBgpPeerGroupMaxRoutes.h b/fboss/cli/fboss2/commands/config/protocol/bgp/peer-group/CmdConfigProtocolBgpPeerGroupMaxRoutes.h deleted file mode 100644 index 655b445f342cb..0000000000000 --- a/fboss/cli/fboss2/commands/config/protocol/bgp/peer-group/CmdConfigProtocolBgpPeerGroupMaxRoutes.h +++ /dev/null @@ -1,46 +0,0 @@ -/* - * 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/config/protocol/bgp/peer-group/CmdConfigProtocolBgpPeerGroup.h" -#include "fboss/cli/fboss2/utils/CmdUtils.h" - -namespace facebook::fboss { - -struct CmdConfigProtocolBgpPeerGroupMaxRoutesTraits - : public WriteCommandTraits { - using ParentCmd = CmdConfigProtocolBgpPeerGroup; - static void addCliArg(CLI::App& cmd, std::vector& args) { - cmd.add_option("msg", args, "Message"); - } - using ObjectArgType = utils::Message; - using RetType = std::string; -}; - -class CmdConfigProtocolBgpPeerGroupMaxRoutes - : public CmdHandler< - CmdConfigProtocolBgpPeerGroupMaxRoutes, - CmdConfigProtocolBgpPeerGroupMaxRoutesTraits> { - public: - using ObjectArgType = - CmdConfigProtocolBgpPeerGroupMaxRoutesTraits::ObjectArgType; - using RetType = CmdConfigProtocolBgpPeerGroupMaxRoutesTraits::RetType; - - RetType queryClient( - const HostInfo& hostInfo, - const utils::Message& peerGroupName, - const ObjectArgType& maxRoutes); - - void printOutput(const RetType& output); -}; - -} // namespace facebook::fboss diff --git a/fboss/cli/fboss2/commands/config/protocol/bgp/peer-group/CmdConfigProtocolBgpPeerGroupNextHopSelf.cpp b/fboss/cli/fboss2/commands/config/protocol/bgp/peer-group/CmdConfigProtocolBgpPeerGroupNextHopSelf.cpp deleted file mode 100644 index 6800cdddac8c9..0000000000000 --- a/fboss/cli/fboss2/commands/config/protocol/bgp/peer-group/CmdConfigProtocolBgpPeerGroupNextHopSelf.cpp +++ /dev/null @@ -1,63 +0,0 @@ -/* - * Copyright (c) 2004-present, Facebook, Inc. - * All rights reserved. - * - * This source code is licensed under the BSD-style license found in the - * LICENSE file in the root directory of this source tree. An additional grant - * of patent rights can be found in the PATENTS file in the same directory. - * - */ - -#include "fboss/cli/fboss2/commands/config/protocol/bgp/peer-group/CmdConfigProtocolBgpPeerGroupNextHopSelf.h" - -#include "fboss/cli/fboss2/CmdHandler.cpp" - -#include -#include "fboss/cli/fboss2/commands/config/protocol/bgp/BgpConfigSession.h" - -namespace facebook::fboss { - -CmdConfigProtocolBgpPeerGroupNextHopSelfTraits::RetType -CmdConfigProtocolBgpPeerGroupNextHopSelf::queryClient( - const HostInfo& /* hostInfo */, - const utils::Message& peerGroupNameMsg, - const ObjectArgType& enableMsg) { - auto& session = BgpConfigSession::getInstance(); - - if (peerGroupNameMsg.data().empty()) { - return "Error: peer-group name is required"; - } - std::string peerGroupName = peerGroupNameMsg.data()[0]; - - if (enableMsg.data().empty()) { - return fmt::format( - "Error: enable value (true/false) is required for peer-group '{}'", - peerGroupName); - } - - std::string enableStr = enableMsg.data()[0]; - bool enable = (enableStr == "true" || enableStr == "1" || enableStr == "yes"); - - session.createPeerGroup(peerGroupName); - session.setPeerGroupNextHopSelf(peerGroupName, enable); - session.saveConfig(); - - return fmt::format( - "Successfully {} next-hop-self for peer-group '{}'\n" - "Config saved to: {}", - enable ? "enabled" : "disabled", - peerGroupName, - session.getSessionConfigPath()); -} - -void CmdConfigProtocolBgpPeerGroupNextHopSelf::printOutput( - const RetType& output) { - std::cout << output << std::endl; -} - -// Explicit template instantiation -template void CmdHandler< - CmdConfigProtocolBgpPeerGroupNextHopSelf, - CmdConfigProtocolBgpPeerGroupNextHopSelfTraits>::run(); - -} // namespace facebook::fboss diff --git a/fboss/cli/fboss2/commands/config/protocol/bgp/peer-group/CmdConfigProtocolBgpPeerGroupNextHopSelf.h b/fboss/cli/fboss2/commands/config/protocol/bgp/peer-group/CmdConfigProtocolBgpPeerGroupNextHopSelf.h deleted file mode 100644 index 83e39041a99b9..0000000000000 --- a/fboss/cli/fboss2/commands/config/protocol/bgp/peer-group/CmdConfigProtocolBgpPeerGroupNextHopSelf.h +++ /dev/null @@ -1,46 +0,0 @@ -/* - * 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/config/protocol/bgp/peer-group/CmdConfigProtocolBgpPeerGroup.h" -#include "fboss/cli/fboss2/utils/CmdUtils.h" - -namespace facebook::fboss { - -struct CmdConfigProtocolBgpPeerGroupNextHopSelfTraits - : public WriteCommandTraits { - using ParentCmd = CmdConfigProtocolBgpPeerGroup; - static void addCliArg(CLI::App& cmd, std::vector& args) { - cmd.add_option("msg", args, "Message"); - } - using ObjectArgType = utils::Message; - using RetType = std::string; -}; - -class CmdConfigProtocolBgpPeerGroupNextHopSelf - : public CmdHandler< - CmdConfigProtocolBgpPeerGroupNextHopSelf, - CmdConfigProtocolBgpPeerGroupNextHopSelfTraits> { - public: - using ObjectArgType = - CmdConfigProtocolBgpPeerGroupNextHopSelfTraits::ObjectArgType; - using RetType = CmdConfigProtocolBgpPeerGroupNextHopSelfTraits::RetType; - - RetType queryClient( - const HostInfo& hostInfo, - const utils::Message& peerGroupName, - const ObjectArgType& enable); - - void printOutput(const RetType& output); -}; - -} // namespace facebook::fboss diff --git a/fboss/cli/fboss2/commands/config/protocol/bgp/peer-group/CmdConfigProtocolBgpPeerGroupPeerTag.cpp b/fboss/cli/fboss2/commands/config/protocol/bgp/peer-group/CmdConfigProtocolBgpPeerGroupPeerTag.cpp deleted file mode 100644 index 1fbb6a228e603..0000000000000 --- a/fboss/cli/fboss2/commands/config/protocol/bgp/peer-group/CmdConfigProtocolBgpPeerGroupPeerTag.cpp +++ /dev/null @@ -1,58 +0,0 @@ -/* - * Copyright (c) 2004-present, Facebook, Inc. - * All rights reserved. - * - * This source code is licensed under the BSD-style license found in the - * LICENSE file in the root directory of this source tree. An additional grant - * of patent rights can be found in the PATENTS file in the same directory. - * - */ - -#include "fboss/cli/fboss2/commands/config/protocol/bgp/peer-group/CmdConfigProtocolBgpPeerGroupPeerTag.h" - -#include "fboss/cli/fboss2/CmdHandler.cpp" - -#include -#include "fboss/cli/fboss2/commands/config/protocol/bgp/BgpConfigSession.h" - -namespace facebook::fboss { - -CmdConfigProtocolBgpPeerGroupPeerTagTraits::RetType -CmdConfigProtocolBgpPeerGroupPeerTag::queryClient( - const HostInfo& /* hostInfo */, - const utils::Message& groupNameArg, - const ObjectArgType& peerTagMsg) { - auto& session = BgpConfigSession::getInstance(); - - if (groupNameArg.data().empty()) { - return "Error: peer-group name is required"; - } - std::string groupName = groupNameArg.data()[0]; - - if (peerTagMsg.data().empty()) { - return fmt::format( - "Error: peer-tag value is required for peer-group '{}'", groupName); - } - - session.createPeerGroup(groupName); - session.setPeerGroupPeerTag(groupName, peerTagMsg.data()[0]); - session.saveConfig(); - - return fmt::format( - "Successfully set peer_tag for peer-group '{}' to: '{}'\n" - "Config saved to: {}", - groupName, - peerTagMsg.data()[0], - session.getSessionConfigPath()); -} - -void CmdConfigProtocolBgpPeerGroupPeerTag::printOutput(const RetType& output) { - std::cout << output << std::endl; -} - -// Explicit template instantiation -template void CmdHandler< - CmdConfigProtocolBgpPeerGroupPeerTag, - CmdConfigProtocolBgpPeerGroupPeerTagTraits>::run(); - -} // namespace facebook::fboss diff --git a/fboss/cli/fboss2/commands/config/protocol/bgp/peer-group/CmdConfigProtocolBgpPeerGroupPeerTag.h b/fboss/cli/fboss2/commands/config/protocol/bgp/peer-group/CmdConfigProtocolBgpPeerGroupPeerTag.h deleted file mode 100644 index 4715f131fe8b8..0000000000000 --- a/fboss/cli/fboss2/commands/config/protocol/bgp/peer-group/CmdConfigProtocolBgpPeerGroupPeerTag.h +++ /dev/null @@ -1,45 +0,0 @@ -/* - * 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/config/protocol/bgp/peer-group/CmdConfigProtocolBgpPeerGroup.h" -#include "fboss/cli/fboss2/utils/CmdUtils.h" - -namespace facebook::fboss { - -struct CmdConfigProtocolBgpPeerGroupPeerTagTraits : public WriteCommandTraits { - using ParentCmd = CmdConfigProtocolBgpPeerGroup; - static void addCliArg(CLI::App& cmd, std::vector& args) { - cmd.add_option("msg", args, "Message"); - } - using ObjectArgType = utils::Message; - using RetType = std::string; -}; - -class CmdConfigProtocolBgpPeerGroupPeerTag - : public CmdHandler< - CmdConfigProtocolBgpPeerGroupPeerTag, - CmdConfigProtocolBgpPeerGroupPeerTagTraits> { - public: - using ObjectArgType = - CmdConfigProtocolBgpPeerGroupPeerTagTraits::ObjectArgType; - using RetType = CmdConfigProtocolBgpPeerGroupPeerTagTraits::RetType; - - RetType queryClient( - const HostInfo& hostInfo, - const utils::Message& groupName, - const ObjectArgType& peerTag); - - void printOutput(const RetType& output); -}; - -} // namespace facebook::fboss diff --git a/fboss/cli/fboss2/commands/config/protocol/bgp/peer-group/CmdConfigProtocolBgpPeerGroupRemoteAsn.cpp b/fboss/cli/fboss2/commands/config/protocol/bgp/peer-group/CmdConfigProtocolBgpPeerGroupRemoteAsn.cpp deleted file mode 100644 index ea967b32e0e2d..0000000000000 --- a/fboss/cli/fboss2/commands/config/protocol/bgp/peer-group/CmdConfigProtocolBgpPeerGroupRemoteAsn.cpp +++ /dev/null @@ -1,64 +0,0 @@ -/* - * Copyright (c) 2004-present, Facebook, Inc. - * All rights reserved. - * - * This source code is licensed under the BSD-style license found in the - * LICENSE file in the root directory of this source tree. An additional grant - * of patent rights can be found in the PATENTS file in the same directory. - * - */ - -#include "fboss/cli/fboss2/commands/config/protocol/bgp/peer-group/CmdConfigProtocolBgpPeerGroupRemoteAsn.h" - -#include "fboss/cli/fboss2/CmdHandler.cpp" - -#include -#include -#include "fboss/cli/fboss2/commands/config/protocol/bgp/BgpConfigSession.h" - -namespace facebook::fboss { - -CmdConfigProtocolBgpPeerGroupRemoteAsnTraits::RetType -CmdConfigProtocolBgpPeerGroupRemoteAsn::queryClient( - const HostInfo& /* hostInfo */, - const utils::Message& groupNameMsg, - const ObjectArgType& remoteAsnMsg) { - auto& session = BgpConfigSession::getInstance(); - - if (groupNameMsg.data().empty()) { - return "Error: peer-group name is required"; - } - if (remoteAsnMsg.data().empty()) { - return "Error: remote-asn value is required"; - } - - std::string groupName = groupNameMsg.data()[0]; - std::string asnStr = remoteAsnMsg.data()[0]; - uint64_t remoteAsn; - try { - remoteAsn = folly::to(asnStr); - } catch (const std::exception& e) { - return fmt::format("Error: Invalid remote-asn '{}': {}", asnStr, e.what()); - } - - session.setPeerGroupRemoteAsn(groupName, remoteAsn); - session.saveConfig(); - - return fmt::format( - "Successfully set peer-group {} remote-asn to: {}\nConfig saved to: {}", - groupName, - remoteAsn, - session.getSessionConfigPath()); -} - -void CmdConfigProtocolBgpPeerGroupRemoteAsn::printOutput( - const RetType& output) { - std::cout << output << std::endl; -} - -// Explicit template instantiation -template void CmdHandler< - CmdConfigProtocolBgpPeerGroupRemoteAsn, - CmdConfigProtocolBgpPeerGroupRemoteAsnTraits>::run(); - -} // namespace facebook::fboss diff --git a/fboss/cli/fboss2/commands/config/protocol/bgp/peer-group/CmdConfigProtocolBgpPeerGroupRemoteAsn.h b/fboss/cli/fboss2/commands/config/protocol/bgp/peer-group/CmdConfigProtocolBgpPeerGroupRemoteAsn.h deleted file mode 100644 index 7f7c79898d233..0000000000000 --- a/fboss/cli/fboss2/commands/config/protocol/bgp/peer-group/CmdConfigProtocolBgpPeerGroupRemoteAsn.h +++ /dev/null @@ -1,46 +0,0 @@ -/* - * 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/config/protocol/bgp/peer-group/CmdConfigProtocolBgpPeerGroup.h" -#include "fboss/cli/fboss2/utils/CmdUtils.h" - -namespace facebook::fboss { - -struct CmdConfigProtocolBgpPeerGroupRemoteAsnTraits - : public WriteCommandTraits { - using ParentCmd = CmdConfigProtocolBgpPeerGroup; - static void addCliArg(CLI::App& cmd, std::vector& args) { - cmd.add_option("msg", args, "Message"); - } - using ObjectArgType = utils::Message; - using RetType = std::string; -}; - -class CmdConfigProtocolBgpPeerGroupRemoteAsn - : public CmdHandler< - CmdConfigProtocolBgpPeerGroupRemoteAsn, - CmdConfigProtocolBgpPeerGroupRemoteAsnTraits> { - public: - using ObjectArgType = - CmdConfigProtocolBgpPeerGroupRemoteAsnTraits::ObjectArgType; - using RetType = CmdConfigProtocolBgpPeerGroupRemoteAsnTraits::RetType; - - RetType queryClient( - const HostInfo& hostInfo, - const utils::Message& groupName, - const ObjectArgType& remoteAsn); - - void printOutput(const RetType& output); -}; - -} // namespace facebook::fboss diff --git a/fboss/cli/fboss2/commands/config/protocol/bgp/peer-group/CmdConfigProtocolBgpPeerGroupRrClient.cpp b/fboss/cli/fboss2/commands/config/protocol/bgp/peer-group/CmdConfigProtocolBgpPeerGroupRrClient.cpp deleted file mode 100644 index e6cff47df37bb..0000000000000 --- a/fboss/cli/fboss2/commands/config/protocol/bgp/peer-group/CmdConfigProtocolBgpPeerGroupRrClient.cpp +++ /dev/null @@ -1,62 +0,0 @@ -/* - * Copyright (c) 2004-present, Facebook, Inc. - * All rights reserved. - * - * This source code is licensed under the BSD-style license found in the - * LICENSE file in the root directory of this source tree. An additional grant - * of patent rights can be found in the PATENTS file in the same directory. - * - */ - -#include "fboss/cli/fboss2/commands/config/protocol/bgp/peer-group/CmdConfigProtocolBgpPeerGroupRrClient.h" - -#include "fboss/cli/fboss2/CmdHandler.cpp" - -#include -#include "fboss/cli/fboss2/commands/config/protocol/bgp/BgpConfigSession.h" - -namespace facebook::fboss { - -CmdConfigProtocolBgpPeerGroupRrClientTraits::RetType -CmdConfigProtocolBgpPeerGroupRrClient::queryClient( - const HostInfo& /* hostInfo */, - const utils::Message& peerGroupNameMsg, - const ObjectArgType& enableMsg) { - auto& session = BgpConfigSession::getInstance(); - - if (peerGroupNameMsg.data().empty()) { - return "Error: peer-group name is required"; - } - std::string peerGroupName = peerGroupNameMsg.data()[0]; - - if (enableMsg.data().empty()) { - return fmt::format( - "Error: enable value (true/false) is required for peer-group '{}'", - peerGroupName); - } - - std::string enableStr = enableMsg.data()[0]; - bool enable = (enableStr == "true" || enableStr == "1" || enableStr == "yes"); - - session.createPeerGroup(peerGroupName); - session.setPeerGroupRrClient(peerGroupName, enable); - session.saveConfig(); - - return fmt::format( - "Successfully {} route-reflector-client for peer-group '{}'\n" - "Config saved to: {}", - enable ? "enabled" : "disabled", - peerGroupName, - session.getSessionConfigPath()); -} - -void CmdConfigProtocolBgpPeerGroupRrClient::printOutput(const RetType& output) { - std::cout << output << std::endl; -} - -// Explicit template instantiation -template void CmdHandler< - CmdConfigProtocolBgpPeerGroupRrClient, - CmdConfigProtocolBgpPeerGroupRrClientTraits>::run(); - -} // namespace facebook::fboss diff --git a/fboss/cli/fboss2/commands/config/protocol/bgp/peer-group/CmdConfigProtocolBgpPeerGroupRrClient.h b/fboss/cli/fboss2/commands/config/protocol/bgp/peer-group/CmdConfigProtocolBgpPeerGroupRrClient.h deleted file mode 100644 index c98cac2f2f97c..0000000000000 --- a/fboss/cli/fboss2/commands/config/protocol/bgp/peer-group/CmdConfigProtocolBgpPeerGroupRrClient.h +++ /dev/null @@ -1,45 +0,0 @@ -/* - * 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/config/protocol/bgp/peer-group/CmdConfigProtocolBgpPeerGroup.h" -#include "fboss/cli/fboss2/utils/CmdUtils.h" - -namespace facebook::fboss { - -struct CmdConfigProtocolBgpPeerGroupRrClientTraits : public WriteCommandTraits { - using ParentCmd = CmdConfigProtocolBgpPeerGroup; - static void addCliArg(CLI::App& cmd, std::vector& args) { - cmd.add_option("msg", args, "Message"); - } - using ObjectArgType = utils::Message; - using RetType = std::string; -}; - -class CmdConfigProtocolBgpPeerGroupRrClient - : public CmdHandler< - CmdConfigProtocolBgpPeerGroupRrClient, - CmdConfigProtocolBgpPeerGroupRrClientTraits> { - public: - using ObjectArgType = - CmdConfigProtocolBgpPeerGroupRrClientTraits::ObjectArgType; - using RetType = CmdConfigProtocolBgpPeerGroupRrClientTraits::RetType; - - RetType queryClient( - const HostInfo& hostInfo, - const utils::Message& peerGroupName, - const ObjectArgType& enable); - - void printOutput(const RetType& output); -}; - -} // namespace facebook::fboss diff --git a/fboss/cli/fboss2/commands/config/protocol/bgp/peer-group/CmdConfigProtocolBgpPeerGroupTimers.cpp b/fboss/cli/fboss2/commands/config/protocol/bgp/peer-group/CmdConfigProtocolBgpPeerGroupTimers.cpp deleted file mode 100644 index f1ebe72da4592..0000000000000 --- a/fboss/cli/fboss2/commands/config/protocol/bgp/peer-group/CmdConfigProtocolBgpPeerGroupTimers.cpp +++ /dev/null @@ -1,44 +0,0 @@ -/* - * Copyright (c) 2004-present, Facebook, Inc. - * All rights reserved. - * - * This source code is licensed under the BSD-style license found in the - * LICENSE file in the root directory of this source tree. An additional grant - * of patent rights can be found in the PATENTS file in the same directory. - * - */ - -#include "fboss/cli/fboss2/commands/config/protocol/bgp/peer-group/CmdConfigProtocolBgpPeerGroupTimers.h" - -#include "fboss/cli/fboss2/CmdHandler.cpp" - -#include - -namespace facebook::fboss { - -CmdConfigProtocolBgpPeerGroupTimersTraits::RetType -CmdConfigProtocolBgpPeerGroupTimers::queryClient( - const HostInfo& /* hostInfo */, - const utils::Message& peerGroupNameMsg) { - if (peerGroupNameMsg.data().empty()) { - return "Error: peer-group name is required"; - } - - std::string groupName = peerGroupNameMsg.data()[0]; - - return fmt::format( - "Configure timers for peer-group {}.\n" - "Use subcommands: hold-time, keepalive, out-delay", - groupName); -} - -void CmdConfigProtocolBgpPeerGroupTimers::printOutput(const RetType& output) { - std::cout << output << std::endl; -} - -// Explicit template instantiation -template void CmdHandler< - CmdConfigProtocolBgpPeerGroupTimers, - CmdConfigProtocolBgpPeerGroupTimersTraits>::run(); - -} // namespace facebook::fboss diff --git a/fboss/cli/fboss2/commands/config/protocol/bgp/peer-group/CmdConfigProtocolBgpPeerGroupTimers.h b/fboss/cli/fboss2/commands/config/protocol/bgp/peer-group/CmdConfigProtocolBgpPeerGroupTimers.h deleted file mode 100644 index 2b457c60909f3..0000000000000 --- a/fboss/cli/fboss2/commands/config/protocol/bgp/peer-group/CmdConfigProtocolBgpPeerGroupTimers.h +++ /dev/null @@ -1,41 +0,0 @@ -/* - * 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/config/protocol/bgp/peer-group/CmdConfigProtocolBgpPeerGroup.h" -#include "fboss/cli/fboss2/utils/CmdUtils.h" - -namespace facebook::fboss { - -struct CmdConfigProtocolBgpPeerGroupTimersTraits : public WriteCommandTraits { - using ParentCmd = CmdConfigProtocolBgpPeerGroup; - using ObjectArgType = std::monostate; - using RetType = std::string; -}; - -class CmdConfigProtocolBgpPeerGroupTimers - : public CmdHandler< - CmdConfigProtocolBgpPeerGroupTimers, - CmdConfigProtocolBgpPeerGroupTimersTraits> { - public: - using ObjectArgType = - CmdConfigProtocolBgpPeerGroupTimersTraits::ObjectArgType; - using RetType = CmdConfigProtocolBgpPeerGroupTimersTraits::RetType; - - RetType queryClient( - const HostInfo& hostInfo, - const utils::Message& peerGroupName); - - void printOutput(const RetType& output); -}; - -} // namespace facebook::fboss diff --git a/fboss/cli/fboss2/commands/config/protocol/bgp/peer-group/CmdConfigProtocolBgpPeerGroupTimersHoldTime.cpp b/fboss/cli/fboss2/commands/config/protocol/bgp/peer-group/CmdConfigProtocolBgpPeerGroupTimersHoldTime.cpp deleted file mode 100644 index 0e5b83da36dd2..0000000000000 --- a/fboss/cli/fboss2/commands/config/protocol/bgp/peer-group/CmdConfigProtocolBgpPeerGroupTimersHoldTime.cpp +++ /dev/null @@ -1,65 +0,0 @@ -/* - * Copyright (c) 2004-present, Facebook, Inc. - * All rights reserved. - * - * This source code is licensed under the BSD-style license found in the - * LICENSE file in the root directory of this source tree. An additional grant - * of patent rights can be found in the PATENTS file in the same directory. - * - */ - -#include "fboss/cli/fboss2/commands/config/protocol/bgp/peer-group/CmdConfigProtocolBgpPeerGroupTimersHoldTime.h" - -#include "fboss/cli/fboss2/CmdHandler.cpp" - -#include -#include -#include "fboss/cli/fboss2/commands/config/protocol/bgp/BgpConfigSession.h" - -namespace facebook::fboss { - -CmdConfigProtocolBgpPeerGroupTimersHoldTimeTraits::RetType -CmdConfigProtocolBgpPeerGroupTimersHoldTime::queryClient( - const HostInfo& /* hostInfo */, - const utils::Message& peerGroupNameMsg, - const ObjectArgType& holdTimeMsg) { - auto& session = BgpConfigSession::getInstance(); - - if (peerGroupNameMsg.data().empty()) { - return "Error: peer-group name is required"; - } - if (holdTimeMsg.data().empty()) { - return "Error: hold-time value is required"; - } - - std::string groupName = peerGroupNameMsg.data()[0]; - std::string holdTimeStr = holdTimeMsg.data()[0]; - int32_t holdTime; - try { - holdTime = folly::to(holdTimeStr); - } catch (const std::exception& e) { - return fmt::format( - "Error: Invalid hold-time '{}': {}", holdTimeStr, e.what()); - } - - session.setPeerGroupHoldTime(groupName, holdTime); - session.saveConfig(); - - return fmt::format( - "Successfully set peer-group {} hold-time to: {} seconds\nConfig saved to: {}", - groupName, - holdTime, - session.getSessionConfigPath()); -} - -void CmdConfigProtocolBgpPeerGroupTimersHoldTime::printOutput( - const RetType& output) { - std::cout << output << std::endl; -} - -// Explicit template instantiation -template void CmdHandler< - CmdConfigProtocolBgpPeerGroupTimersHoldTime, - CmdConfigProtocolBgpPeerGroupTimersHoldTimeTraits>::run(); - -} // namespace facebook::fboss diff --git a/fboss/cli/fboss2/commands/config/protocol/bgp/peer-group/CmdConfigProtocolBgpPeerGroupTimersHoldTime.h b/fboss/cli/fboss2/commands/config/protocol/bgp/peer-group/CmdConfigProtocolBgpPeerGroupTimersHoldTime.h deleted file mode 100644 index 21351570d9400..0000000000000 --- a/fboss/cli/fboss2/commands/config/protocol/bgp/peer-group/CmdConfigProtocolBgpPeerGroupTimersHoldTime.h +++ /dev/null @@ -1,46 +0,0 @@ -/* - * 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/config/protocol/bgp/peer-group/CmdConfigProtocolBgpPeerGroupTimers.h" -#include "fboss/cli/fboss2/utils/CmdUtils.h" - -namespace facebook::fboss { - -struct CmdConfigProtocolBgpPeerGroupTimersHoldTimeTraits - : public WriteCommandTraits { - using ParentCmd = CmdConfigProtocolBgpPeerGroupTimers; - static void addCliArg(CLI::App& cmd, std::vector& args) { - cmd.add_option("msg", args, "Message"); - } - using ObjectArgType = utils::Message; - using RetType = std::string; -}; - -class CmdConfigProtocolBgpPeerGroupTimersHoldTime - : public CmdHandler< - CmdConfigProtocolBgpPeerGroupTimersHoldTime, - CmdConfigProtocolBgpPeerGroupTimersHoldTimeTraits> { - public: - using ObjectArgType = - CmdConfigProtocolBgpPeerGroupTimersHoldTimeTraits::ObjectArgType; - using RetType = CmdConfigProtocolBgpPeerGroupTimersHoldTimeTraits::RetType; - - RetType queryClient( - const HostInfo& hostInfo, - const utils::Message& peerGroupName, - const ObjectArgType& holdTime); - - void printOutput(const RetType& output); -}; - -} // namespace facebook::fboss diff --git a/fboss/cli/fboss2/commands/config/protocol/bgp/peer-group/CmdConfigProtocolBgpPeerGroupTimersKeepalive.cpp b/fboss/cli/fboss2/commands/config/protocol/bgp/peer-group/CmdConfigProtocolBgpPeerGroupTimersKeepalive.cpp deleted file mode 100644 index 7038f9a0e2c50..0000000000000 --- a/fboss/cli/fboss2/commands/config/protocol/bgp/peer-group/CmdConfigProtocolBgpPeerGroupTimersKeepalive.cpp +++ /dev/null @@ -1,71 +0,0 @@ -/* - * Copyright (c) 2004-present, Facebook, Inc. - * All rights reserved. - * - * This source code is licensed under the BSD-style license found in the - * LICENSE file in the root directory of this source tree. An additional grant - * of patent rights can be found in the PATENTS file in the same directory. - * - */ - -#include "fboss/cli/fboss2/commands/config/protocol/bgp/peer-group/CmdConfigProtocolBgpPeerGroupTimersKeepalive.h" - -#include "fboss/cli/fboss2/CmdHandler.cpp" - -#include -#include -#include "fboss/cli/fboss2/commands/config/protocol/bgp/BgpConfigSession.h" - -namespace facebook::fboss { - -CmdConfigProtocolBgpPeerGroupTimersKeepaliveTraits::RetType -CmdConfigProtocolBgpPeerGroupTimersKeepalive::queryClient( - const HostInfo& /* hostInfo */, - const utils::Message& peerGroupNameMsg, - const ObjectArgType& keepaliveMsg) { - auto& session = BgpConfigSession::getInstance(); - - if (peerGroupNameMsg.data().empty()) { - return "Error: peer-group name is required"; - } - std::string peerGroupName = peerGroupNameMsg.data()[0]; - - if (keepaliveMsg.data().empty()) { - return fmt::format( - "Error: keepalive interval is required for peer-group '{}'", - peerGroupName); - } - - int32_t keepalive; - try { - keepalive = folly::to(keepaliveMsg.data()[0]); - } catch (const std::exception& e) { - return fmt::format( - "Error: invalid keepalive value '{}': {}", - keepaliveMsg.data()[0], - e.what()); - } - - session.createPeerGroup(peerGroupName); - session.setPeerGroupKeepalive(peerGroupName, keepalive); - session.saveConfig(); - - return fmt::format( - "Successfully set keepalive interval for peer-group '{}' to: {} seconds\n" - "Config saved to: {}", - peerGroupName, - keepalive, - session.getSessionConfigPath()); -} - -void CmdConfigProtocolBgpPeerGroupTimersKeepalive::printOutput( - const RetType& output) { - std::cout << output << std::endl; -} - -// Explicit template instantiation -template void CmdHandler< - CmdConfigProtocolBgpPeerGroupTimersKeepalive, - CmdConfigProtocolBgpPeerGroupTimersKeepaliveTraits>::run(); - -} // namespace facebook::fboss diff --git a/fboss/cli/fboss2/commands/config/protocol/bgp/peer-group/CmdConfigProtocolBgpPeerGroupTimersKeepalive.h b/fboss/cli/fboss2/commands/config/protocol/bgp/peer-group/CmdConfigProtocolBgpPeerGroupTimersKeepalive.h deleted file mode 100644 index c2088385b391d..0000000000000 --- a/fboss/cli/fboss2/commands/config/protocol/bgp/peer-group/CmdConfigProtocolBgpPeerGroupTimersKeepalive.h +++ /dev/null @@ -1,46 +0,0 @@ -/* - * 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/config/protocol/bgp/peer-group/CmdConfigProtocolBgpPeerGroupTimers.h" -#include "fboss/cli/fboss2/utils/CmdUtils.h" - -namespace facebook::fboss { - -struct CmdConfigProtocolBgpPeerGroupTimersKeepaliveTraits - : public WriteCommandTraits { - using ParentCmd = CmdConfigProtocolBgpPeerGroupTimers; - static void addCliArg(CLI::App& cmd, std::vector& args) { - cmd.add_option("msg", args, "Message"); - } - using ObjectArgType = utils::Message; - using RetType = std::string; -}; - -class CmdConfigProtocolBgpPeerGroupTimersKeepalive - : public CmdHandler< - CmdConfigProtocolBgpPeerGroupTimersKeepalive, - CmdConfigProtocolBgpPeerGroupTimersKeepaliveTraits> { - public: - using ObjectArgType = - CmdConfigProtocolBgpPeerGroupTimersKeepaliveTraits::ObjectArgType; - using RetType = CmdConfigProtocolBgpPeerGroupTimersKeepaliveTraits::RetType; - - RetType queryClient( - const HostInfo& hostInfo, - const utils::Message& peerGroupName, - const ObjectArgType& keepalive); - - void printOutput(const RetType& output); -}; - -} // namespace facebook::fboss diff --git a/fboss/cli/fboss2/commands/config/protocol/bgp/peer-group/CmdConfigProtocolBgpPeerGroupTimersOutDelay.cpp b/fboss/cli/fboss2/commands/config/protocol/bgp/peer-group/CmdConfigProtocolBgpPeerGroupTimersOutDelay.cpp deleted file mode 100644 index 21e4dc4c47eeb..0000000000000 --- a/fboss/cli/fboss2/commands/config/protocol/bgp/peer-group/CmdConfigProtocolBgpPeerGroupTimersOutDelay.cpp +++ /dev/null @@ -1,71 +0,0 @@ -/* - * Copyright (c) 2004-present, Facebook, Inc. - * All rights reserved. - * - * This source code is licensed under the BSD-style license found in the - * LICENSE file in the root directory of this source tree. An additional grant - * of patent rights can be found in the PATENTS file in the same directory. - * - */ - -#include "fboss/cli/fboss2/commands/config/protocol/bgp/peer-group/CmdConfigProtocolBgpPeerGroupTimersOutDelay.h" - -#include "fboss/cli/fboss2/CmdHandler.cpp" - -#include -#include -#include "fboss/cli/fboss2/commands/config/protocol/bgp/BgpConfigSession.h" - -namespace facebook::fboss { - -CmdConfigProtocolBgpPeerGroupTimersOutDelayTraits::RetType -CmdConfigProtocolBgpPeerGroupTimersOutDelay::queryClient( - const HostInfo& /* hostInfo */, - const utils::Message& peerGroupNameMsg, - const ObjectArgType& outDelayMsg) { - auto& session = BgpConfigSession::getInstance(); - - if (peerGroupNameMsg.data().empty()) { - return "Error: peer-group name is required"; - } - std::string peerGroupName = peerGroupNameMsg.data()[0]; - - if (outDelayMsg.data().empty()) { - return fmt::format( - "Error: out-delay value is required for peer-group '{}'", - peerGroupName); - } - - int32_t outDelay; - try { - outDelay = folly::to(outDelayMsg.data()[0]); - } catch (const std::exception& e) { - return fmt::format( - "Error: invalid out-delay value '{}': {}", - outDelayMsg.data()[0], - e.what()); - } - - session.createPeerGroup(peerGroupName); - session.setPeerGroupOutDelay(peerGroupName, outDelay); - session.saveConfig(); - - return fmt::format( - "Successfully set out-delay for peer-group '{}' to: {} seconds\n" - "Config saved to: {}", - peerGroupName, - outDelay, - session.getSessionConfigPath()); -} - -void CmdConfigProtocolBgpPeerGroupTimersOutDelay::printOutput( - const RetType& output) { - std::cout << output << std::endl; -} - -// Explicit template instantiation -template void CmdHandler< - CmdConfigProtocolBgpPeerGroupTimersOutDelay, - CmdConfigProtocolBgpPeerGroupTimersOutDelayTraits>::run(); - -} // namespace facebook::fboss diff --git a/fboss/cli/fboss2/commands/config/protocol/bgp/peer-group/CmdConfigProtocolBgpPeerGroupTimersOutDelay.h b/fboss/cli/fboss2/commands/config/protocol/bgp/peer-group/CmdConfigProtocolBgpPeerGroupTimersOutDelay.h deleted file mode 100644 index 16542ecc3b225..0000000000000 --- a/fboss/cli/fboss2/commands/config/protocol/bgp/peer-group/CmdConfigProtocolBgpPeerGroupTimersOutDelay.h +++ /dev/null @@ -1,46 +0,0 @@ -/* - * 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/config/protocol/bgp/peer-group/CmdConfigProtocolBgpPeerGroupTimers.h" -#include "fboss/cli/fboss2/utils/CmdUtils.h" - -namespace facebook::fboss { - -struct CmdConfigProtocolBgpPeerGroupTimersOutDelayTraits - : public WriteCommandTraits { - using ParentCmd = CmdConfigProtocolBgpPeerGroupTimers; - static void addCliArg(CLI::App& cmd, std::vector& args) { - cmd.add_option("msg", args, "Message"); - } - using ObjectArgType = utils::Message; - using RetType = std::string; -}; - -class CmdConfigProtocolBgpPeerGroupTimersOutDelay - : public CmdHandler< - CmdConfigProtocolBgpPeerGroupTimersOutDelay, - CmdConfigProtocolBgpPeerGroupTimersOutDelayTraits> { - public: - using ObjectArgType = - CmdConfigProtocolBgpPeerGroupTimersOutDelayTraits::ObjectArgType; - using RetType = CmdConfigProtocolBgpPeerGroupTimersOutDelayTraits::RetType; - - RetType queryClient( - const HostInfo& hostInfo, - const utils::Message& peerGroupName, - const ObjectArgType& outDelay); - - void printOutput(const RetType& output); -}; - -} // namespace facebook::fboss diff --git a/fboss/cli/fboss2/commands/config/protocol/bgp/peer-group/CmdConfigProtocolBgpPeerGroupTimersWithdrawUnprogDelay.cpp b/fboss/cli/fboss2/commands/config/protocol/bgp/peer-group/CmdConfigProtocolBgpPeerGroupTimersWithdrawUnprogDelay.cpp deleted file mode 100644 index 92ccb97ff4a69..0000000000000 --- a/fboss/cli/fboss2/commands/config/protocol/bgp/peer-group/CmdConfigProtocolBgpPeerGroupTimersWithdrawUnprogDelay.cpp +++ /dev/null @@ -1,70 +0,0 @@ -/* - * Copyright (c) 2004-present, Facebook, Inc. - * All rights reserved. - * - * This source code is licensed under the BSD-style license found in the - * LICENSE file in the root directory of this source tree. An additional grant - * of patent rights can be found in the PATENTS file in the same directory. - * - */ - -#include "fboss/cli/fboss2/commands/config/protocol/bgp/peer-group/CmdConfigProtocolBgpPeerGroupTimersWithdrawUnprogDelay.h" - -#include "fboss/cli/fboss2/CmdHandler.cpp" - -#include -#include "fboss/cli/fboss2/commands/config/protocol/bgp/BgpConfigSession.h" - -namespace facebook::fboss { - -CmdConfigProtocolBgpPeerGroupTimersWithdrawUnprogDelayTraits::RetType -CmdConfigProtocolBgpPeerGroupTimersWithdrawUnprogDelay::queryClient( - const HostInfo& /* hostInfo */, - const utils::Message& groupNameArg, - const ObjectArgType& delayMsg) { - auto& session = BgpConfigSession::getInstance(); - - if (groupNameArg.data().empty()) { - return "Error: peer-group name is required"; - } - std::string groupName = groupNameArg.data()[0]; - - if (delayMsg.data().empty()) { - return fmt::format( - "Error: withdraw-unprog-delay value (seconds) is required for peer-group '{}'", - groupName); - } - - int32_t delay; - try { - delay = std::stoi(delayMsg.data()[0]); - } catch (const std::exception& e) { - return fmt::format( - "Error: invalid withdraw-unprog-delay value '{}': {}", - delayMsg.data()[0], - e.what()); - } - - session.createPeerGroup(groupName); - session.setPeerGroupWithdrawUnprogDelay(groupName, delay); - session.saveConfig(); - - return fmt::format( - "Successfully set withdraw-unprog-delay for peer-group '{}' to: {} seconds\n" - "Config saved to: {}", - groupName, - delay, - session.getSessionConfigPath()); -} - -void CmdConfigProtocolBgpPeerGroupTimersWithdrawUnprogDelay::printOutput( - const RetType& output) { - std::cout << output << std::endl; -} - -// Explicit template instantiation -template void CmdHandler< - CmdConfigProtocolBgpPeerGroupTimersWithdrawUnprogDelay, - CmdConfigProtocolBgpPeerGroupTimersWithdrawUnprogDelayTraits>::run(); - -} // namespace facebook::fboss diff --git a/fboss/cli/fboss2/commands/config/protocol/bgp/peer-group/CmdConfigProtocolBgpPeerGroupTimersWithdrawUnprogDelay.h b/fboss/cli/fboss2/commands/config/protocol/bgp/peer-group/CmdConfigProtocolBgpPeerGroupTimersWithdrawUnprogDelay.h deleted file mode 100644 index 83355fc1b1e96..0000000000000 --- a/fboss/cli/fboss2/commands/config/protocol/bgp/peer-group/CmdConfigProtocolBgpPeerGroupTimersWithdrawUnprogDelay.h +++ /dev/null @@ -1,48 +0,0 @@ -/* - * 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/config/protocol/bgp/peer-group/CmdConfigProtocolBgpPeerGroupTimers.h" -#include "fboss/cli/fboss2/utils/CmdUtils.h" - -namespace facebook::fboss { - -struct CmdConfigProtocolBgpPeerGroupTimersWithdrawUnprogDelayTraits - : public WriteCommandTraits { - using ParentCmd = CmdConfigProtocolBgpPeerGroupTimers; - static void addCliArg(CLI::App& cmd, std::vector& args) { - cmd.add_option("msg", args, "Message"); - } - using ObjectArgType = utils::Message; - using RetType = std::string; -}; - -class CmdConfigProtocolBgpPeerGroupTimersWithdrawUnprogDelay - : public CmdHandler< - CmdConfigProtocolBgpPeerGroupTimersWithdrawUnprogDelay, - CmdConfigProtocolBgpPeerGroupTimersWithdrawUnprogDelayTraits> { - public: - using ObjectArgType = - CmdConfigProtocolBgpPeerGroupTimersWithdrawUnprogDelayTraits:: - ObjectArgType; - using RetType = - CmdConfigProtocolBgpPeerGroupTimersWithdrawUnprogDelayTraits::RetType; - - RetType queryClient( - const HostInfo& hostInfo, - const utils::Message& peerGroupName, - const ObjectArgType& withdrawUnprogDelay); - - void printOutput(const RetType& output); -}; - -} // namespace facebook::fboss diff --git a/fboss/cli/fboss2/commands/config/protocol/bgp/peer-group/CmdConfigProtocolBgpPeerGroupV4OverV6Nh.cpp b/fboss/cli/fboss2/commands/config/protocol/bgp/peer-group/CmdConfigProtocolBgpPeerGroupV4OverV6Nh.cpp deleted file mode 100644 index ae03372ca1686..0000000000000 --- a/fboss/cli/fboss2/commands/config/protocol/bgp/peer-group/CmdConfigProtocolBgpPeerGroupV4OverV6Nh.cpp +++ /dev/null @@ -1,61 +0,0 @@ -/* - * Copyright (c) 2004-present, Facebook, Inc. - * All rights reserved. - * - * This source code is licensed under the BSD-style license found in the - * LICENSE file in the root directory of this source tree. An additional grant - * of patent rights can be found in the PATENTS file in the same directory. - * - */ - -#include "fboss/cli/fboss2/commands/config/protocol/bgp/peer-group/CmdConfigProtocolBgpPeerGroupV4OverV6Nh.h" - -#include "fboss/cli/fboss2/CmdHandler.cpp" - -#include -#include "fboss/cli/fboss2/commands/config/protocol/bgp/BgpConfigSession.h" - -namespace facebook::fboss { - -CmdConfigProtocolBgpPeerGroupV4OverV6NhTraits::RetType -CmdConfigProtocolBgpPeerGroupV4OverV6Nh::queryClient( - const HostInfo& /* hostInfo */, - const utils::Message& peerGroupNameMsg, - const ObjectArgType& enableMsg) { - auto& session = BgpConfigSession::getInstance(); - - if (peerGroupNameMsg.data().empty()) { - return "Error: peer-group name is required"; - } - std::string peerGroupName = peerGroupNameMsg.data()[0]; - - if (enableMsg.data().empty()) { - return fmt::format( - "Error: enable value required for peer-group '{}'", peerGroupName); - } - std::string enableStr = enableMsg.data()[0]; - bool enable = (enableStr == "true" || enableStr == "1" || enableStr == "yes"); - - session.createPeerGroup(peerGroupName); - session.setPeerGroupIpv4OverIpv6Nh(peerGroupName, enable); - session.saveConfig(); - - return fmt::format( - "Successfully {} v4-over-v6-nexthop for peer-group '{}'\n" - "Config saved to: {}", - enable ? "enabled" : "disabled", - peerGroupName, - session.getSessionConfigPath()); -} - -void CmdConfigProtocolBgpPeerGroupV4OverV6Nh::printOutput( - const RetType& output) { - std::cout << output << std::endl; -} - -// Explicit template instantiation -template void CmdHandler< - CmdConfigProtocolBgpPeerGroupV4OverV6Nh, - CmdConfigProtocolBgpPeerGroupV4OverV6NhTraits>::run(); - -} // namespace facebook::fboss diff --git a/fboss/cli/fboss2/commands/config/protocol/bgp/peer-group/CmdConfigProtocolBgpPeerGroupV4OverV6Nh.h b/fboss/cli/fboss2/commands/config/protocol/bgp/peer-group/CmdConfigProtocolBgpPeerGroupV4OverV6Nh.h deleted file mode 100644 index 89e5f72751f7b..0000000000000 --- a/fboss/cli/fboss2/commands/config/protocol/bgp/peer-group/CmdConfigProtocolBgpPeerGroupV4OverV6Nh.h +++ /dev/null @@ -1,46 +0,0 @@ -/* - * 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/config/protocol/bgp/peer-group/CmdConfigProtocolBgpPeerGroup.h" -#include "fboss/cli/fboss2/utils/CmdUtils.h" - -namespace facebook::fboss { - -struct CmdConfigProtocolBgpPeerGroupV4OverV6NhTraits - : public WriteCommandTraits { - using ParentCmd = CmdConfigProtocolBgpPeerGroup; - static void addCliArg(CLI::App& cmd, std::vector& args) { - cmd.add_option("msg", args, "Message"); - } - using ObjectArgType = utils::Message; - using RetType = std::string; -}; - -class CmdConfigProtocolBgpPeerGroupV4OverV6Nh - : public CmdHandler< - CmdConfigProtocolBgpPeerGroupV4OverV6Nh, - CmdConfigProtocolBgpPeerGroupV4OverV6NhTraits> { - public: - using ObjectArgType = - CmdConfigProtocolBgpPeerGroupV4OverV6NhTraits::ObjectArgType; - using RetType = CmdConfigProtocolBgpPeerGroupV4OverV6NhTraits::RetType; - - RetType queryClient( - const HostInfo& hostInfo, - const utils::Message& peerGroupName, - const ObjectArgType& enable); - - void printOutput(const RetType& output); -}; - -} // namespace facebook::fboss diff --git a/fboss/cli/fboss2/commands/config/protocol/bgp/peer-group/CmdConfigProtocolBgpPeerGroupWarningLimit.cpp b/fboss/cli/fboss2/commands/config/protocol/bgp/peer-group/CmdConfigProtocolBgpPeerGroupWarningLimit.cpp deleted file mode 100644 index ec21c05d82865..0000000000000 --- a/fboss/cli/fboss2/commands/config/protocol/bgp/peer-group/CmdConfigProtocolBgpPeerGroupWarningLimit.cpp +++ /dev/null @@ -1,70 +0,0 @@ -/* - * Copyright (c) 2004-present, Facebook, Inc. - * All rights reserved. - * - * This source code is licensed under the BSD-style license found in the - * LICENSE file in the root directory of this source tree. An additional grant - * of patent rights can be found in the PATENTS file in the same directory. - * - */ - -#include "fboss/cli/fboss2/commands/config/protocol/bgp/peer-group/CmdConfigProtocolBgpPeerGroupWarningLimit.h" - -#include "fboss/cli/fboss2/CmdHandler.cpp" - -#include -#include "fboss/cli/fboss2/commands/config/protocol/bgp/BgpConfigSession.h" - -namespace facebook::fboss { - -CmdConfigProtocolBgpPeerGroupWarningLimitTraits::RetType -CmdConfigProtocolBgpPeerGroupWarningLimit::queryClient( - const HostInfo& /* hostInfo */, - const utils::Message& groupNameArg, - const ObjectArgType& warningLimitMsg) { - auto& session = BgpConfigSession::getInstance(); - - if (groupNameArg.data().empty()) { - return "Error: peer-group name is required"; - } - std::string groupName = groupNameArg.data()[0]; - - if (warningLimitMsg.data().empty()) { - return fmt::format( - "Error: warning-limit value is required for peer-group '{}'", - groupName); - } - - int64_t warningLimit; - try { - warningLimit = std::stoll(warningLimitMsg.data()[0]); - } catch (const std::exception& e) { - return fmt::format( - "Error: invalid warning-limit value '{}': {}", - warningLimitMsg.data()[0], - e.what()); - } - - session.createPeerGroup(groupName); - session.setPeerGroupPreFilterWarningLimit(groupName, warningLimit); - session.saveConfig(); - - return fmt::format( - "Successfully set pre_filter warning_limit for peer-group '{}' to: {}\n" - "Config saved to: {}", - groupName, - warningLimit, - session.getSessionConfigPath()); -} - -void CmdConfigProtocolBgpPeerGroupWarningLimit::printOutput( - const RetType& output) { - std::cout << output << std::endl; -} - -// Explicit template instantiation -template void CmdHandler< - CmdConfigProtocolBgpPeerGroupWarningLimit, - CmdConfigProtocolBgpPeerGroupWarningLimitTraits>::run(); - -} // namespace facebook::fboss diff --git a/fboss/cli/fboss2/commands/config/protocol/bgp/peer-group/CmdConfigProtocolBgpPeerGroupWarningLimit.h b/fboss/cli/fboss2/commands/config/protocol/bgp/peer-group/CmdConfigProtocolBgpPeerGroupWarningLimit.h deleted file mode 100644 index c72b72da59750..0000000000000 --- a/fboss/cli/fboss2/commands/config/protocol/bgp/peer-group/CmdConfigProtocolBgpPeerGroupWarningLimit.h +++ /dev/null @@ -1,46 +0,0 @@ -/* - * 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/config/protocol/bgp/peer-group/CmdConfigProtocolBgpPeerGroup.h" -#include "fboss/cli/fboss2/utils/CmdUtils.h" - -namespace facebook::fboss { - -struct CmdConfigProtocolBgpPeerGroupWarningLimitTraits - : public WriteCommandTraits { - using ParentCmd = CmdConfigProtocolBgpPeerGroup; - static void addCliArg(CLI::App& cmd, std::vector& args) { - cmd.add_option("msg", args, "Message"); - } - using ObjectArgType = utils::Message; - using RetType = std::string; -}; - -class CmdConfigProtocolBgpPeerGroupWarningLimit - : public CmdHandler< - CmdConfigProtocolBgpPeerGroupWarningLimit, - CmdConfigProtocolBgpPeerGroupWarningLimitTraits> { - public: - using ObjectArgType = - CmdConfigProtocolBgpPeerGroupWarningLimitTraits::ObjectArgType; - using RetType = CmdConfigProtocolBgpPeerGroupWarningLimitTraits::RetType; - - RetType queryClient( - const HostInfo& hostInfo, - const utils::Message& groupName, - const ObjectArgType& warningLimit); - - void printOutput(const RetType& output); -}; - -} // namespace facebook::fboss diff --git a/fboss/cli/fboss2/commands/config/protocol/bgp/peer-group/CmdConfigProtocolBgpPeerGroupWarningOnly.cpp b/fboss/cli/fboss2/commands/config/protocol/bgp/peer-group/CmdConfigProtocolBgpPeerGroupWarningOnly.cpp deleted file mode 100644 index ca39a63b8ecd4..0000000000000 --- a/fboss/cli/fboss2/commands/config/protocol/bgp/peer-group/CmdConfigProtocolBgpPeerGroupWarningOnly.cpp +++ /dev/null @@ -1,72 +0,0 @@ -/* - * Copyright (c) 2004-present, Facebook, Inc. - * All rights reserved. - * - * This source code is licensed under the BSD-style license found in the - * LICENSE file in the root directory of this source tree. An additional grant - * of patent rights can be found in the PATENTS file in the same directory. - * - */ - -#include "fboss/cli/fboss2/commands/config/protocol/bgp/peer-group/CmdConfigProtocolBgpPeerGroupWarningOnly.h" - -#include "fboss/cli/fboss2/CmdHandler.cpp" - -#include -#include "fboss/cli/fboss2/commands/config/protocol/bgp/BgpConfigSession.h" - -namespace facebook::fboss { - -CmdConfigProtocolBgpPeerGroupWarningOnlyTraits::RetType -CmdConfigProtocolBgpPeerGroupWarningOnly::queryClient( - const HostInfo& /* hostInfo */, - const utils::Message& groupNameArg, - const ObjectArgType& warningOnlyMsg) { - auto& session = BgpConfigSession::getInstance(); - - if (groupNameArg.data().empty()) { - return "Error: peer-group name is required"; - } - std::string groupName = groupNameArg.data()[0]; - - if (warningOnlyMsg.data().empty()) { - return fmt::format( - "Error: warning-only value (true|false) is required for peer-group '{}'", - groupName); - } - - std::string value = warningOnlyMsg.data()[0]; - bool warningOnly; - if (value == "true" || value == "1") { - warningOnly = true; - } else if (value == "false" || value == "0") { - warningOnly = false; - } else { - return fmt::format( - "Error: invalid warning-only value '{}', expected 'true' or 'false'", - value); - } - - session.createPeerGroup(groupName); - session.setPeerGroupPreFilterWarningOnly(groupName, warningOnly); - session.saveConfig(); - - return fmt::format( - "Successfully set pre_filter warning_only for peer-group '{}' to: {}\n" - "Config saved to: {}", - groupName, - warningOnly ? "true" : "false", - session.getSessionConfigPath()); -} - -void CmdConfigProtocolBgpPeerGroupWarningOnly::printOutput( - const RetType& output) { - std::cout << output << std::endl; -} - -// Explicit template instantiation -template void CmdHandler< - CmdConfigProtocolBgpPeerGroupWarningOnly, - CmdConfigProtocolBgpPeerGroupWarningOnlyTraits>::run(); - -} // namespace facebook::fboss diff --git a/fboss/cli/fboss2/commands/config/protocol/bgp/peer-group/CmdConfigProtocolBgpPeerGroupWarningOnly.h b/fboss/cli/fboss2/commands/config/protocol/bgp/peer-group/CmdConfigProtocolBgpPeerGroupWarningOnly.h deleted file mode 100644 index 9288c4dd9ae45..0000000000000 --- a/fboss/cli/fboss2/commands/config/protocol/bgp/peer-group/CmdConfigProtocolBgpPeerGroupWarningOnly.h +++ /dev/null @@ -1,46 +0,0 @@ -/* - * 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/config/protocol/bgp/peer-group/CmdConfigProtocolBgpPeerGroup.h" -#include "fboss/cli/fboss2/utils/CmdUtils.h" - -namespace facebook::fboss { - -struct CmdConfigProtocolBgpPeerGroupWarningOnlyTraits - : public WriteCommandTraits { - using ParentCmd = CmdConfigProtocolBgpPeerGroup; - static void addCliArg(CLI::App& cmd, std::vector& args) { - cmd.add_option("msg", args, "Message"); - } - using ObjectArgType = utils::Message; - using RetType = std::string; -}; - -class CmdConfigProtocolBgpPeerGroupWarningOnly - : public CmdHandler< - CmdConfigProtocolBgpPeerGroupWarningOnly, - CmdConfigProtocolBgpPeerGroupWarningOnlyTraits> { - public: - using ObjectArgType = - CmdConfigProtocolBgpPeerGroupWarningOnlyTraits::ObjectArgType; - using RetType = CmdConfigProtocolBgpPeerGroupWarningOnlyTraits::RetType; - - RetType queryClient( - const HostInfo& hostInfo, - const utils::Message& groupName, - const ObjectArgType& warningOnly); - - void printOutput(const RetType& output); -}; - -} // namespace facebook::fboss diff --git a/fboss/cli/fboss2/commands/config/protocol/bgp/peer/CmdConfigProtocolBgpPeer.cpp b/fboss/cli/fboss2/commands/config/protocol/bgp/peer/CmdConfigProtocolBgpPeer.cpp deleted file mode 100644 index 756a1f4e958af..0000000000000 --- a/fboss/cli/fboss2/commands/config/protocol/bgp/peer/CmdConfigProtocolBgpPeer.cpp +++ /dev/null @@ -1,52 +0,0 @@ -/* - * Copyright (c) 2004-present, Facebook, Inc. - * All rights reserved. - * - * This source code is licensed under the BSD-style license found in the - * LICENSE file in the root directory of this source tree. An additional grant - * of patent rights can be found in the PATENTS file in the same directory. - * - */ - -#include "fboss/cli/fboss2/commands/config/protocol/bgp/peer/CmdConfigProtocolBgpPeer.h" - -#include "fboss/cli/fboss2/CmdHandler.cpp" - -#include -#include "fboss/cli/fboss2/commands/config/protocol/bgp/BgpConfigSession.h" - -namespace facebook::fboss { - -CmdConfigProtocolBgpPeerTraits::RetType CmdConfigProtocolBgpPeer::queryClient( - const HostInfo& /* hostInfo */, - const ObjectArgType& peerAddressList) { - auto& session = BgpConfigSession::getInstance(); - - if (peerAddressList.data().empty()) { - return "Error: peer address is required"; - } - - // Extract the first IP from the list as peer address - std::string peerAddress = peerAddressList.data()[0]; - - // Create peer entry in the peers list (translates to BGP++ thrift format) - session.createPeer(peerAddress); - session.saveConfig(); - - return fmt::format( - "BGP peer {} created.\n" - "Use subcommands: remote-asn, local-addr, next-hop4, next-hop6, peer-group\n" - "Config saved to: {}", - peerAddress, - session.getSessionConfigPath()); -} - -void CmdConfigProtocolBgpPeer::printOutput(const RetType& output) { - std::cout << output << std::endl; -} - -// Explicit template instantiation -template void -CmdHandler::run(); - -} // namespace facebook::fboss diff --git a/fboss/cli/fboss2/commands/config/protocol/bgp/peer/CmdConfigProtocolBgpPeer.h b/fboss/cli/fboss2/commands/config/protocol/bgp/peer/CmdConfigProtocolBgpPeer.h deleted file mode 100644 index eaee525fe41b3..0000000000000 --- a/fboss/cli/fboss2/commands/config/protocol/bgp/peer/CmdConfigProtocolBgpPeer.h +++ /dev/null @@ -1,42 +0,0 @@ -/* - * 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/config/protocol/bgp/CmdConfigProtocolBgp.h" -#include "fboss/cli/fboss2/utils/CmdUtils.h" - -namespace facebook::fboss { - -struct CmdConfigProtocolBgpPeerTraits : public WriteCommandTraits { - using ParentCmd = CmdConfigProtocolBgp; - static void addCliArg(CLI::App& cmd, std::vector& args) { - cmd.add_option("ipAddrs", args, "IPv4 or IPv6 addr(s)"); - } - using ObjectArgType = utils::IPList; - using RetType = std::string; -}; - -class CmdConfigProtocolBgpPeer : public CmdHandler< - CmdConfigProtocolBgpPeer, - CmdConfigProtocolBgpPeerTraits> { - public: - using ObjectArgType = CmdConfigProtocolBgpPeerTraits::ObjectArgType; - using RetType = CmdConfigProtocolBgpPeerTraits::RetType; - - RetType queryClient( - const HostInfo& hostInfo, - const ObjectArgType& peerAddress); - - void printOutput(const RetType& output); -}; - -} // namespace facebook::fboss diff --git a/fboss/cli/fboss2/commands/config/protocol/bgp/peer/CmdConfigProtocolBgpPeerAdvertiseLbw.cpp b/fboss/cli/fboss2/commands/config/protocol/bgp/peer/CmdConfigProtocolBgpPeerAdvertiseLbw.cpp deleted file mode 100644 index 2cc45ff10335e..0000000000000 --- a/fboss/cli/fboss2/commands/config/protocol/bgp/peer/CmdConfigProtocolBgpPeerAdvertiseLbw.cpp +++ /dev/null @@ -1,60 +0,0 @@ -/* - * Copyright (c) 2004-present, Facebook, Inc. - * All rights reserved. - * - * This source code is licensed under the BSD-style license found in the - * LICENSE file in the root directory of this source tree. An additional grant - * of patent rights can be found in the PATENTS file in the same directory. - * - */ - -#include "fboss/cli/fboss2/commands/config/protocol/bgp/peer/CmdConfigProtocolBgpPeerAdvertiseLbw.h" - -#include "fboss/cli/fboss2/CmdHandler.cpp" - -#include -#include "fboss/cli/fboss2/commands/config/protocol/bgp/BgpConfigSession.h" - -namespace facebook::fboss { - -CmdConfigProtocolBgpPeerAdvertiseLbwTraits::RetType -CmdConfigProtocolBgpPeerAdvertiseLbw::queryClient( - const HostInfo& /* hostInfo */, - const utils::IPList& peerAddressList, - const ObjectArgType& enableMsg) { - auto& session = BgpConfigSession::getInstance(); - - if (peerAddressList.data().empty()) { - return "Error: peer address is required"; - } - std::string peerAddress = peerAddressList.data()[0]; - - if (enableMsg.data().empty()) { - return fmt::format( - "Error: enable value required for peer '{}'", peerAddress); - } - std::string enableStr = enableMsg.data()[0]; - bool enable = (enableStr == "true" || enableStr == "1" || enableStr == "yes"); - - session.createPeer(peerAddress); - session.setPeerAdvertiseLbw(peerAddress, enable); - session.saveConfig(); - - return fmt::format( - "Successfully {} advertise-link-bandwidth for peer '{}'\n" - "Config saved to: {}", - enable ? "enabled" : "disabled", - peerAddress, - session.getSessionConfigPath()); -} - -void CmdConfigProtocolBgpPeerAdvertiseLbw::printOutput(const RetType& output) { - std::cout << output << std::endl; -} - -// Explicit template instantiation -template void CmdHandler< - CmdConfigProtocolBgpPeerAdvertiseLbw, - CmdConfigProtocolBgpPeerAdvertiseLbwTraits>::run(); - -} // namespace facebook::fboss diff --git a/fboss/cli/fboss2/commands/config/protocol/bgp/peer/CmdConfigProtocolBgpPeerAdvertiseLbw.h b/fboss/cli/fboss2/commands/config/protocol/bgp/peer/CmdConfigProtocolBgpPeerAdvertiseLbw.h deleted file mode 100644 index 0b6de963abefc..0000000000000 --- a/fboss/cli/fboss2/commands/config/protocol/bgp/peer/CmdConfigProtocolBgpPeerAdvertiseLbw.h +++ /dev/null @@ -1,45 +0,0 @@ -/* - * 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/config/protocol/bgp/peer/CmdConfigProtocolBgpPeer.h" -#include "fboss/cli/fboss2/utils/CmdUtils.h" - -namespace facebook::fboss { - -struct CmdConfigProtocolBgpPeerAdvertiseLbwTraits : public WriteCommandTraits { - using ParentCmd = CmdConfigProtocolBgpPeer; - static void addCliArg(CLI::App& cmd, std::vector& args) { - cmd.add_option("msg", args, "Message"); - } - using ObjectArgType = utils::Message; - using RetType = std::string; -}; - -class CmdConfigProtocolBgpPeerAdvertiseLbw - : public CmdHandler< - CmdConfigProtocolBgpPeerAdvertiseLbw, - CmdConfigProtocolBgpPeerAdvertiseLbwTraits> { - public: - using ObjectArgType = - CmdConfigProtocolBgpPeerAdvertiseLbwTraits::ObjectArgType; - using RetType = CmdConfigProtocolBgpPeerAdvertiseLbwTraits::RetType; - - RetType queryClient( - const HostInfo& hostInfo, - const utils::IPList& peerAddress, - const ObjectArgType& enable); - - void printOutput(const RetType& output); -}; - -} // namespace facebook::fboss diff --git a/fboss/cli/fboss2/commands/config/protocol/bgp/peer/CmdConfigProtocolBgpPeerDescription.cpp b/fboss/cli/fboss2/commands/config/protocol/bgp/peer/CmdConfigProtocolBgpPeerDescription.cpp deleted file mode 100644 index 51f1fcab3265b..0000000000000 --- a/fboss/cli/fboss2/commands/config/protocol/bgp/peer/CmdConfigProtocolBgpPeerDescription.cpp +++ /dev/null @@ -1,58 +0,0 @@ -/* - * Copyright (c) 2004-present, Facebook, Inc. - * All rights reserved. - * - * This source code is licensed under the BSD-style license found in the - * LICENSE file in the root directory of this source tree. An additional grant - * of patent rights can be found in the PATENTS file in the same directory. - * - */ - -#include "fboss/cli/fboss2/commands/config/protocol/bgp/peer/CmdConfigProtocolBgpPeerDescription.h" - -#include "fboss/cli/fboss2/CmdHandler.cpp" - -#include -#include "fboss/cli/fboss2/commands/config/protocol/bgp/BgpConfigSession.h" - -namespace facebook::fboss { - -CmdConfigProtocolBgpPeerDescriptionTraits::RetType -CmdConfigProtocolBgpPeerDescription::queryClient( - const HostInfo& /* hostInfo */, - const utils::IPList& peerAddressList, - const ObjectArgType& descriptionMsg) { - auto& session = BgpConfigSession::getInstance(); - - if (peerAddressList.data().empty()) { - return "Error: peer address is required"; - } - std::string peerAddress = peerAddressList.data()[0]; - - if (descriptionMsg.data().empty()) { - return fmt::format( - "Error: description value is required for peer '{}'", peerAddress); - } - - session.createPeer(peerAddress); - session.setPeerDescription(peerAddress, descriptionMsg.data()[0]); - session.saveConfig(); - - return fmt::format( - "Successfully set description for peer '{}' to: '{}'\n" - "Config saved to: {}", - peerAddress, - descriptionMsg.data()[0], - session.getSessionConfigPath()); -} - -void CmdConfigProtocolBgpPeerDescription::printOutput(const RetType& output) { - std::cout << output << std::endl; -} - -// Explicit template instantiation -template void CmdHandler< - CmdConfigProtocolBgpPeerDescription, - CmdConfigProtocolBgpPeerDescriptionTraits>::run(); - -} // namespace facebook::fboss diff --git a/fboss/cli/fboss2/commands/config/protocol/bgp/peer/CmdConfigProtocolBgpPeerDescription.h b/fboss/cli/fboss2/commands/config/protocol/bgp/peer/CmdConfigProtocolBgpPeerDescription.h deleted file mode 100644 index 7e689baf6f19e..0000000000000 --- a/fboss/cli/fboss2/commands/config/protocol/bgp/peer/CmdConfigProtocolBgpPeerDescription.h +++ /dev/null @@ -1,45 +0,0 @@ -/* - * 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/config/protocol/bgp/peer/CmdConfigProtocolBgpPeer.h" -#include "fboss/cli/fboss2/utils/CmdUtils.h" - -namespace facebook::fboss { - -struct CmdConfigProtocolBgpPeerDescriptionTraits : public WriteCommandTraits { - using ParentCmd = CmdConfigProtocolBgpPeer; - static void addCliArg(CLI::App& cmd, std::vector& args) { - cmd.add_option("msg", args, "Message"); - } - using ObjectArgType = utils::Message; - using RetType = std::string; -}; - -class CmdConfigProtocolBgpPeerDescription - : public CmdHandler< - CmdConfigProtocolBgpPeerDescription, - CmdConfigProtocolBgpPeerDescriptionTraits> { - public: - using ObjectArgType = - CmdConfigProtocolBgpPeerDescriptionTraits::ObjectArgType; - using RetType = CmdConfigProtocolBgpPeerDescriptionTraits::RetType; - - RetType queryClient( - const HostInfo& hostInfo, - const utils::IPList& peerAddress, - const ObjectArgType& description); - - void printOutput(const RetType& output); -}; - -} // namespace facebook::fboss diff --git a/fboss/cli/fboss2/commands/config/protocol/bgp/peer/CmdConfigProtocolBgpPeerDisableIpv4Afi.cpp b/fboss/cli/fboss2/commands/config/protocol/bgp/peer/CmdConfigProtocolBgpPeerDisableIpv4Afi.cpp deleted file mode 100644 index ccbf99291fce6..0000000000000 --- a/fboss/cli/fboss2/commands/config/protocol/bgp/peer/CmdConfigProtocolBgpPeerDisableIpv4Afi.cpp +++ /dev/null @@ -1,62 +0,0 @@ -/* - * Copyright (c) 2004-present, Facebook, Inc. - * All rights reserved. - * - * This source code is licensed under the BSD-style license found in the - * LICENSE file in the root directory of this source tree. An additional grant - * of patent rights can be found in the PATENTS file in the same directory. - * - */ - -#include "fboss/cli/fboss2/commands/config/protocol/bgp/peer/CmdConfigProtocolBgpPeerDisableIpv4Afi.h" - -#include "fboss/cli/fboss2/CmdHandler.cpp" - -#include -#include "fboss/cli/fboss2/commands/config/protocol/bgp/BgpConfigSession.h" - -namespace facebook::fboss { - -CmdConfigProtocolBgpPeerDisableIpv4AfiTraits::RetType -CmdConfigProtocolBgpPeerDisableIpv4Afi::queryClient( - const HostInfo& /* hostInfo */, - const utils::IPList& peerAddressList, - const ObjectArgType& disableMsg) { - auto& session = BgpConfigSession::getInstance(); - - if (peerAddressList.data().empty()) { - return "Error: peer address is required"; - } - std::string peerAddress = peerAddressList.data()[0]; - - if (disableMsg.data().empty()) { - return fmt::format( - "Error: disable value required for peer '{}'", peerAddress); - } - std::string disableStr = disableMsg.data()[0]; - bool disable = - (disableStr == "true" || disableStr == "1" || disableStr == "yes"); - - session.createPeer(peerAddress); - session.setPeerDisableIpv4Afi(peerAddress, disable); - session.saveConfig(); - - return fmt::format( - "Successfully {} IPv4 AFI for peer '{}'\n" - "Config saved to: {}", - disable ? "disabled" : "enabled", - peerAddress, - session.getSessionConfigPath()); -} - -void CmdConfigProtocolBgpPeerDisableIpv4Afi::printOutput( - const RetType& output) { - std::cout << output << std::endl; -} - -// Explicit template instantiation -template void CmdHandler< - CmdConfigProtocolBgpPeerDisableIpv4Afi, - CmdConfigProtocolBgpPeerDisableIpv4AfiTraits>::run(); - -} // namespace facebook::fboss diff --git a/fboss/cli/fboss2/commands/config/protocol/bgp/peer/CmdConfigProtocolBgpPeerDisableIpv4Afi.h b/fboss/cli/fboss2/commands/config/protocol/bgp/peer/CmdConfigProtocolBgpPeerDisableIpv4Afi.h deleted file mode 100644 index 285bc4fb56be1..0000000000000 --- a/fboss/cli/fboss2/commands/config/protocol/bgp/peer/CmdConfigProtocolBgpPeerDisableIpv4Afi.h +++ /dev/null @@ -1,46 +0,0 @@ -/* - * 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/config/protocol/bgp/peer/CmdConfigProtocolBgpPeer.h" -#include "fboss/cli/fboss2/utils/CmdUtils.h" - -namespace facebook::fboss { - -struct CmdConfigProtocolBgpPeerDisableIpv4AfiTraits - : public WriteCommandTraits { - using ParentCmd = CmdConfigProtocolBgpPeer; - static void addCliArg(CLI::App& cmd, std::vector& args) { - cmd.add_option("msg", args, "Message"); - } - using ObjectArgType = utils::Message; - using RetType = std::string; -}; - -class CmdConfigProtocolBgpPeerDisableIpv4Afi - : public CmdHandler< - CmdConfigProtocolBgpPeerDisableIpv4Afi, - CmdConfigProtocolBgpPeerDisableIpv4AfiTraits> { - public: - using ObjectArgType = - CmdConfigProtocolBgpPeerDisableIpv4AfiTraits::ObjectArgType; - using RetType = CmdConfigProtocolBgpPeerDisableIpv4AfiTraits::RetType; - - RetType queryClient( - const HostInfo& hostInfo, - const utils::IPList& peerAddress, - const ObjectArgType& disable); - - void printOutput(const RetType& output); -}; - -} // namespace facebook::fboss diff --git a/fboss/cli/fboss2/commands/config/protocol/bgp/peer/CmdConfigProtocolBgpPeerEgressPolicy.cpp b/fboss/cli/fboss2/commands/config/protocol/bgp/peer/CmdConfigProtocolBgpPeerEgressPolicy.cpp deleted file mode 100644 index 816f2e529b030..0000000000000 --- a/fboss/cli/fboss2/commands/config/protocol/bgp/peer/CmdConfigProtocolBgpPeerEgressPolicy.cpp +++ /dev/null @@ -1,59 +0,0 @@ -/* - * Copyright (c) 2004-present, Facebook, Inc. - * All rights reserved. - * - * This source code is licensed under the BSD-style license found in the - * LICENSE file in the root directory of this source tree. An additional grant - * of patent rights can be found in the PATENTS file in the same directory. - * - */ - -#include "fboss/cli/fboss2/commands/config/protocol/bgp/peer/CmdConfigProtocolBgpPeerEgressPolicy.h" - -#include "fboss/cli/fboss2/CmdHandler.cpp" - -#include -#include "fboss/cli/fboss2/commands/config/protocol/bgp/BgpConfigSession.h" - -namespace facebook::fboss { - -CmdConfigProtocolBgpPeerEgressPolicyTraits::RetType -CmdConfigProtocolBgpPeerEgressPolicy::queryClient( - const HostInfo& /* hostInfo */, - const utils::IPList& peerAddressList, - const ObjectArgType& policyMsg) { - auto& session = BgpConfigSession::getInstance(); - - if (peerAddressList.data().empty()) { - return "Error: peer address is required"; - } - std::string peerAddress = peerAddressList.data()[0]; - - if (policyMsg.data().empty()) { - return fmt::format( - "Error: egress policy name is required for peer '{}'", peerAddress); - } - std::string policyName = policyMsg.data()[0]; - - session.createPeer(peerAddress); - session.setPeerEgressPolicy(peerAddress, policyName); - session.saveConfig(); - - return fmt::format( - "Successfully set egress policy for peer '{}' to: '{}'\n" - "Config saved to: {}", - peerAddress, - policyName, - session.getSessionConfigPath()); -} - -void CmdConfigProtocolBgpPeerEgressPolicy::printOutput(const RetType& output) { - std::cout << output << std::endl; -} - -// Explicit template instantiation -template void CmdHandler< - CmdConfigProtocolBgpPeerEgressPolicy, - CmdConfigProtocolBgpPeerEgressPolicyTraits>::run(); - -} // namespace facebook::fboss diff --git a/fboss/cli/fboss2/commands/config/protocol/bgp/peer/CmdConfigProtocolBgpPeerEgressPolicy.h b/fboss/cli/fboss2/commands/config/protocol/bgp/peer/CmdConfigProtocolBgpPeerEgressPolicy.h deleted file mode 100644 index abf65d3c8a601..0000000000000 --- a/fboss/cli/fboss2/commands/config/protocol/bgp/peer/CmdConfigProtocolBgpPeerEgressPolicy.h +++ /dev/null @@ -1,45 +0,0 @@ -/* - * 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/config/protocol/bgp/peer/CmdConfigProtocolBgpPeer.h" -#include "fboss/cli/fboss2/utils/CmdUtils.h" - -namespace facebook::fboss { - -struct CmdConfigProtocolBgpPeerEgressPolicyTraits : public WriteCommandTraits { - using ParentCmd = CmdConfigProtocolBgpPeer; - static void addCliArg(CLI::App& cmd, std::vector& args) { - cmd.add_option("msg", args, "Message"); - } - using ObjectArgType = utils::Message; - using RetType = std::string; -}; - -class CmdConfigProtocolBgpPeerEgressPolicy - : public CmdHandler< - CmdConfigProtocolBgpPeerEgressPolicy, - CmdConfigProtocolBgpPeerEgressPolicyTraits> { - public: - using ObjectArgType = - CmdConfigProtocolBgpPeerEgressPolicyTraits::ObjectArgType; - using RetType = CmdConfigProtocolBgpPeerEgressPolicyTraits::RetType; - - RetType queryClient( - const HostInfo& hostInfo, - const utils::IPList& peerAddress, - const ObjectArgType& policyName); - - void printOutput(const RetType& output); -}; - -} // namespace facebook::fboss diff --git a/fboss/cli/fboss2/commands/config/protocol/bgp/peer/CmdConfigProtocolBgpPeerHoldTime.cpp b/fboss/cli/fboss2/commands/config/protocol/bgp/peer/CmdConfigProtocolBgpPeerHoldTime.cpp deleted file mode 100644 index 521aeb95a135b..0000000000000 --- a/fboss/cli/fboss2/commands/config/protocol/bgp/peer/CmdConfigProtocolBgpPeerHoldTime.cpp +++ /dev/null @@ -1,69 +0,0 @@ -/* - * Copyright (c) 2004-present, Facebook, Inc. - * All rights reserved. - * - * This source code is licensed under the BSD-style license found in the - * LICENSE file in the root directory of this source tree. An additional grant - * of patent rights can be found in the PATENTS file in the same directory. - * - */ - -#include "fboss/cli/fboss2/commands/config/protocol/bgp/peer/CmdConfigProtocolBgpPeerHoldTime.h" - -#include "fboss/cli/fboss2/CmdHandler.cpp" - -#include -#include -#include "fboss/cli/fboss2/commands/config/protocol/bgp/BgpConfigSession.h" - -namespace facebook::fboss { - -CmdConfigProtocolBgpPeerHoldTimeTraits::RetType -CmdConfigProtocolBgpPeerHoldTime::queryClient( - const HostInfo& /* hostInfo */, - const utils::IPList& peerAddressList, - const ObjectArgType& holdTimeMsg) { - auto& session = BgpConfigSession::getInstance(); - - if (peerAddressList.data().empty()) { - return "Error: peer address is required"; - } - std::string peerAddress = peerAddressList.data()[0]; - - if (holdTimeMsg.data().empty()) { - return fmt::format( - "Error: hold-time value is required for peer '{}'", peerAddress); - } - - int32_t holdTime; - try { - holdTime = folly::to(holdTimeMsg.data()[0]); - } catch (const std::exception& e) { - return fmt::format( - "Error: invalid hold-time value '{}': {}", - holdTimeMsg.data()[0], - e.what()); - } - - session.createPeer(peerAddress); - session.setPeerHoldTime(peerAddress, holdTime); - session.saveConfig(); - - return fmt::format( - "Successfully set hold-time for peer '{}' to: {} seconds\n" - "Config saved to: {}", - peerAddress, - holdTime, - session.getSessionConfigPath()); -} - -void CmdConfigProtocolBgpPeerHoldTime::printOutput(const RetType& output) { - std::cout << output << std::endl; -} - -// Explicit template instantiation -template void CmdHandler< - CmdConfigProtocolBgpPeerHoldTime, - CmdConfigProtocolBgpPeerHoldTimeTraits>::run(); - -} // namespace facebook::fboss diff --git a/fboss/cli/fboss2/commands/config/protocol/bgp/peer/CmdConfigProtocolBgpPeerHoldTime.h b/fboss/cli/fboss2/commands/config/protocol/bgp/peer/CmdConfigProtocolBgpPeerHoldTime.h deleted file mode 100644 index 22c2149ab7ebc..0000000000000 --- a/fboss/cli/fboss2/commands/config/protocol/bgp/peer/CmdConfigProtocolBgpPeerHoldTime.h +++ /dev/null @@ -1,44 +0,0 @@ -/* - * 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/config/protocol/bgp/peer/CmdConfigProtocolBgpPeer.h" -#include "fboss/cli/fboss2/utils/CmdUtils.h" - -namespace facebook::fboss { - -struct CmdConfigProtocolBgpPeerHoldTimeTraits : public WriteCommandTraits { - using ParentCmd = CmdConfigProtocolBgpPeer; - static void addCliArg(CLI::App& cmd, std::vector& args) { - cmd.add_option("msg", args, "Message"); - } - using ObjectArgType = utils::Message; - using RetType = std::string; -}; - -class CmdConfigProtocolBgpPeerHoldTime - : public CmdHandler< - CmdConfigProtocolBgpPeerHoldTime, - CmdConfigProtocolBgpPeerHoldTimeTraits> { - public: - using ObjectArgType = CmdConfigProtocolBgpPeerHoldTimeTraits::ObjectArgType; - using RetType = CmdConfigProtocolBgpPeerHoldTimeTraits::RetType; - - RetType queryClient( - const HostInfo& hostInfo, - const utils::IPList& peerAddress, - const ObjectArgType& holdTime); - - void printOutput(const RetType& output); -}; - -} // namespace facebook::fboss diff --git a/fboss/cli/fboss2/commands/config/protocol/bgp/peer/CmdConfigProtocolBgpPeerIngressPolicy.cpp b/fboss/cli/fboss2/commands/config/protocol/bgp/peer/CmdConfigProtocolBgpPeerIngressPolicy.cpp deleted file mode 100644 index 0f26e9045b9e2..0000000000000 --- a/fboss/cli/fboss2/commands/config/protocol/bgp/peer/CmdConfigProtocolBgpPeerIngressPolicy.cpp +++ /dev/null @@ -1,59 +0,0 @@ -/* - * Copyright (c) 2004-present, Facebook, Inc. - * All rights reserved. - * - * This source code is licensed under the BSD-style license found in the - * LICENSE file in the root directory of this source tree. An additional grant - * of patent rights can be found in the PATENTS file in the same directory. - * - */ - -#include "fboss/cli/fboss2/commands/config/protocol/bgp/peer/CmdConfigProtocolBgpPeerIngressPolicy.h" - -#include "fboss/cli/fboss2/CmdHandler.cpp" - -#include -#include "fboss/cli/fboss2/commands/config/protocol/bgp/BgpConfigSession.h" - -namespace facebook::fboss { - -CmdConfigProtocolBgpPeerIngressPolicyTraits::RetType -CmdConfigProtocolBgpPeerIngressPolicy::queryClient( - const HostInfo& /* hostInfo */, - const utils::IPList& peerAddressList, - const ObjectArgType& policyMsg) { - auto& session = BgpConfigSession::getInstance(); - - if (peerAddressList.data().empty()) { - return "Error: peer address is required"; - } - std::string peerAddress = peerAddressList.data()[0]; - - if (policyMsg.data().empty()) { - return fmt::format( - "Error: ingress policy name is required for peer '{}'", peerAddress); - } - std::string policyName = policyMsg.data()[0]; - - session.createPeer(peerAddress); - session.setPeerIngressPolicy(peerAddress, policyName); - session.saveConfig(); - - return fmt::format( - "Successfully set ingress policy for peer '{}' to: '{}'\n" - "Config saved to: {}", - peerAddress, - policyName, - session.getSessionConfigPath()); -} - -void CmdConfigProtocolBgpPeerIngressPolicy::printOutput(const RetType& output) { - std::cout << output << std::endl; -} - -// Explicit template instantiation -template void CmdHandler< - CmdConfigProtocolBgpPeerIngressPolicy, - CmdConfigProtocolBgpPeerIngressPolicyTraits>::run(); - -} // namespace facebook::fboss diff --git a/fboss/cli/fboss2/commands/config/protocol/bgp/peer/CmdConfigProtocolBgpPeerIngressPolicy.h b/fboss/cli/fboss2/commands/config/protocol/bgp/peer/CmdConfigProtocolBgpPeerIngressPolicy.h deleted file mode 100644 index 57647d9acd537..0000000000000 --- a/fboss/cli/fboss2/commands/config/protocol/bgp/peer/CmdConfigProtocolBgpPeerIngressPolicy.h +++ /dev/null @@ -1,45 +0,0 @@ -/* - * 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/config/protocol/bgp/peer/CmdConfigProtocolBgpPeer.h" -#include "fboss/cli/fboss2/utils/CmdUtils.h" - -namespace facebook::fboss { - -struct CmdConfigProtocolBgpPeerIngressPolicyTraits : public WriteCommandTraits { - using ParentCmd = CmdConfigProtocolBgpPeer; - static void addCliArg(CLI::App& cmd, std::vector& args) { - cmd.add_option("msg", args, "Message"); - } - using ObjectArgType = utils::Message; - using RetType = std::string; -}; - -class CmdConfigProtocolBgpPeerIngressPolicy - : public CmdHandler< - CmdConfigProtocolBgpPeerIngressPolicy, - CmdConfigProtocolBgpPeerIngressPolicyTraits> { - public: - using ObjectArgType = - CmdConfigProtocolBgpPeerIngressPolicyTraits::ObjectArgType; - using RetType = CmdConfigProtocolBgpPeerIngressPolicyTraits::RetType; - - RetType queryClient( - const HostInfo& hostInfo, - const utils::IPList& peerAddress, - const ObjectArgType& policyName); - - void printOutput(const RetType& output); -}; - -} // namespace facebook::fboss diff --git a/fboss/cli/fboss2/commands/config/protocol/bgp/peer/CmdConfigProtocolBgpPeerLinkBandwidth.cpp b/fboss/cli/fboss2/commands/config/protocol/bgp/peer/CmdConfigProtocolBgpPeerLinkBandwidth.cpp deleted file mode 100644 index 7f42aecef6632..0000000000000 --- a/fboss/cli/fboss2/commands/config/protocol/bgp/peer/CmdConfigProtocolBgpPeerLinkBandwidth.cpp +++ /dev/null @@ -1,95 +0,0 @@ -/* - * Copyright (c) 2004-present, Facebook, Inc. - * All rights reserved. - * - * This source code is licensed under the BSD-style license found in the - * LICENSE file in the root directory of this source tree. An additional grant - * of patent rights can be found in the PATENTS file in the same directory. - * - */ - -#include "fboss/cli/fboss2/commands/config/protocol/bgp/peer/CmdConfigProtocolBgpPeerLinkBandwidth.h" - -#include "fboss/cli/fboss2/CmdHandler.cpp" - -#include -#include -#include "fboss/cli/fboss2/commands/config/protocol/bgp/BgpConfigSession.h" - -namespace facebook::fboss { - -namespace { -// Parse bandwidth string like "10G", "100M", "1000" to bps -int64_t parseBandwidth(const std::string& bwStr) { - std::string upper = bwStr; - for (auto& c : upper) { - c = std::toupper(c); - } - - int64_t multiplier = 1; - std::string numPart = upper; - - if (upper.back() == 'G') { - multiplier = 1000000000LL; - numPart = upper.substr(0, upper.length() - 1); - } else if (upper.back() == 'M') { - multiplier = 1000000LL; - numPart = upper.substr(0, upper.length() - 1); - } else if (upper.back() == 'K') { - multiplier = 1000LL; - numPart = upper.substr(0, upper.length() - 1); - } - - return folly::to(numPart) * multiplier; -} -} // namespace - -CmdConfigProtocolBgpPeerLinkBandwidthTraits::RetType -CmdConfigProtocolBgpPeerLinkBandwidth::queryClient( - const HostInfo& /* hostInfo */, - const utils::IPList& peerAddressList, - const ObjectArgType& bandwidthMsg) { - auto& session = BgpConfigSession::getInstance(); - - if (peerAddressList.data().empty()) { - return "Error: peer address is required"; - } - std::string peerAddress = peerAddressList.data()[0]; - - if (bandwidthMsg.data().empty()) { - return fmt::format( - "Error: link-bandwidth value is required for peer '{}'", peerAddress); - } - - int64_t bps; - try { - bps = parseBandwidth(bandwidthMsg.data()[0]); - } catch (const std::exception& e) { - return fmt::format( - "Error: invalid link-bandwidth value '{}': {}", - bandwidthMsg.data()[0], - e.what()); - } - - session.createPeer(peerAddress); - session.setPeerLbwValue(peerAddress, bps); - session.saveConfig(); - - return fmt::format( - "Successfully set link-bandwidth for peer '{}' to: {} bps\n" - "Config saved to: {}", - peerAddress, - bps, - session.getSessionConfigPath()); -} - -void CmdConfigProtocolBgpPeerLinkBandwidth::printOutput(const RetType& output) { - std::cout << output << std::endl; -} - -// Explicit template instantiation -template void CmdHandler< - CmdConfigProtocolBgpPeerLinkBandwidth, - CmdConfigProtocolBgpPeerLinkBandwidthTraits>::run(); - -} // namespace facebook::fboss diff --git a/fboss/cli/fboss2/commands/config/protocol/bgp/peer/CmdConfigProtocolBgpPeerLinkBandwidth.h b/fboss/cli/fboss2/commands/config/protocol/bgp/peer/CmdConfigProtocolBgpPeerLinkBandwidth.h deleted file mode 100644 index 21efd752db201..0000000000000 --- a/fboss/cli/fboss2/commands/config/protocol/bgp/peer/CmdConfigProtocolBgpPeerLinkBandwidth.h +++ /dev/null @@ -1,45 +0,0 @@ -/* - * 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/config/protocol/bgp/peer/CmdConfigProtocolBgpPeer.h" -#include "fboss/cli/fboss2/utils/CmdUtils.h" - -namespace facebook::fboss { - -struct CmdConfigProtocolBgpPeerLinkBandwidthTraits : public WriteCommandTraits { - using ParentCmd = CmdConfigProtocolBgpPeer; - static void addCliArg(CLI::App& cmd, std::vector& args) { - cmd.add_option("msg", args, "Message"); - } - using ObjectArgType = utils::Message; - using RetType = std::string; -}; - -class CmdConfigProtocolBgpPeerLinkBandwidth - : public CmdHandler< - CmdConfigProtocolBgpPeerLinkBandwidth, - CmdConfigProtocolBgpPeerLinkBandwidthTraits> { - public: - using ObjectArgType = - CmdConfigProtocolBgpPeerLinkBandwidthTraits::ObjectArgType; - using RetType = CmdConfigProtocolBgpPeerLinkBandwidthTraits::RetType; - - RetType queryClient( - const HostInfo& hostInfo, - const utils::IPList& peerAddress, - const ObjectArgType& bandwidth); - - void printOutput(const RetType& output); -}; - -} // namespace facebook::fboss diff --git a/fboss/cli/fboss2/commands/config/protocol/bgp/peer/CmdConfigProtocolBgpPeerLocalAddr.cpp b/fboss/cli/fboss2/commands/config/protocol/bgp/peer/CmdConfigProtocolBgpPeerLocalAddr.cpp deleted file mode 100644 index 16b702514cb68..0000000000000 --- a/fboss/cli/fboss2/commands/config/protocol/bgp/peer/CmdConfigProtocolBgpPeerLocalAddr.cpp +++ /dev/null @@ -1,59 +0,0 @@ -/* - * Copyright (c) 2004-present, Facebook, Inc. - * All rights reserved. - * - * This source code is licensed under the BSD-style license found in the - * LICENSE file in the root directory of this source tree. An additional grant - * of patent rights can be found in the PATENTS file in the same directory. - * - */ - -#include "fboss/cli/fboss2/commands/config/protocol/bgp/peer/CmdConfigProtocolBgpPeerLocalAddr.h" - -#include "fboss/cli/fboss2/CmdHandler.cpp" - -#include -#include "fboss/cli/fboss2/commands/config/protocol/bgp/BgpConfigSession.h" - -namespace facebook::fboss { - -CmdConfigProtocolBgpPeerLocalAddrTraits::RetType -CmdConfigProtocolBgpPeerLocalAddr::queryClient( - const HostInfo& /* hostInfo */, - const utils::IPList& peerAddressList, - const ObjectArgType& localAddrMsg) { - auto& session = BgpConfigSession::getInstance(); - - if (peerAddressList.data().empty()) { - return "Error: peer address is required"; - } - std::string peerAddress = peerAddressList.data()[0]; - - if (localAddrMsg.data().empty()) { - return fmt::format( - "Error: local-addr value required for peer '{}'", peerAddress); - } - std::string localAddr = localAddrMsg.data()[0]; - - session.createPeer(peerAddress); - session.setPeerLocalAddr(peerAddress, localAddr); - session.saveConfig(); - - return fmt::format( - "Successfully set local-addr for peer '{}' to: '{}'\n" - "Config saved to: {}", - peerAddress, - localAddr, - session.getSessionConfigPath()); -} - -void CmdConfigProtocolBgpPeerLocalAddr::printOutput(const RetType& output) { - std::cout << output << std::endl; -} - -// Explicit template instantiation -template void CmdHandler< - CmdConfigProtocolBgpPeerLocalAddr, - CmdConfigProtocolBgpPeerLocalAddrTraits>::run(); - -} // namespace facebook::fboss diff --git a/fboss/cli/fboss2/commands/config/protocol/bgp/peer/CmdConfigProtocolBgpPeerLocalAddr.h b/fboss/cli/fboss2/commands/config/protocol/bgp/peer/CmdConfigProtocolBgpPeerLocalAddr.h deleted file mode 100644 index e982e20e79d0b..0000000000000 --- a/fboss/cli/fboss2/commands/config/protocol/bgp/peer/CmdConfigProtocolBgpPeerLocalAddr.h +++ /dev/null @@ -1,44 +0,0 @@ -/* - * 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/config/protocol/bgp/peer/CmdConfigProtocolBgpPeer.h" -#include "fboss/cli/fboss2/utils/CmdUtils.h" - -namespace facebook::fboss { - -struct CmdConfigProtocolBgpPeerLocalAddrTraits : public WriteCommandTraits { - using ParentCmd = CmdConfigProtocolBgpPeer; - static void addCliArg(CLI::App& cmd, std::vector& args) { - cmd.add_option("msg", args, "Message"); - } - using ObjectArgType = utils::Message; - using RetType = std::string; -}; - -class CmdConfigProtocolBgpPeerLocalAddr - : public CmdHandler< - CmdConfigProtocolBgpPeerLocalAddr, - CmdConfigProtocolBgpPeerLocalAddrTraits> { - public: - using ObjectArgType = CmdConfigProtocolBgpPeerLocalAddrTraits::ObjectArgType; - using RetType = CmdConfigProtocolBgpPeerLocalAddrTraits::RetType; - - RetType queryClient( - const HostInfo& hostInfo, - const utils::IPList& peerAddress, - const ObjectArgType& localAddr); - - void printOutput(const RetType& output); -}; - -} // namespace facebook::fboss diff --git a/fboss/cli/fboss2/commands/config/protocol/bgp/peer/CmdConfigProtocolBgpPeerMaxRoutes.cpp b/fboss/cli/fboss2/commands/config/protocol/bgp/peer/CmdConfigProtocolBgpPeerMaxRoutes.cpp deleted file mode 100644 index 8f7171c157076..0000000000000 --- a/fboss/cli/fboss2/commands/config/protocol/bgp/peer/CmdConfigProtocolBgpPeerMaxRoutes.cpp +++ /dev/null @@ -1,69 +0,0 @@ -/* - * Copyright (c) 2004-present, Facebook, Inc. - * All rights reserved. - * - * This source code is licensed under the BSD-style license found in the - * LICENSE file in the root directory of this source tree. An additional grant - * of patent rights can be found in the PATENTS file in the same directory. - * - */ - -#include "fboss/cli/fboss2/commands/config/protocol/bgp/peer/CmdConfigProtocolBgpPeerMaxRoutes.h" - -#include "fboss/cli/fboss2/CmdHandler.cpp" - -#include -#include -#include "fboss/cli/fboss2/commands/config/protocol/bgp/BgpConfigSession.h" - -namespace facebook::fboss { - -CmdConfigProtocolBgpPeerMaxRoutesTraits::RetType -CmdConfigProtocolBgpPeerMaxRoutes::queryClient( - const HostInfo& /* hostInfo */, - const utils::IPList& peerAddressList, - const ObjectArgType& maxRoutesMsg) { - auto& session = BgpConfigSession::getInstance(); - - if (peerAddressList.data().empty()) { - return "Error: peer address is required"; - } - std::string peerAddress = peerAddressList.data()[0]; - - if (maxRoutesMsg.data().empty()) { - return fmt::format( - "Error: max-routes value is required for peer '{}'", peerAddress); - } - - int64_t maxRoutes; - try { - maxRoutes = folly::to(maxRoutesMsg.data()[0]); - } catch (const std::exception& e) { - return fmt::format( - "Error: invalid max-routes value '{}': {}", - maxRoutesMsg.data()[0], - e.what()); - } - - session.createPeer(peerAddress); - session.setPeerPreFilterMaxRoutes(peerAddress, maxRoutes); - session.saveConfig(); - - return fmt::format( - "Successfully set max-routes for peer '{}' to: {}\n" - "Config saved to: {}", - peerAddress, - maxRoutes, - session.getSessionConfigPath()); -} - -void CmdConfigProtocolBgpPeerMaxRoutes::printOutput(const RetType& output) { - std::cout << output << std::endl; -} - -// Explicit template instantiation -template void CmdHandler< - CmdConfigProtocolBgpPeerMaxRoutes, - CmdConfigProtocolBgpPeerMaxRoutesTraits>::run(); - -} // namespace facebook::fboss diff --git a/fboss/cli/fboss2/commands/config/protocol/bgp/peer/CmdConfigProtocolBgpPeerMaxRoutes.h b/fboss/cli/fboss2/commands/config/protocol/bgp/peer/CmdConfigProtocolBgpPeerMaxRoutes.h deleted file mode 100644 index 4cec005468d9b..0000000000000 --- a/fboss/cli/fboss2/commands/config/protocol/bgp/peer/CmdConfigProtocolBgpPeerMaxRoutes.h +++ /dev/null @@ -1,44 +0,0 @@ -/* - * 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/config/protocol/bgp/peer/CmdConfigProtocolBgpPeer.h" -#include "fboss/cli/fboss2/utils/CmdUtils.h" - -namespace facebook::fboss { - -struct CmdConfigProtocolBgpPeerMaxRoutesTraits : public WriteCommandTraits { - using ParentCmd = CmdConfigProtocolBgpPeer; - static void addCliArg(CLI::App& cmd, std::vector& args) { - cmd.add_option("msg", args, "Message"); - } - using ObjectArgType = utils::Message; - using RetType = std::string; -}; - -class CmdConfigProtocolBgpPeerMaxRoutes - : public CmdHandler< - CmdConfigProtocolBgpPeerMaxRoutes, - CmdConfigProtocolBgpPeerMaxRoutesTraits> { - public: - using ObjectArgType = CmdConfigProtocolBgpPeerMaxRoutesTraits::ObjectArgType; - using RetType = CmdConfigProtocolBgpPeerMaxRoutesTraits::RetType; - - RetType queryClient( - const HostInfo& hostInfo, - const utils::IPList& peerAddress, - const ObjectArgType& maxRoutes); - - void printOutput(const RetType& output); -}; - -} // namespace facebook::fboss diff --git a/fboss/cli/fboss2/commands/config/protocol/bgp/peer/CmdConfigProtocolBgpPeerNextHop4.cpp b/fboss/cli/fboss2/commands/config/protocol/bgp/peer/CmdConfigProtocolBgpPeerNextHop4.cpp deleted file mode 100644 index 2e0846c898931..0000000000000 --- a/fboss/cli/fboss2/commands/config/protocol/bgp/peer/CmdConfigProtocolBgpPeerNextHop4.cpp +++ /dev/null @@ -1,59 +0,0 @@ -/* - * Copyright (c) 2004-present, Facebook, Inc. - * All rights reserved. - * - * This source code is licensed under the BSD-style license found in the - * LICENSE file in the root directory of this source tree. An additional grant - * of patent rights can be found in the PATENTS file in the same directory. - * - */ - -#include "fboss/cli/fboss2/commands/config/protocol/bgp/peer/CmdConfigProtocolBgpPeerNextHop4.h" - -#include "fboss/cli/fboss2/CmdHandler.cpp" - -#include -#include "fboss/cli/fboss2/commands/config/protocol/bgp/BgpConfigSession.h" - -namespace facebook::fboss { - -CmdConfigProtocolBgpPeerNextHop4Traits::RetType -CmdConfigProtocolBgpPeerNextHop4::queryClient( - const HostInfo& /* hostInfo */, - const utils::IPList& peerAddressList, - const ObjectArgType& nextHop4Msg) { - auto& session = BgpConfigSession::getInstance(); - - if (peerAddressList.data().empty()) { - return "Error: peer address is required"; - } - std::string peerAddress = peerAddressList.data()[0]; - - if (nextHop4Msg.data().empty()) { - return fmt::format( - "Error: next-hop4 value required for peer '{}'", peerAddress); - } - std::string nextHop4 = nextHop4Msg.data()[0]; - - session.createPeer(peerAddress); - session.setPeerNextHop4(peerAddress, nextHop4); - session.saveConfig(); - - return fmt::format( - "Successfully set next-hop4 for peer '{}' to: '{}'\n" - "Config saved to: {}", - peerAddress, - nextHop4, - session.getSessionConfigPath()); -} - -void CmdConfigProtocolBgpPeerNextHop4::printOutput(const RetType& output) { - std::cout << output << std::endl; -} - -// Explicit template instantiation -template void CmdHandler< - CmdConfigProtocolBgpPeerNextHop4, - CmdConfigProtocolBgpPeerNextHop4Traits>::run(); - -} // namespace facebook::fboss diff --git a/fboss/cli/fboss2/commands/config/protocol/bgp/peer/CmdConfigProtocolBgpPeerNextHop4.h b/fboss/cli/fboss2/commands/config/protocol/bgp/peer/CmdConfigProtocolBgpPeerNextHop4.h deleted file mode 100644 index 9f84c4b6813ab..0000000000000 --- a/fboss/cli/fboss2/commands/config/protocol/bgp/peer/CmdConfigProtocolBgpPeerNextHop4.h +++ /dev/null @@ -1,44 +0,0 @@ -/* - * 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/config/protocol/bgp/peer/CmdConfigProtocolBgpPeer.h" -#include "fboss/cli/fboss2/utils/CmdUtils.h" - -namespace facebook::fboss { - -struct CmdConfigProtocolBgpPeerNextHop4Traits : public WriteCommandTraits { - using ParentCmd = CmdConfigProtocolBgpPeer; - static void addCliArg(CLI::App& cmd, std::vector& args) { - cmd.add_option("msg", args, "Message"); - } - using ObjectArgType = utils::Message; - using RetType = std::string; -}; - -class CmdConfigProtocolBgpPeerNextHop4 - : public CmdHandler< - CmdConfigProtocolBgpPeerNextHop4, - CmdConfigProtocolBgpPeerNextHop4Traits> { - public: - using ObjectArgType = CmdConfigProtocolBgpPeerNextHop4Traits::ObjectArgType; - using RetType = CmdConfigProtocolBgpPeerNextHop4Traits::RetType; - - RetType queryClient( - const HostInfo& hostInfo, - const utils::IPList& peerAddress, - const ObjectArgType& nextHop4); - - void printOutput(const RetType& output); -}; - -} // namespace facebook::fboss diff --git a/fboss/cli/fboss2/commands/config/protocol/bgp/peer/CmdConfigProtocolBgpPeerNextHop6.cpp b/fboss/cli/fboss2/commands/config/protocol/bgp/peer/CmdConfigProtocolBgpPeerNextHop6.cpp deleted file mode 100644 index da3f6b4a57d9c..0000000000000 --- a/fboss/cli/fboss2/commands/config/protocol/bgp/peer/CmdConfigProtocolBgpPeerNextHop6.cpp +++ /dev/null @@ -1,59 +0,0 @@ -/* - * Copyright (c) 2004-present, Facebook, Inc. - * All rights reserved. - * - * This source code is licensed under the BSD-style license found in the - * LICENSE file in the root directory of this source tree. An additional grant - * of patent rights can be found in the PATENTS file in the same directory. - * - */ - -#include "fboss/cli/fboss2/commands/config/protocol/bgp/peer/CmdConfigProtocolBgpPeerNextHop6.h" - -#include "fboss/cli/fboss2/CmdHandler.cpp" - -#include -#include "fboss/cli/fboss2/commands/config/protocol/bgp/BgpConfigSession.h" - -namespace facebook::fboss { - -CmdConfigProtocolBgpPeerNextHop6Traits::RetType -CmdConfigProtocolBgpPeerNextHop6::queryClient( - const HostInfo& /* hostInfo */, - const utils::IPList& peerAddressList, - const ObjectArgType& nextHop6Msg) { - auto& session = BgpConfigSession::getInstance(); - - if (peerAddressList.data().empty()) { - return "Error: peer address is required"; - } - std::string peerAddress = peerAddressList.data()[0]; - - if (nextHop6Msg.data().empty()) { - return fmt::format( - "Error: next-hop6 value required for peer '{}'", peerAddress); - } - std::string nextHop6 = nextHop6Msg.data()[0]; - - session.createPeer(peerAddress); - session.setPeerNextHop6(peerAddress, nextHop6); - session.saveConfig(); - - return fmt::format( - "Successfully set next-hop6 for peer '{}' to: '{}'\n" - "Config saved to: {}", - peerAddress, - nextHop6, - session.getSessionConfigPath()); -} - -void CmdConfigProtocolBgpPeerNextHop6::printOutput(const RetType& output) { - std::cout << output << std::endl; -} - -// Explicit template instantiation -template void CmdHandler< - CmdConfigProtocolBgpPeerNextHop6, - CmdConfigProtocolBgpPeerNextHop6Traits>::run(); - -} // namespace facebook::fboss diff --git a/fboss/cli/fboss2/commands/config/protocol/bgp/peer/CmdConfigProtocolBgpPeerNextHop6.h b/fboss/cli/fboss2/commands/config/protocol/bgp/peer/CmdConfigProtocolBgpPeerNextHop6.h deleted file mode 100644 index 6c7ba583d3743..0000000000000 --- a/fboss/cli/fboss2/commands/config/protocol/bgp/peer/CmdConfigProtocolBgpPeerNextHop6.h +++ /dev/null @@ -1,44 +0,0 @@ -/* - * 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/config/protocol/bgp/peer/CmdConfigProtocolBgpPeer.h" -#include "fboss/cli/fboss2/utils/CmdUtils.h" - -namespace facebook::fboss { - -struct CmdConfigProtocolBgpPeerNextHop6Traits : public WriteCommandTraits { - using ParentCmd = CmdConfigProtocolBgpPeer; - static void addCliArg(CLI::App& cmd, std::vector& args) { - cmd.add_option("msg", args, "Message"); - } - using ObjectArgType = utils::Message; - using RetType = std::string; -}; - -class CmdConfigProtocolBgpPeerNextHop6 - : public CmdHandler< - CmdConfigProtocolBgpPeerNextHop6, - CmdConfigProtocolBgpPeerNextHop6Traits> { - public: - using ObjectArgType = CmdConfigProtocolBgpPeerNextHop6Traits::ObjectArgType; - using RetType = CmdConfigProtocolBgpPeerNextHop6Traits::RetType; - - RetType queryClient( - const HostInfo& hostInfo, - const utils::IPList& peerAddress, - const ObjectArgType& nextHop6); - - void printOutput(const RetType& output); -}; - -} // namespace facebook::fboss diff --git a/fboss/cli/fboss2/commands/config/protocol/bgp/peer/CmdConfigProtocolBgpPeerNextHopSelf.cpp b/fboss/cli/fboss2/commands/config/protocol/bgp/peer/CmdConfigProtocolBgpPeerNextHopSelf.cpp deleted file mode 100644 index 5e4f9d722770d..0000000000000 --- a/fboss/cli/fboss2/commands/config/protocol/bgp/peer/CmdConfigProtocolBgpPeerNextHopSelf.cpp +++ /dev/null @@ -1,62 +0,0 @@ -/* - * Copyright (c) 2004-present, Facebook, Inc. - * All rights reserved. - * - * This source code is licensed under the BSD-style license found in the - * LICENSE file in the root directory of this source tree. An additional grant - * of patent rights can be found in the PATENTS file in the same directory. - * - */ - -#include "fboss/cli/fboss2/commands/config/protocol/bgp/peer/CmdConfigProtocolBgpPeerNextHopSelf.h" - -#include "fboss/cli/fboss2/CmdHandler.cpp" - -#include -#include "fboss/cli/fboss2/commands/config/protocol/bgp/BgpConfigSession.h" - -namespace facebook::fboss { - -CmdConfigProtocolBgpPeerNextHopSelfTraits::RetType -CmdConfigProtocolBgpPeerNextHopSelf::queryClient( - const HostInfo& /* hostInfo */, - const utils::IPList& peerAddressList, - const ObjectArgType& enableMsg) { - auto& session = BgpConfigSession::getInstance(); - - if (peerAddressList.data().empty()) { - return "Error: peer address is required"; - } - std::string peerAddress = peerAddressList.data()[0]; - - if (enableMsg.data().empty()) { - return fmt::format( - "Error: enable value (true/false) is required for peer '{}'", - peerAddress); - } - - std::string enableStr = enableMsg.data()[0]; - bool enable = (enableStr == "true" || enableStr == "1" || enableStr == "yes"); - - session.createPeer(peerAddress); - session.setPeerNextHopSelf(peerAddress, enable); - session.saveConfig(); - - return fmt::format( - "Successfully {} next-hop-self for peer '{}'\n" - "Config saved to: {}", - enable ? "enabled" : "disabled", - peerAddress, - session.getSessionConfigPath()); -} - -void CmdConfigProtocolBgpPeerNextHopSelf::printOutput(const RetType& output) { - std::cout << output << std::endl; -} - -// Explicit template instantiation -template void CmdHandler< - CmdConfigProtocolBgpPeerNextHopSelf, - CmdConfigProtocolBgpPeerNextHopSelfTraits>::run(); - -} // namespace facebook::fboss diff --git a/fboss/cli/fboss2/commands/config/protocol/bgp/peer/CmdConfigProtocolBgpPeerNextHopSelf.h b/fboss/cli/fboss2/commands/config/protocol/bgp/peer/CmdConfigProtocolBgpPeerNextHopSelf.h deleted file mode 100644 index 3fd450a707441..0000000000000 --- a/fboss/cli/fboss2/commands/config/protocol/bgp/peer/CmdConfigProtocolBgpPeerNextHopSelf.h +++ /dev/null @@ -1,45 +0,0 @@ -/* - * 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/config/protocol/bgp/peer/CmdConfigProtocolBgpPeer.h" -#include "fboss/cli/fboss2/utils/CmdUtils.h" - -namespace facebook::fboss { - -struct CmdConfigProtocolBgpPeerNextHopSelfTraits : public WriteCommandTraits { - using ParentCmd = CmdConfigProtocolBgpPeer; - static void addCliArg(CLI::App& cmd, std::vector& args) { - cmd.add_option("msg", args, "Message"); - } - using ObjectArgType = utils::Message; - using RetType = std::string; -}; - -class CmdConfigProtocolBgpPeerNextHopSelf - : public CmdHandler< - CmdConfigProtocolBgpPeerNextHopSelf, - CmdConfigProtocolBgpPeerNextHopSelfTraits> { - public: - using ObjectArgType = - CmdConfigProtocolBgpPeerNextHopSelfTraits::ObjectArgType; - using RetType = CmdConfigProtocolBgpPeerNextHopSelfTraits::RetType; - - RetType queryClient( - const HostInfo& hostInfo, - const utils::IPList& peerAddress, - const ObjectArgType& enable); - - void printOutput(const RetType& output); -}; - -} // namespace facebook::fboss diff --git a/fboss/cli/fboss2/commands/config/protocol/bgp/peer/CmdConfigProtocolBgpPeerPassive.cpp b/fboss/cli/fboss2/commands/config/protocol/bgp/peer/CmdConfigProtocolBgpPeerPassive.cpp deleted file mode 100644 index afdf292bac5d7..0000000000000 --- a/fboss/cli/fboss2/commands/config/protocol/bgp/peer/CmdConfigProtocolBgpPeerPassive.cpp +++ /dev/null @@ -1,60 +0,0 @@ -/* - * Copyright (c) 2004-present, Facebook, Inc. - * All rights reserved. - * - * This source code is licensed under the BSD-style license found in the - * LICENSE file in the root directory of this source tree. An additional grant - * of patent rights can be found in the PATENTS file in the same directory. - * - */ - -#include "fboss/cli/fboss2/commands/config/protocol/bgp/peer/CmdConfigProtocolBgpPeerPassive.h" - -#include "fboss/cli/fboss2/CmdHandler.cpp" - -#include -#include "fboss/cli/fboss2/commands/config/protocol/bgp/BgpConfigSession.h" - -namespace facebook::fboss { - -CmdConfigProtocolBgpPeerPassiveTraits::RetType -CmdConfigProtocolBgpPeerPassive::queryClient( - const HostInfo& /* hostInfo */, - const utils::IPList& peerAddressList, - const ObjectArgType& enableMsg) { - auto& session = BgpConfigSession::getInstance(); - - if (peerAddressList.data().empty()) { - return "Error: peer address is required"; - } - std::string peerAddress = peerAddressList.data()[0]; - - if (enableMsg.data().empty()) { - return fmt::format( - "Error: enable value required for peer '{}'", peerAddress); - } - std::string enableStr = enableMsg.data()[0]; - bool enable = (enableStr == "true" || enableStr == "1" || enableStr == "yes"); - - session.createPeer(peerAddress); - session.setPeerPassive(peerAddress, enable); - session.saveConfig(); - - return fmt::format( - "Successfully {} passive mode for peer '{}'\n" - "Config saved to: {}", - enable ? "enabled" : "disabled", - peerAddress, - session.getSessionConfigPath()); -} - -void CmdConfigProtocolBgpPeerPassive::printOutput(const RetType& output) { - std::cout << output << std::endl; -} - -// Explicit template instantiation -template void CmdHandler< - CmdConfigProtocolBgpPeerPassive, - CmdConfigProtocolBgpPeerPassiveTraits>::run(); - -} // namespace facebook::fboss diff --git a/fboss/cli/fboss2/commands/config/protocol/bgp/peer/CmdConfigProtocolBgpPeerPassive.h b/fboss/cli/fboss2/commands/config/protocol/bgp/peer/CmdConfigProtocolBgpPeerPassive.h deleted file mode 100644 index fc03203a62d81..0000000000000 --- a/fboss/cli/fboss2/commands/config/protocol/bgp/peer/CmdConfigProtocolBgpPeerPassive.h +++ /dev/null @@ -1,44 +0,0 @@ -/* - * 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/config/protocol/bgp/peer/CmdConfigProtocolBgpPeer.h" -#include "fboss/cli/fboss2/utils/CmdUtils.h" - -namespace facebook::fboss { - -struct CmdConfigProtocolBgpPeerPassiveTraits : public WriteCommandTraits { - using ParentCmd = CmdConfigProtocolBgpPeer; - static void addCliArg(CLI::App& cmd, std::vector& args) { - cmd.add_option("msg", args, "Message"); - } - using ObjectArgType = utils::Message; - using RetType = std::string; -}; - -class CmdConfigProtocolBgpPeerPassive - : public CmdHandler< - CmdConfigProtocolBgpPeerPassive, - CmdConfigProtocolBgpPeerPassiveTraits> { - public: - using ObjectArgType = CmdConfigProtocolBgpPeerPassiveTraits::ObjectArgType; - using RetType = CmdConfigProtocolBgpPeerPassiveTraits::RetType; - - RetType queryClient( - const HostInfo& hostInfo, - const utils::IPList& peerAddress, - const ObjectArgType& enable); - - void printOutput(const RetType& output); -}; - -} // namespace facebook::fboss diff --git a/fboss/cli/fboss2/commands/config/protocol/bgp/peer/CmdConfigProtocolBgpPeerPeerGroup.cpp b/fboss/cli/fboss2/commands/config/protocol/bgp/peer/CmdConfigProtocolBgpPeerPeerGroup.cpp deleted file mode 100644 index 6d39e53a2ad90..0000000000000 --- a/fboss/cli/fboss2/commands/config/protocol/bgp/peer/CmdConfigProtocolBgpPeerPeerGroup.cpp +++ /dev/null @@ -1,56 +0,0 @@ -/* - * Copyright (c) 2004-present, Facebook, Inc. - * All rights reserved. - * - * This source code is licensed under the BSD-style license found in the - * LICENSE file in the root directory of this source tree. An additional grant - * of patent rights can be found in the PATENTS file in the same directory. - * - */ - -#include "fboss/cli/fboss2/commands/config/protocol/bgp/peer/CmdConfigProtocolBgpPeerPeerGroup.h" - -#include "fboss/cli/fboss2/CmdHandler.cpp" - -#include -#include "fboss/cli/fboss2/commands/config/protocol/bgp/BgpConfigSession.h" - -namespace facebook::fboss { - -CmdConfigProtocolBgpPeerPeerGroupTraits::RetType -CmdConfigProtocolBgpPeerPeerGroup::queryClient( - const HostInfo& /* hostInfo */, - const utils::IPList& peerAddressList, - const ObjectArgType& peerGroupNameMsg) { - auto& session = BgpConfigSession::getInstance(); - - if (peerAddressList.data().empty()) { - return "Error: peer address is required"; - } - if (peerGroupNameMsg.data().empty()) { - return "Error: peer-group name is required"; - } - - std::string peerAddress = peerAddressList.data()[0]; - std::string peerGroupName = peerGroupNameMsg.data()[0]; - - session.setPeerGroupName(peerAddress, peerGroupName); - session.saveConfig(); - - return fmt::format( - "Successfully set peer {} peer-group to: {}\nConfig saved to: {}", - peerAddress, - peerGroupName, - session.getSessionConfigPath()); -} - -void CmdConfigProtocolBgpPeerPeerGroup::printOutput(const RetType& output) { - std::cout << output << std::endl; -} - -// Explicit template instantiation -template void CmdHandler< - CmdConfigProtocolBgpPeerPeerGroup, - CmdConfigProtocolBgpPeerPeerGroupTraits>::run(); - -} // namespace facebook::fboss diff --git a/fboss/cli/fboss2/commands/config/protocol/bgp/peer/CmdConfigProtocolBgpPeerPeerGroup.h b/fboss/cli/fboss2/commands/config/protocol/bgp/peer/CmdConfigProtocolBgpPeerPeerGroup.h deleted file mode 100644 index 346af38f9ca42..0000000000000 --- a/fboss/cli/fboss2/commands/config/protocol/bgp/peer/CmdConfigProtocolBgpPeerPeerGroup.h +++ /dev/null @@ -1,44 +0,0 @@ -/* - * 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/config/protocol/bgp/peer/CmdConfigProtocolBgpPeer.h" -#include "fboss/cli/fboss2/utils/CmdUtils.h" - -namespace facebook::fboss { - -struct CmdConfigProtocolBgpPeerPeerGroupTraits : public WriteCommandTraits { - using ParentCmd = CmdConfigProtocolBgpPeer; - static void addCliArg(CLI::App& cmd, std::vector& args) { - cmd.add_option("msg", args, "Message"); - } - using ObjectArgType = utils::Message; - using RetType = std::string; -}; - -class CmdConfigProtocolBgpPeerPeerGroup - : public CmdHandler< - CmdConfigProtocolBgpPeerPeerGroup, - CmdConfigProtocolBgpPeerPeerGroupTraits> { - public: - using ObjectArgType = CmdConfigProtocolBgpPeerPeerGroupTraits::ObjectArgType; - using RetType = CmdConfigProtocolBgpPeerPeerGroupTraits::RetType; - - RetType queryClient( - const HostInfo& hostInfo, - const utils::IPList& peerAddress, - const ObjectArgType& peerGroupName); - - void printOutput(const RetType& output); -}; - -} // namespace facebook::fboss diff --git a/fboss/cli/fboss2/commands/config/protocol/bgp/peer/CmdConfigProtocolBgpPeerPeerId.cpp b/fboss/cli/fboss2/commands/config/protocol/bgp/peer/CmdConfigProtocolBgpPeerPeerId.cpp deleted file mode 100644 index a4ac20d0e5ecd..0000000000000 --- a/fboss/cli/fboss2/commands/config/protocol/bgp/peer/CmdConfigProtocolBgpPeerPeerId.cpp +++ /dev/null @@ -1,58 +0,0 @@ -/* - * Copyright (c) 2004-present, Facebook, Inc. - * All rights reserved. - * - * This source code is licensed under the BSD-style license found in the - * LICENSE file in the root directory of this source tree. An additional grant - * of patent rights can be found in the PATENTS file in the same directory. - * - */ - -#include "fboss/cli/fboss2/commands/config/protocol/bgp/peer/CmdConfigProtocolBgpPeerPeerId.h" - -#include "fboss/cli/fboss2/CmdHandler.cpp" - -#include -#include "fboss/cli/fboss2/commands/config/protocol/bgp/BgpConfigSession.h" - -namespace facebook::fboss { - -CmdConfigProtocolBgpPeerPeerIdTraits::RetType -CmdConfigProtocolBgpPeerPeerId::queryClient( - const HostInfo& /* hostInfo */, - const utils::IPList& peerAddressList, - const ObjectArgType& peerIdMsg) { - auto& session = BgpConfigSession::getInstance(); - - if (peerAddressList.data().empty()) { - return "Error: peer address is required"; - } - std::string peerAddress = peerAddressList.data()[0]; - - if (peerIdMsg.data().empty()) { - return fmt::format( - "Error: peer-id value is required for peer '{}'", peerAddress); - } - - session.createPeer(peerAddress); - session.setPeerPeerId(peerAddress, peerIdMsg.data()[0]); - session.saveConfig(); - - return fmt::format( - "Successfully set peer-id for peer '{}' to: '{}'\n" - "Config saved to: {}", - peerAddress, - peerIdMsg.data()[0], - session.getSessionConfigPath()); -} - -void CmdConfigProtocolBgpPeerPeerId::printOutput(const RetType& output) { - std::cout << output << std::endl; -} - -// Explicit template instantiation -template void CmdHandler< - CmdConfigProtocolBgpPeerPeerId, - CmdConfigProtocolBgpPeerPeerIdTraits>::run(); - -} // namespace facebook::fboss diff --git a/fboss/cli/fboss2/commands/config/protocol/bgp/peer/CmdConfigProtocolBgpPeerPeerId.h b/fboss/cli/fboss2/commands/config/protocol/bgp/peer/CmdConfigProtocolBgpPeerPeerId.h deleted file mode 100644 index 2091ffe5a495a..0000000000000 --- a/fboss/cli/fboss2/commands/config/protocol/bgp/peer/CmdConfigProtocolBgpPeerPeerId.h +++ /dev/null @@ -1,44 +0,0 @@ -/* - * 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/config/protocol/bgp/peer/CmdConfigProtocolBgpPeer.h" -#include "fboss/cli/fboss2/utils/CmdUtils.h" - -namespace facebook::fboss { - -struct CmdConfigProtocolBgpPeerPeerIdTraits : public WriteCommandTraits { - using ParentCmd = CmdConfigProtocolBgpPeer; - static void addCliArg(CLI::App& cmd, std::vector& args) { - cmd.add_option("msg", args, "Message"); - } - using ObjectArgType = utils::Message; - using RetType = std::string; -}; - -class CmdConfigProtocolBgpPeerPeerId - : public CmdHandler< - CmdConfigProtocolBgpPeerPeerId, - CmdConfigProtocolBgpPeerPeerIdTraits> { - public: - using ObjectArgType = CmdConfigProtocolBgpPeerPeerIdTraits::ObjectArgType; - using RetType = CmdConfigProtocolBgpPeerPeerIdTraits::RetType; - - RetType queryClient( - const HostInfo& hostInfo, - const utils::IPList& peerAddress, - const ObjectArgType& peerId); - - void printOutput(const RetType& output); -}; - -} // namespace facebook::fboss diff --git a/fboss/cli/fboss2/commands/config/protocol/bgp/peer/CmdConfigProtocolBgpPeerRemoteAsn.cpp b/fboss/cli/fboss2/commands/config/protocol/bgp/peer/CmdConfigProtocolBgpPeerRemoteAsn.cpp deleted file mode 100644 index dbfb714a3b9e3..0000000000000 --- a/fboss/cli/fboss2/commands/config/protocol/bgp/peer/CmdConfigProtocolBgpPeerRemoteAsn.cpp +++ /dev/null @@ -1,69 +0,0 @@ -/* - * Copyright (c) 2004-present, Facebook, Inc. - * All rights reserved. - * - * This source code is licensed under the BSD-style license found in the - * LICENSE file in the root directory of this source tree. An additional grant - * of patent rights can be found in the PATENTS file in the same directory. - * - */ - -#include "fboss/cli/fboss2/commands/config/protocol/bgp/peer/CmdConfigProtocolBgpPeerRemoteAsn.h" - -#include "fboss/cli/fboss2/CmdHandler.cpp" - -#include -#include -#include "fboss/cli/fboss2/commands/config/protocol/bgp/BgpConfigSession.h" - -namespace facebook::fboss { - -CmdConfigProtocolBgpPeerRemoteAsnTraits::RetType -CmdConfigProtocolBgpPeerRemoteAsn::queryClient( - const HostInfo& /* hostInfo */, - const utils::IPList& peerAddressList, - const ObjectArgType& remoteAsnMsg) { - auto& session = BgpConfigSession::getInstance(); - - if (peerAddressList.data().empty()) { - return "Error: peer address is required"; - } - - if (remoteAsnMsg.data().empty()) { - return "Error: remote-asn value is required"; - } - - // Extract the first IP from the list as peer address - std::string peerAddress = peerAddressList.data()[0]; - - // Extract string and parse to uint64_t (4-byte ASN per RFC 6793) - std::string asnStr = remoteAsnMsg.data()[0]; - uint64_t remoteAsn; - try { - remoteAsn = folly::to(asnStr); - } catch (const std::exception& e) { - return fmt::format( - "Error: Invalid remote-asn value '{}': {}", asnStr, e.what()); - } - - // Set the remote-asn for the peer (internally maps to remote_as_4_byte) - session.setPeerRemoteAsn(peerAddress, remoteAsn); - session.saveConfig(); - - return fmt::format( - "Successfully set BGP peer {} remote-asn to: {}\nConfig saved to: {}", - peerAddress, - remoteAsn, - session.getSessionConfigPath()); -} - -void CmdConfigProtocolBgpPeerRemoteAsn::printOutput(const RetType& output) { - std::cout << output << std::endl; -} - -// Explicit template instantiation -template void CmdHandler< - CmdConfigProtocolBgpPeerRemoteAsn, - CmdConfigProtocolBgpPeerRemoteAsnTraits>::run(); - -} // namespace facebook::fboss diff --git a/fboss/cli/fboss2/commands/config/protocol/bgp/peer/CmdConfigProtocolBgpPeerRemoteAsn.h b/fboss/cli/fboss2/commands/config/protocol/bgp/peer/CmdConfigProtocolBgpPeerRemoteAsn.h deleted file mode 100644 index 76059df98381a..0000000000000 --- a/fboss/cli/fboss2/commands/config/protocol/bgp/peer/CmdConfigProtocolBgpPeerRemoteAsn.h +++ /dev/null @@ -1,44 +0,0 @@ -/* - * 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/config/protocol/bgp/peer/CmdConfigProtocolBgpPeer.h" -#include "fboss/cli/fboss2/utils/CmdUtils.h" - -namespace facebook::fboss { - -struct CmdConfigProtocolBgpPeerRemoteAsnTraits : public WriteCommandTraits { - using ParentCmd = CmdConfigProtocolBgpPeer; - static void addCliArg(CLI::App& cmd, std::vector& args) { - cmd.add_option("msg", args, "Message"); - } - using ObjectArgType = utils::Message; - using RetType = std::string; -}; - -class CmdConfigProtocolBgpPeerRemoteAsn - : public CmdHandler< - CmdConfigProtocolBgpPeerRemoteAsn, - CmdConfigProtocolBgpPeerRemoteAsnTraits> { - public: - using ObjectArgType = CmdConfigProtocolBgpPeerRemoteAsnTraits::ObjectArgType; - using RetType = CmdConfigProtocolBgpPeerRemoteAsnTraits::RetType; - - RetType queryClient( - const HostInfo& hostInfo, - const utils::IPList& peerAddress, - const ObjectArgType& remoteAsn); - - void printOutput(const RetType& output); -}; - -} // namespace facebook::fboss diff --git a/fboss/cli/fboss2/commands/config/protocol/bgp/peer/CmdConfigProtocolBgpPeerRrClient.cpp b/fboss/cli/fboss2/commands/config/protocol/bgp/peer/CmdConfigProtocolBgpPeerRrClient.cpp deleted file mode 100644 index 025e3a4d4c797..0000000000000 --- a/fboss/cli/fboss2/commands/config/protocol/bgp/peer/CmdConfigProtocolBgpPeerRrClient.cpp +++ /dev/null @@ -1,62 +0,0 @@ -/* - * Copyright (c) 2004-present, Facebook, Inc. - * All rights reserved. - * - * This source code is licensed under the BSD-style license found in the - * LICENSE file in the root directory of this source tree. An additional grant - * of patent rights can be found in the PATENTS file in the same directory. - * - */ - -#include "fboss/cli/fboss2/commands/config/protocol/bgp/peer/CmdConfigProtocolBgpPeerRrClient.h" - -#include "fboss/cli/fboss2/CmdHandler.cpp" - -#include -#include "fboss/cli/fboss2/commands/config/protocol/bgp/BgpConfigSession.h" - -namespace facebook::fboss { - -CmdConfigProtocolBgpPeerRrClientTraits::RetType -CmdConfigProtocolBgpPeerRrClient::queryClient( - const HostInfo& /* hostInfo */, - const utils::IPList& peerAddressList, - const ObjectArgType& enableMsg) { - auto& session = BgpConfigSession::getInstance(); - - if (peerAddressList.data().empty()) { - return "Error: peer address is required"; - } - std::string peerAddress = peerAddressList.data()[0]; - - if (enableMsg.data().empty()) { - return fmt::format( - "Error: enable value (true/false) is required for peer '{}'", - peerAddress); - } - - std::string enableStr = enableMsg.data()[0]; - bool enable = (enableStr == "true" || enableStr == "1" || enableStr == "yes"); - - session.createPeer(peerAddress); - session.setPeerRrClient(peerAddress, enable); - session.saveConfig(); - - return fmt::format( - "Successfully {} route-reflector-client for peer '{}'\n" - "Config saved to: {}", - enable ? "enabled" : "disabled", - peerAddress, - session.getSessionConfigPath()); -} - -void CmdConfigProtocolBgpPeerRrClient::printOutput(const RetType& output) { - std::cout << output << std::endl; -} - -// Explicit template instantiation -template void CmdHandler< - CmdConfigProtocolBgpPeerRrClient, - CmdConfigProtocolBgpPeerRrClientTraits>::run(); - -} // namespace facebook::fboss diff --git a/fboss/cli/fboss2/commands/config/protocol/bgp/peer/CmdConfigProtocolBgpPeerRrClient.h b/fboss/cli/fboss2/commands/config/protocol/bgp/peer/CmdConfigProtocolBgpPeerRrClient.h deleted file mode 100644 index 2eaba6e61b7c0..0000000000000 --- a/fboss/cli/fboss2/commands/config/protocol/bgp/peer/CmdConfigProtocolBgpPeerRrClient.h +++ /dev/null @@ -1,44 +0,0 @@ -/* - * 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/config/protocol/bgp/peer/CmdConfigProtocolBgpPeer.h" -#include "fboss/cli/fboss2/utils/CmdUtils.h" - -namespace facebook::fboss { - -struct CmdConfigProtocolBgpPeerRrClientTraits : public WriteCommandTraits { - using ParentCmd = CmdConfigProtocolBgpPeer; - static void addCliArg(CLI::App& cmd, std::vector& args) { - cmd.add_option("msg", args, "Message"); - } - using ObjectArgType = utils::Message; - using RetType = std::string; -}; - -class CmdConfigProtocolBgpPeerRrClient - : public CmdHandler< - CmdConfigProtocolBgpPeerRrClient, - CmdConfigProtocolBgpPeerRrClientTraits> { - public: - using ObjectArgType = CmdConfigProtocolBgpPeerRrClientTraits::ObjectArgType; - using RetType = CmdConfigProtocolBgpPeerRrClientTraits::RetType; - - RetType queryClient( - const HostInfo& hostInfo, - const utils::IPList& peerAddress, - const ObjectArgType& enable); - - void printOutput(const RetType& output); -}; - -} // namespace facebook::fboss diff --git a/fboss/cli/fboss2/commands/config/protocol/bgp/peer/CmdConfigProtocolBgpPeerTimers.cpp b/fboss/cli/fboss2/commands/config/protocol/bgp/peer/CmdConfigProtocolBgpPeerTimers.cpp deleted file mode 100644 index c4a43abf408b8..0000000000000 --- a/fboss/cli/fboss2/commands/config/protocol/bgp/peer/CmdConfigProtocolBgpPeerTimers.cpp +++ /dev/null @@ -1,47 +0,0 @@ -/* - * Copyright (c) 2004-present, Facebook, Inc. - * All rights reserved. - * - * This source code is licensed under the BSD-style license found in the - * LICENSE file in the root directory of this source tree. An additional grant - * of patent rights can be found in the PATENTS file in the same directory. - * - */ - -#include "fboss/cli/fboss2/commands/config/protocol/bgp/peer/CmdConfigProtocolBgpPeerTimers.h" - -#include "fboss/cli/fboss2/CmdHandler.cpp" - -#include - -namespace facebook::fboss { - -CmdConfigProtocolBgpPeerTimersTraits::RetType -CmdConfigProtocolBgpPeerTimers::queryClient( - const HostInfo& /* hostInfo */, - const utils::IPList& peerAddressList) { - if (peerAddressList.data().empty()) { - return "Error: peer address is required"; - } - std::string peerAddress = peerAddressList.data()[0]; - - return fmt::format( - "BGP peer timers configuration for peer '{}'.\n" - "Available subcommands:\n" - " hold-time - Set hold time\n" - " keepalive - Set keepalive interval\n" - " out-delay - Set output delay\n" - " withdraw-unprog-delay - Set withdraw unprog delay", - peerAddress); -} - -void CmdConfigProtocolBgpPeerTimers::printOutput(const RetType& output) { - std::cout << output << std::endl; -} - -// Explicit template instantiation -template void CmdHandler< - CmdConfigProtocolBgpPeerTimers, - CmdConfigProtocolBgpPeerTimersTraits>::run(); - -} // namespace facebook::fboss diff --git a/fboss/cli/fboss2/commands/config/protocol/bgp/peer/CmdConfigProtocolBgpPeerTimers.h b/fboss/cli/fboss2/commands/config/protocol/bgp/peer/CmdConfigProtocolBgpPeerTimers.h deleted file mode 100644 index a3fe71082ab57..0000000000000 --- a/fboss/cli/fboss2/commands/config/protocol/bgp/peer/CmdConfigProtocolBgpPeerTimers.h +++ /dev/null @@ -1,40 +0,0 @@ -/* - * 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/config/protocol/bgp/peer/CmdConfigProtocolBgpPeer.h" -#include "fboss/cli/fboss2/utils/CmdUtils.h" - -namespace facebook::fboss { - -struct CmdConfigProtocolBgpPeerTimersTraits : public WriteCommandTraits { - using ParentCmd = CmdConfigProtocolBgpPeer; - using ObjectArgType = std::monostate; - using RetType = std::string; -}; - -class CmdConfigProtocolBgpPeerTimers - : public CmdHandler< - CmdConfigProtocolBgpPeerTimers, - CmdConfigProtocolBgpPeerTimersTraits> { - public: - using ObjectArgType = CmdConfigProtocolBgpPeerTimersTraits::ObjectArgType; - using RetType = CmdConfigProtocolBgpPeerTimersTraits::RetType; - - RetType queryClient( - const HostInfo& hostInfo, - const utils::IPList& peerAddress); - - void printOutput(const RetType& output); -}; - -} // namespace facebook::fboss diff --git a/fboss/cli/fboss2/commands/config/protocol/bgp/peer/CmdConfigProtocolBgpPeerTimersKeepalive.cpp b/fboss/cli/fboss2/commands/config/protocol/bgp/peer/CmdConfigProtocolBgpPeerTimersKeepalive.cpp deleted file mode 100644 index c6a582ebab8c9..0000000000000 --- a/fboss/cli/fboss2/commands/config/protocol/bgp/peer/CmdConfigProtocolBgpPeerTimersKeepalive.cpp +++ /dev/null @@ -1,70 +0,0 @@ -/* - * Copyright (c) 2004-present, Facebook, Inc. - * All rights reserved. - * - * This source code is licensed under the BSD-style license found in the - * LICENSE file in the root directory of this source tree. An additional grant - * of patent rights can be found in the PATENTS file in the same directory. - * - */ - -#include "fboss/cli/fboss2/commands/config/protocol/bgp/peer/CmdConfigProtocolBgpPeerTimersKeepalive.h" - -#include "fboss/cli/fboss2/CmdHandler.cpp" - -#include -#include "fboss/cli/fboss2/commands/config/protocol/bgp/BgpConfigSession.h" - -namespace facebook::fboss { - -CmdConfigProtocolBgpPeerTimersKeepaliveTraits::RetType -CmdConfigProtocolBgpPeerTimersKeepalive::queryClient( - const HostInfo& /* hostInfo */, - const utils::IPList& peerAddressList, - const ObjectArgType& keepaliveMsg) { - auto& session = BgpConfigSession::getInstance(); - - if (peerAddressList.data().empty()) { - return "Error: peer address is required"; - } - std::string peerAddress = peerAddressList.data()[0]; - - if (keepaliveMsg.data().empty()) { - return fmt::format( - "Error: keepalive value (seconds) is required for peer '{}'", - peerAddress); - } - - int32_t keepalive; - try { - keepalive = std::stoi(keepaliveMsg.data()[0]); - } catch (const std::exception& e) { - return fmt::format( - "Error: invalid keepalive value '{}': {}", - keepaliveMsg.data()[0], - e.what()); - } - - session.createPeer(peerAddress); - session.setPeerKeepalive(peerAddress, keepalive); - session.saveConfig(); - - return fmt::format( - "Successfully set keepalive for peer '{}' to: {} seconds\n" - "Config saved to: {}", - peerAddress, - keepalive, - session.getSessionConfigPath()); -} - -void CmdConfigProtocolBgpPeerTimersKeepalive::printOutput( - const RetType& output) { - std::cout << output << std::endl; -} - -// Explicit template instantiation -template void CmdHandler< - CmdConfigProtocolBgpPeerTimersKeepalive, - CmdConfigProtocolBgpPeerTimersKeepaliveTraits>::run(); - -} // namespace facebook::fboss diff --git a/fboss/cli/fboss2/commands/config/protocol/bgp/peer/CmdConfigProtocolBgpPeerTimersKeepalive.h b/fboss/cli/fboss2/commands/config/protocol/bgp/peer/CmdConfigProtocolBgpPeerTimersKeepalive.h deleted file mode 100644 index 8449f76d2d7fb..0000000000000 --- a/fboss/cli/fboss2/commands/config/protocol/bgp/peer/CmdConfigProtocolBgpPeerTimersKeepalive.h +++ /dev/null @@ -1,46 +0,0 @@ -/* - * 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/config/protocol/bgp/peer/CmdConfigProtocolBgpPeerTimers.h" -#include "fboss/cli/fboss2/utils/CmdUtils.h" - -namespace facebook::fboss { - -struct CmdConfigProtocolBgpPeerTimersKeepaliveTraits - : public WriteCommandTraits { - using ParentCmd = CmdConfigProtocolBgpPeerTimers; - static void addCliArg(CLI::App& cmd, std::vector& args) { - cmd.add_option("msg", args, "Message"); - } - using ObjectArgType = utils::Message; - using RetType = std::string; -}; - -class CmdConfigProtocolBgpPeerTimersKeepalive - : public CmdHandler< - CmdConfigProtocolBgpPeerTimersKeepalive, - CmdConfigProtocolBgpPeerTimersKeepaliveTraits> { - public: - using ObjectArgType = - CmdConfigProtocolBgpPeerTimersKeepaliveTraits::ObjectArgType; - using RetType = CmdConfigProtocolBgpPeerTimersKeepaliveTraits::RetType; - - RetType queryClient( - const HostInfo& hostInfo, - const utils::IPList& peerAddress, - const ObjectArgType& keepalive); - - void printOutput(const RetType& output); -}; - -} // namespace facebook::fboss diff --git a/fboss/cli/fboss2/commands/config/protocol/bgp/peer/CmdConfigProtocolBgpPeerTimersOutDelay.cpp b/fboss/cli/fboss2/commands/config/protocol/bgp/peer/CmdConfigProtocolBgpPeerTimersOutDelay.cpp deleted file mode 100644 index 46cb4cbe18168..0000000000000 --- a/fboss/cli/fboss2/commands/config/protocol/bgp/peer/CmdConfigProtocolBgpPeerTimersOutDelay.cpp +++ /dev/null @@ -1,70 +0,0 @@ -/* - * Copyright (c) 2004-present, Facebook, Inc. - * All rights reserved. - * - * This source code is licensed under the BSD-style license found in the - * LICENSE file in the root directory of this source tree. An additional grant - * of patent rights can be found in the PATENTS file in the same directory. - * - */ - -#include "fboss/cli/fboss2/commands/config/protocol/bgp/peer/CmdConfigProtocolBgpPeerTimersOutDelay.h" - -#include "fboss/cli/fboss2/CmdHandler.cpp" - -#include -#include "fboss/cli/fboss2/commands/config/protocol/bgp/BgpConfigSession.h" - -namespace facebook::fboss { - -CmdConfigProtocolBgpPeerTimersOutDelayTraits::RetType -CmdConfigProtocolBgpPeerTimersOutDelay::queryClient( - const HostInfo& /* hostInfo */, - const utils::IPList& peerAddressList, - const ObjectArgType& outDelayMsg) { - auto& session = BgpConfigSession::getInstance(); - - if (peerAddressList.data().empty()) { - return "Error: peer address is required"; - } - std::string peerAddress = peerAddressList.data()[0]; - - if (outDelayMsg.data().empty()) { - return fmt::format( - "Error: out-delay value (seconds) is required for peer '{}'", - peerAddress); - } - - int32_t outDelay; - try { - outDelay = std::stoi(outDelayMsg.data()[0]); - } catch (const std::exception& e) { - return fmt::format( - "Error: invalid out-delay value '{}': {}", - outDelayMsg.data()[0], - e.what()); - } - - session.createPeer(peerAddress); - session.setPeerOutDelay(peerAddress, outDelay); - session.saveConfig(); - - return fmt::format( - "Successfully set out-delay for peer '{}' to: {} seconds\n" - "Config saved to: {}", - peerAddress, - outDelay, - session.getSessionConfigPath()); -} - -void CmdConfigProtocolBgpPeerTimersOutDelay::printOutput( - const RetType& output) { - std::cout << output << std::endl; -} - -// Explicit template instantiation -template void CmdHandler< - CmdConfigProtocolBgpPeerTimersOutDelay, - CmdConfigProtocolBgpPeerTimersOutDelayTraits>::run(); - -} // namespace facebook::fboss diff --git a/fboss/cli/fboss2/commands/config/protocol/bgp/peer/CmdConfigProtocolBgpPeerTimersOutDelay.h b/fboss/cli/fboss2/commands/config/protocol/bgp/peer/CmdConfigProtocolBgpPeerTimersOutDelay.h deleted file mode 100644 index a4817268b16a1..0000000000000 --- a/fboss/cli/fboss2/commands/config/protocol/bgp/peer/CmdConfigProtocolBgpPeerTimersOutDelay.h +++ /dev/null @@ -1,46 +0,0 @@ -/* - * 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/config/protocol/bgp/peer/CmdConfigProtocolBgpPeerTimers.h" -#include "fboss/cli/fboss2/utils/CmdUtils.h" - -namespace facebook::fboss { - -struct CmdConfigProtocolBgpPeerTimersOutDelayTraits - : public WriteCommandTraits { - using ParentCmd = CmdConfigProtocolBgpPeerTimers; - static void addCliArg(CLI::App& cmd, std::vector& args) { - cmd.add_option("msg", args, "Message"); - } - using ObjectArgType = utils::Message; - using RetType = std::string; -}; - -class CmdConfigProtocolBgpPeerTimersOutDelay - : public CmdHandler< - CmdConfigProtocolBgpPeerTimersOutDelay, - CmdConfigProtocolBgpPeerTimersOutDelayTraits> { - public: - using ObjectArgType = - CmdConfigProtocolBgpPeerTimersOutDelayTraits::ObjectArgType; - using RetType = CmdConfigProtocolBgpPeerTimersOutDelayTraits::RetType; - - RetType queryClient( - const HostInfo& hostInfo, - const utils::IPList& peerAddress, - const ObjectArgType& outDelay); - - void printOutput(const RetType& output); -}; - -} // namespace facebook::fboss diff --git a/fboss/cli/fboss2/commands/config/protocol/bgp/peer/CmdConfigProtocolBgpPeerTimersWithdrawUnprogDelay.cpp b/fboss/cli/fboss2/commands/config/protocol/bgp/peer/CmdConfigProtocolBgpPeerTimersWithdrawUnprogDelay.cpp deleted file mode 100644 index ff95a1dce0d78..0000000000000 --- a/fboss/cli/fboss2/commands/config/protocol/bgp/peer/CmdConfigProtocolBgpPeerTimersWithdrawUnprogDelay.cpp +++ /dev/null @@ -1,70 +0,0 @@ -/* - * Copyright (c) 2004-present, Facebook, Inc. - * All rights reserved. - * - * This source code is licensed under the BSD-style license found in the - * LICENSE file in the root directory of this source tree. An additional grant - * of patent rights can be found in the PATENTS file in the same directory. - * - */ - -#include "fboss/cli/fboss2/commands/config/protocol/bgp/peer/CmdConfigProtocolBgpPeerTimersWithdrawUnprogDelay.h" - -#include "fboss/cli/fboss2/CmdHandler.cpp" - -#include -#include "fboss/cli/fboss2/commands/config/protocol/bgp/BgpConfigSession.h" - -namespace facebook::fboss { - -CmdConfigProtocolBgpPeerTimersWithdrawUnprogDelayTraits::RetType -CmdConfigProtocolBgpPeerTimersWithdrawUnprogDelay::queryClient( - const HostInfo& /* hostInfo */, - const utils::IPList& peerAddressList, - const ObjectArgType& delayMsg) { - auto& session = BgpConfigSession::getInstance(); - - if (peerAddressList.data().empty()) { - return "Error: peer address is required"; - } - std::string peerAddress = peerAddressList.data()[0]; - - if (delayMsg.data().empty()) { - return fmt::format( - "Error: withdraw-unprog-delay value (seconds) is required for peer '{}'", - peerAddress); - } - - int32_t delay; - try { - delay = std::stoi(delayMsg.data()[0]); - } catch (const std::exception& e) { - return fmt::format( - "Error: invalid withdraw-unprog-delay value '{}': {}", - delayMsg.data()[0], - e.what()); - } - - session.createPeer(peerAddress); - session.setPeerWithdrawUnprogDelay(peerAddress, delay); - session.saveConfig(); - - return fmt::format( - "Successfully set withdraw-unprog-delay for peer '{}' to: {} seconds\n" - "Config saved to: {}", - peerAddress, - delay, - session.getSessionConfigPath()); -} - -void CmdConfigProtocolBgpPeerTimersWithdrawUnprogDelay::printOutput( - const RetType& output) { - std::cout << output << std::endl; -} - -// Explicit template instantiation -template void CmdHandler< - CmdConfigProtocolBgpPeerTimersWithdrawUnprogDelay, - CmdConfigProtocolBgpPeerTimersWithdrawUnprogDelayTraits>::run(); - -} // namespace facebook::fboss diff --git a/fboss/cli/fboss2/commands/config/protocol/bgp/peer/CmdConfigProtocolBgpPeerTimersWithdrawUnprogDelay.h b/fboss/cli/fboss2/commands/config/protocol/bgp/peer/CmdConfigProtocolBgpPeerTimersWithdrawUnprogDelay.h deleted file mode 100644 index 47b28cd791ce7..0000000000000 --- a/fboss/cli/fboss2/commands/config/protocol/bgp/peer/CmdConfigProtocolBgpPeerTimersWithdrawUnprogDelay.h +++ /dev/null @@ -1,47 +0,0 @@ -/* - * 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/config/protocol/bgp/peer/CmdConfigProtocolBgpPeerTimers.h" -#include "fboss/cli/fboss2/utils/CmdUtils.h" - -namespace facebook::fboss { - -struct CmdConfigProtocolBgpPeerTimersWithdrawUnprogDelayTraits - : public WriteCommandTraits { - using ParentCmd = CmdConfigProtocolBgpPeerTimers; - static void addCliArg(CLI::App& cmd, std::vector& args) { - cmd.add_option("msg", args, "Message"); - } - using ObjectArgType = utils::Message; - using RetType = std::string; -}; - -class CmdConfigProtocolBgpPeerTimersWithdrawUnprogDelay - : public CmdHandler< - CmdConfigProtocolBgpPeerTimersWithdrawUnprogDelay, - CmdConfigProtocolBgpPeerTimersWithdrawUnprogDelayTraits> { - public: - using ObjectArgType = - CmdConfigProtocolBgpPeerTimersWithdrawUnprogDelayTraits::ObjectArgType; - using RetType = - CmdConfigProtocolBgpPeerTimersWithdrawUnprogDelayTraits::RetType; - - RetType queryClient( - const HostInfo& hostInfo, - const utils::IPList& peerAddress, - const ObjectArgType& withdrawUnprogDelay); - - void printOutput(const RetType& output); -}; - -} // namespace facebook::fboss diff --git a/fboss/cli/fboss2/commands/config/protocol/bgp/peer/CmdConfigProtocolBgpPeerType.cpp b/fboss/cli/fboss2/commands/config/protocol/bgp/peer/CmdConfigProtocolBgpPeerType.cpp deleted file mode 100644 index af460feecbf90..0000000000000 --- a/fboss/cli/fboss2/commands/config/protocol/bgp/peer/CmdConfigProtocolBgpPeerType.cpp +++ /dev/null @@ -1,58 +0,0 @@ -/* - * Copyright (c) 2004-present, Facebook, Inc. - * All rights reserved. - * - * This source code is licensed under the BSD-style license found in the - * LICENSE file in the root directory of this source tree. An additional grant - * of patent rights can be found in the PATENTS file in the same directory. - * - */ - -#include "fboss/cli/fboss2/commands/config/protocol/bgp/peer/CmdConfigProtocolBgpPeerType.h" - -#include "fboss/cli/fboss2/CmdHandler.cpp" - -#include -#include "fboss/cli/fboss2/commands/config/protocol/bgp/BgpConfigSession.h" - -namespace facebook::fboss { - -CmdConfigProtocolBgpPeerTypeTraits::RetType -CmdConfigProtocolBgpPeerType::queryClient( - const HostInfo& /* hostInfo */, - const utils::IPList& peerAddressList, - const ObjectArgType& peerTypeMsg) { - auto& session = BgpConfigSession::getInstance(); - - if (peerAddressList.data().empty()) { - return "Error: peer address is required"; - } - std::string peerAddress = peerAddressList.data()[0]; - - if (peerTypeMsg.data().empty()) { - return fmt::format( - "Error: type value is required for peer '{}'", peerAddress); - } - - session.createPeer(peerAddress); - session.setPeerType(peerAddress, peerTypeMsg.data()[0]); - session.saveConfig(); - - return fmt::format( - "Successfully set type for peer '{}' to: '{}'\n" - "Config saved to: {}", - peerAddress, - peerTypeMsg.data()[0], - session.getSessionConfigPath()); -} - -void CmdConfigProtocolBgpPeerType::printOutput(const RetType& output) { - std::cout << output << std::endl; -} - -// Explicit template instantiation -template void CmdHandler< - CmdConfigProtocolBgpPeerType, - CmdConfigProtocolBgpPeerTypeTraits>::run(); - -} // namespace facebook::fboss diff --git a/fboss/cli/fboss2/commands/config/protocol/bgp/peer/CmdConfigProtocolBgpPeerType.h b/fboss/cli/fboss2/commands/config/protocol/bgp/peer/CmdConfigProtocolBgpPeerType.h deleted file mode 100644 index 8b5a593677bf1..0000000000000 --- a/fboss/cli/fboss2/commands/config/protocol/bgp/peer/CmdConfigProtocolBgpPeerType.h +++ /dev/null @@ -1,43 +0,0 @@ -/* - * 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/config/protocol/bgp/peer/CmdConfigProtocolBgpPeer.h" -#include "fboss/cli/fboss2/utils/CmdUtils.h" - -namespace facebook::fboss { - -struct CmdConfigProtocolBgpPeerTypeTraits : public WriteCommandTraits { - using ParentCmd = CmdConfigProtocolBgpPeer; - static void addCliArg(CLI::App& cmd, std::vector& args) { - cmd.add_option("msg", args, "Message"); - } - using ObjectArgType = utils::Message; - using RetType = std::string; -}; - -class CmdConfigProtocolBgpPeerType : public CmdHandler< - CmdConfigProtocolBgpPeerType, - CmdConfigProtocolBgpPeerTypeTraits> { - public: - using ObjectArgType = CmdConfigProtocolBgpPeerTypeTraits::ObjectArgType; - using RetType = CmdConfigProtocolBgpPeerTypeTraits::RetType; - - RetType queryClient( - const HostInfo& hostInfo, - const utils::IPList& peerAddress, - const ObjectArgType& peerType); - - void printOutput(const RetType& output); -}; - -} // namespace facebook::fboss diff --git a/fboss/cli/fboss2/commands/config/protocol/bgp/peer/CmdConfigProtocolBgpPeerV4OverV6Nh.cpp b/fboss/cli/fboss2/commands/config/protocol/bgp/peer/CmdConfigProtocolBgpPeerV4OverV6Nh.cpp deleted file mode 100644 index 94c3500946321..0000000000000 --- a/fboss/cli/fboss2/commands/config/protocol/bgp/peer/CmdConfigProtocolBgpPeerV4OverV6Nh.cpp +++ /dev/null @@ -1,60 +0,0 @@ -/* - * Copyright (c) 2004-present, Facebook, Inc. - * All rights reserved. - * - * This source code is licensed under the BSD-style license found in the - * LICENSE file in the root directory of this source tree. An additional grant - * of patent rights can be found in the PATENTS file in the same directory. - * - */ - -#include "fboss/cli/fboss2/commands/config/protocol/bgp/peer/CmdConfigProtocolBgpPeerV4OverV6Nh.h" - -#include "fboss/cli/fboss2/CmdHandler.cpp" - -#include -#include "fboss/cli/fboss2/commands/config/protocol/bgp/BgpConfigSession.h" - -namespace facebook::fboss { - -CmdConfigProtocolBgpPeerV4OverV6NhTraits::RetType -CmdConfigProtocolBgpPeerV4OverV6Nh::queryClient( - const HostInfo& /* hostInfo */, - const utils::IPList& peerAddressList, - const ObjectArgType& enableMsg) { - auto& session = BgpConfigSession::getInstance(); - - if (peerAddressList.data().empty()) { - return "Error: peer address is required"; - } - std::string peerAddress = peerAddressList.data()[0]; - - if (enableMsg.data().empty()) { - return fmt::format( - "Error: enable value required for peer '{}'", peerAddress); - } - std::string enableStr = enableMsg.data()[0]; - bool enable = (enableStr == "true" || enableStr == "1" || enableStr == "yes"); - - session.createPeer(peerAddress); - session.setPeerIpv4OverIpv6Nh(peerAddress, enable); - session.saveConfig(); - - return fmt::format( - "Successfully {} v4-over-v6-nexthop for peer '{}'\n" - "Config saved to: {}", - enable ? "enabled" : "disabled", - peerAddress, - session.getSessionConfigPath()); -} - -void CmdConfigProtocolBgpPeerV4OverV6Nh::printOutput(const RetType& output) { - std::cout << output << std::endl; -} - -// Explicit template instantiation -template void CmdHandler< - CmdConfigProtocolBgpPeerV4OverV6Nh, - CmdConfigProtocolBgpPeerV4OverV6NhTraits>::run(); - -} // namespace facebook::fboss diff --git a/fboss/cli/fboss2/commands/config/protocol/bgp/peer/CmdConfigProtocolBgpPeerV4OverV6Nh.h b/fboss/cli/fboss2/commands/config/protocol/bgp/peer/CmdConfigProtocolBgpPeerV4OverV6Nh.h deleted file mode 100644 index af68ab9d07c62..0000000000000 --- a/fboss/cli/fboss2/commands/config/protocol/bgp/peer/CmdConfigProtocolBgpPeerV4OverV6Nh.h +++ /dev/null @@ -1,44 +0,0 @@ -/* - * 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/config/protocol/bgp/peer/CmdConfigProtocolBgpPeer.h" -#include "fboss/cli/fboss2/utils/CmdUtils.h" - -namespace facebook::fboss { - -struct CmdConfigProtocolBgpPeerV4OverV6NhTraits : public WriteCommandTraits { - using ParentCmd = CmdConfigProtocolBgpPeer; - static void addCliArg(CLI::App& cmd, std::vector& args) { - cmd.add_option("msg", args, "Message"); - } - using ObjectArgType = utils::Message; - using RetType = std::string; -}; - -class CmdConfigProtocolBgpPeerV4OverV6Nh - : public CmdHandler< - CmdConfigProtocolBgpPeerV4OverV6Nh, - CmdConfigProtocolBgpPeerV4OverV6NhTraits> { - public: - using ObjectArgType = CmdConfigProtocolBgpPeerV4OverV6NhTraits::ObjectArgType; - using RetType = CmdConfigProtocolBgpPeerV4OverV6NhTraits::RetType; - - RetType queryClient( - const HostInfo& hostInfo, - const utils::IPList& peerAddress, - const ObjectArgType& enable); - - void printOutput(const RetType& output); -}; - -} // namespace facebook::fboss diff --git a/fboss/cli/fboss2/commands/config/protocol/bgp/peer/CmdConfigProtocolBgpPeerWarningLimit.cpp b/fboss/cli/fboss2/commands/config/protocol/bgp/peer/CmdConfigProtocolBgpPeerWarningLimit.cpp deleted file mode 100644 index 6b361f89e0e25..0000000000000 --- a/fboss/cli/fboss2/commands/config/protocol/bgp/peer/CmdConfigProtocolBgpPeerWarningLimit.cpp +++ /dev/null @@ -1,68 +0,0 @@ -/* - * Copyright (c) 2004-present, Facebook, Inc. - * All rights reserved. - * - * This source code is licensed under the BSD-style license found in the - * LICENSE file in the root directory of this source tree. An additional grant - * of patent rights can be found in the PATENTS file in the same directory. - * - */ - -#include "fboss/cli/fboss2/commands/config/protocol/bgp/peer/CmdConfigProtocolBgpPeerWarningLimit.h" - -#include "fboss/cli/fboss2/CmdHandler.cpp" - -#include -#include "fboss/cli/fboss2/commands/config/protocol/bgp/BgpConfigSession.h" - -namespace facebook::fboss { - -CmdConfigProtocolBgpPeerWarningLimitTraits::RetType -CmdConfigProtocolBgpPeerWarningLimit::queryClient( - const HostInfo& /* hostInfo */, - const utils::IPList& peerAddressList, - const ObjectArgType& warningLimitMsg) { - auto& session = BgpConfigSession::getInstance(); - - if (peerAddressList.data().empty()) { - return "Error: peer address is required"; - } - std::string peerAddress = peerAddressList.data()[0]; - - if (warningLimitMsg.data().empty()) { - return fmt::format( - "Error: warning-limit value is required for peer '{}'", peerAddress); - } - - int64_t warningLimit; - try { - warningLimit = std::stoll(warningLimitMsg.data()[0]); - } catch (const std::exception& e) { - return fmt::format( - "Error: invalid warning-limit value '{}': {}", - warningLimitMsg.data()[0], - e.what()); - } - - session.createPeer(peerAddress); - session.setPeerPreFilterWarningLimit(peerAddress, warningLimit); - session.saveConfig(); - - return fmt::format( - "Successfully set pre_filter warning_limit for peer '{}' to: {}\n" - "Config saved to: {}", - peerAddress, - warningLimit, - session.getSessionConfigPath()); -} - -void CmdConfigProtocolBgpPeerWarningLimit::printOutput(const RetType& output) { - std::cout << output << std::endl; -} - -// Explicit template instantiation -template void CmdHandler< - CmdConfigProtocolBgpPeerWarningLimit, - CmdConfigProtocolBgpPeerWarningLimitTraits>::run(); - -} // namespace facebook::fboss diff --git a/fboss/cli/fboss2/commands/config/protocol/bgp/peer/CmdConfigProtocolBgpPeerWarningLimit.h b/fboss/cli/fboss2/commands/config/protocol/bgp/peer/CmdConfigProtocolBgpPeerWarningLimit.h deleted file mode 100644 index 97dbae5059eab..0000000000000 --- a/fboss/cli/fboss2/commands/config/protocol/bgp/peer/CmdConfigProtocolBgpPeerWarningLimit.h +++ /dev/null @@ -1,45 +0,0 @@ -/* - * 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/config/protocol/bgp/peer/CmdConfigProtocolBgpPeer.h" -#include "fboss/cli/fboss2/utils/CmdUtils.h" - -namespace facebook::fboss { - -struct CmdConfigProtocolBgpPeerWarningLimitTraits : public WriteCommandTraits { - using ParentCmd = CmdConfigProtocolBgpPeer; - static void addCliArg(CLI::App& cmd, std::vector& args) { - cmd.add_option("msg", args, "Message"); - } - using ObjectArgType = utils::Message; - using RetType = std::string; -}; - -class CmdConfigProtocolBgpPeerWarningLimit - : public CmdHandler< - CmdConfigProtocolBgpPeerWarningLimit, - CmdConfigProtocolBgpPeerWarningLimitTraits> { - public: - using ObjectArgType = - CmdConfigProtocolBgpPeerWarningLimitTraits::ObjectArgType; - using RetType = CmdConfigProtocolBgpPeerWarningLimitTraits::RetType; - - RetType queryClient( - const HostInfo& hostInfo, - const utils::IPList& peerAddress, - const ObjectArgType& warningLimit); - - void printOutput(const RetType& output); -}; - -} // namespace facebook::fboss diff --git a/fboss/cli/fboss2/commands/config/protocol/bgp/peer/CmdConfigProtocolBgpPeerWarningOnly.cpp b/fboss/cli/fboss2/commands/config/protocol/bgp/peer/CmdConfigProtocolBgpPeerWarningOnly.cpp deleted file mode 100644 index 2871824d4475c..0000000000000 --- a/fboss/cli/fboss2/commands/config/protocol/bgp/peer/CmdConfigProtocolBgpPeerWarningOnly.cpp +++ /dev/null @@ -1,71 +0,0 @@ -/* - * Copyright (c) 2004-present, Facebook, Inc. - * All rights reserved. - * - * This source code is licensed under the BSD-style license found in the - * LICENSE file in the root directory of this source tree. An additional grant - * of patent rights can be found in the PATENTS file in the same directory. - * - */ - -#include "fboss/cli/fboss2/commands/config/protocol/bgp/peer/CmdConfigProtocolBgpPeerWarningOnly.h" - -#include "fboss/cli/fboss2/CmdHandler.cpp" - -#include -#include "fboss/cli/fboss2/commands/config/protocol/bgp/BgpConfigSession.h" - -namespace facebook::fboss { - -CmdConfigProtocolBgpPeerWarningOnlyTraits::RetType -CmdConfigProtocolBgpPeerWarningOnly::queryClient( - const HostInfo& /* hostInfo */, - const utils::IPList& peerAddressList, - const ObjectArgType& warningOnlyMsg) { - auto& session = BgpConfigSession::getInstance(); - - if (peerAddressList.data().empty()) { - return "Error: peer address is required"; - } - std::string peerAddress = peerAddressList.data()[0]; - - if (warningOnlyMsg.data().empty()) { - return fmt::format( - "Error: warning-only value (true|false) is required for peer '{}'", - peerAddress); - } - - std::string value = warningOnlyMsg.data()[0]; - bool warningOnly; - if (value == "true" || value == "1") { - warningOnly = true; - } else if (value == "false" || value == "0") { - warningOnly = false; - } else { - return fmt::format( - "Error: invalid warning-only value '{}', expected 'true' or 'false'", - value); - } - - session.createPeer(peerAddress); - session.setPeerPreFilterWarningOnly(peerAddress, warningOnly); - session.saveConfig(); - - return fmt::format( - "Successfully set pre_filter warning_only for peer '{}' to: {}\n" - "Config saved to: {}", - peerAddress, - warningOnly ? "true" : "false", - session.getSessionConfigPath()); -} - -void CmdConfigProtocolBgpPeerWarningOnly::printOutput(const RetType& output) { - std::cout << output << std::endl; -} - -// Explicit template instantiation -template void CmdHandler< - CmdConfigProtocolBgpPeerWarningOnly, - CmdConfigProtocolBgpPeerWarningOnlyTraits>::run(); - -} // namespace facebook::fboss diff --git a/fboss/cli/fboss2/commands/config/protocol/bgp/peer/CmdConfigProtocolBgpPeerWarningOnly.h b/fboss/cli/fboss2/commands/config/protocol/bgp/peer/CmdConfigProtocolBgpPeerWarningOnly.h deleted file mode 100644 index 22f27198597dd..0000000000000 --- a/fboss/cli/fboss2/commands/config/protocol/bgp/peer/CmdConfigProtocolBgpPeerWarningOnly.h +++ /dev/null @@ -1,45 +0,0 @@ -/* - * 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/config/protocol/bgp/peer/CmdConfigProtocolBgpPeer.h" -#include "fboss/cli/fboss2/utils/CmdUtils.h" - -namespace facebook::fboss { - -struct CmdConfigProtocolBgpPeerWarningOnlyTraits : public WriteCommandTraits { - using ParentCmd = CmdConfigProtocolBgpPeer; - static void addCliArg(CLI::App& cmd, std::vector& args) { - cmd.add_option("msg", args, "Message"); - } - using ObjectArgType = utils::Message; - using RetType = std::string; -}; - -class CmdConfigProtocolBgpPeerWarningOnly - : public CmdHandler< - CmdConfigProtocolBgpPeerWarningOnly, - CmdConfigProtocolBgpPeerWarningOnlyTraits> { - public: - using ObjectArgType = - CmdConfigProtocolBgpPeerWarningOnlyTraits::ObjectArgType; - using RetType = CmdConfigProtocolBgpPeerWarningOnlyTraits::RetType; - - RetType queryClient( - const HostInfo& hostInfo, - const utils::IPList& peerAddress, - const ObjectArgType& warningOnly); - - void printOutput(const RetType& output); -}; - -} // namespace facebook::fboss diff --git a/fboss/cli/fboss2/commands/config/protocol/bgp/policy/CmdConfigProtocolBgpPolicy.cpp b/fboss/cli/fboss2/commands/config/protocol/bgp/policy/CmdConfigProtocolBgpPolicy.cpp new file mode 100644 index 0000000000000..ace33a2b0c2ce --- /dev/null +++ b/fboss/cli/fboss2/commands/config/protocol/bgp/policy/CmdConfigProtocolBgpPolicy.cpp @@ -0,0 +1,30 @@ +/* + * Copyright (c) 2004-present, Facebook, Inc. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + * + */ + +#include "fboss/cli/fboss2/commands/config/protocol/bgp/policy/CmdConfigProtocolBgpPolicy.h" + +#include "fboss/cli/fboss2/CmdHandler.cpp" + +namespace facebook::fboss { + +CmdConfigProtocolBgpPolicyTraits::RetType +CmdConfigProtocolBgpPolicy::queryClient(const HostInfo& /* hostInfo */) { + return "BGP policy configuration. Use subcommands: as-path-list"; +} + +void CmdConfigProtocolBgpPolicy::printOutput(const RetType& output) { + std::cout << output << std::endl; +} + +// Explicit template instantiation +template void +CmdHandler::run(); + +} // namespace facebook::fboss diff --git a/fboss/cli/fboss2/commands/config/protocol/bgp/policy/CmdConfigProtocolBgpPolicy.h b/fboss/cli/fboss2/commands/config/protocol/bgp/policy/CmdConfigProtocolBgpPolicy.h new file mode 100644 index 0000000000000..4830fe77f5545 --- /dev/null +++ b/fboss/cli/fboss2/commands/config/protocol/bgp/policy/CmdConfigProtocolBgpPolicy.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/config/protocol/bgp/CmdConfigProtocolBgp.h" + +namespace facebook::fboss { + +// Grouping node for `config protocol bgp policy ...`. Holds no +// state of its own; the per-object-type dispatchers (as-path-list, and later +// community-list/prefix-list/routing-policy) are its subcommands. Mirrors +// CmdConfigProtocolBgp. +struct CmdConfigProtocolBgpPolicyTraits : public WriteCommandTraits { + using ParentCmd = CmdConfigProtocolBgp; + using ObjectArgType = std::monostate; + using RetType = std::string; +}; + +class CmdConfigProtocolBgpPolicy : public CmdHandler< + CmdConfigProtocolBgpPolicy, + CmdConfigProtocolBgpPolicyTraits> { + public: + using ObjectArgType = CmdConfigProtocolBgpPolicyTraits::ObjectArgType; + using RetType = CmdConfigProtocolBgpPolicyTraits::RetType; + + RetType queryClient(const HostInfo& hostInfo); + + void printOutput(const RetType& output); +}; + +} // namespace facebook::fboss diff --git a/fboss/cli/fboss2/commands/config/protocol/bgp/policy/as-path-list/CmdConfigProtocolBgpPolicyAsPathList.cpp b/fboss/cli/fboss2/commands/config/protocol/bgp/policy/as-path-list/CmdConfigProtocolBgpPolicyAsPathList.cpp new file mode 100644 index 0000000000000..c6c6392be2f0c --- /dev/null +++ b/fboss/cli/fboss2/commands/config/protocol/bgp/policy/as-path-list/CmdConfigProtocolBgpPolicyAsPathList.cpp @@ -0,0 +1,322 @@ +/* + * Copyright (c) 2004-present, Facebook, Inc. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + * + */ + +#include "fboss/cli/fboss2/commands/config/protocol/bgp/policy/as-path-list/CmdConfigProtocolBgpPolicyAsPathList.h" + +#include "fboss/cli/fboss2/CmdHandler.cpp" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "configerator/structs/neteng/bgp_policy/thrift/gen-cpp2/bgp_policy_types.h" +#include "configerator/structs/neteng/bgp_policy/thrift/gen-cpp2/routing_policy_types.h" +#include "fboss/cli/fboss2/commands/config/protocol/bgp/BgpCliAttrHandlers.h" +#include "fboss/cli/fboss2/commands/config/protocol/bgp/BgpCliValueParsers.h" +#include "fboss/cli/fboss2/session/ConfigSession.h" +#include "fboss/cli/fboss2/utils/CmdUtilsCommon.h" +#include "fboss/cli/fboss2/utils/HostInfo.h" +#include "fmt/format.h" + +namespace facebook::fboss { + +namespace { + +// CLI keyword selecting the nested entry, and the attribute names, exactly as +// documented. Kept here so the valid-attribute sets and the handler tables +// stay in sync. +constexpr std::string_view kEntryKeyword = "entry"; +constexpr std::string_view kDescription = "description"; +constexpr std::string_view kAsnRegexp = "asn-regexp"; +constexpr std::string_view kMatchLogic = "match-logic"; + +// match-logic values (routing_policy.MatchValueLogicOperator names). +constexpr std::string_view kMatchLogicEqual = "EQUAL"; +constexpr std::string_view kMatchLogicNotEqual = "NOT_EQUAL"; + +using AsPathList = bgp::bgp_policy::AsPathList; +using AsPathListEntry = bgp::bgp_policy::AsPathListEntry; +using MatchValueLogicOperator = bgp::routing_policy::MatchValueLogicOperator; +using bgpcli::AttrHandler; +using bgpcli::enumAttr; +using bgpcli::joinedStringAttr; +using bgpcli::ok; +using bgpcli::Result; +using bgpcli::Tokens; + +// ---- list-level attribute handlers ---------------------------------------- +const std::map, std::less<>>& +listAttrHandlers() { + static const std::map, std::less<>> + kHandlers = { + {std::string(kDescription), + joinedStringAttr( + kDescription, + [](AsPathList& l, const std::string& v) { + l.description() = v; + })}, + }; + return kHandlers; +} + +// ---- entry-level attribute handlers ---------------------------------------- +const std::map, std::less<>>& +entryAttrHandlers() { + static const std::map, std::less<>> + kHandlers = { + {std::string(kDescription), + joinedStringAttr( + kDescription, + [](AsPathListEntry& e, const std::string& v) { + e.description() = v; + })}, + // The AS path is modelled as an AsPathType union; the CLI sets the + // inline `as_path` (AsPath) arm and its regexp, matching the thrift + // path .as_path.as_path.asn_regexp. The pattern may contain spaces + // (AS-path regexes separate ASNs with spaces, e.g. `^65000 65001$`), + // so the tokens are re-joined rather than required to be single. + {std::string(kAsnRegexp), + joinedStringAttr( + kAsnRegexp, + [](AsPathListEntry& e, const std::string& v) { + e.as_path()->set_as_path().asn_regexp() = v; + })}, + {std::string(kMatchLogic), + enumAttr( + kMatchLogic, + fmt::format("{}|{}", kMatchLogicEqual, kMatchLogicNotEqual), + [](const std::string& s) + -> std::optional { + if (s == kMatchLogicEqual) { + return MatchValueLogicOperator::EQUAL; + } + if (s == kMatchLogicNotEqual) { + return MatchValueLogicOperator::NOT_EQUAL; + } + return std::nullopt; + }, + [](AsPathListEntry& e, MatchValueLogicOperator op) { + e.match_logic_type() = op; + })}, + }; + return kHandlers; +} + +template +std::string validAttrList(const Handlers& handlers) { + std::string out; + for (const auto& [name, _] : handlers) { + if (!out.empty()) { + out += ", "; + } + out += name; + } + return out; +} + +// Find the as-path-list keyed by name, creating it if absent. Setting an +// attribute on a not-yet-created list implicitly creates it, so command +// ordering stays forgiving; a bare `as-path-list ` creates one +// explicitly. AsPathList's only key field is `name`. +AsPathList& findOrCreateList( + bgp::thrift::BgpConfig& cfg, + const std::string& name) { + auto& lists = *cfg.policies().ensure().aspath_lists(); + for (auto& list : lists) { + if (*list.name() == name) { + return list; + } + } + lists.emplace_back(); + auto& list = lists.back(); + list.name() = name; + return list; +} + +// Find the entry keyed by sequence_number within a list, creating it if +// absent. sequence_number is the entry's identity. +AsPathListEntry& findOrCreateEntry(AsPathList& list, int64_t seqNum) { + auto& entries = *list.as_path_list(); + for (auto& entry : entries) { + if (entry.sequence_number().has_value() && + *entry.sequence_number() == seqNum) { + return entry; + } + } + entries.emplace_back(); + auto& entry = entries.back(); + entry.sequence_number() = seqNum; + return entry; +} + +bool asPathListExists( + const bgp::thrift::BgpConfig& cfg, + const std::string& name) { + if (!cfg.policies().has_value()) { + return false; + } + for (const auto& list : *cfg.policies()->aspath_lists()) { + if (*list.name() == name) { + return true; + } + } + return false; +} + +bool asPathListEntryExists(const AsPathList& list, int64_t seqNum) { + for (const auto& entry : *list.as_path_list()) { + if (entry.sequence_number().has_value() && + *entry.sequence_number() == seqNum) { + return true; + } + } + return false; +} + +} // namespace + +// Parse + validate at construction so queryClient stays a thin dispatch. +// Throwing std::invalid_argument is how the framework surfaces arg parse +// errors (same mechanism as BgpPeerGroupConfig). +BgpAsPathListConfig::BgpAsPathListConfig(std::vector v) + : utils::BaseObjectArgType(v) { + if (v.empty()) { + throw std::invalid_argument( + "Error: as-path-list is required, optionally followed by " + "`entry ` and an "); + } + if (v[0].empty()) { + throw std::invalid_argument("Error: as-path-list name must not be empty"); + } + listName_ = v[0]; + + if (v.size() == 1) { + return; // bare `as-path-list `: create the list + } + + // `entry ` selects the nested entry; anything else is a list-level + // attribute. + size_t attrStart = 1; + if (v[1] == kEntryKeyword) { + if (v.size() < 3) { + throw std::invalid_argument("Error: `entry` requires a "); + } + auto seq = bgpcli::parseNonNegInt64(v[2]); + if (!seq) { + throw std::invalid_argument( + fmt::format( + "Error: entry must be a non-negative integer, got '{}'", + v[2])); + } + hasEntry_ = true; + seqNum_ = *seq; + if (v.size() == 3) { + return; // bare `as-path-list entry `: create the entry + } + attrStart = 3; + } + + attr_ = v[attrStart]; + values_.assign(v.begin() + attrStart + 1, v.end()); + + const bool known = hasEntry_ + ? entryAttrHandlers().find(attr_) != entryAttrHandlers().end() + : listAttrHandlers().find(attr_) != listAttrHandlers().end(); + if (!known) { + throw std::invalid_argument( + fmt::format( + "Error: unknown as-path-list {} attribute '{}'. Valid " + "attributes: {}", + hasEntry_ ? "entry" : "list", + attr_, + hasEntry_ ? validAttrList(entryAttrHandlers()) + : validAttrList(listAttrHandlers()))); + } +} + +CmdConfigProtocolBgpPolicyAsPathListTraits::RetType +CmdConfigProtocolBgpPolicyAsPathList::queryClient( + const HostInfo& /* hostInfo */, + const ObjectArgType& args) { + auto& session = ConfigSession::getInstance(); + auto& cfg = session.getBgpConfig(); + const bool listCreated = !asPathListExists(cfg, args.listName()); + auto& list = findOrCreateList(cfg, args.listName()); + + Result result; + if (!args.hasEntry()) { + result = args.attr().empty() + ? ok(listCreated + ? fmt::format( + "Successfully created BGP as-path-list {}", + args.listName()) + : fmt::format( + "BGP as-path-list {} already exists", args.listName())) + // The attribute is guaranteed valid: BgpAsPathListConfig's constructor + // rejects an unknown attribute before we get here. + : listAttrHandlers().find(args.attr())->second(list, args.values()); + } else { + const bool entryCreated = !asPathListEntryExists(list, args.seqNum()); + auto& entry = findOrCreateEntry(list, args.seqNum()); + result = args.attr().empty() + ? ok(entryCreated + ? fmt::format( + "Successfully created BGP as-path-list {} entry {}", + args.listName(), + args.seqNum()) + : fmt::format( + "BGP as-path-list {} entry {} already exists", + args.listName(), + args.seqNum())) + : entryAttrHandlers().find(args.attr())->second(entry, args.values()); + // A rejected value must not leave a phantom entry behind. If the list was + // freshly created too, the list-level rollback below removes it (and the + // entry with it), so only roll back the entry when the list pre-existed. + if (!result.ok && entryCreated && !listCreated) { + list.as_path_list()->pop_back(); + } + } + + if (result.ok) { + if (!args.attr().empty()) { + result.message += args.hasEntry() + ? fmt::format( + " for as-path-list {} entry {}", args.listName(), args.seqNum()) + : fmt::format(" for as-path-list {}", args.listName()); + } + session.saveBgpConfig(); + result.message += + fmt::format("\nConfig saved to: {}", session.getBgpSessionConfigPath()); + } else if (listCreated) { + // Drop the phantom list (and any entry created under it this invocation) + // so a rejected value is not visible to later lookups in the same process. + cfg.policies()->aspath_lists()->pop_back(); + } + return result.message; +} + +void CmdConfigProtocolBgpPolicyAsPathList::printOutput(const RetType& output) { + std::cout << output << std::endl; +} + +// Explicit template instantiation +template void CmdHandler< + CmdConfigProtocolBgpPolicyAsPathList, + CmdConfigProtocolBgpPolicyAsPathListTraits>::run(); + +} // namespace facebook::fboss diff --git a/fboss/cli/fboss2/commands/config/protocol/bgp/policy/as-path-list/CmdConfigProtocolBgpPolicyAsPathList.h b/fboss/cli/fboss2/commands/config/protocol/bgp/policy/as-path-list/CmdConfigProtocolBgpPolicyAsPathList.h new file mode 100644 index 0000000000000..c3f1736a786be --- /dev/null +++ b/fboss/cli/fboss2/commands/config/protocol/bgp/policy/as-path-list/CmdConfigProtocolBgpPolicyAsPathList.h @@ -0,0 +1,98 @@ +/* + * 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 "CLI/App.hpp" +#include "fboss/cli/fboss2/CmdHandler.h" +#include "fboss/cli/fboss2/commands/config/protocol/bgp/policy/CmdConfigProtocolBgpPolicy.h" +#include "fboss/cli/fboss2/utils/CmdUtilsCommon.h" +#include "fboss/cli/fboss2/utils/HostInfo.h" + +namespace facebook::fboss { + +// Parsed `as-path-list [entry ] [ ...]`, +// validated at construction. An AsPathList is keyed by ; within a list, +// `entry ` selects (or creates) a keyed AsPathListEntry. Attributes +// apply to the list (bare ` `) or the entry (` entry +// `) depending on whether an entry key is present. +// +// Grammar (from the FBOSS proposed syntax): +// as-path-list (create/select list) +// as-path-list description (list attribute) +// as-path-list entry (create/select entry) +// as-path-list entry asn-regexp (entry attribute) +// as-path-list entry description (entry attribute) +// as-path-list entry match-logic (entry attribute) +class BgpAsPathListConfig : public utils::BaseObjectArgType { + public: + // NOLINTNEXTLINE(google-explicit-constructor) + /* implicit */ BgpAsPathListConfig(std::vector v); + const std::string& listName() const { + return listName_; + } + bool hasEntry() const { + return hasEntry_; + } + int64_t seqNum() const { + return seqNum_; + } + const std::string& attr() const { + return attr_; + } + const std::vector& values() const { + return values_; + } + const static utils::ObjectArgTypeId id = + utils::ObjectArgTypeId::OBJECT_ARG_TYPE_ID_MESSAGE; + + private: + std::string listName_; + bool hasEntry_{false}; + int64_t seqNum_{0}; + std::string attr_; // matched dispatch key ("" = bare create) + std::vector values_; +}; + +// Single handler for the whole `config protocol bgp policy as-path-list` +// family; dispatch and per-attribute parsing live in the .cpp so a new tunable +// is a one-entry change. Mirrors CmdConfigProtocolBgpPeerGroup, with a second +// (entry) key level. +struct CmdConfigProtocolBgpPolicyAsPathListTraits : public WriteCommandTraits { + using ParentCmd = CmdConfigProtocolBgpPolicy; + static void addCliArg(CLI::App& cmd, std::vector& args) { + // Stops CLI11 from classifying attribute tokens as subcommands once the + // list name is consumed. See CmdConfigProtocolBgpNeighborTraits. + cmd.positionals_at_end(); + cmd.add_option( + "args", args, " [entry ] [ ...]"); + } + using ObjectArgType = BgpAsPathListConfig; + using RetType = std::string; +}; + +class CmdConfigProtocolBgpPolicyAsPathList + : public CmdHandler< + CmdConfigProtocolBgpPolicyAsPathList, + CmdConfigProtocolBgpPolicyAsPathListTraits> { + public: + using ObjectArgType = + CmdConfigProtocolBgpPolicyAsPathListTraits::ObjectArgType; + using RetType = CmdConfigProtocolBgpPolicyAsPathListTraits::RetType; + + RetType queryClient(const HostInfo& hostInfo, const ObjectArgType& args); + + void printOutput(const RetType& output); +}; + +} // namespace facebook::fboss diff --git a/fboss/cli/fboss2/commands/config/session/CmdConfigSessionClear.cpp b/fboss/cli/fboss2/commands/config/session/CmdConfigSessionClear.cpp index e86900b021758..6f130f1e79bd9 100644 --- a/fboss/cli/fboss2/commands/config/session/CmdConfigSessionClear.cpp +++ b/fboss/cli/fboss2/commands/config/session/CmdConfigSessionClear.cpp @@ -28,10 +28,12 @@ CmdConfigSessionClearTraits::RetType CmdConfigSessionClear::queryClient( // getInstance(), which would create a session if one doesn't exist std::string sessionConfigPath = ConfigSession::getSessionConfigPathStatic(); std::string metadataPath = ConfigSession::getSessionMetadataPathStatic(); + std::string bgpConfigPath = ConfigSession::getBgpSessionConfigPathStatic(); std::error_code ec; bool removedConfig = false; bool removedMetadata = false; + bool removedBgpConfig = false; // Remove session config file (~/.fboss2/agent.conf) if (fs::exists(sessionConfigPath)) { @@ -60,7 +62,23 @@ CmdConfigSessionClearTraits::RetType CmdConfigSessionClear::queryClient( removedMetadata = true; } - if (removedConfig || removedMetadata) { + // Remove staged BGP config file (~/.fboss2/bgp_config.json). BGP global edits + // are staged here (alongside any peer edits from BgpConfigSession), so a + // BGP-only session must be cleared too. + if (fs::exists(bgpConfigPath)) { + ec.clear(); + fs::remove(bgpConfigPath, ec); + if (ec) { + throw std::runtime_error( + fmt::format( + "Failed to remove BGP session config file {}: {}", + bgpConfigPath, + ec.message())); + } + removedBgpConfig = true; + } + + if (removedConfig || removedMetadata || removedBgpConfig) { return "Config session cleared successfully."; } return "No config session exists. Nothing to clear."; diff --git a/fboss/cli/fboss2/commands/config/session/CmdConfigSessionCommit.cpp b/fboss/cli/fboss2/commands/config/session/CmdConfigSessionCommit.cpp index 51350b63152a9..178be1c072dc6 100644 --- a/fboss/cli/fboss2/commands/config/session/CmdConfigSessionCommit.cpp +++ b/fboss/cli/fboss2/commands/config/session/CmdConfigSessionCommit.cpp @@ -23,7 +23,7 @@ CmdConfigSessionCommitTraits::RetType CmdConfigSessionCommit::queryClient( const HostInfo& hostInfo) { auto& session = ConfigSession::getInstance(); - if (!session.sessionExists()) { + if (!session.hasActiveSession()) { return "No config session exists. Make a config change first."; } @@ -59,6 +59,11 @@ CmdConfigSessionCommitTraits::RetType CmdConfigSessionCommit::queryClient( fmt::format("{} (warmboot)", serviceName)); } break; + case cli::ConfigActionLevel::BGP_RESTART: + for (const auto& serviceName : serviceNamesList) { + restartedServices.push_back(fmt::format("{} (restart)", serviceName)); + } + break; case cli::ConfigActionLevel::HITLESS: for (const auto& serviceName : serviceNamesList) { reloadedServices.push_back(serviceName); diff --git a/fboss/cli/fboss2/commands/config/session/CmdConfigSessionDiff.cpp b/fboss/cli/fboss2/commands/config/session/CmdConfigSessionDiff.cpp index 2581050523919..784af028a8ae0 100644 --- a/fboss/cli/fboss2/commands/config/session/CmdConfigSessionDiff.cpp +++ b/fboss/cli/fboss2/commands/config/session/CmdConfigSessionDiff.cpp @@ -17,11 +17,13 @@ #include "fboss/cli/fboss2/utils/CmdUtils.h" #include "fboss/cli/fboss2/utils/HostInfo.h" +#include #include #include #include #include #include +#include #include #include #include @@ -32,27 +34,68 @@ namespace facebook::fboss { namespace { -// Helper function to get config content from a revision specifier -// Returns the content and a label for the revision +// Git-relative paths of the two config files tracked in the /etc/coop repo. +constexpr auto kAgentGitRelPath = "cli/agent.conf"; +constexpr auto kBgpGitRelPath = "bgpcpp/bgpcpp.conf"; + +// A diffable config domain. The agent config and the BGP config are tracked in +// the same /etc/coop git repo but live in different files; `config session +// diff` shows whichever domain(s) are staged/relevant. +struct DiffDomain { + std::string name; // "Agent" / "BGP" (section header when >1 domain shown) + std::string gitRelPath; // path in the git repo (e.g. cli/agent.conf) + std::string systemPath; // current live file + std::string sessionPath; // staged session file (~/.fboss2/...) + bool staged; // a session edit is staged for this domain +}; + +std::vector allDomains(ConfigSession& session) { + return { + DiffDomain{ + "Agent", + kAgentGitRelPath, + session.getSystemConfigPath(), + session.getSessionConfigPath(), + session.sessionExists()}, + DiffDomain{ + "BGP", + kBgpGitRelPath, + session.getBgpSystemConfigPath(), + session.getBgpSessionConfigPath(), + session.bgpSessionExists()}, + }; +} + +// Read a file, returning empty content (not an error) when it doesn't exist. +std::string readFileOrEmpty(const std::string& path) { + std::string content; + folly::readFile(path.c_str(), content); + return content; +} + +// Get config content from a revision specifier for a specific domain file. +// "current" reads the live system file. A path absent at the given revision +// (e.g. a commit predating BGP config) is treated as empty content. std::pair getRevisionContent( const std::string& revision, - ConfigSession& session) { - auto& git = session.getGit(); - std::string cliConfigPath = session.getCliConfigPath(); - + const DiffDomain& domain, + Git& git) { if (revision == "current") { - // Read the current live config (via the symlink or directly from cli path) - std::string content; - if (!folly::readFile(cliConfigPath.c_str(), content)) { - throw std::runtime_error( - "Failed to read current config from " + cliConfigPath); - } - return {content, "current live config"}; + return {readFileOrEmpty(domain.systemPath), "current live config"}; } - - // Resolve the commit SHA and get the content from Git std::string resolvedSha = git.resolveRef(revision); - std::string content = git.fileAtRevision(resolvedSha, "cli/agent.conf"); + // Verify the revision is real before treating a missing domain path as empty. + // cli/agent.conf is present in every commit (including the initial one), so a + // genuinely invalid revision throws here and propagates; only a path absent + // at an otherwise-valid revision (e.g. bgpcpp.conf before BGP existed) is + // treated as empty. + git.fileAtRevision(resolvedSha, kAgentGitRelPath); + std::string content; + try { + content = git.fileAtRevision(resolvedSha, domain.gitRelPath); + } catch (const std::exception&) { + content = ""; + } return {content, Git::shortSha1(revision)}; } @@ -116,64 +159,108 @@ std::string executeDiff( } } +// Append a (optionally headered) diff section to the combined output. +void appendSection( + std::string& out, + const std::string& name, + const std::string& body, + bool withHeader) { + if (withHeader) { + if (!out.empty()) { + out += "\n"; + } + out += fmt::format("===== {} config =====\n", name); + } + out += body; + if (!body.empty() && body.back() != '\n') { + out += "\n"; + } +} + } // namespace CmdConfigSessionDiffTraits::RetType CmdConfigSessionDiff::queryClient( const HostInfo& /* hostInfo */, const utils::RevisionList& revisions) { auto& session = ConfigSession::getInstance(); - - std::string systemConfigPath = session.getSystemConfigPath(); - std::string sessionConfigPath = session.getSessionConfigPath(); - - // Mode 1: No arguments - diff session vs current live config + auto& git = session.getGit(); + auto domains = allDomains(session); + + // Modes 1 and 2 both diff each staged domain's session file against some + // "base" (current live config for mode 1; a revision for mode 2). The only + // difference is how the base content+label is obtained, so share the loop. + auto diffStagedDomains = + [&](const std::function( + const DiffDomain&)>& getBase) { + int stagedCount = 0; + for (const auto& d : domains) { + stagedCount += d.staged ? 1 : 0; + } + std::string out; + for (const auto& d : domains) { + if (!d.staged) { + continue; + } + auto [baseContent, baseLabel] = getBase(d); + std::string sessionContent; + if (!folly::readFile(d.sessionPath.c_str(), sessionContent)) { + throw std::runtime_error( + "Failed to read session config from " + d.sessionPath); + } + appendSection( + out, + d.name, + executeDiff( + baseContent, sessionContent, baseLabel, "session config"), + stagedCount > 1); + } + return out; + }; + + // Mode 1: No arguments - diff each staged domain's session vs current live. if (revisions.empty()) { - if (!session.sessionExists()) { + if (!session.hasActiveSession()) { return "No config session exists. Make a config change first."; } - - std::string currentContent; - if (!folly::readFile(systemConfigPath.c_str(), currentContent)) { - throw std::runtime_error( - "Failed to read current config from " + systemConfigPath); - } - - std::string sessionContent; - if (!folly::readFile(sessionConfigPath.c_str(), sessionContent)) { - throw std::runtime_error( - "Failed to read session config from " + sessionConfigPath); - } - - return executeDiff( - currentContent, - sessionContent, - "current live config", - "session config"); + return diffStagedDomains([&](const DiffDomain& d) { + return std::make_pair( + readFileOrEmpty(d.systemPath), std::string("current live config")); + }); } - // Mode 2: One argument - diff session vs specified revision + // Mode 2: One argument - diff each staged domain's session vs a revision. if (revisions.size() == 1) { - if (!session.sessionExists()) { + if (!session.hasActiveSession()) { return "No config session exists. Make a config change first."; } - - auto [revContent, revLabel] = getRevisionContent(revisions[0], session); - - std::string sessionContent; - if (!folly::readFile(sessionConfigPath.c_str(), sessionContent)) { - throw std::runtime_error( - "Failed to read session config from " + sessionConfigPath); - } - - return executeDiff(revContent, sessionContent, revLabel, "session config"); + return diffStagedDomains([&](const DiffDomain& d) { + return getRevisionContent(revisions[0], d, git); + }); } - // Mode 3: Two arguments - diff between two revisions + // Mode 3: Two arguments - diff between two revisions for each domain that + // has content at either revision. if (revisions.size() == 2) { - auto [content1, label1] = getRevisionContent(revisions[0], session); - auto [content2, label2] = getRevisionContent(revisions[1], session); - - return executeDiff(content1, content2, label1, label2); + // Pre-compute each domain's rendered diff section so headers are only added + // when more than one domain is shown. + std::vector> sections; // {name, body} + for (const auto& d : domains) { + auto [c1, l1] = getRevisionContent(revisions[0], d, git); + auto [c2, l2] = getRevisionContent(revisions[1], d, git); + if (c1.empty() && c2.empty()) { + continue; // domain absent at both revisions + } + sections.emplace_back(d.name, executeDiff(c1, c2, l1, l2)); + } + if (sections.empty()) { + return "No config found at the given revisions."; + } + std::string out; + const bool multi = sections.size() > 1; + for (const auto& [name, body] : sections) { + appendSection(out, name, body, multi); + } + return out; } // More than 2 arguments is an error diff --git a/fboss/cli/fboss2/commands/delete/protocol/bgp/CmdDeleteProtocolBgp.cpp b/fboss/cli/fboss2/commands/delete/protocol/bgp/CmdDeleteProtocolBgp.cpp new file mode 100644 index 0000000000000..26fbfb3f972cb --- /dev/null +++ b/fboss/cli/fboss2/commands/delete/protocol/bgp/CmdDeleteProtocolBgp.cpp @@ -0,0 +1,32 @@ +/* + * Copyright (c) 2004-present, Facebook, Inc. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + * + */ + +#include "fboss/cli/fboss2/commands/delete/protocol/bgp/CmdDeleteProtocolBgp.h" +#include +#include + +#include "fboss/cli/fboss2/CmdHandler.cpp" +#include "fboss/cli/fboss2/utils/HostInfo.h" + +namespace facebook::fboss { + +CmdDeleteProtocolBgpTraits::RetType CmdDeleteProtocolBgp::queryClient( + const HostInfo& /* hostInfo */) { + return "Delete BGP configuration objects. Use subcommands: neighbor"; +} + +void CmdDeleteProtocolBgp::printOutput(const RetType& output) { + std::cout << output << std::endl; +} + +template void +CmdHandler::run(); + +} // namespace facebook::fboss diff --git a/fboss/cli/fboss2/commands/delete/protocol/bgp/CmdDeleteProtocolBgp.h b/fboss/cli/fboss2/commands/delete/protocol/bgp/CmdDeleteProtocolBgp.h new file mode 100644 index 0000000000000..a79dfc13580fd --- /dev/null +++ b/fboss/cli/fboss2/commands/delete/protocol/bgp/CmdDeleteProtocolBgp.h @@ -0,0 +1,41 @@ +/* + * 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/commands/delete/protocol/CmdDeleteProtocol.h" +#include "fboss/cli/fboss2/utils/CmdUtilsCommon.h" +#include "fboss/cli/fboss2/utils/HostInfo.h" + +namespace facebook::fboss { + +struct CmdDeleteProtocolBgpTraits : public WriteCommandTraits { + using ParentCmd = CmdDeleteProtocol; + static constexpr utils::ObjectArgTypeId ObjectArgTypeId = + utils::ObjectArgTypeId::OBJECT_ARG_TYPE_ID_NONE; + using ObjectArgType = std::monostate; + using RetType = std::string; +}; + +class CmdDeleteProtocolBgp + : public CmdHandler { + public: + using ObjectArgType = CmdDeleteProtocolBgpTraits::ObjectArgType; + using RetType = CmdDeleteProtocolBgpTraits::RetType; + + RetType queryClient(const HostInfo& hostInfo); + + void printOutput(const RetType& output); +}; + +} // namespace facebook::fboss diff --git a/fboss/cli/fboss2/commands/delete/protocol/bgp/neighbor/CmdDeleteProtocolBgpNeighbor.cpp b/fboss/cli/fboss2/commands/delete/protocol/bgp/neighbor/CmdDeleteProtocolBgpNeighbor.cpp new file mode 100644 index 0000000000000..ff337a33e0aeb --- /dev/null +++ b/fboss/cli/fboss2/commands/delete/protocol/bgp/neighbor/CmdDeleteProtocolBgpNeighbor.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/delete/protocol/bgp/neighbor/CmdDeleteProtocolBgpNeighbor.h" + +#include "fboss/cli/fboss2/CmdHandler.cpp" + +#include +#include +#include +#include +#include +#include +#include +#include "fboss/cli/fboss2/commands/config/protocol/bgp/BgpPeerAddr.h" +#include "fboss/cli/fboss2/session/ConfigSession.h" +#include "fboss/cli/fboss2/utils/CmdUtilsCommon.h" +#include "fboss/cli/fboss2/utils/HostInfo.h" +#include "fmt/format.h" + +namespace facebook::fboss { + +// Parse + validate at construction so queryClient stays a thin dispatch. +// Throwing std::invalid_argument is how the framework surfaces arg parse +// errors (same mechanism as BgpNeighborConfig). +BgpNeighborRef::BgpNeighborRef(std::vector v) + : utils::BaseObjectArgType(v) { + if (v.size() != 1) { + throw std::invalid_argument( + "Error: delete protocol bgp neighbor requires exactly one " + ""); + } + auto normalized = bgpcli::normalizeBgpPeerAddr(v[0]); + if (!normalized) { + throw std::invalid_argument( + fmt::format("Error: Invalid neighbor address '{}'", v[0])); + } + peerAddr_ = std::move(*normalized); +} + +CmdDeleteProtocolBgpNeighborTraits::RetType +CmdDeleteProtocolBgpNeighbor::queryClient( + const HostInfo& /* hostInfo */, + const ObjectArgType& args) { + auto& session = ConfigSession::getInstance(); + auto& cfg = session.getBgpConfig(); + auto it = bgpcli::findBgpPeer(cfg, args.peerAddr()); + if (it == cfg.peers()->end()) { + // Nothing is persisted for an unknown neighbor, so a typo'd delete can't + // stage an unrelated session change. + return fmt::format("Error: BGP neighbor {} not found", args.peerAddr()); + } + cfg.peers()->erase(it); + session.saveBgpConfig(); + return fmt::format( + "Successfully deleted BGP neighbor {}\nConfig saved to: {}", + args.peerAddr(), + session.getBgpSessionConfigPath()); +} + +void CmdDeleteProtocolBgpNeighbor::printOutput(const RetType& output) { + std::cout << output << std::endl; +} + +// Explicit template instantiation +template void CmdHandler< + CmdDeleteProtocolBgpNeighbor, + CmdDeleteProtocolBgpNeighborTraits>::run(); + +} // namespace facebook::fboss diff --git a/fboss/cli/fboss2/commands/delete/protocol/bgp/neighbor/CmdDeleteProtocolBgpNeighbor.h b/fboss/cli/fboss2/commands/delete/protocol/bgp/neighbor/CmdDeleteProtocolBgpNeighbor.h new file mode 100644 index 0000000000000..fa71727255c67 --- /dev/null +++ b/fboss/cli/fboss2/commands/delete/protocol/bgp/neighbor/CmdDeleteProtocolBgpNeighbor.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 +#include +#include "CLI/App.hpp" +#include "fboss/cli/fboss2/CmdHandler.h" +#include "fboss/cli/fboss2/commands/delete/protocol/bgp/CmdDeleteProtocolBgp.h" +#include "fboss/cli/fboss2/utils/CmdUtilsCommon.h" +#include "fboss/cli/fboss2/utils/HostInfo.h" + +namespace facebook::fboss { + +// Parsed `delete protocol bgp neighbor `, validated at +// construction. The address is normalized the same way the config command +// normalizes it, so any spelling of a peer's address deletes that peer. +class BgpNeighborRef : public utils::BaseObjectArgType { + public: + // NOLINTNEXTLINE(google-explicit-constructor) + /* implicit */ BgpNeighborRef(std::vector v); + const std::string& peerAddr() const { + return peerAddr_; + } + const static utils::ObjectArgTypeId id = + utils::ObjectArgTypeId::OBJECT_ARG_TYPE_ID_MESSAGE; + + private: + std::string peerAddr_; +}; + +struct CmdDeleteProtocolBgpNeighborTraits : public WriteCommandTraits { + using ParentCmd = CmdDeleteProtocolBgp; + static void addCliArg(CLI::App& cmd, std::vector& args) { + cmd.add_option("args", args, ""); + } + using ObjectArgType = BgpNeighborRef; + using RetType = std::string; +}; + +class CmdDeleteProtocolBgpNeighbor : public CmdHandler< + CmdDeleteProtocolBgpNeighbor, + CmdDeleteProtocolBgpNeighborTraits> { + public: + using ObjectArgType = CmdDeleteProtocolBgpNeighborTraits::ObjectArgType; + using RetType = CmdDeleteProtocolBgpNeighborTraits::RetType; + + RetType queryClient(const HostInfo& hostInfo, const ObjectArgType& args); + + void printOutput(const RetType& output); +}; + +} // namespace facebook::fboss diff --git a/fboss/cli/fboss2/commands/delete/protocol/bgp/peer-group/CmdDeleteProtocolBgpPeerGroup.cpp b/fboss/cli/fboss2/commands/delete/protocol/bgp/peer-group/CmdDeleteProtocolBgpPeerGroup.cpp new file mode 100644 index 0000000000000..7fbe569d326e1 --- /dev/null +++ b/fboss/cli/fboss2/commands/delete/protocol/bgp/peer-group/CmdDeleteProtocolBgpPeerGroup.cpp @@ -0,0 +1,81 @@ +/* + * Copyright (c) 2004-present, Facebook, Inc. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + * + */ + +#include "fboss/cli/fboss2/commands/delete/protocol/bgp/peer-group/CmdDeleteProtocolBgpPeerGroup.h" + +#include "fboss/cli/fboss2/CmdHandler.cpp" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "fboss/cli/fboss2/session/ConfigSession.h" +#include "fboss/cli/fboss2/utils/CmdUtilsCommon.h" +#include "fboss/cli/fboss2/utils/HostInfo.h" +#include "fmt/format.h" + +namespace facebook::fboss { + +// Parse + validate at construction so queryClient stays a thin dispatch. +// Throwing std::invalid_argument is how the framework surfaces arg parse +// errors (same mechanism as BgpPeerGroupConfig). +BgpPeerGroupRef::BgpPeerGroupRef(std::vector v) + : utils::BaseObjectArgType(v) { + if (v.size() != 1) { + throw std::invalid_argument( + "Error: delete protocol bgp peer-group requires exactly one "); + } + if (v[0].empty()) { + throw std::invalid_argument("Error: peer-group name must not be empty"); + } + groupName_ = std::move(v[0]); +} + +CmdDeleteProtocolBgpPeerGroupTraits::RetType +CmdDeleteProtocolBgpPeerGroup::queryClient( + const HostInfo& /* hostInfo */, + const ObjectArgType& args) { + auto& session = ConfigSession::getInstance(); + auto& cfg = session.getBgpConfig(); + if (!cfg.peer_groups().has_value()) { + // Nothing is persisted for an unknown group, so a typo'd delete can't + // stage an unrelated session change. + return fmt::format("Error: BGP peer-group {} not found", args.groupName()); + } + auto& groups = *cfg.peer_groups(); + auto it = std::find_if(groups.begin(), groups.end(), [&](const auto& group) { + return *group.name() == args.groupName(); + }); + if (it == groups.end()) { + return fmt::format("Error: BGP peer-group {} not found", args.groupName()); + } + groups.erase(it); + session.saveBgpConfig(); + return fmt::format( + "Successfully deleted BGP peer-group {}\nConfig saved to: {}", + args.groupName(), + session.getBgpSessionConfigPath()); +} + +void CmdDeleteProtocolBgpPeerGroup::printOutput(const RetType& output) { + std::cout << output << std::endl; +} + +// Explicit template instantiation +template void CmdHandler< + CmdDeleteProtocolBgpPeerGroup, + CmdDeleteProtocolBgpPeerGroupTraits>::run(); + +} // namespace facebook::fboss diff --git a/fboss/cli/fboss2/commands/delete/protocol/bgp/peer-group/CmdDeleteProtocolBgpPeerGroup.h b/fboss/cli/fboss2/commands/delete/protocol/bgp/peer-group/CmdDeleteProtocolBgpPeerGroup.h new file mode 100644 index 0000000000000..c9793226e9a24 --- /dev/null +++ b/fboss/cli/fboss2/commands/delete/protocol/bgp/peer-group/CmdDeleteProtocolBgpPeerGroup.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 +#include +#include "CLI/App.hpp" +#include "fboss/cli/fboss2/CmdHandler.h" +#include "fboss/cli/fboss2/commands/delete/protocol/bgp/CmdDeleteProtocolBgp.h" +#include "fboss/cli/fboss2/utils/CmdUtilsCommon.h" +#include "fboss/cli/fboss2/utils/HostInfo.h" + +namespace facebook::fboss { + +// Parsed `delete protocol bgp peer-group `, validated at construction. +// The group name is the group's identity (an arbitrary string), matched +// exactly the same way the config command stores it. Mirrors BgpNeighborRef. +class BgpPeerGroupRef : public utils::BaseObjectArgType { + public: + // NOLINTNEXTLINE(google-explicit-constructor) + /* implicit */ BgpPeerGroupRef(std::vector v); + const std::string& groupName() const { + return groupName_; + } + const static utils::ObjectArgTypeId id = + utils::ObjectArgTypeId::OBJECT_ARG_TYPE_ID_MESSAGE; + + private: + std::string groupName_; +}; + +struct CmdDeleteProtocolBgpPeerGroupTraits : public WriteCommandTraits { + using ParentCmd = CmdDeleteProtocolBgp; + static void addCliArg(CLI::App& cmd, std::vector& args) { + cmd.add_option("args", args, ""); + } + using ObjectArgType = BgpPeerGroupRef; + using RetType = std::string; +}; + +class CmdDeleteProtocolBgpPeerGroup : public CmdHandler< + CmdDeleteProtocolBgpPeerGroup, + CmdDeleteProtocolBgpPeerGroupTraits> { + public: + using ObjectArgType = CmdDeleteProtocolBgpPeerGroupTraits::ObjectArgType; + using RetType = CmdDeleteProtocolBgpPeerGroupTraits::RetType; + + RetType queryClient(const HostInfo& hostInfo, const ObjectArgType& args); + + void printOutput(const RetType& output); +}; + +} // namespace facebook::fboss diff --git a/fboss/cli/fboss2/commands/delete/protocol/bgp/policy/CmdDeleteProtocolBgpPolicy.cpp b/fboss/cli/fboss2/commands/delete/protocol/bgp/policy/CmdDeleteProtocolBgpPolicy.cpp new file mode 100644 index 0000000000000..bc15db0a326f4 --- /dev/null +++ b/fboss/cli/fboss2/commands/delete/protocol/bgp/policy/CmdDeleteProtocolBgpPolicy.cpp @@ -0,0 +1,32 @@ +/* + * Copyright (c) 2004-present, Facebook, Inc. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + * + */ + +#include "fboss/cli/fboss2/commands/delete/protocol/bgp/policy/CmdDeleteProtocolBgpPolicy.h" +#include +#include + +#include "fboss/cli/fboss2/CmdHandler.cpp" +#include "fboss/cli/fboss2/utils/HostInfo.h" + +namespace facebook::fboss { + +CmdDeleteProtocolBgpPolicyTraits::RetType +CmdDeleteProtocolBgpPolicy::queryClient(const HostInfo& /* hostInfo */) { + return "Delete BGP policy objects. Use subcommands: as-path-list"; +} + +void CmdDeleteProtocolBgpPolicy::printOutput(const RetType& output) { + std::cout << output << std::endl; +} + +template void +CmdHandler::run(); + +} // namespace facebook::fboss diff --git a/fboss/cli/fboss2/commands/delete/protocol/bgp/policy/CmdDeleteProtocolBgpPolicy.h b/fboss/cli/fboss2/commands/delete/protocol/bgp/policy/CmdDeleteProtocolBgpPolicy.h new file mode 100644 index 0000000000000..d658fee78a4ba --- /dev/null +++ b/fboss/cli/fboss2/commands/delete/protocol/bgp/policy/CmdDeleteProtocolBgpPolicy.h @@ -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. + * + */ + +#pragma once + +#include +#include "fboss/cli/fboss2/CmdHandler.h" +#include "fboss/cli/fboss2/commands/delete/protocol/bgp/CmdDeleteProtocolBgp.h" +#include "fboss/cli/fboss2/utils/CmdUtilsCommon.h" +#include "fboss/cli/fboss2/utils/HostInfo.h" + +namespace facebook::fboss { + +// Grouping node for `delete protocol bgp policy `. Holds no +// state of its own; the per-object-type delete commands are its subcommands. +struct CmdDeleteProtocolBgpPolicyTraits : public WriteCommandTraits { + using ParentCmd = CmdDeleteProtocolBgp; + static constexpr utils::ObjectArgTypeId ObjectArgTypeId = + utils::ObjectArgTypeId::OBJECT_ARG_TYPE_ID_NONE; + using ObjectArgType = std::monostate; + using RetType = std::string; +}; + +class CmdDeleteProtocolBgpPolicy : public CmdHandler< + CmdDeleteProtocolBgpPolicy, + CmdDeleteProtocolBgpPolicyTraits> { + public: + using ObjectArgType = CmdDeleteProtocolBgpPolicyTraits::ObjectArgType; + using RetType = CmdDeleteProtocolBgpPolicyTraits::RetType; + + RetType queryClient(const HostInfo& hostInfo); + + void printOutput(const RetType& output); +}; + +} // namespace facebook::fboss diff --git a/fboss/cli/fboss2/commands/delete/protocol/bgp/policy/as-path-list/CmdDeleteProtocolBgpPolicyAsPathList.cpp b/fboss/cli/fboss2/commands/delete/protocol/bgp/policy/as-path-list/CmdDeleteProtocolBgpPolicyAsPathList.cpp new file mode 100644 index 0000000000000..b9f3bfb68dab5 --- /dev/null +++ b/fboss/cli/fboss2/commands/delete/protocol/bgp/policy/as-path-list/CmdDeleteProtocolBgpPolicyAsPathList.cpp @@ -0,0 +1,80 @@ +/* + * Copyright (c) 2004-present, Facebook, Inc. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + * + */ + +#include "fboss/cli/fboss2/commands/delete/protocol/bgp/policy/as-path-list/CmdDeleteProtocolBgpPolicyAsPathList.h" + +#include "fboss/cli/fboss2/CmdHandler.cpp" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "fboss/cli/fboss2/session/ConfigSession.h" +#include "fboss/cli/fboss2/utils/CmdUtilsCommon.h" +#include "fboss/cli/fboss2/utils/HostInfo.h" +#include "fmt/format.h" + +namespace facebook::fboss { + +// Parse + validate at construction so queryClient stays a thin dispatch. +BgpAsPathListRef::BgpAsPathListRef(std::vector v) + : utils::BaseObjectArgType(v) { + if (v.size() != 1) { + throw std::invalid_argument( + "Error: delete protocol bgp policy as-path-list requires exactly one " + ""); + } + if (v[0].empty()) { + throw std::invalid_argument("Error: as-path-list name must not be empty"); + } + listName_ = std::move(v[0]); +} + +CmdDeleteProtocolBgpPolicyAsPathListTraits::RetType +CmdDeleteProtocolBgpPolicyAsPathList::queryClient( + const HostInfo& /* hostInfo */, + const ObjectArgType& args) { + auto& session = ConfigSession::getInstance(); + auto& cfg = session.getBgpConfig(); + if (!cfg.policies().has_value()) { + // Nothing is persisted for an unknown list, so a typo'd delete can't stage + // an unrelated session change. + return fmt::format("Error: BGP as-path-list {} not found", args.listName()); + } + auto& lists = *cfg.policies()->aspath_lists(); + auto it = std::find_if(lists.begin(), lists.end(), [&](const auto& list) { + return *list.name() == args.listName(); + }); + if (it == lists.end()) { + return fmt::format("Error: BGP as-path-list {} not found", args.listName()); + } + lists.erase(it); + session.saveBgpConfig(); + return fmt::format( + "Successfully deleted BGP as-path-list {}\nConfig saved to: {}", + args.listName(), + session.getBgpSessionConfigPath()); +} + +void CmdDeleteProtocolBgpPolicyAsPathList::printOutput(const RetType& output) { + std::cout << output << std::endl; +} + +// Explicit template instantiation +template void CmdHandler< + CmdDeleteProtocolBgpPolicyAsPathList, + CmdDeleteProtocolBgpPolicyAsPathListTraits>::run(); + +} // namespace facebook::fboss diff --git a/fboss/cli/fboss2/commands/delete/protocol/bgp/policy/as-path-list/CmdDeleteProtocolBgpPolicyAsPathList.h b/fboss/cli/fboss2/commands/delete/protocol/bgp/policy/as-path-list/CmdDeleteProtocolBgpPolicyAsPathList.h new file mode 100644 index 0000000000000..4249bdd3546a4 --- /dev/null +++ b/fboss/cli/fboss2/commands/delete/protocol/bgp/policy/as-path-list/CmdDeleteProtocolBgpPolicyAsPathList.h @@ -0,0 +1,63 @@ +/* + * Copyright (c) 2004-present, Facebook, Inc. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + * + */ + +#pragma once + +#include +#include +#include "CLI/App.hpp" +#include "fboss/cli/fboss2/CmdHandler.h" +#include "fboss/cli/fboss2/commands/delete/protocol/bgp/policy/CmdDeleteProtocolBgpPolicy.h" +#include "fboss/cli/fboss2/utils/CmdUtilsCommon.h" +#include "fboss/cli/fboss2/utils/HostInfo.h" + +namespace facebook::fboss { + +// Parsed `delete protocol bgp policy as-path-list `, validated at +// construction. The name is the list's identity, matched exactly the same way +// the config command stores it. Mirrors BgpPeerGroupRef. +class BgpAsPathListRef : public utils::BaseObjectArgType { + public: + // NOLINTNEXTLINE(google-explicit-constructor) + /* implicit */ BgpAsPathListRef(std::vector v); + const std::string& listName() const { + return listName_; + } + const static utils::ObjectArgTypeId id = + utils::ObjectArgTypeId::OBJECT_ARG_TYPE_ID_MESSAGE; + + private: + std::string listName_; +}; + +struct CmdDeleteProtocolBgpPolicyAsPathListTraits : public WriteCommandTraits { + using ParentCmd = CmdDeleteProtocolBgpPolicy; + static void addCliArg(CLI::App& cmd, std::vector& args) { + cmd.add_option("args", args, ""); + } + using ObjectArgType = BgpAsPathListRef; + using RetType = std::string; +}; + +class CmdDeleteProtocolBgpPolicyAsPathList + : public CmdHandler< + CmdDeleteProtocolBgpPolicyAsPathList, + CmdDeleteProtocolBgpPolicyAsPathListTraits> { + public: + using ObjectArgType = + CmdDeleteProtocolBgpPolicyAsPathListTraits::ObjectArgType; + using RetType = CmdDeleteProtocolBgpPolicyAsPathListTraits::RetType; + + RetType queryClient(const HostInfo& hostInfo, const ObjectArgType& args); + + void printOutput(const RetType& output); +}; + +} // namespace facebook::fboss diff --git a/fboss/cli/fboss2/docs/BGP_CLI_TEST_PLAN.md b/fboss/cli/fboss2/docs/BGP_CLI_TEST_PLAN.md index 65cb71b350d0c..37a64fd61426b 100644 --- a/fboss/cli/fboss2/docs/BGP_CLI_TEST_PLAN.md +++ b/fboss/cli/fboss2/docs/BGP_CLI_TEST_PLAN.md @@ -79,10 +79,10 @@ fboss2 config protocol bgp peer-group RSW-FSW-V6 timers keepalive 10 fboss2 config protocol bgp peer-group RSW-RTSW-V6 disable-ipv4-afi true # Peer configuration (with explicit disable_ipv4_afi: false preserved) -fboss2 config protocol bgp peer 2401:db00:501c::/64 remote-asn 65000 -fboss2 config protocol bgp peer 2401:db00:501c::/64 local-addr 2401:db00:501c::a -fboss2 config protocol bgp peer 2401:db00:501c::/64 passive true -fboss2 config protocol bgp peer 2401:db00:501c::/64 disable-ipv4-afi false +fboss2 config protocol bgp neighbor 2401:db00:501c::/64 remote-asn 65000 +fboss2 config protocol bgp neighbor 2401:db00:501c::/64 bind-addr address 2401:db00:501c::a +fboss2 config protocol bgp neighbor 2401:db00:501c::/64 connect-mode PASSIVE +fboss2 config protocol bgp neighbor 2401:db00:501c::/64 afi disable-ipv4-afi false ``` ### Step 5: Execute Script @@ -136,12 +136,13 @@ diff <(jq -S . /tmp/rsw_bgp_nao.txt) <(jq -S . ~/.fboss2/bgp_config.json) | Peer Group | `config protocol bgp peer-group timers hold-time ` | Set hold time | | Peer Group | `config protocol bgp peer-group timers keepalive ` | Set keepalive interval | | Peer Group | `config protocol bgp peer-group max-routes ` | Set max routes | -| Peer | `config protocol bgp peer remote-asn ` | Set remote AS | -| Peer | `config protocol bgp peer peer-group ` | Assign to peer group | -| Peer | `config protocol bgp peer local-addr ` | Set local address | -| Peer | `config protocol bgp peer passive ` | Set passive mode | -| Peer | `config protocol bgp peer disable-ipv4-afi ` | Disable IPv4 AFI | -| Peer | `config protocol bgp peer link-bandwidth ` | Set link bandwidth | +| Neighbor | `config protocol bgp neighbor remote-asn ` | Set remote AS | +| Neighbor | `config protocol bgp neighbor peer-group ` | Assign to peer group | +| Neighbor | `config protocol bgp neighbor bind-addr address ` | Set local address | +| Neighbor | `config protocol bgp neighbor connect-mode ` | Set connect mode | +| Neighbor | `delete protocol bgp neighbor ` | Delete a neighbor | +| Neighbor | `config protocol bgp neighbor afi disable-ipv4-afi ` | Disable IPv4 AFI | +| Neighbor | `config protocol bgp neighbor link-bandwidth ` | Set link bandwidth | ## Automated Test Script diff --git a/fboss/cli/fboss2/session/ConfigSession.cpp b/fboss/cli/fboss2/session/ConfigSession.cpp index a04b3af1bb24b..aea3bcfc941dc 100644 --- a/fboss/cli/fboss2/session/ConfigSession.cpp +++ b/fboss/cli/fboss2/session/ConfigSession.cpp @@ -30,6 +30,7 @@ #include #include #include +#include #include #include #include @@ -356,6 +357,22 @@ std::string ConfigSession::getSessionMetadataPathStatic() { return getSessionDir() + "/cli_metadata.json"; } +std::string ConfigSession::getBgpSessionConfigPathStatic() { + return getSessionDir() + "/bgp_config.json"; +} + +std::string ConfigSession::fileAtRevisionOrEmpty( + const std::string& revision, + const std::string& gitRelPath) const { + try { + return git_->fileAtRevision(revision, gitRelPath); + } catch (const std::exception&) { + // The path did not exist at that revision (e.g. a commit predating BGP + // config). Treat as empty. + return ""; + } +} + std::string ConfigSession::getSessionConfigPath() const { return sessionConfigDir_ + "/agent.conf"; } @@ -376,6 +393,14 @@ bool ConfigSession::sessionExists() const { return fs::exists(getSessionConfigPath()); } +bool ConfigSession::hasActiveSession() const { + // An agent config session (agent.conf) OR a protocol session staged outside + // agent.conf. BGP is the latter: a staged ~/.fboss2/bgp_config.json (written + // by either the typed global config here or BgpConfigSession's peer edits) + // with a recorded BGP_RESTART action, but never touching agent.conf. + return sessionExists() || bgpSessionExists(); +} + cfg::AgentConfig& ConfigSession::getAgentConfig() { if (!configLoaded_) { loadConfig(); @@ -466,6 +491,119 @@ void ConfigSession::saveConfig() { saveConfig(cli::ServiceType::AGENT, cli::ConfigActionLevel::HITLESS); } +void ConfigSession::recordServiceAction( + cli::ServiceType service, + cli::ConfigActionLevel actionLevel) { + // Record the command from /proc/self/cmdline so it shows up in this + // session's command history, exactly like saveConfig() does. Config for + // this service lives in a separate file (e.g. ~/.fboss2/bgp_config.json), + // so we deliberately do NOT touch the agent config here. + std::string rawCmd = readCommandLineFromProc(); + auto pos = rawCmd.find("config "); + if (pos != std::string::npos) { + std::string cmd = rawCmd.substr(pos); + if (commands_.empty() || commands_.back() != cmd) { + commands_.push_back(cmd); + } + } + + // Update and persist the required action level for this service. + updateRequiredAction(service, actionLevel); + saveMetadata(); +} + +std::string ConfigSession::getBgpSessionConfigPath() const { + return sessionConfigDir_ + "/bgp_config.json"; +} + +std::string ConfigSession::getBgpSystemConfigDir() const { + return systemConfigDir_ + "/bgpcpp"; +} + +std::string ConfigSession::getBgpSystemConfigPath() const { + return getBgpSystemConfigDir() + "/bgpcpp.conf"; +} + +std::string ConfigSession::getBgpSystemConfigLinkPath() const { + return systemConfigDir_ + "/bgpcpp.conf"; +} + +bool ConfigSession::bgpSessionExists() const { + return fs::exists(getBgpSessionConfigPath()); +} + +void ConfigSession::loadBgpConfig() { + if (bgpConfigLoaded_) { + return; + } + + // Prefer staged edits; otherwise seed from the running bgpd config; else + // schema defaults. A read failure on a file that exists is logged (not + // silently treated as "no config") so a permission/IO error doesn't + // masquerade as a fresh session. + std::string content; + std::string sessionPath = getBgpSessionConfigPath(); + std::string systemPath = getBgpSystemConfigPath(); + if (fs::exists(sessionPath)) { + if (!folly::readFile(sessionPath.c_str(), content)) { + LOG(WARNING) << "Failed to read staged BGP config " << sessionPath + << "; starting from defaults"; + } + } else if (fs::exists(systemPath)) { + if (!folly::readFile(systemPath.c_str(), content)) { + LOG(WARNING) << "Failed to read system BGP config " << systemPath + << "; starting from defaults"; + } + } + + bgpConfig_ = bgp::thrift::BgpConfig(); + if (!content.empty()) { + try { + apache::thrift::SimpleJSONSerializer::deserialize( + content, bgpConfig_); + } catch (const std::exception& ex) { + LOG(WARNING) << "Failed to parse BGP config, starting from defaults: " + << ex.what(); + bgpConfig_ = bgp::thrift::BgpConfig(); + } + } + bgpConfigLoaded_ = true; +} + +bgp::thrift::BgpConfig& ConfigSession::getBgpConfig() { + if (!bgpConfigLoaded_) { + loadBgpConfig(); + } + return bgpConfig_; +} + +const bgp::thrift::BgpConfig& ConfigSession::getBgpConfig() const { + if (!bgpConfigLoaded_) { + throw std::runtime_error( + "BGP config not loaded yet. Call getBgpConfig() (non-const) first."); + } + return bgpConfig_; +} + +void ConfigSession::saveBgpConfig() { + if (!bgpConfigLoaded_) { + loadBgpConfig(); + } + + // Serialize the entire typed config (round-tripped through parse so integer + // map keys become string keys, mirroring saveConfig() for the agent). + auto json = + apache::thrift::SimpleJSONSerializer::serialize(bgpConfig_); + std::string prettyJson = folly::toPrettyJson(folly::parseJson(json)); + folly::writeFileAtomic( + getBgpSessionConfigPath(), prettyJson, 0644, folly::SyncType::WITH_SYNC); + + // Record the command (mirrors saveConfig) and that bgpd must be restarted + // for this change to take effect on a subsequent `config session commit`. + recordServiceAction( + cli::ServiceType::BGP, cli::ConfigActionLevel::BGP_RESTART); +} + Git& ConfigSession::getGit() { return *git_; } @@ -625,6 +763,7 @@ ConfigSession::applyServiceActions( switch (level) { case cli::ConfigActionLevel::AGENT_COLDBOOT: case cli::ConfigActionLevel::AGENT_WARMBOOT: + case cli::ConfigActionLevel::BGP_RESTART: serviceNames[service] = fbossServiceUtil_->restartService(service, level); break; @@ -665,7 +804,12 @@ void ConfigSession::loadConfig() { void ConfigSession::initializeSession() { initializeGit(); - if (!sessionExists()) { + // Resume an existing session if EITHER an agent (agent.conf) or a BGP + // (bgp_config.json) session is staged. Keying only on the agent session file + // would misdetect a BGP-only session as fresh and clear its recorded + // BGP_RESTART action on the next (separate-process) CLI invocation, + // silently dropping the staged change at commit time. + if (!hasActiveSession()) { // Starting a new session - reset all state to ensure we don't carry over // stale data from a previous session (e.g., if the singleton persisted // in memory but the session files were deleted). @@ -729,8 +873,30 @@ void ConfigSession::initializeGit() { cliConfigPath, seedContent, 0644, folly::SyncType::WITH_SYNC); } + // Seed an empty metadata file and include it in the initial commit so the + // baseline shows up in `git log -- cli/cli_metadata.json`. No-arg + // rollback() walks metadata history (so BGP-only commits, which never touch + // agent.conf, are reachable); without the baseline in that history, rolling + // back to the very first commit would fail with "no previous revision". + std::string initialMetadataPath = getSystemMetadataPath(); + if (!fs::exists(initialMetadataPath)) { + folly::writeFileAtomic( + initialMetadataPath, "{}", 0644, folly::SyncType::WITH_SYNC); + } + + // Seed the running bgpd config into the baseline commit too. Without it, + // the first revision has no BGP snapshot, so a rollback to it would read + // the empty target as "BGP never existed" and DELETE the running + // bgpcpp.conf. + std::vector initialFiles = { + cliConfigPath, initialMetadataPath}; + std::string bgpSystemPath = getBgpSystemConfigPath(); + if (fs::exists(bgpSystemPath)) { + initialFiles.push_back(bgpSystemPath); + } + try { - git_->commit({cliConfigPath}, "Initial commit", username_, ""); + git_->commit(initialFiles, "Initial commit", username_, ""); } catch (const std::exception&) { // Another process may have raced us to the initial commit. // If commits now exist, swallow the error; otherwise re-throw. @@ -758,11 +924,19 @@ void ConfigSession::copySystemConfigToSession() const { } ConfigSession::CommitResult ConfigSession::commit(const HostInfo& hostInfo) { - if (!sessionExists()) { + // A BGP-only session stages bgp_config.json but never agent.conf, so the + // agent-config file operations below are guarded on hasAgentSession. + const bool hasAgentSession = sessionExists(); + if (!hasActiveSession()) { throw std::runtime_error( "No config session exists. Make a config change first."); } + // A BGP-only session never ran initializeSession(), so ensure the /etc/coop + // git repo and its initial commit exist before we commit into it. Idempotent + // for agent sessions, which initialized git when the session started. + initializeGit(); + // Check if someone else committed changes while this session was in progress std::string currentHead = git_->getHead(); if (!base_.empty() && currentHead != base_) { @@ -784,25 +958,70 @@ ConfigSession::CommitResult ConfigSession::commit(const HostInfo& hostInfo) { ensureDirectoryExists(cliConfigDir); - // Read the session config content + // Read the staged agent config (only present for an agent config session; a + // BGP-only session never writes agent.conf). oldConfigData is read for + // rollback if needed. std::string sessionConfigData; - if (!folly::readFile(sessionConfigPath.c_str(), sessionConfigData)) { - throw std::runtime_error( - fmt::format( - "Failed to read session config from {}", sessionConfigPath)); + std::string oldConfigData; + if (hasAgentSession) { + if (!folly::readFile(sessionConfigPath.c_str(), sessionConfigData)) { + throw std::runtime_error( + fmt::format( + "Failed to read session config from {}", sessionConfigPath)); + } + if (fs::exists(cliConfigPath)) { + if (!folly::readFile(cliConfigPath.c_str(), oldConfigData)) { + throw std::runtime_error( + fmt::format("Failed to read CLI config from {}", cliConfigPath)); + } + } } - // Read the old config for rollback if needed - std::string oldConfigData; - if (fs::exists(cliConfigPath)) { - if (!folly::readFile(cliConfigPath.c_str(), oldConfigData)) { + // Capture the running BGP system config up front: it tells us whether the + // staged BGP config actually changed (so we can skip a needless bgpd + // restart) and is the snapshot we restore if the commit fails partway. + const std::string bgpSystemPath = getBgpSystemConfigPath(); + const bool bgpSystemConfigExisted = fs::exists(bgpSystemPath); + std::string bgpOldData; + if (bgpSystemConfigExisted) { + // Check the read: a silently-failed read would leave bgpOldData empty and + // make the restore-on-failure path below write an empty bgpcpp.conf, + // corrupting the running config (mirrors the guard in rollback()). + if (!folly::readFile(bgpSystemPath.c_str(), bgpOldData)) { + throw std::runtime_error( + fmt::format( + "Failed to read current BGP config from {}", bgpSystemPath)); + } + } + + // saveBgpConfig() records BGP_RESTART unconditionally, so re-committing an + // unchanged BGP config would otherwise still bounce bgpd (a disruptive, + // traffic-affecting restart for no effective change). Treat BGP as changed + // only when the staged config differs from what is running. + bool bgpConfigChanged = false; + if (requiredActions_.count(cli::ServiceType::BGP) > 0 && bgpSessionExists()) { + std::string stagedBgpData; + if (!folly::readFile(getBgpSessionConfigPath().c_str(), stagedBgpData)) { throw std::runtime_error( - fmt::format("Failed to read CLI config from {}", cliConfigPath)); + fmt::format( + "Failed to read staged BGP config from {}", + getBgpSessionConfigPath())); } + bgpConfigChanged = (stagedBgpData != bgpOldData); } - // Early return if there are no changes to commit - if (sessionConfigData == oldConfigData && requiredActions_.empty()) { + // Copy requiredActions_ before we reset it (returned in CommitResult) and + // drop BGP when the BGP config is unchanged, so an unchanged BGP commit + // neither promotes the config nor restarts bgpd. + auto actions = requiredActions_; + if (!bgpConfigChanged) { + actions.erase(cli::ServiceType::BGP); + } + + // Early return if there are no changes to commit. + const bool agentConfigChanged = + hasAgentSession && sessionConfigData != oldConfigData; + if (!agentConfigChanged && actions.empty()) { return CommitResult{"", {}, {}}; } @@ -834,34 +1053,79 @@ ConfigSession::CommitResult ConfigSession::commit(const HostInfo& hostInfo) { "Failed to copy metadata to {}: {}", targetMetadataPath, e.what())); } - // Atomically write the session config to the CLI config path - folly::writeFileAtomic( - cliConfigPath, sessionConfigData, 0644, folly::SyncType::WITH_SYNC); + // Files to include in the git commit. The metadata file is always part of a + // commit; agent.conf and its symlink are added only when an agent config + // session was staged. BGP config (if staged this session) is added below; it + // lives under /etc/coop/bgpcpp/ so it's versioned by this same /etc/coop + // repo. + std::vector commitFiles = {targetMetadataPath}; - // Ensure the system config symlink points to the CLI config - atomicSymlinkUpdate(systemConfigPath, "cli/agent.conf"); + if (hasAgentSession) { + // Atomically write the session config to the CLI config path + folly::writeFileAtomic( + cliConfigPath, sessionConfigData, 0644, folly::SyncType::WITH_SYNC); - // Apply the config based on the required action levels for each service - // Copy requiredActions_ before we reset it - this will be returned in - // CommitResult - auto actions = requiredActions_; + // Ensure the system config symlink points to the CLI config + atomicSymlinkUpdate(systemConfigPath, "cli/agent.conf"); + + commitFiles.push_back(cliConfigPath); + commitFiles.push_back(systemConfigPath); + } // Apply the config based on the required action level std::string commitSha; std::map> serviceNames; + + // stagingBgp reflects the trimmed action set: false when the BGP config was + // unchanged, so we neither promote bgpcpp.conf nor restart bgpd below. The + // prior BGP config (bgpOldData / bgpSystemConfigExisted, captured above) is + // the snapshot restored if the commit fails partway. + const bool stagingBgp = actions.count(cli::ServiceType::BGP) > 0; + std::string bgpConfPath; + try { + // If this session staged BGP config changes, promote the staged + // bgp_config.json to /etc/coop/bgpcpp/bgpcpp.conf BEFORE bgpd is + // restarted below, so the restart picks up the new config. The promoted + // file is committed as part of this commit's git operation. The staged + // session file is left in place until the whole commit succeeds, so a + // failure here can be rolled back and retried. + if (stagingBgp && bgpSessionExists()) { + std::string staged; + if (!folly::readFile(getBgpSessionConfigPath().c_str(), staged)) { + throw std::runtime_error( + fmt::format( + "Failed to read staged BGP config from {}", + getBgpSessionConfigPath())); + } + ensureDirectoryExists(getBgpSystemConfigDir()); + folly::writeFileAtomic( + getBgpSystemConfigPath(), staged, 0644, folly::SyncType::WITH_SYNC); + bgpConfPath = getBgpSystemConfigPath(); + commitFiles.push_back(bgpConfPath); + // Keep the daemon-facing path (/etc/coop/bgpcpp.conf) a symlink into the + // CLI-managed bgpcpp/ subdir, mirroring agent.conf -> cli/agent.conf. + // bgpd reads its provisioned --config /etc/coop/bgpcpp.conf and follows + // the symlink, so no per-device systemd --config override is needed. The + // symlink is git-tracked alongside the config so a rollback restores it. + atomicSymlinkUpdate(getBgpSystemConfigLinkPath(), kBgpGitRelPath); + commitFiles.push_back(getBgpSystemConfigLinkPath()); + } else if (bgpSystemConfigExisted) { + // Agent-only commit: still track the running bgpd config so a later + // rollback has a snapshot to restore instead of wiping it. No restart — + // the file is already the running config. + commitFiles.push_back(bgpSystemPath); + } + serviceNames = applyServiceActions(actions, hostInfo); // Create a Git commit with all changed files: // - cli/agent.conf (the config file) // - cli/cli_metadata.json (the metadata file) // - agent.conf (the symlink, in case it was updated) + // - bgpcpp/bgpcpp.conf (the bgp config, if staged this session) std::string commitMessage = fmt::format("Config commit by {}", username_); - commitSha = git_->commit( - {cliConfigPath, targetMetadataPath, systemConfigPath}, - commitMessage, - username_, - ""); + commitSha = git_->commit(commitFiles, commitMessage, username_, ""); LOG(INFO) << "Config committed as " << Git::shortSha1(commitSha); } catch (const std::exception& ex) { // Rollback: restore the old config, then re-apply actions @@ -871,6 +1135,18 @@ ConfigSession::CommitResult ConfigSession::commit(const HostInfo& hostInfo) { folly::writeFileAtomic( cliConfigPath, oldConfigData, 0644, folly::SyncType::WITH_SYNC); } + // Restore the BGP system config to its pre-commit state. The staged + // session file is left intact, so the user can retry after the failure + // is resolved. + if (stagingBgp && !bgpConfPath.empty()) { + if (bgpSystemConfigExisted) { + folly::writeFileAtomic( + bgpConfPath, bgpOldData, 0644, folly::SyncType::WITH_SYNC); + } else { + std::error_code rmEc; + fs::remove(bgpConfPath, rmEc); + } + } applyServiceActions(actions, hostInfo); } catch (const std::exception& rollbackEx) { // If rollback also fails, include both errors in the message @@ -886,15 +1162,26 @@ ConfigSession::CommitResult ConfigSession::commit(const HostInfo& hostInfo) { ex.what())); } - // Only remove the session config after everything succeeded - std::error_code ec; - fs::remove(sessionConfigPath, ec); - if (ec) { - // Log warning but don't fail - the commit succeeded - LOG(WARNING) << fmt::format( - "Failed to remove session config {}: {}", - sessionConfigPath, - ec.message()); + // Now that the commit has fully succeeded, clear the staged BGP session file + // (it was deliberately left in place for rollback). Force a re-seed from the + // newly promoted system config on next access. + if (stagingBgp) { + std::error_code rmEc; + fs::remove(getBgpSessionConfigPath(), rmEc); + bgpConfigLoaded_ = false; + } + + // Only remove the agent session config after everything succeeded. + if (hasAgentSession) { + std::error_code ec; + fs::remove(sessionConfigPath, ec); + if (ec) { + // Log warning but don't fail - the commit succeeded + LOG(WARNING) << fmt::format( + "Failed to remove session config {}: {}", + sessionConfigPath, + ec.message()); + } } // Reset action level for all services after successful commit @@ -909,7 +1196,7 @@ ConfigSession::CommitResult ConfigSession::commit(const HostInfo& hostInfo) { } void ConfigSession::rebase() { - if (!sessionExists()) { + if (!hasActiveSession()) { throw std::runtime_error( "No config session exists. Make a config change first."); } @@ -922,33 +1209,46 @@ void ConfigSession::rebase() { "No rebase needed: session is already based on the current HEAD."); } - // Get the three versions of the config: - // 1. Base config (what the session was originally based on) - // 2. Current HEAD config (what someone else committed) - // 3. Session config (user's changes) - std::string cliConfigRelPath = "cli/agent.conf"; - std::string baseConfig = git_->fileAtRevision(base_, cliConfigRelPath); - std::string headConfig = git_->fileAtRevision(currentHead, cliConfigRelPath); - - std::string sessionConfigPath = getSessionConfigPath(); - std::string sessionConfig; - if (!folly::readFile(sessionConfigPath.c_str(), sessionConfig)) { - throw std::runtime_error( - fmt::format( - "Failed to read session config from {}", sessionConfigPath)); - } - - // Parse all three as JSON - folly::dynamic baseJson = folly::parseJson(baseConfig); - folly::dynamic headJson = folly::parseJson(headConfig); - folly::dynamic sessionJson = folly::parseJson(sessionConfig); - - // Perform a 3-way merge - // For each key in session that differs from base, apply to head - // If head also changed the same key differently, that's a conflict std::vector conflicts; - folly::dynamic mergedJson = - threeWayMerge(baseJson, headJson, sessionJson, "", conflicts); + + // 3-way merge a single staged file against the base/head revisions of its + // git-tracked counterpart. Returns the merged pretty JSON, or nullopt if the + // domain is not staged this session. A missing file at a revision (e.g. a + // commit predating BGP config) is treated as an empty object. + auto mergeStaged = + [&](const std::string& gitRelPath, + const std::string& sessionPath) -> std::optional { + if (!fs::exists(sessionPath)) { + return std::nullopt; + } + std::string sessionConfig; + if (!folly::readFile(sessionPath.c_str(), sessionConfig)) { + throw std::runtime_error( + fmt::format("Failed to read session config from {}", sessionPath)); + } + std::string baseConfig = fileAtRevisionOrEmpty(base_, gitRelPath); + std::string headConfig = fileAtRevisionOrEmpty(currentHead, gitRelPath); + // A missing file at a revision is treated as an empty object. + folly::dynamic baseJson = folly::dynamic::object; + if (!baseConfig.empty()) { + baseJson = folly::parseJson(baseConfig); + } + folly::dynamic headJson = folly::dynamic::object; + if (!headConfig.empty()) { + headJson = folly::parseJson(headConfig); + } + folly::dynamic sessionJson = folly::parseJson(sessionConfig); + folly::dynamic merged = + threeWayMerge(baseJson, headJson, sessionJson, "", conflicts); + return folly::toPrettyJson(merged); + }; + + // Merge each staged domain. Conflicts from both are aggregated so the user + // sees every conflicting path at once. + std::optional agentMerged = + mergeStaged("cli/agent.conf", getSessionConfigPath()); + std::optional bgpMerged = + mergeStaged(kBgpGitRelPath, getBgpSessionConfigPath()); if (!conflicts.empty()) { std::string conflictList; @@ -961,22 +1261,39 @@ void ConfigSession::rebase() { conflictList)); } - // Write the merged config to the session file - std::string mergedConfigStr = folly::toPrettyJson(mergedJson); - folly::writeFileAtomic( - sessionConfigPath, mergedConfigStr, 0644, folly::SyncType::WITH_SYNC); + // Write the merged config(s) back to the session file(s). + if (agentMerged) { + folly::writeFileAtomic( + getSessionConfigPath(), *agentMerged, 0644, folly::SyncType::WITH_SYNC); + } + if (bgpMerged) { + folly::writeFileAtomic( + getBgpSessionConfigPath(), + *bgpMerged, + 0644, + folly::SyncType::WITH_SYNC); + } - // Update the base to current HEAD + // Update the base to current HEAD (single repo, shared across domains). base_ = currentHead; saveMetadata(); - // Reload the config into memory - loadConfig(); + // Reload in-memory state for whichever domains were rebased. + if (agentMerged) { + loadConfig(); + } + if (bgpMerged) { + bgpConfigLoaded_ = false; + } } std::string ConfigSession::rollback(const HostInfo& hostInfo) { - // Get the commit history to find the previous commit - auto commits = git_->log(getCliConfigPath(), 2); + // Find the previous commit using the metadata file's history. The metadata + // (cli/cli_metadata.json) is committed by every config commit -- agent OR + // BGP -- whereas cli/agent.conf is unchanged by a BGP-only commit. Logging + // the metadata path therefore includes BGP-only commits, so no-arg rollback + // doesn't silently skip them. + auto commits = git_->log(getSystemMetadataPath(), 2); if (commits.size() < 2) { throw std::runtime_error( "Cannot rollback: no previous revision available in Git history"); @@ -1006,6 +1323,11 @@ std::string ConfigSession::rollback( git_->fileAtRevision(resolvedSha, "cli/cli_metadata.json"); std::string metadataPath = fmt::format("{}/cli_metadata.json", cliConfigDir); + // Target BGP config at that revision ("" if the commit predates BGP config). + std::string bgpSystemPath = getBgpSystemConfigPath(); + std::string targetBgpData = + fileAtRevisionOrEmpty(resolvedSha, kBgpGitRelPath); + // Read the current config for rollback if needed std::string oldConfigData; if (fs::exists(cliConfigPath)) { @@ -1021,38 +1343,90 @@ std::string ConfigSession::rollback( fmt::format("Failed to read current metadata from {}", metadataPath)); } } + std::string oldBgpData; + const bool bgpSystemExisted = fs::exists(bgpSystemPath); + if (bgpSystemExisted) { + if (!folly::readFile(bgpSystemPath.c_str(), oldBgpData)) { + // Don't proceed: a failed read here would make the restore-on-failure + // path below write an empty bgpcpp.conf, corrupting the running config. + throw std::runtime_error( + fmt::format( + "Failed to read current BGP config from {}", bgpSystemPath)); + } + } - // Atomically write the target config and metadata to the CLI directory - folly::writeFileAtomic( - cliConfigPath, targetConfigData, 0644, folly::SyncType::WITH_SYNC); + // Only act on a service whose config actually changes in this rollback. A + // BGP-only commit leaves cli/agent.conf identical, and vice versa. + const bool agentChanged = targetConfigData != oldConfigData; + const bool bgpChanged = targetBgpData != oldBgpData; + + // Always restore the metadata (it records the new rollback base). Only + // rewrite the agent config + symlink when it actually changed, to avoid + // needless writes and symlink churn on a BGP-only rollback. folly::writeFileAtomic( metadataPath, targetMetadataData, 0644, folly::SyncType::WITH_SYNC); + if (agentChanged) { + folly::writeFileAtomic( + cliConfigPath, targetConfigData, 0644, folly::SyncType::WITH_SYNC); + atomicSymlinkUpdate(systemConfigPath, "cli/agent.conf"); + } - // Ensure the system config symlink points to the CLI config - atomicSymlinkUpdate(systemConfigPath, "cli/agent.conf"); + // Promote the target BGP config (if it changed) so bgpd picks it up when + // restarted below. An empty target means BGP didn't exist at that revision, + // so remove the running config file. + if (bgpChanged) { + if (!targetBgpData.empty()) { + ensureDirectoryExists(getBgpSystemConfigDir()); + folly::writeFileAtomic( + bgpSystemPath, targetBgpData, 0644, folly::SyncType::WITH_SYNC); + } else if (bgpSystemExisted) { + std::error_code rmEc; + fs::remove(bgpSystemPath, rmEc); + if (rmEc) { + throw std::runtime_error( + fmt::format( + "Failed to remove BGP config {} while rolling back to a " + "pre-BGP revision: {}", + bgpSystemPath, + rmEc.message())); + } + } + } - // Reload the config - if this fails, restore the old config and metadata - // TODO: look at all the metadata files in the revision range and - // decide whether or not we need to restart the agent based on the highest - // action level in that range. + // Apply the rolled-back config - if this fails, restore prior state. std::string newCommitSha; try { - auto client = - utils::createClient>( - hostInfo); - client->sync_reloadConfig(); + // Reload the agent only if its config changed. + if (agentChanged) { + auto client = utils::createClient< + apache::thrift::Client>(hostInfo); + client->sync_reloadConfig(); + } + // Restart bgpd only if its config changed. + if (bgpChanged) { + ensureFbossServiceUtil(hostInfo); + fbossServiceUtil_->restartService( + cli::ServiceType::BGP, cli::ConfigActionLevel::BGP_RESTART); + } - // Create a Git commit for the rollback with all relevant files + // Create a Git commit for the rollback. Metadata always changes; the agent + // config + symlink are included only when they were actually rewritten + // above (a BGP-only rollback leaves them untouched, so committing them + // could capture unrelated on-disk drift into the rollback commit). + std::vector rollbackFiles = {metadataPath}; + if (agentChanged) { + rollbackFiles.push_back(cliConfigPath); + rollbackFiles.push_back(systemConfigPath); + } + if (bgpChanged && !targetBgpData.empty()) { + rollbackFiles.push_back(bgpSystemPath); + } std::string commitMessage = fmt::format( "Rollback to {} by {}", Git::shortSha1(resolvedSha), username_); - newCommitSha = git_->commit( - {cliConfigPath, metadataPath, systemConfigPath}, - commitMessage, - username_, - ""); + newCommitSha = git_->commit(rollbackFiles, commitMessage, username_, ""); LOG(INFO) << "Rollback committed as " << Git::shortSha1(newCommitSha); } catch (const std::exception& ex) { - // Rollback: restore the old config and metadata + // Rollback: restore the old config, metadata, and BGP config. try { if (!oldConfigData.empty()) { folly::writeFileAtomic( @@ -1062,6 +1436,15 @@ std::string ConfigSession::rollback( folly::writeFileAtomic( metadataPath, oldMetadataData, 0644, folly::SyncType::WITH_SYNC); } + if (bgpChanged) { + if (bgpSystemExisted) { + folly::writeFileAtomic( + bgpSystemPath, oldBgpData, 0644, folly::SyncType::WITH_SYNC); + } else { + std::error_code rmEc; + fs::remove(bgpSystemPath, rmEc); + } + } } catch (const std::exception& rollbackEx) { // If rollback also fails, include both errors in the message throw std::runtime_error( @@ -1076,26 +1459,45 @@ std::string ConfigSession::rollback( ex.what())); } + // The on-disk config changed underneath any cached in-memory state; force a + // reload on next access regardless of whether the session is clean. + configLoaded_ = false; + bgpConfigLoaded_ = false; + // Update the session state after rollback // Check if the current session is clean (no pending changes) if (commands_.empty()) { - // Session is clean - update base to the new rollback commit and sync the - // session config to match the rolled-back configuration + // Session is clean - update base to the new rollback commit and sync any + // active session config to match the rolled-back configuration. base_ = newCommitSha; - // Copy the rolled-back config to the session config - folly::writeFileAtomic( - getSessionConfigPath(), - targetConfigData, - 0644, - folly::SyncType::WITH_SYNC); + // Only re-seed a session file that already exists, and seed each domain + // from its own rolled-back data. Unconditionally writing the agent session + // file would materialize a phantom agent session after a BGP-only rollback + // (and leave the BGP session file stale); keep the two domains symmetric. + if (sessionExists()) { + folly::writeFileAtomic( + getSessionConfigPath(), + targetConfigData, + 0644, + folly::SyncType::WITH_SYNC); + } + if (bgpSessionExists()) { + if (!targetBgpData.empty()) { + folly::writeFileAtomic( + getBgpSessionConfigPath(), + targetBgpData, + 0644, + folly::SyncType::WITH_SYNC); + } else { + std::error_code rmEc; + fs::remove(getBgpSessionConfigPath(), rmEc); + } + } // Save the updated metadata (with new base) saveMetadata(); - // Force config reload from session config on next access - configLoaded_ = false; - LOG(INFO) << "Session updated to rollback commit " << Git::shortSha1(newCommitSha); } else { diff --git a/fboss/cli/fboss2/session/ConfigSession.h b/fboss/cli/fboss2/session/ConfigSession.h index edc74ee635b6f..ca84cbc6b93cb 100644 --- a/fboss/cli/fboss2/session/ConfigSession.h +++ b/fboss/cli/fboss2/session/ConfigSession.h @@ -9,6 +9,7 @@ #pragma once +#include #include #include #include @@ -102,6 +103,9 @@ class ConfigSession { static std::string getSessionDir(); static std::string getSessionConfigPathStatic(); static std::string getSessionMetadataPathStatic(); + // ~/.fboss2/bgp_config.json — staged BGP edits (used by `config session + // clear` without instantiating a session). + static std::string getBgpSessionConfigPathStatic(); // Get the path to the session config file (~/.fboss2/agent.conf) std::string getSessionConfigPath() const; @@ -145,9 +149,16 @@ class ConfigSession { std::string rollback(const HostInfo& hostInfo); std::string rollback(const HostInfo& hostInfo, const std::string& commitSha); - // Check if a session exists + // Check if an agent config session exists (~/.fboss2/agent.conf staged). bool sessionExists() const; + // Check if any committable session is staged: either an agent config session + // (agent.conf) or a protocol session persisted outside agent.conf (e.g. a + // BGP++ session at ~/.fboss2/bgp_config.json recorded via + // recordServiceAction()). The commit path uses this rather than + // sessionExists() so a BGP-only change can be committed. + bool hasActiveSession() const; + // Get the parsed agent configuration cfg::AgentConfig& getAgentConfig(); const cfg::AgentConfig& getAgentConfig() const; @@ -169,6 +180,43 @@ class ConfigSession { // Save the configuration for AGENT service with HITLESS action level. void saveConfig(); + // Record that a service requires an action for a config change that is + // persisted OUTSIDE the agent config file (e.g. BGP++ writes its own + // ~/.fboss2/bgp_config.json). This updates the action level for the service + // in the shared session metadata and persists it, WITHOUT rewriting + // agent.conf. A subsequent `config session commit` then applies the recorded + // action (e.g. restart bgpd). + void recordServiceAction( + cli::ServiceType service, + cli::ConfigActionLevel actionLevel); + + // ==================== BGP configuration ==================== + // ConfigSession owns the BGP config as a typed bgp::thrift::BgpConfig, + // exactly the way it owns cfg::AgentConfig for the agent. It is BGP-*aware* + // but agnostic about the config's internal structure: getBgpConfig() exposes + // the WHOLE typed config (router_id, ASNs, peers, peer_groups, networks, ...) + // and commands mutate whichever typed fields they need -- mirroring how + // interface commands mutate getAgentConfig().sw()->ports(). ConfigSession has + // no notion of "global" vs "peer" vs "peer-group". + + // Typed, mutable view of the entire BGP config. Lazily seeded from the staged + // ~/.fboss2/bgp_config.json, else the running /etc/coop/bgpcpp/bgpcpp.conf, + // else schema defaults. Mirrors getAgentConfig(). + bgp::thrift::BgpConfig& getBgpConfig(); + const bgp::thrift::BgpConfig& getBgpConfig() const; + + // Persist the typed BGP config back to ~/.fboss2/bgp_config.json and record + // that bgpd must be restarted for this change to take effect on a + // subsequent `config session commit`. Mirrors saveConfig() for the agent. + void saveBgpConfig(); + + // ~/.fboss2/bgp_config.json (staged BGP edits) + std::string getBgpSessionConfigPath() const; + // /etc/coop/bgpcpp/bgpcpp.conf (config read by the bgpd daemon) + std::string getBgpSystemConfigPath() const; + // Whether a BGP session is staged (~/.fboss2/bgp_config.json exists) + bool bgpSessionExists() const; + // Get the Git instance for this config session // Used to access the Git repository for history, rollback, etc. Git& getGit(); @@ -239,6 +287,31 @@ class ConfigSession { std::unique_ptr portMap_; bool configLoaded_ = false; + // Typed view of the entire BGP config (lazily loaded), mirroring + // agentConfig_. + bgp::thrift::BgpConfig bgpConfig_; + bool bgpConfigLoaded_ = false; + + // /etc/coop/bgpcpp (directory holding the bgpd daemon's config) + std::string getBgpSystemConfigDir() const; + // /etc/coop/bgpcpp.conf — the stable path the bgpd daemon is configured to + // read (--config). commit() keeps it as a symlink into the CLI-managed + // bgpcpp/ subdir (kBgpGitRelPath), mirroring how agent.conf symlinks to + // cli/agent.conf, so the daemon needs no per-device --config override. + std::string getBgpSystemConfigLinkPath() const; + // Lazily seed bgpConfig_ from disk (staged file, else running config, else + // defaults). Mirrors loadConfig() for the agent. + void loadBgpConfig(); + + // git relative path of the bgpd config tracked in the /etc/coop repo. + static constexpr auto kBgpGitRelPath = "bgpcpp/bgpcpp.conf"; + // Like Git::fileAtRevision but returns "" instead of throwing when the path + // does not exist at that revision (e.g. a pre-BGP commit). Used by + // rebase/rollback/diff so a missing bgpcpp.conf is treated as empty. + std::string fileAtRevisionOrEmpty( + const std::string& revision, + const std::string& gitRelPath) const; + // Track the highest action level required for pending config changes per // service. Persisted to disk so it survives across CLI invocations within a // session. diff --git a/fboss/cli/fboss2/session/FbossServiceUtil.cpp b/fboss/cli/fboss2/session/FbossServiceUtil.cpp index 525c280d46d78..060d1a886bde4 100644 --- a/fboss/cli/fboss2/session/FbossServiceUtil.cpp +++ b/fboss/cli/fboss2/session/FbossServiceUtil.cpp @@ -25,6 +25,7 @@ namespace { constexpr std::string_view kWedgeAgent = "wedge_agent"; constexpr std::string_view kSwAgent = "fboss_sw_agent"; constexpr std::string_view kHwAgentPrefix = "fboss_hw_agent@"; +constexpr std::string_view kBgpd = "bgpd"; } // namespace namespace facebook::fboss { @@ -48,6 +49,8 @@ std::string FbossServiceUtil::getServiceName(cli::ServiceType service) { switch (service) { case cli::ServiceType::AGENT: return std::string(kWedgeAgent); + case cli::ServiceType::BGP: + return std::string(kBgpd); } throw std::runtime_error("Unknown service type"); } @@ -128,6 +131,9 @@ std::vector FbossServiceUtil::getServicesToRestart( } return services; } + case cli::ServiceType::BGP: + // BGP++ is a single, mode-independent service. + return {std::string(kBgpd)}; } throw std::runtime_error("Unknown service type"); } @@ -151,7 +157,11 @@ std::vector FbossServiceUtil::reloadConfig( reloadedServices.emplace_back(serviceName); break; } - // TODO: Add cases for future services (e.g., BGP) + case cli::ServiceType::BGP: + // bgpd has no hitless reloadConfig() RPC; config changes are applied by + // restarting the service (BGP_RESTART), so this path is never taken. + throw std::runtime_error( + "bgpd does not support config reload; it must be restarted"); } return reloadedServices; } @@ -159,14 +169,30 @@ std::vector FbossServiceUtil::reloadConfig( std::vector FbossServiceUtil::restartService( cli::ServiceType service, cli::ConfigActionLevel level) { - std::string restartType = (level == cli::ConfigActionLevel::AGENT_COLDBOOT) - ? "coldboot" - : "warmboot"; + std::string restartType; + switch (level) { + case cli::ConfigActionLevel::AGENT_COLDBOOT: + restartType = "coldboot"; + break; + case cli::ConfigActionLevel::AGENT_WARMBOOT: + restartType = "warmboot"; + break; + case cli::ConfigActionLevel::BGP_RESTART: + restartType = "restart"; + break; + case cli::ConfigActionLevel::HITLESS: + // Not expected: HITLESS is applied via reloadConfig(), not restart. + restartType = "reload"; + break; + } auto services = getServicesToRestart(service); - LOG(INFO) << "Restarting agents (" << restartType << ")..."; + LOG(INFO) << "Restarting " << getServiceName(service) << " (" << restartType + << ")..."; + // Only AGENT_COLDBOOT needs the coldboot marker file; every other level is a + // plain restart-and-wait. if (level == cli::ConfigActionLevel::AGENT_COLDBOOT) { performColdboot(services); } else { diff --git a/fboss/cli/fboss2/test/config/BUCK b/fboss/cli/fboss2/test/config/BUCK index 30e227905b081..c772d826cad6d 100644 --- a/fboss/cli/fboss2/test/config/BUCK +++ b/fboss/cli/fboss2/test/config/BUCK @@ -10,6 +10,9 @@ cpp_unittest( "CmdConfigAdminDistanceTest.cpp", "CmdConfigAppliedInfoTest.cpp", "CmdConfigArpTest.cpp", + "CmdConfigBgpNeighborTest.cpp", + "CmdConfigBgpPeerGroupTest.cpp", + "CmdConfigBgpPolicyAsPathListTest.cpp", "CmdConfigCoppTest.cpp", "CmdConfigDhcpTest.cpp", "CmdConfigHistoryTest.cpp", @@ -40,6 +43,11 @@ cpp_unittest( "CmdDeleteInterfaceTest.cpp", "CmdDeleteTunnelIpInIpTest.cpp", "ConfigSessionSystemdTest.cpp", + "CmdConfigIpRouteTest.cpp", + "CmdDeleteBgpNeighborTest.cpp", + "CmdDeleteBgpPeerGroupTest.cpp", + "CmdDeleteBgpPolicyAsPathListTest.cpp", + "CmdDeleteConfigInterfaceTest.cpp", "ProfileValidationTest.cpp", ], headers = [ diff --git a/fboss/cli/fboss2/test/config/BgpConfigSessionTest.cpp b/fboss/cli/fboss2/test/config/BgpConfigSessionTest.cpp index efaa968b47704..e2d6d848d3310 100644 --- a/fboss/cli/fboss2/test/config/BgpConfigSessionTest.cpp +++ b/fboss/cli/fboss2/test/config/BgpConfigSessionTest.cpp @@ -2,11 +2,11 @@ #include "fboss/cli/fboss2/test/config/CmdConfigTestBase.h" -#include // NOLINT(misc-include-cleaner) #include #include #include +#include #include "fboss/cli/fboss2/commands/config/protocol/bgp/BgpConfigSession.h" @@ -75,14 +75,6 @@ TEST_F(BgpConfigSessionTest, setConfedAsn) { EXPECT_EQ(config["local_confed_as_4_byte"].asInt(), 65100); } -TEST_F(BgpConfigSessionTest, setClusterId) { - session_->setClusterId("1.2.3.4"); - EXPECT_EQ(session_->getClusterId(), "1.2.3.4"); - - auto& config = session_->getBgpConfig(); - EXPECT_EQ(config["cluster_id"].asString(), "1.2.3.4"); -} - TEST_F(BgpConfigSessionTest, setListenAddress) { session_->setListenAddress("0.0.0.0"); EXPECT_EQ(session_->getListenAddress(), "0.0.0.0"); @@ -207,160 +199,6 @@ TEST_F(BgpConfigSessionTest, addMultipleNetworks6) { EXPECT_FALSE(config["networks6"][1]["install_to_fib"].asBool()); } -// ==================== Peer Configuration Tests ==================== - -TEST_F(BgpConfigSessionTest, createPeerAndSetRemoteAsn) { - session_->createPeer("2001:db8::1"); - session_->setPeerRemoteAsn("2001:db8::1", 65001); - - auto& config = session_->getBgpConfig(); - ASSERT_TRUE(config.count("peers")); - ASSERT_EQ(config["peers"].size(), 1); - - auto& peer = config["peers"][0]; - EXPECT_EQ(peer["peer_addr"].asString(), "2001:db8::1"); - EXPECT_EQ(peer["remote_as_4_byte"].asInt(), 65001); -} - -TEST_F(BgpConfigSessionTest, setPeerDescription) { - session_->createPeer("10.0.0.1"); - session_->setPeerDescription("10.0.0.1", "Test peer description"); - - auto& config = session_->getBgpConfig(); - EXPECT_EQ( - config["peers"][0]["description"].asString(), "Test peer description"); -} - -TEST_F(BgpConfigSessionTest, setPeerPeerGroupName) { - session_->createPeer("2001:db8::1"); - session_->setPeerGroupName("2001:db8::1", "RSW-FSW-V6"); - - auto& config = session_->getBgpConfig(); - EXPECT_EQ(config["peers"][0]["peer_group_name"].asString(), "RSW-FSW-V6"); -} - -TEST_F(BgpConfigSessionTest, setPeerDisableIpv4AfiTrue) { - session_->createPeer("2001:db8::1"); - session_->setPeerDisableIpv4Afi("2001:db8::1", true); - - auto& config = session_->getBgpConfig(); - EXPECT_TRUE(config["peers"][0]["disable_ipv4_afi"].asBool()); -} - -TEST_F(BgpConfigSessionTest, setPeerDisableIpv4AfiFalse) { - // Critical test: Verify that disable_ipv4_afi: false is preserved - session_->createPeer("2001:db8::1"); - session_->setPeerDisableIpv4Afi("2001:db8::1", false); - - auto& config = session_->getBgpConfig(); - // Must be explicitly set to false, not omitted - ASSERT_TRUE(config["peers"][0].count("disable_ipv4_afi")); - EXPECT_FALSE(config["peers"][0]["disable_ipv4_afi"].asBool()); -} - -TEST_F(BgpConfigSessionTest, setPeerNextHopSelf) { - session_->createPeer("2001:db8::1"); - session_->setPeerNextHopSelf("2001:db8::1", true); - - auto& config = session_->getBgpConfig(); - EXPECT_TRUE(config["peers"][0]["next_hop_self"].asBool()); -} - -TEST_F(BgpConfigSessionTest, setPeerConfedPeer) { - session_->createPeer("2001:db8::1"); - session_->setPeerConfedPeer("2001:db8::1", true); - - auto& config = session_->getBgpConfig(); - EXPECT_TRUE(config["peers"][0]["is_confed_peer"].asBool()); -} - -TEST_F(BgpConfigSessionTest, setPeerPassive) { - session_->createPeer("2001:db8::1"); - session_->setPeerPassive("2001:db8::1", true); - - auto& config = session_->getBgpConfig(); - EXPECT_TRUE(config["peers"][0]["is_passive"].asBool()); -} - -TEST_F(BgpConfigSessionTest, setPeerRrClient) { - session_->createPeer("2001:db8::1"); - session_->setPeerRrClient("2001:db8::1", true); - - auto& config = session_->getBgpConfig(); - EXPECT_TRUE(config["peers"][0]["is_rr_client"].asBool()); -} - -TEST_F(BgpConfigSessionTest, setPeerTimers) { - session_->createPeer("2001:db8::1"); - session_->setPeerHoldTime("2001:db8::1", 90); - session_->setPeerKeepalive("2001:db8::1", 30); - session_->setPeerOutDelay("2001:db8::1", 5); - - auto& config = session_->getBgpConfig(); - ASSERT_TRUE(config["peers"][0].count("bgp_peer_timers")); - - auto& timers = config["peers"][0]["bgp_peer_timers"]; - EXPECT_EQ(timers["hold_time_seconds"].asInt(), 90); - EXPECT_EQ(timers["keep_alive_seconds"].asInt(), 30); - EXPECT_EQ(timers["out_delay_seconds"].asInt(), 5); -} - -TEST_F(BgpConfigSessionTest, setPeerPolicies) { - session_->createPeer("2001:db8::1"); - session_->setPeerIngressPolicy("2001:db8::1", "INGRESS_POLICY"); - session_->setPeerEgressPolicy("2001:db8::1", "EGRESS_POLICY"); - - auto& config = session_->getBgpConfig(); - EXPECT_EQ( - config["peers"][0]["ingress_policy_name"].asString(), "INGRESS_POLICY"); - EXPECT_EQ( - config["peers"][0]["egress_policy_name"].asString(), "EGRESS_POLICY"); -} - -TEST_F(BgpConfigSessionTest, setPeerPreFilterMaxRoutes) { - session_->createPeer("2001:db8::1"); - session_->setPeerPreFilterMaxRoutes("2001:db8::1", 1000); - - auto& config = session_->getBgpConfig(); - ASSERT_TRUE(config["peers"][0].count("pre_filter")); - EXPECT_EQ(config["peers"][0]["pre_filter"]["max_routes"].asInt(), 1000); -} - -TEST_F(BgpConfigSessionTest, setPeerLinkBandwidth) { - session_->createPeer("2001:db8::1"); - session_->setPeerAdvertiseLbw("2001:db8::1", true); - session_->setPeerLbwValue("2001:db8::1", 10000000000); - - auto& config = session_->getBgpConfig(); - EXPECT_TRUE(config["peers"][0]["advertise_link_bandwidth"].asBool()); - EXPECT_EQ(config["peers"][0]["link_bandwidth_bps"].asInt(), 10000000000); -} - -TEST_F(BgpConfigSessionTest, setPeerIpv4OverIpv6Nh) { - session_->createPeer("2001:db8::1"); - session_->setPeerIpv4OverIpv6Nh("2001:db8::1", true); - - auto& config = session_->getBgpConfig(); - EXPECT_TRUE(config["peers"][0]["v4_over_v6_nexthop"].asBool()); -} - -TEST_F(BgpConfigSessionTest, setPeerPeerId) { - session_->createPeer("2001:db8::1"); - session_->setPeerPeerId("2001:db8::1", "fsw001.p001.f01.qzd1:v6:1"); - - auto& config = session_->getBgpConfig(); - EXPECT_EQ( - config["peers"][0]["peer_id"].asString(), "fsw001.p001.f01.qzd1:v6:1"); -} - -TEST_F(BgpConfigSessionTest, setPeerType) { - session_->createPeer("2001:db8::1"); - session_->setPeerType("2001:db8::1", "FSW"); - - auto& config = session_->getBgpConfig(); - EXPECT_EQ(config["peers"][0]["type"].asString(), "FSW"); -} - // ==================== Peer Group Configuration Tests ==================== TEST_F(BgpConfigSessionTest, createPeerGroupAndSetDescription) { @@ -488,14 +326,6 @@ TEST_F(BgpConfigSessionTest, fullRswConfigurationWorkflow) { session_->createPeerGroup("RSW-RTSW-V6"); session_->setPeerGroupDisableIpv4Afi("RSW-RTSW-V6", true); - // Create peer with peer_group_name - session_->createPeer("2401:db00:e50e:1300::20"); - session_->setPeerRemoteAsn("2401:db00:e50e:1300::20", 65000); - session_->setPeerGroupName("2401:db00:e50e:1300::20", "RSW-FSW-V6"); - session_->setPeerDisableIpv4Afi("2401:db00:e50e:1300::20", false); - session_->setPeerPeerId( - "2401:db00:e50e:1300::20", "fsw001.p001.f01.qzd1:v6:1"); - // Verify the complete configuration auto& config = session_->getBgpConfig(); @@ -541,17 +371,6 @@ TEST_F(BgpConfigSessionTest, fullRswConfigurationWorkflow) { } EXPECT_TRUE(foundFswGroup); EXPECT_TRUE(foundRtswGroup); - - // Verify peer - ASSERT_EQ(config["peers"].size(), 1); - auto& peer = config["peers"][0]; - EXPECT_EQ(peer["peer_addr"].asString(), "2401:db00:e50e:1300::20"); - EXPECT_EQ(peer["remote_as_4_byte"].asInt(), 65000); - EXPECT_EQ(peer["peer_group_name"].asString(), "RSW-FSW-V6"); - // Critical: disable_ipv4_afi: false must be preserved - ASSERT_TRUE(peer.count("disable_ipv4_afi")); - EXPECT_FALSE(peer["disable_ipv4_afi"].asBool()); - EXPECT_EQ(peer["peer_id"].asString(), "fsw001.p001.f01.qzd1:v6:1"); } TEST_F(BgpConfigSessionTest, exportConfigProducesValidJson) { @@ -559,8 +378,6 @@ TEST_F(BgpConfigSessionTest, exportConfigProducesValidJson) { session_->setLocalAsn(65000); session_->addNetwork6("2001:db8::/32", "TEST_POLICY", true, 0); session_->createPeerGroup("TEST-GROUP"); - session_->createPeer("2001:db8::1"); - session_->setPeerRemoteAsn("2001:db8::1", 65001); std::string jsonStr = session_->exportConfig(); @@ -571,41 +388,6 @@ TEST_F(BgpConfigSessionTest, exportConfigProducesValidJson) { EXPECT_EQ(parsed["local_as_4_byte"].asInt(), 65000); EXPECT_EQ(parsed["networks6"].size(), 1); EXPECT_EQ(parsed["peer_groups"].size(), 1); - EXPECT_EQ(parsed["peers"].size(), 1); -} - -TEST_F(BgpConfigSessionTest, multiplePeersWithDifferentConfigs) { - // Create multiple peers with different configurations - session_->createPeer("2001:db8::1"); - session_->setPeerRemoteAsn("2001:db8::1", 65001); - session_->setPeerDisableIpv4Afi("2001:db8::1", true); - session_->setPeerNextHopSelf("2001:db8::1", true); - - session_->createPeer("2001:db8::2"); - session_->setPeerRemoteAsn("2001:db8::2", 65002); - session_->setPeerDisableIpv4Afi("2001:db8::2", false); - session_->setPeerPassive("2001:db8::2", true); - - session_->createPeer("10.0.0.1"); - session_->setPeerRemoteAsn("10.0.0.1", 65003); - session_->setPeerRrClient("10.0.0.1", true); - - auto& config = session_->getBgpConfig(); - ASSERT_EQ(config["peers"].size(), 3); - - // Find each peer and verify its config - for (const auto& peer : config["peers"]) { - std::string addr = peer["peer_addr"].asString(); - if (addr == "2001:db8::1") { - EXPECT_TRUE(peer["disable_ipv4_afi"].asBool()); - EXPECT_TRUE(peer["next_hop_self"].asBool()); - } else if (addr == "2001:db8::2") { - EXPECT_FALSE(peer["disable_ipv4_afi"].asBool()); - EXPECT_TRUE(peer["is_passive"].asBool()); - } else if (addr == "10.0.0.1") { - EXPECT_TRUE(peer["is_rr_client"].asBool()); - } - } } TEST_F(BgpConfigSessionTest, stubModePreventsSave) { @@ -623,7 +405,6 @@ TEST_F(BgpConfigSessionTest, stubModeCanClearSession) { // In stub mode, clearSession() should not throw // The actual clearing behavior depends on implementation details session_->setRouterId("10.0.0.1"); - session_->createPeer("2001:db8::1"); EXPECT_EQ(session_->getRouterId(), "10.0.0.1"); @@ -633,24 +414,6 @@ TEST_F(BgpConfigSessionTest, stubModeCanClearSession) { // ==================== Edge Case Tests ==================== -TEST_F(BgpConfigSessionTest, updateExistingPeer) { - // Create peer and set initial values - session_->createPeer("2001:db8::1"); - session_->setPeerRemoteAsn("2001:db8::1", 65001); - session_->setPeerDescription("2001:db8::1", "Initial description"); - - // Update the same peer - session_->setPeerRemoteAsn("2001:db8::1", 65002); - session_->setPeerDescription("2001:db8::1", "Updated description"); - - auto& config = session_->getBgpConfig(); - // Should still only have one peer - ASSERT_EQ(config["peers"].size(), 1); - EXPECT_EQ(config["peers"][0]["remote_as_4_byte"].asInt(), 65002); - EXPECT_EQ( - config["peers"][0]["description"].asString(), "Updated description"); -} - TEST_F(BgpConfigSessionTest, updateExistingPeerGroup) { session_->createPeerGroup("TEST-GROUP"); session_->setPeerGroupDescription("TEST-GROUP", "Initial"); @@ -662,39 +425,10 @@ TEST_F(BgpConfigSessionTest, updateExistingPeerGroup) { EXPECT_EQ(config["peer_groups"][0]["description"].asString(), "Updated"); } -TEST_F(BgpConfigSessionTest, ipv4PeerAddress) { - // Test with IPv4 peer address - session_->createPeer("192.168.1.1"); - session_->setPeerRemoteAsn("192.168.1.1", 65000); - - auto& config = session_->getBgpConfig(); - EXPECT_EQ(config["peers"][0]["peer_addr"].asString(), "192.168.1.1"); -} - -TEST_F(BgpConfigSessionTest, specialCharactersInDescription) { - session_->createPeer("2001:db8::1"); - session_->setPeerDescription( - "2001:db8::1", "Test \"quotes\" and 'apostrophes' and special: !@#$%"); - - auto& config = session_->getBgpConfig(); - EXPECT_EQ( - config["peers"][0]["description"].asString(), - "Test \"quotes\" and 'apostrophes' and special: !@#$%"); -} - TEST_F(BgpConfigSessionTest, largeAsnNumber) { // Test with 4-byte ASN session_->setLocalAsn(4200000000); EXPECT_EQ(session_->getLocalAsn(), 4200000000); } -TEST_F(BgpConfigSessionTest, largeLinkBandwidth) { - // Test with 100G bandwidth - session_->createPeer("2001:db8::1"); - session_->setPeerLbwValue("2001:db8::1", 100000000000); - - auto& config = session_->getBgpConfig(); - EXPECT_EQ(config["peers"][0]["link_bandwidth_bps"].asInt(), 100000000000); -} - } // namespace facebook::fboss diff --git a/fboss/cli/fboss2/test/config/CmdConfigBgpNeighborTest.cpp b/fboss/cli/fboss2/test/config/CmdConfigBgpNeighborTest.cpp new file mode 100644 index 0000000000000..49b0cc9d552f7 --- /dev/null +++ b/fboss/cli/fboss2/test/config/CmdConfigBgpNeighborTest.cpp @@ -0,0 +1,316 @@ +// (c) Meta Platforms, Inc. and affiliates. Confidential and proprietary. + +#include +#include +#include + +#include +#include +#include +#include + +#include "configerator/structs/neteng/fboss/bgp/if/gen-cpp2/bgp_attr_types.h" +#include "fboss/cli/fboss2/commands/config/protocol/bgp/neighbor/CmdConfigProtocolBgpNeighbor.h" +#include "fboss/cli/fboss2/session/ConfigSession.h" +#include "fboss/cli/fboss2/test/config/CmdConfigTestBase.h" +#include "fboss/cli/fboss2/utils/HostInfo.h" + +using namespace ::testing; +using facebook::neteng::fboss::bgp_attr::AddPath; + +namespace facebook::fboss { + +// The neighbor dispatcher only touches the BGP side of ConfigSession, which +// seeds from thrift schema defaults when neither a staged session nor a +// system bgpcpp.conf exists — so no seed agent config is needed (mirrors +// BgpConfigSessionTest). +class CmdConfigBgpNeighborTestFixture : public CmdConfigTestBase { + public: + CmdConfigBgpNeighborTestFixture() + : CmdConfigTestBase("bgp_neighbor_test_%%%%-%%%%-%%%%", "") {} + + void SetUp() override { + CmdConfigTestBase::SetUp(); + setupTestableConfigSession(); + } + + std::string run(const std::vector& tokens) { + CmdConfigProtocolBgpNeighbor cmd; + HostInfo hostInfo("testhost"); + return cmd.queryClient(hostInfo, BgpNeighborConfig(tokens)); + } + + const std::vector& peers() { + return *ConfigSession::getInstance().getBgpConfig().peers(); + } + + bool sessionFileExists() { + return std::filesystem::exists( + ConfigSession::getInstance().getBgpSessionConfigPath()); + } +}; + +// ============================================================================== +// BgpNeighborConfig (arg) validation +// ============================================================================== + +TEST_F(CmdConfigBgpNeighborTestFixture, argValidation) { + // Bare create. + EXPECT_EQ(BgpNeighborConfig({"10.0.0.1"}).peerAddr(), "10.0.0.1"); + EXPECT_TRUE(BgpNeighborConfig({"10.0.0.1"}).attr().empty()); + + // Address normalization. + EXPECT_EQ(BgpNeighborConfig({"2001:DB8::1"}).peerAddr(), "2001:db8::1"); + + // Prefix notation (passive listening sessions). + EXPECT_EQ(BgpNeighborConfig({"2001:db8::/64"}).peerAddr(), "2001:db8::/64"); + + // Single-token attribute. + auto singleTok = BgpNeighborConfig({"10.0.0.1", "remote-asn", "65000"}); + EXPECT_EQ(singleTok.attr(), "remote-asn"); + EXPECT_EQ(singleTok.values(), std::vector({"65000"})); + + // Two-token attribute wins longest-prefix match. + auto twoTok = BgpNeighborConfig({"10.0.0.1", "timers", "hold-time", "30"}); + EXPECT_EQ(twoTok.attr(), "timers hold-time"); + EXPECT_EQ(twoTok.values(), std::vector({"30"})); + + // Invalid: empty, bad address, unknown attribute, incomplete group. + EXPECT_THROW(BgpNeighborConfig({}), std::invalid_argument); + EXPECT_THROW(BgpNeighborConfig({"not-an-ip"}), std::invalid_argument); + EXPECT_THROW( + BgpNeighborConfig({"10.0.0.1", "no-such-attr", "1"}), + std::invalid_argument); + EXPECT_THROW( + BgpNeighborConfig({"10.0.0.1", "timers"}), std::invalid_argument); +} + +// ============================================================================== +// Attribute handlers +// ============================================================================== + +TEST_F(CmdConfigBgpNeighborTestFixture, bareCreate) { + auto result = run({"10.0.0.1"}); + EXPECT_THAT(result, HasSubstr("Successfully created BGP neighbor 10.0.0.1")); + ASSERT_EQ(peers().size(), 1); + EXPECT_EQ(*peers()[0].peer_addr(), "10.0.0.1"); + // bgpd aborts at config load on unparseable (empty) address fields, so a + // new peer must be born with parseable any-addresses of the right family. + EXPECT_EQ(*peers()[0].local_addr(), "0.0.0.0"); + EXPECT_EQ(*peers()[0].next_hop4(), "0.0.0.0"); + EXPECT_EQ(*peers()[0].next_hop6(), "::"); + EXPECT_TRUE(sessionFileExists()); + + run({"2001:db8::1"}); + ASSERT_EQ(peers().size(), 2); + EXPECT_EQ(*peers()[1].local_addr(), "::"); +} + +TEST_F(CmdConfigBgpNeighborTestFixture, setRemoteAsn) { + auto result = run({"10.0.0.1", "remote-asn", "65000"}); + EXPECT_THAT(result, HasSubstr("65000")); + ASSERT_EQ(peers().size(), 1); + EXPECT_EQ(peers()[0].remote_as_4_byte().value_or(0), 65000); +} + +TEST_F(CmdConfigBgpNeighborTestFixture, timersAccumulate) { + run({"10.0.0.1", "timers", "hold-time", "90"}); + run({"10.0.0.1", "timers", "keepalive", "30"}); + run({"10.0.0.1", "graceful-restart", "restart-time", "120"}); + ASSERT_EQ(peers().size(), 1); + const auto& timers = *peers()[0].bgp_peer_timers(); + EXPECT_EQ(*timers.hold_time_seconds(), 90); + EXPECT_EQ(*timers.keep_alive_seconds(), 30); + EXPECT_EQ(timers.graceful_restart_seconds().value_or(0), 120); +} + +TEST_F(CmdConfigBgpNeighborTestFixture, addPathMerge) { + run({"10.0.0.1", "add-path", "receive", "true"}); + EXPECT_EQ(peers()[0].add_path().value_or(AddPath(0)), AddPath::RECEIVE); + + run({"10.0.0.1", "add-path", "send", "true"}); + EXPECT_EQ(peers()[0].add_path().value_or(AddPath(0)), AddPath::BOTH); + + run({"10.0.0.1", "add-path", "receive", "false"}); + EXPECT_EQ(peers()[0].add_path().value_or(AddPath(0)), AddPath::SEND); + + run({"10.0.0.1", "add-path", "send", "false"}); + EXPECT_FALSE(peers()[0].add_path().has_value()); +} + +TEST_F(CmdConfigBgpNeighborTestFixture, connectMode) { + run({"10.0.0.1", "connect-mode", "PASSIVE"}); + EXPECT_TRUE(peers()[0].is_passive().value_or(false)); + + run({"10.0.0.1", "connect-mode", "ACTIVE"}); + EXPECT_FALSE(peers()[0].is_passive().value_or(true)); + + // BOTH has no thrift representation and must be rejected, leaving the + // previous value intact and nothing new persisted. + auto result = run({"10.0.0.1", "connect-mode", "BOTH"}); + EXPECT_THAT(result, HasSubstr("Error")); + EXPECT_FALSE(peers()[0].is_passive().value_or(true)); +} + +TEST_F(CmdConfigBgpNeighborTestFixture, boolAndStringAttributes) { + run({"10.0.0.1", "rr-client", "true"}); + run({"10.0.0.1", "confed-peer", "false"}); + run({"10.0.0.1", "redistribute-peer", "true"}); + run({"10.0.0.1", "enhanced-route-refresh", "true"}); + run({"10.0.0.1", "afi", "disable-ipv4-afi", "true"}); + run({"10.0.0.1", "afi", "disable-ipv6-afi", "false"}); + run({"10.0.0.1", "afi", "ipv4-over-ipv6-nh", "true"}); + run({"10.0.0.1", "graceful-restart", "stateful-ha", "true"}); + run({"10.0.0.1", "peer-group", "MY-GROUP"}); + run({"10.0.0.1", "peer-tag", "FSW"}); + run({"10.0.0.1", "ingress-policy", "IN_POLICY"}); + run({"10.0.0.1", "egress-policy", "OUT_POLICY"}); + run({"10.0.0.1", "bind-addr", "address", "10.0.0.2"}); + run({"10.0.0.1", "local-asn", "64512"}); + run({"10.0.0.1", "description", "spine", "peer"}); + + ASSERT_EQ(peers().size(), 1); + const auto& peer = peers()[0]; + EXPECT_TRUE(peer.is_rr_client().value_or(false)); + EXPECT_FALSE(peer.is_confed_peer().value_or(true)); + EXPECT_TRUE(peer.is_redistribute_peer().value_or(false)); + EXPECT_TRUE(peer.enhanced_route_refresh().value_or(false)); + EXPECT_TRUE(peer.disable_ipv4_afi().value_or(false)); + EXPECT_FALSE(peer.disable_ipv6_afi().value_or(true)); + EXPECT_TRUE(peer.v4_over_v6_nexthop().value_or(false)); + EXPECT_TRUE(peer.enable_stateful_ha().value_or(false)); + EXPECT_EQ(peer.peer_group_name().value_or(""), "MY-GROUP"); + EXPECT_EQ(peer.peer_tag().value_or(""), "FSW"); + EXPECT_EQ(peer.ingress_policy_name().value_or(""), "IN_POLICY"); + EXPECT_EQ(peer.egress_policy_name().value_or(""), "OUT_POLICY"); + EXPECT_EQ(*peer.local_addr(), "10.0.0.2"); + EXPECT_EQ(peer.local_as_4_byte().value_or(0), 64512); + // Multi-token description is re-joined. + EXPECT_EQ(peer.description().value_or(""), "spine peer"); +} + +TEST_F(CmdConfigBgpNeighborTestFixture, parityAttributes) { + // Attributes carried over from the deleted per-attribute peer commands. + run({"10.0.0.1", "next-hop4", "10.1.1.1"}); + run({"10.0.0.1", "next-hop6", "2001:db8::a"}); + run({"10.0.0.1", "next-hop-self", "true"}); + run({"10.0.0.1", "peer-id", "test-peer:v6:1"}); + run({"10.0.0.1", "type", "BGP_MONITOR"}); + run({"10.0.0.1", "link-bandwidth", "100G"}); + run({"10.0.0.1", "advertise-lbw", "1"}); + run({"10.0.0.1", "receive-lbw", "ACCEPT"}); // enum value name accepted + run( + {"10.0.0.1", + "advertise-lbw", + "true"}); // bool alias (old peer cmd compat) + run({"10.0.0.1", "timers", "withdraw-unprog-delay", "5"}); + run({"10.0.0.1", "max-route", "pre-warning-threshold", "100"}); + run({"10.0.0.1", "max-route", "pre-warning-only", "true"}); + run({"10.0.0.1", "max-route", "post-warning-only", "false"}); + + ASSERT_EQ(peers().size(), 1); + const auto& peer = peers()[0]; + EXPECT_EQ(*peer.next_hop4(), "10.1.1.1"); + EXPECT_EQ(*peer.next_hop6(), "2001:db8::a"); + EXPECT_TRUE(peer.next_hop_self().value_or(false)); + EXPECT_EQ(peer.peer_id().value_or(""), "test-peer:v6:1"); + EXPECT_EQ(peer.type().value_or(""), "BGP_MONITOR"); + EXPECT_EQ(peer.link_bandwidth_bps().value_or(""), "100G"); + EXPECT_EQ( + static_cast(peer.advertise_link_bandwidth().value_or( + facebook::neteng::fboss::bgp_attr::AdvertiseLinkBandwidth::DISABLE)), + 1); + EXPECT_EQ( + static_cast(peer.receive_link_bandwidth().value_or( + facebook::neteng::fboss::bgp_attr::ReceiveLinkBandwidth::DISABLE)), + 1); + EXPECT_EQ(*peer.bgp_peer_timers()->withdraw_unprog_delay_seconds(), 5); + EXPECT_EQ(*peer.pre_filter()->warning_limit(), 100); + EXPECT_TRUE(*peer.pre_filter()->warning_only()); + EXPECT_FALSE(*peer.post_filter()->warning_only()); +} + +TEST_F(CmdConfigBgpNeighborTestFixture, valueValidation) { + // ASN must fit in 4 bytes (RFC 6793). + EXPECT_THAT( + run({"10.0.0.1", "remote-asn", "4294967296"}), HasSubstr("Invalid")); + EXPECT_THAT( + run({"10.0.0.1", "local-asn", "18446744073709551615"}), + HasSubstr("Invalid")); + // IP-valued attributes validate the address and its family. + EXPECT_THAT( + run({"10.0.0.1", "bind-addr", "address", "not-an-ip"}), + HasSubstr("Invalid")); + EXPECT_THAT(run({"10.0.0.1", "next-hop4", "2001:db8::a"}), HasSubstr("IPv4")); + EXPECT_THAT(run({"10.0.0.1", "next-hop6", "10.1.1.1"}), HasSubstr("IPv6")); + // Enum-valued attributes reject non-member modes but accept value names + // and true/false aliases (compat with the replaced boolean peer commands). + EXPECT_THAT( + run({"10.0.0.1", "advertise-lbw", "99"}), HasSubstr("not a valid mode")); + // link-bandwidth must be digits with an optional K/M/G suffix. + EXPECT_THAT(run({"10.0.0.1", "link-bandwidth", "10Q"}), HasSubstr("Invalid")); + EXPECT_THAT(run({"10.0.0.1", "link-bandwidth", "G10"}), HasSubstr("Invalid")); + EXPECT_FALSE(sessionFileExists()) + << "session file should not exist after rejected input"; +} + +TEST_F(CmdConfigBgpNeighborTestFixture, routeLimits) { + run({"10.0.0.1", "max-route", "pre-filter", "45000"}); + run({"10.0.0.1", "max-route", "post-filter", "50000"}); + run({"10.0.0.1", "max-route", "post-warning-threshold", "48000"}); + const auto& peer = peers()[0]; + EXPECT_EQ(*peer.pre_filter()->max_routes(), 45000); + EXPECT_EQ(*peer.post_filter()->max_routes(), 50000); + EXPECT_EQ(*peer.post_filter()->warning_limit(), 48000); +} + +TEST_F(CmdConfigBgpNeighborTestFixture, normalizedAddressesShareOnePeer) { + run({"2001:DB8::1", "remote-asn", "65000"}); + run({"2001:db8:0:0::1", "timers", "hold-time", "30"}); + ASSERT_EQ(peers().size(), 1); + EXPECT_EQ(*peers()[0].peer_addr(), "2001:db8::1"); +} + +TEST_F(CmdConfigBgpNeighborTestFixture, distinctAddressesCreateDistinctPeers) { + run({"10.0.0.1", "remote-asn", "65000"}); + run({"10.0.0.2", "remote-asn", "65001"}); + EXPECT_EQ(peers().size(), 2); +} + +// ============================================================================== +// Reject paths — the error is surfaced and nothing is persisted +// ============================================================================== + +TEST_F(CmdConfigBgpNeighborTestFixture, invalidValuesRejected) { + EXPECT_THAT(run({"10.0.0.1", "rr-client", "maybe"}), HasSubstr("Invalid")); + EXPECT_THAT( + run({"10.0.0.1", "timers", "hold-time", "-1"}), + HasSubstr("non-negative")); + EXPECT_THAT( + run({"10.0.0.1", "remote-asn", "not-a-number"}), HasSubstr("Invalid")); + EXPECT_THAT( + run({"10.0.0.1", "max-route", "pre-filter", "-5"}), + HasSubstr("non-negative")); + EXPECT_FALSE(sessionFileExists()) + << "session file should not exist after rejected input"; +} + +TEST_F(CmdConfigBgpNeighborTestFixture, unsupportedAttributesRejected) { + // No thrift representation (or not per-neighbor) — refuse instead of + // persisting dead config. + EXPECT_THAT( + run({"10.0.0.1", "peer-port", "1179"}), HasSubstr("not supported")); + EXPECT_THAT( + run({"10.0.0.1", "bind-addr", "port", "1179"}), + HasSubstr("not supported")); + EXPECT_THAT( + run({"10.0.0.1", "afi", "ipv4-labeled-unicast", "true"}), + HasSubstr("not supported")); + EXPECT_THAT( + run({"10.0.0.1", "afi", "ipv6-labeled-unicast", "true"}), + HasSubstr("not supported")); + EXPECT_FALSE(sessionFileExists()) + << "session file should not exist after rejected input"; +} + +} // namespace facebook::fboss diff --git a/fboss/cli/fboss2/test/config/CmdConfigBgpPeerGroupTest.cpp b/fboss/cli/fboss2/test/config/CmdConfigBgpPeerGroupTest.cpp new file mode 100644 index 0000000000000..1585e6e683dcb --- /dev/null +++ b/fboss/cli/fboss2/test/config/CmdConfigBgpPeerGroupTest.cpp @@ -0,0 +1,247 @@ +// (c) Meta Platforms, Inc. and affiliates. Confidential and proprietary. + +#include +#include +#include + +#include +#include +#include +#include + +#include "configerator/structs/neteng/fboss/bgp/if/gen-cpp2/bgp_attr_types.h" +#include "fboss/cli/fboss2/commands/config/protocol/bgp/peer-group/CmdConfigProtocolBgpPeerGroup.h" +#include "fboss/cli/fboss2/session/ConfigSession.h" +#include "fboss/cli/fboss2/test/config/CmdConfigTestBase.h" +#include "fboss/cli/fboss2/utils/HostInfo.h" + +using namespace ::testing; +using facebook::neteng::fboss::bgp_attr::AddPath; + +namespace facebook::fboss { + +// The peer-group dispatcher only touches the BGP side of ConfigSession, which +// seeds from thrift schema defaults when neither a staged session nor a +// system bgpcpp.conf exists — so no seed agent config is needed (mirrors +// CmdConfigBgpNeighborTest). +class CmdConfigBgpPeerGroupTestFixture : public CmdConfigTestBase { + public: + CmdConfigBgpPeerGroupTestFixture() + : CmdConfigTestBase("bgp_peer_group_test_%%%%-%%%%-%%%%", "") {} + + void SetUp() override { + CmdConfigTestBase::SetUp(); + setupTestableConfigSession(); + } + + std::string run(const std::vector& tokens) { + CmdConfigProtocolBgpPeerGroup cmd; + HostInfo hostInfo("testhost"); + return cmd.queryClient(hostInfo, BgpPeerGroupConfig(tokens)); + } + + const std::vector& groups() { + return ConfigSession::getInstance().getBgpConfig().peer_groups().ensure(); + } + + bool sessionFileExists() { + return std::filesystem::exists( + ConfigSession::getInstance().getBgpSessionConfigPath()); + } +}; + +// ============================================================================== +// BgpPeerGroupConfig (arg) validation +// ============================================================================== + +TEST_F(CmdConfigBgpPeerGroupTestFixture, argValidation) { + // Bare create. + EXPECT_EQ(BgpPeerGroupConfig({"SPINE"}).groupName(), "SPINE"); + EXPECT_TRUE(BgpPeerGroupConfig({"SPINE"}).attr().empty()); + + // Single-token attribute. + auto singleTok = BgpPeerGroupConfig({"SPINE", "remote-asn", "65000"}); + EXPECT_EQ(singleTok.attr(), "remote-asn"); + EXPECT_EQ(singleTok.values(), std::vector({"65000"})); + + // Two-token attribute wins longest-prefix match. + auto twoTok = BgpPeerGroupConfig({"SPINE", "timers", "hold-time", "30"}); + EXPECT_EQ(twoTok.attr(), "timers hold-time"); + EXPECT_EQ(twoTok.values(), std::vector({"30"})); + + // Invalid: empty, empty name, unknown attribute, incomplete group. + EXPECT_THROW(BgpPeerGroupConfig({}), std::invalid_argument); + EXPECT_THROW(BgpPeerGroupConfig({""}), std::invalid_argument); + EXPECT_THROW( + BgpPeerGroupConfig({"SPINE", "no-such-attr", "1"}), + std::invalid_argument); + EXPECT_THROW(BgpPeerGroupConfig({"SPINE", "timers"}), std::invalid_argument); +} + +// ============================================================================== +// Attribute handlers +// ============================================================================== + +TEST_F(CmdConfigBgpPeerGroupTestFixture, bareCreate) { + auto result = run({"SPINE"}); + EXPECT_THAT(result, HasSubstr("Successfully created BGP peer-group SPINE")); + ASSERT_EQ(groups().size(), 1); + EXPECT_EQ(*groups()[0].name(), "SPINE"); + // PeerGroup's only non-optional field is `name`; nothing else is seeded. + EXPECT_FALSE(groups()[0].local_addr().has_value()); + EXPECT_TRUE(sessionFileExists()); + + run({"LEAF"}); + ASSERT_EQ(groups().size(), 2); + EXPECT_EQ(*groups()[1].name(), "LEAF"); +} + +TEST_F(CmdConfigBgpPeerGroupTestFixture, setRemoteAsn) { + auto result = run({"SPINE", "remote-asn", "65000"}); + EXPECT_THAT(result, HasSubstr("65000")); + ASSERT_EQ(groups().size(), 1); + EXPECT_EQ(groups()[0].remote_as_4_byte().value_or(0), 65000); +} + +TEST_F(CmdConfigBgpPeerGroupTestFixture, timersAccumulate) { + run({"SPINE", "timers", "hold-time", "90"}); + run({"SPINE", "timers", "keepalive", "30"}); + run({"SPINE", "graceful-restart", "restart-time", "120"}); + ASSERT_EQ(groups().size(), 1); + const auto& timers = *groups()[0].bgp_peer_timers(); + EXPECT_EQ(*timers.hold_time_seconds(), 90); + EXPECT_EQ(*timers.keep_alive_seconds(), 30); + EXPECT_EQ(timers.graceful_restart_seconds().value_or(0), 120); +} + +TEST_F(CmdConfigBgpPeerGroupTestFixture, addPathMerge) { + run({"SPINE", "add-path", "receive", "true"}); + EXPECT_EQ(groups()[0].add_path().value_or(AddPath(0)), AddPath::RECEIVE); + + run({"SPINE", "add-path", "send", "true"}); + EXPECT_EQ(groups()[0].add_path().value_or(AddPath(0)), AddPath::BOTH); + + run({"SPINE", "add-path", "receive", "false"}); + EXPECT_EQ(groups()[0].add_path().value_or(AddPath(0)), AddPath::SEND); + + run({"SPINE", "add-path", "send", "false"}); + EXPECT_FALSE(groups()[0].add_path().has_value()); +} + +TEST_F(CmdConfigBgpPeerGroupTestFixture, connectMode) { + run({"SPINE", "connect-mode", "PASSIVE"}); + EXPECT_TRUE(groups()[0].is_passive().value_or(false)); + + run({"SPINE", "connect-mode", "ACTIVE"}); + EXPECT_FALSE(groups()[0].is_passive().value_or(true)); + + // BOTH has no thrift representation and must be rejected, leaving the + // previous value intact and nothing new persisted. + auto result = run({"SPINE", "connect-mode", "BOTH"}); + EXPECT_THAT(result, HasSubstr("Error")); + EXPECT_FALSE(groups()[0].is_passive().value_or(true)); +} + +TEST_F(CmdConfigBgpPeerGroupTestFixture, boolAndStringAttributes) { + run({"SPINE", "rr-client", "true"}); + run({"SPINE", "confed-peer", "false"}); + run({"SPINE", "redistribute-peer", "true"}); + run({"SPINE", "enhanced-route-refresh", "true"}); + run({"SPINE", "afi", "disable-ipv4-afi", "true"}); + run({"SPINE", "afi", "disable-ipv6-afi", "false"}); + run({"SPINE", "afi", "ipv4-over-ipv6-nh", "true"}); + run({"SPINE", "graceful-restart", "stateful-ha", "true"}); + run({"SPINE", "peer-tag", "FSW"}); + run({"SPINE", "ingress-policy", "IN_POLICY"}); + run({"SPINE", "egress-policy", "OUT_POLICY"}); + run({"SPINE", "local-asn", "64512"}); + run({"SPINE", "next-hop-self", "true"}); + run({"SPINE", "description", "spine", "group"}); + + ASSERT_EQ(groups().size(), 1); + const auto& group = groups()[0]; + EXPECT_TRUE(group.is_rr_client().value_or(false)); + EXPECT_FALSE(group.is_confed_peer().value_or(true)); + EXPECT_TRUE(group.is_redistribute_peer().value_or(false)); + EXPECT_TRUE(group.enhanced_route_refresh().value_or(false)); + EXPECT_TRUE(group.disable_ipv4_afi().value_or(false)); + EXPECT_FALSE(group.disable_ipv6_afi().value_or(true)); + EXPECT_TRUE(group.v4_over_v6_nexthop().value_or(false)); + EXPECT_TRUE(group.enable_stateful_ha().value_or(false)); + EXPECT_EQ(group.peer_tag().value_or(""), "FSW"); + EXPECT_EQ(group.ingress_policy_name().value_or(""), "IN_POLICY"); + EXPECT_EQ(group.egress_policy_name().value_or(""), "OUT_POLICY"); + EXPECT_EQ(group.local_as_4_byte().value_or(0), 64512); + EXPECT_TRUE(group.next_hop_self().value_or(false)); + // Multi-token description is re-joined. + EXPECT_EQ(group.description().value_or(""), "spine group"); +} + +TEST_F(CmdConfigBgpPeerGroupTestFixture, routeLimits) { + run({"SPINE", "max-route", "pre-filter", "45000"}); + run({"SPINE", "max-route", "post-filter", "50000"}); + run({"SPINE", "max-route", "post-warning-threshold", "48000"}); + // Parity attributes carried over from the deleted per-attribute commands. + run({"SPINE", "max-route", "pre-warning-threshold", "40000"}); + run({"SPINE", "max-route", "pre-warning-only", "true"}); + run({"SPINE", "max-route", "post-warning-only", "false"}); + const auto& group = groups()[0]; + EXPECT_EQ(*group.pre_filter()->max_routes(), 45000); + EXPECT_EQ(*group.post_filter()->max_routes(), 50000); + EXPECT_EQ(*group.post_filter()->warning_limit(), 48000); + EXPECT_EQ(*group.pre_filter()->warning_limit(), 40000); + EXPECT_TRUE(*group.pre_filter()->warning_only()); + EXPECT_FALSE(*group.post_filter()->warning_only()); +} + +TEST_F(CmdConfigBgpPeerGroupTestFixture, valueValidation) { + // ASN must fit in 4 bytes (RFC 6793). + EXPECT_THAT(run({"SPINE", "remote-asn", "4294967296"}), HasSubstr("Invalid")); + EXPECT_THAT( + run({"SPINE", "local-asn", "18446744073709551615"}), + HasSubstr("Invalid")); + EXPECT_FALSE(sessionFileExists()) + << "session file should not exist after rejected input"; +} + +TEST_F(CmdConfigBgpPeerGroupTestFixture, namedGroupsAreDistinct) { + run({"SPINE", "remote-asn", "65000"}); + run({"LEAF", "remote-asn", "65001"}); + ASSERT_EQ(groups().size(), 2); + // Re-referencing an existing group by name updates it, not appends. + run({"SPINE", "local-asn", "64512"}); + EXPECT_EQ(groups().size(), 2); +} + +// ============================================================================== +// Reject paths — the error is surfaced and nothing is persisted +// ============================================================================== + +TEST_F(CmdConfigBgpPeerGroupTestFixture, invalidValuesRejected) { + EXPECT_THAT(run({"SPINE", "rr-client", "maybe"}), HasSubstr("Invalid")); + EXPECT_THAT( + run({"SPINE", "timers", "hold-time", "-1"}), HasSubstr("non-negative")); + EXPECT_THAT( + run({"SPINE", "remote-asn", "not-a-number"}), HasSubstr("Invalid")); + EXPECT_THAT( + run({"SPINE", "max-route", "pre-filter", "-5"}), + HasSubstr("non-negative")); + EXPECT_FALSE(sessionFileExists()) + << "session file should not exist after rejected input"; +} + +TEST_F(CmdConfigBgpPeerGroupTestFixture, unsupportedAttributesRejected) { + // These attributes have no per-peer-group thrift field, so they are not in + // the dispatch table and are refused at parse time as unknown attributes. + EXPECT_THROW(run({"SPINE", "peer-port", "1179"}), std::invalid_argument); + EXPECT_THROW( + run({"SPINE", "afi", "ipv4-labeled-unicast", "true"}), + std::invalid_argument); + EXPECT_THROW( + run({"SPINE", "afi", "ipv6-labeled-unicast", "true"}), + std::invalid_argument); + EXPECT_FALSE(sessionFileExists()) + << "session file should not exist after rejected input"; +} + +} // namespace facebook::fboss diff --git a/fboss/cli/fboss2/test/config/CmdConfigBgpPolicyAsPathListTest.cpp b/fboss/cli/fboss2/test/config/CmdConfigBgpPolicyAsPathListTest.cpp new file mode 100644 index 0000000000000..582cd97cbaad0 --- /dev/null +++ b/fboss/cli/fboss2/test/config/CmdConfigBgpPolicyAsPathListTest.cpp @@ -0,0 +1,239 @@ +// (c) Meta Platforms, Inc. and affiliates. Confidential and proprietary. + +#include +#include +#include + +#include +#include +#include +#include + +#include "configerator/structs/neteng/bgp_policy/thrift/gen-cpp2/bgp_policy_types.h" +#include "configerator/structs/neteng/bgp_policy/thrift/gen-cpp2/routing_policy_types.h" +#include "fboss/cli/fboss2/commands/config/protocol/bgp/policy/as-path-list/CmdConfigProtocolBgpPolicyAsPathList.h" +#include "fboss/cli/fboss2/session/ConfigSession.h" +#include "fboss/cli/fboss2/test/config/CmdConfigTestBase.h" +#include "fboss/cli/fboss2/utils/HostInfo.h" + +using namespace ::testing; +using facebook::bgp::routing_policy::MatchValueLogicOperator; + +namespace facebook::fboss { + +// The as-path-list dispatcher only touches the BGP side of ConfigSession, +// which seeds from thrift schema defaults when neither a staged session nor a +// system bgpcpp.conf exists — so no seed agent config is needed (mirrors +// CmdConfigBgpPeerGroupTest). +class CmdConfigBgpPolicyAsPathListTestFixture : public CmdConfigTestBase { + public: + CmdConfigBgpPolicyAsPathListTestFixture() + : CmdConfigTestBase("bgp_aspath_list_test_%%%%-%%%%-%%%%", "") {} + + void SetUp() override { + CmdConfigTestBase::SetUp(); + setupTestableConfigSession(); + } + + std::string run(const std::vector& tokens) { + CmdConfigProtocolBgpPolicyAsPathList cmd; + HostInfo hostInfo("testhost"); + return cmd.queryClient(hostInfo, BgpAsPathListConfig(tokens)); + } + + const std::vector& lists() { + return *ConfigSession::getInstance() + .getBgpConfig() + .policies() + .ensure() + .aspath_lists(); + } + + bool sessionFileExists() { + return std::filesystem::exists( + ConfigSession::getInstance().getBgpSessionConfigPath()); + } +}; + +// ============================================================================== +// BgpAsPathListConfig (arg) validation +// ============================================================================== + +TEST_F(CmdConfigBgpPolicyAsPathListTestFixture, argValidation) { + // Bare create list. + auto bareList = BgpAsPathListConfig({"AS100"}); + EXPECT_EQ(bareList.listName(), "AS100"); + EXPECT_FALSE(bareList.hasEntry()); + EXPECT_TRUE(bareList.attr().empty()); + + // List-level attribute. + auto listAttr = BgpAsPathListConfig({"AS100", "description", "my", "list"}); + EXPECT_FALSE(listAttr.hasEntry()); + EXPECT_EQ(listAttr.attr(), "description"); + EXPECT_EQ(listAttr.values(), std::vector({"my", "list"})); + + // Bare create entry. + auto bareEntry = BgpAsPathListConfig({"AS100", "entry", "10"}); + EXPECT_TRUE(bareEntry.hasEntry()); + EXPECT_EQ(bareEntry.seqNum(), 10); + EXPECT_TRUE(bareEntry.attr().empty()); + + // Entry-level attribute. + auto entryAttr = + BgpAsPathListConfig({"AS100", "entry", "10", "asn-regexp", "^65000_"}); + EXPECT_TRUE(entryAttr.hasEntry()); + EXPECT_EQ(entryAttr.seqNum(), 10); + EXPECT_EQ(entryAttr.attr(), "asn-regexp"); + EXPECT_EQ(entryAttr.values(), std::vector({"^65000_"})); + + // Invalid: empty, empty name, bad seq-num, unknown list/entry attributes, + // entry keyword without a seq-num. + EXPECT_THROW(BgpAsPathListConfig({}), std::invalid_argument); + EXPECT_THROW(BgpAsPathListConfig({""}), std::invalid_argument); + EXPECT_THROW( + BgpAsPathListConfig({"AS100", "entry", "not-a-num"}), + std::invalid_argument); + EXPECT_THROW(BgpAsPathListConfig({"AS100", "entry"}), std::invalid_argument); + EXPECT_THROW( + BgpAsPathListConfig({"AS100", "no-such-attr", "1"}), + std::invalid_argument); + // asn-regexp is an entry attribute, not a list attribute. + EXPECT_THROW( + BgpAsPathListConfig({"AS100", "asn-regexp", "^65000_"}), + std::invalid_argument); + // description is a list attribute, not valid at the entry level? It IS valid + // at both levels, so this must NOT throw. + EXPECT_NO_THROW( + BgpAsPathListConfig({"AS100", "entry", "10", "description", "x"})); +} + +// ============================================================================== +// List-level handlers +// ============================================================================== + +TEST_F(CmdConfigBgpPolicyAsPathListTestFixture, bareCreateList) { + auto result = run({"AS100"}); + EXPECT_THAT(result, HasSubstr("Successfully created BGP as-path-list AS100")); + ASSERT_EQ(lists().size(), 1); + EXPECT_EQ(*lists()[0].name(), "AS100"); + EXPECT_TRUE(lists()[0].as_path_list()->empty()); + EXPECT_TRUE(sessionFileExists()); + + run({"AS200"}); + ASSERT_EQ(lists().size(), 2); + EXPECT_EQ(*lists()[1].name(), "AS200"); +} + +TEST_F(CmdConfigBgpPolicyAsPathListTestFixture, setListDescription) { + auto result = run({"AS100", "description", "spine", "as-paths"}); + EXPECT_THAT(result, HasSubstr("Successfully set description")); + ASSERT_EQ(lists().size(), 1); + // Multi-token description is re-joined. + EXPECT_EQ(*lists()[0].description(), "spine as-paths"); +} + +TEST_F(CmdConfigBgpPolicyAsPathListTestFixture, namedListsAreDistinct) { + run({"AS100", "description", "one"}); + run({"AS200", "description", "two"}); + ASSERT_EQ(lists().size(), 2); + // Re-referencing an existing list by name updates it, not appends. + run({"AS100", "description", "one-updated"}); + ASSERT_EQ(lists().size(), 2); + EXPECT_EQ(*lists()[0].description(), "one-updated"); +} + +// ============================================================================== +// Entry-level handlers +// ============================================================================== + +TEST_F(CmdConfigBgpPolicyAsPathListTestFixture, bareCreateEntry) { + auto result = run({"AS100", "entry", "10"}); + EXPECT_THAT( + result, + HasSubstr("Successfully created BGP as-path-list AS100 entry 10")); + ASSERT_EQ(lists().size(), 1); + ASSERT_EQ(lists()[0].as_path_list()->size(), 1); + EXPECT_EQ(*lists()[0].as_path_list()->at(0).sequence_number(), 10); +} + +TEST_F(CmdConfigBgpPolicyAsPathListTestFixture, setEntryAttributes) { + run({"AS100", "entry", "10", "asn-regexp", "^65000_"}); + run({"AS100", "entry", "10", "description", "match", "65000"}); + run({"AS100", "entry", "10", "match-logic", "NOT_EQUAL"}); + + ASSERT_EQ(lists().size(), 1); + ASSERT_EQ(lists()[0].as_path_list()->size(), 1); + const auto& entry = lists()[0].as_path_list()->at(0); + EXPECT_EQ(*entry.sequence_number(), 10); + EXPECT_EQ(entry.as_path()->as_path_ref()->asn_regexp().value(), "^65000_"); + EXPECT_EQ(*entry.description(), "match 65000"); + EXPECT_EQ(*entry.match_logic_type(), MatchValueLogicOperator::NOT_EQUAL); +} + +TEST_F(CmdConfigBgpPolicyAsPathListTestFixture, entriesAccumulateAndAreKeyed) { + run({"AS100", "entry", "10", "asn-regexp", "^65000_"}); + run({"AS100", "entry", "20", "asn-regexp", "^65001_"}); + ASSERT_EQ(lists().size(), 1); + ASSERT_EQ(lists()[0].as_path_list()->size(), 2); + + // Re-referencing an existing entry by seq-num updates it, not appends. + run({"AS100", "entry", "10", "match-logic", "EQUAL"}); + EXPECT_EQ(lists()[0].as_path_list()->size(), 2); +} + +TEST_F(CmdConfigBgpPolicyAsPathListTestFixture, asnRegexpAllowsSpaces) { + // AS-path regexes separate ASNs with spaces; the pattern must survive as a + // single re-joined value. + run({"AS100", "entry", "10", "asn-regexp", "^65000", "65001$"}); + const auto& entry = lists()[0].as_path_list()->at(0); + EXPECT_EQ( + entry.as_path()->as_path_ref()->asn_regexp().value(), "^65000 65001$"); +} + +TEST_F(CmdConfigBgpPolicyAsPathListTestFixture, reReferenceReportsExisting) { + run({"AS100"}); + // A second bare reference must not claim to have created it again. + EXPECT_THAT(run({"AS100"}), HasSubstr("already exists")); + ASSERT_EQ(lists().size(), 1); + + run({"AS100", "entry", "10"}); + EXPECT_THAT( + run({"AS100", "entry", "10"}), HasSubstr("entry 10 already exists")); + EXPECT_EQ(lists()[0].as_path_list()->size(), 1); +} + +TEST_F(CmdConfigBgpPolicyAsPathListTestFixture, matchLogicDefaultIsEqual) { + run({"AS100", "entry", "10"}); + const auto& entry = lists()[0].as_path_list()->at(0); + EXPECT_EQ(*entry.match_logic_type(), MatchValueLogicOperator::EQUAL); +} + +// ============================================================================== +// Reject paths — the error is surfaced and nothing is persisted +// ============================================================================== + +TEST_F(CmdConfigBgpPolicyAsPathListTestFixture, invalidMatchLogicRejected) { + auto result = run({"AS100", "entry", "10", "match-logic", "MAYBE"}); + EXPECT_THAT(result, HasSubstr("Invalid")); + // The rejected value must not leave a phantom list/entry behind. + EXPECT_TRUE(lists().empty()); + EXPECT_FALSE(sessionFileExists()) + << "session file should not exist after rejected input"; +} + +TEST_F( + CmdConfigBgpPolicyAsPathListTestFixture, + rejectedEntryOnExistingListKeepsList) { + // Create the list first, then reject an entry value on it. + run({"AS100", "description", "keep-me"}); + ASSERT_EQ(lists().size(), 1); + + auto result = run({"AS100", "entry", "10", "match-logic", "MAYBE"}); + EXPECT_THAT(result, HasSubstr("Invalid")); + // The pre-existing list survives; only the phantom entry is rolled back. + ASSERT_EQ(lists().size(), 1); + EXPECT_TRUE(lists()[0].as_path_list()->empty()); + EXPECT_EQ(*lists()[0].description(), "keep-me"); +} + +} // namespace facebook::fboss diff --git a/fboss/cli/fboss2/test/config/CmdConfigSessionDiffTest.cpp b/fboss/cli/fboss2/test/config/CmdConfigSessionDiffTest.cpp index 87fc6505a8a1d..e669565f7e741 100644 --- a/fboss/cli/fboss2/test/config/CmdConfigSessionDiffTest.cpp +++ b/fboss/cli/fboss2/test/config/CmdConfigSessionDiffTest.cpp @@ -6,8 +6,12 @@ #include #include "fboss/cli/fboss2/commands/config/session/CmdConfigSessionDiff.h" +#include "fboss/cli/fboss2/session/ConfigSession.h" #include "fboss/cli/fboss2/utils/CmdUtils.h" +#include +#include + using namespace ::testing; namespace facebook::fboss { @@ -313,4 +317,23 @@ TEST_F(CmdConfigSessionDiffTestFixture, printOutputNoDifferences) { EXPECT_NE(output.find("No differences"), std::string::npos); } +TEST_F(CmdConfigSessionDiffTestFixture, diffBgpSession) { + setupTestableConfigSession(); + // Remove the agent session so only a BGP session is staged. + std::filesystem::remove(getSessionConfigPath()); + + auto& session = ConfigSession::getInstance(); + session.getBgpConfig().router_id() = "9.9.9.9"; + session.saveBgpConfig(); + + auto cmd = CmdConfigSessionDiff(); + utils::RevisionList emptyRevisions(std::vector{}); + auto result = cmd.queryClient(localhost(), emptyRevisions); + + // A BGP-only session must be visible (not gated out as "no session"), and + // the staged router_id must show up in the diff. + EXPECT_EQ(result.find("No config session exists"), std::string::npos); + EXPECT_NE(result.find("9.9.9.9"), std::string::npos); +} + } // namespace facebook::fboss diff --git a/fboss/cli/fboss2/test/config/CmdConfigSessionTest.cpp b/fboss/cli/fboss2/test/config/CmdConfigSessionTest.cpp index e91e95c08376e..f142911167558 100644 --- a/fboss/cli/fboss2/test/config/CmdConfigSessionTest.cpp +++ b/fboss/cli/fboss2/test/config/CmdConfigSessionTest.cpp @@ -9,12 +9,14 @@ #include #include #include +#include #include #include #include #include #include "fboss/cli/fboss2/test/TestableConfigSession.h" +#include "fboss/cli/fboss2/test/config/MockSystemdInterface.h" namespace fs = std::filesystem; @@ -187,7 +189,7 @@ TEST_F(ConfigSessionTestFixture, sessionCommit) { // Verify metadata file was also committed to git auto metadataCommits = git.log(targetMetadata.string()); - EXPECT_EQ(metadataCommits.size(), 2); // 2 commits + EXPECT_EQ(metadataCommits.size(), 3); // Initial baseline + 2 commits } } @@ -501,7 +503,8 @@ TEST_F(ConfigSessionTestFixture, rollbackToSpecificCommit) { // Verify metadata file history auto metadataCommits = git.log(metadataPath.string()); - EXPECT_EQ(metadataCommits.size(), 3); // 2 commits + rollback + EXPECT_EQ( + metadataCommits.size(), 4); // Initial baseline + 2 commits + rollback } } @@ -1275,4 +1278,320 @@ TEST_F(ConfigSessionTestFixture, commitTwiceSecondIsEmpty) { } } +// Editing one part of the typed BGP config (e.g. a top-level field) and saving +// must round-trip the WHOLE config, so peers/peer-groups already staged in the +// file survive. ConfigSession owns the entire typed bgp::thrift::BgpConfig and +// is agnostic about which section a command touches. +TEST_F(ConfigSessionTestFixture, bgpConfigEditPreservesOtherSections) { + fs::path sessionDir = getTestHomeDir() / ".fboss2"; + TestableConfigSession session( + sessionDir.string(), (getTestEtcDir() / "coop").string()); + + // Stage a config that already has peers/peer-groups (as another command would + // have done via the same typed config). + fs::path bgpPath = sessionDir / "bgp_config.json"; + folly::dynamic staged = folly::dynamic::object( + "peers", + folly::dynamic::array( + folly::dynamic::object("peer_addr", "2401:db00::1")( + "is_passive", true)))( + "peer_groups", + folly::dynamic::array(folly::dynamic::object("name", "RACK"))); + ASSERT_TRUE(folly::writeFile(folly::toPrettyJson(staged), bgpPath.c_str())); + + // Edit a top-level field via the typed API and persist. + auto& bgp = session.getBgpConfig(); + bgp.router_id() = "10.0.0.1"; + bgp.count_confeds_in_as_path_len() = true; + session.setCommandLine( + "fboss2-dev config protocol bgp global router-id 10.0.0.1"); + session.saveBgpConfig(); + + // The saved file carries the edited fields AND the untouched peers/groups. + std::string content; + ASSERT_TRUE(folly::readFile(bgpPath.c_str(), content)); + auto saved = folly::parseJson(content); + + EXPECT_EQ(saved["router_id"].asString(), "10.0.0.1"); + ASSERT_TRUE(saved.count("count_confeds_in_as_path_len")); + EXPECT_TRUE(saved["count_confeds_in_as_path_len"].asBool()); + + ASSERT_TRUE(saved.count("peers")); + ASSERT_EQ(saved["peers"].size(), 1); + EXPECT_EQ(saved["peers"][0]["peer_addr"].asString(), "2401:db00::1"); + EXPECT_TRUE(saved["peers"][0]["is_passive"].asBool()); + + ASSERT_TRUE(saved.count("peer_groups")); + ASSERT_EQ(saved["peer_groups"].size(), 1); + EXPECT_EQ(saved["peer_groups"][0]["name"].asString(), "RACK"); + + // A BGP restart must be recorded so `config session commit` applies it. + EXPECT_EQ( + session.getRequiredAction(cli::ServiceType::BGP), + cli::ConfigActionLevel::BGP_RESTART); +} + +// A staged BGP edit persists to disk and is seeded back by a fresh session via +// the typed view. +TEST_F(ConfigSessionTestFixture, bgpConfigEditPersistsAndSeeds) { + fs::path sessionDir = getTestHomeDir() / ".fboss2"; + fs::path bgpPath = sessionDir / "bgp_config.json"; + { + TestableConfigSession session( + sessionDir.string(), (getTestEtcDir() / "coop").string()); + auto& bgp = session.getBgpConfig(); + bgp.local_as_4_byte() = 65001; + session.setCommandLine( + "fboss2-dev config protocol bgp global local-asn 65001"); + session.saveBgpConfig(); + } + ASSERT_TRUE(fs::exists(bgpPath)); + + { + TestableConfigSession session( + sessionDir.string(), (getTestEtcDir() / "coop").string()); + auto& bgp = session.getBgpConfig(); + ASSERT_TRUE(bgp.local_as_4_byte().has_value()); + EXPECT_EQ(*bgp.local_as_4_byte(), 65001); + } +} + +// A BGP-only session must be rebaseable when another commit advances HEAD; the +// staged BGP global edit is preserved (merged against an unchanged bgpcpp.conf) +// and the agent change committed by the other user is folded in. +TEST_F(ConfigSessionTestFixture, rebaseBgpSession) { + fs::path sessionDir1 = getTestHomeDir() / ".fboss2_user1"; + fs::path sessionDir2 = getTestHomeDir() / ".fboss2_user2"; + + setupMockedAgentServer(); + // Only user1's agent commit reloads the agent; rebase does not. + EXPECT_CALL(getMockAgent(), reloadConfig()).Times(1); + + TestableConfigSession session1( + sessionDir1.string(), (getTestEtcDir() / "coop").string()); + TestableConfigSession session2( + sessionDir2.string(), (getTestEtcDir() / "coop").string()); + + // user1 commits an agent change -> HEAD advances, session2's base goes stale. + (*session1.getAgentConfig().sw()->ports())[0].description() = "User1 change"; + session1.setCommandLine("config interface eth1/1/1 description User1"); + session1.saveConfig(cli::ServiceType::AGENT, cli::ConfigActionLevel::HITLESS); + EXPECT_FALSE(session1.commit(localhost()).commitSha.empty()); + + // user2 stages a BGP change. + session2.getBgpConfig().router_id() = "7.7.7.7"; + session2.setCommandLine("config protocol bgp global router-id 7.7.7.7"); + session2.saveBgpConfig(); + + // Committing fails (stale base); rebase resolves it without conflicts. + EXPECT_THROW(session2.commit(localhost()), std::runtime_error); + EXPECT_NO_THROW(session2.rebase()); + + // The staged BGP value survives the rebase... + std::string bgp; + ASSERT_TRUE( + folly::readFile((sessionDir2 / "bgp_config.json").string().c_str(), bgp)); + EXPECT_THAT(bgp, ::testing::HasSubstr("7.7.7.7")); + // ...and user1's agent change was folded into session2's agent config. + std::string agent; + ASSERT_TRUE( + folly::readFile((sessionDir2 / "agent.conf").string().c_str(), agent)); + EXPECT_THAT(agent, ::testing::HasSubstr("User1 change")); +} + +// Rolling back to an earlier commit must restore the BGP system config and +// restart bgpd (not just the agent config). +TEST_F(ConfigSessionTestFixture, rollbackBgpConfig) { + fs::path sessionDir = getTestHomeDir() / ".fboss2"; + fs::path bgpSys = getTestEtcDir() / "coop" / "bgpcpp" / "bgpcpp.conf"; + + auto makeSession = [&]() { + auto s = std::make_unique( + sessionDir.string(), (getTestEtcDir() / "coop").string()); + // BGP commits/rollback restart bgpd via systemd; mock it out. + s->setMockSystemdFactory([] { + return std::make_unique<::testing::NiceMock>(); + }); + return s; + }; + + std::string sha1; + { + auto s = makeSession(); + s->getBgpConfig().router_id() = "1.1.1.1"; + s->setCommandLine("config protocol bgp global router-id 1.1.1.1"); + s->saveBgpConfig(); + sha1 = s->commit(localhost()).commitSha; + ASSERT_FALSE(sha1.empty()); + } + { + auto s = makeSession(); + s->getBgpConfig().router_id() = "2.2.2.2"; + s->setCommandLine("config protocol bgp global router-id 2.2.2.2"); + s->saveBgpConfig(); + ASSERT_FALSE(s->commit(localhost()).commitSha.empty()); + } + + // System BGP config now reflects the latest commit. + std::string content; + ASSERT_TRUE(folly::readFile(bgpSys.string().c_str(), content)); + EXPECT_THAT(content, ::testing::HasSubstr("2.2.2.2")); + + // Roll back to the first commit -> BGP config restored to 1.1.1.1. + { + auto s = makeSession(); + std::string rb = s->rollback(localhost(), sha1); + EXPECT_FALSE(rb.empty()); + } + ASSERT_TRUE(folly::readFile(bgpSys.string().c_str(), content)); + EXPECT_THAT(content, ::testing::HasSubstr("1.1.1.1")); + EXPECT_THAT(content, ::testing::Not(::testing::HasSubstr("2.2.2.2"))); +} + +// Re-committing a BGP config that is byte-identical to the running +// /etc/coop/bgpcpp/bgpcpp.conf must be a no-op: no git commit and (crucially) +// no disruptive bgpd restart. saveBgpConfig() records BGP_RESTART +// unconditionally, so commit() compares staged vs running content. +TEST_F(ConfigSessionTestFixture, commitUnchangedBgpConfigIsNoOp) { + fs::path sessionDir = getTestHomeDir() / ".fboss2"; + auto makeSession = [&]() { + auto s = std::make_unique( + sessionDir.string(), (getTestEtcDir() / "coop").string()); + s->setMockSystemdFactory([] { + return std::make_unique<::testing::NiceMock>(); + }); + return s; + }; + + { + auto s = makeSession(); + s->getBgpConfig().router_id() = "1.1.1.1"; + s->setCommandLine("config protocol bgp global router-id 1.1.1.1"); + s->saveBgpConfig(); + ASSERT_FALSE(s->commit(localhost()).commitSha.empty()); + } + // Stage the SAME value again and commit -> no-op (empty commitSha, so no git + // revision and no bgpd restart). + { + auto s = makeSession(); + s->getBgpConfig().router_id() = "1.1.1.1"; + s->setCommandLine("config protocol bgp global router-id 1.1.1.1"); + s->saveBgpConfig(); + auto result = s->commit(localhost()); + EXPECT_TRUE(result.commitSha.empty()) + << "committing an unchanged BGP config should be a no-op (no restart)"; + EXPECT_EQ(result.actions.count(cli::ServiceType::BGP), 0u) + << "unchanged BGP config must not apply BGP_RESTART"; + } +} + +// commit() must abort if it cannot read the running bgpcpp.conf, instead of +// silently proceeding with an empty snapshot (which a later failed-commit +// rollback would write back, corrupting the running config). Mirrors the guard +// rollback() already has. +TEST_F(ConfigSessionTestFixture, commitThrowsWhenRunningBgpConfigUnreadable) { + fs::path sessionDir = getTestHomeDir() / ".fboss2"; + fs::path bgpSys = getTestEtcDir() / "coop" / "bgpcpp" / "bgpcpp.conf"; + auto makeSession = [&]() { + auto s = std::make_unique( + sessionDir.string(), (getTestEtcDir() / "coop").string()); + s->setMockSystemdFactory([] { + return std::make_unique<::testing::NiceMock>(); + }); + return s; + }; + + { + auto s = makeSession(); + s->getBgpConfig().router_id() = "1.1.1.1"; + s->setCommandLine("config protocol bgp global router-id 1.1.1.1"); + s->saveBgpConfig(); + ASSERT_FALSE(s->commit(localhost()).commitSha.empty()); + } + ASSERT_TRUE(fs::exists(bgpSys)); + + // Make the running config unreadable; running as a non-root user this makes + // folly::readFile fail. (Skip the assertion if we happen to run as root, + // where permission bits are bypassed.) + fs::permissions(bgpSys, fs::perms::none); + if (::geteuid() != 0) { + auto s = makeSession(); + s->getBgpConfig().router_id() = "2.2.2.2"; + s->setCommandLine("config protocol bgp global router-id 2.2.2.2"); + s->saveBgpConfig(); + EXPECT_THROW(s->commit(localhost()), std::runtime_error); + } + // Restore perms so fixture teardown can remove the temp tree. + fs::permissions(bgpSys, fs::perms::owner_all); +} + +// A BGP-only session (bgp_config.json + metadata present, agent.conf session +// file absent) must RESUME on the next CLI invocation, not be misdetected as +// fresh -- otherwise requiredActions_ (BGP_RESTART) is cleared and the +// staged change is silently dropped at commit time. +TEST_F(ConfigSessionTestFixture, bgpOnlySessionResumesAcrossInvocations) { + fs::path sessionDir = getTestHomeDir() / ".fboss2"; + fs::path agentSess = sessionDir / "agent.conf"; + fs::path bgpSess = sessionDir / "bgp_config.json"; + auto makeSession = [&]() { + auto s = std::make_unique( + sessionDir.string(), (getTestEtcDir() / "coop").string()); + s->setMockSystemdFactory([] { + return std::make_unique<::testing::NiceMock>(); + }); + return s; + }; + + { + auto s = makeSession(); + s->getBgpConfig().router_id() = "9.9.9.9"; + s->setCommandLine("config protocol bgp global router-id 9.9.9.9"); + s->saveBgpConfig(); + } + // Simulate a pure BGP-only session: drop the agent session file that the + // first construction seeded, leaving only the staged BGP config + metadata. + fs::remove(agentSess); + ASSERT_TRUE(fs::exists(bgpSess)); + ASSERT_FALSE(fs::exists(agentSess)); + + // The next invocation must resume and still commit the staged BGP change. + { + auto s = makeSession(); + auto result = s->commit(localhost()); + EXPECT_FALSE(result.commitSha.empty()) + << "BGP-only session must resume, not be wiped as a fresh session"; + } +} + +// No-arg rollback() walks cli/cli_metadata.json history so BGP-only commits are +// reachable. The initial baseline commit must therefore also touch metadata, or +// rolling back to the baseline (after exactly one real commit) fails. +TEST_F(ConfigSessionTestFixture, noArgRollbackReachesBaseline) { + fs::path sessionDir = getTestHomeDir() / ".fboss2"; + auto makeSession = [&]() { + auto s = std::make_unique( + sessionDir.string(), (getTestEtcDir() / "coop").string()); + s->setMockSystemdFactory([] { + return std::make_unique<::testing::NiceMock>(); + }); + return s; + }; + + { + auto s = makeSession(); + s->getBgpConfig().router_id() = "1.1.1.1"; + s->setCommandLine("config protocol bgp global router-id 1.1.1.1"); + s->saveBgpConfig(); + ASSERT_FALSE(s->commit(localhost()).commitSha.empty()); + } + // Exactly one real commit sits on top of the baseline; no-arg rollback must + // still reach the baseline (it appears in metadata history now). + { + auto s = makeSession(); + std::string rb; + EXPECT_NO_THROW(rb = s->rollback(localhost())); + EXPECT_FALSE(rb.empty()); + } +} + } // namespace facebook::fboss diff --git a/fboss/cli/fboss2/test/config/CmdConfigTestBase.cpp b/fboss/cli/fboss2/test/config/CmdConfigTestBase.cpp index 4d7a06adf9e9a..a96f50ad77163 100644 --- a/fboss/cli/fboss2/test/config/CmdConfigTestBase.cpp +++ b/fboss/cli/fboss2/test/config/CmdConfigTestBase.cpp @@ -2,7 +2,7 @@ #include "fboss/cli/fboss2/test/config/CmdConfigTestBase.h" -#include +#include #include #include @@ -52,9 +52,15 @@ CmdConfigTestBase::CmdConfigTestBase( // Create session config path sessionConfigPath_ = testHomeDir_ / ".fboss2" / "agent.conf"; - // Initialize Git repository and create initial commit + // Initialize Git repository and create initial commit. Mirror + // ConfigSession::initializeGit(): the baseline commit also touches + // cli/cli_metadata.json so it shows up in `git log -- cli/cli_metadata.json`, + // which no-arg rollback() walks (so the baseline is reachable after the very + // first real commit). + std::filesystem::path metadataPath = cliConfigDir_ / "cli_metadata.json"; + createTestConfig(metadataPath, "{}"); git_.init(); - git_.commit({"cli/agent.conf"}, "Initial commit"); + git_.commit({"cli/agent.conf", "cli/cli_metadata.json"}, "Initial commit"); } void CmdConfigTestBase::SetUp() { diff --git a/fboss/cli/fboss2/test/config/CmdDeleteBgpNeighborTest.cpp b/fboss/cli/fboss2/test/config/CmdDeleteBgpNeighborTest.cpp new file mode 100644 index 0000000000000..ad85ed75dfc17 --- /dev/null +++ b/fboss/cli/fboss2/test/config/CmdDeleteBgpNeighborTest.cpp @@ -0,0 +1,116 @@ +// (c) Meta Platforms, Inc. and affiliates. Confidential and proprietary. + +#include +#include +#include + +#include +#include +#include +#include + +#include "fboss/cli/fboss2/commands/config/protocol/bgp/neighbor/CmdConfigProtocolBgpNeighbor.h" +#include "fboss/cli/fboss2/commands/delete/protocol/bgp/neighbor/CmdDeleteProtocolBgpNeighbor.h" +#include "fboss/cli/fboss2/session/ConfigSession.h" +#include "fboss/cli/fboss2/test/config/CmdConfigTestBase.h" +#include "fboss/cli/fboss2/utils/HostInfo.h" + +using namespace ::testing; + +namespace facebook::fboss { + +// Deleting only touches the BGP side of ConfigSession, which seeds from +// thrift schema defaults when neither a staged session nor a system +// bgpcpp.conf exists — so no seed agent config is needed (mirrors +// CmdConfigBgpNeighborTest). +class CmdDeleteBgpNeighborTestFixture : public CmdConfigTestBase { + public: + CmdDeleteBgpNeighborTestFixture() + : CmdConfigTestBase("bgp_neighbor_delete_test_%%%%-%%%%-%%%%", "") {} + + void SetUp() override { + CmdConfigTestBase::SetUp(); + setupTestableConfigSession(); + } + + std::string configure(const std::vector& tokens) { + CmdConfigProtocolBgpNeighbor cmd; + HostInfo hostInfo("testhost"); + return cmd.queryClient(hostInfo, BgpNeighborConfig(tokens)); + } + + std::string del(const std::vector& tokens) { + CmdDeleteProtocolBgpNeighbor cmd; + HostInfo hostInfo("testhost"); + return cmd.queryClient(hostInfo, BgpNeighborRef(tokens)); + } + + const std::vector& peers() { + return *ConfigSession::getInstance().getBgpConfig().peers(); + } + + bool sessionFileExists() { + return std::filesystem::exists( + ConfigSession::getInstance().getBgpSessionConfigPath()); + } +}; + +// ============================================================================== +// BgpNeighborRef (arg) validation +// ============================================================================== + +TEST_F(CmdDeleteBgpNeighborTestFixture, argValidation) { + // Address normalization mirrors the config command. + EXPECT_EQ(BgpNeighborRef({"2001:DB8::1"}).peerAddr(), "2001:db8::1"); + EXPECT_EQ(BgpNeighborRef({"2001:db8::/64"}).peerAddr(), "2001:db8::/64"); + + // Invalid: empty, extra tokens, bad address. + EXPECT_THROW(BgpNeighborRef({}), std::invalid_argument); + EXPECT_THROW(BgpNeighborRef({"10.0.0.1", "10.0.0.2"}), std::invalid_argument); + EXPECT_THROW(BgpNeighborRef({"not-an-ip"}), std::invalid_argument); +} + +// ============================================================================== +// queryClient +// ============================================================================== + +TEST_F(CmdDeleteBgpNeighborTestFixture, deleteExistingNeighbor) { + configure({"10.0.0.1", "remote-asn", "65000"}); + configure({"10.0.0.2", "remote-asn", "65001"}); + ASSERT_EQ(peers().size(), 2); + + auto result = del({"10.0.0.1"}); + EXPECT_THAT(result, HasSubstr("Successfully deleted BGP neighbor 10.0.0.1")); + // The other neighbor survives untouched. + ASSERT_EQ(peers().size(), 1); + EXPECT_EQ(*peers()[0].peer_addr(), "10.0.0.2"); + EXPECT_EQ(peers()[0].remote_as_4_byte().value_or(0), 65001); + EXPECT_TRUE(sessionFileExists()); +} + +TEST_F(CmdDeleteBgpNeighborTestFixture, deleteByAnySpelling) { + configure({"2001:db8::1", "remote-asn", "65000"}); + ASSERT_EQ(peers().size(), 1); + // A different spelling of the same address deletes the same peer. + auto result = del({"2001:DB8:0:0::1"}); + EXPECT_THAT(result, HasSubstr("Successfully deleted")); + EXPECT_TRUE(peers().empty()); +} + +TEST_F(CmdDeleteBgpNeighborTestFixture, deleteUnknownNeighborRejected) { + auto result = del({"192.0.2.99"}); + EXPECT_THAT(result, HasSubstr("Error: BGP neighbor 192.0.2.99 not found")); + // Nothing was persisted for the failed delete. + EXPECT_FALSE(sessionFileExists()) + << "session file should not exist after rejected delete"; +} + +TEST_F(CmdDeleteBgpNeighborTestFixture, deleteUnknownLeavesOthersIntact) { + configure({"10.0.0.1", "remote-asn", "65000"}); + auto result = del({"10.0.0.99"}); + EXPECT_THAT(result, HasSubstr("not found")); + ASSERT_EQ(peers().size(), 1); + EXPECT_EQ(*peers()[0].peer_addr(), "10.0.0.1"); +} + +} // namespace facebook::fboss diff --git a/fboss/cli/fboss2/test/config/CmdDeleteBgpPeerGroupTest.cpp b/fboss/cli/fboss2/test/config/CmdDeleteBgpPeerGroupTest.cpp new file mode 100644 index 0000000000000..826e20c805127 --- /dev/null +++ b/fboss/cli/fboss2/test/config/CmdDeleteBgpPeerGroupTest.cpp @@ -0,0 +1,106 @@ +// (c) Meta Platforms, Inc. and affiliates. Confidential and proprietary. + +#include +#include +#include + +#include +#include +#include +#include + +#include "fboss/cli/fboss2/commands/config/protocol/bgp/peer-group/CmdConfigProtocolBgpPeerGroup.h" +#include "fboss/cli/fboss2/commands/delete/protocol/bgp/peer-group/CmdDeleteProtocolBgpPeerGroup.h" +#include "fboss/cli/fboss2/session/ConfigSession.h" +#include "fboss/cli/fboss2/test/config/CmdConfigTestBase.h" +#include "fboss/cli/fboss2/utils/HostInfo.h" + +using namespace ::testing; + +namespace facebook::fboss { + +// Deleting only touches the BGP side of ConfigSession, which seeds from +// thrift schema defaults when neither a staged session nor a system +// bgpcpp.conf exists — so no seed agent config is needed (mirrors +// CmdConfigBgpPeerGroupTest). +class CmdDeleteBgpPeerGroupTestFixture : public CmdConfigTestBase { + public: + CmdDeleteBgpPeerGroupTestFixture() + : CmdConfigTestBase("bgp_peer_group_delete_test_%%%%-%%%%-%%%%", "") {} + + void SetUp() override { + CmdConfigTestBase::SetUp(); + setupTestableConfigSession(); + } + + std::string configure(const std::vector& tokens) { + CmdConfigProtocolBgpPeerGroup cmd; + HostInfo hostInfo("testhost"); + return cmd.queryClient(hostInfo, BgpPeerGroupConfig(tokens)); + } + + std::string del(const std::vector& tokens) { + CmdDeleteProtocolBgpPeerGroup cmd; + HostInfo hostInfo("testhost"); + return cmd.queryClient(hostInfo, BgpPeerGroupRef(tokens)); + } + + const std::vector& groups() { + return ConfigSession::getInstance().getBgpConfig().peer_groups().ensure(); + } + + bool sessionFileExists() { + return std::filesystem::exists( + ConfigSession::getInstance().getBgpSessionConfigPath()); + } +}; + +// ============================================================================== +// BgpPeerGroupRef (arg) validation +// ============================================================================== + +TEST_F(CmdDeleteBgpPeerGroupTestFixture, argValidation) { + EXPECT_EQ(BgpPeerGroupRef({"SPINE"}).groupName(), "SPINE"); + + // Invalid: empty, empty name, extra tokens. + EXPECT_THROW(BgpPeerGroupRef({}), std::invalid_argument); + EXPECT_THROW(BgpPeerGroupRef({""}), std::invalid_argument); + EXPECT_THROW(BgpPeerGroupRef({"SPINE", "LEAF"}), std::invalid_argument); +} + +// ============================================================================== +// queryClient +// ============================================================================== + +TEST_F(CmdDeleteBgpPeerGroupTestFixture, deleteExistingGroup) { + configure({"SPINE", "remote-asn", "65000"}); + configure({"LEAF", "remote-asn", "65001"}); + ASSERT_EQ(groups().size(), 2); + + auto result = del({"SPINE"}); + EXPECT_THAT(result, HasSubstr("Successfully deleted BGP peer-group SPINE")); + // The other group survives untouched. + ASSERT_EQ(groups().size(), 1); + EXPECT_EQ(*groups()[0].name(), "LEAF"); + EXPECT_EQ(groups()[0].remote_as_4_byte().value_or(0), 65001); + EXPECT_TRUE(sessionFileExists()); +} + +TEST_F(CmdDeleteBgpPeerGroupTestFixture, deleteUnknownGroupRejected) { + auto result = del({"NO-SUCH-GROUP"}); + EXPECT_THAT( + result, HasSubstr("Error: BGP peer-group NO-SUCH-GROUP not found")); + // Nothing was persisted for the failed delete. + EXPECT_FALSE(sessionFileExists()) + << "session file should not exist after rejected delete"; +} + +TEST_F(CmdDeleteBgpPeerGroupTestFixture, deleteUnknownLeavesOthersIntact) { + configure({"SPINE", "remote-asn", "65000"}); + auto result = del({"LEAF"}); + EXPECT_THAT(result, HasSubstr("not found")); + ASSERT_EQ(groups().size(), 1); + EXPECT_EQ(*groups()[0].name(), "SPINE"); +} + +} // namespace facebook::fboss diff --git a/fboss/cli/fboss2/test/config/CmdDeleteBgpPolicyAsPathListTest.cpp b/fboss/cli/fboss2/test/config/CmdDeleteBgpPolicyAsPathListTest.cpp new file mode 100644 index 0000000000000..953c8d1a007fb --- /dev/null +++ b/fboss/cli/fboss2/test/config/CmdDeleteBgpPolicyAsPathListTest.cpp @@ -0,0 +1,113 @@ +// (c) Meta Platforms, Inc. and affiliates. Confidential and proprietary. + +#include +#include +#include + +#include +#include +#include +#include + +#include "configerator/structs/neteng/bgp_policy/thrift/gen-cpp2/bgp_policy_types.h" +#include "fboss/cli/fboss2/commands/config/protocol/bgp/policy/as-path-list/CmdConfigProtocolBgpPolicyAsPathList.h" +#include "fboss/cli/fboss2/commands/delete/protocol/bgp/policy/as-path-list/CmdDeleteProtocolBgpPolicyAsPathList.h" +#include "fboss/cli/fboss2/session/ConfigSession.h" +#include "fboss/cli/fboss2/test/config/CmdConfigTestBase.h" +#include "fboss/cli/fboss2/utils/HostInfo.h" + +using namespace ::testing; + +namespace facebook::fboss { + +// Deleting only touches the BGP side of ConfigSession, which seeds from thrift +// schema defaults when neither a staged session nor a system bgpcpp.conf +// exists — so no seed agent config is needed (mirrors +// CmdConfigBgpPeerGroupTest). +class CmdDeleteBgpPolicyAsPathListTestFixture : public CmdConfigTestBase { + public: + CmdDeleteBgpPolicyAsPathListTestFixture() + : CmdConfigTestBase("bgp_aspath_list_delete_test_%%%%-%%%%-%%%%", "") {} + + void SetUp() override { + CmdConfigTestBase::SetUp(); + setupTestableConfigSession(); + } + + std::string configure(const std::vector& tokens) { + CmdConfigProtocolBgpPolicyAsPathList cmd; + HostInfo hostInfo("testhost"); + return cmd.queryClient(hostInfo, BgpAsPathListConfig(tokens)); + } + + std::string del(const std::vector& tokens) { + CmdDeleteProtocolBgpPolicyAsPathList cmd; + HostInfo hostInfo("testhost"); + return cmd.queryClient(hostInfo, BgpAsPathListRef(tokens)); + } + + const std::vector& lists() { + return *ConfigSession::getInstance() + .getBgpConfig() + .policies() + .ensure() + .aspath_lists(); + } + + bool sessionFileExists() { + return std::filesystem::exists( + ConfigSession::getInstance().getBgpSessionConfigPath()); + } +}; + +// ============================================================================== +// BgpAsPathListRef (arg) validation +// ============================================================================== + +TEST_F(CmdDeleteBgpPolicyAsPathListTestFixture, argValidation) { + EXPECT_EQ(BgpAsPathListRef({"AS100"}).listName(), "AS100"); + + // Invalid: empty, empty name, extra tokens. + EXPECT_THROW(BgpAsPathListRef({}), std::invalid_argument); + EXPECT_THROW(BgpAsPathListRef({""}), std::invalid_argument); + EXPECT_THROW(BgpAsPathListRef({"AS100", "AS200"}), std::invalid_argument); +} + +// ============================================================================== +// queryClient +// ============================================================================== + +TEST_F(CmdDeleteBgpPolicyAsPathListTestFixture, deleteExistingList) { + configure({"AS100", "entry", "10", "asn-regexp", "^65000_"}); + configure({"AS200", "description", "keep"}); + ASSERT_EQ(lists().size(), 2); + + auto result = del({"AS100"}); + EXPECT_THAT(result, HasSubstr("Successfully deleted BGP as-path-list AS100")); + // The other list survives untouched. + ASSERT_EQ(lists().size(), 1); + EXPECT_EQ(*lists()[0].name(), "AS200"); + EXPECT_EQ(*lists()[0].description(), "keep"); + EXPECT_TRUE(sessionFileExists()); +} + +TEST_F(CmdDeleteBgpPolicyAsPathListTestFixture, deleteUnknownListRejected) { + auto result = del({"NO-SUCH-LIST"}); + EXPECT_THAT( + result, HasSubstr("Error: BGP as-path-list NO-SUCH-LIST not found")); + // Nothing was persisted for the failed delete. + EXPECT_FALSE(sessionFileExists()) + << "session file should not exist after rejected delete"; +} + +TEST_F( + CmdDeleteBgpPolicyAsPathListTestFixture, + deleteUnknownLeavesOthersIntact) { + configure({"AS100", "description", "one"}); + auto result = del({"AS200"}); + EXPECT_THAT(result, HasSubstr("not found")); + ASSERT_EQ(lists().size(), 1); + EXPECT_EQ(*lists()[0].name(), "AS100"); +} + +} // namespace facebook::fboss diff --git a/fboss/cli/fboss2/test/integration_test/BUCK b/fboss/cli/fboss2/test/integration_test/BUCK index 9b22dc88d2edb..e75a8402e7eb6 100644 --- a/fboss/cli/fboss2/test/integration_test/BUCK +++ b/fboss/cli/fboss2/test/integration_test/BUCK @@ -13,11 +13,17 @@ oncall("fboss_oss") # # Unlike the Python-based CLI tests, these C++ tests directly invoke the CLI # library code rather than spawning a subprocess. +# bazelify: extra_dep = //neteng/fboss/bgp/if:bgp_thrift cpp_binary( name = "fboss2_integration_test", srcs = [ "ConfigAdminDistanceTest.cpp", "ConfigArpTest.cpp", + "ConfigBgpGlobalTest.cpp", + "ConfigBgpNeighborTest.cpp", + "ConfigBgpPeerGroupTest.cpp", + "ConfigBgpPolicyAsPathListTest.cpp", + "ConfigBgpSessionTest.cpp", "ConfigConcurrentSessionsTest.cpp", "ConfigCoppTest.cpp", "ConfigDhcpSourceOverrideTest.cpp", diff --git a/fboss/cli/fboss2/test/integration_test/ConfigBgpGlobalTest.cpp b/fboss/cli/fboss2/test/integration_test/ConfigBgpGlobalTest.cpp new file mode 100644 index 0000000000000..a4afe16a8f35c --- /dev/null +++ b/fboss/cli/fboss2/test/integration_test/ConfigBgpGlobalTest.cpp @@ -0,0 +1,117 @@ +// (c) Meta Platforms, Inc. and affiliates. Confidential and proprietary. + +/** + * End-to-end tests for `fboss2-dev config protocol bgp global `. + * + * Scope: the BGP *global* tunables only. Each positive test stages the change + * AND commits it, then asserts the value landed at the correct thrift field + * path in bgpd's own running config — fetched over its getRunningConfig RPC + * (see ConfigBgpTestBase::setAndCommit) — which proves the daemon parsed and + * adopted the promoted /etc/coop/bgpcpp/bgpcpp.conf after the commit-triggered + * restart, not merely that the CLI wrote the file. Session-lifecycle behavior + * (clear / diff / rollback / commit-restart mechanics) lives in + * ConfigBgpSessionTest. + * + * - count-confeds-in-as-path-len + * -> BgpConfig.count_confeds_in_as_path_len + * - graceful-restart-time + * -> BgpConfig.graceful_restart_convergence_seconds + * - rib-allocated-path-ids + * -> BgpConfig.bgp_setting_config.enable_rib_allocated_path_id + * + * Requirements: + * - The fboss2-dev binary under test (config subcommand tree). + * - HOME is set (the session file lives under $HOME/.fboss2). + * - bgpd is installed/active (commit restarts it). + */ + +#include +#include +#include + +#include "fboss/cli/fboss2/test/integration_test/ConfigBgpTestBase.h" + +using namespace facebook::fboss; +using ::testing::HasSubstr; + +class ConfigBgpGlobalTest : public ConfigBgpTestBase {}; + +TEST_F(ConfigBgpGlobalTest, SetCountConfedsInAsPathLenTrue) { + auto config = setAndCommit("count-confeds-in-as-path-len", "true"); + ASSERT_TRUE(config.count("count_confeds_in_as_path_len")); + EXPECT_TRUE(config["count_confeds_in_as_path_len"].asBool()); +} + +TEST_F(ConfigBgpGlobalTest, SetCountConfedsInAsPathLenFalse) { + auto config = setAndCommit("count-confeds-in-as-path-len", "false"); + ASSERT_TRUE(config.count("count_confeds_in_as_path_len")); + EXPECT_FALSE(config["count_confeds_in_as_path_len"].asBool()); +} + +TEST_F(ConfigBgpGlobalTest, SetGracefulRestartTime) { + // Stage a value that differs from the current system config: committing an + // unchanged config is a no-op that yields no SHA, and the shipped + // bgpcpp.conf already sets 120. + int64_t target = 120; + try { + auto current = readSystemBgpConfig(); + if (current.count("graceful_restart_convergence_seconds") && + current["graceful_restart_convergence_seconds"].asInt() == target) { + target = 121; + } + } catch (const std::exception&) { + // No readable system config yet — any value is a change. + } + auto config = setAndCommit("graceful-restart-time", std::to_string(target)); + ASSERT_TRUE(config.count("graceful_restart_convergence_seconds")); + EXPECT_EQ(config["graceful_restart_convergence_seconds"].asInt(), target); + // Must not write the per-peer timer field name. + EXPECT_FALSE(config.count("graceful_restart_seconds")); +} + +TEST_F(ConfigBgpGlobalTest, SetRibAllocatedPathIdsTrue) { + auto config = setAndCommit("rib-allocated-path-ids", "true"); + ASSERT_TRUE(config.count("bgp_setting_config")); + ASSERT_TRUE( + config["bgp_setting_config"].count("enable_rib_allocated_path_id")); + EXPECT_TRUE( + config["bgp_setting_config"]["enable_rib_allocated_path_id"].asBool()); + // Must be nested, not at the top level. + EXPECT_FALSE(config.count("enable_rib_allocated_path_id")); +} + +TEST_F(ConfigBgpGlobalTest, SetRibAllocatedPathIdsFalse) { + auto config = setAndCommit("rib-allocated-path-ids", "false"); + ASSERT_TRUE(config.count("bgp_setting_config")); + ASSERT_TRUE( + config["bgp_setting_config"].count("enable_rib_allocated_path_id")); + EXPECT_FALSE( + config["bgp_setting_config"]["enable_rib_allocated_path_id"].asBool()); +} + +TEST_F(ConfigBgpGlobalTest, InvalidBoolValueRejected) { + clearBgpSession(); + auto result = runCli( + {"config", + "protocol", + "bgp", + "global", + "count-confeds-in-as-path-len", + "maybe"}); + // The handler reports the error on stdout; ensure it did not silently + // succeed and persist a bogus value. + EXPECT_THAT(result.stdout, HasSubstr("Invalid value")); + // The handler must bail out before saving, so no session file is written for + // the rejected input. + EXPECT_FALSE(std::filesystem::exists(bgpSessionPath())) + << "session file should not exist after rejected input"; +} + +TEST_F(ConfigBgpGlobalTest, NegativeGracefulRestartTimeRejected) { + clearBgpSession(); + auto result = runCli( + {"config", "protocol", "bgp", "global", "graceful-restart-time", "-1"}); + EXPECT_THAT(result.stdout, HasSubstr("non-negative")); + EXPECT_FALSE(std::filesystem::exists(bgpSessionPath())) + << "session file should not exist after rejected input"; +} diff --git a/fboss/cli/fboss2/test/integration_test/ConfigBgpNeighborTest.cpp b/fboss/cli/fboss2/test/integration_test/ConfigBgpNeighborTest.cpp new file mode 100644 index 0000000000000..069467ba60f02 --- /dev/null +++ b/fboss/cli/fboss2/test/integration_test/ConfigBgpNeighborTest.cpp @@ -0,0 +1,238 @@ +// (c) Meta Platforms, Inc. and affiliates. Confidential and proprietary. + +/** + * End-to-end tests for `fboss2-dev config protocol bgp neighbor + * [ ...]`. + * + * Scope: the per-neighbor attributes. The commit-path tests stage the change + * AND commit it, then assert the value landed at the correct thrift field + * path inside the matching .peers[] entry of the promoted system config + * (/etc/coop/bgpcpp/bgpcpp.conf). The remaining attributes are verified in + * the staged session JSON (~/.fboss2/bgp_config.json) to keep the suite's + * daemon-restart count down; the promote mechanics are identical for every + * attribute and are covered by the commit tests + ConfigBgpSessionTest. + * + * Requirements: + * - The fboss2-dev binary under test (config subcommand tree). + * - HOME is set (the session file lives under $HOME/.fboss2). + * - bgpd is installed/active (commit restarts it). + */ + +#include +#include +#include +#include + +#include "fboss/cli/fboss2/test/integration_test/ConfigBgpTestBase.h" +#include "folly/json/dynamic.h" +#include "gmock/gmock.h" + +using namespace facebook::fboss; +using ::testing::HasSubstr; +using ::testing::Not; + +namespace { +// Test-only neighbor addresses, from the documentation range (RFC 3849) so +// they never collide with a real peer in the device's running BGP config. +const std::string kNeighbor = "2001:db8::1234"; +const std::string kNeighborV4 = "192.0.2.101"; +} // namespace + +class ConfigBgpNeighborTest : public ConfigBgpTestBase { + protected: + // Run `config protocol bgp neighbor ` WITHOUT clearing the + // staged session, so attributes can accumulate across invocations. + auto runNeighbor(const std::vector& tokens) { + std::vector args = {"config", "protocol", "bgp", "neighbor"}; + args.insert(args.end(), tokens.begin(), tokens.end()); + auto result = runCli(args); + EXPECT_EQ(result.exitCode, 0) + << "stdout=" << result.stdout << " stderr=" << result.stderr; + return result; + } + + // Stage a neighbor command expected to succeed and return the staged + // session JSON. + folly::dynamic stageNeighbor(const std::vector& tokens) { + auto result = runNeighbor(tokens); + EXPECT_THAT(result.stdout, Not(HasSubstr("Error:"))); + return readBgpSessionConfig(); + } + + // The .peers[] entry for addr, or nullptr. + static const folly::dynamic* findPeer( + const folly::dynamic& config, + const std::string& addr) { + if (config.count("peers") == 0) { + return nullptr; + } + for (const auto& peer : config["peers"]) { + if (peer.count("peer_addr") && peer["peer_addr"].asString() == addr) { + return &peer; + } + } + return nullptr; + } +}; + +TEST_F(ConfigBgpNeighborTest, SetRemoteAsnAndCommit) { + discardSession(); + clearBgpSession(); + stageNeighbor({kNeighbor, "remote-asn", "65000"}); + commitAndGetSha(); + ASSERT_TRUE(waitForBgpDaemonActive()) + << "bgpd did not return active after commit; state=" + << bgpDaemonActiveState(); + // Two layers: the promoted file proves what the commit wrote; the daemon's + // getRunningConfig RPC proves bgpd parsed and adopted it after the restart. + auto config = readSystemBgpConfig(); + const auto* peer = findPeer(config, kNeighbor); + ASSERT_NE(peer, nullptr) << "committed config has no peer " << kNeighbor; + ASSERT_TRUE(peer->count("remote_as_4_byte")); + EXPECT_EQ((*peer)["remote_as_4_byte"].asInt(), 65000); + + auto running = readRunningBgpConfigViaRpc(); + const auto* runningPeer = findPeer(running, kNeighbor); + ASSERT_NE(runningPeer, nullptr) + << "bgpd's running config has no peer " << kNeighbor; + EXPECT_EQ((*runningPeer)["remote_as_4_byte"].asInt(), 65000); +} + +TEST_F(ConfigBgpNeighborTest, SetTimersHoldTimeAndCommit) { + discardSession(); + clearBgpSession(); + stageNeighbor({kNeighbor, "timers", "hold-time", "90"}); + commitAndGetSha(); + ASSERT_TRUE(waitForBgpDaemonActive()) + << "bgpd did not return active after commit; state=" + << bgpDaemonActiveState(); + // Verify nested timer fields through the daemon's own view of its config. + auto running = readRunningBgpConfigViaRpc(); + const auto* peer = findPeer(running, kNeighbor); + ASSERT_NE(peer, nullptr) << "bgpd's running config has no peer " << kNeighbor; + ASSERT_TRUE(peer->count("bgp_peer_timers")); + EXPECT_EQ((*peer)["bgp_peer_timers"]["hold_time_seconds"].asInt(), 90); +} + +TEST_F(ConfigBgpNeighborTest, BareCreateStagesPeer) { + clearBgpSession(); + auto config = stageNeighbor({kNeighborV4}); + const auto* peer = findPeer(config, kNeighborV4); + ASSERT_NE(peer, nullptr) << "bare `neighbor ` did not create the peer"; +} + +TEST_F(ConfigBgpNeighborTest, AttributesAccumulateOnOnePeer) { + clearBgpSession(); + stageNeighbor({kNeighbor, "description", "test", "spine", "peer"}); + stageNeighbor({kNeighbor, "connect-mode", "PASSIVE"}); + stageNeighbor({kNeighbor, "afi", "disable-ipv4-afi", "true"}); + stageNeighbor({kNeighbor, "graceful-restart", "stateful-ha", "true"}); + stageNeighbor({kNeighbor, "max-route", "pre-filter", "45000"}); + stageNeighbor({kNeighbor, "peer-group", "TEST-GROUP"}); + auto config = stageNeighbor({kNeighbor, "local-asn", "64512"}); + + const auto* peer = findPeer(config, kNeighbor); + ASSERT_NE(peer, nullptr); + EXPECT_EQ((*peer)["description"].asString(), "test spine peer"); + EXPECT_TRUE((*peer)["is_passive"].asBool()); + EXPECT_TRUE((*peer)["disable_ipv4_afi"].asBool()); + EXPECT_TRUE((*peer)["enable_stateful_ha"].asBool()); + EXPECT_EQ((*peer)["pre_filter"]["max_routes"].asInt(), 45000); + EXPECT_EQ((*peer)["peer_group_name"].asString(), "TEST-GROUP"); + EXPECT_EQ((*peer)["local_as_4_byte"].asInt(), 64512); + // Exactly one entry for this neighbor despite seven invocations. + int count = 0; + for (const auto& p : config["peers"]) { + count += (p["peer_addr"].asString() == kNeighbor); + } + EXPECT_EQ(count, 1); +} + +TEST_F(ConfigBgpNeighborTest, AddPathMergesDirections) { + clearBgpSession(); + // AddPath enum: RECEIVE=1, SEND=2, BOTH=3. + auto config = stageNeighbor({kNeighbor, "add-path", "receive", "true"}); + const auto* peer = findPeer(config, kNeighbor); + ASSERT_NE(peer, nullptr); + EXPECT_EQ((*peer)["add_path"].asInt(), 1); + + config = stageNeighbor({kNeighbor, "add-path", "send", "true"}); + peer = findPeer(config, kNeighbor); + EXPECT_EQ((*peer)["add_path"].asInt(), 3); +} + +TEST_F(ConfigBgpNeighborTest, InvalidBoolValueRejected) { + clearBgpSession(); + auto result = runNeighbor({kNeighbor, "rr-client", "maybe"}); + EXPECT_THAT(result.stdout, HasSubstr("Invalid")); + EXPECT_FALSE(std::filesystem::exists(bgpSessionPath())) + << "session file should not exist after rejected input"; +} + +TEST_F(ConfigBgpNeighborTest, UnsupportedAttributeRejected) { + clearBgpSession(); + // peer-port has no per-neighbor thrift field; the CLI must refuse it + // instead of persisting dead config. + auto result = runNeighbor({kNeighbor, "peer-port", "1179"}); + EXPECT_THAT(result.stdout, HasSubstr("not supported")); + EXPECT_FALSE(std::filesystem::exists(bgpSessionPath())) + << "session file should not exist after rejected input"; +} + +TEST_F(ConfigBgpNeighborTest, ConnectModeBothRejected) { + clearBgpSession(); + auto result = runNeighbor({kNeighbor, "connect-mode", "BOTH"}); + EXPECT_THAT(result.stdout, HasSubstr("Error")); + EXPECT_FALSE(std::filesystem::exists(bgpSessionPath())) + << "session file should not exist after rejected input"; +} + +TEST_F(ConfigBgpNeighborTest, DeleteStagedNeighbor) { + clearBgpSession(); + stageNeighbor({kNeighbor, "remote-asn", "65000"}); + stageNeighbor({kNeighborV4, "remote-asn", "65001"}); + + auto result = runCli({"delete", "protocol", "bgp", "neighbor", kNeighbor}); + EXPECT_EQ(result.exitCode, 0) + << "stdout=" << result.stdout << " stderr=" << result.stderr; + EXPECT_THAT(result.stdout, HasSubstr("Successfully deleted")); + + auto config = readBgpSessionConfig(); + EXPECT_EQ(findPeer(config, kNeighbor), nullptr) + << "deleted neighbor still present in the staged session"; + EXPECT_NE(findPeer(config, kNeighborV4), nullptr) + << "unrelated neighbor was removed"; +} + +TEST_F(ConfigBgpNeighborTest, DeleteUnknownNeighborRejected) { + clearBgpSession(); + auto result = + runCli({"delete", "protocol", "bgp", "neighbor", "192.0.2.222"}); + EXPECT_THAT(result.stdout, HasSubstr("not found")); + EXPECT_FALSE(std::filesystem::exists(bgpSessionPath())) + << "session file should not exist after rejected delete"; +} + +TEST_F(ConfigBgpNeighborTest, DeleteNeighborAndCommit) { + // Land a neighbor in the system config, then delete it through a second + // commit and verify it is gone from the promoted config. + discardSession(); + clearBgpSession(); + stageNeighbor({kNeighbor, "remote-asn", "65000"}); + commitAndGetSha(); + ASSERT_TRUE(waitForBgpDaemonActive()) + << "bgpd did not return active after commit; state=" + << bgpDaemonActiveState(); + ASSERT_NE(findPeer(readRunningBgpConfigViaRpc(), kNeighbor), nullptr) + << "setup commit did not land the neighbor in bgpd's running config"; + + clearBgpSession(); + auto result = runCli({"delete", "protocol", "bgp", "neighbor", kNeighbor}); + EXPECT_THAT(result.stdout, HasSubstr("Successfully deleted")); + commitAndGetSha(); + ASSERT_TRUE(waitForBgpDaemonActive()) + << "bgpd did not return active after delete commit; state=" + << bgpDaemonActiveState(); + EXPECT_EQ(findPeer(readRunningBgpConfigViaRpc(), kNeighbor), nullptr) + << "deleted neighbor still present in bgpd's running config"; +} diff --git a/fboss/cli/fboss2/test/integration_test/ConfigBgpPeerGroupTest.cpp b/fboss/cli/fboss2/test/integration_test/ConfigBgpPeerGroupTest.cpp new file mode 100644 index 0000000000000..631c056430725 --- /dev/null +++ b/fboss/cli/fboss2/test/integration_test/ConfigBgpPeerGroupTest.cpp @@ -0,0 +1,237 @@ +// (c) Meta Platforms, Inc. and affiliates. Confidential and proprietary. + +/** + * End-to-end tests for `fboss2-dev config protocol bgp peer-group + * [ ...]`. + * + * Scope: the per-peer-group attributes. The commit-path tests stage the change + * AND commit it, then assert the value landed at the correct thrift field + * path inside the matching .peer_groups[] entry of bgpd's running config + * (via getRunningConfig RPC). The remaining attributes are verified in the + * staged session JSON (~/.fboss2/bgp_config.json) to keep the suite's + * daemon-restart count down; the promote mechanics are identical for every + * attribute and are covered by the commit tests + ConfigBgpSessionTest. + * + * Requirements: + * - The fboss2-dev binary under test (config subcommand tree). + * - HOME is set (the session file lives under $HOME/.fboss2). + * - bgpd is installed/active (commit restarts it). + */ + +#include +#include +#include +#include + +#include "fboss/cli/fboss2/test/integration_test/ConfigBgpTestBase.h" +#include "folly/json/dynamic.h" +#include "gmock/gmock.h" + +using namespace facebook::fboss; +using ::testing::HasSubstr; +using ::testing::Not; + +namespace { +// Test-only peer-group names, unlikely to collide with a real group in the +// device's running BGP config. +const std::string kGroup = "FBOSS2-TEST-GROUP"; +const std::string kGroup2 = "FBOSS2-TEST-GROUP-2"; +} // namespace + +class ConfigBgpPeerGroupTest : public ConfigBgpTestBase { + protected: + // Run `config protocol bgp peer-group ` WITHOUT clearing the + // staged session, so attributes can accumulate across invocations. + auto runPeerGroup(const std::vector& tokens) { + std::vector args = {"config", "protocol", "bgp", "peer-group"}; + args.insert(args.end(), tokens.begin(), tokens.end()); + auto result = runCli(args); + EXPECT_EQ(result.exitCode, 0) + << "stdout=" << result.stdout << " stderr=" << result.stderr; + return result; + } + + // Stage a peer-group command expected to succeed and return the staged + // session JSON. + folly::dynamic stagePeerGroup(const std::vector& tokens) { + auto result = runPeerGroup(tokens); + EXPECT_THAT(result.stdout, Not(HasSubstr("Error:"))); + return readBgpSessionConfig(); + } + + // The .peer_groups[] entry named `name`, or nullptr. + static const folly::dynamic* findGroup( + const folly::dynamic& config, + const std::string& name) { + if (config.count("peer_groups") == 0) { + return nullptr; + } + for (const auto& group : config["peer_groups"]) { + if (group.count("name") && group["name"].asString() == name) { + return &group; + } + } + return nullptr; + } +}; + +TEST_F(ConfigBgpPeerGroupTest, SetRemoteAsnAndCommit) { + discardSession(); + clearBgpSession(); + stagePeerGroup({kGroup, "remote-asn", "65000"}); + commitAndGetSha(); + ASSERT_TRUE(waitForBgpDaemonActive()) + << "bgpd did not return active after commit; state=" + << bgpDaemonActiveState(); + // Two layers: the promoted file proves what the commit wrote; the daemon's + // getRunningConfig RPC proves bgpd parsed and adopted it after the restart. + auto config = readSystemBgpConfig(); + const auto* group = findGroup(config, kGroup); + ASSERT_NE(group, nullptr) << "committed config has no peer-group " << kGroup; + ASSERT_TRUE(group->count("remote_as_4_byte")); + EXPECT_EQ((*group)["remote_as_4_byte"].asInt(), 65000); + + auto running = readRunningBgpConfigViaRpc(); + const auto* runningGroup = findGroup(running, kGroup); + ASSERT_NE(runningGroup, nullptr) + << "bgpd's running config has no peer-group " << kGroup; + EXPECT_EQ((*runningGroup)["remote_as_4_byte"].asInt(), 65000); +} + +TEST_F(ConfigBgpPeerGroupTest, SetTimersHoldTimeAndCommit) { + discardSession(); + clearBgpSession(); + stagePeerGroup({kGroup, "timers", "hold-time", "90"}); + commitAndGetSha(); + ASSERT_TRUE(waitForBgpDaemonActive()) + << "bgpd did not return active after commit; state=" + << bgpDaemonActiveState(); + // Verify nested timer fields through the daemon's own view of its config. + auto running = readRunningBgpConfigViaRpc(); + const auto* group = findGroup(running, kGroup); + ASSERT_NE(group, nullptr) + << "bgpd's running config has no peer-group " << kGroup; + ASSERT_TRUE(group->count("bgp_peer_timers")); + EXPECT_EQ((*group)["bgp_peer_timers"]["hold_time_seconds"].asInt(), 90); +} + +TEST_F(ConfigBgpPeerGroupTest, BareCreateStagesGroup) { + clearBgpSession(); + auto config = stagePeerGroup({kGroup}); + const auto* group = findGroup(config, kGroup); + ASSERT_NE(group, nullptr) << "bare `peer-group ` did not create it"; +} + +TEST_F(ConfigBgpPeerGroupTest, AttributesAccumulateOnOneGroup) { + clearBgpSession(); + stagePeerGroup({kGroup, "description", "test", "spine", "group"}); + stagePeerGroup({kGroup, "connect-mode", "PASSIVE"}); + stagePeerGroup({kGroup, "afi", "disable-ipv4-afi", "true"}); + stagePeerGroup({kGroup, "graceful-restart", "stateful-ha", "true"}); + stagePeerGroup({kGroup, "max-route", "pre-filter", "45000"}); + auto config = stagePeerGroup({kGroup, "local-asn", "64512"}); + + const auto* group = findGroup(config, kGroup); + ASSERT_NE(group, nullptr); + EXPECT_EQ((*group)["description"].asString(), "test spine group"); + EXPECT_TRUE((*group)["is_passive"].asBool()); + EXPECT_TRUE((*group)["disable_ipv4_afi"].asBool()); + EXPECT_TRUE((*group)["enable_stateful_ha"].asBool()); + EXPECT_EQ((*group)["pre_filter"]["max_routes"].asInt(), 45000); + EXPECT_EQ((*group)["local_as_4_byte"].asInt(), 64512); + // Exactly one entry for this group despite six invocations. + int count = 0; + for (const auto& g : config["peer_groups"]) { + count += (g["name"].asString() == kGroup); + } + EXPECT_EQ(count, 1); +} + +TEST_F(ConfigBgpPeerGroupTest, AddPathMergesDirections) { + clearBgpSession(); + // AddPath enum: RECEIVE=1, SEND=2, BOTH=3. + auto config = stagePeerGroup({kGroup, "add-path", "receive", "true"}); + const auto* group = findGroup(config, kGroup); + ASSERT_NE(group, nullptr); + EXPECT_EQ((*group)["add_path"].asInt(), 1); + + config = stagePeerGroup({kGroup, "add-path", "send", "true"}); + group = findGroup(config, kGroup); + EXPECT_EQ((*group)["add_path"].asInt(), 3); +} + +TEST_F(ConfigBgpPeerGroupTest, InvalidBoolValueRejected) { + clearBgpSession(); + auto result = runPeerGroup({kGroup, "rr-client", "maybe"}); + EXPECT_THAT(result.stdout, HasSubstr("Invalid")); + EXPECT_FALSE(std::filesystem::exists(bgpSessionPath())) + << "session file should not exist after rejected input"; +} + +TEST_F(ConfigBgpPeerGroupTest, UnsupportedAttributeRejected) { + clearBgpSession(); + // peer-port has no per-peer-group thrift field; the CLI must refuse it + // instead of persisting dead config. + auto result = runPeerGroup({kGroup, "peer-port", "1179"}); + EXPECT_THAT(result.stdout, HasSubstr("unknown")); + EXPECT_FALSE(std::filesystem::exists(bgpSessionPath())) + << "session file should not exist after rejected input"; +} + +TEST_F(ConfigBgpPeerGroupTest, ConnectModeBothRejected) { + clearBgpSession(); + auto result = runPeerGroup({kGroup, "connect-mode", "BOTH"}); + EXPECT_THAT(result.stdout, HasSubstr("Error")); + EXPECT_FALSE(std::filesystem::exists(bgpSessionPath())) + << "session file should not exist after rejected input"; +} + +TEST_F(ConfigBgpPeerGroupTest, DeleteStagedGroup) { + clearBgpSession(); + stagePeerGroup({kGroup, "remote-asn", "65000"}); + stagePeerGroup({kGroup2, "remote-asn", "65001"}); + + auto result = runCli({"delete", "protocol", "bgp", "peer-group", kGroup}); + EXPECT_EQ(result.exitCode, 0) + << "stdout=" << result.stdout << " stderr=" << result.stderr; + EXPECT_THAT(result.stdout, HasSubstr("Successfully deleted")); + + auto config = readBgpSessionConfig(); + EXPECT_EQ(findGroup(config, kGroup), nullptr) + << "deleted peer-group still present in the staged session"; + EXPECT_NE(findGroup(config, kGroup2), nullptr) + << "unrelated peer-group was removed"; +} + +TEST_F(ConfigBgpPeerGroupTest, DeleteUnknownGroupRejected) { + clearBgpSession(); + auto result = + runCli({"delete", "protocol", "bgp", "peer-group", "NO-SUCH-GROUP"}); + EXPECT_THAT(result.stdout, HasSubstr("not found")); + EXPECT_FALSE(std::filesystem::exists(bgpSessionPath())) + << "session file should not exist after rejected delete"; +} + +TEST_F(ConfigBgpPeerGroupTest, DeleteGroupAndCommit) { + // Land a peer-group in the system config, then delete it through a second + // commit and verify it is gone from bgpd's running config. + discardSession(); + clearBgpSession(); + stagePeerGroup({kGroup, "remote-asn", "65000"}); + commitAndGetSha(); + ASSERT_TRUE(waitForBgpDaemonActive()) + << "bgpd did not return active after commit; state=" + << bgpDaemonActiveState(); + ASSERT_NE(findGroup(readRunningBgpConfigViaRpc(), kGroup), nullptr) + << "setup commit did not land the peer-group in bgpd's running config"; + + clearBgpSession(); + auto result = runCli({"delete", "protocol", "bgp", "peer-group", kGroup}); + EXPECT_THAT(result.stdout, HasSubstr("Successfully deleted")); + commitAndGetSha(); + ASSERT_TRUE(waitForBgpDaemonActive()) + << "bgpd did not return active after delete commit; state=" + << bgpDaemonActiveState(); + EXPECT_EQ(findGroup(readRunningBgpConfigViaRpc(), kGroup), nullptr) + << "deleted peer-group still present in bgpd's running config"; +} diff --git a/fboss/cli/fboss2/test/integration_test/ConfigBgpPolicyAsPathListTest.cpp b/fboss/cli/fboss2/test/integration_test/ConfigBgpPolicyAsPathListTest.cpp new file mode 100644 index 0000000000000..8b8d96f0487b8 --- /dev/null +++ b/fboss/cli/fboss2/test/integration_test/ConfigBgpPolicyAsPathListTest.cpp @@ -0,0 +1,255 @@ +// (c) Meta Platforms, Inc. and affiliates. Confidential and proprietary. + +/** + * End-to-end tests for `fboss2-dev config protocol bgp policy as-path-list + * [entry ] [ ...]`. + * + * Scope: the AS-path list and its entries. The commit-path tests stage the + * change AND commit it, then assert the value landed at the correct thrift + * field path inside the matching .policies.aspath_lists[] entry of bgpd's + * running config (via getRunningConfig RPC) — which also confirms bgpd + * accepts and adopts a `.policies` blob at all. The remaining attributes are + * verified in the staged session JSON (~/.fboss2/bgp_config.json) to keep the + * suite's daemon-restart count down; the promote mechanics are identical for + * every attribute and are covered by the commit tests + ConfigBgpSessionTest. + * + * Requirements: + * - The fboss2-dev binary under test (config subcommand tree). + * - HOME is set (the session file lives under $HOME/.fboss2). + * - bgpd is installed/active (commit restarts it). + */ + +#include +#include +#include +#include + +#include "fboss/cli/fboss2/test/integration_test/ConfigBgpTestBase.h" +#include "folly/json/dynamic.h" +#include "gmock/gmock.h" + +using namespace facebook::fboss; +using ::testing::HasSubstr; +using ::testing::Not; + +namespace { +// Test-only AS-path-list names, unlikely to collide with a real list in the +// device's running BGP config. +const std::string kList = "FBOSS2-TEST-ASPATH"; +const std::string kList2 = "FBOSS2-TEST-ASPATH-2"; +} // namespace + +class ConfigBgpPolicyAsPathListTest : public ConfigBgpTestBase { + protected: + // Run `config protocol bgp policy as-path-list ` WITHOUT clearing + // the staged session, so attributes can accumulate across invocations. + auto runAsPathList(const std::vector& tokens) { + std::vector args = { + "config", "protocol", "bgp", "policy", "as-path-list"}; + args.insert(args.end(), tokens.begin(), tokens.end()); + auto result = runCli(args); + EXPECT_EQ(result.exitCode, 0) + << "stdout=" << result.stdout << " stderr=" << result.stderr; + return result; + } + + // Stage an as-path-list command expected to succeed and return the staged + // session JSON. + folly::dynamic stageAsPathList(const std::vector& tokens) { + auto result = runAsPathList(tokens); + EXPECT_THAT(result.stdout, Not(HasSubstr("Error:"))); + return readBgpSessionConfig(); + } + + // The .policies.aspath_lists[] entry named `name`, or nullptr. + static const folly::dynamic* findList( + const folly::dynamic& config, + const std::string& name) { + if (config.count("policies") == 0 || + config["policies"].count("aspath_lists") == 0) { + return nullptr; + } + for (const auto& list : config["policies"]["aspath_lists"]) { + if (list.count("name") && list["name"].asString() == name) { + return &list; + } + } + return nullptr; + } + + // The .as_path_list[] entry with sequence_number `seq` inside `list`, or + // nullptr. + static const folly::dynamic* findEntry( + const folly::dynamic& list, + int64_t seq) { + if (list.count("as_path_list") == 0) { + return nullptr; + } + for (const auto& entry : list["as_path_list"]) { + if (entry.count("sequence_number") && + entry["sequence_number"].asInt() == seq) { + return &entry; + } + } + return nullptr; + } +}; + +TEST_F(ConfigBgpPolicyAsPathListTest, SetListDescriptionAndCommit) { + discardSession(); + clearBgpSession(); + stageAsPathList({kList, "description", "test", "spine", "as-paths"}); + commitAndGetSha(); + ASSERT_TRUE(waitForBgpDaemonActive()) + << "bgpd did not return active after commit; state=" + << bgpDaemonActiveState(); + // Two layers: the promoted file proves what the commit wrote; the daemon's + // getRunningConfig RPC proves bgpd parsed and adopted the .policies blob. + auto config = readSystemBgpConfig(); + const auto* list = findList(config, kList); + ASSERT_NE(list, nullptr) << "committed config has no as-path-list " << kList; + EXPECT_EQ((*list)["description"].asString(), "test spine as-paths"); + + auto running = readRunningBgpConfigViaRpc(); + const auto* runningList = findList(running, kList); + ASSERT_NE(runningList, nullptr) + << "bgpd's running config has no as-path-list " << kList; + EXPECT_EQ((*runningList)["description"].asString(), "test spine as-paths"); +} + +TEST_F(ConfigBgpPolicyAsPathListTest, SetEntryAsnRegexpAndCommit) { + discardSession(); + clearBgpSession(); + stageAsPathList({kList, "entry", "10", "asn-regexp", "^65000_"}); + commitAndGetSha(); + ASSERT_TRUE(waitForBgpDaemonActive()) + << "bgpd did not return active after commit; state=" + << bgpDaemonActiveState(); + // Verify the nested union path .as_path.as_path.asn_regexp through the + // daemon's own view of its config. + auto running = readRunningBgpConfigViaRpc(); + const auto* list = findList(running, kList); + ASSERT_NE(list, nullptr) << "bgpd's running config has no as-path-list " + << kList; + const auto* entry = findEntry(*list, 10); + ASSERT_NE(entry, nullptr) << "running config has no entry 10"; + EXPECT_EQ((*entry)["as_path"]["as_path"]["asn_regexp"].asString(), "^65000_"); +} + +TEST_F(ConfigBgpPolicyAsPathListTest, BareCreateListStagesList) { + clearBgpSession(); + auto config = stageAsPathList({kList}); + const auto* list = findList(config, kList); + ASSERT_NE(list, nullptr) << "bare `as-path-list ` did not create it"; +} + +TEST_F(ConfigBgpPolicyAsPathListTest, EntryAttributesAccumulate) { + clearBgpSession(); + stageAsPathList({kList, "entry", "10", "asn-regexp", "^65000_"}); + stageAsPathList({kList, "entry", "10", "description", "match", "65000"}); + auto config = + stageAsPathList({kList, "entry", "10", "match-logic", "NOT_EQUAL"}); + + const auto* list = findList(config, kList); + ASSERT_NE(list, nullptr); + const auto* entry = findEntry(*list, 10); + ASSERT_NE(entry, nullptr); + EXPECT_EQ((*entry)["as_path"]["as_path"]["asn_regexp"].asString(), "^65000_"); + EXPECT_EQ((*entry)["description"].asString(), "match 65000"); + // MatchValueLogicOperator.NOT_EQUAL = 1. + EXPECT_EQ((*entry)["match_logic_type"].asInt(), 1); + // Exactly one entry despite three invocations. + EXPECT_EQ((*list)["as_path_list"].size(), 1); +} + +TEST_F(ConfigBgpPolicyAsPathListTest, MultipleEntriesAreKeyedBySeq) { + clearBgpSession(); + stageAsPathList({kList, "entry", "10", "asn-regexp", "^65000_"}); + auto config = + stageAsPathList({kList, "entry", "20", "asn-regexp", "^65001_"}); + const auto* list = findList(config, kList); + ASSERT_NE(list, nullptr); + EXPECT_EQ((*list)["as_path_list"].size(), 2); + ASSERT_NE(findEntry(*list, 10), nullptr); + ASSERT_NE(findEntry(*list, 20), nullptr); +} + +TEST_F(ConfigBgpPolicyAsPathListTest, InvalidMatchLogicRejected) { + clearBgpSession(); + auto result = runAsPathList({kList, "entry", "10", "match-logic", "MAYBE"}); + EXPECT_THAT(result.stdout, HasSubstr("Invalid")); + EXPECT_FALSE(std::filesystem::exists(bgpSessionPath())) + << "session file should not exist after rejected input"; +} + +TEST_F(ConfigBgpPolicyAsPathListTest, UnknownAttributeRejected) { + clearBgpSession(); + // asn-regexp is an entry attribute; used at the list level it is unknown and + // the CLI must refuse it instead of persisting dead config. An unknown + // attribute is rejected at arg-parse time (a thrown std::invalid_argument), + // so the message is surfaced on stderr rather than stdout. + auto result = runCli( + {"config", + "protocol", + "bgp", + "policy", + "as-path-list", + kList, + "asn-regexp", + "^65000_"}); + EXPECT_THAT(result.stderr, HasSubstr("unknown")); + EXPECT_FALSE(std::filesystem::exists(bgpSessionPath())) + << "session file should not exist after rejected input"; +} + +TEST_F(ConfigBgpPolicyAsPathListTest, DeleteStagedList) { + clearBgpSession(); + stageAsPathList({kList, "description", "one"}); + stageAsPathList({kList2, "description", "two"}); + + auto result = + runCli({"delete", "protocol", "bgp", "policy", "as-path-list", kList}); + EXPECT_EQ(result.exitCode, 0) + << "stdout=" << result.stdout << " stderr=" << result.stderr; + EXPECT_THAT(result.stdout, HasSubstr("Successfully deleted")); + + auto config = readBgpSessionConfig(); + EXPECT_EQ(findList(config, kList), nullptr) + << "deleted as-path-list still present in the staged session"; + EXPECT_NE(findList(config, kList2), nullptr) + << "unrelated as-path-list was removed"; +} + +TEST_F(ConfigBgpPolicyAsPathListTest, DeleteUnknownListRejected) { + clearBgpSession(); + auto result = runCli( + {"delete", "protocol", "bgp", "policy", "as-path-list", "NO-SUCH-LIST"}); + EXPECT_THAT(result.stdout, HasSubstr("not found")); + EXPECT_FALSE(std::filesystem::exists(bgpSessionPath())) + << "session file should not exist after rejected delete"; +} + +TEST_F(ConfigBgpPolicyAsPathListTest, DeleteListAndCommit) { + // Land an as-path-list in the system config, then delete it through a second + // commit and verify it is gone from bgpd's running config. + discardSession(); + clearBgpSession(); + stageAsPathList({kList, "entry", "10", "asn-regexp", "^65000_"}); + commitAndGetSha(); + ASSERT_TRUE(waitForBgpDaemonActive()) + << "bgpd did not return active after commit; state=" + << bgpDaemonActiveState(); + ASSERT_NE(findList(readRunningBgpConfigViaRpc(), kList), nullptr) + << "setup commit did not land the as-path-list in bgpd's running config"; + + clearBgpSession(); + auto result = + runCli({"delete", "protocol", "bgp", "policy", "as-path-list", kList}); + EXPECT_THAT(result.stdout, HasSubstr("Successfully deleted")); + commitAndGetSha(); + ASSERT_TRUE(waitForBgpDaemonActive()) + << "bgpd did not return active after delete commit; state=" + << bgpDaemonActiveState(); + EXPECT_EQ(findList(readRunningBgpConfigViaRpc(), kList), nullptr) + << "deleted as-path-list still present in bgpd's running config"; +} diff --git a/fboss/cli/fboss2/test/integration_test/ConfigBgpSessionTest.cpp b/fboss/cli/fboss2/test/integration_test/ConfigBgpSessionTest.cpp new file mode 100644 index 0000000000000..d4595f1beb188 --- /dev/null +++ b/fboss/cli/fboss2/test/integration_test/ConfigBgpSessionTest.cpp @@ -0,0 +1,218 @@ +// (c) Meta Platforms, Inc. and affiliates. Confidential and proprietary. + +/** + * End-to-end tests for BGP config *session* management, mirroring the agent + * config session tests (e.g. ConfigSessionClearTest) but for the BGP domain. + * + * BGP global edits stage ~/.fboss2/bgp_config.json (no agent.conf), and + * `config session commit` promotes it to /etc/coop/bgpcpp/bgpcpp.conf and + * restarts bgpd. These tests exercise the session lifecycle around that: + * - clear (including a BGP-only session) + * - diff (a staged BGP change must be visible) + * - commit (bgpd is restarted) + * - rollback (the bgpd system config is restored) + * + * Per-attribute global config behavior lives in ConfigBgpGlobalTest. + * + * Requirements: + * - The fboss2-dev binary under test (config subcommand tree). + * - HOME is set (the session file lives under $HOME/.fboss2). + * - bgpd installed; commit/rollback tests skip if bgpd is not active. + */ + +#include +#include +#include + +#include "fboss/cli/fboss2/test/integration_test/ConfigBgpTestBase.h" + +namespace fs = std::filesystem; + +using namespace facebook::fboss; +using ::testing::HasSubstr; +using ::testing::Not; + +class ConfigBgpSessionTest : public ConfigBgpTestBase {}; + +// Staging a BGP global change creates a session file; `config session clear` +// removes it (a BGP-only session has no agent.conf to key off of). +TEST_F(ConfigBgpSessionTest, CreateAndClearBgpSession) { + runBgpGlobal("graceful-restart-time", "55"); + ASSERT_TRUE(fs::exists(bgpSessionPath())) + << "expected a staged BGP session after a global change"; + + auto result = runCli({"config", "session", "clear"}); + ASSERT_EQ(result.exitCode, 0) << "stderr=" << result.stderr; + + EXPECT_FALSE(fs::exists(bgpSessionPath())) + << "config session clear must remove the staged BGP config"; +} + +TEST_F(ConfigBgpSessionTest, ClearNonExistentBgpSession) { + // Start fully clean: no agent session, no BGP session, no metadata. + discardSession(); + clearBgpSession(); + + auto result = runCli({"config", "session", "clear"}); + ASSERT_EQ(result.exitCode, 0) << "stderr=" << result.stderr; + EXPECT_THAT(result.stdout, HasSubstr("No config session exists")); +} + +// `config session diff` must surface a staged BGP global change. +TEST_F(ConfigBgpSessionTest, DiffShowsStagedBgpChange) { + runBgpGlobal("graceful-restart-time", "77"); + + auto result = runCli({"config", "session", "diff"}); + ASSERT_EQ(result.exitCode, 0) << "stderr=" << result.stderr; + EXPECT_THAT(result.stdout, Not(HasSubstr("No config session exists"))); + EXPECT_THAT(result.stdout, HasSubstr("graceful_restart_convergence_seconds")); +} + +// Committing a staged BGP change restarts the bgpd daemon. +TEST_F(ConfigBgpSessionTest, CommitRestartsBgpDaemon) { + if (bgpDaemonActiveState() != "active") { + GTEST_SKIP() << "bgpd is not active on this DUT; skipping commit/restart " + "verification"; + } + discardSession(); + const std::string pidBefore = bgpDaemonMainPid(); + + runBgpGlobal("graceful-restart-time", "123"); + ASSERT_FALSE(commitAndGetSha().empty()); + + EXPECT_TRUE(waitForBgpDaemonActive()) + << "bgpd did not return to active after commit; state=" + << bgpDaemonActiveState(); + + const std::string pidAfter = bgpDaemonMainPid(); + EXPECT_NE(pidAfter, "0") << "bgpd has no MainPID after commit"; + EXPECT_NE(pidAfter, pidBefore) << "bgpd MainPID unchanged (" << pidBefore + << "); expected a restart on commit"; +} + +// Rolling back a committed BGP change restores the bgpd system config and +// restarts bgpd (not just the agent config). +TEST_F(ConfigBgpSessionTest, RollbackRestoresBgpConfig) { + if (bgpDaemonActiveState() != "active") { + GTEST_SKIP() << "bgpd is not active on this DUT; skipping rollback " + "verification"; + } + discardSession(); + + auto convergenceSeconds = [&]() { + return readSystemBgpConfig()["graceful_restart_convergence_seconds"] + .asInt(); + }; + + // Commit A (111), then commit B (222). + runBgpGlobal("graceful-restart-time", "111"); + std::string shaA = commitAndGetSha(); + ASSERT_FALSE(shaA.empty()) << "could not parse commit A sha"; + ASSERT_TRUE(waitForBgpDaemonActive()); + + runBgpGlobal("graceful-restart-time", "222"); + ASSERT_FALSE(commitAndGetSha().empty()) << "could not parse commit B sha"; + ASSERT_TRUE(waitForBgpDaemonActive()); + + EXPECT_EQ(convergenceSeconds(), 222) + << "system bgp config should reflect the latest commit"; + + // Roll back to commit A -> system bgp config restored to 111, bgpd restart. + resetBgpDaemonLimit(); // avoid systemd start-limit on the rollback's restart + auto rb = runCli({"config", "rollback", shaA}); + EXPECT_EQ(rb.exitCode, 0) << "stderr=" << rb.stderr; + ASSERT_TRUE(waitForBgpDaemonActive()) + << "bgpd did not return active after rollback"; + + EXPECT_EQ(convergenceSeconds(), 111) + << "rollback must restore the bgpd system config"; +} + +// Committing a BGP config identical to what is already running must be a no-op: +// "Nothing to commit" and, crucially, NO bgpd restart (a restart is traffic +// affecting). saveBgpConfig() always records BGP_RESTART, so commit() has to +// compare the staged config against the running one. +TEST_F(ConfigBgpSessionTest, CommitUnchangedBgpConfigDoesNotRestart) { + if (bgpDaemonActiveState() != "active") { + GTEST_SKIP() << "bgpd is not active on this DUT; skipping no-op restart " + "verification"; + } + discardSession(); + + // Establish a known running value. + runBgpGlobal("graceful-restart-time", "88"); + ASSERT_FALSE(commitAndGetSha().empty()); + ASSERT_TRUE(waitForBgpDaemonActive()); + const std::string pidBefore = bgpDaemonMainPid(); + ASSERT_NE(pidBefore, "0"); + + // Stage the SAME value and commit again -> nothing to commit, no restart. + runBgpGlobal("graceful-restart-time", "88"); + auto result = runCli({"config", "session", "commit"}); + EXPECT_EQ(result.exitCode, 0) << "stderr=" << result.stderr; + EXPECT_THAT(result.stdout, HasSubstr("Nothing to commit")) + << "an unchanged BGP commit should report nothing to commit"; + + EXPECT_EQ(bgpDaemonMainPid(), pidBefore) + << "bgpd was restarted for an unchanged BGP config (pidBefore=" + << pidBefore << ")"; +} + +// An agent-only commit (no BGP staged) must still snapshot the running bgpd +// config, so a rollback to it restores BGP instead of wiping it. Pre-fix, +// bgpcpp.conf was untracked at such commits and rollback deleted it. +TEST_F(ConfigBgpSessionTest, AgentCommitSnapshotsBgpAndRollbackPreservesIt) { + if (bgpDaemonActiveState() != "active") { + GTEST_SKIP() << "bgpd is not active on this DUT; skipping cross-domain " + "rollback verification"; + } + discardSession(); + clearBgpSession(); + + // The running (default) BGP config that must survive a rollback unchanged. + ASSERT_TRUE(fs::exists(systemBgpConfigPath())) + << "expected a running bgpd config on an active-BGP DUT"; + const folly::dynamic defaultBgp = readSystemBgpConfig(); + + // Step 1: an agent-only commit (interface description). No BGP is staged. + Interface intf = findFirstEthInterface(); + auto setRes = + runCli({"config", "interface", intf.name, "description", "bgp-rb-test"}); + ASSERT_EQ(setRes.exitCode, 0) << "stderr=" << setRes.stderr; + ASSERT_FALSE(fs::exists(bgpSessionPath())) + << "an interface-description change must not stage a BGP session"; + resetBgpDaemonLimit(); + commitConfig(); + waitForAgentReady(); + const std::string agentSha = gitHead(); + ASSERT_FALSE(agentSha.empty()) + << "could not read git HEAD after agent commit"; + + // The fix: bgpcpp.conf must be tracked at the agent-only commit, so a later + // rollback to it has a faithful BGP snapshot to restore. + EXPECT_TRUE(bgpTrackedAtRevision(agentSha)) + << "agent-only commit " << agentSha + << " did not snapshot bgpcpp.conf; a rollback to it would wipe BGP"; + + // Step 2: stage + commit a BGP change so the running config diverges from the + // snapshot captured at agentSha. + runBgpGlobal("graceful-restart-time", "222"); + ASSERT_FALSE(commitAndGetSha().empty()) << "BGP commit produced no SHA"; + ASSERT_TRUE(waitForBgpDaemonActive()); + EXPECT_EQ( + readSystemBgpConfig()["graceful_restart_convergence_seconds"].asInt(), + 222); + + // Step 3: roll back to the agent-only commit. BGP must be RESTORED to the + // original default snapshot (not deleted), and bgpd must come back. + resetBgpDaemonLimit(); + auto rb = runCli({"config", "rollback", agentSha}); + EXPECT_EQ(rb.exitCode, 0) << "stderr=" << rb.stderr; + ASSERT_TRUE(waitForBgpDaemonActive()) + << "bgpd did not return active after rollback"; + + EXPECT_TRUE(fs::exists(systemBgpConfigPath())) + << "rollback to an agent-era commit wiped the running bgpd config"; + EXPECT_EQ(readSystemBgpConfig(), defaultBgp) + << "rollback did not restore the original (default) bgpd config"; +} diff --git a/fboss/cli/fboss2/test/integration_test/ConfigBgpTestBase.h b/fboss/cli/fboss2/test/integration_test/ConfigBgpTestBase.h new file mode 100644 index 0000000000000..2103373448c8a --- /dev/null +++ b/fboss/cli/fboss2/test/integration_test/ConfigBgpTestBase.h @@ -0,0 +1,264 @@ +// (c) Meta Platforms, Inc. and affiliates. Confidential and proprietary. + +#pragma once + +/** + * Shared fixture + helpers for BGP config integration tests. + * + * BGP config is not pushed into the agent's running config like the + * SwitchConfig-backed commands. Instead the CLI stages a BGP++ + * thrift-compatible JSON file at ~/.fboss2/bgp_config.json; `config session + * commit` promotes it to /etc/coop/bgpcpp/bgpcpp.conf and restarts the bgpd + * daemon. These helpers let the global-attribute tests and the + * session-lifecycle tests share the staging, commit, daemon-state, and + * system-config plumbing. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "fboss/cli/fboss2/test/integration_test/Fboss2IntegrationTest.h" +#include "fboss/cli/fboss2/utils/CmdClientUtilsCommon.h" +#include "neteng/fboss/bgp/if/gen-cpp2/TBgpService.h" + +namespace facebook::fboss { + +class ConfigBgpTestBase : public Fboss2IntegrationTest { + protected: + void SetUp() override { + Fboss2IntegrationTest::SetUp(); + if (bgpDaemonActiveState() != "active") { + GTEST_SKIP() << "skipping because bgp is not active"; + } + } + + // Staged session file written by the CLI (~/.fboss2/bgp_config.json). + std::string bgpSessionPath() const { + // NOLINTNEXTLINE(concurrency-mt-unsafe): HOME is read-only in practice + const char* home = std::getenv("HOME"); + if (home == nullptr) { + throw std::runtime_error("HOME environment variable not set"); + } + return std::string(home) + "/.fboss2/bgp_config.json"; + } + + // System config consumed by the bgpd daemon (written on commit). + std::string systemBgpConfigPath() const { + return "/etc/coop/bgpcpp/bgpcpp.conf"; + } + + // Remove any pre-existing staged BGP session so each test starts clean. + void clearBgpSession() const { + std::remove(bgpSessionPath().c_str()); + } + + folly::dynamic readBgpSessionConfig() const { + std::string content; + if (!folly::readFile(bgpSessionPath().c_str(), content)) { + throw std::runtime_error( + "Failed to read BGP session config at " + bgpSessionPath()); + } + return folly::parseJson(content); + } + + folly::dynamic readSystemBgpConfig() const { + std::string content; + if (!folly::readFile(systemBgpConfigPath().c_str(), content)) { + throw std::runtime_error( + "Failed to read system BGP config at " + systemBgpConfigPath()); + } + return folly::parseJson(content); + } + + // The running config as reported by the bgpd daemon itself, over its + // TBgpService::getRunningConfig thrift RPC. Unlike readSystemBgpConfig() + // (the promoted file on disk, which only proves what the CLI wrote), this + // proves the daemon parsed and adopted the config after the post-commit + // restart. Mirrors Fboss2IntegrationTest::getRunningConfig() for the agent. + // + // Retries on connection errors: systemd reports bgpd "active" as soon as + // the process starts, but the thrift server only binds its port a few + // seconds later — an RPC issued right after a commit-triggered restart + // would otherwise race that window and get ECONNREFUSED. + folly::dynamic readRunningBgpConfigViaRpc( + std::chrono::seconds timeout = std::chrono::seconds(30)) const { + auto deadline = std::chrono::steady_clock::now() + timeout; + while (true) { + try { + HostInfo hostInfo("localhost"); + auto client = utils::createClient>(hostInfo); + std::string configStr; + client->sync_getRunningConfig(configStr); + return folly::parseJson(configStr); + } catch (const std::exception&) { + if (std::chrono::steady_clock::now() >= deadline) { + throw; + } + std::this_thread::sleep_for(std::chrono::seconds(1)); + } + } + } + + // Root of the git repo that versions both the agent config (cli/agent.conf) + // and the BGP config (bgpcpp/bgpcpp.conf). + std::string coopRepoDir() const { + return "/etc/coop"; + } + + // Current git HEAD sha of the /etc/coop repo (trimmed; "" on failure). + // runCmd uses folly::Subprocess, which does not search PATH, so git is + // invoked by full path. -c safe.directory mirrors the CLI's Git class: + // /etc/coop is typically owned by another user (e.g. coop), which git + // otherwise rejects as dubious ownership. + std::string gitHead() const { + auto r = runCmd( + {"/usr/bin/git", + "-c", + "safe.directory=" + coopRepoDir(), + "-C", + coopRepoDir(), + "rev-parse", + "HEAD"}); + std::string s = r.stdout; + while (!s.empty() && + (s.back() == '\n' || s.back() == '\r' || s.back() == ' ')) { + s.pop_back(); + } + return s; + } + + // True if bgpcpp/bgpcpp.conf is tracked (present in the tree) at the given + // revision. `git cat-file -e :` exits 0 iff the blob exists. + bool bgpTrackedAtRevision(const std::string& sha) const { + auto r = runCmd( + {"/usr/bin/git", + "-c", + "safe.directory=" + coopRepoDir(), + "-C", + coopRepoDir(), + "cat-file", + "-e", + sha + ":bgpcpp/bgpcpp.conf"}); + return r.exitCode == 0; + } + + // Stage a `config protocol bgp global ` change (no commit). + // Returns the staged session JSON. + folly::dynamic runBgpGlobal(const std::string& attr, const std::string& value) + const { + clearBgpSession(); + XLOG(INFO) << "Running: config protocol bgp global " << attr << " " + << value; + auto result = runCli({"config", "protocol", "bgp", "global", attr, value}); + EXPECT_EQ(result.exitCode, 0) + << "stdout=" << result.stdout << " stderr=" << result.stderr; + // Handler error strings are printed to stdout with exit code 0, so guard + // against silent failures by rejecting an "Error" prefix. + EXPECT_THAT(result.stdout, ::testing::Not(::testing::HasSubstr("Error:"))); + return readBgpSessionConfig(); + } + + // Reset bgpd's systemd start-limit burst counter. Tests commit several + // times in quick succession, and each commit restarts bgpd; without this, + // systemd's default StartLimitBurst (5 starts / 10s) refuses a rapid restart + // with "start limit hit" — a test artifact, not a product issue. + void resetBgpDaemonLimit() const { + runCmd({"/usr/bin/systemctl", "reset-failed", "bgpd"}); + } + + // Run `config session commit` and return the short SHA parsed from + // "Config session committed successfully as ". Empty string on failure. + std::string commitAndGetSha() const { + resetBgpDaemonLimit(); + auto r = runCli({"config", "session", "commit"}); + EXPECT_EQ(r.exitCode, 0) << "stderr=" << r.stderr; + const std::string marker = "as "; + auto pos = r.stdout.find(marker); + if (pos == std::string::npos) { + return ""; + } + pos += marker.size(); + auto end = r.stdout.find_first_of(" .\n", pos); + return r.stdout.substr(pos, end - pos); + } + + // Stage a global change, commit it, wait for bgpd to come back, and return + // bgpd's own running config as reported over its getRunningConfig RPC. + // + // The returned config is the daemon's adopted view, which is strictly + // stronger than the promoted file on disk (readSystemBgpConfig): a value + // present here proves the commit promoted /etc/coop/bgpcpp/bgpcpp.conf AND + // that bgpd parsed and adopted it after the commit-triggered restart, not + // merely that the CLI wrote the file. The running config shares the same + // BgpConfig JSON schema as the promoted file, so callers assert the same + // field paths against it. + // + // A no-op commit (staged value already current) yields no SHA and is fine: + // the caller's assertions on the returned config still verify the value; + // commit/restart mechanics are covered by ConfigBgpSessionTest. + folly::dynamic setAndCommit(const std::string& attr, const std::string& value) + const { + discardSession(); + runBgpGlobal(attr, value); + commitAndGetSha(); + EXPECT_TRUE(waitForBgpDaemonActive()) + << "bgpd did not return active after commit; state=" + << bgpDaemonActiveState(); + return readRunningBgpConfigViaRpc(); + } + + // Trailing-whitespace-trimmed stdout of a `systemctl` query for bgpd. + std::string systemctlValue(const std::string& property) const { + // Full path: folly::Subprocess (used by runCmd) does not search PATH. + auto r = runCmd( + {"/usr/bin/systemctl", "show", "bgpd", "-p", property, "--value"}); + std::string s = r.stdout; + while (!s.empty() && + (s.back() == '\n' || s.back() == '\r' || s.back() == ' ')) { + s.pop_back(); + } + return s; + } + + // "active" / "inactive" / "failed" / ... (trimmed). + std::string bgpDaemonActiveState() const { + auto r = runCmd({"/usr/bin/systemctl", "is-active", "bgpd"}); + std::string s = r.stdout; + while (!s.empty() && + (s.back() == '\n' || s.back() == '\r' || s.back() == ' ')) { + s.pop_back(); + } + return s; + } + + // bgpd main PID per systemd ("0" if not running). + std::string bgpDaemonMainPid() const { + return systemctlValue("MainPID"); + } + + // Poll until bgpd reports active or the timeout elapses. + bool waitForBgpDaemonActive( + std::chrono::seconds timeout = std::chrono::seconds(60)) const { + auto deadline = std::chrono::steady_clock::now() + timeout; + while (std::chrono::steady_clock::now() < deadline) { + if (bgpDaemonActiveState() == "active") { + return true; + } + std::this_thread::sleep_for(std::chrono::seconds(1)); + } + return bgpDaemonActiveState() == "active"; + } +}; + +} // namespace facebook::fboss diff --git a/fboss/cli/fboss2/tools/bgp_json_to_cli.py b/fboss/cli/fboss2/tools/bgp_json_to_cli.py index ddf328c4fd3ca..87cdf57419ac2 100644 --- a/fboss/cli/fboss2/tools/bgp_json_to_cli.py +++ b/fboss/cli/fboss2/tools/bgp_json_to_cli.py @@ -10,17 +10,17 @@ This enables round-trip conversion: JSON -> CLI -> JSON (via fboss2). Usage: - python3 bgp_json_to_cli.py [--output output.sh] [--stub] + python3 bgp_json_to_cli.py [--output output.sh] Example: - python3 bgp_json_to_cli.py rsw_bgp_config.json --stub > configure_bgp.sh + python3 bgp_json_to_cli.py rsw_bgp_config.json > configure_bgp.sh """ import argparse import json import shlex import sys -from typing import Any, Dict, List +from typing import Any def escape_shell_arg(arg: str) -> str: @@ -28,7 +28,7 @@ def escape_shell_arg(arg: str) -> str: return shlex.quote(str(arg)) -def _generate_global_basic_commands(config: Dict[str, Any]) -> List[str]: +def _generate_global_basic_commands(config: dict[str, Any]) -> list[str]: """Generate basic global BGP commands.""" commands = [] if "router_id" in config: @@ -47,14 +47,10 @@ def _generate_global_basic_commands(config: Dict[str, Any]) -> List[str]: commands.append( f"config protocol bgp global confed-asn {escape_shell_arg(str(config['local_confed_as_4_byte']))}" ) - if "cluster_id" in config: - commands.append( - f"config protocol bgp global cluster-id {escape_shell_arg(config['cluster_id'])}" - ) return commands -def _generate_network6_commands(networks: List[Dict[str, Any]]) -> List[str]: +def _generate_network6_commands(networks: list[dict[str, Any]]) -> list[str]: """Generate network6 commands for global BGP config.""" commands = [] for network in networks: @@ -76,7 +72,7 @@ def _generate_network6_commands(networks: List[Dict[str, Any]]) -> List[str]: return commands -def _generate_switch_limit_commands(switch_limit: Dict[str, Any]) -> List[str]: +def _generate_switch_limit_commands(switch_limit: dict[str, Any]) -> list[str]: """Generate switch_limit_config commands.""" commands = [] if "prefix_limit" in switch_limit: @@ -98,7 +94,7 @@ def _generate_switch_limit_commands(switch_limit: Dict[str, Any]) -> List[str]: return commands -def generate_global_commands(config: Dict[str, Any]) -> List[str]: +def generate_global_commands(config: dict[str, Any]) -> list[str]: """Generate CLI commands for global BGP settings.""" commands = [] commands.extend(_generate_global_basic_commands(config)) @@ -110,8 +106,8 @@ def generate_global_commands(config: Dict[str, Any]) -> List[str]: def _generate_peer_group_basic_commands( - peer_group: Dict[str, Any], escaped_name: str -) -> List[str]: + peer_group: dict[str, Any], escaped_name: str +) -> list[str]: """Generate basic peer-group commands (remote-asn, description, policies).""" commands = [] if "remote_as_4_byte" in peer_group: @@ -138,8 +134,8 @@ def _generate_peer_group_basic_commands( def _generate_peer_group_bool_commands( - peer_group: Dict[str, Any], escaped_name: str -) -> List[str]: + peer_group: dict[str, Any], escaped_name: str +) -> list[str]: """Generate boolean flag commands for peer-group.""" commands = [] bool_fields = [ @@ -158,8 +154,8 @@ def _generate_peer_group_bool_commands( def _generate_peer_group_timer_commands( - timers: Dict[str, Any], escaped_name: str -) -> List[str]: + timers: dict[str, Any], escaped_name: str +) -> list[str]: """Generate timer commands for peer-group.""" commands = [] if timers.get("hold_time_seconds"): @@ -182,8 +178,8 @@ def _generate_peer_group_timer_commands( def _generate_peer_group_prefilter_commands( - pre_filter: Dict[str, Any], escaped_name: str -) -> List[str]: + pre_filter: dict[str, Any], escaped_name: str +) -> list[str]: """Generate pre_filter commands for peer-group.""" commands = [] if pre_filter.get("max_routes"): @@ -201,7 +197,7 @@ def _generate_peer_group_prefilter_commands( return commands -def generate_peer_group_commands(peer_group: Dict[str, Any]) -> List[str]: +def generate_peer_group_commands(peer_group: dict[str, Any]) -> list[str]: """Generate CLI commands for a peer group.""" name = peer_group.get("name", "") if not name: @@ -228,179 +224,164 @@ def format_bandwidth(bps: int) -> str: """Format bandwidth in bps to human-readable format (e.g., 10G).""" if bps >= 1_000_000_000_000 and bps % 1_000_000_000_000 == 0: return f"{bps // 1_000_000_000_000}T" - elif bps >= 1_000_000_000 and bps % 1_000_000_000 == 0: + if bps >= 1_000_000_000 and bps % 1_000_000_000 == 0: return f"{bps // 1_000_000_000}G" - elif bps >= 1_000_000 and bps % 1_000_000 == 0: + if bps >= 1_000_000 and bps % 1_000_000 == 0: return f"{bps // 1_000_000}M" - elif bps >= 1_000 and bps % 1_000 == 0: + if bps >= 1_000 and bps % 1_000 == 0: return f"{bps // 1_000}K" return str(bps) -def _generate_peer_basic_commands(peer: Dict[str, Any], peer_addr: str) -> List[str]: - """Generate basic peer commands (remote-asn, peer-group, description, etc.).""" +# (json field, CLI attribute, escape as free-form shell arg) — fields whose +# value maps 1:1 onto a neighbor attribute token. +_NEIGHBOR_SCALAR_FIELDS = [ + ("remote_as_4_byte", "remote-asn", False), + ("local_as_4_byte", "local-asn", False), + ("peer_group_name", "peer-group", True), + ("description", "description", True), + ("peer_tag", "peer-tag", True), + ("local_addr", "bind-addr address", True), + ("ingress_policy_name", "ingress-policy", True), + ("egress_policy_name", "egress-policy", True), + ("next_hop4", "next-hop4", True), + ("next_hop6", "next-hop6", True), + ("peer_id", "peer-id", True), + ("type", "type", True), +] + + +def _generate_neighbor_basic_commands(peer: dict[str, Any], prefix: str) -> list[str]: + """Generate scalar neighbor commands (ASNs, names, policies, addresses).""" commands = [] - if "remote_as_4_byte" in peer: - commands.append( - f"config protocol bgp peer {peer_addr} remote-asn {escape_shell_arg(str(peer['remote_as_4_byte']))}" - ) - if "peer_group_name" in peer: - commands.append( - f"config protocol bgp peer {peer_addr} peer-group {escape_shell_arg(peer['peer_group_name'])}" - ) - if "description" in peer: - commands.append( - f"config protocol bgp peer {peer_addr} description {escape_shell_arg(peer['description'])}" - ) - if "local_addr" in peer: - commands.append( - f"config protocol bgp peer {peer_addr} local-addr {escape_shell_arg(peer['local_addr'])}" - ) - if "ingress_policy_name" in peer: - commands.append( - f"config protocol bgp peer {peer_addr} ingress-policy {escape_shell_arg(peer['ingress_policy_name'])}" - ) - if "egress_policy_name" in peer: - commands.append( - f"config protocol bgp peer {peer_addr} egress-policy {escape_shell_arg(peer['egress_policy_name'])}" - ) + for field, cli_name, escape in _NEIGHBOR_SCALAR_FIELDS: + if field in peer: + value = escape_shell_arg(str(peer[field])) if escape else peer[field] + commands.append(f"{prefix} {cli_name} {value}") + if "is_passive" in peer: + mode = "PASSIVE" if peer["is_passive"] else "ACTIVE" + commands.append(f"{prefix} connect-mode {mode}") + if "link_bandwidth_bps" in peer: + bw = peer["link_bandwidth_bps"] + bw_str = format_bandwidth(bw) if isinstance(bw, int) else str(bw) + commands.append(f"{prefix} link-bandwidth {bw_str}") + if "advertise_link_bandwidth" in peer: + commands.append(f"{prefix} advertise-lbw {peer['advertise_link_bandwidth']}") + if "receive_link_bandwidth" in peer: + commands.append(f"{prefix} receive-lbw {peer['receive_link_bandwidth']}") return commands -def _generate_peer_bool_commands(peer: Dict[str, Any], peer_addr: str) -> List[str]: - """Generate boolean flag commands for peer.""" +def _generate_neighbor_bool_commands(peer: dict[str, Any], prefix: str) -> list[str]: + """Generate boolean flag commands for a neighbor.""" commands = [] bool_fields = [ - ("is_passive", "passive"), ("is_rr_client", "rr-client"), + ("is_confed_peer", "confed-peer"), + ("is_redistribute_peer", "redistribute-peer"), + ("enhanced_route_refresh", "enhanced-route-refresh"), + ("disable_ipv4_afi", "afi disable-ipv4-afi"), + ("disable_ipv6_afi", "afi disable-ipv6-afi"), + ("v4_over_v6_nexthop", "afi ipv4-over-ipv6-nh"), + ("enable_stateful_ha", "graceful-restart stateful-ha"), ("next_hop_self", "next-hop-self"), - ("v4_over_v6_nexthop", "v4-over-v6-nh"), - ("disable_ipv4_afi", "disable-ipv4-afi"), - ("advertise_link_bandwidth", "advertise-lbw"), ] for field, cli_name in bool_fields: if field in peer: - commands.append( - f"config protocol bgp peer {peer_addr} {cli_name} {str(peer[field]).lower()}" - ) + commands.append(f"{prefix} {cli_name} {str(peer[field]).lower()}") return commands -def _generate_peer_nexthop_commands(peer: Dict[str, Any], peer_addr: str) -> List[str]: - """Generate next-hop and link-bandwidth commands for peer.""" +def _generate_neighbor_add_path_commands( + peer: dict[str, Any], prefix: str +) -> list[str]: + """Generate add-path commands from the AddPath enum (RECEIVE=1, SEND=2, BOTH=3).""" + if "add_path" not in peer: + return [] + raw = peer["add_path"] + value = ( + {"RECEIVE": 1, "SEND": 2, "BOTH": 3}.get(raw, raw) + if isinstance(raw, str) + else raw + ) + value = int(value) commands = [] - if "next_hop4" in peer: - commands.append( - f"config protocol bgp peer {peer_addr} next-hop4 {escape_shell_arg(peer['next_hop4'])}" - ) - if "next_hop6" in peer: - commands.append( - f"config protocol bgp peer {peer_addr} next-hop6 {escape_shell_arg(peer['next_hop6'])}" - ) - if "link_bandwidth_bps" in peer: - bw = peer["link_bandwidth_bps"] - bw_str = format_bandwidth(bw) if isinstance(bw, int) else str(bw) - commands.append( - f"config protocol bgp peer {peer_addr} link-bandwidth {escape_shell_arg(bw_str)}" - ) + if value & 1: + commands.append(f"{prefix} add-path receive true") + if value & 2: + commands.append(f"{prefix} add-path send true") return commands -def _generate_peer_timer_commands(timers: Dict[str, Any], peer_addr: str) -> List[str]: - """Generate timer commands for peer.""" +def _generate_neighbor_timer_commands(timers: dict[str, Any], prefix: str) -> list[str]: + """Generate timer and graceful-restart commands for a neighbor.""" commands = [] - if timers.get("hold_time_seconds"): - commands.append( - f"config protocol bgp peer {peer_addr} hold-time {escape_shell_arg(str(timers['hold_time_seconds']))}" - ) - if timers.get("keep_alive_seconds"): - commands.append( - f"config protocol bgp peer {peer_addr} timers keepalive {escape_shell_arg(str(timers['keep_alive_seconds']))}" - ) + if "hold_time_seconds" in timers: + commands.append(f"{prefix} timers hold-time {timers['hold_time_seconds']}") + if "keep_alive_seconds" in timers: + commands.append(f"{prefix} timers keepalive {timers['keep_alive_seconds']}") if "out_delay_seconds" in timers: + commands.append(f"{prefix} timers out-delay {timers['out_delay_seconds']}") + if "withdraw_unprog_delay_seconds" in timers: commands.append( - f"config protocol bgp peer {peer_addr} timers out-delay {escape_shell_arg(str(timers['out_delay_seconds']))}" + f"{prefix} timers withdraw-unprog-delay {timers['withdraw_unprog_delay_seconds']}" ) - if "withdraw_unprog_delay_seconds" in timers: + if "graceful_restart_seconds" in timers: commands.append( - f"config protocol bgp peer {peer_addr} timers withdraw-unprog-delay {escape_shell_arg(str(timers['withdraw_unprog_delay_seconds']))}" + f"{prefix} graceful-restart restart-time {timers['graceful_restart_seconds']}" ) return commands -def _generate_peer_prefilter_commands( - pre_filter: Dict[str, Any], peer_addr: str -) -> List[str]: - """Generate pre_filter commands for peer.""" +def _generate_neighbor_route_limit_commands( + peer: dict[str, Any], prefix: str +) -> list[str]: + """Generate max-route commands from pre_filter/post_filter.""" commands = [] - if pre_filter.get("max_routes"): - commands.append( - f"config protocol bgp peer {peer_addr} max-routes {escape_shell_arg(str(pre_filter['max_routes']))}" - ) + pre_filter = peer.get("pre_filter", {}) + if "max_routes" in pre_filter: + commands.append(f"{prefix} max-route pre-filter {pre_filter['max_routes']}") if "warning_limit" in pre_filter: commands.append( - f"config protocol bgp peer {peer_addr} warning-limit {escape_shell_arg(str(pre_filter['warning_limit']))}" + f"{prefix} max-route pre-warning-threshold {pre_filter['warning_limit']}" ) if "warning_only" in pre_filter: commands.append( - f"config protocol bgp peer {peer_addr} warning-only {str(pre_filter['warning_only']).lower()}" + f"{prefix} max-route pre-warning-only {str(pre_filter['warning_only']).lower()}" ) - return commands - - -def _generate_peer_id_commands(peer: Dict[str, Any], peer_addr: str) -> List[str]: - """Generate peer identification commands.""" - commands = [] - if "peer_id" in peer: + post_filter = peer.get("post_filter", {}) + if "max_routes" in post_filter: + commands.append(f"{prefix} max-route post-filter {post_filter['max_routes']}") + if "warning_limit" in post_filter: commands.append( - f"config protocol bgp peer {peer_addr} peer-id {escape_shell_arg(peer['peer_id'])}" + f"{prefix} max-route post-warning-threshold {post_filter['warning_limit']}" ) - if "type" in peer: + if "warning_only" in post_filter: commands.append( - f"config protocol bgp peer {peer_addr} type {escape_shell_arg(peer['type'])}" + f"{prefix} max-route post-warning-only {str(post_filter['warning_only']).lower()}" ) return commands -def generate_peer_commands(peer: Dict[str, Any]) -> List[str]: - """Generate CLI commands for a peer.""" +def generate_peer_commands(peer: dict[str, Any]) -> list[str]: + """Generate `config protocol bgp neighbor` CLI commands for a peer.""" peer_addr = peer.get("peer_addr", "") if not peer_addr: return [] - escaped_peer_addr = escape_shell_arg(peer_addr) + prefix = f"config protocol bgp neighbor {escape_shell_arg(peer_addr)}" commands = [] - commands.extend(_generate_peer_basic_commands(peer, escaped_peer_addr)) - commands.extend(_generate_peer_bool_commands(peer, escaped_peer_addr)) - commands.extend(_generate_peer_nexthop_commands(peer, escaped_peer_addr)) - commands.extend( - _generate_peer_timer_commands( - peer.get("bgp_peer_timers", {}), escaped_peer_addr - ) - ) + commands.extend(_generate_neighbor_basic_commands(peer, prefix)) + commands.extend(_generate_neighbor_bool_commands(peer, prefix)) + commands.extend(_generate_neighbor_add_path_commands(peer, prefix)) commands.extend( - _generate_peer_prefilter_commands(peer.get("pre_filter", {}), escaped_peer_addr) + _generate_neighbor_timer_commands(peer.get("bgp_peer_timers", {}), prefix) ) - commands.extend(_generate_peer_id_commands(peer, escaped_peer_addr)) + commands.extend(_generate_neighbor_route_limit_commands(peer, prefix)) return commands -def generate_stub_commands(commands: List[str], binary: str = "fboss2") -> List[str]: - """Wrap commands for stub mode execution.""" - stub_commands = [ - "#!/bin/bash", - "# BGP configuration script generated by j2c", - "# Run with FBOSS_BGP_STUB_MODE=1 for testing", - "", - "export FBOSS_BGP_STUB_MODE=1", - "", - ] - for cmd in commands: - stub_commands.append(f"{binary} {cmd}") - return stub_commands - - -def generate_exec_commands(commands: List[str], binary: str = "fboss2") -> List[str]: +def generate_exec_commands(commands: list[str], binary: str = "fboss2") -> list[str]: """Generate executable commands with custom binary.""" exec_commands = [ "#!/bin/bash", @@ -424,9 +405,7 @@ def generate_exec_commands(commands: List[str], binary: str = "fboss2") -> List[ return exec_commands -def json_to_cli( - config: Dict[str, Any], stub: bool = False, binary: str = "fboss2" -) -> List[str]: +def json_to_cli(config: dict[str, Any], binary: str = "fboss2") -> list[str]: """Convert BGP++ JSON config to CLI commands.""" commands = [] @@ -441,9 +420,6 @@ def json_to_cli( for peer in config.get("peers", []): commands.extend(generate_peer_commands(peer)) - if stub: - return generate_stub_commands(commands, binary) - return generate_exec_commands(commands, binary) @@ -455,11 +431,6 @@ def main() -> int: parser.add_argument( "--output", "-o", help="Output file (default: stdout)", default=None ) - parser.add_argument( - "--stub", - action="store_true", - help="Generate stub mode script with FBOSS_BGP_STUB_MODE=1", - ) parser.add_argument( "--binary", "-b", @@ -474,7 +445,7 @@ def main() -> int: args = parser.parse_args() try: - with open(args.input, "r") as f: + with open(args.input) as f: config = json.load(f) except FileNotFoundError: print(f"Error: Input file '{args.input}' not found", file=sys.stderr) @@ -493,7 +464,7 @@ def main() -> int: commands.extend(generate_peer_commands(peer)) output = "\n".join(commands) else: - commands = json_to_cli(config, stub=args.stub, binary=args.binary) + commands = json_to_cli(config, binary=args.binary) output = "\n".join(commands) if args.output: diff --git a/fboss/cli/fboss2/tools/tests/test_bgp_json_to_cli.py b/fboss/cli/fboss2/tools/tests/test_bgp_json_to_cli.py index 0c5f7a604aad2..c4ede68bb8250 100644 --- a/fboss/cli/fboss2/tools/tests/test_bgp_json_to_cli.py +++ b/fboss/cli/fboss2/tools/tests/test_bgp_json_to_cli.py @@ -24,7 +24,6 @@ generate_global_commands, generate_peer_commands, generate_peer_group_commands, - generate_stub_commands, json_to_cli, ) @@ -43,12 +42,12 @@ def test_string_with_space_is_quoted(self) -> None: self.assertEqual(result, "'hello world'") def test_string_with_special_chars_is_quoted(self) -> None: - """Strings with shell special characters should be quoted.""" + """Shell specials should be quoted AND escaped so they stay literal.""" result = escape_shell_arg("value$var") self.assertEqual(result, "'value$var'") def test_string_with_backslash_is_quoted(self) -> None: - """Strings with backslash should be quoted.""" + """Backslashes should be quoted and doubled to stay literal.""" result = escape_shell_arg("path\\to\\file") self.assertEqual(result, "'path\\to\\file'") @@ -57,6 +56,12 @@ def test_empty_string(self) -> None: result = escape_shell_arg("") self.assertEqual(result, "''") + def test_embedded_quotes_and_dollars_escaped(self) -> None: + """Shell specials must be neutralized (shlex single-quoting).""" + self.assertEqual(escape_shell_arg('rack "A1" $spine'), "'rack \"A1\" $spine'") + self.assertEqual(escape_shell_arg("tick `id`"), "'tick `id`'") + self.assertEqual(escape_shell_arg("back\\slash"), "'back\\slash'") + def test_ipv6_address_not_escaped(self) -> None: """IPv6 addresses should not be quoted.""" result = escape_shell_arg("2401:db00:501c::/64") @@ -134,14 +139,6 @@ def test_confed_asn(self) -> None: commands = generate_global_commands(config) self.assertEqual(commands, ["config protocol bgp global confed-asn 65001"]) - def test_cluster_id(self) -> None: - """Cluster ID should generate correct command.""" - config = {"cluster_id": "192.168.1.1"} - commands = generate_global_commands(config) - self.assertEqual( - commands, ["config protocol bgp global cluster-id 192.168.1.1"] - ) - def test_networks6_with_all_fields(self) -> None: """networks6 should generate command with all fields.""" config = { @@ -162,14 +159,7 @@ def test_networks6_with_all_fields(self) -> None: def test_networks6_with_install_to_fib_true(self) -> None: """networks6 with install_to_fib=true should preserve value.""" - config = { - "networks6": [ - { - "prefix": "2001:db8::/32", - "install_to_fib": True, - } - ] - } + config = {"networks6": [{"prefix": "2001:db8::/32", "install_to_fib": True}]} commands = generate_global_commands(config) self.assertIn("install-to-fib true", commands[0]) @@ -368,7 +358,7 @@ def test_peer_tag(self) -> None: class GeneratePeerCommandsTest(unittest.TestCase): - """Tests for generate_peer_commands function.""" + """Tests for generate_peer_commands (neighbor grammar).""" def test_empty_peer_addr_returns_empty(self) -> None: """Peer without peer_addr should return empty list.""" @@ -380,36 +370,55 @@ def test_remote_asn(self) -> None: """remote_as_4_byte should generate correct command.""" peer = {"peer_addr": "2001:db8::1", "remote_as_4_byte": 65000} commands = generate_peer_commands(peer) - self.assertIn("config protocol bgp peer 2001:db8::1 remote-asn 65000", commands) + self.assertIn( + "config protocol bgp neighbor 2001:db8::1 remote-asn 65000", commands + ) + + def test_local_asn(self) -> None: + """local_as_4_byte should generate local-asn command.""" + peer = {"peer_addr": "2001:db8::1", "local_as_4_byte": 64512} + commands = generate_peer_commands(peer) + self.assertIn( + "config protocol bgp neighbor 2001:db8::1 local-asn 64512", commands + ) def test_peer_group_name(self) -> None: """peer_group_name should generate peer-group command.""" peer = {"peer_addr": "2001:db8::1", "peer_group_name": "MY-GROUP"} commands = generate_peer_commands(peer) self.assertIn( - "config protocol bgp peer 2001:db8::1 peer-group MY-GROUP", commands + "config protocol bgp neighbor 2001:db8::1 peer-group MY-GROUP", commands ) def test_local_addr(self) -> None: - """local_addr should generate local-addr command.""" + """local_addr should generate bind-addr address command.""" peer = {"peer_addr": "2001:db8::1", "local_addr": "2001:db8::a"} commands = generate_peer_commands(peer) self.assertIn( - "config protocol bgp peer 2001:db8::1 local-addr 2001:db8::a", commands + "config protocol bgp neighbor 2001:db8::1 bind-addr address 2001:db8::a", + commands, ) def test_is_passive(self) -> None: - """is_passive should generate passive command.""" + """is_passive should generate connect-mode command.""" peer = {"peer_addr": "2001:db8::1", "is_passive": True} commands = generate_peer_commands(peer) - self.assertIn("config protocol bgp peer 2001:db8::1 passive true", commands) + self.assertIn( + "config protocol bgp neighbor 2001:db8::1 connect-mode PASSIVE", commands + ) + peer["is_passive"] = False + commands = generate_peer_commands(peer) + self.assertIn( + "config protocol bgp neighbor 2001:db8::1 connect-mode ACTIVE", commands + ) def test_disable_ipv4_afi_false_preserved(self) -> None: """disable_ipv4_afi: false should NOT be omitted.""" peer = {"peer_addr": "2001:db8::1", "disable_ipv4_afi": False} commands = generate_peer_commands(peer) self.assertIn( - "config protocol bgp peer 2001:db8::1 disable-ipv4-afi false", commands + "config protocol bgp neighbor 2001:db8::1 afi disable-ipv4-afi false", + commands, ) def test_disable_ipv4_afi_true(self) -> None: @@ -417,58 +426,40 @@ def test_disable_ipv4_afi_true(self) -> None: peer = {"peer_addr": "2001:db8::1", "disable_ipv4_afi": True} commands = generate_peer_commands(peer) self.assertIn( - "config protocol bgp peer 2001:db8::1 disable-ipv4-afi true", commands + "config protocol bgp neighbor 2001:db8::1 afi disable-ipv4-afi true", + commands, ) - def test_next_hop4_and_next_hop6(self) -> None: - """next_hop4 and next_hop6 should generate correct commands.""" - peer = { - "peer_addr": "2001:db8::1", - "next_hop4": "0.0.0.0", - "next_hop6": "2001:db8::a", - } + def test_stateful_ha(self) -> None: + """enable_stateful_ha should generate graceful-restart stateful-ha.""" + peer = {"peer_addr": "2001:db8::1", "enable_stateful_ha": True} commands = generate_peer_commands(peer) self.assertIn( - "config protocol bgp peer 2001:db8::1 next-hop4 0.0.0.0", commands - ) - self.assertIn( - "config protocol bgp peer 2001:db8::1 next-hop6 2001:db8::a", commands + "config protocol bgp neighbor 2001:db8::1 graceful-restart stateful-ha true", + commands, ) - def test_link_bandwidth_string(self) -> None: - """link_bandwidth_bps as string should be used directly.""" - peer = {"peer_addr": "2001:db8::1", "link_bandwidth_bps": "10G"} + def test_add_path_both(self) -> None: + """add_path BOTH (3) should generate both directions.""" + peer = {"peer_addr": "2001:db8::1", "add_path": 3} commands = generate_peer_commands(peer) self.assertIn( - "config protocol bgp peer 2001:db8::1 link-bandwidth 10G", commands + "config protocol bgp neighbor 2001:db8::1 add-path receive true", commands ) - - def test_link_bandwidth_int(self) -> None: - """link_bandwidth_bps as int should be formatted.""" - peer = {"peer_addr": "2001:db8::1", "link_bandwidth_bps": 10_000_000_000} - commands = generate_peer_commands(peer) self.assertIn( - "config protocol bgp peer 2001:db8::1 link-bandwidth 10G", commands + "config protocol bgp neighbor 2001:db8::1 add-path send true", commands ) - def test_advertise_link_bandwidth(self) -> None: - """advertise_link_bandwidth should generate advertise-lbw command.""" - peer = {"peer_addr": "2001:db8::1", "advertise_link_bandwidth": 1} - commands = generate_peer_commands(peer) - self.assertIn("config protocol bgp peer 2001:db8::1 advertise-lbw 1", commands) - - def test_peer_id_and_type(self) -> None: - """peer_id and type should generate correct commands.""" - peer = { - "peer_addr": "2001:db8::1", - "peer_id": "test-peer:v6:1", - "type": "BGP_MONITOR", - } + def test_add_path_receive_only(self) -> None: + """add_path RECEIVE (1) should generate only the receive direction.""" + peer = {"peer_addr": "2001:db8::1", "add_path": 1} commands = generate_peer_commands(peer) self.assertIn( - "config protocol bgp peer 2001:db8::1 peer-id test-peer:v6:1", commands + "config protocol bgp neighbor 2001:db8::1 add-path receive true", commands + ) + self.assertNotIn( + "config protocol bgp neighbor 2001:db8::1 add-path send true", commands ) - self.assertIn("config protocol bgp peer 2001:db8::1 type BGP_MONITOR", commands) def test_timers(self) -> None: """Timer fields should generate correct commands.""" @@ -478,35 +469,101 @@ def test_timers(self) -> None: "hold_time_seconds": 120, "keep_alive_seconds": 40, "out_delay_seconds": 0, - "withdraw_unprog_delay_seconds": 0, + "graceful_restart_seconds": 60, }, } commands = generate_peer_commands(peer) - self.assertIn("config protocol bgp peer 2001:db8::1 hold-time 120", commands) self.assertIn( - "config protocol bgp peer 2001:db8::1 timers keepalive 40", commands + "config protocol bgp neighbor 2001:db8::1 timers hold-time 120", commands + ) + self.assertIn( + "config protocol bgp neighbor 2001:db8::1 timers keepalive 40", commands + ) + self.assertIn( + "config protocol bgp neighbor 2001:db8::1 timers out-delay 0", commands + ) + self.assertIn( + "config protocol bgp neighbor 2001:db8::1 graceful-restart restart-time 60", + commands, + ) + + def test_route_limits(self) -> None: + """pre_filter/post_filter should generate max-route commands.""" + peer = { + "peer_addr": "2001:db8::1", + "pre_filter": {"max_routes": 45000}, + "post_filter": {"max_routes": 50000, "warning_limit": 48000}, + } + commands = generate_peer_commands(peer) + self.assertIn( + "config protocol bgp neighbor 2001:db8::1 max-route pre-filter 45000", + commands, + ) + self.assertIn( + "config protocol bgp neighbor 2001:db8::1 max-route post-filter 50000", + commands, + ) + self.assertIn( + "config protocol bgp neighbor 2001:db8::1 max-route post-warning-threshold 48000", + commands, ) - def test_pre_filter(self) -> None: - """pre_filter fields should generate correct commands.""" + def test_parity_fields(self) -> None: + """Fields carried over from the old peer commands map to the new grammar.""" peer = { "peer_addr": "2001:db8::1", - "pre_filter": { - "max_routes": 45000, - "warning_limit": 0, - "warning_only": False, - }, + "next_hop4": "0.0.0.0", + "next_hop6": "2001:db8::a", + "next_hop_self": True, + "link_bandwidth_bps": "10G", + "advertise_link_bandwidth": 1, + "receive_link_bandwidth": 1, + "peer_id": "test-peer:v6:1", + "type": "BGP_MONITOR", + "bgp_peer_timers": {"withdraw_unprog_delay_seconds": 5}, + "pre_filter": {"warning_limit": 100, "warning_only": True}, + } + commands = generate_peer_commands(peer) + prefix = "config protocol bgp neighbor 2001:db8::1" + self.assertIn(f"{prefix} next-hop4 0.0.0.0", commands) + self.assertIn(f"{prefix} next-hop6 2001:db8::a", commands) + self.assertIn(f"{prefix} next-hop-self true", commands) + self.assertIn(f"{prefix} link-bandwidth 10G", commands) + self.assertIn(f"{prefix} advertise-lbw 1", commands) + self.assertIn(f"{prefix} receive-lbw 1", commands) + self.assertIn(f"{prefix} peer-id test-peer:v6:1", commands) + self.assertIn(f"{prefix} type BGP_MONITOR", commands) + self.assertIn(f"{prefix} timers withdraw-unprog-delay 5", commands) + self.assertIn(f"{prefix} max-route pre-warning-threshold 100", commands) + self.assertIn(f"{prefix} max-route pre-warning-only true", commands) + + def test_zero_values_preserved(self) -> None: + """Explicit zero timers/limits must not be dropped.""" + peer = { + "peer_addr": "2001:db8::1", + "bgp_peer_timers": {"hold_time_seconds": 0, "keep_alive_seconds": 0}, + "pre_filter": {"max_routes": 0}, } commands = generate_peer_commands(peer) - self.assertIn("config protocol bgp peer 2001:db8::1 max-routes 45000", commands) - self.assertIn("config protocol bgp peer 2001:db8::1 warning-limit 0", commands) + prefix = "config protocol bgp neighbor 2001:db8::1" + self.assertIn(f"{prefix} timers hold-time 0", commands) + self.assertIn(f"{prefix} timers keepalive 0", commands) + self.assertIn(f"{prefix} max-route pre-filter 0", commands) + + def test_add_path_symbolic(self) -> None: + """add_path serialized as an enum name must not crash the tool.""" + peer = {"peer_addr": "2001:db8::1", "add_path": "BOTH"} + commands = generate_peer_commands(peer) + self.assertIn( + "config protocol bgp neighbor 2001:db8::1 add-path receive true", commands + ) self.assertIn( - "config protocol bgp peer 2001:db8::1 warning-only false", commands + "config protocol bgp neighbor 2001:db8::1 add-path send true", commands ) class GenerateScriptCommandsTest(unittest.TestCase): - """Tests for generate_exec_commands and generate_stub_commands.""" + """Tests for generate_exec_commands.""" def test_exec_commands_contains_header(self) -> None: """Exec commands should contain shebang and header.""" @@ -524,12 +581,6 @@ def test_exec_commands_prepends_binary(self) -> None: "/path/to/fboss2 config protocol bgp global router-id 10.0.0.1", result ) - def test_stub_commands_contains_stub_env(self) -> None: - """Stub commands should set FBOSS_BGP_STUB_MODE.""" - commands = ["test command"] - result = generate_stub_commands(commands, "fboss2") - self.assertIn("export FBOSS_BGP_STUB_MODE=1", result) - class JsonToCliIntegrationTest(unittest.TestCase): """Integration tests for json_to_cli function.""" @@ -537,7 +588,7 @@ class JsonToCliIntegrationTest(unittest.TestCase): def test_minimal_config(self) -> None: """Minimal config with just router_id should work.""" config = {"router_id": "10.0.0.1"} - commands = json_to_cli(config, stub=False, binary="fboss2") + commands = json_to_cli(config, binary="fboss2") self.assertTrue(any("router-id 10.0.0.1" in cmd for cmd in commands)) def test_full_rsw_config(self) -> None: @@ -569,10 +620,7 @@ def test_full_rsw_config(self) -> None: "keep_alive_seconds": 10, }, }, - { - "name": "RSW-RTSW-V6", - "disable_ipv4_afi": True, - }, + {"name": "RSW-RTSW-V6", "disable_ipv4_afi": True}, ], "peers": [ { @@ -589,7 +637,7 @@ def test_full_rsw_config(self) -> None: ], } - commands = json_to_cli(config, stub=False, binary="fboss2") + commands = json_to_cli(config, binary="fboss2") joined = "\n".join(commands) # Verify global commands @@ -605,51 +653,38 @@ def test_full_rsw_config(self) -> None: self.assertIn("peer-group RSW-RTSW-V6 disable-ipv4-afi true", joined) # Verify peer commands - self.assertIn("peer 2401:db00:501c::/64 remote-asn 65000", joined) - self.assertIn("peer 2401:db00:501c::/64 disable-ipv4-afi false", joined) - self.assertIn("peer 2401:db00:e50e:1000:: peer-group RSW-FSW-V6", joined) - - def test_stub_mode(self) -> None: - """Stub mode should set FBOSS_BGP_STUB_MODE environment variable.""" - config = {"router_id": "10.0.0.1"} - commands = json_to_cli(config, stub=True, binary="fboss2") - self.assertIn("export FBOSS_BGP_STUB_MODE=1", commands) + self.assertIn("neighbor 2401:db00:501c::/64 remote-asn 65000", joined) + self.assertIn("neighbor 2401:db00:501c::/64 afi disable-ipv4-afi false", joined) + self.assertIn("neighbor 2401:db00:e50e:1000:: peer-group RSW-FSW-V6", joined) def test_empty_config(self) -> None: """Empty config should produce script with no commands.""" config = {} - commands = json_to_cli(config, stub=False, binary="fboss2") + commands = json_to_cli(config, binary="fboss2") # Should only have header and footer, no actual BGP commands self.assertTrue(any("#!/bin/bash" in cmd for cmd in commands)) def test_custom_binary(self) -> None: """Custom binary path should be used in commands.""" config = {"router_id": "10.0.0.1"} - commands = json_to_cli(config, stub=False, binary="/custom/path/fboss2") + commands = json_to_cli(config, binary="/custom/path/fboss2") self.assertTrue(any("/custom/path/fboss2" in cmd for cmd in commands)) def test_injection_in_router_id_neutralized(self) -> None: """Command injection in router_id should be single-quoted.""" config = {"router_id": "$(reboot)"} - commands = json_to_cli(config, stub=False, binary="fboss2") + commands = json_to_cli(config, binary="fboss2") joined = "\n".join(commands) self.assertIn("router-id '$(reboot)'", joined) self.assertNotIn("router-id $(reboot)", joined) def test_injection_in_peer_addr_neutralized(self) -> None: """Command injection in peer_addr should be single-quoted.""" - config = { - "peers": [ - { - "peer_addr": "$(malicious)", - "remote_as_4_byte": 65000, - } - ] - } - commands = json_to_cli(config, stub=False, binary="fboss2") + config = {"peers": [{"peer_addr": "$(malicious)", "remote_as_4_byte": 65000}]} + commands = json_to_cli(config, binary="fboss2") joined = "\n".join(commands) - self.assertIn("peer '$(malicious)'", joined) - self.assertNotIn("peer $(malicious)", joined) + self.assertIn("neighbor '$(malicious)'", joined) + self.assertNotIn("neighbor $(malicious)", joined) if __name__ == "__main__":