diff --git a/src/DbInterface.cpp b/src/DbInterface.cpp index 8681edff..cd6ea0ba 100644 --- a/src/DbInterface.cpp +++ b/src/DbInterface.cpp @@ -96,6 +96,25 @@ void DbInterface::setMuxState(const std::string &portName, mux_state::MuxState:: ))); } +// +// ---> setPeerMuxState(const std::string &portName, mux_state::MuxState::Label label); +// +// set MUX state in APP DB for orchagent processing +// +void DbInterface::setPeerMuxState(const std::string &portName, mux_state::MuxState::Label label) +{ + MUXLOGDEBUG(boost::format("%s: setting peer mux to %s") % portName % mMuxState[label]); + + boost::asio::io_service &ioService = mStrand.context(); + ioService.post(mStrand.wrap(boost::bind( + &DbInterface::handleSetPeerMuxState, + this, + portName, + label + ))); +} + + // // ---> probeMuxState(const std::string &portName) // @@ -235,6 +254,9 @@ void DbInterface::initialize() mAppDbMuxCommandTablePtr = std::make_shared ( mAppDbPtr.get(), APP_MUX_CABLE_COMMAND_TABLE_NAME ); + mAppDbPeerMuxCommandTablePtr = std::make_shared ( + mAppDbPtr.get(), PEER_FORWARDING_STATE_COMMAND_TABLE + ); mStateDbMuxLinkmgrTablePtr = std::make_shared ( mStateDbPtr.get(), STATE_MUX_LINKMGR_TABLE_NAME ); @@ -317,6 +339,23 @@ void DbInterface::handleSetMuxState(const std::string portName, mux_state::MuxSt } } +// +// ---> handleSetPeerMuxState(const std::string portName, mux_state::MuxState::Label label); +// +// set MUX state in APP DB for orchagent processing +// +void DbInterface::handleSetPeerMuxState(const std::string portName, mux_state::MuxState::Label label) +{ + MUXLOGDEBUG(boost::format("%s: setting peer mux state to %s") % portName % mMuxState[label]); + + if (label <= mux_state::MuxState::Label::Unknown) { + std::vector values = { + {"state", mMuxState[label]}, + }; + mAppDbPeerMuxCommandTablePtr->set(portName, values); + } +} + // // ---> handleProbeMuxState(const std::string portName) // @@ -625,6 +664,64 @@ void DbInterface::getPortCableType(std::shared_ptr configDbCo processPortCableType(entries); } +// +// ---> processSoCIpAddress(std::vector &entries); +// +// process SoC addresses and build a map of port name to SoC address +// +void DbInterface::processSoCIpAddress(std::vector &entries) +{ + for (auto &entry: entries) { + std::string portName = kfvKey(entry); + std::string operation = kfvOp(entry); + std::vector fieldValues = kfvFieldsValues(entry); + + std::vector::const_iterator cit = std::find_if( + fieldValues.cbegin(), + fieldValues.cend(), + [] (const swss::FieldValueTuple &fv) {return fvField(fv) == "soc_ipv4";} + ); + if (cit != fieldValues.cend()) { + const std::string f = cit->first; + std::string SoCIpAddress = cit->second; + + MUXLOGDEBUG(boost::format("port: %s, %s = %s") % portName % f % SoCIpAddress); + + size_t pos = SoCIpAddress.find("/"); + if (pos != std::string::npos) { + SoCIpAddress.erase(pos); + } + + boost::system::error_code errorCode; + boost::asio::ip::address ipAddress = boost::asio::ip::make_address(SoCIpAddress, errorCode); + if (!errorCode) { + mMuxManagerPtr->addOrUpdateMuxPortSoCAddress(portName, ipAddress); + } else { + MUXLOGFATAL(boost::format("%s: Received invalid SoC IP: %s, error code: %d") % + portName % + SoCIpAddress % + errorCode + ); + } + } + } +} + +// +// ---> getSoCIpAddress(std::shared_ptr configDbConnector); +// +// retrieve SoC IP address for port in active-active cable type +// +void DbInterface::getSoCIpAddress(std::shared_ptr configDbConnector) +{ + MUXLOGINFO("Reading SoC IP addresses"); + swss::Table configDbMuxCableTable(configDbConnector.get(), CFG_MUX_CABLE_TABLE_NAME); + std::vector entries; + + configDbMuxCableTable.getContent(entries); + processSoCIpAddress(entries); +} + // // ---> processMuxPortConfigNotifiction(std::deque &entries); // @@ -902,6 +999,51 @@ void DbInterface::handleMuxResponseNotifiction(swss::SubscriberStateTable &appdb processMuxResponseNotifiction(entries); } +// +// ---> processPeerMuxResponseNotification(std::deque &entries); +// +// process peer MUX response (from xcvrd) notification +// +void DbInterface::processPeerMuxResponseNotification(std::deque &entries) +{ + for (auto &entry: entries) { + std::string port = kfvKey(entry); + std::string oprtation = kfvOp(entry); + std::vector fieldValues = kfvFieldsValues(entry); + + std::vector::const_iterator cit = std::find_if( + fieldValues.cbegin(), + fieldValues.cend(), + [] (const swss::FieldValueTuple &fv) {return fvField(fv) == "state";} + ); + if (cit != fieldValues.cend()) { + const std::string f = cit->first; + const std::string v = cit->second; + + MUXLOGDEBUG(boost::format("port: %s, operation: %s, f: %s, v: %s") % + port % + oprtation % + f % + v + ); + mMuxManagerPtr->processPeerMuxState(port, v); + } + } +} + +// +// ---> handlePeerMuxResponseNotification(swss::SubscriberStateTable &stateDbPeerMuxResponseTable); +// +// handles peer MUX response (from xcvrd) notification +// +void DbInterface::handlePeerMuxResponseNotification(swss::SubscriberStateTable &stateDbPeerMuxResponseTable) +{ + std::deque entries; + + stateDbPeerMuxResponseTable.pops(entries); + processPeerMuxResponseNotification(entries); +} + // // ---> processMuxStateNotifiction(std::deque &entries); // @@ -1026,11 +1168,14 @@ void DbInterface::handleSwssNotification() swss::SubscriberStateTable stateDbRouteTable(stateDbPtr.get(), STATE_ROUTE_TABLE_NAME); // for getting peer's link status swss::SubscriberStateTable stateDbMuxInfoTable(stateDbPtr.get(), MUX_CABLE_INFO_TABLE); + // for getting peer's admin forwarding state + swss::SubscriberStateTable stateDbPeerMuxResponseTable(stateDbPtr.get(), PEER_FORWARDING_STATE_RESPONSE_TABLE); getTorMacAddress(configDbPtr); getLoopback2InterfaceInfo(configDbPtr); getPortCableType(configDbPtr); getServerIpAddress(configDbPtr); + getSoCIpAddress(configDbPtr); NetMsgInterface netMsgInterface(*this); swss::NetDispatcher::getInstance().registerMessageHandler(RTM_NEWNEIGH, &netMsgInterface); @@ -1048,6 +1193,7 @@ void DbInterface::handleSwssNotification() swssSelect.addSelectable(&stateDbPortTable); swssSelect.addSelectable(&stateDbRouteTable); swssSelect.addSelectable(&stateDbMuxInfoTable); + swssSelect.addSelectable(&stateDbPeerMuxResponseTable); swssSelect.addSelectable(&netlinkNeighbor); while (mPollSwssNotifcation) { @@ -1078,6 +1224,8 @@ void DbInterface::handleSwssNotification() handleDefaultRouteStateNotification(stateDbRouteTable); } else if (selectable == static_cast (&stateDbMuxInfoTable)) { handlePeerLinkStateNotification(stateDbMuxInfoTable); + } else if (selectable == static_cast (&stateDbPeerMuxResponseTable)) { + handlePeerMuxResponseNotification(stateDbPeerMuxResponseTable); } else if (selectable == static_cast (&netlinkNeighbor)) { continue; } else { diff --git a/src/DbInterface.h b/src/DbInterface.h index 5ba97c7c..0e54fa32 100644 --- a/src/DbInterface.h +++ b/src/DbInterface.h @@ -45,7 +45,9 @@ class MuxManagerTest; namespace mux { #define MUX_CABLE_INFO_TABLE "MUX_CABLE_INFO" -#define LINK_PROBE_STATS_TABLE_NAME "LINK_PROBE_STATS" +#define LINK_PROBE_STATS_TABLE_NAME "LINK_PROBE_STATS" +#define PEER_FORWARDING_STATE_COMMAND_TABLE "HW_FORWARDING_STATE_PEER" +#define PEER_FORWARDING_STATE_RESPONSE_TABLE "HW_MUX_CABLE_TABLE_PEER" class MuxManager; using ServerIpPortMap = std::map; @@ -133,6 +135,18 @@ class DbInterface */ virtual void setMuxState(const std::string &portName, mux_state::MuxState::Label label); + /** + *@method setPeerMuxState + * + *@brief set peer MUX state in APP DB for orchagent processing + * + *@param portName (in) MUX/port name + *@param label (in) label of target state + * + *@return none + */ + virtual void setPeerMuxState(const std::string &portName, mux_state::MuxState::Label label); + /** *@method probeMuxState * @@ -271,6 +285,18 @@ class DbInterface */ void handleSetMuxState(const std::string portName, mux_state::MuxState::Label label); + /** + *@method handleSetPeerMuxState + * + *@brief set peer MUX state in APP DB for orchagent processing + * + *@param portName (in) MUX/port name + *@param label (in) label of target state + * + *@return none + */ + void handleSetPeerMuxState(const std::string portName, mux_state::MuxState::Label label); + /** *@method handleProbeMuxState * @@ -435,6 +461,28 @@ class DbInterface */ void getPortCableType(std::shared_ptr configDbConnector); + /** + *@method processSoCIpAddress + * + *@brief process SoC IP address and builds a map of IP to port name + * + *@param entries config_db MUX_CABLE entries + * + *@return none + */ + inline void processSoCIpAddress(std::vector &entries); + + /** + *@method getSoCIpAddress + * + *@brief retrieve SoC IP address and builds a map of IP to port name + * + *@param configDbConnector config db connector + * + *@return none + */ + void getSoCIpAddress(std::shared_ptr configDbConnector); + /** *@method processMuxPortConfigNotifiction * @@ -545,6 +593,28 @@ class DbInterface */ void handleMuxResponseNotifiction(swss::SubscriberStateTable &appdbPortTable); + /** + *@method processPeerMuxResponseNotification + * + *@brief process peer MUX response (from xcvrd) notification + * + *@param entries (in) reference to app db peer mux response table entries + * + *@return none + */ + inline void processPeerMuxResponseNotification(std::deque &entries); + + /** + *@method handlePeerMuxResponseNotification + * + *@brief handles peer MUX response (from xcvrd) notification + * + *@param appdbPortTable (in) reference to app db peer mux response table + * + *@return none + */ + void handlePeerMuxResponseNotification(swss::SubscriberStateTable &stateDbPeerMuxResponseTable); + /** *@method processMuxStateNotifiction * @@ -616,6 +686,8 @@ class DbInterface std::shared_ptr mAppDbMuxTablePtr; // for communicating with the driver (probing the mux) std::shared_ptr mAppDbMuxCommandTablePtr; + // for communicating with xcvrd to set peer mux state + std::shared_ptr mAppDbPeerMuxCommandTablePtr; // for writing the current mux linkmgr health std::shared_ptr mStateDbMuxLinkmgrTablePtr; // for writing mux metrics diff --git a/src/MuxManager.cpp b/src/MuxManager.cpp index 3662bfce..23cce5d9 100644 --- a/src/MuxManager.cpp +++ b/src/MuxManager.cpp @@ -139,9 +139,36 @@ void MuxManager::addOrUpdateMuxPort(const std::string &portName, boost::asio::ip MUXLOGWARNING(boost::format("%s: server IP: %s") % portName % address); std::shared_ptr muxPortPtr = getMuxPortPtrOrThrow(portName); + common::MuxPortConfig::PortCableType portCableType = getMuxPortCableType(portName); if (address.is_v4()) { - muxPortPtr->handleBladeIpv4AddressUpdate(address); + if (portCableType == common::MuxPortConfig::PortCableType::ActiveStandby) { + // notify server IP address for ports in active-standby cable type + muxPortPtr->handleBladeIpv4AddressUpdate(address); + } + + } else if (address.is_v6()) { + // handle IPv6 probing + } +} + +// +// ---> addOrUpdateMuxPortSoCAddress(const std::string &portName, boost::asio::ip::address address); +// +// update MUX port SoC IPv4 Address. If port is not found, create new MuxPort object +// +void MuxManager::addOrUpdateMuxPortSoCAddress(const std::string &portName, boost::asio::ip::address address) +{ + MUXLOGWARNING(boost::format("%s: SoC IP: %s") % portName % address); + + std::shared_ptr muxPortPtr = getMuxPortPtrOrThrow(portName); + common::MuxPortConfig::PortCableType portCableType = getMuxPortCableType(portName); + + if (address.is_v4()) { + if (portCableType == common::MuxPortConfig::PortCableType::ActiveActive) { + // notify NiC IP address for ports in active-active cable type + muxPortPtr->handleSoCIpv4AddressUpdate(address); + } } else if (address.is_v6()) { // handle IPv6 probing } @@ -174,6 +201,8 @@ void MuxManager::updatePortCableType(const std::string &portName, const std::str common::MuxPortConfig::PortCableType portCableType; if (cableType == "active-standby") { portCableType = common::MuxPortConfig::PortCableType::ActiveStandby; + } else if (cableType == "active-active") { + portCableType = common::MuxPortConfig::PortCableType::ActiveActive; } else { MUXLOGERROR( boost::format( @@ -287,6 +316,21 @@ void MuxManager::processProbeMuxState(const std::string &portName, const std::st } } +// +// ---> processPeerMuxState(const std::string &portName, const std::string &peerMuxState); +// +// update peer MUX port state db notification +// +void MuxManager::processPeerMuxState(const std::string &portName, const std::string &peerMuxState) +{ + MUXLOGINFO(boost::format("%s: state db peer mux state: %s") % portName % peerMuxState); + + PortMapIterator portMapIterator = mPortMap.find(portName); + if (portMapIterator != mPortMap.end()) { + portMapIterator->second->handlePeerMuxState(peerMuxState); + } +} + // // ---> addOrUpdateDefaultRouteState(boost::asio::ip::address& address, const std::string &routeState); // @@ -351,6 +395,11 @@ std::shared_ptr MuxManager::getMuxPortPtrOrThrow(const std::string &por mIoService, muxPortCableType ); + if (muxPortCableType == common::MuxPortConfig::PortCableType::ActiveActive) { + std::array address; + generateServerMac(serverId, address); + muxPortPtr->setServerMacAddress(address); + } mPortMap.insert({portName, muxPortPtr}); } else { @@ -401,4 +450,23 @@ void MuxManager::handleProcessTerminate() mDbInterfacePtr->getBarrier().wait(); } +// +// ---> generateServerMac(); +// +// generate known MAC address based on server ID +// +void MuxManager::generateServerMac(uint16_t serverId, std::array &address) +{ + if (serverId >= KNOWN_MAC_COUNT) { + throw std::range_error("Out of MAC address range"); + } + int addrIndex = ETHER_ADDR_LEN - 1; + uint32_t offset = KNOWN_MAC_START[addrIndex] + serverId; + address = KNOWN_MAC_START; + while (offset && addrIndex >= 0) { + address[addrIndex--] = offset % 0xff; + offset /= 0xff; + } +} + } /* namespace mux */ diff --git a/src/MuxManager.h b/src/MuxManager.h index 864f8c4d..a65cbb2f 100644 --- a/src/MuxManager.h +++ b/src/MuxManager.h @@ -47,6 +47,9 @@ using PortMapIterator = PortMap::iterator; using PortCableTypeMap = std::map; using PortCableTypeMapIterator = PortCableTypeMap::iterator; +const std::array KNOWN_MAC_START = {0x04, 0x27, 0x28, 0x7a, 0x00, 0x00}; +const size_t KNOWN_MAC_COUNT = 1024; + /** *@class MuxManager * @@ -239,6 +242,18 @@ class MuxManager */ void addOrUpdateMuxPort(const std::string &portName, boost::asio::ip::address address); + /** + *@method addOrUpdateMuxPortSoCAddress + * + *@brief update MUX port SoC IPv4 Address. If port is not found, create new MuxPort object + * + *@param portName (in) Mux port name + *@param address (in) SoC IP address + * + *@return none + */ + void addOrUpdateMuxPortSoCAddress(const std::string &portName, boost::asio::ip::address address); + /** *@method updateMuxPortConfig * @@ -346,6 +361,18 @@ class MuxManager */ void processProbeMuxState(const std::string &portName, const std::string &muxState); + /** + *@method processPeerMuxState + * + *@brief update peer MUX port state db notification + * + *@param portName (in) Mux port name + *@param peerMuxState (in) Peer mux port state + * + *@return none + */ + void processPeerMuxState(const std::string &portName, const std::string &peerMuxState); + /** * @method addOrUpdateDefaultRouteState * @@ -403,6 +430,15 @@ class MuxManager */ void handleProcessTerminate(); + /** + *@method generateServerMac + * + *@brief generate Server MAC for port in active-active cable type + * + *@return none + */ + void generateServerMac(uint16_t serverId, std::array &address); + private: friend class test::MuxManagerTest; /** diff --git a/src/MuxPort.cpp b/src/MuxPort.cpp index 7c15f77f..d8130434 100644 --- a/src/MuxPort.cpp +++ b/src/MuxPort.cpp @@ -71,12 +71,23 @@ MuxPort::MuxPort( mStrand(ioService) { assert(dbInterfacePtr != nullptr); - if (portCableType == common::MuxPortConfig::PortCableType::ActiveStandby) { - mLinkManagerStateMachinePtr = std::make_shared( - this, - mStrand, - mMuxPortConfig - ); + switch (portCableType) { + case common::MuxPortConfig::PortCableType::ActiveActive: + mLinkManagerStateMachinePtr = std::make_shared( + this, + mStrand, + mMuxPortConfig + ); + break; + case common::MuxPortConfig::PortCableType::ActiveStandby: + mLinkManagerStateMachinePtr = std::make_shared( + this, + mStrand, + mMuxPortConfig + ); + break; + default: + break; } assert(mLinkManagerStateMachinePtr.get() != nullptr); } @@ -93,6 +104,23 @@ void MuxPort::handleBladeIpv4AddressUpdate(boost::asio::ip::address address) ))); } +// +// ---> handleSoCIpv4AddressUpdate(boost::asio::ip::address address); +// +// handles SoC IP address update for port in active-active cable type +// +void MuxPort::handleSoCIpv4AddressUpdate(boost::asio::ip::address address) +{ + MUXLOGDEBUG(boost::format("port: %s") % mMuxPortConfig.getPortName()); + + boost::asio::io_service &ioService = mStrand.context(); + ioService.post(mStrand.wrap(boost::bind( + &link_manager::LinkManagerStateMachineBase::handleSwssSoCIpv4AddressUpdate, + mLinkManagerStateMachinePtr.get(), + address + ))); +} + // // ---> handleLinkState(const std::string &linkState); // @@ -256,6 +284,32 @@ void MuxPort::handleMuxConfig(const std::string &config) ))); } +// +// ---> handlePeerMuxState(const std::string &peerMuxState); +// +// handles peer MUX state updates +// +void MuxPort::handlePeerMuxState(const std::string &peerMuxState) +{ + MUXLOGDEBUG(boost::format("port: %s, state db peer mux state: %s") % mMuxPortConfig.getPortName() % peerMuxState); + + mux_state::MuxState::Label label = mux_state::MuxState::Label::Unknown; + if (peerMuxState == "active") { + label = mux_state::MuxState::Label::Active; + } else if (peerMuxState == "standby") { + label = mux_state::MuxState::Label::Standby; + } else if (peerMuxState == "error") { + label = mux_state::MuxState::Label::Error; + } + + boost::asio::io_service &ioService = mStrand.context(); + ioService.post(mStrand.wrap(boost::bind( + &link_manager::LinkManagerStateMachineBase::handlePeerMuxStateNotification, + mLinkManagerStateMachinePtr.get(), + label + ))); +} + // // ---> handleDefaultRouteState(const std::string &routeState); // diff --git a/src/MuxPort.h b/src/MuxPort.h index 1bf7292f..e419ac20 100644 --- a/src/MuxPort.h +++ b/src/MuxPort.h @@ -29,6 +29,7 @@ #include "link_prober/LinkProber.h" #include "link_prober/LinkProberStateMachineBase.h" +#include "link_manager/LinkManagerStateMachineActiveActive.h" #include "link_manager/LinkManagerStateMachineActiveStandby.h" #include "common/MuxPortConfig.h" @@ -113,6 +114,17 @@ class MuxPort: public std::enable_shared_from_this */ inline void setMuxState(mux_state::MuxState::Label label) {mDbInterfacePtr->setMuxState(mMuxPortConfig.getPortName(), label);}; + /** + *@method setPeerMuxState + * + *@brief set peer MUX state in APP DB for orchagent processing + * + *@param label (in) label of target state + * + *@return none + */ + inline void setPeerMuxState(mux_state::MuxState::Label label) { mDbInterfacePtr->setPeerMuxState(mMuxPortConfig.getPortName(), label); }; + /** *@method getMuxState * @@ -203,6 +215,17 @@ class MuxPort: public std::enable_shared_from_this */ inline void setServerIpv4Address(const boost::asio::ip::address &address) {mMuxPortConfig.setBladeIpv4Address(address);}; + /** + *@method setServerMacAddress + * + *@brief setter for server MAC address + * + *@param address (in) server MAC address + * + *@return none + */ + inline void setServerMacAddress(const std::array &address) {mMuxPortConfig.setBladeMacAddress(address);}; + /** *@method handleBladeIpv4AddressUpdate * @@ -212,7 +235,18 @@ class MuxPort: public std::enable_shared_from_this * *@return none */ - void handleBladeIpv4AddressUpdate(boost::asio::ip::address addres); + void handleBladeIpv4AddressUpdate(boost::asio::ip::address addres); + + /** + *@method handleSoCIpv4AddressUpdate + * + *@brief update SoC IPv4 Address + * + *@param addres (in) server/blade IP address + * + *@return none + */ + void handleSoCIpv4AddressUpdate(boost::asio::ip::address addres); /** *@method handleLinkState @@ -291,6 +325,18 @@ class MuxPort: public std::enable_shared_from_this */ void handleMuxConfig(const std::string &config); + /** + *@method handlePeerMuxState + * + *@brief handles peer MUX state updates + * + *@param peerMuxState (in) peer MUX state + * + *@return none + */ + void handlePeerMuxState(const std::string &peerMuxState); + + /** * @method handleDefaultRouteState(const std::string &routeState) * diff --git a/src/common/StateMachine.h b/src/common/StateMachine.h index d1f0039d..43de28c9 100644 --- a/src/common/StateMachine.h +++ b/src/common/StateMachine.h @@ -33,6 +33,7 @@ namespace link_manager { class LinkManagerStateMachineBase; class ActiveStandbyStateMachine; +class ActiveActiveStateMachine; } namespace link_prober { @@ -111,9 +112,19 @@ class StateMachine */ boost::asio::io_service::strand& getStrand() {return mStrand;}; + /** + *@method getMuxPortConfig + * + *@brief getter MuxPortConfig object + * + *@return reference to MuxPortConfig object + */ + const MuxPortConfig& getMuxPortConfig() const {return mMuxPortConfig;}; + private: friend class link_manager::LinkManagerStateMachineBase; friend class link_manager::ActiveStandbyStateMachine; + friend class link_manager::ActiveActiveStateMachine; friend class link_prober::LinkProberStateMachineActiveStandby; friend class link_prober::LinkProberStateMachineBase; friend class link_prober::LinkProberStateMachineActiveActive; @@ -141,15 +152,6 @@ class StateMachine */ State* getCurrentState() {return mCurrentState;}; - /** - *@method getMuxPortConfig - * - *@brief getter MuxPortConfig object - * - *@return reference to MuxPortConfig object - */ - const MuxPortConfig& getMuxPortConfig() const {return mMuxPortConfig;}; - private: boost::asio::io_service::strand mStrand; State *mCurrentState = nullptr; diff --git a/src/link_manager/LinkManagerStateMachineActiveActive.cpp b/src/link_manager/LinkManagerStateMachineActiveActive.cpp new file mode 100644 index 00000000..dfcded86 --- /dev/null +++ b/src/link_manager/LinkManagerStateMachineActiveActive.cpp @@ -0,0 +1,934 @@ +/* + * Copyright 2021 (c) Microsoft Corporation. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include + +#include "link_manager/LinkManagerStateMachineActiveActive.h" +#include "common/MuxLogger.h" +#include "common/MuxException.h" +#include "MuxPort.h" + +namespace link_manager +{ + +constexpr auto MAX_BACKOFF_FACTOR = 32; + +// +// ---> ActiveActiveStateMachine( +// mux::MuxPort *muxPortPtr, +// boost::asio::io_service::strand &strand, +// common::MuxPortConfig &muxPortConfig +// ) +// +// Construct a new Active Active State Machine object +// +ActiveActiveStateMachine::ActiveActiveStateMachine( + mux::MuxPort *muxPortPtr, + boost::asio::io_service::strand &strand, + common::MuxPortConfig &muxPortConfig +) + : LinkManagerStateMachineBase( + muxPortPtr, + strand, + muxPortConfig, + {link_prober::LinkProberState::Label::Wait, + mux_state::MuxState::Label::Wait, + link_state::LinkState::Label::Down} + ), + mDeadlineTimer(strand.context()), + mWaitTimer(strand.context()), + mPeerWaitTimer(strand.context()) +{ + assert(muxPortPtr != nullptr); + mMuxPortPtr->setMuxLinkmgrState(mLabel); + initializeTransitionFunctionTable(); +} + +// +// ---> activateStateMachine(); +// +// activate the state machine by starting the link prober. +// +void ActiveActiveStateMachine::activateStateMachine() +{ + if (mComponentInitState.all()) { + std::array macAddress = mMuxPortConfig.getBladeMacAddress(); + std::array macAddressStr = {0}; + + snprintf( + macAddressStr.data(), macAddressStr.size(), "%02x:%02x:%02x:%02x:%02x:%02x", + macAddress[0], macAddress[1], macAddress[2], macAddress[3], macAddress[4], macAddress[5] + ); + + MUXLOGWARNING( + boost::format("%s: MUX port link prober initialized with server IP: %s, server MAC: %s") % + mMuxPortConfig.getPortName() % + mMuxPortConfig.getBladeIpv4Address().to_string() % + macAddressStr.data() + ); + // make link prober state match the MUX state since the state machine is activated for the first time + CompositeState nextState = mCompositeState; + initLinkProberState(nextState); + LOGWARNING_MUX_STATE_TRANSITION(mMuxPortConfig.getPortName(), mCompositeState, nextState); + mCompositeState = nextState; + + mInitializeProberFnPtr(); + mStartProbingFnPtr(); + + updateMuxLinkmgrState(); + } +} + +/*-------------------------------------------------------------------------------------------------------------- +| db event handlers +---------------------------------------------------------------------------------------------------------------*/ + +// +// ---> handleSwssSoCIpv4AddressUpdate(boost::asio::ip::address address); +// +// initialize the link prober component. Note if this is the last component to be initialized, +// state machine will be activated +// +void ActiveActiveStateMachine::handleSwssSoCIpv4AddressUpdate(boost::asio::ip::address address) +{ + if (!mComponentInitState.test(LinkProberComponent)) { + mMuxPortConfig.setBladeIpv4Address(address); + + try { + mLinkProberPtr = std::make_shared( + mMuxPortConfig, + getStrand().context(), + mLinkProberStateMachinePtr.get() + ); + mInitializeProberFnPtr = boost::bind( + &link_prober::LinkProber::initialize, mLinkProberPtr.get() + ); + mStartProbingFnPtr = boost::bind( + &link_prober::LinkProber::startProbing, mLinkProberPtr.get() + ); + mUpdateEthernetFrameFnPtr = boost::bind( + &link_prober::LinkProber::updateEthernetFrame, mLinkProberPtr.get() + ); + mProbePeerTorFnPtr = boost::bind( + &link_prober::LinkProber::probePeerTor, mLinkProberPtr.get() + ); + mSuspendTxFnPtr = boost::bind( + &link_prober::LinkProber::suspendTxProbes, mLinkProberPtr.get(), boost::placeholders::_1 + ); + mResumeTxFnPtr = boost::bind( + &link_prober::LinkProber::resumeTxProbes, mLinkProberPtr.get() + ); + mShutdownTxFnPtr = boost::bind( + &link_prober::LinkProber::shutdownTxProbes, mLinkProberPtr.get() + ); + mRestartTxFnPtr = boost::bind( + &link_prober::LinkProber::restartTxProbes, mLinkProberPtr.get() + ); + + setComponentInitState(LinkProberComponent); + + activateStateMachine(); + } catch (const std::bad_alloc &ex) { + std::ostringstream errMsg; + errMsg << "Failed allocate memory. Exception details: " << ex.what(); + + throw MUX_ERROR(BadAlloc, errMsg.str()); + } + } else if (address != mMuxPortConfig.getBladeIpv4Address()) { + mMuxPortConfig.setBladeIpv4Address(address); + mUpdateEthernetFrameFnPtr(); + } +} + +// +// ---> handleMuxStateNotification(mux_state::MuxState::Label label); +// +// handle MUX state notification +// +void ActiveActiveStateMachine::handleMuxStateNotification(mux_state::MuxState::Label label) +{ + MUXLOGWARNING(boost::format("%s: state db mux state: %s") % mMuxPortConfig.getPortName() % mMuxStateName[label]); + + mWaitTimer.cancel(); + + if (mComponentInitState.all()) { + if (mMuxStateMachine.getWaitStateCause() != mux_state::WaitState::WaitStateCause::SwssUpdate) { + MUXLOGWARNING(boost::format("%s: Received unsolicited MUX state change notification!") % mMuxPortConfig.getPortName()); + } + mProbePeerTorFnPtr(); + mResumeTxFnPtr(); + postMuxStateEvent(label); + } else { + if (label == mux_state::MuxState::Unknown) { + // probe xcvrd to read the current mux state + probeMuxState(); + } + enterMuxState(mCompositeState, label); + setComponentInitState(MuxStateComponent); + activateStateMachine(); + } +} + +// +// ---> handleSwssLinkStateNotification(const link_state::LinkState::Label label); +// +// handle link state notification +// +void ActiveActiveStateMachine::handleSwssLinkStateNotification(const link_state::LinkState::Label label) +{ + MUXLOGINFO(boost::format("%s: state db link state: %s") % mMuxPortConfig.getPortName() % mLinkStateName[label]); + + if (mComponentInitState.all()) { + if (label == link_state::LinkState::Label::Up) { + mLinkStateMachine.postLinkStateEvent(link_state::LinkStateMachine::getUpEvent()); + } else if (label == link_state::LinkState::Label::Down) { + mLinkStateMachine.postLinkStateEvent(link_state::LinkStateMachine::getDownEvent()); + } + } else { + enterLinkState(mCompositeState, label); + setComponentInitState(LinkStateComponent); + activateStateMachine(); + } +} + +// +// ---> handleMuxConfigNotification(const common::MuxPortConfig::Mode mode); +// +// handle MUX configuration change notification +// +void ActiveActiveStateMachine::handleMuxConfigNotification(const common::MuxPortConfig::Mode mode) +{ + if (mComponentInitState.all()) { + CompositeState nextState = mCompositeState; + if (mode == common::MuxPortConfig::Mode::Active && ms(mCompositeState) != mux_state::MuxState::Label::Active) { + switchMuxState(nextState, mux_state::MuxState::Label::Active); + } else if (mode == common::MuxPortConfig::Mode::Standby && ms(mCompositeState) != mux_state::MuxState::Label::Standby) { + switchMuxState(nextState, mux_state::MuxState::Label::Standby); + } else { + // enforce link prober state to match mux state to trigger possible transitions + initLinkProberState(nextState); + probeMuxState(); + } + LOGWARNING_MUX_STATE_TRANSITION(mMuxPortConfig.getPortName(), mCompositeState, nextState); + mCompositeState = nextState; + updateMuxLinkmgrState(); + } + + mMuxPortConfig.setMode(mode); +} + +// +// ---> handleProbeMuxStateNotification(mux_state::MuxState::Label label); +// +// handle probe MUX state notification +// +void ActiveActiveStateMachine::handleProbeMuxStateNotification(mux_state::MuxState::Label label) +{ + MUXLOGWARNING(boost::format("%s: app db mux state: %s") % mMuxPortConfig.getPortName() % mMuxStateName[label]); + + mWaitTimer.cancel(); + if (label == mux_state::MuxState::Label::Active || label == mux_state::MuxState::Label::Standby) { + mMuxProbeBackoffFactor = 1; + mDeadlineTimer.cancel(); + } + + if (mComponentInitState.all()) { + if (mMuxStateMachine.getWaitStateCause() != mux_state::WaitState::WaitStateCause::DriverUpdate) { + MUXLOGWARNING( + boost::format("%s: Received unsolicited MUX state probe notification!") % + mMuxPortConfig.getPortName() + ); + } + + postMuxStateEvent(label); + } else { + if (label == mux_state::MuxState::Unknown) { + MUXLOGWARNING( + boost::format("%s: xcvrd reports MUX state as '%s' during init. phase! Is there a functioning MUX?") % + mMuxPortConfig.getPortName() % + mMuxStateName[label] + ); + probeMuxState(); + } + + enterMuxState(mCompositeState, label); + setComponentInitState(MuxStateComponent); + activateStateMachine(); + } +} + +// +// ---> handlePeerMuxStateNotification(mux_state::MuxState::Label label); +// +// handle peer MUX state notification +// +void ActiveActiveStateMachine::handlePeerMuxStateNotification(mux_state::MuxState::Label label) +{ + MUXLOGWARNING(boost::format("%s: state db mux state: %s") % mMuxPortConfig.getPortName() % mMuxStateName[label]); + + mPeerWaitTimer.cancel(); + enterPeerMuxState(label); +} + +/*-------------------------------------------------------------------------------------------------------------- +| link prober event handlers +---------------------------------------------------------------------------------------------------------------*/ + +// +// ---> handleSuspendTimerExpiry(); +// +// handle completion of sending switch command to peer ToR +// +void ActiveActiveStateMachine::handleSuspendTimerExpiry() +{ + MUXLOGDEBUG(mMuxPortConfig.getPortName()); + mResumeTxFnPtr(); +} + +/*-------------------------------------------------------------------------------------------------------------- +| state handlers +---------------------------------------------------------------------------------------------------------------*/ + +// +// ---> handleStateChange(LinkProberEvent &event, link_prober::LinkProberState::Label state); +// +// handles LinkProberEvent +// +void ActiveActiveStateMachine::handleStateChange( + LinkProberEvent &event, + link_prober::LinkProberState::Label state +) +{ + if ((dynamic_cast(mLinkProberStateMachinePtr->getCurrentState()))->getStateLabel() == state) { + MUXLOGWARNING( + boost::format("%s: Received link prober event, new state: %s") % + mMuxPortConfig.getPortName() % + mLinkProberStateName[state] + ); + + CompositeState nextState = mCompositeState; + ps(nextState) = state; + mStateTransitionHandler[ps(nextState)][ms(nextState)][ls(nextState)](nextState); + LOGWARNING_MUX_STATE_TRANSITION(mMuxPortConfig.getPortName(), mCompositeState, nextState); + mCompositeState = nextState; + } + + updateMuxLinkmgrState(); +} + +// +// ---> handleStateChange(MuxStateEvent &event, mux_state::MuxState::Label state); +// +// handles MuxStateEvent +// +void ActiveActiveStateMachine::handleStateChange( + MuxStateEvent &event, + mux_state::MuxState::Label state +) +{ + if ((dynamic_cast(mMuxStateMachine.getCurrentState()))->getStateLabel() == state) { + MUXLOGWARNING( + boost::format("%s: Received mux state event, new state: %s") % + mMuxPortConfig.getPortName() % + mMuxStateName[state] + ); + + CompositeState nextState = mCompositeState; + ms(nextState) = state; + mStateTransitionHandler[ps(nextState)][ms(nextState)][ls(nextState)](nextState); + LOGINFO_MUX_STATE_TRANSITION(mMuxPortConfig.getPortName(), mCompositeState, nextState); + mCompositeState = nextState; + } + + updateMuxLinkmgrState(); +} + +// +// ---> handleStateChange(LinkStateEvent &event, link_state::LinkState::Label state); +// +// handles LinkStateEvent +// +void ActiveActiveStateMachine::handleStateChange( + LinkStateEvent &event, + link_state::LinkState::Label state +) +{ + if ((dynamic_cast(mLinkStateMachine.getCurrentState()))->getStateLabel() == state) { + MUXLOGWARNING( + boost::format("%s: Received link state event, new state: %s") % + mMuxPortConfig.getPortName() % + mLinkStateName[state] + ); + + CompositeState nextState = mCompositeState; + ls(nextState) = state; + if (ls(mCompositeState) == link_state::LinkState::Down && ls(nextState) == link_state::LinkState::Up) { + initLinkProberState(nextState); + initPeerLinkProberState(); + } else if (ls(mCompositeState) == link_state::LinkState::Up && ls(nextState) == link_state::LinkState::Down && ms(mCompositeState) == mux_state::MuxState::Label::Active) { + switchMuxState(nextState, mux_state::MuxState::Label::Standby); + } else { + mStateTransitionHandler[ps(nextState)][ms(nextState)][ls(nextState)](nextState); + } + LOGWARNING_MUX_STATE_TRANSITION(mMuxPortConfig.getPortName(), mCompositeState, nextState); + mCompositeState = nextState; + } + + updateMuxLinkmgrState(); +} + +// +// ---> handlePeerStateChange(LinkProberEvent &event, link_prober::LinkProberState::Label state); +// +// handles peer LinkProberEvent +// +void ActiveActiveStateMachine::handlePeerStateChange( + LinkProberEvent &event, + link_prober::LinkProberState::Label state +) +{ + if ((dynamic_cast(mLinkProberStateMachinePtr->getCurrentPeerState()))->getStateLabel() == state) { + MUXLOGWARNING( + boost::format("%s: Received peer link prober event, new state: %s") % + mMuxPortConfig.getPortName() % + mLinkProberStateName[state] + ); + + enterPeerLinkProberState(state); + switch (state) { + case link_prober::LinkProberState::Label::PeerActive: { + enterPeerMuxState(mux_state::MuxState::Active); + break; + } + case link_prober::LinkProberState::Label::PeerUnknown: { + if (mLabel == Label::Healthy) { + switchPeerMuxState(mux_state::MuxState::Label::Standby); + } + break; + } + default: { + break; + } + } + } +} + +/*-------------------------------------------------------------------------------------------------------------- + | state transition functions + ---------------------------------------------------------------------------------------------------------------*/ + +// +// ---> initializeTransitionFunctionTable(); +// +// initialize transition function table +// +void ActiveActiveStateMachine::initializeTransitionFunctionTable() +{ + MUXLOGWARNING("Initializing State Transition Table..."); + LinkManagerStateMachineBase::initializeTransitionFunctionTable(); + + mStateTransitionHandler[link_prober::LinkProberState::Label::Active] + [mux_state::MuxState::Label::Standby] + [link_state::LinkState::Label::Up] = + boost::bind( + &ActiveActiveStateMachine::LinkProberActiveMuxStandbyLinkUpTransitionFunction, + this, + boost::placeholders::_1 + ); + + mStateTransitionHandler[link_prober::LinkProberState::Label::Active] + [mux_state::MuxState::Label::Unknown] + [link_state::LinkState::Label::Up] = + boost::bind( + &ActiveActiveStateMachine::LinkProberActiveMuxUnknownLinkUpTransitionFunction, + this, + boost::placeholders::_1 + ); + + mStateTransitionHandler[link_prober::LinkProberState::Label::Unknown] + [mux_state::MuxState::Label::Active] + [link_state::LinkState::Label::Up] = + boost::bind( + &ActiveActiveStateMachine::LinkProberUnknownMuxActiveLinkUpTransitionFunction, + this, + boost::placeholders::_1 + ); + + mStateTransitionHandler[link_prober::LinkProberState::Label::Active] + [mux_state::MuxState::Label::Unknown] + [link_state::LinkState::Label::Up] = + boost::bind( + &ActiveActiveStateMachine::LinkProberActiveMuxUnknownLinkUpTransitionFunction, + this, + boost::placeholders::_1 + ); + + mStateTransitionHandler[link_prober::LinkProberState::Label::Unknown] + [mux_state::MuxState::Label::Unknown] + [link_state::LinkState::Label::Up] = + boost::bind( + &ActiveActiveStateMachine::LinkProberUnknownMuxUnknownLinkUpTransitionFunction, + this, + boost::placeholders::_1 + ); + + mStateTransitionHandler[link_prober::LinkProberState::Label::Active] + [mux_state::MuxState::Label::Error] + [link_state::LinkState::Label::Up] = + boost::bind( + &ActiveActiveStateMachine::LinkProberActiveMuxErrorLinkUpTransitionFunction, + this, + boost::placeholders::_1 + ); + + mStateTransitionHandler[link_prober::LinkProberState::Label::Active] + [mux_state::MuxState::Label::Wait] + [link_state::LinkState::Label::Up] = + boost::bind( + &ActiveActiveStateMachine::LinkProberActiveMuxWaitLinkUpTransitionFunction, + this, + boost::placeholders::_1 + ); + + mStateTransitionHandler[link_prober::LinkProberState::Label::Unknown] + [mux_state::MuxState::Label::Wait] + [link_state::LinkState::Label::Up] = + boost::bind( + &ActiveActiveStateMachine::LinkProberUnknownMuxWaitLinkUpTransitionFunction, + this, + boost::placeholders::_1 + ); +} + +// +// ---> LinkProberActiveMuxStandbyLinkUpTransitionFunction(CompositeState &nextState); +// +// transition function when entering {LinkProberActive, MuxStandby, LinkUp} state +// +void ActiveActiveStateMachine::LinkProberActiveMuxStandbyLinkUpTransitionFunction(CompositeState &nextState) +{ + MUXLOGINFO(mMuxPortConfig.getPortName()); + switchMuxState(nextState, mux_state::MuxState::Label::Active); +} + +// +// ---> LinkProberUnknownMuxActiveLinkUpTransitionFunction(CompositeState &nextState); +// +// transition function when entering {LinkProberUnknown, MuxActive, LinkUp} state +// +void ActiveActiveStateMachine::LinkProberUnknownMuxActiveLinkUpTransitionFunction(CompositeState &nextState) +{ + MUXLOGINFO(mMuxPortConfig.getPortName()); + switchMuxState(nextState, mux_state::MuxState::Label::Standby); +} + +// +// ---> LinkProberActiveMuxUnknownLinkUpTransitionFunction(CompositeState &nextState); +// +// transition function when entering {LinkProberActive, MuxUnknown, LinkUp} state +// +void ActiveActiveStateMachine::LinkProberActiveMuxUnknownLinkUpTransitionFunction(CompositeState &nextState) +{ + MUXLOGINFO(mMuxPortConfig.getPortName()); + if (ps(mCompositeState) != link_prober::LinkProberState::Active) { + switchMuxState(nextState, mux_state::MuxState::Label::Active); + } else { + startMuxProbeTimer(); + } +} + +// +// ---> LinkProberUnknownMuxUnknownLinkUpTransitionFunction(CompositeState &nextState); +// +// transition function when entering {LinkProberUnknown, MuxUnknown, LinkUp} state +// +void ActiveActiveStateMachine::LinkProberUnknownMuxUnknownLinkUpTransitionFunction(CompositeState &nextState) +{ + MUXLOGINFO(mMuxPortConfig.getPortName()); + if (ps(mCompositeState) != link_prober::LinkProberState::Unknown) { + switchMuxState(nextState, mux_state::MuxState::Label::Standby); + } else { + startMuxProbeTimer(); + } +} + +// +// ---> LinkProberActiveMuxErrorLinkUpTransitionFunction(CompositeState &nextState); +// +// transition function when entering {LinkProberActive, MuxError, LinkUp} state +// +void ActiveActiveStateMachine::LinkProberActiveMuxErrorLinkUpTransitionFunction(CompositeState &nextState) +{ + MUXLOGINFO(mMuxPortConfig.getPortName()); + startMuxProbeTimer(); +} + +// +// ---> LinkProberActiveMuxWaitLinkUpTransitionFunction(CompositeState &nextState); +// +// transition function when entering {LinkProberActive, MuxWait, LinkUp} state +// +void ActiveActiveStateMachine::LinkProberActiveMuxWaitLinkUpTransitionFunction(CompositeState &nextState) +{ + MUXLOGINFO(mMuxPortConfig.getPortName()); + switchMuxState(nextState, mux_state::MuxState::Label::Active); +} + +// +// ---> LinkProberUnknownMuxWaitLinkUpTransitionFunction(CompositeState &nextState); +// +// transition function when entering {LinkProberUnknown, MuxWait, LinkUp} state +// +void ActiveActiveStateMachine::LinkProberUnknownMuxWaitLinkUpTransitionFunction(CompositeState &nextState) +{ + MUXLOGINFO(mMuxPortConfig.getPortName()); + switchMuxState(nextState, mux_state::MuxState::Label::Standby); +} + +/*-------------------------------------------------------------------------------------------------------------- + | utility methods to check/modify state + ---------------------------------------------------------------------------------------------------------------*/ + +// +// ---> setLabel(Label label); +// +// sets linkmgr State db state +// +void ActiveActiveStateMachine::setLabel(Label label) +{ + if (mLabel != label) { + mLabel = label; + mMuxPortPtr->setMuxLinkmgrState(label); + + MUXLOGWARNING( + boost::format("%s: Linkmgrd state is: %s %s") % + mMuxPortConfig.getPortName() % + mMuxStateName[ms(mCompositeState)] % + mLinkHealthName[static_cast(label)] + ); + } +} + +// +// ---> enterLinkProberState(CompositeState &nextState, link_prober::LinkProberState::Label label); +// +// force LinkProberState to switch state +// +void ActiveActiveStateMachine::enterLinkProberState( + CompositeState &nextState, + link_prober::LinkProberState::Label label +) +{ + mLinkProberStateMachinePtr->enterState(label); + ps(nextState) = label; +} + +// +// ---> enterMuxState(CompositeState &nextState, mux_state::MuxState::Label label); +// +// force MuxState to switch state +// +void ActiveActiveStateMachine::enterMuxState( + CompositeState &nextState, + mux_state::MuxState::Label label +) +{ + mMuxStateMachine.enterState(label); + ms(nextState) = label; +} + +// +// ---> enterLinkState(CompositeState &nextState, link_state::LinkState::Label label); +// +// force LinkState to switch state +// +void ActiveActiveStateMachine::enterLinkState( + CompositeState &nextState, + link_state::LinkState::Label label +) +{ + mLinkStateMachine.enterState(label); + ls(nextState) = label; +} + +// +// ---> enterPeerMuxState(mux_state::MuxState::Label label); +// +// force peer MuxState to switch state +// +void ActiveActiveStateMachine::enterPeerMuxState(mux_state::MuxState::Label label) +{ + mPeerMuxState = label; +} + +// +// ---> enterPeerLinkProberState(link_prober::LinkProberState::Label label); +// +// force peer LinkProberState to switch state +// +void ActiveActiveStateMachine::enterPeerLinkProberState(link_prober::LinkProberState::Label label) +{ + mLinkProberStateMachinePtr->enterPeerState(label); + mPeerLinkProberState = label; +} + +// +// ---> switchMuxState(CompositeState &nextState, mux_state::MuxState::Label label, bool forceSwitch); +// +// switch MUX to the target state +// +void ActiveActiveStateMachine::switchMuxState( + CompositeState &nextState, + mux_state::MuxState::Label label +) +{ + if (mMuxPortConfig.getMode() == common::MuxPortConfig::Mode::Auto) { + MUXLOGWARNING( + boost::format("%s: Switching MUX state to '%s'") % + mMuxPortConfig.getPortName() % + mMuxStateName[label] + ); + if (label == mux_state::MuxState::Label::Standby) { + // suspend heartbeat to help peer ToR to toggle + mSuspendTxFnPtr(mMuxPortConfig.getLinkWaitTimeout_msec()); + } + enterMuxState(nextState, label); + mMuxStateMachine.setWaitStateCause(mux_state::WaitState::WaitStateCause::SwssUpdate); + mMuxPortPtr->setMuxState(label); + mDeadlineTimer.cancel(); + startMuxWaitTimer(); + } else { + probeMuxState(); + } +} + +// +// ---> switchPeerMuxState(CompositeState &nextState, mux_state::MuxState::Label label, bool forceSwitch); +// +// switch peer MUX to the target state +// +void ActiveActiveStateMachine::switchPeerMuxState(mux_state::MuxState::Label label) +{ + MUXLOGWARNING( + boost::format("%s: Switching peer MUX state to '%s'") % + mMuxPortConfig.getPortName() % + mMuxStateName[label] + ); + enterPeerMuxState(label); + mMuxPortPtr->setPeerMuxState(label); + startPeerMuxWaitTimer(); +} + +// +// ---> probeMuxState(); +// +// probe mux state +// +void ActiveActiveStateMachine::probeMuxState() +{ + mMuxStateMachine.setWaitStateCause(mux_state::WaitState::WaitStateCause::DriverUpdate); + mMuxPortPtr->probeMuxState(); + startMuxWaitTimer(); +} + +// +// ---> updateMuxLinkmgrState(); +// +// update State DB MUX LinkMgr state +// +void ActiveActiveStateMachine::updateMuxLinkmgrState() +{ + Label label = Label::Unhealthy; + if (ls(mCompositeState) == link_state::LinkState::Label::Up && ps(mCompositeState) == link_prober::LinkProberState::Label::Active && ms(mCompositeState) == mux_state::MuxState::Label::Active) { + label = Label::Healthy; + } + setLabel(label); +} + +// +// ---> initLinkProberState(CompositeState &compositeState); +// +// initialize LinkProberState when configuring the composite state machine +// +void ActiveActiveStateMachine::initLinkProberState(CompositeState &compositeState) +{ + switch (ms(compositeState)) { + case mux_state::MuxState::Label::Active: + enterLinkProberState(compositeState, link_prober::LinkProberState::Label::Active); + break; + case mux_state::MuxState::Label::Standby: + enterLinkProberState(compositeState, link_prober::LinkProberState::Label::Unknown); + break; + case mux_state::MuxState::Label::Unknown: + enterLinkProberState(compositeState, link_prober::LinkProberState::Label::Wait); + break; + case mux_state::MuxState::Label::Error: + enterLinkProberState(compositeState, link_prober::LinkProberState::Label::Wait); + break; + case mux_state::MuxState::Label::Wait: + enterLinkProberState(compositeState, link_prober::LinkProberState::Label::Wait); + break; + default: + break; + } +} + +// +// ---> initPeerLinkProberState(); +// +// initialize peer LinkProberState when configuring the composite state machine +// +void ActiveActiveStateMachine::initPeerLinkProberState() +{ + switch (mPeerMuxState) { + case mux_state::MuxState::Label::Active: + enterPeerLinkProberState(link_prober::LinkProberState::Label::PeerActive); + break; + case mux_state::MuxState::Label::Standby: + enterPeerLinkProberState(link_prober::LinkProberState::Label::PeerUnknown); + break; + case mux_state::MuxState::Label::Unknown: + enterPeerLinkProberState(link_prober::LinkProberState::Label::PeerWait); + break; + case mux_state::MuxState::Label::Error: + enterPeerLinkProberState(link_prober::LinkProberState::Label::PeerWait); + break; + case mux_state::MuxState::Label::Wait: + enterPeerLinkProberState(link_prober::LinkProberState::Label::PeerWait); + break; + default: + break; + } +} + +// +// ---> startMuxProbeTimer(); +// +// start a mux probe and wait for mux probe notification(active or standby) from xcvrd +// +void ActiveActiveStateMachine::startMuxProbeTimer() +{ + probeMuxState(); + mDeadlineTimer.expires_from_now(boost::posix_time::milliseconds( + mMuxProbeBackoffFactor * mMuxPortConfig.getNegativeStateChangeRetryCount() * mMuxPortConfig.getTimeoutIpv4_msec() + )); + mDeadlineTimer.async_wait(getStrand().wrap(boost::bind( + &ActiveActiveStateMachine::handleMuxProbeTimeout, + this, + boost::asio::placeholders::error + ))); +} + +// +// ---> handleMuxProbeTimeout(boost::system::error_code errorCode); +// +// handles when xcvrd has timeout responding mux probe +// +void ActiveActiveStateMachine::handleMuxProbeTimeout(boost::system::error_code errorCode) +{ + MUXLOGDEBUG(mMuxPortConfig.getPortName()); + if (errorCode == boost::system::errc::success) { + if (ms(mCompositeState) == mux_state::MuxState::Label::Unknown || + ms(mCompositeState) == mux_state::MuxState::Label::Error || + ms(mCompositeState) == mux_state::MuxState::Label::Wait) { + mMuxProbeBackoffFactor <<= 1; + mMuxProbeBackoffFactor = mMuxProbeBackoffFactor > MAX_BACKOFF_FACTOR ? MAX_BACKOFF_FACTOR : mMuxProbeBackoffFactor; + startMuxProbeTimer(); + } else { + mMuxProbeBackoffFactor = 1; + } + } +} + +// +// ---> startMuxWaitTimer(uint32_t factor); +// +// start a timer to wait for mux state notification from xcvrd/orchagent +// +void ActiveActiveStateMachine::startMuxWaitTimer(uint32_t factor) +{ + mWaitTimer.expires_from_now(boost::posix_time::milliseconds( + factor * mMuxPortConfig.getNegativeStateChangeRetryCount() * mMuxPortConfig.getTimeoutIpv4_msec() + )); + mWaitTimer.async_wait(getStrand().wrap(boost::bind( + &ActiveActiveStateMachine::handleMuxWaitTimeout, + this, + boost::asio::placeholders::error + ))); +} + +// +// ---> handleMuxWaitTimeout(boost::system::error_code errorCode); +// +// handle when xcrvrd/orchagent has timed out responding mux state +// +void ActiveActiveStateMachine::handleMuxWaitTimeout(boost::system::error_code errorCode) +{ + if (errorCode == boost::system::errc::success) { + if (mMuxStateMachine.getWaitStateCause() == mux_state::WaitState::WaitStateCause::SwssUpdate) { + MUXLOGTIMEOUT(mMuxPortConfig.getPortName(), "orchagent timed out responding to linkmgrd", mCompositeState); + } else if (mMuxStateMachine.getWaitStateCause() == mux_state::WaitState::WaitStateCause::DriverUpdate) { + MUXLOGTIMEOUT(mMuxPortConfig.getPortName(), "xcvrd timed out responding to linkmgrd", mCompositeState); + } else { + MUXLOGTIMEOUT(mMuxPortConfig.getPortName(), "Unknown timeout reason!!!", mCompositeState); + } + startMuxWaitTimer(MAX_BACKOFF_FACTOR); + } +} + +// +// ---> startPeerMuxWaitTimer(uint32_t factor); +// +// start a timer to wait for peer mux state notification from xcvrd/orchagent +// +void ActiveActiveStateMachine::startPeerMuxWaitTimer(uint32_t factor) +{ + mPeerWaitTimer.expires_from_now(boost::posix_time::milliseconds( + factor * mMuxPortConfig.getNegativeStateChangeRetryCount() * mMuxPortConfig.getTimeoutIpv4_msec() + )); + mPeerWaitTimer.async_wait(getStrand().wrap(boost::bind( + &ActiveActiveStateMachine::handlePeerMuxWaitTimeout, + this, + boost::asio::placeholders::error + ))); +} + +// +// ---> handlePeerMuxWaitTimeout(boost::system::error_code errorCode); +// +// handle when xcrvrd/orchagent has timed out responding peer mux state +// +void ActiveActiveStateMachine::handlePeerMuxWaitTimeout(boost::system::error_code errorCode) +{ + if (errorCode == boost::system::errc::success) { + MUXLOGWARNING( + boost::format("%s: %s, current peer mux state: %s") % + mMuxPortConfig.getPortName() % + "xcvrd timed out responding to linkmgrd peer mux state" % + mMuxStateName[mPeerMuxState] + ); + } else { + MUXLOGWARNING( + boost::format("%s: %s, current peer mux state: %s") % + mMuxPortConfig.getPortName() % + "Unknown timeout reason!!!" % + mMuxStateName[mPeerMuxState] + ); + } + startPeerMuxWaitTimer(MAX_BACKOFF_FACTOR); +} + +} /* namespace link_manager */ diff --git a/src/link_manager/LinkManagerStateMachineActiveActive.h b/src/link_manager/LinkManagerStateMachineActiveActive.h new file mode 100644 index 00000000..ff778fbd --- /dev/null +++ b/src/link_manager/LinkManagerStateMachineActiveActive.h @@ -0,0 +1,532 @@ +/* + * Copyright 2021 (c) Microsoft Corporation. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef LINK_MANAGER_LINKMANAGERSTATEMACHINEACTIVEACTIVE_H_ +#define LINK_MANAGER_LINKMANAGERSTATEMACHINEACTIVEACTIVE_H_ + +#include +#include +#include +#include +#include +#include + +#include "link_manager/LinkManagerStateMachineBase.h" +#include "link_prober/LinkProber.h" +#include "link_prober/LinkProberState.h" +#include "link_state/LinkState.h" +#include "link_state/LinkStateMachine.h" +#include "mux_state/MuxState.h" +#include "mux_state/MuxStateMachine.h" + +namespace mux +{ +#define ps(compositeState) std::get<0>(compositeState) +#define ms(compositeState) std::get<1>(compositeState) +#define ls(compositeState) std::get<2>(compositeState) + +class MuxPort; +} // namespace mux + +namespace link_manager +{ + +class ActiveActiveStateMachine : public LinkManagerStateMachineBase, + public std::enable_shared_from_this +{ +public: + /** + * @method ActiveActiveStateMachine + * + * @brief Construct a new Active Active State Machine object + */ + ActiveActiveStateMachine() = delete; + + /** + * @method ActiveActiveStateMachine + * + * @brief Construct a new Active Active State Machine object + */ + ActiveActiveStateMachine(const ActiveActiveStateMachine &) = delete; + + /** + * @method ActiveActiveStateMachine + * + * @brief Construct a new Active Active State Machine object + * + * @param muxPortPtr pointer to a MuxPort object + * @param strand boost strand object + * @param muxPortConfig reference to MuxPortConfig object + */ + ActiveActiveStateMachine( + mux::MuxPort *muxPortPtr, + boost::asio::io_service::strand &strand, + common::MuxPortConfig &muxPortConfig + ); + + /** + * @method ~ActiveActiveStateMachine + * + * @brief Destroy the Active Active State Machine object + */ + virtual ~ActiveActiveStateMachine() = default; + +public: + /** + * @method activateStateMachine + * + * @brief activate the state machine by starting the link prober. + */ + void activateStateMachine(); + +public: // db event handlers + /** + * @method handleSwssSoCIpv4AddressUpdate + * + * @brief initialize the link prober component. Note if this is the last component to be initialized, + * state machine will be activated + * + * @param address SoC IPv4 address + */ + void handleSwssSoCIpv4AddressUpdate(boost::asio::ip::address address) override; + + /** + * @method handleMuxStateNotification + * + * @brief handle MUX state notification + * + * @param label new MuxState label + */ + void handleMuxStateNotification(mux_state::MuxState::Label label) override; + + /** + * @method handleSwssLinkStateNotification + * + * @brief handle link state notification + * + * @param label new LinkState label + */ + void handleSwssLinkStateNotification(const link_state::LinkState::Label label) override; + + /** + * @method handleMuxConfigNotification + * + * @brief handle MUX configuration change notification + * + * @param mode new MUX config mode + */ + void handleMuxConfigNotification(const common::MuxPortConfig::Mode mode) override; + + /** + * @method handleProbeMuxStateNotification + * + * @brief handle probe MUX state notification + * + * @param label new MuxState label + */ + void handleProbeMuxStateNotification(mux_state::MuxState::Label label) override; + + /** + * @method handlePeerMuxStateNotification + * + * @brief handle peer MUX state notification + * + * @param label new peer MuxState label + */ + void handlePeerMuxStateNotification(mux_state::MuxState::Label label) override; + +public: // link prober event handlers + /** + * @method handleSuspendTimerExpiry + * + * @brief handle completion of sending switch command to peer ToR + */ + void handleSuspendTimerExpiry() override; + +public: // state handlers + /** + * @method handleStateChange + * + * @brief handles LinkProberEvent + * + * @param event LinkProberEvent object + * @param state new LibProberState label + */ + void handleStateChange(LinkProberEvent &event, link_prober::LinkProberState::Label state) override; + + /** + * @method handleStateChange + * + * @brief handles MuxStateEvent + * + * @param event MuxStateEvent object + * @param state new MuxState label + */ + void handleStateChange(MuxStateEvent &event, mux_state::MuxState::Label state) override; + + /** + * @method handleStateChange + * + * @brief handles LinkStateEvent + * + * @param event LinkStateEvent object + * @param state new LinkState label + */ + void handleStateChange(LinkStateEvent &event, link_state::LinkState::Label state) override; + + /** + * @method handlePeerStateChange + * + * @brief handles peer LinkProberEvent + * + * @param event LinkProberEvent object + * @param state new peer LinkState label + */ + void handlePeerStateChange(LinkProberEvent &event, link_prober::LinkProberState::Label state) override; + +public: // state transition functions + /** + * @method initializeTransitionFunctionTable + * + * @brief initialize static transition function table + */ + void initializeTransitionFunctionTable() override; + + /** + * @method LinkProberActiveMuxStandbyLinkUpTransitionFunction + * + * @brief transition function when entering {LinkProberActive, MuxStandby, LinkUp} state + * + * @param nextState reference to composite state + */ + void LinkProberActiveMuxStandbyLinkUpTransitionFunction(CompositeState &nextState); + + /** + * @method LinkProberActiveMuxUnknownLinkUpTransitionFunction + * + * @brief transition function when entering {LinkProberActive, MuxUnknown, LinkUp} state + * + * @param nextState reference to composite state + */ + void LinkProberActiveMuxUnknownLinkUpTransitionFunction(CompositeState &nextState); + + /** + * @method LinkProberUnknownMuxActiveLinkUpTransitionFunction + * + * @brief transition function when entering {LinkProberUnknown, MuxActive, LinkUp} state + * + * @param nextState reference to composite state + */ + void LinkProberUnknownMuxActiveLinkUpTransitionFunction(CompositeState &nextState); + + /** + * @method LinkProberUnknownMuxUnknownLinkUpTransitionFunction + * + * @brief transition function when entering {LinkProberUnknown, MuxUnknown, LinkUp} state + * + * @param nextState reference to composite state + */ + void LinkProberUnknownMuxUnknownLinkUpTransitionFunction(CompositeState &nextState); + + /** + * @method LinkProberActiveMuxErrorLinkUpTransitionFunction + * + * @brief transition function when entering {LinkProberActive, MuxError, LinkUp} state + * + * @param nextState reference to composite state + */ + void LinkProberActiveMuxErrorLinkUpTransitionFunction(CompositeState &nextState); + + /** + * @method LinkProberActiveMuxWaitLinkUpTransitionFunction + * + * @brief transition function when entering {LinkProberActive, MuxWait, LinkUp} state + * + * @param nextState reference to composite state + */ + void LinkProberActiveMuxWaitLinkUpTransitionFunction(CompositeState &nextState); + + /** + * @method LinkProberUnknownMuxWaitLinkUpTransitionFunction + * + * @brief transition function when entering {LinkProberUnknown, MuxWait, LinkUp} state + * + * @param nextState reference to composite state + */ + void LinkProberUnknownMuxWaitLinkUpTransitionFunction(CompositeState &nextState); + +private: // utility methods to check/modify state + /** + * @method setLabel + * + * @brief sets linkmgr State db state + * + * @param label new LinkManagerStateMachine label + */ + inline void setLabel(Label label) override; + + /** + * @method enterLinkProberState + * + * @brief force LinkProberState to switch state + * + * @param nextState reference to composite state + * @param label new LinkProberState label to switch to + */ + inline void enterLinkProberState(CompositeState &nextState, link_prober::LinkProberState::Label label); + + /** + * @method enterMuxState + * + * @brief force MuxState to switch state + * + * @param nextState reference to composite state + * @param label new MuxState label to switch to + */ + inline void enterMuxState(CompositeState &nextState, mux_state::MuxState::Label label); + + /** + * @method enterLinkState + * + * @brief force LinkState to switch state + * + * @param nextState reference to composite state + * @param label new LinkState label to switch to + */ + inline void enterLinkState(CompositeState &nextState, link_state::LinkState::Label label); + + /** + * @method enterPeerMuxState + * + * @brief force peer MuxState to switch state + * + * @param nextState reference to composite state + * @param label new peer MuxState label to switch to + */ + inline void enterPeerMuxState(mux_state::MuxState::Label label); + + /** + * @method enterPeerLinkProberState + * + * @brief force peer LinkProberState to switch state + * + * @param label new peer LinkProberState to switch to + */ + inline void enterPeerLinkProberState(link_prober::LinkProberState::Label label); + + /** + * @method switchMuxState + * + * @brief switch MUX to the target state + * + * @param nextState reference to composite state + * @param label new MuxState label to switch to + */ + inline void switchMuxState(CompositeState &nextState, mux_state::MuxState::Label label); + + /** + * @method switchPeerMuxState + * + * @brief switch peer MUX to the target state + * + * @param label new MuxState label to switch to + */ + inline void switchPeerMuxState(mux_state::MuxState::Label label); + + /** + * @method probeMuxState + * + * @brief probe mux state + */ + inline void probeMuxState(); + + /** + * @method updateMuxLinkmgrState + * + * @brief update State DB MUX LinkMgr state + */ + void updateMuxLinkmgrState(); + + /** + * @method initLinkProberState + * + * @brief initialize LinkProberState when configuring the composite state machine + * + * @param compositeState reference to composite state + */ + void initLinkProberState(CompositeState &compositeState); + + /** + * @method initPeerLinkProberState + * + * @brief initialize peer LinkProberState when configuring the composite state machine + */ + void initPeerLinkProberState(); + +private: + /** + * @brief start a mux probe and wait for mux probe notification(active or standby) from xcvrd + */ + inline void startMuxProbeTimer(); + + /** + * @brief handles when xcvrd has timeout responding mux probe + * + * @param errorCode error code object + */ + void handleMuxProbeTimeout(boost::system::error_code errorCode); + + /** + * @method startMuxWaitTimer + * + * @brief start a timer to wait for mux state notification from xcvrd/orchagent + * + * @param factor factor used to scale the timeout + */ + inline void startMuxWaitTimer(uint32_t factor = 1); + + /** + * @method handleMuxWaitTimeout + * + * @brief handle when xcrvrd/orchagent has timed out responding mux state + * + * @param errorCode error code object + */ + void handleMuxWaitTimeout(boost::system::error_code errorCode); + + /** + * @method startPeerMuxWaitTimer + * + * @brief start a timer to wait for peer mux state notification from xcvrd/orchagent + * + * @param factor factor used to scale the timeout + */ + inline void startPeerMuxWaitTimer(uint32_t factor = 1); + + /** + * @method handlePeerMuxWaitTimeout + * + * @brief handle when xcrvrd/orchagent has timed out responding peer mux state + * + * @param errorCode error code object + */ + void handlePeerMuxWaitTimeout(boost::system::error_code errorCode); + +private: // testing only + friend class mux::MuxPort; + friend class test::FakeMuxPort; + friend class test::MuxManagerTest; + + /** + * @method setInitializeProberFnPtr + * + * @brief set new InitializeProberFnPtr for the state machine. This method is used for testing + * + * @param initializeProberFnPtr (in) pointer to new InitializeProberFnPtr + */ + void setInitializeProberFnPtr(boost::function initializeProberFnPtr) { mInitializeProberFnPtr = initializeProberFnPtr; }; + + /** + * @method setStartProbingFnPtr + * + * @brief set new StartProbingFnPtr for the state machine. This method is used for testing + * + * @param startProbingFnPtr (in) pointer to new StartProbingFnPtr + */ + void setStartProbingFnPtr(boost::function startProbingFnPtr) { mStartProbingFnPtr = startProbingFnPtr; }; + + /** + * @method setUpdateEthernetFrameFnPtr + * + * @brief set new UpdateEthernetFrameFnPtr for the state machine. This method is used for testing + * + * @param updateEthernetFrameFnPtr (in) pointer to new UpdateEthernetFrameFnPtr + */ + void setUpdateEthernetFrameFnPtr(boost::function updateEthernetFrameFnPtr) { mUpdateEthernetFrameFnPtr = updateEthernetFrameFnPtr; }; + + /** + * @method setProbePeerTorFnPtr + * + * @brief set new ProbePeerTorFnPtr for the state machine. This method is used for testing + * + * @param probePeerTorFnPtr (in) pointer to new ProbePeerTorFnPtr + */ + void setProbePeerTorFnPtr(boost::function probePeerTorFnPtr) { mProbePeerTorFnPtr = probePeerTorFnPtr; }; + + /** + * @method setSuspendTxFnPtr + * + * @brief set new SuspendTXFnPtr for the state machine. This method is used for testing + * + * @param suspendTxFnPtr (in) pointer to new SuspendTxFnPtr + */ + void setSuspendTxFnPtr(boost::function suspendTxFnPtr) { mSuspendTxFnPtr = suspendTxFnPtr; }; + + /** + * @method setResumeTxFnPtr + * + * @brief set new ResumeTXFnPtr for the state machine. This method is used for testing + * + * @param resumeTxFnPtr (in) pointer to new ResumeTxFnPtr + */ + void setResumeTxFnPtr(boost::function resumeTxFnPtr) { mResumeTxFnPtr = resumeTxFnPtr; }; + + /** + * @method setShutdownTxFnPtr + * + * @brief set shutdownTxFnPtr. This method is used for testing + * + * @param shutdownTxFnPtr (in) pointer to new ShutdownTxFnPtr + * + * @return none + */ + void setShutdownTxFnPtr(boost::function shutdownTxFnPtr) { mShutdownTxFnPtr = shutdownTxFnPtr; } + + /** + * @method setRestartTxFnPtr + * + * @brief set restartTxFnPtr. This method is used for testing + * + * @param restartTxFnPtr (in) pointer to new restartTxFnPtr + * + * @return none + */ + void setRestartTxFnPtr(boost::function restartTxFnPtr) { mRestartTxFnPtr = restartTxFnPtr; } + +private: // peer link prober state and mux state + link_prober::LinkProberState::Label mPeerLinkProberState = link_prober::LinkProberState::Label::PeerWait; + mux_state::MuxState::Label mPeerMuxState = mux_state::MuxState::Label::Wait; + +private: + uint32_t mMuxProbeBackoffFactor = 1; + + boost::asio::deadline_timer mDeadlineTimer; + boost::asio::deadline_timer mWaitTimer; + boost::asio::deadline_timer mPeerWaitTimer; + + boost::function mInitializeProberFnPtr; + boost::function mStartProbingFnPtr; + boost::function mUpdateEthernetFrameFnPtr; + boost::function mProbePeerTorFnPtr; + boost::function mSuspendTxFnPtr; + boost::function mResumeTxFnPtr; + boost::function mShutdownTxFnPtr; + boost::function mRestartTxFnPtr; +}; + +} /* namespace link_manager */ + +#endif /* LINK_MANAGER_LINKMANAGERSTATEMACHINEACTIVEACTIVE_H_ */ diff --git a/src/link_manager/LinkManagerStateMachineActiveStandby.cpp b/src/link_manager/LinkManagerStateMachineActiveStandby.cpp index 3ec92655..6d72f433 100644 --- a/src/link_manager/LinkManagerStateMachineActiveStandby.cpp +++ b/src/link_manager/LinkManagerStateMachineActiveStandby.cpp @@ -28,36 +28,6 @@ #include "common/MuxException.h" #include "MuxPort.h" -#define LOG_MUX_STATE_TRANSITION(level, portName, currentState, nextState) \ - do { \ - MUXLOG##level(boost::format("%s: (P: %s, M: %s, L: %s) -> (P: %s, M: %s, L: %s)") % \ - portName % \ - mLinkProberStateName[ps(currentState)] % \ - mMuxStateName[ms(currentState)] % \ - mLinkStateName[ls(currentState)] % \ - mLinkProberStateName[ps(nextState)] % \ - mMuxStateName[ms(nextState)] % \ - mLinkStateName[ls(nextState)] \ - ); \ - } while (0) - -#define LOGWARNING_MUX_STATE_TRANSITION(portName, currentState, nextState) \ - LOG_MUX_STATE_TRANSITION(WARNING, portName, currentState, nextState) - -#define LOGINFO_MUX_STATE_TRANSITION(portName, currentState, nextState) \ - LOG_MUX_STATE_TRANSITION(INFO, portName, currentState, nextState) - -#define MUXLOGTIMEOUT(portname, msg, currentState) \ - do { \ - MUXLOGWARNING(boost::format("%s: %s, current state: (P: %s, M: %s, L: %s)") % \ - portname % \ - msg % \ - mLinkProberStateName[ps(currentState)] % \ - mMuxStateName[ms(currentState)] % \ - mLinkStateName[ls(currentState)] \ - ); \ - } while (0) - namespace link_manager { @@ -1050,41 +1020,6 @@ void ActiveStandbyStateMachine::initLinkProberState(CompositeState &compositeSta } } -// -// ---> postMuxStateEvent(mux_state::MuxState::Label label) -// -// post event to MUX state machine to change state -// -void ActiveStandbyStateMachine::postMuxStateEvent(mux_state::MuxState::Label label) -{ - switch (label) { - case mux_state::MuxState::Label::Active: - mMuxStateMachine.postMuxStateEvent(mux_state::MuxStateMachine::getActiveEvent()); - break; - case mux_state::MuxState::Label::Standby: - mMuxStateMachine.postMuxStateEvent(mux_state::MuxStateMachine::getStandbyEvent()); - break; - case mux_state::MuxState::Label::Unknown: - mMuxStateMachine.postMuxStateEvent(mux_state::MuxStateMachine::getUnknownEvent()); - break; - case mux_state::MuxState::Label::Error: - mMuxStateMachine.postMuxStateEvent(mux_state::MuxStateMachine::getErrorEvent()); - break; - default: - break; - } -} - -// -// ---> noopTransitionFunction(CompositeState &nextState); -// -// No-op transition function -// -void ActiveStandbyStateMachine::noopTransitionFunction(CompositeState &nextState) -{ - MUXLOGINFO(mMuxPortConfig.getPortName()); -} - // // ---> LinkProberStandbyMuxActiveLinkUpTransitionFunction(CompositeState &nextState); // diff --git a/src/link_manager/LinkManagerStateMachineActiveStandby.h b/src/link_manager/LinkManagerStateMachineActiveStandby.h index b60fe277..8c4a2de2 100644 --- a/src/link_manager/LinkManagerStateMachineActiveStandby.h +++ b/src/link_manager/LinkManagerStateMachineActiveStandby.h @@ -86,20 +86,6 @@ class ActiveStandbyStateMachine: public LinkManagerStateMachineBase, Count }; -private: - /** - *@enum anonymous - * - *@brief used to reference bits corresponding to respective state machine init state - */ - enum { - LinkProberComponent, - MuxStateComponent, - LinkStateComponent, - - ComponentCount - }; - public: /** *@method ActiveStandbyStateMachine @@ -488,29 +474,6 @@ class ActiveStandbyStateMachine: public LinkManagerStateMachineBase, */ void initLinkProberState(CompositeState &compositeState); - /** - *@method postMuxStateEvent - * - *@brief post event to MUX state machine to change state - * - *@param label (in) new state label to post event for - * - * - *@return none - */ - void postMuxStateEvent(mux_state::MuxState::Label label); - - /** - *@method noopTransitionFunction - * - *@brief No-op transition function - * - *@param nextState (in, out) reference to composite - * - *@return none - */ - void noopTransitionFunction(CompositeState &nextState); - /** *@method LinkProberStandbyMuxActiveLinkUpTransitionFunction * @@ -812,17 +775,6 @@ class ActiveStandbyStateMachine: public LinkManagerStateMachineBase, mSendPeerSwitchCommandFnPtr = sendPeerSwitchCommandFnPtr; }; - /** - *@method setComponentInitState - * - *@brief set component inti state. This method is used for testing - * - *@param component (in) component index - * - *@return none - */ - void setComponentInitState(uint8_t component) {mComponentInitState.set(component);}; - /** * @method setShutdownTxFnPtr * @@ -904,8 +856,6 @@ class ActiveStandbyStateMachine: public LinkManagerStateMachineBase, bool mContinuousLinkProberUnknownEvent = false; // When posting unknown_end event, we want to make sure the previous state is unknown. DefaultRoute mDefaultRouteState = DefaultRoute::Wait; - - std::bitset mComponentInitState = {0}; }; } /* namespace link_manager */ diff --git a/src/link_manager/LinkManagerStateMachineBase.cpp b/src/link_manager/LinkManagerStateMachineBase.cpp index 0411f460..71c2b760 100644 --- a/src/link_manager/LinkManagerStateMachineBase.cpp +++ b/src/link_manager/LinkManagerStateMachineBase.cpp @@ -26,7 +26,7 @@ LinkProberEvent LinkManagerStateMachineBase::mLinkProberEvent; MuxStateEvent LinkManagerStateMachineBase::mMuxStateEvent; LinkStateEvent LinkManagerStateMachineBase::mLinkStateEvent; -std::vector LinkManagerStateMachineBase::mLinkProberStateName = {"Active", "Standby", "Unknown", "Wait"}; +std::vector LinkManagerStateMachineBase::mLinkProberStateName = {"Active", "Standby", "Unknown", "Wait", "PeerWait", "PeerActive", "PeerUnknown"}; std::vector LinkManagerStateMachineBase::mMuxStateName = {"Active", "Standby", "Unknown", "Error", "Wait"}; std::vector LinkManagerStateMachineBase::mLinkStateName = {"Up", "Down"}; std::vector LinkManagerStateMachineBase::mLinkHealthName = {"Uninitialized", "Unhealthy", "Healthy"}; @@ -56,6 +56,11 @@ LinkManagerStateMachineBase::LinkManagerStateMachineBase( this, strand, mMuxPortConfig, ps(mCompositeState) ); break; + case common::MuxPortConfig::PortCableType::ActiveActive: + mLinkProberStateMachinePtr = std::make_shared( + this, strand, mMuxPortConfig, ps(mCompositeState) + ); + break; default: break; } @@ -113,6 +118,17 @@ void LinkManagerStateMachineBase::handleSwssBladeIpv4AddressUpdate(boost::asio:: MUXLOGINFO(mMuxPortConfig.getPortName()); } +// +// ---> handleSwssSoCIpv4AddressUpdate(boost::asio::ip::address address); +// +// initialize LinkProber component. Note if this is the last component to be initialized, +// state machine will be activated +// +void LinkManagerStateMachineBase::handleSwssSoCIpv4AddressUpdate(boost::asio::ip::address address) +{ + MUXLOGINFO(mMuxPortConfig.getPortName()); +} + // // ---> handleGetServerMacAddressNotification(std::array address); // @@ -172,6 +188,15 @@ void LinkManagerStateMachineBase::handlePeerLinkStateNotification(const link_sta MUXLOGINFO(mMuxPortConfig.getPortName()); } +// ---> handlePeerMuxStateNotification(mux_state::MuxState::Label label); +// +// handle peer MUX state change notification +// +void LinkManagerStateMachineBase::handlePeerMuxStateNotification(mux_state::MuxState::Label label) +{ + MUXLOGINFO(mMuxPortConfig.getPortName()); +} + // // ---> handleMuxConfigNotification(const common::MuxPortConfig::Mode mode); // @@ -252,13 +277,29 @@ void LinkManagerStateMachineBase::handleResetLinkProberPckLossCount() MUXLOGINFO(mMuxPortConfig.getPortName()); } -// ---> LinkManagerStateMachineBase::setComponentInitState(uint8_t component) // -// set component inti state. This method is used for testing +// ---> postMuxStateEvent(mux_state::MuxState::Label label) +// +// post event to MUX state machine to change state // -void LinkManagerStateMachineBase::setComponentInitState(uint8_t component) +void LinkManagerStateMachineBase::postMuxStateEvent(mux_state::MuxState::Label label) { - MUXLOGINFO(mMuxPortConfig.getPortName()); -}; + switch (label) { + case mux_state::MuxState::Label::Active: + mMuxStateMachine.postMuxStateEvent(mux_state::MuxStateMachine::getActiveEvent()); + break; + case mux_state::MuxState::Label::Standby: + mMuxStateMachine.postMuxStateEvent(mux_state::MuxStateMachine::getStandbyEvent()); + break; + case mux_state::MuxState::Label::Unknown: + mMuxStateMachine.postMuxStateEvent(mux_state::MuxStateMachine::getUnknownEvent()); + break; + case mux_state::MuxState::Label::Error: + mMuxStateMachine.postMuxStateEvent(mux_state::MuxStateMachine::getErrorEvent()); + break; + default: + break; + } +} } /* namespace link_manager */ diff --git a/src/link_manager/LinkManagerStateMachineBase.h b/src/link_manager/LinkManagerStateMachineBase.h index a857facc..ed09d1f5 100644 --- a/src/link_manager/LinkManagerStateMachineBase.h +++ b/src/link_manager/LinkManagerStateMachineBase.h @@ -32,6 +32,41 @@ #include "mux_state/MuxState.h" #include "mux_state/MuxStateMachine.h" +#define LOG_MUX_STATE_TRANSITION(level, portName, currentState, nextState) \ + do { \ + MUXLOG##level(boost::format("%s: (P: %s, M: %s, L: %s) -> (P: %s, M: %s, L: %s)") % \ + portName % \ + mLinkProberStateName[ps(currentState)] % \ + mMuxStateName[ms(currentState)] % \ + mLinkStateName[ls(currentState)] % \ + mLinkProberStateName[ps(nextState)] % \ + mMuxStateName[ms(nextState)] % \ + mLinkStateName[ls(nextState)] \ + ); \ + } while (0) + +#define LOGWARNING_MUX_STATE_TRANSITION(portName, currentState, nextState) \ + LOG_MUX_STATE_TRANSITION(WARNING, portName, currentState, nextState) + +#define LOGINFO_MUX_STATE_TRANSITION(portName, currentState, nextState) \ + LOG_MUX_STATE_TRANSITION(INFO, portName, currentState, nextState) + +#define MUXLOGTIMEOUT(portname, msg, currentState) \ + do { \ + MUXLOGWARNING(boost::format("%s: %s, current state: (P: %s, M: %s, L: %s)") % \ + portname % \ + msg % \ + mLinkProberStateName[ps(currentState)] % \ + mMuxStateName[ms(currentState)] % \ + mLinkStateName[ls(currentState)] \ + ); \ + } while (0) + +namespace test { +class FakeMuxPort; +class MuxManagerTest; +} + namespace mux { #define ps(compositeState) std::get<0>(compositeState) #define ms(compositeState) std::get<1>(compositeState) @@ -42,6 +77,7 @@ class MuxPort; namespace link_manager { class ActiveStandbyStateMachine; +class ActiveActiveStateMachine; /** *@class LinkProberEvent @@ -248,6 +284,16 @@ class LinkManagerStateMachineBase : public common::StateMachine { */ virtual void handleSwssBladeIpv4AddressUpdate(boost::asio::ip::address address); + /** + *@method handleSwssSoCIpv4AddressUpdate + * + *@brief initialize LinkProber component. Note if this is the last component to be initialized, + * state machine will be activated + * + *@return none + */ + virtual void handleSwssSoCIpv4AddressUpdate(boost::asio::ip::address address); + /** *@method handleGetServerMacNotification * @@ -314,6 +360,17 @@ class LinkManagerStateMachineBase : public common::StateMachine { */ virtual void handlePeerLinkStateNotification(const link_state::LinkState::Label label); + /** + *@method handlePeerMuxStateNotification + * + *@brief handle peer MUX state notification + * + *@param label (in) new peer MuxState label + * + *@return none + */ + virtual void handlePeerMuxStateNotification(mux_state::MuxState::Label label); + /** *@method handleMuxConfigNotification * @@ -393,17 +450,6 @@ class LinkManagerStateMachineBase : public common::StateMachine { */ virtual void handleResetLinkProberPckLossCount(); - /** - *@method setComponentInitState - * - *@brief set component inti state. This method is used for testing - * - *@param component (in) component index - * - *@return none - */ - virtual void setComponentInitState(uint8_t component); - public: /** *@method getLinkProberStateMachinePtr @@ -433,7 +479,25 @@ class LinkManagerStateMachineBase : public common::StateMachine { link_state::LinkStateMachine& getLinkStateMachine() {return mLinkStateMachine;}; private: + /** + *@enum anonymous + * + *@brief used to reference bits corresponding to respective state machine init state + */ + enum { + LinkProberComponent, + MuxStateComponent, + LinkStateComponent, + + ComponentCount + }; + +private: + friend class mux::MuxPort; + friend class test::FakeMuxPort; + friend class test::MuxManagerTest; friend class ActiveStandbyStateMachine; + friend class ActiveActiveStateMachine; private: /** @@ -450,10 +514,33 @@ class LinkManagerStateMachineBase : public common::StateMachine { * * @brief NO-OP transition function * - * @param nextState reference to CompositeState object + * @param nextState reference to CompositeState object */ void noopTransitionFunction(CompositeState& nextState); + /** + *@method setComponentInitState + * + *@brief set component inti state. This method is used for testing + * + *@param component (in) component index + * + *@return none + */ + void setComponentInitState(uint8_t component) {mComponentInitState.set(component);}; + + /** + *@method postMuxStateEvent + * + *@brief post event to MUX state machine to change state + * + *@param label (in) new state label to post event for + * + * + *@return none + */ + void postMuxStateEvent(mux_state::MuxState::Label label); + private: static LinkProberEvent mLinkProberEvent; static MuxStateEvent mMuxStateEvent; @@ -478,6 +565,8 @@ class LinkManagerStateMachineBase : public common::StateMachine { link_state::LinkStateMachine mLinkStateMachine; Label mLabel = Label::Uninitialized; + + std::bitset mComponentInitState = {0}; }; } /* namespace link_manager */ diff --git a/src/link_manager/subdir.mk b/src/link_manager/subdir.mk index fdbe3018..0836852c 100644 --- a/src/link_manager/subdir.mk +++ b/src/link_manager/subdir.mk @@ -2,15 +2,17 @@ CPP_SRCS += \ ./src/link_manager/LinkManagerStateMachineBase.cpp \ ./src/link_manager/LinkManagerStateMachineActiveStandby.cpp \ + ./src/link_manager/LinkManagerStateMachineActiveActive.cpp \ OBJS += \ ./src/link_manager/LinkManagerStateMachineBase.o \ ./src/link_manager/LinkManagerStateMachineActiveStandby.o \ - + ./src/link_manager/LinkManagerStateMachineActiveActive.o \ CPP_DEPS += \ ./src/link_manager/LinkManagerStateMachineBase.d \ ./src/link_manager/LinkManagerStateMachineActiveStandby.d \ + ./src/link_manager/LinkManagerStateMachineActiveActive.d \ # Each subdirectory must supply rules for building sources it contributes src/link_manager/%.o: src/link_manager/%.cpp diff --git a/src/link_prober/LinkProberState.h b/src/link_prober/LinkProberState.h index e2f88ac8..8c87d2c1 100644 --- a/src/link_prober/LinkProberState.h +++ b/src/link_prober/LinkProberState.h @@ -54,6 +54,7 @@ class LinkProberState: public common::State Standby, Unknown, Wait, + PeerWait, PeerActive, PeerUnknown, Count diff --git a/src/link_prober/LinkProberStateMachineActiveActive.cpp b/src/link_prober/LinkProberStateMachineActiveActive.cpp index 0953dc92..f4981a2c 100644 --- a/src/link_prober/LinkProberStateMachineActiveActive.cpp +++ b/src/link_prober/LinkProberStateMachineActiveActive.cpp @@ -42,7 +42,7 @@ LinkProberStateMachineActiveActive::LinkProberStateMachineActiveActive( : LinkProberStateMachineBase(linkManagerStateMachinePtr, strand, muxPortConfig) { enterState(label); - enterPeerState(LinkProberState::Label::PeerUnknown); + enterPeerState(LinkProberState::Label::PeerWait); } // @@ -60,11 +60,24 @@ void LinkProberStateMachineActiveActive::enterState(LinkProberState::Label label case LinkProberState::Label::Unknown: setCurrentState(dynamic_cast(getUnknownState())); break; + case LinkProberState::Label::Wait: + setCurrentState(dynamic_cast(getWaitState())); + break; default: break; } } +// +// ---> getCurrentPeerState(); +// +// getter for current peer state +// +LinkProberState *LinkProberStateMachineActiveActive::getCurrentPeerState() +{ + return mCurrentPeerState; +} + // // ---> enterPeerState(LinkProberState::Label label); // @@ -80,6 +93,9 @@ void LinkProberStateMachineActiveActive::enterPeerState(LinkProberState::Label l case LinkProberState::Label::PeerUnknown: setCurrentPeerState(dynamic_cast(getPeerUnknownState())); break; + case LinkProberState::Label::PeerWait: + setCurrentPeerState(dynamic_cast(getPeerWaitState())); + break; default: break; } @@ -148,6 +164,21 @@ void LinkProberStateMachineActiveActive::processEvent(IcmpPeerUnknownEvent &Icmp } } +// +// ---> processEvent(SuspendTimerExpiredEvent &suspendTimerExpiredEvent); +// +// process LinkProberState suspend timer expiry event +// +void LinkProberStateMachineActiveActive::processEvent(SuspendTimerExpiredEvent &suspendTimerExpiredEvent) +{ + boost::asio::io_service::strand &strand = mLinkManagerStateMachinePtr->getStrand(); + boost::asio::io_service &ioService = strand.context(); + ioService.post(strand.wrap(boost::bind( + &link_manager::LinkManagerStateMachineBase::handleSuspendTimerExpiry, + mLinkManagerStateMachinePtr + ))); +} + // // ---> postLinkManagerPeerEvent(LinkProberState* linkProberState); // diff --git a/src/link_prober/LinkProberStateMachineActiveActive.h b/src/link_prober/LinkProberStateMachineActiveActive.h index 347b527e..cdc9ac75 100644 --- a/src/link_prober/LinkProberStateMachineActiveActive.h +++ b/src/link_prober/LinkProberStateMachineActiveActive.h @@ -107,17 +107,16 @@ class LinkProberStateMachineActiveActive : public LinkProberStateMachineBase */ void processEvent(IcmpPeerUnknownEvent &icmpPeerUnknownEvent) override; -private: /** - *@method enterPeerState + *@method processEvent * - *@brief force the state machine to enter a given peer state + *@brief process SuspendTimerExpiredEvent * - *@param label (in) label of target peer state + *@param suspendTimerExpiredEvent (in) reference to the SuspendTimerExpiredEvent event * *@return none */ - void enterPeerState(LinkProberState::Label label); + void processEvent(SuspendTimerExpiredEvent &suspendTimerExpiredEvent) override; /** *@method getCurrentPeerState @@ -126,8 +125,20 @@ class LinkProberStateMachineActiveActive : public LinkProberStateMachineBase * *@return current peer state of the state machine */ - LinkProberState *getCurrentPeerState() { return mCurrentPeerState; } + LinkProberState *getCurrentPeerState() override; + + /** + *@method enterPeerState + * + *@brief force the state machine to enter a given peer state + * + *@param label (in) label of target peer state + * + *@return none + */ + void enterPeerState(LinkProberState::Label label) override; +private: /** *@method setCurrentPeerState * diff --git a/src/link_prober/LinkProberStateMachineBase.cpp b/src/link_prober/LinkProberStateMachineBase.cpp index 8f8b9873..57f48404 100644 --- a/src/link_prober/LinkProberStateMachineBase.cpp +++ b/src/link_prober/LinkProberStateMachineBase.cpp @@ -48,7 +48,8 @@ LinkProberStateMachineBase::LinkProberStateMachineBase( mUnknownState(*this, muxPortConfig), mWaitState(*this, muxPortConfig), mPeerActiveState(*this, muxPortConfig), - mPeerUnknownState(*this, muxPortConfig) + mPeerUnknownState(*this, muxPortConfig), + mPeerWaitState(*this, muxPortConfig) { } @@ -229,6 +230,27 @@ void LinkProberStateMachineBase::handlePckLossRatioUpdate(const uint64_t unknown MUXLOGERROR(mMuxPortConfig.getPortName()); } +// +// ---> getCurrentPeerState(); +// +// getter for current peer state +// +LinkProberState *LinkProberStateMachineBase::getCurrentPeerState() +{ + MUXLOGERROR(mMuxPortConfig.getPortName()); + return nullptr; +} + +// +// ---> enterPeerState(LinkProberState::Label label); +// +// force the state machine to enter a given peer state +// +void LinkProberStateMachineBase::enterPeerState(LinkProberState::Label label) +{ + MUXLOGERROR(mMuxPortConfig.getPortName()); +} + // // ---> postLinkManagerEvent(LinkProberState* linkProberState); // diff --git a/src/link_prober/LinkProberStateMachineBase.h b/src/link_prober/LinkProberStateMachineBase.h index 55e76b1f..b700b3a7 100644 --- a/src/link_prober/LinkProberStateMachineBase.h +++ b/src/link_prober/LinkProberStateMachineBase.h @@ -20,6 +20,7 @@ #include "link_prober/ActiveState.h" #include "link_prober/PeerActiveState.h" #include "link_prober/PeerUnknownState.h" +#include "link_prober/PeerWaitState.h" #include "link_prober/StandbyState.h" #include "link_prober/UnknownState.h" #include "link_prober/WaitState.h" @@ -290,6 +291,27 @@ class LinkProberStateMachineBase : public common::StateMachine */ virtual void handlePckLossRatioUpdate(const uint64_t unknownEventCount, const uint64_t expectedPacketCount); +public: + /** + *@method getCurrentPeerState + * + *@brief getter for current peer state + * + *@return current peer state of the state machine + */ + virtual LinkProberState *getCurrentPeerState(); + + /** + *@method enterPeerState + * + *@brief force the state machine to enter a given peer state + * + *@param label (in) label of target peer state + * + *@return none + */ + virtual void enterPeerState(LinkProberState::Label label); + public: /** *@method getActiveState @@ -345,6 +367,15 @@ class LinkProberStateMachineBase : public common::StateMachine */ PeerUnknownState* getPeerUnknownState() {return &mPeerUnknownState;}; + /** + *@method getPeerWaitState + * + *@brief getter for PeerWaitState object + * + *@return pointer to PeerWaitState object + */ + PeerWaitState* getPeerWaitState() {return &mPeerWaitState;}; + public: /** *@method getIcmpSelfEvent @@ -452,6 +483,7 @@ class LinkProberStateMachineBase : public common::StateMachine WaitState mWaitState; PeerActiveState mPeerActiveState; PeerUnknownState mPeerUnknownState; + PeerWaitState mPeerWaitState; }; } // namespace link_prober diff --git a/src/link_prober/PeerWaitState.cpp b/src/link_prober/PeerWaitState.cpp new file mode 100644 index 00000000..8a076212 --- /dev/null +++ b/src/link_prober/PeerWaitState.cpp @@ -0,0 +1,97 @@ +/* + * Copyright 2021 (c) Microsoft Corporation. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "link_prober/PeerActiveState.h" +#include "link_prober/PeerWaitState.h" +#include "link_prober/PeerUnknownState.h" +#include "link_prober/LinkProberStateMachineBase.h" + +#include "common/MuxLogger.h" + +namespace link_prober +{ + +// +// ---> PeerWaitState(LinkProberStateMachineBase &stateMachine, common::MuxPortConfig &muxPortConfig); +// +// class constructor +// +PeerWaitState::PeerWaitState( + LinkProberStateMachineBase &stateMachine, + common::MuxPortConfig &muxPortConfig +) + : LinkProberState(stateMachine, muxPortConfig) +{ +} + +// +// ---> handleEvent(IcmpPeerActiveEvent &event); +// +// handle IcmpPeerActiveEvent from LinkProber +// +LinkProberState *PeerWaitState::handleEvent(IcmpPeerActiveEvent &event) +{ + MUXLOGDEBUG(getMuxPortConfig().getPortName()); + + LinkProberStateMachineBase *stateMachine = dynamic_cast(getStateMachine()); + LinkProberState *nextState; + + mPeerUnknownEvent = 0; + if (++mPeerActiveEvent >= getMuxPortConfig().getPositiveStateChangeRetryCount()) { + nextState = dynamic_cast(stateMachine->getPeerActiveState()); + } else { + nextState = dynamic_cast(stateMachine->getPeerWaitState()); + } + + return nextState; +} + +// +// ---> handleEvent(IcmpPeerUnknownEvent &event); +// +// handle IcmpPeerUnknownEvent from LinkProber +// +LinkProberState *PeerWaitState::handleEvent(IcmpPeerUnknownEvent &event) +{ + MUXLOGDEBUG(getMuxPortConfig().getPortName()); + + LinkProberStateMachineBase *stateMachine = dynamic_cast(getStateMachine()); + LinkProberState *nextState; + + mPeerActiveEvent = 0; + if (++mPeerUnknownEvent >= getMuxPortConfig().getNegativeStateChangeRetryCount()) { + nextState = dynamic_cast(stateMachine->getPeerUnknownState()); + } else { + nextState = dynamic_cast(stateMachine->getPeerWaitState()); + } + + return nextState; +} + +// +// ---> resetState(); +// +// reset current state attributes +// +void PeerWaitState::resetState() +{ + MUXLOGDEBUG(getMuxPortConfig().getPortName()); + + mPeerActiveEvent = 0; + mPeerUnknownEvent = 0; +} + +} /* namespace link_prober */ diff --git a/src/link_prober/PeerWaitState.h b/src/link_prober/PeerWaitState.h new file mode 100644 index 00000000..0ac38f6d --- /dev/null +++ b/src/link_prober/PeerWaitState.h @@ -0,0 +1,119 @@ +/* + * Copyright 2021 (c) Microsoft Corporation. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef LINK_PROBER_PEERWAITSTATE_H_ +#define LINK_PROBER_PEERWAITSTATE_H_ + +#include + +#include "LinkProberState.h" + +namespace link_prober +{ +class LinkProberStateMachineBase; + +/** + *@class PeerWaitState + * + *@brief maintains peer Wait state of LinkProber + */ +class PeerWaitState : public LinkProberState +{ +public: + /** + *@method PeerWaitState + * + *@brief class default constructor + */ + PeerWaitState() = delete; + + /** + *@method PeerWaitState + * + *@brief class copy constructor + * + *@param PeerWaitState (in) reference to PeerWaitState object to be copied + */ + PeerWaitState(const PeerWaitState &) = delete; + + /** + *@method PeerWaitState + * + *@brief class constructor + * + *@param stateMachine (in) reference to LinkProberStateMachineBase + *@param muxPortConfig (in) reference to MuxPortConfig object + */ + PeerWaitState( + LinkProberStateMachineBase &stateMachine, + common::MuxPortConfig &muxPortConfig + ); + + /** + *@method ~PeerWaitState + * + *@brief class destructor + */ + virtual ~PeerWaitState() = default; + + /** + *@method handleEvent + * + *@brief handle IcmpPeerActiveEvent from LinkProber + * + *@param event (in) reference to IcmpPeerActiveEvent + * + *@return pointer to next LinkProberState + */ + virtual LinkProberState *handleEvent(IcmpPeerActiveEvent &event) override; + + /** + *@method handleEvent + * + *@brief handle IcmpPeerUnknownEvent from LinkProber + * + *@param event (in) reference to IcmpPeerUnknownEvent + * + *@return pointer to next LinkProberState + */ + virtual LinkProberState *handleEvent(IcmpPeerUnknownEvent &event) override; + + /** + *@method resetState + * + *@brief reset current state attributes + * + *@return none + */ + virtual void resetState() override; + + /** + *@method getStateLabel + * + *@brief getter for LinkeProberState label + * + *@return LinkProberState Unknown label + */ + virtual LinkProberState::Label getStateLabel() override { return LinkProberState::Label::PeerWait; }; + +private: + uint8_t mPeerActiveEvent = 0; + uint8_t mPeerUnknownEvent = 0; +}; + +} /* namespace link_prober */ + +#endif /* LINK_PROBER_PEERWAITSTATE_H_ */ diff --git a/src/link_prober/WaitState.cpp b/src/link_prober/WaitState.cpp index b2e75e93..3e66339d 100644 --- a/src/link_prober/WaitState.cpp +++ b/src/link_prober/WaitState.cpp @@ -57,6 +57,7 @@ LinkProberState* WaitState::handleEvent(IcmpPeerEvent &event) LinkProberState *nextState; mSelfEventCount = 0; + mUnknownEventCount = 0; if (++mPeerEventCount >= getMuxPortConfig().getPositiveStateChangeRetryCount()) { nextState = dynamic_cast (stateMachine->getStandbyState()); } @@ -80,6 +81,7 @@ LinkProberState* WaitState::handleEvent(IcmpSelfEvent &event) LinkProberState *nextState; mPeerEventCount = 0; + mUnknownEventCount = 0; if (++mSelfEventCount >= getMuxPortConfig().getPositiveStateChangeRetryCount()) { nextState = dynamic_cast (stateMachine->getActiveState()); } @@ -101,8 +103,20 @@ LinkProberState* WaitState::handleEvent(IcmpUnknownEvent &event) LinkProberStateMachineBase *stateMachine = dynamic_cast (getStateMachine()); LinkProberState *nextState = dynamic_cast (stateMachine->getWaitState()); - - resetState(); + common::MuxPortConfig::PortCableType portCableType = stateMachine->getMuxPortConfig().getPortCableType(); + + switch (portCableType) { + case common::MuxPortConfig::PortCableType::ActiveActive: + if (++mUnknownEventCount >= getMuxPortConfig().getNegativeStateChangeRetryCount()) { + nextState = dynamic_cast (stateMachine->getUnknownState()); + } + break; + case common::MuxPortConfig::PortCableType::ActiveStandby: + resetState(); + break; + default: + break; + } return nextState; } @@ -117,6 +131,7 @@ void WaitState::resetState() MUXLOGDEBUG(getMuxPortConfig().getPortName()); mSelfEventCount = 0; + mUnknownEventCount = 0; mPeerEventCount = 0; } diff --git a/src/link_prober/WaitState.h b/src/link_prober/WaitState.h index 3c59608a..8cce5d9e 100644 --- a/src/link_prober/WaitState.h +++ b/src/link_prober/WaitState.h @@ -130,6 +130,7 @@ class WaitState : public LinkProberState private: uint8_t mSelfEventCount = 0; uint8_t mPeerEventCount = 0; + uint8_t mUnknownEventCount = 0; }; } /* namespace link_prober */ diff --git a/src/link_prober/subdir.mk b/src/link_prober/subdir.mk index 8ebce0b8..2c65a917 100644 --- a/src/link_prober/subdir.mk +++ b/src/link_prober/subdir.mk @@ -3,6 +3,7 @@ CPP_SRCS += \ ./src/link_prober/ActiveState.cpp \ ./src/link_prober/PeerActiveState.cpp \ ./src/link_prober/PeerUnknownState.cpp \ + ./src/link_prober/PeerWaitState.cpp \ ./src/link_prober/IcmpPayload.cpp \ ./src/link_prober/LinkProber.cpp \ ./src/link_prober/LinkProberState.cpp \ @@ -17,6 +18,7 @@ OBJS += \ ./src/link_prober/ActiveState.o \ ./src/link_prober/PeerActiveState.o \ ./src/link_prober/PeerUnknownState.o \ + ./src/link_prober/PeerWaitState.o \ ./src/link_prober/IcmpPayload.o \ ./src/link_prober/LinkProber.o \ ./src/link_prober/LinkProberState.o \ @@ -31,6 +33,7 @@ CPP_DEPS += \ ./src/link_prober/ActiveState.d \ ./src/link_prober/PeerActiveState.d \ ./src/link_prober/PeerUnknownState.d \ + ./src/link_prober/PeerWaitState.d \ ./src/link_prober/IcmpPayload.d \ ./src/link_prober/LinkProber.d \ ./src/link_prober/LinkProberState.d \ diff --git a/test/FakeDbInterface.cpp b/test/FakeDbInterface.cpp index 42cc48a2..cf52fc9a 100644 --- a/test/FakeDbInterface.cpp +++ b/test/FakeDbInterface.cpp @@ -40,9 +40,16 @@ FakeDbInterface::FakeDbInterface(mux::MuxManager *muxManager, boost::asio::io_se void FakeDbInterface::setMuxState(const std::string &portName, mux_state::MuxState::Label label) { + mLastSetMuxState = label; mSetMuxStateInvokeCount++; } +void FakeDbInterface::setPeerMuxState(const std::string &portName, mux_state::MuxState::Label label) +{ + mLastSetPeerMuxState = label; + mSetPeerMuxStateInvokeCount++; +} + void FakeDbInterface::getMuxState(const std::string &portName) { mGetMuxStateInvokeCount++; diff --git a/test/FakeDbInterface.h b/test/FakeDbInterface.h index cf51b600..e1a4c662 100644 --- a/test/FakeDbInterface.h +++ b/test/FakeDbInterface.h @@ -37,6 +37,7 @@ class FakeDbInterface: public mux::DbInterface virtual ~FakeDbInterface() = default; virtual void setMuxState(const std::string &portName, mux_state::MuxState::Label label) override; + virtual void setPeerMuxState(const std::string &portName, mux_state::MuxState::Label label) override; virtual void getMuxState(const std::string &portName) override; virtual void probeMuxState(const std::string &portName) override; virtual void setMuxLinkmgrState( @@ -67,7 +68,11 @@ class FakeDbInterface: public mux::DbInterface uint32_t mMuxStateRequest[mux_state::MuxState::Label::Count] = {0, 0, 0, 0}; + mux_state::MuxState::Label mLastSetMuxState; + mux_state::MuxState::Label mLastSetPeerMuxState; + uint32_t mSetMuxStateInvokeCount = 0; + uint32_t mSetPeerMuxStateInvokeCount = 0; uint32_t mGetMuxStateInvokeCount = 0; uint32_t mProbeMuxStateInvokeCount = 0; uint32_t mSetMuxLinkmgrStateInvokeCount = 0; diff --git a/test/FakeLinkProber.cpp b/test/FakeLinkProber.cpp index d107e056..cfb9f3ff 100644 --- a/test/FakeLinkProber.cpp +++ b/test/FakeLinkProber.cpp @@ -42,12 +42,11 @@ void FakeLinkProber::postLinkProberEvent(E &e) { boost::asio::io_service::strand& strand = mLinkProberStateMachine->getStrand(); boost::asio::io_service &ioService = strand.context(); - ioService.post(strand.wrap(boost::bind( - static_cast - (&link_prober::LinkProberStateMachineBase::processEvent), - mLinkProberStateMachine, - e - ))); + ioService.post( + strand.wrap( + [this, &e]() { mLinkProberStateMachine->processEvent(e); } + ) + ); } template @@ -59,6 +58,12 @@ void FakeLinkProber::postLinkProberEvent(link_probe template void FakeLinkProber::postLinkProberEvent(link_prober::IcmpUnknownEvent &event); +template +void FakeLinkProber::postLinkProberEvent(link_prober::IcmpPeerActiveEvent &event); + +template +void FakeLinkProber::postLinkProberEvent(link_prober::IcmpPeerUnknownEvent &event); + void FakeLinkProber::postSuspendTimerExpiredEvent() { // inform the composite state machine about Suspend timer expiry diff --git a/test/FakeMuxPort.cpp b/test/FakeMuxPort.cpp index 84d8cbef..eae277f6 100644 --- a/test/FakeMuxPort.cpp +++ b/test/FakeMuxPort.cpp @@ -35,24 +35,70 @@ FakeMuxPort::FakeMuxPort( common::MuxConfig &muxConfig, std::string &portName, uint16_t serverId, - boost::asio::io_service &ioService -) : - mux::MuxPort( - dbInterface, - muxConfig, - portName, - serverId, - ioService, - common::MuxPortConfig::PortCableType::ActiveStandby - ), - mActiveStandbyStateMachinePtr( - std::dynamic_pointer_cast(getLinkManagerStateMachinePtr()) - ), - mFakeLinkProber( - std::make_shared (mActiveStandbyStateMachinePtr->getLinkProberStateMachinePtr().get()) - ) + boost::asio::io_service &ioService, + common::MuxPortConfig::PortCableType portCableType +) + : mux::MuxPort( + dbInterface, + muxConfig, + portName, + serverId, + ioService, + portCableType + ), + mActiveActiveStateMachinePtr( + std::dynamic_pointer_cast(getLinkManagerStateMachinePtr()) + ), + mActiveStandbyStateMachinePtr( + std::dynamic_pointer_cast(getLinkManagerStateMachinePtr()) + ), + mFakeLinkProber( + std::make_shared(getLinkManagerStateMachinePtr()->getLinkProberStateMachinePtr().get()) + ) { mMuxPortConfig.setMode(common::MuxPortConfig::Mode::Auto); + switch (portCableType) { + case common::MuxPortConfig::PortCableType::ActiveStandby: + initLinkProberActiveStandby(); + break; + case common::MuxPortConfig::PortCableType::ActiveActive: + initLinkProberActiveActive(); + break; + default: + break; + } +} + +inline void FakeMuxPort::initLinkProberActiveActive() +{ + getActiveActiveStateMachinePtr()->setInitializeProberFnPtr( + boost::bind(&FakeLinkProber::initialize, mFakeLinkProber.get()) + ); + getActiveActiveStateMachinePtr()->setStartProbingFnPtr( + boost::bind(&FakeLinkProber::startProbing, mFakeLinkProber.get()) + ); + getActiveActiveStateMachinePtr()->setUpdateEthernetFrameFnPtr( + boost::bind(&FakeLinkProber::updateEthernetFrame, mFakeLinkProber.get()) + ); + getActiveActiveStateMachinePtr()->setProbePeerTorFnPtr( + boost::bind(&FakeLinkProber::probePeerTor, mFakeLinkProber.get()) + ); + getActiveActiveStateMachinePtr()->setSuspendTxFnPtr( + boost::bind(&FakeLinkProber::suspendTxProbes, mFakeLinkProber.get(), boost::placeholders::_1) + ); + getActiveActiveStateMachinePtr()->setResumeTxFnPtr( + boost::bind(&FakeLinkProber::resumeTxProbes, mFakeLinkProber.get()) + ); + getActiveActiveStateMachinePtr()->setShutdownTxFnPtr( + boost::bind(&FakeLinkProber::shutdownTxProbes, mFakeLinkProber.get()) + ); + getActiveActiveStateMachinePtr()->setRestartTxFnPtr( + boost::bind(&FakeLinkProber::restartTxProbes, mFakeLinkProber.get()) + ); +} + +inline void FakeMuxPort::initLinkProberActiveStandby() +{ getActiveStandbyStateMachinePtr()->setInitializeProberFnPtr( boost::bind(&FakeLinkProber::initialize, mFakeLinkProber.get()) ); diff --git a/test/FakeMuxPort.h b/test/FakeMuxPort.h index 49eb0b13..f2145843 100644 --- a/test/FakeMuxPort.h +++ b/test/FakeMuxPort.h @@ -41,20 +41,29 @@ class FakeMuxPort : public ::mux::MuxPort { common::MuxConfig& muxConfig, std::string& portName, uint16_t serverId, - boost::asio::io_service& ioService); + boost::asio::io_service& ioService, + common::MuxPortConfig::PortCableType portCableType = common::MuxPortConfig::PortCableType::ActiveStandby); virtual ~FakeMuxPort() = default; void activateStateMachine(); + std::shared_ptr getActiveActiveStateMachinePtr() { return mActiveActiveStateMachinePtr; } std::shared_ptr getActiveStandbyStateMachinePtr() { return mActiveStandbyStateMachinePtr; } - const link_manager::ActiveStandbyStateMachine::CompositeState& getCompositeState() { return getActiveStandbyStateMachinePtr()->getCompositeState(); }; - link_prober::LinkProberStateMachineBase* getLinkProberStateMachinePtr() { return getActiveStandbyStateMachinePtr()->getLinkProberStateMachinePtr().get(); }; - mux_state::MuxStateMachine& getMuxStateMachine() { return getActiveStandbyStateMachinePtr()->getMuxStateMachine(); }; - link_state::LinkStateMachine& getLinkStateMachine() { return getActiveStandbyStateMachinePtr()->getLinkStateMachine(); }; + const link_manager::ActiveStandbyStateMachine::CompositeState& getCompositeState() { return getLinkManagerStateMachinePtr()->getCompositeState(); }; + link_prober::LinkProberStateMachineBase* getLinkProberStateMachinePtr() { return getLinkManagerStateMachinePtr()->getLinkProberStateMachinePtr().get(); }; + mux_state::MuxStateMachine& getMuxStateMachine() { return getLinkManagerStateMachinePtr()->getMuxStateMachine(); }; + link_state::LinkStateMachine& getLinkStateMachine() { return getLinkManagerStateMachinePtr()->getLinkStateMachine(); }; bool getPendingMuxModeChange() { return getActiveStandbyStateMachinePtr()->mPendingMuxModeChange; }; common::MuxPortConfig::Mode getTargetMuxMode() { return getActiveStandbyStateMachinePtr()->mTargetMuxMode; }; + link_prober::LinkProberState::Label getPeerLinkProberState() { return getActiveActiveStateMachinePtr()->mPeerLinkProberState; }; + mux_state::MuxState::Label getPeerMuxState() { return getActiveActiveStateMachinePtr()->mPeerMuxState; }; + + inline void initLinkProberActiveActive(); + inline void initLinkProberActiveStandby(); + + std::shared_ptr mActiveActiveStateMachinePtr; std::shared_ptr mActiveStandbyStateMachinePtr; std::shared_ptr mFakeLinkProber; }; diff --git a/test/LinkManagerStateMachineActiveActiveTest.cpp b/test/LinkManagerStateMachineActiveActiveTest.cpp new file mode 100644 index 00000000..949fe08f --- /dev/null +++ b/test/LinkManagerStateMachineActiveActiveTest.cpp @@ -0,0 +1,398 @@ +/* + * Copyright 2021 (c) Microsoft Corporation. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "LinkManagerStateMachineActiveActiveTest.h" +#include "link_prober/LinkProberStateMachineBase.h" + +#define VALIDATE_STATE(p, m, l) \ + do { \ + mTestCompositeState = mFakeMuxPort.getCompositeState(); \ + EXPECT_EQ(ps(mTestCompositeState), link_prober::LinkProberState::Label::p); \ + EXPECT_EQ(ms(mTestCompositeState), mux_state::MuxState::Label::m); \ + EXPECT_EQ(ls(mTestCompositeState), link_state::LinkState::Label::l); \ + } while (0) + +#define VALIDATE_PEER_STATE(p, m) \ + do { \ + EXPECT_EQ(mFakeMuxPort.getPeerLinkProberState(), link_prober::LinkProberState::Label::p); \ + EXPECT_EQ(mFakeMuxPort.getPeerMuxState(), mux_state::MuxState::Label::m); \ + } while (0) + +namespace test +{ + +LinkManagerStateMachineActiveActiveTest::LinkManagerStateMachineActiveActiveTest() + : mDbInterfacePtr(std::make_shared(&mIoService)), + mFakeMuxPort( + mDbInterfacePtr, + mMuxConfig, + mPortName, + mServerId, + mIoService, + mPortCableType + ) +{ + mMuxConfig.setTimeoutIpv4_msec(10); + mMuxConfig.setPositiveStateChangeRetryCount(mPositiveUpdateCount); + mMuxConfig.setMuxStateChangeRetryCount(mPositiveUpdateCount); + mMuxConfig.setLinkStateChangeRetryCount(mPositiveUpdateCount); +} + +void LinkManagerStateMachineActiveActiveTest::runIoService(uint32_t count) +{ + if (count == 0) { + if (mIoService.stopped()) { + mIoService.restart(); + } + mIoService.run(); + } + + for (uint8_t i = 0; i < count; i++) { + if (mIoService.stopped()) { + mIoService.restart(); + } + mIoService.run_one(); + } +} + +void LinkManagerStateMachineActiveActiveTest::pollIoService(uint32_t count) +{ + if (count == 0) { + if (mIoService.stopped()) { + mIoService.restart(); + } + mIoService.poll(); + } + + for (uint8_t i = 0; i < count; i++) { + if (mIoService.stopped()) { + mIoService.restart(); + } + mIoService.poll_one(); + } +} + +void LinkManagerStateMachineActiveActiveTest::postLinkProberEvent(link_prober::LinkProberState::Label label, uint32_t count) +{ + switch (label) { + case link_prober::LinkProberState::Active: + for (uint8_t i = 0; i < mMuxConfig.getPositiveStateChangeRetryCount(); i++) { + mFakeMuxPort.mFakeLinkProber->postLinkProberEvent( + link_prober::LinkProberStateMachineBase::getIcmpSelfEvent() + ); + runIoService(count); + } + break; + case link_prober::LinkProberState::Unknown: + for (uint8_t i = 0; i < mMuxConfig.getNegativeStateChangeRetryCount(); i++) { + mFakeMuxPort.mFakeLinkProber->postLinkProberEvent( + link_prober::LinkProberStateMachineBase::getIcmpUnknownEvent() + ); + runIoService(count); + } + break; + default: + break; + } +} + +void LinkManagerStateMachineActiveActiveTest::postPeerLinkProberEvent(link_prober::LinkProberState::Label label, uint32_t count) +{ + switch (label) { + case link_prober::LinkProberState::PeerActive: + for (uint8_t i = 0; i < mMuxConfig.getPositiveStateChangeRetryCount(); i++) { + mFakeMuxPort.mFakeLinkProber->postLinkProberEvent( + link_prober::LinkProberStateMachineBase::getIcmpPeerActiveEvent() + ); + runIoService(count); + } + break; + case link_prober::LinkProberState::PeerUnknown: + for (uint8_t i = 0; i < mMuxConfig.getNegativeStateChangeRetryCount(); i++) { + mFakeMuxPort.mFakeLinkProber->postLinkProberEvent( + link_prober::LinkProberStateMachineBase::getIcmpPeerUnknownEvent() + ); + runIoService(count); + } + break; + default: + break; + } +} + +void LinkManagerStateMachineActiveActiveTest::postMuxEvent(mux_state::MuxState::Label label, uint32_t count) +{ + mux_state::MuxStateMachine& muxStateMachine = mFakeMuxPort.getMuxStateMachine(); + for (uint8_t i = 0; i < mMuxConfig.getMuxStateChangeRetryCount(); i++) { + switch (label) { + case mux_state::MuxState::Active: + muxStateMachine.postMuxStateEvent(mux_state::MuxStateMachine::getActiveEvent()); + break; + case mux_state::MuxState::Standby: + muxStateMachine.postMuxStateEvent(mux_state::MuxStateMachine::getStandbyEvent()); + break; + case mux_state::MuxState::Unknown: + muxStateMachine.postMuxStateEvent(mux_state::MuxStateMachine::getUnknownEvent()); + break; + case mux_state::MuxState::Error: + muxStateMachine.postMuxStateEvent(mux_state::MuxStateMachine::getErrorEvent()); + break; + default: + break; + } + runIoService(count); + } +} + +void LinkManagerStateMachineActiveActiveTest::postLinkEvent(link_state::LinkState::Label label, uint32_t count) +{ + link_state::LinkStateMachine& linkStateMachine = mFakeMuxPort.getLinkStateMachine(); + for (uint8_t i = 0; i < mMuxConfig.getLinkStateChangeRetryCount(); i++) { + switch (label) { + case link_state::LinkState::Up: + linkStateMachine.postLinkStateEvent(link_state::LinkStateMachine::getUpEvent()); + break; + case link_state::LinkState::Down: + linkStateMachine.postLinkStateEvent(link_state::LinkStateMachine::getDownEvent()); + break; + default: + break; + } + runIoService(count); + } +} + +void LinkManagerStateMachineActiveActiveTest::handleMuxState(std::string state, uint32_t count) +{ + for (uint8_t i = 0; i < mPositiveUpdateCount; i++) { + mFakeMuxPort.handleMuxState(state); + runIoService(count); + } +} + +void LinkManagerStateMachineActiveActiveTest::handlePeerMuxState(std::string state, uint32_t count) +{ + for (uint8_t i = 0; i < mPositiveUpdateCount; i++) { + mFakeMuxPort.handlePeerMuxState(state); + runIoService(count); + } +} + +void LinkManagerStateMachineActiveActiveTest::handleProbeMuxState(std::string state, uint32_t count) +{ + for (uint8_t i = 0; i < mPositiveUpdateCount; i++) { + mFakeMuxPort.handleProbeMuxState(state); + runIoService(count); + } +} + +void LinkManagerStateMachineActiveActiveTest::handleLinkState(std::string linkState, uint32_t count) +{ + for (uint8_t i = 0; i < mMuxConfig.getLinkStateChangeRetryCount(); i++) { + mFakeMuxPort.handleLinkState(linkState); + runIoService(count); + } +} + +void LinkManagerStateMachineActiveActiveTest::handleMuxConfig(std::string config, uint32_t count) +{ + mFakeMuxPort.handleMuxConfig(config); + runIoService(count); +} + +void LinkManagerStateMachineActiveActiveTest::activateStateMachine() +{ + mFakeMuxPort.activateStateMachine(); +} + +void LinkManagerStateMachineActiveActiveTest::setMuxActive() +{ + activateStateMachine(); + VALIDATE_STATE(Wait, Wait, Down); + + postLinkEvent(link_state::LinkState::Up); + VALIDATE_STATE(Wait, Wait, Up); + + postLinkProberEvent(link_prober::LinkProberState::Active, 3); + VALIDATE_STATE(Active, Active, Up); + EXPECT_EQ(mDbInterfacePtr->mSetMuxStateInvokeCount, 1); + EXPECT_EQ(mDbInterfacePtr->mLastSetMuxState, mux_state::MuxState::Label::Active); + + handleMuxState("active", 3); + VALIDATE_STATE(Active, Active, Up); +} + +void LinkManagerStateMachineActiveActiveTest::setMuxStandby() +{ + activateStateMachine(); + VALIDATE_STATE(Wait, Wait, Down); + + postLinkEvent(link_state::LinkState::Up); + VALIDATE_STATE(Wait, Wait, Up); + + postLinkProberEvent(link_prober::LinkProberState::Unknown, 3); + VALIDATE_STATE(Unknown, Standby, Up); + EXPECT_EQ(mDbInterfacePtr->mSetMuxStateInvokeCount, 1); + EXPECT_EQ(mDbInterfacePtr->mLastSetMuxState, mux_state::MuxState::Label::Standby); + + handleMuxState("standby", 3); + VALIDATE_STATE(Unknown, Standby, Up); +} + +TEST_F(LinkManagerStateMachineActiveActiveTest, MuxActive) +{ + setMuxActive(); +} + +TEST_F(LinkManagerStateMachineActiveActiveTest, MuxActiveLinkProberUnknown) +{ + setMuxActive(); + + postLinkProberEvent(link_prober::LinkProberState::Unknown, 3); + EXPECT_EQ(mDbInterfacePtr->mSetMuxStateInvokeCount, 2); + EXPECT_EQ(mDbInterfacePtr->mLastSetMuxState, mux_state::MuxState::Label::Standby); + EXPECT_EQ(mFakeMuxPort.mFakeLinkProber->mSuspendTxProbeCallCount, 1); + VALIDATE_STATE(Unknown, Standby, Up); + + handleMuxState("standby", 3); + VALIDATE_STATE(Unknown, Standby, Up); + + postLinkProberEvent(link_prober::LinkProberState::Active, 3); + VALIDATE_STATE(Active, Active, Up); + EXPECT_EQ(mDbInterfacePtr->mSetMuxStateInvokeCount, 3); + EXPECT_EQ(mDbInterfacePtr->mLastSetMuxState, mux_state::MuxState::Label::Active); +} + +TEST_F(LinkManagerStateMachineActiveActiveTest, MuxActiveLinkDown) +{ + setMuxActive(); + + postLinkEvent(link_state::LinkState::Label::Down, 2); + EXPECT_EQ(mDbInterfacePtr->mSetMuxStateInvokeCount, 2); + EXPECT_EQ(mDbInterfacePtr->mLastSetMuxState, mux_state::MuxState::Label::Standby); + VALIDATE_STATE(Active, Standby, Down); + + postLinkProberEvent(link_prober::LinkProberState::Unknown, 3); + VALIDATE_STATE(Unknown, Standby, Down); + + postLinkEvent(link_state::LinkState::Label::Up, 2); + VALIDATE_STATE(Unknown, Standby, Up); + + postLinkProberEvent(link_prober::LinkProberState::Active, 3); + VALIDATE_STATE(Active, Active, Up); + EXPECT_EQ(mDbInterfacePtr->mSetMuxStateInvokeCount, 3); + EXPECT_EQ(mDbInterfacePtr->mLastSetMuxState, mux_state::MuxState::Label::Active); + + handleMuxState("active", 3); + VALIDATE_STATE(Active, Active, Up); +} + +TEST_F(LinkManagerStateMachineActiveActiveTest, MuxActiveConfigStandby) +{ + setMuxActive(); + + handleMuxConfig("standby", 1); + EXPECT_EQ(mDbInterfacePtr->mSetMuxStateInvokeCount, 2); + EXPECT_EQ(mDbInterfacePtr->mLastSetMuxState, mux_state::MuxState::Label::Standby); + VALIDATE_STATE(Active, Standby, Up); + + handleMuxState("standby", 3); + VALIDATE_STATE(Active, Standby, Up); + + postLinkProberEvent(link_prober::LinkProberState::Active, 3); + EXPECT_EQ(mDbInterfacePtr->mSetMuxStateInvokeCount, 2); + VALIDATE_STATE(Active, Standby, Up); + + handleMuxConfig("auto", 1); + handleProbeMuxState("standby", 3); + VALIDATE_STATE(Unknown, Standby, Up); + + postLinkProberEvent(link_prober::LinkProberState::Active, 3); + EXPECT_EQ(mDbInterfacePtr->mSetMuxStateInvokeCount, 3); + EXPECT_EQ(mDbInterfacePtr->mLastSetMuxState, mux_state::MuxState::Label::Active); + VALIDATE_STATE(Active, Active, Up); + + handleMuxState("active", 3); + VALIDATE_STATE(Active, Active, Up); +} + +TEST_F(LinkManagerStateMachineActiveActiveTest, MuxActiveLinkProberPeerActive) +{ + setMuxActive(); + + VALIDATE_PEER_STATE(PeerWait, Wait); + + postPeerLinkProberEvent(link_prober::LinkProberState::PeerActive); + VALIDATE_PEER_STATE(PeerActive, Active); + EXPECT_EQ(mDbInterfacePtr->mSetPeerMuxStateInvokeCount, 0); +} + +TEST_F(LinkManagerStateMachineActiveActiveTest, MuxActiveLinkProberPeerUnknown) +{ + setMuxActive(); + + VALIDATE_PEER_STATE(PeerWait, Wait); + + postPeerLinkProberEvent(link_prober::LinkProberState::PeerUnknown, 3); + VALIDATE_PEER_STATE(PeerUnknown, Standby); + EXPECT_EQ(mDbInterfacePtr->mSetPeerMuxStateInvokeCount, 1); + EXPECT_EQ(mDbInterfacePtr->mLastSetPeerMuxState, mux_state::MuxState::Label::Standby); + + handlePeerMuxState("standby", 1); + VALIDATE_PEER_STATE(PeerUnknown, Standby); +} + +TEST_F(LinkManagerStateMachineActiveActiveTest, MuxStandby) +{ + setMuxStandby(); +} + +TEST_F(LinkManagerStateMachineActiveActiveTest, MuxStandbyLinkDown) +{ + setMuxStandby(); + + postLinkEvent(link_state::LinkState::Label::Down, 2); + EXPECT_EQ(mDbInterfacePtr->mSetMuxStateInvokeCount, 1); + VALIDATE_STATE(Unknown, Standby, Down); + + postLinkEvent(link_state::LinkState::Label::Up, 2); + EXPECT_EQ(mDbInterfacePtr->mSetMuxStateInvokeCount, 1); + VALIDATE_STATE(Unknown, Standby, Up); +} + +TEST_F(LinkManagerStateMachineActiveActiveTest, MuxStandbyLinkProberPeerActive) +{ + setMuxStandby(); + + VALIDATE_PEER_STATE(PeerWait, Wait); + + postPeerLinkProberEvent(link_prober::LinkProberState::PeerActive); + VALIDATE_PEER_STATE(PeerActive, Active); + EXPECT_EQ(mDbInterfacePtr->mSetPeerMuxStateInvokeCount, 0); +} + +TEST_F(LinkManagerStateMachineActiveActiveTest, MuxStandbyLinkProberPeerUnknown) +{ + setMuxStandby(); + + VALIDATE_PEER_STATE(PeerWait, Wait); + + postPeerLinkProberEvent(link_prober::LinkProberState::PeerUnknown); + VALIDATE_PEER_STATE(PeerUnknown, Wait); + EXPECT_EQ(mDbInterfacePtr->mSetPeerMuxStateInvokeCount, 0); +} + +} /* namespace test */ diff --git a/test/LinkManagerStateMachineActiveActiveTest.h b/test/LinkManagerStateMachineActiveActiveTest.h new file mode 100644 index 00000000..6ebe714e --- /dev/null +++ b/test/LinkManagerStateMachineActiveActiveTest.h @@ -0,0 +1,67 @@ +/* + * Copyright 2021 (c) Microsoft Corporation. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef LINKMANAGERSTATEMACHINEACTIVEACTIVETEST_H_ +#define LINKMANAGERSTATEMACHINEACTIVEACTIVETEST_H_ + +#include "gtest/gtest.h" + +#include "FakeMuxPort.h" +#include "FakeLinkProber.h" + +namespace test +{ + +class LinkManagerStateMachineActiveActiveTest : public ::testing::Test +{ +public: + LinkManagerStateMachineActiveActiveTest(); + virtual ~LinkManagerStateMachineActiveActiveTest() = default; + + void runIoService(uint32_t count = 0); + void pollIoService(uint32_t count = 0); + void postLinkProberEvent(link_prober::LinkProberState::Label label, uint32_t count = 0); + void postPeerLinkProberEvent(link_prober::LinkProberState::Label label, uint32_t count = 0); + void postMuxEvent(mux_state::MuxState::Label label, uint32_t count = 0); + void postLinkEvent(link_state::LinkState::Label label, uint32_t count = 0); + void postSuspendTimerExpiredEvent(uint32_t count = 0); + void handleMuxState(std::string, uint32_t count = 0); + void handlePeerMuxState(std::string, uint32_t count = 0); + void handleProbeMuxState(std::string, uint32_t count = 0); + void handleLinkState(std::string linkState, uint32_t count = 0); + void handleMuxConfig(std::string config, uint32_t count = 0); + void activateStateMachine(); + void setMuxActive(); + void setMuxStandby(); + +public: + boost::asio::io_service mIoService; + common::MuxConfig mMuxConfig; + std::shared_ptr mDbInterfacePtr; + std::string mPortName = "Ethernet10"; + std::string mSmartNicIpAddress = "192.168.1.20"; + common::MuxPortConfig::PortCableType mPortCableType = common::MuxPortConfig::PortCableType::ActiveActive; + uint16_t mServerId = 10; + + FakeMuxPort mFakeMuxPort; + link_manager::ActiveStandbyStateMachine::CompositeState mTestCompositeState; + + uint8_t mPositiveUpdateCount = 2; +}; + +} /* namespace test */ + +#endif /* LINKMANAGERSTATEMACHINEACTIVEACTIVETEST_H_ */ diff --git a/test/MuxManagerTest.cpp b/test/MuxManagerTest.cpp index a14c13f8..3140b1dd 100644 --- a/test/MuxManagerTest.cpp +++ b/test/MuxManagerTest.cpp @@ -30,6 +30,10 @@ namespace test { +const std::string MuxManagerTest::PortName = "Ethernet0"; +const std::string MuxManagerTest::ServerAddress = "192.168.0.1"; +const std::string MuxManagerTest::SoCAddress = "192.168.0.2"; + MuxManagerTest::MuxManagerTest() : mMuxManagerPtr(std::make_shared ()), mDbInterfacePtr(std::make_shared (mMuxManagerPtr.get(), &mMuxManagerPtr->getIoService())), @@ -48,6 +52,14 @@ void MuxManagerTest::runIoService(uint32_t count) } } +void MuxManagerTest::pollIoService(uint32_t count) +{ + for (uint32_t i = 0; i < count; i++) { + mMuxManagerPtr->getIoService().poll_one(); + mMuxManagerPtr->getIoService().reset(); + } +} + common::MuxPortConfig::Mode MuxManagerTest::getMode(std::string port) { std::shared_ptr muxPortPtr = mMuxManagerPtr->mPortMap[port]; @@ -131,14 +143,17 @@ link_manager::LinkManagerStateMachineBase::CompositeState MuxManagerTest::getCom common::MuxPortConfig::PortCableType MuxManagerTest::getPortCableType(const std::string &port) { - return mMuxManagerPtr->mPortCableTypeMap.at(port); + return mMuxManagerPtr->getMuxPortCableType(port); } void MuxManagerTest::processServerIpAddress(std::vector &servers) { mDbInterfacePtr->processServerIpAddress(servers); +} - EXPECT_TRUE(mMuxManagerPtr->mPortMap.size() == 1); +void MuxManagerTest::processSoCIpAddress(std::vector &servers) +{ + mDbInterfacePtr->processSoCIpAddress(servers); } void MuxManagerTest::processServerMacAddress( @@ -185,71 +200,138 @@ void MuxManagerTest::updatePortCableType(const std::string &port, const std::str mMuxManagerPtr->updatePortCableType(port, cableType); } -void MuxManagerTest::createPort(std::string port) +void MuxManagerTest::initLinkProberActiveActive(std::shared_ptr linkManagerStateMachineActiveActive) { - EXPECT_TRUE(mMuxManagerPtr->mPortMap.size() == 0); - EXPECT_TRUE(mMuxManagerPtr->mPortCableTypeMap.size() == 0); - - updatePortCableType(port, "active-standby"); - EXPECT_TRUE(mMuxManagerPtr->mPortCableTypeMap.size() == 1); - - std::deque entries = { - {port, "SET", {{"oper_status", "up"}}}, - }; - - mDbInterfacePtr->processLinkStateNotifiction(entries); - std::shared_ptr muxPortPtr = mMuxManagerPtr->mPortMap["Ethernet0"]; - std::shared_ptr linkManagerStateMachine = std::dynamic_pointer_cast ( - muxPortPtr->getLinkManagerStateMachinePtr() + mFakeLinkProber = std::make_shared (linkManagerStateMachineActiveActive->getLinkProberStateMachinePtr().get()); + linkManagerStateMachineActiveActive->setInitializeProberFnPtr( + boost::bind(&FakeLinkProber::initialize, mFakeLinkProber.get()) + ); + linkManagerStateMachineActiveActive->setStartProbingFnPtr( + boost::bind(&FakeLinkProber::startProbing, mFakeLinkProber.get()) + ); + linkManagerStateMachineActiveActive->setUpdateEthernetFrameFnPtr( + boost::bind(&FakeLinkProber::updateEthernetFrame, mFakeLinkProber.get()) + ); + linkManagerStateMachineActiveActive->setProbePeerTorFnPtr( + boost::bind(&FakeLinkProber::probePeerTor, mFakeLinkProber.get()) + ); + linkManagerStateMachineActiveActive->setSuspendTxFnPtr( + boost::bind(&FakeLinkProber::suspendTxProbes, mFakeLinkProber.get(), boost::placeholders::_1) + ); + linkManagerStateMachineActiveActive->setResumeTxFnPtr( + boost::bind(&FakeLinkProber::resumeTxProbes, mFakeLinkProber.get()) + ); + linkManagerStateMachineActiveActive->setShutdownTxFnPtr( + boost::bind(&FakeLinkProber::shutdownTxProbes, mFakeLinkProber.get()) + ); + linkManagerStateMachineActiveActive->setRestartTxFnPtr( + boost::bind(&FakeLinkProber::restartTxProbes, mFakeLinkProber.get()) ); - EXPECT_TRUE(mMuxManagerPtr->mPortMap.size() == 1); - EXPECT_TRUE(linkManagerStateMachine->mComponentInitState.test(link_manager::ActiveStandbyStateMachine::LinkStateComponent) == 0); - - runIoService(); - - EXPECT_TRUE(linkManagerStateMachine->mComponentInitState.test(link_manager::ActiveStandbyStateMachine::LinkStateComponent) == 1); + linkManagerStateMachineActiveActive->mComponentInitState.set(0); +} - // Initialize a FakeLinkProber - mFakeLinkProber = std::make_shared (linkManagerStateMachine->getLinkProberStateMachinePtr().get()); - linkManagerStateMachine->setInitializeProberFnPtr( +void MuxManagerTest::initLinkProberActiveStandby(std::shared_ptr linkManagerStateMachineActiveStandby) +{ + mFakeLinkProber = std::make_shared (linkManagerStateMachineActiveStandby->getLinkProberStateMachinePtr().get()); + linkManagerStateMachineActiveStandby->setInitializeProberFnPtr( boost::bind(&FakeLinkProber::initialize, mFakeLinkProber.get()) ); - linkManagerStateMachine->setStartProbingFnPtr( + linkManagerStateMachineActiveStandby->setStartProbingFnPtr( boost::bind(&FakeLinkProber::startProbing, mFakeLinkProber.get()) ); - linkManagerStateMachine->setUpdateEthernetFrameFnPtr( + linkManagerStateMachineActiveStandby->setUpdateEthernetFrameFnPtr( boost::bind(&FakeLinkProber::updateEthernetFrame, mFakeLinkProber.get()) ); - linkManagerStateMachine->setSuspendTxFnPtr( + linkManagerStateMachineActiveStandby->setSuspendTxFnPtr( boost::bind(&FakeLinkProber::suspendTxProbes, mFakeLinkProber.get(), boost::placeholders::_1) ); - linkManagerStateMachine->setShutdownTxFnPtr( + linkManagerStateMachineActiveStandby->setShutdownTxFnPtr( boost::bind(&FakeLinkProber::shutdownTxProbes, mFakeLinkProber.get()) ); - linkManagerStateMachine->setRestartTxFnPtr( + linkManagerStateMachineActiveStandby->setRestartTxFnPtr( boost::bind(&FakeLinkProber::restartTxProbes, mFakeLinkProber.get()) ); - linkManagerStateMachine->setDecreaseIntervalFnPtr( + linkManagerStateMachineActiveStandby->setDecreaseIntervalFnPtr( boost::bind(&FakeLinkProber::decreaseProbeIntervalAfterSwitch, mFakeLinkProber.get(), boost::placeholders::_1) ); - linkManagerStateMachine->setRevertIntervalFnPtr( + linkManagerStateMachineActiveStandby->setRevertIntervalFnPtr( boost::bind(&FakeLinkProber::revertProbeIntervalAfterSwitchComplete, mFakeLinkProber.get()) ); + linkManagerStateMachineActiveStandby->setSendPeerSwitchCommandFnPtr( + boost::bind(&FakeLinkProber::sendPeerSwitchCommand, mFakeLinkProber.get()) + ); - linkManagerStateMachine->mComponentInitState.set(0); + linkManagerStateMachineActiveStandby->mComponentInitState.set(0); +} - std::string ipAddress = "192.168.0.1"; - std::vector servers; - servers = { - {port, "SET", {{"server_ipv4", ipAddress + "/32"}, {"server_ipv6", "2603:10e1:100:f::1/128"}}}, - {"Ethernet1234", "SET", {{"server_ipv4", "250.260.270.280/32"}}}, +void MuxManagerTest::generateServerMac(const std::string &port, std::array &address) +{ + std::shared_ptr muxPortPtr = mMuxManagerPtr->mPortMap[port]; + mMuxManagerPtr->generateServerMac(muxPortPtr->mMuxPortConfig.getServerId(), address); +} + +void MuxManagerTest::createPort(std::string port, common::MuxPortConfig::PortCableType portCableType) +{ + EXPECT_TRUE(mMuxManagerPtr->mPortMap.size() == 0); + EXPECT_TRUE(mMuxManagerPtr->mPortCableTypeMap.size() == 0); + + updatePortCableType(port, PortCableTypeValues[portCableType]); + EXPECT_TRUE(mMuxManagerPtr->mPortCableTypeMap.size() == 1); + + std::deque entries = { + {port, "SET", {{"oper_status", "up"}}}, }; - processServerIpAddress(servers); + mDbInterfacePtr->processLinkStateNotifiction(entries); + std::shared_ptr muxPortPtr = mMuxManagerPtr->mPortMap[port]; + std::shared_ptr linkManagerStateMachine = muxPortPtr->getLinkManagerStateMachinePtr(); + + EXPECT_TRUE(mMuxManagerPtr->mPortMap.size() == 1); + EXPECT_TRUE(linkManagerStateMachine->mComponentInitState.test(link_manager::LinkManagerStateMachineBase::LinkStateComponent) == 0); runIoService(); + EXPECT_TRUE(linkManagerStateMachine->mComponentInitState.test(link_manager::LinkManagerStateMachineBase::LinkStateComponent) == 1); + + // Initialize a FakeLinkProber + switch (portCableType) { + case common::MuxPortConfig::PortCableType::ActiveActive: { + initLinkProberActiveActive(std::dynamic_pointer_cast(linkManagerStateMachine)); + break; + } + case common::MuxPortConfig::PortCableType::ActiveStandby: { + initLinkProberActiveStandby(std::dynamic_pointer_cast(linkManagerStateMachine)); + break; + } + default: + break; + } + + std::string ipAddress = ServerAddress; + std::string iPAddressSoC = SoCAddress; + std::vector servers; + if (portCableType == common::MuxPortConfig::PortCableType::ActiveActive) { + servers = { + {port, "SET", {{"server_ipv4", ipAddress + "/32"}, {"server_ipv6", "2603:10e1:100:f::1/128"}, {"soc_ipv4", iPAddressSoC + "/32"}, {"cable_type", "active-active"}}}, + {"Ethernet1234", "SET", {{"server_ipv4", "250.260.270.280/32"}}}, + }; + + } else if (portCableType == common::MuxPortConfig::PortCableType::ActiveStandby) { + servers = { + {port, "SET", {{"server_ipv4", ipAddress + "/32"}, {"server_ipv6", "2603:10e1:100:f::1/128"}}}, + {"Ethernet1234", "SET", {{"server_ipv4", "250.260.270.280/32"}}}, + }; + } + + processServerIpAddress(servers); + pollIoService(); + EXPECT_TRUE(mMuxManagerPtr->mPortMap.size() == 1); + processSoCIpAddress(servers); + pollIoService(); + EXPECT_TRUE(mMuxManagerPtr->mPortMap.size() == 1); + + entries.clear(); entries = { {port, "SET", {{"state", "active"}}}, @@ -257,31 +339,31 @@ void MuxManagerTest::createPort(std::string port) mDbInterfacePtr->processMuxStateNotifiction(entries); EXPECT_TRUE(mMuxManagerPtr->mPortMap.size() == 1); - EXPECT_TRUE(linkManagerStateMachine->mComponentInitState.test(link_manager::ActiveStandbyStateMachine::MuxStateComponent) == 0); + EXPECT_TRUE(linkManagerStateMachine->mComponentInitState.test(link_manager::LinkManagerStateMachineBase::MuxStateComponent) == 0); runIoService(); - EXPECT_TRUE(linkManagerStateMachine->mComponentInitState.test(link_manager::ActiveStandbyStateMachine::MuxStateComponent) == 1); + EXPECT_TRUE(linkManagerStateMachine->mComponentInitState.test(link_manager::LinkManagerStateMachineBase::MuxStateComponent) == 1); } TEST_F(MuxManagerTest, UpdatePortCableTypeActiveStandby) { - std::string port = "Ethernet0"; + std::string port = MuxManagerTest::PortName; updatePortCableType(port, "active-standby"); EXPECT_TRUE(getPortCableType(port) == common::MuxPortConfig::PortCableType::ActiveStandby); } TEST_F(MuxManagerTest, UpdatePortCableTypeUnsupported) { - std::string port = "Ethernet0"; + std::string port = MuxManagerTest::PortName; updatePortCableType(port, "active-standby-active"); EXPECT_TRUE(getPortCableType(port) == common::MuxPortConfig::PortCableType::ActiveStandby); } TEST_F(MuxManagerTest, AddPort) { - std::string port = "Ethernet0"; - std::string ipAddress = "192.168.0.1"; + std::string port = MuxManagerTest::PortName; + std::string ipAddress = MuxManagerTest::ServerAddress; createPort(port); @@ -298,6 +380,29 @@ TEST_F(MuxManagerTest, AddPort) EXPECT_TRUE(getBladeIpv4Address(port).to_string() == ipAddress); } +TEST_F(MuxManagerTest, AddPortActiveActive) +{ + std::string port = MuxManagerTest::PortName; + std::string ipAddress = MuxManagerTest::ServerAddress; + std::string ipAddressSoC = MuxManagerTest::ServerAddress; + + createPort(port, common::MuxPortConfig::ActiveActive); + + std::array serverMac = {0, 'b', 2, 'd', 4, 'f'}; + boost::asio::ip::address serverAddress = boost::asio::ip::address::from_string(ipAddress); + + updateServerMacAddress(serverAddress, serverMac.data()); + + runIoService(); + + std::array bladeMacAddress = getBladeMacAddress(port); + EXPECT_TRUE(bladeMacAddress != serverMac); + + std::array knownMac; + generateServerMac(port, knownMac); + EXPECT_TRUE(bladeMacAddress == knownMac); +} + TEST_F(MuxManagerTest, Loopback2Address) { std::string port = "Ethernet0"; @@ -442,7 +547,7 @@ TEST_P(MuxResponseTest, MuxResponse) { std::string port = "Ethernet0"; - createPort(port); + createPort(port, std::get<3> (GetParam())); std::deque entries = { {port, "SET", {{"response", std::get<0> (GetParam())}}}, @@ -458,10 +563,14 @@ INSTANTIATE_TEST_CASE_P( MuxState, MuxResponseTest, ::testing::Values( - std::make_tuple("active", 1, mux_state::MuxState::Label::Active), - std::make_tuple("standby", 3, mux_state::MuxState::Label::Standby), - std::make_tuple("unknown", 3, mux_state::MuxState::Label::Wait), - std::make_tuple("error", 3, mux_state::MuxState::Label::Wait) + std::make_tuple("active", 1, mux_state::MuxState::Label::Active, common::MuxPortConfig::PortCableType::ActiveStandby), + std::make_tuple("standby", 3, mux_state::MuxState::Label::Standby, common::MuxPortConfig::PortCableType::ActiveStandby), + std::make_tuple("unknown", 3, mux_state::MuxState::Label::Wait, common::MuxPortConfig::PortCableType::ActiveStandby), + std::make_tuple("error", 3, mux_state::MuxState::Label::Wait, common::MuxPortConfig::PortCableType::ActiveStandby), + std::make_tuple("active", 1, mux_state::MuxState::Label::Active, common::MuxPortConfig::PortCableType::ActiveActive), + std::make_tuple("standby", 3, mux_state::MuxState::Label::Standby, common::MuxPortConfig::PortCableType::ActiveActive), + std::make_tuple("unknown", 3, mux_state::MuxState::Label::Unknown, common::MuxPortConfig::PortCableType::ActiveActive), + std::make_tuple("error", 3, mux_state::MuxState::Label::Unknown, common::MuxPortConfig::PortCableType::ActiveActive) ) ); @@ -495,7 +604,7 @@ TEST_P(MuxConfigUpdateTest, MuxPortConfigUpdate) { std::string port = "Ethernet0"; - createPort("Ethernet0"); + createPort("Ethernet0", std::get<2> (GetParam())); std::string state = std::get<0> (GetParam()); std::deque entries = { @@ -511,9 +620,14 @@ INSTANTIATE_TEST_CASE_P( AutoActiveManual, MuxConfigUpdateTest, ::testing::Values( - std::make_tuple("auto", common::MuxPortConfig::Mode::Auto), - std::make_tuple("active", common::MuxPortConfig::Mode::Active), - std::make_tuple("manual", common::MuxPortConfig::Mode::Manual) + std::make_tuple("auto", common::MuxPortConfig::Mode::Auto, common::MuxPortConfig::PortCableType::ActiveStandby), + std::make_tuple("active", common::MuxPortConfig::Mode::Active, common::MuxPortConfig::PortCableType::ActiveStandby), + std::make_tuple("standby", common::MuxPortConfig::Mode::Standby, common::MuxPortConfig::PortCableType::ActiveStandby), + std::make_tuple("manual", common::MuxPortConfig::Mode::Manual, common::MuxPortConfig::PortCableType::ActiveStandby), + std::make_tuple("auto", common::MuxPortConfig::Mode::Auto, common::MuxPortConfig::PortCableType::ActiveActive), + std::make_tuple("active", common::MuxPortConfig::Mode::Active, common::MuxPortConfig::PortCableType::ActiveActive), + std::make_tuple("standby", common::MuxPortConfig::Mode::Standby, common::MuxPortConfig::PortCableType::ActiveActive), + std::make_tuple("manual", common::MuxPortConfig::Mode::Manual, common::MuxPortConfig::PortCableType::ActiveActive) ) ); diff --git a/test/MuxManagerTest.h b/test/MuxManagerTest.h index b76dd31d..266ebb3c 100644 --- a/test/MuxManagerTest.h +++ b/test/MuxManagerTest.h @@ -39,6 +39,8 @@ class MuxManager; namespace test { +const char* PortCableTypeValues[] = {"active-standby", "active-active"}; + class MuxManagerTest: public testing::Test { public: @@ -46,6 +48,7 @@ class MuxManagerTest: public testing::Test virtual ~MuxManagerTest() = default; void runIoService(uint32_t count = 1); + void pollIoService(uint32_t count = 1); common::MuxPortConfig::Mode getMode(std::string port); uint32_t getPositiveStateChangeRetryCount(std::string port); uint32_t getNegativeStateChangeRetryCount(std::string port); @@ -60,6 +63,7 @@ class MuxManagerTest: public testing::Test void processMuxPortConfigNotifiction(std::deque &entries); link_manager::LinkManagerStateMachineBase::CompositeState getCompositeStateMachineState(std::string port); void processServerIpAddress(std::vector &servers); + void processSoCIpAddress(std::vector &servers); void processServerMacAddress(std::string port, std::array ip, std::array mac); void processLoopback2InterfaceInfo(std::vector &loopbackIntfs); void processTorMacAddress(std::string &mac); @@ -68,7 +72,15 @@ class MuxManagerTest: public testing::Test void updateServerMacAddress(boost::asio::ip::address serverIp, const uint8_t *serverMac); void processGetMuxState(const std::string &portName, const std::string &muxState); void updatePortCableType(const std::string &port, const std::string &cableType); - void createPort(std::string port); + void initLinkProberActiveActive(std::shared_ptr linkManagerStateMachine); + void initLinkProberActiveStandby(std::shared_ptr linkManagerStateMachine); + void generateServerMac(const std::string &portName, std::array &address); + void createPort(std::string port, common::MuxPortConfig::PortCableType portCableType = common::MuxPortConfig::PortCableType::ActiveStandby); + +public: + static const std::string PortName; + static const std::string ServerAddress; + static const std::string SoCAddress; public: std::shared_ptr mMuxManagerPtr; @@ -79,7 +91,7 @@ class MuxManagerTest: public testing::Test }; class MuxResponseTest: public MuxManagerTest, - public testing::WithParamInterface> + public testing::WithParamInterface> { }; @@ -89,7 +101,7 @@ class GetMuxStateTest: public MuxManagerTest, }; class MuxConfigUpdateTest: public MuxManagerTest, - public testing::WithParamInterface> + public testing::WithParamInterface> { }; diff --git a/test/subdir.mk b/test/subdir.mk index 56786ca0..e10deb8a 100644 --- a/test/subdir.mk +++ b/test/subdir.mk @@ -4,6 +4,7 @@ CPP_SRCS += \ ./test/FakeLinkProber.cpp \ ./test/FakeMuxPort.cpp \ ./test/LinkManagerStateMachineTest.cpp \ + ./test/LinkManagerStateMachineActiveActiveTest.cpp \ ./test/LinkProberTest.cpp \ ./test/MuxManagerTest.cpp \ ./test/MockLinkManagerStateMachine.cpp \ @@ -15,6 +16,7 @@ OBJS_LINKMGRD_TEST += \ ./test/FakeLinkProber.o \ ./test/FakeMuxPort.o \ ./test/LinkManagerStateMachineTest.o \ + ./test/LinkManagerStateMachineActiveActiveTest.o \ ./test/LinkProberTest.o \ ./test/MuxManagerTest.o \ ./test/MockLinkManagerStateMachine.o \ @@ -26,6 +28,7 @@ CPP_DEPS += \ ./test/FakeLinkProber.d \ ./test/FakeMuxPort.d \ ./test/LinkManagerStateMachineTest.d \ + ./test/LinkManagerStateMachineActiveActiveTest.d \ ./test/LinkProberTest.d \ ./test/MuxManagerTest.d \ ./test/MockLinkManagerStateMachine.d \