fboss/agent/state/SwitchState.cpp (381 lines of code) (raw):
/*
* 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/agent/state/SwitchState.h"
#include "fboss/agent/FbossError.h"
#include "fboss/agent/state/AclEntry.h"
#include "fboss/agent/state/AclMap.h"
#include "fboss/agent/state/AclTableGroupMap.h"
#include "fboss/agent/state/AggregatePort.h"
#include "fboss/agent/state/AggregatePortMap.h"
#include "fboss/agent/state/ControlPlane.h"
#include "fboss/agent/state/Interface.h"
#include "fboss/agent/state/InterfaceMap.h"
#include "fboss/agent/state/LabelForwardingInformationBase.h"
#include "fboss/agent/state/Port.h"
#include "fboss/agent/state/PortMap.h"
#include "fboss/agent/state/QosPolicyMap.h"
#include "fboss/agent/state/SflowCollectorMap.h"
#include "fboss/agent/state/SwitchSettings.h"
#include "fboss/agent/state/Transceiver.h"
#include "fboss/agent/state/TransceiverMap.h"
#include "fboss/agent/state/Vlan.h"
#include "fboss/agent/state/VlanMap.h"
#include "fboss/agent/state/NodeBase-defs.h"
using std::make_shared;
using std::shared_ptr;
using std::chrono::seconds;
namespace {
constexpr auto kInterfaces = "interfaces";
constexpr auto kPorts = "ports";
constexpr auto kVlans = "vlans";
constexpr auto kDefaultVlan = "defaultVlan";
constexpr auto kAcls = "acls";
constexpr auto kSflowCollectors = "sFlowCollectors";
constexpr auto kControlPlane = "controlPlane";
constexpr auto kQosPolicies = "qosPolicies";
constexpr auto kArpTimeout = "arpTimeout";
constexpr auto kNdpTimeout = "ndpTimeout";
constexpr auto kArpAgerInterval = "arpAgerInterval";
constexpr auto kMaxNeighborProbes = "maxNeighborProbes";
constexpr auto kStaleEntryInterval = "staleEntryInterval";
constexpr auto kLoadBalancers = "loadBalancers";
constexpr auto kMirrors = "mirrors";
constexpr auto kAggregatePorts = "aggregatePorts";
constexpr auto kLabelForwardingInformationBase = "labelFib";
constexpr auto kSwitchSettings = "switchSettings";
constexpr auto kDefaultDataplaneQosPolicy = "defaultDataPlaneQosPolicy";
constexpr auto kQcmCfg = "qcmConfig";
constexpr auto kBufferPoolCfgs = "bufferPoolConfigs";
constexpr auto kFibs = "fibs";
constexpr auto kTransceivers = "transceivers";
constexpr auto kAclTableGroups = "aclTableGroups";
} // namespace
// TODO: it might be worth splitting up limits for ecmp/ucmp
DEFINE_uint32(
ecmp_width,
64,
"Max ecmp width. Also implies ucmp normalization factor");
namespace facebook::fboss {
SwitchStateFields::SwitchStateFields()
: ports(make_shared<PortMap>()),
aggPorts(make_shared<AggregatePortMap>()),
vlans(make_shared<VlanMap>()),
interfaces(make_shared<InterfaceMap>()),
acls(make_shared<AclMap>()),
aclTableGroups(make_shared<AclTableGroupMap>()),
sFlowCollectors(make_shared<SflowCollectorMap>()),
qosPolicies(make_shared<QosPolicyMap>()),
controlPlane(make_shared<ControlPlane>()),
loadBalancers(make_shared<LoadBalancerMap>()),
mirrors(make_shared<MirrorMap>()),
fibs(make_shared<ForwardingInformationBaseMap>()),
labelFib(make_shared<LabelForwardingInformationBase>()),
switchSettings(make_shared<SwitchSettings>()),
transceivers(make_shared<TransceiverMap>()) {}
state::SwitchState SwitchStateFields::toThrift() const {
auto state = state::SwitchState();
state.portMap_ref() = ports->toThrift();
state.vlanMap_ref() = vlans->toThrift();
state.aclMap_ref() = acls->toThrift();
return state;
}
SwitchStateFields SwitchStateFields::fromThrift(
const state::SwitchState& state) {
auto fields = SwitchStateFields();
fields.ports = PortMap::fromThrift(state.get_portMap());
fields.vlans = VlanMap::fromThrift(state.get_vlanMap());
fields.acls = AclMap::fromThrift(state.get_aclMap());
return fields;
}
bool SwitchStateFields::operator==(const SwitchStateFields& other) const {
// TODO: add rest of fields as we convert them to thrifty
return std::tie(*ports, *vlans, *acls) ==
std::tie(*other.ports, *other.vlans, *other.acls);
}
folly::dynamic SwitchStateFields::toFollyDynamic() const {
folly::dynamic switchState = folly::dynamic::object;
switchState[kInterfaces] = interfaces->toFollyDynamic();
switchState[kPorts] = ports->toFollyDynamic();
switchState[kVlans] = vlans->toFollyDynamic();
switchState[kAcls] = acls->toFollyDynamic();
switchState[kSflowCollectors] = sFlowCollectors->toFollyDynamic();
switchState[kDefaultVlan] = static_cast<uint32_t>(defaultVlan);
switchState[kControlPlane] = controlPlane->toFollyDynamic();
switchState[kLoadBalancers] = loadBalancers->toFollyDynamic();
switchState[kMirrors] = mirrors->toFollyDynamic();
switchState[kAggregatePorts] = aggPorts->toFollyDynamic();
switchState[kLabelForwardingInformationBase] = labelFib->toFollyDynamic();
switchState[kSwitchSettings] = switchSettings->toFollyDynamic();
if (qcmCfg) {
switchState[kQcmCfg] = qcmCfg->toFollyDynamic();
}
if (bufferPoolCfgs) {
switchState[kBufferPoolCfgs] = bufferPoolCfgs->toFollyDynamic();
}
if (defaultDataPlaneQosPolicy) {
switchState[kDefaultDataplaneQosPolicy] =
defaultDataPlaneQosPolicy->toFollyDynamic();
}
switchState[kQosPolicies] = qosPolicies->toFollyDynamic();
switchState[kFibs] = fibs->toFollyDynamic();
switchState[kTransceivers] = transceivers->toFollyDynamic();
if (aclTableGroups) {
switchState[kAclTableGroups] = aclTableGroups->toFollyDynamic();
}
return switchState;
}
SwitchStateFields SwitchStateFields::fromFollyDynamic(
const folly::dynamic& swJson) {
SwitchStateFields switchState;
switchState.interfaces = InterfaceMap::fromFollyDynamic(swJson[kInterfaces]);
switchState.ports = PortMap::fromFollyDynamic(swJson[kPorts]);
switchState.vlans = VlanMap::fromFollyDynamic(swJson[kVlans]);
switchState.acls = AclMap::fromFollyDynamic(swJson[kAcls]);
if (swJson.count(kSflowCollectors) > 0) {
switchState.sFlowCollectors =
SflowCollectorMap::fromFollyDynamic(swJson[kSflowCollectors]);
}
switchState.defaultVlan = VlanID(swJson[kDefaultVlan].asInt());
if (swJson.find(kQosPolicies) != swJson.items().end()) {
switchState.qosPolicies =
QosPolicyMap::fromFollyDynamic(swJson[kQosPolicies]);
}
if (swJson.find(kControlPlane) != swJson.items().end()) {
switchState.controlPlane =
ControlPlane::fromFollyDynamic(swJson[kControlPlane]);
}
if (swJson.find(kLoadBalancers) != swJson.items().end()) {
switchState.loadBalancers =
LoadBalancerMap::fromFollyDynamic(swJson[kLoadBalancers]);
}
if (swJson.find(kMirrors) != swJson.items().end()) {
switchState.mirrors = MirrorMap::fromFollyDynamic(swJson[kMirrors]);
}
if (swJson.find(kAggregatePorts) != swJson.items().end()) {
switchState.aggPorts =
AggregatePortMap::fromFollyDynamic(swJson[kAggregatePorts]);
}
if (swJson.find(kLabelForwardingInformationBase) != swJson.items().end()) {
switchState.labelFib = LabelForwardingInformationBase::fromFollyDynamic(
swJson[kLabelForwardingInformationBase]);
}
if (swJson.find(kSwitchSettings) != swJson.items().end()) {
switchState.switchSettings =
SwitchSettings::fromFollyDynamic(swJson[kSwitchSettings]);
}
if (swJson.find(kDefaultDataplaneQosPolicy) != swJson.items().end()) {
switchState.defaultDataPlaneQosPolicy =
QosPolicy::fromFollyDynamic(swJson[kDefaultDataplaneQosPolicy]);
auto name = switchState.defaultDataPlaneQosPolicy->getName();
/* for backward compatibility, this policy is also kept in qos policy map.
* remove it, if it exists */
/* TODO(pshaikh): remove this after one pushes, after next push, logic
* that keeps default qos policy in qos policy map will be removed. */
switchState.qosPolicies->removeNodeIf(name);
}
if (swJson.find(kQcmCfg) != swJson.items().end()) {
switchState.qcmCfg = QcmCfg::fromFollyDynamic(swJson[kQcmCfg]);
}
if (swJson.find(kBufferPoolCfgs) != swJson.items().end()) {
switchState.bufferPoolCfgs =
BufferPoolCfgMap::fromFollyDynamic(swJson[kBufferPoolCfgs]);
}
if (swJson.find(kFibs) != swJson.items().end()) {
switchState.fibs =
ForwardingInformationBaseMap::fromFollyDynamic(swJson[kFibs]);
}
// TODO(joseph5wu) Will eventually make transceivers as a mandatory field
if (const auto& values = swJson.find(kTransceivers);
values != swJson.items().end()) {
switchState.transceivers = TransceiverMap::fromFollyDynamic(values->second);
}
if (swJson.find(kAclTableGroups) != swJson.items().end()) {
switchState.aclTableGroups =
AclTableGroupMap::fromFollyDynamic(swJson[kAclTableGroups]);
}
// TODO verify that created state here is internally consistent t4155406
return switchState;
}
SwitchState::SwitchState() {}
SwitchState::~SwitchState() {}
void SwitchState::modify(std::shared_ptr<SwitchState>* state) {
if (!(*state)->isPublished()) {
return;
}
*state = (*state)->clone();
}
std::shared_ptr<Port> SwitchState::getPort(PortID id) const {
return getFields()->ports->getPort(id);
}
void SwitchState::registerPort(PortID id, const std::string& name) {
writableFields()->ports->registerPort(id, name);
}
void SwitchState::addPort(const std::shared_ptr<Port>& port) {
writableFields()->ports->addPort(port);
}
void SwitchState::resetPorts(std::shared_ptr<PortMap> ports) {
writableFields()->ports.swap(ports);
}
void SwitchState::resetVlans(std::shared_ptr<VlanMap> vlans) {
writableFields()->vlans.swap(vlans);
}
void SwitchState::addVlan(const std::shared_ptr<Vlan>& vlan) {
auto* fields = writableFields();
// For ease-of-use, automatically clone the VlanMap if we are still
// pointing to a published map.
if (fields->vlans->isPublished()) {
fields->vlans = fields->vlans->clone();
}
fields->vlans->addVlan(vlan);
}
void SwitchState::setDefaultVlan(VlanID id) {
writableFields()->defaultVlan = id;
}
void SwitchState::setArpTimeout(seconds timeout) {
writableFields()->arpTimeout = timeout;
}
void SwitchState::setNdpTimeout(seconds timeout) {
writableFields()->ndpTimeout = timeout;
}
void SwitchState::setArpAgerInterval(seconds interval) {
writableFields()->arpAgerInterval = interval;
}
void SwitchState::setMaxNeighborProbes(uint32_t maxNeighborProbes) {
writableFields()->maxNeighborProbes = maxNeighborProbes;
}
void SwitchState::setStaleEntryInterval(seconds interval) {
writableFields()->staleEntryInterval = interval;
}
void SwitchState::addIntf(const std::shared_ptr<Interface>& intf) {
auto* fields = writableFields();
// For ease-of-use, automatically clone the InterfaceMap if we are still
// pointing to a published map.
if (fields->interfaces->isPublished()) {
fields->interfaces = fields->interfaces->clone();
}
fields->interfaces->addInterface(intf);
}
void SwitchState::resetIntfs(std::shared_ptr<InterfaceMap> intfs) {
writableFields()->interfaces.swap(intfs);
}
void SwitchState::addAcl(const std::shared_ptr<AclEntry>& acl) {
auto* fields = writableFields();
// For ease-of-use, automatically clone the AclMap if we are still
// pointing to a published map.
if (fields->acls->isPublished()) {
fields->acls = fields->acls->clone();
}
fields->acls->addEntry(acl);
}
std::shared_ptr<AclEntry> SwitchState::getAcl(const std::string& name) const {
return getFields()->acls->getEntryIf(name);
}
void SwitchState::resetAcls(std::shared_ptr<AclMap> acls) {
writableFields()->acls.swap(acls);
}
void SwitchState::resetAclTableGroups(
std::shared_ptr<AclTableGroupMap> aclTableGroups) {
writableFields()->aclTableGroups.swap(aclTableGroups);
}
void SwitchState::resetAggregatePorts(
std::shared_ptr<AggregatePortMap> aggPorts) {
writableFields()->aggPorts.swap(aggPorts);
}
void SwitchState::resetSflowCollectors(
const std::shared_ptr<SflowCollectorMap>& collectors) {
writableFields()->sFlowCollectors = collectors;
}
void SwitchState::resetQosPolicies(std::shared_ptr<QosPolicyMap> qosPolicies) {
writableFields()->qosPolicies = qosPolicies;
}
void SwitchState::resetControlPlane(
std::shared_ptr<ControlPlane> controlPlane) {
writableFields()->controlPlane = controlPlane;
}
void SwitchState::resetLoadBalancers(
std::shared_ptr<LoadBalancerMap> loadBalancers) {
writableFields()->loadBalancers.swap(loadBalancers);
}
void SwitchState::resetSwitchSettings(
std::shared_ptr<SwitchSettings> switchSettings) {
writableFields()->switchSettings = switchSettings;
}
void SwitchState::resetQcmCfg(std::shared_ptr<QcmCfg> qcmCfg) {
writableFields()->qcmCfg = qcmCfg;
}
void SwitchState::resetBufferPoolCfgs(std::shared_ptr<BufferPoolCfgMap> cfgs) {
writableFields()->bufferPoolCfgs = cfgs;
}
const std::shared_ptr<LoadBalancerMap>& SwitchState::getLoadBalancers() const {
return getFields()->loadBalancers;
}
void SwitchState::resetMirrors(std::shared_ptr<MirrorMap> mirrors) {
writableFields()->mirrors.swap(mirrors);
}
const std::shared_ptr<MirrorMap>& SwitchState::getMirrors() const {
return getFields()->mirrors;
}
const std::shared_ptr<ForwardingInformationBaseMap>& SwitchState::getFibs()
const {
return getFields()->fibs;
}
void SwitchState::resetLabelForwardingInformationBase(
std::shared_ptr<LabelForwardingInformationBase> labelFib) {
writableFields()->labelFib.swap(labelFib);
}
void SwitchState::resetForwardingInformationBases(
std::shared_ptr<ForwardingInformationBaseMap> fibs) {
writableFields()->fibs.swap(fibs);
}
void SwitchState::addTransceiver(
const std::shared_ptr<Transceiver>& transceiver) {
auto* fields = writableFields();
// For ease-of-use, automatically clone the TransceiverMap if we are still
// pointing to a published map.
if (fields->transceivers->isPublished()) {
fields->transceivers = fields->transceivers->clone();
}
fields->transceivers->addTransceiver(transceiver);
}
void SwitchState::resetTransceivers(
std::shared_ptr<TransceiverMap> transceivers) {
writableFields()->transceivers.swap(transceivers);
}
std::shared_ptr<AclTableMap> SwitchState::getAclTablesForStage(
cfg::AclStage aclStage) const {
if (getAclTableGroups() &&
getAclTableGroups()->getAclTableGroupIf(aclStage) &&
getAclTableGroups()->getAclTableGroup(aclStage)->getAclTableMap()) {
return getAclTableGroups()->getAclTableGroup(aclStage)->getAclTableMap();
}
return nullptr;
}
std::shared_ptr<AclMap> SwitchState::getAclsForTable(
cfg::AclStage aclStage,
const std::string& tableName) const {
auto aclTableMap = getAclTablesForStage(aclStage);
if (aclTableMap && aclTableMap->getTableIf(tableName)) {
return aclTableMap->getTable(tableName)->getAclMap();
}
return nullptr;
}
std::shared_ptr<SwitchState> SwitchState::modifyTransceivers(
const std::shared_ptr<SwitchState>& state,
const std::unordered_map<TransceiverID, TransceiverInfo>& currentTcvrs) {
auto origTcvrs = state->getTransceivers();
TransceiverMap::NodeContainer newTcvrs;
bool changed = false;
for (const auto& tcvrInfo : currentTcvrs) {
auto origTcvr = origTcvrs->getTransceiverIf(tcvrInfo.first);
auto newTcvr = Transceiver::createPresentTransceiver(tcvrInfo.second);
if (!newTcvr) {
// If the transceiver used to be present but now was removed
changed |= (origTcvr != nullptr);
continue;
} else {
if (origTcvr && *origTcvr == *newTcvr) {
newTcvrs.emplace(origTcvr->getID(), origTcvr);
} else {
changed = true;
newTcvrs.emplace(newTcvr->getID(), newTcvr);
}
}
}
if (changed) {
XLOG(DBG2) << "New TransceiverMap has " << newTcvrs.size()
<< " present transceivers, original map has "
<< origTcvrs->size();
auto newState = state->clone();
newState->resetTransceivers(origTcvrs->clone(newTcvrs));
return newState;
} else {
XLOG(DBG2)
<< "Current transceivers from QsfpCache has the same transceiver size:"
<< origTcvrs->size()
<< ", no need to reset TransceiverMap in current SwitchState";
return nullptr;
}
}
template class NodeBaseT<SwitchState, SwitchStateFields>;
} // namespace facebook::fboss